From af0c1bc08437af4633164411917d36c1b25b7c2d Mon Sep 17 00:00:00 2001 From: Will Springer Date: Sun, 12 Sep 2021 00:55:30 -0700 Subject: [PATCH] asm/arm: allow r9 when usable, make diagnostics more specific As described in #85247, there are a number of cases where inline ARM assembly in Rust programs is prevented from using certain registers. Sometimes these registers are only reserved in certain situations; however, they are all unconditionally restricted by the compiler. Many of rustc's diagnostics describe the registers as being "used internally by LLVM", which obfuscates the meaning of the registers and is ultimately unhelpful to low-level programmers. At the moment, it does not seem possible to ensure that the frame pointer or base pointer will be elided, or to understand the conditions under which this may occur. Some of this is due to the addition of a codegen abstraction layer, where these behaviors may vary between backends and may not be configurable. As such, this information is not available to Rust's assembly parser. However, register r9 has a condition that is rather easily checked: the relocation model. r9 is used as the "static base" (SB) for "read-write position independence" (RWPI) as a base from which to address writable data, as defined in the Procedure Call Standard for the Arm Architecture[1]. The register is always available for use if RWPI is not used[2]. Rust allows RWPI mode to be selected on a per-target basis, as well as making it available as a codegen option in rustc. Thus, we can predicate usage of r9 on this setting with confidence. Allow r9 to be used in `asm!` blocks when the target does not use RWPI, and define what r6 and r9 are reserved for in their respective diagnostics. The frame pointer (r7/r11) and base pointer (r6) remain unconditionally restricted for now. [1]: https://developer.arm.com/documentation/ihi0042/j/ [2]: https://developer.arm.com/documentation/dui0056/d/using-the-procedure-call-standard/read-write-position-independence/register-usage-with-rwpi --- compiler/rustc_target/src/asm/arm.rs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_target/src/asm/arm.rs b/compiler/rustc_target/src/asm/arm.rs index 4c323fc35d643..b7aca6cbbe956 100644 --- a/compiler/rustc_target/src/asm/arm.rs +++ b/compiler/rustc_target/src/asm/arm.rs @@ -1,5 +1,5 @@ use super::{InlineAsmArch, InlineAsmType}; -use crate::spec::Target; +use crate::spec::{RelocModel, Target}; use rustc_macros::HashStable_Generic; use std::fmt; @@ -88,6 +88,18 @@ fn frame_pointer_r7( } } +fn rwpi_enabled( + _arch: InlineAsmArch, + _has_feature: impl FnMut(&str) -> bool, + target: &Target, +) -> Result<(), &'static str> { + if let RelocModel::Rwpi | RelocModel::RopiRwpi = target.relocation_model { + Err("the RWPI static base register (r9) cannot be used as an operand for inline asm") + } else { + Ok(()) + } +} + def_regs! { Arm ArmInlineAsmReg ArmInlineAsmRegClass { r0: reg, reg_thumb = ["r0", "a1"], @@ -98,6 +110,7 @@ def_regs! { r5: reg, reg_thumb = ["r5", "v2"], r7: reg, reg_thumb = ["r7", "v4"] % frame_pointer_r7, r8: reg = ["r8", "v5"], + r9: reg = ["r9", "v6", "rfp"] % rwpi_enabled, r10: reg = ["r10", "sl"], r11: reg = ["r11", "fp"] % frame_pointer_r11, r12: reg = ["r12", "ip"], @@ -183,9 +196,7 @@ def_regs! { q14: qreg = ["q14"], q15: qreg = ["q15"], #error = ["r6", "v3"] => - "r6 is used internally by LLVM and cannot be used as an operand for inline asm", - #error = ["r9", "v6", "rfp"] => - "r9 is used internally by LLVM and cannot be used as an operand for inline asm", + "the base pointer (r6) cannot be used as an operand for inline asm", #error = ["r13", "sp"] => "the stack pointer cannot be used as an operand for inline asm", #error = ["r15", "pc"] =>