From 4ebde950f5afc16366b04b4c191f1c6198726da7 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Thu, 15 Jan 2015 00:23:43 +1100 Subject: [PATCH 1/7] Document, tweak and refactor some trans code. --- src/librustc_llvm/lib.rs | 2 +- src/librustc_trans/trans/cabi_x86_64.rs | 43 +++++++++++++------------ src/librustc_trans/trans/type_.rs | 7 ++++ 3 files changed, 31 insertions(+), 21 deletions(-) diff --git a/src/librustc_llvm/lib.rs b/src/librustc_llvm/lib.rs index a4f9b1f98d4..e7d613f67b6 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/cabi_x86_64.rs b/src/librustc_trans/trans/cabi_x86_64.rs index 86190b1e566..c0e80ff08a2 100644 --- a/src/librustc_trans/trans/cabi_x86_64.rs +++ b/src/librustc_trans/trans/cabi_x86_64.rs @@ -34,6 +34,7 @@ enum RegClass { SSEDs, SSEDv, SSEInt, + /// Data that can appear in the upper half of an SSE register. SSEUp, X87, X87Up, @@ -155,26 +156,28 @@ fn classify_ty(ty: Type) -> Vec { 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, + + (_, _) => newv + }; + cls[i] = to_write; } fn classify_struct(tys: &[Type], diff --git a/src/librustc_trans/trans/type_.rs b/src/librustc_trans/trans/type_.rs index 9cae142c03a..acb1623db33 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 From 3d59a476e56b855fb27405016f0592ca7913b624 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Thu, 15 Jan 2015 00:27:38 +1100 Subject: [PATCH 2/7] Support SSE types in extern {} better. This seems to work on x86-64, but I am not able to test on other platforms. cc #20043 --- src/librustc_trans/trans/cabi_aarch64.rs | 13 ++++- src/librustc_trans/trans/cabi_arm.rs | 18 ++++++- src/librustc_trans/trans/cabi_mips.rs | 15 +++++- src/librustc_trans/trans/cabi_x86_64.rs | 66 +++++++++++++++++++++--- 4 files changed, 102 insertions(+), 10 deletions(-) diff --git a/src/librustc_trans/trans/cabi_aarch64.rs b/src/librustc_trans/trans/cabi_aarch64.rs index 66308503b81..ca9b3791ea9 100644 --- a/src/librustc_trans/trans/cabi_aarch64.rs +++ b/src/librustc_trans/trans/cabi_aarch64.rs @@ -11,7 +11,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; @@ -50,6 +50,11 @@ 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") } } @@ -80,6 +85,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") } } diff --git a/src/librustc_trans/trans/cabi_arm.rs b/src/librustc_trans/trans/cabi_arm.rs index 830771d7397..2da0c3787e8 100644 --- a/src/librustc_trans/trans/cabi_arm.rs +++ b/src/librustc_trans/trans/cabi_arm.rs @@ -11,7 +11,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; @@ -57,6 +57,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") } } @@ -90,6 +95,11 @@ 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") } } @@ -123,6 +133,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") } } diff --git a/src/librustc_trans/trans/cabi_mips.rs b/src/librustc_trans/trans/cabi_mips.rs index 4dfe8daf339..0a0ab784f57 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; @@ -50,7 +50,12 @@ 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") } } @@ -80,6 +85,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") } } diff --git a/src/librustc_trans/trans/cabi_x86_64.rs b/src/librustc_trans/trans/cabi_x86_64.rs index c0e80ff08a2..72ace5f9561 100644 --- a/src/librustc_trans/trans/cabi_x86_64.rs +++ b/src/librustc_trans/trans/cabi_x86_64.rs @@ -16,7 +16,7 @@ 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; @@ -114,7 +114,12 @@ fn classify_ty(ty: Type) -> Vec { 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") } } @@ -143,6 +148,13 @@ fn classify_ty(ty: Type) -> Vec { 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") } } @@ -175,6 +187,12 @@ fn classify_ty(ty: Type) -> Vec { (_, X87Up) | (_, ComplexX87) => Memory, + (SSEFv, SSEUp) | + (SSEFs, SSEUp) | + (SSEDv, SSEUp) | + (SSEDs, SSEUp) | + (SSEInt(_), SSEUp) => return, + (_, _) => newv }; cls[i] = to_write; @@ -240,6 +258,27 @@ fn classify_ty(ty: Type) -> Vec { 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, + 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") } } @@ -248,7 +287,7 @@ fn classify_ty(ty: Type) -> Vec { 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 { @@ -320,9 +359,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)), + // FIXME: need to handle the element types, since + // C doesn't distinguish between the contained + // types of the vector at all; normalise to u8, + // maybe? + SSEInt => panic!("llregtype: SSEInt not yet supported"), + _ => 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 * elts_per_word) as u64); tys.push(vec_ty); i += vec_len; continue; @@ -337,7 +386,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, From 5edbe1f5ddab26a5a8ea75d447d5a37d8f7a3347 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Thu, 15 Jan 2015 01:08:22 +1100 Subject: [PATCH 3/7] Add `Type::int_width` for retrieving integer's bit width. --- src/librustc_trans/trans/base.rs | 32 +++++++++++------------- src/librustc_trans/trans/cabi_aarch64.rs | 13 ++-------- src/librustc_trans/trans/cabi_arm.rs | 19 +++----------- src/librustc_trans/trans/cabi_mips.rs | 12 ++------- src/librustc_trans/trans/cabi_x86_64.rs | 13 ++-------- src/librustc_trans/trans/expr.rs | 22 ++++++++-------- src/librustc_trans/trans/type_.rs | 7 ++++++ 7 files changed, 41 insertions(+), 77 deletions(-) diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs index ea98d6bb74e..cee6018d9c0 100644 --- a/src/librustc_trans/trans/base.rs +++ b/src/librustc_trans/trans/base.rs @@ -847,26 +847,24 @@ pub fn cast_shift_rhs(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 ca9b3791ea9..3485e29707a 100644 --- a/src/librustc_trans/trans/cabi_aarch64.rs +++ b/src/librustc_trans/trans/cabi_aarch64.rs @@ -10,7 +10,6 @@ #![allow(non_upper_case_globals)] -use llvm; use llvm::{Integer, Pointer, Float, Double, Struct, Array, Vector}; use llvm::{StructRetAttribute, ZExtAttribute}; use trans::cabi::{FnType, ArgType}; @@ -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, @@ -61,11 +56,7 @@ fn ty_align(ty: Type) -> uint { 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, diff --git a/src/librustc_trans/trans/cabi_arm.rs b/src/librustc_trans/trans/cabi_arm.rs index 2da0c3787e8..13c70875f68 100644 --- a/src/librustc_trans/trans/cabi_arm.rs +++ b/src/librustc_trans/trans/cabi_arm.rs @@ -10,7 +10,6 @@ #![allow(non_upper_case_globals)] -use llvm; use llvm::{Integer, Pointer, Float, Double, Struct, Array, Vector}; use llvm::{StructRetAttribute, ZExtAttribute}; use trans::cabi::{FnType, ArgType}; @@ -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, @@ -75,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, @@ -106,11 +97,7 @@ fn ios_ty_align(ty: Type) -> uint { 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, diff --git a/src/librustc_trans/trans/cabi_mips.rs b/src/librustc_trans/trans/cabi_mips.rs index 0a0ab784f57..70b29b5fb75 100644 --- a/src/librustc_trans/trans/cabi_mips.rs +++ b/src/librustc_trans/trans/cabi_mips.rs @@ -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, @@ -61,11 +57,7 @@ fn ty_align(ty: Type) -> uint { 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, diff --git a/src/librustc_trans/trans/cabi_x86_64.rs b/src/librustc_trans/trans/cabi_x86_64.rs index 72ace5f9561..d8bd57785d2 100644 --- a/src/librustc_trans/trans/cabi_x86_64.rs +++ b/src/librustc_trans/trans/cabi_x86_64.rs @@ -14,7 +14,6 @@ #![allow(non_upper_case_globals)] use self::RegClass::*; -use llvm; use llvm::{Integer, Pointer, Float, Double}; use llvm::{Struct, Array, Attribute, Vector}; use llvm::{StructRetAttribute, ByValAttribute, ZExtAttribute}; @@ -94,11 +93,7 @@ fn classify_ty(ty: Type) -> Vec { 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, @@ -125,11 +120,7 @@ fn classify_ty(ty: Type) -> Vec { 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, diff --git a/src/librustc_trans/trans/expr.rs b/src/librustc_trans/trans/expr.rs index ac50445be2f..433d989f22c 100644 --- a/src/librustc_trans/trans/expr.rs +++ b/src/librustc_trans/trans/expr.rs @@ -1906,18 +1906,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/type_.rs b/src/librustc_trans/trans/type_.rs index acb1623db33..0124ab72f6b 100644 --- a/src/librustc_trans/trans/type_.rs +++ b/src/librustc_trans/trans/type_.rs @@ -333,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 + } + } } From 7d4f358de7de97b443a97e1f18a16781d472bbda Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Thu, 15 Jan 2015 13:49:41 +1100 Subject: [PATCH 4/7] Support SSE with integer types in x86-64 FFI. Unlike the intrinics in C, this types the SSE values base on integer size. This matches the LLVM intrinsics which have concrete vector types (`<4 x i32>` etc.), and is no loss of expressivity: if one is using a C function that really takes an untyped integral SSE value, just give it whatever Rust type makes most sense. --- src/librustc_trans/trans/cabi_x86_64.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/librustc_trans/trans/cabi_x86_64.rs b/src/librustc_trans/trans/cabi_x86_64.rs index d8bd57785d2..980a70256e9 100644 --- a/src/librustc_trans/trans/cabi_x86_64.rs +++ b/src/librustc_trans/trans/cabi_x86_64.rs @@ -32,7 +32,7 @@ enum RegClass { SSEFv, SSEDs, SSEDv, - SSEInt, + SSEInt(/* bitwidth */ u64), /// Data that can appear in the upper half of an SSE register. SSEUp, X87, @@ -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 } } @@ -254,7 +254,7 @@ fn classify_ty(ty: Type) -> Vec { let elt = ty.element_type(); let eltsz = ty_size(elt); let mut reg = match elt.kind() { - Integer => SSEInt, + Integer => SSEInt(elt.int_width()), Float => SSEFv, Double => SSEDv, _ => panic!("classify: unhandled vector element type") @@ -350,19 +350,19 @@ fn llreg_ty(ccx: &CrateContext, cls: &[RegClass]) -> Type { Int => { tys.push(Type::i64(ccx)); } - SSEFv | SSEDv | SSEInt => { + SSEFv | SSEDv | SSEInt(_) => { let (elts_per_word, elt_ty) = match cls[i] { SSEFv => (2, Type::f32(ccx)), SSEDv => (1, Type::f64(ccx)), - // FIXME: need to handle the element types, since - // C doesn't distinguish between the contained - // types of the vector at all; normalise to u8, - // maybe? - SSEInt => panic!("llregtype: SSEInt not yet supported"), + 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(&elt_ty, (vec_len * elts_per_word) as u64); + let vec_ty = Type::vector(&elt_ty, vec_len as u64 * elts_per_word); tys.push(vec_ty); i += vec_len; continue; From 9e83ae931c802608962fb5f9c90220d80d2eaa1c Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Thu, 15 Jan 2015 14:43:24 +1100 Subject: [PATCH 5/7] Put vector types in regs for arm & mips FFI. This seems to match what clang does on arm, but I cannot do any experimentation with mips, but it matches how the LLVM intrinsics are defined in any case... --- src/librustc_trans/trans/cabi_aarch64.rs | 3 ++- src/librustc_trans/trans/cabi_arm.rs | 3 ++- src/librustc_trans/trans/cabi_mips.rs | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/librustc_trans/trans/cabi_aarch64.rs b/src/librustc_trans/trans/cabi_aarch64.rs index 3485e29707a..0d8ef9e2fc9 100644 --- a/src/librustc_trans/trans/cabi_aarch64.rs +++ b/src/librustc_trans/trans/cabi_aarch64.rs @@ -139,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 13c70875f68..7d1a8ab1452 100644 --- a/src/librustc_trans/trans/cabi_arm.rs +++ b/src/librustc_trans/trans/cabi_arm.rs @@ -169,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 70b29b5fb75..776be8855cb 100644 --- a/src/librustc_trans/trans/cabi_mips.rs +++ b/src/librustc_trans/trans/cabi_mips.rs @@ -123,7 +123,8 @@ fn is_reg_ty(ty: Type) -> bool { Integer | Pointer | Float - | Double => true, + | Double + | Vector => true, _ => false }; } From 4f08de84c923f08535e9a6f754b2776286ebcd01 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Fri, 16 Jan 2015 09:23:18 +1100 Subject: [PATCH 6/7] Add comprehensive test for no-ICE behaviour of SIMD FFI. This just compiles a test using SIMD in FFI (mostly importing LLVM intrinsics) for almost all rustc's supported platforms, but not linking it or running it, so there's absolutely no guarantee that this is correct. --- src/test/run-make/simd-ffi/Makefile | 33 ++++++++++++ src/test/run-make/simd-ffi/simd.rs | 81 +++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+) create mode 100644 src/test/run-make/simd-ffi/Makefile create mode 100755 src/test/run-make/simd-ffi/simd.rs 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..d5945db9479 --- /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 or the MIT license +// , 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, 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; + } +} From c8e0e9549d4327c54d9eb5fd0de5e23312c34fe9 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Fri, 16 Jan 2015 10:16:20 +1100 Subject: [PATCH 7/7] Feature gate SIMD in FFI, due to unknown ABIs. I don't know if this handling of SIMD types is correct for the C ABI on all platforms, so lets add an even finer feature gate than just the `simd` one. The `simd` one can be used with (relatively) little risk of complete nonsense, the reason for it is that it is likely that things will change. Using the types in FFI with an incorrect ABI will at best give absolute nonsense results, but possibly cause serious breakage too, so this is a step up in badness, hence a new feature gate. --- src/librustc_trans/trans/foreign.rs | 34 ++++++++++++++++++- src/libsyntax/feature_gate.rs | 4 +++ .../compile-fail/feature-gate-simd-ffi.rs | 26 ++++++++++++++ src/test/run-make/simd-ffi/simd.rs | 2 +- 4 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 src/test/compile-fail/feature-gate-simd-ffi.rs 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/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(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 or the MIT license +// , 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/simd.rs b/src/test/run-make/simd-ffi/simd.rs index d5945db9479..76079ddb8bd 100755 --- a/src/test/run-make/simd-ffi/simd.rs +++ b/src/test/run-make/simd-ffi/simd.rs @@ -14,7 +14,7 @@ // cross-compiled standard libraries. #![no_std] -#![feature(simd, link_llvm_intrinsics, lang_items)] +#![feature(simd, simd_ffi, link_llvm_intrinsics, lang_items)] #[repr(C)]