[llvm] [IR] Only follow ptr/ptr-vector sources in stripAndAccumulateConstantOffsets bitcasts (PR #203356)

Anshil Gandhi via llvm-commits llvm-commits at lists.llvm.org
Thu Jun 11 13:07:07 PDT 2026


https://github.com/gandhi56 updated https://github.com/llvm/llvm-project/pull/203356

>From 6aec6e5129006784ceef6d501c0d6e9dc686038d Mon Sep 17 00:00:00 2001
From: Anshil Gandhi <Anshil.Gandhi at amd.com>
Date: Thu, 11 Jun 2026 11:59:14 -0500
Subject: [PATCH] [IR] Only follow ptr (or ptr-vector) sources in
 stripAndAccumulateConstantOffsets bitcasts

stripAndAccumulateConstantOffsets used to walk through every bitcast operand.
If the operand was not a pointer (e.g. a same-sized byte type such as b64
reinterpreted as ptr), the walk could continue and violate the invariant that
the stripped value remains ptr or ptr-vector.

Align with stripPointerCastsAndOffsets: only look through bitcasts whose
source type is a pointer or pointer vector; keep addrspacecast as its own case.

Add unit tests for ptr-to-ptr bitcast (GEP offset accumulated through) and
for non-pointer-to-ptr bitcast (stop at the bitcast while still applying the
GEP offset).

This commit is needed in PR #177908.
---
 llvm/lib/IR/Value.cpp                  | 16 +++++----
 llvm/unittests/IR/InstructionsTest.cpp | 45 ++++++++++++++++++++++++++
 2 files changed, 55 insertions(+), 6 deletions(-)

diff --git a/llvm/lib/IR/Value.cpp b/llvm/lib/IR/Value.cpp
index 6f71a5093e4a3..fa74b7cbb353d 100644
--- a/llvm/lib/IR/Value.cpp
+++ b/llvm/lib/IR/Value.cpp
@@ -783,17 +783,21 @@ const Value *Value::stripAndAccumulateConstantOffsets(
         }
       }
       V = GEP->getPointerOperand();
-    } else if (Operator::getOpcode(V) == Instruction::BitCast ||
-               Operator::getOpcode(V) == Instruction::AddrSpaceCast) {
+    } else if (Operator::getOpcode(V) == Instruction::BitCast) {
+      const Value *Src = cast<Operator>(V)->getOperand(0);
+      if (!Src->getType()->isPtrOrPtrVectorTy())
+        return V;
+      V = Src;
+    } else if (Operator::getOpcode(V) == Instruction::AddrSpaceCast) {
       V = cast<Operator>(V)->getOperand(0);
     } else if (auto *GA = dyn_cast<GlobalAlias>(V)) {
       if (!GA->isInterposable())
         V = GA->getAliasee();
     } else if (const auto *Call = dyn_cast<CallBase>(V)) {
-        if (const Value *RV = Call->getReturnedArgOperand())
-          V = RV;
-        if (AllowInvariantGroup && Call->isLaunderOrStripInvariantGroup())
-          V = Call->getArgOperand(0);
+      if (const Value *RV = Call->getReturnedArgOperand())
+        V = RV;
+      if (AllowInvariantGroup && Call->isLaunderOrStripInvariantGroup())
+        V = Call->getArgOperand(0);
     } else if (auto *Int2Ptr = dyn_cast<Operator>(V)) {
       // Try to accumulate across (inttoptr (add (ptrtoint p), off)).
       if (!AllowNonInbounds || !LookThroughIntToPtr || !Int2Ptr ||
diff --git a/llvm/unittests/IR/InstructionsTest.cpp b/llvm/unittests/IR/InstructionsTest.cpp
index a1f22bbac02a1..4fd178a2504c2 100644
--- a/llvm/unittests/IR/InstructionsTest.cpp
+++ b/llvm/unittests/IR/InstructionsTest.cpp
@@ -2011,4 +2011,49 @@ TEST(InstructionsTest, StripAndAccumulateConstantOffset) {
   EXPECT_TRUE(Offset.isZero());
 }
 
+TEST(InstructionsTest, StripAndAccumulateConstantOffsets_ThroughPtrBitCast) {
+  LLVMContext C;
+  DataLayout DL("e-p:64:64:64-i64:64");
+  std::unique_ptr<Module> M = parseIR(C, R"(
+  define void @foo(ptr %p) {
+    %bc = bitcast ptr %p to ptr
+    %gep = getelementptr inbounds i8, ptr %bc, i64 2
+    ret void
+  })");
+  ASSERT_TRUE(M);
+  BasicBlock &BB = M->getFunction("foo")->getEntryBlock();
+  auto It = BB.begin();
+  Value *BC = &*It++;
+  (void)BC;
+  Value *GEP = &*It++;
+  EXPECT_EQ(isa<GetElementPtrInst>(GEP), true);
+  APInt Offset(DL.getIndexTypeSizeInBits(GEP->getType()), 0);
+  Value *Stripped = GEP->stripAndAccumulateConstantOffsets(
+      DL, Offset, /*AllowNonInbounds=*/true);
+  EXPECT_EQ(Stripped, M->getFunction("foo")->getArg(0));
+  EXPECT_EQ(Offset, APInt(DL.getIndexTypeSizeInBits(GEP->getType()), 2));
+}
+
+TEST(InstructionsTest,
+     StripAndAccumulateConstantOffsets_NoLookThroughNonPtrToPtrBitCast) {
+  LLVMContext C;
+  DataLayout DL("e-p:64:64:64-i64:64");
+  std::unique_ptr<Module> M = parseIR(C, R"(
+  define void @foo(b64 %x) {
+    %bc = bitcast b64 %x to ptr
+    %gep = getelementptr inbounds i8, ptr %bc, i64 1
+    ret void
+  })");
+  ASSERT_TRUE(M);
+  BasicBlock &BB = M->getFunction("foo")->getEntryBlock();
+  auto It = BB.begin();
+  Value *BC = &*It++;
+  Value *GEP = &*It++;
+  APInt Offset(DL.getIndexTypeSizeInBits(GEP->getType()), 0);
+  Value *Stripped = GEP->stripAndAccumulateConstantOffsets(
+      DL, Offset, /*AllowNonInbounds=*/true);
+  EXPECT_EQ(Stripped, BC);
+  EXPECT_EQ(Offset, APInt(DL.getIndexTypeSizeInBits(GEP->getType()), 1));
+}
+
 } // end anonymous namespace



More information about the llvm-commits mailing list