Skip to content

Commit 55f3b92

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. Proof: https://alive2.llvm.org/ce/z/-2rLLQ
1 parent 0ba7bfc commit 55f3b92

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
@@ -398,6 +398,9 @@ class GVNPass : public PassInfoMixin<GVNPass> {
398398
void verifyRemoved(const Instruction *I) const;
399399
bool splitCriticalEdges();
400400
BasicBlock *splitCriticalEdges(BasicBlock *Pred, BasicBlock *Succ);
401+
void
402+
mergeSplitedCriticalEdges(SmallVectorImpl<BasicBlock *> &SplitedCriticalEdges,
403+
MapVector<BasicBlock *, Value *> &PredLoad);
401404
bool
402405
propagateEquality(Value *LHS, Value *RHS,
403406
const std::variant<BasicBlockEdge, Instruction *> &Root);

llvm/lib/Transforms/Scalar/GVN.cpp

Lines changed: 77 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1646,6 +1646,56 @@ void GVNPass::eliminatePartiallyRedundantLoad(
16461646
salvageAndRemoveInstruction(Load);
16471647
}
16481648

1649+
/// Merges splited critical edge blocks that originate from the same
1650+
/// predecessor.
1651+
void GVNPass::mergeSplitedCriticalEdges(
1652+
SmallVectorImpl<BasicBlock *> &SplitedCriticalEdges,
1653+
MapVector<BasicBlock *, Value *> &PredLoad) {
1654+
if (SplitedCriticalEdges.size() <= 1)
1655+
return;
1656+
1657+
MapVector<BasicBlock *, SmallVector<BasicBlock *, 4>> PredEdgeMap;
1658+
// Group the splited edge blocks based on their predecessor (Pred).
1659+
// Example: Pred1 -> {Edge1, Edge2}, Pred2 -> {Edge3}
1660+
for (BasicBlock *Edge : SplitedCriticalEdges) {
1661+
// A splited edge block must have exactly one predecessor.
1662+
assert(Edge->getSinglePredecessor() &&
1663+
"Splited edge block should have a single predecessor");
1664+
auto *Pred = Edge->getSinglePredecessor();
1665+
PredEdgeMap[Pred].push_back(Edge);
1666+
}
1667+
1668+
// Iterate over the grouped edge blocks to perform the merge.
1669+
for (auto &PredEdgeEl : PredEdgeMap) {
1670+
BasicBlock *Pred = PredEdgeEl.first;
1671+
SmallVector<BasicBlock *, 4> &BBs = PredEdgeEl.second;
1672+
if (BBs.size() <= 1)
1673+
continue;
1674+
1675+
// Select the first edge block as the representative block that will
1676+
// remain after merging.
1677+
BasicBlock *MergedBlock = BBs[0];
1678+
for (BasicBlock *BB : llvm::drop_begin(BBs)) {
1679+
// Redirect all jumps to this block (BB) from its predecessor (which
1680+
// should only be Pred) to the MergedBlock.
1681+
Instruction *PredTI = Pred->getTerminator();
1682+
for (unsigned I = 0, E = PredTI->getNumSuccessors(); I < E; ++I) {
1683+
if (PredTI->getSuccessor(I) == BB) {
1684+
PredTI->setSuccessor(I, MergedBlock);
1685+
LLVM_DEBUG(dbgs() << " Redirected successor in " << Pred->getName()
1686+
<< " from " << BB->getName() << " to "
1687+
<< MergedBlock->getName() << "\n");
1688+
}
1689+
}
1690+
// Remove the block from the SplitedCriticalEdges list as well
1691+
auto it = find(SplitedCriticalEdges, BB);
1692+
SplitedCriticalEdges.erase(it);
1693+
DeleteDeadBlock(BB);
1694+
}
1695+
PredLoad[MergedBlock] = nullptr;
1696+
}
1697+
}
1698+
16491699
bool GVNPass::PerformLoadPRE(LoadInst *Load, AvailValInBlkVect &ValuesPerBlock,
16501700
UnavailBlkVect &UnavailableBlocks) {
16511701
// Okay, we have *some* definitions of the value. This means that the value
@@ -1769,9 +1819,10 @@ bool GVNPass::PerformLoadPRE(LoadInst *Load, AvailValInBlkVect &ValuesPerBlock,
17691819
}
17701820

17711821
// Decide whether PRE is profitable for this load.
1772-
unsigned NumInsertPreds = PredLoads.size() + CriticalEdgePredSplit.size();
1822+
unsigned NumInsertPreds = PredLoads.size();
17731823
unsigned NumUnavailablePreds = NumInsertPreds +
1774-
CriticalEdgePredAndLoad.size();
1824+
CriticalEdgePredAndLoad.size() +
1825+
CriticalEdgePredSplit.size();
17751826
assert(NumUnavailablePreds != 0 &&
17761827
"Fully available value should already be eliminated!");
17771828
(void)NumUnavailablePreds;
@@ -1800,14 +1851,36 @@ bool GVNPass::PerformLoadPRE(LoadInst *Load, AvailValInBlkVect &ValuesPerBlock,
18001851
return false;
18011852
}
18021853

1803-
// Split critical edges, and update the unavailable predecessors accordingly.
1854+
// Verify that all successors of the predecessor (Pred) are included in the
1855+
// current group (BBs). If Pred has a successor that is not in BBs, merging
1856+
// these blocks could make the Pred -> (block not in BBs) edge critical again.
1857+
// If there is at least one CriticalEdgePredSplit that cannot be merged, it
1858+
// must be rejected because it would require inserting new loads into multiple
1859+
// predecessors.
1860+
if (CriticalEdgePredSplit.size() > 1 - PredLoads.size()) {
1861+
for (BasicBlock *OrigPred : CriticalEdgePredSplit) {
1862+
auto *PredTI = OrigPred->getTerminator();
1863+
for (unsigned i = 0, e = PredTI->getNumSuccessors(); i < e - 1; ++i)
1864+
if (PredTI->getSuccessor(i) != PredTI->getSuccessor(i + 1))
1865+
return false;
1866+
}
1867+
}
1868+
1869+
// The edge from Pred to LoadBB is a critical edge will be splitted.
1870+
SmallVector<BasicBlock *, 4> SplitedCriticalEdges;
18041871
for (BasicBlock *OrigPred : CriticalEdgePredSplit) {
18051872
BasicBlock *NewPred = splitCriticalEdges(OrigPred, LoadBB);
1873+
SplitedCriticalEdges.push_back(NewPred);
18061874
assert(!PredLoads.count(OrigPred) && "Split edges shouldn't be in map!");
1807-
PredLoads[NewPred] = nullptr;
18081875
LLVM_DEBUG(dbgs() << "Split critical edge " << OrigPred->getName() << "->"
18091876
<< LoadBB->getName() << '\n');
18101877
}
1878+
// Attempts to merge the blocks created by splitting the CriticalEdges. The
1879+
// merged blocks are removed from SplitedCriticalEdges.
1880+
mergeSplitedCriticalEdges(SplitedCriticalEdges, PredLoads);
1881+
// Add the unmerged blocks separately.
1882+
for (auto BB : SplitedCriticalEdges)
1883+
PredLoads[BB] = nullptr;
18111884

18121885
for (auto &CEP : CriticalEdgePredAndLoad)
18131886
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
@@ -1303,10 +1303,13 @@ define void @test20(i1 %cond, i1 %cond2, ptr %p1, ptr %p2) {
13031303
; CHECK-NEXT: store i16 [[DEC]], ptr [[P1]], align 2
13041304
; CHECK-NEXT: br label [[IF_END:%.*]]
13051305
; CHECK: if.else:
1306-
; CHECK-NEXT: br i1 [[COND2:%.*]], label [[IF_END]], label [[IF_END]]
1306+
; CHECK-NEXT: br i1 [[COND2:%.*]], label [[IF_ELSE_IF_END_CRIT_EDGE:%.*]], label [[IF_ELSE_IF_END_CRIT_EDGE]]
1307+
; CHECK: if.else.if.end_crit_edge:
1308+
; CHECK-NEXT: [[V2_PRE:%.*]] = load i16, ptr [[P1]], align 2
1309+
; CHECK-NEXT: br label [[IF_END]]
13071310
; CHECK: if.end:
1308-
; CHECK-NEXT: [[V2:%.*]] = load i16, ptr [[P1]], align 2
1309-
; CHECK-NEXT: store i16 [[V2]], ptr [[P2:%.*]], align 2
1311+
; CHECK-NEXT: [[V3:%.*]] = phi i16 [ [[V2_PRE]], [[IF_ELSE_IF_END_CRIT_EDGE]] ], [ [[DEC]], [[IF_THEN]] ]
1312+
; CHECK-NEXT: store i16 [[V3]], ptr [[P2:%.*]], align 2
13101313
; CHECK-NEXT: ret void
13111314
;
13121315
entry:
@@ -1338,14 +1341,17 @@ define void @test21(i1 %cond, i32 %code, ptr %p1, ptr %p2) {
13381341
; CHECK-NEXT: store i16 [[DEC]], ptr [[P1]], align 2
13391342
; CHECK-NEXT: br label [[IF_END:%.*]]
13401343
; CHECK: if.else:
1341-
; CHECK-NEXT: switch i32 [[CODE:%.*]], label [[IF_END]] [
1342-
; CHECK-NEXT: i32 1, label [[IF_END]]
1343-
; CHECK-NEXT: i32 2, label [[IF_END]]
1344-
; CHECK-NEXT: i32 3, label [[IF_END]]
1344+
; CHECK-NEXT: switch i32 [[CODE:%.*]], label [[IF_ELSE_IF_END_CRIT_EDGE:%.*]] [
1345+
; CHECK-NEXT: i32 1, label [[IF_ELSE_IF_END_CRIT_EDGE]]
1346+
; CHECK-NEXT: i32 2, label [[IF_ELSE_IF_END_CRIT_EDGE]]
1347+
; CHECK-NEXT: i32 3, label [[IF_ELSE_IF_END_CRIT_EDGE]]
13451348
; CHECK-NEXT: ]
1349+
; CHECK: if.else.if.end_crit_edge:
1350+
; CHECK-NEXT: [[V2_PRE:%.*]] = load i16, ptr [[P1]], align 2
1351+
; CHECK-NEXT: br label [[IF_END]]
13461352
; CHECK: if.end:
1347-
; CHECK-NEXT: [[V2:%.*]] = load i16, ptr [[P1]], align 2
1348-
; CHECK-NEXT: store i16 [[V2]], ptr [[P2:%.*]], align 2
1353+
; CHECK-NEXT: [[V3:%.*]] = phi i16 [ [[V2_PRE]], [[IF_ELSE_IF_END_CRIT_EDGE]] ], [ [[DEC]], [[IF_THEN]] ]
1354+
; CHECK-NEXT: store i16 [[V3]], ptr [[P2:%.*]], align 2
13491355
; CHECK-NEXT: ret void
13501356
;
13511357
entry:

0 commit comments

Comments
 (0)