[llvm] [InstCombine] Fold chained GEP with constant base into single GEP (PR #170439)

Jianjian Guan via llvm-commits llvm-commits at lists.llvm.org
Thu Dec 11 22:22:05 PST 2025


https://github.com/jacquesguan updated https://github.com/llvm/llvm-project/pull/170439

>From 58671e1c0dab3659b3a9583333d9f10efb7348b8 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/7] [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 c6de57cb34c69..ed2df767d8b32 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 552114af6e2ecf936fe8a108dfda34e81b1041b4 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/7] 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
+}
+

>From 1b4d39ddf658e3a9b1e55e6a1d536b1a7da6b20f Mon Sep 17 00:00:00 2001
From: Jianjian GUAN <jacquesguan at me.com>
Date: Tue, 9 Dec 2025 16:33:32 +0800
Subject: [PATCH 3/7] Address comment

---
 .../InstCombine/InstructionCombining.cpp      | 16 +++-
 .../gep-fold-chained-const-select.ll          | 85 ++++++++++++++++++-
 2 files changed, 95 insertions(+), 6 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
index ed2df767d8b32..e3d7009fc60d4 100644
--- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
@@ -2811,12 +2811,22 @@ Instruction *InstCombinerImpl::visitGEPOfGEP(GetElementPtrInst &GEP,
     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 ((match(SrcIdx, m_APInt(ConstOffset)) &&
+         match(GEPIdx,
+               m_Select(m_Value(Cond), m_APInt(TrueVal), m_APInt(FalseVal)))) ||
+        (match(GEPIdx, m_APInt(ConstOffset)) &&
+         match(SrcIdx,
+               m_Select(m_Value(Cond), m_APInt(TrueVal), m_APInt(FalseVal))))) {
+      // Make sure the select has only one use.
+      if ((isa<SelectInst>(GEPIdx) && !GEPIdx->hasOneUse()) ||
+          (isa<SelectInst>(SrcIdx) && !SrcIdx->hasOneUse()))
+        return nullptr;
+
       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),
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 ad955673744e6..964b1cc1337fa 100644
--- a/llvm/test/Transforms/InstCombine/gep-fold-chained-const-select.ll
+++ b/llvm/test/Transforms/InstCombine/gep-fold-chained-const-select.ll
@@ -29,6 +29,34 @@ define ptr @src_nuw(i32 %arg0, ptr %arg1) {
   ret ptr %v3
 }
 
+define ptr @src_inbounds_nuw(i32 %arg0, ptr %arg1) {
+; CHECK-LABEL: @src_inbounds_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 inbounds 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
+}
+
+define ptr @src_swap(i32 %arg0, ptr %arg1) {
+; CHECK-LABEL: @src_swap(
+; CHECK-NEXT:    [[V1:%.*]] = icmp sgt i32 [[ARG0:%.*]], 3
+; CHECK-NEXT:    [[V2:%.*]] = select i1 [[V1]], i64 63252, i64 29452
+; CHECK-NEXT:    [[V0:%.*]] = getelementptr i8, ptr [[ARG1:%.*]], i64 [[V2]]
+; CHECK-NEXT:    ret ptr [[V0]]
+;
+  %v1 = icmp sgt i32 %arg0, 3
+  %v2 = select i1 %v1, i64 55104, i64 21304
+  %v0 = getelementptr i8, ptr %arg1, i64 %v2
+  %v3 = getelementptr i8, ptr %v0, i64 8148
+  ret ptr %v3
+}
+
 ; Fail 1: Different GEP type
 define ptr @src_fail_different_type(i32 %arg0, ptr %arg1) {
 ; CHECK-LABEL: @src_fail_different_type(
@@ -61,13 +89,13 @@ define ptr @src_fail_no_constant_idx(i32 %arg0, ptr %arg1, i64 %arg2) {
   ret ptr %v3
 }
 
-; Fail 2: Multiple use
+; Fail 3: Multiple use of select
 define ptr @src_fail_select_multiple_use(i32 %arg0, ptr %arg1, ptr %arg2) {
 ; CHECK-LABEL: @src_fail_select_multiple_use(
+; 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:    [[TMP1:%.*]] = select i1 [[V1]], i64 63252, i64 29452
-; CHECK-NEXT:    [[V3:%.*]] = getelementptr i8, ptr [[ARG1:%.*]], i64 [[TMP1]]
+; CHECK-NEXT:    [[V3:%.*]] = getelementptr i8, ptr [[V0]], i64 [[V2]]
 ; CHECK-NEXT:    [[V4:%.*]] = getelementptr i8, ptr [[ARG2:%.*]], i64 [[V2]]
 ; CHECK-NEXT:    store ptr [[V3]], ptr [[V4]], align 8
 ; CHECK-NEXT:    ret ptr [[V3]]
@@ -81,3 +109,54 @@ define ptr @src_fail_select_multiple_use(i32 %arg0, ptr %arg1, ptr %arg2) {
   ret ptr %v3
 }
 
+; Fail 4: Multiple use of source GEP
+define ptr @src_fail_source_gep_multiple_use(i32 %arg0, ptr %arg1, ptr %arg2) {
+; CHECK-LABEL: @src_fail_source_gep_multiple_use(
+; CHECK-NEXT:    [[V1:%.*]] = icmp sgt i32 [[ARG0:%.*]], 3
+; 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 [[ARG1]], i64 8248
+; 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 %v0, i64 100
+  store ptr %v3, ptr %v4, align 8
+  ret ptr %v3
+}
+
+; TODO: constant vector index
+
+define <2 x ptr> @fail_vector_const_vector_select(i32 %arg0, <2 x ptr> %arg1) {
+; CHECK-LABEL: @fail_vector_const_vector_select(
+; CHECK-NEXT:    [[V0:%.*]] = getelementptr i8, <2 x ptr> [[ARG1:%.*]], <2 x i64> <i64 8148, i64 8848>
+; CHECK-NEXT:    [[V1:%.*]] = icmp sgt i32 [[ARG0:%.*]], 3
+; CHECK-NEXT:    [[V2:%.*]] = select i1 [[V1]], <2 x i64> <i64 55104, i64 56654>, <2 x i64> <i64 21304, i64 21224>
+; CHECK-NEXT:    [[V3:%.*]] = getelementptr i8, <2 x ptr> [[V0]], <2 x i64> [[V2]]
+; CHECK-NEXT:    ret <2 x ptr> [[V3]]
+;
+  %v0 = getelementptr i8, <2 x ptr> %arg1, <2 x i64> <i64 8148, i64 8848>
+  %v1 = icmp sgt i32 %arg0, 3
+  %v2 = select i1 %v1, <2 x i64> <i64 55104, i64 56654>, <2 x i64> <i64 21304, i64 21224>
+  %v3 = getelementptr i8, <2 x ptr> %v0, <2 x i64> %v2
+  ret <2 x ptr> %v3
+}
+
+
+define <2 x ptr> @fail_scalar_const_vector_select(i32 %arg0, <2 x ptr> %arg1) {
+; CHECK-LABEL: @fail_scalar_const_vector_select(
+; CHECK-NEXT:    [[V0:%.*]] = getelementptr i8, <2 x ptr> [[ARG1:%.*]], i64 8148
+; CHECK-NEXT:    [[V1:%.*]] = icmp sgt i32 [[ARG0:%.*]], 3
+; CHECK-NEXT:    [[V2:%.*]] = select i1 [[V1]], <2 x i64> <i64 55104, i64 56654>, <2 x i64> <i64 21304, i64 21224>
+; CHECK-NEXT:    [[V3:%.*]] = getelementptr i8, <2 x ptr> [[V0]], <2 x i64> [[V2]]
+; CHECK-NEXT:    ret <2 x ptr> [[V3]]
+;
+  %v0 = getelementptr i8, <2 x ptr> %arg1, i64 8148
+  %v1 = icmp sgt i32 %arg0, 3
+  %v2 = select i1 %v1, <2 x i64> <i64 55104, i64 56654>, <2 x i64> <i64 21304, i64 21224>
+  %v3 = getelementptr i8, <2 x ptr> %v0, <2 x i64> %v2
+  ret <2 x ptr> %v3
+}

>From 7aa148a7032fbcb8670ce8fac01e416c9045cbaa Mon Sep 17 00:00:00 2001
From: Jianjian GUAN <jacquesguan at me.com>
Date: Tue, 9 Dec 2025 17:47:59 +0800
Subject: [PATCH 4/7] Add splat case

---
 .../InstCombine/gep-fold-chained-const-select.ll   | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

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 964b1cc1337fa..c295c81e1733e 100644
--- a/llvm/test/Transforms/InstCombine/gep-fold-chained-const-select.ll
+++ b/llvm/test/Transforms/InstCombine/gep-fold-chained-const-select.ll
@@ -57,6 +57,20 @@ define ptr @src_swap(i32 %arg0, ptr %arg1) {
   ret ptr %v3
 }
 
+define <2 x ptr> @src_splat(i32 %arg0, <2 x ptr> %arg1) {
+; CHECK-LABEL: @src_splat(
+; CHECK-NEXT:    [[V1:%.*]] = icmp sgt i32 [[ARG0:%.*]], 3
+; CHECK-NEXT:    [[TMP1:%.*]] = select i1 [[V1]], i64 63252, i64 29452
+; CHECK-NEXT:    [[V3:%.*]] = getelementptr i8, <2 x ptr> [[ARG1:%.*]], i64 [[TMP1]]
+; CHECK-NEXT:    ret <2 x ptr> [[V3]]
+;
+  %v0 = getelementptr i8, <2 x ptr> %arg1, <2 x i64> splat (i64 8148)
+  %v1 = icmp sgt i32 %arg0, 3
+  %v2 = select i1 %v1, <2 x i64> splat (i64 55104), <2 x i64> splat (i64 21304)
+  %v3 = getelementptr i8, <2 x ptr> %v0, <2 x i64> %v2
+  ret <2 x ptr> %v3
+}
+
 ; Fail 1: Different GEP type
 define ptr @src_fail_different_type(i32 %arg0, ptr %arg1) {
 ; CHECK-LABEL: @src_fail_different_type(

>From 3e7794cecdcf93b19cd8aaca13ee664243bb9b6f Mon Sep 17 00:00:00 2001
From: Jianjian GUAN <jacquesguan at me.com>
Date: Thu, 11 Dec 2025 17:52:09 +0800
Subject: [PATCH 5/7] Address comment

---
 .../InstCombine/InstructionCombining.cpp      | 19 ++++++++----
 .../gep-fold-chained-const-select.ll          | 29 ++++++++++++++-----
 2 files changed, 35 insertions(+), 13 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
index e3d7009fc60d4..85d880d2c437e 100644
--- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
@@ -2818,10 +2818,11 @@ Instruction *InstCombinerImpl::visitGEPOfGEP(GetElementPtrInst &GEP,
         (match(GEPIdx, m_APInt(ConstOffset)) &&
          match(SrcIdx,
                m_Select(m_Value(Cond), m_APInt(TrueVal), m_APInt(FalseVal))))) {
+      auto *Select = isa<SelectInst>(GEPIdx) ? cast<SelectInst>(GEPIdx) : cast<SelectInst>(SrcIdx);
+
       // Make sure the select has only one use.
-      if ((isa<SelectInst>(GEPIdx) && !GEPIdx->hasOneUse()) ||
-          (isa<SelectInst>(SrcIdx) && !SrcIdx->hasOneUse()))
-        return nullptr;
+      if (!Select->hasOneUse())
+      return nullptr;
 
       if (TrueVal->getBitWidth() != ConstOffset->getBitWidth() ||
           FalseVal->getBitWidth() != ConstOffset->getBitWidth())
@@ -2829,12 +2830,18 @@ Instruction *InstCombinerImpl::visitGEPOfGEP(GetElementPtrInst &GEP,
 
       APInt NewTrueVal = *ConstOffset + *TrueVal;
       APInt NewFalseVal = *ConstOffset + *FalseVal;
-      Value *NewSelect = Builder.CreateSelect(Cond, Builder.getInt(NewTrueVal),
-                                              Builder.getInt(NewFalseVal));
+      Constant *NewTrue = Builder.getInt(NewTrueVal);
+      Constant *NewFalse = Builder.getInt(NewFalseVal);
+      // Consider vector splat.
+      if (auto *VT = dyn_cast<VectorType>(Select->getType())) {
+        NewTrue = ConstantVector::getSplat(VT->getElementCount(), NewTrue);
+        NewFalse = ConstantVector::getSplat(VT->getElementCount(), NewFalse);
+      }
+      Value *NewSelect = Builder.CreateSelect(Cond, NewTrue, NewFalse);
       GEPNoWrapFlags Flags =
           getMergedGEPNoWrapFlags(*Src, *cast<GEPOperator>(&GEP));
       return replaceInstUsesWith(GEP,
-                                 Builder.CreateGEP(GEP.getSourceElementType(),
+                                 Builder.CreateGEP(GEP.getResultElementType(),
                                                    Src->getPointerOperand(),
                                                    NewSelect, "", Flags));
     }
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 c295c81e1733e..f3b4758fa5cc8 100644
--- a/llvm/test/Transforms/InstCombine/gep-fold-chained-const-select.ll
+++ b/llvm/test/Transforms/InstCombine/gep-fold-chained-const-select.ll
@@ -60,8 +60,8 @@ define ptr @src_swap(i32 %arg0, ptr %arg1) {
 define <2 x ptr> @src_splat(i32 %arg0, <2 x ptr> %arg1) {
 ; CHECK-LABEL: @src_splat(
 ; CHECK-NEXT:    [[V1:%.*]] = icmp sgt i32 [[ARG0:%.*]], 3
-; CHECK-NEXT:    [[TMP1:%.*]] = select i1 [[V1]], i64 63252, i64 29452
-; CHECK-NEXT:    [[V3:%.*]] = getelementptr i8, <2 x ptr> [[ARG1:%.*]], i64 [[TMP1]]
+; CHECK-NEXT:    [[TMP1:%.*]] = select i1 [[V1]], <2 x i64> splat (i64 63252), <2 x i64> splat (i64 29452)
+; CHECK-NEXT:    [[V3:%.*]] = getelementptr i8, <2 x ptr> [[ARG1:%.*]], <2 x i64> [[TMP1]]
 ; CHECK-NEXT:    ret <2 x ptr> [[V3]]
 ;
   %v0 = getelementptr i8, <2 x ptr> %arg1, <2 x i64> splat (i64 8148)
@@ -71,6 +71,20 @@ define <2 x ptr> @src_splat(i32 %arg0, <2 x ptr> %arg1) {
   ret <2 x ptr> %v3
 }
 
+define <2 x ptr> @src_splat_scalar_ptr(i32 %arg0, ptr %arg1) {
+; CHECK-LABEL: @src_splat_scalar_ptr(
+; CHECK-NEXT:    [[V1:%.*]] = icmp sgt i32 [[ARG0:%.*]], 3
+; CHECK-NEXT:    [[TMP1:%.*]] = select i1 [[V1]], <2 x i64> splat (i64 63252), <2 x i64> splat (i64 29452)
+; CHECK-NEXT:    [[V3:%.*]] = getelementptr i8, ptr [[ARG1:%.*]], <2 x i64> [[TMP1]]
+; CHECK-NEXT:    ret <2 x ptr> [[V3]]
+;
+  %v0 = getelementptr i8, ptr %arg1, <2 x i64> splat (i64 8148)
+  %v1 = icmp sgt i32 %arg0, 3
+  %v2 = select i1 %v1, <2 x i64> splat (i64 55104), <2 x i64> splat (i64 21304)
+  %v3 = getelementptr i8, <2 x ptr> %v0, <2 x i64> %v2
+  ret <2 x ptr> %v3
+}
+
 ; Fail 1: Different GEP type
 define ptr @src_fail_different_type(i32 %arg0, ptr %arg1) {
 ; CHECK-LABEL: @src_fail_different_type(
@@ -123,22 +137,23 @@ define ptr @src_fail_select_multiple_use(i32 %arg0, ptr %arg1, ptr %arg2) {
   ret ptr %v3
 }
 
+declare void @use(ptr)
+
 ; Fail 4: Multiple use of source GEP
 define ptr @src_fail_source_gep_multiple_use(i32 %arg0, ptr %arg1, ptr %arg2) {
 ; CHECK-LABEL: @src_fail_source_gep_multiple_use(
+; CHECK-NEXT:    [[V0:%.*]] = getelementptr inbounds nuw i8, ptr [[ARG1:%.*]], i64 8148
 ; CHECK-NEXT:    [[V1:%.*]] = icmp sgt i32 [[ARG0:%.*]], 3
+; CHECK-NEXT:    call void @use(ptr nonnull [[V0]])
 ; 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 [[ARG1]], i64 8248
-; CHECK-NEXT:    store ptr [[V3]], ptr [[V4]], align 8
+; CHECK-NEXT:    [[V3:%.*]] = getelementptr i8, ptr [[ARG1]], i64 [[TMP1]]
 ; 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
+  call void @use(ptr %v0)
   %v3 = getelementptr i8, ptr %v0, i64 %v2
-  %v4 = getelementptr i8, ptr %v0, i64 100
-  store ptr %v3, ptr %v4, align 8
   ret ptr %v3
 }
 

>From 66afed322f037fdf99f83327fea50f4cabb9c1cd Mon Sep 17 00:00:00 2001
From: Jianjian GUAN <jacquesguan at me.com>
Date: Thu, 11 Dec 2025 17:56:12 +0800
Subject: [PATCH 6/7] Format

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

diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
index 85d880d2c437e..17c2618f0ffef 100644
--- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
@@ -2818,11 +2818,12 @@ Instruction *InstCombinerImpl::visitGEPOfGEP(GetElementPtrInst &GEP,
         (match(GEPIdx, m_APInt(ConstOffset)) &&
          match(SrcIdx,
                m_Select(m_Value(Cond), m_APInt(TrueVal), m_APInt(FalseVal))))) {
-      auto *Select = isa<SelectInst>(GEPIdx) ? cast<SelectInst>(GEPIdx) : cast<SelectInst>(SrcIdx);
+      auto *Select = isa<SelectInst>(GEPIdx) ? cast<SelectInst>(GEPIdx)
+                                             : cast<SelectInst>(SrcIdx);
 
       // Make sure the select has only one use.
       if (!Select->hasOneUse())
-      return nullptr;
+        return nullptr;
 
       if (TrueVal->getBitWidth() != ConstOffset->getBitWidth() ||
           FalseVal->getBitWidth() != ConstOffset->getBitWidth())

>From 53be14b21c2d38e6fa8fe07ad5ef09a20a1c6177 Mon Sep 17 00:00:00 2001
From: Jianjian GUAN <jacquesguan at me.com>
Date: Fri, 12 Dec 2025 14:21:39 +0800
Subject: [PATCH 7/7] Address comment

---
 .../InstCombine/InstructionCombining.cpp      |  9 ++------
 .../gep-fold-chained-const-select.ll          | 22 +++++++++++++++++--
 2 files changed, 22 insertions(+), 9 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
index 17c2618f0ffef..6e08af63f0863 100644
--- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
@@ -2831,13 +2831,8 @@ Instruction *InstCombinerImpl::visitGEPOfGEP(GetElementPtrInst &GEP,
 
       APInt NewTrueVal = *ConstOffset + *TrueVal;
       APInt NewFalseVal = *ConstOffset + *FalseVal;
-      Constant *NewTrue = Builder.getInt(NewTrueVal);
-      Constant *NewFalse = Builder.getInt(NewFalseVal);
-      // Consider vector splat.
-      if (auto *VT = dyn_cast<VectorType>(Select->getType())) {
-        NewTrue = ConstantVector::getSplat(VT->getElementCount(), NewTrue);
-        NewFalse = ConstantVector::getSplat(VT->getElementCount(), NewFalse);
-      }
+      Constant *NewTrue = ConstantInt::get(Select->getType(), NewTrueVal);
+      Constant *NewFalse = ConstantInt::get(Select->getType(), NewFalseVal);
       Value *NewSelect = Builder.CreateSelect(Cond, NewTrue, NewFalse);
       GEPNoWrapFlags Flags =
           getMergedGEPNoWrapFlags(*Src, *cast<GEPOperator>(&GEP));
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 f3b4758fa5cc8..35397eaf4438e 100644
--- a/llvm/test/Transforms/InstCombine/gep-fold-chained-const-select.ll
+++ b/llvm/test/Transforms/InstCombine/gep-fold-chained-const-select.ll
@@ -46,8 +46,8 @@ define ptr @src_inbounds_nuw(i32 %arg0, ptr %arg1) {
 define ptr @src_swap(i32 %arg0, ptr %arg1) {
 ; CHECK-LABEL: @src_swap(
 ; CHECK-NEXT:    [[V1:%.*]] = icmp sgt i32 [[ARG0:%.*]], 3
-; CHECK-NEXT:    [[V2:%.*]] = select i1 [[V1]], i64 63252, i64 29452
-; CHECK-NEXT:    [[V0:%.*]] = getelementptr i8, ptr [[ARG1:%.*]], i64 [[V2]]
+; CHECK-NEXT:    [[TMP1:%.*]] = select i1 [[V1]], i64 63252, i64 29452
+; CHECK-NEXT:    [[V0:%.*]] = getelementptr i8, ptr [[ARG1:%.*]], i64 [[TMP1]]
 ; CHECK-NEXT:    ret ptr [[V0]]
 ;
   %v1 = icmp sgt i32 %arg0, 3
@@ -157,6 +157,24 @@ define ptr @src_fail_source_gep_multiple_use(i32 %arg0, ptr %arg1, ptr %arg2) {
   ret ptr %v3
 }
 
+define ptr @src_fail_source_gep_multiple_swap(i32 %arg0, ptr %arg1) {
+; CHECK-LABEL: @src_fail_source_gep_multiple_swap(
+; CHECK-NEXT:    [[V1:%.*]] = icmp sgt i32 [[ARG0:%.*]], 3
+; CHECK-NEXT:    [[V2:%.*]] = select i1 [[V1]], i64 55104, i64 21304
+; CHECK-NEXT:    [[GEP1:%.*]] = getelementptr inbounds nuw i8, ptr [[ARG1:%.*]], i64 [[V2]]
+; CHECK-NEXT:    call void @use(ptr nonnull [[GEP1]])
+; CHECK-NEXT:    [[TMP1:%.*]] = select i1 [[V1]], i64 63252, i64 29452
+; CHECK-NEXT:    [[GEP2:%.*]] = getelementptr i8, ptr [[ARG1]], i64 [[TMP1]]
+; CHECK-NEXT:    ret ptr [[GEP2]]
+;
+  %v1 = icmp sgt i32 %arg0, 3
+  %v2 = select i1 %v1, i64 55104, i64 21304
+  %gep1 = getelementptr inbounds nuw i8, ptr %arg1, i64 %v2
+  call void @use(ptr %gep1)
+  %gep2 = getelementptr i8, ptr %gep1, i64 8148
+  ret ptr %gep2
+}
+
 ; TODO: constant vector index
 
 define <2 x ptr> @fail_vector_const_vector_select(i32 %arg0, <2 x ptr> %arg1) {



More information about the llvm-commits mailing list