diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp index 23aaa5160abd2..df96c6f437c2e 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp @@ -817,6 +817,32 @@ static bool containsLongjmpableCalls(const Function *F) { return false; } +// When a function contains a setjmp call but not other calls that can longjmp, +// we don't do setjmp transformation for that setjmp. But we need to convert the +// setjmp calls into "i32 0" so they don't cause link time errors. setjmp always +// returns 0 when called directly. +static void nullifySetjmp(Function *F) { + Module &M = *F->getParent(); + IRBuilder<> IRB(M.getContext()); + Function *SetjmpF = M.getFunction("setjmp"); + SmallVector ToErase; + + for (User *U : SetjmpF->users()) { + auto *CI = dyn_cast(U); + // FIXME 'invoke' to setjmp can happen when we use Wasm EH + Wasm SjLj, but + // we don't support two being used together yet. + if (!CI) + report_fatal_error("Wasm EH + Wasm SjLj is not fully supported yet"); + BasicBlock *BB = CI->getParent(); + if (BB->getParent() != F) // in other function + continue; + ToErase.push_back(CI); + CI->replaceAllUsesWith(IRB.getInt32(0)); + } + for (auto *I : ToErase) + I->eraseFromParent(); +} + bool WebAssemblyLowerEmscriptenEHSjLj::runOnModule(Module &M) { LLVM_DEBUG(dbgs() << "********** Lower Emscripten EH & SjLj **********\n"); @@ -886,6 +912,10 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runOnModule(Module &M) { EHTypeIDF = getEmscriptenFunction(EHTypeIDTy, "llvm_eh_typeid_for", &M); } + // Functions that contains calls to setjmp but don't have other longjmpable + // calls within them. + SmallPtrSet SetjmpUsersToNullify; + if ((EnableEmSjLj || EnableWasmSjLj) && SetjmpF) { // Precompute setjmp users for (User *U : SetjmpF->users()) { @@ -896,6 +926,8 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runOnModule(Module &M) { // so can ignore it if (containsLongjmpableCalls(UserF)) SetjmpUsers.insert(UserF); + else + SetjmpUsersToNullify.insert(UserF); } else { std::string S; raw_string_ostream SS(S); @@ -975,6 +1007,14 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runOnModule(Module &M) { runSjLjOnFunction(*F); } + // Replace unnecessary setjmp calls with 0 + if ((EnableEmSjLj || EnableWasmSjLj) && !SetjmpUsersToNullify.empty()) { + Changed = true; + assert(SetjmpF); + for (Function *F : SetjmpUsersToNullify) + nullifySetjmp(F); + } + if (!Changed) { // Delete unused global variables and functions if (ResumeF) diff --git a/llvm/test/CodeGen/WebAssembly/lower-em-sjlj.ll b/llvm/test/CodeGen/WebAssembly/lower-em-sjlj.ll index fcfd503234065..77dcff2e0799e 100644 --- a/llvm/test/CodeGen/WebAssembly/lower-em-sjlj.ll +++ b/llvm/test/CodeGen/WebAssembly/lower-em-sjlj.ll @@ -131,7 +131,7 @@ entry: ; 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) { +define i32 @setjmp_only(i8* %ptr) { ; CHECK-LABEL: @setjmp_only entry: %buf = alloca [1 x %struct.__jmp_buf_tag], align 16 @@ -139,11 +139,12 @@ entry: %call = call i32 @setjmp(%struct.__jmp_buf_tag* %arraydecay) #0 ; free cannot longjmp call void @free(i8* %ptr) - ret void + ret i32 %call ; CHECK-NOT: @malloc ; CHECK-NOT: %setjmpTable ; CHECK-NOT: @saveSetjmp ; CHECK-NOT: @testSetjmp +; CHECK: ret i32 0 } ; Test SSA validity