Skip to content

ensure that aarch64 hardfloat targets do not disable neon or fp-armv8 #133396

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

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
309 changes: 306 additions & 3 deletions compiler/rustc_target/src/spec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@
//! the target's settings, though `target-feature` and `link-args` will *add*
//! to the list specified by the target, rather than replace.

// ignore-tidy-filelength

use std::assert_matches::assert_matches;
use std::borrow::Cow;
use std::collections::BTreeMap;
use std::hash::{Hash, Hasher};
Expand Down Expand Up @@ -1605,13 +1608,11 @@ macro_rules! supported_targets {

#[cfg(test)]
mod tests {
mod tests_impl;

// Cannot put this into a separate file without duplication, make an exception.
$(
#[test] // `#[test]`
fn $module() {
tests_impl::test_target(crate::spec::targets::$module::target());
crate::spec::targets::$module::target().test_target()
}
)+
}
Expand Down Expand Up @@ -2594,6 +2595,18 @@ impl TargetOptions {
.collect();
}
}

pub(crate) fn has_neg_feature(&self, search_feature: &str) -> bool {
self.features.split(',').any(|f| {
if let Some(f) = f.strip_prefix('-')
&& f == search_feature
{
true
} else {
false
}
})
}
}

impl Default for TargetOptions {
Expand Down Expand Up @@ -2849,6 +2862,295 @@ impl Target {
self.max_atomic_width.unwrap_or_else(|| self.pointer_width.into())
}

fn check_consistency(&self) -> Result<(), String> {
macro_rules! check {
($b:expr, $($msg:tt)*) => {
if !$b {
return Err(format!($($msg)*));
}
}
}
macro_rules! check_eq {
($left:expr, $right:expr, $($msg:tt)*) => {
if ($left) != ($right) {
return Err(format!($($msg)*));
}
}
}
macro_rules! check_ne {
($left:expr, $right:expr, $($msg:tt)*) => {
if ($left) == ($right) {
return Err(format!($($msg)*));
}
}
}
macro_rules! check_matches {
($left:expr, $right:pat, $($msg:tt)*) => {
if !matches!($left, $right) {
return Err(format!($($msg)*));
}
}
}

check_eq!(
self.is_like_osx,
self.vendor == "apple",
"`is_like_osx` must be set if and only if `vendor` is `apple`"
);
check_eq!(
self.is_like_solaris,
self.os == "solaris" || self.os == "illumos",
"`is_like_solaris` must be set if and only if `os` is `solaris` or `illumos`"
);
check_eq!(
self.is_like_windows,
self.os == "windows" || self.os == "uefi",
"`is_like_windows` must be set if and only if `os` is `windows` or `uefi`"
);
check_eq!(
self.is_like_wasm,
self.arch == "wasm32" || self.arch == "wasm64",
"`is_like_wasm` must be set if and only if `arch` is `wasm32` or `wasm64`"
);
if self.is_like_msvc {
check!(self.is_like_windows, "if `is_like_msvc` is set, `is_like_windows` must be set");
}

// Check that default linker flavor is compatible with some other key properties.
check_eq!(
self.is_like_osx,
matches!(self.linker_flavor, LinkerFlavor::Darwin(..)),
"`linker_flavor` must be `darwin` if and only if `is_like_osx` is set"
);
check_eq!(
self.is_like_msvc,
matches!(self.linker_flavor, LinkerFlavor::Msvc(..)),
"`linker_flavor` must be `mscv` if and only if `is_like_msvc` is set"
);
check_eq!(
self.is_like_wasm && self.os != "emscripten",
matches!(self.linker_flavor, LinkerFlavor::WasmLld(..)),
"`linker_flavor` must be `wasm-lld` if and only if `is_like_wasm` is set and the `os` is not `emscripten`",
);
check_eq!(
self.os == "emscripten",
matches!(self.linker_flavor, LinkerFlavor::EmCc),
"`linker_flavor` must be `em-cc` if and only if `os` is `emscripten`"
);
check_eq!(
self.arch == "bpf",
matches!(self.linker_flavor, LinkerFlavor::Bpf),
"`linker_flavor` must be `bpf` if and only if `arch` is `bpf`"
);
check_eq!(
self.arch == "nvptx64",
matches!(self.linker_flavor, LinkerFlavor::Ptx),
"`linker_flavor` must be `ptc` if and only if `arch` is `nvptx64`"
);

for args in [
&self.pre_link_args,
&self.late_link_args,
&self.late_link_args_dynamic,
&self.late_link_args_static,
&self.post_link_args,
] {
for (&flavor, flavor_args) in args {
check!(!flavor_args.is_empty(), "linker flavor args must not be empty");
// Check that flavors mentioned in link args are compatible with the default flavor.
match self.linker_flavor {
LinkerFlavor::Gnu(..) => {
check_matches!(
flavor,
LinkerFlavor::Gnu(..),
"mixing GNU and non-GNU linker flavors"
);
}
LinkerFlavor::Darwin(..) => {
check_matches!(
flavor,
LinkerFlavor::Darwin(..),
"mixing Darwin and non-Darwin linker flavors"
)
}
LinkerFlavor::WasmLld(..) => {
check_matches!(
flavor,
LinkerFlavor::WasmLld(..),
"mixing wasm and non-wasm linker flavors"
)
}
LinkerFlavor::Unix(..) => {
check_matches!(
flavor,
LinkerFlavor::Unix(..),
"mixing unix and non-unix linker flavors"
);
}
LinkerFlavor::Msvc(..) => {
check_matches!(
flavor,
LinkerFlavor::Msvc(..),
"mixing MSVC and non-MSVC linker flavors"
);
}
LinkerFlavor::EmCc
| LinkerFlavor::Bpf
| LinkerFlavor::Ptx
| LinkerFlavor::Llbc => {
check_eq!(flavor, self.linker_flavor, "mixing different linker flavors")
}
}

// Check that link args for cc and non-cc versions of flavors are consistent.
let check_noncc = |noncc_flavor| -> Result<(), String> {
if let Some(noncc_args) = args.get(&noncc_flavor) {
for arg in flavor_args {
if let Some(suffix) = arg.strip_prefix("-Wl,") {
check!(
noncc_args.iter().any(|a| a == suffix),
" link args for cc and non-cc versions of flavors are not consistent"
);
}
}
}
Ok(())
};

match self.linker_flavor {
LinkerFlavor::Gnu(Cc::Yes, lld) => check_noncc(LinkerFlavor::Gnu(Cc::No, lld))?,
LinkerFlavor::WasmLld(Cc::Yes) => check_noncc(LinkerFlavor::WasmLld(Cc::No))?,
LinkerFlavor::Unix(Cc::Yes) => check_noncc(LinkerFlavor::Unix(Cc::No))?,
_ => {}
}
}

// Check that link args for lld and non-lld versions of flavors are consistent.
for cc in [Cc::No, Cc::Yes] {
check_eq!(
args.get(&LinkerFlavor::Gnu(cc, Lld::No)),
args.get(&LinkerFlavor::Gnu(cc, Lld::Yes)),
"link args for lld and non-lld versions of flavors are not consistent",
);
check_eq!(
args.get(&LinkerFlavor::Darwin(cc, Lld::No)),
args.get(&LinkerFlavor::Darwin(cc, Lld::Yes)),
"link args for lld and non-lld versions of flavors are not consistent",
);
}
check_eq!(
args.get(&LinkerFlavor::Msvc(Lld::No)),
args.get(&LinkerFlavor::Msvc(Lld::Yes)),
"link args for lld and non-lld versions of flavors are not consistent",
);
}

if self.link_self_contained.is_disabled() {
check!(
self.pre_link_objects_self_contained.is_empty()
&& self.post_link_objects_self_contained.is_empty(),
"if `link_self_contained` is disabled, then `pre_link_objects_self_contained` and `post_link_objects_self_contained` must be empty",
);
}

// If your target really needs to deviate from the rules below,
// except it and document the reasons.
// Keep the default "unknown" vendor instead.
check_ne!(self.vendor, "", "`vendor` cannot be empty");
check_ne!(self.os, "", "`os` cannot be empty");
if !self.can_use_os_unknown() {
// Keep the default "none" for bare metal targets instead.
check_ne!(self.os, "unknown", "`unknown` os can only be used on particular targets");
}

// Check dynamic linking stuff
// BPF: when targeting user space vms (like rbpf), those can load dynamic libraries.
// hexagon: when targeting QuRT, that OS can load dynamic libraries.
// wasm{32,64}: dynamic linking is inherent in the definition of the VM.
if self.os == "none"
&& (self.arch != "bpf"
&& self.arch != "hexagon"
&& self.arch != "wasm32"
&& self.arch != "wasm64")
{
check!(
!self.dynamic_linking,
"dynamic linking is not supported on this OS/architecture"
);
}
if self.only_cdylib
|| self.crt_static_allows_dylibs
|| !self.late_link_args_dynamic.is_empty()
{
check!(
self.dynamic_linking,
"dynamic linking must be allowed when `only_cdylib` or `crt_static_allows_dylibs` or `late_link_args_dynamic` are set"
);
}
// Apparently PIC was slow on wasm at some point, see comments in wasm_base.rs
if self.dynamic_linking && !(self.is_like_wasm && self.os != "emscripten") {
assert_eq!(self.relocation_model, RelocModel::Pic);
}
if self.position_independent_executables {
assert_eq!(self.relocation_model, RelocModel::Pic);
}
// The UEFI targets do not support dynamic linking but still require PIC (#101377).
if self.relocation_model == RelocModel::Pic && self.os != "uefi" {
assert!(self.dynamic_linking || self.position_independent_executables);
}
if self.static_position_independent_executables {
assert!(self.position_independent_executables);
}
if self.position_independent_executables {
assert!(self.executables);
}

// Check crt static stuff
if self.crt_static_default || self.crt_static_allows_dylibs {
assert!(self.crt_static_respected);
}

// Check that RISC-V targets always specify which ABI they use.
match &*self.arch {
"riscv32" => {
assert_matches!(&*self.llvm_abiname, "ilp32" | "ilp32f" | "ilp32d" | "ilp32e")
}
"riscv64" => {
// Note that the `lp64e` is still unstable as it's not (yet) part of the ELF psABI.
assert_matches!(&*self.llvm_abiname, "lp64" | "lp64f" | "lp64d" | "lp64q" | "lp64e")
}
_ => {}
}

// Check that aarch64 hardfloat targets do not disable neon or fp-armv8 (which are
// on-by-default).
if self.arch == "aarch64" && self.abi != "softfloat" {
check!(
!self.has_neg_feature("neon") && !self.has_neg_feature("fp-armv8"),
"aarch64 hardfloat targets must not disable the `neon` or `fp-armv8` target features"
);
}

Ok(())
}

/// Test target self-consistency and JSON encoding/decoding roundtrip.
#[cfg(test)]
fn test_target(mut self) {
let recycled_target = Target::from_json(self.to_json()).map(|(j, _)| j);
self.update_to_cli();
self.check_consistency().unwrap();
assert_eq!(recycled_target, Ok(self));
}

// Add your target to the whitelist if it has `std` library
// and you certainly want "unknown" for the OS name.
fn can_use_os_unknown(&self) -> bool {
self.llvm_target == "wasm32-unknown-unknown"
|| self.llvm_target == "wasm64-unknown-unknown"
|| (self.env == "sgx" && self.vendor == "fortanix")
}

/// Loads a target descriptor from a JSON object.
pub fn from_json(obj: Json) -> Result<(Target, TargetWarnings), String> {
// While ugly, this code must remain this way to retain
Expand Down Expand Up @@ -3456,6 +3758,7 @@ impl Target {
key!(supports_xray, bool);

base.update_from_cli();
base.check_consistency()?;

// Each field should have been read using `Json::remove` so any keys remaining are unused.
let remaining_keys = obj.keys();
Expand Down
Loading
Loading