Description
Proposal
This MCP proposes to add a -Zfixed-x18
flag on aarch64 targets. The flag has the same effect as the -ffixed-x18
flag that is supported by clang and gcc. When the flag is passed, the x18 register will not be used as a temporary register in the generated machine code.
Proposed implementation: rust-lang/rust#124655
Feature request: rust-lang/rust#121970
Motivation
This motivation for this change is use in the Linux Kernel. When compiling Rust code for the kernel, the aarch64-unknown-none
target is used, and this is a platform where x18 is a temporary caller-saved register by default. I am proposing to add this flag so that the Linux Kernel can make x18 into a reserved register when necessary.
The Linux Kernel has some cases where it needs to reserve x18, but does not pass the -Zsanitizer=shadow-call-stack
flag. This is due to the dynamic shadow call stack feature, where the Linux Kernel is able to choose whether SCS should be enabled at boot. This works by having the compiler emit PACIASP/AUTIASP instructions instead of SCS_PUSH/SCS_POP. If Linux decides to enable SCS at boot, then it will use the unwind tables to find the PACIASP/AUTIASP instructions, and modify the machine code at runtime by replacing PACIASP/AUTIASP with SCS_PUSH/SCS_POP instructions in all functions. The transformation from PACIASP/AUTIASP to SCS_PUSH/SCS_POP is only valid if the x18 register is reserved globally.
The Linux Kernel configuration used by Android uses the dynamic shadow call stack feature in production, so a Rust equivalent to clang's -ffixed-x18
flag is a prerequisite for using Rust in the Linux Kernel on Android.
ABI compatibility
Although this flag changes the ABI by changing the purpose of the x18 register, it does so in a way that is not breaking. On its own, mixing code that does or does not use the -Zfixed-x18
flag can't lead to UB. See rust-lang/rust#124323 for a more detailed discussion on this.
Architecture specific flags
Rustc does not currently have any flags that are architecture-specific. This MCP is proposing to add the first such flag.
Alternatives
This MCP proposes to expose this feature via a flag called -Zfixed-x18
, however there alternatives.
Adding a new target feature
Internally, clang implements the -ffixed-x18
flag by passing a target feature called reserve-x18
to LLVM, and our -Zfixed-x18
flag would be implemented in the same manner. However, this means that an alternative implementation would be expose reserve-x18
as a stable target feature in the compiler.
This MCP does not propose to add a new target feature because reserving the x18 register does not really seem like it "fits in" with the notion of a target feature. It seems better to keep that as an implementation detail and expose a -ffixed-x18
flag like clang.
The author of this MCP has previously proposed to implement this feature by exposing a target feature: rust-lang/rust#124323.
Global target features / target modifiers
Effectively, this flag could be considered to modify the target that the compiler is compiling for. We could add a more general flag to make modifications to the target. It would be required that the same modifications are made to all compilation units (else UB), so that this could be used with other features that modify the target and can cause UB when mixed.
The author believes that some sort of global target feature / target modifier feature should be added to the language, but the author does not believe that this particular flag should be a global target feature / target modifier because mixing it cannot result in UB.
Add a new target
There are already some aarch64 targets that always reserve the x18 register, so we could add a target that is like aarch64-unknown-none
except that the x18 register is reserved. This MCP does not propose that solution due to several disadvantages:
- Using built-in targets come with the assumption that all flags specified in this way can be split into a short list of targets with particular values for all of the flags. However, in the Linux Kernel, these flags are instead specified using individual on/off toggles. To support this using targets, we need O(2^n) different targets.
- Using a
target.json
is perma-unstable. (The Linux Kernel is usingtarget.json
on x86 right now due to similar issues with a different flag.)
Finally, the usual argument for why this kind of thing should be part of the target is that mixing it can cause UB, which is not the case for -Zfixed-x18
. (Even though all known use-cases require it to not be mixed.)
Mentors or Reviewers
If you have a reviewer or mentor in mind for this work, mention them
here. You can put your own name here if you are planning to mentor the
work.
Process
The main points of the Major Change Process are as follows:
- File an issue describing the proposal.
- A compiler team member or contributor who is knowledgeable in the area can second by writing
@rustbot second
.- Finding a "second" suffices for internal changes. If however, you are proposing a new public-facing feature, such as a
-C flag
, then full team check-off is required. - Compiler team members can initiate a check-off via
@rfcbot fcp merge
on either the MCP or the PR.
- Finding a "second" suffices for internal changes. If however, you are proposing a new public-facing feature, such as a
- Once an MCP is seconded, the Final Comment Period begins. If no objections are raised after 10 days, the MCP is considered approved.
You can read more about Major Change Proposals on forge.
Comments
This issue is not meant to be used for technical discussion. There is a Zulip stream for that. Use this issue to leave procedural comments, such as volunteering to review, indicating that you second the proposal (or third, etc), or raising a concern that you would like to be addressed.