LLVM  8.0.0svn
Macros | Functions | Variables
WebAssemblyLowerEmscriptenEHSjLj.cpp File Reference

This file lowers exception-related instructions and setjmp/longjmp function calls in order to use Emscripten's JavaScript try and catch mechanism. More...

#include "WebAssembly.h"
#include "llvm/IR/CallSite.h"
#include "llvm/IR/Dominators.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
#include "llvm/Transforms/Utils/SSAUpdater.h"
Include dependency graph for WebAssemblyLowerEmscriptenEHSjLj.cpp:

Go to the source code of this file.


#define DEBUG_TYPE   "wasm-lower-em-ehsjlj"


 INITIALIZE_PASS (WebAssemblyLowerEmscriptenEHSjLj, DEBUG_TYPE, "WebAssembly Lower Emscripten Exceptions / Setjmp / Longjmp", false, false) ModulePass *llvm
static bool canThrow (const Value *V)
static GlobalVariablecreateGlobalVariableI32 (Module &M, IRBuilder<> &IRB, const char *Name)
static std::string getSignature (FunctionType *FTy)


static cl::list< std::string > EHWhitelist ("emscripten-cxx-exceptions-whitelist", cl::desc("The list of function names in which Emscripten-style " "exception handling is enabled (see emscripten " "EMSCRIPTEN_CATCHING_WHITELIST options)"), cl::CommaSeparated)

Detailed Description

This file lowers exception-related instructions and setjmp/longjmp function calls in order to use Emscripten's JavaScript try and catch mechanism.

To handle exceptions and setjmp/longjmps, 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 / SjLj scheme, see these files in fastcomp LLVM branch: (Location: https://github.com/kripken/emscripten-fastcomp) lib/Target/JSBackend/NaCl/LowerEmExceptionsPass.cpp lib/Target/JSBackend/NaCl/LowerEmSetjmp.cpp lib/Target/JSBackend/JSBackend.cpp lib/Target/JSBackend/CallHandlers.h

In detail, 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). These variables are used for both exceptions and setjmp/longjmps. __THREW indicates whether an exception or a longjmp occurred or not. 0 means nothing occurred, 1 means an exception occurred, and other numbers mean a longjmp occurred. In the case of longjmp, __threwValue variable indicates the corresponding setjmp buffer the longjmp corresponds to. In exception handling, __tempRet0 indicates the type of an exception caught, and in setjmp/longjmp, it means the second argument to longjmp function.

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 (arg1, arg2) to label invoke.cont unwind label lpad into THREW = 0; call (func, arg1, arg2) __THREW__.val = THREW; THREW = 0; if (__THREW__.val == 1) goto lpad else goto 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 (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 (a) where __resumeException() is a function in JS glue code.

6) Lower call .eh.typeid.for(type) (intrinsic) into call (type) llvm_eh_typeid_for function will be generated in JS glue code.

7) In the function entry that calls setjmp, initialize setjmpTable and sejmpTableSize as follows: setjmpTableSize = 4; setjmpTable = (int *) malloc(40); setjmpTable[0] = 0; setjmpTable and setjmpTableSize are used in saveSetjmp() function in JS code.

8) Lower setjmp(buf) into setjmpTable = saveSetjmp(buf, label, setjmpTable, setjmpTableSize); setjmpTableSize = __tempRet0; For each dynamic setjmp call, setjmpTable stores its ID (a number which is incrementally assigned from 0) and its label (a unique number that represents each callsite of setjmp). When we need more entries in setjmpTable, it is reallocated in saveSetjmp() in JS code and it will return the new table address, and assign the new table size in __tempRet0. saveSetjmp also stores the setjmp's ID into the buffer buf. A BB with setjmp is split into two after setjmp call in order to make the post-setjmp BB the possible destination of longjmp BB.

9) Lower longjmp(buf, value) into emscripten_longjmp_jmpbuf(buf, value) emscripten_longjmp_jmpbuf will be lowered to emscripten_longjmp later.

10) Lower every call that might longjmp into THREW = 0; call (func, arg1, arg2) __THREW__.val = THREW; THREW = 0; if (__THREW__.val != 0 & threwValue != 0) { label = testSetjmp(mem[__THREW.val], setjmpTable, setjmpTableSize); if (label == 0) emscripten_longjmp(__THREW__.val, threwValue); __tempRet0 = __threwValue; } else { label = -1; } longjmp_result = __tempRet0; switch label { label 1: goto post-setjmp BB 1 label 2: goto post-setjmp BB 2 ... default: goto splitted next BB } testSetjmp examines setjmpTable to see if there is a matching setjmp call. After calling an invoke wrapper, if a longjmp occurred, __THREW will be the address of matching jmp_buf buffer and threwValue be the second argument to longjmp. mem[__THREW.val] is a setjmp ID that is stored in saveSetjmp. testSetjmp returns a setjmp label, a unique ID to each setjmp callsite. Label 0 means this longjmp buffer does not correspond to one of the setjmp callsites in this function, so in this case we just chain the longjmp to the caller. (Here we call emscripten_longjmp, which is different from emscripten_longjmp_jmpbuf. emscripten_longjmp_jmpbuf takes jmp_buf as its first argument, while emscripten_longjmp takes an int. Both of them will eventually be lowered to emscripten_longjmp in s2wasm, but here we need two signatures - we can't translate an int value to a jmp_buf.) Label -1 means no longjmp occurred. Otherwise we jump to the right post-setjmp BB based on the label.


Definition in file WebAssemblyLowerEmscriptenEHSjLj.cpp.

Macro Definition Documentation


#define DEBUG_TYPE   "wasm-lower-em-ehsjlj"

Definition at line 217 of file WebAssemblyLowerEmscriptenEHSjLj.cpp.

Function Documentation

◆ canThrow()

static bool canThrow ( const Value V)

Definition at line 321 of file WebAssemblyLowerEmscriptenEHSjLj.cpp.

References F().

Referenced by getSignature().

◆ createGlobalVariableI32()

static GlobalVariable* createGlobalVariableI32 ( Module M,
IRBuilder<> &  IRB,
const char Name 

◆ getSignature()

static std::string getSignature ( FunctionType FTy)

Definition at line 350 of file WebAssemblyLowerEmscriptenEHSjLj.cpp.

References llvm::MCID::Add, llvm::SSAUpdater::AddAvailableValue(), llvm::SwitchInst::addCase(), llvm::PHINode::addIncoming(), llvm::SmallVectorImpl< T >::append(), llvm::AMDGPU::HSAMD::Kernel::Key::Args, assert(), C, Callee, canThrow(), llvm::DenseMapBase< DenseMap< KeyT, ValueT, KeyInfoT, BucketT >, KeyT, ValueT, KeyInfoT, BucketT >::count(), llvm::SmallPtrSetImpl< PtrType >::count(), llvm::BasicBlock::Create(), llvm::Function::Create(), llvm::BinaryOperator::Create(), llvm::IRBuilder< T, Inserter >::CreateAnd(), llvm::IRBuilder< T, Inserter >::CreateBr(), llvm::IRBuilder< T, Inserter >::CreateCall(), llvm::IRBuilder< T, Inserter >::CreateCondBr(), llvm::IRBuilder< T, Inserter >::CreateExtractValue(), llvm::CallInst::CreateFree(), createGlobalVariableI32(), llvm::IRBuilder< T, Inserter >::CreateICmpEQ(), llvm::IRBuilder< T, Inserter >::CreateICmpNE(), llvm::IRBuilder< T, Inserter >::CreateInsertValue(), llvm::IRBuilder< T, Inserter >::CreateIntToPtr(), llvm::IRBuilder< T, Inserter >::CreateLoad(), llvm::CallInst::CreateMalloc(), llvm::IRBuilder< T, Inserter >::CreatePHI(), llvm::IRBuilder< T, Inserter >::CreateRetVoid(), llvm::IRBuilder< T, Inserter >::CreateStore(), llvm::IRBuilder< T, Inserter >::CreateSwitch(), llvm::IRBuilder< T, Inserter >::CreateUnreachable(), llvm::DominatorTree::dominates(), llvm::dyn_cast(), llvm::StringMap< ValueTy, AllocatorTy >::end(), llvm::GlobalVariable::eraseFromParent(), llvm::Function::eraseFromParent(), llvm::GlobalValue::ExternalLinkage, F(), llvm::StringMap< ValueTy, AllocatorTy >::find(), llvm::AttributeList::FunctionIndex, llvm::FunctionType::get(), llvm::AttributeList::get(), llvm::UndefValue::get(), llvm::AttributeList::getAttributes(), llvm::Function::getContext(), llvm::Module::getContext(), llvm::Function::getEntryBlock(), llvm::BasicBlock::getFirstInsertionPt(), llvm::BasicBlock::getFirstNonPHI(), llvm::AttributeList::getFnAttributes(), llvm::Module::getFunction(), llvm::Function::getFunctionType(), llvm::IRBuilderBase::getInt32(), llvm::Type::getInt32PtrTy(), llvm::IRBuilderBase::getInt32Ty(), llvm::Type::getInt32Ty(), llvm::IRBuilderBase::getInt8PtrTy(), llvm::Type::getInt8PtrTy(), llvm::Function::getIntrinsicID(), llvm::Instruction::getModule(), llvm::BasicBlock::getModule(), llvm::Value::getName(), llvm::Module::getNamedGlobal(), llvm::ilist_node_with_parent< NodeTy, ParentTy, Options >::getNextNode(), llvm::AttributeList::getParamAttributes(), llvm::FunctionType::getParamType(), llvm::Instruction::getParent(), llvm::BasicBlock::getParent(), llvm::GlobalValue::getParent(), llvm::AttributeList::getRetAttributes(), llvm::FunctionType::getReturnType(), llvm::BasicBlock::getTerminator(), llvm::Value::getType(), llvm::PointerType::getUnqual(), llvm::Use::getUser(), llvm::IRBuilderBase::getVoidTy(), I, llvm::ARM_PROC::IE, llvm::SSAUpdater::Initialize(), llvm::SmallPtrSetImpl< PtrType >::insert(), llvm::GlobalValue::isDeclaration(), llvm::FunctionType::isVarArg(), llvm::makeArrayRef(), llvm::FunctionType::param_begin(), llvm::FunctionType::param_end(), llvm::FunctionType::params(), llvm::SmallVectorTemplateBase< T, isPodLike< T >::value >::push_back(), llvm::DominatorTreeBase< NodeT, IsPostDom >::recalculate(), llvm::remove_if(), replace(), llvm::Value::replaceAllUsesWith(), llvm::report_fatal_error(), llvm::SSAUpdater::RewriteUse(), llvm::SSAUpdater::RewriteUseAfterInsertions(), llvm::CallBase< InstTy >::setAttributes(), llvm::CallBase< InstTy >::setCallingConv(), llvm::Instruction::setDebugLoc(), llvm::IRBuilderBase::SetInsertPoint(), llvm::Value::setName(), SI, llvm::SmallVectorBase::size(), llvm::BasicBlock::size(), llvm::SplitBlock(), SSA, llvm::StringRef::startswith(), llvm::raw_string_ostream::str(), llvm::Value::takeName(), llvm::RegState::Undef, llvm::Value::use_begin(), llvm::Value::use_empty(), llvm::Value::use_end(), llvm::Value::users(), and llvm::GlobalValue::WeakODRLinkage.

Referenced by llvm::DWARFUnitIndex::getFromHash().


INITIALIZE_PASS ( WebAssemblyLowerEmscriptenEHSjLj  ,
"WebAssembly Lower Emscripten Exceptions / Setjmp / Longjmp"  ,
false  ,

Definition at line 312 of file WebAssemblyLowerEmscriptenEHSjLj.cpp.

Variable Documentation

◆ EHWhitelist

cl::list<std::string> EHWhitelist("emscripten-cxx-exceptions-whitelist", cl::desc("The list of function names in which Emscripten-style " "exception handling is enabled (see emscripten " "EMSCRIPTEN_CATCHING_WHITELIST options)"), cl::CommaSeparated)