auto merge of #21233 : huonw/rust/simd-size, r=Aatch

This stops the compiler ICEing on the use of SIMD types in FFI signatures. It emits correct code for LLVM intrinsics, but I am quite unsure about the ABI handling in general so I've added a new feature gate `simd_ffi` to try to ensure people don't use it without realising there's a non-trivial risk of codegen brokenness.

Closes #20043.
This commit is contained in:
bors 2015-01-17 10:58:43 +00:00
commit 89c4e3792d
13 changed files with 359 additions and 114 deletions

View File

@ -304,7 +304,7 @@ pub enum RealPredicate {
// The LLVM TypeKind type - must stay in sync with the def of
// LLVMTypeKind in llvm/include/llvm-c/Core.h
#[derive(Copy, PartialEq)]
#[derive(Copy, PartialEq, Show)]
#[repr(C)]
pub enum TypeKind {
Void = 0,

View File

@ -835,26 +835,24 @@ pub fn cast_shift_rhs<F, G>(op: ast::BinOp,
G: FnOnce(ValueRef, Type) -> ValueRef,
{
// Shifts may have any size int on the rhs
unsafe {
if ast_util::is_shift_binop(op) {
let mut rhs_llty = val_ty(rhs);
let mut lhs_llty = val_ty(lhs);
if rhs_llty.kind() == Vector { rhs_llty = rhs_llty.element_type() }
if lhs_llty.kind() == Vector { lhs_llty = lhs_llty.element_type() }
let rhs_sz = llvm::LLVMGetIntTypeWidth(rhs_llty.to_ref());
let lhs_sz = llvm::LLVMGetIntTypeWidth(lhs_llty.to_ref());
if lhs_sz < rhs_sz {
trunc(rhs, lhs_llty)
} else if lhs_sz > rhs_sz {
// FIXME (#1877: If shifting by negative
// values becomes not undefined then this is wrong.
zext(rhs, lhs_llty)
} else {
rhs
}
if ast_util::is_shift_binop(op) {
let mut rhs_llty = val_ty(rhs);
let mut lhs_llty = val_ty(lhs);
if rhs_llty.kind() == Vector { rhs_llty = rhs_llty.element_type() }
if lhs_llty.kind() == Vector { lhs_llty = lhs_llty.element_type() }
let rhs_sz = rhs_llty.int_width();
let lhs_sz = lhs_llty.int_width();
if lhs_sz < rhs_sz {
trunc(rhs, lhs_llty)
} else if lhs_sz > rhs_sz {
// FIXME (#1877: If shifting by negative
// values becomes not undefined then this is wrong.
zext(rhs, lhs_llty)
} else {
rhs
}
} else {
rhs
}
}

View File

@ -10,8 +10,7 @@
#![allow(non_upper_case_globals)]
use llvm;
use llvm::{Integer, Pointer, Float, Double, Struct, Array};
use llvm::{Integer, Pointer, Float, Double, Struct, Array, Vector};
use llvm::{StructRetAttribute, ZExtAttribute};
use trans::cabi::{FnType, ArgType};
use trans::context::CrateContext;
@ -30,11 +29,7 @@ fn align(off: uint, ty: Type) -> uint {
fn ty_align(ty: Type) -> uint {
match ty.kind() {
Integer => {
unsafe {
((llvm::LLVMGetIntTypeWidth(ty.to_ref()) as uint) + 7) / 8
}
}
Integer => ((ty.int_width() as uint) + 7) / 8,
Pointer => 8,
Float => 4,
Double => 8,
@ -50,17 +45,18 @@ fn ty_align(ty: Type) -> uint {
let elt = ty.element_type();
ty_align(elt)
}
Vector => {
let len = ty.vector_length();
let elt = ty.element_type();
ty_align(elt) * len
}
_ => panic!("ty_align: unhandled type")
}
}
fn ty_size(ty: Type) -> uint {
match ty.kind() {
Integer => {
unsafe {
((llvm::LLVMGetIntTypeWidth(ty.to_ref()) as uint) + 7) / 8
}
}
Integer => ((ty.int_width() as uint) + 7) / 8,
Pointer => 8,
Float => 4,
Double => 8,
@ -80,6 +76,12 @@ fn ty_size(ty: Type) -> uint {
let eltsz = ty_size(elt);
len * eltsz
}
Vector => {
let len = ty.vector_length();
let elt = ty.element_type();
let eltsz = ty_size(elt);
len * eltsz
}
_ => panic!("ty_size: unhandled type")
}
}
@ -137,7 +139,8 @@ fn is_reg_ty(ty: Type) -> bool {
Integer
| Pointer
| Float
| Double => true,
| Double
| Vector => true,
_ => false
}
}

View File

@ -10,8 +10,7 @@
#![allow(non_upper_case_globals)]
use llvm;
use llvm::{Integer, Pointer, Float, Double, Struct, Array};
use llvm::{Integer, Pointer, Float, Double, Struct, Array, Vector};
use llvm::{StructRetAttribute, ZExtAttribute};
use trans::cabi::{FnType, ArgType};
use trans::context::CrateContext;
@ -37,11 +36,7 @@ fn align(off: uint, ty: Type, align_fn: TyAlignFn) -> uint {
fn general_ty_align(ty: Type) -> uint {
match ty.kind() {
Integer => {
unsafe {
((llvm::LLVMGetIntTypeWidth(ty.to_ref()) as uint) + 7) / 8
}
}
Integer => ((ty.int_width() as uint) + 7) / 8,
Pointer => 4,
Float => 4,
Double => 8,
@ -57,6 +52,11 @@ fn general_ty_align(ty: Type) -> uint {
let elt = ty.element_type();
general_ty_align(elt)
}
Vector => {
let len = ty.vector_length();
let elt = ty.element_type();
general_ty_align(elt) * len
}
_ => panic!("ty_align: unhandled type")
}
}
@ -70,11 +70,7 @@ fn general_ty_align(ty: Type) -> uint {
// /iPhoneOSABIReference/Articles/ARMv6FunctionCallingConventions.html
fn ios_ty_align(ty: Type) -> uint {
match ty.kind() {
Integer => {
unsafe {
cmp::min(4, ((llvm::LLVMGetIntTypeWidth(ty.to_ref()) as uint) + 7) / 8)
}
}
Integer => cmp::min(4, ((ty.int_width() as uint) + 7) / 8),
Pointer => 4,
Float => 4,
Double => 4,
@ -90,17 +86,18 @@ fn ios_ty_align(ty: Type) -> uint {
let elt = ty.element_type();
ios_ty_align(elt)
}
Vector => {
let len = ty.vector_length();
let elt = ty.element_type();
ios_ty_align(elt) * len
}
_ => panic!("ty_align: unhandled type")
}
}
fn ty_size(ty: Type, align_fn: TyAlignFn) -> uint {
match ty.kind() {
Integer => {
unsafe {
((llvm::LLVMGetIntTypeWidth(ty.to_ref()) as uint) + 7) / 8
}
}
Integer => ((ty.int_width() as uint) + 7) / 8,
Pointer => 4,
Float => 4,
Double => 8,
@ -123,6 +120,12 @@ fn ty_size(ty: Type, align_fn: TyAlignFn) -> uint {
let eltsz = ty_size(elt, align_fn);
len * eltsz
}
Vector => {
let len = ty.vector_length();
let elt = ty.element_type();
let eltsz = ty_size(elt, align_fn);
len * eltsz
}
_ => panic!("ty_size: unhandled type")
}
}
@ -166,7 +169,8 @@ fn is_reg_ty(ty: Type) -> bool {
Integer
| Pointer
| Float
| Double => true,
| Double
| Vector => true,
_ => false
}
}

View File

@ -13,7 +13,7 @@
use libc::c_uint;
use std::cmp;
use llvm;
use llvm::{Integer, Pointer, Float, Double, Struct, Array};
use llvm::{Integer, Pointer, Float, Double, Struct, Array, Vector};
use llvm::{StructRetAttribute, ZExtAttribute};
use trans::cabi::{ArgType, FnType};
use trans::context::CrateContext;
@ -30,11 +30,7 @@ fn align(off: uint, ty: Type) -> uint {
fn ty_align(ty: Type) -> uint {
match ty.kind() {
Integer => {
unsafe {
((llvm::LLVMGetIntTypeWidth(ty.to_ref()) as uint) + 7) / 8
}
}
Integer => ((ty.int_width() as uint) + 7) / 8,
Pointer => 4,
Float => 4,
Double => 8,
@ -50,17 +46,18 @@ fn ty_align(ty: Type) -> uint {
let elt = ty.element_type();
ty_align(elt)
}
_ => panic!("ty_size: unhandled type")
Vector => {
let len = ty.vector_length();
let elt = ty.element_type();
ty_align(elt) * len
}
_ => panic!("ty_align: unhandled type")
}
}
fn ty_size(ty: Type) -> uint {
match ty.kind() {
Integer => {
unsafe {
((llvm::LLVMGetIntTypeWidth(ty.to_ref()) as uint) + 7) / 8
}
}
Integer => ((ty.int_width() as uint) + 7) / 8,
Pointer => 4,
Float => 4,
Double => 8,
@ -80,6 +77,12 @@ fn ty_size(ty: Type) -> uint {
let eltsz = ty_size(elt);
len * eltsz
}
Vector => {
let len = ty.vector_length();
let elt = ty.element_type();
let eltsz = ty_size(elt);
len * eltsz
}
_ => panic!("ty_size: unhandled type")
}
}
@ -120,7 +123,8 @@ fn is_reg_ty(ty: Type) -> bool {
Integer
| Pointer
| Float
| Double => true,
| Double
| Vector => true,
_ => false
};
}

View File

@ -14,9 +14,8 @@
#![allow(non_upper_case_globals)]
use self::RegClass::*;
use llvm;
use llvm::{Integer, Pointer, Float, Double};
use llvm::{Struct, Array, Attribute};
use llvm::{Struct, Array, Attribute, Vector};
use llvm::{StructRetAttribute, ByValAttribute, ZExtAttribute};
use trans::cabi::{ArgType, FnType};
use trans::context::CrateContext;
@ -33,7 +32,8 @@ enum RegClass {
SSEFv,
SSEDs,
SSEDv,
SSEInt,
SSEInt(/* bitwidth */ u64),
/// Data that can appear in the upper half of an SSE register.
SSEUp,
X87,
X87Up,
@ -57,7 +57,7 @@ impl TypeMethods for Type {
impl RegClass {
fn is_sse(&self) -> bool {
match *self {
SSEFs | SSEFv | SSEDs | SSEDv => true,
SSEFs | SSEFv | SSEDs | SSEDv | SSEInt(_) => true,
_ => false
}
}
@ -93,11 +93,7 @@ fn classify_ty(ty: Type) -> Vec<RegClass> {
fn ty_align(ty: Type) -> uint {
match ty.kind() {
Integer => {
unsafe {
((llvm::LLVMGetIntTypeWidth(ty.to_ref()) as uint) + 7) / 8
}
}
Integer => ((ty.int_width() as uint) + 7) / 8,
Pointer => 8,
Float => 4,
Double => 8,
@ -113,17 +109,18 @@ fn classify_ty(ty: Type) -> Vec<RegClass> {
let elt = ty.element_type();
ty_align(elt)
}
_ => panic!("ty_size: unhandled type")
Vector => {
let len = ty.vector_length();
let elt = ty.element_type();
ty_align(elt) * len
}
_ => panic!("ty_align: unhandled type")
}
}
fn ty_size(ty: Type) -> uint {
match ty.kind() {
Integer => {
unsafe {
((llvm::LLVMGetIntTypeWidth(ty.to_ref()) as uint) + 7) / 8
}
}
Integer => (ty.int_width() as uint + 7) / 8,
Pointer => 8,
Float => 4,
Double => 8,
@ -142,6 +139,13 @@ fn classify_ty(ty: Type) -> Vec<RegClass> {
let eltsz = ty_size(elt);
len * eltsz
}
Vector => {
let len = ty.vector_length();
let elt = ty.element_type();
let eltsz = ty_size(elt);
len * eltsz
}
_ => panic!("ty_size: unhandled type")
}
}
@ -155,26 +159,34 @@ fn classify_ty(ty: Type) -> Vec<RegClass> {
fn unify(cls: &mut [RegClass],
i: uint,
newv: RegClass) {
if cls[i] == newv {
return;
} else if cls[i] == NoClass {
cls[i] = newv;
} else if newv == NoClass {
return;
} else if cls[i] == Memory || newv == Memory {
cls[i] = Memory;
} else if cls[i] == Int || newv == Int {
cls[i] = Int;
} else if cls[i] == X87 ||
cls[i] == X87Up ||
cls[i] == ComplexX87 ||
newv == X87 ||
newv == X87Up ||
newv == ComplexX87 {
cls[i] = Memory;
} else {
cls[i] = newv;
}
if cls[i] == newv { return }
let to_write = match (cls[i], newv) {
(NoClass, _) => newv,
(_, NoClass) => return,
(Memory, _) |
(_, Memory) => Memory,
(Int, _) |
(_, Int) => Int,
(X87, _) |
(X87Up, _) |
(ComplexX87, _) |
(_, X87) |
(_, X87Up) |
(_, ComplexX87) => Memory,
(SSEFv, SSEUp) |
(SSEFs, SSEUp) |
(SSEDv, SSEUp) |
(SSEDs, SSEUp) |
(SSEInt(_), SSEUp) => return,
(_, _) => newv
};
cls[i] = to_write;
}
fn classify_struct(tys: &[Type],
@ -237,6 +249,27 @@ fn classify_ty(ty: Type) -> Vec<RegClass> {
i += 1u;
}
}
Vector => {
let len = ty.vector_length();
let elt = ty.element_type();
let eltsz = ty_size(elt);
let mut reg = match elt.kind() {
Integer => SSEInt(elt.int_width()),
Float => SSEFv,
Double => SSEDv,
_ => panic!("classify: unhandled vector element type")
};
let mut i = 0u;
while i < len {
unify(cls, ix + (off + i * eltsz) / 8, reg);
// everything after the first one is the upper
// half of a register.
reg = SSEUp;
i += 1u;
}
}
_ => panic!("classify: unhandled type")
}
}
@ -245,7 +278,7 @@ fn classify_ty(ty: Type) -> Vec<RegClass> {
let mut i = 0u;
let ty_kind = ty.kind();
let e = cls.len();
if cls.len() > 2u && (ty_kind == Struct || ty_kind == Array) {
if cls.len() > 2u && (ty_kind == Struct || ty_kind == Array || ty_kind == Vector) {
if cls[i].is_sse() {
i += 1u;
while i < e {
@ -317,9 +350,19 @@ fn llreg_ty(ccx: &CrateContext, cls: &[RegClass]) -> Type {
Int => {
tys.push(Type::i64(ccx));
}
SSEFv => {
SSEFv | SSEDv | SSEInt(_) => {
let (elts_per_word, elt_ty) = match cls[i] {
SSEFv => (2, Type::f32(ccx)),
SSEDv => (1, Type::f64(ccx)),
SSEInt(bits) => {
assert!(bits == 8 || bits == 16 || bits == 32 || bits == 64,
"llreg_ty: unsupported SSEInt width {}", bits);
(64 / bits, Type::ix(ccx, bits))
}
_ => unreachable!(),
};
let vec_len = llvec_len(&cls[(i + 1u)..]);
let vec_ty = Type::vector(&Type::f32(ccx), (vec_len * 2u) as u64);
let vec_ty = Type::vector(&elt_ty, vec_len as u64 * elts_per_word);
tys.push(vec_ty);
i += vec_len;
continue;
@ -334,7 +377,12 @@ fn llreg_ty(ccx: &CrateContext, cls: &[RegClass]) -> Type {
}
i += 1u;
}
return Type::struct_(ccx, tys.as_slice(), false);
if tys.len() == 1 && tys[0].kind() == Vector {
// if the type contains only a vector, pass it as that vector.
tys[0]
} else {
Type::struct_(ccx, tys.as_slice(), false)
}
}
pub fn compute_abi_info(ccx: &CrateContext,

View File

@ -1905,18 +1905,16 @@ fn int_cast(bcx: Block,
signed: bool)
-> ValueRef {
let _icx = push_ctxt("int_cast");
unsafe {
let srcsz = llvm::LLVMGetIntTypeWidth(llsrctype.to_ref());
let dstsz = llvm::LLVMGetIntTypeWidth(lldsttype.to_ref());
return if dstsz == srcsz {
BitCast(bcx, llsrc, lldsttype)
} else if srcsz > dstsz {
TruncOrBitCast(bcx, llsrc, lldsttype)
} else if signed {
SExtOrBitCast(bcx, llsrc, lldsttype)
} else {
ZExtOrBitCast(bcx, llsrc, lldsttype)
};
let srcsz = llsrctype.int_width();
let dstsz = lldsttype.int_width();
return if dstsz == srcsz {
BitCast(bcx, llsrc, lldsttype)
} else if srcsz > dstsz {
TruncOrBitCast(bcx, llsrc, lldsttype)
} else if signed {
SExtOrBitCast(bcx, llsrc, lldsttype)
} else {
ZExtOrBitCast(bcx, llsrc, lldsttype)
}
}

View File

@ -36,6 +36,7 @@ use syntax::parse::token::{InternedString, special_idents};
use syntax::parse::token;
use syntax::{ast};
use syntax::{attr, ast_map};
use syntax::print::pprust;
use util::ppaux::Repr;
///////////////////////////////////////////////////////////////////////////
@ -426,16 +427,47 @@ pub fn trans_native_call<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
return bcx;
}
// feature gate SIMD types in FFI, since I (huonw) am not sure the
// ABIs are handled at all correctly.
fn gate_simd_ffi(tcx: &ty::ctxt, decl: &ast::FnDecl, ty: &ty::BareFnTy) {
if !tcx.sess.features.borrow().simd_ffi {
let check = |&: ast_ty: &ast::Ty, ty: ty::Ty| {
if ty::type_is_simd(tcx, ty) {
tcx.sess.span_err(ast_ty.span,
&format!("use of SIMD type `{}` in FFI is highly experimental and \
may result in invalid code",
pprust::ty_to_string(ast_ty))[]);
tcx.sess.span_help(ast_ty.span,
"add #![feature(simd_ffi)] to the crate attributes to enable");
}
};
let sig = &ty.sig.0;
for (input, ty) in decl.inputs.iter().zip(sig.inputs.iter()) {
check(&*input.ty, *ty)
}
match decl.output {
ast::NoReturn(_) => {}
ast::Return(ref ty) => check(&**ty, sig.output.unwrap())
}
}
}
pub fn trans_foreign_mod(ccx: &CrateContext, foreign_mod: &ast::ForeignMod) {
let _icx = push_ctxt("foreign::trans_foreign_mod");
for foreign_item in foreign_mod.items.iter() {
let lname = link_name(&**foreign_item);
if let ast::ForeignItemFn(..) = foreign_item.node {
if let ast::ForeignItemFn(ref decl, _) = foreign_item.node {
match foreign_mod.abi {
Rust | RustIntrinsic => {}
abi => {
let ty = ty::node_id_to_type(ccx.tcx(), foreign_item.id);
match ty.sty {
ty::ty_bare_fn(_, bft) => gate_simd_ffi(ccx.tcx(), &**decl, bft),
_ => ccx.tcx().sess.span_bug(foreign_item.span,
"foreign fn's sty isn't a bare_fn_ty?")
}
register_foreign_item_fn(ccx, abi, ty,
&lname.get()[]);
// Unlike for other items, we shouldn't call

View File

@ -284,6 +284,13 @@ impl Type {
}
}
/// Return the number of elements in `self` if it is a LLVM vector type.
pub fn vector_length(&self) -> uint {
unsafe {
llvm::LLVMGetVectorSize(self.to_ref()) as uint
}
}
pub fn array_length(&self) -> uint {
unsafe {
llvm::LLVMGetArrayLength(self.to_ref()) as uint
@ -326,6 +333,13 @@ impl Type {
_ => panic!("llvm_float_width called on a non-float type")
}
}
/// Retrieve the bit width of the integer type `self`.
pub fn int_width(&self) -> u64 {
unsafe {
llvm::LLVMGetIntTypeWidth(self.to_ref()) as u64
}
}
}

View File

@ -72,6 +72,7 @@ static KNOWN_FEATURES: &'static [(&'static str, Status)] = &[
("slicing_syntax", Active),
("box_syntax", Active),
("on_unimplemented", Active),
("simd_ffi", Active),
("if_let", Accepted),
("while_let", Accepted),
@ -128,6 +129,7 @@ pub struct Features {
pub visible_private_types: bool,
pub quote: bool,
pub old_orphan_check: bool,
pub simd_ffi: bool,
}
impl Features {
@ -139,6 +141,7 @@ impl Features {
visible_private_types: false,
quote: false,
old_orphan_check: false,
simd_ffi: false,
}
}
}
@ -524,6 +527,7 @@ fn check_crate_inner<F>(cm: &CodeMap, span_handler: &SpanHandler, krate: &ast::C
visible_private_types: cx.has_feature("visible_private_types"),
quote: cx.has_feature("quote"),
old_orphan_check: cx.has_feature("old_orphan_check"),
simd_ffi: cx.has_feature("simd_ffi"),
},
unknown_features)
}

View File

@ -0,0 +1,26 @@
// 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.
#![feature(simd)]
#![allow(dead_code)]
use std::simd::f32x4;
#[simd] #[derive(Copy)] #[repr(C)] struct LocalSimd(u8, u8);
extern {
fn foo() -> f32x4; //~ ERROR use of SIMD type
fn bar(x: f32x4); //~ ERROR use of SIMD type
fn baz() -> LocalSimd; //~ ERROR use of SIMD type
fn qux(x: LocalSimd); //~ ERROR use of SIMD type
}
fn main() {}

View File

@ -0,0 +1,33 @@
-include ../tools.mk
# construct a fairly exhaustive list of platforms that we
# support. These ones don't follow a pattern
TARGETS=arm-linux-androideabi arm-unknown-linux-gnueabihf arm-unknown-linux-gnueabi
# these ones do, each OS lists the architectures it supports
LINUX=aarch64 i686 x86_64 mips mipsel
WINDOWS=i686 x86_64
# fails with: failed to get iphonesimulator SDK path: no such file or directory
#IOS=i386 aarch64 armv7
DARWIN=i686 x86_64
$(foreach arch,$(LINUX),$(eval TARGETS += $(arch)-unknown-linux-gnu))
$(foreach arch,$(WINDOWS),$(eval TARGETS += $(arch)-pc-windows-gnu))
#$(foreach arch,$(IOS),$(eval TARGETS += $(arch)-apple-ios))
$(foreach arch,$(DARWIN),$(eval TARGETS += $(arch)-apple-darwin))
all: $(TARGETS)
define MK_TARGETS
# compile the rust file to the given target, but only to asm and IR
# form, to avoid having to have an appropriate linker.
#
# we need some features because the integer SIMD instructions are not
# enabled by-default for i686 and ARM; these features will be invalid
# on some platforms, but LLVM just prints a warning so that's fine for
# now.
$(1): simd.rs
$$(RUSTC) --target=$(1) --emit=llvm-ir,asm simd.rs -C target-feature='+neon,+sse2'
endef
$(foreach targetxxx,$(TARGETS),$(eval $(call MK_TARGETS,$(targetxxx))))

View File

@ -0,0 +1,81 @@
// 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.
// ensures that public symbols are not removed completely
#![crate_type = "lib"]
// we can compile to a variety of platforms, because we don't need
// cross-compiled standard libraries.
#![no_std]
#![feature(simd, simd_ffi, link_llvm_intrinsics, lang_items)]
#[repr(C)]
#[derive(Copy)]
#[simd]
pub struct f32x4(f32, f32, f32, f32);
extern {
#[link_name = "llvm.sqrt.v4f32"]
fn vsqrt(x: f32x4) -> f32x4;
}
pub fn foo(x: f32x4) -> f32x4 {
unsafe {vsqrt(x)}
}
#[repr(C)]
#[derive(Copy)]
#[simd]
pub struct i32x4(i32, i32, i32, i32);
extern {
// _mm_sll_epi32
#[cfg(any(target_arch = "x86",
target_arch = "x86-64"))]
#[link_name = "llvm.x86.sse2.psll.d"]
fn integer(a: i32x4, b: i32x4) -> i32x4;
// vmaxq_s32
#[cfg(any(target_arch = "arm"))]
#[link_name = "llvm.arm.neon.vmaxs.v4i32"]
fn integer(a: i32x4, b: i32x4) -> i32x4;
// vmaxq_s32
#[cfg(any(target_arch = "aarch64"))]
#[link_name = "llvm.aarch64.neon.maxs.v4i32"]
fn integer(a: i32x4, b: i32x4) -> i32x4;
// just some substitute foreign symbol, not an LLVM intrinsic; so
// we still get type checking, but not as detailed as (ab)using
// LLVM.
#[cfg(not(any(target_arch = "x86",
target_arch = "x86-64",
target_arch = "arm",
target_arch = "aarch64")))]
fn integer(a: i32x4, b: i32x4) -> i32x4;
}
pub fn bar(a: i32x4, b: i32x4) -> i32x4 {
unsafe {integer(a, b)}
}
#[lang = "sized"]
trait Sized {}
#[lang = "copy"]
trait Copy {}
mod std {
pub mod marker {
pub use Copy;
}
}