1 //===--- WebAssemblyExceptionInfo.cpp - Exception Infomation --------------===//
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
7 //===----------------------------------------------------------------------===//
10 /// \brief This file implements WebAssemblyException information analysis.
12 //===----------------------------------------------------------------------===//
14 #include "WebAssemblyExceptionInfo.h"
15 #include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
16 #include "Utils/WebAssemblyUtilities.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/MC/MCAsmInfo.h"
23 #include "llvm/Target/TargetMachine.h"
27 #define DEBUG_TYPE "wasm-exception-info"
29 char WebAssemblyExceptionInfo::ID
= 0;
31 INITIALIZE_PASS_BEGIN(WebAssemblyExceptionInfo
, DEBUG_TYPE
,
32 "WebAssembly Exception Information", true, true)
33 INITIALIZE_PASS_DEPENDENCY(MachineDominatorTree
)
34 INITIALIZE_PASS_DEPENDENCY(MachineDominanceFrontier
)
35 INITIALIZE_PASS_END(WebAssemblyExceptionInfo
, DEBUG_TYPE
,
36 "WebAssembly Exception Information", true, true)
38 bool WebAssemblyExceptionInfo::runOnMachineFunction(MachineFunction
&MF
) {
39 LLVM_DEBUG(dbgs() << "********** Exception Info Calculation **********\n"
40 "********** Function: "
41 << MF
.getName() << '\n');
43 if (MF
.getTarget().getMCAsmInfo()->getExceptionHandlingType() !=
44 ExceptionHandling::Wasm
||
45 !MF
.getFunction().hasPersonalityFn())
47 auto &MDT
= getAnalysis
<MachineDominatorTree
>();
48 auto &MDF
= getAnalysis
<MachineDominanceFrontier
>();
49 recalculate(MF
, MDT
, MDF
);
54 // Check if Dst is reachable from Src using BFS. Search only within BBs
55 // dominated by Header.
56 static bool isReachableAmongDominated(const MachineBasicBlock
*Src
,
57 const MachineBasicBlock
*Dst
,
58 const MachineBasicBlock
*Header
,
59 const MachineDominatorTree
&MDT
) {
60 assert(MDT
.dominates(Header
, Dst
));
61 SmallVector
<const MachineBasicBlock
*, 8> WL
;
62 SmallPtrSet
<const MachineBasicBlock
*, 8> Visited
;
66 const auto *MBB
= WL
.pop_back_val();
70 for (auto *Succ
: MBB
->successors())
71 if (!Visited
.count(Succ
) && MDT
.dominates(Header
, Succ
))
77 void WebAssemblyExceptionInfo::recalculate(
78 MachineFunction
&MF
, MachineDominatorTree
&MDT
,
79 const MachineDominanceFrontier
&MDF
) {
80 // Postorder traversal of the dominator tree.
81 SmallVector
<std::unique_ptr
<WebAssemblyException
>, 8> Exceptions
;
82 for (auto DomNode
: post_order(&MDT
)) {
83 MachineBasicBlock
*EHPad
= DomNode
->getBlock();
84 if (!EHPad
->isEHPad())
86 auto WE
= std::make_unique
<WebAssemblyException
>(EHPad
);
87 discoverAndMapException(WE
.get(), MDT
, MDF
);
88 Exceptions
.push_back(std::move(WE
));
91 // WasmEHFuncInfo contains a map of <catchpad, its next unwind destination>,
92 // which means, if an exception is not caught by the catchpad, it should end
93 // up in the next unwind destination stored in this data structure. (It is
94 // written as catchswitch's 'unwind' destination in ll files.) The below is an
95 // intuitive example of their relationship in C++ code:
98 // } catch (int) { // catchpad
99 // ... // this catch (int) { ... } is grouped as an exception
101 // } catch (...) { // next unwind destination
103 // (The example is try-catches for illustration purpose, but the unwind
104 // destination can be also a cleanuppad generated by destructor calls.) So the
105 // unwind destination is in the outside of the catchpad's exception.
107 // We group exceptions in this analysis simply by including all BBs dominated
108 // by an EH pad. But in case the EH pad's unwind destination does not have any
109 // children outside of the exception, that unwind destination ends up also
110 // being dominated by the EH pad and included in the exception, which is not
111 // semantically correct, because it unwinds/rethrows into an inner scope.
113 // Here we extract those unwind destinations from their (incorrect) parent
114 // exception. Note that the unwind destinations may not be an immediate
115 // children of the parent exception, so we have to traverse the parent chain.
117 // We should traverse BBs in the preorder of the dominator tree, because
118 // otherwise the result can be incorrect. For example, when there are three
119 // exceptions A, B, and C and A > B > C (> is subexception relationship here),
120 // and A's unwind destination is B and B's is C. When we visit B before A, we
121 // end up extracting C only out of B but not out of A.
122 const auto *EHInfo
= MF
.getWasmEHFuncInfo();
123 SmallVector
<std::pair
<WebAssemblyException
*, WebAssemblyException
*>>
125 for (auto *DomNode
: depth_first(&MDT
)) {
126 MachineBasicBlock
*EHPad
= DomNode
->getBlock();
127 if (!EHPad
->isEHPad())
129 if (!EHInfo
->hasUnwindDest(EHPad
))
131 auto *UnwindDest
= EHInfo
->getUnwindDest(EHPad
);
132 auto *SrcWE
= getExceptionFor(EHPad
);
133 auto *DstWE
= getExceptionFor(UnwindDest
);
134 if (SrcWE
->contains(DstWE
)) {
135 UnwindWEVec
.push_back(std::make_pair(SrcWE
, DstWE
));
136 LLVM_DEBUG(dbgs() << "Unwind destination ExceptionInfo fix:\n "
137 << DstWE
->getEHPad()->getNumber() << "."
138 << DstWE
->getEHPad()->getName()
139 << "'s exception is taken out of "
140 << SrcWE
->getEHPad()->getNumber() << "."
141 << SrcWE
->getEHPad()->getName() << "'s exception\n");
142 DstWE
->setParentException(SrcWE
->getParentException());
146 // After fixing subexception relationship between unwind destinations above,
147 // there can still be remaining discrepancies.
149 // For example, suppose Exception A is dominated by EHPad A and Exception B is
150 // dominated by EHPad B. EHPad A's unwind destination is EHPad B, but because
151 // EHPad B is dominated by EHPad A, the initial grouping makes Exception B a
152 // subexception of Exception A, and we fix it by taking Exception B out of
153 // Exception A above. But there can still be remaining BBs within Exception A
154 // that are reachable from Exception B. These BBs semantically don't belong
155 // to Exception A and were not a part of this 'catch' clause or cleanup code
156 // in the original code, but they just happened to be grouped within Exception
157 // A because they were dominated by EHPad A. We fix this case by taking those
158 // BBs out of the incorrect exception and all its subexceptions that it
161 // 1. First, we take out remaining incorrect subexceptions. This part is
162 // easier, because we haven't added BBs to exceptions yet, we only need to
163 // change parent exception pointer.
164 for (auto *DomNode
: depth_first(&MDT
)) {
165 MachineBasicBlock
*EHPad
= DomNode
->getBlock();
166 if (!EHPad
->isEHPad())
168 auto *WE
= getExceptionFor(EHPad
);
170 // For each source EHPad -> unwind destination EHPad
171 for (auto &P
: UnwindWEVec
) {
172 auto *SrcWE
= P
.first
;
173 auto *DstWE
= P
.second
;
174 // If WE (the current EH pad's exception) is still contained in SrcWE but
175 // reachable from DstWE that was taken out of SrcWE above, we have to take
176 // out WE out of SrcWE too.
177 if (WE
!= SrcWE
&& SrcWE
->contains(WE
) && !DstWE
->contains(WE
) &&
178 isReachableAmongDominated(DstWE
->getEHPad(), EHPad
, SrcWE
->getEHPad(),
180 LLVM_DEBUG(dbgs() << "Remaining reachable ExceptionInfo fix:\n "
181 << WE
->getEHPad()->getNumber() << "."
182 << WE
->getEHPad()->getName()
183 << "'s exception is taken out of "
184 << SrcWE
->getEHPad()->getNumber() << "."
185 << SrcWE
->getEHPad()->getName() << "'s exception\n");
186 WE
->setParentException(SrcWE
->getParentException());
191 // Add BBs to exceptions' block set. This is a preparation to take out
192 // remaining incorect BBs from exceptions, because we need to iterate over BBs
193 // for each exception.
194 for (auto *DomNode
: post_order(&MDT
)) {
195 MachineBasicBlock
*MBB
= DomNode
->getBlock();
196 WebAssemblyException
*WE
= getExceptionFor(MBB
);
197 for (; WE
; WE
= WE
->getParentException())
198 WE
->addToBlocksSet(MBB
);
201 // 2. We take out remaining individual BBs out. Now we have added BBs to each
202 // exceptions' BlockSet, when we take a BB out of an exception, we need to fix
204 for (auto &P
: UnwindWEVec
) {
205 auto *SrcWE
= P
.first
;
206 auto *DstWE
= P
.second
;
208 for (auto *MBB
: SrcWE
->getBlocksSet()) {
209 if (MBB
->isEHPad()) {
210 assert(!isReachableAmongDominated(DstWE
->getEHPad(), MBB
,
211 SrcWE
->getEHPad(), MDT
) &&
212 "We already handled EH pads above");
215 if (isReachableAmongDominated(DstWE
->getEHPad(), MBB
, SrcWE
->getEHPad(),
217 LLVM_DEBUG(dbgs() << "Remainder BB: " << MBB
->getNumber() << "."
218 << MBB
->getName() << " is\n");
219 WebAssemblyException
*InnerWE
= getExceptionFor(MBB
);
220 while (InnerWE
!= SrcWE
) {
222 << " removed from " << InnerWE
->getEHPad()->getNumber()
223 << "." << InnerWE
->getEHPad()->getName()
224 << "'s exception\n");
225 InnerWE
->removeFromBlocksSet(MBB
);
226 InnerWE
= InnerWE
->getParentException();
228 SrcWE
->removeFromBlocksSet(MBB
);
229 LLVM_DEBUG(dbgs() << " removed from " << SrcWE
->getEHPad()->getNumber()
230 << "." << SrcWE
->getEHPad()->getName()
231 << "'s exception\n");
232 changeExceptionFor(MBB
, SrcWE
->getParentException());
233 if (SrcWE
->getParentException())
234 SrcWE
->getParentException()->addToBlocksSet(MBB
);
239 // Add BBs to exceptions' block vector
240 for (auto DomNode
: post_order(&MDT
)) {
241 MachineBasicBlock
*MBB
= DomNode
->getBlock();
242 WebAssemblyException
*WE
= getExceptionFor(MBB
);
243 for (; WE
; WE
= WE
->getParentException())
244 WE
->addToBlocksVector(MBB
);
247 SmallVector
<WebAssemblyException
*, 8> ExceptionPointers
;
248 ExceptionPointers
.reserve(Exceptions
.size());
250 // Add subexceptions to exceptions
251 for (auto &WE
: Exceptions
) {
252 ExceptionPointers
.push_back(WE
.get());
253 if (WE
->getParentException())
254 WE
->getParentException()->getSubExceptions().push_back(std::move(WE
));
256 addTopLevelException(std::move(WE
));
259 // For convenience, Blocks and SubExceptions are inserted in postorder.
260 // Reverse the lists.
261 for (auto *WE
: ExceptionPointers
) {
263 std::reverse(WE
->getSubExceptions().begin(), WE
->getSubExceptions().end());
267 void WebAssemblyExceptionInfo::releaseMemory() {
269 TopLevelExceptions
.clear();
272 void WebAssemblyExceptionInfo::getAnalysisUsage(AnalysisUsage
&AU
) const {
273 AU
.setPreservesAll();
274 AU
.addRequired
<MachineDominatorTree
>();
275 AU
.addRequired
<MachineDominanceFrontier
>();
276 MachineFunctionPass::getAnalysisUsage(AU
);
279 void WebAssemblyExceptionInfo::discoverAndMapException(
280 WebAssemblyException
*WE
, const MachineDominatorTree
&MDT
,
281 const MachineDominanceFrontier
&MDF
) {
282 unsigned NumBlocks
= 0;
283 unsigned NumSubExceptions
= 0;
285 // Map blocks that belong to a catchpad / cleanuppad
286 MachineBasicBlock
*EHPad
= WE
->getEHPad();
287 SmallVector
<MachineBasicBlock
*, 8> WL
;
289 while (!WL
.empty()) {
290 MachineBasicBlock
*MBB
= WL
.pop_back_val();
292 // Find its outermost discovered exception. If this is a discovered block,
293 // check if it is already discovered to be a subexception of this exception.
294 WebAssemblyException
*SubE
= getOutermostException(MBB
);
297 // Discover a subexception of this exception.
298 SubE
->setParentException(WE
);
300 NumBlocks
+= SubE
->getBlocksVector().capacity();
301 // All blocks that belong to this subexception have been already
302 // discovered. Skip all of them. Add the subexception's landing pad's
303 // dominance frontier to the worklist.
304 for (auto &Frontier
: MDF
.find(SubE
->getEHPad())->second
)
305 if (MDT
.dominates(EHPad
, Frontier
))
306 WL
.push_back(Frontier
);
311 // This is an undiscovered block. Map it to the current exception.
312 changeExceptionFor(MBB
, WE
);
315 // Add successors dominated by the current BB to the worklist.
316 for (auto *Succ
: MBB
->successors())
317 if (MDT
.dominates(EHPad
, Succ
))
321 WE
->getSubExceptions().reserve(NumSubExceptions
);
322 WE
->reserveBlocks(NumBlocks
);
325 WebAssemblyException
*
326 WebAssemblyExceptionInfo::getOutermostException(MachineBasicBlock
*MBB
) const {
327 WebAssemblyException
*WE
= getExceptionFor(MBB
);
329 while (WebAssemblyException
*Parent
= WE
->getParentException())
335 void WebAssemblyException::print(raw_ostream
&OS
, unsigned Depth
) const {
336 OS
.indent(Depth
* 2) << "Exception at depth " << getExceptionDepth()
339 for (unsigned I
= 0; I
< getBlocks().size(); ++I
) {
340 MachineBasicBlock
*MBB
= getBlocks()[I
];
343 OS
<< "%bb." << MBB
->getNumber();
344 if (const auto *BB
= MBB
->getBasicBlock())
346 OS
<< "." << BB
->getName();
348 if (getEHPad() == MBB
)
349 OS
<< " (landing-pad)";
353 for (auto &SubE
: SubExceptions
)
354 SubE
->print(OS
, Depth
+ 2);
357 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
358 LLVM_DUMP_METHOD
void WebAssemblyException::dump() const { print(dbgs()); }
361 raw_ostream
&operator<<(raw_ostream
&OS
, const WebAssemblyException
&WE
) {
366 void WebAssemblyExceptionInfo::print(raw_ostream
&OS
, const Module
*) const {
367 for (auto &WE
: TopLevelExceptions
)