Skip to content

Commit 2e98397

Browse files
committed
[WebAssembly] Fix wasm.lsda() optimization in WasmEHPrepare
Summary: When we insert a call to the personality function wrapper (`_Unwind_CallPersonality`) for a catch pad, we store some necessary info in `__wasm_lpad_context` struct and pass it. One of the info is the LSDA address for the function. For this, we insert a call to `wasm.lsda()`, which will be lowered down to the address of LSDA, and store it in a field in `__wasm_lpad_context`. There are exceptions to this personality call insertion: catchpads for `catch (...)` and cleanuppads (for destructors) don't need personality function calls, because we don't need to figure out whether the current exception should be caught or not. (They always should.) There was a little optimization to `wasm.lsda()` call insertion. Because the LSDA address is the same throughout a function, we don't need to insert a store of `wasm.lsda()` return value in every catchpad. For example: ``` try { foo(); } catch (int) { // wasm.lsda() call and a store are inserted here, like, in // pseudocode, // %lsda = wasm.lsda(); // store %lsda to a field in __wasm_lpad_context try { foo(); } catch (int) { // We don't need to insert the wasm.lsda() and store again, because // to arrive here, we have already stored the LSDA address to // __wasm_lpad_context in the outer catch. } } ``` So the previous algorithm checked if the current catch has a parent EH pad, we didn't insert a call to `wasm.lsda()` and its store. But this was incorrect, because what if the outer catch is `catch (...)` or a cleanuppad? ``` try { foo(); } catch (...) { // wasm.lsda() call and a store are NOT inserted here try { foo(); } catch (int) { // We need wasm.lsda() here! } } ``` In this case we need to insert `wasm.lsda()` in the inner catchpad, because the outer catchpad does not have one. To minimize the number of inserted `wasm.lsda()` calls and stores, we need a way to figure out whether we have encountered `wasm.lsda()` call in any of EH pads that dominates the current EH pad. To figure that out, we now visit EH pads in BFS order in the dominator tree so that we visit parent BBs first before visiting its child BBs in the domtree. We keep a set named `ExecutedLSDA`, which basically means "Do we have `wasm.lsda()` either in the current EH pad or any of its parent EH pads in the dominator tree?". This is to prevent scanning the domtree up to the root in the worst case every time we examine an EH pad: each EH pad only needs to examine its immediate parent EH pad. - If any of its parent EH pads in the domtree has `wasm.lsda()`, this means we don't need `wasm.lsda()` in the current EH pad. We also insert the current EH pad in `ExecutedLSDA` set. - If none of its parent EH pad has `wasm.lsda()` - If the current EH pad is a `catch (...)` or a cleanuppad, done. - If the current EH pad is neither a `catch (...)` nor a cleanuppad, add `wasm.lsda()` and the store in the current EH pad, and add the current EH pad to `ExecutedLSDA` set. Reviewers: dschuff Subscribers: sbc100, jgravelle-google, hiraditya, sunfish, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D77423
1 parent 6a57ba1 commit 2e98397

File tree

2 files changed

+324
-55
lines changed

2 files changed

+324
-55
lines changed

llvm/lib/CodeGen/WasmEHPrepare.cpp

Lines changed: 110 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@
7777
//
7878
//===----------------------------------------------------------------------===//
7979

80+
#include "llvm/ADT/BreadthFirstIterator.h"
8081
#include "llvm/ADT/SetVector.h"
8182
#include "llvm/ADT/Statistic.h"
8283
#include "llvm/ADT/Triple.h"
@@ -118,14 +119,17 @@ class WasmEHPrepare : public FunctionPass {
118119
bool prepareEHPads(Function &F);
119120
bool prepareThrows(Function &F);
120121

121-
void prepareEHPad(BasicBlock *BB, bool NeedLSDA, unsigned Index = 0);
122+
bool IsEHPadFunctionsSetUp = false;
123+
void setupEHPadFunctions(Function &F);
124+
void prepareEHPad(BasicBlock *BB, bool NeedPersonality, bool NeedLSDA = false,
125+
unsigned Index = 0);
122126
void prepareTerminateCleanupPad(BasicBlock *BB);
123127

124128
public:
125129
static char ID; // Pass identification, replacement for typeid
126130

127131
WasmEHPrepare() : FunctionPass(ID) {}
128-
132+
void getAnalysisUsage(AnalysisUsage &AU) const override;
129133
bool doInitialization(Module &M) override;
130134
bool runOnFunction(Function &F) override;
131135

@@ -136,11 +140,18 @@ class WasmEHPrepare : public FunctionPass {
136140
} // end anonymous namespace
137141

138142
char WasmEHPrepare::ID = 0;
139-
INITIALIZE_PASS(WasmEHPrepare, DEBUG_TYPE, "Prepare WebAssembly exceptions",
140-
false, false)
143+
INITIALIZE_PASS_BEGIN(WasmEHPrepare, DEBUG_TYPE,
144+
"Prepare WebAssembly exceptions", false, false)
145+
INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass)
146+
INITIALIZE_PASS_END(WasmEHPrepare, DEBUG_TYPE, "Prepare WebAssembly exceptions",
147+
false, false)
141148

142149
FunctionPass *llvm::createWasmEHPass() { return new WasmEHPrepare(); }
143150

151+
void WasmEHPrepare::getAnalysisUsage(AnalysisUsage &AU) const {
152+
AU.addRequired<DominatorTreeWrapperPass>();
153+
}
154+
144155
bool WasmEHPrepare::doInitialization(Module &M) {
145156
IRBuilder<> IRB(M.getContext());
146157
LPadContextTy = StructType::get(IRB.getInt32Ty(), // lpad_index
@@ -165,6 +176,7 @@ static void eraseDeadBBsAndChildren(const Container &BBs) {
165176
}
166177

167178
bool WasmEHPrepare::runOnFunction(Function &F) {
179+
IsEHPadFunctionsSetUp = false;
168180
bool Changed = false;
169181
Changed |= prepareThrows(F);
170182
Changed |= prepareEHPads(F);
@@ -201,23 +213,95 @@ bool WasmEHPrepare::prepareThrows(Function &F) {
201213
}
202214

203215
bool WasmEHPrepare::prepareEHPads(Function &F) {
204-
Module &M = *F.getParent();
205-
IRBuilder<> IRB(F.getContext());
216+
auto &DT = getAnalysis<DominatorTreeWrapperPass>().getDomTree();
217+
bool Changed = false;
206218

207-
SmallVector<BasicBlock *, 16> CatchPads;
208-
SmallVector<BasicBlock *, 16> CleanupPads;
209-
for (BasicBlock &BB : F) {
210-
if (!BB.isEHPad())
219+
// There are two things to decide: whether we need a personality function call
220+
// and whether we need a `wasm.lsda()` call and its store.
221+
//
222+
// For the personality function call, catchpads with `catch (...)` and
223+
// cleanuppads don't need it, because exceptions are always caught. Others all
224+
// need it.
225+
//
226+
// For `wasm.lsda()` and its store, in order to minimize the number of them,
227+
// we need a way to figure out whether we have encountered `wasm.lsda()` call
228+
// in any of EH pads that dominates the current EH pad. To figure that out, we
229+
// now visit EH pads in BFS order in the dominator tree so that we visit
230+
// parent BBs first before visiting its child BBs in the domtree.
231+
//
232+
// We keep a set named `ExecutedLSDA`, which basically means "Do we have
233+
// `wasm.lsda() either in the current EH pad or any of its parent EH pads in
234+
// the dominator tree?". This is to prevent scanning the domtree up to the
235+
// root every time we examine an EH pad, in the worst case: each EH pad only
236+
// needs to check its immediate parent EH pad.
237+
//
238+
// - If any of its parent EH pads in the domtree has `wasm.lsda`, this means
239+
// we don't need `wasm.lsda()` in the current EH pad. We also insert the
240+
// current EH pad in `ExecutedLSDA` set.
241+
// - If none of its parent EH pad has `wasm.lsda()`,
242+
// - If the current EH pad is a `catch (...)` or a cleanuppad, done.
243+
// - If the current EH pad is neither a `catch (...)` nor a cleanuppad,
244+
// add `wasm.lsda()` and the store in the current EH pad, and add the
245+
// current EH pad to `ExecutedLSDA` set.
246+
//
247+
// TODO Can we not store LSDA address in user function but make libcxxabi
248+
// compute it?
249+
DenseSet<Value *> ExecutedLSDA;
250+
unsigned Index = 0;
251+
for (auto DomNode : breadth_first(&DT)) {
252+
auto *BB = DomNode->getBlock();
253+
auto *Pad = BB->getFirstNonPHI();
254+
if (!Pad || (!isa<CatchPadInst>(Pad) && !isa<CleanupPadInst>(Pad)))
211255
continue;
212-
auto *Pad = BB.getFirstNonPHI();
213-
if (isa<CatchPadInst>(Pad))
214-
CatchPads.push_back(&BB);
215-
else if (isa<CleanupPadInst>(Pad))
216-
CleanupPads.push_back(&BB);
256+
Changed = true;
257+
258+
Value *ParentPad = nullptr;
259+
if (CatchPadInst *CPI = dyn_cast<CatchPadInst>(Pad)) {
260+
ParentPad = CPI->getCatchSwitch()->getParentPad();
261+
if (ExecutedLSDA.count(ParentPad)) {
262+
ExecutedLSDA.insert(CPI);
263+
// We insert its associated catchswitch too, because
264+
// FuncletPadInst::getParentPad() returns a CatchSwitchInst if the child
265+
// FuncletPadInst is a CleanupPadInst.
266+
ExecutedLSDA.insert(CPI->getCatchSwitch());
267+
}
268+
} else { // CleanupPadInst
269+
ParentPad = cast<CleanupPadInst>(Pad)->getParentPad();
270+
if (ExecutedLSDA.count(ParentPad))
271+
ExecutedLSDA.insert(Pad);
272+
}
273+
274+
if (CatchPadInst *CPI = dyn_cast<CatchPadInst>(Pad)) {
275+
if (CPI->getNumArgOperands() == 1 &&
276+
cast<Constant>(CPI->getArgOperand(0))->isNullValue())
277+
// In case of a single catch (...), we need neither personality call nor
278+
// wasm.lsda() call
279+
prepareEHPad(BB, false);
280+
else {
281+
if (ExecutedLSDA.count(CPI))
282+
// catch (type), but one of parents already has wasm.lsda() call
283+
prepareEHPad(BB, true, false, Index++);
284+
else {
285+
// catch (type), and none of parents has wasm.lsda() call. We have to
286+
// add the call in this EH pad, and record this EH pad in
287+
// ExecutedLSDA.
288+
ExecutedLSDA.insert(CPI);
289+
ExecutedLSDA.insert(CPI->getCatchSwitch());
290+
prepareEHPad(BB, true, true, Index++);
291+
}
292+
}
293+
} else if (isa<CleanupPadInst>(Pad)) {
294+
// Cleanup pads need neither personality call nor wasm.lsda() call
295+
prepareEHPad(BB, false);
296+
}
217297
}
218298

219-
if (CatchPads.empty() && CleanupPads.empty())
220-
return false;
299+
return Changed;
300+
}
301+
302+
void WasmEHPrepare::setupEHPadFunctions(Function &F) {
303+
Module &M = *F.getParent();
304+
IRBuilder<> IRB(F.getContext());
221305
assert(F.hasPersonalityFn() && "Personality function not found");
222306

223307
// __wasm_lpad_context global variable
@@ -252,29 +336,16 @@ bool WasmEHPrepare::prepareEHPads(Function &F) {
252336
"_Unwind_CallPersonality", IRB.getInt32Ty(), IRB.getInt8PtrTy());
253337
if (Function *F = dyn_cast<Function>(CallPersonalityF.getCallee()))
254338
F->setDoesNotThrow();
255-
256-
unsigned Index = 0;
257-
for (auto *BB : CatchPads) {
258-
auto *CPI = cast<CatchPadInst>(BB->getFirstNonPHI());
259-
// In case of a single catch (...), we don't need to emit LSDA
260-
if (CPI->getNumArgOperands() == 1 &&
261-
cast<Constant>(CPI->getArgOperand(0))->isNullValue())
262-
prepareEHPad(BB, false);
263-
else
264-
prepareEHPad(BB, true, Index++);
265-
}
266-
267-
// Cleanup pads don't need LSDA.
268-
for (auto *BB : CleanupPads)
269-
prepareEHPad(BB, false);
270-
271-
return true;
272339
}
273340

274-
// Prepare an EH pad for Wasm EH handling. If NeedLSDA is false, Index is
341+
// Prepare an EH pad for Wasm EH handling. If NeedPersonality is false, Index is
275342
// ignored.
276-
void WasmEHPrepare::prepareEHPad(BasicBlock *BB, bool NeedLSDA,
277-
unsigned Index) {
343+
void WasmEHPrepare::prepareEHPad(BasicBlock *BB, bool NeedPersonality,
344+
bool NeedLSDA, unsigned Index) {
345+
if (!IsEHPadFunctionsSetUp) {
346+
IsEHPadFunctionsSetUp = true;
347+
setupEHPadFunctions(*BB->getParent());
348+
}
278349
assert(BB->isEHPad() && "BB is not an EHPad!");
279350
IRBuilder<> IRB(BB->getContext());
280351
IRB.SetInsertPoint(&*BB->getFirstInsertionPt());
@@ -304,7 +375,7 @@ void WasmEHPrepare::prepareEHPad(BasicBlock *BB, bool NeedLSDA,
304375

305376
// In case it is a catchpad with single catch (...) or a cleanuppad, we don't
306377
// need to call personality function because we don't need a selector.
307-
if (!NeedLSDA) {
378+
if (!NeedPersonality) {
308379
if (GetSelectorCI) {
309380
assert(GetSelectorCI->use_empty() &&
310381
"wasm.get.ehselector() still has uses!");
@@ -322,14 +393,8 @@ void WasmEHPrepare::prepareEHPad(BasicBlock *BB, bool NeedLSDA,
322393
// Pseudocode: __wasm_lpad_context.lpad_index = index;
323394
IRB.CreateStore(IRB.getInt32(Index), LPadIndexField);
324395

325-
// Store LSDA address only if this catchpad belongs to a top-level
326-
// catchswitch. If there is another catchpad that dominates this pad, we don't
327-
// need to store LSDA address again, because they are the same throughout the
328-
// function and have been already stored before.
329-
// TODO Can we not store LSDA address in user function but make libcxxabi
330-
// compute it?
331396
auto *CPI = cast<CatchPadInst>(FPI);
332-
if (isa<ConstantTokenNone>(CPI->getCatchSwitch()->getParentPad()))
397+
if (NeedLSDA)
333398
// Pseudocode: __wasm_lpad_context.lsda = wasm.lsda();
334399
IRB.CreateStore(IRB.CreateCall(LSDAF), LSDAField);
335400

0 commit comments

Comments
 (0)