about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2015-01-17 10:58:43 +0000
committerbors <bors@rust-lang.org>2015-01-17 10:58:43 +0000
commit89c4e3792ddc5b45706ea0e919806a248f7a87c3 (patch)
tree31af99a5d3e3fe2cbc72ea7a7a168a796327c322 /src
parent3e6eaeb69ff729096253670aba752c03225113bc (diff)
parentc8e0e9549d4327c54d9eb5fd0de5e23312c34fe9 (diff)
downloadrust-89c4e3792ddc5b45706ea0e919806a248f7a87c3.tar.gz
rust-89c4e3792ddc5b45706ea0e919806a248f7a87c3.zip
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.
Diffstat (limited to 'src')
-rw-r--r--src/librustc_llvm/lib.rs2
-rw-r--r--src/librustc_trans/trans/base.rs32
-rw-r--r--src/librustc_trans/trans/cabi_aarch64.rs29
-rw-r--r--src/librustc_trans/trans/cabi_arm.rs40
-rw-r--r--src/librustc_trans/trans/cabi_mips.rs30
-rw-r--r--src/librustc_trans/trans/cabi_x86_64.rs126
-rw-r--r--src/librustc_trans/trans/expr.rs22
-rw-r--r--src/librustc_trans/trans/foreign.rs34
-rw-r--r--src/librustc_trans/trans/type_.rs14
-rw-r--r--src/libsyntax/feature_gate.rs4
-rw-r--r--src/test/compile-fail/feature-gate-simd-ffi.rs26
-rw-r--r--src/test/run-make/simd-ffi/Makefile33
-rwxr-xr-xsrc/test/run-make/simd-ffi/simd.rs81
13 files changed, 359 insertions, 114 deletions
diff --git a/src/librustc_llvm/lib.rs b/src/librustc_llvm/lib.rs
index 3edb1ec48a0..0d1836e0144 100644
--- a/src/librustc_llvm/lib.rs
+++ b/src/librustc_llvm/lib.rs
@@ -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,
diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs
index 3072bcae0bf..037f20ee4c5 100644
--- a/src/librustc_trans/trans/base.rs
+++ b/src/librustc_trans/trans/base.rs
@@ -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
     }
 }
 
diff --git a/src/librustc_trans/trans/cabi_aarch64.rs b/src/librustc_trans/trans/cabi_aarch64.rs
index 66308503b81..0d8ef9e2fc9 100644
--- a/src/librustc_trans/trans/cabi_aarch64.rs
+++ b/src/librustc_trans/trans/cabi_aarch64.rs
@@ -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
     }
 }
diff --git a/src/librustc_trans/trans/cabi_arm.rs b/src/librustc_trans/trans/cabi_arm.rs
index 830771d7397..7d1a8ab1452 100644
--- a/src/librustc_trans/trans/cabi_arm.rs
+++ b/src/librustc_trans/trans/cabi_arm.rs
@@ -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
     }
 }
diff --git a/src/librustc_trans/trans/cabi_mips.rs b/src/librustc_trans/trans/cabi_mips.rs
index 4dfe8daf339..776be8855cb 100644
--- a/src/librustc_trans/trans/cabi_mips.rs
+++ b/src/librustc_trans/trans/cabi_mips.rs
@@ -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
     };
 }
diff --git a/src/librustc_trans/trans/cabi_x86_64.rs b/src/librustc_trans/trans/cabi_x86_64.rs
index 86190b1e566..980a70256e9 100644
--- a/src/librustc_trans/trans/cabi_x86_64.rs
+++ b/src/librustc_trans/trans/cabi_x86_64.rs
@@ -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,
diff --git a/src/librustc_trans/trans/expr.rs b/src/librustc_trans/trans/expr.rs
index 7abd59ca0cd..11d1c7e6796 100644
--- a/src/librustc_trans/trans/expr.rs
+++ b/src/librustc_trans/trans/expr.rs
@@ -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)
     }
 }
 
diff --git a/src/librustc_trans/trans/foreign.rs b/src/librustc_trans/trans/foreign.rs
index fb61bab6ade..abb961d87de 100644
--- a/src/librustc_trans/trans/foreign.rs
+++ b/src/librustc_trans/trans/foreign.rs
@@ -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
diff --git a/src/librustc_trans/trans/type_.rs b/src/librustc_trans/trans/type_.rs
index 9cae142c03a..0124ab72f6b 100644
--- a/src/librustc_trans/trans/type_.rs
+++ b/src/librustc_trans/trans/type_.rs
@@ -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
+        }
+    }
 }
 
 
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index 8929bbe0232..9231d4ad659 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -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)
 }
diff --git a/src/test/compile-fail/feature-gate-simd-ffi.rs b/src/test/compile-fail/feature-gate-simd-ffi.rs
new file mode 100644
index 00000000000..409c85b7198
--- /dev/null
+++ b/src/test/compile-fail/feature-gate-simd-ffi.rs
@@ -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() {}
diff --git a/src/test/run-make/simd-ffi/Makefile b/src/test/run-make/simd-ffi/Makefile
new file mode 100644
index 00000000000..68a6a5fbfe8
--- /dev/null
+++ b/src/test/run-make/simd-ffi/Makefile
@@ -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))))
diff --git a/src/test/run-make/simd-ffi/simd.rs b/src/test/run-make/simd-ffi/simd.rs
new file mode 100755
index 00000000000..76079ddb8bd
--- /dev/null
+++ b/src/test/run-make/simd-ffi/simd.rs
@@ -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;
+    }
+}