docs/compiler/turboshaft/compiler-turboshaft-ir.md
This document dives into the details of Turboshaft's Intermediate Representation (IR), focusing on operation definitions and the side-effect system.
Turboshaft is V8's next-generation optimizing compiler. Unlike its predecessor, TurboFan, which used a "Sea-of-Nodes" IR where control and data flow were intermingled, Turboshaft uses a more traditional Control Flow Graph (CFG) representation.
In Turboshaft:
Goto, Branch, Switch).Operations are defined in src/compiler/turboshaft/operations.h. Each operation is represented by a C++ struct (e.g., WordBinopOp, LoadOp) and has a corresponding Opcode.
Turboshaft categorizes operations into several lists:
WordBinop, FloatBinop, Load, Store).CheckedClosure, CheckMaps).GenericBinop).To define a new operation Foo:
V(Foo) to the appropriate operation list macro (e.g., TURBOSHAFT_MACHINE_OPERATION_LIST).struct FooOp deriving from OperationT<FooOp> or FixedArityOperationT<N, FooOp> (if it has fixed N inputs).options() returning a tuple of these options (used for printing and hashing), or custom PrintOptions and hash_value.outputs_rep() and inputs_rep() methods defining representations.OpEffects as a static effects member or non-static Effects() method.instruction-selector.cc.One of Turboshaft's most innovative features is its fine-grained side-effect system, which enables safe and aggressive operation reordering.
Instead of explicit effect chains (like in TurboFan), Turboshaft uses a Produces/Consumes model based on bitmasks.
Defined in EffectDimensions (within operations.h), these are the individual bits of side effects:
load_heap_memory: Reading from the JS heap.store_heap_memory: Writing to the JS heap.load_off_heap_memory: Reading from C++ memory or external resources.store_off_heap_memory: Writing to C++ memory or external resources.control_flow: Operation affects control flow (branches, throws, deopts).before_raw_heap_access / after_raw_heap_access: Used to ensure consistency during raw heap operations (like inline allocation) where the heap might be temporarily invalid.Each operation defines its OpEffects, which consists of:
EffectDimensions bitmask of what effects this operation generates.EffectDimensions bitmask of what effects this operation depends on.can_create_identity: True if operation produces a fresh identity (e.g., allocation). Prevents GVN and moving in/out of loops.can_allocate: True if operation can trigger GC.required_when_unused: True if operation should not be removed even if its result is unused (e.g., stores, side-effecting ops).Two operations cannot be reordered if the first operation produces an effect dimension that the second operation consumes.
Example:
StoreOp produces store_heap_memory and consumes store_heap_memory and load_heap_memory.LoadOp produces load_heap_memory and consumes store_heap_memory.If we have:
StoreOp(addr1, val) (Produces store_heap_memory)LoadOp(addr2) (Consumes store_heap_memory)They cannot be reordered because operation 1 produces an effect that operation 2 consumes.
However, if we have two loads:
LoadOp(addr1) (Produces load_heap_memory, Consumes store_heap_memory)LoadOp(addr2) (Produces load_heap_memory, Consumes store_heap_memory)Neither produces an effect that the other consumes (they both produce load, but consume store). Thus, they can be freely reordered.
This system allows Turboshaft to be very generous with reorderings when operations are high-level and pure, while automatically becoming more restrictive as operations are lowered to low-level memory accesses.
src/compiler/turboshaft/operations.h: The source of truth for operations and effects.