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/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" 24 25 using namespace llvm; 26 27 #define DEBUG_TYPE "wasm-exception-info" 28 29 char WebAssemblyExceptionInfo::ID = 0; 30 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) 37 38 bool WebAssemblyExceptionInfo::runOnMachineFunction(MachineFunction &MF) { 39 LLVM_DEBUG(dbgs() << "********** Exception Info Calculation **********\n" 40 "********** Function: " 41 << MF.getName() << '\n'); 42 releaseMemory(); 43 if (MF.getTarget().getMCAsmInfo()->getExceptionHandlingType() != 44 ExceptionHandling::Wasm || 45 !MF.getFunction().hasPersonalityFn()) 46 return false; 47 auto &MDT = getAnalysis<MachineDominatorTree>(); 48 auto &MDF = getAnalysis<MachineDominanceFrontier>(); 49 recalculate(MF, MDT, MDF); 50 LLVM_DEBUG(dump()); 51 return false; 52 } 53 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; 63 WL.push_back(Src); 64 65 while (!WL.empty()) { 66 const auto *MBB = WL.pop_back_val(); 67 if (MBB == Dst) 68 return true; 69 Visited.insert(MBB); 70 for (auto *Succ : MBB->successors()) 71 if (!Visited.count(Succ) && MDT.dominates(Header, Succ)) 72 WL.push_back(Succ); 73 } 74 return false; 75 } 76 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()) 85 continue; 86 auto WE = std::make_unique<WebAssemblyException>(EHPad); 87 discoverAndMapException(WE.get(), MDT, MDF); 88 Exceptions.push_back(std::move(WE)); 89 } 90 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: 96 // try { 97 // try { 98 // } catch (int) { // catchpad 99 // ... // this catch (int) { ... } is grouped as an exception 100 // } 101 // } catch (...) { // next unwind destination 102 // } 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. 106 // 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. 112 // 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. 116 // 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 *>> 124 UnwindWEVec; 125 for (auto *DomNode : depth_first(&MDT)) { 126 MachineBasicBlock *EHPad = DomNode->getBlock(); 127 if (!EHPad->isEHPad()) 128 continue; 129 if (!EHInfo->hasUnwindDest(EHPad)) 130 continue; 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()); 143 } 144 } 145 146 // After fixing subexception relationship between unwind destinations above, 147 // there can still be remaining discrepancies. 148 // 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 159 // belongs to. 160 // 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()) 167 continue; 168 auto *WE = getExceptionFor(EHPad); 169 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(), 179 MDT)) { 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()); 187 } 188 } 189 } 190 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); 199 } 200 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 203 // those sets too. 204 for (auto &P : UnwindWEVec) { 205 auto *SrcWE = P.first; 206 auto *DstWE = P.second; 207 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"); 213 continue; 214 } 215 if (isReachableAmongDominated(DstWE->getEHPad(), MBB, SrcWE->getEHPad(), 216 MDT)) { 217 LLVM_DEBUG(dbgs() << "Remainder BB: " << MBB->getNumber() << "." 218 << MBB->getName() << " is\n"); 219 WebAssemblyException *InnerWE = getExceptionFor(MBB); 220 while (InnerWE != SrcWE) { 221 LLVM_DEBUG(dbgs() 222 << " removed from " << InnerWE->getEHPad()->getNumber() 223 << "." << InnerWE->getEHPad()->getName() 224 << "'s exception\n"); 225 InnerWE->removeFromBlocksSet(MBB); 226 InnerWE = InnerWE->getParentException(); 227 } 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); 235 } 236 } 237 } 238 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); 245 } 246 247 SmallVector<WebAssemblyException*, 8> ExceptionPointers; 248 ExceptionPointers.reserve(Exceptions.size()); 249 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)); 255 else 256 addTopLevelException(std::move(WE)); 257 } 258 259 // For convenience, Blocks and SubExceptions are inserted in postorder. 260 // Reverse the lists. 261 for (auto *WE : ExceptionPointers) { 262 WE->reverseBlock(); 263 std::reverse(WE->getSubExceptions().begin(), WE->getSubExceptions().end()); 264 } 265 } 266 267 void WebAssemblyExceptionInfo::releaseMemory() { 268 BBMap.clear(); 269 TopLevelExceptions.clear(); 270 } 271 272 void WebAssemblyExceptionInfo::getAnalysisUsage(AnalysisUsage &AU) const { 273 AU.setPreservesAll(); 274 AU.addRequired<MachineDominatorTree>(); 275 AU.addRequired<MachineDominanceFrontier>(); 276 MachineFunctionPass::getAnalysisUsage(AU); 277 } 278 279 void WebAssemblyExceptionInfo::discoverAndMapException( 280 WebAssemblyException *WE, const MachineDominatorTree &MDT, 281 const MachineDominanceFrontier &MDF) { 282 unsigned NumBlocks = 0; 283 unsigned NumSubExceptions = 0; 284 285 // Map blocks that belong to a catchpad / cleanuppad 286 MachineBasicBlock *EHPad = WE->getEHPad(); 287 SmallVector<MachineBasicBlock *, 8> WL; 288 WL.push_back(EHPad); 289 while (!WL.empty()) { 290 MachineBasicBlock *MBB = WL.pop_back_val(); 291 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); 295 if (SubE) { 296 if (SubE != WE) { 297 // Discover a subexception of this exception. 298 SubE->setParentException(WE); 299 ++NumSubExceptions; 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); 307 } 308 continue; 309 } 310 311 // This is an undiscovered block. Map it to the current exception. 312 changeExceptionFor(MBB, WE); 313 ++NumBlocks; 314 315 // Add successors dominated by the current BB to the worklist. 316 for (auto *Succ : MBB->successors()) 317 if (MDT.dominates(EHPad, Succ)) 318 WL.push_back(Succ); 319 } 320 321 WE->getSubExceptions().reserve(NumSubExceptions); 322 WE->reserveBlocks(NumBlocks); 323 } 324 325 WebAssemblyException * 326 WebAssemblyExceptionInfo::getOutermostException(MachineBasicBlock *MBB) const { 327 WebAssemblyException *WE = getExceptionFor(MBB); 328 if (WE) { 329 while (WebAssemblyException *Parent = WE->getParentException()) 330 WE = Parent; 331 } 332 return WE; 333 } 334 335 void WebAssemblyException::print(raw_ostream &OS, unsigned Depth) const { 336 OS.indent(Depth * 2) << "Exception at depth " << getExceptionDepth() 337 << " containing: "; 338 339 for (unsigned I = 0; I < getBlocks().size(); ++I) { 340 MachineBasicBlock *MBB = getBlocks()[I]; 341 if (I) 342 OS << ", "; 343 OS << "%bb." << MBB->getNumber(); 344 if (const auto *BB = MBB->getBasicBlock()) 345 if (BB->hasName()) 346 OS << "." << BB->getName(); 347 348 if (getEHPad() == MBB) 349 OS << " (landing-pad)"; 350 } 351 OS << "\n"; 352 353 for (auto &SubE : SubExceptions) 354 SubE->print(OS, Depth + 2); 355 } 356 357 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) 358 LLVM_DUMP_METHOD void WebAssemblyException::dump() const { print(dbgs()); } 359 #endif 360 361 raw_ostream &operator<<(raw_ostream &OS, const WebAssemblyException &WE) { 362 WE.print(OS); 363 return OS; 364 } 365 366 void WebAssemblyExceptionInfo::print(raw_ostream &OS, const Module *) const { 367 for (auto &WE : TopLevelExceptions) 368 WE->print(OS); 369 } 370