From ba7316655645c020263e207ee9c036131b511d45 Mon Sep 17 00:00:00 2001 From: Julian Frimmel Date: Sun, 6 Oct 2024 12:33:25 +0200 Subject: [PATCH 1/4] Support `clobber_abi` for AVR inline assembly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit adds the relevant registers to the list of clobbered regis- ters (part of #93335). This follows the [ABI documentation] of AVR-GCC: > The [...] call-clobbered general purpose registers (GPRs) are > registers that might be destroyed (clobbered) by a function call. > > - **R18–R27, R30, R31** > > These GPRs are call clobbered. An ordinary function may use them > without restoring the contents. [...] > > - **R0, T-Flag** > > The temporary register and the T-flag in SREG are also call- > clobbered, but this knowledge is not exposed explicitly to the > compiler (R0 is a fixed register). Therefore this commit lists the aforementioned registers `r18–r27`, `r30` and `r31` as clobbered registers. Since the `r0` register (listed above as well) is not available in inline assembly at all (potentially because the AVR-GCC considers it a fixed register causing the register to never be used in register allocation and LLVM adopting this), there is no need to list it in the clobber list (the `r0`-variant is not even available). A comment was added to ensure, that the `r0` gets added to the clobber-list once the register gets usable in inline ASM. Since the SREG is normally considered clobbered anyways (unless the user supplies the `preserve_flags`-option), there is no need to explicitly list a bit in this register (which is not possible to list anyways). Note, that this commit completely ignores the case of interrupts (that are described in the ABI-specification), since every register touched in an ISR need to be saved anyways. [ABI documentation]: https://gcc.gnu.org/wiki/avr-gcc#Call-Used_Registers --- compiler/rustc_target/src/asm/avr.rs | 5 +++++ compiler/rustc_target/src/asm/mod.rs | 22 ++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/compiler/rustc_target/src/asm/avr.rs b/compiler/rustc_target/src/asm/avr.rs index 276f376b297e6..9adcbecdf3c10 100644 --- a/compiler/rustc_target/src/asm/avr.rs +++ b/compiler/rustc_target/src/asm/avr.rs @@ -106,6 +106,11 @@ def_regs! { "the stack pointer cannot be used as an operand for inline asm", #error = ["r0", "r1", "r1r0"] => "r0 and r1 are not available due to an issue in LLVM", + // If this changes within LLVM, the compiler might use the registers + // in the future. This must be reflected in the set of clobbered + // registers, else the clobber ABI implementation is *unsound*, as + // this generates invalid code (register is not marked as clobbered + // but may change the register content). } } diff --git a/compiler/rustc_target/src/asm/mod.rs b/compiler/rustc_target/src/asm/mod.rs index 9fe733e063cf5..204d5d5336102 100644 --- a/compiler/rustc_target/src/asm/mod.rs +++ b/compiler/rustc_target/src/asm/mod.rs @@ -928,6 +928,7 @@ pub enum InlineAsmClobberAbi { AArch64, AArch64NoX18, Arm64EC, + Avr, RiscV, RiscVE, LoongArch, @@ -986,6 +987,10 @@ impl InlineAsmClobberAbi { }), _ => Err(&["C", "system", "efiapi"]), }, + InlineAsmArch::Avr => match name { + "C" | "system" => Ok(InlineAsmClobberAbi::Avr), + _ => Err(&["C", "system"]), + }, InlineAsmArch::LoongArch64 => match name { "C" | "system" => Ok(InlineAsmClobberAbi::LoongArch), _ => Err(&["C", "system"]), @@ -1133,6 +1138,23 @@ impl InlineAsmClobberAbi { d24, d25, d26, d27, d28, d29, d30, d31, } }, + InlineAsmClobberAbi::Avr => clobbered_regs! { + Avr AvrInlineAsmReg { + // The list of "Call-Used Registers" according to + // https://gcc.gnu.org/wiki/avr-gcc#Call-Used_Registers + + // Clobbered registers available in inline assembly + r18, r19, r20, r21, r22, r23, r24, r25, r26, r27, r30, r31, + // As per the AVR-GCC-ABI documentation linked above, the R0 + // register is a clobbered register as well. Since we don't + // allow the usage of R0 in inline assembly, nothing has to + // be done here. + // Likewise, the T-flag in the SREG should be clobbered, but + // this is not necessary to be listed here, since the SREG + // is considered clobbered anyways unless `preserve_flags` + // is used. + } + }, InlineAsmClobberAbi::RiscV => clobbered_regs! { RiscV RiscVInlineAsmReg { // ra From d7e0a3eee0c5e79134664293eea3c46b91f0bec7 Mon Sep 17 00:00:00 2001 From: Julian Frimmel Date: Sun, 6 Oct 2024 13:09:29 +0200 Subject: [PATCH 2/4] Add test case for the clobber options --- tests/codegen/asm-clobber_abi-avr.rs | 43 ++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 tests/codegen/asm-clobber_abi-avr.rs diff --git a/tests/codegen/asm-clobber_abi-avr.rs b/tests/codegen/asm-clobber_abi-avr.rs new file mode 100644 index 0000000000000..6e0c75368e23f --- /dev/null +++ b/tests/codegen/asm-clobber_abi-avr.rs @@ -0,0 +1,43 @@ +//@ assembly-output: emit-asm +//@ compile-flags: --target avr-unknown-gnu-atmega328 +//@ needs-llvm-components: avr + +#![crate_type = "rlib"] +#![feature(no_core, rustc_attrs, lang_items, asm_experimental_arch)] +#![no_core] + +#[lang = "sized"] +trait Sized {} + +#[rustc_builtin_macro] +macro_rules! asm { + () => {}; +} + +// CHECK-LABEL: @sreg_is_clobbered +// CHECK: void asm sideeffect "", "~{sreg}"() +#[no_mangle] +pub unsafe fn sreg_is_clobbered() { + asm!("", options(nostack, nomem)); +} + +// CHECK-LABEL: @sreg_is_not_clobbered_if_preserve_flags_is_used +// CHECK: void asm sideeffect "", ""() +#[no_mangle] +pub unsafe fn sreg_is_not_clobbered_if_preserve_flags_is_used() { + asm!("", options(nostack, nomem, preserves_flags)); +} + +// CHECK-LABEL: @clobber_abi +// CHECK: asm sideeffect "", "={r18},={r19},={r20},={r21},={r22},={r23},={r24},={r25},={r26},={r27},={r30},={r31},~{sreg}"() +#[no_mangle] +pub unsafe fn clobber_abi() { + asm!("", clobber_abi("C"), options(nostack, nomem)); +} + +// CHECK-LABEL: @clobber_abi_with_preserved_flags +// CHECK: asm sideeffect "", "={r18},={r19},={r20},={r21},={r22},={r23},={r24},={r25},={r26},={r27},={r30},={r31}"() +#[no_mangle] +pub unsafe fn clobber_abi_with_preserved_flags() { + asm!("", clobber_abi("C"), options(nostack, nomem, preserves_flags)); +} From 2bd3bbb2e0132d6d9c3766f35c2efc34414a8a66 Mon Sep 17 00:00:00 2001 From: Julian Frimmel Date: Sat, 2 Nov 2024 08:25:53 +0100 Subject: [PATCH 3/4] Move & rename test case to match naming of #132456 --- tests/codegen/{asm-clobber_abi-avr.rs => asm/avr-clobbers.rs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/codegen/{asm-clobber_abi-avr.rs => asm/avr-clobbers.rs} (100%) diff --git a/tests/codegen/asm-clobber_abi-avr.rs b/tests/codegen/asm/avr-clobbers.rs similarity index 100% rename from tests/codegen/asm-clobber_abi-avr.rs rename to tests/codegen/asm/avr-clobbers.rs From 67d2f3f6857a1d28113f1a63eb003b53d17088c9 Mon Sep 17 00:00:00 2001 From: Julian Frimmel Date: Thu, 28 Nov 2024 12:57:42 +0100 Subject: [PATCH 4/4] Reword error message of reserved AVR registers Those are reserved as per the GCC (and thus LLVM) ABI, which is distinct from an issue. The rewording was requested in this [review]. [review]: https://github.com/rust-lang/rust/pull/131323#issuecomment-2479178721 --- compiler/rustc_target/src/asm/avr.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_target/src/asm/avr.rs b/compiler/rustc_target/src/asm/avr.rs index 9adcbecdf3c10..55d393c81d3ec 100644 --- a/compiler/rustc_target/src/asm/avr.rs +++ b/compiler/rustc_target/src/asm/avr.rs @@ -105,7 +105,7 @@ def_regs! { #error = ["SP", "SPL", "SPH"] => "the stack pointer cannot be used as an operand for inline asm", #error = ["r0", "r1", "r1r0"] => - "r0 and r1 are not available due to an issue in LLVM", + "LLVM reserves r0 (scratch register) and r1 (zero register)", // If this changes within LLVM, the compiler might use the registers // in the future. This must be reflected in the set of clobbered // registers, else the clobber ABI implementation is *unsound*, as