diff --git a/llvm/include/llvm/CodeGen/ISDOpcodes.h b/llvm/include/llvm/CodeGen/ISDOpcodes.h index 80ef32aff62ae0..1042318343987d 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 41fed692c70255..b28a8b118de7a1 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 432209e8ecb0ae..08dce6a2fc9e54 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 720393158aa5e2..9e7a4030d24ece 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 ee31baac7b3219..0c0e700f6abcac 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 bbf1b0fd590ef9..a30d174c22d6ac 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(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(N2) && "NoFPClass is not Constant"); + FPClassTest NoFPClass = static_cast(N2->getAsZExtVal()); + assert(llvm::to_underlying(NoFPClass) <= + BitmaskEnumDetail::Mask() && + "FPClassTest value too large"); + break; + } case ISD::AssertSext: case ISD::AssertZext: { EVT EVT = cast(N2)->getVT(); diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp index 8e74a076cc013e..d99bd230c7861d 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(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 8faf97271d99e9..6f846bedf3c82f 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 1bc30336a02bf2..586728a44571ed 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 00000000000000..3139aa0ef0bf6b --- /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 00000000000000..aaeb6c11fa5987 --- /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 00000000000000..b9737fe1175b9f --- /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 00000000000000..55f0af904a38da --- /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 + +@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@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@GOTPCREL(%rip), %rax +; SSE-NEXT: movlps %xmm0, (%rax) +; SSE-NEXT: retq +entry: + store <2 x float> %e.coerce, ptr @gf, align 8 + ret void +}