123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370 |
- //===--- WebAssemblyExceptionInfo.cpp - Exception Infomation --------------===//
- //
- // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
- // See https://llvm.org/LICENSE.txt for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- //
- //===----------------------------------------------------------------------===//
- ///
- /// \file
- /// \brief This file implements WebAssemblyException information analysis.
- ///
- //===----------------------------------------------------------------------===//
- #include "WebAssemblyExceptionInfo.h"
- #include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
- #include "Utils/WebAssemblyUtilities.h"
- #include "llvm/ADT/DepthFirstIterator.h"
- #include "llvm/ADT/PostOrderIterator.h"
- #include "llvm/CodeGen/MachineDominanceFrontier.h"
- #include "llvm/CodeGen/MachineDominators.h"
- #include "llvm/CodeGen/WasmEHFuncInfo.h"
- #include "llvm/InitializePasses.h"
- #include "llvm/MC/MCAsmInfo.h"
- #include "llvm/Target/TargetMachine.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_DEPENDENCY(MachineDominanceFrontier)
- INITIALIZE_PASS_END(WebAssemblyExceptionInfo, DEBUG_TYPE,
- "WebAssembly Exception Information", true, true)
- bool WebAssemblyExceptionInfo::runOnMachineFunction(MachineFunction &MF) {
- LLVM_DEBUG(dbgs() << "********** Exception Info Calculation **********\n"
- "********** Function: "
- << MF.getName() << '\n');
- releaseMemory();
- if (MF.getTarget().getMCAsmInfo()->getExceptionHandlingType() !=
- ExceptionHandling::Wasm ||
- !MF.getFunction().hasPersonalityFn())
- return false;
- auto &MDT = getAnalysis<MachineDominatorTree>();
- auto &MDF = getAnalysis<MachineDominanceFrontier>();
- recalculate(MF, MDT, MDF);
- LLVM_DEBUG(dump());
- return false;
- }
- // Check if Dst is reachable from Src using BFS. Search only within BBs
- // dominated by Header.
- static bool isReachableAmongDominated(const MachineBasicBlock *Src,
- const MachineBasicBlock *Dst,
- const MachineBasicBlock *Header,
- const MachineDominatorTree &MDT) {
- assert(MDT.dominates(Header, Dst));
- SmallVector<const MachineBasicBlock *, 8> WL;
- SmallPtrSet<const MachineBasicBlock *, 8> Visited;
- WL.push_back(Src);
- while (!WL.empty()) {
- const auto *MBB = WL.pop_back_val();
- if (MBB == Dst)
- return true;
- Visited.insert(MBB);
- for (auto *Succ : MBB->successors())
- if (!Visited.count(Succ) && MDT.dominates(Header, Succ))
- WL.push_back(Succ);
- }
- return false;
- }
- void WebAssemblyExceptionInfo::recalculate(
- MachineFunction &MF, MachineDominatorTree &MDT,
- const MachineDominanceFrontier &MDF) {
- // Postorder traversal of the dominator tree.
- SmallVector<std::unique_ptr<WebAssemblyException>, 8> Exceptions;
- for (auto *DomNode : post_order(&MDT)) {
- MachineBasicBlock *EHPad = DomNode->getBlock();
- if (!EHPad->isEHPad())
- continue;
- auto WE = std::make_unique<WebAssemblyException>(EHPad);
- discoverAndMapException(WE.get(), MDT, MDF);
- Exceptions.push_back(std::move(WE));
- }
- // WasmEHFuncInfo contains a map of <catchpad, its next unwind destination>,
- // 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.
- //
- // We should traverse BBs in the preorder of the dominator tree, because
- // otherwise the result can be incorrect. For example, when there are three
- // exceptions A, B, and C and A > B > C (> is subexception relationship here),
- // and A's unwind destination is B and B's is C. When we visit B before A, we
- // end up extracting C only out of B but not out of A.
- const auto *EHInfo = MF.getWasmEHFuncInfo();
- SmallVector<std::pair<WebAssemblyException *, WebAssemblyException *>>
- UnwindWEVec;
- for (auto *DomNode : depth_first(&MDT)) {
- MachineBasicBlock *EHPad = DomNode->getBlock();
- if (!EHPad->isEHPad())
- continue;
- if (!EHInfo->hasUnwindDest(EHPad))
- continue;
- auto *UnwindDest = EHInfo->getUnwindDest(EHPad);
- auto *SrcWE = getExceptionFor(EHPad);
- auto *DstWE = getExceptionFor(UnwindDest);
- if (SrcWE->contains(DstWE)) {
- UnwindWEVec.push_back(std::make_pair(SrcWE, DstWE));
- LLVM_DEBUG(dbgs() << "Unwind destination ExceptionInfo fix:\n "
- << DstWE->getEHPad()->getNumber() << "."
- << DstWE->getEHPad()->getName()
- << "'s exception is taken out of "
- << SrcWE->getEHPad()->getNumber() << "."
- << SrcWE->getEHPad()->getName() << "'s exception\n");
- DstWE->setParentException(SrcWE->getParentException());
- }
- }
- // After fixing subexception relationship between unwind destinations above,
- // there can still be remaining discrepancies.
- //
- // For example, suppose Exception A is dominated by EHPad A and Exception B is
- // dominated by EHPad B. EHPad A's unwind destination is EHPad B, but because
- // EHPad B is dominated by EHPad A, the initial grouping makes Exception B a
- // subexception of Exception A, and we fix it by taking Exception B out of
- // Exception A above. But there can still be remaining BBs within Exception A
- // that are reachable from Exception B. These BBs semantically don't belong
- // to Exception A and were not a part of this 'catch' clause or cleanup code
- // in the original code, but they just happened to be grouped within Exception
- // A because they were dominated by EHPad A. We fix this case by taking those
- // BBs out of the incorrect exception and all its subexceptions that it
- // belongs to.
- //
- // 1. First, we take out remaining incorrect subexceptions. This part is
- // easier, because we haven't added BBs to exceptions yet, we only need to
- // change parent exception pointer.
- for (auto *DomNode : depth_first(&MDT)) {
- MachineBasicBlock *EHPad = DomNode->getBlock();
- if (!EHPad->isEHPad())
- continue;
- auto *WE = getExceptionFor(EHPad);
- // For each source EHPad -> unwind destination EHPad
- for (auto &P : UnwindWEVec) {
- auto *SrcWE = P.first;
- auto *DstWE = P.second;
- // If WE (the current EH pad's exception) is still contained in SrcWE but
- // reachable from DstWE that was taken out of SrcWE above, we have to take
- // out WE out of SrcWE too.
- if (WE != SrcWE && SrcWE->contains(WE) && !DstWE->contains(WE) &&
- isReachableAmongDominated(DstWE->getEHPad(), EHPad, SrcWE->getEHPad(),
- MDT)) {
- LLVM_DEBUG(dbgs() << "Remaining reachable ExceptionInfo fix:\n "
- << WE->getEHPad()->getNumber() << "."
- << WE->getEHPad()->getName()
- << "'s exception is taken out of "
- << SrcWE->getEHPad()->getNumber() << "."
- << SrcWE->getEHPad()->getName() << "'s exception\n");
- WE->setParentException(SrcWE->getParentException());
- }
- }
- }
- // Add BBs to exceptions' block set. This is a preparation to take out
- // remaining incorect BBs from exceptions, because we need to iterate over BBs
- // for each exception.
- for (auto *DomNode : post_order(&MDT)) {
- MachineBasicBlock *MBB = DomNode->getBlock();
- WebAssemblyException *WE = getExceptionFor(MBB);
- for (; WE; WE = WE->getParentException())
- WE->addToBlocksSet(MBB);
- }
- // 2. We take out remaining individual BBs out. Now we have added BBs to each
- // exceptions' BlockSet, when we take a BB out of an exception, we need to fix
- // those sets too.
- for (auto &P : UnwindWEVec) {
- auto *SrcWE = P.first;
- auto *DstWE = P.second;
- for (auto *MBB : SrcWE->getBlocksSet()) {
- if (MBB->isEHPad()) {
- assert(!isReachableAmongDominated(DstWE->getEHPad(), MBB,
- SrcWE->getEHPad(), MDT) &&
- "We already handled EH pads above");
- continue;
- }
- if (isReachableAmongDominated(DstWE->getEHPad(), MBB, SrcWE->getEHPad(),
- MDT)) {
- LLVM_DEBUG(dbgs() << "Remainder BB: " << MBB->getNumber() << "."
- << MBB->getName() << " is\n");
- WebAssemblyException *InnerWE = getExceptionFor(MBB);
- while (InnerWE != SrcWE) {
- LLVM_DEBUG(dbgs()
- << " removed from " << InnerWE->getEHPad()->getNumber()
- << "." << InnerWE->getEHPad()->getName()
- << "'s exception\n");
- InnerWE->removeFromBlocksSet(MBB);
- InnerWE = InnerWE->getParentException();
- }
- SrcWE->removeFromBlocksSet(MBB);
- LLVM_DEBUG(dbgs() << " removed from " << SrcWE->getEHPad()->getNumber()
- << "." << SrcWE->getEHPad()->getName()
- << "'s exception\n");
- changeExceptionFor(MBB, SrcWE->getParentException());
- if (SrcWE->getParentException())
- SrcWE->getParentException()->addToBlocksSet(MBB);
- }
- }
- }
- // Add BBs to exceptions' block vector
- for (auto *DomNode : post_order(&MDT)) {
- MachineBasicBlock *MBB = DomNode->getBlock();
- WebAssemblyException *WE = getExceptionFor(MBB);
- for (; WE; WE = WE->getParentException())
- WE->addToBlocksVector(MBB);
- }
- SmallVector<WebAssemblyException*, 8> ExceptionPointers;
- ExceptionPointers.reserve(Exceptions.size());
- // Add subexceptions to exceptions
- for (auto &WE : Exceptions) {
- ExceptionPointers.push_back(WE.get());
- if (WE->getParentException())
- WE->getParentException()->getSubExceptions().push_back(std::move(WE));
- else
- addTopLevelException(std::move(WE));
- }
- // For convenience, Blocks and SubExceptions are inserted in postorder.
- // Reverse the lists.
- for (auto *WE : ExceptionPointers) {
- WE->reverseBlock();
- std::reverse(WE->getSubExceptions().begin(), WE->getSubExceptions().end());
- }
- }
- void WebAssemblyExceptionInfo::releaseMemory() {
- BBMap.clear();
- TopLevelExceptions.clear();
- }
- void WebAssemblyExceptionInfo::getAnalysisUsage(AnalysisUsage &AU) const {
- AU.setPreservesAll();
- AU.addRequired<MachineDominatorTree>();
- AU.addRequired<MachineDominanceFrontier>();
- MachineFunctionPass::getAnalysisUsage(AU);
- }
- void WebAssemblyExceptionInfo::discoverAndMapException(
- WebAssemblyException *WE, const MachineDominatorTree &MDT,
- const MachineDominanceFrontier &MDF) {
- unsigned NumBlocks = 0;
- unsigned NumSubExceptions = 0;
- // Map blocks that belong to a catchpad / cleanuppad
- MachineBasicBlock *EHPad = WE->getEHPad();
- SmallVector<MachineBasicBlock *, 8> 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. Add the subexception's landing pad's
- // dominance frontier to the worklist.
- for (auto &Frontier : MDF.find(SubE->getEHPad())->second)
- 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;
- // Add successors dominated by the current BB to the worklist.
- for (auto *Succ : MBB->successors())
- if (MDT.dominates(EHPad, Succ))
- WL.push_back(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);
- }
|