Skip to content

Commit aa31720

Browse files
feat: doc for llvm-passes (#4892)
* [K/N] Add doc for llvm-passes --------- Co-authored-by: Troels Bjerre Lund <[email protected]>
1 parent 8c54e6e commit aa31720

File tree

3 files changed

+130
-1
lines changed

3 files changed

+130
-1
lines changed

docs/kr.tree

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,7 @@
207207
<toc-element toc-title="Reference and tips">
208208
<toc-element toc-title="Target support" topic="native-target-support.md"/>
209209
<toc-element toc-title="Improving compilation time" topic="native-improving-compilation-time.md"/>
210+
<toc-element toc-title="Customizing LLVM passes" topic="native-llvm-passes.md"/>
210211
<toc-element toc-title="License files" topic="native-binary-licenses.md"/>
211212
<toc-element topic="native-faq.md"/>
212213
</toc-element>

docs/topics/native/native-improving-compilation-time.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,4 +156,9 @@ create an [issue in YouTrack](https://kotl.in/issue).
156156
## Windows configuration
157157

158158
Windows Security may slow down the Kotlin/Native compiler. You can avoid this by adding the `.konan` directory,
159-
which is located in `%\USERPROFILE%` by default, to Windows Security exclusions. Learn how to [add exclusions to Windows Security](https://support.microsoft.com/en-us/windows/add-an-exclusion-to-windows-security-811816c0-4dfd-af4a-47e4-c301afe13b26).
159+
which is located in `%\USERPROFILE%` by default, to Windows Security exclusions. Learn how to [add exclusions to Windows Security](https://support.microsoft.com/en-us/windows/add-an-exclusion-to-windows-security-811816c0-4dfd-af4a-47e4-c301afe13b26).
160+
161+
## LLVM configuration
162+
<primary-label ref="advanced"/>
163+
164+
If the above tips didn't help with improving compilation time, consider [customizing the LLVM backend](native-llvm-passes.md).
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
[//]: # (title: Tips for customizing LLVM backend)
2+
<primary-label ref="advanced"/>
3+
4+
The Kotlin/Native compiler uses [LLVM](https://llvm.org/) to optimize and generate binary executables for different target platforms.
5+
A noticeable part of the compilation time is also spent in LLVM, and for large apps, this can end up
6+
taking an unacceptably long time.
7+
8+
You can customize how Kotlin/Native uses LLVM and adjust the list of optimization passes.
9+
10+
## Examine the build log
11+
12+
Let's take a look at the build log to understand how much compilation time is spent on LLVM optimization passes:
13+
14+
1. Run the `linkRelease*` Gradle task with `-Pkotlin.internal.compiler.arguments.log.level=warning` option to make Gradle
15+
output LLVM profiling details, for example:
16+
17+
```bash
18+
./gradlew linkReleaseExecutableMacosArm64 -Pkotlin.internal.compiler.arguments.log.level=warning
19+
```
20+
21+
While executing, the task prints the necessary compiler arguments, for example:
22+
23+
```none
24+
> Task :linkReleaseExecutableMacosArm64
25+
Run in-process tool "konanc"
26+
Entry point method = org.jetbrains.kotlin.cli.utilities.MainKt.daemonMain
27+
Classpath = [
28+
/Users/user/.konan/kotlin-native-prebuilt-macos-aarch64-2.2.0/konan/lib/kotlin-native-compiler-embeddable.jar
29+
/Users/user/.konan/kotlin-native-prebuilt-macos-aarch64-2.2.0/konan/lib/trove4j.jar
30+
]
31+
Arguments = [
32+
-Xinclude=...
33+
-library
34+
/Users/user/.konan/kotlin-native-prebuilt-macos-aarch64-2.2.0/klib/common/stdlib
35+
-no-endorsed-libs
36+
-nostdlib
37+
...
38+
]
39+
```
40+
41+
2. Run the command line compiler with the provided arguments plus the `-Xprofile-phases` argument, for example:
42+
43+
```bash
44+
/Users/user/.konan/kotlin-native-prebuilt-macos-aarch64-2.2.0/bin/kotlinc-native \
45+
-Xinclude=... \
46+
-library /Users/user/.konan/kotlin-native-prebuilt-macos-aarch64-2.2.0/klib/common/stdlib \
47+
... \
48+
-Xprofile-phases
49+
```
50+
51+
3. Examine the generated output in the build log. The log can contain tens of thousands of lines; sections with LLVM
52+
profiling are at the end.
53+
54+
Here is an excerpt from such a run of a simple Kotlin/Native program:
55+
56+
```none
57+
Frontend: 275 msec
58+
PsiToIr: 1186 msec
59+
...
60+
... 30k lines
61+
...
62+
LinkBitcodeDependencies: 476 msec
63+
StackProtectorPhase: 0 msec
64+
MandatoryBitcodeLLVMPostprocessingPhase: 2 msec
65+
===-------------------------------------------------------------------------===
66+
Pass execution timing report
67+
===-------------------------------------------------------------------------===
68+
Total Execution Time: 6.7726 seconds (6.7192 wall clock)
69+
70+
---User Time--- --System Time-- --User+System-- ---Wall Time--- --- Name ---
71+
0.9778 ( 22.4%) 0.5043 ( 21.0%) 1.4821 ( 21.9%) 1.4628 ( 21.8%) InstCombinePass
72+
0.3827 ( 8.8%) 0.2497 ( 10.4%) 0.6323 ( 9.3%) 0.6283 ( 9.4%) InlinerPass
73+
0.2815 ( 6.4%) 0.1792 ( 7.5%) 0.4608 ( 6.8%) 0.4555 ( 6.8%) SimplifyCFGPass
74+
...
75+
0.6444 (100.0%) 0.5474 (100.0%) 1.1917 (100.0%) 1.1870 (100.0%) Total
76+
77+
ModuleBitcodeOptimization: 8118 msec
78+
...
79+
LTOBitcodeOptimization: 1399 msec
80+
...
81+
```
82+
83+
The Kotlin/Native compiler runs two separate sequences of LLVM optimizations: the module passes and the link-time
84+
passes. For a typical compilation, the two pipelines are run back to back, and the only real distinction is in which
85+
LLVM optimization passes they run.
86+
87+
In the log above, the two LLVM optimizations are `ModuleBitcodeOptimization` and `LTOBitcodeOptimization`.
88+
The formatted tables are the optimizations' output with timing for each pass.
89+
90+
## Customize LLVM optimization passes
91+
92+
If one of the passes above seems unreasonably long, you can skip it. However, this might hurt runtime performance,
93+
so you should check for changes in the benchmarks' performance afterward.
94+
95+
Currently, there is no direct way to [disable a given pass](https://youtrack.jetbrains.com/issue/KT-69212).
96+
However, you can provide a new list of passes to run by using the following compiler options:
97+
98+
| **Option** | **Default value for release binary** |
99+
|------------------------|--------------------------------------|
100+
| `-Xllvm-module-passes` | `"default<O3>"` |
101+
| `-Xllvm-lto-passes` | `"internalize,globaldce,lto<O3>"` |
102+
103+
The default values are unfolded to a long list of actual passes, from which you need to exclude the undesired ones.
104+
105+
To get the list of actual passes, run the [`opt`](https://llvm.org/docs/CommandGuide/opt.html) tool, which is
106+
automatically downloaded with the LLVM distribution to the
107+
`~/.konan/dependencies/llvm-{VERSION}-{ARCH}-{OS}-dev-{BUILD}/bin` directory.
108+
109+
For example, to get the list of the link-time passes, run:
110+
111+
```bash
112+
opt -print-pipeline-passes -passes="internalize,globaldce,lto<O3>" < /dev/null
113+
```
114+
115+
This outputs a warning and a long list of passes, which depends on the LLVM version.
116+
117+
There are two differences between the list of passes from the `opt` tool and the passes that Kotlin/Native
118+
compiler actually runs:
119+
120+
* Since `opt` is a debug tool, it includes one or more `verify` passes, which are not normally run.
121+
* Kotlin/Native disables the `devirt` passes since the Kotlin compiler already does them itself.
122+
123+
After disabling any passes, always rerun performance tests to check if the runtime performance degradation is acceptable.

0 commit comments

Comments
 (0)