From 80ef1dbf2d51d2f2fd039d98a9150d2614e775b0 Mon Sep 17 00:00:00 2001 From: Andrew Gallant Date: Tue, 29 Nov 2016 19:02:00 -0500 Subject: [PATCH] Add new #[target_feature = "..."] attribute. This commit adds a new attribute that instructs the compiler to emit target specific code for a single function. For example, the following function is permitted to use instructions that are part of SSE 4.2: #[target_feature = "+sse4.2"] fn foo() { ... } In particular, use of this attribute does not require setting the -C target-feature or -C target-cpu options on rustc. This attribute does not have any protections built into it. For example, nothing stops one from calling the above `foo` function on hosts without SSE 4.2 support. Doing so may result in a SIGILL. This commit also expands the target feature whitelist to include lzcnt, popcnt and sse4a. Namely, lzcnt and popcnt have their own CPUID bits, but were introduced with SSE4. --- src/librustc_driver/target_features.rs | 3 +- src/librustc_llvm/lib.rs | 8 ++--- src/librustc_trans/attributes.rs | 30 +++++++++++++++---- src/libsyntax/feature_gate.rs | 7 +++++ src/test/compile-fail/gated-target_feature.rs | 13 ++++++++ 5 files changed, 50 insertions(+), 11 deletions(-) create mode 100644 src/test/compile-fail/gated-target_feature.rs diff --git a/src/librustc_driver/target_features.rs b/src/librustc_driver/target_features.rs index 876323d599e..124e7aafcc5 100644 --- a/src/librustc_driver/target_features.rs +++ b/src/librustc_driver/target_features.rs @@ -24,7 +24,8 @@ const X86_WHITELIST: &'static [&'static str] = &["avx\0", "avx2\0", "bmi\0", "bmi2\0", "sse\0", "sse2\0", "sse3\0", "sse4.1\0", "sse4.2\0", - "ssse3\0", "tbm\0"]; + "ssse3\0", "tbm\0", "lzcnt\0", "popcnt\0", + "sse4a\0"]; /// Add `target_feature = "..."` cfgs for a variety of platform /// specific features (SSE, NEON etc.). diff --git a/src/librustc_llvm/lib.rs b/src/librustc_llvm/lib.rs index c4ec418f224..5792ff7bd39 100644 --- a/src/librustc_llvm/lib.rs +++ b/src/librustc_llvm/lib.rs @@ -66,13 +66,13 @@ pub fn into_result(self) -> Result<(), ()> { pub fn AddFunctionAttrStringValue(llfn: ValueRef, idx: AttributePlace, - attr: &'static str, - value: &'static str) { + attr: &CStr, + value: &CStr) { unsafe { LLVMRustAddFunctionAttrStringValue(llfn, idx.as_uint(), - attr.as_ptr() as *const _, - value.as_ptr() as *const _) + attr.as_ptr(), + value.as_ptr()) } } diff --git a/src/librustc_trans/attributes.rs b/src/librustc_trans/attributes.rs index f1e90419a49..efdd1b736f0 100644 --- a/src/librustc_trans/attributes.rs +++ b/src/librustc_trans/attributes.rs @@ -9,6 +9,8 @@ // except according to those terms. //! Set and unset common attributes on LLVM values. +use std::ffi::{CStr, CString}; + use llvm::{self, Attribute, ValueRef}; use llvm::AttributePlace::Function; pub use syntax::attr::InlineAttr; @@ -61,10 +63,8 @@ pub fn set_frame_pointer_elimination(ccx: &CrateContext, llfn: ValueRef) { // parameter. if ccx.sess().must_not_eliminate_frame_pointers() { llvm::AddFunctionAttrStringValue( - llfn, - llvm::AttributePlace::Function, - "no-frame-pointer-elim\0", - "true\0") + llfn, llvm::AttributePlace::Function, + cstr("no-frame-pointer-elim\0"), cstr("true\0")); } } @@ -75,9 +75,17 @@ pub fn from_fn_attrs(ccx: &CrateContext, attrs: &[ast::Attribute], llfn: ValueRe inline(llfn, find_inline_attr(Some(ccx.sess().diagnostic()), attrs)); set_frame_pointer_elimination(ccx, llfn); - + let mut target_features = vec![]; for attr in attrs { - if attr.check_name("cold") { + if attr.check_name("target_feature") { + if let Some(val) = attr.value_str() { + for feat in val.as_str().split(",").map(|f| f.trim()) { + if !feat.is_empty() && !feat.contains('\0') { + target_features.push(feat.to_string()); + } + } + } + } else if attr.check_name("cold") { Attribute::Cold.apply_llfn(Function, llfn); } else if attr.check_name("naked") { naked(llfn, true); @@ -88,4 +96,14 @@ pub fn from_fn_attrs(ccx: &CrateContext, attrs: &[ast::Attribute], llfn: ValueRe unwind(llfn, true); } } + if !target_features.is_empty() { + let val = CString::new(target_features.join(",")).unwrap(); + llvm::AddFunctionAttrStringValue( + llfn, llvm::AttributePlace::Function, + cstr("target-features\0"), &val); + } +} + +fn cstr(s: &'static str) -> &CStr { + CStr::from_bytes_with_nul(s.as_bytes()).expect("null-terminated string") } diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index aa6a29b78b0..9313a268129 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -316,6 +316,9 @@ pub fn new() -> Features { // Allows `break {expr}` with a value inside `loop`s. (active, loop_break_value, "1.14.0", Some(37339)), + + // Allows #[target_feature(...)] + (active, target_feature, "1.15.0", None), ); declare_features! ( @@ -664,6 +667,10 @@ pub fn deprecated_attributes() -> Vec<&'static (&'static str, AttributeType, Att "the `#[naked]` attribute \ is an experimental feature", cfg_fn!(naked_functions))), + ("target_feature", Whitelisted, Gated( + Stability::Unstable, "target_feature", + "the `#[target_feature]` attribute is an experimental feature", + cfg_fn!(target_feature))), ("export_name", Whitelisted, Ungated), ("inline", Whitelisted, Ungated), ("link", Whitelisted, Ungated), diff --git a/src/test/compile-fail/gated-target_feature.rs b/src/test/compile-fail/gated-target_feature.rs new file mode 100644 index 00000000000..da2e41a0f5e --- /dev/null +++ b/src/test/compile-fail/gated-target_feature.rs @@ -0,0 +1,13 @@ +// Copyright 2015 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. + +#[target_feature = "+sse2"] +//~^ the `#[target_feature]` attribute is an experimental feature +fn foo() {} -- GitLab