Skip to content

[SimplifyCFG] Avoid increasing too many phi entries when removing empty blocks #104887

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Sep 25, 2024
35 changes: 34 additions & 1 deletion llvm/lib/Transforms/Utils/Local.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,12 @@ static cl::opt<unsigned> PHICSENumPHISmallSize(
"When the basic block contains not more than this number of PHI nodes, "
"perform a (faster!) exhaustive search instead of set-driven one."));

static cl::opt<unsigned> MaxPhiEntriesIncreaseAfterRemovingEmptyBlock(
"max-phi-entries-increase-after-removing-empty-block", cl::init(1000),
cl::Hidden,
cl::desc("Stop removing an empty block if removing it will introduce more "
"than this number of phi entries in its successor"));

// Max recursion depth for collectBitParts used when detecting bswap and
// bitreverse idioms.
static const unsigned BitPartRecursionMaxDepth = 48;
Expand Down Expand Up @@ -1047,6 +1053,33 @@ CanRedirectPredsOfEmptyBBToSucc(BasicBlock *BB, BasicBlock *Succ,
return true;
}

/// Check whether removing \p BB will make the phis in its \p Succ have too
/// many incoming entries. This function does not check whether \p BB is
/// foldable or not.
static bool introduceTooManyPhiEntries(BasicBlock *BB, BasicBlock *Succ) {
// If BB only has one predecessor, then removing it will not introduce more
// incoming edges for phis.
if (BB->hasNPredecessors(1))
return false;
unsigned NumPreds = pred_size(BB);
unsigned NumChangedPhi = 0;
for (auto &Phi : Succ->phis()) {
// If the incoming value is a phi and the phi is defined in BB,
// then removing BB will not increase the total phi entries of the ir.
if (auto *IncomingPhi = dyn_cast<PHINode>(Phi.getIncomingValueForBlock(BB)))
if (IncomingPhi->getParent() == BB)
continue;
// Otherwise, we need to add entries to the phi
NumChangedPhi++;
}
// For every phi that needs to be changed, (NumPreds - 1) new entries will be
// added. If the total increase in phi entries exceeds
// MaxPhiEntriesIncreaseAfterRemovingEmptyBlock, it will be considered as
// introducing too many new phi entries.
return (NumPreds - 1) * NumChangedPhi >
MaxPhiEntriesIncreaseAfterRemovingEmptyBlock;
}

/// Replace a value flowing from a block to a phi with
/// potentially multiple instances of that value flowing from the
/// block's predecessors to the phi.
Expand Down Expand Up @@ -1146,7 +1179,7 @@ bool llvm::TryToSimplifyUncondBranchFromEmptyBlock(BasicBlock *BB,
BBKillable ||
CanRedirectPredsOfEmptyBBToSucc(BB, Succ, BBPreds, SuccPreds, CommonPred);

if (!BBKillable && !BBPhisMergeable)
if ((!BBKillable && !BBPhisMergeable) || introduceTooManyPhiEntries(BB, Succ))
return false;

// Check to see if merging these blocks/phis would cause conflicts for any of
Expand Down
164 changes: 164 additions & 0 deletions llvm/test/Transforms/SimplifyCFG/avoid-complex-phi.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
; RUN: opt < %s -max-phi-entries-increase-after-removing-empty-block=12 -passes=simplifycfg -S | FileCheck --check-prefixes=CHECK-12 %s
; RUN: opt < %s -max-phi-entries-increase-after-removing-empty-block=11 -passes=simplifycfg -S | FileCheck --check-prefixes=CHECK-11 %s
; RUN: opt < %s -max-phi-entries-increase-after-removing-empty-block=4 -passes=simplifycfg -S | FileCheck --check-prefixes=CHECK-4 %s
;
; This test has the following CFG:
; 1. entry has a switch to 4 blocks: B1 - B4
; 2. For B1 and B2, it branches to B5 and B6
; 3. For B3 and B4, it branches to B5 and B7
; 4. In B5, %val is defined as phi taking values from B1 to B4
; 5. B5, B6, B7 branch to block Merge unconditionally
; 6. Block Merge has 5 phis(%x1 - %x4 and %val_merge).
;
; If we remove B5, %x1 - %x4 will increase the number of phi entries by (4 - 1) * 4 = 12. For %val_merge, since the value taking from B5
; is defined in B5, it will not increase the number of phi entries (it can be considered as move the entries from %val to
; %val_merge). Therefore, removing B5 will increase the number of phi entries by 12 (not (4 - 1) * 5 = 15).
;
; If we remove B6 / B7, it will increase the number of phi entries by (2 - 1) * 5 = 5.
;
; In the first test, max-phi-entries-increase-after-removing-empty-block is set to be 12, then B5 will be removed.
; In the second test, max-phi-entries-increase-after-removing-empty-block is set to be 11, then B5 should not be removed,
; but B6 and B7 can be removed.
; In the third test, max-phi-entries-increase-after-removing-empty-block is set to be 4, then no BB can be removed.
;
define void @foo(i32 %a, i32 %val1, i32 %val2, i32 %val3, i32 %val4) {
; CHECK-12-LABEL: define void @foo(
; CHECK-12-SAME: i32 [[A:%.*]], i32 [[VAL1:%.*]], i32 [[VAL2:%.*]], i32 [[VAL3:%.*]], i32 [[VAL4:%.*]]) {
; CHECK-12-NEXT: [[ENTRY:.*:]]
; CHECK-12-NEXT: switch i32 [[A]], label %[[B1:.*]] [
; CHECK-12-NEXT: i32 4, label %[[B4:.*]]
; CHECK-12-NEXT: i32 2, label %[[B2:.*]]
; CHECK-12-NEXT: i32 3, label %[[B3:.*]]
; CHECK-12-NEXT: ]
; CHECK-12: [[B1]]:
; CHECK-12-NEXT: [[CMP1:%.*]] = icmp eq i32 [[VAL1]], 1
; CHECK-12-NEXT: br i1 [[CMP1]], label %[[B6:.*]], label %[[MERGE:.*]]
; CHECK-12: [[B2]]:
; CHECK-12-NEXT: [[CMP2:%.*]] = icmp eq i32 [[VAL2]], 2
; CHECK-12-NEXT: br i1 [[CMP2]], label %[[B6]], label %[[MERGE]]
; CHECK-12: [[B3]]:
; CHECK-12-NEXT: [[CMP3:%.*]] = icmp eq i32 [[VAL3]], 3
; CHECK-12-NEXT: br i1 [[CMP3]], label %[[B7:.*]], label %[[MERGE]]
; CHECK-12: [[B4]]:
; CHECK-12-NEXT: [[CMP4:%.*]] = icmp eq i32 [[VAL4]], 4
; CHECK-12-NEXT: br i1 [[CMP4]], label %[[B7]], label %[[MERGE]]
; CHECK-12: [[B6]]:
; CHECK-12-NEXT: br label %[[MERGE]]
; CHECK-12: [[B7]]:
; CHECK-12-NEXT: br label %[[MERGE]]
; CHECK-12: [[MERGE]]:
; CHECK-12-NEXT: [[X1:%.*]] = phi i16 [ 0, %[[B6]] ], [ 2, %[[B7]] ], [ 1, %[[B4]] ], [ 1, %[[B3]] ], [ 1, %[[B2]] ], [ 1, %[[B1]] ]
; CHECK-12-NEXT: [[X2:%.*]] = phi i16 [ 0, %[[B6]] ], [ 2, %[[B7]] ], [ 2, %[[B4]] ], [ 2, %[[B3]] ], [ 2, %[[B2]] ], [ 2, %[[B1]] ]
; CHECK-12-NEXT: [[X3:%.*]] = phi i16 [ 0, %[[B6]] ], [ 2, %[[B7]] ], [ 3, %[[B4]] ], [ 3, %[[B3]] ], [ 3, %[[B2]] ], [ 3, %[[B1]] ]
; CHECK-12-NEXT: [[X4:%.*]] = phi i16 [ 0, %[[B6]] ], [ 2, %[[B7]] ], [ 4, %[[B4]] ], [ 4, %[[B3]] ], [ 4, %[[B2]] ], [ 4, %[[B1]] ]
; CHECK-12-NEXT: [[VAL_MERGE:%.*]] = phi i32 [ 0, %[[B6]] ], [ 2, %[[B7]] ], [ [[VAL1]], %[[B1]] ], [ [[VAL2]], %[[B2]] ], [ [[VAL3]], %[[B3]] ], [ [[VAL4]], %[[B4]] ]
; CHECK-12-NEXT: ret void
;
; CHECK-11-LABEL: define void @foo(
; CHECK-11-SAME: i32 [[A:%.*]], i32 [[VAL1:%.*]], i32 [[VAL2:%.*]], i32 [[VAL3:%.*]], i32 [[VAL4:%.*]]) {
; CHECK-11-NEXT: [[ENTRY:.*:]]
; CHECK-11-NEXT: switch i32 [[A]], label %[[B1:.*]] [
; CHECK-11-NEXT: i32 4, label %[[B4:.*]]
; CHECK-11-NEXT: i32 2, label %[[B2:.*]]
; CHECK-11-NEXT: i32 3, label %[[B3:.*]]
; CHECK-11-NEXT: ]
; CHECK-11: [[B1]]:
; CHECK-11-NEXT: [[CMP1:%.*]] = icmp eq i32 [[VAL1]], 1
; CHECK-11-NEXT: br i1 [[CMP1]], label %[[MERGE:.*]], label %[[B5:.*]]
; CHECK-11: [[B2]]:
; CHECK-11-NEXT: [[CMP2:%.*]] = icmp eq i32 [[VAL2]], 2
; CHECK-11-NEXT: br i1 [[CMP2]], label %[[MERGE]], label %[[B5]]
; CHECK-11: [[B3]]:
; CHECK-11-NEXT: [[CMP3:%.*]] = icmp eq i32 [[VAL3]], 3
; CHECK-11-NEXT: br i1 [[CMP3]], label %[[MERGE]], label %[[B5]]
; CHECK-11: [[B4]]:
; CHECK-11-NEXT: [[CMP4:%.*]] = icmp eq i32 [[VAL4]], 4
; CHECK-11-NEXT: br i1 [[CMP4]], label %[[MERGE]], label %[[B5]]
; CHECK-11: [[B5]]:
; CHECK-11-NEXT: [[VAL:%.*]] = phi i32 [ [[VAL1]], %[[B1]] ], [ [[VAL2]], %[[B2]] ], [ [[VAL3]], %[[B3]] ], [ [[VAL4]], %[[B4]] ]
; CHECK-11-NEXT: br label %[[MERGE]]
; CHECK-11: [[MERGE]]:
; CHECK-11-NEXT: [[X1:%.*]] = phi i16 [ 1, %[[B5]] ], [ 0, %[[B2]] ], [ 0, %[[B1]] ], [ 2, %[[B4]] ], [ 2, %[[B3]] ]
; CHECK-11-NEXT: [[X2:%.*]] = phi i16 [ 2, %[[B5]] ], [ 0, %[[B2]] ], [ 0, %[[B1]] ], [ 2, %[[B4]] ], [ 2, %[[B3]] ]
; CHECK-11-NEXT: [[X3:%.*]] = phi i16 [ 3, %[[B5]] ], [ 0, %[[B2]] ], [ 0, %[[B1]] ], [ 2, %[[B4]] ], [ 2, %[[B3]] ]
; CHECK-11-NEXT: [[X4:%.*]] = phi i16 [ 4, %[[B5]] ], [ 0, %[[B2]] ], [ 0, %[[B1]] ], [ 2, %[[B4]] ], [ 2, %[[B3]] ]
; CHECK-11-NEXT: [[VAL_MERGE:%.*]] = phi i32 [ [[VAL]], %[[B5]] ], [ 0, %[[B2]] ], [ 0, %[[B1]] ], [ 2, %[[B4]] ], [ 2, %[[B3]] ]
; CHECK-11-NEXT: ret void
;
; CHECK-4-LABEL: define void @foo(
; CHECK-4-SAME: i32 [[A:%.*]], i32 [[VAL1:%.*]], i32 [[VAL2:%.*]], i32 [[VAL3:%.*]], i32 [[VAL4:%.*]]) {
; CHECK-4-NEXT: [[ENTRY:.*:]]
; CHECK-4-NEXT: switch i32 [[A]], label %[[B1:.*]] [
; CHECK-4-NEXT: i32 4, label %[[B4:.*]]
; CHECK-4-NEXT: i32 2, label %[[B2:.*]]
; CHECK-4-NEXT: i32 3, label %[[B3:.*]]
; CHECK-4-NEXT: ]
; CHECK-4: [[B1]]:
; CHECK-4-NEXT: [[CMP1:%.*]] = icmp eq i32 [[VAL1]], 1
; CHECK-4-NEXT: br i1 [[CMP1]], label %[[B6:.*]], label %[[B5:.*]]
; CHECK-4: [[B2]]:
; CHECK-4-NEXT: [[CMP2:%.*]] = icmp eq i32 [[VAL2]], 2
; CHECK-4-NEXT: br i1 [[CMP2]], label %[[B6]], label %[[B5]]
; CHECK-4: [[B3]]:
; CHECK-4-NEXT: [[CMP3:%.*]] = icmp eq i32 [[VAL3]], 3
; CHECK-4-NEXT: br i1 [[CMP3]], label %[[B7:.*]], label %[[B5]]
; CHECK-4: [[B4]]:
; CHECK-4-NEXT: [[CMP4:%.*]] = icmp eq i32 [[VAL4]], 4
; CHECK-4-NEXT: br i1 [[CMP4]], label %[[B7]], label %[[B5]]
; CHECK-4: [[B5]]:
; CHECK-4-NEXT: [[VAL:%.*]] = phi i32 [ [[VAL1]], %[[B1]] ], [ [[VAL2]], %[[B2]] ], [ [[VAL3]], %[[B3]] ], [ [[VAL4]], %[[B4]] ]
; CHECK-4-NEXT: br label %[[MERGE:.*]]
; CHECK-4: [[B6]]:
; CHECK-4-NEXT: br label %[[MERGE]]
; CHECK-4: [[B7]]:
; CHECK-4-NEXT: br label %[[MERGE]]
; CHECK-4: [[MERGE]]:
; CHECK-4-NEXT: [[X1:%.*]] = phi i16 [ 1, %[[B5]] ], [ 0, %[[B6]] ], [ 2, %[[B7]] ]
; CHECK-4-NEXT: [[X2:%.*]] = phi i16 [ 2, %[[B5]] ], [ 0, %[[B6]] ], [ 2, %[[B7]] ]
; CHECK-4-NEXT: [[X3:%.*]] = phi i16 [ 3, %[[B5]] ], [ 0, %[[B6]] ], [ 2, %[[B7]] ]
; CHECK-4-NEXT: [[X4:%.*]] = phi i16 [ 4, %[[B5]] ], [ 0, %[[B6]] ], [ 2, %[[B7]] ]
; CHECK-4-NEXT: [[VAL_MERGE:%.*]] = phi i32 [ [[VAL]], %[[B5]] ], [ 0, %[[B6]] ], [ 2, %[[B7]] ]
; CHECK-4-NEXT: ret void
;
entry:
switch i32 %a, label %B1 [
i32 4, label %B4
i32 2, label %B2
i32 3, label %B3
]

B1: ; preds = %entry
%cmp1 = icmp eq i32 %val1, 1
br i1 %cmp1, label %B6, label %B5

B2: ; preds = %entry
%cmp2 = icmp eq i32 %val2, 2
br i1 %cmp2, label %B6, label %B5

B3: ; preds = %entry
%cmp3 = icmp eq i32 %val3, 3
br i1 %cmp3, label %B7, label %B5

B4: ; preds = %entry
%cmp4 = icmp eq i32 %val4, 4
br i1 %cmp4, label %B7, label %B5

B5: ; preds = %B4, %B3, %B2, %B1
%val = phi i32 [ %val1, %B1 ], [ %val2, %B2 ], [ %val3, %B3 ], [ %val4, %B4 ]
br label %Merge

B6: ; preds = %B2, %B1
br label %Merge

B7: ; preds = %B4, %B3
br label %Merge

Merge: ; preds = %B7, %B6, %B5
%x1 = phi i16 [ 1, %B5 ], [ 0, %B6 ], [ 2, %B7 ]
%x2 = phi i16 [ 2, %B5 ], [ 0, %B6 ], [ 2, %B7 ]
%x3 = phi i16 [ 3, %B5 ], [ 0, %B6 ], [ 2, %B7 ]
%x4 = phi i16 [ 4, %B5 ], [ 0, %B6 ], [ 2, %B7 ]
%val_merge = phi i32 [ %val, %B5 ], [ 0, %B6 ], [ 2, %B7 ]
ret void
}
Loading