Skip to content

Commit 73edb08

Browse files
committed
[SimplifyCFG] Simplify conditional branches on const icmp eq's
1 parent 820b358 commit 73edb08

File tree

9 files changed

+106
-15
lines changed

9 files changed

+106
-15
lines changed

llvm/include/llvm/Transforms/Utils/Local.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -392,7 +392,8 @@ Instruction *removeUnwindEdge(BasicBlock *BB, DomTreeUpdater *DTU = nullptr);
392392
///
393393
/// Returns true if any basic block was removed.
394394
bool removeUnreachableBlocks(Function &F, DomTreeUpdater *DTU = nullptr,
395-
MemorySSAUpdater *MSSAU = nullptr);
395+
MemorySSAUpdater *MSSAU = nullptr,
396+
bool KeepOneInputPHIs = false);
396397

397398
/// Combine the metadata of two instructions so that K can replace J. Some
398399
/// metadata kinds can only be kept if K does not move, meaning it dominated

llvm/lib/Transforms/Scalar/SimplifyCFGPass.cpp

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "llvm/ADT/Statistic.h"
2727
#include "llvm/Analysis/AssumptionCache.h"
2828
#include "llvm/Analysis/CFG.h"
29+
#include "llvm/Analysis/ConstantFolding.h"
2930
#include "llvm/Analysis/DomTreeUpdater.h"
3031
#include "llvm/Analysis/GlobalsModRef.h"
3132
#include "llvm/Analysis/TargetTransformInfo.h"
@@ -262,12 +263,52 @@ static bool iterativelySimplifyCFG(Function &F, const TargetTransformInfo &TTI,
262263
return Changed;
263264
}
264265

266+
static void decayOneInputPHIs(BasicBlock &BB) {
267+
if (BB.empty() || !isa<PHINode>(BB.begin()))
268+
return;
269+
unsigned NumPreds = cast<PHINode>(BB.front()).getNumIncomingValues();
270+
if (NumPreds != 1)
271+
return;
272+
for (PHINode &Phi : make_early_inc_range(BB.phis())) {
273+
if (Value *PhiConst = Phi.hasConstantValue()) {
274+
Phi.replaceAllUsesWith(PhiConst);
275+
Phi.eraseFromParent();
276+
// Additionally, constant-fold conditional branch if there is one present,
277+
// and the PHI has decayed into a constant, since it may make another CFG
278+
// edge dead.
279+
if (!isa<Constant>(PhiConst))
280+
continue;
281+
if (BranchInst *BI = dyn_cast<BranchInst>(BB.getTerminator()))
282+
if (BI->isConditional())
283+
if (auto *Cmp = dyn_cast<CmpInst>(BI->getCondition()))
284+
if (auto *LHS = dyn_cast<Constant>(Cmp->getOperand(0)))
285+
if (auto *RHS = dyn_cast<Constant>(Cmp->getOperand(1))) {
286+
const DataLayout &DL = BB.getModule()->getDataLayout();
287+
Constant *ConstCond = ConstantFoldCompareInstOperands(
288+
Cmp->getPredicate(), LHS, RHS, DL);
289+
if (ConstCond)
290+
BI->setCondition(ConstCond);
291+
}
292+
}
293+
}
294+
}
295+
296+
static bool removeUnreachableBlocksAndSimplify(Function &F,
297+
DomTreeUpdater *DTU) {
298+
bool Changed = removeUnreachableBlocks(F, DTU, /*MSSAU=*/nullptr,
299+
/*KeepOneInputPHIs=*/true);
300+
if (Changed)
301+
for (BasicBlock &BB : F)
302+
decayOneInputPHIs(BB);
303+
return Changed;
304+
}
305+
265306
static bool simplifyFunctionCFGImpl(Function &F, const TargetTransformInfo &TTI,
266307
DominatorTree *DT,
267308
const SimplifyCFGOptions &Options) {
268309
DomTreeUpdater DTU(DT, DomTreeUpdater::UpdateStrategy::Eager);
269310

270-
bool EverChanged = removeUnreachableBlocks(F, DT ? &DTU : nullptr);
311+
bool EverChanged = removeUnreachableBlocksAndSimplify(F, DT ? &DTU : nullptr);
271312
EverChanged |=
272313
tailMergeBlocksWithSimilarFunctionTerminators(F, DT ? &DTU : nullptr);
273314
EverChanged |= iterativelySimplifyCFG(F, TTI, DT ? &DTU : nullptr, Options);
@@ -285,7 +326,7 @@ static bool simplifyFunctionCFGImpl(Function &F, const TargetTransformInfo &TTI,
285326

286327
do {
287328
EverChanged = iterativelySimplifyCFG(F, TTI, DT ? &DTU : nullptr, Options);
288-
EverChanged |= removeUnreachableBlocks(F, DT ? &DTU : nullptr);
329+
EverChanged |= removeUnreachableBlocksAndSimplify(F, DT ? &DTU : nullptr);
289330
} while (EverChanged);
290331

291332
return true;

llvm/lib/Transforms/Utils/Local.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3080,7 +3080,8 @@ Instruction *llvm::removeUnwindEdge(BasicBlock *BB, DomTreeUpdater *DTU) {
30803080
/// if they are in a dead cycle. Return true if a change was made, false
30813081
/// otherwise.
30823082
bool llvm::removeUnreachableBlocks(Function &F, DomTreeUpdater *DTU,
3083-
MemorySSAUpdater *MSSAU) {
3083+
MemorySSAUpdater *MSSAU,
3084+
bool KeepOneInputPHIs) {
30843085
SmallPtrSet<BasicBlock *, 16> Reachable;
30853086
bool Changed = markAliveBlocks(F, Reachable, DTU);
30863087

@@ -3111,7 +3112,7 @@ bool llvm::removeUnreachableBlocks(Function &F, DomTreeUpdater *DTU,
31113112
if (MSSAU)
31123113
MSSAU->removeBlocks(BlocksToRemove);
31133114

3114-
DeleteDeadBlocks(BlocksToRemove.takeVector(), DTU);
3115+
DeleteDeadBlocks(BlocksToRemove.takeVector(), DTU, KeepOneInputPHIs);
31153116

31163117
return Changed;
31173118
}

llvm/test/Analysis/MemorySSA/nondeterminism.ll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
; RUN: opt -passes=simplifycfg -S --preserve-ll-uselistorder %s | FileCheck %s
22
; REQUIRES: x86-registered-target
33
; CHECK-LABEL: @n
4-
; CHECK: uselistorder i16 0, { 3, 2, 4, 1, 5, 0, 6 }
4+
; CHECK: uselistorder i16 0, { 0, 1, 4, 3, 5, 2, 6 }
55

66
; Note: test was added in an effort to ensure determinism when updating memoryssa. See PR42574.
77
; If the uselistorder check becomes no longer relevant, the test can be disabled or removed.

llvm/test/Transforms/Coroutines/coro-alloca-01.ll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ suspend:
4242
; CHECK-LABEL: @f(
4343
; CHECK: %x.reload.addr = getelementptr inbounds %f.Frame, ptr %hdl, i32 0, i32 2
4444
; CHECK: %y.reload.addr = getelementptr inbounds %f.Frame, ptr %hdl, i32 0, i32 3
45-
; CHECK: %alias_phi = phi ptr [ %y.reload.addr, %merge.from.flag_false ], [ %x.reload.addr, %entry ]
45+
; CHECK: %alias_phi = select i1 %n, ptr %x.reload.addr, ptr %y.reload.addr
4646
; CHECK: %alias_phi.spill.addr = getelementptr inbounds %f.Frame, ptr %hdl, i32 0, i32 4
4747
; CHECK: store ptr %alias_phi, ptr %alias_phi.spill.addr, align 8
4848

llvm/test/Transforms/Coroutines/coro-alloca-07.ll

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -70,11 +70,7 @@ declare void @free(ptr)
7070
; CHECK-NEXT: store ptr @f.destroy, ptr [[DESTROY_ADDR]], align 8
7171
; CHECK-NEXT: [[X_RELOAD_ADDR:%.*]] = getelementptr inbounds [[F_FRAME]], ptr [[HDL]], i32 0, i32 2
7272
; CHECK-NEXT: [[Y_RELOAD_ADDR:%.*]] = getelementptr inbounds [[F_FRAME]], ptr [[HDL]], i32 0, i32 3
73-
; CHECK-NEXT: br i1 [[N:%.*]], label [[MERGE:%.*]], label [[MERGE_FROM_FLAG_FALSE:%.*]]
74-
; CHECK: merge.from.flag_false:
75-
; CHECK-NEXT: br label [[MERGE:%.*]]
76-
; CHECK: merge:
77-
; CHECK-NEXT: [[ALIAS_PHI:%.*]] = phi ptr [ [[Y_RELOAD_ADDR]], [[MERGE_FROM_FLAG_FALSE]] ], [ [[X_RELOAD_ADDR]], [[ENTRY:%.*]] ]
73+
; CHECK-NEXT: [[ALIAS_PHI:%.*]] = select i1 [[N:%.*]], ptr [[X_RELOAD_ADDR]], ptr [[Y_RELOAD_ADDR]]
7874
; CHECK-NEXT: [[ALIAS_PHI_SPILL_ADDR:%.*]] = getelementptr inbounds [[F_FRAME]], ptr [[HDL]], i32 0, i32 4
7975
; CHECK-NEXT: store ptr [[ALIAS_PHI]], ptr [[ALIAS_PHI_SPILL_ADDR]], align 8
8076
; CHECK-NEXT: store i8 1, ptr [[ALIAS_PHI]], align 1

llvm/test/Transforms/Coroutines/coro-alloca-outside-frame.ll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ suspend:
4343
; CHECK: %x = alloca i64, align 8, !coro.outside.frame !0
4444
; CHECK-NOT: %x.reload.addr = getelementptr inbounds %f.Frame, ptr %hdl, i32 0, i32 2
4545
; CHECK: %y.reload.addr = getelementptr inbounds %f.Frame, ptr %hdl, i32 0, i32 2
46-
; CHECK: %alias_phi = phi ptr [ %y.reload.addr, %merge.from.flag_false ], [ %x, %entry ]
46+
; CHECK: %alias_phi = select i1 %n, ptr %x, ptr %y.reload.addr
4747

4848
declare ptr @llvm.coro.free(token, ptr)
4949
declare i32 @llvm.coro.size.i32()

llvm/test/Transforms/Coroutines/coro-spill-defs-before-corobegin.ll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,15 +51,15 @@ lpad:
5151
; CHECK-LABEL: @f(
5252
; CHECK: %alloc = call ptr @malloc(i32 32)
5353
; CHECK-NEXT: %flag = call i1 @check(ptr %alloc)
54-
; CHECK-NEXT: %spec.select = select i1 %flag, i32 0, i32 1
54+
; CHECK-NEXT: %value_phi = select i1 %flag, i32 0, i32 1
5555
; CHECK-NEXT: %value_invoke = call i32 @calc()
5656
; CHECK-NEXT: %hdl = call noalias nonnull ptr @llvm.coro.begin(token %id, ptr %alloc)
5757

5858
; CHECK: store ptr @f.destroy, ptr %destroy.addr
5959
; CHECK-NEXT: %value_invoke.spill.addr = getelementptr inbounds %f.Frame, ptr %hdl, i32 0, i32 3
6060
; CHECK-NEXT: store i32 %value_invoke, ptr %value_invoke.spill.addr
6161
; CHECK-NEXT: %value_phi.spill.addr = getelementptr inbounds %f.Frame, ptr %hdl, i32 0, i32 2
62-
; CHECK-NEXT: store i32 %spec.select, ptr %value_phi.spill.addr
62+
; CHECK-NEXT: store i32 %value_phi, ptr %value_phi.spill.addr
6363

6464
declare ptr @llvm.coro.free(token, ptr)
6565
declare i32 @llvm.coro.size.i32()
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4
2+
; RUN: opt < %s -passes=simplifycfg -simplifycfg-require-and-preserve-domtree=1 -S | FileCheck %s
3+
4+
; A pre-existing foldable pattern should remain intact
5+
define void @const_valued_cond_br(ptr %P) {
6+
; CHECK-LABEL: define void @const_valued_cond_br(
7+
; CHECK-SAME: ptr [[P:%.*]]) {
8+
; CHECK-NEXT: entry:
9+
; CHECK-NEXT: [[COND:%.*]] = icmp eq i32 42, 42
10+
; CHECK-NEXT: br i1 [[COND]], label [[A:%.*]], label [[B:%.*]]
11+
; CHECK: a:
12+
; CHECK-NEXT: store i32 123, ptr [[P]], align 4
13+
; CHECK-NEXT: br label [[B]]
14+
; CHECK: b:
15+
; CHECK-NEXT: ret void
16+
;
17+
entry:
18+
%cond = icmp eq i32 42, 42
19+
br i1 %cond, label %a, label %b
20+
a:
21+
store i32 123, ptr %P
22+
br label %b
23+
b:
24+
ret void
25+
}
26+
27+
; When the phi decays to a constant, the terminator of `b` gets constant-folded,
28+
; enabling further simplification.
29+
define void @intersection_block_with_dead_predecessor(ptr %P) {
30+
; CHECK-LABEL: define void @intersection_block_with_dead_predecessor(
31+
; CHECK-SAME: ptr [[P:%.*]]) {
32+
; CHECK-NEXT: entry:
33+
; CHECK-NEXT: [[COND:%.*]] = icmp eq i32 1, 1
34+
; CHECK-NEXT: store i32 321, ptr [[P]], align 4
35+
; CHECK-NEXT: ret void
36+
;
37+
entry:
38+
br label %b
39+
b:
40+
%x = phi i32 [1, %entry], [2, %a]
41+
switch i32 %x, label %c [
42+
i32 1, label %d
43+
]
44+
c:
45+
store i32 123, ptr %P
46+
ret void
47+
d:
48+
store i32 321, ptr %P
49+
ret void
50+
a: ; unreachable
51+
br label %b
52+
}

0 commit comments

Comments
 (0)