diff options
| author | Alex Crichton <alex@alexcrichton.com> | 2017-10-18 10:30:29 -0700 |
|---|---|---|
| committer | Alex Crichton <alex@alexcrichton.com> | 2017-11-25 11:03:13 -0800 |
| commit | fe53a8106dfb54b5fe04d2ce7e8ee6472b0d5b16 (patch) | |
| tree | da4d366dc1d68821db4af9ad3213c7481e7064dd /src/librustc_trans | |
| parent | cc6b88ccb2fd10c2ad04a30ba648a1e9abf7ba4b (diff) | |
| download | rust-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.rs | 23 | ||||
| -rw-r--r-- | src/librustc_trans/diagnostics.rs | 4 | ||||
| -rw-r--r-- | src/librustc_trans/intrinsic.rs | 16 | ||||
| -rw-r--r-- | src/librustc_trans/type_.rs | 4 | ||||
| -rw-r--r-- | src/librustc_trans/type_of.rs | 19 |
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, &[ |
