Change:
On the target, specifying the target feature using the flag results in the following warning: The desired effect of this flag is to reserve the x18 register so that LLVM does not use it during codegen. Note that despite the warning, the flag still works fine today since it is passed on to LLVM. This feature is necessary for Rust to work when using the Linux Kernel's dynamic shadow call stack feature on aarch64 (). The shadow call stack feature requires that the x18 register is reserved, since it is used to store the shadow stack. In C code, the dynamic version of shadow call stack requires you to pass but not (which is only used for non-dynamic SCS). Clang has a collection of flags for reserving other registers than x18 as well. I think it would make sense to add those as well, even if they are less useful than x18. Related to . Related to .
This affects the ABI, right? I think adding a target spec option makes the most sense (or actually you can already set it as target feature in the target spec without giving a warning). That way you can't enable or disable it for individual functions or crates, which would break the ABI.
Yeah, it would be nice to have a flag for this (or having something like "global target features", i.e. like , but that it ensures it is set the same way for all compilation units).
There could be ABI concerns with this in general. AAPCS64 defines x18 as a platform register if required, or temporary if a platform register isn't needed. For x18 it's probably unlikely for it to have ABI concerns as it's already reserved for use on some platforms, the ones where it's not already reserved, it's only supposed to be a temporary register so not using it should be OK as it is not used for any fixed purpose. That's obviously assuming a platform defines its ABI based on the Arm standards. LLVM does support reserve on other registers, most notably for ABI reasons, x1 through x7 which would have ABI problems. LLVM does have test cases to ensure that a function call doesn't occur if any of the argument registers have been reserved. I'm not sure what other codegen backends do in that case though, or if they even support reserving those registers. The analysis that LLVM does, doesn't seem to care if it would need to use that register for ABI reasons, even a function such as can't be called in a quick test. Using target features for this feels strange to me though, I'm wondering if a codegen option to reserve certain registers would be a better approach here. While this issue is specifically about reserving a register on AArch64, LLVM does support this for multiple other architectures and multiple registers, which we should maybe consider for the future.

Discussion:
codegen_llvm_error_writing_def_file = Error writing .DEF file: {$error} <ins> codegen_llvm_fixed_x18_invalid_arch = the `-Zfixed-x18` flag is not supported on the `{$arch}` architecture </ins> codegen_llvm_from_llvm_diag = {$message} codegen_llvm_from_llvm_optimization_diag = {$filename}:{$line}:{$column} {$pass_name} ({$kind}): {$message}

pub(crate) struct InvalidTargetFeaturePrefix<'a> { pub feature: &'a str, } <ins> #[derive(Diagnostic)] #[diag(codegen_llvm_fixed_x18_invalid_arch)] pub(crate) struct FixedX18InvalidArch<'a> { pub arch: &'a str, } </ins>

use crate::back::write::create_informational_target_machine; use crate::errors::{ <del> InvalidTargetFeaturePrefix, PossibleFeature, TargetFeatureDisableOrEnable, </del> <ins> FixedX18InvalidArch, InvalidTargetFeaturePrefix, PossibleFeature, TargetFeatureDisableOrEnable, </ins> UnknownCTargetFeature, UnknownCTargetFeaturePrefix, UnstableCTargetFeature, }; use crate::llvm;

.flatten(); features.extend(feats); <ins> // -Zfixed-x18 if sess.opts.unstable_opts.fixed_x18 { if sess.target.arch != "aarch64" { sess.dcx().emit_fatal(FixedX18InvalidArch { arch: &sess.target.arch }); } else { features.push("+reserve-x18".into()); } } </ins> if diagnostics && let Some(f) = check_tied_features(sess, &featsmap) { sess.dcx().emit_err(TargetFeatureDisableOrEnable { features: f,

tracked!(emit_thin_lto, false); tracked!(export_executable_symbols, true); tracked!(fewer_names, Some(true)); <ins> tracked!(fixed_x18, true); </ins> tracked!(flatten_format_args, false); tracked!(force_unstable_if_unmarked, true); tracked!(fuel, Some(("abc".to_string(), 99)));

fewer_names: Option<bool> = (None, parse_opt_bool, [TRACKED], "reduce memory use by retaining fewer names within compilation artifacts (LLVM-IR)  (default: no)"), <ins> fixed_x18: bool = (false, parse_bool, [TRACKED], "make the x18 register reserved on AArch64 (default: no)"), </ins> flatten_format_args: bool = (true, parse_bool, [TRACKED], "flatten nested format_args!() and literals into a simplified format_args!() call  (default: yes)"),

<ins> # `fixed-x18` This option prevents the compiler from using the x18 register. It is only supported on aarch64. From the [ABI spec][arm-abi]: > X18 is the platform register and is reserved for the use of platform ABIs. > This is an additional temporary register on platforms that don't assign a > special meaning to it. This flag only has an effect when the x18 register would otherwise be considered a temporary register. When the flag is applied, x18 is always a reserved register. This flag is intended for use with the shadow call stack sanitizer. Generally, when that sanitizer is enabled, the x18 register is used to store a pointer to the shadow stack. Enabling this flag prevents the compiler from overwriting the shadow stack pointer with temporary data, which is necessary for the sanitizer to work correctly. Currently, the `-Zsanitizer=shadow-call-stack` flag is only supported on platforms that always treat x18 as a reserved register, and the `-Zfixed-x18` flag is not required to use the sanitizer on such platforms. However, the sanitizer may be supported on targets where this is not the case in the future. It is undefined behavior for `-Zsanitizer=shadow-call-stack` code to call into code where x18 is a temporary register. On the other hand, when you are *not* using the shadow call stack sanitizer, compilation units compiled with and without the `-Zfixed-x18` flag are compatible with each other. [arm-abi]: https://developer.arm.com/documentation/den0024/a/The-ABI-for-ARM-64-bit-Architecture/Register-use-in-the-AArch64-Procedure-Call-Standard/Parameters-in-general-purpose-registers </ins>

<ins> // Test that the `reserve-x18` target feature is (not) emitted when // the `-Zfixed-x18` flag is (not) set. //@ revisions: unset set //@ needs-llvm-components: aarch64 //@ compile-flags: --target aarch64-unknown-none //@ [set] compile-flags: -Zfixed-x18 #![crate_type = "lib"] #![feature(no_core, lang_items)] #![no_core] #[lang = "sized"] trait Sized {} #[no_mangle] pub fn foo() { // CHECK: @foo() unnamed_addr #0 // unset-NOT: attributes #0 = { {{.*}}"target-features"="{{[^"]*}}+reserve-x18{{.*}} } // set: attributes #0 = { {{.*}}"target-features"="{{[^"]*}}+reserve-x18{{.*}} } } </ins>

<ins> // This tests that -Zfixed-x18 causes a compilation failure on targets other than aarch64. // Behavior on aarch64 is tested by tests/codegen/fixed-x18.rs. // //@ revisions: x64 i686 arm riscv32 riscv64 //@ error-pattern: the `-Zfixed-x18` flag is not supported //@ dont-check-compiler-stderr // //@ compile-flags: -Zfixed-x18 //@ [x64] needs-llvm-components: x86 //@ [x64] compile-flags: --target=x86_64-unknown-linux-gnu --crate-type=rlib //@ [i686] needs-llvm-components: x86 //@ [i686] compile-flags: --target=i686-unknown-linux-gnu --crate-type=rlib //@ [arm] needs-llvm-components: arm //@ [arm] compile-flags: --target=armv7-unknown-linux-gnueabihf --crate-type=rlib //@ [riscv32] needs-llvm-components: riscv //@ [riscv32] compile-flags: --target=riscv32i-unknown-none-elf --crate-type=rlib //@ [riscv64] needs-llvm-components: riscv //@ [riscv64] compile-flags: --target=riscv64gc-unknown-none-elf --crate-type=rlib #![crate_type = "lib"] #![feature(no_core, lang_items)] #![no_core] #[lang = "sized"] trait Sized {} </ins>