Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 33 additions & 5 deletions llvm/lib/Transforms/Scalar/DFAJumpThreading.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -122,16 +122,22 @@ static cl::opt<unsigned>
cl::desc("Maximum cost accepted for the transformation"),
cl::Hidden, cl::init(50));

extern cl::opt<bool> ProfcheckDisableMetadataFixes;

} // namespace llvm

static cl::opt<double> MaxClonedRate(
"dfa-max-cloned-rate",
cl::desc(
"Maximum cloned instructions rate accepted for the transformation"),
cl::Hidden, cl::init(7.5));

static cl::opt<unsigned>
MaxOuterUseBlocks("dfa-max-out-use-blocks",
cl::desc("Maximum unduplicated blocks with outer uses "
"accepted for the transformation"),
cl::Hidden, cl::init(40));

extern cl::opt<bool> ProfcheckDisableMetadataFixes;

} // namespace llvm

namespace {
class SelectInstToUnfold {
SelectInst *SI;
Expand Down Expand Up @@ -965,8 +971,16 @@ struct TransformDFA {
// SLPVectorizer.
// TODO: Thread the switch partially before reaching the threshold.
uint64_t NumOrigInst = 0;
for (auto *BB : DuplicateMap.keys())
uint64_t NumOuterUseBlock = 0;
for (auto *BB : DuplicateMap.keys()) {
NumOrigInst += BB->sizeWithoutDebug();
// Only unduplicated blocks with single predecessor require new phi
// nodes.
for (auto *Succ : successors(BB))
if (!DuplicateMap.count(Succ) && Succ->getSinglePredecessor())
NumOuterUseBlock++;
}

if (double(NumClonedInst) / double(NumOrigInst) > MaxClonedRate) {
LLVM_DEBUG(dbgs() << "DFA Jump Threading: Not jump threading, too much "
"instructions wll be cloned\n");
Expand All @@ -977,6 +991,20 @@ struct TransformDFA {
return false;
}

// Too much unduplicated blocks with outer uses may cause too much
// insertions of phi nodes for duplicated definitions. TODO: Drop this
// threshold if we come up with another way to reduce the number of inserted
// phi nodes.
if (NumOuterUseBlock > MaxOuterUseBlocks) {
LLVM_DEBUG(dbgs() << "DFA Jump Threading: Not jump threading, too much "
"blocks with outer uses\n");
ORE->emit([&]() {
return OptimizationRemarkMissed(DEBUG_TYPE, "NotProfitable", Switch)
<< "Too much blocks with outer uses.";
});
return false;
}

InstructionCost DuplicationCost = 0;

unsigned JumpTableSize = 0;
Expand Down
253 changes: 253 additions & 0 deletions llvm/test/Transforms/DFAJumpThreading/max-outer-uses.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
; RUN: opt -S -passes=dfa-jump-threading -dfa-max-out-use-blocks=5 %s | FileCheck %s

define void @max_outer_uses_by_switch(i32 %cond) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, it seems like this doesn't really demonstrate the phi explosion issue. Without the limit you get:

diff --git a/llvm/test/Transforms/DFAJumpThreading/max-outer-uses.ll b/llvm/test/Transforms/DFAJumpThreading/max-outer-uses.ll
index f059754b8221..24f06c77614c 100644
--- a/llvm/test/Transforms/DFAJumpThreading/max-outer-uses.ll
+++ b/llvm/test/Transforms/DFAJumpThreading/max-outer-uses.ll
@@ -1,5 +1,5 @@
 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
-; RUN: opt -S -passes=dfa-jump-threading -dfa-max-out-use-blocks=5 %s | FileCheck %s
+; RUN: opt -S -passes=dfa-jump-threading -dfa-max-out-use-blocks=10 %s | FileCheck %s
 
 define void @max_outer_uses_by_switch(i32 %cond) {
 ; CHECK-LABEL: define void @max_outer_uses_by_switch(
@@ -7,23 +7,36 @@ define void @max_outer_uses_by_switch(i32 %cond) {
 ; CHECK-NEXT:  [[ENTRY:.*]]:
 ; CHECK-NEXT:    br label %[[SWITCH_BB:.*]]
 ; CHECK:       [[SWITCH_BB]]:
-; CHECK-NEXT:    [[PHI:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[ONE:%.*]], %[[CASE1_SUCC:.*]] ], [ 2, %[[CASE2:.*]] ]
+; CHECK-NEXT:    [[PHI:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ poison, %[[CASE1_SUCC:.*]] ]
 ; CHECK-NEXT:    switch i32 [[PHI]], label %[[DEFAULT_DEST:.*]] [
 ; CHECK-NEXT:      i32 0, label %[[CASE1:.*]]
-; CHECK-NEXT:      i32 1, label %[[CASE2]]
+; CHECK-NEXT:      i32 1, label %[[CASE2:.*]]
 ; CHECK-NEXT:    ]
+; CHECK:       [[SWITCH_BB_JT2:.*]]:
+; CHECK-NEXT:    [[PHI_JT2:%.*]] = phi i32 [ 2, %[[CASE2]] ]
+; CHECK-NEXT:    br label %[[DEFAULT_DEST]]
+; CHECK:       [[SWITCH_BB_JT1:.*]]:
+; CHECK-NEXT:    [[PHI_JT1:%.*]] = phi i32 [ [[ONE_JT1:%.*]], %[[CASE1_SUCC_JT1:.*]] ]
+; CHECK-NEXT:    br label %[[CASE2]]
 ; CHECK:       [[CASE1]]:
-; CHECK-NEXT:    br label %[[CASE1_SUCC]]
+; CHECK-NEXT:    br label %[[CASE1_SUCC_JT1]]
 ; CHECK:       [[CASE1_SUCC]]:
-; CHECK-NEXT:    [[ONE]] = phi i32 [ 1, %[[CASE1]] ]
 ; CHECK-NEXT:    switch i32 [[COND]], label %[[SWITCH_BB]] [
 ; CHECK-NEXT:      i32 0, label %[[OUTER1:.*]]
 ; CHECK-NEXT:      i32 1, label %[[OUTER2:.*]]
 ; CHECK-NEXT:      i32 2, label %[[OUTER3:.*]]
 ; CHECK-NEXT:      i32 3, label %[[OUTER4:.*]]
 ; CHECK-NEXT:    ]
+; CHECK:       [[CASE1_SUCC_JT1]]:
+; CHECK-NEXT:    [[ONE_JT1]] = phi i32 [ 1, %[[CASE1]] ]
+; CHECK-NEXT:    switch i32 [[COND]], label %[[SWITCH_BB_JT1]] [
+; CHECK-NEXT:      i32 0, label %[[OUTER1]]
+; CHECK-NEXT:      i32 1, label %[[OUTER2]]
+; CHECK-NEXT:      i32 2, label %[[OUTER3]]
+; CHECK-NEXT:      i32 3, label %[[OUTER4]]
+; CHECK-NEXT:    ]
 ; CHECK:       [[CASE2]]:
-; CHECK-NEXT:    br label %[[SWITCH_BB]]
+; CHECK-NEXT:    br label %[[SWITCH_BB_JT2]]
 ; CHECK:       [[OUTER1]]:
 ; CHECK-NEXT:    ret void
 ; CHECK:       [[OUTER2]]:

which does not introduce any new (multi-operand) phis.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I updated the tests now. They should show new phis now.

; CHECK-LABEL: define void @max_outer_uses_by_switch(
; CHECK-SAME: i32 [[COND:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*]]:
; CHECK-NEXT: br label %[[SWITCH_BB:.*]]
; CHECK: [[SWITCH_BB]]:
; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[ONE:%.*]], %[[CASE1_SUCC:.*]] ], [ 2, %[[CASE2:.*]] ]
; CHECK-NEXT: switch i32 [[PHI]], label %[[DEFAULT_DEST:.*]] [
; CHECK-NEXT: i32 0, label %[[CASE1:.*]]
; CHECK-NEXT: i32 1, label %[[CASE2]]
; CHECK-NEXT: ]
; CHECK: [[CASE1]]:
; CHECK-NEXT: br label %[[CASE1_SUCC]]
; CHECK: [[CASE1_SUCC]]:
; CHECK-NEXT: [[ONE]] = phi i32 [ 1, %[[CASE1]] ]
; CHECK-NEXT: switch i32 [[COND]], label %[[SWITCH_BB]] [
; CHECK-NEXT: i32 0, label %[[OUTER1:.*]]
; CHECK-NEXT: i32 1, label %[[OUTER2:.*]]
; CHECK-NEXT: i32 2, label %[[OUTER3:.*]]
; CHECK-NEXT: i32 3, label %[[OUTER4:.*]]
; CHECK-NEXT: ]
; CHECK: [[CASE2]]:
; CHECK-NEXT: br label %[[SWITCH_BB]]
; CHECK: [[OUTER1]]:
; CHECK-NEXT: ret void
; CHECK: [[OUTER2]]:
; CHECK-NEXT: ret void
; CHECK: [[OUTER3]]:
; CHECK-NEXT: ret void
; CHECK: [[OUTER4]]:
; CHECK-NEXT: ret void
; CHECK: [[DEFAULT_DEST]]:
; CHECK-NEXT: ret void
;
entry:
br label %switch_bb

switch_bb:
%phi = phi i32 [ 0, %entry ], [ %one, %case1_succ ], [ 2, %case2 ]
switch i32 %phi, label %default_dest [
i32 0, label %case1
i32 1, label %case2
]

case1:
br label %case1_succ

case1_succ:
%one = phi i32 [ 1, %case1 ]
switch i32 %cond, label %switch_bb [
i32 0, label %outer1
i32 1, label %outer2
i32 2, label %outer3
i32 3, label %outer4
]

case2:
br label %switch_bb

outer1:
ret void

outer2:
ret void

outer3:
ret void

outer4:
ret void

default_dest:
ret void
}

define void @less_outer_uses_by_switch(i32 %cond, i1 %c1) {
; CHECK-LABEL: define void @less_outer_uses_by_switch(
; CHECK-SAME: i32 [[COND:%.*]], i1 [[C1:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*]]:
; CHECK-NEXT: br label %[[SWITCH_BB:.*]]
; CHECK: [[SWITCH_BB]]:
; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ poison, %[[CASE1_SUCC:.*]] ]
; CHECK-NEXT: switch i32 [[PHI]], label %[[DEFAULT_DEST:.*]] [
; CHECK-NEXT: i32 0, label %[[CASE1:.*]]
; CHECK-NEXT: i32 1, label %[[CASE2:.*]]
; CHECK-NEXT: ]
; CHECK: [[SWITCH_BB_JT2:.*]]:
; CHECK-NEXT: [[PHI_JT2:%.*]] = phi i32 [ 2, %[[CASE2]] ]
; CHECK-NEXT: br label %[[DEFAULT_DEST]]
; CHECK: [[SWITCH_BB_JT1:.*]]:
; CHECK-NEXT: [[PHI_JT1:%.*]] = phi i32 [ [[ONE_JT1:%.*]], %[[CASE1_SUCC_JT1:.*]] ]
; CHECK-NEXT: br label %[[CASE2]]
; CHECK: [[CASE1]]:
; CHECK-NEXT: br label %[[CASE1_SUCC_JT1]]
; CHECK: [[CASE1_SUCC]]:
; CHECK-NEXT: switch i32 [[COND]], label %[[SWITCH_BB]] [
; CHECK-NEXT: i32 0, label %[[OUTER1:.*]]
; CHECK-NEXT: i32 1, label %[[OUTER2:.*]]
; CHECK-NEXT: ]
; CHECK: [[CASE1_SUCC_JT1]]:
; CHECK-NEXT: [[ONE_JT1]] = phi i32 [ 1, %[[CASE1]] ]
; CHECK-NEXT: switch i32 [[COND]], label %[[SWITCH_BB_JT1]] [
; CHECK-NEXT: i32 0, label %[[OUTER1]]
; CHECK-NEXT: i32 1, label %[[OUTER2]]
; CHECK-NEXT: ]
; CHECK: [[CASE2]]:
; CHECK-NEXT: br label %[[SWITCH_BB_JT2]]
; CHECK: [[OUTER1]]:
; CHECK-NEXT: ret void
; CHECK: [[OUTER2]]:
; CHECK-NEXT: ret void
; CHECK: [[DEFAULT_DEST]]:
; CHECK-NEXT: ret void
;
entry:
br label %switch_bb

switch_bb:
%phi = phi i32 [ 0, %entry ], [ %one, %case1_succ ], [ 2, %case2 ]
switch i32 %phi, label %default_dest [
i32 0, label %case1
i32 1, label %case2
]

case1:
br label %case1_succ

case1_succ:
%one = phi i32 [ 1, %case1 ]
switch i32 %cond, label %switch_bb [
i32 0, label %outer1
i32 1, label %outer2
]

case2:
br label %switch_bb

outer1:
ret void

outer2:
ret void

default_dest:
ret void
}


define void @max_outer_uses_multi_preds(i32 %cond) {
; CHECK-LABEL: define void @max_outer_uses_multi_preds(
; CHECK-SAME: i32 [[COND:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*]]:
; CHECK-NEXT: br label %[[SWITCH_BB:.*]]
; CHECK: [[SWITCH_BB]]:
; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ poison, %[[CASE1_SUCC:.*]] ]
; CHECK-NEXT: switch i32 [[PHI]], label %[[DEFAULT_DEST:.*]] [
; CHECK-NEXT: i32 0, label %[[CASE1:.*]]
; CHECK-NEXT: i32 1, label %[[CASE2:.*]]
; CHECK-NEXT: i32 2, label %[[CASE3:.*]]
; CHECK-NEXT: ]
; CHECK: [[SWITCH_BB_JT2:.*]]:
; CHECK-NEXT: [[PHI_JT2:%.*]] = phi i32 [ 2, %[[CASE2]] ]
; CHECK-NEXT: br label %[[CASE3]]
; CHECK: [[SWITCH_BB_JT1:.*]]:
; CHECK-NEXT: [[PHI_JT1:%.*]] = phi i32 [ [[ONE_JT1:%.*]], %[[CASE1_SUCC_JT1:.*]] ]
; CHECK-NEXT: br label %[[CASE2]]
; CHECK: [[CASE1]]:
; CHECK-NEXT: br label %[[CASE1_SUCC_JT1]]
; CHECK: [[CASE1_SUCC]]:
; CHECK-NEXT: switch i32 [[COND]], label %[[SWITCH_BB]] [
; CHECK-NEXT: i32 0, label %[[OUTER1:.*]]
; CHECK-NEXT: i32 1, label %[[OUTER2:.*]]
; CHECK-NEXT: i32 2, label %[[OUTER3:.*]]
; CHECK-NEXT: i32 3, label %[[OUTER4:.*]]
; CHECK-NEXT: ]
; CHECK: [[CASE1_SUCC_JT1]]:
; CHECK-NEXT: [[ONE_JT1]] = phi i32 [ 1, %[[CASE1]] ]
; CHECK-NEXT: switch i32 [[COND]], label %[[SWITCH_BB_JT1]] [
; CHECK-NEXT: i32 0, label %[[OUTER1]]
; CHECK-NEXT: i32 1, label %[[OUTER2]]
; CHECK-NEXT: i32 2, label %[[OUTER3]]
; CHECK-NEXT: i32 3, label %[[OUTER4]]
; CHECK-NEXT: ]
; CHECK: [[CASE2]]:
; CHECK-NEXT: br label %[[SWITCH_BB_JT2]]
; CHECK: [[CASE3]]:
; CHECK-NEXT: switch i32 [[COND]], label %[[OUTER4]] [
; CHECK-NEXT: i32 0, label %[[OUTER1]]
; CHECK-NEXT: i32 1, label %[[OUTER2]]
; CHECK-NEXT: i32 2, label %[[OUTER3]]
; CHECK-NEXT: ]
; CHECK: [[OUTER1]]:
; CHECK-NEXT: ret void
; CHECK: [[OUTER2]]:
; CHECK-NEXT: ret void
; CHECK: [[OUTER3]]:
; CHECK-NEXT: ret void
; CHECK: [[OUTER4]]:
; CHECK-NEXT: ret void
; CHECK: [[DEFAULT_DEST]]:
; CHECK-NEXT: ret void
;
entry:
br label %switch_bb

switch_bb:
%phi = phi i32 [ 0, %entry ], [ %one, %case1_succ ], [ 2, %case2 ]
switch i32 %phi, label %default_dest [
i32 0, label %case1
i32 1, label %case2
i32 2, label %case3
]

case1:
br label %case1_succ

case1_succ:
%one = phi i32 [ 1, %case1 ]
switch i32 %cond, label %switch_bb [
i32 0, label %outer1
i32 1, label %outer2
i32 2, label %outer3
i32 3, label %outer4
]

case2:
br label %switch_bb

case3:
switch i32 %cond, label %outer4 [
i32 0, label %outer1
i32 1, label %outer2
i32 2, label %outer3
]

outer1:
ret void

outer2:
ret void

outer3:
ret void

outer4:
ret void

default_dest:
ret void
}