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

Fix some issues with folded AArch64 features #107294

Merged
merged 4 commits into from
May 23, 2023
Merged
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
1 change: 1 addition & 0 deletions compiler/rustc_codegen_llvm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#![feature(iter_intersperse)]
#![feature(let_chains)]
#![feature(never_type)]
#![feature(impl_trait_in_assoc_type)]
#![recursion_limit = "256"]
#![allow(rustc::potential_query_instability)]
#![deny(rustc::untranslatable_diagnostic)]
Expand Down
166 changes: 128 additions & 38 deletions compiler/rustc_codegen_llvm/src/llvm_util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ use rustc_session::config::PrintRequest;
use rustc_session::Session;
use rustc_span::symbol::Symbol;
use rustc_target::spec::{MergeFunctions, PanicStrategy};
use smallvec::{smallvec, SmallVec};
use std::ffi::{CStr, CString};

use std::path::Path;
Expand Down Expand Up @@ -132,6 +131,60 @@ pub fn time_trace_profiler_finish(file_name: &Path) {
}
}

pub enum TargetFeatureFoldStrength<'a> {
// The feature is only tied when enabling the feature, disabling
// this feature shouldn't disable the tied feature.
EnableOnly(&'a str),
// The feature is tied for both enabling and disabling this feature.
Both(&'a str),
}

impl<'a> TargetFeatureFoldStrength<'a> {
fn as_str(&self) -> &'a str {
match self {
TargetFeatureFoldStrength::EnableOnly(feat) => feat,
TargetFeatureFoldStrength::Both(feat) => feat,
}
}
}

pub struct LLVMFeature<'a> {
pub llvm_feature_name: &'a str,
pub dependency: Option<TargetFeatureFoldStrength<'a>>,
}

impl<'a> LLVMFeature<'a> {
pub fn new(llvm_feature_name: &'a str) -> Self {
Self { llvm_feature_name, dependency: None }
}

pub fn with_dependency(
llvm_feature_name: &'a str,
dependency: TargetFeatureFoldStrength<'a>,
) -> Self {
Self { llvm_feature_name, dependency: Some(dependency) }
}

pub fn contains(&self, feat: &str) -> bool {
self.iter().any(|dep| dep == feat)
}

pub fn iter(&'a self) -> impl Iterator<Item = &'a str> {
let dependencies = self.dependency.iter().map(|feat| feat.as_str());
std::iter::once(self.llvm_feature_name).chain(dependencies)
}
}

impl<'a> IntoIterator for LLVMFeature<'a> {
type Item = &'a str;
type IntoIter = impl Iterator<Item = &'a str>;

fn into_iter(self) -> Self::IntoIter {
let dependencies = self.dependency.into_iter().map(|feat| feat.as_str());
std::iter::once(self.llvm_feature_name).chain(dependencies)
}
}

// WARNING: the features after applying `to_llvm_features` must be known
// to LLVM or the feature detection code will walk past the end of the feature
// array, leading to crashes.
Expand All @@ -147,36 +200,65 @@ pub fn time_trace_profiler_finish(file_name: &Path) {
// Though note that Rust can also be build with an external precompiled version of LLVM
// which might lead to failures if the oldest tested / supported LLVM version
// doesn't yet support the relevant intrinsics
pub fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> SmallVec<[&'a str; 2]> {
pub fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> LLVMFeature<'a> {
let arch = if sess.target.arch == "x86_64" { "x86" } else { &*sess.target.arch };
match (arch, s) {
("x86", "sse4.2") => smallvec!["sse4.2", "crc32"],
("x86", "pclmulqdq") => smallvec!["pclmul"],
("x86", "rdrand") => smallvec!["rdrnd"],
("x86", "bmi1") => smallvec!["bmi"],
("x86", "cmpxchg16b") => smallvec!["cx16"],
("aarch64", "rcpc2") => smallvec!["rcpc-immo"],
("aarch64", "dpb") => smallvec!["ccpp"],
("aarch64", "dpb2") => smallvec!["ccdp"],
("aarch64", "frintts") => smallvec!["fptoint"],
("aarch64", "fcma") => smallvec!["complxnum"],
("aarch64", "pmuv3") => smallvec!["perfmon"],
("aarch64", "paca") => smallvec!["pauth"],
("aarch64", "pacg") => smallvec!["pauth"],
// Rust ties fp and neon together. In LLVM neon implicitly enables fp,
// but we manually enable neon when a feature only implicitly enables fp
("aarch64", "f32mm") => smallvec!["f32mm", "neon"],
("aarch64", "f64mm") => smallvec!["f64mm", "neon"],
("aarch64", "fhm") => smallvec!["fp16fml", "neon"],
("aarch64", "fp16") => smallvec!["fullfp16", "neon"],
("aarch64", "jsconv") => smallvec!["jsconv", "neon"],
("aarch64", "sve") => smallvec!["sve", "neon"],
("aarch64", "sve2") => smallvec!["sve2", "neon"],
("aarch64", "sve2-aes") => smallvec!["sve2-aes", "neon"],
("aarch64", "sve2-sm4") => smallvec!["sve2-sm4", "neon"],
("aarch64", "sve2-sha3") => smallvec!["sve2-sha3", "neon"],
("aarch64", "sve2-bitperm") => smallvec!["sve2-bitperm", "neon"],
(_, s) => smallvec![s],
("x86", "sse4.2") => {
LLVMFeature::with_dependency("sse4.2", TargetFeatureFoldStrength::EnableOnly("crc32"))
}
("x86", "pclmulqdq") => LLVMFeature::new("pclmul"),
("x86", "rdrand") => LLVMFeature::new("rdrnd"),
("x86", "bmi1") => LLVMFeature::new("bmi"),
("x86", "cmpxchg16b") => LLVMFeature::new("cx16"),
("aarch64", "rcpc2") => LLVMFeature::new("rcpc-immo"),
("aarch64", "dpb") => LLVMFeature::new("ccpp"),
("aarch64", "dpb2") => LLVMFeature::new("ccdp"),
("aarch64", "frintts") => LLVMFeature::new("fptoint"),
("aarch64", "fcma") => LLVMFeature::new("complxnum"),
("aarch64", "pmuv3") => LLVMFeature::new("perfmon"),
("aarch64", "paca") => LLVMFeature::new("pauth"),
("aarch64", "pacg") => LLVMFeature::new("pauth"),
// Rust ties fp and neon together.
("aarch64", "neon") => {
LLVMFeature::with_dependency("neon", TargetFeatureFoldStrength::Both("fp-armv8"))
}
// In LLVM neon implicitly enables fp, but we manually enable
// neon when a feature only implicitly enables fp
("aarch64", "f32mm") => {
LLVMFeature::with_dependency("f32mm", TargetFeatureFoldStrength::EnableOnly("neon"))
}
("aarch64", "f64mm") => {
LLVMFeature::with_dependency("f64mm", TargetFeatureFoldStrength::EnableOnly("neon"))
}
("aarch64", "fhm") => {
LLVMFeature::with_dependency("fp16fml", TargetFeatureFoldStrength::EnableOnly("neon"))
}
("aarch64", "fp16") => {
LLVMFeature::with_dependency("fullfp16", TargetFeatureFoldStrength::EnableOnly("neon"))
}
("aarch64", "jsconv") => {
LLVMFeature::with_dependency("jsconv", TargetFeatureFoldStrength::EnableOnly("neon"))
}
("aarch64", "sve") => {
LLVMFeature::with_dependency("sve", TargetFeatureFoldStrength::EnableOnly("neon"))
}
("aarch64", "sve2") => {
LLVMFeature::with_dependency("sve2", TargetFeatureFoldStrength::EnableOnly("neon"))
}
("aarch64", "sve2-aes") => {
LLVMFeature::with_dependency("sve2-aes", TargetFeatureFoldStrength::EnableOnly("neon"))
}
("aarch64", "sve2-sm4") => {
LLVMFeature::with_dependency("sve2-sm4", TargetFeatureFoldStrength::EnableOnly("neon"))
}
("aarch64", "sve2-sha3") => {
LLVMFeature::with_dependency("sve2-sha3", TargetFeatureFoldStrength::EnableOnly("neon"))
}
("aarch64", "sve2-bitperm") => LLVMFeature::with_dependency(
"sve2-bitperm",
TargetFeatureFoldStrength::EnableOnly("neon"),
),
(_, s) => LLVMFeature::new(s),
}
}

Expand Down Expand Up @@ -274,18 +356,17 @@ fn print_target_features(sess: &Session, tm: &llvm::TargetMachine) {
let mut rustc_target_features = supported_target_features(sess)
.iter()
.map(|(feature, _gate)| {
let desc = if let Some(llvm_feature) = to_llvm_features(sess, *feature).first() {
// LLVM asserts that these are sorted. LLVM and Rust both use byte comparison for these strings.
// LLVM asserts that these are sorted. LLVM and Rust both use byte comparison for these strings.
let llvm_feature = to_llvm_features(sess, *feature).llvm_feature_name;
let desc =
match llvm_target_features.binary_search_by_key(&llvm_feature, |(f, _d)| f).ok() {
Some(index) => {
known_llvm_target_features.insert(llvm_feature);
llvm_target_features[index].1
}
None => "",
}
} else {
""
};
};

(*feature, desc)
})
.collect::<Vec<_>>();
Expand Down Expand Up @@ -469,10 +550,19 @@ pub(crate) fn global_llvm_features(sess: &Session, diagnostics: bool) -> Vec<Str
// passing requests down to LLVM. This means that all in-language
// features also work on the command line instead of having two
// different names when the LLVM name and the Rust name differ.
let llvm_feature = to_llvm_features(sess, feature);

Some(
to_llvm_features(sess, feature)
.into_iter()
.map(move |f| format!("{}{}", enable_disable, f)),
std::iter::once(format!("{}{}", enable_disable, llvm_feature.llvm_feature_name))
.chain(llvm_feature.dependency.into_iter().filter_map(move |feat| {
match (enable_disable, feat) {
('-' | '+', TargetFeatureFoldStrength::Both(f))
| ('+', TargetFeatureFoldStrength::EnableOnly(f)) => {
Some(format!("{}{}", enable_disable, f))
}
_ => None,
}
})),
)
})
.flatten();
Expand Down
29 changes: 29 additions & 0 deletions tests/codegen/tied-features-strength.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// ignore-tidy-linelength
// revisions: ENABLE_SVE DISABLE_SVE DISABLE_NEON ENABLE_NEON
// compile-flags: --crate-type=rlib --target=aarch64-unknown-linux-gnu
// needs-llvm-components: aarch64

// The "+v8a" feature is matched as optional as it isn't added when we
// are targeting older LLVM versions. Once the min supported version
// is LLVM-14 we can remove the optional regex matching for this feature.

// [ENABLE_SVE] compile-flags: -C target-feature=+sve
// ENABLE_SVE: attributes #0 = { {{.*}} "target-features"="{{((\+outline-atomics,?)|(\+v8a,?)?|(\+sve,?)|(\+neon,?))*}}" }

// [DISABLE_SVE] compile-flags: -C target-feature=-sve
// DISABLE_SVE: attributes #0 = { {{.*}} "target-features"="{{((\+outline-atomics,?)|(\+v8a,?)?|(-sve,?)|(\+neon,?))*}}" }

// [DISABLE_NEON] compile-flags: -C target-feature=-neon
// DISABLE_NEON: attributes #0 = { {{.*}} "target-features"="{{((\+outline-atomics,?)|(\+v8a,?)?|(-fp-armv8,?)|(-neon,?))*}}" }

// [ENABLE_NEON] compile-flags: -C target-feature=+neon
// ENABLE_NEON: attributes #0 = { {{.*}} "target-features"="{{((\+outline-atomics,?)|(\+v8a,?)?|(\+fp-armv8,?)|(\+neon,?))*}}" }


#![feature(no_core, lang_items)]
#![no_core]

#[lang = "sized"]
trait Sized {}

pub fn test() {}