Skip to content

refactor goto_block and also add unwind_to_block #66646

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Nov 27, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 6 additions & 9 deletions src/librustc_mir/const_eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -323,8 +323,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
ecx: &mut InterpCx<'mir, 'tcx, Self>,
instance: ty::Instance<'tcx>,
args: &[OpTy<'tcx>],
dest: Option<PlaceTy<'tcx>>,
ret: Option<mir::BasicBlock>,
ret: Option<(PlaceTy<'tcx>, mir::BasicBlock)>,
_unwind: Option<mir::BasicBlock> // unwinding is not supported in consts
) -> InterpResult<'tcx, Option<&'mir mir::Body<'tcx>>> {
debug!("eval_fn_call: {:?}", instance);
Expand All @@ -337,8 +336,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
// Some functions we support even if they are non-const -- but avoid testing
// that for const fn! We certainly do *not* want to actually call the fn
// though, so be sure we return here.
return if ecx.hook_panic_fn(instance, args, dest)? {
ecx.goto_block(ret)?; // fully evaluated and done
return if ecx.hook_panic_fn(instance, args, ret)? {
Ok(None)
} else {
throw_unsup_format!("calling non-const function `{}`", instance)
Expand All @@ -364,8 +362,8 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
_ecx: &mut InterpCx<'mir, 'tcx, Self>,
fn_val: !,
_args: &[OpTy<'tcx>],
_dest: Option<PlaceTy<'tcx>>,
_ret: Option<mir::BasicBlock>,
_ret: Option<(PlaceTy<'tcx>, mir::BasicBlock)>,
_unwind: Option<mir::BasicBlock>
) -> InterpResult<'tcx> {
match fn_val {}
}
Expand All @@ -375,11 +373,10 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
span: Span,
instance: ty::Instance<'tcx>,
args: &[OpTy<'tcx>],
dest: Option<PlaceTy<'tcx>>,
_ret: Option<mir::BasicBlock>,
ret: Option<(PlaceTy<'tcx>, mir::BasicBlock)>,
_unwind: Option<mir::BasicBlock>
) -> InterpResult<'tcx> {
if ecx.emulate_intrinsic(span, instance, args, dest)? {
if ecx.emulate_intrinsic(span, instance, args, ret)? {
return Ok(());
}
// An intrinsic that we do not support
Expand Down
38 changes: 33 additions & 5 deletions src/librustc_mir/interpret/eval_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -553,6 +553,37 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
}
}

/// Jump to the given block.
#[inline]
pub fn go_to_block(&mut self, target: mir::BasicBlock) {
let frame = self.frame_mut();
frame.block = Some(target);
frame.stmt = 0;
}

/// *Return* to the given `target` basic block.
/// Do *not* use for unwinding! Use `unwind_to_block` instead.
///
/// If `target` is `None`, that indicates the function cannot return, so we raise UB.
pub fn return_to_block(&mut self, target: Option<mir::BasicBlock>) -> InterpResult<'tcx> {
if let Some(target) = target {
Ok(self.go_to_block(target))
} else {
throw_ub!(Unreachable)
}
}

/// *Unwind* to the given `target` basic block.
/// Do *not* use for returning! Use `return_to_block` instead.
///
/// If `target` is `None`, that indicates the function does not need cleanup during
/// unwinding, and we will just keep propagating that upwards.
pub fn unwind_to_block(&mut self, target: Option<mir::BasicBlock>) {
let frame = self.frame_mut();
frame.block = target;
frame.stmt = 0;
}

/// Pops the current frame from the stack, deallocating the
/// memory for allocated locals.
///
Expand Down Expand Up @@ -628,10 +659,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
if cur_unwinding {
// Follow the unwind edge.
let unwind = next_block.expect("Encounted StackPopCleanup::None when unwinding!");
let next_frame = self.frame_mut();
// If `unwind` is `None`, we'll leave that function immediately again.
next_frame.block = unwind;
next_frame.stmt = 0;
self.unwind_to_block(unwind);
} else {
// Follow the normal return edge.
// Validate the return value. Do this after deallocating so that we catch dangling
Expand All @@ -658,7 +686,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {

// Jump to new block -- *after* validation so that the spans make more sense.
if let Some(ret) = next_block {
self.goto_block(ret)?;
self.return_to_block(ret)?;
}
}

Expand Down
75 changes: 44 additions & 31 deletions src/librustc_mir/interpret/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ use rustc::ty::layout::{LayoutOf, Primitive, Size};
use rustc::ty::subst::SubstsRef;
use rustc::hir::def_id::DefId;
use rustc::ty::TyCtxt;
use rustc::mir::BinOp;
use rustc::mir::interpret::{InterpResult, Scalar, GlobalId, ConstValue};
use rustc::mir::{
self, BinOp,
interpret::{InterpResult, Scalar, GlobalId, ConstValue}
};

use super::{
Machine, PlaceTy, OpTy, InterpCx, ImmTy,
Expand Down Expand Up @@ -91,16 +93,20 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
span: Span,
instance: ty::Instance<'tcx>,
args: &[OpTy<'tcx, M::PointerTag>],
dest: Option<PlaceTy<'tcx, M::PointerTag>>,
ret: Option<(PlaceTy<'tcx, M::PointerTag>, mir::BasicBlock)>,
) -> 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)
// We currently do not handle any intrinsics that are *allowed* to diverge,
// but `transmute` could lack a return place in case of UB.
let (dest, ret) = match ret {
Some(p) => p,
None => match intrinsic_name {
"transmute" => throw_ub!(Unreachable),
_ => return Ok(false),
}
};
let intrinsic_name = &*self.tcx.item_name(instance.def_id()).as_str();

match intrinsic_name {
"caller_location" => {
Expand Down Expand Up @@ -268,34 +274,39 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// 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() {
//
// Control flow is weird because we cannot early-return (to reach the
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we have a bunch of situations like that in miri... Maybe we should consider using try blocks at some point

// `go_to_block` at the end).
let done = 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);
}
}
true
} else { false }
} else { false };

// 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.",
);
if !done {
// 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)?;
}
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" => {
Expand Down Expand Up @@ -350,6 +361,8 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
_ => return Ok(false),
}

self.dump_place(*dest);
self.go_to_block(ret);
Ok(true)
}

Expand All @@ -360,7 +373,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
&mut self,
instance: ty::Instance<'tcx>,
args: &[OpTy<'tcx, M::PointerTag>],
_dest: Option<PlaceTy<'tcx, M::PointerTag>>,
_ret: Option<(PlaceTy<'tcx, M::PointerTag>, mir::BasicBlock)>,
) -> InterpResult<'tcx, bool> {
let def_id = instance.def_id();
if Some(def_id) == self.tcx.lang_items().panic_fn() {
Expand Down
20 changes: 9 additions & 11 deletions src/librustc_mir/interpret/machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ pub trait Machine<'mir, 'tcx>: Sized {
/// Returns either the mir to use for the call, or `None` if execution should
/// just proceed (which usually means this hook did all the work that the
/// called function should usually have done). In the latter case, it is
/// this hook's responsibility to call `goto_block(ret)` to advance the instruction pointer!
/// this hook's responsibility to advance the instruction pointer!
/// (This is to support functions like `__rust_maybe_catch_panic` that neither find a MIR
/// nor just jump to `ret`, but instead push their own stack frame.)
/// Passing `dest`and `ret` in the same `Option` proved very annoying when only one of them
Expand All @@ -150,30 +150,28 @@ pub trait Machine<'mir, 'tcx>: Sized {
ecx: &mut InterpCx<'mir, 'tcx, Self>,
instance: ty::Instance<'tcx>,
args: &[OpTy<'tcx, Self::PointerTag>],
dest: Option<PlaceTy<'tcx, Self::PointerTag>>,
ret: Option<mir::BasicBlock>,
unwind: Option<mir::BasicBlock>
ret: Option<(PlaceTy<'tcx, Self::PointerTag>, mir::BasicBlock)>,
unwind: Option<mir::BasicBlock>,
) -> InterpResult<'tcx, Option<&'mir mir::Body<'tcx>>>;

/// Execute `fn_val`. it is the hook's responsibility to advance the instruction
/// Execute `fn_val`. It is the hook's responsibility to advance the instruction
/// pointer as appropriate.
fn call_extra_fn(
ecx: &mut InterpCx<'mir, 'tcx, Self>,
fn_val: Self::ExtraFnVal,
args: &[OpTy<'tcx, Self::PointerTag>],
dest: Option<PlaceTy<'tcx, Self::PointerTag>>,
ret: Option<mir::BasicBlock>,
ret: Option<(PlaceTy<'tcx, Self::PointerTag>, mir::BasicBlock)>,
unwind: Option<mir::BasicBlock>,
) -> InterpResult<'tcx>;

/// Directly process an intrinsic without pushing a stack frame.
/// If this returns successfully, the engine will take care of jumping to the next block.
/// Directly process an intrinsic without pushing a stack frame. It is the hook's
/// responsibility to advance the instruction pointer as appropriate.
fn call_intrinsic(
ecx: &mut InterpCx<'mir, 'tcx, Self>,
span: Span,
instance: ty::Instance<'tcx>,
args: &[OpTy<'tcx, Self::PointerTag>],
dest: Option<PlaceTy<'tcx, Self::PointerTag>>,
ret: Option<mir::BasicBlock>,
ret: Option<(PlaceTy<'tcx, Self::PointerTag>, mir::BasicBlock)>,
unwind: Option<mir::BasicBlock>,
) -> InterpResult<'tcx>;

Expand Down
Loading