Implement Fcvtn (single->half)

This commit is contained in:
MerryMage 2018-10-21 18:46:59 +01:00
parent 7920dc1d2f
commit a69e8a6f77
3 changed files with 95 additions and 5 deletions

View file

@ -122,14 +122,14 @@ namespace ChocolArm64.Instruction
for (int Index = 0; Index < Elems; Index++)
{
EmitVectorExtractF(Context, Op.Rd, Index, SizeF);
EmitVectorExtractF(Context, Op.Rn, Index, SizeF);
if (SizeF == 0)
{
//TODO: This need the half precision floating point type,
//that is not yet supported on .NET. We should probably
//do our own implementation on the meantime.
throw new NotImplementedException();
Context.Emit(OpCodes.Ldc_I4_0);
Context.EmitCall(typeof(ASoftFloat), nameof(ASoftFloat.ConvertSingleToHalf));
EmitVectorInsert(Context, Op.Rd, Part + Index, 1);
}
else /* if (SizeF == 1) */
{

View file

@ -231,6 +231,67 @@ namespace ChocolArm64.Instruction
uint new_exp = (uint)((exponent + 127) & 0xFF) << 23;
return BitConverter.Int32BitsToSingle((int)((x_sign << 31) | new_exp | (x_mantissa << 13)));
}
public static ushort ConvertSingleToHalf(float op1, bool alt_hp)
{
uint op1_bits = (uint)BitConverter.SingleToInt32Bits(op1);
uint sign = (op1_bits >> 31) & 1;
uint exp = (op1_bits >> 23) & 0xFF;
uint mantissa = op1_bits & ((1u << 23) - 1);
// Convert from biased-single to biased-half precision
int new_exp = ((int)exp - 127) + 15;
ushort new_mantissa = (ushort)(mantissa >> 13);
if (exp == 0xFF && mantissa != 0)
{
// NaN
if (alt_hp)
{
return (ushort)(sign << 15);
}
return (ushort)((sign << 15) | 0x7E00u | new_mantissa);
}
else if (exp == 0xFF && mantissa == 0)
{
// Infinity
if (alt_hp)
{
return (ushort)((sign << 15) | 0x7FFFu);
}
return (ushort)((sign << 15) | 0x7C00u);
}
else if (exp == 0 && mantissa == 0)
{
// Zero
return (ushort)(sign << 15);
}
if (new_exp <= 0)
{
// Output a denormal
new_mantissa |= 0x0400; // explicit leading bit
new_mantissa >>= 1 - new_exp;
return (ushort)((sign << 15) | new_mantissa);
}
// TODO: Rounding correction
// We currently assume truncation rounding
if (new_exp >= 0x1F && !alt_hp)
{
// Output an infinity
return (ushort)((sign << 15) | 0x7C00u);
}
else if (new_exp >= 0x20 && alt_hp)
{
// Output a maximum value
return (ushort)((sign << 15) | 0x7FFFu);
}
return (ushort)((sign << 15) | (ushort)((uint)new_exp << 10) | new_mantissa);
}
}
static class ASoftFloat_32

View file

@ -39,5 +39,34 @@ namespace Ryujinx.Tests.Cpu
CompareAgainstUnicorn();
}
[TestCase((ushort)0x0000, 0x00000000u)] // Positive Zero
[TestCase((ushort)0x8000, 0x80000000u)] // Negative Zero
[TestCase((ushort)0x3E00, 0x3FC00000u)] // +1.5
[TestCase((ushort)0xBE00, 0xBFC00000u)] // -1.5
[TestCase((ushort)0xFFFF, 0xFFFFE000u)] // -QNaN
[TestCase((ushort)0x7C00, 0x7F800000u)] // +Inf
[TestCase((ushort)0x3C00, 0x3F800000u)] // 1.0
[TestCase((ushort)0xC000, 0xC0000000u)] // -2.0
[TestCase((ushort)0x7BFF, 0x477FE000u)] // 65504.0 (Largest Normal)
[TestCase((ushort)0x03FF, 0x387FC000u)] // 0.00006097555 (Largest Subnormal)
[TestCase((ushort)0x0001, 0x33800000u)] // 5.96046448e-8 (Smallest Subnormal)
public void Fcvtn_V_f16(ushort Result, uint Value)
{
uint Opcode = 0x0E216801; // FCVTN V1.4H, V0.4S
Vector128<float> V0 = Sse.StaticCast<uint, float>(Sse2.SetAllVector128(Value));
AThreadState ThreadState = SingleOpcode(Opcode, V0: V0);
Assert.Multiple(() =>
{
Assert.That(Sse2.Extract(Sse.StaticCast<float, ushort>(ThreadState.V1), (byte)0), Is.EqualTo(Result));
Assert.That(Sse2.Extract(Sse.StaticCast<float, ushort>(ThreadState.V1), (byte)1), Is.EqualTo(Result));
Assert.That(Sse2.Extract(Sse.StaticCast<float, ushort>(ThreadState.V1), (byte)2), Is.EqualTo(Result));
Assert.That(Sse2.Extract(Sse.StaticCast<float, ushort>(ThreadState.V1), (byte)3), Is.EqualTo(Result));
});
CompareAgainstUnicorn();
}
}
}