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

Rework riscv -march and -mabi detection #796

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

Conversation

dramforever
Copy link

Currently all the riscv*gc targets use the 'd' double-float ABI, and
soft-float otherwise. There's no need to detect the operating system
type. Fixes #795.

I'm not sure how correct this is. I've checked all the targets on version
1.69.0-nightly (ef934d9b6 2023-02-08) with this script:

rustc --print target-list |
  grep riscv |
  while IFS= read target; do
    rustc --target "$target" --print target-spec-json -Z unstable-options |
      jq --arg name "$target" -r '"\(.["llvm-abiname"]) \($name)"'
  done

Which gives:

ilp32d riscv32gc-unknown-linux-gnu
ilp32d riscv32gc-unknown-linux-musl
null riscv32i-unknown-none-elf
null riscv32im-unknown-none-elf
null riscv32imac-unknown-none-elf
null riscv32imac-unknown-xous-elf
null riscv32imc-esp-espidf
null riscv32imc-unknown-none-elf
lp64d riscv64gc-unknown-freebsd
lp64d riscv64gc-unknown-linux-gnu
lp64d riscv64gc-unknown-linux-musl
lp64d riscv64gc-unknown-none-elf
lp64d riscv64gc-unknown-openbsd
null riscv64imac-unknown-none-elf

Also I'm not sure whether to add tests or not. Please advise on this.

src/lib.rs Outdated

// If riscv32gc-* or riscv64gc-* then double-float ABI,
// otherwise use soft-float ABI.
let float_abi = if arch.contains("g") { "d" } else { "" };
Copy link
Member

Choose a reason for hiding this comment

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

This looks fragile to me, maybe we could use arch.ends_with("gc") or contains("gc") at least.

Copy link
Author

Choose a reason for hiding this comment

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

My reasoning is that g means imafd_zifencei_zicsr, so it implies d for double, which is required for double float ABI. Currently it seems (see table in top post) that rustc does use lp64d/ilp32d for all targets with double. Maybe I can change the code to detect d or g and/or change the comment to reflect this reasoning better?

I guess I'll also change it so that it handles e.g. riscv64imac_zsomething with a multi-letter extension tha happens to contain g. Not sure if something other than an ISA/-march string appears here though tbh.

Choose a reason for hiding this comment

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

In the RISC-V Unprivileged Specification RISC-V ISA can be decided from ISA string, which indicates that if it has D inside, the target must support double length floating points; and G means that it has IMAFD and Zifencei (F indicates Zicsr). Using C here would not cover this case correctly, as that C doesn't imply G or D.

It is possible to have an RVD implementation without RVC, for example disabling C instruction set on RV64GC platform (which is sometimes common on embedded SoC or developing new SoCs); detecting GC for D will not be correct in those cases.

Copy link
Author

Choose a reason for hiding this comment

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

I've added a bunch of comments explaining the logic, and also updated the detection logic to handle the possible future existence of multi-letter extensions, so it won't get confused by e.g. riscv64imac_zsomething containing a g.

@dramforever dramforever force-pushed the riscv-abi-arch-fix branch 6 times, most recently from 23303fc to 6a1aff2 Compare March 19, 2023 06:40
@dramforever
Copy link
Author

dramforever commented Apr 8, 2023

Additional justification for inferring the ABI: https://github.com/riscv-non-isa/riscv-toolchain-conventions#specifying-the-target-isa-with--march

A target -march which includes floating point instructions implies a hardfloat calling convention, but can be overridden using the -mabi flag

Since the Rust target implies -march, it makes sense to also infer -mabi from it.

I can add a comment linking to it, if the maintainers are okay with this logic in general.

@tea
Copy link

tea commented Jun 24, 2024

Can this be revived? As it stands, cc-rs is sort-of useless for embedded RISCV*GC targets (at least without target-specific workarounds similar to mentioned in #795).

@NobodyXu
Copy link
Collaborator

Can this be revived? As it stands, cc-rs is sort-of useless for embedded RISCV*GC targets (at least without target-specific workarounds similar to mentioned in #795).

I think it needs to resolve merge conflicts and rebase against main, then I can do another review pass on it

@dramforever
Copy link
Author

As original author of this PR I'm interested in reviving this

@dramforever
Copy link
Author

Aaaand, rebased, hope this works.

I haven't added the toolchain convention reference, because it has been removed and it's now only a de facto standard.

Honestly, we should just get the information from rustc itself once it's available...

src/lib.rs Outdated Show resolved Hide resolved
src/lib.rs Outdated Show resolved Hide resolved
src/lib.rs Show resolved Hide resolved
Instead of adding cases for all the operating systems, Use target
architecture directly as ISA string, replacing "riscv" with "rv", and
detect floating point ABI based on support for the D and F extensions.

Fixes rust-lang#795
}

cmd.args.push("-mcmodel=medany".into());
Copy link
Collaborator

Choose a reason for hiding this comment

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

This flag is pushed unconditionally now and it was conditional before.
Can you please add a comment explain why?

Copy link
Author

Choose a reason for hiding this comment

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

My understanding is that there was no reason to make this conditional in the first place and everything uses medium/medany (same thing). But I'll verify it later.

.split(['_', 'z', 's'])
.next()
// The arch string starts with 32 or 64
.expect("arch string cannot be empty");
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm bit confused on this, I checked the riscv targets rust supported and doesn't find any z, s or underscore in after the prefix "riscv" .

What is this trying to detect?
Is there any new target which contains z, s or underscore?

Copy link
Author

Choose a reason for hiding this comment

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

i don't know how else to address the fact that isa string parsing is "fragile" #796 (comment)

in case in the future we add extensions like zsomething the arch string could look like riscv64imaczsomething or riscv64imac_zsomething and we don't want to match on the g of zsomething

i don't think there's any easy way to say how the rust project will decide to name future riscv architectures, but if you're okay with this being "fragile" i can remove this part

Copy link

@Vollbrecht Vollbrecht Jun 26, 2024

Choose a reason for hiding this comment

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

just to chim in here, to give an idea about how for example the riscv-esp-elf-gcc sees target definitions, it looks something like the following:

*multilib_defaults:
march=rv32imafdc_zicsr_zifencei mabi=ilp32

*multilib_extra:


*multilib_matches:
march=rv32i_zicsr_zifencei march=rv32i_zicsr_zifencei;march=rv32iac_zicsr_zifencei march=rv32iac_zicsr_zifencei;march=rv32ic_zicsr_zifencei march=rv32ic_zicsr_zifencei;march=rv32im_zicsr_zifencei march=rv32im_zicsr_zifencei;march=rv32imc_zicsr_zifencei march=rv32imc_zicsr_zifencei;march=rv32imac_zicsr_zifencei march=rv32imac_zicsr_zifencei;march=rv32imafc_zicsr_zifencei march=rv32imafc_zicsr_zifencei;march=rv32imafdc_zicsr_zifencei march=rv32imafdc_zicsr_zifencei;march=rv32imafdc_zicsr_zifencei_xesppie march=rv32imafdc_zicsr_zifencei_xesppie;mabi=ilp32 mabi=ilp32;mabi=ilp32f mabi=ilp32f;fno-rtti fno-rtti;

this above is part of the output of ./riscv32-esp-elf-gcc -dumpspecs and presumably it looks similar on other gcc based variants.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Oh so it's non-standard rust target used in riscv-esp-elf-gcc?

If it is non-standard then it would indeed makes more sense to pass them as environment variables.

Choose a reason for hiding this comment

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

I am not sure what you mean by non-standard rust target, but our target descriptions are similar on how the other riscv targets on the rust site are being expressed.

But as dramforever mentions downstream gcc & clang is more complicated matter. Mapping to thouse means that there are possible different ISA versions that can be associated with. Rust target names in its current form lag that expressiveness if i see it correctly.

Copy link
Collaborator

@NobodyXu NobodyXu Jun 26, 2024

Choose a reason for hiding this comment

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

I am not sure what you mean by non-standard rust target, but our target descriptions are similar on how the other riscv targets on the rust site are being expressed.

I consider any target not in rustup or maintainer by the rust officially to be a non-standard one.

I heard of cargo plugins for esp and I think it sets the cflags and etc to work?

Would it makes sense here?

Copy link
Author

Choose a reason for hiding this comment

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

I guess it might make sense to summarize the situation:

  • Rust targets lists the instruction extensions after riscv{32,64}. f means hardware f32, d means hardware f64, and g is abbreviation of imafd
  • Currently there are only single letters there so we can detect it based on just looking for g/d or f
  • Previously a maintainer complained that it was fragile... but with no real way to predict future target names IMO it will never be not fragile
  • There's no way to get the target ABI used by rustc here, IIUC. If there was this whole detection would be obsolete for everyone.
  • The de facto standard for -mabi is to use the "maximum" supported hard float ABI

Copy link
Collaborator

Choose a reason for hiding this comment

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

There's no way to get the target ABI used by rustc here, IIUC. If there was this whole detection would be obsolete for everyone.

We do have a crate to extract some riscv target information from nightly rust.

I wonder if we could get this from it as well?

https://github.com/rust-lang/cc-rs/blob/main/dev-tools/gen-target-info/src/main.rs

@dramforever
Copy link
Author

@NobodyXu I just thought of another solution, can we just delete this section of the altogether and rely on the user to have configured the toolchain correctly or add the options for themselves?

@NobodyXu
Copy link
Collaborator

can we just delete this section of the altogether and rely on the user to have configured the toolchain correctly or add the options for themselves?

If it is optional flags that's there to improve performance, then maybe we can do it.

If it is required for it to compile, then I think we should keep it

@dramforever
Copy link
Author

dramforever commented Jun 26, 2024

If it is optional flags that's there to improve performance, then maybe we can do it.

If it is required for it to compile, then I think we should keep it

Unfortunately it is required for correctness :(

(Edit I mean, required for compilation to go through. The linker will catch (most) mistakes.)

@tea
Copy link

tea commented Sep 21, 2024

What is preventing this PR from being merged as is? Would it introduce any regressions?

It might not be perfect, it might be fragile for some obscure corner cases, but right now cc-rs can't even handle the bog-standard riscv64gc arch on non-standard OSes! In particular, it blocks introducing risc-v support on Redox, at least without forking this crate which is pretty much a non-starter.

Maybe the PR could be merged in (to move the needle in the right direction), and then you guys could continue perfecting it?

@NobodyXu
Copy link
Collaborator

What is preventing this PR from being merged as is? Would it introduce any regressions?

we were previously discussing about getting the flags from rustc codebase at build time.

It might not be perfect, it might be fragile for some obscure corner cases, but right now cc-rs can't even handle the bog-standard riscv64gc arch on non-standard OSes! In particular, it blocks introducing risc-v support on Redox, at least without forking this crate which is pretty much a non-starter.

I've posted this on zulip asking for a final review from someone understands riscv before merging in, I'm not very familiar with riscv so I don't know if it'd work/break something.

It would be great if it has CI running for riscv, with that I can merge in with confidence.

@dramforever
Copy link
Author

I'm still interested in working on this, although I would like some input on what kind of detection is expected here before trying to make up some logic without evidence to back it

@NobodyXu
Copy link
Collaborator

I'm still interested in working on this, although I would like some input on what kind of detection is expected here before trying to make up some logic without evidence to back it

cc-rs has an infrastructure to extract information from rustc +nightly -Zunstable-options --print all-target-specs-json.

https://github.com/rust-lang/cc-rs/blob/main/dev-tools/gen-target-info/src/main.rs

Basically you need to modify the main.rs to parse the riscv related target information, and write them to src/target_info.rs as an (sorted) array.

Once you've done that, you'd need to run cargo run on that crate, which will generate an updated src/target_info.rs for you and you can use it in lib.rs, without hardcoding anything.

You'd also need to stage and commit src/target_info.rs as well @dramforever

@tea
Copy link

tea commented Oct 21, 2024

I'd like to understand a state of things a little better. To give a concrete example, I'm targeting riscv64gc-unknown-redox. This is a non-standard target, using patched Rust, and it uses lp64d abi. cc-rs, as it stands now, instructs gcc to use soft-float abi so everything breaks. The PR, as it stands now, fixes that. Now, I saw several comments here what seems to be quite differing in nature.

we were previously discussing about getting the flags from rustc codebase at build time.

That should work for us I think, and for any non-standard target, including custom json target definitions.

But then the very last comment:

Basically you need to modify the main.rs to parse the riscv related target information, and write them to src/target_info.rs as an (sorted) array.

Seem to indicate an idea of hardcoding options inside cc-rs code, so that wouldn't exactly work for anything not known by stock Rust at the time of generation.

So, assuming we are left to fend for ourselves so to speak... More specifically

If it is non-standard then it would indeed makes more sense to pass them as environment variables.

How one would go about doing it? IIUC right now it is impossible to do because cc-rs would override it unconditionally. Let's say that is changed, so we'd provide CFLAGS="-mabi=lp64d" CXXFLAGS="-mabi=lp64d" (and march to boot) and make it work that way, right?

If so, it would probably be almost fine... Not exactly frictionless, as it would have to be put in bunch of places to make things like CI work; and also they have to be only present for riscv and not other targets. But that should be more manageable than the current approach of adding patches to all those Cargo.toml files.

@NobodyXu
Copy link
Collaborator

Basically you need to modify the main.rs to parse the riscv related target information, and write them to src/target_info.rs as an (sorted) array.

Seem to indicate an idea of hardcoding options inside cc-rs code, so that wouldn't exactly work for anything not known by stock Rust at the time of generation.

What I was describing is our build time rustc target info extraction.

In the gen-target-info workspace, we get the flags from rustc, we have a workspace which run rustc and exteact target info to files that are included in cc.

We run this in CI once a week.

however, there's a better option now.

@madsmtm is working on #1225 which would enable cc-rs to use information from cargo environment if present, thus it should fix the custom target use cases.

@madsmtm madsmtm mentioned this pull request Nov 4, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

ABI mismatch for riscv64gc-unknown-none-elf
6 participants