]> git.lizzy.rs Git - rust.git/blob - src/shims/intrinsics.rs
implement simd_select
[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::{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             #[rustfmt::skip]
94             | "sinf32"
95             | "fabsf32"
96             | "cosf32"
97             | "sqrtf32"
98             | "expf32"
99             | "exp2f32"
100             | "logf32"
101             | "log10f32"
102             | "log2f32"
103             | "floorf32"
104             | "ceilf32"
105             | "truncf32"
106             | "roundf32"
107             => {
108                 let &[ref f] = check_arg_count(args)?;
109                 // FIXME: Using host floats.
110                 let f = f32::from_bits(this.read_scalar(f)?.to_u32()?);
111                 let f = match intrinsic_name {
112                     "sinf32" => f.sin(),
113                     "fabsf32" => f.abs(),
114                     "cosf32" => f.cos(),
115                     "sqrtf32" => f.sqrt(),
116                     "expf32" => f.exp(),
117                     "exp2f32" => f.exp2(),
118                     "logf32" => f.ln(),
119                     "log10f32" => f.log10(),
120                     "log2f32" => f.log2(),
121                     "floorf32" => f.floor(),
122                     "ceilf32" => f.ceil(),
123                     "truncf32" => f.trunc(),
124                     "roundf32" => f.round(),
125                     _ => bug!(),
126                 };
127                 this.write_scalar(Scalar::from_u32(f.to_bits()), dest)?;
128             }
129
130             #[rustfmt::skip]
131             | "sinf64"
132             | "fabsf64"
133             | "cosf64"
134             | "sqrtf64"
135             | "expf64"
136             | "exp2f64"
137             | "logf64"
138             | "log10f64"
139             | "log2f64"
140             | "floorf64"
141             | "ceilf64"
142             | "truncf64"
143             | "roundf64"
144             => {
145                 let &[ref f] = check_arg_count(args)?;
146                 // FIXME: Using host floats.
147                 let f = f64::from_bits(this.read_scalar(f)?.to_u64()?);
148                 let f = match intrinsic_name {
149                     "sinf64" => f.sin(),
150                     "fabsf64" => f.abs(),
151                     "cosf64" => f.cos(),
152                     "sqrtf64" => f.sqrt(),
153                     "expf64" => f.exp(),
154                     "exp2f64" => f.exp2(),
155                     "logf64" => f.ln(),
156                     "log10f64" => f.log10(),
157                     "log2f64" => f.log2(),
158                     "floorf64" => f.floor(),
159                     "ceilf64" => f.ceil(),
160                     "truncf64" => f.trunc(),
161                     "roundf64" => f.round(),
162                     _ => bug!(),
163                 };
164                 this.write_scalar(Scalar::from_u64(f.to_bits()), dest)?;
165             }
166
167             #[rustfmt::skip]
168             | "fadd_fast"
169             | "fsub_fast"
170             | "fmul_fast"
171             | "fdiv_fast"
172             | "frem_fast"
173             => {
174                 let &[ref a, ref b] = check_arg_count(args)?;
175                 let a = this.read_immediate(a)?;
176                 let b = this.read_immediate(b)?;
177                 let op = match intrinsic_name {
178                     "fadd_fast" => mir::BinOp::Add,
179                     "fsub_fast" => mir::BinOp::Sub,
180                     "fmul_fast" => mir::BinOp::Mul,
181                     "fdiv_fast" => mir::BinOp::Div,
182                     "frem_fast" => mir::BinOp::Rem,
183                     _ => bug!(),
184                 };
185                 let float_finite = |x: ImmTy<'tcx, _>| -> InterpResult<'tcx, bool> {
186                     Ok(match x.layout.ty.kind() {
187                         ty::Float(FloatTy::F32) => x.to_scalar()?.to_f32()?.is_finite(),
188                         ty::Float(FloatTy::F64) => x.to_scalar()?.to_f64()?.is_finite(),
189                         _ => bug!(
190                             "`{}` called with non-float input type {:?}",
191                             intrinsic_name,
192                             x.layout.ty
193                         ),
194                     })
195                 };
196                 match (float_finite(a)?, float_finite(b)?) {
197                     (false, false) => throw_ub_format!(
198                         "`{}` intrinsic called with non-finite value as both parameters",
199                         intrinsic_name,
200                     ),
201                     (false, _) => throw_ub_format!(
202                         "`{}` intrinsic called with non-finite value as first parameter",
203                         intrinsic_name,
204                     ),
205                     (_, false) => throw_ub_format!(
206                         "`{}` intrinsic called with non-finite value as second parameter",
207                         intrinsic_name,
208                     ),
209                     _ => {}
210                 }
211                 this.binop_ignore_overflow(op, &a, &b, dest)?;
212             }
213
214             #[rustfmt::skip]
215             | "minnumf32"
216             | "maxnumf32"
217             | "copysignf32"
218             => {
219                 let &[ref a, ref b] = check_arg_count(args)?;
220                 let a = this.read_scalar(a)?.to_f32()?;
221                 let b = this.read_scalar(b)?.to_f32()?;
222                 let res = match intrinsic_name {
223                     "minnumf32" => a.min(b),
224                     "maxnumf32" => a.max(b),
225                     "copysignf32" => a.copy_sign(b),
226                     _ => bug!(),
227                 };
228                 this.write_scalar(Scalar::from_f32(res), dest)?;
229             }
230
231             #[rustfmt::skip]
232             | "minnumf64"
233             | "maxnumf64"
234             | "copysignf64"
235             => {
236                 let &[ref a, ref b] = check_arg_count(args)?;
237                 let a = this.read_scalar(a)?.to_f64()?;
238                 let b = this.read_scalar(b)?.to_f64()?;
239                 let res = match intrinsic_name {
240                     "minnumf64" => a.min(b),
241                     "maxnumf64" => a.max(b),
242                     "copysignf64" => a.copy_sign(b),
243                     _ => bug!(),
244                 };
245                 this.write_scalar(Scalar::from_f64(res), dest)?;
246             }
247
248             "powf32" => {
249                 let &[ref f, ref f2] = check_arg_count(args)?;
250                 // FIXME: Using host floats.
251                 let f = f32::from_bits(this.read_scalar(f)?.to_u32()?);
252                 let f2 = f32::from_bits(this.read_scalar(f2)?.to_u32()?);
253                 this.write_scalar(Scalar::from_u32(f.powf(f2).to_bits()), dest)?;
254             }
255
256             "powf64" => {
257                 let &[ref f, ref f2] = check_arg_count(args)?;
258                 // FIXME: Using host floats.
259                 let f = f64::from_bits(this.read_scalar(f)?.to_u64()?);
260                 let f2 = f64::from_bits(this.read_scalar(f2)?.to_u64()?);
261                 this.write_scalar(Scalar::from_u64(f.powf(f2).to_bits()), dest)?;
262             }
263
264             "fmaf32" => {
265                 let &[ref a, ref b, ref c] = check_arg_count(args)?;
266                 let a = this.read_scalar(a)?.to_f32()?;
267                 let b = this.read_scalar(b)?.to_f32()?;
268                 let c = this.read_scalar(c)?.to_f32()?;
269                 let res = a.mul_add(b, c).value;
270                 this.write_scalar(Scalar::from_f32(res), dest)?;
271             }
272
273             "fmaf64" => {
274                 let &[ref a, ref b, ref c] = check_arg_count(args)?;
275                 let a = this.read_scalar(a)?.to_f64()?;
276                 let b = this.read_scalar(b)?.to_f64()?;
277                 let c = this.read_scalar(c)?.to_f64()?;
278                 let res = a.mul_add(b, c).value;
279                 this.write_scalar(Scalar::from_f64(res), dest)?;
280             }
281
282             "powif32" => {
283                 let &[ref f, ref i] = check_arg_count(args)?;
284                 // FIXME: Using host floats.
285                 let f = f32::from_bits(this.read_scalar(f)?.to_u32()?);
286                 let i = this.read_scalar(i)?.to_i32()?;
287                 this.write_scalar(Scalar::from_u32(f.powi(i).to_bits()), dest)?;
288             }
289
290             "powif64" => {
291                 let &[ref f, ref i] = check_arg_count(args)?;
292                 // FIXME: Using host floats.
293                 let f = f64::from_bits(this.read_scalar(f)?.to_u64()?);
294                 let i = this.read_scalar(i)?.to_i32()?;
295                 this.write_scalar(Scalar::from_u64(f.powi(i).to_bits()), dest)?;
296             }
297
298             "float_to_int_unchecked" => {
299                 let &[ref val] = check_arg_count(args)?;
300                 let val = this.read_immediate(val)?;
301
302                 let res = match val.layout.ty.kind() {
303                     ty::Float(FloatTy::F32) =>
304                         this.float_to_int_unchecked(val.to_scalar()?.to_f32()?, dest.layout.ty)?,
305                     ty::Float(FloatTy::F64) =>
306                         this.float_to_int_unchecked(val.to_scalar()?.to_f64()?, dest.layout.ty)?,
307                     _ =>
308                         bug!(
309                             "`float_to_int_unchecked` called with non-float input type {:?}",
310                             val.layout.ty
311                         ),
312                 };
313
314                 this.write_scalar(res, dest)?;
315             }
316
317             // SIMD operations
318             #[rustfmt::skip]
319             | "simd_add"
320             | "simd_sub"
321             | "simd_mul"
322             | "simd_div"
323             | "simd_rem"
324             | "simd_shl"
325             | "simd_shr"
326             | "simd_and"
327             | "simd_or"
328             | "simd_eq" => {
329                 let &[ref left, ref right] = check_arg_count(args)?;
330                 let (left, left_len) = this.operand_to_simd(left)?;
331                 let (right, right_len) = this.operand_to_simd(right)?;
332                 let (dest, dest_len) = this.place_to_simd(dest)?;
333
334                 assert_eq!(dest_len, left_len);
335                 assert_eq!(dest_len, right_len);
336
337                 let op = match intrinsic_name {
338                     "simd_add" => mir::BinOp::Add,
339                     "simd_sub" => mir::BinOp::Sub,
340                     "simd_mul" => mir::BinOp::Mul,
341                     "simd_div" => mir::BinOp::Div,
342                     "simd_rem" => mir::BinOp::Rem,
343                     "simd_shl" => mir::BinOp::Shl,
344                     "simd_shr" => mir::BinOp::Shr,
345                     "simd_and" => mir::BinOp::BitAnd,
346                     "simd_or" => mir::BinOp::BitOr,
347                     "simd_eq" => mir::BinOp::Eq,
348                     _ => unreachable!(),
349                 };
350
351                 for i in 0..dest_len {
352                     let left = this.read_immediate(&this.mplace_index(&left, i)?.into())?;
353                     let right = this.read_immediate(&this.mplace_index(&right, i)?.into())?;
354                     let dest = this.mplace_index(&dest, i)?;
355                     let (val, overflowed, ty) = this.overflowing_binary_op(op, &left, &right)?;
356                     if matches!(op, mir::BinOp::Shl | mir::BinOp::Shr) {
357                         // Shifts have extra UB as SIMD operations that the MIR binop does not have.
358                         // See <https://github.com/rust-lang/rust/issues/91237>.
359                         if overflowed {
360                             let r_val = right.to_scalar()?.to_bits(right.layout.size)?;
361                             throw_ub_format!("overflowing shift by {} in `{}` in SIMD lane {}", r_val, intrinsic_name, i);
362                         }
363                     }
364                     if matches!(op, mir::BinOp::Eq) {
365                         // Special handling for boolean-returning operations
366                         assert_eq!(ty, this.tcx.types.bool);
367                         let val = val.to_bool().unwrap();
368                         let val = bool_to_simd_element(val, dest.layout.size);
369                         this.write_scalar(val, &dest.into())?;
370                     } else {
371                         assert_eq!(ty, dest.layout.ty);
372                         this.write_scalar(val, &dest.into())?;
373                     }
374                 }
375             }
376             "simd_reduce_any" => {
377                 let &[ref arg] = check_arg_count(args)?;
378                 let (arg, arg_len) = this.operand_to_simd(arg)?;
379
380                 let mut res = false; // the neutral element
381                 for i in 0..arg_len {
382                     let op = this.read_immediate(&this.mplace_index(&arg, i)?.into())?;
383                     let val = simd_element_to_bool(op)?;
384                     res = res | val;
385                 }
386
387                 this.write_scalar(Scalar::from_bool(res), dest)?;
388             }
389             "simd_select" => {
390                 let &[ref mask, ref yes, ref no] = check_arg_count(args)?;
391                 let (mask, mask_len) = this.operand_to_simd(mask)?;
392                 let (yes, yes_len) = this.operand_to_simd(yes)?;
393                 let (no, no_len) = this.operand_to_simd(no)?;
394                 let (dest, dest_len) = this.place_to_simd(dest)?;
395
396                 assert_eq!(dest_len, mask_len);
397                 assert_eq!(dest_len, yes_len);
398                 assert_eq!(dest_len, no_len);
399
400                 for i in 0..dest_len {
401                     let mask = this.read_immediate(&this.mplace_index(&mask, i)?.into())?;
402                     let yes = this.read_immediate(&this.mplace_index(&yes, i)?.into())?;
403                     let no = this.read_immediate(&this.mplace_index(&no, i)?.into())?;
404                     let dest = this.mplace_index(&dest, i)?;
405
406                     let mask = simd_element_to_bool(mask)?;
407                     let val = if mask { yes } else { no };
408                     this.write_immediate(*val, &dest.into())?;
409                 }
410             }
411
412             // Atomic operations
413             "atomic_load" => this.atomic_load(args, dest, AtomicReadOp::SeqCst)?,
414             "atomic_load_relaxed" => this.atomic_load(args, dest, AtomicReadOp::Relaxed)?,
415             "atomic_load_acq" => this.atomic_load(args, dest, AtomicReadOp::Acquire)?,
416
417             "atomic_store" => this.atomic_store(args, AtomicWriteOp::SeqCst)?,
418             "atomic_store_relaxed" => this.atomic_store(args, AtomicWriteOp::Relaxed)?,
419             "atomic_store_rel" => this.atomic_store(args, AtomicWriteOp::Release)?,
420
421             "atomic_fence_acq" => this.atomic_fence(args, AtomicFenceOp::Acquire)?,
422             "atomic_fence_rel" => this.atomic_fence(args, AtomicFenceOp::Release)?,
423             "atomic_fence_acqrel" => this.atomic_fence(args, AtomicFenceOp::AcqRel)?,
424             "atomic_fence" => this.atomic_fence(args, AtomicFenceOp::SeqCst)?,
425
426             "atomic_singlethreadfence_acq" => this.compiler_fence(args, AtomicFenceOp::Acquire)?,
427             "atomic_singlethreadfence_rel" => this.compiler_fence(args, AtomicFenceOp::Release)?,
428             "atomic_singlethreadfence_acqrel" =>
429                 this.compiler_fence(args, AtomicFenceOp::AcqRel)?,
430             "atomic_singlethreadfence" => this.compiler_fence(args, AtomicFenceOp::SeqCst)?,
431
432             "atomic_xchg" => this.atomic_exchange(args, dest, AtomicRwOp::SeqCst)?,
433             "atomic_xchg_acq" => this.atomic_exchange(args, dest, AtomicRwOp::Acquire)?,
434             "atomic_xchg_rel" => this.atomic_exchange(args, dest, AtomicRwOp::Release)?,
435             "atomic_xchg_acqrel" => this.atomic_exchange(args, dest, AtomicRwOp::AcqRel)?,
436             "atomic_xchg_relaxed" => this.atomic_exchange(args, dest, AtomicRwOp::Relaxed)?,
437
438             #[rustfmt::skip]
439             "atomic_cxchg" =>
440                 this.atomic_compare_exchange(args, dest, AtomicRwOp::SeqCst, AtomicReadOp::SeqCst)?,
441             #[rustfmt::skip]
442             "atomic_cxchg_acq" =>
443                 this.atomic_compare_exchange(args, dest, AtomicRwOp::Acquire, AtomicReadOp::Acquire)?,
444             #[rustfmt::skip]
445             "atomic_cxchg_rel" =>
446                 this.atomic_compare_exchange(args, dest, AtomicRwOp::Release, AtomicReadOp::Relaxed)?,
447             #[rustfmt::skip]
448             "atomic_cxchg_acqrel" =>
449                 this.atomic_compare_exchange(args, dest, AtomicRwOp::AcqRel, AtomicReadOp::Acquire)?,
450             #[rustfmt::skip]
451             "atomic_cxchg_relaxed" =>
452                 this.atomic_compare_exchange(args, dest, AtomicRwOp::Relaxed, AtomicReadOp::Relaxed)?,
453             #[rustfmt::skip]
454             "atomic_cxchg_acq_failrelaxed" =>
455                 this.atomic_compare_exchange(args, dest, AtomicRwOp::Acquire, AtomicReadOp::Relaxed)?,
456             #[rustfmt::skip]
457             "atomic_cxchg_acqrel_failrelaxed" =>
458                 this.atomic_compare_exchange(args, dest, AtomicRwOp::AcqRel, AtomicReadOp::Relaxed)?,
459             #[rustfmt::skip]
460             "atomic_cxchg_failrelaxed" =>
461                 this.atomic_compare_exchange(args, dest, AtomicRwOp::SeqCst, AtomicReadOp::Relaxed)?,
462             #[rustfmt::skip]
463             "atomic_cxchg_failacq" =>
464                 this.atomic_compare_exchange(args, dest, AtomicRwOp::SeqCst, AtomicReadOp::Acquire)?,
465
466             #[rustfmt::skip]
467             "atomic_cxchgweak" =>
468                 this.atomic_compare_exchange_weak(args, dest, AtomicRwOp::SeqCst, AtomicReadOp::SeqCst)?,
469             #[rustfmt::skip]
470             "atomic_cxchgweak_acq" =>
471                 this.atomic_compare_exchange_weak(args, dest, AtomicRwOp::Acquire, AtomicReadOp::Acquire)?,
472             #[rustfmt::skip]
473             "atomic_cxchgweak_rel" =>
474                 this.atomic_compare_exchange_weak(args, dest, AtomicRwOp::Release, AtomicReadOp::Relaxed)?,
475             #[rustfmt::skip]
476             "atomic_cxchgweak_acqrel" =>
477                 this.atomic_compare_exchange_weak(args, dest, AtomicRwOp::AcqRel, AtomicReadOp::Acquire)?,
478             #[rustfmt::skip]
479             "atomic_cxchgweak_relaxed" =>
480                 this.atomic_compare_exchange_weak(args, dest, AtomicRwOp::Relaxed, AtomicReadOp::Relaxed)?,
481             #[rustfmt::skip]
482             "atomic_cxchgweak_acq_failrelaxed" =>
483                 this.atomic_compare_exchange_weak(args, dest, AtomicRwOp::Acquire, AtomicReadOp::Relaxed)?,
484             #[rustfmt::skip]
485             "atomic_cxchgweak_acqrel_failrelaxed" =>
486                 this.atomic_compare_exchange_weak(args, dest, AtomicRwOp::AcqRel, AtomicReadOp::Relaxed)?,
487             #[rustfmt::skip]
488             "atomic_cxchgweak_failrelaxed" =>
489                 this.atomic_compare_exchange_weak(args, dest, AtomicRwOp::SeqCst, AtomicReadOp::Relaxed)?,
490             #[rustfmt::skip]
491             "atomic_cxchgweak_failacq" =>
492                 this.atomic_compare_exchange_weak(args, dest, AtomicRwOp::SeqCst, AtomicReadOp::Acquire)?,
493
494             #[rustfmt::skip]
495             "atomic_or" =>
496                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitOr, false), AtomicRwOp::SeqCst)?,
497             #[rustfmt::skip]
498             "atomic_or_acq" =>
499                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitOr, false), AtomicRwOp::Acquire)?,
500             #[rustfmt::skip]
501             "atomic_or_rel" =>
502                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitOr, false), AtomicRwOp::Release)?,
503             #[rustfmt::skip]
504             "atomic_or_acqrel" =>
505                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitOr, false), AtomicRwOp::AcqRel)?,
506             #[rustfmt::skip]
507             "atomic_or_relaxed" =>
508                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitOr, false), AtomicRwOp::Relaxed)?,
509             #[rustfmt::skip]
510             "atomic_xor" =>
511                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitXor, false), AtomicRwOp::SeqCst)?,
512             #[rustfmt::skip]
513             "atomic_xor_acq" =>
514                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitXor, false), AtomicRwOp::Acquire)?,
515             #[rustfmt::skip]
516             "atomic_xor_rel" =>
517                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitXor, false), AtomicRwOp::Release)?,
518             #[rustfmt::skip]
519             "atomic_xor_acqrel" =>
520                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitXor, false), AtomicRwOp::AcqRel)?,
521             #[rustfmt::skip]
522             "atomic_xor_relaxed" =>
523                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitXor, false), AtomicRwOp::Relaxed)?,
524             #[rustfmt::skip]
525             "atomic_and" =>
526                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, false), AtomicRwOp::SeqCst)?,
527             #[rustfmt::skip]
528             "atomic_and_acq" =>
529                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, false), AtomicRwOp::Acquire)?,
530             #[rustfmt::skip]
531             "atomic_and_rel" =>
532                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, false), AtomicRwOp::Release)?,
533             #[rustfmt::skip]
534             "atomic_and_acqrel" =>
535                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, false), AtomicRwOp::AcqRel)?,
536             #[rustfmt::skip]
537             "atomic_and_relaxed" =>
538                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, false), AtomicRwOp::Relaxed)?,
539             #[rustfmt::skip]
540             "atomic_nand" =>
541                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, true), AtomicRwOp::SeqCst)?,
542             #[rustfmt::skip]
543             "atomic_nand_acq" =>
544                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, true), AtomicRwOp::Acquire)?,
545             #[rustfmt::skip]
546             "atomic_nand_rel" =>
547                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, true), AtomicRwOp::Release)?,
548             #[rustfmt::skip]
549             "atomic_nand_acqrel" =>
550                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, true), AtomicRwOp::AcqRel)?,
551             #[rustfmt::skip]
552             "atomic_nand_relaxed" =>
553                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, true), AtomicRwOp::Relaxed)?,
554             #[rustfmt::skip]
555             "atomic_xadd" =>
556                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::Add, false), AtomicRwOp::SeqCst)?,
557             #[rustfmt::skip]
558             "atomic_xadd_acq" =>
559                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::Add, false), AtomicRwOp::Acquire)?,
560             #[rustfmt::skip]
561             "atomic_xadd_rel" =>
562                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::Add, false), AtomicRwOp::Release)?,
563             #[rustfmt::skip]
564             "atomic_xadd_acqrel" =>
565                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::Add, false), AtomicRwOp::AcqRel)?,
566             #[rustfmt::skip]
567             "atomic_xadd_relaxed" =>
568                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::Add, false), AtomicRwOp::Relaxed)?,
569             #[rustfmt::skip]
570             "atomic_xsub" =>
571                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::Sub, false), AtomicRwOp::SeqCst)?,
572             #[rustfmt::skip]
573             "atomic_xsub_acq" =>
574                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::Sub, false), AtomicRwOp::Acquire)?,
575             #[rustfmt::skip]
576             "atomic_xsub_rel" =>
577                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::Sub, false), AtomicRwOp::Release)?,
578             #[rustfmt::skip]
579             "atomic_xsub_acqrel" =>
580                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::Sub, false), AtomicRwOp::AcqRel)?,
581             #[rustfmt::skip]
582             "atomic_xsub_relaxed" =>
583                 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::Sub, false), AtomicRwOp::Relaxed)?,
584             "atomic_min" => this.atomic_op(args, dest, AtomicOp::Min, AtomicRwOp::SeqCst)?,
585             "atomic_min_acq" => this.atomic_op(args, dest, AtomicOp::Min, AtomicRwOp::Acquire)?,
586             "atomic_min_rel" => this.atomic_op(args, dest, AtomicOp::Min, AtomicRwOp::Release)?,
587             "atomic_min_acqrel" => this.atomic_op(args, dest, AtomicOp::Min, AtomicRwOp::AcqRel)?,
588             "atomic_min_relaxed" =>
589                 this.atomic_op(args, dest, AtomicOp::Min, AtomicRwOp::Relaxed)?,
590             "atomic_max" => this.atomic_op(args, dest, AtomicOp::Max, AtomicRwOp::SeqCst)?,
591             "atomic_max_acq" => this.atomic_op(args, dest, AtomicOp::Max, AtomicRwOp::Acquire)?,
592             "atomic_max_rel" => this.atomic_op(args, dest, AtomicOp::Max, AtomicRwOp::Release)?,
593             "atomic_max_acqrel" => this.atomic_op(args, dest, AtomicOp::Max, AtomicRwOp::AcqRel)?,
594             "atomic_max_relaxed" =>
595                 this.atomic_op(args, dest, AtomicOp::Max, AtomicRwOp::Relaxed)?,
596             "atomic_umin" => this.atomic_op(args, dest, AtomicOp::Min, AtomicRwOp::SeqCst)?,
597             "atomic_umin_acq" => this.atomic_op(args, dest, AtomicOp::Min, AtomicRwOp::Acquire)?,
598             "atomic_umin_rel" => this.atomic_op(args, dest, AtomicOp::Min, AtomicRwOp::Release)?,
599             "atomic_umin_acqrel" =>
600                 this.atomic_op(args, dest, AtomicOp::Min, AtomicRwOp::AcqRel)?,
601             "atomic_umin_relaxed" =>
602                 this.atomic_op(args, dest, AtomicOp::Min, AtomicRwOp::Relaxed)?,
603             "atomic_umax" => this.atomic_op(args, dest, AtomicOp::Max, AtomicRwOp::SeqCst)?,
604             "atomic_umax_acq" => this.atomic_op(args, dest, AtomicOp::Max, AtomicRwOp::Acquire)?,
605             "atomic_umax_rel" => this.atomic_op(args, dest, AtomicOp::Max, AtomicRwOp::Release)?,
606             "atomic_umax_acqrel" =>
607                 this.atomic_op(args, dest, AtomicOp::Max, AtomicRwOp::AcqRel)?,
608             "atomic_umax_relaxed" =>
609                 this.atomic_op(args, dest, AtomicOp::Max, AtomicRwOp::Relaxed)?,
610
611             // Other
612             "exact_div" => {
613                 let &[ref num, ref denom] = check_arg_count(args)?;
614                 this.exact_div(&this.read_immediate(num)?, &this.read_immediate(denom)?, dest)?;
615             }
616
617             "try" => return this.handle_try(args, dest, ret),
618
619             "breakpoint" => {
620                 let &[] = check_arg_count(args)?;
621                 // normally this would raise a SIGTRAP, which aborts if no debugger is connected
622                 throw_machine_stop!(TerminationInfo::Abort("Trace/breakpoint trap".to_string()))
623             }
624
625             name => throw_unsup_format!("unimplemented intrinsic: {}", name),
626         }
627
628         trace!("{:?}", this.dump_place(**dest));
629         this.go_to_block(ret);
630         Ok(())
631     }
632
633     fn atomic_load(
634         &mut self,
635         args: &[OpTy<'tcx, Tag>],
636         dest: &PlaceTy<'tcx, Tag>,
637         atomic: AtomicReadOp,
638     ) -> InterpResult<'tcx> {
639         let this = self.eval_context_mut();
640
641         let &[ref place] = check_arg_count(args)?;
642         let place = this.deref_operand(place)?;
643
644         // make sure it fits into a scalar; otherwise it cannot be atomic
645         let val = this.read_scalar_atomic(&place, atomic)?;
646
647         // Check alignment requirements. Atomics must always be aligned to their size,
648         // even if the type they wrap would be less aligned (e.g. AtomicU64 on 32bit must
649         // be 8-aligned).
650         let align = Align::from_bytes(place.layout.size.bytes()).unwrap();
651         this.memory.check_ptr_access_align(
652             place.ptr,
653             place.layout.size,
654             align,
655             CheckInAllocMsg::MemoryAccessTest,
656         )?;
657         // Perform regular access.
658         this.write_scalar(val, dest)?;
659         Ok(())
660     }
661
662     fn atomic_store(
663         &mut self,
664         args: &[OpTy<'tcx, Tag>],
665         atomic: AtomicWriteOp,
666     ) -> InterpResult<'tcx> {
667         let this = self.eval_context_mut();
668
669         let &[ref place, ref val] = check_arg_count(args)?;
670         let place = this.deref_operand(place)?;
671         let val = this.read_scalar(val)?; // make sure it fits into a scalar; otherwise it cannot be atomic
672
673         // Check alignment requirements. Atomics must always be aligned to their size,
674         // even if the type they wrap would be less aligned (e.g. AtomicU64 on 32bit must
675         // be 8-aligned).
676         let align = Align::from_bytes(place.layout.size.bytes()).unwrap();
677         this.memory.check_ptr_access_align(
678             place.ptr,
679             place.layout.size,
680             align,
681             CheckInAllocMsg::MemoryAccessTest,
682         )?;
683
684         // Perform atomic store
685         this.write_scalar_atomic(val, &place, atomic)?;
686         Ok(())
687     }
688
689     fn compiler_fence(
690         &mut self,
691         args: &[OpTy<'tcx, Tag>],
692         atomic: AtomicFenceOp,
693     ) -> InterpResult<'tcx> {
694         let &[] = check_arg_count(args)?;
695         let _ = atomic;
696         //FIXME: compiler fences are currently ignored
697         Ok(())
698     }
699
700     fn atomic_fence(
701         &mut self,
702         args: &[OpTy<'tcx, Tag>],
703         atomic: AtomicFenceOp,
704     ) -> InterpResult<'tcx> {
705         let this = self.eval_context_mut();
706         let &[] = check_arg_count(args)?;
707         this.validate_atomic_fence(atomic)?;
708         Ok(())
709     }
710
711     fn atomic_op(
712         &mut self,
713         args: &[OpTy<'tcx, Tag>],
714         dest: &PlaceTy<'tcx, Tag>,
715         atomic_op: AtomicOp,
716         atomic: AtomicRwOp,
717     ) -> InterpResult<'tcx> {
718         let this = self.eval_context_mut();
719
720         let &[ref place, ref rhs] = check_arg_count(args)?;
721         let place = this.deref_operand(place)?;
722
723         if !place.layout.ty.is_integral() {
724             bug!("Atomic arithmetic operations only work on integer types");
725         }
726         let rhs = this.read_immediate(rhs)?;
727
728         // Check alignment requirements. Atomics must always be aligned to their size,
729         // even if the type they wrap would be less aligned (e.g. AtomicU64 on 32bit must
730         // be 8-aligned).
731         let align = Align::from_bytes(place.layout.size.bytes()).unwrap();
732         this.memory.check_ptr_access_align(
733             place.ptr,
734             place.layout.size,
735             align,
736             CheckInAllocMsg::MemoryAccessTest,
737         )?;
738
739         match atomic_op {
740             AtomicOp::Min => {
741                 let old = this.atomic_min_max_scalar(&place, rhs, true, atomic)?;
742                 this.write_immediate(*old, &dest)?; // old value is returned
743                 Ok(())
744             }
745             AtomicOp::Max => {
746                 let old = this.atomic_min_max_scalar(&place, rhs, false, atomic)?;
747                 this.write_immediate(*old, &dest)?; // old value is returned
748                 Ok(())
749             }
750             AtomicOp::MirOp(op, neg) => {
751                 let old = this.atomic_op_immediate(&place, &rhs, op, neg, atomic)?;
752                 this.write_immediate(*old, dest)?; // old value is returned
753                 Ok(())
754             }
755         }
756     }
757
758     fn atomic_exchange(
759         &mut self,
760         args: &[OpTy<'tcx, Tag>],
761         dest: &PlaceTy<'tcx, Tag>,
762         atomic: AtomicRwOp,
763     ) -> InterpResult<'tcx> {
764         let this = self.eval_context_mut();
765
766         let &[ref place, ref new] = check_arg_count(args)?;
767         let place = this.deref_operand(place)?;
768         let new = this.read_scalar(new)?;
769
770         // Check alignment requirements. Atomics must always be aligned to their size,
771         // even if the type they wrap would be less aligned (e.g. AtomicU64 on 32bit must
772         // be 8-aligned).
773         let align = Align::from_bytes(place.layout.size.bytes()).unwrap();
774         this.memory.check_ptr_access_align(
775             place.ptr,
776             place.layout.size,
777             align,
778             CheckInAllocMsg::MemoryAccessTest,
779         )?;
780
781         let old = this.atomic_exchange_scalar(&place, new, atomic)?;
782         this.write_scalar(old, dest)?; // old value is returned
783         Ok(())
784     }
785
786     fn atomic_compare_exchange_impl(
787         &mut self,
788         args: &[OpTy<'tcx, Tag>],
789         dest: &PlaceTy<'tcx, Tag>,
790         success: AtomicRwOp,
791         fail: AtomicReadOp,
792         can_fail_spuriously: bool,
793     ) -> InterpResult<'tcx> {
794         let this = self.eval_context_mut();
795
796         let &[ref place, ref expect_old, ref new] = check_arg_count(args)?;
797         let place = this.deref_operand(place)?;
798         let expect_old = this.read_immediate(expect_old)?; // read as immediate for the sake of `binary_op()`
799         let new = this.read_scalar(new)?;
800
801         // Check alignment requirements. Atomics must always be aligned to their size,
802         // even if the type they wrap would be less aligned (e.g. AtomicU64 on 32bit must
803         // be 8-aligned).
804         let align = Align::from_bytes(place.layout.size.bytes()).unwrap();
805         this.memory.check_ptr_access_align(
806             place.ptr,
807             place.layout.size,
808             align,
809             CheckInAllocMsg::MemoryAccessTest,
810         )?;
811
812         let old = this.atomic_compare_exchange_scalar(
813             &place,
814             &expect_old,
815             new,
816             success,
817             fail,
818             can_fail_spuriously,
819         )?;
820
821         // Return old value.
822         this.write_immediate(old, dest)?;
823         Ok(())
824     }
825
826     fn atomic_compare_exchange(
827         &mut self,
828         args: &[OpTy<'tcx, Tag>],
829         dest: &PlaceTy<'tcx, Tag>,
830         success: AtomicRwOp,
831         fail: AtomicReadOp,
832     ) -> InterpResult<'tcx> {
833         self.atomic_compare_exchange_impl(args, dest, success, fail, false)
834     }
835
836     fn atomic_compare_exchange_weak(
837         &mut self,
838         args: &[OpTy<'tcx, Tag>],
839         dest: &PlaceTy<'tcx, Tag>,
840         success: AtomicRwOp,
841         fail: AtomicReadOp,
842     ) -> InterpResult<'tcx> {
843         self.atomic_compare_exchange_impl(args, dest, success, fail, true)
844     }
845
846     fn float_to_int_unchecked<F>(
847         &self,
848         f: F,
849         dest_ty: ty::Ty<'tcx>,
850     ) -> InterpResult<'tcx, Scalar<Tag>>
851     where
852         F: Float + Into<Scalar<Tag>>,
853     {
854         let this = self.eval_context_ref();
855
856         // Step 1: cut off the fractional part of `f`. The result of this is
857         // guaranteed to be precisely representable in IEEE floats.
858         let f = f.round_to_integral(Round::TowardZero).value;
859
860         // Step 2: Cast the truncated float to the target integer type and see if we lose any information in this step.
861         Ok(match dest_ty.kind() {
862             // Unsigned
863             ty::Uint(t) => {
864                 let size = Integer::from_uint_ty(this, *t).size();
865                 let res = f.to_u128(size.bits_usize());
866                 if res.status.is_empty() {
867                     // No status flags means there was no further rounding or other loss of precision.
868                     Scalar::from_uint(res.value, size)
869                 } else {
870                     // `f` was not representable in this integer type.
871                     throw_ub_format!(
872                         "`float_to_int_unchecked` intrinsic called on {} which cannot be represented in target type `{:?}`",
873                         f,
874                         dest_ty,
875                     );
876                 }
877             }
878             // Signed
879             ty::Int(t) => {
880                 let size = Integer::from_int_ty(this, *t).size();
881                 let res = f.to_i128(size.bits_usize());
882                 if res.status.is_empty() {
883                     // No status flags means there was no further rounding or other loss of precision.
884                     Scalar::from_int(res.value, size)
885                 } else {
886                     // `f` was not representable in this integer type.
887                     throw_ub_format!(
888                         "`float_to_int_unchecked` intrinsic called on {} which cannot be represented in target type `{:?}`",
889                         f,
890                         dest_ty,
891                     );
892                 }
893             }
894             // Nothing else
895             _ => bug!("`float_to_int_unchecked` called with non-int output type {:?}", dest_ty),
896         })
897     }
898 }