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

rustc_target: RISC-V Zfinx is incompatible with {ILP32,LP64}[FD] ABIs #138872

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

a4lg
Copy link

@a4lg a4lg commented Mar 24, 2025

Because RISC-V Calling Conventions note that:

This means code targeting the Zfinx extension always uses the ILP32, ILP32E or LP64 integer calling-convention only ABIs as there is no dedicated hardware floating-point register file.

{ILP32,LP64}[FD] ABIs with hardware floating-point calling conventions are incompatible with the Zfinx extension.

This commit adds "zfinx" to the incompatible feature list to those ABIs and tests whether trying to add "zdinx" (that is analogous to "zfinx" but in double-precision) on a LP64D ABI configuration results in an error (it also tests extension implication; Zdinx requires Zfinx extension).

Links: RISC-V psABI specification version 1.0
https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/v1.0/riscv-cc.adoc#named-abis
https://github.com/riscv-non-isa/riscv-elf-psabi-doc/releases/tag/v1.0

Related:

@rustbot label +T-compiler +O-riscv +A-target-feature

Because RISC-V Calling Conventions note that:

> This means code targeting the Zfinx extension always uses the ILP32,
> ILP32E or LP64 integer calling-convention only ABIs as there is no
> dedicated hardware floating-point register file.

{ILP32,LP64}[FD] ABIs with hardware floating-point calling conventions
are incompatible with the "Zfinx" extension.

This commit adds "zfinx" to the incompatible feature list to those ABIs
and tests whether trying to add "zdinx" (that is analogous to "zfinx" but
in double-precision) on a LP64D ABI configuration results in an error
(it also tests extension implication; "Zdinx" requires "Zfinx" extension).

Link: RISC-V psABI specification version 1.0
<https://github.com/riscv-non-isa/riscv-elf-psabi-doc/releases/tag/v1.0>
@rustbot
Copy link
Collaborator

rustbot commented Mar 24, 2025

r? @jieyouxu

rustbot has assigned @jieyouxu.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

@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. A-target-feature Area: Enabling/disabling target features like AVX, Neon, etc. O-riscv Target: RISC-V architecture labels Mar 24, 2025
@a4lg
Copy link
Author

a4lg commented Mar 24, 2025

To note, both GNU and LLVM toolchains check those conditions by making sure that F and Zfinx cannot coexist.

This commit implements roughly the equivalent more directly on the ABI level but not on the extension level (i.e. without prohibiting coexistence F and Zfinx on integer ABIs) because Rust currently does not implement checking / modifying feature sets after all feature sets are determined.

I'm in an experiment how to implement those (and possibly complex implication rules on RISC-V) cleanly (while supporting all codegen backend). That's because, while prohibiting F + Zfinx can be an ABI matter, there are some cases where RISC-V-related extension compatibility checks / complex implications are not an ABI matter.

@jieyouxu
Copy link
Member

r? @workingjubilee (or reroll)

@rustbot rustbot assigned workingjubilee and unassigned jieyouxu Mar 24, 2025
@workingjubilee
Copy link
Member

This commit implements roughly the equivalent more directly on the ABI level but not on the extension level (i.e. without prohibiting coexistence F and Zfinx on integer ABIs) because Rust currently does not implement checking / modifying feature sets after all feature sets are determined.

Hrrrrm.

cc @RalfJung @veluca93 I feel like we did build up some mechanisms for this somewhere, but maybe we really didn't and that would be new?

@a4lg
Copy link
Author

a4lg commented Mar 25, 2025

Note that, while long-term solution is required sometime, I think this PR itself will not be blocked by those because:

  1. Both F and Zfinx extensions are now experimental in Rust (I suppose to tackle with ABI-related issues?).
  2. {ILP32,LP64}[FD] ABIs also forbid Zfinx (while F and Zfinx are stated incompatible in the extension level; both GNU and LLVM resolves this issue in the extension level but rather indirectly from the ABI side).
    I submitted this PR because handling this condition on the ABI side generates useful diagnostics for Rust users.

FYI, I'll list some of the examples here but I think discussing about it is another topic.
Could someone lead me somewhere better to discuss about such issues?

  • An example of extension constraints: Zvl[X]b (extensions constraining vector register length; [X] is a power of two between 32 and 65536) requires one of the vector extension subsets including V (which requires Zvl128b), Zve32x (requires Zvl32b) and Zve64d (requires Zvl64b) but Zvl*b cannot imply vector extensions because vector register size cannot constrain the vector arithmetic available on the platform on most cases (related: rustc_target: Add more RISC-V vector-related features and use zvl*b target features in vector ABI check #138742).
  • Implication from the C extension (compression instruction extension) to compression extension subsets is rather complex mainly because subsets are ratified later than C (on LLVM, correct rules will be implemented in LLVM 21; [RISCV] Implement the implications of C extension llvm/llvm-project#132259). This is the reason I don't submit a PR to add compression extension subsets for a while (despite that those are now discoverable on runtime from the Linux kernel).
    • C implies Zca (only this rule can be easily implemented in Rust but also requires backporting of above PR due to the fact that extension implication of default target features is effectively performed on the backend, at least on LLVM codegen).
    • C and F implies Zcf only on RV32.
    • C and D implies Zcd (both on RV32/RV64).
  • Handling a group of extensions as a superset (combination) can be convenient sometimes and LLVM implements this.
    For instance, Zk (standard scalar cryptography superset) is a combination of Zkn, Zkr and Zkt (and Zkn is also a combination of Zbkb, Zbkc, Zbkx, Zkne, Zknd and Zknh). Of course, Zk requires all Zkn, Zkr and Zkt. But not only that, the Zk extension is automatically inferred when all the subset prerequisites are enabled.

@RalfJung
Copy link
Member

RalfJung commented Mar 25, 2025

abi_required_features is exactly the place to encode when particular target features are required by or incompatible with a particular ABI. So without knowing anything about the specifics of Zfinx, this looks like the right place to register such a conflict.

I have no idea what this sentence means:
"Rust currently does not implement checking / modifying feature sets after all feature sets are determined."

Copy link
Member

Choose a reason for hiding this comment

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

Please make the filename and test comment more specific. We already have tests ensuring that forbidden targets in an attribute are rejected, so if this is just "exactly the same test as before but with a different target feature", there's no point in such duplication.

Copy link
Author

Choose a reason for hiding this comment

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

Okay, I'll fix that.

@RalfJung
Copy link
Member

RalfJung commented Mar 25, 2025

This commit implements roughly the equivalent more directly on the ABI level but not on the extension level (i.e. without prohibiting coexistence F and Zfinx on integer ABIs)

Should we be rejecting F + Zfinx for the integer ABIs? What happens if you try that combination?

@RalfJung
Copy link
Member

I am confused about the status of Zfinx + F now. Does adding Zfinx somehow change the ABI of the function? Adding them to the check the way this PR does makes it sound like yes, it changes the ABI.

OTOH some of the other comments above make it sound like it's not about the ABI at all, it's more that within a function, we can't have both Zfinx and F for some reason. That would seem to me like it's a different case and requires new machinery in rustc to check (or we can rely on LLVM checking this, since it's a per-function thing and not some global soundness property).

@a4lg
Copy link
Author

a4lg commented Mar 25, 2025

abi_required_features is exactly the place to encode when particular target features are required by or incompatible with a particular ABI. So without knowing anything about the specifics of Zfinx, this looks like the right place to register such a conflict.

As in the first comment, RISC-V Calling Conventions (a part of RISC-V psABI) states that:

This means code targeting the Zfinx extension always uses the ILP32, ILP32E or LP64 integer calling-convention only ABIs as there is no dedicated hardware floating-point register file.

So, I think this is valid.

Give me some time to reply your other comments.

@workingjubilee
Copy link
Member

workingjubilee commented Mar 26, 2025

@RalfJung ah, Zfinx + F is effectively a nonsensical combination because

  • F means "this CPU does float operations in float registers".
  • Zfinx means "this CPU does float operations in integer registers"... literally "f-in-x", as the integer registers on RISCV are all x{N}.

There is a subset of float operations that thus exists in each context... the ones that require specifically F, for instance, are the "load from F register to X register" ones, but fused-multiply-add exists on both. The fmadd instruction is even encoded identically for each variant. The instruction's operands change meaning, however, for Zfinx: each bitfield in the encoding that addresses an F register now addresses the identically-numbered X register.

@workingjubilee
Copy link
Member

@a4lg Can you add comments that record the fact that Zfinx is incompatible at the opcode level with F, as the two features use the same opcodes yet address different registers? Most features aren't "mutually exclusive" in this way.

@RalfJung
Copy link
Member

I see, thanks.

So... what does LLVM do when you enable both?

@workingjubilee
Copy link
Member

die, hopefully

@workingjubilee
Copy link
Member

@a4lg Can you obtain a definitive answer instead of my hopes?

@12101111
Copy link
Contributor

Clang driver will print a error message

https://github.com/llvm/llvm-project/blob/release/20.x/clang/test/Driver/riscv-arch.c#L534

LLVM will crash

LLVM ERROR: 'f' and 'zfinx' extensions are incompatible

PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace.
Stack dump:
0.      Program arguments: llc test.ll -mtriple riscv64-unknown-linux-musl -mattr=+i,+m,+a,+c,+f,+zfinx
#0 0x00007f351dc9dbc2 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (/usr/lib/llvm/20/bin/../lib/libLLVM.so.20.1+libcxx+0x43aebc2)
#1 0x00007f351dc9ac70 llvm::sys::RunSignalHandlers() (/usr/lib/llvm/20/bin/../lib/libLLVM.so.20.1+libcxx+0x43abc70)
#2 0x00007f351dc9e3b6 (/usr/lib/llvm/20/bin/../lib/libLLVM.so.20.1+libcxx+0x43af3b6)
zsh: abort (core dumped)  llc test.ll -mtriple riscv64-unknown-linux-musl -mattr=+i,+m,+a,+c,+f,+zfinx

The check code in llvm: https://github.com/llvm/llvm-project/blob/release/20.x/llvm/lib/TargetParser/RISCVISAInfo.cpp#L724

Some tests:
https://github.com/llvm/llvm-project/blob/release/20.x/llvm/test/MC/RISCV/option-invalid.s#L47
https://github.com/llvm/llvm-project/blob/release/20.x/llvm/unittests/TargetParser/RISCVISAInfoTest.cpp#L607

@RalfJung
Copy link
Member

LLVM will crash

Oh lol.^^

I'm fine with landing this PR, but it's only a half-way solution then. Could you file an issue tracking adding proper rustc checks here so one can never hit the LLVM error?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-target-feature Area: Enabling/disabling target features like AVX, Neon, etc. O-riscv Target: RISC-V architecture 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.

6 participants