[llvm] 780054d - CodeGen: Add ISD::AssertNoFPClass (#138839)

via llvm-commits llvm-commits at lists.llvm.org
Thu May 15 01:05:19 PDT 2025


Author: YunQiang Su
Date: 2025-05-15T16:05:15+08:00
New Revision: 780054d3ff18075a6bc433029f336931792b1d2d

URL: https://github.com/llvm/llvm-project/commit/780054d3ff18075a6bc433029f336931792b1d2d
DIFF: https://github.com/llvm/llvm-project/commit/780054d3ff18075a6bc433029f336931792b1d2d.diff

LOG: CodeGen: Add ISD::AssertNoFPClass (#138839)

It is used to mark a value that we are sure that it is not some fcType.
The examples include:

  * An arguments of a function is marked with nofpclass
  * Output value of an intrinsic can be sure to not be some type

So that the following operation can make some assumptions.

Added: 
    llvm/test/CodeGen/AArch64/nofpclass.ll
    llvm/test/CodeGen/ARM/nofpclass.ll
    llvm/test/CodeGen/Mips/nofpclass.ll
    llvm/test/CodeGen/X86/nofpclass.ll

Modified: 
    llvm/include/llvm/CodeGen/ISDOpcodes.h
    llvm/include/llvm/Target/TargetSelectionDAG.td
    llvm/lib/CodeGen/SelectionDAG/LegalizeFloatTypes.cpp
    llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h
    llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp
    llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
    llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
    llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp
    llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/CodeGen/ISDOpcodes.h b/llvm/include/llvm/CodeGen/ISDOpcodes.h
index 80ef32aff62ae..1042318343987 100644
--- a/llvm/include/llvm/CodeGen/ISDOpcodes.h
+++ b/llvm/include/llvm/CodeGen/ISDOpcodes.h
@@ -67,6 +67,15 @@ enum NodeType {
   /// poisoned the assertion will not be true for that value.
   AssertAlign,
 
+  /// AssertNoFPClass - These nodes record if a register contains a float
+  /// value that is known to be not some type.
+  /// This node takes two operands.  The first is the node that is known
+  /// never to be some float types; the second is a constant value with
+  /// the value of FPClassTest (casted to uint32_t).
+  /// NOTE: In case of the source value (or any vector element value) is
+  /// poisoned the assertion will not be true for that value.
+  AssertNoFPClass,
+
   /// Various leaf nodes.
   BasicBlock,
   VALUETYPE,

diff  --git a/llvm/include/llvm/Target/TargetSelectionDAG.td b/llvm/include/llvm/Target/TargetSelectionDAG.td
index 41fed692c7025..b28a8b118de7a 100644
--- a/llvm/include/llvm/Target/TargetSelectionDAG.td
+++ b/llvm/include/llvm/Target/TargetSelectionDAG.td
@@ -875,6 +875,7 @@ def SDT_assert : SDTypeProfile<1, 1,
   [SDTCisInt<0>, SDTCisInt<1>, SDTCisSameAs<1, 0>]>;
 def assertsext : SDNode<"ISD::AssertSext", SDT_assert>;
 def assertzext : SDNode<"ISD::AssertZext", SDT_assert>;
+def assertnofpclass : SDNode<"ISD::AssertNoFPClass", SDTFPUnaryOp>;
 def assertalign : SDNode<"ISD::AssertAlign", SDT_assert>;
 
 def convergencectrl_anchor : SDNode<"ISD::CONVERGENCECTRL_ANCHOR",

diff  --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeFloatTypes.cpp b/llvm/lib/CodeGen/SelectionDAG/LegalizeFloatTypes.cpp
index 432209e8ecb0a..08dce6a2fc9e5 100644
--- a/llvm/lib/CodeGen/SelectionDAG/LegalizeFloatTypes.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeFloatTypes.cpp
@@ -168,6 +168,7 @@ void DAGTypeLegalizer::SoftenFloatResult(SDNode *N, unsigned ResNo) {
     case ISD::POISON:
     case ISD::UNDEF:       R = SoftenFloatRes_UNDEF(N); break;
     case ISD::VAARG:       R = SoftenFloatRes_VAARG(N); break;
+    case ISD::AssertNoFPClass:       R = GetSoftenedFloat(N->getOperand(0)); break;
     case ISD::VECREDUCE_FADD:
     case ISD::VECREDUCE_FMUL:
     case ISD::VECREDUCE_FMIN:
@@ -2582,6 +2583,7 @@ bool DAGTypeLegalizer::PromoteFloatOperand(SDNode *N, unsigned OpNo) {
     case ISD::LLROUND:
     case ISD::LRINT:
     case ISD::LLRINT:     R = PromoteFloatOp_UnaryOp(N, OpNo); break;
+    case ISD::AssertNoFPClass:     R = PromoteFloatOp_AssertNoFPClass(N, OpNo); break;
     case ISD::FP_TO_SINT_SAT:
     case ISD::FP_TO_UINT_SAT:
                           R = PromoteFloatOp_FP_TO_XINT_SAT(N, OpNo); break;
@@ -2640,6 +2642,12 @@ SDValue DAGTypeLegalizer::PromoteFloatOp_UnaryOp(SDNode *N, unsigned OpNo) {
   return DAG.getNode(N->getOpcode(), SDLoc(N), N->getValueType(0), Op);
 }
 
+// Convert the promoted float value to the desired integer type
+SDValue DAGTypeLegalizer::PromoteFloatOp_AssertNoFPClass(SDNode *N,
+                                                         unsigned OpNo) {
+  return GetPromotedFloat(N->getOperand(0));
+}
+
 SDValue DAGTypeLegalizer::PromoteFloatOp_FP_TO_XINT_SAT(SDNode *N,
                                                         unsigned OpNo) {
   SDValue Op = GetPromotedFloat(N->getOperand(0));
@@ -2804,6 +2812,9 @@ void DAGTypeLegalizer::PromoteFloatResult(SDNode *N, unsigned ResNo) {
     case ISD::FTAN:
     case ISD::FTANH:
     case ISD::FCANONICALIZE: R = PromoteFloatRes_UnaryOp(N); break;
+    case ISD::AssertNoFPClass:
+      R = PromoteFloatRes_AssertNoFPClass(N);
+      break;
 
     // Binary FP Operations
     case ISD::FADD:
@@ -2996,10 +3007,16 @@ SDValue DAGTypeLegalizer::PromoteFloatRes_UnaryOp(SDNode *N) {
   EVT VT = N->getValueType(0);
   EVT NVT = TLI.getTypeToTransformTo(*DAG.getContext(), VT);
   SDValue Op = GetPromotedFloat(N->getOperand(0));
-
   return DAG.getNode(N->getOpcode(), SDLoc(N), NVT, Op);
 }
 
+// Unary operation with a more non-float operand where the result and the
+// operand have PromoteFloat type action.  Construct a new SDNode with the
+// promoted float value of the old operand.
+SDValue DAGTypeLegalizer::PromoteFloatRes_AssertNoFPClass(SDNode *N) {
+  return GetPromotedFloat(N->getOperand(0));
+}
+
 // Binary operations where the result and both operands have PromoteFloat type
 // action.  Construct a new SDNode with the promoted float values of the old
 // operands.
@@ -3281,6 +3298,9 @@ void DAGTypeLegalizer::SoftPromoteHalfResult(SDNode *N, unsigned ResNo) {
   case ISD::FTAN:
   case ISD::FTANH:
   case ISD::FCANONICALIZE: R = SoftPromoteHalfRes_UnaryOp(N); break;
+  case ISD::AssertNoFPClass:
+    R = SoftPromoteHalfRes_AssertNoFPClass(N);
+    break;
 
   // Binary FP Operations
   case ISD::FADD:
@@ -3607,6 +3627,10 @@ SDValue DAGTypeLegalizer::SoftPromoteHalfRes_UnaryOp(SDNode *N) {
   return DAG.getNode(GetPromotionOpcode(NVT, OVT), dl, MVT::i16, Res);
 }
 
+SDValue DAGTypeLegalizer::SoftPromoteHalfRes_AssertNoFPClass(SDNode *N) {
+  return GetSoftPromotedHalf(N->getOperand(0));
+}
+
 SDValue DAGTypeLegalizer::SoftPromoteHalfRes_BinOp(SDNode *N) {
   EVT OVT = N->getValueType(0);
   EVT NVT = TLI.getTypeToTransformTo(*DAG.getContext(), OVT);

diff  --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h b/llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h
index 720393158aa5e..9e7a4030d24ec 100644
--- a/llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h
+++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h
@@ -772,6 +772,7 @@ class LLVM_LIBRARY_VISIBILITY DAGTypeLegalizer {
   SDValue PromoteFloatRes_SELECT(SDNode *N);
   SDValue PromoteFloatRes_SELECT_CC(SDNode *N);
   SDValue PromoteFloatRes_UnaryOp(SDNode *N);
+  SDValue PromoteFloatRes_AssertNoFPClass(SDNode *N);
   SDValue PromoteFloatRes_UNDEF(SDNode *N);
   SDValue BitcastToInt_ATOMIC_SWAP(SDNode *N);
   SDValue PromoteFloatRes_XINT_TO_FP(SDNode *N);
@@ -785,6 +786,7 @@ class LLVM_LIBRARY_VISIBILITY DAGTypeLegalizer {
   SDValue PromoteFloatOp_FP_EXTEND(SDNode *N, unsigned OpNo);
   SDValue PromoteFloatOp_STRICT_FP_EXTEND(SDNode *N, unsigned OpNo);
   SDValue PromoteFloatOp_UnaryOp(SDNode *N, unsigned OpNo);
+  SDValue PromoteFloatOp_AssertNoFPClass(SDNode *N, unsigned OpNo);
   SDValue PromoteFloatOp_FP_TO_XINT_SAT(SDNode *N, unsigned OpNo);
   SDValue PromoteFloatOp_STORE(SDNode *N, unsigned OpNo);
   SDValue PromoteFloatOp_ATOMIC_STORE(SDNode *N, unsigned OpNo);
@@ -820,6 +822,7 @@ class LLVM_LIBRARY_VISIBILITY DAGTypeLegalizer {
   SDValue SoftPromoteHalfRes_SELECT(SDNode *N);
   SDValue SoftPromoteHalfRes_SELECT_CC(SDNode *N);
   SDValue SoftPromoteHalfRes_UnaryOp(SDNode *N);
+  SDValue SoftPromoteHalfRes_AssertNoFPClass(SDNode *N);
   SDValue SoftPromoteHalfRes_XINT_TO_FP(SDNode *N);
   SDValue SoftPromoteHalfRes_UNDEF(SDNode *N);
   SDValue SoftPromoteHalfRes_VECREDUCE(SDNode *N);

diff  --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp b/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp
index ee31baac7b321..0c0e700f6abca 100644
--- a/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp
@@ -61,6 +61,7 @@ void DAGTypeLegalizer::ScalarizeVectorResult(SDNode *N, unsigned ResNo) {
   case ISD::AssertZext:
   case ISD::AssertSext:
   case ISD::FPOWI:
+  case ISD::AssertNoFPClass:
     R = ScalarizeVecRes_UnaryOpWithExtraInput(N);
     break;
   case ISD::INSERT_VECTOR_ELT: R = ScalarizeVecRes_INSERT_VECTOR_ELT(N); break;
@@ -1276,6 +1277,7 @@ void DAGTypeLegalizer::SplitVectorResult(SDNode *N, unsigned ResNo) {
   case ISD::UINT_TO_FP:
   case ISD::VP_UINT_TO_FP:
   case ISD::FCANONICALIZE:
+  case ISD::AssertNoFPClass:
     SplitVecRes_UnaryOp(N, Lo, Hi);
     break;
   case ISD::ADDRSPACECAST:
@@ -2614,7 +2616,7 @@ void DAGTypeLegalizer::SplitVecRes_UnaryOp(SDNode *N, SDValue &Lo,
   const SDNodeFlags Flags = N->getFlags();
   unsigned Opcode = N->getOpcode();
   if (N->getNumOperands() <= 2) {
-    if (Opcode == ISD::FP_ROUND) {
+    if (Opcode == ISD::FP_ROUND || Opcode == ISD::AssertNoFPClass) {
       Lo = DAG.getNode(Opcode, dl, LoVT, Lo, N->getOperand(1), Flags);
       Hi = DAG.getNode(Opcode, dl, HiVT, Hi, N->getOperand(1), Flags);
     } else {
@@ -4872,6 +4874,7 @@ void DAGTypeLegalizer::WidenVectorResult(SDNode *N, unsigned ResNo) {
   case ISD::FREEZE:
   case ISD::ARITH_FENCE:
   case ISD::FCANONICALIZE:
+  case ISD::AssertNoFPClass:
     Res = WidenVecRes_Unary(N);
     break;
   case ISD::FMA: case ISD::VP_FMA:
@@ -5616,6 +5619,9 @@ SDValue DAGTypeLegalizer::WidenVecRes_Unary(SDNode *N) {
   SDValue InOp = GetWidenedVector(N->getOperand(0));
   if (N->getNumOperands() == 1)
     return DAG.getNode(N->getOpcode(), SDLoc(N), WidenVT, InOp, N->getFlags());
+  if (N->getOpcode() == ISD::AssertNoFPClass)
+    return DAG.getNode(N->getOpcode(), SDLoc(N), WidenVT, InOp,
+                       N->getOperand(1), N->getFlags());
 
   assert(N->getNumOperands() == 3 && "Unexpected number of operands!");
   assert(N->isVPOpcode() && "Expected VP opcode");

diff  --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
index bbf1b0fd590ef..a30d174c22d6a 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
@@ -5831,6 +5831,15 @@ bool SelectionDAG::isKnownNeverNaN(SDValue Op, const APInt &DemandedElts,
         return false;
     return true;
   }
+  case ISD::AssertNoFPClass: {
+    FPClassTest NoFPClass =
+        static_cast<FPClassTest>(Op.getConstantOperandVal(1));
+    if ((NoFPClass & fcNan) == fcNan)
+      return true;
+    if (SNaN && (NoFPClass & fcSNan) == fcSNan)
+      return true;
+    return isKnownNeverNaN(Op.getOperand(0), DemandedElts, SNaN, Depth + 1);
+  }
   default:
     if (Opcode >= ISD::BUILTIN_OP_END || Opcode == ISD::INTRINSIC_WO_CHAIN ||
         Opcode == ISD::INTRINSIC_W_CHAIN || Opcode == ISD::INTRINSIC_VOID) {
@@ -7490,6 +7499,16 @@ SDValue SelectionDAG::getNode(unsigned Opcode, const SDLoc &DL, EVT VT,
            N2.getOpcode() == ISD::TargetConstant && "Invalid FP_ROUND!");
     if (N1.getValueType() == VT) return N1;  // noop conversion.
     break;
+  case ISD::AssertNoFPClass: {
+    assert(N1.getValueType().isFloatingPoint() &&
+           "AssertNoFPClass is used for a non-floating type");
+    assert(isa<ConstantSDNode>(N2) && "NoFPClass is not Constant");
+    FPClassTest NoFPClass = static_cast<FPClassTest>(N2->getAsZExtVal());
+    assert(llvm::to_underlying(NoFPClass) <=
+               BitmaskEnumDetail::Mask<FPClassTest>() &&
+           "FPClassTest value too large");
+    break;
+  }
   case ISD::AssertSext:
   case ISD::AssertZext: {
     EVT EVT = cast<VTSDNode>(N2)->getVT();

diff  --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
index 8e74a076cc013..d99bd230c7861 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
@@ -11803,9 +11803,18 @@ void SelectionDAGISel::LowerArguments(const Function &F) {
         else if (Arg.hasAttribute(Attribute::ZExt))
           AssertOp = ISD::AssertZext;
 
-        ArgValues.push_back(getCopyFromParts(DAG, dl, &InVals[i], NumParts,
-                                             PartVT, VT, nullptr, NewRoot,
-                                             F.getCallingConv(), AssertOp));
+        SDValue OutVal =
+            getCopyFromParts(DAG, dl, &InVals[i], NumParts, PartVT, VT, nullptr,
+                             NewRoot, F.getCallingConv(), AssertOp);
+
+        FPClassTest NoFPClass = Arg.getNoFPClass();
+        if (NoFPClass != fcNone) {
+          SDValue SDNoFPClass = DAG.getTargetConstant(
+              static_cast<uint64_t>(NoFPClass), dl, MVT::i32);
+          OutVal = DAG.getNode(ISD::AssertNoFPClass, dl, OutVal.getValueType(),
+                               OutVal, SDNoFPClass);
+        }
+        ArgValues.push_back(OutVal);
       }
 
       i += NumParts;

diff  --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp
index 8faf97271d99e..6f846bedf3c82 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp
@@ -124,6 +124,7 @@ std::string SDNode::getOperationName(const SelectionDAG *G) const {
   case ISD::TokenFactor:                return "TokenFactor";
   case ISD::AssertSext:                 return "AssertSext";
   case ISD::AssertZext:                 return "AssertZext";
+  case ISD::AssertNoFPClass:            return "AssertNoFPClass";
   case ISD::AssertAlign:                return "AssertAlign";
 
   case ISD::BasicBlock:                 return "BasicBlock";

diff  --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp
index 1bc30336a02bf..586728a44571e 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp
@@ -3264,6 +3264,7 @@ void SelectionDAGISel::SelectCodeCommon(SDNode *NodeToMatch,
     return;
   case ISD::AssertSext:
   case ISD::AssertZext:
+  case ISD::AssertNoFPClass:
   case ISD::AssertAlign:
     ReplaceUses(SDValue(NodeToMatch, 0), NodeToMatch->getOperand(0));
     CurDAG->RemoveDeadNode(NodeToMatch);

diff  --git a/llvm/test/CodeGen/AArch64/nofpclass.ll b/llvm/test/CodeGen/AArch64/nofpclass.ll
new file mode 100644
index 0000000000000..3139aa0ef0bf6
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/nofpclass.ll
@@ -0,0 +1,182 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
+; RUN: llc --mtriple=aarch64-linux-gnu < %s | FileCheck %s
+
+define float @f(float nofpclass(nan) %a, float nofpclass(nan) %b) {
+; CHECK-LABEL: f:
+; CHECK:       // %bb.0: // %entry
+; CHECK-NEXT:    fmaxnm s0, s0, s1
+; CHECK-NEXT:    ret
+entry:
+  %cond = tail call float @llvm.maximumnum.f32(float %a, float %b)
+  ret float %cond
+}
+
+define <4 x float> @fv4f32(<4 x float> nofpclass(nan) %a, <4 x float> nofpclass(nan) %b) {
+; CHECK-LABEL: fv4f32:
+; CHECK:       // %bb.0: // %entry
+; CHECK-NEXT:    fmaxnm v0.4s, v0.4s, v1.4s
+; CHECK-NEXT:    ret
+entry:
+  %c = call <4 x float> @llvm.maximumnum.v4f32(<4 x float> %a, <4 x float> %b)
+  ret <4 x float> %c
+}
+
+define {float, float} @m({float, float} nofpclass(nan) %a0, {float, float} nofpclass(nan) %a1) {
+; CHECK-LABEL: m:
+; CHECK:       // %bb.0: // %entry
+; CHECK-NEXT:    fmaxnm s1, s1, s3
+; CHECK-NEXT:    fmaxnm s0, s0, s2
+; CHECK-NEXT:    ret
+entry:
+  %a0f0 = extractvalue {float, float} %a0, 0
+  %a0f1 = extractvalue {float, float} %a0, 1
+  %a1f0 = extractvalue {float, float} %a1, 0
+  %a1f1 = extractvalue {float, float} %a1, 1
+  %max0 = tail call float @llvm.maximumnum.f32(float %a0f0, float %a1f0)
+  %max1 = tail call float @llvm.maximumnum.f32(float %a0f1, float %a1f1)
+  %ret0 = insertvalue {float, float} poison, float %max0, 0
+  %ret1 = insertvalue {float, float} %ret0, float %max1, 1
+  ret {float, float} %ret1
+}
+
+define [2 x float] @mA([2 x float] nofpclass(nan) %a0, [2 x float] nofpclass(nan) %a1) {
+; CHECK-LABEL: mA:
+; CHECK:       // %bb.0: // %entry
+; CHECK-NEXT:    fmaxnm s1, s1, s3
+; CHECK-NEXT:    fmaxnm s0, s0, s2
+; CHECK-NEXT:    ret
+entry:
+  %a0f0 = extractvalue [2 x float] %a0, 0
+  %a0f1 = extractvalue [2 x float] %a0, 1
+  %a1f0 = extractvalue [2 x float] %a1, 0
+  %a1f1 = extractvalue [2 x float] %a1, 1
+  %max0 = tail call float @llvm.maximumnum.f32(float %a0f0, float %a1f0)
+  %max1 = tail call float @llvm.maximumnum.f32(float %a0f1, float %a1f1)
+  %ret0 = insertvalue [2 x float] poison, float %max0, 0
+  %ret1 = insertvalue [2 x float] %ret0, float %max1, 1
+  ret [2 x float] %ret1
+}
+
+define float @fS(float nofpclass(snan) %a, float nofpclass(snan) %b) {
+; CHECK-LABEL: fS:
+; CHECK:       // %bb.0: // %entry
+; CHECK-NEXT:    fmaxnm s0, s0, s1
+; CHECK-NEXT:    ret
+entry:
+  %cond = tail call float @llvm.maximumnum.f32(float %a, float %b)
+  ret float %cond
+}
+
+define <4 x float> @fSv4f32(<4 x float> nofpclass(snan) %a, <4 x float> nofpclass(snan) %b) {
+; CHECK-LABEL: fSv4f32:
+; CHECK:       // %bb.0: // %entry
+; CHECK-NEXT:    fmaxnm v0.4s, v0.4s, v1.4s
+; CHECK-NEXT:    ret
+entry:
+  %c = call <4 x float> @llvm.maximumnum.v4f32(<4 x float> %a, <4 x float> %b)
+  ret <4 x float> %c
+}
+
+define {float, float} @mS({float, float} nofpclass(snan) %a0, {float, float} nofpclass(snan) %a1) {
+; CHECK-LABEL: mS:
+; CHECK:       // %bb.0: // %entry
+; CHECK-NEXT:    fmaxnm s1, s1, s3
+; CHECK-NEXT:    fmaxnm s0, s0, s2
+; CHECK-NEXT:    ret
+entry:
+  %a0f0 = extractvalue {float, float} %a0, 0
+  %a0f1 = extractvalue {float, float} %a0, 1
+  %a1f0 = extractvalue {float, float} %a1, 0
+  %a1f1 = extractvalue {float, float} %a1, 1
+  %max0 = tail call float @llvm.maximumnum.f32(float %a0f0, float %a1f0)
+  %max1 = tail call float @llvm.maximumnum.f32(float %a0f1, float %a1f1)
+  %ret0 = insertvalue {float, float} poison, float %max0, 0
+  %ret1 = insertvalue {float, float} %ret0, float %max1, 1
+  ret {float, float} %ret1
+}
+
+define [2 x float] @mAS([2 x float] nofpclass(snan) %a0, [2 x float] nofpclass(snan) %a1) {
+; CHECK-LABEL: mAS:
+; CHECK:       // %bb.0: // %entry
+; CHECK-NEXT:    fmaxnm s1, s1, s3
+; CHECK-NEXT:    fmaxnm s0, s0, s2
+; CHECK-NEXT:    ret
+entry:
+  %a0f0 = extractvalue [2 x float] %a0, 0
+  %a0f1 = extractvalue [2 x float] %a0, 1
+  %a1f0 = extractvalue [2 x float] %a1, 0
+  %a1f1 = extractvalue [2 x float] %a1, 1
+  %max0 = tail call float @llvm.maximumnum.f32(float %a0f0, float %a1f0)
+  %max1 = tail call float @llvm.maximumnum.f32(float %a0f1, float %a1f1)
+  %ret0 = insertvalue [2 x float] poison, float %max0, 0
+  %ret1 = insertvalue [2 x float] %ret0, float %max1, 1
+  ret [2 x float] %ret1
+}
+
+define float @fQ(float nofpclass(qnan) %a, float nofpclass(qnan) %b) {
+; CHECK-LABEL: fQ:
+; CHECK:       // %bb.0: // %entry
+; CHECK-NEXT:    fminnm s1, s1, s1
+; CHECK-NEXT:    fminnm s0, s0, s0
+; CHECK-NEXT:    fmaxnm s0, s0, s1
+; CHECK-NEXT:    ret
+entry:
+  %cond = tail call float @llvm.maximumnum.f32(float %a, float %b)
+  ret float %cond
+}
+
+define <4 x float> @fQv4f32(<4 x float> nofpclass(qnan) %a, <4 x float> nofpclass(qnan) %b) {
+; CHECK-LABEL: fQv4f32:
+; CHECK:       // %bb.0: // %entry
+; CHECK-NEXT:    fminnm v1.4s, v1.4s, v1.4s
+; CHECK-NEXT:    fminnm v0.4s, v0.4s, v0.4s
+; CHECK-NEXT:    fmaxnm v0.4s, v0.4s, v1.4s
+; CHECK-NEXT:    ret
+entry:
+  %c = call <4 x float> @llvm.maximumnum.v4f32(<4 x float> %a, <4 x float> %b)
+  ret <4 x float> %c
+}
+
+define {float, float} @mQ({float, float} nofpclass(qnan) %a0, {float, float} nofpclass(qnan) %a1) {
+; CHECK-LABEL: mQ:
+; CHECK:       // %bb.0: // %entry
+; CHECK-NEXT:    fminnm s2, s2, s2
+; CHECK-NEXT:    fminnm s0, s0, s0
+; CHECK-NEXT:    fminnm s3, s3, s3
+; CHECK-NEXT:    fminnm s1, s1, s1
+; CHECK-NEXT:    fmaxnm s0, s0, s2
+; CHECK-NEXT:    fmaxnm s1, s1, s3
+; CHECK-NEXT:    ret
+entry:
+  %a0f0 = extractvalue {float, float} %a0, 0
+  %a0f1 = extractvalue {float, float} %a0, 1
+  %a1f0 = extractvalue {float, float} %a1, 0
+  %a1f1 = extractvalue {float, float} %a1, 1
+  %max0 = tail call float @llvm.maximumnum.f32(float %a0f0, float %a1f0)
+  %max1 = tail call float @llvm.maximumnum.f32(float %a0f1, float %a1f1)
+  %ret0 = insertvalue {float, float} poison, float %max0, 0
+  %ret1 = insertvalue {float, float} %ret0, float %max1, 1
+  ret {float, float} %ret1
+}
+
+define [2 x float] @mAQ([2 x float] nofpclass(qnan) %a0, [2 x float] nofpclass(qnan) %a1) {
+; CHECK-LABEL: mAQ:
+; CHECK:       // %bb.0: // %entry
+; CHECK-NEXT:    fminnm s2, s2, s2
+; CHECK-NEXT:    fminnm s0, s0, s0
+; CHECK-NEXT:    fminnm s3, s3, s3
+; CHECK-NEXT:    fminnm s1, s1, s1
+; CHECK-NEXT:    fmaxnm s0, s0, s2
+; CHECK-NEXT:    fmaxnm s1, s1, s3
+; CHECK-NEXT:    ret
+entry:
+  %a0f0 = extractvalue [2 x float] %a0, 0
+  %a0f1 = extractvalue [2 x float] %a0, 1
+  %a1f0 = extractvalue [2 x float] %a1, 0
+  %a1f1 = extractvalue [2 x float] %a1, 1
+  %max0 = tail call float @llvm.maximumnum.f32(float %a0f0, float %a1f0)
+  %max1 = tail call float @llvm.maximumnum.f32(float %a0f1, float %a1f1)
+  %ret0 = insertvalue [2 x float] poison, float %max0, 0
+  %ret1 = insertvalue [2 x float] %ret0, float %max1, 1
+  ret [2 x float] %ret1
+}

diff  --git a/llvm/test/CodeGen/ARM/nofpclass.ll b/llvm/test/CodeGen/ARM/nofpclass.ll
new file mode 100644
index 0000000000000..aaeb6c11fa598
--- /dev/null
+++ b/llvm/test/CodeGen/ARM/nofpclass.ll
@@ -0,0 +1,37 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
+; RUN: llc -mtriple=armv8-unknown-none-eabi < %s | FileCheck %s --check-prefixes=CHECK,HARD
+; RUN: llc -mtriple=armv8-unknown-none-eabi -mattr=+soft-float < %s | FileCheck %s --check-prefixes=CHECK,SOFT
+
+define nofpclass(nan inf) half @f1(half returned nofpclass(nan inf) %x) {
+; CHECK-LABEL: f1:
+; CHECK:       @ %bb.0: @ %entry
+; CHECK-NEXT:    bx lr
+entry:
+  ret half %x
+}
+
+define noundef half @f2(half nofpclass(nan) %a) {
+; HARD-LABEL: f2:
+; HARD:       @ %bb.0: @ %entry
+; HARD-NEXT:    vmov.f32 s0, #1.000000e+00
+; HARD-NEXT:    vmov s2, r0
+; HARD-NEXT:    vcvtb.f32.f16 s2, s2
+; HARD-NEXT:    vadd.f32 s0, s2, s0
+; HARD-NEXT:    vcvtb.f16.f32 s0, s0
+; HARD-NEXT:    vmov r0, s0
+; HARD-NEXT:    bx lr
+;
+; SOFT-LABEL: f2:
+; SOFT:       @ %bb.0: @ %entry
+; SOFT-NEXT:    .save {r11, lr}
+; SOFT-NEXT:    push {r11, lr}
+; SOFT-NEXT:    uxth r0, r0
+; SOFT-NEXT:    bl __aeabi_h2f
+; SOFT-NEXT:    mov r1, #1065353216
+; SOFT-NEXT:    bl __aeabi_fadd
+; SOFT-NEXT:    bl __aeabi_f2h
+; SOFT-NEXT:    pop {r11, pc}
+entry:
+  %0 = fadd half %a, 0xH3C00
+  ret half %0
+}

diff  --git a/llvm/test/CodeGen/Mips/nofpclass.ll b/llvm/test/CodeGen/Mips/nofpclass.ll
new file mode 100644
index 0000000000000..b9737fe1175b9
--- /dev/null
+++ b/llvm/test/CodeGen/Mips/nofpclass.ll
@@ -0,0 +1,224 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
+; RUN: llc --mtriple=mipsisa32r6-linux-gnu < %s | FileCheck %s --check-prefix=MIPS32R6
+; RUN: llc --mtriple=mipsisa64r6-linux-gnu < %s | FileCheck %s --check-prefix=MIPS64R6
+
+define float @f(float nofpclass(nan) %a, float nofpclass(nan) %b) {
+; MIPS32R6-LABEL: f:
+; MIPS32R6:       # %bb.0: # %entry
+; MIPS32R6-NEXT:    jr $ra
+; MIPS32R6-NEXT:    max.s $f0, $f12, $f14
+;
+; MIPS64R6-LABEL: f:
+; MIPS64R6:       # %bb.0: # %entry
+; MIPS64R6-NEXT:    jr $ra
+; MIPS64R6-NEXT:    max.s $f0, $f12, $f13
+entry:
+  %cond = tail call float @llvm.maximumnum.f32(float %a, float %b)
+  ret float %cond
+}
+
+define {float, float} @m({float, float} nofpclass(nan) %a0, {float, float} nofpclass(nan) %a1) {
+; MIPS32R6-LABEL: m:
+; MIPS32R6:       # %bb.0: # %entry
+; MIPS32R6-NEXT:    mtc1 $6, $f0
+; MIPS32R6-NEXT:    max.s $f0, $f12, $f0
+; MIPS32R6-NEXT:    mtc1 $7, $f1
+; MIPS32R6-NEXT:    jr $ra
+; MIPS32R6-NEXT:    max.s $f2, $f14, $f1
+;
+; MIPS64R6-LABEL: m:
+; MIPS64R6:       # %bb.0: # %entry
+; MIPS64R6-NEXT:    max.s $f0, $f12, $f14
+; MIPS64R6-NEXT:    jr $ra
+; MIPS64R6-NEXT:    max.s $f2, $f13, $f15
+entry:
+  %a0f0 = extractvalue {float, float} %a0, 0
+  %a0f1 = extractvalue {float, float} %a0, 1
+  %a1f0 = extractvalue {float, float} %a1, 0
+  %a1f1 = extractvalue {float, float} %a1, 1
+  %max0 = tail call float @llvm.maximumnum.f32(float %a0f0, float %a1f0)
+  %max1 = tail call float @llvm.maximumnum.f32(float %a0f1, float %a1f1)
+  %ret0 = insertvalue {float, float} poison, float %max0, 0
+  %ret1 = insertvalue {float, float} %ret0, float %max1, 1
+  ret {float, float} %ret1
+}
+
+define [2 x float] @mA([2 x float] nofpclass(nan) %a0, [2 x float] nofpclass(nan) %a1) {
+; MIPS32R6-LABEL: mA:
+; MIPS32R6:       # %bb.0: # %entry
+; MIPS32R6-NEXT:    mtc1 $6, $f0
+; MIPS32R6-NEXT:    max.s $f0, $f12, $f0
+; MIPS32R6-NEXT:    mtc1 $7, $f1
+; MIPS32R6-NEXT:    jr $ra
+; MIPS32R6-NEXT:    max.s $f2, $f14, $f1
+;
+; MIPS64R6-LABEL: mA:
+; MIPS64R6:       # %bb.0: # %entry
+; MIPS64R6-NEXT:    max.s $f0, $f12, $f14
+; MIPS64R6-NEXT:    jr $ra
+; MIPS64R6-NEXT:    max.s $f2, $f13, $f15
+entry:
+  %a0f0 = extractvalue [2 x float] %a0, 0
+  %a0f1 = extractvalue [2 x float] %a0, 1
+  %a1f0 = extractvalue [2 x float] %a1, 0
+  %a1f1 = extractvalue [2 x float] %a1, 1
+  %max0 = tail call float @llvm.maximumnum.f32(float %a0f0, float %a1f0)
+  %max1 = tail call float @llvm.maximumnum.f32(float %a0f1, float %a1f1)
+  %ret0 = insertvalue [2 x float] poison, float %max0, 0
+  %ret1 = insertvalue [2 x float] %ret0, float %max1, 1
+  ret [2 x float] %ret1
+}
+
+define float @fS(float nofpclass(snan) %a, float nofpclass(snan) %b) {
+; MIPS32R6-LABEL: fS:
+; MIPS32R6:       # %bb.0: # %entry
+; MIPS32R6-NEXT:    jr $ra
+; MIPS32R6-NEXT:    max.s $f0, $f12, $f14
+;
+; MIPS64R6-LABEL: fS:
+; MIPS64R6:       # %bb.0: # %entry
+; MIPS64R6-NEXT:    jr $ra
+; MIPS64R6-NEXT:    max.s $f0, $f12, $f13
+entry:
+  %cond = tail call float @llvm.maximumnum.f32(float %a, float %b)
+  ret float %cond
+}
+
+define {float, float} @mS({float, float} nofpclass(snan) %a0, {float, float} nofpclass(snan) %a1) {
+; MIPS32R6-LABEL: mS:
+; MIPS32R6:       # %bb.0: # %entry
+; MIPS32R6-NEXT:    mtc1 $6, $f0
+; MIPS32R6-NEXT:    max.s $f0, $f12, $f0
+; MIPS32R6-NEXT:    mtc1 $7, $f1
+; MIPS32R6-NEXT:    jr $ra
+; MIPS32R6-NEXT:    max.s $f2, $f14, $f1
+;
+; MIPS64R6-LABEL: mS:
+; MIPS64R6:       # %bb.0: # %entry
+; MIPS64R6-NEXT:    max.s $f0, $f12, $f14
+; MIPS64R6-NEXT:    jr $ra
+; MIPS64R6-NEXT:    max.s $f2, $f13, $f15
+entry:
+  %a0f0 = extractvalue {float, float} %a0, 0
+  %a0f1 = extractvalue {float, float} %a0, 1
+  %a1f0 = extractvalue {float, float} %a1, 0
+  %a1f1 = extractvalue {float, float} %a1, 1
+  %max0 = tail call float @llvm.maximumnum.f32(float %a0f0, float %a1f0)
+  %max1 = tail call float @llvm.maximumnum.f32(float %a0f1, float %a1f1)
+  %ret0 = insertvalue {float, float} poison, float %max0, 0
+  %ret1 = insertvalue {float, float} %ret0, float %max1, 1
+  ret {float, float} %ret1
+}
+
+define [2 x float] @mAS([2 x float] nofpclass(snan) %a0, [2 x float] nofpclass(snan) %a1) {
+; MIPS32R6-LABEL: mAS:
+; MIPS32R6:       # %bb.0: # %entry
+; MIPS32R6-NEXT:    mtc1 $6, $f0
+; MIPS32R6-NEXT:    max.s $f0, $f12, $f0
+; MIPS32R6-NEXT:    mtc1 $7, $f1
+; MIPS32R6-NEXT:    jr $ra
+; MIPS32R6-NEXT:    max.s $f2, $f14, $f1
+;
+; MIPS64R6-LABEL: mAS:
+; MIPS64R6:       # %bb.0: # %entry
+; MIPS64R6-NEXT:    max.s $f0, $f12, $f14
+; MIPS64R6-NEXT:    jr $ra
+; MIPS64R6-NEXT:    max.s $f2, $f13, $f15
+entry:
+  %a0f0 = extractvalue [2 x float] %a0, 0
+  %a0f1 = extractvalue [2 x float] %a0, 1
+  %a1f0 = extractvalue [2 x float] %a1, 0
+  %a1f1 = extractvalue [2 x float] %a1, 1
+  %max0 = tail call float @llvm.maximumnum.f32(float %a0f0, float %a1f0)
+  %max1 = tail call float @llvm.maximumnum.f32(float %a0f1, float %a1f1)
+  %ret0 = insertvalue [2 x float] poison, float %max0, 0
+  %ret1 = insertvalue [2 x float] %ret0, float %max1, 1
+  ret [2 x float] %ret1
+}
+
+define float @fQ(float nofpclass(qnan) %a, float nofpclass(qnan) %b) {
+; MIPS32R6-LABEL: fQ:
+; MIPS32R6:       # %bb.0: # %entry
+; MIPS32R6-NEXT:    min.s $f0, $f14, $f14
+; MIPS32R6-NEXT:    min.s $f1, $f12, $f12
+; MIPS32R6-NEXT:    jr $ra
+; MIPS32R6-NEXT:    max.s $f0, $f1, $f0
+;
+; MIPS64R6-LABEL: fQ:
+; MIPS64R6:       # %bb.0: # %entry
+; MIPS64R6-NEXT:    min.s $f0, $f13, $f13
+; MIPS64R6-NEXT:    min.s $f1, $f12, $f12
+; MIPS64R6-NEXT:    jr $ra
+; MIPS64R6-NEXT:    max.s $f0, $f1, $f0
+entry:
+  %cond = tail call float @llvm.maximumnum.f32(float %a, float %b)
+  ret float %cond
+}
+
+define {float, float} @mQ({float, float} nofpclass(qnan) %a0, {float, float} nofpclass(qnan) %a1) {
+; MIPS32R6-LABEL: mQ:
+; MIPS32R6:       # %bb.0: # %entry
+; MIPS32R6-NEXT:    min.s $f0, $f12, $f12
+; MIPS32R6-NEXT:    mtc1 $6, $f1
+; MIPS32R6-NEXT:    min.s $f1, $f1, $f1
+; MIPS32R6-NEXT:    max.s $f0, $f0, $f1
+; MIPS32R6-NEXT:    min.s $f1, $f14, $f14
+; MIPS32R6-NEXT:    mtc1 $7, $f2
+; MIPS32R6-NEXT:    min.s $f2, $f2, $f2
+; MIPS32R6-NEXT:    jr $ra
+; MIPS32R6-NEXT:    max.s $f2, $f1, $f2
+;
+; MIPS64R6-LABEL: mQ:
+; MIPS64R6:       # %bb.0: # %entry
+; MIPS64R6-NEXT:    min.s $f0, $f14, $f14
+; MIPS64R6-NEXT:    min.s $f1, $f12, $f12
+; MIPS64R6-NEXT:    max.s $f0, $f1, $f0
+; MIPS64R6-NEXT:    min.s $f1, $f15, $f15
+; MIPS64R6-NEXT:    min.s $f2, $f13, $f13
+; MIPS64R6-NEXT:    jr $ra
+; MIPS64R6-NEXT:    max.s $f2, $f2, $f1
+entry:
+  %a0f0 = extractvalue {float, float} %a0, 0
+  %a0f1 = extractvalue {float, float} %a0, 1
+  %a1f0 = extractvalue {float, float} %a1, 0
+  %a1f1 = extractvalue {float, float} %a1, 1
+  %max0 = tail call float @llvm.maximumnum.f32(float %a0f0, float %a1f0)
+  %max1 = tail call float @llvm.maximumnum.f32(float %a0f1, float %a1f1)
+  %ret0 = insertvalue {float, float} poison, float %max0, 0
+  %ret1 = insertvalue {float, float} %ret0, float %max1, 1
+  ret {float, float} %ret1
+}
+
+define [2 x float] @mAQ([2 x float] nofpclass(qnan) %a0, [2 x float] nofpclass(qnan) %a1) {
+; MIPS32R6-LABEL: mAQ:
+; MIPS32R6:       # %bb.0: # %entry
+; MIPS32R6-NEXT:    min.s $f0, $f12, $f12
+; MIPS32R6-NEXT:    mtc1 $6, $f1
+; MIPS32R6-NEXT:    min.s $f1, $f1, $f1
+; MIPS32R6-NEXT:    max.s $f0, $f0, $f1
+; MIPS32R6-NEXT:    min.s $f1, $f14, $f14
+; MIPS32R6-NEXT:    mtc1 $7, $f2
+; MIPS32R6-NEXT:    min.s $f2, $f2, $f2
+; MIPS32R6-NEXT:    jr $ra
+; MIPS32R6-NEXT:    max.s $f2, $f1, $f2
+;
+; MIPS64R6-LABEL: mAQ:
+; MIPS64R6:       # %bb.0: # %entry
+; MIPS64R6-NEXT:    min.s $f0, $f14, $f14
+; MIPS64R6-NEXT:    min.s $f1, $f12, $f12
+; MIPS64R6-NEXT:    max.s $f0, $f1, $f0
+; MIPS64R6-NEXT:    min.s $f1, $f15, $f15
+; MIPS64R6-NEXT:    min.s $f2, $f13, $f13
+; MIPS64R6-NEXT:    jr $ra
+; MIPS64R6-NEXT:    max.s $f2, $f2, $f1
+entry:
+  %a0f0 = extractvalue [2 x float] %a0, 0
+  %a0f1 = extractvalue [2 x float] %a0, 1
+  %a1f0 = extractvalue [2 x float] %a1, 0
+  %a1f1 = extractvalue [2 x float] %a1, 1
+  %max0 = tail call float @llvm.maximumnum.f32(float %a0f0, float %a1f0)
+  %max1 = tail call float @llvm.maximumnum.f32(float %a0f1, float %a1f1)
+  %ret0 = insertvalue [2 x float] poison, float %max0, 0
+  %ret1 = insertvalue [2 x float] %ret0, float %max1, 1
+  ret [2 x float] %ret1
+}

diff  --git a/llvm/test/CodeGen/X86/nofpclass.ll b/llvm/test/CodeGen/X86/nofpclass.ll
new file mode 100644
index 0000000000000..55f0af904a38d
--- /dev/null
+++ b/llvm/test/CodeGen/X86/nofpclass.ll
@@ -0,0 +1,25 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
+; RUN: llc < %s -mtriple=x86_64-unknown-unknown -mattr=-sse2,-sse | FileCheck %s --check-prefix=NOSSE
+; RUN: llc < %s -mtriple=x86_64-unknown-unknown | FileCheck %s --check-prefix=SSE
+
+ at gf = global { float, float } zeroinitializer, align 8
+
+define void @f(<2 x float> noundef nofpclass(nan inf) %e.coerce) {
+; NOSSE-LABEL: f:
+; NOSSE:       # %bb.0: # %entry
+; NOSSE-NEXT:    flds {{[0-9]+}}(%rsp)
+; NOSSE-NEXT:    flds {{[0-9]+}}(%rsp)
+; NOSSE-NEXT:    movq gf at GOTPCREL(%rip), %rax
+; NOSSE-NEXT:    fstps 4(%rax)
+; NOSSE-NEXT:    fstps (%rax)
+; NOSSE-NEXT:    retq
+;
+; SSE-LABEL: f:
+; SSE:       # %bb.0: # %entry
+; SSE-NEXT:    movq gf at GOTPCREL(%rip), %rax
+; SSE-NEXT:    movlps %xmm0, (%rax)
+; SSE-NEXT:    retq
+entry:
+  store <2 x float> %e.coerce, ptr @gf, align 8
+  ret void
+}


        


More information about the llvm-commits mailing list