[WinEHPrepare] Fix catchret successor phi demotion
authorJoseph Tremoulet <jotrem@microsoft.com>
Mon, 17 Aug 2015 13:51:37 +0000 (13:51 +0000)
committerJoseph Tremoulet <jotrem@microsoft.com>
Mon, 17 Aug 2015 13:51:37 +0000 (13:51 +0000)
Summary:
When demoting an SSA value that has a use on a phi and one of the phi's
predecessors terminates with catchret, the edge needs to be split and the
load inserted in the new block, else we'll still have a cross-funclet SSA
value.

Add a test for this, and for the similar case where a def to be spilled is
on and invoke and a critical edge, which was already implemented but
missing a test.

Reviewers: majnemer

Subscribers: llvm-commits

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

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

lib/CodeGen/WinEHPrepare.cpp
test/CodeGen/WinEH/wineh-demotion.ll

index 8ab098b..a299c4f 100644 (file)
@@ -3289,6 +3289,42 @@ void WinEHPrepare::replaceUseWithLoad(Value *V, Use &U, AllocaInst *&SpillSlot,
     // coming in from the same block, which is illegal SSA form.
     // For this reason, we keep track of and reuse loads we insert.
     BasicBlock *IncomingBlock = UsingPHI->getIncomingBlock(U);
+    if (auto *CatchRet =
+            dyn_cast<CatchReturnInst>(IncomingBlock->getTerminator())) {
+      // Putting a load above a catchret and use on the phi would still leave
+      // a cross-funclet def/use.  We need to split the edge, change the
+      // catchret to target the new block, and put the load there.
+      BasicBlock *PHIBlock = UsingInst->getParent();
+      BasicBlock *NewBlock = SplitEdge(IncomingBlock, PHIBlock);
+      // SplitEdge gives us:
+      //   IncomingBlock:
+      //     ...
+      //     br label %NewBlock
+      //   NewBlock:
+      //     catchret label %PHIBlock
+      // But we need:
+      //   IncomingBlock:
+      //     ...
+      //     catchret label %NewBlock
+      //   NewBlock:
+      //     br label %PHIBlock
+      // So move the terminators to each others' blocks and swap their
+      // successors.
+      BranchInst *Goto = cast<BranchInst>(IncomingBlock->getTerminator());
+      Goto->removeFromParent();
+      CatchRet->removeFromParent();
+      IncomingBlock->getInstList().push_back(CatchRet);
+      NewBlock->getInstList().push_back(Goto);
+      Goto->setSuccessor(0, PHIBlock);
+      CatchRet->setSuccessor(NewBlock);
+      // Update the color mapping for the newly split edge.
+      std::set<BasicBlock *> &ColorsForPHIBlock = BlockColors[PHIBlock];
+      BlockColors[NewBlock] = ColorsForPHIBlock;
+      for (BasicBlock *FuncletPad : ColorsForPHIBlock)
+        FuncletBlocks[FuncletPad].insert(NewBlock);
+      // Treat the new block as incoming for load insertion.
+      IncomingBlock = NewBlock;
+    }
     Value *&Load = Loads[IncomingBlock];
     // Insert the load into the predecessor block
     if (!Load)
index 9f49e13..d29c5d2 100644 (file)
@@ -294,3 +294,89 @@ catchend:
 exit:
   ret void
 }
+
+; CHECK-LABEL: @test6(
+define void @test6() personality i32 (...)* @__CxxFrameHandler3 {
+entry:
+  ; Since %x needs to be stored but the edge to loop is critical,
+  ; it needs to be split
+  ; CHECK: entry:
+  ; CHECK: invoke i32 @g
+  ; CHECK-NEXT: to label %[[SplitBlock:[^ ]+]] unwind label %to_caller
+  %x = invoke i32 @g()
+          to label %loop unwind label %to_caller
+  ; The store should be in the split block
+  ; CHECK: [[SplitBlock]]:
+  ; CHECK: store i32 %x, i32* [[SpillSlot:%[^ ]+]]
+  ; CHECK: br label %loop
+to_caller:
+  cleanuppad void []
+  cleanupret void unwind to caller
+loop:
+  invoke void @f()
+          to label %loop unwind label %cleanup
+cleanup:
+  ; CHECK: cleanup:
+  ; CHECK:   [[Load:%[^ ]+]] = load i32, i32* [[SpillSlot]]
+  ; CHECK:   call void @h(i32 [[Load]])
+  cleanuppad void []
+  call void @h(i32 %x)
+  cleanupret void unwind to caller
+}
+
+; CHECK-LABEL: @test7(
+define void @test7() personality i32 (...)* @__CxxFrameHandler3 {
+entry:
+  ; %x is an EH pad phi, so gets stored in pred here
+  ; CHECK: entry:
+  ; CHECK:   store i32 1, i32* [[SlotX:%[^ ]+]]
+  ; CHECK:   invoke void @f()
+  invoke void @f()
+     to label %invoke.cont unwind label %catchpad
+invoke.cont:
+  ; %x is an EH pad phi, so gets stored in pred here
+  ; CHECK: invoke.cont:
+  ; CHECK:   store i32 2, i32* [[SlotX]]
+  ; CHECK:   invoke void @f()
+  invoke void @f()
+    to label %exit unwind label %catchpad
+catchpad:
+  ; %x phi should be eliminated
+  ; CHECK: catchpad:
+  ; CHECK-NEXT: catchpad void
+  %x = phi i32 [ 1, %entry ], [ 2, %invoke.cont ]
+  catchpad void [] to label %catch unwind label %catchend
+catch:
+  %b = call i1 @i()
+  br i1 %b, label %left, label %right
+left:
+  ; Edge from %left to %join needs to be split so that
+  ; the load of %x can be inserted *after* the catchret
+  ; CHECK: left:
+  ; CHECK-NEXT: catchret void to label %[[SplitLeft:[^ ]+]]
+  catchret void to label %join
+  ; CHECK: [[SplitLeft]]:
+  ; CHECK:   [[LoadX:%[^ ]+]] = load i32, i32* [[SlotX]]
+  ; CHECK:   br label %join
+right:
+  ; Edge from %right to %join needs to be split so that
+  ; the load of %y can be inserted *after* the catchret
+  ; CHECK: right:
+  ; CHECK:   store i32 %y, i32* [[SlotY:%[^ ]+]]
+  ; CHECK:   catchret void to label %[[SplitRight:[^ ]+]]
+  %y = call i32 @g()
+  catchret void to label %join
+  ; CHECK: [[SplitRight]]:
+  ; CHECK:   [[LoadY:%[^ ]+]] = load i32, i32* [[SlotY]]
+  ; CHECK:   br label %join
+catchend:
+  catchendpad unwind to caller
+join:
+  ; CHECK: join:
+  ; CHECK:   %phi = phi i32 [ [[LoadX]], %[[SplitLeft]] ], [ [[LoadY]], %[[SplitRight]] ]
+  %phi = phi i32 [ %x, %left ], [ %y, %right ]
+  call void @h(i32 %phi)
+  br label %exit
+exit:
+  ret void
+}