/// for the start of this frame. When we finish executing this frame,
/// we use this to register a completed event with `measureme`.
pub timing: Option<measureme::DetachedTiming>,
+
+ /// Indicates whether a `Frame` is part of a workspace-local crate and is also not
+ /// `#[track_caller]`. We compute this once on creation and store the result, as an
+ /// optimization.
+ /// This is used by `MiriMachine::current_span` and `MiriMachine::caller_span`
+ pub is_user_relevant: bool,
}
impl<'tcx> std::fmt::Debug for FrameData<'tcx> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
// Omitting `timing`, it does not support `Debug`.
- let FrameData { stacked_borrows, catch_unwind, timing: _ } = self;
+ let FrameData { stacked_borrows, catch_unwind, timing: _, is_user_relevant: _ } = self;
f.debug_struct("FrameData")
.field("stacked_borrows", stacked_borrows)
.field("catch_unwind", catch_unwind)
impl VisitTags for FrameData<'_> {
fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
- let FrameData { catch_unwind, stacked_borrows, timing: _ } = self;
+ let FrameData { catch_unwind, stacked_borrows, timing: _, is_user_relevant: _ } = self;
catch_unwind.visit_tags(visit);
stacked_borrows.visit_tags(visit);
}
/// Pointer provenance.
-#[derive(Debug, Clone, Copy)]
+#[derive(Clone, Copy)]
pub enum Provenance {
Concrete {
alloc_id: AllocId,
#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
static_assert_size!(Scalar<Provenance>, 32);
-impl interpret::Provenance for Provenance {
- /// We use absolute addresses in the `offset` of a `Pointer<Provenance>`.
- const OFFSET_IS_ADDR: bool = true;
-
- /// We cannot err on partial overwrites, it happens too often in practice (due to unions).
- const ERR_ON_PARTIAL_PTR_OVERWRITE: bool = false;
-
- fn fmt(ptr: &Pointer<Self>, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- let (prov, addr) = ptr.into_parts(); // address is absolute
- write!(f, "{:#x}", addr.bytes())?;
-
- match prov {
+impl fmt::Debug for Provenance {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ match self {
Provenance::Concrete { alloc_id, sb } => {
// Forward `alternate` flag to `alloc_id` printing.
if f.alternate() {
write!(f, "[wildcard]")?;
}
}
-
Ok(())
}
+}
+
+impl interpret::Provenance for Provenance {
+ /// We use absolute addresses in the `offset` of a `Pointer<Provenance>`.
+ const OFFSET_IS_ADDR: bool = true;
fn get_alloc_id(self) -> Option<AllocId> {
match self {
/// Miri does not expose env vars from the host to the emulated program.
pub(crate) env_vars: EnvVars<'tcx>,
+ /// Return place of the main function.
+ pub(crate) main_fn_ret_place: Option<MemPlace<Provenance>>,
+
/// Program arguments (`Option` because we can only initialize them after creating the ecx).
/// These are *pointers* to argc/argv because macOS.
/// We also need the full command line as one string because of Windows.
intptrcast: RefCell::new(intptrcast::GlobalStateInner::new(config)),
// `env_vars` depends on a full interpreter so we cannot properly initialize it yet.
env_vars: EnvVars::default(),
+ main_fn_ret_place: None,
argc: None,
argv: None,
cmd_line: None,
pub(crate) fn late_init(
this: &mut MiriInterpCx<'mir, 'tcx>,
config: &MiriConfig,
+ on_main_stack_empty: StackEmptyCallback<'mir, 'tcx>,
) -> InterpResult<'tcx> {
EnvVars::init(this, config)?;
MiriMachine::init_extern_statics(this)?;
- ThreadManager::init(this);
+ ThreadManager::init(this, on_main_stack_empty);
Ok(())
}
threads,
tls,
env_vars,
+ main_fn_ret_place,
argc,
argv,
cmd_line,
data_race.visit_tags(visit);
stacked_borrows.visit_tags(visit);
intptrcast.visit_tags(visit);
+ main_fn_ret_place.visit_tags(visit);
argc.visit_tags(visit);
argv.visit_tags(visit);
cmd_line.visit_tags(visit);
let alloc = alloc.into_owned();
let stacks = ecx.machine.stacked_borrows.as_ref().map(|stacked_borrows| {
- Stacks::new_allocation(
+ stacked_borrows::Stacks::new_allocation(
id,
alloc.size(),
stacked_borrows,
kind,
- ecx.machine.current_span(),
+ &ecx.machine,
)
});
let race_alloc = ecx.machine.data_race.as_ref().map(|data_race| {
range: AllocRange,
) -> InterpResult<'tcx> {
if let Some(data_race) = &alloc_extra.data_race {
- data_race.read(
- alloc_id,
- range,
- machine.data_race.as_ref().unwrap(),
- &machine.threads,
- )?;
+ data_race.read(alloc_id, range, machine)?;
}
if let Some(stacked_borrows) = &alloc_extra.stacked_borrows {
- stacked_borrows.borrow_mut().before_memory_read(
- alloc_id,
- prov_extra,
- range,
- machine.stacked_borrows.as_ref().unwrap(),
- machine.current_span(),
- &machine.threads,
- )?;
+ stacked_borrows
+ .borrow_mut()
+ .before_memory_read(alloc_id, prov_extra, range, machine)?;
}
if let Some(weak_memory) = &alloc_extra.weak_memory {
weak_memory.memory_accessed(range, machine.data_race.as_ref().unwrap());
range: AllocRange,
) -> InterpResult<'tcx> {
if let Some(data_race) = &mut alloc_extra.data_race {
- data_race.write(
- alloc_id,
- range,
- machine.data_race.as_mut().unwrap(),
- &machine.threads,
- )?;
+ data_race.write(alloc_id, range, machine)?;
}
if let Some(stacked_borrows) = &mut alloc_extra.stacked_borrows {
- stacked_borrows.get_mut().before_memory_write(
- alloc_id,
- prov_extra,
- range,
- machine.stacked_borrows.as_ref().unwrap(),
- machine.current_span(),
- &machine.threads,
- )?;
+ stacked_borrows.get_mut().before_memory_write(alloc_id, prov_extra, range, machine)?;
}
if let Some(weak_memory) = &alloc_extra.weak_memory {
weak_memory.memory_accessed(range, machine.data_race.as_ref().unwrap());
machine.emit_diagnostic(NonHaltingDiagnostic::FreedAlloc(alloc_id));
}
if let Some(data_race) = &mut alloc_extra.data_race {
- data_race.deallocate(
- alloc_id,
- range,
- machine.data_race.as_mut().unwrap(),
- &machine.threads,
- )?;
+ data_race.deallocate(alloc_id, range, machine)?;
}
if let Some(stacked_borrows) = &mut alloc_extra.stacked_borrows {
stacked_borrows.get_mut().before_memory_deallocation(
alloc_id,
prove_extra,
range,
- machine.stacked_borrows.as_ref().unwrap(),
- machine.current_span(),
- &machine.threads,
+ machine,
)
} else {
Ok(())
stacked_borrows: stacked_borrows.map(|sb| sb.borrow_mut().new_frame(&ecx.machine)),
catch_unwind: None,
timing,
+ is_user_relevant: ecx.machine.is_user_relevant(&frame),
};
+
Ok(frame.with_extra(extra))
}
#[inline(always)]
fn after_stack_push(ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> {
+ if ecx.frame().extra.is_user_relevant {
+ // We just pushed a local frame, so we know that the topmost local frame is the topmost
+ // frame. If we push a non-local frame, there's no need to do anything.
+ let stack_len = ecx.active_thread_stack().len();
+ ecx.active_thread_mut().set_top_user_relevant_frame(stack_len - 1);
+ }
+
if ecx.machine.stacked_borrows.is_some() { ecx.retag_return_place() } else { Ok(()) }
}
mut frame: Frame<'mir, 'tcx, Provenance, FrameData<'tcx>>,
unwinding: bool,
) -> InterpResult<'tcx, StackPopJump> {
+ if frame.extra.is_user_relevant {
+ // All that we store is whether or not the frame we just removed is local, so now we
+ // have no idea where the next topmost local frame is. So we recompute it.
+ // (If this ever becomes a bottleneck, we could have `push` store the previous
+ // user-relevant frame and restore that here.)
+ ecx.active_thread_mut().recompute_top_user_relevant_frame();
+ }
let timing = frame.extra.timing.take();
if let Some(stacked_borrows) = &ecx.machine.stacked_borrows {
stacked_borrows.borrow_mut().end_call(&frame.extra);