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 ecx = self.eval_context_mut();
35 let unprefixed_name = link_name.as_str().strip_prefix("llvm.x86.").unwrap();
37 match unprefixed_name {
38 "addcarry.32" | "addcarry.64" | "subborrow.32" | "subborrow.64" => {
44 if unprefixed_name.ends_with("64") && ecx.tcx.sess.target.arch != Arch::X86_64 {
45 return interp_ok(EmulateItemResult::NotSupported);
46 }
47
48 let [cb_in, a, b] = ecx.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(ecx, cb_in, a, b, op)?;
56 ecx.write_scalar(cb_out, &ecx.project_field(dest, FieldIdx::ZERO)?)?;
57 ecx.write_immediate(*sum, &ecx.project_field(dest, FieldIdx::ONE)?)?;
58 }
59
60 "sse2.pause" => {
66 let [] = ecx.check_shim_sig_llvm_intrinsic(link_name, args)?;
67 if ecx.tcx.sess.internal_target_features.contains(&Symbol::intern("sse2")) {
69 ecx.yield_active_thread();
70 }
71 }
72
73 "pclmulqdq" | "pclmulqdq.256" | "pclmulqdq.512" => {
74 let mut len = 2; ecx.expect_target_feature_for_intrinsic(link_name, "pclmulqdq")?;
76 if unprefixed_name.ends_with(".256") {
77 ecx.expect_target_feature_for_intrinsic(link_name, "vpclmulqdq")?;
78 len = 4;
79 } else if unprefixed_name.ends_with(".512") {
80 ecx.expect_target_feature_for_intrinsic(link_name, "vpclmulqdq")?;
81 ecx.expect_target_feature_for_intrinsic(link_name, "avx512f")?;
82 len = 8;
83 }
84
85 let [left, right, imm] = ecx.check_shim_sig_llvm_intrinsic(link_name, args)?;
86
87 pclmulqdq(ecx, left, right, imm, dest, len)?;
88 }
89
90 name if name.starts_with("bmi.") => {
91 return bmi::EvalContextExt::emulate_x86_bmi_intrinsic(ecx, link_name, args, dest);
92 }
93 name if name.starts_with("vgf2p8affine") || name.starts_with("vgf2p8mulb") => {
96 return gfni::EvalContextExt::emulate_x86_gfni_intrinsic(
97 ecx, link_name, args, dest,
98 );
99 }
100 name if name.starts_with("sha") => {
101 return sha::EvalContextExt::emulate_x86_sha_intrinsic(ecx, link_name, args, dest);
102 }
103 name if name.starts_with("sse.") => {
104 return sse::EvalContextExt::emulate_x86_sse_intrinsic(ecx, link_name, args, dest);
105 }
106 name if name.starts_with("sse2.") => {
107 return sse2::EvalContextExt::emulate_x86_sse2_intrinsic(
108 ecx, link_name, args, dest,
109 );
110 }
111 name if name.starts_with("sse3.") => {
112 return sse3::EvalContextExt::emulate_x86_sse3_intrinsic(
113 ecx, link_name, args, dest,
114 );
115 }
116 name if name.starts_with("ssse3.") => {
117 return ssse3::EvalContextExt::emulate_x86_ssse3_intrinsic(
118 ecx, link_name, args, dest,
119 );
120 }
121 name if name.starts_with("sse41.") => {
122 return sse41::EvalContextExt::emulate_x86_sse41_intrinsic(
123 ecx, link_name, args, dest,
124 );
125 }
126 name if name.starts_with("sse42.") => {
127 return sse42::EvalContextExt::emulate_x86_sse42_intrinsic(
128 ecx, link_name, args, dest,
129 );
130 }
131 name if name.starts_with("aesni.") => {
132 return aesni::EvalContextExt::emulate_x86_aesni_intrinsic(
133 ecx, link_name, args, dest,
134 );
135 }
136 name if name.starts_with("avx.") => {
137 return avx::EvalContextExt::emulate_x86_avx_intrinsic(ecx, link_name, args, dest);
138 }
139 name if name.starts_with("avx2.") => {
140 return avx2::EvalContextExt::emulate_x86_avx2_intrinsic(
141 ecx, link_name, args, dest,
142 );
143 }
144 name if name.starts_with("avx512.") => {
145 return avx512::EvalContextExt::emulate_x86_avx512_intrinsic(
146 ecx, 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 Cmp {
171 gt: bool,
173 lt: bool,
175 eq: bool,
177 unord: bool,
179 },
180 Min,
187 Max,
194}
195
196impl FloatBinOp {
197 fn cmp_from_imm<'tcx>(
200 ecx: &crate::MiriInterpCx<'tcx>,
201 imm: i8,
202 intrinsic: Symbol,
203 ) -> InterpResult<'tcx, Self> {
204 if imm & !0b1_1111 != 0 {
206 panic!("invalid `imm` parameter of {intrinsic}: 0x{imm:x}");
207 }
208 let (gt, lt, eq, mut unord) = match imm & 0b111 {
214 0x0 => (false, false, true, false),
216 0x1 => (false, true, false, false),
218 0x2 => (false, true, true, false),
220 0x3 => (false, false, false, true),
222 0x4 => (true, true, false, true),
224 0x5 => (true, false, true, true),
226 0x6 => (true, false, false, true),
228 0x7 => (true, true, true, false),
230 _ => unreachable!(),
231 };
232 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
241fn 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 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 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
298fn 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
328fn 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 Rcp,
363 Rsqrt,
368}
369
370fn 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 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 let res = math::apply_random_float_error(ecx, rsqrt, -12);
391 interp_ok(Scalar::from_f32(res))
392 }
393 }
394}
395
396fn 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
419fn 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 Left,
446 RightLogic,
448 RightArith,
450}
451
452fn 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 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 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 Scalar::from_uint(res, dest.layout.size)
494 }
495 ShiftOp::RightArith => {
496 let left = left.to_int(dest.layout.size)?;
497 let res = left.checked_shr(shift).unwrap_or(left >> 127);
499 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 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 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 Scalar::from_uint(res, dest.layout.size)
544 }
545 ShiftOp::RightArith => {
546 let left = left.to_int(dest.layout.size)?;
547 let res = left.checked_shr(shift).unwrap_or(left >> 127);
549 Scalar::from_int(res, dest.layout.size)
551 }
552 };
553 ecx.write_scalar(res, &dest)?;
554 }
555
556 interp_ok(())
557}
558
559fn extract_first_u64<'tcx>(
562 ecx: &crate::MiriInterpCx<'tcx>,
563 op: &OpTy<'tcx>,
564) -> InterpResult<'tcx, u64> {
565 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 ecx.read_scalar(&ecx.project_index(&op, 0)?)?.to_u64()
571}
572
573fn 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
605fn 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
631fn rounding_from_imm<'tcx>(rounding: i32) -> InterpResult<'tcx, rustc_apfloat::Round> {
634 match rounding & !0b1000 {
638 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 0b100..=0b111 => interp_ok(rustc_apfloat::Round::NearestTiesToEven),
648 rounding => panic!("invalid rounding mode 0x{rounding:02x}"),
649 }
650}
651
652fn 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 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 ImmTy::from_int(dest.layout.size.signed_int_min(), dest.layout)
677 });
678 ecx.write_immediate(*res, &dest)?;
679 }
680 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
689fn 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 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
720fn 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 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 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 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
782fn 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
813fn 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
846fn 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 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 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 let divided =
888 ecx.binary_op(mir::BinOp::Div, &added, &ImmTy::from_uint(2u32, twice_wide))?;
889
890 let res = ecx.int_to_int_or_float(÷d, dest.layout)?;
892 ecx.write_immediate(*res, &dest)?;
893 }
894
895 interp_ok(())
896}
897
898fn pmulh<'tcx>(
906 ecx: &mut crate::MiriInterpCx<'tcx>,
907 left: &OpTy<'tcx>,
908 right: &OpTy<'tcx>,
909 dest: &MPlaceTy<'tcx>,
910) -> InterpResult<'tcx, ()> {
911 let (left, left_len) = ecx.project_to_simd(left)?;
912 let (right, right_len) = ecx.project_to_simd(right)?;
913 let (dest, dest_len) = ecx.project_to_simd(dest)?;
914
915 assert_eq!(dest_len, left_len);
916 assert_eq!(dest_len, right_len);
917
918 for i in 0..dest_len {
919 let left = ecx.read_immediate(&ecx.project_index(&left, i)?)?;
920 let right = ecx.read_immediate(&ecx.project_index(&right, i)?)?;
921 let dest = ecx.project_index(&dest, i)?;
922
923 let twice_wide = ecx.layout_of(ecx.get_twice_wide_int_ty(left.layout.ty))?;
925 let left = ecx.int_to_int_or_float(&left, twice_wide)?;
926 let right = ecx.int_to_int_or_float(&right, twice_wide)?;
927
928 let multiplied = ecx.binary_op(mir::BinOp::Mul, &left, &right)?;
930 let high = ecx.binary_op(
932 mir::BinOp::Shr,
933 &multiplied,
934 &ImmTy::from_uint(dest.layout.size.bits(), twice_wide),
935 )?;
936
937 let res = ecx.int_to_int_or_float(&high, dest.layout)?;
939 ecx.write_immediate(*res, &dest)?;
940 }
941
942 interp_ok(())
943}
944
945fn mpsadbw<'tcx>(
957 ecx: &mut crate::MiriInterpCx<'tcx>,
958 left: &OpTy<'tcx>,
959 right: &OpTy<'tcx>,
960 imm: &OpTy<'tcx>,
961 dest: &MPlaceTy<'tcx>,
962) -> InterpResult<'tcx, ()> {
963 assert_eq!(left.layout, right.layout);
964 assert_eq!(left.layout.size, dest.layout.size);
965
966 let (num_chunks, op_items_per_chunk, left) = split_simd_to_128bit_chunks(ecx, left)?;
967 assert!(num_chunks <= 2);
968
969 let (_, _, right) = split_simd_to_128bit_chunks(ecx, right)?;
970 let (_, dest_items_per_chunk, dest) = split_simd_to_128bit_chunks(ecx, dest)?;
971
972 assert_eq!(op_items_per_chunk, dest_items_per_chunk.strict_mul(2));
973
974 let imm = ecx.read_scalar(imm)?.to_uint(imm.layout.size)?;
975
976 for i in 0..num_chunks {
977 let left = ecx.project_index(&left, i)?;
978 let right = ecx.project_index(&right, i)?;
979 let dest = ecx.project_index(&dest, i)?;
980
981 let lane_imm = imm.strict_shr(i.strict_mul(3).try_into().unwrap());
983
984 let left_base = u64::try_from((lane_imm >> 2) & 1).unwrap().strict_mul(4);
987 let right_base = u64::try_from(lane_imm & 0b11).unwrap().strict_mul(4);
990
991 for j in 0..dest_items_per_chunk {
992 let left_offset = left_base.strict_add(j);
993 let mut res: u16 = 0;
994 for k in 0..4 {
995 let left = ecx
996 .read_scalar(&ecx.project_index(&left, left_offset.strict_add(k))?)?
997 .to_u8()?;
998 let right = ecx
999 .read_scalar(&ecx.project_index(&right, right_base.strict_add(k))?)?
1000 .to_u8()?;
1001 res = res.strict_add(left.abs_diff(right).into());
1002 }
1003 ecx.write_scalar(Scalar::from_u16(res), &ecx.project_index(&dest, j)?)?;
1004 }
1005 }
1006
1007 interp_ok(())
1008}
1009
1010fn psadbw<'tcx>(
1020 ecx: &mut crate::MiriInterpCx<'tcx>,
1021 left: &OpTy<'tcx>,
1022 right: &OpTy<'tcx>,
1023 dest: &MPlaceTy<'tcx>,
1024) -> InterpResult<'tcx, ()> {
1025 let (left, left_len) = ecx.project_to_simd(left)?;
1026 let (right, right_len) = ecx.project_to_simd(right)?;
1027 let (dest, dest_len) = ecx.project_to_simd(dest)?;
1028
1029 assert_eq!(left_len, right_len);
1033 assert_eq!(left_len, left.layout.layout.size().bytes());
1034 assert_eq!(dest_len, left_len.strict_div(8));
1035
1036 for i in 0..dest_len {
1037 let dest = ecx.project_index(&dest, i)?;
1038
1039 let mut acc: u16 = 0;
1040 for j in 0..8 {
1041 let src_index = i.strict_mul(8).strict_add(j);
1042
1043 let left = ecx.project_index(&left, src_index)?;
1044 let left = ecx.read_scalar(&left)?.to_u8()?;
1045
1046 let right = ecx.project_index(&right, src_index)?;
1047 let right = ecx.read_scalar(&right)?.to_u8()?;
1048
1049 acc = acc.strict_add(left.abs_diff(right).into());
1050 }
1051
1052 ecx.write_scalar(Scalar::from_u64(acc.into()), &dest)?;
1053 }
1054
1055 interp_ok(())
1056}
1057
1058fn pmaddwd<'tcx>(
1065 ecx: &mut crate::MiriInterpCx<'tcx>,
1066 left: &OpTy<'tcx>,
1067 right: &OpTy<'tcx>,
1068 dest: &MPlaceTy<'tcx>,
1069) -> InterpResult<'tcx, ()> {
1070 let (left, left_len) = ecx.project_to_simd(left)?;
1071 let (right, right_len) = ecx.project_to_simd(right)?;
1072 let (dest, dest_len) = ecx.project_to_simd(dest)?;
1073
1074 assert_eq!(left_len, right_len);
1078 assert_eq!(dest_len.strict_mul(2), left_len);
1079
1080 for i in 0..dest_len {
1081 let j1 = i.strict_mul(2);
1082 let left1 = ecx.read_scalar(&ecx.project_index(&left, j1)?)?.to_i16()?;
1083 let right1 = ecx.read_scalar(&ecx.project_index(&right, j1)?)?.to_i16()?;
1084
1085 let j2 = j1.strict_add(1);
1086 let left2 = ecx.read_scalar(&ecx.project_index(&left, j2)?)?.to_i16()?;
1087 let right2 = ecx.read_scalar(&ecx.project_index(&right, j2)?)?.to_i16()?;
1088
1089 let dest = ecx.project_index(&dest, i)?;
1090
1091 let mul1 = i32::from(left1).strict_mul(right1.into());
1093 let mul2 = i32::from(left2).strict_mul(right2.into());
1094 let res = mul1.wrapping_add(mul2);
1097
1098 ecx.write_scalar(Scalar::from_i32(res), &dest)?;
1099 }
1100
1101 interp_ok(())
1102}
1103
1104fn pmaddbw<'tcx>(
1113 ecx: &mut crate::MiriInterpCx<'tcx>,
1114 left: &OpTy<'tcx>,
1115 right: &OpTy<'tcx>,
1116 dest: &MPlaceTy<'tcx>,
1117) -> InterpResult<'tcx, ()> {
1118 let (left, left_len) = ecx.project_to_simd(left)?;
1119 let (right, right_len) = ecx.project_to_simd(right)?;
1120 let (dest, dest_len) = ecx.project_to_simd(dest)?;
1121
1122 assert_eq!(left_len, right_len);
1126 assert_eq!(dest_len.strict_mul(2), left_len);
1127
1128 for i in 0..dest_len {
1129 let j1 = i.strict_mul(2);
1130 let left1 = ecx.read_scalar(&ecx.project_index(&left, j1)?)?.to_u8()?;
1131 let right1 = ecx.read_scalar(&ecx.project_index(&right, j1)?)?.to_i8()?;
1132
1133 let j2 = j1.strict_add(1);
1134 let left2 = ecx.read_scalar(&ecx.project_index(&left, j2)?)?.to_u8()?;
1135 let right2 = ecx.read_scalar(&ecx.project_index(&right, j2)?)?.to_i8()?;
1136
1137 let dest = ecx.project_index(&dest, i)?;
1138
1139 let mul1 = i16::from(left1).strict_mul(right1.into());
1141 let mul2 = i16::from(left2).strict_mul(right2.into());
1142 let res = mul1.saturating_add(mul2);
1143
1144 ecx.write_scalar(Scalar::from_i16(res), &dest)?;
1145 }
1146
1147 interp_ok(())
1148}
1149
1150fn permute<'tcx>(
1167 ecx: &mut crate::MiriInterpCx<'tcx>,
1168 values: &OpTy<'tcx>,
1169 indices: &OpTy<'tcx>,
1170 dest: &MPlaceTy<'tcx>,
1171) -> InterpResult<'tcx, ()> {
1172 let (values, values_len) = ecx.project_to_simd(values)?;
1173 let (indices, indices_len) = ecx.project_to_simd(indices)?;
1174 let (dest, dest_len) = ecx.project_to_simd(dest)?;
1175
1176 assert_eq!(dest_len, values_len);
1181 assert_eq!(dest_len, indices_len);
1182
1183 assert!(dest_len.is_power_of_two());
1186 let mask = u128::from(dest_len).strict_sub(1);
1187
1188 for i in 0..dest_len {
1189 let dest = ecx.project_index(&dest, i)?;
1190 let index_place = ecx.project_index(&indices, i)?;
1191 let index = ecx.read_scalar(&index_place)?.to_uint(index_place.layout.size)?;
1192 let element = ecx.project_index(&values, u64::try_from(index & mask).unwrap())?;
1194
1195 ecx.copy_op(&element, &dest)?;
1196 }
1197
1198 interp_ok(())
1199}
1200
1201fn permute2<'tcx>(
1215 ecx: &mut crate::MiriInterpCx<'tcx>,
1216 left: &OpTy<'tcx>,
1217 indices: &OpTy<'tcx>,
1218 right: &OpTy<'tcx>,
1219 dest: &MPlaceTy<'tcx>,
1220) -> InterpResult<'tcx, ()> {
1221 let (left, left_len) = ecx.project_to_simd(left)?;
1222 let (indices, indices_len) = ecx.project_to_simd(indices)?;
1223 let (right, right_len) = ecx.project_to_simd(right)?;
1224 let (dest, dest_len) = ecx.project_to_simd(dest)?;
1225
1226 assert_eq!(dest_len, left_len);
1227 assert_eq!(dest_len, indices_len);
1228 assert_eq!(dest_len, right_len);
1229
1230 assert!(dest_len.is_power_of_two());
1233 let lane_mask = u128::from(dest_len).strict_sub(1);
1234 let vector_select_bit = u128::from(dest_len);
1235
1236 for i in 0..dest_len {
1237 let dest = ecx.project_index(&dest, i)?;
1238 let index_place = ecx.project_index(&indices, i)?;
1239 let index = ecx.read_scalar(&index_place)?.to_uint(index_place.layout.size)?;
1240 let lane = u64::try_from(index & lane_mask).unwrap();
1242 let src = if index & vector_select_bit == 0 { &left } else { &right };
1243 let element = ecx.project_index(src, lane)?;
1244
1245 ecx.copy_op(&element, &dest)?;
1246 }
1247
1248 interp_ok(())
1249}
1250
1251fn pmulhrsw<'tcx>(
1259 ecx: &mut crate::MiriInterpCx<'tcx>,
1260 left: &OpTy<'tcx>,
1261 right: &OpTy<'tcx>,
1262 dest: &MPlaceTy<'tcx>,
1263) -> InterpResult<'tcx, ()> {
1264 let (left, left_len) = ecx.project_to_simd(left)?;
1265 let (right, right_len) = ecx.project_to_simd(right)?;
1266 let (dest, dest_len) = ecx.project_to_simd(dest)?;
1267
1268 assert_eq!(dest_len, left_len);
1269 assert_eq!(dest_len, right_len);
1270
1271 for i in 0..dest_len {
1272 let left = ecx.read_scalar(&ecx.project_index(&left, i)?)?.to_i16()?;
1273 let right = ecx.read_scalar(&ecx.project_index(&right, i)?)?.to_i16()?;
1274 let dest = ecx.project_index(&dest, i)?;
1275
1276 let res = (i32::from(left).strict_mul(right.into()) >> 14).strict_add(1) >> 1;
1277
1278 #[expect(clippy::as_conversions)]
1281 let res = res as i16;
1282
1283 ecx.write_scalar(Scalar::from_i16(res), &dest)?;
1284 }
1285
1286 interp_ok(())
1287}
1288
1289fn pclmulqdq<'tcx>(
1300 ecx: &mut MiriInterpCx<'tcx>,
1301 left: &OpTy<'tcx>,
1302 right: &OpTy<'tcx>,
1303 imm8: &OpTy<'tcx>,
1304 dest: &MPlaceTy<'tcx>,
1305 len: u64,
1306) -> InterpResult<'tcx, ()> {
1307 assert_eq!(left.layout, right.layout);
1308 assert_eq!(left.layout.size, dest.layout.size);
1309 assert!([2u64, 4, 8].contains(&len));
1310
1311 let src_layout = ecx.layout_of(Ty::new_array(ecx.tcx.tcx, ecx.tcx.types.u64, len))?;
1315 let dest_layout = ecx.layout_of(Ty::new_array(ecx.tcx.tcx, ecx.tcx.types.u128, len / 2))?;
1316
1317 let left = left.transmute(src_layout, ecx)?;
1318 let right = right.transmute(src_layout, ecx)?;
1319 let dest = dest.transmute(dest_layout, ecx)?;
1320
1321 let imm8 = ecx.read_scalar(imm8)?.to_u8()?;
1322
1323 for i in 0..(len / 2) {
1324 let lo = i.strict_mul(2);
1325 let hi = i.strict_mul(2).strict_add(1);
1326
1327 let index = if (imm8 & 0x01) == 0 { lo } else { hi };
1329 let left = ecx.read_scalar(&ecx.project_index(&left, index)?)?.to_u64()?;
1330
1331 let index = if (imm8 & 0x10) == 0 { lo } else { hi };
1333 let right = ecx.read_scalar(&ecx.project_index(&right, index)?)?.to_u64()?;
1334
1335 let result = left.widening_carryless_mul(right);
1336
1337 let dest = ecx.project_index(&dest, i)?;
1338 ecx.write_scalar(Scalar::from_u128(result), &dest)?;
1339 }
1340
1341 interp_ok(())
1342}
1343
1344fn pshufb<'tcx>(
1357 ecx: &mut crate::MiriInterpCx<'tcx>,
1358 left: &OpTy<'tcx>,
1359 right: &OpTy<'tcx>,
1360 dest: &MPlaceTy<'tcx>,
1361) -> InterpResult<'tcx, ()> {
1362 let (left, left_len) = ecx.project_to_simd(left)?;
1363 let (right, right_len) = ecx.project_to_simd(right)?;
1364 let (dest, dest_len) = ecx.project_to_simd(dest)?;
1365
1366 assert_eq!(dest_len, left_len);
1367 assert_eq!(dest_len, right_len);
1368
1369 for i in 0..dest_len {
1370 let right = ecx.read_scalar(&ecx.project_index(&right, i)?)?.to_u8()?;
1371 let dest = ecx.project_index(&dest, i)?;
1372
1373 let res = if right & 0x80 == 0 {
1374 let block_offset = i & !15; let j = block_offset.strict_add((right % 16).into());
1377 ecx.read_scalar(&ecx.project_index(&left, j)?)?
1378 } else {
1379 Scalar::from_u8(0)
1381 };
1382
1383 ecx.write_scalar(res, &dest)?;
1384 }
1385
1386 interp_ok(())
1387}
1388
1389fn pack_generic<'tcx>(
1397 ecx: &mut crate::MiriInterpCx<'tcx>,
1398 left: &OpTy<'tcx>,
1399 right: &OpTy<'tcx>,
1400 dest: &MPlaceTy<'tcx>,
1401 f: impl Fn(Scalar) -> InterpResult<'tcx, Scalar>,
1402) -> InterpResult<'tcx, ()> {
1403 assert_eq!(left.layout, right.layout);
1404 assert_eq!(left.layout.size, dest.layout.size);
1405
1406 let (num_chunks, op_items_per_chunk, left) = split_simd_to_128bit_chunks(ecx, left)?;
1407 let (_, _, right) = split_simd_to_128bit_chunks(ecx, right)?;
1408 let (_, dest_items_per_chunk, dest) = split_simd_to_128bit_chunks(ecx, dest)?;
1409
1410 assert_eq!(dest_items_per_chunk, op_items_per_chunk.strict_mul(2));
1411
1412 for i in 0..num_chunks {
1413 let left = ecx.project_index(&left, i)?;
1414 let right = ecx.project_index(&right, i)?;
1415 let dest = ecx.project_index(&dest, i)?;
1416
1417 for j in 0..op_items_per_chunk {
1418 let left = ecx.read_scalar(&ecx.project_index(&left, j)?)?;
1419 let right = ecx.read_scalar(&ecx.project_index(&right, j)?)?;
1420 let left_dest = ecx.project_index(&dest, j)?;
1421 let right_dest = ecx.project_index(&dest, j.strict_add(op_items_per_chunk))?;
1422
1423 let left_res = f(left)?;
1424 let right_res = f(right)?;
1425
1426 ecx.write_scalar(left_res, &left_dest)?;
1427 ecx.write_scalar(right_res, &right_dest)?;
1428 }
1429 }
1430
1431 interp_ok(())
1432}
1433
1434fn packsswb<'tcx>(
1441 ecx: &mut crate::MiriInterpCx<'tcx>,
1442 left: &OpTy<'tcx>,
1443 right: &OpTy<'tcx>,
1444 dest: &MPlaceTy<'tcx>,
1445) -> InterpResult<'tcx, ()> {
1446 pack_generic(ecx, left, right, dest, |op| {
1447 let op = op.to_i16()?;
1448 let res = i8::try_from(op).unwrap_or(if op < 0 { i8::MIN } else { i8::MAX });
1449 interp_ok(Scalar::from_i8(res))
1450 })
1451}
1452
1453fn packuswb<'tcx>(
1460 ecx: &mut crate::MiriInterpCx<'tcx>,
1461 left: &OpTy<'tcx>,
1462 right: &OpTy<'tcx>,
1463 dest: &MPlaceTy<'tcx>,
1464) -> InterpResult<'tcx, ()> {
1465 pack_generic(ecx, left, right, dest, |op| {
1466 let op = op.to_i16()?;
1467 let res = u8::try_from(op).unwrap_or(if op < 0 { 0 } else { u8::MAX });
1468 interp_ok(Scalar::from_u8(res))
1469 })
1470}
1471
1472fn packssdw<'tcx>(
1479 ecx: &mut crate::MiriInterpCx<'tcx>,
1480 left: &OpTy<'tcx>,
1481 right: &OpTy<'tcx>,
1482 dest: &MPlaceTy<'tcx>,
1483) -> InterpResult<'tcx, ()> {
1484 pack_generic(ecx, left, right, dest, |op| {
1485 let op = op.to_i32()?;
1486 let res = i16::try_from(op).unwrap_or(if op < 0 { i16::MIN } else { i16::MAX });
1487 interp_ok(Scalar::from_i16(res))
1488 })
1489}
1490
1491fn packusdw<'tcx>(
1498 ecx: &mut crate::MiriInterpCx<'tcx>,
1499 left: &OpTy<'tcx>,
1500 right: &OpTy<'tcx>,
1501 dest: &MPlaceTy<'tcx>,
1502) -> InterpResult<'tcx, ()> {
1503 pack_generic(ecx, left, right, dest, |op| {
1504 let op = op.to_i32()?;
1505 let res = u16::try_from(op).unwrap_or(if op < 0 { 0 } else { u16::MAX });
1506 interp_ok(Scalar::from_u16(res))
1507 })
1508}
1509
1510fn psign<'tcx>(
1515 ecx: &mut crate::MiriInterpCx<'tcx>,
1516 left: &OpTy<'tcx>,
1517 right: &OpTy<'tcx>,
1518 dest: &MPlaceTy<'tcx>,
1519) -> InterpResult<'tcx, ()> {
1520 let (left, left_len) = ecx.project_to_simd(left)?;
1521 let (right, right_len) = ecx.project_to_simd(right)?;
1522 let (dest, dest_len) = ecx.project_to_simd(dest)?;
1523
1524 assert_eq!(dest_len, left_len);
1525 assert_eq!(dest_len, right_len);
1526
1527 for i in 0..dest_len {
1528 let dest = ecx.project_index(&dest, i)?;
1529 let left = ecx.read_immediate(&ecx.project_index(&left, i)?)?;
1530 let right = ecx.read_scalar(&ecx.project_index(&right, i)?)?.to_int(dest.layout.size)?;
1531
1532 let res =
1533 ecx.binary_op(mir::BinOp::Mul, &left, &ImmTy::from_int(right.signum(), dest.layout))?;
1534
1535 ecx.write_immediate(*res, &dest)?;
1536 }
1537
1538 interp_ok(())
1539}
1540
1541fn carrying_add<'tcx>(
1545 ecx: &mut crate::MiriInterpCx<'tcx>,
1546 cb_in: &OpTy<'tcx>,
1547 a: &OpTy<'tcx>,
1548 b: &OpTy<'tcx>,
1549 op: mir::BinOp,
1550) -> InterpResult<'tcx, (ImmTy<'tcx>, Scalar)> {
1551 assert!(op == mir::BinOp::AddWithOverflow || op == mir::BinOp::SubWithOverflow);
1552
1553 let cb_in = ecx.read_scalar(cb_in)?.to_u8()? != 0;
1554 let a = ecx.read_immediate(a)?;
1555 let b = ecx.read_immediate(b)?;
1556
1557 let (sum, overflow1) = ecx.binary_op(op, &a, &b)?.to_pair(ecx);
1558 let (sum, overflow2) =
1559 ecx.binary_op(op, &sum, &ImmTy::from_uint(cb_in, a.layout))?.to_pair(ecx);
1560 let cb_out = overflow1.to_scalar().to_bool()? | overflow2.to_scalar().to_bool()?;
1561
1562 interp_ok((sum, Scalar::from_u8(cb_out.into())))
1563}