Tips for customizing LLVM backend
The Kotlin/Native compiler uses LLVM to optimize and generate binary executables for different target platforms. A noticeable part of the compilation time is also spent in LLVM, and for large apps, this can end up taking an unacceptably long time.
You can customize how Kotlin/Native uses LLVM and adjust the list of optimization passes.
Examine the build log
Let's take a look at the build log to understand how much compilation time is spent on LLVM optimization passes:
Run the
linkRelease*
Gradle task with-Pkotlin.internal.compiler.arguments.log.level=warning
option to make Gradle output LLVM profiling details, for example:./gradlew linkReleaseExecutableMacosArm64 -Pkotlin.internal.compiler.arguments.log.level=warningWhile executing, the task prints the necessary compiler arguments, for example:
> Task :linkReleaseExecutableMacosArm64 Run in-process tool "konanc" Entry point method = org.jetbrains.kotlin.cli.utilities.MainKt.daemonMain Classpath = [ /Users/user/.konan/kotlin-native-prebuilt-macos-aarch64-2.2.0/konan/lib/kotlin-native-compiler-embeddable.jar /Users/user/.konan/kotlin-native-prebuilt-macos-aarch64-2.2.0/konan/lib/trove4j.jar ] Arguments = [ -Xinclude=... -library /Users/user/.konan/kotlin-native-prebuilt-macos-aarch64-2.2.0/klib/common/stdlib -no-endorsed-libs -nostdlib ... ]Run the command line compiler with the provided arguments plus the
-Xprofile-phases
argument, for example:/Users/user/.konan/kotlin-native-prebuilt-macos-aarch64-2.2.0/bin/kotlinc-native \ -Xinclude=... \ -library /Users/user/.konan/kotlin-native-prebuilt-macos-aarch64-2.2.0/klib/common/stdlib \ ... \ -Xprofile-phasesExamine the generated output in the build log. The log can contain tens of thousands of lines; sections with LLVM profiling are at the end.
Here is an excerpt from such a run of a simple Kotlin/Native program:
The Kotlin/Native compiler runs two separate sequences of LLVM optimizations: the module passes and the link-time passes. For a typical compilation, the two pipelines are run back to back, and the only real distinction is in which LLVM optimization passes they run.
In the log above, the two LLVM optimizations are ModuleBitcodeOptimization
and LTOBitcodeOptimization
. The formatted tables are the optimizations' output with timing for each pass.
Customize LLVM optimization passes
If one of the passes above seems unreasonably long, you can skip it. However, this might hurt runtime performance, so you should check for changes in the benchmarks' performance afterward.
Currently, there is no direct way to disable a given pass. However, you can provide a new list of passes to run by using the following compiler options:
Option | Default value for release binary |
---|---|
|
|
|
|
The default values are unfolded to a long list of actual passes, from which you need to exclude the undesired ones.
To get the list of actual passes, run the opt
tool, which is automatically downloaded with the LLVM distribution to the ~/.konan/dependencies/llvm-{VERSION}-{ARCH}-{OS}-dev-{BUILD}/bin
directory.
For example, to get the list of the link-time passes, run:
This outputs a warning and a long list of passes, which depends on the LLVM version.
There are two differences between the list of passes from the opt
tool and the passes that Kotlin/Native compiler actually runs:
Since
opt
is a debug tool, it includes one or moreverify
passes, which are not normally run.Kotlin/Native disables the
devirt
passes since the Kotlin compiler already does them itself.
After disabling any passes, always rerun performance tests to check if the runtime performance degradation is acceptable.