[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