Auto merge of #66874 - RalfJung:miri-assert-panic, r=oli-obk
Miri engine: proper support for `Assert` MIR terminators This puts down the basis for https://github.com/rust-lang/miri/issues/1070, and I also did some clean-up. The Miri side of this is at https://github.com/rust-lang/miri/pull/1084. r? @oli-obk
This commit is contained in:
commit
2da942f328
13 changed files with 195 additions and 118 deletions
|
@ -471,6 +471,13 @@ impl<Tag> From<Scalar<Tag>> for ScalarMaybeUndef<Tag> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<Tag> From<Pointer<Tag>> for ScalarMaybeUndef<Tag> {
|
||||||
|
#[inline(always)]
|
||||||
|
fn from(s: Pointer<Tag>) -> Self {
|
||||||
|
ScalarMaybeUndef::Scalar(s.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<Tag: fmt::Debug, Id: fmt::Debug> fmt::Debug for ScalarMaybeUndef<Tag, Id> {
|
impl<Tag: fmt::Debug, Id: fmt::Debug> fmt::Debug for ScalarMaybeUndef<Tag, Id> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
|
|
|
@ -23,7 +23,7 @@ use syntax::{source_map::{Span, DUMMY_SP}, symbol::Symbol};
|
||||||
use crate::interpret::{self,
|
use crate::interpret::{self,
|
||||||
PlaceTy, MPlaceTy, OpTy, ImmTy, Immediate, Scalar, Pointer,
|
PlaceTy, MPlaceTy, OpTy, ImmTy, Immediate, Scalar, Pointer,
|
||||||
RawConst, ConstValue, Machine,
|
RawConst, ConstValue, Machine,
|
||||||
InterpResult, InterpErrorInfo, GlobalId, InterpCx, StackPopCleanup,
|
InterpResult, InterpErrorInfo, GlobalId, InterpCx, StackPopCleanup, AssertMessage,
|
||||||
Allocation, AllocId, MemoryKind, Memory,
|
Allocation, AllocId, MemoryKind, Memory,
|
||||||
snapshot, RefTracking, intern_const_alloc_recursive,
|
snapshot, RefTracking, intern_const_alloc_recursive,
|
||||||
};
|
};
|
||||||
|
@ -395,6 +395,40 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn assert_panic(
|
||||||
|
ecx: &mut InterpCx<'mir, 'tcx, Self>,
|
||||||
|
_span: Span,
|
||||||
|
msg: &AssertMessage<'tcx>,
|
||||||
|
_unwind: Option<mir::BasicBlock>,
|
||||||
|
) -> InterpResult<'tcx> {
|
||||||
|
use rustc::mir::interpret::PanicInfo::*;
|
||||||
|
Err(match msg {
|
||||||
|
BoundsCheck { ref len, ref index } => {
|
||||||
|
let len = ecx
|
||||||
|
.read_immediate(ecx.eval_operand(len, None)?)
|
||||||
|
.expect("can't eval len")
|
||||||
|
.to_scalar()?
|
||||||
|
.to_machine_usize(&*ecx)?;
|
||||||
|
let index = ecx
|
||||||
|
.read_immediate(ecx.eval_operand(index, None)?)
|
||||||
|
.expect("can't eval index")
|
||||||
|
.to_scalar()?
|
||||||
|
.to_machine_usize(&*ecx)?;
|
||||||
|
err_panic!(BoundsCheck { len, index })
|
||||||
|
}
|
||||||
|
Overflow(op) => err_panic!(Overflow(*op)),
|
||||||
|
OverflowNeg => err_panic!(OverflowNeg),
|
||||||
|
DivisionByZero => err_panic!(DivisionByZero),
|
||||||
|
RemainderByZero => err_panic!(RemainderByZero),
|
||||||
|
ResumedAfterReturn(generator_kind)
|
||||||
|
=> err_panic!(ResumedAfterReturn(*generator_kind)),
|
||||||
|
ResumedAfterPanic(generator_kind)
|
||||||
|
=> err_panic!(ResumedAfterPanic(*generator_kind)),
|
||||||
|
Panic { .. } => bug!("`Panic` variant cannot occur in MIR"),
|
||||||
|
}
|
||||||
|
.into())
|
||||||
|
}
|
||||||
|
|
||||||
fn ptr_to_int(
|
fn ptr_to_int(
|
||||||
_mem: &Memory<'mir, 'tcx, Self>,
|
_mem: &Memory<'mir, 'tcx, Self>,
|
||||||
_ptr: Pointer,
|
_ptr: Pointer,
|
||||||
|
@ -423,7 +457,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn tag_allocation<'b>(
|
fn init_allocation_extra<'b>(
|
||||||
_memory_extra: &(),
|
_memory_extra: &(),
|
||||||
_id: AllocId,
|
_id: AllocId,
|
||||||
alloc: Cow<'b, Allocation>,
|
alloc: Cow<'b, Allocation>,
|
||||||
|
@ -518,7 +552,7 @@ pub fn const_caller_location<'tcx>(
|
||||||
tcx.type_of(tcx.require_lang_item(PanicLocationLangItem, None))
|
tcx.type_of(tcx.require_lang_item(PanicLocationLangItem, None))
|
||||||
.subst(tcx, tcx.mk_substs([tcx.lifetimes.re_static.into()].iter())),
|
.subst(tcx, tcx.mk_substs([tcx.lifetimes.re_static.into()].iter())),
|
||||||
);
|
);
|
||||||
let loc_place = ecx.alloc_caller_location(file, line, col).unwrap();
|
let loc_place = ecx.alloc_caller_location(file, line, col);
|
||||||
intern_const_alloc_recursive(&mut ecx, None, loc_place).unwrap();
|
intern_const_alloc_recursive(&mut ecx, None, loc_place).unwrap();
|
||||||
let loc_const = ty::Const {
|
let loc_const = ty::Const {
|
||||||
ty: loc_ty,
|
ty: loc_ty,
|
||||||
|
|
|
@ -55,7 +55,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||||
).ok_or_else(|| err_inval!(TooGeneric))?;
|
).ok_or_else(|| err_inval!(TooGeneric))?;
|
||||||
|
|
||||||
let fn_ptr = self.memory.create_fn_alloc(FnVal::Instance(instance));
|
let fn_ptr = self.memory.create_fn_alloc(FnVal::Instance(instance));
|
||||||
self.write_scalar(Scalar::Ptr(fn_ptr.into()), dest)?;
|
self.write_scalar(fn_ptr, dest)?;
|
||||||
}
|
}
|
||||||
_ => bug!("reify fn pointer on {:?}", src.layout.ty),
|
_ => bug!("reify fn pointer on {:?}", src.layout.ty),
|
||||||
}
|
}
|
||||||
|
@ -88,8 +88,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||||
ty::ClosureKind::FnOnce,
|
ty::ClosureKind::FnOnce,
|
||||||
);
|
);
|
||||||
let fn_ptr = self.memory.create_fn_alloc(FnVal::Instance(instance));
|
let fn_ptr = self.memory.create_fn_alloc(FnVal::Instance(instance));
|
||||||
let val = Immediate::Scalar(Scalar::Ptr(fn_ptr.into()).into());
|
self.write_scalar(fn_ptr, dest)?;
|
||||||
self.write_immediate(val, dest)?;
|
|
||||||
}
|
}
|
||||||
_ => bug!("closure fn pointer on {:?}", src.layout.ty),
|
_ => bug!("closure fn pointer on {:?}", src.layout.ty),
|
||||||
}
|
}
|
||||||
|
|
|
@ -164,6 +164,20 @@ impl<'tcx, Tag: Copy + 'static> LocalState<'tcx, Tag> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'mir, 'tcx, Tag, Extra> Frame<'mir, 'tcx, Tag, Extra> {
|
||||||
|
/// Return the `SourceInfo` of the current instruction.
|
||||||
|
pub fn current_source_info(&self) -> Option<mir::SourceInfo> {
|
||||||
|
self.block.map(|block| {
|
||||||
|
let block = &self.body.basic_blocks()[block];
|
||||||
|
if self.stmt < block.statements.len() {
|
||||||
|
block.statements[self.stmt].source_info
|
||||||
|
} else {
|
||||||
|
block.terminator().source_info
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> HasDataLayout for InterpCx<'mir, 'tcx, M> {
|
impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> HasDataLayout for InterpCx<'mir, 'tcx, M> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn data_layout(&self) -> &layout::TargetDataLayout {
|
fn data_layout(&self) -> &layout::TargetDataLayout {
|
||||||
|
@ -236,6 +250,12 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||||
self.memory.force_bits(scalar, size)
|
self.memory.force_bits(scalar, size)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Call this to turn untagged "global" pointers (obtained via `tcx`) into
|
||||||
|
/// the *canonical* machine pointer to the allocation. Must never be used
|
||||||
|
/// for any other pointers!
|
||||||
|
///
|
||||||
|
/// This represents a *direct* access to that memory, as opposed to access
|
||||||
|
/// through a pointer that was created by the program.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn tag_static_base_pointer(&self, ptr: Pointer) -> Pointer<M::PointerTag> {
|
pub fn tag_static_base_pointer(&self, ptr: Pointer) -> Pointer<M::PointerTag> {
|
||||||
self.memory.tag_static_base_pointer(ptr)
|
self.memory.tag_static_base_pointer(ptr)
|
||||||
|
@ -828,34 +848,28 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||||
pub fn generate_stacktrace(&self, explicit_span: Option<Span>) -> Vec<FrameInfo<'tcx>> {
|
pub fn generate_stacktrace(&self, explicit_span: Option<Span>) -> Vec<FrameInfo<'tcx>> {
|
||||||
let mut last_span = None;
|
let mut last_span = None;
|
||||||
let mut frames = Vec::new();
|
let mut frames = Vec::new();
|
||||||
for &Frame { instance, span, body, block, stmt, .. } in self.stack().iter().rev() {
|
for frame in self.stack().iter().rev() {
|
||||||
// make sure we don't emit frames that are duplicates of the previous
|
// make sure we don't emit frames that are duplicates of the previous
|
||||||
if explicit_span == Some(span) {
|
if explicit_span == Some(frame.span) {
|
||||||
last_span = Some(span);
|
last_span = Some(frame.span);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if let Some(last) = last_span {
|
if let Some(last) = last_span {
|
||||||
if last == span {
|
if last == frame.span {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
last_span = Some(span);
|
last_span = Some(frame.span);
|
||||||
}
|
}
|
||||||
|
|
||||||
let lint_root = block.and_then(|block| {
|
let lint_root = frame.current_source_info().and_then(|source_info| {
|
||||||
let block = &body.basic_blocks()[block];
|
match &frame.body.source_scopes[source_info.scope].local_data {
|
||||||
let source_info = if stmt < block.statements.len() {
|
|
||||||
block.statements[stmt].source_info
|
|
||||||
} else {
|
|
||||||
block.terminator().source_info
|
|
||||||
};
|
|
||||||
match &body.source_scopes[source_info.scope].local_data {
|
|
||||||
mir::ClearCrossCrate::Set(data) => Some(data.lint_root),
|
mir::ClearCrossCrate::Set(data) => Some(data.lint_root),
|
||||||
mir::ClearCrossCrate::Clear => None,
|
mir::ClearCrossCrate::Clear => None,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
frames.push(FrameInfo { call_site: span, instance, lint_root });
|
frames.push(FrameInfo { call_site: frame.span, instance: frame.instance, lint_root });
|
||||||
}
|
}
|
||||||
trace!("generate stacktrace: {:#?}, {:?}", frames, explicit_span);
|
trace!("generate stacktrace: {:#?}, {:?}", frames, explicit_span);
|
||||||
frames
|
frames
|
||||||
|
|
|
@ -110,13 +110,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||||
|
|
||||||
match intrinsic_name {
|
match intrinsic_name {
|
||||||
"caller_location" => {
|
"caller_location" => {
|
||||||
let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span);
|
let location = self.alloc_caller_location_for_span(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,
|
|
||||||
caller.col_display as u32 + 1,
|
|
||||||
)?;
|
|
||||||
self.write_scalar(location.ptr, dest)?;
|
self.write_scalar(location.ptr, dest)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,50 +1,48 @@
|
||||||
use rustc::middle::lang_items::PanicLocationLangItem;
|
use rustc::middle::lang_items::PanicLocationLangItem;
|
||||||
use rustc::mir::interpret::{Pointer, PointerArithmetic, Scalar};
|
|
||||||
use rustc::ty::subst::Subst;
|
use rustc::ty::subst::Subst;
|
||||||
use rustc_target::abi::{LayoutOf, Size};
|
use rustc_target::abi::LayoutOf;
|
||||||
use syntax_pos::Symbol;
|
use syntax_pos::{Symbol, Span};
|
||||||
|
|
||||||
use crate::interpret::{MemoryKind, MPlaceTy, intrinsics::{InterpCx, InterpResult, Machine}};
|
use crate::interpret::{Scalar, MemoryKind, MPlaceTy, intrinsics::{InterpCx, Machine}};
|
||||||
|
|
||||||
impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||||
pub fn alloc_caller_location(
|
crate fn alloc_caller_location(
|
||||||
&mut self,
|
&mut self,
|
||||||
filename: Symbol,
|
filename: Symbol,
|
||||||
line: u32,
|
line: u32,
|
||||||
col: u32,
|
col: u32,
|
||||||
) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
|
) -> MPlaceTy<'tcx, M::PointerTag> {
|
||||||
|
let file = self.allocate_str(&filename.as_str(), MemoryKind::CallerLocation);
|
||||||
let line = Scalar::from_u32(line);
|
let line = Scalar::from_u32(line);
|
||||||
let col = Scalar::from_u32(col);
|
let col = Scalar::from_u32(col);
|
||||||
|
|
||||||
let ptr_size = self.pointer_size();
|
// Allocate memory for `CallerLocation` struct.
|
||||||
let u32_size = Size::from_bits(32);
|
|
||||||
|
|
||||||
let loc_ty = self.tcx.type_of(self.tcx.require_lang_item(PanicLocationLangItem, None))
|
let loc_ty = self.tcx.type_of(self.tcx.require_lang_item(PanicLocationLangItem, None))
|
||||||
.subst(*self.tcx, self.tcx.mk_substs([self.tcx.lifetimes.re_static.into()].iter()));
|
.subst(*self.tcx, self.tcx.mk_substs([self.tcx.lifetimes.re_static.into()].iter()));
|
||||||
let loc_layout = self.layout_of(loc_ty)?;
|
let loc_layout = self.layout_of(loc_ty).unwrap();
|
||||||
|
|
||||||
let file_alloc = self.tcx.allocate_bytes(filename.as_str().as_bytes());
|
|
||||||
let file_ptr = Pointer::new(file_alloc, Size::ZERO);
|
|
||||||
let file = Scalar::Ptr(self.tag_static_base_pointer(file_ptr));
|
|
||||||
let file_len = Scalar::from_uint(filename.as_str().len() as u128, ptr_size);
|
|
||||||
|
|
||||||
let location = self.allocate(loc_layout, MemoryKind::CallerLocation);
|
let location = self.allocate(loc_layout, MemoryKind::CallerLocation);
|
||||||
|
|
||||||
let file_out = self.mplace_field(location, 0)?;
|
// Initialize fields.
|
||||||
let file_ptr_out = self.force_ptr(self.mplace_field(file_out, 0)?.ptr)?;
|
self.write_immediate(file.to_ref(), self.mplace_field(location, 0).unwrap().into())
|
||||||
let file_len_out = self.force_ptr(self.mplace_field(file_out, 1)?.ptr)?;
|
.expect("writing to memory we just allocated cannot fail");
|
||||||
let line_out = self.force_ptr(self.mplace_field(location, 1)?.ptr)?;
|
self.write_scalar(line, self.mplace_field(location, 1).unwrap().into())
|
||||||
let col_out = self.force_ptr(self.mplace_field(location, 2)?.ptr)?;
|
.expect("writing to memory we just allocated cannot fail");
|
||||||
|
self.write_scalar(col, self.mplace_field(location, 2).unwrap().into())
|
||||||
|
.expect("writing to memory we just allocated cannot fail");
|
||||||
|
|
||||||
let layout = &self.tcx.data_layout;
|
location
|
||||||
// We just allocated this, so we can skip the bounds checks.
|
}
|
||||||
let alloc = self.memory.get_raw_mut(file_ptr_out.alloc_id)?;
|
|
||||||
|
|
||||||
alloc.write_scalar(layout, file_ptr_out, file.into(), ptr_size)?;
|
pub fn alloc_caller_location_for_span(
|
||||||
alloc.write_scalar(layout, file_len_out, file_len.into(), ptr_size)?;
|
&mut self,
|
||||||
alloc.write_scalar(layout, line_out, line.into(), u32_size)?;
|
span: Span,
|
||||||
alloc.write_scalar(layout, col_out, col.into(), u32_size)?;
|
) -> MPlaceTy<'tcx, M::PointerTag> {
|
||||||
|
let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span);
|
||||||
Ok(location)
|
let caller = self.tcx.sess.source_map().lookup_char_pos(topmost.lo());
|
||||||
|
self.alloc_caller_location(
|
||||||
|
Symbol::intern(&caller.file.name.to_string()),
|
||||||
|
caller.line as u32,
|
||||||
|
caller.col_display as u32 + 1,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ use rustc::ty::{self, Ty, TyCtxt};
|
||||||
use syntax_pos::Span;
|
use syntax_pos::Span;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
Allocation, AllocId, InterpResult, Scalar, AllocationExtra,
|
Allocation, AllocId, InterpResult, Scalar, AllocationExtra, AssertMessage,
|
||||||
InterpCx, PlaceTy, OpTy, ImmTy, MemoryKind, Pointer, Memory,
|
InterpCx, PlaceTy, OpTy, ImmTy, MemoryKind, Pointer, Memory,
|
||||||
Frame, Operand,
|
Frame, Operand,
|
||||||
};
|
};
|
||||||
|
@ -175,6 +175,14 @@ pub trait Machine<'mir, 'tcx>: Sized {
|
||||||
unwind: Option<mir::BasicBlock>,
|
unwind: Option<mir::BasicBlock>,
|
||||||
) -> InterpResult<'tcx>;
|
) -> InterpResult<'tcx>;
|
||||||
|
|
||||||
|
/// Called to evaluate `Assert` MIR terminators that trigger a panic.
|
||||||
|
fn assert_panic(
|
||||||
|
ecx: &mut InterpCx<'mir, 'tcx, Self>,
|
||||||
|
span: Span,
|
||||||
|
msg: &AssertMessage<'tcx>,
|
||||||
|
unwind: Option<mir::BasicBlock>,
|
||||||
|
) -> InterpResult<'tcx>;
|
||||||
|
|
||||||
/// Called for read access to a foreign static item.
|
/// Called for read access to a foreign static item.
|
||||||
///
|
///
|
||||||
/// This will only be called once per static and machine; the result is cached in
|
/// This will only be called once per static and machine; the result is cached in
|
||||||
|
@ -233,20 +241,19 @@ pub trait Machine<'mir, 'tcx>: Sized {
|
||||||
/// cache the result. (This relies on `AllocMap::get_or` being able to add the
|
/// cache the result. (This relies on `AllocMap::get_or` being able to add the
|
||||||
/// owned allocation to the map even when the map is shared.)
|
/// owned allocation to the map even when the map is shared.)
|
||||||
///
|
///
|
||||||
/// For static allocations, the tag returned must be the same as the one returned by
|
/// Also return the "base" tag to use for this allocation: the one that is used for direct
|
||||||
/// `tag_static_base_pointer`.
|
/// accesses to this allocation. If `kind == STATIC_KIND`, this tag must be consistent
|
||||||
fn tag_allocation<'b>(
|
/// with `tag_static_base_pointer`.
|
||||||
|
fn init_allocation_extra<'b>(
|
||||||
memory_extra: &Self::MemoryExtra,
|
memory_extra: &Self::MemoryExtra,
|
||||||
id: AllocId,
|
id: AllocId,
|
||||||
alloc: Cow<'b, Allocation>,
|
alloc: Cow<'b, Allocation>,
|
||||||
kind: Option<MemoryKind<Self::MemoryKinds>>,
|
kind: Option<MemoryKind<Self::MemoryKinds>>,
|
||||||
) -> (Cow<'b, Allocation<Self::PointerTag, Self::AllocExtra>>, Self::PointerTag);
|
) -> (Cow<'b, Allocation<Self::PointerTag, Self::AllocExtra>>, Self::PointerTag);
|
||||||
|
|
||||||
/// Return the "base" tag for the given static allocation: the one that is used for direct
|
/// Return the "base" tag for the given *static* allocation: the one that is used for direct
|
||||||
/// accesses to this static/const/fn allocation.
|
/// accesses to this static/const/fn allocation. If `id` is not a static allocation,
|
||||||
///
|
/// this will return an unusable tag (i.e., accesses will be UB)!
|
||||||
/// Be aware that requesting the `Allocation` for that `id` will lead to cycles
|
|
||||||
/// for cyclic statics!
|
|
||||||
fn tag_static_base_pointer(
|
fn tag_static_base_pointer(
|
||||||
memory_extra: &Self::MemoryExtra,
|
memory_extra: &Self::MemoryExtra,
|
||||||
id: AllocId,
|
id: AllocId,
|
||||||
|
|
|
@ -143,6 +143,12 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Call this to turn untagged "global" pointers (obtained via `tcx`) into
|
||||||
|
/// the *canonical* machine pointer to the allocation. Must never be used
|
||||||
|
/// for any other pointers!
|
||||||
|
///
|
||||||
|
/// This represents a *direct* access to that memory, as opposed to access
|
||||||
|
/// through a pointer that was created by the program.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn tag_static_base_pointer(&self, ptr: Pointer) -> Pointer<M::PointerTag> {
|
pub fn tag_static_base_pointer(&self, ptr: Pointer) -> Pointer<M::PointerTag> {
|
||||||
ptr.with_tag(M::tag_static_base_pointer(&self.extra, ptr.alloc_id))
|
ptr.with_tag(M::tag_static_base_pointer(&self.extra, ptr.alloc_id))
|
||||||
|
@ -191,7 +197,9 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
|
||||||
kind: MemoryKind<M::MemoryKinds>,
|
kind: MemoryKind<M::MemoryKinds>,
|
||||||
) -> Pointer<M::PointerTag> {
|
) -> Pointer<M::PointerTag> {
|
||||||
let id = self.tcx.alloc_map.lock().reserve();
|
let id = self.tcx.alloc_map.lock().reserve();
|
||||||
let (alloc, tag) = M::tag_allocation(&self.extra, id, Cow::Owned(alloc), Some(kind));
|
debug_assert_ne!(Some(kind), M::STATIC_KIND.map(MemoryKind::Machine),
|
||||||
|
"dynamically allocating static memory");
|
||||||
|
let (alloc, tag) = M::init_allocation_extra(&self.extra, id, Cow::Owned(alloc), Some(kind));
|
||||||
self.alloc_map.insert(id, (kind, alloc.into_owned()));
|
self.alloc_map.insert(id, (kind, alloc.into_owned()));
|
||||||
Pointer::from(id).with_tag(tag)
|
Pointer::from(id).with_tag(tag)
|
||||||
}
|
}
|
||||||
|
@ -350,7 +358,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
|
||||||
sptr
|
sptr
|
||||||
} else {
|
} else {
|
||||||
// A "real" access, we must get a pointer.
|
// A "real" access, we must get a pointer.
|
||||||
Scalar::Ptr(self.force_ptr(sptr)?)
|
Scalar::from(self.force_ptr(sptr)?)
|
||||||
};
|
};
|
||||||
Ok(match normalized.to_bits_or_ptr(self.pointer_size(), self) {
|
Ok(match normalized.to_bits_or_ptr(self.pointer_size(), self) {
|
||||||
Ok(bits) => {
|
Ok(bits) => {
|
||||||
|
@ -473,14 +481,15 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// We got tcx memory. Let the machine figure out whether and how to
|
// We got tcx memory. Let the machine initialize its "extra" stuff.
|
||||||
// turn that into memory with the right pointer tag.
|
let (alloc, tag) = M::init_allocation_extra(
|
||||||
Ok(M::tag_allocation(
|
|
||||||
memory_extra,
|
memory_extra,
|
||||||
id, // always use the ID we got as input, not the "hidden" one.
|
id, // always use the ID we got as input, not the "hidden" one.
|
||||||
alloc,
|
alloc,
|
||||||
M::STATIC_KIND.map(MemoryKind::Machine),
|
M::STATIC_KIND.map(MemoryKind::Machine),
|
||||||
).0)
|
);
|
||||||
|
debug_assert_eq!(tag, M::tag_static_base_pointer(memory_extra, id));
|
||||||
|
Ok(alloc)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gives raw access to the `Allocation`, without bounds or alignment checks.
|
/// Gives raw access to the `Allocation`, without bounds or alignment checks.
|
||||||
|
|
|
@ -24,7 +24,7 @@ use rustc_macros::HashStable;
|
||||||
///
|
///
|
||||||
/// For optimization of a few very common cases, there is also a representation for a pair of
|
/// For optimization of a few very common cases, there is also a representation for a pair of
|
||||||
/// primitive values (`ScalarPair`). It allows Miri to avoid making allocations for checked binary
|
/// primitive values (`ScalarPair`). It allows Miri to avoid making allocations for checked binary
|
||||||
/// operations and fat pointers. This idea was taken from rustc's codegen.
|
/// operations and wide pointers. This idea was taken from rustc's codegen.
|
||||||
/// In particular, thanks to `ScalarPair`, arithmetic operations and casts can be entirely
|
/// In particular, thanks to `ScalarPair`, arithmetic operations and casts can be entirely
|
||||||
/// defined on `Immediate`, and do not have to work with a `Place`.
|
/// defined on `Immediate`, and do not have to work with a `Place`.
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, HashStable, Hash)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, HashStable, Hash)]
|
||||||
|
@ -47,6 +47,13 @@ impl<Tag> From<Scalar<Tag>> for Immediate<Tag> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<Tag> From<Pointer<Tag>> for Immediate<Tag> {
|
||||||
|
#[inline(always)]
|
||||||
|
fn from(val: Pointer<Tag>) -> Self {
|
||||||
|
Immediate::Scalar(Scalar::from(val).into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'tcx, Tag> Immediate<Tag> {
|
impl<'tcx, Tag> Immediate<Tag> {
|
||||||
pub fn new_slice(
|
pub fn new_slice(
|
||||||
val: Scalar<Tag>,
|
val: Scalar<Tag>,
|
||||||
|
@ -60,14 +67,14 @@ impl<'tcx, Tag> Immediate<Tag> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_dyn_trait(val: Scalar<Tag>, vtable: Pointer<Tag>) -> Self {
|
pub fn new_dyn_trait(val: Scalar<Tag>, vtable: Pointer<Tag>) -> Self {
|
||||||
Immediate::ScalarPair(val.into(), Scalar::Ptr(vtable).into())
|
Immediate::ScalarPair(val.into(), vtable.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn to_scalar_or_undef(self) -> ScalarMaybeUndef<Tag> {
|
pub fn to_scalar_or_undef(self) -> ScalarMaybeUndef<Tag> {
|
||||||
match self {
|
match self {
|
||||||
Immediate::Scalar(val) => val,
|
Immediate::Scalar(val) => val,
|
||||||
Immediate::ScalarPair(..) => bug!("Got a fat pointer where a scalar was expected"),
|
Immediate::ScalarPair(..) => bug!("Got a wide pointer where a scalar was expected"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -324,7 +331,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||||
Ok(self.read_immediate(op)?.to_scalar_or_undef())
|
Ok(self.read_immediate(op)?.to_scalar_or_undef())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Turn the MPlace into a string (must already be dereferenced!)
|
// Turn the wide MPlace into a string (must already be dereferenced!)
|
||||||
pub fn read_str(
|
pub fn read_str(
|
||||||
&self,
|
&self,
|
||||||
mplace: MPlaceTy<'tcx, M::PointerTag>,
|
mplace: MPlaceTy<'tcx, M::PointerTag>,
|
||||||
|
|
|
@ -125,7 +125,7 @@ impl<Tag> MemPlace<Tag> {
|
||||||
Self::from_scalar_ptr(ptr.into(), align)
|
Self::from_scalar_ptr(ptr.into(), align)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Turn a mplace into a (thin or fat) pointer, as a reference, pointing to the same space.
|
/// Turn a mplace into a (thin or wide) pointer, as a reference, pointing to the same space.
|
||||||
/// This is the inverse of `ref_to_mplace`.
|
/// This is the inverse of `ref_to_mplace`.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn to_ref(self) -> Immediate<Tag> {
|
pub fn to_ref(self) -> Immediate<Tag> {
|
||||||
|
@ -278,7 +278,7 @@ where
|
||||||
M::MemoryMap: AllocMap<AllocId, (MemoryKind<M::MemoryKinds>, Allocation<Tag, M::AllocExtra>)>,
|
M::MemoryMap: AllocMap<AllocId, (MemoryKind<M::MemoryKinds>, Allocation<Tag, M::AllocExtra>)>,
|
||||||
M::AllocExtra: AllocationExtra<Tag>,
|
M::AllocExtra: AllocationExtra<Tag>,
|
||||||
{
|
{
|
||||||
/// Take a value, which represents a (thin or fat) reference, and make it a place.
|
/// Take a value, which represents a (thin or wide) reference, and make it a place.
|
||||||
/// Alignment is just based on the type. This is the inverse of `MemPlace::to_ref()`.
|
/// Alignment is just based on the type. This is the inverse of `MemPlace::to_ref()`.
|
||||||
///
|
///
|
||||||
/// Only call this if you are sure the place is "valid" (aligned and inbounds), or do not
|
/// Only call this if you are sure the place is "valid" (aligned and inbounds), or do not
|
||||||
|
@ -694,6 +694,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Write a scalar to a place
|
/// Write a scalar to a place
|
||||||
|
#[inline(always)]
|
||||||
pub fn write_scalar(
|
pub fn write_scalar(
|
||||||
&mut self,
|
&mut self,
|
||||||
val: impl Into<ScalarMaybeUndef<M::PointerTag>>,
|
val: impl Into<ScalarMaybeUndef<M::PointerTag>>,
|
||||||
|
@ -1041,6 +1042,24 @@ where
|
||||||
MPlaceTy::from_aligned_ptr(ptr, layout)
|
MPlaceTy::from_aligned_ptr(ptr, layout)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a wide MPlace.
|
||||||
|
pub fn allocate_str(
|
||||||
|
&mut self,
|
||||||
|
str: &str,
|
||||||
|
kind: MemoryKind<M::MemoryKinds>,
|
||||||
|
) -> MPlaceTy<'tcx, M::PointerTag> {
|
||||||
|
let ptr = self.memory.allocate_static_bytes(str.as_bytes(), kind);
|
||||||
|
let meta = Scalar::from_uint(str.len() as u128, self.pointer_size());
|
||||||
|
let mplace = MemPlace {
|
||||||
|
ptr: ptr.into(),
|
||||||
|
align: Align::from_bytes(1).unwrap(),
|
||||||
|
meta: Some(meta),
|
||||||
|
};
|
||||||
|
|
||||||
|
let layout = self.layout_of(self.tcx.mk_static_str()).unwrap();
|
||||||
|
MPlaceTy { mplace, layout }
|
||||||
|
}
|
||||||
|
|
||||||
pub fn write_discriminant_index(
|
pub fn write_discriminant_index(
|
||||||
&mut self,
|
&mut self,
|
||||||
variant_index: VariantIdx,
|
variant_index: VariantIdx,
|
||||||
|
|
|
@ -7,8 +7,8 @@ use syntax::source_map::Span;
|
||||||
use rustc_target::spec::abi::Abi;
|
use rustc_target::spec::abi::Abi;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
GlobalId, InterpResult, PointerArithmetic,
|
GlobalId, InterpResult, InterpCx, Machine,
|
||||||
InterpCx, Machine, OpTy, ImmTy, PlaceTy, MPlaceTy, StackPopCleanup, FnVal,
|
OpTy, ImmTy, PlaceTy, MPlaceTy, StackPopCleanup, FnVal,
|
||||||
};
|
};
|
||||||
|
|
||||||
impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||||
|
@ -115,40 +115,14 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||||
expected,
|
expected,
|
||||||
ref msg,
|
ref msg,
|
||||||
target,
|
target,
|
||||||
..
|
cleanup,
|
||||||
} => {
|
} => {
|
||||||
let cond_val = self.read_immediate(self.eval_operand(cond, None)?)?
|
let cond_val = self.read_immediate(self.eval_operand(cond, None)?)?
|
||||||
.to_scalar()?.to_bool()?;
|
.to_scalar()?.to_bool()?;
|
||||||
if expected == cond_val {
|
if expected == cond_val {
|
||||||
self.go_to_block(target);
|
self.go_to_block(target);
|
||||||
} else {
|
} else {
|
||||||
// Compute error message
|
M::assert_panic(self, terminator.source_info.span, msg, cleanup)?;
|
||||||
use rustc::mir::interpret::PanicInfo::*;
|
|
||||||
return Err(match msg {
|
|
||||||
BoundsCheck { ref len, ref index } => {
|
|
||||||
let len = self
|
|
||||||
.read_immediate(self.eval_operand(len, None)?)
|
|
||||||
.expect("can't eval len")
|
|
||||||
.to_scalar()?
|
|
||||||
.to_bits(self.memory.pointer_size())? as u64;
|
|
||||||
let index = self
|
|
||||||
.read_immediate(self.eval_operand(index, None)?)
|
|
||||||
.expect("can't eval index")
|
|
||||||
.to_scalar()?
|
|
||||||
.to_bits(self.memory.pointer_size())? as u64;
|
|
||||||
err_panic!(BoundsCheck { len, index })
|
|
||||||
}
|
|
||||||
Overflow(op) => err_panic!(Overflow(*op)),
|
|
||||||
OverflowNeg => err_panic!(OverflowNeg),
|
|
||||||
DivisionByZero => err_panic!(DivisionByZero),
|
|
||||||
RemainderByZero => err_panic!(RemainderByZero),
|
|
||||||
ResumedAfterReturn(generator_kind)
|
|
||||||
=> err_panic!(ResumedAfterReturn(*generator_kind)),
|
|
||||||
ResumedAfterPanic(generator_kind)
|
|
||||||
=> err_panic!(ResumedAfterPanic(*generator_kind)),
|
|
||||||
Panic { .. } => bug!("`Panic` variant cannot occur in MIR"),
|
|
||||||
}
|
|
||||||
.into());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,15 +138,21 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||||
return Ok(())
|
return Ok(())
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// It is UB to ever encounter this.
|
||||||
|
Unreachable => throw_ub!(Unreachable),
|
||||||
|
|
||||||
|
// These should never occur for MIR we actually run.
|
||||||
|
DropAndReplace { .. } |
|
||||||
|
FalseEdges { .. } |
|
||||||
|
FalseUnwind { .. } =>
|
||||||
|
bug!("{:#?} should have been eliminated by MIR pass", terminator.kind),
|
||||||
|
|
||||||
|
// These are not (yet) supported. It is unclear if they even can occur in
|
||||||
|
// MIR that we actually run.
|
||||||
Yield { .. } |
|
Yield { .. } |
|
||||||
GeneratorDrop |
|
GeneratorDrop |
|
||||||
DropAndReplace { .. } |
|
Abort =>
|
||||||
Abort => unimplemented!("{:#?}", terminator.kind),
|
throw_unsup_format!("Unsupported terminator kind: {:#?}", terminator.kind),
|
||||||
FalseEdges { .. } => bug!("should have been eliminated by\
|
|
||||||
`simplify_branches` mir pass"),
|
|
||||||
FalseUnwind { .. } => bug!("should have been eliminated by\
|
|
||||||
`simplify_branches` mir pass"),
|
|
||||||
Unreachable => throw_ub!(Unreachable),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -67,7 +67,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||||
// allocation is correctly aligned as we created it above. Also we're only offsetting by
|
// allocation is correctly aligned as we created it above. Also we're only offsetting by
|
||||||
// multiples of `ptr_align`, which means that it will stay aligned to `ptr_align`.
|
// multiples of `ptr_align`, which means that it will stay aligned to `ptr_align`.
|
||||||
let vtable_alloc = self.memory.get_raw_mut(vtable.alloc_id)?;
|
let vtable_alloc = self.memory.get_raw_mut(vtable.alloc_id)?;
|
||||||
vtable_alloc.write_ptr_sized(tcx, vtable, Scalar::Ptr(drop).into())?;
|
vtable_alloc.write_ptr_sized(tcx, vtable, drop.into())?;
|
||||||
|
|
||||||
let size_ptr = vtable.offset(ptr_size, tcx)?;
|
let size_ptr = vtable.offset(ptr_size, tcx)?;
|
||||||
vtable_alloc.write_ptr_sized(tcx, size_ptr, Scalar::from_uint(size, ptr_size).into())?;
|
vtable_alloc.write_ptr_sized(tcx, size_ptr, Scalar::from_uint(size, ptr_size).into())?;
|
||||||
|
@ -87,7 +87,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||||
// We cannot use `vtable_allic` as we are creating fn ptrs in this loop.
|
// We cannot use `vtable_allic` as we are creating fn ptrs in this loop.
|
||||||
let method_ptr = vtable.offset(ptr_size * (3 + i as u64), tcx)?;
|
let method_ptr = vtable.offset(ptr_size * (3 + i as u64), tcx)?;
|
||||||
self.memory.get_raw_mut(vtable.alloc_id)?
|
self.memory.get_raw_mut(vtable.alloc_id)?
|
||||||
.write_ptr_sized(tcx, method_ptr, Scalar::Ptr(fn_ptr).into())?;
|
.write_ptr_sized(tcx, method_ptr, fn_ptr.into())?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -156,6 +156,15 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine {
|
||||||
throw_unsup!(ConstPropUnsupported("calling intrinsics isn't supported in ConstProp"));
|
throw_unsup!(ConstPropUnsupported("calling intrinsics isn't supported in ConstProp"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn assert_panic(
|
||||||
|
_ecx: &mut InterpCx<'mir, 'tcx, Self>,
|
||||||
|
_span: Span,
|
||||||
|
_msg: &rustc::mir::interpret::AssertMessage<'tcx>,
|
||||||
|
_unwind: Option<rustc::mir::BasicBlock>,
|
||||||
|
) -> InterpResult<'tcx> {
|
||||||
|
bug!("panics terminators are not evaluated in ConstProp");
|
||||||
|
}
|
||||||
|
|
||||||
fn ptr_to_int(
|
fn ptr_to_int(
|
||||||
_mem: &Memory<'mir, 'tcx, Self>,
|
_mem: &Memory<'mir, 'tcx, Self>,
|
||||||
_ptr: Pointer,
|
_ptr: Pointer,
|
||||||
|
@ -182,7 +191,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn tag_allocation<'b>(
|
fn init_allocation_extra<'b>(
|
||||||
_memory_extra: &(),
|
_memory_extra: &(),
|
||||||
_id: AllocId,
|
_id: AllocId,
|
||||||
alloc: Cow<'b, Allocation>,
|
alloc: Cow<'b, Allocation>,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue