diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs index 1982f04195f..c6944e7b756 100644 --- a/src/librustc_trans/trans/base.rs +++ b/src/librustc_trans/trans/base.rs @@ -348,17 +348,14 @@ pub fn compare_simd_types<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, lhs: ValueRef, rhs: ValueRef, t: Ty<'tcx>, + ret_ty: Type, op: ast::BinOp_, debug_loc: DebugLoc) -> ValueRef { let signed = match t.sty { ty::TyFloat(_) => { - // The comparison operators for floating point vectors are challenging. - // LLVM outputs a `< size x i1 >`, but if we perform a sign extension - // then bitcast to a floating point vector, the result will be `-NaN` - // for each truth value. Because of this they are unsupported. - bcx.sess().bug("compare_simd_types: comparison operators \ - not supported for floating point SIMD types") + let cmp = bin_op_to_fcmp_predicate(bcx.ccx(), op); + return SExt(bcx, FCmp(bcx, cmp, lhs, rhs, debug_loc), ret_ty); }, ty::TyUint(_) => false, ty::TyInt(_) => true, @@ -370,7 +367,7 @@ pub fn compare_simd_types<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, // to get the correctly sized type. This will compile to a single instruction // once the IR is converted to assembly if the SIMD instruction is supported // by the target architecture. - SExt(bcx, ICmp(bcx, cmp, lhs, rhs, debug_loc), val_ty(lhs)) + SExt(bcx, ICmp(bcx, cmp, lhs, rhs, debug_loc), ret_ty) } // Iterates through the elements of a structural type. diff --git a/src/librustc_trans/trans/expr.rs b/src/librustc_trans/trans/expr.rs index c5043f867de..9ba45e0d481 100644 --- a/src/librustc_trans/trans/expr.rs +++ b/src/librustc_trans/trans/expr.rs @@ -1797,7 +1797,7 @@ fn trans_eager_binop<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, } ast::BiEq | ast::BiNe | ast::BiLt | ast::BiGe | ast::BiLe | ast::BiGt => { if is_simd { - base::compare_simd_types(bcx, lhs, rhs, intype, op.node, binop_debug_loc) + base::compare_simd_types(bcx, lhs, rhs, intype, val_ty(lhs), op.node, binop_debug_loc) } else { base::compare_scalar_types(bcx, lhs, rhs, intype, op.node, binop_debug_loc) } diff --git a/src/librustc_trans/trans/intrinsic.rs b/src/librustc_trans/trans/intrinsic.rs index 33e5d814eb1..489c54dc6e2 100644 --- a/src/librustc_trans/trans/intrinsic.rs +++ b/src/librustc_trans/trans/intrinsic.rs @@ -800,7 +800,15 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, _ => C_null(llret_ty) } } - + (_, name) if name.starts_with("simd_") => { + generic_simd_intrinsic(bcx, name, + substs, + callee_ty, + &llargs, + ret_ty, llret_ty, + call_debug_location, + call_info) + } // This requires that atomic intrinsics follow a specific naming pattern: // "atomic_[_]", and no ordering means SeqCst (_, name) if name.starts_with("atomic_") => { @@ -1263,3 +1271,125 @@ fn get_rust_try_fn<'a, 'tcx>(fcx: &FunctionContext<'a, 'tcx>, *ccx.rust_try_fn().borrow_mut() = Some(rust_try); return rust_try } + +fn generic_simd_intrinsic<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + name: &str, + _substs: subst::Substs<'tcx>, + callee_ty: Ty<'tcx>, + llargs: &[ValueRef], + ret_ty: Ty<'tcx>, + llret_ty: Type, + call_debug_location: DebugLoc, + call_info: NodeIdAndSpan) -> ValueRef { + let tcx = bcx.tcx(); + let arg_tys = match callee_ty.sty { + ty::TyBareFn(_, ref f) => { + bcx.tcx().erase_late_bound_regions(&f.sig.inputs()) + } + _ => unreachable!() + }; + + let comparison = match name { + "simd_eq" => Some(ast::BiEq), + "simd_ne" => Some(ast::BiNe), + "simd_lt" => Some(ast::BiLt), + "simd_le" => Some(ast::BiLe), + "simd_gt" => Some(ast::BiGt), + "simd_ge" => Some(ast::BiGe), + _ => None + }; + + macro_rules! require { + ($cond: expr, $($fmt: tt)*) => { + if !$cond { + bcx.sess().span_err(call_info.span, &format!($($fmt)*)); + return C_null(llret_ty) + } + } + } + + if let Some(cmp_op) = comparison { + assert_eq!(arg_tys.len(), 2); + // we need nominal equality here, not LLVM (structural) + // equality + require!(arg_tys[0] == arg_tys[1], + "SIMD comparison intrinsic monomorphised with different input types"); + require!(arg_tys[0].is_simd(tcx), + "SIMD comparison intrinsic monomorphised for non-SIMD argument type"); + require!(ret_ty.is_simd(tcx), + "SIMD comparison intrinsic monomorphised for non-SIMD return type"); + + let in_len = arg_tys[0].simd_size(tcx); + let out_len = ret_ty.simd_size(tcx); + require!(in_len == out_len, + "SIMD comparison intrinsic monomorphised for non-SIMD argument type"); + require!(llret_ty.element_type().kind() == llvm::Integer, + "SIMD comparison intrinsic monomorphised with non-integer return"); + + return compare_simd_types(bcx, + llargs[0], + llargs[1], + arg_tys[0].simd_type(tcx), + llret_ty, + cmp_op, + call_debug_location) + } + + if name.starts_with("simd_shuffle") { + let n: usize = match name["simd_shuffle".len()..].parse() { + Ok(n) => n, + Err(_) => tcx.sess.span_bug(call_info.span, + "bad `simd_shuffle` instruction only caught in trans?") + }; + assert_eq!(llargs.len(), 2 + n); + + require!(arg_tys[0] == arg_tys[1], + "SIMD shuffle intrinsic monomorphised with different input types"); + require!(ret_ty.is_simd(tcx), + "SIMD shuffle intrinsic monomorphised for non-SIMD return type"); + + let in_len = arg_tys[0].simd_size(tcx); + let out_len = ret_ty.simd_size(tcx); + require!(out_len == n, + "SIMD shuffle intrinsic monomorphised with return type of length {} (expected {})", + out_len, n); + require!(arg_tys[0].simd_type(tcx) == ret_ty.simd_type(tcx), + "SIMD shuffle intrinsic monomorphised with different \ + input and return element types"); + + let total_len = in_len as u64 * 2; + + let indices: Option> = llargs[2..] + .iter() + .enumerate() + .map(|(i, val)| { + let arg_idx = i + 2; + let c = const_to_opt_uint(*val); + match c { + None => { + bcx.sess().span_err(call_info.span, + &format!("SIMD shuffle intrinsic argument #{} \ + is not a constant", + arg_idx)); + None + } + Some(idx) if idx >= total_len => { + bcx.sess().span_err(call_info.span, + &format!("SIMD shuffle intrinsic argument #{} \ + is out of bounds (limit {})", + arg_idx, total_len)); + None + } + Some(idx) => Some(C_i32(bcx.ccx(), idx as i32)), + } + }) + .collect(); + let indices = match indices { + Some(i) => i, + None => return C_null(llret_ty) + }; + + return ShuffleVector(bcx, llargs[0], llargs[1], C_vector(&indices)) + } + C_null(llret_ty) +} diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index e32964db748..749bc8ab294 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -110,6 +110,7 @@ use util::lev_distance::lev_distance; use std::cell::{Cell, Ref, RefCell}; use std::collections::HashSet; +use std::iter; use std::mem::replace; use std::slice; use syntax::{self, abi, attr}; @@ -5091,6 +5092,7 @@ pub fn check_intrinsic_type(ccx: &CrateCtxt, it: &ast::ForeignItem) { let tcx = ccx.tcx; let name = it.ident.name.as_str(); + let mut infer_ctxt = None; let (n_tps, inputs, output) = if name.starts_with("atomic_") { let split : Vec<&str> = name.split('_').collect(); assert!(split.len() >= 2, "Atomic intrinsic not correct format"); @@ -5338,7 +5340,28 @@ pub fn check_intrinsic_type(ccx: &CrateCtxt, it: &ast::ForeignItem) { "discriminant_value" => (1, vec![ tcx.mk_imm_ref(tcx.mk_region(ty::ReLateBound(ty::DebruijnIndex::new(1), ty::BrAnon(0))), - param(ccx, 0))], tcx.types.u64), + param(ccx, 0))], tcx.types.u64), + "simd_eq" | "simd_ne" | "simd_lt" | "simd_le" | "simd_gt" | "simd_ge" => { + (2, vec![param(ccx, 0), param(ccx, 0)], param(ccx, 1)) + } + name if name.starts_with("simd_shuffle") => { + match name["simd_shuffle".len()..].parse() { + Ok(n) => { + let mut params = vec![param(ccx, 0), param(ccx, 0)]; + params.extend(iter::repeat(tcx.types.u32).take(n)); + + let ictxt = infer::new_infer_ctxt(tcx, &tcx.tables, None, false); + let ret = ictxt.next_ty_var(); + infer_ctxt = Some(ictxt); + (2, params, ret) + } + Err(_) => { + span_err!(tcx.sess, it.span, E0439, + "invalid `simd_shuffle`, needs length: `{}`", name); + return + } + } + } "try" => { let mut_u8 = tcx.mk_mut_ptr(tcx.types.u8); @@ -5381,7 +5404,7 @@ pub fn check_intrinsic_type(ccx: &CrateCtxt, it: &ast::ForeignItem) { i_n_tps, n_tps); } else { require_same_types(tcx, - None, + infer_ctxt.as_ref(), false, it.span, i_ty.ty, diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index 62804426df6..093ffc6a996 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -2800,5 +2800,6 @@ register_diagnostics! { // type because its default value `{}` references the type `Self`" E0399, // trait items need to be implemented because the associated // type `{}` was overridden - E0436 // functional record update requires a struct + E0436, // functional record update requires a struct + E0439 // invalid `simd_shuffle`, needs length: `{}` }