# Ignition: V8's Interpreter

Ignition is V8's interpreter. It is the first tier in V8's execution pipeline, responsible for executing JavaScript code quickly after parsing without waiting for expensive compilation. For a high-level overview of Ignition, see the [V8 blog post](https://v8.dev/blog/ignition-interpreter). It was originally written using the backend of [TurboFan](../compiler/turbofan/compiler-turbofan.md).

## Architecture & Design

Ignition is designed as a high-performance interpreter with several key architectural choices:

### 1. Register-Based with Accumulator
Unlike stack-based interpreters (like the JVM) that push and pop values from an expression stack, Ignition uses a **register file** mapped to the native stack.
*   **Virtual Registers**: Function locals, parameters, and temporaries are mapped to fixed slots in the stack frame.
*   **The Accumulator**: To keep bytecodes compact, V8 uses a dedicated **accumulator register**. Many operations implicitly read from or write to the accumulator, avoiding the need to specify register indices in the bytecode operands.

### 2. Fixed-Size Stack Frame
Ignition uses a fixed-size stack frame for a given function, calculated at compile time by the `BytecodeGenerator`. The frame layout (defined in `src/execution/frame-constants.h`) includes (from high to low addresses):
*   **Parameters**: Passed by the caller (negative offsets from FP).
*   **Fixed Header**: Return address, saved frame pointer (FP points here), [Constant Pool] (optional), Context, JSFunction, and argument count (argc).
*   **Unoptimized Header**:
    *   `BytecodeArray`: Pointer to the bytecode being executed.
    *   `offset / cell`: The current bytecode offset or FeedbackCell.
    *   `FBV`: Pointer to the `FeedbackVector`.
*   **Register File**: The virtual registers used by the function (positive offsets from FP).

### 3. Threaded Dispatch
Ignition does not use a centralized `switch` statement in a loop for interpretation. Instead, it uses **threaded dispatch**:
*   Every bytecode handler is a standalone chunk of machine code.
*   At the end of each handler, it fetches the next bytecode, looks up its handler address in a **dispatch table** (stored in a dedicated CPU register for fast access), and directly jumps to it.
*   This minimizes branch mispredictions compared to a giant switch loop.

### 4. Wide and Extra-Wide Bytecodes
To keep the common case compact, standard bytecodes use 8-bit operands (supporting up to 256 registers or constant pool indices). However, large functions may exceed these limits.
*   V8 solves this using **prefix bytecodes**: `Wide` and `ExtraWide`.
*   If a bytecode needs to reference a register index `> 255`, the generator emits a `Wide` prefix followed by the bytecode. The `Wide` prefix signals the interpreter to read 16-bit operands for the next instruction.
*   `ExtraWide` allows for 32-bit operands.

### 5. The Constant Pool
Complex objects, large integers, and strings are not embedded directly in the bytecode stream. Instead, they are stored in a `FixedArray` called the **Constant Pool** attached to the `BytecodeArray`. Bytecodes reference constants via their index in this pool. The `ConstantArrayBuilder` manages this, placing constants in 8-bit, 16-bit, or 32-bit slices based on frequency and size, coordinating with Wide/ExtraWide bytecodes.

## Bytecode Example

To understand how Ignition executes code, let's look at a concrete JavaScript function and the bytecode V8 generates for it.

```javascript
function foo(obj, x) {
  if (x > 0) {
    return obj.y + x;
  }
  return 0;
}
```

Here is the generated bytecode (simplified for readability):

```text
         0 : LdaZero                           // Accumulator = 0
         1 : TestGreaterThan a1, [0]           // Compare a1 (x) with accumulator (0)
         5 : JumpIfFalse [13] (at 18)          // If false, jump to offset 18
         7 : GetNamedProperty a0, [0], [1]     // Load obj.y into accumulator (a0 is obj, [0] is name "y")
        11 : Star0                             // Store accumulator (obj.y) in register r0
        12 : Ldar a1                           // Load a1 (x) into accumulator
        14 : Add r0, [0]                       // Add r0 (obj.y) to accumulator (x)
        17 : Return                            // Return accumulator
        18 : LdaZero                           // Accumulator = 0
        19 : Return                            // Return accumulator
```

### Explanation of Bytecodes:
*   **`LdaZero`**: Loads the constant `0` into the accumulator.
*   **`TestGreaterThan a1, [0]`**: Compares the value in register `a1` (parameter `x`) with the accumulator (`0`). The `[0]` is a feedback slot index for the Inline Cache.
*   **`JumpIfFalse [13]`**: If the previous test was false, jumps `13` bytes forward to offset `18`.
*   **`GetNamedProperty a0, [0], [1]`**: Loads a named property from object in `a0` (`obj`). `[0]` is the index in the constant pool for the string `"y"`. `[1]` is the feedback slot. The result is placed in the accumulator.
*   **`Star0`**: Stores the value in the accumulator into virtual register `r0`.
*   **`Ldar a1`**: Loads the value from register `a1` (`x`) into the accumulator.
*   **`Add r0, [0]`**: Adds the value in `r0` to the accumulator. `[0]` is the feedback slot.
*   **`Return`**: Returns the value currently in the accumulator.

## Bytecode Generation

The process of turning JavaScript source code into bytecode is handled by the `BytecodeGenerator` in `src/interpreter/bytecode-generator.cc`.

### The AST Visitor

`BytecodeGenerator` is an Abstract Syntax Tree (AST) visitor. It inherits from `AstVisitor<BytecodeGenerator>` and implements `Visit...` methods for all AST node types (e.g., `VisitBlock`, `VisitBinaryOperation`, `VisitCall`).

When `GenerateBytecode` is called:
1.  It traverses the AST starting from the root function literal.
2.  For each node, it calls the corresponding `Visit` method.
3.  The `Visit` method emits bytecodes using the `BytecodeArrayBuilder`.

### Registers and The Accumulator

*   **Register Allocation**: The generator manages a set of virtual registers. It uses a `BytecodeRegisterAllocator` to allocate and free registers as it evaluates expressions.
*   **Accumulator Optimization**: The generator tries to keep values in the accumulator as much as possible to reduce bytecode size. For example, when evaluating `a + b`:
    1.  Visit `a` for accumulator value (loads `a` into accumulator).
    2.  Store accumulator to a temporary register `r0`.
    3.  Visit `b` for accumulator value (loads `b` into accumulator).
    4.  Emit `Add r0`, which adds `r0` (containing `a`) to the accumulator (containing `b`).

### Control Flow

*   **Labels**: For jumps (loops, conditionals), the generator uses `BytecodeLabel` to mark jump targets. The `BytecodeArrayWriter` patches these labels with actual offsets later.
*   **Scopes**: The generator tracks the current lexical scope and emits bytecodes to create and link context objects (`LdaContext`, `StaContext`) when entering blocks with new variables.

---

## Optimizations

*   **Star Lookahead**: Ignition includes optimizations like `StarDispatchLookahead`. It checks if the next bytecode is a short `Star` bytecode (storing accumulator to register). If so, it can inline the effect to avoid a full dispatch.

## Example Bytecodes

Here are a few examples of Ignition bytecodes (defined in `src/interpreter/bytecodes.h`):

*   `LdaSmi <imm>`: Load a Small Integer immediate into the accumulator.
*   `Star <r>`: Store the value in the accumulator into register `<r>`.
*   `Ldar <r>`: Load the value from register `<r>` into the accumulator.
*   `Add <r>, [slot]`: Add the value in register `<r>` to the accumulator, updating feedback in the specified slot.
*   `CallProperty <r>, <r_list>, <r_count>, [slot]`: Call a property on an object.

## Interaction with Feedback

As Ignition executes bytecode, it collects type feedback for operations that can benefit from optimization (like property accesses and arithmetic).

*   The bytecode contains indices to slots in the `FeedbackVector`.
*   Handlers write feedback to these slots (e.g., recording the `Map` of an object seen at a property load).
*   This feedback is later used by Maglev and TurboFan to generate optimized code.

## File Structure
*   `src/interpreter/interpreter.cc`: Setup code and dispatch table initialization.
*   `src/interpreter/interpreter-assembler.cc`: Contains the actual bytecode handler implementations (using CodeStubAssembler).
*   `src/interpreter/bytecode-generator.cc`: Generates bytecode from AST.
*   `src/interpreter/bytecodes.h`: Defines all bytecodes and their properties.

## External Resources

### Talks
- [V8: Hooking up the Ignition to the Turbofan](https://docs.google.com/presentation/d/1chhN90uB8yPaIhx_h2M3lPyxPgdPmkADqSNAoXYQiVE/edit)
- [Ignition: jump-starting an interpreter for V8](https://docs.google.com/presentation/d/1HgDDXBYqCJNasBKBDf9szap1j4q4wnSHhOYpaNy5mHU/edit#slide=id.g1357e6d1a4_0_58)
- [Ignition: an interpreter for V8](https://docs.google.com/presentation/d/1OqjVqRhtwlKeKfvMdX6HaCIu9wpZsrzqpIVIwQSuiXQ/edit) ([video](https://youtu.be/r5OWCtuKiAk))

### Articles
- [Understanding V8’s bytecode](https://medium.com/dailyjs/understanding-v8s-bytecode-317d46c94775)

### Design Docs
- [Ignition design document](https://docs.google.com/document/d/11T2CRex9hXxoJwbYqVQ32yIPMh0uouUZLdyrtmMoL44/edit?ts=56f27d9d#heading=h.6jz9dj3bnr8t)
- [Register equivalence optimization](https://docs.google.com/document/d/1wW_VkkIwhAAgAxLYM0wvoTEkq8XykibDIikGpWH7l1I/edit?ts=570d7131#heading=h.6jz9dj3bnr8t)

## See Also
-   [Hidden Classes and Inline Caches](../runtime/hidden-classes-and-ics.md)
-   [Function Architecture](../runtime/function-architecture.md)
