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,
// 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.
_ => 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_<operation>[_<ordering>]", and no ordering means SeqCst
(_, name) if name.starts_with("atomic_") => {
*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<Vec<_>> = 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)
+}
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};
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");
"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);
i_n_tps, n_tps);
} else {
require_same_types(tcx,
- None,
+ infer_ctxt.as_ref(),
false,
it.span,
i_ty.ty,