[llvm] [GVN] Propagate constant equalities into expressions defined outside... (PR #211492)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Jul 23 01:22:02 PDT 2026
https://github.com/msolk created https://github.com/llvm/llvm-project/pull/211492
…the dominated region
GVN's existing equality propagation replaces direct uses of a value with a known-equal constant within the region dominated by a branch or switch edge: after `if (x == 5)`, uses of `x` in the "then" block become `5`. It does not, however, handle values computed from `x` prior to the branch, for example:
a = x + 1
if (x == 5) {
use(a) // a is known to be 6 here, but was not folded
}
This change clones such expressions into the dominated region with the constant substituted for the compared value, allowing them to be folded in the same way direct uses of `x` are.
The implementation is intentionally conservative and has a few limitations: it only follows simple chains of casts, unary operators, and binary operators built solely from the compared value; it leaves PHI operands and llvm.fake.use operands unmodified; and it requires a PostDominatorTree, which GVN now computes unconditionally, to bound the region into which expressions may be cloned. It also does not share a clone across multiple uses of the same expression, so the same computation may be duplicated if it does not fully constant-fold, relying on later DCE/CSE passes for cleanup.
>From 6acce13273a47508d41fe67af2a16a608d137980 Mon Sep 17 00:00:00 2001
From: Mayank Solanki <mayank.solanki at amd.com>
Date: Wed, 15 Jul 2026 12:17:33 +0530
Subject: [PATCH] [GVN] Propagate constant equalities into expressions defined
outside the dominated region
GVN's existing equality propagation replaces direct uses of a value
with a known-equal constant within the region dominated by a branch or
switch edge: after `if (x == 5)`, uses of `x` in the "then" block become
`5`. It does not, however, handle values computed from `x` prior to the
branch, for example:
a = x + 1
if (x == 5) {
use(a) // a is known to be 6 here, but was not folded
}
This change clones such expressions into the dominated region with the
constant substituted for the compared value, allowing them to be folded
in the same way direct uses of `x` are.
The implementation is intentionally conservative and has a few
limitations: it only follows simple chains of casts, unary operators,
and binary operators built solely from the compared value; it leaves PHI
operands and llvm.fake.use operands unmodified; and it requires a
PostDominatorTree, which GVN now computes unconditionally, to bound the
region into which expressions may be cloned. It also does not share a
clone across multiple uses of the same expression, so the same
computation may be duplicated if it does not fully constant-fold,
relying on later DCE/CSE passes for cleanup.
Co-authored-by: rbhetala <Rajasekharvenkata.Bhetala at amd.com>
Co-authored-by: Cursor <cursoragent at cursor.com>
---
llvm/include/llvm/Transforms/Scalar/GVN.h | 14 +-
llvm/lib/Transforms/Scalar/GVN.cpp | 181 +++++++++++++++++++-
llvm/test/Transforms/GVN/const-expr-prop.ll | 172 +++++++++++++++++++
3 files changed, 361 insertions(+), 6 deletions(-)
create mode 100644 llvm/test/Transforms/GVN/const-expr-prop.ll
diff --git a/llvm/include/llvm/Transforms/Scalar/GVN.h b/llvm/include/llvm/Transforms/Scalar/GVN.h
index 82684dcafc0f4..acd949fea361c 100644
--- a/llvm/include/llvm/Transforms/Scalar/GVN.h
+++ b/llvm/include/llvm/Transforms/Scalar/GVN.h
@@ -59,6 +59,7 @@ class MemorySSAUpdater;
class NonLocalDepResult;
class OptimizationRemarkEmitter;
class PHINode;
+class PostDominatorTree;
class TargetLibraryInfo;
class Value;
class IntrinsicInst;
@@ -247,6 +248,7 @@ class GVNPass : public OptionalPassInfoMixin<GVNPass> {
MemoryDependenceResults *MD = nullptr;
DominatorTree *DT = nullptr;
+ PostDominatorTree *PDT = nullptr;
const TargetLibraryInfo *TLI = nullptr;
AssumptionCache *AC = nullptr;
SetVector<BasicBlock *> DeadBlocks;
@@ -352,8 +354,8 @@ class GVNPass : public OptionalPassInfoMixin<GVNPass> {
using UnavailBlkVect = SmallVector<BasicBlock *, 64>;
bool runImpl(Function &F, AssumptionCache &RunAC, DominatorTree &RunDT,
- const TargetLibraryInfo &RunTLI, AAResults &RunAA,
- MemoryDependenceResults *RunMD, LoopInfo &LI,
+ PostDominatorTree &RunPDT, const TargetLibraryInfo &RunTLI,
+ AAResults &RunAA, MemoryDependenceResults *RunMD, LoopInfo &LI,
OptimizationRemarkEmitter *ORE, MemorySSA *MSSA = nullptr);
// List of critical edges to be split between iterations.
@@ -506,6 +508,14 @@ class GVNPass : public OptionalPassInfoMixin<GVNPass> {
bool
propagateEquality(Value *LHS, Value *RHS,
const std::variant<BasicBlockEdge, Instruction *> &Root);
+ /// Given that LHS and RHS are known to be equal along the \p Root edge (one
+ /// of them being a constant), clone expressions that are solely built from
+ /// the non-constant value and are used outside the block(s) dominated by
+ /// \p Root into that dominated region with the constant substituted in.
+ /// This exposes further constant folding for uses that propagateEquality
+ /// cannot handle because they are not a direct use of LHS.
+ bool propagateConstExpressions(Value *LHS, Value *RHS,
+ const BasicBlockEdge &Root);
bool processFoldableCondBr(CondBrInst *BI);
void addDeadBlock(BasicBlock *BB);
void assignValNumForDeadCode();
diff --git a/llvm/lib/Transforms/Scalar/GVN.cpp b/llvm/lib/Transforms/Scalar/GVN.cpp
index 42fd413423129..86363aedfc0a6 100644
--- a/llvm/lib/Transforms/Scalar/GVN.cpp
+++ b/llvm/lib/Transforms/Scalar/GVN.cpp
@@ -41,6 +41,7 @@
#include "llvm/Analysis/MemorySSAUpdater.h"
#include "llvm/Analysis/OptimizationRemarkEmitter.h"
#include "llvm/Analysis/PHITransAddr.h"
+#include "llvm/Analysis/PostDominators.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/Analysis/ValueTracking.h"
#include "llvm/IR/Attributes.h"
@@ -145,6 +146,11 @@ static cl::opt<uint32_t> MaxNumInsnsPerBlock(
cl::desc("Max number of instructions to scan in each basic block in GVN "
"(default = 100)"));
+static cl::opt<bool> GVNPropagateConstExp(
+ "gvn-const-expr-prop", cl::ReallyHidden, cl::init(true),
+ cl::desc("Propagate expressions defined outside the dominating blocks "
+ "for equality checks"));
+
struct llvm::GVNPass::Expression {
uint32_t Opcode;
bool Commutative = false;
@@ -883,6 +889,7 @@ PreservedAnalyses GVNPass::run(Function &F, FunctionAnalysisManager &AM) {
// behavior, but until then don't change the order here.
auto &AC = AM.getResult<AssumptionAnalysis>(F);
auto &DT = AM.getResult<DominatorTreeAnalysis>(F);
+ auto &PDT = AM.getResult<PostDominatorTreeAnalysis>(F);
auto &TLI = AM.getResult<TargetLibraryAnalysis>(F);
auto &AA = AM.getResult<AAManager>(F);
auto *MemDep =
@@ -895,7 +902,7 @@ PreservedAnalyses GVNPass::run(Function &F, FunctionAnalysisManager &AM) {
MSSA = &AM.getResult<MemorySSAAnalysis>(F);
}
auto &ORE = AM.getResult<OptimizationRemarkEmitterAnalysis>(F);
- bool Changed = runImpl(F, AC, DT, TLI, AA, MemDep, LI, &ORE,
+ bool Changed = runImpl(F, AC, DT, PDT, TLI, AA, MemDep, LI, &ORE,
MSSA ? &MSSA->getMSSA() : nullptr);
if (!Changed)
return PreservedAnalyses::all();
@@ -3083,6 +3090,151 @@ void GVNPass::assignBlockRPONumber(Function &F) {
InvalidBlockRPONumbers = false;
}
+namespace {
+
+/// Check if \p Expr is an expression involving only \p Base and/or constants.
+bool isExprBuiltFromOnly(Value *Expr, Value *Base) {
+ if (isa<Constant>(Expr))
+ return false;
+ if (Expr == Base)
+ return true;
+ if (!isa<Instruction>(Expr))
+ return false;
+ if (!isa<CastInst, BinaryOperator, UnaryOperator>(Expr))
+ return false;
+ if (auto *II = dyn_cast<IntrinsicInst>(Expr))
+ if (II->getIntrinsicID() == Intrinsic::fake_use)
+ return false;
+ auto *ExprInst = cast<Instruction>(Expr);
+ bool UsedAtleastOnce = false;
+ for (unsigned i = 0, e = ExprInst->getNumOperands(); i != e; ++i) {
+ auto *Op = ExprInst->getOperand(i);
+ if (isa<Constant>(Op))
+ continue;
+ if (!isExprBuiltFromOnly(Op, Base))
+ return false;
+ UsedAtleastOnce = true;
+ }
+ return UsedAtleastOnce;
+}
+
+/// Clone the valid-use expression \p Expr, replacing any use of \p OldVal
+/// with \p NewVal, inserting the cloned instructions right before
+/// \p InsertPt. This avoids creating a new expression for the same value
+/// more than once and keeps the clone as close as possible to its first use.
+Value *cloneExprReplacingOperand(Value *Expr, const Value *OldVal,
+ Value *NewVal, Instruction *InsertPt) {
+ if (isa<Constant>(Expr))
+ return Expr;
+ if (Expr == OldVal)
+ return NewVal;
+ if (!isa<Instruction>(Expr))
+ return nullptr;
+ // Only handle Cast, BinOp, UnaryOp for now.
+ if (!isa<CastInst, BinaryOperator, UnaryOperator>(Expr))
+ return nullptr;
+ auto *ExprInst = cast<Instruction>(Expr);
+ SmallVector<Value *, 4> NewOps;
+ for (unsigned i = 0, e = ExprInst->getNumOperands(); i != e; ++i) {
+ auto *Op = ExprInst->getOperand(i);
+ auto *NewOp = cloneExprReplacingOperand(Op, OldVal, NewVal, InsertPt);
+ if (!NewOp)
+ return nullptr;
+ NewOps.push_back(NewOp);
+ }
+ auto *NewInst = ExprInst->clone();
+ for (unsigned i = 0, e = NewOps.size(); i != e; ++i)
+ NewInst->setOperand(i, NewOps[i]);
+
+ NewInst->insertBefore(InsertPt->getIterator());
+ LLVM_DEBUG(dbgs() << "ConstPropExpr: Cloning Inst " << *NewInst << "\n");
+ return NewInst;
+}
+
+} // end anonymous namespace
+
+/// LHS and RHS are known to be equal along the \p Root edge, with one of
+/// them being a constant. Look for instructions dominated by \p Root that
+/// use an expression built solely from the non-constant value but whose
+/// operand is defined outside the region dominated by \p Root (so
+/// propagateEquality's direct-use replacement cannot reach it). Clone such
+/// expressions into the dominated region with the constant substituted in,
+/// exposing further constant folding. Returns whether a change was made.
+bool GVNPass::propagateConstExpressions(Value *LHS, Value *RHS,
+ const BasicBlockEdge &Root) {
+ if (!GVNPropagateConstExp || !LHS->getType()->isIntegerTy() ||
+ !(isa<Constant>(LHS) || isa<Constant>(RHS)))
+ return false;
+ if (!isa<Constant>(RHS))
+ std::swap(LHS, RHS);
+ if (!DT || !PDT)
+ return false;
+ if (!isOnlyReachableViaThisEdge(Root, DT))
+ return false;
+
+ BasicBlock *RootBB = const_cast<BasicBlock *>(Root.getEnd());
+
+ if (PDT->dominates(Root.getEnd(), Root.getStart()))
+ return false;
+
+ for (const BasicBlock *BB : successors(Root.getStart())) {
+ if (BB != Root.getEnd() &&
+ (PDT->dominates(Root.getEnd(), BB) ||
+ isPotentiallyReachable(BB, Root.getEnd())))
+ return false;
+ }
+
+ bool ChangedIR = false;
+ for (BasicBlock *BB : depth_first(RootBB)) {
+ if (!DT->dominates(Root.getEnd(), BB))
+ continue;
+ if (PDT->dominates(BB, Root.getStart()))
+ break;
+ for (Instruction &I : *BB) {
+ if (isa<PHINode>(&I))
+ continue;
+ if (auto *II = dyn_cast<IntrinsicInst>(&I))
+ if (II->getIntrinsicID() == Intrinsic::fake_use)
+ continue;
+
+ for (unsigned OpNum = 0; OpNum < I.getNumOperands(); ++OpNum) {
+ Value *Op = I.getOperand(OpNum);
+ if (!isa<Instruction>(Op) || !isExprBuiltFromOnly(Op, LHS))
+ continue;
+
+ Instruction *OpInst = cast<Instruction>(Op);
+ if (DT->dominates(Root.getEnd(), OpInst->getParent()))
+ continue;
+
+ Value *ClonedExpr = cloneExprReplacingOperand(OpInst, LHS, RHS, &I);
+ if (!ClonedExpr || ClonedExpr == OpInst)
+ continue;
+
+ LLVM_DEBUG(dbgs() << "ConstPropExpr: From: " << I);
+ I.setOperand(OpNum, ClonedExpr);
+ LLVM_DEBUG(dbgs() << "\nConstPropExpr: To: " << I << "\n");
+
+ if (isa<Instruction>(ClonedExpr) && ClonedExpr->hasOneUse()) {
+ auto *CI = cast<Instruction>(ClonedExpr);
+ const DataLayout &DL = I.getDataLayout();
+ if (Value *V = simplifyInstruction(CI, {DL, TLI, DT, AC})) {
+ I.setOperand(OpNum, V);
+ LLVM_DEBUG(dbgs() << "ConstPropExpr: Optimized instruction: " << I
+ << "\n");
+ }
+ ++NumGVNEqProp;
+ }
+ ChangedIR = true;
+ }
+ }
+ }
+
+ if (ChangedIR)
+ LLVM_DEBUG(dbgs() << "ConstPropExpr: With " << *LHS << " == " << *RHS
+ << "\n");
+ return ChangedIR;
+}
+
/// The given values are known to be equal in every use
/// dominated by 'Root'. Exploit this, for example by replacing 'LHS' with
/// 'RHS' everywhere in the scope. Returns whether a change was made.
@@ -3354,10 +3506,25 @@ bool GVNPass::processInstruction(Instruction *I) {
Value *TrueVal = ConstantInt::getTrue(TrueSucc->getContext());
BasicBlockEdge TrueE(Parent, TrueSucc);
Changed |= propagateEquality(BranchCond, TrueVal, TrueE);
+ Changed |= propagateConstExpressions(BranchCond, TrueVal, TrueE);
Value *FalseVal = ConstantInt::getFalse(FalseSucc->getContext());
BasicBlockEdge FalseE(Parent, FalseSucc);
Changed |= propagateEquality(BranchCond, FalseVal, FalseE);
+ Changed |= propagateConstExpressions(BranchCond, FalseVal, FalseE);
+
+ // If the condition is a comparison, also propagate the equality (or
+ // disequality) between its operands into whichever edge it is known to
+ // hold along, e.g. for "if (x == 5) ... " propagate x == 5 into the
+ // true edge. This mirrors the equality propagateEquality() itself
+ // derives from CmpInst equivalences.
+ if (CmpInst *Cmp = dyn_cast<CmpInst>(BranchCond)) {
+ Value *Op0 = Cmp->getOperand(0), *Op1 = Cmp->getOperand(1);
+ if (Cmp->isEquivalence(/*Invert=*/false))
+ Changed |= propagateConstExpressions(Op0, Op1, TrueE);
+ if (Cmp->isEquivalence(/*Invert=*/true))
+ Changed |= propagateConstExpressions(Op0, Op1, FalseE);
+ }
return Changed;
}
@@ -3379,6 +3546,7 @@ bool GVNPass::processInstruction(Instruction *I) {
if (SwitchEdges.lookup(Dst) == 1) {
BasicBlockEdge E(Parent, Dst);
Changed |= propagateEquality(SwitchCond, Case.getCaseValue(), E);
+ Changed |= propagateConstExpressions(SwitchCond, Case.getCaseValue(), E);
}
}
return Changed;
@@ -3449,11 +3617,13 @@ bool GVNPass::processInstruction(Instruction *I) {
/// runOnFunction - This is the main transformation entry point for a function.
bool GVNPass::runImpl(Function &F, AssumptionCache &RunAC, DominatorTree &RunDT,
- const TargetLibraryInfo &RunTLI, AAResults &RunAA,
- MemoryDependenceResults *RunMD, LoopInfo &LI,
- OptimizationRemarkEmitter *RunORE, MemorySSA *MSSA) {
+ PostDominatorTree &RunPDT, const TargetLibraryInfo &RunTLI,
+ AAResults &RunAA, MemoryDependenceResults *RunMD,
+ LoopInfo &LI, OptimizationRemarkEmitter *RunORE,
+ MemorySSA *MSSA) {
AC = &RunAC;
DT = &RunDT;
+ PDT = &RunPDT;
VN.setDomTree(DT);
TLI = &RunTLI;
AA = &RunAA;
@@ -4012,6 +4182,7 @@ class llvm::GVNLegacyPass : public FunctionPass {
return Impl.runImpl(
F, getAnalysis<AssumptionCacheTracker>().getAssumptionCache(F),
getAnalysis<DominatorTreeWrapperPass>().getDomTree(),
+ getAnalysis<PostDominatorTreeWrapperPass>().getPostDomTree(),
getAnalysis<TargetLibraryInfoWrapperPass>().getTLI(F),
getAnalysis<AAResultsWrapperPass>().getAAResults(),
Impl.isMemDepEnabled()
@@ -4025,6 +4196,7 @@ class llvm::GVNLegacyPass : public FunctionPass {
void getAnalysisUsage(AnalysisUsage &AU) const override {
AU.addRequired<AssumptionCacheTracker>();
AU.addRequired<DominatorTreeWrapperPass>();
+ AU.addRequired<PostDominatorTreeWrapperPass>();
AU.addRequired<TargetLibraryInfoWrapperPass>();
AU.addRequired<LoopInfoWrapperPass>();
if (Impl.isMemDepEnabled())
@@ -4051,6 +4223,7 @@ INITIALIZE_PASS_DEPENDENCY(AssumptionCacheTracker)
INITIALIZE_PASS_DEPENDENCY(MemoryDependenceWrapperPass)
INITIALIZE_PASS_DEPENDENCY(MemorySSAWrapperPass)
INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass)
+INITIALIZE_PASS_DEPENDENCY(PostDominatorTreeWrapperPass)
INITIALIZE_PASS_DEPENDENCY(TargetLibraryInfoWrapperPass)
INITIALIZE_PASS_DEPENDENCY(AAResultsWrapperPass)
INITIALIZE_PASS_DEPENDENCY(GlobalsAAWrapperPass)
diff --git a/llvm/test/Transforms/GVN/const-expr-prop.ll b/llvm/test/Transforms/GVN/const-expr-prop.ll
new file mode 100644
index 0000000000000..0e41370f5c6f9
--- /dev/null
+++ b/llvm/test/Transforms/GVN/const-expr-prop.ll
@@ -0,0 +1,172 @@
+; RUN: opt -S -passes=gvn -gvn-const-expr-prop=true < %s | FileCheck %s
+
+declare void @llvm.fake.use(...)
+
+; %a is built solely from %x but is defined *outside* the region dominated by
+; the true edge of %c, so propagateEquality's direct-use replacement cannot
+; reach it. propagateConstExpressions should clone %a into if.true with %x
+; replaced by the known constant 5, and constant-fold the clone.
+define i32 @add_outside_dominated_region(i32 %x) {
+; CHECK-LABEL: @add_outside_dominated_region(
+; CHECK: if.true:
+; CHECK-NEXT: ret i32 6
+ %a = add i32 %x, 1
+ %c = icmp eq i32 %x, 5
+ br i1 %c, label %if.true, label %if.false
+if.true:
+ ret i32 %a
+if.false:
+ ret i32 0
+}
+
+; Same as above, but with the constant on the left-hand side of the compare.
+define i32 @add_outside_dominated_region_constant_lhs(i32 %x) {
+; CHECK-LABEL: @add_outside_dominated_region_constant_lhs(
+; CHECK: if.true:
+; CHECK-NEXT: ret i32 6
+ %a = add i32 %x, 1
+ %c = icmp eq i32 5, %x
+ br i1 %c, label %if.true, label %if.false
+if.true:
+ ret i32 %a
+if.false:
+ ret i32 0
+}
+
+; Cast expressions should also be cloned and folded.
+define i64 @zext_outside_dominated_region(i32 %x) {
+; CHECK-LABEL: @zext_outside_dominated_region(
+; CHECK: if.true:
+; CHECK-NEXT: ret i64 5
+ %z = zext i32 %x to i64
+ %c = icmp eq i32 %x, 5
+ br i1 %c, label %if.true, label %if.false
+if.true:
+ ret i64 %z
+if.false:
+ ret i64 0
+}
+
+; Chains of expressions built solely from %x should be cloned recursively.
+define i32 @chained_expr_outside_dominated_region(i32 %x) {
+; CHECK-LABEL: @chained_expr_outside_dominated_region(
+; CHECK: if.true:
+; CHECK-NEXT: ret i32 12
+ %a = add i32 %x, 1
+ %m = mul i32 %a, 2
+ %c = icmp eq i32 %x, 5
+ br i1 %c, label %if.true, label %if.false
+if.true:
+ ret i32 %m
+if.false:
+ ret i32 0
+}
+
+; Switch case edges are also handled, not just conditional branches.
+define i32 @switch_case_outside_dominated_region(i32 %x) {
+; CHECK-LABEL: @switch_case_outside_dominated_region(
+; CHECK: case3:
+; CHECK-NEXT: ret i32 13
+ %a = add i32 %x, 10
+ switch i32 %x, label %default [
+ i32 3, label %case3
+ ]
+case3:
+ ret i32 %a
+default:
+ ret i32 0
+}
+
+; The dominated region is intentionally widened beyond Root.getEnd(): an
+; expression only reachable through a whole diamond nested inside if.true
+; should still be folded.
+define i32 @nested_diamond_inside_dominated_region(i32 %x, i1 %arg) {
+; CHECK-LABEL: @nested_diamond_inside_dominated_region(
+; CHECK: inner.merge:
+; CHECK-NEXT: ret i32 6
+ %a = add i32 %x, 1
+ %c = icmp eq i32 %x, 5
+ br i1 %c, label %if.true, label %if.false
+if.true:
+ br i1 %arg, label %inner.a, label %inner.b
+inner.a:
+ br label %inner.merge
+inner.b:
+ br label %inner.merge
+inner.merge:
+ ret i32 %a
+if.false:
+ ret i32 0
+}
+
+; PHI operands are deliberately left untouched even inside the dominated
+; region: rewriting them would need to account for which predecessor the
+; value flows from. Use different incoming values so the PHI cannot be
+; trivially simplified away before we get a chance to (not) rewrite it.
+define i32 @phi_operand_not_touched(i32 %x, i1 %arg) {
+; CHECK-LABEL: @phi_operand_not_touched(
+; CHECK: inner.merge:
+; CHECK-NEXT: %p = phi i32 [ %a, %inner.a ], [ 0, %inner.b ]
+; CHECK-NEXT: ret i32 %p
+ %a = add i32 %x, 1
+ %c = icmp eq i32 %x, 5
+ br i1 %c, label %if.true, label %if.false
+if.true:
+ br i1 %arg, label %inner.a, label %inner.b
+inner.a:
+ br label %inner.merge
+inner.b:
+ br label %inner.merge
+inner.merge:
+ %p = phi i32 [ %a, %inner.a ], [ 0, %inner.b ]
+ ret i32 %p
+if.false:
+ ret i32 0
+}
+
+; llvm.fake.use operands are deliberately left untouched, but other uses in
+; the same block are still folded normally.
+define i32 @fake_use_operand_not_touched(i32 %x) {
+; CHECK-LABEL: @fake_use_operand_not_touched(
+; CHECK: if.true:
+; CHECK-NEXT: call void (...) @llvm.fake.use(i32 %a)
+; CHECK-NEXT: ret i32 6
+ %a = add i32 %x, 1
+ %c = icmp eq i32 %x, 5
+ br i1 %c, label %if.true, label %if.false
+if.true:
+ call void (...) @llvm.fake.use(i32 %a)
+ ret i32 %a
+if.false:
+ ret i32 0
+}
+
+; if.true is reachable from more than one predecessor, so the edge equality
+; does not hold throughout it and no propagation should happen.
+define i32 @not_only_reachable_via_edge(i32 %x, i1 %arg) {
+; CHECK-LABEL: @not_only_reachable_via_edge(
+; CHECK: if.true:
+; CHECK-NEXT: ret i32 %a
+ %a = add i32 %x, 1
+ %c = icmp eq i32 %x, 5
+ br i1 %c, label %if.true, label %other
+other:
+ br label %if.true
+if.true:
+ ret i32 %a
+}
+
+; Only integer equalities are supported: an expression built from a pointer
+; known-equal to null should not be folded.
+define i64 @pointer_type_not_propagated(ptr %x) {
+; CHECK-LABEL: @pointer_type_not_propagated(
+; CHECK: if.true:
+; CHECK-NEXT: ret i64 %a
+ %a = ptrtoint ptr %x to i64
+ %c = icmp eq ptr %x, null
+ br i1 %c, label %if.true, label %if.false
+if.true:
+ ret i64 %a
+if.false:
+ ret i64 0
+}
More information about the llvm-commits
mailing list