-
Notifications
You must be signed in to change notification settings - Fork 12.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Compute target_feature
from LLVM
#31709
Changes from all commits
64a35f9
c883463
92e24b9
358e41c
3fc5d27
f942c28
5879ee1
1ad8561
deaa2fe
ce99a5e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One of the purposes of a macro like this was to ensure that we had control over the names exported. We don't want to blanket export all features that LLVM itself exposes, and we also don't want to tie ourselves to exactly the names LLVM has. In other words this is here to ensure we can provide a stable interface over the possibly unstable interface of LLVM. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Filtering and renaming these features to a "rust" convention instead of the LLVM one can be done very easily with a map. The filtering makes sense to me, as rust might want to expose only a subset of the LLVM features through There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The purpose of this macro was that for the function/name pair the cfg directive name would be defined if the function returned true. This is the only place that does that. Basically, we need to filter what's coming out of LLVM with an explicit whitelist of what cfg directives are defined. We don't need to map names yet (as we just use LLVM names), but we may support that one day. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I added a whitelist of known LLVM features which is used to filter which ones are exposed through |
||
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) } { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It might be nice to have an assertion that the strings end in |
||
cfg.push(attr::mk_name_value_item_str(tf.clone(), intern(&feat[..feat.len()-1]))) | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks like this is safe to remove now; #21627 references an upstream fix which went into llvm 18 months ago llvm-mirror/llvm@ca07e25
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome! Would you be interested in prep'ing a separate PR for that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sure. I'll open a new PR if that removal doesn't happen here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah it's probably good to keep these separate, so let's split that change out of this PR.