[llvm] cd84489 - [Loads] Only allow replacement with derefable constant global (#217350)

via llvm-commits llvm-commits at lists.llvm.org
Thu Aug 20 05:31:38 PDT 2026


Author: Nikita Popov
Date: 2026-08-20T14:31:33+02:00
New Revision: cd8448925c6dab8d2b0919216d293f36e68dbf08

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

LOG: [Loads] Only allow replacement with derefable constant global (#217350)

isPointerAlwaysReplaceable() currently allows replacing with Constants
that are dereferenceable for at least one byte. This patch further
restricts this carveout to cases where the Constant is based on a
constant global.

This fixes the case from #59679. In that case, the replacement is
between a noalias pointer and a dereferenceable global. After the
replacement, the noalias pointer is assumed to no longer alias with the
replaced global write, resulting in a miscompile.

The remaining carveout for constant globals is still not correct, but I
think there's less issues we can encounter in the read-only case, and we
have to support this for vtable assumptions to work.

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

Added: 
    llvm/test/Transforms/GVN/noalias-pointer-global-replacement.ll

Modified: 
    llvm/lib/Analysis/Loads.cpp
    llvm/test/Transforms/SCCP/replace-dereferenceable-ptr-with-undereferenceable.ll
    llvm/unittests/Analysis/LoadsTest.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Analysis/Loads.cpp b/llvm/lib/Analysis/Loads.cpp
index 84dcbbaa4b6dd..ab8e48347c889 100644
--- a/llvm/lib/Analysis/Loads.cpp
+++ b/llvm/lib/Analysis/Loads.cpp
@@ -852,8 +852,15 @@ static bool isPointerAlwaysReplaceable(const Value *From, const Value *To,
   if (isa<ConstantPointerNull>(From) &&
       From->getType()->getPointerAddressSpace() == 0)
     return true;
+  // Allow replacement with dereferenceable constants. This is not strictly
+  // correct, but required for vtable assumptions.
+  auto IsBasedOnConstantGlobal = [](const Value *V) {
+    auto *GV = dyn_cast<GlobalVariable>(getUnderlyingObject(V));
+    return GV && GV->isConstant();
+  };
   if (isa<Constant>(To) && To->getType()->isPointerTy() &&
-      isDereferenceablePointer(To, Type::getInt8Ty(To->getContext()), DL))
+      isDereferenceablePointer(To, Type::getInt8Ty(To->getContext()), DL) &&
+      IsBasedOnConstantGlobal(To))
     return true;
   return getUnderlyingObjectAggressive(From) ==
          getUnderlyingObjectAggressive(To);

diff  --git a/llvm/test/Transforms/GVN/noalias-pointer-global-replacement.ll b/llvm/test/Transforms/GVN/noalias-pointer-global-replacement.ll
new file mode 100644
index 0000000000000..f1bda58d357b3
--- /dev/null
+++ b/llvm/test/Transforms/GVN/noalias-pointer-global-replacement.ll
@@ -0,0 +1,36 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -S -passes=gvn < %s | FileCheck %s
+
+; https://github.com/llvm/llvm-project/issues/59679
+; Make sure we don't replace the %ptr in the if.then branch with @x and then
+; conclude that it can't alias with the read from the noalias pointer %ptr.
+
+ at x = global i32 0
+
+define i32 @test(ptr noalias %ptr) {
+; CHECK-LABEL: define i32 @test(
+; CHECK-SAME: ptr noalias [[PTR:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    store i32 1, ptr [[PTR]], align 4
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq ptr [[PTR]], @x
+; CHECK-NEXT:    br i1 [[CMP]], label %[[IF_THEN:.*]], label %[[IF_END:.*]]
+; CHECK:       [[IF_THEN]]:
+; CHECK-NEXT:    store i32 2, ptr [[PTR]], align 4
+; CHECK-NEXT:    br label %[[IF_END]]
+; CHECK:       [[IF_END]]:
+; CHECK-NEXT:    [[V:%.*]] = phi i32 [ 2, %[[IF_THEN]] ], [ 1, %[[ENTRY]] ]
+; CHECK-NEXT:    ret i32 [[V]]
+;
+entry:
+  store i32 1, ptr %ptr
+  %cmp = icmp eq ptr %ptr, @x
+  br i1 %cmp, label %if.then, label %if.end
+
+if.then:
+  store i32 2, ptr %ptr
+  br label %if.end
+
+if.end:
+  %v = load i32, ptr %ptr
+  ret i32 %v
+}

diff  --git a/llvm/test/Transforms/SCCP/replace-dereferenceable-ptr-with-undereferenceable.ll b/llvm/test/Transforms/SCCP/replace-dereferenceable-ptr-with-undereferenceable.ll
index f336fcbe70c77..de327a22589df 100644
--- a/llvm/test/Transforms/SCCP/replace-dereferenceable-ptr-with-undereferenceable.ll
+++ b/llvm/test/Transforms/SCCP/replace-dereferenceable-ptr-with-undereferenceable.ll
@@ -3,6 +3,9 @@
 
 @y = common global [1 x i32] zeroinitializer, align 4
 @x = common global [1 x i32] zeroinitializer, align 4
+ at c = constant i32 42
+
+declare void @use(ptr)
 
 define i32 @eq_undereferenceable(ptr %p) {
 ; CHECK-LABEL: @eq_undereferenceable(
@@ -32,14 +35,14 @@ if.end:                                           ; preds = %if.then, %entry
 }
 
 
-define i32 @eq_dereferenceable(ptr %p) {
-; CHECK-LABEL: @eq_dereferenceable(
+define i32 @eq_dereferenceable_not_constant(ptr %p) {
+; CHECK-LABEL: @eq_dereferenceable_not_constant(
 ; CHECK-NEXT:  entry:
 ; CHECK-NEXT:    store i32 1, ptr @y, align 4
 ; CHECK-NEXT:    [[CMP:%.*]] = icmp eq ptr [[P:%.*]], @x
 ; CHECK-NEXT:    br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_END:%.*]]
 ; CHECK:       if.then:
-; CHECK-NEXT:    store i32 2, ptr @x, align 4
+; CHECK-NEXT:    store i32 2, ptr [[P]], align 4
 ; CHECK-NEXT:    br label [[IF_END]]
 ; CHECK:       if.end:
 ; CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr @y, align 4
@@ -59,6 +62,32 @@ if.end:                                           ; preds = %if.then, %entry
   ret i32 %0
 }
 
+define i32 @eq_dereferenceable_constant(ptr %p) {
+; CHECK-LABEL: @eq_dereferenceable_constant(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    store i32 1, ptr @y, align 4
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq ptr [[P:%.*]], @c
+; CHECK-NEXT:    br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_END:%.*]]
+; CHECK:       if.then:
+; CHECK-NEXT:    call void @use(ptr @c)
+; CHECK-NEXT:    ret i32 42
+; CHECK:       if.end:
+; CHECK-NEXT:    ret i32 0
+;
+entry:
+  store i32 1, ptr @y, align 4
+  %cmp = icmp eq ptr %p, @c
+  br i1 %cmp, label %if.then, label %if.end
+
+if.then:
+  call void @use(ptr %p)
+  %v = load i32, ptr %p, align 4
+  ret i32 %v
+
+if.end:
+  ret i32 0
+}
+
 define i1 @eq_undereferenceable_cmp_simp(ptr %p) {
 ; CHECK-LABEL: @eq_undereferenceable_cmp_simp(
 ; CHECK-NEXT:  entry:

diff  --git a/llvm/unittests/Analysis/LoadsTest.cpp b/llvm/unittests/Analysis/LoadsTest.cpp
index e3ee4c8852c84..8b15bda08485e 100644
--- a/llvm/unittests/Analysis/LoadsTest.cpp
+++ b/llvm/unittests/Analysis/LoadsTest.cpp
@@ -100,8 +100,8 @@ TEST(LoadsTest, CanReplacePointersIfEqual) {
   LLVMContext C;
   std::unique_ptr<Module> M = parseIR(C,
                                       R"IR(
- at y = common global [1 x i32] zeroinitializer, align 4
- at x = common global [1 x i32] zeroinitializer, align 4
+ at y = constant [1 x i32] zeroinitializer, align 4
+ at x = constant [1 x i32] zeroinitializer, align 4
 declare void @use(ptr)
 
 define void @f(ptr %p1, ptr %p2, i64 %i, ptr addrspace(1) %p1as1) {


        


More information about the llvm-commits mailing list