From c5c73f7e84d4c3e200763fbff719954e0b4a8b84 Mon Sep 17 00:00:00 2001 From: Matt Arsenault Date: Wed, 13 Aug 2014 15:21:58 -0700 Subject: [PATCH 1/2] Add minnum / maxnum intrinsics --- docs/LangRef.rst | 87 +++++++- include/llvm/CodeGen/ISDOpcodes.h | 3 +- include/llvm/CodeGen/RuntimeLibcalls.h | 10 + include/llvm/CodeGen/SelectionDAG.h | 5 +- include/llvm/IR/Constants.h | 3 + include/llvm/IR/Intrinsics.td | 2 + include/llvm/IR/PatternMatch.h | 12 ++ include/llvm/Target/TargetSelectionDAG.td | 2 + include/llvm/Transforms/Utils/VectorUtils.h | 10 + lib/Analysis/ConstantFolding.cpp | 23 +++ lib/Analysis/TargetTransformInfo.cpp | 2 + lib/Analysis/ValueTracking.cpp | 2 + lib/CodeGen/BasicTargetTransformInfo.cpp | 2 + lib/CodeGen/SelectionDAG/DAGCombiner.cpp | 55 +++++ lib/CodeGen/SelectionDAG/LegalizeDAG.cpp | 10 + lib/CodeGen/SelectionDAG/LegalizeFloatTypes.cpp | 50 +++++ lib/CodeGen/SelectionDAG/LegalizeTypes.h | 4 + lib/CodeGen/SelectionDAG/LegalizeVectorOps.cpp | 2 + lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp | 7 + lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp | 44 ++++ lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h | 1 + lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp | 2 + lib/CodeGen/TargetLoweringBase.cpp | 20 ++ lib/Target/R600/AMDGPUISelLowering.cpp | 2 + lib/Target/R600/SIISelLowering.cpp | 5 + lib/Target/R600/SIInstructions.td | 8 +- lib/Target/X86/X86ISelLowering.cpp | 2 + lib/Transforms/InstCombine/InstCombineCalls.cpp | 81 ++++++++ lib/Transforms/Vectorize/BBVectorize.cpp | 2 + test/CodeGen/PowerPC/fmax.ll | 86 ++++++++ test/CodeGen/PowerPC/fmin.ll | 86 ++++++++ test/CodeGen/R600/fmax.f64.ll | 75 +++++++ test/CodeGen/R600/fmax.ll | 198 ++++++++++++++++-- test/CodeGen/R600/fmin.f64.ll | 75 +++++++ test/CodeGen/R600/fmin.ll | 196 ++++++++++++++++-- test/CodeGen/X86/fmax.ll | 50 +++++ test/CodeGen/X86/fmin.ll | 95 +++++++++ test/Transforms/InstCombine/maxnum.ll | 222 +++++++++++++++++++++ test/Transforms/InstCombine/minnum.ll | 244 +++++++++++++++++++++++ test/Transforms/LoopVectorize/intrinsic.ll | 56 ++++++ test/Transforms/SimplifyCFG/speculate-math.ll | 35 ++++ 41 files changed, 1844 insertions(+), 32 deletions(-) create mode 100644 test/CodeGen/PowerPC/fmax.ll create mode 100644 test/CodeGen/PowerPC/fmin.ll create mode 100644 test/CodeGen/R600/fmax.f64.ll create mode 100644 test/CodeGen/R600/fmin.f64.ll create mode 100644 test/CodeGen/X86/fmax.ll create mode 100644 test/CodeGen/X86/fmin.ll create mode 100644 test/Transforms/InstCombine/maxnum.ll create mode 100644 test/Transforms/InstCombine/minnum.ll diff --git a/docs/LangRef.rst b/docs/LangRef.rst index f2d6982..b7fffcd 100644 --- a/docs/LangRef.rst +++ b/docs/LangRef.rst @@ -8020,9 +8020,9 @@ all types however. declare float @llvm.fabs.f32(float %Val) declare double @llvm.fabs.f64(double %Val) - declare x86_fp80 @llvm.fabs.f80(x86_fp80 %Val) + declare x86_fp80 @llvm.fabs.f80(x86_fp80 %Val) declare fp128 @llvm.fabs.f128(fp128 %Val) - declare ppc_fp128 @llvm.fabs.ppcf128(ppc_fp128 %Val) + declare ppc_fp128 @llvm.fabs.ppcf128(ppc_fp128 %Val) Overview: """"""""" @@ -8042,6 +8042,89 @@ Semantics: This function returns the same values as the libm ``fabs`` functions would, and handles error conditions in the same way. +'``llvm.minnum.*``' Intrinsic +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Syntax: +""""""" + +This is an overloaded intrinsic. You can use ``llvm.minnum`` on any +floating point or vector of floating point type. Not all targets support +all types however. + +:: + + declare float @llvm.minnum.f32(float %Val) + declare double @llvm.minnum.f64(double %Val) + declare x86_fp80 @llvm.minnum.f80(x86_fp80 %Val) + declare fp128 @llvm.minnum.f128(fp128 %Val) + declare ppc_fp128 @llvm.minnum.ppcf128(ppc_fp128 %Val) + +Overview: +""""""""" + +The '``llvm.minnum.*``' intrinsics return the minimum of the two +arguments. + + +Arguments: +"""""""""" + +The arguments and return value are floating point numbers of the same +type. + +Semantics: +"""""""""" + +Follows the IEEE-754 semantics for minNum, which also match for libm's +fmin. + +If either operand is a NaN, returns the other non-NaN operand. Returns +NaN only if both operands are NaN. If the operands compare equal, +returns a value that compares equal to both operands. This means that +fmin(+/-0.0, +/-0.0) could return either -0.0 or 0.0. + +'``llvm.maxnum.*``' Intrinsic +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Syntax: +""""""" + +This is an overloaded intrinsic. You can use ``llvm.maxnum`` on any +floating point or vector of floating point type. Not all targets support +all types however. + +:: + + declare float @llvm.maxnum.f32(float %Val) + declare double @llvm.maxnum.f64(double %Val) + declare x86_fp80 @llvm.maxnum.f80(x86_fp80 %Val) + declare fp128 @llvm.maxnum.f128(fp128 %Val) + declare ppc_fp128 @llvm.maxnum.ppcf128(ppc_fp128 %Val) + +Overview: +""""""""" + +The '``llvm.maxnum.*``' intrinsics return the maximum of the two +arguments. + + +Arguments: +"""""""""" + +The arguments and return value are floating point numbers of the same +type. + +Semantics: +"""""""""" +Follows the IEEE-754 semantics for maxNum, which also match for libm's +fmax. + +If either operand is a NaN, returns the other non-NaN operand. Returns +NaN only if both operands are NaN. If the operands compare equal, +returns a value that compares equal to both operands. This means that +fmax(+/-0.0, +/-0.0) could return either -0.0 or 0.0. + '``llvm.copysign.*``' Intrinsic ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/include/llvm/CodeGen/ISDOpcodes.h b/include/llvm/CodeGen/ISDOpcodes.h index 8444761..0ca896b 100644 --- a/include/llvm/CodeGen/ISDOpcodes.h +++ b/include/llvm/CodeGen/ISDOpcodes.h @@ -485,7 +485,8 @@ namespace ISD { FNEG, FABS, FSQRT, FSIN, FCOS, FPOWI, FPOW, FLOG, FLOG2, FLOG10, FEXP, FEXP2, FCEIL, FTRUNC, FRINT, FNEARBYINT, FROUND, FFLOOR, - + FMIN, FMAX, + /// FSINCOS - Compute both fsin and fcos as a single operation. FSINCOS, diff --git a/include/llvm/CodeGen/RuntimeLibcalls.h b/include/llvm/CodeGen/RuntimeLibcalls.h index 81db8a2..64c9c47 100644 --- a/include/llvm/CodeGen/RuntimeLibcalls.h +++ b/include/llvm/CodeGen/RuntimeLibcalls.h @@ -203,6 +203,16 @@ namespace RTLIB { COPYSIGN_F80, COPYSIGN_F128, COPYSIGN_PPCF128, + FMIN_F32, + FMIN_F64, + FMIN_F80, + FMIN_F128, + FMIN_PPCF128, + FMAX_F32, + FMAX_F64, + FMAX_F80, + FMAX_F128, + FMAX_PPCF128, // CONVERSION FPEXT_F64_F128, diff --git a/include/llvm/CodeGen/SelectionDAG.h b/include/llvm/CodeGen/SelectionDAG.h index eaec6dc..3e091d9 100644 --- a/include/llvm/CodeGen/SelectionDAG.h +++ b/include/llvm/CodeGen/SelectionDAG.h @@ -1064,7 +1064,10 @@ public: case ISD::SADDO: case ISD::UADDO: case ISD::ADDC: - case ISD::ADDE: return true; + case ISD::ADDE: + case ISD::FMIN: + case ISD::FMAX: + return true; default: return false; } } diff --git a/include/llvm/IR/Constants.h b/include/llvm/IR/Constants.h index 122dda2..4823be0 100644 --- a/include/llvm/IR/Constants.h +++ b/include/llvm/IR/Constants.h @@ -264,6 +264,9 @@ public: /// isNegative - Return true if the sign bit is set. bool isNegative() const { return Val.isNegative(); } + /// isInfinity - Return true if the value is infinity + bool isInfinity() const { return Val.isInfinity(); } + /// isNaN - Return true if the value is a NaN. bool isNaN() const { return Val.isNaN(); } diff --git a/include/llvm/IR/Intrinsics.td b/include/llvm/IR/Intrinsics.td index 548866f..8c76243 100644 --- a/include/llvm/IR/Intrinsics.td +++ b/include/llvm/IR/Intrinsics.td @@ -328,6 +328,8 @@ let Properties = [IntrNoMem] in { def int_exp : Intrinsic<[llvm_anyfloat_ty], [LLVMMatchType<0>]>; def int_exp2 : Intrinsic<[llvm_anyfloat_ty], [LLVMMatchType<0>]>; def int_fabs : Intrinsic<[llvm_anyfloat_ty], [LLVMMatchType<0>]>; + def int_minnum : Intrinsic<[llvm_anyfloat_ty], [LLVMMatchType<0>, LLVMMatchType<0>]>; + def int_maxnum : Intrinsic<[llvm_anyfloat_ty], [LLVMMatchType<0>, LLVMMatchType<0>]>; def int_copysign : Intrinsic<[llvm_anyfloat_ty], [LLVMMatchType<0>, LLVMMatchType<0>]>; def int_floor : Intrinsic<[llvm_anyfloat_ty], [LLVMMatchType<0>]>; diff --git a/include/llvm/IR/PatternMatch.h b/include/llvm/IR/PatternMatch.h index 530ad82..5083c1b 100644 --- a/include/llvm/IR/PatternMatch.h +++ b/include/llvm/IR/PatternMatch.h @@ -1228,6 +1228,18 @@ m_BSwap(const Opnd0 &Op0) { return m_Intrinsic(Op0); } +template +inline typename m_Intrinsic_Ty::Ty +m_FMin(const Opnd0 &Op0, const Opnd1 &Op1) { + return m_Intrinsic(Op0, Op1); +} + +template +inline typename m_Intrinsic_Ty::Ty +m_FMax(const Opnd0 &Op0, const Opnd1 &Op1) { + return m_Intrinsic(Op0, Op1); +} + } // end namespace PatternMatch } // end namespace llvm diff --git a/include/llvm/Target/TargetSelectionDAG.td b/include/llvm/Target/TargetSelectionDAG.td index 2d822de..880cca4 100644 --- a/include/llvm/Target/TargetSelectionDAG.td +++ b/include/llvm/Target/TargetSelectionDAG.td @@ -369,6 +369,8 @@ def fdiv : SDNode<"ISD::FDIV" , SDTFPBinOp>; def frem : SDNode<"ISD::FREM" , SDTFPBinOp>; def fma : SDNode<"ISD::FMA" , SDTFPTernaryOp>; def fabs : SDNode<"ISD::FABS" , SDTFPUnaryOp>; +def fmin : SDNode<"ISD::FMIN" , SDTFPBinOp>; +def fmax : SDNode<"ISD::FMAX" , SDTFPBinOp>; def fgetsign : SDNode<"ISD::FGETSIGN" , SDTFPToIntOp>; def fneg : SDNode<"ISD::FNEG" , SDTFPUnaryOp>; def fsqrt : SDNode<"ISD::FSQRT" , SDTFPUnaryOp>; diff --git a/include/llvm/Transforms/Utils/VectorUtils.h b/include/llvm/Transforms/Utils/VectorUtils.h index 44a7149..ac2eabd 100644 --- a/include/llvm/Transforms/Utils/VectorUtils.h +++ b/include/llvm/Transforms/Utils/VectorUtils.h @@ -36,6 +36,8 @@ static inline bool isTriviallyVectorizable(Intrinsic::ID ID) { case Intrinsic::log10: case Intrinsic::log2: case Intrinsic::fabs: + case Intrinsic::minnum: + case Intrinsic::maxnum: case Intrinsic::copysign: case Intrinsic::floor: case Intrinsic::ceil: @@ -153,6 +155,14 @@ getIntrinsicIDForCall(CallInst *CI, const TargetLibraryInfo *TLI) { case LibFunc::fabsf: case LibFunc::fabsl: return checkUnaryFloatSignature(*CI, Intrinsic::fabs); + case LibFunc::fmin: + case LibFunc::fminf: + case LibFunc::fminl: + return checkBinaryFloatSignature(*CI, Intrinsic::minnum); + case LibFunc::fmax: + case LibFunc::fmaxf: + case LibFunc::fmaxl: + return checkBinaryFloatSignature(*CI, Intrinsic::maxnum); case LibFunc::copysign: case LibFunc::copysignf: case LibFunc::copysignl: diff --git a/lib/Analysis/ConstantFolding.cpp b/lib/Analysis/ConstantFolding.cpp index 88e2558..18fc1c6 100644 --- a/lib/Analysis/ConstantFolding.cpp +++ b/lib/Analysis/ConstantFolding.cpp @@ -1236,6 +1236,8 @@ Constant *llvm::ConstantFoldLoadThroughGEPIndices(Constant *C, bool llvm::canConstantFoldCallTo(const Function *F) { switch (F->getIntrinsicID()) { case Intrinsic::fabs: + case Intrinsic::minnum: + case Intrinsic::maxnum: case Intrinsic::log: case Intrinsic::log2: case Intrinsic::log10: @@ -1627,6 +1629,27 @@ static Constant *ConstantFoldScalarCall(StringRef Name, unsigned IntrinsicID, V1.copySign(V2); return ConstantFP::get(Ty->getContext(), V1); } + + if (IntrinsicID == Intrinsic::minnum) { + const APFloat &V1 = Op1->getValueAPF(); + const APFloat &V2 = Op2->getValueAPF(); + if (V1.isNaN()) + return Op2; + if (V2.isNaN()) + return Op1; + return (V2.compare(V1) == APFloat::cmpLessThan) ? Op2 : Op1; + } + + if (IntrinsicID == Intrinsic::maxnum) { + const APFloat &V1 = Op1->getValueAPF(); + const APFloat &V2 = Op2->getValueAPF(); + if (V1.isNaN()) + return Op2; + if (V2.isNaN()) + return Op1; + return (V1.compare(V2) == APFloat::cmpLessThan) ? Op2 : Op1; + } + if (!TLI) return nullptr; if (Name == "pow" && TLI->has(LibFunc::pow)) diff --git a/lib/Analysis/TargetTransformInfo.cpp b/lib/Analysis/TargetTransformInfo.cpp index 9a0133a..401e58c 100644 --- a/lib/Analysis/TargetTransformInfo.cpp +++ b/lib/Analysis/TargetTransformInfo.cpp @@ -474,6 +474,8 @@ struct NoTTI final : ImmutablePass, TargetTransformInfo { // These will all likely lower to a single selection DAG node. if (Name == "copysign" || Name == "copysignf" || Name == "copysignl" || Name == "fabs" || Name == "fabsf" || Name == "fabsl" || Name == "sin" || + Name == "fmin" || Name == "fminf" || Name == "fminl" || + Name == "fmax" || Name == "fmaxf" || Name == "fmaxl" || Name == "sinf" || Name == "sinl" || Name == "cos" || Name == "cosf" || Name == "cosl" || Name == "sqrt" || Name == "sqrtf" || Name == "sqrtl") return false; diff --git a/lib/Analysis/ValueTracking.cpp b/lib/Analysis/ValueTracking.cpp index f208bb7..47a8630 100644 --- a/lib/Analysis/ValueTracking.cpp +++ b/lib/Analysis/ValueTracking.cpp @@ -2577,6 +2577,8 @@ bool llvm::isSafeToSpeculativelyExecute(const Value *V, case Intrinsic::fma: case Intrinsic::fmuladd: case Intrinsic::fabs: + case Intrinsic::minnum: + case Intrinsic::maxnum: return true; // TODO: some fp intrinsics are marked as having the same error handling // as libm. They're safe to speculate when they won't error. diff --git a/lib/CodeGen/BasicTargetTransformInfo.cpp b/lib/CodeGen/BasicTargetTransformInfo.cpp index 9a3e296..7790cd9 100644 --- a/lib/CodeGen/BasicTargetTransformInfo.cpp +++ b/lib/CodeGen/BasicTargetTransformInfo.cpp @@ -565,6 +565,8 @@ unsigned BasicTTI::getIntrinsicInstrCost(Intrinsic::ID IID, Type *RetTy, case Intrinsic::log10: ISD = ISD::FLOG10; break; case Intrinsic::log2: ISD = ISD::FLOG2; break; case Intrinsic::fabs: ISD = ISD::FABS; break; + case Intrinsic::minnum: ISD = ISD::FMIN; break; + case Intrinsic::maxnum: ISD = ISD::FMAX; break; case Intrinsic::copysign: ISD = ISD::FCOPYSIGN; break; case Intrinsic::floor: ISD = ISD::FFLOOR; break; case Intrinsic::ceil: ISD = ISD::FCEIL; break; diff --git a/lib/CodeGen/SelectionDAG/DAGCombiner.cpp b/lib/CodeGen/SelectionDAG/DAGCombiner.cpp index 013fbb0..e63fd3c 100644 --- a/lib/CodeGen/SelectionDAG/DAGCombiner.cpp +++ b/lib/CodeGen/SelectionDAG/DAGCombiner.cpp @@ -289,6 +289,8 @@ namespace { SDValue visitFCEIL(SDNode *N); SDValue visitFTRUNC(SDNode *N); SDValue visitFFLOOR(SDNode *N); + SDValue visitFMIN(SDNode *N); + SDValue visitFMAX(SDNode *N); SDValue visitBRCOND(SDNode *N); SDValue visitBR_CC(SDNode *N); SDValue visitLOAD(SDNode *N); @@ -1317,6 +1319,8 @@ SDValue DAGCombiner::visit(SDNode *N) { case ISD::FNEG: return visitFNEG(N); case ISD::FABS: return visitFABS(N); case ISD::FFLOOR: return visitFFLOOR(N); + case ISD::FMIN: return visitFMIN(N); + case ISD::FMAX: return visitFMAX(N); case ISD::FCEIL: return visitFCEIL(N); case ISD::FTRUNC: return visitFTRUNC(N); case ISD::BRCOND: return visitBRCOND(N); @@ -7389,6 +7393,57 @@ SDValue DAGCombiner::visitFNEG(SDNode *N) { return SDValue(); } +SDValue DAGCombiner::visitFMIN(SDNode *N) { + SDValue N0 = N->getOperand(0); + SDValue N1 = N->getOperand(1); + const ConstantFPSDNode *N0CFP = dyn_cast(N0); + const ConstantFPSDNode *N1CFP = dyn_cast(N1); + + if (N0CFP && N1CFP) { + const APFloat &C0 = N0CFP->getValueAPF(); + const APFloat &C1 = N1CFP->getValueAPF(); + + if (C0.isNaN()) + return N1; + if (C1.isNaN()) + return N0; + return (C1.compare(C0) == APFloat::cmpLessThan) ? N1 : N0; + } + + if (N0CFP) { + EVT VT = N->getValueType(0); + // Canonicalize to constant on RHS. + return DAG.getNode(ISD::FMIN, SDLoc(N), VT, N1, N0); + } + + return SDValue(); +} + +SDValue DAGCombiner::visitFMAX(SDNode *N) { + SDValue N0 = N->getOperand(0); + SDValue N1 = N->getOperand(1); + const ConstantFPSDNode *N0CFP = dyn_cast(N0); + const ConstantFPSDNode *N1CFP = dyn_cast(N1); + + if (N0CFP && N1CFP) { + const APFloat &C0 = N0CFP->getValueAPF(); + const APFloat &C1 = N1CFP->getValueAPF(); + if (C0.isNaN()) + return N1; + if (C1.isNaN()) + return N0; + return (C0.compare(C1) == APFloat::cmpLessThan) ? N1 : N0; + } + + if (N0CFP) { + EVT VT = N->getValueType(0); + // Canonicalize to constant on RHS. + return DAG.getNode(ISD::FMAX, SDLoc(N), VT, N1, N0); + } + + return SDValue(); +} + SDValue DAGCombiner::visitFABS(SDNode *N) { SDValue N0 = N->getOperand(0); EVT VT = N->getValueType(0); diff --git a/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp b/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp index dd932eb..3f3109f 100644 --- a/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp +++ b/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp @@ -3399,6 +3399,16 @@ void SelectionDAGLegalize::ExpandNode(SDNode *Node) { Results.push_back(Tmp1); break; } + case ISD::FMIN: + Results.push_back(ExpandFPLibCall(Node, RTLIB::FMIN_F32, RTLIB::FMIN_F64, + RTLIB::FMIN_F80, RTLIB::FMIN_F128, + RTLIB::FMIN_PPCF128)); + break; + case ISD::FMAX: + Results.push_back(ExpandFPLibCall(Node, RTLIB::FMAX_F32, RTLIB::FMAX_F64, + RTLIB::FMAX_F80, RTLIB::FMAX_F128, + RTLIB::FMAX_PPCF128)); + break; case ISD::FSQRT: Results.push_back(ExpandFPLibCall(Node, RTLIB::SQRT_F32, RTLIB::SQRT_F64, RTLIB::SQRT_F80, RTLIB::SQRT_F128, diff --git a/lib/CodeGen/SelectionDAG/LegalizeFloatTypes.cpp b/lib/CodeGen/SelectionDAG/LegalizeFloatTypes.cpp index bcda2b8..c9fe8fd 100644 --- a/lib/CodeGen/SelectionDAG/LegalizeFloatTypes.cpp +++ b/lib/CodeGen/SelectionDAG/LegalizeFloatTypes.cpp @@ -68,6 +68,8 @@ void DAGTypeLegalizer::SoftenFloatResult(SDNode *N, unsigned ResNo) { case ISD::EXTRACT_VECTOR_ELT: R = SoftenFloatRes_EXTRACT_VECTOR_ELT(N); break; case ISD::FABS: R = SoftenFloatRes_FABS(N); break; + case ISD::FMIN: R = SoftenFloatRes_FMIN(N); break; + case ISD::FMAX: R = SoftenFloatRes_FMAX(N); break; case ISD::FADD: R = SoftenFloatRes_FADD(N); break; case ISD::FCEIL: R = SoftenFloatRes_FCEIL(N); break; case ISD::FCOPYSIGN: R = SoftenFloatRes_FCOPYSIGN(N); break; @@ -153,6 +155,32 @@ SDValue DAGTypeLegalizer::SoftenFloatRes_FABS(SDNode *N) { return DAG.getNode(ISD::AND, SDLoc(N), NVT, Op, Mask); } +SDValue DAGTypeLegalizer::SoftenFloatRes_FMIN(SDNode *N) { + EVT NVT = TLI.getTypeToTransformTo(*DAG.getContext(), N->getValueType(0)); + SDValue Ops[2] = { GetSoftenedFloat(N->getOperand(0)), + GetSoftenedFloat(N->getOperand(1)) }; + return TLI.makeLibCall(DAG, GetFPLibCall(N->getValueType(0), + RTLIB::FMIN_F32, + RTLIB::FMIN_F64, + RTLIB::FMIN_F80, + RTLIB::FMIN_F128, + RTLIB::FMIN_PPCF128), + NVT, Ops, 2, false, SDLoc(N)).first; +} + +SDValue DAGTypeLegalizer::SoftenFloatRes_FMAX(SDNode *N) { + EVT NVT = TLI.getTypeToTransformTo(*DAG.getContext(), N->getValueType(0)); + SDValue Ops[2] = { GetSoftenedFloat(N->getOperand(0)), + GetSoftenedFloat(N->getOperand(1)) }; + return TLI.makeLibCall(DAG, GetFPLibCall(N->getValueType(0), + RTLIB::FMAX_F32, + RTLIB::FMAX_F64, + RTLIB::FMAX_F80, + RTLIB::FMAX_F128, + RTLIB::FMAX_PPCF128), + NVT, Ops, 2, false, SDLoc(N)).first; +} + SDValue DAGTypeLegalizer::SoftenFloatRes_FADD(SDNode *N) { EVT NVT = TLI.getTypeToTransformTo(*DAG.getContext(), N->getValueType(0)); SDValue Ops[2] = { GetSoftenedFloat(N->getOperand(0)), @@ -856,6 +884,8 @@ void DAGTypeLegalizer::ExpandFloatResult(SDNode *N, unsigned ResNo) { case ISD::ConstantFP: ExpandFloatRes_ConstantFP(N, Lo, Hi); break; case ISD::FABS: ExpandFloatRes_FABS(N, Lo, Hi); break; + case ISD::FMIN: ExpandFloatRes_FMIN(N, Lo, Hi); break; + case ISD::FMAX: ExpandFloatRes_FMAX(N, Lo, Hi); break; case ISD::FADD: ExpandFloatRes_FADD(N, Lo, Hi); break; case ISD::FCEIL: ExpandFloatRes_FCEIL(N, Lo, Hi); break; case ISD::FCOPYSIGN: ExpandFloatRes_FCOPYSIGN(N, Lo, Hi); break; @@ -919,6 +949,26 @@ void DAGTypeLegalizer::ExpandFloatRes_FABS(SDNode *N, SDValue &Lo, ISD::SETEQ); } +void DAGTypeLegalizer::ExpandFloatRes_FMIN(SDNode *N, SDValue &Lo, + SDValue &Hi) { + SDValue Call = LibCallify(GetFPLibCall(N->getValueType(0), + RTLIB::FMIN_F32, RTLIB::FMIN_F64, + RTLIB::FMIN_F80, RTLIB::FMIN_F128, + RTLIB::FMIN_PPCF128), + N, false); + GetPairElements(Call, Lo, Hi); +} + +void DAGTypeLegalizer::ExpandFloatRes_FMAX(SDNode *N, SDValue &Lo, + SDValue &Hi) { + SDValue Call = LibCallify(GetFPLibCall(N->getValueType(0), + RTLIB::FMAX_F32, RTLIB::FMAX_F64, + RTLIB::FMAX_F80, RTLIB::FMAX_F128, + RTLIB::FMAX_PPCF128), + N, false); + GetPairElements(Call, Lo, Hi); +} + void DAGTypeLegalizer::ExpandFloatRes_FADD(SDNode *N, SDValue &Lo, SDValue &Hi) { SDValue Call = LibCallify(GetFPLibCall(N->getValueType(0), diff --git a/lib/CodeGen/SelectionDAG/LegalizeTypes.h b/lib/CodeGen/SelectionDAG/LegalizeTypes.h index b82b39f..2ca1c78 100644 --- a/lib/CodeGen/SelectionDAG/LegalizeTypes.h +++ b/lib/CodeGen/SelectionDAG/LegalizeTypes.h @@ -387,6 +387,8 @@ private: SDValue SoftenFloatRes_ConstantFP(ConstantFPSDNode *N); SDValue SoftenFloatRes_EXTRACT_VECTOR_ELT(SDNode *N); SDValue SoftenFloatRes_FABS(SDNode *N); + SDValue SoftenFloatRes_FMIN(SDNode *N); + SDValue SoftenFloatRes_FMAX(SDNode *N); SDValue SoftenFloatRes_FADD(SDNode *N); SDValue SoftenFloatRes_FCEIL(SDNode *N); SDValue SoftenFloatRes_FCOPYSIGN(SDNode *N); @@ -450,6 +452,8 @@ private: void ExpandFloatResult(SDNode *N, unsigned ResNo); void ExpandFloatRes_ConstantFP(SDNode *N, SDValue &Lo, SDValue &Hi); void ExpandFloatRes_FABS (SDNode *N, SDValue &Lo, SDValue &Hi); + void ExpandFloatRes_FMIN (SDNode *N, SDValue &Lo, SDValue &Hi); + void ExpandFloatRes_FMAX (SDNode *N, SDValue &Lo, SDValue &Hi); void ExpandFloatRes_FADD (SDNode *N, SDValue &Lo, SDValue &Hi); void ExpandFloatRes_FCEIL (SDNode *N, SDValue &Lo, SDValue &Hi); void ExpandFloatRes_FCOPYSIGN (SDNode *N, SDValue &Lo, SDValue &Hi); diff --git a/lib/CodeGen/SelectionDAG/LegalizeVectorOps.cpp b/lib/CodeGen/SelectionDAG/LegalizeVectorOps.cpp index 16cf2de..f276f14 100644 --- a/lib/CodeGen/SelectionDAG/LegalizeVectorOps.cpp +++ b/lib/CodeGen/SelectionDAG/LegalizeVectorOps.cpp @@ -290,6 +290,8 @@ SDValue VectorLegalizer::LegalizeOp(SDValue Op) { case ISD::FP_TO_UINT: case ISD::FNEG: case ISD::FABS: + case ISD::FMIN: + case ISD::FMAX: case ISD::FCOPYSIGN: case ISD::FSQRT: case ISD::FSIN: diff --git a/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp b/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp index 28727a6..00914c5 100644 --- a/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp +++ b/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp @@ -106,6 +106,9 @@ void DAGTypeLegalizer::ScalarizeVectorResult(SDNode *N, unsigned ResNo) { case ISD::FCOPYSIGN: case ISD::FDIV: case ISD::FMUL: + case ISD::FMIN: + case ISD::FMAX: + case ISD::FPOW: case ISD::FREM: case ISD::FSUB: @@ -627,6 +630,8 @@ void DAGTypeLegalizer::SplitVectorResult(SDNode *N, unsigned ResNo) { case ISD::FCOPYSIGN: case ISD::FSUB: case ISD::FMUL: + case ISD::FMIN: + case ISD::FMAX: case ISD::SDIV: case ISD::UDIV: case ISD::FDIV: @@ -1583,6 +1588,8 @@ void DAGTypeLegalizer::WidenVectorResult(SDNode *N, unsigned ResNo) { case ISD::OR: case ISD::SUB: case ISD::XOR: + case ISD::FMIN: + case ISD::FMAX: Res = WidenVecRes_Binary(N); break; diff --git a/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp index 3ab0dea..01382c76 100644 --- a/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ b/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -5158,6 +5158,18 @@ SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I, unsigned Intrinsic) { getValue(I.getArgOperand(0)))); return nullptr; } + case Intrinsic::minnum: + setValue(&I, DAG.getNode(ISD::FMIN, sdl, + getValue(I.getArgOperand(0)).getValueType(), + getValue(I.getArgOperand(0)), + getValue(I.getArgOperand(1)))); + return nullptr; + case Intrinsic::maxnum: + setValue(&I, DAG.getNode(ISD::FMAX, sdl, + getValue(I.getArgOperand(0)).getValueType(), + getValue(I.getArgOperand(0)), + getValue(I.getArgOperand(1)))); + return nullptr; case Intrinsic::copysign: setValue(&I, DAG.getNode(ISD::FCOPYSIGN, sdl, getValue(I.getArgOperand(0)).getValueType(), @@ -5941,6 +5953,26 @@ bool SelectionDAGBuilder::visitUnaryFloatCall(const CallInst &I, return true; } +/// visitBinaryFloatCall - If a call instruction is a unary floating-point +/// operation (as expected), translate it to an SDNode with the specified opcode +/// and return true. +bool SelectionDAGBuilder::visitBinaryFloatCall(const CallInst &I, + unsigned Opcode) { + // Sanity check that it really is a unary floating-point call. + if (I.getNumArgOperands() != 2 || + !I.getArgOperand(0)->getType()->isFloatingPointTy() || + I.getType() != I.getArgOperand(0)->getType() || + I.getType() != I.getArgOperand(1)->getType() || + !I.onlyReadsMemory()) + return false; + + SDValue Tmp0 = getValue(I.getArgOperand(0)); + SDValue Tmp1 = getValue(I.getArgOperand(1)); + EVT VT = Tmp0.getValueType(); + setValue(&I, DAG.getNode(Opcode, getCurSDLoc(), VT, Tmp0, Tmp1)); + return true; +} + void SelectionDAGBuilder::visitCall(const CallInst &I) { // Handle inline assembly differently. if (isa(I.getCalledValue())) { @@ -5997,6 +6029,18 @@ void SelectionDAGBuilder::visitCall(const CallInst &I) { if (visitUnaryFloatCall(I, ISD::FABS)) return; break; + case LibFunc::fmin: + case LibFunc::fminf: + case LibFunc::fminl: + if (visitBinaryFloatCall(I, ISD::FMIN)) + return; + break; + case LibFunc::fmax: + case LibFunc::fmaxf: + case LibFunc::fmaxl: + if (visitBinaryFloatCall(I, ISD::FMAX)) + return; + break; case LibFunc::sin: case LibFunc::sinf: case LibFunc::sinl: diff --git a/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h b/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h index 45dd554..4d03b1e 100644 --- a/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h +++ b/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h @@ -762,6 +762,7 @@ private: bool visitStrLenCall(const CallInst &I); bool visitStrNLenCall(const CallInst &I); bool visitUnaryFloatCall(const CallInst &I, unsigned Opcode); + bool visitBinaryFloatCall(const CallInst &I, unsigned Opcode); void visitAtomicLoad(const LoadInst &I); void visitAtomicStore(const StoreInst &I); diff --git a/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp b/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp index c10803f..06fc54b 100644 --- a/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp +++ b/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp @@ -141,6 +141,8 @@ std::string SDNode::getOperationName(const SelectionDAG *G) const { // Unary operators case ISD::FABS: return "fabs"; + case ISD::FMIN: return "fmin"; + case ISD::FMAX: return "fmax"; case ISD::FNEG: return "fneg"; case ISD::FSQRT: return "fsqrt"; case ISD::FSIN: return "fsin"; diff --git a/lib/CodeGen/TargetLoweringBase.cpp b/lib/CodeGen/TargetLoweringBase.cpp index b60a73f..dc883be 100644 --- a/lib/CodeGen/TargetLoweringBase.cpp +++ b/lib/CodeGen/TargetLoweringBase.cpp @@ -206,6 +206,16 @@ static void InitLibcallNames(const char **Names, const Triple &TT) { Names[RTLIB::FLOOR_F80] = "floorl"; Names[RTLIB::FLOOR_F128] = "floorl"; Names[RTLIB::FLOOR_PPCF128] = "floorl"; + Names[RTLIB::FMIN_F32] = "fminf"; + Names[RTLIB::FMIN_F64] = "fmin"; + Names[RTLIB::FMIN_F80] = "fminl"; + Names[RTLIB::FMIN_F128] = "fminl"; + Names[RTLIB::FMIN_PPCF128] = "fminl"; + Names[RTLIB::FMAX_F32] = "fmaxf"; + Names[RTLIB::FMAX_F64] = "fmax"; + Names[RTLIB::FMAX_F80] = "fmaxl"; + Names[RTLIB::FMAX_F128] = "fmaxl"; + Names[RTLIB::FMAX_PPCF128] = "fmaxl"; Names[RTLIB::ROUND_F32] = "roundf"; Names[RTLIB::ROUND_F64] = "round"; Names[RTLIB::ROUND_F80] = "roundl"; @@ -757,6 +767,8 @@ void TargetLoweringBase::initActions() { // These operations default to expand. setOperationAction(ISD::FGETSIGN, (MVT::SimpleValueType)VT, Expand); setOperationAction(ISD::CONCAT_VECTORS, (MVT::SimpleValueType)VT, Expand); + setOperationAction(ISD::FMIN, (MVT::SimpleValueType)VT, Expand); + setOperationAction(ISD::FMAX, (MVT::SimpleValueType)VT, Expand); // These library functions default to expand. setOperationAction(ISD::FROUND, (MVT::SimpleValueType)VT, Expand); @@ -793,6 +805,8 @@ void TargetLoweringBase::initActions() { setOperationAction(ISD::FEXP , MVT::f16, Expand); setOperationAction(ISD::FEXP2, MVT::f16, Expand); setOperationAction(ISD::FFLOOR, MVT::f16, Expand); + setOperationAction(ISD::FMIN, MVT::f16, Expand); + setOperationAction(ISD::FMAX, MVT::f16, Expand); setOperationAction(ISD::FNEARBYINT, MVT::f16, Expand); setOperationAction(ISD::FCEIL, MVT::f16, Expand); setOperationAction(ISD::FRINT, MVT::f16, Expand); @@ -804,6 +818,8 @@ void TargetLoweringBase::initActions() { setOperationAction(ISD::FEXP , MVT::f32, Expand); setOperationAction(ISD::FEXP2, MVT::f32, Expand); setOperationAction(ISD::FFLOOR, MVT::f32, Expand); + setOperationAction(ISD::FMIN, MVT::f32, Expand); + setOperationAction(ISD::FMAX, MVT::f32, Expand); setOperationAction(ISD::FNEARBYINT, MVT::f32, Expand); setOperationAction(ISD::FCEIL, MVT::f32, Expand); setOperationAction(ISD::FRINT, MVT::f32, Expand); @@ -815,6 +831,8 @@ void TargetLoweringBase::initActions() { setOperationAction(ISD::FEXP , MVT::f64, Expand); setOperationAction(ISD::FEXP2, MVT::f64, Expand); setOperationAction(ISD::FFLOOR, MVT::f64, Expand); + setOperationAction(ISD::FMIN, MVT::f64, Expand); + setOperationAction(ISD::FMAX, MVT::f64, Expand); setOperationAction(ISD::FNEARBYINT, MVT::f64, Expand); setOperationAction(ISD::FCEIL, MVT::f64, Expand); setOperationAction(ISD::FRINT, MVT::f64, Expand); @@ -826,6 +844,8 @@ void TargetLoweringBase::initActions() { setOperationAction(ISD::FEXP , MVT::f128, Expand); setOperationAction(ISD::FEXP2, MVT::f128, Expand); setOperationAction(ISD::FFLOOR, MVT::f128, Expand); + setOperationAction(ISD::FMIN, MVT::f128, Expand); + setOperationAction(ISD::FMAX, MVT::f128, Expand); setOperationAction(ISD::FNEARBYINT, MVT::f128, Expand); setOperationAction(ISD::FCEIL, MVT::f128, Expand); setOperationAction(ISD::FRINT, MVT::f128, Expand); diff --git a/lib/Target/R600/AMDGPUISelLowering.cpp b/lib/Target/R600/AMDGPUISelLowering.cpp index 89dab09..73a18d2 100644 --- a/lib/Target/R600/AMDGPUISelLowering.cpp +++ b/lib/Target/R600/AMDGPUISelLowering.cpp @@ -344,6 +344,8 @@ AMDGPUTargetLowering::AMDGPUTargetLowering(TargetMachine &TM) : for (MVT VT : FloatVectorTypes) { setOperationAction(ISD::FABS, VT, Expand); + setOperationAction(ISD::FMIN, VT, Expand); + setOperationAction(ISD::FMAX, VT, Expand); setOperationAction(ISD::FADD, VT, Expand); setOperationAction(ISD::FCEIL, VT, Expand); setOperationAction(ISD::FCOS, VT, Expand); diff --git a/lib/Target/R600/SIISelLowering.cpp b/lib/Target/R600/SIISelLowering.cpp index 7b969d7..04e427f 100644 --- a/lib/Target/R600/SIISelLowering.cpp +++ b/lib/Target/R600/SIISelLowering.cpp @@ -90,6 +90,11 @@ SITargetLowering::SITargetLowering(TargetMachine &TM) : setOperationAction(ISD::FSIN, MVT::f32, Custom); setOperationAction(ISD::FCOS, MVT::f32, Custom); + setOperationAction(ISD::FMIN, MVT::f32, Legal); + setOperationAction(ISD::FMAX, MVT::f32, Legal); + setOperationAction(ISD::FMIN, MVT::f64, Legal); + setOperationAction(ISD::FMAX, MVT::f64, Legal); + // We need to custom lower vector stores from local memory setOperationAction(ISD::LOAD, MVT::v4i32, Custom); setOperationAction(ISD::LOAD, MVT::v8i32, Custom); diff --git a/lib/Target/R600/SIInstructions.td b/lib/Target/R600/SIInstructions.td index e4d7dc7..a9d1856 100644 --- a/lib/Target/R600/SIInstructions.td +++ b/lib/Target/R600/SIInstructions.td @@ -1388,8 +1388,8 @@ defm V_MAX_LEGACY_F32 : VOP2Inst <0x0000000e, "V_MAX_LEGACY_F32", VOP_F32_F32_F32, AMDGPUfmax >; -defm V_MIN_F32 : VOP2Inst <0x0000000f, "V_MIN_F32", VOP_F32_F32_F32>; -defm V_MAX_F32 : VOP2Inst <0x00000010, "V_MAX_F32", VOP_F32_F32_F32>; +defm V_MIN_F32 : VOP2Inst <0x0000000f, "V_MIN_F32", VOP_F32_F32_F32, fmin>; +defm V_MAX_F32 : VOP2Inst <0x00000010, "V_MAX_F32", VOP_F32_F32_F32, fmax>; defm V_MIN_I32 : VOP2Inst <0x00000011, "V_MIN_I32", VOP_I32_I32_I32, AMDGPUsmin>; defm V_MAX_I32 : VOP2Inst <0x00000012, "V_MAX_I32", VOP_I32_I32_I32, AMDGPUsmax>; defm V_MIN_U32 : VOP2Inst <0x00000013, "V_MIN_U32", VOP_I32_I32_I32, AMDGPUumin>; @@ -1579,10 +1579,10 @@ defm V_MUL_F64 : VOP3Inst <0x00000165, "V_MUL_F64", VOP_F64_F64_F64, fmul >; defm V_MIN_F64 : VOP3Inst <0x00000166, "V_MIN_F64", - VOP_F64_F64_F64 + VOP_F64_F64_F64, fmin >; defm V_MAX_F64 : VOP3Inst <0x00000167, "V_MAX_F64", - VOP_F64_F64_F64 + VOP_F64_F64_F64, fmax >; } // isCommutable = 1 diff --git a/lib/Target/X86/X86ISelLowering.cpp b/lib/Target/X86/X86ISelLowering.cpp index 8343a85..14a9f4d 100644 --- a/lib/Target/X86/X86ISelLowering.cpp +++ b/lib/Target/X86/X86ISelLowering.cpp @@ -810,6 +810,8 @@ void X86TargetLowering::resetOperationActions() { setOperationAction(ISD::FLOG10, MVT::f80, Expand); setOperationAction(ISD::FEXP, MVT::f80, Expand); setOperationAction(ISD::FEXP2, MVT::f80, Expand); + setOperationAction(ISD::FMIN, MVT::f80, Expand); + setOperationAction(ISD::FMAX, MVT::f80, Expand); // First set operation action for all vector types to either promote // (for widening) or expand (for scalarization). Then we will selectively diff --git a/lib/Transforms/InstCombine/InstCombineCalls.cpp b/lib/Transforms/InstCombine/InstCombineCalls.cpp index 30fc3c9..f909797 100644 --- a/lib/Transforms/InstCombine/InstCombineCalls.cpp +++ b/lib/Transforms/InstCombine/InstCombineCalls.cpp @@ -518,6 +518,87 @@ Instruction *InstCombiner::visitCallInst(CallInst &CI) { } } break; + case Intrinsic::minnum: + case Intrinsic::maxnum: { + Value *Arg0 = II->getArgOperand(0); + Value *Arg1 = II->getArgOperand(1); + + // fmin(x, x) -> x + if (Arg0 == Arg1) + return ReplaceInstUsesWith(CI, Arg0); + + const ConstantFP *C0 = dyn_cast(Arg0); + const ConstantFP *C1 = dyn_cast(Arg1); + + // Canonicalize constants into the RHS. + if (C0 && !C1) { + II->setArgOperand(0, Arg1); + II->setArgOperand(1, Arg0); + return II; + } + + // fmin(x, nan) -> x + if (C1 && C1->isNaN()) + return ReplaceInstUsesWith(CI, Arg0); + + // fmin(undef, x) -> x + if (isa(Arg0)) + return ReplaceInstUsesWith(CI, Arg1); + + // fmin(x, undef) -> x + if (isa(Arg1)) + return ReplaceInstUsesWith(CI, Arg0); + + Value *X = nullptr; + Value *Y = nullptr; + if (II->getIntrinsicID() == Intrinsic::minnum) { + // fmin(x, fmin(x, y)) -> fmin(x, y) + // fmin(y, fmin(x, y)) -> fmin(x, y) + if (match(Arg1, m_FMin(m_Value(X), m_Value(Y)))) { + if (Arg0 == X || Arg0 == Y) + return ReplaceInstUsesWith(CI, Arg1); + } + + // fmin(fmin(x, y), x) -> fmin(x, y) + // fmin(fmin(x, y), y) -> fmin(x, y) + if (match(Arg0, m_FMin(m_Value(X), m_Value(Y)))) { + if (Arg1 == X || Arg1 == Y) + return ReplaceInstUsesWith(CI, Arg0); + } + + // TODO: fmin(nnan x, inf) -> x + // TODO: fmin(nnan ninf x, flt_max) -> x + if (C1 && C1->isInfinity()) { + // fmin(x, -inf) -> -inf + if (C1->isNegative()) + return ReplaceInstUsesWith(CI, Arg1); + } + } else { + assert(II->getIntrinsicID() == Intrinsic::maxnum); + // fmax(x, fmax(x, y)) -> fmax(x, y) + // fmax(y, fmax(x, y)) -> fmax(x, y) + if (match(Arg1, m_FMax(m_Value(X), m_Value(Y)))) { + if (Arg0 == X || Arg0 == Y) + return ReplaceInstUsesWith(CI, Arg1); + } + + // fmax(fmax(x, y), x) -> fmax(x, y) + // fmax(fmax(x, y), y) -> fmax(x, y) + if (match(Arg0, m_FMax(m_Value(X), m_Value(Y)))) { + if (Arg1 == X || Arg1 == Y) + return ReplaceInstUsesWith(CI, Arg0); + } + + // TODO: fmax(nnan x, -inf) -> x + // TODO: fmax(nnan ninf x, -flt_max) -> x + if (C1 && C1->isInfinity()) { + // fmax(x, inf) -> inf + if (!C1->isNegative()) + return ReplaceInstUsesWith(CI, Arg1); + } + } + break; + } case Intrinsic::ppc_altivec_lvx: case Intrinsic::ppc_altivec_lvxl: // Turn PPC lvx -> load if the pointer is known aligned. diff --git a/lib/Transforms/Vectorize/BBVectorize.cpp b/lib/Transforms/Vectorize/BBVectorize.cpp index 50c3fa4..b4991bc 100644 --- a/lib/Transforms/Vectorize/BBVectorize.cpp +++ b/lib/Transforms/Vectorize/BBVectorize.cpp @@ -685,6 +685,8 @@ namespace { case Intrinsic::trunc: case Intrinsic::floor: case Intrinsic::fabs: + case Intrinsic::minnum: + case Intrinsic::maxnum: return Config.VectorizeMath; case Intrinsic::bswap: case Intrinsic::ctpop: diff --git a/test/CodeGen/PowerPC/fmax.ll b/test/CodeGen/PowerPC/fmax.ll new file mode 100644 index 0000000..1825850 --- /dev/null +++ b/test/CodeGen/PowerPC/fmax.ll @@ -0,0 +1,86 @@ +; RUN: llc -march=ppc32 -mtriple=powerpc-unknown-linux-gnu < %s | FileCheck %s + +declare float @fmaxf(float, float) +declare double @fmax(double, double) +declare ppc_fp128 @fmaxl(ppc_fp128, ppc_fp128) +declare float @llvm.maxnum.f32(float, float) +declare double @llvm.maxnum.f64(double, double) +declare ppc_fp128 @llvm.maxnum.ppcf128(ppc_fp128, ppc_fp128) + +declare <2 x float> @llvm.maxnum.v2f32(<2 x float>, <2 x float>) +declare <4 x float> @llvm.maxnum.v4f32(<4 x float>, <4 x float>) +declare <8 x float> @llvm.maxnum.v8f32(<8 x float>, <8 x float>) + +; CHECK-LABEL: @test_fmaxf +; CHECK: bl fmaxf +define float @test_fmaxf(float %x, float %y) { + %z = call float @fmaxf(float %x, float %y) readnone + ret float %z +} + +; CHECK-LABEL: @test_fmax +; CHECK: bl fmax +define double @test_fmax(double %x, double %y) { + %z = call double @fmax(double %x, double %y) readnone + ret double %z +} + +; CHECK-LABEL: @test_fmaxl +; CHECK: bl fmaxl +define ppc_fp128 @test_fmaxl(ppc_fp128 %x, ppc_fp128 %y) { + %z = call ppc_fp128 @fmaxl(ppc_fp128 %x, ppc_fp128 %y) readnone + ret ppc_fp128 %z +} + +; CHECK-LABEL: @test_intrinsic_fmaxf +; CHECK: bl fmaxf +define float @test_intrinsic_fmaxf(float %x, float %y) { + %z = call float @llvm.maxnum.f32(float %x, float %y) readnone + ret float %z +} + +; CHECK-LABEL: @test_intrinsic_fmax +; CHECK: bl fmax +define double @test_intrinsic_fmax(double %x, double %y) { + %z = call double @llvm.maxnum.f64(double %x, double %y) readnone + ret double %z +} + +; CHECK-LABEL: @test_intrinsic_fmaxl +; CHECK: bl fmaxl +define ppc_fp128 @test_intrinsic_fmaxl(ppc_fp128 %x, ppc_fp128 %y) { + %z = call ppc_fp128 @llvm.maxnum.ppcf128(ppc_fp128 %x, ppc_fp128 %y) readnone + ret ppc_fp128 %z +} + +; CHECK-LABEL: @test_intrinsic_fmaxf_v2f32 +; CHECK: bl fmaxf +; CHECK: bl fmaxf +define <2 x float> @test_intrinsic_fmaxf_v2f32(<2 x float> %x, <2 x float> %y) { + %z = call <2 x float> @llvm.maxnum.v2f32(<2 x float> %x, <2 x float> %y) readnone + ret <2 x float> %z +} + +; CHECK-LABEL: @test_intrinsic_fmaxf_v4f32 +; CHECK: bl fmaxf +; CHECK: bl fmaxf +; CHECK: bl fmaxf +; CHECK: bl fmaxf +define <4 x float> @test_intrinsic_fmaxf_v4f32(<4 x float> %x, <4 x float> %y) { + %z = call <4 x float> @llvm.maxnum.v4f32(<4 x float> %x, <4 x float> %y) readnone + ret <4 x float> %z +} + +; CHECK-LABEL: @test_intrinsic_fmaxf_v8f32 +; CHECK: bl fmaxf +; CHECK: bl fmaxf +; CHECK: bl fmaxf +; CHECK: bl fmaxf +; CHECK: bl fmaxf +; CHECK: bl fmaxf +; CHECK: bl fmaxf +; CHECK: bl fmaxf +define <8 x float> @test_intrinsic_fmaxf_v8f32(<8 x float> %x, <8 x float> %y) { + %z = call <8 x float> @llvm.maxnum.v8f32(<8 x float> %x, <8 x float> %y) readnone + ret <8 x float> %z +} diff --git a/test/CodeGen/PowerPC/fmin.ll b/test/CodeGen/PowerPC/fmin.ll new file mode 100644 index 0000000..fe91284 --- /dev/null +++ b/test/CodeGen/PowerPC/fmin.ll @@ -0,0 +1,86 @@ +; RUN: llc -march=ppc32 -mtriple=powerpc-unknown-linux-gnu < %s | FileCheck %s + +declare float @fminf(float, float) +declare double @fmin(double, double) +declare ppc_fp128 @fminl(ppc_fp128, ppc_fp128) +declare float @llvm.minnum.f32(float, float) +declare double @llvm.minnum.f64(double, double) +declare ppc_fp128 @llvm.minnum.ppcf128(ppc_fp128, ppc_fp128) + +declare <2 x float> @llvm.minnum.v2f32(<2 x float>, <2 x float>) +declare <4 x float> @llvm.minnum.v4f32(<4 x float>, <4 x float>) +declare <8 x float> @llvm.minnum.v8f32(<8 x float>, <8 x float>) + +; CHECK-LABEL: @test_fminf +; CHECK: bl fminf +define float @test_fminf(float %x, float %y) { + %z = call float @fminf(float %x, float %y) readnone + ret float %z +} + +; CHECK-LABEL: @test_fmin +; CHECK: bl fmin +define double @test_fmin(double %x, double %y) { + %z = call double @fmin(double %x, double %y) readnone + ret double %z +} + +; CHECK-LABEL: @test_fminl +; CHECK: bl fminl +define ppc_fp128 @test_fminl(ppc_fp128 %x, ppc_fp128 %y) { + %z = call ppc_fp128 @fminl(ppc_fp128 %x, ppc_fp128 %y) readnone + ret ppc_fp128 %z +} + +; CHECK-LABEL: @test_intrinsic_fmin_f32 +; CHECK: bl fminf +define float @test_intrinsic_fmin_f32(float %x, float %y) { + %z = call float @llvm.minnum.f32(float %x, float %y) readnone + ret float %z +} + +; CHECK-LABEL: @test_intrinsic_fmin_f64 +; CHECK: bl fmin +define double @test_intrinsic_fmin_f64(double %x, double %y) { + %z = call double @llvm.minnum.f64(double %x, double %y) readnone + ret double %z +} + +; CHECK-LABEL: @test_intrinsic_fmin_f128 +; CHECK: bl fminl +define ppc_fp128 @test_intrinsic_fmin_f128(ppc_fp128 %x, ppc_fp128 %y) { + %z = call ppc_fp128 @llvm.minnum.ppcf128(ppc_fp128 %x, ppc_fp128 %y) readnone + ret ppc_fp128 %z +} + +; CHECK-LABEL: @test_intrinsic_fminf_v2f32 +; CHECK: bl fminf +; CHECK: bl fminf +define <2 x float> @test_intrinsic_fminf_v2f32(<2 x float> %x, <2 x float> %y) { + %z = call <2 x float> @llvm.minnum.v2f32(<2 x float> %x, <2 x float> %y) readnone + ret <2 x float> %z +} + +; CHECK-LABEL: @test_intrinsic_fmin_v4f32 +; CHECK: bl fminf +; CHECK: bl fminf +; CHECK: bl fminf +; CHECK: bl fminf +define <4 x float> @test_intrinsic_fmin_v4f32(<4 x float> %x, <4 x float> %y) { + %z = call <4 x float> @llvm.minnum.v4f32(<4 x float> %x, <4 x float> %y) readnone + ret <4 x float> %z +} + +; CHECK-LABEL: @test_intrinsic_fmin_v8f32 +; CHECK: bl fminf +; CHECK: bl fminf +; CHECK: bl fminf +; CHECK: bl fminf +; CHECK: bl fminf +; CHECK: bl fminf +; CHECK: bl fminf +; CHECK: bl fminf +define <8 x float> @test_intrinsic_fmin_v8f32(<8 x float> %x, <8 x float> %y) { + %z = call <8 x float> @llvm.minnum.v8f32(<8 x float> %x, <8 x float> %y) readnone + ret <8 x float> %z +} diff --git a/test/CodeGen/R600/fmax.f64.ll b/test/CodeGen/R600/fmax.f64.ll new file mode 100644 index 0000000..af762e1 --- /dev/null +++ b/test/CodeGen/R600/fmax.f64.ll @@ -0,0 +1,75 @@ +; RUN: llc -march=r600 -mcpu=SI < %s | FileCheck -check-prefix=SI -check-prefix=FUNC %s + +declare double @llvm.maxnum.f64(double, double) #0 +declare <2 x double> @llvm.maxnum.v2f64(<2 x double>, <2 x double>) #0 +declare <4 x double> @llvm.maxnum.v4f64(<4 x double>, <4 x double>) #0 +declare <8 x double> @llvm.maxnum.v8f64(<8 x double>, <8 x double>) #0 +declare <16 x double> @llvm.maxnum.v16f64(<16 x double>, <16 x double>) #0 + +; FUNC-LABEL: @test_fmax_f64 +; SI: V_MAX_F64 +define void @test_fmax_f64(double addrspace(1)* %out, double %a, double %b) nounwind { + %val = call double @llvm.maxnum.f64(double %a, double %b) #0 + store double %val, double addrspace(1)* %out, align 8 + ret void +} + +; FUNC-LABEL: @test_fmax_v2f64 +; SI: V_MAX_F64 +; SI: V_MAX_F64 +define void @test_fmax_v2f64(<2 x double> addrspace(1)* %out, <2 x double> %a, <2 x double> %b) nounwind { + %val = call <2 x double> @llvm.maxnum.v2f64(<2 x double> %a, <2 x double> %b) #0 + store <2 x double> %val, <2 x double> addrspace(1)* %out, align 16 + ret void +} + +; FUNC-LABEL: @test_fmax_v4f64 +; SI: V_MAX_F64 +; SI: V_MAX_F64 +; SI: V_MAX_F64 +; SI: V_MAX_F64 +define void @test_fmax_v4f64(<4 x double> addrspace(1)* %out, <4 x double> %a, <4 x double> %b) nounwind { + %val = call <4 x double> @llvm.maxnum.v4f64(<4 x double> %a, <4 x double> %b) #0 + store <4 x double> %val, <4 x double> addrspace(1)* %out, align 32 + ret void +} + +; FUNC-LABEL: @test_fmax_v8f64 +; SI: V_MAX_F64 +; SI: V_MAX_F64 +; SI: V_MAX_F64 +; SI: V_MAX_F64 +; SI: V_MAX_F64 +; SI: V_MAX_F64 +; SI: V_MAX_F64 +; SI: V_MAX_F64 +define void @test_fmax_v8f64(<8 x double> addrspace(1)* %out, <8 x double> %a, <8 x double> %b) nounwind { + %val = call <8 x double> @llvm.maxnum.v8f64(<8 x double> %a, <8 x double> %b) #0 + store <8 x double> %val, <8 x double> addrspace(1)* %out, align 64 + ret void +} + +; FUNC-LABEL: @test_fmax_v16f64 +; SI: V_MAX_F64 +; SI: V_MAX_F64 +; SI: V_MAX_F64 +; SI: V_MAX_F64 +; SI: V_MAX_F64 +; SI: V_MAX_F64 +; SI: V_MAX_F64 +; SI: V_MAX_F64 +; SI: V_MAX_F64 +; SI: V_MAX_F64 +; SI: V_MAX_F64 +; SI: V_MAX_F64 +; SI: V_MAX_F64 +; SI: V_MAX_F64 +; SI: V_MAX_F64 +; SI: V_MAX_F64 +define void @test_fmax_v16f64(<16 x double> addrspace(1)* %out, <16 x double> %a, <16 x double> %b) nounwind { + %val = call <16 x double> @llvm.maxnum.v16f64(<16 x double> %a, <16 x double> %b) #0 + store <16 x double> %val, <16 x double> addrspace(1)* %out, align 128 + ret void +} + +attributes #0 = { nounwind readnone } diff --git a/test/CodeGen/R600/fmax.ll b/test/CodeGen/R600/fmax.ll index d7127f4..9d8cfe4 100644 --- a/test/CodeGen/R600/fmax.ll +++ b/test/CodeGen/R600/fmax.ll @@ -1,17 +1,191 @@ -;RUN: llc < %s -march=r600 -mcpu=redwood | FileCheck %s +; RUN: llc -march=r600 -mcpu=SI < %s | FileCheck -check-prefix=SI -check-prefix=FUNC %s -;CHECK: MAX * T{{[0-9]+\.[XYZW], T[0-9]+\.[XYZW], T[0-9]+\.[XYZW]}} +declare float @llvm.maxnum.f32(float, float) #0 +declare <2 x float> @llvm.maxnum.v2f32(<2 x float>, <2 x float>) #0 +declare <4 x float> @llvm.maxnum.v4f32(<4 x float>, <4 x float>) #0 +declare <8 x float> @llvm.maxnum.v8f32(<8 x float>, <8 x float>) #0 +declare <16 x float> @llvm.maxnum.v16f32(<16 x float>, <16 x float>) #0 -define void @test(<4 x float> inreg %reg0) #0 { - %r0 = extractelement <4 x float> %reg0, i32 0 - %r1 = extractelement <4 x float> %reg0, i32 1 - %r2 = fcmp oge float %r0, %r1 - %r3 = select i1 %r2, float %r0, float %r1 - %vec = insertelement <4 x float> undef, float %r3, i32 0 - call void @llvm.R600.store.swizzle(<4 x float> %vec, i32 0, i32 0) - ret void +declare double @llvm.maxnum.f64(double, double) + +; FUNC-LABEL: @test_fmax_f32 +; SI: V_MAX_F32_e32 +define void @test_fmax_f32(float addrspace(1)* %out, float %a, float %b) nounwind { + %val = call float @llvm.maxnum.f32(float %a, float %b) #0 + store float %val, float addrspace(1)* %out, align 4 + ret void +} + +; FUNC-LABEL: @test_fmax_v2f32 +; SI: V_MAX_F32_e32 +; SI: V_MAX_F32_e32 +define void @test_fmax_v2f32(<2 x float> addrspace(1)* %out, <2 x float> %a, <2 x float> %b) nounwind { + %val = call <2 x float> @llvm.maxnum.v2f32(<2 x float> %a, <2 x float> %b) #0 + store <2 x float> %val, <2 x float> addrspace(1)* %out, align 8 + ret void +} + +; FUNC-LABEL: @test_fmax_v4f32 +; SI: V_MAX_F32_e32 +; SI: V_MAX_F32_e32 +; SI: V_MAX_F32_e32 +; SI: V_MAX_F32_e32 +define void @test_fmax_v4f32(<4 x float> addrspace(1)* %out, <4 x float> %a, <4 x float> %b) nounwind { + %val = call <4 x float> @llvm.maxnum.v4f32(<4 x float> %a, <4 x float> %b) #0 + store <4 x float> %val, <4 x float> addrspace(1)* %out, align 16 + ret void +} + +; FUNC-LABEL: @test_fmax_v8f32 +; SI: V_MAX_F32_e32 +; SI: V_MAX_F32_e32 +; SI: V_MAX_F32_e32 +; SI: V_MAX_F32_e32 +; SI: V_MAX_F32_e32 +; SI: V_MAX_F32_e32 +; SI: V_MAX_F32_e32 +; SI: V_MAX_F32_e32 +define void @test_fmax_v8f32(<8 x float> addrspace(1)* %out, <8 x float> %a, <8 x float> %b) nounwind { + %val = call <8 x float> @llvm.maxnum.v8f32(<8 x float> %a, <8 x float> %b) #0 + store <8 x float> %val, <8 x float> addrspace(1)* %out, align 32 + ret void +} + +; FUNC-LABEL: @test_fmax_v16f32 +; SI: V_MAX_F32_e32 +; SI: V_MAX_F32_e32 +; SI: V_MAX_F32_e32 +; SI: V_MAX_F32_e32 +; SI: V_MAX_F32_e32 +; SI: V_MAX_F32_e32 +; SI: V_MAX_F32_e32 +; SI: V_MAX_F32_e32 +; SI: V_MAX_F32_e32 +; SI: V_MAX_F32_e32 +; SI: V_MAX_F32_e32 +; SI: V_MAX_F32_e32 +; SI: V_MAX_F32_e32 +; SI: V_MAX_F32_e32 +; SI: V_MAX_F32_e32 +; SI: V_MAX_F32_e32 +define void @test_fmax_v16f32(<16 x float> addrspace(1)* %out, <16 x float> %a, <16 x float> %b) nounwind { + %val = call <16 x float> @llvm.maxnum.v16f32(<16 x float> %a, <16 x float> %b) #0 + store <16 x float> %val, <16 x float> addrspace(1)* %out, align 64 + ret void +} + +; FUNC-LABEL: @constant_fold_fmax_f32 +; SI-NOT: V_MAX_F32_e32 +; SI: V_MOV_B32_e32 [[REG:v[0-9]+]], 2.0 +; SI: BUFFER_STORE_DWORD [[REG]] +define void @constant_fold_fmax_f32(float addrspace(1)* %out) nounwind { + %val = call float @llvm.maxnum.f32(float 1.0, float 2.0) #0 + store float %val, float addrspace(1)* %out, align 4 + ret void +} + +; FUNC-LABEL: @constant_fold_fmax_f32_nan_nan +; SI-NOT: V_MAX_F32_e32 +; SI: V_MOV_B32_e32 [[REG:v[0-9]+]], 0x7fc00000 +; SI: BUFFER_STORE_DWORD [[REG]] +define void @constant_fold_fmax_f32_nan_nan(float addrspace(1)* %out) nounwind { + %val = call float @llvm.maxnum.f32(float 0x7FF8000000000000, float 0x7FF8000000000000) #0 + store float %val, float addrspace(1)* %out, align 4 + ret void +} + +; FUNC-LABEL: @constant_fold_fmax_f32_val_nan +; SI-NOT: V_MAX_F32_e32 +; SI: V_MOV_B32_e32 [[REG:v[0-9]+]], 1.0 +; SI: BUFFER_STORE_DWORD [[REG]] +define void @constant_fold_fmax_f32_val_nan(float addrspace(1)* %out) nounwind { + %val = call float @llvm.maxnum.f32(float 1.0, float 0x7FF8000000000000) #0 + store float %val, float addrspace(1)* %out, align 4 + ret void +} + +; FUNC-LABEL: @constant_fold_fmax_f32_nan_val +; SI-NOT: V_MAX_F32_e32 +; SI: V_MOV_B32_e32 [[REG:v[0-9]+]], 1.0 +; SI: BUFFER_STORE_DWORD [[REG]] +define void @constant_fold_fmax_f32_nan_val(float addrspace(1)* %out) nounwind { + %val = call float @llvm.maxnum.f32(float 0x7FF8000000000000, float 1.0) #0 + store float %val, float addrspace(1)* %out, align 4 + ret void +} + +; FUNC-LABEL: @constant_fold_fmax_f32_p0_p0 +; SI-NOT: V_MAX_F32_e32 +; SI: V_MOV_B32_e32 [[REG:v[0-9]+]], 0 +; SI: BUFFER_STORE_DWORD [[REG]] +define void @constant_fold_fmax_f32_p0_p0(float addrspace(1)* %out) nounwind { + %val = call float @llvm.maxnum.f32(float 0.0, float 0.0) #0 + store float %val, float addrspace(1)* %out, align 4 + ret void +} + +; FUNC-LABEL: @constant_fold_fmax_f32_p0_n0 +; SI-NOT: V_MAX_F32_e32 +; SI: V_MOV_B32_e32 [[REG:v[0-9]+]], 0 +; SI: BUFFER_STORE_DWORD [[REG]] +define void @constant_fold_fmax_f32_p0_n0(float addrspace(1)* %out) nounwind { + %val = call float @llvm.maxnum.f32(float 0.0, float -0.0) #0 + store float %val, float addrspace(1)* %out, align 4 + ret void +} + +; FUNC-LABEL: @constant_fold_fmax_f32_n0_p0 +; SI-NOT: V_MAX_F32_e32 +; SI: V_MOV_B32_e32 [[REG:v[0-9]+]], 0x80000000 +; SI: BUFFER_STORE_DWORD [[REG]] +define void @constant_fold_fmax_f32_n0_p0(float addrspace(1)* %out) nounwind { + %val = call float @llvm.maxnum.f32(float -0.0, float 0.0) #0 + store float %val, float addrspace(1)* %out, align 4 + ret void +} + +; FUNC-LABEL: @constant_fold_fmax_f32_n0_n0 +; SI-NOT: V_MAX_F32_e32 +; SI: V_MOV_B32_e32 [[REG:v[0-9]+]], 0x80000000 +; SI: BUFFER_STORE_DWORD [[REG]] +define void @constant_fold_fmax_f32_n0_n0(float addrspace(1)* %out) nounwind { + %val = call float @llvm.maxnum.f32(float -0.0, float -0.0) #0 + store float %val, float addrspace(1)* %out, align 4 + ret void +} + +; FUNC-LABEL: @fmax_var_immediate_f32 +; SI: V_MAX_F32_e64 {{v[0-9]+}}, {{s[0-9]+}}, 2.0 +define void @fmax_var_immediate_f32(float addrspace(1)* %out, float %a) nounwind { + %val = call float @llvm.maxnum.f32(float %a, float 2.0) #0 + store float %val, float addrspace(1)* %out, align 4 + ret void +} + +; FUNC-LABEL: @fmax_immediate_var_f32 +; SI: V_MAX_F32_e64 {{v[0-9]+}}, {{s[0-9]+}}, 2.0 +define void @fmax_immediate_var_f32(float addrspace(1)* %out, float %a) nounwind { + %val = call float @llvm.maxnum.f32(float 2.0, float %a) #0 + store float %val, float addrspace(1)* %out, align 4 + ret void +} + +; FUNC-LABEL: @fmax_var_literal_f32 +; SI: V_MOV_B32_e32 [[REG:v[0-9]+]], 0x42c60000 +; SI: V_MAX_F32_e32 {{v[0-9]+}}, {{s[0-9]+}}, [[REG]] +define void @fmax_var_literal_f32(float addrspace(1)* %out, float %a) nounwind { + %val = call float @llvm.maxnum.f32(float %a, float 99.0) #0 + store float %val, float addrspace(1)* %out, align 4 + ret void } -declare void @llvm.R600.store.swizzle(<4 x float>, i32, i32) +; FUNC-LABEL: @fmax_literal_var_f32 +; SI: V_MOV_B32_e32 [[REG:v[0-9]+]], 0x42c60000 +; SI: V_MAX_F32_e32 {{v[0-9]+}}, {{s[0-9]+}}, [[REG]] +define void @fmax_literal_var_f32(float addrspace(1)* %out, float %a) nounwind { + %val = call float @llvm.maxnum.f32(float 99.0, float %a) #0 + store float %val, float addrspace(1)* %out, align 4 + ret void +} -attributes #0 = { "ShaderType"="0" } \ No newline at end of file +attributes #0 = { nounwind readnone } diff --git a/test/CodeGen/R600/fmin.f64.ll b/test/CodeGen/R600/fmin.f64.ll new file mode 100644 index 0000000..5feef60 --- /dev/null +++ b/test/CodeGen/R600/fmin.f64.ll @@ -0,0 +1,75 @@ +; RUN: llc -march=r600 -mcpu=SI < %s | FileCheck -check-prefix=SI -check-prefix=FUNC %s + +declare double @llvm.minnum.f64(double, double) #0 +declare <2 x double> @llvm.minnum.v2f64(<2 x double>, <2 x double>) #0 +declare <4 x double> @llvm.minnum.v4f64(<4 x double>, <4 x double>) #0 +declare <8 x double> @llvm.minnum.v8f64(<8 x double>, <8 x double>) #0 +declare <16 x double> @llvm.minnum.v16f64(<16 x double>, <16 x double>) #0 + +; FUNC-LABEL: @test_fmin_f64 +; SI: V_MIN_F64 +define void @test_fmin_f64(double addrspace(1)* %out, double %a, double %b) nounwind { + %val = call double @llvm.minnum.f64(double %a, double %b) #0 + store double %val, double addrspace(1)* %out, align 8 + ret void +} + +; FUNC-LABEL: @test_fmin_v2f64 +; SI: V_MIN_F64 +; SI: V_MIN_F64 +define void @test_fmin_v2f64(<2 x double> addrspace(1)* %out, <2 x double> %a, <2 x double> %b) nounwind { + %val = call <2 x double> @llvm.minnum.v2f64(<2 x double> %a, <2 x double> %b) #0 + store <2 x double> %val, <2 x double> addrspace(1)* %out, align 16 + ret void +} + +; FUNC-LABEL: @test_fmin_v4f64 +; SI: V_MIN_F64 +; SI: V_MIN_F64 +; SI: V_MIN_F64 +; SI: V_MIN_F64 +define void @test_fmin_v4f64(<4 x double> addrspace(1)* %out, <4 x double> %a, <4 x double> %b) nounwind { + %val = call <4 x double> @llvm.minnum.v4f64(<4 x double> %a, <4 x double> %b) #0 + store <4 x double> %val, <4 x double> addrspace(1)* %out, align 32 + ret void +} + +; FUNC-LABEL: @test_fmin_v8f64 +; SI: V_MIN_F64 +; SI: V_MIN_F64 +; SI: V_MIN_F64 +; SI: V_MIN_F64 +; SI: V_MIN_F64 +; SI: V_MIN_F64 +; SI: V_MIN_F64 +; SI: V_MIN_F64 +define void @test_fmin_v8f64(<8 x double> addrspace(1)* %out, <8 x double> %a, <8 x double> %b) nounwind { + %val = call <8 x double> @llvm.minnum.v8f64(<8 x double> %a, <8 x double> %b) #0 + store <8 x double> %val, <8 x double> addrspace(1)* %out, align 64 + ret void +} + +; FUNC-LABEL: @test_fmin_v16f64 +; SI: V_MIN_F64 +; SI: V_MIN_F64 +; SI: V_MIN_F64 +; SI: V_MIN_F64 +; SI: V_MIN_F64 +; SI: V_MIN_F64 +; SI: V_MIN_F64 +; SI: V_MIN_F64 +; SI: V_MIN_F64 +; SI: V_MIN_F64 +; SI: V_MIN_F64 +; SI: V_MIN_F64 +; SI: V_MIN_F64 +; SI: V_MIN_F64 +; SI: V_MIN_F64 +; SI: V_MIN_F64 +define void @test_fmin_v16f64(<16 x double> addrspace(1)* %out, <16 x double> %a, <16 x double> %b) nounwind { + %val = call <16 x double> @llvm.minnum.v16f64(<16 x double> %a, <16 x double> %b) #0 + store <16 x double> %val, <16 x double> addrspace(1)* %out, align 128 + ret void +} + +attributes #0 = { nounwind readnone } diff --git a/test/CodeGen/R600/fmin.ll b/test/CodeGen/R600/fmin.ll index defa8c0..2fc5671 100644 --- a/test/CodeGen/R600/fmin.ll +++ b/test/CodeGen/R600/fmin.ll @@ -1,17 +1,189 @@ -;RUN: llc < %s -march=r600 -mcpu=redwood | FileCheck %s +; RUN: llc -march=r600 -mcpu=SI < %s | FileCheck -check-prefix=SI -check-prefix=FUNC %s -;CHECK: MIN * T{{[0-9]+\.[XYZW], T[0-9]+\.[XYZW], T[0-9]+\.[XYZW]}} +declare float @llvm.minnum.f32(float, float) #0 +declare <2 x float> @llvm.minnum.v2f32(<2 x float>, <2 x float>) #0 +declare <4 x float> @llvm.minnum.v4f32(<4 x float>, <4 x float>) #0 +declare <8 x float> @llvm.minnum.v8f32(<8 x float>, <8 x float>) #0 +declare <16 x float> @llvm.minnum.v16f32(<16 x float>, <16 x float>) #0 -define void @test(<4 x float> inreg %reg0) #0 { - %r0 = extractelement <4 x float> %reg0, i32 0 - %r1 = extractelement <4 x float> %reg0, i32 1 - %r2 = fcmp uge float %r0, %r1 - %r3 = select i1 %r2, float %r1, float %r0 - %vec = insertelement <4 x float> undef, float %r3, i32 0 - call void @llvm.R600.store.swizzle(<4 x float> %vec, i32 0, i32 0) - ret void +; FUNC-LABEL: @test_fmin_f32 +; SI: V_MIN_F32_e32 +define void @test_fmin_f32(float addrspace(1)* %out, float %a, float %b) nounwind { + %val = call float @llvm.minnum.f32(float %a, float %b) #0 + store float %val, float addrspace(1)* %out, align 4 + ret void } -declare void @llvm.R600.store.swizzle(<4 x float>, i32, i32) +; FUNC-LABEL: @test_fmin_v2f32 +; SI: V_MIN_F32_e32 +; SI: V_MIN_F32_e32 +define void @test_fmin_v2f32(<2 x float> addrspace(1)* %out, <2 x float> %a, <2 x float> %b) nounwind { + %val = call <2 x float> @llvm.minnum.v2f32(<2 x float> %a, <2 x float> %b) #0 + store <2 x float> %val, <2 x float> addrspace(1)* %out, align 8 + ret void +} + +; FUNC-LABEL: @test_fmin_v4f32 +; SI: V_MIN_F32_e32 +; SI: V_MIN_F32_e32 +; SI: V_MIN_F32_e32 +; SI: V_MIN_F32_e32 +define void @test_fmin_v4f32(<4 x float> addrspace(1)* %out, <4 x float> %a, <4 x float> %b) nounwind { + %val = call <4 x float> @llvm.minnum.v4f32(<4 x float> %a, <4 x float> %b) #0 + store <4 x float> %val, <4 x float> addrspace(1)* %out, align 16 + ret void +} + +; FUNC-LABEL: @test_fmin_v8f32 +; SI: V_MIN_F32_e32 +; SI: V_MIN_F32_e32 +; SI: V_MIN_F32_e32 +; SI: V_MIN_F32_e32 +; SI: V_MIN_F32_e32 +; SI: V_MIN_F32_e32 +; SI: V_MIN_F32_e32 +; SI: V_MIN_F32_e32 +define void @test_fmin_v8f32(<8 x float> addrspace(1)* %out, <8 x float> %a, <8 x float> %b) nounwind { + %val = call <8 x float> @llvm.minnum.v8f32(<8 x float> %a, <8 x float> %b) #0 + store <8 x float> %val, <8 x float> addrspace(1)* %out, align 32 + ret void +} + +; FUNC-LABEL: @test_fmin_v16f32 +; SI: V_MIN_F32_e32 +; SI: V_MIN_F32_e32 +; SI: V_MIN_F32_e32 +; SI: V_MIN_F32_e32 +; SI: V_MIN_F32_e32 +; SI: V_MIN_F32_e32 +; SI: V_MIN_F32_e32 +; SI: V_MIN_F32_e32 +; SI: V_MIN_F32_e32 +; SI: V_MIN_F32_e32 +; SI: V_MIN_F32_e32 +; SI: V_MIN_F32_e32 +; SI: V_MIN_F32_e32 +; SI: V_MIN_F32_e32 +; SI: V_MIN_F32_e32 +; SI: V_MIN_F32_e32 +define void @test_fmin_v16f32(<16 x float> addrspace(1)* %out, <16 x float> %a, <16 x float> %b) nounwind { + %val = call <16 x float> @llvm.minnum.v16f32(<16 x float> %a, <16 x float> %b) #0 + store <16 x float> %val, <16 x float> addrspace(1)* %out, align 64 + ret void +} + +; FUNC-LABEL: @constant_fold_fmin_f32 +; SI-NOT: V_MIN_F32_e32 +; SI: V_MOV_B32_e32 [[REG:v[0-9]+]], 1.0 +; SI: BUFFER_STORE_DWORD [[REG]] +define void @constant_fold_fmin_f32(float addrspace(1)* %out) nounwind { + %val = call float @llvm.minnum.f32(float 1.0, float 2.0) #0 + store float %val, float addrspace(1)* %out, align 4 + ret void +} + +; FUNC-LABEL: @constant_fold_fmin_f32_nan_nan +; SI-NOT: V_MIN_F32_e32 +; SI: V_MOV_B32_e32 [[REG:v[0-9]+]], 0x7fc00000 +; SI: BUFFER_STORE_DWORD [[REG]] +define void @constant_fold_fmin_f32_nan_nan(float addrspace(1)* %out) nounwind { + %val = call float @llvm.minnum.f32(float 0x7FF8000000000000, float 0x7FF8000000000000) #0 + store float %val, float addrspace(1)* %out, align 4 + ret void +} + +; FUNC-LABEL: @constant_fold_fmin_f32_val_nan +; SI-NOT: V_MIN_F32_e32 +; SI: V_MOV_B32_e32 [[REG:v[0-9]+]], 1.0 +; SI: BUFFER_STORE_DWORD [[REG]] +define void @constant_fold_fmin_f32_val_nan(float addrspace(1)* %out) nounwind { + %val = call float @llvm.minnum.f32(float 1.0, float 0x7FF8000000000000) #0 + store float %val, float addrspace(1)* %out, align 4 + ret void +} + +; FUNC-LABEL: @constant_fold_fmin_f32_nan_val +; SI-NOT: V_MIN_F32_e32 +; SI: V_MOV_B32_e32 [[REG:v[0-9]+]], 1.0 +; SI: BUFFER_STORE_DWORD [[REG]] +define void @constant_fold_fmin_f32_nan_val(float addrspace(1)* %out) nounwind { + %val = call float @llvm.minnum.f32(float 0x7FF8000000000000, float 1.0) #0 + store float %val, float addrspace(1)* %out, align 4 + ret void +} + +; FUNC-LABEL: @constant_fold_fmin_f32_p0_p0 +; SI-NOT: V_MIN_F32_e32 +; SI: V_MOV_B32_e32 [[REG:v[0-9]+]], 0 +; SI: BUFFER_STORE_DWORD [[REG]] +define void @constant_fold_fmin_f32_p0_p0(float addrspace(1)* %out) nounwind { + %val = call float @llvm.minnum.f32(float 0.0, float 0.0) #0 + store float %val, float addrspace(1)* %out, align 4 + ret void +} + +; FUNC-LABEL: @constant_fold_fmin_f32_p0_n0 +; SI-NOT: V_MIN_F32_e32 +; SI: V_MOV_B32_e32 [[REG:v[0-9]+]], 0 +; SI: BUFFER_STORE_DWORD [[REG]] +define void @constant_fold_fmin_f32_p0_n0(float addrspace(1)* %out) nounwind { + %val = call float @llvm.minnum.f32(float 0.0, float -0.0) #0 + store float %val, float addrspace(1)* %out, align 4 + ret void +} + +; FUNC-LABEL: @constant_fold_fmin_f32_n0_p0 +; SI-NOT: V_MIN_F32_e32 +; SI: V_MOV_B32_e32 [[REG:v[0-9]+]], 0x80000000 +; SI: BUFFER_STORE_DWORD [[REG]] +define void @constant_fold_fmin_f32_n0_p0(float addrspace(1)* %out) nounwind { + %val = call float @llvm.minnum.f32(float -0.0, float 0.0) #0 + store float %val, float addrspace(1)* %out, align 4 + ret void +} + +; FUNC-LABEL: @constant_fold_fmin_f32_n0_n0 +; SI-NOT: V_MIN_F32_e32 +; SI: V_MOV_B32_e32 [[REG:v[0-9]+]], 0x80000000 +; SI: BUFFER_STORE_DWORD [[REG]] +define void @constant_fold_fmin_f32_n0_n0(float addrspace(1)* %out) nounwind { + %val = call float @llvm.minnum.f32(float -0.0, float -0.0) #0 + store float %val, float addrspace(1)* %out, align 4 + ret void +} + +; FUNC-LABEL: @fmin_var_immediate_f32 +; SI: V_MIN_F32_e64 {{v[0-9]+}}, {{s[0-9]+}}, 2.0 +define void @fmin_var_immediate_f32(float addrspace(1)* %out, float %a) nounwind { + %val = call float @llvm.minnum.f32(float %a, float 2.0) #0 + store float %val, float addrspace(1)* %out, align 4 + ret void +} + +; FUNC-LABEL: @fmin_immediate_var_f32 +; SI: V_MIN_F32_e64 {{v[0-9]+}}, {{s[0-9]+}}, 2.0 +define void @fmin_immediate_var_f32(float addrspace(1)* %out, float %a) nounwind { + %val = call float @llvm.minnum.f32(float 2.0, float %a) #0 + store float %val, float addrspace(1)* %out, align 4 + ret void +} + +; FUNC-LABEL: @fmin_var_literal_f32 +; SI: V_MOV_B32_e32 [[REG:v[0-9]+]], 0x42c60000 +; SI: V_MIN_F32_e32 {{v[0-9]+}}, {{s[0-9]+}}, [[REG]] +define void @fmin_var_literal_f32(float addrspace(1)* %out, float %a) nounwind { + %val = call float @llvm.minnum.f32(float %a, float 99.0) #0 + store float %val, float addrspace(1)* %out, align 4 + ret void +} + +; FUNC-LABEL: @fmin_literal_var_f32 +; SI: V_MOV_B32_e32 [[REG:v[0-9]+]], 0x42c60000 +; SI: V_MIN_F32_e32 {{v[0-9]+}}, {{s[0-9]+}}, [[REG]] +define void @fmin_literal_var_f32(float addrspace(1)* %out, float %a) nounwind { + %val = call float @llvm.minnum.f32(float 99.0, float %a) #0 + store float %val, float addrspace(1)* %out, align 4 + ret void +} -attributes #0 = { "ShaderType"="0" } \ No newline at end of file +attributes #0 = { nounwind readnone } diff --git a/test/CodeGen/X86/fmax.ll b/test/CodeGen/X86/fmax.ll new file mode 100644 index 0000000..23678c4 --- /dev/null +++ b/test/CodeGen/X86/fmax.ll @@ -0,0 +1,50 @@ +; RUN: llc -march=x86 -mtriple=i386-linux-gnu < %s | FileCheck %s + +declare float @fmaxf(float, float) +declare double @fmax(double, double) +declare x86_fp80 @fmaxl(x86_fp80, x86_fp80) +declare float @llvm.maxnum.f32(float, float) +declare double @llvm.maxnum.f64(double, double) +declare x86_fp80 @llvm.maxnum.f80(x86_fp80, x86_fp80) + +; CHECK-LABEL: @test_fmaxf +; CHECK: calll fmaxf +define float @test_fmaxf(float %x, float %y) { + %z = call float @fmaxf(float %x, float %y) readnone + ret float %z +} + +; CHECK-LABEL: @test_fmax +; CHECK: calll fmax +define double @test_fmax(double %x, double %y) { + %z = call double @fmax(double %x, double %y) readnone + ret double %z +} + +; CHECK-LABEL: @test_fmaxl +; CHECK: calll fmaxl +define x86_fp80 @test_fmaxl(x86_fp80 %x, x86_fp80 %y) { + %z = call x86_fp80 @fmaxl(x86_fp80 %x, x86_fp80 %y) readnone + ret x86_fp80 %z +} + +; CHECK-LABEL: @test_intrinsic_fmaxf +; CHECK: calll fmaxf +define float @test_intrinsic_fmaxf(float %x, float %y) { + %z = call float @llvm.maxnum.f32(float %x, float %y) readnone + ret float %z +} + +; CHECK-LABEL: @test_intrinsic_fmax +; CHECK: calll fmax +define double @test_intrinsic_fmax(double %x, double %y) { + %z = call double @llvm.maxnum.f64(double %x, double %y) readnone + ret double %z +} + +; CHECK-LABEL: @test_intrinsic_fmaxl +; CHECK: calll fmaxl +define x86_fp80 @test_intrinsic_fmaxl(x86_fp80 %x, x86_fp80 %y) { + %z = call x86_fp80 @llvm.maxnum.f80(x86_fp80 %x, x86_fp80 %y) readnone + ret x86_fp80 %z +} diff --git a/test/CodeGen/X86/fmin.ll b/test/CodeGen/X86/fmin.ll new file mode 100644 index 0000000..1e33cf4 --- /dev/null +++ b/test/CodeGen/X86/fmin.ll @@ -0,0 +1,95 @@ +; RUN: llc -march=x86 -mtriple=i386-linux-gnu -mattr=+sse,+sse2 < %s | FileCheck %s + +declare float @fminf(float, float) +declare double @fmin(double, double) +declare x86_fp80 @fminl(x86_fp80, x86_fp80) +declare float @llvm.minnum.f32(float, float) +declare double @llvm.minnum.f64(double, double) +declare x86_fp80 @llvm.minnum.f80(x86_fp80, x86_fp80) + +declare <2 x float> @llvm.minnum.v2f32(<2 x float>, <2 x float>) +declare <4 x float> @llvm.minnum.v4f32(<4 x float>, <4 x float>) +declare <2 x double> @llvm.minnum.v2f64(<2 x double>, <2 x double>) +declare <8 x double> @llvm.minnum.v8f64(<8 x double>, <8 x double>) + +; CHECK-LABEL: @test_fminf +; CHECK: jmp fminf +define float @test_fminf(float %x, float %y) { + %z = call float @fminf(float %x, float %y) readnone + ret float %z +} + +; CHECK-LABEL: @test_fmin +; CHECK: jmp fmin +define double @test_fmin(double %x, double %y) { + %z = call double @fmin(double %x, double %y) readnone + ret double %z +} + +; CHECK-LABEL: @test_fminl +; CHECK: calll fminl +define x86_fp80 @test_fminl(x86_fp80 %x, x86_fp80 %y) { + %z = call x86_fp80 @fminl(x86_fp80 %x, x86_fp80 %y) readnone + ret x86_fp80 %z +} + +; CHECK-LABEL: @test_intrinsic_fminf +; CHECK: jmp fminf +define float @test_intrinsic_fminf(float %x, float %y) { + %z = call float @llvm.minnum.f32(float %x, float %y) readnone + ret float %z +} + +; CHECK-LABEL: @test_intrinsic_fmin +; CHECK: jmp fmin +define double @test_intrinsic_fmin(double %x, double %y) { + %z = call double @llvm.minnum.f64(double %x, double %y) readnone + ret double %z +} + +; CHECK-LABEL: @test_intrinsic_fminl +; CHECK: calll fminl +define x86_fp80 @test_intrinsic_fminl(x86_fp80 %x, x86_fp80 %y) { + %z = call x86_fp80 @llvm.minnum.f80(x86_fp80 %x, x86_fp80 %y) readnone + ret x86_fp80 %z +} + +; CHECK-LABEL: @test_intrinsic_fmin_v2f32 +; CHECK: calll fminf +; CHECK: calll fminf +define <2 x float> @test_intrinsic_fmin_v2f32(<2 x float> %x, <2 x float> %y) { + %z = call <2 x float> @llvm.minnum.v2f32(<2 x float> %x, <2 x float> %y) readnone + ret <2 x float> %z +} + +; CHECK-LABEL: @test_intrinsic_fmin_v4f32 +; CHECK: calll fminf +; CHECK: calll fminf +; CHECK: calll fminf +; CHECK: calll fminf +define <4 x float> @test_intrinsic_fmin_v4f32(<4 x float> %x, <4 x float> %y) { + %z = call <4 x float> @llvm.minnum.v4f32(<4 x float> %x, <4 x float> %y) readnone + ret <4 x float> %z +} + +; CHECK-LABEL: @test_intrinsic_fmin_v2f64 +; CHECK: calll fmin +; CHECK: calll fmin +define <2 x double> @test_intrinsic_fmin_v2f64(<2 x double> %x, <2 x double> %y) { + %z = call <2 x double> @llvm.minnum.v2f64(<2 x double> %x, <2 x double> %y) readnone + ret <2 x double> %z +} + +; CHECK-LABEL: @test_intrinsic_fmin_v8f64 +; CHECK: calll fmin +; CHECK: calll fmin +; CHECK: calll fmin +; CHECK: calll fmin +; CHECK: calll fmin +; CHECK: calll fmin +; CHECK: calll fmin +; CHECK: calll fmin +define <8 x double> @test_intrinsic_fmin_v8f64(<8 x double> %x, <8 x double> %y) { + %z = call <8 x double> @llvm.minnum.v8f64(<8 x double> %x, <8 x double> %y) readnone + ret <8 x double> %z +} diff --git a/test/Transforms/InstCombine/maxnum.ll b/test/Transforms/InstCombine/maxnum.ll new file mode 100644 index 0000000..585d9f4 --- /dev/null +++ b/test/Transforms/InstCombine/maxnum.ll @@ -0,0 +1,222 @@ +; RUN: opt -S -instcombine < %s | FileCheck %s + +declare float @llvm.maxnum.f32(float, float) #0 +declare float @llvm.maxnum.v2f32(<2 x float>, <2 x float>) #0 +declare <4 x float> @llvm.maxnum.v4f32(<4 x float>, <4 x float>) #0 + +declare double @llvm.maxnum.f64(double, double) #0 +declare <2 x double> @llvm.maxnum.v2f64(<2 x double>, <2 x double>) #0 + +; CHECK-LABEL: @constant_fold_maxnum_f32 +; CHECK-NEXT: ret float 2.000000e+00 +define float @constant_fold_maxnum_f32() #0 { + %x = call float @llvm.maxnum.f32(float 1.0, float 2.0) #0 + ret float %x +} + +; CHECK-LABEL: @constant_fold_maxnum_f32_inv +; CHECK-NEXT: ret float 2.000000e+00 +define float @constant_fold_maxnum_f32_inv() #0 { + %x = call float @llvm.maxnum.f32(float 2.0, float 1.0) #0 + ret float %x +} + +; CHECK-LABEL: @constant_fold_maxnum_f32_nan0 +; CHECK-NEXT: ret float 2.000000e+00 +define float @constant_fold_maxnum_f32_nan0() #0 { + %x = call float @llvm.maxnum.f32(float 0x7FF8000000000000, float 2.0) #0 + ret float %x +} + +; CHECK-LABEL: @constant_fold_maxnum_f32_nan1 +; CHECK-NEXT: ret float 2.000000e+00 +define float @constant_fold_maxnum_f32_nan1() #0 { + %x = call float @llvm.maxnum.f32(float 2.0, float 0x7FF8000000000000) #0 + ret float %x +} + +; CHECK-LABEL: @constant_fold_maxnum_f32_nan_nan +; CHECK-NEXT: ret float 0x7FF8000000000000 +define float @constant_fold_maxnum_f32_nan_nan() #0 { + %x = call float @llvm.maxnum.f32(float 0x7FF8000000000000, float 0x7FF8000000000000) #0 + ret float %x +} + +; CHECK-LABEL: @constant_fold_maxnum_f32_p0_p0 +; CHECK-NEXT: ret float 0.000000e+00 +define float @constant_fold_maxnum_f32_p0_p0() #0 { + %x = call float @llvm.maxnum.f32(float 0.0, float 0.0) #0 + ret float %x +} + +; CHECK-LABEL: @constant_fold_maxnum_f32_p0_n0 +; CHECK-NEXT: ret float 0.000000e+00 +define float @constant_fold_maxnum_f32_p0_n0() #0 { + %x = call float @llvm.maxnum.f32(float 0.0, float -0.0) #0 + ret float %x +} + +; CHECK-LABEL: @constant_fold_maxnum_f32_n0_p0 +; CHECK-NEXT: ret float -0.000000e+00 +define float @constant_fold_maxnum_f32_n0_p0() #0 { + %x = call float @llvm.maxnum.f32(float -0.0, float 0.0) #0 + ret float %x +} + +; CHECK-LABEL: @constant_fold_maxnum_f32_n0_n0 +; CHECK-NEXT: ret float -0.000000e+00 +define float @constant_fold_maxnum_f32_n0_n0() #0 { + %x = call float @llvm.maxnum.f32(float -0.0, float -0.0) #0 + ret float %x +} + +; CHECK-LABEL: @constant_fold_maxnum_v4f32 +; CHECK-NEXT: ret <4 x float> +define <4 x float> @constant_fold_maxnum_v4f32() #0 { + %x = call <4 x float> @llvm.maxnum.v4f32(<4 x float> , <4 x float> ) + ret <4 x float> %x +} + +; CHECK-LABEL: @constant_fold_maxnum_f64 +; CHECK-NEXT: ret double 2.000000e+00 +define double @constant_fold_maxnum_f64() #0 { + %x = call double @llvm.maxnum.f64(double 1.0, double 2.0) #0 + ret double %x +} + +; CHECK-LABEL: @constant_fold_maxnum_f64_nan0 +; CHECK-NEXT: ret double 2.000000e+00 +define double @constant_fold_maxnum_f64_nan0() #0 { + %x = call double @llvm.maxnum.f64(double 0x7FF8000000000000, double 2.0) #0 + ret double %x +} + +; CHECK-LABEL: @constant_fold_maxnum_f64_nan1 +; CHECK-NEXT: ret double 2.000000e+00 +define double @constant_fold_maxnum_f64_nan1() #0 { + %x = call double @llvm.maxnum.f64(double 2.0, double 0x7FF8000000000000) #0 + ret double %x +} + +; CHECK-LABEL: @constant_fold_maxnum_f64_nan_nan +; CHECK-NEXT: ret double 0x7FF8000000000000 +define double @constant_fold_maxnum_f64_nan_nan() #0 { + %x = call double @llvm.maxnum.f64(double 0x7FF8000000000000, double 0x7FF8000000000000) #0 + ret double %x +} + +; CHECK-LABEL: @canonicalize_constant_maxnum_f32 +; CHECK: call float @llvm.maxnum.f32(float %x, float 1.000000e+00) +define float @canonicalize_constant_maxnum_f32(float %x) #0 { + %y = call float @llvm.maxnum.f32(float 1.0, float %x) #0 + ret float %y +} + +; CHECK-LABEL: @noop_maxnum_f32 +; CHECK-NEXT: ret float %x +define float @noop_maxnum_f32(float %x) #0 { + %y = call float @llvm.maxnum.f32(float %x, float %x) #0 + ret float %y +} + +; CHECK-LABEL: @maxnum_f32_nan_val +; CHECK-NEXT: ret float %x +define float @maxnum_f32_nan_val(float %x) #0 { + %y = call float @llvm.maxnum.f32(float 0x7FF8000000000000, float %x) #0 + ret float %y +} + +; CHECK-LABEL: @maxnum_f32_val_nan +; CHECK-NEXT: ret float %x +define float @maxnum_f32_val_nan(float %x) #0 { + %y = call float @llvm.maxnum.f32(float %x, float 0x7FF8000000000000) #0 + ret float %y +} + +; CHECK-LABEL: @fold_maxnum_f32_undef_undef +; CHECK-NEXT: ret float undef +define float @fold_maxnum_f32_undef_undef(float %x) nounwind { + %val = call float @llvm.maxnum.f32(float undef, float undef) #0 + ret float %val +} + +; CHECK-LABEL: @fold_maxnum_f32_val_undef +; CHECK-NEXT: ret float %x +define float @fold_maxnum_f32_val_undef(float %x) nounwind { + %val = call float @llvm.maxnum.f32(float %x, float undef) #0 + ret float %val +} + +; CHECK-LABEL: @fold_maxnum_f32_undef_val +; CHECK-NEXT: ret float %x +define float @fold_maxnum_f32_undef_val(float %x) nounwind { + %val = call float @llvm.maxnum.f32(float undef, float %x) #0 + ret float %val +} + +; CHECK-LABEL: @maxnum_x_maxnum_x_y +; CHECK-NEXT: call float @llvm.maxnum.f32(float %x, float %y) +; CHECK-NEXT: ret float +define float @maxnum_x_maxnum_x_y(float %x, float %y) #0 { + %a = call float @llvm.maxnum.f32(float %x, float %y) #0 + %b = call float @llvm.maxnum.f32(float %x, float %a) #0 + ret float %b +} + +; CHECK-LABEL: @maxnum_y_maxnum_x_y +; CHECK-NEXT: call float @llvm.maxnum.f32(float %x, float %y) +; CHECK-NEXT: ret float +define float @maxnum_y_maxnum_x_y(float %x, float %y) #0 { + %a = call float @llvm.maxnum.f32(float %x, float %y) #0 + %b = call float @llvm.maxnum.f32(float %y, float %a) #0 + ret float %b +} + +; CHECK-LABEL: @maxnum_z_maxnum_x_y +; CHECK-NEXT: call float @llvm.maxnum.f32(float %x, float %y) +; CHECK-NEXT: call float @llvm.maxnum.f32(float %z, float %a) +; CHECK-NEXT: ret float +define float @maxnum_z_maxnum_x_y(float %x, float %y, float %z) #0 { + %a = call float @llvm.maxnum.f32(float %x, float %y) #0 + %b = call float @llvm.maxnum.f32(float %z, float %a) #0 + ret float %b +} + +; CHECK-LABEL: @maxnum_maxnum_x_y_z +; CHECK-NEXT: call float @llvm.maxnum.f32(float %x, float %y) +; CHECK-NEXT: call float @llvm.maxnum.f32(float %a, float %z) +; CHECK-NEXT: ret float +define float @maxnum_maxnum_x_y_z(float %x, float %y, float %z) #0 { + %a = call float @llvm.maxnum.f32(float %x, float %y) #0 + %b = call float @llvm.maxnum.f32(float %a, float %z) #0 + ret float %b +} + +; CHECK-LABEL: @maxnum4 +; CHECK-NEXT: call float @llvm.maxnum.f32(float %x, float %y) +; CHECK-NEXT: call float @llvm.maxnum.f32(float %z, float %w) +; CHECK-NEXT: call float @llvm.maxnum.f32(float %a, float %b) +; CHECK-NEXT: ret float +define float @maxnum4(float %x, float %y, float %z, float %w) #0 { + %a = call float @llvm.maxnum.f32(float %x, float %y) #0 + %b = call float @llvm.maxnum.f32(float %z, float %w) #0 + %c = call float @llvm.maxnum.f32(float %a, float %b) #0 + ret float %c +} + +; CHECK-LABEL: @fold_maxnum_f32_inf_val +; CHECK-NEXT: ret float 0x7FF0000000000000 +define float @fold_maxnum_f32_inf_val(float %x) nounwind { + %val = call float @llvm.maxnum.f32(float 0x7FF0000000000000, float %x) #0 + ret float %val +} + +; CHECK-LABEL: @fold_maxnum_f32_neginf_val +; CHECK-NEXT: call float @llvm.maxnum.f32(float %x, float 0xFFF0000000000000) +; CHECK-NEXT: ret float +define float @fold_maxnum_f32_neginf_val(float %x) nounwind { + %val = call float @llvm.maxnum.f32(float 0xFFF0000000000000, float %x) #0 + ret float %val +} + +attributes #0 = { nounwind readnone } diff --git a/test/Transforms/InstCombine/minnum.ll b/test/Transforms/InstCombine/minnum.ll new file mode 100644 index 0000000..57d6e16 --- /dev/null +++ b/test/Transforms/InstCombine/minnum.ll @@ -0,0 +1,244 @@ +; RUN: opt -S -instcombine < %s | FileCheck %s + +declare float @llvm.minnum.f32(float, float) #0 +declare float @llvm.minnum.v2f32(<2 x float>, <2 x float>) #0 +declare <4 x float> @llvm.minnum.v4f32(<4 x float>, <4 x float>) #0 + +declare double @llvm.minnum.f64(double, double) #0 +declare <2 x double> @llvm.minnum.v2f64(<2 x double>, <2 x double>) #0 + +declare float @llvm.fmax.f32(float, float) #0 + +; CHECK-LABEL: @constant_fold_minnum_f32 +; CHECK-NEXT: ret float 1.000000e+00 +define float @constant_fold_minnum_f32() #0 { + %x = call float @llvm.minnum.f32(float 1.0, float 2.0) #0 + ret float %x +} + +; CHECK-LABEL: @constant_fold_minnum_f32_inv +; CHECK-NEXT: ret float 1.000000e+00 +define float @constant_fold_minnum_f32_inv() #0 { + %x = call float @llvm.minnum.f32(float 2.0, float 1.0) #0 + ret float %x +} + +; CHECK-LABEL: @constant_fold_minnum_f32_nan0 +; CHECK-NEXT: ret float 2.000000e+00 +define float @constant_fold_minnum_f32_nan0() #0 { + %x = call float @llvm.minnum.f32(float 0x7FF8000000000000, float 2.0) #0 + ret float %x +} + +; CHECK-LABEL: @constant_fold_minnum_f32_nan1 +; CHECK-NEXT: ret float 2.000000e+00 +define float @constant_fold_minnum_f32_nan1() #0 { + %x = call float @llvm.minnum.f32(float 2.0, float 0x7FF8000000000000) #0 + ret float %x +} + +; CHECK-LABEL: @constant_fold_minnum_f32_nan_nan +; CHECK-NEXT: ret float 0x7FF8000000000000 +define float @constant_fold_minnum_f32_nan_nan() #0 { + %x = call float @llvm.minnum.f32(float 0x7FF8000000000000, float 0x7FF8000000000000) #0 + ret float %x +} + +; CHECK-LABEL: @constant_fold_minnum_f32_p0_p0 +; CHECK-NEXT: ret float 0.000000e+00 +define float @constant_fold_minnum_f32_p0_p0() #0 { + %x = call float @llvm.minnum.f32(float 0.0, float 0.0) #0 + ret float %x +} + +; CHECK-LABEL: @constant_fold_minnum_f32_p0_n0 +; CHECK-NEXT: ret float 0.000000e+00 +define float @constant_fold_minnum_f32_p0_n0() #0 { + %x = call float @llvm.minnum.f32(float 0.0, float -0.0) #0 + ret float %x +} + +; CHECK-LABEL: @constant_fold_minnum_f32_n0_p0 +; CHECK-NEXT: ret float -0.000000e+00 +define float @constant_fold_minnum_f32_n0_p0() #0 { + %x = call float @llvm.minnum.f32(float -0.0, float 0.0) #0 + ret float %x +} + +; CHECK-LABEL: @constant_fold_minnum_f32_n0_n0 +; CHECK-NEXT: ret float -0.000000e+00 +define float @constant_fold_minnum_f32_n0_n0() #0 { + %x = call float @llvm.minnum.f32(float -0.0, float -0.0) #0 + ret float %x +} + +; CHECK-LABEL: @constant_fold_minnum_v4f32 +; CHECK-NEXT: ret <4 x float> +define <4 x float> @constant_fold_minnum_v4f32() #0 { + %x = call <4 x float> @llvm.minnum.v4f32(<4 x float> , <4 x float> ) + ret <4 x float> %x +} + +; CHECK-LABEL: @constant_fold_minnum_f64 +; CHECK-NEXT: ret double 1.000000e+00 +define double @constant_fold_minnum_f64() #0 { + %x = call double @llvm.minnum.f64(double 1.0, double 2.0) #0 + ret double %x +} + +; CHECK-LABEL: @constant_fold_minnum_f64_nan0 +; CHECK-NEXT: ret double 2.000000e+00 +define double @constant_fold_minnum_f64_nan0() #0 { + %x = call double @llvm.minnum.f64(double 0x7FF8000000000000, double 2.0) #0 + ret double %x +} + +; CHECK-LABEL: @constant_fold_minnum_f64_nan1 +; CHECK-NEXT: ret double 2.000000e+00 +define double @constant_fold_minnum_f64_nan1() #0 { + %x = call double @llvm.minnum.f64(double 2.0, double 0x7FF8000000000000) #0 + ret double %x +} + +; CHECK-LABEL: @constant_fold_minnum_f64_nan_nan +; CHECK-NEXT: ret double 0x7FF8000000000000 +define double @constant_fold_minnum_f64_nan_nan() #0 { + %x = call double @llvm.minnum.f64(double 0x7FF8000000000000, double 0x7FF8000000000000) #0 + ret double %x +} + +; CHECK-LABEL: @canonicalize_constant_minnum_f32 +; CHECK: call float @llvm.minnum.f32(float %x, float 1.000000e+00) +define float @canonicalize_constant_minnum_f32(float %x) #0 { + %y = call float @llvm.minnum.f32(float 1.0, float %x) #0 + ret float %y +} + +; CHECK-LABEL: @noop_minnum_f32 +; CHECK-NEXT: ret float %x +define float @noop_minnum_f32(float %x) #0 { + %y = call float @llvm.minnum.f32(float %x, float %x) #0 + ret float %y +} + +; CHECK-LABEL: @minnum_f32_nan_val +; CHECK-NEXT: ret float %x +define float @minnum_f32_nan_val(float %x) #0 { + %y = call float @llvm.minnum.f32(float 0x7FF8000000000000, float %x) #0 + ret float %y +} + +; CHECK-LABEL: @minnum_f32_val_nan +; CHECK-NEXT: ret float %x +define float @minnum_f32_val_nan(float %x) #0 { + %y = call float @llvm.minnum.f32(float %x, float 0x7FF8000000000000) #0 + ret float %y +} + +; CHECK-LABEL: @fold_minnum_f32_undef_undef +; CHECK-NEXT: ret float undef +define float @fold_minnum_f32_undef_undef(float %x) nounwind { + %val = call float @llvm.minnum.f32(float undef, float undef) #0 + ret float %val +} + +; CHECK-LABEL: @fold_minnum_f32_val_undef +; CHECK-NEXT: ret float %x +define float @fold_minnum_f32_val_undef(float %x) nounwind { + %val = call float @llvm.minnum.f32(float %x, float undef) #0 + ret float %val +} + +; CHECK-LABEL: @fold_minnum_f32_undef_val +; CHECK-NEXT: ret float %x +define float @fold_minnum_f32_undef_val(float %x) nounwind { + %val = call float @llvm.minnum.f32(float undef, float %x) #0 + ret float %val +} + +; CHECK-LABEL: @minnum_x_minnum_x_y +; CHECK-NEXT: call float @llvm.minnum.f32(float %x, float %y) +; CHECK-NEXT: ret float +define float @minnum_x_minnum_x_y(float %x, float %y) #0 { + %a = call float @llvm.minnum.f32(float %x, float %y) #0 + %b = call float @llvm.minnum.f32(float %x, float %a) #0 + ret float %b +} + +; CHECK-LABEL: @minnum_y_minnum_x_y +; CHECK-NEXT: call float @llvm.minnum.f32(float %x, float %y) +; CHECK-NEXT: ret float +define float @minnum_y_minnum_x_y(float %x, float %y) #0 { + %a = call float @llvm.minnum.f32(float %x, float %y) #0 + %b = call float @llvm.minnum.f32(float %y, float %a) #0 + ret float %b +} + +; CHECK-LABEL: @minnum_z_minnum_x_y +; CHECK-NEXT: call float @llvm.minnum.f32(float %x, float %y) +; CHECK-NEXT: call float @llvm.minnum.f32(float %z, float %a) +; CHECK-NEXT: ret float +define float @minnum_z_minnum_x_y(float %x, float %y, float %z) #0 { + %a = call float @llvm.minnum.f32(float %x, float %y) #0 + %b = call float @llvm.minnum.f32(float %z, float %a) #0 + ret float %b +} + +; CHECK-LABEL: @minnum_minnum_x_y_z +; CHECK-NEXT: call float @llvm.minnum.f32(float %x, float %y) +; CHECK-NEXT: call float @llvm.minnum.f32(float %a, float %z) +; CHECK-NEXT: ret float +define float @minnum_minnum_x_y_z(float %x, float %y, float %z) #0 { + %a = call float @llvm.minnum.f32(float %x, float %y) #0 + %b = call float @llvm.minnum.f32(float %a, float %z) #0 + ret float %b +} + +; CHECK-LABEL: @minnum4 +; CHECK-NEXT: call float @llvm.minnum.f32(float %x, float %y) +; CHECK-NEXT: call float @llvm.minnum.f32(float %z, float %w) +; CHECK-NEXT: call float @llvm.minnum.f32(float %a, float %b) +; CHECK-NEXT: ret float +define float @minnum4(float %x, float %y, float %z, float %w) #0 { + %a = call float @llvm.minnum.f32(float %x, float %y) #0 + %b = call float @llvm.minnum.f32(float %z, float %w) #0 + %c = call float @llvm.minnum.f32(float %a, float %b) #0 + ret float %c +} + +; CHECK-LABEL: @minnum_x_fmax_x_y +; CHECK-NEXT: call float @llvm.fmax.f32 +; CHECK-NEXT: call float @llvm.minnum.f32 +; CHECK-NEXT: ret float +define float @minnum_x_fmax_x_y(float %x, float %y) #0 { + %a = call float @llvm.fmax.f32(float %x, float %y) #0 + %b = call float @llvm.minnum.f32(float %x, float %a) #0 + ret float %b +} + +; CHECK-LABEL: @fmax_x_minnum_x_y +; CHECK-NEXT: call float @llvm.minnum.f32 +; CHECK-NEXT: call float @llvm.fmax.f32 +; CHECK-NEXT: ret float +define float @fmax_x_minnum_x_y(float %x, float %y) #0 { + %a = call float @llvm.minnum.f32(float %x, float %y) #0 + %b = call float @llvm.fmax.f32(float %x, float %a) #0 + ret float %b +} + +; CHECK-LABEL: @fold_minnum_f32_inf_val +; CHECK-NEXT: call float @llvm.minnum.f32(float %x, float 0x7FF0000000000000) +; CHECK-NEXT: ret float +define float @fold_minnum_f32_inf_val(float %x) nounwind { + %val = call float @llvm.minnum.f32(float 0x7FF0000000000000, float %x) #0 + ret float %val +} + +; CHECK-LABEL: @fold_minnum_f32_minf_val +; CHECK-NEXT: ret float 0xFFF0000000000000 +define float @fold_minnum_f32_minf_val(float %x) nounwind { + %val = call float @llvm.minnum.f32(float 0xFFF0000000000000, float %x) #0 + ret float %val +} + +attributes #0 = { nounwind readnone } diff --git a/test/Transforms/LoopVectorize/intrinsic.ll b/test/Transforms/LoopVectorize/intrinsic.ll index 80149d2..d48731a 100644 --- a/test/Transforms/LoopVectorize/intrinsic.ll +++ b/test/Transforms/LoopVectorize/intrinsic.ll @@ -1192,3 +1192,59 @@ for.body: ; preds = %entry, %for.body for.end: ; preds = %for.body, %entry ret void } + +declare float @llvm.minnum.f32(float, float) nounwind readnone + +;CHECK-LABEL: @minnum_f32( +;CHECK: llvm.minnum.v4f32 +;CHECK: ret void +define void @minnum_f32(i32 %n, float* noalias %y, float* noalias %x, float* noalias %z) nounwind uwtable { +entry: + %cmp9 = icmp sgt i32 %n, 0 + br i1 %cmp9, label %for.body, label %for.end + +for.body: ; preds = %entry, %for.body + %indvars.iv = phi i64 [ %indvars.iv.next, %for.body ], [ 0, %entry ] + %arrayidx = getelementptr inbounds float* %y, i64 %indvars.iv + %0 = load float* %arrayidx, align 4 + %arrayidx2 = getelementptr inbounds float* %z, i64 %indvars.iv + %1 = load float* %arrayidx2, align 4 + %call = tail call float @llvm.minnum.f32(float %0, float %1) nounwind readnone + %arrayidx4 = getelementptr inbounds float* %x, i64 %indvars.iv + store float %call, float* %arrayidx4, align 4 + %indvars.iv.next = add i64 %indvars.iv, 1 + %lftr.wideiv = trunc i64 %indvars.iv.next to i32 + %exitcond = icmp eq i32 %lftr.wideiv, %n + br i1 %exitcond, label %for.end, label %for.body + +for.end: ; preds = %for.body, %entry + ret void +} + +declare float @llvm.maxnum.f32(float, float) nounwind readnone + +;CHECK-LABEL: @maxnum_f32( +;CHECK: llvm.maxnum.v4f32 +;CHECK: ret void +define void @maxnum_f32(i32 %n, float* noalias %y, float* noalias %x, float* noalias %z) nounwind uwtable { +entry: + %cmp9 = icmp sgt i32 %n, 0 + br i1 %cmp9, label %for.body, label %for.end + +for.body: ; preds = %entry, %for.body + %indvars.iv = phi i64 [ %indvars.iv.next, %for.body ], [ 0, %entry ] + %arrayidx = getelementptr inbounds float* %y, i64 %indvars.iv + %0 = load float* %arrayidx, align 4 + %arrayidx2 = getelementptr inbounds float* %z, i64 %indvars.iv + %1 = load float* %arrayidx2, align 4 + %call = tail call float @llvm.maxnum.f32(float %0, float %1) nounwind readnone + %arrayidx4 = getelementptr inbounds float* %x, i64 %indvars.iv + store float %call, float* %arrayidx4, align 4 + %indvars.iv.next = add i64 %indvars.iv, 1 + %lftr.wideiv = trunc i64 %indvars.iv.next to i32 + %exitcond = icmp eq i32 %lftr.wideiv, %n + br i1 %exitcond, label %for.end, label %for.body + +for.end: ; preds = %for.body, %entry + ret void +} diff --git a/test/Transforms/SimplifyCFG/speculate-math.ll b/test/Transforms/SimplifyCFG/speculate-math.ll index 29583b6..0ba93d2 100644 --- a/test/Transforms/SimplifyCFG/speculate-math.ll +++ b/test/Transforms/SimplifyCFG/speculate-math.ll @@ -4,6 +4,8 @@ declare float @llvm.sqrt.f32(float) nounwind readonly declare float @llvm.fma.f32(float, float, float) nounwind readonly declare float @llvm.fmuladd.f32(float, float, float) nounwind readonly declare float @llvm.fabs.f32(float) nounwind readonly +declare float @llvm.minnum.f32(float, float) nounwind readonly +declare float @llvm.maxnum.f32(float, float) nounwind readonly ; CHECK-LABEL: @sqrt_test( ; CHECK: select @@ -73,3 +75,36 @@ test_fmuladd.exit: ; preds = %cond.else.i, %en ret void } +; CHECK-LABEL: @minnum_test( +; CHECK: select +define void @minnum_test(float addrspace(1)* noalias nocapture %out, float %a, float %b) nounwind { +entry: + %cmp.i = fcmp olt float %a, 0.000000e+00 + br i1 %cmp.i, label %test_minnum.exit, label %cond.else.i + +cond.else.i: ; preds = %entry + %0 = tail call float @llvm.minnum.f32(float %a, float %b) nounwind readnone + br label %test_minnum.exit + +test_minnum.exit: ; preds = %cond.else.i, %entry + %cond.i = phi float [ %0, %cond.else.i ], [ 0x7FF8000000000000, %entry ] + store float %cond.i, float addrspace(1)* %out, align 4 + ret void +} + +; CHECK-LABEL: @maxnum_test( +; CHECK: select +define void @maxnum_test(float addrspace(1)* noalias nocapture %out, float %a, float %b) nounwind { +entry: + %cmp.i = fcmp olt float %a, 0.000000e+00 + br i1 %cmp.i, label %test_maxnum.exit, label %cond.else.i + +cond.else.i: ; preds = %entry + %0 = tail call float @llvm.maxnum.f32(float %a, float %b) nounwind readnone + br label %test_maxnum.exit + +test_maxnum.exit: ; preds = %cond.else.i, %entry + %cond.i = phi float [ %0, %cond.else.i ], [ 0x7FF8000000000000, %entry ] + store float %cond.i, float addrspace(1)* %out, align 4 + ret void +} -- 1.8.4.3