Path: blob/main/contrib/llvm-project/llvm/lib/Target/WebAssembly/WebAssemblyExceptionInfo.cpp
35266 views
//===--- WebAssemblyExceptionInfo.cpp - Exception Infomation --------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7///8/// \file9/// \brief This file implements WebAssemblyException information analysis.10///11//===----------------------------------------------------------------------===//1213#include "WebAssemblyExceptionInfo.h"14#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"15#include "WebAssemblyUtilities.h"16#include "llvm/ADT/DepthFirstIterator.h"17#include "llvm/ADT/PostOrderIterator.h"18#include "llvm/CodeGen/MachineDominanceFrontier.h"19#include "llvm/CodeGen/MachineDominators.h"20#include "llvm/CodeGen/WasmEHFuncInfo.h"21#include "llvm/InitializePasses.h"22#include "llvm/IR/Function.h"23#include "llvm/MC/MCAsmInfo.h"24#include "llvm/Target/TargetMachine.h"2526using namespace llvm;2728#define DEBUG_TYPE "wasm-exception-info"2930char WebAssemblyExceptionInfo::ID = 0;3132INITIALIZE_PASS_BEGIN(WebAssemblyExceptionInfo, DEBUG_TYPE,33"WebAssembly Exception Information", true, true)34INITIALIZE_PASS_DEPENDENCY(MachineDominatorTreeWrapperPass)35INITIALIZE_PASS_DEPENDENCY(MachineDominanceFrontier)36INITIALIZE_PASS_END(WebAssemblyExceptionInfo, DEBUG_TYPE,37"WebAssembly Exception Information", true, true)3839bool WebAssemblyExceptionInfo::runOnMachineFunction(MachineFunction &MF) {40LLVM_DEBUG(dbgs() << "********** Exception Info Calculation **********\n"41"********** Function: "42<< MF.getName() << '\n');43releaseMemory();44if (MF.getTarget().getMCAsmInfo()->getExceptionHandlingType() !=45ExceptionHandling::Wasm ||46!MF.getFunction().hasPersonalityFn())47return false;48auto &MDT = getAnalysis<MachineDominatorTreeWrapperPass>().getDomTree();49auto &MDF = getAnalysis<MachineDominanceFrontier>();50recalculate(MF, MDT, MDF);51LLVM_DEBUG(dump());52return false;53}5455// Check if Dst is reachable from Src using BFS. Search only within BBs56// dominated by Header.57static bool isReachableAmongDominated(const MachineBasicBlock *Src,58const MachineBasicBlock *Dst,59const MachineBasicBlock *Header,60const MachineDominatorTree &MDT) {61assert(MDT.dominates(Header, Dst));62SmallVector<const MachineBasicBlock *, 8> WL;63SmallPtrSet<const MachineBasicBlock *, 8> Visited;64WL.push_back(Src);6566while (!WL.empty()) {67const auto *MBB = WL.pop_back_val();68if (MBB == Dst)69return true;70Visited.insert(MBB);71for (auto *Succ : MBB->successors())72if (!Visited.count(Succ) && MDT.dominates(Header, Succ))73WL.push_back(Succ);74}75return false;76}7778void WebAssemblyExceptionInfo::recalculate(79MachineFunction &MF, MachineDominatorTree &MDT,80const MachineDominanceFrontier &MDF) {81// Postorder traversal of the dominator tree.82SmallVector<std::unique_ptr<WebAssemblyException>, 8> Exceptions;83for (auto *DomNode : post_order(&MDT)) {84MachineBasicBlock *EHPad = DomNode->getBlock();85if (!EHPad->isEHPad())86continue;87auto WE = std::make_unique<WebAssemblyException>(EHPad);88discoverAndMapException(WE.get(), MDT, MDF);89Exceptions.push_back(std::move(WE));90}9192// WasmEHFuncInfo contains a map of <catchpad, its next unwind destination>,93// which means, if an exception is not caught by the catchpad, it should end94// up in the next unwind destination stored in this data structure. (It is95// written as catchswitch's 'unwind' destination in ll files.) The below is an96// intuitive example of their relationship in C++ code:97// try {98// try {99// } catch (int) { // catchpad100// ... // this catch (int) { ... } is grouped as an exception101// }102// } catch (...) { // next unwind destination103// }104// (The example is try-catches for illustration purpose, but the unwind105// destination can be also a cleanuppad generated by destructor calls.) So the106// unwind destination is in the outside of the catchpad's exception.107//108// We group exceptions in this analysis simply by including all BBs dominated109// by an EH pad. But in case the EH pad's unwind destination does not have any110// children outside of the exception, that unwind destination ends up also111// being dominated by the EH pad and included in the exception, which is not112// semantically correct, because it unwinds/rethrows into an inner scope.113//114// Here we extract those unwind destinations from their (incorrect) parent115// exception. Note that the unwind destinations may not be an immediate116// children of the parent exception, so we have to traverse the parent chain.117//118// We should traverse BBs in the preorder of the dominator tree, because119// otherwise the result can be incorrect. For example, when there are three120// exceptions A, B, and C and A > B > C (> is subexception relationship here),121// and A's unwind destination is B and B's is C. When we visit B before A, we122// end up extracting C only out of B but not out of A.123const auto *EHInfo = MF.getWasmEHFuncInfo();124assert(EHInfo);125SmallVector<std::pair<WebAssemblyException *, WebAssemblyException *>>126UnwindWEVec;127for (auto *DomNode : depth_first(&MDT)) {128MachineBasicBlock *EHPad = DomNode->getBlock();129if (!EHPad->isEHPad())130continue;131if (!EHInfo->hasUnwindDest(EHPad))132continue;133auto *UnwindDest = EHInfo->getUnwindDest(EHPad);134auto *SrcWE = getExceptionFor(EHPad);135auto *DstWE = getExceptionFor(UnwindDest);136if (SrcWE->contains(DstWE)) {137UnwindWEVec.push_back(std::make_pair(SrcWE, DstWE));138LLVM_DEBUG(dbgs() << "Unwind destination ExceptionInfo fix:\n "139<< DstWE->getEHPad()->getNumber() << "."140<< DstWE->getEHPad()->getName()141<< "'s exception is taken out of "142<< SrcWE->getEHPad()->getNumber() << "."143<< SrcWE->getEHPad()->getName() << "'s exception\n");144DstWE->setParentException(SrcWE->getParentException());145}146}147148// After fixing subexception relationship between unwind destinations above,149// there can still be remaining discrepancies.150//151// For example, suppose Exception A is dominated by EHPad A and Exception B is152// dominated by EHPad B. EHPad A's unwind destination is EHPad B, but because153// EHPad B is dominated by EHPad A, the initial grouping makes Exception B a154// subexception of Exception A, and we fix it by taking Exception B out of155// Exception A above. But there can still be remaining BBs within Exception A156// that are reachable from Exception B. These BBs semantically don't belong157// to Exception A and were not a part of this 'catch' clause or cleanup code158// in the original code, but they just happened to be grouped within Exception159// A because they were dominated by EHPad A. We fix this case by taking those160// BBs out of the incorrect exception and all its subexceptions that it161// belongs to.162//163// 1. First, we take out remaining incorrect subexceptions. This part is164// easier, because we haven't added BBs to exceptions yet, we only need to165// change parent exception pointer.166for (auto *DomNode : depth_first(&MDT)) {167MachineBasicBlock *EHPad = DomNode->getBlock();168if (!EHPad->isEHPad())169continue;170auto *WE = getExceptionFor(EHPad);171172// For each source EHPad -> unwind destination EHPad173for (auto &P : UnwindWEVec) {174auto *SrcWE = P.first;175auto *DstWE = P.second;176// If WE (the current EH pad's exception) is still contained in SrcWE but177// reachable from DstWE that was taken out of SrcWE above, we have to take178// out WE out of SrcWE too.179if (WE != SrcWE && SrcWE->contains(WE) && !DstWE->contains(WE) &&180isReachableAmongDominated(DstWE->getEHPad(), EHPad, SrcWE->getEHPad(),181MDT)) {182LLVM_DEBUG(dbgs() << "Remaining reachable ExceptionInfo fix:\n "183<< WE->getEHPad()->getNumber() << "."184<< WE->getEHPad()->getName()185<< "'s exception is taken out of "186<< SrcWE->getEHPad()->getNumber() << "."187<< SrcWE->getEHPad()->getName() << "'s exception\n");188WE->setParentException(SrcWE->getParentException());189}190}191}192193// Add BBs to exceptions' block set. This is a preparation to take out194// remaining incorect BBs from exceptions, because we need to iterate over BBs195// for each exception.196for (auto *DomNode : post_order(&MDT)) {197MachineBasicBlock *MBB = DomNode->getBlock();198WebAssemblyException *WE = getExceptionFor(MBB);199for (; WE; WE = WE->getParentException())200WE->addToBlocksSet(MBB);201}202203// 2. We take out remaining individual BBs out. Now we have added BBs to each204// exceptions' BlockSet, when we take a BB out of an exception, we need to fix205// those sets too.206for (auto &P : UnwindWEVec) {207auto *SrcWE = P.first;208auto *DstWE = P.second;209210SrcWE->getBlocksSet().remove_if([&](MachineBasicBlock *MBB){211if (MBB->isEHPad()) {212assert(!isReachableAmongDominated(DstWE->getEHPad(), MBB,213SrcWE->getEHPad(), MDT) &&214"We already handled EH pads above");215return false;216}217if (isReachableAmongDominated(DstWE->getEHPad(), MBB, SrcWE->getEHPad(),218MDT)) {219LLVM_DEBUG(dbgs() << "Remainder BB: " << MBB->getNumber() << "."220<< MBB->getName() << " is\n");221WebAssemblyException *InnerWE = getExceptionFor(MBB);222while (InnerWE != SrcWE) {223LLVM_DEBUG(dbgs()224<< " removed from " << InnerWE->getEHPad()->getNumber()225<< "." << InnerWE->getEHPad()->getName()226<< "'s exception\n");227InnerWE->removeFromBlocksSet(MBB);228InnerWE = InnerWE->getParentException();229}230LLVM_DEBUG(dbgs() << " removed from " << SrcWE->getEHPad()->getNumber()231<< "." << SrcWE->getEHPad()->getName()232<< "'s exception\n");233changeExceptionFor(MBB, SrcWE->getParentException());234if (SrcWE->getParentException())235SrcWE->getParentException()->addToBlocksSet(MBB);236return true;237}238return false;239});240}241242// Add BBs to exceptions' block vector243for (auto *DomNode : post_order(&MDT)) {244MachineBasicBlock *MBB = DomNode->getBlock();245WebAssemblyException *WE = getExceptionFor(MBB);246for (; WE; WE = WE->getParentException())247WE->addToBlocksVector(MBB);248}249250SmallVector<WebAssemblyException*, 8> ExceptionPointers;251ExceptionPointers.reserve(Exceptions.size());252253// Add subexceptions to exceptions254for (auto &WE : Exceptions) {255ExceptionPointers.push_back(WE.get());256if (WE->getParentException())257WE->getParentException()->getSubExceptions().push_back(std::move(WE));258else259addTopLevelException(std::move(WE));260}261262// For convenience, Blocks and SubExceptions are inserted in postorder.263// Reverse the lists.264for (auto *WE : ExceptionPointers) {265WE->reverseBlock();266std::reverse(WE->getSubExceptions().begin(), WE->getSubExceptions().end());267}268}269270void WebAssemblyExceptionInfo::releaseMemory() {271BBMap.clear();272TopLevelExceptions.clear();273}274275void WebAssemblyExceptionInfo::getAnalysisUsage(AnalysisUsage &AU) const {276AU.setPreservesAll();277AU.addRequired<MachineDominatorTreeWrapperPass>();278AU.addRequired<MachineDominanceFrontier>();279MachineFunctionPass::getAnalysisUsage(AU);280}281282void WebAssemblyExceptionInfo::discoverAndMapException(283WebAssemblyException *WE, const MachineDominatorTree &MDT,284const MachineDominanceFrontier &MDF) {285unsigned NumBlocks = 0;286unsigned NumSubExceptions = 0;287288// Map blocks that belong to a catchpad / cleanuppad289MachineBasicBlock *EHPad = WE->getEHPad();290SmallVector<MachineBasicBlock *, 8> WL;291WL.push_back(EHPad);292while (!WL.empty()) {293MachineBasicBlock *MBB = WL.pop_back_val();294295// Find its outermost discovered exception. If this is a discovered block,296// check if it is already discovered to be a subexception of this exception.297WebAssemblyException *SubE = getOutermostException(MBB);298if (SubE) {299if (SubE != WE) {300// Discover a subexception of this exception.301SubE->setParentException(WE);302++NumSubExceptions;303NumBlocks += SubE->getBlocksVector().capacity();304// All blocks that belong to this subexception have been already305// discovered. Skip all of them. Add the subexception's landing pad's306// dominance frontier to the worklist.307for (auto &Frontier : MDF.find(SubE->getEHPad())->second)308if (MDT.dominates(EHPad, Frontier))309WL.push_back(Frontier);310}311continue;312}313314// This is an undiscovered block. Map it to the current exception.315changeExceptionFor(MBB, WE);316++NumBlocks;317318// Add successors dominated by the current BB to the worklist.319for (auto *Succ : MBB->successors())320if (MDT.dominates(EHPad, Succ))321WL.push_back(Succ);322}323324WE->getSubExceptions().reserve(NumSubExceptions);325WE->reserveBlocks(NumBlocks);326}327328WebAssemblyException *329WebAssemblyExceptionInfo::getOutermostException(MachineBasicBlock *MBB) const {330WebAssemblyException *WE = getExceptionFor(MBB);331if (WE) {332while (WebAssemblyException *Parent = WE->getParentException())333WE = Parent;334}335return WE;336}337338void WebAssemblyException::print(raw_ostream &OS, unsigned Depth) const {339OS.indent(Depth * 2) << "Exception at depth " << getExceptionDepth()340<< " containing: ";341342for (unsigned I = 0; I < getBlocks().size(); ++I) {343MachineBasicBlock *MBB = getBlocks()[I];344if (I)345OS << ", ";346OS << "%bb." << MBB->getNumber();347if (const auto *BB = MBB->getBasicBlock())348if (BB->hasName())349OS << "." << BB->getName();350351if (getEHPad() == MBB)352OS << " (landing-pad)";353}354OS << "\n";355356for (auto &SubE : SubExceptions)357SubE->print(OS, Depth + 2);358}359360#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)361LLVM_DUMP_METHOD void WebAssemblyException::dump() const { print(dbgs()); }362#endif363364raw_ostream &operator<<(raw_ostream &OS, const WebAssemblyException &WE) {365WE.print(OS);366return OS;367}368369void WebAssemblyExceptionInfo::print(raw_ostream &OS, const Module *) const {370for (auto &WE : TopLevelExceptions)371WE->print(OS);372}373374375