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};
11 use helpers::check_arg_count;
14 MirOp(mir::BinOp, bool),
19 impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
20 pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
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();
30 if this.emulate_intrinsic(instance, args, ret)? {
34 // All supported intrinsics have a return place.
35 let intrinsic_name = &*this.tcx.item_name(instance.def_id()).as_str();
36 let (dest, ret) = match ret {
37 None => throw_unsup_format!("unimplemented (diverging) intrinsic: {}", intrinsic_name),
41 // Then handle terminating intrinsics.
42 match intrinsic_name {
43 // Miri overwriting CTFE intrinsics.
44 "ptr_guaranteed_eq" => {
45 let &[ref left, ref right] = check_arg_count(args)?;
46 let left = this.read_immediate(left)?;
47 let right = this.read_immediate(right)?;
48 this.binop_ignore_overflow(mir::BinOp::Eq, &left, &right, dest)?;
50 "ptr_guaranteed_ne" => {
51 let &[ref left, ref right] = check_arg_count(args)?;
52 let left = this.read_immediate(left)?;
53 let right = this.read_immediate(right)?;
54 this.binop_ignore_overflow(mir::BinOp::Ne, &left, &right, dest)?;
57 // Raw memory accesses
59 let &[ref place] = check_arg_count(args)?;
60 let place = this.deref_operand(place)?;
61 this.copy_op(&place.into(), dest)?;
64 let &[ref place, ref dest] = check_arg_count(args)?;
65 let place = this.deref_operand(place)?;
66 this.copy_op(dest, &place.into())?;
69 "write_bytes" | "volatile_set_memory" => {
70 let &[ref ptr, ref val_byte, ref count] = check_arg_count(args)?;
71 let ty = instance.substs.type_at(0);
72 let ty_layout = this.layout_of(ty)?;
73 let val_byte = this.read_scalar(val_byte)?.to_u8()?;
74 let ptr = this.read_pointer(ptr)?;
75 let count = this.read_scalar(count)?.to_machine_usize(this)?;
76 let byte_count = ty_layout.size.checked_mul(count, this).ok_or_else(|| {
77 err_ub_format!("overflow computing total size of `{}`", intrinsic_name)
80 .write_bytes(ptr, iter::repeat(val_byte).take(byte_count.bytes() as usize))?;
83 // Floating-point operations
99 let &[ref f] = check_arg_count(args)?;
100 // FIXME: Using host floats.
101 let f = f32::from_bits(this.read_scalar(f)?.to_u32()?);
102 let f = match intrinsic_name {
104 "fabsf32" => f.abs(),
106 "sqrtf32" => f.sqrt(),
108 "exp2f32" => f.exp2(),
110 "log10f32" => f.log10(),
111 "log2f32" => f.log2(),
112 "floorf32" => f.floor(),
113 "ceilf32" => f.ceil(),
114 "truncf32" => f.trunc(),
115 "roundf32" => f.round(),
118 this.write_scalar(Scalar::from_u32(f.to_bits()), dest)?;
136 let &[ref f] = check_arg_count(args)?;
137 // FIXME: Using host floats.
138 let f = f64::from_bits(this.read_scalar(f)?.to_u64()?);
139 let f = match intrinsic_name {
141 "fabsf64" => f.abs(),
143 "sqrtf64" => f.sqrt(),
145 "exp2f64" => f.exp2(),
147 "log10f64" => f.log10(),
148 "log2f64" => f.log2(),
149 "floorf64" => f.floor(),
150 "ceilf64" => f.ceil(),
151 "truncf64" => f.trunc(),
152 "roundf64" => f.round(),
155 this.write_scalar(Scalar::from_u64(f.to_bits()), dest)?;
165 let &[ref a, ref b] = check_arg_count(args)?;
166 let a = this.read_immediate(a)?;
167 let b = this.read_immediate(b)?;
168 let op = match intrinsic_name {
169 "fadd_fast" => mir::BinOp::Add,
170 "fsub_fast" => mir::BinOp::Sub,
171 "fmul_fast" => mir::BinOp::Mul,
172 "fdiv_fast" => mir::BinOp::Div,
173 "frem_fast" => mir::BinOp::Rem,
176 let float_finite = |x: ImmTy<'tcx, _>| -> InterpResult<'tcx, bool> {
177 Ok(match x.layout.ty.kind() {
178 ty::Float(FloatTy::F32) => x.to_scalar()?.to_f32()?.is_finite(),
179 ty::Float(FloatTy::F64) => x.to_scalar()?.to_f64()?.is_finite(),
181 "`{}` called with non-float input type {:?}",
187 match (float_finite(a)?, float_finite(b)?) {
188 (false, false) => throw_ub_format!(
189 "`{}` intrinsic called with non-finite value as both parameters",
192 (false, _) => throw_ub_format!(
193 "`{}` intrinsic called with non-finite value as first parameter",
196 (_, false) => throw_ub_format!(
197 "`{}` intrinsic called with non-finite value as second parameter",
202 this.binop_ignore_overflow(op, &a, &b, dest)?;
210 let &[ref a, ref b] = check_arg_count(args)?;
211 let a = this.read_scalar(a)?.to_f32()?;
212 let b = this.read_scalar(b)?.to_f32()?;
213 let res = match intrinsic_name {
214 "minnumf32" => a.min(b),
215 "maxnumf32" => a.max(b),
216 "copysignf32" => a.copy_sign(b),
219 this.write_scalar(Scalar::from_f32(res), dest)?;
227 let &[ref a, ref b] = check_arg_count(args)?;
228 let a = this.read_scalar(a)?.to_f64()?;
229 let b = this.read_scalar(b)?.to_f64()?;
230 let res = match intrinsic_name {
231 "minnumf64" => a.min(b),
232 "maxnumf64" => a.max(b),
233 "copysignf64" => a.copy_sign(b),
236 this.write_scalar(Scalar::from_f64(res), dest)?;
240 let &[ref f, ref f2] = check_arg_count(args)?;
241 // FIXME: Using host floats.
242 let f = f32::from_bits(this.read_scalar(f)?.to_u32()?);
243 let f2 = f32::from_bits(this.read_scalar(f2)?.to_u32()?);
244 this.write_scalar(Scalar::from_u32(f.powf(f2).to_bits()), dest)?;
248 let &[ref f, ref f2] = check_arg_count(args)?;
249 // FIXME: Using host floats.
250 let f = f64::from_bits(this.read_scalar(f)?.to_u64()?);
251 let f2 = f64::from_bits(this.read_scalar(f2)?.to_u64()?);
252 this.write_scalar(Scalar::from_u64(f.powf(f2).to_bits()), dest)?;
256 let &[ref a, ref b, ref c] = check_arg_count(args)?;
257 let a = this.read_scalar(a)?.to_f32()?;
258 let b = this.read_scalar(b)?.to_f32()?;
259 let c = this.read_scalar(c)?.to_f32()?;
260 let res = a.mul_add(b, c).value;
261 this.write_scalar(Scalar::from_f32(res), dest)?;
265 let &[ref a, ref b, ref c] = check_arg_count(args)?;
266 let a = this.read_scalar(a)?.to_f64()?;
267 let b = this.read_scalar(b)?.to_f64()?;
268 let c = this.read_scalar(c)?.to_f64()?;
269 let res = a.mul_add(b, c).value;
270 this.write_scalar(Scalar::from_f64(res), dest)?;
274 let &[ref f, ref i] = check_arg_count(args)?;
275 // FIXME: Using host floats.
276 let f = f32::from_bits(this.read_scalar(f)?.to_u32()?);
277 let i = this.read_scalar(i)?.to_i32()?;
278 this.write_scalar(Scalar::from_u32(f.powi(i).to_bits()), dest)?;
282 let &[ref f, ref i] = check_arg_count(args)?;
283 // FIXME: Using host floats.
284 let f = f64::from_bits(this.read_scalar(f)?.to_u64()?);
285 let i = this.read_scalar(i)?.to_i32()?;
286 this.write_scalar(Scalar::from_u64(f.powi(i).to_bits()), dest)?;
289 "float_to_int_unchecked" => {
290 let &[ref val] = check_arg_count(args)?;
291 let val = this.read_immediate(val)?;
293 let res = match val.layout.ty.kind() {
294 ty::Float(FloatTy::F32) =>
295 this.float_to_int_unchecked(val.to_scalar()?.to_f32()?, dest.layout.ty)?,
296 ty::Float(FloatTy::F64) =>
297 this.float_to_int_unchecked(val.to_scalar()?.to_f64()?, dest.layout.ty)?,
300 "`float_to_int_unchecked` called with non-float input type {:?}",
305 this.write_scalar(res, dest)?;
317 let &[ref left, ref right] = check_arg_count(args)?;
318 let (left, left_len) = this.operand_to_simd(left)?;
319 let (right, right_len) = this.operand_to_simd(right)?;
320 let (dest, dest_len) = this.place_to_simd(dest)?;
322 assert_eq!(dest_len, left_len);
323 assert_eq!(dest_len, right_len);
325 let op = match intrinsic_name {
326 "simd_add" => mir::BinOp::Add,
327 "simd_sub" => mir::BinOp::Sub,
328 "simd_mul" => mir::BinOp::Mul,
329 "simd_div" => mir::BinOp::Div,
330 "simd_rem" => mir::BinOp::Rem,
331 "simd_shl" => mir::BinOp::Shl,
332 "simd_shr" => mir::BinOp::Shr,
336 for i in 0..dest_len {
337 let left = this.read_immediate(&this.mplace_index(&left, i)?.into())?;
338 let right = this.read_immediate(&this.mplace_index(&right, i)?.into())?;
339 let dest = this.mplace_index(&dest, i)?;
340 let (val, overflowed, ty) = this.overflowing_binary_op(op, &left, &right)?;
341 assert_eq!(ty, dest.layout.ty);
342 if matches!(op, mir::BinOp::Shl | mir::BinOp::Shr) {
343 // Shifts have extra UB as SIMD operations that the MIR binop does not have.
344 // See <https://github.com/rust-lang/rust/issues/91237>.
346 let r_val = right.to_scalar()?.to_bits(right.layout.size)?;
347 throw_ub_format!("overflowing shift by {} in `{}` in SIMD lane {}", r_val, intrinsic_name, i);
350 this.write_scalar(val, &dest.into())?;
355 "atomic_load" => this.atomic_load(args, dest, AtomicReadOp::SeqCst)?,
356 "atomic_load_relaxed" => this.atomic_load(args, dest, AtomicReadOp::Relaxed)?,
357 "atomic_load_acq" => this.atomic_load(args, dest, AtomicReadOp::Acquire)?,
359 "atomic_store" => this.atomic_store(args, AtomicWriteOp::SeqCst)?,
360 "atomic_store_relaxed" => this.atomic_store(args, AtomicWriteOp::Relaxed)?,
361 "atomic_store_rel" => this.atomic_store(args, AtomicWriteOp::Release)?,
363 "atomic_fence_acq" => this.atomic_fence(args, AtomicFenceOp::Acquire)?,
364 "atomic_fence_rel" => this.atomic_fence(args, AtomicFenceOp::Release)?,
365 "atomic_fence_acqrel" => this.atomic_fence(args, AtomicFenceOp::AcqRel)?,
366 "atomic_fence" => this.atomic_fence(args, AtomicFenceOp::SeqCst)?,
368 "atomic_singlethreadfence_acq" => this.compiler_fence(args, AtomicFenceOp::Acquire)?,
369 "atomic_singlethreadfence_rel" => this.compiler_fence(args, AtomicFenceOp::Release)?,
370 "atomic_singlethreadfence_acqrel" =>
371 this.compiler_fence(args, AtomicFenceOp::AcqRel)?,
372 "atomic_singlethreadfence" => this.compiler_fence(args, AtomicFenceOp::SeqCst)?,
374 "atomic_xchg" => this.atomic_exchange(args, dest, AtomicRwOp::SeqCst)?,
375 "atomic_xchg_acq" => this.atomic_exchange(args, dest, AtomicRwOp::Acquire)?,
376 "atomic_xchg_rel" => this.atomic_exchange(args, dest, AtomicRwOp::Release)?,
377 "atomic_xchg_acqrel" => this.atomic_exchange(args, dest, AtomicRwOp::AcqRel)?,
378 "atomic_xchg_relaxed" => this.atomic_exchange(args, dest, AtomicRwOp::Relaxed)?,
382 this.atomic_compare_exchange(args, dest, AtomicRwOp::SeqCst, AtomicReadOp::SeqCst)?,
384 "atomic_cxchg_acq" =>
385 this.atomic_compare_exchange(args, dest, AtomicRwOp::Acquire, AtomicReadOp::Acquire)?,
387 "atomic_cxchg_rel" =>
388 this.atomic_compare_exchange(args, dest, AtomicRwOp::Release, AtomicReadOp::Relaxed)?,
390 "atomic_cxchg_acqrel" =>
391 this.atomic_compare_exchange(args, dest, AtomicRwOp::AcqRel, AtomicReadOp::Acquire)?,
393 "atomic_cxchg_relaxed" =>
394 this.atomic_compare_exchange(args, dest, AtomicRwOp::Relaxed, AtomicReadOp::Relaxed)?,
396 "atomic_cxchg_acq_failrelaxed" =>
397 this.atomic_compare_exchange(args, dest, AtomicRwOp::Acquire, AtomicReadOp::Relaxed)?,
399 "atomic_cxchg_acqrel_failrelaxed" =>
400 this.atomic_compare_exchange(args, dest, AtomicRwOp::AcqRel, AtomicReadOp::Relaxed)?,
402 "atomic_cxchg_failrelaxed" =>
403 this.atomic_compare_exchange(args, dest, AtomicRwOp::SeqCst, AtomicReadOp::Relaxed)?,
405 "atomic_cxchg_failacq" =>
406 this.atomic_compare_exchange(args, dest, AtomicRwOp::SeqCst, AtomicReadOp::Acquire)?,
409 "atomic_cxchgweak" =>
410 this.atomic_compare_exchange_weak(args, dest, AtomicRwOp::SeqCst, AtomicReadOp::SeqCst)?,
412 "atomic_cxchgweak_acq" =>
413 this.atomic_compare_exchange_weak(args, dest, AtomicRwOp::Acquire, AtomicReadOp::Acquire)?,
415 "atomic_cxchgweak_rel" =>
416 this.atomic_compare_exchange_weak(args, dest, AtomicRwOp::Release, AtomicReadOp::Relaxed)?,
418 "atomic_cxchgweak_acqrel" =>
419 this.atomic_compare_exchange_weak(args, dest, AtomicRwOp::AcqRel, AtomicReadOp::Acquire)?,
421 "atomic_cxchgweak_relaxed" =>
422 this.atomic_compare_exchange_weak(args, dest, AtomicRwOp::Relaxed, AtomicReadOp::Relaxed)?,
424 "atomic_cxchgweak_acq_failrelaxed" =>
425 this.atomic_compare_exchange_weak(args, dest, AtomicRwOp::Acquire, AtomicReadOp::Relaxed)?,
427 "atomic_cxchgweak_acqrel_failrelaxed" =>
428 this.atomic_compare_exchange_weak(args, dest, AtomicRwOp::AcqRel, AtomicReadOp::Relaxed)?,
430 "atomic_cxchgweak_failrelaxed" =>
431 this.atomic_compare_exchange_weak(args, dest, AtomicRwOp::SeqCst, AtomicReadOp::Relaxed)?,
433 "atomic_cxchgweak_failacq" =>
434 this.atomic_compare_exchange_weak(args, dest, AtomicRwOp::SeqCst, AtomicReadOp::Acquire)?,
438 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitOr, false), AtomicRwOp::SeqCst)?,
441 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitOr, false), AtomicRwOp::Acquire)?,
444 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitOr, false), AtomicRwOp::Release)?,
446 "atomic_or_acqrel" =>
447 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitOr, false), AtomicRwOp::AcqRel)?,
449 "atomic_or_relaxed" =>
450 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitOr, false), AtomicRwOp::Relaxed)?,
453 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitXor, false), AtomicRwOp::SeqCst)?,
456 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitXor, false), AtomicRwOp::Acquire)?,
459 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitXor, false), AtomicRwOp::Release)?,
461 "atomic_xor_acqrel" =>
462 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitXor, false), AtomicRwOp::AcqRel)?,
464 "atomic_xor_relaxed" =>
465 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitXor, false), AtomicRwOp::Relaxed)?,
468 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, false), AtomicRwOp::SeqCst)?,
471 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, false), AtomicRwOp::Acquire)?,
474 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, false), AtomicRwOp::Release)?,
476 "atomic_and_acqrel" =>
477 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, false), AtomicRwOp::AcqRel)?,
479 "atomic_and_relaxed" =>
480 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, false), AtomicRwOp::Relaxed)?,
483 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, true), AtomicRwOp::SeqCst)?,
486 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, true), AtomicRwOp::Acquire)?,
489 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, true), AtomicRwOp::Release)?,
491 "atomic_nand_acqrel" =>
492 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, true), AtomicRwOp::AcqRel)?,
494 "atomic_nand_relaxed" =>
495 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, true), AtomicRwOp::Relaxed)?,
498 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::Add, false), AtomicRwOp::SeqCst)?,
501 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::Add, false), AtomicRwOp::Acquire)?,
504 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::Add, false), AtomicRwOp::Release)?,
506 "atomic_xadd_acqrel" =>
507 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::Add, false), AtomicRwOp::AcqRel)?,
509 "atomic_xadd_relaxed" =>
510 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::Add, false), AtomicRwOp::Relaxed)?,
513 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::Sub, false), AtomicRwOp::SeqCst)?,
516 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::Sub, false), AtomicRwOp::Acquire)?,
519 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::Sub, false), AtomicRwOp::Release)?,
521 "atomic_xsub_acqrel" =>
522 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::Sub, false), AtomicRwOp::AcqRel)?,
524 "atomic_xsub_relaxed" =>
525 this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::Sub, false), AtomicRwOp::Relaxed)?,
526 "atomic_min" => this.atomic_op(args, dest, AtomicOp::Min, AtomicRwOp::SeqCst)?,
527 "atomic_min_acq" => this.atomic_op(args, dest, AtomicOp::Min, AtomicRwOp::Acquire)?,
528 "atomic_min_rel" => this.atomic_op(args, dest, AtomicOp::Min, AtomicRwOp::Release)?,
529 "atomic_min_acqrel" => this.atomic_op(args, dest, AtomicOp::Min, AtomicRwOp::AcqRel)?,
530 "atomic_min_relaxed" =>
531 this.atomic_op(args, dest, AtomicOp::Min, AtomicRwOp::Relaxed)?,
532 "atomic_max" => this.atomic_op(args, dest, AtomicOp::Max, AtomicRwOp::SeqCst)?,
533 "atomic_max_acq" => this.atomic_op(args, dest, AtomicOp::Max, AtomicRwOp::Acquire)?,
534 "atomic_max_rel" => this.atomic_op(args, dest, AtomicOp::Max, AtomicRwOp::Release)?,
535 "atomic_max_acqrel" => this.atomic_op(args, dest, AtomicOp::Max, AtomicRwOp::AcqRel)?,
536 "atomic_max_relaxed" =>
537 this.atomic_op(args, dest, AtomicOp::Max, AtomicRwOp::Relaxed)?,
538 "atomic_umin" => this.atomic_op(args, dest, AtomicOp::Min, AtomicRwOp::SeqCst)?,
539 "atomic_umin_acq" => this.atomic_op(args, dest, AtomicOp::Min, AtomicRwOp::Acquire)?,
540 "atomic_umin_rel" => this.atomic_op(args, dest, AtomicOp::Min, AtomicRwOp::Release)?,
541 "atomic_umin_acqrel" =>
542 this.atomic_op(args, dest, AtomicOp::Min, AtomicRwOp::AcqRel)?,
543 "atomic_umin_relaxed" =>
544 this.atomic_op(args, dest, AtomicOp::Min, AtomicRwOp::Relaxed)?,
545 "atomic_umax" => this.atomic_op(args, dest, AtomicOp::Max, AtomicRwOp::SeqCst)?,
546 "atomic_umax_acq" => this.atomic_op(args, dest, AtomicOp::Max, AtomicRwOp::Acquire)?,
547 "atomic_umax_rel" => this.atomic_op(args, dest, AtomicOp::Max, AtomicRwOp::Release)?,
548 "atomic_umax_acqrel" =>
549 this.atomic_op(args, dest, AtomicOp::Max, AtomicRwOp::AcqRel)?,
550 "atomic_umax_relaxed" =>
551 this.atomic_op(args, dest, AtomicOp::Max, AtomicRwOp::Relaxed)?,
553 // Query type information
554 "assert_zero_valid" | "assert_uninit_valid" => {
555 let &[] = check_arg_count(args)?;
556 let ty = instance.substs.type_at(0);
557 let layout = this.layout_of(ty)?;
558 // Abort here because the caller might not be panic safe.
559 if layout.abi.is_uninhabited() {
560 // Use this message even for the other intrinsics, as that's what codegen does
561 throw_machine_stop!(TerminationInfo::Abort(format!(
562 "aborted execution: attempted to instantiate uninhabited type `{}`",
566 if intrinsic_name == "assert_zero_valid"
567 && !layout.might_permit_raw_init(this, /*zero:*/ true)
569 throw_machine_stop!(TerminationInfo::Abort(format!(
570 "aborted execution: attempted to zero-initialize type `{}`, which is invalid",
574 if intrinsic_name == "assert_uninit_valid"
575 && !layout.might_permit_raw_init(this, /*zero:*/ false)
577 throw_machine_stop!(TerminationInfo::Abort(format!(
578 "aborted execution: attempted to leave type `{}` uninitialized, which is invalid",
586 let &[ref num, ref denom] = check_arg_count(args)?;
587 this.exact_div(&this.read_immediate(num)?, &this.read_immediate(denom)?, dest)?;
590 "try" => return this.handle_try(args, dest, ret),
593 let &[] = check_arg_count(args)?;
594 // normally this would raise a SIGTRAP, which aborts if no debugger is connected
595 throw_machine_stop!(TerminationInfo::Abort("Trace/breakpoint trap".to_string()))
598 name => throw_unsup_format!("unimplemented intrinsic: {}", name),
601 trace!("{:?}", this.dump_place(**dest));
602 this.go_to_block(ret);
608 args: &[OpTy<'tcx, Tag>],
609 dest: &PlaceTy<'tcx, Tag>,
610 atomic: AtomicReadOp,
611 ) -> InterpResult<'tcx> {
612 let this = self.eval_context_mut();
614 let &[ref place] = check_arg_count(args)?;
615 let place = this.deref_operand(place)?;
617 // make sure it fits into a scalar; otherwise it cannot be atomic
618 let val = this.read_scalar_atomic(&place, atomic)?;
620 // Check alignment requirements. Atomics must always be aligned to their size,
621 // even if the type they wrap would be less aligned (e.g. AtomicU64 on 32bit must
623 let align = Align::from_bytes(place.layout.size.bytes()).unwrap();
624 this.memory.check_ptr_access_align(
628 CheckInAllocMsg::MemoryAccessTest,
630 // Perform regular access.
631 this.write_scalar(val, dest)?;
637 args: &[OpTy<'tcx, Tag>],
638 atomic: AtomicWriteOp,
639 ) -> InterpResult<'tcx> {
640 let this = self.eval_context_mut();
642 let &[ref place, ref val] = check_arg_count(args)?;
643 let place = this.deref_operand(place)?;
644 let val = this.read_scalar(val)?; // make sure it fits into a scalar; otherwise it cannot be atomic
646 // Check alignment requirements. Atomics must always be aligned to their size,
647 // even if the type they wrap would be less aligned (e.g. AtomicU64 on 32bit must
649 let align = Align::from_bytes(place.layout.size.bytes()).unwrap();
650 this.memory.check_ptr_access_align(
654 CheckInAllocMsg::MemoryAccessTest,
657 // Perform atomic store
658 this.write_scalar_atomic(val, &place, atomic)?;
664 args: &[OpTy<'tcx, Tag>],
665 atomic: AtomicFenceOp,
666 ) -> InterpResult<'tcx> {
667 let &[] = check_arg_count(args)?;
669 //FIXME: compiler fences are currently ignored
675 args: &[OpTy<'tcx, Tag>],
676 atomic: AtomicFenceOp,
677 ) -> InterpResult<'tcx> {
678 let this = self.eval_context_mut();
679 let &[] = check_arg_count(args)?;
680 this.validate_atomic_fence(atomic)?;
686 args: &[OpTy<'tcx, Tag>],
687 dest: &PlaceTy<'tcx, Tag>,
690 ) -> InterpResult<'tcx> {
691 let this = self.eval_context_mut();
693 let &[ref place, ref rhs] = check_arg_count(args)?;
694 let place = this.deref_operand(place)?;
696 if !place.layout.ty.is_integral() {
697 bug!("Atomic arithmetic operations only work on integer types");
699 let rhs = this.read_immediate(rhs)?;
701 // Check alignment requirements. Atomics must always be aligned to their size,
702 // even if the type they wrap would be less aligned (e.g. AtomicU64 on 32bit must
704 let align = Align::from_bytes(place.layout.size.bytes()).unwrap();
705 this.memory.check_ptr_access_align(
709 CheckInAllocMsg::MemoryAccessTest,
714 let old = this.atomic_min_max_scalar(&place, rhs, true, atomic)?;
715 this.write_immediate(*old, &dest)?; // old value is returned
719 let old = this.atomic_min_max_scalar(&place, rhs, false, atomic)?;
720 this.write_immediate(*old, &dest)?; // old value is returned
723 AtomicOp::MirOp(op, neg) => {
724 let old = this.atomic_op_immediate(&place, &rhs, op, neg, atomic)?;
725 this.write_immediate(*old, dest)?; // old value is returned
733 args: &[OpTy<'tcx, Tag>],
734 dest: &PlaceTy<'tcx, Tag>,
736 ) -> InterpResult<'tcx> {
737 let this = self.eval_context_mut();
739 let &[ref place, ref new] = check_arg_count(args)?;
740 let place = this.deref_operand(place)?;
741 let new = this.read_scalar(new)?;
743 // Check alignment requirements. Atomics must always be aligned to their size,
744 // even if the type they wrap would be less aligned (e.g. AtomicU64 on 32bit must
746 let align = Align::from_bytes(place.layout.size.bytes()).unwrap();
747 this.memory.check_ptr_access_align(
751 CheckInAllocMsg::MemoryAccessTest,
754 let old = this.atomic_exchange_scalar(&place, new, atomic)?;
755 this.write_scalar(old, dest)?; // old value is returned
759 fn atomic_compare_exchange_impl(
761 args: &[OpTy<'tcx, Tag>],
762 dest: &PlaceTy<'tcx, Tag>,
765 can_fail_spuriously: bool,
766 ) -> InterpResult<'tcx> {
767 let this = self.eval_context_mut();
769 let &[ref place, ref expect_old, ref new] = check_arg_count(args)?;
770 let place = this.deref_operand(place)?;
771 let expect_old = this.read_immediate(expect_old)?; // read as immediate for the sake of `binary_op()`
772 let new = this.read_scalar(new)?;
774 // Check alignment requirements. Atomics must always be aligned to their size,
775 // even if the type they wrap would be less aligned (e.g. AtomicU64 on 32bit must
777 let align = Align::from_bytes(place.layout.size.bytes()).unwrap();
778 this.memory.check_ptr_access_align(
782 CheckInAllocMsg::MemoryAccessTest,
785 let old = this.atomic_compare_exchange_scalar(
795 this.write_immediate(old, dest)?;
799 fn atomic_compare_exchange(
801 args: &[OpTy<'tcx, Tag>],
802 dest: &PlaceTy<'tcx, Tag>,
805 ) -> InterpResult<'tcx> {
806 self.atomic_compare_exchange_impl(args, dest, success, fail, false)
809 fn atomic_compare_exchange_weak(
811 args: &[OpTy<'tcx, Tag>],
812 dest: &PlaceTy<'tcx, Tag>,
815 ) -> InterpResult<'tcx> {
816 self.atomic_compare_exchange_impl(args, dest, success, fail, true)
819 fn float_to_int_unchecked<F>(
822 dest_ty: ty::Ty<'tcx>,
823 ) -> InterpResult<'tcx, Scalar<Tag>>
825 F: Float + Into<Scalar<Tag>>,
827 let this = self.eval_context_ref();
829 // Step 1: cut off the fractional part of `f`. The result of this is
830 // guaranteed to be precisely representable in IEEE floats.
831 let f = f.round_to_integral(Round::TowardZero).value;
833 // Step 2: Cast the truncated float to the target integer type and see if we lose any information in this step.
834 Ok(match dest_ty.kind() {
837 let size = Integer::from_uint_ty(this, *t).size();
838 let res = f.to_u128(size.bits_usize());
839 if res.status.is_empty() {
840 // No status flags means there was no further rounding or other loss of precision.
841 Scalar::from_uint(res.value, size)
843 // `f` was not representable in this integer type.
845 "`float_to_int_unchecked` intrinsic called on {} which cannot be represented in target type `{:?}`",
853 let size = Integer::from_int_ty(this, *t).size();
854 let res = f.to_i128(size.bits_usize());
855 if res.status.is_empty() {
856 // No status flags means there was no further rounding or other loss of precision.
857 Scalar::from_int(res.value, size)
859 // `f` was not representable in this integer type.
861 "`float_to_int_unchecked` intrinsic called on {} which cannot be represented in target type `{:?}`",
868 _ => bug!("`float_to_int_unchecked` called with non-int output type {:?}", dest_ty),