Skip to content

Commit 54b14b7

Browse files
committed
Auto merge of #2001 - RalfJung:simd, r=RalfJung
add more simd_reduce intrinsics This makes large parts of the portable-simd test suite work. :D Cc rust-lang/portable-simd#255
2 parents 3854a76 + b491b72 commit 54b14b7

File tree

3 files changed

+84
-20
lines changed

3 files changed

+84
-20
lines changed

src/machine.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,7 @@ pub struct PrimitiveLayouts<'tcx> {
267267
pub u8: TyAndLayout<'tcx>,
268268
pub u32: TyAndLayout<'tcx>,
269269
pub usize: TyAndLayout<'tcx>,
270+
pub bool: TyAndLayout<'tcx>,
270271
}
271272

272273
impl<'mir, 'tcx: 'mir> PrimitiveLayouts<'tcx> {
@@ -279,6 +280,7 @@ impl<'mir, 'tcx: 'mir> PrimitiveLayouts<'tcx> {
279280
u8: layout_cx.layout_of(layout_cx.tcx.types.u8)?,
280281
u32: layout_cx.layout_of(layout_cx.tcx.types.u32)?,
281282
usize: layout_cx.layout_of(layout_cx.tcx.types.usize)?,
283+
bool: layout_cx.layout_of(layout_cx.tcx.types.bool)?,
282284
})
283285
}
284286
}

src/shims/intrinsics.rs

Lines changed: 71 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -324,12 +324,22 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
324324

325325
assert_eq!(dest_len, op_len);
326326

327+
enum Op {
328+
MirOp(mir::UnOp),
329+
Abs,
330+
}
331+
let which = match intrinsic_name {
332+
"simd_neg" => Op::MirOp(mir::UnOp::Neg),
333+
"simd_fabs" => Op::Abs,
334+
_ => unreachable!(),
335+
};
336+
327337
for i in 0..dest_len {
328338
let op = this.read_immediate(&this.mplace_index(&op, i)?.into())?;
329339
let dest = this.mplace_index(&dest, i)?;
330-
let val = match intrinsic_name {
331-
"simd_neg" => this.unary_op(mir::UnOp::Neg, &op)?.to_scalar()?,
332-
"simd_fabs" => {
340+
let val = match which {
341+
Op::MirOp(mir_op) => this.unary_op(mir_op, &op)?.to_scalar()?,
342+
Op::Abs => {
333343
// Works for f32 and f64.
334344
let ty::Float(float_ty) = op.layout.ty.kind() else {
335345
bug!("simd_fabs operand is not a float")
@@ -341,7 +351,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
341351
FloatTy::F64 => Scalar::from_f64(op.to_f64()?.abs()),
342352
}
343353
}
344-
_ => bug!(),
345354
};
346355
this.write_scalar(val, &dest.into())?;
347356
}
@@ -373,7 +382,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
373382
assert_eq!(dest_len, left_len);
374383
assert_eq!(dest_len, right_len);
375384

376-
let op = match intrinsic_name {
385+
let mir_op = match intrinsic_name {
377386
"simd_add" => BinOp::Add,
378387
"simd_sub" => BinOp::Sub,
379388
"simd_mul" => BinOp::Mul,
@@ -397,16 +406,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
397406
let left = this.read_immediate(&this.mplace_index(&left, i)?.into())?;
398407
let right = this.read_immediate(&this.mplace_index(&right, i)?.into())?;
399408
let dest = this.mplace_index(&dest, i)?;
400-
let (val, overflowed, ty) = this.overflowing_binary_op(op, &left, &right)?;
401-
if matches!(op, BinOp::Shl | BinOp::Shr) {
409+
let (val, overflowed, ty) = this.overflowing_binary_op(mir_op, &left, &right)?;
410+
if matches!(mir_op, BinOp::Shl | BinOp::Shr) {
402411
// Shifts have extra UB as SIMD operations that the MIR binop does not have.
403412
// See <https://github.com/rust-lang/rust/issues/91237>.
404413
if overflowed {
405414
let r_val = right.to_scalar()?.to_bits(right.layout.size)?;
406415
throw_ub_format!("overflowing shift by {} in `{}` in SIMD lane {}", r_val, intrinsic_name, i);
407416
}
408417
}
409-
if matches!(op, BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge) {
418+
if matches!(mir_op, BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge) {
410419
// Special handling for boolean-returning operations
411420
assert_eq!(ty, this.tcx.types.bool);
412421
let val = val.to_bool().unwrap();
@@ -419,28 +428,70 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
419428
}
420429
}
421430
}
422-
"simd_reduce_any" | "simd_reduce_all" => {
431+
#[rustfmt::skip]
432+
| "simd_reduce_and"
433+
| "simd_reduce_or"
434+
| "simd_reduce_xor"
435+
| "simd_reduce_any"
436+
| "simd_reduce_all" => {
437+
use mir::BinOp;
438+
423439
let &[ref op] = check_arg_count(args)?;
424440
let (op, op_len) = this.operand_to_simd(op)?;
425441

426-
// the neutral element
427-
let mut res = match intrinsic_name {
428-
"simd_reduce_any" => false,
429-
"simd_reduce_all" => true,
430-
_ => bug!(),
442+
let imm_from_bool =
443+
|b| ImmTy::from_scalar(Scalar::from_bool(b), this.machine.layouts.bool);
444+
445+
enum Op {
446+
MirOp(BinOp),
447+
MirOpBool(BinOp),
448+
}
449+
// The initial value is the neutral element.
450+
let (which, init) = match intrinsic_name {
451+
"simd_reduce_and" => (Op::MirOp(BinOp::BitAnd), ImmTy::from_int(-1, dest.layout)),
452+
"simd_reduce_or" => (Op::MirOp(BinOp::BitOr), ImmTy::from_int(0, dest.layout)),
453+
"simd_reduce_xor" => (Op::MirOp(BinOp::BitXor), ImmTy::from_int(0, dest.layout)),
454+
"simd_reduce_any" => (Op::MirOpBool(BinOp::BitOr), imm_from_bool(false)),
455+
"simd_reduce_all" => (Op::MirOpBool(BinOp::BitAnd), imm_from_bool(true)),
456+
_ => unreachable!(),
431457
};
432458

459+
let mut res = init;
433460
for i in 0..op_len {
434461
let op = this.read_immediate(&this.mplace_index(&op, i)?.into())?;
435-
let val = simd_element_to_bool(op)?;
436-
res = match intrinsic_name {
437-
"simd_reduce_any" => res | val,
438-
"simd_reduce_all" => res & val,
439-
_ => bug!(),
462+
res = match which {
463+
Op::MirOp(mir_op) => {
464+
this.binary_op(mir_op, &res, &op)?
465+
}
466+
Op::MirOpBool(mir_op) => {
467+
let op = imm_from_bool(simd_element_to_bool(op)?);
468+
this.binary_op(mir_op, &res, &op)?
469+
}
440470
};
441471
}
472+
this.write_immediate(*res, dest)?;
473+
}
474+
#[rustfmt::skip]
475+
| "simd_reduce_add_ordered"
476+
| "simd_reduce_mul_ordered" => {
477+
use mir::BinOp;
478+
479+
let &[ref op, ref init] = check_arg_count(args)?;
480+
let (op, op_len) = this.operand_to_simd(op)?;
481+
let init = this.read_immediate(init)?;
442482

443-
this.write_scalar(Scalar::from_bool(res), dest)?;
483+
let mir_op = match intrinsic_name {
484+
"simd_reduce_add_ordered" => BinOp::Add,
485+
"simd_reduce_mul_ordered" => BinOp::Mul,
486+
_ => unreachable!(),
487+
};
488+
489+
let mut res = init;
490+
for i in 0..op_len {
491+
let op = this.read_immediate(&this.mplace_index(&op, i)?.into())?;
492+
res = this.binary_op(mir_op, &res, &op)?;
493+
}
494+
this.write_immediate(*res, dest)?;
444495
}
445496
"simd_select" => {
446497
let &[ref mask, ref yes, ref no] = check_arg_count(args)?;

tests/run-pass/portable-simd.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,17 @@ fn simd_ops_i32() {
6868
assert_eq!(a.lanes_lt(i32x4::splat(5)*b), Mask::from_int(i32x4::from_array([0, 0, -1, 0])));
6969
assert_eq!(a.lanes_ge(i32x4::splat(5)*b), Mask::from_int(i32x4::from_array([-1, -1, 0, -1])));
7070
assert_eq!(a.lanes_gt(i32x4::splat(5)*b), Mask::from_int(i32x4::from_array([-1, 0, 0, -1])));
71+
72+
assert_eq!(a.horizontal_and(), 10);
73+
assert_eq!(b.horizontal_and(), 0);
74+
assert_eq!(a.horizontal_or(), 10);
75+
assert_eq!(b.horizontal_or(), -1);
76+
assert_eq!(a.horizontal_xor(), 0);
77+
assert_eq!(b.horizontal_xor(), -4);
78+
assert_eq!(a.horizontal_sum(), 40);
79+
assert_eq!(b.horizontal_sum(), 2);
80+
assert_eq!(a.horizontal_product(), 100*100);
81+
assert_eq!(b.horizontal_product(), 6*-4);
7182
}
7283

7384
fn simd_intrinsics() {

0 commit comments

Comments
 (0)