-
Notifications
You must be signed in to change notification settings - Fork 15.2k
[SimplifyCFG] Simplify switch instruction that has duplicate arms #114262
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
Changes from 28 commits
fbfa46a
2db9f00
4e56067
9e10655
64ddee6
c8eecb6
9e9afc2
ef1bc74
794d4e8
32b627f
4190352
f04d67f
4ca2777
28620df
9dcf124
41edd15
5c247db
c3a1361
b563080
67b3c80
94ff86e
c6a5e52
241fef5
aa4cf43
8153c4b
d1cd111
4949890
e91253c
d87ea1d
be55920
085b048
3f0b020
3430a66
852f3f0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -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); | ||||||||||||||||
|
|
@@ -7436,6 +7437,176 @@ static bool simplifySwitchOfCmpIntrinsic(SwitchInst *SI, IRBuilderBase &Builder, | |||||||||||||||
| return true; | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| /// Checking whether two CaseHandle.getCaseSuccessor() are equal depends on | ||||||||||||||||
| /// the BasicBlock and the incoming values of their successor PHINodes. | ||||||||||||||||
| /// PHINode::getIncomingValueForBlock is O(|Preds|), so we'd like to avoid | ||||||||||||||||
| /// calling this function on each BasicBlock every time isEqual is called, | ||||||||||||||||
| /// especially since the same BasicBlock may be passed as an argument multiple | ||||||||||||||||
| /// times. To do this, we can precompute a map of PHINode -> Pred BasicBlock -> | ||||||||||||||||
| /// IncomingValue and add it in the Wrapper so isEqual can do O(1) checking | ||||||||||||||||
| /// of the incoming values. | ||||||||||||||||
| struct CaseHandleWrapper { | ||||||||||||||||
| const SwitchInst::CaseHandle Case; | ||||||||||||||||
| DenseMap<PHINode *, DenseMap<BasicBlock *, Value *>> *PhiPredIVs; | ||||||||||||||||
|
||||||||||||||||
| }; | ||||||||||||||||
|
|
||||||||||||||||
| namespace llvm { | ||||||||||||||||
| template <> struct DenseMapInfo<const CaseHandleWrapper *> { | ||||||||||||||||
| static const CaseHandleWrapper *getEmptyKey() { | ||||||||||||||||
| return static_cast<CaseHandleWrapper *>( | ||||||||||||||||
| DenseMapInfo<void *>::getEmptyKey()); | ||||||||||||||||
| } | ||||||||||||||||
| static const CaseHandleWrapper *getTombstoneKey() { | ||||||||||||||||
| return static_cast<CaseHandleWrapper *>( | ||||||||||||||||
| DenseMapInfo<void *>::getTombstoneKey()); | ||||||||||||||||
| } | ||||||||||||||||
| static unsigned getHashValue(const CaseHandleWrapper *CHW) { | ||||||||||||||||
| BasicBlock *Succ = CHW->Case.getCaseSuccessor(); | ||||||||||||||||
| BranchInst *BI = cast<BranchInst>(Succ->getTerminator()); | ||||||||||||||||
| assert(BI->isUnconditional() && | ||||||||||||||||
| "Only supporting unconditional branches for now"); | ||||||||||||||||
| assert(BI->getNumSuccessors() == 1 && | ||||||||||||||||
| "Expected unconditional branches to have one successor"); | ||||||||||||||||
| assert(Succ->size() == 1 && "Expected just a single branch in the BB"); | ||||||||||||||||
|
|
||||||||||||||||
| // Since we assume the BB is just a single BranchInst with a single | ||||||||||||||||
| // succsessor, we hash as the BB and the incoming Values of its successor | ||||||||||||||||
michaelmaitland marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||||||||||
| // PHIs. Initially, we tried to just use the successor BB as the hash, but | ||||||||||||||||
| // this had poor performance. We find that the extra computation of getting | ||||||||||||||||
| // the incoming PHI values here leads to better performance on overall Set | ||||||||||||||||
| // performance. We also tried to build a map from BB -> Succs.IncomingValues | ||||||||||||||||
|
||||||||||||||||
| // PHIs. Initially, we tried to just use the successor BB as the hash, but | |
| // this had poor performance. We find that the extra computation of getting | |
| // the incoming PHI values here leads to better performance on overall Set | |
| // performance. We also tried to build a map from BB -> Succs.IncomingValues | |
| // PHIs. Initially, we tried to just use the successor BB as the hash, but | |
| // including the incoming PHI values leads to better performance. | |
| // We also tried to build a map from BB -> Succs.IncomingValues |
There was a lot of performance in here.
michaelmaitland marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
michaelmaitland marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
michaelmaitland marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
michaelmaitland marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The dominator tree should be updated as well.
| 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 | ||
michaelmaitland marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| ; RUN: opt < %s -O3 -S | FileCheck %s -check-prefix=O3 | ||
michaelmaitland marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| define i32 @switch_all_duplicate_arms(i32 %0, i32 %1, i32 %2, i32 %3) { | ||
michaelmaitland marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| ; 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 | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I prefer to store
BasicBlock *Succhere. Then we can handle the default dest BB as well.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure that will work for us, because there is no O(1) way to go from BasicBlock * -> CaseHandles in constant time. We store the CaseHandle so we can do the replacement in CaseHandle quickly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see. We can use
SwitchInst::getSuccessor/SwitchInst::setSuccessorhere. The default dest BB is the first successor.