> ## 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.

# MLOAD (0x51)

> Load 32-byte word from memory at given offset

<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:** `0x51`
**Introduced:** Frontier (EVM genesis)

MLOAD reads a 32-byte word from memory at the specified offset. The value is interpreted as a big-endian 256-bit unsigned integer and pushed to the stack. Uninitialized memory reads as zero.

This is the primary mechanism for reading arbitrary data from memory during execution.

## Specification

**Stack Input:**

```
offset (top)
```

**Stack Output:**

```
value  (32 bytes read at offset, as uint256)
```

**Gas Cost:** 3 + memory expansion cost

**Operation:**

```
value = memory[offset:offset+32]  (big-endian)
```

## Behavior

MLOAD pops an offset from the stack, reads 32 bytes starting at that offset, and pushes the result as a 256-bit value.

* Offset is interpreted as unsigned 256-bit integer (max 2^256 - 1)
* Reads exactly 32 bytes (1 word)
* Uninitialized bytes read as 0x00
* Memory automatically expands to accommodate read (quadratic cost)
* Bytes are combined in big-endian order (byte 0 = most significant)

## Examples

### Basic Load

```typescript theme={null}
import { mload } from '@tevm/voltaire/evm/instructions/memory';
import { createFrame } from '@tevm/voltaire/evm/Frame';

const frame = createFrame();

// Write test data at offset 0
frame.memory.set(0, 0x12);
frame.memory.set(1, 0x34);
frame.memory.set(2, 0x56);
frame.memory.set(3, 0x78);
// ... bytes 4-31 are zero

frame.stack.push(0n);  // offset
const err = mload(frame);

console.log(frame.stack[0]);  // 0x12345678_00000000_...00000000n
console.log(frame.pc);         // 1 (incremented)
```

### Load from Uninitialized Memory

```typescript theme={null}
const frame = createFrame();
frame.stack.push(0n);  // offset

mload(frame);

// Uninitialized memory reads as zero
console.log(frame.stack[0]);  // 0n
console.log(frame.memorySize);  // 32 (expanded to 1 word)
```

### Load with Non-Zero Offset

```typescript theme={null}
const frame = createFrame();

// Write pattern starting at offset 32
for (let i = 0; i < 32; i++) {
  frame.memory.set(32 + i, i);
}

frame.stack.push(32n);  // offset
mload(frame);

// Result: bytes 0-31 (each byte contains its index)
console.log(frame.stack[0]);  // 0x00010203_...1f n
```

### Multiple Reads

```typescript theme={null}
const frame = createFrame();

// Initialize memory
for (let i = 0; i < 64; i++) {
  frame.memory.set(i, i);
}

// Read word 1 (bytes 0-31)
frame.stack.push(0n);
mload(frame);
const word1 = frame.stack.pop();

// Read word 2 (bytes 32-63)
frame.stack.push(32n);
mload(frame);
const word2 = frame.stack.pop();

console.log(word1 !== word2);  // true (different data)
```

## Gas Cost

**Base cost:** 3 gas (GasFastestStep)

**Memory expansion:** Quadratic based on access range

**Formula:**

```
words_required = ceil((offset + 32) / 32)
expansion_cost = (words_required)² / 512 + 3 * (words_required - words_old)
total_cost = 3 + expansion_cost
```

**Examples:**

* **Reading bytes 0-31:** 1 word, no prior expansion: 3 gas
* **Reading bytes 1-32:** 2 words (rounds up), 1 word prior: 3 + (4 - 1) = 6 gas
* **Reading bytes 0-4095:** \~125 words: 3 + (125² / 512 + expansion) ≈ 3 + 30 = 33 gas

Memory is expensive for large accesses due to quadratic expansion formula.

## Edge Cases

### Byte Alignment

```typescript theme={null}
// Reading at non-word-aligned offset
const frame = createFrame();

// Write at offset 1
frame.memory.set(1, 0xff);
frame.stack.push(1n);
mload(frame);

// Reads bytes 1-32, expands memory to 2 words (64 bytes)
console.log(frame.memorySize);  // 64
console.log(frame.stack[0]);    // 0xff000000_...00000000n
```

### Maximum Offset

```typescript theme={null}
const frame = createFrame({ gasRemaining: 1000000000n });

const maxOffset = BigInt(Number.MAX_SAFE_INTEGER);
frame.stack.push(maxOffset);

const err = mload(frame);
console.log(err);  // May be OutOfGas if expansion is too large
```

### Out of Bounds

```typescript theme={null}
const frame = createFrame({ gasRemaining: 2n });

frame.stack.push(0n);
const err = mload(frame);

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

### Stack Underflow

```typescript theme={null}
const frame = createFrame();

// No offset on stack
const err = mload(frame);
console.log(err);  // { type: "StackUnderflow" }
```

## Common Usage

### Loading ABI-Encoded Data

```solidity theme={null}
// ABI encoding: function selector (4 bytes) + parameters
assembly {
    // calldata is in memory starting at offset 0
    let selector := mload(0)
    // Selector is in high 4 bytes: selector >> 224
    let func := selector
}
```

### Reading Function Parameters

```solidity theme={null}
assembly {
    // Free memory pointer
    let ptr := mload(0x40)

    // Load stored value
    let value := mload(ptr)

    // Load next value
    let next := mload(add(ptr, 0x20))
}
```

### Iterating Memory

```solidity theme={null}
assembly {
    let offset := 0x20
    let ptr := mload(offset)

    // Chain loading
    let first := mload(ptr)
    let second := mload(add(ptr, 0x20))
    let third := mload(add(ptr, 0x40))
}
```

## Memory Safety

**Load safety properties:**

* **No side effects:** Reading memory never modifies state or storage
* **Initialization:** Uninitialized memory safely reads as zero
* **Bounds:** Out-of-bounds reads don't error - they just allocate and charge gas
* **Atomicity:** 32-byte load is atomic (no tearing)

Applications must ensure offset validity:

```solidity theme={null}
// Good: Check bounds before reading
require(offset + 32 <= memorySize, "out of bounds");
let value := mload(offset);

// Bad: Assumes bounds checking
let value := mload(userSuppliedOffset);  // Can read beyond allocated
```

## Implementation

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    /**
     * MLOAD opcode (0x51) - Load 32-byte word from memory
     */
    export function mload(frame: FrameType): EvmError | null {
      // Pop offset from stack
      if (frame.stack.length < 1) {
        return { type: "StackUnderflow" };
      }
      const offset = frame.stack.pop();

      // Cast offset to u32 (check bounds)
      const off = Number(offset);
      if (!Number.isSafeInteger(off) || off < 0) {
        return { type: "OutOfBounds" };
      }

      // Calculate memory expansion
      const endBytes = BigInt(off + 32);
      const expansionCost = calculateMemoryExpansion(endBytes);

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

      // Expand memory
      const alignedSize = Math.ceil((off + 32) / 32) * 32;
      frame.memorySize = Math.max(frame.memorySize, alignedSize);

      // Read 32 bytes in big-endian order
      let result = 0n;
      for (let i = 0; i < 32; i++) {
        const byte = frame.memory.get(off + i) ?? 0;
        result = (result << 8n) | BigInt(byte);
      }

      // Push result to stack
      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 { mload } from './0x51_MLOAD.js';

describe('MLOAD (0x51)', () => {
  it('loads 32 bytes from memory', () => {
    const frame = createFrame();
    for (let i = 0; i < 32; i++) {
      frame.memory.set(i, i + 1);
    }
    frame.stack.push(0n);

    expect(mload(frame)).toBeNull();
    expect(frame.stack[0]).toBe(0x0102030405...n);
    expect(frame.pc).toBe(1);
  });

  it('loads from uninitialized memory as zero', () => {
    const frame = createFrame();
    frame.stack.push(0n);
    mload(frame);

    expect(frame.stack[0]).toBe(0n);
    expect(frame.memorySize).toBe(32);
  });

  it('expands memory to word boundary', () => {
    const frame = createFrame();
    frame.stack.push(1n);  // Offset 1 -> bytes 1-32 = 2 words

    mload(frame);

    expect(frame.memorySize).toBe(64);
  });

  it('charges correct gas for expansion', () => {
    const frame = createFrame({ gasRemaining: 1000n });
    frame.stack.push(0n);

    mload(frame);

    // 3 base + memory expansion
    expect(frame.gasRemaining).toBeLessThan(1000n);
  });

  it('returns StackUnderflow when empty', () => {
    const frame = createFrame();
    expect(mload(frame)).toEqual({ type: "StackUnderflow" });
  });

  it('returns OutOfGas when insufficient', () => {
    const frame = createFrame({ gasRemaining: 2n });
    frame.stack.push(0n);

    expect(mload(frame)).toEqual({ type: "OutOfGas" });
  });
});
```

### Edge Cases Tested

* Basic load (32 bytes)
* Uninitialized memory (zeros)
* Non-zero offset with word boundary alignment
* Memory expansion costs
* Stack underflow/overflow
* Out of gas conditions
* Endianness verification

## Security Considerations

### Memory Disclosure

Memory is transaction-scoped and doesn't persist to state. However, careful handling needed:

```solidity theme={null}
// Good: Clear sensitive data
assembly {
    mstore(offset, 0)  // Clear temporary value
}

// Risky: Sensitive data in memory
assembly {
    let privateKey := mload(0x80)  // Don't do this
}
```

### Out-of-Bounds Reads

Memory expands automatically - reading beyond allocated areas is safe but expensive:

```solidity theme={null}
// Safe but expensive
let value := mload(1000000)  // Quadratic gas cost to expand

// Better: Validate before reading
require(offset < endOfData, "invalid offset");
let value := mload(offset);
```

## Benchmarks

MLOAD is among the fastest EVM operations:

**Relative performance:**

* MLOAD (initialized): 1.0x baseline
* MLOAD (uninitialized): 1.0x baseline
* MSTORE: 1.0x (similar cost)
* SLOAD: 100x slower (storage vs memory)

**Gas scaling:**

* First word: 3 gas
* Second word: 3 gas (expansion ≈ 3)
* Large memory: Quadratic scaling beyond practical use

## References

* **[Yellow Paper](https://ethereum.github.io/yellowpaper/paper.pdf)** - Section 9.2 (Memory)
* **[evm.codes - MLOAD](https://www.evm.codes/#51)** - Interactive reference
* **[Solidity Docs](https://docs.soliditylang.org/)** - Assembly and memory layout
