From e9538ca02fed4dd61020812e75f74653f1f01602 Mon Sep 17 00:00:00 2001 From: Serge Pavlov Date: Tue, 26 May 2020 19:24:05 +0700 Subject: [PATCH] [FPEnv] Intrinsic llvm.roundeven This intrinsic implements IEEE-754 operation roundToIntegralTiesToEven, and performs rounding to the nearest integer value, rounding halfway cases to even. The intrinsic represents the missed case of IEEE-754 rounding operations and now llvm provides full support of the rounding operations defined by the standard. Differential Revision: https://reviews.llvm.org/D75670 --- docs/LangRef.rst | 74 ++++++++++++++++ include/llvm/Analysis/TargetLibraryInfo.def | 9 ++ include/llvm/CodeGen/BasicTTIImpl.h | 3 + include/llvm/CodeGen/ISDOpcodes.h | 2 + include/llvm/IR/ConstrainedOps.def | 1 + include/llvm/IR/Intrinsics.td | 4 + include/llvm/IR/RuntimeLibcalls.def | 5 ++ lib/Analysis/ConstantFolding.cpp | 7 ++ lib/Analysis/InstructionSimplify.cpp | 2 + lib/Analysis/TargetLibraryInfo.cpp | 3 + lib/Analysis/ValueTracking.cpp | 5 ++ lib/Analysis/VectorUtils.cpp | 1 + lib/CodeGen/IntrinsicLowering.cpp | 4 + lib/CodeGen/SelectionDAG/LegalizeDAG.cpp | 9 ++ .../SelectionDAG/LegalizeFloatTypes.cpp | 25 ++++++ lib/CodeGen/SelectionDAG/LegalizeTypes.h | 2 + .../SelectionDAG/LegalizeVectorOps.cpp | 1 + .../SelectionDAG/LegalizeVectorTypes.cpp | 3 + lib/CodeGen/SelectionDAG/SelectionDAG.cpp | 1 + .../SelectionDAG/SelectionDAGBuilder.cpp | 2 + .../SelectionDAG/SelectionDAGDumper.cpp | 2 + lib/CodeGen/TargetLoweringBase.cpp | 2 + .../InstCombine/InstCombineCalls.cpp | 1 + .../InstCombine/InstCombineCasts.cpp | 1 + lib/Transforms/Utils/SimplifyLibCalls.cpp | 2 + test/CodeGen/Generic/fpoperations.ll | 21 +++++ .../ExecutionEngine/Interpreter/intrinsics.ll | 4 + .../InstCombine/double-float-shrink-2.ll | 86 +++++++++++++++++++ .../InstCombine/float-shrink-compare.ll | 54 ++++++++++++ .../InstSimplify/known-never-nan.ll | 11 +++ .../InstSimplify/round-intrinsics.ll | 11 +++ test/Transforms/LICM/hoist-round.ll | 5 +- test/Transforms/LoopVectorize/intrinsic.ll | 52 +++++++++++ unittests/Analysis/TargetLibraryInfoTest.cpp | 3 + unittests/IR/IRBuilderTest.cpp | 25 ++++++ 35 files changed, 442 insertions(+), 1 deletion(-) create mode 100644 test/CodeGen/Generic/fpoperations.ll diff --git a/docs/LangRef.rst b/docs/LangRef.rst index 8bcad09964e..01f41a7ea3f 100644 --- a/docs/LangRef.rst +++ b/docs/LangRef.rst @@ -13160,6 +13160,44 @@ Semantics: This function returns the same values as the libm ``round`` functions would, and handles error conditions in the same way. +'``llvm.roundeven.*``' Intrinsic +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Syntax: +""""""" + +This is an overloaded intrinsic. You can use ``llvm.roundeven`` on any +floating-point or vector of floating-point type. Not all targets support +all types however. + +:: + + declare float @llvm.roundeven.f32(float %Val) + declare double @llvm.roundeven.f64(double %Val) + declare x86_fp80 @llvm.roundeven.f80(x86_fp80 %Val) + declare fp128 @llvm.roundeven.f128(fp128 %Val) + declare ppc_fp128 @llvm.roundeven.ppcf128(ppc_fp128 %Val) + +Overview: +""""""""" + +The '``llvm.roundeven.*``' intrinsics returns the operand rounded to the nearest +integer in floating-point format rounding halfway cases to even (that is, to the +nearest value that is an even integer). + +Arguments: +"""""""""" + +The argument and return value are floating-point numbers of the same type. + +Semantics: +"""""""""" + +This function implements IEEE-754 operation ``roundToIntegralTiesToEven``. It +also behaves in the same way as C standard function ``roundeven``, except that +it does not raise floating point exceptions. + + '``llvm.lround.*``' Intrinsic ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -18174,6 +18212,42 @@ This function returns the same values as the libm ``round`` functions would and handles error conditions in the same way. +'``llvm.experimental.constrained.roundeven``' Intrinsic +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Syntax: +""""""" + +:: + + declare + @llvm.experimental.constrained.roundeven( , + metadata ) + +Overview: +""""""""" + +The '``llvm.experimental.constrained.roundeven``' intrinsic returns the first +operand rounded to the nearest integer in floating-point format, rounding +halfway cases to even (that is, to the nearest value that is an even integer), +regardless of the current rounding direction. + +Arguments: +"""""""""" + +The first argument and the return value are floating-point numbers of the same +type. + +The second argument specifies the exception behavior as described above. + +Semantics: +"""""""""" + +This function implements IEEE-754 operation ``roundToIntegralTiesToEven``. It +also behaves in the same way as C standard function ``roundeven`` and can signal +the invalid operation exception for a SNAN operand. + + '``llvm.experimental.constrained.lround``' Intrinsic ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/include/llvm/Analysis/TargetLibraryInfo.def b/include/llvm/Analysis/TargetLibraryInfo.def index f782c56d96a..0022e7b8b55 100644 --- a/include/llvm/Analysis/TargetLibraryInfo.def +++ b/include/llvm/Analysis/TargetLibraryInfo.def @@ -1158,6 +1158,15 @@ TLI_DEFINE_STRING_INTERNAL("rmdir") /// double round(double x); TLI_DEFINE_ENUM_INTERNAL(round) TLI_DEFINE_STRING_INTERNAL("round") +/// double roundeven(double x); +TLI_DEFINE_ENUM_INTERNAL(roundeven) +TLI_DEFINE_STRING_INTERNAL("roundeven") +/// float roundevenf(float x); +TLI_DEFINE_ENUM_INTERNAL(roundevenf) +TLI_DEFINE_STRING_INTERNAL("roundevenf") +/// long double roundevenl(long double x); +TLI_DEFINE_ENUM_INTERNAL(roundevenl) +TLI_DEFINE_STRING_INTERNAL("roundevenl") /// float roundf(float x); TLI_DEFINE_ENUM_INTERNAL(roundf) TLI_DEFINE_STRING_INTERNAL("roundf") diff --git a/include/llvm/CodeGen/BasicTTIImpl.h b/include/llvm/CodeGen/BasicTTIImpl.h index 7866e71853c..cc751a5b478 100644 --- a/include/llvm/CodeGen/BasicTTIImpl.h +++ b/include/llvm/CodeGen/BasicTTIImpl.h @@ -1328,6 +1328,9 @@ public: case Intrinsic::round: ISDs.push_back(ISD::FROUND); break; + case Intrinsic::roundeven: + ISDs.push_back(ISD::FROUNDEVEN); + break; case Intrinsic::pow: ISDs.push_back(ISD::FPOW); break; diff --git a/include/llvm/CodeGen/ISDOpcodes.h b/include/llvm/CodeGen/ISDOpcodes.h index 839e82d9d84..f081a53263e 100644 --- a/include/llvm/CodeGen/ISDOpcodes.h +++ b/include/llvm/CodeGen/ISDOpcodes.h @@ -376,6 +376,7 @@ enum NodeType { STRICT_FCEIL, STRICT_FFLOOR, STRICT_FROUND, + STRICT_FROUNDEVEN, STRICT_FTRUNC, STRICT_LROUND, STRICT_LLROUND, @@ -752,6 +753,7 @@ enum NodeType { FRINT, FNEARBYINT, FROUND, + FROUNDEVEN, FFLOOR, LROUND, LLROUND, diff --git a/include/llvm/IR/ConstrainedOps.def b/include/llvm/IR/ConstrainedOps.def index 9be92b36da9..ecba68fe0c0 100644 --- a/include/llvm/IR/ConstrainedOps.def +++ b/include/llvm/IR/ConstrainedOps.def @@ -91,6 +91,7 @@ DAG_FUNCTION(pow, 2, 1, experimental_constrained_pow, FPOW) DAG_FUNCTION(powi, 2, 1, experimental_constrained_powi, FPOWI) DAG_FUNCTION(rint, 1, 1, experimental_constrained_rint, FRINT) DAG_FUNCTION(round, 1, 0, experimental_constrained_round, FROUND) +DAG_FUNCTION(roundeven, 1, 0, experimental_constrained_roundeven, FROUNDEVEN) DAG_FUNCTION(sin, 1, 1, experimental_constrained_sin, FSIN) DAG_FUNCTION(sqrt, 1, 1, experimental_constrained_sqrt, FSQRT) DAG_FUNCTION(trunc, 1, 0, experimental_constrained_trunc, FTRUNC) diff --git a/include/llvm/IR/Intrinsics.td b/include/llvm/IR/Intrinsics.td index 51df06cee35..7bfb25b0ed7 100644 --- a/include/llvm/IR/Intrinsics.td +++ b/include/llvm/IR/Intrinsics.td @@ -579,6 +579,7 @@ let IntrProperties = [IntrNoMem, IntrSpeculatable, IntrWillReturn] in { def int_rint : Intrinsic<[llvm_anyfloat_ty], [LLVMMatchType<0>]>; def int_nearbyint : Intrinsic<[llvm_anyfloat_ty], [LLVMMatchType<0>]>; def int_round : Intrinsic<[llvm_anyfloat_ty], [LLVMMatchType<0>]>; + def int_roundeven : Intrinsic<[llvm_anyfloat_ty], [LLVMMatchType<0>]>; def int_canonicalize : Intrinsic<[llvm_anyfloat_ty], [LLVMMatchType<0>], [IntrNoMem]>; @@ -783,6 +784,9 @@ let IntrProperties = [IntrInaccessibleMemOnly, IntrWillReturn] in { def int_experimental_constrained_round : Intrinsic<[ llvm_anyfloat_ty ], [ LLVMMatchType<0>, llvm_metadata_ty ]>; + def int_experimental_constrained_roundeven : Intrinsic<[ llvm_anyfloat_ty ], + [ LLVMMatchType<0>, + llvm_metadata_ty ]>; def int_experimental_constrained_trunc : Intrinsic<[ llvm_anyfloat_ty ], [ LLVMMatchType<0>, llvm_metadata_ty ]>; diff --git a/include/llvm/IR/RuntimeLibcalls.def b/include/llvm/IR/RuntimeLibcalls.def index fe2c32e3c97..903db6c7049 100644 --- a/include/llvm/IR/RuntimeLibcalls.def +++ b/include/llvm/IR/RuntimeLibcalls.def @@ -234,6 +234,11 @@ HANDLE_LIBCALL(ROUND_F64, "round") HANDLE_LIBCALL(ROUND_F80, "roundl") HANDLE_LIBCALL(ROUND_F128, "roundl") HANDLE_LIBCALL(ROUND_PPCF128, "roundl") +HANDLE_LIBCALL(ROUNDEVEN_F32, "roundevenf") +HANDLE_LIBCALL(ROUNDEVEN_F64, "roundeven") +HANDLE_LIBCALL(ROUNDEVEN_F80, "roundevenl") +HANDLE_LIBCALL(ROUNDEVEN_F128, "roundevenl") +HANDLE_LIBCALL(ROUNDEVEN_PPCF128, "roundevenl") HANDLE_LIBCALL(FLOOR_F32, "floorf") HANDLE_LIBCALL(FLOOR_F64, "floor") HANDLE_LIBCALL(FLOOR_F80, "floorl") diff --git a/lib/Analysis/ConstantFolding.cpp b/lib/Analysis/ConstantFolding.cpp index 4fdc73cdbe5..7eafc7a6623 100644 --- a/lib/Analysis/ConstantFolding.cpp +++ b/lib/Analysis/ConstantFolding.cpp @@ -1493,6 +1493,7 @@ bool llvm::canConstantFoldCallTo(const CallBase *Call, const Function *F) { case Intrinsic::ceil: case Intrinsic::floor: case Intrinsic::round: + case Intrinsic::roundeven: case Intrinsic::trunc: case Intrinsic::nearbyint: case Intrinsic::rint: @@ -1501,6 +1502,7 @@ bool llvm::canConstantFoldCallTo(const CallBase *Call, const Function *F) { case Intrinsic::experimental_constrained_ceil: case Intrinsic::experimental_constrained_floor: case Intrinsic::experimental_constrained_round: + case Intrinsic::experimental_constrained_roundeven: case Intrinsic::experimental_constrained_trunc: case Intrinsic::experimental_constrained_nearbyint: case Intrinsic::experimental_constrained_rint: @@ -1785,6 +1787,11 @@ static Constant *ConstantFoldScalarCall1(StringRef Name, return ConstantFP::get(Ty->getContext(), U); } + if (IntrinsicID == Intrinsic::roundeven) { + U.roundToIntegral(APFloat::rmNearestTiesToEven); + return ConstantFP::get(Ty->getContext(), U); + } + if (IntrinsicID == Intrinsic::ceil) { U.roundToIntegral(APFloat::rmTowardPositive); return ConstantFP::get(Ty->getContext(), U); diff --git a/lib/Analysis/InstructionSimplify.cpp b/lib/Analysis/InstructionSimplify.cpp index 15f5a9c672c..45850e41f97 100644 --- a/lib/Analysis/InstructionSimplify.cpp +++ b/lib/Analysis/InstructionSimplify.cpp @@ -5061,6 +5061,7 @@ static bool IsIdempotent(Intrinsic::ID ID) { case Intrinsic::rint: case Intrinsic::nearbyint: case Intrinsic::round: + case Intrinsic::roundeven: case Intrinsic::canonicalize: return true; } @@ -5176,6 +5177,7 @@ static Value *simplifyUnaryIntrinsic(Function *F, Value *Op0, case Intrinsic::trunc: case Intrinsic::ceil: case Intrinsic::round: + case Intrinsic::roundeven: case Intrinsic::nearbyint: case Intrinsic::rint: { // floor (sitofp x) -> sitofp x diff --git a/lib/Analysis/TargetLibraryInfo.cpp b/lib/Analysis/TargetLibraryInfo.cpp index cae71d130d7..336480e8b9d 100644 --- a/lib/Analysis/TargetLibraryInfo.cpp +++ b/lib/Analysis/TargetLibraryInfo.cpp @@ -1341,6 +1341,9 @@ bool TargetLibraryInfoImpl::isValidProtoForLibFunc(const FunctionType &FTy, case LibFunc_round: case LibFunc_roundf: case LibFunc_roundl: + case LibFunc_roundeven: + case LibFunc_roundevenf: + case LibFunc_roundevenl: case LibFunc_sin: case LibFunc_sinf: case LibFunc_sinh: diff --git a/lib/Analysis/ValueTracking.cpp b/lib/Analysis/ValueTracking.cpp index 1b73a206209..545dab7714d 100644 --- a/lib/Analysis/ValueTracking.cpp +++ b/lib/Analysis/ValueTracking.cpp @@ -3227,6 +3227,10 @@ Intrinsic::ID llvm::getIntrinsicForCallSite(const CallBase &CB, case LibFunc_roundf: case LibFunc_roundl: return Intrinsic::round; + case LibFunc_roundeven: + case LibFunc_roundevenf: + case LibFunc_roundevenl: + return Intrinsic::roundeven; case LibFunc_pow: case LibFunc_powf: case LibFunc_powl: @@ -3567,6 +3571,7 @@ bool llvm::isKnownNeverNaN(const Value *V, const TargetLibraryInfo *TLI, case Intrinsic::rint: case Intrinsic::nearbyint: case Intrinsic::round: + case Intrinsic::roundeven: return isKnownNeverNaN(II->getArgOperand(0), TLI, Depth + 1); case Intrinsic::sqrt: return isKnownNeverNaN(II->getArgOperand(0), TLI, Depth + 1) && diff --git a/lib/Analysis/VectorUtils.cpp b/lib/Analysis/VectorUtils.cpp index 8a8bb19f366..23531b65ea3 100644 --- a/lib/Analysis/VectorUtils.cpp +++ b/lib/Analysis/VectorUtils.cpp @@ -78,6 +78,7 @@ bool llvm::isTriviallyVectorizable(Intrinsic::ID ID) { case Intrinsic::rint: case Intrinsic::nearbyint: case Intrinsic::round: + case Intrinsic::roundeven: case Intrinsic::pow: case Intrinsic::fma: case Intrinsic::fmuladd: diff --git a/lib/CodeGen/IntrinsicLowering.cpp b/lib/CodeGen/IntrinsicLowering.cpp index d6635a6337a..e37c21e7659 100644 --- a/lib/CodeGen/IntrinsicLowering.cpp +++ b/lib/CodeGen/IntrinsicLowering.cpp @@ -421,6 +421,10 @@ void IntrinsicLowering::LowerIntrinsicCall(CallInst *CI) { ReplaceFPIntrinsicWithCall(CI, "roundf", "round", "roundl"); break; } + case Intrinsic::roundeven: { + ReplaceFPIntrinsicWithCall(CI, "roundevenf", "roundeven", "roundevenl"); + break; + } case Intrinsic::copysign: { ReplaceFPIntrinsicWithCall(CI, "copysignf", "copysign", "copysignl"); break; diff --git a/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp b/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp index 8bf6cb51414..2ffcc859f80 100644 --- a/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp +++ b/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp @@ -4107,6 +4107,14 @@ void SelectionDAGLegalize::ConvertNodeToLibcall(SDNode *Node) { RTLIB::ROUND_F128, RTLIB::ROUND_PPCF128, Results); break; + case ISD::FROUNDEVEN: + case ISD::STRICT_FROUNDEVEN: + ExpandFPLibCall(Node, RTLIB::ROUNDEVEN_F32, + RTLIB::ROUNDEVEN_F64, + RTLIB::ROUNDEVEN_F80, + RTLIB::ROUNDEVEN_F128, + RTLIB::ROUNDEVEN_PPCF128, Results); + break; case ISD::FPOWI: case ISD::STRICT_FPOWI: { RTLIB::Libcall LC; @@ -4601,6 +4609,7 @@ void SelectionDAGLegalize::PromoteNode(SDNode *Node) { case ISD::FRINT: case ISD::FNEARBYINT: case ISD::FROUND: + case ISD::FROUNDEVEN: case ISD::FTRUNC: case ISD::FNEG: case ISD::FSQRT: diff --git a/lib/CodeGen/SelectionDAG/LegalizeFloatTypes.cpp b/lib/CodeGen/SelectionDAG/LegalizeFloatTypes.cpp index 37e5abaae3e..7e8ad28f9b1 100644 --- a/lib/CodeGen/SelectionDAG/LegalizeFloatTypes.cpp +++ b/lib/CodeGen/SelectionDAG/LegalizeFloatTypes.cpp @@ -113,6 +113,8 @@ void DAGTypeLegalizer::SoftenFloatResult(SDNode *N, unsigned ResNo) { case ISD::FRINT: R = SoftenFloatRes_FRINT(N); break; case ISD::STRICT_FROUND: case ISD::FROUND: R = SoftenFloatRes_FROUND(N); break; + case ISD::STRICT_FROUNDEVEN: + case ISD::FROUNDEVEN: R = SoftenFloatRes_FROUNDEVEN(N); break; case ISD::STRICT_FSIN: case ISD::FSIN: R = SoftenFloatRes_FSIN(N); break; case ISD::STRICT_FSQRT: @@ -616,6 +618,15 @@ SDValue DAGTypeLegalizer::SoftenFloatRes_FROUND(SDNode *N) { RTLIB::ROUND_PPCF128)); } +SDValue DAGTypeLegalizer::SoftenFloatRes_FROUNDEVEN(SDNode *N) { + return SoftenFloatRes_Unary(N, GetFPLibCall(N->getValueType(0), + RTLIB::ROUNDEVEN_F32, + RTLIB::ROUNDEVEN_F64, + RTLIB::ROUNDEVEN_F80, + RTLIB::ROUNDEVEN_F128, + RTLIB::ROUNDEVEN_PPCF128)); +} + SDValue DAGTypeLegalizer::SoftenFloatRes_FSIN(SDNode *N) { return SoftenFloatRes_Unary(N, GetFPLibCall(N->getValueType(0), RTLIB::SIN_F32, @@ -1178,6 +1189,8 @@ void DAGTypeLegalizer::ExpandFloatResult(SDNode *N, unsigned ResNo) { case ISD::FRINT: ExpandFloatRes_FRINT(N, Lo, Hi); break; case ISD::STRICT_FROUND: case ISD::FROUND: ExpandFloatRes_FROUND(N, Lo, Hi); break; + case ISD::STRICT_FROUNDEVEN: + case ISD::FROUNDEVEN: ExpandFloatRes_FROUNDEVEN(N, Lo, Hi); break; case ISD::STRICT_FSIN: case ISD::FSIN: ExpandFloatRes_FSIN(N, Lo, Hi); break; case ISD::STRICT_FSQRT: @@ -1504,6 +1517,16 @@ void DAGTypeLegalizer::ExpandFloatRes_FROUND(SDNode *N, RTLIB::ROUND_PPCF128), Lo, Hi); } +void DAGTypeLegalizer::ExpandFloatRes_FROUNDEVEN(SDNode *N, + SDValue &Lo, SDValue &Hi) { + ExpandFloatRes_Unary(N, GetFPLibCall(N->getValueType(0), + RTLIB::ROUNDEVEN_F32, + RTLIB::ROUNDEVEN_F64, + RTLIB::ROUNDEVEN_F80, + RTLIB::ROUNDEVEN_F128, + RTLIB::ROUNDEVEN_PPCF128), Lo, Hi); +} + void DAGTypeLegalizer::ExpandFloatRes_FSIN(SDNode *N, SDValue &Lo, SDValue &Hi) { ExpandFloatRes_Unary(N, GetFPLibCall(N->getValueType(0), @@ -2136,6 +2159,7 @@ void DAGTypeLegalizer::PromoteFloatResult(SDNode *N, unsigned ResNo) { case ISD::FNEG: case ISD::FRINT: case ISD::FROUND: + case ISD::FROUNDEVEN: case ISD::FSIN: case ISD::FSQRT: case ISD::FTRUNC: @@ -2476,6 +2500,7 @@ void DAGTypeLegalizer::SoftPromoteHalfResult(SDNode *N, unsigned ResNo) { case ISD::FREEZE: case ISD::FRINT: case ISD::FROUND: + case ISD::FROUNDEVEN: case ISD::FSIN: case ISD::FSQRT: case ISD::FTRUNC: diff --git a/lib/CodeGen/SelectionDAG/LegalizeTypes.h b/lib/CodeGen/SelectionDAG/LegalizeTypes.h index b729565ef7e..4bc75ceb492 100644 --- a/lib/CodeGen/SelectionDAG/LegalizeTypes.h +++ b/lib/CodeGen/SelectionDAG/LegalizeTypes.h @@ -530,6 +530,7 @@ private: SDValue SoftenFloatRes_FREM(SDNode *N); SDValue SoftenFloatRes_FRINT(SDNode *N); SDValue SoftenFloatRes_FROUND(SDNode *N); + SDValue SoftenFloatRes_FROUNDEVEN(SDNode *N); SDValue SoftenFloatRes_FSIN(SDNode *N); SDValue SoftenFloatRes_FSQRT(SDNode *N); SDValue SoftenFloatRes_FSUB(SDNode *N); @@ -603,6 +604,7 @@ private: void ExpandFloatRes_FREM (SDNode *N, SDValue &Lo, SDValue &Hi); void ExpandFloatRes_FRINT (SDNode *N, SDValue &Lo, SDValue &Hi); void ExpandFloatRes_FROUND (SDNode *N, SDValue &Lo, SDValue &Hi); + void ExpandFloatRes_FROUNDEVEN(SDNode *N, SDValue &Lo, SDValue &Hi); void ExpandFloatRes_FSIN (SDNode *N, SDValue &Lo, SDValue &Hi); void ExpandFloatRes_FSQRT (SDNode *N, SDValue &Lo, SDValue &Hi); void ExpandFloatRes_FSUB (SDNode *N, SDValue &Lo, SDValue &Hi); diff --git a/lib/CodeGen/SelectionDAG/LegalizeVectorOps.cpp b/lib/CodeGen/SelectionDAG/LegalizeVectorOps.cpp index 8f746ec45f6..93ce338ff23 100644 --- a/lib/CodeGen/SelectionDAG/LegalizeVectorOps.cpp +++ b/lib/CodeGen/SelectionDAG/LegalizeVectorOps.cpp @@ -427,6 +427,7 @@ SDValue VectorLegalizer::LegalizeOp(SDValue Op) { case ISD::FRINT: case ISD::FNEARBYINT: case ISD::FROUND: + case ISD::FROUNDEVEN: case ISD::FFLOOR: case ISD::FP_ROUND: case ISD::FP_EXTEND: diff --git a/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp b/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp index 6601dc68223..ff2c8d3a8db 100644 --- a/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp +++ b/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp @@ -95,6 +95,7 @@ void DAGTypeLegalizer::ScalarizeVectorResult(SDNode *N, unsigned ResNo) { case ISD::FP_TO_UINT: case ISD::FRINT: case ISD::FROUND: + case ISD::FROUNDEVEN: case ISD::FSIN: case ISD::FSQRT: case ISD::FTRUNC: @@ -888,6 +889,7 @@ void DAGTypeLegalizer::SplitVectorResult(SDNode *N, unsigned ResNo) { case ISD::FP_TO_UINT: case ISD::FRINT: case ISD::FROUND: + case ISD::FROUNDEVEN: case ISD::FSIN: case ISD::FSQRT: case ISD::FTRUNC: @@ -2825,6 +2827,7 @@ void DAGTypeLegalizer::WidenVectorResult(SDNode *N, unsigned ResNo) { case ISD::FNEARBYINT: case ISD::FRINT: case ISD::FROUND: + case ISD::FROUNDEVEN: case ISD::FSIN: case ISD::FSQRT: case ISD::FTRUNC: { diff --git a/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/lib/CodeGen/SelectionDAG/SelectionDAG.cpp index 2dcab73b177..cfb15d6ca9d 100644 --- a/lib/CodeGen/SelectionDAG/SelectionDAG.cpp +++ b/lib/CodeGen/SelectionDAG/SelectionDAG.cpp @@ -4103,6 +4103,7 @@ bool SelectionDAG::isKnownNeverNaN(SDValue Op, bool SNaN, unsigned Depth) const case ISD::FFLOOR: case ISD::FCEIL: case ISD::FROUND: + case ISD::FROUNDEVEN: case ISD::FRINT: case ISD::FNEARBYINT: { if (SNaN) diff --git a/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp index efdf696f879..dd03e415910 100644 --- a/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ b/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -6072,6 +6072,7 @@ void SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I, case Intrinsic::rint: case Intrinsic::nearbyint: case Intrinsic::round: + case Intrinsic::roundeven: case Intrinsic::canonicalize: { unsigned Opcode; switch (Intrinsic) { @@ -6086,6 +6087,7 @@ void SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I, case Intrinsic::rint: Opcode = ISD::FRINT; break; case Intrinsic::nearbyint: Opcode = ISD::FNEARBYINT; break; case Intrinsic::round: Opcode = ISD::FROUND; break; + case Intrinsic::roundeven: Opcode = ISD::FROUNDEVEN; break; case Intrinsic::canonicalize: Opcode = ISD::FCANONICALIZE; break; } diff --git a/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp b/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp index 816b1dcded2..7f9b8b7b28a 100644 --- a/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp +++ b/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp @@ -211,6 +211,8 @@ std::string SDNode::getOperationName(const SelectionDAG *G) const { case ISD::STRICT_FNEARBYINT: return "strict_fnearbyint"; case ISD::FROUND: return "fround"; case ISD::STRICT_FROUND: return "strict_fround"; + case ISD::FROUNDEVEN: return "froundeven"; + case ISD::STRICT_FROUNDEVEN: return "strict_froundeven"; case ISD::FEXP: return "fexp"; case ISD::STRICT_FEXP: return "strict_fexp"; case ISD::FEXP2: return "fexp2"; diff --git a/lib/CodeGen/TargetLoweringBase.cpp b/lib/CodeGen/TargetLoweringBase.cpp index b8062672efe..62c3af95f95 100644 --- a/lib/CodeGen/TargetLoweringBase.cpp +++ b/lib/CodeGen/TargetLoweringBase.cpp @@ -693,6 +693,7 @@ void TargetLoweringBase::initActions() { // These library functions default to expand. setOperationAction(ISD::FROUND, VT, Expand); + setOperationAction(ISD::FROUNDEVEN, VT, Expand); setOperationAction(ISD::FPOWI, VT, Expand); // These operations default to expand for vector types. @@ -758,6 +759,7 @@ void TargetLoweringBase::initActions() { setOperationAction(ISD::FRINT, VT, Expand); setOperationAction(ISD::FTRUNC, VT, Expand); setOperationAction(ISD::FROUND, VT, Expand); + setOperationAction(ISD::FROUNDEVEN, VT, Expand); setOperationAction(ISD::LROUND, VT, Expand); setOperationAction(ISD::LLROUND, VT, Expand); setOperationAction(ISD::LRINT, VT, Expand); diff --git a/lib/Transforms/InstCombine/InstCombineCalls.cpp b/lib/Transforms/InstCombine/InstCombineCalls.cpp index 118013a3876..7e20d241bba 100644 --- a/lib/Transforms/InstCombine/InstCombineCalls.cpp +++ b/lib/Transforms/InstCombine/InstCombineCalls.cpp @@ -2422,6 +2422,7 @@ Instruction *InstCombiner::visitCallInst(CallInst &CI) { case Intrinsic::ceil: case Intrinsic::floor: case Intrinsic::round: + case Intrinsic::roundeven: case Intrinsic::nearbyint: case Intrinsic::rint: case Intrinsic::trunc: { diff --git a/lib/Transforms/InstCombine/InstCombineCasts.cpp b/lib/Transforms/InstCombine/InstCombineCasts.cpp index c68f9e89800..714d1ae8aae 100644 --- a/lib/Transforms/InstCombine/InstCombineCasts.cpp +++ b/lib/Transforms/InstCombine/InstCombineCasts.cpp @@ -1741,6 +1741,7 @@ Instruction *InstCombiner::visitFPTrunc(FPTruncInst &FPT) { case Intrinsic::nearbyint: case Intrinsic::rint: case Intrinsic::round: + case Intrinsic::roundeven: case Intrinsic::trunc: { Value *Src = II->getArgOperand(0); if (!Src->hasOneUse()) diff --git a/lib/Transforms/Utils/SimplifyLibCalls.cpp b/lib/Transforms/Utils/SimplifyLibCalls.cpp index 828f4ee5bbe..c32db981ee7 100644 --- a/lib/Transforms/Utils/SimplifyLibCalls.cpp +++ b/lib/Transforms/Utils/SimplifyLibCalls.cpp @@ -2930,6 +2930,8 @@ Value *LibCallSimplifier::optimizeFloatingPointLibCall(CallInst *CI, return replaceUnaryCall(CI, Builder, Intrinsic::floor); case LibFunc_round: return replaceUnaryCall(CI, Builder, Intrinsic::round); + case LibFunc_roundeven: + return replaceUnaryCall(CI, Builder, Intrinsic::roundeven); case LibFunc_nearbyint: return replaceUnaryCall(CI, Builder, Intrinsic::nearbyint); case LibFunc_rint: diff --git a/test/CodeGen/Generic/fpoperations.ll b/test/CodeGen/Generic/fpoperations.ll new file mode 100644 index 00000000000..53dd307db24 --- /dev/null +++ b/test/CodeGen/Generic/fpoperations.ll @@ -0,0 +1,21 @@ +; RUN: llc < %s | FileCheck %s + +; This test checks default lowering of the intrinsics operating floating point +; values. MSP430 is used as a target in this test because it does not have +; native FP support, so it won't get custom lowering for these intrinsics. +; +; REQUIRES: msp430-registered-target + +target datalayout = "e-p:16:16:16-i8:8:8-i16:16:16-i32:16:32-n8:16" +target triple = "msp430---elf" + + +define float @roundeven_01(float %x) { +entry: + %res = call float @llvm.roundeven.f32(float %x) + ret float %res +} +; CHECK-LABEL: roundeven_01: +; CHECK: call #roundeven + +declare float @llvm.roundeven.f32(float %x) diff --git a/test/ExecutionEngine/Interpreter/intrinsics.ll b/test/ExecutionEngine/Interpreter/intrinsics.ll index 49d0bbee304..468b6b7ab24 100644 --- a/test/ExecutionEngine/Interpreter/intrinsics.ll +++ b/test/ExecutionEngine/Interpreter/intrinsics.ll @@ -13,6 +13,8 @@ declare float @llvm.trunc.f32(float) declare double @llvm.trunc.f64(double) declare float @llvm.round.f32(float) declare double @llvm.round.f64(double) +declare float @llvm.roundeven.f32(float) +declare double @llvm.roundeven.f64(double) declare float @llvm.copysign.f32(float, float) declare double @llvm.copysign.f64(double, double) @@ -29,6 +31,8 @@ define i32 @main() { %trunc64 = call double @llvm.trunc.f64(double 0.000000e+00) %round32 = call float @llvm.round.f32(float 0.000000e+00) %round64 = call double @llvm.round.f64(double 0.000000e+00) + %roundeven32 = call float @llvm.roundeven.f32(float 0.000000e+00) + %roundeven64 = call double @llvm.roundeven.f64(double 0.000000e+00) %copysign32 = call float @llvm.copysign.f32(float 0.000000e+00, float 0.000000e+00) %copysign64 = call double @llvm.copysign.f64(double 0.000000e+00, double 0.000000e+00) ret i32 0 diff --git a/test/Transforms/InstCombine/double-float-shrink-2.ll b/test/Transforms/InstCombine/double-float-shrink-2.ll index 76e497bd68f..3a8f224b1d8 100644 --- a/test/Transforms/InstCombine/double-float-shrink-2.ll +++ b/test/Transforms/InstCombine/double-float-shrink-2.ll @@ -10,6 +10,7 @@ declare double @floor(double) declare double @ceil(double) declare double @round(double) +declare double @roundeven(double) declare double @nearbyint(double) declare double @trunc(double) declare double @fabs(double) @@ -32,6 +33,9 @@ declare <2 x float> @llvm.rint.v2f32(<2 x float>) declare double @llvm.round.f64(double) declare <2 x double> @llvm.round.v2f64(<2 x double>) +declare double @llvm.roundeven.f64(double) +declare <2 x double> @llvm.roundeven.v2f64(<2 x double>) + declare double @llvm.trunc.f64(double) declare <2 x double> @llvm.trunc.v2f64(<2 x double>) @@ -71,6 +75,18 @@ define float @test_shrink_libcall_round(float %C) { ret float %F } +define float @test_shrink_libcall_roundeven(float %C) { +; CHECK-LABEL: @test_shrink_libcall_roundeven( +; CHECK-NEXT: [[F:%.*]] = call float @llvm.roundeven.f32(float [[C:%.*]]) +; CHECK-NEXT: ret float [[F]] +; + %D = fpext float %C to double + ; --> roundeven + %E = call double @roundeven(double %D) + %F = fptrunc double %E to float + ret float %F +} + define float @test_shrink_libcall_nearbyint(float %C) { ; CHECK-LABEL: @test_shrink_libcall_nearbyint( ; CHECK-NEXT: [[F:%.*]] = call float @llvm.nearbyint.f32(float [[C:%.*]]) @@ -186,6 +202,17 @@ define float @test_shrink_intrin_round(float %C) { ret float %F } +define float @test_shrink_intrin_roundeven(float %C) { +; CHECK-LABEL: @test_shrink_intrin_roundeven( +; CHECK-NEXT: [[TMP1:%.*]] = call float @llvm.roundeven.f32(float [[C:%.*]]) +; CHECK-NEXT: ret float [[TMP1]] +; + %D = fpext float %C to double + %E = call double @llvm.roundeven.f64(double %D) + %F = fptrunc double %E to float + ret float %F +} + define float @test_shrink_intrin_trunc(float %C) { ; CHECK-LABEL: @test_shrink_intrin_trunc( ; CHECK-NEXT: [[TMP1:%.*]] = call float @llvm.trunc.f32(float [[C:%.*]]) @@ -292,6 +319,23 @@ define <2 x float> @test_shrink_intrin_round_multi_use(<2 x float> %C) { ret <2 x float> %F } +define <2 x float> @test_shrink_intrin_roundeven_multi_use(<2 x float> %C) { +; CHECK-LABEL: @test_shrink_intrin_roundeven_multi_use( +; CHECK-NEXT: [[D:%.*]] = fpext <2 x float> [[C:%.*]] to <2 x double> +; CHECK-NEXT: [[E:%.*]] = call <2 x double> @llvm.roundeven.v2f64(<2 x double> [[D]]) +; CHECK-NEXT: [[F:%.*]] = fptrunc <2 x double> [[E]] to <2 x float> +; CHECK-NEXT: call void @use_v2f64(<2 x double> [[D]]) +; CHECK-NEXT: call void @use_v2f64(<2 x double> [[E]]) +; CHECK-NEXT: ret <2 x float> [[F]] +; + %D = fpext <2 x float> %C to <2 x double> + %E = call <2 x double> @llvm.roundeven.v2f64(<2 x double> %D) + %F = fptrunc <2 x double> %E to <2 x float> + call void @use_v2f64(<2 x double> %D) + call void @use_v2f64(<2 x double> %E) + ret <2 x float> %F +} + define <2 x float> @test_shrink_intrin_trunc_multi_use(<2 x float> %C) { ; CHECK-LABEL: @test_shrink_intrin_trunc_multi_use( ; CHECK-NEXT: [[D:%.*]] = fpext <2 x float> [[C:%.*]] to <2 x double> @@ -352,6 +396,17 @@ define float @test_no_shrink_intrin_round(double %D) { ret float %F } +define float @test_no_shrink_intrin_roundeven(double %D) { +; CHECK-LABEL: @test_no_shrink_intrin_roundeven( +; CHECK-NEXT: [[E:%.*]] = call double @llvm.roundeven.f64(double [[D:%.*]]) +; CHECK-NEXT: [[F:%.*]] = fptrunc double [[E]] to float +; CHECK-NEXT: ret float [[F]] +; + %E = call double @llvm.roundeven.f64(double %D) + %F = fptrunc double %E to float + ret float %F +} + define float @test_no_shrink_intrin_nearbyint(double %D) { ; CHECK-LABEL: @test_no_shrink_intrin_nearbyint( ; CHECK-NEXT: [[E:%.*]] = call double @llvm.nearbyint.f64(double [[D:%.*]]) @@ -424,6 +479,15 @@ define float @test_shrink_float_convertible_constant_intrin_round() { ret float %F } +define float @test_shrink_float_convertible_constant_intrin_roundeven() { +; CHECK-LABEL: @test_shrink_float_convertible_constant_intrin_roundeven( +; CHECK-NEXT: ret float 2.000000e+00 +; + %E = call double @llvm.roundeven.f64(double 2.1) + %F = fptrunc double %E to float + ret float %F +} + define float @test_shrink_float_convertible_constant_intrin_nearbyint() { ; CHECK-LABEL: @test_shrink_float_convertible_constant_intrin_nearbyint( ; CHECK-NEXT: ret float 2.000000e+00 @@ -494,6 +558,17 @@ define half @test_no_shrink_mismatched_type_intrin_round(double %D) { ret half %F } +define half @test_no_shrink_mismatched_type_intrin_roundeven(double %D) { +; CHECK-LABEL: @test_no_shrink_mismatched_type_intrin_roundeven( +; CHECK-NEXT: [[E:%.*]] = call double @llvm.roundeven.f64(double [[D:%.*]]) +; CHECK-NEXT: [[F:%.*]] = fptrunc double [[E]] to half +; CHECK-NEXT: ret half [[F]] +; + %E = call double @llvm.roundeven.f64(double %D) + %F = fptrunc double %E to half + ret half %F +} + define half @test_no_shrink_mismatched_type_intrin_nearbyint(double %D) { ; CHECK-LABEL: @test_no_shrink_mismatched_type_intrin_nearbyint( ; CHECK-NEXT: [[E:%.*]] = call double @llvm.nearbyint.f64(double [[D:%.*]]) @@ -573,6 +648,17 @@ define <2 x double> @test_shrink_intrin_round_fp16_vec(<2 x half> %C) { ret <2 x double> %E } +define <2 x double> @test_shrink_intrin_roundeven_fp16_vec(<2 x half> %C) { +; CHECK-LABEL: @test_shrink_intrin_roundeven_fp16_vec( +; CHECK-NEXT: [[TMP1:%.*]] = call <2 x half> @llvm.roundeven.v2f16(<2 x half> [[C:%.*]]) +; CHECK-NEXT: [[E:%.*]] = fpext <2 x half> [[TMP1]] to <2 x double> +; CHECK-NEXT: ret <2 x double> [[E]] +; + %D = fpext <2 x half> %C to <2 x double> + %E = call <2 x double> @llvm.roundeven.v2f64(<2 x double> %D) + ret <2 x double> %E +} + define float @test_shrink_intrin_nearbyint_fp16_src(half %C) { ; CHECK-LABEL: @test_shrink_intrin_nearbyint_fp16_src( ; CHECK-NEXT: [[TMP1:%.*]] = call half @llvm.nearbyint.f16(half [[C:%.*]]) diff --git a/test/Transforms/InstCombine/float-shrink-compare.ll b/test/Transforms/InstCombine/float-shrink-compare.ll index ca2f6d1c23c..aa0dd5e3007 100644 --- a/test/Transforms/InstCombine/float-shrink-compare.ll +++ b/test/Transforms/InstCombine/float-shrink-compare.ll @@ -160,6 +160,32 @@ define i1 @test6_intrin(float %x, float %y) { ret i1 %cmp } +define i1 @test6a(float %x, float %y) { +; CHECK-LABEL: @test6a( +; CHECK-NEXT: [[ROUND:%.*]] = call float @llvm.roundeven.f32(float %x) +; CHECK-NEXT: [[CMP:%.*]] = fcmp oeq float [[ROUND]], %y +; CHECK-NEXT: ret i1 [[CMP]] +; + %x.ext = fpext float %x to double + %round = call double @roundeven(double %x.ext) nounwind readnone + %y.ext = fpext float %y to double + %cmp = fcmp oeq double %round, %y.ext + ret i1 %cmp +} + +define i1 @test6a_intrin(float %x, float %y) { +; CHECK-LABEL: @test6a_intrin( +; CHECK-NEXT: [[ROUND:%.*]] = call float @llvm.roundeven.f32(float %x) +; CHECK-NEXT: [[CMP:%.*]] = fcmp oeq float [[ROUND]], %y +; CHECK-NEXT: ret i1 [[CMP]] +; + %x.ext = fpext float %x to double + %round = call double @llvm.roundeven.f64(double %x.ext) nounwind readnone + %y.ext = fpext float %y to double + %cmp = fcmp oeq double %round, %y.ext + ret i1 %cmp +} + define i1 @test7(float %x, float %y) { ; CHECK-LABEL: @test7( ; CHECK-NEXT: [[TRUNC:%.*]] = call float @llvm.trunc.f32(float %x) @@ -329,6 +355,32 @@ define i1 @test13_intrin(float %x, float %y) { ret i1 %cmp } +define i1 @test13a(float %x, float %y) { +; CHECK-LABEL: @test13a( +; CHECK-NEXT: [[ROUND:%.*]] = call float @llvm.roundeven.f32(float %x) +; CHECK-NEXT: [[CMP:%.*]] = fcmp oeq float [[ROUND]], %y +; CHECK-NEXT: ret i1 [[CMP]] +; + %x.ext = fpext float %x to double + %y.ext = fpext float %y to double + %round = call double @roundeven(double %x.ext) nounwind readnone + %cmp = fcmp oeq double %y.ext, %round + ret i1 %cmp +} + +define i1 @test13a_intrin(float %x, float %y) { +; CHECK-LABEL: @test13a_intrin( +; CHECK-NEXT: [[ROUND:%.*]] = call float @llvm.roundeven.f32(float %x) +; CHECK-NEXT: [[CMP:%.*]] = fcmp oeq float [[ROUND]], %y +; CHECK-NEXT: ret i1 [[CMP]] +; + %x.ext = fpext float %x to double + %y.ext = fpext float %y to double + %round = call double @llvm.roundeven.f64(double %x.ext) nounwind readnone + %cmp = fcmp oeq double %y.ext, %round + ret i1 %cmp +} + define i1 @test14(float %x, float %y) { ; CHECK-LABEL: @test14( ; CHECK-NEXT: [[TRUNC:%.*]] = call float @llvm.trunc.f32(float %x) @@ -462,6 +514,7 @@ declare double @floor(double) nounwind readnone declare double @nearbyint(double) nounwind readnone declare double @rint(double) nounwind readnone declare double @round(double) nounwind readnone +declare double @roundeven(double) nounwind readnone declare double @trunc(double) nounwind readnone declare double @fmin(double, double) nounwind readnone declare double @fmax(double, double) nounwind readnone @@ -471,4 +524,5 @@ declare double @llvm.ceil.f64(double) nounwind readnone declare double @llvm.floor.f64(double) nounwind readnone declare double @llvm.nearbyint.f64(double) nounwind readnone declare double @llvm.round.f64(double) nounwind readnone +declare double @llvm.roundeven.f64(double) nounwind readnone declare double @llvm.trunc.f64(double) nounwind readnone diff --git a/test/Transforms/InstSimplify/known-never-nan.ll b/test/Transforms/InstSimplify/known-never-nan.ll index 109775607bc..c2c26e6ee97 100644 --- a/test/Transforms/InstSimplify/known-never-nan.ll +++ b/test/Transforms/InstSimplify/known-never-nan.ll @@ -147,6 +147,16 @@ define i1 @round_nnan_src(double %arg) { ret i1 %tmp } +define i1 @roundeven_nnan_src(double %arg) { +; CHECK-LABEL: @roundeven_nnan_src( +; CHECK-NEXT: ret i1 false +; + %nnan = fadd nnan double %arg, 1.0 + %op = call double @llvm.roundeven.f64(double %nnan) + %tmp = fcmp uno double %op, %op + ret i1 %tmp +} + define i1 @known_nan_select(i1 %cond, double %arg0, double %arg1) { ; CHECK-LABEL: @known_nan_select( ; CHECK-NEXT: ret i1 true @@ -416,3 +426,4 @@ declare double @llvm.trunc.f64(double) declare double @llvm.rint.f64(double) declare double @llvm.nearbyint.f64(double) declare double @llvm.round.f64(double) +declare double @llvm.roundeven.f64(double) diff --git a/test/Transforms/InstSimplify/round-intrinsics.ll b/test/Transforms/InstSimplify/round-intrinsics.ll index 42c78e000ac..3b63bd6be6f 100644 --- a/test/Transforms/InstSimplify/round-intrinsics.ll +++ b/test/Transforms/InstSimplify/round-intrinsics.ll @@ -81,6 +81,16 @@ define float @uitofp_round(i32 %arg) { ret float %round } +define float @uitofp_roundeven(i32 %arg) { +; CHECK-LABEL: @uitofp_roundeven( +; CHECK-NEXT: [[CVT:%.*]] = uitofp i32 [[ARG:%.*]] to float +; CHECK-NEXT: ret float [[CVT]] +; + %cvt = uitofp i32 %arg to float + %round = call float @llvm.roundeven.f32(float %cvt) + ret float %round +} + define float @sitofp_nearbyint(i32 %arg) { ; CHECK-LABEL: @sitofp_nearbyint( ; CHECK-NEXT: [[CVT:%.*]] = sitofp i32 [[ARG:%.*]] to float @@ -125,6 +135,7 @@ declare float @llvm.floor.f32(float) #0 declare float @llvm.trunc.f32(float) #0 declare float @llvm.ceil.f32(float) #0 declare float @llvm.round.f32(float) #0 +declare float @llvm.roundeven.f32(float) #0 declare float @llvm.nearbyint.f32(float) #0 declare float @llvm.rint.f32(float) #0 diff --git a/test/Transforms/LICM/hoist-round.ll b/test/Transforms/LICM/hoist-round.ll index 10f75be4d32..c48847b40db 100644 --- a/test/Transforms/LICM/hoist-round.ll +++ b/test/Transforms/LICM/hoist-round.ll @@ -20,6 +20,7 @@ target datalayout = "E-m:e-p:32:32-i8:8:8-i16:16:16-i64:32:32-f64:32:32-v64:32:3 ; CHECK: call float @llvm.minnum.f32 ; CHECK: call float @llvm.maxnum.f32 ; CHECK: call float @llvm.powi.f32 +; CHECK: call float @llvm.roundeven.f32 ; CHECK: for.body: define void @test(float %arg1, float %arg2) { @@ -45,7 +46,8 @@ for.body: %tmp.11 = call float @llvm.minimum.f32(float %tmp.10, float %arg2) %tmp.12 = call float @llvm.maximum.f32(float %tmp.11, float %arg2) %tmp.13 = call float @llvm.powi.f32(float %tmp.12, i32 4) - call void @consume(float %tmp.13) + %tmp.14 = call float @llvm.roundeven.f32(float %tmp.13) + call void @consume(float %tmp.14) %IND.new = add i32 %IND, 1 br label %for.head @@ -68,3 +70,4 @@ declare float @llvm.maxnum.f32(float, float) declare float @llvm.minimum.f32(float, float) declare float @llvm.maximum.f32(float, float) declare float @llvm.powi.f32(float, i32) +declare float @llvm.roundeven.f32(float) diff --git a/test/Transforms/LoopVectorize/intrinsic.ll b/test/Transforms/LoopVectorize/intrinsic.ll index 50cdb73ae8e..c2036c61133 100644 --- a/test/Transforms/LoopVectorize/intrinsic.ll +++ b/test/Transforms/LoopVectorize/intrinsic.ll @@ -832,6 +832,58 @@ for.end: ; preds = %for.body, %entry declare double @llvm.round.f64(double) nounwind readnone +;CHECK-LABEL: @roundeven_f32( +;CHECK: llvm.roundeven.v4f32 +;CHECK: ret void +define void @roundeven_f32(i32 %n, float* noalias %y, float* noalias %x) nounwind uwtable { +entry: + %cmp6 = icmp sgt i32 %n, 0 + br i1 %cmp6, 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, float* %y, i64 %indvars.iv + %0 = load float, float* %arrayidx, align 4 + %call = tail call float @llvm.roundeven.f32(float %0) nounwind readnone + %arrayidx2 = getelementptr inbounds float, float* %x, i64 %indvars.iv + store float %call, float* %arrayidx2, 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.roundeven.f32(float) nounwind readnone + +;CHECK-LABEL: @roundeven_f64( +;CHECK: llvm.roundeven.v4f64 +;CHECK: ret void +define void @roundeven_f64(i32 %n, double* noalias %y, double* noalias %x) nounwind uwtable { +entry: + %cmp6 = icmp sgt i32 %n, 0 + br i1 %cmp6, 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 double, double* %y, i64 %indvars.iv + %0 = load double, double* %arrayidx, align 8 + %call = tail call double @llvm.roundeven.f64(double %0) nounwind readnone + %arrayidx2 = getelementptr inbounds double, double* %x, i64 %indvars.iv + store double %call, double* %arrayidx2, align 8 + %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 double @llvm.roundeven.f64(double) nounwind readnone + ;CHECK-LABEL: @fma_f32( ;CHECK: llvm.fma.v4f32 ;CHECK: ret void diff --git a/unittests/Analysis/TargetLibraryInfoTest.cpp b/unittests/Analysis/TargetLibraryInfoTest.cpp index f21081467c1..bd5fefc013e 100644 --- a/unittests/Analysis/TargetLibraryInfoTest.cpp +++ b/unittests/Analysis/TargetLibraryInfoTest.cpp @@ -276,6 +276,9 @@ TEST_F(TargetLibraryInfoTest, ValidProto) { "declare double @round(double)\n" "declare float @roundf(float)\n" "declare x86_fp80 @roundl(x86_fp80)\n" + "declare double @roundeven(double)\n" + "declare float @roundevenf(float)\n" + "declare x86_fp80 @roundevenl(x86_fp80)\n" "declare i32 @scanf(i8*, ...)\n" "declare void @setbuf(%struct*, i8*)\n" "declare i32 @setitimer(i32, %struct*, %struct*)\n" diff --git a/unittests/IR/IRBuilderTest.cpp b/unittests/IR/IRBuilderTest.cpp index 5af5bc87944..bf230597a58 100644 --- a/unittests/IR/IRBuilderTest.cpp +++ b/unittests/IR/IRBuilderTest.cpp @@ -121,6 +121,12 @@ TEST_F(IRBuilderTest, Intrinsics) { EXPECT_EQ(II->getIntrinsicID(), Intrinsic::fma); EXPECT_TRUE(II->hasNoInfs()); EXPECT_FALSE(II->hasNoNaNs()); + + Call = Builder.CreateUnaryIntrinsic(Intrinsic::roundeven, V); + II = cast(Call); + EXPECT_EQ(II->getIntrinsicID(), Intrinsic::roundeven); + EXPECT_FALSE(II->hasNoInfs()); + EXPECT_FALSE(II->hasNoNaNs()); } TEST_F(IRBuilderTest, IntrinsicsWithScalableVectors) { @@ -307,6 +313,25 @@ TEST_F(IRBuilderTest, ConstrainedFP) { EXPECT_FALSE(verifyModule(*M)); } +TEST_F(IRBuilderTest, ConstrainedFPIntrinsics) { + IRBuilder<> Builder(BB); + Value *V; + Value *VDouble; + ConstrainedFPIntrinsic *CII; + GlobalVariable *GVDouble = new GlobalVariable( + *M, Type::getDoubleTy(Ctx), true, GlobalValue::ExternalLinkage, nullptr); + VDouble = Builder.CreateLoad(GVDouble->getValueType(), GVDouble); + + Builder.setDefaultConstrainedExcept(fp::ebStrict); + Builder.setDefaultConstrainedRounding(RoundingMode::TowardZero); + Function *Fn = Intrinsic::getDeclaration(M.get(), + Intrinsic::experimental_constrained_roundeven, { Type::getDoubleTy(Ctx) }); + V = Builder.CreateConstrainedFPCall(Fn, { VDouble }); + CII = cast(V); + EXPECT_EQ(Intrinsic::experimental_constrained_roundeven, CII->getIntrinsicID()); + EXPECT_EQ(fp::ebStrict, CII->getExceptionBehavior()); +} + TEST_F(IRBuilderTest, Lifetime) { IRBuilder<> Builder(BB); AllocaInst *Var1 = Builder.CreateAlloca(Builder.getInt8Ty());