[llvm] [InstCombine] Remove Multiplication in Range Comparisons When Invertible (PR #186347)

Kunqiu Chen via llvm-commits llvm-commits at lists.llvm.org
Fri Mar 13 02:52:11 PDT 2026


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

>From 5280b3302edf2e0fc7dbf932f1000b0bfc77344a Mon Sep 17 00:00:00 2001
From: Camsyn <camsyn at foxmail.com>
Date: Fri, 13 Mar 2026 17:51:56 +0800
Subject: [PATCH] Pre-commit test

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

diff --git a/llvm/test/Transforms/InstCombine/icmp-mul.ll b/llvm/test/Transforms/InstCombine/icmp-mul.ll
index 49e1e11fe6c36..e9d11d7f97a44 100644
--- a/llvm/test/Transforms/InstCombine/icmp-mul.ll
+++ b/llvm/test/Transforms/InstCombine/icmp-mul.ll
@@ -1568,3 +1568,234 @@ 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
+
+; 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
+; Y = [-2^26, 262657)
+; 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
+; Y = [-2^15, 257)
+; 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
+; Y = [-2^7, 17)
+; 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
+; Y = [0, 50)
+; Invertible: yes
+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
+; Y = [0, 200)
+; 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
+; Y = [200, 256)
+; 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
+; Y = [60, 128)
+; 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
+; Y = [60, 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
+; Y = [-2^7, 16)
+; 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
+; Y = [0, 45)
+; 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
+; Y = [0, 16)
+; 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
+; Y = [-2^7, 60)
+; 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
+; Y = [60, 256)
+; 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
+}



More information about the llvm-commits mailing list