target specs: stricter checks for LLVM ABI values, and correlate that with cfg(target_abi)#153769
target specs: stricter checks for LLVM ABI values, and correlate that with cfg(target_abi)#153769RalfJung wants to merge 3 commits intorust-lang:mainfrom
Conversation
|
r? @JohnTitor rustbot has assigned @JohnTitor. Use Why was this reviewer chosen?The reviewer was selected based on:
|
| llvm_floatabi: if arch.target_arch() == crate::spec::Arch::Arm { | ||
| Some(FloatAbi::Hard) | ||
| } else { | ||
| // `llvm_floatabi` makes no sense on x86 and aarch64. | ||
| None | ||
| }, |
There was a problem hiding this comment.
@nikic just to double check -- it is correct that x86 and aarch64 ignore LLVM's FloatAbi field, right? A quick grep seems to confirm this but I don't want to accidentally break the ABI here.^^
ae18b24 to
7fce460
Compare
| "AIX targets always use the AIX ABI and `llvm_abiname` should be left empty", | ||
| check_matches!( | ||
| (&*self.llvm_abiname, &self.cfg_abi), | ||
| ("", Abi::VecDefault | Abi::VecExtAbi), |
There was a problem hiding this comment.
Interestingly we don't seem to have any target that uses VecDefault. I just inferred from other places in the compiler that this is an AIX ABI.
@Gelbpunkt @daltenty any idea why this even exists as a possible value for cfg(target_abi)?
There was a problem hiding this comment.
Yes, VecDefault is an AIX ABI. We use VecExtAbi which corresponds to the AIX Extended Altivec ABI. See https://www.ibm.com/docs/en/aix/7.1.0?topic=processor-legacy-abi-compatibility-interoperability for some more information. I think we can safely get rid of VecDefault, the baseline for the AIX targets supported includes AltiVec so I see no reason to ever use the legacy ABI (and we currently don't).
There was a problem hiding this comment.
Okay, I'll remove it.
How would the ABI even be controlled, i.e. how would LLVM be informed about which ABI to use?
There was a problem hiding this comment.
Setting the LLVM ABI to vec-extabi will use that one, leaving it empty / at the default should be the legacy ABI as far as I can tell. LLVM's -vec-extabi flag is not the default (and neither is Clang's -mabi=vec-extabi).
There was a problem hiding this comment.
Not even IBM uses it for the Rustc on AIX:
bash-5.3$ rustc --print target-list | grep aix
powerpc64-ibm-aix
bash-5.3$ RUSTC_BOOTSTRAP=1 rustc -Z unstable-options --target powerpc64-ibm-aix --print target-spec-json
{
"abi": "vec-extabi",
"arch": "powerpc64",
"archive-format": "aix_big",
"binary-format": "xcoff",
"code-model": "large",
"cpu": "pwr7",
"crt-objects-fallback": "false",
"crt-static-respected": true,
"data-layout": "E-m:a-Fi64-i64:64-i128:128-n32:64-S128-v256:256:256-v512:512:512",
"default-dwarf-version": 3,
"dll-suffix": ".a",
"dynamic-linking": true,
"eh-frame-header": false,
"has-thread-local": true,
"is-like-aix": true,
"linker": "ld",
"linker-flavor": "unix",
"linker-is-gnu": false,
"llvm-target": "powerpc64-ibm-aix",
"max-atomic-width": 64,
"metadata": {
"description": "64-bit AIX (7.2 and newer)",
"host_tools": false,
"std": null,
"tier": 3
},
"os": "aix",
"pre-link-args": {
"unix": [
"-b64",
"-bpT:0x100000000",
"-bpD:0x110000000",
"-bcdtors:all:0:s"
]
},
"pre-link-objects": {
"dynamic-nopic-exe": [
"/usr/lib/crt0_64.o",
"/usr/lib/crti_64.o"
],
"dynamic-pic-exe": [
"/usr/lib/crt0_64.o",
"/usr/lib/crti_64.o"
]
},
"target-endian": "big",
"target-family": [
"unix"
],
"target-pointer-width": 64,
"vendor": "ibm"
}
bash-5.3$ rustc --version
rustc 1.92.0 (afb17749a 2026-02-05) (IBM Open SDK for Rust on AIX 1.92.0.0 (5900-BND, 5765-J24))
There was a problem hiding this comment.
Wait, I'm confused now. We leave the LLVM ABI name empty, so we should actually be using the legacy ABI for codegen?
There was a problem hiding this comment.
The only valid LLVM ABI names for PowerPC are "elfv1" and "elfv2", as far as I can tell by grepping the LLVM source code.
There was a problem hiding this comment.
Okay, looking a bit closer, we'd have to set EnableAIXExtendedAltivecABI to 1 in TargetOpts (https://github.com/llvm/llvm-project/blob/main/llvm/include/llvm/Target/TargetOptions.h#L179). We should probably do that if we're targeting AIX somewhere here: https://github.com/rust-lang/rust/blob/main/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp#L315
There was a problem hiding this comment.
Oh no, what a mess -- an architecture-specific field in the global TargetOptions struct?!? No idea how we'd best wire that up.
Could you file an issue? That change is definitely outside the scope of this PR.
| (Some(RustcAbi::Softfloat), Abi::SoftFloat) | ||
| | ( | ||
| None, | ||
| Abi::Ilp32 |
There was a problem hiding this comment.
I have no idea what the "ilp32" value for aarch64 means. It sure sounds like it talks about the actual calling convention, but it does not seem to correlate with anything that would impact the actual calling convention...
There was a problem hiding this comment.
It seems "ilp32" was introduced in c3fbafd. @joshtriplett maybe you could help us out here. :)
The value is only used by two targets, aarch64_be_unknown_linux_gnu_ilp32 and aarch64_unknown_linux_gnu_ilp32. Are we relying on LLVM implicitly configuring the ABI based on llvm_target?
There was a problem hiding this comment.
This is about pointer and c_long widths and the same as target_abi="x32" in x86_64 (x86_64-unknown-linux-gnux32).
AFAIK it is common to use target_pointer_width to detect this, and the target_abi value about this is neither reliable nor convenient (e.g., non-Linux AArch64 ILP32 targets (arm64_32-apple-watchos) doesn't have this cfg, and value is different between platforms (x32 vs ilp32. mips64 n32, rv64ilp32, and ppc64 lv2 may also have different values)), so I do not feel it necessary to set target_abi for this purpose. However, @Amanieu who added AArch64 Linux ILP32 targets in #81455 or @joshtriplett who added these values in c3fbafd may have a different opinion.
There was a problem hiding this comment.
This is about pointer and c_long widths and the same as target_abi="x32" in x86_64
Ah! That makes sense, thanks.
I don't plan to change any of the existing target_abi values in this PR, I just want to map out how we use that value and ensure we are and remain consistent about it.
7fce460 to
a16a988
Compare
| check!(self.rustc_abi.is_none(), "`rustc_abi` is unused on MIPS"); | ||
| check_matches!( | ||
| (&*self.llvm_abiname, &self.cfg_abi), | ||
| ("o32", Abi::Unspecified | Abi::Other(_)), |
There was a problem hiding this comment.
It is my understanding based on reading the LLVM sources that o32 is the default value for the ABI for MIPS32, so I forced all targets to set this explicitly and updated our built-in targets accordingly. Cc @LukasWoodtli
There was a problem hiding this comment.
Yes, the n32 ABI is only intended for CPUs running in 64-bit mode, o32 is the native 32-bit CPU ABI
d0775f2 to
a03f108
Compare
a03f108 to
5dd6bdc
Compare
| && sess.target.os == Os::Windows | ||
| && sess.target.env == Env::Gnu | ||
| && sess.target.abi != Abi::Llvm); | ||
| && sess.target.cfg_abi != Abi::Llvm); |
There was a problem hiding this comment.
If I follow correctly your PR description, this and all the other conditions on cfg_abi should eventually disappear, as they do not represent the actual abi?
There was a problem hiding this comment.
It does not represent the calling convention. There are more things to an ABI. So, this check here might be entirely reasonable -- I am not sure. But for something like "are arguments passed in this or that register", cfg_abi shouldn't be used.
The first commit renames
target.abitotarget.cfg_abito make it less likely that someone will use it to determine things about the actual ccABI, i.e. the calling convention used on the target.target.abidoes not control that calling convention, it just sometimes informs the user about that calling convention (and also about other aspects of the ABI).The second commit tightens the checks for
llvm_abiname,llvm_floatabiandrustc_abiin our target specs. Those are the fields that actually control the ccABI. With this commit, we now have an allowlist of value for these fields for all architectures (we previously only had that for some architectures). We also check thatcfg(target_abi)suitably correlates with the actual ccABI. I based this check on our in-tree targets. For all ccABIs where we had a bunch of "random" values that don't directly correlate to the ccABI (like "uwp"), I also allowedcfg(target_abi)to remain empty, and whenever it is allowed to be empty I also allowed arbitrary other values for JSON targets. However, there's still a risk that JSON targets will fail this new check -- the idea is that we'll then get bugreports and can adjust the check as needed.I had to adjust the target specs for non-ARM32 Apple targets as those were all setting
llvm_floatabi, which to my knowledge makes no sense -- LLVM only actually does anything with that field on ARM32. I also adjusted the target specs for MIPS32 targets: one of them was setting llvm_abiname, and then it seems safer and more consistent to set that for all targets, so I set it to "o32" everywhere which seems to be the default.Cc @workingjubilee