#include "llvm/CodeGen/MachineBasicBlock.h"
#include "llvm/CodeGen/Passes.h"
#include "llvm/CodeGen/WasmEHFuncInfo.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/IntrinsicsWebAssembly.h"
#include "llvm/InitializePasses.h"
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
using namespace llvm;
#define DEBUG_TYPE "wasmehprepare"
namespace {
class WasmEHPrepare : public FunctionPass {
Type *LPadContextTy = nullptr; GlobalVariable *LPadContextGV = nullptr;
Value *LPadIndexField = nullptr; Value *LSDAField = nullptr; Value *SelectorField = nullptr;
Function *ThrowF = nullptr; Function *LPadIndexF = nullptr; Function *LSDAF = nullptr; Function *GetExnF = nullptr; Function *CatchF = nullptr; Function *GetSelectorF = nullptr; FunctionCallee CallPersonalityF =
nullptr;
bool prepareThrows(Function &F);
bool prepareEHPads(Function &F);
void prepareEHPad(BasicBlock *BB, bool NeedPersonality, unsigned Index = 0);
public:
static char ID;
WasmEHPrepare() : FunctionPass(ID) {}
bool doInitialization(Module &M) override;
bool runOnFunction(Function &F) override;
StringRef getPassName() const override {
return "WebAssembly Exception handling preparation";
}
};
}
char WasmEHPrepare::ID = 0;
INITIALIZE_PASS_BEGIN(WasmEHPrepare, DEBUG_TYPE,
"Prepare WebAssembly exceptions", false, false)
INITIALIZE_PASS_END(WasmEHPrepare, DEBUG_TYPE, "Prepare WebAssembly exceptions",
false, false)
FunctionPass *llvm::createWasmEHPass() { return new WasmEHPrepare(); }
bool WasmEHPrepare::doInitialization(Module &M) {
IRBuilder<> IRB(M.getContext());
LPadContextTy = StructType::get(IRB.getInt32Ty(), IRB.getInt8PtrTy(), IRB.getInt32Ty() );
return false;
}
template <typename Container>
static void eraseDeadBBsAndChildren(const Container &BBs) {
SmallVector<BasicBlock *, 8> WL(BBs.begin(), BBs.end());
while (!WL.empty()) {
auto *BB = WL.pop_back_val();
if (!pred_empty(BB))
continue;
WL.append(succ_begin(BB), succ_end(BB));
DeleteDeadBlock(BB);
}
}
bool WasmEHPrepare::runOnFunction(Function &F) {
bool Changed = false;
Changed |= prepareThrows(F);
Changed |= prepareEHPads(F);
return Changed;
}
bool WasmEHPrepare::prepareThrows(Function &F) {
Module &M = *F.getParent();
IRBuilder<> IRB(F.getContext());
bool Changed = false;
ThrowF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_throw);
for (User *U : ThrowF->users()) {
auto *ThrowI = cast<CallInst>(U);
if (ThrowI->getFunction() != &F)
continue;
Changed = true;
auto *BB = ThrowI->getParent();
SmallVector<BasicBlock *, 4> Succs(successors(BB));
auto &InstList = BB->getInstList();
InstList.erase(std::next(BasicBlock::iterator(ThrowI)), InstList.end());
IRB.SetInsertPoint(BB);
IRB.CreateUnreachable();
eraseDeadBBsAndChildren(Succs);
}
return Changed;
}
bool WasmEHPrepare::prepareEHPads(Function &F) {
Module &M = *F.getParent();
IRBuilder<> IRB(F.getContext());
SmallVector<BasicBlock *, 16> CatchPads;
SmallVector<BasicBlock *, 16> CleanupPads;
for (BasicBlock &BB : F) {
if (!BB.isEHPad())
continue;
auto *Pad = BB.getFirstNonPHI();
if (isa<CatchPadInst>(Pad))
CatchPads.push_back(&BB);
else if (isa<CleanupPadInst>(Pad))
CleanupPads.push_back(&BB);
}
if (CatchPads.empty() && CleanupPads.empty())
return false;
assert(F.hasPersonalityFn() && "Personality function not found");
LPadContextGV = cast<GlobalVariable>(
M.getOrInsertGlobal("__wasm_lpad_context", LPadContextTy));
LPadContextGV->setThreadLocalMode(GlobalValue::GeneralDynamicTLSModel);
LPadIndexField = IRB.CreateConstGEP2_32(LPadContextTy, LPadContextGV, 0, 0,
"lpad_index_gep");
LSDAField =
IRB.CreateConstGEP2_32(LPadContextTy, LPadContextGV, 0, 1, "lsda_gep");
SelectorField = IRB.CreateConstGEP2_32(LPadContextTy, LPadContextGV, 0, 2,
"selector_gep");
LPadIndexF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_landingpad_index);
LSDAF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_lsda);
GetExnF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_get_exception);
GetSelectorF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_get_ehselector);
CatchF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_catch);
CallPersonalityF = M.getOrInsertFunction(
"_Unwind_CallPersonality", IRB.getInt32Ty(), IRB.getInt8PtrTy());
if (Function *F = dyn_cast<Function>(CallPersonalityF.getCallee()))
F->setDoesNotThrow();
unsigned Index = 0;
for (auto *BB : CatchPads) {
auto *CPI = cast<CatchPadInst>(BB->getFirstNonPHI());
if (CPI->getNumArgOperands() == 1 &&
cast<Constant>(CPI->getArgOperand(0))->isNullValue())
prepareEHPad(BB, false);
else
prepareEHPad(BB, true, Index++);
}
for (auto *BB : CleanupPads)
prepareEHPad(BB, false);
return true;
}
void WasmEHPrepare::prepareEHPad(BasicBlock *BB, bool NeedPersonality,
unsigned Index) {
assert(BB->isEHPad() && "BB is not an EHPad!");
IRBuilder<> IRB(BB->getContext());
IRB.SetInsertPoint(&*BB->getFirstInsertionPt());
auto *FPI = cast<FuncletPadInst>(BB->getFirstNonPHI());
Instruction *GetExnCI = nullptr, *GetSelectorCI = nullptr;
for (auto &U : FPI->uses()) {
if (auto *CI = dyn_cast<CallInst>(U.getUser())) {
if (CI->getCalledOperand() == GetExnF)
GetExnCI = CI;
if (CI->getCalledOperand() == GetSelectorF)
GetSelectorCI = CI;
}
}
if (!GetExnCI) {
assert(!GetSelectorCI &&
"wasm.get.ehselector() cannot exist w/o wasm.get.exception()");
return;
}
Instruction *CatchCI =
IRB.CreateCall(CatchF, {IRB.getInt32(WebAssembly::CPP_EXCEPTION)}, "exn");
GetExnCI->replaceAllUsesWith(CatchCI);
GetExnCI->eraseFromParent();
if (!NeedPersonality) {
if (GetSelectorCI) {
assert(GetSelectorCI->use_empty() &&
"wasm.get.ehselector() still has uses!");
GetSelectorCI->eraseFromParent();
}
return;
}
IRB.SetInsertPoint(CatchCI->getNextNode());
IRB.CreateCall(LPadIndexF, {FPI, IRB.getInt32(Index)});
IRB.CreateStore(IRB.getInt32(Index), LPadIndexField);
auto *CPI = cast<CatchPadInst>(FPI);
IRB.CreateStore(IRB.CreateCall(LSDAF), LSDAField);
CallInst *PersCI = IRB.CreateCall(CallPersonalityF, CatchCI,
OperandBundleDef("funclet", CPI));
PersCI->setDoesNotThrow();
Instruction *Selector =
IRB.CreateLoad(IRB.getInt32Ty(), SelectorField, "selector");
assert(GetSelectorCI && "wasm.get.ehselector() call does not exist");
GetSelectorCI->replaceAllUsesWith(Selector);
GetSelectorCI->eraseFromParent();
}
void llvm::calculateWasmEHInfo(const Function *F, WasmEHFuncInfo &EHInfo) {
for (const auto &BB : *F) {
if (!BB.isEHPad())
continue;
const Instruction *Pad = BB.getFirstNonPHI();
if (const auto *CatchPad = dyn_cast<CatchPadInst>(Pad)) {
const auto *UnwindBB = CatchPad->getCatchSwitch()->getUnwindDest();
if (!UnwindBB)
continue;
const Instruction *UnwindPad = UnwindBB->getFirstNonPHI();
if (const auto *CatchSwitch = dyn_cast<CatchSwitchInst>(UnwindPad))
EHInfo.setUnwindDest(&BB, *CatchSwitch->handlers().begin());
else EHInfo.setUnwindDest(&BB, UnwindBB);
}
}
}