1 //=== WebAssemblyLateEHPrepare.cpp - WebAssembly Exception Preparation -===// 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 Does various transformations for exception handling. 11 /// 12 //===----------------------------------------------------------------------===// 13 14 #include "MCTargetDesc/WebAssemblyMCTargetDesc.h" 15 #include "Utils/WebAssemblyUtilities.h" 16 #include "WebAssembly.h" 17 #include "WebAssemblySubtarget.h" 18 #include "llvm/ADT/SmallPtrSet.h" 19 #include "llvm/CodeGen/MachineFunctionPass.h" 20 #include "llvm/CodeGen/MachineInstrBuilder.h" 21 #include "llvm/CodeGen/WasmEHFuncInfo.h" 22 #include "llvm/MC/MCAsmInfo.h" 23 #include "llvm/Support/Debug.h" 24 #include "llvm/Target/TargetMachine.h" 25 using namespace llvm; 26 27 #define DEBUG_TYPE "wasm-late-eh-prepare" 28 29 namespace { 30 class WebAssemblyLateEHPrepare final : public MachineFunctionPass { 31 StringRef getPassName() const override { 32 return "WebAssembly Late Prepare Exception"; 33 } 34 35 bool runOnMachineFunction(MachineFunction &MF) override; 36 bool removeUnreachableEHPads(MachineFunction &MF); 37 void recordCatchRetBBs(MachineFunction &MF); 38 bool hoistCatches(MachineFunction &MF); 39 bool addCatchAlls(MachineFunction &MF); 40 bool replaceFuncletReturns(MachineFunction &MF); 41 bool removeUnnecessaryUnreachables(MachineFunction &MF); 42 bool restoreStackPointer(MachineFunction &MF); 43 44 MachineBasicBlock *getMatchingEHPad(MachineInstr *MI); 45 SmallPtrSet<MachineBasicBlock *, 8> CatchRetBBs; 46 47 public: 48 static char ID; // Pass identification, replacement for typeid 49 WebAssemblyLateEHPrepare() : MachineFunctionPass(ID) {} 50 }; 51 } // end anonymous namespace 52 53 char WebAssemblyLateEHPrepare::ID = 0; 54 INITIALIZE_PASS(WebAssemblyLateEHPrepare, DEBUG_TYPE, 55 "WebAssembly Late Exception Preparation", false, false) 56 57 FunctionPass *llvm::createWebAssemblyLateEHPrepare() { 58 return new WebAssemblyLateEHPrepare(); 59 } 60 61 // Returns the nearest EH pad that dominates this instruction. This does not use 62 // dominator analysis; it just does BFS on its predecessors until arriving at an 63 // EH pad. This assumes valid EH scopes so the first EH pad it arrives in all 64 // possible search paths should be the same. 65 // Returns nullptr in case it does not find any EH pad in the search, or finds 66 // multiple different EH pads. 67 MachineBasicBlock * 68 WebAssemblyLateEHPrepare::getMatchingEHPad(MachineInstr *MI) { 69 MachineFunction *MF = MI->getParent()->getParent(); 70 SmallVector<MachineBasicBlock *, 2> WL; 71 SmallPtrSet<MachineBasicBlock *, 2> Visited; 72 WL.push_back(MI->getParent()); 73 MachineBasicBlock *EHPad = nullptr; 74 while (!WL.empty()) { 75 MachineBasicBlock *MBB = WL.pop_back_val(); 76 if (Visited.count(MBB)) 77 continue; 78 Visited.insert(MBB); 79 if (MBB->isEHPad()) { 80 if (EHPad && EHPad != MBB) 81 return nullptr; 82 EHPad = MBB; 83 continue; 84 } 85 if (MBB == &MF->front()) 86 return nullptr; 87 for (auto *Pred : MBB->predecessors()) 88 if (!CatchRetBBs.count(Pred)) // We don't go into child scopes 89 WL.push_back(Pred); 90 } 91 return EHPad; 92 } 93 94 // Erase the specified BBs if the BB does not have any remaining predecessors, 95 // and also all its dead children. 96 template <typename Container> 97 static void eraseDeadBBsAndChildren(const Container &MBBs) { 98 SmallVector<MachineBasicBlock *, 8> WL(MBBs.begin(), MBBs.end()); 99 SmallPtrSet<MachineBasicBlock *, 8> Deleted; 100 while (!WL.empty()) { 101 MachineBasicBlock *MBB = WL.pop_back_val(); 102 if (Deleted.count(MBB) || !MBB->pred_empty()) 103 continue; 104 SmallVector<MachineBasicBlock *, 4> Succs(MBB->successors()); 105 WL.append(MBB->succ_begin(), MBB->succ_end()); 106 for (auto *Succ : Succs) 107 MBB->removeSuccessor(Succ); 108 // To prevent deleting the same BB multiple times, which can happen when 109 // 'MBBs' contain both a parent and a child 110 Deleted.insert(MBB); 111 MBB->eraseFromParent(); 112 } 113 } 114 115 bool WebAssemblyLateEHPrepare::runOnMachineFunction(MachineFunction &MF) { 116 LLVM_DEBUG(dbgs() << "********** Late EH Prepare **********\n" 117 "********** Function: " 118 << MF.getName() << '\n'); 119 120 if (MF.getTarget().getMCAsmInfo()->getExceptionHandlingType() != 121 ExceptionHandling::Wasm) 122 return false; 123 124 bool Changed = false; 125 if (MF.getFunction().hasPersonalityFn()) { 126 Changed |= removeUnreachableEHPads(MF); 127 recordCatchRetBBs(MF); 128 Changed |= hoistCatches(MF); 129 Changed |= addCatchAlls(MF); 130 Changed |= replaceFuncletReturns(MF); 131 } 132 Changed |= removeUnnecessaryUnreachables(MF); 133 if (MF.getFunction().hasPersonalityFn()) 134 Changed |= restoreStackPointer(MF); 135 return Changed; 136 } 137 138 // Remove unreachable EH pads and its children. If they remain, CFG 139 // stackification can be tricky. 140 bool WebAssemblyLateEHPrepare::removeUnreachableEHPads(MachineFunction &MF) { 141 SmallVector<MachineBasicBlock *, 4> ToDelete; 142 for (auto &MBB : MF) 143 if (MBB.isEHPad() && MBB.pred_empty()) 144 ToDelete.push_back(&MBB); 145 eraseDeadBBsAndChildren(ToDelete); 146 return !ToDelete.empty(); 147 } 148 149 // Record which BB ends with catchret instruction, because this will be replaced 150 // with 'br's later. This set of catchret BBs is necessary in 'getMatchingEHPad' 151 // function. 152 void WebAssemblyLateEHPrepare::recordCatchRetBBs(MachineFunction &MF) { 153 CatchRetBBs.clear(); 154 for (auto &MBB : MF) { 155 auto Pos = MBB.getFirstTerminator(); 156 if (Pos == MBB.end()) 157 continue; 158 MachineInstr *TI = &*Pos; 159 if (TI->getOpcode() == WebAssembly::CATCHRET) 160 CatchRetBBs.insert(&MBB); 161 } 162 } 163 164 // Hoist catch instructions to the beginning of their matching EH pad BBs in 165 // case, 166 // (1) catch instruction is not the first instruction in EH pad. 167 // ehpad: 168 // some_other_instruction 169 // ... 170 // %exn = catch 0 171 // (2) catch instruction is in a non-EH pad BB. For example, 172 // ehpad: 173 // br bb0 174 // bb0: 175 // %exn = catch 0 176 bool WebAssemblyLateEHPrepare::hoistCatches(MachineFunction &MF) { 177 bool Changed = false; 178 SmallVector<MachineInstr *, 16> Catches; 179 for (auto &MBB : MF) 180 for (auto &MI : MBB) 181 if (WebAssembly::isCatch(MI.getOpcode())) 182 Catches.push_back(&MI); 183 184 for (auto *Catch : Catches) { 185 MachineBasicBlock *EHPad = getMatchingEHPad(Catch); 186 assert(EHPad && "No matching EH pad for catch"); 187 auto InsertPos = EHPad->begin(); 188 // Skip EH_LABELs in the beginning of an EH pad if present. We don't use 189 // these labels at the moment, but other targets also seem to have an 190 // EH_LABEL instruction in the beginning of an EH pad. 191 while (InsertPos != EHPad->end() && InsertPos->isEHLabel()) 192 InsertPos++; 193 if (InsertPos == Catch) 194 continue; 195 Changed = true; 196 EHPad->insert(InsertPos, Catch->removeFromParent()); 197 } 198 return Changed; 199 } 200 201 // Add catch_all to beginning of cleanup pads. 202 bool WebAssemblyLateEHPrepare::addCatchAlls(MachineFunction &MF) { 203 bool Changed = false; 204 const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo(); 205 206 for (auto &MBB : MF) { 207 if (!MBB.isEHPad()) 208 continue; 209 auto InsertPos = MBB.begin(); 210 // Skip EH_LABELs in the beginning of an EH pad if present. 211 while (InsertPos != MBB.end() && InsertPos->isEHLabel()) 212 InsertPos++; 213 // This runs after hoistCatches(), so we assume that if there is a catch, 214 // that should be the first non-EH-label instruction in an EH pad. 215 if (InsertPos == MBB.end() || 216 !WebAssembly::isCatch(InsertPos->getOpcode())) { 217 Changed = true; 218 BuildMI(MBB, InsertPos, 219 InsertPos == MBB.end() ? DebugLoc() : InsertPos->getDebugLoc(), 220 TII.get(WebAssembly::CATCH_ALL)); 221 } 222 } 223 return Changed; 224 } 225 226 // Replace pseudo-instructions catchret and cleanupret with br and rethrow 227 // respectively. 228 bool WebAssemblyLateEHPrepare::replaceFuncletReturns(MachineFunction &MF) { 229 bool Changed = false; 230 const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo(); 231 232 for (auto &MBB : MF) { 233 auto Pos = MBB.getFirstTerminator(); 234 if (Pos == MBB.end()) 235 continue; 236 MachineInstr *TI = &*Pos; 237 238 switch (TI->getOpcode()) { 239 case WebAssembly::CATCHRET: { 240 // Replace a catchret with a branch 241 MachineBasicBlock *TBB = TI->getOperand(0).getMBB(); 242 if (!MBB.isLayoutSuccessor(TBB)) 243 BuildMI(MBB, TI, TI->getDebugLoc(), TII.get(WebAssembly::BR)) 244 .addMBB(TBB); 245 TI->eraseFromParent(); 246 Changed = true; 247 break; 248 } 249 case WebAssembly::CLEANUPRET: { 250 // Replace a cleanupret with a rethrow. For C++ support, currently 251 // rethrow's immediate argument is always 0 (= the latest exception). 252 BuildMI(MBB, TI, TI->getDebugLoc(), TII.get(WebAssembly::RETHROW)) 253 .addImm(0); 254 TI->eraseFromParent(); 255 Changed = true; 256 break; 257 } 258 } 259 } 260 return Changed; 261 } 262 263 // Remove unnecessary unreachables after a throw or rethrow. 264 bool WebAssemblyLateEHPrepare::removeUnnecessaryUnreachables( 265 MachineFunction &MF) { 266 bool Changed = false; 267 for (auto &MBB : MF) { 268 for (auto &MI : MBB) { 269 if (MI.getOpcode() != WebAssembly::THROW && 270 MI.getOpcode() != WebAssembly::RETHROW) 271 continue; 272 Changed = true; 273 274 // The instruction after the throw should be an unreachable or a branch to 275 // another BB that should eventually lead to an unreachable. Delete it 276 // because throw itself is a terminator, and also delete successors if 277 // any. 278 MBB.erase(std::next(MI.getIterator()), MBB.end()); 279 SmallVector<MachineBasicBlock *, 8> Succs(MBB.successors()); 280 for (auto *Succ : Succs) 281 if (!Succ->isEHPad()) 282 MBB.removeSuccessor(Succ); 283 eraseDeadBBsAndChildren(Succs); 284 } 285 } 286 287 return Changed; 288 } 289 290 // After the stack is unwound due to a thrown exception, the __stack_pointer 291 // global can point to an invalid address. This inserts instructions that 292 // restore __stack_pointer global. 293 bool WebAssemblyLateEHPrepare::restoreStackPointer(MachineFunction &MF) { 294 const auto *FrameLowering = static_cast<const WebAssemblyFrameLowering *>( 295 MF.getSubtarget().getFrameLowering()); 296 if (!FrameLowering->needsPrologForEH(MF)) 297 return false; 298 bool Changed = false; 299 300 for (auto &MBB : MF) { 301 if (!MBB.isEHPad()) 302 continue; 303 Changed = true; 304 305 // Insert __stack_pointer restoring instructions at the beginning of each EH 306 // pad, after the catch instruction. Here it is safe to assume that SP32 307 // holds the latest value of __stack_pointer, because the only exception for 308 // this case is when a function uses the red zone, but that only happens 309 // with leaf functions, and we don't restore __stack_pointer in leaf 310 // functions anyway. 311 auto InsertPos = MBB.begin(); 312 // Skip EH_LABELs in the beginning of an EH pad if present. 313 while (InsertPos != MBB.end() && InsertPos->isEHLabel()) 314 InsertPos++; 315 assert(InsertPos != MBB.end() && 316 WebAssembly::isCatch(InsertPos->getOpcode()) && 317 "catch/catch_all should be present in every EH pad at this point"); 318 ++InsertPos; // Skip the catch instruction 319 FrameLowering->writeSPToGlobal(FrameLowering->getSPReg(MF), MF, MBB, 320 InsertPos, MBB.begin()->getDebugLoc()); 321 } 322 return Changed; 323 } 324