[llvm] [InstCombine] Canonicalize the fcmp range check idiom into `fabs + fcmp` (PR #76367)

Yingwei Zheng via llvm-commits llvm-commits at lists.llvm.org
Mon Feb 5 10:09:33 PST 2024


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

>From 463eb6e066fd1919ee3084ed66d8cd7eb20ea3da Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Tue, 26 Dec 2023 03:41:39 +0800
Subject: [PATCH 1/5] [InstCombine] Add pre-commit tests. NFC.

---
 .../InstCombine/fcmp-range-check-idiom.ll     | 295 ++++++++++++++++++
 1 file changed, 295 insertions(+)
 create mode 100644 llvm/test/Transforms/InstCombine/fcmp-range-check-idiom.ll

diff --git a/llvm/test/Transforms/InstCombine/fcmp-range-check-idiom.ll b/llvm/test/Transforms/InstCombine/fcmp-range-check-idiom.ll
new file mode 100644
index 00000000000000..ce0f6e0e9f9422
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/fcmp-range-check-idiom.ll
@@ -0,0 +1,295 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4
+; RUN: opt -S -passes=instcombine %s | FileCheck %s
+
+declare void @use(i1)
+
+define i1 @test_and_olt(float %x) {
+; CHECK-LABEL: define i1 @test_and_olt(
+; CHECK-SAME: float [[X:%.*]]) {
+; CHECK-NEXT:    [[CMP1:%.*]] = fcmp olt float [[X]], 0x3C00000000000000
+; CHECK-NEXT:    [[CMP2:%.*]] = fcmp ogt float [[X]], 0xBC00000000000000
+; CHECK-NEXT:    [[COND:%.*]] = and i1 [[CMP1]], [[CMP2]]
+; CHECK-NEXT:    ret i1 [[COND]]
+;
+  %cmp1 = fcmp olt float %x, 0x3C00000000000000
+  %cmp2 = fcmp ogt float %x, 0xBC00000000000000
+  %cond = and i1 %cmp1, %cmp2
+  ret i1 %cond
+}
+define i1 @test_and_ole(float %x) {
+; CHECK-LABEL: define i1 @test_and_ole(
+; CHECK-SAME: float [[X:%.*]]) {
+; CHECK-NEXT:    [[CMP1:%.*]] = fcmp ole float [[X]], 0x3C00000000000000
+; CHECK-NEXT:    [[CMP2:%.*]] = fcmp oge float [[X]], 0xBC00000000000000
+; CHECK-NEXT:    [[COND:%.*]] = and i1 [[CMP1]], [[CMP2]]
+; CHECK-NEXT:    ret i1 [[COND]]
+;
+  %cmp1 = fcmp ole float %x, 0x3C00000000000000
+  %cmp2 = fcmp oge float %x, 0xBC00000000000000
+  %cond = and i1 %cmp1, %cmp2
+  ret i1 %cond
+}
+define i1 @test_or_ogt(float %x) {
+; CHECK-LABEL: define i1 @test_or_ogt(
+; CHECK-SAME: float [[X:%.*]]) {
+; CHECK-NEXT:    [[CMP1:%.*]] = fcmp ogt float [[X]], 0x3C00000000000000
+; CHECK-NEXT:    [[CMP2:%.*]] = fcmp olt float [[X]], 0xBC00000000000000
+; CHECK-NEXT:    [[COND:%.*]] = or i1 [[CMP1]], [[CMP2]]
+; CHECK-NEXT:    ret i1 [[COND]]
+;
+  %cmp1 = fcmp ogt float %x, 0x3C00000000000000
+  %cmp2 = fcmp olt float %x, 0xBC00000000000000
+  %cond = or i1 %cmp1, %cmp2
+  ret i1 %cond
+}
+define i1 @test_or_oge(float %x) {
+; CHECK-LABEL: define i1 @test_or_oge(
+; CHECK-SAME: float [[X:%.*]]) {
+; CHECK-NEXT:    [[CMP1:%.*]] = fcmp oge float [[X]], 0x3C00000000000000
+; CHECK-NEXT:    [[CMP2:%.*]] = fcmp ole float [[X]], 0xBC00000000000000
+; CHECK-NEXT:    [[COND:%.*]] = or i1 [[CMP1]], [[CMP2]]
+; CHECK-NEXT:    ret i1 [[COND]]
+;
+  %cmp1 = fcmp oge float %x, 0x3C00000000000000
+  %cmp2 = fcmp ole float %x, 0xBC00000000000000
+  %cond = or i1 %cmp1, %cmp2
+  ret i1 %cond
+}
+define i1 @test_and_ult(float %x) {
+; CHECK-LABEL: define i1 @test_and_ult(
+; CHECK-SAME: float [[X:%.*]]) {
+; CHECK-NEXT:    [[CMP1:%.*]] = fcmp ult float [[X]], 0x3C00000000000000
+; CHECK-NEXT:    [[CMP2:%.*]] = fcmp ugt float [[X]], 0xBC00000000000000
+; CHECK-NEXT:    [[COND:%.*]] = and i1 [[CMP1]], [[CMP2]]
+; CHECK-NEXT:    ret i1 [[COND]]
+;
+  %cmp1 = fcmp ult float %x, 0x3C00000000000000
+  %cmp2 = fcmp ugt float %x, 0xBC00000000000000
+  %cond = and i1 %cmp1, %cmp2
+  ret i1 %cond
+}
+define i1 @test_and_ule(float %x) {
+; CHECK-LABEL: define i1 @test_and_ule(
+; CHECK-SAME: float [[X:%.*]]) {
+; CHECK-NEXT:    [[CMP1:%.*]] = fcmp ule float [[X]], 0x3C00000000000000
+; CHECK-NEXT:    [[CMP2:%.*]] = fcmp uge float [[X]], 0xBC00000000000000
+; CHECK-NEXT:    [[COND:%.*]] = and i1 [[CMP1]], [[CMP2]]
+; CHECK-NEXT:    ret i1 [[COND]]
+;
+  %cmp1 = fcmp ule float %x, 0x3C00000000000000
+  %cmp2 = fcmp uge float %x, 0xBC00000000000000
+  %cond = and i1 %cmp1, %cmp2
+  ret i1 %cond
+}
+define i1 @test_or_ugt(float %x) {
+; CHECK-LABEL: define i1 @test_or_ugt(
+; CHECK-SAME: float [[X:%.*]]) {
+; CHECK-NEXT:    [[CMP1:%.*]] = fcmp ugt float [[X]], 0x3C00000000000000
+; CHECK-NEXT:    [[CMP2:%.*]] = fcmp ult float [[X]], 0xBC00000000000000
+; CHECK-NEXT:    [[COND:%.*]] = or i1 [[CMP1]], [[CMP2]]
+; CHECK-NEXT:    ret i1 [[COND]]
+;
+  %cmp1 = fcmp ugt float %x, 0x3C00000000000000
+  %cmp2 = fcmp ult float %x, 0xBC00000000000000
+  %cond = or i1 %cmp1, %cmp2
+  ret i1 %cond
+}
+define i1 @test_or_uge(float %x) {
+; CHECK-LABEL: define i1 @test_or_uge(
+; CHECK-SAME: float [[X:%.*]]) {
+; CHECK-NEXT:    [[CMP1:%.*]] = fcmp uge float [[X]], 0x3C00000000000000
+; CHECK-NEXT:    [[CMP2:%.*]] = fcmp ule float [[X]], 0xBC00000000000000
+; CHECK-NEXT:    [[COND:%.*]] = or i1 [[CMP1]], [[CMP2]]
+; CHECK-NEXT:    ret i1 [[COND]]
+;
+  %cmp1 = fcmp uge float %x, 0x3C00000000000000
+  %cmp2 = fcmp ule float %x, 0xBC00000000000000
+  %cond = or i1 %cmp1, %cmp2
+  ret i1 %cond
+}
+define i1 @test_and_olt_commuted(float %x) {
+; CHECK-LABEL: define i1 @test_and_olt_commuted(
+; CHECK-SAME: float [[X:%.*]]) {
+; CHECK-NEXT:    [[CMP1:%.*]] = fcmp olt float [[X]], 0x3C00000000000000
+; CHECK-NEXT:    [[CMP2:%.*]] = fcmp ogt float [[X]], 0xBC00000000000000
+; CHECK-NEXT:    [[COND:%.*]] = and i1 [[CMP2]], [[CMP1]]
+; CHECK-NEXT:    ret i1 [[COND]]
+;
+  %cmp1 = fcmp olt float %x, 0x3C00000000000000
+  %cmp2 = fcmp ogt float %x, 0xBC00000000000000
+  %cond = and i1 %cmp2, %cmp1
+  ret i1 %cond
+}
+define i1 @test_and_olt_subnormal(float %x) {
+; CHECK-LABEL: define i1 @test_and_olt_subnormal(
+; CHECK-SAME: float [[X:%.*]]) {
+; CHECK-NEXT:    [[CMP1:%.*]] = fcmp olt float [[X]], 0x36A0000000000000
+; CHECK-NEXT:    [[CMP2:%.*]] = fcmp ogt float [[X]], 0xB6A0000000000000
+; CHECK-NEXT:    [[COND:%.*]] = and i1 [[CMP1]], [[CMP2]]
+; CHECK-NEXT:    ret i1 [[COND]]
+;
+  %cmp1 = fcmp olt float %x, 0x36A0000000000000
+  %cmp2 = fcmp ogt float %x, 0xB6A0000000000000
+  %cond = and i1 %cmp1, %cmp2
+  ret i1 %cond
+}
+define i1 @test_and_olt_infinity(float %x) {
+; CHECK-LABEL: define i1 @test_and_olt_infinity(
+; CHECK-SAME: float [[X:%.*]]) {
+; CHECK-NEXT:    [[TMP1:%.*]] = call float @llvm.fabs.f32(float [[X]])
+; CHECK-NEXT:    [[COND:%.*]] = fcmp one float [[TMP1]], 0x7FF0000000000000
+; CHECK-NEXT:    ret i1 [[COND]]
+;
+  %cmp1 = fcmp olt float %x, 0x7FF0000000000000
+  %cmp2 = fcmp ogt float %x, 0xFFF0000000000000
+  %cond = and i1 %cmp1, %cmp2
+  ret i1 %cond
+}
+define i1 @test_and_olt_zero(float %x) {
+; CHECK-LABEL: define i1 @test_and_olt_zero(
+; CHECK-SAME: float [[X:%.*]]) {
+; CHECK-NEXT:    ret i1 false
+;
+  %cmp1 = fcmp olt float %x, 0x0000000000000000
+  %cmp2 = fcmp ogt float %x, 0x8000000000000000
+  %cond = and i1 %cmp1, %cmp2
+  ret i1 %cond
+}
+define i1 @test_and_ole_zero(float %x) {
+; CHECK-LABEL: define i1 @test_and_ole_zero(
+; CHECK-SAME: float [[X:%.*]]) {
+; CHECK-NEXT:    [[COND:%.*]] = fcmp oeq float [[X]], 0.000000e+00
+; CHECK-NEXT:    ret i1 [[COND]]
+;
+  %cmp1 = fcmp ole float %x, 0x0000000000000000
+  %cmp2 = fcmp oge float %x, 0x8000000000000000
+  %cond = and i1 %cmp1, %cmp2
+  ret i1 %cond
+}
+define i1 @test_and_olt_logical(float %x) {
+; CHECK-LABEL: define i1 @test_and_olt_logical(
+; CHECK-SAME: float [[X:%.*]]) {
+; CHECK-NEXT:    [[CMP1:%.*]] = fcmp olt float [[X]], 0x3C00000000000000
+; CHECK-NEXT:    [[CMP2:%.*]] = fcmp ogt float [[X]], 0xBC00000000000000
+; CHECK-NEXT:    [[COND:%.*]] = and i1 [[CMP1]], [[CMP2]]
+; CHECK-NEXT:    ret i1 [[COND]]
+;
+  %cmp1 = fcmp olt float %x, 0x3C00000000000000
+  %cmp2 = fcmp ogt float %x, 0xBC00000000000000
+  %cond = select i1 %cmp1, i1 %cmp2, i1 false
+  ret i1 %cond
+}
+define <2 x i1> @test_and_olt_undef(<2 x float> %x) {
+; CHECK-LABEL: define <2 x i1> @test_and_olt_undef(
+; CHECK-SAME: <2 x float> [[X:%.*]]) {
+; CHECK-NEXT:    [[CMP1:%.*]] = fcmp olt <2 x float> [[X]], <float 0x3C00000000000000, float undef>
+; CHECK-NEXT:    [[CMP2:%.*]] = fcmp ogt <2 x float> [[X]], <float 0xBC00000000000000, float undef>
+; CHECK-NEXT:    [[COND:%.*]] = and <2 x i1> [[CMP1]], [[CMP2]]
+; CHECK-NEXT:    ret <2 x i1> [[COND]]
+;
+  %cmp1 = fcmp olt <2 x float> %x, <float 0x3C00000000000000, float undef>
+  %cmp2 = fcmp ogt <2 x float> %x, <float 0xBC00000000000000, float undef>
+  %cond = and <2 x i1> %cmp1, %cmp2
+  ret <2 x i1> %cond
+}
+define i1 @test_and_olt_nan(float %x) {
+; CHECK-LABEL: define i1 @test_and_olt_nan(
+; CHECK-SAME: float [[X:%.*]]) {
+; CHECK-NEXT:    ret i1 false
+;
+  %cmp1 = fcmp olt float %x, 0x7FF8000000000000
+  %cmp2 = fcmp ogt float %x, 0xFFF8000000000000
+  %cond = and i1 %cmp1, %cmp2
+  ret i1 %cond
+}
+define i1 @test_and_ogt(float %x) {
+; CHECK-LABEL: define i1 @test_and_ogt(
+; CHECK-SAME: float [[X:%.*]]) {
+; CHECK-NEXT:    [[CMP1:%.*]] = fcmp ogt float [[X]], 0x3C00000000000000
+; CHECK-NEXT:    [[CMP2:%.*]] = fcmp olt float [[X]], 0xBC00000000000000
+; CHECK-NEXT:    [[COND:%.*]] = and i1 [[CMP1]], [[CMP2]]
+; CHECK-NEXT:    ret i1 [[COND]]
+;
+  %cmp1 = fcmp ogt float %x, 0x3C00000000000000
+  %cmp2 = fcmp olt float %x, 0xBC00000000000000
+  %cond = and i1 %cmp1, %cmp2
+  ret i1 %cond
+}
+define i1 @test_or_olt(float %x) {
+; CHECK-LABEL: define i1 @test_or_olt(
+; CHECK-SAME: float [[X:%.*]]) {
+; CHECK-NEXT:    [[CMP1:%.*]] = fcmp olt float [[X]], 0x3C00000000000000
+; CHECK-NEXT:    [[CMP2:%.*]] = fcmp ogt float [[X]], 0xBC00000000000000
+; CHECK-NEXT:    [[COND:%.*]] = or i1 [[CMP1]], [[CMP2]]
+; CHECK-NEXT:    ret i1 [[COND]]
+;
+  %cmp1 = fcmp olt float %x, 0x3C00000000000000
+  %cmp2 = fcmp ogt float %x, 0xBC00000000000000
+  %cond = or i1 %cmp1, %cmp2
+  ret i1 %cond
+}
+; Negative tests
+define i1 @test_and_olt_multiuse(float %x) {
+; CHECK-LABEL: define i1 @test_and_olt_multiuse(
+; CHECK-SAME: float [[X:%.*]]) {
+; CHECK-NEXT:    [[CMP1:%.*]] = fcmp olt float [[X]], 0x3C00000000000000
+; CHECK-NEXT:    call void @use(i1 [[CMP1]])
+; CHECK-NEXT:    [[CMP2:%.*]] = fcmp ogt float [[X]], 0xBC00000000000000
+; CHECK-NEXT:    [[COND:%.*]] = and i1 [[CMP1]], [[CMP2]]
+; CHECK-NEXT:    ret i1 [[COND]]
+;
+  %cmp1 = fcmp olt float %x, 0x3C00000000000000
+  call void @use(i1 %cmp1)
+  %cmp2 = fcmp ogt float %x, 0xBC00000000000000
+  %cond = and i1 %cmp1, %cmp2
+  ret i1 %cond
+}
+define i1 @test_and_olt_mismatched_lhs(float %x, float %y) {
+; CHECK-LABEL: define i1 @test_and_olt_mismatched_lhs(
+; CHECK-SAME: float [[X:%.*]], float [[Y:%.*]]) {
+; CHECK-NEXT:    [[CMP1:%.*]] = fcmp olt float [[X]], 0x3C00000000000000
+; CHECK-NEXT:    [[CMP2:%.*]] = fcmp ogt float [[Y]], 0xBC00000000000000
+; CHECK-NEXT:    [[COND:%.*]] = and i1 [[CMP1]], [[CMP2]]
+; CHECK-NEXT:    ret i1 [[COND]]
+;
+  %cmp1 = fcmp olt float %x, 0x3C00000000000000
+  %cmp2 = fcmp ogt float %y, 0xBC00000000000000
+  %cond = and i1 %cmp1, %cmp2
+  ret i1 %cond
+}
+define i1 @test_and_olt_same_sign(float %x) {
+; CHECK-LABEL: define i1 @test_and_olt_same_sign(
+; CHECK-SAME: float [[X:%.*]]) {
+; CHECK-NEXT:    ret i1 false
+;
+  %cmp1 = fcmp olt float %x, 0x3C00000000000000
+  %cmp2 = fcmp ogt float %x, 0x3C00000000000000
+  %cond = and i1 %cmp1, %cmp2
+  ret i1 %cond
+}
+define i1 @test_and_olt_mismatched_mag(float %x) {
+; CHECK-LABEL: define i1 @test_and_olt_mismatched_mag(
+; CHECK-SAME: float [[X:%.*]]) {
+; CHECK-NEXT:    [[CMP1:%.*]] = fcmp olt float [[X]], 0x3C80000000000000
+; CHECK-NEXT:    [[CMP2:%.*]] = fcmp ogt float [[X]], 0xBC00000000000000
+; CHECK-NEXT:    [[COND:%.*]] = and i1 [[CMP1]], [[CMP2]]
+; CHECK-NEXT:    ret i1 [[COND]]
+;
+  %cmp1 = fcmp olt float %x, 0x3C80000000000000
+  %cmp2 = fcmp ogt float %x, 0xBC00000000000000
+  %cond = and i1 %cmp1, %cmp2
+  ret i1 %cond
+}
+define i1 @test_and_olt_wrong_pred2(float %x) {
+; CHECK-LABEL: define i1 @test_and_olt_wrong_pred2(
+; CHECK-SAME: float [[X:%.*]]) {
+; CHECK-NEXT:    [[CMP1:%.*]] = fcmp olt float [[X]], 0x3C00000000000000
+; CHECK-NEXT:    [[CMP2:%.*]] = fcmp oge float [[X]], 0xBC00000000000000
+; CHECK-NEXT:    [[COND:%.*]] = and i1 [[CMP1]], [[CMP2]]
+; CHECK-NEXT:    ret i1 [[COND]]
+;
+  %cmp1 = fcmp olt float %x, 0x3C00000000000000
+  %cmp2 = fcmp oge float %x, 0xBC00000000000000
+  %cond = and i1 %cmp1, %cmp2
+  ret i1 %cond
+}

>From f5aa6c79cadd45e70ebb632f2491e6041f0772a3 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Tue, 26 Dec 2023 03:43:27 +0800
Subject: [PATCH 2/5] [InstCombine] Canonicalize the fcmp range check idiom
 into fabs + fcmp

---
 .../InstCombine/InstCombineAndOrXor.cpp       | 26 +++++++
 .../InstCombine/fcmp-range-check-idiom.ll     | 70 +++++++------------
 2 files changed, 53 insertions(+), 43 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
index 1cfa797be2207e..91fa8172d16d12 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
@@ -1419,6 +1419,32 @@ Value *InstCombinerImpl::foldLogicOfFCmps(FCmpInst *LHS, FCmpInst *RHS,
     }
   }
 
+  // Canonicalize the range check idiom:
+  // and (fcmp olt/ole/ult/ule x, C), (fcmp ogt/oge/ugt/uge x, -C)
+  // --> fabs(x) olt/ole/ult/ule C
+  // or  (fcmp ogt/oge/ugt/uge x, C), (fcmp olt/ole/ult/ule x, -C)
+  // --> fabs(x) ogt/oge/ugt/uge C
+  // TODO: Generalize to handle a negated variable operand?
+  const APFloat *LHSC, *RHSC;
+  if (LHS->hasOneUse() && RHS->hasOneUse() && LHS0 == RHS0 &&
+      FCmpInst::getSwappedPredicate(PredL) == PredR &&
+      match(LHS1, m_APFloatAllowUndef(LHSC)) &&
+      match(RHS1, m_APFloatAllowUndef(RHSC)) &&
+      LHSC->bitwiseIsEqual(neg(*RHSC))) {
+    auto IsLessThanOrLessEqual = [](FCmpInst::Predicate Pred) {
+      return (getFCmpCode(Pred) & 0b0110) == 0b0100;
+    };
+    if (IsLessThanOrLessEqual(IsAnd ? PredR : PredL)) {
+      std::swap(LHSC, RHSC);
+      std::swap(PredL, PredR);
+    }
+    if (IsLessThanOrLessEqual(IsAnd ? PredL : PredR)) {
+      Value *FAbs = Builder.CreateUnaryIntrinsic(Intrinsic::fabs, LHS0);
+      return Builder.CreateFCmp(PredL, FAbs,
+                                ConstantFP::get(LHS0->getType(), *LHSC));
+    }
+  }
+
   return nullptr;
 }
 
diff --git a/llvm/test/Transforms/InstCombine/fcmp-range-check-idiom.ll b/llvm/test/Transforms/InstCombine/fcmp-range-check-idiom.ll
index ce0f6e0e9f9422..06e5ca05ff1501 100644
--- a/llvm/test/Transforms/InstCombine/fcmp-range-check-idiom.ll
+++ b/llvm/test/Transforms/InstCombine/fcmp-range-check-idiom.ll
@@ -6,9 +6,8 @@ declare void @use(i1)
 define i1 @test_and_olt(float %x) {
 ; CHECK-LABEL: define i1 @test_and_olt(
 ; CHECK-SAME: float [[X:%.*]]) {
-; CHECK-NEXT:    [[CMP1:%.*]] = fcmp olt float [[X]], 0x3C00000000000000
-; CHECK-NEXT:    [[CMP2:%.*]] = fcmp ogt float [[X]], 0xBC00000000000000
-; CHECK-NEXT:    [[COND:%.*]] = and i1 [[CMP1]], [[CMP2]]
+; CHECK-NEXT:    [[TMP1:%.*]] = call float @llvm.fabs.f32(float [[X]])
+; CHECK-NEXT:    [[COND:%.*]] = fcmp olt float [[TMP1]], 0x3C00000000000000
 ; CHECK-NEXT:    ret i1 [[COND]]
 ;
   %cmp1 = fcmp olt float %x, 0x3C00000000000000
@@ -19,9 +18,8 @@ define i1 @test_and_olt(float %x) {
 define i1 @test_and_ole(float %x) {
 ; CHECK-LABEL: define i1 @test_and_ole(
 ; CHECK-SAME: float [[X:%.*]]) {
-; CHECK-NEXT:    [[CMP1:%.*]] = fcmp ole float [[X]], 0x3C00000000000000
-; CHECK-NEXT:    [[CMP2:%.*]] = fcmp oge float [[X]], 0xBC00000000000000
-; CHECK-NEXT:    [[COND:%.*]] = and i1 [[CMP1]], [[CMP2]]
+; CHECK-NEXT:    [[TMP1:%.*]] = call float @llvm.fabs.f32(float [[X]])
+; CHECK-NEXT:    [[COND:%.*]] = fcmp ole float [[TMP1]], 0x3C00000000000000
 ; CHECK-NEXT:    ret i1 [[COND]]
 ;
   %cmp1 = fcmp ole float %x, 0x3C00000000000000
@@ -32,9 +30,8 @@ define i1 @test_and_ole(float %x) {
 define i1 @test_or_ogt(float %x) {
 ; CHECK-LABEL: define i1 @test_or_ogt(
 ; CHECK-SAME: float [[X:%.*]]) {
-; CHECK-NEXT:    [[CMP1:%.*]] = fcmp ogt float [[X]], 0x3C00000000000000
-; CHECK-NEXT:    [[CMP2:%.*]] = fcmp olt float [[X]], 0xBC00000000000000
-; CHECK-NEXT:    [[COND:%.*]] = or i1 [[CMP1]], [[CMP2]]
+; CHECK-NEXT:    [[TMP1:%.*]] = call float @llvm.fabs.f32(float [[X]])
+; CHECK-NEXT:    [[COND:%.*]] = fcmp ogt float [[TMP1]], 0x3C00000000000000
 ; CHECK-NEXT:    ret i1 [[COND]]
 ;
   %cmp1 = fcmp ogt float %x, 0x3C00000000000000
@@ -45,9 +42,8 @@ define i1 @test_or_ogt(float %x) {
 define i1 @test_or_oge(float %x) {
 ; CHECK-LABEL: define i1 @test_or_oge(
 ; CHECK-SAME: float [[X:%.*]]) {
-; CHECK-NEXT:    [[CMP1:%.*]] = fcmp oge float [[X]], 0x3C00000000000000
-; CHECK-NEXT:    [[CMP2:%.*]] = fcmp ole float [[X]], 0xBC00000000000000
-; CHECK-NEXT:    [[COND:%.*]] = or i1 [[CMP1]], [[CMP2]]
+; CHECK-NEXT:    [[TMP1:%.*]] = call float @llvm.fabs.f32(float [[X]])
+; CHECK-NEXT:    [[COND:%.*]] = fcmp oge float [[TMP1]], 0x3C00000000000000
 ; CHECK-NEXT:    ret i1 [[COND]]
 ;
   %cmp1 = fcmp oge float %x, 0x3C00000000000000
@@ -58,9 +54,8 @@ define i1 @test_or_oge(float %x) {
 define i1 @test_and_ult(float %x) {
 ; CHECK-LABEL: define i1 @test_and_ult(
 ; CHECK-SAME: float [[X:%.*]]) {
-; CHECK-NEXT:    [[CMP1:%.*]] = fcmp ult float [[X]], 0x3C00000000000000
-; CHECK-NEXT:    [[CMP2:%.*]] = fcmp ugt float [[X]], 0xBC00000000000000
-; CHECK-NEXT:    [[COND:%.*]] = and i1 [[CMP1]], [[CMP2]]
+; CHECK-NEXT:    [[TMP1:%.*]] = call float @llvm.fabs.f32(float [[X]])
+; CHECK-NEXT:    [[COND:%.*]] = fcmp ult float [[TMP1]], 0x3C00000000000000
 ; CHECK-NEXT:    ret i1 [[COND]]
 ;
   %cmp1 = fcmp ult float %x, 0x3C00000000000000
@@ -71,9 +66,8 @@ define i1 @test_and_ult(float %x) {
 define i1 @test_and_ule(float %x) {
 ; CHECK-LABEL: define i1 @test_and_ule(
 ; CHECK-SAME: float [[X:%.*]]) {
-; CHECK-NEXT:    [[CMP1:%.*]] = fcmp ule float [[X]], 0x3C00000000000000
-; CHECK-NEXT:    [[CMP2:%.*]] = fcmp uge float [[X]], 0xBC00000000000000
-; CHECK-NEXT:    [[COND:%.*]] = and i1 [[CMP1]], [[CMP2]]
+; CHECK-NEXT:    [[TMP1:%.*]] = call float @llvm.fabs.f32(float [[X]])
+; CHECK-NEXT:    [[COND:%.*]] = fcmp ule float [[TMP1]], 0x3C00000000000000
 ; CHECK-NEXT:    ret i1 [[COND]]
 ;
   %cmp1 = fcmp ule float %x, 0x3C00000000000000
@@ -84,9 +78,8 @@ define i1 @test_and_ule(float %x) {
 define i1 @test_or_ugt(float %x) {
 ; CHECK-LABEL: define i1 @test_or_ugt(
 ; CHECK-SAME: float [[X:%.*]]) {
-; CHECK-NEXT:    [[CMP1:%.*]] = fcmp ugt float [[X]], 0x3C00000000000000
-; CHECK-NEXT:    [[CMP2:%.*]] = fcmp ult float [[X]], 0xBC00000000000000
-; CHECK-NEXT:    [[COND:%.*]] = or i1 [[CMP1]], [[CMP2]]
+; CHECK-NEXT:    [[TMP1:%.*]] = call float @llvm.fabs.f32(float [[X]])
+; CHECK-NEXT:    [[COND:%.*]] = fcmp ugt float [[TMP1]], 0x3C00000000000000
 ; CHECK-NEXT:    ret i1 [[COND]]
 ;
   %cmp1 = fcmp ugt float %x, 0x3C00000000000000
@@ -97,9 +90,8 @@ define i1 @test_or_ugt(float %x) {
 define i1 @test_or_uge(float %x) {
 ; CHECK-LABEL: define i1 @test_or_uge(
 ; CHECK-SAME: float [[X:%.*]]) {
-; CHECK-NEXT:    [[CMP1:%.*]] = fcmp uge float [[X]], 0x3C00000000000000
-; CHECK-NEXT:    [[CMP2:%.*]] = fcmp ule float [[X]], 0xBC00000000000000
-; CHECK-NEXT:    [[COND:%.*]] = or i1 [[CMP1]], [[CMP2]]
+; CHECK-NEXT:    [[TMP1:%.*]] = call float @llvm.fabs.f32(float [[X]])
+; CHECK-NEXT:    [[COND:%.*]] = fcmp uge float [[TMP1]], 0x3C00000000000000
 ; CHECK-NEXT:    ret i1 [[COND]]
 ;
   %cmp1 = fcmp uge float %x, 0x3C00000000000000
@@ -110,9 +102,8 @@ define i1 @test_or_uge(float %x) {
 define i1 @test_and_olt_commuted(float %x) {
 ; CHECK-LABEL: define i1 @test_and_olt_commuted(
 ; CHECK-SAME: float [[X:%.*]]) {
-; CHECK-NEXT:    [[CMP1:%.*]] = fcmp olt float [[X]], 0x3C00000000000000
-; CHECK-NEXT:    [[CMP2:%.*]] = fcmp ogt float [[X]], 0xBC00000000000000
-; CHECK-NEXT:    [[COND:%.*]] = and i1 [[CMP2]], [[CMP1]]
+; CHECK-NEXT:    [[TMP1:%.*]] = call float @llvm.fabs.f32(float [[X]])
+; CHECK-NEXT:    [[COND:%.*]] = fcmp olt float [[TMP1]], 0x3C00000000000000
 ; CHECK-NEXT:    ret i1 [[COND]]
 ;
   %cmp1 = fcmp olt float %x, 0x3C00000000000000
@@ -123,9 +114,8 @@ define i1 @test_and_olt_commuted(float %x) {
 define i1 @test_and_olt_subnormal(float %x) {
 ; CHECK-LABEL: define i1 @test_and_olt_subnormal(
 ; CHECK-SAME: float [[X:%.*]]) {
-; CHECK-NEXT:    [[CMP1:%.*]] = fcmp olt float [[X]], 0x36A0000000000000
-; CHECK-NEXT:    [[CMP2:%.*]] = fcmp ogt float [[X]], 0xB6A0000000000000
-; CHECK-NEXT:    [[COND:%.*]] = and i1 [[CMP1]], [[CMP2]]
+; CHECK-NEXT:    [[TMP1:%.*]] = call float @llvm.fabs.f32(float [[X]])
+; CHECK-NEXT:    [[COND:%.*]] = fcmp olt float [[TMP1]], 0x36A0000000000000
 ; CHECK-NEXT:    ret i1 [[COND]]
 ;
   %cmp1 = fcmp olt float %x, 0x36A0000000000000
@@ -169,9 +159,8 @@ define i1 @test_and_ole_zero(float %x) {
 define i1 @test_and_olt_logical(float %x) {
 ; CHECK-LABEL: define i1 @test_and_olt_logical(
 ; CHECK-SAME: float [[X:%.*]]) {
-; CHECK-NEXT:    [[CMP1:%.*]] = fcmp olt float [[X]], 0x3C00000000000000
-; CHECK-NEXT:    [[CMP2:%.*]] = fcmp ogt float [[X]], 0xBC00000000000000
-; CHECK-NEXT:    [[COND:%.*]] = and i1 [[CMP1]], [[CMP2]]
+; CHECK-NEXT:    [[TMP1:%.*]] = call float @llvm.fabs.f32(float [[X]])
+; CHECK-NEXT:    [[COND:%.*]] = fcmp olt float [[TMP1]], 0x3C00000000000000
 ; CHECK-NEXT:    ret i1 [[COND]]
 ;
   %cmp1 = fcmp olt float %x, 0x3C00000000000000
@@ -182,9 +171,8 @@ define i1 @test_and_olt_logical(float %x) {
 define <2 x i1> @test_and_olt_undef(<2 x float> %x) {
 ; CHECK-LABEL: define <2 x i1> @test_and_olt_undef(
 ; CHECK-SAME: <2 x float> [[X:%.*]]) {
-; CHECK-NEXT:    [[CMP1:%.*]] = fcmp olt <2 x float> [[X]], <float 0x3C00000000000000, float undef>
-; CHECK-NEXT:    [[CMP2:%.*]] = fcmp ogt <2 x float> [[X]], <float 0xBC00000000000000, float undef>
-; CHECK-NEXT:    [[COND:%.*]] = and <2 x i1> [[CMP1]], [[CMP2]]
+; CHECK-NEXT:    [[TMP1:%.*]] = call <2 x float> @llvm.fabs.v2f32(<2 x float> [[X]])
+; CHECK-NEXT:    [[COND:%.*]] = fcmp olt <2 x float> [[TMP1]], <float 0x3C00000000000000, float 0x3C00000000000000>
 ; CHECK-NEXT:    ret <2 x i1> [[COND]]
 ;
   %cmp1 = fcmp olt <2 x float> %x, <float 0x3C00000000000000, float undef>
@@ -205,10 +193,7 @@ define i1 @test_and_olt_nan(float %x) {
 define i1 @test_and_ogt(float %x) {
 ; CHECK-LABEL: define i1 @test_and_ogt(
 ; CHECK-SAME: float [[X:%.*]]) {
-; CHECK-NEXT:    [[CMP1:%.*]] = fcmp ogt float [[X]], 0x3C00000000000000
-; CHECK-NEXT:    [[CMP2:%.*]] = fcmp olt float [[X]], 0xBC00000000000000
-; CHECK-NEXT:    [[COND:%.*]] = and i1 [[CMP1]], [[CMP2]]
-; CHECK-NEXT:    ret i1 [[COND]]
+; CHECK-NEXT:    ret i1 false
 ;
   %cmp1 = fcmp ogt float %x, 0x3C00000000000000
   %cmp2 = fcmp olt float %x, 0xBC00000000000000
@@ -218,9 +203,8 @@ define i1 @test_and_ogt(float %x) {
 define i1 @test_or_olt(float %x) {
 ; CHECK-LABEL: define i1 @test_or_olt(
 ; CHECK-SAME: float [[X:%.*]]) {
-; CHECK-NEXT:    [[CMP1:%.*]] = fcmp olt float [[X]], 0x3C00000000000000
-; CHECK-NEXT:    [[CMP2:%.*]] = fcmp ogt float [[X]], 0xBC00000000000000
-; CHECK-NEXT:    [[COND:%.*]] = or i1 [[CMP1]], [[CMP2]]
+; CHECK-NEXT:    [[TMP1:%.*]] = call float @llvm.fabs.f32(float [[X]])
+; CHECK-NEXT:    [[COND:%.*]] = fcmp ogt float [[TMP1]], 0xBC00000000000000
 ; CHECK-NEXT:    ret i1 [[COND]]
 ;
   %cmp1 = fcmp olt float %x, 0x3C00000000000000

>From 7f91ae34599a3e1a46ae61a85901d4d8dbb0dbb9 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Sun, 7 Jan 2024 20:30:06 +0800
Subject: [PATCH 3/5] [InstCombine] Preserve fast-math flags.

---
 .../Transforms/InstCombine/InstCombineAndOrXor.cpp  |  5 +++++
 .../InstCombine/fcmp-range-check-idiom.ll           | 13 +++++++++++++
 2 files changed, 18 insertions(+)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
index 91fa8172d16d12..64bac1117cbc7a 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
@@ -1439,6 +1439,11 @@ Value *InstCombinerImpl::foldLogicOfFCmps(FCmpInst *LHS, FCmpInst *RHS,
       std::swap(PredL, PredR);
     }
     if (IsLessThanOrLessEqual(IsAnd ? PredL : PredR)) {
+      BuilderTy::FastMathFlagGuard Guard(Builder);
+      FastMathFlags FMF = LHS->getFastMathFlags();
+      FMF &= RHS->getFastMathFlags();
+      Builder.setFastMathFlags(FMF);
+
       Value *FAbs = Builder.CreateUnaryIntrinsic(Intrinsic::fabs, LHS0);
       return Builder.CreateFCmp(PredL, FAbs,
                                 ConstantFP::get(LHS0->getType(), *LHSC));
diff --git a/llvm/test/Transforms/InstCombine/fcmp-range-check-idiom.ll b/llvm/test/Transforms/InstCombine/fcmp-range-check-idiom.ll
index 06e5ca05ff1501..67ea0c89fb2ffe 100644
--- a/llvm/test/Transforms/InstCombine/fcmp-range-check-idiom.ll
+++ b/llvm/test/Transforms/InstCombine/fcmp-range-check-idiom.ll
@@ -277,3 +277,16 @@ define i1 @test_and_olt_wrong_pred2(float %x) {
   %cond = and i1 %cmp1, %cmp2
   ret i1 %cond
 }
+
+define i1 @test_and_olt_fmf_propagation(float %x) {
+; CHECK-LABEL: define i1 @test_and_olt_fmf_propagation(
+; CHECK-SAME: float [[X:%.*]]) {
+; CHECK-NEXT:    [[TMP1:%.*]] = call nnan ninf nsz float @llvm.fabs.f32(float [[X]])
+; CHECK-NEXT:    [[COND:%.*]] = fcmp nnan ninf nsz olt float [[TMP1]], 0x3C00000000000000
+; CHECK-NEXT:    ret i1 [[COND]]
+;
+  %cmp1 = fcmp nsz nnan ninf olt float %x, 0x3C00000000000000
+  %cmp2 = fcmp nsz nnan ninf ogt float %x, 0xBC00000000000000
+  %cond = and i1 %cmp1, %cmp2
+  ret i1 %cond
+}

>From adfb79d17946aa2c14b973e028b4f64fb9a4fecc Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Mon, 22 Jan 2024 20:41:17 +0800
Subject: [PATCH 4/5] fixup! [InstCombine] Canonicalize the fcmp range check
 idiom into fabs + fcmp

---
 .../InstCombine/InstCombineAndOrXor.cpp       | 14 ++++++--
 .../InstCombine/fcmp-range-check-idiom.ll     | 36 +++++++++++++++++++
 2 files changed, 47 insertions(+), 3 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
index 64bac1117cbc7a..5635a2056f0670 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
@@ -1426,13 +1426,21 @@ Value *InstCombinerImpl::foldLogicOfFCmps(FCmpInst *LHS, FCmpInst *RHS,
   // --> fabs(x) ogt/oge/ugt/uge C
   // TODO: Generalize to handle a negated variable operand?
   const APFloat *LHSC, *RHSC;
-  if (LHS->hasOneUse() && RHS->hasOneUse() && LHS0 == RHS0 &&
+  if (LHS0 == RHS0 && LHS->hasOneUse() && RHS->hasOneUse() &&
       FCmpInst::getSwappedPredicate(PredL) == PredR &&
       match(LHS1, m_APFloatAllowUndef(LHSC)) &&
       match(RHS1, m_APFloatAllowUndef(RHSC)) &&
       LHSC->bitwiseIsEqual(neg(*RHSC))) {
     auto IsLessThanOrLessEqual = [](FCmpInst::Predicate Pred) {
-      return (getFCmpCode(Pred) & 0b0110) == 0b0100;
+      switch (Pred) {
+      case FCmpInst::FCMP_OLT:
+      case FCmpInst::FCMP_OLE:
+      case FCmpInst::FCMP_ULT:
+      case FCmpInst::FCMP_ULE:
+        return true;
+      default:
+        return false;
+      }
     };
     if (IsLessThanOrLessEqual(IsAnd ? PredR : PredL)) {
       std::swap(LHSC, RHSC);
@@ -1441,7 +1449,7 @@ Value *InstCombinerImpl::foldLogicOfFCmps(FCmpInst *LHS, FCmpInst *RHS,
     if (IsLessThanOrLessEqual(IsAnd ? PredL : PredR)) {
       BuilderTy::FastMathFlagGuard Guard(Builder);
       FastMathFlags FMF = LHS->getFastMathFlags();
-      FMF &= RHS->getFastMathFlags();
+      FMF |= RHS->getFastMathFlags();
       Builder.setFastMathFlags(FMF);
 
       Value *FAbs = Builder.CreateUnaryIntrinsic(Intrinsic::fabs, LHS0);
diff --git a/llvm/test/Transforms/InstCombine/fcmp-range-check-idiom.ll b/llvm/test/Transforms/InstCombine/fcmp-range-check-idiom.ll
index 67ea0c89fb2ffe..b0f97ecf4f6f74 100644
--- a/llvm/test/Transforms/InstCombine/fcmp-range-check-idiom.ll
+++ b/llvm/test/Transforms/InstCombine/fcmp-range-check-idiom.ll
@@ -15,6 +15,7 @@ define i1 @test_and_olt(float %x) {
   %cond = and i1 %cmp1, %cmp2
   ret i1 %cond
 }
+
 define i1 @test_and_ole(float %x) {
 ; CHECK-LABEL: define i1 @test_and_ole(
 ; CHECK-SAME: float [[X:%.*]]) {
@@ -27,6 +28,7 @@ define i1 @test_and_ole(float %x) {
   %cond = and i1 %cmp1, %cmp2
   ret i1 %cond
 }
+
 define i1 @test_or_ogt(float %x) {
 ; CHECK-LABEL: define i1 @test_or_ogt(
 ; CHECK-SAME: float [[X:%.*]]) {
@@ -39,6 +41,7 @@ define i1 @test_or_ogt(float %x) {
   %cond = or i1 %cmp1, %cmp2
   ret i1 %cond
 }
+
 define i1 @test_or_oge(float %x) {
 ; CHECK-LABEL: define i1 @test_or_oge(
 ; CHECK-SAME: float [[X:%.*]]) {
@@ -51,6 +54,7 @@ define i1 @test_or_oge(float %x) {
   %cond = or i1 %cmp1, %cmp2
   ret i1 %cond
 }
+
 define i1 @test_and_ult(float %x) {
 ; CHECK-LABEL: define i1 @test_and_ult(
 ; CHECK-SAME: float [[X:%.*]]) {
@@ -63,6 +67,7 @@ define i1 @test_and_ult(float %x) {
   %cond = and i1 %cmp1, %cmp2
   ret i1 %cond
 }
+
 define i1 @test_and_ule(float %x) {
 ; CHECK-LABEL: define i1 @test_and_ule(
 ; CHECK-SAME: float [[X:%.*]]) {
@@ -75,6 +80,7 @@ define i1 @test_and_ule(float %x) {
   %cond = and i1 %cmp1, %cmp2
   ret i1 %cond
 }
+
 define i1 @test_or_ugt(float %x) {
 ; CHECK-LABEL: define i1 @test_or_ugt(
 ; CHECK-SAME: float [[X:%.*]]) {
@@ -87,6 +93,7 @@ define i1 @test_or_ugt(float %x) {
   %cond = or i1 %cmp1, %cmp2
   ret i1 %cond
 }
+
 define i1 @test_or_uge(float %x) {
 ; CHECK-LABEL: define i1 @test_or_uge(
 ; CHECK-SAME: float [[X:%.*]]) {
@@ -99,6 +106,7 @@ define i1 @test_or_uge(float %x) {
   %cond = or i1 %cmp1, %cmp2
   ret i1 %cond
 }
+
 define i1 @test_and_olt_commuted(float %x) {
 ; CHECK-LABEL: define i1 @test_and_olt_commuted(
 ; CHECK-SAME: float [[X:%.*]]) {
@@ -111,6 +119,7 @@ define i1 @test_and_olt_commuted(float %x) {
   %cond = and i1 %cmp2, %cmp1
   ret i1 %cond
 }
+
 define i1 @test_and_olt_subnormal(float %x) {
 ; CHECK-LABEL: define i1 @test_and_olt_subnormal(
 ; CHECK-SAME: float [[X:%.*]]) {
@@ -123,6 +132,7 @@ define i1 @test_and_olt_subnormal(float %x) {
   %cond = and i1 %cmp1, %cmp2
   ret i1 %cond
 }
+
 define i1 @test_and_olt_infinity(float %x) {
 ; CHECK-LABEL: define i1 @test_and_olt_infinity(
 ; CHECK-SAME: float [[X:%.*]]) {
@@ -135,6 +145,7 @@ define i1 @test_and_olt_infinity(float %x) {
   %cond = and i1 %cmp1, %cmp2
   ret i1 %cond
 }
+
 define i1 @test_and_olt_zero(float %x) {
 ; CHECK-LABEL: define i1 @test_and_olt_zero(
 ; CHECK-SAME: float [[X:%.*]]) {
@@ -145,6 +156,7 @@ define i1 @test_and_olt_zero(float %x) {
   %cond = and i1 %cmp1, %cmp2
   ret i1 %cond
 }
+
 define i1 @test_and_ole_zero(float %x) {
 ; CHECK-LABEL: define i1 @test_and_ole_zero(
 ; CHECK-SAME: float [[X:%.*]]) {
@@ -156,6 +168,7 @@ define i1 @test_and_ole_zero(float %x) {
   %cond = and i1 %cmp1, %cmp2
   ret i1 %cond
 }
+
 define i1 @test_and_olt_logical(float %x) {
 ; CHECK-LABEL: define i1 @test_and_olt_logical(
 ; CHECK-SAME: float [[X:%.*]]) {
@@ -168,6 +181,7 @@ define i1 @test_and_olt_logical(float %x) {
   %cond = select i1 %cmp1, i1 %cmp2, i1 false
   ret i1 %cond
 }
+
 define <2 x i1> @test_and_olt_undef(<2 x float> %x) {
 ; CHECK-LABEL: define <2 x i1> @test_and_olt_undef(
 ; CHECK-SAME: <2 x float> [[X:%.*]]) {
@@ -180,6 +194,7 @@ define <2 x i1> @test_and_olt_undef(<2 x float> %x) {
   %cond = and <2 x i1> %cmp1, %cmp2
   ret <2 x i1> %cond
 }
+
 define i1 @test_and_olt_nan(float %x) {
 ; CHECK-LABEL: define i1 @test_and_olt_nan(
 ; CHECK-SAME: float [[X:%.*]]) {
@@ -190,6 +205,7 @@ define i1 @test_and_olt_nan(float %x) {
   %cond = and i1 %cmp1, %cmp2
   ret i1 %cond
 }
+
 define i1 @test_and_ogt(float %x) {
 ; CHECK-LABEL: define i1 @test_and_ogt(
 ; CHECK-SAME: float [[X:%.*]]) {
@@ -200,6 +216,7 @@ define i1 @test_and_ogt(float %x) {
   %cond = and i1 %cmp1, %cmp2
   ret i1 %cond
 }
+
 define i1 @test_or_olt(float %x) {
 ; CHECK-LABEL: define i1 @test_or_olt(
 ; CHECK-SAME: float [[X:%.*]]) {
@@ -212,7 +229,9 @@ define i1 @test_or_olt(float %x) {
   %cond = or i1 %cmp1, %cmp2
   ret i1 %cond
 }
+
 ; Negative tests
+
 define i1 @test_and_olt_multiuse(float %x) {
 ; CHECK-LABEL: define i1 @test_and_olt_multiuse(
 ; CHECK-SAME: float [[X:%.*]]) {
@@ -228,6 +247,7 @@ define i1 @test_and_olt_multiuse(float %x) {
   %cond = and i1 %cmp1, %cmp2
   ret i1 %cond
 }
+
 define i1 @test_and_olt_mismatched_lhs(float %x, float %y) {
 ; CHECK-LABEL: define i1 @test_and_olt_mismatched_lhs(
 ; CHECK-SAME: float [[X:%.*]], float [[Y:%.*]]) {
@@ -241,6 +261,7 @@ define i1 @test_and_olt_mismatched_lhs(float %x, float %y) {
   %cond = and i1 %cmp1, %cmp2
   ret i1 %cond
 }
+
 define i1 @test_and_olt_same_sign(float %x) {
 ; CHECK-LABEL: define i1 @test_and_olt_same_sign(
 ; CHECK-SAME: float [[X:%.*]]) {
@@ -251,6 +272,7 @@ define i1 @test_and_olt_same_sign(float %x) {
   %cond = and i1 %cmp1, %cmp2
   ret i1 %cond
 }
+
 define i1 @test_and_olt_mismatched_mag(float %x) {
 ; CHECK-LABEL: define i1 @test_and_olt_mismatched_mag(
 ; CHECK-SAME: float [[X:%.*]]) {
@@ -264,6 +286,7 @@ define i1 @test_and_olt_mismatched_mag(float %x) {
   %cond = and i1 %cmp1, %cmp2
   ret i1 %cond
 }
+
 define i1 @test_and_olt_wrong_pred2(float %x) {
 ; CHECK-LABEL: define i1 @test_and_olt_wrong_pred2(
 ; CHECK-SAME: float [[X:%.*]]) {
@@ -290,3 +313,16 @@ define i1 @test_and_olt_fmf_propagation(float %x) {
   %cond = and i1 %cmp1, %cmp2
   ret i1 %cond
 }
+
+define i1 @test_and_olt_fmf_propagation_union(float %x) {
+; CHECK-LABEL: define i1 @test_and_olt_fmf_propagation_union(
+; CHECK-SAME: float [[X:%.*]]) {
+; CHECK-NEXT:    [[TMP1:%.*]] = call nnan ninf nsz float @llvm.fabs.f32(float [[X]])
+; CHECK-NEXT:    [[COND:%.*]] = fcmp nnan ninf nsz olt float [[TMP1]], 0x3C00000000000000
+; CHECK-NEXT:    ret i1 [[COND]]
+;
+  %cmp1 = fcmp nnan ninf olt float %x, 0x3C00000000000000
+  %cmp2 = fcmp nsz nnan ogt float %x, 0xBC00000000000000
+  %cond = and i1 %cmp1, %cmp2
+  ret i1 %cond
+}

>From 11881d57d7567dfe72fc58627b09a410b7d58738 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Tue, 6 Feb 2024 01:59:11 +0800
Subject: [PATCH 5/5] fixup! [InstCombine] Canonicalize the fcmp range check
 idiom into fabs + fcmp

Add more NaN tests.
---
 .../InstCombine/InstCombineAndOrXor.cpp       |  5 ++-
 .../InstCombine/fcmp-range-check-idiom.ll     | 33 +++++++++++++++++++
 2 files changed, 35 insertions(+), 3 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
index 5635a2056f0670..64891c6abf9526 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
@@ -1448,9 +1448,8 @@ Value *InstCombinerImpl::foldLogicOfFCmps(FCmpInst *LHS, FCmpInst *RHS,
     }
     if (IsLessThanOrLessEqual(IsAnd ? PredL : PredR)) {
       BuilderTy::FastMathFlagGuard Guard(Builder);
-      FastMathFlags FMF = LHS->getFastMathFlags();
-      FMF |= RHS->getFastMathFlags();
-      Builder.setFastMathFlags(FMF);
+      Builder.setFastMathFlags(LHS->getFastMathFlags() |
+                               RHS->getFastMathFlags());
 
       Value *FAbs = Builder.CreateUnaryIntrinsic(Intrinsic::fabs, LHS0);
       return Builder.CreateFCmp(PredL, FAbs,
diff --git a/llvm/test/Transforms/InstCombine/fcmp-range-check-idiom.ll b/llvm/test/Transforms/InstCombine/fcmp-range-check-idiom.ll
index b0f97ecf4f6f74..0893b27f5cf49f 100644
--- a/llvm/test/Transforms/InstCombine/fcmp-range-check-idiom.ll
+++ b/llvm/test/Transforms/InstCombine/fcmp-range-check-idiom.ll
@@ -206,6 +206,39 @@ define i1 @test_and_olt_nan(float %x) {
   ret i1 %cond
 }
 
+define i1 @test_and_ult_nan(float %x) {
+; CHECK-LABEL: define i1 @test_and_ult_nan(
+; CHECK-SAME: float [[X:%.*]]) {
+; CHECK-NEXT:    ret i1 true
+;
+  %cmp1 = fcmp ult float %x, 0x7FF0000020000000
+  %cmp2 = fcmp ugt float %x, 0xFFF0000020000000
+  %cond = and i1 %cmp1, %cmp2
+  ret i1 %cond
+}
+
+define i1 @test_or_ogt_nan(float %x) {
+; CHECK-LABEL: define i1 @test_or_ogt_nan(
+; CHECK-SAME: float [[X:%.*]]) {
+; CHECK-NEXT:    ret i1 false
+;
+  %cmp1 = fcmp ogt float %x, 0x7FF0000020000000
+  %cmp2 = fcmp olt float %x, 0xFFF0000020000000
+  %cond = or i1 %cmp1, %cmp2
+  ret i1 %cond
+}
+
+define i1 @test_or_ugt_nan(float %x) {
+; CHECK-LABEL: define i1 @test_or_ugt_nan(
+; CHECK-SAME: float [[X:%.*]]) {
+; CHECK-NEXT:    ret i1 true
+;
+  %cmp1 = fcmp ugt float %x, 0x7FF0000020000000
+  %cmp2 = fcmp ult float %x, 0xFFF0000020000000
+  %cond = or i1 %cmp1, %cmp2
+  ret i1 %cond
+}
+
 define i1 @test_and_ogt(float %x) {
 ; CHECK-LABEL: define i1 @test_and_ogt(
 ; CHECK-SAME: float [[X:%.*]]) {



More information about the llvm-commits mailing list