]> git.lizzy.rs Git - rust.git/commitdiff
Add comparison and shuffle SIMD intrinsics.
authorHuon Wilson <dbau.pp+github@gmail.com>
Thu, 16 Jul 2015 18:59:23 +0000 (11:59 -0700)
committerHuon Wilson <dbau.pp+github@gmail.com>
Mon, 17 Aug 2015 21:41:37 +0000 (14:41 -0700)
- simd_eq, simd_ne, simd_lt, simd_le, simd_gt, simd_ge
- simd_shuffleNNN

src/librustc_trans/trans/base.rs
src/librustc_trans/trans/expr.rs
src/librustc_trans/trans/intrinsic.rs
src/librustc_typeck/check/mod.rs
src/librustc_typeck/diagnostics.rs

index 1982f04195f0b3739dbad3fbcef7c8848669bfdc..c6944e7b75659a5e90362d9c74f22862cc391e48 100644 (file)
@@ -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.
index c5043f867ded09d3e939e697eb9c1ddd84760d7c..9ba45e0d481a3e04315a496603b028e1877343b4 100644 (file)
@@ -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)
         }
index 33e5d814eb186a12787eb1b8c548bad8605a3d5d..489c54dc6e2d262fdd939e0b5e7807d3d189378b 100644 (file)
@@ -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_<operation>[_<ordering>]", 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<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)
+}
index e32964db7486a51d4612e617c01050325179d982..749bc8ab2942bfd6b7fe0a95e880a268636a626f 100644 (file)
 
 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 @@ fn param<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, n: u32) -> Ty<'tcx> {
 
     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 @@ fn param<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, n: u32) -> Ty<'tcx> {
             "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 @@ fn param<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, n: u32) -> Ty<'tcx> {
              i_n_tps, n_tps);
     } else {
         require_same_types(tcx,
-                           None,
+                           infer_ctxt.as_ref(),
                            false,
                            it.span,
                            i_ty.ty,
index 62804426df69f21beccd0a7bf709e9168cab7ef7..093ffc6a9963e147f4498048500d4a1404fa92b0 100644 (file)
@@ -2800,5 +2800,6 @@ struct Foo<'a, T: 'a> {
            // 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: `{}`
 }