Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support clobber_abi in AVR inline assembly #131323

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

jfrimmel
Copy link
Contributor

@jfrimmel jfrimmel commented Oct 6, 2024

This PR implements the clobber_abi part necessary to eventually stabilize the inline assembly for AVR. This is tracked in #93335.
This is heavily inspired by the sibling-PR #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.

r? @Amanieu

@rustbot label +O-AVR

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. O-AVR Target: AVR processors (ATtiny, ATmega, etc.) labels Oct 6, 2024
@@ -106,6 +106,7 @@ 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 issue is resolved, then R0 has to be added to clobbered registers
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to carefully keep track of LLVM here: if LLVM ceases to treat these registers as reserved then we must immediately add them to the set of clobbered registers, otherwise incorrect code will be generated. This comment should be updated to reflect this.

Also, while reviewing the GCC docs, it seems that R1 always holds a value of 0. If this is a guarantee that asm can use then it should be documented, similar to how on x86 we guarantee the direction flag is always clear when entering or exiting inline asm.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the feedback!

Do you have an idea on how to track LLVM here? I'd expect the ABI to be stable, but I don't know on how to test this, except for producing register-heavy code and hoping, that the "no more fixed register" would be used causing the test to fail. This seems rather fragile, though.
Or do you "just" want to phrase the comment different, so that the required immediate action is more clear?

For the second issue, I'll go ahead and document, that the R1 register needs to be restored if altered.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's enough to just reword the comment.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've reworded the comment, please have a look if this works for you.

Regarding the R1 register content: the unstable-book already documents, that the value needs to be restored if altered:

| AVR | `r0`, `r1`, `r1r0` | Due to an issue in LLVM, the `r0` and `r1` registers cannot be used as inputs or outputs. If modified, they must be restored to their original values before the end of the block. |

This commit adds the relevant registers to the list of clobbered regis-
ters (part of rust-lang#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
@jfrimmel
Copy link
Contributor Author

@rustbot labels: +A-inline-assembly
@rustbot ready

@rustbot rustbot added the A-inline-assembly Area: Inline assembly (`asm!(…)`) label Oct 14, 2024
Comment on lines 107 to 108
#error = ["r0", "r1", "r1r0"] =>
"r0 and r1 are not available due to an issue in LLVM",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(IIUC since r1 is a zero register, r1 and r1r0 cannot be used as an operand for inline asm, regardless of an LLVM issue.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think, the fact that r1 is the zero-reg is the cause for the registers not being available for inline asm (although I'm not sure). For now, I think this is fine (please correct me if you think otherwise!).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For now, I think this is fine (please correct me if you think otherwise!).

Agreed. We may need to figure it out in some way before stabilization, but at least it should not be a blocker of clobber_abi implementation.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would actually be nice to make the error message here clearer than "due to an issue in LLVM". The reason is that r0 is reserved as a scratch register and r1 is reserved as a constant zero register by LLVM.

tests/codegen/asm-clobber_abi-avr.rs Outdated Show resolved Hide resolved
@taiki-e
Copy link
Member

taiki-e commented Nov 2, 2024

Btw, AVRTiny1 appears to have a different convention (r0-r15 are unavailable, r16-r17 are similar to r0-r1 in AVR, r18-r19 are call-saved).

Footnotes

  1. avrtiny, attiny4, attiny5, attiny9, attiny10, attiny20, attiny40, attiny102, and attiny104 are marked as FamilyTiny in LLVM

@jfrimmel
Copy link
Contributor Author

jfrimmel commented Nov 2, 2024

Hm, I think in order to properly address the reduced tiny cores, one would need to wait for #131651 and parse the -Ctarget-cpu-flag to detect such a core. I suppose, this change would also be necessary for the general inline assembly and thus is somewhat out of scope of this PR. I see two options:

  1. merge this as-is and perform the necessary changes in a follow-up PR
  2. close this and wait for Create a generic AVR target: avr-unknown-unknown #131651 to be merged, then fix the reduced tiny cores and then the clobber ABI.

@taiki-e
Copy link
Member

taiki-e commented Nov 3, 2024

I suppose, this change would also be necessary for the general inline assembly and thus is somewhat out of scope of this PR.

I think it is indeed true that inline assembly support for AVRTiny is already broken (registers that should be reserved are not reserved) and therefore does not need to be fixed in this PR.
(EDIT: It should need to be fixed before stabilization, so I added it to #93335 (comment) 's list.)

parse the -Ctarget-cpu-flag to detect such a core.

I think it is better to refer to the tinyencoding target feature as LLVM does, instead of parsing the target-cpu.
(I think this could be implemented in a similar way to #132472 (comment).)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-inline-assembly Area: Inline assembly (`asm!(…)`) O-AVR Target: AVR processors (ATtiny, ATmega, etc.) S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants