Open-Source Wikis

/

Kotlin

/

Compiler

/

JVM backend

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 backend

The 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:

  1. Common lowerings — generic IR transformations from compiler/ir/backend.common/.
  2. JVM-specific lowerings — under backend.jvm/lower/. These desugar Kotlin features into JVM-shaped IR: inline classes erase to their underlying type, data class equals/hashCode get materialized, suspend continuations get state machines, properties become getter/setter methods, vararg becomes arrays, etc.
  3. Code generationbackend.jvm/codegen/ walks the lowered IR and emits .class files via ASM. Generic types are erased here; signatures are mangled appropriately.
  4. Output — the chosen OutputCollector writes 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 Continuation parameter. The transformation is in the lower/ directory.
  • Default arguments — generate $default synthetic methods alongside the original.
  • Sealed classes — compiled with permitsSubclasses constant pool entries on JDK 17+.
  • reified type parameters — only valid in inline functions; 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: .class files. Consumed by the rest of the JVM toolchain (javac, JARs, app servers, Android, ...).
  • Used by: compiler/cli/cli-jvm/ (the kotlinc-jvm CLI), compiler/build-tools/kotlin-build-tools-impl/, KGP's KotlinCompile task, 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.

JVM backend – Kotlin wiki | Factory