[llvm] [InstCombine] canonicalize sign bit checks (PR #122962)

Jacob Young via llvm-commits llvm-commits at lists.llvm.org
Wed Jan 15 10:05:27 PST 2025


https://github.com/jacobly0 updated https://github.com/llvm/llvm-project/pull/122962

>From 1e39a639988de9eb70293a09c6fe436bd335cf7e Mon Sep 17 00:00:00 2001
From: Jacob Young <jacobly0 at users.noreply.github.com>
Date: Sun, 12 Jan 2025 10:21:39 -0500
Subject: [PATCH 1/4] [InstCombine][NFC] precommit tests for signed floor
 division

---
 llvm/test/Transforms/InstCombine/add.ll       |  34 ++
 llvm/test/Transforms/InstCombine/icmp-srem.ll | 385 ++++++++++++++++++
 2 files changed, 419 insertions(+)
 create mode 100644 llvm/test/Transforms/InstCombine/icmp-srem.ll

diff --git a/llvm/test/Transforms/InstCombine/add.ll b/llvm/test/Transforms/InstCombine/add.ll
index 222f87fa3a5f18..5684bee06b9164 100644
--- a/llvm/test/Transforms/InstCombine/add.ll
+++ b/llvm/test/Transforms/InstCombine/add.ll
@@ -3018,6 +3018,40 @@ define i32 @floor_sdiv_wrong_op(i32 %x, i32 %y) {
   ret i32 %r
 }
 
+define i32 @floor_sdiv_using_srem_by_8(i32 %x) {
+; CHECK-LABEL: @floor_sdiv_using_srem_by_8(
+; CHECK-NEXT:    [[D:%.*]] = sdiv i32 [[X:%.*]], 8
+; CHECK-NEXT:    [[R:%.*]] = srem i32 [[X]], 8
+; CHECK-NEXT:    [[I:%.*]] = icmp ugt i32 [[R]], -2147483648
+; CHECK-NEXT:    [[S:%.*]] = sext i1 [[I]] to i32
+; CHECK-NEXT:    [[F:%.*]] = add nsw i32 [[D]], [[S]]
+; CHECK-NEXT:    ret i32 [[F]]
+;
+  %d = sdiv i32 %x, 8
+  %r = srem i32 %x, 8
+  %i = icmp ugt i32 %r, -2147483648
+  %s = sext i1 %i to i32
+  %f = add i32 %d, %s
+  ret i32 %f
+}
+
+define i32 @floor_sdiv_using_srem_by_2(i32 %x) {
+; CHECK-LABEL: @floor_sdiv_using_srem_by_2(
+; CHECK-NEXT:    [[D:%.*]] = sdiv i32 [[X:%.*]], 2
+; CHECK-NEXT:    [[R:%.*]] = srem i32 [[X]], 2
+; CHECK-NEXT:    [[I:%.*]] = icmp ugt i32 [[R]], -2147483648
+; CHECK-NEXT:    [[S:%.*]] = sext i1 [[I]] to i32
+; CHECK-NEXT:    [[F:%.*]] = add nsw i32 [[D]], [[S]]
+; CHECK-NEXT:    ret i32 [[F]]
+;
+  %d = sdiv i32 %x, 2
+  %r = srem i32 %x, 2
+  %i = icmp ugt i32 %r, -2147483648
+  %s = sext i1 %i to i32
+  %f = add i32 %d, %s
+  ret i32 %f
+}
+
 ; (X s>> (BW - 1)) + (zext (X s> 0)) --> (X s>> (BW - 1)) | (zext (X != 0))
 
 define i8 @signum_i8_i8(i8 %x) {
diff --git a/llvm/test/Transforms/InstCombine/icmp-srem.ll b/llvm/test/Transforms/InstCombine/icmp-srem.ll
new file mode 100644
index 00000000000000..5d57f8f26e21ff
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/icmp-srem.ll
@@ -0,0 +1,385 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+define i1 @icmp_ugt_sremsmin_smin(i32 %x) {
+; CHECK-LABEL: define i1 @icmp_ugt_sremsmin_smin(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = srem i32 [[X]], -2147483648
+; CHECK-NEXT:    [[C:%.*]] = icmp ugt i32 [[R]], -2147483648
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %r = srem i32 %x, -2147483648
+  %c = icmp ugt i32 %r, -2147483648
+  ret i1 %c
+}
+
+define i1 @icmp_ugt_sremsmin_sminp1(i32 %x) {
+; CHECK-LABEL: define i1 @icmp_ugt_sremsmin_sminp1(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = srem i32 [[X]], -2147483648
+; CHECK-NEXT:    [[C:%.*]] = icmp ugt i32 [[R]], -2147483647
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %r = srem i32 %x, -2147483648
+  %c = icmp ugt i32 %r, -2147483647
+  ret i1 %c
+}
+
+define i1 @icmp_ugt_sremsmin_smaxm1(i32 %x) {
+; CHECK-LABEL: define i1 @icmp_ugt_sremsmin_smaxm1(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = srem i32 [[X]], -2147483648
+; CHECK-NEXT:    [[C:%.*]] = icmp ugt i32 [[R]], 2147483646
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %r = srem i32 %x, -2147483648
+  %c = icmp ugt i32 %r, 2147483646
+  ret i1 %c
+}
+
+define i1 @icmp_ugt_sremsmin_smax(i32 %x) {
+; CHECK-LABEL: define i1 @icmp_ugt_sremsmin_smax(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT:    [[C:%.*]] = icmp ugt i32 [[X]], -2147483648
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %r = srem i32 %x, -2147483648
+  %c = icmp ugt i32 %r, 2147483647
+  ret i1 %c
+}
+
+define i1 @icmp_ult_sremsmin_smin(i32 %x) {
+; CHECK-LABEL: define i1 @icmp_ult_sremsmin_smin(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = srem i32 [[X]], -2147483648
+; CHECK-NEXT:    [[C:%.*]] = icmp sgt i32 [[R]], -1
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %r = srem i32 %x, -2147483648
+  %c = icmp ult i32 %r, -2147483648
+  ret i1 %c
+}
+
+define i1 @icmp_ult_sremsmin_sminp1(i32 %x) {
+; CHECK-LABEL: define i1 @icmp_ult_sremsmin_sminp1(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = srem i32 [[X]], -2147483648
+; CHECK-NEXT:    [[C:%.*]] = icmp ult i32 [[R]], -2147483647
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %r = srem i32 %x, -2147483648
+  %c = icmp ult i32 %r, -2147483647
+  ret i1 %c
+}
+
+define i1 @icmp_ult_sremsmin_sminp2(i32 %x) {
+; CHECK-LABEL: define i1 @icmp_ult_sremsmin_sminp2(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = srem i32 [[X]], -2147483648
+; CHECK-NEXT:    [[C:%.*]] = icmp ult i32 [[R]], -2147483646
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %r = srem i32 %x, -2147483648
+  %c = icmp ult i32 %r, -2147483646
+  ret i1 %c
+}
+
+define i1 @icmp_ult_sremsmin_smax(i32 %x) {
+; CHECK-LABEL: define i1 @icmp_ult_sremsmin_smax(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = srem i32 [[X]], -2147483648
+; CHECK-NEXT:    [[C:%.*]] = icmp ult i32 [[R]], 2147483647
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %r = srem i32 %x, -2147483648
+  %c = icmp ult i32 %r, 2147483647
+  ret i1 %c
+}
+
+define i1 @icmp_ugt_srem5_smin(i32 %x) {
+; CHECK-LABEL: define i1 @icmp_ugt_srem5_smin(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = srem i32 [[X]], 5
+; CHECK-NEXT:    [[C:%.*]] = icmp ugt i32 [[R]], -2147483648
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %r = srem i32 %x, 5
+  %c = icmp ugt i32 %r, -2147483648
+  ret i1 %c
+}
+
+define i1 @icmp_ugt_srem5_m5(i32 %x) {
+; CHECK-LABEL: define i1 @icmp_ugt_srem5_m5(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = srem i32 [[X]], 5
+; CHECK-NEXT:    [[C:%.*]] = icmp ugt i32 [[R]], -5
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %r = srem i32 %x, 5
+  %c = icmp ugt i32 %r, -5
+  ret i1 %c
+}
+
+define i1 @icmp_ugt_srem5_m4(i32 %x) {
+; CHECK-LABEL: define i1 @icmp_ugt_srem5_m4(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = srem i32 [[X]], 5
+; CHECK-NEXT:    [[C:%.*]] = icmp ugt i32 [[R]], -4
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %r = srem i32 %x, 5
+  %c = icmp ugt i32 %r, -4
+  ret i1 %c
+}
+
+define i1 @icmp_ugt_srem5_3(i32 %x) {
+; CHECK-LABEL: define i1 @icmp_ugt_srem5_3(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = srem i32 [[X]], 5
+; CHECK-NEXT:    [[C:%.*]] = icmp ugt i32 [[R]], 3
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %r = srem i32 %x, 5
+  %c = icmp ugt i32 %r, 3
+  ret i1 %c
+}
+
+define i1 @icmp_ugt_srem5_4(i32 %x) {
+; CHECK-LABEL: define i1 @icmp_ugt_srem5_4(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = srem i32 [[X]], 5
+; CHECK-NEXT:    [[C:%.*]] = icmp ugt i32 [[R]], 4
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %r = srem i32 %x, 5
+  %c = icmp ugt i32 %r, 4
+  ret i1 %c
+}
+
+define i1 @icmp_ugt_srem5_smaxm1(i32 %x) {
+; CHECK-LABEL: define i1 @icmp_ugt_srem5_smaxm1(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = srem i32 [[X]], 5
+; CHECK-NEXT:    [[C:%.*]] = icmp ugt i32 [[R]], 2147483646
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %r = srem i32 %x, 5
+  %c = icmp ugt i32 %r, 2147483646
+  ret i1 %c
+}
+
+define i1 @icmp_ult_srem5_sminp1(i32 %x) {
+; CHECK-LABEL: define i1 @icmp_ult_srem5_sminp1(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = srem i32 [[X]], 5
+; CHECK-NEXT:    [[C:%.*]] = icmp ult i32 [[R]], -2147483647
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %r = srem i32 %x, 5
+  %c = icmp ult i32 %r, -2147483647
+  ret i1 %c
+}
+
+define i1 @icmp_ult_srem5_m4(i32 %x) {
+; CHECK-LABEL: define i1 @icmp_ult_srem5_m4(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = srem i32 [[X]], 5
+; CHECK-NEXT:    [[C:%.*]] = icmp ult i32 [[R]], -4
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %r = srem i32 %x, 5
+  %c = icmp ult i32 %r, -4
+  ret i1 %c
+}
+
+define i1 @icmp_ult_srem5_m3(i32 %x) {
+; CHECK-LABEL: define i1 @icmp_ult_srem5_m3(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = srem i32 [[X]], 5
+; CHECK-NEXT:    [[C:%.*]] = icmp ult i32 [[R]], -3
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %r = srem i32 %x, 5
+  %c = icmp ult i32 %r, -3
+  ret i1 %c
+}
+
+define i1 @icmp_ult_srem5_4(i32 %x) {
+; CHECK-LABEL: define i1 @icmp_ult_srem5_4(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = srem i32 [[X]], 5
+; CHECK-NEXT:    [[C:%.*]] = icmp ult i32 [[R]], 4
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %r = srem i32 %x, 5
+  %c = icmp ult i32 %r, 4
+  ret i1 %c
+}
+
+define i1 @icmp_ult_srem5_5(i32 %x) {
+; CHECK-LABEL: define i1 @icmp_ult_srem5_5(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = srem i32 [[X]], 5
+; CHECK-NEXT:    [[C:%.*]] = icmp ult i32 [[R]], 5
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %r = srem i32 %x, 5
+  %c = icmp ult i32 %r, 5
+  ret i1 %c
+}
+
+define i1 @icmp_ult_srem5_smax(i32 %x) {
+; CHECK-LABEL: define i1 @icmp_ult_srem5_smax(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = srem i32 [[X]], 5
+; CHECK-NEXT:    [[C:%.*]] = icmp ult i32 [[R]], 2147483647
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %r = srem i32 %x, 5
+  %c = icmp ult i32 %r, 2147483647
+  ret i1 %c
+}
+
+define i1 @icmp_ugt_sremsmax_smin(i32 %x) {
+; CHECK-LABEL: define i1 @icmp_ugt_sremsmax_smin(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = srem i32 [[X]], 2147483647
+; CHECK-NEXT:    [[C:%.*]] = icmp ugt i32 [[R]], -2147483648
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %r = srem i32 %x, 2147483647
+  %c = icmp ugt i32 %r, -2147483648
+  ret i1 %c
+}
+
+define i1 @icmp_ugt_sremsmax_sminp1(i32 %x) {
+; CHECK-LABEL: define i1 @icmp_ugt_sremsmax_sminp1(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = srem i32 [[X]], 2147483647
+; CHECK-NEXT:    [[C:%.*]] = icmp ugt i32 [[R]], -2147483647
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %r = srem i32 %x, 2147483647
+  %c = icmp ugt i32 %r, -2147483647
+  ret i1 %c
+}
+
+define i1 @icmp_ugt_sremsmax_sminp2(i32 %x) {
+; CHECK-LABEL: define i1 @icmp_ugt_sremsmax_sminp2(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = srem i32 [[X]], 2147483647
+; CHECK-NEXT:    [[C:%.*]] = icmp ugt i32 [[R]], -2147483646
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %r = srem i32 %x, 2147483647
+  %c = icmp ugt i32 %r, -2147483646
+  ret i1 %c
+}
+
+define i1 @icmp_ugt_sremsmax_smaxm2(i32 %x) {
+; CHECK-LABEL: define i1 @icmp_ugt_sremsmax_smaxm2(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = srem i32 [[X]], 2147483647
+; CHECK-NEXT:    [[C:%.*]] = icmp ugt i32 [[R]], 2147483645
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %r = srem i32 %x, 2147483647
+  %c = icmp ugt i32 %r, 2147483645
+  ret i1 %c
+}
+
+define i1 @icmp_ugt_sremsmax_smaxm1(i32 %x) {
+; CHECK-LABEL: define i1 @icmp_ugt_sremsmax_smaxm1(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = srem i32 [[X]], 2147483647
+; CHECK-NEXT:    [[C:%.*]] = icmp ugt i32 [[R]], 2147483646
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %r = srem i32 %x, 2147483647
+  %c = icmp ugt i32 %r, 2147483646
+  ret i1 %c
+}
+
+define i1 @icmp_ugt_sremsmax_smax(i32 %x) {
+; CHECK-LABEL: define i1 @icmp_ugt_sremsmax_smax(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = srem i32 [[X]], 2147483647
+; CHECK-NEXT:    [[C:%.*]] = icmp slt i32 [[R]], 0
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %r = srem i32 %x, 2147483647
+  %c = icmp ugt i32 %r, 2147483647
+  ret i1 %c
+}
+
+define i1 @icmp_ult_sremsmax_smin(i32 %x) {
+; CHECK-LABEL: define i1 @icmp_ult_sremsmax_smin(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = srem i32 [[X]], 2147483647
+; CHECK-NEXT:    [[C:%.*]] = icmp sgt i32 [[R]], -1
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %r = srem i32 %x, 2147483647
+  %c = icmp ult i32 %r, -2147483648
+  ret i1 %c
+}
+
+define i1 @icmp_ult_sremsmax_sminp1(i32 %x) {
+; CHECK-LABEL: define i1 @icmp_ult_sremsmax_sminp1(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = srem i32 [[X]], 2147483647
+; CHECK-NEXT:    [[C:%.*]] = icmp ult i32 [[R]], -2147483647
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %r = srem i32 %x, 2147483647
+  %c = icmp ult i32 %r, -2147483647
+  ret i1 %c
+}
+
+define i1 @icmp_ult_sremsmax_sminp2(i32 %x) {
+; CHECK-LABEL: define i1 @icmp_ult_sremsmax_sminp2(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = srem i32 [[X]], 2147483647
+; CHECK-NEXT:    [[C:%.*]] = icmp ult i32 [[R]], -2147483646
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %r = srem i32 %x, 2147483647
+  %c = icmp ult i32 %r, -2147483646
+  ret i1 %c
+}
+
+define i1 @icmp_ult_sremsmax_sminp3(i32 %x) {
+; CHECK-LABEL: define i1 @icmp_ult_sremsmax_sminp3(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = srem i32 [[X]], 2147483647
+; CHECK-NEXT:    [[C:%.*]] = icmp ult i32 [[R]], -2147483645
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %r = srem i32 %x, 2147483647
+  %c = icmp ult i32 %r, -2147483645
+  ret i1 %c
+}
+
+define i1 @icmp_ult_sremsmax_smaxm1(i32 %x) {
+; CHECK-LABEL: define i1 @icmp_ult_sremsmax_smaxm1(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = srem i32 [[X]], 2147483647
+; CHECK-NEXT:    [[C:%.*]] = icmp ult i32 [[R]], 2147483646
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %r = srem i32 %x, 2147483647
+  %c = icmp ult i32 %r, 2147483646
+  ret i1 %c
+}
+
+define i1 @icmp_ult_sremsmax_smax(i32 %x) {
+; CHECK-LABEL: define i1 @icmp_ult_sremsmax_smax(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = srem i32 [[X]], 2147483647
+; CHECK-NEXT:    [[C:%.*]] = icmp ult i32 [[R]], 2147483647
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %r = srem i32 %x, 2147483647
+  %c = icmp ult i32 %r, 2147483647
+  ret i1 %c
+}

>From a31cf76efddc9cdd5a407b587803248e09c86ddf Mon Sep 17 00:00:00 2001
From: Jacob Young <jacobly0 at users.noreply.github.com>
Date: Sun, 12 Jan 2025 11:30:26 -0500
Subject: [PATCH 2/4] [InstCombine] fold unsigned predicates to signed on srem
 result

This allows optimization of more signed floor implementations when the
divisor is a known power of two to an arithmetic shift.

Proof for the implemented optimizations:
https://alive2.llvm.org/ce/z/j6C-Nz

Proof for the test cases:
https://alive2.llvm.org/ce/z/M_PBjw
---
 .../InstCombine/InstCombineCompares.cpp       | 33 ++++++++++++++++++-
 llvm/test/Transforms/InstCombine/add.ll       | 12 ++-----
 llvm/test/Transforms/InstCombine/icmp-srem.ll | 33 +++++++++----------
 3 files changed, 50 insertions(+), 28 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index 2e457257599493..5a4791870ac77b 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -2674,10 +2674,41 @@ Instruction *InstCombinerImpl::foldICmpShrConstant(ICmpInst &Cmp,
 Instruction *InstCombinerImpl::foldICmpSRemConstant(ICmpInst &Cmp,
                                                     BinaryOperator *SRem,
                                                     const APInt &C) {
+  const ICmpInst::Predicate Pred = Cmp.getPredicate();
+  if (Pred == ICmpInst::ICMP_UGT || Pred == ICmpInst::ICMP_ULT) {
+    // Canonicalize unsigned predicates to signed:
+    // (X s% DivisorC) u> C -> (X s% DivisorC) s< 0
+    //   iff (C s< 0 ? ~C : C) u>= abs(DivisorC)-1
+    // (X s% DivisorC) u< C+1 -> (X s% DivisorC) s> -1
+    //   iff (C+1 s< 0 ? ~C : C) u>= abs(DivisorC)-1
+
+    const APInt *DivisorC;
+    if (!match(SRem->getOperand(1), m_APInt(DivisorC)))
+      return nullptr;
+
+    APInt NormalizedC = C;
+    if (Pred == ICmpInst::ICMP_ULT) {
+      assert(!NormalizedC.isZero() &&
+             "ult X, 0 should have been simplified already.");
+      --NormalizedC;
+    }
+    if (C.isNegative())
+      NormalizedC.flipAllBits();
+    assert(!DivisorC->isZero() &&
+           "srem X, 0 should have been simplified already.");
+    if (!NormalizedC.uge(DivisorC->abs() - 1))
+      return nullptr;
+
+    Type *Ty = SRem->getType();
+    if (Pred == ICmpInst::ICMP_UGT)
+      return new ICmpInst(ICmpInst::ICMP_SLT, SRem,
+                          ConstantInt::getNullValue(Ty));
+    return new ICmpInst(ICmpInst::ICMP_SGT, SRem,
+                        ConstantInt::getAllOnesValue(Ty));
+  }
   // Match an 'is positive' or 'is negative' comparison of remainder by a
   // constant power-of-2 value:
   // (X % pow2C) sgt/slt 0
-  const ICmpInst::Predicate Pred = Cmp.getPredicate();
   if (Pred != ICmpInst::ICMP_SGT && Pred != ICmpInst::ICMP_SLT &&
       Pred != ICmpInst::ICMP_EQ && Pred != ICmpInst::ICMP_NE)
     return nullptr;
diff --git a/llvm/test/Transforms/InstCombine/add.ll b/llvm/test/Transforms/InstCombine/add.ll
index 5684bee06b9164..495f99824652d6 100644
--- a/llvm/test/Transforms/InstCombine/add.ll
+++ b/llvm/test/Transforms/InstCombine/add.ll
@@ -3020,11 +3020,7 @@ define i32 @floor_sdiv_wrong_op(i32 %x, i32 %y) {
 
 define i32 @floor_sdiv_using_srem_by_8(i32 %x) {
 ; CHECK-LABEL: @floor_sdiv_using_srem_by_8(
-; CHECK-NEXT:    [[D:%.*]] = sdiv i32 [[X:%.*]], 8
-; CHECK-NEXT:    [[R:%.*]] = srem i32 [[X]], 8
-; CHECK-NEXT:    [[I:%.*]] = icmp ugt i32 [[R]], -2147483648
-; CHECK-NEXT:    [[S:%.*]] = sext i1 [[I]] to i32
-; CHECK-NEXT:    [[F:%.*]] = add nsw i32 [[D]], [[S]]
+; CHECK-NEXT:    [[F:%.*]] = ashr i32 [[X:%.*]], 3
 ; CHECK-NEXT:    ret i32 [[F]]
 ;
   %d = sdiv i32 %x, 8
@@ -3037,11 +3033,7 @@ define i32 @floor_sdiv_using_srem_by_8(i32 %x) {
 
 define i32 @floor_sdiv_using_srem_by_2(i32 %x) {
 ; CHECK-LABEL: @floor_sdiv_using_srem_by_2(
-; CHECK-NEXT:    [[D:%.*]] = sdiv i32 [[X:%.*]], 2
-; CHECK-NEXT:    [[R:%.*]] = srem i32 [[X]], 2
-; CHECK-NEXT:    [[I:%.*]] = icmp ugt i32 [[R]], -2147483648
-; CHECK-NEXT:    [[S:%.*]] = sext i1 [[I]] to i32
-; CHECK-NEXT:    [[F:%.*]] = add nsw i32 [[D]], [[S]]
+; CHECK-NEXT:    [[F:%.*]] = ashr i32 [[X:%.*]], 1
 ; CHECK-NEXT:    ret i32 [[F]]
 ;
   %d = sdiv i32 %x, 2
diff --git a/llvm/test/Transforms/InstCombine/icmp-srem.ll b/llvm/test/Transforms/InstCombine/icmp-srem.ll
index 5d57f8f26e21ff..dbf10d80bc2428 100644
--- a/llvm/test/Transforms/InstCombine/icmp-srem.ll
+++ b/llvm/test/Transforms/InstCombine/icmp-srem.ll
@@ -4,8 +4,7 @@
 define i1 @icmp_ugt_sremsmin_smin(i32 %x) {
 ; CHECK-LABEL: define i1 @icmp_ugt_sremsmin_smin(
 ; CHECK-SAME: i32 [[X:%.*]]) {
-; CHECK-NEXT:    [[R:%.*]] = srem i32 [[X]], -2147483648
-; CHECK-NEXT:    [[C:%.*]] = icmp ugt i32 [[R]], -2147483648
+; CHECK-NEXT:    [[C:%.*]] = icmp ugt i32 [[X]], -2147483648
 ; CHECK-NEXT:    ret i1 [[C]]
 ;
   %r = srem i32 %x, -2147483648
@@ -64,7 +63,7 @@ define i1 @icmp_ult_sremsmin_sminp1(i32 %x) {
 ; CHECK-LABEL: define i1 @icmp_ult_sremsmin_sminp1(
 ; CHECK-SAME: i32 [[X:%.*]]) {
 ; CHECK-NEXT:    [[R:%.*]] = srem i32 [[X]], -2147483648
-; CHECK-NEXT:    [[C:%.*]] = icmp ult i32 [[R]], -2147483647
+; CHECK-NEXT:    [[C:%.*]] = icmp sgt i32 [[R]], -1
 ; CHECK-NEXT:    ret i1 [[C]]
 ;
   %r = srem i32 %x, -2147483648
@@ -100,7 +99,7 @@ define i1 @icmp_ugt_srem5_smin(i32 %x) {
 ; CHECK-LABEL: define i1 @icmp_ugt_srem5_smin(
 ; CHECK-SAME: i32 [[X:%.*]]) {
 ; CHECK-NEXT:    [[R:%.*]] = srem i32 [[X]], 5
-; CHECK-NEXT:    [[C:%.*]] = icmp ugt i32 [[R]], -2147483648
+; CHECK-NEXT:    [[C:%.*]] = icmp slt i32 [[R]], 0
 ; CHECK-NEXT:    ret i1 [[C]]
 ;
   %r = srem i32 %x, 5
@@ -112,7 +111,7 @@ define i1 @icmp_ugt_srem5_m5(i32 %x) {
 ; CHECK-LABEL: define i1 @icmp_ugt_srem5_m5(
 ; CHECK-SAME: i32 [[X:%.*]]) {
 ; CHECK-NEXT:    [[R:%.*]] = srem i32 [[X]], 5
-; CHECK-NEXT:    [[C:%.*]] = icmp ugt i32 [[R]], -5
+; CHECK-NEXT:    [[C:%.*]] = icmp slt i32 [[R]], 0
 ; CHECK-NEXT:    ret i1 [[C]]
 ;
   %r = srem i32 %x, 5
@@ -148,7 +147,7 @@ define i1 @icmp_ugt_srem5_4(i32 %x) {
 ; CHECK-LABEL: define i1 @icmp_ugt_srem5_4(
 ; CHECK-SAME: i32 [[X:%.*]]) {
 ; CHECK-NEXT:    [[R:%.*]] = srem i32 [[X]], 5
-; CHECK-NEXT:    [[C:%.*]] = icmp ugt i32 [[R]], 4
+; CHECK-NEXT:    [[C:%.*]] = icmp slt i32 [[R]], 0
 ; CHECK-NEXT:    ret i1 [[C]]
 ;
   %r = srem i32 %x, 5
@@ -160,7 +159,7 @@ define i1 @icmp_ugt_srem5_smaxm1(i32 %x) {
 ; CHECK-LABEL: define i1 @icmp_ugt_srem5_smaxm1(
 ; CHECK-SAME: i32 [[X:%.*]]) {
 ; CHECK-NEXT:    [[R:%.*]] = srem i32 [[X]], 5
-; CHECK-NEXT:    [[C:%.*]] = icmp ugt i32 [[R]], 2147483646
+; CHECK-NEXT:    [[C:%.*]] = icmp slt i32 [[R]], 0
 ; CHECK-NEXT:    ret i1 [[C]]
 ;
   %r = srem i32 %x, 5
@@ -172,7 +171,7 @@ define i1 @icmp_ult_srem5_sminp1(i32 %x) {
 ; CHECK-LABEL: define i1 @icmp_ult_srem5_sminp1(
 ; CHECK-SAME: i32 [[X:%.*]]) {
 ; CHECK-NEXT:    [[R:%.*]] = srem i32 [[X]], 5
-; CHECK-NEXT:    [[C:%.*]] = icmp ult i32 [[R]], -2147483647
+; CHECK-NEXT:    [[C:%.*]] = icmp sgt i32 [[R]], -1
 ; CHECK-NEXT:    ret i1 [[C]]
 ;
   %r = srem i32 %x, 5
@@ -184,7 +183,7 @@ define i1 @icmp_ult_srem5_m4(i32 %x) {
 ; CHECK-LABEL: define i1 @icmp_ult_srem5_m4(
 ; CHECK-SAME: i32 [[X:%.*]]) {
 ; CHECK-NEXT:    [[R:%.*]] = srem i32 [[X]], 5
-; CHECK-NEXT:    [[C:%.*]] = icmp ult i32 [[R]], -4
+; CHECK-NEXT:    [[C:%.*]] = icmp sgt i32 [[R]], -1
 ; CHECK-NEXT:    ret i1 [[C]]
 ;
   %r = srem i32 %x, 5
@@ -220,7 +219,7 @@ define i1 @icmp_ult_srem5_5(i32 %x) {
 ; CHECK-LABEL: define i1 @icmp_ult_srem5_5(
 ; CHECK-SAME: i32 [[X:%.*]]) {
 ; CHECK-NEXT:    [[R:%.*]] = srem i32 [[X]], 5
-; CHECK-NEXT:    [[C:%.*]] = icmp ult i32 [[R]], 5
+; CHECK-NEXT:    [[C:%.*]] = icmp sgt i32 [[R]], -1
 ; CHECK-NEXT:    ret i1 [[C]]
 ;
   %r = srem i32 %x, 5
@@ -232,7 +231,7 @@ define i1 @icmp_ult_srem5_smax(i32 %x) {
 ; CHECK-LABEL: define i1 @icmp_ult_srem5_smax(
 ; CHECK-SAME: i32 [[X:%.*]]) {
 ; CHECK-NEXT:    [[R:%.*]] = srem i32 [[X]], 5
-; CHECK-NEXT:    [[C:%.*]] = icmp ult i32 [[R]], 2147483647
+; CHECK-NEXT:    [[C:%.*]] = icmp sgt i32 [[R]], -1
 ; CHECK-NEXT:    ret i1 [[C]]
 ;
   %r = srem i32 %x, 5
@@ -244,7 +243,7 @@ define i1 @icmp_ugt_sremsmax_smin(i32 %x) {
 ; CHECK-LABEL: define i1 @icmp_ugt_sremsmax_smin(
 ; CHECK-SAME: i32 [[X:%.*]]) {
 ; CHECK-NEXT:    [[R:%.*]] = srem i32 [[X]], 2147483647
-; CHECK-NEXT:    [[C:%.*]] = icmp ugt i32 [[R]], -2147483648
+; CHECK-NEXT:    [[C:%.*]] = icmp slt i32 [[R]], 0
 ; CHECK-NEXT:    ret i1 [[C]]
 ;
   %r = srem i32 %x, 2147483647
@@ -256,7 +255,7 @@ define i1 @icmp_ugt_sremsmax_sminp1(i32 %x) {
 ; CHECK-LABEL: define i1 @icmp_ugt_sremsmax_sminp1(
 ; CHECK-SAME: i32 [[X:%.*]]) {
 ; CHECK-NEXT:    [[R:%.*]] = srem i32 [[X]], 2147483647
-; CHECK-NEXT:    [[C:%.*]] = icmp ugt i32 [[R]], -2147483647
+; CHECK-NEXT:    [[C:%.*]] = icmp slt i32 [[R]], 0
 ; CHECK-NEXT:    ret i1 [[C]]
 ;
   %r = srem i32 %x, 2147483647
@@ -292,7 +291,7 @@ define i1 @icmp_ugt_sremsmax_smaxm1(i32 %x) {
 ; CHECK-LABEL: define i1 @icmp_ugt_sremsmax_smaxm1(
 ; CHECK-SAME: i32 [[X:%.*]]) {
 ; CHECK-NEXT:    [[R:%.*]] = srem i32 [[X]], 2147483647
-; CHECK-NEXT:    [[C:%.*]] = icmp ugt i32 [[R]], 2147483646
+; CHECK-NEXT:    [[C:%.*]] = icmp slt i32 [[R]], 0
 ; CHECK-NEXT:    ret i1 [[C]]
 ;
   %r = srem i32 %x, 2147483647
@@ -328,7 +327,7 @@ define i1 @icmp_ult_sremsmax_sminp1(i32 %x) {
 ; CHECK-LABEL: define i1 @icmp_ult_sremsmax_sminp1(
 ; CHECK-SAME: i32 [[X:%.*]]) {
 ; CHECK-NEXT:    [[R:%.*]] = srem i32 [[X]], 2147483647
-; CHECK-NEXT:    [[C:%.*]] = icmp ult i32 [[R]], -2147483647
+; CHECK-NEXT:    [[C:%.*]] = icmp sgt i32 [[R]], -1
 ; CHECK-NEXT:    ret i1 [[C]]
 ;
   %r = srem i32 %x, 2147483647
@@ -340,7 +339,7 @@ define i1 @icmp_ult_sremsmax_sminp2(i32 %x) {
 ; CHECK-LABEL: define i1 @icmp_ult_sremsmax_sminp2(
 ; CHECK-SAME: i32 [[X:%.*]]) {
 ; CHECK-NEXT:    [[R:%.*]] = srem i32 [[X]], 2147483647
-; CHECK-NEXT:    [[C:%.*]] = icmp ult i32 [[R]], -2147483646
+; CHECK-NEXT:    [[C:%.*]] = icmp sgt i32 [[R]], -1
 ; CHECK-NEXT:    ret i1 [[C]]
 ;
   %r = srem i32 %x, 2147483647
@@ -376,7 +375,7 @@ define i1 @icmp_ult_sremsmax_smax(i32 %x) {
 ; CHECK-LABEL: define i1 @icmp_ult_sremsmax_smax(
 ; CHECK-SAME: i32 [[X:%.*]]) {
 ; CHECK-NEXT:    [[R:%.*]] = srem i32 [[X]], 2147483647
-; CHECK-NEXT:    [[C:%.*]] = icmp ult i32 [[R]], 2147483647
+; CHECK-NEXT:    [[C:%.*]] = icmp sgt i32 [[R]], -1
 ; CHECK-NEXT:    ret i1 [[C]]
 ;
   %r = srem i32 %x, 2147483647

>From 89fdfb60fab85aa287c38737f83a7203bee1ab0f Mon Sep 17 00:00:00 2001
From: Jacob Young <jacobly0 at users.noreply.github.com>
Date: Mon, 13 Jan 2025 15:05:35 -0500
Subject: [PATCH 3/4] [InstCombine][NFC] precommit tests for sign bit check
 canonicalization

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

diff --git a/llvm/test/Transforms/InstCombine/icmp-srem.ll b/llvm/test/Transforms/InstCombine/icmp-srem.ll
index dbf10d80bc2428..705ef2b5d937d6 100644
--- a/llvm/test/Transforms/InstCombine/icmp-srem.ll
+++ b/llvm/test/Transforms/InstCombine/icmp-srem.ll
@@ -382,3 +382,99 @@ define i1 @icmp_ult_sremsmax_smax(i32 %x) {
   %c = icmp ult i32 %r, 2147483647
   ret i1 %c
 }
+
+define i1 @icmp_slt_srem_pos_range(i32 %x, i32 range(i32 999, -2147483648) %y) {
+; CHECK-LABEL: define i1 @icmp_slt_srem_pos_range(
+; CHECK-SAME: i32 [[X:%.*]], i32 range(i32 999, -2147483648) [[Y:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = srem i32 [[X]], 1000
+; CHECK-NEXT:    [[C:%.*]] = icmp slt i32 [[R]], [[Y]]
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %r = srem i32 %x, 1000
+  %c = icmp slt i32 %r, %y
+  ret i1 %c
+}
+
+define i1 @icmp_sle_srem_pos_range(i32 %x, i32 range(i32 999, -2147483648) %y) {
+; CHECK-LABEL: define i1 @icmp_sle_srem_pos_range(
+; CHECK-SAME: i32 [[X:%.*]], i32 range(i32 999, -2147483648) [[Y:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = srem i32 [[X]], 1000
+; CHECK-NEXT:    [[C:%.*]] = icmp sle i32 [[R]], [[Y]]
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %r = srem i32 %x, 1000
+  %c = icmp sle i32 %r, %y
+  ret i1 %c
+}
+
+define i1 @icmp_sgt_srem_pos_range(i32 %x, i32 range(i32 999, -2147483648) %y) {
+; CHECK-LABEL: define i1 @icmp_sgt_srem_pos_range(
+; CHECK-SAME: i32 [[X:%.*]], i32 range(i32 999, -2147483648) [[Y:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = srem i32 [[X]], 1000
+; CHECK-NEXT:    [[C:%.*]] = icmp sgt i32 [[R]], [[Y]]
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %r = srem i32 %x, 1000
+  %c = icmp sgt i32 %r, %y
+  ret i1 %c
+}
+
+define i1 @icmp_sge_srem_pos_range(i32 %x, i32 range(i32 999, -2147483648) %y) {
+; CHECK-LABEL: define i1 @icmp_sge_srem_pos_range(
+; CHECK-SAME: i32 [[X:%.*]], i32 range(i32 999, -2147483648) [[Y:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = srem i32 [[X]], 1000
+; CHECK-NEXT:    [[C:%.*]] = icmp sge i32 [[R]], [[Y]]
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %r = srem i32 %x, 1000
+  %c = icmp sge i32 %r, %y
+  ret i1 %c
+}
+
+define i1 @icmp_slt_srem_neg_range(i32 %x, i32 range(i32 -2147483648, -999) %y) {
+; CHECK-LABEL: define i1 @icmp_slt_srem_neg_range(
+; CHECK-SAME: i32 [[X:%.*]], i32 range(i32 -2147483648, -999) [[Y:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = srem i32 [[X]], 1000
+; CHECK-NEXT:    [[C:%.*]] = icmp slt i32 [[R]], [[Y]]
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %r = srem i32 %x, 1000
+  %c = icmp slt i32 %r, %y
+  ret i1 %c
+}
+
+define i1 @icmp_sle_srem_neg_range(i32 %x, i32 range(i32 -2147483648, -999) %y) {
+; CHECK-LABEL: define i1 @icmp_sle_srem_neg_range(
+; CHECK-SAME: i32 [[X:%.*]], i32 range(i32 -2147483648, -999) [[Y:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = srem i32 [[X]], 1000
+; CHECK-NEXT:    [[C:%.*]] = icmp sle i32 [[R]], [[Y]]
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %r = srem i32 %x, 1000
+  %c = icmp sle i32 %r, %y
+  ret i1 %c
+}
+
+define i1 @icmp_sgt_srem_neg_range(i32 %x, i32 range(i32 -2147483648, -999) %y) {
+; CHECK-LABEL: define i1 @icmp_sgt_srem_neg_range(
+; CHECK-SAME: i32 [[X:%.*]], i32 range(i32 -2147483648, -999) [[Y:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = srem i32 [[X]], 1000
+; CHECK-NEXT:    [[C:%.*]] = icmp sgt i32 [[R]], [[Y]]
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %r = srem i32 %x, 1000
+  %c = icmp sgt i32 %r, %y
+  ret i1 %c
+}
+
+define i1 @icmp_sge_srem_neg_range(i32 %x, i32 range(i32 -2147483648, -999) %y) {
+; CHECK-LABEL: define i1 @icmp_sge_srem_neg_range(
+; CHECK-SAME: i32 [[X:%.*]], i32 range(i32 -2147483648, -999) [[Y:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = srem i32 [[X]], 1000
+; CHECK-NEXT:    [[C:%.*]] = icmp sge i32 [[R]], [[Y]]
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %r = srem i32 %x, 1000
+  %c = icmp sge i32 %r, %y
+  ret i1 %c
+}

>From 32ab6eef8876747067f4f76c57711b79f5699a02 Mon Sep 17 00:00:00 2001
From: Jacob Young <jacobly0 at users.noreply.github.com>
Date: Wed, 15 Jan 2025 13:04:42 -0500
Subject: [PATCH 4/4] [InstCombine] canonicalize sign bit checks

This is a generalization of #122520 to other instructions with
constrained result ranges.
---
 llvm/include/llvm/IR/ConstantRange.h          |   6 ++
 llvm/lib/Analysis/InstructionSimplify.cpp     |  10 +-
 llvm/lib/IR/ConstantRange.cpp                 |  10 ++
 .../InstCombine/InstCombineCompares.cpp       | 101 ++++++++++++------
 .../InstCombine/InstCombineInternal.h         |   1 +
 llvm/test/Transforms/InstCombine/icmp-dom.ll  |   6 +-
 llvm/test/Transforms/InstCombine/icmp-srem.ll |  24 ++---
 llvm/test/Transforms/InstCombine/smin-icmp.ll |  13 ++-
 llvm/unittests/IR/ConstantRangeTest.cpp       |  13 ++-
 9 files changed, 115 insertions(+), 69 deletions(-)

diff --git a/llvm/include/llvm/IR/ConstantRange.h b/llvm/include/llvm/IR/ConstantRange.h
index d086c25390fd22..92e9341352c177 100644
--- a/llvm/include/llvm/IR/ConstantRange.h
+++ b/llvm/include/llvm/IR/ConstantRange.h
@@ -128,6 +128,12 @@ class [[nodiscard]] ConstantRange {
   /// NOTE: false does not mean that inverse predicate holds!
   bool icmp(CmpInst::Predicate Pred, const ConstantRange &Other) const;
 
+  /// Does the predicate \p Pred or its inverse hold between ranges this and \p
+  /// Other? Returns `true` if the predicate always holds, `false` if the
+  /// inverse always holds, or `std::nullopt` otherwise.
+  std::optional<bool> icmpOrInverse(CmpInst::Predicate Pred,
+                                    const ConstantRange &Other) const;
+
   /// Return true iff CR1 ult CR2 is equivalent to CR1 slt CR2.
   /// Does not depend on strictness/direction of the predicate.
   static bool
diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index d69747e30f884d..dae4f37908cf2c 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -3783,13 +3783,9 @@ static Value *simplifyICmpInst(CmpPredicate Pred, Value *LHS, Value *RHS,
   // If both operands have range metadata, use the metadata
   // to simplify the comparison.
   if (std::optional<ConstantRange> RhsCr = getRange(RHS, Q.IIQ))
-    if (std::optional<ConstantRange> LhsCr = getRange(LHS, Q.IIQ)) {
-      if (LhsCr->icmp(Pred, *RhsCr))
-        return ConstantInt::getTrue(ITy);
-
-      if (LhsCr->icmp(CmpInst::getInversePredicate(Pred), *RhsCr))
-        return ConstantInt::getFalse(ITy);
-    }
+    if (std::optional<ConstantRange> LhsCr = getRange(LHS, Q.IIQ))
+      if (auto Res = LhsCr->icmpOrInverse(Pred, *RhsCr))
+        return ConstantInt::getBool(ITy, *Res);
 
   // Compare of cast, for example (zext X) != 0 -> X != 0
   if (isa<CastInst>(LHS) && (isa<Constant>(RHS) || isa<CastInst>(RHS))) {
diff --git a/llvm/lib/IR/ConstantRange.cpp b/llvm/lib/IR/ConstantRange.cpp
index 35664353989929..c5380ab2ffeda4 100644
--- a/llvm/lib/IR/ConstantRange.cpp
+++ b/llvm/lib/IR/ConstantRange.cpp
@@ -274,6 +274,16 @@ bool ConstantRange::icmp(CmpInst::Predicate Pred,
   }
 }
 
+std::optional<bool>
+ConstantRange::icmpOrInverse(CmpInst::Predicate Pred,
+                             const ConstantRange &Other) const {
+  if (icmp(Pred, Other))
+    return true;
+  if (icmp(CmpInst::getInversePredicate(Pred), Other))
+    return false;
+  return std::nullopt;
+}
+
 /// Exact mul nuw region for single element RHS.
 static ConstantRange makeExactMulNUWRegion(const APInt &V) {
   unsigned BitWidth = V.getBitWidth();
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index 5a4791870ac77b..eb239810d625d2 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -2674,41 +2674,10 @@ Instruction *InstCombinerImpl::foldICmpShrConstant(ICmpInst &Cmp,
 Instruction *InstCombinerImpl::foldICmpSRemConstant(ICmpInst &Cmp,
                                                     BinaryOperator *SRem,
                                                     const APInt &C) {
-  const ICmpInst::Predicate Pred = Cmp.getPredicate();
-  if (Pred == ICmpInst::ICMP_UGT || Pred == ICmpInst::ICMP_ULT) {
-    // Canonicalize unsigned predicates to signed:
-    // (X s% DivisorC) u> C -> (X s% DivisorC) s< 0
-    //   iff (C s< 0 ? ~C : C) u>= abs(DivisorC)-1
-    // (X s% DivisorC) u< C+1 -> (X s% DivisorC) s> -1
-    //   iff (C+1 s< 0 ? ~C : C) u>= abs(DivisorC)-1
-
-    const APInt *DivisorC;
-    if (!match(SRem->getOperand(1), m_APInt(DivisorC)))
-      return nullptr;
-
-    APInt NormalizedC = C;
-    if (Pred == ICmpInst::ICMP_ULT) {
-      assert(!NormalizedC.isZero() &&
-             "ult X, 0 should have been simplified already.");
-      --NormalizedC;
-    }
-    if (C.isNegative())
-      NormalizedC.flipAllBits();
-    assert(!DivisorC->isZero() &&
-           "srem X, 0 should have been simplified already.");
-    if (!NormalizedC.uge(DivisorC->abs() - 1))
-      return nullptr;
-
-    Type *Ty = SRem->getType();
-    if (Pred == ICmpInst::ICMP_UGT)
-      return new ICmpInst(ICmpInst::ICMP_SLT, SRem,
-                          ConstantInt::getNullValue(Ty));
-    return new ICmpInst(ICmpInst::ICMP_SGT, SRem,
-                        ConstantInt::getAllOnesValue(Ty));
-  }
   // Match an 'is positive' or 'is negative' comparison of remainder by a
   // constant power-of-2 value:
   // (X % pow2C) sgt/slt 0
+  const ICmpInst::Predicate Pred = Cmp.getPredicate();
   if (Pred != ICmpInst::ICMP_SGT && Pred != ICmpInst::ICMP_SLT &&
       Pred != ICmpInst::ICMP_EQ && Pred != ICmpInst::ICMP_NE)
     return nullptr;
@@ -3164,7 +3133,10 @@ Instruction *InstCombinerImpl::foldICmpAddConstant(ICmpInst &Cmp,
 
   if (ICmpInst::isUnsigned(Pred) && Add->hasNoSignedWrap() &&
       C.isNonNegative() && (C - *C2).isNonNegative() &&
-      computeConstantRange(X, /*ForSigned=*/true).add(*C2).isAllNonNegative())
+      computeConstantRange(X, /*ForSigned=*/true, /*UseInstrInfo=*/true, &AC,
+                           Add, &DT)
+          .add(*C2)
+          .isAllNonNegative())
     return new ICmpInst(ICmpInst::getSignedPredicate(Pred), X,
                         ConstantInt::get(Ty, C - *C2));
 
@@ -7056,6 +7028,66 @@ static Instruction *canonicalizeICmpBool(ICmpInst &I,
   }
 }
 
+// (icmp X, Y) --> (icmp slt/sgt X, 0/-1) iff Y is outside the signed range of X
+static ICmpInst *canonicalizeSignBitCheck(ICmpInst::Predicate Pred, Value *X,
+                                          const ConstantRange &XRange,
+                                          const ConstantRange &YRange) {
+  if (XRange.isSignWrappedSet())
+    return nullptr;
+  unsigned BitWidth = XRange.getBitWidth();
+  APInt SMin = APInt::getSignedMinValue(BitWidth);
+  APInt Zero = APInt::getZero(BitWidth);
+  auto NegResult =
+      XRange.intersectWith(ConstantRange(SMin, Zero), ConstantRange::Signed)
+          .icmpOrInverse(Pred, YRange);
+  if (!NegResult)
+    return nullptr;
+  auto PosResult =
+      XRange.intersectWith(ConstantRange(Zero, SMin), ConstantRange::Signed)
+          .icmpOrInverse(Pred, YRange);
+  if (!PosResult)
+    return nullptr;
+  assert(NegResult != PosResult &&
+         "Known result should been simplified already.");
+  Type *Ty = X->getType();
+  if (*NegResult)
+    return new ICmpInst(ICmpInst::ICMP_SLT, X, ConstantInt::getNullValue(Ty));
+  return new ICmpInst(ICmpInst::ICMP_SGT, X, ConstantInt::getAllOnesValue(Ty));
+}
+
+// Try to fold an icmp using the constant ranges of its operands.
+Instruction *InstCombinerImpl::foldICmpUsingConstantRanges(ICmpInst &Cmp) {
+  Value *X = Cmp.getOperand(0);
+  if (!X->getType()->isIntOrIntVectorTy())
+    return nullptr;
+  Value *Y = Cmp.getOperand(1);
+  ICmpInst::Predicate Pred = Cmp.getPredicate();
+  ConstantRange XRange = computeConstantRange(
+      X, ICmpInst::isSigned(Pred), /*UseInstrInfo=*/true, &AC, &Cmp, &DT);
+  if (XRange.isFullSet())
+    return nullptr; // early out if we don't have any information
+  ConstantRange YRange = computeConstantRange(
+      Y, ICmpInst::isSigned(Pred), /*UseInstrInfo=*/true, &AC, &Cmp, &DT);
+  if (YRange.isFullSet())
+    return nullptr; // early out if we don't have any information
+  if (auto Res = XRange.icmpOrInverse(Pred, YRange))
+    return replaceInstUsesWith(Cmp, ConstantInt::getBool(Cmp.getType(), *Res));
+  if (ICmpInst::isUnsigned(Pred)) {
+    // Check if this icmp is actually a sign bit check.
+    const APInt *C;
+    bool IgnoreTrueIfSigned;
+    if (!match(Y, m_APInt(C)) ||
+        !isSignBitCheck(Pred, *C, IgnoreTrueIfSigned)) {
+      if (ICmpInst *Res = canonicalizeSignBitCheck(Pred, X, XRange, YRange))
+        return Res;
+      if (ICmpInst *Res = canonicalizeSignBitCheck(
+              ICmpInst::getSwappedPredicate(Pred), Y, YRange, XRange))
+        return Res;
+    }
+  }
+  return nullptr;
+}
+
 // Transform pattern like:
 //   (1 << Y) u<= X  or  ~(-1 << Y) u<  X  or  ((1 << Y)+(-1)) u<  X
 //   (1 << Y) u>  X  or  ~(-1 << Y) u>= X  or  ((1 << Y)+(-1)) u>= X
@@ -7428,6 +7460,9 @@ Instruction *InstCombinerImpl::visitICmpInst(ICmpInst &I) {
   if (Instruction *Res = canonicalizeICmpPredicate(I))
     return Res;
 
+  if (Instruction *Res = foldICmpUsingConstantRanges(I))
+    return Res;
+
   if (Instruction *Res = foldICmpWithConstant(I))
     return Res;
 
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
index 83e1da98deeda0..577a053e59e934 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
+++ b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
@@ -668,6 +668,7 @@ class LLVM_LIBRARY_VISIBILITY InstCombinerImpl final
   Instruction *foldICmpWithCastOp(ICmpInst &ICmp);
   Instruction *foldICmpWithZextOrSext(ICmpInst &ICmp);
 
+  Instruction *foldICmpUsingConstantRanges(ICmpInst &Cmp);
   Instruction *foldICmpUsingKnownBits(ICmpInst &Cmp);
   Instruction *foldICmpWithDominatingICmp(ICmpInst &Cmp);
   Instruction *foldICmpWithConstant(ICmpInst &Cmp);
diff --git a/llvm/test/Transforms/InstCombine/icmp-dom.ll b/llvm/test/Transforms/InstCombine/icmp-dom.ll
index 3cf3a7af77041c..66e9e514a9022a 100644
--- a/llvm/test/Transforms/InstCombine/icmp-dom.ll
+++ b/llvm/test/Transforms/InstCombine/icmp-dom.ll
@@ -381,11 +381,11 @@ falselabel:
 
 define i8 @PR48900_alt(i8 %i, ptr %p) {
 ; CHECK-LABEL: @PR48900_alt(
-; CHECK-NEXT:    [[SMAX:%.*]] = call i8 @llvm.smax.i8(i8 [[I:%.*]], i8 -127)
-; CHECK-NEXT:    [[I4:%.*]] = icmp ugt i8 [[SMAX]], -128
+; CHECK-NEXT:    [[I4:%.*]] = icmp slt i8 [[I:%.*]], 0
 ; CHECK-NEXT:    br i1 [[I4]], label [[TRUELABEL:%.*]], label [[FALSELABEL:%.*]]
 ; CHECK:       truelabel:
-; CHECK-NEXT:    [[UMIN:%.*]] = call i8 @llvm.smin.i8(i8 [[SMAX]], i8 -126)
+; CHECK-NEXT:    [[TMP1:%.*]] = icmp slt i8 [[I]], -126
+; CHECK-NEXT:    [[UMIN:%.*]] = select i1 [[TMP1]], i8 -127, i8 -126
 ; CHECK-NEXT:    ret i8 [[UMIN]]
 ; CHECK:       falselabel:
 ; CHECK-NEXT:    ret i8 0
diff --git a/llvm/test/Transforms/InstCombine/icmp-srem.ll b/llvm/test/Transforms/InstCombine/icmp-srem.ll
index 705ef2b5d937d6..9ab92f15ae7d2d 100644
--- a/llvm/test/Transforms/InstCombine/icmp-srem.ll
+++ b/llvm/test/Transforms/InstCombine/icmp-srem.ll
@@ -398,9 +398,7 @@ define i1 @icmp_slt_srem_pos_range(i32 %x, i32 range(i32 999, -2147483648) %y) {
 define i1 @icmp_sle_srem_pos_range(i32 %x, i32 range(i32 999, -2147483648) %y) {
 ; CHECK-LABEL: define i1 @icmp_sle_srem_pos_range(
 ; CHECK-SAME: i32 [[X:%.*]], i32 range(i32 999, -2147483648) [[Y:%.*]]) {
-; CHECK-NEXT:    [[R:%.*]] = srem i32 [[X]], 1000
-; CHECK-NEXT:    [[C:%.*]] = icmp sle i32 [[R]], [[Y]]
-; CHECK-NEXT:    ret i1 [[C]]
+; CHECK-NEXT:    ret i1 true
 ;
   %r = srem i32 %x, 1000
   %c = icmp sle i32 %r, %y
@@ -410,9 +408,7 @@ define i1 @icmp_sle_srem_pos_range(i32 %x, i32 range(i32 999, -2147483648) %y) {
 define i1 @icmp_sgt_srem_pos_range(i32 %x, i32 range(i32 999, -2147483648) %y) {
 ; CHECK-LABEL: define i1 @icmp_sgt_srem_pos_range(
 ; CHECK-SAME: i32 [[X:%.*]], i32 range(i32 999, -2147483648) [[Y:%.*]]) {
-; CHECK-NEXT:    [[R:%.*]] = srem i32 [[X]], 1000
-; CHECK-NEXT:    [[C:%.*]] = icmp sgt i32 [[R]], [[Y]]
-; CHECK-NEXT:    ret i1 [[C]]
+; CHECK-NEXT:    ret i1 false
 ;
   %r = srem i32 %x, 1000
   %c = icmp sgt i32 %r, %y
@@ -434,9 +430,7 @@ define i1 @icmp_sge_srem_pos_range(i32 %x, i32 range(i32 999, -2147483648) %y) {
 define i1 @icmp_slt_srem_neg_range(i32 %x, i32 range(i32 -2147483648, -999) %y) {
 ; CHECK-LABEL: define i1 @icmp_slt_srem_neg_range(
 ; CHECK-SAME: i32 [[X:%.*]], i32 range(i32 -2147483648, -999) [[Y:%.*]]) {
-; CHECK-NEXT:    [[R:%.*]] = srem i32 [[X]], 1000
-; CHECK-NEXT:    [[C:%.*]] = icmp slt i32 [[R]], [[Y]]
-; CHECK-NEXT:    ret i1 [[C]]
+; CHECK-NEXT:    ret i1 false
 ;
   %r = srem i32 %x, 1000
   %c = icmp slt i32 %r, %y
@@ -446,9 +440,7 @@ define i1 @icmp_slt_srem_neg_range(i32 %x, i32 range(i32 -2147483648, -999) %y)
 define i1 @icmp_sle_srem_neg_range(i32 %x, i32 range(i32 -2147483648, -999) %y) {
 ; CHECK-LABEL: define i1 @icmp_sle_srem_neg_range(
 ; CHECK-SAME: i32 [[X:%.*]], i32 range(i32 -2147483648, -999) [[Y:%.*]]) {
-; CHECK-NEXT:    [[R:%.*]] = srem i32 [[X]], 1000
-; CHECK-NEXT:    [[C:%.*]] = icmp sle i32 [[R]], [[Y]]
-; CHECK-NEXT:    ret i1 [[C]]
+; CHECK-NEXT:    ret i1 false
 ;
   %r = srem i32 %x, 1000
   %c = icmp sle i32 %r, %y
@@ -458,9 +450,7 @@ define i1 @icmp_sle_srem_neg_range(i32 %x, i32 range(i32 -2147483648, -999) %y)
 define i1 @icmp_sgt_srem_neg_range(i32 %x, i32 range(i32 -2147483648, -999) %y) {
 ; CHECK-LABEL: define i1 @icmp_sgt_srem_neg_range(
 ; CHECK-SAME: i32 [[X:%.*]], i32 range(i32 -2147483648, -999) [[Y:%.*]]) {
-; CHECK-NEXT:    [[R:%.*]] = srem i32 [[X]], 1000
-; CHECK-NEXT:    [[C:%.*]] = icmp sgt i32 [[R]], [[Y]]
-; CHECK-NEXT:    ret i1 [[C]]
+; CHECK-NEXT:    ret i1 true
 ;
   %r = srem i32 %x, 1000
   %c = icmp sgt i32 %r, %y
@@ -470,9 +460,7 @@ define i1 @icmp_sgt_srem_neg_range(i32 %x, i32 range(i32 -2147483648, -999) %y)
 define i1 @icmp_sge_srem_neg_range(i32 %x, i32 range(i32 -2147483648, -999) %y) {
 ; CHECK-LABEL: define i1 @icmp_sge_srem_neg_range(
 ; CHECK-SAME: i32 [[X:%.*]], i32 range(i32 -2147483648, -999) [[Y:%.*]]) {
-; CHECK-NEXT:    [[R:%.*]] = srem i32 [[X]], 1000
-; CHECK-NEXT:    [[C:%.*]] = icmp sge i32 [[R]], [[Y]]
-; CHECK-NEXT:    ret i1 [[C]]
+; CHECK-NEXT:    ret i1 true
 ;
   %r = srem i32 %x, 1000
   %c = icmp sge i32 %r, %y
diff --git a/llvm/test/Transforms/InstCombine/smin-icmp.ll b/llvm/test/Transforms/InstCombine/smin-icmp.ll
index f05c1698f1a980..dda59cefbc6f10 100644
--- a/llvm/test/Transforms/InstCombine/smin-icmp.ll
+++ b/llvm/test/Transforms/InstCombine/smin-icmp.ll
@@ -965,9 +965,9 @@ define void @eq_smin_v2i32_constant(<2 x i32> %y) {
 ; CHECK-NEXT:    call void @use_v2i1(<2 x i1> [[CMP4]])
 ; CHECK-NEXT:    [[CMP5:%.*]] = icmp ult <2 x i32> [[COND]], splat (i32 10)
 ; CHECK-NEXT:    call void @use_v2i1(<2 x i1> [[CMP5]])
-; CHECK-NEXT:    [[CMP6:%.*]] = icmp ult <2 x i32> [[COND]], splat (i32 11)
+; CHECK-NEXT:    [[CMP6:%.*]] = icmp sgt <2 x i32> [[Y]], splat (i32 -1)
 ; CHECK-NEXT:    call void @use_v2i1(<2 x i1> [[CMP6]])
-; CHECK-NEXT:    [[CMP7:%.*]] = icmp ugt <2 x i32> [[COND]], splat (i32 10)
+; CHECK-NEXT:    [[CMP7:%.*]] = icmp slt <2 x i32> [[Y]], zeroinitializer
 ; CHECK-NEXT:    call void @use_v2i1(<2 x i1> [[CMP7]])
 ; CHECK-NEXT:    [[CMP8:%.*]] = icmp ugt <2 x i32> [[COND]], splat (i32 9)
 ; CHECK-NEXT:    call void @use_v2i1(<2 x i1> [[CMP8]])
@@ -1004,18 +1004,17 @@ define void @eq_smin_v2i32_constant(<2 x i32> %y) {
 ; icmp pred smin(C1, Y), C2 where C1 < C2
 define void @slt_smin_v2i32_constant(<2 x i32> %y) {
 ; CHECK-LABEL: @slt_smin_v2i32_constant(
-; CHECK-NEXT:    [[COND:%.*]] = call <2 x i32> @llvm.smin.v2i32(<2 x i32> [[Y:%.*]], <2 x i32> splat (i32 5))
 ; CHECK-NEXT:    call void @use_v2i1(<2 x i1> splat (i1 true))
 ; CHECK-NEXT:    call void @use_v2i1(<2 x i1> splat (i1 true))
 ; CHECK-NEXT:    call void @use_v2i1(<2 x i1> zeroinitializer)
 ; CHECK-NEXT:    call void @use_v2i1(<2 x i1> zeroinitializer)
-; CHECK-NEXT:    [[CMP5:%.*]] = icmp ult <2 x i32> [[COND]], splat (i32 10)
+; CHECK-NEXT:    [[CMP5:%.*]] = icmp sgt <2 x i32> [[Y:%.*]], splat (i32 -1)
 ; CHECK-NEXT:    call void @use_v2i1(<2 x i1> [[CMP5]])
-; CHECK-NEXT:    [[CMP6:%.*]] = icmp ult <2 x i32> [[COND]], splat (i32 11)
+; CHECK-NEXT:    [[CMP6:%.*]] = icmp sgt <2 x i32> [[Y]], splat (i32 -1)
 ; CHECK-NEXT:    call void @use_v2i1(<2 x i1> [[CMP6]])
-; CHECK-NEXT:    [[CMP7:%.*]] = icmp ugt <2 x i32> [[COND]], splat (i32 10)
+; CHECK-NEXT:    [[CMP7:%.*]] = icmp slt <2 x i32> [[Y]], zeroinitializer
 ; CHECK-NEXT:    call void @use_v2i1(<2 x i1> [[CMP7]])
-; CHECK-NEXT:    [[CMP8:%.*]] = icmp ugt <2 x i32> [[COND]], splat (i32 9)
+; CHECK-NEXT:    [[CMP8:%.*]] = icmp slt <2 x i32> [[Y]], zeroinitializer
 ; CHECK-NEXT:    call void @use_v2i1(<2 x i1> [[CMP8]])
 ; CHECK-NEXT:    call void @use_v2i1(<2 x i1> zeroinitializer)
 ; CHECK-NEXT:    call void @use_v2i1(<2 x i1> splat (i1 true))
diff --git a/llvm/unittests/IR/ConstantRangeTest.cpp b/llvm/unittests/IR/ConstantRangeTest.cpp
index c390ffea1c3523..b4f4ee1a04902c 100644
--- a/llvm/unittests/IR/ConstantRangeTest.cpp
+++ b/llvm/unittests/IR/ConstantRangeTest.cpp
@@ -1694,12 +1694,23 @@ void ICmpTestImpl(CmpInst::Predicate Pred) {
   EnumerateTwoInterestingConstantRanges(
       [&](const ConstantRange &CR1, const ConstantRange &CR2) {
         bool Exhaustive = true;
+        bool ExhaustiveInverse = true;
         ForeachNumInConstantRange(CR1, [&](const APInt &N1) {
           ForeachNumInConstantRange(CR2, [&](const APInt &N2) {
-            Exhaustive &= ICmpInst::compare(N1, N2, Pred);
+            bool Res = ICmpInst::compare(N1, N2, Pred);
+            Exhaustive &= Res;
+            ExhaustiveInverse &= !Res;
           });
         });
+
+        std::optional<bool> ExhaustiveOrInverse;
+        if (Exhaustive) // Expect true if Exhaustive && ExhaustiveInverse.
+          ExhaustiveOrInverse = true;
+        else if (ExhaustiveInverse)
+          ExhaustiveOrInverse = false;
+
         EXPECT_EQ(CR1.icmp(Pred, CR2), Exhaustive);
+        EXPECT_EQ(CR1.icmpOrInverse(Pred, CR2), ExhaustiveOrInverse);
       });
 }
 



More information about the llvm-commits mailing list