[WinEH] Create a separate MBB for funclet prologues
authorDavid Majnemer <david.majnemer@gmail.com>
Tue, 6 Oct 2015 23:31:59 +0000 (23:31 +0000)
committerDavid Majnemer <david.majnemer@gmail.com>
Tue, 6 Oct 2015 23:31:59 +0000 (23:31 +0000)
Our current emission strategy is to emit the funclet prologue in the
CatchPad's normal destination.  This is problematic because
intra-funclet control flow to the normal destination is not erroneous
and results in us reevaluating the prologue if said control flow is
taken.

Instead, use the CatchPad's location for the funclet prologue.  This
correctly models our desire to have unwind edges evaluate the prologue
but edges to the normal destination result in typical control flow.

Differential Revision: http://reviews.llvm.org/D13424

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@249483 91177308-0d34-0410-b5e6-96231b3b80d8

lib/CodeGen/SelectionDAG/FunctionLoweringInfo.cpp
lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
lib/CodeGen/WinEHPrepare.cpp
test/CodeGen/X86/funclet-layout.ll
test/CodeGen/X86/seh-catchpad.ll
test/CodeGen/X86/win-catchpad-csrs.ll
test/CodeGen/X86/win-catchpad.ll
test/CodeGen/X86/win-funclet-cfi.ll
test/CodeGen/X86/win32-seh-catchpad.ll

index d718ede35cde2abdc9bd1abf607a171f5e0a69e7..a7f5ba1269eaade4133b19bce0e426e3e6a3520d 100644 (file)
@@ -216,14 +216,17 @@ void FunctionLoweringInfo::set(const Function &fn, MachineFunction &mf,
     // are really data, and no instructions can live here.
     if (BB->isEHPad()) {
       const Instruction *I = BB->getFirstNonPHI();
+      // FIXME: Don't mark SEH functions without __finally blocks as having
+      // funclets.
       if (!isa<LandingPadInst>(I))
         MMI.setHasEHFunclets(true);
-      if (isa<CatchPadInst>(I) || isa<CatchEndPadInst>(I) ||
-          isa<CleanupEndPadInst>(I)) {
+      if (isa<CatchEndPadInst>(I) || isa<CleanupEndPadInst>(I)) {
         assert(&*BB->begin() == I &&
                "WinEHPrepare failed to remove PHIs from imaginary BBs");
         continue;
       }
+      if (isa<CatchPadInst>(I) || isa<CleanupPadInst>(I))
+        assert(&*BB->begin() == I && "WinEHPrepare failed to demote PHIs");
     }
 
     MachineBasicBlock *MBB = mf.CreateMachineBasicBlock(BB);
index fc140f655716cf7427a4f44520a3b10db5fd5b45..22809aa0b3e27670e95ff04ca31ba502a62d763d 100644 (file)
@@ -1160,7 +1160,35 @@ SDValue SelectionDAGBuilder::getValueImpl(const Value *V) {
 }
 
 void SelectionDAGBuilder::visitCatchPad(const CatchPadInst &I) {
-  llvm_unreachable("should never codegen catchpads");
+  auto Pers = classifyEHPersonality(FuncInfo.Fn->getPersonalityFn());
+  bool IsMSVCCXX = Pers == EHPersonality::MSVC_CXX;
+  bool IsSEH = isAsynchronousEHPersonality(Pers);
+  MachineBasicBlock *CatchPadMBB = FuncInfo.MBB;
+  // In MSVC C++, catchblocks are funclets and need prologues.
+  if (IsMSVCCXX)
+    CatchPadMBB->setIsEHFuncletEntry();
+
+  MachineBasicBlock *NormalDestMBB = FuncInfo.MBBMap[I.getNormalDest()];
+
+  // Update machine-CFG edge.
+  FuncInfo.MBB->addSuccessor(NormalDestMBB);
+
+  // CatchPads in SEH are not funclets, they are merely markers which indicate
+  // where to insert register restoration code.
+  if (IsSEH) {
+    DAG.setRoot(DAG.getNode(ISD::CATCHRET, getCurSDLoc(), MVT::Other,
+                            getControlRoot(), DAG.getBasicBlock(NormalDestMBB),
+                            DAG.getBasicBlock(FuncInfo.MF->begin())));
+    return;
+  }
+
+  // If this is not a fall-through branch or optimizations are switched off,
+  // emit the branch.
+  if (NormalDestMBB != NextBlock(CatchPadMBB) ||
+      TM.getOptLevel() == CodeGenOpt::None)
+    DAG.setRoot(DAG.getNode(ISD::BR, getCurSDLoc(), MVT::Other,
+                            getControlRoot(),
+                            DAG.getBasicBlock(NormalDestMBB)));
 }
 
 void SelectionDAGBuilder::visitCatchRet(const CatchReturnInst &I) {
@@ -1168,6 +1196,18 @@ void SelectionDAGBuilder::visitCatchRet(const CatchReturnInst &I) {
   MachineBasicBlock *TargetMBB = FuncInfo.MBBMap[I.getSuccessor()];
   FuncInfo.MBB->addSuccessor(TargetMBB);
 
+  auto Pers = classifyEHPersonality(FuncInfo.Fn->getPersonalityFn());
+  bool IsSEH = isAsynchronousEHPersonality(Pers);
+  if (IsSEH) {
+    // If this is not a fall-through branch or optimizations are switched off,
+    // emit the branch.
+    if (TargetMBB != NextBlock(FuncInfo.MBB) ||
+        TM.getOptLevel() == CodeGenOpt::None)
+      DAG.setRoot(DAG.getNode(ISD::BR, getCurSDLoc(), MVT::Other,
+                              getControlRoot(), DAG.getBasicBlock(TargetMBB)));
+    return;
+  }
+
   // Figure out the funclet membership for the catchret's successor.
   // This will be used by the FuncletLayout pass to determine how to order the
   // BB's.
@@ -1225,8 +1265,8 @@ findUnwindDestinations(FunctionLoweringInfo &FuncInfo,
       break;
     } else if (const auto *CPI = dyn_cast<CatchPadInst>(Pad)) {
       // Add the catchpad handler to the possible destinations.
-      UnwindDests.push_back(FuncInfo.MBBMap[CPI->getNormalDest()]);
-      // In MSVC C++ and CoreCLR, catchblocks are funclets and need prologues.
+      UnwindDests.push_back(FuncInfo.MBBMap[EHPadBB]);
+      // In MSVC C++, catchblocks are funclets and need prologues.
       if (IsMSVCCXX || IsCoreCLR)
         UnwindDests.back()->setIsEHFuncletEntry();
       EHPadBB = CPI->getUnwindDest();
index cd813e6f9bc65337303c59861208e85024a3b0f4..a9fec81207179853cbac15a4f230a1306e4f17e0 100644 (file)
@@ -2612,7 +2612,7 @@ static void addTryBlockMapEntry(WinEHFuncInfo &FuncInfo, int TryLow,
     else
       HT.TypeDescriptor = cast<GlobalVariable>(TypeInfo->stripPointerCasts());
     HT.Adjectives = cast<ConstantInt>(CPI->getArgOperand(1))->getZExtValue();
-    HT.Handler = CPI->getNormalDest();
+    HT.Handler = CPI->getParent();
     HT.CatchObjRecoverIdx = -2;
     if (isa<ConstantPointerNull>(CPI->getArgOperand(2)))
       HT.CatchObj.Alloca = nullptr;
@@ -2770,8 +2770,7 @@ static void calculateExplicitSEHStateNumbers(WinEHFuncInfo &FuncInfo,
     const Function *Filter = dyn_cast<Function>(FilterOrNull);
     assert((Filter || FilterOrNull->isNullValue()) &&
            "unexpected filter value");
-    int TryState =
-        addSEHExcept(FuncInfo, ParentState, Filter, CPI->getNormalDest());
+    int TryState = addSEHExcept(FuncInfo, ParentState, Filter, CatchPadBB);
 
     // Everything in the __try block uses TryState as its parent state.
     FuncInfo.EHPadStateMap[CPI] = TryState;
index ffd4b49b688d9483db7ac62c3af0b7679b51e05a..053d484889b60d3db20f7a51a17398ea431bba67 100644 (file)
@@ -93,13 +93,14 @@ unreachable:                                      ; preds = %catch, %entry
 ; CHECK: # %try.cont.5
 ; CHECK: retq
 
-; The inner catch funclet contains %catch.3
-; CHECK: # %catch.3
-; CHECK: retq
+; The outer catch funclet contains %catch.dispatch
+; CHECK: # %catch.dispatch{{$}}
+; CHECK: callq _CxxThrowException
+; CHECK: # %unreachable
+; CHECK: ud2
 
-; The outer catch funclet contains %catch and %try.cont
-; CHECK: # %catch{{$}}
-; CHECK: # %try.cont{{$}}
+; The inner catch funclet contains %catch.dispatch.1
+; CHECK: # %catch.dispatch.1
 ; CHECK: retq
 
 
@@ -149,13 +150,13 @@ exit_two:
 ; CHECK-NOT: # exit_two
 ; CHECK: ud2
 
-; The catch(int) funclet contains %catch.2
-; CHECK: # %catch.2
+; The catch(...) funclet contains %catch.dispatch
+; CHECK: # %catch.dispatch{{$}}
 ; CHECK: callq exit
 ; CHECK: ud2
 
-; The catch(...) funclet contains %catch
-; CHECK: # %catch{{$}}
+; The catch(int) funclet contains %catch.dispatch.1
+; CHECK: # %catch.dispatch.1
 ; CHECK: callq exit
 ; CHECK: ud2
 
index e70ea793c79ed66b6beedee3fde8d8f91e4651c4..fc8e721c63dd9946d1d6aa73dabc4400fd52b47e 100644 (file)
@@ -112,7 +112,7 @@ ehcleanup.end:                                    ; preds = %ehcleanup
 ; CHECK:         addq    $48, %rsp
 ; CHECK:         popq    %rbp
 ; CHECK:         retq
-; CHECK: .LBB1_[[except1bb:[0-9]+]]:                                # %__except
+; CHECK: .LBB1_[[except1bb:[0-9]+]]:                                # %catch.dispatch
 ; CHECK: .Ltmp2:
 ; CHECK:         movl    $1, %ecx
 ; CHECK:         xorl    %edx, %edx
@@ -120,7 +120,9 @@ ehcleanup.end:                                    ; preds = %ehcleanup
 ; CHECK: .Ltmp3:
 ; CHECK:         callq   "?fin$0@0@main@@"
 ; CHECK:         jmp     .LBB1_[[epilogue]]
-; CHECK: .LBB1_[[except2bb:[0-9]+]]:                                # %__except.ret
+; CHECK: .LBB1_[[except2bb:[0-9]+]]:                                # %catch.dispatch.7
+; CHECK:         jmp     .LBB1_7
+; CHECK: # %__except.9
 ; CHECK:         leaq    "??_C@_06IBDBCMGJ@caught?$AA@"(%rip), %rcx
 ; CHECK:         callq   puts
 ; CHECK:         jmp     .LBB1_[[epilogue]]
index b9f99e61669987942731fd01cf0d29e9836bb4e9..05a59ac9b6234480296f6a58ea3e8330e5dae35a 100644 (file)
@@ -72,7 +72,7 @@ catchendblock:                                    ; preds = %catch,
 ; X86: jmp [[contbb]]
 
 ; X86: "?catch$[[catch1bb:[0-9]+]]@?0?try_catch_catch@4HA":
-; X86: LBB0_[[catch1bb]]: # %catch{{$}}
+; X86: LBB0_[[catch1bb]]: # %catch.dispatch{{$}}
 ; X86: pushl %ebp
 ; X86-NOT: pushl
 ; X86: subl $16, %esp
@@ -119,7 +119,7 @@ catchendblock:                                    ; preds = %catch,
 ; X64: retq
 
 ; X64: "?catch$[[catch1bb:[0-9]+]]@?0?try_catch_catch@4HA":
-; X64: LBB0_[[catch1bb]]: # %catch{{$}}
+; X64: LBB0_[[catch1bb]]: # %catch.dispatch{{$}}
 ; X64: movq %rdx, 16(%rsp)
 ; X64: pushq %rbp
 ; X64: .seh_pushreg 5
index 2c05fbe22f11bd0f478a687eec291843ed54b5c8..45d1085f3ad74aef9dfbfa1f668734ca67edd635 100644 (file)
@@ -25,6 +25,7 @@ $"\01??_R0H@8" = comdat any
 
 
 declare void @f(i32 %p, i32* %l)
+declare i1 @getbool()
 declare i32 @__CxxFrameHandler3(...)
 
 define i32 @try_catch_catch() personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) {
@@ -74,24 +75,24 @@ catchendblock:                                    ; preds = %catch, %catch.2, %c
 ; X86: retl
 
 ; FIXME: These should be de-duplicated.
-; X86: [[restorebb1:LBB0_[0-9]+]]: # %invoke.cont.2
+; X86: [[restorebb2:LBB0_[0-9]+]]: # %invoke.cont.3
 ; X86: movl -16(%ebp), %esp
 ; X86: addl $12, %ebp
 ; X86: jmp [[contbb]]
 
-; X86: [[restorebb2:LBB0_[0-9]+]]: # %invoke.cont.3
+; X86: [[restorebb1:LBB0_[0-9]+]]: # %invoke.cont.2
 ; X86: movl -16(%ebp), %esp
 ; X86: addl $12, %ebp
 ; X86: jmp [[contbb]]
 
 ; X86: "?catch$[[catch1bb:[0-9]+]]@?0?try_catch_catch@4HA":
-; X86: LBB0_[[catch1bb]]: # %catch{{$}}
+; X86: LBB0_[[catch1bb]]: # %catch.dispatch{{$}}
 ; X86: pushl %ebp
 ; X86: subl $8, %esp
 ; X86: addl $12, %ebp
+; X86: leal -[[local_offs]](%ebp), %[[addr_reg:[a-z]+]]
 ; X86: movl -32(%ebp), %[[e_reg:[a-z]+]]
 ; X86: movl $1, -{{[0-9]+}}(%ebp)
-; X86: leal -[[local_offs]](%ebp), %[[addr_reg:[a-z]+]]
 ; X86-DAG: movl %[[addr_reg]], 4(%esp)
 ; X86-DAG: movl %[[e_reg]], (%esp)
 ; X86: calll _f
@@ -101,12 +102,12 @@ catchendblock:                                    ; preds = %catch, %catch.2, %c
 ; X86-NEXT: retl
 
 ; X86: "?catch$[[catch2bb:[0-9]+]]@?0?try_catch_catch@4HA":
-; X86: LBB0_[[catch2bb]]: # %catch.2{{$}}
+; X86: LBB0_[[catch2bb]]: # %catch.dispatch.2{{$}}
 ; X86: pushl %ebp
 ; X86: subl $8, %esp
 ; X86: addl $12, %ebp
-; X86: movl $1, -{{[0-9]+}}(%ebp)
 ; X86: leal -[[local_offs]](%ebp), %[[addr_reg:[a-z]+]]
+; X86: movl $1, -{{[0-9]+}}(%ebp)
 ; X86-DAG: movl %[[addr_reg]], 4(%esp)
 ; X86-DAG: movl $3, (%esp)
 ; X86: calll _f
@@ -145,7 +146,7 @@ catchendblock:                                    ; preds = %catch, %catch.2, %c
 ; X64: retq
 
 ; X64: "?catch$[[catch1bb:[0-9]+]]@?0?try_catch_catch@4HA":
-; X64: LBB0_[[catch1bb]]: # %catch{{$}}
+; X64: LBB0_[[catch1bb]]: # %catch.dispatch{{$}}
 ; X64: movq %rdx, 16(%rsp)
 ; X64: pushq %rbp
 ; X64: .seh_pushreg 5
@@ -163,7 +164,7 @@ catchendblock:                                    ; preds = %catch, %catch.2, %c
 ; X64-NEXT: retq
 
 ; X64: "?catch$[[catch2bb:[0-9]+]]@?0?try_catch_catch@4HA":
-; X64: LBB0_[[catch2bb]]: # %catch.2{{$}}
+; X64: LBB0_[[catch2bb]]: # %catch.dispatch.2{{$}}
 ; X64: movq %rdx, 16(%rsp)
 ; X64: pushq %rbp
 ; X64: .seh_pushreg 5
@@ -221,3 +222,138 @@ catchendblock:                                    ; preds = %catch, %catch.2, %c
 ; X64-NEXT: .long   1
 ; X64-NEXT: .long   .Ltmp3@IMGREL+1
 ; X64-NEXT: .long   -1
+
+
+define i32 @branch_to_normal_dest() personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) {
+entry:
+  invoke void @f(i32 1, i32* null)
+          to label %try.cont unwind label %catch.dispatch
+
+catch.dispatch:
+  %0 = catchpad [i8* null, i32 64, i8* null]
+          to label %catch unwind label %catchendblock
+
+catch:
+  %V = call i1 @getbool()
+  br i1 %V, label %catch, label %catch.done
+
+catch.done:
+  catchret %0 to label %try.cont
+
+try.cont:
+  ret i32 0
+
+catchendblock:
+  catchendpad unwind to caller
+}
+
+; X86-LABEL: _branch_to_normal_dest:
+; X86: calll _f
+
+; X86: [[contbb:LBB1_[0-9]+]]: # %try.cont
+; X86: retl
+
+; X86: [[restorebb:LBB1_[0-9]+]]: # %catch.done
+; X86: movl -16(%ebp), %esp
+; X86: addl $12, %ebp
+; X86: jmp [[contbb]]
+
+; X86: "?catch$[[catchdispbb:[0-9]+]]@?0?branch_to_normal_dest@4HA":
+; X86: LBB1_[[catchdispbb]]: # %catch.dispatch{{$}}
+; X86: pushl %ebp
+; X86: subl $8, %esp
+; X86: addl $12, %ebp
+
+; X86: LBB1_[[catchbb:[0-9]+]]: # %catch
+; X86: movl    $-1, -16(%ebp)
+; X86: calll   _getbool
+; X86: testb   $1, %al
+; X86: jne LBB1_[[catchbb]]
+; X86: # %catch.done
+; X86-NEXT: movl $[[restorebb]], %eax
+; X86-NEXT: addl $8, %esp
+; X86-NEXT: popl %ebp
+; X86-NEXT: retl
+
+; X86: L__ehtable$branch_to_normal_dest:
+; X86: $handlerMap$0$branch_to_normal_dest:
+; X86-NEXT:   .long   64
+; X86-NEXT:   .long   0
+; X86-NEXT:   .long   0
+; X86-NEXT:   .long   "?catch$[[catchdispbb]]@?0?branch_to_normal_dest@4HA"
+
+; X64-LABEL: branch_to_normal_dest:
+; X64: # %entry
+; X64: pushq %rbp
+; X64: .seh_pushreg 5
+; X64: subq $48, %rsp
+; X64: .seh_stackalloc 48
+; X64: leaq 48(%rsp), %rbp
+; X64: .seh_setframe 5, 48
+; X64: .seh_endprologue
+; X64: .Ltmp[[before_call:[0-9]+]]:
+; X64: callq f
+; X64: .Ltmp[[after_call:[0-9]+]]:
+; X64: [[contbb:\.LBB1_[0-9]+]]: # %try.cont
+; X64: addq $48, %rsp
+; X64: popq %rbp
+; X64: retq
+
+; X64: "?catch$[[catchbb:[0-9]+]]@?0?branch_to_normal_dest@4HA":
+; X64: LBB1_[[catchbb]]: # %catch.dispatch{{$}}
+; X64: movq %rdx, 16(%rsp)
+; X64: pushq %rbp
+; X64: .seh_pushreg 5
+; X64: subq $32, %rsp
+; X64: .seh_stackalloc 32
+; X64: leaq 48(%rdx), %rbp
+; X64: .seh_endprologue
+; X64: .LBB1_[[normal_dest_bb:[0-9]+]]: # %catch
+; X64: callq   getbool
+; X64: testb   $1, %al
+; X64: jne     .LBB1_[[normal_dest_bb]]
+; X64: # %catch.done
+; X64: leaq [[contbb]](%rip), %rax
+; X64-NEXT: addq $32, %rsp
+; X64-NEXT: popq %rbp
+; X64-NEXT: retq
+
+; X64-LABEL: $cppxdata$branch_to_normal_dest:
+; X64-NEXT: .long   429065506
+; X64-NEXT: .long   2
+; X64-NEXT: .long   ($stateUnwindMap$branch_to_normal_dest)@IMGREL
+; X64-NEXT: .long   1
+; X64-NEXT: .long   ($tryMap$branch_to_normal_dest)@IMGREL
+; X64-NEXT: .long   3
+; X64-NEXT: .long   ($ip2state$branch_to_normal_dest)@IMGREL
+; X64-NEXT: .long   40
+; X64-NEXT: .long   0
+; X64-NEXT: .long   1
+
+; X64-LABEL: $stateUnwindMap$branch_to_normal_dest:
+; X64-NEXT: .long   -1
+; X64-NEXT: .long   0
+; X64-NEXT: .long   -1
+; X64-NEXT: .long   0
+
+; X64-LABEL: $tryMap$branch_to_normal_dest:
+; X64-NEXT: .long   0
+; X64-NEXT: .long   0
+; X64-NEXT: .long   1
+; X64-NEXT: .long   1
+; X64-NEXT: .long   ($handlerMap$0$branch_to_normal_dest)@IMGREL
+
+; X64-LABEL: $handlerMap$0$branch_to_normal_dest:
+; X64-NEXT: .long   64
+; X64-NEXT: .long   0
+; X64-NEXT: .long   0
+; X64-NEXT: .long   "?catch$[[catchbb]]@?0?branch_to_normal_dest@4HA"@IMGREL
+; X64-NEXT: .long   56
+
+; X64-LABEL: $ip2state$branch_to_normal_dest:
+; X64-NEXT: .long   .Lfunc_begin1@IMGREL
+; X64-NEXT: .long   -1
+; X64-NEXT: .long   .Ltmp[[before_call]]@IMGREL
+; X64-NEXT: .long   0
+; X64-NEXT: .long   .Ltmp[[after_call]]@IMGREL+1
+; X64-NEXT: .long   -1
index 63be4f26afa62d3aebc53fb4ad705d63b0a47e6c..52589ee1918dbb3b390f79441c55dfec7378bbd0 100644 (file)
@@ -70,7 +70,7 @@ declare i32 @__CxxFrameHandler3(...)
 ; CHECK: "?catch$[[catch:[0-9]+]]@?0??f@@YAXXZ@4HA":
 ; CHECK: .seh_proc "?catch$[[catch]]@?0??f@@YAXXZ@4HA"
 ; CHECK-NEXT: .seh_handler __CxxFrameHandler3, @unwind, @except
-; CHECK: LBB0_[[catch]]: # %catch{{$}}
+; CHECK: LBB0_[[catch]]: # %catch.dispatch{{$}}
 
 ; Emit CFI for pushing RBP.
 ; CHECK: movq    %rdx, 16(%rsp)
index 1d635dc5711f016e3ca4c6cc957c648b94157727..dd14f2df6899eb4aa0559eca9bc8451ec6ae8714 100644 (file)
@@ -197,6 +197,35 @@ entry:
   ret i32 1
 }
 
+define void @code_in_catchpad() #0 personality i8* bitcast (i32 (...)* @_except_handler3 to i8*) {
+entry:
+  invoke void @f(i32 1) #3
+          to label %__except unwind label %catch.dispatch
+
+catch.dispatch:                                   ; preds = %entry
+  %0 = catchpad [i8* bitcast (i32 ()* @try_except_filter_catchall to i8*)] to label %__except.ret unwind label %catchendblock
+
+__except.ret:                                     ; preds = %catch.dispatch
+  call void @f(i32 2)
+  catchret %0 to label %__except
+
+__except:
+  ret void
+
+catchendblock:                                    ; preds = %catch.dispatch
+  catchendpad unwind to caller
+}
+
+; CHECK-LABEL: _code_in_catchpad:
+; CHECK: # %catch.dispatch
+; CHECK-NEXT:         movl    -24(%ebp), %esp
+; CHECK-NEXT:         addl    $12, %ebp
+; CHECK: # %__except.ret
+; CHECK-NEXT:         movl    $-1, -16(%ebp)
+; CHECK-NEXT:         movl    $2, (%esp)
+; CHECK-NEXT:         calll   _f
+
+
 ; Function Attrs: nounwind readnone
 declare i8* @llvm.frameaddress(i32) #1