Index: llvm/docs/LangRef.rst =================================================================== --- llvm/docs/LangRef.rst +++ llvm/docs/LangRef.rst @@ -12344,6 +12344,83 @@ This function returns the same values as the libm ``round`` functions would, and handles error conditions in the same way. +'``llvm.lround.*``' Intrinsic +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Syntax: +""""""" + +This is an overloaded intrinsic. You can use ``llvm.lround`` on any +floating-point or vector of floating-point type. Not all targets support +all types however. + +:: + + declare i32 @llvm.lround.i32.f32(float %Val) + declare i32 @llvm.lround.i32.f64(double %Val) + declare i32 @llvm.lround.i32.f80(float %Val) + declare i32 @llvm.lround.i32.f128(double %Val) + declare i32 @llvm.lround.i32.ppcf128(double %Val) + + declare i64 @llvm.lround.i64.f32(float %Val) + declare i64 @llvm.lround.i64.f64(double %Val) + declare i64 @llvm.lround.i64.f80(float %Val) + declare i64 @llvm.lround.i64.f128(double %Val) + declare i64 @llvm.lround.i64.ppcf128(double %Val) + +Overview: +""""""""" + +The '``llvm.lround.*``' intrinsics returns the operand rounded to the +nearest integer. + +Arguments: +"""""""""" + +The argument is a floating-point number and return is i32 for +``llvm.lround.i32`` and i64 for ``llvm.lround.i64``. + +Semantics: +"""""""""" + +This function returns the same values as the libm ``lround`` +functions would, but without setting errno. + +'``llvm.llround.*``' Intrinsic +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Syntax: +""""""" + +This is an overloaded intrinsic. You can use ``llvm.llround`` on any +floating-point or vector of floating-point type. Not all targets support +all types however. + +:: + + declare i64 @llvm.lround.f32(float %Val) + declare i64 @llvm.lround.f64(double %Val) + declare i64 @llvm.lround.f80(float %Val) + declare i64 @llvm.lround.f128(double %Val) + declare i64 @llvm.lround.ppcf128(double %Val) + +Overview: +""""""""" + +The '``llvm.llround.*``' intrinsics returns the operand rounded to the +nearest integer. + +Arguments: +"""""""""" + +The argument is a floating-point number and return is i64. + +Semantics: +"""""""""" + +This function returns the same values as the libm ``llround`` +functions would, but without setting errno. + Bit Manipulation Intrinsics --------------------------- Index: llvm/include/llvm/CodeGen/ISDOpcodes.h =================================================================== --- llvm/include/llvm/CodeGen/ISDOpcodes.h +++ llvm/include/llvm/CodeGen/ISDOpcodes.h @@ -585,6 +585,8 @@ FNEG, FABS, FSQRT, FCBRT, FSIN, FCOS, FPOWI, FPOW, FLOG, FLOG2, FLOG10, FEXP, FEXP2, FCEIL, FTRUNC, FRINT, FNEARBYINT, FROUND, FFLOOR, + LROUND, LLROUND, + /// FMINNUM/FMAXNUM - Perform floating-point minimum or maximum on two /// values. // Index: llvm/include/llvm/IR/Intrinsics.td =================================================================== --- llvm/include/llvm/IR/Intrinsics.td +++ llvm/include/llvm/IR/Intrinsics.td @@ -538,6 +538,10 @@ def int_round : Intrinsic<[llvm_anyfloat_ty], [LLVMMatchType<0>]>; def int_canonicalize : Intrinsic<[llvm_anyfloat_ty], [LLVMMatchType<0>], [IntrNoMem]>; + + def int_lround_i32 : Intrinsic<[llvm_i32_ty], [llvm_anyfloat_ty]>; + def int_lround_i64 : Intrinsic<[llvm_i64_ty], [llvm_anyfloat_ty]>; + def int_llround : Intrinsic<[llvm_i64_ty], [llvm_anyfloat_ty]>; } def int_minnum : Intrinsic<[llvm_anyfloat_ty], Index: llvm/include/llvm/IR/RuntimeLibcalls.def =================================================================== --- llvm/include/llvm/IR/RuntimeLibcalls.def +++ llvm/include/llvm/IR/RuntimeLibcalls.def @@ -254,6 +254,16 @@ HANDLE_LIBCALL(FMAX_F80, "fmaxl") HANDLE_LIBCALL(FMAX_F128, "fmaxl") HANDLE_LIBCALL(FMAX_PPCF128, "fmaxl") +HANDLE_LIBCALL(LROUND_F32, "lroundf") +HANDLE_LIBCALL(LROUND_F64, "lround") +HANDLE_LIBCALL(LROUND_F80, "lroundl") +HANDLE_LIBCALL(LROUND_F128, "lroundl") +HANDLE_LIBCALL(LROUND_PPCF128, "lroundl") +HANDLE_LIBCALL(LLROUND_F32, "llroundf") +HANDLE_LIBCALL(LLROUND_F64, "llround") +HANDLE_LIBCALL(LLROUND_F80, "llroundl") +HANDLE_LIBCALL(LLROUND_F128, "llroundl") +HANDLE_LIBCALL(LLROUND_PPCF128, "llroundl") // Conversion HANDLE_LIBCALL(FPEXT_F32_PPCF128, "__gcc_stoq") Index: llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp =================================================================== --- llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp +++ llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp @@ -149,6 +149,10 @@ RTLIB::Libcall Call_I32, RTLIB::Libcall Call_I64, RTLIB::Libcall Call_I128); + SDValue ExpandArgFPLibCall(SDNode *Node, bool isSigned, + RTLIB::Libcall Call_F32, RTLIB::Libcall Call_F64, + RTLIB::Libcall Call_F80, RTLIB::Libcall Call_F128, + RTLIB::Libcall Call_PPCF128); void ExpandDivRemLibCall(SDNode *Node, SmallVectorImpl &Results); void ExpandSinCosLibCall(SDNode *Node, SmallVectorImpl &Results); @@ -2139,6 +2143,27 @@ return ExpandLibCall(LC, Node, isSigned); } +/// Expand the node to a libcall based on first argument type (for instance +/// lround and its variant). +SDValue SelectionDAGLegalize::ExpandArgFPLibCall(SDNode* Node, bool isSigned, + RTLIB::Libcall Call_F32, + RTLIB::Libcall Call_F64, + RTLIB::Libcall Call_F80, + RTLIB::Libcall Call_F128, + RTLIB::Libcall Call_PPCF128) { + RTLIB::Libcall LC; + switch (Node->getOperand(0).getValueType().getSimpleVT().SimpleTy) { + default: llvm_unreachable("Unexpected request for libcall!"); + case MVT::f32: LC = Call_F32; break; + case MVT::f64: LC = Call_F64; break; + case MVT::f80: LC = Call_F80; break; + case MVT::f128: LC = Call_F128; break; + case MVT::ppcf128: LC = Call_PPCF128; break; + } + + return ExpandLibCall(LC, Node, isSigned); +} + /// Issue libcalls to __{u}divmod to compute div / rem pairs. void SelectionDAGLegalize::ExpandDivRemLibCall(SDNode *Node, @@ -2849,6 +2874,18 @@ if (TLI.expandFP_TO_UINT(Node, Tmp1, DAG)) Results.push_back(Tmp1); break; + case ISD::LROUND: + Results.push_back(ExpandArgFPLibCall(Node, true, RTLIB::LROUND_F32, + RTLIB::LROUND_F64, RTLIB::LROUND_F80, + RTLIB::LROUND_F128, + RTLIB::LROUND_PPCF128)); + break; + case ISD::LLROUND: + Results.push_back(ExpandArgFPLibCall(Node, true, RTLIB::LLROUND_F32, + RTLIB::LLROUND_F64, RTLIB::LLROUND_F80, + RTLIB::LLROUND_F128, + RTLIB::LLROUND_PPCF128)); + break; case ISD::VAARG: Results.push_back(DAG.expandVAArg(Node)); Results.push_back(Results[0].getValue(1)); Index: llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp =================================================================== --- llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -6007,6 +6007,9 @@ case Intrinsic::nearbyint: Opcode = ISD::FNEARBYINT; break; case Intrinsic::round: Opcode = ISD::FROUND; break; case Intrinsic::canonicalize: Opcode = ISD::FCANONICALIZE; break; + case Intrinsic::lround_i32: Opcode = ISD::LROUND; break; + case Intrinsic::lround_i64: Opcode = ISD::LROUND; break; + case Intrinsic::llround: Opcode = ISD::LLROUND; break; } setValue(&I, DAG.getNode(Opcode, sdl, @@ -6014,6 +6017,22 @@ getValue(I.getArgOperand(0)))); return nullptr; } + case Intrinsic::lround_i32: + case Intrinsic::lround_i64: + case Intrinsic::llround: { + unsigned Opcode; + MVT RetVT; + switch (Intrinsic) { + default: llvm_unreachable("Impossible intrinsic"); // Can't reach here. + case Intrinsic::lround_i32: Opcode = ISD::LROUND; RetVT = MVT::i32; break; + case Intrinsic::lround_i64: Opcode = ISD::LROUND; RetVT = MVT::i64; break; + case Intrinsic::llround: Opcode = ISD::LLROUND; RetVT = MVT::i64; break; + } + + setValue(&I, DAG.getNode(Opcode, sdl, RetVT, + getValue(I.getArgOperand(0)))); + return nullptr; + } case Intrinsic::minnum: { auto VT = getValue(I.getArgOperand(0)).getValueType(); unsigned Opc = Index: llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp =================================================================== --- llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp +++ llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp @@ -325,6 +325,8 @@ case ISD::ADDRSPACECAST: return "addrspacecast"; case ISD::FP16_TO_FP: return "fp16_to_fp"; case ISD::FP_TO_FP16: return "fp_to_fp16"; + case ISD::LROUND: return "lround"; + case ISD::LLROUND: return "llround"; // Control flow instructions case ISD::BR: return "br"; Index: llvm/lib/CodeGen/TargetLoweringBase.cpp =================================================================== --- llvm/lib/CodeGen/TargetLoweringBase.cpp +++ llvm/lib/CodeGen/TargetLoweringBase.cpp @@ -713,6 +713,11 @@ setOperationAction(ISD::FROUND, VT, Expand); } + // More library functions default to expand. + setOperationAction(ISD::LROUND, MVT::i32, Expand); + setOperationAction(ISD::LROUND, MVT::i64, Expand); + setOperationAction(ISD::LLROUND, MVT::i64, Expand); + // Default ISD::TRAP to expand (which turns it into abort). setOperationAction(ISD::TRAP, MVT::Other, Expand); Index: llvm/test/CodeGen/AArch64/llround-conv.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/AArch64/llround-conv.ll @@ -0,0 +1,72 @@ +; RUN: llc < %s -mtriple=aarch64 -mattr=+neon | FileCheck %s + +; CHECK-LABEL: testmsws: +; CHECK: bl llroundf +define i32 @testmsws(float %x) { +entry: + %call = tail call i64 @llroundf(float %x) #2 + %conv = trunc i64 %call to i32 + ret i32 %conv +} + +; CHECK-LABEL: testmsxs: +; CHECK: b llroundf +define i64 @testmsxs(float %x) { +entry: + %call = tail call i64 @llroundf(float %x) #2 + ret i64 %call +} + +; CHECK-LABEL: testmswd: +; CHECK: bl llround +define i32 @testmswd(double %x) { +entry: + %call = tail call i64 @llround(double %x) + %conv = trunc i64 %call to i32 + ret i32 %conv +} + +; CHECK-LABEL: testmsxd: +; CHECK: b llround +define i64 @testmsxd(double %x) { +entry: + %call = tail call i64 @llround(double %x) + ret i64 %call +} + +; CHECK-LABEL: testmuws: +; CHECK: bl llroundf +define i32 @testmuws(float %x) { +entry: + %call = tail call i64 @llroundf(float %x) + %conv = trunc i64 %call to i32 + ret i32 %conv +} + +; CHECK-LABEL: testmuxs: +; CHECK: b llroundf +define i64 @testmuxs(float %x) { +entry: + %call = tail call i64 @llroundf(float %x) + ret i64 %call +} + +; CHECK-LABEL: testmuwd: +; CHECK: bl llround +define i32 @testmuwd(double %x) { +entry: + %call = tail call i64 @llround(double %x) + %conv = trunc i64 %call to i32 + ret i32 %conv +} + +; CHECK-LABEL: testmuxd: +; CHECK: b llround +define i64 @testmuxd(double %x) { +entry: + %call = tail call i64 @llround(double %x) + ret i64 %call +} + +declare i64 @llroundf(float) nounwind +declare i64 @llround(double) nounwind Index: llvm/test/CodeGen/AArch64/lround-conv.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/AArch64/lround-conv.ll @@ -0,0 +1,72 @@ +; RUN: llc < %s -mtriple=aarch64 -mattr=+neon | FileCheck %s + +; CHECK-LABEL: testmsws: +; CHECK: bl lroundf +define i32 @testmsws(float %x) { +entry: + %call = tail call i64 @lroundf(float %x) #2 + %conv = trunc i64 %call to i32 + ret i32 %conv +} + +; CHECK-LABEL: testmsxs: +; CHECK: b lroundf +define i64 @testmsxs(float %x) { +entry: + %call = tail call i64 @lroundf(float %x) #2 + ret i64 %call +} + +; CHECK-LABEL: testmswd: +; CHECK: bl lround +define i32 @testmswd(double %x) { +entry: + %call = tail call i64 @lround(double %x) + %conv = trunc i64 %call to i32 + ret i32 %conv +} + +; CHECK-LABEL: testmsxd: +; CHECK: b lround +define i64 @testmsxd(double %x) { +entry: + %call = tail call i64 @lround(double %x) + ret i64 %call +} + +; CHECK-LABEL: testmuws: +; CHECK: bl lroundf +define i32 @testmuws(float %x) { +entry: + %call = tail call i64 @lroundf(float %x) + %conv = trunc i64 %call to i32 + ret i32 %conv +} + +; CHECK-LABEL: testmuxs: +; CHECK: b lroundf +define i64 @testmuxs(float %x) { +entry: + %call = tail call i64 @lroundf(float %x) + ret i64 %call +} + +; CHECK-LABEL: testmuwd: +; CHECK: bl lround +define i32 @testmuwd(double %x) { +entry: + %call = tail call i64 @lround(double %x) + %conv = trunc i64 %call to i32 + ret i32 %conv +} + +; CHECK-LABEL: testmuxd: +; CHECK: b lround +define i64 @testmuxd(double %x) { +entry: + %call = tail call i64 @lround(double %x) + ret i64 %call +} + +declare i64 @lroundf(float) nounwind +declare i64 @lround(double) nounwind