[llvm] [SCCP] Consider provenance when propagating constant ptrs (PR #160083)

Antonio Frighetto via llvm-commits llvm-commits at lists.llvm.org
Tue Dec 16 07:18:40 PST 2025


https://github.com/antoniofrighetto updated https://github.com/llvm/llvm-project/pull/160083

>From f40e7b25be66f31f64fa7426a24274d00fb5dda1 Mon Sep 17 00:00:00 2001
From: Antonio Frighetto <me at antoniofrighetto.com>
Date: Mon, 22 Sep 2025 12:02:41 +0200
Subject: [PATCH] [ValueLattice][SCCP] Consider provenance for
 predicate-derived pointer constants

Similarly to what it is being already done in GVN (fb632ed2377d280b581b8d4653b855e60d611f77),
make sure pointers equalities derived via PredicatedInfo may be
propagated so long as pointer constants' provenance is taken
into account, which may be non-existent. This is achieved by
tracking this in ValueLatticeElement and restrain replacement
to uses allowed by `canReplacePointersInUseIfEqual`.

Fixes: https://github.com/llvm/llvm-project/issues/159565.
---
 llvm/include/llvm/Analysis/ValueLattice.h     | 14 ++-
 llvm/lib/Analysis/Loads.cpp                   |  4 +-
 llvm/lib/Transforms/Utils/SCCPSolver.cpp      | 30 ++++++-
 .../SCCP/assume-equality-pointers.ll          | 86 +++++++++++++++++++
 ...eferenceable-ptr-with-undereferenceable.ll |  4 +-
 5 files changed, 130 insertions(+), 8 deletions(-)
 create mode 100644 llvm/test/Transforms/SCCP/assume-equality-pointers.ll

diff --git a/llvm/include/llvm/Analysis/ValueLattice.h b/llvm/include/llvm/Analysis/ValueLattice.h
index 262ff58f07dfd..560bcbabe09cf 100644
--- a/llvm/include/llvm/Analysis/ValueLattice.h
+++ b/llvm/include/llvm/Analysis/ValueLattice.h
@@ -80,6 +80,11 @@ class ValueLatticeElement {
   /// Number of times a constant range has been extended with widening enabled.
   unsigned NumRangeExtensions : 8;
 
+  // Pointer constants derived from equality predicates may have different
+  // provenance than the original value. Limit constant propagation if this
+  // happens to be the case.
+  bool MayHaveDifferentProvenance = false;
+
   /// The union either stores a pointer to a constant or a constant range,
   /// associated to the lattice element. We have to ensure that Range is
   /// initialized or destroyed when changing state to or from constantrange.
@@ -148,7 +153,8 @@ class ValueLatticeElement {
   ~ValueLatticeElement() { destroy(); }
 
   ValueLatticeElement(const ValueLatticeElement &Other)
-      : Tag(Other.Tag), NumRangeExtensions(0) {
+      : Tag(Other.Tag), NumRangeExtensions(0),
+        MayHaveDifferentProvenance(Other.MayHaveDifferentProvenance) {
     switch (Other.Tag) {
     case constantrange:
     case constantrange_including_undef:
@@ -167,7 +173,8 @@ class ValueLatticeElement {
   }
 
   ValueLatticeElement(ValueLatticeElement &&Other)
-      : Tag(Other.Tag), NumRangeExtensions(0) {
+      : Tag(Other.Tag), NumRangeExtensions(0),
+        MayHaveDifferentProvenance(Other.MayHaveDifferentProvenance) {
     switch (Other.Tag) {
     case constantrange:
     case constantrange_including_undef:
@@ -492,6 +499,9 @@ class ValueLatticeElement {
 
   unsigned getNumRangeExtensions() const { return NumRangeExtensions; }
   void setNumRangeExtensions(unsigned N) { NumRangeExtensions = N; }
+
+  bool mayHaveDifferentProvenance() const { return MayHaveDifferentProvenance; }
+  void setMayHaveDifferentProvenance(bool V) { MayHaveDifferentProvenance = V; }
 };
 
 static_assert(sizeof(ValueLatticeElement) <= 40,
diff --git a/llvm/lib/Analysis/Loads.cpp b/llvm/lib/Analysis/Loads.cpp
index 73f40f13a2623..529d541b6db43 100644
--- a/llvm/lib/Analysis/Loads.cpp
+++ b/llvm/lib/Analysis/Loads.cpp
@@ -803,7 +803,7 @@ Value *llvm::FindAvailableLoadedValue(LoadInst *Load, BatchAAResults &AA,
 
 // Returns true if a use is either in an ICmp/PtrToInt or a Phi/Select that only
 // feeds into them.
-static bool isPointerUseReplacable(const Use &U, bool HasNonAddressBits) {
+static bool isPointerUseReplaceable(const Use &U, bool HasNonAddressBits) {
   unsigned Limit = 40;
   SmallVector<const User *> Worklist({U.getUser()});
   SmallPtrSet<const User *, 8> Visited;
@@ -859,7 +859,7 @@ bool llvm::canReplacePointersInUseIfEqual(const Use &U, const Value *To,
 
   bool HasNonAddressBits =
       DL.getAddressSizeInBits(Ty) != DL.getPointerTypeSizeInBits(Ty);
-  return isPointerUseReplacable(U, HasNonAddressBits);
+  return isPointerUseReplaceable(U, HasNonAddressBits);
 }
 
 bool llvm::canReplacePointersIfEqual(const Value *From, const Value *To,
diff --git a/llvm/lib/Transforms/Utils/SCCPSolver.cpp b/llvm/lib/Transforms/Utils/SCCPSolver.cpp
index 021bf0618754a..be4b9b010963f 100644
--- a/llvm/lib/Transforms/Utils/SCCPSolver.cpp
+++ b/llvm/lib/Transforms/Utils/SCCPSolver.cpp
@@ -16,6 +16,7 @@
 #include "llvm/ADT/SetVector.h"
 #include "llvm/Analysis/ConstantFolding.h"
 #include "llvm/Analysis/InstructionSimplify.h"
+#include "llvm/Analysis/Loads.h"
 #include "llvm/Analysis/ValueLattice.h"
 #include "llvm/Analysis/ValueLatticeUtils.h"
 #include "llvm/Analysis/ValueTracking.h"
@@ -83,6 +84,28 @@ bool SCCPSolver::tryToReplaceWithConstant(Value *V) {
     return false;
   }
 
+  // For pointer constants derived from PredicateInfo, the constant may have
+  // different provenance. Take this into account during constant pointer
+  // propagation.
+  if (auto *I = dyn_cast<Instruction>(V); I && I->getType()->isPointerTy()) {
+    const auto &LV = getLatticeValueFor(V);
+    if (LV.mayHaveDifferentProvenance()) {
+      bool MadeChange = false;
+      const auto &DL = cast<Instruction>(V)->getDataLayout();
+
+      V->replaceUsesWithIf(Const, [&](Use &U) {
+        bool CanReplace = canReplacePointersInUseIfEqual(U, Const, DL);
+        if (CanReplace)
+          LLVM_DEBUG(dbgs() << "  Constant pointer: " << *Const << " = " << *V
+                            << '\n');
+
+        MadeChange |= CanReplace;
+        return CanReplace;
+      });
+      return MadeChange;
+    }
+  }
+
   LLVM_DEBUG(dbgs() << "  Constant: " << *Const << " = " << *V << '\n');
 
   // Replaces all of the uses of a variable with uses of the constant.
@@ -356,11 +379,12 @@ bool SCCPSolver::simplifyInstsInBlock(BasicBlock &BB,
     if (Inst.getType()->isVoidTy())
       continue;
     if (tryToReplaceWithConstant(&Inst)) {
-      if (wouldInstructionBeTriviallyDead(&Inst))
+      if (isInstructionTriviallyDead(&Inst)) {
         Inst.eraseFromParent();
+        ++InstRemovedStat;
+      }
 
       MadeChanges = true;
-      ++InstRemovedStat;
     } else if (replaceSignedInst(*this, InsertedValues, Inst)) {
       MadeChanges = true;
       ++InstReplacedStat;
@@ -2076,6 +2100,8 @@ void SCCPInstVisitor::handlePredicate(Instruction *I, Value *CopyOf,
     // For non-integer values or integer constant expressions, only
     // propagate equal constants or not-constants.
     addAdditionalUser(OtherOp, I);
+    if (CopyOf->getType()->isPointerTy())
+      CondVal.setMayHaveDifferentProvenance(true);
     mergeInValue(IV, I, CondVal);
     return;
   } else if (Pred == CmpInst::ICMP_NE && CondVal.isConstant()) {
diff --git a/llvm/test/Transforms/SCCP/assume-equality-pointers.ll b/llvm/test/Transforms/SCCP/assume-equality-pointers.ll
new file mode 100644
index 0000000000000..c2234685b7824
--- /dev/null
+++ b/llvm/test/Transforms/SCCP/assume-equality-pointers.ll
@@ -0,0 +1,86 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt < %s -passes=ipsccp -S | FileCheck %s
+
+define ptr @assume_pointers_equality_maybe_different_provenance_1(ptr %x) {
+; CHECK-LABEL: define ptr @assume_pointers_equality_maybe_different_provenance_1(
+; CHECK-SAME: ptr [[X:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq ptr [[X]], inttoptr (i64 12345678 to ptr)
+; CHECK-NEXT:    call void @llvm.assume(i1 [[CMP]])
+; CHECK-NEXT:    ret ptr [[X]]
+;
+entry:
+  %cmp = icmp eq ptr %x, inttoptr (i64 12345678 to ptr)
+  call void @llvm.assume(i1 %cmp)
+  ret ptr %x
+}
+
+define ptr @assume_pointers_equality_maybe_different_provenance_2(ptr %x, i1 %cond) {
+; CHECK-LABEL: define ptr @assume_pointers_equality_maybe_different_provenance_2(
+; CHECK-SAME: ptr [[X:%.*]], i1 [[COND:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq ptr [[X]], inttoptr (i64 12345678 to ptr)
+; CHECK-NEXT:    call void @llvm.assume(i1 [[CMP]])
+; CHECK-NEXT:    br i1 [[COND]], label %[[NEXT:.*]], label %[[EXIT:.*]]
+; CHECK:       [[NEXT]]:
+; CHECK-NEXT:    br label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    [[PHI:%.*]] = phi ptr [ [[X]], %[[ENTRY]] ], [ inttoptr (i64 12345678 to ptr), %[[NEXT]] ]
+; CHECK-NEXT:    ret ptr [[PHI]]
+;
+entry:
+  %cmp = icmp eq ptr %x, inttoptr (i64 12345678 to ptr)
+  call void @llvm.assume(i1 %cmp)
+  br i1 %cond, label %next, label %exit
+
+next:
+  br label %exit
+
+exit:
+  %phi = phi ptr [ %x, %entry ], [ inttoptr (i64 12345678 to ptr), %next ]
+  ret ptr %phi
+}
+
+define internal ptr @callee(ptr returned %p) memory(none) {
+; CHECK-LABEL: define internal ptr @callee(
+; CHECK-SAME: ptr [[P:%.*]]) #[[ATTR0:[0-9]+]] {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    ret ptr poison
+;
+entry:
+  ret ptr %p
+}
+
+define ptr @assume_pointers_equality_maybe_different_provenance_3(ptr %x, i1 %cond) {
+; CHECK-LABEL: define ptr @assume_pointers_equality_maybe_different_provenance_3(
+; CHECK-SAME: ptr [[X:%.*]], i1 [[COND:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq ptr [[X]], inttoptr (i64 12345678 to ptr)
+; CHECK-NEXT:    call void @llvm.assume(i1 [[CMP]])
+; CHECK-NEXT:    br i1 [[COND]], label %[[NEXT:.*]], label %[[EXIT:.*]]
+; CHECK:       [[NEXT]]:
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 true, ptr [[X]], ptr inttoptr (i64 12345678 to ptr)
+; CHECK-NEXT:    br label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    [[PHI:%.*]] = phi ptr [ [[X]], %[[ENTRY]] ], [ [[SEL]], %[[NEXT]] ]
+; CHECK-NEXT:    [[Q:%.*]] = call ptr @callee(ptr [[PHI]])
+; CHECK-NEXT:    [[SEL2:%.*]] = select i1 [[COND]], ptr [[Q]], ptr [[X]]
+; CHECK-NEXT:    ret ptr [[SEL2]]
+;
+entry:
+  %cmp = icmp eq ptr %x, inttoptr (i64 12345678 to ptr)
+  call void @llvm.assume(i1 %cmp)
+  br i1 %cond, label %next, label %exit
+
+next:
+  %sel = select i1 %cond, ptr %x, ptr inttoptr (i64 12345678 to ptr)
+  br label %exit
+
+exit:
+  %phi = phi ptr [ %x, %entry ], [ %sel, %next ]
+  %q = call ptr @callee(ptr %phi)
+  %sel2 = select i1 %cond, ptr %q, ptr %x
+  ret ptr %sel2
+}
+
+declare void @llvm.assume(i1)
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 39af513a4506c..f336fcbe70c77 100644
--- a/llvm/test/Transforms/SCCP/replace-dereferenceable-ptr-with-undereferenceable.ll
+++ b/llvm/test/Transforms/SCCP/replace-dereferenceable-ptr-with-undereferenceable.ll
@@ -11,7 +11,7 @@ define i32 @eq_undereferenceable(ptr %p) {
 ; CHECK-NEXT:    [[CMP:%.*]] = icmp eq ptr [[P:%.*]], getelementptr inbounds (i32, ptr @x, i64 1)
 ; CHECK-NEXT:    br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_END:%.*]]
 ; CHECK:       if.then:
-; CHECK-NEXT:    store i32 2, ptr getelementptr inbounds (i32, ptr @x, i64 1), 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
@@ -65,7 +65,7 @@ define i1 @eq_undereferenceable_cmp_simp(ptr %p) {
 ; CHECK-NEXT:    [[CMP_0:%.*]] = icmp eq ptr [[P:%.*]], getelementptr inbounds (i32, ptr @x, i64 1)
 ; CHECK-NEXT:    br i1 [[CMP_0]], label [[IF_THEN:%.*]], label [[IF_END:%.*]]
 ; CHECK:       if.then:
-; CHECK-NEXT:    store i32 2, ptr getelementptr inbounds (i32, ptr @x, i64 1), align 4
+; CHECK-NEXT:    store i32 2, ptr [[P]], align 4
 ; CHECK-NEXT:    ret i1 true
 ; CHECK:       if.end:
 ; CHECK-NEXT:    ret i1 false



More information about the llvm-commits mailing list