> ## Documentation Index
> Fetch the complete documentation index at: https://voltaire.tevm.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# SHL (0x1b)

> Shift left operation for efficient multiplication by powers of 2 (EIP-145, Constantinople+)

<Warning>
  **This page is a placeholder.** All examples on this page are currently AI-generated and are not correct. This documentation will be completed in the future with accurate, tested examples.
</Warning>

## Overview

**Opcode:** `0x1b`
**Introduced:** Constantinople (EIP-145)

SHL performs logical shift left on a 256-bit value, shifting bits toward the most significant position. Vacated bits (on the right) are filled with zeros. This operation efficiently multiplies by powers of 2 and is critical for bit manipulation and data packing.

Before EIP-145, left shifts required expensive MUL + EXP operations (5-60+ gas). SHL reduces this to 3 gas.

## Specification

**Stack Input:**

```
shift (top) - number of bit positions to shift
value - 256-bit value to shift
```

**Stack Output:**

```
value << shift (mod 2^256)
```

**Gas Cost:** 3 (GasFastestStep)

**Hardfork:** Constantinople (EIP-145) or later

**Shift Behavior:**

```
shift < 256: value << shift (wraps at 256 bits)
shift >= 256: 0 (all bits shifted out)
```

## Behavior

SHL pops two values from the stack:

1. **shift** - number of bit positions to shift left (0-255)
2. **value** - 256-bit value to be shifted

Result is value shifted left by shift positions, with zeros filling vacated bits. If shift >= 256, result is 0 (all bits shifted out).

**Overflow:** High-order bits are discarded (wraps at 256 bits).

## Examples

### Basic Left Shift

```typescript theme={null}
import { shl } from '@tevm/voltaire/evm/bitwise';
import { createFrame } from '@tevm/voltaire/evm/Frame';

// Shift 0xFF left by 8 bits (multiply by 256)
const frame = createFrame({ stack: [8n, 0xFFn] });
const err = shl(frame);

console.log(frame.stack[0].toString(16));  // 'ff00'
```

### Multiply by Power of 2

```typescript theme={null}
// Shift left by N = multiply by 2^N
// 5 << 3 = 5 * 8 = 40
const frame = createFrame({ stack: [3n, 5n] });
shl(frame);

console.log(frame.stack[0]);  // 40n
```

### Zero Shift (Identity)

```typescript theme={null}
// Shift by 0 positions = identity
const value = 0x123456n;
const frame = createFrame({ stack: [0n, value] });
shl(frame);

console.log(frame.stack[0] === value);  // true
```

### Maximum Shift (Overflow)

```typescript theme={null}
// Shift >= 256 results in 0
const value = 0xFFFFFFFFn;
const frame = createFrame({ stack: [256n, value] });
shl(frame);

console.log(frame.stack[0]);  // 0n
```

### Partial Overflow

```typescript theme={null}
// High bits are discarded
const value = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFn;
const frame = createFrame({ stack: [4n, value] });
shl(frame);

// Result: lower 252 bits are all 1s, upper 4 bits are 0
const expected = ((1n << 256n) - 1n) - ((1n << 4n) - 1n);
console.log(frame.stack[0] === expected);  // true
```

### Pack Address into uint256

```typescript theme={null}
// Shift address to upper bits (leave lower bits for flags)
const address = 0xdEaDbEeFcAfE1234567890ABCDEf1234567890ABn;
const frame = createFrame({ stack: [96n, address] });  // Shift left 96 bits
shl(frame);

// Address now in upper 160 bits, lower 96 bits available for data
console.log(frame.stack[0].toString(16));
```

## Gas Cost

**Cost:** 3 gas (GasFastestStep)

**Pre-EIP-145 equivalent:**

```solidity theme={null}
// Before Constantinople: expensive!
result = value * (2 ** shift)  // MUL (5 gas) + EXP (10 + 50/byte gas)
// Total: 15-1615 gas

// After Constantinople: cheap!
assembly { result := shl(shift, value) }  // 3 gas
```

**Savings:** 12-1612 gas per shift operation.

## Edge Cases

### Zero Value

```typescript theme={null}
// Shifting zero always yields zero
const frame = createFrame({ stack: [100n, 0n] });
shl(frame);

console.log(frame.stack[0]);  // 0n
```

### Zero Shift

```typescript theme={null}
// No shift = identity
const value = 0xDEADBEEFn;
const frame = createFrame({ stack: [0n, value] });
shl(frame);

console.log(frame.stack[0] === value);  // true
```

### Shift by 1 (Double)

```typescript theme={null}
// Shift left by 1 = multiply by 2
const value = 42n;
const frame = createFrame({ stack: [1n, value] });
shl(frame);

console.log(frame.stack[0]);  // 84n
```

### Shift by 255 (Near Max)

```typescript theme={null}
// Shift to MSB position
const value = 1n;
const frame = createFrame({ stack: [255n, value] });
shl(frame);

const expected = 1n << 255n;  // 0x8000...0000
console.log(frame.stack[0] === expected);  // true
```

### Shift by 256+ (Complete Overflow)

```typescript theme={null}
// Any shift >= 256 yields 0
for (const shift of [256n, 257n, 1000n, (1n << 200n)]) {
  const frame = createFrame({ stack: [shift, 0xFFFFn] });
  shl(frame);
  console.log(frame.stack[0]);  // 0n for all
}
```

### Large Value, Small Shift

```typescript theme={null}
// Shifting MAX - some bits overflow
const MAX = (1n << 256n) - 1n;
const frame = createFrame({ stack: [1n, MAX] });
shl(frame);

// Result: all bits set except LSB
const expected = MAX - 1n;
console.log(frame.stack[0] === expected);  // true
```

### Hardfork Check (Pre-Constantinople)

```typescript theme={null}
// SHL is invalid before Constantinople
const frame = createFrame({
  stack: [8n, 0xFFn],
  hardfork: 'byzantium'  // Before Constantinople
});
const err = shl(frame);

console.log(err);  // { type: "InvalidOpcode" }
```

### Stack Underflow

```typescript theme={null}
// Insufficient stack items
const frame = createFrame({ stack: [8n] });
const err = shl(frame);

console.log(err);  // { type: "StackUnderflow" }
```

### Out of Gas

```typescript theme={null}
// Insufficient gas
const frame = createFrame({ stack: [8n, 0xFFn], gasRemaining: 2n });
const err = shl(frame);

console.log(err);  // { type: "OutOfGas" }
console.log(frame.gasRemaining);  // 0n
```

## Common Usage

### Multiply by Power of 2

```solidity theme={null}
// Efficient multiplication by 256
assembly {
    result := shl(8, value)  // 3 gas vs MUL (5 gas)
}
```

### Pack Data Fields

```solidity theme={null}
// Pack timestamp (40 bits) + amount (216 bits)
function pack(uint40 timestamp, uint216 amount) pure returns (uint256) {
    return (uint256(timestamp) << 216) | uint256(amount);
    // Or in assembly:
    // assembly {
    //     result := or(shl(216, timestamp), amount)
    // }
}
```

### Align to Byte Boundary

```solidity theme={null}
// Shift to align data to byte boundary
function alignToBytes(uint256 value, uint256 bytePos) pure returns (uint256) {
    assembly {
        result := shl(mul(8, bytePos), value)
    }
}
```

### Create Bit Mask

```solidity theme={null}
// Create mask with N consecutive ones at position P
function createMask(uint256 numBits, uint256 position) pure returns (uint256) {
    require(numBits + position <= 256, "overflow");
    uint256 mask = (1 << numBits) - 1;
    return mask << position;
    // assembly { result := shl(position, sub(shl(numBits, 1), 1)) }
}
```

### Scale Fixed-Point Numbers

```solidity theme={null}
// Fixed-point arithmetic: shift to scale by 10^18
uint256 constant SCALE = 1e18;

function toFixedPoint(uint256 value) pure returns (uint256) {
    // In practice, use MUL for non-power-of-2 scaling
    // But for powers of 2 (e.g., binary fixed-point):
    return value << 64;  // Q64.64 fixed-point
}
```

### Efficient Array Indexing

```solidity theme={null}
// Calculate array slot offset (element size * index)
// For 32-byte elements: index << 5 (multiply by 32)
function getArraySlot(uint256 baseSlot, uint256 index) pure returns (uint256) {
    assembly {
        let offset := shl(5, index)  // index * 32
        mstore(0, add(baseSlot, offset))
        return(0, 32)
    }
}
```

## Implementation

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    /**
     * SHL opcode (0x1b) - Shift left operation (EIP-145)
     */
    export function shl(frame: FrameType): EvmError | null {
      // Check hardfork (Constantinople or later)
      if (frame.hardfork.isBefore('constantinople')) {
        return { type: "InvalidOpcode" };
      }

      // Consume gas (GasFastestStep = 3)
      frame.gasRemaining -= 3n;
      if (frame.gasRemaining < 0n) {
        frame.gasRemaining = 0n;
        return { type: "OutOfGas" };
      }

      // Pop operands
      if (frame.stack.length < 2) return { type: "StackUnderflow" };
      const shift = frame.stack.pop();
      const value = frame.stack.pop();

      // Compute shift left (with overflow handling)
      const result = shift >= 256n
        ? 0n
        : (value << shift) & ((1n << 256n) - 1n);

      // Push result
      if (frame.stack.length >= 1024) return { type: "StackOverflow" };
      frame.stack.push(result);

      // Increment PC
      frame.pc += 1;

      return null;
    }
    ```
  </Tab>
</Tabs>

## Testing

### Test Coverage

```typescript theme={null}
import { describe, it, expect } from 'vitest';
import { shl } from './shl.js';

describe('SHL (0x1b)', () => {
  it('shifts left by 8 bits', () => {
    const frame = createFrame({ stack: [8n, 0xFFn] });
    expect(shl(frame)).toBeNull();
    expect(frame.stack[0]).toBe(0xFF00n);
  });

  it('multiplies by power of 2', () => {
    const frame = createFrame({ stack: [3n, 5n] });
    expect(shl(frame)).toBeNull();
    expect(frame.stack[0]).toBe(40n);  // 5 * 2^3
  });

  it('handles zero shift (identity)', () => {
    const value = 0x123456n;
    const frame = createFrame({ stack: [0n, value] });
    expect(shl(frame)).toBeNull();
    expect(frame.stack[0]).toBe(value);
  });

  it('returns 0 for shift >= 256', () => {
    const frame = createFrame({ stack: [256n, 0xFFFFFFFFn] });
    expect(shl(frame)).toBeNull();
    expect(frame.stack[0]).toBe(0n);
  });

  it('handles partial overflow', () => {
    const value = 0xFFn;
    const frame = createFrame({ stack: [252n, value] });
    expect(shl(frame)).toBeNull();

    // 0xFF << 252 = 0xFF00...0000 (252 zeros)
    const expected = 0xFFn << 252n;
    expect(frame.stack[0]).toBe(expected);
  });

  it('shifts MAX value by 1', () => {
    const MAX = (1n << 256n) - 1n;
    const frame = createFrame({ stack: [1n, MAX] });
    expect(shl(frame)).toBeNull();

    // All bits except LSB
    expect(frame.stack[0]).toBe(MAX - 1n);
  });

  it('shifts 1 to MSB position', () => {
    const frame = createFrame({ stack: [255n, 1n] });
    expect(shl(frame)).toBeNull();
    expect(frame.stack[0]).toBe(1n << 255n);
  });

  it('returns InvalidOpcode before Constantinople', () => {
    const frame = createFrame({
      stack: [8n, 0xFFn],
      hardfork: 'byzantium'
    });
    expect(shl(frame)).toEqual({ type: 'InvalidOpcode' });
  });

  it('returns StackUnderflow with insufficient stack', () => {
    const frame = createFrame({ stack: [8n] });
    expect(shl(frame)).toEqual({ type: 'StackUnderflow' });
  });

  it('returns OutOfGas when insufficient gas', () => {
    const frame = createFrame({ stack: [8n, 0xFFn], gasRemaining: 2n });
    expect(shl(frame)).toEqual({ type: 'OutOfGas' });
  });
});
```

### Edge Cases Tested

* Basic shift operations
* Multiplication by powers of 2
* Zero shift (identity)
* Shift >= 256 (overflow to zero)
* Partial overflow
* Maximum value shifts
* Shift to MSB position
* Zero value shifts
* Hardfork compatibility
* Stack underflow
* Out of gas

## Security

### Unchecked Shift Amount

```solidity theme={null}
// RISKY: User-controlled shift without bounds
function shiftLeft(uint256 value, uint256 shift) pure returns (uint256) {
    return value << shift;  // shift >= 256 → 0 (may not be intended)
}

// SAFER: Validate shift range
function shiftLeft(uint256 value, uint256 shift) pure returns (uint256) {
    require(shift < 256, "shift overflow");
    return value << shift;
}
```

### Overflow Assumptions

```solidity theme={null}
// WRONG: Assuming shifted value is always larger
function packData(uint96 flags, uint160 addr) pure returns (uint256) {
    uint256 packed = (uint256(flags) << 160) | uint256(addr);
    require(packed > addr, "packing failed");  // FALSE if flags = 0!
    return packed;
}

// CORRECT: Proper validation
function packData(uint96 flags, uint160 addr) pure returns (uint256) {
    return (uint256(flags) << 160) | uint256(addr);
    // No assumption about relative magnitude
}
```

### Data Loss from Overflow

```solidity theme={null}
// DANGEROUS: High bits silently discarded
function encode(uint128 high, uint128 low) pure returns (uint256) {
    // If high > type(uint128).max, upper bits are lost!
    return (uint256(high) << 128) | uint256(low);
}

// SAFER: Validate inputs
function encode(uint128 high, uint128 low) pure returns (uint256) {
    // Type system enforces high <= type(uint128).max
    return (uint256(high) << 128) | uint256(low);
}
```

### Incorrect Multiplication

```solidity theme={null}
// Use SHL only for powers of 2
function multiply(uint256 a, uint256 b) pure returns (uint256) {
    // WRONG: Only works if b is a power of 2
    return a << b;  // Treats b as exponent, not multiplicand!
}

// CORRECT: Use MUL for general multiplication
function multiply(uint256 a, uint256 b) pure returns (uint256) {
    return a * b;
}
```

## Benchmarks

SHL is one of the fastest EVM operations:

**Execution time (relative):**

* SHL: 1.0x (baseline, fastest tier)
* SHR/SAR: 1.0x (same tier)
* MUL: 1.2x
* DIV: 2.5x

**Gas comparison (left shift by 8):**

| Method                | Gas | Notes            |
| --------------------- | --- | ---------------- |
| SHL (Constantinople+) | 3   | Native shift     |
| MUL (pre-EIP-145)     | 5   | value \* 256     |
| EXP + MUL (variable)  | 65+ | value \* 2^shift |

**Gas savings:** 2-1612 gas per shift vs pre-EIP-145 methods.

## References

* [EIP-145](https://eips.ethereum.org/EIPS/eip-145) - Bitwise shifting instructions in EVM
* [Yellow Paper](https://ethereum.github.io/yellowpaper/paper.pdf) - Section 9.4.1
* [EVM Codes - SHL](https://www.evm.codes/#1b)

## Related Documentation

* [SHR (0x1c)](/evm/instructions/bitwise/shr) - Logical shift right
* [SAR (0x1d)](/evm/instructions/bitwise/sar) - Arithmetic shift right
* [MUL (0x02)](/evm/instructions/arithmetic/mul) - Multiplication
* [Hardfork](/primitives/hardfork) - Constantinople
