From ea7524c4575318b8e348165e62f6c421421ccd48 Mon Sep 17 00:00:00 2001 From: Richard Diamond Date: Fri, 14 Mar 2014 18:59:36 -0500 Subject: [PATCH 1/2] Added two simd expressions: * ExprSimd - SIMD gather * ExprSwizzle - Two operand shuffle Added ty_simd and a corresponding ty_infer type. Added simd_syntax for accessing the new expressions as well as a new crate providing basic multiple-data types and implementations. --- mk/crates.mk | 7 +- src/librustc/metadata/tydecode.rs | 16 ++ src/librustc/metadata/tyencode.rs | 5 + src/librustc/middle/cfg/construct.rs | 11 +- src/librustc/middle/const_eval.rs | 50 ++++ src/librustc/middle/dataflow.rs | 7 +- src/librustc/middle/liveness.rs | 13 +- src/librustc/middle/mem_categorization.rs | 7 +- src/librustc/middle/moves.rs | 9 +- src/librustc/middle/trans/base.rs | 30 ++- src/librustc/middle/trans/common.rs | 5 + src/librustc/middle/trans/datum.rs | 10 +- src/librustc/middle/trans/debuginfo.rs | 30 ++- src/librustc/middle/trans/expr.rs | 123 +++++++++- src/librustc/middle/trans/mod.rs | 1 + src/librustc/middle/trans/reflect.rs | 7 + src/librustc/middle/trans/simd.rs | 23 ++ src/librustc/middle/trans/type_of.rs | 7 + src/librustc/middle/ty.rs | 214 ++++++++++++----- src/librustc/middle/ty_fold.rs | 2 +- src/librustc/middle/typeck/astconv.rs | 27 +++ src/librustc/middle/typeck/check/method.rs | 3 +- src/librustc/middle/typeck/check/mod.rs | 225 +++++++++++++++++- src/librustc/middle/typeck/coherence.rs | 4 +- src/librustc/middle/typeck/infer/combine.rs | 115 ++++++++- src/librustc/middle/typeck/infer/mod.rs | 36 ++- src/librustc/middle/typeck/infer/resolve.rs | 69 +++++- src/librustc/middle/typeck/infer/to_str.rs | 5 + src/librustc/middle/typeck/infer/unify.rs | 13 + src/librustc/middle/typeck/variance.rs | 3 + src/librustc/util/ppaux.rs | 3 +- src/librustdoc/clean.rs | 1 + src/libsimd/lib.rs | 214 +++++++++++++++++ src/libsimd_syntax/lib.rs | 221 +++++++++++++++++ src/libstd/intrinsics.rs | 6 + src/libstd/reflect.rs | 13 + src/libstd/repr.rs | 6 + src/libstd/unstable/mod.rs | 3 +- src/libsyntax/ast.rs | 27 ++- src/libsyntax/fold.rs | 9 + src/libsyntax/parse/parser.rs | 2 +- src/libsyntax/print/pprust.rs | 20 ++ src/libsyntax/visit.rs | 16 ++ src/test/compile-fail/simd-if-cond.rs | 44 ++++ src/test/compile-fail/simd-struct-indexing.rs | 40 ++++ .../simd-swizzle-mask-const-expr.rs | 24 ++ .../simd-swizzle-mask-out-of-bounds.rs | 23 ++ src/test/compile-fail/simd-type.rs | 3 - src/test/run-pass/md-gather-infer.rs | 28 +++ src/test/run-pass/reflect-visit-type.rs | 6 + src/test/run-pass/simd-binop.rs | 208 ++++++++++++++-- src/test/run-pass/simd-generics.rs | 10 +- src/test/run-pass/simd-index.rs | 42 ++++ src/test/run-pass/simd-struct-swizzle.rs | 47 ++++ src/test/run-pass/simd-swizzle.rs | 55 +++++ 55 files changed, 2015 insertions(+), 133 deletions(-) create mode 100644 src/librustc/middle/trans/simd.rs create mode 100644 src/libsimd/lib.rs create mode 100644 src/libsimd_syntax/lib.rs create mode 100644 src/test/compile-fail/simd-if-cond.rs create mode 100644 src/test/compile-fail/simd-struct-indexing.rs create mode 100644 src/test/compile-fail/simd-swizzle-mask-const-expr.rs create mode 100644 src/test/compile-fail/simd-swizzle-mask-out-of-bounds.rs create mode 100644 src/test/run-pass/md-gather-infer.rs create mode 100644 src/test/run-pass/simd-index.rs create mode 100644 src/test/run-pass/simd-struct-swizzle.rs create mode 100644 src/test/run-pass/simd-swizzle.rs diff --git a/mk/crates.mk b/mk/crates.mk index 16485b9e6b529..927c09338300c 100644 --- a/mk/crates.mk +++ b/mk/crates.mk @@ -50,8 +50,9 @@ ################################################################################ TARGET_CRATES := std extra green rustuv native flate arena glob term semver \ - uuid serialize sync getopts collections num test time rand -HOST_CRATES := syntax rustc rustdoc fourcc hexfloat + uuid serialize sync getopts collections num test time rand \ + simd +HOST_CRATES := syntax rustc rustdoc fourcc hexfloat simd_syntax CRATES := $(TARGET_CRATES) $(HOST_CRATES) TOOLS := compiletest rustdoc rustc @@ -81,6 +82,8 @@ DEPS_num := std rand DEPS_test := std extra collections getopts serialize term DEPS_time := std serialize DEPS_rand := std +DEPS_simd := std simd_syntax +DEPS_simd_syntax := syntax std TOOL_DEPS_compiletest := test green rustuv getopts TOOL_DEPS_rustdoc := rustdoc native diff --git a/src/librustc/metadata/tydecode.rs b/src/librustc/metadata/tydecode.rs index 17e334d05fbab..7ed038b3dc17f 100644 --- a/src/librustc/metadata/tydecode.rs +++ b/src/librustc/metadata/tydecode.rs @@ -413,6 +413,22 @@ fn parse_ty(st: &mut PState, conv: conv_did) -> ty::t { assert_eq!(next(st), ']'); return ty::mk_struct(st.tcx, did, substs); } + 'S' => { + assert_eq!(next(st), '['); + let prim_ty = parse_ty(st, |x, y| conv(x, y) ); + if !ty::type_is_simd_scalar(st.tcx, prim_ty) { + error!("invalid type in simd: {}", ::util::ppaux::ty_to_str(st.tcx, prim_ty)); + fail!(); + } + assert_eq!(next(st), 'x'); + let count = parse_uint(st); + if count == 0 { + error!("invalid simd element count: {}", count); + fail!(); + } + assert_eq!(next(st), ']'); + return ty::mk_simd(st.tcx, prim_ty, count); + } c => { error!("unexpected char in type string: {}", c); fail!();} } } diff --git a/src/librustc/metadata/tyencode.rs b/src/librustc/metadata/tyencode.rs index 590487f20d09a..71babc9a7d01d 100644 --- a/src/librustc/metadata/tyencode.rs +++ b/src/librustc/metadata/tyencode.rs @@ -336,6 +336,11 @@ fn enc_sty(w: &mut MemWriter, cx: @ctxt, st: &ty::sty) { enc_substs(w, cx, substs); mywrite!(w, "]"); } + ty::ty_simd(t, n) => { + mywrite!(w, "S["); + enc_ty(w, cx, t); + mywrite!(w, "x{}]", n); + } ty::ty_err => fail!("shouldn't encode error type") } } diff --git a/src/librustc/middle/cfg/construct.rs b/src/librustc/middle/cfg/construct.rs index 0100a82a9d5aa..8bf760402d7d0 100644 --- a/src/librustc/middle/cfg/construct.rs +++ b/src/librustc/middle/cfg/construct.rs @@ -12,6 +12,7 @@ use middle::cfg::*; use middle::graph; use middle::typeck; use middle::ty; +use std::vec; use std::vec_ng::Vec; use syntax::ast; use syntax::ast_util; @@ -370,7 +371,7 @@ impl CFGBuilder { self.call(expr, pred, e, []) } - ast::ExprTup(ref exprs) => { + ast::ExprTup(ref exprs) | ast::ExprSimd(ref exprs) => { self.straightline(expr, pred, exprs.as_slice()) } @@ -380,6 +381,14 @@ impl CFGBuilder { fields.iter().map(|f| f.expr).collect(); self.straightline(expr, base_exit, field_exprs.as_slice()) } + ast::ExprSwizzle(left, opt_right, ref mask) => { + let exprs = vec::build(Some(3), |push| { + push(left); + opt_right.iter().advance(|&r| { push(r); true }); + mask.iter().advance(|&m| { push(m); true }); + }); + self.straightline(expr, pred, exprs) + } ast::ExprRepeat(elem, count, _) => { self.straightline(expr, pred, [elem, count]) diff --git a/src/librustc/middle/const_eval.rs b/src/librustc/middle/const_eval.rs index 1f675b7bb4a68..15b690fb8e396 100644 --- a/src/librustc/middle/const_eval.rs +++ b/src/librustc/middle/const_eval.rs @@ -551,3 +551,53 @@ pub fn lit_expr_eq(tcx: middle::ty::ctxt, a: &Expr, b: &Expr) -> Option { pub fn lit_eq(a: &Lit, b: &Lit) -> Option { compare_const_vals(&lit_to_const(a), &lit_to_const(b)).map(|val| val == 0) } + +// Returns the evaluated positive integer for the provided expression, or reports error +pub fn eval_positive_integer(tcx: &T, + count_expr: &ast::Expr, + msg: &str) -> uint { + match eval_const_expr_partial(tcx, count_expr) { + Ok(ref const_val) => match *const_val { + const_int(count) => if count < 0 { + tcx.ty_ctxt().sess.span_err(count_expr.span, + format!("expected positive integer for \ + {:s} but found negative integer", + msg)); + return 0; + } else { + return count as uint + }, + const_uint(count) => return count as uint, + const_float(count) => { + tcx.ty_ctxt().sess.span_err(count_expr.span, + format!("expected positive integer for \ + {:s} but found float", msg)); + return count as uint; + } + const_str(_) => { + tcx.ty_ctxt().sess.span_err(count_expr.span, + format!("expected positive integer for \ + {:s} but found string", msg)); + return 0; + } + const_bool(_) => { + tcx.ty_ctxt().sess.span_err(count_expr.span, + format!("expected positive integer for \ + {:s} but found boolean", msg)); + return 0; + } + const_binary(_) => { + tcx.ty_ctxt().sess.span_err(count_expr.span, + format!("expected positive integer for \ + {:s} but found binary array", msg)); + return 0; + } + }, + Err(..) => { + tcx.ty_ctxt().sess.span_err(count_expr.span, + format!("expected constant integer for {:s} \ + but found variable", msg)); + return 0; + } + } +} diff --git a/src/librustc/middle/dataflow.rs b/src/librustc/middle/dataflow.rs index 57b5be4e96069..1ed4f980e3cd2 100644 --- a/src/librustc/middle/dataflow.rs +++ b/src/librustc/middle/dataflow.rs @@ -597,9 +597,14 @@ impl<'a, O:DataFlowOperator> PropagationContext<'a, O> { self.walk_call(expr.id, [e], in_out, loop_scopes); } - ast::ExprTup(ref exprs) => { + ast::ExprTup(ref exprs) | ast::ExprSimd(ref exprs) => { self.walk_exprs(exprs.as_slice(), in_out, loop_scopes); } + ast::ExprSwizzle(left, opt_right, ref mask) => { + self.walk_expr(left, in_out, loop_scopes); + self.walk_opt_expr(opt_right, in_out, loop_scopes); + self.walk_exprs(mask.as_slice(), in_out, loop_scopes); + } ast::ExprBinary(op, l, r) if ast_util::lazy_binop(op) => { self.walk_expr(l, in_out, loop_scopes); diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs index fa435378ab7df..9264dd9843653 100644 --- a/src/librustc/middle/liveness.rs +++ b/src/librustc/middle/liveness.rs @@ -547,7 +547,8 @@ fn visit_expr(v: &mut LivenessVisitor, expr: &Expr, this: @IrMaps) { ExprAgain(_) | ExprLit(_) | ExprRet(..) | ExprBlock(..) | ExprAssign(..) | ExprAssignOp(..) | ExprMac(..) | ExprStruct(..) | ExprRepeat(..) | ExprParen(..) | - ExprInlineAsm(..) | ExprBox(..) => { + ExprInlineAsm(..) | ExprBox(..) | ExprSwizzle(..) | + ExprSimd(..) => { visit::walk_expr(v, expr, this); } } @@ -1211,6 +1212,12 @@ impl Liveness { self.propagate_through_expr(field.expr, succ) }) } + ExprSwizzle(left, opt_right, _) => { + // no need to propagate through the mask; + // it has to be a constant expresssion + let succ = self.propagate_through_expr(left, succ); + self.propagate_through_opt_expr(opt_right, succ) + } ExprCall(f, ref args) => { // calling a fn with bot return type means that the fn @@ -1231,7 +1238,7 @@ impl Liveness { self.propagate_through_exprs(args.as_slice(), succ) } - ExprTup(ref exprs) => { + ExprTup(ref exprs) | ExprSimd(ref exprs) => { self.propagate_through_exprs(exprs.as_slice(), succ) } @@ -1527,7 +1534,7 @@ fn check_expr(this: &mut Liveness, expr: &Expr) { ExprAgain(..) | ExprLit(_) | ExprBlock(..) | ExprMac(..) | ExprAddrOf(..) | ExprStruct(..) | ExprRepeat(..) | ExprParen(..) | ExprFnBlock(..) | ExprProc(..) | ExprPath(..) | - ExprBox(..) => { + ExprBox(..) | ExprSwizzle(..) | ExprSimd(..) => { visit::walk_expr(this, expr, ()); } ExprForLoop(..) => fail!("non-desugared expr_for_loop") diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index c07cd2570a30b..d193a5c8f3076 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -208,7 +208,7 @@ pub fn opt_deref_kind(t: ty::t) -> Option { } ty::ty_vec(_, ty::vstore_fixed(_)) | - ty::ty_str(ty::vstore_fixed(_)) => { + ty::ty_str(ty::vstore_fixed(_)) | ty::ty_simd(..) => { Some(deref_interior(InteriorElement(element_kind(t)))) } @@ -473,7 +473,8 @@ impl MemCategorizationContext { ast::ExprBlock(..) | ast::ExprLoop(..) | ast::ExprMatch(..) | ast::ExprLit(..) | ast::ExprBreak(..) | ast::ExprMac(..) | ast::ExprAgain(..) | ast::ExprStruct(..) | ast::ExprRepeat(..) | - ast::ExprInlineAsm(..) | ast::ExprBox(..) => { + ast::ExprInlineAsm(..) | ast::ExprBox(..) | ast::ExprSwizzle(..) | + ast::ExprSimd(..) => { Ok(self.cat_rvalue_node(expr.id(), expr.span(), expr_ty)) } @@ -814,7 +815,7 @@ impl MemCategorizationContext { //! - `derefs`: the deref number to be used for //! the implicit index deref, if any (see above) - let element_ty = match ty::index(base_cmt.ty) { + let element_ty = match ty::index(self.tcx(), base_cmt.ty) { Some(ref mt) => mt.ty, None => { self.tcx().sess.span_bug( diff --git a/src/librustc/middle/moves.rs b/src/librustc/middle/moves.rs index b52ec7be63114..4f7d9c64fd07a 100644 --- a/src/librustc/middle/moves.rs +++ b/src/librustc/middle/moves.rs @@ -465,8 +465,15 @@ impl VisitContext { } } } + ExprSwizzle(left_expr, opt_right_expr, _) => { + self.consume_expr(left_expr); + match opt_right_expr { + Some(r) => self.consume_expr(r), + None => {} + } + } - ExprTup(ref exprs) => { + ExprTup(ref exprs) | ExprSimd(ref exprs) => { self.consume_exprs(exprs.as_slice()); } diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index 156a4f914a909..8ea6f36846d00 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -581,9 +581,19 @@ pub fn maybe_name_value(cx: &CrateContext, v: ValueRef, s: &str) { } } - // Used only for creating scalar comparison glue. -pub enum scalar_type { nil_type, signed_int, unsigned_int, floating_point, } +pub enum inner_vector_type { + vsigned_int, + vunsigned_int, + vfloating_point +} +pub enum scalar_type { + nil_type, + signed_int, + unsigned_int, + floating_point, + vector_type(inner_vector_type), +} // NB: This produces an i1, not a Rust bool (i8). pub fn compare_scalar_types<'a>( @@ -601,6 +611,16 @@ pub fn compare_scalar_types<'a>( ty::ty_uint(_) | ty::ty_char => f(unsigned_int), ty::ty_int(_) => f(signed_int), ty::ty_float(_) => f(floating_point), + ty::ty_simd(..) => { + match ty::get(ty::simd_type(cx.tcx(), t)).sty { + ty::ty_bool | ty::ty_ptr(_) | ty::ty_uint(..) => { + f(vector_type(vunsigned_int)) + } + ty::ty_int(..) => f(vector_type(vsigned_int)), + ty::ty_float(..) => f(vector_type(vfloating_point)), + _ => unreachable!(), + } + } // Should never get here, because t is scalar. _ => cx.sess().bug("non-scalar type passed to compare_scalar_types") } @@ -631,7 +651,7 @@ pub fn compare_scalar_values<'a>( _ => die(cx) } } - floating_point => { + floating_point | vector_type(vfloating_point) => { let cmp = match op { ast::BiEq => lib::llvm::RealOEQ, ast::BiNe => lib::llvm::RealUNE, @@ -643,7 +663,7 @@ pub fn compare_scalar_values<'a>( }; return FCmp(cx, cmp, lhs, rhs); } - signed_int => { + signed_int | vector_type(vsigned_int) => { let cmp = match op { ast::BiEq => lib::llvm::IntEQ, ast::BiNe => lib::llvm::IntNE, @@ -655,7 +675,7 @@ pub fn compare_scalar_values<'a>( }; return ICmp(cx, cmp, lhs, rhs); } - unsigned_int => { + unsigned_int | vector_type(vunsigned_int) => { let cmp = match op { ast::BiEq => lib::llvm::IntEQ, ast::BiNe => lib::llvm::IntNE, diff --git a/src/librustc/middle/trans/common.rs b/src/librustc/middle/trans/common.rs index bfe5d5187d79d..d2a87f855d3cd 100644 --- a/src/librustc/middle/trans/common.rs +++ b/src/librustc/middle/trans/common.rs @@ -680,6 +680,11 @@ pub fn C_bytes(bytes: &[u8]) -> ValueRef { return llvm::LLVMConstStringInContext(base::task_llcx(), ptr, bytes.len() as c_uint, True); } } +pub fn C_vector(elts: &[ValueRef]) -> ValueRef { + unsafe { + llvm::LLVMConstVector(elts.as_ptr(), elts.len() as u32) + } +} pub fn get_param(fndecl: ValueRef, param: uint) -> ValueRef { unsafe { diff --git a/src/librustc/middle/trans/datum.rs b/src/librustc/middle/trans/datum.rs index fe848c429aee2..cca4985151370 100644 --- a/src/librustc/middle/trans/datum.rs +++ b/src/librustc/middle/trans/datum.rs @@ -25,6 +25,7 @@ use middle::trans::glue; use middle::trans::tvec; use middle::trans::type_of; use middle::trans::write_guard; +use middle::trans::simd; use middle::ty; use util::ppaux::{ty_to_str}; @@ -529,9 +530,12 @@ impl Datum { } pub fn get_vec_base_and_len<'a>(&self, bcx: &'a Block<'a>) -> (ValueRef, ValueRef) { - //! Converts a vector into the slice pair. - - tvec::get_base_and_len(bcx, self.val, self.ty) + //! Converts a vector or a simd into the slice pair. + if ty::type_is_simd(bcx.ccx().tcx, self.ty) { + simd::get_base_and_len(bcx, self.val, self.ty) + } else { + tvec::get_base_and_len(bcx, self.val, self.ty) + } } } diff --git a/src/librustc/middle/trans/debuginfo.rs b/src/librustc/middle/trans/debuginfo.rs index 6ade20d29136d..ce826b0e55b5e 100644 --- a/src/librustc/middle/trans/debuginfo.rs +++ b/src/librustc/middle/trans/debuginfo.rs @@ -2129,15 +2129,14 @@ fn type_metadata(cx: &CrateContext, ty::ty_trait(def_id, ref substs, trait_store, mutability, ref bounds) => { trait_metadata(cx, def_id, t, substs, trait_store, mutability, bounds) }, - ty::ty_struct(def_id, ref substs) => { - if ty::type_is_simd(cx.tcx, t) { - let element_type = ty::simd_type(cx.tcx, t); - let len = ty::simd_size(cx.tcx, t); - fixed_vec_metadata(cx, element_type, len, usage_site_span) - } else { - prepare_struct_metadata(cx, t, def_id, substs, usage_site_span).finalize(cx) - } + ty::ty_struct(def_id, ref substs) if !ty::type_is_simd(cx.tcx, t) => { + prepare_struct_metadata(cx, t, def_id, substs, usage_site_span).finalize(cx) }, + _ if ty::type_is_simd(cx.tcx, t) => { + let element_type = ty::simd_type(cx.tcx, t); + let len = ty::simd_size(cx.tcx, t); + fixed_vec_metadata(cx, element_type, len, usage_site_span) + } ty::ty_tup(ref elements) => { prepare_tuple_metadata(cx, t, @@ -2578,6 +2577,21 @@ fn populate_scope_map(cx: &CrateContext, walk_expr(cx, *ie, scope_stack, scope_map); } } + ast::ExprSimd(ref exprs) => { + for &e in exprs.iter() { + walk_expr(cx, e, scope_stack, scope_map); + } + } + ast::ExprSwizzle(left, opt_right, _) => { + walk_expr(cx, left, scope_stack, scope_map); + match opt_right { + Some(right) => { + walk_expr(cx, right, scope_stack, scope_map); + } + None => {} + }; + // mask is a constant expression, so skip. + } ast::ExprAssign(sub_exp1, sub_exp2) | ast::ExprRepeat(sub_exp1, sub_exp2, _) => { diff --git a/src/librustc/middle/trans/expr.rs b/src/librustc/middle/trans/expr.rs index d143d6743052c..fea945579c817 100644 --- a/src/librustc/middle/trans/expr.rs +++ b/src/librustc/middle/trans/expr.rs @@ -65,6 +65,7 @@ use middle::ty::{AutoBorrowObj, AutoDerefRef, AutoAddEnv, AutoObject, AutoUnsafe use middle::ty::{AutoPtr, AutoBorrowVec, AutoBorrowVecRef, AutoBorrowFn}; use middle::ty; use middle::typeck::MethodCall; +use middle::const_eval; use util::common::indenter; use util::ppaux::Repr; use util::nodemap::NodeMap; @@ -458,6 +459,12 @@ fn trans_datum_unadjusted<'a>(bcx: &'a Block<'a>, ast::ExprLogLevel => { trans_log_level(bcx) } + ast::ExprSimd(ref exprs) => { + trans_simd(bcx, expr, exprs) + } + ast::ExprSwizzle(left, opt_right, ref mask) => { + trans_simd_swizzle(bcx, expr, left, opt_right, mask) + } _ => { bcx.tcx().sess.span_bug( expr.span, @@ -467,6 +474,112 @@ fn trans_datum_unadjusted<'a>(bcx: &'a Block<'a>, } } } +fn trans_simd_swizzle<'a>(bcx: &'a Block<'a>, + expr: &ast::Expr, + left: &ast::Expr, + opt_right: Option<@ast::Expr>, + mask: &Vec<@ast::Expr>) + -> DatumBlock<'a, Expr> { + use std::iter; + + let mut bcx = bcx; + let _icx = push_ctxt("trans_simd_swizzle"); + let tcx = bcx.tcx(); + let get_ll_mask = || { + let mask = mask.map(|&m| { + let m = const_eval::eval_positive_integer(&tcx, m, "swizzle mask"); + C_i32(m as i32) + }); + C_vector(mask.as_slice()) + }; + + let left_ty = expr_ty_adjusted(bcx, left); + let left_datum = unpack_datum!(bcx, trans(bcx, left)); + let left_llval = left_datum.to_llscalarish(bcx); + let (left_llval, right_llval, mask) = match opt_right { + Some(right) => { + let datum = unpack_datum!(bcx, trans(bcx, right)); + let right_ty = expr_ty_adjusted(bcx, right); + let left_size = ty::simd_size(bcx.tcx(), left_ty); + let right_size = ty::simd_size(bcx.tcx(), right_ty); + + if left_size == right_size { + (left_llval, datum.to_llscalarish(bcx), get_ll_mask()) + } else { + // LLVM wants both operands to be the same type + // so create a shuffle to enlarge the smaller side. + let min = if left_size < right_size { left_size } + else { right_size }; + let max = if left_size > right_size { left_size } + else { right_size }; + let new_mask = vec::build(Some(max), |push| { + for m in iter::range(0, min) { + push(C_i32(m as i32)); + } + for _ in iter::range(min, max) { + // these may safely be any valid index because + // we remove any possiblity of accessing these in typeck. + push(C_i32(0)); + } + }); + let new_mask_vector = C_vector(new_mask); + + if left_size < right_size { + let delta = right_size - left_size; + let mask = mask.iter().map(|&m| { + let m = const_eval::eval_positive_integer(&bcx.tcx(), + m, + "swizzle mask"); + let m = if m >= left_size { m + delta } + else { m }; + C_i32(m as i32) + }).to_owned_vec(); + let mask = C_vector(mask); + let left_llval = ShuffleVector(bcx, + left_llval, + C_undef(type_of::type_of(bcx.ccx(), + left_ty)), + new_mask_vector); + (left_llval, datum.to_llscalarish(bcx), mask) + } else { + let datum = datum.to_llscalarish(bcx); + let datum = ShuffleVector(bcx, + datum, + C_undef(type_of::type_of(bcx.ccx(), + right_ty)), + new_mask_vector); + (left_llval, datum, get_ll_mask()) + } + } + } + None => (left_llval, + C_undef(type_of::type_of(bcx.ccx(), left_ty)), + get_ll_mask()), + }; + + let shuffle = ShuffleVector(bcx, left_llval, right_llval, mask); + DatumBlock(bcx, Datum(shuffle, expr_ty(bcx, expr), RvalueExpr(Rvalue(ByValue)))) +} +fn trans_simd<'a>(bcx: &'a Block<'a>, + expr: &ast::Expr, + exprs: &Vec<@ast::Expr>) + -> DatumBlock<'a, Expr> { + let mut bcx = bcx; + let _icx = push_ctxt("trans_simd"); + + let ty = expr_ty(bcx, expr); + let llty = type_of::type_of(bcx.ccx(), ty); + let first = unsafe { + llvm::LLVMGetUndef(llty.to_ref()) + }; + + let last = exprs.iter().enumerate().fold(first, |acc, (i, e)| { + let e_datum = trans(bcx, *e); + let e_llval = unpack_datum!(bcx, e_datum); + InsertElement(bcx, acc, e_llval.to_llscalarish(bcx), C_i32(i as i32)) + }); + DatumBlock(bcx, Datum(last, ty, RvalueExpr(Rvalue(ByValue)))) +} fn trans_rec_field<'a>(bcx: &'a Block<'a>, base: &ast::Expr, @@ -1369,13 +1482,19 @@ fn trans_eager_binop<'a>( if ty::type_is_bot(rhs_t) { C_bool(false) } else { - if !ty::type_is_scalar(rhs_t) { + let is_simd = ty::type_is_simd(tcx, rhs_t); + if !ty::type_is_scalar(rhs_t) && + !is_simd { bcx.tcx().sess.span_bug(binop_expr.span, "non-scalar comparison"); } let cmpr = base::compare_scalar_types(bcx, lhs, rhs, rhs_t, op); bcx = cmpr.bcx; - ZExt(bcx, cmpr.val, Type::i8()) + if is_simd { + ZExt(bcx, cmpr.val, Type::vector(&Type::i8(), ty::simd_size(tcx, rhs_t) as u64)) + } else { + ZExt(bcx, cmpr.val, Type::i8()) + } } } _ => { diff --git a/src/librustc/middle/trans/mod.rs b/src/librustc/middle/trans/mod.rs index 7ac491edfeb2a..098235148255e 100644 --- a/src/librustc/middle/trans/mod.rs +++ b/src/librustc/middle/trans/mod.rs @@ -46,3 +46,4 @@ pub mod value; pub mod basic_block; pub mod llrepr; pub mod cleanup; +pub mod simd; diff --git a/src/librustc/middle/trans/reflect.rs b/src/librustc/middle/trans/reflect.rs index 6c87ae94ba4b3..f1f87c92de236 100644 --- a/src/librustc/middle/trans/reflect.rs +++ b/src/librustc/middle/trans/reflect.rs @@ -278,6 +278,13 @@ impl<'a> Reflector<'a> { }) } + ty::ty_simd(inner_ty, count) => { + let extra = vec_ng::append(vec!(self.c_tydesc(inner_ty), + self.c_uint(count)), + self.c_size_and_align(t).as_slice()); + self.visit("simd", extra.as_slice()) + } + // FIXME (#2595): visiting all the variants in turn is probably // not ideal. It'll work but will get costly on big enums. Maybe // let the visitor tell us if it wants to visit only a particular diff --git a/src/librustc/middle/trans/simd.rs b/src/librustc/middle/trans/simd.rs new file mode 100644 index 0000000000000..77531d8e7f346 --- /dev/null +++ b/src/librustc/middle/trans/simd.rs @@ -0,0 +1,23 @@ +// Copyright 2012-2014 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. + +use middle::ty; +use middle::ty::{simd_size}; +use middle::trans::build::GEPi; +use middle::trans::common::{Block, C_uint}; +use lib::llvm::ValueRef; + +pub fn get_base_and_len(bcx: &Block, + llval: ValueRef, + ty: ty::t) + -> (ValueRef, ValueRef) { + (GEPi(bcx, llval, [0, 0]), + C_uint(bcx.ccx(), simd_size(bcx.ccx().tcx, ty))) +} diff --git a/src/librustc/middle/trans/type_of.rs b/src/librustc/middle/trans/type_of.rs index de3aff0fff613..3ed7950e3f5e1 100644 --- a/src/librustc/middle/trans/type_of.rs +++ b/src/librustc/middle/trans/type_of.rs @@ -161,6 +161,10 @@ pub fn sizing_type_of(cx: &CrateContext, t: ty::t) -> Type { } } + ty::ty_simd(t, n) => { + Type::vector(&type_of(cx, t), n as u64) + } + ty::ty_self(_) | ty::ty_infer(..) | ty::ty_param(..) | ty::ty_err(..) => { cx.tcx.sess.bug(format!("fictitious type {:?} in sizing_type_of()", ty::get(t).sty)) } @@ -288,6 +292,9 @@ pub fn type_of(cx: &CrateContext, t: ty::t) -> Type { adt::incomplete_type_of(cx, repr, name) } } + ty::ty_simd(t, n) => { + Type::vector(&type_of(cx, t), n as u64) + } ty::ty_self(..) => cx.tcx.sess.unimpl("type_of: ty_self"), ty::ty_infer(..) => cx.tcx.sess.bug("type_of with ty_infer"), ty::ty_param(..) => cx.tcx.sess.bug("type_of with ty_param"), diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index c0ad18d9520e3..f055e3954cd1a 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -756,6 +756,10 @@ pub enum sty { ty_struct(DefId, substs), ty_tup(Vec), + // Note it is an error for a ty_simd to have a length of zero. + ty_simd(t, /* primitive */ + uint /* length */), + ty_param(param_ty), // type parameter ty_self(DefId), /* special, implicit `self` type parameter; * def_id is the id of the trait */ @@ -826,6 +830,7 @@ pub enum type_err { terr_integer_as_char, terr_int_mismatch(expected_found), terr_float_mismatch(expected_found), + terr_md_mismatch(expected_found), terr_traits(expected_found), terr_builtin_bounds(expected_found), terr_variadic_mismatch(expected_found) @@ -879,6 +884,39 @@ pub struct IntVid(uint); #[deriving(Clone, Eq, Hash)] pub struct FloatVid(uint); +#[deriving(Clone, Eq, Hash)] +pub struct MDVid(uint); + +#[deriving(Clone, Eq)] +pub enum MDVarValue { + IntMDType(ast::IntTy), + UintMDType(ast::UintTy), + FloatMDType(ast::FloatTy), +} +impl MDVarValue { + pub fn to_t(&self) -> t { + match self { + &IntMDType(ast) => mk_mach_int(ast), + &UintMDType(ast) => mk_mach_uint(ast), + &FloatMDType(ast) => mk_mach_float(ast), + } + } +} + +#[deriving(Clone, Eq, Hash)] +pub enum MDInnerVid { + IntMDInnerVid(IntVid), + FloatMDInnerVid(FloatVid), +} +impl MDInnerVid { + pub fn to_infer(&self, cx: ctxt) -> t { + match self { + &IntMDInnerVid(vid) => mk_int_var(cx, vid), + &FloatMDInnerVid(vid) => mk_float_var(cx, vid), + } + } +} + #[deriving(Clone, Eq, Encodable, Decodable, Hash)] pub struct RegionVid { id: uint @@ -888,7 +926,9 @@ pub struct RegionVid { pub enum InferTy { TyVar(TyVid), IntVar(IntVid), - FloatVar(FloatVid) + FloatVar(FloatVid), + // multiple data + MDVar(MDVid, MDInnerVid, uint), } #[deriving(Clone, Encodable, Decodable, Hash, Show)] @@ -948,6 +988,32 @@ impl fmt::Show for FloatVid { } } +impl Vid for MDVid { + fn to_uint(&self) -> uint { let MDVid(v) = *self; v } +} +impl fmt::Show for MDVid { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f.buf, "", self.to_uint()) + } +} +impl fmt::Show for MDVarValue { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + &IntMDType(t) => t.fmt(f), + &UintMDType(t) => t.fmt(f), + &FloatMDType(t) => t.fmt(f), + } + } +} +impl fmt::Show for MDInnerVid { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + &IntMDInnerVid(vid) => vid.fmt(f), + &FloatMDInnerVid(vid) => vid.fmt(f), + } + } +} + impl Vid for RegionVid { fn to_uint(&self) -> uint { self.id } } @@ -971,6 +1037,13 @@ impl fmt::Show for InferTy { TyVar(ref v) => v.fmt(f), IntVar(ref v) => v.fmt(f), FloatVar(ref v) => v.fmt(f), + MDVar(MDVid(v), inner, count) => { + try!(write!(f.buf, "") + } } } } @@ -1246,6 +1319,9 @@ pub fn mk_t(cx: ctxt, st: sty) -> t { // T -> _|_ is *not* _|_ ! flags &= !(has_ty_bot as uint); } + &ty_simd(t, _) => { + flags |= get(t).flags; + } } let t = ~t_box_ { @@ -1446,6 +1522,12 @@ pub fn mk_struct(cx: ctxt, struct_id: ast::DefId, substs: substs) -> t { // take a copy of substs so that we own the vectors inside mk_t(cx, ty_struct(struct_id, substs)) } +pub fn mk_simd(cx: ctxt, ty: t, count: uint) -> t { + assert!(count != 0 && + (type_is_simd_scalar(cx, ty) || + type_needs_infer(ty))); + mk_t(cx, ty_simd(ty, count)) +} pub fn mk_var(cx: ctxt, v: TyVid) -> t { mk_infer(cx, TyVar(v)) } @@ -1453,6 +1535,13 @@ pub fn mk_int_var(cx: ctxt, v: IntVid) -> t { mk_infer(cx, IntVar(v)) } pub fn mk_float_var(cx: ctxt, v: FloatVid) -> t { mk_infer(cx, FloatVar(v)) } +pub fn mk_md_var(cx: ctxt, + vid: MDVid, + inner: MDInnerVid, + count: uint) -> t { + mk_infer(cx, MDVar(vid, inner, count)) +} + pub fn mk_infer(cx: ctxt, it: InferTy) -> t { mk_t(cx, ty_infer(it)) } pub fn mk_self(cx: ctxt, did: ast::DefId) -> t { mk_t(cx, ty_self(did)) } @@ -1473,7 +1562,7 @@ pub fn maybe_walk_ty(ty: t, f: |t| -> bool) { ty_nil | ty_bot | ty_bool | ty_char | ty_int(_) | ty_uint(_) | ty_float(_) | ty_str(_) | ty_self(_) | ty_infer(_) | ty_param(_) | ty_err => {} - ty_box(ty) | ty_uniq(ty) => maybe_walk_ty(ty, f), + ty_box(ty) | ty_uniq(ty) | ty_simd(ty, _) => maybe_walk_ty(ty, f), ty_vec(ref tm, _) | ty_unboxed_vec(ref tm) | ty_ptr(ref tm) | ty_rptr(_, ref tm) => { maybe_walk_ty(tm.ty, f); @@ -1616,7 +1705,8 @@ pub fn type_is_self(ty: t) -> bool { pub fn type_is_structural(ty: t) -> bool { match get(ty).sty { - ty_struct(..) | ty_tup(_) | ty_enum(..) | ty_closure(_) | ty_trait(..) | + ty_struct(..) | ty_tup(_) | ty_simd(..) | + ty_enum(..) | ty_closure(_) | ty_trait(..) | ty_vec(_, vstore_fixed(_)) | ty_str(vstore_fixed(_)) | ty_vec(_, vstore_slice(_)) | ty_str(vstore_slice(_)) => true, @@ -1626,14 +1716,29 @@ pub fn type_is_structural(ty: t) -> bool { pub fn type_is_sequence(ty: t) -> bool { match get(ty).sty { - ty_str(_) | ty_vec(_, _) => true, + ty_str(_) | ty_vec(_, _) | ty_simd(..) => true, _ => false } } - +pub fn type_is_simd_scalar(_: ctxt, ty: t) -> bool { + match get(ty).sty { + ty_int(_) | ty_float(_) | ty_uint(_) | ty_bool | + ty_infer(IntVar(_)) | ty_infer(FloatVar(_)) => true, + _ => false, + } +} pub fn type_is_simd(cx: ctxt, ty: t) -> bool { match get(ty).sty { ty_struct(did, _) => lookup_simd(cx, did), + ty_simd(..) => true, + ty_infer(MDVar(..)) => true, + _ => false + } +} +pub fn type_is_simd_strict(_: ctxt, ty: t) -> bool { + match get(ty).sty { + ty_simd(..) => true, + ty_infer(MDVar(..)) => true, _ => false } } @@ -1649,6 +1754,7 @@ pub fn sequence_element_type(cx: ctxt, ty: t) -> t { match get(ty).sty { ty_str(_) => return mk_mach_uint(ast::TyU8), ty_vec(mt, _) | ty_unboxed_vec(mt) => return mt.ty, + ty_simd(t, _) => return t, _ => cx.sess.bug("sequence_element_type called on non-sequence value"), } } @@ -1659,7 +1765,9 @@ pub fn simd_type(cx: ctxt, ty: t) -> t { let fields = lookup_struct_fields(cx, did); lookup_field_type(cx, did, fields.get(0).id, substs) } - _ => fail!("simd_type called on invalid type") + ty_simd(t, _) => t, + ty_infer(MDVar(_, inner, _)) => inner.to_infer(cx), + _ => cx.sess.bug(format!("simd_type called on invalid type: {:s}", ty.to_str())) } } @@ -1669,13 +1777,34 @@ pub fn simd_size(cx: ctxt, ty: t) -> uint { let fields = lookup_struct_fields(cx, did); fields.len() } - _ => fail!("simd_size called on invalid type") + ty_simd(_, s) => s, + ty_infer(MDVar(_, _, s)) => s, + _ => cx.sess.bug(format!("simd_size called on invalid type: {:s}", + ::util::ppaux::ty_to_str(cx, ty))), + } +} +pub fn simd_size_span(cx: ctxt, ty: t, sp: Span) -> uint { + match get(ty).sty { + ty_struct(did, _) => { + let fields = lookup_struct_fields(cx, did); + fields.len() + } + ty_simd(_, s) => s, + ty_infer(MDVar(_, _, s)) => s, + _ => cx.sess.span_bug(sp, + format!("simd_size called on invalid type: {:s}", + ::util::ppaux::ty_to_str(cx, ty))), } } -pub fn get_element_type(ty: t, i: uint) -> t { +pub fn get_element_type(cx: ctxt, ty: t, i: uint) -> t { match get(ty).sty { ty_tup(ref ts) => return *ts.get(i), + ty_struct(did, ref substs) => { + let fields = lookup_struct_fields(cx, did); + lookup_field_type(cx, did, fields.as_slice()[i].id, substs) + } + ty_simd(t, _) => t, _ => fail!("get_element_type called on invalid type") } } @@ -2131,7 +2260,7 @@ pub fn type_contents(cx: ctxt, ty: t) -> TypeContents { let result = match get(ty).sty { // Scalar and unique types are sendable, freezable, and durable ty_nil | ty_bot | ty_bool | ty_int(_) | ty_uint(_) | ty_float(_) | - ty_bare_fn(_) | ty::ty_char => { + ty_bare_fn(_) | ty::ty_char | ty_simd(..) => { TC::None } @@ -2415,6 +2544,8 @@ pub fn is_instantiable(cx: ctxt, r_ty: t) -> bool { ty_vec(_, vstore_fixed(0)) => false, // don't need no contents ty_vec(mt, vstore_fixed(_)) => type_requires(cx, seen, r_ty, mt.ty), + ty_simd(t, _) => type_requires(cx, seen, r_ty, t), + ty_nil | ty_bot | ty_bool | @@ -2712,10 +2843,14 @@ pub fn deref(t: t, explicit: bool) -> Option { } // Returns the type and mutability of t[i] -pub fn index(t: t) -> Option { +pub fn index(cx: ctxt, t: t) -> Option { match get(t).sty { ty_vec(mt, _) => Some(mt), ty_str(_) => Some(mt {ty: mk_u8(), mutbl: ast::MutImmutable}), + ty_simd(elmt, _) => Some(mt { ty: elmt, mutbl: ast::MutImmutable }), + ty_infer(MDVar(_, ty, _)) => { + Some(mt { ty: ty.to_infer(cx), mutbl: ast::MutImmutable }) + } _ => None } } @@ -3377,6 +3512,7 @@ pub fn expr_kind(tcx: ctxt, } } } + ast::ExprSimd(_) | ast::ExprSwizzle(..) => RvalueDatumExpr, ast::ExprBreak(..) | ast::ExprAgain(..) | @@ -3490,9 +3626,11 @@ pub fn ty_sort_str(cx: ctxt, t: t) -> ~str { ty_trait(id, _, _, _, _) => format!("trait {}", item_path_str(cx, id)), ty_struct(id, _) => format!("struct {}", item_path_str(cx, id)), ty_tup(_) => ~"tuple", + ty_simd(_, _) => ~"internal simd", ty_infer(TyVar(_)) => ~"inferred type", ty_infer(IntVar(_)) => ~"integral variable", ty_infer(FloatVar(_)) => ~"floating-point variable", + ty_infer(MDVar(..)) => ~"multiple-data variable", ty_param(_) => ~"type parameter", ty_self(_) => ~"self", ty_err => ~"type error" @@ -3637,6 +3775,11 @@ pub fn type_err_to_str(cx: ctxt, err: &type_err) -> ~str { values.expected.to_str(), values.found.to_str()) } + terr_md_mismatch(ref values) => { + format!("expected `{}` but found `{}` in multiple data type", + values.expected.to_str(), + values.found.to_str()) + } terr_variadic_mismatch(ref values) => { format!("expected {} fn but found {} function", if values.expected { "variadic" } else { "non-variadic" }, @@ -4374,7 +4517,7 @@ pub fn is_binopable(cx: ctxt, ty: t, op: ast::BinOp) -> bool { } fn tycat(cx: ctxt, ty: t) -> int { - if type_is_simd(cx, ty) { + if type_is_simd_strict(cx, ty) { return tycat(cx, simd_type(cx, ty)) } match get(ty).sty { @@ -4492,49 +4635,7 @@ impl ExprTyProvider for ctxt { // Returns the repeat count for a repeating vector expression. pub fn eval_repeat_count(tcx: &T, count_expr: &ast::Expr) -> uint { - match const_eval::eval_const_expr_partial(tcx, count_expr) { - Ok(ref const_val) => match *const_val { - const_eval::const_int(count) => if count < 0 { - tcx.ty_ctxt().sess.span_err(count_expr.span, - "expected positive integer for \ - repeat count but found negative integer"); - return 0; - } else { - return count as uint - }, - const_eval::const_uint(count) => return count as uint, - const_eval::const_float(count) => { - tcx.ty_ctxt().sess.span_err(count_expr.span, - "expected positive integer for \ - repeat count but found float"); - return count as uint; - } - const_eval::const_str(_) => { - tcx.ty_ctxt().sess.span_err(count_expr.span, - "expected positive integer for \ - repeat count but found string"); - return 0; - } - const_eval::const_bool(_) => { - tcx.ty_ctxt().sess.span_err(count_expr.span, - "expected positive integer for \ - repeat count but found boolean"); - return 0; - } - const_eval::const_binary(_) => { - tcx.ty_ctxt().sess.span_err(count_expr.span, - "expected positive integer for \ - repeat count but found binary array"); - return 0; - } - }, - Err(..) => { - tcx.ty_ctxt().sess.span_err(count_expr.span, - "expected constant integer for repeat count \ - but found variable"); - return 0; - } - } + const_eval::eval_positive_integer(tcx, count_expr, "repeat count") } // Determine what purity to check a nested function under @@ -4997,6 +5098,11 @@ pub fn hash_crate_independent(tcx: ctxt, t: t, svh: &Svh) -> u64 { byte!(24); mt(&mut state, m); } + ty_simd(t, n) => { + byte!(27); + hash!(&t); + hash!(&n); + } } }); diff --git a/src/librustc/middle/ty_fold.rs b/src/librustc/middle/ty_fold.rs index 769924c02c864..3d9ae4c566a0b 100644 --- a/src/librustc/middle/ty_fold.rs +++ b/src/librustc/middle/ty_fold.rs @@ -189,7 +189,7 @@ pub fn super_fold_sty(this: &mut T, ty::ty_nil | ty::ty_bot | ty::ty_bool | ty::ty_char | ty::ty_int(_) | ty::ty_uint(_) | ty::ty_float(_) | ty::ty_err | ty::ty_infer(_) | - ty::ty_param(..) | ty::ty_self(_) => { + ty::ty_param(..) | ty::ty_self(_) | ty::ty_simd(..) => { (*sty).clone() } } diff --git a/src/librustc/middle/typeck/astconv.rs b/src/librustc/middle/typeck/astconv.rs index 8336fa2d6b5ea..14409f5847b78 100644 --- a/src/librustc/middle/typeck/astconv.rs +++ b/src/librustc/middle/typeck/astconv.rs @@ -646,6 +646,33 @@ pub fn ast_ty_to_ty( // and will not descend into this routine. this.ty_infer(ast_ty.span) } + ast::TySimd(primitive, count) => { + let count = match const_eval::eval_const_expr_partial(&tcx, count) { + Ok(ref r) => { + match *r { + const_eval::const_int(0) | const_eval::const_uint(0) => { + tcx.sess.span_fatal( + ast_ty.span, "expected integer constant expr => 0 \ + for simd length"); + } + const_eval::const_int(i) => i as uint, + const_eval::const_uint(i) => i as uint, + _ => { + tcx.sess.span_fatal( + ast_ty.span, "expected integer constant expr for simd length"); + } + } + } + Err(ref r) => { + tcx.sess.span_fatal( + ast_ty.span, + format!("expected constant expr for simd length: {}", *r)); + } + }; + ast_ty_to_prim_ty(tcx, primitive).map_or(ty::mk_err(), |ty| { + ty::mk_simd(tcx, ty, count) + }) + } }); let mut ast_ty_to_ty_cache = tcx.ast_ty_to_ty_cache.borrow_mut(); diff --git a/src/librustc/middle/typeck/check/method.rs b/src/librustc/middle/typeck/check/method.rs index 0d37904445ff6..be8acc54724e8 100644 --- a/src/librustc/middle/typeck/check/method.rs +++ b/src/librustc/middle/typeck/check/method.rs @@ -838,10 +838,11 @@ impl<'a> LookupContext<'a> { ty_bare_fn(..) | ty_box(..) | ty_uniq(..) | ty_rptr(..) | ty_infer(IntVar(_)) | ty_infer(FloatVar(_)) | + ty_infer(MDVar(..)) | ty_self(_) | ty_param(..) | ty_nil | ty_bot | ty_bool | ty_char | ty_int(..) | ty_uint(..) | ty_float(..) | ty_enum(..) | ty_ptr(..) | ty_struct(..) | ty_tup(..) | - ty_str(..) | ty_vec(..) | ty_trait(..) | ty_closure(..) => { + ty_str(..) | ty_vec(..) | ty_trait(..) | ty_closure(..) | ty_simd(..) => { self.search_for_some_kind_of_autorefd_method( AutoPtr, autoderefs, [MutImmutable, MutMutable], |m,r| ty::mk_rptr(tcx, r, ty::mt {ty:self_ty, mutbl:m})) diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index 51efcb7d1c387..9aba8ccbab1a7 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -2048,8 +2048,16 @@ fn check_expr_with_unifier(fcx: @FnCtxt, let result_t = match op { ast::BiEq | ast::BiNe | ast::BiLt | ast::BiLe | ast::BiGe | - ast::BiGt => ty::mk_bool(), - _ => lhs_t + ast::BiGt => { + if ty::type_is_simd_strict(tcx, lhs_t) { + ty::mk_simd(tcx, + ty::mk_bool(), + ty::simd_size_span(tcx, lhs_t, expr.span)) + } else { + ty::mk_bool() + } + } + _ => lhs_t, }; fcx.write_ty(expr.id, result_t); @@ -3144,6 +3152,192 @@ fn check_expr_with_unifier(fcx: @FnCtxt, "structure constructor does not name a structure type"); } } + } + ast::ExprSimd(ref elements) => { + if elements.len() == 0 { + tcx.sess.span_err(expr.span, + "SIMD vectors cannot have a length of zero"); + fcx.write_error(id); + } else { + let mut err_field = false; + let mut bot_field = false; + let (head_ty, offset) = match expected.and_then(|ex_ty| { + resolve_type(fcx.infcx(), + ex_ty, + force_tvar).ok() + }) { + Some(ty) if ty::type_is_simd(tcx, ty) => (ty::simd_type(tcx, ty), 0), + None if expected.map_or(false, { + |ex_ty| ty::type_is_simd(tcx, ex_ty) } ) => { + (ty::simd_type(tcx, expected.unwrap()), 0) + } + _ => { + let &head_expr = elements + .iter() + .next() + .expect("wat"); + check_expr(fcx, head_expr); + let head_ty = fcx.expr_ty(head_expr); + let head_ty = structurally_resolved_type(fcx, + head_expr.span, + head_ty); + (head_ty, 1) + } + }; + + for &e in elements.iter().skip(offset) { + check_expr_has_type(fcx, e, head_ty); + let t = fcx.expr_ty(e); + err_field = err_field || ty::type_is_error(t); + bot_field = bot_field || ty::type_is_bot(t); + } + if err_field { + fcx.write_error(id); + } else if bot_field { + fcx.write_bot(id); + } else { + let ty = match ty::get(head_ty).sty { + ty::ty_infer(ty::TyVar(_)) => tcx.sess.span_bug(expr.span, + "unexpected TyVar"), + ty::ty_infer(ty::MDVar(..)) => tcx.sess.span_bug(expr.span, + "unexpected MDVar"), + ty::ty_infer(ty::IntVar(vid)) => + fcx.infcx().next_md_var(ty::IntMDInnerVid(vid), elements.len()), + ty::ty_infer(ty::FloatVar(vid)) => + fcx.infcx().next_md_var(ty::FloatMDInnerVid(vid), elements.len()), + _ => ty::mk_simd(tcx, + head_ty, + elements.len()), + }; + fcx.write_ty(id, ty); + } + } + } + ast::ExprSwizzle(left, opt_right, ref mask) => { + check_expr(fcx, left); + let raw_left_ty = fcx.expr_ty(left); + let raw_right_ty = opt_right.map(|right_expr| { + check_expr(fcx, right_expr); + fcx.expr_ty(right_expr) + }); + if ty::type_is_error(raw_left_ty) || + raw_right_ty.map_or(false, |t| ty::type_is_error(t) ) { + fcx.write_error(id); + } else if ty::type_is_bot(raw_left_ty) || + raw_right_ty.map_or(false, |t| ty::type_is_error(t) ) { + fcx.write_bot(id); + } else { + fn deref(fcx: @FnCtxt, expr: &ast::Expr, ty: ty::t) -> (ty::t, Option) { + let (ty, ads, inner) = + autoderef(fcx, expr.span, ty, + Some(expr.id), + NoPreference, + |t, _| { + match ty::type_is_simd(fcx.tcx(), t) { + true => Some(ty::simd_type(fcx.tcx(), t)), + false => None, + } + }); + fcx.write_autoderef_adjustment(expr.id, ads); + let ty = match ty::get(ty).sty { + ty::ty_infer(ty::TyVar(..)) => { + structurally_resolved_type(fcx, + expr.span, + ty) + } + _ => ty, + }; + (ty, inner) + } + let (left_ty, left_inner) = deref(fcx, left, raw_left_ty); + let right_ty = raw_right_ty.map(|raw_ty| { + deref(fcx, opt_right.unwrap(), raw_ty) + }); + + + let mask = mask.iter().map(|&m| { + check_expr(fcx, m); + (const_eval::eval_positive_integer(fcx, m, "swizzle mask"), m) + }).to_owned_vec(); + + if !ty::type_is_simd(tcx, left_ty) { + fcx.type_error_message(left.span, + |actual| { + format!("cannot swizzle non-SIMD type `{}`", + actual) + }, + left_ty, + None); + fcx.write_error(id); + } else if !check_possible_simd_type(tcx, + left.span, + left_ty) { + fcx.write_error(id); + } else if right_ty.map_or(false, |(right_ty, right_inner)| { + if !ty::type_is_simd(tcx, right_ty) { + fcx.type_error_message(opt_right.unwrap().span, + |actual| { + format!("cannot swizzle non-SIMD type `{}`", + actual) + }, + right_ty, + None); + true + } else if !check_possible_simd_type(tcx, + opt_right.unwrap().span, + right_ty) { + true + } else if left_inner != right_inner { + fcx.type_error_message(opt_right.unwrap().span, + |actual| { + format!("swizzle operands must be homogeneous \ + with respect to their element types: \ + expected `{}` but found `{}`", + left_inner.unwrap().to_str(), actual) + }, + right_inner.unwrap(), + None); + true + } else { + false + } + }) { + fcx.write_error(id); + } else { + let size = ty::simd_size_span(tcx, left_ty, left.span) + + right_ty.map_or(0, |(r, _)| { + ty::simd_size_span(tcx, + r, + opt_right.unwrap().span) + }); + if mask.iter().all(|&(m, expr)| { + if m >= size { + tcx.sess.span_err(expr.span, + format!("invalid mask index in \ + SIMD swizzle: `{:}`", + m)); + false + } else { true } + }) { + if ty::type_needs_infer(left_ty) { + let ty = match ty::get(left_ty).sty { + ty::ty_infer(ty::MDVar(_, inner, _)) => { + fcx.infcx().next_md_var(inner, mask.len()) + } + _ => unreachable!(), + }; + fcx.write_ty(id, ty); + } else { + fcx.write_ty(id, + ty::mk_simd(tcx, + ty::simd_type(tcx, left_ty), + mask.len())); + } + } else { + fcx.write_error(id); + } + } + } } ast::ExprField(base, field, ref tys) => { check_field(fcx, expr, lvalue_pref, base, field.name, tys.as_slice()); @@ -3160,7 +3354,7 @@ fn check_expr_with_unifier(fcx: @FnCtxt, } else { let (base_t, autoderefs, field_ty) = autoderef(fcx, expr.span, raw_base_t, Some(base.id), - lvalue_pref, |base_t, _| ty::index(base_t)); + lvalue_pref, |base_t, _| ty::index(tcx, base_t)); match field_ty { Some(mt) => { require_integral(fcx, idx.span, idx_t); @@ -3461,31 +3655,46 @@ pub fn check_instantiable(tcx: ty::ctxt, pub fn check_simd(tcx: ty::ctxt, sp: Span, id: ast::NodeId) { let t = ty::node_id_to_type(tcx, id); + check_possible_simd_type(tcx, sp, t); +} +pub fn check_possible_simd_type(tcx: ty::ctxt, sp: Span, t: ty::t) -> bool { if ty::type_needs_subst(t) { tcx.sess.span_err(sp, "SIMD vector cannot be generic"); - return; + return false; } match ty::get(t).sty { ty::ty_struct(did, ref substs) => { let fields = ty::lookup_struct_fields(tcx, did); if fields.is_empty() { tcx.sess.span_err(sp, "SIMD vector cannot be empty"); - return; + return false; } let e = ty::lookup_field_type(tcx, did, fields.get(0).id, substs); if !fields.iter().all( |f| ty::lookup_field_type(tcx, did, f.id, substs) == e) { tcx.sess.span_err(sp, "SIMD vector should be homogeneous"); - return; + return false; } - if !ty::type_is_machine(e) { + if !ty::type_is_simd_scalar(tcx, e) { tcx.sess.span_err(sp, "SIMD vector element type should be \ machine type"); - return; + return false; + } + } + ty::ty_simd(_, 0) | ty::ty_infer(ty::MDVar(_, _, 0)) => { + tcx.sess.span_err(sp, "SIMD vector cannot be empty"); + return false; + } + ty::ty_simd(inner, _) => { + if !ty::type_is_simd_scalar(tcx, inner) { + tcx.sess.span_err(sp, "SIMD vector element type should be \ + machine type"); + return false; } } _ => () } + return true; } pub fn check_enum_variants(ccx: @CrateCtxt, diff --git a/src/librustc/middle/typeck/coherence.rs b/src/librustc/middle/typeck/coherence.rs index ca5befa8d4e06..a08a30cbda705 100644 --- a/src/librustc/middle/typeck/coherence.rs +++ b/src/librustc/middle/typeck/coherence.rs @@ -21,7 +21,7 @@ use middle::ty::get; use middle::ty::{ImplContainer, lookup_item_type, subst}; use middle::ty::{substs, t, ty_bool, ty_char, ty_bot, ty_box, ty_enum, ty_err}; use middle::ty::{ty_str, ty_vec, ty_float, ty_infer, ty_int, ty_nil}; -use middle::ty::{ty_param, ty_param_bounds_and_ty, ty_ptr}; +use middle::ty::{ty_param, ty_param_bounds_and_ty, ty_ptr, ty_simd}; use middle::ty::{ty_rptr, ty_self, ty_struct, ty_trait, ty_tup}; use middle::ty::{ty_uint, ty_uniq, ty_bare_fn, ty_closure}; use middle::ty::{ty_unboxed_vec, type_is_ty_var}; @@ -85,7 +85,7 @@ fn get_base_type(inference_context: &InferCtxt, ty_str(..) | ty_vec(..) | ty_bare_fn(..) | ty_closure(..) | ty_tup(..) | ty_infer(..) | ty_param(..) | ty_self(..) | ty_unboxed_vec(..) | ty_err | ty_box(_) | - ty_uniq(_) | ty_ptr(_) | ty_rptr(_, _) => { + ty_uniq(_) | ty_ptr(_) | ty_rptr(_, _) | ty_simd(..) => { debug!("(getting base type) no base type; found {:?}", get(original_type).sty); None diff --git a/src/librustc/middle/typeck/infer/combine.rs b/src/librustc/middle/typeck/infer/combine.rs index 32b6da40b0855..7e40615db431b 100644 --- a/src/librustc/middle/typeck/infer/combine.rs +++ b/src/librustc/middle/typeck/infer/combine.rs @@ -47,7 +47,8 @@ // now. -use middle::ty::{FloatVar, FnSig, IntVar, TyVar}; +use middle::ty::{FloatVar, FnSig, IntVar, TyVar, MDVar}; +use middle::ty::{IntMDInnerVid, FloatMDInnerVid}; use middle::ty::{IntType, UintType, substs}; use middle::ty::{BuiltinBounds}; use middle::ty; @@ -474,6 +475,88 @@ pub fn super_tys(this: &C, a: ty::t, b: ty::t) -> cres { unify_float_variable(this, !this.a_is_expected(), v_id, v) } + // Relate multiple data variables to other multiple data types. + // we assume the lengths were already checked. + (&ty::ty_infer(MDVar(vid, FloatMDInnerVid(inner_vid), left_len)), + &ty::ty_simd(simd_ty, right_len)) + if ty::type_is_fp(simd_ty) && left_len == right_len => { + match ty::get(simd_ty).sty { + ty::ty_float(ast_ty) => unify_md_variable(this, + this.a_is_expected(), + vid, + FloatMDInnerVid(inner_vid), + ty::FloatMDType(ast_ty), + left_len), + _ => unreachable!(), + } + } + (&ty::ty_simd(simd_ty, left_len), + &ty::ty_infer(MDVar(vid, FloatMDInnerVid(inner_vid), right_len))) + if ty::type_is_fp(simd_ty) && left_len == right_len => { + match ty::get(simd_ty).sty { + ty::ty_float(ast_ty) => unify_md_variable(this, + !this.a_is_expected(), + vid, + FloatMDInnerVid(inner_vid), + ty::FloatMDType(ast_ty), + left_len), + _ => unreachable!(), + } + } + (&ty::ty_infer(MDVar(vid, IntMDInnerVid(inner_vid), left_len)), + &ty::ty_simd(simd_ty, right_len)) + if ty::type_is_integral(simd_ty) && left_len == right_len => { + match ty::get(simd_ty).sty { + ty::ty_int(ast_ty) => unify_md_variable(this, + this.a_is_expected(), + vid, + IntMDInnerVid(inner_vid), + ty::IntMDType(ast_ty), + left_len), + ty::ty_uint(ast_ty) => unify_md_variable(this, + this.a_is_expected(), + vid, + IntMDInnerVid(inner_vid), + ty::UintMDType(ast_ty), + left_len), + _ => unreachable!(), + } + }, + (&ty::ty_simd(simd_ty, left_len), + &ty::ty_infer(MDVar(vid, IntMDInnerVid(inner_vid), right_len))) + if ty::type_is_integral(simd_ty) && left_len == right_len => { + match ty::get(simd_ty).sty { + ty::ty_int(ast_ty) => unify_md_variable(this, + !this.a_is_expected(), + vid, + IntMDInnerVid(inner_vid), + ty::IntMDType(ast_ty), + left_len), + ty::ty_uint(ast_ty) => unify_md_variable(this, + !this.a_is_expected(), + vid, + IntMDInnerVid(inner_vid), + ty::UintMDType(ast_ty), + left_len), + _ => unreachable!(), + } + } + (&ty::ty_infer(MDVar(a_id, FloatMDInnerVid(_), left_len)), + &ty::ty_infer(MDVar(b_id, FloatMDInnerVid(_), right_len))) + if left_len == right_len => { + if_ok!(this.infcx().simple_vars(this.a_is_expected(), + a_id, b_id)); + Ok(a) + } + (&ty::ty_infer(MDVar(a_id, IntMDInnerVid(_), left_len)), + &ty::ty_infer(MDVar(b_id, IntMDInnerVid(_), right_len))) + if left_len == right_len => { + if_ok!(this.infcx().simple_vars(this.a_is_expected(), + a_id, b_id)); + Ok(a) + } + + (&ty::ty_char, _) | (&ty::ty_nil, _) | (&ty::ty_bool, _) | @@ -600,4 +683,34 @@ pub fn super_tys(this: &C, a: ty::t, b: ty::t) -> cres { if_ok!(this.infcx().simple_var_t(vid_is_expected, vid, val)); Ok(ty::mk_mach_float(val)) } + fn unify_md_variable( + this: &C, + vid_is_expected: bool, + vid: ty::MDVid, + inner_vid: ty::MDInnerVid, + val: ty::MDVarValue, + len: uint) -> cres + { + try!(this.infcx().simple_var_t(vid_is_expected, vid, val)); + let inner = match inner_vid { + ty::IntMDInnerVid(inner_vid) => + try!(unify_integral_variable(this, + vid_is_expected, + inner_vid, + match val { + ty::IntMDType(ast_ty) => IntType(ast_ty), + ty::UintMDType(ast_ty) => UintType(ast_ty), + _ => unreachable!(), + })), + ty::FloatMDInnerVid(inner_vid) => + try!(unify_float_variable(this, + vid_is_expected, + inner_vid, + match val { + ty::FloatMDType(ast_ty) => ast_ty, + _ => unreachable!(), + })), + }; + Ok(ty::mk_simd(this.infcx().tcx, inner, len)) + } } diff --git a/src/librustc/middle/typeck/infer/mod.rs b/src/librustc/middle/typeck/infer/mod.rs index e10aa8159d0e4..7955b0b1c39c3 100644 --- a/src/librustc/middle/typeck/infer/mod.rs +++ b/src/librustc/middle/typeck/infer/mod.rs @@ -12,7 +12,7 @@ #[allow(non_camel_case_types)]; -pub use middle::ty::IntVarValue; +pub use middle::ty::{IntVarValue, MDVarValue}; pub use middle::typeck::infer::resolve::resolve_and_force_all_but_regions; pub use middle::typeck::infer::resolve::{force_all, not_regions}; pub use middle::typeck::infer::resolve::{force_ivar}; @@ -94,6 +94,10 @@ pub struct InferCtxt { Option>>, float_var_counter: Cell, + md_var_bindings: RefCell>>, + md_var_counter: Cell, + // For region variables. region_vars: RegionVarBindings, } @@ -280,6 +284,9 @@ pub fn new_infer_ctxt(tcx: ty::ctxt) -> InferCtxt { float_var_bindings: RefCell::new(new_ValsAndBindings()), float_var_counter: Cell::new(0), + md_var_bindings: RefCell::new(new_ValsAndBindings()), + md_var_counter: Cell::new(0), + region_vars: RegionVarBindings(tcx), } } @@ -655,6 +662,33 @@ impl InferCtxt { ty::mk_float_var(self.tcx, self.next_float_var_id()) } + pub fn next_int_md_var(&self, count: uint) -> ty::t { + ty::mk_md_var(self.tcx, + self.next_md_var_id(), + ty::IntMDInnerVid(self.next_int_var_id()), + count) + } + pub fn next_float_md_var(&self, count: uint) -> ty::t { + ty::mk_md_var(self.tcx, + self.next_md_var_id(), + ty::FloatMDInnerVid(self.next_float_var_id()), + count) + } + pub fn next_md_var(&self, inner: ty::MDInnerVid, count: uint) -> ty::t { + ty::mk_md_var(self.tcx, + self.next_md_var_id(), + inner, + count) + } + pub fn next_md_var_id(&self) -> ty::MDVid { + let mut var_counter = self.md_var_counter.get(); + let mut var_bindings = self.md_var_bindings.borrow_mut(); + let result = ty::MDVid(next_simple_var(&mut var_counter, + var_bindings.get())); + self.md_var_counter.set(var_counter); + result + } + pub fn next_region_var(&self, origin: RegionVariableOrigin) -> ty::Region { ty::ReInfer(ty::ReVar(self.region_vars.new_region_var(origin))) } diff --git a/src/librustc/middle/typeck/infer/resolve.rs b/src/librustc/middle/typeck/infer/resolve.rs index d4ee567ba41b7..869a6ade9d367 100644 --- a/src/librustc/middle/typeck/infer/resolve.rs +++ b/src/librustc/middle/typeck/infer/resolve.rs @@ -61,18 +61,20 @@ use util::ppaux::ty_to_str; use std::vec_ng::Vec; use syntax::ast; -pub static resolve_nested_tvar: uint = 0b0000000001; -pub static resolve_rvar: uint = 0b0000000010; -pub static resolve_ivar: uint = 0b0000000100; -pub static resolve_fvar: uint = 0b0000001000; -pub static resolve_fnvar: uint = 0b0000010000; -pub static resolve_all: uint = 0b0000011111; -pub static force_tvar: uint = 0b0000100000; -pub static force_rvar: uint = 0b0001000000; -pub static force_ivar: uint = 0b0010000000; -pub static force_fvar: uint = 0b0100000000; -pub static force_fnvar: uint = 0b1000000000; -pub static force_all: uint = 0b1111100000; +pub static resolve_nested_tvar: uint = 0b000000000001; +pub static resolve_rvar: uint = 0b000000000010; +pub static resolve_ivar: uint = 0b000000000100; +pub static resolve_fvar: uint = 0b000000001000; +pub static resolve_mdvar: uint = 0b000000010000; +pub static resolve_fnvar: uint = 0b000000100000; +pub static resolve_all: uint = 0b000000111111; +pub static force_tvar: uint = 0b00000100000; +pub static force_rvar: uint = 0b00001000000; +pub static force_ivar: uint = 0b00010000000; +pub static force_fvar: uint = 0b00100000000; +pub static force_mdvar: uint = 0b01000000000; +pub static force_fnvar: uint = 0b10000000000; +pub static force_all: uint = 0b11111100000; pub static not_regions: uint = !(force_rvar | resolve_rvar); @@ -174,6 +176,9 @@ impl<'a> ResolveState<'a> { ty::ty_infer(FloatVar(vid)) => { self.resolve_float_var(vid) } + ty::ty_infer(ty::MDVar(vid, inner, count)) => { + self.resolve_md_var(vid, inner, count) + } _ => { if self.modes & resolve_all == 0 { // if we are only resolving top-level type @@ -290,4 +295,44 @@ impl<'a> ResolveState<'a> { } } } + pub fn resolve_md_var(&mut self, + vid: ty::MDVid, + inner: ty::MDInnerVid, + count: uint) -> ty::t { + if !self.should(resolve_mdvar) { + return ty::mk_md_var(self.infcx.tcx, vid, inner, count); + } + + let node = self.infcx.get(vid); + match node.possible_types { + Some(ty::IntMDType(t)) => ty::mk_simd(self.infcx.tcx, ty::mk_mach_int(t), count), + Some(ty::UintMDType(t)) => ty::mk_simd(self.infcx.tcx, ty::mk_mach_uint(t), count), + Some(ty::FloatMDType(t)) => ty::mk_simd(self.infcx.tcx, ty::mk_mach_float(t), count), + None => { + if self.should(force_mdvar) { + let (inner, val) = match inner { + ty::FloatMDInnerVid(inner_vid) => { + let node = self.infcx.get(inner_vid); + let ast_ty = node.possible_types.unwrap_or(ast::TyF64); + (ty::mk_mach_float(ast_ty), ty::FloatMDType(ast_ty)) + } + ty::IntMDInnerVid(inner_vid) => { + let node = self.infcx.get(inner_vid); + let ast_ty = node.possible_types.unwrap_or(IntType(ast::TyI)); + match ast_ty { + IntType(ast_ty) => + (ty::mk_mach_int(ast_ty), ty::IntMDType(ast_ty)), + UintType(ast_ty) => + (ty::mk_mach_uint(ast_ty), ty::UintMDType(ast_ty)), + } + } + }; + self.infcx.set(vid, Root(Some(val), node.rank)); + ty::mk_simd(self.infcx.tcx, inner, count) + } else { + ty::mk_md_var(self.infcx.tcx, vid, inner, count) + } + } + } + } } diff --git a/src/librustc/middle/typeck/infer/to_str.rs b/src/librustc/middle/typeck/infer/to_str.rs index aa3e3a23182e0..201547e308d01 100644 --- a/src/librustc/middle/typeck/infer/to_str.rs +++ b/src/librustc/middle/typeck/infer/to_str.rs @@ -92,3 +92,8 @@ impl InferStr for ty::TraitRef { trait_ref_to_str(cx.tcx, self) } } +impl InferStr for ty::MDVarValue { + fn inf_str(&self, _cx: &InferCtxt) -> ~str { + self.to_str() + } +} diff --git a/src/librustc/middle/typeck/infer/unify.rs b/src/librustc/middle/typeck/infer/unify.rs index b58fa905ca4eb..495648546502f 100644 --- a/src/librustc/middle/typeck/infer/unify.rs +++ b/src/librustc/middle/typeck/infer/unify.rs @@ -309,3 +309,16 @@ impl SimplyUnifiable for ast::FloatTy { return ty::terr_float_mismatch(err); } } + +impl UnifyVid> for ty::MDVid { + fn appropriate_vals_and_bindings<'v>(infcx: &'v InferCtxt) + -> &'v RefCell>> { + return &infcx.md_var_bindings; + } +} + +impl SimplyUnifiable for ty::MDVarValue { + fn to_type_err(err: expected_found) -> ty::type_err { + return ty::terr_md_mismatch(err); + } +} diff --git a/src/librustc/middle/typeck/variance.rs b/src/librustc/middle/typeck/variance.rs index 3712f04635878..6888f1392c17e 100644 --- a/src/librustc/middle/typeck/variance.rs +++ b/src/librustc/middle/typeck/variance.rs @@ -667,6 +667,9 @@ impl<'a> ConstraintContext<'a> { self.add_constraints_from_ty(subty, variance); } } + ty::ty_simd(t, _) => { + self.add_constraints_from_ty(t, variance); + } ty::ty_enum(def_id, ref substs) | ty::ty_struct(def_id, ref substs) => { diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index 2f06ab67a3bab..50a76b8cba0d2 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -16,7 +16,7 @@ use middle::ty::{BrFresh, ctxt}; use middle::ty::{mt, t, param_ty}; use middle::ty::{ReFree, ReScope, ReInfer, ReStatic, Region, ReEmpty}; -use middle::ty::{ty_bool, ty_char, ty_bot, ty_box, ty_struct, ty_enum}; +use middle::ty::{ty_bool, ty_char, ty_bot, ty_box, ty_struct, ty_enum, ty_simd}; use middle::ty::{ty_err, ty_str, ty_vec, ty_float, ty_bare_fn, ty_closure}; use middle::ty::{ty_nil, ty_param, ty_ptr, ty_rptr, ty_self, ty_tup}; use middle::ty::{ty_uniq, ty_trait, ty_int, ty_uint, ty_unboxed_vec, ty_infer}; @@ -453,6 +453,7 @@ pub fn ty_to_str(cx: ctxt, typ: t) -> ~str { let strs = elems.map(|elem| ty_to_str(cx, *elem)); ~"(" + strs.connect(",") + ")" } + ty_simd(t, count) => format!("<{:s}, ..{:u}>", ty_to_str(cx, t), count), ty_closure(ref f) => { closure_to_str(cx, f) } diff --git a/src/librustdoc/clean.rs b/src/librustdoc/clean.rs index c27725fd5852a..609e6217148cd 100644 --- a/src/librustdoc/clean.rs +++ b/src/librustdoc/clean.rs @@ -705,6 +705,7 @@ impl Clean for ast::Ty { TyClosure(ref c) => Closure(~c.clean()), TyBareFn(ref barefn) => BareFunction(~barefn.clean()), TyBot => Bottom, + TySimd(t, ref count) => FixedVector(~t.clean(), count.span.to_str()), ref x => fail!("Unimplemented type {:?}", x), } } diff --git a/src/libsimd/lib.rs b/src/libsimd/lib.rs new file mode 100644 index 0000000000000..9f6788a9713d3 --- /dev/null +++ b/src/libsimd/lib.rs @@ -0,0 +1,214 @@ +// Copyright 2012-2014 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. + +#[crate_id = "simd#0.10-pre"]; +#[crate_type = "rlib"]; +#[license = "MIT/ASL2"]; +#[comment = "A link-time library to facilitate access to SIMD types & operations"]; + +#[feature(macro_registrar, simd, phase, macro_rules)]; +#[allow(experimental)]; +#[experimental]; + +#[phase(syntax)] +extern crate simd_syntax; + +use std::iter; +use std::container::Container; +use std::cast; + +pub trait Simd { + fn every(self, value: PrimitiveTy) -> bool; + fn any(self, value: PrimitiveTy) -> bool; + + fn iter<'a>(&'a self) -> Items<'a, PrimitiveTy>; + fn mut_iter<'a>(&'a mut self) -> MutItems<'a, PrimitiveTy>; + + fn as_slice<'a>(&'a self) -> &'a [PrimitiveTy]; + fn as_mut_slice<'a>(&'a mut self) -> &'a mut [PrimitiveTy]; + + fn len(&self) -> uint; +} +pub trait BoolSimd { + fn every_true(self) -> bool; + fn every_false(self) -> bool; + fn any_true(self) -> bool; + fn any_false(self) -> bool; +} +impl> BoolSimd for T { + #[inline] fn every_true(self) -> bool { self.every(true) } + #[inline] fn every_false(self) -> bool { self.every(false) } + #[inline] fn any_true(self) -> bool { self.any(true) } + #[inline] fn any_false(self) -> bool { self.any(false) } +} + +#[deriving(Eq, Clone)] +pub struct Items<'a, ElemT> { + priv vec: *ElemT, + priv pos: uint, + priv len: uint, +} +#[deriving(Eq, Clone)] +pub struct MutItems<'a, ElemT> { + priv vec: *mut ElemT, + priv pos: uint, + priv len: uint, +} + +impl<'a, ElemT> iter::Iterator<&'a ElemT> for Items<'a, ElemT> { + fn next(&mut self) -> Option<&'a ElemT> { + if self.pos >= self.len { + None + } else { + self.pos += 1; + Some(unsafe { cast::transmute(self.vec.offset((self.pos - 1) as int)) }) + } + } +} +impl<'a, ElemT> iter::Iterator<&'a mut ElemT> for MutItems<'a, ElemT> { + fn next(&mut self) -> Option<&'a mut ElemT> { + if self.pos >= self.len { + None + } else { + self.pos += 1; + Some(unsafe { cast::transmute(self.vec.offset((self.pos - 1) as int)) }) + } + } +} + +macro_rules! _def( + ($ident:ident = ($prim:ty, ..$len:expr)) => { + def_type_simd!( #[allow(non_camel_case_types)] + pub type $ident = <$prim, ..$len>) + impl Simd<$prim> for $ident { + #[inline] fn every(self, value: $prim) -> bool { + for i in iter::range(0u, $len as uint) { + if self[i] != value { + return false; + } + } + return true; + } + #[inline] fn any(self, value: $prim) -> bool { + for i in iter::range(0u, $len as uint) { + if self[i] == value { + return true; + } + } + return false; + } + + fn iter<'a>(&'a self) -> Items<'a, $prim> { + Items { + vec: unsafe { cast::transmute(self) }, + pos: 0, + len: $len, + } + } + fn mut_iter<'a>(&'a mut self) -> MutItems<'a, $prim> { + MutItems { + vec: unsafe { cast::transmute(self) }, + pos: 0, + len: $len, + } + } + + fn as_slice<'a>(&'a self) -> &'a [$prim] { + use std::raw::Slice; + unsafe { + cast::transmute_copy(&Slice { + data: self as *$ident as *$prim, + len: $len, + }) + } + } + fn as_mut_slice<'a>(&'a mut self) -> &'a mut [$prim] { + use std::raw::Slice; + unsafe { + cast::transmute_copy(&Slice { + data: cast::transmute::<*mut $prim, *$prim> + (self as *mut $ident as *mut $prim), + len: $len, + }) + } + } + + #[inline(always)] fn len(&self) -> uint { $len } + } + } +) +_def!(boolx2 = (bool, ..2 )) +_def!(boolx4 = (bool, ..4 )) +_def!(boolx8 = (bool, ..8 )) +_def!(boolx16 = (bool, ..16)) +_def!(boolx32 = (bool, ..32)) +_def!(boolx64 = (bool, ..64)) + +_def!(i8x16 = (i8, ..16)) +_def!(i8x32 = (i8, ..32)) +_def!(i8x64 = (i8, ..64)) +_def!(u8x16 = (u8, ..16)) +_def!(u8x32 = (u8, ..32)) +_def!(u8x64 = (u8, ..64)) + +_def!(i16x8 = (i16, ..8 )) +_def!(i16x16 = (i16, ..16)) +_def!(i16x32 = (i16, ..32)) +_def!(u16x8 = (u16, ..8 )) +_def!(u16x16 = (u16, ..16)) +_def!(u16x32 = (u16, ..32)) + +_def!(i32x4 = (i32, ..4 )) +_def!(i32x8 = (i32, ..8 )) +_def!(i32x16 = (i32, ..16)) +_def!(u32x4 = (u32, ..4 )) +_def!(u32x8 = (u32, ..8 )) +_def!(u32x16 = (u32, ..16)) + +_def!(i64x2 = (i64, ..2 )) +_def!(i64x4 = (i64, ..4 )) +_def!(i64x8 = (i64, ..8 )) +_def!(u64x2 = (u64, ..2 )) +_def!(u64x4 = (u64, ..4 )) +_def!(u64x8 = (u64, ..8 )) + +_def!(f32x4 = (f32, ..4 )) +_def!(f32x8 = (f32, ..8 )) +_def!(f32x16 = (f32, ..16)) +_def!(f64x2 = (f64, ..2 )) +_def!(f64x4 = (f64, ..4 )) +_def!(f64x8 = (f64, ..8 )) + +#[cfg(test)] +mod test { + use super::Simd; + #[test] + fn as_slice() { + let v: super::i32x4 = gather_simd!(1, 2, 3, 4); + assert!(v.as_slice() == [1, 2, 3, 4]); + + let v: super::i32x4 = gather_simd!(1, 2, 3, 4); + let v = swizzle_simd!(v -> (3, 2, 1, 0)); + assert!(v.as_slice() == [4, 3, 2, 1]); + } + #[test] + fn as_mut_slice() { + let mut v: super::i32x4 = gather_simd!(1, 2, 3, 4); + assert!(v.as_slice() == [1, 2, 3, 4]); + v.as_mut_slice()[0] = 10; + assert!(v.as_slice() == [10, 2, 3, 4]); + + let v: super::i32x4 = gather_simd!(1, 2, 3, 4); + let mut v = swizzle_simd!(v -> (3, 2, 1, 0)); + assert!(v.as_slice() == [4, 3, 2, 1]); + v.as_mut_slice()[0] = 10; + assert!(v.as_slice() == [10, 3, 2, 1]); + } +} diff --git a/src/libsimd_syntax/lib.rs b/src/libsimd_syntax/lib.rs new file mode 100644 index 0000000000000..9dbc5f83779a1 --- /dev/null +++ b/src/libsimd_syntax/lib.rs @@ -0,0 +1,221 @@ +// Copyright 2012-2014 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. + +#[crate_id = "simd_syntax#0.10-pre"]; +#[crate_type = "dylib"]; +#[license = "MIT/ASL2"]; +#[comment = "A parse-time library to facilitate access to SIMD types & operations"]; + +#[feature(macro_registrar, managed_boxes)]; +#[allow(deprecated_owned_vector)]; + +extern crate syntax; + +use syntax::ast::{Expr, ExprSwizzle, ExprSimd, DUMMY_NODE_ID, Name}; +use syntax::ast::{P, Item, TokenTree, Ty, TySimd}; +use syntax::{ast, ast_util}; +use syntax::codemap::{respan, mk_sp}; +use syntax::ext::base::{get_exprs_from_tts, SyntaxExtension, BasicMacroExpander}; +use syntax::ext::base::{ExtCtxt, MacResult, MRExpr, MRItem, NormalTT}; +use syntax::codemap::{Span, Spanned}; +use syntax::parse::{parser, token, tts_to_parser}; +use syntax::parse::token::keywords; +use syntax::parse::common::{seq_sep_trailing_allowed}; +use syntax::parse::attr::ParserAttr; + +use std::option::{Option, Some, None}; +use std::vec_ng::Vec; + +#[macro_registrar] +pub fn macro_registrar(register: |Name, SyntaxExtension|) { + register(token::intern("gather_simd"), + NormalTT(~BasicMacroExpander { + expander: make_simd, + span: None, + }, + None)); + register(token::intern("def_type_simd"), + NormalTT(~BasicMacroExpander { + expander: make_def_simd_type, + span: None, + }, + None)); + register(token::intern("swizzle_simd"), + NormalTT(~BasicMacroExpander { + expander: make_swizzle, + span: None, + }, + None)); +} + +fn make_simd(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree]) -> MacResult { + let elements = match get_exprs_from_tts(cx, sp, tts) { + Some(e) => e, + None => { + cx.span_err(sp, "SIMD gather with zero elements"); + return MacResult::dummy_expr(sp); + } + }; + MRExpr(@Expr{ id: DUMMY_NODE_ID, span: sp, node: ExprSimd(elements)}) +} +fn make_def_simd_type(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree]) -> MacResult { + + let parse_simd_ty = |cx: &mut ExtCtxt, + parser: &mut parser::Parser, + sep_token: &[token::Token], + require_simd_ty: bool| + -> Option> { + if parser.eat(&token::LT) { + let lo = parser.span.lo; + let prim = parser.parse_ty(false); + parser.expect(&token::COMMA); + parser.expect(&token::DOTDOT); + + let count = { + let mut lhs = cx.expand_expr(parser.parse_bottom_expr()); + loop { + if parser.token == token::GT && + parser.look_ahead(1, |token| { + sep_token.iter().any(|t| t == token ) + }) { + break; + } else { + lhs = parser.parse_more_binops(lhs, 0); + } + } + lhs + }; + parser.expect(&token::GT); + parser.bump(); + Some(P(Ty{ id: DUMMY_NODE_ID, + node: TySimd(prim, count), + span: mk_sp(lo, parser.span.hi), + })) + } else if !require_simd_ty { + Some(parser.parse_ty(false)) + } else { + cx.span_err(parser.span, "expected a SIMD type"); + None + } + }; + + let mut parser = + tts_to_parser(cx.parse_sess(), + tts.to_owned().move_iter().collect(), + cx.cfg()); + + let attrs = { + let mut attrs = Vec::new(); + let mut cont = true; + while cont { + match parser.token { + token::INTERPOLATED(token::NtAttr(attr)) => { + parser.bump(); + attrs.push(*attr); + } + token::POUND => { + let lo = parser.span.lo; + parser.bump(); + parser.expect(&token::LBRACKET); + let meta_item = parser.parse_meta_item(); + parser.expect(&token::RBRACKET); + let hi = parser.span.hi; + attrs.push(Spanned { + span: mk_sp(lo, hi), + node: ast::Attribute_ { + style: ast::AttrOuter, + value: meta_item, + is_sugared_doc: false, + } + }); + } + _ => cont = false, + }; + } + attrs + }; + + let vis = parser.parse_visibility(); + let ty_type; + + if parser.eat_keyword(keywords::Type) { + ty_type = keywords::Type; + } else if parser.eat_keyword(keywords::Struct) { + ty_type = keywords::Struct; + } else { + let token_str = parser.this_token_to_str(); + parser.span_err(sp, + format!("expected `type` or `struct` but found `{}`", + token_str)); + return MacResult::dummy_expr(sp); + } + + let ident = parser.parse_ident(); + let opt_inner; + match ty_type { + keywords::Type => { + parser.expect(&token::EQ); + opt_inner = parse_simd_ty(cx, &mut parser, [token::SEMI, token::EOF], true); + parser.expect(&token::EOF); + } + keywords::Struct => { + parser.expect(&token::LPAREN); + opt_inner = parse_simd_ty(cx, &mut parser, [token::RPAREN], true); + parser.eat(&token::SEMI); + parser.expect(&token::EOF); + } + _ => unreachable!(), + }; + match opt_inner { + Some(inner) => { + let item = Item{ + vis: vis, + attrs: attrs, + ident: ident, + id: ast::DUMMY_NODE_ID, + node: match ty_type { + keywords::Struct => ast::ItemStruct(@ast::StructDef { + fields: Vec::from_elem(1, + respan(inner.span, ast::StructField_ { + kind: ast::UnnamedField, + id: ast::DUMMY_NODE_ID, + ty: inner, + attrs: Vec::new(), + })), + ctor_id: Some(ast::DUMMY_NODE_ID), + }, ast_util::empty_generics()), + keywords::Type => ast::ItemTy(inner, ast_util::empty_generics()), + _ => unreachable!(), + }, + span: sp, + }; + MRItem(@item) + } + None => MacResult::dummy_expr(sp) + } +} +fn make_swizzle(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree]) -> MacResult { + let mut parser = tts_to_parser(cx.parse_sess(), + tts.to_owned().move_iter().collect(), + cx.cfg()); + let left = parser.parse_expr(); + let opt_right = if parser.eat(&token::DOTDOT) { + Some(parser.parse_expr()) + } else { None }; + parser.expect(&token::RARROW); + let mask = parser.parse_unspanned_seq(&token::LPAREN, + &token::RPAREN, + seq_sep_trailing_allowed(token::COMMA), + |s| { + cx.expand_expr(s.parse_expr()) + }); + + MRExpr(@Expr{ id: DUMMY_NODE_ID, span: sp, node: ExprSwizzle(left, opt_right, mask)}) +} diff --git a/src/libstd/intrinsics.rs b/src/libstd/intrinsics.rs index f0ea36c251ede..18e1b42ecbf27 100644 --- a/src/libstd/intrinsics.rs +++ b/src/libstd/intrinsics.rs @@ -162,6 +162,12 @@ pub trait TyVisitor { fn visit_trait(&mut self, name: &str) -> bool; fn visit_param(&mut self, i: uint) -> bool; fn visit_self(&mut self) -> bool; + + fn visit_simd(&mut self, + inner_ty: *TyDesc, + count: uint, + size: uint, + align: uint) -> bool; } extern "rust-intrinsic" { diff --git a/src/libstd/reflect.rs b/src/libstd/reflect.rs index a9e70bd3c6316..51212c14a7b0d 100644 --- a/src/libstd/reflect.rs +++ b/src/libstd/reflect.rs @@ -441,4 +441,17 @@ impl TyVisitor for MovePtrAdaptor { self.align_to::<&'static u8>(); true } + + fn visit_simd(&mut self, + inner_ty: *TyDesc, + count: uint, + size: uint, + align: uint) -> bool { + self.align(align); + if ! self.inner.visit_simd(inner_ty, count, size, align) { + return false + } + self.bump(size); + true + } } diff --git a/src/libstd/repr.rs b/src/libstd/repr.rs index c1b276899d5a3..bfd5a47b2ca3c 100644 --- a/src/libstd/repr.rs +++ b/src/libstd/repr.rs @@ -595,6 +595,12 @@ impl<'a> TyVisitor for ReprVisitor<'a> { fn visit_param(&mut self, _i: uint) -> bool { true } fn visit_self(&mut self) -> bool { true } + + fn visit_simd(&mut self, + _inner_ty: *TyDesc, + _count: uint, + _size: uint, + _align: uint) -> bool { true } } pub fn write_repr(writer: &mut io::Writer, object: &T) -> io::IoResult<()> { diff --git a/src/libstd/unstable/mod.rs b/src/libstd/unstable/mod.rs index 7bee0cf48ee5c..075efdf0836c8 100644 --- a/src/libstd/unstable/mod.rs +++ b/src/libstd/unstable/mod.rs @@ -8,7 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#[doc(hidden)]; +//#[doc(hidden)]; +#[allow(missing_doc)]; use prelude::*; use libc::uintptr_t; diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 3e600249a7df3..05c2afec6ee90 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -498,6 +498,14 @@ pub enum Expr_ { ExprCall(@Expr, Vec<@Expr> ), ExprMethodCall(Ident, Vec> , Vec<@Expr> ), ExprTup(Vec<@Expr> ), + // Because ty::ty_simd isn't an accessible type from source. + // Its intended consumption is through a syntax extension. + ExprSimd(Vec<@Expr>), + // As with ExprSimd, ExprSwizzle also isn't available from source + ExprSwizzle(@Expr /* left */, + Option<@Expr> /* right */, + Vec<@Expr> /* mask; may be any constant expression */), + ExprBinary(BinOp, @Expr, @Expr), ExprUnary(UnOp, @Expr), ExprLit(@Lit), @@ -765,7 +773,7 @@ pub struct Ty { span: Span, } -// Not represented directly in the AST, referred to by name through a ty_path. +// Used in TySimd; otherwise referred to by name through a ty_path. #[deriving(Clone, Eq, Encodable, Decodable, Hash)] pub enum PrimTy { TyInt(IntTy), @@ -776,6 +784,19 @@ pub enum PrimTy { TyChar } +impl fmt::Show for PrimTy { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + &TyInt(t) => t.to_str().fmt(f), + &TyUint(t) => t.to_str().fmt(f), + &TyFloat(t) => t.to_str().fmt(f), + &TyStr => "str".fmt(f), + &TyBool => "bool".fmt(f), + &TyChar => "char".fmt(f), + } + } +} + #[deriving(Clone, Eq, Encodable, Decodable, Hash)] pub enum Onceness { Once, @@ -829,6 +850,10 @@ pub enum Ty_ { TyTup(Vec> ), TyPath(Path, Option>, NodeId), // for #7264; see above TyTypeof(@Expr), + + // only accessable through a syntax extension. + TySimd(P, @Expr), + // TyInfer means the type should be inferred instead of it having been // specified. This can appear anywhere in a type. TyInfer, diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 0b56cd07c887f..de63b077085e5 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -148,6 +148,7 @@ pub trait Folder { fn fold_ty(&mut self, t: P) -> P { let node = match t.node { TyNil | TyBot | TyInfer => t.node.clone(), + TySimd(ty, count) => TySimd(self.fold_ty(ty), self.fold_expr(count)), TyBox(ty) => TyBox(self.fold_ty(ty)), TyUniq(ty) => TyUniq(self.fold_ty(ty)), TyVec(ty) => TyVec(self.fold_ty(ty)), @@ -747,6 +748,14 @@ pub fn noop_fold_expr(e: @Expr, folder: &mut T) -> @Expr { ExprRepeat(folder.fold_expr(expr), folder.fold_expr(count), mutt) } ExprTup(ref elts) => ExprTup(elts.map(|x| folder.fold_expr(*x))), + ExprSimd(ref exprs) => { + ExprSimd(exprs.map(|&x| folder.fold_expr(x) )) + } + ExprSwizzle(left, right, ref mask) => { + ExprSwizzle(folder.fold_expr(left), + right.map(|x| folder.fold_expr(x) ), + mask.map(|&x| folder.fold_expr(x) )) + } ExprCall(f, ref args) => { ExprCall(folder.fold_expr(f), args.map(|&x| folder.fold_expr(x))) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index b4f7238c9c785..acc4f4efe326d 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -4046,7 +4046,7 @@ impl Parser { } // parse visiility: PUB, PRIV, or nothing - fn parse_visibility(&mut self) -> Visibility { + pub fn parse_visibility(&mut self) -> Visibility { if self.eat_keyword(keywords::Pub) { Public } else if self.eat_keyword(keywords::Priv) { Private } else { Inherited } diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 36c3922048367..d4c815607ba02 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -506,6 +506,7 @@ pub fn print_type(s: &mut State, ty: &ast::Ty) -> io::IoResult<()> { ast::TyInfer => { try!(word(&mut s.s, "_")); } + ast::TySimd(..) => fail!("can't pretty print simd types"), } end(s) } @@ -1243,6 +1244,25 @@ pub fn print_expr(s: &mut State, expr: &ast::Expr) -> io::IoResult<()> { } try!(pclose(s)); } + ast::ExprSimd(ref exprs) => { + try!(word(&mut s.s, "gather_simd!(")); + try!(commasep_exprs(s, Inconsistent, exprs.as_slice())); + try!(word(&mut s.s, ")")); + } + ast::ExprSwizzle(left, right_opt, ref mask) => { + try!(word(&mut s.s, "swizzle_simd!(")); + try!(print_expr(s, left)); + match right_opt { + Some(right) => { + try!(word_space(s, "@")); + try!(print_expr(s, right)); + } + None => {} + }; + try!(word(&mut s.s, " -> (")); + try!(commasep_exprs(s, Inconsistent, mask.as_slice())); + try!(word(&mut s.s, ")")); + } ast::ExprCall(func, ref args) => { try!(print_expr(s, func)); try!(print_call_post(s, args.as_slice())); diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index 538528fb1484a..202c406276e27 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -366,6 +366,10 @@ pub fn walk_ty>(visitor: &mut V, typ: &Ty, env: E) { TyTypeof(expression) => { visitor.visit_expr(expression, env) } + TySimd(ty, len) => { + visitor.visit_ty(ty, env.clone()); + visitor.visit_expr(len, env) + } TyNil | TyBot | TyInfer => {} } } @@ -656,6 +660,18 @@ pub fn walk_expr>(visitor: &mut V, expression: &Expr, en visitor.visit_expr(*subexpression, env.clone()) } } + ExprSimd(ref exprs) => { + for &e in exprs.iter() { + visitor.visit_expr(e, env.clone()); + } + } + ExprSwizzle(left_expr, right_expr, ref mask) => { + visitor.visit_expr(left_expr, env.clone()); + walk_expr_opt(visitor, right_expr, env.clone()); + for &m in mask.iter() { + visitor.visit_expr(m, env.clone()); + } + } ExprCall(callee_expression, ref arguments) => { for argument in arguments.iter() { visitor.visit_expr(*argument, env.clone()) diff --git a/src/test/compile-fail/simd-if-cond.rs b/src/test/compile-fail/simd-if-cond.rs new file mode 100644 index 0000000000000..b9fe04192adef --- /dev/null +++ b/src/test/compile-fail/simd-if-cond.rs @@ -0,0 +1,44 @@ +// Copyright 2012-2013 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. + +// force-host +// xfail-stage1 + +#[feature(simd, phase)]; +#[allow(experimental)]; +#[phase(syntax)] +extern crate simd_syntax; +extern crate simd; + +#[simd] +#[deriving(Show)] +struct RGBA { + r: f32, + g: f32, + b: f32, + a: f32, +} + +fn main() { + // this should be Okay: + let r = gather_simd!(0.0f32) == gather_simd!(1.0f32); + let _ = if r[0] { Some(true) } + else { None }; + + if gather_simd!(0.0f32) == gather_simd!(1.0f32) { + //~^ ERROR expected `bool` but found `` (expected bool but found internal simd) + } + + // simd structures should still require impls of the respective cmps. + let v1 = RGBA{ r: 0.0f32, g: 0.25f32, b: 0.5f32, a: 0.75f32 }; + let v2 = RGBA{ r: 0.0f32, g: 0.25f32, b: 0.5f32, a: 0.75f32 }; + let _ = v1 == v2; + //~^ ERROR binary operation `==` cannot be applied to type `RGBA` +} diff --git a/src/test/compile-fail/simd-struct-indexing.rs b/src/test/compile-fail/simd-struct-indexing.rs new file mode 100644 index 0000000000000..a45712ef5d2be --- /dev/null +++ b/src/test/compile-fail/simd-struct-indexing.rs @@ -0,0 +1,40 @@ +// Copyright 2012-2014 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. + + +// checks that struct simd types are not implicitly indexable. + +#[feature(simd)]; +#[allow(experimental)]; + +#[simd] +struct RGBA { + r: f32, + g: f32, + b: f32, + a: f32, +} + +#[inline(never)] +fn index(v: &mut RGBA) { + v[0] = 10.0f32; + //~^ ERROR +} + + +pub fn main() { + let mut v = RGBA { + r: 20.0f32, + g: 20.0f32, + b: 20.0f32, + a: 20.0f32, + }; + index(&mut v); +} diff --git a/src/test/compile-fail/simd-swizzle-mask-const-expr.rs b/src/test/compile-fail/simd-swizzle-mask-const-expr.rs new file mode 100644 index 0000000000000..4b6dfc57fcbb0 --- /dev/null +++ b/src/test/compile-fail/simd-swizzle-mask-const-expr.rs @@ -0,0 +1,24 @@ +// Copyright 2012 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, phase)]; + +#[phase(syntax)] +extern crate simd_syntax; +extern crate simd; + +#[inline(never)] fn get_num() -> i32 { 10 } + +fn main() { + let v = gather_simd!(0); + let i = get_num(); + let _ = swizzle_simd!(v -> (i)); + //~^ ERROR expected constant integer for swizzle mask but found variable +} diff --git a/src/test/compile-fail/simd-swizzle-mask-out-of-bounds.rs b/src/test/compile-fail/simd-swizzle-mask-out-of-bounds.rs new file mode 100644 index 0000000000000..3061f9c7a3e45 --- /dev/null +++ b/src/test/compile-fail/simd-swizzle-mask-out-of-bounds.rs @@ -0,0 +1,23 @@ +// Copyright 2012 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, phase)]; + +#[phase(syntax)] +extern crate simd_syntax; +extern crate simd; + +fn main() { + let v = gather_simd!(0.0f32, 0.0f32, 0.0f32, 0.0f32); + let _ = swizzle_simd!(v -> (15)); + //~^ ERROR invalid mask index in SIMD swizzle: `15` + let _ = swizzle_simd!(v -> (0, 0, 0, 0, 10)); + //~^ ERROR invalid mask index in SIMD swizzle: `10` +} diff --git a/src/test/compile-fail/simd-type.rs b/src/test/compile-fail/simd-type.rs index 6a57ee76cdff2..a880b88a0439a 100644 --- a/src/test/compile-fail/simd-type.rs +++ b/src/test/compile-fail/simd-type.rs @@ -19,7 +19,4 @@ struct empty; //~ ERROR SIMD vector cannot be empty #[simd] struct i64f64(i64, f64); //~ ERROR SIMD vector should be homogeneous -#[simd] -struct int4(int, int, int, int); //~ ERROR SIMD vector element type should be machine type - fn main() {} diff --git a/src/test/run-pass/md-gather-infer.rs b/src/test/run-pass/md-gather-infer.rs new file mode 100644 index 0000000000000..88fe15750446d --- /dev/null +++ b/src/test/run-pass/md-gather-infer.rs @@ -0,0 +1,28 @@ +// Copyright 2012 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, phase)]; + +#[phase(syntax)] +extern crate simd_syntax; +extern crate simd; +use simd::{f64x8, i64x4}; + +#[inline(never)] fn i64x4_f(_: i64x4) {} +#[inline(never)] fn f64x8_f(_: f64x8) {} + +pub fn main() { + let v = gather_simd!(0, 1, 2, 3); + i64x4_f(v); + + let v = gather_simd!(1.0, 2.0, 3.0, 4.0); + let v = swizzle_simd!(v -> (3, 2, 1, 0, 0, 1, 2, 3)); + f64x8_f(v); +} diff --git a/src/test/run-pass/reflect-visit-type.rs b/src/test/run-pass/reflect-visit-type.rs index f81ddabf77c7f..ebb5f36abc935 100644 --- a/src/test/run-pass/reflect-visit-type.rs +++ b/src/test/run-pass/reflect-visit-type.rs @@ -138,6 +138,12 @@ impl TyVisitor for MyVisitor { fn visit_trait(&mut self, _name: &str) -> bool { true } fn visit_param(&mut self, _i: uint) -> bool { true } fn visit_self(&mut self) -> bool { true } + + fn visit_simd(&mut self, + _inner: *TyDesc, + _count: uint, + _size: uint, + _align: uint) -> bool { true } } fn visit_ty(v: &mut MyVisitor) { diff --git a/src/test/run-pass/simd-binop.rs b/src/test/run-pass/simd-binop.rs index 85c6555d7ce18..eaf1cdd8be6d2 100644 --- a/src/test/run-pass/simd-binop.rs +++ b/src/test/run-pass/simd-binop.rs @@ -8,31 +8,211 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#[feature(phase, simd)]; #[allow(experimental)]; -use std::unstable::simd::{i32x4, f32x4, u32x4}; +#[phase(syntax)] extern crate simd_syntax; +extern crate simd; +extern crate test; -fn test_int(e: i32) -> i32 { - let v = i32x4(e, 0i32, 0i32, 0i32); - let i32x4(e2, _, _, _) = v * v + v - v; - e2 +use simd::{Simd, f64x4, boolx8}; + +#[inline(never)] fn test_int(e: i32) -> i32 { + let v = gather_simd!(e, 0i32, 0i32, 0i32); + (v * v + v - v)[0] +} + +#[inline(never)] fn test_float(e: f32) -> f32 { + let v = gather_simd!(e, 0f32, 0f32, 0f32); + (v * v + v - v)[0] } -fn test_float(e: f32) -> f32 { - let v = f32x4(e, 0f32, 0f32, 0f32); - let f32x4(e2, _, _, _) = v * v + v - v; - e2 +#[inline(never)] pub fn test_shift(e: u32) -> u32 { + let v = gather_simd!(e, 0u32, 0u32, 0u32); + let one = gather_simd!(1u32, 0u32, 0u32, 0u32); + (v << one >> one)[0] } -pub fn test_shift(e: u32) -> u32 { - let v = u32x4(e, 0u32, 0u32, 0u32); - let one = u32x4(1u32, 0u32, 0u32, 0u32); - let u32x4(e2, _, _, _) = v << one >> one; - e2 +#[inline(never)] fn fake_mutate(_: &mut T) {} + +#[inline(never)] fn f64x4_actions(lhs: f64x4, rhs: f64x4) -> (boolx8, boolx8, boolx8) { + let llhs1 = swizzle_simd!(lhs > rhs .. lhs >= rhs -> (0, 1, 2, 3, 4, 5, 6, 7)); + let llhs2 = swizzle_simd!(lhs == rhs .. lhs != rhs -> (0, 1, 2, 3, 4, 5, 6, 7)); + let llhs3 = swizzle_simd!(lhs <= rhs .. lhs < rhs -> (0, 1, 2, 3, 4, 5, 6, 7)); + let m1 = swizzle_simd!(llhs1 .. llhs2 -> (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)); + let m2 = swizzle_simd!(llhs2 .. llhs3 -> (4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)); + (swizzle_simd!(m1 .. m2 -> (0, 1, 2, 3, 4, 5, 6, 7)), + swizzle_simd!(m1 .. m2 -> (8, 9, 10, 11, 12, 13, 14, 15)), + swizzle_simd!(m1 .. m2 -> (16, 17, 18, 19, 20, 21, 22, 23))) } pub fn main() { assert_eq!(test_int(3i32), 9i32); assert_eq!(test_float(3f32), 9f32); assert_eq!(test_shift(3u32), 3u32); + + + let mut v1 = gather_simd!(3.1415926535f64, 6.2821853070f64, 0.25f64, 0.25f64); + let mut v2 = swizzle_simd!(v1 -> (1, 0, 2, 2)); + fake_mutate(&mut v1); + fake_mutate(&mut v2); + + let (cond0a, cond0b, cond0c) = f64x4_actions(v1, v2); + let cond1 = swizzle_simd!(cond0a -> (0, 1, 2, 3)); + let cond2 = swizzle_simd!(cond0a -> (4, 5, 6, 7)); + let cond3 = swizzle_simd!(cond0b -> (0, 1, 2, 3)); + let cond4 = swizzle_simd!(cond0b -> (4, 5, 6, 7)); + let cond5 = swizzle_simd!(cond0c -> (0, 1, 2, 3)); + let cond6 = swizzle_simd!(cond0c -> (4, 5, 6, 7)); + + fn format_fold_f(string: ~str, (idx, v): (uint, &bool)) -> ~str { + string + format!(" {:}:{:}", idx, v) + } + + assert!(cond1[0] == false); + assert!(cond1[1] == true ); + assert!(cond1[2] == false); + assert!(cond1[3] == false); + assert!(cond2[0] == false); + assert!(cond2[1] == true ); + assert!(cond2[2] == true ); + assert!(cond2[3] == true ); + assert!(cond3[0] == false); + assert!(cond3[1] == false); + assert!(cond3[2] == true ); + assert!(cond3[3] == true ); + assert!(cond4[0] == true ); + assert!(cond4[1] == true ); + assert!(cond4[2] == false); + assert!(cond4[3] == false); + assert!(cond5[0] == true ); + assert!(cond5[1] == false); + assert!(cond5[2] == true ); + assert!(cond5[3] == true ); + assert!(cond6[0] == true ); + assert!(cond6[1] == false); + assert!(cond6[2] == false); + assert!(cond6[3] == false); + + // watch out, these are long. + let mut v1 = gather_simd!(3i8, 1i8, 4i8, 1i8, 5i8, 9i8, 2i8, 0i8, + 6i8, 2i8, 8i8, 2i8, 1i8, 8i8, 5i8, 0i8); + let mut v2 = gather_simd!(6i8, 2i8, 8i8, 2i8, 1i8, 8i8, 5i8, 0i8, + 3i8, 1i8, 4i8, 1i8, 5i8, 9i8, 2i8, 0i8); + fake_mutate(&mut v1); + fake_mutate(&mut v2); + + println!("{}", v1.iter().fold(~"v1:", |string, v| string + format!(" {}", v) )); + println!("{}", v2.iter().fold(~"v2:", |string, v| string + format!(" {}", v) )); + + let cond1 = v1 > v2; assert_eq!(cond1.len(), 16); + let cond2 = v1 >= v2; assert_eq!(cond2.len(), 16); + let cond3 = v1 == v2; assert_eq!(cond3.len(), 16); + let cond4 = v1 != v2; assert_eq!(cond4.len(), 16); + let cond5 = v1 <= v2; assert_eq!(cond5.len(), 16); + let cond6 = v1 < v2; assert_eq!(cond6.len(), 16); + + println!("{}", cond1.iter().enumerate().fold(~"cond1:", format_fold_f)); + println!("{}", cond2.iter().enumerate().fold(~"cond2:", format_fold_f)); + println!("{}", cond3.iter().enumerate().fold(~"cond3:", format_fold_f)); + println!("{}", cond4.iter().enumerate().fold(~"cond4:", format_fold_f)); + println!("{}", cond5.iter().enumerate().fold(~"cond5:", format_fold_f)); + println!("{}", cond6.iter().enumerate().fold(~"cond6:", format_fold_f)); + + assert!(cond1[0] == false && + cond1[1] == false && + cond1[2] == false && + cond1[3] == false && + cond1[4] == true && + cond1[5] == true && + cond1[6] == false && + cond1[7] == false && + cond1[8] == true && + cond1[9] == true && + cond1[10] == true && + cond1[11] == true && + cond1[12] == false && + cond1[13] == false && + cond1[14] == true && + cond1[15] == false); + assert!(cond2[0] == false && + cond2[1] == false && + cond2[2] == false && + cond2[3] == false && + cond2[4] == true && + cond2[5] == true && + cond2[6] == false && + cond2[7] == true && + cond2[8] == true && + cond2[9] == true && + cond2[10] == true && + cond2[11] == true && + cond2[12] == false && + cond2[13] == false && + cond2[14] == true && + cond2[15] == true); + assert!(cond3[0] == false && + cond3[1] == false && + cond3[2] == false && + cond3[3] == false && + cond3[4] == false && + cond3[5] == false && + cond3[6] == false && + cond3[7] == true && + cond3[8] == false && + cond3[9] == false && + cond3[10] == false && + cond3[11] == false && + cond3[12] == false && + cond3[13] == false && + cond3[14] == false && + cond3[15] == true); + assert!(cond4[0] == true && + cond4[1] == true && + cond4[2] == true && + cond4[3] == true && + cond4[4] == true && + cond4[5] == true && + cond4[6] == true && + cond4[7] == false && + cond4[8] == true && + cond4[9] == true && + cond4[10] == true && + cond4[11] == true && + cond4[12] == true && + cond4[13] == true && + cond4[14] == true && + cond4[15] == false); + assert!(cond5[0] == true && + cond5[1] == true && + cond5[2] == true && + cond5[3] == true && + cond5[4] == false && + cond5[5] == false && + cond5[6] == true && + cond5[7] == true && + cond5[8] == false && + cond5[9] == false && + cond5[10] == false && + cond5[11] == false && + cond5[12] == true && + cond5[13] == true && + cond5[14] == false && + cond5[15] == true); + assert!(cond6[0] == true && + cond6[1] == true && + cond6[2] == true && + cond6[3] == true && + cond6[4] == false && + cond6[5] == false && + cond6[6] == true && + cond6[7] == false && + cond6[8] == false && + cond6[9] == false && + cond6[10] == false && + cond6[11] == false && + cond6[12] == true && + cond6[13] == true && + cond6[14] == false && + cond6[15] == false); } diff --git a/src/test/run-pass/simd-generics.rs b/src/test/run-pass/simd-generics.rs index 7ef8913a84753..a070164b7db5e 100644 --- a/src/test/run-pass/simd-generics.rs +++ b/src/test/run-pass/simd-generics.rs @@ -10,7 +10,9 @@ // ignore-fast -#[feature(simd)]; +#[feature(simd, phase)]; +#[allow(experimental)]; +#[phase(syntax)] extern crate simd_syntax; use std::ops; @@ -22,14 +24,16 @@ fn add>(lhs: T, rhs: T) -> T { impl ops::Add for f32x4 { fn add(&self, rhs: &f32x4) -> f32x4 { - *self + *rhs + let lhs = swizzle_simd!(self -> (0, 1, 2, 3)); + let rhs = swizzle_simd!(rhs -> (0, 1, 2, 3)); + let sum = lhs + rhs; + f32x4(sum[0], sum[1], sum[2], sum[3]) } } pub fn main() { let lr = f32x4(1.0f32, 2.0f32, 3.0f32, 4.0f32); - // lame-o let f32x4(x, y, z, w) = add(lr, lr); assert_eq!(x, 2.0f32); assert_eq!(y, 4.0f32); diff --git a/src/test/run-pass/simd-index.rs b/src/test/run-pass/simd-index.rs new file mode 100644 index 0000000000000..1b31bb297994a --- /dev/null +++ b/src/test/run-pass/simd-index.rs @@ -0,0 +1,42 @@ +// Copyright 2012-2014 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. + + +// checks that strict simd types are implicitly indexable. + +#[feature(simd, phase)]; +#[allow(experimental)]; + +#[phase(syntax)] extern crate simd_syntax; + +def_type_simd!(#[allow(non_camel_case_types)] type f32x4 = ;) +def_type_simd!(#[allow(non_camel_case_types)] type i32x4 = ;) + +#[inline(never)] +fn f32x4_index(v: &mut f32x4) { + v[0] = 10.0f32; +} +#[inline(never)] +fn i32x4_index(v: &mut i32x4) { + v[0] = 10i32; +} + + +pub fn main() { + let mut v = gather_simd!(20.0f32, 20.0f32, 20.0f32, 20.0f32); + f32x4_index(&mut v); + //assert!(v[0] == 10.0f32); + //assert!(v[1] == 20.0f32); + + let mut v = gather_simd!(15, 15, 15, 15); + i32x4_index(&mut v); + //assert!(v[0] == 10); + //assert!(v[1] == 15); +} diff --git a/src/test/run-pass/simd-struct-swizzle.rs b/src/test/run-pass/simd-struct-swizzle.rs new file mode 100644 index 0000000000000..12db0a5afa2c7 --- /dev/null +++ b/src/test/run-pass/simd-struct-swizzle.rs @@ -0,0 +1,47 @@ +// Copyright 2012-2014 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. + + +// checks that struct simd types can be swizzled. + +#[feature(simd, phase)]; +#[allow(experimental)]; +#[phase(syntax)] extern crate simd_syntax; +extern crate simd; + +use simd::f32x4; + +#[simd] +struct RGBA { + r: f32, + g: f32, + b: f32, + a: f32, +} + +#[inline(never)] +fn get(v: RGBA) -> f32x4 { + swizzle_simd!(v -> (3, 2, 1, 0)) +} + + +pub fn main() { + let v1 = RGBA { + r: 1.0f32, + g: 2.0f32, + b: 3.0f32, + a: 4.0f32, + }; + let v2 = get(v1); + assert_eq!(v1.r, v2[3]); + assert_eq!(v1.g, v2[2]); + assert_eq!(v1.b, v2[1]); + assert_eq!(v1.a, v2[0]); +} diff --git a/src/test/run-pass/simd-swizzle.rs b/src/test/run-pass/simd-swizzle.rs new file mode 100644 index 0000000000000..5d83dd4b83d33 --- /dev/null +++ b/src/test/run-pass/simd-swizzle.rs @@ -0,0 +1,55 @@ +// Copyright 2012-2013 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. + +// force-host +// xfail-stage1 + +#[feature(simd, phase)]; +#[allow(experimental)]; + +#[phase(syntax)] +extern crate simd_syntax; +extern crate simd; + +use simd::BoolSimd; +use simd::f32x4; + +#[simd] +struct RGBA { + r: f32, + g: f32, + b: f32, + a: f32, +} + +static const_expr: i32 = 15 / 3; + +fn main() { + let v = gather_simd!(1.0, 2.0, 4.0, 8.0); + let rev_v = swizzle_simd!(v -> (3, 2, 1, 0)); + let gather_rev = gather_simd!(8.0, 4.0, 2.0, 1.0); + let cond = rev_v == gather_rev; + assert!(cond.every_true()); + assert!((swizzle_simd!(v -> (0, 1, 2, 3, + 3, 2, 1, 0)) == + gather_simd!(1.0, 2.0, 4.0, 8.0, + 8.0, 4.0, 2.0, 1.0)).every_true()); + assert_eq!(swizzle_simd!(v -> (0))[0], 1.0); + + assert_eq!(swizzle_simd!(v -> (const_expr - 2))[0], 8.0); + + let rgba = RGBA{ r: 1.0f32, + g: 2.0f32, + b: 3.0f32, + a: 4.0f32 }; + + assert!((swizzle_simd!(rgba -> (3, 2, 1, 0)) == + gather_simd!(4.0, 3.0, 2.0, 1.0)).every_true()); +} From 05b5f57ac4e6095db3bf9d6c6663bbcf7e5c2804 Mon Sep 17 00:00:00 2001 From: Richard Diamond Date: Sat, 15 Mar 2014 14:20:32 -0500 Subject: [PATCH 2/2] ```make install``` needs dylib output. --- src/libsimd/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libsimd/lib.rs b/src/libsimd/lib.rs index 9f6788a9713d3..4c3bc91a6b35a 100644 --- a/src/libsimd/lib.rs +++ b/src/libsimd/lib.rs @@ -9,6 +9,7 @@ // except according to those terms. #[crate_id = "simd#0.10-pre"]; +#[crate_type = "dylib"]; #[crate_type = "rlib"]; #[license = "MIT/ASL2"]; #[comment = "A link-time library to facilitate access to SIMD types & operations"];