Index: include/llvm/CodeGen/Passes.h =================================================================== --- include/llvm/CodeGen/Passes.h +++ include/llvm/CodeGen/Passes.h @@ -322,7 +322,7 @@ /// createDwarfEHPass - This pass mulches exception handling code into a form /// adapted to code generation. Required if using dwarf exception handling. - FunctionPass *createDwarfEHPass(); + FunctionPass *createDwarfEHPass(bool PruneUnreachableResumes = true); /// createWinEHPass - Prepares personality functions used by MSVC on Windows, /// in addition to the Itanium LSDA based personalities. @@ -333,6 +333,10 @@ /// FunctionPass *createSjLjEHPreparePass(); + /// createWasmEHPreparePass - This pass adapts exception handling code to use + /// WebAssembly's exception handling scheme. + FunctionPass *createWasmEHPreparePass(); + /// LocalStackSlotAllocation - This pass assigns local frame indices to stack /// slots relative to one another and allocates base registers to access them /// when it is estimated by the target to be out of range of normal frame Index: include/llvm/IR/IntrinsicsWebAssembly.td =================================================================== --- include/llvm/IR/IntrinsicsWebAssembly.td +++ include/llvm/IR/IntrinsicsWebAssembly.td @@ -35,4 +35,18 @@ def int_wasm_throw: Intrinsic<[], [llvm_i32_ty, llvm_ptr_ty], [Throws]>; def int_wasm_rethrow: Intrinsic<[], [], [Throws]>; +// This intrinsic has a functionality of wasm catch / if_except / else / end +// instructions combined. Wasm catch instruction returns a caught wasm +// exception, which is of wasm except_ref type. Wasm if_except instruction is +// used to extract a LLVM IR pointer from it. A call to this instruction is +// lowered into a sequence of catch / if_except / else / end in the backend. +def int_wasm_catch_extract : Intrinsic<[llvm_ptr_ty], [], [IntrHasSideEffects]>; + +// WebAssembly EH must maintain the landingpads in the order assigned to them +// by WasmEHPrepare pass to generate landingpad table in EHStreamer. This is +// used in order to give them the indices in WasmEHPrepare. +def int_wasm_eh_landingpad_index: Intrinsic<[], [llvm_i32_ty], [IntrNoMem]>; + +// Returns LSDA address of the current function. +def int_wasm_eh_lsda : Intrinsic<[llvm_ptr_ty], [], [IntrNoMem]>; } Index: include/llvm/InitializePasses.h =================================================================== --- include/llvm/InitializePasses.h +++ include/llvm/InitializePasses.h @@ -382,6 +382,7 @@ void initializeVerifierLegacyPassPass(PassRegistry&); void initializeVirtRegMapPass(PassRegistry&); void initializeVirtRegRewriterPass(PassRegistry&); +void initializeWasmEHPreparePass(PassRegistry&); void initializeWholeProgramDevirtPass(PassRegistry&); void initializeWinEHPreparePass(PassRegistry&); void initializeWriteBitcodePassPass(PassRegistry&); Index: lib/CodeGen/CMakeLists.txt =================================================================== --- lib/CodeGen/CMakeLists.txt +++ lib/CodeGen/CMakeLists.txt @@ -156,6 +156,7 @@ TwoAddressInstructionPass.cpp UnreachableBlockElim.cpp VirtRegMap.cpp + WasmEHPrepare.cpp WinEHPrepare.cpp XRayInstrumentation.cpp Index: lib/CodeGen/CodeGen.cpp =================================================================== --- lib/CodeGen/CodeGen.cpp +++ lib/CodeGen/CodeGen.cpp @@ -100,6 +100,7 @@ initializeUnreachableMachineBlockElimPass(Registry); initializeVirtRegMapPass(Registry); initializeVirtRegRewriterPass(Registry); + initializeWasmEHPreparePass(Registry); initializeWinEHPreparePass(Registry); initializeXRayInstrumentationPass(Registry); initializeMIRCanonicalizerPass(Registry); Index: lib/CodeGen/DwarfEHPrepare.cpp =================================================================== --- lib/CodeGen/DwarfEHPrepare.cpp +++ lib/CodeGen/DwarfEHPrepare.cpp @@ -50,6 +50,7 @@ DominatorTree *DT = nullptr; const TargetLowering *TLI = nullptr; + bool PruneUnreachableResumes; bool InsertUnwindResumeCalls(Function &Fn); Value *GetExceptionObject(ResumeInst *RI); @@ -61,7 +62,8 @@ public: static char ID; // Pass identification, replacement for typeid. - DwarfEHPrepare() : FunctionPass(ID) {} + DwarfEHPrepare(bool PruneUnreachableResumes = true) + : FunctionPass(ID), PruneUnreachableResumes(PruneUnreachableResumes) {} bool runOnFunction(Function &Fn) override; @@ -89,7 +91,9 @@ INITIALIZE_PASS_END(DwarfEHPrepare, DEBUG_TYPE, "Prepare DWARF exceptions", false, false) -FunctionPass *llvm::createDwarfEHPass() { return new DwarfEHPrepare(); } +FunctionPass *llvm::createDwarfEHPass(bool PruneUnreachableResumes) { + return new DwarfEHPrepare(PruneUnreachableResumes); +} void DwarfEHPrepare::getAnalysisUsage(AnalysisUsage &AU) const { AU.addRequired(); @@ -202,9 +206,12 @@ LLVMContext &Ctx = Fn.getContext(); - size_t ResumesLeft = pruneUnreachableResumes(Fn, Resumes, CleanupLPads); - if (ResumesLeft == 0) - return true; // We pruned them all. + size_t ResumesLeft = Resumes.size(); + if (PruneUnreachableResumes) { + ResumesLeft = pruneUnreachableResumes(Fn, Resumes, CleanupLPads); + if (ResumesLeft == 0) + return true; // We pruned them all. + } // Find the rewind function if we didn't already. if (!RewindFunction) { Index: lib/CodeGen/TargetPassConfig.cpp =================================================================== --- lib/CodeGen/TargetPassConfig.cpp +++ lib/CodeGen/TargetPassConfig.cpp @@ -654,7 +654,10 @@ addPass(createDwarfEHPass()); break; case ExceptionHandling::Wasm: - // TODO to prevent warning + addPass(createWasmEHPreparePass()); + // We shouldn't prune unreachable resumes in Wasm EH, because unwinding + // stops at every call frame with a landing pad. + addPass(createDwarfEHPass(false)); break; case ExceptionHandling::None: addPass(createLowerInvokePass()); Index: lib/CodeGen/WasmEHPrepare.cpp =================================================================== --- /dev/null +++ lib/CodeGen/WasmEHPrepare.cpp @@ -0,0 +1,333 @@ +//===-- WasmEHPrepare - Prepare excepton handling for WebAssembly --------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This transformation is designed for use by code generators which use +// WebAssembly exception handling scheme. +// +// * This pass does the following things: +// +// 1. Insert a call to wasm.eh.landingpad.index() before every invoke +// instruction. Each landing pad has its own index starting from 0, and the +// argument to wasm.eh.landingpad.index() is the index of the landing pad +// each invoke instruction unwinds to. +// e.g. +// call void wasm.eh.landingpad.index(index); +// invoke void @foo() ... // original invoke inst +// +// wasm.eh.landingpad.index() function is lowered later to pass information +// to LSDA generator. +// +// 2. Adds following code after every landingpad instruction: +// (In C-style pseudocode) +// +// {old_exn, old_selector} = landingpad .. // original landingpad inst +// void *exn = wasm.catch.extract(0); +// __wasm_lpad_context.lpad_index = index; +// __wasm_lpad_context.lsda = wasm.eh.lsda(); +// _Unwind_CallPersonality(exn); +// int selector = __wasm.landingpad_context.selector; +// // use {exn, selector} instead hereafter +// +// The old landingpad instruction will be deleted in instruction selection +// phase. +// +// +// * Background: WebAssembly EH instructions +// WebAssembly's exception handling instructions are structured as follows: +// +// try +// instruction* +// wasm_exn (wasm except_ref type) = catch +// exn (i8* type) = extract exception pointer from wasm_exn +// use i8* exn to handle the exception +// try_end +// +// A catch instruction in WebAssembly does not correspond to a C++ catch clause. +// In the WebAssembly, there is a single catch instruction for all exceptions in +// the current stage. WebAssembly catch instruction returns an exception object +// whose type is except_ref type, an opaque type to represent an exception in +// WebAssembly. This except_ref type object contains the original i8* exception +// pointer and possibly other information the WebAssembly's host environment +// needs. Since this is an opaque type whose internal structure is only known +// to WebAssembly's host environment, this type cannot be represented in LLVM IR +// here, so we use wasm.catch.extract() intrinsic function that includes +// catching an exception and extract the exception pointer from it. The call to +// this intrinsic function will be lowered to WebAssembly instructions in the +// custom instruction selection phase in +// lib/Target/WebAssembly/WebAssemblyISelLowering.cpp. +// +// +// * Background: Direct personality function call +// In WebAssembly EH, the VM is responsible for unwinding stack once an +// exception is thrown. After stack is unwound, the control flow is transfered +// to WebAssembly 'catch' instruction, which returns a caught exception object. +// +// Unwinding stack is not done by libunwind but the VM, so the personality +// function in libcxxabi cannot be called from libunwind during the unwinding +// process. So after a catch instruction, we insert a call to a wrapper function +// in libunwind that in turn calls the real personality function. +// +// In Itanium EH, if the personality function decides there is no matching catch +// clause in a call frame and no cleanup action to perform, the unwinder doesn't +// stop there and continues unwinding. But in Wasm EH, the unwinder stops at +// every call frame with a landing pad, after which the personality function is +// called from the compiler-generated user code here. +// +// In libunwind/include/Unwind-wasm.c, we have this struct that serves as a +// communincation channel between the compiler-generated user code and the +// personality function in libcxxabi. +// +// struct _Unwind_LandingPadContext { +// uintptr_t lpad_index; +// uintptr_t lsda; +// uintptr_t selector; +// }; +// struct _Unwind_LandingPadContext __wasm_lpad_context = ...; +// +// And this wrapper in libunwind calls the personality function. +// +// _Unwind_Reason_Code _Unwind_CallPersonality(void *exception_ptr) { +// struct _Unwind_Exception *exception_obj = +// (struct _Unwind_Exception *)exception_ptr; +// _Unwind_Reason_Code ret = __gxx_personality_v0( +// 1, _UA_CLEANUP_PHASE, exception_obj->exception_class, exception_obj, +// (struct _Unwind_Context *)__wasm_lpad_context); +// return ret; +// } +// +// We pass a landing pad index, and the address of LSDA for the current function +// to the wrapper function _Unwind_CallPersonality in libunwind, and we +// retrieve the selector after it returns. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/SetVector.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/ADT/Triple.h" +#include "llvm/CodeGen/Passes.h" +#include "llvm/CodeGen/TargetLowering.h" +#include "llvm/CodeGen/TargetPassConfig.h" +#include "llvm/CodeGen/TargetSubtargetInfo.h" +#include "llvm/IR/Dominators.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/Intrinsics.h" +#include "llvm/Pass.h" +#include "llvm/Transforms/Utils/BasicBlockUtils.h" +using namespace llvm; + +#define DEBUG_TYPE "wasmehprepare" + +namespace { +class WasmEHPrepare : public FunctionPass { + Type *LPadContextTy = nullptr; // type of 'struct _Unwind_LandingPadContext' + GlobalVariable *LPadContextGV = nullptr; // __wasm_lpad_context + + // Field addresses of struct _Unwind_LandingPadContext + Value *LPadIndexField = nullptr; // lpad_index field + Value *LSDAField = nullptr; // lsda field + Value *SelectorField = nullptr; // selector + + Function *CatchF = nullptr; // wasm.catch.extract() intrinsic + Function *LPadIndexF = nullptr; // wasm.eh.landingpad.index() intrinsic + Function *LSDAF = nullptr; // wasm.eh.lsda() intrinsic + Function *CallPersonalityF = nullptr; // _Unwind_CallPersonality() wrapper + + void prepareLandingPad(BasicBlock *BB, unsigned Index, bool IsTopLevelLPad); + void substituteLPadValues(LandingPadInst *LPI, Value *Exn, Value *Selector); + +public: + static char ID; // Pass identification, replacement for typeid + + WasmEHPrepare() : FunctionPass(ID) {} + + bool doInitialization(Module &M) override; + bool runOnFunction(Function &F) override; + + StringRef getPassName() const override { + return "WebAssembly Exception handling preparation"; + } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.addRequired(); + AU.addRequired(); + } +}; +} // end anonymous namespace + +char WasmEHPrepare::ID = 0; +INITIALIZE_PASS(WasmEHPrepare, DEBUG_TYPE, "Prepare WebAssembly exceptions", + false, false); + +FunctionPass *llvm::createWasmEHPreparePass() { return new WasmEHPrepare(); } + +bool WasmEHPrepare::doInitialization(Module &M) { + IRBuilder<> IRB(M.getContext()); + LPadContextTy = StructType::get(IRB.getInt32Ty(), // lpad_index + IRB.getInt8PtrTy(), // lsda + IRB.getInt32Ty() // selector + ); + return false; +} + +bool WasmEHPrepare::runOnFunction(Function &F) { + SmallVector LPads; + for (BasicBlock &BB : F) + if (BB.isEHPad()) + LPads.push_back(&BB); + + if (LPads.empty()) + return false; + + assert(F.hasPersonalityFn() && "Personality function not found"); + + Module &M = *F.getParent(); + IRBuilder<> IRB(F.getContext()); + + // wasm.catch.extract() intinsic. Will be lowered to multiple WebAssmebly + // instructions that catches a wasm exception and extract a pointer value from + // it + CatchF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_catch_extract); + // wasm.eh.landingpad.index() intrinsic, which is to specify landingpad index + LPadIndexF = + Intrinsic::getDeclaration(&M, Intrinsic::wasm_eh_landingpad_index); + // wasm.eh.lsda() intrinsic. Returns the address of LSDA table for the current + // function. + LSDAF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_eh_lsda); + // _Unwind_CallPersonality() wrapper function, which calls the personality + CallPersonalityF = cast(M.getOrInsertFunction( + "_Unwind_CallPersonality", IRB.getInt32Ty(), IRB.getInt8PtrTy())); + + // _Unwind_GetExceptionProxy() returns a fake exception object to be used in + // case we catch a foreign exception. The call to this function will be + // generated when expanding pseudo instructions. + M.getOrInsertFunction("_Unwind_GetExceptionProxy", IRB.getInt8PtrTy()); + + // We may need to generate calls to _Unwind_Resume in CFGStackify pass in + // backend. Insert the function declaration now. + const TargetMachine &TM = + getAnalysis().getTM(); + const TargetLowering *TLI = TM.getSubtargetImpl(F)->getTargetLowering(); + FunctionType *FTy = + FunctionType::get(IRB.getVoidTy(), IRB.getInt8PtrTy(), false); + const char *RewindName = TLI->getLibcallName(RTLIB::UNWIND_RESUME); + F.getParent()->getOrInsertFunction(RewindName, FTy); + + // __wasm_lpad_context global variable + LPadContextGV = cast( + M.getOrInsertGlobal("__wasm_lpad_context", LPadContextTy)); + 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"); + + // Calculate top-level landing pads: landing pads that is not dominated by any + // of other landing pads. + auto &DT = getAnalysis().getDomTree(); + SmallPtrSet TopLevelLPads; + DomTreeNode *Root = DT.getRootNode(); + SmallVector WL; + WL.push_back(Root); + while (!WL.empty()) { + DomTreeNode *N = WL.pop_back_val(); + if (N->getBlock()->isEHPad()) + TopLevelLPads.insert(N->getBlock()); + else + WL.append(N->begin(), N->end()); + } + + for (unsigned I = 0, E = LPads.size(); I != E; ++I) + prepareLandingPad(LPads[I], I, TopLevelLPads.count(LPads[I]) > 0); + + return true; +} + +void WasmEHPrepare::prepareLandingPad(BasicBlock *BB, unsigned Index, + bool IsTopLevelLPad) { + assert(BB->isEHPad() && "BB is not a landing pad!"); + Function *F = BB->getParent(); + LandingPadInst *LPI = BB->getLandingPadInst(); + IRBuilder<> IRB(F->getContext()); + + // Create a call to wasm.eh.landingpad.index intrinsic before every invoke + // instruction that has this BB as its unwind destination. This is to create a + // map of in SelectionDAG, which is to be + // used to create a map of in + // SelectionDAGISel, again which is to be used in EHStreamer to emit LSDA + // tables. + // + // The reason we do this not in BBs with a landingpad instruction but + // in those with a invoke is, in SelectioDAG, the map of is constructed before instruction selection for a + // landingpad block. Since SelectionDAG visits BBs in reverse post order, BBs + // with an invoke are always visited before their corresponding landingpad BB, + // so it is ensured an entry for the landing pad BB in the map has been + // generated before the landing pad BB is visited. + for (BasicBlock *Pred : predecessors(BB)) { + auto *II = dyn_cast(Pred->getTerminator()); + assert(II && "Landingpad's predecessor does not end with invoke"); + IRB.SetInsertPoint(II); + IRB.CreateCall(LPadIndexF, IRB.getInt32(Index)); + } + + IRB.SetInsertPoint(&*BB->getFirstInsertionPt()); + // void *exn = wasm.catch.extract(); + Instruction *Exn = IRB.CreateCall(CatchF, {}, "exn"); + + // __wasm_lpad_context.lpad_index = index; + IRB.CreateStore(IRB.getInt32(Index), LPadIndexField, true); + + // If this is not a top level landing pad, i.e., there is another landing pad + // that dominates this landing pad, we don't need to store LSDA address again, + // because they are the same throughout the function and have been already + // stored before. + // TODO Can we not store LSDA address in user functoin but make libcxxabi + // compute it? + if (IsTopLevelLPad) + // __wasm_lpad_context.lsda = wasm.eh.lsda(); + IRB.CreateStore(IRB.CreateCall(LSDAF), LSDAField, true); + // _Unwind_CallPersonality(exn); + IRB.CreateCall(CallPersonalityF, Exn); + // int selector = __wasm.landingpad_context.selector; + Instruction *Selector = IRB.CreateLoad(SelectorField, "selector"); + substituteLPadValues(LPI, Exn, Selector); +} + +void WasmEHPrepare::substituteLPadValues(LandingPadInst *LPI, Value *Exn, + Value *Selector) { + SmallVector UseWorkList(LPI->user_begin(), LPI->user_end()); + while (!UseWorkList.empty()) { + Value *V = UseWorkList.pop_back_val(); + auto *EVI = dyn_cast(V); + if (!EVI) + continue; + if (EVI->getNumIndices() != 1) + continue; + if (*EVI->idx_begin() == 0) + EVI->replaceAllUsesWith(Exn); + else if (*EVI->idx_begin() == 1) + EVI->replaceAllUsesWith(Selector); + if (EVI->use_empty()) + EVI->eraseFromParent(); + } + + if (LPI->use_empty()) + return; + + // There are still some uses of LPI. Construct an aggregate with the exception + // values and replace the LPI with that aggregate. + Type *LPadType = LPI->getType(); + Value *LPadVal = UndefValue::get(LPadType); + auto *SelI = cast(Selector); + IRBuilder<> Builder(SelI->getParent(), std::next(SelI->getIterator())); + LPadVal = Builder.CreateInsertValue(LPadVal, Exn, 0, "lpad.val"); + LPadVal = Builder.CreateInsertValue(LPadVal, Selector, 1, "lpad.val"); + LPI->replaceAllUsesWith(LPadVal); +} Index: test/CodeGen/WebAssembly/wasmehprepare.ll =================================================================== --- /dev/null +++ test/CodeGen/WebAssembly/wasmehprepare.ll @@ -0,0 +1,161 @@ +; RUN: opt < %s -wasmehprepare -S | FileCheck %s + +target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" +target triple = "wasm32-unknown-unknown-wasm" + +; CHECK: @__wasm_lpad_context = external global { i32, i8*, i32 } + +@_ZTIi = external constant i8* + +; There are two landing pads: one landing pad has two invokes as its +; predecessors, and the other has one. +define void @test1() personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) { +; CHECK-LABEL: @test1() +entry: + invoke void @foo() + to label %invoke.cont unwind label %lpad +; CHECK: entry: +; CHECK-NEXT: call void @llvm.wasm.eh.landingpad.index(i32 0) + +invoke.cont: ; preds = %entry + invoke void @foo() + to label %try.cont unwind label %lpad +; CHECK: invoke.cont: +; CHECK-NEXT: call void @llvm.wasm.eh.landingpad.index(i32 0) + +lpad: ; preds = %invoke.cont, %entry + %0 = landingpad { i8*, i32 } + catch i8* null + %1 = extractvalue { i8*, i32 } %0, 0 + %2 = extractvalue { i8*, i32 } %0, 1 + %3 = call i8* @__cxa_begin_catch(i8* %1) + call void @__cxa_end_catch() + br label %try.cont +; CHECK: lpad: +; CHECK: store volatile i32 0, i32* getelementptr inbounds ({ i32, i8*, i32 }, { i32, i8*, i32 }* @__wasm_lpad_context, i32 0, i32 0) + +try.cont: ; preds = %invoke.cont, %lpad + invoke void @foo() + to label %try.cont6 unwind label %lpad2 +; CHECK: try.cont: +; CHECK-NEXT: call void @llvm.wasm.eh.landingpad.index(i32 1) + +lpad2: ; preds = %try.cont + %4 = landingpad { i8*, i32 } + catch i8* bitcast (i8** @_ZTIi to i8*) + %5 = extractvalue { i8*, i32 } %4, 0 + %6 = extractvalue { i8*, i32 } %4, 1 + %7 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*)) + %matches = icmp eq i32 %6, %7 + br i1 %matches, label %catch4, label %eh.resume +; CHECK: lpad2: +; CHECK-NEXT: landingpad +; CHECK-NEXT: catch +; CHECK-NEXT: %[[EXN:.*]] = call i8* @llvm.wasm.catch.extract() +; CHECK-NEXT: store volatile i32 1, i32* getelementptr inbounds ({ i32, i8*, i32 }, { i32, i8*, i32 }* @__wasm_lpad_context, i32 0, i32 0) +; CHECK-NEXT: %[[LSDA:.*]] = call i8* @llvm.wasm.eh.lsda() +; CHECK-NEXT: store volatile i8* %[[LSDA]], i8** getelementptr inbounds ({ i32, i8*, i32 }, { i32, i8*, i32 }* @__wasm_lpad_context, i32 0, i32 1) +; CHECK-NEXT: call i32 @_Unwind_CallPersonality(i8* %[[EXN]]) +; CHECK-NEXT: %[[SELECTOR:.*]] = load i32, i32* getelementptr inbounds ({ i32, i8*, i32 }, { i32, i8*, i32 }* @__wasm_lpad_context, i32 0, i32 2) +; CHECK: icmp eq i32 %[[SELECTOR]] + +catch4: ; preds = %lpad2 + %8 = call i8* @__cxa_begin_catch(i8* %5) + %9 = bitcast i8* %8 to i32* + %10 = load i32, i32* %9, align 4 + call void @__cxa_end_catch() + br label %try.cont6 +; CHECK: catch4: +; CHECK-NEXT: call i8* @__cxa_begin_catch(i8* %[[EXN]]) + +try.cont6: ; preds = %try.cont, %catch4 + ret void + +eh.resume: ; preds = %lpad2 + %lpad.val = insertvalue { i8*, i32 } undef, i8* %5, 0 + %lpad.val9 = insertvalue { i8*, i32 } %lpad.val, i32 %6, 1 + resume { i8*, i32 } %lpad.val9 +; CHECK: eh.resume: +; CHECK-NEXT: %[[VAL:.*]] = insertvalue { i8*, i32 } undef, i8* %[[EXN]], 0 +; CHECK-NEXT: %[[VAL2:.*]] = insertvalue { i8*, i32 } %[[VAL]], i32 %[[SELECTOR]], 1 +; CHECK-NEXT: resume { i8*, i32 } %[[VAL2]] +} + +; Optimization test: non-top-level landing pads should not store LSDA address +; again unnecessarily. +define void @test2() personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) { +; CHECK-LABEL: @test2() +entry: + invoke void @foo() + to label %try.cont unwind label %lpad + +lpad: ; preds = %entry + %0 = landingpad { i8*, i32 } + catch i8* null + %1 = extractvalue { i8*, i32 } %0, 0 + %2 = extractvalue { i8*, i32 } %0, 1 + %3 = call i8* @__cxa_begin_catch(i8* %1) #3 + invoke void @foo() + to label %invoke.cont2 unwind label %lpad1 +; CHECK: lpad: +; CHECK-NEXT: landingpad +; CHECK-NEXT: catch +; CHECK-NEXT: @llvm.wasm.catch.extract +; CHECK-NEXT: store volatile i32 0 +; CHECK-NEXT: @llvm.wasm.eh.lsda +; CHECK-NEXT: store volatile i8* +; CHECK-NEXT: call i32 @_Unwind_CallPersonality + +invoke.cont2: ; preds = %lpad + call void @__cxa_end_catch() + br label %try.cont + +try.cont: ; preds = %entry, %invoke.cont2 + ret void + +lpad1: ; preds = %lpad + %4 = landingpad { i8*, i32 } + cleanup + %5 = extractvalue { i8*, i32 } %4, 0 + %6 = extractvalue { i8*, i32 } %4, 1 + invoke void @__cxa_end_catch() + to label %eh.resume unwind label %terminate.lpad +; CHECK: lpad1: +; CHECK-NEXT: landingpad +; CHECK-NEXT: cleanup +; CHECK-NEXT: @llvm.wasm.catch.extract +; CHECK-NEXT: store volatile i32 1 +; CHECK-NEXT: call i32 @_Unwind_CallPersonality + +eh.resume: ; preds = %lpad1 + %lpad.val = insertvalue { i8*, i32 } undef, i8* %5, 0 + %lpad.val5 = insertvalue { i8*, i32 } %lpad.val, i32 %6, 1 + resume { i8*, i32 } %lpad.val5 + +terminate.lpad: ; preds = %lpad1 + %7 = landingpad { i8*, i32 } + catch i8* null + %8 = extractvalue { i8*, i32 } %7, 0 + call void @__clang_call_terminate(i8* %8) + unreachable +; CHECK: terminate.lpad: +; CHECK-NEXT: landingpad +; CHECK-NEXT: catch +; CHECK-NEXT: @llvm.wasm.catch.extract +; CHECK-NEXT: store volatile i32 2 +; CHECK-NEXT: call i32 @_Unwind_CallPersonality +} + +declare void @foo() +declare i32 @__gxx_personality_v0(...) +declare i8* @__cxa_begin_catch(i8*) +declare void @__cxa_end_catch() +declare i32 @llvm.eh.typeid.for(i8*) +declare void @__clang_call_terminate(i8*) + +; CHECK-DAG: declare i8* @llvm.wasm.catch.extract() +; CHECK-DAG: declare void @llvm.wasm.eh.landingpad.index(i32) +; CHECK-DAG: declare i8* @llvm.wasm.eh.lsda() +; CHECK-DAG: declare i32 @_Unwind_CallPersonality(i8*) +; CHECK-DAG: declare i8* @_Unwind_GetExceptionProxy() +; CHECK-DAG: declare void @_Unwind_Resume(i8*) Index: tools/opt/opt.cpp =================================================================== --- tools/opt/opt.cpp +++ tools/opt/opt.cpp @@ -414,6 +414,7 @@ initializePostInlineEntryExitInstrumenterPass(Registry); initializeUnreachableBlockElimLegacyPassPass(Registry); initializeExpandReductionsPass(Registry); + initializeWasmEHPreparePass(Registry); initializeWriteBitcodePassPass(Registry); #ifdef LINK_POLLY_INTO_TOOLS