mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-22 14:55:26 +00:00
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`.
This commit is contained in:
commit
92e3fb3ebe
@ -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
|
||||
|
@ -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)) \
|
||||
$$<
|
||||
|
@ -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" }
|
||||
|
@ -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;
|
||||
|
@ -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) => {
|
||||
|
@ -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")
|
||||
}
|
||||
|
@ -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")
|
||||
|
@ -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,
|
||||
|
@ -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)) {
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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:
|
||||
|
18
src/test/run-pass/sse2.rs
Normal file
18
src/test/run-pass/sse2.rs
Normal file
@ -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");
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user