[llvm] [DeadStoreElimination] Optimize tautological assignments (PR #75744)

Shreyansh Chouhan via llvm-commits llvm-commits at lists.llvm.org
Sat Dec 23 08:31:43 PST 2023


https://github.com/BK1603 updated https://github.com/llvm/llvm-project/pull/75744

>From e464dc0f8a0fca7ebe089e7e3561fbcaf9a4ebe8 Mon Sep 17 00:00:00 2001
From: Shreyansh Chouhan <chouhan.shreyansh2702 at gmail.com>
Date: Sun, 17 Dec 2023 22:48:51 +0530
Subject: [PATCH] [DeadStoreElimination] Optimize tautological assignments

If a store is immediately dominated by a condition that ensures that the value
being stored in a memory location is already present at that memory location,
consider the store a noop.

Fixes #63419
---
 .../Scalar/DeadStoreElimination.cpp           | 48 ++++++++++
 .../DeadStoreElimination/noop-stores.ll       | 88 ++++++++++++++++++-
 2 files changed, 134 insertions(+), 2 deletions(-)

diff --git a/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp b/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp
index dd0a290252dae3..a803e0720460ae 100644
--- a/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp
+++ b/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp
@@ -1931,6 +1931,54 @@ struct DSEState {
     if (!Store)
       return false;
 
+    // If there is a dominating condition, that ensures that the value
+    // being stored in a memory location is already present at the
+    // memory location, the store is a noop.
+    BasicBlock *StoreBB = DefI->getParent();
+    Value *StorePtr = Store->getPointerOperand();
+    Value *StoreVal = Store->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;
+
+    // Load can be either of the two operands
+    ICmpInst *CI = dyn_cast<ICmpInst>(BI->getCondition());
+    if (!CI || !CI->isEquality())
+      return false;
+    // Check if load is from the operand that we are storing to.
+    LoadInst *ICmpL = nullptr;
+    Value *ICmpV = nullptr;
+    for (Value *Op : CI->operands()) {
+      LLVM_DEBUG(dbgs() << "Operand: " << *Op << "\n");
+      auto *LI = dyn_cast<LoadInst>(Op);
+      if (LI && LI->getPointerOperand() == StorePtr)
+        ICmpL = LI;
+      if (Op == StoreVal)
+        ICmpV = Op;
+    }
+
+    if (!ICmpL || !ICmpV)
+      return false;
+
+    MemoryAccess *LoadAcc, *ClobAcc;
+    LoadAcc = MSSA.getMemoryAccess(ICmpL)->getDefiningAccess();
+    ClobAcc = MSSA.getSkipSelfWalker()->getClobberingMemoryAccess(Def, BatchAA);
+
+    if (!MSSA.dominates(ClobAcc, LoadAcc))
+      return false;
+
+    ICmpInst::Predicate Pred = CI->getPredicate();
+    if (Pred == ICmpInst::ICMP_EQ && StoreBB == BI->getSuccessor(0))
+      return true;
+
+    if (Pred == ICmpInst::ICMP_NE && StoreBB == BI->getSuccessor(1))
+      return true;
+
     if (auto *LoadI = dyn_cast<LoadInst>(Store->getOperand(0))) {
       if (LoadI->getPointerOperand() == Store->getOperand(1)) {
         // Get the defining access for the load.
diff --git a/llvm/test/Transforms/DeadStoreElimination/noop-stores.ll b/llvm/test/Transforms/DeadStoreElimination/noop-stores.ll
index 3703b8d039ead0..fbc39a5654bfc0 100644
--- a/llvm/test/Transforms/DeadStoreElimination/noop-stores.ll
+++ b/llvm/test/Transforms/DeadStoreElimination/noop-stores.ll
@@ -16,6 +16,7 @@ declare void @llvm.init.trampoline(ptr, ptr, ptr)
 define void @test_load_volatile(ptr %Q) {
 ; CHECK-LABEL: @test_load_volatile(
 ; CHECK-NEXT:    [[A:%.*]] = load volatile i32, ptr [[Q:%.*]], align 4
+; CHECK-NEXT:    store i32 [[A]], ptr [[Q]], align 4
 ; CHECK-NEXT:    ret void
 ;
   %a = load volatile i32, ptr %Q
@@ -38,9 +39,12 @@ define void @test_store_volatile(ptr %Q) {
 ; PR2599 - load -> store to same address.
 define void @test12(ptr %x) nounwind  {
 ; CHECK-LABEL: @test12(
-; CHECK-NEXT:    [[TEMP7:%.*]] = getelementptr { i32, i32 }, ptr [[X:%.*]], i32 0, i32 1
+; CHECK-NEXT:    [[TEMP4:%.*]] = getelementptr { i32, i32 }, ptr [[X:%.*]], i32 0, i32 0
+; CHECK-NEXT:    [[TEMP5:%.*]] = load i32, ptr [[TEMP4]], align 4
+; CHECK-NEXT:    [[TEMP7:%.*]] = getelementptr { i32, i32 }, ptr [[X]], i32 0, i32 1
 ; CHECK-NEXT:    [[TEMP8:%.*]] = load i32, ptr [[TEMP7]], align 4
 ; CHECK-NEXT:    [[TEMP17:%.*]] = sub i32 0, [[TEMP8]]
+; CHECK-NEXT:    store i32 [[TEMP5]], ptr [[TEMP4]], align 4
 ; CHECK-NEXT:    store i32 [[TEMP17]], ptr [[TEMP7]], align 4
 ; CHECK-NEXT:    ret void
 ;
@@ -58,10 +62,12 @@ define void @test12(ptr %x) nounwind  {
 define i32 @test26(i1 %c, ptr %p) {
 ; CHECK-LABEL: @test26(
 ; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[V:%.*]] = load i32, ptr [[P:%.*]], align 4
 ; CHECK-NEXT:    br i1 [[C:%.*]], label [[BB1:%.*]], label [[BB2:%.*]]
 ; CHECK:       bb1:
 ; CHECK-NEXT:    br label [[BB3:%.*]]
 ; CHECK:       bb2:
+; CHECK-NEXT:    store i32 [[V]], ptr [[P]], align 4
 ; CHECK-NEXT:    br label [[BB3]]
 ; CHECK:       bb3:
 ; CHECK-NEXT:    ret i32 0
@@ -82,12 +88,14 @@ bb3:
 define i32 @test27(i1 %c, ptr %p) {
 ; CHECK-LABEL: @test27(
 ; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[V:%.*]] = load i32, ptr [[P:%.*]], align 4
 ; CHECK-NEXT:    br i1 [[C:%.*]], label [[BB1:%.*]], label [[BB2:%.*]]
 ; CHECK:       bb1:
 ; CHECK-NEXT:    br label [[BB3:%.*]]
 ; CHECK:       bb2:
 ; CHECK-NEXT:    br label [[BB3]]
 ; CHECK:       bb3:
+; CHECK-NEXT:    store i32 [[V]], ptr [[P]], align 4
 ; CHECK-NEXT:    ret i32 0
 ;
 entry:
@@ -106,8 +114,10 @@ bb3:
 define i32 @test31(i1 %c, ptr %p, i32 %i) {
 ; CHECK-LABEL: @test31(
 ; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[V:%.*]] = load i32, ptr [[P:%.*]], align 4
 ; CHECK-NEXT:    br label [[BB1:%.*]]
 ; CHECK:       bb1:
+; CHECK-NEXT:    store i32 [[V]], ptr [[P]], align 4
 ; CHECK-NEXT:    br i1 [[C:%.*]], label [[BB1]], label [[BB2:%.*]]
 ; CHECK:       bb2:
 ; CHECK-NEXT:    ret i32 0
@@ -158,8 +168,10 @@ define i32 @test33(i1 %c, ptr %p, i32 %i) {
 ; CHECK-NEXT:  entry:
 ; CHECK-NEXT:    br label [[BB1:%.*]]
 ; CHECK:       bb1:
+; CHECK-NEXT:    [[V:%.*]] = load i32, ptr [[P:%.*]], align 4
 ; CHECK-NEXT:    br label [[BB2:%.*]]
 ; CHECK:       bb2:
+; CHECK-NEXT:    store i32 [[V]], ptr [[P]], align 4
 ; CHECK-NEXT:    call void @unknown_func()
 ; CHECK-NEXT:    br i1 [[C:%.*]], label [[BB1]], label [[BB3:%.*]]
 ; CHECK:       bb3:
@@ -198,7 +210,9 @@ define void @test43(ptr %Q) {
 ; We CAN remove it when the unkown write comes AFTER.
 define void @test44(ptr %Q) {
 ; CHECK-LABEL: @test44(
-; CHECK-NEXT:    call void @unkown_write(ptr [[Q:%.*]])
+; CHECK-NEXT:    [[A:%.*]] = load i32, ptr [[Q:%.*]], align 4
+; CHECK-NEXT:    store i32 [[A]], ptr [[Q]], align 4
+; CHECK-NEXT:    call void @unkown_write(ptr [[Q]])
 ; CHECK-NEXT:    ret void
 ;
   %a = load i32, ptr %Q
@@ -209,6 +223,8 @@ define void @test44(ptr %Q) {
 
 define void @test45(ptr %Q) {
 ; CHECK-LABEL: @test45(
+; CHECK-NEXT:    [[A:%.*]] = load i32, ptr [[Q:%.*]], align 4
+; CHECK-NEXT:    store i32 [[A]], ptr [[Q]], align 4
 ; CHECK-NEXT:    ret void
 ;
   %a = load i32, ptr %Q
@@ -254,10 +270,12 @@ bb2:
 define i32 @test47(i1 %c, ptr %p, i32 %i) {
 ; CHECK-LABEL: @test47(
 ; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[V:%.*]] = load i32, ptr [[P:%.*]], align 4
 ; CHECK-NEXT:    br label [[BB1:%.*]]
 ; CHECK:       bb1:
 ; CHECK-NEXT:    br i1 [[C:%.*]], label [[BB1]], label [[BB2:%.*]]
 ; CHECK:       bb2:
+; CHECK-NEXT:    store i32 [[V]], ptr [[P]], align 4
 ; CHECK-NEXT:    br i1 [[C]], label [[BB3:%.*]], label [[BB1]]
 ; CHECK:       bb3:
 ; CHECK-NEXT:    ret i32 0
@@ -279,7 +297,9 @@ bb3:
 define void @test_noalias_store_between_load_and_store(ptr noalias %x, ptr noalias %y) {
 ; CHECK-LABEL: @test_noalias_store_between_load_and_store(
 ; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[LV:%.*]] = load i32, ptr [[X:%.*]], align 4
 ; CHECK-NEXT:    store i32 0, ptr [[Y:%.*]], align 4
+; CHECK-NEXT:    store i32 [[LV]], ptr [[X]], align 4
 ; CHECK-NEXT:    ret void
 ;
 entry:
@@ -295,7 +315,9 @@ entry:
 define void @test_noalias_store_between_load_and_store_elimin_order(ptr noalias %x, ptr noalias %y) {
 ; CHECK-LABEL: @test_noalias_store_between_load_and_store_elimin_order(
 ; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[LV:%.*]] = load i32, ptr [[X:%.*]], align 4
 ; CHECK-NEXT:    store i32 0, ptr [[Y:%.*]], align 4
+; CHECK-NEXT:    store i32 [[LV]], ptr [[X]], align 4
 ; CHECK-NEXT:    ret void
 ;
 entry:
@@ -795,3 +817,65 @@ join:
   store i8 %v, ptr %q, align 1
   ret void
 }
+
+; If the condition ensures that the value is already present in
+; in the variable, treat the store as noop and optimize. Should
+; not optimize stores if a clobber happens after the load that
+; is used in condition check.
+define void @remove_tautological_store(ptr %x, ptr %y, ptr %z) {
+; CHECK-LABEL: @remove_tautological_store(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[VALX:%.*]] = load i32, ptr [[X:%.*]], align 4
+; CHECK-NEXT:    [[CMPX:%.*]] = icmp eq i32 [[VALX]], 4
+; CHECK-NEXT:    br i1 [[CMPX]], label [[IF_X:%.*]], label [[END:%.*]]
+; CHECK:       ty:
+; CHECK-NEXT:    [[VALY:%.*]] = load i32, ptr [[Y:%.*]], align 4
+; CHECK-NEXT:    store i32 5, ptr [[Y]], align 4
+; CHECK-NEXT:    [[CMPY:%.*]] = icmp eq i32 [[VALY]], 4
+; CHECK-NEXT:    br i1 [[CMPY]], label [[IF_Y:%.*]], label [[END]]
+; CHECK:       tz:
+; CHECK-NEXT:    [[VALZ:%.*]] = load i32, ptr [[Z:%.*]], align 4
+; CHECK-NEXT:    [[CMPZ:%.*]] = icmp eq i32 [[VALZ]], 4
+; CHECK-NEXT:    br i1 [[CMPZ]], label [[IF_Z:%.*]], label [[END]]
+; CHECK:       if.x:
+; CHECK-NEXT:    br label [[TY:%.*]]
+; CHECK:       if.y:
+; CHECK-NEXT:    store i32 4, ptr [[Y]], align 4
+; CHECK-NEXT:    br label [[TZ:%.*]]
+; CHECK:       if.z:
+; CHECK-NEXT:    store i32 5, ptr [[Z]], align 4
+; CHECK-NEXT:    br label [[END]]
+; CHECK:       end:
+; CHECK-NEXT:    ret void
+;
+entry:
+  %valx = load i32, ptr %x, align 4
+  %cmpx = icmp eq i32 %valx, 4
+  br i1 %cmpx, label %if.x, label %end
+
+ty:
+  %valy = load i32, ptr %y, align 4
+  store i32 5, ptr %y, align 4
+  %cmpy = icmp eq i32 %valy, 4
+  br i1 %cmpy, label %if.y, label %end
+
+tz:
+  %valz = load i32, ptr %z, align 4
+  %cmpz = icmp eq i32 %valz, 4
+  br i1 %cmpz, label %if.z, label %end
+
+if.x:
+  store i32 4, ptr %x, align 4
+  br label %ty
+
+if.y:
+  store i32 4, ptr %y, align 4
+  br label %tz
+
+if.z:
+  store i32 5, ptr %z, align 4
+  br label %end
+
+end:
+  ret void
+}



More information about the llvm-commits mailing list