docs/wasm/architecture.md
This document describes the architecture of WebAssembly (Wasm) implementation in V8, focusing on its compilation pipeline.
V8 provides a high-performance WebAssembly implementation. Similar to JavaScript, it uses a multi-tiered compilation strategy to balance startup time and peak performance.
Wasm execution in V8 typically goes through two main tiers (Liftoff and Turboshaft), but an interpreter is also available:
DrumBrake is V8's WebAssembly interpreter.
src/wasm/interpreter/wasm-interpreter.ccLiftoff is V8's baseline compiler for WebAssembly.
src/wasm/baseline/liftoff-compiler.ccFor hot functions, V8 uses Turboshaft (which replaced TurboFan for Wasm) to generate highly optimized machine code.
src/wasm/turboshaft-graph-interface.cc contains the logic to build a Turboshaft graph from Wasm.src/wasm/function-compiler.cc calls compiler::turboshaft::ExecuteTurboshaftWasmCompilation to run the pipeline.V8 uses a dynamic tiering strategy to balance startup time and execution speed:
TriggerTierUp function in src/wasm/module-compiler.cc manages queueing and executing these background optimization jobs.If the WebAssembly module was compiled with WebAssembly.compileStreaming then the Turboshaft-generated machine code will also get cached. When the same WebAssembly module is fetched again from the same URL then the cached code can be used immediately without additional compilation. More information about code caching is available in a separate blog post.
Code caching gets triggered whenever the amount of generated Turboshaft code reaches a certain threshold. This means that for large WebAssembly modules the Turboshaft code gets cached incrementally, whereas for small WebAssembly modules the Turboshaft code may never get cached. Liftoff code does not get cached, as Liftoff compilation is nearly as fast as loading code from the cache.
As mentioned earlier, Turboshaft applies optimizations, many of which involve re-ordering code, eliminating variables or even skipping whole sections of code. This means that if you want to set a breakpoint at a specific instruction, it might not be clear where program execution should actually stop. In other words, Turboshaft code is not well suited for debugging. Therefore, when debugging is started by opening DevTools, all Turboshaft code is replaced by Liftoff code again ("tiered down"), as each WebAssembly instruction maps to exactly one section of machine code and all local and global variables are intact.
Within DevTools all code will get tiered up (recompiled with Turboshaft) again when the Performance tab is opened and the "Record" button in clicked. The "Record" button starts performance profiling. Profiling the Liftoff code would not be representative as it is only used while Turboshaft isn’t finished and can be significantly slower than Turboshaft’s output, which will be running for the vast majority of time.
For experimentation, V8 and Chrome can be configured to compile WebAssembly code only with Liftoff or only with Turboshaft. It is even possible to experiment with lazy compilation, where functions only get compiled when they get called for the first time. The following flags enable these experimental modes:
Liftoff only:
--liftoff --no-wasm-tier-up flags.chrome://flags/#enable-webassembly-tiering) and enable WebAssembly baseline compiler (chrome://flags/#enable-webassembly-baseline).Turboshaft only:
--no-liftoff --no-wasm-tier-up flags.chrome://flags/#enable-webassembly-tiering) and disable WebAssembly baseline compiler (chrome://flags/#enable-webassembly-baseline).Lazy compilation:
--wasm-lazy-compilation flag.chrome://flags/#enable-webassembly-lazy-compilation).There are different ways to measure the compilation time of Liftoff and Turboshaft. In the production configuration of V8, the compilation time of Liftoff can be measured from JavaScript by measuring the time it takes for new WebAssembly.Module() to finish, or the time it takes WebAssembly.compile() to resolve the promise. To measure the compilation time of Turboshaft, one can do the same in a Turboshaft-only configuration.
The compilation can also be measured in more detail in chrome://tracing/ by enabling the v8.wasm category. Liftoff compilation is then the time spent from starting the compilation until the wasm.BaselineFinished event, Turboshaft compilation ends at the wasm.TopTierFinished event. Compilation itself starts at the wasm.StartStreamingCompilation event for WebAssembly.compileStreaming(), at the wasm.SyncCompile event for new WebAssembly.Module(), and at the wasm.AsyncCompile event for WebAssembly.compile(), respectively. Liftoff compilation is indicated with wasm.BaselineCompilation events, Turboshaft compilation with wasm.TopTierCompilation events.
More detailed tracing data is available with the v8.wasm.detailed category, which, among other information, provides the compilation time of single functions.
src/wasm/module-decoder.h).src/wasm/function-body-decoder.h).src/wasm/wasm-engine.h).src/wasm/: Core WebAssembly implementation.src/wasm/baseline/: Liftoff baseline compiler.src/wasm/interpreter/: DrumBrake interpreter.src/wasm/turboshaft-graph-interface.cc: Wasm frontend for Turboshaft.