From 53b9af02c8437c03508f5fb34d5902352300bf9a Mon Sep 17 00:00:00 2001 From: Derek Schuff Date: Tue, 9 Aug 2016 00:29:55 +0000 Subject: [PATCH] [WebAssembly] Fix bugs in WebAssemblyLowerEmscriptenExceptions pass * Delete extra '_' prefixes from JS library function names. fixImports() function in JS glue code deals with this for wasm. * Change command-line option names in order to be consistent with asm.js. * Add missing lowering code for llvm.eh.typeid.for intrinsics * Delete commas in mangled function names * Fix a function argument attributes bug. Because we add the pointer to the original callee as the first argument of invoke wrapper, all argument attribute indices have to be incremented by one. Patch by Heejin Ahn Differential Revision: https://reviews.llvm.org/D23258 llvm-svn: 278081 --- .../WebAssemblyLowerEmscriptenExceptions.cpp | 125 +++++++++++++++------ .../WebAssembly/WebAssemblyTargetMachine.cpp | 2 +- .../CodeGen/WebAssembly/lower-em-exceptions.ll | 58 +++++++++- 3 files changed, 145 insertions(+), 40 deletions(-) diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenExceptions.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenExceptions.cpp index 3e3178e..c906767 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenExceptions.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenExceptions.cpp @@ -24,7 +24,7 @@ /// 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 +/// 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). /// @@ -77,31 +77,33 @@ /// %val = landingpad catch c1 catch c2 catch c3 ... /// ... use %val ... /// into -/// %fmc = call @___cxa_find_matching_catch_N(c1, c2, c3, ...) +/// %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 +/// 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. +/// call @__resumeException(%a) +/// where __resumeException() is a function in JS glue code. /// -/// TODO: Handle i64 types +/// 6) Lower +/// call @llvm.eh.typeid.for(type) (intrinsic) +/// into +/// call @llvm_eh_typeid_for(type) +/// llvm_eh_typeid_for function will be generated in JS glue code. /// ///===----------------------------------------------------------------------===// #include "WebAssembly.h" -#include "llvm/ADT/IndexedMap.h" -#include "llvm/IR/Constants.h" +#include "llvm/IR/CallSite.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" +#include using namespace llvm; @@ -114,9 +116,9 @@ class WebAssemblyLowerEmscriptenExceptions final : public ModulePass { } bool runOnFunction(Function &F); - // Returns ___cxa_find_matching_catch_N function, where N = NumClauses + 2. + // 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 + // 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); @@ -126,8 +128,9 @@ class WebAssemblyLowerEmscriptenExceptions final : public ModulePass { GlobalVariable *ThrewGV; // __THREW__ GlobalVariable *ThrewValueGV; // threwValue GlobalVariable *TempRet0GV; // tempRet0 - Function *ResumeF; - // ___cxa_find_matching_catch_N functions. + Function *ResumeF; // __resumeException + Function *EHTypeIdF; // llvm_eh_typeid_for + // __cxa_find_matching_catch_N functions. // Indexed by the number of clauses in an original landingpad instruction. DenseMap FindMatchingCatches; // Map of @@ -178,9 +181,9 @@ static inline std::string createGlobalValueName(const Module &M, // 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. +// and 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); @@ -191,6 +194,9 @@ static std::string getSignature(FunctionType *FTy) { OS << "_..."; Sig = OS.str(); Sig.erase(std::remove_if(Sig.begin(), Sig.end(), isspace), Sig.end()); + // When s2wasm parses .s file, a comma means the end of an argument. So a + // mangled function name can contain any character but a comma. + std::replace(Sig.begin(), Sig.end(), ',', '.'); return Sig; } @@ -203,7 +209,7 @@ Function *WebAssemblyLowerEmscriptenExceptions::getFindMatchingCatch( FunctionType *FTy = FunctionType::get(Int8PtrTy, Args, false); Function *F = Function::Create( FTy, GlobalValue::ExternalLinkage, - "___cxa_find_matching_catch_" + Twine(NumClauses + 2), &M); + "__cxa_find_matching_catch_" + Twine(NumClauses + 2), &M); FindMatchingCatches[NumClauses] = F; return F; } @@ -239,10 +245,12 @@ WebAssemblyLowerEmscriptenExceptions::getInvokeWrapper(Module &M, } bool WebAssemblyLowerEmscriptenExceptions::runOnModule(Module &M) { - IRBuilder<> Builder(M.getContext()); + LLVMContext &C = M.getContext(); + IRBuilder<> Builder(C); IntegerType *Int1Ty = Builder.getInt1Ty(); PointerType *Int8PtrTy = Builder.getInt8PtrTy(); IntegerType *Int32Ty = Builder.getInt32Ty(); + Type *VoidTy = Builder.getVoidTy(); // Create global variables __THREW__, threwValue, and tempRet0 ThrewGV = new GlobalVariable(M, Int1Ty, false, GlobalValue::ExternalLinkage, @@ -255,11 +263,15 @@ bool WebAssemblyLowerEmscriptenExceptions::runOnModule(Module &M) { M, Int32Ty, false, GlobalValue::ExternalLinkage, Builder.getInt32(0), createGlobalValueName(M, "tempRet0")); - // Register ___resumeException function - Type *VoidTy = Type::getVoidTy(M.getContext()); + // Register __resumeException function FunctionType *ResumeFTy = FunctionType::get(VoidTy, Int8PtrTy, false); ResumeF = Function::Create(ResumeFTy, GlobalValue::ExternalLinkage, - "___resumeException", &M); + "__resumeException", &M); + + // Register llvm_eh_typeid_for function + FunctionType *EHTypeIdTy = FunctionType::get(Int32Ty, Int8PtrTy, false); + EHTypeIdF = Function::Create(EHTypeIdTy, GlobalValue::ExternalLinkage, + "llvm_eh_typeid_for", &M); bool Changed = false; for (Function &F : M) { @@ -283,9 +295,9 @@ bool WebAssemblyLowerEmscriptenExceptions::runOnModule(Module &M) { 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); + BasicBlock *EntryBB = BasicBlock::Create(C, "entry", F); + BasicBlock *ThenBB = BasicBlock::Create(C, "if.then", F); + BasicBlock *EndBB = BasicBlock::Create(C, "if.end", F); Builder.SetInsertPoint(EntryBB); Value *Threw = Builder.CreateLoad(ThrewGV, ThrewGV->getName() + ".val"); @@ -305,7 +317,7 @@ bool WebAssemblyLowerEmscriptenExceptions::runOnModule(Module &M) { 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); + EntryBB = BasicBlock::Create(C, "entry", F); Builder.SetInsertPoint(EntryBB); Builder.CreateStore(&*F->arg_begin(), TempRet0GV); Builder.CreateRetVoid(); @@ -315,10 +327,12 @@ bool WebAssemblyLowerEmscriptenExceptions::runOnModule(Module &M) { bool WebAssemblyLowerEmscriptenExceptions::runOnFunction(Function &F) { Module &M = *F.getParent(); - IRBuilder<> Builder(M.getContext()); + LLVMContext &C = F.getContext(); + IRBuilder<> Builder(C); bool Changed = false; SmallVector ToErase; SmallPtrSet LandingPads; + bool AllowExceptions = true; // will later change based on whitelist option for (BasicBlock &BB : F) { auto *II = dyn_cast(BB.getTerminator()); @@ -328,7 +342,8 @@ bool WebAssemblyLowerEmscriptenExceptions::runOnFunction(Function &F) { LandingPads.insert(II->getLandingPadInst()); Builder.SetInsertPoint(II); - if (canThrow(II->getCalledValue())) { + bool NeedInvoke = AllowExceptions && canThrow(II->getCalledValue()); + if (NeedInvoke) { // 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. @@ -336,7 +351,7 @@ bool WebAssemblyLowerEmscriptenExceptions::runOnFunction(Function &F) { if (auto *F = dyn_cast(II->getCalledValue())) F->removeFnAttr(Attribute::NoReturn); AttributeSet NewAttrs = II->getAttributes(); - NewAttrs.removeAttribute(M.getContext(), AttributeSet::FunctionIndex, + NewAttrs.removeAttribute(C, AttributeSet::FunctionIndex, Attribute::NoReturn); II->setAttributes(NewAttrs); } @@ -354,8 +369,32 @@ bool WebAssemblyLowerEmscriptenExceptions::runOnFunction(Function &F) { CallInst *NewCall = Builder.CreateCall(getInvokeWrapper(M, II), CallArgs); NewCall->takeName(II); NewCall->setCallingConv(II->getCallingConv()); - NewCall->setAttributes(II->getAttributes()); NewCall->setDebugLoc(II->getDebugLoc()); + + // Because we added the pointer to the callee as first argument, all + // argument attribute indices have to be incremented by one. + SmallVector AttributesVec; + const AttributeSet &InvokePAL = II->getAttributes(); + CallSite::arg_iterator AI = II->arg_begin(); + unsigned i = 1; // Argument attribute index starts from 1 + for (unsigned e = II->getNumArgOperands(); i <= e; ++AI, ++i) { + if (InvokePAL.hasAttributes(i)) { + AttrBuilder B(InvokePAL, i); + AttributesVec.push_back(AttributeSet::get(C, i + 1, B)); + } + } + // Add any return attributes. + if (InvokePAL.hasAttributes(AttributeSet::ReturnIndex)) + AttributesVec.push_back( + AttributeSet::get(C, InvokePAL.getRetAttributes())); + // Add any function attributes. + if (InvokePAL.hasAttributes(AttributeSet::FunctionIndex)) + AttributesVec.push_back( + AttributeSet::get(C, InvokePAL.getFnAttributes())); + // Reconstruct the AttributesList based on the vector we constructed. + AttributeSet NewCallPAL = AttributeSet::get(C, AttributesVec); + NewCall->setAttributes(NewCallPAL); + II->replaceAllUsesWith(NewCall); ToErase.push_back(II); @@ -374,8 +413,8 @@ bool WebAssemblyLowerEmscriptenExceptions::runOnFunction(Function &F) { CallInst *NewCall = Builder.CreateCall(II->getCalledValue(), CallArgs); NewCall->takeName(II); NewCall->setCallingConv(II->getCallingConv()); - NewCall->setAttributes(II->getAttributes()); NewCall->setDebugLoc(II->getDebugLoc()); + NewCall->setAttributes(II->getAttributes()); II->replaceAllUsesWith(NewCall); ToErase.push_back(II); @@ -399,7 +438,7 @@ bool WebAssemblyLowerEmscriptenExceptions::runOnFunction(Function &F) { Builder.SetInsertPoint(RI); Value *Low = Builder.CreateExtractValue(Input, 0, "low"); - // Create a call to ___resumeException function + // Create a call to __resumeException function Value *Args[] = {Low}; Builder.CreateCall(ResumeF, Args); @@ -409,6 +448,26 @@ bool WebAssemblyLowerEmscriptenExceptions::runOnFunction(Function &F) { } } + // Process llvm.eh.typeid.for intrinsics + for (BasicBlock &BB : F) { + for (Instruction &I : BB) { + auto *CI = dyn_cast(&I); + if (!CI) + continue; + const Function *Callee = CI->getCalledFunction(); + if (!Callee) + continue; + if (Callee->getIntrinsicID() != Intrinsic::eh_typeid_for) + continue; + + Builder.SetInsertPoint(CI); + CallInst *NewCI = + Builder.CreateCall(EHTypeIdF, CI->getArgOperand(0), "typeid"); + CI->replaceAllUsesWith(NewCI); + ToErase.push_back(CI); + } + } + // Look for orphan landingpads, can occur in blocks with no predecesors for (BasicBlock &BB : F) { Instruction *I = BB.getFirstNonPHI(); @@ -437,7 +496,7 @@ bool WebAssemblyLowerEmscriptenExceptions::runOnFunction(Function &F) { FMCArgs.push_back(Clause); } - // Create a call to ___cxa_find_matching_catch_N function + // 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()); diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp index 83ef03a..e442cb1 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp @@ -31,7 +31,7 @@ using namespace llvm; // Emscripten's asm.js-style exception handling static cl::opt EnableEmExceptionHandling( - "wasm-em-exception-handling", + "enable-emscripten-cxx-exceptions", cl::desc("WebAssembly Emscripten-style exception handling"), cl::init(false)); diff --git a/llvm/test/CodeGen/WebAssembly/lower-em-exceptions.ll b/llvm/test/CodeGen/WebAssembly/lower-em-exceptions.ll index 247118b..5e9431d 100644 --- a/llvm/test/CodeGen/WebAssembly/lower-em-exceptions.ll +++ b/llvm/test/CodeGen/WebAssembly/lower-em-exceptions.ll @@ -30,17 +30,20 @@ lpad: ; preds = %entry %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: %[[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 +; CHECK-NEXT: %[[CDR:.*]] = 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 +; CHECK: catch.dispatch: +; CHECK-NEXT: %[[TYPEID:.*]] = call i32 @llvm_eh_typeid_for(i8* bitcast (i8** @_ZTIi to i8*)) +; CHECK-NEXT: %matches = icmp eq i32 %[[CDR]], %[[TYPEID]] catch1: ; preds = %catch.dispatch %4 = call i8* @__cxa_begin_catch(i8* %1) @@ -81,7 +84,7 @@ lpad: ; preds = %entry %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: %[[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 @@ -104,11 +107,54 @@ eh.resume: ; preds = %filter.dispatch ; 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: call void @__resumeException(i8* %[[LOW]]) ; CHECK-NEXT: unreachable } +; Test if argument attributes indices in newly created call instructions are correct +define void @arg_attributes() personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) { +; CHECK-LABEL: @arg_attributes( +entry: + %0 = invoke noalias i8* @bar(i8 signext 1, i8 zeroext 2) + to label %invoke.cont unwind label %lpad +; CHECK: entry: +; CHECK-NEXT: store i1 false, i1* @[[__THREW__]] +; CHECK-NEXT: %0 = call noalias i8* @"__invoke_i8*_i8_i8"(i8* (i8, i8)* @bar, i8 signext 1, i8 zeroext 2) + +invoke.cont: ; preds = %entry + br label %try.cont + +lpad: ; preds = %entry + %1 = landingpad { i8*, i32 } + catch i8* bitcast (i8** @_ZTIi to i8*) + catch i8* null + %2 = extractvalue { i8*, i32 } %1, 0 + %3 = extractvalue { i8*, i32 } %1, 1 + br label %catch.dispatch + +catch.dispatch: ; preds = %lpad + %4 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*)) + %matches = icmp eq i32 %3, %4 + br i1 %matches, label %catch1, label %catch + +catch1: ; preds = %catch.dispatch + %5 = call i8* @__cxa_begin_catch(i8* %2) + %6 = bitcast i8* %5 to i32* + %7 = load i32, i32* %6, align 4 + call void @__cxa_end_catch() + br label %try.cont + +try.cont: ; preds = %catch, %catch1, %invoke.cont + ret void + +catch: ; preds = %catch.dispatch + %8 = call i8* @__cxa_begin_catch(i8* %2) + call void @__cxa_end_catch() + br label %try.cont +} + declare void @foo(i32) +declare i8* @bar(i8, i8) declare i32 @__gxx_personality_v0(...) declare i32 @llvm.eh.typeid.for(i8*) @@ -117,9 +163,9 @@ 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 @__resumeException(i8*) ; CHECK: declare void @__invoke_void_i32(void (i32)*, i32) -; CHECK: declare i8* @___cxa_find_matching_catch_4(i8*, i8*) +; CHECK: declare i8* @__cxa_find_matching_catch_4(i8*, i8*) ; setThrew function creation ; CHECK-LABEL: define void @setThrew(i1 %threw, i32 %value) { -- 2.7.4