[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