JetBrains/kotlin
JVM backend
Active contributors: Sergej Jaskiewicz, Ivan Kylchik, Wojciech Litewka
The JVM backend takes lowered IR and emits JVM bytecode (.class files). It is the most heavily used Kotlin backend and the one the language was first built around. The modern path lives in compiler/ir/backend.jvm/; the older descriptor-driven path in compiler/backend/ and compiler/backend.jvm/ is retained for compatibility but is no longer the primary target.
Purpose
Translate the (lowered) IR produced by compiler/ir/ir.psi2ir/ (K1) or compiler/fir/fir2ir/ (K2) into JVM class files conforming to the platform's bytecode rules. Handle JVM-specific concerns: signature mangling, generic erasure, sealed/inline classes, default-argument bridges, suspend continuations, mangled names for inline classes, and so on.
Directory layout
compiler/ir/backend.jvm/
├── lower/ JVM-specific IR lowerings
├── codegen/ Bytecode emission (uses ASM)
├── entrypoint/ Backend driver / entry points
├── src/ Cross-cutting JVM backend code
└── LockPriority.md Notes on locking strategy in the backendThe backend uses ASM (Java bytecode toolkit) under the hood. ASM lives in dependencies/ (third-party).
Key abstractions
| Type | Where | Role |
|---|---|---|
JvmIrCodegenFactory |
compiler/ir/backend.jvm/.../JvmIrCodegenFactory.kt |
Top-level entry into the JVM IR backend. |
JvmBackendContext |
compiler/ir/backend.jvm/.../JvmBackendContext.kt |
Carries configuration, intrinsics, and shared state. |
JvmFileLoweringPipeline |
compiler/ir/backend.jvm/.../lower/... |
The ordered list of JVM lowerings. |
MethodVisitor-style emitters in codegen/ |
compiler/ir/backend.jvm/codegen/... |
Walk lowered IR and emit ASM bytecode. |
JvmSymbols |
compiler/ir/backend.jvm/.../JvmSymbols.kt |
Tabulated references to JVM-specific built-ins (e.g., Continuation, kotlin.jvm.functions.*). |
How it works
A typical JVM compile, after IR is built:
- Common lowerings — generic IR transformations from
compiler/ir/backend.common/. - JVM-specific lowerings — under
backend.jvm/lower/. These desugar Kotlin features into JVM-shaped IR: inline classes erase to their underlying type,data classequals/hashCodeget materialized,suspendcontinuations get state machines, properties become getter/setter methods,varargbecomes arrays, etc. - Code generation —
backend.jvm/codegen/walks the lowered IR and emits.classfiles via ASM. Generic types are erased here; signatures are mangled appropriately. - Output — the chosen
OutputCollectorwrites class files to disk or to the build output.
graph LR
IR["Lowered IR<br/>(post backend.common)"] --> JvmLower["JVM lowerings<br/>backend.jvm/lower/"]
JvmLower --> Codegen["JVM codegen<br/>backend.jvm/codegen/"]
Codegen --> ASM["ASM ClassWriter"]
ASM --> Out[".class files"]Inline classes, suspend, and other thorns
Some Kotlin features map awkwardly to JVM bytecode. The JVM backend has dedicated lowerings for each:
- Inline classes — erased to the wrapped type at runtime; multiple compiler-emitted bridge methods preserve type identity at the type-system level.
- Suspend functions — compiled to a state-machine body taking a
Continuationparameter. The transformation is in thelower/directory. - Default arguments — generate
$defaultsynthetic methods alongside the original. - Sealed classes — compiled with
permitsSubclassesconstant pool entries on JDK 17+. reifiedtype parameters — only valid ininlinefunctions; the JVM backend substitutes the type at the call site.
Java interop
The JVM backend emits class files that interact with arbitrary Java code, so it has to honor JVM ABI rules. compiler/javac-wrapper/ and compiler/light-classes/ are the cross-language helpers; the JVM backend itself reads Java metadata from class files to align signatures, generic bounds, and nullability annotations.
Legacy path
The pre-IR JVM backend (descriptor-driven) lives at compiler/backend/ and compiler/backend.jvm/. It still exists because some long-tail tooling and old language-version selections route through it. New work uses the IR backend; the old one is in maintenance.
Integration points
- Input: lowered IR.
- Output:
.classfiles. Consumed by the rest of the JVM toolchain (javac, JARs, app servers, Android, ...). - Used by:
compiler/cli/cli-jvm/(thekotlinc-jvmCLI),compiler/build-tools/kotlin-build-tools-impl/, KGP'sKotlinCompiletask, and the Maven/JPS plugins.
Where to start
For a new JVM lowering, browse compiler/ir/backend.jvm/lower/ for an analogous pass. The codegen layer is harder to extend; most user-visible behavior lives in the lowerings. The LockPriority.md file at the backend root documents an ordering rule for backend locks — read it before introducing concurrency.
See backend IR for the shared lowering substrate.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.