Skip to content

Commit be0efa1

Browse files
committed
[WebAssembly] Handle EH terminate pads for cleanup
Terminate pads, cleanup pads with `__clang_call_terminate` call, have `catch` instruction in them because `__clang_call_terminate` takes an exception pointer. But these terminate pads should be reached also in case of foreign exception. So this pass attaches an additional `catch_all` BB after every terminate pad BB, with a call to `std::terminate`. Reviewed By: tlively Differential Revision: https://reviews.llvm.org/D94050
1 parent e117295 commit be0efa1

File tree

5 files changed

+164
-0
lines changed

5 files changed

+164
-0
lines changed

llvm/lib/Target/WebAssembly/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ add_llvm_target(WebAssemblyCodeGen
2222
WebAssemblyCFGSort.cpp
2323
WebAssemblyDebugFixup.cpp
2424
WebAssemblyDebugValueManager.cpp
25+
WebAssemblyHandleEHTerminatePads.cpp
2526
WebAssemblyLateEHPrepare.cpp
2627
WebAssemblyExceptionInfo.cpp
2728
WebAssemblyExplicitLocals.cpp

llvm/lib/Target/WebAssembly/WebAssembly.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ FunctionPass *createWebAssemblyFixIrreducibleControlFlow();
4949
FunctionPass *createWebAssemblyLateEHPrepare();
5050
FunctionPass *createWebAssemblyCFGSort();
5151
FunctionPass *createWebAssemblyCFGStackify();
52+
FunctionPass *createWebAssemblyHandleEHTerminatePads();
5253
FunctionPass *createWebAssemblyExplicitLocals();
5354
FunctionPass *createWebAssemblyLowerBrUnless();
5455
FunctionPass *createWebAssemblyRegNumbering();
@@ -75,6 +76,7 @@ void initializeWebAssemblyLateEHPreparePass(PassRegistry &);
7576
void initializeWebAssemblyExceptionInfoPass(PassRegistry &);
7677
void initializeWebAssemblyCFGSortPass(PassRegistry &);
7778
void initializeWebAssemblyCFGStackifyPass(PassRegistry &);
79+
void initializeWebAssemblyHandleEHTerminatePadsPass(PassRegistry &);
7880
void initializeWebAssemblyExplicitLocalsPass(PassRegistry &);
7981
void initializeWebAssemblyLowerBrUnlessPass(PassRegistry &);
8082
void initializeWebAssemblyRegNumberingPass(PassRegistry &);
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
// WebAssemblyHandleEHTerminatePads.cpp - WebAssembly Handle EH TerminatePads //
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
///
9+
/// \file
10+
/// \brief Add catch_all blocks to terminate pads.
11+
///
12+
/// Terminate pads are cleanup pads with a __clang_call_terminate call. These
13+
/// are reached when an exception is thrown again in the middle of processing a
14+
/// thrown exception, to terminate the program. These are cleanup pads that
15+
/// should run regardless whether the thrown exception is a C++ exception or
16+
/// not.
17+
///
18+
/// Because __clang_call_terminate takes an exception pointer, and
19+
/// llvm.get.exception intrinsic is selected to 'catch' instruction in
20+
/// instruction selection, terminate pads have a catch instruction and are in
21+
/// this form after LateEHPrepare, even though they are cleanup pads:
22+
/// termpad:
23+
/// %exn = catch $__cpp_exception
24+
/// call @__clang_call_terminate(%exn)
25+
/// unreachable
26+
///
27+
/// This pass assumes LateEHPrepare ensured every terminate pad is a single
28+
/// BB.
29+
///
30+
/// __clang_call_terminate is a function generated by clang, in the form of
31+
/// void __clang_call_terminate(i8* %arg) {
32+
/// call @__cxa_begin_catch(%arg)
33+
/// call void @std::terminate()
34+
/// unreachable
35+
/// }
36+
///
37+
/// To make the terminate pads reachable when a foreign exception is thrown,
38+
/// this pass attaches an additional catch_all BB after this catch terminate pad
39+
/// BB, with a call to std::terminate, because foreign exceptions don't have a
40+
/// valid exception pointer to call __cxa_begin_catch with. So the code example
41+
/// becomes:
42+
/// termpad:
43+
/// %exn = catch $__cpp_exception
44+
/// call @__clang_call_terminate(%exn)
45+
/// unreachable
46+
/// termpad-catchall:
47+
/// catch_all
48+
/// call @std::terminate()
49+
/// unreachable
50+
///
51+
/// We do this at the very end of compilation pipeline, even after CFGStackify,
52+
/// because even though wasm spec allows multiple catch/catch_all blocks per a
53+
/// try instruction, it has been convenient to maintain the invariant so far
54+
/// that there has been only a single catch or catch_all attached to a try. This
55+
/// assumption makes ExceptionInfo generation and CFGStackify simpler, because
56+
/// we have been always able to assume an EH pad is an end of try block and a
57+
/// start of catch/catch_all block.
58+
//===----------------------------------------------------------------------===//
59+
60+
#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
61+
#include "WebAssembly.h"
62+
#include "WebAssemblySubtarget.h"
63+
#include "WebAssemblyUtilities.h"
64+
#include "llvm/CodeGen/MachineModuleInfo.h"
65+
#include "llvm/MC/MCAsmInfo.h"
66+
#include "llvm/Target/TargetMachine.h"
67+
using namespace llvm;
68+
69+
#define DEBUG_TYPE "wasm-handle-termpads"
70+
71+
namespace {
72+
class WebAssemblyHandleEHTerminatePads final : public MachineFunctionPass {
73+
StringRef getPassName() const override {
74+
return "WebAssembly Handle EH Terminate Pads";
75+
}
76+
77+
bool runOnMachineFunction(MachineFunction &MF) override;
78+
79+
public:
80+
static char ID; // Pass identification, replacement for typeid
81+
WebAssemblyHandleEHTerminatePads() : MachineFunctionPass(ID) {}
82+
};
83+
} // end anonymous namespace
84+
85+
char WebAssemblyHandleEHTerminatePads::ID = 0;
86+
INITIALIZE_PASS(WebAssemblyHandleEHTerminatePads, DEBUG_TYPE,
87+
"WebAssembly Handle EH Terminate Pads", false, false)
88+
89+
FunctionPass *llvm::createWebAssemblyHandleEHTerminatePads() {
90+
return new WebAssemblyHandleEHTerminatePads();
91+
}
92+
93+
bool WebAssemblyHandleEHTerminatePads::runOnMachineFunction(
94+
MachineFunction &MF) {
95+
LLVM_DEBUG(dbgs() << "********** Handle EH Terminate Pads **********\n"
96+
"********** Function: "
97+
<< MF.getName() << '\n');
98+
99+
if (MF.getTarget().getMCAsmInfo()->getExceptionHandlingType() !=
100+
ExceptionHandling::Wasm ||
101+
!MF.getFunction().hasPersonalityFn())
102+
return false;
103+
104+
const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
105+
106+
// Find calls to __clang_call_terminate()
107+
SmallVector<MachineInstr *, 8> ClangCallTerminateCalls;
108+
for (auto &MBB : MF) {
109+
for (auto &MI : MBB) {
110+
if (MI.isCall()) {
111+
const MachineOperand &CalleeOp = MI.getOperand(0);
112+
if (CalleeOp.isGlobal() && CalleeOp.getGlobal()->getName() ==
113+
WebAssembly::ClangCallTerminateFn)
114+
ClangCallTerminateCalls.push_back(&MI);
115+
}
116+
}
117+
}
118+
119+
if (ClangCallTerminateCalls.empty())
120+
return false;
121+
122+
for (auto *Call : ClangCallTerminateCalls) {
123+
// This should be an EH pad because LateEHPrepare ensures terminate pads are
124+
// a single BB.
125+
MachineBasicBlock *CatchBB = Call->getParent();
126+
assert(CatchBB->isEHPad());
127+
128+
auto *CatchAllBB = MF.CreateMachineBasicBlock();
129+
MF.insert(std::next(CatchBB->getIterator()), CatchAllBB);
130+
CatchAllBB->setIsEHPad(true);
131+
for (auto *Pred : CatchBB->predecessors())
132+
Pred->addSuccessor(CatchAllBB);
133+
134+
// If the definition of __clang_call_terminate exists in the module, there
135+
// should be a declaration of std::terminate within the same module, because
136+
// __clang_call_terminate calls it.
137+
const auto *StdTerminateFn =
138+
MF.getMMI().getModule()->getNamedValue(WebAssembly::StdTerminateFn);
139+
assert(StdTerminateFn && "std::terminate() does not exist in the module");
140+
141+
// Generate a BB in the form of:
142+
// catch_all
143+
// call @std::terminate
144+
// unreachable
145+
BuildMI(CatchAllBB, Call->getDebugLoc(), TII.get(WebAssembly::CATCH_ALL));
146+
BuildMI(CatchAllBB, Call->getDebugLoc(), TII.get(WebAssembly::CALL))
147+
.addGlobalAddress(StdTerminateFn);
148+
BuildMI(CatchAllBB, Call->getDebugLoc(), TII.get(WebAssembly::UNREACHABLE));
149+
}
150+
151+
return true;
152+
}

llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeWebAssemblyTarget() {
8282
initializeWebAssemblyExceptionInfoPass(PR);
8383
initializeWebAssemblyCFGSortPass(PR);
8484
initializeWebAssemblyCFGStackifyPass(PR);
85+
initializeWebAssemblyHandleEHTerminatePadsPass(PR);
8586
initializeWebAssemblyExplicitLocalsPass(PR);
8687
initializeWebAssemblyLowerBrUnlessPass(PR);
8788
initializeWebAssemblyRegNumberingPass(PR);
@@ -485,6 +486,10 @@ void WebAssemblyPassConfig::addPreEmitPass() {
485486
// Insert BLOCK and LOOP markers.
486487
addPass(createWebAssemblyCFGStackify());
487488

489+
// Handle terminate pads for cleanups
490+
if (TM->Options.ExceptionModel == ExceptionHandling::Wasm)
491+
addPass(createWebAssemblyHandleEHTerminatePads());
492+
488493
// Insert explicit local.get and local.set operators.
489494
if (!WasmDisableExplicitLocals)
490495
addPass(createWebAssemblyExplicitLocals());

llvm/test/CodeGen/WebAssembly/exception.ll

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,9 @@ ehcleanup: ; preds = %entry
132132
; CHECK: catch $[[EXN:[0-9]+]]=, __cpp_exception
133133
; CHECK: call __clang_call_terminate, $[[EXN]]
134134
; CHECK: unreachable
135+
; CHECK: catch_all
136+
; CHECK: call _ZSt9terminatev
137+
; CHECK: unreachable
135138
; CHECK: end_try
136139
; CHECK: rethrow
137140
; CHECK: end_try
@@ -429,6 +432,7 @@ declare i32 @llvm.eh.typeid.for(i8*)
429432
declare i8* @__cxa_begin_catch(i8*)
430433
declare void @__cxa_end_catch()
431434
declare void @__clang_call_terminate(i8*)
435+
declare void @_ZSt9terminatev()
432436
declare %struct.Temp* @_ZN4TempD2Ev(%struct.Temp* returned)
433437

434438
; CHECK: __cpp_exception:

0 commit comments

Comments
 (0)