@@ -3,7 +3,7 @@ use rustc_abi::Endian;
33use rustc_apfloat:: ieee:: { Double , Half , Quad , Single } ;
44use rustc_apfloat:: { Float , Round } ;
55use rustc_middle:: mir:: interpret:: { InterpErrorKind , UndefinedBehaviorInfo } ;
6- use rustc_middle:: ty:: FloatTy ;
6+ use rustc_middle:: ty:: { FloatTy , ScalarInt } ;
77use rustc_middle:: { bug, err_ub_format, mir, span_bug, throw_unsup_format, ty} ;
88use rustc_span:: { Symbol , sym} ;
99use tracing:: trace;
@@ -724,6 +724,49 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
724724 self . write_scalar ( val, & dest) ?;
725725 }
726726 }
727+ sym:: simd_funnel_shl | sym:: simd_funnel_shr => {
728+ let ( left, _) = self . project_to_simd ( & args[ 0 ] ) ?;
729+ let ( right, _) = self . project_to_simd ( & args[ 1 ] ) ?;
730+ let ( shift, _) = self . project_to_simd ( & args[ 2 ] ) ?;
731+ let ( dest, _) = self . project_to_simd ( & dest) ?;
732+
733+ let ( len, elem_ty) = args[ 0 ] . layout . ty . simd_size_and_type ( * self . tcx ) ;
734+ let ( elem_size, _signed) = elem_ty. int_size_and_signed ( * self . tcx ) ;
735+ let elem_size_bits = u128:: from ( elem_size. bits ( ) ) ;
736+
737+ let is_left = intrinsic_name == sym:: simd_funnel_shl;
738+
739+ for i in 0 ..len {
740+ let left =
741+ self . read_scalar ( & self . project_index ( & left, i) ?) ?. to_bits ( elem_size) ?;
742+ let right =
743+ self . read_scalar ( & self . project_index ( & right, i) ?) ?. to_bits ( elem_size) ?;
744+ let shift_bits =
745+ self . read_scalar ( & self . project_index ( & shift, i) ?) ?. to_bits ( elem_size) ?;
746+
747+ if shift_bits >= elem_size_bits {
748+ throw_ub_format ! (
749+ "overflowing shift by {shift_bits} in `{intrinsic_name}` in lane {i}"
750+ ) ;
751+ }
752+ let inv_shift_bits = u32:: try_from ( elem_size_bits - shift_bits) . unwrap ( ) ;
753+
754+ // As `left` and `right` both occupy the lower `elem_size` bits, so we can treat
755+ // the lower `elem_size` bits as an integer of that width. So the implementation
756+ // of funnel shifts become easy.
757+ // Note that the `unbounded_sh{l,r}`s are needed only in case we are using this
758+ // on `u128xN`
759+ let result_bits = if is_left {
760+ ( left << shift_bits) | right. unbounded_shr ( inv_shift_bits)
761+ } else {
762+ left. unbounded_shl ( inv_shift_bits) | ( right >> shift_bits)
763+ } ;
764+ let ( result, _overflow) = ScalarInt :: truncate_from_uint ( result_bits, elem_size) ;
765+
766+ let dest = self . project_index ( & dest, i) ?;
767+ self . write_scalar ( result, & dest) ?;
768+ }
769+ }
727770
728771 // Unsupported intrinsic: skip the return_to_block below.
729772 _ => return interp_ok ( false ) ,
0 commit comments