[llvm] [GlobalOpt] Remove all stores to GV (PR #84694)

Anshil Gandhi via llvm-commits llvm-commits at lists.llvm.org
Sun Mar 10 14:21:56 PDT 2024


https://github.com/gandhi56 created https://github.com/llvm/llvm-project/pull/84694

When CleanupPointerRootUsers is called on GV,
the assumption is that there is no load from GV.
Erase all stores to GV in this case.

Fixes https://github.com/llvm/llvm-project/issues/64680.

>From eaa260252a2299e657e207cfda10af5903d6c5a9 Mon Sep 17 00:00:00 2001
From: Anshil Gandhi <gandhi21299 at gmail.com>
Date: Sun, 10 Mar 2024 14:29:37 -0600
Subject: [PATCH] [GlobalOpt] Remove all stores to GV

When CleanupPointerRootUsers is called on GV,
the assumption is that there is no load from GV.
Erase all stores to GV in this case.

Fixes https://github.com/llvm/llvm-project/issues/64680.
---
 llvm/lib/Transforms/IPO/GlobalOpt.cpp         | 10 ++-
 .../GlobalOpt/cleanup-pointer-root-stores.ll  | 62 +++++++++++++++++++
 .../Transforms/GlobalOpt/dead-store-status.ll |  2 -
 3 files changed, 71 insertions(+), 3 deletions(-)
 create mode 100644 llvm/test/Transforms/GlobalOpt/cleanup-pointer-root-stores.ll

diff --git a/llvm/lib/Transforms/IPO/GlobalOpt.cpp b/llvm/lib/Transforms/IPO/GlobalOpt.cpp
index da714c9a75701b..2b3154eeb6100f 100644
--- a/llvm/lib/Transforms/IPO/GlobalOpt.cpp
+++ b/llvm/lib/Transforms/IPO/GlobalOpt.cpp
@@ -204,8 +204,9 @@ CleanupPointerRootUsers(GlobalVariable *GV,
   // If Dead[n].first is the only use of a malloc result, we can delete its
   // chain of computation and the store to the global in Dead[n].second.
   SmallVector<std::pair<Instruction *, Instruction *>, 32> Dead;
-
+  SmallVector<StoreInst *> StoresToGV;
   SmallVector<User *> Worklist(GV->users());
+
   // Constants can't be pointers to dynamically allocated memory.
   while (!Worklist.empty()) {
     User *U = Worklist.pop_back_val();
@@ -217,6 +218,8 @@ CleanupPointerRootUsers(GlobalVariable *GV,
       } else if (Instruction *I = dyn_cast<Instruction>(V)) {
         if (I->hasOneUse())
           Dead.push_back(std::make_pair(I, SI));
+        else
+          StoresToGV.push_back(SI);
       }
     } else if (MemSetInst *MSI = dyn_cast<MemSetInst>(U)) {
       if (isa<Constant>(MSI->getValue())) {
@@ -241,6 +244,11 @@ CleanupPointerRootUsers(GlobalVariable *GV,
     }
   }
 
+  // We assert here that GV is never load from so
+  // we can safely remove all stores to GV.
+  for (StoreInst *SI : StoresToGV)
+    SI->eraseFromParent();
+
   for (int i = 0, e = Dead.size(); i != e; ++i) {
     if (IsSafeComputationToRemove(Dead[i].first, GetTLI)) {
       Dead[i].second->eraseFromParent();
diff --git a/llvm/test/Transforms/GlobalOpt/cleanup-pointer-root-stores.ll b/llvm/test/Transforms/GlobalOpt/cleanup-pointer-root-stores.ll
new file mode 100644
index 00000000000000..980fb6f5dc8bfe
--- /dev/null
+++ b/llvm/test/Transforms/GlobalOpt/cleanup-pointer-root-stores.ll
@@ -0,0 +1,62 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4
+; RUN: opt -passes=globalopt < %s -S | FileCheck %s
+
+ at a = internal unnamed_addr global i32 0, align 4
+ at b = internal unnamed_addr global [3 x ptr] zeroinitializer, align 16
+
+define i32 @main() local_unnamed_addr {
+; CHECK-LABEL: define i32 @main() local_unnamed_addr {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[E:%.*]] = alloca i32, align 4
+; CHECK-NEXT:    [[DOTPR:%.*]] = load i32, ptr @a, align 4
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp slt i32 [[DOTPR]], 3
+; CHECK-NEXT:    br i1 [[CMP1]], label [[FOR_BODY:%.*]], label [[FOR_END:%.*]]
+; CHECK:       for.body:
+; CHECK-NEXT:    store i32 8, ptr [[E]], align 4
+; CHECK-NEXT:    call void @bar20_()
+; CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[E]], align 4
+; CHECK-NEXT:    [[TOBOOL_NOT:%.*]] = icmp eq i32 [[TMP0]], 0
+; CHECK-NEXT:    br i1 [[TOBOOL_NOT]], label [[IF_THEN:%.*]], label [[IF_END:%.*]]
+; CHECK:       if.then:
+; CHECK-NEXT:    call void @foo()
+; CHECK-NEXT:    br label [[IF_END]]
+; CHECK:       if.end:
+; CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr @a, align 4
+; CHECK-NEXT:    [[INC:%.*]] = add nsw i32 [[TMP1]], 1
+; CHECK-NEXT:    store i32 [[INC]], ptr @a, align 4
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i32 [[TMP1]], 2
+; CHECK-NEXT:    br i1 [[CMP]], label [[FOR_BODY]], label [[FOR_END]]
+; CHECK:       for.end:
+; CHECK-NEXT:    ret i32 0
+;
+entry:
+  %e = alloca i32, align 4
+  %.pr = load i32, ptr @a, align 4
+  %cmp1 = icmp slt i32 %.pr, 3
+  br i1 %cmp1, label %for.body, label %for.end
+
+for.body:                                         ; preds = %entry, %if.end
+  store i32 8, ptr %e, align 4
+  call void @bar20_()
+  %0 = load i32, ptr %e, align 4
+  %tobool.not = icmp eq i32 %0, 0
+  br i1 %tobool.not, label %if.then, label %if.end
+
+if.then:                                          ; preds = %for.body
+  call void @foo()
+  br label %if.end
+
+if.end:                                           ; preds = %if.then, %for.body
+  store ptr %e, ptr getelementptr inbounds ([3 x ptr], ptr @b, i64 0, i64 2), align 16
+  %1 = load i32, ptr @a, align 4
+  %inc = add nsw i32 %1, 1
+  store i32 %inc, ptr @a, align 4
+  %cmp = icmp slt i32 %1, 2
+  br i1 %cmp, label %for.body, label %for.end
+
+for.end:                                          ; preds = %if.end, %entry
+  ret i32 0
+}
+
+declare void @bar20_() local_unnamed_addr
+declare void @foo() local_unnamed_addr
diff --git a/llvm/test/Transforms/GlobalOpt/dead-store-status.ll b/llvm/test/Transforms/GlobalOpt/dead-store-status.ll
index 9a8fbb8d65f0e0..597c08929af902 100644
--- a/llvm/test/Transforms/GlobalOpt/dead-store-status.ll
+++ b/llvm/test/Transforms/GlobalOpt/dead-store-status.ll
@@ -4,8 +4,6 @@
 ; false. This was caught by the pass return status check that is hidden under
 ; EXPENSIVE_CHECKS.
 
-; CHECK: @global = internal unnamed_addr global ptr null, align 1
-
 ; CHECK-LABEL: @foo
 ; CHECK-NEXT: entry:
 ; CHECK-NEXT: ret i16 undef



More information about the llvm-commits mailing list