[llvm] [IndVarSimplify] Add experimental pointer IV elimination (PR #209413)

via llvm-commits llvm-commits at lists.llvm.org
Tue Jul 14 02:31:29 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-transforms

Author: masolank-us

<details>
<summary>Changes</summary>

Adds an opt-in IndVarSimplify transform that rewrites simple pointer induction variables to integer IVs and reconstructs pointer uses from the original base and scaled stride. This lets the pass reuse integer-IV reasoning for pointer loops while preserving pointer semantics, including non-unit and negative constant strides.

The transform is disabled by default and gated behind two hidden flags since it is still experimental:
  -indvars-eliminate-pointer-ivs (default off)
  -indvars-max-pointer-iv-stride (default 64)

To keep the transform sound, it is restricted to innermost loops with a single exit block, and it bails out if the pointer's base is defined inside an enclosing loop (i.e. is not loop-invariant there), since such bases would make the new integer IV vary with the outer loop and can lead to incorrect SCEV exit-value computation.

Original Author: Rajasekhar <Rajasekharvenkata.Bhetala@<!-- -->amd.com>

---

Patch is 70.65 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/209413.diff


2 Files Affected:

- (modified) llvm/lib/Transforms/Scalar/IndVarSimplify.cpp (+171-1) 
- (added) llvm/test/Transforms/IndVarSimplify/pointer-iv.ll (+1633) 


``````````diff
diff --git a/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp b/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp
index c92efadded635..b53783ea53691 100644
--- a/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp
+++ b/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp
@@ -35,6 +35,7 @@
 #include "llvm/Analysis/LoopPass.h"
 #include "llvm/Analysis/MemorySSA.h"
 #include "llvm/Analysis/MemorySSAUpdater.h"
+#include "llvm/Analysis/OptimizationRemarkEmitter.h"
 #include "llvm/Analysis/ScalarEvolution.h"
 #include "llvm/Analysis/ScalarEvolutionExpressions.h"
 #include "llvm/Analysis/ScalarEvolutionPatternMatch.h"
@@ -126,6 +127,15 @@ static cl::opt<bool>
 AllowIVWidening("indvars-widen-indvars", cl::Hidden, cl::init(true),
                 cl::desc("Allow widening of indvars to eliminate s/zext"));
 
+static cl::opt<bool> AllowPointerIVElimination(
+    "indvars-eliminate-pointer-ivs", cl::ReallyHidden, cl::init(false),
+    cl::desc("Allow elimination of pointer induction variables"));
+
+static cl::opt<int64_t> MaxPointerIVStride(
+    "indvars-max-pointer-iv-stride", cl::ReallyHidden, cl::init(64),
+    cl::desc("Maximum stride value for pointer IV elimination. Larger strides "
+             "are unlikely to benefit from this optimization."));
+
 namespace {
 
 class IndVarSimplify {
@@ -143,6 +153,7 @@ class IndVarSimplify {
   bool RunUnswitching = false;
 
   bool handleFloatingPointIV(Loop *L, PHINode *PH);
+  bool handlePointerIV(Loop *L, PHINode *PN);
   bool rewriteNonIntegerIVs(Loop *L);
 
   bool simplifyAndExtend(Loop *L, SCEVExpander &Rewriter, LoopInfo *LI);
@@ -512,6 +523,163 @@ bool IndVarSimplify::handleFloatingPointIV(Loop *L, PHINode *PN) {
   return true;
 }
 
+/// If the loop has pointer induction variables then replace them with
+/// integer induction variables and compute pointer values as base + (iv *
+/// stride). For example, for(int *p = base; p != end; p += 2)
+///   bar(*p)
+/// is converted into
+/// for(int i = 0; base + i * 2 != end; i++)
+///   bar(*(base + (i * 2)));
+bool IndVarSimplify::handlePointerIV(Loop *L, PHINode *PN) {
+  // Respect the user-controlled opt-out first.
+  if (!AllowPointerIVElimination)
+    return false;
+
+  // Only handle pointer PHI nodes with one preheader incoming value and one
+  // backedge incoming value.
+  if (!PN->getType()->isPointerTy() || PN->getNumIncomingValues() != 2)
+    return false;
+
+  // Limit the transform to simple loop shapes for now.
+  if (!L->isInnermost() || !L->getExitBlock())
+    return false;
+
+  unsigned IncomingEdge = L->contains(PN->getIncomingBlock(0)) ? 1 : 0;
+  unsigned BackEdge = IncomingEdge ^ 1;
+
+  // Require a simple pointer increment recurrence.
+  auto *Incr = dyn_cast<GetElementPtrInst>(PN->getIncomingValue(BackEdge));
+  if (!Incr || Incr->getPointerOperand() != PN || Incr->getNumIndices() != 1)
+    return false;
+
+  Type *ElemType = Incr->getSourceElementType();
+  if (!ElemType)
+    return false;
+
+  auto *ConstStride = dyn_cast<ConstantInt>(Incr->getOperand(1));
+  // Only handle constant strides.
+  if (!ConstStride)
+    return false;
+
+  int64_t StrideInt = ConstStride->getSExtValue();
+  // Limit transformation to loops with small strides
+  if (StrideInt == 0 || StrideInt > MaxPointerIVStride ||
+      StrideInt < -MaxPointerIVStride)
+    return false;
+
+  Value *BasePtr = PN->getIncomingValue(IncomingEdge);
+
+  // Skip transformation if the base pointer is defined inside a parent loop.
+  // Pointer transformation on such loops can lead to incorrect SCEV computation
+  // of exit values when combined with other transformations.
+  if (auto *BasePtrInst = dyn_cast<Instruction>(BasePtr)) {
+    Loop *BasePtrLoop = LI->getLoopFor(BasePtrInst->getParent());
+    if (BasePtrLoop && BasePtrLoop != L && BasePtrLoop->contains(L))
+      return false;
+  }
+
+  // Create integer induction variable
+  const DataLayout &DL = PN->getDataLayout();
+  auto *IntPtrType = DL.getIntPtrType(PN->getType());
+
+  // Insert new integer PHI in the loop header
+  PHINode *NewPHI =
+      PHINode::Create(IntPtrType, 2, PN->getName() + ".int", PN->getIterator());
+  NewPHI->addIncoming(ConstantInt::get(IntPtrType, 0),
+                      PN->getIncomingBlock(IncomingEdge));
+  NewPHI->setDebugLoc(PN->getDebugLoc());
+
+  // Create integer increment - place it in the same block as the original
+  // increment For negative strides, we still increment the integer IV by 1, but
+  // the scaling will handle the negative direction
+  BasicBlock *IncrBB = Incr->getParent();
+  BinaryOperator *NewAdd = BinaryOperator::CreateAdd(
+      NewPHI, ConstantInt::get(IntPtrType, 1), NewPHI->getName() + ".next",
+      IncrBB->getFirstInsertionPt());
+  NewAdd->setDebugLoc(Incr->getDebugLoc());
+  NewPHI->addIncoming(NewAdd, PN->getIncomingBlock(BackEdge));
+
+  if (const auto *AR = dyn_cast<SCEVAddRecExpr>(SE->getSCEV(NewAdd))) {
+    NewAdd->setHasNoUnsignedWrap(AR->hasNoUnsignedWrap());
+    NewAdd->setHasNoSignedWrap(AR->hasNoSignedWrap());
+  }
+
+  // Helper lambda to create scaled GEP for pointer computation
+  auto createScaledGEP = [&](Value *IntIV, Instruction *InsertPt,
+                             const Twine &Name) -> Value * {
+
+    // For stride of 1 use the IV directly (scaling handled by stride
+    Value *ScaledIndex = IntIV;
+    if (StrideInt != 1) {
+      IRBuilder<> Builder(InsertPt);
+      Value *StrideConst = ConstantInt::getSigned(IntPtrType, StrideInt);
+      ScaledIndex =
+          Builder.CreateMul(IntIV, StrideConst, IntIV->getName() + ".scaled");
+    }
+
+    IRBuilder<> Builder(InsertPt);
+    Value *ComputedPtr = Builder.CreateGEP(ElemType, BasePtr, ScaledIndex, Name);
+    return ComputedPtr;
+  };
+
+  // Collect all users that need to be replaced
+  SmallVector<std::pair<Use *, Value *>, 8> ReplacementPairs;
+
+  // Handle users of the PHI node
+  for (Use &U : PN->uses()) {
+    if (auto *UserInst = dyn_cast<Instruction>(U.getUser())) {
+      if (UserInst != Incr) {
+        if (isa<PHINode>(UserInst) && !L->contains(UserInst->getParent()))
+          continue;
+
+        auto *InstPt = isa<PHINode>(UserInst)
+                           ? &*PN->getParent()->getFirstInsertionPt()
+                           : UserInst;
+        Value *ComputedPtr =
+            createScaledGEP(NewPHI, InstPt, PN->getName() + ".computed");
+        ReplacementPairs.push_back({&U, ComputedPtr});
+      }
+    }
+  }
+
+  // Handle users of the increment instruction
+  for (Use &U : Incr->uses()) {
+    if (auto *UserInst = dyn_cast<Instruction>(U.getUser())) {
+      if (UserInst != PN) {
+        if (isa<PHINode>(UserInst) && !L->contains(UserInst->getParent()))
+          continue;
+
+        auto *InstPt = isa<PHINode>(UserInst) ? Incr : UserInst;
+        Value *ComputedPtr =
+            createScaledGEP(NewAdd, InstPt, Incr->getName() + ".computed");
+        ReplacementPairs.push_back({&U, ComputedPtr});
+      }
+    }
+  }
+
+  // Perform all replacements
+  for (auto &Pair : ReplacementPairs) {
+    Use *U = Pair.first;
+    Value *ComputedPtr = Pair.second;
+    U->set(ComputedPtr);
+  }
+
+  RecursivelyDeleteTriviallyDeadInstructions(Incr, TLI, MSSAU.get());
+  RecursivelyDeleteTriviallyDeadInstructions(PN, TLI, MSSAU.get());
+
+  // Emit optimization remark
+  OptimizationRemarkEmitter ORE(L->getHeader()->getParent());
+  ORE.emit([&]() {
+    auto Remark = OptimizationRemark(DEBUG_TYPE, "PointerIVSimplified",
+                                     L->getStartLoc(), L->getHeader())
+                  << "Simplified pointer induction variable with stride "
+                  << ore::NV("Stride", StrideInt);
+    return Remark;
+  });
+
+  return true;
+}
+
 bool IndVarSimplify::rewriteNonIntegerIVs(Loop *L) {
   // First step.  Check to see if there are any floating-point recurrences.
   // If there are, change them into integer recurrences, permitting analysis by
@@ -522,8 +690,10 @@ bool IndVarSimplify::rewriteNonIntegerIVs(Loop *L) {
 
   bool Changed = false;
   for (WeakTrackingVH &PHI : PHIs)
-    if (PHINode *PN = dyn_cast_or_null<PHINode>(&*PHI))
+    if (PHINode *PN = dyn_cast_or_null<PHINode>(&*PHI)) {
       Changed |= handleFloatingPointIV(L, PN);
+      Changed |= handlePointerIV(L, PN);
+    }
 
   // If the loop previously had floating-point IV, ScalarEvolution
   // may not have been able to compute a trip count. Now that we've done some
diff --git a/llvm/test/Transforms/IndVarSimplify/pointer-iv.ll b/llvm/test/Transforms/IndVarSimplify/pointer-iv.ll
new file mode 100644
index 0000000000000..30946640e1a64
--- /dev/null
+++ b/llvm/test/Transforms/IndVarSimplify/pointer-iv.ll
@@ -0,0 +1,1633 @@
+; RUN: opt < %s -passes=indvars -indvars-eliminate-pointer-ivs=true -S | FileCheck %s
+
+; Comprehensive test coverage for pointer IV elimination in IndVarSimplify.
+
+ at global_array = global [1024 x i8] zeroinitializer
+%struct.Point = type { i32, i32 }
+declare void @use_value(i64)
+
+; Positive Cases
+
+; TEST: Basic pointer IV transformation
+define void @test_basic_transformation(ptr %base, i32 %n) {
+; CHECK-LABEL: @test_basic_transformation(
+; CHECK:       loop:
+; CHECK:         [[IV:%.*]] = phi i64 [ 0, {{.*}} ], [ [[IV_NEXT:%.*]], %loop ]
+; CHECK:         [[IV_NEXT]] = add nuw nsw i64 [[IV]], 1
+; CHECK:         [[PTR:%.*]] = getelementptr i8, ptr %base, i64 [[IV]]
+; CHECK:         store i8 42, ptr [[PTR]]
+;
+entry:
+  %cmp = icmp sgt i32 %n, 0
+  br i1 %cmp, label %loop.ph, label %exit
+
+loop.ph:
+  br label %loop
+
+loop:
+  %p = phi ptr [ %base, %loop.ph ], [ %p.next, %loop ]
+  %i = phi i32 [ 0, %loop.ph ], [ %i.next, %loop ]
+  store i8 42, ptr %p, align 1
+  %p.next = getelementptr inbounds nuw i8, ptr %p, i64 1
+  %i.next = add nuw nsw i32 %i, 1
+  %cmp.loop = icmp slt i32 %i.next, %n
+  br i1 %cmp.loop, label %loop, label %exit
+
+exit:
+  ret void
+}
+
+; TEST: Stride != 1
+define void @test_stride_4(ptr %base, i32 %n) {
+; CHECK-LABEL: @test_stride_4(
+; CHECK:       loop:
+; CHECK:         [[IV:%.*]] = phi i64
+; CHECK:         [[SCALED:%.*]] = mul {{.*}} i64 [[IV]], 4
+; CHECK:         [[PTR:%.*]] = getelementptr i32, ptr %base, i64 [[SCALED]]
+; CHECK:         store i32 0, ptr [[PTR]]
+;
+entry:
+  %cmp = icmp sgt i32 %n, 0
+  br i1 %cmp, label %loop.ph, label %exit
+
+loop.ph:
+  br label %loop
+
+loop:
+  %p = phi ptr [ %base, %loop.ph ], [ %p.next, %loop ]
+  %i = phi i32 [ 0, %loop.ph ], [ %i.next, %loop ]
+  store i32 0, ptr %p, align 4
+  %p.next = getelementptr inbounds i32, ptr %p, i64 4
+  %i.next = add nuw nsw i32 %i, 1
+  %cmp.loop = icmp slt i32 %i.next, %n
+  br i1 %cmp.loop, label %loop, label %exit
+
+exit:
+  ret void
+}
+
+; TEST: Negative stride
+define void @test_negative_stride(ptr %end, i32 %n) {
+; CHECK-LABEL: @test_negative_stride(
+; CHECK:       loop:
+; CHECK:         [[IV:%.*]] = phi i64
+; CHECK:         [[SCALED:%.*]] = mul {{.*}} i64 [[IV]], -1
+; CHECK:         [[PTR:%.*]] = getelementptr i8, ptr %end, i64 [[SCALED]]
+; CHECK:         store i8 0, ptr [[PTR]]
+;
+entry:
+  %cmp = icmp sgt i32 %n, 0
+  br i1 %cmp, label %loop.ph, label %exit
+
+loop.ph:
+  br label %loop
+
+loop:
+  %p = phi ptr [ %end, %loop.ph ], [ %p.next, %loop ]
+  %i = phi i32 [ 0, %loop.ph ], [ %i.next, %loop ]
+  store i8 0, ptr %p, align 1
+  %p.next = getelementptr inbounds i8, ptr %p, i64 -1
+  %i.next = add nuw nsw i32 %i, 1
+  %cmp.loop = icmp slt i32 %i.next, %n
+  br i1 %cmp.loop, label %loop, label %exit
+
+exit:
+  ret void
+}
+
+; TEST: GEP without nuw flag
+; Note: Other passes may still add nuw/nsw after our transformation,
+; so we just verify the transformation happens correctly.
+define void @test_no_nuw_flag(ptr %base, i32 %n) {
+; CHECK-LABEL: @test_no_nuw_flag(
+; CHECK:       loop:
+; CHECK:         [[IV:%.*]] = phi i64
+; CHECK:         [[IV_NEXT:%.*]] = add {{.*}} i64 [[IV]], 1
+; CHECK:         [[PTR:%.*]] = getelementptr i8, ptr %base, i64 [[IV]]
+; CHECK:         store i8 42, ptr [[PTR]]
+;
+entry:
+  %cmp = icmp sgt i32 %n, 0
+  br i1 %cmp, label %loop.ph, label %exit
+
+loop.ph:
+  br label %loop
+
+loop:
+  %p = phi ptr [ %base, %loop.ph ], [ %p.next, %loop ]
+  %i = phi i32 [ 0, %loop.ph ], [ %i.next, %loop ]
+  store i8 42, ptr %p, align 1
+  ; GEP without nuw flag
+  %p.next = getelementptr inbounds i8, ptr %p, i64 1
+  %i.next = add nuw nsw i32 %i, 1
+  %cmp.loop = icmp slt i32 %i.next, %n
+  br i1 %cmp.loop, label %loop, label %exit
+
+exit:
+  ret void
+}
+
+; TEST: Base pointer is not an instruction (function argument)
+define void @test_base_is_argument(ptr %base, i32 %n) {
+; Function argument as base - SHOULD be transformed
+; CHECK-LABEL: @test_base_is_argument(
+; CHECK:       loop:
+; CHECK:         [[IV:%.*]] = phi i64 [ 0, {{.*}} ]
+; CHECK:         [[PTR:%.*]] = getelementptr {{.*}} ptr %base, i64 [[IV]]
+;
+entry:
+  %cmp = icmp sgt i32 %n, 0
+  br i1 %cmp, label %loop.ph, label %exit
+
+loop.ph:
+  br label %loop
+
+loop:
+  %p = phi ptr [ %base, %loop.ph ], [ %p.next, %loop ]
+  %i = phi i32 [ 0, %loop.ph ], [ %i.next, %loop ]
+  store i8 42, ptr %p, align 1
+  %p.next = getelementptr inbounds nuw i8, ptr %p, i64 1
+  %i.next = add nuw nsw i32 %i, 1
+  %cmp.loop = icmp slt i32 %i.next, %n
+  br i1 %cmp.loop, label %loop, label %exit
+
+exit:
+  ret void
+}
+
+; TEST: Base pointer is global constant (not instruction)
+define void @test_base_is_global(i32 %n) {
+; Global as base - SHOULD be transformed (not an instruction)
+; CHECK-LABEL: @test_base_is_global(
+; CHECK:       loop:
+; CHECK:         [[IV:%.*]] = phi i64 [ 0, {{.*}} ]
+; CHECK:         [[PTR:%.*]] = getelementptr {{.*}} ptr @global_array, i64 [[IV]]
+;
+entry:
+  %cmp = icmp sgt i32 %n, 0
+  br i1 %cmp, label %loop.ph, label %exit
+
+loop.ph:
+  br label %loop
+
+loop:
+  %p = phi ptr [ @global_array, %loop.ph ], [ %p.next, %loop ]
+  %i = phi i32 [ 0, %loop.ph ], [ %i.next, %loop ]
+  store i8 42, ptr %p, align 1
+  %p.next = getelementptr inbounds nuw i8, ptr %p, i64 1
+  %i.next = add nuw nsw i32 %i, 1
+  %cmp.loop = icmp slt i32 %i.next, %n
+  br i1 %cmp.loop, label %loop, label %exit
+
+exit:
+  ret void
+}
+
+; TEST: Base pointer from same loop (should transform)
+define void @test_base_from_same_loop_header(ptr %input, i32 %n) {
+; Base computed in same loop header - SHOULD be transformed
+; CHECK-LABEL: @test_base_from_same_loop_header(
+; CHECK:       loop:
+; CHECK:         [[IV:%.*]] = phi i64
+; CHECK:         [[PTR:%.*]] = getelementptr {{.*}} ptr %input, i64 [[IV]]
+;
+entry:
+  %cmp = icmp sgt i32 %n, 0
+  br i1 %cmp, label %loop.ph, label %exit
+
+loop.ph:
+  br label %loop
+
+loop:
+  %p = phi ptr [ %input, %loop.ph ], [ %p.next, %loop ]
+  %i = phi i32 [ 0, %loop.ph ], [ %i.next, %loop ]
+  store i8 42, ptr %p, align 1
+  %p.next = getelementptr inbounds nuw i8, ptr %p, i64 1
+  %i.next = add nuw nsw i32 %i, 1
+  %cmp.loop = icmp slt i32 %i.next, %n
+  br i1 %cmp.loop, label %loop, label %exit
+
+exit:
+  ret void
+}
+
+; TEST: Multiple uses of PHI in loop body
+define void @test_multiple_phi_uses(ptr %base, i32 %n) {
+; Multiple uses of the pointer PHI in loop body
+; CHECK-LABEL: @test_multiple_phi_uses(
+; CHECK:       loop:
+; CHECK:         [[IV:%.*]] = phi i64
+; CHECK:         [[PTR1:%.*]] = getelementptr {{.*}} ptr %base, i64 [[IV]]
+; CHECK:         load i8, ptr [[PTR1]]
+; CHECK:         [[PTR2:%.*]] = getelementptr {{.*}} ptr %base, i64 [[IV]]
+; CHECK:         store i8 {{.*}}, ptr [[PTR2]]
+;
+entry:
+  %cmp = icmp sgt i32 %n, 0
+  br i1 %cmp, label %loop.ph, label %exit
+
+loop.ph:
+  br label %loop
+
+loop:
+  %p = phi ptr [ %base, %loop.ph ], [ %p.next, %loop ]
+  %i = phi i32 [ 0, %loop.ph ], [ %i.next, %loop ]
+  ; First use of %p
+  %val = load i8, ptr %p, align 1
+  %inc = add i8 %val, 1
+  ; Second use of %p
+  store i8 %inc, ptr %p, align 1
+  %p.next = getelementptr inbounds nuw i8, ptr %p, i64 1
+  %i.next = add nuw nsw i32 %i, 1
+  %cmp.loop = icmp slt i32 %i.next, %n
+  br i1 %cmp.loop, label %loop, label %exit
+
+exit:
+  ret void
+}
+
+; TEST: Increment used multiple times in loop body
+define void @test_multiple_incr_uses(ptr %base, i32 %n) {
+; Multiple uses of the increment GEP in loop body
+; CHECK-LABEL: @test_multiple_incr_uses(
+; CHECK:       loop:
+; CHECK:         [[IV:%.*]] = phi i64
+; CHECK:         [[IV_NEXT:%.*]] = add {{.*}} i64 [[IV]], 1
+; CHECK:         [[NEXT_PTR1:%.*]] = getelementptr {{.*}} ptr %base, i64 [[IV_NEXT]]
+; CHECK:         load i8, ptr [[NEXT_PTR1]]
+; CHECK:         [[NEXT_PTR2:%.*]] = getelementptr {{.*}} ptr %base, i64 [[IV_NEXT]]
+; CHECK:         store i8 {{.*}}, ptr [[NEXT_PTR2]]
+;
+entry:
+  %cmp = icmp sgt i32 %n, 0
+  br i1 %cmp, label %loop.ph, label %exit
+
+loop.ph:
+  br label %loop
+
+loop:
+  %p = phi ptr [ %base, %loop.ph ], [ %p.next, %loop ]
+  %i = phi i32 [ 0, %loop.ph ], [ %i.next, %loop ]
+  %p.next = getelementptr inbounds nuw i8, ptr %p, i64 1
+  ; First use of %p.next (lookahead)
+  %next.val = load i8, ptr %p.next, align 1
+  %inc = add i8 %next.val, 1
+  ; Second use of %p.next
+  store i8 %inc, ptr %p.next, align 1
+  %i.next = add nuw nsw i32 %i, 1
+  %cmp.loop = icmp slt i32 %i.next, %n
+  br i1 %cmp.loop, label %loop, label %exit
+
+exit:
+  ret void
+}
+
+; TEST: Non-unit stride (should now be optimized with multiplication)
+define void @test_non_unit_stride(ptr %A, ptr %last, ptr %B) {
+; CHECK-LABEL: @test_non_unit_stride(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq ptr [[A:%.*]], [[LAST:%.*]]
+; CHECK-NEXT:    br i1 [[CMP]], label [[EXIT:%.*]], label [[LOOP_PREHEADER:%.*]]
+; CHECK:       loop.preheader:
+; CHECK-NEXT:    br label [[LOOP:%.*]]
+; CHECK:       loop:
+; CHECK-NEXT:    [[A_PHI_INT:%.*]] = phi i64 [ 0, [[LOOP_PREHEADER]] ], [ [[A_PHI_INT_NEXT:%.*]], [[LOOP]] ]
+; CHECK-NEXT:    [[A_PHI_INT_NEXT]] = add{{.*}} i64 [[A_PHI_INT]], 1
+; CHECK-NEXT:    [[B_PHI_INT_SCALED:%.*]] = mul{{.*}} i64 [[A_PHI_INT]], 2
+; CHECK-NEXT:    [[B_COMPUTED:%.*]] = getelementptr i32, ptr [[B:%.*]], i64 [[B_PHI_INT_SCALED]]
+; CHECK-NEXT:    [[VAL:%.*]] = load i32, ptr [[B_COMPUTED]], align 4
+; CHECK-NEXT:    [[A_PHI_INT_SCALED:%.*]] = mul{{.*}} i64 [[A_PHI_INT]], 2
+; CHECK-NEXT:    [[A_COMPUTED:%.*]] = getelementptr i32, ptr [[A]], i64 [[A_PHI_INT_SCALED]]
+; CHECK-NEXT:    store i32 [[VAL]], ptr [[A_COMPUTED]], align 4
+; CHECK-NEXT:    [[A_PHI_INT_NEXT_SCALED:%.*]] = mul{{.*}} i64 [[A_PHI_INT_NEXT]], 2
+; CHECK-NEXT:    [[A_NEXT_COMPUTED:%.*]] = getelementptr i32, ptr [[A]], i64 [[A_PHI_INT_NEXT_SCALED]]
+; CHECK-NEXT:    [[CMP_NEXT:%.*]] = icmp eq ptr [[A_NEXT_COMPUTED]], [[LAST]]
+; CHECK-NEXT:    br i1 [[CMP_NEXT]], label [[EXIT_LOOPEXIT:%.*]], label [[LOOP]]
+; CHECK:       exit.loopexit:
+; CHECK-NEXT:    br label [[EXIT]]
+; CHECK:       exit:
+; CHECK-NEXT:    ret void
+;
+entry:
+  %cmp = icmp eq ptr %A, %last
+  br i1 %cmp, label %exit, label %loop
+
+loop:
+  %A.phi = phi ptr [ %A.next, %loop ], [ %A, %entry ]
+  %B.phi = phi ptr [ %B.next, %loop ], [ %B, %entry ]
+  %val = load i32, ptr %B.phi, align 4
+  store i32 %val, ptr %A.phi, align 4
+  %A.next = getelementptr inbounds i32, ptr %A.phi, i64 2  ; stride of 2, not 1
+  %B.next = getelementptr inbounds i32, ptr %B.phi, i64 2
+  %cmp.next = icmp eq ptr %A.next, %last
+  br i1 %cmp.next, label %exit, label %loop
+
+exit:
+  ret void
+}
+
+; TEST: Multiple uses of GEP
+define void @test_multiple_gep_uses(ptr %A, ptr %last, ptr %B) {
+; CHECK-LABEL: @test_multiple_gep_uses(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq ptr [[A:%.*]], [[LAST:%.*]]
+; CHECK-NEXT:    br i1 [[CMP]], label [[EXIT:%.*]], label [[LOOP:%.*]]
+; CHECK:       loop.preheader:
+; CHECK-NEXT:    br label [[LOOP1:%.*]]
+; CHECK:       loop:
+; CHECK-NEXT:    [[A_PHI_INT:%.*]] = phi i64 [ 0, [[LOOP]] ], [ [[A_PHI_INT_NEXT:%.*]], [[LOOP1]] ]
+; CHECK-NEXT:    [[A_PHI_INT_NEXT]] = add{{.*}} i64 [[A_PHI_INT]], 1
+; CHECK-NEXT:    [[B_PHI:%.*]] = getelementptr i32, ptr [[B:%.*]], i64 [[A_PHI_INT]]
+; CHECK-NEXT:    [[VAL:%.*]] = load i32, ptr [[B_PHI]], align 4
+; CHECK-NEXT:    [[A_PHI:%.*]] = getelementptr i32, ptr [[A]], i64 [[A_PHI_INT]]
+; CHECK-NEXT:    store i32 [[VAL]], ptr [[A_PHI]], align 4
+; CHECK-NEXT:    [[A_NEXT:%.*]] = getelementptr i32, ptr [[A]], i64 [[A_PHI_INT_NEXT]]
+; CHECK-NEXT:    [[EXTRA_USE:%.*]] = ptrtoint ptr [[A_NEXT]] to i64
+; CHECK-NEXT:    call void @use_value(i64 [[EXTRA_USE]])
+; CHECK-NEXT:    [[A_NEXT_COMPUTED:%.*]] = getelementptr i3...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/209413


More information about the llvm-commits mailing list