Index: lib/Target/WebAssembly/CMakeLists.txt =================================================================== --- lib/Target/WebAssembly/CMakeLists.txt +++ lib/Target/WebAssembly/CMakeLists.txt @@ -17,6 +17,7 @@ WebAssemblyCallIndirectFixup.cpp WebAssemblyCFGStackify.cpp WebAssemblyCFGSort.cpp + WebAssemblyExceptionInfo.cpp WebAssemblyExceptionPrepare.cpp WebAssemblyExplicitLocals.cpp WebAssemblyFastISel.cpp Index: lib/Target/WebAssembly/WebAssembly.h =================================================================== --- lib/Target/WebAssembly/WebAssembly.h +++ lib/Target/WebAssembly/WebAssembly.h @@ -69,6 +69,7 @@ void initializeWebAssemblyRegColoringPass(PassRegistry &); void initializeWebAssemblyExplicitLocalsPass(PassRegistry &); void initializeWebAssemblyFixIrreducibleControlFlowPass(PassRegistry &); +void initializeWebAssemblyExceptionInfoPass(PassRegistry &); void initializeWebAssemblyCFGSortPass(PassRegistry &); void initializeWebAssemblyExceptionPreparePass(PassRegistry &); void initializeWebAssemblyCFGStackifyPass(PassRegistry &); Index: lib/Target/WebAssembly/WebAssemblyExceptionInfo.h =================================================================== --- /dev/null +++ lib/Target/WebAssembly/WebAssemblyExceptionInfo.h @@ -0,0 +1,163 @@ +//= WebAssemblyExceptionInfo.h - WebAssembly Exception Info -*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief This file implements WebAssemblyException information analysis. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_WEBASSEMBLY_WEBASSEMBLYEXCEPTIONINFO_H +#define LLVM_LIB_TARGET_WEBASSEMBLY_WEBASSEMBLYEXCEPTIONINFO_H + +#include "WebAssembly.h" +#include "llvm/ADT/SetVector.h" +#include "llvm/CodeGen/MachineFunctionPass.h" + +namespace llvm { + +class MachineDominatorTree; + +class WebAssemblyException { + MachineBasicBlock *EHPad = nullptr; // EH pad + + WebAssemblyException *ParentException = nullptr; + std::vector SubExceptions; + std::vector Blocks; + SmallPtrSet BlockSet; + SetVector Frontiers; + + WebAssemblyException(const WebAssemblyException &) = delete; + const WebAssemblyException &operator=(const WebAssemblyException &) = delete; + +public: + WebAssemblyException(MachineBasicBlock *EHPad) + : EHPad(EHPad), ParentException(nullptr) {} + ~WebAssemblyException() { + for (size_t i = 0, e = SubExceptions.size(); i != e; ++i) + delete SubExceptions[i]; + } + + MachineBasicBlock *getEHPad() const { return EHPad; } + MachineBasicBlock *getHeader() const { return EHPad; } + WebAssemblyException *getParentException() const { return ParentException; } + void setParentException(WebAssemblyException *WE) { ParentException = WE; } + + bool contains(const WebAssemblyException *WE) const { + if (WE == this) + return true; + if (!WE) + return false; + return contains(WE->getParentException()); + } + bool contains(const MachineBasicBlock *MBB) const { + return BlockSet.count(MBB); + } + + void addBlock(MachineBasicBlock *MBB) { + Blocks.push_back(MBB); + BlockSet.insert(MBB); + } + ArrayRef getBlocks() const { return Blocks; } + typedef + typename ArrayRef::const_iterator block_iterator; + block_iterator block_begin() const { return getBlocks().begin(); } + block_iterator block_end() const { return getBlocks().end(); } + inline iterator_range blocks() const { + return make_range(block_begin(), block_end()); + } + unsigned getNumBlocks() const { return Blocks.size(); } + std::vector &getBlocksVector() { return Blocks; } + + void addFrontier(MachineBasicBlock *MBB) { Frontiers.insert(MBB); } + const SetVector &getFrontiers() const { + return Frontiers; + } + + const std::vector &getSubExceptions() const { + return SubExceptions; + } + std::vector &getSubExceptions() { + return SubExceptions; + } + void addSubException(WebAssemblyException *E) { SubExceptions.push_back(E); } + typedef typename std::vector::const_iterator iterator; + iterator begin() const { return SubExceptions.begin(); } + iterator end() const { return SubExceptions.end(); } + + void reserveBlocks(unsigned Size) { Blocks.reserve(Size); } + void reverseBlock(unsigned From = 0) { + std::reverse(Blocks.begin() + From, Blocks.end()); + } + + // Return the nesting level. An outermost one has depth 1. + unsigned getExceptionDepth() const { + unsigned D = 1; + for (const WebAssemblyException *CurException = ParentException; + CurException; CurException = CurException->ParentException) + ++D; + return D; + } + + void print(raw_ostream &OS, unsigned Depth = 0) const; + void dump() const; +}; + +raw_ostream &operator<<(raw_ostream &OS, const WebAssemblyException &WE); + +class WebAssemblyExceptionInfo : public MachineFunctionPass { + // Mapping of basic blocks to the innermost exception they occur in + DenseMap BBMap; + std::vector TopLevelExceptions; + WebAssemblyExceptionInfo(const WebAssemblyExceptionInfo &) = delete; + WebAssemblyExceptionInfo & + operator=(const WebAssemblyExceptionInfo &) = delete; + + void discoverAndMapException(WebAssemblyException *WE, + const MachineDominatorTree &MDT); + WebAssemblyException *getOutermostException(MachineBasicBlock *MBB) const; + +public: + static char ID; + WebAssemblyExceptionInfo() : MachineFunctionPass(ID) { + initializeWebAssemblyExceptionInfoPass(*PassRegistry::getPassRegistry()); + } + ~WebAssemblyExceptionInfo() { releaseMemory(); } + + bool runOnMachineFunction(MachineFunction &) override; + void releaseMemory() override; + void recalculate(MachineDominatorTree &MDT); + void getAnalysisUsage(AnalysisUsage &AU) const override; + + bool empty() const { return TopLevelExceptions.empty(); } + + // Return the innermost exception that MBB lives in. If the block is not in an + // exception, null is returned. + WebAssemblyException *getExceptionFor(const MachineBasicBlock *MBB) const { + return BBMap.lookup(MBB); + } + + void changeExceptionFor(MachineBasicBlock *MBB, WebAssemblyException *WE) { + if (!WE) { + BBMap.erase(MBB); + return; + } + BBMap[MBB] = WE; + } + + void addTopLevelException(WebAssemblyException *WE) { + assert(!WE->getParentException() && "Not a top level exception!"); + TopLevelExceptions.push_back(WE); + } + + void print(raw_ostream &OS, const Module *M = nullptr) const override; +}; + +} // end namespace llvm + +#endif Index: lib/Target/WebAssembly/WebAssemblyExceptionInfo.cpp =================================================================== --- /dev/null +++ lib/Target/WebAssembly/WebAssemblyExceptionInfo.cpp @@ -0,0 +1,213 @@ +//===--- WebAssemblyExceptionInfo.cpp - Exception Infomation --------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief This file implements WebAssemblyException information analysis. +/// +/// +/// WebAssembly instructions for exception handling are structured as follows: +/// try +/// instructions* +/// catch ----| +/// instructions* | -> This part consists of WebAssemblyException +/// end ----| +// +/// A WebAssemblyException object contains BBs that belong to a 'catch' part of +/// the try-catch-end structure to be created later. 'try' and 'end' markers +/// are not present at this stage and will be generated in CFGStackify pass. +/// Because CFGSort requires all the BBs within a catch part to be sorted +/// together as it does for loops, this pass calculates the nesting structure of +/// catch part of exceptions in a function. +/// +/// An exception catch part begins with an EH pad and ends with a block when one +/// of these conditions is met: +/// 1. a block that ends with a 'catchret' instruction +/// 2. a block that ends with a 'cleanupret' instruction +/// 3. a block that ends with a 'unreachable' instruction +/// 4. a block that is not dominated by the EH pad +/// +//===----------------------------------------------------------------------===// + +#include "WebAssemblyExceptionInfo.h" +#include "MCTargetDesc/WebAssemblyMCTargetDesc.h" +#include "llvm/ADT/PostOrderIterator.h" +#include "llvm/CodeGen/MachineDominators.h" + +using namespace llvm; + +#define DEBUG_TYPE "wasm-exception-info" + +char WebAssemblyExceptionInfo::ID = 0; + +INITIALIZE_PASS_BEGIN(WebAssemblyExceptionInfo, DEBUG_TYPE, + "WebAssembly Exception Information", true, true) +INITIALIZE_PASS_DEPENDENCY(MachineDominatorTree) +INITIALIZE_PASS_END(WebAssemblyExceptionInfo, DEBUG_TYPE, + "WebAssembly Exception Information", true, true) + +bool WebAssemblyExceptionInfo::runOnMachineFunction(MachineFunction &F) { + releaseMemory(); + auto &MDT = getAnalysis(); + recalculate(MDT); + return false; +} + +void WebAssemblyExceptionInfo::recalculate(MachineDominatorTree &MDT) { + // Postorder traversal of the dominator tree. + SmallVector Exceptions; + for (auto DomNode : post_order(&MDT)) { + MachineBasicBlock *EHPad = DomNode->getBlock(); + if (!EHPad->isEHPad()) + continue; + auto *WE = new WebAssemblyException(EHPad); + discoverAndMapException(WE, MDT); + Exceptions.push_back(WE); + } + + // Add BBs to exceptions + for (auto DomNode : post_order(&MDT)) { + MachineBasicBlock *MBB = DomNode->getBlock(); + WebAssemblyException *WE = getExceptionFor(MBB); + for (; WE; WE = WE->getParentException()) + WE->addBlock(MBB); + } + + // Add subexceptions to exceptions + for (auto *WE : Exceptions) { + if (WE->getParentException()) + WE->getParentException()->getSubExceptions().push_back(WE); + else + addTopLevelException(WE); + } + + // For convenience, Blocks and SubExceptions are inserted in postorder. + // Reverse the lists. + for (auto *WE : Exceptions) { + WE->reverseBlock(); + std::reverse(WE->getSubExceptions().begin(), WE->getSubExceptions().end()); + } +} + +void WebAssemblyExceptionInfo::releaseMemory() { + BBMap.clear(); + for (auto *WE : TopLevelExceptions) + delete WE; + TopLevelExceptions.clear(); +} + +void WebAssemblyExceptionInfo::getAnalysisUsage(AnalysisUsage &AU) const { + AU.setPreservesAll(); + AU.addRequired(); + MachineFunctionPass::getAnalysisUsage(AU); +} + +void WebAssemblyExceptionInfo::discoverAndMapException( + WebAssemblyException *WE, const MachineDominatorTree &MDT) { + unsigned NumBlocks = 0; + unsigned NumSubExceptions = 0; + + // Map blocks that belong to a catchpad / cleanuppad + MachineBasicBlock *EHPad = WE->getEHPad(); + SmallVector WL; + WL.push_back(EHPad); + while (!WL.empty()) { + MachineBasicBlock *MBB = WL.pop_back_val(); + + // Find its outermost discovered exception. If this is a discovered block, + // check if it is already discovered to be a subexception of this exception. + WebAssemblyException *SubE = getOutermostException(MBB); + if (SubE) { + if (SubE != WE) { + // Discover a subexception of this exception. + SubE->setParentException(WE); + ++NumSubExceptions; + NumBlocks += SubE->getBlocksVector().capacity(); + // All blocks that belong to this subexception have been already + // discovered. Skip all of them. + for (auto *Frontier : SubE->getFrontiers()) + if (MDT.dominates(EHPad, Frontier)) + WL.push_back(Frontier); + } + continue; + } + + // This is an undiscovered block. Map it to the current exception. + changeExceptionFor(MBB, WE); + ++NumBlocks; + + // catchret, cleanupret, and unreachable instructions mark the end border of + // an exception. + if (MBB->getFirstTerminator() != MBB->end()) { + switch (MBB->getFirstTerminator()->getOpcode()) { + case WebAssembly::CATCHRET: + case WebAssembly::CLEANUPRET: + case WebAssembly::UNREACHABLE: + for (auto *Succ : MBB->successors()) + WE->addFrontier(Succ); + continue; + } + } + + // Add successors dominated by the current BB to the worklist. + for (auto *Succ : MBB->successors()) + if (MDT.dominates(EHPad, Succ)) + WL.push_back(Succ); + else + WE->addFrontier(Succ); + } + + WE->getSubExceptions().reserve(NumSubExceptions); + WE->reserveBlocks(NumBlocks); +} + +WebAssemblyException * +WebAssemblyExceptionInfo::getOutermostException(MachineBasicBlock *MBB) const { + WebAssemblyException *WE = getExceptionFor(MBB); + if (WE) { + while (WebAssemblyException *Parent = WE->getParentException()) + WE = Parent; + } + return WE; +} + +void WebAssemblyException::print(raw_ostream &OS, unsigned Depth) const { + OS.indent(Depth * 2) << "Exception at depth " << getExceptionDepth() + << " containing: "; + + for (unsigned I = 0; I < getBlocks().size(); ++I) { + MachineBasicBlock *MBB = getBlocks()[I]; + if (I) + OS << ", "; + OS << "%bb." << MBB->getNumber(); + if (const auto *BB = MBB->getBasicBlock()) + if (BB->hasName()) + OS << "." << BB->getName(); + + if (getEHPad() == MBB) + OS << " (landing-pad)"; + } + OS << "\n"; + + for (auto &SubE : SubExceptions) + SubE->print(OS, Depth + 2); +} + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) +LLVM_DUMP_METHOD void WebAssemblyException::dump() const { print(dbgs()); } +#endif + +raw_ostream &operator<<(raw_ostream &OS, const WebAssemblyException &WE) { + WE.print(OS); + return OS; +} + +void WebAssemblyExceptionInfo::print(raw_ostream &OS, const Module *) const { + for (auto *WE : TopLevelExceptions) + WE->print(OS); +} Index: lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp =================================================================== --- lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp +++ lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp @@ -65,6 +65,7 @@ initializeWebAssemblyRegColoringPass(PR); initializeWebAssemblyExplicitLocalsPass(PR); initializeWebAssemblyFixIrreducibleControlFlowPass(PR); + initializeWebAssemblyExceptionInfoPass(PR); initializeWebAssemblyCFGSortPass(PR); initializeWebAssemblyExceptionPreparePass(PR); initializeWebAssemblyCFGStackifyPass(PR); Index: unittests/Target/WebAssembly/CMakeLists.txt =================================================================== --- /dev/null +++ unittests/Target/WebAssembly/CMakeLists.txt @@ -0,0 +1,18 @@ +include_directories( + ${CMAKE_SOURCE_DIR}/lib/Target/WebAssembly + ${CMAKE_BINARY_DIR}/lib/Target/WebAssembly + ) + +set(LLVM_LINK_COMPONENTS + CodeGen + Core + MC + MIRParser + WebAssemblyCodeGen + WebAssemblyDesc + WebAssemblyInfo + ) + +add_llvm_unittest(WebAssemblyTests + WebAssemblyExceptionInfoTest.cpp + ) Index: unittests/Target/WebAssembly/WebAssemblyExceptionInfoTest.cpp =================================================================== --- /dev/null +++ unittests/Target/WebAssembly/WebAssemblyExceptionInfoTest.cpp @@ -0,0 +1,405 @@ +//=== WebAssemblyExceptionInfoTest.cpp - WebAssebmlyExceptionInfo unit tests =// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "WebAssemblyExceptionInfo.h" +#include "llvm/CodeGen/MIRParser/MIRParser.h" +#include "llvm/CodeGen/MachineDominators.h" +#include "llvm/CodeGen/MachineModuleInfo.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/TargetSelect.h" +#include "llvm/Target/TargetMachine.h" +#include "gtest/gtest.h" + +using namespace llvm; + +namespace { + +std::unique_ptr createTargetMachine() { + auto TT(Triple::normalize("wasm32-unknown-unknown")); + std::string CPU(""); + std::string FS(""); + + LLVMInitializeWebAssemblyTargetInfo(); + LLVMInitializeWebAssemblyTarget(); + LLVMInitializeWebAssemblyTargetMC(); + + std::string Error; + const Target *TheTarget = TargetRegistry::lookupTarget(TT, Error); + assert(TheTarget); + + return std::unique_ptr(TheTarget->createTargetMachine( + TT, CPU, FS, TargetOptions(), None, None, CodeGenOpt::Default)); +} + +std::unique_ptr parseMIR(LLVMContext &Context, + std::unique_ptr &MIR, + const TargetMachine &TM, StringRef MIRCode, + const char *FuncName, MachineModuleInfo &MMI) { + SMDiagnostic Diagnostic; + std::unique_ptr MBuffer = MemoryBuffer::getMemBuffer(MIRCode); + MIR = createMIRParser(std::move(MBuffer), Context); + if (!MIR) + return nullptr; + + std::unique_ptr M = MIR->parseIRModule(); + if (!M) + return nullptr; + + M->setDataLayout(TM.createDataLayout()); + + if (MIR->parseMachineFunctions(*M, MMI)) + return nullptr; + + return M; +} + +} // namespace + +TEST(WebAssemblyExceptionInfoTest, TEST0) { + std::unique_ptr TM = createTargetMachine(); + ASSERT_TRUE(TM); + + StringRef MIRString = R"MIR( +--- | + target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" + target triple = "wasm32-unknown-unknown" + + declare i32 @__gxx_wasm_personality_v0(...) + + define hidden void @test0() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { + unreachable + } + +... +--- +name: test0 +liveins: + - { reg: '$arguments' } + - { reg: '$value_stack' } +body: | + bb.0: + successors: %bb.1, %bb.2 + liveins: $arguments, $value_stack + BR %bb.1, implicit-def dead $arguments + + bb.1: + ; predecessors: %bb.0 + successors: %bb.7 + liveins: $value_stack + BR %bb.7, implicit-def $arguments + + bb.2 (landing-pad): + ; predecessors: %bb.0 + successors: %bb.3, %bb.9 + liveins: $value_stack + CLEANUPRET implicit-def dead $arguments + + bb.3 (landing-pad): + ; predecessors: %bb.2 + successors: %bb.4, %bb.6 + liveins: $value_stack + BR_IF %bb.4, %58:i32, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack + BR %bb.6, implicit-def $arguments + + bb.4: + ; predecessors: %bb.3 + successors: %bb.5, %bb.8 + liveins: $value_stack + BR %bb.5, implicit-def dead $arguments + + bb.5: + ; predecessors: %bb.4 + successors: %bb.7 + liveins: $value_stack + CATCHRET %bb.7, %bb.0, implicit-def dead $arguments + + bb.6: + ; predecessors: %bb.3 + successors: %bb.10, %bb.9 + liveins: $value_stack + BR %bb.10, implicit-def dead $arguments + + bb.7: + ; predecessors: %bb.5, %bb.1 + liveins: $value_stack + RETURN_VOID implicit-def $arguments + + bb.8 (landing-pad): + ; predecessors: %bb.4 + successors: %bb.9 + liveins: $value_stack + CLEANUPRET implicit-def dead $arguments + + bb.9 (landing-pad): + ; predecessors: %bb.2, %bb.6, %bb.8 + liveins: $value_stack + CLEANUPRET implicit-def dead $arguments + + bb.10: + ; predecessors: %bb.6 + liveins: $value_stack + UNREACHABLE implicit-def $arguments +)MIR"; + + LLVMContext Context; + std::unique_ptr MIR; + MachineModuleInfo MMI(TM.get()); + std::unique_ptr M = + parseMIR(Context, MIR, *TM, MIRString, "test0", MMI); + ASSERT_TRUE(M); + + Function *F = M->getFunction("test0"); + auto *MF = MMI.getMachineFunction(*F); + ASSERT_TRUE(MF); + + WebAssemblyExceptionInfo WEI; + MachineDominatorTree MDT; + MDT.runOnMachineFunction(*MF); + WEI.recalculate(MDT); + + // Exception info structure: + // |- bb3 (ehpad), bb4, bb6, bb8, bb10 + // |- bb8 (ehpad) + // |- bb2 (ehpad) + // |- bb9 (ehpad) + + auto *MBB3 = MF->getBlockNumbered(3); + auto *WE0 = WEI.getExceptionFor(MBB3); + ASSERT_TRUE(WE0); + EXPECT_EQ(WE0->getEHPad(), MBB3); + EXPECT_EQ(WE0->getParentException(), nullptr); + EXPECT_EQ(WE0->getExceptionDepth(), (unsigned) 1); + + auto *MBB4 = MF->getBlockNumbered(4); + WE0 = WEI.getExceptionFor(MBB4); + ASSERT_TRUE(WE0); + EXPECT_EQ(WE0->getEHPad(), MBB3); + + auto *MBB5 = MF->getBlockNumbered(5); + WE0 = WEI.getExceptionFor(MBB5); + ASSERT_TRUE(WE0); + EXPECT_EQ(WE0->getEHPad(), MBB3); + + auto *MBB6 = MF->getBlockNumbered(6); + WE0 = WEI.getExceptionFor(MBB6); + ASSERT_TRUE(WE0); + EXPECT_EQ(WE0->getEHPad(), MBB3); + + auto *MBB10 = MF->getBlockNumbered(10); + WE0 = WEI.getExceptionFor(MBB10); + ASSERT_TRUE(WE0); + EXPECT_EQ(WE0->getEHPad(), MBB3); + + auto *MBB8 = MF->getBlockNumbered(8); + auto *WE0_0 = WEI.getExceptionFor(MBB8); + ASSERT_TRUE(WE0_0); + EXPECT_EQ(WE0_0->getEHPad(), MBB8); + EXPECT_EQ(WE0_0->getParentException(), WE0); + EXPECT_EQ(WE0_0->getExceptionDepth(), (unsigned) 2); + + auto *MBB2 = MF->getBlockNumbered(2); + auto *WE1 = WEI.getExceptionFor(MBB2); + ASSERT_TRUE(WE1); + EXPECT_EQ(WE1->getEHPad(), MBB2); + EXPECT_EQ(WE1->getParentException(), nullptr); + EXPECT_EQ(WE1->getExceptionDepth(), (unsigned) 1); + + auto *MBB9 = MF->getBlockNumbered(3); + auto *WE2 = WEI.getExceptionFor(MBB9); + ASSERT_TRUE(WE2); + EXPECT_EQ(WE2->getEHPad(), MBB9); + EXPECT_EQ(WE2->getParentException(), nullptr); + EXPECT_EQ(WE2->getExceptionDepth(), (unsigned) 1); +} + +TEST(WebAssemblyExceptionInfoTest, TEST1) { + std::unique_ptr TM = createTargetMachine(); + ASSERT_TRUE(TM); + + StringRef MIRString = R"MIR( +--- | + target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" + target triple = "wasm32-unknown-unknown" + + declare i32 @__gxx_wasm_personality_v0(...) + + define hidden void @test1() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { + unreachable + } + +... +--- +name: test1 +liveins: + - { reg: '$arguments' } + - { reg: '$value_stack' } +body: | + bb.0: + successors: %bb.9, %bb.1 + liveins: $arguments, $value_stack + BR %bb.9, implicit-def dead $arguments + + bb.1 (landing-pad): + ; predecessors: %bb.0 + successors: %bb.2, %bb.8 + liveins: $value_stack + %52:i32 = CATCH_I32 0, implicit-def dead $arguments + BR_IF %bb.2, %32:i32, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack + BR %bb.8, implicit-def $arguments + + bb.2: + ; predecessors: %bb.1 + successors: %bb.7, %bb.3, %bb.11 + liveins: $value_stack + BR %bb.7, implicit-def dead $arguments + + bb.3 (landing-pad): + ; predecessors: %bb.2 + successors: %bb.4, %bb.6 + liveins: $value_stack + BR_IF %bb.4, %43:i32, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack + BR %bb.6, implicit-def $arguments + + bb.4: + ; predecessors: %bb.3 + successors: %bb.5, %bb.10 + liveins: $value_stack + BR %bb.5, implicit-def dead $arguments + + bb.5: + ; predecessors: %bb.4 + successors: %bb.7(0x80000000); %bb.7(200.00%) + liveins: $value_stack + CATCHRET %bb.7, %bb.1, implicit-def dead $arguments + + bb.6: + ; predecessors: %bb.3 + successors: %bb.12, %bb.11 + liveins: $value_stack + BR %bb.12, implicit-def dead $arguments + + bb.7: + ; predecessors: %bb.2, %bb.5 + successors: %bb.9(0x80000000); %bb.9(200.00%) + liveins: $value_stack + CATCHRET %bb.9, %bb.0, implicit-def dead $arguments + + bb.8: + ; predecessors: %bb.1 + liveins: $value_stack + UNREACHABLE implicit-def $arguments + + bb.9: + ; predecessors: %bb.0, %bb.7 + liveins: $value_stack + RETURN_VOID implicit-def $arguments + + bb.10 (landing-pad): + ; predecessors: %bb.4 + successors: %bb.11 + liveins: $value_stack + CLEANUPRET implicit-def dead $arguments + + bb.11 (landing-pad): + ; predecessors: %bb.2, %bb.6, %bb.10 + liveins: $value_stack + CLEANUPRET implicit-def dead $arguments + + bb.12: + ; predecessors: %bb.6 + liveins: $value_stack + UNREACHABLE implicit-def $arguments +)MIR"; + + LLVMContext Context; + std::unique_ptr MIR; + MachineModuleInfo MMI(TM.get()); + std::unique_ptr M = + parseMIR(Context, MIR, *TM, MIRString, "test1", MMI); + ASSERT_TRUE(M); + + Function *F = M->getFunction("test1"); + auto *MF = MMI.getMachineFunction(*F); + ASSERT_TRUE(MF); + + WebAssemblyExceptionInfo WEI; + MachineDominatorTree MDT; + MDT.runOnMachineFunction(*MF); + WEI.recalculate(MDT); + + // Exception info structure: + // |- bb1 (ehpad), bb2, bb3, bb4, bb5, bb6, bb7, bb8, bb10, bb11, bb12 + // |- bb3 (ehpad), bb4, bb5, bb6, bb10, bb12 + // |- bb10 (ehpad) + // |- bb11 (ehpad) + + auto *MBB1 = MF->getBlockNumbered(1); + auto *WE0 = WEI.getExceptionFor(MBB1); + ASSERT_TRUE(WE0); + EXPECT_EQ(WE0->getEHPad(), MBB1); + EXPECT_EQ(WE0->getParentException(), nullptr); + EXPECT_EQ(WE0->getExceptionDepth(), (unsigned) 1); + + auto *MBB2 = MF->getBlockNumbered(2); + WE0 = WEI.getExceptionFor(MBB2); + ASSERT_TRUE(WE0); + EXPECT_EQ(WE0->getEHPad(), MBB1); + + auto *MBB7 = MF->getBlockNumbered(7); + WE0 = WEI.getExceptionFor(MBB7); + ASSERT_TRUE(WE0); + EXPECT_EQ(WE0->getEHPad(), MBB1); + + auto *MBB8 = MF->getBlockNumbered(8); + WE0 = WEI.getExceptionFor(MBB8); + ASSERT_TRUE(WE0); + EXPECT_EQ(WE0->getEHPad(), MBB1); + + auto *MBB3 = MF->getBlockNumbered(3); + auto *WE0_0 = WEI.getExceptionFor(MBB3); + ASSERT_TRUE(WE0_0); + EXPECT_EQ(WE0_0->getEHPad(), MBB3); + EXPECT_EQ(WE0_0->getParentException(), WE0); + EXPECT_EQ(WE0_0->getExceptionDepth(), (unsigned) 2); + + auto *MBB4 = MF->getBlockNumbered(4); + WE0_0 = WEI.getExceptionFor(MBB4); + ASSERT_TRUE(WE0_0); + EXPECT_EQ(WE0_0->getEHPad(), MBB3); + + auto *MBB5 = MF->getBlockNumbered(5); + WE0_0 = WEI.getExceptionFor(MBB5); + ASSERT_TRUE(WE0_0); + EXPECT_EQ(WE0_0->getEHPad(), MBB3); + + auto *MBB6 = MF->getBlockNumbered(6); + WE0_0 = WEI.getExceptionFor(MBB6); + ASSERT_TRUE(WE0_0); + EXPECT_EQ(WE0_0->getEHPad(), MBB3); + + auto *MBB12 = MF->getBlockNumbered(12); + WE0_0 = WEI.getExceptionFor(MBB12); + ASSERT_TRUE(WE0_0); + EXPECT_EQ(WE0_0->getEHPad(), MBB3); + + auto *MBB10 = MF->getBlockNumbered(10); + auto *WE0_0_0 = WEI.getExceptionFor(MBB10); + ASSERT_TRUE(WE0_0_0); + EXPECT_EQ(WE0_0_0->getEHPad(), MBB10); + EXPECT_EQ(WE0_0_0->getParentException(), WE0_0); + EXPECT_EQ(WE0_0_0->getExceptionDepth(), (unsigned) 3); + + auto *MBB11 = MF->getBlockNumbered(11); + auto *WE0_1 = WEI.getExceptionFor(MBB11); + ASSERT_TRUE(WE0_1); + EXPECT_EQ(WE0_1->getEHPad(), MBB11); + EXPECT_EQ(WE0_1->getParentException(), WE0); + EXPECT_EQ(WE0_1->getExceptionDepth(), (unsigned) 2); +}