about summary refs log tree commit diff
path: root/compiler/rustc_codegen_llvm/src
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_codegen_llvm/src')
-rw-r--r--compiler/rustc_codegen_llvm/src/attributes.rs26
-rw-r--r--compiler/rustc_codegen_llvm/src/back/write.rs17
-rw-r--r--compiler/rustc_codegen_llvm/src/lib.rs10
-rw-r--r--compiler/rustc_codegen_llvm/src/llvm_util.rs194
4 files changed, 158 insertions, 89 deletions
diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs
index 13a41388f5e..8f30f0895f4 100644
--- a/compiler/rustc_codegen_llvm/src/attributes.rs
+++ b/compiler/rustc_codegen_llvm/src/attributes.rs
@@ -79,13 +79,11 @@ pub fn sanitize_attrs<'ll>(
     }
     if enabled.contains(SanitizerSet::MEMTAG) {
         // Check to make sure the mte target feature is actually enabled.
-        let sess = cx.tcx.sess;
-        let features = llvm_util::llvm_global_features(sess).join(",");
-        let mte_feature_enabled = features.rfind("+mte");
-        let mte_feature_disabled = features.rfind("-mte");
-
-        if mte_feature_enabled.is_none() || (mte_feature_disabled > mte_feature_enabled) {
-            sess.err("`-Zsanitizer=memtag` requires `-Ctarget-feature=+mte`");
+        let features = cx.tcx.global_backend_features(());
+        let mte_feature =
+            features.iter().map(|s| &s[..]).rfind(|n| ["+mte", "-mte"].contains(&&n[..]));
+        if let None | Some("-mte") = mte_feature {
+            cx.tcx.sess.err("`-Zsanitizer=memtag` requires `-Ctarget-feature=+mte`");
         }
 
         attrs.push(llvm::AttributeKind::SanitizeMemTag.create_attr(cx.llcx));
@@ -382,10 +380,7 @@ pub fn from_fn_attrs<'ll, 'tcx>(
     let mut function_features = function_features
         .iter()
         .flat_map(|feat| {
-            llvm_util::to_llvm_feature(cx.tcx.sess, feat)
-                .into_iter()
-                .map(|f| format!("+{}", f))
-                .collect::<Vec<String>>()
+            llvm_util::to_llvm_features(cx.tcx.sess, feat).into_iter().map(|f| format!("+{}", f))
         })
         .chain(codegen_fn_attrs.instruction_set.iter().map(|x| match x {
             InstructionSetAttr::ArmA32 => "-thumb-mode".to_string(),
@@ -418,10 +413,11 @@ pub fn from_fn_attrs<'ll, 'tcx>(
     }
 
     if !function_features.is_empty() {
-        let mut global_features = llvm_util::llvm_global_features(cx.tcx.sess);
-        global_features.extend(function_features.into_iter());
-        let features = global_features.join(",");
-        let val = CString::new(features).unwrap();
+        let global_features = cx.tcx.global_backend_features(()).iter().map(|s| &s[..]);
+        let val = global_features
+            .chain(function_features.iter().map(|s| &s[..]))
+            .intersperse(",")
+            .collect::<SmallCStr>();
         to_add.push(llvm::CreateAttrStringValue(cx.llcx, cstr!("target-features"), &val));
     }
 
diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs
index e60ad170434..c18719d4ad7 100644
--- a/compiler/rustc_codegen_llvm/src/back/write.rs
+++ b/compiler/rustc_codegen_llvm/src/back/write.rs
@@ -100,7 +100,10 @@ pub fn write_output_file<'ll>(
 
 pub fn create_informational_target_machine(sess: &Session) -> &'static mut llvm::TargetMachine {
     let config = TargetMachineFactoryConfig { split_dwarf_file: None };
-    target_machine_factory(sess, config::OptLevel::No)(config)
+    // Can't use query system here quite yet because this function is invoked before the query
+    // system/tcx is set up.
+    let features = llvm_util::global_llvm_features(sess, false);
+    target_machine_factory(sess, config::OptLevel::No, &features)(config)
         .unwrap_or_else(|err| llvm_err(sess.diagnostic(), &err).raise())
 }
 
@@ -115,8 +118,12 @@ pub fn create_target_machine(tcx: TyCtxt<'_>, mod_name: &str) -> &'static mut ll
         None
     };
     let config = TargetMachineFactoryConfig { split_dwarf_file };
-    target_machine_factory(tcx.sess, tcx.backend_optimization_level(()))(config)
-        .unwrap_or_else(|err| llvm_err(tcx.sess.diagnostic(), &err).raise())
+    target_machine_factory(
+        &tcx.sess,
+        tcx.backend_optimization_level(()),
+        tcx.global_backend_features(()),
+    )(config)
+    .unwrap_or_else(|err| llvm_err(tcx.sess.diagnostic(), &err).raise())
 }
 
 pub fn to_llvm_opt_settings(
@@ -171,6 +178,7 @@ pub(crate) fn to_llvm_code_model(code_model: Option<CodeModel>) -> llvm::CodeMod
 pub fn target_machine_factory(
     sess: &Session,
     optlvl: config::OptLevel,
+    target_features: &[String],
 ) -> TargetMachineFactoryFn<LlvmCodegenBackend> {
     let reloc_model = to_llvm_relocation_model(sess.relocation_model());
 
@@ -195,8 +203,7 @@ pub fn target_machine_factory(
 
     let triple = SmallCStr::new(&sess.target.llvm_target);
     let cpu = SmallCStr::new(llvm_util::target_cpu(sess));
-    let features = llvm_util::llvm_global_features(sess).join(",");
-    let features = CString::new(features).unwrap();
+    let features = CString::new(target_features.join(",")).unwrap();
     let abi = SmallCStr::new(&sess.target.llvm_abiname);
     let trap_unreachable =
         sess.opts.debugging_opts.trap_unreachable.unwrap_or(sess.target.trap_unreachable);
diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs
index c2211005a03..09c2c4ec771 100644
--- a/compiler/rustc_codegen_llvm/src/lib.rs
+++ b/compiler/rustc_codegen_llvm/src/lib.rs
@@ -11,6 +11,7 @@
 #![feature(extern_types)]
 #![feature(once_cell)]
 #![feature(nll)]
+#![feature(iter_intersperse)]
 #![recursion_limit = "256"]
 #![allow(rustc::potential_query_instability)]
 
@@ -32,6 +33,7 @@ use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::{ErrorReported, FatalError, Handler};
 use rustc_metadata::EncodedMetadata;
 use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
+use rustc_middle::ty::query::Providers;
 use rustc_middle::ty::TyCtxt;
 use rustc_session::config::{OptLevel, OutputFilenames, PrintRequest};
 use rustc_session::Session;
@@ -126,8 +128,9 @@ impl ExtraBackendMethods for LlvmCodegenBackend {
         &self,
         sess: &Session,
         optlvl: OptLevel,
+        target_features: &[String],
     ) -> TargetMachineFactoryFn<Self> {
-        back::write::target_machine_factory(sess, optlvl)
+        back::write::target_machine_factory(sess, optlvl, target_features)
     }
     fn target_cpu<'b>(&self, sess: &'b Session) -> &'b str {
         llvm_util::target_cpu(sess)
@@ -251,6 +254,11 @@ impl CodegenBackend for LlvmCodegenBackend {
         llvm_util::init(sess); // Make sure llvm is inited
     }
 
+    fn provide(&self, providers: &mut Providers) {
+        providers.global_backend_features =
+            |tcx, ()| llvm_util::global_llvm_features(tcx.sess, true)
+    }
+
     fn print(&self, req: PrintRequest, sess: &Session) {
         match req {
             PrintRequest::RelocationModels => {
diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs
index 6c974eb9c0b..3b06587061d 100644
--- a/compiler/rustc_codegen_llvm/src/llvm_util.rs
+++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs
@@ -2,14 +2,18 @@ use crate::back::write::create_informational_target_machine;
 use crate::{llvm, llvm_util};
 use libc::c_int;
 use libloading::Library;
-use rustc_codegen_ssa::target_features::{supported_target_features, tied_target_features};
+use rustc_codegen_ssa::target_features::{
+    supported_target_features, tied_target_features, RUSTC_SPECIFIC_FEATURES,
+};
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_data_structures::small_c_str::SmallCStr;
 use rustc_fs_util::path_to_c_string;
 use rustc_middle::bug;
 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 tracing::debug;
 
@@ -155,9 +159,10 @@ pub fn time_trace_profiler_finish(file_name: &Path) {
     }
 }
 
-// WARNING: the features after applying `to_llvm_feature` must be known
+// 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.
+//
 // To find a list of LLVM's names, check llvm-project/llvm/include/llvm/Support/*TargetParser.def
 // where the * matches the architecture's name
 // Beware to not use the llvm github project for this, but check the git submodule
@@ -165,35 +170,35 @@ 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_feature<'a>(sess: &Session, s: &'a str) -> Vec<&'a str> {
+pub fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> SmallVec<[&'a str; 2]> {
     let arch = if sess.target.arch == "x86_64" { "x86" } else { &*sess.target.arch };
     match (arch, s) {
         ("x86", "sse4.2") => {
             if get_version() >= (14, 0, 0) {
-                vec!["sse4.2", "crc32"]
+                smallvec!["sse4.2", "crc32"]
             } else {
-                vec!["sse4.2"]
+                smallvec!["sse4.2"]
             }
         }
-        ("x86", "pclmulqdq") => vec!["pclmul"],
-        ("x86", "rdrand") => vec!["rdrnd"],
-        ("x86", "bmi1") => vec!["bmi"],
-        ("x86", "cmpxchg16b") => vec!["cx16"],
-        ("x86", "avx512vaes") => vec!["vaes"],
-        ("x86", "avx512gfni") => vec!["gfni"],
-        ("x86", "avx512vpclmulqdq") => vec!["vpclmulqdq"],
-        ("aarch64", "fp") => vec!["fp-armv8"],
-        ("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"],
-        ("aarch64", "pmuv3") => vec!["perfmon"],
-        ("aarch64", "paca") => vec!["pauth"],
-        ("aarch64", "pacg") => vec!["pauth"],
-        (_, s) => vec![s],
+        ("x86", "pclmulqdq") => smallvec!["pclmul"],
+        ("x86", "rdrand") => smallvec!["rdrnd"],
+        ("x86", "bmi1") => smallvec!["bmi"],
+        ("x86", "cmpxchg16b") => smallvec!["cx16"],
+        ("x86", "avx512vaes") => smallvec!["vaes"],
+        ("x86", "avx512gfni") => smallvec!["gfni"],
+        ("x86", "avx512vpclmulqdq") => smallvec!["vpclmulqdq"],
+        ("aarch64", "fp") => smallvec!["fp-armv8"],
+        ("aarch64", "fp16") => smallvec!["fullfp16"],
+        ("aarch64", "fhm") => smallvec!["fp16fml"],
+        ("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"],
+        (_, s) => smallvec![s],
     }
 }
 
@@ -207,7 +212,6 @@ pub fn check_tied_features(
         // Tied features must be set to the same value, or not set at all
         let mut tied_iter = tied.iter();
         let enabled = features.get(tied_iter.next().unwrap());
-
         if tied_iter.any(|f| enabled != features.get(f)) {
             return Some(tied);
         }
@@ -224,8 +228,8 @@ pub fn target_features(sess: &Session) -> Vec<Symbol> {
                 if sess.is_nightly_build() || gate.is_none() { Some(feature) } else { None }
             })
             .filter(|feature| {
-                for llvm_feature in to_llvm_feature(sess, feature) {
-                    let cstr = CString::new(llvm_feature).unwrap();
+                for llvm_feature in to_llvm_features(sess, feature) {
+                    let cstr = SmallCStr::new(llvm_feature);
                     if unsafe { llvm::LLVMRustHasFeature(target_machine, cstr.as_ptr()) } {
                         return true;
                     }
@@ -292,9 +296,9 @@ fn print_target_features(sess: &Session, tm: &llvm::TargetMachine) {
     let mut rustc_target_features = supported_target_features(sess)
         .iter()
         .filter_map(|(feature, _gate)| {
-            for llvm_feature in to_llvm_feature(sess, *feature) {
+            for llvm_feature in to_llvm_features(sess, *feature) {
                 // LLVM asserts that these are sorted. LLVM and Rust both use byte comparison for these strings.
-                match target_features.binary_search_by_key(&llvm_feature, |(f, _d)| (*f)).ok().map(
+                match target_features.binary_search_by_key(&llvm_feature, |(f, _d)| f).ok().map(
                     |index| {
                         let (_f, desc) = target_features.remove(index);
                         (*feature, desc)
@@ -364,14 +368,7 @@ pub fn target_cpu(sess: &Session) -> &str {
 
 /// The list of LLVM features computed from CLI flags (`-Ctarget-cpu`, `-Ctarget-feature`,
 /// `--target` and similar).
-// FIXME(nagisa): Cache the output of this somehow? Maybe make this a query? We're calling this
-// for every function that has `#[target_feature]` on it. The global features won't change between
-// the functions; only crates, maybe…
-pub fn llvm_global_features(sess: &Session) -> Vec<String> {
-    // FIXME(nagisa): this should definitely be available more centrally and to other codegen backends.
-    /// These features control behaviour of rustc rather than llvm.
-    const RUSTC_SPECIFIC_FEATURES: &[&str] = &["crt-static"];
-
+pub(crate) fn global_llvm_features(sess: &Session, diagnostics: bool) -> Vec<String> {
     // Features that come earlier are overriden by conflicting features later in the string.
     // Typically we'll want more explicit settings to override the implicit ones, so:
     //
@@ -417,47 +414,108 @@ pub fn llvm_global_features(sess: &Session) -> Vec<String> {
         Some(_) | None => {}
     };
 
-    fn strip(s: &str) -> &str {
-        s.strip_prefix(&['+', '-']).unwrap_or(s)
-    }
+    // Features implied by an implicit or explicit `--target`.
+    features.extend(
+        sess.target
+            .features
+            .split(',')
+            .filter(|v| !v.is_empty() && backend_feature_name(v).is_some())
+            .map(String::from),
+    );
 
-    let filter = |s: &str| {
-        if s.is_empty() {
-            return vec![];
-        }
-        let feature = strip(s);
-        if feature == s {
-            return vec![s.to_string()];
+    // -Ctarget-features
+    let supported_features = supported_target_features(sess);
+    let feats = sess
+        .opts
+        .cg
+        .target_feature
+        .split(',')
+        .filter_map(|s| {
+            let enable_disable = match s.chars().next() {
+                None => return None,
+                Some(c @ '+' | c @ '-') => c,
+                Some(_) => {
+                    if diagnostics {
+                        let mut diag = sess.struct_warn(&format!(
+                            "unknown feature specified for `-Ctarget-feature`: `{}`",
+                            s
+                        ));
+                        diag.note("features must begin with a `+` to enable or `-` to disable it");
+                        diag.emit();
+                    }
+                    return None;
+                }
+            };
+
+            let feature = backend_feature_name(s)?;
+            // Warn against use of LLVM specific feature names on the CLI.
+            if diagnostics && !supported_features.iter().any(|&(v, _)| v == feature) {
+                let rust_feature = supported_features.iter().find_map(|&(rust_feature, _)| {
+                    let llvm_features = to_llvm_features(sess, rust_feature);
+                    if llvm_features.contains(&feature) && !llvm_features.contains(&rust_feature) {
+                        Some(rust_feature)
+                    } else {
+                        None
+                    }
+                });
+                let mut diag = sess.struct_warn(&format!(
+                    "unknown feature specified for `-Ctarget-feature`: `{}`",
+                    feature
+                ));
+                diag.note("it is still passed through to the codegen backend");
+                if let Some(rust_feature) = rust_feature {
+                    diag.help(&format!("you might have meant: `{}`", rust_feature));
+                } else {
+                    diag.note("consider filing a feature request");
+                }
+                diag.emit();
+            }
+            Some((enable_disable, feature))
+        })
+        .collect::<SmallVec<[(char, &str); 8]>>();
+
+    if diagnostics {
+        // FIXME(nagisa): figure out how to not allocate a full hashset here.
+        let featmap = feats.iter().map(|&(flag, feat)| (feat, flag == '+')).collect();
+        if let Some(f) = check_tied_features(sess, &featmap) {
+            sess.err(&format!(
+                "target features {} must all be enabled or disabled together",
+                f.join(", ")
+            ));
         }
+    }
 
-        // Rustc-specific feature requests like `+crt-static` or `-crt-static`
-        // are not passed down to LLVM.
+    features.extend(feats.into_iter().flat_map(|(enable_disable, feature)| {
+        // rustc-specific features do not get passed down to LLVM…
         if RUSTC_SPECIFIC_FEATURES.contains(&feature) {
-            return vec![];
+            return SmallVec::<[_; 2]>::new();
         }
-        // ... otherwise though we run through `to_llvm_feature` feature when
+        // ... otherwise though we run through `to_llvm_feature when
         // 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.
-        to_llvm_feature(sess, feature).iter().map(|f| format!("{}{}", &s[..1], f)).collect()
-    };
-
-    // Features implied by an implicit or explicit `--target`.
-    features.extend(sess.target.features.split(',').flat_map(&filter));
+        to_llvm_features(sess, feature)
+            .into_iter()
+            .map(|f| format!("{}{}", enable_disable, f))
+            .collect()
+    }));
+    features
+}
 
-    // -Ctarget-features
-    let feats: Vec<&str> = sess.opts.cg.target_feature.split(',').collect();
-    // LLVM enables based on the last occurence of a feature
-    if let Some(f) =
-        check_tied_features(sess, &feats.iter().map(|f| (strip(f), !f.starts_with("-"))).collect())
-    {
-        sess.err(&format!(
-            "Target features {} must all be enabled or disabled together",
-            f.join(", ")
-        ));
+/// Returns a feature name for the given `+feature` or `-feature` string.
+///
+/// Only allows features that are backend specific (i.e. not [`RUSTC_SPECIFIC_FEATURES`].)
+fn backend_feature_name(s: &str) -> Option<&str> {
+    // features must start with a `+` or `-`.
+    let feature = s.strip_prefix(&['+', '-'][..]).unwrap_or_else(|| {
+        bug!("target feature `{}` must begin with a `+` or `-`", s);
+    });
+    // Rustc-specific feature requests like `+crt-static` or `-crt-static`
+    // are not passed down to LLVM.
+    if RUSTC_SPECIFIC_FEATURES.contains(&feature) {
+        return None;
     }
-    features.extend(feats.iter().flat_map(|&f| filter(f)));
-    features
+    Some(feature)
 }
 
 pub fn tune_cpu(sess: &Session) -> Option<&str> {