[llvm] b8fc71b - [WebAssembly] Share rethrowing BBs in LowerEmscriptenEHSjLj

Heejin Ahn via llvm-commits llvm-commits at lists.llvm.org
Mon Aug 30 21:44:55 PDT 2021


Author: Heejin Ahn
Date: 2021-08-30T21:44:34-07:00
New Revision: b8fc71b7aeeda0c86a4920da201cc19132ad6d03

URL: https://github.com/llvm/llvm-project/commit/b8fc71b7aeeda0c86a4920da201cc19132ad6d03
DIFF: https://github.com/llvm/llvm-project/commit/b8fc71b7aeeda0c86a4920da201cc19132ad6d03.diff

LOG: [WebAssembly] Share rethrowing BBs in LowerEmscriptenEHSjLj

There are three kinds of "rethrowing" BBs in this pass:
1. In Emscripten SjLj, after a possibly longjmping function call, we
   check if the thrown longjmp corresponds to one of setjmps within the
   current function. If not, we rethrow the longjmp by calling
   `emscripten_longjmp`.
2. In Emscripten EH, after a possibly throwing function call, we check
   if the thrown exception corresponds to the current `catch` clauses.
   If not, we rethrow the exception by calling `__resumeException`.
3. When both Emscripten EH and SjLj are used, when we check for an
   exception after a possibly throwing function call, it is possible
   that we get not an exception but a longjmp. In this case, we
   shouldn't swallow it; we should rethrow the longjmp by calling
   `emscripten_longjmp`.
4. When both Emscripten EH and SjLj are used, when we check for a
   longjmp after a possibly longjmping function call, it is possible
   that we get not a longjmp but an exception. In this case, we
   shouldn't swallot it; we should rethrow the exception by calling
   `__resumeException`.

Case 1 is in Emscripten SjLj, 2 is in Emscripten EH, and 3 and 4 are
relevant when both Emscripten EH and SjLj are used. 3 and 4 were first
implemented in D106525.

We create BBs for 1, 3, and 4 in this pass. We create those BBs for
every throwing/longjmping function call, along with other BBs that
contain condition checks. What this CL does is to create a single BB
within a function for each of 1, 3, and 4 cases. These BBs are exiting
BBs in the function and thus don't have successors, so easy to be shared
between calls.

The names of BBs created are:
Case 1: `call.em.longjmp`
Case 3: `rethrow.exn`
Case 4: `rethrow.longjmp`

For the case 2 we don't currently create BBs; we only replace the
existing `resume` instruction with `call @__resumeException`. And Clang
already creates only a single `resume` BB per function and reuses it,
so we don't need to optimize this case.

Not sure what are good benchmarks for EH/SjLj, but this decreases the
size of the object file for `grfmt_jpeg.bc` (presumably from opencv) we
got from one of our users by 8.9%. Even after running `wasm-opt -O4` on
them, there is still 4.8% improvement.

Reviewed By: dschuff

Differential Revision: https://reviews.llvm.org/D108945

Added: 
    

Modified: 
    llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp
    llvm/test/CodeGen/WebAssembly/lower-em-ehsjlj.ll
    llvm/test/CodeGen/WebAssembly/lower-em-sjlj-debuginfo.ll
    llvm/test/CodeGen/WebAssembly/lower-em-sjlj.ll

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp
index 110f5239b34cb..2e5b71ac59748 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp
@@ -263,7 +263,10 @@ class WebAssemblyLowerEmscriptenEHSjLj final : public ModulePass {
   Value *wrapInvoke(CallBase *CI);
   void wrapTestSetjmp(BasicBlock *BB, DebugLoc DL, Value *Threw,
                       Value *SetjmpTable, Value *SetjmpTableSize, Value *&Label,
-                      Value *&LongjmpResult, BasicBlock *&EndBB);
+                      Value *&LongjmpResult, BasicBlock *&CallEmLongjmpBB,
+                      PHINode *&CallEmLongjmpBBThrewPHI,
+                      PHINode *&CallEmLongjmpBBThrewValuePHI,
+                      BasicBlock *&EndBB);
   Function *getInvokeWrapper(CallBase *CI);
 
   bool areAllExceptionsAllowed() const { return EHAllowlistSet.empty(); }
@@ -585,7 +588,8 @@ static bool isEmAsmCall(const Value *Callee) {
 void WebAssemblyLowerEmscriptenEHSjLj::wrapTestSetjmp(
     BasicBlock *BB, DebugLoc DL, Value *Threw, Value *SetjmpTable,
     Value *SetjmpTableSize, Value *&Label, Value *&LongjmpResult,
-    BasicBlock *&EndBB) {
+    BasicBlock *&CallEmLongjmpBB, PHINode *&CallEmLongjmpBBThrewPHI,
+    PHINode *&CallEmLongjmpBBThrewValuePHI, BasicBlock *&EndBB) {
   Function *F = BB->getParent();
   Module *M = F->getParent();
   LLVMContext &C = M->getContext();
@@ -604,10 +608,27 @@ void WebAssemblyLowerEmscriptenEHSjLj::wrapTestSetjmp(
   Value *Cmp1 = IRB.CreateAnd(ThrewCmp, ThrewValueCmp, "cmp1");
   IRB.CreateCondBr(Cmp1, ThenBB1, ElseBB1);
 
+  // Generate call.em.longjmp BB once and share it within the function
+  if (!CallEmLongjmpBB) {
+    // emscripten_longjmp(%__THREW__.val, %__threwValue.val);
+    CallEmLongjmpBB = BasicBlock::Create(C, "call.em.longjmp", F);
+    IRB.SetInsertPoint(CallEmLongjmpBB);
+    CallEmLongjmpBBThrewPHI = IRB.CreatePHI(getAddrIntType(M), 4, "threw.phi");
+    CallEmLongjmpBBThrewValuePHI =
+        IRB.CreatePHI(IRB.getInt32Ty(), 4, "threwvalue.phi");
+    CallEmLongjmpBBThrewPHI->addIncoming(Threw, ThenBB1);
+    CallEmLongjmpBBThrewValuePHI->addIncoming(ThrewValue, ThenBB1);
+    IRB.CreateCall(EmLongjmpF,
+                   {CallEmLongjmpBBThrewPHI, CallEmLongjmpBBThrewValuePHI});
+    IRB.CreateUnreachable();
+  } else {
+    CallEmLongjmpBBThrewPHI->addIncoming(Threw, ThenBB1);
+    CallEmLongjmpBBThrewValuePHI->addIncoming(ThrewValue, ThenBB1);
+  }
+
   // %label = testSetjmp(mem[%__THREW__.val], setjmpTable, setjmpTableSize);
   // if (%label == 0)
   IRB.SetInsertPoint(ThenBB1);
-  BasicBlock *ThenBB2 = BasicBlock::Create(C, "if.then2", F);
   BasicBlock *EndBB2 = BasicBlock::Create(C, "if.end2", F);
   Value *ThrewPtr =
       IRB.CreateIntToPtr(Threw, getAddrPtrType(M), Threw->getName() + ".p");
@@ -616,12 +637,7 @@ void WebAssemblyLowerEmscriptenEHSjLj::wrapTestSetjmp(
   Value *ThenLabel = IRB.CreateCall(
       TestSetjmpF, {LoadedThrew, SetjmpTable, SetjmpTableSize}, "label");
   Value *Cmp2 = IRB.CreateICmpEQ(ThenLabel, IRB.getInt32(0));
-  IRB.CreateCondBr(Cmp2, ThenBB2, EndBB2);
-
-  // emscripten_longjmp(%__THREW__.val, %__threwValue.val);
-  IRB.SetInsertPoint(ThenBB2);
-  IRB.CreateCall(EmLongjmpF, {Threw, ThrewValue});
-  IRB.CreateUnreachable();
+  IRB.CreateCondBr(Cmp2, CallEmLongjmpBB, EndBB2);
 
   // setTempRet0(%__threwValue.val);
   IRB.SetInsertPoint(EndBB2);
@@ -840,6 +856,12 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runEHOnFunction(Function &F) {
   SmallVector<Instruction *, 64> ToErase;
   SmallPtrSet<LandingPadInst *, 32> LandingPads;
 
+  // rethrow.longjmp BB that will be shared within the function.
+  BasicBlock *RethrowLongjmpBB = nullptr;
+  // PHI node for the loaded value of __THREW__ global variable in
+  // rethrow.longjmp BB
+  PHINode *RethrowLongjmpBBThrewPHI = nullptr;
+
   for (BasicBlock &BB : F) {
     auto *II = dyn_cast<InvokeInst>(BB.getTerminator());
     if (!II)
@@ -869,27 +891,36 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runEHOnFunction(Function &F) {
       // else
       //   goto %longjmp.rethrow
       //
-      // longjmp.rethrow: ;; This is longjmp. Rethrow it
+      // rethrow.longjmp: ;; This is longjmp. Rethrow it
       //   %__threwValue.val = __threwValue
       //   emscripten_longjmp(%__THREW__.val, %__threwValue.val);
       //
       // tail: ;; Nothing happened or an exception is thrown
       //   ... Continue exception handling ...
       if (DoSjLj && !SetjmpUsers.count(&F) && canLongjmp(Callee)) {
+        // Create longjmp.rethrow BB once and share it within the function
+        if (!RethrowLongjmpBB) {
+          RethrowLongjmpBB = BasicBlock::Create(C, "rethrow.longjmp", &F);
+          IRB.SetInsertPoint(RethrowLongjmpBB);
+          RethrowLongjmpBBThrewPHI =
+              IRB.CreatePHI(getAddrIntType(&M), 4, "threw.phi");
+          RethrowLongjmpBBThrewPHI->addIncoming(Threw, &BB);
+          Value *ThrewValue = IRB.CreateLoad(IRB.getInt32Ty(), ThrewValueGV,
+                                             ThrewValueGV->getName() + ".val");
+          IRB.CreateCall(EmLongjmpF, {RethrowLongjmpBBThrewPHI, ThrewValue});
+          IRB.CreateUnreachable();
+        } else {
+          RethrowLongjmpBBThrewPHI->addIncoming(Threw, &BB);
+        }
+
+        IRB.SetInsertPoint(II); // Restore the insert point back
         BasicBlock *Tail = BasicBlock::Create(C, "tail", &F);
-        BasicBlock *RethrowBB = BasicBlock::Create(C, "longjmp.rethrow", &F);
         Value *CmpEqOne =
             IRB.CreateICmpEQ(Threw, getAddrSizeInt(&M, 1), "cmp.eq.one");
         Value *CmpEqZero =
             IRB.CreateICmpEQ(Threw, getAddrSizeInt(&M, 0), "cmp.eq.zero");
         Value *Or = IRB.CreateOr(CmpEqZero, CmpEqOne, "or");
-        IRB.CreateCondBr(Or, Tail, RethrowBB);
-        IRB.SetInsertPoint(RethrowBB);
-        Value *ThrewValue = IRB.CreateLoad(IRB.getInt32Ty(), ThrewValueGV,
-                                           ThrewValueGV->getName() + ".val");
-        IRB.CreateCall(EmLongjmpF, {Threw, ThrewValue});
-
-        IRB.CreateUnreachable();
+        IRB.CreateCondBr(Or, Tail, RethrowLongjmpBB);
         IRB.SetInsertPoint(Tail);
         BB.replaceSuccessorsPhiUsesWith(&BB, Tail);
       }
@@ -1204,6 +1235,17 @@ void WebAssemblyLowerEmscriptenEHSjLj::handleLongjmpableCallsForEmscriptenSjLj(
   Instruction *SetjmpTable = *SetjmpTableInsts.begin();
   Instruction *SetjmpTableSize = *SetjmpTableSizeInsts.begin();
 
+  // call.em.longjmp BB that will be shared within the function.
+  BasicBlock *CallEmLongjmpBB = nullptr;
+  // PHI node for the loaded value of __THREW__ global variable in
+  // call.em.longjmp BB
+  PHINode *CallEmLongjmpBBThrewPHI = nullptr;
+  // PHI node for the loaded value of __threwValue global variable in
+  // call.em.longjmp BB
+  PHINode *CallEmLongjmpBBThrewValuePHI = nullptr;
+  // rethrow.exn BB that will be shared within the function.
+  BasicBlock *RethrowExnBB = nullptr;
+
   // Because we are creating new BBs while processing and don't want to make
   // all these newly created BBs candidates again for longjmp processing, we
   // first make the vector of candidate BBs.
@@ -1297,19 +1339,26 @@ void WebAssemblyLowerEmscriptenEHSjLj::handleLongjmpableCallsForEmscriptenSjLj(
         // tail:
         //   ...
         if (supportsException(&F) && canThrow(Callee)) {
-          IRB.SetInsertPoint(CI);
           // We will add a new conditional branch. So remove the branch created
           // when we split the BB
           ToErase.push_back(BB->getTerminator());
+
+          // Generate rethrow.exn BB once and share it within the function
+          if (!RethrowExnBB) {
+            RethrowExnBB = BasicBlock::Create(C, "rethrow.exn", &F);
+            IRB.SetInsertPoint(RethrowExnBB);
+            CallInst *Exn =
+                IRB.CreateCall(getFindMatchingCatch(M, 0), {}, "exn");
+            IRB.CreateCall(ResumeF, {Exn});
+            IRB.CreateUnreachable();
+          }
+
+          IRB.SetInsertPoint(CI);
           BasicBlock *NormalBB = BasicBlock::Create(C, "normal", &F);
-          BasicBlock *RethrowBB = BasicBlock::Create(C, "eh.rethrow", &F);
           Value *CmpEqOne =
               IRB.CreateICmpEQ(Threw, getAddrSizeInt(&M, 1), "cmp.eq.one");
-          IRB.CreateCondBr(CmpEqOne, RethrowBB, NormalBB);
-          IRB.SetInsertPoint(RethrowBB);
-          CallInst *Exn = IRB.CreateCall(getFindMatchingCatch(M, 0), {}, "exn");
-          IRB.CreateCall(ResumeF, {Exn});
-          IRB.CreateUnreachable();
+          IRB.CreateCondBr(CmpEqOne, RethrowExnBB, NormalBB);
+
           IRB.SetInsertPoint(NormalBB);
           IRB.CreateBr(Tail);
           BB = NormalBB; // New insertion point to insert testSetjmp()
@@ -1328,7 +1377,9 @@ void WebAssemblyLowerEmscriptenEHSjLj::handleLongjmpableCallsForEmscriptenSjLj(
       Value *LongjmpResult = nullptr;
       BasicBlock *EndBB = nullptr;
       wrapTestSetjmp(BB, CI->getDebugLoc(), Threw, SetjmpTable, SetjmpTableSize,
-                     Label, LongjmpResult, EndBB);
+                     Label, LongjmpResult, CallEmLongjmpBB,
+                     CallEmLongjmpBBThrewPHI, CallEmLongjmpBBThrewValuePHI,
+                     EndBB);
       assert(Label && LongjmpResult && EndBB);
 
       // Create switch instruction

diff  --git a/llvm/test/CodeGen/WebAssembly/lower-em-ehsjlj.ll b/llvm/test/CodeGen/WebAssembly/lower-em-ehsjlj.ll
index 9d6ece6525f0f..85229ef101f4e 100644
--- a/llvm/test/CodeGen/WebAssembly/lower-em-ehsjlj.ll
+++ b/llvm/test/CodeGen/WebAssembly/lower-em-ehsjlj.ll
@@ -46,7 +46,7 @@ lpad:                                             ; preds = %entry
   call void @__cxa_end_catch()
   br label %try.cont
 
-try.cont:                                         ; preds = %entry, %lpad
+try.cont:                                         ; preds = %lpad, %entry
   ret void
 }
 
@@ -63,21 +63,22 @@ entry:
 ; CHECK:      %cmp.eq.one = icmp eq i32 %__THREW__.val, 1
 ; CHECK-NEXT: %cmp.eq.zero = icmp eq i32 %__THREW__.val, 0
 ; CHECK-NEXT: %or = or i1 %cmp.eq.zero, %cmp.eq.one
-; CHECK-NEXT: br i1 %or, label %tail, label %longjmp.rethrow
+; CHECK-NEXT: br i1 %or, label %tail, label %rethrow.longjmp
 
 ; CHECK: try.cont:
 ; CHECK-NEXT:  %phi = phi i32 [ undef, %tail ], [ undef, %lpad ]
 ; CHECK-NEXT:  ret void
 
+; CHECK:    rethrow.longjmp:
+; CHECK-NEXT: %threw.phi = phi i32 [ %__THREW__.val, %entry ]
+; CHECK-NEXT: %__threwValue.val = load i32, i32* @__threwValue, align 4
+; CHECK-NEXT: call void @emscripten_longjmp(i32 %threw.phi, i32 %__threwValue.val
+; CHECK-NEXT: unreachable
+
 ; CHECK:    tail:
 ; CHECK-NEXT: %cmp = icmp eq i32 %__THREW__.val, 1
 ; CHECK-NEXT: br i1 %cmp, label %lpad, label %try.cont
 
-; CHECK:    longjmp.rethrow:
-; CHECK-NEXT: %__threwValue.val = load i32, i32* @__threwValue, align 4
-; CHECK-NEXT: call void @emscripten_longjmp(i32 %__THREW__.val, i32 %__threwValue.val)
-; CHECK-NEXT: unreachable
-
 lpad:                                             ; preds = %entry
   %0 = landingpad { i8*, i32 }
           catch i8* null
@@ -87,7 +88,7 @@ lpad:                                             ; preds = %entry
   call void @__cxa_end_catch()
   br label %try.cont
 
-try.cont:                                         ; preds = %entry, %lpad
+try.cont:                                         ; preds = %lpad, %entry
  %phi = phi i32 [ undef, %entry ], [ undef, %lpad ]
   ret void
 }
@@ -111,19 +112,19 @@ if.end:                                           ; preds = %entry
 
 ; CHECK:    if.end:
 ; CHECK:      %cmp.eq.one = icmp eq i32 %__THREW__.val, 1
-; CHECK-NEXT: br i1 %cmp.eq.one, label %eh.rethrow, label %normal
-
-; CHECK:    normal:
-; CHECK-NEXT: icmp ne i32 %__THREW__.val, 0
+; CHECK-NEXT: br i1 %cmp.eq.one, label %rethrow.exn, label %normal
 
-; CHECK:    eh.rethrow:
+; CHECK:    rethrow.exn:
 ; CHECK-NEXT: %exn = call i8* @__cxa_find_matching_catch_2()
-; CHECK-NEXT: %[[BUF:.*]] = bitcast i32* %setjmpTable1 to i8*
+; CHECK-NEXT: %[[BUF:.*]] = bitcast i32* %setjmpTable{{.*}} to i8*
 ; CHECK-NEXT: call void @free(i8* %[[BUF]])
 ; CHECK-NEXT: call void @__resumeException(i8* %exn)
 ; CHECK-NEXT: unreachable
 
-return:                                           ; preds = %entry, %if.end
+; CHECK:    normal:
+; CHECK-NEXT: icmp ne i32 %__THREW__.val, 0
+
+return:                                           ; preds = %if.end, %entry
   ret void
 }
 
@@ -142,17 +143,81 @@ if.end:                                           ; preds = %entry
   call void @foo()
   br label %throw
 
-throw:                                            ; preds = %entry, %if.end
+throw:                                            ; preds = %if.end, %entry
   call void @__cxa_throw(i8* null, i8* null, i8* null) #1
   unreachable
 
-; CHECK: throw:
-; CHECK:      %[[BUF:.*]] = bitcast i32* %setjmpTable5 to i8*
+; CHECK:    throw:
+; CHECK:      %[[BUF:.*]] = bitcast i32* %setjmpTable{{.*}} to i8*
 ; CHECK-NEXT: call void @free(i8* %[[BUF]])
 ; CHECK-NEXT: call void @__cxa_throw(i8* null, i8* null, i8* null)
 ; CHECK-NEXT: unreachable
 }
 
+; The same case with @rethrow_longjmp, but there are multiple function calls
+; that can possibly longjmp (instead of throwing exception) so we have to
+; rethrow them. Here we test if we correclty generate only one 'rethrow.longjmp'
+; BB and share it for multiple calls.
+define void @rethrow_longjmp_multi() personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) {
+; CHECK-LABEL: @rethrow_longjmp_multi
+entry:
+  invoke void @foo()
+          to label %bb unwind label %lpad
+
+bb:                                               ; preds = %entry
+  invoke void @foo()
+          to label %try.cont unwind label %lpad
+
+lpad:                                             ; preds = %bb, %entry
+  %0 = landingpad { i8*, i32 }
+          catch i8* null
+  %1 = extractvalue { i8*, i32 } %0, 0
+  %2 = extractvalue { i8*, i32 } %0, 1
+  %3 = call i8* @__cxa_begin_catch(i8* %1) #5
+  call void @__cxa_end_catch()
+  br label %try.cont
+
+try.cont:                                         ; preds = %lpad, %bb
+ %phi = phi i32 [ undef, %bb ], [ undef, %lpad ]
+  ret void
+
+; CHECK:    rethrow.longjmp:
+; CHECK-NEXT: %threw.phi = phi i32 [ %__THREW__.val, %entry ], [ %__THREW__.val1, %bb ]
+; CHECK-NEXT: %__threwValue.val = load i32, i32* @__threwValue, align 4
+; CHECK-NEXT: call void @emscripten_longjmp(i32 %threw.phi, i32 %__threwValue.val)
+; CHECK-NEXT: unreachable
+}
+
+; The same case with @rethrow_exception, but there are multiple function calls
+; that can possibly throw (instead of longjmping) so we have to rethrow them.
+; Here we test if correctly we generate only one 'rethrow.exn' BB and share it
+; for multiple calls.
+define void @rethrow_exception_multi() {
+; CHECK-LABEL: @rethrow_exception_multi
+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
+  %cmp = icmp ne i32 %call, 0
+  br i1 %cmp, label %return, label %if.end
+
+if.end:                                           ; preds = %entry
+  call void @foo()
+  call void @foo()
+  br label %return
+
+return:                                           ; preds = %entry, %if.end
+  ret void
+
+; CHECK:    rethrow.exn:
+; CHECK-NEXT: %setjmpTable{{.*}} = phi i32* [ %setjmpTable{{.*}}, %if.end.split ], [ %setjmpTable{{.*}}, %if.end ]
+; CHECK-NEXT: %exn = call i8* @__cxa_find_matching_catch_2()
+; CHECK-NEXT: %{{.*}} = bitcast i32* %setjmpTable{{.*}} to i8*
+; CHECK-NEXT: tail call void @free(i8* %{{.*}})
+; CHECK-NEXT: call void @__resumeException(i8* %exn)
+; CHECK-NEXT: unreachable
+}
+
 declare void @foo()
 ; Function Attrs: returns_twice
 declare i32 @setjmp(%struct.__jmp_buf_tag*)

diff  --git a/llvm/test/CodeGen/WebAssembly/lower-em-sjlj-debuginfo.ll b/llvm/test/CodeGen/WebAssembly/lower-em-sjlj-debuginfo.ll
index 6e0b711273c8e..19e012e547f18 100644
--- a/llvm/test/CodeGen/WebAssembly/lower-em-sjlj-debuginfo.ll
+++ b/llvm/test/CodeGen/WebAssembly/lower-em-sjlj-debuginfo.ll
@@ -38,7 +38,7 @@ entry:
 ; CHECK: if.end:
   ; CHECK: call i32 @getTempRet0{{.*}}, !dbg ![[DL2]]
 
-; CHECK: if.then2:
+; CHECK: call.em.longjmp:
   ; CHECK: call void @emscripten_longjmp{{.*}}, !dbg ![[DL2]]
 
 ; CHECK: if.end2:

diff  --git a/llvm/test/CodeGen/WebAssembly/lower-em-sjlj.ll b/llvm/test/CodeGen/WebAssembly/lower-em-sjlj.ll
index fb2686ef1b1f0..85ea09ef538b4 100644
--- a/llvm/test/CodeGen/WebAssembly/lower-em-sjlj.ll
+++ b/llvm/test/CodeGen/WebAssembly/lower-em-sjlj.ll
@@ -61,7 +61,7 @@ entry:
 ; CHECK-NEXT: %[[__THREW__VAL_P_LOADED:.*]] = load [[PTR]], [[PTR]]* %[[__THREW__VAL_P]]
 ; CHECK-NEXT: %[[LABEL:.*]] = call i32 @testSetjmp([[PTR]] %[[__THREW__VAL_P_LOADED]], i32* %[[SETJMP_TABLE1]], i32 %[[SETJMP_TABLE_SIZE1]])
 ; CHECK-NEXT: %[[CMP:.*]] = icmp eq i32 %[[LABEL]], 0
-; CHECK-NEXT: br i1 %[[CMP]], label %if.then2, label %if.end2
+; CHECK-NEXT: br i1 %[[CMP]], label %call.em.longjmp, label %if.end2
 
 ; CHECK: if.else1:
 ; CHECK-NEXT: br label %if.end
@@ -73,10 +73,12 @@ entry:
 ; CHECK-NEXT:   i32 1, label %entry.split.split
 ; CHECK-NEXT: ]
 
-; CHECK: if.then2:
-; CHECK-NEXT: %[[BUF:.*]] = bitcast i32* %[[SETJMP_TABLE1]] to i8*
-; CHECK-NEXT: call void @free(i8* %[[BUF]])
-; CHECK-NEXT: call void @emscripten_longjmp([[PTR]] %[[__THREW__VAL]], i32 %[[THREWVALUE_VAL]])
+; CHECK: call.em.longjmp:
+; CHECK-NEXT: %threw.phi = phi [[PTR]] [ %[[__THREW__VAL]], %if.then1 ]
+; CHECK-NEXT: %threwvalue.phi = phi i32 [ %[[THREWVALUE_VAL]], %if.then1 ]
+; CHECK-NEXT: %{{.*}} = bitcast i32* %[[SETJMP_TABLE1]] to i8*
+; CHECK-NEXT: tail call void @free(i8* %{{.*}})
+; CHECK-NEXT: call void @emscripten_longjmp([[PTR]] %threw.phi, i32 %threwvalue.phi)
 ; CHECK-NEXT: unreachable
 
 ; CHECK: if.end2:
@@ -85,8 +87,8 @@ entry:
 }
 
 ; Test a case of a function call (which is not longjmp) after a setjmp
-define void @setjmp_other() {
-; CHECK-LABEL: @setjmp_other
+define void @setjmp_longjmpable_call() {
+; CHECK-LABEL: @setjmp_longjmpable_call
 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
@@ -105,6 +107,28 @@ entry:
 ; CHECK-NEXT: ret void
 }
 
+; When there are multiple longjmpable calls after setjmp. In this test we
+; specifically check if 'call.em.longjmp' BB, which rethrows longjmps by calling
+; emscripten_longjmp for ones that are not for this function's setjmp, is
+; correctly created for multiple predecessors.
+define void @setjmp_multiple_longjmpable_calls() {
+; CHECK-LABEL: @setjmp_multiple_longjmpable_calls
+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
+  call void @foo()
+  call void @foo()
+  ret void
+; CHECK: call.em.longjmp:
+; CHECK-NEXT:  %threw.phi = phi [[PTR]] [ %__THREW__.val, %if.then1 ], [ %__THREW__.val4, %if.then15 ]
+; CHECK-NEXT:  %threwvalue.phi = phi i32 [ %__threwValue.val, %if.then1 ], [ %__threwValue.val8, %if.then15 ]
+; CHECK-NEXT: %{{.*}} = bitcast i32* %[[SETJMP_TABLE1]] to i8*
+; CHECK-NEXT: tail call void @free(i8* %{{.*}})
+; CHECK-NEXT: call void @emscripten_longjmp([[PTR]] %threw.phi, i32 %threwvalue.phi)
+; CHECK-NEXT: unreachable
+}
+
 ; 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) {


        


More information about the llvm-commits mailing list