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, SimdAlign};
7use rustc_middle::{err_ub_format, mir, throw_unsup_format, ty};
8use rustc_span::{Symbol, bug, span_bug, 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::GenericArgs<'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]#[doc(hidden)]unsafe impl ::core::clone::TrivialClone
    for Op {
}
#[automatically_derived]impl ::core::clone::Clone for Op {
    #[inline]
    fn clone(&self) -> Self {
        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::unreachable_default_msg()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_span::macros::bug_impl(Some(self.cur_span()),
    format_args!("{0} operand is not a float", intrinsic_name),
    Location::caller())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_span::macros::bug_impl(Some(self.cur_span()),
    format_args!("{0} operand is not a float", intrinsic_name),
    Location::caller())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 => {
149                                    self.float_round::<Half>(op.to_float()?, rounding)?
150                                }
151                                FloatTy::F32 => {
152                                    self.float_round::<Single>(op.to_float()?, rounding)?
153                                }
154                                FloatTy::F64 => {
155                                    self.float_round::<Double>(op.to_float()?, rounding)?
156                                }
157                                FloatTy::F128 => {
158                                    self.float_round::<Quad>(op.to_float()?, rounding)?
159                                }
160                            }
161                        }
162                        Op::Numeric(name) => {
163                            self.numeric_intrinsic(name, op.to_scalar(), op.layout, op.layout)?
164                        }
165                    };
166                    self.write_scalar(val, &dest)?;
167                }
168            }
169            sym::simd_add
170            | sym::simd_sub
171            | sym::simd_mul
172            | sym::simd_div
173            | sym::simd_rem
174            | sym::simd_shl
175            | sym::simd_shr
176            | sym::simd_and
177            | sym::simd_or
178            | sym::simd_xor
179            | sym::simd_eq
180            | sym::simd_ne
181            | sym::simd_lt
182            | sym::simd_le
183            | sym::simd_gt
184            | sym::simd_ge
185            | sym::simd_maximum_number_nsz
186            | sym::simd_minimum_number_nsz
187            | sym::simd_saturating_add
188            | sym::simd_saturating_sub
189            | sym::simd_arith_offset => {
190                use mir::BinOp;
191
192                let (left, left_len) = self.project_to_simd(&args[0])?;
193                let (right, right_len) = self.project_to_simd(&args[1])?;
194                let (dest, dest_len) = self.project_to_simd(&dest)?;
195
196                {
    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);
197                {
    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);
198
199                enum Op {
200                    MirOp(BinOp),
201                    SaturatingOp(BinOp),
202                    FMinMax(MinMax),
203                    WrappingOffset,
204                }
205                let which = match intrinsic_name {
206                    sym::simd_add => Op::MirOp(BinOp::Add),
207                    sym::simd_sub => Op::MirOp(BinOp::Sub),
208                    sym::simd_mul => Op::MirOp(BinOp::Mul),
209                    sym::simd_div => Op::MirOp(BinOp::Div),
210                    sym::simd_rem => Op::MirOp(BinOp::Rem),
211                    sym::simd_shl => Op::MirOp(BinOp::ShlUnchecked),
212                    sym::simd_shr => Op::MirOp(BinOp::ShrUnchecked),
213                    sym::simd_and => Op::MirOp(BinOp::BitAnd),
214                    sym::simd_or => Op::MirOp(BinOp::BitOr),
215                    sym::simd_xor => Op::MirOp(BinOp::BitXor),
216                    sym::simd_eq => Op::MirOp(BinOp::Eq),
217                    sym::simd_ne => Op::MirOp(BinOp::Ne),
218                    sym::simd_lt => Op::MirOp(BinOp::Lt),
219                    sym::simd_le => Op::MirOp(BinOp::Le),
220                    sym::simd_gt => Op::MirOp(BinOp::Gt),
221                    sym::simd_ge => Op::MirOp(BinOp::Ge),
222                    sym::simd_maximum_number_nsz => Op::FMinMax(MinMax::MaximumNumberNsz),
223                    sym::simd_minimum_number_nsz => Op::FMinMax(MinMax::MinimumNumberNsz),
224                    sym::simd_saturating_add => Op::SaturatingOp(BinOp::Add),
225                    sym::simd_saturating_sub => Op::SaturatingOp(BinOp::Sub),
226                    sym::simd_arith_offset => Op::WrappingOffset,
227                    _ => ::core::panicking::unreachable_default_msg()unreachable!(),
228                };
229
230                for i in 0..dest_len {
231                    let left = self.read_immediate(&self.project_index(&left, i)?)?;
232                    let right = self.read_immediate(&self.project_index(&right, i)?)?;
233                    let dest = self.project_index(&dest, i)?;
234                    let val = match which {
235                        Op::MirOp(mir_op) => {
236                            // this does NaN adjustments.
237                            let val = self.binary_op(mir_op, &left, &right).map_err_kind(|kind| {
238                                match kind {
239                                    InterpErrorKind::UndefinedBehavior(UndefinedBehaviorInfo::ShiftOverflow { shift_amount, .. }) => {
240                                        // this resets the interpreter backtrace, but it's not worth avoiding that.
241                                        let shift_amount = match shift_amount {
242                                            Either::Left(v) => v.to_string(),
243                                            Either::Right(v) => v.to_string(),
244                                        };
245                                        ::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}")
246                                    }
247                                    kind => kind
248                                }
249                            })?;
250                            if #[allow(non_exhaustive_omitted_patterns)] match mir_op {
    BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge =>
        true,
    _ => false,
}matches!(
251                                mir_op,
252                                BinOp::Eq
253                                    | BinOp::Ne
254                                    | BinOp::Lt
255                                    | BinOp::Le
256                                    | BinOp::Gt
257                                    | BinOp::Ge
258                            ) {
259                                // Special handling for boolean-returning operations
260                                {
    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);
261                                let val = val.to_scalar().to_bool().unwrap();
262                                bool_to_simd_element(val, dest.layout.size)
263                            } else {
264                                {
    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);
265                                {
    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);
266                                val.to_scalar()
267                            }
268                        }
269                        Op::SaturatingOp(mir_op) => self.saturating_arith(mir_op, &left, &right)?,
270                        Op::WrappingOffset => {
271                            let ptr = left.to_scalar().to_pointer(self);
272                            let offset_count = right.to_scalar().to_target_isize(self)?;
273                            let pointee_ty = left.layout.ty.builtin_deref(true).unwrap();
274
275                            let pointee_size =
276                                i64::try_from(self.layout_of(pointee_ty)?.size.bytes()).unwrap();
277                            let offset_bytes = offset_count.wrapping_mul(pointee_size);
278                            let offset_ptr = ptr.wrapping_signed_offset(offset_bytes, self);
279                            Scalar::from_maybe_pointer(offset_ptr, self)
280                        }
281                        Op::FMinMax(op) => self.fminmax_op(op, &left, &right)?,
282                    };
283                    self.write_scalar(val, &dest)?;
284                }
285            }
286            sym::simd_reduce_and
287            | sym::simd_reduce_or
288            | sym::simd_reduce_xor
289            | sym::simd_reduce_any
290            | sym::simd_reduce_all
291            | sym::simd_reduce_max
292            | sym::simd_reduce_min => {
293                use mir::BinOp;
294
295                let (op, op_len) = self.project_to_simd(&args[0])?;
296
297                let imm_from_bool = |b| {
298                    ImmTy::from_scalar(
299                        Scalar::from_bool(b),
300                        self.layout_of(self.tcx.types.bool).unwrap(),
301                    )
302                };
303
304                enum Op {
305                    MirOp(BinOp),
306                    MirOpBool(BinOp),
307                    MinMax(MinMax),
308                }
309                let which = match intrinsic_name {
310                    sym::simd_reduce_and => Op::MirOp(BinOp::BitAnd),
311                    sym::simd_reduce_or => Op::MirOp(BinOp::BitOr),
312                    sym::simd_reduce_xor => Op::MirOp(BinOp::BitXor),
313                    sym::simd_reduce_any => Op::MirOpBool(BinOp::BitOr),
314                    sym::simd_reduce_all => Op::MirOpBool(BinOp::BitAnd),
315                    sym::simd_reduce_max => Op::MinMax(MinMax::MaximumNumberNsz),
316                    sym::simd_reduce_min => Op::MinMax(MinMax::MinimumNumberNsz),
317                    _ => ::core::panicking::unreachable_default_msg()unreachable!(),
318                };
319
320                // Initialize with first lane, then proceed with the rest.
321                let mut res = self.read_immediate(&self.project_index(&op, 0)?)?;
322                if #[allow(non_exhaustive_omitted_patterns)] match which {
    Op::MirOpBool(_) => true,
    _ => false,
}matches!(which, Op::MirOpBool(_)) {
323                    // Convert to `bool` scalar.
324                    res = imm_from_bool(simd_element_to_bool(res)?);
325                }
326                for i in 1..op_len {
327                    let op = self.read_immediate(&self.project_index(&op, i)?)?;
328                    res = match which {
329                        Op::MirOp(mir_op) => self.binary_op(mir_op, &res, &op)?,
330                        Op::MirOpBool(mir_op) => {
331                            let op = imm_from_bool(simd_element_to_bool(op)?);
332                            self.binary_op(mir_op, &res, &op)?
333                        }
334                        Op::MinMax(mmop) => {
335                            if #[allow(non_exhaustive_omitted_patterns)] match res.layout.ty.kind() {
    ty::Float(_) => true,
    _ => false,
}matches!(res.layout.ty.kind(), ty::Float(_)) {
336                                ImmTy::from_scalar(self.fminmax_op(mmop, &res, &op)?, res.layout)
337                            } else {
338                                // Just boring integers, no NaNs to worry about.
339                                let mirop = match mmop {
340                                    MinMax::MinimumNumberNsz | MinMax::Minimum => BinOp::Le,
341                                    MinMax::MaximumNumberNsz | MinMax::Maximum => BinOp::Ge,
342                                };
343                                if self.binary_op(mirop, &res, &op)?.to_scalar().to_bool()? {
344                                    res
345                                } else {
346                                    op
347                                }
348                            }
349                        }
350                    };
351                }
352                self.write_immediate(*res, &dest)?;
353            }
354            sym::simd_reduce_add_ordered | sym::simd_reduce_mul_ordered => {
355                use mir::BinOp;
356
357                let (op, op_len) = self.project_to_simd(&args[0])?;
358                let init = self.read_immediate(&args[1])?;
359
360                let mir_op = match intrinsic_name {
361                    sym::simd_reduce_add_ordered => BinOp::Add,
362                    sym::simd_reduce_mul_ordered => BinOp::Mul,
363                    _ => ::core::panicking::unreachable_default_msg()unreachable!(),
364                };
365
366                let mut res = init;
367                for i in 0..op_len {
368                    let op = self.read_immediate(&self.project_index(&op, i)?)?;
369                    res = self.binary_op(mir_op, &res, &op)?;
370                }
371                self.write_immediate(*res, &dest)?;
372            }
373            sym::simd_select => {
374                let (mask, mask_len) = self.project_to_simd(&args[0])?;
375                let (yes, yes_len) = self.project_to_simd(&args[1])?;
376                let (no, no_len) = self.project_to_simd(&args[2])?;
377                let (dest, dest_len) = self.project_to_simd(&dest)?;
378
379                {
    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);
380                {
    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);
381                {
    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);
382
383                for i in 0..dest_len {
384                    let mask = self.read_immediate(&self.project_index(&mask, i)?)?;
385                    let yes = self.read_immediate(&self.project_index(&yes, i)?)?;
386                    let no = self.read_immediate(&self.project_index(&no, i)?)?;
387                    let dest = self.project_index(&dest, i)?;
388
389                    let val = if simd_element_to_bool(mask)? { yes } else { no };
390                    self.write_immediate(*val, &dest)?;
391                }
392            }
393            // Variant of `select` that takes a bitmask rather than a "vector of bool".
394            sym::simd_select_bitmask => {
395                let mask = &args[0];
396                let (yes, yes_len) = self.project_to_simd(&args[1])?;
397                let (no, no_len) = self.project_to_simd(&args[2])?;
398                let (dest, dest_len) = self.project_to_simd(&dest)?;
399                let bitmask_len = dest_len.next_multiple_of(8);
400                if bitmask_len > 64 {
401                    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!(
402                        "simd_select_bitmask: vectors larger than 64 elements are currently not supported"
403                    );
404                }
405
406                {
    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);
407                {
    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);
408
409                // Read the mask, either as an integer or as an array.
410                let mask: u64 = match mask.layout.ty.kind() {
411                    ty::Uint(_) => {
412                        // Any larger integer type is fine.
413                        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);
414                        self.read_scalar(mask)?.to_bits(mask.layout.size)?.try_into().unwrap()
415                    }
416                    ty::Array(elem, _len) if elem == &self.tcx.types.u8 => {
417                        // The array must have exactly the right size.
418                        {
    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);
419                        // Read the raw bytes.
420                        let mask = mask.assert_mem_place(); // arrays cannot be immediate
421                        let mask_bytes =
422                            self.read_bytes_ptr_strip_provenance(mask.ptr(), mask.layout.size)?;
423                        // Turn them into a `u64` in the right way.
424                        let mask_size = mask.layout.size.bytes_usize();
425                        let mut mask_arr = [0u8; 8];
426                        match self.tcx.data_layout.endian {
427                            Endian::Little => {
428                                // Fill the first N bytes.
429                                mask_arr[..mask_size].copy_from_slice(mask_bytes);
430                                u64::from_le_bytes(mask_arr)
431                            }
432                            Endian::Big => {
433                                // Fill the last N bytes.
434                                let i = mask_arr.len().strict_sub(mask_size);
435                                mask_arr[i..].copy_from_slice(mask_bytes);
436                                u64::from_be_bytes(mask_arr)
437                            }
438                        }
439                    }
440                    _ => ::rustc_span::macros::bug_impl(None,
    format_args!("simd_select_bitmask: invalid mask type {0}",
        mask.layout.ty), Location::caller())bug!("simd_select_bitmask: invalid mask type {}", mask.layout.ty),
441                };
442
443                let dest_len = u32::try_from(dest_len).unwrap();
444                for i in 0..dest_len {
445                    let bit_i = simd_bitmask_index(i, dest_len, self.tcx.data_layout.endian);
446                    let mask = mask & 1u64.strict_shl(bit_i);
447                    let yes = self.read_immediate(&self.project_index(&yes, i.into())?)?;
448                    let no = self.read_immediate(&self.project_index(&no, i.into())?)?;
449                    let dest = self.project_index(&dest, i.into())?;
450
451                    let val = if mask != 0 { yes } else { no };
452                    self.write_immediate(*val, &dest)?;
453                }
454                // The remaining bits of the mask are ignored.
455            }
456            // Converts a "vector of bool" into a bitmask.
457            sym::simd_bitmask => {
458                let (op, op_len) = self.project_to_simd(&args[0])?;
459                let bitmask_len = op_len.next_multiple_of(8);
460                if bitmask_len > 64 {
461                    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!(
462                        "simd_bitmask: vectors larger than 64 elements are currently not supported"
463                    );
464                }
465
466                let op_len = u32::try_from(op_len).unwrap();
467                let mut res = 0u64;
468                for i in 0..op_len {
469                    let op = self.read_immediate(&self.project_index(&op, i.into())?)?;
470                    if simd_element_to_bool(op)? {
471                        let bit_i = simd_bitmask_index(i, op_len, self.tcx.data_layout.endian);
472                        res |= 1u64.strict_shl(bit_i);
473                    }
474                }
475                // Write the result, depending on the `dest` type.
476                // Returns either an unsigned integer or array of `u8`.
477                match dest.layout.ty.kind() {
478                    ty::Uint(_) => {
479                        // Any larger integer type is fine, it will be zero-extended.
480                        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);
481                        self.write_scalar(Scalar::from_uint(res, dest.layout.size), &dest)?;
482                    }
483                    ty::Array(elem, _len) if elem == &self.tcx.types.u8 => {
484                        // The array must have exactly the right size.
485                        {
    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);
486                        // We have to write the result byte-for-byte.
487                        let res_size = dest.layout.size.bytes_usize();
488                        let res_bytes;
489                        let res_bytes_slice = match self.tcx.data_layout.endian {
490                            Endian::Little => {
491                                res_bytes = res.to_le_bytes();
492                                &res_bytes[..res_size] // take the first N bytes
493                            }
494                            Endian::Big => {
495                                res_bytes = res.to_be_bytes();
496                                &res_bytes[res_bytes.len().strict_sub(res_size)..] // take the last N bytes
497                            }
498                        };
499                        self.write_bytes_ptr(dest.ptr(), res_bytes_slice.iter().cloned())?;
500                    }
501                    _ => ::rustc_span::macros::bug_impl(None,
    format_args!("simd_bitmask: invalid return type {0}", dest.layout.ty),
    Location::caller())bug!("simd_bitmask: invalid return type {}", dest.layout.ty),
502                }
503            }
504            sym::simd_cast
505            | sym::simd_as
506            | sym::simd_cast_ptr
507            | sym::simd_with_exposed_provenance => {
508                let (op, op_len) = self.project_to_simd(&args[0])?;
509                let (dest, dest_len) = self.project_to_simd(&dest)?;
510
511                {
    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);
512
513                let unsafe_cast = intrinsic_name == sym::simd_cast;
514                let safe_cast = intrinsic_name == sym::simd_as;
515                let ptr_cast = intrinsic_name == sym::simd_cast_ptr;
516                let from_exposed_cast = intrinsic_name == sym::simd_with_exposed_provenance;
517
518                for i in 0..dest_len {
519                    let op = self.read_immediate(&self.project_index(&op, i)?)?;
520                    let dest = self.project_index(&dest, i)?;
521
522                    let val = match (op.layout.ty.kind(), dest.layout.ty.kind()) {
523                        // Int-to-(int|float): always safe
524                        (ty::Int(_) | ty::Uint(_), ty::Int(_) | ty::Uint(_) | ty::Float(_))
525                            if safe_cast || unsafe_cast =>
526                            self.int_to_int_or_float(&op, dest.layout)?,
527                        // Float-to-float: always safe
528                        (ty::Float(_), ty::Float(_)) if safe_cast || unsafe_cast =>
529                            self.float_to_float_or_int(&op, dest.layout)?,
530                        // Float-to-int in safe mode
531                        (ty::Float(_), ty::Int(_) | ty::Uint(_)) if safe_cast =>
532                            self.float_to_float_or_int(&op, dest.layout)?,
533                        // Float-to-int in unchecked mode
534                        (ty::Float(_), ty::Int(_) | ty::Uint(_)) if unsafe_cast => {
535                            self.float_to_int_checked(&op, dest.layout, Round::TowardZero)?
536                                .ok_or_else(|| {
537                                    ::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!(
538                                        "`simd_cast` intrinsic called on {op} which cannot be represented in target type `{:?}`",
539                                        dest.layout.ty
540                                    )
541                                })?
542                        }
543                        // Ptr-to-ptr cast
544                        (ty::RawPtr(..), ty::RawPtr(..)) if ptr_cast =>
545                            self.ptr_to_ptr(&op, dest.layout)?,
546                        // Int->Ptr casts
547                        (ty::Int(_) | ty::Uint(_), ty::RawPtr(..)) if from_exposed_cast =>
548                            self.pointer_with_exposed_provenance_cast(&op, dest.layout)?,
549                        // Error otherwise
550                        _ =>
551                            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!(
552                                "Unsupported SIMD cast from element type {from_ty} to {to_ty}",
553                                from_ty = op.layout.ty,
554                                to_ty = dest.layout.ty,
555                            ),
556                    };
557                    self.write_immediate(*val, &dest)?;
558                }
559            }
560            sym::simd_shuffle_const_generic => {
561                let (left, left_len) = self.project_to_simd(&args[0])?;
562                let (right, right_len) = self.project_to_simd(&args[1])?;
563                let (dest, dest_len) = self.project_to_simd(&dest)?;
564
565                let index = generic_args[2].expect_const().to_branch();
566                let index_len = index.len();
567
568                {
    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);
569                {
    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);
570
571                for i in 0..dest_len {
572                    let src_index: u64 =
573                        index[usize::try_from(i).unwrap()].to_leaf().to_u32().into();
574                    let dest = self.project_index(&dest, i)?;
575
576                    let val = if src_index < left_len {
577                        self.read_immediate(&self.project_index(&left, src_index)?)?
578                    } else if src_index < left_len.strict_add(right_len) {
579                        let right_idx = src_index.strict_sub(left_len);
580                        self.read_immediate(&self.project_index(&right, right_idx)?)?
581                    } else {
582                        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!(
583                            "`simd_shuffle_const_generic` index {src_index} is out-of-bounds for 2 vectors with length {dest_len}"
584                        );
585                    };
586                    self.write_immediate(*val, &dest)?;
587                }
588            }
589            sym::simd_shuffle => {
590                let (left, left_len) = self.project_to_simd(&args[0])?;
591                let (right, right_len) = self.project_to_simd(&args[1])?;
592                let (index, index_len) = self.project_to_simd(&args[2])?;
593                let (dest, dest_len) = self.project_to_simd(&dest)?;
594
595                {
    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);
596                {
    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);
597
598                for i in 0..dest_len {
599                    let src_index: u64 = self
600                        .read_immediate(&self.project_index(&index, i)?)?
601                        .to_scalar()
602                        .to_u32()?
603                        .into();
604                    let dest = self.project_index(&dest, i)?;
605
606                    let val = if src_index < left_len {
607                        self.read_immediate(&self.project_index(&left, src_index)?)?
608                    } else if src_index < left_len.strict_add(right_len) {
609                        let right_idx = src_index.strict_sub(left_len);
610                        self.read_immediate(&self.project_index(&right, right_idx)?)?
611                    } else {
612                        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!(
613                            "`simd_shuffle` index {src_index} is out-of-bounds for 2 vectors with length {dest_len}"
614                        );
615                    };
616                    self.write_immediate(*val, &dest)?;
617                }
618            }
619            sym::simd_gather => {
620                let (passthru, passthru_len) = self.project_to_simd(&args[0])?;
621                let (ptrs, ptrs_len) = self.project_to_simd(&args[1])?;
622                let (mask, mask_len) = self.project_to_simd(&args[2])?;
623                let (dest, dest_len) = self.project_to_simd(&dest)?;
624
625                {
    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);
626                {
    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);
627                {
    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);
628
629                for i in 0..dest_len {
630                    let passthru = self.read_immediate(&self.project_index(&passthru, i)?)?;
631                    let ptr = self.read_immediate(&self.project_index(&ptrs, i)?)?;
632                    let mask = self.read_immediate(&self.project_index(&mask, i)?)?;
633                    let dest = self.project_index(&dest, i)?;
634
635                    let val = if simd_element_to_bool(mask)? {
636                        let place = self.deref_pointer(&ptr)?;
637                        self.read_immediate(&place)?
638                    } else {
639                        passthru
640                    };
641                    self.write_immediate(*val, &dest)?;
642                }
643            }
644            sym::simd_scatter => {
645                let (value, value_len) = self.project_to_simd(&args[0])?;
646                let (ptrs, ptrs_len) = self.project_to_simd(&args[1])?;
647                let (mask, mask_len) = self.project_to_simd(&args[2])?;
648
649                {
    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);
650                {
    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);
651
652                for i in 0..ptrs_len {
653                    let value = self.read_immediate(&self.project_index(&value, i)?)?;
654                    let ptr = self.read_immediate(&self.project_index(&ptrs, i)?)?;
655                    let mask = self.read_immediate(&self.project_index(&mask, i)?)?;
656
657                    if simd_element_to_bool(mask)? {
658                        let place = self.deref_pointer(&ptr)?;
659                        self.write_immediate(*value, &place)?;
660                    }
661                }
662            }
663            sym::simd_masked_load => {
664                let dest_layout = dest.layout;
665
666                let (mask, mask_len) = self.project_to_simd(&args[0])?;
667                let ptr = self.read_pointer(&args[1])?;
668                let (default, default_len) = self.project_to_simd(&args[2])?;
669                let (dest, dest_len) = self.project_to_simd(&dest)?;
670
671                {
    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);
672                {
    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);
673
674                self.check_simd_ptr_alignment(
675                    ptr,
676                    dest_layout,
677                    generic_args[3].expect_const().to_branch()[0].to_leaf().to_simd_alignment(),
678                )?;
679
680                for i in 0..dest_len {
681                    let mask = self.read_immediate(&self.project_index(&mask, i)?)?;
682                    let default = self.read_immediate(&self.project_index(&default, i)?)?;
683                    let dest = self.project_index(&dest, i)?;
684
685                    let val = if simd_element_to_bool(mask)? {
686                        // Size * u64 is implemented as always checked
687                        let ptr = ptr.wrapping_offset(dest.layout.size * i, self);
688                        // we have already checked the alignment requirements
689                        let place = self.ptr_to_mplace_unaligned(ptr, dest.layout);
690                        self.read_immediate(&place)?
691                    } else {
692                        default
693                    };
694                    self.write_immediate(*val, &dest)?;
695                }
696            }
697            sym::simd_masked_store => {
698                let (mask, mask_len) = self.project_to_simd(&args[0])?;
699                let ptr = self.read_pointer(&args[1])?;
700                let (vals, vals_len) = self.project_to_simd(&args[2])?;
701
702                {
    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);
703
704                self.check_simd_ptr_alignment(
705                    ptr,
706                    args[2].layout,
707                    generic_args[3].expect_const().to_branch()[0].to_leaf().to_simd_alignment(),
708                )?;
709
710                for i in 0..vals_len {
711                    let mask = self.read_immediate(&self.project_index(&mask, i)?)?;
712                    let val = self.read_immediate(&self.project_index(&vals, i)?)?;
713
714                    if simd_element_to_bool(mask)? {
715                        // Size * u64 is implemented as always checked
716                        let ptr = ptr.wrapping_offset(val.layout.size * i, self);
717                        // we have already checked the alignment requirements
718                        let place = self.ptr_to_mplace_unaligned(ptr, val.layout);
719                        self.write_immediate(*val, &place)?
720                    };
721                }
722            }
723            sym::simd_fma | sym::simd_relaxed_fma => {
724                // `simd_fma` should always deterministically use `mul_add`, whereas `relaxed_fma`
725                // is non-deterministic, and can use either `mul_add` or `a * b + c`
726                let typ = match intrinsic_name {
727                    sym::simd_fma => MulAddType::Fused,
728                    sym::simd_relaxed_fma => MulAddType::Nondeterministic,
729                    _ => ::core::panicking::unreachable_default_msg()unreachable!(),
730                };
731
732                let (a, a_len) = self.project_to_simd(&args[0])?;
733                let (b, b_len) = self.project_to_simd(&args[1])?;
734                let (c, c_len) = self.project_to_simd(&args[2])?;
735                let (dest, dest_len) = self.project_to_simd(&dest)?;
736
737                {
    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);
738                {
    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);
739                {
    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);
740
741                for i in 0..dest_len {
742                    let a = self.read_scalar(&self.project_index(&a, i)?)?;
743                    let b = self.read_scalar(&self.project_index(&b, i)?)?;
744                    let c = self.read_scalar(&self.project_index(&c, i)?)?;
745                    let dest = self.project_index(&dest, i)?;
746
747                    let ty::Float(float_ty) = dest.layout.ty.kind() else {
748                        ::rustc_span::macros::bug_impl(Some(self.cur_span()),
    format_args!("{0} operand is not a float", intrinsic_name),
    Location::caller())span_bug!(self.cur_span(), "{} operand is not a float", intrinsic_name)
749                    };
750
751                    let val = match float_ty {
752                        FloatTy::F16 => self.float_muladd::<Half>(a, b, c, typ)?,
753                        FloatTy::F32 => self.float_muladd::<Single>(a, b, c, typ)?,
754                        FloatTy::F64 => self.float_muladd::<Double>(a, b, c, typ)?,
755                        FloatTy::F128 => self.float_muladd::<Quad>(a, b, c, typ)?,
756                    };
757                    self.write_scalar(val, &dest)?;
758                }
759            }
760            sym::simd_funnel_shl | sym::simd_funnel_shr => {
761                let shift_left = intrinsic_name == sym::simd_funnel_shl;
762
763                let (left, left_len) = self.project_to_simd(&args[0])?;
764                let (right, right_len) = self.project_to_simd(&args[1])?;
765                let (shift, shift_len) = self.project_to_simd(&args[2])?;
766                let (dest, dest_len) = self.project_to_simd(&dest)?;
767
768                {
    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);
769                {
    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);
770                {
    match (&dest_len, &shift_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, shift_len);
771
772                for i in 0..dest_len {
773                    let left = self.read_immediate(&self.project_index(&left, i)?)?;
774                    let right = self.read_immediate(&self.project_index(&right, i)?)?;
775                    let shift_bits = self.read_immediate(&self.project_index(&shift, i)?)?;
776                    let dest = self.project_index(&dest, i)?;
777
778                    let result = self.funnel_shift(&left, &right, &shift_bits, shift_left)
779                        .map_err_kind(|_err| {
780                            ::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))
            })))err_ub_format!(
781                                "overflowing shift by {shift_bits} in `{intrinsic_name}` in lane {i}"
782                            )
783                        })?;
784
785                    self.write_scalar(result, &dest)?;
786                }
787            }
788
789            // Unsupported intrinsic: skip the return_to_block below.
790            _ => return interp_ok(false),
791        }
792
793        {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event /rustc-dev/5b7b37aefcb5b2fba3c2392bc9065b9e4602d494/compiler/rustc_const_eval/src/interpret/intrinsics/simd.rs:793",
                        "rustc_const_eval::interpret::intrinsics::simd",
                        ::tracing::Level::TRACE,
                        ::tracing_core::__macro_support::Option::Some("/rustc-dev/5b7b37aefcb5b2fba3c2392bc9065b9e4602d494/compiler/rustc_const_eval/src/interpret/intrinsics/simd.rs"),
                        ::tracing_core::__macro_support::Option::Some(793u32),
                        ::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};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("{0:?}",
                                                    self.dump_place(&dest.clone().into())) as
                                            &dyn ::tracing::field::Value))])
            });
    } else { ; }
};trace!("{:?}", self.dump_place(&dest.clone().into()));
794        self.return_to_block(ret)?;
795        interp_ok(true)
796    }
797
798    fn fminmax_op(
799        &self,
800        op: MinMax,
801        left: &ImmTy<'tcx, M::Provenance>,
802        right: &ImmTy<'tcx, M::Provenance>,
803    ) -> InterpResult<'tcx, Scalar<M::Provenance>> {
804        {
    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);
805        let ty::Float(float_ty) = left.layout.ty.kind() else {
806            ::rustc_span::macros::bug_impl(None,
    format_args!("fmax operand is not a float"), Location::caller())bug!("fmax operand is not a float")
807        };
808        let left = left.to_scalar();
809        let right = right.to_scalar();
810        interp_ok(match float_ty {
811            FloatTy::F16 => self.float_minmax::<Half>(left, right, op)?,
812            FloatTy::F32 => self.float_minmax::<Single>(left, right, op)?,
813            FloatTy::F64 => self.float_minmax::<Double>(left, right, op)?,
814            FloatTy::F128 => self.float_minmax::<Quad>(left, right, op)?,
815        })
816    }
817
818    fn check_simd_ptr_alignment(
819        &self,
820        ptr: Pointer<Option<M::Provenance>>,
821        vector_layout: TyAndLayout<'tcx>,
822        alignment: SimdAlign,
823    ) -> InterpResult<'tcx> {
824        // Packed SIMD types with non-power-of-two element counts use BackendRepr::Memory
825        // instead of BackendRepr::SimdVector. We need to handle both cases.
826        // FIXME: remove the BackendRepr::Memory case when SIMD vectors are always passed as BackendRepr::SimdVector.
827        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");
828        match vector_layout.backend_repr {
829            BackendRepr::SimdVector { .. } | BackendRepr::Memory { .. } => {}
830            _ => {
831                ::rustc_span::macros::bug_impl(Some(self.cur_span()),
    format_args!("SIMD type has unexpected backend_repr: {0:?}",
        vector_layout.backend_repr), Location::caller());span_bug!(
832                    self.cur_span(),
833                    "SIMD type has unexpected backend_repr: {:?}",
834                    vector_layout.backend_repr
835                );
836            }
837        }
838
839        let align = match alignment {
840            ty::SimdAlign::Unaligned => {
841                // The pointer is supposed to be unaligned, so no check is required.
842                return interp_ok(());
843            }
844            ty::SimdAlign::Element => {
845                // Take the alignment of the only field, which is an array and therefore has the same
846                // alignment as the element type.
847                vector_layout.field(self, 0).align
848            }
849            ty::SimdAlign::Vector => vector_layout.align,
850        };
851
852        self.check_ptr_align(ptr, align)
853    }
854}
855
856fn simd_bitmask_index(idx: u32, vec_len: u32, endianness: Endian) -> u32 {
857    if !(idx < vec_len) {
    ::core::panicking::panic("assertion failed: idx < vec_len")
};assert!(idx < vec_len);
858    match endianness {
859        Endian::Little => idx,
860        #[expect(clippy::arithmetic_side_effects)] // idx < vec_len
861        Endian::Big => vec_len - 1 - idx, // reverse order of bits
862    }
863}
864
865fn bool_to_simd_element<Prov: Provenance>(b: bool, size: Size) -> Scalar<Prov> {
866    // SIMD uses all-1 as pattern for "true". In two's complement,
867    // -1 has all its bits set to one and `from_int` will truncate or
868    // sign-extend it to `size` as required.
869    let val = if b { -1 } else { 0 };
870    Scalar::from_int(val, size)
871}
872
873fn simd_element_to_bool<Prov: Provenance>(elem: ImmTy<'_, Prov>) -> InterpResult<'_, bool> {
874    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!(
875        matches!(elem.layout.ty.kind(), ty::Int(_) | ty::Uint(_)),
876        "SIMD mask element type must be an integer, but this is `{}`",
877        elem.layout.ty
878    );
879    let val = elem.to_scalar().to_int(elem.layout.size)?;
880    interp_ok(match val {
881        0 => false,
882        -1 => true,
883        _ => 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"),
884    })
885}