@@ -17,24 +17,22 @@ use rustc_target::spec::PanicStrategy;
17
17
18
18
use crate :: * ;
19
19
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.
25
21
#[ derive( Debug ) ]
26
22
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 ,
33
31
}
34
32
35
33
impl < ' mir , ' tcx > EvalContextExt < ' mir , ' tcx > for crate :: MiriEvalContext < ' mir , ' tcx > { }
36
34
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
38
36
/// by libpanic_unwind to delegate the actual unwinding process to Miri.
39
37
fn handle_miri_start_panic (
40
38
& mut self ,
@@ -46,47 +44,50 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
46
44
trace ! ( "miri_start_panic: {:?}" , this. frame( ) . span) ;
47
45
48
46
// 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 ( ) ?;
50
48
assert ! (
51
49
this. machine. panic_payload. is_none( ) ,
52
50
"the panic runtime should avoid double-panics"
53
51
) ;
54
- this. machine . panic_payload = Some ( scalar ) ;
52
+ this. machine . panic_payload = Some ( payload ) ;
55
53
56
54
// Jump to the unwind block to begin unwinding.
57
55
this. unwind_to_block ( unwind) ;
58
56
return Ok ( ( ) ) ;
59
57
}
60
58
61
- fn handle_catch_panic (
59
+ /// Handles the `try` intrinsic, the underlying implementation of `std::panicking::try`.
60
+ fn handle_try (
62
61
& mut self ,
63
62
args : & [ OpTy < ' tcx , Tag > ] ,
64
63
dest : PlaceTy < ' tcx , Tag > ,
65
64
ret : mir:: BasicBlock ,
66
65
) -> InterpResult < ' tcx > {
67
66
let this = self . eval_context_mut ( ) ;
68
- let tcx = & { this. tcx . tcx } ;
69
67
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>`.
76
78
77
79
// 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 ( ) ;
87
88
this. call_function (
88
89
f_instance,
89
- & [ f_arg . into ( ) ] ,
90
+ & [ data . into ( ) ] ,
90
91
Some ( ret_place) ,
91
92
// Directly return to caller.
92
93
StackPopCleanup :: Goto { ret : Some ( ret) , unwind : None } ,
@@ -95,12 +96,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
95
96
// We ourselves will return `0`, eventually (will be overwritten if we catch a panic).
96
97
this. write_null ( dest) ?;
97
98
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 .
99
100
// This lets `handle_stack_pop` (below) know that we should stop unwinding
100
101
// 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 } ) ;
104
104
}
105
105
106
106
return Ok ( ( ) ) ;
@@ -110,45 +110,45 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
110
110
& mut self ,
111
111
mut extra : FrameData < ' tcx > ,
112
112
unwinding : bool ,
113
- ) -> InterpResult < ' tcx , StackPopInfo > {
113
+ ) -> InterpResult < ' tcx , StackPopJump > {
114
114
let this = self . eval_context_mut ( ) ;
115
115
116
116
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
+ }
117
120
118
121
// We only care about `catch_panic` if we're unwinding - if we're doing a normal
119
122
// 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 `,
122
125
// and we are unwinding, so we should catch that.
123
126
trace ! ( "unwinding: found catch_panic frame during unwinding: {:?}" , this. frame( ) . span) ;
124
127
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 ) ?;
138
130
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 ( ) ;
143
134
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 )
145
149
} 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 )
150
151
}
151
- Ok ( res)
152
152
}
153
153
154
154
/// Starta a panic in the interpreter with the given message as payload.
0 commit comments