InstCombine: Fold and (fcmp), (is.fpclass) into is.fpclass
authorMatt Arsenault <Matthew.Arsenault@amd.com>
Mon, 12 Dec 2022 03:08:28 +0000 (22:08 -0500)
committerMatt Arsenault <arsenm2@gmail.com>
Thu, 9 Feb 2023 01:40:20 +0000 (21:40 -0400)
Fold class test performed by an fcmp into another class. For now this
avoids introducing new class calls then there isn't one that already
exists.

llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
llvm/test/Transforms/InstCombine/combine-is.fpclass-and-fcmp.ll
llvm/test/Transforms/InstCombine/is_fpclass.ll

index f11d2fd..5b168e4 100644 (file)
@@ -1246,6 +1246,161 @@ static Value *matchIsFiniteTest(InstCombiner::BuilderTy &Builder, FCmpInst *LHS,
   return Builder.CreateFCmp(FCmpInst::getOrderedPredicate(PredR), RHS0, RHS1);
 }
 
+/// Returns a pair of values, which if passed to llvm.is.fpclass, returns the
+/// same result as an fcmp with the given operands.
+static std::pair<Value *, unsigned> fcmpToClassTest(FCmpInst::Predicate Pred,
+                                                    Value *LHS, Value *RHS) {
+  const APFloat *ConstRHS;
+  if (!match(RHS, m_APFloat(ConstRHS)))
+    return {nullptr, 0};
+
+  if (ConstRHS->isZero()) {
+    switch (Pred) {
+    // TODO: Compares eq/ne with 0 depends on the denormal handling mode.
+    case FCmpInst::FCMP_ORD:
+      // Canonical form of ord/uno is with a zero. We could also handle
+      // non-canonical other non-NaN constants or LHS == RHS.
+      return {LHS, ~fcNan & fcAllFlags};
+    case FCmpInst::FCMP_UNO:
+      return {LHS, fcNan};
+    default:
+      break;
+    }
+
+    return {nullptr, 0};
+  }
+
+  Value *Src = LHS;
+  const bool IsFabs = match(LHS, m_FAbs(m_Value(Src)));
+
+  // Compute the test mask that would return true for the ordered comparisons.
+  unsigned Mask;
+
+  if (ConstRHS->isInfinity()) {
+    switch (Pred) {
+    case FCmpInst::FCMP_OEQ:
+    case FCmpInst::FCMP_UNE: {
+      // Match __builtin_isinf patterns
+      //
+      //   fcmp oeq x, +inf -> is_fpclass x, fcPosInf
+      //   fcmp oeq fabs(x), +inf -> is_fpclass x, fcInf
+      //   fcmp oeq x, -inf -> is_fpclass x, fcNegInf
+      //   fcmp oeq fabs(x), -inf -> is_fpclass x, 0 -> false
+      //
+      //   fcmp une x, +inf -> is_fpclass x, ~fcPosInf
+      //   fcmp une fabs(x), +inf -> is_fpclass x, ~fcInf
+      //   fcmp une x, -inf -> is_fpclass x, ~fcNegInf
+      //   fcmp une fabs(x), -inf -> is_fpclass x, fcAllFlags -> true
+
+      if (ConstRHS->isNegative()) {
+        Mask = fcNegInf;
+        if (IsFabs)
+          Mask = 0;
+      } else {
+        Mask = fcPosInf;
+        if (IsFabs)
+          Mask |= fcNegInf;
+      }
+
+      break;
+    }
+    case FCmpInst::FCMP_ONE:
+    case FCmpInst::FCMP_UEQ: {
+      // Match __builtin_isinf patterns
+      //   fcmp one x, -inf -> is_fpclass x, fcNegInf
+      //   fcmp one fabs(x), -inf -> is_fpclass x, ~fcNegInf & ~fcNan
+      //   fcmp one x, +inf -> is_fpclass x, ~fcNegInf & ~fcNan
+      //   fcmp one fabs(x), +inf -> is_fpclass x, ~fcInf & fcNan
+      //
+      //   fcmp ueq x, +inf -> is_fpclass x, fcPosInf|fcNan
+      //   fcmp ueq (fabs x), +inf -> is_fpclass x, fcInf|fcNan
+      //   fcmp ueq x, -inf -> is_fpclass x, fcNegInf|fcNan
+      //   fcmp ueq fabs(x), -inf -> is_fpclass x, fcNan
+      if (ConstRHS->isNegative()) {
+        Mask = ~fcNegInf & ~fcNan;
+        if (IsFabs)
+          Mask = ~fcNan;
+      } else {
+        Mask = ~fcPosInf & ~fcNan;
+        if (IsFabs)
+          Mask &= ~fcNegInf;
+      }
+
+      Mask &= fcAllFlags;
+      break;
+    }
+    case FCmpInst::FCMP_OLT:
+    case FCmpInst::FCMP_UGE: {
+      if (ConstRHS->isNegative()) // TODO
+        return {nullptr, 0};
+
+      // fcmp olt fabs(x), +inf -> fcFinite
+      // fcmp uge fabs(x), +inf -> ~fcFinite
+      // fcmp olt x, +inf -> fcFinite|fcNegInf
+      // fcmp uge x, +inf -> ~(fcFinite|fcNegInf)
+      Mask = fcFinite;
+      if (!IsFabs)
+        Mask |= fcNegInf;
+      break;
+    }
+    case FCmpInst::FCMP_OGE:
+    case FCmpInst::FCMP_ULT: {
+      if (ConstRHS->isNegative()) // TODO
+        return {nullptr, 0};
+
+      // fcmp oge fabs(x), +inf -> fcInf
+      // fcmp oge x, +inf -> fcPosInf
+      // fcmp ult fabs(x), +inf -> ~fcInf
+      // fcmp ult x, +inf -> ~fcPosInf
+      Mask = fcPosInf;
+      if (IsFabs)
+        Mask |= fcNegInf;
+      break;
+    }
+    default:
+      return {nullptr, 0};
+    }
+  } else if (ConstRHS->isSmallestNormalized() && !ConstRHS->isNegative()) {
+    // Match pattern that's used in __builtin_isnormal.
+    switch (Pred) {
+    case FCmpInst::FCMP_OLT:
+    case FCmpInst::FCMP_UGE: {
+      // fcmp olt x, smallest_normal -> fcNegInf|fcNegNormal|fcSubnormal|fcZero
+      // fcmp olt fabs(x), smallest_normal -> fcSubnormal|fcZero
+      // fcmp uge x, smallest_normal -> fcNan|fcPosNormal|fcPosInf
+      // fcmp uge fabs(x), smallest_normal -> ~(fcSubnormal|fcZero)
+      Mask = fcZero | fcSubnormal;
+      if (!IsFabs)
+        Mask |= fcNegNormal | fcNegInf;
+
+      break;
+    }
+    case FCmpInst::FCMP_OGE:
+    case FCmpInst::FCMP_ULT: {
+      // fcmp oge x, smallest_normal -> fcPosNormal | fcPosInf
+      // fcmp oge fabs(x), smallest_normal -> fcInf | fcNormal
+      // fcmp ult x, smallest_normal -> ~(fcPosNormal | fcPosInf)
+      // fcmp ult fabs(x), smallest_normal -> ~(fcInf | fcNormal)
+      Mask = fcPosInf | fcPosNormal;
+      if (IsFabs)
+        Mask |= fcNegInf | fcNegNormal;
+      break;
+    }
+    default:
+      return {nullptr, 0};
+    }
+  } else
+    return {nullptr, 0};
+
+  // Invert the comparison for the unordered cases.
+  if (FCmpInst::isUnordered(Pred))
+    Mask = ~Mask & fcAllFlags;
+
+  assert((Mask & ~fcAllFlags) == 0);
+
+  return {Src, Mask};
+}
+
 Value *InstCombinerImpl::foldLogicOfFCmps(FCmpInst *LHS, FCmpInst *RHS,
                                           bool IsAnd, bool IsLogicalSelect) {
   Value *LHS0 = LHS->getOperand(0), *LHS1 = LHS->getOperand(1);
@@ -1316,6 +1471,19 @@ Value *InstCombinerImpl::foldLogicOfFCmps(FCmpInst *LHS, FCmpInst *RHS,
   return nullptr;
 }
 
+/// Match an fcmp against a special value that performs a test possible by
+/// llvm.is.fpclass.
+static bool matchIsFPClassLikeFCmp(Value *Op, Value *&ClassVal,
+                                   uint64_t &ClassMask) {
+  auto *FCmp = dyn_cast<FCmpInst>(Op);
+  if (!FCmp || !FCmp->hasOneUse())
+    return false;
+
+  std::tie(ClassVal, ClassMask) = fcmpToClassTest(
+      FCmp->getPredicate(), FCmp->getOperand(0), FCmp->getOperand(1));
+  return ClassVal != nullptr;
+}
+
 /// or (is_fpclass x, mask0), (is_fpclass x, mask1)
 ///     -> is_fpclass x, (mask0 | mask1)
 /// and (is_fpclass x, mask0), (is_fpclass x, mask1)
@@ -1324,13 +1492,26 @@ Value *InstCombinerImpl::foldLogicOfFCmps(FCmpInst *LHS, FCmpInst *RHS,
 ///     -> is_fpclass x, (mask0 ^ mask1)
 Instruction *InstCombinerImpl::foldLogicOfIsFPClass(BinaryOperator &BO,
                                                     Value *Op0, Value *Op1) {
-  Value *ClassVal;
+  Value *ClassVal0 = nullptr;
+  Value *ClassVal1 = nullptr;
   uint64_t ClassMask0, ClassMask1;
 
-  if (match(Op0, m_OneUse(m_Intrinsic<Intrinsic::is_fpclass>(
-                     m_Value(ClassVal), m_ConstantInt(ClassMask0)))) &&
+  // Restrict to folding one fcmp into one is.fpclass for now, don't introduce a
+  // new class.
+  //
+  // TODO: Support forming is.fpclass out of 2 separate fcmps when codegen is
+  // better.
+
+  bool IsLHSClass =
+      match(Op0, m_OneUse(m_Intrinsic<Intrinsic::is_fpclass>(
+                     m_Value(ClassVal0), m_ConstantInt(ClassMask0))));
+  bool IsRHSClass =
       match(Op1, m_OneUse(m_Intrinsic<Intrinsic::is_fpclass>(
-                     m_Specific(ClassVal), m_ConstantInt(ClassMask1))))) {
+                     m_Value(ClassVal1), m_ConstantInt(ClassMask1))));
+  if (((IsLHSClass && IsRHSClass) ||
+       ((!IsLHSClass && matchIsFPClassLikeFCmp(Op0, ClassVal0, ClassMask0)) ||
+        (!IsRHSClass && matchIsFPClassLikeFCmp(Op1, ClassVal1, ClassMask1)))) &&
+      ClassVal0 == ClassVal1) {
     unsigned NewClassMask;
     switch (BO.getOpcode()) {
     case Instruction::And:
@@ -1346,8 +1527,7 @@ Instruction *InstCombinerImpl::foldLogicOfIsFPClass(BinaryOperator &BO,
       llvm_unreachable("not a binary logic operator");
     }
 
-    // TODO: Also check for special fcmps
-    auto *II = cast<IntrinsicInst>(Op0);
+    auto *II = IsLHSClass ? cast<IntrinsicInst>(Op0) : cast<IntrinsicInst>(Op1);
     II->setArgOperand(
         1, ConstantInt::get(II->getArgOperand(1)->getType(), NewClassMask));
     return replaceInstUsesWith(BO, II);
index 56ac3f4..35e0e13 100644 (file)
@@ -3,10 +3,8 @@
 
 define i1 @fcmp_oeq_inf_or_class_normal(half %x) {
 ; CHECK-LABEL: @fcmp_oeq_inf_or_class_normal(
-; CHECK-NEXT:    [[OEQ_INF:%.*]] = fcmp oeq half [[X:%.*]], 0xH7C00
-; CHECK-NEXT:    [[CLASS:%.*]] = call i1 @llvm.is.fpclass.f16(half [[X]], i32 264)
-; CHECK-NEXT:    [[OR:%.*]] = or i1 [[OEQ_INF]], [[CLASS]]
-; CHECK-NEXT:    ret i1 [[OR]]
+; CHECK-NEXT:    [[CLASS:%.*]] = call i1 @llvm.is.fpclass.f16(half [[X:%.*]], i32 776)
+; CHECK-NEXT:    ret i1 [[CLASS]]
 ;
   %oeq.inf = fcmp oeq half %x, 0xH7C00
   %class = call i1 @llvm.is.fpclass.f16(half %x, i32 264)
@@ -16,10 +14,8 @@ define i1 @fcmp_oeq_inf_or_class_normal(half %x) {
 
 define i1 @class_normal_or_fcmp_oeq_inf(half %x) {
 ; CHECK-LABEL: @class_normal_or_fcmp_oeq_inf(
-; CHECK-NEXT:    [[OEQ_INF:%.*]] = fcmp oeq half [[X:%.*]], 0xH7C00
-; CHECK-NEXT:    [[CLASS:%.*]] = call i1 @llvm.is.fpclass.f16(half [[X]], i32 264)
-; CHECK-NEXT:    [[OR:%.*]] = or i1 [[CLASS]], [[OEQ_INF]]
-; CHECK-NEXT:    ret i1 [[OR]]
+; CHECK-NEXT:    [[CLASS:%.*]] = call i1 @llvm.is.fpclass.f16(half [[X:%.*]], i32 776)
+; CHECK-NEXT:    ret i1 [[CLASS]]
 ;
   %oeq.inf = fcmp oeq half %x, 0xH7C00
   %class = call i1 @llvm.is.fpclass.f16(half %x, i32 264)
@@ -29,10 +25,8 @@ define i1 @class_normal_or_fcmp_oeq_inf(half %x) {
 
 define <2 x i1> @fcmp_oeq_inf_or_class_normal_vector(<2 x half> %x) {
 ; CHECK-LABEL: @fcmp_oeq_inf_or_class_normal_vector(
-; CHECK-NEXT:    [[OEQ_INF:%.*]] = fcmp oeq <2 x half> [[X:%.*]], <half 0xH7C00, half 0xH7C00>
-; CHECK-NEXT:    [[CLASS:%.*]] = call <2 x i1> @llvm.is.fpclass.v2f16(<2 x half> [[X]], i32 264)
-; CHECK-NEXT:    [[OR:%.*]] = or <2 x i1> [[OEQ_INF]], [[CLASS]]
-; CHECK-NEXT:    ret <2 x i1> [[OR]]
+; CHECK-NEXT:    [[CLASS:%.*]] = call <2 x i1> @llvm.is.fpclass.v2f16(<2 x half> [[X:%.*]], i32 776)
+; CHECK-NEXT:    ret <2 x i1> [[CLASS]]
 ;
   %oeq.inf = fcmp oeq <2 x half> %x, <half 0xH7C00, half 0xH7C00>
   %class = call <2 x i1> @llvm.is.fpclass.v2f16(<2 x half> %x, i32 264)
@@ -129,11 +123,8 @@ define i1 @fcmp_ord_and_class_isnan_commute(half %x) {
 
 define i1 @fcmp_isfinite_and_class_subnormal(half %x) {
 ; CHECK-LABEL: @fcmp_isfinite_and_class_subnormal(
-; CHECK-NEXT:    [[FABS:%.*]] = call half @llvm.fabs.f16(half [[X:%.*]])
-; CHECK-NEXT:    [[IS_FINITE:%.*]] = fcmp olt half [[FABS]], 0xH7C00
-; CHECK-NEXT:    [[SUBNORMAL_CLASS:%.*]] = call i1 @llvm.is.fpclass.f16(half [[X]], i32 144)
-; CHECK-NEXT:    [[AND:%.*]] = and i1 [[IS_FINITE]], [[SUBNORMAL_CLASS]]
-; CHECK-NEXT:    ret i1 [[AND]]
+; CHECK-NEXT:    [[SUBNORMAL_CLASS:%.*]] = call i1 @llvm.is.fpclass.f16(half [[X:%.*]], i32 144)
+; CHECK-NEXT:    ret i1 [[SUBNORMAL_CLASS]]
 ;
   %fabs = call half @llvm.fabs.f16(half %x)
   %is.finite = fcmp olt half %fabs, 0xH7C00
@@ -144,11 +135,8 @@ define i1 @fcmp_isfinite_and_class_subnormal(half %x) {
 
 define i1 @fcmp_isfinite_or_class_subnormal(half %x) {
 ; CHECK-LABEL: @fcmp_isfinite_or_class_subnormal(
-; CHECK-NEXT:    [[FABS:%.*]] = call half @llvm.fabs.f16(half [[X:%.*]])
-; CHECK-NEXT:    [[IS_FINITE:%.*]] = fcmp olt half [[FABS]], 0xH7C00
-; CHECK-NEXT:    [[SUBNORMAL_CLASS:%.*]] = call i1 @llvm.is.fpclass.f16(half [[X]], i32 144)
-; CHECK-NEXT:    [[OR:%.*]] = or i1 [[IS_FINITE]], [[SUBNORMAL_CLASS]]
-; CHECK-NEXT:    ret i1 [[OR]]
+; CHECK-NEXT:    [[SUBNORMAL_CLASS:%.*]] = call i1 @llvm.is.fpclass.f16(half [[X:%.*]], i32 504)
+; CHECK-NEXT:    ret i1 [[SUBNORMAL_CLASS]]
 ;
   %fabs = call half @llvm.fabs.f16(half %x)
   %is.finite = fcmp olt half %fabs, 0xH7C00
@@ -160,11 +148,8 @@ define i1 @fcmp_isfinite_or_class_subnormal(half %x) {
 ; -> isfinite
 define i1 @fcmp_issubnormal_or_class_finite(half %x) {
 ; CHECK-LABEL: @fcmp_issubnormal_or_class_finite(
-; CHECK-NEXT:    [[FABS:%.*]] = call half @llvm.fabs.f16(half [[X:%.*]])
-; CHECK-NEXT:    [[IS_SUBNORMAL:%.*]] = fcmp olt half [[FABS]], 0xH0400
-; CHECK-NEXT:    [[IS_FINITE_CLASS:%.*]] = call i1 @llvm.is.fpclass.f16(half [[X]], i32 504)
-; CHECK-NEXT:    [[OR:%.*]] = or i1 [[IS_SUBNORMAL]], [[IS_FINITE_CLASS]]
-; CHECK-NEXT:    ret i1 [[OR]]
+; CHECK-NEXT:    [[IS_FINITE_CLASS:%.*]] = call i1 @llvm.is.fpclass.f16(half [[X:%.*]], i32 504)
+; CHECK-NEXT:    ret i1 [[IS_FINITE_CLASS]]
 ;
   %fabs = call half @llvm.fabs.f16(half %x)
   %is.subnormal = fcmp olt half %fabs, 0xH0400
@@ -176,11 +161,8 @@ define i1 @fcmp_issubnormal_or_class_finite(half %x) {
 ; -> isfinite
 define i1 @class_finite_or_fcmp_issubnormal(half %x) {
 ; CHECK-LABEL: @class_finite_or_fcmp_issubnormal(
-; CHECK-NEXT:    [[FABS:%.*]] = call half @llvm.fabs.f16(half [[X:%.*]])
-; CHECK-NEXT:    [[IS_SUBNORMAL:%.*]] = fcmp olt half [[FABS]], 0xH0400
-; CHECK-NEXT:    [[IS_FINITE_CLASS:%.*]] = call i1 @llvm.is.fpclass.f16(half [[X]], i32 504)
-; CHECK-NEXT:    [[OR:%.*]] = or i1 [[IS_FINITE_CLASS]], [[IS_SUBNORMAL]]
-; CHECK-NEXT:    ret i1 [[OR]]
+; CHECK-NEXT:    [[IS_FINITE_CLASS:%.*]] = call i1 @llvm.is.fpclass.f16(half [[X:%.*]], i32 504)
+; CHECK-NEXT:    ret i1 [[IS_FINITE_CLASS]]
 ;
   %fabs = call half @llvm.fabs.f16(half %x)
   %is.subnormal = fcmp olt half %fabs, 0xH0400
@@ -192,11 +174,8 @@ define i1 @class_finite_or_fcmp_issubnormal(half %x) {
 ; -> issubnormal
 define i1 @fcmp_issubnormal_and_class_finite(half %x) {
 ; CHECK-LABEL: @fcmp_issubnormal_and_class_finite(
-; CHECK-NEXT:    [[FABS:%.*]] = call half @llvm.fabs.f16(half [[X:%.*]])
-; CHECK-NEXT:    [[IS_SUBNORMAL:%.*]] = fcmp olt half [[FABS]], 0xH0400
-; CHECK-NEXT:    [[IS_FINITE_CLASS:%.*]] = call i1 @llvm.is.fpclass.f16(half [[X]], i32 504)
-; CHECK-NEXT:    [[AND:%.*]] = and i1 [[IS_SUBNORMAL]], [[IS_FINITE_CLASS]]
-; CHECK-NEXT:    ret i1 [[AND]]
+; CHECK-NEXT:    [[IS_FINITE_CLASS:%.*]] = call i1 @llvm.is.fpclass.f16(half [[X:%.*]], i32 240)
+; CHECK-NEXT:    ret i1 [[IS_FINITE_CLASS]]
 ;
   %fabs = call half @llvm.fabs.f16(half %x)
   %is.subnormal = fcmp olt half %fabs, 0xH0400
@@ -207,11 +186,8 @@ define i1 @fcmp_issubnormal_and_class_finite(half %x) {
 
 define i1 @class_inf_or_fcmp_issubnormal(half %x) {
 ; CHECK-LABEL: @class_inf_or_fcmp_issubnormal(
-; CHECK-NEXT:    [[FABS:%.*]] = call half @llvm.fabs.f16(half [[X:%.*]])
-; CHECK-NEXT:    [[IS_SUBNORMAL:%.*]] = fcmp olt half [[FABS]], 0xH0400
-; CHECK-NEXT:    [[IS_INF_CLASS:%.*]] = call i1 @llvm.is.fpclass.f16(half [[X]], i32 516)
-; CHECK-NEXT:    [[OR:%.*]] = or i1 [[IS_INF_CLASS]], [[IS_SUBNORMAL]]
-; CHECK-NEXT:    ret i1 [[OR]]
+; CHECK-NEXT:    [[IS_INF_CLASS:%.*]] = call i1 @llvm.is.fpclass.f16(half [[X:%.*]], i32 756)
+; CHECK-NEXT:    ret i1 [[IS_INF_CLASS]]
 ;
   %fabs = call half @llvm.fabs.f16(half %x)
   %is.subnormal = fcmp olt half %fabs, 0xH0400
@@ -223,11 +199,8 @@ define i1 @class_inf_or_fcmp_issubnormal(half %x) {
 ; -> isfinite
 define <2 x i1> @class_finite_or_fcmp_issubnormal_vector(<2 x half> %x) {
 ; CHECK-LABEL: @class_finite_or_fcmp_issubnormal_vector(
-; CHECK-NEXT:    [[FABS:%.*]] = call <2 x half> @llvm.fabs.v2f16(<2 x half> [[X:%.*]])
-; CHECK-NEXT:    [[IS_SUBNORMAL:%.*]] = fcmp olt <2 x half> [[FABS]], <half 0xH0400, half 0xH0400>
-; CHECK-NEXT:    [[IS_FINITE_CLASS:%.*]] = call <2 x i1> @llvm.is.fpclass.v2f16(<2 x half> [[X]], i32 504)
-; CHECK-NEXT:    [[OR:%.*]] = or <2 x i1> [[IS_FINITE_CLASS]], [[IS_SUBNORMAL]]
-; CHECK-NEXT:    ret <2 x i1> [[OR]]
+; CHECK-NEXT:    [[IS_FINITE_CLASS:%.*]] = call <2 x i1> @llvm.is.fpclass.v2f16(<2 x half> [[X:%.*]], i32 504)
+; CHECK-NEXT:    ret <2 x i1> [[IS_FINITE_CLASS]]
 ;
   %fabs = call <2 x half> @llvm.fabs.v2f16(<2 x half> %x)
   %is.subnormal = fcmp olt <2 x half> %fabs, <half 0xH0400, half 0xH0400>
@@ -342,10 +315,8 @@ define i1 @class_normal_or_fcmp_une_zero(half %x) {
 
 define i1 @fcmp_oeq_inf_xor_class_normal(half %x) {
 ; CHECK-LABEL: @fcmp_oeq_inf_xor_class_normal(
-; CHECK-NEXT:    [[OEQ_INF:%.*]] = fcmp oeq half [[X:%.*]], 0xH7C00
-; CHECK-NEXT:    [[CLASS:%.*]] = call i1 @llvm.is.fpclass.f16(half [[X]], i32 264)
-; CHECK-NEXT:    [[XOR:%.*]] = xor i1 [[OEQ_INF]], [[CLASS]]
-; CHECK-NEXT:    ret i1 [[XOR]]
+; CHECK-NEXT:    [[CLASS:%.*]] = call i1 @llvm.is.fpclass.f16(half [[X:%.*]], i32 776)
+; CHECK-NEXT:    ret i1 [[CLASS]]
 ;
   %oeq.inf = fcmp oeq half %x, 0xH7C00
   %class = call i1 @llvm.is.fpclass.f16(half %x, i32 264)
@@ -355,10 +326,8 @@ define i1 @fcmp_oeq_inf_xor_class_normal(half %x) {
 
 define i1 @class_normal_xor_fcmp_oeq_inf(half %x) {
 ; CHECK-LABEL: @class_normal_xor_fcmp_oeq_inf(
-; CHECK-NEXT:    [[OEQ_INF:%.*]] = fcmp oeq half [[X:%.*]], 0xH7C00
-; CHECK-NEXT:    [[CLASS:%.*]] = call i1 @llvm.is.fpclass.f16(half [[X]], i32 264)
-; CHECK-NEXT:    [[XOR:%.*]] = xor i1 [[CLASS]], [[OEQ_INF]]
-; CHECK-NEXT:    ret i1 [[XOR]]
+; CHECK-NEXT:    [[CLASS:%.*]] = call i1 @llvm.is.fpclass.f16(half [[X:%.*]], i32 776)
+; CHECK-NEXT:    ret i1 [[CLASS]]
 ;
   %oeq.inf = fcmp oeq half %x, 0xH7C00
   %class = call i1 @llvm.is.fpclass.f16(half %x, i32 264)
index 631eec0..0d9a3b9 100644 (file)
@@ -607,10 +607,8 @@ define <2 x i1> @test_class_xor_vector(<2 x float> %x) {
 
 define i1 @test_fold_or_class_f32_0(float %a) {
 ; CHECK-LABEL: @test_fold_or_class_f32_0(
-; CHECK-NEXT:    [[CLASS0:%.*]] = call i1 @llvm.is.fpclass.f32(float [[A:%.*]], i32 1)
-; CHECK-NEXT:    [[CLASS1:%.*]] = fcmp uno float [[A]], 0.000000e+00
-; CHECK-NEXT:    [[OR:%.*]] = or i1 [[CLASS0]], [[CLASS1]]
-; CHECK-NEXT:    ret i1 [[OR]]
+; CHECK-NEXT:    [[CLASS0:%.*]] = fcmp uno float [[A:%.*]], 0.000000e+00
+; CHECK-NEXT:    ret i1 [[CLASS0]]
 ;
   %class0 = call i1 @llvm.is.fpclass.f32(float %a, i32 1)
   %class1 = fcmp uno float %a, 0.000000e+00
@@ -620,10 +618,8 @@ define i1 @test_fold_or_class_f32_0(float %a) {
 
 define i1 @test_fold_or3_class_f32_0(float %a) {
 ; CHECK-LABEL: @test_fold_or3_class_f32_0(
-; CHECK-NEXT:    [[CLASS0:%.*]] = fcmp uno float [[A:%.*]], 0.000000e+00
-; CHECK-NEXT:    [[CLASS2:%.*]] = call i1 @llvm.is.fpclass.f32(float [[A]], i32 4)
-; CHECK-NEXT:    [[OR_1:%.*]] = or i1 [[CLASS0]], [[CLASS2]]
-; CHECK-NEXT:    ret i1 [[OR_1]]
+; CHECK-NEXT:    [[CLASS2:%.*]] = call i1 @llvm.is.fpclass.f32(float [[A:%.*]], i32 7)
+; CHECK-NEXT:    ret i1 [[CLASS2]]
 ;
   %class0 = call i1 @llvm.is.fpclass.f32(float %a, i32 1)
   %class1 = call i1 @llvm.is.fpclass.f32(float %a, i32 2)
@@ -635,24 +631,7 @@ define i1 @test_fold_or3_class_f32_0(float %a) {
 
 define i1 @test_fold_or_all_tests_class_f32_0(float %a) {
 ; CHECK-LABEL: @test_fold_or_all_tests_class_f32_0(
-; CHECK-NEXT:    [[CLASS0:%.*]] = fcmp uno float [[A:%.*]], 0.000000e+00
-; CHECK-NEXT:    [[CLASS2:%.*]] = call i1 @llvm.is.fpclass.f32(float [[A]], i32 4)
-; CHECK-NEXT:    [[CLASS3:%.*]] = call i1 @llvm.is.fpclass.f32(float [[A]], i32 8)
-; CHECK-NEXT:    [[CLASS4:%.*]] = call i1 @llvm.is.fpclass.f32(float [[A]], i32 16)
-; CHECK-NEXT:    [[CLASS5:%.*]] = call i1 @llvm.is.fpclass.f32(float [[A]], i32 32)
-; CHECK-NEXT:    [[CLASS6:%.*]] = call i1 @llvm.is.fpclass.f32(float [[A]], i32 64)
-; CHECK-NEXT:    [[CLASS7:%.*]] = call i1 @llvm.is.fpclass.f32(float [[A]], i32 128)
-; CHECK-NEXT:    [[CLASS8:%.*]] = call i1 @llvm.is.fpclass.f32(float [[A]], i32 256)
-; CHECK-NEXT:    [[CLASS9:%.*]] = call i1 @llvm.is.fpclass.f32(float [[A]], i32 512)
-; CHECK-NEXT:    [[OR_1:%.*]] = or i1 [[CLASS0]], [[CLASS2]]
-; CHECK-NEXT:    [[OR_2:%.*]] = or i1 [[OR_1]], [[CLASS3]]
-; CHECK-NEXT:    [[OR_3:%.*]] = or i1 [[OR_2]], [[CLASS4]]
-; CHECK-NEXT:    [[OR_4:%.*]] = or i1 [[OR_3]], [[CLASS5]]
-; CHECK-NEXT:    [[OR_5:%.*]] = or i1 [[OR_4]], [[CLASS6]]
-; CHECK-NEXT:    [[OR_6:%.*]] = or i1 [[OR_5]], [[CLASS7]]
-; CHECK-NEXT:    [[OR_7:%.*]] = or i1 [[OR_6]], [[CLASS8]]
-; CHECK-NEXT:    [[OR_8:%.*]] = or i1 [[OR_7]], [[CLASS9]]
-; CHECK-NEXT:    ret i1 [[OR_8]]
+; CHECK-NEXT:    ret i1 true
 ;
   %class0 = call i1 @llvm.is.fpclass.f32(float %a, i32 1)
   %class1 = call i1 @llvm.is.fpclass.f32(float %a, i32 2)
@@ -759,9 +738,7 @@ define <2 x i1> @test_fold_or_class_v2f32(<2 x float> %a) {
 define i1 @test_fold_and_class_f32_0(float %a) {
 ; CHECK-LABEL: @test_fold_and_class_f32_0(
 ; CHECK-NEXT:    [[CLASS0:%.*]] = call i1 @llvm.is.fpclass.f32(float [[A:%.*]], i32 1)
-; CHECK-NEXT:    [[CLASS1:%.*]] = fcmp uno float [[A]], 0.000000e+00
-; CHECK-NEXT:    [[AND:%.*]] = and i1 [[CLASS0]], [[CLASS1]]
-; CHECK-NEXT:    ret i1 [[AND]]
+; CHECK-NEXT:    ret i1 [[CLASS0]]
 ;
   %class0 = call i1 @llvm.is.fpclass.f32(float %a, i32 1)
   %class1 = fcmp uno float %a, 0.000000e+00
@@ -771,12 +748,8 @@ define i1 @test_fold_and_class_f32_0(float %a) {
 
 define i1 @test_fold_and3_class_f32_0(float %a) {
 ; CHECK-LABEL: @test_fold_and3_class_f32_0(
-; CHECK-NEXT:    [[CLASS0:%.*]] = fcmp uno float [[A:%.*]], 0.000000e+00
-; CHECK-NEXT:    [[CLASS1:%.*]] = call i1 @llvm.is.fpclass.f32(float [[A]], i32 2)
-; CHECK-NEXT:    [[CLASS2:%.*]] = call i1 @llvm.is.fpclass.f32(float [[A]], i32 7)
-; CHECK-NEXT:    [[AND_0:%.*]] = and i1 [[CLASS0]], [[CLASS1]]
-; CHECK-NEXT:    [[AND_1:%.*]] = and i1 [[AND_0]], [[CLASS2]]
-; CHECK-NEXT:    ret i1 [[AND_1]]
+; CHECK-NEXT:    [[CLASS1:%.*]] = call i1 @llvm.is.fpclass.f32(float [[A:%.*]], i32 2)
+; CHECK-NEXT:    ret i1 [[CLASS1]]
 ;
   %class0 = call i1 @llvm.is.fpclass.f32(float %a, i32 3)
   %class1 = call i1 @llvm.is.fpclass.f32(float %a, i32 2)
@@ -814,24 +787,7 @@ define i1 @test_fold_and_all_tests_class_f32_0(float %a) {
 
 define i1 @test_fold_and_not_all_tests_class_f32_0(float %a) {
 ; CHECK-LABEL: @test_fold_and_not_all_tests_class_f32_0(
-; CHECK-NEXT:    [[CLASS0:%.*]] = fcmp ord float [[A:%.*]], 0.000000e+00
-; CHECK-NEXT:    [[CLASS2:%.*]] = call i1 @llvm.is.fpclass.f32(float [[A]], i32 1019)
-; CHECK-NEXT:    [[CLASS3:%.*]] = call i1 @llvm.is.fpclass.f32(float [[A]], i32 1015)
-; CHECK-NEXT:    [[CLASS4:%.*]] = call i1 @llvm.is.fpclass.f32(float [[A]], i32 1007)
-; CHECK-NEXT:    [[CLASS5:%.*]] = call i1 @llvm.is.fpclass.f32(float [[A]], i32 991)
-; CHECK-NEXT:    [[CLASS6:%.*]] = call i1 @llvm.is.fpclass.f32(float [[A]], i32 959)
-; CHECK-NEXT:    [[CLASS7:%.*]] = call i1 @llvm.is.fpclass.f32(float [[A]], i32 895)
-; CHECK-NEXT:    [[CLASS8:%.*]] = call i1 @llvm.is.fpclass.f32(float [[A]], i32 767)
-; CHECK-NEXT:    [[CLASS9:%.*]] = call i1 @llvm.is.fpclass.f32(float [[A]], i32 511)
-; CHECK-NEXT:    [[AND_1:%.*]] = and i1 [[CLASS0]], [[CLASS2]]
-; CHECK-NEXT:    [[AND_2:%.*]] = and i1 [[AND_1]], [[CLASS3]]
-; CHECK-NEXT:    [[AND_3:%.*]] = and i1 [[AND_2]], [[CLASS4]]
-; CHECK-NEXT:    [[AND_4:%.*]] = and i1 [[AND_3]], [[CLASS5]]
-; CHECK-NEXT:    [[AND_5:%.*]] = and i1 [[AND_4]], [[CLASS6]]
-; CHECK-NEXT:    [[AND_6:%.*]] = and i1 [[AND_5]], [[CLASS7]]
-; CHECK-NEXT:    [[AND_7:%.*]] = and i1 [[AND_6]], [[CLASS8]]
-; CHECK-NEXT:    [[AND_8:%.*]] = and i1 [[AND_7]], [[CLASS9]]
-; CHECK-NEXT:    ret i1 [[AND_8]]
+; CHECK-NEXT:    ret i1 false
 ;
   %class0 = call i1 @llvm.is.fpclass.f32(float %a, i32 1022)
   %class1 = call i1 @llvm.is.fpclass.f32(float %a, i32 1021)
@@ -958,10 +914,8 @@ define <2 x i1> @test_fold_and_class_v2f32(<2 x float> %a) {
 
 define i1 @test_fold_xor_class_f32_0(float %a) {
 ; CHECK-LABEL: @test_fold_xor_class_f32_0(
-; CHECK-NEXT:    [[CLASS0:%.*]] = call i1 @llvm.is.fpclass.f32(float [[A:%.*]], i32 1)
-; CHECK-NEXT:    [[CLASS1:%.*]] = fcmp uno float [[A]], 0.000000e+00
-; CHECK-NEXT:    [[XOR:%.*]] = xor i1 [[CLASS0]], [[CLASS1]]
-; CHECK-NEXT:    ret i1 [[XOR]]
+; CHECK-NEXT:    [[CLASS0:%.*]] = call i1 @llvm.is.fpclass.f32(float [[A:%.*]], i32 2)
+; CHECK-NEXT:    ret i1 [[CLASS0]]
 ;
   %class0 = call i1 @llvm.is.fpclass.f32(float %a, i32 1)
   %class1 = fcmp uno float %a, 0.000000e+00
@@ -971,10 +925,8 @@ define i1 @test_fold_xor_class_f32_0(float %a) {
 
 define i1 @test_fold_xor3_class_f32_0(float %a) {
 ; CHECK-LABEL: @test_fold_xor3_class_f32_0(
-; CHECK-NEXT:    [[CLASS0:%.*]] = fcmp uno float [[A:%.*]], 0.000000e+00
-; CHECK-NEXT:    [[CLASS2:%.*]] = call i1 @llvm.is.fpclass.f32(float [[A]], i32 4)
-; CHECK-NEXT:    [[XOR_1:%.*]] = xor i1 [[CLASS0]], [[CLASS2]]
-; CHECK-NEXT:    ret i1 [[XOR_1]]
+; CHECK-NEXT:    [[CLASS2:%.*]] = call i1 @llvm.is.fpclass.f32(float [[A:%.*]], i32 7)
+; CHECK-NEXT:    ret i1 [[CLASS2]]
 ;
   %class0 = call i1 @llvm.is.fpclass.f32(float %a, i32 1)
   %class1 = call i1 @llvm.is.fpclass.f32(float %a, i32 2)
@@ -986,24 +938,7 @@ define i1 @test_fold_xor3_class_f32_0(float %a) {
 
 define i1 @test_fold_xor_all_tests_class_f32_0(float %a) {
 ; CHECK-LABEL: @test_fold_xor_all_tests_class_f32_0(
-; CHECK-NEXT:    [[CLASS0:%.*]] = fcmp uno float [[A:%.*]], 0.000000e+00
-; CHECK-NEXT:    [[CLASS2:%.*]] = call i1 @llvm.is.fpclass.f32(float [[A]], i32 4)
-; CHECK-NEXT:    [[CLASS3:%.*]] = call i1 @llvm.is.fpclass.f32(float [[A]], i32 8)
-; CHECK-NEXT:    [[CLASS4:%.*]] = call i1 @llvm.is.fpclass.f32(float [[A]], i32 16)
-; CHECK-NEXT:    [[CLASS5:%.*]] = call i1 @llvm.is.fpclass.f32(float [[A]], i32 32)
-; CHECK-NEXT:    [[CLASS6:%.*]] = call i1 @llvm.is.fpclass.f32(float [[A]], i32 64)
-; CHECK-NEXT:    [[CLASS7:%.*]] = call i1 @llvm.is.fpclass.f32(float [[A]], i32 128)
-; CHECK-NEXT:    [[CLASS8:%.*]] = call i1 @llvm.is.fpclass.f32(float [[A]], i32 256)
-; CHECK-NEXT:    [[CLASS9:%.*]] = call i1 @llvm.is.fpclass.f32(float [[A]], i32 512)
-; CHECK-NEXT:    [[XOR_1:%.*]] = xor i1 [[CLASS0]], [[CLASS2]]
-; CHECK-NEXT:    [[XOR_2:%.*]] = xor i1 [[XOR_1]], [[CLASS3]]
-; CHECK-NEXT:    [[XOR_3:%.*]] = xor i1 [[XOR_2]], [[CLASS4]]
-; CHECK-NEXT:    [[XOR_4:%.*]] = xor i1 [[XOR_3]], [[CLASS5]]
-; CHECK-NEXT:    [[XOR_5:%.*]] = xor i1 [[XOR_4]], [[CLASS6]]
-; CHECK-NEXT:    [[XOR_6:%.*]] = xor i1 [[XOR_5]], [[CLASS7]]
-; CHECK-NEXT:    [[XOR_7:%.*]] = xor i1 [[XOR_6]], [[CLASS8]]
-; CHECK-NEXT:    [[XOR_8:%.*]] = xor i1 [[XOR_7]], [[CLASS9]]
-; CHECK-NEXT:    ret i1 [[XOR_8]]
+; CHECK-NEXT:    ret i1 true
 ;
   %class0 = call i1 @llvm.is.fpclass.f32(float %a, i32 1)
   %class1 = call i1 @llvm.is.fpclass.f32(float %a, i32 2)