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

# Fusion Detection

> Identify multi-instruction patterns for optimization and static analysis

<Card title="Try it Live" icon="play" href="https://playground.tevm.sh?example=primitives/bytecode.ts">
  Run Bytecode examples in the interactive playground
</Card>

Fusion detection identifies multi-instruction patterns that can be optimized, statically analyzed, or replaced with synthetic opcodes. These patterns are common in compiler-generated bytecode and provide opportunities for performance improvements and deeper analysis.

## What are Fusions?

**Fusions** are sequences of 2-4 EVM instructions that:

1. **Occur frequently** in compiler output (Solidity, Vyper, etc.)
2. **Can be optimized** by combining into single operations
3. **Enable static analysis** (e.g., PUSH+JUMP = static jump target)
4. **Reveal semantics** (e.g., function dispatch, callvalue checks)

### Example

```solidity theme={null}
// Solidity: x = x + 5
```

Compiles to:

```
PUSH1 0x05    // Push immediate value 5
ADD           // Add to stack top
```

This **PUSH+ADD fusion** can be detected and:

* Optimized to single "add immediate" operation
* Recognized as constant addition pattern
* Analyzed for gas savings

## Fusion Categories

### 1. Arithmetic Fusions

Immediate arithmetic operations:

| Pattern      | Bytecode            | Meaning            |
| ------------ | ------------------- | ------------------ |
| **PUSH+ADD** | `PUSH value`, `ADD` | Add immediate      |
| **PUSH+SUB** | `PUSH value`, `SUB` | Subtract immediate |
| **PUSH+MUL** | `PUSH value`, `MUL` | Multiply immediate |
| **PUSH+DIV** | `PUSH value`, `DIV` | Divide immediate   |

**Example:**

```typescript theme={null}
// Detect PUSH+ADD
for (const inst of code.scan({ detectFusions: true })) {
  if (inst.type === 'push_add_fusion') {
    console.log(`Add immediate: +${inst.value} at PC ${inst.pc}`);
  }
}
```

### 2. Bitwise Fusions

Immediate bitwise operations:

| Pattern      | Bytecode            | Meaning               |
| ------------ | ------------------- | --------------------- |
| **PUSH+AND** | `PUSH value`, `AND` | Bitwise AND with mask |
| **PUSH+OR**  | `PUSH value`, `OR`  | Bitwise OR with mask  |
| **PUSH+XOR** | `PUSH value`, `XOR` | Bitwise XOR with mask |

**Common use**: Masking (e.g., `PUSH 0xFF, AND` = mask to byte)

### 3. Memory Fusions

Immediate memory access:

| Pattern          | Bytecode                 | Meaning                     |
| ---------------- | ------------------------ | --------------------------- |
| **PUSH+MLOAD**   | `PUSH offset`, `MLOAD`   | Load from fixed address     |
| **PUSH+MSTORE**  | `PUSH offset`, `MSTORE`  | Store to fixed address      |
| **PUSH+MSTORE8** | `PUSH offset`, `MSTORE8` | Store byte to fixed address |

**Example:**

```typescript theme={null}
for (const inst of code.scan({ detectFusions: true })) {
  if (inst.type === 'push_mstore_fusion') {
    console.log(`Store to memory[${inst.value}]`);
  }
}
```

### 4. Control Flow Fusions

Static control flow:

| Pattern               | Bytecode                         | Meaning                                      |
| --------------------- | -------------------------------- | -------------------------------------------- |
| **PUSH+JUMP**         | `PUSH target`, `JUMP`            | **Static jump** (target known!)              |
| **PUSH+JUMPI**        | `PUSH target`, `JUMPI`           | Conditional jump to known target             |
| **ISZERO+PUSH+JUMPI** | `ISZERO`, `PUSH target`, `JUMPI` | Inverted conditional (Solidity `if (!cond)`) |

<Tip title="Static Jump Detection">
  **PUSH+JUMP** fusions reveal compile-time jump targets. This enables:

  * Control flow graph construction without execution
  * Dead code detection
  * Function boundary identification
  * Jump target validation at compile time
</Tip>

**Example:**

```typescript theme={null}
// Extract all static jump targets
const jumpTargets = new Set<bigint>();

for (const inst of code.scan({ detectFusions: true })) {
  if (inst.type === 'push_jump_fusion') {
    jumpTargets.add(inst.value);
    console.log(`Static jump to PC ${inst.value}`);
  }
}

// Validate targets are JUMPDEST
jumpTargets.forEach(target => {
  const instruction = code.getInstructionAt(Number(target));
  if (instruction?.opcode !== 'JUMPDEST') {
    console.error(`Invalid jump target: ${target} is not JUMPDEST`);
  }
});
```

### 5. Stack Manipulation Fusions

Complex stack patterns:

| Pattern              | Bytecode                       | Meaning                    |
| -------------------- | ------------------------------ | -------------------------- |
| **DUP2+MSTORE+PUSH** | `DUP2`, `MSTORE`, `PUSH value` | Memory write pattern       |
| **DUP3+ADD+MSTORE**  | `DUP3`, `ADD`, `MSTORE`        | Offset calculation + store |
| **SWAP1+DUP2+ADD**   | `SWAP1`, `DUP2`, `ADD`         | Stack rearrange + add      |
| **PUSH+DUP3+ADD**    | `PUSH value`, `DUP3`, `ADD`    | Immediate + dup + add      |
| **PUSH+ADD+DUP1**    | `PUSH value`, `ADD`, `DUP1`    | Add immediate + duplicate  |
| **MLOAD+SWAP1+DUP2** | `MLOAD`, `SWAP1`, `DUP2`       | Load + rearrange           |

These patterns appear in:

* ABI encoding/decoding
* Struct field access
* Array element computation
* Memory copying

### 6. Multi-Instruction Fusions

Sequences of same instruction:

| Pattern         | Bytecode                         | Meaning           |
| --------------- | -------------------------------- | ----------------- |
| **MULTI\_PUSH** | `PUSH1`, `PUSH1`, `PUSH1` (2-3x) | Batch push values |
| **MULTI\_POP**  | `POP`, `POP`, `POP` (2-3x)       | Batch pop values  |

**Example:**

```typescript theme={null}
for (const inst of code.scan({ detectFusions: true })) {
  if (inst.type === 'multi_push') {
    console.log(`Push ${inst.count} values: ${inst.values.join(', ')}`);
  }
}
```

### 7. Solidity-Specific Patterns

High-level language patterns:

| Pattern                | Bytecode                                       | Meaning                 |
| ---------------------- | ---------------------------------------------- | ----------------------- |
| **FUNCTION\_DISPATCH** | `PUSH4 selector`, `EQ`, `PUSH target`, `JUMPI` | Function selector check |
| **CALLVALUE\_CHECK**   | `CALLVALUE`, `DUP1`, `ISZERO`                  | Non-payable check       |
| **PUSH0+REVERT**       | `PUSH0`, `PUSH0`, `REVERT`                     | Empty revert (no error) |

#### FUNCTION\_DISPATCH

Extracts function selectors from Solidity function dispatcher:

```typescript theme={null}
const functions = new Map<number, bigint>();

for (const inst of code.scan({ detectFusions: true })) {
  if (inst.type === 'function_dispatch') {
    const selector = inst.selector;
    const target = inst.target;

    functions.set(selector, target);
    console.log(`Function 0x${selector.toString(16)} at PC ${target}`);
  }
}

console.log(`\nFound ${functions.size} functions`);
```

This enables:

* **ABI reconstruction** from bytecode alone
* **Function boundary detection** for decompilation
* **Selector collision detection**
* **Gas profiling per function**

#### CALLVALUE\_CHECK

Detects non-payable function checks:

```typescript theme={null}
for (const inst of code.scan({ detectFusions: true })) {
  if (inst.type === 'callvalue_check') {
    console.log(`Non-payable check at PC ${inst.pc}`);
  }
}
```

Indicates Solidity function without `payable` modifier.

## Detection API

<Tabs>
  <Tab title="TypeScript">
    Fusion detection integrates with iteration:

    ```typescript theme={null}
    // Enable fusion detection
    for (const inst of code.scan({ detectFusions: true })) {
      // inst.type includes fusion types
      switch (inst.type) {
        case 'push_add_fusion':
          console.log(`Add ${inst.value}`);
          break;

        case 'push_jump_fusion':
          console.log(`Static jump to ${inst.value}`);
          break;

        case 'function_dispatch':
          console.log(`Function ${inst.selector.toString(16)}`);
          break;

        default:
          console.log(`Regular: ${inst.type}`);
      }
    }
    ```
  </Tab>
</Tabs>

### Options

```typescript theme={null}
interface ScanOptions {
  /** Enable fusion pattern detection (default: false) */
  detectFusions?: boolean

  /** Minimum fusion length to detect (default: 2) */
  minFusionLength?: number

  /** Maximum fusion length to detect (default: 4) */
  maxFusionLength?: number

  /** Specific fusion types to detect (default: all) */
  fusionTypes?: FusionType[]
}
```

### Selective Detection

```typescript theme={null}
// Only detect control flow fusions
for (const inst of code.scan({
  detectFusions: true,
  fusionTypes: ['push_jump_fusion', 'push_jumpi_fusion', 'iszero_jumpi']
})) {
  if (inst.type.includes('jump')) {
    console.log(`Control flow: ${inst.type}`);
  }
}
```

## Usage Patterns

### Fusion Statistics

```typescript theme={null}
const fusionCounts = new Map<string, number>();

for (const inst of code.scan({ detectFusions: true })) {
  if (inst.type.includes('fusion') || inst.type.includes('dispatch') || inst.type.includes('check')) {
    const count = fusionCounts.get(inst.type) || 0;
    fusionCounts.set(inst.type, count + 1);
  }
}

console.log('Fusion patterns detected:');
Array(fusionCounts.entries())
  .sort((a, b) => b[1] - a[1])
  .forEach(([type, count]) => {
    console.log(`  ${type}: ${count}x`);
  });
```

### Optimization Opportunities

```typescript theme={null}
let gasSavings = 0;

for (const inst of code.scan({ detectFusions: true })) {
  switch (inst.type) {
    case 'push_add_fusion':
    case 'push_mul_fusion':
    case 'push_sub_fusion':
      // Each fusion saves ~3 gas (one instruction)
      gasSavings += 3;
      break;

    case 'multi_push':
      // Batch operations save gas
      gasSavings += (inst.count - 1) * 2;
      break;

    case 'push_jump_fusion':
      // Static jumps can be optimized further
      gasSavings += 5;
      break;
  }
}

console.log(`Potential gas savings: ${gasSavings} gas`);
```

### Control Flow Graph from Fusions

```typescript theme={null}
// Build CFG using static jump detection
const cfg = new Map<number, number[]>(); // PC → successor PCs

let currentBlock = 0;
const successors: number[] = [];

for (const inst of code.scan({ detectFusions: true })) {
  if (inst.type === 'push_jump_fusion') {
    // Static jump - add edge
    successors.push(Number(inst.value));
    cfg.set(currentBlock, [...successors]);
    currentBlock = inst.pc + inst.original_length;
    successors.length = 0;
  } else if (inst.type === 'push_jumpi_fusion') {
    // Conditional - add both edges
    successors.push(Number(inst.value)); // Jump target
    successors.push(inst.pc + inst.original_length); // Fallthrough
    cfg.set(currentBlock, [...successors]);
    currentBlock = inst.pc + inst.original_length;
    successors.length = 0;
  } else if (inst.type === 'jumpdest') {
    currentBlock = inst.pc;
  }
}

console.log('Control flow graph:');
cfg.forEach((succs, pc) => {
  console.log(`  PC ${pc} → [${succs.join(', ')}]`);
});
```

### Function Extraction

```typescript theme={null}
// Extract function bodies using dispatch pattern
const functions = new Map<number, { start: number; end: number }>();

for (const inst of code.scan({ detectFusions: true })) {
  if (inst.type === 'function_dispatch') {
    const selector = inst.selector;
    const start = Number(inst.target);

    // Find end (next JUMPDEST or end of code)
    let end = code.size();
    for (const next of code.scan()) {
      if (next.pc > start && next.type === 'jumpdest') {
        end = next.pc;
        break;
      }
    }

    functions.set(selector, { start, end });
  }
}

// Extract each function's bytecode
functions.forEach(({ start, end }, selector) => {
  const functionCode = code.slice(start, end);
  console.log(`\nFunction 0x${selector.toString(16)}:`);
  console.log(functionCode.prettyPrint({ compact: true }));
});
```

### Pattern Frequency Analysis

```typescript theme={null}
// Identify most common patterns for optimization priority
const patterns = new Map<string, { count: number; gasCost: number }>();

for (const inst of code.scan({ detectFusions: true })) {
  if (inst.type.includes('fusion')) {
    const entry = patterns.get(inst.type) || { count: 0, gasCost: 0 };

    entry.count++;

    // Estimate gas for pattern
    switch (inst.type) {
      case 'push_add_fusion':
      case 'push_sub_fusion':
      case 'push_mul_fusion':
        entry.gasCost += 6; // PUSH(3) + ADD/SUB/MUL(3)
        break;
      case 'push_jump_fusion':
        entry.gasCost += 11; // PUSH(3) + JUMP(8)
        break;
      // ... other patterns
    }

    patterns.set(inst.type, entry);
  }
}

// Sort by total gas impact
const sorted = Array(patterns.entries())
  .map(([type, { count, gasCost }]) => ({
    type,
    count,
    totalGas: gasCost,
    avgGas: gasCost / count
  }))
  .sort((a, b) => b.totalGas - a.totalGas);

console.log('Patterns by gas impact:');
sorted.forEach(({ type, count, totalGas }) => {
  console.log(`  ${type}: ${count}x (${totalGas} gas total)`);
});
```

## Integration with Other APIs

### With prettyPrint

Pretty print annotates fusions with ⚡ symbol:

```typescript theme={null}
const output = code.prettyPrint({
  showFusions: true,
  colors: true
});

console.log(output);

// Output:
//    1 |    0 | ⚡ 60 01  | PUSH1     | 0x1 ⚡ PUSH+ADD
//    2 |    2 |    01     | ADD       | [gas: 3]
```

### With analyzeBlocks

Detect fusions within blocks:

```typescript theme={null}
const blocks = code.analyzeBlocks();

blocks.forEach(block => {
  const blockFusions: OpcodeData[] = [];

  for (const inst of code.scan({ detectFusions: true })) {
    if (inst.pc >= block.startPc && inst.pc < block.endPc) {
      if (inst.type.includes('fusion')) {
        blockFusions.push(inst);
      }
    }
  }

  if (blockFusions.length > 0) {
    console.log(`Block ${block.index}: ${blockFusions.length} fusions`);
    blockFusions.forEach(f => console.log(`  ${f.type} at PC ${f.pc}`));
  }
});
```

### With analyzeGas

Estimate gas savings from fusion optimization:

```typescript theme={null}
const gasAnalysis = code.analyzeGas();
let fusionGasSavings = 0;

for (const inst of code.scan({ detectFusions: true })) {
  if (inst.type.includes('fusion')) {
    // Each fusion saves 1-2 instructions worth of gas
    fusionGasSavings += 3;
  }
}

const optimizedGas = Number(gasAnalysis.total) - fusionGasSavings;
console.log(`Current gas: ${gasAnalysis.total}`);
console.log(`Potential savings: ${fusionGasSavings}`);
console.log(`Optimized gas: ${optimizedGas}`);
```

## Advanced Patterns

### Custom Fusion Detection

Implement custom pattern matching:

```typescript theme={null}
function detectCustomPattern(code: BrandedBytecode): CustomPattern[] {
  const patterns: CustomPattern[] = [];

  // Collect instructions for lookahead
  const instructions = Array(code.scan({ detectFusions: true }));

  for (let i = 0; i < instructions.length; i++) {
    const inst1 = instructions[i];
    const inst2 = instructions[i + 1];
    const inst3 = instructions[i + 2];
    const inst4 = instructions[i + 3];

    // Custom pattern: PUSH1 0x40, MLOAD, PUSH1 0x20, ADD
    // (Common "get free memory pointer + 32" pattern)
    if (
      inst1.type === 'push' && inst1.value === 0x40n &&
      inst2?.opcode === 'MLOAD' &&
      inst3?.type === 'push' && inst3.value === 0x20n &&
      inst4?.opcode === 'ADD'
    ) {
      patterns.push({
        type: 'free_memory_alloc',
        pc: inst1.pc,
        size: 32
      });
    }
  }

  return patterns;
}
```

### Fusion-Based Decompilation

Use fusions to identify high-level constructs:

```typescript theme={null}
interface HighLevelConstruct {
  type: 'function' | 'modifier' | 'constructor' | 'fallback';
  selector?: number;
  startPc: number;
  endPc: number;
  hasCallvalueCheck: boolean;
}

function identifyConstructs(code: BrandedBytecode): HighLevelConstruct[] {
  const constructs: HighLevelConstruct[] = [];
  let currentConstruct: Partial<HighLevelConstruct> | null = null;

  for (const inst of code.scan({ detectFusions: true })) {
    if (inst.type === 'function_dispatch') {
      if (currentConstruct) {
        constructs.push(currentConstruct as HighLevelConstruct);
      }

      currentConstruct = {
        type: 'function',
        selector: inst.selector,
        startPc: Number(inst.target),
        endPc: 0,
        hasCallvalueCheck: false
      };
    } else if (inst.type === 'callvalue_check' && currentConstruct) {
      currentConstruct.hasCallvalueCheck = true;
    }
  }

  return constructs;
}
```

### Compiler Fingerprinting

Different compilers generate different fusion patterns:

```typescript theme={null}
interface CompilerFingerprint {
  name: string;
  version?: string;
  confidence: number;
}

function detectCompiler(code: BrandedBytecode): CompilerFingerprint {
  const patterns = {
    push0Revert: 0,
    functionDispatch: 0,
    callvalueCheck: 0,
    multiPush: 0
  };

  for (const inst of code.scan({ detectFusions: true })) {
    if (inst.type === 'push0_revert') patterns.push0Revert++;
    if (inst.type === 'function_dispatch') patterns.functionDispatch++;
    if (inst.type === 'callvalue_check') patterns.callvalueCheck++;
    if (inst.type === 'multi_push') patterns.multiPush++;
  }

  // Solidity 0.8.0+ uses PUSH0 (EIP-3855)
  if (patterns.push0Revert > 0 && patterns.functionDispatch > 0) {
    return {
      name: 'Solidity',
      version: '>=0.8.0',
      confidence: 0.95
    };
  }

  // Vyper tends to have fewer fusion opportunities
  if (patterns.functionDispatch === 0 && patterns.multiPush > 5) {
    return {
      name: 'Vyper',
      confidence: 0.7
    };
  }

  return { name: 'Unknown', confidence: 0.0 };
}
```

## Performance

### Detection Overhead

Fusion detection adds minimal overhead:

* **Disabled**: \~0.5ms per 1KB bytecode
* **Enabled**: \~0.8ms per 1KB bytecode
* **Overhead**: \~60% (still sub-millisecond for most contracts)

<Tip>
  Enable fusion detection only when needed. For simple iteration, leave disabled for maximum performance.
</Tip>

### Caching

Fusion analysis is deterministic - results can be cached:

```typescript theme={null}
const fusionCache = new WeakMap<BrandedBytecode, OpcodeData[]>();

function getFusions(code: BrandedBytecode): OpcodeData[] {
  if (fusionCache.has(code)) {
    return fusionCache.get(code)!;
  }

  const fusions: OpcodeData[] = [];
  for (const inst of code.scan({ detectFusions: true })) {
    if (inst.type.includes('fusion') || inst.type.includes('dispatch')) {
      fusions.push(inst);
    }
  }

  fusionCache.set(code, fusions);
  return fusions;
}
```

## Limitations

<Warning title="Pattern Matching Limitations">
  Fusion detection is based on **sequential pattern matching** and cannot:

  * **Detect patterns across basic blocks** - Limited to same block
  * **Handle data dependencies** - Only structural patterns
  * **Account for runtime values** - Only compile-time constants
  * **Detect semantically equivalent variants** - Only exact patterns

  Results represent **syntactic patterns**, not semantic equivalence.
</Warning>

### What's Detected

✅ Sequential instruction patterns (2-4 instructions)
✅ Immediate values in PUSH instructions
✅ Static jump targets
✅ Function selectors (4-byte constants)
✅ Common compiler idioms

### What's Not Detected

❌ Semantically equivalent but structurally different patterns
❌ Patterns spanning multiple basic blocks
❌ Data-dependent patterns
❌ Runtime-computed patterns

## Use Cases

### 1. Bytecode Optimization

Identify patterns for optimization passes:

```typescript theme={null}
const optimizations: string[] = [];

for (const inst of code.scan({ detectFusions: true })) {
  if (inst.type === 'push_add_fusion' && inst.value === 1n) {
    optimizations.push(`INC at PC ${inst.pc}`); // ADD 1 → INC
  }

  if (inst.type === 'push_mul_fusion' && inst.value === 2n) {
    optimizations.push(`SHL at PC ${inst.pc}`); // MUL 2 → SHL 1
  }
}

console.log(`Found ${optimizations.length} optimization opportunities`);
```

### 2. Security Analysis

Detect suspicious patterns:

```typescript theme={null}
const suspiciousPatterns: string[] = [];

for (const inst of code.scan({ detectFusions: true })) {
  // Multiple callvalue checks (unusual)
  if (inst.type === 'callvalue_check') {
    suspiciousPatterns.push('Multiple callvalue checks detected');
  }

  // Function dispatch with very high selector (collision attempt?)
  if (inst.type === 'function_dispatch' && inst.selector > 0xFFFFFF00) {
    suspiciousPatterns.push(`Suspicious selector: 0x${inst.selector.toString(16)}`);
  }
}
```

### 3. Reverse Engineering

Reconstruct contract structure:

```typescript theme={null}
// Build function map from fusions
const contractStructure = {
  functions: new Map<number, { pc: number; isPayable: boolean }>(),
  constructor: null as number | null,
  fallback: null as number | null
};

for (const inst of code.scan({ detectFusions: true })) {
  if (inst.type === 'function_dispatch') {
    contractStructure.functions.set(inst.selector, {
      pc: Number(inst.target),
      isPayable: false // Updated by callvalue_check
    });
  }

  if (inst.type === 'callvalue_check') {
    // Mark previous function as non-payable
    // (implementation detail - track context)
  }
}

console.log(`Contract has ${contractStructure.functions.size} functions`);
```

## See Also

* [Instruction Types](/primitives/bytecode/instruction-types) - OpcodeData union with all fusion types
* [scan](/primitives/bytecode/scan) - Iterator with fusion detection
* [Synthetic Opcodes](/primitives/bytecode/synthetic-opcodes) - Extended opcode set
* [analyzeBlocks](/primitives/bytecode/analyze-blocks) - Basic block analysis
* [prettyPrint](/primitives/bytecode/pretty-print) - Visual fusion annotations
