Skip to content

Commit 82374ad

Browse files
committed
comments and slight refactoring
1 parent 80f9484 commit 82374ad

File tree

6 files changed

+67
-71
lines changed

6 files changed

+67
-71
lines changed

rust-version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
8831d766ace89bc74714918a7d9fbd3ca5ec946a
1+
3e525e3f6d9e85d54fa4c49b52df85aa0c990100

src/helpers.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ fn resolve_did<'mir, 'tcx>(tcx: TyCtxt<'tcx>, path: &[&str]) -> InterpResult<'tc
5353

5454
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
5555

56+
/// Gets an instance for a path.
5657
fn resolve_path(&self, path: &[&str]) -> InterpResult<'tcx, ty::Instance<'tcx>> {
5758
Ok(ty::Instance::mono(self.eval_context_ref().tcx.tcx, resolve_did(self.eval_context_ref().tcx.tcx, path)?))
5859
}

src/machine.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,11 @@ pub const NUM_CPUS: u64 = 1;
2525
pub struct FrameData<'tcx> {
2626
/// Extra data for Stacked Borrows.
2727
pub call_id: stacked_borrows::CallId,
28-
/// If this is Some(), then this is a special 'catch unwind'
29-
/// frame. When this frame is popped during unwinding a panic,
30-
/// we stop unwinding, and use the `CatchUnwindData` to
31-
/// store the panic payload and continue execution in the parent frame.
28+
29+
/// If this is Some(), then this is a special "catch unwind" frame (the frame of the closure
30+
/// called by `__rustc_maybe_catch_panic`). When this frame is popped during unwinding a panic,
31+
/// we stop unwinding, use the `CatchUnwindData` to
32+
/// store the panic payload, and continue execution in the parent frame.
3233
pub catch_panic: Option<CatchUnwindData<'tcx>>,
3334
}
3435

src/shims/foreign_items.rs

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -130,27 +130,21 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
130130

131131
// First: functions that diverge.
132132
match link_name {
133-
// Note that this matches calls to the *foreign* item "__rust_start_panic" -
134-
// that is, calls `extern "Rust" { fn __rust_start_panic(...) }`
135-
// We forward this to the underlying *implementation* in "libpanic_unwind"
133+
// Note that this matches calls to the *foreign* item `__rust_start_panic* -
134+
// that is, calls to `extern "Rust" { fn __rust_start_panic(...) }`.
135+
// We forward this to the underlying *implementation* in "libpanic_unwind".
136136
"__rust_start_panic" => {
137137
let start_panic_instance = this.resolve_path(&["panic_unwind", "__rust_start_panic"])?;
138138
return Ok(Some(this.load_mir(start_panic_instance.def, None)?));
139139
}
140-
141-
// During a normal (non-Miri) compilation,
142-
// this gets resolved to the '#[panic_handler]` function at link time,
143-
// which corresponds to the function with the `#[panic_handler]` attribute.
144-
//
145-
// Since we're interpreting mir, we forward it to the implementation of `panic_impl`
146-
//
147-
// This is used by libcore to forward panics to the actual
148-
// panic impl
140+
// Similarly, we forward calls to the `panic_impl` foreign item to its implementation.
141+
// The implementation is provided by the function with the `#[panic_handler]` attribute.
149142
"panic_impl" => {
150143
let panic_impl_id = this.tcx.lang_items().panic_impl().unwrap();
151144
let panic_impl_instance = ty::Instance::mono(*this.tcx, panic_impl_id);
152145
return Ok(Some(this.load_mir(panic_impl_instance.def, None)?));
153146
}
147+
154148
"exit" | "ExitProcess" => {
155149
// it's really u32 for ExitProcess, but we have to put it into the `Exit` error variant anyway
156150
let code = this.read_scalar(args[0])?.to_i32()?;

src/shims/intrinsics.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
4545

4646
// Handle non-diverging intrinsics
4747
// The intrinsic itself cannot diverge (otherwise, we would have handled it above),
48-
// so if we got here without a return place... (can happen e.g., for transmute returning `!`)
48+
// so if we got here without a return place that's UB (can happen e.g., for transmute returning `!`).
4949
let dest = match dest {
5050
Some(dest) => dest,
5151
None => throw_ub!(Unreachable)

src/shims/panic.rs

Lines changed: 53 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,42 @@
1+
//! Panic runtime for Miri.
2+
//!
3+
//! The core pieces of the runtime are:
4+
//! - An implementation of `__rust_maybe_catch_panic` that pushes the invoked stack frame with
5+
//! some extra metadata derived from the panic-catching arguments of `__rust_maybe_catch_panic`.
6+
//! - A hack in `libpanic_unwind` that calls the `miri_start_panic` intrinsic instead of the
7+
//! target-native panic runtime. (This lives in the rustc repo.)
8+
//! - An implementation of `miri_start_panic` that stores its argument (the panic payload), and then
9+
//! immediately returns, but on the *unwind* edge (not the normal return edge), thus initiating unwinding.
10+
//! - A hook executed each time a frame is popped, such that if the frame pushed by `__rust_maybe_catch_panic`
11+
//! gets popped *during unwinding*, we take the panic payload and store it according to the extra
12+
//! metadata we remembered when pushing said frame.
13+
114
use rustc::mir;
215
use crate::*;
316
use super::machine::FrameData;
417
use rustc_target::spec::PanicStrategy;
518
use crate::rustc_target::abi::LayoutOf;
619

720
/// Holds all of the relevant data for a call to
8-
/// __rust_maybe_catch_panic
21+
/// `__rust_maybe_catch_panic`.
922
///
1023
/// If a panic occurs, we update this data with
11-
/// the information from the panic site
24+
/// the information from the panic site.
1225
#[derive(Debug)]
1326
pub struct CatchUnwindData<'tcx> {
14-
/// The 'data' argument passed to `__rust_maybe_catch_panic`
15-
pub data: Pointer<Tag>,
16-
/// The `data_ptr` argument passed to `__rust_maybe_catch_panic`
27+
/// The dereferenced `data_ptr` argument passed to `__rust_maybe_catch_panic`.
1728
pub data_place: MPlaceTy<'tcx, Tag>,
18-
/// The `vtable_ptr` argument passed to `__rust_maybe_catch_panic`
29+
/// The dereferenced `vtable_ptr` argument passed to `__rust_maybe_catch_panic`.
1930
pub vtable_place: MPlaceTy<'tcx, Tag>,
20-
/// The `dest` from the original call to `__rust_maybe_catch_panic`
31+
/// The `dest` from the original call to `__rust_maybe_catch_panic`.
2132
pub dest: PlaceTy<'tcx, Tag>,
22-
/// The `ret` from the original call to `__rust_maybe_catch_panic`
23-
pub ret: mir::BasicBlock,
2433
}
2534

2635
impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
2736
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
2837

2938
/// Handles the special "miri_start_panic" intrinsic, which is called
30-
/// by libpanic_unwind to delegate the actual unwinding process to Miri
39+
/// by libpanic_unwind to delegate the actual unwinding process to Miri.
3140
#[inline(always)]
3241
fn handle_miri_start_panic(
3342
&mut self,
@@ -44,14 +53,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
4453
throw_unsup_format!("the evaluated program panicked");
4554
}
4655

47-
// Get the raw pointer stored in arg[0]
56+
// Get the raw pointer stored in arg[0] (the panic payload).
4857
let scalar = this.read_immediate(args[0])?;
58+
assert!(this.machine.panic_payload.is_none(), "the panic runtime should avoid double-panics");
4959
this.machine.panic_payload = Some(scalar);
5060

51-
// Jump to the unwind block to begin unwinding
52-
// We don't use 'goto_block' - if `unwind` is None,
53-
// we'll end up immediately returning out of the
54-
// function during the next step() call
61+
// Jump to the unwind block to begin unwinding.
62+
// We don't use `goto_block` as that is just meant for normal returns.
5563
let next_frame = this.frame_mut();
5664
next_frame.block = unwind;
5765
next_frame.stmt = 0;
@@ -74,16 +82,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
7482
// data_ptr: *mut usize,
7583
// vtable_ptr: *mut usize,
7684
// ) -> u32
85+
86+
// Get all the arguments.
7787
let f = this.read_scalar(args[0])?.not_undef()?;
78-
let data = this.read_scalar(args[1])?.not_undef()?;
88+
let f_arg = this.read_scalar(args[1])?.not_undef()?;
7989
let data_place = this.deref_operand(args[2])?;
8090
let vtable_place = this.deref_operand(args[3])?;
91+
92+
// Now we make a function call, and pass `f_arg` as first and only argument.
8193
let f_instance = this.memory.get_fn(f)?.as_instance()?;
82-
this.write_null(dest)?;
8394
trace!("__rust_maybe_catch_panic: {:?}", f_instance);
84-
85-
86-
// Now we make a function call.
8795
// TODO: consider making this reusable? `InterpCx::step` does something similar
8896
// for the TLS destructors, and of course `eval_main`.
8997
let mir = this.load_mir(f_instance.def, None)?;
@@ -98,32 +106,30 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
98106
StackPopCleanup::Goto { ret: Some(ret), unwind: None },
99107
)?;
100108

109+
let mut args = this.frame().body.args_iter();
110+
// First argument.
111+
let arg_local = args
112+
.next()
113+
.expect("Argument to __rust_maybe_catch_panic does not take enough arguments.");
114+
let arg_dest = this.local_place(arg_local)?;
115+
this.write_scalar(f_arg, arg_dest)?;
116+
// No more arguments.
117+
args.next().expect_none("__rust_maybe_catch_panic argument has more arguments than expected");
118+
119+
// We ourselves will return `0`, eventually (will be overwritten if we catch a panic).
120+
this.write_null(dest)?;
121+
101122
// In unwind mode, we tag this frame with some extra data.
102123
// This lets `handle_stack_pop` (below) know that we should stop unwinding
103124
// when we pop this frame.
104125
if this.tcx.tcx.sess.panic_strategy() == PanicStrategy::Unwind {
105126
this.frame_mut().extra.catch_panic = Some(CatchUnwindData {
106-
data: data.to_ptr()?,
107127
data_place,
108128
vtable_place,
109129
dest,
110-
ret,
111130
})
112131
}
113132

114-
let mut args = this.frame().body.args_iter();
115-
116-
let arg_local = args
117-
.next()
118-
.expect("Argument to __rust_maybe_catch_panic does not take enough arguments.");
119-
let arg_dest = this.local_place(arg_local)?;
120-
this.write_scalar(data, arg_dest)?;
121-
122-
args.next().expect_none("__rust_maybe_catch_panic argument has more arguments than expected");
123-
124-
// We ourselves will return `0`, eventually (because we will not return if we paniced).
125-
this.write_null(dest)?;
126-
127133
return Ok(());
128134
}
129135

@@ -140,33 +146,27 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
140146
// We only care about `catch_panic` if we're unwinding - if we're doing a normal
141147
// return, then we don't need to do anything special.
142148
let res = if let (true, Some(unwind_data)) = (unwinding, extra.catch_panic.take()) {
143-
// We've just popped the frame that was immediately above
144-
// the frame which originally called `__rust_maybe_catch_panic`
145-
trace!("unwinding: found catch_panic frame: {:?}", this.frame().span);
149+
// We've just popped a frame that was pushed by `__rust_maybe_catch_panic`,
150+
// and we are unwinding, so we should catch that.
151+
trace!("unwinding: found catch_panic frame during unwinding: {:?}", this.frame().span);
146152

147-
// `panic_payload` now holds a '*mut (dyn Any + Send)',
153+
// `panic_payload` now holds a `*mut (dyn Any + Send)`,
148154
// provided by the `miri_start_panic` intrinsic.
149155
// We want to split this into its consituient parts -
150-
// the data and vtable pointers - and store them back
151-
// into the panic handler frame
152-
let real_ret = this.machine.panic_payload.take().unwrap();
153-
154-
let payload = this.ref_to_mplace(real_ret)?;
156+
// the data and vtable pointers - and store them according to
157+
// `unwind_data`, i.e., we store them where `__rust_maybe_catch_panic`
158+
// was told to put them.
159+
let payload = this.machine.panic_payload.take().unwrap();
160+
let payload = this.ref_to_mplace(payload)?;
155161
let payload_data_place = payload.ptr;
156162
let payload_vtable_place = payload.meta.expect("Expected fat pointer");
157163

158-
159-
let data_place = unwind_data.data_place;
160-
let vtable_place = unwind_data.vtable_place;
161-
let dest = unwind_data.dest;
162-
163-
// Here, we write to the pointers provided to the call
164-
// to '__rust_maybe_catch_panic`.
165-
this.write_scalar(payload_data_place, data_place.into())?;
166-
this.write_scalar(payload_vtable_place, vtable_place.into())?;
164+
this.write_scalar(payload_data_place, unwind_data.data_place.into())?;
165+
this.write_scalar(payload_vtable_place, unwind_data.vtable_place.into())?;
167166

168167
// We set the return value of `__rust_maybe_catch_panic` to 1,
169168
// since there was a panic.
169+
let dest = unwind_data.dest;
170170
this.write_scalar(Scalar::from_int(1, dest.layout.size), dest)?;
171171

172172
StackPopInfo::StopUnwinding

0 commit comments

Comments
 (0)