[llvm] [InstCombine] Fold y = Cx ∈ R into x ∈ R' for zext(x) (PR #186347)

Kunqiu Chen via llvm-commits llvm-commits at lists.llvm.org
Sat Mar 14 02:25:51 PDT 2026


https://github.com/Camsyn updated https://github.com/llvm/llvm-project/pull/186347

>From d9d2785dff60cdfb133f88401fa2a68142e98bf9 Mon Sep 17 00:00:00 2001
From: Camsyn <camsyn at foxmail.com>
Date: Sat, 14 Mar 2026 01:04:43 +0800
Subject: [PATCH 1/5] Pre-commit test

---
 llvm/test/Transforms/InstCombine/icmp-mul.ll | 296 +++++++++++++++++++
 1 file changed, 296 insertions(+)

diff --git a/llvm/test/Transforms/InstCombine/icmp-mul.ll b/llvm/test/Transforms/InstCombine/icmp-mul.ll
index 49e1e11fe6c36..66e162b901ace 100644
--- a/llvm/test/Transforms/InstCombine/icmp-mul.ll
+++ b/llvm/test/Transforms/InstCombine/icmp-mul.ll
@@ -1568,3 +1568,299 @@ entry:
   %cmp = icmp slt i64 %mul1, %mul2
   ret i1 %cmp
 }
+
+; Test for icmp (mul (zext x), C) to icmp x,
+; if mul is invertible on given predicate constraint
+; Refer to https://github.com/llvm/llvm-project/pull/186347 to understand
+; the mathematical model.
+
+; Come from https://github.com/llvm/llvm-project/pull/185907#discussion_r2919506475
+; N = 9, M = 27
+; n = 2^9 = 512, m = 2^27 = 134217728, C = 262657
+; k = floor((n - 1) * C / m) = floor(511 * 262657 / 134217728) = 0
+; CR = [-2^26, 262657), Y = [0, 134217728)
+; Invertible: yes
+define i1 @slt_invertible_zext_mul_full_image(<2 x i9> %v) {
+; CHECK-LABEL: @slt_invertible_zext_mul_full_image(
+; CHECK-NEXT:    [[E:%.*]] = extractelement <2 x i9> [[V:%.*]], i64 0
+; CHECK-NEXT:    [[Z:%.*]] = zext i9 [[E]] to i27
+; CHECK-NEXT:    [[M:%.*]] = mul nuw i27 [[Z]], 262657
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i27 [[M]], 262657
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %e = extractelement <2 x i9> %v, i64 0
+  %z = zext i9 %e to i27
+  %m = mul i27 %z, 262657
+  %cmp = icmp slt i27 %m, 262657
+  ret i1 %cmp
+}
+
+; N = 8, M = 16
+; n = 2^8 = 256, m = 2^16 = 65536, C = 257
+; k = floor((n - 1) * C / m) = floor(255 * 257 / 65536) = 0
+; CR = [-2^15, 257), Y = [0, 65536)
+; Invertible: yes
+define i1 @slt_invertible_zext_mul_full_image_i16(i8 %v) {
+; CHECK-LABEL: @slt_invertible_zext_mul_full_image_i16(
+; CHECK-NEXT:    [[Z:%.*]] = zext i8 [[V:%.*]] to i16
+; CHECK-NEXT:    [[M:%.*]] = mul nuw i16 [[Z]], 257
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i16 [[M]], 257
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %z = zext i8 %v to i16
+  %m = mul nuw i16 %z, 257
+  %cmp = icmp slt i16 %m, 257
+  ret i1 %cmp
+}
+
+; N = 4, M = 8
+; n = 2^4 = 16, m = 2^8 = 256, C = 17
+; k = floor((n - 1) * C / m) = floor(15 * 17 / 256) = 0
+; CR = [-2^7, 17), Y = [0, 256)
+; Invertible: yes
+define i1 @slt_invertible_zext_mul_full_image_i8(i4 %v) {
+; CHECK-LABEL: @slt_invertible_zext_mul_full_image_i8(
+; CHECK-NEXT:    [[Z:%.*]] = zext i4 [[V:%.*]] to i8
+; CHECK-NEXT:    [[M:%.*]] = mul nuw i8 [[Z]], 17
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i8 [[M]], 17
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %z = zext i4 %v to i8
+  %m = mul nuw i8 %z, 17
+  %cmp = icmp slt i8 %m, 17
+  ret i1 %cmp
+}
+
+; N = 4, M = 8
+; n = 2^4 = 16, m = 2^8 = 256, C = 10
+; k = floor((n - 1) * C / m) = floor(15 * 10 / 256) = 0
+; CR = [0, 50), Y = [0, 151)
+; Invertible: yes, because CR ⊆ Y
+define i1 @ult_invertible_zext_mul_partial_image(i4 %x) {
+; CHECK-LABEL: @ult_invertible_zext_mul_partial_image(
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i4 [[X:%.*]], 5
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %z = zext i4 %x to i8
+  %m = mul i8 %z, 10
+  %cmp = icmp ult i8 %m, 50
+  ret i1 %cmp
+}
+
+; N = 4, M = 8
+; n = 2^4 = 16, m = 2^8 = 256, C = 10
+; k = floor((n - 1) * C / m) = floor(15 * 10 / 256) = 0
+; CR = [0, 200), Y = [0, 151)
+; Invertible: yes
+define i1 @ult_invertible_zext_mul_all_true(i4 %x) {
+; CHECK-LABEL: @ult_invertible_zext_mul_all_true(
+; CHECK-NEXT:    ret i1 true
+;
+  %z = zext i4 %x to i8
+  %m = mul i8 %z, 10
+  %cmp = icmp ult i8 %m, 200
+  ret i1 %cmp
+}
+
+; N = 4, M = 8
+; n = 2^4 = 16, m = 2^8 = 256, C = 10
+; k = floor((n - 1) * C / m) = floor(15 * 10 / 256) = 0
+; CR = [200, 256), Y = [0, 151)
+; Invertible: yes
+define i1 @uge_invertible_zext_mul_all_false(i4 %x) {
+; CHECK-LABEL: @uge_invertible_zext_mul_all_false(
+; CHECK-NEXT:    ret i1 false
+;
+  %z = zext i4 %x to i8
+  %m = mul i8 %z, 10
+  %cmp = icmp uge i8 %m, 200
+  ret i1 %cmp
+}
+
+; N = 4, M = 8
+; n = 2^4 = 16, m = 2^8 = 256, C = 20
+; k = floor((n - 1) * C / m) = floor(15 * 20 / 256) = 1
+; CR = [60, 128), Y = [45, 256)
+; Invertible: yes
+define i1 @sge_invertible_tail_of_zext_mul(i4 %x) {
+; CHECK-LABEL: @sge_invertible_tail_of_zext_mul(
+; CHECK-NEXT:    [[Z:%.*]] = zext i4 [[X:%.*]] to i8
+; CHECK-NEXT:    [[M:%.*]] = mul i8 [[Z]], 20
+; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i8 [[M]], 59
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %z = zext i4 %x to i8
+  %m = mul i8 %z, 20
+  %cmp = icmp sge i8 %m, 60
+  ret i1 %cmp
+}
+
+; N = 4, M = 8
+; n = 2^4 = 16, m = 2^8 = 256, C = 20
+; k = floor((n - 1) * C / m) = floor(15 * 20 / 256) = 1
+; CR = [60, 256), Y = [45, 256)
+; Invertible: yes
+define i1 @uge_invertible_tail_of_zext_mul(i4 %x) {
+; CHECK-LABEL: @uge_invertible_tail_of_zext_mul(
+; CHECK-NEXT:    [[Z:%.*]] = zext i4 [[X:%.*]] to i8
+; CHECK-NEXT:    [[M:%.*]] = mul i8 [[Z]], 20
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ugt i8 [[M]], 59
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %z = zext i4 %x to i8
+  %m = mul i8 %z, 20
+  %cmp = icmp uge i8 %m, 60
+  ret i1 %cmp
+}
+
+; N = 4, M = 8
+; n = 2^4 = 16, m = 2^8 = 256, C = 18
+; k = floor((n - 1) * C / m) = floor(15 * 18 / 256) = 1
+; CR = [-2^7, 16), Y = [15, 256)
+; Invertible: yes on Y.inverse() = [16, 2^7)
+define i1 @slt_noninvertible_signed_range_before_tail(i4 %v) {
+; CHECK-LABEL: @slt_noninvertible_signed_range_before_tail(
+; CHECK-NEXT:    [[Z:%.*]] = zext i4 [[V:%.*]] to i8
+; CHECK-NEXT:    [[CAST:%.*]] = mul nuw i8 [[Z]], 18
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i8 [[CAST]], 16
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %z = zext i4 %v to i8
+  %cast = mul nuw i8 %z, 18
+  %cmp = icmp slt i8 %cast, 16
+  ret i1 %cmp
+}
+
+; N = 4, M = 8
+; n = 2^4 = 16, m = 2^8 = 256, C = 20
+; k = floor((n - 1) * C / m) = floor(15 * 20 / 256) = 1
+; CR = [0, 45), Y = [45, 256)
+; Invertible: yes on Y.inverse() = [45, 256)
+define i1 @ult_noninvertible_zext_mul_range(i4 %x) {
+; CHECK-LABEL: @ult_noninvertible_zext_mul_range(
+; CHECK-NEXT:    [[Z:%.*]] = zext i4 [[X:%.*]] to i8
+; CHECK-NEXT:    [[M:%.*]] = mul i8 [[Z]], 20
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i8 [[M]], 45
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %z = zext i4 %x to i8
+  %m = mul i8 %z, 20
+  %cmp = icmp ult i8 %m, 45
+  ret i1 %cmp
+}
+
+; N = 4, M = 8
+; n = 2^4 = 16, m = 2^8 = 256, C = 18
+; k = floor((n - 1) * C / m) = floor(15 * 18 / 256) = 1
+; CR = [0, 16), Y = [15, 256)
+; Invertible: yes on Y.inverse() = [16, 256)
+define i1 @ult_noninvertible_zext_mul_before_tail(i4 %v) {
+; CHECK-LABEL: @ult_noninvertible_zext_mul_before_tail(
+; CHECK-NEXT:    [[Z:%.*]] = zext i4 [[V:%.*]] to i8
+; CHECK-NEXT:    [[CAST:%.*]] = mul i8 [[Z]], 18
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i8 [[CAST]], 16
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %z = zext i4 %v to i8
+  %cast = mul i8 %z, 18
+  %cmp = icmp ult i8 %cast, 16
+  ret i1 %cmp
+}
+
+; N = 4, M = 8
+; n = 2^4 = 16, m = 2^8 = 256, C = 20
+; k = floor((n - 1) * C / m) = floor(15 * 20 / 256) = 1
+; CR = [-2^7, 60), Y = [45, 256)
+; Invertible: yes on Y.inverse() = [60, 2^7)
+define i1 @slt_noninvertible_crosses_wrap(i4 %v) {
+; CHECK-LABEL: @slt_noninvertible_crosses_wrap(
+; CHECK-NEXT:    [[Z:%.*]] = zext i4 [[V:%.*]] to i8
+; CHECK-NEXT:    [[CAST:%.*]] = mul i8 [[Z]], 20
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i8 [[CAST]], 60
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %z = zext i4 %v to i8
+  %cast = mul i8 %z, 20
+  %cmp = icmp slt i8 %cast, 60
+  ret i1 %cmp
+}
+
+; N = 5, M = 8
+; n = 2^5 = 32, m = 2^8 = 256, C = 20
+; k = floor((n - 1) * C / m) = floor(31 * 20 / 256) = 2
+; CR = [60, 256), Y = none
+; Invertible: no
+define i1 @uge_noninvertible_multiple_wraps(i5 %x) {
+; CHECK-LABEL: @uge_noninvertible_multiple_wraps(
+; CHECK-NEXT:    [[Z:%.*]] = zext i5 [[X:%.*]] to i8
+; CHECK-NEXT:    [[M:%.*]] = mul i8 [[Z]], 20
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ugt i8 [[M]], 59
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %z = zext i5 %x to i8
+  %m = mul i8 %z, 20
+  %cmp = icmp uge i8 %m, 60
+  ret i1 %cmp
+}
+
+; Tests for CmpCR built through add.
+
+; N = 4, M = 8
+; n = 2^4 = 16, m = 2^8 = 256, C = 20
+; k = floor((n - 1) * C / m) = floor(15 * 20 / 256) = 1
+; CmpCR = [110, 228), Y = [45, 256)
+; Invertible: yes
+define i1 @sge_invertible_tail_of_zext_mul_plus_offset(i4 %x) {
+; CHECK-LABEL: @sge_invertible_tail_of_zext_mul_plus_offset(
+; CHECK-NEXT:    [[Z:%.*]] = zext i4 [[X:%.*]] to i8
+; CHECK-NEXT:    [[M:%.*]] = mul i8 [[Z]], 20
+; CHECK-NEXT:    [[A:%.*]] = add i8 [[M]], -100
+; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i8 [[A]], 9
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %z = zext i4 %x to i8
+  %m = mul i8 %z, 20
+  %a = sub i8 %m, 100
+  %cmp = icmp sge i8 %a, 10
+  ret i1 %cmp
+}
+
+; N = 4, M = 8
+; n = 2^4 = 16, m = 2^8 = 256, C = 20
+; k = floor((n - 1) * C / m) = floor(15 * 20 / 256) = 1
+; CmpCR = [100, 200), Y = [45, 256)
+; Invertible: yes
+define i1 @ult_invertible_zext_mul_plus_offset(i4 %x) {
+; CHECK-LABEL: @ult_invertible_zext_mul_plus_offset(
+; CHECK-NEXT:    [[Z:%.*]] = zext i4 [[X:%.*]] to i8
+; CHECK-NEXT:    [[M:%.*]] = mul i8 [[Z]], 20
+; CHECK-NEXT:    [[A:%.*]] = add i8 [[M]], -100
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i8 [[A]], 100
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %z = zext i4 %x to i8
+  %m = mul i8 %z, 20
+  %a = add i8 %m, -100
+  %cmp = icmp ult i8 %a, 100
+  ret i1 %cmp
+}
+
+; N = 4, M = 8
+; n = 2^4 = 16, m = 2^8 = 256, C = 20
+; k = floor((n - 1) * C / m) = floor(15 * 20 / 256) = 1
+; CmpCR = [122, 60), Y = [45, 256)
+; Invertible: yes on CmpCR.inverse() = [60, 122)
+define i1 @slt_inverse_invertible_zext_mul_plus_offset(i4 %x) {
+; CHECK-LABEL: @slt_inverse_invertible_zext_mul_plus_offset(
+; CHECK-NEXT:    [[Z:%.*]] = zext i4 [[X:%.*]] to i8
+; CHECK-NEXT:    [[M:%.*]] = mul i8 [[Z]], 20
+; CHECK-NEXT:    [[A:%.*]] = add i8 [[M]], 6
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i8 [[A]], 66
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %z = zext i4 %x to i8
+  %m = mul i8 %z, 20
+  %a = add i8 %m, 6
+  %cmp = icmp slt i8 %a, 66
+  ret i1 %cmp
+}

>From 06b5e34371aa218c864bd8f4411427bc7f81b13b Mon Sep 17 00:00:00 2001
From: Camsyn <camsyn at foxmail.com>
Date: Sat, 14 Mar 2026 01:29:27 +0800
Subject: [PATCH 2/5] =?UTF-8?q?[InstCombine]=20Fold=20y=20=3D=20Cx=20?=
 =?UTF-8?q?=E2=88=88=20R=20into=20x=20=E2=88=88=20R'=20for=20zext(x)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../InstCombine/InstCombineCompares.cpp       | 202 +++++++++++++++++-
 1 file changed, 201 insertions(+), 1 deletion(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index 7f1ced9505b9b..8ea28e8fce3da 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -32,6 +32,7 @@
 #include "llvm/Support/KnownBits.h"
 #include "llvm/Transforms/InstCombine/InstCombiner.h"
 #include <bitset>
+#include <optional>
 
 using namespace llvm;
 using namespace PatternMatch;
@@ -2178,7 +2179,165 @@ Instruction *InstCombinerImpl::foldICmpOrConstant(ICmpInst &Cmp,
   return nullptr;
 }
 
-/// Fold icmp (mul X, Y), C.
+/// Refer to https://github.com/llvm/llvm-project/pull/186347 for the
+/// underlying math model.
+///
+/// Compute the result range Y on which y = Cx mod m is invertible, where
+/// x ranges over [0, n - 1], n = 2^N, and m = 2^M.
+///
+/// When IsDomainReturned is true, the returned Y is also the reachable image of
+/// the multiply, i.e. the domain. Otherwise, the returned Y is only the unique
+/// invertible result range.
+static std::optional<ConstantRange>
+getInvertibleResultRangeForZExtMul(const APInt &C, unsigned N,
+                                   bool &IsDomainReturned) {
+  unsigned M = C.getBitWidth();
+  assert(N < M && "Expected zext to a wider type");
+
+  // Use a widened type so that n = 2^N, m = 2^M, and (n - 1) * C are all
+  // representable without wrapping during the analysis.
+  unsigned WideBits = N + M;
+  APInt WideC = C.zext(WideBits);
+  APInt n = APInt::getOneBitSet(WideBits, N);
+  APInt m = APInt::getOneBitSet(WideBits, M);
+
+  // k = floor(((n - 1) * C) / m) counts how many times the walk y = Cx crosses
+  // the modulus while x ranges over [0, n - 1].
+  APInt MaxY = (n - 1) * WideC;
+  APInt k = MaxY.lshr(M);
+
+  if (k.isZero()) {
+    // k = 0: the walk never wraps, so Y is the full result space. The compare
+    // still needs to be intersected with the reachable image [0, MaxY].
+    // f(x) = Cx:      |
+    //               Y |  /
+    //               Y | /
+    //               Y |/
+    //                 ----
+    IsDomainReturned = true;
+    return ConstantRange::getNonEmpty(APInt::getZero(M), (MaxY + 1).trunc(M));
+  }
+
+  if (k.isOne()) {
+    // k = 1: only the unique upper tail [((n - 1) * C + 1) mod m, m) is
+    // invertible.
+    // f(x) = Cx:    Y |   /
+    //               Y |  /
+    //                 | /   /
+    //                 |/   /
+    //                 -------
+    IsDomainReturned = false;
+    APInt TailLo = (MaxY - m + 1).trunc(M);
+    return ConstantRange::getNonEmpty(TailLo, APInt::getZero(M));
+  }
+
+  // k >= 2: the walk overlaps itself too much to have a unique inverse.
+  // f(x) = Cx:        |   /   /
+  //                   |  /   /
+  // Y does not exist. | /   /   /
+  //                   |/   /   /
+  //                   -----------
+  return std::nullopt;
+}
+
+/// Given a compared result range CmpCR, constant C, and number N, considering
+///       y = f(x) = Cx, where C: iM, y: iM, x: iN,
+/// this function tries to find the equivalent range X of x,
+///          where x ∈ X iff y ∈ CmpCR.
+/// Refer to https://github.com/llvm/llvm-project/pull/186347 for the
+/// underlying math model.
+static std::optional<ConstantRange>
+getEquivalentRangeForZExtMul(const ConstantRange &CmpCR, const APInt &C,
+                             unsigned N) {
+  assert(N < C.getBitWidth() && "Expected zext to a wider type");
+  assert(!CmpCR.isEmptySet() && "Unexpected empty set");
+  assert(!CmpCR.isFullSet() && "Unexpected full set");
+  assert(!C.isOne() && "mul x, 1 should be folded before.");
+
+  // ==================================================================== //
+  // 1. Calculate the invertible interval Y for mul (zext x), C
+  // ==================================================================== //
+  bool IsDomainReturned = false;
+  auto Y = getInvertibleResultRangeForZExtMul(C, N, IsDomainReturned);
+  if (!Y)
+    return std::nullopt;
+
+  // ==================================================================== //
+  // 2. Calculate the equivalent range X via f^{-1} on CmpCR
+  // ==================================================================== //
+
+  auto TryGetSourceRange =
+      [&](const ConstantRange &CR) -> std::optional<ConstantRange> {
+    // If Y is also the domain, only CR ∩ Y matters. Otherwise, the whole
+    // compare range must stay inside the unique invertible tail.
+    std::optional<ConstantRange> ActiveCmpY;
+    if (IsDomainReturned) {
+      // Y is domain of Cx: keep only the reachable part
+      // ActiveCmpY = null if
+      //        L-------U    : Y
+      //        --U   L----- : CR
+      ActiveCmpY = Y->exactIntersectWith(CR);
+
+      if (ActiveCmpY == Y)
+        return /* Y ⊆ CR*/ ConstantRange::getFull(N);
+      if (ActiveCmpY->isEmptySet())
+        return /* Y ∩ CR = ∅ */ ConstantRange::getEmpty(N);
+    } else {
+      // Otherwise, CR must stays entirely inside Y
+      // ActiveCmpY = null of CR has some values non-invertible,
+      ActiveCmpY = Y->contains(CR) ? std::optional(CR) : std::nullopt;
+    }
+    // If ActiveCmpY = null, there are >1 separate intervals of x,
+    // making Cx ∈ CR. I.e., we cannot derive a single X.
+    if (!ActiveCmpY)
+      return std::nullopt;
+    // For an invertible half-open interval Y = [y0, y1), the corresponding
+    // source interval is f^{-1}(Y) = [ceil(y0 / C), ceil(y1 / C)) = [x0, x1).
+    APInt Y0 = ActiveCmpY->getLower();
+    APInt Y1 = ActiveCmpY->getUpper() - 1;
+    // x0 = ceil(y0 / C)
+    APInt X0 = APIntOps::RoundingUDiv(Y0, C, APInt::Rounding::UP);
+    // x1 = ceil(y1 / C) = floor((y1 - 1) / C) + 1
+    APInt X1 = APIntOps::RoundingUDiv(Y1, C, APInt::Rounding::DOWN) + 1;
+    return ConstantRange::getNonEmpty(X0.trunc(N), X1.trunc(N));
+  };
+
+  // Try to get single X to make Cx ∈ CmpCR by f^{-1}(CmpCR)
+  if (auto SrcCR = TryGetSourceRange(CmpCR))
+    return SrcCR;
+  // Try to get single X to make Cx ∈ CmpCR by f^{-1}(CmpCR.inverse()).inverse()
+  if (auto InvSrcCR = TryGetSourceRange(CmpCR.inverse()))
+    return InvSrcCR->inverse();
+
+  return std::nullopt;
+}
+
+/// Materialize the source range returned by getEquivalentRangeForZExtMul() as
+/// an icmp (or a constant true/false) over X.
+static Value *emitICmpForEquivalentRange(Value *X, const ConstantRange &SrcCR,
+                                         InstCombiner::BuilderTy &Builder) {
+  auto *Ty = cast<IntegerType>(X->getType());
+
+  if (SrcCR.isEmptySet())
+    return Builder.getFalse();
+  if (SrcCR.isFullSet())
+    return Builder.getTrue();
+
+  ICmpInst::Predicate NewPred;
+  APInt NewC, Offset;
+  SrcCR.getEquivalentICmp(NewPred, NewC, Offset);
+
+  Value *NewX = X;
+  if (!Offset.isZero()) {
+    if (Offset.isNegative())
+      NewX = Builder.CreateSub(X, ConstantInt::get(Ty, -Offset));
+    else
+      NewX = Builder.CreateAdd(X, ConstantInt::get(Ty, Offset));
+  }
+
+  return Builder.CreateICmp(NewPred, NewX, ConstantInt::get(Ty, NewC));
+}
+
 Instruction *InstCombinerImpl::foldICmpMulConstant(ICmpInst &Cmp,
                                                    BinaryOperator *Mul,
                                                    const APInt &C) {
@@ -2197,6 +2356,26 @@ Instruction *InstCombinerImpl::foldICmpMulConstant(ICmpInst &Cmp,
   if (!match(Mul->getOperand(1), m_APInt(MulC)))
     return nullptr;
 
+  // Try to match and optimize the follow pattern
+  //   y = mul (zext x), C
+  //   icmp pred  y, C2          ; y in CR?
+  // -->
+  //   icmp pred‘ (x + C3), C4   ; x in CR'?
+  // if y = Cx is invertible on y \in CR
+  //
+  // This currently only handles scalar, C > 0, and zext-based patterns;
+  // FIXME: can vector, C < 0, and sext-based patterns be supported?
+  Value *NarrowX;
+  if (ICmpInst::isRelational(Pred) && MulTy->isIntegerTy() &&
+      MulC->isStrictlyPositive() && match(X, m_ZExt(m_Value(NarrowX)))) {
+    auto *SrcTy = cast<IntegerType>(NarrowX->getType());
+    ConstantRange CmpCR = ConstantRange::makeExactICmpRegion(Pred, C);
+    if (auto SrcCR =
+            getEquivalentRangeForZExtMul(CmpCR, *MulC, SrcTy->getBitWidth()))
+      return replaceInstUsesWith(
+          Cmp, emitICmpForEquivalentRange(NarrowX, *SrcCR, Builder));
+  }
+
   // If this is a test of the sign bit and the multiply is sign-preserving with
   // a constant operand, use the multiply LHS operand instead:
   // (X * +MulC) < 0 --> X < 0
@@ -3197,6 +3376,27 @@ Instruction *InstCombinerImpl::foldICmpAddConstant(ICmpInst &Cmp,
                         ConstantInt::get(Ty, C - *C2));
 
   auto CR = ConstantRange::makeExactICmpRegion(Pred, C).subtract(*C2);
+
+  // Try to match and optimize the follow pattern
+  //   y = mul (zext x), C
+  //   icmp pred  (y + C1), C2  ; y in CR?
+  // -->
+  //   icmp pred’ (x + C3), C4  ; x in CR'?
+  // if y = Cx is invertible on y \in CR
+  //
+  // This currently only handles scalar, C > 0, and zext-based patterns;
+  // FIXME: can vector, C < 0, and sext-based patterns be supported?
+  Value *NarrowX;
+  const APInt *MulC;
+  if (match(X, m_Mul(m_ZExt(m_Value(NarrowX)), m_APInt(MulC))) &&
+      X->getType()->isIntegerTy() && MulC->isStrictlyPositive()) {
+    auto *SrcTy = cast<IntegerType>(NarrowX->getType());
+    if (auto SrcCR =
+            getEquivalentRangeForZExtMul(CR, *MulC, SrcTy->getBitWidth()))
+      return replaceInstUsesWith(
+          Cmp, emitICmpForEquivalentRange(NarrowX, *SrcCR, Builder));
+  }
+
   const APInt &Upper = CR.getUpper();
   const APInt &Lower = CR.getLower();
   if (Cmp.isSigned()) {

>From 99ba87363cfc4da254c9073ed0f610addc4157a1 Mon Sep 17 00:00:00 2001
From: Camsyn <camsyn at foxmail.com>
Date: Sat, 14 Mar 2026 01:30:11 +0800
Subject: [PATCH 3/5] Update tests

---
 llvm/test/Transforms/InstCombine/icmp-mul.ll | 59 +++++++-------------
 1 file changed, 20 insertions(+), 39 deletions(-)

diff --git a/llvm/test/Transforms/InstCombine/icmp-mul.ll b/llvm/test/Transforms/InstCombine/icmp-mul.ll
index 66e162b901ace..d499b38975c69 100644
--- a/llvm/test/Transforms/InstCombine/icmp-mul.ll
+++ b/llvm/test/Transforms/InstCombine/icmp-mul.ll
@@ -1583,9 +1583,7 @@ entry:
 define i1 @slt_invertible_zext_mul_full_image(<2 x i9> %v) {
 ; CHECK-LABEL: @slt_invertible_zext_mul_full_image(
 ; CHECK-NEXT:    [[E:%.*]] = extractelement <2 x i9> [[V:%.*]], i64 0
-; CHECK-NEXT:    [[Z:%.*]] = zext i9 [[E]] to i27
-; CHECK-NEXT:    [[M:%.*]] = mul nuw i27 [[Z]], 262657
-; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i27 [[M]], 262657
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i9 [[E]], 1
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ;
   %e = extractelement <2 x i9> %v, i64 0
@@ -1602,9 +1600,7 @@ define i1 @slt_invertible_zext_mul_full_image(<2 x i9> %v) {
 ; Invertible: yes
 define i1 @slt_invertible_zext_mul_full_image_i16(i8 %v) {
 ; CHECK-LABEL: @slt_invertible_zext_mul_full_image_i16(
-; CHECK-NEXT:    [[Z:%.*]] = zext i8 [[V:%.*]] to i16
-; CHECK-NEXT:    [[M:%.*]] = mul nuw i16 [[Z]], 257
-; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i16 [[M]], 257
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i8 [[V:%.*]], 1
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ;
   %z = zext i8 %v to i16
@@ -1620,9 +1616,7 @@ define i1 @slt_invertible_zext_mul_full_image_i16(i8 %v) {
 ; Invertible: yes
 define i1 @slt_invertible_zext_mul_full_image_i8(i4 %v) {
 ; CHECK-LABEL: @slt_invertible_zext_mul_full_image_i8(
-; CHECK-NEXT:    [[Z:%.*]] = zext i4 [[V:%.*]] to i8
-; CHECK-NEXT:    [[M:%.*]] = mul nuw i8 [[Z]], 17
-; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i8 [[M]], 17
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i4 [[V:%.*]], 1
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ;
   %z = zext i4 %v to i8
@@ -1684,9 +1678,8 @@ define i1 @uge_invertible_zext_mul_all_false(i4 %x) {
 ; Invertible: yes
 define i1 @sge_invertible_tail_of_zext_mul(i4 %x) {
 ; CHECK-LABEL: @sge_invertible_tail_of_zext_mul(
-; CHECK-NEXT:    [[Z:%.*]] = zext i4 [[X:%.*]] to i8
-; CHECK-NEXT:    [[M:%.*]] = mul i8 [[Z]], 20
-; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i8 [[M]], 59
+; CHECK-NEXT:    [[TMP1:%.*]] = add i4 [[X:%.*]], -3
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i4 [[TMP1]], 4
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ;
   %z = zext i4 %x to i8
@@ -1702,9 +1695,8 @@ define i1 @sge_invertible_tail_of_zext_mul(i4 %x) {
 ; Invertible: yes
 define i1 @uge_invertible_tail_of_zext_mul(i4 %x) {
 ; CHECK-LABEL: @uge_invertible_tail_of_zext_mul(
-; CHECK-NEXT:    [[Z:%.*]] = zext i4 [[X:%.*]] to i8
-; CHECK-NEXT:    [[M:%.*]] = mul i8 [[Z]], 20
-; CHECK-NEXT:    [[CMP:%.*]] = icmp ugt i8 [[M]], 59
+; CHECK-NEXT:    [[TMP1:%.*]] = add i4 [[X:%.*]], -3
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i4 [[TMP1]], -6
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ;
   %z = zext i4 %x to i8
@@ -1720,9 +1712,7 @@ define i1 @uge_invertible_tail_of_zext_mul(i4 %x) {
 ; Invertible: yes on Y.inverse() = [16, 2^7)
 define i1 @slt_noninvertible_signed_range_before_tail(i4 %v) {
 ; CHECK-LABEL: @slt_noninvertible_signed_range_before_tail(
-; CHECK-NEXT:    [[Z:%.*]] = zext i4 [[V:%.*]] to i8
-; CHECK-NEXT:    [[CAST:%.*]] = mul nuw i8 [[Z]], 18
-; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i8 [[CAST]], 16
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i4 [[V:%.*]], 1
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ;
   %z = zext i4 %v to i8
@@ -1738,9 +1728,8 @@ define i1 @slt_noninvertible_signed_range_before_tail(i4 %v) {
 ; Invertible: yes on Y.inverse() = [45, 256)
 define i1 @ult_noninvertible_zext_mul_range(i4 %x) {
 ; CHECK-LABEL: @ult_noninvertible_zext_mul_range(
-; CHECK-NEXT:    [[Z:%.*]] = zext i4 [[X:%.*]] to i8
-; CHECK-NEXT:    [[M:%.*]] = mul i8 [[Z]], 20
-; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i8 [[M]], 45
+; CHECK-NEXT:    [[TMP1:%.*]] = add i4 [[X:%.*]], 3
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i4 [[TMP1]], 6
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ;
   %z = zext i4 %x to i8
@@ -1756,9 +1745,8 @@ define i1 @ult_noninvertible_zext_mul_range(i4 %x) {
 ; Invertible: yes on Y.inverse() = [16, 256)
 define i1 @ult_noninvertible_zext_mul_before_tail(i4 %v) {
 ; CHECK-LABEL: @ult_noninvertible_zext_mul_before_tail(
-; CHECK-NEXT:    [[Z:%.*]] = zext i4 [[V:%.*]] to i8
-; CHECK-NEXT:    [[CAST:%.*]] = mul i8 [[Z]], 18
-; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i8 [[CAST]], 16
+; CHECK-NEXT:    [[TMP1:%.*]] = add i4 [[V:%.*]], 1
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i4 [[TMP1]], 2
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ;
   %z = zext i4 %v to i8
@@ -1774,9 +1762,8 @@ define i1 @ult_noninvertible_zext_mul_before_tail(i4 %v) {
 ; Invertible: yes on Y.inverse() = [60, 2^7)
 define i1 @slt_noninvertible_crosses_wrap(i4 %v) {
 ; CHECK-LABEL: @slt_noninvertible_crosses_wrap(
-; CHECK-NEXT:    [[Z:%.*]] = zext i4 [[V:%.*]] to i8
-; CHECK-NEXT:    [[CAST:%.*]] = mul i8 [[Z]], 20
-; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i8 [[CAST]], 60
+; CHECK-NEXT:    [[TMP1:%.*]] = add i4 [[V:%.*]], -7
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i4 [[TMP1]], -4
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ;
   %z = zext i4 %v to i8
@@ -1812,10 +1799,8 @@ define i1 @uge_noninvertible_multiple_wraps(i5 %x) {
 ; Invertible: yes
 define i1 @sge_invertible_tail_of_zext_mul_plus_offset(i4 %x) {
 ; CHECK-LABEL: @sge_invertible_tail_of_zext_mul_plus_offset(
-; CHECK-NEXT:    [[Z:%.*]] = zext i4 [[X:%.*]] to i8
-; CHECK-NEXT:    [[M:%.*]] = mul i8 [[Z]], 20
-; CHECK-NEXT:    [[A:%.*]] = add i8 [[M]], -100
-; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i8 [[A]], 9
+; CHECK-NEXT:    [[TMP1:%.*]] = add i4 [[X:%.*]], -6
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i4 [[TMP1]], 6
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ;
   %z = zext i4 %x to i8
@@ -1832,10 +1817,8 @@ define i1 @sge_invertible_tail_of_zext_mul_plus_offset(i4 %x) {
 ; Invertible: yes
 define i1 @ult_invertible_zext_mul_plus_offset(i4 %x) {
 ; CHECK-LABEL: @ult_invertible_zext_mul_plus_offset(
-; CHECK-NEXT:    [[Z:%.*]] = zext i4 [[X:%.*]] to i8
-; CHECK-NEXT:    [[M:%.*]] = mul i8 [[Z]], 20
-; CHECK-NEXT:    [[A:%.*]] = add i8 [[M]], -100
-; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i8 [[A]], 100
+; CHECK-NEXT:    [[TMP1:%.*]] = add i4 [[X:%.*]], -5
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i4 [[TMP1]], 5
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ;
   %z = zext i4 %x to i8
@@ -1852,10 +1835,8 @@ define i1 @ult_invertible_zext_mul_plus_offset(i4 %x) {
 ; Invertible: yes on CmpCR.inverse() = [60, 122)
 define i1 @slt_inverse_invertible_zext_mul_plus_offset(i4 %x) {
 ; CHECK-LABEL: @slt_inverse_invertible_zext_mul_plus_offset(
-; CHECK-NEXT:    [[Z:%.*]] = zext i4 [[X:%.*]] to i8
-; CHECK-NEXT:    [[M:%.*]] = mul i8 [[Z]], 20
-; CHECK-NEXT:    [[A:%.*]] = add i8 [[M]], 6
-; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i8 [[A]], 66
+; CHECK-NEXT:    [[TMP1:%.*]] = add i4 [[X:%.*]], -7
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i4 [[TMP1]], -4
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ;
   %z = zext i4 %x to i8

>From 007be67f5cc73c1b717960ec1511ceaaaaedefea Mon Sep 17 00:00:00 2001
From: Camsyn <camsyn at foxmail.com>
Date: Sat, 14 Mar 2026 17:02:30 +0800
Subject: [PATCH 4/5] refactor: extract CR to icmp into a util function

---
 .../InstCombine/InstCombineCompares.cpp       | 88 ++++++-------------
 1 file changed, 28 insertions(+), 60 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index 8ea28e8fce3da..17516d6e1dabc 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -2312,32 +2312,29 @@ getEquivalentRangeForZExtMul(const ConstantRange &CmpCR, const APInt &C,
   return std::nullopt;
 }
 
-/// Materialize the source range returned by getEquivalentRangeForZExtMul() as
-/// an icmp (or a constant true/false) over X.
-static Value *emitICmpForEquivalentRange(Value *X, const ConstantRange &SrcCR,
-                                         InstCombiner::BuilderTy &Builder) {
+/// Given a value @param X and a constant range @param CR , emit @return
+/// the equivalent icmp expression on X. E.g., CR = [3, 7) --> (x - 3) <u 4
+///   -  CR = [a, b), a < b ---> (x - a) <u (b - a)
+///   -  CR = [a, b), a > b ---> (x - b) >u (a - b)
+static ICmpInst *emitICmpForEquivalentRange(Value *X, const ConstantRange &CR,
+                                            InstCombiner::BuilderTy &Builder) {
+  assert(isa<IntegerType>(X->getType()) &&
+         "The value X constrained by CR must be an integer.");
   auto *Ty = cast<IntegerType>(X->getType());
+  CmpInst::Predicate EquivPred;
+  APInt EquivCmpC;
+  APInt EquivOffset;
 
-  if (SrcCR.isEmptySet())
-    return Builder.getFalse();
-  if (SrcCR.isFullSet())
-    return Builder.getTrue();
-
-  ICmpInst::Predicate NewPred;
-  APInt NewC, Offset;
-  SrcCR.getEquivalentICmp(NewPred, NewC, Offset);
-
-  Value *NewX = X;
-  if (!Offset.isZero()) {
-    if (Offset.isNegative())
-      NewX = Builder.CreateSub(X, ConstantInt::get(Ty, -Offset));
-    else
-      NewX = Builder.CreateAdd(X, ConstantInt::get(Ty, Offset));
-  }
-
-  return Builder.CreateICmp(NewPred, NewX, ConstantInt::get(Ty, NewC));
+  CR.getEquivalentICmp(EquivPred, EquivCmpC, EquivOffset);
+  return new ICmpInst(
+      EquivPred,
+      EquivOffset.isZero()
+          ? X
+          : Builder.CreateAdd(X, ConstantInt::get(Ty, EquivOffset)),
+      ConstantInt::get(Ty, EquivCmpC));
 }
 
+/// Fold icmp (mul X, Y), C.
 Instruction *InstCombinerImpl::foldICmpMulConstant(ICmpInst &Cmp,
                                                    BinaryOperator *Mul,
                                                    const APInt &C) {
@@ -2372,8 +2369,7 @@ Instruction *InstCombinerImpl::foldICmpMulConstant(ICmpInst &Cmp,
     ConstantRange CmpCR = ConstantRange::makeExactICmpRegion(Pred, C);
     if (auto SrcCR =
             getEquivalentRangeForZExtMul(CmpCR, *MulC, SrcTy->getBitWidth()))
-      return replaceInstUsesWith(
-          Cmp, emitICmpForEquivalentRange(NarrowX, *SrcCR, Builder));
+      return emitICmpForEquivalentRange(NarrowX, *SrcCR, Builder);
   }
 
   // If this is a test of the sign bit and the multiply is sign-preserving with
@@ -3393,8 +3389,7 @@ Instruction *InstCombinerImpl::foldICmpAddConstant(ICmpInst &Cmp,
     auto *SrcTy = cast<IntegerType>(NarrowX->getType());
     if (auto SrcCR =
             getEquivalentRangeForZExtMul(CR, *MulC, SrcTy->getBitWidth()))
-      return replaceInstUsesWith(
-          Cmp, emitICmpForEquivalentRange(NarrowX, *SrcCR, Builder));
+      return emitICmpForEquivalentRange(NarrowX, *SrcCR, Builder);
   }
 
   const APInt &Upper = CR.getUpper();
@@ -3477,20 +3472,9 @@ Instruction *InstCombinerImpl::foldICmpAddConstant(ICmpInst &Cmp,
   if (match(X, m_ZExt(m_Value(V)))) {
     Type *NewCmpTy = V->getType();
     unsigned NewCmpBW = NewCmpTy->getScalarSizeInBits();
-    if (shouldChangeType(Ty, NewCmpTy)) {
-      ConstantRange SrcCR = CR.truncate(NewCmpBW, TruncInst::NoUnsignedWrap);
-      CmpInst::Predicate EquivPred;
-      APInt EquivInt;
-      APInt EquivOffset;
-
-      SrcCR.getEquivalentICmp(EquivPred, EquivInt, EquivOffset);
-      return new ICmpInst(
-          EquivPred,
-          EquivOffset.isZero()
-              ? V
-              : Builder.CreateAdd(V, ConstantInt::get(NewCmpTy, EquivOffset)),
-          ConstantInt::get(NewCmpTy, EquivInt));
-    }
+    if (shouldChangeType(Ty, NewCmpTy))
+      return emitICmpForEquivalentRange(
+          V, CR.truncate(NewCmpBW, TruncInst::NoUnsignedWrap), Builder);
   }
 
   return nullptr;
@@ -4313,16 +4297,7 @@ foldICmpUSubSatOrUAddSatWithConstant(CmpPredicate Pred, SaturatingInst *II,
   if (!Combination)
     return nullptr;
 
-  CmpInst::Predicate EquivPred;
-  APInt EquivInt;
-  APInt EquivOffset;
-
-  Combination->getEquivalentICmp(EquivPred, EquivInt, EquivOffset);
-
-  return new ICmpInst(
-      EquivPred,
-      Builder.CreateAdd(Op0, ConstantInt::get(Op1->getType(), EquivOffset)),
-      ConstantInt::get(Op1->getType(), EquivInt));
+  return emitICmpForEquivalentRange(Op0, *Combination, Builder);
 }
 
 static Instruction *
@@ -6006,18 +5981,11 @@ Instruction *InstCombinerImpl::foldICmpWithClamp(ICmpInst &I, Value *X,
   }
 
   ConstantRange CR = ConstantRange::getNonEmpty(*Lo, *Hi + 1);
-  ICmpInst::Predicate Pred;
-  APInt C, Offset;
-  if (I.getPredicate() == ICmpInst::ICMP_EQ)
-    CR.getEquivalentICmp(Pred, C, Offset);
-  else
-    CR.inverse().getEquivalentICmp(Pred, C, Offset);
 
-  if (!Offset.isZero())
-    X = Builder.CreateAdd(X, ConstantInt::get(X->getType(), Offset));
+  ICmpInst *EquivICmp = emitICmpForEquivalentRange(
+      X, I.getPredicate() == ICmpInst::ICMP_EQ ? CR : CR.inverse(), Builder);
 
-  return replaceInstUsesWith(
-      I, Builder.CreateICmp(Pred, X, ConstantInt::get(X->getType(), C)));
+  return replaceInstUsesWith(I, EquivICmp);
 }
 
 // Canonicalize checking for a power-of-2-or-zero value:

>From d19d79e5135853e02bded32f067c815cd04f31c7 Mon Sep 17 00:00:00 2001
From: Camsyn <camsyn at foxmail.com>
Date: Sat, 14 Mar 2026 17:25:27 +0800
Subject: [PATCH 5/5] chore: fix comments

---
 .../InstCombine/InstCombineCompares.cpp       | 40 +++++++++----------
 1 file changed, 20 insertions(+), 20 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index 17516d6e1dabc..2ff760d034e5d 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -2247,15 +2247,15 @@ getInvertibleResultRangeForZExtMul(const APInt &C, unsigned N,
 /// Refer to https://github.com/llvm/llvm-project/pull/186347 for the
 /// underlying math model.
 static std::optional<ConstantRange>
-getEquivalentRangeForZExtMul(const ConstantRange &CmpCR, const APInt &C,
-                             unsigned N) {
+getEquivalentSourceRangeForZExtMul(const ConstantRange &CmpCR, const APInt &C,
+                                   unsigned N) {
   assert(N < C.getBitWidth() && "Expected zext to a wider type");
   assert(!CmpCR.isEmptySet() && "Unexpected empty set");
   assert(!CmpCR.isFullSet() && "Unexpected full set");
   assert(!C.isOne() && "mul x, 1 should be folded before.");
 
   // ==================================================================== //
-  // 1. Calculate the invertible interval Y for mul (zext x), C
+  // 1. Calculate the invertible interval Y for mul (zext x), C.
   // ==================================================================== //
   bool IsDomainReturned = false;
   auto Y = getInvertibleResultRangeForZExtMul(C, N, IsDomainReturned);
@@ -2263,7 +2263,7 @@ getEquivalentRangeForZExtMul(const ConstantRange &CmpCR, const APInt &C,
     return std::nullopt;
 
   // ==================================================================== //
-  // 2. Calculate the equivalent range X via f^{-1} on CmpCR
+  // 2. Calculate the equivalent range X via f^{-1} on CmpCR.
   // ==================================================================== //
 
   auto TryGetSourceRange =
@@ -2272,7 +2272,7 @@ getEquivalentRangeForZExtMul(const ConstantRange &CmpCR, const APInt &C,
     // compare range must stay inside the unique invertible tail.
     std::optional<ConstantRange> ActiveCmpY;
     if (IsDomainReturned) {
-      // Y is domain of Cx: keep only the reachable part
+      // Y is domain of Cx: keep only the reachable part.
       // ActiveCmpY = null if
       //        L-------U    : Y
       //        --U   L----- : CR
@@ -2283,8 +2283,8 @@ getEquivalentRangeForZExtMul(const ConstantRange &CmpCR, const APInt &C,
       if (ActiveCmpY->isEmptySet())
         return /* Y ∩ CR = ∅ */ ConstantRange::getEmpty(N);
     } else {
-      // Otherwise, CR must stays entirely inside Y
-      // ActiveCmpY = null of CR has some values non-invertible,
+      // Otherwise, CR must stays entirely inside Y.
+      // ActiveCmpY = null if CR has some values non-invertible.
       ActiveCmpY = Y->contains(CR) ? std::optional(CR) : std::nullopt;
     }
     // If ActiveCmpY = null, there are >1 separate intervals of x,
@@ -2302,18 +2302,18 @@ getEquivalentRangeForZExtMul(const ConstantRange &CmpCR, const APInt &C,
     return ConstantRange::getNonEmpty(X0.trunc(N), X1.trunc(N));
   };
 
-  // Try to get single X to make Cx ∈ CmpCR by f^{-1}(CmpCR)
+  // Try to get single X to make Cx ∈ CmpCR by f^{-1}(CmpCR).
   if (auto SrcCR = TryGetSourceRange(CmpCR))
     return SrcCR;
-  // Try to get single X to make Cx ∈ CmpCR by f^{-1}(CmpCR.inverse()).inverse()
+  // Try to get single X to make Cx ∈ CmpCR by f^{-1}(CmpCR.inverse()).inverse().
   if (auto InvSrcCR = TryGetSourceRange(CmpCR.inverse()))
     return InvSrcCR->inverse();
 
   return std::nullopt;
 }
 
-/// Given a value @param X and a constant range @param CR , emit @return
-/// the equivalent icmp expression on X. E.g., CR = [3, 7) --> (x - 3) <u 4
+/// Given a value @param X and a constant range @param CR , emit @return the
+/// equivalent icmp expression on X. E.g., CR = [3, 7) --> (x - 3) <u 4.
 ///   -  CR = [a, b), a < b ---> (x - a) <u (b - a)
 ///   -  CR = [a, b), a > b ---> (x - b) >u (a - b)
 static ICmpInst *emitICmpForEquivalentRange(Value *X, const ConstantRange &CR,
@@ -2353,22 +2353,22 @@ Instruction *InstCombinerImpl::foldICmpMulConstant(ICmpInst &Cmp,
   if (!match(Mul->getOperand(1), m_APInt(MulC)))
     return nullptr;
 
-  // Try to match and optimize the follow pattern
+  // Try to match and optimize the follow pattern:
   //   y = mul (zext x), C
-  //   icmp pred  y, C2          ; y in CR?
+  //   icmp pred  y, C2          ; y in CR ?
   // -->
   //   icmp pred‘ (x + C3), C4   ; x in CR'?
   // if y = Cx is invertible on y \in CR
   //
-  // This currently only handles scalar, C > 0, and zext-based patterns;
+  // This currently only handles scalar, C > 0, and zext-based patterns.
   // FIXME: can vector, C < 0, and sext-based patterns be supported?
   Value *NarrowX;
   if (ICmpInst::isRelational(Pred) && MulTy->isIntegerTy() &&
       MulC->isStrictlyPositive() && match(X, m_ZExt(m_Value(NarrowX)))) {
     auto *SrcTy = cast<IntegerType>(NarrowX->getType());
     ConstantRange CmpCR = ConstantRange::makeExactICmpRegion(Pred, C);
-    if (auto SrcCR =
-            getEquivalentRangeForZExtMul(CmpCR, *MulC, SrcTy->getBitWidth()))
+    if (auto SrcCR = getEquivalentSourceRangeForZExtMul(CmpCR, *MulC,
+                                                        SrcTy->getBitWidth()))
       return emitICmpForEquivalentRange(NarrowX, *SrcCR, Builder);
   }
 
@@ -3373,14 +3373,14 @@ Instruction *InstCombinerImpl::foldICmpAddConstant(ICmpInst &Cmp,
 
   auto CR = ConstantRange::makeExactICmpRegion(Pred, C).subtract(*C2);
 
-  // Try to match and optimize the follow pattern
+  // Try to match and optimize the follow pattern:
   //   y = mul (zext x), C
-  //   icmp pred  (y + C1), C2  ; y in CR?
+  //   icmp pred  (y + C1), C2  ; y in CR ?
   // -->
   //   icmp pred’ (x + C3), C4  ; x in CR'?
   // if y = Cx is invertible on y \in CR
   //
-  // This currently only handles scalar, C > 0, and zext-based patterns;
+  // This currently only handles scalar, C > 0, and zext-based patterns.
   // FIXME: can vector, C < 0, and sext-based patterns be supported?
   Value *NarrowX;
   const APInt *MulC;
@@ -3388,7 +3388,7 @@ Instruction *InstCombinerImpl::foldICmpAddConstant(ICmpInst &Cmp,
       X->getType()->isIntegerTy() && MulC->isStrictlyPositive()) {
     auto *SrcTy = cast<IntegerType>(NarrowX->getType());
     if (auto SrcCR =
-            getEquivalentRangeForZExtMul(CR, *MulC, SrcTy->getBitWidth()))
+            getEquivalentSourceRangeForZExtMul(CR, *MulC, SrcTy->getBitWidth()))
       return emitICmpForEquivalentRange(NarrowX, *SrcCR, Builder);
   }
 



More information about the llvm-commits mailing list