diff --git a/include/llvm/CodeGen/WasmEHFuncInfo.h b/include/llvm/CodeGen/WasmEHFuncInfo.h index adcd16a5003..fdd0a44ed00 100644 --- a/include/llvm/CodeGen/WasmEHFuncInfo.h +++ b/include/llvm/CodeGen/WasmEHFuncInfo.h @@ -32,27 +32,42 @@ struct WasmEHFuncInfo { // When there is an entry , if an exception is not caught by A, it // should next unwind to the EH pad B. DenseMap SrcToUnwindDest; + DenseMap UnwindDestToSrc; // reverse map // Helper functions const BasicBlock *getUnwindDest(const BasicBlock *BB) const { return SrcToUnwindDest.lookup(BB).get(); } + const BasicBlock *getUnwindSrc(const BasicBlock *BB) const { + return UnwindDestToSrc.lookup(BB).get(); + } void setUnwindDest(const BasicBlock *BB, const BasicBlock *Dest) { SrcToUnwindDest[BB] = Dest; + UnwindDestToSrc[Dest] = BB; } bool hasUnwindDest(const BasicBlock *BB) const { return SrcToUnwindDest.count(BB); } + bool hasUnwindSrc(const BasicBlock *BB) const { + return UnwindDestToSrc.count(BB); + } MachineBasicBlock *getUnwindDest(MachineBasicBlock *MBB) const { return SrcToUnwindDest.lookup(MBB).get(); } + MachineBasicBlock *getUnwindSrc(MachineBasicBlock *MBB) const { + return UnwindDestToSrc.lookup(MBB).get(); + } void setUnwindDest(MachineBasicBlock *MBB, MachineBasicBlock *Dest) { SrcToUnwindDest[MBB] = Dest; + UnwindDestToSrc[Dest] = MBB; } bool hasUnwindDest(MachineBasicBlock *MBB) const { return SrcToUnwindDest.count(MBB); } + bool hasUnwindSrc(MachineBasicBlock *MBB) const { + return UnwindDestToSrc.count(MBB); + } }; // Analyze the IR in the given function to build WasmEHFuncInfo. diff --git a/lib/CodeGen/SelectionDAG/FunctionLoweringInfo.cpp b/lib/CodeGen/SelectionDAG/FunctionLoweringInfo.cpp index b5f2c154582..4ab29edfa35 100644 --- a/lib/CodeGen/SelectionDAG/FunctionLoweringInfo.cpp +++ b/lib/CodeGen/SelectionDAG/FunctionLoweringInfo.cpp @@ -341,6 +341,13 @@ void FunctionLoweringInfo::set(const Function &fn, MachineFunction &mf, NewMap[MBBMap[Src]] = MBBMap[Dst]; } EHInfo.SrcToUnwindDest = std::move(NewMap); + NewMap.clear(); + for (auto &KV : EHInfo.UnwindDestToSrc) { + const auto *Src = KV.first.get(); + const auto *Dst = KV.second.get(); + NewMap[MBBMap[Src]] = MBBMap[Dst]; + } + EHInfo.UnwindDestToSrc = std::move(NewMap); } } diff --git a/lib/Target/WebAssembly/WebAssemblyCFGSort.cpp b/lib/Target/WebAssembly/WebAssemblyCFGSort.cpp index eb3e9b91d40..1ada9394c86 100644 --- a/lib/Target/WebAssembly/WebAssemblyCFGSort.cpp +++ b/lib/Target/WebAssembly/WebAssemblyCFGSort.cpp @@ -29,6 +29,7 @@ #include "llvm/CodeGen/MachineLoopInfo.h" #include "llvm/CodeGen/MachineRegisterInfo.h" #include "llvm/CodeGen/Passes.h" +#include "llvm/CodeGen/WasmEHFuncInfo.h" #include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" using namespace llvm; @@ -218,6 +219,7 @@ static void sortBlocks(MachineFunction &MF, const MachineLoopInfo &MLI, CompareBlockNumbersBackwards> Ready; + const auto *EHInfo = MF.getWasmEHFuncInfo(); SortRegionInfo SRI(MLI, WEI); SmallVector Entries; for (MachineBasicBlock *MBB = &MF.front();;) { @@ -245,8 +247,33 @@ static void sortBlocks(MachineFunction &MF, const MachineLoopInfo &MLI, if (SuccL->getHeader() == Succ && SuccL->contains(MBB)) continue; // Decrement the predecessor count. If it's now zero, it's ready. - if (--NumPredsLeft[Succ->getNumber()] == 0) + if (--NumPredsLeft[Succ->getNumber()] == 0) { + // When we are in a SortRegion, we allow sorting of not only BBs that + // belong to the current (innermost) region but also BBs that are + // dominated by the current region header. But we should not do this for + // exceptions because there can be cases in which, for example: + // EHPad A's unwind destination (where the exception lands when it is + // not caught by EHPad A) is EHPad B, so EHPad B does not belong to the + // exception dominated by EHPad A. But EHPad B is dominated by EHPad A, + // so EHPad B can be sorted within EHPad A's exception. This is + // incorrect because we may end up delegating/rethrowing to an inner + // scope in CFGStackify. So here we make sure those unwind destinations + // are deferred until their unwind source's exception is sorted. + if (EHInfo && EHInfo->hasUnwindSrc(Succ)) { + auto *UnwindSrc = EHInfo->getUnwindSrc(Succ); + bool IsDeferred = false; + for (Entry &E : reverse(Entries)) { + if (E.TheRegion->getHeader() == UnwindSrc) { + E.Deferred.push_back(Succ); + IsDeferred = true; + break; + } + } + if (IsDeferred) + continue; + } Preferred.push(Succ); + } } // Determine the block to follow MBB. First try to find a preferred block, // to preserve the original block order when possible. diff --git a/lib/Target/WebAssembly/WebAssemblyExceptionInfo.cpp b/lib/Target/WebAssembly/WebAssemblyExceptionInfo.cpp index c75de7aa207..dfc135c9266 100644 --- a/lib/Target/WebAssembly/WebAssemblyExceptionInfo.cpp +++ b/lib/Target/WebAssembly/WebAssemblyExceptionInfo.cpp @@ -17,6 +17,7 @@ #include "llvm/ADT/PostOrderIterator.h" #include "llvm/CodeGen/MachineDominanceFrontier.h" #include "llvm/CodeGen/MachineDominators.h" +#include "llvm/CodeGen/WasmEHFuncInfo.h" #include "llvm/InitializePasses.h" using namespace llvm; @@ -39,12 +40,13 @@ bool WebAssemblyExceptionInfo::runOnMachineFunction(MachineFunction &MF) { releaseMemory(); auto &MDT = getAnalysis(); auto &MDF = getAnalysis(); - recalculate(MDT, MDF); + recalculate(MF, MDT, MDF); return false; } void WebAssemblyExceptionInfo::recalculate( - MachineDominatorTree &MDT, const MachineDominanceFrontier &MDF) { + MachineFunction &MF, MachineDominatorTree &MDT, + const MachineDominanceFrontier &MDF) { // Postorder traversal of the dominator tree. SmallVector, 8> Exceptions; for (auto DomNode : post_order(&MDT)) { @@ -56,6 +58,49 @@ void WebAssemblyExceptionInfo::recalculate( Exceptions.push_back(std::move(WE)); } + // WasmEHFuncInfo contains a map of , + // which means, if an exception is not caught by the catchpad, it should end + // up in the next unwind destination stored in this data structure. (It is + // written as catchswitch's 'unwind' destination in ll files.) The below is an + // intuitive example of their relationship in C++ code: + // try { + // try { + // } catch (int) { // catchpad + // ... // this catch (int) { ... } is grouped as an exception + // } + // } catch (...) { // next unwind destination + // } + // (The example is try-catches for illustration purpose, but the unwind + // destination can be also a cleanuppad generated by destructor calls.) So the + // unwind destination is in the outside of the catchpad's exception. + // + // We group exceptions in this analysis simply by including all BBs dominated + // by an EH pad. But in case the EH pad's unwind destination does not have any + // children outside of the exception, that unwind destination ends up also + // being dominated by the EH pad and included in the exception, which is not + // semantically correct, because it unwinds/rethrows into an inner scope. + // + // Here we extract those unwind destinations from their (incorrect) parent + // exception. Note that the unwind destinations may not be an immediate + // children of the parent exception, so we have to traverse the parent chain. + const auto *EHInfo = MF.getWasmEHFuncInfo(); + for (auto &MBB : MF) { + if (!MBB.isEHPad()) + continue; + auto *EHPad = &MBB; + if (!EHInfo->hasUnwindDest(EHPad)) + continue; + auto *UnwindDest = EHInfo->getUnwindDest(EHPad); + auto *WE = getExceptionFor(EHPad); + auto *UnwindDestWE = getExceptionFor(UnwindDest); + if (WE->contains(UnwindDestWE)) { + if (WE->getParentException()) + UnwindDestWE->setParentException(WE->getParentException()); + else + UnwindDestWE->setParentException(nullptr); + } + } + // Add BBs to exceptions for (auto DomNode : post_order(&MDT)) { MachineBasicBlock *MBB = DomNode->getBlock(); diff --git a/lib/Target/WebAssembly/WebAssemblyExceptionInfo.h b/lib/Target/WebAssembly/WebAssemblyExceptionInfo.h index 50151ec8da5..df18bec85e7 100644 --- a/lib/Target/WebAssembly/WebAssemblyExceptionInfo.h +++ b/lib/Target/WebAssembly/WebAssemblyExceptionInfo.h @@ -137,7 +137,7 @@ public: bool runOnMachineFunction(MachineFunction &) override; void releaseMemory() override; - void recalculate(MachineDominatorTree &MDT, + void recalculate(MachineFunction &MF, MachineDominatorTree &MDT, const MachineDominanceFrontier &MDF); void getAnalysisUsage(AnalysisUsage &AU) const override; diff --git a/test/CodeGen/WebAssembly/cfg-stackify-eh.ll b/test/CodeGen/WebAssembly/cfg-stackify-eh.ll index 722937638b7..531a93c1e31 100644 --- a/test/CodeGen/WebAssembly/cfg-stackify-eh.ll +++ b/test/CodeGen/WebAssembly/cfg-stackify-eh.ll @@ -97,40 +97,42 @@ try.cont: ; preds = %catch, %catch2, %en ; CHECK-LABEL: test1 ; CHECK: try -; CHECK: call foo +; CHECK: call foo ; CHECK: catch ; CHECK: block ; CHECK: block -; CHECK: br_if 0, {{.*}} # 0: down to label[[L0:[0-9]+]] -; CHECK: call $drop=, __cxa_begin_catch +; CHECK: br_if 0, {{.*}} # 0: down to label[[L0:[0-9+]]] +; CHECK: call $drop=, __cxa_begin_catch, $0 ; CHECK: try -; CHECK: call foo -; CHECK: br 2 # 2: down to label[[L1:[0-9]+]] -; CHECK: catch ; CHECK: try +; CHECK: call foo +; CHECK: br 3 # 3: down to label[[L1:[0-9+]]] +; CHECK: catch ; CHECK: block -; CHECK: br_if 0, {{.*}} # 0: down to label[[L2:[0-9]+]] -; CHECK: call $drop=, __cxa_begin_catch -; CHECK: try -; CHECK: call foo -; CHECK: br 2 # 2: down to label[[L3:[0-9]+]] -; CHECK: catch -; CHECK: call __cxa_end_catch -; CHECK: rethrow 0 # down to catch[[C0:[0-9]+]] -; CHECK: end_try -; CHECK: end_block # label[[L2]]: -; CHECK: rethrow 1 # down to catch[[C0]] -; CHECK: catch_all # catch[[C0]]: -; CHECK: call __cxa_end_catch -; CHECK: rethrow 0 # to caller -; CHECK: end_try # label[[L3]]: -; CHECK: call __cxa_end_catch -; CHECK: br 2 # 2: down to label[[L1]] +; CHECK: block +; CHECK: br_if 0, {{.*}} # 0: down to label[[L2:[0-9+]]] +; CHECK: call $drop=, __cxa_begin_catch +; CHECK: try +; CHECK: call foo +; CHECK: br 2 # 2: down to label[[L3:[0-9+]]] +; CHECK: catch_all +; CHECK: call __cxa_end_catch +; CHECK: rethrow 0 # down to catch[[L4:[0-9+]]] +; CHECK: end_try +; CHECK: end_block # label[[L2]]: +; CHECK: rethrow 1 # down to catch[[L4]] +; CHECK: end_block # label[[L3]]: +; CHECK: call __cxa_end_catch +; CHECK: br 3 # 3: down to label[[L1]] +; CHECK: end_try +; CHECK: catch_all # catch[[L4]]: +; CHECK: call __cxa_end_catch +; CHECK: rethrow 0 # to caller ; CHECK: end_try ; CHECK: end_block # label[[L0]]: ; CHECK: rethrow 1 # to caller ; CHECK: end_block # label[[L1]]: -; CHECK: call __cxa_end_catch +; CHECK: call __cxa_end_catch ; CHECK: end_try define void @test1() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { entry: diff --git a/unittests/Target/WebAssembly/WebAssemblyExceptionInfoTest.cpp b/unittests/Target/WebAssembly/WebAssemblyExceptionInfoTest.cpp index c63328a278f..003f8295ab6 100644 --- a/unittests/Target/WebAssembly/WebAssemblyExceptionInfoTest.cpp +++ b/unittests/Target/WebAssembly/WebAssemblyExceptionInfoTest.cpp @@ -169,7 +169,7 @@ body: | MachineDominanceFrontier MDF; MDT.runOnMachineFunction(*MF); MDF.getBase().analyze(MDT.getBase()); - WEI.recalculate(MDT, MDF); + WEI.recalculate(*MF, MDT, MDF); // Exception info structure: // |- bb2 (ehpad), bb3, bb4, bb5, bb6, bb8, bb9, bb10 @@ -344,7 +344,7 @@ body: | MachineDominanceFrontier MDF; MDT.runOnMachineFunction(*MF); MDF.getBase().analyze(MDT.getBase()); - WEI.recalculate(MDT, MDF); + WEI.recalculate(*MF, MDT, MDF); // Exception info structure: // |- bb1 (ehpad), bb2, bb3, bb4, bb5, bb6, bb7, bb8, bb10, bb11, bb12