docs/interpreter/interpreter-ignition.md
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. It was originally written using the backend of TurboFan.
Ignition is designed as a high-performance interpreter with several key architectural choices:
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.
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):
BytecodeArray: Pointer to the bytecode being executed.offset / cell: The current bytecode offset or FeedbackCell.FBV: Pointer to the FeedbackVector.Ignition does not use a centralized switch statement in a loop for interpretation. Instead, it uses threaded dispatch:
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.
Wide and ExtraWide.> 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.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.
To understand how Ignition executes code, let's look at a concrete JavaScript function and the bytecode V8 generates for it.
function foo(obj, x) {
if (x > 0) {
return obj.y + x;
}
return 0;
}
Here is the generated bytecode (simplified for readability):
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
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.The process of turning JavaScript source code into bytecode is handled by the BytecodeGenerator in src/interpreter/bytecode-generator.cc.
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:
Visit method.Visit method emits bytecodes using the BytecodeArrayBuilder.BytecodeRegisterAllocator to allocate and free registers as it evaluates expressions.a + b:
a for accumulator value (loads a into accumulator).r0.b for accumulator value (loads b into accumulator).Add r0, which adds r0 (containing a) to the accumulator (containing b).BytecodeLabel to mark jump targets. The BytecodeArrayWriter patches these labels with actual offsets later.LdaContext, StaContext) when entering blocks with new variables.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.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.As Ignition executes bytecode, it collects type feedback for operations that can benefit from optimization (like property accesses and arithmetic).
FeedbackVector.Map of an object seen at a property load).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.