Skip to content

Commit 1bfbde6

Browse files
committed
Add comparison and shuffle SIMD intrinsics.
- simd_eq, simd_ne, simd_lt, simd_le, simd_gt, simd_ge - simd_shuffleNNN
1 parent 4f44258 commit 1bfbde6

File tree

5 files changed

+163
-12
lines changed

5 files changed

+163
-12
lines changed

src/librustc_trans/trans/base.rs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -348,17 +348,14 @@ pub fn compare_simd_types<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
348348
lhs: ValueRef,
349349
rhs: ValueRef,
350350
t: Ty<'tcx>,
351+
ret_ty: Type,
351352
op: ast::BinOp_,
352353
debug_loc: DebugLoc)
353354
-> ValueRef {
354355
let signed = match t.sty {
355356
ty::TyFloat(_) => {
356-
// The comparison operators for floating point vectors are challenging.
357-
// LLVM outputs a `< size x i1 >`, but if we perform a sign extension
358-
// then bitcast to a floating point vector, the result will be `-NaN`
359-
// for each truth value. Because of this they are unsupported.
360-
bcx.sess().bug("compare_simd_types: comparison operators \
361-
not supported for floating point SIMD types")
357+
let cmp = bin_op_to_fcmp_predicate(bcx.ccx(), op);
358+
return SExt(bcx, FCmp(bcx, cmp, lhs, rhs, debug_loc), ret_ty);
362359
},
363360
ty::TyUint(_) => false,
364361
ty::TyInt(_) => true,
@@ -370,7 +367,7 @@ pub fn compare_simd_types<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
370367
// to get the correctly sized type. This will compile to a single instruction
371368
// once the IR is converted to assembly if the SIMD instruction is supported
372369
// by the target architecture.
373-
SExt(bcx, ICmp(bcx, cmp, lhs, rhs, debug_loc), val_ty(lhs))
370+
SExt(bcx, ICmp(bcx, cmp, lhs, rhs, debug_loc), ret_ty)
374371
}
375372

376373
// Iterates through the elements of a structural type.

src/librustc_trans/trans/expr.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1797,7 +1797,7 @@ fn trans_eager_binop<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
17971797
}
17981798
ast::BiEq | ast::BiNe | ast::BiLt | ast::BiGe | ast::BiLe | ast::BiGt => {
17991799
if is_simd {
1800-
base::compare_simd_types(bcx, lhs, rhs, intype, op.node, binop_debug_loc)
1800+
base::compare_simd_types(bcx, lhs, rhs, intype, val_ty(lhs), op.node, binop_debug_loc)
18011801
} else {
18021802
base::compare_scalar_types(bcx, lhs, rhs, intype, op.node, binop_debug_loc)
18031803
}

src/librustc_trans/trans/intrinsic.rs

Lines changed: 131 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -800,7 +800,15 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
800800
_ => C_null(llret_ty)
801801
}
802802
}
803-
803+
(_, name) if name.starts_with("simd_") => {
804+
generic_simd_intrinsic(bcx, name,
805+
substs,
806+
callee_ty,
807+
&llargs,
808+
ret_ty, llret_ty,
809+
call_debug_location,
810+
call_info)
811+
}
804812
// This requires that atomic intrinsics follow a specific naming pattern:
805813
// "atomic_<operation>[_<ordering>]", and no ordering means SeqCst
806814
(_, name) if name.starts_with("atomic_") => {
@@ -1263,3 +1271,125 @@ fn get_rust_try_fn<'a, 'tcx>(fcx: &FunctionContext<'a, 'tcx>,
12631271
*ccx.rust_try_fn().borrow_mut() = Some(rust_try);
12641272
return rust_try
12651273
}
1274+
1275+
fn generic_simd_intrinsic<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
1276+
name: &str,
1277+
_substs: subst::Substs<'tcx>,
1278+
callee_ty: Ty<'tcx>,
1279+
llargs: &[ValueRef],
1280+
ret_ty: Ty<'tcx>,
1281+
llret_ty: Type,
1282+
call_debug_location: DebugLoc,
1283+
call_info: NodeIdAndSpan) -> ValueRef {
1284+
let tcx = bcx.tcx();
1285+
let arg_tys = match callee_ty.sty {
1286+
ty::TyBareFn(_, ref f) => {
1287+
bcx.tcx().erase_late_bound_regions(&f.sig.inputs())
1288+
}
1289+
_ => unreachable!()
1290+
};
1291+
1292+
let comparison = match name {
1293+
"simd_eq" => Some(ast::BiEq),
1294+
"simd_ne" => Some(ast::BiNe),
1295+
"simd_lt" => Some(ast::BiLt),
1296+
"simd_le" => Some(ast::BiLe),
1297+
"simd_gt" => Some(ast::BiGt),
1298+
"simd_ge" => Some(ast::BiGe),
1299+
_ => None
1300+
};
1301+
1302+
macro_rules! require {
1303+
($cond: expr, $($fmt: tt)*) => {
1304+
if !$cond {
1305+
bcx.sess().span_err(call_info.span, &format!($($fmt)*));
1306+
return C_null(llret_ty)
1307+
}
1308+
}
1309+
}
1310+
1311+
if let Some(cmp_op) = comparison {
1312+
assert_eq!(arg_tys.len(), 2);
1313+
// we need nominal equality here, not LLVM (structural)
1314+
// equality
1315+
require!(arg_tys[0] == arg_tys[1],
1316+
"SIMD comparison intrinsic monomorphised with different input types");
1317+
require!(arg_tys[0].is_simd(tcx),
1318+
"SIMD comparison intrinsic monomorphised for non-SIMD argument type");
1319+
require!(ret_ty.is_simd(tcx),
1320+
"SIMD comparison intrinsic monomorphised for non-SIMD return type");
1321+
1322+
let in_len = arg_tys[0].simd_size(tcx);
1323+
let out_len = ret_ty.simd_size(tcx);
1324+
require!(in_len == out_len,
1325+
"SIMD comparison intrinsic monomorphised for non-SIMD argument type");
1326+
require!(llret_ty.element_type().kind() == llvm::Integer,
1327+
"SIMD comparison intrinsic monomorphised with non-integer return");
1328+
1329+
return compare_simd_types(bcx,
1330+
llargs[0],
1331+
llargs[1],
1332+
arg_tys[0].simd_type(tcx),
1333+
llret_ty,
1334+
cmp_op,
1335+
call_debug_location)
1336+
}
1337+
1338+
if name.starts_with("simd_shuffle") {
1339+
let n: usize = match name["simd_shuffle".len()..].parse() {
1340+
Ok(n) => n,
1341+
Err(_) => tcx.sess.span_bug(call_info.span,
1342+
"bad `simd_shuffle` instruction only caught in trans?")
1343+
};
1344+
assert_eq!(llargs.len(), 2 + n);
1345+
1346+
require!(arg_tys[0] == arg_tys[1],
1347+
"SIMD shuffle intrinsic monomorphised with different input types");
1348+
require!(ret_ty.is_simd(tcx),
1349+
"SIMD shuffle intrinsic monomorphised for non-SIMD return type");
1350+
1351+
let in_len = arg_tys[0].simd_size(tcx);
1352+
let out_len = ret_ty.simd_size(tcx);
1353+
require!(out_len == n,
1354+
"SIMD shuffle intrinsic monomorphised with return type of length {} (expected {})",
1355+
out_len, n);
1356+
require!(arg_tys[0].simd_type(tcx) == ret_ty.simd_type(tcx),
1357+
"SIMD shuffle intrinsic monomorphised with different \
1358+
input and return element types");
1359+
1360+
let total_len = in_len as u64 * 2;
1361+
1362+
let indices: Option<Vec<_>> = llargs[2..]
1363+
.iter()
1364+
.enumerate()
1365+
.map(|(i, val)| {
1366+
let arg_idx = i + 2;
1367+
let c = const_to_opt_uint(*val);
1368+
match c {
1369+
None => {
1370+
bcx.sess().span_err(call_info.span,
1371+
&format!("SIMD shuffle intrinsic argument #{} \
1372+
is not a constant",
1373+
arg_idx));
1374+
None
1375+
}
1376+
Some(idx) if idx >= total_len => {
1377+
bcx.sess().span_err(call_info.span,
1378+
&format!("SIMD shuffle intrinsic argument #{} \
1379+
is out of bounds (limit {})",
1380+
arg_idx, total_len));
1381+
None
1382+
}
1383+
Some(idx) => Some(C_i32(bcx.ccx(), idx as i32)),
1384+
}
1385+
})
1386+
.collect();
1387+
let indices = match indices {
1388+
Some(i) => i,
1389+
None => return C_null(llret_ty)
1390+
};
1391+
1392+
return ShuffleVector(bcx, llargs[0], llargs[1], C_vector(&indices))
1393+
}
1394+
C_null(llret_ty)
1395+
}

src/librustc_typeck/check/mod.rs

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ use util::lev_distance::lev_distance;
110110

111111
use std::cell::{Cell, Ref, RefCell};
112112
use std::collections::HashSet;
113+
use std::iter;
113114
use std::mem::replace;
114115
use std::slice;
115116
use syntax::{self, abi, attr};
@@ -5091,6 +5092,7 @@ pub fn check_intrinsic_type(ccx: &CrateCtxt, it: &ast::ForeignItem) {
50915092

50925093
let tcx = ccx.tcx;
50935094
let name = it.ident.name.as_str();
5095+
let mut infer_ctxt = None;
50945096
let (n_tps, inputs, output) = if name.starts_with("atomic_") {
50955097
let split : Vec<&str> = name.split('_').collect();
50965098
assert!(split.len() >= 2, "Atomic intrinsic not correct format");
@@ -5338,7 +5340,28 @@ pub fn check_intrinsic_type(ccx: &CrateCtxt, it: &ast::ForeignItem) {
53385340
"discriminant_value" => (1, vec![
53395341
tcx.mk_imm_ref(tcx.mk_region(ty::ReLateBound(ty::DebruijnIndex::new(1),
53405342
ty::BrAnon(0))),
5341-
param(ccx, 0))], tcx.types.u64),
5343+
param(ccx, 0))], tcx.types.u64),
5344+
"simd_eq" | "simd_ne" | "simd_lt" | "simd_le" | "simd_gt" | "simd_ge" => {
5345+
(2, vec![param(ccx, 0), param(ccx, 0)], param(ccx, 1))
5346+
}
5347+
name if name.starts_with("simd_shuffle") => {
5348+
match name["simd_shuffle".len()..].parse() {
5349+
Ok(n) => {
5350+
let mut params = vec![param(ccx, 0), param(ccx, 0)];
5351+
params.extend(iter::repeat(tcx.types.u32).take(n));
5352+
5353+
let ictxt = infer::new_infer_ctxt(tcx, &tcx.tables, None, false);
5354+
let ret = ictxt.next_ty_var();
5355+
infer_ctxt = Some(ictxt);
5356+
(2, params, ret)
5357+
}
5358+
Err(_) => {
5359+
span_err!(tcx.sess, it.span, E0439,
5360+
"invalid `simd_shuffle`, needs length: `{}`", name);
5361+
return
5362+
}
5363+
}
5364+
}
53425365

53435366
"try" => {
53445367
let mut_u8 = tcx.mk_mut_ptr(tcx.types.u8);
@@ -5381,7 +5404,7 @@ pub fn check_intrinsic_type(ccx: &CrateCtxt, it: &ast::ForeignItem) {
53815404
i_n_tps, n_tps);
53825405
} else {
53835406
require_same_types(tcx,
5384-
None,
5407+
infer_ctxt.as_ref(),
53855408
false,
53865409
it.span,
53875410
i_ty.ty,

src/librustc_typeck/diagnostics.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2800,5 +2800,6 @@ register_diagnostics! {
28002800
// type because its default value `{}` references the type `Self`"
28012801
E0399, // trait items need to be implemented because the associated
28022802
// type `{}` was overridden
2803-
E0436 // functional record update requires a struct
2803+
E0436, // functional record update requires a struct
2804+
E0439 // invalid `simd_shuffle`, needs length: `{}`
28042805
}

0 commit comments

Comments
 (0)