Skip to content

Commit

Permalink
Auto merge of #45367 - alexcrichton:simd-llvm-changes, r=eddyb
Browse files Browse the repository at this point in the history
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/stdarch#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/stdarch#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!
  • Loading branch information
bors committed Nov 22, 2017
2 parents 1dc0b57 + 74dd1c2 commit 7eb90e4
Show file tree
Hide file tree
Showing 10 changed files with 122 additions and 4 deletions.
5 changes: 5 additions & 0 deletions src/libcore/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
1 change: 1 addition & 0 deletions src/librustc_llvm/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
23 changes: 23 additions & 0 deletions src/librustc_trans/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
4 changes: 2 additions & 2 deletions src/librustc_trans/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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!
```
"##,

Expand Down
16 changes: 16 additions & 0 deletions src/librustc_trans/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
4 changes: 4 additions & 0 deletions src/librustc_trans/type_.rs
Original file line number Diff line number Diff line change
Expand Up @@ -286,4 +286,8 @@ impl Type {
Type::i8(ccx)
}
}

pub fn x86_mmx(ccx: &CrateContext) -> Type {
ty!(llvm::LLVMX86MMXTypeInContext(ccx.llcx()))
}
}
19 changes: 17 additions & 2 deletions src/librustc_trans/type_of.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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, &[
Expand Down
4 changes: 4 additions & 0 deletions src/librustc_typeck/check/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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: `{}`",
Expand Down
23 changes: 23 additions & 0 deletions src/test/codegen/nontemporal.rs
Original file line number Diff line number Diff line change
@@ -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);
}
}
27 changes: 27 additions & 0 deletions src/test/codegen/x86_mmx.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// 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(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
}

0 comments on commit 7eb90e4

Please sign in to comment.