about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2016-04-20 09:57:57 -0700
committerbors <bors@rust-lang.org>2016-04-20 09:57:57 -0700
commit92e3fb3ebe6d7129ff0fe85c17f30c8669899129 (patch)
tree7d783992f028cebcef184a04ebd6d08dc44bd224
parent6ece1447f02b2310e5f4c7ef1efd1312476cae80 (diff)
parentce99a5e5d8ead9140bb9b48c01f1a200950f8ea3 (diff)
downloadrust-92e3fb3ebe6d7129ff0fe85c17f30c8669899129.tar.gz
rust-92e3fb3ebe6d7129ff0fe85c17f30c8669899129.zip
Auto merge of #31709 - ranma42:target_feature-from-llvm, r=alexcrichton
Compute `target_feature` from LLVM

This is a work-in-progress fix for #31662.

The logic that computes the target features from the command line has been replaced with queries to the `TargetMachine`.
-rw-r--r--mk/crates.mk2
-rw-r--r--mk/rustllvm.mk4
-rw-r--r--src/librustc/Cargo.toml1
-rw-r--r--src/librustc/lib.rs1
-rw-r--r--src/librustc/session/mod.rs57
-rw-r--r--src/librustc_driver/target_features.rs105
-rw-r--r--src/librustc_llvm/build.rs7
-rw-r--r--src/librustc_llvm/lib.rs3
-rw-r--r--src/librustc_trans/back/write.rs32
-rw-r--r--src/librustc_trans/base.rs20
-rw-r--r--src/rustllvm/PassWrapper.cpp69
-rw-r--r--src/test/run-make/print-cfg/Makefile1
-rw-r--r--src/test/run-pass/sse2.rs18
13 files changed, 203 insertions, 117 deletions
diff --git a/mk/crates.mk b/mk/crates.mk
index 4003e092034..c55a6e791b6 100644
--- a/mk/crates.mk
+++ b/mk/crates.mk
@@ -97,7 +97,7 @@ DEPS_rustc_const_eval := rustc_const_math rustc syntax log serialize \
 					     rustc_back graphviz
 
 DEPS_rustc := syntax fmt_macros flate arena serialize getopts rbml \
-              log graphviz rustc_back rustc_data_structures\
+              log graphviz rustc_llvm rustc_back rustc_data_structures\
 		  	  rustc_const_math
 DEPS_rustc_back := std syntax flate log libc
 DEPS_rustc_borrowck := rustc rustc_mir log graphviz syntax
diff --git a/mk/rustllvm.mk b/mk/rustllvm.mk
index 6adffda7d1b..834a11d37fa 100644
--- a/mk/rustllvm.mk
+++ b/mk/rustllvm.mk
@@ -43,6 +43,9 @@ $$(RT_OUTPUT_DIR_$(1))/$$(call CFG_STATIC_LIB_NAME_$(1),rustllvm): \
 	@$$(call E, link: $$@)
 	$$(Q)$$(call CFG_CREATE_ARCHIVE_$(1),$$@) $$^
 
+RUSTLLVM_COMPONENTS_$(1) = $$(shell echo $$(LLVM_ALL_COMPONENTS_$(1)) |\
+	tr 'a-z-' 'A-Z_'| sed -e 's/^ //;s/\([^ ]*\)/\-DLLVM_COMPONENT_\1/g')
+
 # On MSVC we need to double-escape arguments that llvm-config printed which
 # start with a '/'. The shell we're running in will auto-translate the argument
 # `/foo` to `C:/msys64/foo` but we really want it to be passed through as `/foo`
@@ -51,6 +54,7 @@ $(1)/rustllvm/%.o: $(S)src/rustllvm/%.cpp $$(MKFILE_DEPS) $$(LLVM_CONFIG_$(1))
 	@$$(call E, compile: $$@)
 	$$(Q)$$(call CFG_COMPILE_CXX_$(1), $$@,) \
 		$$(subst  /,//,$$(LLVM_CXXFLAGS_$(1))) \
+		$$(RUSTLLVM_COMPONENTS_$(1)) \
 		$$(EXTRA_RUSTLLVM_CXXFLAGS_$(1)) \
 		$$(RUSTLLVM_INCS_$(1)) \
 		$$<
diff --git a/src/librustc/Cargo.toml b/src/librustc/Cargo.toml
index d77268219fc..9291227a734 100644
--- a/src/librustc/Cargo.toml
+++ b/src/librustc/Cargo.toml
@@ -19,5 +19,6 @@ rustc_back = { path = "../librustc_back" }
 rustc_bitflags = { path = "../librustc_bitflags" }
 rustc_const_math = { path = "../librustc_const_math" }
 rustc_data_structures = { path = "../librustc_data_structures" }
+rustc_llvm = { path = "../librustc_llvm" }
 serialize = { path = "../libserialize" }
 syntax = { path = "../libsyntax" }
diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs
index d1bbbf08ac2..ed63783366b 100644
--- a/src/librustc/lib.rs
+++ b/src/librustc/lib.rs
@@ -49,6 +49,7 @@ extern crate getopts;
 extern crate graphviz;
 extern crate libc;
 extern crate rbml;
+extern crate rustc_llvm as llvm;
 extern crate rustc_back;
 extern crate rustc_data_structures;
 extern crate serialize;
diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs
index 815e60a8e03..da285c62ae4 100644
--- a/src/librustc/session/mod.rs
+++ b/src/librustc/session/mod.rs
@@ -30,13 +30,16 @@ use syntax::{ast, codemap};
 use syntax::feature_gate::AttributeType;
 
 use rustc_back::target::Target;
+use llvm;
 
 use std::path::{Path, PathBuf};
 use std::cell::{Cell, RefCell};
 use std::collections::{HashMap, HashSet};
 use std::env;
+use std::ffi::CString;
 use std::rc::Rc;
 use std::fmt;
+use libc::c_int;
 
 pub mod config;
 pub mod filesearch;
@@ -491,9 +494,63 @@ pub fn build_session_(sopts: config::Options,
         imported_macro_spans: RefCell::new(HashMap::new()),
     };
 
+    init_llvm(&sess);
+
     sess
 }
 
+fn init_llvm(sess: &Session) {
+    unsafe {
+        // Before we touch LLVM, make sure that multithreading is enabled.
+        use std::sync::Once;
+        static INIT: Once = Once::new();
+        static mut POISONED: bool = false;
+        INIT.call_once(|| {
+            if llvm::LLVMStartMultithreaded() != 1 {
+                // use an extra bool to make sure that all future usage of LLVM
+                // cannot proceed despite the Once not running more than once.
+                POISONED = true;
+            }
+
+            configure_llvm(sess);
+        });
+
+        if POISONED {
+            bug!("couldn't enable multi-threaded LLVM");
+        }
+    }
+}
+
+unsafe fn configure_llvm(sess: &Session) {
+    let mut llvm_c_strs = Vec::new();
+    let mut llvm_args = Vec::new();
+
+    {
+        let mut add = |arg: &str| {
+            let s = CString::new(arg).unwrap();
+            llvm_args.push(s.as_ptr());
+            llvm_c_strs.push(s);
+        };
+        add("rustc"); // fake program name
+        if sess.time_llvm_passes() { add("-time-passes"); }
+        if sess.print_llvm_passes() { add("-debug-pass=Structure"); }
+
+        // FIXME #21627 disable faulty FastISel on AArch64 (even for -O0)
+        if sess.target.target.arch == "aarch64" { add("-fast-isel=0"); }
+
+        for arg in &sess.opts.cg.llvm_args {
+            add(&(*arg));
+        }
+    }
+
+    llvm::LLVMInitializePasses();
+
+    llvm::initialize_available_targets();
+
+    llvm::LLVMRustSetLLVMOptions(llvm_args.len() as c_int,
+                                 llvm_args.as_ptr());
+}
+
 pub fn early_error(output: config::ErrorOutputType, msg: &str) -> ! {
     let mut emitter: Box<Emitter> = match output {
         config::ErrorOutputType::HumanReadable(color_config) => {
diff --git a/src/librustc_driver/target_features.rs b/src/librustc_driver/target_features.rs
index 27ffb595a40..fad0af19a12 100644
--- a/src/librustc_driver/target_features.rs
+++ b/src/librustc_driver/target_features.rs
@@ -9,79 +9,54 @@
 // except according to those terms.
 
 use syntax::{ast, attr};
+use llvm::LLVMRustHasFeature;
 use rustc::session::Session;
+use rustc_trans::back::write::create_target_machine;
 use syntax::parse::token::InternedString;
 use syntax::parse::token::intern_and_get_ident as intern;
+use libc::c_char;
+
+// WARNING: the features must be known to LLVM or the feature
+// detection code will walk past the end of the feature array,
+// leading to crashes.
+
+const ARM_WHITELIST: &'static [&'static str] = &[
+    "neon\0",
+    "vfp2\0",
+    "vfp3\0",
+    "vfp4\0",
+];
+
+const X86_WHITELIST: &'static [&'static str] = &[
+    "avx\0",
+    "avx2\0",
+    "sse\0",
+    "sse2\0",
+    "sse3\0",
+    "sse4.1\0",
+    "sse4.2\0",
+    "ssse3\0",
+];
 
 /// Add `target_feature = "..."` cfgs for a variety of platform
 /// specific features (SSE, NEON etc.).
 ///
-/// This uses a scheme similar to that employed by clang: reimplement
-/// the target feature knowledge. *Theoretically* we could query LLVM
-/// since that has perfect knowledge about what things are enabled in
-/// code-generation, however, it is extremely non-obvious how to do
-/// this successfully. Each platform defines a subclass of a
-/// SubtargetInfo, which knows all this information, but the ways to
-/// query them do not seem to be public.
+/// This is performed by checking whether a whitelisted set of
+/// features is available on the target machine, by querying LLVM.
 pub fn add_configuration(cfg: &mut ast::CrateConfig, sess: &Session) {
-    let tf = InternedString::new("target_feature");
-    macro_rules! fillout {
-        ($($func: ident, $name: expr;)*) => {{
-            $(if $func(sess) {
-                cfg.push(attr::mk_name_value_item_str(tf.clone(), intern($name)))
-            })*
-        }}
-    }
-    fillout! {
-        has_sse, "sse";
-        has_sse2, "sse2";
-        has_sse3, "sse3";
-        has_ssse3, "ssse3";
-        has_sse41, "sse4.1";
-        has_sse42, "sse4.2";
-        has_avx, "avx";
-        has_avx2, "avx2";
-        has_neon, "neon";
-        has_vfp, "vfp";
-    }
-}
-
+    let target_machine = create_target_machine(sess);
 
-fn features_contain(sess: &Session, s: &str) -> bool {
-    sess.target.target.options.features.contains(s) || sess.opts.cg.target_feature.contains(s)
-}
+    let whitelist = match &*sess.target.target.arch {
+        "arm" => ARM_WHITELIST,
+        "x86" | "x86_64" => X86_WHITELIST,
+        _ => &[],
+    };
 
-pub fn has_sse(sess: &Session) -> bool {
-    features_contain(sess, "+sse") || has_sse2(sess)
-}
-pub fn has_sse2(sess: &Session) -> bool {
-    // x86-64 requires at least SSE2 support
-    sess.target.target.arch == "x86_64" || features_contain(sess, "+sse2") || has_sse3(sess)
-}
-pub fn has_sse3(sess: &Session) -> bool {
-    features_contain(sess, "+sse3") || has_ssse3(sess)
-}
-pub fn has_ssse3(sess: &Session) -> bool {
-    features_contain(sess, "+ssse3") || has_sse41(sess)
-}
-pub fn has_sse41(sess: &Session) -> bool {
-    features_contain(sess, "+sse4.1") || has_sse42(sess)
-}
-pub fn has_sse42(sess: &Session) -> bool {
-    features_contain(sess, "+sse4.2") || has_avx(sess)
-}
-pub fn has_avx(sess: &Session) -> bool {
-    features_contain(sess, "+avx") || has_avx2(sess)
-}
-pub fn has_avx2(sess: &Session) -> bool {
-    features_contain(sess, "+avx2")
-}
-
-pub fn has_neon(sess: &Session) -> bool {
-    // AArch64 requires NEON support
-    sess.target.target.arch == "aarch64" || features_contain(sess, "+neon")
-}
-pub fn has_vfp(sess: &Session) -> bool {
-    // AArch64 requires VFP support
-    sess.target.target.arch == "aarch64" || features_contain(sess, "+vfp")
+    let tf = InternedString::new("target_feature");
+    for feat in whitelist {
+        assert_eq!(feat.chars().last(), Some('\0'));
+        if unsafe { LLVMRustHasFeature(target_machine, feat.as_ptr() as *const c_char) } {
+            cfg.push(attr::mk_name_value_item_str(tf.clone(), intern(&feat[..feat.len()-1])))
+        }
+    }
 }
diff --git a/src/librustc_llvm/build.rs b/src/librustc_llvm/build.rs
index dcfb518ba79..0c6db2cb8ba 100644
--- a/src/librustc_llvm/build.rs
+++ b/src/librustc_llvm/build.rs
@@ -100,6 +100,13 @@ fn main() {
         }
         cfg.flag(flag);
     }
+
+    for component in &components[..] {
+        let mut flag = String::from("-DLLVM_COMPONENT_");
+        flag.push_str(&component.to_uppercase());
+        cfg.flag(&flag);
+    }
+
     cfg.file("../rustllvm/ExecutionEngineWrapper.cpp")
        .file("../rustllvm/PassWrapper.cpp")
        .file("../rustllvm/RustWrapper.cpp")
diff --git a/src/librustc_llvm/lib.rs b/src/librustc_llvm/lib.rs
index a026e546e9c..12d652bd5cb 100644
--- a/src/librustc_llvm/lib.rs
+++ b/src/librustc_llvm/lib.rs
@@ -2013,6 +2013,9 @@ extern {
     pub fn LLVMRustFindAndCreatePass(Pass: *const c_char) -> PassRef;
     pub fn LLVMRustAddPass(PM: PassManagerRef, Pass: PassRef);
 
+    pub fn LLVMRustHasFeature(T: TargetMachineRef,
+                              s: *const c_char) -> bool;
+
     pub fn LLVMRustCreateTargetMachine(Triple: *const c_char,
                                        CPU: *const c_char,
                                        Features: *const c_char,
diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs
index e6a51eb7c87..8a915f04405 100644
--- a/src/librustc_trans/back/write.rs
+++ b/src/librustc_trans/back/write.rs
@@ -31,7 +31,7 @@ use std::str;
 use std::sync::{Arc, Mutex};
 use std::sync::mpsc::channel;
 use std::thread;
-use libc::{c_uint, c_int, c_void};
+use libc::{c_uint, c_void};
 
 pub fn llvm_err(handler: &errors::Handler, msg: String) -> ! {
     match llvm::last_error() {
@@ -984,36 +984,6 @@ pub fn run_assembler(sess: &Session, outputs: &OutputFilenames) {
     }
 }
 
-pub unsafe fn configure_llvm(sess: &Session) {
-    let mut llvm_c_strs = Vec::new();
-    let mut llvm_args = Vec::new();
-
-    {
-        let mut add = |arg: &str| {
-            let s = CString::new(arg).unwrap();
-            llvm_args.push(s.as_ptr());
-            llvm_c_strs.push(s);
-        };
-        add("rustc"); // fake program name
-        if sess.time_llvm_passes() { add("-time-passes"); }
-        if sess.print_llvm_passes() { add("-debug-pass=Structure"); }
-
-        // FIXME #21627 disable faulty FastISel on AArch64 (even for -O0)
-        if sess.target.target.arch == "aarch64" { add("-fast-isel=0"); }
-
-        for arg in &sess.opts.cg.llvm_args {
-            add(&(*arg));
-        }
-    }
-
-    llvm::LLVMInitializePasses();
-
-    llvm::initialize_available_targets();
-
-    llvm::LLVMRustSetLLVMOptions(llvm_args.len() as c_int,
-                                 llvm_args.as_ptr());
-}
-
 pub unsafe fn with_llvm_pmb(llmod: ModuleRef,
                             config: &ModuleConfig,
                             f: &mut FnMut(llvm::PassManagerBuilderRef)) {
diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs
index 8052fc21cc7..cea67f46db5 100644
--- a/src/librustc_trans/base.rs
+++ b/src/librustc_trans/base.rs
@@ -2713,26 +2713,6 @@ pub fn trans_crate<'tcx>(tcx: &TyCtxt<'tcx>,
         tcx.sess.opts.debug_assertions
     };
 
-    // Before we touch LLVM, make sure that multithreading is enabled.
-    unsafe {
-        use std::sync::Once;
-        static INIT: Once = Once::new();
-        static mut POISONED: bool = false;
-        INIT.call_once(|| {
-            if llvm::LLVMStartMultithreaded() != 1 {
-                // use an extra bool to make sure that all future usage of LLVM
-                // cannot proceed despite the Once not running more than once.
-                POISONED = true;
-            }
-
-            ::back::write::configure_llvm(&tcx.sess);
-        });
-
-        if POISONED {
-            bug!("couldn't enable multi-threaded LLVM");
-        }
-    }
-
     let link_meta = link::build_link_meta(&tcx, name);
 
     let codegen_units = tcx.sess.opts.cg.codegen_units;
diff --git a/src/rustllvm/PassWrapper.cpp b/src/rustllvm/PassWrapper.cpp
index d6985719acb..b3d4e35d7b0 100644
--- a/src/rustllvm/PassWrapper.cpp
+++ b/src/rustllvm/PassWrapper.cpp
@@ -97,6 +97,75 @@ LLVMRustAddPass(LLVMPassManagerRef PM, Pass *pass) {
     pm->add(pass);
 }
 
+#ifdef LLVM_COMPONENT_X86
+#define SUBTARGET_X86 SUBTARGET(X86)
+#else
+#define SUBTARGET_X86
+#endif
+
+#ifdef LLVM_COMPONENT_ARM
+#define SUBTARGET_ARM SUBTARGET(ARM)
+#else
+#define SUBTARGET_ARM
+#endif
+
+#ifdef LLVM_COMPONENT_AARCH64
+#define SUBTARGET_AARCH64 SUBTARGET(AArch64)
+#else
+#define SUBTARGET_AARCH64
+#endif
+
+#ifdef LLVM_COMPONENT_MIPS
+#define SUBTARGET_MIPS SUBTARGET(Mips)
+#else
+#define SUBTARGET_MIPS
+#endif
+
+#ifdef LLVM_COMPONENT_POWERPC
+#define SUBTARGET_PPC SUBTARGET(PPC)
+#else
+#define SUBTARGET_PPC
+#endif
+
+#define GEN_SUBTARGETS    \
+        SUBTARGET_X86     \
+        SUBTARGET_ARM     \
+        SUBTARGET_AARCH64 \
+        SUBTARGET_MIPS    \
+        SUBTARGET_PPC
+
+#define SUBTARGET(x) namespace llvm {                \
+    extern const SubtargetFeatureKV x##FeatureKV[];  \
+    extern const SubtargetFeatureKV x##SubTypeKV[];  \
+  }
+
+GEN_SUBTARGETS
+#undef SUBTARGET
+
+extern "C" bool
+LLVMRustHasFeature(LLVMTargetMachineRef TM,
+		   const char *feature) {
+    TargetMachine *Target = unwrap(TM);
+    const MCSubtargetInfo *MCInfo = Target->getMCSubtargetInfo();
+    const FeatureBitset &Bits = MCInfo->getFeatureBits();
+    const llvm::SubtargetFeatureKV *FeatureEntry;
+
+#define SUBTARGET(x)                                        \
+    if (MCInfo->isCPUStringValid(x##SubTypeKV[0].Key)) {    \
+        FeatureEntry = x##FeatureKV;                       \
+    } else
+
+    GEN_SUBTARGETS {
+        return false;
+    }
+#undef SUBTARGET
+
+    while (strcmp(feature, FeatureEntry->Key) != 0)
+        FeatureEntry++;
+
+    return (Bits & FeatureEntry->Value) == FeatureEntry->Value;
+}
+
 extern "C" LLVMTargetMachineRef
 LLVMRustCreateTargetMachine(const char *triple,
                             const char *cpu,
diff --git a/src/test/run-make/print-cfg/Makefile b/src/test/run-make/print-cfg/Makefile
index c74233d495b..a820a463f4a 100644
--- a/src/test/run-make/print-cfg/Makefile
+++ b/src/test/run-make/print-cfg/Makefile
@@ -5,6 +5,7 @@ all: default
 	$(RUSTC) --target x86_64-pc-windows-gnu --print cfg | grep x86_64
 	$(RUSTC) --target i686-pc-windows-msvc --print cfg | grep msvc
 	$(RUSTC) --target i686-apple-darwin --print cfg | grep macos
+	$(RUSTC) --target i686-unknown-linux-gnu --print cfg | grep sse2
 
 ifdef IS_WINDOWS
 default:
diff --git a/src/test/run-pass/sse2.rs b/src/test/run-pass/sse2.rs
new file mode 100644
index 00000000000..78d91b2f312
--- /dev/null
+++ b/src/test/run-pass/sse2.rs
@@ -0,0 +1,18 @@
+// Copyright 2012-2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(cfg_target_feature)]
+
+pub fn main() {
+    if cfg!(any(target_arch = "x86", target_arch = "x86_64")) {
+        assert!(cfg!(target_feature = "sse2"),
+            "SSE2 was not detected as available on an x86 platform");
+    }
+}