[llvm] [InstCombine] Fold chained GEP with constant base into single GEP (PR #170439)
Jianjian Guan via llvm-commits
llvm-commits at lists.llvm.org
Wed Dec 3 22:45:36 PST 2025
https://github.com/jacquesguan updated https://github.com/llvm/llvm-project/pull/170439
>From 1b1d2da1640556e6e96cbd2e29d2230b8f44b9f8 Mon Sep 17 00:00:00 2001
From: Jianjian GUAN <jacquesguan at me.com>
Date: Mon, 1 Dec 2025 16:47:28 +0800
Subject: [PATCH 1/2] [InstCombine] Fold chained GEP with constant base into
single GEP
---
.../InstCombine/InstructionCombining.cpp | 27 +++++++++++
.../gep-fold-chained-const-select.ll | 46 +++++++++++++++++++
2 files changed, 73 insertions(+)
create mode 100644 llvm/test/Transforms/InstCombine/gep-fold-chained-const-select.ll
diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
index 5bc9c28bed141..fe68b1866d535 100644
--- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
@@ -2803,6 +2803,33 @@ Instruction *InstCombinerImpl::visitGEPOfGEP(GetElementPtrInst &GEP,
if (Src->getResultElementType() != GEP.getSourceElementType())
return nullptr;
+ // Fold chained GEP with constant base into single GEP:
+ // gep i8, (gep i8, %base, C1), (select Cond, C2, C3)
+ // -> gep i8, %base, (select Cond, C1+C2, C1+C3)
+ if (GEP.getNumIndices() == 1 && Src->getNumIndices() == 1) {
+ Value *SrcIdx = *Src->idx_begin();
+ Value *GEPIdx = *GEP.idx_begin();
+ const APInt *ConstOffset, *TrueVal, *FalseVal;
+ Value *Cond;
+ if (match(SrcIdx, m_APInt(ConstOffset)) &&
+ match(GEPIdx,
+ m_Select(m_Value(Cond), m_APInt(TrueVal), m_APInt(FalseVal)))) {
+ if (TrueVal->getBitWidth() != ConstOffset->getBitWidth() ||
+ FalseVal->getBitWidth() != ConstOffset->getBitWidth())
+ return nullptr;
+ APInt NewTrueVal = *ConstOffset + *TrueVal;
+ APInt NewFalseVal = *ConstOffset + *FalseVal;
+ Value *NewSelect = Builder.CreateSelect(Cond, Builder.getInt(NewTrueVal),
+ Builder.getInt(NewFalseVal));
+ GEPNoWrapFlags Flags =
+ getMergedGEPNoWrapFlags(*Src, *cast<GEPOperator>(&GEP));
+ return replaceInstUsesWith(GEP,
+ Builder.CreateGEP(GEP.getSourceElementType(),
+ Src->getPointerOperand(),
+ NewSelect, "", Flags));
+ }
+ }
+
// Find out whether the last index in the source GEP is a sequential idx.
bool EndsWithSequential = false;
for (gep_type_iterator I = gep_type_begin(*Src), E = gep_type_end(*Src);
diff --git a/llvm/test/Transforms/InstCombine/gep-fold-chained-const-select.ll b/llvm/test/Transforms/InstCombine/gep-fold-chained-const-select.ll
new file mode 100644
index 0000000000000..0591986ae4509
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/gep-fold-chained-const-select.ll
@@ -0,0 +1,46 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt < %s -S -passes=instcombine | FileCheck %s
+
+define ptr @fold(i32 %arg0, ptr %arg1) {
+; CHECK-LABEL: @fold(
+; CHECK-NEXT: [[V1:%.*]] = icmp sgt i32 [[ARG0:%.*]], 3
+; CHECK-NEXT: [[V2:%.*]] = select i1 [[V1]], i64 63252, i64 29452
+; CHECK-NEXT: [[V3:%.*]] = getelementptr i8, ptr [[V0:%.*]], i64 [[V2]]
+; CHECK-NEXT: ret ptr [[V3]]
+;
+ %v0 = getelementptr inbounds nuw i8, ptr %arg1, i64 8148
+ %v1 = icmp sgt i32 %arg0, 3
+ %v2 = select i1 %v1, i64 55104, i64 21304
+ %v3 = getelementptr i8, ptr %v0, i64 %v2
+ ret ptr %v3
+}
+
+define ptr @no_fold_0(i32 %arg0, ptr %arg1) {
+; CHECK-LABEL: @no_fold_0(
+; CHECK-NEXT: [[V0:%.*]] = getelementptr inbounds nuw i8, ptr [[ARG1:%.*]], i64 8148
+; CHECK-NEXT: [[V1:%.*]] = icmp sgt i32 [[ARG0:%.*]], 3
+; CHECK-NEXT: [[V2:%.*]] = select i1 [[V1]], i64 55104, i64 21304
+; CHECK-NEXT: [[V3:%.*]] = getelementptr i16, ptr [[V0]], i64 [[V2]]
+; CHECK-NEXT: ret ptr [[V3]]
+;
+ %v0 = getelementptr inbounds nuw i8, ptr %arg1, i64 8148
+ %v1 = icmp sgt i32 %arg0, 3
+ %v2 = select i1 %v1, i64 55104, i64 21304
+ %v3 = getelementptr i16, ptr %v0, i64 %v2
+ ret ptr %v3
+}
+
+define ptr @no_fold_1(i32 %arg0, ptr %arg1, i64 %arg2) {
+; CHECK-LABEL: @no_fold_1(
+; CHECK-NEXT: [[V0:%.*]] = getelementptr inbounds nuw i8, ptr [[ARG1:%.*]], i64 [[ARG2:%.*]]
+; CHECK-NEXT: [[V1:%.*]] = icmp sgt i32 [[ARG0:%.*]], 3
+; CHECK-NEXT: [[V2:%.*]] = select i1 [[V1]], i64 55104, i64 21304
+; CHECK-NEXT: [[V3:%.*]] = getelementptr i8, ptr [[V0]], i64 [[V2]]
+; CHECK-NEXT: ret ptr [[V3]]
+;
+ %v0 = getelementptr inbounds nuw i8, ptr %arg1, i64 %arg2
+ %v1 = icmp sgt i32 %arg0, 3
+ %v2 = select i1 %v1, i64 55104, i64 21304
+ %v3 = getelementptr i8, ptr %v0, i64 %v2
+ ret ptr %v3
+}
>From 712fd11acc670acc5ba99933153b770ed8ddae72 Mon Sep 17 00:00:00 2001
From: Jianjian GUAN <jacquesguan at me.com>
Date: Thu, 4 Dec 2025 14:44:56 +0800
Subject: [PATCH 2/2] Add more cases
---
.../gep-fold-chained-const-select.ll | 55 ++++++++++++++++---
1 file changed, 46 insertions(+), 9 deletions(-)
diff --git a/llvm/test/Transforms/InstCombine/gep-fold-chained-const-select.ll b/llvm/test/Transforms/InstCombine/gep-fold-chained-const-select.ll
index 0591986ae4509..ad955673744e6 100644
--- a/llvm/test/Transforms/InstCombine/gep-fold-chained-const-select.ll
+++ b/llvm/test/Transforms/InstCombine/gep-fold-chained-const-select.ll
@@ -1,22 +1,37 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt < %s -S -passes=instcombine | FileCheck %s
-define ptr @fold(i32 %arg0, ptr %arg1) {
-; CHECK-LABEL: @fold(
+define ptr @src_origin(i32 %arg0, ptr %arg1) {
+; CHECK-LABEL: @src_origin(
; CHECK-NEXT: [[V1:%.*]] = icmp sgt i32 [[ARG0:%.*]], 3
-; CHECK-NEXT: [[V2:%.*]] = select i1 [[V1]], i64 63252, i64 29452
-; CHECK-NEXT: [[V3:%.*]] = getelementptr i8, ptr [[V0:%.*]], i64 [[V2]]
+; CHECK-NEXT: [[TMP1:%.*]] = select i1 [[V1]], i64 63252, i64 29452
+; CHECK-NEXT: [[V3:%.*]] = getelementptr i8, ptr [[ARG1:%.*]], i64 [[TMP1]]
; CHECK-NEXT: ret ptr [[V3]]
;
- %v0 = getelementptr inbounds nuw i8, ptr %arg1, i64 8148
+ %v0 = getelementptr i8, ptr %arg1, i64 8148
%v1 = icmp sgt i32 %arg0, 3
%v2 = select i1 %v1, i64 55104, i64 21304
%v3 = getelementptr i8, ptr %v0, i64 %v2
ret ptr %v3
}
-define ptr @no_fold_0(i32 %arg0, ptr %arg1) {
-; CHECK-LABEL: @no_fold_0(
+define ptr @src_nuw(i32 %arg0, ptr %arg1) {
+; CHECK-LABEL: @src_nuw(
+; CHECK-NEXT: [[V1:%.*]] = icmp sgt i32 [[ARG0:%.*]], 3
+; CHECK-NEXT: [[TMP1:%.*]] = select i1 [[V1]], i64 63252, i64 29452
+; CHECK-NEXT: [[V3:%.*]] = getelementptr nuw i8, ptr [[ARG1:%.*]], i64 [[TMP1]]
+; CHECK-NEXT: ret ptr [[V3]]
+;
+ %v0 = getelementptr nuw i8, ptr %arg1, i64 8148
+ %v1 = icmp sgt i32 %arg0, 3
+ %v2 = select i1 %v1, i64 55104, i64 21304
+ %v3 = getelementptr nuw i8, ptr %v0, i64 %v2
+ ret ptr %v3
+}
+
+; Fail 1: Different GEP type
+define ptr @src_fail_different_type(i32 %arg0, ptr %arg1) {
+; CHECK-LABEL: @src_fail_different_type(
; CHECK-NEXT: [[V0:%.*]] = getelementptr inbounds nuw i8, ptr [[ARG1:%.*]], i64 8148
; CHECK-NEXT: [[V1:%.*]] = icmp sgt i32 [[ARG0:%.*]], 3
; CHECK-NEXT: [[V2:%.*]] = select i1 [[V1]], i64 55104, i64 21304
@@ -30,8 +45,9 @@ define ptr @no_fold_0(i32 %arg0, ptr %arg1) {
ret ptr %v3
}
-define ptr @no_fold_1(i32 %arg0, ptr %arg1, i64 %arg2) {
-; CHECK-LABEL: @no_fold_1(
+; Fail 2: No constant idx
+define ptr @src_fail_no_constant_idx(i32 %arg0, ptr %arg1, i64 %arg2) {
+; CHECK-LABEL: @src_fail_no_constant_idx(
; CHECK-NEXT: [[V0:%.*]] = getelementptr inbounds nuw i8, ptr [[ARG1:%.*]], i64 [[ARG2:%.*]]
; CHECK-NEXT: [[V1:%.*]] = icmp sgt i32 [[ARG0:%.*]], 3
; CHECK-NEXT: [[V2:%.*]] = select i1 [[V1]], i64 55104, i64 21304
@@ -44,3 +60,24 @@ define ptr @no_fold_1(i32 %arg0, ptr %arg1, i64 %arg2) {
%v3 = getelementptr i8, ptr %v0, i64 %v2
ret ptr %v3
}
+
+; Fail 2: Multiple use
+define ptr @src_fail_select_multiple_use(i32 %arg0, ptr %arg1, ptr %arg2) {
+; CHECK-LABEL: @src_fail_select_multiple_use(
+; CHECK-NEXT: [[V1:%.*]] = icmp sgt i32 [[ARG0:%.*]], 3
+; CHECK-NEXT: [[V2:%.*]] = select i1 [[V1]], i64 55104, i64 21304
+; CHECK-NEXT: [[TMP1:%.*]] = select i1 [[V1]], i64 63252, i64 29452
+; CHECK-NEXT: [[V3:%.*]] = getelementptr i8, ptr [[ARG1:%.*]], i64 [[TMP1]]
+; CHECK-NEXT: [[V4:%.*]] = getelementptr i8, ptr [[ARG2:%.*]], i64 [[V2]]
+; CHECK-NEXT: store ptr [[V3]], ptr [[V4]], align 8
+; CHECK-NEXT: ret ptr [[V3]]
+;
+ %v0 = getelementptr inbounds nuw i8, ptr %arg1, i64 8148
+ %v1 = icmp sgt i32 %arg0, 3
+ %v2 = select i1 %v1, i64 55104, i64 21304
+ %v3 = getelementptr i8, ptr %v0, i64 %v2
+ %v4 = getelementptr i8, ptr %arg2, i64 %v2
+ store ptr %v3, ptr %v4, align 8
+ ret ptr %v3
+}
+
More information about the llvm-commits
mailing list