forked from OSchip/llvm-project
371 lines
14 KiB
C++
371 lines
14 KiB
C++
//===--- 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);
|
|
}
|