[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