Path: blob/main/contrib/llvm-project/llvm/lib/Target/WebAssembly/WebAssemblyLateEHPrepare.cpp
35266 views
//=== WebAssemblyLateEHPrepare.cpp - WebAssembly Exception Preparation -===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7///8/// \file9/// \brief Does various transformations for exception handling.10///11//===----------------------------------------------------------------------===//1213#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"14#include "WebAssembly.h"15#include "WebAssemblySubtarget.h"16#include "WebAssemblyUtilities.h"17#include "llvm/ADT/SmallPtrSet.h"18#include "llvm/CodeGen/MachineFunctionPass.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"24using namespace llvm;2526#define DEBUG_TYPE "wasm-late-eh-prepare"2728namespace {29class WebAssemblyLateEHPrepare final : public MachineFunctionPass {30StringRef getPassName() const override {31return "WebAssembly Late Prepare Exception";32}3334bool runOnMachineFunction(MachineFunction &MF) override;35bool removeUnreachableEHPads(MachineFunction &MF);36void recordCatchRetBBs(MachineFunction &MF);37bool hoistCatches(MachineFunction &MF);38bool addCatchAlls(MachineFunction &MF);39bool replaceFuncletReturns(MachineFunction &MF);40bool removeUnnecessaryUnreachables(MachineFunction &MF);41bool restoreStackPointer(MachineFunction &MF);4243MachineBasicBlock *getMatchingEHPad(MachineInstr *MI);44SmallPtrSet<MachineBasicBlock *, 8> CatchRetBBs;4546public:47static char ID; // Pass identification, replacement for typeid48WebAssemblyLateEHPrepare() : MachineFunctionPass(ID) {}49};50} // end anonymous namespace5152char WebAssemblyLateEHPrepare::ID = 0;53INITIALIZE_PASS(WebAssemblyLateEHPrepare, DEBUG_TYPE,54"WebAssembly Late Exception Preparation", false, false)5556FunctionPass *llvm::createWebAssemblyLateEHPrepare() {57return new WebAssemblyLateEHPrepare();58}5960// Returns the nearest EH pad that dominates this instruction. This does not use61// dominator analysis; it just does BFS on its predecessors until arriving at an62// EH pad. This assumes valid EH scopes so the first EH pad it arrives in all63// possible search paths should be the same.64// Returns nullptr in case it does not find any EH pad in the search, or finds65// multiple different EH pads.66MachineBasicBlock *67WebAssemblyLateEHPrepare::getMatchingEHPad(MachineInstr *MI) {68MachineFunction *MF = MI->getParent()->getParent();69SmallVector<MachineBasicBlock *, 2> WL;70SmallPtrSet<MachineBasicBlock *, 2> Visited;71WL.push_back(MI->getParent());72MachineBasicBlock *EHPad = nullptr;73while (!WL.empty()) {74MachineBasicBlock *MBB = WL.pop_back_val();75if (!Visited.insert(MBB).second)76continue;77if (MBB->isEHPad()) {78if (EHPad && EHPad != MBB)79return nullptr;80EHPad = MBB;81continue;82}83if (MBB == &MF->front())84return nullptr;85for (auto *Pred : MBB->predecessors())86if (!CatchRetBBs.count(Pred)) // We don't go into child scopes87WL.push_back(Pred);88}89return EHPad;90}9192// Erase the specified BBs if the BB does not have any remaining predecessors,93// and also all its dead children.94template <typename Container>95static void eraseDeadBBsAndChildren(const Container &MBBs) {96SmallVector<MachineBasicBlock *, 8> WL(MBBs.begin(), MBBs.end());97SmallPtrSet<MachineBasicBlock *, 8> Deleted;98while (!WL.empty()) {99MachineBasicBlock *MBB = WL.pop_back_val();100if (Deleted.count(MBB) || !MBB->pred_empty())101continue;102SmallVector<MachineBasicBlock *, 4> Succs(MBB->successors());103WL.append(MBB->succ_begin(), MBB->succ_end());104for (auto *Succ : Succs)105MBB->removeSuccessor(Succ);106// To prevent deleting the same BB multiple times, which can happen when107// 'MBBs' contain both a parent and a child108Deleted.insert(MBB);109MBB->eraseFromParent();110}111}112113bool WebAssemblyLateEHPrepare::runOnMachineFunction(MachineFunction &MF) {114LLVM_DEBUG(dbgs() << "********** Late EH Prepare **********\n"115"********** Function: "116<< MF.getName() << '\n');117118if (MF.getTarget().getMCAsmInfo()->getExceptionHandlingType() !=119ExceptionHandling::Wasm)120return false;121122bool Changed = false;123if (MF.getFunction().hasPersonalityFn()) {124Changed |= removeUnreachableEHPads(MF);125recordCatchRetBBs(MF);126Changed |= hoistCatches(MF);127Changed |= addCatchAlls(MF);128Changed |= replaceFuncletReturns(MF);129}130Changed |= removeUnnecessaryUnreachables(MF);131if (MF.getFunction().hasPersonalityFn())132Changed |= restoreStackPointer(MF);133return Changed;134}135136// Remove unreachable EH pads and its children. If they remain, CFG137// stackification can be tricky.138bool WebAssemblyLateEHPrepare::removeUnreachableEHPads(MachineFunction &MF) {139SmallVector<MachineBasicBlock *, 4> ToDelete;140for (auto &MBB : MF)141if (MBB.isEHPad() && MBB.pred_empty())142ToDelete.push_back(&MBB);143eraseDeadBBsAndChildren(ToDelete);144return !ToDelete.empty();145}146147// Record which BB ends with catchret instruction, because this will be replaced148// with 'br's later. This set of catchret BBs is necessary in 'getMatchingEHPad'149// function.150void WebAssemblyLateEHPrepare::recordCatchRetBBs(MachineFunction &MF) {151CatchRetBBs.clear();152for (auto &MBB : MF) {153auto Pos = MBB.getFirstTerminator();154if (Pos == MBB.end())155continue;156MachineInstr *TI = &*Pos;157if (TI->getOpcode() == WebAssembly::CATCHRET)158CatchRetBBs.insert(&MBB);159}160}161162// Hoist catch instructions to the beginning of their matching EH pad BBs in163// case,164// (1) catch instruction is not the first instruction in EH pad.165// ehpad:166// some_other_instruction167// ...168// %exn = catch 0169// (2) catch instruction is in a non-EH pad BB. For example,170// ehpad:171// br bb0172// bb0:173// %exn = catch 0174bool WebAssemblyLateEHPrepare::hoistCatches(MachineFunction &MF) {175bool Changed = false;176SmallVector<MachineInstr *, 16> Catches;177for (auto &MBB : MF)178for (auto &MI : MBB)179if (WebAssembly::isCatch(MI.getOpcode()))180Catches.push_back(&MI);181182for (auto *Catch : Catches) {183MachineBasicBlock *EHPad = getMatchingEHPad(Catch);184assert(EHPad && "No matching EH pad for catch");185auto InsertPos = EHPad->begin();186// Skip EH_LABELs in the beginning of an EH pad if present. We don't use187// these labels at the moment, but other targets also seem to have an188// EH_LABEL instruction in the beginning of an EH pad.189while (InsertPos != EHPad->end() && InsertPos->isEHLabel())190InsertPos++;191if (InsertPos == Catch)192continue;193Changed = true;194EHPad->insert(InsertPos, Catch->removeFromParent());195}196return Changed;197}198199// Add catch_all to beginning of cleanup pads.200bool WebAssemblyLateEHPrepare::addCatchAlls(MachineFunction &MF) {201bool Changed = false;202const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();203204for (auto &MBB : MF) {205if (!MBB.isEHPad())206continue;207auto InsertPos = MBB.begin();208// Skip EH_LABELs in the beginning of an EH pad if present.209while (InsertPos != MBB.end() && InsertPos->isEHLabel())210InsertPos++;211// This runs after hoistCatches(), so we assume that if there is a catch,212// that should be the first non-EH-label instruction in an EH pad.213if (InsertPos == MBB.end() ||214!WebAssembly::isCatch(InsertPos->getOpcode())) {215Changed = true;216BuildMI(MBB, InsertPos,217InsertPos == MBB.end() ? DebugLoc() : InsertPos->getDebugLoc(),218TII.get(WebAssembly::CATCH_ALL));219}220}221return Changed;222}223224// Replace pseudo-instructions catchret and cleanupret with br and rethrow225// respectively.226bool WebAssemblyLateEHPrepare::replaceFuncletReturns(MachineFunction &MF) {227bool Changed = false;228const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();229230for (auto &MBB : MF) {231auto Pos = MBB.getFirstTerminator();232if (Pos == MBB.end())233continue;234MachineInstr *TI = &*Pos;235236switch (TI->getOpcode()) {237case WebAssembly::CATCHRET: {238// Replace a catchret with a branch239MachineBasicBlock *TBB = TI->getOperand(0).getMBB();240if (!MBB.isLayoutSuccessor(TBB))241BuildMI(MBB, TI, TI->getDebugLoc(), TII.get(WebAssembly::BR))242.addMBB(TBB);243TI->eraseFromParent();244Changed = true;245break;246}247case WebAssembly::RETHROW:248// These RETHROWs here were lowered from llvm.wasm.rethrow() intrinsics,249// generated in Clang for when an exception is not caught by the given250// type (e.g. catch (int)).251//252// RETHROW's BB argument is the EH pad where the exception to rethrow has253// been caught. (Until this point, RETHROW has just a '0' as a placeholder254// argument.) For these llvm.wasm.rethrow()s, we can safely assume the255// exception comes from the nearest dominating EH pad, because catch.start256// EH pad is structured like this:257//258// catch.start:259// catchpad ...260// %matches = compare ehselector with typeid261// br i1 %matches, label %catch, label %rethrow262//263// rethrow:264// ;; rethrows the exception caught in 'catch.start'265// call @llvm.wasm.rethrow()266TI->removeOperand(0);267TI->addOperand(MachineOperand::CreateMBB(getMatchingEHPad(TI)));268Changed = true;269break;270case WebAssembly::CLEANUPRET: {271// CLEANUPRETs have the EH pad BB the exception to rethrow has been caught272// as an argument. Use it and change the instruction opcode to 'RETHROW'273// to make rethrowing instructions consistent.274//275// This is because we cannot safely assume that it is always the nearest276// dominating EH pad, in case there are code transformations such as277// inlining.278BuildMI(MBB, TI, TI->getDebugLoc(), TII.get(WebAssembly::RETHROW))279.addMBB(TI->getOperand(0).getMBB());280TI->eraseFromParent();281Changed = true;282break;283}284}285}286return Changed;287}288289// Remove unnecessary unreachables after a throw or rethrow.290bool WebAssemblyLateEHPrepare::removeUnnecessaryUnreachables(291MachineFunction &MF) {292bool Changed = false;293for (auto &MBB : MF) {294for (auto &MI : MBB) {295if (MI.getOpcode() != WebAssembly::THROW &&296MI.getOpcode() != WebAssembly::RETHROW)297continue;298Changed = true;299300// The instruction after the throw should be an unreachable or a branch to301// another BB that should eventually lead to an unreachable. Delete it302// because throw itself is a terminator, and also delete successors if303// any.304MBB.erase(std::next(MI.getIterator()), MBB.end());305SmallVector<MachineBasicBlock *, 8> Succs(MBB.successors());306for (auto *Succ : Succs)307if (!Succ->isEHPad())308MBB.removeSuccessor(Succ);309eraseDeadBBsAndChildren(Succs);310}311}312313return Changed;314}315316// After the stack is unwound due to a thrown exception, the __stack_pointer317// global can point to an invalid address. This inserts instructions that318// restore __stack_pointer global.319bool WebAssemblyLateEHPrepare::restoreStackPointer(MachineFunction &MF) {320const auto *FrameLowering = static_cast<const WebAssemblyFrameLowering *>(321MF.getSubtarget().getFrameLowering());322if (!FrameLowering->needsPrologForEH(MF))323return false;324bool Changed = false;325326for (auto &MBB : MF) {327if (!MBB.isEHPad())328continue;329Changed = true;330331// Insert __stack_pointer restoring instructions at the beginning of each EH332// pad, after the catch instruction. Here it is safe to assume that SP32333// holds the latest value of __stack_pointer, because the only exception for334// this case is when a function uses the red zone, but that only happens335// with leaf functions, and we don't restore __stack_pointer in leaf336// functions anyway.337auto InsertPos = MBB.begin();338// Skip EH_LABELs in the beginning of an EH pad if present.339while (InsertPos != MBB.end() && InsertPos->isEHLabel())340InsertPos++;341assert(InsertPos != MBB.end() &&342WebAssembly::isCatch(InsertPos->getOpcode()) &&343"catch/catch_all should be present in every EH pad at this point");344++InsertPos; // Skip the catch instruction345FrameLowering->writeSPToGlobal(FrameLowering->getSPReg(MF), MF, MBB,346InsertPos, MBB.begin()->getDebugLoc());347}348return Changed;349}350351352