summaryrefslogtreecommitdiffstats
path: root/src/doc/rustc-dev-guide/src/backend/updating-llvm.md
blob: 0de0767b6d276c0eb0ab1b936887f7b470e62a52 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
# Updating LLVM

<!-- toc -->

The Rust compiler uses LLVM as its primary codegen backend today, and naturally
we want to at least occasionally update this dependency! Currently we do not
have a strict policy about when to update LLVM or what it can be updated to, but
a few guidelines are applied:

* We try to always support the latest released version of LLVM
* We try to support the "last few" versions of LLVM (how many is changing over
  time)
* We allow moving to arbitrary commits during development.
* Strongly prefer to upstream all patches to LLVM before including them in
  rustc.

This policy may change over time (or may actually start to exist as a formal
policy!), but for now these are rough guidelines!

## Why update LLVM?

There are a few reasons nowadays that we want to update LLVM in one way or
another:

* A bug could have been fixed! Often we find bugs in the compiler and fix
  them upstream in LLVM. We'll want to pull fixes back to the compiler itself as
  they're merged upstream.

* A new feature may be available in LLVM that we want to use in rustc,
  but we don't want to wait for a full LLVM release to test it out.

* LLVM itself may have a new release and we'd like to update to this LLVM
  release.

Each of these reasons has a different strategy for updating LLVM, and we'll go
over them in detail here.

## Bugfix Updates

For updates of LLVM that are to fix a small bug, we cherry-pick the bugfix to
the branch we're already using. The steps for this are:

1. Make sure the bugfix is in upstream LLVM.
2. Identify the branch that rustc is currently using. The `src/llvm-project`
   submodule is always pinned to a branch of the
   [rust-lang/llvm-project](https://github.com/rust-lang/llvm-project) repository.
3. Fork the rust-lang/llvm-project repository
4. Check out the appropriate branch (typically named `rustc/a.b-yyyy-mm-dd`)
5. Cherry-pick the upstream commit onto the branch
6. Push this branch to your fork
7. Send a Pull Request to rust-lang/llvm-project to the same branch as before.
   Be sure to reference the Rust and/or LLVM issue that you're fixing in the PR
   description.
8. Wait for the PR to be merged
9. Send a PR to rust-lang/rust updating the `src/llvm-project` submodule with
   your bugfix. This can be done locally with `git submodule update --remote
   src/llvm-project` typically.
10. Wait for PR to be merged

The tl;dr; is that we can cherry-pick bugfixes at any time and pull them back
into the rust-lang/llvm-project branch that we're using, and getting it into the
compiler is just updating the submodule via a PR!

Example PRs look like:
[#59089](https://github.com/rust-lang/rust/pull/59089)

## Feature updates

> Note that this information is as of the time of this writing <!-- date:
2021-10 --> (October 2021). The process for updating LLVM changes with
practically all LLVM updates, so this may be out of date!

Unlike bugfixes, updating to pick up a new feature of LLVM typically requires a
lot more work. This is where we can't reasonably cherry-pick commits backwards
so we need to do a full update. There's a lot of stuff to do here, so let's go
through each in detail.

1. Create a new branch in the [rust-lang/llvm-project repository]. This branch
   should be named `rustc/a.b-yyyy-mm-dd` where `a.b` is the current version
   number of LLVM in-tree at the time of the branch and the remaining part is
   today's date. Move this branch to the commit in LLVM that you'd like, which
   for this is probably the current LLVM HEAD.

2. Apply Rust-specific patches to the llvm-project repository. All features and
   bugfixes are upstream, but there's often some weird build-related patches
   that don't make sense to upstream which we have on our repositories. These
   patches are around the latest patches in the rust-lang/llvm-project branch
   that rustc is currently using.

3. Build the new LLVM in the `rust` repository. To do this you'll want to update
   the `src/llvm-project` repository to your branch and the revision you've
   created. It's also typically a good idea to update `.gitmodules` with the new
   branch name of the LLVM submodule. Make sure you've committed changes to
   `src/llvm-project` to ensure submodule updates aren't reverted. Some commands
   you should execute are:

   * `./x.py build src/llvm` - test that LLVM still builds
   * `./x.py build src/tools/lld` - same for LLD
   * `./x.py build` - build the rest of rustc

   You'll likely need to update [`llvm-wrapper/*.cpp`][`llvm-wrapper`] to compile
   with updated LLVM bindings. Note that you should use `#ifdef` and such to ensure
   that the bindings still compile on older LLVM versions.

   Note that `profile = "compiler"` and other defaults set by `./x.py setup`
   download LLVM from CI instead of building it from source. You should
   disable this temporarily to make sure your changes are being used, by setting
   ```toml
   [llvm]
   download-ci-llvm = false
   ```
   in config.toml.

4. Test for regressions across other platforms. LLVM often has at least one bug
   for non-tier-1 architectures, so it's good to do some more testing before
   sending this to bors! If you're low on resources you can send the PR as-is
   now to bors, though, and it'll get tested anyway.

   Ideally, build LLVM and test it on a few platforms:

   * Linux
   * macOS
   * Windows

   and afterwards run some docker containers that CI also does:

   * `./src/ci/docker/run.sh wasm32`
   * `./src/ci/docker/run.sh arm-android`
   * `./src/ci/docker/run.sh dist-various-1`
   * `./src/ci/docker/run.sh dist-various-2`
   * `./src/ci/docker/run.sh armhf-gnu`

5. Prepare a PR to `rust-lang/rust`. Work with maintainers of
   `rust-lang/llvm-project` to get your commit in a branch of that repository,
   and then you can send a PR to `rust-lang/rust`. You'll change at least
   `src/llvm-project` and will likely also change [`llvm-wrapper`] as well.

For prior art, previous LLVM updates look like
[#62474](https://github.com/rust-lang/rust/pull/62474)
[#62592](https://github.com/rust-lang/rust/pull/62592)
[#67759](https://github.com/rust-lang/rust/pull/67759)
[#73526](https://github.com/rust-lang/rust/pull/73526)
[#81451](https://github.com/rust-lang/rust/pull/81451). Note that sometimes it's
easiest to land [`llvm-wrapper`] compatibility as a PR before actually updating
`src/llvm-project`. This way while you're working through LLVM issues others
interested in trying out the new LLVM can benefit from work you've done to
update the C++ bindings.

[rust-lang/llvm-project repository]: https://github.com/rust-lang/llvm-project
[`llvm-wrapper`]: https://github.com/rust-lang/rust/tree/master/compiler/rustc_llvm/llvm-wrapper

### Caveats and gotchas

Ideally the above instructions are pretty smooth, but here's some caveats to
keep in mind while going through them:

* LLVM bugs are hard to find, don't hesitate to ask for help! Bisection is
  definitely your friend here (yes LLVM takes forever to build, yet bisection is
  still your friend)
* If you've got general questions, [wg-llvm] can help you out.
* Creating branches is a privileged operation on GitHub, so you'll need someone
  with write access to create the branches for you most likely.

[wg-llvm]: https://rust-lang.zulipchat.com/#narrow/stream/187780-t-compiler.2Fwg-llvm

## New LLVM Release Updates

Updating to a new release of LLVM is very similar to the "feature updates"
section above. The release process for LLVM is often months-long though and we
like to ensure compatibility ASAP. The main tweaks to the "feature updates"
section above is generally around branch naming. The sequence of events
typically looks like:

1. LLVM announces that its latest release version has branched. This will show
   up as a branch in https://github.com/llvm/llvm-project typically named
   `release/$N.x` where `$N` is the version of LLVM that's being released.

2. We then follow the "feature updates" section above to create a new branch of
   LLVM in our rust-lang/llvm-project repository. This follows the same naming
   convention of branches as usual, except that `a.b` is the new version. This
   update is eventually landed in the rust-lang/rust repository.

3. Over the next few months, LLVM will continually push commits to its
   `release/a.b` branch. Often those are bug fixes we'd like to have as well.
   The merge process for that is to use `git merge` itself to merge LLVM's
   `release/a.b` branch with the branch created in step 2. This is typically
   done multiple times when necessary while LLVM's release branch is baking.

4. LLVM then announces the release of version `a.b`.

5. After LLVM's official release, we follow the "feature update" section again
   to create a new branch in the rust-lang/llvm-project repository, this time
   with a new date. The commit history should look much cleaner as just a few
   Rust-specific commits stacked on top of stock LLVM's release branch.