[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