[llvm] [InstCombine] Handle isNanOrInf idioms (PR #80414)

Yingwei Zheng via llvm-commits llvm-commits at lists.llvm.org
Fri Feb 2 21:07:29 PST 2024


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

>From 00d27cb9f41cfad57a77a90d51ecc0a868235b91 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Fri, 2 Feb 2024 18:16:47 +0800
Subject: [PATCH 1/3] [InstCombine] Add pre-commit tests. NFC.

---
 .../InstCombine/fpclass-check-idioms.ll       | 181 ++++++++++++++++++
 1 file changed, 181 insertions(+)
 create mode 100644 llvm/test/Transforms/InstCombine/fpclass-check-idioms.ll

diff --git a/llvm/test/Transforms/InstCombine/fpclass-check-idioms.ll b/llvm/test/Transforms/InstCombine/fpclass-check-idioms.ll
new file mode 100644
index 0000000000000..06e60af336377
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/fpclass-check-idioms.ll
@@ -0,0 +1,181 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4
+; RUN: opt -S -passes=instcombine < %s | FileCheck %s
+
+define i1 @f32_fcnan_fcinf(float %a) {
+; CHECK-LABEL: define i1 @f32_fcnan_fcinf(
+; CHECK-SAME: float [[A:%.*]]) {
+; CHECK-NEXT:    [[I32:%.*]] = bitcast float [[A]] to i32
+; CHECK-NEXT:    [[AND:%.*]] = and i32 [[I32]], 2139095040
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[AND]], 2139095040
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %i32 = bitcast float %a to i32
+  %and = and i32 %i32, 2139095040
+  %cmp = icmp eq i32 %and, 2139095040
+  ret i1 %cmp
+}
+
+define i1 @f32_not_fcnan_fcinf(float %a) {
+; CHECK-LABEL: define i1 @f32_not_fcnan_fcinf(
+; CHECK-SAME: float [[A:%.*]]) {
+; CHECK-NEXT:    [[I32:%.*]] = bitcast float [[A]] to i32
+; CHECK-NEXT:    [[AND:%.*]] = and i32 [[I32]], 2139095040
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ne i32 [[AND]], 2139095040
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %i32 = bitcast float %a to i32
+  %and = and i32 %i32, 2139095040
+  %cmp = icmp ne i32 %and, 2139095040
+  ret i1 %cmp
+}
+
+define i1 @f64_fcnan_fcinf(double %a) {
+; CHECK-LABEL: define i1 @f64_fcnan_fcinf(
+; CHECK-SAME: double [[A:%.*]]) {
+; CHECK-NEXT:    [[I64:%.*]] = bitcast double [[A]] to i64
+; CHECK-NEXT:    [[AND:%.*]] = and i64 [[I64]], 9218868437227405312
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i64 [[AND]], 9218868437227405312
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %i64 = bitcast double %a to i64
+  %and = and i64 %i64, 9218868437227405312
+  %cmp = icmp eq i64 %and, 9218868437227405312
+  ret i1 %cmp
+}
+
+; TODO: handle more fpclass check idioms
+define i1 @f32_fcinf(float %a) {
+; CHECK-LABEL: define i1 @f32_fcinf(
+; CHECK-SAME: float [[A:%.*]]) {
+; CHECK-NEXT:    [[TMP1:%.*]] = call float @llvm.fabs.f32(float [[A]])
+; CHECK-NEXT:    [[AND:%.*]] = bitcast float [[TMP1]] to i32
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[AND]], 2139095040
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %i32 = bitcast float %a to i32
+  %and = and i32 %i32, 2147483647
+  %cmp = icmp eq i32 %and, 2139095040
+  ret i1 %cmp
+}
+
+define i1 @f32_fcnan(float %a) {
+; CHECK-LABEL: define i1 @f32_fcnan(
+; CHECK-SAME: float [[A:%.*]]) {
+; CHECK-NEXT:    [[I32:%.*]] = bitcast float [[A]] to i32
+; CHECK-NEXT:    [[AND1:%.*]] = and i32 [[I32]], 2139095040
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i32 [[AND1]], 2139095040
+; CHECK-NEXT:    [[AND2:%.*]] = and i32 [[I32]], 8388607
+; CHECK-NEXT:    [[CMP2:%.*]] = icmp ne i32 [[AND2]], 0
+; CHECK-NEXT:    [[RES:%.*]] = and i1 [[CMP1]], [[CMP2]]
+; CHECK-NEXT:    ret i1 [[RES]]
+;
+  %i32 = bitcast float %a to i32
+  %and1 = and i32 %i32, 2139095040
+  %cmp1 = icmp eq i32 %and1, 2139095040
+  %and2 = and i32 %i32, 8388607
+  %cmp2 = icmp ne i32 %and2, 0
+  %res = and i1 %cmp1, %cmp2
+  ret i1 %res
+}
+
+; Negative tests
+
+define i1 @f32_fcnan_fcinf_wrong_mask1(float %a) {
+; CHECK-LABEL: define i1 @f32_fcnan_fcinf_wrong_mask1(
+; CHECK-SAME: float [[A:%.*]]) {
+; CHECK-NEXT:    [[I32:%.*]] = bitcast float [[A]] to i32
+; CHECK-NEXT:    [[AND:%.*]] = and i32 [[I32]], 2139095041
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[AND]], 2139095040
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %i32 = bitcast float %a to i32
+  %and = and i32 %i32, 2139095041
+  %cmp = icmp eq i32 %and, 2139095040
+  ret i1 %cmp
+}
+
+define i1 @f32_fcnan_fcinf_wrong_mask2(float %a) {
+; CHECK-LABEL: define i1 @f32_fcnan_fcinf_wrong_mask2(
+; CHECK-SAME: float [[A:%.*]]) {
+; CHECK-NEXT:    [[I32:%.*]] = bitcast float [[A]] to i32
+; CHECK-NEXT:    [[AND:%.*]] = and i32 [[I32]], 2139095040
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[AND]], 2130706432
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %i32 = bitcast float %a to i32
+  %and = and i32 %i32, 2139095040
+  %cmp = icmp eq i32 %and, 2130706432
+  ret i1 %cmp
+}
+
+define i1 @f64_fcnan_fcinf_wrong_mask3(double %a) {
+; CHECK-LABEL: define i1 @f64_fcnan_fcinf_wrong_mask3(
+; CHECK-SAME: double [[A:%.*]]) {
+; CHECK-NEXT:    [[I64:%.*]] = bitcast double [[A]] to i64
+; CHECK-NEXT:    [[AND:%.*]] = and i64 [[I64]], 2139095040
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i64 [[AND]], 2139095040
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %i64 = bitcast double %a to i64
+  %and = and i64 %i64, 2139095040
+  %cmp = icmp eq i64 %and, 2139095040
+  ret i1 %cmp
+}
+
+define i1 @f32_fcnan_fcinf_wrong_pred(float %a) {
+; CHECK-LABEL: define i1 @f32_fcnan_fcinf_wrong_pred(
+; CHECK-SAME: float [[A:%.*]]) {
+; CHECK-NEXT:    [[I32:%.*]] = bitcast float [[A]] to i32
+; CHECK-NEXT:    [[AND:%.*]] = and i32 [[I32]], 2139095040
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ne i32 [[AND]], 2139095040
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %i32 = bitcast float %a to i32
+  %and = and i32 %i32, 2139095040
+  %cmp = icmp slt i32 %and, 2139095040
+  ret i1 %cmp
+}
+
+define i1 @f32_fcnan_fcinf_wrong_type1(<2 x float> %a) {
+; CHECK-LABEL: define i1 @f32_fcnan_fcinf_wrong_type1(
+; CHECK-SAME: <2 x float> [[A:%.*]]) {
+; CHECK-NEXT:    [[I64:%.*]] = bitcast <2 x float> [[A]] to i64
+; CHECK-NEXT:    [[AND:%.*]] = and i64 [[I64]], 2139095040
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i64 [[AND]], 2139095040
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %i64 = bitcast <2 x float> %a to i64
+  %and = and i64 %i64, 2139095040
+  %cmp = icmp eq i64 %and, 2139095040
+  ret i1 %cmp
+}
+
+define i1 @f32_fcnan_fcinf_wrong_type2(x86_fp80 %a) {
+; CHECK-LABEL: define i1 @f32_fcnan_fcinf_wrong_type2(
+; CHECK-SAME: x86_fp80 [[A:%.*]]) {
+; CHECK-NEXT:    [[I80:%.*]] = bitcast x86_fp80 [[A]] to i80
+; CHECK-NEXT:    [[AND:%.*]] = and i80 [[I80]], 2139095040
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i80 [[AND]], 2139095040
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %i80 = bitcast x86_fp80 %a to i80
+  %and = and i80 %i80, 2139095040
+  %cmp = icmp eq i80 %and, 2139095040
+  ret i1 %cmp
+}
+
+define i1 @f32_fcnan_fcinf_noimplicitfloat(float %a) #0 {
+; CHECK-LABEL: define i1 @f32_fcnan_fcinf_noimplicitfloat(
+; CHECK-SAME: float [[A:%.*]]) #[[ATTR0:[0-9]+]] {
+; CHECK-NEXT:    [[I32:%.*]] = bitcast float [[A]] to i32
+; CHECK-NEXT:    [[AND:%.*]] = and i32 [[I32]], 2139095040
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[AND]], 2139095040
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %i32 = bitcast float %a to i32
+  %and = and i32 %i32, 2139095040
+  %cmp = icmp eq i32 %and, 2139095040
+  ret i1 %cmp
+}
+
+attributes #0 = { noimplicitfloat }

>From fe7f045de7208e988c400f23206fe5103b60378f Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Fri, 2 Feb 2024 18:18:56 +0800
Subject: [PATCH 2/3] [InstCombine] Handle isNanOrInf idioms

---
 .../InstCombine/InstCombineCompares.cpp       | 29 +++++++++++++++++++
 .../InstCombine/fpclass-check-idioms.ll       | 20 +++++--------
 2 files changed, 37 insertions(+), 12 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index d295853798b80..0fe75309296b4 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -1827,6 +1827,35 @@ Instruction *InstCombinerImpl::foldICmpAndConstConst(ICmpInst &Cmp,
     }
   }
 
+  // (icmp eq (and (bitcast X to int), ExponentMask), ExponentMask) -->
+  // llvm.is.fpclass(X, fcInf|fcNan)
+  // (icmp ne (and (bitcast X to int), ExponentMask), ExponentMask) -->
+  // llvm.is.fpclass(X, ~(fcInf|fcNan))
+  Value *V;
+  if (!Cmp.getParent()->getParent()->hasFnAttribute(
+          Attribute::NoImplicitFloat) &&
+      Cmp.isEquality() && match(X, m_OneUse(m_BitCast(m_Value(V))))) {
+    Type *SrcType = V->getType();
+    Type *DstType = X->getType();
+    // Make sure the bitcast doesn't change between scalar and vector and
+    // doesn't change the number of vector elements.
+    if (SrcType->isVectorTy() == DstType->isVectorTy() &&
+        SrcType->getScalarSizeInBits() == DstType->getScalarSizeInBits()) {
+      Type *FPType = SrcType->getScalarType();
+      if (FPType->isIEEELikeFPTy() && C1 == *C2) {
+        APInt ExponentMask = APInt::getBitsSet(
+            FPType->getScalarSizeInBits(), FPType->getFPMantissaWidth() - 1,
+            FPType->getScalarSizeInBits() - 1);
+        if (C1 == ExponentMask) {
+          unsigned Mask = FPClassTest::fcNan | FPClassTest::fcInf;
+          if (isICMP_NE)
+            Mask = ~Mask & fcAllFlags;
+          return replaceInstUsesWith(Cmp, Builder.createIsFPClass(V, Mask));
+        }
+      }
+    }
+  }
+
   return nullptr;
 }
 
diff --git a/llvm/test/Transforms/InstCombine/fpclass-check-idioms.ll b/llvm/test/Transforms/InstCombine/fpclass-check-idioms.ll
index 06e60af336377..dd4a3e0afb50a 100644
--- a/llvm/test/Transforms/InstCombine/fpclass-check-idioms.ll
+++ b/llvm/test/Transforms/InstCombine/fpclass-check-idioms.ll
@@ -4,9 +4,8 @@
 define i1 @f32_fcnan_fcinf(float %a) {
 ; CHECK-LABEL: define i1 @f32_fcnan_fcinf(
 ; CHECK-SAME: float [[A:%.*]]) {
-; CHECK-NEXT:    [[I32:%.*]] = bitcast float [[A]] to i32
-; CHECK-NEXT:    [[AND:%.*]] = and i32 [[I32]], 2139095040
-; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[AND]], 2139095040
+; CHECK-NEXT:    [[TMP1:%.*]] = call float @llvm.fabs.f32(float [[A]])
+; CHECK-NEXT:    [[CMP:%.*]] = fcmp ueq float [[TMP1]], 0x7FF0000000000000
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ;
   %i32 = bitcast float %a to i32
@@ -18,9 +17,8 @@ define i1 @f32_fcnan_fcinf(float %a) {
 define i1 @f32_not_fcnan_fcinf(float %a) {
 ; CHECK-LABEL: define i1 @f32_not_fcnan_fcinf(
 ; CHECK-SAME: float [[A:%.*]]) {
-; CHECK-NEXT:    [[I32:%.*]] = bitcast float [[A]] to i32
-; CHECK-NEXT:    [[AND:%.*]] = and i32 [[I32]], 2139095040
-; CHECK-NEXT:    [[CMP:%.*]] = icmp ne i32 [[AND]], 2139095040
+; CHECK-NEXT:    [[TMP1:%.*]] = call float @llvm.fabs.f32(float [[A]])
+; CHECK-NEXT:    [[CMP:%.*]] = fcmp one float [[TMP1]], 0x7FF0000000000000
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ;
   %i32 = bitcast float %a to i32
@@ -32,9 +30,8 @@ define i1 @f32_not_fcnan_fcinf(float %a) {
 define i1 @f64_fcnan_fcinf(double %a) {
 ; CHECK-LABEL: define i1 @f64_fcnan_fcinf(
 ; CHECK-SAME: double [[A:%.*]]) {
-; CHECK-NEXT:    [[I64:%.*]] = bitcast double [[A]] to i64
-; CHECK-NEXT:    [[AND:%.*]] = and i64 [[I64]], 9218868437227405312
-; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i64 [[AND]], 9218868437227405312
+; CHECK-NEXT:    [[TMP1:%.*]] = call double @llvm.fabs.f64(double [[A]])
+; CHECK-NEXT:    [[CMP:%.*]] = fcmp ueq double [[TMP1]], 0x7FF0000000000000
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ;
   %i64 = bitcast double %a to i64
@@ -125,9 +122,8 @@ define i1 @f64_fcnan_fcinf_wrong_mask3(double %a) {
 define i1 @f32_fcnan_fcinf_wrong_pred(float %a) {
 ; CHECK-LABEL: define i1 @f32_fcnan_fcinf_wrong_pred(
 ; CHECK-SAME: float [[A:%.*]]) {
-; CHECK-NEXT:    [[I32:%.*]] = bitcast float [[A]] to i32
-; CHECK-NEXT:    [[AND:%.*]] = and i32 [[I32]], 2139095040
-; CHECK-NEXT:    [[CMP:%.*]] = icmp ne i32 [[AND]], 2139095040
+; CHECK-NEXT:    [[TMP1:%.*]] = call float @llvm.fabs.f32(float [[A]])
+; CHECK-NEXT:    [[CMP:%.*]] = fcmp one float [[TMP1]], 0x7FF0000000000000
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ;
   %i32 = bitcast float %a to i32

>From 1fee81183893e5cc88274b0b3469b3f057a4e87c Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Sat, 3 Feb 2024 12:59:24 +0800
Subject: [PATCH 3/3] fixup! [InstCombine] Handle isNanOrInf idioms

Address review comments.
---
 .../InstCombine/InstCombineCompares.cpp       | 22 +++++++--------
 .../InstCombine/fpclass-check-idioms.ll       | 28 ++++++++++++++++++-
 2 files changed, 37 insertions(+), 13 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index 0fe75309296b4..15ec3fd2ceca0 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -1837,21 +1837,19 @@ Instruction *InstCombinerImpl::foldICmpAndConstConst(ICmpInst &Cmp,
       Cmp.isEquality() && match(X, m_OneUse(m_BitCast(m_Value(V))))) {
     Type *SrcType = V->getType();
     Type *DstType = X->getType();
+    Type *FPType = SrcType->getScalarType();
     // Make sure the bitcast doesn't change between scalar and vector and
     // doesn't change the number of vector elements.
     if (SrcType->isVectorTy() == DstType->isVectorTy() &&
-        SrcType->getScalarSizeInBits() == DstType->getScalarSizeInBits()) {
-      Type *FPType = SrcType->getScalarType();
-      if (FPType->isIEEELikeFPTy() && C1 == *C2) {
-        APInt ExponentMask = APInt::getBitsSet(
-            FPType->getScalarSizeInBits(), FPType->getFPMantissaWidth() - 1,
-            FPType->getScalarSizeInBits() - 1);
-        if (C1 == ExponentMask) {
-          unsigned Mask = FPClassTest::fcNan | FPClassTest::fcInf;
-          if (isICMP_NE)
-            Mask = ~Mask & fcAllFlags;
-          return replaceInstUsesWith(Cmp, Builder.createIsFPClass(V, Mask));
-        }
+        SrcType->getScalarSizeInBits() == DstType->getScalarSizeInBits() &&
+        FPType->isIEEELikeFPTy() && C1 == *C2) {
+      APInt ExponentMask =
+          APFloat::getInf(FPType->getFltSemantics()).bitcastToAPInt();
+      if (C1 == ExponentMask) {
+        unsigned Mask = FPClassTest::fcNan | FPClassTest::fcInf;
+        if (isICMP_NE)
+          Mask = ~Mask & fcAllFlags;
+        return replaceInstUsesWith(Cmp, Builder.createIsFPClass(V, Mask));
       }
     }
   }
diff --git a/llvm/test/Transforms/InstCombine/fpclass-check-idioms.ll b/llvm/test/Transforms/InstCombine/fpclass-check-idioms.ll
index dd4a3e0afb50a..019db343c61cc 100644
--- a/llvm/test/Transforms/InstCombine/fpclass-check-idioms.ll
+++ b/llvm/test/Transforms/InstCombine/fpclass-check-idioms.ll
@@ -75,6 +75,32 @@ define i1 @f32_fcnan(float %a) {
   ret i1 %res
 }
 
+define i1 @f32_fcnan_fcinf_strictfp(float %a) strictfp {
+; CHECK-LABEL: define i1 @f32_fcnan_fcinf_strictfp(
+; CHECK-SAME: float [[A:%.*]]) #[[ATTR0:[0-9]+]] {
+; CHECK-NEXT:    [[TMP1:%.*]] = call float @llvm.fabs.f32(float [[A]])
+; CHECK-NEXT:    [[CMP:%.*]] = fcmp ueq float [[TMP1]], 0x7FF0000000000000
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %i32 = bitcast float %a to i32
+  %and = and i32 %i32, 2139095040
+  %cmp = icmp eq i32 %and, 2139095040
+  ret i1 %cmp
+}
+
+define <2 x i1> @f32_fcnan_fcinf_vec(<2 x float> %a) {
+; CHECK-LABEL: define <2 x i1> @f32_fcnan_fcinf_vec(
+; CHECK-SAME: <2 x float> [[A:%.*]]) {
+; CHECK-NEXT:    [[TMP1:%.*]] = call <2 x float> @llvm.fabs.v2f32(<2 x float> [[A]])
+; CHECK-NEXT:    [[CMP:%.*]] = fcmp ueq <2 x float> [[TMP1]], <float 0x7FF0000000000000, float 0x7FF0000000000000>
+; CHECK-NEXT:    ret <2 x i1> [[CMP]]
+;
+  %i32 = bitcast <2 x float> %a to <2 x i32>
+  %and = and <2 x i32> %i32, <i32 2139095040, i32 2139095040>
+  %cmp = icmp eq <2 x i32> %and, <i32 2139095040, i32 2139095040>
+  ret <2 x i1> %cmp
+}
+
 ; Negative tests
 
 define i1 @f32_fcnan_fcinf_wrong_mask1(float %a) {
@@ -162,7 +188,7 @@ define i1 @f32_fcnan_fcinf_wrong_type2(x86_fp80 %a) {
 
 define i1 @f32_fcnan_fcinf_noimplicitfloat(float %a) #0 {
 ; CHECK-LABEL: define i1 @f32_fcnan_fcinf_noimplicitfloat(
-; CHECK-SAME: float [[A:%.*]]) #[[ATTR0:[0-9]+]] {
+; CHECK-SAME: float [[A:%.*]]) #[[ATTR1:[0-9]+]] {
 ; CHECK-NEXT:    [[I32:%.*]] = bitcast float [[A]] to i32
 ; CHECK-NEXT:    [[AND:%.*]] = and i32 [[I32]], 2139095040
 ; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[AND]], 2139095040



More information about the llvm-commits mailing list