Skip to content

Commit a7891c0

Browse files
committedMar 15, 2020
Auto merge of #1227 - RalfJung:unwind, r=RalfJung
adjust Miri to needs of changed unwinding strategy
2 parents a7580f7 + 17a677f commit a7891c0

File tree

5 files changed

+78
-85
lines changed

5 files changed

+78
-85
lines changed
 

‎rust-version

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1572c433eed495d0ade41511ae106b180e02851d
1+
7cdbc87a49b0b705a41a004a6d486b0952521ae7

‎src/machine.rs

+8-8
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,10 @@ pub struct FrameData<'tcx> {
3232
/// Extra data for Stacked Borrows.
3333
pub call_id: stacked_borrows::CallId,
3434

35-
/// If this is Some(), then this is a special "catch unwind" frame (the frame of the closure
36-
/// called by `__rustc_maybe_catch_panic`). When this frame is popped during unwinding a panic,
37-
/// we stop unwinding, use the `CatchUnwindData` to
38-
/// store the panic payload, and continue execution in the parent frame.
39-
pub catch_panic: Option<CatchUnwindData<'tcx>>,
35+
/// If this is Some(), then this is a special "catch unwind" frame (the frame of `try_fn`
36+
/// called by `try`). When this frame is popped during unwinding a panic,
37+
/// we stop unwinding, use the `CatchUnwindData` to handle catching.
38+
pub catch_unwind: Option<CatchUnwindData<'tcx>>,
4039
}
4140

4241
/// Extra memory kinds
@@ -163,7 +162,8 @@ pub struct Evaluator<'tcx> {
163162

164163
/// The temporary used for storing the argument of
165164
/// the call to `miri_start_panic` (the panic payload) when unwinding.
166-
pub(crate) panic_payload: Option<ImmTy<'tcx, Tag>>,
165+
/// This is pointer-sized, and matches the `Payload` type in `src/libpanic_unwind/miri.rs`.
166+
pub(crate) panic_payload: Option<Scalar<Tag>>,
167167
}
168168

169169
impl<'tcx> Evaluator<'tcx> {
@@ -405,15 +405,15 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'tcx> {
405405
let call_id = stacked_borrows.map_or(NonZeroU64::new(1).unwrap(), |stacked_borrows| {
406406
stacked_borrows.borrow_mut().new_call()
407407
});
408-
Ok(FrameData { call_id, catch_panic: None })
408+
Ok(FrameData { call_id, catch_unwind: None })
409409
}
410410

411411
#[inline(always)]
412412
fn stack_pop(
413413
ecx: &mut InterpCx<'mir, 'tcx, Self>,
414414
extra: FrameData<'tcx>,
415415
unwinding: bool,
416-
) -> InterpResult<'tcx, StackPopInfo> {
416+
) -> InterpResult<'tcx, StackPopJump> {
417417
ecx.handle_stack_pop(extra, unwinding)
418418
}
419419

‎src/shims/foreign_items.rs

+5-9
Original file line numberDiff line numberDiff line change
@@ -150,12 +150,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
150150

151151
// Second: some functions that we forward to MIR implementations.
152152
match link_name {
153-
// This matches calls to the *foreign* item `__rust_start_panic*, that is,
154-
// calls to `extern "Rust" { fn __rust_start_panic(...) }`.
153+
// This matches calls to the foreign item `__rust_start_panic`, that is,
154+
// calls to `extern "Rust" { fn __rust_start_panic(...) }`
155+
// (and `__rust_panic_cleanup`, respectively).
155156
// We forward this to the underlying *implementation* in the panic runtime crate.
156157
// Normally, this will be either `libpanic_unwind` or `libpanic_abort`, but it could
157158
// also be a custom user-provided implementation via `#![feature(panic_runtime)]`
158-
"__rust_start_panic" => {
159+
"__rust_start_panic" | "__rust_panic_cleanup"=> {
159160
// FIXME we might want to cache this... but it's not really performance-critical.
160161
let panic_runtime = tcx
161162
.crates()
@@ -164,7 +165,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
164165
.expect("No panic runtime found!");
165166
let panic_runtime = tcx.crate_name(*panic_runtime);
166167
let start_panic_instance =
167-
this.resolve_path(&[&*panic_runtime.as_str(), "__rust_start_panic"])?;
168+
this.resolve_path(&[&*panic_runtime.as_str(), link_name])?;
168169
return Ok(Some(&*this.load_mir(start_panic_instance.def, None)?));
169170
}
170171
_ => {}
@@ -291,11 +292,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
291292
this.write_scalar(new_ptr, dest)?;
292293
}
293294

294-
"__rust_maybe_catch_panic" => {
295-
this.handle_catch_panic(args, dest, ret)?;
296-
return Ok(false);
297-
}
298-
299295
"memcmp" => {
300296
let left = this.read_scalar(args[0])?.not_undef()?;
301297
let right = this.read_scalar(args[1])?.not_undef()?;

‎src/shims/intrinsics.rs

+2-5
Original file line numberDiff line numberDiff line change
@@ -34,17 +34,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
3434
// First handle intrinsics without return place.
3535
let (dest, ret) = match ret {
3636
None => match intrinsic_name {
37-
"abort" => {
38-
// FIXME: remove, once the intrinsic on the rustc side is fixed.
39-
throw_machine_stop!(TerminationInfo::Abort);
40-
}
37+
"miri_start_panic" => return this.handle_miri_start_panic(args, unwind),
4138
_ => throw_unsup_format!("unimplemented (diverging) intrinsic: {}", intrinsic_name),
4239
},
4340
Some(p) => p,
4441
};
4542

4643
match intrinsic_name {
47-
"miri_start_panic" => return this.handle_miri_start_panic(args, unwind),
44+
"try" => return this.handle_try(args, dest, ret),
4845

4946
"arith_offset" => {
5047
let offset = this.read_scalar(args[1])?.to_machine_isize(this)?;

‎src/shims/panic.rs

+62-62
Original file line numberDiff line numberDiff line change
@@ -17,24 +17,22 @@ use rustc_target::spec::PanicStrategy;
1717

1818
use crate::*;
1919

20-
/// Holds all of the relevant data for a call to
21-
/// `__rust_maybe_catch_panic`.
22-
///
23-
/// If a panic occurs, we update this data with
24-
/// the information from the panic site.
20+
/// Holds all of the relevant data for when unwinding hits a `try` frame.
2521
#[derive(Debug)]
2622
pub struct CatchUnwindData<'tcx> {
27-
/// The dereferenced `data_ptr` argument passed to `__rust_maybe_catch_panic`.
28-
pub data_place: MPlaceTy<'tcx, Tag>,
29-
/// The dereferenced `vtable_ptr` argument passed to `__rust_maybe_catch_panic`.
30-
pub vtable_place: MPlaceTy<'tcx, Tag>,
31-
/// The `dest` from the original call to `__rust_maybe_catch_panic`.
32-
pub dest: PlaceTy<'tcx, Tag>,
23+
/// The `catch_fn` callback to call in case of a panic.
24+
catch_fn: Scalar<Tag>,
25+
/// The `data` argument for that callback.
26+
data: Scalar<Tag>,
27+
/// The return place from the original call to `try`.
28+
dest: PlaceTy<'tcx, Tag>,
29+
/// The return block from the original call to `try`.
30+
ret: mir::BasicBlock,
3331
}
3432

3533
impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
3634
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
37-
/// Handles the special "miri_start_panic" intrinsic, which is called
35+
/// Handles the special `miri_start_panic` intrinsic, which is called
3836
/// by libpanic_unwind to delegate the actual unwinding process to Miri.
3937
fn handle_miri_start_panic(
4038
&mut self,
@@ -46,47 +44,50 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
4644
trace!("miri_start_panic: {:?}", this.frame().span);
4745

4846
// Get the raw pointer stored in arg[0] (the panic payload).
49-
let scalar = this.read_immediate(args[0])?;
47+
let payload = this.read_scalar(args[0])?.not_undef()?;
5048
assert!(
5149
this.machine.panic_payload.is_none(),
5250
"the panic runtime should avoid double-panics"
5351
);
54-
this.machine.panic_payload = Some(scalar);
52+
this.machine.panic_payload = Some(payload);
5553

5654
// Jump to the unwind block to begin unwinding.
5755
this.unwind_to_block(unwind);
5856
return Ok(());
5957
}
6058

61-
fn handle_catch_panic(
59+
/// Handles the `try` intrinsic, the underlying implementation of `std::panicking::try`.
60+
fn handle_try(
6261
&mut self,
6362
args: &[OpTy<'tcx, Tag>],
6463
dest: PlaceTy<'tcx, Tag>,
6564
ret: mir::BasicBlock,
6665
) -> InterpResult<'tcx> {
6766
let this = self.eval_context_mut();
68-
let tcx = &{ this.tcx.tcx };
6967

70-
// fn __rust_maybe_catch_panic(
71-
// f: fn(*mut u8),
72-
// data: *mut u8,
73-
// data_ptr: *mut usize,
74-
// vtable_ptr: *mut usize,
75-
// ) -> u32
68+
// Signature:
69+
// fn r#try(try_fn: fn(*mut u8), data: *mut u8, catch_fn: fn(*mut u8, *mut u8)) -> i32
70+
// Calls `try_fn` with `data` as argument. If that executes normally, returns 0.
71+
// If that unwinds, calls `catch_fn` with the first argument being `data` and
72+
// then second argument being a target-dependent `payload` (i.e. it is up to us to define
73+
// what that is), and returns 1.
74+
// The `payload` is passed (by libstd) to `__rust_panic_cleanup`, which is then expected to
75+
// return a `Box<dyn Any + Send + 'static>`.
76+
// In Miri, `miri_start_panic` is passed exactly that type, so we make the `payload` simply
77+
// a pointer to `Box<dyn Any + Send + 'static>`.
7678

7779
// Get all the arguments.
78-
let f = this.read_scalar(args[0])?.not_undef()?;
79-
let f_arg = this.read_scalar(args[1])?.not_undef()?;
80-
let data_place = this.deref_operand(args[2])?;
81-
let vtable_place = this.deref_operand(args[3])?;
82-
83-
// Now we make a function call, and pass `f_arg` as first and only argument.
84-
let f_instance = this.memory.get_fn(f)?.as_instance()?;
85-
trace!("__rust_maybe_catch_panic: {:?}", f_instance);
86-
let ret_place = MPlaceTy::dangling(this.layout_of(tcx.mk_unit())?, this).into();
80+
let try_fn = this.read_scalar(args[0])?.not_undef()?;
81+
let data = this.read_scalar(args[1])?.not_undef()?;
82+
let catch_fn = this.read_scalar(args[2])?.not_undef()?;
83+
84+
// Now we make a function call, and pass `data` as first and only argument.
85+
let f_instance = this.memory.get_fn(try_fn)?.as_instance()?;
86+
trace!("try_fn: {:?}", f_instance);
87+
let ret_place = MPlaceTy::dangling(this.layout_of(this.tcx.mk_unit())?, this).into();
8788
this.call_function(
8889
f_instance,
89-
&[f_arg.into()],
90+
&[data.into()],
9091
Some(ret_place),
9192
// Directly return to caller.
9293
StackPopCleanup::Goto { ret: Some(ret), unwind: None },
@@ -95,12 +96,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
9596
// We ourselves will return `0`, eventually (will be overwritten if we catch a panic).
9697
this.write_null(dest)?;
9798

98-
// In unwind mode, we tag this frame with some extra data.
99+
// In unwind mode, we tag this frame with the extra data needed to catch unwinding.
99100
// This lets `handle_stack_pop` (below) know that we should stop unwinding
100101
// when we pop this frame.
101-
if this.tcx.tcx.sess.panic_strategy() == PanicStrategy::Unwind {
102-
this.frame_mut().extra.catch_panic =
103-
Some(CatchUnwindData { data_place, vtable_place, dest })
102+
if this.tcx.sess.panic_strategy() == PanicStrategy::Unwind {
103+
this.frame_mut().extra.catch_unwind = Some(CatchUnwindData { catch_fn, data, dest, ret });
104104
}
105105

106106
return Ok(());
@@ -110,45 +110,45 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
110110
&mut self,
111111
mut extra: FrameData<'tcx>,
112112
unwinding: bool,
113-
) -> InterpResult<'tcx, StackPopInfo> {
113+
) -> InterpResult<'tcx, StackPopJump> {
114114
let this = self.eval_context_mut();
115115

116116
trace!("handle_stack_pop(extra = {:?}, unwinding = {})", extra, unwinding);
117+
if let Some(stacked_borrows) = this.memory.extra.stacked_borrows.as_ref() {
118+
stacked_borrows.borrow_mut().end_call(extra.call_id);
119+
}
117120

118121
// We only care about `catch_panic` if we're unwinding - if we're doing a normal
119122
// return, then we don't need to do anything special.
120-
let res = if let (true, Some(unwind_data)) = (unwinding, extra.catch_panic.take()) {
121-
// We've just popped a frame that was pushed by `__rust_maybe_catch_panic`,
123+
if let (true, Some(catch_unwind)) = (unwinding, extra.catch_unwind.take()) {
124+
// We've just popped a frame that was pushed by `try`,
122125
// and we are unwinding, so we should catch that.
123126
trace!("unwinding: found catch_panic frame during unwinding: {:?}", this.frame().span);
124127

125-
// `panic_payload` now holds a `*mut (dyn Any + Send)`,
126-
// provided by the `miri_start_panic` intrinsic.
127-
// We want to split this into its consituient parts -
128-
// the data and vtable pointers - and store them according to
129-
// `unwind_data`, i.e., we store them where `__rust_maybe_catch_panic`
130-
// was told to put them.
131-
let payload = this.machine.panic_payload.take().unwrap();
132-
let payload = this.ref_to_mplace(payload)?;
133-
let payload_data_place = payload.ptr;
134-
let payload_vtable_place = payload.meta.unwrap_meta();
135-
136-
this.write_scalar(payload_data_place, unwind_data.data_place.into())?;
137-
this.write_scalar(payload_vtable_place, unwind_data.vtable_place.into())?;
128+
// We set the return value of `try` to 1, since there was a panic.
129+
this.write_scalar(Scalar::from_i32(1), catch_unwind.dest)?;
138130

139-
// We set the return value of `__rust_maybe_catch_panic` to 1,
140-
// since there was a panic.
141-
let dest = unwind_data.dest;
142-
this.write_scalar(Scalar::from_int(1, dest.layout.size), dest)?;
131+
// `panic_payload` holds what was passed to `miri_start_panic`.
132+
// This is exactly the second argument we need to pass to `catch_fn`.
133+
let payload = this.machine.panic_payload.take().unwrap();
143134

144-
StackPopInfo::StopUnwinding
135+
// Push the `catch_fn` stackframe.
136+
let f_instance = this.memory.get_fn(catch_unwind.catch_fn)?.as_instance()?;
137+
trace!("catch_fn: {:?}", f_instance);
138+
let ret_place = MPlaceTy::dangling(this.layout_of(this.tcx.mk_unit())?, this).into();
139+
this.call_function(
140+
f_instance,
141+
&[catch_unwind.data.into(), payload.into()],
142+
Some(ret_place),
143+
// Directly return to caller of `try`.
144+
StackPopCleanup::Goto { ret: Some(catch_unwind.ret), unwind: None },
145+
)?;
146+
147+
// We pushed a new stack frame, the engine should not do any jumping now!
148+
Ok(StackPopJump::NoJump)
145149
} else {
146-
StackPopInfo::Normal
147-
};
148-
if let Some(stacked_borrows) = this.memory.extra.stacked_borrows.as_ref() {
149-
stacked_borrows.borrow_mut().end_call(extra.call_id);
150+
Ok(StackPopJump::Normal)
150151
}
151-
Ok(res)
152152
}
153153

154154
/// Starta a panic in the interpreter with the given message as payload.

0 commit comments

Comments
 (0)
Please sign in to comment.