]> git.lizzy.rs Git - rust.git/blob - src/shims/intrinsics.rs
Auto merge of #2064 - RalfJung:rustup, r=RalfJung
[rust.git] / src / shims / intrinsics.rs
1 use std::iter;
2
3 use log::trace;
4
5 use rustc_apfloat::{Float, Round};
6 use rustc_middle::ty::layout::{HasParamEnv, IntegerExt, LayoutOf};
7 use rustc_middle::{mir, mir::BinOp, ty, ty::FloatTy};
8 use rustc_target::abi::{Align, Endian, HasDataLayout, Integer, Size};
9
10 use crate::*;
11 use helpers::check_arg_count;
12
13 pub enum AtomicOp {
14     MirOp(mir::BinOp, bool),
15     Max,
16     Min,
17 }
18
19 impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
20 pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
21     fn call_intrinsic(
22         &mut self,
23         instance: ty::Instance<'tcx>,
24         args: &[OpTy<'tcx, Tag>],
25         ret: Option<(&PlaceTy<'tcx, Tag>, mir::BasicBlock)>,
26         _unwind: StackPopUnwind,
27     ) -> InterpResult<'tcx> {
28         let this = self.eval_context_mut();
29
30         if this.emulate_intrinsic(instance, args, ret)? {
31             return Ok(());
32         }
33
34         // All supported intrinsics have a return place.
35         let intrinsic_name = this.tcx.item_name(instance.def_id());
36         let intrinsic_name = intrinsic_name.as_str();
37         let (dest, ret) = match ret {
38             None => throw_unsup_format!("unimplemented (diverging) intrinsic: {}", intrinsic_name),
39             Some(p) => p,
40         };
41
42         // Then handle terminating intrinsics.
43         match intrinsic_name {
44             // Miri overwriting CTFE intrinsics.
45             "ptr_guaranteed_eq" => {
46                 let &[ref left, ref right] = check_arg_count(args)?;
47                 let left = this.read_immediate(left)?;
48                 let right = this.read_immediate(right)?;
49                 this.binop_ignore_overflow(mir::BinOp::Eq, &left, &right, dest)?;
50             }
51             "ptr_guaranteed_ne" => {
52                 let &[ref left, ref right] = check_arg_count(args)?;
53                 let left = this.read_immediate(left)?;
54                 let right = this.read_immediate(right)?;
55                 this.binop_ignore_overflow(mir::BinOp::Ne, &left, &right, dest)?;
56             }
57             "const_allocate" => {
58                 // For now, for compatibility with the run-time implementation of this, we just return null.
59                 // See <https://github.com/rust-lang/rust/issues/93935>.
60                 this.write_null(dest)?;
61             }
62             "const_deallocate" => {
63                 // complete NOP
64             }
65
66             // Raw memory accesses
67             "volatile_load" => {
68                 let &[ref place] = check_arg_count(args)?;
69                 let place = this.deref_operand(place)?;
70                 this.copy_op(&place.into(), dest)?;
71             }
72             "volatile_store" => {
73                 let &[ref place, ref dest] = check_arg_count(args)?;
74                 let place = this.deref_operand(place)?;
75                 this.copy_op(dest, &place.into())?;
76             }
77
78             "write_bytes" | "volatile_set_memory" => {
79                 let &[ref ptr, ref val_byte, ref count] = check_arg_count(args)?;
80                 let ty = instance.substs.type_at(0);
81                 let ty_layout = this.layout_of(ty)?;
82                 let val_byte = this.read_scalar(val_byte)?.to_u8()?;
83                 let ptr = this.read_pointer(ptr)?;
84                 let count = this.read_scalar(count)?.to_machine_usize(this)?;
85                 // `checked_mul` enforces a too small bound (the correct one would probably be machine_isize_max),
86                 // but no actual allocation can be big enough for the difference to be noticeable.
87                 let byte_count = ty_layout.size.checked_mul(count, this).ok_or_else(|| {
88                     err_ub_format!("overflow computing total size of `{}`", intrinsic_name)
89                 })?;
90                 this.write_bytes_ptr(
91                     ptr,
92                     iter::repeat(val_byte).take(byte_count.bytes() as usize),
93                 )?;
94             }
95
96             // Floating-point operations
97             "fabsf32" => {
98                 let &[ref f] = check_arg_count(args)?;
99                 let f = this.read_scalar(f)?.to_f32()?;
100                 // Can be implemented in soft-floats.
101                 this.write_scalar(Scalar::from_f32(f.abs()), dest)?;
102             }
103             "fabsf64" => {
104                 let &[ref f] = check_arg_count(args)?;
105                 let f = this.read_scalar(f)?.to_f64()?;
106                 // Can be implemented in soft-floats.
107                 this.write_scalar(Scalar::from_f64(f.abs()), dest)?;
108             }
109             #[rustfmt::skip]
110             | "sinf32"
111             | "cosf32"
112             | "sqrtf32"
113             | "expf32"
114             | "exp2f32"
115             | "logf32"
116             | "log10f32"
117             | "log2f32"
118             | "floorf32"
119             | "ceilf32"
120             | "truncf32"
121             | "roundf32"
122             => {
123                 let &[ref f] = check_arg_count(args)?;
124                 // FIXME: Using host floats.
125                 let f = f32::from_bits(this.read_scalar(f)?.to_u32()?);
126                 let f = match intrinsic_name {
127                     "sinf32" => f.sin(),
128                     "cosf32" => f.cos(),
129                     "sqrtf32" => f.sqrt(),
130                     "expf32" => f.exp(),
131                     "exp2f32" => f.exp2(),
132                     "logf32" => f.ln(),
133                     "log10f32" => f.log10(),
134                     "log2f32" => f.log2(),
135                     "floorf32" => f.floor(),
136                     "ceilf32" => f.ceil(),
137                     "truncf32" => f.trunc(),
138                     "roundf32" => f.round(),
139                     _ => bug!(),
140                 };
141                 this.write_scalar(Scalar::from_u32(f.to_bits()), dest)?;
142             }
143
144             #[rustfmt::skip]
145             | "sinf64"
146             | "cosf64"
147             | "sqrtf64"
148             | "expf64"
149             | "exp2f64"
150             | "logf64"
151             | "log10f64"
152             | "log2f64"
153             | "floorf64"
154             | "ceilf64"
155             | "truncf64"
156             | "roundf64"
157             => {
158                 let &[ref f] = check_arg_count(args)?;
159                 // FIXME: Using host floats.
160                 let f = f64::from_bits(this.read_scalar(f)?.to_u64()?);
161                 let f = match intrinsic_name {
162                     "sinf64" => f.sin(),
163                     "cosf64" => f.cos(),
164                     "sqrtf64" => f.sqrt(),
165                     "expf64" => f.exp(),
166                     "exp2f64" => f.exp2(),
167                     "logf64" => f.ln(),
168                     "log10f64" => f.log10(),
169                     "log2f64" => f.log2(),
170                     "floorf64" => f.floor(),
171                     "ceilf64" => f.ceil(),
172                     "truncf64" => f.trunc(),
173                     "roundf64" => f.round(),
174                     _ => bug!(),
175                 };
176                 this.write_scalar(Scalar::from_u64(f.to_bits()), dest)?;
177             }
178
179             #[rustfmt::skip]
180             | "fadd_fast"
181             | "fsub_fast"
182             | "fmul_fast"
183             | "fdiv_fast"
184             | "frem_fast"
185             => {
186                 let &[ref a, ref b] = check_arg_count(args)?;
187                 let a = this.read_immediate(a)?;
188                 let b = this.read_immediate(b)?;
189                 let op = match intrinsic_name {
190                     "fadd_fast" => mir::BinOp::Add,
191                     "fsub_fast" => mir::BinOp::Sub,
192                     "fmul_fast" => mir::BinOp::Mul,
193                     "fdiv_fast" => mir::BinOp::Div,
194                     "frem_fast" => mir::BinOp::Rem,
195                     _ => bug!(),
196                 };
197                 let float_finite = |x: ImmTy<'tcx, _>| -> InterpResult<'tcx, bool> {
198                     Ok(match x.layout.ty.kind() {
199                         ty::Float(FloatTy::F32) => x.to_scalar()?.to_f32()?.is_finite(),
200                         ty::Float(FloatTy::F64) => x.to_scalar()?.to_f64()?.is_finite(),
201                         _ => bug!(
202                             "`{}` called with non-float input type {:?}",
203                             intrinsic_name,
204                             x.layout.ty
205                         ),
206                     })
207                 };
208                 match (float_finite(a)?, float_finite(b)?) {
209                     (false, false) => throw_ub_format!(
210                         "`{}` intrinsic called with non-finite value as both parameters",
211                         intrinsic_name,
212                     ),
213                     (false, _) => throw_ub_format!(
214                         "`{}` intrinsic called with non-finite value as first parameter",
215                         intrinsic_name,
216                     ),
217                     (_, false) => throw_ub_format!(
218                         "`{}` intrinsic called with non-finite value as second parameter",
219                         intrinsic_name,
220                     ),
221                     _ => {}
222                 }
223                 this.binop_ignore_overflow(op, &a, &b, dest)?;
224             }
225
226             #[rustfmt::skip]
227             | "minnumf32"
228             | "maxnumf32"
229             | "copysignf32"
230             => {
231                 let &[ref a, ref b] = check_arg_count(args)?;
232                 let a = this.read_scalar(a)?.to_f32()?;
233                 let b = this.read_scalar(b)?.to_f32()?;
234                 let res = match intrinsic_name {
235                     "minnumf32" => a.min(b),
236                     "maxnumf32" => a.max(b),
237                     "copysignf32" => a.copy_sign(b),
238                     _ => bug!(),
239                 };
240                 this.write_scalar(Scalar::from_f32(res), dest)?;
241             }
242
243             #[rustfmt::skip]
244             | "minnumf64"
245             | "maxnumf64"
246             | "copysignf64"
247             => {
248                 let &[ref a, ref b] = check_arg_count(args)?;
249                 let a = this.read_scalar(a)?.to_f64()?;
250                 let b = this.read_scalar(b)?.to_f64()?;
251                 let res = match intrinsic_name {
252                     "minnumf64" => a.min(b),
253                     "maxnumf64" => a.max(b),
254                     "copysignf64" => a.copy_sign(b),
255                     _ => bug!(),
256                 };
257                 this.write_scalar(Scalar::from_f64(res), dest)?;
258             }
259
260             "powf32" => {
261                 let &[ref f, ref f2] = check_arg_count(args)?;
262                 // FIXME: Using host floats.
263                 let f = f32::from_bits(this.read_scalar(f)?.to_u32()?);
264                 let f2 = f32::from_bits(this.read_scalar(f2)?.to_u32()?);
265                 this.write_scalar(Scalar::from_u32(f.powf(f2).to_bits()), dest)?;
266             }
267
268             "powf64" => {
269                 let &[ref f, ref f2] = check_arg_count(args)?;
270                 // FIXME: Using host floats.
271                 let f = f64::from_bits(this.read_scalar(f)?.to_u64()?);
272                 let f2 = f64::from_bits(this.read_scalar(f2)?.to_u64()?);
273                 this.write_scalar(Scalar::from_u64(f.powf(f2).to_bits()), dest)?;
274             }
275
276             "fmaf32" => {
277                 let &[ref a, ref b, ref c] = check_arg_count(args)?;
278                 let a = this.read_scalar(a)?.to_f32()?;
279                 let b = this.read_scalar(b)?.to_f32()?;
280                 let c = this.read_scalar(c)?.to_f32()?;
281                 let res = a.mul_add(b, c).value;
282                 this.write_scalar(Scalar::from_f32(res), dest)?;
283             }
284
285             "fmaf64" => {
286                 let &[ref a, ref b, ref c] = check_arg_count(args)?;
287                 let a = this.read_scalar(a)?.to_f64()?;
288                 let b = this.read_scalar(b)?.to_f64()?;
289                 let c = this.read_scalar(c)?.to_f64()?;
290                 let res = a.mul_add(b, c).value;
291                 this.write_scalar(Scalar::from_f64(res), dest)?;
292             }
293
294             "powif32" => {
295                 let &[ref f, ref i] = check_arg_count(args)?;
296                 // FIXME: Using host floats.
297                 let f = f32::from_bits(this.read_scalar(f)?.to_u32()?);
298                 let i = this.read_scalar(i)?.to_i32()?;
299                 this.write_scalar(Scalar::from_u32(f.powi(i).to_bits()), dest)?;
300             }
301
302             "powif64" => {
303                 let &[ref f, ref i] = check_arg_count(args)?;
304                 // FIXME: Using host floats.
305                 let f = f64::from_bits(this.read_scalar(f)?.to_u64()?);
306                 let i = this.read_scalar(i)?.to_i32()?;
307                 this.write_scalar(Scalar::from_u64(f.powi(i).to_bits()), dest)?;
308             }
309
310             "float_to_int_unchecked" => {
311                 let &[ref val] = check_arg_count(args)?;
312                 let val = this.read_immediate(val)?;
313
314                 let res = match val.layout.ty.kind() {
315                     ty::Float(FloatTy::F32) =>
316                         this.float_to_int_unchecked(val.to_scalar()?.to_f32()?, dest.layout.ty)?,
317                     ty::Float(FloatTy::F64) =>
318                         this.float_to_int_unchecked(val.to_scalar()?.to_f64()?, dest.layout.ty)?,
319                     _ =>
320                         bug!(
321                             "`float_to_int_unchecked` called with non-float input type {:?}",
322                             val.layout.ty
323                         ),
324                 };
325
326                 this.write_scalar(res, dest)?;
327             }
328
329             // SIMD operations
330             #[rustfmt::skip]
331             | "simd_neg"
332             | "simd_fabs"
333             | "simd_ceil"
334             | "simd_floor"
335             | "simd_round"
336             | "simd_trunc"
337             | "simd_fsqrt" => {
338                 let &[ref op] = check_arg_count(args)?;
339                 let (op, op_len) = this.operand_to_simd(op)?;
340                 let (dest, dest_len) = this.place_to_simd(dest)?;
341
342                 assert_eq!(dest_len, op_len);
343
344                 #[derive(Copy, Clone)]
345                 enum HostFloatOp {
346                     Ceil,
347                     Floor,
348                     Round,
349                     Trunc,
350                     Sqrt,
351                 }
352                 #[derive(Copy, Clone)]
353                 enum Op {
354                     MirOp(mir::UnOp),
355                     Abs,
356                     HostOp(HostFloatOp),
357                 }
358                 let which = match intrinsic_name {
359                     "simd_neg" => Op::MirOp(mir::UnOp::Neg),
360                     "simd_fabs" => Op::Abs,
361                     "simd_ceil" => Op::HostOp(HostFloatOp::Ceil),
362                     "simd_floor" => Op::HostOp(HostFloatOp::Floor),
363                     "simd_round" => Op::HostOp(HostFloatOp::Round),
364                     "simd_trunc" => Op::HostOp(HostFloatOp::Trunc),
365                     "simd_fsqrt" => Op::HostOp(HostFloatOp::Sqrt),
366                     _ => unreachable!(),
367                 };
368
369                 for i in 0..dest_len {
370                     let op = this.read_immediate(&this.mplace_index(&op, i)?.into())?;
371                     let dest = this.mplace_index(&dest, i)?;
372                     let val = match which {
373                         Op::MirOp(mir_op) => this.unary_op(mir_op, &op)?.to_scalar()?,
374                         Op::Abs => {
375                             // Works for f32 and f64.
376                             let ty::Float(float_ty) = op.layout.ty.kind() else {
377                                 bug!("{} operand is not a float", intrinsic_name)
378                             };
379                             let op = op.to_scalar()?;
380                             match float_ty {
381                                 FloatTy::F32 => Scalar::from_f32(op.to_f32()?.abs()),
382                                 FloatTy::F64 => Scalar::from_f64(op.to_f64()?.abs()),
383                             }
384                         }
385                         Op::HostOp(host_op) => {
386                             let ty::Float(float_ty) = op.layout.ty.kind() else {
387                                 bug!("{} operand is not a float", intrinsic_name)
388                             };
389                             // FIXME using host floats
390                             match float_ty {
391                                 FloatTy::F32 => {
392                                     let f = f32::from_bits(op.to_scalar()?.to_u32()?);
393                                     let res = match host_op {
394                                         HostFloatOp::Ceil => f.ceil(),
395                                         HostFloatOp::Floor => f.floor(),
396                                         HostFloatOp::Round => f.round(),
397                                         HostFloatOp::Trunc => f.trunc(),
398                                         HostFloatOp::Sqrt => f.sqrt(),
399                                     };
400                                     Scalar::from_u32(res.to_bits())
401                                 }
402                                 FloatTy::F64 => {
403                                     let f = f64::from_bits(op.to_scalar()?.to_u64()?);
404                                     let res = match host_op {
405                                         HostFloatOp::Ceil => f.ceil(),
406                                         HostFloatOp::Floor => f.floor(),
407                                         HostFloatOp::Round => f.round(),
408                                         HostFloatOp::Trunc => f.trunc(),
409                                         HostFloatOp::Sqrt => f.sqrt(),
410                                     };
411                                     Scalar::from_u64(res.to_bits())
412                                 }
413                             }
414
415                         }
416                     };
417                     this.write_scalar(val, &dest.into())?;
418                 }
419             }
420             #[rustfmt::skip]
421             | "simd_add"
422             | "simd_sub"
423             | "simd_mul"
424             | "simd_div"
425             | "simd_rem"
426             | "simd_shl"
427             | "simd_shr"
428             | "simd_and"
429             | "simd_or"
430             | "simd_xor"
431             | "simd_eq"
432             | "simd_ne"
433             | "simd_lt"
434             | "simd_le"
435             | "simd_gt"
436             | "simd_ge"
437             | "simd_fmax"
438             | "simd_fmin"
439             | "simd_saturating_add"
440             | "simd_saturating_sub"
441             | "simd_arith_offset" => {
442                 use mir::BinOp;
443
444                 let &[ref left, ref right] = check_arg_count(args)?;
445                 let (left, left_len) = this.operand_to_simd(left)?;
446                 let (right, right_len) = this.operand_to_simd(right)?;
447                 let (dest, dest_len) = this.place_to_simd(dest)?;
448
449                 assert_eq!(dest_len, left_len);
450                 assert_eq!(dest_len, right_len);
451
452                 enum Op {
453                     MirOp(BinOp),
454                     SaturatingOp(BinOp),
455                     FMax,
456                     FMin,
457                     WrappingOffset,
458                 }
459                 let which = match intrinsic_name {
460                     "simd_add" => Op::MirOp(BinOp::Add),
461                     "simd_sub" => Op::MirOp(BinOp::Sub),
462                     "simd_mul" => Op::MirOp(BinOp::Mul),
463                     "simd_div" => Op::MirOp(BinOp::Div),
464                     "simd_rem" => Op::MirOp(BinOp::Rem),
465                     "simd_shl" => Op::MirOp(BinOp::Shl),
466                     "simd_shr" => Op::MirOp(BinOp::Shr),
467                     "simd_and" => Op::MirOp(BinOp::BitAnd),
468                     "simd_or" => Op::MirOp(BinOp::BitOr),
469                     "simd_xor" => Op::MirOp(BinOp::BitXor),
470                     "simd_eq" => Op::MirOp(BinOp::Eq),
471                     "simd_ne" => Op::MirOp(BinOp::Ne),
472                     "simd_lt" => Op::MirOp(BinOp::Lt),
473                     "simd_le" => Op::MirOp(BinOp::Le),
474                     "simd_gt" => Op::MirOp(BinOp::Gt),
475                     "simd_ge" => Op::MirOp(BinOp::Ge),
476                     "simd_fmax" => Op::FMax,
477                     "simd_fmin" => Op::FMin,
478                     "simd_saturating_add" => Op::SaturatingOp(BinOp::Add),
479                     "simd_saturating_sub" => Op::SaturatingOp(BinOp::Sub),
480                     "simd_arith_offset" => Op::WrappingOffset,
481                     _ => unreachable!(),
482                 };
483
484                 for i in 0..dest_len {
485                     let left = this.read_immediate(&this.mplace_index(&left, i)?.into())?;
486                     let right = this.read_immediate(&this.mplace_index(&right, i)?.into())?;
487                     let dest = this.mplace_index(&dest, i)?;
488                     let val = match which {
489                         Op::MirOp(mir_op) => {
490                             let (val, overflowed, ty) = this.overflowing_binary_op(mir_op, &left, &right)?;
491                             if matches!(mir_op, BinOp::Shl | BinOp::Shr) {
492                                 // Shifts have extra UB as SIMD operations that the MIR binop does not have.
493                                 // See <https://github.com/rust-lang/rust/issues/91237>.
494                                 if overflowed {
495                                     let r_val = right.to_scalar()?.to_bits(right.layout.size)?;
496                                     throw_ub_format!("overflowing shift by {} in `{}` in SIMD lane {}", r_val, intrinsic_name, i);
497                                 }
498                             }
499                             if matches!(mir_op, BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge) {
500                                 // Special handling for boolean-returning operations
501                                 assert_eq!(ty, this.tcx.types.bool);
502                                 let val = val.to_bool().unwrap();
503                                 bool_to_simd_element(val, dest.layout.size)
504                             } else {
505                                 assert_ne!(ty, this.tcx.types.bool);
506                                 assert_eq!(ty, dest.layout.ty);
507                                 val
508                             }
509                         }
510                         Op::SaturatingOp(mir_op) => {
511                             this.saturating_arith(mir_op, &left, &right)?
512                         }
513                         Op::WrappingOffset => {
514                             let ptr = this.scalar_to_ptr(left.to_scalar()?)?;
515                             let offset_count = right.to_scalar()?.to_machine_isize(this)?;
516                             let pointee_ty = left.layout.ty.builtin_deref(true).unwrap().ty;
517
518                             let pointee_size = i64::try_from(this.layout_of(pointee_ty)?.size.bytes()).unwrap();
519                             let offset_bytes = offset_count.wrapping_mul(pointee_size);
520                             let offset_ptr = ptr.wrapping_signed_offset(offset_bytes, this);
521                             Scalar::from_maybe_pointer(offset_ptr, this)
522                         }
523                         Op::FMax => {
524                             fmax_op(&left, &right)?
525                         }
526                         Op::FMin => {
527                             fmin_op(&left, &right)?
528                         }
529                     };
530                     this.write_scalar(val, &dest.into())?;
531                 }
532             }
533             "simd_fma" => {
534                 let &[ref a, ref b, ref c] = check_arg_count(args)?;
535                 let (a, a_len) = this.operand_to_simd(a)?;
536                 let (b, b_len) = this.operand_to_simd(b)?;
537                 let (c, c_len) = this.operand_to_simd(c)?;
538                 let (dest, dest_len) = this.place_to_simd(dest)?;
539
540                 assert_eq!(dest_len, a_len);
541                 assert_eq!(dest_len, b_len);
542                 assert_eq!(dest_len, c_len);
543
544                 for i in 0..dest_len {
545                     let a = this.read_immediate(&this.mplace_index(&a, i)?.into())?.to_scalar()?;
546                     let b = this.read_immediate(&this.mplace_index(&b, i)?.into())?.to_scalar()?;
547                     let c = this.read_immediate(&this.mplace_index(&c, i)?.into())?.to_scalar()?;
548                     let dest = this.mplace_index(&dest, i)?;
549
550                     // Works for f32 and f64.
551                     let ty::Float(float_ty) = dest.layout.ty.kind() else {
552                         bug!("{} operand is not a float", intrinsic_name)
553                     };
554                     let val = match float_ty {
555                         FloatTy::F32 =>
556                             Scalar::from_f32(a.to_f32()?.mul_add(b.to_f32()?, c.to_f32()?).value),
557                         FloatTy::F64 =>
558                             Scalar::from_f64(a.to_f64()?.mul_add(b.to_f64()?, c.to_f64()?).value),
559                     };
560                     this.write_scalar(val, &dest.into())?;
561                 }
562             }
563             #[rustfmt::skip]
564             | "simd_reduce_and"
565             | "simd_reduce_or"
566             | "simd_reduce_xor"
567             | "simd_reduce_any"
568             | "simd_reduce_all"
569             | "simd_reduce_max"
570             | "simd_reduce_min" => {
571                 use mir::BinOp;
572
573                 let &[ref op] = check_arg_count(args)?;
574                 let (op, op_len) = this.operand_to_simd(op)?;
575
576                 let imm_from_bool =
577                     |b| ImmTy::from_scalar(Scalar::from_bool(b), this.machine.layouts.bool);
578
579                 enum Op {
580                     MirOp(BinOp),
581                     MirOpBool(BinOp),
582                     Max,
583                     Min,
584                 }
585                 let which = match intrinsic_name {
586                     "simd_reduce_and" => Op::MirOp(BinOp::BitAnd),
587                     "simd_reduce_or" => Op::MirOp(BinOp::BitOr),
588                     "simd_reduce_xor" => Op::MirOp(BinOp::BitXor),
589                     "simd_reduce_any" => Op::MirOpBool(BinOp::BitOr),
590                     "simd_reduce_all" => Op::MirOpBool(BinOp::BitAnd),
591                     "simd_reduce_max" => Op::Max,
592                     "simd_reduce_min" => Op::Min,
593                     _ => unreachable!(),
594                 };
595
596                 // Initialize with first lane, then proceed with the rest.
597                 let mut res = this.read_immediate(&this.mplace_index(&op, 0)?.into())?;
598                 if matches!(which, Op::MirOpBool(_)) {
599                     // Convert to `bool` scalar.
600                     res = imm_from_bool(simd_element_to_bool(res)?);
601                 }
602                 for i in 1..op_len {
603                     let op = this.read_immediate(&this.mplace_index(&op, i)?.into())?;
604                     res = match which {
605                         Op::MirOp(mir_op) => {
606                             this.binary_op(mir_op, &res, &op)?
607                         }
608                         Op::MirOpBool(mir_op) => {
609                             let op = imm_from_bool(simd_element_to_bool(op)?);
610                             this.binary_op(mir_op, &res, &op)?
611                         }
612                         Op::Max => {
613                             if matches!(res.layout.ty.kind(), ty::Float(_)) {
614                                 ImmTy::from_scalar(fmax_op(&res, &op)?, res.layout)
615                             } else {
616                                 // Just boring integers, so NaNs to worry about
617                                 if this.binary_op(BinOp::Ge, &res, &op)?.to_scalar()?.to_bool()? {
618                                     res
619                                 } else {
620                                     op
621                                 }
622                             }
623                         }
624                         Op::Min => {
625                             if matches!(res.layout.ty.kind(), ty::Float(_)) {
626                                 ImmTy::from_scalar(fmin_op(&res, &op)?, res.layout)
627                             } else {
628                                 // Just boring integers, so NaNs to worry about
629                                 if this.binary_op(BinOp::Le, &res, &op)?.to_scalar()?.to_bool()? {
630                                     res
631                                 } else {
632                                     op
633                                 }
634                             }
635                         }
636                     };
637                 }
638                 this.write_immediate(*res, dest)?;
639             }
640             #[rustfmt::skip]
641             | "simd_reduce_add_ordered"
642             | "simd_reduce_mul_ordered" => {
643                 use mir::BinOp;
644
645                 let &[ref op, ref init] = check_arg_count(args)?;
646                 let (op, op_len) = this.operand_to_simd(op)?;
647                 let init = this.read_immediate(init)?;
648
649                 let mir_op = match intrinsic_name {
650                     "simd_reduce_add_ordered" => BinOp::Add,
651                     "simd_reduce_mul_ordered" => BinOp::Mul,
652                     _ => unreachable!(),
653                 };
654
655                 let mut res = init;
656                 for i in 0..op_len {
657                     let op = this.read_immediate(&this.mplace_index(&op, i)?.into())?;
658                     res = this.binary_op(mir_op, &res, &op)?;
659                 }
660                 this.write_immediate(*res, dest)?;
661             }
662             "simd_select" => {
663                 let &[ref mask, ref yes, ref no] = check_arg_count(args)?;
664                 let (mask, mask_len) = this.operand_to_simd(mask)?;
665                 let (yes, yes_len) = this.operand_to_simd(yes)?;
666                 let (no, no_len) = this.operand_to_simd(no)?;
667                 let (dest, dest_len) = this.place_to_simd(dest)?;
668
669                 assert_eq!(dest_len, mask_len);
670                 assert_eq!(dest_len, yes_len);
671                 assert_eq!(dest_len, no_len);
672
673                 for i in 0..dest_len {
674                     let mask = this.read_immediate(&this.mplace_index(&mask, i)?.into())?;
675                     let yes = this.read_immediate(&this.mplace_index(&yes, i)?.into())?;
676                     let no = this.read_immediate(&this.mplace_index(&no, i)?.into())?;
677                     let dest = this.mplace_index(&dest, i)?;
678
679                     let val = if simd_element_to_bool(mask)? { yes } else { no };
680                     this.write_immediate(*val, &dest.into())?;
681                 }
682             }
683             "simd_select_bitmask" => {
684                 let &[ref mask, ref yes, ref no] = check_arg_count(args)?;
685                 let (yes, yes_len) = this.operand_to_simd(yes)?;
686                 let (no, no_len) = this.operand_to_simd(no)?;
687                 let (dest, dest_len) = this.place_to_simd(dest)?;
688                 let bitmask_len = dest_len.max(8);
689
690                 assert!(mask.layout.ty.is_integral());
691                 assert!(bitmask_len <= 64);
692                 assert_eq!(bitmask_len, mask.layout.size.bits());
693                 assert_eq!(dest_len, yes_len);
694                 assert_eq!(dest_len, no_len);
695
696                 let mask: u64 = this
697                     .read_scalar(mask)?
698                     .check_init()?
699                     .to_bits(mask.layout.size)?
700                     .try_into()
701                     .unwrap();
702                 for i in 0..dest_len {
703                     let mask =
704                         mask & (1 << simd_bitmask_index(i, dest_len, this.data_layout().endian));
705                     let yes = this.read_immediate(&this.mplace_index(&yes, i)?.into())?;
706                     let no = this.read_immediate(&this.mplace_index(&no, i)?.into())?;
707                     let dest = this.mplace_index(&dest, i)?;
708
709                     let val = if mask != 0 { yes } else { no };
710                     this.write_immediate(*val, &dest.into())?;
711                 }
712                 for i in dest_len..bitmask_len {
713                     // If the mask is "padded", ensure that padding is all-zero.
714                     let mask = mask & (1 << i);
715                     if mask != 0 {
716                         throw_ub_format!(
717                             "a SIMD bitmask less than 8 bits long must be filled with 0s for the remaining bits"
718                         );
719                     }
720                 }
721             }
722             #[rustfmt::skip]
723             "simd_cast" | "simd_as" => {
724                 let &[ref op] = check_arg_count(args)?;
725                 let (op, op_len) = this.operand_to_simd(op)?;
726                 let (dest, dest_len) = this.place_to_simd(dest)?;
727
728                 assert_eq!(dest_len, op_len);
729
730                 let safe_cast = intrinsic_name == "simd_as";
731
732                 for i in 0..dest_len {
733                     let op = this.read_immediate(&this.mplace_index(&op, i)?.into())?;
734                     let dest = this.mplace_index(&dest, i)?;
735
736                     let val = match (op.layout.ty.kind(), dest.layout.ty.kind()) {
737                         // Int-to-(int|float): always safe
738                         (ty::Int(_) | ty::Uint(_), ty::Int(_) | ty::Uint(_) | ty::Float(_)) =>
739                             this.misc_cast(&op, dest.layout.ty)?,
740                         // Float-to-float: always safe
741                         (ty::Float(_), ty::Float(_)) =>
742                             this.misc_cast(&op, dest.layout.ty)?,
743                         // Float-to-int in safe mode
744                         (ty::Float(_), ty::Int(_) | ty::Uint(_)) if safe_cast =>
745                             this.misc_cast(&op, dest.layout.ty)?,
746                         // Float-to-int in unchecked mode
747                         (ty::Float(FloatTy::F32), ty::Int(_) | ty::Uint(_)) if !safe_cast =>
748                             this.float_to_int_unchecked(op.to_scalar()?.to_f32()?, dest.layout.ty)?.into(),
749                         (ty::Float(FloatTy::F64), ty::Int(_) | ty::Uint(_)) if !safe_cast =>
750                             this.float_to_int_unchecked(op.to_scalar()?.to_f64()?, dest.layout.ty)?.into(),
751                         _ =>
752                             throw_unsup_format!(
753                                 "Unsupported SIMD cast from element type {} to {}",
754                                 op.layout.ty,
755                                 dest.layout.ty
756                             ),
757                     };
758                     this.write_immediate(val, &dest.into())?;
759                 }
760             }
761             "simd_shuffle" => {
762                 let &[ref left, ref right, ref index] = check_arg_count(args)?;
763                 let (left, left_len) = this.operand_to_simd(left)?;
764                 let (right, right_len) = this.operand_to_simd(right)?;
765                 let (dest, dest_len) = this.place_to_simd(dest)?;
766
767                 // `index` is an array, not a SIMD type
768                 let ty::Array(_, index_len) = index.layout.ty.kind() else {
769                     bug!("simd_shuffle index argument has non-array type {}", index.layout.ty)
770                 };
771                 let index_len = index_len.eval_usize(*this.tcx, this.param_env());
772
773                 assert_eq!(left_len, right_len);
774                 assert_eq!(index_len, dest_len);
775
776                 for i in 0..dest_len {
777                     let src_index: u64 = this
778                         .read_immediate(&this.operand_index(&index, i)?.into())?
779                         .to_scalar()?
780                         .to_u32()?
781                         .into();
782                     let dest = this.mplace_index(&dest, i)?;
783
784                     let val = if src_index < left_len {
785                         this.read_immediate(&this.mplace_index(&left, src_index)?.into())?
786                     } else if src_index < left_len.checked_add(right_len).unwrap() {
787                         this.read_immediate(
788                             &this.mplace_index(&right, src_index - left_len)?.into(),
789                         )?
790                     } else {
791                         bug!(
792                             "simd_shuffle index {} is out of bounds for 2 vectors of size {}",
793                             src_index,
794                             left_len
795                         );
796                     };
797                     this.write_immediate(*val, &dest.into())?;
798                 }
799             }
800             "simd_gather" => {
801                 let &[ref passthru, ref ptrs, ref mask] = check_arg_count(args)?;
802                 let (passthru, passthru_len) = this.operand_to_simd(passthru)?;
803                 let (ptrs, ptrs_len) = this.operand_to_simd(ptrs)?;
804                 let (mask, mask_len) = this.operand_to_simd(mask)?;
805                 let (dest, dest_len) = this.place_to_simd(dest)?;
806
807                 assert_eq!(dest_len, passthru_len);
808                 assert_eq!(dest_len, ptrs_len);
809                 assert_eq!(dest_len, mask_len);
810
811                 for i in 0..dest_len {
812                     let passthru = this.read_immediate(&this.mplace_index(&passthru, i)?.into())?;
813                     let ptr = this.read_immediate(&this.mplace_index(&ptrs, i)?.into())?;
814                     let mask = this.read_immediate(&this.mplace_index(&mask, i)?.into())?;
815                     let dest = this.mplace_index(&dest, i)?;
816
817                     let val = if simd_element_to_bool(mask)? {
818                         let place = this.deref_operand(&ptr.into())?;
819                         this.read_immediate(&place.into())?
820                     } else {
821                         passthru
822                     };
823                     this.write_immediate(*val, &dest.into())?;
824                 }
825             }
826             "simd_scatter" => {
827                 let &[ref value, ref ptrs, ref mask] = check_arg_count(args)?;
828                 let (value, value_len) = this.operand_to_simd(value)?;
829                 let (ptrs, ptrs_len) = this.operand_to_simd(ptrs)?;
830                 let (mask, mask_len) = this.operand_to_simd(mask)?;
831
832                 assert_eq!(ptrs_len, value_len);
833                 assert_eq!(ptrs_len, mask_len);
834
835                 for i in 0..ptrs_len {
836                     let value = this.read_immediate(&this.mplace_index(&value, i)?.into())?;
837                     let ptr = this.read_immediate(&this.mplace_index(&ptrs, i)?.into())?;
838                     let mask = this.read_immediate(&this.mplace_index(&mask, i)?.into())?;
839
840                     if simd_element_to_bool(mask)? {
841                         let place = this.deref_operand(&ptr.into())?;
842                         this.write_immediate(*value, &place.into())?;
843                     }
844                 }
845             }
846             "simd_bitmask" => {
847                 let &[ref op] = check_arg_count(args)?;
848                 let (op, op_len) = this.operand_to_simd(op)?;
849                 let bitmask_len = op_len.max(8);
850
851                 assert!(dest.layout.ty.is_integral());
852                 assert!(bitmask_len <= 64);
853                 assert_eq!(bitmask_len, dest.layout.size.bits());
854
855                 let mut res = 0u64;
856                 for i in 0..op_len {
857                     let op = this.read_immediate(&this.mplace_index(&op, i)?.into())?;
858                     if simd_element_to_bool(op)? {
859                         res |= 1 << simd_bitmask_index(i, op_len, this.data_layout().endian);
860                     }
861                 }
862                 this.write_int(res, dest)?;
863             }
864
865             // Atomic operations
866             "atomic_load" => this.atomic_load(args, dest, AtomicReadOp::SeqCst)?,
867             "atomic_load_relaxed" => this.atomic_load(args, dest, AtomicReadOp::Relaxed)?,
868             "atomic_load_acq" => this.atomic_load(args, dest, AtomicReadOp::Acquire)?,
869
870             "atomic_store" => this.atomic_store(args, AtomicWriteOp::SeqCst)?,
871             "atomic_store_relaxed" => this.atomic_store(args, AtomicWriteOp::Relaxed)?,
872             "atomic_store_rel" => this.atomic_store(args, AtomicWriteOp::Release)?,
873
874             "atomic_fence_acq" => this.atomic_fence(args, AtomicFenceOp::Acquire)?,
875             "atomic_fence_rel" => this.atomic_fence(args, AtomicFenceOp::Release)?,
876             "atomic_fence_acqrel" => this.atomic_fence(args, AtomicFenceOp::AcqRel)?,
877             "atomic_fence" => this.atomic_fence(args, AtomicFenceOp::SeqCst)?,
878
879             "atomic_singlethreadfence_acq" => this.compiler_fence(args, AtomicFenceOp::Acquire)?,
880             "atomic_singlethreadfence_rel" => this.compiler_fence(args, AtomicFenceOp::Release)?,
881             "atomic_singlethreadfence_acqrel" =>
882                 this.compiler_fence(args, AtomicFenceOp::AcqRel)?,
883             "atomic_singlethreadfence" => this.compiler_fence(args, AtomicFenceOp::SeqCst)?,
884
885             "atomic_xchg" => this.atomic_exchange(args, dest, AtomicRwOp::SeqCst)?,
886             "atomic_xchg_acq" => this.atomic_exchange(args, dest, AtomicRwOp::Acquire)?,
887             "atomic_xchg_rel" => this.atomic_exchange(args, dest, AtomicRwOp::Release)?,
888             "atomic_xchg_acqrel" => this.atomic_exchange(args, dest, AtomicRwOp::AcqRel)?,
889             "atomic_xchg_relaxed" => this.atomic_exchange(args, dest, AtomicRwOp::Relaxed)?,
890
891             #[rustfmt::skip]
892             "atomic_cxchg" =>
893                 this.atomic_compare_exchange(args, dest, AtomicRwOp::SeqCst, AtomicReadOp::SeqCst)?,
894             #[rustfmt::skip]
895             "atomic_cxchg_acq" =>
896                 this.atomic_compare_exchange(args, dest, AtomicRwOp::Acquire, AtomicReadOp::Acquire)?,
897             #[rustfmt::skip]
898             "atomic_cxchg_rel" =>
899                 this.atomic_compare_exchange(args, dest, AtomicRwOp::Release, AtomicReadOp::Relaxed)?,
900             #[rustfmt::skip]
901             "atomic_cxchg_acqrel" =>
902                 this.atomic_compare_exchange(args, dest, AtomicRwOp::AcqRel, AtomicReadOp::Acquire)?,
903             #[rustfmt::skip]
904             "atomic_cxchg_relaxed" =>
905                 this.atomic_compare_exchange(args, dest, AtomicRwOp::Relaxed, AtomicReadOp::Relaxed)?,
906             #[rustfmt::skip]
907             "atomic_cxchg_acq_failrelaxed" =>
908                 this.atomic_compare_exchange(args, dest, AtomicRwOp::Acquire, AtomicReadOp::Relaxed)?,
909             #[rustfmt::skip]
910             "atomic_cxchg_acqrel_failrelaxed" =>
911                 this.atomic_compare_exchange(args, dest, AtomicRwOp::AcqRel, AtomicReadOp::Relaxed)?,
912             #[rustfmt::skip]
913             "atomic_cxchg_failrelaxed" =>
914                 this.atomic_compare_exchange(args, dest, AtomicRwOp::SeqCst, AtomicReadOp::Relaxed)?,
915             #[rustfmt::skip]
916             "atomic_cxchg_failacq" =>
917                 this.atomic_compare_exchange(args, dest, AtomicRwOp::SeqCst, AtomicReadOp::Acquire)?,
918
919             #[rustfmt::skip]
920             "atomic_cxchgweak" =>
921                 this.atomic_compare_exchange_weak(args, dest, AtomicRwOp::SeqCst, AtomicReadOp::SeqCst)?,
922             #[rustfmt::skip]
923             "atomic_cxchgweak_acq" =>
924                 this.atomic_compare_exchange_weak(args, dest, AtomicRwOp::Acquire, AtomicReadOp::Acquire)?,
925             #[rustfmt::skip]
926             "atomic_cxchgweak_rel" =>
927                 this.atomic_compare_exchange_weak(args, dest, AtomicRwOp::Release, AtomicReadOp::Relaxed)?,
928             #[rustfmt::skip]
929             "atomic_cxchgweak_acqrel" =>
930                 this.atomic_compare_exchange_weak(args, dest, AtomicRwOp::AcqRel, AtomicReadOp::Acquire)?,
931             #[rustfmt::skip]
932             "atomic_cxchgweak_relaxed" =>
933                 this.atomic_compare_exchange_weak(args, dest, AtomicRwOp::Relaxed, AtomicReadOp::Relaxed)?,
934             #[rustfmt::skip]
935             "atomic_cxchgweak_acq_failrelaxed" =>
936                 this.atomic_compare_exchange_weak(args, dest, AtomicRwOp::Acquire, AtomicReadOp::Relaxed)?,
937             #[rustfmt::skip]
938             "atomic_cxchgweak_acqrel_failrelaxed" =>
939                 this.atomic_compare_exchange_weak(args, dest, AtomicRwOp::AcqRel, AtomicReadOp::Relaxed)?,
940             #[rustfmt::skip]
941             "atomic_cxchgweak_failrelaxed" =>
942                 this.atomic_compare_exchange_weak(args, dest, AtomicRwOp::SeqCst, AtomicReadOp::Relaxed)?,
943             #[rustfmt::skip]
944             "atomic_cxchgweak_failacq" =>
945                 this.atomic_compare_exchange_weak(args, dest, AtomicRwOp::SeqCst, AtomicReadOp::Acquire)?,
946
947             #[rustfmt::skip]
948             "atomic_or" =>
949                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitOr, false), AtomicRwOp::SeqCst)?,
950             #[rustfmt::skip]
951             "atomic_or_acq" =>
952                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitOr, false), AtomicRwOp::Acquire)?,
953             #[rustfmt::skip]
954             "atomic_or_rel" =>
955                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitOr, false), AtomicRwOp::Release)?,
956             #[rustfmt::skip]
957             "atomic_or_acqrel" =>
958                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitOr, false), AtomicRwOp::AcqRel)?,
959             #[rustfmt::skip]
960             "atomic_or_relaxed" =>
961                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitOr, false), AtomicRwOp::Relaxed)?,
962             #[rustfmt::skip]
963             "atomic_xor" =>
964                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitXor, false), AtomicRwOp::SeqCst)?,
965             #[rustfmt::skip]
966             "atomic_xor_acq" =>
967                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitXor, false), AtomicRwOp::Acquire)?,
968             #[rustfmt::skip]
969             "atomic_xor_rel" =>
970                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitXor, false), AtomicRwOp::Release)?,
971             #[rustfmt::skip]
972             "atomic_xor_acqrel" =>
973                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitXor, false), AtomicRwOp::AcqRel)?,
974             #[rustfmt::skip]
975             "atomic_xor_relaxed" =>
976                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitXor, false), AtomicRwOp::Relaxed)?,
977             #[rustfmt::skip]
978             "atomic_and" =>
979                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, false), AtomicRwOp::SeqCst)?,
980             #[rustfmt::skip]
981             "atomic_and_acq" =>
982                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, false), AtomicRwOp::Acquire)?,
983             #[rustfmt::skip]
984             "atomic_and_rel" =>
985                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, false), AtomicRwOp::Release)?,
986             #[rustfmt::skip]
987             "atomic_and_acqrel" =>
988                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, false), AtomicRwOp::AcqRel)?,
989             #[rustfmt::skip]
990             "atomic_and_relaxed" =>
991                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, false), AtomicRwOp::Relaxed)?,
992             #[rustfmt::skip]
993             "atomic_nand" =>
994                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, true), AtomicRwOp::SeqCst)?,
995             #[rustfmt::skip]
996             "atomic_nand_acq" =>
997                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, true), AtomicRwOp::Acquire)?,
998             #[rustfmt::skip]
999             "atomic_nand_rel" =>
1000                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, true), AtomicRwOp::Release)?,
1001             #[rustfmt::skip]
1002             "atomic_nand_acqrel" =>
1003                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, true), AtomicRwOp::AcqRel)?,
1004             #[rustfmt::skip]
1005             "atomic_nand_relaxed" =>
1006                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, true), AtomicRwOp::Relaxed)?,
1007             #[rustfmt::skip]
1008             "atomic_xadd" =>
1009                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::Add, false), AtomicRwOp::SeqCst)?,
1010             #[rustfmt::skip]
1011             "atomic_xadd_acq" =>
1012                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::Add, false), AtomicRwOp::Acquire)?,
1013             #[rustfmt::skip]
1014             "atomic_xadd_rel" =>
1015                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::Add, false), AtomicRwOp::Release)?,
1016             #[rustfmt::skip]
1017             "atomic_xadd_acqrel" =>
1018                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::Add, false), AtomicRwOp::AcqRel)?,
1019             #[rustfmt::skip]
1020             "atomic_xadd_relaxed" =>
1021                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::Add, false), AtomicRwOp::Relaxed)?,
1022             #[rustfmt::skip]
1023             "atomic_xsub" =>
1024                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::Sub, false), AtomicRwOp::SeqCst)?,
1025             #[rustfmt::skip]
1026             "atomic_xsub_acq" =>
1027                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::Sub, false), AtomicRwOp::Acquire)?,
1028             #[rustfmt::skip]
1029             "atomic_xsub_rel" =>
1030                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::Sub, false), AtomicRwOp::Release)?,
1031             #[rustfmt::skip]
1032             "atomic_xsub_acqrel" =>
1033                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::Sub, false), AtomicRwOp::AcqRel)?,
1034             #[rustfmt::skip]
1035             "atomic_xsub_relaxed" =>
1036                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::Sub, false), AtomicRwOp::Relaxed)?,
1037             "atomic_min" => this.atomic_op(args, dest, AtomicOp::Min, AtomicRwOp::SeqCst)?,
1038             "atomic_min_acq" => this.atomic_op(args, dest, AtomicOp::Min, AtomicRwOp::Acquire)?,
1039             "atomic_min_rel" => this.atomic_op(args, dest, AtomicOp::Min, AtomicRwOp::Release)?,
1040             "atomic_min_acqrel" => this.atomic_op(args, dest, AtomicOp::Min, AtomicRwOp::AcqRel)?,
1041             "atomic_min_relaxed" =>
1042                 this.atomic_op(args, dest, AtomicOp::Min, AtomicRwOp::Relaxed)?,
1043             "atomic_max" => this.atomic_op(args, dest, AtomicOp::Max, AtomicRwOp::SeqCst)?,
1044             "atomic_max_acq" => this.atomic_op(args, dest, AtomicOp::Max, AtomicRwOp::Acquire)?,
1045             "atomic_max_rel" => this.atomic_op(args, dest, AtomicOp::Max, AtomicRwOp::Release)?,
1046             "atomic_max_acqrel" => this.atomic_op(args, dest, AtomicOp::Max, AtomicRwOp::AcqRel)?,
1047             "atomic_max_relaxed" =>
1048                 this.atomic_op(args, dest, AtomicOp::Max, AtomicRwOp::Relaxed)?,
1049             "atomic_umin" => this.atomic_op(args, dest, AtomicOp::Min, AtomicRwOp::SeqCst)?,
1050             "atomic_umin_acq" => this.atomic_op(args, dest, AtomicOp::Min, AtomicRwOp::Acquire)?,
1051             "atomic_umin_rel" => this.atomic_op(args, dest, AtomicOp::Min, AtomicRwOp::Release)?,
1052             "atomic_umin_acqrel" =>
1053                 this.atomic_op(args, dest, AtomicOp::Min, AtomicRwOp::AcqRel)?,
1054             "atomic_umin_relaxed" =>
1055                 this.atomic_op(args, dest, AtomicOp::Min, AtomicRwOp::Relaxed)?,
1056             "atomic_umax" => this.atomic_op(args, dest, AtomicOp::Max, AtomicRwOp::SeqCst)?,
1057             "atomic_umax_acq" => this.atomic_op(args, dest, AtomicOp::Max, AtomicRwOp::Acquire)?,
1058             "atomic_umax_rel" => this.atomic_op(args, dest, AtomicOp::Max, AtomicRwOp::Release)?,
1059             "atomic_umax_acqrel" =>
1060                 this.atomic_op(args, dest, AtomicOp::Max, AtomicRwOp::AcqRel)?,
1061             "atomic_umax_relaxed" =>
1062                 this.atomic_op(args, dest, AtomicOp::Max, AtomicRwOp::Relaxed)?,
1063
1064             // Other
1065             "exact_div" => {
1066                 let &[ref num, ref denom] = check_arg_count(args)?;
1067                 this.exact_div(&this.read_immediate(num)?, &this.read_immediate(denom)?, dest)?;
1068             }
1069
1070             "try" => return this.handle_try(args, dest, ret),
1071
1072             "breakpoint" => {
1073                 let &[] = check_arg_count(args)?;
1074                 // normally this would raise a SIGTRAP, which aborts if no debugger is connected
1075                 throw_machine_stop!(TerminationInfo::Abort("Trace/breakpoint trap".to_string()))
1076             }
1077
1078             name => throw_unsup_format!("unimplemented intrinsic: {}", name),
1079         }
1080
1081         trace!("{:?}", this.dump_place(**dest));
1082         this.go_to_block(ret);
1083         Ok(())
1084     }
1085
1086     fn atomic_load(
1087         &mut self,
1088         args: &[OpTy<'tcx, Tag>],
1089         dest: &PlaceTy<'tcx, Tag>,
1090         atomic: AtomicReadOp,
1091     ) -> InterpResult<'tcx> {
1092         let this = self.eval_context_mut();
1093
1094         let &[ref place] = check_arg_count(args)?;
1095         let place = this.deref_operand(place)?;
1096
1097         // make sure it fits into a scalar; otherwise it cannot be atomic
1098         let val = this.read_scalar_atomic(&place, atomic)?;
1099
1100         // Check alignment requirements. Atomics must always be aligned to their size,
1101         // even if the type they wrap would be less aligned (e.g. AtomicU64 on 32bit must
1102         // be 8-aligned).
1103         let align = Align::from_bytes(place.layout.size.bytes()).unwrap();
1104         this.check_ptr_access_align(
1105             place.ptr,
1106             place.layout.size,
1107             align,
1108             CheckInAllocMsg::MemoryAccessTest,
1109         )?;
1110         // Perform regular access.
1111         this.write_scalar(val, dest)?;
1112         Ok(())
1113     }
1114
1115     fn atomic_store(
1116         &mut self,
1117         args: &[OpTy<'tcx, Tag>],
1118         atomic: AtomicWriteOp,
1119     ) -> InterpResult<'tcx> {
1120         let this = self.eval_context_mut();
1121
1122         let &[ref place, ref val] = check_arg_count(args)?;
1123         let place = this.deref_operand(place)?;
1124         let val = this.read_scalar(val)?; // make sure it fits into a scalar; otherwise it cannot be atomic
1125
1126         // Check alignment requirements. Atomics must always be aligned to their size,
1127         // even if the type they wrap would be less aligned (e.g. AtomicU64 on 32bit must
1128         // be 8-aligned).
1129         let align = Align::from_bytes(place.layout.size.bytes()).unwrap();
1130         this.check_ptr_access_align(
1131             place.ptr,
1132             place.layout.size,
1133             align,
1134             CheckInAllocMsg::MemoryAccessTest,
1135         )?;
1136
1137         // Perform atomic store
1138         this.write_scalar_atomic(val, &place, atomic)?;
1139         Ok(())
1140     }
1141
1142     fn compiler_fence(
1143         &mut self,
1144         args: &[OpTy<'tcx, Tag>],
1145         atomic: AtomicFenceOp,
1146     ) -> InterpResult<'tcx> {
1147         let &[] = check_arg_count(args)?;
1148         let _ = atomic;
1149         //FIXME: compiler fences are currently ignored
1150         Ok(())
1151     }
1152
1153     fn atomic_fence(
1154         &mut self,
1155         args: &[OpTy<'tcx, Tag>],
1156         atomic: AtomicFenceOp,
1157     ) -> InterpResult<'tcx> {
1158         let this = self.eval_context_mut();
1159         let &[] = check_arg_count(args)?;
1160         this.validate_atomic_fence(atomic)?;
1161         Ok(())
1162     }
1163
1164     fn atomic_op(
1165         &mut self,
1166         args: &[OpTy<'tcx, Tag>],
1167         dest: &PlaceTy<'tcx, Tag>,
1168         atomic_op: AtomicOp,
1169         atomic: AtomicRwOp,
1170     ) -> InterpResult<'tcx> {
1171         let this = self.eval_context_mut();
1172
1173         let &[ref place, ref rhs] = check_arg_count(args)?;
1174         let place = this.deref_operand(place)?;
1175
1176         if !place.layout.ty.is_integral() {
1177             bug!("Atomic arithmetic operations only work on integer types");
1178         }
1179         let rhs = this.read_immediate(rhs)?;
1180
1181         // Check alignment requirements. Atomics must always be aligned to their size,
1182         // even if the type they wrap would be less aligned (e.g. AtomicU64 on 32bit must
1183         // be 8-aligned).
1184         let align = Align::from_bytes(place.layout.size.bytes()).unwrap();
1185         this.check_ptr_access_align(
1186             place.ptr,
1187             place.layout.size,
1188             align,
1189             CheckInAllocMsg::MemoryAccessTest,
1190         )?;
1191
1192         match atomic_op {
1193             AtomicOp::Min => {
1194                 let old = this.atomic_min_max_scalar(&place, rhs, true, atomic)?;
1195                 this.write_immediate(*old, &dest)?; // old value is returned
1196                 Ok(())
1197             }
1198             AtomicOp::Max => {
1199                 let old = this.atomic_min_max_scalar(&place, rhs, false, atomic)?;
1200                 this.write_immediate(*old, &dest)?; // old value is returned
1201                 Ok(())
1202             }
1203             AtomicOp::MirOp(op, neg) => {
1204                 let old = this.atomic_op_immediate(&place, &rhs, op, neg, atomic)?;
1205                 this.write_immediate(*old, dest)?; // old value is returned
1206                 Ok(())
1207             }
1208         }
1209     }
1210
1211     fn atomic_exchange(
1212         &mut self,
1213         args: &[OpTy<'tcx, Tag>],
1214         dest: &PlaceTy<'tcx, Tag>,
1215         atomic: AtomicRwOp,
1216     ) -> InterpResult<'tcx> {
1217         let this = self.eval_context_mut();
1218
1219         let &[ref place, ref new] = check_arg_count(args)?;
1220         let place = this.deref_operand(place)?;
1221         let new = this.read_scalar(new)?;
1222
1223         // Check alignment requirements. Atomics must always be aligned to their size,
1224         // even if the type they wrap would be less aligned (e.g. AtomicU64 on 32bit must
1225         // be 8-aligned).
1226         let align = Align::from_bytes(place.layout.size.bytes()).unwrap();
1227         this.check_ptr_access_align(
1228             place.ptr,
1229             place.layout.size,
1230             align,
1231             CheckInAllocMsg::MemoryAccessTest,
1232         )?;
1233
1234         let old = this.atomic_exchange_scalar(&place, new, atomic)?;
1235         this.write_scalar(old, dest)?; // old value is returned
1236         Ok(())
1237     }
1238
1239     fn atomic_compare_exchange_impl(
1240         &mut self,
1241         args: &[OpTy<'tcx, Tag>],
1242         dest: &PlaceTy<'tcx, Tag>,
1243         success: AtomicRwOp,
1244         fail: AtomicReadOp,
1245         can_fail_spuriously: bool,
1246     ) -> InterpResult<'tcx> {
1247         let this = self.eval_context_mut();
1248
1249         let &[ref place, ref expect_old, ref new] = check_arg_count(args)?;
1250         let place = this.deref_operand(place)?;
1251         let expect_old = this.read_immediate(expect_old)?; // read as immediate for the sake of `binary_op()`
1252         let new = this.read_scalar(new)?;
1253
1254         // Check alignment requirements. Atomics must always be aligned to their size,
1255         // even if the type they wrap would be less aligned (e.g. AtomicU64 on 32bit must
1256         // be 8-aligned).
1257         let align = Align::from_bytes(place.layout.size.bytes()).unwrap();
1258         this.check_ptr_access_align(
1259             place.ptr,
1260             place.layout.size,
1261             align,
1262             CheckInAllocMsg::MemoryAccessTest,
1263         )?;
1264
1265         let old = this.atomic_compare_exchange_scalar(
1266             &place,
1267             &expect_old,
1268             new,
1269             success,
1270             fail,
1271             can_fail_spuriously,
1272         )?;
1273
1274         // Return old value.
1275         this.write_immediate(old, dest)?;
1276         Ok(())
1277     }
1278
1279     fn atomic_compare_exchange(
1280         &mut self,
1281         args: &[OpTy<'tcx, Tag>],
1282         dest: &PlaceTy<'tcx, Tag>,
1283         success: AtomicRwOp,
1284         fail: AtomicReadOp,
1285     ) -> InterpResult<'tcx> {
1286         self.atomic_compare_exchange_impl(args, dest, success, fail, false)
1287     }
1288
1289     fn atomic_compare_exchange_weak(
1290         &mut self,
1291         args: &[OpTy<'tcx, Tag>],
1292         dest: &PlaceTy<'tcx, Tag>,
1293         success: AtomicRwOp,
1294         fail: AtomicReadOp,
1295     ) -> InterpResult<'tcx> {
1296         self.atomic_compare_exchange_impl(args, dest, success, fail, true)
1297     }
1298
1299     fn float_to_int_unchecked<F>(
1300         &self,
1301         f: F,
1302         dest_ty: ty::Ty<'tcx>,
1303     ) -> InterpResult<'tcx, Scalar<Tag>>
1304     where
1305         F: Float + Into<Scalar<Tag>>,
1306     {
1307         let this = self.eval_context_ref();
1308
1309         // Step 1: cut off the fractional part of `f`. The result of this is
1310         // guaranteed to be precisely representable in IEEE floats.
1311         let f = f.round_to_integral(Round::TowardZero).value;
1312
1313         // Step 2: Cast the truncated float to the target integer type and see if we lose any information in this step.
1314         Ok(match dest_ty.kind() {
1315             // Unsigned
1316             ty::Uint(t) => {
1317                 let size = Integer::from_uint_ty(this, *t).size();
1318                 let res = f.to_u128(size.bits_usize());
1319                 if res.status.is_empty() {
1320                     // No status flags means there was no further rounding or other loss of precision.
1321                     Scalar::from_uint(res.value, size)
1322                 } else {
1323                     // `f` was not representable in this integer type.
1324                     throw_ub_format!(
1325                         "`float_to_int_unchecked` intrinsic called on {} which cannot be represented in target type `{:?}`",
1326                         f,
1327                         dest_ty,
1328                     );
1329                 }
1330             }
1331             // Signed
1332             ty::Int(t) => {
1333                 let size = Integer::from_int_ty(this, *t).size();
1334                 let res = f.to_i128(size.bits_usize());
1335                 if res.status.is_empty() {
1336                     // No status flags means there was no further rounding or other loss of precision.
1337                     Scalar::from_int(res.value, size)
1338                 } else {
1339                     // `f` was not representable in this integer type.
1340                     throw_ub_format!(
1341                         "`float_to_int_unchecked` intrinsic called on {} which cannot be represented in target type `{:?}`",
1342                         f,
1343                         dest_ty,
1344                     );
1345                 }
1346             }
1347             // Nothing else
1348             _ => bug!("`float_to_int_unchecked` called with non-int output type {:?}", dest_ty),
1349         })
1350     }
1351 }
1352
1353 fn fmax_op<'tcx>(
1354     left: &ImmTy<'tcx, Tag>,
1355     right: &ImmTy<'tcx, Tag>,
1356 ) -> InterpResult<'tcx, Scalar<Tag>> {
1357     assert_eq!(left.layout.ty, right.layout.ty);
1358     let ty::Float(float_ty) = left.layout.ty.kind() else {
1359         bug!("fmax operand is not a float")
1360     };
1361     let left = left.to_scalar()?;
1362     let right = right.to_scalar()?;
1363     Ok(match float_ty {
1364         FloatTy::F32 => Scalar::from_f32(left.to_f32()?.max(right.to_f32()?)),
1365         FloatTy::F64 => Scalar::from_f64(left.to_f64()?.max(right.to_f64()?)),
1366     })
1367 }
1368
1369 fn fmin_op<'tcx>(
1370     left: &ImmTy<'tcx, Tag>,
1371     right: &ImmTy<'tcx, Tag>,
1372 ) -> InterpResult<'tcx, Scalar<Tag>> {
1373     assert_eq!(left.layout.ty, right.layout.ty);
1374     let ty::Float(float_ty) = left.layout.ty.kind() else {
1375         bug!("fmin operand is not a float")
1376     };
1377     let left = left.to_scalar()?;
1378     let right = right.to_scalar()?;
1379     Ok(match float_ty {
1380         FloatTy::F32 => Scalar::from_f32(left.to_f32()?.min(right.to_f32()?)),
1381         FloatTy::F64 => Scalar::from_f64(left.to_f64()?.min(right.to_f64()?)),
1382     })
1383 }
1384
1385 fn bool_to_simd_element(b: bool, size: Size) -> Scalar<Tag> {
1386     // SIMD uses all-1 as pattern for "true"
1387     let val = if b { -1 } else { 0 };
1388     Scalar::from_int(val, size)
1389 }
1390
1391 fn simd_element_to_bool<'tcx>(elem: ImmTy<'tcx, Tag>) -> InterpResult<'tcx, bool> {
1392     let val = elem.to_scalar()?.to_int(elem.layout.size)?;
1393     Ok(match val {
1394         0 => false,
1395         -1 => true,
1396         _ => throw_ub_format!("each element of a SIMD mask must be all-0-bits or all-1-bits"),
1397     })
1398 }
1399
1400 fn simd_bitmask_index(idx: u64, vec_len: u64, endianess: Endian) -> u64 {
1401     assert!(idx < vec_len);
1402     match endianess {
1403         Endian::Little => idx,
1404         Endian::Big => vec_len - 1 - idx, // reverse order of bits
1405     }
1406 }