Opcode Summary

PropertyValue
Opcode0x0B
MnemonicSIGNEXTEND
Gas5
Stack Inputb, x
Stack OutputSign-extended x from (b+1) bytes to 32 bytes
BehaviorExtends the sign bit of the (b+1)th byte of x to fill all higher-order bytes. If the sign bit (bit 8b + 7) is 1, all higher bits are set to 1; if 0, all higher bits are set to 0. Treats x as a two’s complement signed integer of (b+1) bytes.

Threat Surface

SIGNEXTEND is the EVM’s type-system bridge between signed integers of different widths. Its threat surface is fundamentally different from arithmetic opcodes like ADD or MUL: rather than producing wrong magnitudes through overflow, SIGNEXTEND produces wrong signs. A single missing or incorrect SIGNEXTEND can flip a positive value to negative or vice versa, silently corrupting every downstream signed operation — comparisons (SLT, SGT), divisions (SDIV), modular arithmetic (SMOD), and arithmetic shifts (SAR).

The danger is amplified by a critical asymmetry: the EVM has no native concept of sub-256-bit types. The stack holds only 256-bit words. When Solidity compiles int8, int16, or int128 operations, it inserts SIGNEXTEND to re-establish the correct signed interpretation after narrowing operations. If the compiler omits SIGNEXTEND (as happened in the Signed Immutables bug across Solidity 0.6.5–0.8.8), or if a developer writes assembly that forgets it, the value’s bit pattern is correct for its narrow type but wrong when interpreted as int256. A 0xFE that should mean -2 (int8) is instead read as 254 (uint256) — a sign error of 256.

This class of bug — type confusion between signed widths — maps directly to CWE-194 (“Unexpected Sign Extension”), a well-known vulnerability category in systems programming. In the EVM context, the consequences are particularly severe because:

  1. All signed arithmetic depends on correct sign extension. SDIV, SMOD, SLT, SGT, and SAR all interpret their operands as two’s complement int256. If a value wasn’t properly sign-extended, these opcodes operate on the wrong number.
  2. Assembly code bypasses the compiler’s cleanup. Solidity normally inserts SIGNEXTEND at critical points, but inline assembly (assembly { }) gives raw access to stack values without cleanup, exposing unextended bits.
  3. The error is invisible in unsigned context. A value with missing sign extension looks perfectly normal when used in unsigned operations (ADD, MUL, LT, GT). The bug only manifests when the value enters a signed code path, making it difficult to detect through testing.

Smart Contract Threats

T1: Type Confusion Between Signed Integer Widths (High)

The EVM’s 256-bit stack has no type metadata. A value like 0x00000000000000000000000000000000000000000000000000000000000000FE could be:

  • 254 as uint8, uint256
  • -2 as int8 (requires sign extension to 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF FE)
  • 254 as int256 (no sign extension needed — it’s already positive)

When converting between signed types of different sizes, SIGNEXTEND must be applied to preserve semantic correctness. Missing sign extension produces type confusion: the bit pattern is unchanged but its meaning flips.

Common vulnerable patterns:

  • int8 to int256 widening without SIGNEXTEND: A negative int8 value (e.g., -1 = 0xFF) is interpreted as 255 in int256 context
  • int128 to int256 widening: A negative int128 (high bit set in byte 15) becomes a large positive int256
  • Cross-contract signed value passing: Contract A returns an int8 packed in a uint256 word; Contract B interprets it as int256 without sign-extending
contract TypeConfusion {
    int8 internal threshold = -10;
 
    function isAboveThreshold(int256 value) external view returns (bool) {
        int256 t;
        assembly {
            t := sload(threshold.slot)
            // t = 0x00000000...F6 (246 as int256, not -10)
            // Missing: t := signextend(0, t)  -- would correctly produce -10
        }
        return value > t;  // Compares against 246 instead of -10
    }
}

T2: Incorrect Narrowing Before Sign Extension (High)

When narrowing a signed value (e.g., int256 to int8), the correct sequence is: truncate to the target width, then sign-extend back to 256 bits for further use. If the sign extension step is omitted after truncation, the value loses its sign:

function narrowAndUse(int256 x) internal pure returns (int256) {
    int8 narrow = int8(x);  // Truncates to low 8 bits
    // Solidity compiler inserts SIGNEXTEND(0, narrow) here
    // But in assembly, if omitted:
    int256 result;
    assembly {
        let truncated := and(x, 0xFF)  // Correct truncation
        // Missing: truncated := signextend(0, truncated)
        result := truncated  // 0xFF interpreted as 255, not -1
    }
    return result;
}

The danger is acute in hand-optimized assembly libraries where developers truncate values with AND masks but forget the subsequent SIGNEXTEND. The value appears correct for unsigned operations but silently corrupts signed paths.

T3: Assembly-Level Misuse of SIGNEXTEND (High)

Inline assembly is the primary vector for SIGNEXTEND bugs because it bypasses the Solidity compiler’s automatic cleanup. Three common assembly mistakes:

Wrong byte index b: The b parameter is the byte index (0-indexed), not the bit width. For int8, b = 0 (1 byte); for int16, b = 1 (2 bytes); for int128, b = 15 (16 bytes). An off-by-one error in b sign-extends from the wrong bit position:

assembly {
    // WRONG: extends from 2 bytes (int16) instead of 1 byte (int8)
    let bad := signextend(1, val)
 
    // CORRECT: extends from 1 byte (int8)
    let good := signextend(0, val)
}

Omitted SIGNEXTEND after storage/memory load: Storage slots and memory are 256-bit, so loading a packed int8 from storage produces a 256-bit word with the int8 in the low bits and zeros (or neighboring data) in the high bits. Without SIGNEXTEND, negative values appear positive:

assembly {
    let packed := sload(slot)
    let value := and(packed, 0xFF)  // Extract low byte
    // 'value' is now 0x00...00FE if original was -2
    // Must apply: value := signextend(0, value) to get 0xFF...FFFE
}

SIGNEXTEND applied to unsigned value: Applying SIGNEXTEND to a uint8 value of 200 (0xC8) would incorrectly produce -56 in int256 context (0xFFFFFF...C8), because bit 7 of 0xC8 is 1. This confuses unsigned and signed interpretation of the same byte.

T4: Signed Value Corruption in Cross-Contract Calls (Medium)

When contracts exchange signed values through low-level calls, the ABI encoding/decoding must preserve sign extension. If a contract returns a signed value via assembly without proper cleanup, the caller may receive a corrupted value:

contract Provider {
    int8 public delta = -5;  // Stored as 0xFB in low byte of storage slot
 
    function getDelta() external view returns (int256) {
        assembly {
            let raw := sload(delta.slot)
            let val := and(raw, 0xFF)
            // Bug: returns 251 (0xFB) instead of -5
            // Fix: val := signextend(0, val)
            mstore(0x00, val)
            return(0x00, 0x20)
        }
    }
}
 
contract Consumer {
    function adjust(int256 base) external view returns (int256) {
        int256 d = Provider(addr).getDelta();
        return base + d;  // Adds 251 instead of subtracting 5
    }
}

T5: Compiler-Level Sign Extension Bugs (Medium)

The Solidity compiler itself has had sign extension bugs:

  • Signed Immutables Bug (Solidity 0.6.5–0.8.8): Immutable variables of signed types shorter than 256 bits were not properly sign-extended when read through inline assembly. A int8 immutable x = -2 would return 0x00...FE (254) instead of 0xFF...FE (-2) when accessed via assembly { r := x }.

  • Storage Array Sign Extension (Issue #13180): The legacy code generator did not sign-extend higher-order bits for signed integer storage arrays when a single value occupies one slot, affecting types with bit width exceeding 128 bits.

These compiler bugs demonstrate that even the trusted compilation layer can emit incorrect SIGNEXTEND sequences, making the opcode a source of systemic risk rather than just developer error.


Protocol-Level Threats

P1: Consensus Divergence from Implementation Differences (Medium)

SIGNEXTEND’s behavior requires precise bit-level agreement across all EVM clients. The algorithm is:

  1. If b >= 31, return x unchanged (no-op)
  2. Otherwise, examine bit 8b + 7 (the sign bit of the (b+1)th byte)
  3. If the sign bit is 1, set all bits above position 8b + 7 to 1
  4. If the sign bit is 0, set all bits above position 8b + 7 to 0

The ethereumjs-monorepo (Issue #1679) had a confirmed implementation discrepancy in SIGNEXTEND behavior, particularly around the b = 31 boundary. While this was caught before causing a consensus split, it demonstrates that SIGNEXTEND’s bit-manipulation logic is a realistic source of cross-client disagreement. An incorrect SIGNEXTEND in one client would produce different results for any contract using signed integer types, potentially causing a chain fork.

P2: No DoS Vector (Low)

SIGNEXTEND costs a fixed 5 gas regardless of operands. It cannot be used for gas griefing. It operates purely on the stack with no memory or storage access.

P3: No Direct State Impact (None)

SIGNEXTEND modifies only the stack. It cannot cause state bloat, storage writes, or memory expansion.


Edge Cases

Edge CaseBehaviorSecurity Implication
b >= 31 (e.g., b = 31, b = 255)Returns x unchanged (no-op)Safe; the value already occupies all 256 bits. No extension needed. Verified as a past source of implementation bugs (ethereumjs Issue #1679)
b = 0, x = 0x7F (127)Returns 0x000...007F (127)Positive int8 max; high bit of byte 0 is 0, so upper bytes stay 0
b = 0, x = 0x80 (128 unsigned)Returns 0xFFFFFF...FF80 (-128 as int256)Bit 7 is 1 (sign bit for int8), so all upper bits become 1. The unsigned 128 becomes signed -128
b = 0, x = 0xFF (255 unsigned)Returns 0xFFFFFF...FFFF (-1 as int256)Full sign extension of -1 in int8
b = 0, x = 0x00Returns 0x00 (0)Zero is zero in all signed representations
b = 1, x = 0x7FFF (32767)Returns 0x000...7FFF (32767)Positive int16 max; no sign extension needed
b = 1, x = 0x8000 (32768 unsigned)Returns 0xFFFFFF...8000 (-32768 as int256)Negative int16 min; sign bit at bit 15 propagates
b = 0, x = 0xFFFFFF...FF80Returns 0xFFFFFF...FF80 (-128)Already sign-extended; SIGNEXTEND is idempotent for correctly-extended values
b = 0, x = 0x00000...00180 (384 unsigned)Returns 0xFFFFFF...FF80 (-128)Only the low byte matters for b = 0; upper bytes are overwritten by sign extension. Input bits above position 7 are discarded
b = 15, x with bit 127 setUpper 128 bits become all 1sint128 sign extension; critical for DeFi fixed-point math using int128
Large b (e.g., b = 2^256 - 1)Returns x unchanged (no-op)Any b >= 31 is a no-op; giant values are safe

Real-World Exploits

Exploit 1: Solidity Signed Immutables Bug — Compiler-Level SIGNEXTEND Omission (Solidity 0.6.5–0.8.8)

Root cause: The Solidity compiler’s legacy code generator failed to emit SIGNEXTEND when reading back immutable variables of signed integer types shorter than 256 bits.

Details: When immutable variables are assigned during contract construction, values shorter than 32 bytes are stored left-aligned in 32-byte words within the deployed bytecode. The compiler is supposed to apply sign extension (cleanup) when these values are loaded at runtime. However, from Solidity 0.6.5 (when immutables were introduced) through 0.8.8, the legacy code generator omitted the SIGNEXTEND instruction for signed immutables accessed via inline assembly.

For example, a contract with int8 immutable x = -2 would store 0xFE in the bytecode. When loaded, the compiler should emit SIGNEXTEND(0, value) to produce 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF FE (-2 as int256). Instead, the value was returned as 0x00000000000000000000000000000000000000000000000000000000000000FE (254 as int256).

contract C {
    int8 immutable x = -2;
 
    function f() public view returns (bool) {
        int8 y = x;
        bool viaAssembly;
        assembly {
            // Returns 0x00...FE (254) instead of 0xFF...FE (-2)
            // Any signed comparison or arithmetic on this value is wrong
            viaAssembly := sgt(y, 0)  // True (254 > 0) instead of False (-2 > 0)
        }
        return viaAssembly;
    }
}

SIGNEXTEND’s role: The bug was literally a missing SIGNEXTEND instruction in the compiler output. The compiler was supposed to emit SIGNEXTEND(0, ...) for int8, SIGNEXTEND(1, ...) for int16, etc., but didn’t for immutable variables in the legacy code generator. The Yul-IR-based code generator was not affected.

Impact: Classified as “very low” severity by the Solidity team because the bug only manifested when accessing immutable values through inline assembly — normal Solidity operations performed cleanup before signed operations. However, contracts using assembly optimizations with signed immutables could have silently incorrect signed comparisons and arithmetic. The bug was discovered through differential fuzzing between the legacy and Yul code generators.

Affected versions: Solidity 0.6.5 through 0.8.8. Fixed in 0.8.9.

References:


Exploit 2: EthereumJS SIGNEXTEND Implementation Discrepancy — Consensus Risk (Issue #1679)

Root cause: The ethereumjs-monorepo’s EVM implementation had a SIGNEXTEND behavior that differed from Go-Ethereum’s reference implementation, particularly around the b = 31 boundary condition.

Details: During analysis of SIGNEXTEND correctness (Issue #1679), it was discovered that the ethereumjs implementation did not match geth’s handling of the edge case where b = 31. When b = 31, the sign bit is at position 255 (the MSB of the entire 256-bit word), meaning there are no higher-order bits to fill. This should be a no-op: the value is returned unchanged. The ethereumjs implementation’s handling of this boundary was incorrect or ambiguous.

This is a consensus-critical discrepancy. If ethereumjs and geth produce different SIGNEXTEND results for any input, any contract using signed integers could produce different execution results on nodes running different clients. In the worst case, this leads to a chain split where some nodes accept a block and others reject it.

SIGNEXTEND’s role: The opcode’s bit-manipulation logic — constructing a mask from the sign bit position and applying it — is more complex than simple arithmetic opcodes. The boundary between “extend” (b < 31) and “no-op” (b >= 31) is a natural source of off-by-one errors. Additionally, the behavior when b > 31 (any value above 31 should also be a no-op) must be handled correctly.

Impact: The issue was identified and resolved before causing an actual consensus split. However, it demonstrates that SIGNEXTEND is a realistic vector for cross-client divergence, and that the opcode’s semantics require careful implementation.

References:


Exploit 3: Solidity Storage Array Sign-Extension Bug — Legacy Codegen (Issue #13180)

Root cause: The legacy Solidity code generator did not sign-extend higher-order bits for signed integer storage arrays (where a single value occupies one full slot), unlike the via-IR code generator.

Details: For signed integer arrays like int136[1] stored in a full 256-bit storage slot, the legacy code generator loaded the value via SLOAD without applying SIGNEXTEND. When accessed through inline assembly, this returned a value with unclean higher-order bits — zeros instead of sign-extended ones for negative values. The via-IR code generator correctly applied sign extension.

contract C {
    int136[1] arr;
 
    constructor() {
        arr[0] = -1;  // Stored as 0xFF...FF in two's complement
    }
 
    function f() external view returns (bytes32 r) {
        int136 x = arr[0];
        assembly {
            r := x
            // Legacy codegen: 0x00000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
            // via-IR codegen: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
        }
    }
}

SIGNEXTEND’s role: The bug is another instance of missing SIGNEXTEND in compiler-generated code. For int136, the compiler should emit SIGNEXTEND(16, value) after loading from storage. The legacy codegen omitted this for array elements occupying a full slot.

Impact: Low severity. As with the signed immutables bug, normal Solidity operations perform cleanup before signed operations. The vulnerability only manifests through inline assembly access. However, contracts mixing assembly optimization with signed storage arrays could silently produce incorrect signed arithmetic.

References:


Attack Scenarios

Scenario A: Sign Confusion in Access Control

// Vulnerable: signed threshold loaded without sign extension in assembly
contract VulnerableVault {
    int8 immutable minDelta = -10;  // Minimum allowed price change
 
    function executeSwap(int256 priceDelta) external {
        bool allowed;
        assembly {
            // Bug: minDelta loaded as 246 (0xF6) instead of -10 (0xFF...F6)
            // In Solidity 0.6.5-0.8.8 with legacy codegen
            let threshold := minDelta
            allowed := sgt(priceDelta, threshold)
            // Attacker needs priceDelta > 246 instead of priceDelta > -10
            // This makes the check FAR more restrictive than intended,
            // potentially DoS'ing legitimate operations
        }
        require(allowed, "Delta too low");
        _swap(priceDelta);
    }
}

Attack vector: The sign extension bug inverts the security property. Instead of allowing any delta above -10 (most values), the check requires delta above 246 (very few values). An attacker who discovers this can use it for griefing, or in the reverse scenario where the sign confusion makes the check more permissive, bypass the guard entirely.

Scenario B: Signed Value Corruption in Assembly Math Library

// Vulnerable: manual sign extension with wrong byte index
library SignedMath {
    function abs8(int8 x) internal pure returns (uint8) {
        int256 extended;
        assembly {
            // BUG: signextend(1, x) extends from 2 bytes (int16), not 1 byte (int8)
            // For x = -1 (0xFF as int8):
            //   signextend(1, 0xFF) = 0x00...00FF (255) -- wrong! Treated as positive
            //   signextend(0, 0xFF) = 0xFF...FFFF (-1) -- correct
            extended := signextend(1, x)
        }
        if (extended < 0) {
            return uint8(-x);
        }
        return uint8(x);  // Returns 255 instead of 1
    }
}
 
contract VulnerableProtocol {
    using SignedMath for int8;
 
    function calculateFee(int8 deviation) external pure returns (uint256) {
        uint8 absDeviation = deviation.abs8();
        // abs(-1) returns 255 instead of 1 -- fee is 255x too large
        return uint256(absDeviation) * FEE_PER_UNIT;
    }
}

Attack vector: The off-by-one error in the byte index (1 instead of 0) means SIGNEXTEND treats the value as int16 instead of int8. For int8 negative values where byte 1 is 0x00, the sign bit at position 15 is 0, so the value is not sign-extended — it remains positive. This breaks the abs function: negative inputs are treated as their unsigned equivalents.

Scenario C: Cross-Contract Signed Value Poisoning

// Oracle contract returns signed price deviations via assembly
contract PriceOracle {
    mapping(address => int8) internal deviations;
 
    function getDeviation(address token) external view returns (int256) {
        int256 result;
        assembly {
            mstore(0x00, token)
            mstore(0x20, deviations.slot)
            let slot := keccak256(0x00, 0x40)
            let raw := sload(slot)
            let val := and(raw, 0xFF)
            // BUG: missing signextend(0, val)
            // Negative deviations (e.g., -5 = 0xFB) become positive (251)
            mstore(0x00, val)
            return(0x00, 0x20)
        }
    }
}
 
contract LendingProtocol {
    function liquidate(address token, address user) external {
        int256 deviation = oracle.getDeviation(token);
        // deviation is 251 instead of -5
        // Protocol thinks price spiked UP by 251 instead of DOWN by 5
        // Liquidation logic is completely inverted
        if (deviation < -LIQUIDATION_THRESHOLD) {
            _liquidate(user);  // Never triggers for negative deviations
        }
    }
}

Attack vector: The oracle returns corrupt positive values for negative deviations. A price crash (negative deviation) is interpreted as a price spike (positive), preventing liquidations and allowing undercollateralized positions to persist.

Scenario D: SIGNEXTEND Applied to Unsigned Value

// Vulnerable: applying SIGNEXTEND to a value that should be unsigned
function processAmount(uint8 amount) internal pure returns (uint256) {
    uint256 result;
    assembly {
        // amount = 200 (0xC8)
        // Developer mistakenly applies sign extension
        result := signextend(0, amount)
        // Bit 7 of 0xC8 is 1, so SIGNEXTEND produces:
        // 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF C8
        // This is -56 as int256, or a massive number as uint256
    }
    return result;
    // Returns 0xFFFF...FFC8 (~1.15 * 10^77) instead of 200
}

Attack vector: SIGNEXTEND on an unsigned value whose high bit happens to be set produces an astronomically large uint256. If this value is used as a transfer amount, allocation, or array index, the consequences are catastrophic.


Mitigations

ThreatMitigationImplementation
T1: Type confusion between signed widthsUse Solidity’s type system; avoid raw assembly for signed conversionsLet the compiler handle int8 → int256 widening; it emits SIGNEXTEND automatically
T2: Missing sign extension after narrowingUse SafeCast for type conversionsOpenZeppelin SafeCast.toInt8(), SafeCast.toInt16(), etc. revert on overflow
T3: Wrong byte index in assembly SIGNEXTENDUse named constants for byte indiceslet INT8_B := 0, let INT16_B := 1, let INT128_B := 15; never use raw literals
T3: Omitted SIGNEXTEND after sload/mloadAlways sign-extend after extracting packed signed valuesval := signextend(0, and(sload(slot), 0xFF)) for int8
T3: SIGNEXTEND on unsigned valuesNever apply SIGNEXTEND to unsigned typesUse and(val, 0xFF) for uint8 cleanup, not signextend(0, val)
T4: Cross-contract signed corruptionUse Solidity’s ABI encoder for signed returnsAvoid assembly-level return for signed values; let ABI encoding handle cleanup
T5: Compiler sign extension bugsUpgrade to Solidity >= 0.8.9Fixes the Signed Immutables bug and storage array sign extension issues
T5: Compiler bugs in generalTest with both legacy and via-IR code generatorsDifferential testing catches codegen-specific bugs
General: Assembly signed mathPrefer Solidity-level signed operations over assemblyThe compiler inserts SIGNEXTEND at all critical points; assembly bypasses this
General: Static analysisDetect missing SIGNEXTEND in assembly blocksSlither custom detectors, Mythril symbolic execution, Certora formal verification

Compiler-Level Protections

  • Solidity automatic cleanup: The compiler inserts SIGNEXTEND before every signed comparison (SLT, SGT), signed division (SDIV), signed modulo (SMOD), memory store, and ABI encoding. Normal Solidity code is protected even if intermediate values have dirty high bits.
  • Solidity >= 0.8.9: Fixes the Signed Immutables bug. All signed immutable variables are properly sign-extended when loaded.
  • Via-IR code generator: The newer Yul-based code generator handles sign extension more consistently than the legacy code generator. Compiling with --via-ir eliminates several known sign extension edge cases.
  • SafeCast (OpenZeppelin): Provides checked narrowing conversions (toInt8, toInt128, etc.) that revert on overflow rather than silently truncating.

SIGNEXTEND at the Opcode Level

The correct pattern for sign-extending a value extracted from a packed storage slot:

// Extract int8 from low byte of storage slot and sign-extend
PUSH slot
SLOAD            // Load full 256-bit word
PUSH 0xFF
AND              // Mask to low byte: 0x00...00XX
PUSH 0           // b = 0 for int8 (1-byte type)
SWAP1
SIGNEXTEND       // Extends sign bit at position 7 to fill bits 8-255

Severity Summary

Threat IDCategorySeverityLikelihoodReal-World Precedent
T1Smart ContractHighMediumSigned Immutables Bug (compiler-level type confusion)
T2Smart ContractHighMediumAssembly-heavy DeFi libraries
T3Smart ContractHighMediumOff-by-one in byte index, omitted SIGNEXTEND in assembly
T4Smart ContractMediumLowCross-contract signed value passing
T5Smart ContractMediumLowSolidity 0.6.5–0.8.8 Signed Immutables, Issue #13180
P1ProtocolMediumLowethereumjs Issue #1679 (implementation discrepancy)
P2ProtocolLowN/A

OpcodeRelationship
SDIV (0x05)Signed division interprets operands as two’s complement int256; requires correct SIGNEXTEND on inputs to produce correct quotients
SMOD (0x07)Signed modulo; same dependency on properly sign-extended operands as SDIV
SLT (0x12)Signed less-than comparison; a missing SIGNEXTEND flips comparison results (e.g., -2 appears as 254, failing SLT checks)
SGT (0x13)Signed greater-than comparison; mirror of SLT with the same SIGNEXTEND dependency
SAR (0x1D)Arithmetic (sign-preserving) right shift; operates on int256, so input must be properly sign-extended
BYTE (0x1A)Extracts a single byte from a 256-bit value; often used before SIGNEXTEND to isolate a packed signed value