use rustc::mir::interpret::{InterpResult, Scalar, GlobalId, ConstValue};
use super::{
- Machine, PlaceTy, OpTy, InterpCx,
+ Machine, PlaceTy, OpTy, InterpCx, ImmTy,
};
mod caller_location;
span: Span,
instance: ty::Instance<'tcx>,
args: &[OpTy<'tcx, M::PointerTag>],
- dest: PlaceTy<'tcx, M::PointerTag>,
+ dest: Option<PlaceTy<'tcx, M::PointerTag>>,
) -> InterpResult<'tcx, bool> {
let substs = instance.substs;
- let intrinsic_name = &self.tcx.item_name(instance.def_id()).as_str()[..];
+ // We currently do not handle any diverging intrinsics.
+ let dest = match dest {
+ Some(dest) => dest,
+ None => return Ok(false)
+ };
+ let intrinsic_name = &*self.tcx.item_name(instance.def_id()).as_str();
+
match intrinsic_name {
"caller_location" => {
- let caller = self.tcx.sess.source_map().lookup_char_pos(span.lo());
+ let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span);
+ let caller = self.tcx.sess.source_map().lookup_char_pos(topmost.lo());
let location = self.alloc_caller_location(
Symbol::intern(&caller.file.name.to_string()),
caller.line as u32,
let result = Scalar::from_uint(truncated_bits, layout.size);
self.write_scalar(result, dest)?;
}
+
+ "ptr_offset_from" => {
+ let isize_layout = self.layout_of(self.tcx.types.isize)?;
+ let a = self.read_immediate(args[0])?.to_scalar()?;
+ let b = self.read_immediate(args[1])?.to_scalar()?;
+
+ // Special case: if both scalars are *equal integers*
+ // and not NULL, we pretend there is an allocation of size 0 right there,
+ // and their offset is 0. (There's never a valid object at NULL, making it an
+ // exception from the exception.)
+ // This is the dual to the special exception for offset-by-0
+ // in the inbounds pointer offset operation (see the Miri code, `src/operator.rs`).
+ if a.is_bits() && b.is_bits() {
+ let a = a.to_machine_usize(self)?;
+ let b = b.to_machine_usize(self)?;
+ if a == b && a != 0 {
+ self.write_scalar(Scalar::from_int(0, isize_layout.size), dest)?;
+ return Ok(true);
+ }
+ }
+
+ // General case: we need two pointers.
+ let a = self.force_ptr(a)?;
+ let b = self.force_ptr(b)?;
+ if a.alloc_id != b.alloc_id {
+ throw_ub_format!(
+ "ptr_offset_from cannot compute offset of pointers into different \
+ allocations.",
+ );
+ }
+ let usize_layout = self.layout_of(self.tcx.types.usize)?;
+ let a_offset = ImmTy::from_uint(a.offset.bytes(), usize_layout);
+ let b_offset = ImmTy::from_uint(b.offset.bytes(), usize_layout);
+ let (val, _overflowed, _ty) = self.overflowing_binary_op(
+ BinOp::Sub, a_offset, b_offset,
+ )?;
+ let pointee_layout = self.layout_of(substs.type_at(0))?;
+ let val = ImmTy::from_scalar(val, isize_layout);
+ let size = ImmTy::from_int(pointee_layout.size.bytes(), isize_layout);
+ self.exact_div(val, size, dest)?;
+ }
+
"transmute" => {
self.copy_op_transmute(args[0], dest)?;
}
Ok(true)
}
- /// "Intercept" a function call because we have something special to do for it.
+ /// "Intercept" a function call to a panic-related function
+ /// because we have something special to do for it.
/// Returns `true` if an intercept happened.
- pub fn hook_fn(
+ pub fn hook_panic_fn(
&mut self,
instance: ty::Instance<'tcx>,
args: &[OpTy<'tcx, M::PointerTag>],
return Ok(false);
}
}
+
+ pub fn exact_div(
+ &mut self,
+ a: ImmTy<'tcx, M::PointerTag>,
+ b: ImmTy<'tcx, M::PointerTag>,
+ dest: PlaceTy<'tcx, M::PointerTag>,
+ ) -> InterpResult<'tcx> {
+ // Performs an exact division, resulting in undefined behavior where
+ // `x % y != 0` or `y == 0` or `x == T::min_value() && y == -1`.
+ // First, check x % y != 0.
+ if self.binary_op(BinOp::Rem, a, b)?.to_bits()? != 0 {
+ // Then, check if `b` is -1, which is the "min_value / -1" case.
+ let minus1 = Scalar::from_int(-1, dest.layout.size);
+ let b = b.to_scalar().unwrap();
+ if b == minus1 {
+ throw_ub_format!("exact_div: result of dividing MIN by -1 cannot be represented")
+ } else {
+ throw_ub_format!(
+ "exact_div: {} cannot be divided by {} without remainder",
+ a.to_scalar().unwrap(),
+ b,
+ )
+ }
+ }
+ self.binop_ignore_overflow(BinOp::Div, a, b, dest)
+ }
}