Skip to content
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

[SimplifyCFG] Simplify switch instruction that has duplicate arms #114262

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
108 changes: 108 additions & 0 deletions llvm/lib/Transforms/Utils/SimplifyCFG.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,7 @@ class SimplifyCFGOpt {
bool simplifyCleanupReturn(CleanupReturnInst *RI);
bool simplifyUnreachable(UnreachableInst *UI);
bool simplifySwitch(SwitchInst *SI, IRBuilder<> &Builder);
bool simplifyDuplicateSwitchArms(SwitchInst *SI);
bool simplifyIndirectBr(IndirectBrInst *IBI);
bool simplifyBranch(BranchInst *Branch, IRBuilder<> &Builder);
bool simplifyUncondBranch(BranchInst *BI, IRBuilder<> &Builder);
Expand Down Expand Up @@ -7436,6 +7437,110 @@ static bool simplifySwitchOfCmpIntrinsic(SwitchInst *SI, IRBuilderBase &Builder,
return true;
}

bool SimplifyCFGOpt::simplifyDuplicateSwitchArms(SwitchInst *SI) {
// Simplify the case where multiple arms contain only a terminator, the
// terminators are the same, and their sucessor PHIS incoming values are the
// same.

// Find BBs that are candidates for simplification.
SmallPtrSet<BasicBlock *, 8> BBs;
for (auto &Case : SI->cases()) {
BasicBlock *BB = Case.getCaseSuccessor();

// FIXME: This case needs some extra care because the terminators other than
// SI need to be updated.
if (!BB->hasNPredecessors(1))
continue;

// FIXME: Relax that the terminator is a BranchInst by checking for equality
// on other kinds of terminators.
Instruction *T = BB->getTerminator();
if (T && isa<BranchInst>(T))
BBs.insert(BB);
}

auto IsBranchEq = [](BranchInst *A, BranchInst *B) {
if (A->isConditional() != B->isConditional())
return false;

if (A->isConditional()) {
// If the conditions are instructions, check equality up to commutativity.
// Otherwise, check that the two Values are the same.
Value *AC = A->getCondition();
Value *BC = B->getCondition();
auto *ACI = dyn_cast<Instruction>(AC);
auto *BCI = dyn_cast<Instruction>(BC);
if ((ACI && BCI && !areIdenticalUpToCommutativity(ACI, BCI)) && AC != BC)
return false;
}

if (A->getNumSuccessors() != B->getNumSuccessors())
return false;

for (unsigned I = 0; I < A->getNumSuccessors(); ++I) {
BasicBlock *ASucc = A->getSuccessor(I);
if (ASucc != B->getSuccessor(I))
return false;
// Need to check that PHIs in sucessors have matching values
for (PHINode &Phi : ASucc->phis())
if (Phi.getIncomingValueForBlock(A->getParent()) !=
Phi.getIncomingValueForBlock(B->getParent()))
michaelmaitland marked this conversation as resolved.
Show resolved Hide resolved
michaelmaitland marked this conversation as resolved.
Show resolved Hide resolved
return false;
}

return true;
};

auto IsBBEqualTo = [&IsBranchEq](BasicBlock *A, BasicBlock *B) {
// FIXME: Support more than just a single BranchInst. One way we could do
// this is by taking a hashing approach.
if (A->size() != 1 || B->size() != 1)
return false;

return IsBranchEq(cast<BranchInst>(A->getTerminator()),
cast<BranchInst>(B->getTerminator()));
};

// Construct a map from candidate basic block to an equivalent basic block
// to replace it with. All equivalent basic blocks should be replaced with
// the same basic block. To do this, if there is no equivalent BB in the map,
// then insert into the map BB -> BB. Otherwise, we should check only elements
// in the map for equivalence to ensure that all equivalent BB get replaced
// by the BB in the map. Replacing BB with BB has no impact, so we skip
// a call to setSuccessor when we do the actual replacement.
DenseMap<BasicBlock *, BasicBlock *> ReplaceWith;
for (BasicBlock *BB : BBs) {
bool Inserted = false;
for (auto KV : ReplaceWith) {
if (IsBBEqualTo(BB, KV.first)) {
ReplaceWith[BB] = KV.first;
Inserted = true;
break;
}
}
if (!Inserted)
ReplaceWith[BB] = BB;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I think the way to implement this is with DenseMap<BasicBlock *, SmallVector<BasicBlock *>>. If a given BB is not in ReplaceWith, you add it w/ empty list. If it is, add to list of existing entry. You then iterate over all key BBs in ReplaceWith and replace all items in the SmallVector with the key BB.

In this case your hash function can just be zero (or maybe BB->size()) and isBBEqualTo is your equals function.


// Do the replacement in SI.
bool MadeChange = false;
// There is no fast lookup of BasicBlock -> Cases, so we iterate over cases
// and check that the case was a candidate. BBs is already filtered, so
// hopefully calling contains on it is not too expensive.
for (auto &Case : SI->cases()) {
BasicBlock *OldSucc = Case.getCaseSuccessor();
if (!BBs.contains(OldSucc))
continue;
BasicBlock *NewSucc = ReplaceWith[OldSucc];
if (OldSucc != NewSucc) {
Case.setSuccessor(NewSucc);
MadeChange = true;
}
}

return MadeChange;
}

bool SimplifyCFGOpt::simplifySwitch(SwitchInst *SI, IRBuilder<> &Builder) {
BasicBlock *BB = SI->getParent();

Expand Down Expand Up @@ -7496,6 +7601,9 @@ bool SimplifyCFGOpt::simplifySwitch(SwitchInst *SI, IRBuilder<> &Builder) {
hoistCommonCodeFromSuccessors(SI, !Options.HoistCommonInsts))
return requestResimplify();

if (simplifyDuplicateSwitchArms(SI))
return requestResimplify();

return false;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,16 +139,14 @@ define i32 @PR34471(i32 %x) {
; NO_FWD-NEXT: switch i32 [[X:%.*]], label [[ELSE3:%.*]] [
; NO_FWD-NEXT: i32 17, label [[RETURN:%.*]]
; NO_FWD-NEXT: i32 19, label [[IF19:%.*]]
; NO_FWD-NEXT: i32 42, label [[IF42:%.*]]
; NO_FWD-NEXT: i32 42, label [[IF19]]
; NO_FWD-NEXT: ]
; NO_FWD: if19:
; NO_FWD-NEXT: br label [[RETURN]]
; NO_FWD: if42:
; NO_FWD-NEXT: br label [[RETURN]]
; NO_FWD: else3:
; NO_FWD-NEXT: br label [[RETURN]]
; NO_FWD: return:
; NO_FWD-NEXT: [[R:%.*]] = phi i32 [ [[X]], [[IF19]] ], [ [[X]], [[IF42]] ], [ 0, [[ELSE3]] ], [ 17, [[ENTRY:%.*]] ]
; NO_FWD-NEXT: [[R:%.*]] = phi i32 [ [[X]], [[IF19]] ], [ 0, [[ELSE3]] ], [ 17, [[ENTRY:%.*]] ]
; NO_FWD-NEXT: ret i32 [[R]]
;
; FWD-LABEL: @PR34471(
Expand Down
6 changes: 2 additions & 4 deletions llvm/test/Transforms/SimplifyCFG/HoistCode.ll
Original file line number Diff line number Diff line change
Expand Up @@ -65,14 +65,12 @@ define float @PR39535min_switch(i64 %i, float %x) {
; CHECK-NEXT: entry:
; CHECK-NEXT: switch i64 [[I:%.*]], label [[END:%.*]] [
; CHECK-NEXT: i64 1, label [[BB1:%.*]]
; CHECK-NEXT: i64 2, label [[BB2:%.*]]
; CHECK-NEXT: i64 2, label [[BB1]]
; CHECK-NEXT: ]
; CHECK: bb1:
; CHECK-NEXT: br label [[END]]
; CHECK: bb2:
; CHECK-NEXT: br label [[END]]
; CHECK: end:
; CHECK-NEXT: [[COND:%.*]] = phi fast float [ [[X:%.*]], [[BB1]] ], [ [[X]], [[BB2]] ], [ 0.000000e+00, [[ENTRY:%.*]] ]
; CHECK-NEXT: [[COND:%.*]] = phi fast float [ [[X:%.*]], [[BB1]] ], [ 0.000000e+00, [[ENTRY:%.*]] ]
; CHECK-NEXT: ret float [[COND]]
;
entry:
Expand Down
140 changes: 140 additions & 0 deletions llvm/test/Transforms/SimplifyCFG/switch-dup-bbs.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
; RUN: opt < %s -passes=simplifycfg -S | FileCheck %s -check-prefix=SIMPLIFY-CFG
; RUN: opt < %s -O3 -S | FileCheck %s -check-prefix=O3

define i32 @switch_all_duplicate_arms(i32 %0, i32 %1, i32 %2, i32 %3) {
; SIMPLIFY-CFG-LABEL: define i32 @switch_all_duplicate_arms(
; SIMPLIFY-CFG-SAME: i32 [[TMP0:%.*]], i32 [[TMP1:%.*]], i32 [[TMP2:%.*]], i32 [[TMP3:%.*]]) {
; SIMPLIFY-CFG-NEXT: switch i32 [[TMP1]], label %[[BB6:.*]] [
; SIMPLIFY-CFG-NEXT: i32 0, label %[[BB5:.*]]
; SIMPLIFY-CFG-NEXT: i32 1, label %[[BB5]]
; SIMPLIFY-CFG-NEXT: ]
; SIMPLIFY-CFG: [[BB5]]:
; SIMPLIFY-CFG-NEXT: br label %[[BB6]]
; SIMPLIFY-CFG: [[BB6]]:
; SIMPLIFY-CFG-NEXT: [[TMP8:%.*]] = phi i32 [ [[TMP3]], [[TMP4:%.*]] ], [ [[TMP2]], %[[BB5]] ]
; SIMPLIFY-CFG-NEXT: ret i32 [[TMP8]]
;
; O3-LABEL: define i32 @switch_all_duplicate_arms(
; O3-SAME: i32 [[TMP0:%.*]], i32 [[TMP1:%.*]], i32 [[TMP2:%.*]], i32 [[TMP3:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] {
; O3-NEXT: [[SWITCH:%.*]] = icmp ult i32 [[TMP1]], 2
; O3-NEXT: [[TMP8:%.*]] = select i1 [[SWITCH]], i32 [[TMP2]], i32 [[TMP3]]
; O3-NEXT: ret i32 [[TMP8]]
;
switch i32 %1, label %7 [
i32 0, label %5
i32 1, label %6
]

5:
br label %7

6:
br label %7

7:
%8 = phi i32 [ %3, %4 ], [ %2, %6 ], [ %2, %5 ]
ret i32 %8
}

define i32 @switch_some_duplicate_arms(i32 %0, i32 %1, i32 %2, i32 %3, i32 %4) {
; SIMPLIFY-CFG-LABEL: define i32 @switch_some_duplicate_arms(
; SIMPLIFY-CFG-SAME: i32 [[TMP0:%.*]], i32 [[TMP1:%.*]], i32 [[TMP2:%.*]], i32 [[TMP3:%.*]], i32 [[TMP4:%.*]]) {
; SIMPLIFY-CFG-NEXT: switch i32 [[TMP1]], label %[[BB8:.*]] [
; SIMPLIFY-CFG-NEXT: i32 0, label %[[BB6:.*]]
; SIMPLIFY-CFG-NEXT: i32 1, label %[[BB6]]
; SIMPLIFY-CFG-NEXT: i32 2, label %[[BB7:.*]]
; SIMPLIFY-CFG-NEXT: ]
; SIMPLIFY-CFG: [[BB6]]:
; SIMPLIFY-CFG-NEXT: br label %[[BB8]]
; SIMPLIFY-CFG: [[BB7]]:
; SIMPLIFY-CFG-NEXT: br label %[[BB8]]
; SIMPLIFY-CFG: [[BB8]]:
; SIMPLIFY-CFG-NEXT: [[TMP10:%.*]] = phi i32 [ [[TMP3]], [[TMP5:%.*]] ], [ [[TMP4]], %[[BB7]] ], [ [[TMP2]], %[[BB6]] ]
; SIMPLIFY-CFG-NEXT: ret i32 [[TMP10]]
;
; O3-LABEL: define i32 @switch_some_duplicate_arms(
; O3-SAME: i32 [[TMP0:%.*]], i32 [[TMP1:%.*]], i32 [[TMP2:%.*]], i32 [[TMP3:%.*]], i32 [[TMP4:%.*]]) local_unnamed_addr #[[ATTR0]] {
; O3-NEXT: switch i32 [[TMP1]], label %[[BB8:.*]] [
; O3-NEXT: i32 0, label %[[BB6:.*]]
; O3-NEXT: i32 1, label %[[BB6]]
; O3-NEXT: i32 2, label %[[BB7:.*]]
; O3-NEXT: ]
; O3: [[BB6]]:
; O3-NEXT: br label %[[BB8]]
; O3: [[BB7]]:
; O3-NEXT: br label %[[BB8]]
; O3: [[BB8]]:
; O3-NEXT: [[TMP10:%.*]] = phi i32 [ [[TMP3]], [[TMP5:%.*]] ], [ [[TMP4]], %[[BB7]] ], [ [[TMP2]], %[[BB6]] ]
; O3-NEXT: ret i32 [[TMP10]]
;
switch i32 %1, label %9 [
i32 0, label %6
i32 1, label %7
i32 2, label %8
]

6:
br label %9

7:
br label %9

8:
br label %9

9:
%10 = phi i32 [ %3, %5 ], [ %4, %8 ], [ %2, %7 ], [ %2, %6 ]
ret i32 %10
}

define i32 @switch_duplicate_arms_multipred(i1 %0, i32 %1, i32 %2, i32 %3, i32 %4) {
; SIMPLIFY-CFG-LABEL: define i32 @switch_duplicate_arms_multipred(
; SIMPLIFY-CFG-SAME: i1 [[TMP0:%.*]], i32 [[TMP1:%.*]], i32 [[TMP2:%.*]], i32 [[TMP3:%.*]], i32 [[TMP4:%.*]]) {
; SIMPLIFY-CFG-NEXT: br i1 [[TMP0]], label %[[BB6:.*]], label %[[BB7:.*]]
; SIMPLIFY-CFG: [[BB6]]:
; SIMPLIFY-CFG-NEXT: switch i32 [[TMP2]], label %[[BB9:.*]] [
; SIMPLIFY-CFG-NEXT: i32 0, label %[[BB7]]
; SIMPLIFY-CFG-NEXT: i32 1, label %[[BB8:.*]]
; SIMPLIFY-CFG-NEXT: ]
; SIMPLIFY-CFG: [[BB7]]:
; SIMPLIFY-CFG-NEXT: br label %[[BB9]]
; SIMPLIFY-CFG: [[BB8]]:
; SIMPLIFY-CFG-NEXT: br label %[[BB9]]
; SIMPLIFY-CFG: [[BB9]]:
; SIMPLIFY-CFG-NEXT: [[TMP10:%.*]] = phi i32 [ [[TMP4]], %[[BB6]] ], [ [[TMP3]], %[[BB8]] ], [ [[TMP3]], %[[BB7]] ]
; SIMPLIFY-CFG-NEXT: ret i32 [[TMP10]]
;
; O3-LABEL: define i32 @switch_duplicate_arms_multipred(
; O3-SAME: i1 [[TMP0:%.*]], i32 [[TMP1:%.*]], i32 [[TMP2:%.*]], i32 [[TMP3:%.*]], i32 [[TMP4:%.*]]) local_unnamed_addr #[[ATTR0]] {
; O3-NEXT: br i1 [[TMP0]], label %[[BB6:.*]], label %[[BB7:.*]]
; O3: [[BB6]]:
; O3-NEXT: switch i32 [[TMP2]], label %[[BB9:.*]] [
; O3-NEXT: i32 0, label %[[BB7]]
; O3-NEXT: i32 1, label %[[BB8:.*]]
; O3-NEXT: ]
; O3: [[BB7]]:
; O3-NEXT: br label %[[BB9]]
; O3: [[BB8]]:
; O3-NEXT: br label %[[BB9]]
; O3: [[BB9]]:
; O3-NEXT: [[TMP10:%.*]] = phi i32 [ [[TMP4]], %[[BB6]] ], [ [[TMP3]], %[[BB8]] ], [ [[TMP3]], %[[BB7]] ]
; O3-NEXT: ret i32 [[TMP10]]
;
br i1 %0, label %6, label %7
6:
switch i32 %2, label %9 [
i32 0, label %7
i32 1, label %8
]

7:
br label %9

8:
br label %9

9:
%10 = phi i32 [ %4, %6 ], [ %3, %8 ], [ %3, %7 ]
ret i32 %10
}
6 changes: 2 additions & 4 deletions llvm/test/Transforms/SimplifyCFG/switch-to-select-two-case.ll
Original file line number Diff line number Diff line change
Expand Up @@ -272,16 +272,14 @@ define i8 @switch_to_select_two_case_results_no_default(i32 %i) {
; CHECK-NEXT: i32 0, label [[END:%.*]]
; CHECK-NEXT: i32 2, label [[END]]
; CHECK-NEXT: i32 4, label [[CASE3:%.*]]
; CHECK-NEXT: i32 6, label [[CASE4:%.*]]
; CHECK-NEXT: i32 6, label [[CASE3]]
; CHECK-NEXT: ]
; CHECK: case3:
; CHECK-NEXT: br label [[END]]
; CHECK: case4:
; CHECK-NEXT: br label [[END]]
; CHECK: default:
; CHECK-NEXT: unreachable
; CHECK: end:
; CHECK-NEXT: [[T0:%.*]] = phi i8 [ 44, [[CASE3]] ], [ 44, [[CASE4]] ], [ 42, [[ENTRY:%.*]] ], [ 42, [[ENTRY]] ]
; CHECK-NEXT: [[T0:%.*]] = phi i8 [ 44, [[CASE3]] ], [ 42, [[ENTRY:%.*]] ], [ 42, [[ENTRY]] ]
; CHECK-NEXT: ret i8 [[T0]]
;
entry:
Expand Down
Loading