JetBrains/kotlin
Wasm backend
Active contributors: Ilya Goncharov, Artem Kobzar, Sergej Jaskiewicz
The WebAssembly backend compiles Kotlin to .wasm modules. Two output flavors are supported: Wasm/JS (Wasm module designed to run inside a JS runtime — the default in browsers) and Wasm/WASI (Wasm with the WASI system interface for non-browser hosts). The backend lives in compiler/ir/backend.wasm/ with the Wasm IR proper in wasm/wasm.ir.
Purpose
Take lowered Kotlin IR and produce a Wasm module that runs on the host runtime. Handle Wasm-specific concerns: linear memory, function tables, GC types (when targeting Wasm GC), and the JS bridge for browser-targeted output.
Directory layout
compiler/ir/backend.wasm/ Wasm-specific lowerings + driver
wasm/
├── wasm.config/ Wasm-specific compiler configuration
├── wasm.frontend/ Wasm-specific frontend extensions
├── wasm.ir/ Wasm IR types and emitters
├── wasm.tests/ Wasm test runners + test data
└── wasm.debug.browsers/ Helpers for running tests in browserKey abstractions
| Type | Where | Role |
|---|---|---|
WasmIrModuleSerializer |
compiler/ir/backend.wasm/... |
Serializes Wasm IR for klib. |
WasmModule, WasmFunction, WasmInstr* |
wasm/wasm.ir/... |
The Wasm IR types. Cover both function-section and type-section concepts. |
WasmCompiledModuleFragment |
compiler/ir/backend.wasm/... |
Per-file output of the backend; merged at link time. |
WasmBackendContext |
compiler/ir/backend.wasm/... |
Shared backend state. |
How Wasm compilation works
graph LR
IR["Lowered IR"] --> WasmLower["Wasm lowerings"]
WasmLower --> WasmIR["wasm.ir"]
WasmIR --> Link["Wasm linker"]
Link --> Module[".wasm module"]
Link --> JsGlue[".uninstantiated.mjs<br/>(JS adapter)"]Like JS, Wasm is multi-module: each Kotlin compilation unit produces an IR klib plus per-file Wasm AST fragments. A final link step combines the fragments into one .wasm module.
Wasm/GC
Wasm GC types are first-class in this backend. Instead of representing Kotlin objects in linear memory, the backend uses Wasm's typed reference ((ref ...)) and struct/array ((struct ...), (array ...)) instructions. This significantly reduces output size and runtime cost compared to the older "boxed in linear memory" approach.
Wasm/WASI vs Wasm/JS
- Wasm/JS — the default. Produces a Wasm module plus a JS adapter (
.uninstantiated.mjs) that handles imports, JS interop, and exception bridging. - Wasm/WASI — produces a Wasm module that uses the WASI system interface for I/O. Used for non-browser hosts (e.g., Wasmtime). Has restricted JS interop.
The selection happens via compiler arguments and via the kotlin("multiplatform") Gradle DSL (wasmJs { ... } vs wasmWasi { ... }).
Browser tests
wasm/wasm.debug.browsers/ configures running Wasm test suites in real browsers (Chrome, Firefox) for end-to-end regression. The CI pipeline exercises this on Wasm-enabled browser releases.
Integration points
- Input: lowered IR.
- Output:
.wasmmodules, Wasm klibs, optional JS adapter. - Used by:
compiler/cli/cli-js/(Wasm uses the same CLI shape as JS for historical reasons), KGP'swasmJs/wasmWasitargets. - Tested by:
wasm/wasm.tests/— runs box tests through Node.js and the browsers.
Where to start
Wasm-specific lowerings live in compiler/ir/backend.wasm/.../lower/. The Wasm IR types in wasm/wasm.ir/ are the most useful entry point if you're investigating output. A failing test usually leaves a .wat (WebAssembly Text Format) dump next to the test data — that's the easiest way to inspect what the backend produced.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.