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 14 #include "WebAssemblyExceptionInfo.h" 15 #include "MCTargetDesc/WebAssemblyMCTargetDesc.h" 16 #include "Utils/WebAssemblyUtilities.h" 17 #include "llvm/ADT/DepthFirstIterator.h" 18 #include "llvm/ADT/PostOrderIterator.h" 19 #include "llvm/CodeGen/MachineDominanceFrontier.h" 20 #include "llvm/CodeGen/MachineDominators.h" 21 #include "llvm/CodeGen/WasmEHFuncInfo.h" 22 #include "llvm/InitializePasses.h" 23 #include "llvm/MC/MCAsmInfo.h" 24 #include "llvm/Target/TargetMachine.h" 25 26 using namespace llvm; 27 28 #define DEBUG_TYPE "wasm-exception-info" 29 30 char WebAssemblyExceptionInfo::ID = 0; 31 32 INITIALIZE_PASS_BEGIN(WebAssemblyExceptionInfo, DEBUG_TYPE, 33 "WebAssembly Exception Information", true, true) 34 INITIALIZE_PASS_DEPENDENCY(MachineDominatorTree) 35 INITIALIZE_PASS_DEPENDENCY(MachineDominanceFrontier) 36 INITIALIZE_PASS_END(WebAssemblyExceptionInfo, DEBUG_TYPE, 37 "WebAssembly Exception Information", true, true) 38 39 bool WebAssemblyExceptionInfo::runOnMachineFunction(MachineFunction &MF) { 40 LLVM_DEBUG(dbgs() << "********** Exception Info Calculation **********\n" 41 "********** Function: " 42 << MF.getName() << '\n'); 43 releaseMemory(); 44 if (MF.getTarget().getMCAsmInfo()->getExceptionHandlingType() != 45 ExceptionHandling::Wasm || 46 !MF.getFunction().hasPersonalityFn()) 47 return false; 48 auto &MDT = getAnalysis<MachineDominatorTree>(); 49 auto &MDF = getAnalysis<MachineDominanceFrontier>(); 50 recalculate(MF, MDT, MDF); 51 LLVM_DEBUG(dump()); 52 return false; 53 } 54 55 // Check if Dst is reachable from Src using BFS. Search only within BBs 56 // dominated by Header. 57 static bool isReachableAmongDominated(const MachineBasicBlock *Src, 58 const MachineBasicBlock *Dst, 59 const MachineBasicBlock *Header, 60 const MachineDominatorTree &MDT) { 61 assert(MDT.dominates(Header, Dst)); 62 SmallVector<const MachineBasicBlock *, 8> WL; 63 SmallPtrSet<const MachineBasicBlock *, 8> Visited; 64 WL.push_back(Src); 65 66 while (!WL.empty()) { 67 const auto *MBB = WL.pop_back_val(); 68 if (MBB == Dst) 69 return true; 70 Visited.insert(MBB); 71 for (auto *Succ : MBB->successors()) 72 if (!Visited.count(Succ) && MDT.dominates(Header, Succ)) 73 WL.push_back(Succ); 74 } 75 return false; 76 } 77 78 void WebAssemblyExceptionInfo::recalculate( 79 MachineFunction &MF, MachineDominatorTree &MDT, 80 const MachineDominanceFrontier &MDF) { 81 // Postorder traversal of the dominator tree. 82 SmallVector<std::unique_ptr<WebAssemblyException>, 8> Exceptions; 83 for (auto *DomNode : post_order(&MDT)) { 84 MachineBasicBlock *EHPad = DomNode->getBlock(); 85 if (!EHPad->isEHPad()) 86 continue; 87 auto WE = std::make_unique<WebAssemblyException>(EHPad); 88 discoverAndMapException(WE.get(), MDT, MDF); 89 Exceptions.push_back(std::move(WE)); 90 } 91 92 // WasmEHFuncInfo contains a map of <catchpad, its next unwind destination>, 93 // which means, if an exception is not caught by the catchpad, it should end 94 // up in the next unwind destination stored in this data structure. (It is 95 // written as catchswitch's 'unwind' destination in ll files.) The below is an 96 // intuitive example of their relationship in C++ code: 97 // try { 98 // try { 99 // } catch (int) { // catchpad 100 // ... // this catch (int) { ... } is grouped as an exception 101 // } 102 // } catch (...) { // next unwind destination 103 // } 104 // (The example is try-catches for illustration purpose, but the unwind 105 // destination can be also a cleanuppad generated by destructor calls.) So the 106 // unwind destination is in the outside of the catchpad's exception. 107 // 108 // We group exceptions in this analysis simply by including all BBs dominated 109 // by an EH pad. But in case the EH pad's unwind destination does not have any 110 // children outside of the exception, that unwind destination ends up also 111 // being dominated by the EH pad and included in the exception, which is not 112 // semantically correct, because it unwinds/rethrows into an inner scope. 113 // 114 // Here we extract those unwind destinations from their (incorrect) parent 115 // exception. Note that the unwind destinations may not be an immediate 116 // 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, because 119 // otherwise the result can be incorrect. For example, when there are three 120 // 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, we 122 // end up extracting C only out of B but not out of A. 123 const auto *EHInfo = MF.getWasmEHFuncInfo(); 124 assert(EHInfo); 125 SmallVector<std::pair<WebAssemblyException *, WebAssemblyException *>> 126 UnwindWEVec; 127 for (auto *DomNode : depth_first(&MDT)) { 128 MachineBasicBlock *EHPad = DomNode->getBlock(); 129 if (!EHPad->isEHPad()) 130 continue; 131 if (!EHInfo->hasUnwindDest(EHPad)) 132 continue; 133 auto *UnwindDest = EHInfo->getUnwindDest(EHPad); 134 auto *SrcWE = getExceptionFor(EHPad); 135 auto *DstWE = getExceptionFor(UnwindDest); 136 if (SrcWE->contains(DstWE)) { 137 UnwindWEVec.push_back(std::make_pair(SrcWE, DstWE)); 138 LLVM_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"); 144 DstWE->setParentException(SrcWE->getParentException()); 145 } 146 } 147 148 // 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 is 152 // dominated by EHPad B. EHPad A's unwind destination is EHPad B, but because 153 // EHPad B is dominated by EHPad A, the initial grouping makes Exception B a 154 // subexception of Exception A, and we fix it by taking Exception B out of 155 // Exception A above. But there can still be remaining BBs within Exception A 156 // that are reachable from Exception B. These BBs semantically don't belong 157 // to Exception A and were not a part of this 'catch' clause or cleanup code 158 // in the original code, but they just happened to be grouped within Exception 159 // A because they were dominated by EHPad A. We fix this case by taking those 160 // BBs out of the incorrect exception and all its subexceptions that it 161 // belongs to. 162 // 163 // 1. First, we take out remaining incorrect subexceptions. This part is 164 // easier, because we haven't added BBs to exceptions yet, we only need to 165 // change parent exception pointer. 166 for (auto *DomNode : depth_first(&MDT)) { 167 MachineBasicBlock *EHPad = DomNode->getBlock(); 168 if (!EHPad->isEHPad()) 169 continue; 170 auto *WE = getExceptionFor(EHPad); 171 172 // For each source EHPad -> unwind destination EHPad 173 for (auto &P : UnwindWEVec) { 174 auto *SrcWE = P.first; 175 auto *DstWE = P.second; 176 // If WE (the current EH pad's exception) is still contained in SrcWE but 177 // reachable from DstWE that was taken out of SrcWE above, we have to take 178 // out WE out of SrcWE too. 179 if (WE != SrcWE && SrcWE->contains(WE) && !DstWE->contains(WE) && 180 isReachableAmongDominated(DstWE->getEHPad(), EHPad, SrcWE->getEHPad(), 181 MDT)) { 182 LLVM_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"); 188 WE->setParentException(SrcWE->getParentException()); 189 } 190 } 191 } 192 193 // Add BBs to exceptions' block set. This is a preparation to take out 194 // remaining incorect BBs from exceptions, because we need to iterate over BBs 195 // for each exception. 196 for (auto *DomNode : post_order(&MDT)) { 197 MachineBasicBlock *MBB = DomNode->getBlock(); 198 WebAssemblyException *WE = getExceptionFor(MBB); 199 for (; WE; WE = WE->getParentException()) 200 WE->addToBlocksSet(MBB); 201 } 202 203 // 2. We take out remaining individual BBs out. Now we have added BBs to each 204 // exceptions' BlockSet, when we take a BB out of an exception, we need to fix 205 // those sets too. 206 for (auto &P : UnwindWEVec) { 207 auto *SrcWE = P.first; 208 auto *DstWE = P.second; 209 210 for (auto *MBB : SrcWE->getBlocksSet()) { 211 if (MBB->isEHPad()) { 212 assert(!isReachableAmongDominated(DstWE->getEHPad(), MBB, 213 SrcWE->getEHPad(), MDT) && 214 "We already handled EH pads above"); 215 continue; 216 } 217 if (isReachableAmongDominated(DstWE->getEHPad(), MBB, SrcWE->getEHPad(), 218 MDT)) { 219 LLVM_DEBUG(dbgs() << "Remainder BB: " << MBB->getNumber() << "." 220 << MBB->getName() << " is\n"); 221 WebAssemblyException *InnerWE = getExceptionFor(MBB); 222 while (InnerWE != SrcWE) { 223 LLVM_DEBUG(dbgs() 224 << " removed from " << InnerWE->getEHPad()->getNumber() 225 << "." << InnerWE->getEHPad()->getName() 226 << "'s exception\n"); 227 InnerWE->removeFromBlocksSet(MBB); 228 InnerWE = InnerWE->getParentException(); 229 } 230 SrcWE->removeFromBlocksSet(MBB); 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 } 238 } 239 } 240 241 // Add BBs to exceptions' block vector 242 for (auto *DomNode : post_order(&MDT)) { 243 MachineBasicBlock *MBB = DomNode->getBlock(); 244 WebAssemblyException *WE = getExceptionFor(MBB); 245 for (; WE; WE = WE->getParentException()) 246 WE->addToBlocksVector(MBB); 247 } 248 249 SmallVector<WebAssemblyException*, 8> ExceptionPointers; 250 ExceptionPointers.reserve(Exceptions.size()); 251 252 // Add subexceptions to exceptions 253 for (auto &WE : Exceptions) { 254 ExceptionPointers.push_back(WE.get()); 255 if (WE->getParentException()) 256 WE->getParentException()->getSubExceptions().push_back(std::move(WE)); 257 else 258 addTopLevelException(std::move(WE)); 259 } 260 261 // For convenience, Blocks and SubExceptions are inserted in postorder. 262 // Reverse the lists. 263 for (auto *WE : ExceptionPointers) { 264 WE->reverseBlock(); 265 std::reverse(WE->getSubExceptions().begin(), WE->getSubExceptions().end()); 266 } 267 } 268 269 void WebAssemblyExceptionInfo::releaseMemory() { 270 BBMap.clear(); 271 TopLevelExceptions.clear(); 272 } 273 274 void WebAssemblyExceptionInfo::getAnalysisUsage(AnalysisUsage &AU) const { 275 AU.setPreservesAll(); 276 AU.addRequired<MachineDominatorTree>(); 277 AU.addRequired<MachineDominanceFrontier>(); 278 MachineFunctionPass::getAnalysisUsage(AU); 279 } 280 281 void WebAssemblyExceptionInfo::discoverAndMapException( 282 WebAssemblyException *WE, const MachineDominatorTree &MDT, 283 const MachineDominanceFrontier &MDF) { 284 unsigned NumBlocks = 0; 285 unsigned NumSubExceptions = 0; 286 287 // Map blocks that belong to a catchpad / cleanuppad 288 MachineBasicBlock *EHPad = WE->getEHPad(); 289 SmallVector<MachineBasicBlock *, 8> WL; 290 WL.push_back(EHPad); 291 while (!WL.empty()) { 292 MachineBasicBlock *MBB = WL.pop_back_val(); 293 294 // Find its outermost discovered exception. If this is a discovered block, 295 // check if it is already discovered to be a subexception of this exception. 296 WebAssemblyException *SubE = getOutermostException(MBB); 297 if (SubE) { 298 if (SubE != WE) { 299 // Discover a subexception of this exception. 300 SubE->setParentException(WE); 301 ++NumSubExceptions; 302 NumBlocks += SubE->getBlocksVector().capacity(); 303 // All blocks that belong to this subexception have been already 304 // discovered. Skip all of them. Add the subexception's landing pad's 305 // dominance frontier to the worklist. 306 for (auto &Frontier : MDF.find(SubE->getEHPad())->second) 307 if (MDT.dominates(EHPad, Frontier)) 308 WL.push_back(Frontier); 309 } 310 continue; 311 } 312 313 // This is an undiscovered block. Map it to the current exception. 314 changeExceptionFor(MBB, WE); 315 ++NumBlocks; 316 317 // Add successors dominated by the current BB to the worklist. 318 for (auto *Succ : MBB->successors()) 319 if (MDT.dominates(EHPad, Succ)) 320 WL.push_back(Succ); 321 } 322 323 WE->getSubExceptions().reserve(NumSubExceptions); 324 WE->reserveBlocks(NumBlocks); 325 } 326 327 WebAssemblyException * 328 WebAssemblyExceptionInfo::getOutermostException(MachineBasicBlock *MBB) const { 329 WebAssemblyException *WE = getExceptionFor(MBB); 330 if (WE) { 331 while (WebAssemblyException *Parent = WE->getParentException()) 332 WE = Parent; 333 } 334 return WE; 335 } 336 337 void WebAssemblyException::print(raw_ostream &OS, unsigned Depth) const { 338 OS.indent(Depth * 2) << "Exception at depth " << getExceptionDepth() 339 << " containing: "; 340 341 for (unsigned I = 0; I < getBlocks().size(); ++I) { 342 MachineBasicBlock *MBB = getBlocks()[I]; 343 if (I) 344 OS << ", "; 345 OS << "%bb." << MBB->getNumber(); 346 if (const auto *BB = MBB->getBasicBlock()) 347 if (BB->hasName()) 348 OS << "." << BB->getName(); 349 350 if (getEHPad() == MBB) 351 OS << " (landing-pad)"; 352 } 353 OS << "\n"; 354 355 for (auto &SubE : SubExceptions) 356 SubE->print(OS, Depth + 2); 357 } 358 359 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) 360 LLVM_DUMP_METHOD void WebAssemblyException::dump() const { print(dbgs()); } 361 #endif 362 363 raw_ostream &operator<<(raw_ostream &OS, const WebAssemblyException &WE) { 364 WE.print(OS); 365 return OS; 366 } 367 368 void WebAssemblyExceptionInfo::print(raw_ostream &OS, const Module *) const { 369 for (auto &WE : TopLevelExceptions) 370 WE->print(OS); 371 } 372