Skip to main content

miri/intrinsics/x86/
mod.rs

1use rustc_abi::{FieldIdx, Size};
2use rustc_apfloat::Float;
3use rustc_apfloat::ieee::Single;
4use rustc_middle::ty::Ty;
5use rustc_middle::{mir, ty};
6use rustc_span::Symbol;
7use rustc_target::spec::Arch;
8
9use self::helpers::bool_to_simd_element;
10use crate::*;
11
12mod aesni;
13mod avx;
14mod avx2;
15mod avx512;
16mod bmi;
17mod gfni;
18mod sha;
19mod sse;
20mod sse2;
21mod sse3;
22mod sse41;
23mod sse42;
24mod ssse3;
25
26impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
27pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
28    fn emulate_x86_intrinsic(
29        &mut self,
30        link_name: Symbol,
31        args: &[OpTy<'tcx>],
32        dest: &MPlaceTy<'tcx>,
33    ) -> InterpResult<'tcx, EmulateItemResult> {
34        let this = self.eval_context_mut();
35        // Prefix should have already been checked.
36        let unprefixed_name = link_name.as_str().strip_prefix("llvm.x86.").unwrap();
37        match unprefixed_name {
38            // Used to implement the `_addcarry_u{32, 64}` and the `_subborrow_u{32, 64}` functions.
39            // Computes a + b or a - b with input and output carry/borrow. The input carry/borrow is an 8-bit
40            // value, which is interpreted as 1 if it is non-zero. The output carry/borrow is an 8-bit value that will be 0 or 1.
41            // https://www.intel.com/content/www/us/en/docs/cpp-compiler/developer-guide-reference/2021-8/addcarry-u32-addcarry-u64.html
42            // https://www.intel.com/content/www/us/en/docs/cpp-compiler/developer-guide-reference/2021-8/subborrow-u32-subborrow-u64.html
43            "addcarry.32" | "addcarry.64" | "subborrow.32" | "subborrow.64" => {
44                if unprefixed_name.ends_with("64") && this.tcx.sess.target.arch != Arch::X86_64 {
45                    return interp_ok(EmulateItemResult::NotSupported);
46                }
47
48                let [cb_in, a, b] = this.check_shim_sig_llvm_intrinsic(link_name, args)?;
49                let op = if unprefixed_name.starts_with("add") {
50                    mir::BinOp::AddWithOverflow
51                } else {
52                    mir::BinOp::SubWithOverflow
53                };
54
55                let (sum, cb_out) = carrying_add(this, cb_in, a, b, op)?;
56                this.write_scalar(cb_out, &this.project_field(dest, FieldIdx::ZERO)?)?;
57                this.write_immediate(*sum, &this.project_field(dest, FieldIdx::ONE)?)?;
58            }
59
60            // Used to implement the `_mm_pause` function.
61            // The intrinsic is used to hint the processor that the code is in a spin-loop.
62            // It is compiled down to a `pause` instruction. When SSE2 is not available,
63            // the instruction behaves like a no-op, so it is always safe to call the
64            // intrinsic.
65            "sse2.pause" => {
66                let [] = this.check_shim_sig_llvm_intrinsic(link_name, args)?;
67                // Only exhibit the spin-loop hint behavior when SSE2 is enabled.
68                if this.tcx.sess.internal_target_features.contains(&Symbol::intern("sse2")) {
69                    this.yield_active_thread();
70                }
71            }
72
73            "pclmulqdq" | "pclmulqdq.256" | "pclmulqdq.512" => {
74                let mut len = 2; // in units of 64bits
75                this.expect_target_feature_for_intrinsic(link_name, "pclmulqdq")?;
76                if unprefixed_name.ends_with(".256") {
77                    this.expect_target_feature_for_intrinsic(link_name, "vpclmulqdq")?;
78                    len = 4;
79                } else if unprefixed_name.ends_with(".512") {
80                    this.expect_target_feature_for_intrinsic(link_name, "vpclmulqdq")?;
81                    this.expect_target_feature_for_intrinsic(link_name, "avx512f")?;
82                    len = 8;
83                }
84
85                let [left, right, imm] = this.check_shim_sig_llvm_intrinsic(link_name, args)?;
86
87                pclmulqdq(this, left, right, imm, dest, len)?;
88            }
89
90            name if name.starts_with("bmi.") => {
91                return bmi::EvalContextExt::emulate_x86_bmi_intrinsic(this, link_name, args, dest);
92            }
93            // The GFNI extension does not get its own namespace.
94            // Check for instruction names instead.
95            name if name.starts_with("vgf2p8affine") || name.starts_with("vgf2p8mulb") => {
96                return gfni::EvalContextExt::emulate_x86_gfni_intrinsic(
97                    this, link_name, args, dest,
98                );
99            }
100            name if name.starts_with("sha") => {
101                return sha::EvalContextExt::emulate_x86_sha_intrinsic(this, link_name, args, dest);
102            }
103            name if name.starts_with("sse.") => {
104                return sse::EvalContextExt::emulate_x86_sse_intrinsic(this, link_name, args, dest);
105            }
106            name if name.starts_with("sse2.") => {
107                return sse2::EvalContextExt::emulate_x86_sse2_intrinsic(
108                    this, link_name, args, dest,
109                );
110            }
111            name if name.starts_with("sse3.") => {
112                return sse3::EvalContextExt::emulate_x86_sse3_intrinsic(
113                    this, link_name, args, dest,
114                );
115            }
116            name if name.starts_with("ssse3.") => {
117                return ssse3::EvalContextExt::emulate_x86_ssse3_intrinsic(
118                    this, link_name, args, dest,
119                );
120            }
121            name if name.starts_with("sse41.") => {
122                return sse41::EvalContextExt::emulate_x86_sse41_intrinsic(
123                    this, link_name, args, dest,
124                );
125            }
126            name if name.starts_with("sse42.") => {
127                return sse42::EvalContextExt::emulate_x86_sse42_intrinsic(
128                    this, link_name, args, dest,
129                );
130            }
131            name if name.starts_with("aesni.") => {
132                return aesni::EvalContextExt::emulate_x86_aesni_intrinsic(
133                    this, link_name, args, dest,
134                );
135            }
136            name if name.starts_with("avx.") => {
137                return avx::EvalContextExt::emulate_x86_avx_intrinsic(this, link_name, args, dest);
138            }
139            name if name.starts_with("avx2.") => {
140                return avx2::EvalContextExt::emulate_x86_avx2_intrinsic(
141                    this, link_name, args, dest,
142                );
143            }
144            name if name.starts_with("avx512.") => {
145                return avx512::EvalContextExt::emulate_x86_avx512_intrinsic(
146                    this, link_name, args, dest,
147                );
148            }
149
150            _ => return interp_ok(EmulateItemResult::NotSupported),
151        }
152        interp_ok(EmulateItemResult::NeedsReturn)
153    }
154}
155
156#[derive(Copy, Clone)]
157enum FloatBinOp {
158    /// Comparison
159    ///
160    /// The semantics of this operator is a case distinction: we compare the two operands,
161    /// and then we return one of the four booleans `gt`, `lt`, `eq`, `unord` depending on
162    /// which class they fall into.
163    ///
164    /// AVX supports all 16 combinations, SSE only a subset
165    ///
166    /// <https://www.felixcloutier.com/x86/cmpss>
167    /// <https://www.felixcloutier.com/x86/cmpps>
168    /// <https://www.felixcloutier.com/x86/cmpsd>
169    /// <https://www.felixcloutier.com/x86/cmppd>
170    Cmp {
171        /// Result when lhs < rhs
172        gt: bool,
173        /// Result when lhs > rhs
174        lt: bool,
175        /// Result when lhs == rhs
176        eq: bool,
177        /// Result when lhs is NaN or rhs is NaN
178        unord: bool,
179    },
180    /// Minimum value (with SSE semantics)
181    ///
182    /// <https://www.felixcloutier.com/x86/minss>
183    /// <https://www.felixcloutier.com/x86/minps>
184    /// <https://www.felixcloutier.com/x86/minsd>
185    /// <https://www.felixcloutier.com/x86/minpd>
186    Min,
187    /// Maximum value (with SSE semantics)
188    ///
189    /// <https://www.felixcloutier.com/x86/maxss>
190    /// <https://www.felixcloutier.com/x86/maxps>
191    /// <https://www.felixcloutier.com/x86/maxsd>
192    /// <https://www.felixcloutier.com/x86/maxpd>
193    Max,
194}
195
196impl FloatBinOp {
197    /// Convert from the `imm` argument used to specify the comparison
198    /// operation in intrinsics such as `llvm.x86.sse.cmp.ss`.
199    fn cmp_from_imm<'tcx>(
200        ecx: &crate::MiriInterpCx<'tcx>,
201        imm: i8,
202        intrinsic: Symbol,
203    ) -> InterpResult<'tcx, Self> {
204        // Only bits 0..=4 are used, remaining should be zero.
205        if imm & !0b1_1111 != 0 {
206            panic!("invalid `imm` parameter of {intrinsic}: 0x{imm:x}");
207        }
208        // Bit 4 specifies whether the operation is quiet or signaling, which
209        // we do not care in Miri.
210        // Bits 0..=2 specifies the operation.
211        // `gt` indicates the result to be returned when the LHS is strictly
212        // greater than the RHS, and so on.
213        let (gt, lt, eq, mut unord) = match imm & 0b111 {
214            // Equal
215            0x0 => (false, false, true, false),
216            // Less-than
217            0x1 => (false, true, false, false),
218            // Less-or-equal
219            0x2 => (false, true, true, false),
220            // Unordered (either is NaN)
221            0x3 => (false, false, false, true),
222            // Not equal
223            0x4 => (true, true, false, true),
224            // Not less-than
225            0x5 => (true, false, true, true),
226            // Not less-or-equal
227            0x6 => (true, false, false, true),
228            // Ordered (neither is NaN)
229            0x7 => (true, true, true, false),
230            _ => unreachable!(),
231        };
232        // When bit 3 is 1 (only possible in AVX), unord is toggled.
233        if imm & 0b1000 != 0 {
234            ecx.expect_target_feature_for_intrinsic(intrinsic, "avx")?;
235            unord = !unord;
236        }
237        interp_ok(Self::Cmp { gt, lt, eq, unord })
238    }
239}
240
241/// Performs `which` scalar operation on `left` and `right` and returns
242/// the result.
243fn bin_op_float<'tcx, F: rustc_apfloat::Float>(
244    which: FloatBinOp,
245    left: &ImmTy<'tcx>,
246    right: &ImmTy<'tcx>,
247) -> InterpResult<'tcx, Scalar> {
248    match which {
249        FloatBinOp::Cmp { gt, lt, eq, unord } => {
250            let left = left.to_scalar().to_float::<F>()?;
251            let right = right.to_scalar().to_float::<F>()?;
252
253            let res = match left.partial_cmp(&right) {
254                None => unord,
255                Some(std::cmp::Ordering::Less) => lt,
256                Some(std::cmp::Ordering::Equal) => eq,
257                Some(std::cmp::Ordering::Greater) => gt,
258            };
259            interp_ok(bool_to_simd_element(res, Size::from_bits(F::BITS)))
260        }
261        FloatBinOp::Min => {
262            let left_scalar = left.to_scalar();
263            let left = left_scalar.to_float::<F>()?;
264            let right_scalar = right.to_scalar();
265            let right = right_scalar.to_float::<F>()?;
266            // SSE semantics to handle zero and NaN. Note that `x == F::ZERO`
267            // is true when `x` is either +0 or -0.
268            if (left == F::ZERO && right == F::ZERO)
269                || left.is_nan()
270                || right.is_nan()
271                || left >= right
272            {
273                interp_ok(right_scalar)
274            } else {
275                interp_ok(left_scalar)
276            }
277        }
278        FloatBinOp::Max => {
279            let left_scalar = left.to_scalar();
280            let left = left_scalar.to_float::<F>()?;
281            let right_scalar = right.to_scalar();
282            let right = right_scalar.to_float::<F>()?;
283            // SSE semantics to handle zero and NaN. Note that `x == F::ZERO`
284            // is true when `x` is either +0 or -0.
285            if (left == F::ZERO && right == F::ZERO)
286                || left.is_nan()
287                || right.is_nan()
288                || left <= right
289            {
290                interp_ok(right_scalar)
291            } else {
292                interp_ok(left_scalar)
293            }
294        }
295    }
296}
297
298/// Performs `which` operation on the first component of `left` and `right`
299/// and copies the other components from `left`. The result is stored in `dest`.
300fn bin_op_simd_float_first<'tcx, F: rustc_apfloat::Float>(
301    ecx: &mut crate::MiriInterpCx<'tcx>,
302    which: FloatBinOp,
303    left: &OpTy<'tcx>,
304    right: &OpTy<'tcx>,
305    dest: &MPlaceTy<'tcx>,
306) -> InterpResult<'tcx, ()> {
307    let (left, left_len) = ecx.project_to_simd(left)?;
308    let (right, right_len) = ecx.project_to_simd(right)?;
309    let (dest, dest_len) = ecx.project_to_simd(dest)?;
310
311    assert_eq!(dest_len, left_len);
312    assert_eq!(dest_len, right_len);
313
314    let res0 = bin_op_float::<F>(
315        which,
316        &ecx.read_immediate(&ecx.project_index(&left, 0)?)?,
317        &ecx.read_immediate(&ecx.project_index(&right, 0)?)?,
318    )?;
319    ecx.write_scalar(res0, &ecx.project_index(&dest, 0)?)?;
320
321    for i in 1..dest_len {
322        ecx.copy_op(&ecx.project_index(&left, i)?, &ecx.project_index(&dest, i)?)?;
323    }
324
325    interp_ok(())
326}
327
328/// Performs `which` operation on each component of `left` and
329/// `right`, storing the result is stored in `dest`.
330fn bin_op_simd_float_all<'tcx, F: rustc_apfloat::Float>(
331    ecx: &mut crate::MiriInterpCx<'tcx>,
332    which: FloatBinOp,
333    left: &OpTy<'tcx>,
334    right: &OpTy<'tcx>,
335    dest: &MPlaceTy<'tcx>,
336) -> InterpResult<'tcx, ()> {
337    let (left, left_len) = ecx.project_to_simd(left)?;
338    let (right, right_len) = ecx.project_to_simd(right)?;
339    let (dest, dest_len) = ecx.project_to_simd(dest)?;
340
341    assert_eq!(dest_len, left_len);
342    assert_eq!(dest_len, right_len);
343
344    for i in 0..dest_len {
345        let left = ecx.read_immediate(&ecx.project_index(&left, i)?)?;
346        let right = ecx.read_immediate(&ecx.project_index(&right, i)?)?;
347        let dest = ecx.project_index(&dest, i)?;
348
349        let res = bin_op_float::<F>(which, &left, &right)?;
350        ecx.write_scalar(res, &dest)?;
351    }
352
353    interp_ok(())
354}
355
356#[derive(Copy, Clone)]
357enum FloatUnaryOp {
358    /// Approximation of 1/x
359    ///
360    /// <https://www.felixcloutier.com/x86/rcpss>
361    /// <https://www.felixcloutier.com/x86/rcpps>
362    Rcp,
363    /// Approximation of 1/sqrt(x)
364    ///
365    /// <https://www.felixcloutier.com/x86/rsqrtss>
366    /// <https://www.felixcloutier.com/x86/rsqrtps>
367    Rsqrt,
368}
369
370/// Performs `which` scalar operation on `op` and returns the result.
371fn unary_op_f32<'tcx>(
372    ecx: &mut crate::MiriInterpCx<'tcx>,
373    which: FloatUnaryOp,
374    op: &ImmTy<'tcx>,
375) -> InterpResult<'tcx, Scalar> {
376    match which {
377        FloatUnaryOp::Rcp => {
378            let op = op.to_scalar().to_f32()?;
379            let div = (Single::from_u128(1).value / op).value;
380            // Apply a relative error with a magnitude on the order of 2^-12 to simulate the
381            // inaccuracy of RCP.
382            let res = math::apply_random_float_error(ecx, div, -12);
383            interp_ok(Scalar::from_f32(res))
384        }
385        FloatUnaryOp::Rsqrt => {
386            let op = op.to_scalar().to_f32()?;
387            let rsqrt = (Single::from_u128(1).value / math::sqrt(op)).value;
388            // Apply a relative error with a magnitude on the order of 2^-12 to simulate the
389            // inaccuracy of RSQRT.
390            let res = math::apply_random_float_error(ecx, rsqrt, -12);
391            interp_ok(Scalar::from_f32(res))
392        }
393    }
394}
395
396/// Performs `which` operation on the first component of `op` and copies
397/// the other components. The result is stored in `dest`.
398fn unary_op_ss<'tcx>(
399    ecx: &mut crate::MiriInterpCx<'tcx>,
400    which: FloatUnaryOp,
401    op: &OpTy<'tcx>,
402    dest: &MPlaceTy<'tcx>,
403) -> InterpResult<'tcx, ()> {
404    let (op, op_len) = ecx.project_to_simd(op)?;
405    let (dest, dest_len) = ecx.project_to_simd(dest)?;
406
407    assert_eq!(dest_len, op_len);
408
409    let res0 = unary_op_f32(ecx, which, &ecx.read_immediate(&ecx.project_index(&op, 0)?)?)?;
410    ecx.write_scalar(res0, &ecx.project_index(&dest, 0)?)?;
411
412    for i in 1..dest_len {
413        ecx.copy_op(&ecx.project_index(&op, i)?, &ecx.project_index(&dest, i)?)?;
414    }
415
416    interp_ok(())
417}
418
419/// Performs `which` operation on each component of `op`, storing the
420/// result is stored in `dest`.
421fn unary_op_ps<'tcx>(
422    ecx: &mut crate::MiriInterpCx<'tcx>,
423    which: FloatUnaryOp,
424    op: &OpTy<'tcx>,
425    dest: &MPlaceTy<'tcx>,
426) -> InterpResult<'tcx, ()> {
427    let (op, op_len) = ecx.project_to_simd(op)?;
428    let (dest, dest_len) = ecx.project_to_simd(dest)?;
429
430    assert_eq!(dest_len, op_len);
431
432    for i in 0..dest_len {
433        let op = ecx.read_immediate(&ecx.project_index(&op, i)?)?;
434        let dest = ecx.project_index(&dest, i)?;
435
436        let res = unary_op_f32(ecx, which, &op)?;
437        ecx.write_scalar(res, &dest)?;
438    }
439
440    interp_ok(())
441}
442
443enum ShiftOp {
444    /// Shift left, logically (shift in zeros) -- same as shift left, arithmetically
445    Left,
446    /// Shift right, logically (shift in zeros)
447    RightLogic,
448    /// Shift right, arithmetically (shift in sign)
449    RightArith,
450}
451
452/// Shifts each element of `left` by a scalar amount. The shift amount
453/// is determined by the lowest 64 bits of `right` (which is a 128-bit vector).
454///
455/// For logic shifts, when right is larger than BITS - 1, zero is produced.
456/// For arithmetic right-shifts, when right is larger than BITS - 1, the sign
457/// bit is copied to all bits.
458fn shift_simd_by_scalar<'tcx>(
459    ecx: &mut crate::MiriInterpCx<'tcx>,
460    left: &OpTy<'tcx>,
461    right: &OpTy<'tcx>,
462    which: ShiftOp,
463    dest: &MPlaceTy<'tcx>,
464) -> InterpResult<'tcx, ()> {
465    let (left, left_len) = ecx.project_to_simd(left)?;
466    let (dest, dest_len) = ecx.project_to_simd(dest)?;
467
468    assert_eq!(dest_len, left_len);
469    // `right` may have a different length, and we only care about its
470    // lowest 64bit anyway.
471
472    // Get the 64-bit shift operand and convert it to the type expected
473    // by checked_{shl,shr} (u32).
474    // It is ok to saturate the value to u32::MAX because any value
475    // above BITS - 1 will produce the same result.
476    let shift = u32::try_from(extract_first_u64(ecx, right)?).unwrap_or(u32::MAX);
477
478    for i in 0..dest_len {
479        let left = ecx.read_scalar(&ecx.project_index(&left, i)?)?;
480        let dest = ecx.project_index(&dest, i)?;
481
482        let res = match which {
483            ShiftOp::Left => {
484                let left = left.to_uint(dest.layout.size)?;
485                let res = left.checked_shl(shift).unwrap_or(0);
486                // `truncate` is needed as left-shift can make the absolute value larger.
487                Scalar::from_uint(dest.layout.size.truncate(res), dest.layout.size)
488            }
489            ShiftOp::RightLogic => {
490                let left = left.to_uint(dest.layout.size)?;
491                let res = left.checked_shr(shift).unwrap_or(0);
492                // No `truncate` needed as right-shift can only make the absolute value smaller.
493                Scalar::from_uint(res, dest.layout.size)
494            }
495            ShiftOp::RightArith => {
496                let left = left.to_int(dest.layout.size)?;
497                // On overflow, copy the sign bit to the remaining bits
498                let res = left.checked_shr(shift).unwrap_or(left >> 127);
499                // No `truncate` needed as right-shift can only make the absolute value smaller.
500                Scalar::from_int(res, dest.layout.size)
501            }
502        };
503        ecx.write_scalar(res, &dest)?;
504    }
505
506    interp_ok(())
507}
508
509fn shift_simd_by_simd<'tcx>(
510    ecx: &mut crate::MiriInterpCx<'tcx>,
511    left: &OpTy<'tcx>,
512    right: &OpTy<'tcx>,
513    which: ShiftOp,
514    dest: &MPlaceTy<'tcx>,
515) -> InterpResult<'tcx, ()> {
516    let (left, left_len) = ecx.project_to_simd(left)?;
517    let (right, right_len) = ecx.project_to_simd(right)?;
518    let (dest, dest_len) = ecx.project_to_simd(dest)?;
519
520    assert_eq!(dest_len, left_len);
521    assert_eq!(dest_len, right_len);
522
523    for i in 0..dest_len {
524        let left = ecx.read_scalar(&ecx.project_index(&left, i)?)?;
525        let right = ecx.read_scalar(&ecx.project_index(&right, i)?)?;
526        let dest = ecx.project_index(&dest, i)?;
527
528        // It is ok to saturate the value to u32::MAX because any value
529        // above BITS - 1 will produce the same result.
530        let shift = u32::try_from(right.to_uint(dest.layout.size)?).unwrap_or(u32::MAX);
531
532        let res = match which {
533            ShiftOp::Left => {
534                let left = left.to_uint(dest.layout.size)?;
535                let res = left.checked_shl(shift).unwrap_or(0);
536                // `truncate` is needed as left-shift can make the absolute value larger.
537                Scalar::from_uint(dest.layout.size.truncate(res), dest.layout.size)
538            }
539            ShiftOp::RightLogic => {
540                let left = left.to_uint(dest.layout.size)?;
541                let res = left.checked_shr(shift).unwrap_or(0);
542                // No `truncate` needed as right-shift can only make the absolute value smaller.
543                Scalar::from_uint(res, dest.layout.size)
544            }
545            ShiftOp::RightArith => {
546                let left = left.to_int(dest.layout.size)?;
547                // On overflow, copy the sign bit to the remaining bits
548                let res = left.checked_shr(shift).unwrap_or(left >> 127);
549                // No `truncate` needed as right-shift can only make the absolute value smaller.
550                Scalar::from_int(res, dest.layout.size)
551            }
552        };
553        ecx.write_scalar(res, &dest)?;
554    }
555
556    interp_ok(())
557}
558
559/// Takes a 128-bit vector, transmutes it to `[u64; 2]` and extracts
560/// the first value.
561fn extract_first_u64<'tcx>(
562    ecx: &crate::MiriInterpCx<'tcx>,
563    op: &OpTy<'tcx>,
564) -> InterpResult<'tcx, u64> {
565    // Transmute vector to `[u64; 2]`
566    let array_layout = ecx.layout_of(Ty::new_array(ecx.tcx.tcx, ecx.tcx.types.u64, 2))?;
567    let op = op.transmute(array_layout, ecx)?;
568
569    // Get the first u64 from the array
570    ecx.read_scalar(&ecx.project_index(&op, 0)?)?.to_u64()
571}
572
573// Rounds the first element of `right` according to `rounding`
574// and copies the remaining elements from `left`.
575fn round_first<'tcx, F: rustc_apfloat::Float>(
576    ecx: &mut crate::MiriInterpCx<'tcx>,
577    left: &OpTy<'tcx>,
578    right: &OpTy<'tcx>,
579    rounding: &OpTy<'tcx>,
580    dest: &MPlaceTy<'tcx>,
581) -> InterpResult<'tcx, ()> {
582    let (left, left_len) = ecx.project_to_simd(left)?;
583    let (right, right_len) = ecx.project_to_simd(right)?;
584    let (dest, dest_len) = ecx.project_to_simd(dest)?;
585
586    assert_eq!(dest_len, left_len);
587    assert_eq!(dest_len, right_len);
588
589    let rounding = rounding_from_imm(ecx.read_scalar(rounding)?.to_i32()?)?;
590
591    let op0: F = ecx.read_scalar(&ecx.project_index(&right, 0)?)?.to_float()?;
592    let res = op0.round_to_integral(rounding).value;
593    ecx.write_scalar(
594        Scalar::from_uint(res.to_bits(), Size::from_bits(F::BITS)),
595        &ecx.project_index(&dest, 0)?,
596    )?;
597
598    for i in 1..dest_len {
599        ecx.copy_op(&ecx.project_index(&left, i)?, &ecx.project_index(&dest, i)?)?;
600    }
601
602    interp_ok(())
603}
604
605// Rounds all elements of `op` according to `rounding`.
606fn round_all<'tcx, F: rustc_apfloat::Float>(
607    ecx: &mut crate::MiriInterpCx<'tcx>,
608    op: &OpTy<'tcx>,
609    rounding: &OpTy<'tcx>,
610    dest: &MPlaceTy<'tcx>,
611) -> InterpResult<'tcx, ()> {
612    let (op, op_len) = ecx.project_to_simd(op)?;
613    let (dest, dest_len) = ecx.project_to_simd(dest)?;
614
615    assert_eq!(dest_len, op_len);
616
617    let rounding = rounding_from_imm(ecx.read_scalar(rounding)?.to_i32()?)?;
618
619    for i in 0..dest_len {
620        let op: F = ecx.read_scalar(&ecx.project_index(&op, i)?)?.to_float()?;
621        let res = op.round_to_integral(rounding).value;
622        ecx.write_scalar(
623            Scalar::from_uint(res.to_bits(), Size::from_bits(F::BITS)),
624            &ecx.project_index(&dest, i)?,
625        )?;
626    }
627
628    interp_ok(())
629}
630
631/// Gets equivalent `rustc_apfloat::Round` from rounding mode immediate of
632/// `round.{ss,sd,ps,pd}` intrinsics.
633fn rounding_from_imm<'tcx>(rounding: i32) -> InterpResult<'tcx, rustc_apfloat::Round> {
634    // The fourth bit of `rounding` only affects the SSE status
635    // register, which cannot be accessed from Miri (or from Rust,
636    // for that matter), so we can ignore it.
637    match rounding & !0b1000 {
638        // When the third bit is 0, the rounding mode is determined by the
639        // first two bits.
640        0b000 => interp_ok(rustc_apfloat::Round::NearestTiesToEven),
641        0b001 => interp_ok(rustc_apfloat::Round::TowardNegative),
642        0b010 => interp_ok(rustc_apfloat::Round::TowardPositive),
643        0b011 => interp_ok(rustc_apfloat::Round::TowardZero),
644        // When the third bit is 1, the rounding mode is determined by the
645        // SSE status register. Since we do not support modifying it from
646        // Miri (or Rust), we assume it to be at its default mode (round-to-nearest).
647        0b100..=0b111 => interp_ok(rustc_apfloat::Round::NearestTiesToEven),
648        rounding => panic!("invalid rounding mode 0x{rounding:02x}"),
649    }
650}
651
652/// Converts each element of `op` from floating point to signed integer.
653///
654/// When the input value is NaN or out of range, fall back to minimum value.
655///
656/// If `op` has more elements than `dest`, extra elements are ignored. If `op`
657/// has less elements than `dest`, the rest is filled with zeros.
658fn convert_float_to_int<'tcx>(
659    ecx: &mut crate::MiriInterpCx<'tcx>,
660    op: &OpTy<'tcx>,
661    rnd: rustc_apfloat::Round,
662    dest: &MPlaceTy<'tcx>,
663) -> InterpResult<'tcx, ()> {
664    let (op, op_len) = ecx.project_to_simd(op)?;
665    let (dest, dest_len) = ecx.project_to_simd(dest)?;
666
667    // Output must be *signed* integers.
668    assert!(matches!(dest.layout.field(ecx, 0).ty.kind(), ty::Int(_)));
669
670    for i in 0..op_len.min(dest_len) {
671        let op = ecx.read_immediate(&ecx.project_index(&op, i)?)?;
672        let dest = ecx.project_index(&dest, i)?;
673
674        let res = ecx.float_to_int_checked(&op, dest.layout, rnd)?.unwrap_or_else(|| {
675            // Fallback to minimum according to SSE/AVX semantics.
676            ImmTy::from_int(dest.layout.size.signed_int_min(), dest.layout)
677        });
678        ecx.write_immediate(*res, &dest)?;
679    }
680    // Fill remainder with zeros
681    for i in op_len..dest_len {
682        let dest = ecx.project_index(&dest, i)?;
683        ecx.write_scalar(Scalar::from_int(0, dest.layout.size), &dest)?;
684    }
685
686    interp_ok(())
687}
688
689/// Splits `op` (which must be a SIMD vector) into 128-bit chunks.
690///
691/// Returns a tuple where:
692/// * The first element is the number of 128-bit chunks (let's call it `N`).
693/// * The second element is the number of elements per chunk (let's call it `M`).
694/// * The third element is the `op` vector split into chunks, i.e, it's
695///   type is `[[T; M]; N]` where `T` is the element type of `op`.
696fn split_simd_to_128bit_chunks<'tcx, P: Projectable<'tcx, Provenance>>(
697    ecx: &mut crate::MiriInterpCx<'tcx>,
698    op: &P,
699) -> InterpResult<'tcx, (u64, u64, P)> {
700    let simd_layout = op.layout();
701    let (simd_len, element_ty) = simd_layout.ty.simd_size_and_type(ecx.tcx.tcx);
702
703    assert_eq!(simd_layout.size.bits() % 128, 0);
704    let num_chunks = simd_layout.size.bits() / 128;
705    let items_per_chunk = simd_len.strict_div(num_chunks);
706
707    // Transmute to `[[T; items_per_chunk]; num_chunks]`
708    let chunked_layout = ecx
709        .layout_of(Ty::new_array(
710            ecx.tcx.tcx,
711            Ty::new_array(ecx.tcx.tcx, element_ty, items_per_chunk),
712            num_chunks,
713        ))
714        .unwrap();
715    let chunked_op = op.transmute(chunked_layout, ecx)?;
716
717    interp_ok((num_chunks, items_per_chunk, chunked_op))
718}
719
720/// Conditionally multiplies the packed floating-point elements in
721/// `left` and `right` using the high 4 bits in `imm`, sums the calculated
722/// products (up to 4), and conditionally stores the sum in `dest` using
723/// the low 4 bits of `imm`.
724///
725/// Each 128-bit chunk is treated independently (i.e., the value for
726/// the is i-th 128-bit chunk of `dest` is calculated with the i-th
727/// 128-bit blocks of `left` and `right`).
728fn conditional_dot_product<'tcx>(
729    ecx: &mut crate::MiriInterpCx<'tcx>,
730    left: &OpTy<'tcx>,
731    right: &OpTy<'tcx>,
732    imm: &OpTy<'tcx>,
733    dest: &MPlaceTy<'tcx>,
734) -> InterpResult<'tcx, ()> {
735    assert_eq!(left.layout, dest.layout);
736    assert_eq!(right.layout, dest.layout);
737
738    let (num_chunks, items_per_chunk, left) = split_simd_to_128bit_chunks(ecx, left)?;
739    let (_, _, right) = split_simd_to_128bit_chunks(ecx, right)?;
740    let (_, _, dest) = split_simd_to_128bit_chunks(ecx, dest)?;
741
742    let element_layout = left.layout.field(ecx, 0).field(ecx, 0);
743    assert!(items_per_chunk <= 4);
744
745    // `imm` is a `u8` for SSE4.1 or an `i32` for AVX :/
746    let imm = ecx.read_scalar(imm)?.to_uint(imm.layout.size)?;
747
748    for i in 0..num_chunks {
749        let left = ecx.project_index(&left, i)?;
750        let right = ecx.project_index(&right, i)?;
751        let dest = ecx.project_index(&dest, i)?;
752
753        // Calculate dot product
754        // Elements are floating point numbers, but we can use `from_int`
755        // for the initial value because the representation of 0.0 is all zero bits.
756        let mut sum = ImmTy::from_int(0u8, element_layout);
757        for j in 0..items_per_chunk {
758            if imm & (1 << j.strict_add(4)) != 0 {
759                let left = ecx.read_immediate(&ecx.project_index(&left, j)?)?;
760                let right = ecx.read_immediate(&ecx.project_index(&right, j)?)?;
761
762                let mul = ecx.binary_op(mir::BinOp::Mul, &left, &right)?;
763                sum = ecx.binary_op(mir::BinOp::Add, &sum, &mul)?;
764            }
765        }
766
767        // Write to destination (conditioned to imm)
768        for j in 0..items_per_chunk {
769            let dest = ecx.project_index(&dest, j)?;
770
771            if imm & (1 << j) != 0 {
772                ecx.write_immediate(*sum, &dest)?;
773            } else {
774                ecx.write_scalar(Scalar::from_int(0u8, element_layout.size), &dest)?;
775            }
776        }
777    }
778
779    interp_ok(())
780}
781
782/// Calculates two booleans.
783///
784/// The first is true when all the bits of `op & mask` are zero.
785/// The second is true when `(op & mask) == mask`
786fn test_bits_masked<'tcx>(
787    ecx: &crate::MiriInterpCx<'tcx>,
788    op: &OpTy<'tcx>,
789    mask: &OpTy<'tcx>,
790) -> InterpResult<'tcx, (bool, bool)> {
791    assert_eq!(op.layout, mask.layout);
792
793    let (op, op_len) = ecx.project_to_simd(op)?;
794    let (mask, mask_len) = ecx.project_to_simd(mask)?;
795
796    assert_eq!(op_len, mask_len);
797
798    let mut all_zero = true;
799    let mut masked_set = true;
800    for i in 0..op_len {
801        let op = ecx.project_index(&op, i)?;
802        let mask = ecx.project_index(&mask, i)?;
803
804        let op = ecx.read_scalar(&op)?.to_uint(op.layout.size)?;
805        let mask = ecx.read_scalar(&mask)?.to_uint(mask.layout.size)?;
806        all_zero &= (op & mask) == 0;
807        masked_set &= (op & mask) == mask;
808    }
809
810    interp_ok((all_zero, masked_set))
811}
812
813/// Calculates two booleans.
814///
815/// The first is true when the highest bit of each element of `op & mask` is zero.
816/// The second is true when the highest bit of each element of `!op & mask` is zero.
817fn test_high_bits_masked<'tcx>(
818    ecx: &crate::MiriInterpCx<'tcx>,
819    op: &OpTy<'tcx>,
820    mask: &OpTy<'tcx>,
821) -> InterpResult<'tcx, (bool, bool)> {
822    assert_eq!(op.layout, mask.layout);
823
824    let (op, op_len) = ecx.project_to_simd(op)?;
825    let (mask, mask_len) = ecx.project_to_simd(mask)?;
826
827    assert_eq!(op_len, mask_len);
828
829    let high_bit_offset = op.layout.field(ecx, 0).size.bits().strict_sub(1);
830
831    let mut direct = true;
832    let mut negated = true;
833    for i in 0..op_len {
834        let op = ecx.project_index(&op, i)?;
835        let mask = ecx.project_index(&mask, i)?;
836
837        let op = ecx.read_scalar(&op)?.to_uint(op.layout.size)?;
838        let mask = ecx.read_scalar(&mask)?.to_uint(mask.layout.size)?;
839        direct &= (op & mask) >> high_bit_offset == 0;
840        negated &= (!op & mask) >> high_bit_offset == 0;
841    }
842
843    interp_ok((direct, negated))
844}
845
846/// Average packed unsigned 8-bit or 16-bit integers in `left` and `right`,
847/// rounding up, and store the results in `dest`. Each element is computed
848/// as `(left + right + 1) >> 1` without intermediate overflow.
849///
850/// The elements are widened according to their own signedness, so the
851/// operands are expected to be vectors of unsigned integers.
852///
853/// <https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_avg_epu8>
854/// <https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_avg_epu16>
855/// <https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_avg_epu8>
856/// <https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_avg_epu16>
857/// <https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm512_avg_epu8>
858/// <https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm512_avg_epu16>
859fn pavg<'tcx>(
860    ecx: &mut crate::MiriInterpCx<'tcx>,
861    left: &OpTy<'tcx>,
862    right: &OpTy<'tcx>,
863    dest: &MPlaceTy<'tcx>,
864) -> InterpResult<'tcx, ()> {
865    let (left, left_len) = ecx.project_to_simd(left)?;
866    let (right, right_len) = ecx.project_to_simd(right)?;
867    let (dest, dest_len) = ecx.project_to_simd(dest)?;
868
869    assert_eq!(dest_len, left_len);
870    assert_eq!(dest_len, right_len);
871
872    for i in 0..dest_len {
873        let left = ecx.read_immediate(&ecx.project_index(&left, i)?)?;
874        let right = ecx.read_immediate(&ecx.project_index(&right, i)?)?;
875        let dest = ecx.project_index(&dest, i)?;
876
877        // Widen the operands to avoid overflow
878        let twice_wide = ecx.layout_of(ecx.get_twice_wide_int_ty(left.layout.ty))?;
879        let left = ecx.int_to_int_or_float(&left, twice_wide)?;
880        let right = ecx.int_to_int_or_float(&right, twice_wide)?;
881
882        // Calculate left + right + 1
883        let added = ecx.binary_op(mir::BinOp::Add, &left, &right)?;
884        let added = ecx.binary_op(mir::BinOp::Add, &added, &ImmTy::from_uint(1u32, twice_wide))?;
885
886        // Calculate (left + right + 1) / 2
887        let divided =
888            ecx.binary_op(mir::BinOp::Div, &added, &ImmTy::from_uint(2u32, twice_wide))?;
889
890        // Narrow back to the original type
891        let res = ecx.int_to_int_or_float(&divided, dest.layout)?;
892        ecx.write_immediate(*res, &dest)?;
893    }
894
895    interp_ok(())
896}
897
898/// Compute the sum of absolute differences of quadruplets of unsigned
899/// 8-bit integers in `left` and `right`, and store the 16-bit results
900/// in `right`. Quadruplets are selected from `left` and `right` with
901/// offsets specified in `imm`.
902///
903/// <https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_maddubs_epi16>
904/// <https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_mpsadbw_epu8>
905///
906/// Each 128-bit chunk is treated independently (i.e., the value for
907/// the is i-th 128-bit chunk of `dest` is calculated with the i-th
908/// 128-bit chunks of `left` and `right`).
909fn mpsadbw<'tcx>(
910    ecx: &mut crate::MiriInterpCx<'tcx>,
911    left: &OpTy<'tcx>,
912    right: &OpTy<'tcx>,
913    imm: &OpTy<'tcx>,
914    dest: &MPlaceTy<'tcx>,
915) -> InterpResult<'tcx, ()> {
916    assert_eq!(left.layout, right.layout);
917    assert_eq!(left.layout.size, dest.layout.size);
918
919    let (num_chunks, op_items_per_chunk, left) = split_simd_to_128bit_chunks(ecx, left)?;
920    assert!(num_chunks <= 2);
921
922    let (_, _, right) = split_simd_to_128bit_chunks(ecx, right)?;
923    let (_, dest_items_per_chunk, dest) = split_simd_to_128bit_chunks(ecx, dest)?;
924
925    assert_eq!(op_items_per_chunk, dest_items_per_chunk.strict_mul(2));
926
927    let imm = ecx.read_scalar(imm)?.to_uint(imm.layout.size)?;
928
929    for i in 0..num_chunks {
930        let left = ecx.project_index(&left, i)?;
931        let right = ecx.project_index(&right, i)?;
932        let dest = ecx.project_index(&dest, i)?;
933
934        // The first 128-bit chunk uses the low 3 bits of IMM, the second chunk uses bits 3..6.
935        let lane_imm = imm.strict_shr(i.strict_mul(3).try_into().unwrap());
936
937        // Bit 2 of `lane_imm` specifies the offset for indices of `left`.
938        // The offset is 0 when the bit is 0 or 4 when the bit is 1.
939        let left_base = u64::try_from((lane_imm >> 2) & 1).unwrap().strict_mul(4);
940        // Bits 0..=1 of `lane_imm` specify the offset for indices of
941        // `right` in blocks of 4 elements.
942        let right_base = u64::try_from(lane_imm & 0b11).unwrap().strict_mul(4);
943
944        for j in 0..dest_items_per_chunk {
945            let left_offset = left_base.strict_add(j);
946            let mut res: u16 = 0;
947            for k in 0..4 {
948                let left = ecx
949                    .read_scalar(&ecx.project_index(&left, left_offset.strict_add(k))?)?
950                    .to_u8()?;
951                let right = ecx
952                    .read_scalar(&ecx.project_index(&right, right_base.strict_add(k))?)?
953                    .to_u8()?;
954                res = res.strict_add(left.abs_diff(right).into());
955            }
956            ecx.write_scalar(Scalar::from_u16(res), &ecx.project_index(&dest, j)?)?;
957        }
958    }
959
960    interp_ok(())
961}
962
963/// Compute the absolute differences of packed unsigned 8-bit integers
964/// in `left` and `right`, then horizontally sum each consecutive 8
965/// differences to produce unsigned 16-bit integers, and pack
966/// these unsigned 16-bit integers in the low 16 bits of 64-bit elements
967/// in `dest`.
968///
969/// <https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sad_epu8>
970/// <https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_sad_epu8>
971/// <https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm512_sad_epu8>
972fn psadbw<'tcx>(
973    ecx: &mut crate::MiriInterpCx<'tcx>,
974    left: &OpTy<'tcx>,
975    right: &OpTy<'tcx>,
976    dest: &MPlaceTy<'tcx>,
977) -> InterpResult<'tcx, ()> {
978    let (left, left_len) = ecx.project_to_simd(left)?;
979    let (right, right_len) = ecx.project_to_simd(right)?;
980    let (dest, dest_len) = ecx.project_to_simd(dest)?;
981
982    // fn psadbw(a: u8x16, b: u8x16) -> u64x2;
983    // fn psadbw(a: u8x32, b: u8x32) -> u64x4;
984    // fn vpsadbw(a: u8x64, b: u8x64) -> u64x8;
985    assert_eq!(left_len, right_len);
986    assert_eq!(left_len, left.layout.layout.size().bytes());
987    assert_eq!(dest_len, left_len.strict_div(8));
988
989    for i in 0..dest_len {
990        let dest = ecx.project_index(&dest, i)?;
991
992        let mut acc: u16 = 0;
993        for j in 0..8 {
994            let src_index = i.strict_mul(8).strict_add(j);
995
996            let left = ecx.project_index(&left, src_index)?;
997            let left = ecx.read_scalar(&left)?.to_u8()?;
998
999            let right = ecx.project_index(&right, src_index)?;
1000            let right = ecx.read_scalar(&right)?.to_u8()?;
1001
1002            acc = acc.strict_add(left.abs_diff(right).into());
1003        }
1004
1005        ecx.write_scalar(Scalar::from_u64(acc.into()), &dest)?;
1006    }
1007
1008    interp_ok(())
1009}
1010
1011/// Multiply packed signed 16-bit integers in `left` and `right`, producing intermediate signed 32-bit integers.
1012/// Horizontally add adjacent pairs of intermediate 32-bit integers, and pack the results in `dest`.
1013///
1014/// <https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_madd_epi16>
1015/// <https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_madd_epi16>
1016/// <https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm512_madd_epi16>
1017fn pmaddwd<'tcx>(
1018    ecx: &mut crate::MiriInterpCx<'tcx>,
1019    left: &OpTy<'tcx>,
1020    right: &OpTy<'tcx>,
1021    dest: &MPlaceTy<'tcx>,
1022) -> InterpResult<'tcx, ()> {
1023    let (left, left_len) = ecx.project_to_simd(left)?;
1024    let (right, right_len) = ecx.project_to_simd(right)?;
1025    let (dest, dest_len) = ecx.project_to_simd(dest)?;
1026
1027    // fn  pmaddwd(a: i16x8,  b: i16x8)  -> i32x4;
1028    // fn  pmaddwd(a: i16x16, b: i16x16) -> i32x8;
1029    // fn vpmaddwd(a: i16x32, b: i16x32) -> i32x16;
1030    assert_eq!(left_len, right_len);
1031    assert_eq!(dest_len.strict_mul(2), left_len);
1032
1033    for i in 0..dest_len {
1034        let j1 = i.strict_mul(2);
1035        let left1 = ecx.read_scalar(&ecx.project_index(&left, j1)?)?.to_i16()?;
1036        let right1 = ecx.read_scalar(&ecx.project_index(&right, j1)?)?.to_i16()?;
1037
1038        let j2 = j1.strict_add(1);
1039        let left2 = ecx.read_scalar(&ecx.project_index(&left, j2)?)?.to_i16()?;
1040        let right2 = ecx.read_scalar(&ecx.project_index(&right, j2)?)?.to_i16()?;
1041
1042        let dest = ecx.project_index(&dest, i)?;
1043
1044        // Multiplications are i16*i16->i32, which will not overflow.
1045        let mul1 = i32::from(left1).strict_mul(right1.into());
1046        let mul2 = i32::from(left2).strict_mul(right2.into());
1047        // However, this addition can overflow in the most extreme case
1048        // (-0x8000)*(-0x8000)+(-0x8000)*(-0x8000) = 0x80000000
1049        let res = mul1.wrapping_add(mul2);
1050
1051        ecx.write_scalar(Scalar::from_i32(res), &dest)?;
1052    }
1053
1054    interp_ok(())
1055}
1056
1057/// Multiplies packed 8-bit unsigned integers from `left` and packed
1058/// signed 8-bit integers from `right` into 16-bit signed integers. Then,
1059/// the saturating sum of the products with indices `2*i` and `2*i+1`
1060/// produces the output at index `i`.
1061///
1062/// <https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_maddubs_epi16>
1063/// <https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_maddubs_epi16>
1064/// <https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm512_maddubs_epi16>
1065fn pmaddbw<'tcx>(
1066    ecx: &mut crate::MiriInterpCx<'tcx>,
1067    left: &OpTy<'tcx>,
1068    right: &OpTy<'tcx>,
1069    dest: &MPlaceTy<'tcx>,
1070) -> InterpResult<'tcx, ()> {
1071    let (left, left_len) = ecx.project_to_simd(left)?;
1072    let (right, right_len) = ecx.project_to_simd(right)?;
1073    let (dest, dest_len) = ecx.project_to_simd(dest)?;
1074
1075    // fn pmaddubsw128(a: u8x16, b: i8x16) -> i16x8;
1076    // fn pmaddubsw(   a: u8x32, b: i8x32) -> i16x16;
1077    // fn vpmaddubsw(  a: u8x64, b: i8x64) -> i16x32;
1078    assert_eq!(left_len, right_len);
1079    assert_eq!(dest_len.strict_mul(2), left_len);
1080
1081    for i in 0..dest_len {
1082        let j1 = i.strict_mul(2);
1083        let left1 = ecx.read_scalar(&ecx.project_index(&left, j1)?)?.to_u8()?;
1084        let right1 = ecx.read_scalar(&ecx.project_index(&right, j1)?)?.to_i8()?;
1085
1086        let j2 = j1.strict_add(1);
1087        let left2 = ecx.read_scalar(&ecx.project_index(&left, j2)?)?.to_u8()?;
1088        let right2 = ecx.read_scalar(&ecx.project_index(&right, j2)?)?.to_i8()?;
1089
1090        let dest = ecx.project_index(&dest, i)?;
1091
1092        // Multiplication of a u8 and an i8 into an i16 cannot overflow.
1093        let mul1 = i16::from(left1).strict_mul(right1.into());
1094        let mul2 = i16::from(left2).strict_mul(right2.into());
1095        let res = mul1.saturating_add(mul2);
1096
1097        ecx.write_scalar(Scalar::from_i16(res), &dest)?;
1098    }
1099
1100    interp_ok(())
1101}
1102
1103/// Shuffle elements in `values` across lanes using the corresponding index in
1104/// `indices`, and store the results in `dest`.
1105///
1106/// This helper is shared by both the 32-bit-lane and 64-bit-lane AVX
1107/// permute-by-index intrinsics. The element type is taken from `values` and
1108/// `dest`, while the index lanes are interpreted at their full width (`i32` or
1109/// `i64`, depending on the intrinsic).
1110///
1111/// For a vector with `N` lanes, only the low `log2(N)` bits of each index are
1112/// used. Equivalently, lane `i` of the result is copied from
1113/// `values[indices[i] & (N - 1)]`.
1114///
1115/// <https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_permutevar8x32_epi32>
1116/// <https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_permutevar8x32_ps>
1117/// <https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm512_permutexvar_epi32>
1118/// <https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm512_permutexvar_epi64>
1119fn permute<'tcx>(
1120    ecx: &mut crate::MiriInterpCx<'tcx>,
1121    values: &OpTy<'tcx>,
1122    indices: &OpTy<'tcx>,
1123    dest: &MPlaceTy<'tcx>,
1124) -> InterpResult<'tcx, ()> {
1125    let (values, values_len) = ecx.project_to_simd(values)?;
1126    let (indices, indices_len) = ecx.project_to_simd(indices)?;
1127    let (dest, dest_len) = ecx.project_to_simd(dest)?;
1128
1129    // fn permd(a: u32x8, b: u32x8) -> u32x8;
1130    // fn permps(a: __m256, b: i32x8) -> __m256;
1131    // fn vpermd(a: i32x16, idx: i32x16) -> i32x16;
1132    // fn vpermq(a: i64x8, b: i64x8) -> i64x8;
1133    assert_eq!(dest_len, values_len);
1134    assert_eq!(dest_len, indices_len);
1135
1136    // Only use the lower 3 bits to index into a vector with 8 lanes,
1137    // or the lower 4 bits when indexing into a 16-lane vector.
1138    assert!(dest_len.is_power_of_two());
1139    let mask = u128::from(dest_len).strict_sub(1);
1140
1141    for i in 0..dest_len {
1142        let dest = ecx.project_index(&dest, i)?;
1143        let index_place = ecx.project_index(&indices, i)?;
1144        let index = ecx.read_scalar(&index_place)?.to_uint(index_place.layout.size)?;
1145        // `mask` is at most `dest_len - 1` which fits in a `u64`, so this cannot fail.
1146        let element = ecx.project_index(&values, u64::try_from(index & mask).unwrap())?;
1147
1148        ecx.copy_op(&element, &dest)?;
1149    }
1150
1151    interp_ok(())
1152}
1153
1154/// Shuffle elements from *two* source registers (`left` and `right`) using
1155/// the corresponding index in `indices`, and store the results in `dest`.
1156///
1157/// For indexing, we basically concatenate `left` and `right`, and index into the concatenation.
1158/// More precisely: For a vector with `N` lanes, the low `log2(N)` bits of each index select a
1159/// lane within a source vector. Bit `log2(N)` selects the source vector (`0` =>
1160/// `left`, `1` => `right`), and all higher bits are ignored.
1161/// Equivalently, lane `i` of the result is copied from
1162/// `src[indices[i] & (N - 1)]` where
1163/// `src = if indices[i] & N == 0 { left } else { right }`.
1164///
1165/// <https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm512_permutex2var_epi64>
1166/// <https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm512_permutex2var_epi8>
1167fn permute2<'tcx>(
1168    ecx: &mut crate::MiriInterpCx<'tcx>,
1169    left: &OpTy<'tcx>,
1170    indices: &OpTy<'tcx>,
1171    right: &OpTy<'tcx>,
1172    dest: &MPlaceTy<'tcx>,
1173) -> InterpResult<'tcx, ()> {
1174    let (left, left_len) = ecx.project_to_simd(left)?;
1175    let (indices, indices_len) = ecx.project_to_simd(indices)?;
1176    let (right, right_len) = ecx.project_to_simd(right)?;
1177    let (dest, dest_len) = ecx.project_to_simd(dest)?;
1178
1179    assert_eq!(dest_len, left_len);
1180    assert_eq!(dest_len, indices_len);
1181    assert_eq!(dest_len, right_len);
1182
1183    // Use the low bits to select a lane within either input vector, and the next bit to
1184    // choose between the two vectors.
1185    assert!(dest_len.is_power_of_two());
1186    let lane_mask = u128::from(dest_len).strict_sub(1);
1187    let vector_select_bit = u128::from(dest_len);
1188
1189    for i in 0..dest_len {
1190        let dest = ecx.project_index(&dest, i)?;
1191        let index_place = ecx.project_index(&indices, i)?;
1192        let index = ecx.read_scalar(&index_place)?.to_uint(index_place.layout.size)?;
1193        // `lane_mask` is at most `dest_len - 1` which fits in a `u64`, so this cannot fail.
1194        let lane = u64::try_from(index & lane_mask).unwrap();
1195        let src = if index & vector_select_bit == 0 { &left } else { &right };
1196        let element = ecx.project_index(src, lane)?;
1197
1198        ecx.copy_op(&element, &dest)?;
1199    }
1200
1201    interp_ok(())
1202}
1203
1204/// Multiplies packed 16-bit signed integer values, truncates the 32-bit
1205/// product to the 18 most significant bits by right-shifting, and then
1206/// divides the 18-bit value by 2 (rounding to nearest) by first adding
1207/// 1 and then taking the bits `1..=16`.
1208///
1209/// <https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_mulhrs_epi16>
1210/// <https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_mulhrs_epi16>
1211fn pmulhrsw<'tcx>(
1212    ecx: &mut crate::MiriInterpCx<'tcx>,
1213    left: &OpTy<'tcx>,
1214    right: &OpTy<'tcx>,
1215    dest: &MPlaceTy<'tcx>,
1216) -> InterpResult<'tcx, ()> {
1217    let (left, left_len) = ecx.project_to_simd(left)?;
1218    let (right, right_len) = ecx.project_to_simd(right)?;
1219    let (dest, dest_len) = ecx.project_to_simd(dest)?;
1220
1221    assert_eq!(dest_len, left_len);
1222    assert_eq!(dest_len, right_len);
1223
1224    for i in 0..dest_len {
1225        let left = ecx.read_scalar(&ecx.project_index(&left, i)?)?.to_i16()?;
1226        let right = ecx.read_scalar(&ecx.project_index(&right, i)?)?.to_i16()?;
1227        let dest = ecx.project_index(&dest, i)?;
1228
1229        let res = (i32::from(left).strict_mul(right.into()) >> 14).strict_add(1) >> 1;
1230
1231        // The result of this operation can overflow a signed 16-bit integer.
1232        // When `left` and `right` are -0x8000, the result is 0x8000.
1233        #[expect(clippy::as_conversions)]
1234        let res = res as i16;
1235
1236        ecx.write_scalar(Scalar::from_i16(res), &dest)?;
1237    }
1238
1239    interp_ok(())
1240}
1241
1242/// Perform a carry-less multiplication of two 64-bit integers, selected from `left` and `right` according to `imm8`,
1243/// and store the results in `dst`.
1244///
1245/// `left` and `right` are both vectors of type `len` x i64. Only bits 0 and 4 of `imm8` matter;
1246/// they select the element of `left` and `right`, respectively.
1247///
1248/// `len` is the SIMD vector length (in counts of `i64` values). It is expected to be one of
1249/// `2`, `4`, or `8`.
1250///
1251/// <https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_clmulepi64_si128>
1252fn pclmulqdq<'tcx>(
1253    ecx: &mut MiriInterpCx<'tcx>,
1254    left: &OpTy<'tcx>,
1255    right: &OpTy<'tcx>,
1256    imm8: &OpTy<'tcx>,
1257    dest: &MPlaceTy<'tcx>,
1258    len: u64,
1259) -> InterpResult<'tcx, ()> {
1260    assert_eq!(left.layout, right.layout);
1261    assert_eq!(left.layout.size, dest.layout.size);
1262    assert!([2u64, 4, 8].contains(&len));
1263
1264    // Transmute the input into arrays of `[u64; len]`.
1265    // Transmute the output into an array of `[u128, len / 2]`.
1266
1267    let src_layout = ecx.layout_of(Ty::new_array(ecx.tcx.tcx, ecx.tcx.types.u64, len))?;
1268    let dest_layout = ecx.layout_of(Ty::new_array(ecx.tcx.tcx, ecx.tcx.types.u128, len / 2))?;
1269
1270    let left = left.transmute(src_layout, ecx)?;
1271    let right = right.transmute(src_layout, ecx)?;
1272    let dest = dest.transmute(dest_layout, ecx)?;
1273
1274    let imm8 = ecx.read_scalar(imm8)?.to_u8()?;
1275
1276    for i in 0..(len / 2) {
1277        let lo = i.strict_mul(2);
1278        let hi = i.strict_mul(2).strict_add(1);
1279
1280        // select the 64-bit integer from left that the user specified (low or high)
1281        let index = if (imm8 & 0x01) == 0 { lo } else { hi };
1282        let left = ecx.read_scalar(&ecx.project_index(&left, index)?)?.to_u64()?;
1283
1284        // select the 64-bit integer from right that the user specified (low or high)
1285        let index = if (imm8 & 0x10) == 0 { lo } else { hi };
1286        let right = ecx.read_scalar(&ecx.project_index(&right, index)?)?.to_u64()?;
1287
1288        let result = left.widening_carryless_mul(right);
1289
1290        let dest = ecx.project_index(&dest, i)?;
1291        ecx.write_scalar(Scalar::from_u128(result), &dest)?;
1292    }
1293
1294    interp_ok(())
1295}
1296
1297/// Shuffles bytes from `left` using `right` as pattern. Each 16-byte block is shuffled independently.
1298///
1299/// `left` and `right` are both vectors of type `len` x i8.
1300///
1301/// If the highest bit of a byte in `right` is not set, the corresponding byte in `dest` is taken
1302/// from the current 16-byte block of `left` at the position indicated by the lowest 4 bits of this
1303/// byte in `right`. If the highest bit of a byte in `right` is set, the corresponding byte in
1304/// `dest` is set to `0`.
1305///
1306/// <https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_shuffle_epi8>
1307/// <https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_shuffle_epi8>
1308/// <https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm512_shuffle_epi8>
1309fn pshufb<'tcx>(
1310    ecx: &mut crate::MiriInterpCx<'tcx>,
1311    left: &OpTy<'tcx>,
1312    right: &OpTy<'tcx>,
1313    dest: &MPlaceTy<'tcx>,
1314) -> InterpResult<'tcx, ()> {
1315    let (left, left_len) = ecx.project_to_simd(left)?;
1316    let (right, right_len) = ecx.project_to_simd(right)?;
1317    let (dest, dest_len) = ecx.project_to_simd(dest)?;
1318
1319    assert_eq!(dest_len, left_len);
1320    assert_eq!(dest_len, right_len);
1321
1322    for i in 0..dest_len {
1323        let right = ecx.read_scalar(&ecx.project_index(&right, i)?)?.to_u8()?;
1324        let dest = ecx.project_index(&dest, i)?;
1325
1326        let res = if right & 0x80 == 0 {
1327            // Shuffle each 128-bit (16-byte) block independently.
1328            let block_offset = i & !15; // round down to previous multiple of 16
1329            let j = block_offset.strict_add((right % 16).into());
1330            ecx.read_scalar(&ecx.project_index(&left, j)?)?
1331        } else {
1332            // If the highest bit in `right` is 1, write zero.
1333            Scalar::from_u8(0)
1334        };
1335
1336        ecx.write_scalar(res, &dest)?;
1337    }
1338
1339    interp_ok(())
1340}
1341
1342/// Packs two N-bit integer vectors to a single N/2-bit integers.
1343///
1344/// The conversion from N-bit to N/2-bit should be provided by `f`.
1345///
1346/// Each 128-bit chunk is treated independently (i.e., the value for
1347/// the is i-th 128-bit chunk of `dest` is calculated with the i-th
1348/// 128-bit chunks of `left` and `right`).
1349fn pack_generic<'tcx>(
1350    ecx: &mut crate::MiriInterpCx<'tcx>,
1351    left: &OpTy<'tcx>,
1352    right: &OpTy<'tcx>,
1353    dest: &MPlaceTy<'tcx>,
1354    f: impl Fn(Scalar) -> InterpResult<'tcx, Scalar>,
1355) -> InterpResult<'tcx, ()> {
1356    assert_eq!(left.layout, right.layout);
1357    assert_eq!(left.layout.size, dest.layout.size);
1358
1359    let (num_chunks, op_items_per_chunk, left) = split_simd_to_128bit_chunks(ecx, left)?;
1360    let (_, _, right) = split_simd_to_128bit_chunks(ecx, right)?;
1361    let (_, dest_items_per_chunk, dest) = split_simd_to_128bit_chunks(ecx, dest)?;
1362
1363    assert_eq!(dest_items_per_chunk, op_items_per_chunk.strict_mul(2));
1364
1365    for i in 0..num_chunks {
1366        let left = ecx.project_index(&left, i)?;
1367        let right = ecx.project_index(&right, i)?;
1368        let dest = ecx.project_index(&dest, i)?;
1369
1370        for j in 0..op_items_per_chunk {
1371            let left = ecx.read_scalar(&ecx.project_index(&left, j)?)?;
1372            let right = ecx.read_scalar(&ecx.project_index(&right, j)?)?;
1373            let left_dest = ecx.project_index(&dest, j)?;
1374            let right_dest = ecx.project_index(&dest, j.strict_add(op_items_per_chunk))?;
1375
1376            let left_res = f(left)?;
1377            let right_res = f(right)?;
1378
1379            ecx.write_scalar(left_res, &left_dest)?;
1380            ecx.write_scalar(right_res, &right_dest)?;
1381        }
1382    }
1383
1384    interp_ok(())
1385}
1386
1387/// Converts two 16-bit integer vectors to a single 8-bit integer
1388/// vector with signed saturation.
1389///
1390/// Each 128-bit chunk is treated independently (i.e., the value for
1391/// the is i-th 128-bit chunk of `dest` is calculated with the i-th
1392/// 128-bit chunks of `left` and `right`).
1393fn packsswb<'tcx>(
1394    ecx: &mut crate::MiriInterpCx<'tcx>,
1395    left: &OpTy<'tcx>,
1396    right: &OpTy<'tcx>,
1397    dest: &MPlaceTy<'tcx>,
1398) -> InterpResult<'tcx, ()> {
1399    pack_generic(ecx, left, right, dest, |op| {
1400        let op = op.to_i16()?;
1401        let res = i8::try_from(op).unwrap_or(if op < 0 { i8::MIN } else { i8::MAX });
1402        interp_ok(Scalar::from_i8(res))
1403    })
1404}
1405
1406/// Converts two 16-bit signed integer vectors to a single 8-bit
1407/// unsigned integer vector with saturation.
1408///
1409/// Each 128-bit chunk is treated independently (i.e., the value for
1410/// the is i-th 128-bit chunk of `dest` is calculated with the i-th
1411/// 128-bit chunks of `left` and `right`).
1412fn packuswb<'tcx>(
1413    ecx: &mut crate::MiriInterpCx<'tcx>,
1414    left: &OpTy<'tcx>,
1415    right: &OpTy<'tcx>,
1416    dest: &MPlaceTy<'tcx>,
1417) -> InterpResult<'tcx, ()> {
1418    pack_generic(ecx, left, right, dest, |op| {
1419        let op = op.to_i16()?;
1420        let res = u8::try_from(op).unwrap_or(if op < 0 { 0 } else { u8::MAX });
1421        interp_ok(Scalar::from_u8(res))
1422    })
1423}
1424
1425/// Converts two 32-bit integer vectors to a single 16-bit integer
1426/// vector with signed saturation.
1427///
1428/// Each 128-bit chunk is treated independently (i.e., the value for
1429/// the is i-th 128-bit chunk of `dest` is calculated with the i-th
1430/// 128-bit chunks of `left` and `right`).
1431fn packssdw<'tcx>(
1432    ecx: &mut crate::MiriInterpCx<'tcx>,
1433    left: &OpTy<'tcx>,
1434    right: &OpTy<'tcx>,
1435    dest: &MPlaceTy<'tcx>,
1436) -> InterpResult<'tcx, ()> {
1437    pack_generic(ecx, left, right, dest, |op| {
1438        let op = op.to_i32()?;
1439        let res = i16::try_from(op).unwrap_or(if op < 0 { i16::MIN } else { i16::MAX });
1440        interp_ok(Scalar::from_i16(res))
1441    })
1442}
1443
1444/// Converts two 32-bit integer vectors to a single 16-bit integer
1445/// vector with unsigned saturation.
1446///
1447/// Each 128-bit chunk is treated independently (i.e., the value for
1448/// the is i-th 128-bit chunk of `dest` is calculated with the i-th
1449/// 128-bit chunks of `left` and `right`).
1450fn packusdw<'tcx>(
1451    ecx: &mut crate::MiriInterpCx<'tcx>,
1452    left: &OpTy<'tcx>,
1453    right: &OpTy<'tcx>,
1454    dest: &MPlaceTy<'tcx>,
1455) -> InterpResult<'tcx, ()> {
1456    pack_generic(ecx, left, right, dest, |op| {
1457        let op = op.to_i32()?;
1458        let res = u16::try_from(op).unwrap_or(if op < 0 { 0 } else { u16::MAX });
1459        interp_ok(Scalar::from_u16(res))
1460    })
1461}
1462
1463/// Negates elements from `left` when the corresponding element in
1464/// `right` is negative. If an element from `right` is zero, zero
1465/// is written to the corresponding output element.
1466/// In other words, multiplies `left` with `right.signum()`.
1467fn psign<'tcx>(
1468    ecx: &mut crate::MiriInterpCx<'tcx>,
1469    left: &OpTy<'tcx>,
1470    right: &OpTy<'tcx>,
1471    dest: &MPlaceTy<'tcx>,
1472) -> InterpResult<'tcx, ()> {
1473    let (left, left_len) = ecx.project_to_simd(left)?;
1474    let (right, right_len) = ecx.project_to_simd(right)?;
1475    let (dest, dest_len) = ecx.project_to_simd(dest)?;
1476
1477    assert_eq!(dest_len, left_len);
1478    assert_eq!(dest_len, right_len);
1479
1480    for i in 0..dest_len {
1481        let dest = ecx.project_index(&dest, i)?;
1482        let left = ecx.read_immediate(&ecx.project_index(&left, i)?)?;
1483        let right = ecx.read_scalar(&ecx.project_index(&right, i)?)?.to_int(dest.layout.size)?;
1484
1485        let res =
1486            ecx.binary_op(mir::BinOp::Mul, &left, &ImmTy::from_int(right.signum(), dest.layout))?;
1487
1488        ecx.write_immediate(*res, &dest)?;
1489    }
1490
1491    interp_ok(())
1492}
1493
1494/// Calcultates either `a + b + cb_in` or `a - b - cb_in` depending on the value
1495/// of `op` and returns both the sum and the overflow bit. `op` is expected to be
1496/// either one of `mir::BinOp::AddWithOverflow` and `mir::BinOp::SubWithOverflow`.
1497fn carrying_add<'tcx>(
1498    ecx: &mut crate::MiriInterpCx<'tcx>,
1499    cb_in: &OpTy<'tcx>,
1500    a: &OpTy<'tcx>,
1501    b: &OpTy<'tcx>,
1502    op: mir::BinOp,
1503) -> InterpResult<'tcx, (ImmTy<'tcx>, Scalar)> {
1504    assert!(op == mir::BinOp::AddWithOverflow || op == mir::BinOp::SubWithOverflow);
1505
1506    let cb_in = ecx.read_scalar(cb_in)?.to_u8()? != 0;
1507    let a = ecx.read_immediate(a)?;
1508    let b = ecx.read_immediate(b)?;
1509
1510    let (sum, overflow1) = ecx.binary_op(op, &a, &b)?.to_pair(ecx);
1511    let (sum, overflow2) =
1512        ecx.binary_op(op, &sum, &ImmTy::from_uint(cb_in, a.layout))?.to_pair(ecx);
1513    let cb_out = overflow1.to_scalar().to_bool()? | overflow2.to_scalar().to_bool()?;
1514
1515    interp_ok((sum, Scalar::from_u8(cb_out.into())))
1516}