Skip to content

Commit

Permalink
Rollup merge of rust-lang#131323 - jfrimmel:avr-inline-asm-clobber-ab…
Browse files Browse the repository at this point in the history
…i, r=Amanieu

Support `clobber_abi` in AVR inline assembly

This PR implements the `clobber_abi` part necessary to eventually stabilize the inline assembly for AVR. This is tracked in rust-lang#93335.
This is heavily inspired by the sibling-PR rust-lang#131310 for the MSP430. I've explained my reasoning in the first commit message in detail, which is reproduced below for easier reviewing:

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

r? `@Amanieu`

`@rustbot` label +O-AVR
  • Loading branch information
Zalathar authored Nov 29, 2024
2 parents d6f8829 + 67d2f3f commit 960f367
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 1 deletion.
7 changes: 6 additions & 1 deletion compiler/rustc_target/src/asm/avr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,12 @@ 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
// this generates invalid code (register is not marked as clobbered
// but may change the register content).
}
}

Expand Down
22 changes: 22 additions & 0 deletions compiler/rustc_target/src/asm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -928,6 +928,7 @@ pub enum InlineAsmClobberAbi {
AArch64,
AArch64NoX18,
Arm64EC,
Avr,
RiscV,
RiscVE,
LoongArch,
Expand Down Expand Up @@ -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"]),
Expand Down Expand Up @@ -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
Expand Down
43 changes: 43 additions & 0 deletions tests/codegen/asm/avr-clobbers.rs
Original file line number Diff line number Diff line change
@@ -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));
}

0 comments on commit 960f367

Please sign in to comment.