1use rustc_abi::{CanonAbi, 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::callconv::FnAbi;
8use rustc_target::spec::Arch;
9
10use self::helpers::bool_to_simd_element;
11use crate::*;
12
13mod aesni;
14mod avx;
15mod avx2;
16mod avx512;
17mod bmi;
18mod gfni;
19mod sha;
20mod sse;
21mod sse2;
22mod sse3;
23mod sse41;
24mod sse42;
25mod ssse3;
26
27impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
28pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
29 fn emulate_x86_intrinsic(
30 &mut self,
31 link_name: Symbol,
32 abi: &FnAbi<'tcx, Ty<'tcx>>,
33 args: &[OpTy<'tcx>],
34 dest: &MPlaceTy<'tcx>,
35 ) -> InterpResult<'tcx, EmulateItemResult> {
36 let this = self.eval_context_mut();
37 let unprefixed_name = link_name.as_str().strip_prefix("llvm.x86.").unwrap();
39 match unprefixed_name {
40 "addcarry.32" | "addcarry.64" | "subborrow.32" | "subborrow.64" => {
46 if unprefixed_name.ends_with("64") && this.tcx.sess.target.arch != Arch::X86_64 {
47 return interp_ok(EmulateItemResult::NotSupported);
48 }
49
50 let [cb_in, a, b] =
51 this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
52 let op = if unprefixed_name.starts_with("add") {
53 mir::BinOp::AddWithOverflow
54 } else {
55 mir::BinOp::SubWithOverflow
56 };
57
58 let (sum, cb_out) = carrying_add(this, cb_in, a, b, op)?;
59 this.write_scalar(cb_out, &this.project_field(dest, FieldIdx::ZERO)?)?;
60 this.write_immediate(*sum, &this.project_field(dest, FieldIdx::ONE)?)?;
61 }
62
63 "sse2.pause" => {
69 let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
70 if this.tcx.sess.unstable_target_features.contains(&Symbol::intern("sse2")) {
72 this.yield_active_thread();
73 }
74 }
75
76 "pclmulqdq" | "pclmulqdq.256" | "pclmulqdq.512" => {
77 let mut len = 2; this.expect_target_feature_for_intrinsic(link_name, "pclmulqdq")?;
79 if unprefixed_name.ends_with(".256") {
80 this.expect_target_feature_for_intrinsic(link_name, "vpclmulqdq")?;
81 len = 4;
82 } else if unprefixed_name.ends_with(".512") {
83 this.expect_target_feature_for_intrinsic(link_name, "vpclmulqdq")?;
84 this.expect_target_feature_for_intrinsic(link_name, "avx512f")?;
85 len = 8;
86 }
87
88 let [left, right, imm] =
89 this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
90
91 pclmulqdq(this, left, right, imm, dest, len)?;
92 }
93
94 name if name.starts_with("bmi.") => {
95 return bmi::EvalContextExt::emulate_x86_bmi_intrinsic(
96 this, link_name, abi, args, dest,
97 );
98 }
99 name if name.starts_with("vgf2p8affine") || name.starts_with("vgf2p8mulb") => {
102 return gfni::EvalContextExt::emulate_x86_gfni_intrinsic(
103 this, link_name, abi, args, dest,
104 );
105 }
106 name if name.starts_with("sha") => {
107 return sha::EvalContextExt::emulate_x86_sha_intrinsic(
108 this, link_name, abi, args, dest,
109 );
110 }
111 name if name.starts_with("sse.") => {
112 return sse::EvalContextExt::emulate_x86_sse_intrinsic(
113 this, link_name, abi, args, dest,
114 );
115 }
116 name if name.starts_with("sse2.") => {
117 return sse2::EvalContextExt::emulate_x86_sse2_intrinsic(
118 this, link_name, abi, args, dest,
119 );
120 }
121 name if name.starts_with("sse3.") => {
122 return sse3::EvalContextExt::emulate_x86_sse3_intrinsic(
123 this, link_name, abi, args, dest,
124 );
125 }
126 name if name.starts_with("ssse3.") => {
127 return ssse3::EvalContextExt::emulate_x86_ssse3_intrinsic(
128 this, link_name, abi, args, dest,
129 );
130 }
131 name if name.starts_with("sse41.") => {
132 return sse41::EvalContextExt::emulate_x86_sse41_intrinsic(
133 this, link_name, abi, args, dest,
134 );
135 }
136 name if name.starts_with("sse42.") => {
137 return sse42::EvalContextExt::emulate_x86_sse42_intrinsic(
138 this, link_name, abi, args, dest,
139 );
140 }
141 name if name.starts_with("aesni.") => {
142 return aesni::EvalContextExt::emulate_x86_aesni_intrinsic(
143 this, link_name, abi, args, dest,
144 );
145 }
146 name if name.starts_with("avx.") => {
147 return avx::EvalContextExt::emulate_x86_avx_intrinsic(
148 this, link_name, abi, args, dest,
149 );
150 }
151 name if name.starts_with("avx2.") => {
152 return avx2::EvalContextExt::emulate_x86_avx2_intrinsic(
153 this, link_name, abi, args, dest,
154 );
155 }
156 name if name.starts_with("avx512.") => {
157 return avx512::EvalContextExt::emulate_x86_avx512_intrinsic(
158 this, link_name, abi, args, dest,
159 );
160 }
161
162 _ => return interp_ok(EmulateItemResult::NotSupported),
163 }
164 interp_ok(EmulateItemResult::NeedsReturn)
165 }
166}
167
168#[derive(Copy, Clone)]
169enum FloatBinOp {
170 Cmp {
183 gt: bool,
185 lt: bool,
187 eq: bool,
189 unord: bool,
191 },
192 Min,
199 Max,
206}
207
208impl FloatBinOp {
209 fn cmp_from_imm<'tcx>(
212 ecx: &crate::MiriInterpCx<'tcx>,
213 imm: i8,
214 intrinsic: Symbol,
215 ) -> InterpResult<'tcx, Self> {
216 if imm & !0b1_1111 != 0 {
218 panic!("invalid `imm` parameter of {intrinsic}: 0x{imm:x}");
219 }
220 let (gt, lt, eq, mut unord) = match imm & 0b111 {
226 0x0 => (false, false, true, false),
228 0x1 => (false, true, false, false),
230 0x2 => (false, true, true, false),
232 0x3 => (false, false, false, true),
234 0x4 => (true, true, false, true),
236 0x5 => (true, false, true, true),
238 0x6 => (true, false, false, true),
240 0x7 => (true, true, true, false),
242 _ => unreachable!(),
243 };
244 if imm & 0b1000 != 0 {
246 ecx.expect_target_feature_for_intrinsic(intrinsic, "avx")?;
247 unord = !unord;
248 }
249 interp_ok(Self::Cmp { gt, lt, eq, unord })
250 }
251}
252
253fn bin_op_float<'tcx, F: rustc_apfloat::Float>(
256 which: FloatBinOp,
257 left: &ImmTy<'tcx>,
258 right: &ImmTy<'tcx>,
259) -> InterpResult<'tcx, Scalar> {
260 match which {
261 FloatBinOp::Cmp { gt, lt, eq, unord } => {
262 let left = left.to_scalar().to_float::<F>()?;
263 let right = right.to_scalar().to_float::<F>()?;
264
265 let res = match left.partial_cmp(&right) {
266 None => unord,
267 Some(std::cmp::Ordering::Less) => lt,
268 Some(std::cmp::Ordering::Equal) => eq,
269 Some(std::cmp::Ordering::Greater) => gt,
270 };
271 interp_ok(bool_to_simd_element(res, Size::from_bits(F::BITS)))
272 }
273 FloatBinOp::Min => {
274 let left_scalar = left.to_scalar();
275 let left = left_scalar.to_float::<F>()?;
276 let right_scalar = right.to_scalar();
277 let right = right_scalar.to_float::<F>()?;
278 if (left == F::ZERO && right == F::ZERO)
281 || left.is_nan()
282 || right.is_nan()
283 || left >= right
284 {
285 interp_ok(right_scalar)
286 } else {
287 interp_ok(left_scalar)
288 }
289 }
290 FloatBinOp::Max => {
291 let left_scalar = left.to_scalar();
292 let left = left_scalar.to_float::<F>()?;
293 let right_scalar = right.to_scalar();
294 let right = right_scalar.to_float::<F>()?;
295 if (left == F::ZERO && right == F::ZERO)
298 || left.is_nan()
299 || right.is_nan()
300 || left <= right
301 {
302 interp_ok(right_scalar)
303 } else {
304 interp_ok(left_scalar)
305 }
306 }
307 }
308}
309
310fn bin_op_simd_float_first<'tcx, F: rustc_apfloat::Float>(
313 ecx: &mut crate::MiriInterpCx<'tcx>,
314 which: FloatBinOp,
315 left: &OpTy<'tcx>,
316 right: &OpTy<'tcx>,
317 dest: &MPlaceTy<'tcx>,
318) -> InterpResult<'tcx, ()> {
319 let (left, left_len) = ecx.project_to_simd(left)?;
320 let (right, right_len) = ecx.project_to_simd(right)?;
321 let (dest, dest_len) = ecx.project_to_simd(dest)?;
322
323 assert_eq!(dest_len, left_len);
324 assert_eq!(dest_len, right_len);
325
326 let res0 = bin_op_float::<F>(
327 which,
328 &ecx.read_immediate(&ecx.project_index(&left, 0)?)?,
329 &ecx.read_immediate(&ecx.project_index(&right, 0)?)?,
330 )?;
331 ecx.write_scalar(res0, &ecx.project_index(&dest, 0)?)?;
332
333 for i in 1..dest_len {
334 ecx.copy_op(&ecx.project_index(&left, i)?, &ecx.project_index(&dest, i)?)?;
335 }
336
337 interp_ok(())
338}
339
340fn bin_op_simd_float_all<'tcx, F: rustc_apfloat::Float>(
343 ecx: &mut crate::MiriInterpCx<'tcx>,
344 which: FloatBinOp,
345 left: &OpTy<'tcx>,
346 right: &OpTy<'tcx>,
347 dest: &MPlaceTy<'tcx>,
348) -> InterpResult<'tcx, ()> {
349 let (left, left_len) = ecx.project_to_simd(left)?;
350 let (right, right_len) = ecx.project_to_simd(right)?;
351 let (dest, dest_len) = ecx.project_to_simd(dest)?;
352
353 assert_eq!(dest_len, left_len);
354 assert_eq!(dest_len, right_len);
355
356 for i in 0..dest_len {
357 let left = ecx.read_immediate(&ecx.project_index(&left, i)?)?;
358 let right = ecx.read_immediate(&ecx.project_index(&right, i)?)?;
359 let dest = ecx.project_index(&dest, i)?;
360
361 let res = bin_op_float::<F>(which, &left, &right)?;
362 ecx.write_scalar(res, &dest)?;
363 }
364
365 interp_ok(())
366}
367
368#[derive(Copy, Clone)]
369enum FloatUnaryOp {
370 Rcp,
375 Rsqrt,
380}
381
382fn unary_op_f32<'tcx>(
384 ecx: &mut crate::MiriInterpCx<'tcx>,
385 which: FloatUnaryOp,
386 op: &ImmTy<'tcx>,
387) -> InterpResult<'tcx, Scalar> {
388 match which {
389 FloatUnaryOp::Rcp => {
390 let op = op.to_scalar().to_f32()?;
391 let div = (Single::from_u128(1).value / op).value;
392 let res = math::apply_random_float_error(ecx, div, -12);
395 interp_ok(Scalar::from_f32(res))
396 }
397 FloatUnaryOp::Rsqrt => {
398 let op = op.to_scalar().to_f32()?;
399 let rsqrt = (Single::from_u128(1).value / math::sqrt(op)).value;
400 let res = math::apply_random_float_error(ecx, rsqrt, -12);
403 interp_ok(Scalar::from_f32(res))
404 }
405 }
406}
407
408fn unary_op_ss<'tcx>(
411 ecx: &mut crate::MiriInterpCx<'tcx>,
412 which: FloatUnaryOp,
413 op: &OpTy<'tcx>,
414 dest: &MPlaceTy<'tcx>,
415) -> InterpResult<'tcx, ()> {
416 let (op, op_len) = ecx.project_to_simd(op)?;
417 let (dest, dest_len) = ecx.project_to_simd(dest)?;
418
419 assert_eq!(dest_len, op_len);
420
421 let res0 = unary_op_f32(ecx, which, &ecx.read_immediate(&ecx.project_index(&op, 0)?)?)?;
422 ecx.write_scalar(res0, &ecx.project_index(&dest, 0)?)?;
423
424 for i in 1..dest_len {
425 ecx.copy_op(&ecx.project_index(&op, i)?, &ecx.project_index(&dest, i)?)?;
426 }
427
428 interp_ok(())
429}
430
431fn unary_op_ps<'tcx>(
434 ecx: &mut crate::MiriInterpCx<'tcx>,
435 which: FloatUnaryOp,
436 op: &OpTy<'tcx>,
437 dest: &MPlaceTy<'tcx>,
438) -> InterpResult<'tcx, ()> {
439 let (op, op_len) = ecx.project_to_simd(op)?;
440 let (dest, dest_len) = ecx.project_to_simd(dest)?;
441
442 assert_eq!(dest_len, op_len);
443
444 for i in 0..dest_len {
445 let op = ecx.read_immediate(&ecx.project_index(&op, i)?)?;
446 let dest = ecx.project_index(&dest, i)?;
447
448 let res = unary_op_f32(ecx, which, &op)?;
449 ecx.write_scalar(res, &dest)?;
450 }
451
452 interp_ok(())
453}
454
455enum ShiftOp {
456 Left,
458 RightLogic,
460 RightArith,
462}
463
464fn shift_simd_by_scalar<'tcx>(
471 ecx: &mut crate::MiriInterpCx<'tcx>,
472 left: &OpTy<'tcx>,
473 right: &OpTy<'tcx>,
474 which: ShiftOp,
475 dest: &MPlaceTy<'tcx>,
476) -> InterpResult<'tcx, ()> {
477 let (left, left_len) = ecx.project_to_simd(left)?;
478 let (dest, dest_len) = ecx.project_to_simd(dest)?;
479
480 assert_eq!(dest_len, left_len);
481 let shift = u32::try_from(extract_first_u64(ecx, right)?).unwrap_or(u32::MAX);
489
490 for i in 0..dest_len {
491 let left = ecx.read_scalar(&ecx.project_index(&left, i)?)?;
492 let dest = ecx.project_index(&dest, i)?;
493
494 let res = match which {
495 ShiftOp::Left => {
496 let left = left.to_uint(dest.layout.size)?;
497 let res = left.checked_shl(shift).unwrap_or(0);
498 Scalar::from_uint(dest.layout.size.truncate(res), dest.layout.size)
500 }
501 ShiftOp::RightLogic => {
502 let left = left.to_uint(dest.layout.size)?;
503 let res = left.checked_shr(shift).unwrap_or(0);
504 Scalar::from_uint(res, dest.layout.size)
506 }
507 ShiftOp::RightArith => {
508 let left = left.to_int(dest.layout.size)?;
509 let res = left.checked_shr(shift).unwrap_or(left >> 127);
511 Scalar::from_int(res, dest.layout.size)
513 }
514 };
515 ecx.write_scalar(res, &dest)?;
516 }
517
518 interp_ok(())
519}
520
521fn extract_first_u64<'tcx>(
524 ecx: &crate::MiriInterpCx<'tcx>,
525 op: &OpTy<'tcx>,
526) -> InterpResult<'tcx, u64> {
527 let array_layout = ecx.layout_of(Ty::new_array(ecx.tcx.tcx, ecx.tcx.types.u64, 2))?;
529 let op = op.transmute(array_layout, ecx)?;
530
531 ecx.read_scalar(&ecx.project_index(&op, 0)?)?.to_u64()
533}
534
535fn round_first<'tcx, F: rustc_apfloat::Float>(
538 ecx: &mut crate::MiriInterpCx<'tcx>,
539 left: &OpTy<'tcx>,
540 right: &OpTy<'tcx>,
541 rounding: &OpTy<'tcx>,
542 dest: &MPlaceTy<'tcx>,
543) -> InterpResult<'tcx, ()> {
544 let (left, left_len) = ecx.project_to_simd(left)?;
545 let (right, right_len) = ecx.project_to_simd(right)?;
546 let (dest, dest_len) = ecx.project_to_simd(dest)?;
547
548 assert_eq!(dest_len, left_len);
549 assert_eq!(dest_len, right_len);
550
551 let rounding = rounding_from_imm(ecx.read_scalar(rounding)?.to_i32()?)?;
552
553 let op0: F = ecx.read_scalar(&ecx.project_index(&right, 0)?)?.to_float()?;
554 let res = op0.round_to_integral(rounding).value;
555 ecx.write_scalar(
556 Scalar::from_uint(res.to_bits(), Size::from_bits(F::BITS)),
557 &ecx.project_index(&dest, 0)?,
558 )?;
559
560 for i in 1..dest_len {
561 ecx.copy_op(&ecx.project_index(&left, i)?, &ecx.project_index(&dest, i)?)?;
562 }
563
564 interp_ok(())
565}
566
567fn round_all<'tcx, F: rustc_apfloat::Float>(
569 ecx: &mut crate::MiriInterpCx<'tcx>,
570 op: &OpTy<'tcx>,
571 rounding: &OpTy<'tcx>,
572 dest: &MPlaceTy<'tcx>,
573) -> InterpResult<'tcx, ()> {
574 let (op, op_len) = ecx.project_to_simd(op)?;
575 let (dest, dest_len) = ecx.project_to_simd(dest)?;
576
577 assert_eq!(dest_len, op_len);
578
579 let rounding = rounding_from_imm(ecx.read_scalar(rounding)?.to_i32()?)?;
580
581 for i in 0..dest_len {
582 let op: F = ecx.read_scalar(&ecx.project_index(&op, i)?)?.to_float()?;
583 let res = op.round_to_integral(rounding).value;
584 ecx.write_scalar(
585 Scalar::from_uint(res.to_bits(), Size::from_bits(F::BITS)),
586 &ecx.project_index(&dest, i)?,
587 )?;
588 }
589
590 interp_ok(())
591}
592
593fn rounding_from_imm<'tcx>(rounding: i32) -> InterpResult<'tcx, rustc_apfloat::Round> {
596 match rounding & !0b1000 {
600 0b000 => interp_ok(rustc_apfloat::Round::NearestTiesToEven),
603 0b001 => interp_ok(rustc_apfloat::Round::TowardNegative),
604 0b010 => interp_ok(rustc_apfloat::Round::TowardPositive),
605 0b011 => interp_ok(rustc_apfloat::Round::TowardZero),
606 0b100..=0b111 => interp_ok(rustc_apfloat::Round::NearestTiesToEven),
610 rounding => panic!("invalid rounding mode 0x{rounding:02x}"),
611 }
612}
613
614fn convert_float_to_int<'tcx>(
621 ecx: &mut crate::MiriInterpCx<'tcx>,
622 op: &OpTy<'tcx>,
623 rnd: rustc_apfloat::Round,
624 dest: &MPlaceTy<'tcx>,
625) -> InterpResult<'tcx, ()> {
626 let (op, op_len) = ecx.project_to_simd(op)?;
627 let (dest, dest_len) = ecx.project_to_simd(dest)?;
628
629 assert!(matches!(dest.layout.field(ecx, 0).ty.kind(), ty::Int(_)));
631
632 for i in 0..op_len.min(dest_len) {
633 let op = ecx.read_immediate(&ecx.project_index(&op, i)?)?;
634 let dest = ecx.project_index(&dest, i)?;
635
636 let res = ecx.float_to_int_checked(&op, dest.layout, rnd)?.unwrap_or_else(|| {
637 ImmTy::from_int(dest.layout.size.signed_int_min(), dest.layout)
639 });
640 ecx.write_immediate(*res, &dest)?;
641 }
642 for i in op_len..dest_len {
644 let dest = ecx.project_index(&dest, i)?;
645 ecx.write_scalar(Scalar::from_int(0, dest.layout.size), &dest)?;
646 }
647
648 interp_ok(())
649}
650
651fn split_simd_to_128bit_chunks<'tcx, P: Projectable<'tcx, Provenance>>(
659 ecx: &mut crate::MiriInterpCx<'tcx>,
660 op: &P,
661) -> InterpResult<'tcx, (u64, u64, P)> {
662 let simd_layout = op.layout();
663 let (simd_len, element_ty) = simd_layout.ty.simd_size_and_type(ecx.tcx.tcx);
664
665 assert_eq!(simd_layout.size.bits() % 128, 0);
666 let num_chunks = simd_layout.size.bits() / 128;
667 let items_per_chunk = simd_len.strict_div(num_chunks);
668
669 let chunked_layout = ecx
671 .layout_of(Ty::new_array(
672 ecx.tcx.tcx,
673 Ty::new_array(ecx.tcx.tcx, element_ty, items_per_chunk),
674 num_chunks,
675 ))
676 .unwrap();
677 let chunked_op = op.transmute(chunked_layout, ecx)?;
678
679 interp_ok((num_chunks, items_per_chunk, chunked_op))
680}
681
682fn horizontal_bin_op<'tcx>(
692 ecx: &mut crate::MiriInterpCx<'tcx>,
693 which: mir::BinOp,
694 saturating: bool,
695 left: &OpTy<'tcx>,
696 right: &OpTy<'tcx>,
697 dest: &MPlaceTy<'tcx>,
698) -> InterpResult<'tcx, ()> {
699 assert_eq!(left.layout, dest.layout);
700 assert_eq!(right.layout, dest.layout);
701
702 let (num_chunks, items_per_chunk, left) = split_simd_to_128bit_chunks(ecx, left)?;
703 let (_, _, right) = split_simd_to_128bit_chunks(ecx, right)?;
704 let (_, _, dest) = split_simd_to_128bit_chunks(ecx, dest)?;
705
706 let middle = items_per_chunk / 2;
707 for i in 0..num_chunks {
708 let left = ecx.project_index(&left, i)?;
709 let right = ecx.project_index(&right, i)?;
710 let dest = ecx.project_index(&dest, i)?;
711
712 for j in 0..items_per_chunk {
713 let (k, src) = if j < middle { (j, &left) } else { (j.strict_sub(middle), &right) };
716 let base_i = k.strict_mul(2);
718 let lhs = ecx.read_immediate(&ecx.project_index(src, base_i)?)?;
719 let rhs = ecx.read_immediate(&ecx.project_index(src, base_i.strict_add(1))?)?;
720
721 let res = if saturating {
722 Immediate::from(ecx.saturating_arith(which, &lhs, &rhs)?)
723 } else {
724 *ecx.binary_op(which, &lhs, &rhs)?
725 };
726
727 ecx.write_immediate(res, &ecx.project_index(&dest, j)?)?;
728 }
729 }
730
731 interp_ok(())
732}
733
734fn conditional_dot_product<'tcx>(
743 ecx: &mut crate::MiriInterpCx<'tcx>,
744 left: &OpTy<'tcx>,
745 right: &OpTy<'tcx>,
746 imm: &OpTy<'tcx>,
747 dest: &MPlaceTy<'tcx>,
748) -> InterpResult<'tcx, ()> {
749 assert_eq!(left.layout, dest.layout);
750 assert_eq!(right.layout, dest.layout);
751
752 let (num_chunks, items_per_chunk, left) = split_simd_to_128bit_chunks(ecx, left)?;
753 let (_, _, right) = split_simd_to_128bit_chunks(ecx, right)?;
754 let (_, _, dest) = split_simd_to_128bit_chunks(ecx, dest)?;
755
756 let element_layout = left.layout.field(ecx, 0).field(ecx, 0);
757 assert!(items_per_chunk <= 4);
758
759 let imm = ecx.read_scalar(imm)?.to_uint(imm.layout.size)?;
761
762 for i in 0..num_chunks {
763 let left = ecx.project_index(&left, i)?;
764 let right = ecx.project_index(&right, i)?;
765 let dest = ecx.project_index(&dest, i)?;
766
767 let mut sum = ImmTy::from_int(0u8, element_layout);
771 for j in 0..items_per_chunk {
772 if imm & (1 << j.strict_add(4)) != 0 {
773 let left = ecx.read_immediate(&ecx.project_index(&left, j)?)?;
774 let right = ecx.read_immediate(&ecx.project_index(&right, j)?)?;
775
776 let mul = ecx.binary_op(mir::BinOp::Mul, &left, &right)?;
777 sum = ecx.binary_op(mir::BinOp::Add, &sum, &mul)?;
778 }
779 }
780
781 for j in 0..items_per_chunk {
783 let dest = ecx.project_index(&dest, j)?;
784
785 if imm & (1 << j) != 0 {
786 ecx.write_immediate(*sum, &dest)?;
787 } else {
788 ecx.write_scalar(Scalar::from_int(0u8, element_layout.size), &dest)?;
789 }
790 }
791 }
792
793 interp_ok(())
794}
795
796fn test_bits_masked<'tcx>(
801 ecx: &crate::MiriInterpCx<'tcx>,
802 op: &OpTy<'tcx>,
803 mask: &OpTy<'tcx>,
804) -> InterpResult<'tcx, (bool, bool)> {
805 assert_eq!(op.layout, mask.layout);
806
807 let (op, op_len) = ecx.project_to_simd(op)?;
808 let (mask, mask_len) = ecx.project_to_simd(mask)?;
809
810 assert_eq!(op_len, mask_len);
811
812 let mut all_zero = true;
813 let mut masked_set = true;
814 for i in 0..op_len {
815 let op = ecx.project_index(&op, i)?;
816 let mask = ecx.project_index(&mask, i)?;
817
818 let op = ecx.read_scalar(&op)?.to_uint(op.layout.size)?;
819 let mask = ecx.read_scalar(&mask)?.to_uint(mask.layout.size)?;
820 all_zero &= (op & mask) == 0;
821 masked_set &= (op & mask) == mask;
822 }
823
824 interp_ok((all_zero, masked_set))
825}
826
827fn test_high_bits_masked<'tcx>(
832 ecx: &crate::MiriInterpCx<'tcx>,
833 op: &OpTy<'tcx>,
834 mask: &OpTy<'tcx>,
835) -> InterpResult<'tcx, (bool, bool)> {
836 assert_eq!(op.layout, mask.layout);
837
838 let (op, op_len) = ecx.project_to_simd(op)?;
839 let (mask, mask_len) = ecx.project_to_simd(mask)?;
840
841 assert_eq!(op_len, mask_len);
842
843 let high_bit_offset = op.layout.field(ecx, 0).size.bits().strict_sub(1);
844
845 let mut direct = true;
846 let mut negated = true;
847 for i in 0..op_len {
848 let op = ecx.project_index(&op, i)?;
849 let mask = ecx.project_index(&mask, i)?;
850
851 let op = ecx.read_scalar(&op)?.to_uint(op.layout.size)?;
852 let mask = ecx.read_scalar(&mask)?.to_uint(mask.layout.size)?;
853 direct &= (op & mask) >> high_bit_offset == 0;
854 negated &= (!op & mask) >> high_bit_offset == 0;
855 }
856
857 interp_ok((direct, negated))
858}
859
860fn mpsadbw<'tcx>(
872 ecx: &mut crate::MiriInterpCx<'tcx>,
873 left: &OpTy<'tcx>,
874 right: &OpTy<'tcx>,
875 imm: &OpTy<'tcx>,
876 dest: &MPlaceTy<'tcx>,
877) -> InterpResult<'tcx, ()> {
878 assert_eq!(left.layout, right.layout);
879 assert_eq!(left.layout.size, dest.layout.size);
880
881 let (num_chunks, op_items_per_chunk, left) = split_simd_to_128bit_chunks(ecx, left)?;
882 assert!(num_chunks <= 2);
883
884 let (_, _, right) = split_simd_to_128bit_chunks(ecx, right)?;
885 let (_, dest_items_per_chunk, dest) = split_simd_to_128bit_chunks(ecx, dest)?;
886
887 assert_eq!(op_items_per_chunk, dest_items_per_chunk.strict_mul(2));
888
889 let imm = ecx.read_scalar(imm)?.to_uint(imm.layout.size)?;
890
891 for i in 0..num_chunks {
892 let left = ecx.project_index(&left, i)?;
893 let right = ecx.project_index(&right, i)?;
894 let dest = ecx.project_index(&dest, i)?;
895
896 let lane_imm = imm.strict_shr(i.strict_mul(3).try_into().unwrap());
898
899 let left_base = u64::try_from((lane_imm >> 2) & 1).unwrap().strict_mul(4);
902 let right_base = u64::try_from(lane_imm & 0b11).unwrap().strict_mul(4);
905
906 for j in 0..dest_items_per_chunk {
907 let left_offset = left_base.strict_add(j);
908 let mut res: u16 = 0;
909 for k in 0..4 {
910 let left = ecx
911 .read_scalar(&ecx.project_index(&left, left_offset.strict_add(k))?)?
912 .to_u8()?;
913 let right = ecx
914 .read_scalar(&ecx.project_index(&right, right_base.strict_add(k))?)?
915 .to_u8()?;
916 res = res.strict_add(left.abs_diff(right).into());
917 }
918 ecx.write_scalar(Scalar::from_u16(res), &ecx.project_index(&dest, j)?)?;
919 }
920 }
921
922 interp_ok(())
923}
924
925fn psadbw<'tcx>(
935 ecx: &mut crate::MiriInterpCx<'tcx>,
936 left: &OpTy<'tcx>,
937 right: &OpTy<'tcx>,
938 dest: &MPlaceTy<'tcx>,
939) -> InterpResult<'tcx, ()> {
940 let (left, left_len) = ecx.project_to_simd(left)?;
941 let (right, right_len) = ecx.project_to_simd(right)?;
942 let (dest, dest_len) = ecx.project_to_simd(dest)?;
943
944 assert_eq!(left_len, right_len);
948 assert_eq!(left_len, left.layout.layout.size().bytes());
949 assert_eq!(dest_len, left_len.strict_div(8));
950
951 for i in 0..dest_len {
952 let dest = ecx.project_index(&dest, i)?;
953
954 let mut acc: u16 = 0;
955 for j in 0..8 {
956 let src_index = i.strict_mul(8).strict_add(j);
957
958 let left = ecx.project_index(&left, src_index)?;
959 let left = ecx.read_scalar(&left)?.to_u8()?;
960
961 let right = ecx.project_index(&right, src_index)?;
962 let right = ecx.read_scalar(&right)?.to_u8()?;
963
964 acc = acc.strict_add(left.abs_diff(right).into());
965 }
966
967 ecx.write_scalar(Scalar::from_u64(acc.into()), &dest)?;
968 }
969
970 interp_ok(())
971}
972
973fn pmaddwd<'tcx>(
980 ecx: &mut crate::MiriInterpCx<'tcx>,
981 left: &OpTy<'tcx>,
982 right: &OpTy<'tcx>,
983 dest: &MPlaceTy<'tcx>,
984) -> InterpResult<'tcx, ()> {
985 let (left, left_len) = ecx.project_to_simd(left)?;
986 let (right, right_len) = ecx.project_to_simd(right)?;
987 let (dest, dest_len) = ecx.project_to_simd(dest)?;
988
989 assert_eq!(left_len, right_len);
993 assert_eq!(dest_len.strict_mul(2), left_len);
994
995 for i in 0..dest_len {
996 let j1 = i.strict_mul(2);
997 let left1 = ecx.read_scalar(&ecx.project_index(&left, j1)?)?.to_i16()?;
998 let right1 = ecx.read_scalar(&ecx.project_index(&right, j1)?)?.to_i16()?;
999
1000 let j2 = j1.strict_add(1);
1001 let left2 = ecx.read_scalar(&ecx.project_index(&left, j2)?)?.to_i16()?;
1002 let right2 = ecx.read_scalar(&ecx.project_index(&right, j2)?)?.to_i16()?;
1003
1004 let dest = ecx.project_index(&dest, i)?;
1005
1006 let mul1 = i32::from(left1).strict_mul(right1.into());
1008 let mul2 = i32::from(left2).strict_mul(right2.into());
1009 let res = mul1.wrapping_add(mul2);
1012
1013 ecx.write_scalar(Scalar::from_i32(res), &dest)?;
1014 }
1015
1016 interp_ok(())
1017}
1018
1019fn pmaddbw<'tcx>(
1028 ecx: &mut crate::MiriInterpCx<'tcx>,
1029 left: &OpTy<'tcx>,
1030 right: &OpTy<'tcx>,
1031 dest: &MPlaceTy<'tcx>,
1032) -> InterpResult<'tcx, ()> {
1033 let (left, left_len) = ecx.project_to_simd(left)?;
1034 let (right, right_len) = ecx.project_to_simd(right)?;
1035 let (dest, dest_len) = ecx.project_to_simd(dest)?;
1036
1037 assert_eq!(left_len, right_len);
1041 assert_eq!(dest_len.strict_mul(2), left_len);
1042
1043 for i in 0..dest_len {
1044 let j1 = i.strict_mul(2);
1045 let left1 = ecx.read_scalar(&ecx.project_index(&left, j1)?)?.to_u8()?;
1046 let right1 = ecx.read_scalar(&ecx.project_index(&right, j1)?)?.to_i8()?;
1047
1048 let j2 = j1.strict_add(1);
1049 let left2 = ecx.read_scalar(&ecx.project_index(&left, j2)?)?.to_u8()?;
1050 let right2 = ecx.read_scalar(&ecx.project_index(&right, j2)?)?.to_i8()?;
1051
1052 let dest = ecx.project_index(&dest, i)?;
1053
1054 let mul1 = i16::from(left1).strict_mul(right1.into());
1056 let mul2 = i16::from(left2).strict_mul(right2.into());
1057 let res = mul1.saturating_add(mul2);
1058
1059 ecx.write_scalar(Scalar::from_i16(res), &dest)?;
1060 }
1061
1062 interp_ok(())
1063}
1064
1065fn permute<'tcx>(
1082 ecx: &mut crate::MiriInterpCx<'tcx>,
1083 values: &OpTy<'tcx>,
1084 indices: &OpTy<'tcx>,
1085 dest: &MPlaceTy<'tcx>,
1086) -> InterpResult<'tcx, ()> {
1087 let (values, values_len) = ecx.project_to_simd(values)?;
1088 let (indices, indices_len) = ecx.project_to_simd(indices)?;
1089 let (dest, dest_len) = ecx.project_to_simd(dest)?;
1090
1091 assert_eq!(dest_len, values_len);
1096 assert_eq!(dest_len, indices_len);
1097
1098 assert!(dest_len.is_power_of_two());
1101 let mask = u128::from(dest_len).strict_sub(1);
1102
1103 for i in 0..dest_len {
1104 let dest = ecx.project_index(&dest, i)?;
1105 let index_place = ecx.project_index(&indices, i)?;
1106 let index = ecx.read_scalar(&index_place)?.to_uint(index_place.layout.size)?;
1107 let element = ecx.project_index(&values, u64::try_from(index & mask).unwrap())?;
1109
1110 ecx.copy_op(&element, &dest)?;
1111 }
1112
1113 interp_ok(())
1114}
1115
1116fn permute2<'tcx>(
1129 ecx: &mut crate::MiriInterpCx<'tcx>,
1130 left: &OpTy<'tcx>,
1131 indices: &OpTy<'tcx>,
1132 right: &OpTy<'tcx>,
1133 dest: &MPlaceTy<'tcx>,
1134) -> InterpResult<'tcx, ()> {
1135 let (left, left_len) = ecx.project_to_simd(left)?;
1136 let (indices, indices_len) = ecx.project_to_simd(indices)?;
1137 let (right, right_len) = ecx.project_to_simd(right)?;
1138 let (dest, dest_len) = ecx.project_to_simd(dest)?;
1139
1140 assert_eq!(dest_len, left_len);
1141 assert_eq!(dest_len, indices_len);
1142 assert_eq!(dest_len, right_len);
1143
1144 assert!(dest_len.is_power_of_two());
1147 let lane_mask = u128::from(dest_len).strict_sub(1);
1148 let vector_select_bit = u128::from(dest_len);
1149
1150 for i in 0..dest_len {
1151 let dest = ecx.project_index(&dest, i)?;
1152 let index_place = ecx.project_index(&indices, i)?;
1153 let index = ecx.read_scalar(&index_place)?.to_uint(index_place.layout.size)?;
1154 let lane = u64::try_from(index & lane_mask).unwrap();
1156 let src = if index & vector_select_bit == 0 { &left } else { &right };
1157 let element = ecx.project_index(src, lane)?;
1158
1159 ecx.copy_op(&element, &dest)?;
1160 }
1161
1162 interp_ok(())
1163}
1164
1165fn pmulhrsw<'tcx>(
1173 ecx: &mut crate::MiriInterpCx<'tcx>,
1174 left: &OpTy<'tcx>,
1175 right: &OpTy<'tcx>,
1176 dest: &MPlaceTy<'tcx>,
1177) -> InterpResult<'tcx, ()> {
1178 let (left, left_len) = ecx.project_to_simd(left)?;
1179 let (right, right_len) = ecx.project_to_simd(right)?;
1180 let (dest, dest_len) = ecx.project_to_simd(dest)?;
1181
1182 assert_eq!(dest_len, left_len);
1183 assert_eq!(dest_len, right_len);
1184
1185 for i in 0..dest_len {
1186 let left = ecx.read_scalar(&ecx.project_index(&left, i)?)?.to_i16()?;
1187 let right = ecx.read_scalar(&ecx.project_index(&right, i)?)?.to_i16()?;
1188 let dest = ecx.project_index(&dest, i)?;
1189
1190 let res = (i32::from(left).strict_mul(right.into()) >> 14).strict_add(1) >> 1;
1191
1192 #[expect(clippy::as_conversions)]
1195 let res = res as i16;
1196
1197 ecx.write_scalar(Scalar::from_i16(res), &dest)?;
1198 }
1199
1200 interp_ok(())
1201}
1202
1203fn pclmulqdq<'tcx>(
1214 ecx: &mut MiriInterpCx<'tcx>,
1215 left: &OpTy<'tcx>,
1216 right: &OpTy<'tcx>,
1217 imm8: &OpTy<'tcx>,
1218 dest: &MPlaceTy<'tcx>,
1219 len: u64,
1220) -> InterpResult<'tcx, ()> {
1221 assert_eq!(left.layout, right.layout);
1222 assert_eq!(left.layout.size, dest.layout.size);
1223 assert!([2u64, 4, 8].contains(&len));
1224
1225 let src_layout = ecx.layout_of(Ty::new_array(ecx.tcx.tcx, ecx.tcx.types.u64, len))?;
1229 let dest_layout = ecx.layout_of(Ty::new_array(ecx.tcx.tcx, ecx.tcx.types.u128, len / 2))?;
1230
1231 let left = left.transmute(src_layout, ecx)?;
1232 let right = right.transmute(src_layout, ecx)?;
1233 let dest = dest.transmute(dest_layout, ecx)?;
1234
1235 let imm8 = ecx.read_scalar(imm8)?.to_u8()?;
1236
1237 for i in 0..(len / 2) {
1238 let lo = i.strict_mul(2);
1239 let hi = i.strict_mul(2).strict_add(1);
1240
1241 let index = if (imm8 & 0x01) == 0 { lo } else { hi };
1243 let left = ecx.read_scalar(&ecx.project_index(&left, index)?)?.to_u64()?;
1244
1245 let index = if (imm8 & 0x10) == 0 { lo } else { hi };
1247 let right = ecx.read_scalar(&ecx.project_index(&right, index)?)?.to_u64()?;
1248
1249 let result = left.widening_carryless_mul(right);
1250
1251 let dest = ecx.project_index(&dest, i)?;
1252 ecx.write_scalar(Scalar::from_u128(result), &dest)?;
1253 }
1254
1255 interp_ok(())
1256}
1257
1258fn pshufb<'tcx>(
1271 ecx: &mut crate::MiriInterpCx<'tcx>,
1272 left: &OpTy<'tcx>,
1273 right: &OpTy<'tcx>,
1274 dest: &MPlaceTy<'tcx>,
1275) -> InterpResult<'tcx, ()> {
1276 let (left, left_len) = ecx.project_to_simd(left)?;
1277 let (right, right_len) = ecx.project_to_simd(right)?;
1278 let (dest, dest_len) = ecx.project_to_simd(dest)?;
1279
1280 assert_eq!(dest_len, left_len);
1281 assert_eq!(dest_len, right_len);
1282
1283 for i in 0..dest_len {
1284 let right = ecx.read_scalar(&ecx.project_index(&right, i)?)?.to_u8()?;
1285 let dest = ecx.project_index(&dest, i)?;
1286
1287 let res = if right & 0x80 == 0 {
1288 let block_offset = i & !15; let j = block_offset.strict_add((right % 16).into());
1291 ecx.read_scalar(&ecx.project_index(&left, j)?)?
1292 } else {
1293 Scalar::from_u8(0)
1295 };
1296
1297 ecx.write_scalar(res, &dest)?;
1298 }
1299
1300 interp_ok(())
1301}
1302
1303fn pack_generic<'tcx>(
1311 ecx: &mut crate::MiriInterpCx<'tcx>,
1312 left: &OpTy<'tcx>,
1313 right: &OpTy<'tcx>,
1314 dest: &MPlaceTy<'tcx>,
1315 f: impl Fn(Scalar) -> InterpResult<'tcx, Scalar>,
1316) -> InterpResult<'tcx, ()> {
1317 assert_eq!(left.layout, right.layout);
1318 assert_eq!(left.layout.size, dest.layout.size);
1319
1320 let (num_chunks, op_items_per_chunk, left) = split_simd_to_128bit_chunks(ecx, left)?;
1321 let (_, _, right) = split_simd_to_128bit_chunks(ecx, right)?;
1322 let (_, dest_items_per_chunk, dest) = split_simd_to_128bit_chunks(ecx, dest)?;
1323
1324 assert_eq!(dest_items_per_chunk, op_items_per_chunk.strict_mul(2));
1325
1326 for i in 0..num_chunks {
1327 let left = ecx.project_index(&left, i)?;
1328 let right = ecx.project_index(&right, i)?;
1329 let dest = ecx.project_index(&dest, i)?;
1330
1331 for j in 0..op_items_per_chunk {
1332 let left = ecx.read_scalar(&ecx.project_index(&left, j)?)?;
1333 let right = ecx.read_scalar(&ecx.project_index(&right, j)?)?;
1334 let left_dest = ecx.project_index(&dest, j)?;
1335 let right_dest = ecx.project_index(&dest, j.strict_add(op_items_per_chunk))?;
1336
1337 let left_res = f(left)?;
1338 let right_res = f(right)?;
1339
1340 ecx.write_scalar(left_res, &left_dest)?;
1341 ecx.write_scalar(right_res, &right_dest)?;
1342 }
1343 }
1344
1345 interp_ok(())
1346}
1347
1348fn packsswb<'tcx>(
1355 ecx: &mut crate::MiriInterpCx<'tcx>,
1356 left: &OpTy<'tcx>,
1357 right: &OpTy<'tcx>,
1358 dest: &MPlaceTy<'tcx>,
1359) -> InterpResult<'tcx, ()> {
1360 pack_generic(ecx, left, right, dest, |op| {
1361 let op = op.to_i16()?;
1362 let res = i8::try_from(op).unwrap_or(if op < 0 { i8::MIN } else { i8::MAX });
1363 interp_ok(Scalar::from_i8(res))
1364 })
1365}
1366
1367fn packuswb<'tcx>(
1374 ecx: &mut crate::MiriInterpCx<'tcx>,
1375 left: &OpTy<'tcx>,
1376 right: &OpTy<'tcx>,
1377 dest: &MPlaceTy<'tcx>,
1378) -> InterpResult<'tcx, ()> {
1379 pack_generic(ecx, left, right, dest, |op| {
1380 let op = op.to_i16()?;
1381 let res = u8::try_from(op).unwrap_or(if op < 0 { 0 } else { u8::MAX });
1382 interp_ok(Scalar::from_u8(res))
1383 })
1384}
1385
1386fn packssdw<'tcx>(
1393 ecx: &mut crate::MiriInterpCx<'tcx>,
1394 left: &OpTy<'tcx>,
1395 right: &OpTy<'tcx>,
1396 dest: &MPlaceTy<'tcx>,
1397) -> InterpResult<'tcx, ()> {
1398 pack_generic(ecx, left, right, dest, |op| {
1399 let op = op.to_i32()?;
1400 let res = i16::try_from(op).unwrap_or(if op < 0 { i16::MIN } else { i16::MAX });
1401 interp_ok(Scalar::from_i16(res))
1402 })
1403}
1404
1405fn packusdw<'tcx>(
1412 ecx: &mut crate::MiriInterpCx<'tcx>,
1413 left: &OpTy<'tcx>,
1414 right: &OpTy<'tcx>,
1415 dest: &MPlaceTy<'tcx>,
1416) -> InterpResult<'tcx, ()> {
1417 pack_generic(ecx, left, right, dest, |op| {
1418 let op = op.to_i32()?;
1419 let res = u16::try_from(op).unwrap_or(if op < 0 { 0 } else { u16::MAX });
1420 interp_ok(Scalar::from_u16(res))
1421 })
1422}
1423
1424fn psign<'tcx>(
1429 ecx: &mut crate::MiriInterpCx<'tcx>,
1430 left: &OpTy<'tcx>,
1431 right: &OpTy<'tcx>,
1432 dest: &MPlaceTy<'tcx>,
1433) -> InterpResult<'tcx, ()> {
1434 let (left, left_len) = ecx.project_to_simd(left)?;
1435 let (right, right_len) = ecx.project_to_simd(right)?;
1436 let (dest, dest_len) = ecx.project_to_simd(dest)?;
1437
1438 assert_eq!(dest_len, left_len);
1439 assert_eq!(dest_len, right_len);
1440
1441 for i in 0..dest_len {
1442 let dest = ecx.project_index(&dest, i)?;
1443 let left = ecx.read_immediate(&ecx.project_index(&left, i)?)?;
1444 let right = ecx.read_scalar(&ecx.project_index(&right, i)?)?.to_int(dest.layout.size)?;
1445
1446 let res =
1447 ecx.binary_op(mir::BinOp::Mul, &left, &ImmTy::from_int(right.signum(), dest.layout))?;
1448
1449 ecx.write_immediate(*res, &dest)?;
1450 }
1451
1452 interp_ok(())
1453}
1454
1455fn carrying_add<'tcx>(
1459 ecx: &mut crate::MiriInterpCx<'tcx>,
1460 cb_in: &OpTy<'tcx>,
1461 a: &OpTy<'tcx>,
1462 b: &OpTy<'tcx>,
1463 op: mir::BinOp,
1464) -> InterpResult<'tcx, (ImmTy<'tcx>, Scalar)> {
1465 assert!(op == mir::BinOp::AddWithOverflow || op == mir::BinOp::SubWithOverflow);
1466
1467 let cb_in = ecx.read_scalar(cb_in)?.to_u8()? != 0;
1468 let a = ecx.read_immediate(a)?;
1469 let b = ecx.read_immediate(b)?;
1470
1471 let (sum, overflow1) = ecx.binary_op(op, &a, &b)?.to_pair(ecx);
1472 let (sum, overflow2) =
1473 ecx.binary_op(op, &sum, &ImmTy::from_uint(cb_in, a.layout))?.to_pair(ecx);
1474 let cb_out = overflow1.to_scalar().to_bool()? | overflow2.to_scalar().to_bool()?;
1475
1476 interp_ok((sum, Scalar::from_u8(cb_out.into())))
1477}