[llvm] [InstSimplify] Infer icmp from with.overflow intrinsics (PR #75511)

Yingwei Zheng via llvm-commits llvm-commits at lists.llvm.org
Thu Dec 14 23:54:29 PST 2023


https://github.com/dtcxzyw updated https://github.com/llvm/llvm-project/pull/75511

>From 4ac40335d04034e59f6b2786373d38ca6ac5f024 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Fri, 15 Dec 2023 15:41:45 +0800
Subject: [PATCH 1/2] [InstCombine] Add pre-commit tests from PR75360. NFC.

---
 .../canonicalize-or-with-overflow-icmp.ll     | 307 ++++++++++++++++++
 1 file changed, 307 insertions(+)
 create mode 100644 llvm/test/Transforms/InstCombine/canonicalize-or-with-overflow-icmp.ll

diff --git a/llvm/test/Transforms/InstCombine/canonicalize-or-with-overflow-icmp.ll b/llvm/test/Transforms/InstCombine/canonicalize-or-with-overflow-icmp.ll
new file mode 100644
index 00000000000000..722d6b2290a29b
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/canonicalize-or-with-overflow-icmp.ll
@@ -0,0 +1,307 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+declare { i32, i1 } @llvm.sadd.with.overflow.i32(i32, i32)
+declare { i32, i1 } @llvm.ssub.with.overflow.i32(i32, i32)
+declare { i32, i1 } @llvm.smul.with.overflow.i32(i32, i32)
+declare { i32, i1 } @llvm.uadd.with.overflow.i32(i32, i32)
+
+declare void @use(i1)
+
+; Tests from PR75360
+define i1 @ckd_add_unsigned(i31 %num) {
+; CHECK-LABEL: define i1 @ckd_add_unsigned(
+; CHECK-SAME: i31 [[NUM:%.*]]) {
+; CHECK-NEXT:    [[A0:%.*]] = zext i31 [[NUM]] to i32
+; CHECK-NEXT:    [[A1:%.*]] = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 [[A0]], i32 1)
+; CHECK-NEXT:    [[A2:%.*]] = extractvalue { i32, i1 } [[A1]], 1
+; CHECK-NEXT:    [[A3:%.*]] = extractvalue { i32, i1 } [[A1]], 0
+; CHECK-NEXT:    [[A4:%.*]] = icmp slt i32 [[A3]], 0
+; CHECK-NEXT:    [[A5:%.*]] = or i1 [[A2]], [[A4]]
+; CHECK-NEXT:    ret i1 [[A5]]
+;
+  %a0 = zext i31 %num to i32
+  %a1 = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 %a0, i32 1)
+  %a2 = extractvalue { i32, i1 } %a1, 1
+  %a3 = extractvalue { i32, i1 } %a1, 0
+  %a4 = icmp slt i32 %a3, 0
+  %a5 = or i1 %a2, %a4
+  ret i1 %a5
+}
+
+define i1 @ckd_add_unsigned_commuted(i31 %num) {
+; CHECK-LABEL: define i1 @ckd_add_unsigned_commuted(
+; CHECK-SAME: i31 [[NUM:%.*]]) {
+; CHECK-NEXT:    [[A0:%.*]] = zext i31 [[NUM]] to i32
+; CHECK-NEXT:    [[A1:%.*]] = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 [[A0]], i32 1)
+; CHECK-NEXT:    [[A2:%.*]] = extractvalue { i32, i1 } [[A1]], 1
+; CHECK-NEXT:    [[A3:%.*]] = extractvalue { i32, i1 } [[A1]], 0
+; CHECK-NEXT:    [[A4:%.*]] = icmp slt i32 [[A3]], 0
+; CHECK-NEXT:    [[A5:%.*]] = or i1 [[A4]], [[A2]]
+; CHECK-NEXT:    ret i1 [[A5]]
+;
+  %a0 = zext i31 %num to i32
+  %a1 = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 %a0, i32 1)
+  %a2 = extractvalue { i32, i1 } %a1, 1
+  %a3 = extractvalue { i32, i1 } %a1, 0
+  %a4 = icmp slt i32 %a3, 0
+  %a5 = or i1 %a4, %a2
+  ret i1 %a5
+}
+
+define i1 @ckd_add_unsigned_imply_true(i31 %num) {
+; CHECK-LABEL: define i1 @ckd_add_unsigned_imply_true(
+; CHECK-SAME: i31 [[NUM:%.*]]) {
+; CHECK-NEXT:    [[A0:%.*]] = zext i31 [[NUM]] to i32
+; CHECK-NEXT:    [[A1:%.*]] = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 [[A0]], i32 1)
+; CHECK-NEXT:    [[A2:%.*]] = extractvalue { i32, i1 } [[A1]], 1
+; CHECK-NEXT:    [[A3:%.*]] = extractvalue { i32, i1 } [[A1]], 0
+; CHECK-NEXT:    [[A4:%.*]] = icmp sgt i32 [[A3]], -1
+; CHECK-NEXT:    [[A5:%.*]] = or i1 [[A2]], [[A4]]
+; CHECK-NEXT:    ret i1 [[A5]]
+;
+  %a0 = zext i31 %num to i32
+  %a1 = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 %a0, i32 1)
+  %a2 = extractvalue { i32, i1 } %a1, 1
+  %a3 = extractvalue { i32, i1 } %a1, 0
+  %a4 = icmp sgt i32 %a3, -1
+  %a5 = or i1 %a2, %a4
+  ret i1 %a5
+}
+
+define i1 @canonicalize_or_sadd_with_overflow_icmp(i32 %a0) {
+; CHECK-LABEL: define i1 @canonicalize_or_sadd_with_overflow_icmp(
+; CHECK-SAME: i32 [[A0:%.*]]) {
+; CHECK-NEXT:    [[A1:%.*]] = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 [[A0]], i32 1)
+; CHECK-NEXT:    [[A2:%.*]] = extractvalue { i32, i1 } [[A1]], 1
+; CHECK-NEXT:    [[A3:%.*]] = extractvalue { i32, i1 } [[A1]], 0
+; CHECK-NEXT:    [[A4:%.*]] = icmp slt i32 [[A3]], 0
+; CHECK-NEXT:    [[A5:%.*]] = or i1 [[A2]], [[A4]]
+; CHECK-NEXT:    ret i1 [[A5]]
+;
+  %a1 = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 %a0, i32 1)
+  %a2 = extractvalue { i32, i1 } %a1, 1
+  %a3 = extractvalue { i32, i1 } %a1, 0
+  %a4 = icmp slt i32 %a3, 0
+  %a5 = or i1 %a2, %a4
+  ret i1 %a5
+}
+
+define i1 @canonicalize_or_ssub_with_overflow_icmp(i32 %a0) {
+; CHECK-LABEL: define i1 @canonicalize_or_ssub_with_overflow_icmp(
+; CHECK-SAME: i32 [[A0:%.*]]) {
+; CHECK-NEXT:    [[A1:%.*]] = call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 [[A0]], i32 -1)
+; CHECK-NEXT:    [[A2:%.*]] = extractvalue { i32, i1 } [[A1]], 1
+; CHECK-NEXT:    [[A3:%.*]] = extractvalue { i32, i1 } [[A1]], 0
+; CHECK-NEXT:    [[A4:%.*]] = icmp slt i32 [[A3]], 0
+; CHECK-NEXT:    [[A5:%.*]] = or i1 [[A2]], [[A4]]
+; CHECK-NEXT:    ret i1 [[A5]]
+;
+  %a1 = tail call { i32, i1 } @llvm.ssub.with.overflow.i32(i32 %a0, i32 1)
+  %a2 = extractvalue { i32, i1 } %a1, 1
+  %a3 = extractvalue { i32, i1 } %a1, 0
+  %a4 = icmp slt i32 %a3, 0
+  %a5 = or i1 %a2, %a4
+  ret i1 %a5
+}
+
+define i1 @canonicalize_or_uadd_with_overflow_icmp(i32 %a0) {
+; CHECK-LABEL: define i1 @canonicalize_or_uadd_with_overflow_icmp(
+; CHECK-SAME: i32 [[A0:%.*]]) {
+; CHECK-NEXT:    [[A1:%.*]] = tail call { i32, i1 } @llvm.uadd.with.overflow.i32(i32 [[A0]], i32 1)
+; CHECK-NEXT:    [[A2:%.*]] = extractvalue { i32, i1 } [[A1]], 1
+; CHECK-NEXT:    [[A3:%.*]] = extractvalue { i32, i1 } [[A1]], 0
+; CHECK-NEXT:    [[A4:%.*]] = icmp ult i32 [[A3]], 10
+; CHECK-NEXT:    [[A5:%.*]] = or i1 [[A2]], [[A4]]
+; CHECK-NEXT:    ret i1 [[A5]]
+;
+  %a1 = tail call { i32, i1 } @llvm.uadd.with.overflow.i32(i32 %a0, i32 1)
+  %a2 = extractvalue { i32, i1 } %a1, 1
+  %a3 = extractvalue { i32, i1 } %a1, 0
+  %a4 = icmp ult i32 %a3, 10
+  %a5 = or i1 %a2, %a4
+  ret i1 %a5
+}
+
+define i1 @canonicalize_or_sadd_with_overflow_icmp_eq(i32 %a0) {
+; CHECK-LABEL: define i1 @canonicalize_or_sadd_with_overflow_icmp_eq(
+; CHECK-SAME: i32 [[A0:%.*]]) {
+; CHECK-NEXT:    [[A1:%.*]] = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 [[A0]], i32 1)
+; CHECK-NEXT:    [[A2:%.*]] = extractvalue { i32, i1 } [[A1]], 1
+; CHECK-NEXT:    [[A3:%.*]] = extractvalue { i32, i1 } [[A1]], 0
+; CHECK-NEXT:    [[A4:%.*]] = icmp eq i32 [[A3]], 10
+; CHECK-NEXT:    [[A5:%.*]] = or i1 [[A2]], [[A4]]
+; CHECK-NEXT:    ret i1 [[A5]]
+;
+  %a1 = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 %a0, i32 1)
+  %a2 = extractvalue { i32, i1 } %a1, 1
+  %a3 = extractvalue { i32, i1 } %a1, 0
+  %a4 = icmp eq i32 %a3, 10
+  %a5 = or i1 %a2, %a4
+  ret i1 %a5
+}
+
+define i1 @canonicalize_or_uadd_with_overflow_icmp_ne(i32 %a0) {
+; CHECK-LABEL: define i1 @canonicalize_or_uadd_with_overflow_icmp_ne(
+; CHECK-SAME: i32 [[A0:%.*]]) {
+; CHECK-NEXT:    [[A1:%.*]] = tail call { i32, i1 } @llvm.uadd.with.overflow.i32(i32 [[A0]], i32 1)
+; CHECK-NEXT:    [[A2:%.*]] = extractvalue { i32, i1 } [[A1]], 1
+; CHECK-NEXT:    [[A3:%.*]] = extractvalue { i32, i1 } [[A1]], 0
+; CHECK-NEXT:    [[A4:%.*]] = icmp ne i32 [[A3]], 10
+; CHECK-NEXT:    [[A5:%.*]] = or i1 [[A2]], [[A4]]
+; CHECK-NEXT:    ret i1 [[A5]]
+;
+  %a1 = tail call { i32, i1 } @llvm.uadd.with.overflow.i32(i32 %a0, i32 1)
+  %a2 = extractvalue { i32, i1 } %a1, 1
+  %a3 = extractvalue { i32, i1 } %a1, 0
+  %a4 = icmp ne i32 %a3, 10
+  %a5 = or i1 %a2, %a4
+  ret i1 %a5
+}
+
+; Negative tests
+define i1 @canonicalize_or_sadd_with_overflow_icmp_mismatched_pred(i32 %a0) {
+; CHECK-LABEL: define i1 @canonicalize_or_sadd_with_overflow_icmp_mismatched_pred(
+; CHECK-SAME: i32 [[A0:%.*]]) {
+; CHECK-NEXT:    [[A1:%.*]] = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 [[A0]], i32 1)
+; CHECK-NEXT:    [[A2:%.*]] = extractvalue { i32, i1 } [[A1]], 1
+; CHECK-NEXT:    [[A3:%.*]] = extractvalue { i32, i1 } [[A1]], 0
+; CHECK-NEXT:    [[A4:%.*]] = icmp ult i32 [[A3]], 2
+; CHECK-NEXT:    [[A5:%.*]] = or i1 [[A2]], [[A4]]
+; CHECK-NEXT:    ret i1 [[A5]]
+;
+  %a1 = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 %a0, i32 1)
+  %a2 = extractvalue { i32, i1 } %a1, 1
+  %a3 = extractvalue { i32, i1 } %a1, 0
+  %a4 = icmp ult i32 %a3, 2
+  %a5 = or i1 %a2, %a4
+  ret i1 %a5
+}
+
+define i1 @canonicalize_or_sadd_with_overflow_icmp_non_constant1(i32 %a0, i32 %c) {
+; CHECK-LABEL: define i1 @canonicalize_or_sadd_with_overflow_icmp_non_constant1(
+; CHECK-SAME: i32 [[A0:%.*]], i32 [[C:%.*]]) {
+; CHECK-NEXT:    [[A1:%.*]] = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 [[A0]], i32 [[C]])
+; CHECK-NEXT:    [[A2:%.*]] = extractvalue { i32, i1 } [[A1]], 1
+; CHECK-NEXT:    [[A3:%.*]] = extractvalue { i32, i1 } [[A1]], 0
+; CHECK-NEXT:    [[A4:%.*]] = icmp slt i32 [[A3]], 0
+; CHECK-NEXT:    [[A5:%.*]] = or i1 [[A2]], [[A4]]
+; CHECK-NEXT:    ret i1 [[A5]]
+;
+  %a1 = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 %a0, i32 %c)
+  %a2 = extractvalue { i32, i1 } %a1, 1
+  %a3 = extractvalue { i32, i1 } %a1, 0
+  %a4 = icmp slt i32 %a3, 0
+  %a5 = or i1 %a2, %a4
+  ret i1 %a5
+}
+
+define i1 @canonicalize_or_sadd_with_overflow_icmp_non_constant2(i32 %a0, i32 %c) {
+; CHECK-LABEL: define i1 @canonicalize_or_sadd_with_overflow_icmp_non_constant2(
+; CHECK-SAME: i32 [[A0:%.*]], i32 [[C:%.*]]) {
+; CHECK-NEXT:    [[A1:%.*]] = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 [[A0]], i32 1)
+; CHECK-NEXT:    [[A2:%.*]] = extractvalue { i32, i1 } [[A1]], 1
+; CHECK-NEXT:    [[A3:%.*]] = extractvalue { i32, i1 } [[A1]], 0
+; CHECK-NEXT:    [[A4:%.*]] = icmp slt i32 [[A3]], [[C]]
+; CHECK-NEXT:    [[A5:%.*]] = or i1 [[A2]], [[A4]]
+; CHECK-NEXT:    ret i1 [[A5]]
+;
+  %a1 = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 %a0, i32 1)
+  %a2 = extractvalue { i32, i1 } %a1, 1
+  %a3 = extractvalue { i32, i1 } %a1, 0
+  %a4 = icmp slt i32 %a3, %c
+  %a5 = or i1 %a2, %a4
+  ret i1 %a5
+}
+
+define i1 @canonicalize_or_sadd_with_overflow_icmp_multiuse(i32 %a0) {
+; CHECK-LABEL: define i1 @canonicalize_or_sadd_with_overflow_icmp_multiuse(
+; CHECK-SAME: i32 [[A0:%.*]]) {
+; CHECK-NEXT:    [[A1:%.*]] = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 [[A0]], i32 1)
+; CHECK-NEXT:    [[A2:%.*]] = extractvalue { i32, i1 } [[A1]], 1
+; CHECK-NEXT:    [[A3:%.*]] = extractvalue { i32, i1 } [[A1]], 0
+; CHECK-NEXT:    [[A4:%.*]] = icmp slt i32 [[A3]], 0
+; CHECK-NEXT:    call void @use(i1 [[A4]])
+; CHECK-NEXT:    [[A5:%.*]] = or i1 [[A2]], [[A4]]
+; CHECK-NEXT:    ret i1 [[A5]]
+;
+  %a1 = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 %a0, i32 1)
+  %a2 = extractvalue { i32, i1 } %a1, 1
+  %a3 = extractvalue { i32, i1 } %a1, 0
+  %a4 = icmp slt i32 %a3, 0
+  call void @use(i1 %a4)
+  %a5 = or i1 %a2, %a4
+  ret i1 %a5
+}
+
+define i1 @canonicalize_or_sadd_with_overflow_icmp_overflow(i32 %a0) {
+; CHECK-LABEL: define i1 @canonicalize_or_sadd_with_overflow_icmp_overflow(
+; CHECK-SAME: i32 [[A0:%.*]]) {
+; CHECK-NEXT:    [[A1:%.*]] = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 [[A0]], i32 -2147483647)
+; CHECK-NEXT:    [[A2:%.*]] = extractvalue { i32, i1 } [[A1]], 1
+; CHECK-NEXT:    [[A3:%.*]] = extractvalue { i32, i1 } [[A1]], 0
+; CHECK-NEXT:    [[A4:%.*]] = icmp slt i32 [[A3]], 2
+; CHECK-NEXT:    [[A5:%.*]] = or i1 [[A2]], [[A4]]
+; CHECK-NEXT:    ret i1 [[A5]]
+;
+  %a1 = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 %a0, i32 -2147483647)
+  %a2 = extractvalue { i32, i1 } %a1, 1
+  %a3 = extractvalue { i32, i1 } %a1, 0
+  %a4 = icmp slt i32 %a3, 2
+  %a5 = or i1 %a2, %a4
+  ret i1 %a5
+}
+
+define i1 @canonicalize_or_uadd_with_overflow_icmp_overflow(i32 %a0) {
+; CHECK-LABEL: define i1 @canonicalize_or_uadd_with_overflow_icmp_overflow(
+; CHECK-SAME: i32 [[A0:%.*]]) {
+; CHECK-NEXT:    [[A1:%.*]] = tail call { i32, i1 } @llvm.uadd.with.overflow.i32(i32 [[A0]], i32 3)
+; CHECK-NEXT:    [[A2:%.*]] = extractvalue { i32, i1 } [[A1]], 1
+; CHECK-NEXT:    [[A3:%.*]] = extractvalue { i32, i1 } [[A1]], 0
+; CHECK-NEXT:    [[A4:%.*]] = icmp ult i32 [[A3]], 2
+; CHECK-NEXT:    [[A5:%.*]] = or i1 [[A2]], [[A4]]
+; CHECK-NEXT:    ret i1 [[A5]]
+;
+  %a1 = tail call { i32, i1 } @llvm.uadd.with.overflow.i32(i32 %a0, i32 3)
+  %a2 = extractvalue { i32, i1 } %a1, 1
+  %a3 = extractvalue { i32, i1 } %a1, 0
+  %a4 = icmp ult i32 %a3, 2
+  %a5 = or i1 %a2, %a4
+  ret i1 %a5
+}
+
+define i1 @canonicalize_or_ssub_with_overflow_icmp_overflow(i32 %a0) {
+; CHECK-LABEL: define i1 @canonicalize_or_ssub_with_overflow_icmp_overflow(
+; CHECK-SAME: i32 [[A0:%.*]]) {
+; CHECK-NEXT:    [[A1:%.*]] = tail call { i32, i1 } @llvm.ssub.with.overflow.i32(i32 [[A0]], i32 -2147483648)
+; CHECK-NEXT:    [[A2:%.*]] = extractvalue { i32, i1 } [[A1]], 1
+; CHECK-NEXT:    [[A3:%.*]] = extractvalue { i32, i1 } [[A1]], 0
+; CHECK-NEXT:    [[A4:%.*]] = icmp slt i32 [[A3]], -1
+; CHECK-NEXT:    [[A5:%.*]] = or i1 [[A2]], [[A4]]
+; CHECK-NEXT:    ret i1 [[A5]]
+;
+  %a1 = tail call { i32, i1 } @llvm.ssub.with.overflow.i32(i32 %a0, i32 -2147483648)
+  %a2 = extractvalue { i32, i1 } %a1, 1
+  %a3 = extractvalue { i32, i1 } %a1, 0
+  %a4 = icmp slt i32 %a3, -1
+  %a5 = or i1 %a2, %a4
+  ret i1 %a5
+}
+
+define i1 @canonicalize_or_smul_with_overflow_icmp(i32 %a0) {
+; CHECK-LABEL: define i1 @canonicalize_or_smul_with_overflow_icmp(
+; CHECK-SAME: i32 [[A0:%.*]]) {
+; CHECK-NEXT:    [[A1:%.*]] = tail call { i32, i1 } @llvm.smul.with.overflow.i32(i32 [[A0]], i32 3)
+; CHECK-NEXT:    [[A2:%.*]] = extractvalue { i32, i1 } [[A1]], 1
+; CHECK-NEXT:    [[A3:%.*]] = extractvalue { i32, i1 } [[A1]], 0
+; CHECK-NEXT:    [[A4:%.*]] = icmp slt i32 [[A3]], 10
+; CHECK-NEXT:    [[A5:%.*]] = or i1 [[A2]], [[A4]]
+; CHECK-NEXT:    ret i1 [[A5]]
+;
+  %a1 = tail call { i32, i1 } @llvm.smul.with.overflow.i32(i32 %a0, i32 3)
+  %a2 = extractvalue { i32, i1 } %a1, 1
+  %a3 = extractvalue { i32, i1 } %a1, 0
+  %a4 = icmp slt i32 %a3, 10
+  %a5 = or i1 %a2, %a4
+  ret i1 %a5
+}

>From 7250691a741ccad5e275bc774afa45e2c8b3f6d7 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Fri, 15 Dec 2023 15:52:42 +0800
Subject: [PATCH 2/2] [InstCombine] Canonicalize `icmp pred (X +/- C1), C2`
 into `icmp pred X, C2 -/+ C1` with nowrap flag implied by with.overflow
 intrinsic

---
 .../InstCombine/InstCombineAndOrXor.cpp       | 29 +++++++++
 .../canonicalize-or-with-overflow-icmp.ll     | 64 +++++--------------
 2 files changed, 45 insertions(+), 48 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
index 31db1d3164b772..5e362f4117d051 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
@@ -3756,6 +3756,35 @@ Instruction *InstCombinerImpl::visitOr(BinaryOperator &I) {
     }
   }
 
+  /// Res, Overflow = xxx_with_overflow X, C1
+  /// Try to canonicalize the pattern "Overflow | icmp pred Res, C2" into
+  /// "Overflow | icmp pred X, C2 +/- C1".
+  const WithOverflowInst *WO;
+  const Value *WOV;
+  const APInt *C1, *C2;
+  if (match(&I, m_c_Or(m_CombineAnd(m_ExtractValue<1>(m_CombineAnd(
+                                        m_WithOverflowInst(WO), m_Value(WOV))),
+                                    m_Value(Ov)),
+                       m_OneUse(m_ICmp(Pred, m_ExtractValue<0>(m_Deferred(WOV)),
+                                       m_APInt(C2))))) &&
+      (WO->getBinaryOp() == Instruction::Add ||
+       WO->getBinaryOp() == Instruction::Sub) &&
+      (ICmpInst::isEquality(Pred) ||
+       WO->isSigned() == ICmpInst::isSigned(Pred)) &&
+      match(WO->getRHS(), m_APInt(C1))) {
+    bool Overflow;
+    APInt NewC = WO->getBinaryOp() == Instruction::Add
+                     ? (ICmpInst::isSigned(Pred) ? C2->ssub_ov(*C1, Overflow)
+                                                 : C2->usub_ov(*C1, Overflow))
+                     : (ICmpInst::isSigned(Pred) ? C2->sadd_ov(*C1, Overflow)
+                                                 : C2->uadd_ov(*C1, Overflow));
+    if (!Overflow || ICmpInst::isEquality(Pred)) {
+      Value *NewCmp = Builder.CreateICmp(
+          Pred, WO->getLHS(), ConstantInt::get(WO->getLHS()->getType(), NewC));
+      return BinaryOperator::CreateOr(Ov, NewCmp);
+    }
+  }
+
   // (~x) | y  -->  ~(x & (~y))  iff that gets rid of inversions
   if (sinkNotIntoOtherHandOfLogicalOp(I))
     return &I;
diff --git a/llvm/test/Transforms/InstCombine/canonicalize-or-with-overflow-icmp.ll b/llvm/test/Transforms/InstCombine/canonicalize-or-with-overflow-icmp.ll
index 722d6b2290a29b..2e801489ef4f13 100644
--- a/llvm/test/Transforms/InstCombine/canonicalize-or-with-overflow-icmp.ll
+++ b/llvm/test/Transforms/InstCombine/canonicalize-or-with-overflow-icmp.ll
@@ -12,13 +12,8 @@ declare void @use(i1)
 define i1 @ckd_add_unsigned(i31 %num) {
 ; CHECK-LABEL: define i1 @ckd_add_unsigned(
 ; CHECK-SAME: i31 [[NUM:%.*]]) {
-; CHECK-NEXT:    [[A0:%.*]] = zext i31 [[NUM]] to i32
-; CHECK-NEXT:    [[A1:%.*]] = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 [[A0]], i32 1)
-; CHECK-NEXT:    [[A2:%.*]] = extractvalue { i32, i1 } [[A1]], 1
-; CHECK-NEXT:    [[A3:%.*]] = extractvalue { i32, i1 } [[A1]], 0
-; CHECK-NEXT:    [[A4:%.*]] = icmp slt i32 [[A3]], 0
-; CHECK-NEXT:    [[A5:%.*]] = or i1 [[A2]], [[A4]]
-; CHECK-NEXT:    ret i1 [[A5]]
+; CHECK-NEXT:    [[A2:%.*]] = icmp eq i31 [[NUM]], -1
+; CHECK-NEXT:    ret i1 [[A2]]
 ;
   %a0 = zext i31 %num to i32
   %a1 = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 %a0, i32 1)
@@ -32,13 +27,8 @@ define i1 @ckd_add_unsigned(i31 %num) {
 define i1 @ckd_add_unsigned_commuted(i31 %num) {
 ; CHECK-LABEL: define i1 @ckd_add_unsigned_commuted(
 ; CHECK-SAME: i31 [[NUM:%.*]]) {
-; CHECK-NEXT:    [[A0:%.*]] = zext i31 [[NUM]] to i32
-; CHECK-NEXT:    [[A1:%.*]] = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 [[A0]], i32 1)
-; CHECK-NEXT:    [[A2:%.*]] = extractvalue { i32, i1 } [[A1]], 1
-; CHECK-NEXT:    [[A3:%.*]] = extractvalue { i32, i1 } [[A1]], 0
-; CHECK-NEXT:    [[A4:%.*]] = icmp slt i32 [[A3]], 0
-; CHECK-NEXT:    [[A5:%.*]] = or i1 [[A4]], [[A2]]
-; CHECK-NEXT:    ret i1 [[A5]]
+; CHECK-NEXT:    [[A2:%.*]] = icmp eq i31 [[NUM]], -1
+; CHECK-NEXT:    ret i1 [[A2]]
 ;
   %a0 = zext i31 %num to i32
   %a1 = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 %a0, i32 1)
@@ -52,13 +42,7 @@ define i1 @ckd_add_unsigned_commuted(i31 %num) {
 define i1 @ckd_add_unsigned_imply_true(i31 %num) {
 ; CHECK-LABEL: define i1 @ckd_add_unsigned_imply_true(
 ; CHECK-SAME: i31 [[NUM:%.*]]) {
-; CHECK-NEXT:    [[A0:%.*]] = zext i31 [[NUM]] to i32
-; CHECK-NEXT:    [[A1:%.*]] = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 [[A0]], i32 1)
-; CHECK-NEXT:    [[A2:%.*]] = extractvalue { i32, i1 } [[A1]], 1
-; CHECK-NEXT:    [[A3:%.*]] = extractvalue { i32, i1 } [[A1]], 0
-; CHECK-NEXT:    [[A4:%.*]] = icmp sgt i32 [[A3]], -1
-; CHECK-NEXT:    [[A5:%.*]] = or i1 [[A2]], [[A4]]
-; CHECK-NEXT:    ret i1 [[A5]]
+; CHECK-NEXT:    ret i1 true
 ;
   %a0 = zext i31 %num to i32
   %a1 = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 %a0, i32 1)
@@ -72,11 +56,8 @@ define i1 @ckd_add_unsigned_imply_true(i31 %num) {
 define i1 @canonicalize_or_sadd_with_overflow_icmp(i32 %a0) {
 ; CHECK-LABEL: define i1 @canonicalize_or_sadd_with_overflow_icmp(
 ; CHECK-SAME: i32 [[A0:%.*]]) {
-; CHECK-NEXT:    [[A1:%.*]] = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 [[A0]], i32 1)
-; CHECK-NEXT:    [[A2:%.*]] = extractvalue { i32, i1 } [[A1]], 1
-; CHECK-NEXT:    [[A3:%.*]] = extractvalue { i32, i1 } [[A1]], 0
-; CHECK-NEXT:    [[A4:%.*]] = icmp slt i32 [[A3]], 0
-; CHECK-NEXT:    [[A5:%.*]] = or i1 [[A2]], [[A4]]
+; CHECK-NEXT:    [[TMP1:%.*]] = add i32 [[A0]], -2147483647
+; CHECK-NEXT:    [[A5:%.*]] = icmp sgt i32 [[TMP1]], -1
 ; CHECK-NEXT:    ret i1 [[A5]]
 ;
   %a1 = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 %a0, i32 1)
@@ -90,12 +71,8 @@ define i1 @canonicalize_or_sadd_with_overflow_icmp(i32 %a0) {
 define i1 @canonicalize_or_ssub_with_overflow_icmp(i32 %a0) {
 ; CHECK-LABEL: define i1 @canonicalize_or_ssub_with_overflow_icmp(
 ; CHECK-SAME: i32 [[A0:%.*]]) {
-; CHECK-NEXT:    [[A1:%.*]] = call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 [[A0]], i32 -1)
-; CHECK-NEXT:    [[A2:%.*]] = extractvalue { i32, i1 } [[A1]], 1
-; CHECK-NEXT:    [[A3:%.*]] = extractvalue { i32, i1 } [[A1]], 0
-; CHECK-NEXT:    [[A4:%.*]] = icmp slt i32 [[A3]], 0
-; CHECK-NEXT:    [[A5:%.*]] = or i1 [[A2]], [[A4]]
-; CHECK-NEXT:    ret i1 [[A5]]
+; CHECK-NEXT:    [[TMP1:%.*]] = icmp slt i32 [[A0]], 1
+; CHECK-NEXT:    ret i1 [[TMP1]]
 ;
   %a1 = tail call { i32, i1 } @llvm.ssub.with.overflow.i32(i32 %a0, i32 1)
   %a2 = extractvalue { i32, i1 } %a1, 1
@@ -108,11 +85,8 @@ define i1 @canonicalize_or_ssub_with_overflow_icmp(i32 %a0) {
 define i1 @canonicalize_or_uadd_with_overflow_icmp(i32 %a0) {
 ; CHECK-LABEL: define i1 @canonicalize_or_uadd_with_overflow_icmp(
 ; CHECK-SAME: i32 [[A0:%.*]]) {
-; CHECK-NEXT:    [[A1:%.*]] = tail call { i32, i1 } @llvm.uadd.with.overflow.i32(i32 [[A0]], i32 1)
-; CHECK-NEXT:    [[A2:%.*]] = extractvalue { i32, i1 } [[A1]], 1
-; CHECK-NEXT:    [[A3:%.*]] = extractvalue { i32, i1 } [[A1]], 0
-; CHECK-NEXT:    [[A4:%.*]] = icmp ult i32 [[A3]], 10
-; CHECK-NEXT:    [[A5:%.*]] = or i1 [[A2]], [[A4]]
+; CHECK-NEXT:    [[TMP1:%.*]] = add i32 [[A0]], 1
+; CHECK-NEXT:    [[A5:%.*]] = icmp ult i32 [[TMP1]], 10
 ; CHECK-NEXT:    ret i1 [[A5]]
 ;
   %a1 = tail call { i32, i1 } @llvm.uadd.with.overflow.i32(i32 %a0, i32 1)
@@ -126,11 +100,9 @@ define i1 @canonicalize_or_uadd_with_overflow_icmp(i32 %a0) {
 define i1 @canonicalize_or_sadd_with_overflow_icmp_eq(i32 %a0) {
 ; CHECK-LABEL: define i1 @canonicalize_or_sadd_with_overflow_icmp_eq(
 ; CHECK-SAME: i32 [[A0:%.*]]) {
-; CHECK-NEXT:    [[A1:%.*]] = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 [[A0]], i32 1)
-; CHECK-NEXT:    [[A2:%.*]] = extractvalue { i32, i1 } [[A1]], 1
-; CHECK-NEXT:    [[A3:%.*]] = extractvalue { i32, i1 } [[A1]], 0
-; CHECK-NEXT:    [[A4:%.*]] = icmp eq i32 [[A3]], 10
-; CHECK-NEXT:    [[A5:%.*]] = or i1 [[A2]], [[A4]]
+; CHECK-NEXT:    [[A2:%.*]] = icmp eq i32 [[A0]], 2147483647
+; CHECK-NEXT:    [[TMP1:%.*]] = icmp eq i32 [[A0]], 9
+; CHECK-NEXT:    [[A5:%.*]] = or i1 [[A2]], [[TMP1]]
 ; CHECK-NEXT:    ret i1 [[A5]]
 ;
   %a1 = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 %a0, i32 1)
@@ -144,12 +116,8 @@ define i1 @canonicalize_or_sadd_with_overflow_icmp_eq(i32 %a0) {
 define i1 @canonicalize_or_uadd_with_overflow_icmp_ne(i32 %a0) {
 ; CHECK-LABEL: define i1 @canonicalize_or_uadd_with_overflow_icmp_ne(
 ; CHECK-SAME: i32 [[A0:%.*]]) {
-; CHECK-NEXT:    [[A1:%.*]] = tail call { i32, i1 } @llvm.uadd.with.overflow.i32(i32 [[A0]], i32 1)
-; CHECK-NEXT:    [[A2:%.*]] = extractvalue { i32, i1 } [[A1]], 1
-; CHECK-NEXT:    [[A3:%.*]] = extractvalue { i32, i1 } [[A1]], 0
-; CHECK-NEXT:    [[A4:%.*]] = icmp ne i32 [[A3]], 10
-; CHECK-NEXT:    [[A5:%.*]] = or i1 [[A2]], [[A4]]
-; CHECK-NEXT:    ret i1 [[A5]]
+; CHECK-NEXT:    [[TMP1:%.*]] = icmp ne i32 [[A0]], 9
+; CHECK-NEXT:    ret i1 [[TMP1]]
 ;
   %a1 = tail call { i32, i1 } @llvm.uadd.with.overflow.i32(i32 %a0, i32 1)
   %a2 = extractvalue { i32, i1 } %a1, 1



More information about the llvm-commits mailing list