diff --git a/src/coreclr/jit/hwintrinsiccodegenarm64.cpp b/src/coreclr/jit/hwintrinsiccodegenarm64.cpp index 624c59347b475b..9e90f6e182e7f5 100644 --- a/src/coreclr/jit/hwintrinsiccodegenarm64.cpp +++ b/src/coreclr/jit/hwintrinsiccodegenarm64.cpp @@ -722,6 +722,11 @@ void CodeGen::genHWIntrinsic(GenTreeHWIntrinsic* node) assert(intrin.op3->IsVectorZero()); break; + case NI_Sve2_ConvertToSingleOdd: + case NI_Sve2_ConvertToSingleOddRoundToOdd: + embOpt = INS_OPTS_D_TO_S; + break; + default: break; } @@ -798,6 +803,16 @@ void CodeGen::genHWIntrinsic(GenTreeHWIntrinsic* node) falseReg, opt); break; + case NI_Sve2_ConvertToSingleOdd: + case NI_Sve2_ConvertToSingleOddRoundToOdd: + // TODO-SVE: Optimise away the explicit copying of `embMaskOp1Reg` to `targetReg`. + // For these intrinsics we cannot use movprfx instruction to populate `targetReg` with + // `embMaskOp1Reg`. Thus, we need to perform move before the operation. + GetEmitter()->emitIns_Mov(INS_sve_mov, emitSize, targetReg, embMaskOp1Reg, + /* canSkip */ true, INS_OPTS_SCALABLE_S); + emitInsHelper(targetReg, maskReg, embMaskOp2Reg); + break; + default: assert(targetReg != embMaskOp2Reg); @@ -825,12 +840,26 @@ void CodeGen::genHWIntrinsic(GenTreeHWIntrinsic* node) break; } } + // If `targetReg` and `falseReg` are not same, then we need to move it to `targetReg` first + // so the `insEmbMask` operation can be merged on top of it. else if (targetReg != falseReg) { - // If `targetReg` and `falseReg` are not same, then we need to move it to `targetReg` first - // so the `insEmbMask` operation can be merged on top of it. - if (falseReg != embMaskOp1Reg) + if ((intrinEmbMask.id == NI_Sve2_ConvertToSingleOdd) || + (intrinEmbMask.id == NI_Sve2_ConvertToSingleOddRoundToOdd)) + { + // TODO-SVE: Optimise away the explicit copying of `embMaskOp1Reg` to `targetReg`. + // For these intrinsics we cannot use movprfx instruction to populate `targetReg` with + // `embMaskOp1Reg`. Thus, we need to perform move before the operation, and then "sel" to + // select the active lanes. + assert((targetReg != embMaskOp2Reg) || (embMaskOp1Reg == targetReg)); + GetEmitter()->emitIns_Mov(INS_sve_mov, emitSize, targetReg, embMaskOp1Reg, + /* canSkip */ true, INS_OPTS_SCALABLE_S); + emitInsHelper(targetReg, maskReg, embMaskOp2Reg); + GetEmitter()->emitIns_R_R_R_R(INS_sve_sel, emitSize, targetReg, maskReg, targetReg, + falseReg, opt); + } + else if (falseReg != embMaskOp1Reg) { // At the point, targetReg != embMaskOp1Reg != falseReg if (HWIntrinsicInfo::IsOptionalEmbeddedMaskedOperation(intrinEmbMask.id)) diff --git a/src/coreclr/jit/hwintrinsiclistarm64sve.h b/src/coreclr/jit/hwintrinsiclistarm64sve.h index 56cc4d3c2d9e3e..4fc73e41aa168c 100644 --- a/src/coreclr/jit/hwintrinsiclistarm64sve.h +++ b/src/coreclr/jit/hwintrinsiclistarm64sve.h @@ -342,6 +342,8 @@ HARDWARE_INTRINSIC(Sve2, BitwiseSelectLeftInverted, HARDWARE_INTRINSIC(Sve2, BitwiseSelectRightInverted, -1, 3, {INS_sve_bsl2n, INS_sve_bsl2n, INS_sve_bsl2n, INS_sve_bsl2n, INS_sve_bsl2n, INS_sve_bsl2n, INS_sve_bsl2n, INS_sve_bsl2n, INS_invalid, INS_invalid}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_SpecialCodeGen|HW_Flag_HasRMWSemantics) HARDWARE_INTRINSIC(Sve2, ConvertToDoubleOdd, -1, -1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_fcvtlt, INS_sve_fcvtlt}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_BaseTypeFromFirstArg|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation) HARDWARE_INTRINSIC(Sve2, ConvertToSingleEvenRoundToOdd, -1, -1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_fcvtx, INS_sve_fcvtx}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_BaseTypeFromFirstArg|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation) +HARDWARE_INTRINSIC(Sve2, ConvertToSingleOdd, -1, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_fcvtnt, INS_invalid}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_BaseTypeFromFirstArg|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_HasRMWSemantics) +HARDWARE_INTRINSIC(Sve2, ConvertToSingleOddRoundToOdd, -1, -1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_fcvtxnt, INS_invalid}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_BaseTypeFromFirstArg|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_HasRMWSemantics) HARDWARE_INTRINSIC(Sve2, DotProductRotateComplex, -1, 4, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_cdot, INS_invalid, INS_sve_cdot, INS_invalid, INS_invalid, INS_invalid}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_HasRMWSemantics|HW_Flag_SpecialCodeGen|HW_Flag_HasImmediateOperand) HARDWARE_INTRINSIC(Sve2, DotProductRotateComplexBySelectedIndex, -1, 5, {INS_sve_cdot, INS_invalid, INS_sve_cdot, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_HasRMWSemantics|HW_Flag_SpecialCodeGen|HW_Flag_HasImmediateOperand|HW_Flag_LowVectorOperation|HW_Flag_SpecialImport|HW_Flag_BaseTypeFromSecondArg) HARDWARE_INTRINSIC(Sve2, FusedAddHalving, -1, -1, {INS_sve_shadd, INS_sve_uhadd, INS_sve_shadd, INS_sve_uhadd, INS_sve_shadd, INS_sve_uhadd, INS_sve_shadd, INS_sve_uhadd, INS_invalid, INS_invalid}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation|HW_Flag_HasRMWSemantics|HW_Flag_LowMaskedOperation) diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/Sve2.PlatformNotSupported.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/Sve2.PlatformNotSupported.cs index bcab1359869df7..7dc075973e1246 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/Sve2.PlatformNotSupported.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/Sve2.PlatformNotSupported.cs @@ -1196,6 +1196,26 @@ internal Arm64() { } /// public static Vector BitwiseSelectRightInverted(Vector select, Vector left, Vector right) { throw new PlatformNotSupportedException(); } + // Down convert and narrow (top) + + /// + /// svfloat32_t svcvtnt_f32[_f64]_m(svfloat32_t even, svbool_t pg, svfloat64_t op) + /// svfloat32_t svcvtnt_f32[_f64]_x(svfloat32_t even, svbool_t pg, svfloat64_t op) + /// FCVTNT Ztied.S, Pg/M, Zop.D + /// FCVTNT Ztied.S, Pg/M, Zop.D + /// + public static Vector ConvertToSingleOdd(Vector even, Vector value) { throw new PlatformNotSupportedException(); } + + // Down convert, rounding to odd (top) + + /// + /// svfloat32_t svcvtxnt_f32[_f64]_m(svfloat32_t even, svbool_t pg, svfloat64_t op) + /// svfloat32_t svcvtxnt_f32[_f64]_x(svfloat32_t even, svbool_t pg, svfloat64_t op) + /// FCVTXNT Ztied.S, Pg/M, Zop.D + /// FCVTXNT Ztied.S, Pg/M, Zop.D + /// + public static Vector ConvertToSingleOddRoundToOdd(Vector even, Vector value) { throw new PlatformNotSupportedException(); } + // Complex dot product /// diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/Sve2.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/Sve2.cs index 815909fa63c7fc..c8e9696c0ed8be 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/Sve2.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/Sve2.cs @@ -1196,6 +1196,26 @@ internal Arm64() { } /// public static Vector BitwiseSelectRightInverted(Vector select, Vector left, Vector right) => BitwiseSelectRightInverted(select, left, right); + // Down convert and narrow (top) + + /// + /// svfloat32_t svcvtnt_f32[_f64]_m(svfloat32_t even, svbool_t pg, svfloat64_t op) + /// svfloat32_t svcvtnt_f32[_f64]_x(svfloat32_t even, svbool_t pg, svfloat64_t op) + /// FCVTNT Ztied.S, Pg/M, Zop.D + /// FCVTNT Ztied.S, Pg/M, Zop.D + /// + public static Vector ConvertToSingleOdd(Vector even, Vector value) => ConvertToSingleOdd(even, value); + + // Down convert, rounding to odd (top) + + /// + /// svfloat32_t svcvtxnt_f32[_f64]_m(svfloat32_t even, svbool_t pg, svfloat64_t op) + /// svfloat32_t svcvtxnt_f32[_f64]_x(svfloat32_t even, svbool_t pg, svfloat64_t op) + /// FCVTXNT Ztied.S, Pg/M, Zop.D + /// FCVTXNT Ztied.S, Pg/M, Zop.D + /// + public static Vector ConvertToSingleOddRoundToOdd(Vector even, Vector value) => ConvertToSingleOddRoundToOdd(even, value); + // Complex dot product /// diff --git a/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs b/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs index 0775004609bc4b..aa591b4d1ea80d 100644 --- a/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs +++ b/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs @@ -6354,6 +6354,8 @@ internal Arm64() { } public static System.Numerics.Vector BitwiseSelectRightInverted(System.Numerics.Vector select, System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } public static System.Numerics.Vector ConvertToDoubleOdd(System.Numerics.Vector value) { throw null; } public static System.Numerics.Vector ConvertToSingleEvenRoundToOdd(System.Numerics.Vector value) { throw null; } + public static System.Numerics.Vector ConvertToSingleOdd(System.Numerics.Vector even, System.Numerics.Vector value) { throw null; } + public static System.Numerics.Vector ConvertToSingleOddRoundToOdd(System.Numerics.Vector even, System.Numerics.Vector value) { throw null; } public static System.Numerics.Vector DotProductRotateComplex(System.Numerics.Vector op1, System.Numerics.Vector op2, System.Numerics.Vector op3, [ConstantExpected(Min = 0, Max = (byte)(3))] byte rotation) { throw null; } public static System.Numerics.Vector DotProductRotateComplex(System.Numerics.Vector op1, System.Numerics.Vector op2, System.Numerics.Vector op3, [ConstantExpected(Min = 0, Max = (byte)(3))] byte rotation) { throw null; } public static System.Numerics.Vector DotProductRotateComplexBySelectedIndex(System.Numerics.Vector op1, System.Numerics.Vector op2, System.Numerics.Vector op3, [ConstantExpected(Min = 0, Max = (byte)(3))] byte imm_index, [ConstantExpected(Min = 0, Max = (byte)(3))] byte rotation) { throw null; } diff --git a/src/tests/Common/GenerateHWIntrinsicTests/Arm/Sve2Tests.cs b/src/tests/Common/GenerateHWIntrinsicTests/Arm/Sve2Tests.cs index bfe40fee207c69..6ad73360c19b5c 100644 --- a/src/tests/Common/GenerateHWIntrinsicTests/Arm/Sve2Tests.cs +++ b/src/tests/Common/GenerateHWIntrinsicTests/Arm/Sve2Tests.cs @@ -222,9 +222,13 @@ public static (string templateFileName, Dictionary templateData) ("SveVecTernOpTest.template", new Dictionary { ["TestName"] = "Sve2_BitwiseSelectRightInverted_uint", ["Isa"] = "Sve2", ["LoadIsa"] = "Sve2", ["Method"] = "BitwiseSelectRightInverted", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt32", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt32()", ["ConvertFunc"] = "", ["ValidateIterResult"] = "result[i] != Helpers.BitwiseSelectRightInverted(firstOp[i], secondOp[i], thirdOp[i])", ["GetIterResult"] = "Helpers.BitwiseSelectRightInverted(firstOp[i], secondOp[i], thirdOp[i])"}), ("SveVecTernOpTest.template", new Dictionary { ["TestName"] = "Sve2_BitwiseSelectRightInverted_ulong", ["Isa"] = "Sve2", ["LoadIsa"] = "Sve2", ["Method"] = "BitwiseSelectRightInverted", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt64", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "UInt64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt64()", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt64()", ["ConvertFunc"] = "", ["ValidateIterResult"] = "result[i] != Helpers.BitwiseSelectRightInverted(firstOp[i], secondOp[i], thirdOp[i])", ["GetIterResult"] = "Helpers.BitwiseSelectRightInverted(firstOp[i], secondOp[i], thirdOp[i])"}), - ("SveSimpleVecOpDiffRetTypeTest.template", new Dictionary {["TestName"] = "Sve2_ConvertToDoubleOdd_double_float", ["Isa"] = "Sve2", ["LoadIsa"] = "Sve2", ["Method"] = "ConvertToDoubleOdd", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Single", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSingle()", ["ConvertFunc"] = "", ["ValidateIterResult"] = "Helpers.ConvertToDouble(firstOp[i * 2 + 1]) != result[i]", ["GetIterResult"] = "Helpers.ConvertToDouble(left[i * 2 + 1])"}), + ("SveSimpleVecOpDiffRetTypeTest.template", new Dictionary { ["TestName"] = "Sve2_ConvertToDoubleOdd_double_float", ["Isa"] = "Sve2", ["LoadIsa"] = "Sve2", ["Method"] = "ConvertToDoubleOdd", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Single", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSingle()", ["ConvertFunc"] = "", ["ValidateIterResult"] = "Helpers.ConvertToDouble(firstOp[i * 2 + 1]) != result[i]", ["GetIterResult"] = "Helpers.ConvertToDouble(left[i * 2 + 1])"}), - ("SveSimpleVecOpDiffRetTypeTest.template", new Dictionary {["TestName"] = "Sve2_ConvertToSingleEvenRoundToOdd_float_double", ["Isa"] = "Sve2", ["LoadIsa"] = "Sve2", ["Method"] = "ConvertToSingleEvenRoundToOdd", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Double", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetDouble()", ["ConvertFunc"] = "", ["ValidateIterResult"] = "Helpers.ConvertToSingleEvenRoundToOdd(firstOp, i) != result[i]", ["GetIterResult"] = "Helpers.ConvertToSingleEvenRoundToOdd(left, i)"}), + ("SveSimpleVecOpDiffRetTypeTest.template", new Dictionary { ["TestName"] = "Sve2_ConvertToSingleEvenRoundToOdd_float_double", ["Isa"] = "Sve2", ["LoadIsa"] = "Sve2", ["Method"] = "ConvertToSingleEvenRoundToOdd", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Double", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetDouble()", ["ConvertFunc"] = "", ["ValidateIterResult"] = "Helpers.ConvertToSingleEvenRoundToOdd(firstOp, i) != result[i]", ["GetIterResult"] = "Helpers.ConvertToSingleEvenRoundToOdd(left, i)"}), + + ("SveVecBinOpDifferentRetType.template", new Dictionary { ["TestName"] = "Sve2_ConvertToSingleOdd_float_double", ["Isa"] = "Sve2", ["LoadIsa"] = "Sve2", ["Method"] = "ConvertToSingleOdd", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSingle()", ["NextValueOp2"] = "TestLibrary.Generator.GetDouble()", ["ConvertFunc"] = "", ["ValidateIterResult"] = "Helpers.ConvertToSingleOdd(left, right, i) != result[i]", ["GetIterResult"] = "Helpers.ConvertToSingleOdd(left, right, i)"}), + + ("SveVecBinOpDifferentRetType.template", new Dictionary { ["TestName"] = "Sve2_ConvertToSingleOddRoundToOdd_float_double", ["Isa"] = "Sve2", ["LoadIsa"] = "Sve2", ["Method"] = "ConvertToSingleOddRoundToOdd", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSingle()", ["NextValueOp2"] = "TestLibrary.Generator.GetDouble()", ["ConvertFunc"] = "", ["ValidateIterResult"] = "Helpers.ConvertToSingleOddRoundToOdd(left, right, i) != result[i]", ["GetIterResult"] = "Helpers.ConvertToSingleOddRoundToOdd(left, right, i)"}), ("SveVecImmTernOpFirstArgTest.template", new Dictionary { ["TestName"] = "Sve2_DotProductRotateComplex_int_sbyte_0", ["Isa"] = "Sve2", ["LoadIsa"] = "Sve2", ["Method"] = "DotProductRotateComplex", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "SByte", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "SByte", ["Op4BaseType"] = "Byte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["NextValueOp3"] = "TestLibrary.Generator.GetSByte()", ["Imm"] = "0", ["InvalidImm"] = "4", ["ConvertFunc"] = "", ["ValidateIterResult"] = "Helpers.DotProductRotateComplex(first[i], second, 4 * i, third, Imm) != result[i]", ["GetIterResult"] = "Helpers.DotProductRotateComplex(first[i], second, 4 * i, third, Imm)"}), ("SveVecImmTernOpFirstArgTest.template", new Dictionary { ["TestName"] = "Sve2_DotProductRotateComplex_int_sbyte_1", ["Isa"] = "Sve2", ["LoadIsa"] = "Sve2", ["Method"] = "DotProductRotateComplex", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "SByte", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "SByte", ["Op4BaseType"] = "Byte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["NextValueOp3"] = "TestLibrary.Generator.GetSByte()", ["Imm"] = "1", ["InvalidImm"] = "4", ["ConvertFunc"] = "", ["ValidateIterResult"] = "Helpers.DotProductRotateComplex(first[i], second, 4 * i, third, Imm) != result[i]", ["GetIterResult"] = "Helpers.DotProductRotateComplex(first[i], second, 4 * i, third, Imm)"}), diff --git a/src/tests/JIT/HardwareIntrinsics/Arm/Shared/Helpers.cs b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/Helpers.cs index ab0388d112bb92..30b877de4b5575 100644 --- a/src/tests/JIT/HardwareIntrinsics/Arm/Shared/Helpers.cs +++ b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/Helpers.cs @@ -4328,43 +4328,55 @@ private static (ulong val, bool ovf) ShiftOvf(ulong value, int shift) public static float AbsoluteDifference(float op1, float op2) => MathF.Abs(op1 - op2); - public static float ConvertToSingleEvenRoundToOdd(double[] value, int i) + private static float ConvertToSingleRoundToOdd(double val) { - if (i % 2 == 0) - { - double val = value[i / 2]; - float floatVal = (float)val; + float f = (float)val; - float f = (float)val; + // If val is NaN or Inf there’s nothing else to do + if (double.IsNaN(val) || double.IsInfinity(val)) + return f; - // If val is NaN or Inf there’s nothing else to do - if (double.IsNaN(val) || double.IsInfinity(val)) - return f; + // Detect the cases where the default cast rounded away from zero + if ((val > 0 && (double)f > val) || + (val < 0 && (double)f < val)) + { + // Move toward zero to get truncate() behaviour. + int bits = BitConverter.SingleToInt32Bits(f); + bits += (val > 0) ? -1 : +1; + f = BitConverter.Int32BitsToSingle(bits); + } - // Detect the cases where the default cast rounded away from zero - if ((val > 0 && (double)f > val) || - (val < 0 && (double)f < val)) - { - // Move toward zero to get truncate() behaviour. - int bits = BitConverter.SingleToInt32Bits(f); - bits += (val > 0) ? -1 : +1; - f = BitConverter.Int32BitsToSingle(bits); - } + // Round to odd, force the last bit of the mantissa to 1 if the conversion was inexact + if (val != (double)f) + { + int bits = BitConverter.SingleToInt32Bits(f); + bits |= 0x1; + f = BitConverter.Int32BitsToSingle(bits); + } - // Round to odd, force the last bit of the mantissa to 1 if the conversion was inexact - if (val != (double)f) - { - int bits = BitConverter.SingleToInt32Bits(f); - bits |= 0x1; - f = BitConverter.Int32BitsToSingle(bits); - } + return f; + } - return f; + public static float ConvertToSingleEvenRoundToOdd(double[] value, int i) + { + if (i % 2 == 0) + { + return ConvertToSingleRoundToOdd(value[i / 2]); } return 0f; } + public static float ConvertToSingleOddRoundToOdd(float[] even, double[] op, int i) + { + if (i % 2 != 0) + { + return ConvertToSingleRoundToOdd(op[(i - 1) / 2]); + } + + return even[i]; + } + public static float FusedMultiplyAdd(float op1, float op2, float op3) => MathF.FusedMultiplyAdd(op2, op3, op1); public static float FusedMultiplyAddNegated(float op1, float op2, float op3) => MathF.FusedMultiplyAdd(-op2, op3, -op1); @@ -5874,6 +5886,16 @@ public static float[] ConvertToSingle(ulong[] op1) return result; } + public static float ConvertToSingleOdd(float[] even, double[] op, int i) + { + if (i % 2 == 0) + { + return even[i]; + } + + return (float)op[(i - 1) / 2]; + } + public static float ConvertToSingleUpper(float[] op1, double[] op2, int i) => i < op1.Length ? op1[i] : ConvertToSingle(op2[i - op1.Length]); public static double ConvertToDouble(float op1) => op1;