Skip to content

Commit ede27d8

Browse files
authored
[SimplifyCFG] Add support for sinking instructions with multiple uses (#95521)
Sinking currently only supports instructions that have zero or one uses. Extend this to handle instructions with any number of uses, as long as all uses are consistent (i.e. the "same" for all sinking candidates). After #94462 this is basically just a matter of looping over all uses instead of checking the first one only.
1 parent 1d4e857 commit ede27d8

File tree

2 files changed

+27
-44
lines changed

2 files changed

+27
-44
lines changed

llvm/lib/Transforms/Utils/SimplifyCFG.cpp

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1934,9 +1934,8 @@ static bool canSinkInstructions(
19341934
ArrayRef<Instruction *> Insts,
19351935
DenseMap<const Use *, SmallVector<Value *, 4>> &PHIOperands) {
19361936
// Prune out obviously bad instructions to move. Each instruction must have
1937-
// exactly zero or one use, and we check later that use is by a single, common
1938-
// PHI instruction in the successor.
1939-
bool HasUse = !Insts.front()->user_empty();
1937+
// the same number of uses, and we check later that the uses are consistent.
1938+
std::optional<unsigned> NumUses;
19401939
for (auto *I : Insts) {
19411940
// These instructions may change or break semantics if moved.
19421941
if (isa<PHINode>(I) || I->isEHPad() || isa<AllocaInst>(I) ||
@@ -1956,10 +1955,9 @@ static bool canSinkInstructions(
19561955
if (C->isInlineAsm() || C->cannotMerge() || C->isConvergent())
19571956
return false;
19581957

1959-
// Each instruction must have zero or one use.
1960-
if (HasUse && !I->hasOneUse())
1961-
return false;
1962-
if (!HasUse && !I->user_empty())
1958+
if (!NumUses)
1959+
NumUses = I->getNumUses();
1960+
else if (NumUses != I->getNumUses())
19631961
return false;
19641962
}
19651963

@@ -1987,8 +1985,7 @@ static bool canSinkInstructions(
19871985
// then the other phi operands must match the instructions from Insts. This
19881986
// also has to hold true for any phi nodes that would be created as a result
19891987
// of sinking. Both of these cases are represented by PhiOperands.
1990-
if (HasUse) {
1991-
const Use &U = *I0->use_begin();
1988+
for (const Use &U : I0->uses()) {
19921989
auto It = PHIOperands.find(&U);
19931990
if (It == PHIOperands.end())
19941991
// There may be uses in other blocks when sinking into a loop header.
@@ -2138,11 +2135,11 @@ static void sinkLastInstruction(ArrayRef<BasicBlock*> Blocks) {
21382135
I0->andIRFlags(I);
21392136
}
21402137

2141-
if (!I0->user_empty()) {
2142-
// canSinkLastInstruction checked that all instructions were used by
2143-
// one and only one PHI node. Find that now, RAUW it to our common
2144-
// instruction and nuke it.
2145-
auto *PN = cast<PHINode>(*I0->user_begin());
2138+
for (User *U : make_early_inc_range(I0->users())) {
2139+
// canSinkLastInstruction checked that all instructions are only used by
2140+
// phi nodes in a way that allows replacing the phi node with the common
2141+
// instruction.
2142+
auto *PN = cast<PHINode>(U);
21462143
PN->replaceAllUsesWith(I0);
21472144
PN->eraseFromParent();
21482145
}

llvm/test/Transforms/SimplifyCFG/X86/sink-common-code.ll

Lines changed: 16 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1704,19 +1704,14 @@ return:
17041704

17051705
define ptr @multi_use_in_phi(i1 %cond, ptr %p, i64 %a, i64 %b) {
17061706
; CHECK-LABEL: @multi_use_in_phi(
1707-
; CHECK-NEXT: br i1 [[COND:%.*]], label [[IF:%.*]], label [[ELSE:%.*]]
1707+
; CHECK-NEXT: br i1 [[COND:%.*]], label [[IF:%.*]], label [[JOIN:%.*]]
17081708
; CHECK: if:
17091709
; CHECK-NEXT: call void @dummy()
1710-
; CHECK-NEXT: [[GEP1_A:%.*]] = getelementptr i8, ptr [[P:%.*]], i64 [[A:%.*]]
1711-
; CHECK-NEXT: br label [[JOIN:%.*]]
1712-
; CHECK: else:
1713-
; CHECK-NEXT: [[GEP1_B:%.*]] = getelementptr i8, ptr [[P]], i64 [[A]]
17141710
; CHECK-NEXT: br label [[JOIN]]
17151711
; CHECK: join:
1716-
; CHECK-NEXT: [[GEP1_B_SINK:%.*]] = phi ptr [ [[GEP1_B]], [[ELSE]] ], [ [[GEP1_A]], [[IF]] ]
1717-
; CHECK-NEXT: [[PHI1:%.*]] = phi ptr [ [[GEP1_A]], [[IF]] ], [ [[GEP1_B]], [[ELSE]] ]
1718-
; CHECK-NEXT: [[GEP2_B:%.*]] = getelementptr i8, ptr [[GEP1_B_SINK]], i64 [[B:%.*]]
1719-
; CHECK-NEXT: call void @use.ptr(ptr [[PHI1]])
1712+
; CHECK-NEXT: [[GEP1_B:%.*]] = getelementptr i8, ptr [[P:%.*]], i64 [[A:%.*]]
1713+
; CHECK-NEXT: [[GEP2_B:%.*]] = getelementptr i8, ptr [[GEP1_B]], i64 [[B:%.*]]
1714+
; CHECK-NEXT: call void @use.ptr(ptr [[GEP1_B]])
17201715
; CHECK-NEXT: ret ptr [[GEP2_B]]
17211716
;
17221717
br i1 %cond, label %if, label %else
@@ -1778,23 +1773,16 @@ join:
17781773

17791774
define i64 @multi_use_in_block(i1 %cond, ptr %p, i64 %a, i64 %b) {
17801775
; CHECK-LABEL: @multi_use_in_block(
1781-
; CHECK-NEXT: br i1 [[COND:%.*]], label [[IF:%.*]], label [[ELSE:%.*]]
1776+
; CHECK-NEXT: br i1 [[COND:%.*]], label [[IF:%.*]], label [[JOIN:%.*]]
17821777
; CHECK: if:
17831778
; CHECK-NEXT: call void @dummy()
1784-
; CHECK-NEXT: [[GEP1_A:%.*]] = getelementptr i8, ptr [[P:%.*]], i64 [[A:%.*]]
1785-
; CHECK-NEXT: [[V_A:%.*]] = load i64, ptr [[GEP1_A]], align 8
1786-
; CHECK-NEXT: [[GEP2_A:%.*]] = getelementptr i8, ptr [[GEP1_A]], i64 [[V_A]]
1787-
; CHECK-NEXT: br label [[JOIN:%.*]]
1788-
; CHECK: else:
1789-
; CHECK-NEXT: [[GEP1_B:%.*]] = getelementptr i8, ptr [[P]], i64 [[A]]
1790-
; CHECK-NEXT: [[V_B:%.*]] = load i64, ptr [[GEP1_B]], align 8
1791-
; CHECK-NEXT: [[GEP2_B:%.*]] = getelementptr i8, ptr [[GEP1_B]], i64 [[V_B]]
17921779
; CHECK-NEXT: br label [[JOIN]]
17931780
; CHECK: join:
1794-
; CHECK-NEXT: [[PHI1:%.*]] = phi i64 [ [[V_A]], [[IF]] ], [ [[V_B]], [[ELSE]] ]
1795-
; CHECK-NEXT: [[PHI2:%.*]] = phi ptr [ [[GEP2_A]], [[IF]] ], [ [[GEP2_B]], [[ELSE]] ]
1796-
; CHECK-NEXT: call void @use.ptr(ptr [[PHI2]])
1797-
; CHECK-NEXT: ret i64 [[PHI1]]
1781+
; CHECK-NEXT: [[GEP1_B:%.*]] = getelementptr i8, ptr [[P:%.*]], i64 [[A:%.*]]
1782+
; CHECK-NEXT: [[V_B:%.*]] = load i64, ptr [[GEP1_B]], align 8
1783+
; CHECK-NEXT: [[GEP2_B:%.*]] = getelementptr i8, ptr [[GEP1_B]], i64 [[V_B]]
1784+
; CHECK-NEXT: call void @use.ptr(ptr [[GEP2_B]])
1785+
; CHECK-NEXT: ret i64 [[V_B]]
17981786
;
17991787
br i1 %cond, label %if, label %else
18001788

@@ -1824,19 +1812,17 @@ define i64 @multi_use_in_block_inconsistent(i1 %cond, ptr %p, i64 %a, i64 %b) {
18241812
; CHECK: if:
18251813
; CHECK-NEXT: call void @dummy()
18261814
; CHECK-NEXT: [[GEP1_A:%.*]] = getelementptr i8, ptr [[P:%.*]], i64 [[A:%.*]]
1827-
; CHECK-NEXT: [[V_A:%.*]] = load i64, ptr [[GEP1_A]], align 8
1828-
; CHECK-NEXT: [[GEP2_A:%.*]] = getelementptr i8, ptr [[GEP1_A]], i64 [[V_A]]
18291815
; CHECK-NEXT: br label [[JOIN:%.*]]
18301816
; CHECK: else:
18311817
; CHECK-NEXT: [[GEP1_B:%.*]] = getelementptr i8, ptr [[P]], i64 [[A]]
1832-
; CHECK-NEXT: [[V_B:%.*]] = load i64, ptr [[P]], align 8
1833-
; CHECK-NEXT: [[GEP2_B:%.*]] = getelementptr i8, ptr [[GEP1_B]], i64 [[V_B]]
18341818
; CHECK-NEXT: br label [[JOIN]]
18351819
; CHECK: join:
1836-
; CHECK-NEXT: [[PHI1:%.*]] = phi i64 [ [[V_A]], [[IF]] ], [ [[V_B]], [[ELSE]] ]
1837-
; CHECK-NEXT: [[PHI2:%.*]] = phi ptr [ [[GEP2_A]], [[IF]] ], [ [[GEP2_B]], [[ELSE]] ]
1838-
; CHECK-NEXT: call void @use.ptr(ptr [[PHI2]])
1839-
; CHECK-NEXT: ret i64 [[PHI1]]
1820+
; CHECK-NEXT: [[P_SINK:%.*]] = phi ptr [ [[P]], [[ELSE]] ], [ [[GEP1_A]], [[IF]] ]
1821+
; CHECK-NEXT: [[GEP1_B_SINK:%.*]] = phi ptr [ [[GEP1_B]], [[ELSE]] ], [ [[GEP1_A]], [[IF]] ]
1822+
; CHECK-NEXT: [[V_B:%.*]] = load i64, ptr [[P_SINK]], align 8
1823+
; CHECK-NEXT: [[GEP2_B:%.*]] = getelementptr i8, ptr [[GEP1_B_SINK]], i64 [[V_B]]
1824+
; CHECK-NEXT: call void @use.ptr(ptr [[GEP2_B]])
1825+
; CHECK-NEXT: ret i64 [[V_B]]
18401826
;
18411827
br i1 %cond, label %if, label %else
18421828

0 commit comments

Comments
 (0)