Skip to main content

rustc_const_eval/interpret/intrinsics/
simd.rs

1use either::Either;
2use rustc_abi::{BackendRepr, Endian};
3use rustc_apfloat::ieee::{Double, Half, Quad, Single};
4use rustc_apfloat::{Float, Round};
5use rustc_middle::mir::interpret::{InterpErrorKind, Pointer, UndefinedBehaviorInfo};
6use rustc_middle::ty::{FloatTy, ScalarInt, SimdAlign};
7use rustc_middle::{bug, err_ub_format, mir, span_bug, throw_unsup_format, ty};
8use rustc_span::{Symbol, sym};
9use tracing::trace;
10
11use super::{
12    ImmTy, InterpCx, InterpResult, Machine, MinMax, MulAddType, OpTy, PlaceTy, Provenance, Scalar,
13    Size, TyAndLayout, interp_ok, throw_ub_format,
14};
15use crate::interpret::Writeable;
16
17impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
18    /// Returns `true` if emulation happened.
19    /// Here we implement the intrinsics that are common to all CTFE instances; individual machines can add their own
20    /// intrinsic handling.
21    pub fn eval_simd_intrinsic(
22        &mut self,
23        intrinsic_name: Symbol,
24        generic_args: ty::GenericArgsRef<'tcx>,
25        args: &[OpTy<'tcx, M::Provenance>],
26        dest: &PlaceTy<'tcx, M::Provenance>,
27        ret: Option<mir::BasicBlock>,
28    ) -> InterpResult<'tcx, bool> {
29        let dest = dest.force_mplace(self)?;
30
31        match intrinsic_name {
32            sym::simd_insert | sym::simd_insert_dyn => {
33                let index = u64::from(self.read_scalar(&args[1])?.to_u32()?);
34                let elem = &args[2];
35                let (input, input_len) = self.project_to_simd(&args[0])?;
36                let (dest, dest_len) = self.project_to_simd(&dest)?;
37                match (&input_len, &dest_len) {
    (left_val, right_val) => {
        if !(*left_val == *right_val) {
            let kind = ::core::panicking::AssertKind::Eq;
            ::core::panicking::assert_failed(kind, &*left_val, &*right_val,
                ::core::option::Option::Some(format_args!("Return vector length must match input length")));
        }
    }
};assert_eq!(input_len, dest_len, "Return vector length must match input length");
38                // Bounds are not checked by typeck so we have to do it ourselves.
39                if index >= input_len {
40                    do yeet ::rustc_middle::mir::interpret::InterpErrorKind::UndefinedBehavior(::rustc_middle::mir::interpret::UndefinedBehaviorInfo::Ub(::alloc::__export::must_use({
                    ::alloc::fmt::format(format_args!("`{0}` index {1} is out-of-bounds of vector with length {2}",
                            intrinsic_name, index, input_len))
                })));throw_ub_format!(
41                        "`{intrinsic_name}` index {index} is out-of-bounds of vector with length {input_len}"
42                    );
43                }
44
45                for i in 0..dest_len {
46                    let place = self.project_index(&dest, i)?;
47                    let value =
48                        if i == index { elem.clone() } else { self.project_index(&input, i)? };
49                    self.copy_op(&value, &place)?;
50                }
51            }
52            sym::simd_extract | sym::simd_extract_dyn => {
53                let index = u64::from(self.read_scalar(&args[1])?.to_u32()?);
54                let (input, input_len) = self.project_to_simd(&args[0])?;
55                // Bounds are not checked by typeck so we have to do it ourselves.
56                if index >= input_len {
57                    do yeet ::rustc_middle::mir::interpret::InterpErrorKind::UndefinedBehavior(::rustc_middle::mir::interpret::UndefinedBehaviorInfo::Ub(::alloc::__export::must_use({
                    ::alloc::fmt::format(format_args!("`{0}` index {1} is out-of-bounds of vector with length {2}",
                            intrinsic_name, index, input_len))
                })));throw_ub_format!(
58                        "`{intrinsic_name}` index {index} is out-of-bounds of vector with length {input_len}"
59                    );
60                }
61                self.copy_op(&self.project_index(&input, index)?, &dest)?;
62            }
63            sym::simd_splat => {
64                let elem = &args[0];
65                let (dest, dest_len) = self.project_to_simd(&dest)?;
66
67                for i in 0..dest_len {
68                    let place = self.project_index(&dest, i)?;
69                    self.copy_op(elem, &place)?;
70                }
71            }
72            sym::simd_neg
73            | sym::simd_fabs
74            | sym::simd_ceil
75            | sym::simd_floor
76            | sym::simd_round
77            | sym::simd_round_ties_even
78            | sym::simd_trunc
79            | sym::simd_ctlz
80            | sym::simd_ctpop
81            | sym::simd_cttz
82            | sym::simd_bswap
83            | sym::simd_bitreverse => {
84                let (op, op_len) = self.project_to_simd(&args[0])?;
85                let (dest, dest_len) = self.project_to_simd(&dest)?;
86
87                match (&dest_len, &op_len) {
    (left_val, right_val) => {
        if !(*left_val == *right_val) {
            let kind = ::core::panicking::AssertKind::Eq;
            ::core::panicking::assert_failed(kind, &*left_val, &*right_val,
                ::core::option::Option::None);
        }
    }
};assert_eq!(dest_len, op_len);
88
89                #[derive(#[automatically_derived]
impl ::core::marker::Copy for Op { }Copy, #[automatically_derived]
impl ::core::clone::Clone for Op {
    #[inline]
    fn clone(&self) -> Op {
        let _: ::core::clone::AssertParamIsClone<mir::UnOp>;
        let _: ::core::clone::AssertParamIsClone<rustc_apfloat::Round>;
        let _: ::core::clone::AssertParamIsClone<Symbol>;
        *self
    }
}Clone)]
90                enum Op {
91                    MirOp(mir::UnOp),
92                    Abs,
93                    Round(rustc_apfloat::Round),
94                    Numeric(Symbol),
95                }
96                let which = match intrinsic_name {
97                    sym::simd_neg => Op::MirOp(mir::UnOp::Neg),
98                    sym::simd_fabs => Op::Abs,
99                    sym::simd_ceil => Op::Round(rustc_apfloat::Round::TowardPositive),
100                    sym::simd_floor => Op::Round(rustc_apfloat::Round::TowardNegative),
101                    sym::simd_round => Op::Round(rustc_apfloat::Round::NearestTiesToAway),
102                    sym::simd_round_ties_even => Op::Round(rustc_apfloat::Round::NearestTiesToEven),
103                    sym::simd_trunc => Op::Round(rustc_apfloat::Round::TowardZero),
104                    sym::simd_ctlz => Op::Numeric(sym::ctlz),
105                    sym::simd_ctpop => Op::Numeric(sym::ctpop),
106                    sym::simd_cttz => Op::Numeric(sym::cttz),
107                    sym::simd_bswap => Op::Numeric(sym::bswap),
108                    sym::simd_bitreverse => Op::Numeric(sym::bitreverse),
109                    _ => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
110                };
111
112                for i in 0..dest_len {
113                    let op = self.read_immediate(&self.project_index(&op, i)?)?;
114                    let dest = self.project_index(&dest, i)?;
115                    let val = match which {
116                        Op::MirOp(mir_op) => {
117                            // this already does NaN adjustments
118                            self.unary_op(mir_op, &op)?.to_scalar()
119                        }
120                        Op::Abs => {
121                            // Works for f32 and f64.
122                            let ty::Float(float_ty) = op.layout.ty.kind() else {
123                                ::rustc_middle::util::bug::span_bug_fmt(self.cur_span(),
    format_args!("{0} operand is not a float", intrinsic_name))span_bug!(
124                                    self.cur_span(),
125                                    "{} operand is not a float",
126                                    intrinsic_name
127                                )
128                            };
129                            let op = op.to_scalar();
130                            // "Bitwise" operation, no NaN adjustments
131                            match float_ty {
132                                FloatTy::F16 => Scalar::from_f16(op.to_f16()?.abs()),
133                                FloatTy::F32 => Scalar::from_f32(op.to_f32()?.abs()),
134                                FloatTy::F64 => Scalar::from_f64(op.to_f64()?.abs()),
135                                FloatTy::F128 => Scalar::from_f128(op.to_f128()?.abs()),
136                            }
137                        }
138                        Op::Round(rounding) => {
139                            let ty::Float(float_ty) = op.layout.ty.kind() else {
140                                ::rustc_middle::util::bug::span_bug_fmt(self.cur_span(),
    format_args!("{0} operand is not a float", intrinsic_name))span_bug!(
141                                    self.cur_span(),
142                                    "{} operand is not a float",
143                                    intrinsic_name
144                                )
145                            };
146                            let op = op.to_scalar();
147                            match float_ty {
148                                FloatTy::F16 => self.float_round::<Half>(op, rounding)?,
149                                FloatTy::F32 => self.float_round::<Single>(op, rounding)?,
150                                FloatTy::F64 => self.float_round::<Double>(op, rounding)?,
151                                FloatTy::F128 => self.float_round::<Quad>(op, rounding)?,
152                            }
153                        }
154                        Op::Numeric(name) => {
155                            self.numeric_intrinsic(name, op.to_scalar(), op.layout, op.layout)?
156                        }
157                    };
158                    self.write_scalar(val, &dest)?;
159                }
160            }
161            sym::simd_add
162            | sym::simd_sub
163            | sym::simd_mul
164            | sym::simd_div
165            | sym::simd_rem
166            | sym::simd_shl
167            | sym::simd_shr
168            | sym::simd_and
169            | sym::simd_or
170            | sym::simd_xor
171            | sym::simd_eq
172            | sym::simd_ne
173            | sym::simd_lt
174            | sym::simd_le
175            | sym::simd_gt
176            | sym::simd_ge
177            | sym::simd_fmax
178            | sym::simd_fmin
179            | sym::simd_saturating_add
180            | sym::simd_saturating_sub
181            | sym::simd_arith_offset => {
182                use mir::BinOp;
183
184                let (left, left_len) = self.project_to_simd(&args[0])?;
185                let (right, right_len) = self.project_to_simd(&args[1])?;
186                let (dest, dest_len) = self.project_to_simd(&dest)?;
187
188                match (&dest_len, &left_len) {
    (left_val, right_val) => {
        if !(*left_val == *right_val) {
            let kind = ::core::panicking::AssertKind::Eq;
            ::core::panicking::assert_failed(kind, &*left_val, &*right_val,
                ::core::option::Option::None);
        }
    }
};assert_eq!(dest_len, left_len);
189                match (&dest_len, &right_len) {
    (left_val, right_val) => {
        if !(*left_val == *right_val) {
            let kind = ::core::panicking::AssertKind::Eq;
            ::core::panicking::assert_failed(kind, &*left_val, &*right_val,
                ::core::option::Option::None);
        }
    }
};assert_eq!(dest_len, right_len);
190
191                enum Op {
192                    MirOp(BinOp),
193                    SaturatingOp(BinOp),
194                    FMinMax(MinMax),
195                    WrappingOffset,
196                }
197                let which = match intrinsic_name {
198                    sym::simd_add => Op::MirOp(BinOp::Add),
199                    sym::simd_sub => Op::MirOp(BinOp::Sub),
200                    sym::simd_mul => Op::MirOp(BinOp::Mul),
201                    sym::simd_div => Op::MirOp(BinOp::Div),
202                    sym::simd_rem => Op::MirOp(BinOp::Rem),
203                    sym::simd_shl => Op::MirOp(BinOp::ShlUnchecked),
204                    sym::simd_shr => Op::MirOp(BinOp::ShrUnchecked),
205                    sym::simd_and => Op::MirOp(BinOp::BitAnd),
206                    sym::simd_or => Op::MirOp(BinOp::BitOr),
207                    sym::simd_xor => Op::MirOp(BinOp::BitXor),
208                    sym::simd_eq => Op::MirOp(BinOp::Eq),
209                    sym::simd_ne => Op::MirOp(BinOp::Ne),
210                    sym::simd_lt => Op::MirOp(BinOp::Lt),
211                    sym::simd_le => Op::MirOp(BinOp::Le),
212                    sym::simd_gt => Op::MirOp(BinOp::Gt),
213                    sym::simd_ge => Op::MirOp(BinOp::Ge),
214                    sym::simd_fmax => Op::FMinMax(MinMax::MaximumNumber),
215                    sym::simd_fmin => Op::FMinMax(MinMax::MinimumNumber),
216                    sym::simd_saturating_add => Op::SaturatingOp(BinOp::Add),
217                    sym::simd_saturating_sub => Op::SaturatingOp(BinOp::Sub),
218                    sym::simd_arith_offset => Op::WrappingOffset,
219                    _ => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
220                };
221
222                for i in 0..dest_len {
223                    let left = self.read_immediate(&self.project_index(&left, i)?)?;
224                    let right = self.read_immediate(&self.project_index(&right, i)?)?;
225                    let dest = self.project_index(&dest, i)?;
226                    let val = match which {
227                        Op::MirOp(mir_op) => {
228                            // this does NaN adjustments.
229                            let val = self.binary_op(mir_op, &left, &right).map_err_kind(|kind| {
230                                match kind {
231                                    InterpErrorKind::UndefinedBehavior(UndefinedBehaviorInfo::ShiftOverflow { shift_amount, .. }) => {
232                                        // this resets the interpreter backtrace, but it's not worth avoiding that.
233                                        let shift_amount = match shift_amount {
234                                            Either::Left(v) => v.to_string(),
235                                            Either::Right(v) => v.to_string(),
236                                        };
237                                        ::rustc_middle::mir::interpret::InterpErrorKind::UndefinedBehavior(::rustc_middle::mir::interpret::UndefinedBehaviorInfo::Ub(::alloc::__export::must_use({
                ::alloc::fmt::format(format_args!("overflowing shift by {0} in `{1}` in lane {2}",
                        shift_amount, intrinsic_name, i))
            })))err_ub_format!("overflowing shift by {shift_amount} in `{intrinsic_name}` in lane {i}")
238                                    }
239                                    kind => kind
240                                }
241                            })?;
242                            if #[allow(non_exhaustive_omitted_patterns)] match mir_op {
    BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge =>
        true,
    _ => false,
}matches!(
243                                mir_op,
244                                BinOp::Eq
245                                    | BinOp::Ne
246                                    | BinOp::Lt
247                                    | BinOp::Le
248                                    | BinOp::Gt
249                                    | BinOp::Ge
250                            ) {
251                                // Special handling for boolean-returning operations
252                                match (&val.layout.ty, &self.tcx.types.bool) {
    (left_val, right_val) => {
        if !(*left_val == *right_val) {
            let kind = ::core::panicking::AssertKind::Eq;
            ::core::panicking::assert_failed(kind, &*left_val, &*right_val,
                ::core::option::Option::None);
        }
    }
};assert_eq!(val.layout.ty, self.tcx.types.bool);
253                                let val = val.to_scalar().to_bool().unwrap();
254                                bool_to_simd_element(val, dest.layout.size)
255                            } else {
256                                match (&val.layout.ty, &self.tcx.types.bool) {
    (left_val, right_val) => {
        if *left_val == *right_val {
            let kind = ::core::panicking::AssertKind::Ne;
            ::core::panicking::assert_failed(kind, &*left_val, &*right_val,
                ::core::option::Option::None);
        }
    }
};assert_ne!(val.layout.ty, self.tcx.types.bool);
257                                match (&val.layout.ty, &dest.layout.ty) {
    (left_val, right_val) => {
        if !(*left_val == *right_val) {
            let kind = ::core::panicking::AssertKind::Eq;
            ::core::panicking::assert_failed(kind, &*left_val, &*right_val,
                ::core::option::Option::None);
        }
    }
};assert_eq!(val.layout.ty, dest.layout.ty);
258                                val.to_scalar()
259                            }
260                        }
261                        Op::SaturatingOp(mir_op) => self.saturating_arith(mir_op, &left, &right)?,
262                        Op::WrappingOffset => {
263                            let ptr = left.to_scalar().to_pointer(self)?;
264                            let offset_count = right.to_scalar().to_target_isize(self)?;
265                            let pointee_ty = left.layout.ty.builtin_deref(true).unwrap();
266
267                            let pointee_size =
268                                i64::try_from(self.layout_of(pointee_ty)?.size.bytes()).unwrap();
269                            let offset_bytes = offset_count.wrapping_mul(pointee_size);
270                            let offset_ptr = ptr.wrapping_signed_offset(offset_bytes, self);
271                            Scalar::from_maybe_pointer(offset_ptr, self)
272                        }
273                        Op::FMinMax(op) => self.fminmax_op(op, &left, &right)?,
274                    };
275                    self.write_scalar(val, &dest)?;
276                }
277            }
278            sym::simd_reduce_and
279            | sym::simd_reduce_or
280            | sym::simd_reduce_xor
281            | sym::simd_reduce_any
282            | sym::simd_reduce_all
283            | sym::simd_reduce_max
284            | sym::simd_reduce_min => {
285                use mir::BinOp;
286
287                let (op, op_len) = self.project_to_simd(&args[0])?;
288
289                let imm_from_bool = |b| {
290                    ImmTy::from_scalar(
291                        Scalar::from_bool(b),
292                        self.layout_of(self.tcx.types.bool).unwrap(),
293                    )
294                };
295
296                enum Op {
297                    MirOp(BinOp),
298                    MirOpBool(BinOp),
299                    MinMax(MinMax),
300                }
301                let which = match intrinsic_name {
302                    sym::simd_reduce_and => Op::MirOp(BinOp::BitAnd),
303                    sym::simd_reduce_or => Op::MirOp(BinOp::BitOr),
304                    sym::simd_reduce_xor => Op::MirOp(BinOp::BitXor),
305                    sym::simd_reduce_any => Op::MirOpBool(BinOp::BitOr),
306                    sym::simd_reduce_all => Op::MirOpBool(BinOp::BitAnd),
307                    sym::simd_reduce_max => Op::MinMax(MinMax::MaximumNumber),
308                    sym::simd_reduce_min => Op::MinMax(MinMax::MinimumNumber),
309                    _ => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
310                };
311
312                // Initialize with first lane, then proceed with the rest.
313                let mut res = self.read_immediate(&self.project_index(&op, 0)?)?;
314                if #[allow(non_exhaustive_omitted_patterns)] match which {
    Op::MirOpBool(_) => true,
    _ => false,
}matches!(which, Op::MirOpBool(_)) {
315                    // Convert to `bool` scalar.
316                    res = imm_from_bool(simd_element_to_bool(res)?);
317                }
318                for i in 1..op_len {
319                    let op = self.read_immediate(&self.project_index(&op, i)?)?;
320                    res = match which {
321                        Op::MirOp(mir_op) => self.binary_op(mir_op, &res, &op)?,
322                        Op::MirOpBool(mir_op) => {
323                            let op = imm_from_bool(simd_element_to_bool(op)?);
324                            self.binary_op(mir_op, &res, &op)?
325                        }
326                        Op::MinMax(mmop) => {
327                            if #[allow(non_exhaustive_omitted_patterns)] match res.layout.ty.kind() {
    ty::Float(_) => true,
    _ => false,
}matches!(res.layout.ty.kind(), ty::Float(_)) {
328                                ImmTy::from_scalar(self.fminmax_op(mmop, &res, &op)?, res.layout)
329                            } else {
330                                // Just boring integers, no NaNs to worry about.
331                                let mirop = match mmop {
332                                    MinMax::MinimumNumber | MinMax::Minimum => BinOp::Le,
333                                    MinMax::MaximumNumber | MinMax::Maximum => BinOp::Ge,
334                                };
335                                if self.binary_op(mirop, &res, &op)?.to_scalar().to_bool()? {
336                                    res
337                                } else {
338                                    op
339                                }
340                            }
341                        }
342                    };
343                }
344                self.write_immediate(*res, &dest)?;
345            }
346            sym::simd_reduce_add_ordered | sym::simd_reduce_mul_ordered => {
347                use mir::BinOp;
348
349                let (op, op_len) = self.project_to_simd(&args[0])?;
350                let init = self.read_immediate(&args[1])?;
351
352                let mir_op = match intrinsic_name {
353                    sym::simd_reduce_add_ordered => BinOp::Add,
354                    sym::simd_reduce_mul_ordered => BinOp::Mul,
355                    _ => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
356                };
357
358                let mut res = init;
359                for i in 0..op_len {
360                    let op = self.read_immediate(&self.project_index(&op, i)?)?;
361                    res = self.binary_op(mir_op, &res, &op)?;
362                }
363                self.write_immediate(*res, &dest)?;
364            }
365            sym::simd_select => {
366                let (mask, mask_len) = self.project_to_simd(&args[0])?;
367                let (yes, yes_len) = self.project_to_simd(&args[1])?;
368                let (no, no_len) = self.project_to_simd(&args[2])?;
369                let (dest, dest_len) = self.project_to_simd(&dest)?;
370
371                match (&dest_len, &mask_len) {
    (left_val, right_val) => {
        if !(*left_val == *right_val) {
            let kind = ::core::panicking::AssertKind::Eq;
            ::core::panicking::assert_failed(kind, &*left_val, &*right_val,
                ::core::option::Option::None);
        }
    }
};assert_eq!(dest_len, mask_len);
372                match (&dest_len, &yes_len) {
    (left_val, right_val) => {
        if !(*left_val == *right_val) {
            let kind = ::core::panicking::AssertKind::Eq;
            ::core::panicking::assert_failed(kind, &*left_val, &*right_val,
                ::core::option::Option::None);
        }
    }
};assert_eq!(dest_len, yes_len);
373                match (&dest_len, &no_len) {
    (left_val, right_val) => {
        if !(*left_val == *right_val) {
            let kind = ::core::panicking::AssertKind::Eq;
            ::core::panicking::assert_failed(kind, &*left_val, &*right_val,
                ::core::option::Option::None);
        }
    }
};assert_eq!(dest_len, no_len);
374
375                for i in 0..dest_len {
376                    let mask = self.read_immediate(&self.project_index(&mask, i)?)?;
377                    let yes = self.read_immediate(&self.project_index(&yes, i)?)?;
378                    let no = self.read_immediate(&self.project_index(&no, i)?)?;
379                    let dest = self.project_index(&dest, i)?;
380
381                    let val = if simd_element_to_bool(mask)? { yes } else { no };
382                    self.write_immediate(*val, &dest)?;
383                }
384            }
385            // Variant of `select` that takes a bitmask rather than a "vector of bool".
386            sym::simd_select_bitmask => {
387                let mask = &args[0];
388                let (yes, yes_len) = self.project_to_simd(&args[1])?;
389                let (no, no_len) = self.project_to_simd(&args[2])?;
390                let (dest, dest_len) = self.project_to_simd(&dest)?;
391                let bitmask_len = dest_len.next_multiple_of(8);
392                if bitmask_len > 64 {
393                    do yeet ::rustc_middle::mir::interpret::InterpErrorKind::Unsupported(::rustc_middle::mir::interpret::UnsupportedOpInfo::Unsupported(::alloc::__export::must_use({
                    ::alloc::fmt::format(format_args!("simd_select_bitmask: vectors larger than 64 elements are currently not supported"))
                })));throw_unsup_format!(
394                        "simd_select_bitmask: vectors larger than 64 elements are currently not supported"
395                    );
396                }
397
398                match (&dest_len, &yes_len) {
    (left_val, right_val) => {
        if !(*left_val == *right_val) {
            let kind = ::core::panicking::AssertKind::Eq;
            ::core::panicking::assert_failed(kind, &*left_val, &*right_val,
                ::core::option::Option::None);
        }
    }
};assert_eq!(dest_len, yes_len);
399                match (&dest_len, &no_len) {
    (left_val, right_val) => {
        if !(*left_val == *right_val) {
            let kind = ::core::panicking::AssertKind::Eq;
            ::core::panicking::assert_failed(kind, &*left_val, &*right_val,
                ::core::option::Option::None);
        }
    }
};assert_eq!(dest_len, no_len);
400
401                // Read the mask, either as an integer or as an array.
402                let mask: u64 = match mask.layout.ty.kind() {
403                    ty::Uint(_) => {
404                        // Any larger integer type is fine.
405                        if !(mask.layout.size.bits() >= bitmask_len) {
    ::core::panicking::panic("assertion failed: mask.layout.size.bits() >= bitmask_len")
};assert!(mask.layout.size.bits() >= bitmask_len);
406                        self.read_scalar(mask)?.to_bits(mask.layout.size)?.try_into().unwrap()
407                    }
408                    ty::Array(elem, _len) if elem == &self.tcx.types.u8 => {
409                        // The array must have exactly the right size.
410                        match (&mask.layout.size.bits(), &bitmask_len) {
    (left_val, right_val) => {
        if !(*left_val == *right_val) {
            let kind = ::core::panicking::AssertKind::Eq;
            ::core::panicking::assert_failed(kind, &*left_val, &*right_val,
                ::core::option::Option::None);
        }
    }
};assert_eq!(mask.layout.size.bits(), bitmask_len);
411                        // Read the raw bytes.
412                        let mask = mask.assert_mem_place(); // arrays cannot be immediate
413                        let mask_bytes =
414                            self.read_bytes_ptr_strip_provenance(mask.ptr(), mask.layout.size)?;
415                        // Turn them into a `u64` in the right way.
416                        let mask_size = mask.layout.size.bytes_usize();
417                        let mut mask_arr = [0u8; 8];
418                        match self.tcx.data_layout.endian {
419                            Endian::Little => {
420                                // Fill the first N bytes.
421                                mask_arr[..mask_size].copy_from_slice(mask_bytes);
422                                u64::from_le_bytes(mask_arr)
423                            }
424                            Endian::Big => {
425                                // Fill the last N bytes.
426                                let i = mask_arr.len().strict_sub(mask_size);
427                                mask_arr[i..].copy_from_slice(mask_bytes);
428                                u64::from_be_bytes(mask_arr)
429                            }
430                        }
431                    }
432                    _ => ::rustc_middle::util::bug::bug_fmt(format_args!("simd_select_bitmask: invalid mask type {0}",
        mask.layout.ty))bug!("simd_select_bitmask: invalid mask type {}", mask.layout.ty),
433                };
434
435                let dest_len = u32::try_from(dest_len).unwrap();
436                for i in 0..dest_len {
437                    let bit_i = simd_bitmask_index(i, dest_len, self.tcx.data_layout.endian);
438                    let mask = mask & 1u64.strict_shl(bit_i);
439                    let yes = self.read_immediate(&self.project_index(&yes, i.into())?)?;
440                    let no = self.read_immediate(&self.project_index(&no, i.into())?)?;
441                    let dest = self.project_index(&dest, i.into())?;
442
443                    let val = if mask != 0 { yes } else { no };
444                    self.write_immediate(*val, &dest)?;
445                }
446                // The remaining bits of the mask are ignored.
447            }
448            // Converts a "vector of bool" into a bitmask.
449            sym::simd_bitmask => {
450                let (op, op_len) = self.project_to_simd(&args[0])?;
451                let bitmask_len = op_len.next_multiple_of(8);
452                if bitmask_len > 64 {
453                    do yeet ::rustc_middle::mir::interpret::InterpErrorKind::Unsupported(::rustc_middle::mir::interpret::UnsupportedOpInfo::Unsupported(::alloc::__export::must_use({
                    ::alloc::fmt::format(format_args!("simd_bitmask: vectors larger than 64 elements are currently not supported"))
                })));throw_unsup_format!(
454                        "simd_bitmask: vectors larger than 64 elements are currently not supported"
455                    );
456                }
457
458                let op_len = u32::try_from(op_len).unwrap();
459                let mut res = 0u64;
460                for i in 0..op_len {
461                    let op = self.read_immediate(&self.project_index(&op, i.into())?)?;
462                    if simd_element_to_bool(op)? {
463                        let bit_i = simd_bitmask_index(i, op_len, self.tcx.data_layout.endian);
464                        res |= 1u64.strict_shl(bit_i);
465                    }
466                }
467                // Write the result, depending on the `dest` type.
468                // Returns either an unsigned integer or array of `u8`.
469                match dest.layout.ty.kind() {
470                    ty::Uint(_) => {
471                        // Any larger integer type is fine, it will be zero-extended.
472                        if !(dest.layout.size.bits() >= bitmask_len) {
    ::core::panicking::panic("assertion failed: dest.layout.size.bits() >= bitmask_len")
};assert!(dest.layout.size.bits() >= bitmask_len);
473                        self.write_scalar(Scalar::from_uint(res, dest.layout.size), &dest)?;
474                    }
475                    ty::Array(elem, _len) if elem == &self.tcx.types.u8 => {
476                        // The array must have exactly the right size.
477                        match (&dest.layout.size.bits(), &bitmask_len) {
    (left_val, right_val) => {
        if !(*left_val == *right_val) {
            let kind = ::core::panicking::AssertKind::Eq;
            ::core::panicking::assert_failed(kind, &*left_val, &*right_val,
                ::core::option::Option::None);
        }
    }
};assert_eq!(dest.layout.size.bits(), bitmask_len);
478                        // We have to write the result byte-for-byte.
479                        let res_size = dest.layout.size.bytes_usize();
480                        let res_bytes;
481                        let res_bytes_slice = match self.tcx.data_layout.endian {
482                            Endian::Little => {
483                                res_bytes = res.to_le_bytes();
484                                &res_bytes[..res_size] // take the first N bytes
485                            }
486                            Endian::Big => {
487                                res_bytes = res.to_be_bytes();
488                                &res_bytes[res_bytes.len().strict_sub(res_size)..] // take the last N bytes
489                            }
490                        };
491                        self.write_bytes_ptr(dest.ptr(), res_bytes_slice.iter().cloned())?;
492                    }
493                    _ => ::rustc_middle::util::bug::bug_fmt(format_args!("simd_bitmask: invalid return type {0}",
        dest.layout.ty))bug!("simd_bitmask: invalid return type {}", dest.layout.ty),
494                }
495            }
496            sym::simd_cast
497            | sym::simd_as
498            | sym::simd_cast_ptr
499            | sym::simd_with_exposed_provenance => {
500                let (op, op_len) = self.project_to_simd(&args[0])?;
501                let (dest, dest_len) = self.project_to_simd(&dest)?;
502
503                match (&dest_len, &op_len) {
    (left_val, right_val) => {
        if !(*left_val == *right_val) {
            let kind = ::core::panicking::AssertKind::Eq;
            ::core::panicking::assert_failed(kind, &*left_val, &*right_val,
                ::core::option::Option::None);
        }
    }
};assert_eq!(dest_len, op_len);
504
505                let unsafe_cast = intrinsic_name == sym::simd_cast;
506                let safe_cast = intrinsic_name == sym::simd_as;
507                let ptr_cast = intrinsic_name == sym::simd_cast_ptr;
508                let from_exposed_cast = intrinsic_name == sym::simd_with_exposed_provenance;
509
510                for i in 0..dest_len {
511                    let op = self.read_immediate(&self.project_index(&op, i)?)?;
512                    let dest = self.project_index(&dest, i)?;
513
514                    let val = match (op.layout.ty.kind(), dest.layout.ty.kind()) {
515                        // Int-to-(int|float): always safe
516                        (ty::Int(_) | ty::Uint(_), ty::Int(_) | ty::Uint(_) | ty::Float(_))
517                            if safe_cast || unsafe_cast =>
518                            self.int_to_int_or_float(&op, dest.layout)?,
519                        // Float-to-float: always safe
520                        (ty::Float(_), ty::Float(_)) if safe_cast || unsafe_cast =>
521                            self.float_to_float_or_int(&op, dest.layout)?,
522                        // Float-to-int in safe mode
523                        (ty::Float(_), ty::Int(_) | ty::Uint(_)) if safe_cast =>
524                            self.float_to_float_or_int(&op, dest.layout)?,
525                        // Float-to-int in unchecked mode
526                        (ty::Float(_), ty::Int(_) | ty::Uint(_)) if unsafe_cast => {
527                            self.float_to_int_checked(&op, dest.layout, Round::TowardZero)?
528                                .ok_or_else(|| {
529                                    ::rustc_middle::mir::interpret::InterpErrorKind::UndefinedBehavior(::rustc_middle::mir::interpret::UndefinedBehaviorInfo::Ub(::alloc::__export::must_use({
                ::alloc::fmt::format(format_args!("`simd_cast` intrinsic called on {1} which cannot be represented in target type `{0:?}`",
                        dest.layout.ty, op))
            })))err_ub_format!(
530                                        "`simd_cast` intrinsic called on {op} which cannot be represented in target type `{:?}`",
531                                        dest.layout.ty
532                                    )
533                                })?
534                        }
535                        // Ptr-to-ptr cast
536                        (ty::RawPtr(..), ty::RawPtr(..)) if ptr_cast =>
537                            self.ptr_to_ptr(&op, dest.layout)?,
538                        // Int->Ptr casts
539                        (ty::Int(_) | ty::Uint(_), ty::RawPtr(..)) if from_exposed_cast =>
540                            self.pointer_with_exposed_provenance_cast(&op, dest.layout)?,
541                        // Error otherwise
542                        _ =>
543                            do yeet ::rustc_middle::mir::interpret::InterpErrorKind::Unsupported(::rustc_middle::mir::interpret::UnsupportedOpInfo::Unsupported(::alloc::__export::must_use({
                    ::alloc::fmt::format(format_args!("Unsupported SIMD cast from element type {0} to {1}",
                            op.layout.ty, dest.layout.ty))
                })))throw_unsup_format!(
544                                "Unsupported SIMD cast from element type {from_ty} to {to_ty}",
545                                from_ty = op.layout.ty,
546                                to_ty = dest.layout.ty,
547                            ),
548                    };
549                    self.write_immediate(*val, &dest)?;
550                }
551            }
552            sym::simd_shuffle_const_generic => {
553                let (left, left_len) = self.project_to_simd(&args[0])?;
554                let (right, right_len) = self.project_to_simd(&args[1])?;
555                let (dest, dest_len) = self.project_to_simd(&dest)?;
556
557                let index = generic_args[2].expect_const().to_branch();
558                let index_len = index.len();
559
560                match (&left_len, &right_len) {
    (left_val, right_val) => {
        if !(*left_val == *right_val) {
            let kind = ::core::panicking::AssertKind::Eq;
            ::core::panicking::assert_failed(kind, &*left_val, &*right_val,
                ::core::option::Option::None);
        }
    }
};assert_eq!(left_len, right_len);
561                match (&u64::try_from(index_len).unwrap(), &dest_len) {
    (left_val, right_val) => {
        if !(*left_val == *right_val) {
            let kind = ::core::panicking::AssertKind::Eq;
            ::core::panicking::assert_failed(kind, &*left_val, &*right_val,
                ::core::option::Option::None);
        }
    }
};assert_eq!(u64::try_from(index_len).unwrap(), dest_len);
562
563                for i in 0..dest_len {
564                    let src_index: u64 =
565                        index[usize::try_from(i).unwrap()].to_leaf().to_u32().into();
566                    let dest = self.project_index(&dest, i)?;
567
568                    let val = if src_index < left_len {
569                        self.read_immediate(&self.project_index(&left, src_index)?)?
570                    } else if src_index < left_len.strict_add(right_len) {
571                        let right_idx = src_index.strict_sub(left_len);
572                        self.read_immediate(&self.project_index(&right, right_idx)?)?
573                    } else {
574                        do yeet ::rustc_middle::mir::interpret::InterpErrorKind::UndefinedBehavior(::rustc_middle::mir::interpret::UndefinedBehaviorInfo::Ub(::alloc::__export::must_use({
                    ::alloc::fmt::format(format_args!("`simd_shuffle_const_generic` index {0} is out-of-bounds for 2 vectors with length {1}",
                            src_index, dest_len))
                })));throw_ub_format!(
575                            "`simd_shuffle_const_generic` index {src_index} is out-of-bounds for 2 vectors with length {dest_len}"
576                        );
577                    };
578                    self.write_immediate(*val, &dest)?;
579                }
580            }
581            sym::simd_shuffle => {
582                let (left, left_len) = self.project_to_simd(&args[0])?;
583                let (right, right_len) = self.project_to_simd(&args[1])?;
584                let (index, index_len) = self.project_to_simd(&args[2])?;
585                let (dest, dest_len) = self.project_to_simd(&dest)?;
586
587                match (&left_len, &right_len) {
    (left_val, right_val) => {
        if !(*left_val == *right_val) {
            let kind = ::core::panicking::AssertKind::Eq;
            ::core::panicking::assert_failed(kind, &*left_val, &*right_val,
                ::core::option::Option::None);
        }
    }
};assert_eq!(left_len, right_len);
588                match (&index_len, &dest_len) {
    (left_val, right_val) => {
        if !(*left_val == *right_val) {
            let kind = ::core::panicking::AssertKind::Eq;
            ::core::panicking::assert_failed(kind, &*left_val, &*right_val,
                ::core::option::Option::None);
        }
    }
};assert_eq!(index_len, dest_len);
589
590                for i in 0..dest_len {
591                    let src_index: u64 = self
592                        .read_immediate(&self.project_index(&index, i)?)?
593                        .to_scalar()
594                        .to_u32()?
595                        .into();
596                    let dest = self.project_index(&dest, i)?;
597
598                    let val = if src_index < left_len {
599                        self.read_immediate(&self.project_index(&left, src_index)?)?
600                    } else if src_index < left_len.strict_add(right_len) {
601                        let right_idx = src_index.strict_sub(left_len);
602                        self.read_immediate(&self.project_index(&right, right_idx)?)?
603                    } else {
604                        do yeet ::rustc_middle::mir::interpret::InterpErrorKind::UndefinedBehavior(::rustc_middle::mir::interpret::UndefinedBehaviorInfo::Ub(::alloc::__export::must_use({
                    ::alloc::fmt::format(format_args!("`simd_shuffle` index {0} is out-of-bounds for 2 vectors with length {1}",
                            src_index, dest_len))
                })));throw_ub_format!(
605                            "`simd_shuffle` index {src_index} is out-of-bounds for 2 vectors with length {dest_len}"
606                        );
607                    };
608                    self.write_immediate(*val, &dest)?;
609                }
610            }
611            sym::simd_gather => {
612                let (passthru, passthru_len) = self.project_to_simd(&args[0])?;
613                let (ptrs, ptrs_len) = self.project_to_simd(&args[1])?;
614                let (mask, mask_len) = self.project_to_simd(&args[2])?;
615                let (dest, dest_len) = self.project_to_simd(&dest)?;
616
617                match (&dest_len, &passthru_len) {
    (left_val, right_val) => {
        if !(*left_val == *right_val) {
            let kind = ::core::panicking::AssertKind::Eq;
            ::core::panicking::assert_failed(kind, &*left_val, &*right_val,
                ::core::option::Option::None);
        }
    }
};assert_eq!(dest_len, passthru_len);
618                match (&dest_len, &ptrs_len) {
    (left_val, right_val) => {
        if !(*left_val == *right_val) {
            let kind = ::core::panicking::AssertKind::Eq;
            ::core::panicking::assert_failed(kind, &*left_val, &*right_val,
                ::core::option::Option::None);
        }
    }
};assert_eq!(dest_len, ptrs_len);
619                match (&dest_len, &mask_len) {
    (left_val, right_val) => {
        if !(*left_val == *right_val) {
            let kind = ::core::panicking::AssertKind::Eq;
            ::core::panicking::assert_failed(kind, &*left_val, &*right_val,
                ::core::option::Option::None);
        }
    }
};assert_eq!(dest_len, mask_len);
620
621                for i in 0..dest_len {
622                    let passthru = self.read_immediate(&self.project_index(&passthru, i)?)?;
623                    let ptr = self.read_immediate(&self.project_index(&ptrs, i)?)?;
624                    let mask = self.read_immediate(&self.project_index(&mask, i)?)?;
625                    let dest = self.project_index(&dest, i)?;
626
627                    let val = if simd_element_to_bool(mask)? {
628                        let place = self.deref_pointer(&ptr)?;
629                        self.read_immediate(&place)?
630                    } else {
631                        passthru
632                    };
633                    self.write_immediate(*val, &dest)?;
634                }
635            }
636            sym::simd_scatter => {
637                let (value, value_len) = self.project_to_simd(&args[0])?;
638                let (ptrs, ptrs_len) = self.project_to_simd(&args[1])?;
639                let (mask, mask_len) = self.project_to_simd(&args[2])?;
640
641                match (&ptrs_len, &value_len) {
    (left_val, right_val) => {
        if !(*left_val == *right_val) {
            let kind = ::core::panicking::AssertKind::Eq;
            ::core::panicking::assert_failed(kind, &*left_val, &*right_val,
                ::core::option::Option::None);
        }
    }
};assert_eq!(ptrs_len, value_len);
642                match (&ptrs_len, &mask_len) {
    (left_val, right_val) => {
        if !(*left_val == *right_val) {
            let kind = ::core::panicking::AssertKind::Eq;
            ::core::panicking::assert_failed(kind, &*left_val, &*right_val,
                ::core::option::Option::None);
        }
    }
};assert_eq!(ptrs_len, mask_len);
643
644                for i in 0..ptrs_len {
645                    let value = self.read_immediate(&self.project_index(&value, i)?)?;
646                    let ptr = self.read_immediate(&self.project_index(&ptrs, i)?)?;
647                    let mask = self.read_immediate(&self.project_index(&mask, i)?)?;
648
649                    if simd_element_to_bool(mask)? {
650                        let place = self.deref_pointer(&ptr)?;
651                        self.write_immediate(*value, &place)?;
652                    }
653                }
654            }
655            sym::simd_masked_load => {
656                let dest_layout = dest.layout;
657
658                let (mask, mask_len) = self.project_to_simd(&args[0])?;
659                let ptr = self.read_pointer(&args[1])?;
660                let (default, default_len) = self.project_to_simd(&args[2])?;
661                let (dest, dest_len) = self.project_to_simd(&dest)?;
662
663                match (&dest_len, &mask_len) {
    (left_val, right_val) => {
        if !(*left_val == *right_val) {
            let kind = ::core::panicking::AssertKind::Eq;
            ::core::panicking::assert_failed(kind, &*left_val, &*right_val,
                ::core::option::Option::None);
        }
    }
};assert_eq!(dest_len, mask_len);
664                match (&dest_len, &default_len) {
    (left_val, right_val) => {
        if !(*left_val == *right_val) {
            let kind = ::core::panicking::AssertKind::Eq;
            ::core::panicking::assert_failed(kind, &*left_val, &*right_val,
                ::core::option::Option::None);
        }
    }
};assert_eq!(dest_len, default_len);
665
666                self.check_simd_ptr_alignment(
667                    ptr,
668                    dest_layout,
669                    generic_args[3].expect_const().to_branch()[0].to_leaf().to_simd_alignment(),
670                )?;
671
672                for i in 0..dest_len {
673                    let mask = self.read_immediate(&self.project_index(&mask, i)?)?;
674                    let default = self.read_immediate(&self.project_index(&default, i)?)?;
675                    let dest = self.project_index(&dest, i)?;
676
677                    let val = if simd_element_to_bool(mask)? {
678                        // Size * u64 is implemented as always checked
679                        let ptr = ptr.wrapping_offset(dest.layout.size * i, self);
680                        // we have already checked the alignment requirements
681                        let place = self.ptr_to_mplace_unaligned(ptr, dest.layout);
682                        self.read_immediate(&place)?
683                    } else {
684                        default
685                    };
686                    self.write_immediate(*val, &dest)?;
687                }
688            }
689            sym::simd_masked_store => {
690                let (mask, mask_len) = self.project_to_simd(&args[0])?;
691                let ptr = self.read_pointer(&args[1])?;
692                let (vals, vals_len) = self.project_to_simd(&args[2])?;
693
694                match (&mask_len, &vals_len) {
    (left_val, right_val) => {
        if !(*left_val == *right_val) {
            let kind = ::core::panicking::AssertKind::Eq;
            ::core::panicking::assert_failed(kind, &*left_val, &*right_val,
                ::core::option::Option::None);
        }
    }
};assert_eq!(mask_len, vals_len);
695
696                self.check_simd_ptr_alignment(
697                    ptr,
698                    args[2].layout,
699                    generic_args[3].expect_const().to_branch()[0].to_leaf().to_simd_alignment(),
700                )?;
701
702                for i in 0..vals_len {
703                    let mask = self.read_immediate(&self.project_index(&mask, i)?)?;
704                    let val = self.read_immediate(&self.project_index(&vals, i)?)?;
705
706                    if simd_element_to_bool(mask)? {
707                        // Size * u64 is implemented as always checked
708                        let ptr = ptr.wrapping_offset(val.layout.size * i, self);
709                        // we have already checked the alignment requirements
710                        let place = self.ptr_to_mplace_unaligned(ptr, val.layout);
711                        self.write_immediate(*val, &place)?
712                    };
713                }
714            }
715            sym::simd_fma | sym::simd_relaxed_fma => {
716                // `simd_fma` should always deterministically use `mul_add`, whereas `relaxed_fma`
717                // is non-deterministic, and can use either `mul_add` or `a * b + c`
718                let typ = match intrinsic_name {
719                    sym::simd_fma => MulAddType::Fused,
720                    sym::simd_relaxed_fma => MulAddType::Nondeterministic,
721                    _ => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
722                };
723
724                let (a, a_len) = self.project_to_simd(&args[0])?;
725                let (b, b_len) = self.project_to_simd(&args[1])?;
726                let (c, c_len) = self.project_to_simd(&args[2])?;
727                let (dest, dest_len) = self.project_to_simd(&dest)?;
728
729                match (&dest_len, &a_len) {
    (left_val, right_val) => {
        if !(*left_val == *right_val) {
            let kind = ::core::panicking::AssertKind::Eq;
            ::core::panicking::assert_failed(kind, &*left_val, &*right_val,
                ::core::option::Option::None);
        }
    }
};assert_eq!(dest_len, a_len);
730                match (&dest_len, &b_len) {
    (left_val, right_val) => {
        if !(*left_val == *right_val) {
            let kind = ::core::panicking::AssertKind::Eq;
            ::core::panicking::assert_failed(kind, &*left_val, &*right_val,
                ::core::option::Option::None);
        }
    }
};assert_eq!(dest_len, b_len);
731                match (&dest_len, &c_len) {
    (left_val, right_val) => {
        if !(*left_val == *right_val) {
            let kind = ::core::panicking::AssertKind::Eq;
            ::core::panicking::assert_failed(kind, &*left_val, &*right_val,
                ::core::option::Option::None);
        }
    }
};assert_eq!(dest_len, c_len);
732
733                for i in 0..dest_len {
734                    let a = self.read_scalar(&self.project_index(&a, i)?)?;
735                    let b = self.read_scalar(&self.project_index(&b, i)?)?;
736                    let c = self.read_scalar(&self.project_index(&c, i)?)?;
737                    let dest = self.project_index(&dest, i)?;
738
739                    let ty::Float(float_ty) = dest.layout.ty.kind() else {
740                        ::rustc_middle::util::bug::span_bug_fmt(self.cur_span(),
    format_args!("{0} operand is not a float", intrinsic_name))span_bug!(self.cur_span(), "{} operand is not a float", intrinsic_name)
741                    };
742
743                    let val = match float_ty {
744                        FloatTy::F16 => self.float_muladd::<Half>(a, b, c, typ)?,
745                        FloatTy::F32 => self.float_muladd::<Single>(a, b, c, typ)?,
746                        FloatTy::F64 => self.float_muladd::<Double>(a, b, c, typ)?,
747                        FloatTy::F128 => self.float_muladd::<Quad>(a, b, c, typ)?,
748                    };
749                    self.write_scalar(val, &dest)?;
750                }
751            }
752            sym::simd_funnel_shl | sym::simd_funnel_shr => {
753                let (left, _) = self.project_to_simd(&args[0])?;
754                let (right, _) = self.project_to_simd(&args[1])?;
755                let (shift, _) = self.project_to_simd(&args[2])?;
756                let (dest, _) = self.project_to_simd(&dest)?;
757
758                let (len, elem_ty) = args[0].layout.ty.simd_size_and_type(*self.tcx);
759                let (elem_size, _signed) = elem_ty.int_size_and_signed(*self.tcx);
760                let elem_size_bits = u128::from(elem_size.bits());
761
762                let is_left = intrinsic_name == sym::simd_funnel_shl;
763
764                for i in 0..len {
765                    let left =
766                        self.read_scalar(&self.project_index(&left, i)?)?.to_bits(elem_size)?;
767                    let right =
768                        self.read_scalar(&self.project_index(&right, i)?)?.to_bits(elem_size)?;
769                    let shift_bits =
770                        self.read_scalar(&self.project_index(&shift, i)?)?.to_bits(elem_size)?;
771
772                    if shift_bits >= elem_size_bits {
773                        do yeet ::rustc_middle::mir::interpret::InterpErrorKind::UndefinedBehavior(::rustc_middle::mir::interpret::UndefinedBehaviorInfo::Ub(::alloc::__export::must_use({
                    ::alloc::fmt::format(format_args!("overflowing shift by {0} in `{1}` in lane {2}",
                            shift_bits, intrinsic_name, i))
                })));throw_ub_format!(
774                            "overflowing shift by {shift_bits} in `{intrinsic_name}` in lane {i}"
775                        );
776                    }
777                    let inv_shift_bits = u32::try_from(elem_size_bits - shift_bits).unwrap();
778
779                    // A funnel shift left by S can be implemented as `(x << S) | y.unbounded_shr(SIZE - S)`.
780                    // The `unbounded_shr` is needed because otherwise if `S = 0`, it would be `x | y`
781                    // when it should be `x`.
782                    //
783                    // This selects the least-significant `SIZE - S` bits of `x`, followed by the `S` most
784                    // significant bits of `y`. As `left` and `right` both occupy the lower `SIZE` bits,
785                    // we can treat the lower `SIZE` bits as an integer of the right width and use
786                    // the same implementation, but on a zero-extended `x` and `y`. This works because
787                    // `x << S` just pushes the `SIZE-S` MSBs out, and `y >> (SIZE - S)` shifts in
788                    // zeros, as it is zero-extended. To the lower `SIZE` bits, this looks just like a
789                    // funnel shift left.
790                    //
791                    // Note that the `unbounded_sh{l,r}`s are needed only in case we are using this on
792                    // `u128xN` and `inv_shift_bits == 128`.
793                    let result_bits = if is_left {
794                        (left << shift_bits) | right.unbounded_shr(inv_shift_bits)
795                    } else {
796                        left.unbounded_shl(inv_shift_bits) | (right >> shift_bits)
797                    };
798                    let (result, _overflow) = ScalarInt::truncate_from_uint(result_bits, elem_size);
799
800                    let dest = self.project_index(&dest, i)?;
801                    self.write_scalar(result, &dest)?;
802                }
803            }
804
805            // Unsupported intrinsic: skip the return_to_block below.
806            _ => return interp_ok(false),
807        }
808
809        {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_const_eval/src/interpret/intrinsics/simd.rs:809",
                        "rustc_const_eval::interpret::intrinsics::simd",
                        ::tracing::Level::TRACE,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_const_eval/src/interpret/intrinsics/simd.rs"),
                        ::tracing_core::__macro_support::Option::Some(809u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_const_eval::interpret::intrinsics::simd"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::TRACE <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                let mut iter = __CALLSITE.metadata().fields().iter();
                __CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&format_args!("{0:?}",
                                                    self.dump_place(&dest.clone().into())) as &dyn Value))])
            });
    } else { ; }
};trace!("{:?}", self.dump_place(&dest.clone().into()));
810        self.return_to_block(ret)?;
811        interp_ok(true)
812    }
813
814    fn fminmax_op(
815        &self,
816        op: MinMax,
817        left: &ImmTy<'tcx, M::Provenance>,
818        right: &ImmTy<'tcx, M::Provenance>,
819    ) -> InterpResult<'tcx, Scalar<M::Provenance>> {
820        match (&left.layout.ty, &right.layout.ty) {
    (left_val, right_val) => {
        if !(*left_val == *right_val) {
            let kind = ::core::panicking::AssertKind::Eq;
            ::core::panicking::assert_failed(kind, &*left_val, &*right_val,
                ::core::option::Option::None);
        }
    }
};assert_eq!(left.layout.ty, right.layout.ty);
821        let ty::Float(float_ty) = left.layout.ty.kind() else {
822            ::rustc_middle::util::bug::bug_fmt(format_args!("fmax operand is not a float"))bug!("fmax operand is not a float")
823        };
824        let left = left.to_scalar();
825        let right = right.to_scalar();
826        interp_ok(match float_ty {
827            FloatTy::F16 => self.float_minmax::<Half>(left, right, op)?,
828            FloatTy::F32 => self.float_minmax::<Single>(left, right, op)?,
829            FloatTy::F64 => self.float_minmax::<Double>(left, right, op)?,
830            FloatTy::F128 => self.float_minmax::<Quad>(left, right, op)?,
831        })
832    }
833
834    fn check_simd_ptr_alignment(
835        &self,
836        ptr: Pointer<Option<M::Provenance>>,
837        vector_layout: TyAndLayout<'tcx>,
838        alignment: SimdAlign,
839    ) -> InterpResult<'tcx> {
840        // Packed SIMD types with non-power-of-two element counts use BackendRepr::Memory
841        // instead of BackendRepr::SimdVector. We need to handle both cases.
842        // FIXME: remove the BackendRepr::Memory case when SIMD vectors are always passed as BackendRepr::SimdVector.
843        if !vector_layout.ty.is_simd() {
    {
        ::core::panicking::panic_fmt(format_args!("check_simd_ptr_alignment called on non-SIMD type"));
    }
};assert!(vector_layout.ty.is_simd(), "check_simd_ptr_alignment called on non-SIMD type");
844        match vector_layout.backend_repr {
845            BackendRepr::SimdVector { .. } | BackendRepr::Memory { .. } => {}
846            _ => {
847                ::rustc_middle::util::bug::span_bug_fmt(self.cur_span(),
    format_args!("SIMD type has unexpected backend_repr: {0:?}",
        vector_layout.backend_repr));span_bug!(
848                    self.cur_span(),
849                    "SIMD type has unexpected backend_repr: {:?}",
850                    vector_layout.backend_repr
851                );
852            }
853        }
854
855        let align = match alignment {
856            ty::SimdAlign::Unaligned => {
857                // The pointer is supposed to be unaligned, so no check is required.
858                return interp_ok(());
859            }
860            ty::SimdAlign::Element => {
861                // Take the alignment of the only field, which is an array and therefore has the same
862                // alignment as the element type.
863                vector_layout.field(self, 0).align.abi
864            }
865            ty::SimdAlign::Vector => vector_layout.align.abi,
866        };
867
868        self.check_ptr_align(ptr, align)
869    }
870}
871
872fn simd_bitmask_index(idx: u32, vec_len: u32, endianness: Endian) -> u32 {
873    if !(idx < vec_len) {
    ::core::panicking::panic("assertion failed: idx < vec_len")
};assert!(idx < vec_len);
874    match endianness {
875        Endian::Little => idx,
876        #[expect(clippy::arithmetic_side_effects)] // idx < vec_len
877        Endian::Big => vec_len - 1 - idx, // reverse order of bits
878    }
879}
880
881fn bool_to_simd_element<Prov: Provenance>(b: bool, size: Size) -> Scalar<Prov> {
882    // SIMD uses all-1 as pattern for "true". In two's complement,
883    // -1 has all its bits set to one and `from_int` will truncate or
884    // sign-extend it to `size` as required.
885    let val = if b { -1 } else { 0 };
886    Scalar::from_int(val, size)
887}
888
889fn simd_element_to_bool<Prov: Provenance>(elem: ImmTy<'_, Prov>) -> InterpResult<'_, bool> {
890    if !#[allow(non_exhaustive_omitted_patterns)] match elem.layout.ty.kind() {
            ty::Int(_) | ty::Uint(_) => true,
            _ => false,
        } {
    {
        ::core::panicking::panic_fmt(format_args!("SIMD mask element type must be an integer, but this is `{0}`",
                elem.layout.ty));
    }
};assert!(
891        matches!(elem.layout.ty.kind(), ty::Int(_) | ty::Uint(_)),
892        "SIMD mask element type must be an integer, but this is `{}`",
893        elem.layout.ty
894    );
895    let val = elem.to_scalar().to_int(elem.layout.size)?;
896    interp_ok(match val {
897        0 => false,
898        -1 => true,
899        _ => do yeet ::rustc_middle::mir::interpret::InterpErrorKind::UndefinedBehavior(::rustc_middle::mir::interpret::UndefinedBehaviorInfo::Ub(::alloc::__export::must_use({
                    ::alloc::fmt::format(format_args!("each element of a SIMD mask must be all-0-bits or all-1-bits"))
                })))throw_ub_format!("each element of a SIMD mask must be all-0-bits or all-1-bits"),
900    })
901}