Auto merge of #45367 - alexcrichton:simd-llvm-changes, r=eddyb
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!
This commit is contained in:
commit
128b40fadc
10 changed files with 125 additions and 4 deletions
|
@ -1387,4 +1387,9 @@ extern "rust-intrinsic" {
|
|||
/// # } }
|
||||
/// ```
|
||||
pub fn align_offset(ptr: *const (), align: usize) -> usize;
|
||||
|
||||
/// Emits a `!nontemporal` store according to LLVM (see their docs).
|
||||
/// Probably will never become stable.
|
||||
#[cfg(not(stage0))]
|
||||
pub fn nontemporal_store<T>(ptr: *mut T, val: T);
|
||||
}
|
||||
|
|
|
@ -587,6 +587,7 @@ extern "C" {
|
|||
|
||||
// Operations on other types
|
||||
pub fn LLVMVoidTypeInContext(C: ContextRef) -> TypeRef;
|
||||
pub fn LLVMX86MMXTypeInContext(C: ContextRef) -> TypeRef;
|
||||
pub fn LLVMRustMetadataTypeInContext(C: ContextRef) -> TypeRef;
|
||||
|
||||
// Operations on all values
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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!
|
||||
```
|
||||
"##,
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -286,4 +286,8 @@ impl Type {
|
|||
Type::i8(ccx)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn x86_mmx(ccx: &CrateContext) -> Type {
|
||||
ty!(llvm::LLVMX86MMXTypeInContext(ccx.llcx()))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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, &[
|
||||
|
|
|
@ -318,6 +318,10 @@ pub fn check_intrinsic_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
|||
(0, vec![ptr_ty, tcx.types.usize], tcx.types.usize)
|
||||
},
|
||||
|
||||
"nontemporal_store" => {
|
||||
(1, vec![ tcx.mk_mut_ptr(param(0)), param(0) ], tcx.mk_nil())
|
||||
}
|
||||
|
||||
ref other => {
|
||||
struct_span_err!(tcx.sess, it.span, E0093,
|
||||
"unrecognized intrinsic function: `{}`",
|
||||
|
|
23
src/test/codegen/nontemporal.rs
Normal file
23
src/test/codegen/nontemporal.rs
Normal file
|
@ -0,0 +1,23 @@
|
|||
// Copyright 2017 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.
|
||||
|
||||
// compile-flags: -O
|
||||
|
||||
#![feature(core_intrinsics)]
|
||||
#![crate_type = "lib"]
|
||||
|
||||
#[no_mangle]
|
||||
pub fn a(a: &mut u32, b: u32) {
|
||||
// CHECK-LABEL: define void @a
|
||||
// CHECK: store i32 %b, i32* %a, align 4, !nontemporal
|
||||
unsafe {
|
||||
std::intrinsics::nontemporal_store(a, b);
|
||||
}
|
||||
}
|
30
src/test/codegen/x86_mmx.rs
Normal file
30
src/test/codegen/x86_mmx.rs
Normal file
|
@ -0,0 +1,30 @@
|
|||
// Copyright 2017 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.
|
||||
|
||||
// ignore-arm
|
||||
// ignore-aarch64
|
||||
// ignore-emscripten
|
||||
// compile-flags: -O
|
||||
|
||||
#![feature(repr_simd)]
|
||||
#![crate_type="lib"]
|
||||
|
||||
#[repr(simd)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct i8x8(u64);
|
||||
|
||||
#[no_mangle]
|
||||
pub fn a(a: &mut i8x8, b: i8x8) -> i8x8 {
|
||||
// CHECK-LABEL: define x86_mmx @a(x86_mmx*{{.*}}, x86_mmx{{.*}})
|
||||
// CHECK: store x86_mmx %b, x86_mmx* %a
|
||||
// CHECK: ret x86_mmx %b
|
||||
*a = b;
|
||||
return b
|
||||
}
|
Loading…
Add table
Reference in a new issue