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 "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/IR/Function.h" 24 #include "llvm/MC/MCAsmInfo.h" 25 #include "llvm/Target/TargetMachine.h" 26 27 using namespace llvm; 28 29 #define DEBUG_TYPE "wasm-exception-info" 30 31 char WebAssemblyExceptionInfo::ID = 0; 32 33 INITIALIZE_PASS_BEGIN(WebAssemblyExceptionInfo, DEBUG_TYPE, 34 "WebAssembly Exception Information", true, true) 35 INITIALIZE_PASS_DEPENDENCY(MachineDominatorTreeWrapperPass) 36 INITIALIZE_PASS_DEPENDENCY(MachineDominanceFrontier) 37 INITIALIZE_PASS_END(WebAssemblyExceptionInfo, DEBUG_TYPE, 38 "WebAssembly Exception Information", true, true) 39 40 bool 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); 52 LLVM_DEBUG(dump()); 53 return false; 54 } 55 56 // Check if Dst is reachable from Src using BFS. Search only within BBs 57 // dominated by Header. 58 static bool isReachableAmongDominated(const MachineBasicBlock *Src, 59 const MachineBasicBlock *Dst, 60 const MachineBasicBlock *Header, 61 const MachineDominatorTree &MDT) { 62 assert(MDT.dominates(Header, Dst)); 63 SmallVector<const MachineBasicBlock *, 8> WL; 64 SmallPtrSet<const MachineBasicBlock *, 8> Visited; 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 79 void WebAssemblyExceptionInfo::recalculate( 80 MachineFunction &MF, MachineDominatorTree &MDT, 81 const MachineDominanceFrontier &MDF) { 82 // Postorder traversal of the dominator tree. 83 SmallVector<std::unique_ptr<WebAssemblyException>, 8> Exceptions; 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); 126 SmallVector<std::pair<WebAssemblyException *, WebAssemblyException *>> 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(); 199 WebAssemblyException *WE = getExceptionFor(MBB); 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()) 248 WE->addToBlocksVector(MBB); 249 } 250 251 SmallVector<WebAssemblyException*, 8> ExceptionPointers; 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 271 void WebAssemblyExceptionInfo::releaseMemory() { 272 BBMap.clear(); 273 TopLevelExceptions.clear(); 274 } 275 276 void WebAssemblyExceptionInfo::getAnalysisUsage(AnalysisUsage &AU) const { 277 AU.setPreservesAll(); 278 AU.addRequired<MachineDominatorTreeWrapperPass>(); 279 AU.addRequired<MachineDominanceFrontier>(); 280 MachineFunctionPass::getAnalysisUsage(AU); 281 } 282 283 void WebAssemblyExceptionInfo::discoverAndMapException( 284 WebAssemblyException *WE, const MachineDominatorTree &MDT, 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(); 291 SmallVector<MachineBasicBlock *, 8> WL; 292 WL.push_back(EHPad); 293 while (!WL.empty()) { 294 MachineBasicBlock *MBB = WL.pop_back_val(); 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. 316 changeExceptionFor(MBB, WE); 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 329 WebAssemblyException * 330 WebAssemblyExceptionInfo::getOutermostException(MachineBasicBlock *MBB) const { 331 WebAssemblyException *WE = getExceptionFor(MBB); 332 if (WE) { 333 while (WebAssemblyException *Parent = WE->getParentException()) 334 WE = Parent; 335 } 336 return WE; 337 } 338 339 void WebAssemblyException::print(raw_ostream &OS, unsigned Depth) const { 340 OS.indent(Depth * 2) << "Exception at depth " << getExceptionDepth() 341 << " containing: "; 342 343 for (unsigned I = 0; I < getBlocks().size(); ++I) { 344 MachineBasicBlock *MBB = getBlocks()[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) 362 LLVM_DUMP_METHOD void WebAssemblyException::dump() const { print(dbgs()); } 363 #endif 364 365 raw_ostream &operator<<(raw_ostream &OS, const WebAssemblyException &WE) { 366 WE.print(OS); 367 return OS; 368 } 369 370 void WebAssemblyExceptionInfo::print(raw_ostream &OS, const Module *) const { 371 for (auto &WE : TopLevelExceptions) 372 WE->print(OS); 373 } 374