[llvm] [ConstantFolding] Compare a global and an inbounds GEP (PR #192909)

via llvm-commits llvm-commits at lists.llvm.org
Sun May 10 09:51:49 PDT 2026


https://github.com/mwb-cde updated https://github.com/llvm/llvm-project/pull/192909

>From d4452f45288f879cdb692c2cad8c5d0ae2d83cde Mon Sep 17 00:00:00 2001
From: "mwb.cde" <mwb.cde at gmail.com>
Date: Sun, 19 Apr 2026 09:38:52 +0100
Subject: [PATCH] [ConstantFolding] Compare a global and an inbounds GEP

An inbounds GEP with constant, non-zero indices can sometimes be
compared against the address of a global. Try to use this when
constant-folding compares.

Fixes https://github.com/llvm/llvm-project/issues/48801
---
 llvm/lib/Analysis/ConstantFolding.cpp         |  58 ++++++++++
 .../Transforms/EarlyCSE/cmp-gep-global.ll     | 104 ++++++++++++++++++
 .../binop-select-cast-of-select-cond.ll       |   4 +-
 ...hoist-xor-by-constant-from-xor-by-value.ll |   5 +-
 llvm/test/Transforms/InstSimplify/compare.ll  |  14 ++-
 5 files changed, 176 insertions(+), 9 deletions(-)
 create mode 100644 llvm/test/Transforms/EarlyCSE/cmp-gep-global.ll

diff --git a/llvm/lib/Analysis/ConstantFolding.cpp b/llvm/lib/Analysis/ConstantFolding.cpp
index e035cd1bc1ac0..b16961bac386e 100644
--- a/llvm/lib/Analysis/ConstantFolding.cpp
+++ b/llvm/lib/Analysis/ConstantFolding.cpp
@@ -1328,6 +1328,31 @@ Constant *llvm::ConstantFoldInstOperands(const Instruction *I,
                                       AllowNonDeterministic);
 }
 
+// Try to decide if an inbounds GEP is entirely inside the object it points to.
+// Return std::nullopt if not possible.
+static std::optional<bool> isGEPInObject(const GEPOperator *GEP,
+                                         const DataLayout &DL) {
+  if (!GEP->isInBounds() || !GEP->hasAllConstantIndices()) {
+    return std::nullopt;
+  }
+  if (const auto *GVar = dyn_cast<GlobalVariable>(GEP->getPointerOperand())) {
+    if (GVar->isInterposable() || GVar->hasGlobalUnnamedAddr() ||
+        isa<GlobalAlias>(GVar))
+      return std::nullopt;
+    Type *GVType = GVar->getValueType();
+    if (!GVType->isSized() || GVType->isEmptyTy())
+      return std::nullopt;
+    const auto Bitwidth = DL.getIndexTypeSizeInBits(GEP->getType());
+    APInt ObjectSize(Bitwidth, GVar->getGlobalSize(DL));
+    APInt GEPOffset(Bitwidth, 0);
+    GEP->accumulateConstantOffset(DL, GEPOffset);
+    if (GEPOffset.ult(ObjectSize))
+      return true;
+    return false;
+  }
+  return false;
+}
+
 Constant *llvm::ConstantFoldCompareInstOperands(
     unsigned IntPredicate, Constant *Ops0, Constant *Ops1, const DataLayout &DL,
     const TargetLibraryInfo *TLI, const Instruction *I) {
@@ -1420,6 +1445,39 @@ Constant *llvm::ConstantFoldCompareInstOperands(
             Ops0->getContext(),
             ICmpInst::compare(Offset0, Offset1,
                               ICmpInst::getSignedPredicate(Predicate)));
+
+      // Try to fold a GEP/GlobalVariable comparison.
+      if (IsEqPred && isa<GlobalVariable>(Stripped0) &&
+          isa<GlobalVariable>(Stripped1)) {
+        const auto CanFoldPtr = [](Constant *Op) -> bool {
+          if (const auto *GV = dyn_cast<GlobalVariable>(Op)) {
+            if (GV->isInterposable() || GV->hasGlobalUnnamedAddr() ||
+                isa<GlobalAlias>(GV))
+              return false;
+            Type *GVType = GV->getValueType();
+            if (GVType->isSized() && !GVType->isEmptyTy())
+              return true;
+          }
+          return false;
+        };
+        bool InObject0 = CanFoldPtr(Ops0);
+        if (const auto *GEP = dyn_cast<GEPOperator>(Ops0)) {
+          if (auto CanDecide = isGEPInObject(GEP, DL))
+            InObject0 = *CanDecide;
+          else
+            InObject0 = false;
+        }
+        bool InObject1 = CanFoldPtr(Ops1);
+        if (const auto *GEP = dyn_cast<GEPOperator>(Ops1)) {
+          if (auto CanDecide = isGEPInObject(GEP, DL))
+            InObject1 = *CanDecide;
+          else
+            InObject1 = false;
+        }
+        if (InObject0 && InObject1)
+          return ConstantInt::getBool(Ops0->getContext(),
+                                      Predicate == CmpInst::ICMP_NE);
+      }
     }
   } else if (isa<ConstantExpr>(Ops1)) {
     // If RHS is a constant expression, but the left side isn't, swap the
diff --git a/llvm/test/Transforms/EarlyCSE/cmp-gep-global.ll b/llvm/test/Transforms/EarlyCSE/cmp-gep-global.ll
new file mode 100644
index 0000000000000..9faad7a94942f
--- /dev/null
+++ b/llvm/test/Transforms/EarlyCSE/cmp-gep-global.ll
@@ -0,0 +1,104 @@
+; RUN: opt -S -passes=early-cse < %s | FileCheck %s
+
+%struct.anon = type { i32, i32 }
+
+declare void @foo() #2
+
+ at d = global %struct.anon zeroinitializer, align 4
+ at c = global %struct.anon zeroinitializer, align 4
+
+define i32 @test_different() #0 {
+; CHECK-LABEL: @test_different
+; CHECK-NEXT: entry:
+; CHECK-NEXT: br i1 false, label %{{.+}}, label %{{.+}}
+entry:
+  %cmp = icmp eq ptr @c, @d
+  br i1 %cmp, label %if.then, label %if.end
+
+if.then:                                          ; preds = %entry
+  call void @foo()
+  br label %if.end
+
+if.end:                                           ; preds = %if.then, %entry
+  ret i32 0
+}
+
+define i32 @test_different_first() #0 {
+; CHECK-LABEL: @test_different_first
+; CHECK-NEXT: entry:
+; CHECK-NEXT: br i1 false, label %{{.+}}, label %{{.+}}
+entry:
+  %cmp = icmp eq ptr @c, @d
+  br i1 %cmp, label %if.then, label %if.end
+
+if.then:                                          ; preds = %entry
+  call void @foo()
+  br label %if.end
+
+if.end:                                           ; preds = %if.then, %entry
+  ret i32 0
+}
+
+define i32 @test_different_offset() #0 {
+; CHECK-LABEL: @test_different_offset
+; CHECK-NEXT: entry:
+; CHECK-NEXT: br i1 false, label %{{.+}}, label %{{.+}}
+entry:
+  %cmp = icmp eq ptr @c, getelementptr inbounds nuw (i8, ptr @d, i64 4)
+  br i1 %cmp, label %if.then, label %if.end
+
+if.then:                                          ; preds = %entry
+  call void @foo()
+  br label %if.end
+
+if.end:                                           ; preds = %if.then, %entry
+  ret i32 0
+}
+
+define i32 @test_same() #0 {
+; CHECK-LABEL: @test_same
+; CHECK-NEXT: entry:
+; CHECK-NEXT: br i1 true, label %{{.+}}, label %{{.+}}
+entry:
+  %cmp = icmp eq ptr @c, @c
+  br i1 %cmp, label %if.then, label %if.end
+
+if.then:                                          ; preds = %entry
+  call void @foo()
+  br label %if.end
+
+if.end:                                           ; preds = %if.then, %entry
+  ret i32 0
+}
+
+define i32 @test_same_first() #0 {
+; CHECK-LABEL: @test_same_first
+; CHECK-NEXT: entry:
+; CHECK-NEXT: br i1 true, label %{{.+}}, label %{{.+}}
+entry:
+  %cmp = icmp eq ptr @c, @c
+  br i1 %cmp, label %if.then, label %if.end
+
+if.then:                                          ; preds = %entry
+  call void @foo()
+  br label %if.end
+
+if.end:                                           ; preds = %if.then, %entry
+  ret i32 0
+}
+
+define i32 @test_same_offset() #0 {
+; CHECK-LABEL: @test_same_offset
+; CHECK-NEXT: entry:
+; CHECK-NEXT: br i1 false, label %{{.+}}, label %{{.+}}
+entry:
+  %cmp = icmp eq ptr @c, getelementptr inbounds nuw (i8, ptr @c, i64 4)
+  br i1 %cmp, label %if.then, label %if.end
+
+if.then:                                          ; preds = %entry
+  call void @foo()
+  br label %if.end
+
+if.end:                                           ; preds = %if.then, %entry
+  ret i32 0
+}
diff --git a/llvm/test/Transforms/InstCombine/binop-select-cast-of-select-cond.ll b/llvm/test/Transforms/InstCombine/binop-select-cast-of-select-cond.ll
index 41367d55d8894..8d61cab73e549 100644
--- a/llvm/test/Transforms/InstCombine/binop-select-cast-of-select-cond.ll
+++ b/llvm/test/Transforms/InstCombine/binop-select-cast-of-select-cond.ll
@@ -231,9 +231,7 @@ define <2 x i8> @vectorized_add(<2 x i1> %c, <2 x i8> %arg) {
 define i64 @pr64669(i64 %a) {
 ; CHECK-LABEL: define i64 @pr64669
 ; CHECK-SAME: (i64 [[A:%.*]]) {
-; CHECK-NEXT:    [[CMP_NOT:%.*]] = icmp eq ptr getelementptr inbounds nuw (i8, ptr @b, i64 100), @c
-; CHECK-NEXT:    [[TMP1:%.*]] = add i64 [[A]], 1
-; CHECK-NEXT:    [[ADD:%.*]] = select i1 [[CMP_NOT]], i64 0, i64 [[TMP1]]
+; CHECK-NEXT:    [[ADD:%.*]] = add nsw i64 [[A]], 1
 ; CHECK-NEXT:    ret i64 [[ADD]]
 ;
   %cmp = icmp ne ptr getelementptr inbounds ([72 x i32], ptr @b, i64 0, i64 25), @c
diff --git a/llvm/test/Transforms/InstCombine/hoist-xor-by-constant-from-xor-by-value.ll b/llvm/test/Transforms/InstCombine/hoist-xor-by-constant-from-xor-by-value.ll
index 804843779b25e..395be457cf90a 100644
--- a/llvm/test/Transforms/InstCombine/hoist-xor-by-constant-from-xor-by-value.ll
+++ b/llvm/test/Transforms/InstCombine/hoist-xor-by-constant-from-xor-by-value.ll
@@ -94,12 +94,9 @@ entry:
 
 define i16 @constantexpr2() {
 ; CHECK-LABEL: @constantexpr2(
-; CHECK-NEXT:    [[I0:%.*]] = icmp ne ptr getelementptr inbounds nuw (i8, ptr @global_constant3, i64 40), @global_constant4
-; CHECK-NEXT:    [[I1:%.*]] = zext i1 [[I0]] to i16
 ; CHECK-NEXT:    [[I2:%.*]] = load ptr, ptr @global_constant5, align 1
 ; CHECK-NEXT:    [[I3:%.*]] = load i16, ptr [[I2]], align 1
-; CHECK-NEXT:    [[I4:%.*]] = xor i16 [[I3]], [[I1]]
-; CHECK-NEXT:    [[I5:%.*]] = xor i16 [[I4]], -1
+; CHECK-NEXT:    [[I5:%.*]] = xor i16 [[I3]], -2
 ; CHECK-NEXT:    ret i16 [[I5]]
 ;
   %i0 = icmp ne ptr getelementptr inbounds ([6 x [1 x i64]], ptr @global_constant3, i16 0, i16 5, i16 0), @global_constant4
diff --git a/llvm/test/Transforms/InstSimplify/compare.ll b/llvm/test/Transforms/InstSimplify/compare.ll
index ba10ea532a34a..26100848d1f23 100644
--- a/llvm/test/Transforms/InstSimplify/compare.ll
+++ b/llvm/test/Transforms/InstSimplify/compare.ll
@@ -3357,8 +3357,7 @@ define i1 @globals_inequal() {
 ; TODO: Never equal
 define i1 @globals_offset_inequal() {
 ; CHECK-LABEL: @globals_offset_inequal(
-; CHECK-NEXT:    [[RES:%.*]] = icmp ne ptr getelementptr inbounds nuw (i8, ptr @A, i32 1), getelementptr inbounds nuw (i8, ptr @B, i32 1)
-; CHECK-NEXT:    ret i1 [[RES]]
+; CHECK-NEXT:    ret i1 true
 ;
   %a.off = getelementptr i8, ptr @A, i32 1
   %b.off = getelementptr i8, ptr @B, i32 1
@@ -3366,6 +3365,17 @@ define i1 @globals_offset_inequal() {
   ret i1 %res
 }
 
+define i1 @globals_offset_past_end_inequal() {
+; CHECK-LABEL: @globals_offset_past_end_inequal(
+; CHECK-NEXT:    [[RES:%.*]] = icmp ne ptr getelementptr inbounds nuw (i8, ptr @A, i32 4), getelementptr inbounds nuw (i8, ptr @B, i32 4)
+; CHECK-NEXT:    ret i1 [[RES]]
+;
+  %a.off = getelementptr i32, ptr @A, i32 1
+  %b.off = getelementptr i32, ptr @B, i32 1
+  %res = icmp ne ptr %a.off, %b.off
+  ret i1 %res
+}
+
 
 ; Never equal
 define i1 @test_byval_global_inequal(ptr byval(i32) %a) {



More information about the llvm-commits mailing list