LLVM 20.0.0git
WebAssemblyExceptionInfo.cpp
Go to the documentation of this file.
1//===--- WebAssemblyExceptionInfo.cpp - Exception Infomation --------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8///
9/// \file
10/// \brief This file implements WebAssemblyException information analysis.
11///
12//===----------------------------------------------------------------------===//
13
23#include "llvm/IR/Function.h"
24#include "llvm/MC/MCAsmInfo.h"
26
27using namespace llvm;
28
29#define DEBUG_TYPE "wasm-exception-info"
30
32
34 "WebAssembly Exception Information", true, true)
38 "WebAssembly Exception Information", true, true)
39
40bool WebAssemblyExceptionInfo::runOnMachineFunction(MachineFunction &MF) {
41 LLVM_DEBUG(dbgs() << "********** Exception Info Calculation **********\n"
42 "********** Function: "
43 << MF.getName() << '\n');
44 releaseMemory();
45 if (MF.getTarget().getMCAsmInfo()->getExceptionHandlingType() !=
46 ExceptionHandling::Wasm ||
47 !MF.getFunction().hasPersonalityFn())
48 return false;
49 auto &MDT = getAnalysis<MachineDominatorTreeWrapperPass>().getDomTree();
50 auto &MDF = getAnalysis<MachineDominanceFrontier>();
51 recalculate(MF, MDT, MDF);
53 return false;
54}
55
56// Check if Dst is reachable from Src using BFS. Search only within BBs
57// dominated by Header.
59 const MachineBasicBlock *Dst,
60 const MachineBasicBlock *Header,
61 const MachineDominatorTree &MDT) {
62 assert(MDT.dominates(Header, Dst));
65 WL.push_back(Src);
66
67 while (!WL.empty()) {
68 const auto *MBB = WL.pop_back_val();
69 if (MBB == Dst)
70 return true;
71 Visited.insert(MBB);
72 for (auto *Succ : MBB->successors())
73 if (!Visited.count(Succ) && MDT.dominates(Header, Succ))
74 WL.push_back(Succ);
75 }
76 return false;
77}
78
81 const MachineDominanceFrontier &MDF) {
82 // Postorder traversal of the dominator tree.
84 for (auto *DomNode : post_order(&MDT)) {
85 MachineBasicBlock *EHPad = DomNode->getBlock();
86 if (!EHPad->isEHPad())
87 continue;
88 auto WE = std::make_unique<WebAssemblyException>(EHPad);
89 discoverAndMapException(WE.get(), MDT, MDF);
90 Exceptions.push_back(std::move(WE));
91 }
92
93 // WasmEHFuncInfo contains a map of <catchpad, its next unwind destination>,
94 // which means, if an exception is not caught by the catchpad, it should end
95 // up in the next unwind destination stored in this data structure. (It is
96 // written as catchswitch's 'unwind' destination in ll files.) The below is an
97 // intuitive example of their relationship in C++ code:
98 // try {
99 // try {
100 // } catch (int) { // catchpad
101 // ... // this catch (int) { ... } is grouped as an exception
102 // }
103 // } catch (...) { // next unwind destination
104 // }
105 // (The example is try-catches for illustration purpose, but the unwind
106 // destination can be also a cleanuppad generated by destructor calls.) So the
107 // unwind destination is in the outside of the catchpad's exception.
108 //
109 // We group exceptions in this analysis simply by including all BBs dominated
110 // by an EH pad. But in case the EH pad's unwind destination does not have any
111 // children outside of the exception, that unwind destination ends up also
112 // being dominated by the EH pad and included in the exception, which is not
113 // semantically correct, because it unwinds/rethrows into an inner scope.
114 //
115 // Here we extract those unwind destinations from their (incorrect) parent
116 // exception. Note that the unwind destinations may not be an immediate
117 // children of the parent exception, so we have to traverse the parent chain.
118 //
119 // We should traverse BBs in the preorder of the dominator tree, because
120 // otherwise the result can be incorrect. For example, when there are three
121 // exceptions A, B, and C and A > B > C (> is subexception relationship here),
122 // and A's unwind destination is B and B's is C. When we visit B before A, we
123 // end up extracting C only out of B but not out of A.
124 const auto *EHInfo = MF.getWasmEHFuncInfo();
125 assert(EHInfo);
127 UnwindWEVec;
128 for (auto *DomNode : depth_first(&MDT)) {
129 MachineBasicBlock *EHPad = DomNode->getBlock();
130 if (!EHPad->isEHPad())
131 continue;
132 if (!EHInfo->hasUnwindDest(EHPad))
133 continue;
134 auto *UnwindDest = EHInfo->getUnwindDest(EHPad);
135 auto *SrcWE = getExceptionFor(EHPad);
136 auto *DstWE = getExceptionFor(UnwindDest);
137 if (SrcWE->contains(DstWE)) {
138 UnwindWEVec.push_back(std::make_pair(SrcWE, DstWE));
139 LLVM_DEBUG(dbgs() << "Unwind destination ExceptionInfo fix:\n "
140 << DstWE->getEHPad()->getNumber() << "."
141 << DstWE->getEHPad()->getName()
142 << "'s exception is taken out of "
143 << SrcWE->getEHPad()->getNumber() << "."
144 << SrcWE->getEHPad()->getName() << "'s exception\n");
145 DstWE->setParentException(SrcWE->getParentException());
146 }
147 }
148
149 // After fixing subexception relationship between unwind destinations above,
150 // there can still be remaining discrepancies.
151 //
152 // For example, suppose Exception A is dominated by EHPad A and Exception B is
153 // dominated by EHPad B. EHPad A's unwind destination is EHPad B, but because
154 // EHPad B is dominated by EHPad A, the initial grouping makes Exception B a
155 // subexception of Exception A, and we fix it by taking Exception B out of
156 // Exception A above. But there can still be remaining BBs within Exception A
157 // that are reachable from Exception B. These BBs semantically don't belong
158 // to Exception A and were not a part of this 'catch' clause or cleanup code
159 // in the original code, but they just happened to be grouped within Exception
160 // A because they were dominated by EHPad A. We fix this case by taking those
161 // BBs out of the incorrect exception and all its subexceptions that it
162 // belongs to.
163 //
164 // 1. First, we take out remaining incorrect subexceptions. This part is
165 // easier, because we haven't added BBs to exceptions yet, we only need to
166 // change parent exception pointer.
167 for (auto *DomNode : depth_first(&MDT)) {
168 MachineBasicBlock *EHPad = DomNode->getBlock();
169 if (!EHPad->isEHPad())
170 continue;
171 auto *WE = getExceptionFor(EHPad);
172
173 // For each source EHPad -> unwind destination EHPad
174 for (auto &P : UnwindWEVec) {
175 auto *SrcWE = P.first;
176 auto *DstWE = P.second;
177 // If WE (the current EH pad's exception) is still contained in SrcWE but
178 // reachable from DstWE that was taken out of SrcWE above, we have to take
179 // out WE out of SrcWE too.
180 if (WE != SrcWE && SrcWE->contains(WE) && !DstWE->contains(WE) &&
181 isReachableAmongDominated(DstWE->getEHPad(), EHPad, SrcWE->getEHPad(),
182 MDT)) {
183 LLVM_DEBUG(dbgs() << "Remaining reachable ExceptionInfo fix:\n "
184 << WE->getEHPad()->getNumber() << "."
185 << WE->getEHPad()->getName()
186 << "'s exception is taken out of "
187 << SrcWE->getEHPad()->getNumber() << "."
188 << SrcWE->getEHPad()->getName() << "'s exception\n");
189 WE->setParentException(SrcWE->getParentException());
190 }
191 }
192 }
193
194 // Add BBs to exceptions' block set. This is a preparation to take out
195 // remaining incorect BBs from exceptions, because we need to iterate over BBs
196 // for each exception.
197 for (auto *DomNode : post_order(&MDT)) {
198 MachineBasicBlock *MBB = DomNode->getBlock();
200 for (; WE; WE = WE->getParentException())
201 WE->addToBlocksSet(MBB);
202 }
203
204 // 2. We take out remaining individual BBs out. Now we have added BBs to each
205 // exceptions' BlockSet, when we take a BB out of an exception, we need to fix
206 // those sets too.
207 for (auto &P : UnwindWEVec) {
208 auto *SrcWE = P.first;
209 auto *DstWE = P.second;
210
211 SrcWE->getBlocksSet().remove_if([&](MachineBasicBlock *MBB){
212 if (MBB->isEHPad()) {
213 assert(!isReachableAmongDominated(DstWE->getEHPad(), MBB,
214 SrcWE->getEHPad(), MDT) &&
215 "We already handled EH pads above");
216 return false;
217 }
218 if (isReachableAmongDominated(DstWE->getEHPad(), MBB, SrcWE->getEHPad(),
219 MDT)) {
220 LLVM_DEBUG(dbgs() << "Remainder BB: " << MBB->getNumber() << "."
221 << MBB->getName() << " is\n");
222 WebAssemblyException *InnerWE = getExceptionFor(MBB);
223 while (InnerWE != SrcWE) {
224 LLVM_DEBUG(dbgs()
225 << " removed from " << InnerWE->getEHPad()->getNumber()
226 << "." << InnerWE->getEHPad()->getName()
227 << "'s exception\n");
228 InnerWE->removeFromBlocksSet(MBB);
229 InnerWE = InnerWE->getParentException();
230 }
231 LLVM_DEBUG(dbgs() << " removed from " << SrcWE->getEHPad()->getNumber()
232 << "." << SrcWE->getEHPad()->getName()
233 << "'s exception\n");
234 changeExceptionFor(MBB, SrcWE->getParentException());
235 if (SrcWE->getParentException())
236 SrcWE->getParentException()->addToBlocksSet(MBB);
237 return true;
238 }
239 return false;
240 });
241 }
242
243 // Add BBs to exceptions' block vector
244 for (auto *DomNode : post_order(&MDT)) {
245 MachineBasicBlock *MBB = DomNode->getBlock();
246 WebAssemblyException *WE = getExceptionFor(MBB);
247 for (; WE; WE = WE->getParentException())
249 }
250
252 ExceptionPointers.reserve(Exceptions.size());
253
254 // Add subexceptions to exceptions
255 for (auto &WE : Exceptions) {
256 ExceptionPointers.push_back(WE.get());
257 if (WE->getParentException())
258 WE->getParentException()->getSubExceptions().push_back(std::move(WE));
259 else
260 addTopLevelException(std::move(WE));
261 }
262
263 // For convenience, Blocks and SubExceptions are inserted in postorder.
264 // Reverse the lists.
265 for (auto *WE : ExceptionPointers) {
266 WE->reverseBlock();
267 std::reverse(WE->getSubExceptions().begin(), WE->getSubExceptions().end());
268 }
269}
270
272 BBMap.clear();
273 TopLevelExceptions.clear();
274}
275
277 AU.setPreservesAll();
281}
282
283void WebAssemblyExceptionInfo::discoverAndMapException(
285 const MachineDominanceFrontier &MDF) {
286 unsigned NumBlocks = 0;
287 unsigned NumSubExceptions = 0;
288
289 // Map blocks that belong to a catchpad / cleanuppad
290 MachineBasicBlock *EHPad = WE->getEHPad();
292 WL.push_back(EHPad);
293 while (!WL.empty()) {
295
296 // Find its outermost discovered exception. If this is a discovered block,
297 // check if it is already discovered to be a subexception of this exception.
298 WebAssemblyException *SubE = getOutermostException(MBB);
299 if (SubE) {
300 if (SubE != WE) {
301 // Discover a subexception of this exception.
302 SubE->setParentException(WE);
303 ++NumSubExceptions;
304 NumBlocks += SubE->getBlocksVector().capacity();
305 // All blocks that belong to this subexception have been already
306 // discovered. Skip all of them. Add the subexception's landing pad's
307 // dominance frontier to the worklist.
308 for (auto &Frontier : MDF.find(SubE->getEHPad())->second)
309 if (MDT.dominates(EHPad, Frontier))
310 WL.push_back(Frontier);
311 }
312 continue;
313 }
314
315 // This is an undiscovered block. Map it to the current exception.
317 ++NumBlocks;
318
319 // Add successors dominated by the current BB to the worklist.
320 for (auto *Succ : MBB->successors())
321 if (MDT.dominates(EHPad, Succ))
322 WL.push_back(Succ);
323 }
324
325 WE->getSubExceptions().reserve(NumSubExceptions);
326 WE->reserveBlocks(NumBlocks);
327}
328
330WebAssemblyExceptionInfo::getOutermostException(MachineBasicBlock *MBB) const {
332 if (WE) {
333 while (WebAssemblyException *Parent = WE->getParentException())
334 WE = Parent;
335 }
336 return WE;
337}
338
340 OS.indent(Depth * 2) << "Exception at depth " << getExceptionDepth()
341 << " containing: ";
342
343 for (unsigned I = 0; I < getBlocks().size(); ++I) {
345 if (I)
346 OS << ", ";
347 OS << "%bb." << MBB->getNumber();
348 if (const auto *BB = MBB->getBasicBlock())
349 if (BB->hasName())
350 OS << "." << BB->getName();
351
352 if (getEHPad() == MBB)
353 OS << " (landing-pad)";
354 }
355 OS << "\n";
356
357 for (auto &SubE : SubExceptions)
358 SubE->print(OS, Depth + 2);
359}
360
361#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
363#endif
364
366 WE.print(OS);
367 return OS;
368}
369
371 for (auto &WE : TopLevelExceptions)
372 WE->print(OS);
373}
MachineBasicBlock & MBB
#define LLVM_DUMP_METHOD
Mark debug helper function definitions like dump() that should not be stripped from debug builds.
Definition: Compiler.h:537
#define LLVM_DEBUG(X)
Definition: Debug.h:101
This file builds on the ADT/GraphTraits.h file to build generic depth first graph iterator.
#define I(x, y, z)
Definition: MD5.cpp:58
#define P(N)
if(PassOpts->AAPipeline)
#define INITIALIZE_PASS_DEPENDENCY(depName)
Definition: PassSupport.h:55
#define INITIALIZE_PASS_END(passName, arg, name, cfg, analysis)
Definition: PassSupport.h:57
#define INITIALIZE_PASS_BEGIN(passName, arg, name, cfg, analysis)
Definition: PassSupport.h:52
This file builds on the ADT/GraphTraits.h file to build a generic graph post order iterator.
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
raw_pwrite_stream & OS
WebAssembly Exception Information
static bool isReachableAmongDominated(const MachineBasicBlock *Src, const MachineBasicBlock *Dst, const MachineBasicBlock *Header, const MachineDominatorTree &MDT)
#define DEBUG_TYPE
WebAssembly Exception true
This file implements WebAssemblyException information analysis.
This file provides WebAssembly-specific target descriptions.
This file contains the declaration of the WebAssembly-specific utility functions.
Represent the analysis usage information of a pass.
AnalysisUsage & addRequired()
void setPreservesAll()
Set by analyses that do not transform their input at all.
bool isEHPad() const
Returns true if the block is a landing pad.
int getNumber() const
MachineBasicBlocks are uniquely numbered at the function level, unless they're not in a MachineFuncti...
const BasicBlock * getBasicBlock() const
Return the LLVM basic block that this instance corresponded to originally.
iterator_range< succ_iterator > successors()
iterator find(MachineBasicBlock *B)
Analysis pass which computes a MachineDominatorTree.
DominatorTree Class - Concrete subclass of DominatorTreeBase that is used to compute a normal dominat...
bool dominates(const MachineDomTreeNode *A, const MachineDomTreeNode *B) const
void getAnalysisUsage(AnalysisUsage &AU) const override
getAnalysisUsage - Subclasses that override getAnalysisUsage must call this.
const WasmEHFuncInfo * getWasmEHFuncInfo() const
getWasmEHFuncInfo - Return information about how the current function uses Wasm exception handling.
A Module instance is used to store all the information related to an LLVM module.
Definition: Module.h:65
size_type count(ConstPtrType Ptr) const
count - Return 1 if the specified pointer is in the set, 0 otherwise.
Definition: SmallPtrSet.h:436
std::pair< iterator, bool > insert(PtrType Ptr)
Inserts Ptr if and only if there is no element in the container equal to Ptr.
Definition: SmallPtrSet.h:368
SmallPtrSet - This class implements a set which is optimized for holding SmallSize or less elements.
Definition: SmallPtrSet.h:503
bool empty() const
Definition: SmallVector.h:95
void reserve(size_type N)
Definition: SmallVector.h:677
void push_back(const T &Elt)
Definition: SmallVector.h:427
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Definition: SmallVector.h:1210
void changeExceptionFor(const MachineBasicBlock *MBB, WebAssemblyException *WE)
void getAnalysisUsage(AnalysisUsage &AU) const override
getAnalysisUsage - Subclasses that override getAnalysisUsage must call this.
void recalculate(MachineFunction &MF, MachineDominatorTree &MDT, const MachineDominanceFrontier &MDF)
WebAssemblyException * getExceptionFor(const MachineBasicBlock *MBB) const
void print(raw_ostream &OS, const Module *M=nullptr) const override
print - Print out the internal state of the pass.
void releaseMemory() override
releaseMemory() - This member can be implemented by a pass if it wants to be able to release its memo...
void print(raw_ostream &OS, unsigned Depth=0) const
MachineBasicBlock * getEHPad() const
void addToBlocksSet(MachineBasicBlock *MBB)
const std::vector< std::unique_ptr< WebAssemblyException > > & getSubExceptions() const
ArrayRef< MachineBasicBlock * > getBlocks() const
std::vector< MachineBasicBlock * > & getBlocksVector()
WebAssemblyException * getParentException() const
void setParentException(WebAssemblyException *WE)
void addToBlocksVector(MachineBasicBlock *MBB)
This class implements an extremely fast bulk output stream that can only output to a stream.
Definition: raw_ostream.h:52
raw_ostream & indent(unsigned NumSpaces)
indent - Insert 'NumSpaces' spaces.
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
void dump(const SparseBitVector< ElementSize > &LHS, raw_ostream &out)
iterator_range< po_iterator< T > > post_order(const T &G)
raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition: Debug.cpp:163
raw_ostream & operator<<(raw_ostream &OS, const APFixedPoint &FX)
Definition: APFixedPoint.h:293
iterator_range< df_iterator< T > > depth_first(const T &G)