Skip to content

Commit a4c6eee

Browse files
committed
[GVN] Merge identical critical edge split blocks from the same predecessor
GVN's PerformLoadPRE function split the CriticalEdge to copy the load if it could not hoist the load on the CriticalEdge. This was not supported if more than one splitted CriticalEdge was needed, as it would unnecessarily duplicate the load. This patch fixes this issue by adding the function mergeSplitedCriticalEdges. It identifies a group of identical edge-split blocks that have branched from the same predecessor and merges them into a single block. Modify the branch target of the predecessors to this merged block and remove other blocks. With this patch, even if it has multiple CriticalEdges that cannot hoist a load, if they can be merged, it can suppose optimisation opportunity.
1 parent 0d19efa commit a4c6eee

File tree

3 files changed

+95
-13
lines changed

3 files changed

+95
-13
lines changed

llvm/include/llvm/Transforms/Scalar/GVN.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,9 @@ class GVNPass : public PassInfoMixin<GVNPass> {
382382
void verifyRemoved(const Instruction *I) const;
383383
bool splitCriticalEdges();
384384
BasicBlock *splitCriticalEdges(BasicBlock *Pred, BasicBlock *Succ);
385+
void
386+
mergeSplitedCriticalEdges(SmallVectorImpl<BasicBlock *> &SplitedCriticalEdges,
387+
MapVector<BasicBlock *, Value *> &PredLoad);
385388
bool replaceOperandsForInBlockEquality(Instruction *I) const;
386389
bool propagateEquality(Value *LHS, Value *RHS, const BasicBlockEdge &Root,
387390
bool DominatesByEdge);

llvm/lib/Transforms/Scalar/GVN.cpp

Lines changed: 77 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1580,6 +1580,56 @@ void GVNPass::eliminatePartiallyRedundantLoad(
15801580
});
15811581
}
15821582

1583+
/// Merges splited critical edge blocks that originate from the same
1584+
/// predecessor.
1585+
void GVNPass::mergeSplitedCriticalEdges(
1586+
SmallVectorImpl<BasicBlock *> &SplitedCriticalEdges,
1587+
MapVector<BasicBlock *, Value *> &PredLoad) {
1588+
if (SplitedCriticalEdges.size() <= 1)
1589+
return;
1590+
1591+
MapVector<BasicBlock *, SmallVector<BasicBlock *, 4>> PredEdgeMap;
1592+
// Group the splited edge blocks based on their predecessor (Pred).
1593+
// Example: Pred1 -> {Edge1, Edge2}, Pred2 -> {Edge3}
1594+
for (BasicBlock *Edge : SplitedCriticalEdges) {
1595+
// A splited edge block must have exactly one predecessor.
1596+
assert(Edge->getSinglePredecessor() &&
1597+
"Splited edge block should have a single predecessor");
1598+
auto *Pred = Edge->getSinglePredecessor();
1599+
PredEdgeMap[Pred].push_back(Edge);
1600+
}
1601+
1602+
// Iterate over the grouped edge blocks to perform the merge.
1603+
for (auto &PredEdgeEl : PredEdgeMap) {
1604+
BasicBlock *Pred = PredEdgeEl.first;
1605+
SmallVector<BasicBlock *, 4> &BBs = PredEdgeEl.second;
1606+
if (BBs.size() <= 1)
1607+
continue;
1608+
1609+
// Select the first edge block as the representative block that will
1610+
// remain after merging.
1611+
BasicBlock *MergedBlock = BBs[0];
1612+
for (BasicBlock *BB : llvm::drop_begin(BBs)) {
1613+
// Redirect all jumps to this block (BB) from its predecessor (which
1614+
// should only be Pred) to the MergedBlock.
1615+
Instruction *PredTI = Pred->getTerminator();
1616+
for (unsigned I = 0, E = PredTI->getNumSuccessors(); I < E; ++I) {
1617+
if (PredTI->getSuccessor(I) == BB) {
1618+
PredTI->setSuccessor(I, MergedBlock);
1619+
LLVM_DEBUG(dbgs() << " Redirected successor in " << Pred->getName()
1620+
<< " from " << BB->getName() << " to "
1621+
<< MergedBlock->getName() << "\n");
1622+
}
1623+
}
1624+
// Remove the block from the SplitedCriticalEdges list as well
1625+
auto it = find(SplitedCriticalEdges, BB);
1626+
SplitedCriticalEdges.erase(it);
1627+
DeleteDeadBlock(BB);
1628+
}
1629+
PredLoad[MergedBlock] = nullptr;
1630+
}
1631+
}
1632+
15831633
bool GVNPass::PerformLoadPRE(LoadInst *Load, AvailValInBlkVect &ValuesPerBlock,
15841634
UnavailBlkVect &UnavailableBlocks) {
15851635
// Okay, we have *some* definitions of the value. This means that the value
@@ -1703,9 +1753,10 @@ bool GVNPass::PerformLoadPRE(LoadInst *Load, AvailValInBlkVect &ValuesPerBlock,
17031753
}
17041754

17051755
// Decide whether PRE is profitable for this load.
1706-
unsigned NumInsertPreds = PredLoads.size() + CriticalEdgePredSplit.size();
1756+
unsigned NumInsertPreds = PredLoads.size();
17071757
unsigned NumUnavailablePreds = NumInsertPreds +
1708-
CriticalEdgePredAndLoad.size();
1758+
CriticalEdgePredAndLoad.size() +
1759+
CriticalEdgePredSplit.size();
17091760
assert(NumUnavailablePreds != 0 &&
17101761
"Fully available value should already be eliminated!");
17111762
(void)NumUnavailablePreds;
@@ -1734,14 +1785,36 @@ bool GVNPass::PerformLoadPRE(LoadInst *Load, AvailValInBlkVect &ValuesPerBlock,
17341785
return false;
17351786
}
17361787

1737-
// Split critical edges, and update the unavailable predecessors accordingly.
1788+
// Verify that all successors of the predecessor (Pred) are included in the
1789+
// current group (BBs). If Pred has a successor that is not in BBs, merging
1790+
// these blocks could make the Pred -> (block not in BBs) edge critical again.
1791+
// If there is at least one CriticalEdgePredSplit that cannot be merged, it
1792+
// must be rejected because it would require inserting new loads into multiple
1793+
// predecessors.
1794+
if (CriticalEdgePredSplit.size() > 1 - PredLoads.size()) {
1795+
for (BasicBlock *OrigPred : CriticalEdgePredSplit) {
1796+
auto *PredTI = OrigPred->getTerminator();
1797+
for (unsigned i = 0, e = PredTI->getNumSuccessors(); i < e - 1; ++i)
1798+
if (PredTI->getSuccessor(i) != PredTI->getSuccessor(i + 1))
1799+
return false;
1800+
}
1801+
}
1802+
1803+
// The edge from Pred to LoadBB is a critical edge will be splitted.
1804+
SmallVector<BasicBlock *, 4> SplitedCriticalEdges;
17381805
for (BasicBlock *OrigPred : CriticalEdgePredSplit) {
17391806
BasicBlock *NewPred = splitCriticalEdges(OrigPred, LoadBB);
1807+
SplitedCriticalEdges.push_back(NewPred);
17401808
assert(!PredLoads.count(OrigPred) && "Split edges shouldn't be in map!");
1741-
PredLoads[NewPred] = nullptr;
17421809
LLVM_DEBUG(dbgs() << "Split critical edge " << OrigPred->getName() << "->"
17431810
<< LoadBB->getName() << '\n');
17441811
}
1812+
// Attempts to merge the blocks created by splitting the CriticalEdges. The
1813+
// merged blocks are removed from SplitedCriticalEdges.
1814+
mergeSplitedCriticalEdges(SplitedCriticalEdges, PredLoads);
1815+
// Add the unmerged blocks separately.
1816+
for (auto BB : SplitedCriticalEdges)
1817+
PredLoads[BB] = nullptr;
17451818

17461819
for (auto &CEP : CriticalEdgePredAndLoad)
17471820
PredLoads[CEP.first] = nullptr;

llvm/test/Transforms/GVN/PRE/pre-load.ll

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -980,10 +980,13 @@ define void @test20(i1 %cond, i1 %cond2, ptr %p1, ptr %p2) {
980980
; CHECK-NEXT: store i16 [[DEC]], ptr [[P1]], align 2
981981
; CHECK-NEXT: br label [[IF_END:%.*]]
982982
; CHECK: if.else:
983-
; CHECK-NEXT: br i1 [[COND2:%.*]], label [[IF_END]], label [[IF_END]]
983+
; CHECK-NEXT: br i1 [[COND2:%.*]], label [[IF_ELSE_IF_END_CRIT_EDGE:%.*]], label [[IF_ELSE_IF_END_CRIT_EDGE]]
984+
; CHECK: if.else.if.end_crit_edge:
985+
; CHECK-NEXT: [[V2_PRE:%.*]] = load i16, ptr [[P1]], align 2
986+
; CHECK-NEXT: br label [[IF_END]]
984987
; CHECK: if.end:
985-
; CHECK-NEXT: [[V2:%.*]] = load i16, ptr [[P1]], align 2
986-
; CHECK-NEXT: store i16 [[V2]], ptr [[P2:%.*]], align 2
988+
; CHECK-NEXT: [[V3:%.*]] = phi i16 [ [[V2_PRE]], [[IF_ELSE_IF_END_CRIT_EDGE]] ], [ [[DEC]], [[IF_THEN]] ]
989+
; CHECK-NEXT: store i16 [[V3]], ptr [[P2:%.*]], align 2
987990
; CHECK-NEXT: ret void
988991
;
989992
entry:
@@ -1015,14 +1018,17 @@ define void @test21(i1 %cond, i32 %code, ptr %p1, ptr %p2) {
10151018
; CHECK-NEXT: store i16 [[DEC]], ptr [[P1]], align 2
10161019
; CHECK-NEXT: br label [[IF_END:%.*]]
10171020
; CHECK: if.else:
1018-
; CHECK-NEXT: switch i32 [[CODE:%.*]], label [[IF_END]] [
1019-
; CHECK-NEXT: i32 1, label [[IF_END]]
1020-
; CHECK-NEXT: i32 2, label [[IF_END]]
1021-
; CHECK-NEXT: i32 3, label [[IF_END]]
1021+
; CHECK-NEXT: switch i32 [[CODE:%.*]], label [[IF_ELSE_IF_END_CRIT_EDGE:%.*]] [
1022+
; CHECK-NEXT: i32 1, label [[IF_ELSE_IF_END_CRIT_EDGE]]
1023+
; CHECK-NEXT: i32 2, label [[IF_ELSE_IF_END_CRIT_EDGE]]
1024+
; CHECK-NEXT: i32 3, label [[IF_ELSE_IF_END_CRIT_EDGE]]
10221025
; CHECK-NEXT: ]
1026+
; CHECK: if.else.if.end_crit_edge:
1027+
; CHECK-NEXT: [[V2_PRE:%.*]] = load i16, ptr [[P1]], align 2
1028+
; CHECK-NEXT: br label [[IF_END]]
10231029
; CHECK: if.end:
1024-
; CHECK-NEXT: [[V2:%.*]] = load i16, ptr [[P1]], align 2
1025-
; CHECK-NEXT: store i16 [[V2]], ptr [[P2:%.*]], align 2
1030+
; CHECK-NEXT: [[V3:%.*]] = phi i16 [ [[V2_PRE]], [[IF_ELSE_IF_END_CRIT_EDGE]] ], [ [[DEC]], [[IF_THEN]] ]
1031+
; CHECK-NEXT: store i16 [[V3]], ptr [[P2:%.*]], align 2
10261032
; CHECK-NEXT: ret void
10271033
;
10281034
entry:

0 commit comments

Comments
 (0)