about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAndrew Gallant <jamslam@gmail.com>2016-11-29 19:02:00 -0500
committerAndrew Gallant <jamslam@gmail.com>2016-11-29 20:32:14 -0500
commit80ef1dbf2d51d2f2fd039d98a9150d2614e775b0 (patch)
tree18be670959b4ae99ae1c707015a688e4b55d6b72
parent5de15be5ec9144f6701c8de606fdf83c6eefefef (diff)
downloadrust-80ef1dbf2d51d2f2fd039d98a9150d2614e775b0.tar.gz
rust-80ef1dbf2d51d2f2fd039d98a9150d2614e775b0.zip
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.
-rw-r--r--src/librustc_driver/target_features.rs3
-rw-r--r--src/librustc_llvm/lib.rs8
-rw-r--r--src/librustc_trans/attributes.rs30
-rw-r--r--src/libsyntax/feature_gate.rs7
-rw-r--r--src/test/compile-fail/gated-target_feature.rs13
5 files changed, 50 insertions, 11 deletions
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 ARM_WHITELIST: &'static [&'static str] = &["neon\0", "vfp2\0", "vfp3\0", "
 
 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 @@ impl LLVMRustResult {
 
 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 @@ declare_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 const BUILTIN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeG
                                  "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 <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.
+
+#[target_feature = "+sse2"]
+//~^ the `#[target_feature]` attribute is an experimental feature
+fn foo() {}