[llvm] [InstCombine] Fold isnan idioms (PR #101510)

Yingwei Zheng via llvm-commits llvm-commits at lists.llvm.org
Thu Aug 1 11:16:30 PDT 2024


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

>From 87777a5e9b67bb5817a891b70fa8a15c91e80f44 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Fri, 2 Aug 2024 00:04:37 +0800
Subject: [PATCH 1/3] [InstCombine] Add pre-commit tests. NFC.

---
 .../InstCombine/fpclass-check-idioms.ll       | 235 ++++++++++++++++++
 1 file changed, 235 insertions(+)

diff --git a/llvm/test/Transforms/InstCombine/fpclass-check-idioms.ll b/llvm/test/Transforms/InstCombine/fpclass-check-idioms.ll
index 42c6506e34cf4..e2b214884ea1c 100644
--- a/llvm/test/Transforms/InstCombine/fpclass-check-idioms.ll
+++ b/llvm/test/Transforms/InstCombine/fpclass-check-idioms.ll
@@ -647,6 +647,241 @@ define i1 @f32_fcposinf_multiuse_strictfp(float %a) strictfp {
   ret i1 %cmp
 }
 
+define i1 @isnan_idiom(double %x) {
+; CHECK-LABEL: define i1 @isnan_idiom(
+; CHECK-SAME: double [[X:%.*]]) {
+; CHECK-NEXT:    [[BITS:%.*]] = bitcast double [[X]] to i64
+; CHECK-NEXT:    [[MASK1:%.*]] = and i64 [[BITS]], 9218868437227405312
+; CHECK-NEXT:    [[COND1:%.*]] = icmp eq i64 [[MASK1]], 9218868437227405312
+; CHECK-NEXT:    [[MASK2:%.*]] = and i64 [[BITS]], 4503599627370495
+; CHECK-NEXT:    [[COND2:%.*]] = icmp ne i64 [[MASK2]], 0
+; CHECK-NEXT:    [[RET:%.*]] = and i1 [[COND1]], [[COND2]]
+; CHECK-NEXT:    ret i1 [[RET]]
+;
+  %bits = bitcast double %x to i64
+  %mask1 = and i64 %bits, 9218868437227405312
+  %cond1 = icmp eq i64 %mask1, 9218868437227405312
+  %mask2 = and i64 %bits, 4503599627370495
+  %cond2 = icmp ne i64 %mask2, 0
+  %ret = and i1 %cond1, %cond2
+  ret i1 %ret
+}
+
+define <2 x i1> @isnan_idiom_vec(<2 x double> %x) {
+; CHECK-LABEL: define <2 x i1> @isnan_idiom_vec(
+; CHECK-SAME: <2 x double> [[X:%.*]]) {
+; CHECK-NEXT:    [[BITS:%.*]] = bitcast <2 x double> [[X]] to <2 x i64>
+; CHECK-NEXT:    [[MASK1:%.*]] = and <2 x i64> [[BITS]], <i64 9218868437227405312, i64 9218868437227405312>
+; CHECK-NEXT:    [[COND1:%.*]] = icmp eq <2 x i64> [[MASK1]], <i64 9218868437227405312, i64 9218868437227405312>
+; CHECK-NEXT:    [[MASK2:%.*]] = and <2 x i64> [[BITS]], <i64 4503599627370495, i64 4503599627370495>
+; CHECK-NEXT:    [[COND2:%.*]] = icmp ne <2 x i64> [[MASK2]], zeroinitializer
+; CHECK-NEXT:    [[RET:%.*]] = and <2 x i1> [[COND1]], [[COND2]]
+; CHECK-NEXT:    ret <2 x i1> [[RET]]
+;
+  %bits = bitcast <2 x double> %x to <2 x i64>
+  %mask1 = and <2 x i64> %bits, splat(i64 9218868437227405312)
+  %cond1 = icmp eq <2 x i64> %mask1, splat(i64 9218868437227405312)
+  %mask2 = and <2 x i64> %bits, splat(i64 4503599627370495)
+  %cond2 = icmp ne <2 x i64> %mask2, zeroinitializer
+  %ret = and <2 x i1> %cond1, %cond2
+  ret <2 x i1> %ret
+}
+
+define i1 @isnan_idiom_commuted(double %x) {
+; CHECK-LABEL: define i1 @isnan_idiom_commuted(
+; CHECK-SAME: double [[X:%.*]]) {
+; CHECK-NEXT:    [[BITS:%.*]] = bitcast double [[X]] to i64
+; CHECK-NEXT:    [[MASK1:%.*]] = and i64 [[BITS]], 9218868437227405312
+; CHECK-NEXT:    [[COND1:%.*]] = icmp eq i64 [[MASK1]], 9218868437227405312
+; CHECK-NEXT:    [[MASK2:%.*]] = and i64 [[BITS]], 4503599627370495
+; CHECK-NEXT:    [[COND2:%.*]] = icmp ne i64 [[MASK2]], 0
+; CHECK-NEXT:    [[RET:%.*]] = and i1 [[COND2]], [[COND1]]
+; CHECK-NEXT:    ret i1 [[RET]]
+;
+  %bits = bitcast double %x to i64
+  %mask1 = and i64 %bits, 9218868437227405312
+  %cond1 = icmp eq i64 %mask1, 9218868437227405312
+  %mask2 = and i64 %bits, 4503599627370495
+  %cond2 = icmp ne i64 %mask2, 0
+  %ret = and i1 %cond2, %cond1
+  ret i1 %ret
+}
+
+define i1 @isnotnan_idiom(double %x) {
+; CHECK-LABEL: define i1 @isnotnan_idiom(
+; CHECK-SAME: double [[X:%.*]]) {
+; CHECK-NEXT:    [[BITS:%.*]] = bitcast double [[X]] to i64
+; CHECK-NEXT:    [[MASK1:%.*]] = and i64 [[BITS]], 9218868437227405312
+; CHECK-NEXT:    [[COND1:%.*]] = icmp ne i64 [[MASK1]], 9218868437227405312
+; CHECK-NEXT:    [[MASK2:%.*]] = and i64 [[BITS]], 4503599627370495
+; CHECK-NEXT:    [[COND2:%.*]] = icmp eq i64 [[MASK2]], 0
+; CHECK-NEXT:    [[RET:%.*]] = or i1 [[COND1]], [[COND2]]
+; CHECK-NEXT:    ret i1 [[RET]]
+;
+  %bits = bitcast double %x to i64
+  %mask1 = and i64 %bits, 9218868437227405312
+  %cond1 = icmp ne i64 %mask1, 9218868437227405312
+  %mask2 = and i64 %bits, 4503599627370495
+  %cond2 = icmp eq i64 %mask2, 0
+  %ret = or i1 %cond1, %cond2
+  ret i1 %ret
+}
+
+; negative tests
+
+define i1 @isnan_idiom_no_implicit(double %x) noimplicitfloat {
+; CHECK-LABEL: define i1 @isnan_idiom_no_implicit(
+; CHECK-SAME: double [[X:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT:    [[BITS:%.*]] = bitcast double [[X]] to i64
+; CHECK-NEXT:    [[MASK1:%.*]] = and i64 [[BITS]], 9218868437227405312
+; CHECK-NEXT:    [[COND1:%.*]] = icmp eq i64 [[MASK1]], 9218868437227405312
+; CHECK-NEXT:    [[MASK2:%.*]] = and i64 [[BITS]], 4503599627370495
+; CHECK-NEXT:    [[COND2:%.*]] = icmp ne i64 [[MASK2]], 0
+; CHECK-NEXT:    [[RET:%.*]] = and i1 [[COND1]], [[COND2]]
+; CHECK-NEXT:    ret i1 [[RET]]
+;
+  %bits = bitcast double %x to i64
+  %mask1 = and i64 %bits, 9218868437227405312
+  %cond1 = icmp eq i64 %mask1, 9218868437227405312
+  %mask2 = and i64 %bits, 4503599627370495
+  %cond2 = icmp ne i64 %mask2, 0
+  %ret = and i1 %cond1, %cond2
+  ret i1 %ret
+}
+
+define i1 @isnan_idiom_wrong_pred1(double %x) {
+; CHECK-LABEL: define i1 @isnan_idiom_wrong_pred1(
+; CHECK-SAME: double [[X:%.*]]) {
+; CHECK-NEXT:    [[BITS:%.*]] = bitcast double [[X]] to i64
+; CHECK-NEXT:    [[MASK1:%.*]] = and i64 [[BITS]], 9218868437227405312
+; CHECK-NEXT:    [[COND1:%.*]] = icmp ne i64 [[MASK1]], 9218868437227405312
+; CHECK-NEXT:    [[MASK2:%.*]] = and i64 [[BITS]], 4503599627370495
+; CHECK-NEXT:    [[COND2:%.*]] = icmp ne i64 [[MASK2]], 0
+; CHECK-NEXT:    [[RET:%.*]] = and i1 [[COND1]], [[COND2]]
+; CHECK-NEXT:    ret i1 [[RET]]
+;
+  %bits = bitcast double %x to i64
+  %mask1 = and i64 %bits, 9218868437227405312
+  %cond1 = icmp ne i64 %mask1, 9218868437227405312
+  %mask2 = and i64 %bits, 4503599627370495
+  %cond2 = icmp ne i64 %mask2, 0
+  %ret = and i1 %cond1, %cond2
+  ret i1 %ret
+}
+
+define i1 @isnan_idiom_wrong_pred2(double %x) {
+; CHECK-LABEL: define i1 @isnan_idiom_wrong_pred2(
+; CHECK-SAME: double [[X:%.*]]) {
+; CHECK-NEXT:    [[TMP1:%.*]] = call double @llvm.fabs.f64(double [[X]])
+; CHECK-NEXT:    [[RET:%.*]] = fcmp oeq double [[TMP1]], 0x7FF0000000000000
+; CHECK-NEXT:    ret i1 [[RET]]
+;
+  %bits = bitcast double %x to i64
+  %mask1 = and i64 %bits, 9218868437227405312
+  %cond1 = icmp eq i64 %mask1, 9218868437227405312
+  %mask2 = and i64 %bits, 4503599627370495
+  %cond2 = icmp eq i64 %mask2, 0
+  %ret = and i1 %cond1, %cond2
+  ret i1 %ret
+}
+
+define i1 @isnan_idiom_wrong_pred3(double %x) {
+; CHECK-LABEL: define i1 @isnan_idiom_wrong_pred3(
+; CHECK-SAME: double [[X:%.*]]) {
+; CHECK-NEXT:    [[BITS:%.*]] = bitcast double [[X]] to i64
+; CHECK-NEXT:    [[MASK1:%.*]] = and i64 [[BITS]], 9218868437227405312
+; CHECK-NEXT:    [[COND1:%.*]] = icmp eq i64 [[MASK1]], 9218868437227405312
+; CHECK-NEXT:    [[MASK2:%.*]] = and i64 [[BITS]], 4503599627370495
+; CHECK-NEXT:    [[COND2:%.*]] = icmp ne i64 [[MASK2]], 0
+; CHECK-NEXT:    [[RET:%.*]] = or i1 [[COND1]], [[COND2]]
+; CHECK-NEXT:    ret i1 [[RET]]
+;
+  %bits = bitcast double %x to i64
+  %mask1 = and i64 %bits, 9218868437227405312
+  %cond1 = icmp eq i64 %mask1, 9218868437227405312
+  %mask2 = and i64 %bits, 4503599627370495
+  %cond2 = icmp ne i64 %mask2, 0
+  %ret = or i1 %cond1, %cond2
+  ret i1 %ret
+}
+
+define i1 @isnan_idiom_wrong_mask1(double %x) {
+; CHECK-LABEL: define i1 @isnan_idiom_wrong_mask1(
+; CHECK-SAME: double [[X:%.*]]) {
+; CHECK-NEXT:    [[BITS:%.*]] = bitcast double [[X]] to i64
+; CHECK-NEXT:    [[MASK1:%.*]] = and i64 [[BITS]], 9218868437227405311
+; CHECK-NEXT:    [[COND1:%.*]] = icmp eq i64 [[MASK1]], 9218868437227405311
+; CHECK-NEXT:    ret i1 [[COND1]]
+;
+  %bits = bitcast double %x to i64
+  %mask1 = and i64 %bits, 9218868437227405311
+  %cond1 = icmp eq i64 %mask1, 9218868437227405311
+  %mask2 = and i64 %bits, 4503599627370495
+  %cond2 = icmp ne i64 %mask2, 0
+  %ret = and i1 %cond1, %cond2
+  ret i1 %ret
+}
+
+define i1 @isnan_idiom_wrong_mask2(double %x) {
+; CHECK-LABEL: define i1 @isnan_idiom_wrong_mask2(
+; CHECK-SAME: double [[X:%.*]]) {
+; CHECK-NEXT:    [[BITS:%.*]] = bitcast double [[X]] to i64
+; CHECK-NEXT:    [[MASK1:%.*]] = and i64 [[BITS]], 9218868437227405312
+; CHECK-NEXT:    [[COND1:%.*]] = icmp eq i64 [[MASK1]], 9218868437227405312
+; CHECK-NEXT:    [[MASK2:%.*]] = and i64 [[BITS]], 4503599627370494
+; CHECK-NEXT:    [[COND2:%.*]] = icmp ne i64 [[MASK2]], 0
+; CHECK-NEXT:    [[RET:%.*]] = and i1 [[COND1]], [[COND2]]
+; CHECK-NEXT:    ret i1 [[RET]]
+;
+  %bits = bitcast double %x to i64
+  %mask1 = and i64 %bits, 9218868437227405312
+  %cond1 = icmp eq i64 %mask1, 9218868437227405312
+  %mask2 = and i64 %bits, 4503599627370494
+  %cond2 = icmp ne i64 %mask2, 0
+  %ret = and i1 %cond1, %cond2
+  ret i1 %ret
+}
+
+define i1 @isnan_idiom_wrong_mask3(double %x) {
+; CHECK-LABEL: define i1 @isnan_idiom_wrong_mask3(
+; CHECK-SAME: double [[X:%.*]]) {
+; CHECK-NEXT:    [[BITS:%.*]] = bitcast double [[X]] to i64
+; CHECK-NEXT:    [[MASK1:%.*]] = and i64 [[BITS]], 9218868437227405312
+; CHECK-NEXT:    [[COND1:%.*]] = icmp eq i64 [[MASK1]], 9218868437227405312
+; CHECK-NEXT:    [[MASK2:%.*]] = and i64 [[BITS]], 4503599627370495
+; CHECK-NEXT:    [[COND2:%.*]] = icmp ne i64 [[MASK2]], 4503599627370495
+; CHECK-NEXT:    [[RET:%.*]] = and i1 [[COND1]], [[COND2]]
+; CHECK-NEXT:    ret i1 [[RET]]
+;
+  %bits = bitcast double %x to i64
+  %mask1 = and i64 %bits, 9218868437227405312
+  %cond1 = icmp eq i64 %mask1, 9218868437227405312
+  %mask2 = and i64 %bits, 4503599627370495
+  %cond2 = icmp ne i64 %mask2, 4503599627370495
+  %ret = and i1 %cond1, %cond2
+  ret i1 %ret
+}
+
+define i1 @isnan_idiom_invalid_bitcast(<2 x float> %x) {
+; CHECK-LABEL: define i1 @isnan_idiom_invalid_bitcast(
+; CHECK-SAME: <2 x float> [[X:%.*]]) {
+; CHECK-NEXT:    [[BITS:%.*]] = bitcast <2 x float> [[X]] to i64
+; CHECK-NEXT:    [[MASK1:%.*]] = and i64 [[BITS]], 9218868437227405312
+; CHECK-NEXT:    [[COND1:%.*]] = icmp eq i64 [[MASK1]], 9218868437227405312
+; CHECK-NEXT:    [[MASK2:%.*]] = and i64 [[BITS]], 4503599627370495
+; CHECK-NEXT:    [[COND2:%.*]] = icmp ne i64 [[MASK2]], 0
+; CHECK-NEXT:    [[RET:%.*]] = and i1 [[COND1]], [[COND2]]
+; CHECK-NEXT:    ret i1 [[RET]]
+;
+  %bits = bitcast <2 x float> %x to i64
+  %mask1 = and i64 %bits, 9218868437227405312
+  %cond1 = icmp eq i64 %mask1, 9218868437227405312
+  %mask2 = and i64 %bits, 4503599627370495
+  %cond2 = icmp ne i64 %mask2, 0
+  %ret = and i1 %cond1, %cond2
+  ret i1 %ret
+}
+
 declare void @usei32(i32)
 
 attributes #0 = { noimplicitfloat }

>From 7ab1d50c0d9dd14854853d5b303f2a897528d314 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Fri, 2 Aug 2024 00:05:33 +0800
Subject: [PATCH 2/3] [InstCombine] Fold isnan idioms

---
 .../InstCombine/InstCombineAndOrXor.cpp       | 26 ++++++++++--
 .../InstCombine/fpclass-check-idioms.ll       | 42 +++----------------
 2 files changed, 29 insertions(+), 39 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
index 4ca12d5b92f18..e0021893f7a46 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
@@ -339,8 +339,8 @@ static Value *foldLogOpOfMaskedICmps_NotAllZeros_BMask_Mixed(
   //
   // We currently handle the case of B, C, D, E are constant.
   //
-  const APInt *BCst, *CCst, *DCst, *OrigECst;
-  if (!match(B, m_APInt(BCst)) || !match(C, m_APInt(CCst)) ||
+  const APInt *BCst, *DCst, *OrigECst;
+  if (!match(B, m_APInt(BCst)) || !match(C, m_Zero()) ||
       !match(D, m_APInt(DCst)) || !match(E, m_APInt(OrigECst)))
     return nullptr;
 
@@ -363,8 +363,28 @@ static Value *foldLogOpOfMaskedICmps_NotAllZeros_BMask_Mixed(
   // deduce anything from it.
   // For example,
   // (icmp ne (A & 12), 0) & (icmp eq (A & 3), 1) -> no folding.
-  if ((*BCst & *DCst) == 0)
+  if ((*BCst & *DCst) == 0) {
+    // Try to fold isNaN idiom:
+    // (icmp ne (A & FractionBits), 0) & (icmp eq (A & ExpBits), ExpBits) ->
+    // isNaN(A)
+    Value *Src;
+    if (match(A, m_ElementWiseBitCast(m_Value(Src))) &&
+        Src->getType()->getScalarType()->isIEEELikeFPTy() &&
+        !Builder.GetInsertBlock()->getParent()->hasFnAttribute(
+            Attribute::NoImplicitFloat)) {
+      APInt ExpBits =
+          APFloat::getInf(Src->getType()->getScalarType()->getFltSemantics())
+              .bitcastToAPInt();
+      APInt FractionBits = ~ExpBits;
+      FractionBits.clearSignBit();
+
+      if (*BCst == FractionBits && *DCst == ExpBits && ECst == ExpBits)
+        return Builder.CreateFCmp(IsAnd ? FCmpInst::FCMP_UNO
+                                        : FCmpInst::FCMP_ORD,
+                                  Src, ConstantFP::getZero(Src->getType()));
+    }
     return nullptr;
+  }
 
   // If the following two conditions are met:
   //
diff --git a/llvm/test/Transforms/InstCombine/fpclass-check-idioms.ll b/llvm/test/Transforms/InstCombine/fpclass-check-idioms.ll
index e2b214884ea1c..2ffa6e4da922f 100644
--- a/llvm/test/Transforms/InstCombine/fpclass-check-idioms.ll
+++ b/llvm/test/Transforms/InstCombine/fpclass-check-idioms.ll
@@ -217,12 +217,7 @@ define i1 @f32_fczero_strictfp(float %a) strictfp {
 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:    [[RES:%.*]] = fcmp uno float [[A]], 0.000000e+00
 ; CHECK-NEXT:    ret i1 [[RES]]
 ;
   %i32 = bitcast float %a to i32
@@ -237,12 +232,7 @@ define i1 @f32_fcnan(float %a) {
 define i1 @f32_fcnan_strictfp(float %a) strictfp {
 ; CHECK-LABEL: define i1 @f32_fcnan_strictfp(
 ; CHECK-SAME: float [[A:%.*]]) #[[ATTR0]] {
-; 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:    [[RES:%.*]] = fcmp uno float [[A]], 0.000000e+00
 ; CHECK-NEXT:    ret i1 [[RES]]
 ;
   %i32 = bitcast float %a to i32
@@ -650,12 +640,7 @@ define i1 @f32_fcposinf_multiuse_strictfp(float %a) strictfp {
 define i1 @isnan_idiom(double %x) {
 ; CHECK-LABEL: define i1 @isnan_idiom(
 ; CHECK-SAME: double [[X:%.*]]) {
-; CHECK-NEXT:    [[BITS:%.*]] = bitcast double [[X]] to i64
-; CHECK-NEXT:    [[MASK1:%.*]] = and i64 [[BITS]], 9218868437227405312
-; CHECK-NEXT:    [[COND1:%.*]] = icmp eq i64 [[MASK1]], 9218868437227405312
-; CHECK-NEXT:    [[MASK2:%.*]] = and i64 [[BITS]], 4503599627370495
-; CHECK-NEXT:    [[COND2:%.*]] = icmp ne i64 [[MASK2]], 0
-; CHECK-NEXT:    [[RET:%.*]] = and i1 [[COND1]], [[COND2]]
+; CHECK-NEXT:    [[RET:%.*]] = fcmp uno double [[X]], 0.000000e+00
 ; CHECK-NEXT:    ret i1 [[RET]]
 ;
   %bits = bitcast double %x to i64
@@ -670,12 +655,7 @@ define i1 @isnan_idiom(double %x) {
 define <2 x i1> @isnan_idiom_vec(<2 x double> %x) {
 ; CHECK-LABEL: define <2 x i1> @isnan_idiom_vec(
 ; CHECK-SAME: <2 x double> [[X:%.*]]) {
-; CHECK-NEXT:    [[BITS:%.*]] = bitcast <2 x double> [[X]] to <2 x i64>
-; CHECK-NEXT:    [[MASK1:%.*]] = and <2 x i64> [[BITS]], <i64 9218868437227405312, i64 9218868437227405312>
-; CHECK-NEXT:    [[COND1:%.*]] = icmp eq <2 x i64> [[MASK1]], <i64 9218868437227405312, i64 9218868437227405312>
-; CHECK-NEXT:    [[MASK2:%.*]] = and <2 x i64> [[BITS]], <i64 4503599627370495, i64 4503599627370495>
-; CHECK-NEXT:    [[COND2:%.*]] = icmp ne <2 x i64> [[MASK2]], zeroinitializer
-; CHECK-NEXT:    [[RET:%.*]] = and <2 x i1> [[COND1]], [[COND2]]
+; CHECK-NEXT:    [[RET:%.*]] = fcmp uno <2 x double> [[X]], zeroinitializer
 ; CHECK-NEXT:    ret <2 x i1> [[RET]]
 ;
   %bits = bitcast <2 x double> %x to <2 x i64>
@@ -690,12 +670,7 @@ define <2 x i1> @isnan_idiom_vec(<2 x double> %x) {
 define i1 @isnan_idiom_commuted(double %x) {
 ; CHECK-LABEL: define i1 @isnan_idiom_commuted(
 ; CHECK-SAME: double [[X:%.*]]) {
-; CHECK-NEXT:    [[BITS:%.*]] = bitcast double [[X]] to i64
-; CHECK-NEXT:    [[MASK1:%.*]] = and i64 [[BITS]], 9218868437227405312
-; CHECK-NEXT:    [[COND1:%.*]] = icmp eq i64 [[MASK1]], 9218868437227405312
-; CHECK-NEXT:    [[MASK2:%.*]] = and i64 [[BITS]], 4503599627370495
-; CHECK-NEXT:    [[COND2:%.*]] = icmp ne i64 [[MASK2]], 0
-; CHECK-NEXT:    [[RET:%.*]] = and i1 [[COND2]], [[COND1]]
+; CHECK-NEXT:    [[RET:%.*]] = fcmp uno double [[X]], 0.000000e+00
 ; CHECK-NEXT:    ret i1 [[RET]]
 ;
   %bits = bitcast double %x to i64
@@ -710,12 +685,7 @@ define i1 @isnan_idiom_commuted(double %x) {
 define i1 @isnotnan_idiom(double %x) {
 ; CHECK-LABEL: define i1 @isnotnan_idiom(
 ; CHECK-SAME: double [[X:%.*]]) {
-; CHECK-NEXT:    [[BITS:%.*]] = bitcast double [[X]] to i64
-; CHECK-NEXT:    [[MASK1:%.*]] = and i64 [[BITS]], 9218868437227405312
-; CHECK-NEXT:    [[COND1:%.*]] = icmp ne i64 [[MASK1]], 9218868437227405312
-; CHECK-NEXT:    [[MASK2:%.*]] = and i64 [[BITS]], 4503599627370495
-; CHECK-NEXT:    [[COND2:%.*]] = icmp eq i64 [[MASK2]], 0
-; CHECK-NEXT:    [[RET:%.*]] = or i1 [[COND1]], [[COND2]]
+; CHECK-NEXT:    [[RET:%.*]] = fcmp ord double [[X]], 0.000000e+00
 ; CHECK-NEXT:    ret i1 [[RET]]
 ;
   %bits = bitcast double %x to i64

>From 13d76438bec88d1ee6600b300e2f7a34df2f064f Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Fri, 2 Aug 2024 02:15:51 +0800
Subject: [PATCH 3/3] [InstCombine] Address review comments. NFC.

---
 .../InstCombine/InstCombineAndOrXor.cpp       | 50 +++++++++----------
 .../InstCombine/fpclass-check-idioms.ll       | 33 ++++++++++--
 2 files changed, 53 insertions(+), 30 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
index e0021893f7a46..2db05c669145b 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
@@ -326,8 +326,8 @@ static std::optional<std::pair<unsigned, unsigned>> getMaskedTypeForICmpPair(
 /// (icmp (A & 12) != 0) & (icmp (A & 15) == 8) -> (icmp (A & 15) == 8).
 /// Also used for logical and/or, must be poison safe.
 static Value *foldLogOpOfMaskedICmps_NotAllZeros_BMask_Mixed(
-    ICmpInst *LHS, ICmpInst *RHS, bool IsAnd, Value *A, Value *B, Value *C,
-    Value *D, Value *E, ICmpInst::Predicate PredL, ICmpInst::Predicate PredR,
+    ICmpInst *LHS, ICmpInst *RHS, bool IsAnd, Value *A, Value *B, Value *D,
+    Value *E, ICmpInst::Predicate PredL, ICmpInst::Predicate PredR,
     InstCombiner::BuilderTy &Builder) {
   // We are given the canonical form:
   //   (icmp ne (A & B), 0) & (icmp eq (A & D), E).
@@ -340,8 +340,8 @@ static Value *foldLogOpOfMaskedICmps_NotAllZeros_BMask_Mixed(
   // We currently handle the case of B, C, D, E are constant.
   //
   const APInt *BCst, *DCst, *OrigECst;
-  if (!match(B, m_APInt(BCst)) || !match(C, m_Zero()) ||
-      !match(D, m_APInt(DCst)) || !match(E, m_APInt(OrigECst)))
+  if (!match(B, m_APInt(BCst)) || !match(D, m_APInt(DCst)) ||
+      !match(E, m_APInt(OrigECst)))
     return nullptr;
 
   ICmpInst::Predicate NewCC = IsAnd ? ICmpInst::ICMP_EQ : ICmpInst::ICMP_NE;
@@ -359,29 +359,29 @@ static Value *foldLogOpOfMaskedICmps_NotAllZeros_BMask_Mixed(
   if (*BCst == 0 || *DCst == 0)
     return nullptr;
 
-  // If B and D don't intersect, ie. (B & D) == 0, no folding because we can't
-  // deduce anything from it.
-  // For example,
-  // (icmp ne (A & 12), 0) & (icmp eq (A & 3), 1) -> no folding.
-  if ((*BCst & *DCst) == 0) {
-    // Try to fold isNaN idiom:
-    // (icmp ne (A & FractionBits), 0) & (icmp eq (A & ExpBits), ExpBits) ->
-    // isNaN(A)
+  // If B and D don't intersect, ie. (B & D) == 0, try to fold isNaN idiom:
+  // (icmp ne (A & FractionBits), 0) & (icmp eq (A & ExpBits), ExpBits)
+  // -> isNaN(A)
+  // Otherwise, we cannot deduce anything from it.
+  if (!BCst->intersects(*DCst)) {
     Value *Src;
-    if (match(A, m_ElementWiseBitCast(m_Value(Src))) &&
-        Src->getType()->getScalarType()->isIEEELikeFPTy() &&
+    if (*DCst == ECst && match(A, m_ElementWiseBitCast(m_Value(Src))) &&
         !Builder.GetInsertBlock()->getParent()->hasFnAttribute(
-            Attribute::NoImplicitFloat)) {
-      APInt ExpBits =
-          APFloat::getInf(Src->getType()->getScalarType()->getFltSemantics())
-              .bitcastToAPInt();
+            Attribute::StrictFP)) {
+      Type *Ty = Src->getType()->getScalarType();
+      if (!Ty->isIEEELikeFPTy())
+        return nullptr;
+
+      APInt ExpBits = APFloat::getInf(Ty->getFltSemantics()).bitcastToAPInt();
+      if (ECst != ExpBits)
+        return nullptr;
       APInt FractionBits = ~ExpBits;
       FractionBits.clearSignBit();
+      if (*BCst != FractionBits)
+        return nullptr;
 
-      if (*BCst == FractionBits && *DCst == ExpBits && ECst == ExpBits)
-        return Builder.CreateFCmp(IsAnd ? FCmpInst::FCMP_UNO
-                                        : FCmpInst::FCMP_ORD,
-                                  Src, ConstantFP::getZero(Src->getType()));
+      return Builder.CreateFCmp(IsAnd ? FCmpInst::FCMP_UNO : FCmpInst::FCMP_ORD,
+                                Src, ConstantFP::getZero(Src->getType()));
     }
     return nullptr;
   }
@@ -484,14 +484,12 @@ static Value *foldLogOpOfMaskedICmpsAsymmetric(
   }
   if ((LHSMask & Mask_NotAllZeros) && (RHSMask & BMask_Mixed)) {
     if (Value *V = foldLogOpOfMaskedICmps_NotAllZeros_BMask_Mixed(
-            LHS, RHS, IsAnd, A, B, C, D, E,
-            PredL, PredR, Builder)) {
+            LHS, RHS, IsAnd, A, B, D, E, PredL, PredR, Builder)) {
       return V;
     }
   } else if ((LHSMask & BMask_Mixed) && (RHSMask & Mask_NotAllZeros)) {
     if (Value *V = foldLogOpOfMaskedICmps_NotAllZeros_BMask_Mixed(
-            RHS, LHS, IsAnd, A, D, E, B, C,
-            PredR, PredL, Builder)) {
+            RHS, LHS, IsAnd, A, D, B, C, PredR, PredL, Builder)) {
       return V;
     }
   }
diff --git a/llvm/test/Transforms/InstCombine/fpclass-check-idioms.ll b/llvm/test/Transforms/InstCombine/fpclass-check-idioms.ll
index 2ffa6e4da922f..4034759df0604 100644
--- a/llvm/test/Transforms/InstCombine/fpclass-check-idioms.ll
+++ b/llvm/test/Transforms/InstCombine/fpclass-check-idioms.ll
@@ -232,7 +232,12 @@ define i1 @f32_fcnan(float %a) {
 define i1 @f32_fcnan_strictfp(float %a) strictfp {
 ; CHECK-LABEL: define i1 @f32_fcnan_strictfp(
 ; CHECK-SAME: float [[A:%.*]]) #[[ATTR0]] {
-; CHECK-NEXT:    [[RES:%.*]] = fcmp uno float [[A]], 0.000000e+00
+; 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
@@ -699,9 +704,9 @@ define i1 @isnotnan_idiom(double %x) {
 
 ; negative tests
 
-define i1 @isnan_idiom_no_implicit(double %x) noimplicitfloat {
-; CHECK-LABEL: define i1 @isnan_idiom_no_implicit(
-; CHECK-SAME: double [[X:%.*]]) #[[ATTR1]] {
+define i1 @isnan_idiom_strictfp(double %x) strictfp {
+; CHECK-LABEL: define i1 @isnan_idiom_strictfp(
+; CHECK-SAME: double [[X:%.*]]) #[[ATTR0]] {
 ; CHECK-NEXT:    [[BITS:%.*]] = bitcast double [[X]] to i64
 ; CHECK-NEXT:    [[MASK1:%.*]] = and i64 [[BITS]], 9218868437227405312
 ; CHECK-NEXT:    [[COND1:%.*]] = icmp eq i64 [[MASK1]], 9218868437227405312
@@ -852,6 +857,26 @@ define i1 @isnan_idiom_invalid_bitcast(<2 x float> %x) {
   ret i1 %ret
 }
 
+define i1 @isnan_idiom_ppc_fp128(ppc_fp128 %x) {
+; CHECK-LABEL: define i1 @isnan_idiom_ppc_fp128(
+; CHECK-SAME: ppc_fp128 [[X:%.*]]) {
+; CHECK-NEXT:    [[BITS:%.*]] = bitcast ppc_fp128 [[X]] to i128
+; CHECK-NEXT:    [[MASK1:%.*]] = and i128 [[BITS]], 170058106710732674489630815774616584192
+; CHECK-NEXT:    [[COND1:%.*]] = icmp eq i128 [[MASK1]], 170058106710732674489630815774616584192
+; CHECK-NEXT:    [[MASK2:%.*]] = and i128 [[BITS]], 83076749736557242056487941267521535
+; CHECK-NEXT:    [[COND2:%.*]] = icmp ne i128 [[MASK2]], 0
+; CHECK-NEXT:    [[RET:%.*]] = and i1 [[COND1]], [[COND2]]
+; CHECK-NEXT:    ret i1 [[RET]]
+;
+  %bits = bitcast ppc_fp128 %x to i128
+  %mask1 = and i128 %bits, 170058106710732674489630815774616584192
+  %cond1 = icmp eq i128 %mask1, 170058106710732674489630815774616584192
+  %mask2 = and i128 %bits, 83076749736557242056487941267521535
+  %cond2 = icmp ne i128 %mask2, 0
+  %ret = and i1 %cond1, %cond2
+  ret i1 %ret
+}
+
 declare void @usei32(i32)
 
 attributes #0 = { noimplicitfloat }



More information about the llvm-commits mailing list