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 @@ -869,15 +869,17 @@ 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(); + for (User *U : make_early_inc_range(SetjmpF->users())) { + auto *CB = dyn_cast(U); + BasicBlock *BB = CB->getParent(); if (BB->getParent() != F) // in other function continue; + CallInst *CI = nullptr; + // setjmp cannot throw. So if it is an invoke, lower it to a call + if (auto *II = dyn_cast(CB)) + CI = llvm::changeToCall(II); + else + CI = cast(CB); ToErase.push_back(CI); CI->replaceAllUsesWith(IRB.getInt32(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 @@ -140,10 +140,13 @@ ; free cannot longjmp call void @free(i8* %ptr) ret i32 %call +; CHECK: entry: ; CHECK-NOT: @malloc ; CHECK-NOT: %setjmpTable ; CHECK-NOT: @saveSetjmp ; CHECK-NOT: @testSetjmp +; The remaining setjmp call is converted to constant 0, because setjmp returns 0 +; when called directly. ; CHECK: ret i32 0 } diff --git a/llvm/test/CodeGen/WebAssembly/lower-wasm-ehsjlj.ll b/llvm/test/CodeGen/WebAssembly/lower-wasm-ehsjlj.ll --- a/llvm/test/CodeGen/WebAssembly/lower-wasm-ehsjlj.ll +++ b/llvm/test/CodeGen/WebAssembly/lower-wasm-ehsjlj.ll @@ -260,17 +260,40 @@ ehcleanup: ; preds = %entry %0 = cleanuppad within none [] ; After SjLj transformation, this will be converted to an invoke that - ; eventually unwinds to %catch.longjmp.dispatch. But in case a call has a + ; eventually unwinds to %catch.dispatch.longjmp. But in case a call has a ; "funclet" attribute, we should unwind to the funclet's unwind destination ; first to preserve the scoping structure. But this call's parent is %0 ; (cleanuppad), whose parent is 'none', so we should unwind directly to - ; %catch.longjmp.dispatch. + ; %catch.dispatch.longjmp. %call2 = call noundef %struct.Temp* @_ZN4TempD2Ev(%struct.Temp* noundef %t) #2 [ "funclet"(token %0) ] ; CHECK: %call13 = invoke {{.*}} %struct.Temp* @_ZN4TempD2Ev(%struct.Temp* ; CHECK-NEXT: to label {{.*}} unwind label %catch.dispatch.longjmp cleanupret from %0 unwind to caller } +; This case was adapted from @cleanuppad_no_parent by removing allocas and +; destructor calls, to generate a situation that there's only 'invoke @setjmp' +; and no other longjmpable calls. +define i32 @setjmp_only() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { +; 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 = invoke i32 @setjmp(%struct.__jmp_buf_tag* noundef %arraydecay) #0 + to label %invoke.cont unwind label %ehcleanup + +invoke.cont: ; preds = %entry + ret i32 %call +; CHECK: invoke.cont: +; The remaining setjmp call is converted to constant 0, because setjmp returns 0 +; when called directly. +; CHECK: ret i32 0 + +ehcleanup: ; preds = %entry + %0 = cleanuppad within none [] + cleanupret from %0 unwind to caller +} + declare void @foo() ; Function Attrs: nounwind declare %struct.Temp* @_ZN4TempD2Ev(%struct.Temp* %this) #2