1
Fork 0

rustc_codegen_llvm: make sse4.2 imply crc32 for LLVM 14

This fixes compiling things like the `snap` crate after
https://reviews.llvm.org/D105462. I added a test that verifies the
additional attribute gets specified, and confirmed that I can build
cargo with both LLVM 13 and 14 with this change applied.
This commit is contained in:
Augie Fackler 2021-09-15 10:18:10 -04:00
parent db1fb85cff
commit 4185b76dc3
4 changed files with 83 additions and 34 deletions

View file

@ -305,9 +305,12 @@ pub fn from_fn_attrs(cx: &CodegenCx<'ll, 'tcx>, llfn: &'ll Value, instance: ty::
let mut function_features = codegen_fn_attrs let mut function_features = codegen_fn_attrs
.target_features .target_features
.iter() .iter()
.map(|f| { .flat_map(|f| {
let feature = &f.as_str(); let feature = &f.as_str();
format!("+{}", llvm_util::to_llvm_feature(cx.tcx.sess, feature)) llvm_util::to_llvm_feature(cx.tcx.sess, feature)
.into_iter()
.map(|f| format!("+{}", f))
.collect::<Vec<String>>()
}) })
.chain(codegen_fn_attrs.instruction_set.iter().map(|x| match x { .chain(codegen_fn_attrs.instruction_set.iter().map(|x| match x {
InstructionSetAttr::ArmA32 => "-thumb-mode".to_string(), InstructionSetAttr::ArmA32 => "-thumb-mode".to_string(),

View file

@ -166,25 +166,32 @@ pub fn time_trace_profiler_finish(file_name: &str) {
// Though note that Rust can also be build with an external precompiled version of LLVM // 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 // which might lead to failures if the oldest tested / supported LLVM version
// doesn't yet support the relevant intrinsics // doesn't yet support the relevant intrinsics
pub fn to_llvm_feature<'a>(sess: &Session, s: &'a str) -> &'a str { pub fn to_llvm_feature<'a>(sess: &Session, s: &'a str) -> Vec<&'a str> {
let arch = if sess.target.arch == "x86_64" { "x86" } else { &*sess.target.arch }; let arch = if sess.target.arch == "x86_64" { "x86" } else { &*sess.target.arch };
match (arch, s) { match (arch, s) {
("x86", "pclmulqdq") => "pclmul", ("x86", "sse4.2") => {
("x86", "rdrand") => "rdrnd", if get_version() >= (14, 0, 0) {
("x86", "bmi1") => "bmi", vec!["sse4.2", "crc32"]
("x86", "cmpxchg16b") => "cx16", } else {
("x86", "avx512vaes") => "vaes", vec!["sse4.2"]
("x86", "avx512gfni") => "gfni", }
("x86", "avx512vpclmulqdq") => "vpclmulqdq", }
("aarch64", "fp") => "fp-armv8", ("x86", "pclmulqdq") => vec!["pclmul"],
("aarch64", "fp16") => "fullfp16", ("x86", "rdrand") => vec!["rdrnd"],
("aarch64", "fhm") => "fp16fml", ("x86", "bmi1") => vec!["bmi"],
("aarch64", "rcpc2") => "rcpc-immo", ("x86", "cmpxchg16b") => vec!["cx16"],
("aarch64", "dpb") => "ccpp", ("x86", "avx512vaes") => vec!["vaes"],
("aarch64", "dpb2") => "ccdp", ("x86", "avx512gfni") => vec!["gfni"],
("aarch64", "frintts") => "fptoint", ("x86", "avx512vpclmulqdq") => vec!["vpclmulqdq"],
("aarch64", "fcma") => "complxnum", ("aarch64", "fp") => vec!["fp-armv8"],
(_, s) => s, ("aarch64", "fp16") => vec!["fullfp16"],
("aarch64", "fhm") => vec!["fp16fml"],
("aarch64", "rcpc2") => vec!["rcpc-immo"],
("aarch64", "dpb") => vec!["ccpp"],
("aarch64", "dpb2") => vec!["ccdp"],
("aarch64", "frintts") => vec!["fptoint"],
("aarch64", "fcma") => vec!["complxnum"],
(_, s) => vec![s],
} }
} }
@ -198,9 +205,13 @@ pub fn target_features(sess: &Session) -> Vec<Symbol> {
}, },
) )
.filter(|feature| { .filter(|feature| {
let llvm_feature = to_llvm_feature(sess, feature); for llvm_feature in to_llvm_feature(sess, feature) {
let cstr = CString::new(llvm_feature).unwrap(); let cstr = CString::new(llvm_feature).unwrap();
unsafe { llvm::LLVMRustHasFeature(target_machine, cstr.as_ptr()) } if unsafe { llvm::LLVMRustHasFeature(target_machine, cstr.as_ptr()) } {
return true;
}
}
false
}) })
.map(|feature| Symbol::intern(feature)) .map(|feature| Symbol::intern(feature))
.collect() .collect()
@ -253,12 +264,19 @@ fn print_target_features(sess: &Session, tm: &llvm::TargetMachine) {
let mut rustc_target_features = supported_target_features(sess) let mut rustc_target_features = supported_target_features(sess)
.iter() .iter()
.filter_map(|(feature, _gate)| { .filter_map(|(feature, _gate)| {
let llvm_feature = to_llvm_feature(sess, *feature); for llvm_feature in to_llvm_feature(sess, *feature) {
// 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.
target_features.binary_search_by_key(&llvm_feature, |(f, _d)| *f).ok().map(|index| { match target_features.binary_search_by_key(&llvm_feature, |(f, _d)| (*f)).ok().map(
let (_f, desc) = target_features.remove(index); |index| {
(*feature, desc) let (_f, desc) = target_features.remove(index);
}) (*feature, desc)
},
) {
Some(v) => return Some(v),
None => {}
}
}
None
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
rustc_target_features.extend_from_slice(&[( rustc_target_features.extend_from_slice(&[(
@ -373,30 +391,30 @@ pub fn llvm_global_features(sess: &Session) -> Vec<String> {
let filter = |s: &str| { let filter = |s: &str| {
if s.is_empty() { if s.is_empty() {
return None; return vec![];
} }
let feature = if s.starts_with('+') || s.starts_with('-') { let feature = if s.starts_with('+') || s.starts_with('-') {
&s[1..] &s[1..]
} else { } else {
return Some(s.to_string()); return vec![s.to_string()];
}; };
// Rustc-specific feature requests like `+crt-static` or `-crt-static` // Rustc-specific feature requests like `+crt-static` or `-crt-static`
// are not passed down to LLVM. // are not passed down to LLVM.
if RUSTC_SPECIFIC_FEATURES.contains(&feature) { if RUSTC_SPECIFIC_FEATURES.contains(&feature) {
return None; return vec![];
} }
// ... otherwise though we run through `to_llvm_feature` feature when // ... otherwise though we run through `to_llvm_feature` feature when
// passing requests down to LLVM. This means that all in-language // passing requests down to LLVM. This means that all in-language
// features also work on the command line instead of having two // features also work on the command line instead of having two
// different names when the LLVM name and the Rust name differ. // different names when the LLVM name and the Rust name differ.
Some(format!("{}{}", &s[..1], to_llvm_feature(sess, feature))) to_llvm_feature(sess, feature).iter().map(|f| format!("{}{}", &s[..1], f)).collect()
}; };
// Features implied by an implicit or explicit `--target`. // Features implied by an implicit or explicit `--target`.
features.extend(sess.target.features.split(',').filter_map(&filter)); features.extend(sess.target.features.split(',').flat_map(&filter));
// -Ctarget-features // -Ctarget-features
features.extend(sess.opts.cg.target_feature.split(',').filter_map(&filter)); features.extend(sess.opts.cg.target_feature.split(',').flat_map(&filter));
features features
} }

View file

@ -0,0 +1,12 @@
// only-x86_64
// assembly-output: emit-asm
// compile-flags: --crate-type staticlib -Ctarget-feature=+sse4.2
// CHECK-LABEL: banana
// CHECK: crc32
#[no_mangle]
pub unsafe fn banana(v: u8) -> u32 {
use std::arch::x86_64::*;
let out = !0u32;
_mm_crc32_u8(out, v)
}

View file

@ -0,0 +1,16 @@
// only-x86_64
// min-llvm-version: 14.0
// compile-flags: -Copt-level=3
#![crate_type = "lib"]
#[cfg(target_arch = "x86_64")]
#[target_feature(enable = "sse4.2")]
#[no_mangle]
pub unsafe fn crc32sse(v: u8) -> u32 {
use std::arch::x86_64::*;
let out = !0u32;
_mm_crc32_u8(out, v)
}
// CHECK: attributes #0 {{.*"target-features"="\+sse4.2,\+crc32"}}