Skip to content

-Zfixed-x18 #748

Closed
Closed

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 using target.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.
  • 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Assignees

No one assigned

    Labels

    T-compilerAdd this label so rfcbot knows to poll the compiler teammajor-changeA proposal to make a major change to rustcmajor-change-acceptedA major change proposal that was accepted

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions