[llvm] [DSE] Introduce `eliminateRedundantStoresViaDominatingConditions` (PR #181709)

Antonio Frighetto via llvm-commits llvm-commits at lists.llvm.org
Wed Mar 4 03:32:36 PST 2026


https://github.com/antoniofrighetto updated https://github.com/llvm/llvm-project/pull/181709

>From dbaa13dce021f30911a200ce6676295dbf163f97 Mon Sep 17 00:00:00 2001
From: Antonio Frighetto <me at antoniofrighetto.com>
Date: Mon, 23 Feb 2026 15:12:28 +0100
Subject: [PATCH 1/2] [DSE] Introduce tests for PR181709 (NFC)

---
 .../dead-stores-via-dom-conditions.ll         | 283 ++++++++++++++++++
 1 file changed, 283 insertions(+)
 create mode 100644 llvm/test/Transforms/DeadStoreElimination/dead-stores-via-dom-conditions.ll

diff --git a/llvm/test/Transforms/DeadStoreElimination/dead-stores-via-dom-conditions.ll b/llvm/test/Transforms/DeadStoreElimination/dead-stores-via-dom-conditions.ll
new file mode 100644
index 0000000000000..c1f07c00d648b
--- /dev/null
+++ b/llvm/test/Transforms/DeadStoreElimination/dead-stores-via-dom-conditions.ll
@@ -0,0 +1,283 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt < %s -aa-pipeline=basic-aa -passes='dse,verify<memoryssa>' -S | FileCheck %s
+
+; There exists a dominating condition in the entry block, not the immediate
+; dominator for the store, the edge entry->if.eq dominates the store, no
+; clobber in between, the store is redundant.
+define void @remove_tautological_store_via_dom_condition(ptr %x, i1 %c) {
+; CHECK-LABEL: define void @remove_tautological_store_via_dom_condition(
+; CHECK-SAME: ptr [[X:%.*]], i1 [[C:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[VAL:%.*]] = load i32, ptr [[X]], align 4
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[VAL]], 0
+; CHECK-NEXT:    br i1 [[CMP]], label %[[THEN:.*]], label %[[END:.*]]
+; CHECK:       [[THEN]]:
+; CHECK-NEXT:    br i1 [[C]], label %[[IF_EQ:.*]], label %[[IF_ELSE:.*]]
+; CHECK:       [[IF_EQ]]:
+; CHECK-NEXT:    br label %[[JOIN:.*]]
+; CHECK:       [[IF_ELSE]]:
+; CHECK-NEXT:    br label %[[JOIN]]
+; CHECK:       [[JOIN]]:
+; CHECK-NEXT:    br label %[[INNER:.*]]
+; CHECK:       [[INNER]]:
+; CHECK-NEXT:    store i32 0, ptr [[X]], align 4
+; CHECK-NEXT:    br label %[[END]]
+; CHECK:       [[END]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  %val = load i32, ptr %x, align 4
+  %cmp = icmp eq i32 %val, 0
+  br i1 %cmp, label %then, label %end
+
+then:
+  br i1 %c, label %if.eq, label %if.else
+
+if.eq:
+  br label %join
+
+if.else:
+  br label %join
+
+join:
+  br label %inner
+
+inner:
+  store i32 0, ptr %x, align 4
+  br label %end
+
+end:
+  ret void
+}
+
+; There exists a dominating condition in block then, the edge then->if.eq immediately
+; dominates the first store, but does not dominate the second one, no clobber
+; in between, the first store is redundant.
+define void @remove_tautological_store_via_dom_condition_2(ptr %x, i1 %c) {
+; CHECK-LABEL: define void @remove_tautological_store_via_dom_condition_2(
+; CHECK-SAME: ptr [[X:%.*]], i1 [[C:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    br i1 [[C]], label %[[THEN:.*]], label %[[END:.*]]
+; CHECK:       [[THEN]]:
+; CHECK-NEXT:    [[VAL:%.*]] = load i32, ptr [[X]], align 4
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[VAL]], 0
+; CHECK-NEXT:    br i1 [[CMP]], label %[[IF_EQ:.*]], label %[[IF_ELSE:.*]]
+; CHECK:       [[IF_EQ]]:
+; CHECK-NEXT:    br label %[[JOIN:.*]]
+; CHECK:       [[IF_ELSE]]:
+; CHECK-NEXT:    br label %[[JOIN]]
+; CHECK:       [[JOIN]]:
+; CHECK-NEXT:    store i32 0, ptr [[X]], align 4
+; CHECK-NEXT:    br label %[[INNER:.*]]
+; CHECK:       [[INNER]]:
+; CHECK-NEXT:    br label %[[END]]
+; CHECK:       [[END]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br i1 %c, label %then, label %end
+
+then:
+  %val = load i32, ptr %x, align 4
+  %cmp = icmp eq i32 %val, 0
+  br i1 %cmp, label %if.eq, label %if.else
+
+if.eq:
+  store i32 0, ptr %x, align 4
+  br label %join
+
+if.else:
+  br label %join
+
+join:
+  store i32 0, ptr %x, align 4
+  br label %inner
+
+inner:
+  br label %end
+
+end:
+  ret void
+}
+
+; There exist two dominating conditions in the CFG, the edges entry->if.eq and
+; then->if.eq dominate the stores, no clobber in between, the stores are redundant.
+define void @remove_tautological_store_via_dom_condition_3(ptr %x, ptr %y, i1 %c) {
+; CHECK-LABEL: define void @remove_tautological_store_via_dom_condition_3(
+; CHECK-SAME: ptr [[X:%.*]], ptr [[Y:%.*]], i1 [[C:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[VAL:%.*]] = load i32, ptr [[X]], align 4
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[VAL]], 0
+; CHECK-NEXT:    br i1 [[CMP]], label %[[THEN:.*]], label %[[END:.*]]
+; CHECK:       [[THEN]]:
+; CHECK-NEXT:    [[VAL_2:%.*]] = load i32, ptr [[Y]], align 4
+; CHECK-NEXT:    [[CMP_2:%.*]] = icmp eq i32 [[VAL_2]], 0
+; CHECK-NEXT:    br i1 [[CMP_2]], label %[[IF_EQ:.*]], label %[[IF_ELSE:.*]]
+; CHECK:       [[IF_EQ]]:
+; CHECK-NEXT:    store i32 0, ptr [[X]], align 4
+; CHECK-NEXT:    store i32 0, ptr [[Y]], align 4
+; CHECK-NEXT:    br label %[[JOIN:.*]]
+; CHECK:       [[IF_ELSE]]:
+; CHECK-NEXT:    br label %[[JOIN]]
+; CHECK:       [[JOIN]]:
+; CHECK-NEXT:    br label %[[INNER:.*]]
+; CHECK:       [[INNER]]:
+; CHECK-NEXT:    br label %[[END]]
+; CHECK:       [[END]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  %val = load i32, ptr %x, align 4
+  %cmp = icmp eq i32 %val, 0
+  br i1 %cmp, label %then, label %end
+
+then:
+  %val.2 = load i32, ptr %y, align 4
+  %cmp.2 = icmp eq i32 %val.2, 0
+  br i1 %cmp.2, label %if.eq, label %if.else
+
+if.eq:
+  store i32 0, ptr %x, align 4
+  store i32 0, ptr %y, align 4
+  br label %join
+
+if.else:
+  br label %join
+
+join:
+  br label %inner
+
+inner:
+  br label %end
+
+end:
+  ret void
+}
+
+; Negative tests.
+
+; There exists a dominating condition in the entry block, however,
+; the pointer whose value is implied is clobbered in between.
+define void @remove_tautological_store_via_dom_condition_clobber_between(ptr %x, i1 %c) {
+; CHECK-LABEL: define void @remove_tautological_store_via_dom_condition_clobber_between(
+; CHECK-SAME: ptr [[X:%.*]], i1 [[C:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[VAL:%.*]] = load i32, ptr [[X]], align 4
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[VAL]], 0
+; CHECK-NEXT:    br i1 [[CMP]], label %[[IF_EQ:.*]], label %[[END:.*]]
+; CHECK:       [[IF_EQ]]:
+; CHECK-NEXT:    br label %[[NEXT:.*]]
+; CHECK:       [[NEXT]]:
+; CHECK-NEXT:    call void @opaque(ptr [[X]])
+; CHECK-NEXT:    br i1 [[C]], label %[[INNER:.*]], label %[[END]]
+; CHECK:       [[INNER]]:
+; CHECK-NEXT:    store i32 0, ptr [[X]], align 4
+; CHECK-NEXT:    br label %[[END]]
+; CHECK:       [[END]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  %val = load i32, ptr %x, align 4
+  %cmp = icmp eq i32 %val, 0
+  br i1 %cmp, label %if.eq, label %end
+
+if.eq:
+  br label %next
+
+next:
+  call void @opaque(ptr %x)
+  br i1 %c, label %inner, label %end
+
+inner:
+  store i32 0, ptr %x, align 4
+  br label %end
+
+end:
+  ret void
+}
+
+; Volatile stores should not be eliminated.
+define void @remove_tautological_store_via_dom_condition_volatile_store(ptr %x, i1 %c) {
+; CHECK-LABEL: define void @remove_tautological_store_via_dom_condition_volatile_store(
+; CHECK-SAME: ptr [[X:%.*]], i1 [[C:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[VAL:%.*]] = load i32, ptr [[X]], align 4
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[VAL]], 0
+; CHECK-NEXT:    br i1 [[CMP]], label %[[THEN:.*]], label %[[END:.*]]
+; CHECK:       [[THEN]]:
+; CHECK-NEXT:    br i1 [[C]], label %[[IF_EQ:.*]], label %[[IF_ELSE:.*]]
+; CHECK:       [[IF_EQ]]:
+; CHECK-NEXT:    br label %[[JOIN:.*]]
+; CHECK:       [[IF_ELSE]]:
+; CHECK-NEXT:    br label %[[JOIN]]
+; CHECK:       [[JOIN]]:
+; CHECK-NEXT:    store volatile i32 0, ptr [[X]], align 4
+; CHECK-NEXT:    br label %[[END]]
+; CHECK:       [[END]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  %val = load i32, ptr %x, align 4
+  %cmp = icmp eq i32 %val, 0
+  br i1 %cmp, label %then, label %end
+
+then:
+  br i1 %c, label %if.eq, label %if.else
+
+if.eq:
+  br label %join
+
+if.else:
+  br label %join
+
+join:
+  store volatile i32 0, ptr %x, align 4
+  br label %end
+
+end:
+  ret void
+}
+
+; Atomic stores should not be eliminated.
+define void @remove_tautological_store_via_dom_condition_atomic_store(ptr %x, i1 %c) {
+; CHECK-LABEL: define void @remove_tautological_store_via_dom_condition_atomic_store(
+; CHECK-SAME: ptr [[X:%.*]], i1 [[C:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[VAL:%.*]] = load i32, ptr [[X]], align 4
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[VAL]], 0
+; CHECK-NEXT:    br i1 [[CMP]], label %[[THEN:.*]], label %[[END:.*]]
+; CHECK:       [[THEN]]:
+; CHECK-NEXT:    br i1 [[C]], label %[[IF_EQ:.*]], label %[[IF_ELSE:.*]]
+; CHECK:       [[IF_EQ]]:
+; CHECK-NEXT:    br label %[[JOIN:.*]]
+; CHECK:       [[IF_ELSE]]:
+; CHECK-NEXT:    br label %[[JOIN]]
+; CHECK:       [[JOIN]]:
+; CHECK-NEXT:    store atomic i32 0, ptr [[X]] release, align 4
+; CHECK-NEXT:    br label %[[END]]
+; CHECK:       [[END]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  %val = load i32, ptr %x, align 4
+  %cmp = icmp eq i32 %val, 0
+  br i1 %cmp, label %then, label %end
+
+then:
+  br i1 %c, label %if.eq, label %if.else
+
+if.eq:
+  br label %join
+
+if.else:
+  br label %join
+
+join:
+  store atomic i32 0, ptr %x release, align 4
+  br label %end
+
+end:
+  ret void
+}
+
+declare void @opaque(ptr)

>From 0b281b9fd443fe0019f84c66735484f93f04b7cb Mon Sep 17 00:00:00 2001
From: Antonio Frighetto <me at antoniofrighetto.com>
Date: Mon, 23 Feb 2026 15:12:52 +0100
Subject: [PATCH 2/2] [DSE] Introduce
 `eliminateRedundantStoresViaDominatingConditions`

While optimizing tautological assignments, if there exists a dominating
condition that implies the value being stored in a pointer, and such a
condition appears in a node that dominates the store via equality edge,
then subsequent stores may be redundant, if no write occurs in between.
This is achieved via a DFS top-down walk of the dom-tree, collecting
dominating conditions and propagating them to each subtree, popping
them upon backtracking.

This also generalizes `dominatingConditionImpliesValue` transform,
which was previously taking into account only the immediate dominator.
---
 .../Scalar/DeadStoreElimination.cpp           | 191 ++++++++++++------
 .../dead-stores-via-dom-conditions.ll         |   3 -
 2 files changed, 129 insertions(+), 65 deletions(-)

diff --git a/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp b/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp
index 85d7618b3239c..9b6ae9cd575d1 100644
--- a/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp
+++ b/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp
@@ -174,6 +174,11 @@ static cl::opt<bool> EnableInitializesImprovement(
     "enable-dse-initializes-attr-improvement", cl::init(true), cl::Hidden,
     cl::desc("Enable the initializes attr improvement in DSE"));
 
+static cl::opt<unsigned> MaxDepthRecursion(
+    "dse-max-dom-cond-depth", cl::init(20), cl::Hidden,
+    cl::desc("Limit dominator tree depth for recursion while eliminating "
+             "redundant stores via dominating conditions"));
+
 //===----------------------------------------------------------------------===//
 // Helper functions
 //===----------------------------------------------------------------------===//
@@ -1110,10 +1115,6 @@ struct DSEState {
   /// try folding it into a call to calloc.
   bool tryFoldIntoCalloc(MemoryDef *Def, const Value *DefUO);
 
-  // Check if there is a dominating condition, that implies that the value
-  // being stored in a ptr is already present in the ptr.
-  bool dominatingConditionImpliesValue(MemoryDef *Def);
-
   /// \returns true if \p Def is a no-op store, either because it
   /// directly stores back a loaded value or stores zero to a calloced object.
   bool storeIsNoop(MemoryDef *Def, const Value *DefUO);
@@ -1124,6 +1125,11 @@ struct DSEState {
   /// is already stored at the same location.
   bool eliminateRedundantStoresOfExistingValues();
 
+  /// If there is a dominating condition that implies the value being stored in
+  /// a pointer, and such a condition appears in a node that dominates the
+  /// store, then the store may be redundant if no write occurs in between.
+  bool eliminateRedundantStoresViaDominatingConditions();
+
   // Return the locations written by the initializes attribute.
   // Note that this function considers:
   // 1. Unwind edge: use "initializes" attribute only if the callee has
@@ -2142,6 +2148,124 @@ bool DSEState::eliminateDeadWritesAtEndOfFunction() {
   return MadeChange;
 }
 
+bool DSEState::eliminateRedundantStoresViaDominatingConditions() {
+  bool MadeChange = false;
+  LLVM_DEBUG(dbgs() << "Trying to eliminate MemoryDefs whose value being "
+                       "written is implied by a dominating condition\n");
+
+  // A small struct to record `*Ptr == Val` equality facts.
+  struct EqualityCond {
+    Value *Ptr;
+    Value *Val;
+    Instruction *LI;
+    EqualityCond(Value *Ptr, Value *Val, Instruction *LI)
+        : Ptr(Ptr), Val(Val), LI(LI) {}
+  };
+
+  // We maintain a stack of the active dominating conditions for a given node.
+  SmallVector<EqualityCond, 8> ActiveConditions;
+  auto GetDominatingCondition = [&](BasicBlock *BB)
+      -> std::optional<std::pair<EqualityCond, BasicBlock *>> {
+    auto *BI = dyn_cast<BranchInst>(BB->getTerminator());
+    if (!BI || !BI->isConditional())
+      return std::nullopt;
+
+    // In case both blocks are the same, it is not possible to determine
+    // if optimization is possible. (We would not want to optimize a store
+    // in the FalseBB if condition is true and vice versa.)
+    if (BI->getSuccessor(0) == BI->getSuccessor(1))
+      return std::nullopt;
+
+    Instruction *ICmpL;
+    CmpPredicate Pred;
+    Value *StorePtr, *StoreVal;
+    if (!match(BI->getCondition(),
+               m_c_ICmp(Pred, m_Instruction(ICmpL, m_Load(m_Value(StorePtr))),
+                        m_Value(StoreVal))) ||
+        !ICmpInst::isEquality(Pred))
+      return std::nullopt;
+
+    // Ensure the replacement is allowed when comparing pointers, as
+    // the equality compares addresses only, not pointers' provenance.
+    if (StoreVal->getType()->isPointerTy() &&
+        !canReplacePointersIfEqual(StoreVal, ICmpL, DL))
+      return std::nullopt;
+
+    unsigned ImpliedSuccIdx = (Pred == ICmpInst::ICMP_EQ) ? 0 : 1;
+    BasicBlock *ImpliedSucc = BI->getSuccessor(ImpliedSuccIdx);
+    return {{EqualityCond(StorePtr, StoreVal, ICmpL), ImpliedSucc}};
+  };
+
+  using NodePredicate = std::function<void(DomTreeNode *, unsigned)>;
+  NodePredicate VisitNode = [&](DomTreeNode *Node, unsigned Depth) {
+    if (Depth > MaxDepthRecursion)
+      return;
+
+    BasicBlock *BB = Node->getBlock();
+    // Check for redundant stores against active known conditions.
+    if (auto *Accesses = MSSA.getBlockDefs(BB)) {
+      for (auto &Access : make_early_inc_range(*Accesses)) {
+        auto *Def = dyn_cast<MemoryDef>(&Access);
+        if (!Def)
+          continue;
+
+        auto *SI = dyn_cast<StoreInst>(Def->getMemoryInst());
+        if (!SI || !SI->isUnordered())
+          continue;
+
+        auto It = llvm::find_if(ActiveConditions, [&](const auto &C) {
+          return C.Ptr == SI->getPointerOperand() &&
+                 C.Val == SI->getValueOperand();
+        });
+        if (It == ActiveConditions.end())
+          continue;
+
+        // Found a dominating condition that may imply the value being stored.
+        // Make sure there does not exist any clobbering access between the
+        // load and the potential redundant store.
+        MemoryAccess *LoadAccess = MSSA.getMemoryAccess(It->LI);
+        MemoryAccess *ClobberingAccess =
+            MSSA.getSkipSelfWalker()->getClobberingMemoryAccess(Def, BatchAA);
+        if (MSSA.dominates(ClobberingAccess, LoadAccess)) {
+          LLVM_DEBUG(dbgs()
+                     << "Removing No-Op Store:\n  DEAD: " << *SI << '\n');
+          deleteDeadInstruction(SI);
+          NumRedundantStores++;
+          MadeChange = true;
+        }
+      }
+    }
+
+    // See whether this basic block establishes a dominating condition.
+    auto MaybeCondition = GetDominatingCondition(BB);
+
+    for (DomTreeNode *Child : Node->children()) {
+      bool IsActive = false;
+      if (MaybeCondition) {
+        const auto &[Cond, ImpliedSucc] = *MaybeCondition;
+        if (DT.dominates(BasicBlockEdge(BB, ImpliedSucc), Child->getBlock())) {
+          // Found a condition that holds for this child, dominated by the
+          // current node via the equality edge. Propagate the condition to
+          // the subchilds by pushing it onto the stack.
+          ActiveConditions.emplace_back(Cond);
+          IsActive = true;
+        }
+      }
+
+      // Recursively visit the children of this node. Upon returning, pop
+      // the no longer active condition before visiting any sibling nodes.
+      VisitNode(Child, Depth + 1);
+      if (IsActive)
+        ActiveConditions.pop_back();
+    }
+  };
+
+  // Do a DFS walk of the dom-tree.
+  VisitNode(DT.getRootNode(), 0);
+
+  return MadeChange;
+}
+
 bool DSEState::tryFoldIntoCalloc(MemoryDef *Def, const Value *DefUO) {
   Instruction *DefI = Def->getMemoryInst();
   MemSetInst *MemSet = dyn_cast<MemSetInst>(DefI);
@@ -2246,61 +2370,6 @@ bool DSEState::tryFoldIntoCalloc(MemoryDef *Def, const Value *DefUO) {
   return true;
 }
 
-bool DSEState::dominatingConditionImpliesValue(MemoryDef *Def) {
-  auto *StoreI = cast<StoreInst>(Def->getMemoryInst());
-  BasicBlock *StoreBB = StoreI->getParent();
-  Value *StorePtr = StoreI->getPointerOperand();
-  Value *StoreVal = StoreI->getValueOperand();
-
-  DomTreeNode *IDom = DT.getNode(StoreBB)->getIDom();
-  if (!IDom)
-    return false;
-
-  auto *BI = dyn_cast<BranchInst>(IDom->getBlock()->getTerminator());
-  if (!BI || !BI->isConditional())
-    return false;
-
-  // In case both blocks are the same, it is not possible to determine
-  // if optimization is possible. (We would not want to optimize a store
-  // in the FalseBB if condition is true and vice versa.)
-  if (BI->getSuccessor(0) == BI->getSuccessor(1))
-    return false;
-
-  Instruction *ICmpL;
-  CmpPredicate Pred;
-  if (!match(BI->getCondition(),
-             m_c_ICmp(Pred,
-                      m_CombineAnd(m_Load(m_Specific(StorePtr)),
-                                   m_Instruction(ICmpL)),
-                      m_Specific(StoreVal))) ||
-      !ICmpInst::isEquality(Pred))
-    return false;
-
-  // Ensure the replacement is allowed when comparing pointers, as
-  // the equality compares addresses only, not pointers' provenance.
-  if (StoreVal->getType()->isPointerTy() &&
-      !canReplacePointersIfEqual(StoreVal, ICmpL, DL))
-    return false;
-
-  // In case the else blocks also branches to the if block or the other way
-  // around it is not possible to determine if the optimization is possible.
-  if (Pred == ICmpInst::ICMP_EQ &&
-      !DT.dominates(BasicBlockEdge(BI->getParent(), BI->getSuccessor(0)),
-                    StoreBB))
-    return false;
-
-  if (Pred == ICmpInst::ICMP_NE &&
-      !DT.dominates(BasicBlockEdge(BI->getParent(), BI->getSuccessor(1)),
-                    StoreBB))
-    return false;
-
-  MemoryAccess *LoadAcc = MSSA.getMemoryAccess(ICmpL);
-  MemoryAccess *ClobAcc =
-      MSSA.getSkipSelfWalker()->getClobberingMemoryAccess(Def, BatchAA);
-
-  return MSSA.dominates(ClobAcc, LoadAcc);
-}
-
 bool DSEState::storeIsNoop(MemoryDef *Def, const Value *DefUO) {
   Instruction *DefI = Def->getMemoryInst();
   StoreInst *Store = dyn_cast<StoreInst>(DefI);
@@ -2329,9 +2398,6 @@ bool DSEState::storeIsNoop(MemoryDef *Def, const Value *DefUO) {
   if (!Store)
     return false;
 
-  if (dominatingConditionImpliesValue(Def))
-    return true;
-
   if (auto *LoadI = dyn_cast<LoadInst>(Store->getOperand(0))) {
     if (LoadI->getPointerOperand() == Store->getOperand(1)) {
       // Get the defining access for the load.
@@ -2738,6 +2804,7 @@ static bool eliminateDeadStores(Function &F, AliasAnalysis &AA, MemorySSA &MSSA,
 
   MadeChange |= State.eliminateRedundantStoresOfExistingValues();
   MadeChange |= State.eliminateDeadWritesAtEndOfFunction();
+  MadeChange |= State.eliminateRedundantStoresViaDominatingConditions();
 
   while (!State.ToRemove.empty()) {
     Instruction *DeadInst = State.ToRemove.pop_back_val();
diff --git a/llvm/test/Transforms/DeadStoreElimination/dead-stores-via-dom-conditions.ll b/llvm/test/Transforms/DeadStoreElimination/dead-stores-via-dom-conditions.ll
index c1f07c00d648b..53f570e2b3c53 100644
--- a/llvm/test/Transforms/DeadStoreElimination/dead-stores-via-dom-conditions.ll
+++ b/llvm/test/Transforms/DeadStoreElimination/dead-stores-via-dom-conditions.ll
@@ -20,7 +20,6 @@ define void @remove_tautological_store_via_dom_condition(ptr %x, i1 %c) {
 ; CHECK:       [[JOIN]]:
 ; CHECK-NEXT:    br label %[[INNER:.*]]
 ; CHECK:       [[INNER]]:
-; CHECK-NEXT:    store i32 0, ptr [[X]], align 4
 ; CHECK-NEXT:    br label %[[END]]
 ; CHECK:       [[END]]:
 ; CHECK-NEXT:    ret void
@@ -114,8 +113,6 @@ define void @remove_tautological_store_via_dom_condition_3(ptr %x, ptr %y, i1 %c
 ; CHECK-NEXT:    [[CMP_2:%.*]] = icmp eq i32 [[VAL_2]], 0
 ; CHECK-NEXT:    br i1 [[CMP_2]], label %[[IF_EQ:.*]], label %[[IF_ELSE:.*]]
 ; CHECK:       [[IF_EQ]]:
-; CHECK-NEXT:    store i32 0, ptr [[X]], align 4
-; CHECK-NEXT:    store i32 0, ptr [[Y]], align 4
 ; CHECK-NEXT:    br label %[[JOIN:.*]]
 ; CHECK:       [[IF_ELSE]]:
 ; CHECK-NEXT:    br label %[[JOIN]]



More information about the llvm-commits mailing list