Skip to content

Commit b767368

Browse files
committed
implement SIMD funnel shifts in const-eval
1 parent 1ef7943 commit b767368

File tree

6 files changed

+108
-3
lines changed

6 files changed

+108
-3
lines changed

compiler/rustc_const_eval/src/interpret/intrinsics/simd.rs

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use rustc_abi::{BackendRepr, Endian};
33
use rustc_apfloat::ieee::{Double, Half, Quad, Single};
44
use rustc_apfloat::{Float, Round};
55
use rustc_middle::mir::interpret::{InterpErrorKind, Pointer, UndefinedBehaviorInfo};
6-
use rustc_middle::ty::{FloatTy, SimdAlign};
6+
use rustc_middle::ty::{FloatTy, ScalarInt, SimdAlign};
77
use rustc_middle::{bug, err_ub_format, mir, span_bug, throw_unsup_format, ty};
88
use rustc_span::{Symbol, sym};
99
use tracing::trace;
@@ -744,6 +744,49 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
744744
self.write_scalar(val, &dest)?;
745745
}
746746
}
747+
sym::simd_funnel_shl | sym::simd_funnel_shr => {
748+
let (left, _) = self.project_to_simd(&args[0])?;
749+
let (right, _) = self.project_to_simd(&args[1])?;
750+
let (shift, _) = self.project_to_simd(&args[2])?;
751+
let (dest, _) = self.project_to_simd(&dest)?;
752+
753+
let (len, elem_ty) = args[0].layout.ty.simd_size_and_type(*self.tcx);
754+
let (elem_size, _signed) = elem_ty.int_size_and_signed(*self.tcx);
755+
let elem_size_bits = u128::from(elem_size.bits());
756+
757+
let is_left = intrinsic_name == sym::simd_funnel_shl;
758+
759+
for i in 0..len {
760+
let left =
761+
self.read_scalar(&self.project_index(&left, i)?)?.to_bits(elem_size)?;
762+
let right =
763+
self.read_scalar(&self.project_index(&right, i)?)?.to_bits(elem_size)?;
764+
let shift_bits =
765+
self.read_scalar(&self.project_index(&shift, i)?)?.to_bits(elem_size)?;
766+
767+
if shift_bits >= elem_size_bits {
768+
throw_ub_format!(
769+
"overflowing shift by {shift_bits} in `{intrinsic_name}` in lane {i}"
770+
);
771+
}
772+
let inv_shift_bits = u32::try_from(elem_size_bits - shift_bits).unwrap();
773+
774+
// As `left` and `right` both occupy the lower `elem_size` bits, so we can treat
775+
// the lower `elem_size` bits as an integer of that width. So the implementation
776+
// of funnel shifts become easy.
777+
// Note that the `unbounded_sh{l,r}`s are needed only in case we are using this
778+
// on `u128xN`
779+
let result_bits = if is_left {
780+
(left << shift_bits) | right.unbounded_shr(inv_shift_bits)
781+
} else {
782+
left.unbounded_shl(inv_shift_bits) | (right >> shift_bits)
783+
};
784+
let (result, _overflow) = ScalarInt::truncate_from_uint(result_bits, elem_size);
785+
786+
let dest = self.project_index(&dest, i)?;
787+
self.write_scalar(result, &dest)?;
788+
}
789+
}
747790

748791
// Unsupported intrinsic: skip the return_to_block below.
749792
_ => return interp_ok(false),
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#![feature(core_intrinsics, portable_simd)]
2+
3+
use std::intrinsics::simd::simd_funnel_shl;
4+
use std::simd::*;
5+
6+
fn main() {
7+
unsafe {
8+
let x = i32x2::from_array([1, 1]);
9+
let y = i32x2::from_array([100, 0]);
10+
simd_funnel_shl(x, x, y); //~ERROR: overflowing shift by 100 in `simd_funnel_shl` in lane 0
11+
}
12+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
error: Undefined Behavior: overflowing shift by 100 in `simd_funnel_shl` in lane 0
2+
--> tests/fail/intrinsics/simd-funnel_shl-too-far.rs:LL:CC
3+
|
4+
LL | simd_funnel_shl(x, x, y);
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here
6+
|
7+
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
8+
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
9+
10+
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
11+
12+
error: aborting due to 1 previous error
13+
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#![feature(core_intrinsics, portable_simd)]
2+
3+
use std::intrinsics::simd::simd_funnel_shr;
4+
use std::simd::*;
5+
6+
fn main() {
7+
unsafe {
8+
let x = i32x2::from_array([1, 1]);
9+
let y = i32x2::from_array([20, 40]);
10+
simd_funnel_shr(x, x, y); //~ERROR: overflowing shift by 40 in `simd_funnel_shr` in lane 1
11+
}
12+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
error: Undefined Behavior: overflowing shift by 40 in `simd_funnel_shr` in lane 1
2+
--> tests/fail/intrinsics/simd-funnel_shr-too-far.rs:LL:CC
3+
|
4+
LL | simd_funnel_shr(x, x, y);
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here
6+
|
7+
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
8+
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
9+
10+
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
11+
12+
error: aborting due to 1 previous error
13+

src/tools/miri/tests/pass/intrinsics/portable-simd.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ impl<T: Copy, const N: usize> PackedSimd<T, N> {
6262
#[rustc_nounwind]
6363
pub unsafe fn simd_shuffle_const_generic<T, U, const IDX: &'static [u32]>(x: T, y: T) -> U;
6464

65-
pub fn simd_ops_f16() {
65+
fn simd_ops_f16() {
6666
use intrinsics::*;
6767

6868
// small hack to make type inference better
@@ -273,7 +273,7 @@ fn simd_ops_f64() {
273273
assert_eq!(f64x2::from_array([f64::NAN, 0.0]).reduce_min(), 0.0);
274274
}
275275

276-
pub fn simd_ops_f128() {
276+
fn simd_ops_f128() {
277277
use intrinsics::*;
278278

279279
// small hack to make type inference better
@@ -454,6 +454,18 @@ fn simd_ops_i32() {
454454
0x3fffffffu32 as i32
455455
])
456456
);
457+
458+
// these values are taken from the doctests of `u32::funnel_shl` and `u32::funnel_shr`
459+
let c = u32x4::splat(0x010000b3);
460+
let d = u32x4::splat(0x2fe78e45);
461+
462+
unsafe {
463+
assert_eq!(intrinsics::simd_funnel_shl(c, d, u32x4::splat(0)), c);
464+
assert_eq!(intrinsics::simd_funnel_shl(c, d, u32x4::splat(8)), u32x4::splat(0x0000b32f));
465+
466+
assert_eq!(intrinsics::simd_funnel_shr(c, d, u32x4::splat(0)), d);
467+
assert_eq!(intrinsics::simd_funnel_shr(c, d, u32x4::splat(8)), u32x4::splat(0xb32fe78e));
468+
}
457469
}
458470

459471
fn simd_mask() {

0 commit comments

Comments
 (0)