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