about summary refs log tree commit diff
path: root/src/librustc_trans
diff options
context:
space:
mode:
authorAlex Crichton <alex@alexcrichton.com>2017-10-18 10:30:29 -0700
committerAlex Crichton <alex@alexcrichton.com>2017-11-25 11:03:13 -0800
commitfe53a8106dfb54b5fe04d2ce7e8ee6472b0d5b16 (patch)
treeda4d366dc1d68821db4af9ad3213c7481e7064dd /src/librustc_trans
parentcc6b88ccb2fd10c2ad04a30ba648a1e9abf7ba4b (diff)
downloadrust-fe53a8106dfb54b5fe04d2ce7e8ee6472b0d5b16.tar.gz
rust-fe53a8106dfb54b5fe04d2ce7e8ee6472b0d5b16.zip
rustc: Add support for some more x86 SIMD ops
This commit adds compiler support for two basic operations needed for binding
SIMD on x86 platforms:

* First, a `nontemporal_store` intrinsic was added for the `_mm_stream_ps`, seen
  in rust-lang-nursery/stdsimd#114. This was relatively straightforward and is
  quite similar to the volatile store intrinsic.

* Next, and much more intrusively, a new type to the backend was added. The
  `x86_mmx` type is used in LLVM for a 64-bit vector register and is used in
  various intrinsics like `_mm_abs_pi8` as seen in rust-lang-nursery/stdsimd#74.
  This new type was added as a new layout option as well as having support added
  to the trans backend. The type is enabled with the `#[repr(x86_mmx)]`
  attribute which is intended to just be an implementation detail of SIMD in
  Rust.

I'm not 100% certain about how the `x86_mmx` type was added, so any extra eyes
or thoughts on that would be greatly appreciated!
Diffstat (limited to 'src/librustc_trans')
-rw-r--r--src/librustc_trans/builder.rs23
-rw-r--r--src/librustc_trans/diagnostics.rs4
-rw-r--r--src/librustc_trans/intrinsic.rs16
-rw-r--r--src/librustc_trans/type_.rs4
-rw-r--r--src/librustc_trans/type_of.rs19
5 files changed, 62 insertions, 4 deletions
diff --git a/src/librustc_trans/builder.rs b/src/librustc_trans/builder.rs
index 50e673bdbfd..e40311af595 100644
--- a/src/librustc_trans/builder.rs
+++ b/src/librustc_trans/builder.rs
@@ -612,6 +612,29 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         }
     }
 
+    pub fn nontemporal_store(&self, val: ValueRef, ptr: ValueRef) -> ValueRef {
+        debug!("Store {:?} -> {:?}", Value(val), Value(ptr));
+        assert!(!self.llbuilder.is_null());
+        self.count_insn("store.nontemporal");
+        let ptr = self.check_store(val, ptr);
+        unsafe {
+            let insn = llvm::LLVMBuildStore(self.llbuilder, val, ptr);
+
+            // According to LLVM [1] building a nontemporal store must *always*
+            // point to a metadata value of the integer 1. Who knew?
+            //
+            // [1]: http://llvm.org/docs/LangRef.html#store-instruction
+            let one = C_i32(self.ccx, 1);
+            let node = llvm::LLVMMDNodeInContext(self.ccx.llcx(),
+                                                 &one,
+                                                 1);
+            llvm::LLVMSetMetadata(insn,
+                                  llvm::MD_nontemporal as c_uint,
+                                  node);
+            insn
+        }
+    }
+
     pub fn gep(&self, ptr: ValueRef, indices: &[ValueRef]) -> ValueRef {
         self.count_insn("gep");
         unsafe {
diff --git a/src/librustc_trans/diagnostics.rs b/src/librustc_trans/diagnostics.rs
index 84858676891..8f5d836f56f 100644
--- a/src/librustc_trans/diagnostics.rs
+++ b/src/librustc_trans/diagnostics.rs
@@ -37,13 +37,13 @@ The generic type has to be a SIMD type. Example:
 
 #[repr(simd)]
 #[derive(Copy, Clone)]
-struct i32x1(i32);
+struct i32x2(i32, i32);
 
 extern "platform-intrinsic" {
     fn simd_add<T>(a: T, b: T) -> T;
 }
 
-unsafe { simd_add(i32x1(0), i32x1(1)); } // ok!
+unsafe { simd_add(i32x2(0, 0), i32x2(1, 2)); } // ok!
 ```
 "##,
 
diff --git a/src/librustc_trans/intrinsic.rs b/src/librustc_trans/intrinsic.rs
index adbb45f893b..997dd557353 100644
--- a/src/librustc_trans/intrinsic.rs
+++ b/src/librustc_trans/intrinsic.rs
@@ -540,6 +540,22 @@ pub fn trans_intrinsic_call<'a, 'tcx>(bcx: &Builder<'a, 'tcx>,
             }
         }
 
+        "nontemporal_store" => {
+            let tp_ty = substs.type_at(0);
+            let dst = args[0].deref(bcx.ccx);
+            let val = if let OperandValue::Ref(ptr, align) = args[1].val {
+                bcx.load(ptr, align.non_abi())
+            } else {
+                from_immediate(bcx, args[1].immediate())
+            };
+            let ptr = bcx.pointercast(dst.llval, val_ty(val).ptr_to());
+            let store = bcx.nontemporal_store(val, ptr);
+            unsafe {
+                llvm::LLVMSetAlignment(store, ccx.align_of(tp_ty).abi() as u32);
+            }
+            return
+        }
+
         _ => {
             let intr = match Intrinsic::find(&name) {
                 Some(intr) => intr,
diff --git a/src/librustc_trans/type_.rs b/src/librustc_trans/type_.rs
index 02224858b46..1775e532849 100644
--- a/src/librustc_trans/type_.rs
+++ b/src/librustc_trans/type_.rs
@@ -286,4 +286,8 @@ impl Type {
             Type::i8(ccx)
         }
     }
+
+    pub fn x86_mmx(ccx: &CrateContext) -> Type {
+        ty!(llvm::LLVMX86MMXTypeInContext(ccx.llcx()))
+    }
 }
diff --git a/src/librustc_trans/type_of.rs b/src/librustc_trans/type_of.rs
index 9b32c825117..42e45b3a36f 100644
--- a/src/librustc_trans/type_of.rs
+++ b/src/librustc_trans/type_of.rs
@@ -26,8 +26,23 @@ fn uncached_llvm_type<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
     match layout.abi {
         layout::Abi::Scalar(_) => bug!("handled elsewhere"),
         layout::Abi::Vector => {
-            return Type::vector(&layout.field(ccx, 0).llvm_type(ccx),
-                                layout.fields.count() as u64);
+            // LLVM has a separate type for 64-bit SIMD vectors on X86 called
+            // `x86_mmx` which is needed for some SIMD operations. As a bit of a
+            // hack (all SIMD definitions are super unstable anyway) we
+            // recognize any one-element SIMD vector as "this should be an
+            // x86_mmx" type. In general there shouldn't be a need for other
+            // one-element SIMD vectors, so it's assumed this won't clash with
+            // much else.
+            let use_x86_mmx = layout.fields.count() == 1 &&
+                layout.size.bits() == 64 &&
+                (ccx.sess().target.target.arch == "x86" ||
+                 ccx.sess().target.target.arch == "x86_64");
+            if use_x86_mmx {
+                return Type::x86_mmx(ccx)
+            } else {
+                return Type::vector(&layout.field(ccx, 0).llvm_type(ccx),
+                                    layout.fields.count() as u64);
+            }
         }
         layout::Abi::ScalarPair(..) => {
             return Type::struct_(ccx, &[