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/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 DenseMap<WebAssemblyException *, WebAssemblyException *> UnwindWEMap; 124 for (auto *DomNode : depth_first(&MDT)) { 125 MachineBasicBlock *EHPad = DomNode->getBlock(); 126 if (!EHPad->isEHPad()) 127 continue; 128 if (!EHInfo->hasUnwindDest(EHPad)) 129 continue; 130 auto *UnwindDest = EHInfo->getUnwindDest(EHPad); 131 auto *WE = getExceptionFor(EHPad); 132 auto *UnwindWE = getExceptionFor(UnwindDest); 133 if (WE->contains(UnwindWE)) { 134 UnwindWEMap[WE] = UnwindWE; 135 LLVM_DEBUG(dbgs() << "ExceptionInfo fix: " << WE->getEHPad()->getNumber() 136 << "." << WE->getEHPad()->getName() 137 << "'s exception is taken out of " 138 << UnwindWE->getEHPad()->getNumber() << "." 139 << UnwindWE->getEHPad()->getName() << "'s exception\n"); 140 if (WE->getParentException()) 141 UnwindWE->setParentException(WE->getParentException()); 142 else 143 UnwindWE->setParentException(nullptr); 144 } 145 } 146 147 // Add BBs to exceptions' block set first 148 for (auto *DomNode : post_order(&MDT)) { 149 MachineBasicBlock *MBB = DomNode->getBlock(); 150 WebAssemblyException *WE = getExceptionFor(MBB); 151 for (; WE; WE = WE->getParentException()) 152 WE->addToBlocksSet(MBB); 153 } 154 155 // After fixing subexception relationship between unwind destinations above, 156 // there can still be remaining discrepancies. 157 // 158 // For example, suppose Exception A is dominated by EHPad A and Exception B is 159 // dominated by EHPad B. EHPad A's unwind destination is EHPad B, but because 160 // EHPad B is dominated by EHPad A, the initial grouping makes Exception B a 161 // subexception of Exception A, and we fix it by taking Exception B out of 162 // Exception A above. But there can still be remaining BBs within Exception A 163 // that are reachable from Exception B. These BBs semantically don't belong 164 // to Exception A and were not a part of 'catch' clause or cleanup code in the 165 // original code, but they just happened to be grouped within Exception A 166 // because they were dominated by EHPad A. We fix this case by taking those 167 // BBs out of the incorrect exception and all its subexceptions that it 168 // belongs to. 169 for (auto &KV : UnwindWEMap) { 170 WebAssemblyException *WE = KV.first; 171 WebAssemblyException *UnwindWE = KV.second; 172 173 for (auto *MBB : WE->getBlocksSet()) { 174 if (MBB->isEHPad()) { 175 // If this assertion is triggered, it would be a violation of scoping 176 // rules in ll files, because this means an instruction in an outer 177 // scope tries to unwind to an EH pad in an inner scope. 178 assert(!isReachableAmongDominated(UnwindWE->getEHPad(), MBB, 179 WE->getEHPad(), MDT) && 180 "Outer scope unwinds to inner scope. Bug in scope rules?"); 181 continue; 182 } 183 if (isReachableAmongDominated(UnwindWE->getEHPad(), MBB, WE->getEHPad(), 184 MDT)) { 185 LLVM_DEBUG(dbgs() << "Remainder BB: " << MBB->getNumber() << "." 186 << MBB->getName() << " is "); 187 WebAssemblyException *InnerWE = getExceptionFor(MBB); 188 while (InnerWE != WE) { 189 LLVM_DEBUG(dbgs() 190 << " removed from " << InnerWE->getEHPad()->getNumber() 191 << "." << InnerWE->getEHPad()->getName() 192 << "'s exception\n"); 193 InnerWE->removeFromBlocksSet(MBB); 194 InnerWE = InnerWE->getParentException(); 195 } 196 WE->removeFromBlocksSet(MBB); 197 LLVM_DEBUG(dbgs() << " removed from " << WE->getEHPad()->getNumber() 198 << "." << WE->getEHPad()->getName() 199 << "'s exception\n"); 200 changeExceptionFor(MBB, WE->getParentException()); 201 if (WE->getParentException()) 202 WE->getParentException()->addToBlocksSet(MBB); 203 } 204 } 205 } 206 207 // Add BBs to exceptions' block vector 208 for (auto DomNode : post_order(&MDT)) { 209 MachineBasicBlock *MBB = DomNode->getBlock(); 210 WebAssemblyException *WE = getExceptionFor(MBB); 211 for (; WE; WE = WE->getParentException()) 212 WE->addToBlocksVector(MBB); 213 } 214 215 SmallVector<WebAssemblyException*, 8> ExceptionPointers; 216 ExceptionPointers.reserve(Exceptions.size()); 217 218 // Add subexceptions to exceptions 219 for (auto &WE : Exceptions) { 220 ExceptionPointers.push_back(WE.get()); 221 if (WE->getParentException()) 222 WE->getParentException()->getSubExceptions().push_back(std::move(WE)); 223 else 224 addTopLevelException(std::move(WE)); 225 } 226 227 // For convenience, Blocks and SubExceptions are inserted in postorder. 228 // Reverse the lists. 229 for (auto *WE : ExceptionPointers) { 230 WE->reverseBlock(); 231 std::reverse(WE->getSubExceptions().begin(), WE->getSubExceptions().end()); 232 } 233 } 234 235 void WebAssemblyExceptionInfo::releaseMemory() { 236 BBMap.clear(); 237 TopLevelExceptions.clear(); 238 } 239 240 void WebAssemblyExceptionInfo::getAnalysisUsage(AnalysisUsage &AU) const { 241 AU.setPreservesAll(); 242 AU.addRequired<MachineDominatorTree>(); 243 AU.addRequired<MachineDominanceFrontier>(); 244 MachineFunctionPass::getAnalysisUsage(AU); 245 } 246 247 void WebAssemblyExceptionInfo::discoverAndMapException( 248 WebAssemblyException *WE, const MachineDominatorTree &MDT, 249 const MachineDominanceFrontier &MDF) { 250 unsigned NumBlocks = 0; 251 unsigned NumSubExceptions = 0; 252 253 // Map blocks that belong to a catchpad / cleanuppad 254 MachineBasicBlock *EHPad = WE->getEHPad(); 255 SmallVector<MachineBasicBlock *, 8> WL; 256 WL.push_back(EHPad); 257 while (!WL.empty()) { 258 MachineBasicBlock *MBB = WL.pop_back_val(); 259 260 // Find its outermost discovered exception. If this is a discovered block, 261 // check if it is already discovered to be a subexception of this exception. 262 WebAssemblyException *SubE = getOutermostException(MBB); 263 if (SubE) { 264 if (SubE != WE) { 265 // Discover a subexception of this exception. 266 SubE->setParentException(WE); 267 ++NumSubExceptions; 268 NumBlocks += SubE->getBlocksVector().capacity(); 269 // All blocks that belong to this subexception have been already 270 // discovered. Skip all of them. Add the subexception's landing pad's 271 // dominance frontier to the worklist. 272 for (auto &Frontier : MDF.find(SubE->getEHPad())->second) 273 if (MDT.dominates(EHPad, Frontier)) 274 WL.push_back(Frontier); 275 } 276 continue; 277 } 278 279 // This is an undiscovered block. Map it to the current exception. 280 changeExceptionFor(MBB, WE); 281 ++NumBlocks; 282 283 // Add successors dominated by the current BB to the worklist. 284 for (auto *Succ : MBB->successors()) 285 if (MDT.dominates(EHPad, Succ)) 286 WL.push_back(Succ); 287 } 288 289 WE->getSubExceptions().reserve(NumSubExceptions); 290 WE->reserveBlocks(NumBlocks); 291 } 292 293 WebAssemblyException * 294 WebAssemblyExceptionInfo::getOutermostException(MachineBasicBlock *MBB) const { 295 WebAssemblyException *WE = getExceptionFor(MBB); 296 if (WE) { 297 while (WebAssemblyException *Parent = WE->getParentException()) 298 WE = Parent; 299 } 300 return WE; 301 } 302 303 void WebAssemblyException::print(raw_ostream &OS, unsigned Depth) const { 304 OS.indent(Depth * 2) << "Exception at depth " << getExceptionDepth() 305 << " containing: "; 306 307 for (unsigned I = 0; I < getBlocks().size(); ++I) { 308 MachineBasicBlock *MBB = getBlocks()[I]; 309 if (I) 310 OS << ", "; 311 OS << "%bb." << MBB->getNumber(); 312 if (const auto *BB = MBB->getBasicBlock()) 313 if (BB->hasName()) 314 OS << "." << BB->getName(); 315 316 if (getEHPad() == MBB) 317 OS << " (landing-pad)"; 318 } 319 OS << "\n"; 320 321 for (auto &SubE : SubExceptions) 322 SubE->print(OS, Depth + 2); 323 } 324 325 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) 326 LLVM_DUMP_METHOD void WebAssemblyException::dump() const { print(dbgs()); } 327 #endif 328 329 raw_ostream &operator<<(raw_ostream &OS, const WebAssemblyException &WE) { 330 WE.print(OS); 331 return OS; 332 } 333 334 void WebAssemblyExceptionInfo::print(raw_ostream &OS, const Module *) const { 335 for (auto &WE : TopLevelExceptions) 336 WE->print(OS); 337 } 338