Skip to content

Commit ca486fb

Browse files
committed
working exception throw to/from host
1 parent f542f63 commit ca486fb

File tree

7 files changed

+83
-27
lines changed

7 files changed

+83
-27
lines changed

crates/cranelift/src/compiler.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1374,10 +1374,21 @@ fn save_last_wasm_exit_fp_and_pc(
13741374
ptr: &impl PtrSize,
13751375
limits: Value,
13761376
) {
1377+
// Save the trampoline FP to the limits. Exception unwind needs
1378+
// this so that it can know the SP (bottom of frame) for the very
1379+
// last Wasm frame.
1380+
let trampoline_fp = builder.ins().get_frame_pointer(pointer_type);
1381+
builder.ins().store(
1382+
MemFlags::trusted(),
1383+
trampoline_fp,
1384+
limits,
1385+
ptr.vmstore_context_last_wasm_exit_trampoline_fp(),
1386+
);
1387+
13771388
// Save the exit Wasm FP to the limits. We dereference the current FP to get
13781389
// the previous FP because the current FP is the trampoline's FP, and we
13791390
// want the Wasm function's FP, which is the caller of this trampoline.
1380-
let trampoline_fp = builder.ins().get_frame_pointer(pointer_type);
1391+
13811392
let wasm_fp = builder.ins().load(
13821393
pointer_type,
13831394
MemFlags::trusted(),

crates/environ/src/vmoffsets.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -190,20 +190,26 @@ pub trait PtrSize {
190190
/// Return the offset of the `gc_heap.base` field within a `VMStoreContext`.
191191
fn vmstore_context_gc_heap_base(&self) -> u8 {
192192
let offset = self.vmstore_context_gc_heap() + self.vmmemory_definition_base();
193-
debug_assert!(offset < self.vmstore_context_last_wasm_exit_fp());
193+
debug_assert!(offset < self.vmstore_context_last_wasm_exit_trampoline_fp());
194194
offset
195195
}
196196

197197
/// Return the offset of the `gc_heap.current_length` field within a `VMStoreContext`.
198198
fn vmstore_context_gc_heap_current_length(&self) -> u8 {
199199
let offset = self.vmstore_context_gc_heap() + self.vmmemory_definition_current_length();
200-
debug_assert!(offset < self.vmstore_context_last_wasm_exit_fp());
200+
debug_assert!(offset < self.vmstore_context_last_wasm_exit_trampoline_fp());
201201
offset
202202
}
203203

204+
/// Return the offset of the `last_wasm_exit_trampoline_fp` field
205+
/// of `VMStoreContext`.
206+
fn vmstore_context_last_wasm_exit_trampoline_fp(&self) -> u8 {
207+
self.vmstore_context_gc_heap() + self.size_of_vmmemory_definition()
208+
}
209+
204210
/// Return the offset of the `last_wasm_exit_fp` field of `VMStoreContext`.
205211
fn vmstore_context_last_wasm_exit_fp(&self) -> u8 {
206-
self.vmstore_context_gc_heap() + self.size_of_vmmemory_definition()
212+
self.vmstore_context_last_wasm_exit_trampoline_fp() + self.size()
207213
}
208214

209215
/// Return the offset of the `last_wasm_exit_pc` field of `VMStoreContext`.

crates/unwinder/src/throw.rs

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -50,33 +50,39 @@ pub unsafe fn compute_throw_action<F: Fn(&Frame) -> Option<usize>>(
5050
unwind: &dyn Unwind,
5151
frame_handler: F,
5252
exit_pc: usize,
53-
exit_frame: usize,
53+
exit_trampoline_frame: usize,
5454
entry_frame: usize,
5555
) -> ThrowAction {
5656
// SAFETY: the safety of `visit_frames` relies on the correctness of the
5757
// parameters passed in which is forwarded as a contract to this function
5858
// tiself.
5959
let result = unsafe {
60-
crate::stackwalk::visit_frames(unwind, exit_pc, exit_frame, entry_frame, |frame| {
61-
log::trace!("visit_frame: frame {frame:?}");
62-
let Some(sp) = frame.sp() else {
63-
// Cannot possibly unwind to this frame if SP is not
64-
// known. This is only the case for the first
65-
// (trampoline) frame; after that, we know SP at the
66-
// callsite because we know the offset from the lower
67-
// FP to the next frame's SP.
68-
return ControlFlow::Continue(());
69-
};
60+
crate::stackwalk::visit_frames(
61+
unwind,
62+
exit_pc,
63+
exit_trampoline_frame,
64+
entry_frame,
65+
|frame| {
66+
log::trace!("visit_frame: frame {frame:?}");
67+
let Some(sp) = frame.sp() else {
68+
// Cannot possibly unwind to this frame if SP is not
69+
// known. This is only the case for the first
70+
// (trampoline) frame; after that, we know SP at the
71+
// callsite because we know the offset from the lower
72+
// FP to the next frame's SP.
73+
return ControlFlow::Continue(());
74+
};
7075

71-
if let Some(handler_pc) = frame_handler(&frame) {
72-
return ControlFlow::Break(ThrowAction::Handler {
73-
pc: handler_pc,
74-
sp,
75-
fp: frame.fp(),
76-
});
77-
}
78-
ControlFlow::Continue(())
79-
})
76+
if let Some(handler_pc) = frame_handler(&frame) {
77+
return ControlFlow::Break(ThrowAction::Handler {
78+
pc: handler_pc,
79+
sp,
80+
fp: frame.fp(),
81+
});
82+
}
83+
ControlFlow::Continue(())
84+
},
85+
)
8086
};
8187
match result {
8288
ControlFlow::Break(action) => action,

crates/wasmtime/src/runtime/func.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1546,6 +1546,9 @@ pub(crate) struct EntryStoreContext {
15461546
/// Contains value of `last_wasm_exit_fp` field to restore in
15471547
/// `VMStoreContext` when exiting Wasm.
15481548
pub last_wasm_exit_fp: usize,
1549+
/// Contains value of `last_wasm_exit_trampoline_fp` field to restore in
1550+
/// `VMStoreContext` when exiting Wasm.
1551+
pub last_wasm_exit_trampoline_fp: usize,
15491552
/// Contains value of `last_wasm_entry_fp` field to restore in
15501553
/// `VMStoreContext` when exiting Wasm.
15511554
pub last_wasm_entry_fp: usize,
@@ -1639,6 +1642,11 @@ impl EntryStoreContext {
16391642
unsafe {
16401643
let last_wasm_exit_pc = *store.0.vm_store_context().last_wasm_exit_pc.get();
16411644
let last_wasm_exit_fp = *store.0.vm_store_context().last_wasm_exit_fp.get();
1645+
let last_wasm_exit_trampoline_fp = *store
1646+
.0
1647+
.vm_store_context()
1648+
.last_wasm_exit_trampoline_fp
1649+
.get();
16421650
let last_wasm_entry_fp = *store.0.vm_store_context().last_wasm_entry_fp.get();
16431651

16441652
let stack_chain = (*store.0.vm_store_context().stack_chain.get()).clone();
@@ -1652,6 +1660,7 @@ impl EntryStoreContext {
16521660
stack_limit,
16531661
last_wasm_exit_pc,
16541662
last_wasm_exit_fp,
1663+
last_wasm_exit_trampoline_fp,
16551664
last_wasm_entry_fp,
16561665
stack_chain,
16571666
vm_store_context,
@@ -1671,6 +1680,8 @@ impl EntryStoreContext {
16711680
}
16721681

16731682
*(*self.vm_store_context).last_wasm_exit_fp.get() = self.last_wasm_exit_fp;
1683+
*(*self.vm_store_context).last_wasm_exit_trampoline_fp.get() =
1684+
self.last_wasm_exit_trampoline_fp;
16741685
*(*self.vm_store_context).last_wasm_exit_pc.get() = self.last_wasm_exit_pc;
16751686
*(*self.vm_store_context).last_wasm_entry_fp.get() = self.last_wasm_entry_fp;
16761687
*(*self.vm_store_context).stack_chain.get() = self.stack_chain.clone();

crates/wasmtime/src/runtime/vm/throw.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,10 @@ pub unsafe fn compute_throw(nogc: &mut AutoAssertNoGc, exnref: &VMExnRef) -> Thr
2323
);
2424

2525
// Get the state needed for a stack walk.
26-
let (exit_pc, exit_fp, entry_fp) = unsafe {
26+
let (exit_pc, exit_trampoline_fp, entry_fp) = unsafe {
2727
(
2828
*nogc.vm_store_context().last_wasm_exit_pc.get(),
29-
*nogc.vm_store_context().last_wasm_exit_fp.get(),
29+
*nogc.vm_store_context().last_wasm_exit_trampoline_fp.get(),
3030
*nogc.vm_store_context().last_wasm_entry_fp.get(),
3131
)
3232
};
@@ -104,7 +104,7 @@ pub unsafe fn compute_throw(nogc: &mut AutoAssertNoGc, exnref: &VMExnRef) -> Thr
104104
&wasmtime_unwinder::UnwindHost,
105105
handler_lookup,
106106
exit_pc,
107-
exit_fp,
107+
exit_trampoline_fp,
108108
entry_fp,
109109
)
110110
};

crates/wasmtime/src/runtime/vm/traphandlers.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -681,6 +681,10 @@ mod call_thread_state {
681681
&cx.last_wasm_exit_fp,
682682
&mut (*self.old_state).last_wasm_exit_fp,
683683
);
684+
swap(
685+
&cx.last_wasm_exit_trampoline_fp,
686+
&mut (*self.old_state).last_wasm_exit_trampoline_fp,
687+
);
684688
swap(
685689
&cx.last_wasm_exit_pc,
686690
&mut (*self.old_state).last_wasm_exit_pc,

crates/wasmtime/src/runtime/vm/vmcontext.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1109,6 +1109,19 @@ pub struct VMStoreContext {
11091109
/// The `VMMemoryDefinition` for this store's GC heap.
11101110
pub gc_heap: VMMemoryDefinition,
11111111

1112+
/// The value of the frame pointer register in the trampoline used
1113+
/// to call from Wasm to the host.
1114+
///
1115+
/// Maintained by our Wasm-to-host trampoline, and cleared just
1116+
/// before calling into Wasm in `catch_traps`.
1117+
///
1118+
/// This member is `0` when Wasm is actively running and has not called out
1119+
/// to the host.
1120+
///
1121+
/// Used to find the size of the bottom-most Wasm frame (relative
1122+
/// to `last_wasm_exit_fp`) when walking the stack.
1123+
pub last_wasm_exit_trampoline_fp: UnsafeCell<usize>,
1124+
11121125
/// The value of the frame pointer register when we last called from Wasm to
11131126
/// the host.
11141127
///
@@ -1189,6 +1202,7 @@ impl Default for VMStoreContext {
11891202
current_length: AtomicUsize::new(0),
11901203
},
11911204
last_wasm_exit_fp: UnsafeCell::new(0),
1205+
last_wasm_exit_trampoline_fp: UnsafeCell::new(0),
11921206
last_wasm_exit_pc: UnsafeCell::new(0),
11931207
last_wasm_entry_fp: UnsafeCell::new(0),
11941208
stack_chain: UnsafeCell::new(VMStackChain::Absent),
@@ -1231,6 +1245,10 @@ mod test_vmstore_context {
12311245
offset_of!(VMStoreContext, gc_heap) + offset_of!(VMMemoryDefinition, current_length),
12321246
usize::from(offsets.ptr.vmstore_context_gc_heap_current_length())
12331247
);
1248+
assert_eq!(
1249+
offset_of!(VMStoreContext, last_wasm_exit_trampoline_fp),
1250+
usize::from(offsets.ptr.vmstore_context_last_wasm_exit_trampoline_fp())
1251+
);
12341252
assert_eq!(
12351253
offset_of!(VMStoreContext, last_wasm_exit_fp),
12361254
usize::from(offsets.ptr.vmstore_context_last_wasm_exit_fp())

0 commit comments

Comments
 (0)