Skip to content

Commit bfed3c4

Browse files
committed
implement simd bitmask intrinsics
1 parent 670dc7d commit bfed3c4

File tree

3 files changed

+87
-17
lines changed

3 files changed

+87
-17
lines changed

src/helpers.rs

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -775,18 +775,3 @@ pub fn isolation_abort_error(name: &str) -> InterpResult<'static> {
775775
name,
776776
)))
777777
}
778-
779-
pub fn bool_to_simd_element(b: bool, size: Size) -> Scalar<Tag> {
780-
// SIMD uses all-1 as pattern for "true"
781-
let val = if b { -1 } else { 0 };
782-
Scalar::from_int(val, size)
783-
}
784-
785-
pub fn simd_element_to_bool<'tcx>(elem: ImmTy<'tcx, Tag>) -> InterpResult<'tcx, bool> {
786-
let val = elem.to_scalar()?.to_int(elem.layout.size)?;
787-
Ok(match val {
788-
0 => false,
789-
-1 => true,
790-
_ => throw_ub_format!("each element of a SIMD mask must be all-0-bits or all-1-bits"),
791-
})
792-
}

src/shims/intrinsics.rs

Lines changed: 72 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
1+
use std::convert::TryInto;
12
use std::iter;
23

34
use log::trace;
45

56
use rustc_apfloat::{Float, Round};
67
use rustc_middle::ty::layout::{HasParamEnv, IntegerExt, LayoutOf};
78
use rustc_middle::{mir, mir::BinOp, ty, ty::FloatTy};
8-
use rustc_target::abi::{Align, Integer};
9+
use rustc_target::abi::{Align, Endian, HasDataLayout, Integer, Size};
910

1011
use crate::*;
11-
use helpers::{bool_to_simd_element, check_arg_count, simd_element_to_bool};
12+
use helpers::check_arg_count;
1213

1314
pub enum AtomicOp {
1415
MirOp(mir::BinOp, bool),
@@ -663,6 +664,35 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
663664
this.write_immediate(*val, &dest.into())?;
664665
}
665666
}
667+
"simd_select_bitmask" => {
668+
let &[ref mask, ref yes, ref no] = check_arg_count(args)?;
669+
let (yes, yes_len) = this.operand_to_simd(yes)?;
670+
let (no, no_len) = this.operand_to_simd(no)?;
671+
let (dest, dest_len) = this.place_to_simd(dest)?;
672+
673+
assert!(mask.layout.ty.is_integral());
674+
assert_eq!(dest_len.max(8), mask.layout.size.bits());
675+
assert!(dest_len <= 64);
676+
assert_eq!(dest_len, yes_len);
677+
assert_eq!(dest_len, no_len);
678+
679+
let mask: u64 = this
680+
.read_scalar(mask)?
681+
.check_init()?
682+
.to_bits(mask.layout.size)?
683+
.try_into()
684+
.unwrap();
685+
for i in 0..dest_len {
686+
let mask =
687+
mask & (1 << simd_bitmask_index(i, dest_len, this.data_layout().endian));
688+
let yes = this.read_immediate(&this.mplace_index(&yes, i)?.into())?;
689+
let no = this.read_immediate(&this.mplace_index(&no, i)?.into())?;
690+
let dest = this.mplace_index(&dest, i)?;
691+
692+
let val = if mask != 0 { yes } else { no };
693+
this.write_immediate(*val, &dest.into())?;
694+
}
695+
}
666696
#[rustfmt::skip]
667697
"simd_cast" | "simd_as" => {
668698
let &[ref op] = check_arg_count(args)?;
@@ -787,6 +817,23 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
787817
}
788818
}
789819
}
820+
"simd_bitmask" => {
821+
let &[ref op] = check_arg_count(args)?;
822+
let (op, op_len) = this.operand_to_simd(op)?;
823+
824+
assert!(dest.layout.ty.is_integral());
825+
assert_eq!(op_len.max(8), dest.layout.size.bits());
826+
assert!(op_len <= 64);
827+
828+
let mut res = 0u64;
829+
for i in 0..op_len {
830+
let op = this.read_immediate(&this.mplace_index(&op, i)?.into())?;
831+
if simd_element_to_bool(op)? {
832+
res |= 1 << simd_bitmask_index(i, op_len, this.data_layout().endian);
833+
}
834+
}
835+
this.write_int(res, dest)?;
836+
}
790837

791838
// Atomic operations
792839
"atomic_load" => this.atomic_load(args, dest, AtomicReadOp::SeqCst)?,
@@ -1307,3 +1354,26 @@ fn fmin_op<'tcx>(
13071354
FloatTy::F64 => Scalar::from_f64(left.to_f64()?.min(right.to_f64()?)),
13081355
})
13091356
}
1357+
1358+
fn bool_to_simd_element(b: bool, size: Size) -> Scalar<Tag> {
1359+
// SIMD uses all-1 as pattern for "true"
1360+
let val = if b { -1 } else { 0 };
1361+
Scalar::from_int(val, size)
1362+
}
1363+
1364+
fn simd_element_to_bool<'tcx>(elem: ImmTy<'tcx, Tag>) -> InterpResult<'tcx, bool> {
1365+
let val = elem.to_scalar()?.to_int(elem.layout.size)?;
1366+
Ok(match val {
1367+
0 => false,
1368+
-1 => true,
1369+
_ => throw_ub_format!("each element of a SIMD mask must be all-0-bits or all-1-bits"),
1370+
})
1371+
}
1372+
1373+
fn simd_bitmask_index(idx: u64, len: u64, endianess: Endian) -> u64 {
1374+
assert!(idx < len);
1375+
match endianess {
1376+
Endian::Little => idx,
1377+
Endian::Big => len.max(8) - 1 - idx, // reverse order of bits
1378+
}
1379+
}

tests/run-pass/portable-simd.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,21 @@ fn simd_mask() {
187187
let intmask = Mask::from_int(i32x4::from_array([0, -1, 0, 0]));
188188
assert_eq!(intmask, Mask::from_array([false, true, false, false]));
189189
assert_eq!(intmask.to_array(), [false, true, false, false]);
190+
191+
let values = [
192+
true, false, false, true, false, false, true, false, true, true, false, false, false, true,
193+
false, true,
194+
];
195+
let mask = Mask::<i64, 16>::from_array(values);
196+
let bitmask = mask.to_bitmask();
197+
assert_eq!(bitmask, 0b1010001101001001);
198+
assert_eq!(Mask::<i64, 16>::from_bitmask(bitmask), mask);
199+
200+
let values = [false, false, false, true];
201+
let mask = Mask::<i64, 4>::from_array(values);
202+
let bitmask = mask.to_bitmask();
203+
assert_eq!(bitmask, 0b1000);
204+
assert_eq!(Mask::<i64, 4>::from_bitmask(bitmask), mask);
190205
}
191206

192207
fn simd_cast() {

0 commit comments

Comments
 (0)