[llvm] [InstCombine] optimize unnecessary sext instruction with add + cmp (PR #152291)

Gaurav Dhingra via llvm-commits llvm-commits at lists.llvm.org
Thu Aug 14 05:31:52 PDT 2025


https://github.com/gxyd updated https://github.com/llvm/llvm-project/pull/152291

>From 9248064baf7bb9bce0d214d7a6fe94bc207a15d2 Mon Sep 17 00:00:00 2001
From: Gaurav Dhingra <gauravdhingra.gxyd at gmail.com>
Date: Thu, 24 Jul 2025 10:28:48 +0530
Subject: [PATCH 1/5] [InstCombine][test] add test for sext+add+cmp instruction

---
 .../Transforms/InstCombine/sext_for_add_cmp.ll  | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)
 create mode 100644 llvm/test/Transforms/InstCombine/sext_for_add_cmp.ll

diff --git a/llvm/test/Transforms/InstCombine/sext_for_add_cmp.ll b/llvm/test/Transforms/InstCombine/sext_for_add_cmp.ll
new file mode 100644
index 0000000000000..c89555422f65e
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/sext_for_add_cmp.ll
@@ -0,0 +1,17 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+define i1 @sext_add_cmp_to_add_cmp(i16 noundef %x) {
+; CHECK-LABEL: define i1 @sext_add_cmp_to_add_cmp(
+; CHECK-SAME: i16 noundef [[X:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[CONV:%.*]] = sext i16 [[X]] to i128
+; CHECK-NEXT:    [[ADD:%.*]] = add nsw i128 [[CONV]], 128
+; CHECK-NEXT:    [[OR_COND_I:%.*]] = icmp ult i128 [[ADD]], 256
+; CHECK-NEXT:    ret i1 [[OR_COND_I]]
+;
+entry:
+  %conv = sext i16 %x to i128
+  %add = add i128 %conv, 128
+  %or.cond.i = icmp ult i128 %add, 256
+  ret i1 %or.cond.i
+}

>From ddfe8f2fa903c6fe4d3fffa8d31c3fd6c0437a35 Mon Sep 17 00:00:00 2001
From: Gaurav Dhingra <gauravdhingra.gxyd at gmail.com>
Date: Wed, 6 Aug 2025 16:19:53 +0530
Subject: [PATCH 2/5] [InstCombine] remove unnecessary sext instruction for
 add+cmp

---
 .../InstCombine/InstCombineCompares.cpp       | 34 +++++++++++++++++++
 .../InstCombine/InstCombineInternal.h         |  1 +
 2 files changed, 35 insertions(+)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index cf94d28100488..ae329e9aaec4c 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -6381,6 +6381,36 @@ Instruction *InstCombinerImpl::foldICmpWithZextOrSext(ICmpInst &ICmp) {
   return new ICmpInst(CmpInst::ICMP_SLT, X, Constant::getNullValue(SrcTy));
 }
 
+Instruction *InstCombinerImpl::foldICmpWithSextAndAdd(ICmpInst &ICmp) {
+  Value *X;
+  ConstantInt *Y, *Z;
+  // Match the pattern: icmp ult (add (sext X), Y), Z
+  // where X is a value, Y and Z are integer constants
+  // icmp ult (add(sext(X), Y)), Z  -> icmp ult (add(X, Y)), Z
+  if (match(&ICmp, m_SpecificICmp(CmpInst::ICMP_ULT,
+                            m_Add(m_SExt(m_Value(X)), m_ConstantInt(Y)),
+                            m_ConstantInt(Z)))) {
+    Type *XType = X->getType();
+    if (!XType->isIntegerTy())
+      return nullptr;
+
+    unsigned XBitWidth = XType->getIntegerBitWidth();
+    auto YValue = Y->getSExtValue();
+    auto ZValue = Z->getSExtValue();
+
+    auto MinValue = -(1LL << (XBitWidth - 1));
+    auto MaxValue = (1LL << (XBitWidth - 1)) - 1;
+
+    // // Check if Y and Z fit within X's type without wrapping
+    if (YValue < MinValue || YValue > MaxValue || ZValue < MinValue || ZValue > MaxValue)
+      return nullptr; // Cannot optimize if Y or Z would wrap in X's type
+
+    Value *NewAdd = Builder.CreateAdd(X, ConstantInt::get(XType, YValue));
+    return new ICmpInst(CmpInst::ICMP_ULT, NewAdd, ConstantInt::get(XType, ZValue));
+  }
+  return nullptr;
+}
+
 /// Handle icmp (cast x), (cast or constant).
 Instruction *InstCombinerImpl::foldICmpWithCastOp(ICmpInst &ICmp) {
   // If any operand of ICmp is a inttoptr roundtrip cast then remove it as
@@ -7727,6 +7757,10 @@ Instruction *InstCombinerImpl::visitICmpInst(ICmpInst &I) {
     }
   }
 
+  if (Instruction *Res = foldICmpWithSextAndAdd(I)) {
+    return Res;
+  }
+
   // In case of a comparison with two select instructions having the same
   // condition, check whether one of the resulting branches can be simplified.
   // If so, just compare the other branch and select the appropriate result.
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
index c67e27e5b3e7c..ed652720f0dac 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
+++ b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
@@ -744,6 +744,7 @@ class LLVM_LIBRARY_VISIBILITY InstCombinerImpl final
                                      const APInt &C);
   Instruction *foldICmpTruncWithTruncOrExt(ICmpInst &Cmp,
                                            const SimplifyQuery &Q);
+  Instruction *foldICmpWithSextAndAdd(ICmpInst &ICmp);
   Instruction *foldICmpAndConstant(ICmpInst &Cmp, BinaryOperator *And,
                                    const APInt &C);
   Instruction *foldICmpXorConstant(ICmpInst &Cmp, BinaryOperator *Xor,

>From 22d5e23984d5e2b894e4452049370a6dba265b9a Mon Sep 17 00:00:00 2001
From: Gaurav Dhingra <gauravdhingra.gxyd at gmail.com>
Date: Wed, 6 Aug 2025 16:21:30 +0530
Subject: [PATCH 3/5] [InstCombine][test] update tests for sext instruction
 optimization

---
 llvm/test/Transforms/InstCombine/icmp-add.ll         | 5 ++---
 llvm/test/Transforms/InstCombine/sext_for_add_cmp.ll | 5 ++---
 2 files changed, 4 insertions(+), 6 deletions(-)

diff --git a/llvm/test/Transforms/InstCombine/icmp-add.ll b/llvm/test/Transforms/InstCombine/icmp-add.ll
index 1a41c1f3e1045..df85f5a3caa65 100644
--- a/llvm/test/Transforms/InstCombine/icmp-add.ll
+++ b/llvm/test/Transforms/InstCombine/icmp-add.ll
@@ -3259,9 +3259,8 @@ define i1 @zext_range_check_mergable(i8 %x) {
 define i1 @sext_range_check_ult(i8 %x) {
 ; CHECK-LABEL: @sext_range_check_ult(
 ; CHECK-NEXT:  entry:
-; CHECK-NEXT:    [[CONV:%.*]] = sext i8 [[X:%.*]] to i32
-; CHECK-NEXT:    [[ADD:%.*]] = add nsw i32 [[CONV]], -4
-; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i32 [[ADD]], 3
+; CHECK-NEXT:    [[TMP0:%.*]] = add i8 [[X:%.*]], -4
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i8 [[TMP0]], 3
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ;
 entry:
diff --git a/llvm/test/Transforms/InstCombine/sext_for_add_cmp.ll b/llvm/test/Transforms/InstCombine/sext_for_add_cmp.ll
index c89555422f65e..b8617df750abb 100644
--- a/llvm/test/Transforms/InstCombine/sext_for_add_cmp.ll
+++ b/llvm/test/Transforms/InstCombine/sext_for_add_cmp.ll
@@ -4,9 +4,8 @@ define i1 @sext_add_cmp_to_add_cmp(i16 noundef %x) {
 ; CHECK-LABEL: define i1 @sext_add_cmp_to_add_cmp(
 ; CHECK-SAME: i16 noundef [[X:%.*]]) {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    [[CONV:%.*]] = sext i16 [[X]] to i128
-; CHECK-NEXT:    [[ADD:%.*]] = add nsw i128 [[CONV]], 128
-; CHECK-NEXT:    [[OR_COND_I:%.*]] = icmp ult i128 [[ADD]], 256
+; CHECK-NEXT:    [[TMP0:%.*]] = add i16 [[X]], 128
+; CHECK-NEXT:    [[OR_COND_I:%.*]] = icmp ult i16 [[TMP0]], 256
 ; CHECK-NEXT:    ret i1 [[OR_COND_I]]
 ;
 entry:

>From 7d93fd6f9b574ac18369d774eb129249d2e775e7 Mon Sep 17 00:00:00 2001
From: Gaurav Dhingra <gauravdhingra.gxyd at gmail.com>
Date: Wed, 13 Aug 2025 17:00:57 +0530
Subject: [PATCH 4/5] [InstCombine] use general condition for optimization

---
 .../InstCombine/InstCombineCompares.cpp       | 66 +++++++++++++++----
 1 file changed, 54 insertions(+), 12 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index ae329e9aaec4c..3bfa1a22ccd62 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -6383,30 +6383,72 @@ Instruction *InstCombinerImpl::foldICmpWithZextOrSext(ICmpInst &ICmp) {
 
 Instruction *InstCombinerImpl::foldICmpWithSextAndAdd(ICmpInst &ICmp) {
   Value *X;
-  ConstantInt *Y, *Z;
+  Value *Y, *Z;
   // Match the pattern: icmp ult (add (sext X), Y), Z
   // where X is a value, Y and Z are integer constants
   // icmp ult (add(sext(X), Y)), Z  -> icmp ult (add(X, Y)), Z
   if (match(&ICmp, m_SpecificICmp(CmpInst::ICMP_ULT,
-                            m_Add(m_SExt(m_Value(X)), m_ConstantInt(Y)),
-                            m_ConstantInt(Z)))) {
+                                  m_Add(m_SExt(m_Value(X)), m_Value(Y)),
+                                  m_Value(Z)))) {
     Type *XType = X->getType();
     if (!XType->isIntegerTy())
       return nullptr;
 
     unsigned XBitWidth = XType->getIntegerBitWidth();
-    auto YValue = Y->getSExtValue();
-    auto ZValue = Z->getSExtValue();
+    auto ExtractValue = [&](Value *V, Type *TargetType, int64_t &OutValue) -> Value* {
+      if (auto *C = dyn_cast<ConstantInt>(V)) {
+        OutValue = C->getSExtValue();
+        return ConstantInt::get(TargetType, OutValue);
+      }
+      if (match(V, m_SExt(m_Value()))) {
+        Value *Src = cast<SExtInst>(V)->getOperand(0);
+        if (Src->getType() == TargetType)
+          return Src;
+      }
+      return nullptr;
+    };
 
-    auto MinValue = -(1LL << (XBitWidth - 1));
-    auto MaxValue = (1LL << (XBitWidth - 1)) - 1;
+    int64_t YValue, ZValue;
+    Value *NewY = ExtractValue(Y, XType, YValue);
+    Value *NewZ = ExtractValue(Z, XType, ZValue);
+    if (!NewY || !NewZ)
+      return nullptr;
 
-    // // Check if Y and Z fit within X's type without wrapping
-    if (YValue < MinValue || YValue > MaxValue || ZValue < MinValue || ZValue > MaxValue)
-      return nullptr; // Cannot optimize if Y or Z would wrap in X's type
+    bool AreYZVariables = match(Y, m_SExt(m_Value())) && match(Z, m_SExt(m_Value()));
+    if (AreYZVariables) {
+      bool FoundCondition = false;
+      BasicBlock *BB = ICmp.getParent();
+      for (Instruction &I : *BB) {
+        if (auto *Assume = dyn_cast<IntrinsicInst>(&I)) {
+          if (Assume->getIntrinsicID() == Intrinsic::assume) {
+            Value *Cond = Assume->getOperand(0);
+            ConstantInt *C;
+            APInt MaxValue(Z->getType()->getIntegerBitWidth(), 1);
+            MaxValue <<= (XBitWidth - 1);
+            APInt Limit = MaxValue;
+            Limit += 1;
+            if (match(Cond, m_SpecificICmp(CmpInst::ICMP_ULT,
+                m_Sub(m_SExt(m_Value(Y)), m_SExt(m_Value(Z))), m_ConstantInt(C))) &&
+              C->getValue().ule(Limit)) {
+              FoundCondition = true;
+              if (Assume->use_empty()) {
+                eraseInstFromFunction(*Assume);
+              }
+              break;
+            }
+          }
+        }
+      }
+      if (!FoundCondition)
+        return nullptr;
+    } else {
+      auto MaxValue = (1LL << (XBitWidth - 1));
+      if (YValue - ZValue > MaxValue || YValue - ZValue < -MaxValue)
+        return nullptr;
+    }
 
-    Value *NewAdd = Builder.CreateAdd(X, ConstantInt::get(XType, YValue));
-    return new ICmpInst(CmpInst::ICMP_ULT, NewAdd, ConstantInt::get(XType, ZValue));
+    Value *NewAdd = Builder.CreateAdd(X, NewY);
+    return new ICmpInst(CmpInst::ICMP_ULT, NewAdd, NewZ);
   }
   return nullptr;
 }

>From 7c6ec79fdc9a4025eba999ba0fe966062b3659de Mon Sep 17 00:00:00 2001
From: Gaurav Dhingra <gauravdhingra.gxyd at gmail.com>
Date: Thu, 14 Aug 2025 18:00:55 +0530
Subject: [PATCH 5/5] [formatting] use clang-format to fix formatting issue

---
 .../Transforms/InstCombine/InstCombineCompares.cpp | 14 +++++++++-----
 1 file changed, 9 insertions(+), 5 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index 3bfa1a22ccd62..55cea92ebd263 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -6395,7 +6395,8 @@ Instruction *InstCombinerImpl::foldICmpWithSextAndAdd(ICmpInst &ICmp) {
       return nullptr;
 
     unsigned XBitWidth = XType->getIntegerBitWidth();
-    auto ExtractValue = [&](Value *V, Type *TargetType, int64_t &OutValue) -> Value* {
+    auto ExtractValue = [&](Value *V, Type *TargetType,
+                            int64_t &OutValue) -> Value * {
       if (auto *C = dyn_cast<ConstantInt>(V)) {
         OutValue = C->getSExtValue();
         return ConstantInt::get(TargetType, OutValue);
@@ -6414,7 +6415,8 @@ Instruction *InstCombinerImpl::foldICmpWithSextAndAdd(ICmpInst &ICmp) {
     if (!NewY || !NewZ)
       return nullptr;
 
-    bool AreYZVariables = match(Y, m_SExt(m_Value())) && match(Z, m_SExt(m_Value()));
+    bool AreYZVariables =
+        match(Y, m_SExt(m_Value())) && match(Z, m_SExt(m_Value()));
     if (AreYZVariables) {
       bool FoundCondition = false;
       BasicBlock *BB = ICmp.getParent();
@@ -6427,9 +6429,11 @@ Instruction *InstCombinerImpl::foldICmpWithSextAndAdd(ICmpInst &ICmp) {
             MaxValue <<= (XBitWidth - 1);
             APInt Limit = MaxValue;
             Limit += 1;
-            if (match(Cond, m_SpecificICmp(CmpInst::ICMP_ULT,
-                m_Sub(m_SExt(m_Value(Y)), m_SExt(m_Value(Z))), m_ConstantInt(C))) &&
-              C->getValue().ule(Limit)) {
+            if (match(Cond, m_SpecificICmp(
+                                CmpInst::ICMP_ULT,
+                                m_Sub(m_SExt(m_Value(Y)), m_SExt(m_Value(Z))),
+                                m_ConstantInt(C))) &&
+                C->getValue().ule(Limit)) {
               FoundCondition = true;
               if (Assume->use_empty()) {
                 eraseInstFromFunction(*Assume);



More information about the llvm-commits mailing list