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