diff options
author | Alon Zakai <alonzakai@gmail.com> | 2014-01-09 15:56:56 -0800 |
---|---|---|
committer | Alon Zakai <alonzakai@gmail.com> | 2014-01-09 16:04:25 -0800 |
commit | 245715440fb27520b37e27ad1c62c90acc1abc20 (patch) | |
tree | 4d18aee6379753a994bbcf14be642485405c80ec | |
parent | a78b08fd3d9f5ad0adaa5336411fec34a34c3bf0 (diff) |
exception handling support
-rw-r--r-- | include/llvm/InitializePasses.h | 3 | ||||
-rw-r--r-- | include/llvm/Transforms/NaCl.h | 3 | ||||
-rw-r--r-- | lib/Target/JSBackend/CallHandlers.h | 96 | ||||
-rw-r--r-- | lib/Target/JSBackend/JSBackend.cpp | 33 | ||||
-rw-r--r-- | lib/Transforms/NaCl/CMakeLists.txt | 1 | ||||
-rw-r--r-- | lib/Transforms/NaCl/LowerEmExceptionsPass.cpp | 118 | ||||
-rw-r--r-- | lib/Transforms/NaCl/PNaClABISimplify.cpp | 7 |
7 files changed, 227 insertions, 34 deletions
diff --git a/include/llvm/InitializePasses.h b/include/llvm/InitializePasses.h index 1e2d6203fb..3e0107944d 100644 --- a/include/llvm/InitializePasses.h +++ b/include/llvm/InitializePasses.h @@ -305,7 +305,8 @@ void initializeRewriteLLVMIntrinsicsPass(PassRegistry&); void initializeRewritePNaClLibraryCallsPass(PassRegistry&); void initializeStripAttributesPass(PassRegistry&); void initializeStripMetadataPass(PassRegistry&); -void initializeExpandI64Pass(PassRegistry&); // EMSCRIPTEN +void initializeExpandI64Pass(PassRegistry&); // XXX EMSCRIPTEN +void initializeLowerEmExceptionsPass(PassRegistry&); // XXX EMSCRIPTEN // @LOCALMOD-END } diff --git a/include/llvm/Transforms/NaCl.h b/include/llvm/Transforms/NaCl.h index 429f5a1fe1..03d22c994d 100644 --- a/include/llvm/Transforms/NaCl.h +++ b/include/llvm/Transforms/NaCl.h @@ -50,7 +50,8 @@ ModulePass *createRewritePNaClLibraryCallsPass(); ModulePass *createStripAttributesPass(); ModulePass *createStripMetadataPass(); -ModulePass *createExpandI64Pass(); // EMSCRIPTEN +ModulePass *createExpandI64Pass(); // XXX EMSCRIPTEN +ModulePass *createLowerEmExceptionsPass(); // XXX EMSCRIPTEN void PNaClABISimplifyAddPreOptPasses(PassManager &PM); void PNaClABISimplifyAddPostOptPasses(PassManager &PM); diff --git a/lib/Target/JSBackend/CallHandlers.h b/lib/Target/JSBackend/CallHandlers.h index 221421a80a..aa4a1a7c93 100644 --- a/lib/Target/JSBackend/CallHandlers.h +++ b/lib/Target/JSBackend/CallHandlers.h @@ -2,14 +2,27 @@ // // Each handler needs DEF_CALL_HANDLER and SETUP_CALL_HANDLER -typedef std::string (JSWriter::*CallHandler)(const CallInst*, std::string Name, int NumArgs); +typedef std::string (JSWriter::*CallHandler)(const Instruction*, std::string Name, int NumArgs); typedef std::map<std::string, CallHandler> CallHandlerMap; CallHandlerMap *CallHandlers; // Definitions -const Value *getActuallyCalledValue(const CallInst *CI) { - const Value *CV = CI->getCalledValue(); +unsigned getNumArgOperands(const Instruction *I) { + if (const CallInst *CI = dyn_cast<const CallInst>(I)) { + return CI->getNumArgOperands(); + } else { + return cast<const InvokeInst>(I)->getNumArgOperands(); + } +} + +const Value *getActuallyCalledValue(const Instruction *I) { + const Value *CV = NULL; + if (const CallInst *CI = dyn_cast<const CallInst>(I)) { + CV = CI->getCalledValue(); + } else { + CV = cast<const InvokeInst>(I)->getCalledValue(); + } if (const BitCastInst *B = dyn_cast<const BitCastInst>(CV)) { // if the called value is a bitcast of a function, then we just call it directly, properly // for example, extern void x() in C will turn into void x(...) in LLVM IR, then the IR bitcasts @@ -23,32 +36,55 @@ const Value *getActuallyCalledValue(const CallInst *CI) { } #define DEF_CALL_HANDLER(Ident, Code) \ - std::string CH_##Ident(const CallInst *CI, std::string Name, int NumArgs=-1) { Code } + std::string CH_##Ident(const Instruction *CI, std::string Name, int NumArgs=-1) { Code } DEF_CALL_HANDLER(__default__, { const Value *CV = getActuallyCalledValue(CI); bool NeedCasts; FunctionType *FT; - if (const Function *F = dyn_cast<const Function>(CV)) { + bool Invoke = false; + if (isa<const InvokeInst>(CI)) { + // invoke of f(a, b) turns into invoke_sig(index-of-f, a, b) + Invoke = true; + } + std::string Sig; + const Function *F = dyn_cast<const Function>(CV); + if (F) { NeedCasts = F->isDeclaration(); // if ffi call, need casts FT = F->getFunctionType(); } else { // function pointer call FT = dyn_cast<FunctionType>(dyn_cast<PointerType>(CV->getType())->getElementType()); - std::string Sig = getFunctionSignature(FT); - Name = std::string("FUNCTION_TABLE_") + Sig + "[" + Name + " & #FM_" + Sig + "#]"; ensureFunctionTable(FT); - NeedCasts = false; // function table call, so stays in asm module + if (!Invoke) { + Sig = getFunctionSignature(FT); + Name = std::string("FUNCTION_TABLE_") + Sig + "[" + Name + " & #FM_" + Sig + "#]"; + NeedCasts = false; // function table call, so stays in asm module + } + } + if (Invoke) { + Sig = getFunctionSignature(FT); + Name = "invoke_" + Sig; + NeedCasts = true; } std::string text = Name + "("; - if (NumArgs == -1) NumArgs = CI->getNumOperands()-1; // last operand is the function itself + if (NumArgs == -1) NumArgs = getNumArgOperands(CI); + if (Invoke) { + // add first param + if (F) { + text += utostr(getFunctionIndex(F)); // convert to function pointer + } else { + text += getValueAsStr(CV); // already a function pointer + } + if (NumArgs > 0) text += ","; + } for (int i = 0; i < NumArgs; i++) { if (!NeedCasts) { - text += getValueAsStr(CI->getArgOperand(i)); + text += getValueAsStr(CI->getOperand(i)); } else { - text += getValueAsCastStr(CI->getArgOperand(i), ASM_NONSPECIFIC); + text += getValueAsCastStr(CI->getOperand(i), ASM_NONSPECIFIC); } - if (i < NumArgs - 1) text += ", "; + if (i < NumArgs - 1) text += ","; } text += ")"; // handle return value @@ -70,20 +106,20 @@ DEF_CALL_HANDLER(getHigh32, { }) DEF_CALL_HANDLER(setHigh32, { - return "tempRet0 = " + getValueAsStr(CI->getArgOperand(0)); + return "tempRet0 = " + getValueAsStr(CI->getOperand(0)); }) DEF_CALL_HANDLER(FPtoILow, { - return getAssign(getJSName(CI), CI->getType()) + "(~~" + getValueAsStr(CI->getArgOperand(0)) + ")>>>0"; + return getAssign(getJSName(CI), CI->getType()) + "(~~" + getValueAsStr(CI->getOperand(0)) + ")>>>0"; }) DEF_CALL_HANDLER(FPtoIHigh, { - std::string Input = getValueAsStr(CI->getArgOperand(0)); + std::string Input = getValueAsStr(CI->getOperand(0)); return getAssign(getJSName(CI), CI->getType()) + "+Math_abs(" + Input + ") >= +1 ? " + Input + " > +0 ? (Math_min(+Math_floor(" + Input + " / +4294967296), +4294967295) | 0) >>> 0 : ~~+Math_ceil((" + Input + " - +(~~" + Input + " >>> 0)) / +4294967296) >>> 0 : 0"; }) DEF_CALL_HANDLER(BDtoILow, { - return "HEAPF64[tempDoublePtr>>3] = " + getValueAsStr(CI->getArgOperand(0)) + ";" + getAssign(getJSName(CI), CI->getType()) + "HEAP32[tempDoublePtr>>2]|0"; + return "HEAPF64[tempDoublePtr>>3] = " + getValueAsStr(CI->getOperand(0)) + ";" + getAssign(getJSName(CI), CI->getType()) + "HEAP32[tempDoublePtr>>2]|0"; }) DEF_CALL_HANDLER(BDtoIHigh, { @@ -92,34 +128,34 @@ DEF_CALL_HANDLER(BDtoIHigh, { DEF_CALL_HANDLER(SItoF, { // TODO: fround - return getAssign(getJSName(CI), CI->getType()) + "(+" + getValueAsCastParenStr(CI->getArgOperand(0), ASM_UNSIGNED) + ") + " + - "(+4294967296*(+" + getValueAsCastParenStr(CI->getArgOperand(1), ASM_SIGNED) + "))"; + return getAssign(getJSName(CI), CI->getType()) + "(+" + getValueAsCastParenStr(CI->getOperand(0), ASM_UNSIGNED) + ") + " + + "(+4294967296*(+" + getValueAsCastParenStr(CI->getOperand(1), ASM_SIGNED) + "))"; }) DEF_CALL_HANDLER(UItoF, { // TODO: fround - return getAssign(getJSName(CI), CI->getType()) + "(+" + getValueAsCastParenStr(CI->getArgOperand(0), ASM_UNSIGNED) + ") + " + - "(+4294967296*(+" + getValueAsCastParenStr(CI->getArgOperand(1), ASM_UNSIGNED) + "))"; + return getAssign(getJSName(CI), CI->getType()) + "(+" + getValueAsCastParenStr(CI->getOperand(0), ASM_UNSIGNED) + ") + " + + "(+4294967296*(+" + getValueAsCastParenStr(CI->getOperand(1), ASM_UNSIGNED) + "))"; }) DEF_CALL_HANDLER(SItoD, { - return getAssign(getJSName(CI), CI->getType()) + "(+" + getValueAsCastParenStr(CI->getArgOperand(0), ASM_UNSIGNED) + ") + " + - "(+4294967296*(+" + getValueAsCastParenStr(CI->getArgOperand(1), ASM_SIGNED) + "))"; + return getAssign(getJSName(CI), CI->getType()) + "(+" + getValueAsCastParenStr(CI->getOperand(0), ASM_UNSIGNED) + ") + " + + "(+4294967296*(+" + getValueAsCastParenStr(CI->getOperand(1), ASM_SIGNED) + "))"; }) DEF_CALL_HANDLER(UItoD, { - return getAssign(getJSName(CI), CI->getType()) + "(+" + getValueAsCastParenStr(CI->getArgOperand(0), ASM_UNSIGNED) + ") + " + - "(+4294967296*(+" + getValueAsCastParenStr(CI->getArgOperand(1), ASM_UNSIGNED) + "))"; + return getAssign(getJSName(CI), CI->getType()) + "(+" + getValueAsCastParenStr(CI->getOperand(0), ASM_UNSIGNED) + ") + " + + "(+4294967296*(+" + getValueAsCastParenStr(CI->getOperand(1), ASM_UNSIGNED) + "))"; }) DEF_CALL_HANDLER(BItoD, { - return "HEAP32[tempDoublePtr>>2] = " + getValueAsStr(CI->getArgOperand(0)) + ";" + - "HEAP32[tempDoublePtr+4>>2] = " + getValueAsStr(CI->getArgOperand(1)) + ";" + + return "HEAP32[tempDoublePtr>>2] = " + getValueAsStr(CI->getOperand(0)) + ";" + + "HEAP32[tempDoublePtr+4>>2] = " + getValueAsStr(CI->getOperand(1)) + ";" + getAssign(getJSName(CI), CI->getType()) + "+HEAPF64[tempDoublePtr>>3]"; }) DEF_CALL_HANDLER(llvm_nacl_atomic_store_i32, { - return "HEAP32[" + getValueAsStr(CI->getArgOperand(0)) + ">>2]=" + getValueAsStr(CI->getArgOperand(1)); + return "HEAP32[" + getValueAsStr(CI->getOperand(0)) + ">>2]=" + getValueAsStr(CI->getOperand(1)); }) DEF_CALL_HANDLER(llvm_memcpy_p0i8_p0i8_i32, { @@ -138,7 +174,7 @@ DEF_CALL_HANDLER(llvm_memmove_p0i8_p0i8_i32, { }) DEF_CALL_HANDLER(llvm_expect_i32, { - return getAssign(getJSName(CI), CI->getType()) + getValueAsStr(CI->getArgOperand(0)); + return getAssign(getJSName(CI), CI->getType()) + getValueAsStr(CI->getOperand(0)); }) DEF_CALL_HANDLER(llvm_dbg_declare, { @@ -817,12 +853,12 @@ void setupCallHandlers() { SETUP_CALL_HANDLER(SDL_RWFromMem); } -std::string handleCall(const CallInst *CI) { +std::string handleCall(const Instruction *CI) { const Value *CV = getActuallyCalledValue(CI); assert(!isa<InlineAsm>(CV) && "asm() not supported, use EM_ASM() (see emscripten.h)"); std::string Name = getJSName(CV); if (strcmp(Name.c_str(), "_llvm_dbg_value") == 0) return ""; // ignore this - unsigned NumArgs = CI->getNumArgOperands(); + unsigned NumArgs = getNumArgOperands(CI); CallHandlerMap::iterator CH = CallHandlers->find("___default__"); if (isa<Function>(CV)) { CallHandlerMap::iterator Custom = CallHandlers->find(Name); diff --git a/lib/Target/JSBackend/JSBackend.cpp b/lib/Target/JSBackend/JSBackend.cpp index 7fb4208744..2cc66c3a4b 100644 --- a/lib/Target/JSBackend/JSBackend.cpp +++ b/lib/Target/JSBackend/JSBackend.cpp @@ -237,6 +237,7 @@ namespace { } } std::string getPtrAsStr(const Value* Ptr) { + if (isa<const ConstantPointerNull>(Ptr)) return "0"; if (const Function *F = dyn_cast<Function>(Ptr)) { return utostr(getFunctionIndex(F)); } else if (const Constant *CV = dyn_cast<Constant>(Ptr)) { @@ -941,8 +942,8 @@ void JSWriter::generateInstruction(const Instruction *I, raw_string_ostream& Cod case Instruction::Br: case Instruction::Switch: break; // handled while relooping case Instruction::Unreachable: { - // No need to emit anything, as there should be an abort right before these - // Code << "abort();"; + // Typically there should be an abort right before these, so we don't emit any code // TODO: when ASSERTIONS are on, emit abort(0) + Code << "// unreachable"; break; } case Instruction::Add: @@ -1237,6 +1238,24 @@ void JSWriter::generateInstruction(const Instruction *I, raw_string_ostream& Cod Code << ";"; break; } + case Instruction::Invoke: { + Code << "__THREW__ = 0;"; + const InvokeInst *II = cast<InvokeInst>(I); + Code << handleCall(II) + ';'; + // the check and branch and done in the relooper setup code + break; + } + case Instruction::LandingPad: { + const LandingPadInst *LP = cast<const LandingPadInst>(I); + Code << getAssign(iName, I->getType()); + Code << "___cxa_find_matching_catch(-1,-1"; + unsigned n = LP->getNumClauses(); + for (unsigned i = 0; i < n; i++) { + Code << "," + getValueAsStr(LP->getClause(i)); + } + Code << ")|0;"; + break; + } } // append debug info if (MDNode *N = I->getMetadata("dbg")) { @@ -1357,6 +1376,16 @@ void JSWriter::printFunctionBody(const Function *F) { } break; } + case Instruction::Invoke: { + const InvokeInst* II = cast<InvokeInst>(TI); + BasicBlock *S0 = II->getNormalDest(); + BasicBlock *S1 = II->getUnwindDest(); + std::string P0 = getPhiCode(&*BI, S0); + std::string P1 = getPhiCode(&*BI, S1); + LLVMToRelooper[&*BI]->AddBranchTo(LLVMToRelooper[&*S0], "!__THREW__", P0.size() > 0 ? P0.c_str() : NULL); + LLVMToRelooper[&*BI]->AddBranchTo(LLVMToRelooper[&*S1], NULL, P1.size() > 0 ? P1.c_str() : NULL); + break; + } case Instruction::Ret: case Instruction::Unreachable: break; } diff --git a/lib/Transforms/NaCl/CMakeLists.txt b/lib/Transforms/NaCl/CMakeLists.txt index a344a163b7..9d97378638 100644 --- a/lib/Transforms/NaCl/CMakeLists.txt +++ b/lib/Transforms/NaCl/CMakeLists.txt @@ -29,6 +29,7 @@ add_llvm_library(LLVMNaClTransforms StripAttributes.cpp StripMetadata.cpp ExpandI64.cpp + LowerEmExceptions.cpp ) add_dependencies(LLVMNaClTransforms intrinsics_gen) diff --git a/lib/Transforms/NaCl/LowerEmExceptionsPass.cpp b/lib/Transforms/NaCl/LowerEmExceptionsPass.cpp new file mode 100644 index 0000000000..3690537c4e --- /dev/null +++ b/lib/Transforms/NaCl/LowerEmExceptionsPass.cpp @@ -0,0 +1,118 @@ +//===- LowerEmExceptions - Lower exceptions for Emscripten/JS -----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This is based off the 'cheap' version of LowerInvoke. It does two things: +// +// 1) Lower +// invoke() to l1 unwind l2 +// into +// preinvoke(); // (will clear __THREW__) +// call(); +// threw = postinvoke(); (check __THREW__) +// br threw, l1, l2 +// +// 2) Lower landingpads to return a single i8*, avoid the structural type +// which is unneeded anyhow. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Transforms/Scalar.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Intrinsics.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/Pass.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Target/TargetLowering.h" +#include "llvm/Transforms/Utils/BasicBlockUtils.h" +#include "llvm/Transforms/Utils/Local.h" +#include "llvm/Transforms/NaCl.h" + +using namespace llvm; + +namespace { + class LowerEmExceptions : public ModulePass { + Function *PreInvoke, *PostInvoke; + Module *TheModule; + + public: + static char ID; // Pass identification, replacement for typeid + explicit LowerEmExceptions() : ModulePass(ID), PreInvoke(NULL), PostInvoke(NULL), TheModule(NULL) { + initializeLowerEmExceptionsPass(*PassRegistry::getPassRegistry()); + } + bool runOnModule(Module &M); + }; +} + +char LowerEmExceptions::ID = 0; +INITIALIZE_PASS(LowerEmExceptions, "loweremexceptions", + "Lower invoke and unwind for js/emscripten", + false, false) + +Instruction *getSingleUse(Instruction *I) { + Instruction *Ret = NULL; + for (Instruction::use_iterator UI = I->use_begin(), UE = I->use_end(); UI != UE; ++UI) { + assert(Ret == NULL); + Ret = cast<ExtractElementInst>(*UI); + } + assert(Ret != NULL); + return Ret; +} + +bool LowerEmExceptions::runOnModule(Module &M) { + TheModule = &M; + + Type *Void = Type::getVoidTy(M.getContext()); + Type *i32 = Type::getInt32Ty(M.getContext()); + + SmallVector<Type*, 0> ArgTypes; + FunctionType *VoidFunc = FunctionType::get(Void, ArgTypes, false); + FunctionType *IntFunc = FunctionType::get(i32, ArgTypes, false); + + PreInvoke = Function::Create(VoidFunc, GlobalValue::ExternalLinkage, "preInvoke", TheModule); + PostInvoke = Function::Create(IntFunc, GlobalValue::ExternalLinkage, "postInvoke", TheModule); + + bool Changed = false; + + for (Module::iterator Iter = M.begin(), E = M.end(); Iter != E; ) { + Function *F = Iter++; + for (Function::iterator BB = F->begin(), E = F->end(); BB != E; ++BB) { + if (InvokeInst *II = dyn_cast<InvokeInst>(BB->getTerminator())) { + // Fix up the landingpad. First, make a copy returning just an integer + LandingPadInst *LP = II->getLandingPadInst(); + unsigned Num = LP->getNumClauses(); + LandingPadInst *NewLP = LandingPadInst::Create(i32, LP->getPersonalityFn(), Num, "", LP); + NewLP->setCleanup(LP->isCleanup()); + for (unsigned i = 0; i < Num; i++) NewLP->addClause(LP->getClause(i)); + + // Next, replace the old LP's single use, which is an extractelement, to eliminate the ee's and use the value directly + ExtractElementInst *EE = cast<ExtractElementInst>(getSingleUse(LP)); + EE->replaceAllUsesWith(NewLP); + EE->eraseFromParent(); + + // Finish the LP by replacing it + LP->replaceAllUsesWith(NewLP); + LP->eraseFromParent(); + + Changed = true; + } + } + } + + return Changed; +} + +ModulePass *llvm::createLowerEmExceptionsPass() { + return new LowerEmExceptions(); +} + diff --git a/lib/Transforms/NaCl/PNaClABISimplify.cpp b/lib/Transforms/NaCl/PNaClABISimplify.cpp index 38d8ae0a40..ab88be133c 100644 --- a/lib/Transforms/NaCl/PNaClABISimplify.cpp +++ b/lib/Transforms/NaCl/PNaClABISimplify.cpp @@ -27,6 +27,11 @@ EnableSjLjEH("enable-pnacl-sjlj-eh", "as part of the pnacl-abi-simplify passes"), cl::init(false)); +static cl::opt<bool> // XXX EMSCRIPTEN +EnableEmCxxExceptions("enable-emscripten-cxx-exceptions", + cl::desc("Enables C++ exceptions in emscripten"), + cl::init(false)); + void llvm::PNaClABISimplifyAddPreOptPasses(PassManager &PM) { if (EnableSjLjEH) { // This comes before ExpandTls because it introduces references to @@ -34,6 +39,8 @@ void llvm::PNaClABISimplifyAddPreOptPasses(PassManager &PM) { // InternalizePass because it assumes various variables (including // __pnacl_eh_stack) have not been internalized yet. PM.add(createPNaClSjLjEHPass()); + } else if (EnableEmCxxExceptions) { // XXX EMSCRIPTEN + PM.add(createLowerEmExceptionsPass()); } else { // LowerInvoke prevents use of C++ exception handling by removing // references to BasicBlocks which handle exceptions. |