[llvm] [InstCombine] Rewrite multi-use GEPs when simplifying comparison (PR #146100)

Nikita Popov via llvm-commits llvm-commits at lists.llvm.org
Fri Jun 27 08:54:01 PDT 2025


https://github.com/nikic created https://github.com/llvm/llvm-project/pull/146100

We already do this when both sides are a GEP, but not if only one is. This ensures that the offset arithmetic is not duplicated.

>From c503c5e26baf942773379e1b445e6730066005a9 Mon Sep 17 00:00:00 2001
From: Nikita Popov <npopov at redhat.com>
Date: Fri, 27 Jun 2025 17:44:59 +0200
Subject: [PATCH 1/2] test

---
 llvm/test/Transforms/InstCombine/icmp-gep.ll | 27 ++++++++++++++++++++
 1 file changed, 27 insertions(+)

diff --git a/llvm/test/Transforms/InstCombine/icmp-gep.ll b/llvm/test/Transforms/InstCombine/icmp-gep.ll
index 260462896c39d..20258146d0ac8 100644
--- a/llvm/test/Transforms/InstCombine/icmp-gep.ll
+++ b/llvm/test/Transforms/InstCombine/icmp-gep.ll
@@ -221,6 +221,33 @@ define i1 @eq_base_inbounds_commute_use(i64 %y) {
   ret i1 %r
 }
 
+define i1 @ne_base_inbounds_use_scaled(ptr %x, i64 %y) {
+; CHECK-LABEL: @ne_base_inbounds_use_scaled(
+; CHECK-NEXT:    [[G:%.*]] = getelementptr inbounds i64, ptr [[X:%.*]], i64 [[Y:%.*]]
+; CHECK-NEXT:    call void @use(ptr [[G]])
+; CHECK-NEXT:    [[R:%.*]] = icmp ne i64 [[Y]], 0
+; CHECK-NEXT:    ret i1 [[R]]
+;
+  %g = getelementptr inbounds i64, ptr %x, i64 %y
+  call void @use(ptr %g)
+  %r = icmp ne ptr %g, %x
+  ret i1 %r
+}
+
+define i1 @ne_base_use_scaled(ptr %x, i64 %y) {
+; CHECK-LABEL: @ne_base_use_scaled(
+; CHECK-NEXT:    [[G:%.*]] = getelementptr i64, ptr [[X:%.*]], i64 [[Y:%.*]]
+; CHECK-NEXT:    call void @use(ptr [[G]])
+; CHECK-NEXT:    [[G_IDX_MASK:%.*]] = and i64 [[Y]], 2305843009213693951
+; CHECK-NEXT:    [[R:%.*]] = icmp ne i64 [[G_IDX_MASK]], 0
+; CHECK-NEXT:    ret i1 [[R]]
+;
+  %g = getelementptr i64, ptr %x, i64 %y
+  call void @use(ptr %g)
+  %r = icmp ne ptr %g, %x
+  ret i1 %r
+}
+
 define i1 @eq_bitcast_base(ptr %p, i64 %x) {
 ; CHECK-LABEL: @eq_bitcast_base(
 ; CHECK-NEXT:    [[GEP_IDX_MASK:%.*]] = and i64 [[X:%.*]], 9223372036854775807

>From a9122c1e6a652860a7636e15ae5e227538161f47 Mon Sep 17 00:00:00 2001
From: Nikita Popov <npopov at redhat.com>
Date: Fri, 27 Jun 2025 17:42:15 +0200
Subject: [PATCH 2/2] rewrite multi-use geps during fold

---
 llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp  | 2 +-
 llvm/test/Transforms/InstCombine/icmp-gep.ll             | 7 ++++---
 llvm/test/Transforms/PhaseOrdering/loop-access-checks.ll | 3 ++-
 3 files changed, 7 insertions(+), 5 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index 0894ca92086f3..6de1f8558e8cd 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -711,7 +711,7 @@ Instruction *InstCombinerImpl::foldGEPICmp(GEPOperator *GEPLHS, Value *RHS,
   Value *PtrBase = GEPLHS->getOperand(0);
   if (PtrBase == RHS && CanFold(GEPLHS->getNoWrapFlags())) {
     // ((gep Ptr, OFFSET) cmp Ptr)   ---> (OFFSET cmp 0).
-    Value *Offset = EmitGEPOffset(GEPLHS);
+    Value *Offset = EmitGEPOffset(GEPLHS, /*RewriteGEP=*/true);
     return NewICmp(GEPLHS->getNoWrapFlags(), Offset,
                    Constant::getNullValue(Offset->getType()));
   }
diff --git a/llvm/test/Transforms/InstCombine/icmp-gep.ll b/llvm/test/Transforms/InstCombine/icmp-gep.ll
index 20258146d0ac8..1e48a38803fdb 100644
--- a/llvm/test/Transforms/InstCombine/icmp-gep.ll
+++ b/llvm/test/Transforms/InstCombine/icmp-gep.ll
@@ -223,7 +223,8 @@ define i1 @eq_base_inbounds_commute_use(i64 %y) {
 
 define i1 @ne_base_inbounds_use_scaled(ptr %x, i64 %y) {
 ; CHECK-LABEL: @ne_base_inbounds_use_scaled(
-; CHECK-NEXT:    [[G:%.*]] = getelementptr inbounds i64, ptr [[X:%.*]], i64 [[Y:%.*]]
+; CHECK-NEXT:    [[G_IDX:%.*]] = shl nsw i64 [[Y:%.*]], 3
+; CHECK-NEXT:    [[G:%.*]] = getelementptr inbounds i8, ptr [[X:%.*]], i64 [[G_IDX]]
 ; CHECK-NEXT:    call void @use(ptr [[G]])
 ; CHECK-NEXT:    [[R:%.*]] = icmp ne i64 [[Y]], 0
 ; CHECK-NEXT:    ret i1 [[R]]
@@ -236,9 +237,9 @@ define i1 @ne_base_inbounds_use_scaled(ptr %x, i64 %y) {
 
 define i1 @ne_base_use_scaled(ptr %x, i64 %y) {
 ; CHECK-LABEL: @ne_base_use_scaled(
-; CHECK-NEXT:    [[G:%.*]] = getelementptr i64, ptr [[X:%.*]], i64 [[Y:%.*]]
+; CHECK-NEXT:    [[G_IDX_MASK:%.*]] = shl i64 [[Y:%.*]], 3
+; CHECK-NEXT:    [[G:%.*]] = getelementptr i8, ptr [[X:%.*]], i64 [[G_IDX_MASK]]
 ; CHECK-NEXT:    call void @use(ptr [[G]])
-; CHECK-NEXT:    [[G_IDX_MASK:%.*]] = and i64 [[Y]], 2305843009213693951
 ; CHECK-NEXT:    [[R:%.*]] = icmp ne i64 [[G_IDX_MASK]], 0
 ; CHECK-NEXT:    ret i1 [[R]]
 ;
diff --git a/llvm/test/Transforms/PhaseOrdering/loop-access-checks.ll b/llvm/test/Transforms/PhaseOrdering/loop-access-checks.ll
index e53b8687af915..45f18dd567396 100644
--- a/llvm/test/Transforms/PhaseOrdering/loop-access-checks.ll
+++ b/llvm/test/Transforms/PhaseOrdering/loop-access-checks.ll
@@ -24,7 +24,8 @@ define void @test_fill_with_foreach([2 x i64] %elems.coerce) {
 ; CHECK-NEXT:    [[ELEMS_COERCE_FCA_0_EXTRACT:%.*]] = extractvalue [2 x i64] [[ELEMS_COERCE]], 0
 ; CHECK-NEXT:    [[TMP0:%.*]] = inttoptr i64 [[ELEMS_COERCE_FCA_0_EXTRACT]] to ptr
 ; CHECK-NEXT:    [[ELEMS_COERCE_FCA_1_EXTRACT:%.*]] = extractvalue [2 x i64] [[ELEMS_COERCE]], 1
-; CHECK-NEXT:    [[ADD_PTR_I:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 [[ELEMS_COERCE_FCA_1_EXTRACT]]
+; CHECK-NEXT:    [[ADD_PTR_I_IDX:%.*]] = shl nsw i64 [[ELEMS_COERCE_FCA_1_EXTRACT]], 2
+; CHECK-NEXT:    [[ADD_PTR_I:%.*]] = getelementptr inbounds i8, ptr [[TMP0]], i64 [[ADD_PTR_I_IDX]]
 ; CHECK-NEXT:    [[CMP_NOT_I_I_I_I:%.*]] = icmp slt i64 [[ELEMS_COERCE_FCA_1_EXTRACT]], 0
 ; CHECK-NEXT:    br i1 [[CMP_NOT_I_I_I_I]], label [[ERROR:%.*]], label [[FOR_COND_PREHEADER_SPLIT:%.*]]
 ; CHECK:       for.cond.preheader.split:



More information about the llvm-commits mailing list