[llvm] [InstCombine] fold `gepi _, (srem x, y)` to `gepi _, (urem x, y)` if `y` is power-of-2 (PR #180148)

via llvm-commits llvm-commits at lists.llvm.org
Sun Feb 8 23:14:01 PST 2026


https://github.com/imkiva updated https://github.com/llvm/llvm-project/pull/180148

>From 14945a577fd9fe314cbc383906c2fd92d72b916e Mon Sep 17 00:00:00 2001
From: imkiva <zengtao at iscas.ac.cn>
Date: Fri, 6 Feb 2026 16:34:48 +0800
Subject: [PATCH 01/12] [InstCombine] fold `gepi i8 %ptr, (srem x, y)` to `gepi
 i8 %ptr, (urem x, y)` if `y` is positive power-of-2

---
 .../InstCombine/InstructionCombining.cpp      | 43 +++++++++++++++++++
 1 file changed, 43 insertions(+)

diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
index e2bda2450c66f..f1cc0196d449c 100644
--- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
@@ -50,6 +50,7 @@
 #include "llvm/Analysis/InstructionSimplify.h"
 #include "llvm/Analysis/LastRunTrackingAnalysis.h"
 #include "llvm/Analysis/LazyBlockFrequencyInfo.h"
+#include "llvm/Analysis/Loads.h"
 #include "llvm/Analysis/MemoryBuiltins.h"
 #include "llvm/Analysis/OptimizationRemarkEmitter.h"
 #include "llvm/Analysis/ProfileSummaryInfo.h"
@@ -3347,6 +3348,48 @@ Instruction *InstCombinerImpl::visitGetElementPtrInst(GetElementPtrInst &GEP) {
     }
   }
 
+  // srem -> (and/urem) for inbounds+nuw byte GEP with dereferenceable base ---
+  if (GEPEltType->isIntegerTy(8) && Indices.size() == 1 && GEP.isInBounds() &&
+      GEP.getNoWrapFlags().hasNoUnsignedWrap()) {
+
+    using namespace llvm::PatternMatch;
+
+    Value *X = nullptr;
+    ConstantInt *DivC = nullptr;
+
+    // Match: idx = srem X, C -- where C is a positive power-of-two constant.
+    if (match(Indices[0], m_SRem(m_Value(X), m_ConstantInt(DivC))) &&
+        DivC->getValue().isStrictlyPositive() &&
+        DivC->getValue().isPowerOf2()) {
+
+      uint64_t Div = DivC->getZExtValue();
+      APInt Size(DL.getIndexTypeSizeInBits(GEP.getType()), Div);
+
+      if (isDereferenceableAndAlignedPointer(PtrOp, Align(1), Size, DL, &GEP,
+                                             &AC, &DT, &TLI)) {
+        // If base is at least Div bytes dereferenceable and GEP is
+        // inbounds+nuw, index cannot be negative -> srem by power-of-two can be
+        // treated as urem, and urem by power-of-two folds to 'and'.
+        Instruction *OldIdxI = dyn_cast<Instruction>(Indices[0]);
+        Builder.SetInsertPoint(&GEP);
+        Value *Mask = ConstantInt::get(X->getType(), Div - 1);
+        Value *NewIdx = Builder.CreateAnd(X, Mask, "idx.mask");
+
+        auto *NewGEP = GetElementPtrInst::Create(GEPEltType, PtrOp, {NewIdx},
+                                                 GEP.getName(), &GEP);
+        NewGEP->setIsInBounds(GEP.isInBounds());
+        NewGEP->setNoWrapFlags(GEP.getNoWrapFlags());
+        NewGEP->setDebugLoc(GEP.getDebugLoc());
+
+        Instruction *Res = replaceInstUsesWith(GEP, NewGEP);
+        if (OldIdxI && OldIdxI->use_empty())
+          eraseInstFromFunction(*OldIdxI);
+
+        return Res;
+      }
+    }
+  }
+
   // Eliminate unneeded casts for indices, and replace indices which displace
   // by multiples of a zero size type with zero.
   bool MadeChange = false;

>From 67e0743d462b0e6d0e6cbedfd9489604e522b45d Mon Sep 17 00:00:00 2001
From: imkiva <zengtao at iscas.ac.cn>
Date: Fri, 6 Feb 2026 16:47:17 +0800
Subject: [PATCH 02/12] [InstCombine] add tests

---
 .../InstCombine/gep-srem-to-and-deref.ll      | 155 ++++++++++++++++++
 1 file changed, 155 insertions(+)
 create mode 100644 llvm/test/Transforms/InstCombine/gep-srem-to-and-deref.ll

diff --git a/llvm/test/Transforms/InstCombine/gep-srem-to-and-deref.ll b/llvm/test/Transforms/InstCombine/gep-srem-to-and-deref.ll
new file mode 100644
index 0000000000000..a6bf43601ce8a
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/gep-srem-to-and-deref.ll
@@ -0,0 +1,155 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -passes=instcombine %s -S | FileCheck %s
+
+;NOTE: tests InstCombine transform the following pattern:
+;   %idx = srem i64 %x, 2^k
+; from a dereferenceable(2^k) base, into:
+;   %idx = and i64 %x, (2^k-1)
+
+define ptr @pos_pow2_2(ptr dereferenceable(2) %foo, i64 noundef %x) {
+; CHECK-LABEL: define ptr @pos_pow2_2(
+; CHECK-SAME: ptr dereferenceable(2) [[FOO:%.*]], i64 noundef [[X:%.*]]) {
+; CHECK-NEXT:    [[IDX_MASK:%.*]] = and i64 [[X]], 1
+; CHECK-NEXT:    [[P1:%.*]] = getelementptr inbounds nuw i8, ptr [[FOO]], i64 [[IDX_MASK]]
+; CHECK-NEXT:    ret ptr [[P1]]
+;
+  %idx = srem i64 %x, 2
+  %p = getelementptr inbounds nuw i8, ptr %foo, i64 %idx
+  ret ptr %p
+}
+
+define ptr @pos_pow2_4(ptr dereferenceable(4) %foo, i64 noundef %x) {
+; CHECK-LABEL: define ptr @pos_pow2_4(
+; CHECK-SAME: ptr dereferenceable(4) [[FOO:%.*]], i64 noundef [[X:%.*]]) {
+; CHECK-NEXT:    [[IDX_MASK:%.*]] = and i64 [[X]], 3
+; CHECK-NEXT:    [[P1:%.*]] = getelementptr inbounds nuw i8, ptr [[FOO]], i64 [[IDX_MASK]]
+; CHECK-NEXT:    ret ptr [[P1]]
+;
+  %idx = srem i64 %x, 4
+  %p = getelementptr inbounds nuw i8, ptr %foo, i64 %idx
+  ret ptr %p
+}
+
+define ptr @pos_pow2_8_exact(ptr dereferenceable(8) %foo, i64 noundef %x) {
+; CHECK-LABEL: define ptr @pos_pow2_8_exact(
+; CHECK-SAME: ptr dereferenceable(8) [[FOO:%.*]], i64 noundef [[X:%.*]]) {
+; CHECK-NEXT:    [[IDX_MASK:%.*]] = and i64 [[X]], 7
+; CHECK-NEXT:    [[P1:%.*]] = getelementptr inbounds nuw i8, ptr [[FOO]], i64 [[IDX_MASK]]
+; CHECK-NEXT:    ret ptr [[P1]]
+;
+  %idx = srem i64 %x, 8
+  %p = getelementptr inbounds nuw i8, ptr %foo, i64 %idx
+  ret ptr %p
+}
+
+; dereferenceable(64) implies dereferenceable(8)
+define ptr @pos_pow2_8_more_deref(ptr dereferenceable(64) %foo, i64 noundef %x) {
+; CHECK-LABEL: define ptr @pos_pow2_8_more_deref(
+; CHECK-SAME: ptr dereferenceable(64) [[FOO:%.*]], i64 noundef [[X:%.*]]) {
+; CHECK-NEXT:    [[IDX_MASK:%.*]] = and i64 [[X]], 7
+; CHECK-NEXT:    [[P1:%.*]] = getelementptr inbounds nuw i8, ptr [[FOO]], i64 [[IDX_MASK]]
+; CHECK-NEXT:    ret ptr [[P1]]
+;
+  %idx = srem i64 %x, 8
+  %p = getelementptr inbounds nuw i8, ptr %foo, i64 %idx
+  ret ptr %p
+}
+
+; srem x, 1 is always 0, and mask is 0. GEP should be folded to %foo.
+define ptr @pos_pow2_1(ptr dereferenceable(1) %foo, i64 noundef %x) {
+; CHECK-LABEL: define ptr @pos_pow2_1(
+; CHECK-SAME: ptr dereferenceable(1) [[FOO:%.*]], i64 noundef [[X:%.*]]) {
+; CHECK-NEXT:    ret ptr [[FOO]]
+;
+  %idx = srem i64 %x, 1
+  %p = getelementptr inbounds nuw i8, ptr %foo, i64 %idx
+  ret ptr %p
+}
+
+; Non-power-of-two constant divisor
+define ptr @neg_non_pow2_6(ptr dereferenceable(6) %foo, i64 noundef %x) {
+; CHECK-LABEL: define ptr @neg_non_pow2_6(
+; CHECK-SAME: ptr dereferenceable(6) [[FOO:%.*]], i64 noundef [[X:%.*]]) {
+; CHECK-NEXT:    [[IDX:%.*]] = srem i64 [[X]], 6
+; CHECK-NEXT:    [[P:%.*]] = getelementptr inbounds nuw i8, ptr [[FOO]], i64 [[IDX]]
+; CHECK-NEXT:    ret ptr [[P]]
+;
+  %idx = srem i64 %x, 6
+  %p = getelementptr inbounds nuw i8, ptr %foo, i64 %idx
+  ret ptr %p
+}
+
+; Missing 'no unsigned wrap'
+define ptr @neg_missing_nuw(ptr dereferenceable(4) %foo, i64 noundef %x) {
+; CHECK-LABEL: define ptr @neg_missing_nuw(
+; CHECK-SAME: ptr dereferenceable(4) [[FOO:%.*]], i64 noundef [[X:%.*]]) {
+; CHECK-NEXT:    [[IDX:%.*]] = srem i64 [[X]], 4
+; CHECK-NEXT:    [[P:%.*]] = getelementptr inbounds i8, ptr [[FOO]], i64 [[IDX]]
+; CHECK-NEXT:    ret ptr [[P]]
+;
+  %idx = srem i64 %x, 4
+  %p = getelementptr inbounds i8, ptr %foo, i64 %idx
+  ret ptr %p
+}
+
+; Missing 'inbounds'
+define ptr @neg_missing_inbounds(ptr dereferenceable(4) %foo, i64 noundef %x) {
+; CHECK-LABEL: define ptr @neg_missing_inbounds(
+; CHECK-SAME: ptr dereferenceable(4) [[FOO:%.*]], i64 noundef [[X:%.*]]) {
+; CHECK-NEXT:    [[IDX:%.*]] = srem i64 [[X]], 4
+; CHECK-NEXT:    [[P:%.*]] = getelementptr nuw i8, ptr [[FOO]], i64 [[IDX]]
+; CHECK-NEXT:    ret ptr [[P]]
+;
+  %idx = srem i64 %x, 4
+  %p = getelementptr nuw i8, ptr %foo, i64 %idx
+  ret ptr %p
+}
+
+; Base dereferenceable is too small
+define ptr @neg_deref_too_small(ptr dereferenceable(3) %foo, i64 noundef %x) {
+; CHECK-LABEL: define ptr @neg_deref_too_small(
+; CHECK-SAME: ptr dereferenceable(3) [[FOO:%.*]], i64 noundef [[X:%.*]]) {
+; CHECK-NEXT:    [[IDX:%.*]] = srem i64 [[X]], 4
+; CHECK-NEXT:    [[P:%.*]] = getelementptr inbounds nuw i8, ptr [[FOO]], i64 [[IDX]]
+; CHECK-NEXT:    ret ptr [[P]]
+;
+  %idx = srem i64 %x, 4
+  %p = getelementptr inbounds nuw i8, ptr %foo, i64 %idx
+  ret ptr %p
+}
+
+define ptr @neg_deref_or_null(ptr dereferenceable_or_null(4) %foo, i64 noundef %x) {
+; CHECK-LABEL: define ptr @neg_deref_or_null(
+; CHECK-SAME: ptr dereferenceable_or_null(4) [[FOO:%.*]], i64 noundef [[X:%.*]]) {
+; CHECK-NEXT:    [[IDX:%.*]] = srem i64 [[X]], 4
+; CHECK-NEXT:    [[P:%.*]] = getelementptr inbounds nuw i8, ptr [[FOO]], i64 [[IDX]]
+; CHECK-NEXT:    ret ptr [[P]]
+;
+  %idx = srem i64 %x, 4
+  %p = getelementptr inbounds nuw i8, ptr %foo, i64 %idx
+  ret ptr %p
+}
+
+define ptr @neg_nonconst_divisor(ptr dereferenceable(8) %foo, i64 noundef %x, i64 noundef %d) {
+; CHECK-LABEL: define ptr @neg_nonconst_divisor(
+; CHECK-SAME: ptr dereferenceable(8) [[FOO:%.*]], i64 noundef [[X:%.*]], i64 noundef [[D:%.*]]) {
+; CHECK-NEXT:    [[IDX:%.*]] = srem i64 [[X]], [[D]]
+; CHECK-NEXT:    [[P:%.*]] = getelementptr inbounds nuw i8, ptr [[FOO]], i64 [[IDX]]
+; CHECK-NEXT:    ret ptr [[P]]
+;
+  %idx = srem i64 %x, %d
+  %p = getelementptr inbounds nuw i8, ptr %foo, i64 %idx
+  ret ptr %p
+}
+
+define ptr @neg_not_i8(ptr dereferenceable(16) %foo, i64 noundef %x) {
+; CHECK-LABEL: define ptr @neg_not_i8(
+; CHECK-SAME: ptr dereferenceable(16) [[FOO:%.*]], i64 noundef [[X:%.*]]) {
+; CHECK-NEXT:    [[IDX:%.*]] = srem i64 [[X]], 4
+; CHECK-NEXT:    [[P:%.*]] = getelementptr inbounds nuw i32, ptr [[FOO]], i64 [[IDX]]
+; CHECK-NEXT:    ret ptr [[P]]
+;
+  %idx = srem i64 %x, 4
+  %p = getelementptr inbounds nuw i32, ptr %foo, i64 %idx
+  ret ptr %p
+}

>From 0d6858a367abfc2919bb0f88f59f3c4e5416f407 Mon Sep 17 00:00:00 2001
From: imkiva <zengtao at iscas.ac.cn>
Date: Fri, 6 Feb 2026 17:07:28 +0800
Subject: [PATCH 03/12] [InstCombine] create urem instead of and for code reuse

---
 llvm/lib/Transforms/InstCombine/InstructionCombining.cpp | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
index f1cc0196d449c..ea3198372b9c9 100644
--- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
@@ -3369,11 +3369,10 @@ Instruction *InstCombinerImpl::visitGetElementPtrInst(GetElementPtrInst &GEP) {
                                              &AC, &DT, &TLI)) {
         // If base is at least Div bytes dereferenceable and GEP is
         // inbounds+nuw, index cannot be negative -> srem by power-of-two can be
-        // treated as urem, and urem by power-of-two folds to 'and'.
+        // treated as urem, and urem by power-of-two folds to 'and' later.
         Instruction *OldIdxI = dyn_cast<Instruction>(Indices[0]);
         Builder.SetInsertPoint(&GEP);
-        Value *Mask = ConstantInt::get(X->getType(), Div - 1);
-        Value *NewIdx = Builder.CreateAnd(X, Mask, "idx.mask");
+        Value *NewIdx = Builder.CreateURem(X, DivC, OldIdxI->getName());
 
         auto *NewGEP = GetElementPtrInst::Create(GEPEltType, PtrOp, {NewIdx},
                                                  GEP.getName(), &GEP);

>From 132d413d9b87c3dc02178de98bedd51638e494db Mon Sep 17 00:00:00 2001
From: imkiva <zengtao at iscas.ac.cn>
Date: Fri, 6 Feb 2026 18:16:14 +0800
Subject: [PATCH 04/12] [Build][NFC] fix CI by not using a deprecated function

---
 llvm/lib/Transforms/InstCombine/InstructionCombining.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
index ea3198372b9c9..2bf9ba8c1ba74 100644
--- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
@@ -3375,7 +3375,7 @@ Instruction *InstCombinerImpl::visitGetElementPtrInst(GetElementPtrInst &GEP) {
         Value *NewIdx = Builder.CreateURem(X, DivC, OldIdxI->getName());
 
         auto *NewGEP = GetElementPtrInst::Create(GEPEltType, PtrOp, {NewIdx},
-                                                 GEP.getName(), &GEP);
+                                                 GEP.getName(), GEP.getIterator());
         NewGEP->setIsInBounds(GEP.isInBounds());
         NewGEP->setNoWrapFlags(GEP.getNoWrapFlags());
         NewGEP->setDebugLoc(GEP.getDebugLoc());

>From bf5fa5322133abaa1d64ad034056129201ab25f2 Mon Sep 17 00:00:00 2001
From: imkiva <zengtao at iscas.ac.cn>
Date: Fri, 6 Feb 2026 18:19:52 +0800
Subject: [PATCH 05/12] [NFC] apply clang-format

---
 llvm/lib/Transforms/InstCombine/InstructionCombining.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
index 2bf9ba8c1ba74..5a551f2f8fb23 100644
--- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
@@ -3374,8 +3374,8 @@ Instruction *InstCombinerImpl::visitGetElementPtrInst(GetElementPtrInst &GEP) {
         Builder.SetInsertPoint(&GEP);
         Value *NewIdx = Builder.CreateURem(X, DivC, OldIdxI->getName());
 
-        auto *NewGEP = GetElementPtrInst::Create(GEPEltType, PtrOp, {NewIdx},
-                                                 GEP.getName(), GEP.getIterator());
+        auto *NewGEP = GetElementPtrInst::Create(
+            GEPEltType, PtrOp, {NewIdx}, GEP.getName(), GEP.getIterator());
         NewGEP->setIsInBounds(GEP.isInBounds());
         NewGEP->setNoWrapFlags(GEP.getNoWrapFlags());
         NewGEP->setDebugLoc(GEP.getDebugLoc());

>From 03d719a77208dab2b0b1a6f65cd6d496817a8309 Mon Sep 17 00:00:00 2001
From: imkiva <zengtao at iscas.ac.cn>
Date: Fri, 6 Feb 2026 18:26:26 +0800
Subject: [PATCH 06/12] [InstCombine] relax condition to allow all non-zero
 power-of-2 indices

---
 .../InstCombine/InstructionCombining.cpp      | 27 +++++++------------
 1 file changed, 10 insertions(+), 17 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
index 5a551f2f8fb23..3d1fcf39d7b53 100644
--- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
@@ -3348,31 +3348,24 @@ Instruction *InstCombinerImpl::visitGetElementPtrInst(GetElementPtrInst &GEP) {
     }
   }
 
-  // srem -> (and/urem) for inbounds+nuw byte GEP with dereferenceable base ---
+  // srem -> (and/urem) for inbounds+nuw byte GEP ---
   if (GEPEltType->isIntegerTy(8) && Indices.size() == 1 && GEP.isInBounds() &&
       GEP.getNoWrapFlags().hasNoUnsignedWrap()) {
 
     using namespace llvm::PatternMatch;
 
     Value *X = nullptr;
-    ConstantInt *DivC = nullptr;
-
-    // Match: idx = srem X, C -- where C is a positive power-of-two constant.
-    if (match(Indices[0], m_SRem(m_Value(X), m_ConstantInt(DivC))) &&
-        DivC->getValue().isStrictlyPositive() &&
-        DivC->getValue().isPowerOf2()) {
-
-      uint64_t Div = DivC->getZExtValue();
-      APInt Size(DL.getIndexTypeSizeInBits(GEP.getType()), Div);
-
-      if (isDereferenceableAndAlignedPointer(PtrOp, Align(1), Size, DL, &GEP,
-                                             &AC, &DT, &TLI)) {
-        // If base is at least Div bytes dereferenceable and GEP is
-        // inbounds+nuw, index cannot be negative -> srem by power-of-two can be
-        // treated as urem, and urem by power-of-two folds to 'and' later.
+    Value *Y = nullptr;
+
+    // Match: idx = srem X, Y -- where Y is a power-of-two constant.
+    if (match(Indices[0], m_SRem(m_Value(X), m_Value(Y)))) {
+      if (isKnownToBeAPowerOfTwo(Y, false, &GEP)) {
+        // If GEP is inbounds+nuw, the offset cannot be negative
+        // -> srem by power-of-two can be treated as urem,
+        // and urem by power-of-two folds to 'and' later.
         Instruction *OldIdxI = dyn_cast<Instruction>(Indices[0]);
         Builder.SetInsertPoint(&GEP);
-        Value *NewIdx = Builder.CreateURem(X, DivC, OldIdxI->getName());
+        Value *NewIdx = Builder.CreateURem(X, Y, OldIdxI->getName());
 
         auto *NewGEP = GetElementPtrInst::Create(
             GEPEltType, PtrOp, {NewIdx}, GEP.getName(), GEP.getIterator());

>From 139b00764a12ff41a862fb736379b869ea75a7c9 Mon Sep 17 00:00:00 2001
From: imkiva <zengtao at iscas.ac.cn>
Date: Fri, 6 Feb 2026 18:30:04 +0800
Subject: [PATCH 07/12] [InstCombine] update tests

---
 .../InstCombine/gep-srem-to-and-deref.ll      | 79 +++++--------------
 1 file changed, 18 insertions(+), 61 deletions(-)

diff --git a/llvm/test/Transforms/InstCombine/gep-srem-to-and-deref.ll b/llvm/test/Transforms/InstCombine/gep-srem-to-and-deref.ll
index a6bf43601ce8a..945f34412c1ba 100644
--- a/llvm/test/Transforms/InstCombine/gep-srem-to-and-deref.ll
+++ b/llvm/test/Transforms/InstCombine/gep-srem-to-and-deref.ll
@@ -1,14 +1,9 @@
 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
 ; RUN: opt -passes=instcombine %s -S | FileCheck %s
 
-;NOTE: tests InstCombine transform the following pattern:
-;   %idx = srem i64 %x, 2^k
-; from a dereferenceable(2^k) base, into:
-;   %idx = and i64 %x, (2^k-1)
-
-define ptr @pos_pow2_2(ptr dereferenceable(2) %foo, i64 noundef %x) {
+define ptr @pos_pow2_2(ptr %foo, i64 noundef %x) {
 ; CHECK-LABEL: define ptr @pos_pow2_2(
-; CHECK-SAME: ptr dereferenceable(2) [[FOO:%.*]], i64 noundef [[X:%.*]]) {
+; CHECK-SAME: ptr [[FOO:%.*]], i64 noundef [[X:%.*]]) {
 ; CHECK-NEXT:    [[IDX_MASK:%.*]] = and i64 [[X]], 1
 ; CHECK-NEXT:    [[P1:%.*]] = getelementptr inbounds nuw i8, ptr [[FOO]], i64 [[IDX_MASK]]
 ; CHECK-NEXT:    ret ptr [[P1]]
@@ -18,9 +13,9 @@ define ptr @pos_pow2_2(ptr dereferenceable(2) %foo, i64 noundef %x) {
   ret ptr %p
 }
 
-define ptr @pos_pow2_4(ptr dereferenceable(4) %foo, i64 noundef %x) {
+define ptr @pos_pow2_4(ptr %foo, i64 noundef %x) {
 ; CHECK-LABEL: define ptr @pos_pow2_4(
-; CHECK-SAME: ptr dereferenceable(4) [[FOO:%.*]], i64 noundef [[X:%.*]]) {
+; CHECK-SAME: ptr [[FOO:%.*]], i64 noundef [[X:%.*]]) {
 ; CHECK-NEXT:    [[IDX_MASK:%.*]] = and i64 [[X]], 3
 ; CHECK-NEXT:    [[P1:%.*]] = getelementptr inbounds nuw i8, ptr [[FOO]], i64 [[IDX_MASK]]
 ; CHECK-NEXT:    ret ptr [[P1]]
@@ -30,22 +25,9 @@ define ptr @pos_pow2_4(ptr dereferenceable(4) %foo, i64 noundef %x) {
   ret ptr %p
 }
 
-define ptr @pos_pow2_8_exact(ptr dereferenceable(8) %foo, i64 noundef %x) {
+define ptr @pos_pow2_8_exact(ptr %foo, i64 noundef %x) {
 ; CHECK-LABEL: define ptr @pos_pow2_8_exact(
-; CHECK-SAME: ptr dereferenceable(8) [[FOO:%.*]], i64 noundef [[X:%.*]]) {
-; CHECK-NEXT:    [[IDX_MASK:%.*]] = and i64 [[X]], 7
-; CHECK-NEXT:    [[P1:%.*]] = getelementptr inbounds nuw i8, ptr [[FOO]], i64 [[IDX_MASK]]
-; CHECK-NEXT:    ret ptr [[P1]]
-;
-  %idx = srem i64 %x, 8
-  %p = getelementptr inbounds nuw i8, ptr %foo, i64 %idx
-  ret ptr %p
-}
-
-; dereferenceable(64) implies dereferenceable(8)
-define ptr @pos_pow2_8_more_deref(ptr dereferenceable(64) %foo, i64 noundef %x) {
-; CHECK-LABEL: define ptr @pos_pow2_8_more_deref(
-; CHECK-SAME: ptr dereferenceable(64) [[FOO:%.*]], i64 noundef [[X:%.*]]) {
+; CHECK-SAME: ptr [[FOO:%.*]], i64 noundef [[X:%.*]]) {
 ; CHECK-NEXT:    [[IDX_MASK:%.*]] = and i64 [[X]], 7
 ; CHECK-NEXT:    [[P1:%.*]] = getelementptr inbounds nuw i8, ptr [[FOO]], i64 [[IDX_MASK]]
 ; CHECK-NEXT:    ret ptr [[P1]]
@@ -56,9 +38,9 @@ define ptr @pos_pow2_8_more_deref(ptr dereferenceable(64) %foo, i64 noundef %x)
 }
 
 ; srem x, 1 is always 0, and mask is 0. GEP should be folded to %foo.
-define ptr @pos_pow2_1(ptr dereferenceable(1) %foo, i64 noundef %x) {
+define ptr @pos_pow2_1(ptr %foo, i64 noundef %x) {
 ; CHECK-LABEL: define ptr @pos_pow2_1(
-; CHECK-SAME: ptr dereferenceable(1) [[FOO:%.*]], i64 noundef [[X:%.*]]) {
+; CHECK-SAME: ptr [[FOO:%.*]], i64 noundef [[X:%.*]]) {
 ; CHECK-NEXT:    ret ptr [[FOO]]
 ;
   %idx = srem i64 %x, 1
@@ -67,9 +49,9 @@ define ptr @pos_pow2_1(ptr dereferenceable(1) %foo, i64 noundef %x) {
 }
 
 ; Non-power-of-two constant divisor
-define ptr @neg_non_pow2_6(ptr dereferenceable(6) %foo, i64 noundef %x) {
+define ptr @neg_non_pow2_6(ptr %foo, i64 noundef %x) {
 ; CHECK-LABEL: define ptr @neg_non_pow2_6(
-; CHECK-SAME: ptr dereferenceable(6) [[FOO:%.*]], i64 noundef [[X:%.*]]) {
+; CHECK-SAME: ptr [[FOO:%.*]], i64 noundef [[X:%.*]]) {
 ; CHECK-NEXT:    [[IDX:%.*]] = srem i64 [[X]], 6
 ; CHECK-NEXT:    [[P:%.*]] = getelementptr inbounds nuw i8, ptr [[FOO]], i64 [[IDX]]
 ; CHECK-NEXT:    ret ptr [[P]]
@@ -80,9 +62,9 @@ define ptr @neg_non_pow2_6(ptr dereferenceable(6) %foo, i64 noundef %x) {
 }
 
 ; Missing 'no unsigned wrap'
-define ptr @neg_missing_nuw(ptr dereferenceable(4) %foo, i64 noundef %x) {
+define ptr @neg_missing_nuw(ptr %foo, i64 noundef %x) {
 ; CHECK-LABEL: define ptr @neg_missing_nuw(
-; CHECK-SAME: ptr dereferenceable(4) [[FOO:%.*]], i64 noundef [[X:%.*]]) {
+; CHECK-SAME: ptr [[FOO:%.*]], i64 noundef [[X:%.*]]) {
 ; CHECK-NEXT:    [[IDX:%.*]] = srem i64 [[X]], 4
 ; CHECK-NEXT:    [[P:%.*]] = getelementptr inbounds i8, ptr [[FOO]], i64 [[IDX]]
 ; CHECK-NEXT:    ret ptr [[P]]
@@ -93,9 +75,9 @@ define ptr @neg_missing_nuw(ptr dereferenceable(4) %foo, i64 noundef %x) {
 }
 
 ; Missing 'inbounds'
-define ptr @neg_missing_inbounds(ptr dereferenceable(4) %foo, i64 noundef %x) {
+define ptr @neg_missing_inbounds(ptr %foo, i64 noundef %x) {
 ; CHECK-LABEL: define ptr @neg_missing_inbounds(
-; CHECK-SAME: ptr dereferenceable(4) [[FOO:%.*]], i64 noundef [[X:%.*]]) {
+; CHECK-SAME: ptr [[FOO:%.*]], i64 noundef [[X:%.*]]) {
 ; CHECK-NEXT:    [[IDX:%.*]] = srem i64 [[X]], 4
 ; CHECK-NEXT:    [[P:%.*]] = getelementptr nuw i8, ptr [[FOO]], i64 [[IDX]]
 ; CHECK-NEXT:    ret ptr [[P]]
@@ -105,34 +87,9 @@ define ptr @neg_missing_inbounds(ptr dereferenceable(4) %foo, i64 noundef %x) {
   ret ptr %p
 }
 
-; Base dereferenceable is too small
-define ptr @neg_deref_too_small(ptr dereferenceable(3) %foo, i64 noundef %x) {
-; CHECK-LABEL: define ptr @neg_deref_too_small(
-; CHECK-SAME: ptr dereferenceable(3) [[FOO:%.*]], i64 noundef [[X:%.*]]) {
-; CHECK-NEXT:    [[IDX:%.*]] = srem i64 [[X]], 4
-; CHECK-NEXT:    [[P:%.*]] = getelementptr inbounds nuw i8, ptr [[FOO]], i64 [[IDX]]
-; CHECK-NEXT:    ret ptr [[P]]
-;
-  %idx = srem i64 %x, 4
-  %p = getelementptr inbounds nuw i8, ptr %foo, i64 %idx
-  ret ptr %p
-}
-
-define ptr @neg_deref_or_null(ptr dereferenceable_or_null(4) %foo, i64 noundef %x) {
-; CHECK-LABEL: define ptr @neg_deref_or_null(
-; CHECK-SAME: ptr dereferenceable_or_null(4) [[FOO:%.*]], i64 noundef [[X:%.*]]) {
-; CHECK-NEXT:    [[IDX:%.*]] = srem i64 [[X]], 4
-; CHECK-NEXT:    [[P:%.*]] = getelementptr inbounds nuw i8, ptr [[FOO]], i64 [[IDX]]
-; CHECK-NEXT:    ret ptr [[P]]
-;
-  %idx = srem i64 %x, 4
-  %p = getelementptr inbounds nuw i8, ptr %foo, i64 %idx
-  ret ptr %p
-}
-
-define ptr @neg_nonconst_divisor(ptr dereferenceable(8) %foo, i64 noundef %x, i64 noundef %d) {
+define ptr @neg_nonconst_divisor(ptr %foo, i64 noundef %x, i64 noundef %d) {
 ; CHECK-LABEL: define ptr @neg_nonconst_divisor(
-; CHECK-SAME: ptr dereferenceable(8) [[FOO:%.*]], i64 noundef [[X:%.*]], i64 noundef [[D:%.*]]) {
+; CHECK-SAME: ptr [[FOO:%.*]], i64 noundef [[X:%.*]], i64 noundef [[D:%.*]]) {
 ; CHECK-NEXT:    [[IDX:%.*]] = srem i64 [[X]], [[D]]
 ; CHECK-NEXT:    [[P:%.*]] = getelementptr inbounds nuw i8, ptr [[FOO]], i64 [[IDX]]
 ; CHECK-NEXT:    ret ptr [[P]]
@@ -142,9 +99,9 @@ define ptr @neg_nonconst_divisor(ptr dereferenceable(8) %foo, i64 noundef %x, i6
   ret ptr %p
 }
 
-define ptr @neg_not_i8(ptr dereferenceable(16) %foo, i64 noundef %x) {
+define ptr @neg_not_i8(ptr %foo, i64 noundef %x) {
 ; CHECK-LABEL: define ptr @neg_not_i8(
-; CHECK-SAME: ptr dereferenceable(16) [[FOO:%.*]], i64 noundef [[X:%.*]]) {
+; CHECK-SAME: ptr [[FOO:%.*]], i64 noundef [[X:%.*]]) {
 ; CHECK-NEXT:    [[IDX:%.*]] = srem i64 [[X]], 4
 ; CHECK-NEXT:    [[P:%.*]] = getelementptr inbounds nuw i32, ptr [[FOO]], i64 [[IDX]]
 ; CHECK-NEXT:    ret ptr [[P]]

>From 0d2fe9c9c1f3a97a5f0951e85b26106669d07908 Mon Sep 17 00:00:00 2001
From: imkiva <zengtao at iscas.ac.cn>
Date: Fri, 6 Feb 2026 18:36:41 +0800
Subject: [PATCH 08/12] [NFC] Cleanup old changes

---
 llvm/lib/Transforms/InstCombine/InstructionCombining.cpp | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
index 3d1fcf39d7b53..b5121d8bc371e 100644
--- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
@@ -50,7 +50,6 @@
 #include "llvm/Analysis/InstructionSimplify.h"
 #include "llvm/Analysis/LastRunTrackingAnalysis.h"
 #include "llvm/Analysis/LazyBlockFrequencyInfo.h"
-#include "llvm/Analysis/Loads.h"
 #include "llvm/Analysis/MemoryBuiltins.h"
 #include "llvm/Analysis/OptimizationRemarkEmitter.h"
 #include "llvm/Analysis/ProfileSummaryInfo.h"
@@ -3357,7 +3356,7 @@ Instruction *InstCombinerImpl::visitGetElementPtrInst(GetElementPtrInst &GEP) {
     Value *X = nullptr;
     Value *Y = nullptr;
 
-    // Match: idx = srem X, Y -- where Y is a power-of-two constant.
+    // Match: idx = srem X, Y -- where Y is a power-of-two value.
     if (match(Indices[0], m_SRem(m_Value(X), m_Value(Y)))) {
       if (isKnownToBeAPowerOfTwo(Y, false, &GEP)) {
         // If GEP is inbounds+nuw, the offset cannot be negative

>From a46680c123e0595213f23d3b367ef2d6cd5f6d82 Mon Sep 17 00:00:00 2001
From: imkiva <zengtao at iscas.ac.cn>
Date: Fri, 6 Feb 2026 19:07:46 +0800
Subject: [PATCH 09/12] [InstCombine] review

---
 .../InstCombine/InstructionCombining.cpp      | 19 ++---
 .../InstCombine/gep-srem-to-and-deref.ll      | 75 ++++++++++++-------
 2 files changed, 51 insertions(+), 43 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
index b5121d8bc371e..253204e562ca8 100644
--- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
@@ -3347,12 +3347,9 @@ Instruction *InstCombinerImpl::visitGetElementPtrInst(GetElementPtrInst &GEP) {
     }
   }
 
-  // srem -> (and/urem) for inbounds+nuw byte GEP ---
-  if (GEPEltType->isIntegerTy(8) && Indices.size() == 1 && GEP.isInBounds() &&
+  // srem -> (and/urem) for inbounds+nuw GEP ---
+  if (Indices.size() == 1 && GEP.isInBounds() &&
       GEP.getNoWrapFlags().hasNoUnsignedWrap()) {
-
-    using namespace llvm::PatternMatch;
-
     Value *X = nullptr;
     Value *Y = nullptr;
 
@@ -3363,20 +3360,14 @@ Instruction *InstCombinerImpl::visitGetElementPtrInst(GetElementPtrInst &GEP) {
         // -> srem by power-of-two can be treated as urem,
         // and urem by power-of-two folds to 'and' later.
         Instruction *OldIdxI = dyn_cast<Instruction>(Indices[0]);
-        Builder.SetInsertPoint(&GEP);
         Value *NewIdx = Builder.CreateURem(X, Y, OldIdxI->getName());
 
         auto *NewGEP = GetElementPtrInst::Create(
-            GEPEltType, PtrOp, {NewIdx}, GEP.getName(), GEP.getIterator());
-        NewGEP->setIsInBounds(GEP.isInBounds());
-        NewGEP->setNoWrapFlags(GEP.getNoWrapFlags());
+            GEPEltType, PtrOp, {NewIdx}, GEP.getNoWrapFlags(), GEP.getName(),
+            GEP.getIterator());
         NewGEP->setDebugLoc(GEP.getDebugLoc());
 
-        Instruction *Res = replaceInstUsesWith(GEP, NewGEP);
-        if (OldIdxI && OldIdxI->use_empty())
-          eraseInstFromFunction(*OldIdxI);
-
-        return Res;
+        return replaceInstUsesWith(GEP, NewGEP);
       }
     }
   }
diff --git a/llvm/test/Transforms/InstCombine/gep-srem-to-and-deref.ll b/llvm/test/Transforms/InstCombine/gep-srem-to-and-deref.ll
index 945f34412c1ba..d1f3c2e859bfe 100644
--- a/llvm/test/Transforms/InstCombine/gep-srem-to-and-deref.ll
+++ b/llvm/test/Transforms/InstCombine/gep-srem-to-and-deref.ll
@@ -1,9 +1,9 @@
 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
 ; RUN: opt -passes=instcombine %s -S | FileCheck %s
 
-define ptr @pos_pow2_2(ptr %foo, i64 noundef %x) {
+define ptr @pos_pow2_2(ptr %foo, i64 %x) {
 ; CHECK-LABEL: define ptr @pos_pow2_2(
-; CHECK-SAME: ptr [[FOO:%.*]], i64 noundef [[X:%.*]]) {
+; CHECK-SAME: ptr [[FOO:%.*]], i64 [[X:%.*]]) {
 ; CHECK-NEXT:    [[IDX_MASK:%.*]] = and i64 [[X]], 1
 ; CHECK-NEXT:    [[P1:%.*]] = getelementptr inbounds nuw i8, ptr [[FOO]], i64 [[IDX_MASK]]
 ; CHECK-NEXT:    ret ptr [[P1]]
@@ -13,9 +13,9 @@ define ptr @pos_pow2_2(ptr %foo, i64 noundef %x) {
   ret ptr %p
 }
 
-define ptr @pos_pow2_4(ptr %foo, i64 noundef %x) {
+define ptr @pos_pow2_4(ptr %foo, i64 %x) {
 ; CHECK-LABEL: define ptr @pos_pow2_4(
-; CHECK-SAME: ptr [[FOO:%.*]], i64 noundef [[X:%.*]]) {
+; CHECK-SAME: ptr [[FOO:%.*]], i64 [[X:%.*]]) {
 ; CHECK-NEXT:    [[IDX_MASK:%.*]] = and i64 [[X]], 3
 ; CHECK-NEXT:    [[P1:%.*]] = getelementptr inbounds nuw i8, ptr [[FOO]], i64 [[IDX_MASK]]
 ; CHECK-NEXT:    ret ptr [[P1]]
@@ -25,9 +25,9 @@ define ptr @pos_pow2_4(ptr %foo, i64 noundef %x) {
   ret ptr %p
 }
 
-define ptr @pos_pow2_8_exact(ptr %foo, i64 noundef %x) {
-; CHECK-LABEL: define ptr @pos_pow2_8_exact(
-; CHECK-SAME: ptr [[FOO:%.*]], i64 noundef [[X:%.*]]) {
+define ptr @pos_pow2_8(ptr %foo, i64 %x) {
+; CHECK-LABEL: define ptr @pos_pow2_8(
+; CHECK-SAME: ptr [[FOO:%.*]], i64 [[X:%.*]]) {
 ; CHECK-NEXT:    [[IDX_MASK:%.*]] = and i64 [[X]], 7
 ; CHECK-NEXT:    [[P1:%.*]] = getelementptr inbounds nuw i8, ptr [[FOO]], i64 [[IDX_MASK]]
 ; CHECK-NEXT:    ret ptr [[P1]]
@@ -37,10 +37,39 @@ define ptr @pos_pow2_8_exact(ptr %foo, i64 noundef %x) {
   ret ptr %p
 }
 
+define ptr @pos_not_i8(ptr %foo, i64 %x) {
+; CHECK-LABEL: define ptr @pos_not_i8(
+; CHECK-SAME: ptr [[FOO:%.*]], i64 [[X:%.*]]) {
+; CHECK-NEXT:    [[IDX1:%.*]] = and i64 [[X]], 3
+; CHECK-NEXT:    [[P2:%.*]] = getelementptr inbounds nuw i32, ptr [[FOO]], i64 [[IDX1]]
+; CHECK-NEXT:    ret ptr [[P2]]
+;
+  %idx = srem i64 %x, 4
+  %p = getelementptr inbounds nuw i32, ptr %foo, i64 %idx
+  ret ptr %p
+}
+
+declare void @use(i8)
+
+define ptr @pos_multi_use(ptr %foo, i64 %x) {
+; CHECK-LABEL: define ptr @pos_multi_use(
+; CHECK-SAME: ptr [[FOO:%.*]], i64 [[X:%.*]]) {
+; CHECK-NEXT:    [[IDX:%.*]] = srem i64 [[X]], 4
+; CHECK-NEXT:    call void @use(i64 [[IDX]])
+; CHECK-NEXT:    [[IDX1:%.*]] = and i64 [[X]], 3
+; CHECK-NEXT:    [[P2:%.*]] = getelementptr inbounds nuw i8, ptr [[FOO]], i64 [[IDX1]]
+; CHECK-NEXT:    ret ptr [[P2]]
+;
+  %idx = srem i64 %x, 4
+  call void @use(i64 %idx)
+  %p = getelementptr inbounds nuw i8, ptr %foo, i64 %idx
+  ret ptr %p
+}
+
 ; srem x, 1 is always 0, and mask is 0. GEP should be folded to %foo.
-define ptr @pos_pow2_1(ptr %foo, i64 noundef %x) {
+define ptr @pos_pow2_1(ptr %foo, i64 %x) {
 ; CHECK-LABEL: define ptr @pos_pow2_1(
-; CHECK-SAME: ptr [[FOO:%.*]], i64 noundef [[X:%.*]]) {
+; CHECK-SAME: ptr [[FOO:%.*]], i64 [[X:%.*]]) {
 ; CHECK-NEXT:    ret ptr [[FOO]]
 ;
   %idx = srem i64 %x, 1
@@ -49,9 +78,9 @@ define ptr @pos_pow2_1(ptr %foo, i64 noundef %x) {
 }
 
 ; Non-power-of-two constant divisor
-define ptr @neg_non_pow2_6(ptr %foo, i64 noundef %x) {
+define ptr @neg_non_pow2_6(ptr %foo, i64 %x) {
 ; CHECK-LABEL: define ptr @neg_non_pow2_6(
-; CHECK-SAME: ptr [[FOO:%.*]], i64 noundef [[X:%.*]]) {
+; CHECK-SAME: ptr [[FOO:%.*]], i64 [[X:%.*]]) {
 ; CHECK-NEXT:    [[IDX:%.*]] = srem i64 [[X]], 6
 ; CHECK-NEXT:    [[P:%.*]] = getelementptr inbounds nuw i8, ptr [[FOO]], i64 [[IDX]]
 ; CHECK-NEXT:    ret ptr [[P]]
@@ -62,9 +91,9 @@ define ptr @neg_non_pow2_6(ptr %foo, i64 noundef %x) {
 }
 
 ; Missing 'no unsigned wrap'
-define ptr @neg_missing_nuw(ptr %foo, i64 noundef %x) {
+define ptr @neg_missing_nuw(ptr %foo, i64 %x) {
 ; CHECK-LABEL: define ptr @neg_missing_nuw(
-; CHECK-SAME: ptr [[FOO:%.*]], i64 noundef [[X:%.*]]) {
+; CHECK-SAME: ptr [[FOO:%.*]], i64 [[X:%.*]]) {
 ; CHECK-NEXT:    [[IDX:%.*]] = srem i64 [[X]], 4
 ; CHECK-NEXT:    [[P:%.*]] = getelementptr inbounds i8, ptr [[FOO]], i64 [[IDX]]
 ; CHECK-NEXT:    ret ptr [[P]]
@@ -75,9 +104,9 @@ define ptr @neg_missing_nuw(ptr %foo, i64 noundef %x) {
 }
 
 ; Missing 'inbounds'
-define ptr @neg_missing_inbounds(ptr %foo, i64 noundef %x) {
+define ptr @neg_missing_inbounds(ptr %foo, i64 %x) {
 ; CHECK-LABEL: define ptr @neg_missing_inbounds(
-; CHECK-SAME: ptr [[FOO:%.*]], i64 noundef [[X:%.*]]) {
+; CHECK-SAME: ptr [[FOO:%.*]], i64 [[X:%.*]]) {
 ; CHECK-NEXT:    [[IDX:%.*]] = srem i64 [[X]], 4
 ; CHECK-NEXT:    [[P:%.*]] = getelementptr nuw i8, ptr [[FOO]], i64 [[IDX]]
 ; CHECK-NEXT:    ret ptr [[P]]
@@ -87,9 +116,9 @@ define ptr @neg_missing_inbounds(ptr %foo, i64 noundef %x) {
   ret ptr %p
 }
 
-define ptr @neg_nonconst_divisor(ptr %foo, i64 noundef %x, i64 noundef %d) {
+define ptr @neg_nonconst_divisor(ptr %foo, i64 %x, i64 %d) {
 ; CHECK-LABEL: define ptr @neg_nonconst_divisor(
-; CHECK-SAME: ptr [[FOO:%.*]], i64 noundef [[X:%.*]], i64 noundef [[D:%.*]]) {
+; CHECK-SAME: ptr [[FOO:%.*]], i64 [[X:%.*]], i64 [[D:%.*]]) {
 ; CHECK-NEXT:    [[IDX:%.*]] = srem i64 [[X]], [[D]]
 ; CHECK-NEXT:    [[P:%.*]] = getelementptr inbounds nuw i8, ptr [[FOO]], i64 [[IDX]]
 ; CHECK-NEXT:    ret ptr [[P]]
@@ -98,15 +127,3 @@ define ptr @neg_nonconst_divisor(ptr %foo, i64 noundef %x, i64 noundef %d) {
   %p = getelementptr inbounds nuw i8, ptr %foo, i64 %idx
   ret ptr %p
 }
-
-define ptr @neg_not_i8(ptr %foo, i64 noundef %x) {
-; CHECK-LABEL: define ptr @neg_not_i8(
-; CHECK-SAME: ptr [[FOO:%.*]], i64 noundef [[X:%.*]]) {
-; CHECK-NEXT:    [[IDX:%.*]] = srem i64 [[X]], 4
-; CHECK-NEXT:    [[P:%.*]] = getelementptr inbounds nuw i32, ptr [[FOO]], i64 [[IDX]]
-; CHECK-NEXT:    ret ptr [[P]]
-;
-  %idx = srem i64 %x, 4
-  %p = getelementptr inbounds nuw i32, ptr %foo, i64 %idx
-  ret ptr %p
-}

>From 79f2a7e5e2d56b20f84b16940db202927f039b3f Mon Sep 17 00:00:00 2001
From: imkiva <zengtao at iscas.ac.cn>
Date: Fri, 6 Feb 2026 19:31:07 +0800
Subject: [PATCH 10/12] [InstCombine] review

---
 .../InstCombine/InstructionCombining.cpp      | 21 +++++-------
 .../InstCombine/gep-srem-to-and-deref.ll      | 33 +++++++++----------
 2 files changed, 24 insertions(+), 30 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
index 253204e562ca8..9b3caf57979bc 100644
--- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
@@ -3347,27 +3347,22 @@ Instruction *InstCombinerImpl::visitGetElementPtrInst(GetElementPtrInst &GEP) {
     }
   }
 
-  // srem -> (and/urem) for inbounds+nuw GEP ---
-  if (Indices.size() == 1 && GEP.isInBounds() &&
-      GEP.getNoWrapFlags().hasNoUnsignedWrap()) {
-    Value *X = nullptr;
-    Value *Y = nullptr;
+  // srem -> (and/urem) for inbounds+nuw GEP
+  if (Indices.size() == 1 && GEP.isInBounds() && GEP.hasNoUnsignedWrap()) {
+    Value *X, *Y;
 
     // Match: idx = srem X, Y -- where Y is a power-of-two value.
-    if (match(Indices[0], m_SRem(m_Value(X), m_Value(Y)))) {
-      if (isKnownToBeAPowerOfTwo(Y, false, &GEP)) {
+    if (match(Indices[0], m_OneUse(m_SRem(m_Value(X), m_Value(Y))))) {
+      if (isKnownToBeAPowerOfTwo(Y, /*OrZero=*/true, &GEP)) {
         // If GEP is inbounds+nuw, the offset cannot be negative
         // -> srem by power-of-two can be treated as urem,
         // and urem by power-of-two folds to 'and' later.
+        // OrZero=true is fine here because division by zero is UB.
         Instruction *OldIdxI = dyn_cast<Instruction>(Indices[0]);
         Value *NewIdx = Builder.CreateURem(X, Y, OldIdxI->getName());
 
-        auto *NewGEP = GetElementPtrInst::Create(
-            GEPEltType, PtrOp, {NewIdx}, GEP.getNoWrapFlags(), GEP.getName(),
-            GEP.getIterator());
-        NewGEP->setDebugLoc(GEP.getDebugLoc());
-
-        return replaceInstUsesWith(GEP, NewGEP);
+        return GetElementPtrInst::Create(GEPEltType, PtrOp, {NewIdx},
+                                         GEP.getNoWrapFlags(), GEP.getName());
       }
     }
   }
diff --git a/llvm/test/Transforms/InstCombine/gep-srem-to-and-deref.ll b/llvm/test/Transforms/InstCombine/gep-srem-to-and-deref.ll
index d1f3c2e859bfe..9f529dad5d3d1 100644
--- a/llvm/test/Transforms/InstCombine/gep-srem-to-and-deref.ll
+++ b/llvm/test/Transforms/InstCombine/gep-srem-to-and-deref.ll
@@ -49,23 +49,6 @@ define ptr @pos_not_i8(ptr %foo, i64 %x) {
   ret ptr %p
 }
 
-declare void @use(i8)
-
-define ptr @pos_multi_use(ptr %foo, i64 %x) {
-; CHECK-LABEL: define ptr @pos_multi_use(
-; CHECK-SAME: ptr [[FOO:%.*]], i64 [[X:%.*]]) {
-; CHECK-NEXT:    [[IDX:%.*]] = srem i64 [[X]], 4
-; CHECK-NEXT:    call void @use(i64 [[IDX]])
-; CHECK-NEXT:    [[IDX1:%.*]] = and i64 [[X]], 3
-; CHECK-NEXT:    [[P2:%.*]] = getelementptr inbounds nuw i8, ptr [[FOO]], i64 [[IDX1]]
-; CHECK-NEXT:    ret ptr [[P2]]
-;
-  %idx = srem i64 %x, 4
-  call void @use(i64 %idx)
-  %p = getelementptr inbounds nuw i8, ptr %foo, i64 %idx
-  ret ptr %p
-}
-
 ; srem x, 1 is always 0, and mask is 0. GEP should be folded to %foo.
 define ptr @pos_pow2_1(ptr %foo, i64 %x) {
 ; CHECK-LABEL: define ptr @pos_pow2_1(
@@ -127,3 +110,19 @@ define ptr @neg_nonconst_divisor(ptr %foo, i64 %x, i64 %d) {
   %p = getelementptr inbounds nuw i8, ptr %foo, i64 %idx
   ret ptr %p
 }
+
+declare void @use(i8)
+
+define ptr @neg_multi_use(ptr %foo, i64 %x) {
+; CHECK-LABEL: define ptr @neg_multi_use(
+; CHECK-SAME: ptr [[FOO:%.*]], i64 [[X:%.*]]) {
+; CHECK-NEXT:    [[IDX:%.*]] = srem i64 [[X]], 4
+; CHECK-NEXT:    call void @use(i64 [[IDX]])
+; CHECK-NEXT:    [[P:%.*]] = getelementptr inbounds nuw i8, ptr [[FOO]], i64 [[IDX]]
+; CHECK-NEXT:    ret ptr [[P]]
+;
+  %idx = srem i64 %x, 4
+  call void @use(i64 %idx)
+  %p = getelementptr inbounds nuw i8, ptr %foo, i64 %idx
+  ret ptr %p
+}

>From d4b8bfcf44557426ea5c7c711922af010e2d1e70 Mon Sep 17 00:00:00 2001
From: imkiva <zengtao at iscas.ac.cn>
Date: Mon, 9 Feb 2026 15:11:55 +0800
Subject: [PATCH 11/12] [InstCombine] review

---
 .../InstCombine/InstructionCombining.cpp      | 23 +++++++++----------
 1 file changed, 11 insertions(+), 12 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
index 9b3caf57979bc..ad798b711a271 100644
--- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
@@ -3352,18 +3352,17 @@ Instruction *InstCombinerImpl::visitGetElementPtrInst(GetElementPtrInst &GEP) {
     Value *X, *Y;
 
     // Match: idx = srem X, Y -- where Y is a power-of-two value.
-    if (match(Indices[0], m_OneUse(m_SRem(m_Value(X), m_Value(Y))))) {
-      if (isKnownToBeAPowerOfTwo(Y, /*OrZero=*/true, &GEP)) {
-        // If GEP is inbounds+nuw, the offset cannot be negative
-        // -> srem by power-of-two can be treated as urem,
-        // and urem by power-of-two folds to 'and' later.
-        // OrZero=true is fine here because division by zero is UB.
-        Instruction *OldIdxI = dyn_cast<Instruction>(Indices[0]);
-        Value *NewIdx = Builder.CreateURem(X, Y, OldIdxI->getName());
-
-        return GetElementPtrInst::Create(GEPEltType, PtrOp, {NewIdx},
-                                         GEP.getNoWrapFlags(), GEP.getName());
-      }
+    if (match(Indices[0], m_OneUse(m_SRem(m_Value(X), m_Value(Y)))) &&
+        isKnownToBeAPowerOfTwo(Y, /*OrZero=*/true, &GEP)) {
+      // If GEP is inbounds+nuw, the offset cannot be negative
+      // -> srem by power-of-two can be treated as urem,
+      // and urem by power-of-two folds to 'and' later.
+      // OrZero=true is fine here because division by zero is UB.
+      Instruction *OldIdxI = cast<Instruction>(Indices[0]);
+      Value *NewIdx = Builder.CreateURem(X, Y, OldIdxI->getName());
+
+      return GetElementPtrInst::Create(GEPEltType, PtrOp, {NewIdx},
+                                       GEP.getNoWrapFlags());
     }
   }
 

>From e26f3d7300eb2e11aa0a638913d255e07e2d47a3 Mon Sep 17 00:00:00 2001
From: imkiva <zengtao at iscas.ac.cn>
Date: Mon, 9 Feb 2026 15:13:40 +0800
Subject: [PATCH 12/12] [InstCombine] move to the end

---
 .../InstCombine/InstructionCombining.cpp      | 38 +++++++++----------
 1 file changed, 19 insertions(+), 19 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
index ad798b711a271..c39fda095ccfa 100644
--- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
@@ -3347,25 +3347,6 @@ Instruction *InstCombinerImpl::visitGetElementPtrInst(GetElementPtrInst &GEP) {
     }
   }
 
-  // srem -> (and/urem) for inbounds+nuw GEP
-  if (Indices.size() == 1 && GEP.isInBounds() && GEP.hasNoUnsignedWrap()) {
-    Value *X, *Y;
-
-    // Match: idx = srem X, Y -- where Y is a power-of-two value.
-    if (match(Indices[0], m_OneUse(m_SRem(m_Value(X), m_Value(Y)))) &&
-        isKnownToBeAPowerOfTwo(Y, /*OrZero=*/true, &GEP)) {
-      // If GEP is inbounds+nuw, the offset cannot be negative
-      // -> srem by power-of-two can be treated as urem,
-      // and urem by power-of-two folds to 'and' later.
-      // OrZero=true is fine here because division by zero is UB.
-      Instruction *OldIdxI = cast<Instruction>(Indices[0]);
-      Value *NewIdx = Builder.CreateURem(X, Y, OldIdxI->getName());
-
-      return GetElementPtrInst::Create(GEPEltType, PtrOp, {NewIdx},
-                                       GEP.getNoWrapFlags());
-    }
-  }
-
   // Eliminate unneeded casts for indices, and replace indices which displace
   // by multiples of a zero size type with zero.
   bool MadeChange = false;
@@ -3690,6 +3671,25 @@ Instruction *InstCombinerImpl::visitGetElementPtrInst(GetElementPtrInst &GEP) {
   if (Instruction *R = foldSelectGEP(GEP, Builder))
     return R;
 
+  // srem -> (and/urem) for inbounds+nuw GEP
+  if (Indices.size() == 1 && GEP.isInBounds() && GEP.hasNoUnsignedWrap()) {
+    Value *X, *Y;
+
+    // Match: idx = srem X, Y -- where Y is a power-of-two value.
+    if (match(Indices[0], m_OneUse(m_SRem(m_Value(X), m_Value(Y)))) &&
+        isKnownToBeAPowerOfTwo(Y, /*OrZero=*/true, &GEP)) {
+      // If GEP is inbounds+nuw, the offset cannot be negative
+      // -> srem by power-of-two can be treated as urem,
+      // and urem by power-of-two folds to 'and' later.
+      // OrZero=true is fine here because division by zero is UB.
+      Instruction *OldIdxI = cast<Instruction>(Indices[0]);
+      Value *NewIdx = Builder.CreateURem(X, Y, OldIdxI->getName());
+
+      return GetElementPtrInst::Create(GEPEltType, PtrOp, {NewIdx},
+                                       GEP.getNoWrapFlags());
+        }
+  }
+
   return nullptr;
 }
 



More information about the llvm-commits mailing list