mirror of
https://github.com/rust-lang/rust.git
synced 2025-06-04 19:29:07 +00:00
Add comparison and shuffle SIMD intrinsics.
- simd_eq, simd_ne, simd_lt, simd_le, simd_gt, simd_ge - simd_shuffleNNN
This commit is contained in:
parent
4f4425840d
commit
1bfbde6778
@ -348,17 +348,14 @@ pub fn compare_simd_types<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
|
|||||||
lhs: ValueRef,
|
lhs: ValueRef,
|
||||||
rhs: ValueRef,
|
rhs: ValueRef,
|
||||||
t: Ty<'tcx>,
|
t: Ty<'tcx>,
|
||||||
|
ret_ty: Type,
|
||||||
op: ast::BinOp_,
|
op: ast::BinOp_,
|
||||||
debug_loc: DebugLoc)
|
debug_loc: DebugLoc)
|
||||||
-> ValueRef {
|
-> ValueRef {
|
||||||
let signed = match t.sty {
|
let signed = match t.sty {
|
||||||
ty::TyFloat(_) => {
|
ty::TyFloat(_) => {
|
||||||
// The comparison operators for floating point vectors are challenging.
|
let cmp = bin_op_to_fcmp_predicate(bcx.ccx(), op);
|
||||||
// LLVM outputs a `< size x i1 >`, but if we perform a sign extension
|
return SExt(bcx, FCmp(bcx, cmp, lhs, rhs, debug_loc), ret_ty);
|
||||||
// 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")
|
|
||||||
},
|
},
|
||||||
ty::TyUint(_) => false,
|
ty::TyUint(_) => false,
|
||||||
ty::TyInt(_) => true,
|
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
|
// 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
|
// once the IR is converted to assembly if the SIMD instruction is supported
|
||||||
// by the target architecture.
|
// 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.
|
// Iterates through the elements of a structural type.
|
||||||
|
@ -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 => {
|
ast::BiEq | ast::BiNe | ast::BiLt | ast::BiGe | ast::BiLe | ast::BiGt => {
|
||||||
if is_simd {
|
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 {
|
} else {
|
||||||
base::compare_scalar_types(bcx, lhs, rhs, intype, op.node, binop_debug_loc)
|
base::compare_scalar_types(bcx, lhs, rhs, intype, op.node, binop_debug_loc)
|
||||||
}
|
}
|
||||||
|
@ -800,7 +800,15 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
|
|||||||
_ => C_null(llret_ty)
|
_ => 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:
|
// This requires that atomic intrinsics follow a specific naming pattern:
|
||||||
// "atomic_<operation>[_<ordering>]", and no ordering means SeqCst
|
// "atomic_<operation>[_<ordering>]", and no ordering means SeqCst
|
||||||
(_, name) if name.starts_with("atomic_") => {
|
(_, 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);
|
*ccx.rust_try_fn().borrow_mut() = Some(rust_try);
|
||||||
return 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)
|
||||||
|
}
|
||||||
|
@ -110,6 +110,7 @@ use util::lev_distance::lev_distance;
|
|||||||
|
|
||||||
use std::cell::{Cell, Ref, RefCell};
|
use std::cell::{Cell, Ref, RefCell};
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
use std::iter;
|
||||||
use std::mem::replace;
|
use std::mem::replace;
|
||||||
use std::slice;
|
use std::slice;
|
||||||
use syntax::{self, abi, attr};
|
use syntax::{self, abi, attr};
|
||||||
@ -5091,6 +5092,7 @@ pub fn check_intrinsic_type(ccx: &CrateCtxt, it: &ast::ForeignItem) {
|
|||||||
|
|
||||||
let tcx = ccx.tcx;
|
let tcx = ccx.tcx;
|
||||||
let name = it.ident.name.as_str();
|
let name = it.ident.name.as_str();
|
||||||
|
let mut infer_ctxt = None;
|
||||||
let (n_tps, inputs, output) = if name.starts_with("atomic_") {
|
let (n_tps, inputs, output) = if name.starts_with("atomic_") {
|
||||||
let split : Vec<&str> = name.split('_').collect();
|
let split : Vec<&str> = name.split('_').collect();
|
||||||
assert!(split.len() >= 2, "Atomic intrinsic not correct format");
|
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![
|
"discriminant_value" => (1, vec![
|
||||||
tcx.mk_imm_ref(tcx.mk_region(ty::ReLateBound(ty::DebruijnIndex::new(1),
|
tcx.mk_imm_ref(tcx.mk_region(ty::ReLateBound(ty::DebruijnIndex::new(1),
|
||||||
ty::BrAnon(0))),
|
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" => {
|
"try" => {
|
||||||
let mut_u8 = tcx.mk_mut_ptr(tcx.types.u8);
|
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);
|
i_n_tps, n_tps);
|
||||||
} else {
|
} else {
|
||||||
require_same_types(tcx,
|
require_same_types(tcx,
|
||||||
None,
|
infer_ctxt.as_ref(),
|
||||||
false,
|
false,
|
||||||
it.span,
|
it.span,
|
||||||
i_ty.ty,
|
i_ty.ty,
|
||||||
|
@ -2800,5 +2800,6 @@ register_diagnostics! {
|
|||||||
// type because its default value `{}` references the type `Self`"
|
// type because its default value `{}` references the type `Self`"
|
||||||
E0399, // trait items need to be implemented because the associated
|
E0399, // trait items need to be implemented because the associated
|
||||||
// type `{}` was overridden
|
// type `{}` was overridden
|
||||||
E0436 // functional record update requires a struct
|
E0436, // functional record update requires a struct
|
||||||
|
E0439 // invalid `simd_shuffle`, needs length: `{}`
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user