[llvm] r277391 - [WebAssembly] Add asm.js-style exception handling support
Derek Schuff via llvm-commits
llvm-commits at lists.llvm.org
Mon Aug 1 14:34:04 PDT 2016
Author: dschuff
Date: Mon Aug 1 16:34:04 2016
New Revision: 277391
URL: http://llvm.org/viewvc/llvm-project?rev=277391&view=rev
Log:
[WebAssembly] Add asm.js-style exception handling support
Summary: This patch includes asm.js-style exception handling support for
WebAssembly. The WebAssembly MVP does not have any support for
unwinding or non-local control flow. In order to support C++ exceptions,
emscripten currently uses JavaScript exceptions along with some support
code (written in JavaScript) that is bundled by emscripten with the
generated code.
This scheme lowers exception-related instructions for wasm such that
wasm modules can be compatible with emscripten's existing scheme and
share the support code.
Patch by Heejin Ahn
Differential Revision: https://reviews.llvm.org/D22958
Added:
llvm/trunk/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenExceptions.cpp
llvm/trunk/test/CodeGen/WebAssembly/lower-em-exceptions.ll
Modified:
llvm/trunk/lib/Target/WebAssembly/CMakeLists.txt
llvm/trunk/lib/Target/WebAssembly/WebAssembly.h
llvm/trunk/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
Modified: llvm/trunk/lib/Target/WebAssembly/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Target/WebAssembly/CMakeLists.txt?rev=277391&r1=277390&r2=277391&view=diff
==============================================================================
--- llvm/trunk/lib/Target/WebAssembly/CMakeLists.txt (original)
+++ llvm/trunk/lib/Target/WebAssembly/CMakeLists.txt Mon Aug 1 16:34:04 2016
@@ -20,6 +20,7 @@ add_llvm_target(WebAssemblyCodeGen
WebAssemblyISelLowering.cpp
WebAssemblyInstrInfo.cpp
WebAssemblyLowerBrUnless.cpp
+ WebAssemblyLowerEmscriptenExceptions.cpp
WebAssemblyMachineFunctionInfo.cpp
WebAssemblyMCInstLower.cpp
WebAssemblyOptimizeLiveIntervals.cpp
Modified: llvm/trunk/lib/Target/WebAssembly/WebAssembly.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Target/WebAssembly/WebAssembly.h?rev=277391&r1=277390&r2=277391&view=diff
==============================================================================
--- llvm/trunk/lib/Target/WebAssembly/WebAssembly.h (original)
+++ llvm/trunk/lib/Target/WebAssembly/WebAssembly.h Mon Aug 1 16:34:04 2016
@@ -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.
Added: llvm/trunk/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenExceptions.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenExceptions.cpp?rev=277391&view=auto
==============================================================================
--- llvm/trunk/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenExceptions.cpp (added)
+++ llvm/trunk/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenExceptions.cpp Mon Aug 1 16:34:04 2016
@@ -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<int, Function *> FindMatchingCatches;
+ // Map of <function signature string, invoke_ wrappers>
+ StringMap<Function *> 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<const Function>(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<Type *, 16> 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<Type *, 16> ArgTys;
+ Value *Callee = II->getCalledValue();
+ FunctionType *CalleeFTy;
+ if (auto *F = dyn_cast<Function>(Callee))
+ CalleeFTy = F->getFunctionType();
+ else {
+ auto *CalleeTy = dyn_cast<PointerType>(Callee->getType())->getElementType();
+ CalleeFTy = dyn_cast<FunctionType>(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<Type *, 2> 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<Instruction *, 64> ToErase;
+ SmallPtrSet<LandingPadInst *, 32> LandingPads;
+
+ for (BasicBlock &BB : F) {
+ auto *II = dyn_cast<InvokeInst>(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<Function>(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<Value *, 16> 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<Value *, 16> 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<ResumeInst>(&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<LandingPadInst>(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<Value *, 16> 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<ArrayType>(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;
+}
Modified: llvm/trunk/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp?rev=277391&r1=277390&r2=277391&view=diff
==============================================================================
--- llvm/trunk/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp (original)
+++ llvm/trunk/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp Mon Aug 1 16:34:04 2016
@@ -29,10 +29,20 @@ using namespace llvm;
#define DEBUG_TYPE "wasm"
+// Emscripten's asm.js-style exception handling
+static cl::opt<bool> EnableEmExceptionHandling(
+ "wasm-em-exception-handling",
+ cl::desc("WebAssembly Emscripten-style exception handling"),
+ cl::init(false));
+
extern "C" void LLVMInitializeWebAssemblyTarget() {
// Register the target.
RegisterTargetMachine<WebAssemblyTargetMachine> X(TheWebAssemblyTarget32);
RegisterTargetMachine<WebAssemblyTargetMachine> Y(TheWebAssemblyTarget64);
+
+ // Register exception handling pass to opt
+ initializeWebAssemblyLowerEmscriptenExceptionsPass(
+ *PassRegistry::getPassRegistry());
}
//===----------------------------------------------------------------------===//
@@ -149,6 +159,10 @@ void WebAssemblyPassConfig::addIRPasses(
if (getOptLevel() != CodeGenOpt::None)
addPass(createWebAssemblyOptimizeReturned());
+ // Handle exceptions.
+ if (EnableEmExceptionHandling)
+ addPass(createWebAssemblyLowerEmscriptenExceptions());
+
TargetPassConfig::addIRPasses();
}
Added: llvm/trunk/test/CodeGen/WebAssembly/lower-em-exceptions.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/WebAssembly/lower-em-exceptions.ll?rev=277391&view=auto
==============================================================================
--- llvm/trunk/test/CodeGen/WebAssembly/lower-em-exceptions.ll (added)
+++ llvm/trunk/test/CodeGen/WebAssembly/lower-em-exceptions.ll Mon Aug 1 16:34:04 2016
@@ -0,0 +1,143 @@
+; RUN: opt < %s -wasm-lower-em-exceptions -S | FileCheck %s
+
+ at _ZTIi = external constant i8*
+ at _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: }
More information about the llvm-commits
mailing list