]>
Commit | Line | Data |
---|---|---|
532ac7d7 XL |
1 | ## Debugging LLVM |
2 | ||
3 | > NOTE: If you are looking for info about code generation, please see [this | |
4 | > chapter][codegen] instead. | |
5 | ||
ba9703b0 | 6 | [codegen]: ./codegen.md |
532ac7d7 XL |
7 | |
8 | This section is about debugging compiler bugs in code generation (e.g. why the | |
9 | compiler generated some piece of code or crashed in LLVM). LLVM is a big | |
10 | project on its own that probably needs to have its own debugging document (not | |
11 | that I could find one). But here are some tips that are important in a rustc | |
12 | context: | |
13 | ||
14 | As a general rule, compilers generate lots of information from analyzing code. | |
15 | Thus, a useful first step is usually to find a minimal example. One way to do | |
16 | this is to | |
17 | ||
18 | 1. create a new crate that reproduces the issue (e.g. adding whatever crate is | |
19 | at fault as a dependency, and using it from there) | |
20 | ||
21 | 2. minimize the crate by removing external dependencies; that is, moving | |
22 | everything relevant to the new crate | |
23 | ||
24 | 3. further minimize the issue by making the code shorter (there are tools that | |
25 | help with this like `creduce`) | |
26 | ||
27 | The official compilers (including nightlies) have LLVM assertions disabled, | |
28 | which means that LLVM assertion failures can show up as compiler crashes (not | |
29 | ICEs but "real" crashes) and other sorts of weird behavior. If you are | |
30 | encountering these, it is a good idea to try using a compiler with LLVM | |
31 | assertions enabled - either an "alt" nightly or a compiler you build yourself | |
32 | by setting `[llvm] assertions=true` in your config.toml - and see whether | |
33 | anything turns up. | |
34 | ||
35 | The rustc build process builds the LLVM tools into | |
36 | `./build/<host-triple>/llvm/bin`. They can be called directly. | |
37 | ||
38 | The default rustc compilation pipeline has multiple codegen units, which is | |
39 | hard to replicate manually and means that LLVM is called multiple times in | |
40 | parallel. If you can get away with it (i.e. if it doesn't make your bug | |
41 | disappear), passing `-C codegen-units=1` to rustc will make debugging easier. | |
42 | ||
60c5eb7d | 43 | For rustc to generate LLVM IR, you need to pass the `--emit=llvm-ir` flag. If |
532ac7d7 XL |
44 | you are building via cargo, use the `RUSTFLAGS` environment variable (e.g. |
45 | `RUSTFLAGS='--emit=llvm-ir'`). This causes rustc to spit out LLVM IR into the | |
46 | target directory. | |
47 | ||
48 | `cargo llvm-ir [options] path` spits out the LLVM IR for a particular function | |
49 | at `path`. (`cargo install cargo-asm` installs `cargo asm` and `cargo | |
50 | llvm-ir`). `--build-type=debug` emits code for debug builds. There are also | |
51 | other useful options. Also, debug info in LLVM IR can clutter the output a lot: | |
52 | `RUSTFLAGS="-C debuginfo=0"` is really useful. | |
53 | ||
54 | `RUSTFLAGS="-C save-temps"` outputs LLVM bitcode (not the same as IR) at | |
55 | different stages during compilation, which is sometimes useful. One just needs | |
56 | to convert the bitcode files to `.ll` files using `llvm-dis` which should be in | |
57 | the target local compilation of rustc. | |
58 | ||
59 | If you want to play with the optimization pipeline, you can use the `opt` tool | |
60 | from `./build/<host-triple>/llvm/bin/` with the LLVM IR emitted by rustc. Note | |
61 | that rustc emits different IR depending on whether `-O` is enabled, even | |
62 | without LLVM's optimizations, so if you want to play with the IR rustc emits, | |
63 | you should: | |
64 | ||
65 | ```bash | |
66 | $ rustc +local my-file.rs --emit=llvm-ir -O -C no-prepopulate-passes \ | |
67 | -C codegen-units=1 | |
68 | $ OPT=./build/$TRIPLE/llvm/bin/opt | |
69 | $ $OPT -S -O2 < my-file.ll > my | |
70 | ``` | |
71 | ||
72 | If you just want to get the LLVM IR during the LLVM pipeline, to e.g. see which | |
73 | IR causes an optimization-time assertion to fail, or to see when LLVM performs | |
74 | a particular optimization, you can pass the rustc flag `-C | |
75 | llvm-args=-print-after-all`, and possibly add `-C | |
76 | llvm-args='-filter-print-funcs=EXACT_FUNCTION_NAME` (e.g. `-C | |
77 | llvm-args='-filter-print-funcs=_ZN11collections3str21_$LT$impl$u20$str$GT$\ | |
78 | 7replace17hbe10ea2e7c809b0bE'`). | |
79 | ||
80 | That produces a lot of output into standard error, so you'll want to pipe that | |
81 | to some file. Also, if you are using neither `-filter-print-funcs` nor `-C | |
82 | codegen-units=1`, then, because the multiple codegen units run in parallel, the | |
83 | printouts will mix together and you won't be able to read anything. | |
84 | ||
85 | If you want just the IR for a specific function (say, you want to see why it | |
86 | causes an assertion or doesn't optimize correctly), you can use `llvm-extract`, | |
87 | e.g. | |
88 | ||
89 | ```bash | |
90 | $ ./build/$TRIPLE/llvm/bin/llvm-extract \ | |
91 | -func='_ZN11collections3str21_$LT$impl$u20$str$GT$7replace17hbe10ea2e7c809b0bE' \ | |
92 | -S \ | |
93 | < unextracted.ll \ | |
94 | > extracted.ll | |
95 | ``` | |
96 | ||
60c5eb7d XL |
97 | ### Getting help and asking questions |
98 | ||
99 | If you have some questions, head over to the [rust-lang Zulip] and | |
100 | specifically the `#t-compiler/wg-llvm` stream. | |
101 | ||
102 | [rust-lang Zulip]: https://rust-lang.zulipchat.com/ | |
103 | ||
104 | ### Compiler options to know and love | |
105 | ||
6a06907d | 106 | The `-C help` and `-Z help` compiler switches will list out a variety |
60c5eb7d XL |
107 | of interesting options you may find useful. Here are a few of the most |
108 | common that pertain to LLVM development (some of them are employed in the | |
109 | tutorial above): | |
110 | ||
111 | - The `--emit llvm-ir` option emits a `<filename>.ll` file with LLVM IR in textual format | |
112 | - The `--emit llvm-bc` option emits in bytecode format (`<filename>.bc`) | |
6a06907d | 113 | - Passing `-C llvm-args=<foo>` allows passing pretty much all the |
60c5eb7d | 114 | options that tools like llc and opt would accept; |
6a06907d | 115 | e.g. `-C llvm-args=-print-before-all` to print IR before every LLVM |
60c5eb7d | 116 | pass. |
6a06907d | 117 | - The `-C no-prepopulate-passes` will avoid pre-populate the LLVM pass |
60c5eb7d XL |
118 | manager with a list of passes. This will allow you to view the LLVM |
119 | IR that rustc generates, not the LLVM IR after optimizations. | |
6a06907d XL |
120 | - The `-C passes=val` option allows you to supply a space separated list of extra LLVM passes to run |
121 | - The `-C save-temps` option saves all temporary output files during compilation | |
122 | - The `-Z print-llvm-passes` option will print out LLVM optimization passes being run | |
123 | - The `-Z time-llvm-passes` option measures the time of each LLVM pass | |
124 | - The `-Z verify-llvm-ir` option will verify the LLVM IR for correctness | |
125 | - The `-Z no-parallel-llvm` will disable parallel compilation of distinct compilation units | |
126 | - The `-Z llvm-time-trace` option will output a Chrome profiler compatible JSON file | |
74b04a01 | 127 | which contains details and timings for LLVM passes. |
60c5eb7d | 128 | |
532ac7d7 XL |
129 | ### Filing LLVM bug reports |
130 | ||
131 | When filing an LLVM bug report, you will probably want some sort of minimal | |
132 | working example that demonstrates the problem. The Godbolt compiler explorer is | |
133 | really helpful for this. | |
134 | ||
135 | 1. Once you have some LLVM IR for the problematic code (see above), you can | |
136 | create a minimal working example with Godbolt. Go to | |
137 | [gcc.godbolt.org](https://gcc.godbolt.org). | |
138 | ||
139 | 2. Choose `LLVM-IR` as programming language. | |
140 | ||
141 | 3. Use `llc` to compile the IR to a particular target as is: | |
142 | - There are some useful flags: `-mattr` enables target features, `-march=` | |
143 | selects the target, `-mcpu=` selects the CPU, etc. | |
144 | - Commands like `llc -march=help` output all architectures available, which | |
145 | is useful because sometimes the Rust arch names and the LLVM names do not | |
146 | match. | |
147 | - If you have compiled rustc yourself somewhere, in the target directory | |
148 | you have binaries for `llc`, `opt`, etc. | |
149 | ||
150 | 4. If you want to optimize the LLVM-IR, you can use `opt` to see how the LLVM | |
151 | optimizations transform it. | |
152 | ||
153 | 5. Once you have a godbolt link demonstrating the issue, it is pretty easy to | |
60c5eb7d XL |
154 | fill in an LLVM bug. Just visit [bugs.llvm.org](https://bugs.llvm.org/). |
155 | ||
156 | ### Porting bug fixes from LLVM | |
157 | ||
158 | Once you've identified the bug as an LLVM bug, you will sometimes | |
159 | find that it has already been reported and fixed in LLVM, but we haven't | |
160 | gotten the fix yet (or perhaps you are familiar enough with LLVM to fix it yourself). | |
161 | ||
162 | In that case, we can sometimes opt to port the fix for the bug | |
163 | directly to our own LLVM fork, so that rustc can use it more easily. | |
164 | Our fork of LLVM is maintained in [rust-lang/llvm-project]. Once | |
165 | you've landed the fix there, you'll also need to land a PR modifying | |
166 | our submodule commits -- ask around on Zulip for help. | |
167 | ||
168 | [rust-lang/llvm-project]: https://github.com/rust-lang/llvm-project/ |