Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 44 additions & 1 deletion compiler/rustc_const_eval/src/interpret/intrinsics/simd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use rustc_abi::Endian;
use rustc_apfloat::ieee::{Double, Half, Quad, Single};
use rustc_apfloat::{Float, Round};
use rustc_middle::mir::interpret::{InterpErrorKind, UndefinedBehaviorInfo};
use rustc_middle::ty::FloatTy;
use rustc_middle::ty::{FloatTy, ScalarInt};
use rustc_middle::{bug, err_ub_format, mir, span_bug, throw_unsup_format, ty};
use rustc_span::{Symbol, sym};
use tracing::trace;
Expand Down Expand Up @@ -724,6 +724,49 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
self.write_scalar(val, &dest)?;
}
}
sym::simd_funnel_shl | sym::simd_funnel_shr => {
let (left, _) = self.project_to_simd(&args[0])?;
let (right, _) = self.project_to_simd(&args[1])?;
let (shift, _) = self.project_to_simd(&args[2])?;
let (dest, _) = self.project_to_simd(&dest)?;

let (len, elem_ty) = args[0].layout.ty.simd_size_and_type(*self.tcx);
let (elem_size, _signed) = elem_ty.int_size_and_signed(*self.tcx);
let elem_size_bits = u128::from(elem_size.bits());

let is_left = intrinsic_name == sym::simd_funnel_shl;

for i in 0..len {
let left =
self.read_scalar(&self.project_index(&left, i)?)?.to_bits(elem_size)?;
let right =
self.read_scalar(&self.project_index(&right, i)?)?.to_bits(elem_size)?;
let shift_bits =
self.read_scalar(&self.project_index(&shift, i)?)?.to_bits(elem_size)?;

if shift_bits >= elem_size_bits {
throw_ub_format!(
"overflowing shift by {shift_bits} in `{intrinsic_name}` in lane {i}"
);
}
let inv_shift_bits = u32::try_from(elem_size_bits - shift_bits).unwrap();

// As `left` and `right` both occupy the lower `elem_size` bits, so we can treat
Copy link
Member

@RalfJung RalfJung Nov 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// As `left` and `right` both occupy the lower `elem_size` bits, so we can treat
// A funnel shift by S can be implemented as `(x << S) | (y >> (SIZE-S))`: this ends
// up selecting the first SIZE-S bits from x, followed by the first S bits of y
// (ignoring the corner cases S==0 and S==SIZE).
// As `left` and `right` both occupy the lower `elem_size` bits, we can treat

// the lower `elem_size` bits as an integer of that width. So the implementation
// of funnel shifts become easy.
Comment on lines +755 to +756
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// the lower `elem_size` bits as an integer of that width. So the implementation
// of funnel shifts become easy.
// the lower `elem_size` bits as an integer of the right width. So the implementation
// of funnel shifts becomes easy.

// Note that the `unbounded_sh{l,r}`s are needed only in case we are using this
// on `u128xN`
Comment on lines +757 to +758
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Note that the `unbounded_sh{l,r}`s are needed only in case we are using this
// on `u128xN`
// Note that the `unbounded_sh{l,r}`s are needed only in case we are using this
// on `u128xN` and `inv_shift_bits == 128`.

let result_bits = if is_left {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs a comment explaining why this logic, executed on u128, correctly implements the semantics for all integer types.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i added a comment, could you check if its enough

(left << shift_bits) | right.unbounded_shr(inv_shift_bits)
} else {
left.unbounded_shl(inv_shift_bits) | (right >> shift_bits)
};
let (result, _overflow) = ScalarInt::truncate_from_uint(result_bits, elem_size);

let dest = self.project_index(&dest, i)?;
self.write_scalar(result, &dest)?;
}
}

// Unsupported intrinsic: skip the return_to_block below.
_ => return interp_ok(false),
Expand Down
12 changes: 12 additions & 0 deletions src/tools/miri/tests/fail/intrinsics/simd-funnel_shl-too-far.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#![feature(core_intrinsics, portable_simd)]

use std::intrinsics::simd::simd_funnel_shl;
use std::simd::*;

fn main() {
unsafe {
let x = i32x2::from_array([1, 1]);
let y = i32x2::from_array([100, 0]);
simd_funnel_shl(x, x, y); //~ERROR: overflowing shift by 100 in `simd_funnel_shl` in lane 0
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
error: Undefined Behavior: overflowing shift by 100 in `simd_funnel_shl` in lane 0
--> tests/fail/intrinsics/simd-funnel_shl-too-far.rs:LL:CC
|
LL | simd_funnel_shl(x, x, y);
| ^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here
|
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information

note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

error: aborting due to 1 previous error

12 changes: 12 additions & 0 deletions src/tools/miri/tests/fail/intrinsics/simd-funnel_shr-too-far.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#![feature(core_intrinsics, portable_simd)]

use std::intrinsics::simd::simd_funnel_shr;
use std::simd::*;

fn main() {
unsafe {
let x = i32x2::from_array([1, 1]);
let y = i32x2::from_array([20, 40]);
simd_funnel_shr(x, x, y); //~ERROR: overflowing shift by 40 in `simd_funnel_shr` in lane 1
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
error: Undefined Behavior: overflowing shift by 40 in `simd_funnel_shr` in lane 1
--> tests/fail/intrinsics/simd-funnel_shr-too-far.rs:LL:CC
|
LL | simd_funnel_shr(x, x, y);
| ^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here
|
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information

note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

error: aborting due to 1 previous error

Loading