diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp --- a/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp @@ -253,8 +253,6 @@ Function *getInvokeWrapper(CallBase *CI); bool areAllExceptionsAllowed() const { return EHAllowlistSet.empty(); } - bool canLongjmp(const Value *Callee) const; - bool isEmAsmCall(const Value *Callee) const; bool supportsException(const Function *F) const { return EnableEmEH && (areAllExceptionsAllowed() || EHAllowlistSet.count(std::string(F->getName()))); @@ -505,7 +503,7 @@ return F; } -bool WebAssemblyLowerEmscriptenEHSjLj::canLongjmp(const Value *Callee) const { +static bool canLongjmp(const Value *Callee) { if (auto *CalleeF = dyn_cast(Callee)) if (CalleeF->isIntrinsic()) return false; @@ -543,7 +541,7 @@ return true; } -bool WebAssemblyLowerEmscriptenEHSjLj::isEmAsmCall(const Value *Callee) const { +static bool isEmAsmCall(const Value *Callee) { StringRef CalleeName = Callee->getName(); // This is an exhaustive list from Emscripten's . return CalleeName == "emscripten_asm_const_int" || @@ -689,6 +687,15 @@ } } +static bool containsLongjmpableCalls(const Function *F) { + for (const auto &BB : *F) + for (const auto &I : BB) + if (const auto *CB = dyn_cast(&I)) + if (canLongjmp(CB->getCalledOperand())) + return true; + return false; +} + bool WebAssemblyLowerEmscriptenEHSjLj::runOnModule(Module &M) { LLVM_DEBUG(dbgs() << "********** Lower Emscripten EH & SjLj **********\n"); @@ -697,9 +704,6 @@ Function *SetjmpF = M.getFunction("setjmp"); Function *LongjmpF = M.getFunction("longjmp"); - bool SetjmpUsed = SetjmpF && !SetjmpF->use_empty(); - bool LongjmpUsed = LongjmpF && !LongjmpF->use_empty(); - DoSjLj = EnableEmSjLj && (SetjmpUsed || LongjmpUsed); auto *TPC = getAnalysisIfAvailable(); assert(TPC && "Expected a TargetPassConfig"); @@ -737,6 +741,24 @@ EHTypeIDF = getEmscriptenFunction(EHTypeIDTy, "llvm_eh_typeid_for", &M); } + if (EnableEmSjLj) { + if (SetjmpF) { + // Precompute setjmp users + for (User *U : SetjmpF->users()) { + Function *UserF = cast(U)->getFunction(); + // If we a function that calls setjmp does not contain any other calls + // that can longjmp, we don't need to do any transformation on that + // function, so can ignore it + if (containsLongjmpableCalls(UserF)) + SetjmpUsers.insert(UserF); + } + } + } + + bool SetjmpUsed = SetjmpF && !SetjmpUsers.empty(); + bool LongjmpUsed = LongjmpF && !LongjmpF->use_empty(); + DoSjLj = EnableEmSjLj && (SetjmpUsed || LongjmpUsed); + // Function registration and data pre-gathering for setjmp/longjmp handling if (DoSjLj) { // Register emscripten_longjmp function @@ -759,12 +781,6 @@ {getAddrIntType(&M), Type::getInt32PtrTy(C), IRB.getInt32Ty()}, false); TestSetjmpF = getEmscriptenFunction(FTy, "testSetjmp", &M); - - // Precompute setjmp users - for (User *U : SetjmpF->users()) { - auto *UI = cast(U); - SetjmpUsers.insert(UI->getFunction()); - } } } diff --git a/llvm/test/CodeGen/WebAssembly/lower-em-sjlj-alias.ll b/llvm/test/CodeGen/WebAssembly/lower-em-sjlj-alias.ll --- a/llvm/test/CodeGen/WebAssembly/lower-em-sjlj-alias.ll +++ b/llvm/test/CodeGen/WebAssembly/lower-em-sjlj-alias.ll @@ -23,11 +23,10 @@ store i32 0, i32* %retval, align 4 %arraydecay = getelementptr inbounds [1 x %struct.__jmp_buf_tag], [1 x %struct.__jmp_buf_tag]* %jmp, i32 0, i32 0 %call = call i32 @setjmp(%struct.__jmp_buf_tag* %arraydecay) #0 + call void @foo() ret void ; CHECK-LABEL: entry.split - ; CHECK: call void @free - ; CHECK: ret void } ; This is a dummy dlmalloc implemenation only to make compiler pass, because an @@ -37,6 +36,7 @@ ret i8* %p } +declare void @foo() ; Function Attrs: returns_twice declare i32 @setjmp(%struct.__jmp_buf_tag*) #0 diff --git a/llvm/test/CodeGen/WebAssembly/lower-em-sjlj.ll b/llvm/test/CodeGen/WebAssembly/lower-em-sjlj.ll --- a/llvm/test/CodeGen/WebAssembly/lower-em-sjlj.ll +++ b/llvm/test/CodeGen/WebAssembly/lower-em-sjlj.ll @@ -100,6 +100,23 @@ ; CHECK-NEXT: ret void } +; Test a case where a function has a setjmp call but no other calls that can +; longjmp. We don't need to do any transformation in this case. +define void @setjmp_only(i8* %ptr) { +; CHECK-LABEL: @setjmp_only +entry: + %buf = alloca [1 x %struct.__jmp_buf_tag], align 16 + %arraydecay = getelementptr inbounds [1 x %struct.__jmp_buf_tag], [1 x %struct.__jmp_buf_tag]* %buf, i32 0, i32 0 + %call = call i32 @setjmp(%struct.__jmp_buf_tag* %arraydecay) #0 + ; free cannot longjmp + call void @free(i8* %ptr) + ret void +; CHECK-NOT: @malloc +; CHECK-NOT: %setjmpTable +; CHECK-NOT: @saveSetjmp +; CHECK-NOT: @testSetjmp +} + ; Test SSA validity define void @ssa(i32 %n) { ; CHECK-LABEL: @ssa