]> git.lizzy.rs Git - rust.git/blob - src/shims/intrinsics.rs
implement SIMD float rounding functions
[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             | "simd_ceil"
330             | "simd_floor"
331             | "simd_round"
332             | "simd_trunc" => {
333                 let &[ref op] = check_arg_count(args)?;
334                 let (op, op_len) = this.operand_to_simd(op)?;
335                 let (dest, dest_len) = this.place_to_simd(dest)?;
336
337                 assert_eq!(dest_len, op_len);
338
339                 #[derive(Copy, Clone)]
340                 enum HostFloatOp {
341                     Ceil,
342                     Floor,
343                     Round,
344                     Trunc,
345                 }
346                 #[derive(Copy, Clone)]
347                 enum Op {
348                     MirOp(mir::UnOp),
349                     Abs,
350                     HostOp(HostFloatOp),
351                 }
352                 let which = match intrinsic_name {
353                     "simd_neg" => Op::MirOp(mir::UnOp::Neg),
354                     "simd_fabs" => Op::Abs,
355                     "simd_ceil" => Op::HostOp(HostFloatOp::Ceil),
356                     "simd_floor" => Op::HostOp(HostFloatOp::Floor),
357                     "simd_round" => Op::HostOp(HostFloatOp::Round),
358                     "simd_trunc" => Op::HostOp(HostFloatOp::Trunc),
359                     _ => unreachable!(),
360                 };
361
362                 for i in 0..dest_len {
363                     let op = this.read_immediate(&this.mplace_index(&op, i)?.into())?;
364                     let dest = this.mplace_index(&dest, i)?;
365                     let val = match which {
366                         Op::MirOp(mir_op) => this.unary_op(mir_op, &op)?.to_scalar()?,
367                         Op::Abs => {
368                             // Works for f32 and f64.
369                             let ty::Float(float_ty) = op.layout.ty.kind() else {
370                                 bug!("{} operand is not a float", intrinsic_name)
371                             };
372                             let op = op.to_scalar()?;
373                             match float_ty {
374                                 FloatTy::F32 => Scalar::from_f32(op.to_f32()?.abs()),
375                                 FloatTy::F64 => Scalar::from_f64(op.to_f64()?.abs()),
376                             }
377                         }
378                         Op::HostOp(host_op) => {
379                             let ty::Float(float_ty) = op.layout.ty.kind() else {
380                                 bug!("{} operand is not a float", intrinsic_name)
381                             };
382                             // FIXME using host floats
383                             match float_ty {
384                                 FloatTy::F32 => {
385                                     let f = f32::from_bits(op.to_scalar()?.to_u32()?);
386                                     let res = match host_op {
387                                         HostFloatOp::Ceil => f.ceil(),
388                                         HostFloatOp::Floor => f.floor(),
389                                         HostFloatOp::Round => f.round(),
390                                         HostFloatOp::Trunc => f.trunc(),
391                                     };
392                                     Scalar::from_u32(res.to_bits())
393                                 }
394                                 FloatTy::F64 => {
395                                     let f = f64::from_bits(op.to_scalar()?.to_u64()?);
396                                     let res = match host_op {
397                                         HostFloatOp::Ceil => f.ceil(),
398                                         HostFloatOp::Floor => f.floor(),
399                                         HostFloatOp::Round => f.round(),
400                                         HostFloatOp::Trunc => f.trunc(),
401                                     };
402                                     Scalar::from_u64(res.to_bits())
403                                 }
404                             }
405
406                         }
407                     };
408                     this.write_scalar(val, &dest.into())?;
409                 }
410             }
411             #[rustfmt::skip]
412             | "simd_add"
413             | "simd_sub"
414             | "simd_mul"
415             | "simd_div"
416             | "simd_rem"
417             | "simd_shl"
418             | "simd_shr"
419             | "simd_and"
420             | "simd_or"
421             | "simd_xor"
422             | "simd_eq"
423             | "simd_ne"
424             | "simd_lt"
425             | "simd_le"
426             | "simd_gt"
427             | "simd_ge"
428             | "simd_fmax"
429             | "simd_fmin"
430             | "simd_saturating_add"
431             | "simd_saturating_sub" => {
432                 use mir::BinOp;
433
434                 let &[ref left, ref right] = check_arg_count(args)?;
435                 let (left, left_len) = this.operand_to_simd(left)?;
436                 let (right, right_len) = this.operand_to_simd(right)?;
437                 let (dest, dest_len) = this.place_to_simd(dest)?;
438
439                 assert_eq!(dest_len, left_len);
440                 assert_eq!(dest_len, right_len);
441
442                 enum Op {
443                     MirOp(BinOp),
444                     SaturatingOp(BinOp),
445                     FMax,
446                     FMin,
447                 }
448                 let which = match intrinsic_name {
449                     "simd_add" => Op::MirOp(BinOp::Add),
450                     "simd_sub" => Op::MirOp(BinOp::Sub),
451                     "simd_mul" => Op::MirOp(BinOp::Mul),
452                     "simd_div" => Op::MirOp(BinOp::Div),
453                     "simd_rem" => Op::MirOp(BinOp::Rem),
454                     "simd_shl" => Op::MirOp(BinOp::Shl),
455                     "simd_shr" => Op::MirOp(BinOp::Shr),
456                     "simd_and" => Op::MirOp(BinOp::BitAnd),
457                     "simd_or" => Op::MirOp(BinOp::BitOr),
458                     "simd_xor" => Op::MirOp(BinOp::BitXor),
459                     "simd_eq" => Op::MirOp(BinOp::Eq),
460                     "simd_ne" => Op::MirOp(BinOp::Ne),
461                     "simd_lt" => Op::MirOp(BinOp::Lt),
462                     "simd_le" => Op::MirOp(BinOp::Le),
463                     "simd_gt" => Op::MirOp(BinOp::Gt),
464                     "simd_ge" => Op::MirOp(BinOp::Ge),
465                     "simd_fmax" => Op::FMax,
466                     "simd_fmin" => Op::FMin,
467                     "simd_saturating_add" => Op::SaturatingOp(BinOp::Add),
468                     "simd_saturating_sub" => Op::SaturatingOp(BinOp::Sub),
469                     _ => unreachable!(),
470                 };
471
472                 for i in 0..dest_len {
473                     let left = this.read_immediate(&this.mplace_index(&left, i)?.into())?;
474                     let right = this.read_immediate(&this.mplace_index(&right, i)?.into())?;
475                     let dest = this.mplace_index(&dest, i)?;
476                     let val = match which {
477                         Op::MirOp(mir_op) => {
478                             let (val, overflowed, ty) = this.overflowing_binary_op(mir_op, &left, &right)?;
479                             if matches!(mir_op, BinOp::Shl | BinOp::Shr) {
480                                 // Shifts have extra UB as SIMD operations that the MIR binop does not have.
481                                 // See <https://github.com/rust-lang/rust/issues/91237>.
482                                 if overflowed {
483                                     let r_val = right.to_scalar()?.to_bits(right.layout.size)?;
484                                     throw_ub_format!("overflowing shift by {} in `{}` in SIMD lane {}", r_val, intrinsic_name, i);
485                                 }
486                             }
487                             if matches!(mir_op, BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge) {
488                                 // Special handling for boolean-returning operations
489                                 assert_eq!(ty, this.tcx.types.bool);
490                                 let val = val.to_bool().unwrap();
491                                 bool_to_simd_element(val, dest.layout.size)
492                             } else {
493                                 assert_ne!(ty, this.tcx.types.bool);
494                                 assert_eq!(ty, dest.layout.ty);
495                                 val
496                             }
497                         }
498                         Op::FMax => {
499                             fmax_op(&left, &right)?
500                         }
501                         Op::FMin => {
502                             fmin_op(&left, &right)?
503                         }
504                         Op::SaturatingOp(mir_op) => {
505                             this.saturating_arith(mir_op, &left, &right)?
506                         }
507                     };
508                     this.write_scalar(val, &dest.into())?;
509                 }
510             }
511             #[rustfmt::skip]
512             | "simd_reduce_and"
513             | "simd_reduce_or"
514             | "simd_reduce_xor"
515             | "simd_reduce_any"
516             | "simd_reduce_all"
517             | "simd_reduce_max"
518             | "simd_reduce_min" => {
519                 use mir::BinOp;
520
521                 let &[ref op] = check_arg_count(args)?;
522                 let (op, op_len) = this.operand_to_simd(op)?;
523
524                 let imm_from_bool =
525                     |b| ImmTy::from_scalar(Scalar::from_bool(b), this.machine.layouts.bool);
526
527                 enum Op {
528                     MirOp(BinOp),
529                     MirOpBool(BinOp),
530                     Max,
531                     Min,
532                 }
533                 let which = match intrinsic_name {
534                     "simd_reduce_and" => Op::MirOp(BinOp::BitAnd),
535                     "simd_reduce_or" => Op::MirOp(BinOp::BitOr),
536                     "simd_reduce_xor" => Op::MirOp(BinOp::BitXor),
537                     "simd_reduce_any" => Op::MirOpBool(BinOp::BitOr),
538                     "simd_reduce_all" => Op::MirOpBool(BinOp::BitAnd),
539                     "simd_reduce_max" => Op::Max,
540                     "simd_reduce_min" => Op::Min,
541                     _ => unreachable!(),
542                 };
543
544                 // Initialize with first lane, then proceed with the rest.
545                 let mut res = this.read_immediate(&this.mplace_index(&op, 0)?.into())?;
546                 if matches!(which, Op::MirOpBool(_)) {
547                     // Convert to `bool` scalar.
548                     res = imm_from_bool(simd_element_to_bool(res)?);
549                 }
550                 for i in 1..op_len {
551                     let op = this.read_immediate(&this.mplace_index(&op, i)?.into())?;
552                     res = match which {
553                         Op::MirOp(mir_op) => {
554                             this.binary_op(mir_op, &res, &op)?
555                         }
556                         Op::MirOpBool(mir_op) => {
557                             let op = imm_from_bool(simd_element_to_bool(op)?);
558                             this.binary_op(mir_op, &res, &op)?
559                         }
560                         Op::Max => {
561                             if matches!(res.layout.ty.kind(), ty::Float(_)) {
562                                 ImmTy::from_scalar(fmax_op(&res, &op)?, res.layout)
563                             } else {
564                                 // Just boring integers, so NaNs to worry about
565                                 if this.binary_op(BinOp::Ge, &res, &op)?.to_scalar()?.to_bool()? {
566                                     res
567                                 } else {
568                                     op
569                                 }
570                             }
571                         }
572                         Op::Min => {
573                             if matches!(res.layout.ty.kind(), ty::Float(_)) {
574                                 ImmTy::from_scalar(fmin_op(&res, &op)?, res.layout)
575                             } else {
576                                 // Just boring integers, so NaNs to worry about
577                                 if this.binary_op(BinOp::Le, &res, &op)?.to_scalar()?.to_bool()? {
578                                     res
579                                 } else {
580                                     op
581                                 }
582                             }
583                         }
584                     };
585                 }
586                 this.write_immediate(*res, dest)?;
587             }
588             #[rustfmt::skip]
589             | "simd_reduce_add_ordered"
590             | "simd_reduce_mul_ordered" => {
591                 use mir::BinOp;
592
593                 let &[ref op, ref init] = check_arg_count(args)?;
594                 let (op, op_len) = this.operand_to_simd(op)?;
595                 let init = this.read_immediate(init)?;
596
597                 let mir_op = match intrinsic_name {
598                     "simd_reduce_add_ordered" => BinOp::Add,
599                     "simd_reduce_mul_ordered" => BinOp::Mul,
600                     _ => unreachable!(),
601                 };
602
603                 let mut res = init;
604                 for i in 0..op_len {
605                     let op = this.read_immediate(&this.mplace_index(&op, i)?.into())?;
606                     res = this.binary_op(mir_op, &res, &op)?;
607                 }
608                 this.write_immediate(*res, dest)?;
609             }
610             "simd_select" => {
611                 let &[ref mask, ref yes, ref no] = check_arg_count(args)?;
612                 let (mask, mask_len) = this.operand_to_simd(mask)?;
613                 let (yes, yes_len) = this.operand_to_simd(yes)?;
614                 let (no, no_len) = this.operand_to_simd(no)?;
615                 let (dest, dest_len) = this.place_to_simd(dest)?;
616
617                 assert_eq!(dest_len, mask_len);
618                 assert_eq!(dest_len, yes_len);
619                 assert_eq!(dest_len, no_len);
620
621                 for i in 0..dest_len {
622                     let mask = this.read_immediate(&this.mplace_index(&mask, i)?.into())?;
623                     let yes = this.read_immediate(&this.mplace_index(&yes, i)?.into())?;
624                     let no = this.read_immediate(&this.mplace_index(&no, i)?.into())?;
625                     let dest = this.mplace_index(&dest, i)?;
626
627                     let val = if simd_element_to_bool(mask)? { yes } else { no };
628                     this.write_immediate(*val, &dest.into())?;
629                 }
630             }
631             #[rustfmt::skip]
632             "simd_cast" | "simd_as" => {
633                 let &[ref op] = check_arg_count(args)?;
634                 let (op, op_len) = this.operand_to_simd(op)?;
635                 let (dest, dest_len) = this.place_to_simd(dest)?;
636
637                 assert_eq!(dest_len, op_len);
638
639                 let safe_cast = intrinsic_name == "simd_as";
640
641                 for i in 0..dest_len {
642                     let op = this.read_immediate(&this.mplace_index(&op, i)?.into())?;
643                     let dest = this.mplace_index(&dest, i)?;
644
645                     let val = match (op.layout.ty.kind(), dest.layout.ty.kind()) {
646                         // Int-to-(int|float): always safe
647                         (ty::Int(_) | ty::Uint(_), ty::Int(_) | ty::Uint(_) | ty::Float(_)) =>
648                             this.misc_cast(&op, dest.layout.ty)?,
649                         // Float-to-float: always safe
650                         (ty::Float(_), ty::Float(_)) =>
651                             this.misc_cast(&op, dest.layout.ty)?,
652                         // Float-to-int in safe mode
653                         (ty::Float(_), ty::Int(_) | ty::Uint(_)) if safe_cast =>
654                             this.misc_cast(&op, dest.layout.ty)?,
655                         // Float-to-int in unchecked mode
656                         (ty::Float(FloatTy::F32), ty::Int(_) | ty::Uint(_)) if !safe_cast =>
657                             this.float_to_int_unchecked(op.to_scalar()?.to_f32()?, dest.layout.ty)?.into(),
658                         (ty::Float(FloatTy::F64), ty::Int(_) | ty::Uint(_)) if !safe_cast =>
659                             this.float_to_int_unchecked(op.to_scalar()?.to_f64()?, dest.layout.ty)?.into(),
660                         _ =>
661                             throw_unsup_format!(
662                                 "Unsupported SIMD cast from element type {} to {}",
663                                 op.layout.ty,
664                                 dest.layout.ty
665                             ),
666                     };
667                     this.write_immediate(val, &dest.into())?;
668                 }
669             }
670             "simd_shuffle" => {
671                 let &[ref left, ref right, ref index] = check_arg_count(args)?;
672                 let (left, left_len) = this.operand_to_simd(left)?;
673                 let (right, right_len) = this.operand_to_simd(right)?;
674                 let (dest, dest_len) = this.place_to_simd(dest)?;
675
676                 // `index` is an array, not a SIMD type
677                 let ty::Array(_, index_len) = index.layout.ty.kind() else {
678                     bug!("simd_shuffle index argument has non-array type {}", index.layout.ty)
679                 };
680                 let index_len = index_len.eval_usize(*this.tcx, this.param_env());
681
682                 assert_eq!(left_len, right_len);
683                 assert_eq!(index_len, dest_len);
684
685                 for i in 0..dest_len {
686                     let src_index: u64 = this
687                         .read_immediate(&this.operand_index(&index, i)?.into())?
688                         .to_scalar()?
689                         .to_u32()?
690                         .into();
691                     let dest = this.mplace_index(&dest, i)?;
692
693                     let val = if src_index < left_len {
694                         this.read_immediate(&this.mplace_index(&left, src_index)?.into())?
695                     } else if src_index < left_len.checked_add(right_len).unwrap() {
696                         this.read_immediate(
697                             &this.mplace_index(&right, src_index - left_len)?.into(),
698                         )?
699                     } else {
700                         bug!(
701                             "simd_shuffle index {} is out of bounds for 2 vectors of size {}",
702                             src_index,
703                             left_len
704                         );
705                     };
706                     this.write_immediate(*val, &dest.into())?;
707                 }
708             }
709             "simd_gather" => {
710                 let &[ref passthru, ref ptrs, ref mask] = check_arg_count(args)?;
711                 let (passthru, passthru_len) = this.operand_to_simd(passthru)?;
712                 let (ptrs, ptrs_len) = this.operand_to_simd(ptrs)?;
713                 let (mask, mask_len) = this.operand_to_simd(mask)?;
714                 let (dest, dest_len) = this.place_to_simd(dest)?;
715
716                 assert_eq!(dest_len, passthru_len);
717                 assert_eq!(dest_len, ptrs_len);
718                 assert_eq!(dest_len, mask_len);
719
720                 for i in 0..dest_len {
721                     let passthru = this.read_immediate(&this.mplace_index(&passthru, i)?.into())?;
722                     let ptr = this.read_immediate(&this.mplace_index(&ptrs, i)?.into())?;
723                     let mask = this.read_immediate(&this.mplace_index(&mask, i)?.into())?;
724                     let dest = this.mplace_index(&dest, i)?;
725
726                     let val = if simd_element_to_bool(mask)? {
727                         let place = this.deref_operand(&ptr.into())?;
728                         this.read_immediate(&place.into())?
729                     } else {
730                         passthru
731                     };
732                     this.write_immediate(*val, &dest.into())?;
733                 }
734             }
735             "simd_scatter" => {
736                 let &[ref value, ref ptrs, ref mask] = check_arg_count(args)?;
737                 let (value, value_len) = this.operand_to_simd(value)?;
738                 let (ptrs, ptrs_len) = this.operand_to_simd(ptrs)?;
739                 let (mask, mask_len) = this.operand_to_simd(mask)?;
740
741                 assert_eq!(ptrs_len, value_len);
742                 assert_eq!(ptrs_len, mask_len);
743
744                 for i in 0..ptrs_len {
745                     let value = this.read_immediate(&this.mplace_index(&value, i)?.into())?;
746                     let ptr = this.read_immediate(&this.mplace_index(&ptrs, i)?.into())?;
747                     let mask = this.read_immediate(&this.mplace_index(&mask, i)?.into())?;
748
749                     if simd_element_to_bool(mask)? {
750                         let place = this.deref_operand(&ptr.into())?;
751                         this.write_immediate(*value, &place.into())?;
752                     }
753                 }
754             }
755
756             // Atomic operations
757             "atomic_load" => this.atomic_load(args, dest, AtomicReadOp::SeqCst)?,
758             "atomic_load_relaxed" => this.atomic_load(args, dest, AtomicReadOp::Relaxed)?,
759             "atomic_load_acq" => this.atomic_load(args, dest, AtomicReadOp::Acquire)?,
760
761             "atomic_store" => this.atomic_store(args, AtomicWriteOp::SeqCst)?,
762             "atomic_store_relaxed" => this.atomic_store(args, AtomicWriteOp::Relaxed)?,
763             "atomic_store_rel" => this.atomic_store(args, AtomicWriteOp::Release)?,
764
765             "atomic_fence_acq" => this.atomic_fence(args, AtomicFenceOp::Acquire)?,
766             "atomic_fence_rel" => this.atomic_fence(args, AtomicFenceOp::Release)?,
767             "atomic_fence_acqrel" => this.atomic_fence(args, AtomicFenceOp::AcqRel)?,
768             "atomic_fence" => this.atomic_fence(args, AtomicFenceOp::SeqCst)?,
769
770             "atomic_singlethreadfence_acq" => this.compiler_fence(args, AtomicFenceOp::Acquire)?,
771             "atomic_singlethreadfence_rel" => this.compiler_fence(args, AtomicFenceOp::Release)?,
772             "atomic_singlethreadfence_acqrel" =>
773                 this.compiler_fence(args, AtomicFenceOp::AcqRel)?,
774             "atomic_singlethreadfence" => this.compiler_fence(args, AtomicFenceOp::SeqCst)?,
775
776             "atomic_xchg" => this.atomic_exchange(args, dest, AtomicRwOp::SeqCst)?,
777             "atomic_xchg_acq" => this.atomic_exchange(args, dest, AtomicRwOp::Acquire)?,
778             "atomic_xchg_rel" => this.atomic_exchange(args, dest, AtomicRwOp::Release)?,
779             "atomic_xchg_acqrel" => this.atomic_exchange(args, dest, AtomicRwOp::AcqRel)?,
780             "atomic_xchg_relaxed" => this.atomic_exchange(args, dest, AtomicRwOp::Relaxed)?,
781
782             #[rustfmt::skip]
783             "atomic_cxchg" =>
784                 this.atomic_compare_exchange(args, dest, AtomicRwOp::SeqCst, AtomicReadOp::SeqCst)?,
785             #[rustfmt::skip]
786             "atomic_cxchg_acq" =>
787                 this.atomic_compare_exchange(args, dest, AtomicRwOp::Acquire, AtomicReadOp::Acquire)?,
788             #[rustfmt::skip]
789             "atomic_cxchg_rel" =>
790                 this.atomic_compare_exchange(args, dest, AtomicRwOp::Release, AtomicReadOp::Relaxed)?,
791             #[rustfmt::skip]
792             "atomic_cxchg_acqrel" =>
793                 this.atomic_compare_exchange(args, dest, AtomicRwOp::AcqRel, AtomicReadOp::Acquire)?,
794             #[rustfmt::skip]
795             "atomic_cxchg_relaxed" =>
796                 this.atomic_compare_exchange(args, dest, AtomicRwOp::Relaxed, AtomicReadOp::Relaxed)?,
797             #[rustfmt::skip]
798             "atomic_cxchg_acq_failrelaxed" =>
799                 this.atomic_compare_exchange(args, dest, AtomicRwOp::Acquire, AtomicReadOp::Relaxed)?,
800             #[rustfmt::skip]
801             "atomic_cxchg_acqrel_failrelaxed" =>
802                 this.atomic_compare_exchange(args, dest, AtomicRwOp::AcqRel, AtomicReadOp::Relaxed)?,
803             #[rustfmt::skip]
804             "atomic_cxchg_failrelaxed" =>
805                 this.atomic_compare_exchange(args, dest, AtomicRwOp::SeqCst, AtomicReadOp::Relaxed)?,
806             #[rustfmt::skip]
807             "atomic_cxchg_failacq" =>
808                 this.atomic_compare_exchange(args, dest, AtomicRwOp::SeqCst, AtomicReadOp::Acquire)?,
809
810             #[rustfmt::skip]
811             "atomic_cxchgweak" =>
812                 this.atomic_compare_exchange_weak(args, dest, AtomicRwOp::SeqCst, AtomicReadOp::SeqCst)?,
813             #[rustfmt::skip]
814             "atomic_cxchgweak_acq" =>
815                 this.atomic_compare_exchange_weak(args, dest, AtomicRwOp::Acquire, AtomicReadOp::Acquire)?,
816             #[rustfmt::skip]
817             "atomic_cxchgweak_rel" =>
818                 this.atomic_compare_exchange_weak(args, dest, AtomicRwOp::Release, AtomicReadOp::Relaxed)?,
819             #[rustfmt::skip]
820             "atomic_cxchgweak_acqrel" =>
821                 this.atomic_compare_exchange_weak(args, dest, AtomicRwOp::AcqRel, AtomicReadOp::Acquire)?,
822             #[rustfmt::skip]
823             "atomic_cxchgweak_relaxed" =>
824                 this.atomic_compare_exchange_weak(args, dest, AtomicRwOp::Relaxed, AtomicReadOp::Relaxed)?,
825             #[rustfmt::skip]
826             "atomic_cxchgweak_acq_failrelaxed" =>
827                 this.atomic_compare_exchange_weak(args, dest, AtomicRwOp::Acquire, AtomicReadOp::Relaxed)?,
828             #[rustfmt::skip]
829             "atomic_cxchgweak_acqrel_failrelaxed" =>
830                 this.atomic_compare_exchange_weak(args, dest, AtomicRwOp::AcqRel, AtomicReadOp::Relaxed)?,
831             #[rustfmt::skip]
832             "atomic_cxchgweak_failrelaxed" =>
833                 this.atomic_compare_exchange_weak(args, dest, AtomicRwOp::SeqCst, AtomicReadOp::Relaxed)?,
834             #[rustfmt::skip]
835             "atomic_cxchgweak_failacq" =>
836                 this.atomic_compare_exchange_weak(args, dest, AtomicRwOp::SeqCst, AtomicReadOp::Acquire)?,
837
838             #[rustfmt::skip]
839             "atomic_or" =>
840                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitOr, false), AtomicRwOp::SeqCst)?,
841             #[rustfmt::skip]
842             "atomic_or_acq" =>
843                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitOr, false), AtomicRwOp::Acquire)?,
844             #[rustfmt::skip]
845             "atomic_or_rel" =>
846                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitOr, false), AtomicRwOp::Release)?,
847             #[rustfmt::skip]
848             "atomic_or_acqrel" =>
849                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitOr, false), AtomicRwOp::AcqRel)?,
850             #[rustfmt::skip]
851             "atomic_or_relaxed" =>
852                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitOr, false), AtomicRwOp::Relaxed)?,
853             #[rustfmt::skip]
854             "atomic_xor" =>
855                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitXor, false), AtomicRwOp::SeqCst)?,
856             #[rustfmt::skip]
857             "atomic_xor_acq" =>
858                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitXor, false), AtomicRwOp::Acquire)?,
859             #[rustfmt::skip]
860             "atomic_xor_rel" =>
861                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitXor, false), AtomicRwOp::Release)?,
862             #[rustfmt::skip]
863             "atomic_xor_acqrel" =>
864                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitXor, false), AtomicRwOp::AcqRel)?,
865             #[rustfmt::skip]
866             "atomic_xor_relaxed" =>
867                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitXor, false), AtomicRwOp::Relaxed)?,
868             #[rustfmt::skip]
869             "atomic_and" =>
870                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, false), AtomicRwOp::SeqCst)?,
871             #[rustfmt::skip]
872             "atomic_and_acq" =>
873                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, false), AtomicRwOp::Acquire)?,
874             #[rustfmt::skip]
875             "atomic_and_rel" =>
876                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, false), AtomicRwOp::Release)?,
877             #[rustfmt::skip]
878             "atomic_and_acqrel" =>
879                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, false), AtomicRwOp::AcqRel)?,
880             #[rustfmt::skip]
881             "atomic_and_relaxed" =>
882                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, false), AtomicRwOp::Relaxed)?,
883             #[rustfmt::skip]
884             "atomic_nand" =>
885                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, true), AtomicRwOp::SeqCst)?,
886             #[rustfmt::skip]
887             "atomic_nand_acq" =>
888                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, true), AtomicRwOp::Acquire)?,
889             #[rustfmt::skip]
890             "atomic_nand_rel" =>
891                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, true), AtomicRwOp::Release)?,
892             #[rustfmt::skip]
893             "atomic_nand_acqrel" =>
894                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, true), AtomicRwOp::AcqRel)?,
895             #[rustfmt::skip]
896             "atomic_nand_relaxed" =>
897                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, true), AtomicRwOp::Relaxed)?,
898             #[rustfmt::skip]
899             "atomic_xadd" =>
900                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::Add, false), AtomicRwOp::SeqCst)?,
901             #[rustfmt::skip]
902             "atomic_xadd_acq" =>
903                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::Add, false), AtomicRwOp::Acquire)?,
904             #[rustfmt::skip]
905             "atomic_xadd_rel" =>
906                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::Add, false), AtomicRwOp::Release)?,
907             #[rustfmt::skip]
908             "atomic_xadd_acqrel" =>
909                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::Add, false), AtomicRwOp::AcqRel)?,
910             #[rustfmt::skip]
911             "atomic_xadd_relaxed" =>
912                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::Add, false), AtomicRwOp::Relaxed)?,
913             #[rustfmt::skip]
914             "atomic_xsub" =>
915                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::Sub, false), AtomicRwOp::SeqCst)?,
916             #[rustfmt::skip]
917             "atomic_xsub_acq" =>
918                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::Sub, false), AtomicRwOp::Acquire)?,
919             #[rustfmt::skip]
920             "atomic_xsub_rel" =>
921                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::Sub, false), AtomicRwOp::Release)?,
922             #[rustfmt::skip]
923             "atomic_xsub_acqrel" =>
924                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::Sub, false), AtomicRwOp::AcqRel)?,
925             #[rustfmt::skip]
926             "atomic_xsub_relaxed" =>
927                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::Sub, false), AtomicRwOp::Relaxed)?,
928             "atomic_min" => this.atomic_op(args, dest, AtomicOp::Min, AtomicRwOp::SeqCst)?,
929             "atomic_min_acq" => this.atomic_op(args, dest, AtomicOp::Min, AtomicRwOp::Acquire)?,
930             "atomic_min_rel" => this.atomic_op(args, dest, AtomicOp::Min, AtomicRwOp::Release)?,
931             "atomic_min_acqrel" => this.atomic_op(args, dest, AtomicOp::Min, AtomicRwOp::AcqRel)?,
932             "atomic_min_relaxed" =>
933                 this.atomic_op(args, dest, AtomicOp::Min, AtomicRwOp::Relaxed)?,
934             "atomic_max" => this.atomic_op(args, dest, AtomicOp::Max, AtomicRwOp::SeqCst)?,
935             "atomic_max_acq" => this.atomic_op(args, dest, AtomicOp::Max, AtomicRwOp::Acquire)?,
936             "atomic_max_rel" => this.atomic_op(args, dest, AtomicOp::Max, AtomicRwOp::Release)?,
937             "atomic_max_acqrel" => this.atomic_op(args, dest, AtomicOp::Max, AtomicRwOp::AcqRel)?,
938             "atomic_max_relaxed" =>
939                 this.atomic_op(args, dest, AtomicOp::Max, AtomicRwOp::Relaxed)?,
940             "atomic_umin" => this.atomic_op(args, dest, AtomicOp::Min, AtomicRwOp::SeqCst)?,
941             "atomic_umin_acq" => this.atomic_op(args, dest, AtomicOp::Min, AtomicRwOp::Acquire)?,
942             "atomic_umin_rel" => this.atomic_op(args, dest, AtomicOp::Min, AtomicRwOp::Release)?,
943             "atomic_umin_acqrel" =>
944                 this.atomic_op(args, dest, AtomicOp::Min, AtomicRwOp::AcqRel)?,
945             "atomic_umin_relaxed" =>
946                 this.atomic_op(args, dest, AtomicOp::Min, AtomicRwOp::Relaxed)?,
947             "atomic_umax" => this.atomic_op(args, dest, AtomicOp::Max, AtomicRwOp::SeqCst)?,
948             "atomic_umax_acq" => this.atomic_op(args, dest, AtomicOp::Max, AtomicRwOp::Acquire)?,
949             "atomic_umax_rel" => this.atomic_op(args, dest, AtomicOp::Max, AtomicRwOp::Release)?,
950             "atomic_umax_acqrel" =>
951                 this.atomic_op(args, dest, AtomicOp::Max, AtomicRwOp::AcqRel)?,
952             "atomic_umax_relaxed" =>
953                 this.atomic_op(args, dest, AtomicOp::Max, AtomicRwOp::Relaxed)?,
954
955             // Other
956             "exact_div" => {
957                 let &[ref num, ref denom] = check_arg_count(args)?;
958                 this.exact_div(&this.read_immediate(num)?, &this.read_immediate(denom)?, dest)?;
959             }
960
961             "try" => return this.handle_try(args, dest, ret),
962
963             "breakpoint" => {
964                 let &[] = check_arg_count(args)?;
965                 // normally this would raise a SIGTRAP, which aborts if no debugger is connected
966                 throw_machine_stop!(TerminationInfo::Abort("Trace/breakpoint trap".to_string()))
967             }
968
969             name => throw_unsup_format!("unimplemented intrinsic: {}", name),
970         }
971
972         trace!("{:?}", this.dump_place(**dest));
973         this.go_to_block(ret);
974         Ok(())
975     }
976
977     fn atomic_load(
978         &mut self,
979         args: &[OpTy<'tcx, Tag>],
980         dest: &PlaceTy<'tcx, Tag>,
981         atomic: AtomicReadOp,
982     ) -> InterpResult<'tcx> {
983         let this = self.eval_context_mut();
984
985         let &[ref place] = check_arg_count(args)?;
986         let place = this.deref_operand(place)?;
987
988         // make sure it fits into a scalar; otherwise it cannot be atomic
989         let val = this.read_scalar_atomic(&place, atomic)?;
990
991         // Check alignment requirements. Atomics must always be aligned to their size,
992         // even if the type they wrap would be less aligned (e.g. AtomicU64 on 32bit must
993         // be 8-aligned).
994         let align = Align::from_bytes(place.layout.size.bytes()).unwrap();
995         this.memory.check_ptr_access_align(
996             place.ptr,
997             place.layout.size,
998             align,
999             CheckInAllocMsg::MemoryAccessTest,
1000         )?;
1001         // Perform regular access.
1002         this.write_scalar(val, dest)?;
1003         Ok(())
1004     }
1005
1006     fn atomic_store(
1007         &mut self,
1008         args: &[OpTy<'tcx, Tag>],
1009         atomic: AtomicWriteOp,
1010     ) -> InterpResult<'tcx> {
1011         let this = self.eval_context_mut();
1012
1013         let &[ref place, ref val] = check_arg_count(args)?;
1014         let place = this.deref_operand(place)?;
1015         let val = this.read_scalar(val)?; // make sure it fits into a scalar; otherwise it cannot be atomic
1016
1017         // Check alignment requirements. Atomics must always be aligned to their size,
1018         // even if the type they wrap would be less aligned (e.g. AtomicU64 on 32bit must
1019         // be 8-aligned).
1020         let align = Align::from_bytes(place.layout.size.bytes()).unwrap();
1021         this.memory.check_ptr_access_align(
1022             place.ptr,
1023             place.layout.size,
1024             align,
1025             CheckInAllocMsg::MemoryAccessTest,
1026         )?;
1027
1028         // Perform atomic store
1029         this.write_scalar_atomic(val, &place, atomic)?;
1030         Ok(())
1031     }
1032
1033     fn compiler_fence(
1034         &mut self,
1035         args: &[OpTy<'tcx, Tag>],
1036         atomic: AtomicFenceOp,
1037     ) -> InterpResult<'tcx> {
1038         let &[] = check_arg_count(args)?;
1039         let _ = atomic;
1040         //FIXME: compiler fences are currently ignored
1041         Ok(())
1042     }
1043
1044     fn atomic_fence(
1045         &mut self,
1046         args: &[OpTy<'tcx, Tag>],
1047         atomic: AtomicFenceOp,
1048     ) -> InterpResult<'tcx> {
1049         let this = self.eval_context_mut();
1050         let &[] = check_arg_count(args)?;
1051         this.validate_atomic_fence(atomic)?;
1052         Ok(())
1053     }
1054
1055     fn atomic_op(
1056         &mut self,
1057         args: &[OpTy<'tcx, Tag>],
1058         dest: &PlaceTy<'tcx, Tag>,
1059         atomic_op: AtomicOp,
1060         atomic: AtomicRwOp,
1061     ) -> InterpResult<'tcx> {
1062         let this = self.eval_context_mut();
1063
1064         let &[ref place, ref rhs] = check_arg_count(args)?;
1065         let place = this.deref_operand(place)?;
1066
1067         if !place.layout.ty.is_integral() {
1068             bug!("Atomic arithmetic operations only work on integer types");
1069         }
1070         let rhs = this.read_immediate(rhs)?;
1071
1072         // Check alignment requirements. Atomics must always be aligned to their size,
1073         // even if the type they wrap would be less aligned (e.g. AtomicU64 on 32bit must
1074         // be 8-aligned).
1075         let align = Align::from_bytes(place.layout.size.bytes()).unwrap();
1076         this.memory.check_ptr_access_align(
1077             place.ptr,
1078             place.layout.size,
1079             align,
1080             CheckInAllocMsg::MemoryAccessTest,
1081         )?;
1082
1083         match atomic_op {
1084             AtomicOp::Min => {
1085                 let old = this.atomic_min_max_scalar(&place, rhs, true, atomic)?;
1086                 this.write_immediate(*old, &dest)?; // old value is returned
1087                 Ok(())
1088             }
1089             AtomicOp::Max => {
1090                 let old = this.atomic_min_max_scalar(&place, rhs, false, atomic)?;
1091                 this.write_immediate(*old, &dest)?; // old value is returned
1092                 Ok(())
1093             }
1094             AtomicOp::MirOp(op, neg) => {
1095                 let old = this.atomic_op_immediate(&place, &rhs, op, neg, atomic)?;
1096                 this.write_immediate(*old, dest)?; // old value is returned
1097                 Ok(())
1098             }
1099         }
1100     }
1101
1102     fn atomic_exchange(
1103         &mut self,
1104         args: &[OpTy<'tcx, Tag>],
1105         dest: &PlaceTy<'tcx, Tag>,
1106         atomic: AtomicRwOp,
1107     ) -> InterpResult<'tcx> {
1108         let this = self.eval_context_mut();
1109
1110         let &[ref place, ref new] = check_arg_count(args)?;
1111         let place = this.deref_operand(place)?;
1112         let new = this.read_scalar(new)?;
1113
1114         // Check alignment requirements. Atomics must always be aligned to their size,
1115         // even if the type they wrap would be less aligned (e.g. AtomicU64 on 32bit must
1116         // be 8-aligned).
1117         let align = Align::from_bytes(place.layout.size.bytes()).unwrap();
1118         this.memory.check_ptr_access_align(
1119             place.ptr,
1120             place.layout.size,
1121             align,
1122             CheckInAllocMsg::MemoryAccessTest,
1123         )?;
1124
1125         let old = this.atomic_exchange_scalar(&place, new, atomic)?;
1126         this.write_scalar(old, dest)?; // old value is returned
1127         Ok(())
1128     }
1129
1130     fn atomic_compare_exchange_impl(
1131         &mut self,
1132         args: &[OpTy<'tcx, Tag>],
1133         dest: &PlaceTy<'tcx, Tag>,
1134         success: AtomicRwOp,
1135         fail: AtomicReadOp,
1136         can_fail_spuriously: bool,
1137     ) -> InterpResult<'tcx> {
1138         let this = self.eval_context_mut();
1139
1140         let &[ref place, ref expect_old, ref new] = check_arg_count(args)?;
1141         let place = this.deref_operand(place)?;
1142         let expect_old = this.read_immediate(expect_old)?; // read as immediate for the sake of `binary_op()`
1143         let new = this.read_scalar(new)?;
1144
1145         // Check alignment requirements. Atomics must always be aligned to their size,
1146         // even if the type they wrap would be less aligned (e.g. AtomicU64 on 32bit must
1147         // be 8-aligned).
1148         let align = Align::from_bytes(place.layout.size.bytes()).unwrap();
1149         this.memory.check_ptr_access_align(
1150             place.ptr,
1151             place.layout.size,
1152             align,
1153             CheckInAllocMsg::MemoryAccessTest,
1154         )?;
1155
1156         let old = this.atomic_compare_exchange_scalar(
1157             &place,
1158             &expect_old,
1159             new,
1160             success,
1161             fail,
1162             can_fail_spuriously,
1163         )?;
1164
1165         // Return old value.
1166         this.write_immediate(old, dest)?;
1167         Ok(())
1168     }
1169
1170     fn atomic_compare_exchange(
1171         &mut self,
1172         args: &[OpTy<'tcx, Tag>],
1173         dest: &PlaceTy<'tcx, Tag>,
1174         success: AtomicRwOp,
1175         fail: AtomicReadOp,
1176     ) -> InterpResult<'tcx> {
1177         self.atomic_compare_exchange_impl(args, dest, success, fail, false)
1178     }
1179
1180     fn atomic_compare_exchange_weak(
1181         &mut self,
1182         args: &[OpTy<'tcx, Tag>],
1183         dest: &PlaceTy<'tcx, Tag>,
1184         success: AtomicRwOp,
1185         fail: AtomicReadOp,
1186     ) -> InterpResult<'tcx> {
1187         self.atomic_compare_exchange_impl(args, dest, success, fail, true)
1188     }
1189
1190     fn float_to_int_unchecked<F>(
1191         &self,
1192         f: F,
1193         dest_ty: ty::Ty<'tcx>,
1194     ) -> InterpResult<'tcx, Scalar<Tag>>
1195     where
1196         F: Float + Into<Scalar<Tag>>,
1197     {
1198         let this = self.eval_context_ref();
1199
1200         // Step 1: cut off the fractional part of `f`. The result of this is
1201         // guaranteed to be precisely representable in IEEE floats.
1202         let f = f.round_to_integral(Round::TowardZero).value;
1203
1204         // Step 2: Cast the truncated float to the target integer type and see if we lose any information in this step.
1205         Ok(match dest_ty.kind() {
1206             // Unsigned
1207             ty::Uint(t) => {
1208                 let size = Integer::from_uint_ty(this, *t).size();
1209                 let res = f.to_u128(size.bits_usize());
1210                 if res.status.is_empty() {
1211                     // No status flags means there was no further rounding or other loss of precision.
1212                     Scalar::from_uint(res.value, size)
1213                 } else {
1214                     // `f` was not representable in this integer type.
1215                     throw_ub_format!(
1216                         "`float_to_int_unchecked` intrinsic called on {} which cannot be represented in target type `{:?}`",
1217                         f,
1218                         dest_ty,
1219                     );
1220                 }
1221             }
1222             // Signed
1223             ty::Int(t) => {
1224                 let size = Integer::from_int_ty(this, *t).size();
1225                 let res = f.to_i128(size.bits_usize());
1226                 if res.status.is_empty() {
1227                     // No status flags means there was no further rounding or other loss of precision.
1228                     Scalar::from_int(res.value, size)
1229                 } else {
1230                     // `f` was not representable in this integer type.
1231                     throw_ub_format!(
1232                         "`float_to_int_unchecked` intrinsic called on {} which cannot be represented in target type `{:?}`",
1233                         f,
1234                         dest_ty,
1235                     );
1236                 }
1237             }
1238             // Nothing else
1239             _ => bug!("`float_to_int_unchecked` called with non-int output type {:?}", dest_ty),
1240         })
1241     }
1242 }
1243
1244 fn fmax_op<'tcx>(
1245     left: &ImmTy<'tcx, Tag>,
1246     right: &ImmTy<'tcx, Tag>,
1247 ) -> InterpResult<'tcx, Scalar<Tag>> {
1248     assert_eq!(left.layout.ty, right.layout.ty);
1249     let ty::Float(float_ty) = left.layout.ty.kind() else {
1250         bug!("fmax operand is not a float")
1251     };
1252     let left = left.to_scalar()?;
1253     let right = right.to_scalar()?;
1254     Ok(match float_ty {
1255         FloatTy::F32 => Scalar::from_f32(left.to_f32()?.max(right.to_f32()?)),
1256         FloatTy::F64 => Scalar::from_f64(left.to_f64()?.max(right.to_f64()?)),
1257     })
1258 }
1259
1260 fn fmin_op<'tcx>(
1261     left: &ImmTy<'tcx, Tag>,
1262     right: &ImmTy<'tcx, Tag>,
1263 ) -> InterpResult<'tcx, Scalar<Tag>> {
1264     assert_eq!(left.layout.ty, right.layout.ty);
1265     let ty::Float(float_ty) = left.layout.ty.kind() else {
1266         bug!("fmin operand is not a float")
1267     };
1268     let left = left.to_scalar()?;
1269     let right = right.to_scalar()?;
1270     Ok(match float_ty {
1271         FloatTy::F32 => Scalar::from_f32(left.to_f32()?.min(right.to_f32()?)),
1272         FloatTy::F64 => Scalar::from_f64(left.to_f64()?.min(right.to_f64()?)),
1273     })
1274 }