Revert "Fix: On wasm targets, call panic_in_cleanup if panic occurs in cleanup"

This reverts commit acbfd79acf.
This commit is contained in:
Wesley Wiser
2026-04-01 21:29:42 -05:00
parent 7e46c5f6fb
commit c9d3a00cd1
7 changed files with 24 additions and 138 deletions
@@ -1656,10 +1656,6 @@ fn catch_switch(
unimplemented!();
}
fn get_funclet_cleanuppad(&self, _funclet: &Funclet) -> RValue<'gcc> {
unimplemented!();
}
// Atomic Operations
fn atomic_cmpxchg(
&mut self,
@@ -1297,10 +1297,6 @@ fn catch_switch(
ret
}
fn get_funclet_cleanuppad(&self, funclet: &Funclet<'ll>) -> &'ll Value {
funclet.cleanuppad()
}
// Atomic Operations
fn atomic_cmpxchg(
&mut self,
+19 -77
View File
@@ -214,18 +214,19 @@ fn do_call<Bx: BuilderMethods<'a, 'tcx>>(
mir::UnwindAction::Continue => None,
mir::UnwindAction::Unreachable => None,
mir::UnwindAction::Terminate(reason) => {
if fx.mir[self.bb].is_cleanup && base::wants_wasm_eh(fx.cx.tcx().sess) {
// For wasm, we need to generate a nested `cleanuppad within %outer_pad`
// to catch exceptions during cleanup and call `panic_in_cleanup`.
Some(fx.terminate_block(reason, Some(self.bb)))
} else if fx.mir[self.bb].is_cleanup
&& base::wants_new_eh_instructions(fx.cx.tcx().sess)
{
if fx.mir[self.bb].is_cleanup && base::wants_new_eh_instructions(fx.cx.tcx().sess) {
// MSVC SEH will abort automatically if an exception tries to
// propagate out from cleanup.
// FIXME(@mirkootter): For wasm, we currently do not support terminate during
// cleanup, because this requires a few more changes: The current code
// caches the `terminate_block` for each function; funclet based code - however -
// requires a different terminate_block for each funclet
// Until this is implemented, we just do not unwind inside cleanup blocks
None
} else {
Some(fx.terminate_block(reason, None))
Some(fx.terminate_block(reason))
}
}
};
@@ -237,7 +238,7 @@ fn do_call<Bx: BuilderMethods<'a, 'tcx>>(
if let Some(unwind_block) = unwind_block {
let ret_llbb = if let Some((_, target)) = destination {
self.llbb_with_cleanup(fx, target)
fx.llbb(target)
} else {
fx.unreachable_block()
};
@@ -308,7 +309,7 @@ fn do_inlineasm<Bx: BuilderMethods<'a, 'tcx>>(
) -> MergingSucc {
let unwind_target = match unwind {
mir::UnwindAction::Cleanup(cleanup) => Some(self.llbb_with_cleanup(fx, cleanup)),
mir::UnwindAction::Terminate(reason) => Some(fx.terminate_block(reason, None)),
mir::UnwindAction::Terminate(reason) => Some(fx.terminate_block(reason)),
mir::UnwindAction::Continue => None,
mir::UnwindAction::Unreachable => None,
};
@@ -316,7 +317,7 @@ fn do_inlineasm<Bx: BuilderMethods<'a, 'tcx>>(
if operands.iter().any(|x| matches!(x, InlineAsmOperandRef::Label { .. })) {
assert!(unwind_target.is_none());
let ret_llbb = if let Some(target) = destination {
self.llbb_with_cleanup(fx, target)
fx.llbb(target)
} else {
fx.unreachable_block()
};
@@ -333,7 +334,7 @@ fn do_inlineasm<Bx: BuilderMethods<'a, 'tcx>>(
MergingSucc::False
} else if let Some(cleanup) = unwind_target {
let ret_llbb = if let Some(target) = destination {
self.llbb_with_cleanup(fx, target)
fx.llbb(target)
} else {
fx.unreachable_block()
};
@@ -1895,39 +1896,8 @@ fn unreachable_block(&mut self) -> Bx::BasicBlock {
})
}
fn terminate_block(
&mut self,
reason: UnwindTerminateReason,
outer_catchpad_bb: Option<mir::BasicBlock>,
) -> Bx::BasicBlock {
// mb_funclet_bb should be present if and only if the target is wasm and
// we're terminating because of an unwind in a cleanup block. In that
// case we have nested funclets and the inner catch_switch needs to know
// what outer catch_pad it is contained in.
debug_assert!(
outer_catchpad_bb.is_some()
== (base::wants_wasm_eh(self.cx.tcx().sess)
&& reason == UnwindTerminateReason::InCleanup)
);
// When we aren't in a wasm InCleanup block, there's only one terminate
// block needed so we cache at START_BLOCK index.
let mut cache_bb = mir::START_BLOCK;
// In wasm eh InCleanup, use the outer funclet's cleanup BB as the cache
// key.
if let Some(outer_bb) = outer_catchpad_bb {
let cleanup_kinds =
self.cleanup_kinds.as_ref().expect("cleanup_kinds required for funclets");
cache_bb = cleanup_kinds[outer_bb]
.funclet_bb(outer_bb)
.expect("funclet_bb should be in a funclet");
// Ensure the outer funclet is created first
if self.funclets[cache_bb].is_none() {
self.landing_pad_for(cache_bb);
}
}
if let Some((cached_bb, cached_reason)) = self.terminate_blocks[cache_bb]
fn terminate_block(&mut self, reason: UnwindTerminateReason) -> Bx::BasicBlock {
if let Some((cached_bb, cached_reason)) = self.terminate_block
&& reason == cached_reason
{
return cached_bb;
@@ -1965,35 +1935,12 @@ fn terminate_block(
// cp_terminate:
// %cp = catchpad within %cs [null, i32 64, null]
// ...
//
// By contrast, on WebAssembly targets, we specifically _do_ want to
// catch foreign exceptions. The situation with MSVC is a
// regrettable hack which we don't want to extend to other targets
// unless necessary. For WebAssembly, to generate catch(...) and
// catch only C++ exception instead of generating a catch_all, we
// need to call the intrinsics @llvm.wasm.get.exception and
// @llvm.wasm.get.ehselector in the catch pad. Since we don't do
// this, we generate a catch_all. We originally got this behavior
// by accident but it luckily matches our intention.
llbb = Bx::append_block(self.cx, self.llfn, "cs_terminate");
let cp_llbb = Bx::append_block(self.cx, self.llfn, "cp_terminate");
let mut cs_bx = Bx::build(self.cx, llbb);
// For wasm InCleanup blocks, our catch_switch is nested within the
// outer catchpad, so we need to provide it as the parent value to
// catch_switch.
let mut outer_cleanuppad = None;
if outer_catchpad_bb.is_some() {
// Get the outer funclet's catchpad
let outer_funclet = self.funclets[cache_bb]
.as_ref()
.expect("landing_pad_for didn't create funclet");
outer_cleanuppad = Some(cs_bx.get_funclet_cleanuppad(outer_funclet));
}
let cp_llbb = Bx::append_block(self.cx, self.llfn, "cp_terminate");
let cs = cs_bx.catch_switch(outer_cleanuppad, None, &[cp_llbb]);
drop(cs_bx);
let cs = cs_bx.catch_switch(None, None, &[cp_llbb]);
bx = Bx::build(self.cx, cp_llbb);
let null =
@@ -2014,18 +1961,13 @@ fn terminate_block(
} else {
// Specifying more arguments than necessary usually doesn't
// hurt, but the `WasmEHPrepare` LLVM pass does not recognize
// anything other than a single `null` as a `catch_all` block,
// anything other than a single `null` as a `catch (...)` block,
// leading to problems down the line during instruction
// selection.
&[null] as &[_]
};
funclet = Some(bx.catch_pad(cs, args));
// On wasm, if we wanted to generate a catch(...) and only catch C++
// exceptions, we'd call @llvm.wasm.get.exception and
// @llvm.wasm.get.ehselector selectors here. We want a catch_all so
// we leave them out. This is intentionally diverging from the MSVC
// behavior.
} else {
llbb = Bx::append_block(self.cx, self.llfn, "terminate");
bx = Bx::build(self.cx, llbb);
@@ -2051,7 +1993,7 @@ fn terminate_block(
bx.unreachable();
self.terminate_blocks[cache_bb] = Some((llbb, reason));
self.terminate_block = Some((llbb, reason));
llbb
}
+3 -6
View File
@@ -90,11 +90,8 @@ pub struct FunctionCx<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> {
/// Cached unreachable block
unreachable_block: Option<Bx::BasicBlock>,
/// Cached terminate upon unwinding block and its reason. For non-wasm
/// targets, there is at most one such block per function, stored at index
/// `START_BLOCK`. For wasm targets, each funclet needs its own terminate
/// block, indexed by the cleanup block that is the funclet's head.
terminate_blocks: IndexVec<mir::BasicBlock, Option<(Bx::BasicBlock, UnwindTerminateReason)>>,
/// Cached terminate upon unwinding block and its reason
terminate_block: Option<(Bx::BasicBlock, UnwindTerminateReason)>,
/// A bool flag for each basic block indicating whether it is a cold block.
/// A cold block is a block that is unlikely to be executed at runtime.
@@ -230,7 +227,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
personality_slot: None,
cached_llbbs,
unreachable_block: None,
terminate_blocks: IndexVec::from_elem(None, &mir.basic_blocks),
terminate_block: None,
cleanup_kinds,
landing_pads: IndexVec::from_elem(None, &mir.basic_blocks),
funclets: IndexVec::from_fn_n(|_| None, mir.basic_blocks.len()),
@@ -552,12 +552,12 @@ fn select(
fn set_personality_fn(&mut self, personality: Self::Function);
// These are used by everyone except msvc and wasm EH
// These are used by everyone except msvc
fn cleanup_landing_pad(&mut self, pers_fn: Self::Function) -> (Self::Value, Self::Value);
fn filter_landing_pad(&mut self, pers_fn: Self::Function);
fn resume(&mut self, exn0: Self::Value, exn1: Self::Value);
// These are used by msvc and wasm EH
// These are used only by msvc
fn cleanup_pad(&mut self, parent: Option<Self::Value>, args: &[Self::Value]) -> Self::Funclet;
fn cleanup_ret(&mut self, funclet: &Self::Funclet, unwind: Option<Self::BasicBlock>);
fn catch_pad(&mut self, parent: Self::Value, args: &[Self::Value]) -> Self::Funclet;
@@ -567,7 +567,6 @@ fn catch_switch(
unwind: Option<Self::BasicBlock>,
handlers: &[Self::BasicBlock],
) -> Self::Value;
fn get_funclet_cleanuppad(&self, funclet: &Self::Funclet) -> Self::Value;
fn atomic_cmpxchg(
&mut self,
-34
View File
@@ -1,34 +0,0 @@
//@ compile-flags: -C panic=unwind -Copt-level=0
//@ needs-unwind
//@ only-wasm32
#![crate_type = "lib"]
// Test that `panic_in_cleanup` is called on webassembly targets when a panic
// occurs in a destructor during unwinding.
extern "Rust" {
fn may_panic();
}
struct PanicOnDrop;
impl Drop for PanicOnDrop {
fn drop(&mut self) {
unsafe { may_panic() }
}
}
// CHECK-LABEL: @double_panic
// CHECK: invoke void @may_panic()
// CHECK: invoke void @{{.+}}drop_in_place{{.+}}
// CHECK: unwind label %[[TERMINATE:.*]]
//
// CHECK: [[TERMINATE]]:
// CHECK: call void @{{.*panic_in_cleanup}}
// CHECK: unreachable
#[no_mangle]
pub fn double_panic() {
let _guard = PanicOnDrop;
unsafe { may_panic() }
}
@@ -9,10 +9,6 @@
// Ensure a catch-all generates:
// - `catchpad ... [ptr null]` on Wasm (otherwise LLVM gets confused)
// - `catchpad ... [ptr null, i32 64, ptr null]` on Windows (otherwise we catch SEH exceptions)
//
// Unlike on windows, on Wasm, we specifically do want to catch foreign
// exceptions. To catch only C++ exceptions we'd need to call
// @llvm.wasm.get.exception and @llvm.wasm.get.ehselector in the catchpad.
#![feature(no_core, lang_items, rustc_attrs)]
#![crate_type = "lib"]
@@ -40,14 +36,8 @@ fn panic_cannot_unwind() -> ! {
#[no_mangle]
#[rustc_nounwind]
pub fn doesnt_unwind() {
// CHECK: catchswitch within none [label %{{.*}}] unwind to caller
// emscripten: %catchpad = catchpad within %catchswitch [ptr null]
// wasi: %catchpad = catchpad within %catchswitch [ptr null]
// seh: %catchpad = catchpad within %catchswitch [ptr null, i32 64, ptr null]
//
// We don't call these intrinsics on wasm targets so we generate a catch_all
// instruction which also picks up foreign exceptions
// NOT: @llvm.wasm.get.exception
// NOT: @llvm.wasm.get.ehselector
unwinds();
}