[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