[llvm] fed817a - [DSE] Consider the aliasing through global variable while checking clobber (#120044)

via llvm-commits llvm-commits at lists.llvm.org
Tue Jan 14 10:04:47 PST 2025


Author: Haopeng Liu
Date: 2025-01-14T10:04:41-08:00
New Revision: fed817a8b25e178cd701fefcdfe80c447d2ab212

URL: https://github.com/llvm/llvm-project/commit/fed817a8b25e178cd701fefcdfe80c447d2ab212
DIFF: https://github.com/llvm/llvm-project/commit/fed817a8b25e178cd701fefcdfe80c447d2ab212.diff

LOG: [DSE] Consider the aliasing through global variable while checking clobber (#120044)

While update the read clobber check for the "initializes" attr, we
checked the aliasing among arguments, but didn't consider the aliasing
through global variable. It causes problems in this example:

```
int g_var = 123;

void update(int* ptr) {
  *ptr = g_var;

void foo() {
  g_var = 0;
  bar(&g_var);
}
```
We mistakenly removed `g_var = 0;` as a dead store.

Fix the issue by requiring the CallBase only access argmem or
inaccessiblemem.

Added: 
    

Modified: 
    llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp
    llvm/test/Transforms/DeadStoreElimination/inter-procedural.ll

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp b/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp
index cae5b9c41a37f1..241c57e88b447e 100644
--- a/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp
+++ b/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp
@@ -2262,6 +2262,14 @@ struct DSEState {
   bool eliminateDeadDefs(const MemoryDefWrapper &KillingDefWrapper);
 };
 
+// Return true if "Arg" is function local and isn't captured before "CB".
+bool isFuncLocalAndNotCaptured(Value *Arg, const CallBase *CB,
+                               EarliestEscapeAnalysis &EA) {
+  const Value *UnderlyingObj = getUnderlyingObject(Arg);
+  return isIdentifiedFunctionLocal(UnderlyingObj) &&
+         EA.isNotCapturedBefore(UnderlyingObj, CB, /*OrAt*/ true);
+}
+
 SmallVector<MemoryLocation, 1>
 DSEState::getInitializesArgMemLoc(const Instruction *I) {
   const CallBase *CB = dyn_cast<CallBase>(I);
@@ -2277,6 +2285,13 @@ DSEState::getInitializesArgMemLoc(const Instruction *I) {
       Inits = InitializesAttr.getValueAsConstantRangeList();
 
     Value *CurArg = CB->getArgOperand(Idx);
+    // Check whether "CurArg" could alias with global variables. We require
+    // either it's function local and isn't captured before or the "CB" only
+    // accesses arg or inaccessible mem.
+    if (!Inits.empty() && !CB->onlyAccessesInaccessibleMemOrArgMem() &&
+        !isFuncLocalAndNotCaptured(CurArg, CB, EA))
+      Inits = ConstantRangeList();
+
     // We don't perform incorrect DSE on unwind edges in the current function,
     // and use the "initializes" attribute to kill dead stores if:
     // - The call does not throw exceptions, "CB->doesNotThrow()".

diff  --git a/llvm/test/Transforms/DeadStoreElimination/inter-procedural.ll b/llvm/test/Transforms/DeadStoreElimination/inter-procedural.ll
index d93da9b6612b05..e590c5bf4004af 100644
--- a/llvm/test/Transforms/DeadStoreElimination/inter-procedural.ll
+++ b/llvm/test/Transforms/DeadStoreElimination/inter-procedural.ll
@@ -39,6 +39,25 @@ define i16 @p1_write_then_read_caller() {
   ret i16 %l
 }
 
+declare void @fn_capture(ptr)
+define i16 @p1_write_then_read_caller_escape() {
+; CHECK-LABEL: @p1_write_then_read_caller_escape(
+; CHECK-NEXT:    [[PTR:%.*]] = alloca i16, align 2
+; CHECK-NEXT:    store i16 0, ptr [[PTR]], align 2
+; CHECK-NEXT:    call void @fn_capture(ptr [[PTR]])
+; CHECK-NEXT:    call void @p1_write_then_read(ptr [[PTR]])
+; CHECK-NEXT:    [[L:%.*]] = load i16, ptr [[PTR]], align 2
+; CHECK-NEXT:    ret i16 [[L]]
+;
+  %ptr = alloca i16
+  store i16 0, ptr %ptr
+  call void @fn_capture(ptr %ptr)
+  call void @p1_write_then_read(ptr %ptr)
+  %l = load i16, ptr %ptr
+  ret i16 %l
+}
+
+
 ; Function Attrs: mustprogress nounwind uwtable
 define i16 @p1_write_then_read_caller_with_clobber() {
 ; CHECK-LABEL: @p1_write_then_read_caller_with_clobber(
@@ -221,15 +240,17 @@ declare void @large_p2(ptr nocapture noundef initializes((0, 200)), ptr nocaptur
 ; Function Attrs: mustprogress nounwind uwtable
 define i16 @large_p1_caller() {
 ; CHECK-LABEL: @large_p1_caller(
-; CHECK-NEXT:    [[PTR:%.*]] = alloca [200 x i8], align 1
-; CHECK-NEXT:    call void @large_p1(ptr [[PTR]])
-; CHECK-NEXT:    [[L:%.*]] = load i16, ptr [[PTR]], align 2
+; CHECK-NEXT:    [[PTR:%.*]] = alloca [300 x i8], align 1
+; CHECK-NEXT:    [[TMP:%.*]] = getelementptr i8, ptr [[PTR]], i64 100
+; CHECK-NEXT:    call void @large_p1(ptr [[TMP]])
+; CHECK-NEXT:    [[L:%.*]] = load i16, ptr [[TMP]], align 2
 ; CHECK-NEXT:    ret i16 [[L]]
 ;
-  %ptr = alloca [200 x i8]
-  call void @llvm.memset.p0.i64(ptr %ptr, i8 42, i64 100, i1 false)
-  call void @large_p1(ptr %ptr)
-  %l = load i16, ptr %ptr
+  %ptr = alloca [300 x i8]
+  %tmp = getelementptr i8, ptr %ptr, i64 100
+  call void @llvm.memset.p0.i64(ptr %tmp, i8 42, i64 100, i1 false)
+  call void @large_p1(ptr %tmp)
+  %l = load i16, ptr %tmp
   ret i16 %l
 }
 
@@ -299,3 +320,21 @@ define i16 @large_p2_may_or_partial_alias_caller2(ptr %base1, ptr %base2) {
   ret i16 %l
 }
 
+ at g = global i16 123, align 2
+
+declare void @read_global(ptr nocapture noundef initializes((0, 2))) nounwind
+  memory(read, argmem: write, inaccessiblemem: none) nounwind
+
+define i16 @global_var_alias() {
+; CHECK-LABEL: @global_var_alias(
+; CHECK-NEXT:    store i16 0, ptr @g, align 4
+; CHECK-NEXT:    call void @read_global(ptr @g)
+; CHECK-NEXT:    [[L:%.*]] = load i16, ptr @g, align 2
+; CHECK-NEXT:    ret i16 [[L]]
+;
+  store i16 0, ptr @g, align 4
+  call void @read_global(ptr @g)
+  %l = load i16, ptr @g
+  ret i16 %l
+}
+


        


More information about the llvm-commits mailing list