Skip to content

[SCEV] Directly form add recurrences for simple pointer IVs. - #222915

Open
fhahn wants to merge 2 commits into
llvm:mainfrom
fhahn:scev-ptr-iv-addrec-direct
Open

[SCEV] Directly form add recurrences for simple pointer IVs.#222915
fhahn wants to merge 2 commits into
llvm:mainfrom
fhahn:scev-ptr-iv-addrec-direct

Conversation

@fhahn

@fhahn fhahn commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Add support for PN = PHI(Start, gep Self, LoopInvariant) to
createSimpleAffineAddRec.

This allows to handle simple pointer IVs without going through the more
expensive createAddRecFromPHI machinery in many cases.

This is not completely NFC: previously we created SCEVUnknown for a
trivial AddRec with step 0, now we return the start value.

This gives a modest geomean compile-time decrease

  • stage1-O3: -0.05%
  • stage1-ReleaseThinLTO: -0.06%
  • stage1-ReleaseLTO-g: -0.06%
  • stage1-aarch64-O3: -0.07%
  • stage2-O3: -0.05%
  • clang build: -0.08%

With larger decreases for more SCEV-heavy workloads.
https://llvm-compile-time-tracker.com/compare.php?from=c1b661fde124399f0484c4b39501b37613cc4757&to=5cd19e23596c86eae9607286efe86b7a3e885a86&stat=instructions:u

Add support for PN = PHI(Start, gep Self, LoopInvariant) to
createSimpleAffineAddRec.

This allows to handle simple pointer IVs without going through the more
expensive createAddRecFromPHI machinery in many cases.

This is not completely NFC: previously we created SCEVUnknown for a
trivial AddRec with step 0, now we return the start value.

This gives a modest geomean compile-time decrease
 * stage1-O3: -0.05%
 * stage1-ReleaseThinLTO: -0.06%
 * stage1-ReleaseLTO-g: -0.06%
 * stage1-aarch64-O3: -0.07%
 * stage2-O3: -0.05%
 * clang build: -0.08%

With larger decreases for more SCEV-heavy workloads.
https://llvm-compile-time-tracker.com/compare.php?from=c1b661fde124399f0484c4b39501b37613cc4757&to=5cd19e23596c86eae9607286efe86b7a3e885a86&stat=instructions:u
@llvmorg-github-actions llvmorg-github-actions Bot added the llvm:analysis Includes value tracking, cost tables and constant folding label Sep 11, 2026
@llvmorg-github-actions

Copy link
Copy Markdown

@llvm/pr-subscribers-llvm-analysis

Author: Florian Hahn (fhahn)

Changes

Add support for PN = PHI(Start, gep Self, LoopInvariant) to
createSimpleAffineAddRec.

This allows to handle simple pointer IVs without going through the more
expensive createAddRecFromPHI machinery in many cases.

This is not completely NFC: previously we created SCEVUnknown for a
trivial AddRec with step 0, now we return the start value.

This gives a modest geomean compile-time decrease

  • stage1-O3: -0.05%
  • stage1-ReleaseThinLTO: -0.06%
  • stage1-ReleaseLTO-g: -0.06%
  • stage1-aarch64-O3: -0.07%
  • stage2-O3: -0.05%
  • clang build: -0.08%

With larger decreases for more SCEV-heavy workloads.
https://llvm-compile-time-tracker.com/compare.php?from=c1b661fde124399f0484c4b39501b37613cc4757&to=5cd19e23596c86eae9607286efe86b7a3e885a86&stat=instructions:u


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

2 Files Affected:

  • (modified) llvm/lib/Analysis/ScalarEvolution.cpp (+49-30)
  • (added) llvm/test/Analysis/ScalarEvolution/pointer-iv-addrec.ll (+343)
diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index 06f350fd2179b..810fc9573e310 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -5693,6 +5693,24 @@ bool PredicatedScalarEvolution::areAddRecsEqualWithPreds(
   return true;
 }
 
+static SCEV::NoWrapFlags
+getNoWrapFlagsForGEP(GEPOperator *GEP, const SCEV *Accum, ScalarEvolution &SE) {
+  SCEV::NoWrapFlags Flags = SCEV::FlagAnyWrap;
+  GEPNoWrapFlags NW = GEP->getNoWrapFlags();
+  // If the increment has any nowrap flags, then we know the address
+  // space cannot be wrapped around.
+  if (NW != GEPNoWrapFlags::none())
+    Flags = ScalarEvolution::setFlags(Flags, SCEV::FlagNW);
+  // If the GEP is nuw or nusw with non-negative offset, we know that
+  // no unsigned wrap occurs. We cannot set the nsw flag as only the
+  // offset is treated as signed, while the base is unsigned.
+  if (NW.hasNoUnsignedWrap() ||
+      (NW.hasNoUnsignedSignedWrap() && SE.isKnownNonNegative(Accum)))
+    Flags = ScalarEvolution::setFlags(Flags, SCEV::FlagNUW);
+
+  return Flags;
+}
+
 /// A helper function for createAddRecFromPHI to handle simple cases.
 ///
 /// This function tries to find an AddRec expression for the simplest (yet most
@@ -5706,27 +5724,39 @@ const SCEV *ScalarEvolution::createSimpleAffineAddRec(PHINode *PN,
   assert(L && L->getHeader() == PN->getParent());
   assert(BEValueV && StartValueV);
 
-  auto BO = MatchBinaryOp(BEValueV, getDataLayout(), AC, DT, PN);
-  if (!BO)
-    return nullptr;
+  const SCEV *Accum = nullptr;
+  SCEV::NoWrapFlags Flags = SCEV::FlagAnyWrap;
+  if (auto BO = MatchBinaryOp(BEValueV, getDataLayout(), AC, DT, PN)) {
+    if (BO->Opcode != Instruction::Add)
+      return nullptr;
 
-  if (BO->Opcode != Instruction::Add)
-    return nullptr;
+    if (BO->LHS == PN && L->isLoopInvariant(BO->RHS))
+      Accum = getSCEV(BO->RHS);
+    else if (BO->RHS == PN && L->isLoopInvariant(BO->LHS))
+      Accum = getSCEV(BO->LHS);
 
-  const SCEV *Accum = nullptr;
-  if (BO->LHS == PN && L->isLoopInvariant(BO->RHS))
-    Accum = getSCEV(BO->RHS);
-  else if (BO->RHS == PN && L->isLoopInvariant(BO->LHS))
-    Accum = getSCEV(BO->LHS);
+    if (!Accum)
+      return nullptr;
 
-  if (!Accum)
-    return nullptr;
+    if (BO->IsNUW)
+      Flags = setFlags(Flags, SCEV::FlagNUW);
+    if (BO->IsNSW)
+      Flags = setFlags(Flags, SCEV::FlagNSW);
+  } else {
+    // Handle pointer induction variable: PN = PHI(Start, gep PN,
+    // LoopInvariant).
+    auto *GEP = dyn_cast<GEPOperator>(BEValueV);
+    if (!GEP || GEP->getPointerOperand() != PN || GEP->getNumIndices() != 1)
+      return nullptr;
+    Value *Idx = *GEP->idx_begin();
+    if (!L->isLoopInvariant(Idx))
+      return nullptr;
 
-  SCEV::NoWrapFlags Flags = SCEV::FlagAnyWrap;
-  if (BO->IsNUW)
-    Flags = setFlags(Flags, SCEV::FlagNUW);
-  if (BO->IsNSW)
-    Flags = setFlags(Flags, SCEV::FlagNSW);
+    Type *IntIdxTy = getEffectiveSCEVType(GEP->getType());
+    Accum = getMulExpr(getTruncateOrSignExtend(getSCEV(Idx), IntIdxTy),
+                       getSizeOfExpr(IntIdxTy, GEP->getSourceElementType()));
+    Flags = getNoWrapFlagsForGEP(GEP, Accum, *this);
+  }
 
   const SCEV *StartVal = getSCEV(StartValueV);
   const SCEV *PHISCEV = getAddRecExpr(StartVal, Accum, L, Flags);
@@ -5832,19 +5862,8 @@ const SCEV *ScalarEvolution::createAddRecFromPHI(PHINode *PN) {
               Flags = setFlags(Flags, SCEV::FlagNSW);
           }
         } else if (GEPOperator *GEP = dyn_cast<GEPOperator>(BEValueV)) {
-          if (GEP->getOperand(0) == PN) {
-            GEPNoWrapFlags NW = GEP->getNoWrapFlags();
-            // If the increment has any nowrap flags, then we know the address
-            // space cannot be wrapped around.
-            if (NW != GEPNoWrapFlags::none())
-              Flags = setFlags(Flags, SCEV::FlagNW);
-            // If the GEP is nuw or nusw with non-negative offset, we know that
-            // no unsigned wrap occurs. We cannot set the nsw flag as only the
-            // offset is treated as signed, while the base is unsigned.
-            if (NW.hasNoUnsignedWrap() ||
-                (NW.hasNoUnsignedSignedWrap() && isKnownNonNegative(Accum)))
-              Flags = setFlags(Flags, SCEV::FlagNUW);
-          }
+          if (GEP->getOperand(0) == PN)
+            Flags = getNoWrapFlagsForGEP(GEP, Accum, *this);
 
           // We cannot transfer nuw and nsw flags from subtraction
           // operations -- sub nuw X, Y is not the same as add nuw X, -Y
diff --git a/llvm/test/Analysis/ScalarEvolution/pointer-iv-addrec.ll b/llvm/test/Analysis/ScalarEvolution/pointer-iv-addrec.ll
new file mode 100644
index 0000000000000..348260df97839
--- /dev/null
+++ b/llvm/test/Analysis/ScalarEvolution/pointer-iv-addrec.ll
@@ -0,0 +1,343 @@
+; NOTE: Assertions have been autogenerated by utils/update_analyze_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -passes='print<scalar-evolution>' -disable-output %s 2>&1 | FileCheck %s
+
+target datalayout = "e-m:e-p:64:64-p1:32:32-i64:64-n32:64"
+
+%struct.S = type { i32, i64 }
+
+define void @gep_iv_constant_step(ptr %p, i64 %n) {
+; CHECK-LABEL: 'gep_iv_constant_step'
+; CHECK-NEXT:  Classifying expressions for: @gep_iv_constant_step
+; CHECK-NEXT:    %iv = phi ptr [ %p, %entry ], [ %iv.next, %loop ]
+; CHECK-NEXT:    --> {%p,+,4}<nuw><%loop> U: full-set S: full-set Exits: (-4 + (4 * %n) + %p) LoopDispositions: { %loop: Computable }
+; CHECK-NEXT:    %i = phi i64 [ 0, %entry ], [ %i.next, %loop ]
+; CHECK-NEXT:    --> {0,+,1}<nuw><nsw><%loop> U: [0,-9223372036854775808) S: [0,-9223372036854775808) Exits: (-1 + %n) LoopDispositions: { %loop: Computable }
+; CHECK-NEXT:    %iv.next = getelementptr inbounds i32, ptr %iv, i64 1
+; CHECK-NEXT:    --> {(4 + %p),+,4}<nw><%loop> U: full-set S: full-set Exits: ((4 * %n) + %p) LoopDispositions: { %loop: Computable }
+; CHECK-NEXT:    %i.next = add nuw nsw i64 %i, 1
+; CHECK-NEXT:    --> {1,+,1}<nuw><nsw><%loop> U: [1,-9223372036854775808) S: [1,-9223372036854775808) Exits: %n LoopDispositions: { %loop: Computable }
+; CHECK-NEXT:  Determining loop execution counts for: @gep_iv_constant_step
+; CHECK-NEXT:  Loop %loop: backedge-taken count is (-1 + %n)
+; CHECK-NEXT:  Loop %loop: constant max backedge-taken count is i64 -1
+; CHECK-NEXT:  Loop %loop: symbolic max backedge-taken count is (-1 + %n)
+; CHECK-NEXT:  Loop %loop: Trip multiple is 1
+;
+entry:
+  br label %loop
+
+loop:
+  %iv = phi ptr [ %p, %entry ], [ %iv.next, %loop ]
+  %i = phi i64 [ 0, %entry ], [ %i.next, %loop ]
+  %iv.next = getelementptr inbounds i32, ptr %iv, i64 1
+  %i.next = add nuw nsw i64 %i, 1
+  store i32 0, ptr %iv, align 4
+  %ec = icmp eq i64 %i.next, %n
+  br i1 %ec, label %exit, label %loop
+
+exit:
+  ret void
+}
+
+define void @gep_iv_invariant_step(ptr %p, i64 %step, i64 %n) {
+; CHECK-LABEL: 'gep_iv_invariant_step'
+; CHECK-NEXT:  Classifying expressions for: @gep_iv_invariant_step
+; CHECK-NEXT:    %iv = phi ptr [ %p, %entry ], [ %iv.next, %loop ]
+; CHECK-NEXT:    --> {%p,+,%step}<%loop> U: full-set S: full-set Exits: (((-1 + %n) * %step) + %p) LoopDispositions: { %loop: Computable }
+; CHECK-NEXT:    %i = phi i64 [ 0, %entry ], [ %i.next, %loop ]
+; CHECK-NEXT:    --> {0,+,1}<nuw><nsw><%loop> U: [0,-9223372036854775808) S: [0,-9223372036854775808) Exits: (-1 + %n) LoopDispositions: { %loop: Computable }
+; CHECK-NEXT:    %iv.next = getelementptr i8, ptr %iv, i64 %step
+; CHECK-NEXT:    --> {(%step + %p),+,%step}<%loop> U: full-set S: full-set Exits: ((%step * %n) + %p) LoopDispositions: { %loop: Computable }
+; CHECK-NEXT:    %i.next = add nuw nsw i64 %i, 1
+; CHECK-NEXT:    --> {1,+,1}<nuw><nsw><%loop> U: [1,-9223372036854775808) S: [1,-9223372036854775808) Exits: %n LoopDispositions: { %loop: Computable }
+; CHECK-NEXT:  Determining loop execution counts for: @gep_iv_invariant_step
+; CHECK-NEXT:  Loop %loop: backedge-taken count is (-1 + %n)
+; CHECK-NEXT:  Loop %loop: constant max backedge-taken count is i64 -1
+; CHECK-NEXT:  Loop %loop: symbolic max backedge-taken count is (-1 + %n)
+; CHECK-NEXT:  Loop %loop: Trip multiple is 1
+;
+entry:
+  br label %loop
+
+loop:
+  %iv = phi ptr [ %p, %entry ], [ %iv.next, %loop ]
+  %i = phi i64 [ 0, %entry ], [ %i.next, %loop ]
+  %iv.next = getelementptr i8, ptr %iv, i64 %step
+  %i.next = add nuw nsw i64 %i, 1
+  store i32 0, ptr %iv, align 4
+  %ec = icmp eq i64 %i.next, %n
+  br i1 %ec, label %exit, label %loop
+
+exit:
+  ret void
+}
+
+; The offset may be negative, so only nw can be derived from nusw.
+define void @gep_iv_nusw_negative_step(ptr %p, i64 %n) {
+; CHECK-LABEL: 'gep_iv_nusw_negative_step'
+; CHECK-NEXT:  Classifying expressions for: @gep_iv_nusw_negative_step
+; CHECK-NEXT:    %iv = phi ptr [ %p, %entry ], [ %iv.next, %loop ]
+; CHECK-NEXT:    --> {%p,+,-4}<nw><%loop> U: full-set S: full-set Exits: (4 + (-4 * %n) + %p) LoopDispositions: { %loop: Computable }
+; CHECK-NEXT:    %i = phi i64 [ 0, %entry ], [ %i.next, %loop ]
+; CHECK-NEXT:    --> {0,+,1}<nuw><nsw><%loop> U: [0,-9223372036854775808) S: [0,-9223372036854775808) Exits: (-1 + %n) LoopDispositions: { %loop: Computable }
+; CHECK-NEXT:    %iv.next = getelementptr nusw i32, ptr %iv, i64 -1
+; CHECK-NEXT:    --> {(-4 + %p),+,-4}<nw><%loop> U: full-set S: full-set Exits: ((-4 * %n) + %p) LoopDispositions: { %loop: Computable }
+; CHECK-NEXT:    %i.next = add nuw nsw i64 %i, 1
+; CHECK-NEXT:    --> {1,+,1}<nuw><nsw><%loop> U: [1,-9223372036854775808) S: [1,-9223372036854775808) Exits: %n LoopDispositions: { %loop: Computable }
+; CHECK-NEXT:  Determining loop execution counts for: @gep_iv_nusw_negative_step
+; CHECK-NEXT:  Loop %loop: backedge-taken count is (-1 + %n)
+; CHECK-NEXT:  Loop %loop: constant max backedge-taken count is i64 -1
+; CHECK-NEXT:  Loop %loop: symbolic max backedge-taken count is (-1 + %n)
+; CHECK-NEXT:  Loop %loop: Trip multiple is 1
+;
+entry:
+  br label %loop
+
+loop:
+  %iv = phi ptr [ %p, %entry ], [ %iv.next, %loop ]
+  %i = phi i64 [ 0, %entry ], [ %i.next, %loop ]
+  %iv.next = getelementptr nusw i32, ptr %iv, i64 -1
+  %i.next = add nuw nsw i64 %i, 1
+  store i32 0, ptr %iv, align 4
+  %ec = icmp eq i64 %i.next, %n
+  br i1 %ec, label %exit, label %loop
+
+exit:
+  ret void
+}
+
+; The index is wider than the index type of the address space, so it is
+; truncated before scaling.
+define void @gep_iv_narrow_index_type(ptr addrspace(1) %p, i64 %step, i64 %n) {
+; CHECK-LABEL: 'gep_iv_narrow_index_type'
+; CHECK-NEXT:  Classifying expressions for: @gep_iv_narrow_index_type
+; CHECK-NEXT:    %iv = phi ptr addrspace(1) [ %p, %entry ], [ %iv.next, %loop ]
+; CHECK-NEXT:    --> {%p,+,(4 * (trunc i64 %step to i32))}<nw><%loop> U: full-set S: full-set Exits: ((4 * (trunc i64 %step to i32) * (-1 + (trunc i64 %n to i32))) + %p) LoopDispositions: { %loop: Computable }
+; CHECK-NEXT:    %i = phi i64 [ 0, %entry ], [ %i.next, %loop ]
+; CHECK-NEXT:    --> {0,+,1}<nuw><nsw><%loop> U: [0,-9223372036854775808) S: [0,-9223372036854775808) Exits: (-1 + %n) LoopDispositions: { %loop: Computable }
+; CHECK-NEXT:    %iv.next = getelementptr inbounds i32, ptr addrspace(1) %iv, i64 %step
+; CHECK-NEXT:    --> {((4 * (trunc i64 %step to i32)) + %p),+,(4 * (trunc i64 %step to i32))}<nw><%loop> U: full-set S: full-set Exits: ((4 * (trunc i64 %step to i32) * (trunc i64 %n to i32)) + %p) LoopDispositions: { %loop: Computable }
+; CHECK-NEXT:    %i.next = add nuw nsw i64 %i, 1
+; CHECK-NEXT:    --> {1,+,1}<nuw><nsw><%loop> U: [1,-9223372036854775808) S: [1,-9223372036854775808) Exits: %n LoopDispositions: { %loop: Computable }
+; CHECK-NEXT:  Determining loop execution counts for: @gep_iv_narrow_index_type
+; CHECK-NEXT:  Loop %loop: backedge-taken count is (-1 + %n)
+; CHECK-NEXT:  Loop %loop: constant max backedge-taken count is i64 -1
+; CHECK-NEXT:  Loop %loop: symbolic max backedge-taken count is (-1 + %n)
+; CHECK-NEXT:  Loop %loop: Trip multiple is 1
+;
+entry:
+  br label %loop
+
+loop:
+  %iv = phi ptr addrspace(1) [ %p, %entry ], [ %iv.next, %loop ]
+  %i = phi i64 [ 0, %entry ], [ %i.next, %loop ]
+  %iv.next = getelementptr inbounds i32, ptr addrspace(1) %iv, i64 %step
+  %i.next = add nuw nsw i64 %i, 1
+  store i32 0, ptr addrspace(1) %iv, align 4
+  %ec = icmp eq i64 %i.next, %n
+  br i1 %ec, label %exit, label %loop
+
+exit:
+  ret void
+}
+
+define void @gep_iv_scalable(ptr %p, i64 %n) {
+; CHECK-LABEL: 'gep_iv_scalable'
+; CHECK-NEXT:  Classifying expressions for: @gep_iv_scalable
+; CHECK-NEXT:    %iv = phi ptr [ %p, %entry ], [ %iv.next, %loop ]
+; CHECK-NEXT:    --> {%p,+,(16 * vscale)}<%loop> U: full-set S: full-set Exits: ((16 * vscale * (-1 + %n)) + %p) LoopDispositions: { %loop: Computable }
+; CHECK-NEXT:    %i = phi i64 [ 0, %entry ], [ %i.next, %loop ]
+; CHECK-NEXT:    --> {0,+,1}<nuw><nsw><%loop> U: [0,-9223372036854775808) S: [0,-9223372036854775808) Exits: (-1 + %n) LoopDispositions: { %loop: Computable }
+; CHECK-NEXT:    %iv.next = getelementptr <vscale x 4 x i32>, ptr %iv, i64 1
+; CHECK-NEXT:    --> {((16 * vscale) + %p),+,(16 * vscale)}<%loop> U: full-set S: full-set Exits: ((16 * vscale * %n) + %p) LoopDispositions: { %loop: Computable }
+; CHECK-NEXT:    %i.next = add nuw nsw i64 %i, 1
+; CHECK-NEXT:    --> {1,+,1}<nuw><nsw><%loop> U: [1,-9223372036854775808) S: [1,-9223372036854775808) Exits: %n LoopDispositions: { %loop: Computable }
+; CHECK-NEXT:  Determining loop execution counts for: @gep_iv_scalable
+; CHECK-NEXT:  Loop %loop: backedge-taken count is (-1 + %n)
+; CHECK-NEXT:  Loop %loop: constant max backedge-taken count is i64 -1
+; CHECK-NEXT:  Loop %loop: symbolic max backedge-taken count is (-1 + %n)
+; CHECK-NEXT:  Loop %loop: Trip multiple is 1
+;
+entry:
+  br label %loop
+
+loop:
+  %iv = phi ptr [ %p, %entry ], [ %iv.next, %loop ]
+  %i = phi i64 [ 0, %entry ], [ %i.next, %loop ]
+  %iv.next = getelementptr <vscale x 4 x i32>, ptr %iv, i64 1
+  %i.next = add nuw nsw i64 %i, 1
+  store i32 0, ptr %iv, align 4
+  %ec = icmp eq i64 %i.next, %n
+  br i1 %ec, label %exit, label %loop
+
+exit:
+  ret void
+}
+
+define void @gep_iv_struct_single_index(ptr %p, i64 %n) {
+; CHECK-LABEL: 'gep_iv_struct_single_index'
+; CHECK-NEXT:  Classifying expressions for: @gep_iv_struct_single_index
+; CHECK-NEXT:    %iv = phi ptr [ %p, %entry ], [ %iv.next, %loop ]
+; CHECK-NEXT:    --> {%p,+,16}<nuw><%loop> U: full-set S: full-set Exits: (-16 + (16 * %n) + %p) LoopDispositions: { %loop: Computable }
+; CHECK-NEXT:    %i = phi i64 [ 0, %entry ], [ %i.next, %loop ]
+; CHECK-NEXT:    --> {0,+,1}<nuw><nsw><%loop> U: [0,-9223372036854775808) S: [0,-9223372036854775808) Exits: (-1 + %n) LoopDispositions: { %loop: Computable }
+; CHECK-NEXT:    %iv.next = getelementptr inbounds %struct.S, ptr %iv, i64 1
+; CHECK-NEXT:    --> {(16 + %p),+,16}<nw><%loop> U: full-set S: full-set Exits: ((16 * %n) + %p) LoopDispositions: { %loop: Computable }
+; CHECK-NEXT:    %i.next = add nuw nsw i64 %i, 1
+; CHECK-NEXT:    --> {1,+,1}<nuw><nsw><%loop> U: [1,-9223372036854775808) S: [1,-9223372036854775808) Exits: %n LoopDispositions: { %loop: Computable }
+; CHECK-NEXT:  Determining loop execution counts for: @gep_iv_struct_single_index
+; CHECK-NEXT:  Loop %loop: backedge-taken count is (-1 + %n)
+; CHECK-NEXT:  Loop %loop: constant max backedge-taken count is i64 -1
+; CHECK-NEXT:  Loop %loop: symbolic max backedge-taken count is (-1 + %n)
+; CHECK-NEXT:  Loop %loop: Trip multiple is 1
+;
+entry:
+  br label %loop
+
+loop:
+  %iv = phi ptr [ %p, %entry ], [ %iv.next, %loop ]
+  %i = phi i64 [ 0, %entry ], [ %i.next, %loop ]
+  %iv.next = getelementptr inbounds %struct.S, ptr %iv, i64 1
+  %i.next = add nuw nsw i64 %i, 1
+  store i32 0, ptr %iv, align 4
+  %ec = icmp eq i64 %i.next, %n
+  br i1 %ec, label %exit, label %loop
+
+exit:
+  ret void
+}
+
+; The step is zero, so the PHI is loop-invariant. This is more precise than the
+; general route, which cannot form an add recurrence here.
+define void @gep_iv_zero_step(ptr %p, i64 %n) {
+; CHECK-LABEL: 'gep_iv_zero_step'
+; CHECK-NEXT:  Classifying expressions for: @gep_iv_zero_step
+; CHECK-NEXT:    %iv = phi ptr [ %p, %entry ], [ %iv.next, %loop ]
+; CHECK-NEXT:    --> %p U: full-set S: full-set Exits: %p LoopDispositions: { %loop: Invariant }
+; CHECK-NEXT:    %i = phi i64 [ 0, %entry ], [ %i.next, %loop ]
+; CHECK-NEXT:    --> {0,+,1}<nuw><nsw><%loop> U: [0,-9223372036854775808) S: [0,-9223372036854775808) Exits: (-1 + %n) LoopDispositions: { %loop: Computable }
+; CHECK-NEXT:    %iv.next = getelementptr inbounds i32, ptr %iv, i64 0
+; CHECK-NEXT:    --> %p U: full-set S: full-set Exits: %p LoopDispositions: { %loop: Invariant }
+; CHECK-NEXT:    %i.next = add nuw nsw i64 %i, 1
+; CHECK-NEXT:    --> {1,+,1}<nuw><nsw><%loop> U: [1,-9223372036854775808) S: [1,-9223372036854775808) Exits: %n LoopDispositions: { %loop: Computable }
+; CHECK-NEXT:  Determining loop execution counts for: @gep_iv_zero_step
+; CHECK-NEXT:  Loop %loop: backedge-taken count is (-1 + %n)
+; CHECK-NEXT:  Loop %loop: constant max backedge-taken count is i64 -1
+; CHECK-NEXT:  Loop %loop: symbolic max backedge-taken count is (-1 + %n)
+; CHECK-NEXT:  Loop %loop: Trip multiple is 1
+;
+entry:
+  br label %loop
+
+loop:
+  %iv = phi ptr [ %p, %entry ], [ %iv.next, %loop ]
+  %i = phi i64 [ 0, %entry ], [ %i.next, %loop ]
+  %iv.next = getelementptr inbounds i32, ptr %iv, i64 0
+  %i.next = add nuw nsw i64 %i, 1
+  store i32 0, ptr %iv, align 4
+  %ec = icmp eq i64 %i.next, %n
+  br i1 %ec, label %exit, label %loop
+
+exit:
+  ret void
+}
+
+; More than one index.
+define void @gep_iv_struct_two_indices(ptr %p, i64 %n) {
+; CHECK-LABEL: 'gep_iv_struct_two_indices'
+; CHECK-NEXT:  Classifying expressions for: @gep_iv_struct_two_indices
+; CHECK-NEXT:    %iv = phi ptr [ %p, %entry ], [ %iv.next, %loop ]
+; CHECK-NEXT:    --> {%p,+,16}<nuw><%loop> U: full-set S: full-set Exits: (-16 + (16 * %n) + %p) LoopDispositions: { %loop: Computable }
+; CHECK-NEXT:    %i = phi i64 [ 0, %entry ], [ %i.next, %loop ]
+; CHECK-NEXT:    --> {0,+,1}<nuw><nsw><%loop> U: [0,-9223372036854775808) S: [0,-9223372036854775808) Exits: (-1 + %n) LoopDispositions: { %loop: Computable }
+; CHECK-NEXT:    %iv.next = getelementptr inbounds %struct.S, ptr %iv, i64 1, i32 0
+; CHECK-NEXT:    --> {(16 + %p),+,16}<nw><%loop> U: full-set S: full-set Exits: ((16 * %n) + %p) LoopDispositions: { %loop: Computable }
+; CHECK-NEXT:    %i.next = add nuw nsw i64 %i, 1
+; CHECK-NEXT:    --> {1,+,1}<nuw><nsw><%loop> U: [1,-9223372036854775808) S: [1,-9223372036854775808) Exits: %n LoopDispositions: { %loop: Computable }
+; CHECK-NEXT:  Determining loop execution counts for: @gep_iv_struct_two_indices
+; CHECK-NEXT:  Loop %loop: backedge-taken count is (-1 + %n)
+; CHECK-NEXT:  Loop %loop: constant max backedge-taken count is i64 -1
+; CHECK-NEXT:  Loop %loop: symbolic max backedge-taken count is (-1 + %n)
+; CHECK-NEXT:  Loop %loop: Trip multiple is 1
+;
+entry:
+  br label %loop
+
+loop:
+  %iv = phi ptr [ %p, %entry ], [ %iv.next, %loop ]
+  %i = phi i64 [ 0, %entry ], [ %i.next, %loop ]
+  %iv.next = getelementptr inbounds %struct.S, ptr %iv, i64 1, i32 0
+  %i.next = add nuw nsw i64 %i, 1
+  store i32 0, ptr %iv, align 4
+  %ec = icmp eq i64 %i.next, %n
+  br i1 %ec, label %exit, label %loop
+
+exit:
+  ret void
+}
+
+; The index is not loop-invariant.
+define void @gep_iv_variant_step(ptr %p, i64 %n) {
+; CHECK-LABEL: 'gep_iv_variant_step'
+; CHECK-NEXT:  Classifying expressions for: @gep_iv_variant_step
+; CHECK-NEXT:    %iv = phi ptr [ %p, %entry ], [ %iv.next, %loop ]
+; CHECK-NEXT:    --> {%p,+,0,+,1}<%loop> U: full-set S: full-set Exits: ((trunc i65 (((zext i64 (-2 + %n) to i65) * (zext i64 (-1 + %n) to i65)) /u 2) to i64) + %p) LoopDispositions: { %loop: Computable }
+; CHECK-NEXT:    %i = phi i64 [ 0, %entry ], [ %i.next, %loop ]
+; CHECK-NEXT:    --> {0,+,1}<nuw><nsw><%loop> U: [0,-9223372036854775808) S: [0,-9223372036854775808) Exits: (-1 + %n) LoopDispositions: { %loop: Computable }
+; CHECK-NEXT:    %iv.next = getelementptr i8, ptr %iv, i64 %i
+; CHECK-NEXT:    --> {%p,+,1,+,1}<%loop> U: full-set S: full-set Exits: (-1 + (trunc i65 (((zext i64 (-2 + %n) to i65) * (zext i64 (-1 + %n) to i65)) /u 2) to i64) + %n + %p) LoopDispositions: { %loop: Computable }
+; CHECK-NEXT:    %i.next = add nuw nsw i64 %i, 1
+; CHECK-NEXT:    --> {1,+,1}<nuw><nsw><%loop> U: [1,-922...
[truncated]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

llvm:analysis Includes value tracking, cost tables and constant folding

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant