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