Skip to content

[CVP] Remove Zero-Index GEP #144831

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

veera-sivarajan
Copy link
Contributor

Fixes: #137168

Proof: https://alive2.llvm.org/ce/z/Xoyiar

Replaces all uses of a GEP with its base pointer when all its indices are known to be in the range [0, 1).

This is the same transform as 87a0b1b but in CVP.

@llvmbot
Copy link
Member

llvmbot commented Jun 19, 2025

@llvm/pr-subscribers-llvm-transforms

Author: Veera (veera-sivarajan)

Changes

Fixes: #137168

Proof: https://alive2.llvm.org/ce/z/Xoyiar

Replaces all uses of a GEP with its base pointer when all its indices are known to be in the range [0, 1).

This is the same transform as 87a0b1b but in CVP.


Full diff: https://github.com/llvm/llvm-project/pull/144831.diff

2 Files Affected:

  • (modified) llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp (+21)
  • (added) llvm/test/Transforms/CorrelatedValuePropagation/gep.ll (+45)
diff --git a/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp b/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp
index 4627f537dc16b..11a3a35d1ae7c 100644
--- a/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp
+++ b/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp
@@ -47,6 +47,7 @@ using namespace llvm;
 
 #define DEBUG_TYPE "correlated-value-propagation"
 
+STATISTIC(NumGEPs,      "Number of geps propagated");
 STATISTIC(NumPhis,      "Number of phis propagated");
 STATISTIC(NumPhiCommon, "Number of phis deleted via common incoming value");
 STATISTIC(NumSelects,   "Number of selects propagated");
@@ -1252,6 +1253,23 @@ static bool processTrunc(TruncInst *TI, LazyValueInfo *LVI) {
   return Changed;
 }
 
+// remove a GEP if all indices are in range [0, 1)
+static bool processGEP(GetElementPtrInst *GEP, LazyValueInfo *LVI) {
+  for (unsigned i = 1, e = GEP->getNumOperands(); i != e; ++i) {
+    ConstantRange Range = LVI->getConstantRangeAtUse(GEP->getOperandUse(i),
+                                                     /*UndefAllowed=*/false);
+    const APInt *C = Range.getSingleElement();
+    if (!C || !C->isZero())
+      return false;
+  }
+
+  // all indices are zero
+  Value *Ptr = GEP->getPointerOperand();
+  GEP->replaceAllUsesWith(Ptr);
+  ++NumGEPs;
+  return true;
+}
+
 static bool runImpl(Function &F, LazyValueInfo *LVI, DominatorTree *DT,
                     const SimplifyQuery &SQ) {
   bool FnChanged = false;
@@ -1318,6 +1336,9 @@ static bool runImpl(Function &F, LazyValueInfo *LVI, DominatorTree *DT,
       case Instruction::Trunc:
         BBChanged |= processTrunc(cast<TruncInst>(&II), LVI);
         break;
+      case Instruction::GetElementPtr:
+        BBChanged |= processGEP(cast<GetElementPtrInst>(&II), LVI);
+        break;
       }
     }
 
diff --git a/llvm/test/Transforms/CorrelatedValuePropagation/gep.ll b/llvm/test/Transforms/CorrelatedValuePropagation/gep.ll
new file mode 100644
index 0000000000000..d5c122b3f665e
--- /dev/null
+++ b/llvm/test/Transforms/CorrelatedValuePropagation/gep.ll
@@ -0,0 +1,45 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt < %s -passes=correlated-propagation -S | FileCheck %s
+
+define ptr @noop_gep(ptr %ptr, i8 range(i8 0, 1) %index) {
+; CHECK-LABEL: define ptr @noop_gep(
+; CHECK-SAME: ptr [[PTR:%.*]], i8 range(i8 0, 1) [[INDEX:%.*]]) {
+; CHECK-NEXT:    [[GEP:%.*]] = getelementptr i1, ptr [[PTR]], i8 [[INDEX]]
+; CHECK-NEXT:    ret ptr [[PTR]]
+;
+  %gep = getelementptr i1, ptr %ptr, i8 %index
+  ret ptr %gep
+}
+
+
+define ptr @noop_gep_with_constant(ptr %ptr, i8 range(i8 0, 1) %index) {
+; CHECK-LABEL: define ptr @noop_gep_with_constant(
+; CHECK-SAME: ptr [[PTR:%.*]], i8 range(i8 0, 1) [[INDEX:%.*]]) {
+; CHECK-NEXT:    [[GEP:%.*]] = getelementptr [2 x i1], ptr [[PTR]], i8 0, i8 [[INDEX]]
+; CHECK-NEXT:    ret ptr [[PTR]]
+;
+  %gep = getelementptr [2 x i1], ptr %ptr, i8 0, i8 %index
+  ret ptr %gep
+}
+
+
+define ptr @noop_gep_nonzero_index_negative(ptr %ptr, i8 range(i8 0, 2) %index) {
+; CHECK-LABEL: define ptr @noop_gep_nonzero_index_negative(
+; CHECK-SAME: ptr [[PTR:%.*]], i8 range(i8 0, 2) [[INDEX:%.*]]) {
+; CHECK-NEXT:    [[GEP:%.*]] = getelementptr i1, ptr [[PTR]], i8 [[INDEX]]
+; CHECK-NEXT:    ret ptr [[GEP]]
+;
+  %gep = getelementptr i1, ptr %ptr, i8 %index
+  ret ptr %gep
+}
+
+
+define ptr @gep_no_range_negative(ptr %ptr, i8 %index) {
+; CHECK-LABEL: define ptr @gep_no_range_negative(
+; CHECK-SAME: ptr [[PTR:%.*]], i8 [[INDEX:%.*]]) {
+; CHECK-NEXT:    [[GEP:%.*]] = getelementptr i1, ptr [[PTR]], i8 [[INDEX]]
+; CHECK-NEXT:    ret ptr [[GEP]]
+;
+  %gep = getelementptr i1, ptr %ptr, i8 %index
+  ret ptr %gep
+}

Copy link

github-actions bot commented Jun 19, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.


// all indices are zero
Value *Ptr = GEP->getPointerOperand();
GEP->replaceAllUsesWith(Ptr);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't work for the form getelementptr ty, scalar ptr, vector indices. You can create a splat when the type of GEP differs from the type of source pointer.

Crash reproducer:

define <2 x ptr> @noop_gep_nonzero_index_vec(ptr %ptr, <2 x i64> range(i64 0, 1) %index) {
  %gep = getelementptr i8, ptr %ptr, <2 x i64> %index
  ret <2 x ptr> %gep
}

@@ -1252,6 +1253,23 @@ static bool processTrunc(TruncInst *TI, LazyValueInfo *LVI) {
return Changed;
}

// remove a GEP if all indices are in range [0, 1)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// remove a GEP if all indices are in range [0, 1)
/// Remove a GEP if all indices are in range [0, 1)

@@ -1252,6 +1253,23 @@ static bool processTrunc(TruncInst *TI, LazyValueInfo *LVI) {
return Changed;
}

// remove a GEP if all indices are in range [0, 1)
static bool processGEP(GetElementPtrInst *GEP, LazyValueInfo *LVI) {
for (unsigned i = 1, e = GEP->getNumOperands(); i != e; ++i) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for (unsigned i = 1, e = GEP->getNumOperands(); i != e; ++i) {
for (unsigned I = 1, E = GEP->getNumOperands(); I != E; ++I) {

// all indices are zero
Value *Ptr = GEP->getPointerOperand();
GEP->replaceAllUsesWith(Ptr);
++NumGEPs;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
++NumGEPs;
GEP->eraseFromParent();
++NumGEPs;

; CHECK-NEXT: [[GEP:%.*]] = getelementptr i1, ptr [[PTR]], i8 [[INDEX]]
; CHECK-NEXT: ret ptr [[PTR]]
;
%gep = getelementptr i1, ptr %ptr, i8 %index
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
%gep = getelementptr i1, ptr %ptr, i8 %index
%gep = getelementptr i8, ptr %ptr, i64 %index

Use i8 as the pointee type and i64 as the index type.

@@ -0,0 +1,45 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
; RUN: opt < %s -passes=correlated-propagation -S | FileCheck %s

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add the motivating case from #137168?

Copy link
Contributor

@nikic nikic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is nothing really GEP specific here -- this just uses LVI to determine whether values are constant. However, there is a reason why we don't actually do this very obvious optimization: Unlike SCCP (which does perform this type of optimization), LVI is very, very slow and we need to keep the number of values we query small.

Compile-time: https://llvm-compile-time-tracker.com/compare.php?from=a9a71b6d311892d6add6aab3790b20fe945cca38&to=f4918da327f8d1cb43fb6aa181c948f0f9e37979&stat=instructions:u

@nikic
Copy link
Contributor

nikic commented Jun 19, 2025

A possible way to fix the motivating issue is to enable use of PredicateInfo in non-IP SCCP. This will also have compile-time overhead, but probably less than this? We'd have to evaluate whether it is worthwhile.

Edit: PredicateInfo compile-time: https://llvm-compile-time-tracker.com/compare.php?from=a9a71b6d311892d6add6aab3790b20fe945cca38&to=e237b4a75e0eadb116cec3fa3227f7f2e95ed996&stat=instructions:u

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

Successfully merging this pull request may close these issues.

Missed GEP Optimization for Constant Index
4 participants