Index: llvm/trunk/lib/Target/WebAssembly/CMakeLists.txt =================================================================== --- llvm/trunk/lib/Target/WebAssembly/CMakeLists.txt +++ llvm/trunk/lib/Target/WebAssembly/CMakeLists.txt @@ -20,6 +20,7 @@ WebAssemblyISelLowering.cpp WebAssemblyInstrInfo.cpp WebAssemblyLowerBrUnless.cpp + WebAssemblyLowerEmscriptenExceptions.cpp WebAssemblyMachineFunctionInfo.cpp WebAssemblyMCInstLower.cpp WebAssemblyOptimizeLiveIntervals.cpp Index: llvm/trunk/lib/Target/WebAssembly/WebAssembly.h =================================================================== --- llvm/trunk/lib/Target/WebAssembly/WebAssembly.h +++ llvm/trunk/lib/Target/WebAssembly/WebAssembly.h @@ -16,14 +16,18 @@ #ifndef LLVM_LIB_TARGET_WEBASSEMBLY_WEBASSEMBLY_H #define LLVM_LIB_TARGET_WEBASSEMBLY_WEBASSEMBLY_H +#include "llvm/PassRegistry.h" #include "llvm/Support/CodeGen.h" namespace llvm { class WebAssemblyTargetMachine; +class ModulePass; class FunctionPass; // LLVM IR passes. +ModulePass *createWebAssemblyLowerEmscriptenExceptions(); +void initializeWebAssemblyLowerEmscriptenExceptionsPass(PassRegistry &); FunctionPass *createWebAssemblyOptimizeReturned(); // ISel and immediate followup passes. Index: llvm/trunk/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenExceptions.cpp =================================================================== --- llvm/trunk/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenExceptions.cpp +++ llvm/trunk/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenExceptions.cpp @@ -0,0 +1,458 @@ +// WebAssemblyLowerEmscriptenExceptions.cpp - Lower exceptions for Emscripten // +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief This file lowers exception-related instructions in order to use +/// Emscripten's JavaScript try and catch mechanism to handle exceptions. +/// +/// To handle exceptions, this scheme relies on JavaScript's try and catch +/// syntax and relevant exception-related libraries implemented in JavaScript +/// glue code that will be produced by Emscripten. This is similar to the +/// current Emscripten asm.js exception handling in fastcomp. +/// For fastcomp's EH scheme, see these files in fastcomp LLVM branch: +/// (Location: https://github.com/kripken/emscripten-fastcomp) +/// lib/Target/JSBackend/NaCl/LowerEmExceptionsPass.cpp +/// lib/Target/JSBackend/JSBackend.cpp +/// lib/Target/JSBackend/CallHandlers.h +/// +/// This pass does following things: +/// +/// 1) Create three global variables: __THREW__, threwValue, and tempRet0. +/// tempRet0 will be set within ___cxa_find_matching_catch() function in +/// JS library, and __THREW__ and threwValue will be set in invoke wrappers +/// in JS glue code. For what invoke wrappers are, refer to 3). +/// +/// 2) Create setThrew and setTempRet0 functions. +/// The global variables created in 1) will exist in wasm address space, +/// but their values should be set in JS code, so we provide these functions +/// as interfaces to JS glue code. These functions are equivalent to the +/// following JS functions, which actually exist in asm.js version of JS +/// library. +/// +/// function setThrew(threw, value) { +/// if (__THREW__ == 0) { +/// __THREW__ = threw; +/// threwValue = value; +/// } +/// } +/// +/// function setTempRet0(value) { +/// tempRet0 = value; +/// } +/// +/// 3) Lower +/// invoke @func(arg1, arg2) to label %invoke.cont unwind label %lpad +/// into +/// __THREW__ = 0; +/// call @invoke_SIG(func, arg1, arg2) +/// %__THREW__.val = __THREW__; +/// __THREW__ = 0; +/// br %__THREW__.val, label %lpad, label %invoke.cont +/// SIG is a mangled string generated based on the LLVM IR-level function +/// signature. After LLVM IR types are lowered to the target wasm types, +/// the names for these wrappers will change based on wasm types as well, +/// as in invoke_vi (function takes an int and returns void). The bodies of +/// these wrappers will be generated in JS glue code, and inside those +/// wrappers we use JS try-catch to generate actual exception effects. It +/// also calls the original callee function. An example wrapper in JS code +/// would look like this: +/// function invoke_vi(index,a1) { +/// try { +/// Module["dynCall_vi"](index,a1); // This calls original callee +/// } catch(e) { +/// if (typeof e !== 'number' && e !== 'longjmp') throw e; +/// asm["setThrew"](1, 0); // setThrew is called here +/// } +/// } +/// If an exception is thrown, __THREW__ will be set to true in a wrapper, +/// so we can jump to the right BB based on this value. +/// +/// 4) Lower +/// %val = landingpad catch c1 catch c2 catch c3 ... +/// ... use %val ... +/// into +/// %fmc = call @___cxa_find_matching_catch_N(c1, c2, c3, ...) +/// %val = {%fmc, tempRet0} +/// ... use %val ... +/// Here N is a number calculated based on the number of clauses. +/// Global variable tempRet0 is set within ___cxa_find_matching_catch() in +/// JS glue code. +/// +/// 5) Lower +/// resume {%a, %b} +/// into +/// call @___resumeException(%a) +/// where ___resumeException() is a function in JS glue code. +/// +/// TODO: Handle i64 types +/// +///===----------------------------------------------------------------------===// + +#include "WebAssembly.h" +#include "llvm/ADT/IndexedMap.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Module.h" +#include "llvm/Pass.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; + +#define DEBUG_TYPE "wasm-lower-em-exceptions" + +namespace { +class WebAssemblyLowerEmscriptenExceptions final : public ModulePass { + const char *getPassName() const override { + return "WebAssembly Lower Emscripten Exceptions"; + } + + bool runOnFunction(Function &F); + // Returns ___cxa_find_matching_catch_N function, where N = NumClauses + 2. + // This is because a landingpad instruction contains two more arguments, + // a personality function and a cleanup bit, and ___cxa_find_matching_catch_N + // functions are named after the number of arguments in the original + // landingpad instruction. + Function *getFindMatchingCatch(Module &M, unsigned NumClauses); + + Function *getInvokeWrapper(Module &M, InvokeInst *II); + + GlobalVariable *ThrewGV; // __THREW__ + GlobalVariable *ThrewValueGV; // threwValue + GlobalVariable *TempRet0GV; // tempRet0 + Function *ResumeF; + // ___cxa_find_matching_catch_N functions. + // Indexed by the number of clauses in an original landingpad instruction. + DenseMap FindMatchingCatches; + // Map of + StringMap InvokeWrappers; + +public: + static char ID; + + WebAssemblyLowerEmscriptenExceptions() + : ModulePass(ID), ThrewGV(nullptr), ThrewValueGV(nullptr), + TempRet0GV(nullptr) {} + bool runOnModule(Module &M) override; +}; +} // End anonymous namespace + +char WebAssemblyLowerEmscriptenExceptions::ID = 0; +INITIALIZE_PASS(WebAssemblyLowerEmscriptenExceptions, DEBUG_TYPE, + "WebAssembly Lower Emscripten Exceptions", false, false) + +ModulePass *llvm::createWebAssemblyLowerEmscriptenExceptions() { + return new WebAssemblyLowerEmscriptenExceptions(); +} + +static bool canThrow(const Value *V) { + if (const auto *F = dyn_cast(V)) { + // Intrinsics cannot throw + if (F->isIntrinsic()) + return false; + StringRef Name = F->getName(); + // leave setjmp and longjmp (mostly) alone, we process them properly later + if (Name == "setjmp" || Name == "longjmp") + return false; + return true; + } + return true; // not a function, so an indirect call - can throw, we can't tell +} + +// Returns an available name for a global value. +// If the proposed name already exists in the module, adds '_' at the end of +// the name until the name is available. +static inline std::string createGlobalValueName(const Module &M, + const std::string &Propose) { + std::string Name = Propose; + while (M.getNamedGlobal(Name)) + Name += "_"; + return Name; +} + +// Simple function name mangler. +// This function simply takes LLVM's string representation of parameter types +// concatenate them with '_'. There are non-alphanumeric characters but llc is +// ok with it, and we need to postprocess these names after the lowering phase +// anyway. +static std::string getSignature(FunctionType *FTy) { + std::string Sig; + raw_string_ostream OS(Sig); + OS << *FTy->getReturnType(); + for (Type *ParamTy : FTy->params()) + OS << "_" << *ParamTy; + if (FTy->isVarArg()) + OS << "_..."; + Sig = OS.str(); + Sig.erase(std::remove_if(Sig.begin(), Sig.end(), isspace), Sig.end()); + return Sig; +} + +Function *WebAssemblyLowerEmscriptenExceptions::getFindMatchingCatch( + Module &M, unsigned NumClauses) { + if (FindMatchingCatches.count(NumClauses)) + return FindMatchingCatches[NumClauses]; + PointerType *Int8PtrTy = Type::getInt8PtrTy(M.getContext()); + SmallVector Args(NumClauses, Int8PtrTy); + FunctionType *FTy = FunctionType::get(Int8PtrTy, Args, false); + Function *F = Function::Create( + FTy, GlobalValue::ExternalLinkage, + "___cxa_find_matching_catch_" + Twine(NumClauses + 2), &M); + FindMatchingCatches[NumClauses] = F; + return F; +} + +Function * +WebAssemblyLowerEmscriptenExceptions::getInvokeWrapper(Module &M, + InvokeInst *II) { + SmallVector ArgTys; + Value *Callee = II->getCalledValue(); + FunctionType *CalleeFTy; + if (auto *F = dyn_cast(Callee)) + CalleeFTy = F->getFunctionType(); + else { + auto *CalleeTy = dyn_cast(Callee->getType())->getElementType(); + CalleeFTy = dyn_cast(CalleeTy); + } + + std::string Sig = getSignature(CalleeFTy); + if (InvokeWrappers.find(Sig) != InvokeWrappers.end()) + return InvokeWrappers[Sig]; + + // Put the pointer to the callee as first argument + ArgTys.push_back(PointerType::getUnqual(CalleeFTy)); + // Add argument types + ArgTys.append(CalleeFTy->param_begin(), CalleeFTy->param_end()); + + FunctionType *FTy = FunctionType::get(CalleeFTy->getReturnType(), ArgTys, + CalleeFTy->isVarArg()); + Function *F = Function::Create(FTy, GlobalValue::ExternalLinkage, + "__invoke_" + Sig, &M); + InvokeWrappers[Sig] = F; + return F; +} + +bool WebAssemblyLowerEmscriptenExceptions::runOnModule(Module &M) { + IRBuilder<> Builder(M.getContext()); + IntegerType *Int1Ty = Builder.getInt1Ty(); + PointerType *Int8PtrTy = Builder.getInt8PtrTy(); + IntegerType *Int32Ty = Builder.getInt32Ty(); + + // Create global variables __THREW__, threwValue, and tempRet0 + ThrewGV = new GlobalVariable(M, Int1Ty, false, GlobalValue::ExternalLinkage, + Builder.getFalse(), + createGlobalValueName(M, "__THREW__")); + ThrewValueGV = new GlobalVariable( + M, Int32Ty, false, GlobalValue::ExternalLinkage, Builder.getInt32(0), + createGlobalValueName(M, "threwValue")); + TempRet0GV = new GlobalVariable( + M, Int32Ty, false, GlobalValue::ExternalLinkage, Builder.getInt32(0), + createGlobalValueName(M, "tempRet0")); + + // Register ___resumeException function + Type *VoidTy = Type::getVoidTy(M.getContext()); + FunctionType *ResumeFTy = FunctionType::get(VoidTy, Int8PtrTy, false); + ResumeF = Function::Create(ResumeFTy, GlobalValue::ExternalLinkage, + "___resumeException", &M); + + bool Changed = false; + for (Function &F : M) { + if (F.isDeclaration()) + continue; + Changed |= runOnFunction(F); + } + + if (!Changed) + return false; + + assert(!M.getNamedGlobal("setThrew") && "setThrew already exists"); + assert(!M.getNamedGlobal("setTempRet0") && "setTempRet0 already exists"); + + // Create setThrew function + SmallVector Params = {Int1Ty, Int32Ty}; + FunctionType *FTy = FunctionType::get(VoidTy, Params, false); + Function *F = + Function::Create(FTy, GlobalValue::ExternalLinkage, "setThrew", &M); + Argument *Arg1 = &*(F->arg_begin()); + Argument *Arg2 = &*(++F->arg_begin()); + Arg1->setName("threw"); + Arg2->setName("value"); + BasicBlock *EntryBB = BasicBlock::Create(M.getContext(), "entry", F); + BasicBlock *ThenBB = BasicBlock::Create(M.getContext(), "if.then", F); + BasicBlock *EndBB = BasicBlock::Create(M.getContext(), "if.end", F); + + Builder.SetInsertPoint(EntryBB); + Value *Threw = Builder.CreateLoad(ThrewGV, ThrewGV->getName() + ".val"); + Value *Cmp = Builder.CreateICmpEQ(Threw, Builder.getFalse(), "cmp"); + Builder.CreateCondBr(Cmp, ThenBB, EndBB); + + Builder.SetInsertPoint(ThenBB); + Builder.CreateStore(Arg1, ThrewGV); + Builder.CreateStore(Arg2, ThrewValueGV); + Builder.CreateBr(EndBB); + + Builder.SetInsertPoint(EndBB); + Builder.CreateRetVoid(); + + // Create setTempRet0 function + Params = {Int32Ty}; + FTy = FunctionType::get(VoidTy, Params, false); + F = Function::Create(FTy, GlobalValue::ExternalLinkage, "setTempRet0", &M); + F->arg_begin()->setName("value"); + EntryBB = BasicBlock::Create(M.getContext(), "entry", F); + Builder.SetInsertPoint(EntryBB); + Builder.CreateStore(&*F->arg_begin(), TempRet0GV); + Builder.CreateRetVoid(); + + return true; +} + +bool WebAssemblyLowerEmscriptenExceptions::runOnFunction(Function &F) { + Module &M = *F.getParent(); + IRBuilder<> Builder(M.getContext()); + bool Changed = false; + SmallVector ToErase; + SmallPtrSet LandingPads; + + for (BasicBlock &BB : F) { + auto *II = dyn_cast(BB.getTerminator()); + if (!II) + continue; + Changed = true; + LandingPads.insert(II->getLandingPadInst()); + Builder.SetInsertPoint(II); + + if (canThrow(II->getCalledValue())) { + // If we are calling a function that is noreturn, we must remove that + // attribute. The code we insert here does expect it to return, after we + // catch the exception. + if (II->doesNotReturn()) { + if (auto *F = dyn_cast(II->getCalledValue())) + F->removeFnAttr(Attribute::NoReturn); + AttributeSet NewAttrs = II->getAttributes(); + NewAttrs.removeAttribute(M.getContext(), AttributeSet::FunctionIndex, + Attribute::NoReturn); + II->setAttributes(NewAttrs); + } + + // Pre-invoke + // __THREW__ = 0; + Builder.CreateStore(Builder.getFalse(), ThrewGV); + + // Invoke function wrapper in JavaScript + SmallVector CallArgs; + // Put the pointer to the callee as first argument, so it can be called + // within the invoke wrapper later + CallArgs.push_back(II->getCalledValue()); + CallArgs.append(II->arg_begin(), II->arg_end()); + CallInst *NewCall = Builder.CreateCall(getInvokeWrapper(M, II), CallArgs); + NewCall->takeName(II); + NewCall->setCallingConv(II->getCallingConv()); + NewCall->setAttributes(II->getAttributes()); + NewCall->setDebugLoc(II->getDebugLoc()); + II->replaceAllUsesWith(NewCall); + ToErase.push_back(II); + + // Post-invoke + // %__THREW__.val = __THREW__; __THREW__ = 0; + Value *Threw = Builder.CreateLoad(ThrewGV, ThrewGV->getName() + ".val"); + Builder.CreateStore(Builder.getFalse(), ThrewGV); + + // Insert a branch based on __THREW__ variable + Builder.CreateCondBr(Threw, II->getUnwindDest(), II->getNormalDest()); + + } else { + // This can't throw, and we don't need this invoke, just replace it with a + // call+branch + SmallVector CallArgs(II->arg_begin(), II->arg_end()); + CallInst *NewCall = Builder.CreateCall(II->getCalledValue(), CallArgs); + NewCall->takeName(II); + NewCall->setCallingConv(II->getCallingConv()); + NewCall->setAttributes(II->getAttributes()); + NewCall->setDebugLoc(II->getDebugLoc()); + II->replaceAllUsesWith(NewCall); + ToErase.push_back(II); + + Builder.CreateBr(II->getNormalDest()); + + // Remove any PHI node entries from the exception destination + II->getUnwindDest()->removePredecessor(&BB); + } + } + + // Process resume instructions + for (BasicBlock &BB : F) { + // Scan the body of the basic block for resumes + for (Instruction &I : BB) { + auto *RI = dyn_cast(&I); + if (!RI) + continue; + + // Split the input into legal values + Value *Input = RI->getValue(); + Builder.SetInsertPoint(RI); + Value *Low = Builder.CreateExtractValue(Input, 0, "low"); + + // Create a call to ___resumeException function + Value *Args[] = {Low}; + Builder.CreateCall(ResumeF, Args); + + // Add a terminator to the block + Builder.CreateUnreachable(); + ToErase.push_back(RI); + } + } + + // Look for orphan landingpads, can occur in blocks with no predecesors + for (BasicBlock &BB : F) { + Instruction *I = BB.getFirstNonPHI(); + if (auto *LPI = dyn_cast(I)) + LandingPads.insert(LPI); + } + + // Handle all the landingpad for this function together, as multiple invokes + // may share a single lp + for (LandingPadInst *LPI : LandingPads) { + Builder.SetInsertPoint(LPI); + SmallVector FMCArgs; + for (unsigned i = 0, e = LPI->getNumClauses(); i < e; ++i) { + Constant *Clause = LPI->getClause(i); + // As a temporary workaround for the lack of aggregate varargs support + // in the interface between JS and wasm, break out filter operands into + // their component elements. + if (LPI->isFilter(i)) { + ArrayType *ATy = cast(Clause->getType()); + for (unsigned j = 0, e = ATy->getNumElements(); j < e; ++j) { + Value *EV = + Builder.CreateExtractValue(Clause, makeArrayRef(j), "filter"); + FMCArgs.push_back(EV); + } + } else + FMCArgs.push_back(Clause); + } + + // Create a call to ___cxa_find_matching_catch_N function + Function *FMCF = getFindMatchingCatch(M, FMCArgs.size()); + CallInst *FMCI = Builder.CreateCall(FMCF, FMCArgs, "fmc"); + Value *Undef = UndefValue::get(LPI->getType()); + Value *Pair0 = Builder.CreateInsertValue(Undef, FMCI, 0, "pair0"); + Value *TempRet0 = + Builder.CreateLoad(TempRet0GV, TempRet0GV->getName() + "val"); + Value *Pair1 = Builder.CreateInsertValue(Pair0, TempRet0, 1, "pair1"); + + LPI->replaceAllUsesWith(Pair1); + ToErase.push_back(LPI); + } + + // Erase everything we no longer need in this function + for (Instruction *I : ToErase) + I->eraseFromParent(); + + return Changed; +} Index: llvm/trunk/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp =================================================================== --- llvm/trunk/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp +++ llvm/trunk/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp @@ -29,10 +29,20 @@ #define DEBUG_TYPE "wasm" +// Emscripten's asm.js-style exception handling +static cl::opt EnableEmExceptionHandling( + "wasm-em-exception-handling", + cl::desc("WebAssembly Emscripten-style exception handling"), + cl::init(false)); + extern "C" void LLVMInitializeWebAssemblyTarget() { // Register the target. RegisterTargetMachine X(TheWebAssemblyTarget32); RegisterTargetMachine Y(TheWebAssemblyTarget64); + + // Register exception handling pass to opt + initializeWebAssemblyLowerEmscriptenExceptionsPass( + *PassRegistry::getPassRegistry()); } //===----------------------------------------------------------------------===// @@ -149,6 +159,10 @@ if (getOptLevel() != CodeGenOpt::None) addPass(createWebAssemblyOptimizeReturned()); + // Handle exceptions. + if (EnableEmExceptionHandling) + addPass(createWebAssemblyLowerEmscriptenExceptions()); + TargetPassConfig::addIRPasses(); } Index: llvm/trunk/test/CodeGen/WebAssembly/lower-em-exceptions.ll =================================================================== --- llvm/trunk/test/CodeGen/WebAssembly/lower-em-exceptions.ll +++ llvm/trunk/test/CodeGen/WebAssembly/lower-em-exceptions.ll @@ -0,0 +1,143 @@ +; RUN: opt < %s -wasm-lower-em-exceptions -S | FileCheck %s + +@_ZTIi = external constant i8* +@_ZTIc = external constant i8* +; CHECK: @[[__THREW__:__THREW__.*]] = global i1 false +; CHECK: @[[THREWVALUE:threwValue.*]] = global i32 0 +; CHECK: @[[TEMPRET0:tempRet0.*]] = global i32 0 + +; Test invoke instruction with clauses (try-catch block) +define void @clause() personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) { +; CHECK-LABEL: @clause( +entry: + invoke void @foo(i32 3) + to label %invoke.cont unwind label %lpad +; CHECK: entry: +; CHECK-NEXT: store i1 false, i1* @[[__THREW__]] +; CHECK-NEXT: call void @__invoke_void_i32(void (i32)* @foo, i32 3) +; CHECK-NEXT: %[[__THREW__VAL:.*]] = load i1, i1* @[[__THREW__]] +; CHECK-NEXT: store i1 false, i1* @[[__THREW__]] +; CHECK-NEXT: br i1 %[[__THREW__VAL]], label %lpad, label %invoke.cont + +invoke.cont: ; preds = %entry + br label %try.cont + +lpad: ; preds = %entry + %0 = landingpad { i8*, i32 } + catch i8* bitcast (i8** @_ZTIi to i8*) + catch i8* null + %1 = extractvalue { i8*, i32 } %0, 0 + %2 = extractvalue { i8*, i32 } %0, 1 + br label %catch.dispatch +; CHECK: lpad: +; CHECK-NEXT: %[[FMC:.*]] = call i8* @___cxa_find_matching_catch_4(i8* bitcast (i8** @_ZTIi to i8*), i8* null) +; CHECK-NEXT: %[[IVI1:.*]] = insertvalue { i8*, i32 } undef, i8* %[[FMC]], 0 +; CHECK-NEXT: %[[TEMPRET0_VAL:.*]] = load i32, i32* @[[TEMPRET0]] +; CHECK-NEXT: %[[IVI2:.*]] = insertvalue { i8*, i32 } %[[IVI1]], i32 %[[TEMPRET0_VAL]], 1 +; CHECK-NEXT: extractvalue { i8*, i32 } %[[IVI2]], 0 +; CHECK-NEXT: extractvalue { i8*, i32 } %[[IVI2]], 1 + +catch.dispatch: ; preds = %lpad + %3 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*)) + %matches = icmp eq i32 %2, %3 + br i1 %matches, label %catch1, label %catch + +catch1: ; preds = %catch.dispatch + %4 = call i8* @__cxa_begin_catch(i8* %1) + %5 = bitcast i8* %4 to i32* + %6 = load i32, i32* %5, align 4 + call void @__cxa_end_catch() + br label %try.cont + +try.cont: ; preds = %catch, %catch1, %invoke.cont + ret void + +catch: ; preds = %catch.dispatch + %7 = call i8* @__cxa_begin_catch(i8* %1) + call void @__cxa_end_catch() + br label %try.cont +} + +; Test invoke instruction with filters (functions with throw(...) declaration) +define void @filter() personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) { +; CHECK-LABEL: @filter( +entry: + invoke void @foo(i32 3) + to label %invoke.cont unwind label %lpad +; CHECK: entry: +; CHECK-NEXT: store i1 false, i1* @[[__THREW__]] +; CHECK-NEXT: call void @__invoke_void_i32(void (i32)* @foo, i32 3) +; CHECK-NEXT: %[[__THREW__VAL:.*]] = load i1, i1* @[[__THREW__]] +; CHECK-NEXT: store i1 false, i1* @[[__THREW__]] +; CHECK-NEXT: br i1 %[[__THREW__VAL]], label %lpad, label %invoke.cont + +invoke.cont: ; preds = %entry + ret void + +lpad: ; preds = %entry + %0 = landingpad { i8*, i32 } + filter [2 x i8*] [i8* bitcast (i8** @_ZTIi to i8*), i8* bitcast (i8** @_ZTIc to i8*)] + %1 = extractvalue { i8*, i32 } %0, 0 + %2 = extractvalue { i8*, i32 } %0, 1 + br label %filter.dispatch +; CHECK: lpad: +; CHECK-NEXT: %[[FMC:.*]] = call i8* @___cxa_find_matching_catch_4(i8* bitcast (i8** @_ZTIi to i8*), i8* bitcast (i8** @_ZTIc to i8*)) +; CHECK-NEXT: %[[IVI1:.*]] = insertvalue { i8*, i32 } undef, i8* %[[FMC]], 0 +; CHECK-NEXT: %[[TEMPRET0_VAL:.*]] = load i32, i32* @[[TEMPRET0]] +; CHECK-NEXT: %[[IVI2:.*]] = insertvalue { i8*, i32 } %[[IVI1]], i32 %[[TEMPRET0_VAL]], 1 +; CHECK-NEXT: extractvalue { i8*, i32 } %[[IVI2]], 0 +; CHECK-NEXT: extractvalue { i8*, i32 } %[[IVI2]], 1 + +filter.dispatch: ; preds = %lpad + %ehspec.fails = icmp slt i32 %2, 0 + br i1 %ehspec.fails, label %ehspec.unexpected, label %eh.resume + +ehspec.unexpected: ; preds = %filter.dispatch + call void @__cxa_call_unexpected(i8* %1) #4 + unreachable + +eh.resume: ; preds = %filter.dispatch + %lpad.val = insertvalue { i8*, i32 } undef, i8* %1, 0 + %lpad.val3 = insertvalue { i8*, i32 } %lpad.val, i32 %2, 1 + resume { i8*, i32 } %lpad.val3 +; CHECK: eh.resume: +; CHECK-NEXT: insertvalue +; CHECK-NEXT: %[[LPAD_VAL:.*]] = insertvalue +; CHECK-NEXT: %[[LOW:.*]] = extractvalue { i8*, i32 } %[[LPAD_VAL]], 0 +; CHECK-NEXT: call void @___resumeException(i8* %[[LOW]]) +; CHECK-NEXT: unreachable +} + +declare void @foo(i32) + +declare i32 @__gxx_personality_v0(...) +declare i32 @llvm.eh.typeid.for(i8*) +declare i8* @__cxa_begin_catch(i8*) +declare void @__cxa_end_catch() +declare void @__cxa_call_unexpected(i8*) + +; JS glue functions and invoke wrappers registration +; CHECK: declare void @___resumeException(i8*) +; CHECK: declare void @__invoke_void_i32(void (i32)*, i32) +; CHECK: declare i8* @___cxa_find_matching_catch_4(i8*, i8*) + +; setThrew function creation +; CHECK-LABEL: define void @setThrew(i1 %threw, i32 %value) { +; CHECK: entry: +; CHECK-NEXT: %__THREW__.val = load i1, i1* @__THREW__ +; CHECK-NEXT: %cmp = icmp eq i1 %__THREW__.val, false +; CHECK-NEXT: br i1 %cmp, label %if.then, label %if.end +; CHECK: if.then: +; CHECK-NEXT: store i1 %threw, i1* @__THREW__ +; CHECK-NEXT: store i32 %value, i32* @threwValue +; CHECK-NEXT: br label %if.end +; CHECK: if.end: +; CHECK-NEXT: ret void +; CHECK: } + +; setTempRet0 function creation +; CHECK-LABEL: define void @setTempRet0(i32 %value) { +; CHECK: entry: +; CHECK-NEXT: store i32 %value, i32* @tempRet0 +; CHECK-NEXT: ret void +; CHECK: }