From 95af9dafbe3532f9fe7f7aa319607159ebf91d9d Mon Sep 17 00:00:00 2001 From: Lioncash Date: Sun, 3 Mar 2019 01:08:12 -0500 Subject: [PATCH 1/4] common/fp/op: Add FP conversion functions --- src/CMakeLists.txt | 2 + src/common/fp/op.h | 1 + src/common/fp/op/FPConvert.cpp | 102 +++++++++++++++++++++++++++++++++ src/common/fp/op/FPConvert.h | 18 ++++++ 4 files changed, 123 insertions(+) create mode 100644 src/common/fp/op/FPConvert.cpp create mode 100644 src/common/fp/op/FPConvert.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c9ef4fcb..66c1db77 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -24,6 +24,8 @@ add_library(dynarmic common/fp/info.h common/fp/mantissa_util.h common/fp/op.h + common/fp/op/FPConvert.cpp + common/fp/op/FPConvert.h common/fp/op/FPMulAdd.cpp common/fp/op/FPMulAdd.h common/fp/op/FPRecipEstimate.cpp diff --git a/src/common/fp/op.h b/src/common/fp/op.h index c792e50e..b8ee1441 100644 --- a/src/common/fp/op.h +++ b/src/common/fp/op.h @@ -6,6 +6,7 @@ #pragma once +#include "common/fp/op/FPConvert.h" #include "common/fp/op/FPMulAdd.h" #include "common/fp/op/FPRecipEstimate.h" #include "common/fp/op/FPRecipExponent.h" diff --git a/src/common/fp/op/FPConvert.cpp b/src/common/fp/op/FPConvert.cpp new file mode 100644 index 00000000..559d0242 --- /dev/null +++ b/src/common/fp/op/FPConvert.cpp @@ -0,0 +1,102 @@ +/* This file is part of the dynarmic project. + * Copyright (c) 2019 MerryMage + * This software may be used and distributed according to the terms of the GNU + * General Public License version 2 or any later version. + */ + +#include + +#include "common/common_types.h" +#include "common/fp/fpcr.h" +#include "common/fp/fpsr.h" +#include "common/fp/info.h" +#include "common/fp/op/FPRecipEstimate.h" +#include "common/fp/process_exception.h" +#include "common/fp/process_nan.h" +#include "common/fp/unpacked.h" + +namespace Dynarmic::FP { +namespace { +// We don't care about unreachable code warnings here +// TODO: Remove this disabling of warnings when +// half-float support is added. +#ifdef _MSC_VER +#pragma warning(disable:4702) +#endif +template +FPT_TO FPConvertNaN(FPT_FROM op) { + const bool sign = Common::Bit() - 1>(op); + const u64 frac = [op] { + if constexpr (sizeof(FPT_FROM) == sizeof(u64)) { + return Common::Bits<0, 50>(op); + } + + if constexpr (sizeof(FPT_FROM) == sizeof(u32)) { + return u64{Common::Bits<0, 21>(op)} << 29; + } + + return u64{Common::Bits<0, 8>(op)} << 42; + }(); + + const size_t dest_bit_size = Common::BitSize(); + const u64 shifted_sign = u64{sign} << (dest_bit_size - 1); + const u64 exponent = Common::Ones(dest_bit_size - FPInfo::explicit_mantissa_width); + + if constexpr (sizeof(FPT_TO) == sizeof(u64)) { + return FPT_TO(shifted_sign | exponent << 52 | frac); + } + + if constexpr (sizeof(FPT_TO) == sizeof(u32)) { + return FPT_TO(shifted_sign | exponent << 22 | Common::Bits<29, 50>(frac)); + } + + return FPT_TO(shifted_sign | exponent << 9 | Common::Bits<42, 50>(frac)); +} +#ifdef _MSC_VER +#pragma warning(default:4702) +#endif +} // Anonymous namespace + +template +FPT_TO FPConvert(FPT_FROM op, FPCR fpcr, RoundingMode rounding_mode, FPSR& fpsr) { + const auto [type, sign, value] = FPUnpack(op, fpcr, fpsr); + const bool is_althp = Common::BitSize() == 16 && fpcr.AHP(); + + if (type == FPType::SNaN || type == FPType::QNaN) { + FPT_TO result{}; + + if (is_althp) { + result = FPInfo::Zero(sign); + } else if (fpcr.DN()) { + result = FPInfo::DefaultNaN(); + } else { + result = FPConvertNaN(op); + } + + if (type == FPType::SNaN || is_althp) { + FPProcessException(FPExc::InvalidOp, fpcr, fpsr); + } + + return result; + } + + if (type == FPType::Infinity) { + if (is_althp) { + FPProcessException(FPExc::InvalidOp, fpcr, fpsr); + return static_cast(u32{sign} << 15 | 0b111111111111111); + } + + return FPInfo::Infinity(sign); + } + + if (type == FPType::Zero) { + return FPInfo::Zero(sign); + } + + return FPRoundBase(value, fpcr, rounding_mode, fpsr); +} + +template u64 FPConvert(u32 op, FPCR fpcr, RoundingMode rounding_mode, FPSR& fpsr); +template u32 FPConvert(u64 op, FPCR fpcr, RoundingMode rounding_mode, FPSR& fpsr); + +} // namespace Dynarmic::FP diff --git a/src/common/fp/op/FPConvert.h b/src/common/fp/op/FPConvert.h new file mode 100644 index 00000000..419a158b --- /dev/null +++ b/src/common/fp/op/FPConvert.h @@ -0,0 +1,18 @@ +/* This file is part of the dynarmic project. + * Copyright (c) 2019 MerryMage + * This software may be used and distributed according to the terms of the GNU + * General Public License version 2 or any later version. + */ + +#pragma once + +namespace Dynarmic::FP { + +class FPCR; +class FPSR; +enum class RoundingMode; + +template +FPT_TO FPConvert(FPT_FROM op, FPCR fpcr, RoundingMode rounding_mode, FPSR& fpsr); + +} // namespace Dynarmic::FP From 7c81a58ed3031d44b66594db03144242b36ec213 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Mon, 4 Mar 2019 10:44:17 -0500 Subject: [PATCH 2/4] frontend/ir/ir_emitter: Alter parameters of FPDoubleToSingle() and FPSingleToDouble() to pass along desired rounding mode This will be necessary to special-case the non-IEEE Von Neumann rounding to odd rounding mode. --- src/backend/x64/emit_x64_floating_point.cpp | 44 ++++++++++++++----- .../A32/translate/translate_arm/vfp2.cpp | 5 ++- ...ing_point_data_processing_one_register.cpp | 8 ++-- .../translate/impl/simd_two_register_misc.cpp | 6 ++- src/frontend/ir/ir_emitter.cpp | 10 ++--- src/frontend/ir/ir_emitter.h | 4 +- src/frontend/ir/opcodes.inc | 4 +- 7 files changed, 52 insertions(+), 29 deletions(-) diff --git a/src/backend/x64/emit_x64_floating_point.cpp b/src/backend/x64/emit_x64_floating_point.cpp index c499e20f..88ceb413 100644 --- a/src/backend/x64/emit_x64_floating_point.cpp +++ b/src/backend/x64/emit_x64_floating_point.cpp @@ -1034,26 +1034,46 @@ void EmitX64::EmitFPCompare64(EmitContext& ctx, IR::Inst* inst) { void EmitX64::EmitFPSingleToDouble(EmitContext& ctx, IR::Inst* inst) { auto args = ctx.reg_alloc.GetArgumentInfo(inst); - const Xbyak::Xmm result = ctx.reg_alloc.UseScratchXmm(args[0]); + const auto rounding_mode = static_cast(args[1].GetImmediateU8()); - code.cvtss2sd(result, result); - if (ctx.FPSCR_DN()) { - ForceToDefaultNaN<64>(code, result); + // We special-case the non-IEEE-defined ToOdd rounding mode. + if (rounding_mode == ctx.FPSCR_RMode() && rounding_mode != FP::RoundingMode::ToOdd) { + const Xbyak::Xmm result = ctx.reg_alloc.UseScratchXmm(args[0]); + + code.cvtss2sd(result, result); + if (ctx.FPSCR_DN()) { + ForceToDefaultNaN<64>(code, result); + } + ctx.reg_alloc.DefineValue(inst, result); + } else { + ctx.reg_alloc.HostCall(inst, args[0]); + code.mov(code.ABI_PARAM2.cvt32(), ctx.FPCR()); + code.mov(code.ABI_PARAM3.cvt32(), static_cast(rounding_mode)); + code.lea(code.ABI_PARAM4, code.ptr[code.r15 + code.GetJitStateInfo().offsetof_fpsr_exc]); + code.CallFunction(&FP::FPConvert); } - - ctx.reg_alloc.DefineValue(inst, result); } void EmitX64::EmitFPDoubleToSingle(EmitContext& ctx, IR::Inst* inst) { auto args = ctx.reg_alloc.GetArgumentInfo(inst); - const Xbyak::Xmm result = ctx.reg_alloc.UseScratchXmm(args[0]); + const auto rounding_mode = static_cast(args[1].GetImmediateU8()); - code.cvtsd2ss(result, result); - if (ctx.FPSCR_DN()) { - ForceToDefaultNaN<32>(code, result); + // We special-case the non-IEEE-defined ToOdd rounding mode. + if (rounding_mode == ctx.FPSCR_RMode() && rounding_mode != FP::RoundingMode::ToOdd) { + const Xbyak::Xmm result = ctx.reg_alloc.UseScratchXmm(args[0]); + + code.cvtsd2ss(result, result); + if (ctx.FPSCR_DN()) { + ForceToDefaultNaN<32>(code, result); + } + ctx.reg_alloc.DefineValue(inst, result); + } else { + ctx.reg_alloc.HostCall(inst, args[0]); + code.mov(code.ABI_PARAM2.cvt32(), ctx.FPCR()); + code.mov(code.ABI_PARAM3.cvt32(), static_cast(rounding_mode)); + code.lea(code.ABI_PARAM4, code.ptr[code.r15 + code.GetJitStateInfo().offsetof_fpsr_exc]); + code.CallFunction(&FP::FPConvert); } - - ctx.reg_alloc.DefineValue(inst, result); } template diff --git a/src/frontend/A32/translate/translate_arm/vfp2.cpp b/src/frontend/A32/translate/translate_arm/vfp2.cpp index eb2caa92..638d52ba 100644 --- a/src/frontend/A32/translate/translate_arm/vfp2.cpp +++ b/src/frontend/A32/translate/translate_arm/vfp2.cpp @@ -479,12 +479,13 @@ bool ArmTranslatorVisitor::vfp2_VCVT_f_to_f(Cond cond, bool D, size_t Vd, bool s const auto d = ToExtReg(!sz, Vd, D); // Destination is of opposite size to source const auto m = ToExtReg(sz, Vm, M); const auto reg_m = ir.GetExtendedRegister(m); + const auto rounding_mode = ir.current_location.FPSCR().RMode(); if (sz) { - const auto result = ir.FPDoubleToSingle(reg_m, true); + const auto result = ir.FPDoubleToSingle(reg_m, rounding_mode); ir.SetExtendedRegister(d, result); } else { - const auto result = ir.FPSingleToDouble(reg_m, true); + const auto result = ir.FPSingleToDouble(reg_m, rounding_mode); ir.SetExtendedRegister(d, result); } diff --git a/src/frontend/A64/translate/impl/floating_point_data_processing_one_register.cpp b/src/frontend/A64/translate/impl/floating_point_data_processing_one_register.cpp index a8ac0d6b..342fd4c4 100644 --- a/src/frontend/A64/translate/impl/floating_point_data_processing_one_register.cpp +++ b/src/frontend/A64/translate/impl/floating_point_data_processing_one_register.cpp @@ -104,7 +104,9 @@ bool TranslatorVisitor::FCVT_float(Imm<2> type, Imm<2> opc, Vec Vn, Vec Vd) { return UnallocatedEncoding(); } - IR::UAny operand = V_scalar(*srcsize, Vn); + const IR::UAny operand = V_scalar(*srcsize, Vn); + const auto rounding_mode = ir.current_location->FPCR().RMode(); + IR::UAny result; switch (*srcsize) { case 16: @@ -120,7 +122,7 @@ bool TranslatorVisitor::FCVT_float(Imm<2> type, Imm<2> opc, Vec Vn, Vec Vd) { case 16: return InterpretThisInstruction(); case 64: - result = ir.FPSingleToDouble(operand, true); + result = ir.FPSingleToDouble(operand, rounding_mode); break; } break; @@ -129,7 +131,7 @@ bool TranslatorVisitor::FCVT_float(Imm<2> type, Imm<2> opc, Vec Vn, Vec Vd) { case 16: return InterpretThisInstruction(); case 32: - result = ir.FPDoubleToSingle(operand, true); + result = ir.FPDoubleToSingle(operand, rounding_mode); break; } break; diff --git a/src/frontend/A64/translate/impl/simd_two_register_misc.cpp b/src/frontend/A64/translate/impl/simd_two_register_misc.cpp index d1b5ab50..2dba56e5 100644 --- a/src/frontend/A64/translate/impl/simd_two_register_misc.cpp +++ b/src/frontend/A64/translate/impl/simd_two_register_misc.cpp @@ -348,10 +348,11 @@ bool TranslatorVisitor::FCVTL(bool Q, bool sz, Vec Vn, Vec Vd) { } const IR::U128 part = Vpart(64, Vn, Q); + const auto rounding_mode = ir.current_location->FPCR().RMode(); IR::U128 result = ir.ZeroVector(); for (size_t i = 0; i < 2; i++) { - const IR::U64 element = ir.FPSingleToDouble(ir.VectorGetElement(32, part, i), true); + const IR::U64 element = ir.FPSingleToDouble(ir.VectorGetElement(32, part, i), rounding_mode); result = ir.VectorSetElement(64, result, i, element); } @@ -367,10 +368,11 @@ bool TranslatorVisitor::FCVTN(bool Q, bool sz, Vec Vn, Vec Vd) { } const IR::U128 operand = V(128, Vn); + const auto rounding_mode = ir.current_location->FPCR().RMode(); IR::U128 result = ir.ZeroVector(); for (size_t i = 0; i < 2; i++) { - const IR::U32 element = ir.FPDoubleToSingle(ir.VectorGetElement(64, operand, i), true); + const IR::U32 element = ir.FPDoubleToSingle(ir.VectorGetElement(64, operand, i), rounding_mode); result = ir.VectorSetElement(32, result, i, element); } diff --git a/src/frontend/ir/ir_emitter.cpp b/src/frontend/ir/ir_emitter.cpp index 6452e7f7..ccc7420a 100644 --- a/src/frontend/ir/ir_emitter.cpp +++ b/src/frontend/ir/ir_emitter.cpp @@ -1948,14 +1948,12 @@ U32U64 IREmitter::FPSub(const U32U64& a, const U32U64& b, bool fpcr_controlled) } } -U32 IREmitter::FPDoubleToSingle(const U64& a, bool fpcr_controlled) { - ASSERT(fpcr_controlled); - return Inst(Opcode::FPDoubleToSingle, a); +U32 IREmitter::FPDoubleToSingle(const U64& a, FP::RoundingMode rounding) { + return Inst(Opcode::FPDoubleToSingle, a, Imm8(static_cast(rounding))); } -U64 IREmitter::FPSingleToDouble(const U32& a, bool fpcr_controlled) { - ASSERT(fpcr_controlled); - return Inst(Opcode::FPSingleToDouble, a); +U64 IREmitter::FPSingleToDouble(const U32& a, FP::RoundingMode rounding) { + return Inst(Opcode::FPSingleToDouble, a, Imm8(static_cast(rounding))); } U32 IREmitter::FPToFixedS32(const U32U64& a, size_t fbits, FP::RoundingMode rounding) { diff --git a/src/frontend/ir/ir_emitter.h b/src/frontend/ir/ir_emitter.h index f58c6efa..bff31c94 100644 --- a/src/frontend/ir/ir_emitter.h +++ b/src/frontend/ir/ir_emitter.h @@ -312,8 +312,8 @@ public: U32U64 FPRSqrtStepFused(const U32U64& a, const U32U64& b); U32U64 FPSqrt(const U32U64& a); U32U64 FPSub(const U32U64& a, const U32U64& b, bool fpcr_controlled); - U32 FPDoubleToSingle(const U64& a, bool fpcr_controlled); - U64 FPSingleToDouble(const U32& a, bool fpcr_controlled); + U32 FPDoubleToSingle(const U64& a, FP::RoundingMode rounding); + U64 FPSingleToDouble(const U32& a, FP::RoundingMode rounding); U32 FPToFixedS32(const U32U64& a, size_t fbits, FP::RoundingMode rounding); U64 FPToFixedS64(const U32U64& a, size_t fbits, FP::RoundingMode rounding); U32 FPToFixedU32(const U32U64& a, size_t fbits, FP::RoundingMode rounding); diff --git a/src/frontend/ir/opcodes.inc b/src/frontend/ir/opcodes.inc index 7306e3d0..2797496f 100644 --- a/src/frontend/ir/opcodes.inc +++ b/src/frontend/ir/opcodes.inc @@ -500,8 +500,8 @@ OPCODE(FPSub32, U32, U32, OPCODE(FPSub64, U64, U64, U64 ) // Floating-point conversions -OPCODE(FPSingleToDouble, U64, U32 ) -OPCODE(FPDoubleToSingle, U32, U64 ) +OPCODE(FPSingleToDouble, U64, U32, U8 ) +OPCODE(FPDoubleToSingle, U32, U64, U8 ) OPCODE(FPDoubleToFixedS32, U32, U64, U8, U8 ) OPCODE(FPDoubleToFixedS64, U64, U64, U8, U8 ) OPCODE(FPDoubleToFixedU32, U32, U64, U8, U8 ) From 4339a8fff6db497b120706a80ed3131b63718a23 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Mon, 4 Mar 2019 10:58:19 -0500 Subject: [PATCH 3/4] A64: Implement the scalar version of FCVTXN --- src/frontend/A64/decoder/a64.inc | 2 +- .../translate/impl/simd_scalar_two_register_misc.cpp | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/frontend/A64/decoder/a64.inc b/src/frontend/A64/decoder/a64.inc index 9186a024..199cebfe 100644 --- a/src/frontend/A64/decoder/a64.inc +++ b/src/frontend/A64/decoder/a64.inc @@ -464,7 +464,7 @@ INST(CMLE_1, "CMLE (zero)", "01111 INST(NEG_1, "NEG (vector)", "01111110zz100000101110nnnnnddddd") INST(SQXTUN_1, "SQXTUN, SQXTUN2", "01111110zz100001001010nnnnnddddd") INST(UQXTN_1, "UQXTN, UQXTN2", "01111110zz100001010010nnnnnddddd") -//INST(FCVTXN_1, "FCVTXN, FCVTXN2", "011111100z100001011010nnnnnddddd") +INST(FCVTXN_1, "FCVTXN, FCVTXN2", "011111100z100001011010nnnnnddddd") // Data Processing - FP and SIMD - SIMD Scalar pairwise INST(ADDP_pair, "ADDP (scalar)", "01011110zz110001101110nnnnnddddd") diff --git a/src/frontend/A64/translate/impl/simd_scalar_two_register_misc.cpp b/src/frontend/A64/translate/impl/simd_scalar_two_register_misc.cpp index b8283d7c..7dcc89d3 100644 --- a/src/frontend/A64/translate/impl/simd_scalar_two_register_misc.cpp +++ b/src/frontend/A64/translate/impl/simd_scalar_two_register_misc.cpp @@ -152,6 +152,18 @@ bool TranslatorVisitor::FCVTPU_2(bool sz, Vec Vn, Vec Vd) { return ScalarFPConvertWithRound(*this, sz, Vn, Vd, FP::RoundingMode::TowardsPlusInfinity, Signedness::Unsigned); } +bool TranslatorVisitor::FCVTXN_1(bool sz, Vec Vn, Vec Vd) { + if (!sz) { + return UnallocatedEncoding(); + } + + const IR::U64 element = V_scalar(64, Vn); + const IR::U32 result = ir.FPDoubleToSingle(element, FP::RoundingMode::ToOdd); + + V_scalar(32, Vd, result); + return true; +} + bool TranslatorVisitor::FCVTZS_int_2(bool sz, Vec Vn, Vec Vd) { return ScalarFPConvertWithRound(*this, sz, Vn, Vd, FP::RoundingMode::TowardsZero, Signedness::Signed); } From d3515279dfa7f9240703b38154a8c22207b0eea6 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Mon, 4 Mar 2019 11:25:19 -0500 Subject: [PATCH 4/4] A64: Implement the vector version of FCVTXN --- src/frontend/A64/decoder/a64.inc | 2 +- src/frontend/A64/translate/impl/impl.h | 2 +- .../translate/impl/simd_two_register_misc.cpp | 20 +++++++++++++++++++ 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/frontend/A64/decoder/a64.inc b/src/frontend/A64/decoder/a64.inc index 199cebfe..1df2971c 100644 --- a/src/frontend/A64/decoder/a64.inc +++ b/src/frontend/A64/decoder/a64.inc @@ -666,7 +666,7 @@ INST(NEG_2, "NEG (vector)", "0Q101 INST(SQXTUN_2, "SQXTUN, SQXTUN2", "0Q101110zz100001001010nnnnnddddd") INST(SHLL, "SHLL, SHLL2", "0Q101110zz100001001110nnnnnddddd") INST(UQXTN_2, "UQXTN, UQXTN2", "0Q101110zz100001010010nnnnnddddd") -//INST(FCVTXN_2, "FCVTXN, FCVTXN2", "0Q1011100z100001011010nnnnnddddd") +INST(FCVTXN_2, "FCVTXN, FCVTXN2", "0Q1011100z100001011010nnnnnddddd") //INST(FRINTA_1, "FRINTA (vector)", "0Q10111001111001100010nnnnnddddd") INST(FRINTA_2, "FRINTA (vector)", "0Q1011100z100001100010nnnnnddddd") //INST(FRINTX_1, "FRINTX (vector)", "0Q10111001111001100110nnnnnddddd") diff --git a/src/frontend/A64/translate/impl/impl.h b/src/frontend/A64/translate/impl/impl.h index d65318ed..c117b885 100644 --- a/src/frontend/A64/translate/impl/impl.h +++ b/src/frontend/A64/translate/impl/impl.h @@ -748,7 +748,7 @@ struct TranslatorVisitor final { bool NEG_2(bool Q, Imm<2> size, Vec Vn, Vec Vd); bool SQXTUN_2(bool Q, Imm<2> size, Vec Vn, Vec Vd); bool UQXTN_2(bool Q, Imm<2> size, Vec Vn, Vec Vd); - bool FCVTXN_2(bool Q, bool sz, Vec Vn, Reg Rd); + bool FCVTXN_2(bool Q, bool sz, Vec Vn, Vec Vd); bool FRINTN_1(bool Q, Vec Vn, Vec Vd); bool FRINTN_2(bool Q, bool sz, Vec Vn, Vec Vd); bool FRINTM_1(bool Q, Vec Vn, Vec Vd); diff --git a/src/frontend/A64/translate/impl/simd_two_register_misc.cpp b/src/frontend/A64/translate/impl/simd_two_register_misc.cpp index 2dba56e5..c734d662 100644 --- a/src/frontend/A64/translate/impl/simd_two_register_misc.cpp +++ b/src/frontend/A64/translate/impl/simd_two_register_misc.cpp @@ -397,6 +397,26 @@ bool TranslatorVisitor::FCVTPS_4(bool Q, bool sz, Vec Vn, Vec Vd) { return FloatConvertToInteger(*this, Q, sz, Vn, Vd, Signedness::Signed, FP::RoundingMode::TowardsPlusInfinity); } +bool TranslatorVisitor::FCVTXN_2(bool Q, bool sz, Vec Vn, Vec Vd) { + if (!sz) { + return UnallocatedEncoding(); + } + + const size_t part = Q ? 1 : 0; + const auto operand = ir.GetQ(Vn); + auto result = ir.ZeroVector(); + + for (size_t e = 0; e < 2; ++e) { + const IR::U64 element = ir.VectorGetElement(64, operand, e); + const IR::U32 converted = ir.FPDoubleToSingle(element, FP::RoundingMode::ToOdd); + + result = ir.VectorSetElement(32, result, e, converted); + } + + Vpart(64, Vd, part, result); + return true; +} + bool TranslatorVisitor::FCVTZS_int_4(bool Q, bool sz, Vec Vn, Vec Vd) { return FloatConvertToInteger(*this, Q, sz, Vn, Vd, Signedness::Signed, FP::RoundingMode::TowardsZero); }