[llvm] [InstCombine] Fold (X udiv Y) == X --> Y == 1 for non-zero X (PR #222806)

via llvm-commits llvm-commits at lists.llvm.org
Thu Sep 10 15:50:53 PDT 2026


https://github.com/PiJoules created https://github.com/llvm/llvm-project/pull/222806

Fold `(X udiv Y) == X` to `Y == 1` and `(X udiv Y) != X` to `Y != 1` when `X` is proven non-zero.

When the dividend is non-zero, integer division `X udiv Y` can only equal `X` if the divisor `Y` is 1.

Proof:
- For Y == 1: X / 1 == X for all X.
- For Y >= 2 and X > 0: floor(X / Y) <= X / 2 < X, so the quotient is strictly less than X and can never equal X.
- For Y == 0: udiv by zero is undefined behavior, meaning any result is valid; evaluating to false (or true for !=) is a legal refinement.
- For X == 0: 0 udiv Y == 0 for all non-zero Y, so the fold does not hold without a non-zero check. Therefore, isKnownNonZero(Dividend) is both necessary and sufficient.
- If udiv has the exact flag and X is not a multiple of Y, udiv exact produces poison; refining poison to false (or true for !=) is sound.

This eliminates hardware division instructions and replaces them with a compare against 1.

AI: Gemini was used to help draft this patch and come up with test cases. I reviewed this PR myself to the best of my ability before pushing for review.

>From 11372b90d201c0e6a2deaa16ba5326a61c34e1ef Mon Sep 17 00:00:00 2001
From: Leonard Chan <leonardchan at google.com>
Date: Thu, 10 Sep 2026 22:39:32 +0000
Subject: [PATCH] [InstCombine] Fold (X udiv Y) == X --> Y == 1 for non-zero X
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Fold `(X udiv Y) == X` to `Y == 1` and `(X udiv Y) != X` to `Y != 1`
when `X` is proven non-zero.

When the dividend is non-zero, integer division `X udiv Y` can only equal
`X` if the divisor `Y` is 1.

Proof:
- For Y == 1: X / 1 == X for all X.
- For Y >= 2 and X > 0: floor(X / Y) <= X / 2 < X, so the quotient is
  strictly less than X and can never equal X.
- For Y == 0: udiv by zero is undefined behavior, meaning any result is
  valid; evaluating to false (or true for !=) is a legal refinement.
- For X == 0: 0 udiv Y == 0 for all non-zero Y, so the fold does not
  hold without a non-zero check. Therefore, isKnownNonZero(Dividend) is
  both necessary and sufficient.
- If udiv has the exact flag and X is not a multiple of Y, udiv exact
  produces poison; refining poison to false (or true for !=) is sound.

This eliminates unpipelined hardware division instructions (15–40 CPU
cycles) and replaces them with a 1-cycle immediate compare against 1.

Supports scalars, fixed/scalable vectors, and commuted operands.

AI: Gemini was used to help draft this patch and come up with test cases. I reviewed this PR myself to the best of my ability before pushing for review.
---
 .../InstCombine/InstCombineCompares.cpp       |   8 +
 llvm/test/Transforms/InstCombine/icmp-udiv.ll | 454 ++++++++++++++++++
 2 files changed, 462 insertions(+)
 create mode 100644 llvm/test/Transforms/InstCombine/icmp-udiv.ll

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index a7015a9ddff1b..b693c68dd4b8a 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -5319,6 +5319,14 @@ Instruction *InstCombinerImpl::foldICmpBinOp(ICmpInst &I,
     return new ICmpInst(NewPred, Dividend, Divisor);
   }
 
+  // (X udiv Y) == X --> Y == 1
+  // (X udiv Y) != X --> Y != 1
+  if (I.isEquality() &&
+      match(&I, m_c_ICmp(m_UDiv(m_Value(Dividend), m_Value(Divisor)),
+                         m_Deferred(Dividend))) &&
+      isKnownNonZero(Dividend, Q))
+    return new ICmpInst(Pred, Divisor, ConstantInt::get(Divisor->getType(), 1));
+
   Value *X;
 
   // Convert add-with-unsigned-overflow comparisons into a 'not' with compare.
diff --git a/llvm/test/Transforms/InstCombine/icmp-udiv.ll b/llvm/test/Transforms/InstCombine/icmp-udiv.ll
new file mode 100644
index 0000000000000..b4c7b9be2561e
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/icmp-udiv.ll
@@ -0,0 +1,454 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+; (udiv X, Y) == X --> Y == 1 (for non-zero X)
+; (udiv X, Y) != X --> Y != 1 (for non-zero X)
+
+declare void @use_i32(i32)
+
+; Scalar tests with constant non-zero dividend.
+
+define i1 @icmp_eq_udiv_constant_dividend(i32 %y) {
+; CHECK-LABEL: define i1 @icmp_eq_udiv_constant_dividend(
+; CHECK-SAME: i32 [[Y:%.*]]) {
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[Y]], 1
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %div = udiv i32 42, %y
+  %cmp = icmp eq i32 %div, 42
+  ret i1 %cmp
+}
+
+define i1 @icmp_ne_udiv_constant_dividend(i32 %y) {
+; CHECK-LABEL: define i1 @icmp_ne_udiv_constant_dividend(
+; CHECK-SAME: i32 [[Y:%.*]]) {
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ne i32 [[Y]], 1
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %div = udiv i32 42, %y
+  %cmp = icmp ne i32 %div, 42
+  ret i1 %cmp
+}
+
+; Dynamic non-zero proof via 'or %x, 1'.
+
+define i1 @icmp_eq_udiv_dynamic_nonzero(i32 %x, i32 %y) {
+; CHECK-LABEL: define i1 @icmp_eq_udiv_dynamic_nonzero(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[Y]], 1
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %nz = or i32 %x, 1
+  %div = udiv i32 %nz, %y
+  %cmp = icmp eq i32 %div, %nz
+  ret i1 %cmp
+}
+
+define i1 @icmp_ne_udiv_dynamic_nonzero(i32 %x, i32 %y) {
+; CHECK-LABEL: define i1 @icmp_ne_udiv_dynamic_nonzero(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ne i32 [[Y]], 1
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %nz = or i32 %x, 1
+  %div = udiv i32 %nz, %y
+  %cmp = icmp ne i32 %div, %nz
+  ret i1 %cmp
+}
+
+; Commuted compare operands: X == (X udiv Y).
+
+define i1 @icmp_eq_udiv_dynamic_nonzero_commuted(i32 %x, i32 %y) {
+; CHECK-LABEL: define i1 @icmp_eq_udiv_dynamic_nonzero_commuted(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[Y]], 1
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %nz = or i32 %x, 1
+  %div = udiv i32 %nz, %y
+  %cmp = icmp eq i32 %nz, %div
+  ret i1 %cmp
+}
+
+define i1 @icmp_ne_udiv_dynamic_nonzero_commuted(i32 %x, i32 %y) {
+; CHECK-LABEL: define i1 @icmp_ne_udiv_dynamic_nonzero_commuted(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ne i32 [[Y]], 1
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %nz = or i32 %x, 1
+  %div = udiv i32 %nz, %y
+  %cmp = icmp ne i32 %nz, %div
+  ret i1 %cmp
+}
+
+; Dynamic non-zero proof via 'add nuw %x, 1'.
+
+define i1 @icmp_eq_udiv_dynamic_nonzero_add_nuw(i32 %x, i32 %y) {
+; CHECK-LABEL: define i1 @icmp_eq_udiv_dynamic_nonzero_add_nuw(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[Y]], 1
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %nz = add nuw i32 %x, 1
+  %div = udiv i32 %nz, %y
+  %cmp = icmp eq i32 %div, %nz
+  ret i1 %cmp
+}
+
+; Multi-use of udiv: fold still triggers.
+
+define i1 @icmp_eq_udiv_multi_use(i32 %x, i32 %y) {
+; CHECK-LABEL: define i1 @icmp_eq_udiv_multi_use(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT:    [[NZ:%.*]] = or i32 [[X]], 1
+; CHECK-NEXT:    [[DIV:%.*]] = udiv i32 [[NZ]], [[Y]]
+; CHECK-NEXT:    call void @use_i32(i32 [[DIV]])
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[Y]], 1
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %nz = or i32 %x, 1
+  %div = udiv i32 %nz, %y
+  call void @use_i32(i32 %div)
+  %cmp = icmp eq i32 %div, %nz
+  ret i1 %cmp
+}
+
+define i1 @icmp_ne_udiv_multi_use(i32 %x, i32 %y) {
+; CHECK-LABEL: define i1 @icmp_ne_udiv_multi_use(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT:    [[NZ:%.*]] = or i32 [[X]], 1
+; CHECK-NEXT:    [[DIV:%.*]] = udiv i32 [[NZ]], [[Y]]
+; CHECK-NEXT:    call void @use_i32(i32 [[DIV]])
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ne i32 [[Y]], 1
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %nz = or i32 %x, 1
+  %div = udiv i32 %nz, %y
+  call void @use_i32(i32 %div)
+  %cmp = icmp ne i32 %div, %nz
+  ret i1 %cmp
+}
+
+; Vector tests (<2 x i32>).
+
+define <2 x i1> @icmp_eq_udiv_vec(<2 x i32> %x, <2 x i32> %y) {
+; CHECK-LABEL: define <2 x i1> @icmp_eq_udiv_vec(
+; CHECK-SAME: <2 x i32> [[X:%.*]], <2 x i32> [[Y:%.*]]) {
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq <2 x i32> [[Y]], splat (i32 1)
+; CHECK-NEXT:    ret <2 x i1> [[CMP]]
+;
+  %nz = or <2 x i32> %x, <i32 1, i32 1>
+  %div = udiv <2 x i32> %nz, %y
+  %cmp = icmp eq <2 x i32> %div, %nz
+  ret <2 x i1> %cmp
+}
+
+define <2 x i1> @icmp_ne_udiv_vec(<2 x i32> %x, <2 x i32> %y) {
+; CHECK-LABEL: define <2 x i1> @icmp_ne_udiv_vec(
+; CHECK-SAME: <2 x i32> [[X:%.*]], <2 x i32> [[Y:%.*]]) {
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ne <2 x i32> [[Y]], splat (i32 1)
+; CHECK-NEXT:    ret <2 x i1> [[CMP]]
+;
+  %nz = or <2 x i32> %x, <i32 1, i32 1>
+  %div = udiv <2 x i32> %nz, %y
+  %cmp = icmp ne <2 x i32> %div, %nz
+  ret <2 x i1> %cmp
+}
+
+define <2 x i1> @icmp_eq_udiv_vec_commuted(<2 x i32> %x, <2 x i32> %y) {
+; CHECK-LABEL: define <2 x i1> @icmp_eq_udiv_vec_commuted(
+; CHECK-SAME: <2 x i32> [[X:%.*]], <2 x i32> [[Y:%.*]]) {
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq <2 x i32> [[Y]], splat (i32 1)
+; CHECK-NEXT:    ret <2 x i1> [[CMP]]
+;
+  %nz = or <2 x i32> %x, <i32 1, i32 1>
+  %div = udiv <2 x i32> %nz, %y
+  %cmp = icmp eq <2 x i32> %nz, %div
+  ret <2 x i1> %cmp
+}
+
+define <vscale x 2 x i1> @icmp_eq_udiv_scalable_vec(<vscale x 2 x i32> %x, <vscale x 2 x i32> %y) {
+; CHECK-LABEL: define <vscale x 2 x i1> @icmp_eq_udiv_scalable_vec(
+; CHECK-SAME: <vscale x 2 x i32> [[X:%.*]], <vscale x 2 x i32> [[Y:%.*]]) {
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq <vscale x 2 x i32> [[Y]], splat (i32 1)
+; CHECK-NEXT:    ret <vscale x 2 x i1> [[CMP]]
+;
+  %nz = or <vscale x 2 x i32> %x, splat (i32 1)
+  %div = udiv <vscale x 2 x i32> %nz, %y
+  %cmp = icmp eq <vscale x 2 x i32> %div, %nz
+  ret <vscale x 2 x i1> %cmp
+}
+
+; Negative tests: X is not known non-zero. Fold must not trigger.
+
+define i1 @not_icmp_eq_udiv_dividend_unknown_zero(i32 %x, i32 %y) {
+; CHECK-LABEL: define i1 @not_icmp_eq_udiv_dividend_unknown_zero(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT:    [[DIV:%.*]] = udiv i32 [[X]], [[Y]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[DIV]], [[X]]
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %div = udiv i32 %x, %y
+  %cmp = icmp eq i32 %div, %x
+  ret i1 %cmp
+}
+
+define i1 @not_icmp_ne_udiv_dividend_unknown_zero(i32 %x, i32 %y) {
+; CHECK-LABEL: define i1 @not_icmp_ne_udiv_dividend_unknown_zero(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT:    [[DIV:%.*]] = udiv i32 [[X]], [[Y]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ne i32 [[DIV]], [[X]]
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %div = udiv i32 %x, %y
+  %cmp = icmp ne i32 %div, %x
+  ret i1 %cmp
+}
+
+define i1 @not_icmp_eq_udiv_dividend_unknown_zero_commuted(i32 %x, i32 %y) {
+; CHECK-LABEL: define i1 @not_icmp_eq_udiv_dividend_unknown_zero_commuted(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT:    [[DIV:%.*]] = udiv i32 [[X]], [[Y]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[X]], [[DIV]]
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %div = udiv i32 %x, %y
+  %cmp = icmp eq i32 %x, %div
+  ret i1 %cmp
+}
+
+; Negative test: different dividend.
+
+define i1 @not_icmp_eq_udiv_different_dividend(i32 %x, i32 %y, i32 %z) {
+; CHECK-LABEL: define i1 @not_icmp_eq_udiv_different_dividend(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]], i32 [[Z:%.*]]) {
+; CHECK-NEXT:    [[NZ:%.*]] = or i32 [[X]], 1
+; CHECK-NEXT:    [[DIV:%.*]] = udiv i32 [[NZ]], [[Y]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[DIV]], [[Z]]
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %nz = or i32 %x, 1
+  %div = udiv i32 %nz, %y
+  %cmp = icmp eq i32 %div, %z
+  ret i1 %cmp
+}
+
+; Negative test: sdiv instead of udiv.
+
+define i1 @not_icmp_eq_sdiv(i32 %x, i32 %y) {
+; CHECK-LABEL: define i1 @not_icmp_eq_sdiv(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT:    [[NZ:%.*]] = or i32 [[X]], 1
+; CHECK-NEXT:    [[DIV:%.*]] = sdiv i32 [[NZ]], [[Y]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[DIV]], [[NZ]]
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %nz = or i32 %x, 1
+  %div = sdiv i32 %nz, %y
+  %cmp = icmp eq i32 %div, %nz
+  ret i1 %cmp
+}
+
+; Negative test: vector where not all elements are known non-zero.
+
+define <2 x i1> @not_icmp_eq_udiv_vec_partial_nonzero(<2 x i32> %x, <2 x i32> %y) {
+; CHECK-LABEL: define <2 x i1> @not_icmp_eq_udiv_vec_partial_nonzero(
+; CHECK-SAME: <2 x i32> [[X:%.*]], <2 x i32> [[Y:%.*]]) {
+; CHECK-NEXT:    [[NZ:%.*]] = or <2 x i32> [[X]], <i32 1, i32 0>
+; CHECK-NEXT:    [[DIV:%.*]] = udiv <2 x i32> [[NZ]], [[Y]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq <2 x i32> [[DIV]], [[NZ]]
+; CHECK-NEXT:    ret <2 x i1> [[CMP]]
+;
+  %nz = or <2 x i32> %x, <i32 1, i32 0>
+  %div = udiv <2 x i32> %nz, %y
+  %cmp = icmp eq <2 x i32> %div, %nz
+  ret <2 x i1> %cmp
+}
+
+; Negative test: relational predicate is not folded by this equality rule.
+
+define i1 @not_icmp_ult_udiv(i32 %x, i32 %y) {
+; CHECK-LABEL: define i1 @not_icmp_ult_udiv(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT:    [[NZ:%.*]] = or i32 [[X]], 1
+; CHECK-NEXT:    [[DIV:%.*]] = udiv i32 [[NZ]], [[Y]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i32 [[DIV]], [[NZ]]
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %nz = or i32 %x, 1
+  %div = udiv i32 %nz, %y
+  %cmp = icmp ult i32 %div, %nz
+  ret i1 %cmp
+}
+
+; Context-sensitive non-zero proofs: llvm.assume and dominating branch.
+
+declare void @llvm.assume(i1)
+
+define i1 @icmp_eq_udiv_assume(i32 %x, i32 %y) {
+; CHECK-LABEL: define i1 @icmp_eq_udiv_assume(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT:    [[NZ:%.*]] = icmp ne i32 [[X]], 0
+; CHECK-NEXT:    call void @llvm.assume(i1 [[NZ]])
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[Y]], 1
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %nz = icmp ne i32 %x, 0
+  call void @llvm.assume(i1 %nz)
+  %div = udiv i32 %x, %y
+  %cmp = icmp eq i32 %div, %x
+  ret i1 %cmp
+}
+
+define i1 @icmp_eq_udiv_dominating_branch(i32 %x, i32 %y) {
+; CHECK-LABEL: define i1 @icmp_eq_udiv_dominating_branch(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[NZ_NOT:%.*]] = icmp eq i32 [[X]], 0
+; CHECK-NEXT:    br i1 [[NZ_NOT]], label %[[ELSE:.*]], label %[[THEN:.*]]
+; CHECK:       [[THEN]]:
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[Y]], 1
+; CHECK-NEXT:    ret i1 [[CMP]]
+; CHECK:       [[ELSE]]:
+; CHECK-NEXT:    ret i1 false
+;
+entry:
+  %nz = icmp ne i32 %x, 0
+  br i1 %nz, label %then, label %else
+
+then:
+  %div = udiv i32 %x, %y
+  %cmp = icmp eq i32 %div, %x
+  ret i1 %cmp
+
+else:
+  ret i1 false
+}
+
+; i1 bitwidth tests.
+
+define i1 @icmp_eq_udiv_i1(i1 %y) {
+; CHECK-LABEL: define i1 @icmp_eq_udiv_i1(
+; CHECK-SAME: i1 [[Y:%.*]]) {
+; CHECK-NEXT:    ret i1 true
+;
+  %div = udiv i1 true, %y
+  %cmp = icmp eq i1 %div, true
+  ret i1 %cmp
+}
+
+define i1 @icmp_ne_udiv_i1(i1 %y) {
+; CHECK-LABEL: define i1 @icmp_ne_udiv_i1(
+; CHECK-SAME: i1 [[Y:%.*]]) {
+; CHECK-NEXT:    ret i1 false
+;
+  %div = udiv i1 true, %y
+  %cmp = icmp ne i1 %div, true
+  ret i1 %cmp
+}
+
+; Commuted constant dividend tests.
+
+define i1 @icmp_eq_udiv_constant_dividend_commuted(i32 %y) {
+; CHECK-LABEL: define i1 @icmp_eq_udiv_constant_dividend_commuted(
+; CHECK-SAME: i32 [[Y:%.*]]) {
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[Y]], 1
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %div = udiv i32 42, %y
+  %cmp = icmp eq i32 42, %div
+  ret i1 %cmp
+}
+
+define i1 @icmp_ne_udiv_constant_dividend_commuted(i32 %y) {
+; CHECK-LABEL: define i1 @icmp_ne_udiv_constant_dividend_commuted(
+; CHECK-SAME: i32 [[Y:%.*]]) {
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ne i32 [[Y]], 1
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %div = udiv i32 42, %y
+  %cmp = icmp ne i32 42, %div
+  ret i1 %cmp
+}
+
+; Commuted vector ne test.
+
+define <2 x i1> @icmp_ne_udiv_vec_commuted(<2 x i32> %x, <2 x i32> %y) {
+; CHECK-LABEL: define <2 x i1> @icmp_ne_udiv_vec_commuted(
+; CHECK-SAME: <2 x i32> [[X:%.*]], <2 x i32> [[Y:%.*]]) {
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ne <2 x i32> [[Y]], splat (i32 1)
+; CHECK-NEXT:    ret <2 x i1> [[CMP]]
+;
+  %nz = or <2 x i32> %x, <i32 1, i32 1>
+  %div = udiv <2 x i32> %nz, %y
+  %cmp = icmp ne <2 x i32> %nz, %div
+  ret <2 x i1> %cmp
+}
+
+; Scalable vector ne test.
+
+define <vscale x 2 x i1> @icmp_ne_udiv_scalable_vec(<vscale x 2 x i32> %x, <vscale x 2 x i32> %y) {
+; CHECK-LABEL: define <vscale x 2 x i1> @icmp_ne_udiv_scalable_vec(
+; CHECK-SAME: <vscale x 2 x i32> [[X:%.*]], <vscale x 2 x i32> [[Y:%.*]]) {
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ne <vscale x 2 x i32> [[Y]], splat (i32 1)
+; CHECK-NEXT:    ret <vscale x 2 x i1> [[CMP]]
+;
+  %nz = or <vscale x 2 x i32> %x, splat (i32 1)
+  %div = udiv <vscale x 2 x i32> %nz, %y
+  %cmp = icmp ne <vscale x 2 x i32> %div, %nz
+  ret <vscale x 2 x i1> %cmp
+}
+
+; Constant non-1 divisor with dynamic non-zero dividend.
+
+define i1 @icmp_eq_udiv_constant_divisor_nz(i32 %x) {
+; CHECK-LABEL: define i1 @icmp_eq_udiv_constant_divisor_nz(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT:    ret i1 false
+;
+  %nz = or i32 %x, 1
+  %div = udiv i32 %nz, 3
+  %cmp = icmp eq i32 %div, %nz
+  ret i1 %cmp
+}
+
+define i1 @icmp_ne_udiv_constant_divisor_nz(i32 %x) {
+; CHECK-LABEL: define i1 @icmp_ne_udiv_constant_divisor_nz(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT:    ret i1 true
+;
+  %nz = or i32 %x, 1
+  %div = udiv i32 %nz, 3
+  %cmp = icmp ne i32 %div, %nz
+  ret i1 %cmp
+}
+
+; Exact udiv.
+
+define i1 @icmp_eq_udiv_exact(i32 %x, i32 %y) {
+; CHECK-LABEL: define i1 @icmp_eq_udiv_exact(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[Y]], 1
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %nz = or i32 %x, 1
+  %div = udiv exact i32 %nz, %y
+  %cmp = icmp eq i32 %div, %nz
+  ret i1 %cmp
+}
+
+; Wide integer bitwidth (i128).
+
+define i1 @icmp_eq_udiv_i128(i128 %x, i128 %y) {
+; CHECK-LABEL: define i1 @icmp_eq_udiv_i128(
+; CHECK-SAME: i128 [[X:%.*]], i128 [[Y:%.*]]) {
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i128 [[Y]], 1
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %nz = or i128 %x, 1
+  %div = udiv i128 %nz, %y
+  %cmp = icmp eq i128 %div, %nz
+  ret i1 %cmp
+}
+



More information about the llvm-commits mailing list