The Java Virtual Machine & the Kotlin Compiler
The Java Virtual Machine & the Kotlin Compiler
The Java
Virtual Machine
& the Kotlin
Compiler
@kotlin | Developed by JetBrains
The Java language
● Has a garbage collector, meaning you can allocate memory and it will be freed automatically.
Compilation process – Java vs C
Java bytecode
public class examples/Main {
public <init>()V
L0
LINENUMBER 3 L0
ALOAD 0
INVOKESPECIAL java/lang/Object.<init> ()V
RETURN
L1
public class Main { LOCALVARIABLE this Lorg/examples/Main; L0 L1 0
MAXSTACK = 1
public static void main(String[] args) {
MAXLOCALS = 1
System.out.print("Hello, World!");
} public static main([Ljava/lang/String;)V
} L0
LINENUMBER 5 L0
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "Hello, World!"
ICONST_0
ANEWARRAY java/lang/Object
INVOKEVIRTUAL java/io/PrintStream.print
(Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream;
POP
L1
LINENUMBER 6 L1
RETURN
L2
LOCALVARIABLE args [Ljava/lang/String; L0 L2 0
MAXSTACK = 3
MAXLOCALS = 1
}
The JVM under the hood
JVM
Class loading
services Interpreter
JIT-compiler
.сlass files Memory management
with bytecodes bytecode => machine code
(heap, GC)
translation services
Memory organisation
lifetime
Generations
● Young
● Old
Stack
A D
C
B E
Serial garbage collector
The first garbage collector created was the serial garbage collector, which is single-threaded, and the parallel
and CMS garbage collectors are based on it.
Eden S0 S1 Tenured
How garbage collection works
Current state:
Before GC:
Surviving objects
After GC:
How garbage collection works
Current state:
Before GC:
After GC:
How garbage collection works
Current state:
Before GC:
After GC:
How garbage collection works
Current state:
Before GC:
After GC:
● Pieces of code are compiled for a specific platform to optimize the execution time.
Interpreter JIT-compiler
Starts working almost instantly. Kicks in after a long delay (needs time for
vs
● ●
optimizations).
● The performance of the executable
code is poor. ● The performance of the executable
(compiled) code is high.
Just-in-time compilation
Code that will take a long time to run or code that runs frequently, because the compilation
overhead will be covered by the profit from having optimized execution.
Just-in-time compilation
JVM
Class loading
services Interpreter
JIT-compiler
.сlass files Memory management
with bytecodes bytecode => machine code
(heap, GC)
translation services
The JVM under the hood
class PiUtils {
private static final double PI = 3.141592653589;
class PiUtils {
private static final double PI = 4;
● Provides the ability to integrate into the compilation process (compiler plugins).
.kt files
Kotlin compiler
Parser
Lexer PSI or Lighter AST builder
.kt files
Frontend
Diagnostics Type inference Resolution
Backend
IR generator IR optimizer
.kt files
Frontend
Diagnostics Type inference Resolution
Backend
IR generator IR optimizer
.kt files
Frontend
Diagnostics Type inference Resolution
Backend
IR generator IR optimizer
FUN
'fun' ' ' 'hello' VALUE_PARAMETER_LIST ' ' '=' ' ' ==.
'String'
Kotlin compiler: PSI
fun hello(user: String) = println("Hello, $user")
CALL_EXPRESSION
REFERENCE_EXPRESSION VALUE_ARGUMENT_LIST
STRING_TEMPLATE
'user'
Kotlin compiler: PSI
fun hello(user: String) = println("Hello, $user")
CALL_EXPRESSION
REFERENCE_EXPRESSION VALUE_ARGUMENT_LIST
STRING_TEMPLATE
'user'
Kotlin compiler: PSI
fun hello(user: String) = println("Hello, $user")
CALL_EXPRESSION
REFERENCE_EXPRESSION VALUE_ARGUMENT_LIST
STRING_TEMPLATE
'user'
Kotlin compiler
Parser
Lexer PSI or Lighter AST builder
.kt files
Frontend
Diagnostics Type inference Resolution
Backend
IR generator IR optimizer
returnTypeRef
ResolvedTypeRef(=kotlin/String)
FIR? Another tree!
fun hello(user: String) = println("Hello, $user")
returnTypeRef
ResolvedTypeRef(=kotlin/String)
FIR? Another tree!
fun hello(user: String) = println("Hello, $user")
StringConcatenationCall
argumentList typeRef
arguments arguments
calleeReference typeRef
StringConcatenationCall
argumentList typeRef
arguments arguments
calleeReference typeRef
StringConcatenationCall
argumentList typeRef
arguments arguments
calleeReference typeRef
StringConcatenationCall
argumentList typeRef
arguments arguments
calleeReference typeRef
if (b) { when {
println("Hello") b => println("Hello")
}
}
.kt files
Frontend
Diagnostics Type inference Resolution
Backend
IR generator IR optimizer
} }
Library A Library B
.kt files
Frontend
Diagnostics Type inference Resolution
Backend
IR generator IR optimizer
valueParameters
returnTypeRef ValueParameter (name = user) ConstExpression (value = "Hello, ", kind = String)
returnTypeRef typeRef
● Return analysis
while(true) {
● Smart cast analysis
if (Random.nextBoolean()) {
a = 15
break
Each variable is initialized before being }
used.
}
Each immutable variable is not reassigned
after initialization.
println(a) // It compiles!
Kotlin compiler: control- and data-flow analysis
● Variable initialization analysis fun bar(): Int {
print("Again")
● Return analysis
while (true) {
● Smart cast analysis
print(" and again")
}
} // It compiles!
If the return type is not Unit, then the
function won’t return control unless it
returns something. fun baz(): Long {
error("YOLO! :)")
} // It compiles!
Kotlin compiler: control- and data-flow analysis
fun Any=.printFirstElement() {
● Variable initialization analysis
when (this) {
● Return analysis
is List==> => get(0)
● Smart cast analysis
is Iterable==> => iterator().next()
}
}
fun foo()
Enter while loop
}
Evaluate loop
condition
fun bar(x: Any, b: Boolean) {
true false
while (true) {
if (b) { Exit loop block Enter loop block Exit loop
x as A
Exit if Enter if Function call: x.foo()
break
} false
Evaluate if condition Exit function bar
} true
x.foo()
Enter if branch Type operator: x as A
}
Jump: break
Kotlin compiler: control- and data-flow analysis
interface A { Enter function bar
fun foo()
Enter while loop
}
Evaluate loop
condition
fun bar(x: Any, b: Boolean) {
true false
while (true) {
if (b) { Exit loop block Enter loop block Exit loop
x as A
Exit if Enter if Function call: x.foo()
break
} false
Evaluate if condition Exit function bar
} true
x.foo()
Enter if branch Type operator: x as A
}
Jump: break
Kotlin compiler: control- and data-flow analysis
interface A { Enter function bar
fun foo()
Enter while loop
}
Evaluate loop
condition
fun bar(x: Any, b: Boolean) {
true false
while (true) {
if (b) { Exit loop block Enter loop block Exit loop
x as A
Exit if Enter if Function call: x.foo()
break
} false
Evaluate if condition Exit function bar
} true
x.foo()
Enter if branch Type operator: x as A
}
Jump: break
Kotlin compiler: control- and data-flow analysis
interface A { Enter function bar
fun foo()
Enter while loop
}
Evaluate loop
condition
fun bar(x: Any, b: Boolean) {
true false
while (true) {
if (b) { Exit loop block Enter loop block Exit loop
x as A
Exit if Enter if Function call: x.foo()
break
} false
Evaluate if condition Exit function bar
} true
x.foo()
Enter if branch Type operator: x as A
}
Jump: break
Kotlin compiler: control- and data-flow analysis
interface A { Enter function bar
fun foo()
Enter while loop
}
Evaluate loop
condition
fun bar(x: Any, b: Boolean) {
true false
while (true) {
if (b) { Exit loop block Enter loop block Exit loop
x as A
Exit if Enter if Function call: x.foo()
break
} false
Evaluate if condition Exit function bar
} true
x.foo()
Enter if branch Type operator: x as A
}
Jump: break
Kotlin compiler: control- and data-flow analysis
interface A { Enter function bar
fun foo()
Enter while loop
}
Evaluate loop
condition
fun bar(x: Any, b: Boolean) {
true false
while (true) {
if (b) { Exit loop block Enter loop block Exit loop
x as A
Exit if Enter if Function call: x.foo()
break
} false
Evaluate if condition Exit function bar
} true
x.foo()
Enter if branch Type operator: x as A
}
Jump: break
Kotlin compiler: control- and data-flow analysis
interface A { Enter function bar
fun foo()
Enter while loop
}
Evaluate loop
condition
fun bar(x: Any, b: Boolean) {
true false
while (true) {
if (b) { Exit loop block Enter loop block Exit loop
x as A
Exit if Enter if Function call: x.foo()
break
} false
Evaluate if condition Exit function bar
} true
x.foo()
Enter if branch Type operator: x as A
}
Jump: break
Kotlin compiler: control- and data-flow analysis
interface A { Enter function bar
fun foo()
Enter while loop
}
Evaluate loop
condition
fun bar(x: Any, b: Boolean) {
true false
while (true) {
if (b) { Exit loop block Enter loop block Exit loop
x as A
Exit if Enter if Function call: x.foo()
break
} false
Evaluate if condition Exit function bar
} true
x.foo()
Enter if branch Type operator: x as A
}
Jump: break
Kotlin compiler: control- and data-flow analysis
interface A { Enter function bar
fun foo()
Enter while loop
}
Evaluate loop
condition
fun bar(x: Any, b: Boolean) {
true false
while (true) {
if (b) { Exit loop block Enter loop block Exit loop
x as A
Exit if Enter if Function call: x.foo()
break
} false
Evaluate if condition Exit function bar
} true
x.foo()
Enter if branch Type operator: x as A
}
Jump: break
Kotlin compiler: control- and data-flow analysis
interface A { Enter function bar
fun foo()
Enter while loop
}
Evaluate loop
condition
fun bar(x: Any, b: Boolean) {
true false
while (true) {
if (b) { Exit loop block Enter loop block Exit loop
x as A
Exit if Enter if Function call: x.foo()
break
} false
Evaluate if condition Exit function bar
} true
x.foo()
Enter if branch Type operator: x as A
}
Jump: break
Kotlin compiler: control- and data-flow analysis
interface A { Enter function bar
fun foo()
Enter while loop
}
Evaluate loop
condition
fun bar(x: Any, b: Boolean) {
true false
while (true) {
if (b) { Exit loop block Enter loop block Exit loop
x as A
Exit if Enter if Function call: x.foo()
break
} false
Evaluate if condition Exit function bar
} true
x.foo()
Enter if branch Type operator: x as A
}
Jump: break
Kotlin compiler: control- and data-flow analysis
interface A { Enter function bar
fun foo()
Enter while loop
}
Evaluate loop
condition
fun bar(x: Any, b: Boolean) {
true false
while (true) {
if (b) { Exit loop block Enter loop block Exit loop
x as A
Exit if Enter if Function call: x.foo()
break
} false
Evaluate if condition Exit function bar
} true
x.foo()
Enter if branch Type operator: x as A
}
Jump: break
Kotlin compiler: control- and data-flow analysis
interface A { Enter function bar
fun foo()
Enter while loop
}
Evaluate loop
condition
fun bar(x: Any, b: Boolean) {
true false
while (true) {
if (b) { Exit loop block Enter loop block Exit loop
x as A
Exit if Enter if Function call: x.foo()
break
} false
Evaluate if condition Exit function bar
} true
x.foo()
Enter if branch Type operator: x as A
}
Jump: break
Kotlin compiler: control- and data-flow analysis
interface A { Enter function bar
fun foo()
Enter while loop
}
Evaluate loop
condition
fun bar(x: Any, b: Boolean) {
true false
while (true) {
if (b) { Exit loop block Enter loop block Exit loop
x as A
Exit if Enter if Function call: x.foo()
break
} false
Evaluate if condition Exit function bar
} true
Jump: break
Kotlin compiler
Parser
Lexer PSI or Lighter AST builder
.kt files
Frontend
Diagnostics Type inference Resolution
Backend
IR generator IR optimizer
.kt files
Frontend
Diagnostics Type inference Resolution
Backend
IR generator IR optimizer
On the backend, we
DO NOT resolve, but
JVM JavaScript Native Other
only use the received
(WASM, Python, etc.?)
information
Kotlin compiler
Parser
Lexer PSI or Lighter AST builder
.kt files
Frontend
Diagnostics Type inference Resolution
Backend
IR generator IR optimizer
Type: kotlin.String
Type: kotlin.String
Type: kotlin.Unit
Call: kotlin.io.println(kotlin.String)
package helloWorld
FUN name:hello visibility:public modality:FINAL =>(user:kotlin.String) FUN name:main visibility:public modality: FINAL =>(args:kotlin.Array<kotlin.String>)
returnType:kotlin.Unit returnType:kotlin.Unit
VALUE_PARAMETER name:user index:0 type:kotlin.String BLOCK_BODY VALUE_PARAMETER name:args index:0 type:kotlin.Array<kotlin.String> BLOCK_BODY
CALL 'public final fun println (message: kotlin.Any?): CALL 'public final fun get (index: kotlin.Int): T of CALL 'public final fun hello (user: kotlin.String):
kotlin.Unit [inline] declared in kotlin.io.ConsoleKt' kotlin.Array [operator] declared in kotlin.Array' kotlin.Unit declared in helloworld'
type=kotlin.Unit origin=null type kotlin.String origin=null type kotlin.Unit origin=null
message: index:
$this: user:
STRING_CONCATENATION type=kotlin.String
CONST Int type=kotlin.Int value=0
RETURN type=kotlin.Nothing
from='public final fun hello (user: kotlin.String): kotlin.Unit declared in helloworld'
message:
STRING_CONCATENATION type=kotlin.String
RETURN type=kotlin.Nothing
from='public final fun hello (user: kotlin.String): kotlin.Unit declared in helloworld'
message:
STRING_CONCATENATION type=kotlin.String
RETURN type=kotlin.Nothing
from='public final fun hello (user: kotlin.String): kotlin.Unit declared in helloworld'
message:
STRING_CONCATENATION type=kotlin.String
RETURN type=kotlin.Nothing
from='public final fun hello (user: kotlin.String): kotlin.Unit declared in helloworld'
message:
STRING_CONCATENATION type=kotlin.String
RETURN type=kotlin.Nothing
from='public final fun hello (user: kotlin.String): kotlin.Unit declared in helloworld'
message:
STRING_CONCATENATION type=kotlin.String
RETURN type=kotlin.Nothing
from='public final fun hello (user: kotlin.String): kotlin.Unit declared in helloworld'
message:
STRING_CONCATENATION type=kotlin.String
RETURN type=kotlin.Nothing
from='public final fun hello (user: kotlin.String): kotlin.Unit declared in helloworld'
message:
STRING_CONCATENATION type=kotlin.String
RETURN type=kotlin.Nothing
from='public final fun hello (user: kotlin.String): kotlin.Unit declared in helloworld'
message:
STRING_CONCATENATION type=kotlin.String
.kt files
Frontend
Diagnostics Type inference Resolution
Backend
IR generator IR optimizer
Platform-specific code,
such as bytecode for the
JVM JavaScript Native Other
JVM
(WASM, Python, etc.?)
Kotlin compiler
Parser
Lexer PSI or Lighter AST builder
.kt files
Frontend
Diagnostics Type inference Resolution
Backend
IR generator IR optimizer
Platform-specific code,
such as bytecode for the
JVM JavaScript Native Other
JVM
(WASM, Python, etc.?)
KLibs
JAR analogues – store a serialized IR for the subsequent use of cross-platform libraries.
JVM code
Kotlin/JVM Classes
Native code
Compiler plugins
You can extend any compile phase via compiler plugins. To do so, you need to implement compiler
extensions and register them.
Don’t forget to use supportsK2 = true if you need to support the FIR frontend
Compiler plugins: FIR extensions
To get the full list of the extensions, please see: package org.jetbrains.kotlin.fir.extensions (link).
Consider an example:
=*
* Generates top level class
*
* package foo.bar
*
* public final class MyClass {
* fun foo(): String = "Hello world"
* }
=/
Compiler plugins: FIR extensions
You use a special key to mark everything generated by the compiler and can transfer any
information between frontend and backend. So it also helps to find the new declaration to generate
it’s IR.
==.
}
Compiler plugins: FIR extensions
}
Compiler plugins: IR extensions
Actually, the compiler has only one extension for IRs: IrGenerationExtension.
You just need to implement some transformers and accept them: