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 = 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 target_machine = create_target_machine(sess); + + let whitelist = match &*sess.target.target.arch { + "arm" => ARM_WHITELIST, + "x86" | "x86_64" => X86_WHITELIST, + _ => &[], + }; + 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"; + 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]))) + } } } - - -fn features_contain(sess: &Session, s: &str) -> bool { - sess.target.target.options.features.contains(s) || sess.opts.cg.target_feature.contains(s) -} - -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") -} 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 or the MIT license +// , 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"); + } +}