[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