Update SoftFloat.cs

This commit is contained in:
LDj3SNuD 2018-12-18 00:54:19 +01:00 committed by GitHub
parent 5a8931e681
commit cfb7c165be
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -13,187 +13,68 @@ namespace ChocolArm64.Instructions
InvSqrtEstimateTable = BuildInvSqrtEstimateTable();
}
private static readonly byte[] RecipEstimateTable;
private static readonly byte[] InvSqrtEstimateTable;
internal static readonly byte[] RecipEstimateTable;
internal static readonly byte[] InvSqrtEstimateTable;
private static byte[] BuildRecipEstimateTable()
{
byte[] table = new byte[256];
for (ulong index = 0; index < 256; index++)
byte[] tbl = new byte[256];
for (uint idx = 0u; idx < 256u; idx++)
{
ulong a = index | 0x100;
uint src = idx + 256u;
a = (a << 1) + 1;
ulong b = 0x80000 / a;
b = (b + 1) >> 1;
Debug.Assert(256u <= src && src < 512u);
table[index] = (byte)(b & 0xFF);
src = (src << 1) + 1u;
uint aux = 0x00080000u / src;
uint dst = (aux + 1u) >> 1;
Debug.Assert(256u <= dst && dst < 512u);
tbl[idx] = (byte)(dst - 256u);
}
return table;
return tbl;
}
private static byte[] BuildInvSqrtEstimateTable()
{
byte[] table = new byte[512];
for (ulong index = 128; index < 512; index++)
byte[] tbl = new byte[384];
for (uint idx = 0u; idx < 384u; idx++)
{
ulong a = index;
if (a < 256)
uint src = idx + 128u;
Debug.Assert(128u <= src && src < 512u);
if (src < 256u)
{
a = (a << 1) + 1;
src = (src << 1) + 1u;
}
else
{
a = (a | 1) << 1;
src = (src >> 1) << 1;
src = (src + 1u) << 1;
}
ulong b = 256;
while (a * (b + 1) * (b + 1) < (1ul << 28))
uint aux = 512u;
while (src * (aux + 1u) * (aux + 1u) < 0x10000000u)
{
b++;
}
b = (b + 1) >> 1;
table[index] = (byte)(b & 0xFF);
}
return table;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float RecipEstimate(float x)
{
return (float)RecipEstimate((double)x);
}
public static double RecipEstimate(double x)
{
ulong xBits = (ulong)BitConverter.DoubleToInt64Bits(x);
ulong xSign = xBits & 0x8000000000000000;
ulong xExp = (xBits >> 52) & 0x7FF;
ulong scaled = xBits & ((1ul << 52) - 1);
if (xExp >= 2045)
{
if (xExp == 0x7ff && scaled != 0)
{
// NaN
return BitConverter.Int64BitsToDouble((long)(xBits | 0x0008000000000000));
aux = aux + 1u;
}
// Infinity, or Out of range -> Zero
return BitConverter.Int64BitsToDouble((long)xSign);
uint dst = (aux + 1u) >> 1;
Debug.Assert(256u <= dst && dst < 512u);
tbl[idx] = (byte)(dst - 256u);
}
if (xExp == 0)
{
if (scaled == 0)
{
// Zero -> Infinity
return BitConverter.Int64BitsToDouble((long)(xSign | 0x7FF0000000000000));
}
// Denormal
if ((scaled & (1ul << 51)) == 0)
{
xExp = ~0ul;
scaled <<= 2;
}
else
{
scaled <<= 1;
}
}
scaled >>= 44;
scaled &= 0xFF;
ulong resultExp = (2045 - xExp) & 0x7FF;
ulong estimate = (ulong)RecipEstimateTable[scaled];
ulong fraction = estimate << 44;
if (resultExp == 0)
{
fraction >>= 1;
fraction |= 1ul << 51;
}
else if (resultExp == 0x7FF)
{
resultExp = 0;
fraction >>= 2;
fraction |= 1ul << 50;
}
ulong result = xSign | (resultExp << 52) | fraction;
return BitConverter.Int64BitsToDouble((long)result);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float InvSqrtEstimate(float x)
{
return (float)InvSqrtEstimate((double)x);
}
public static double InvSqrtEstimate(double x)
{
ulong xBits = (ulong)BitConverter.DoubleToInt64Bits(x);
ulong xSign = xBits & 0x8000000000000000;
long xExp = (long)((xBits >> 52) & 0x7FF);
ulong scaled = xBits & ((1ul << 52) - 1);
if (xExp == 0x7FF && scaled != 0)
{
// NaN
return BitConverter.Int64BitsToDouble((long)(xBits | 0x0008000000000000));
}
if (xExp == 0)
{
if (scaled == 0)
{
// Zero -> Infinity
return BitConverter.Int64BitsToDouble((long)(xSign | 0x7FF0000000000000));
}
// Denormal
while ((scaled & (1 << 51)) == 0)
{
scaled <<= 1;
xExp--;
}
scaled <<= 1;
}
if (xSign != 0)
{
// Negative -> NaN
return BitConverter.Int64BitsToDouble((long)0x7FF8000000000000);
}
if (xExp == 0x7ff && scaled == 0)
{
// Infinity -> Zero
return BitConverter.Int64BitsToDouble((long)xSign);
}
if (((ulong)xExp & 1) == 1)
{
scaled >>= 45;
scaled &= 0xFF;
scaled |= 0x80;
}
else
{
scaled >>= 44;
scaled &= 0xFF;
scaled |= 0x100;
}
ulong resultExp = ((ulong)(3068 - xExp) / 2) & 0x7FF;
ulong estimate = (ulong)InvSqrtEstimateTable[scaled];
ulong fraction = estimate << 44;
ulong result = xSign | (resultExp << 52) | fraction;
return BitConverter.Int64BitsToDouble((long)result);
return tbl;
}
}
@ -1276,6 +1157,95 @@ namespace ChocolArm64.Instructions
return result;
}
public static float FPRecipEstimate(float value, CpuThreadState state)
{
Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat32.FPRecipEstimate: state.Fpcr = 0x{state.Fpcr:X8}");
value.FPUnpack(out FpType type, out bool sign, out uint op, state);
float result;
if (type == FpType.SNaN || type == FpType.QNaN)
{
result = FPProcessNaN(type, op, state);
}
else if (type == FpType.Infinity)
{
result = FPZero(sign);
}
else if (type == FpType.Zero)
{
result = FPInfinity(sign);
FPProcessException(FpExc.DivideByZero, state);
}
else if (MathF.Abs(value) < MathF.Pow(2f, -128))
{
bool overflowToInf;
switch (state.FPRoundingMode())
{
default:
case RoundMode.ToNearest: overflowToInf = true; break;
case RoundMode.TowardsPlusInfinity: overflowToInf = !sign; break;
case RoundMode.TowardsMinusInfinity: overflowToInf = sign; break;
case RoundMode.TowardsZero: overflowToInf = false; break;
}
result = overflowToInf ? FPInfinity(sign) : FPMaxNormal(sign);
FPProcessException(FpExc.Overflow, state);
FPProcessException(FpExc.Inexact, state);
}
else if (state.GetFpcrFlag(Fpcr.Fz) && (MathF.Abs(value) >= MathF.Pow(2f, 126)))
{
result = FPZero(sign);
state.SetFpsrFlag(Fpsr.Ufc);
}
else
{
ulong fraction = (ulong)(op & 0x007FFFFFu) << 29;
uint exp = (op & 0x7F800000u) >> 23;
if (exp == 0u)
{
if ((fraction & 0x0008000000000000ul) == 0ul)
{
fraction = (fraction & 0x0003FFFFFFFFFFFFul) << 2;
exp -= 1u;
}
else
{
fraction = (fraction & 0x0007FFFFFFFFFFFFul) << 1;
}
}
uint scaled = (uint)(((fraction & 0x000FF00000000000ul) | 0x0010000000000000ul) >> 44);
uint resultExp = 253u - exp;
uint estimate = SoftFloat.RecipEstimateTable[scaled - 256u] + 256u;
fraction = (ulong)(estimate & 0xFFu) << 44;
if (resultExp == 0u)
{
fraction = ((fraction & 0x000FFFFFFFFFFFFEul) | 0x0010000000000000ul) >> 1;
}
else if (resultExp + 1u == 0u)
{
fraction = ((fraction & 0x000FFFFFFFFFFFFCul) | 0x0010000000000000ul) >> 2;
resultExp = 0u;
}
result = BitConverter.Int32BitsToSingle(
(int)((sign ? 1u : 0u) << 31 | (resultExp & 0xFFu) << 23 | (uint)(fraction >> 29) & 0x007FFFFFu));
}
return result;
}
public static float FPRecipStepFused(float value1, float value2, CpuThreadState state)
{
Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat32.FPRecipStepFused: state.Fpcr = 0x{state.Fpcr:X8}");
@ -1343,6 +1313,71 @@ namespace ChocolArm64.Instructions
return result;
}
public static float FPRSqrtEstimate(float value, CpuThreadState state)
{
Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat32.FPRSqrtEstimate: state.Fpcr = 0x{state.Fpcr:X8}");
value.FPUnpack(out FpType type, out bool sign, out uint op, state);
float result;
if (type == FpType.SNaN || type == FpType.QNaN)
{
result = FPProcessNaN(type, op, state);
}
else if (type == FpType.Zero)
{
result = FPInfinity(sign);
FPProcessException(FpExc.DivideByZero, state);
}
else if (sign)
{
result = FPDefaultNaN();
FPProcessException(FpExc.InvalidOp, state);
}
else if (type == FpType.Infinity)
{
result = FPZero(false);
}
else
{
ulong fraction = (ulong)(op & 0x007FFFFFu) << 29;
uint exp = (op & 0x7F800000u) >> 23;
if (exp == 0u)
{
while ((fraction & 0x0008000000000000ul) == 0ul)
{
fraction = (fraction & 0x0007FFFFFFFFFFFFul) << 1;
exp -= 1u;
}
fraction = (fraction & 0x0007FFFFFFFFFFFFul) << 1;
}
uint scaled;
if ((exp & 1u) == 0u)
{
scaled = (uint)(((fraction & 0x000FF00000000000ul) | 0x0010000000000000ul) >> 44);
}
else
{
scaled = (uint)(((fraction & 0x000FE00000000000ul) | 0x0010000000000000ul) >> 45);
}
uint resultExp = (380u - exp) >> 1;
uint estimate = SoftFloat.InvSqrtEstimateTable[scaled - 128u] + 256u;
result = BitConverter.Int32BitsToSingle((int)((resultExp & 0xFFu) << 23 | (estimate & 0xFFu) << 15));
}
return result;
}
public static float FPRSqrtStepFused(float value1, float value2, CpuThreadState state)
{
Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat32.FPRSqrtStepFused: state.Fpcr = 0x{state.Fpcr:X8}");
@ -1490,6 +1525,11 @@ namespace ChocolArm64.Instructions
return sign ? -0f : +0f;
}
private static float FPMaxNormal(bool sign)
{
return sign ? float.MinValue : float.MaxValue;
}
private static float FPTwo(bool sign)
{
return sign ? -2f : +2f;
@ -2201,6 +2241,95 @@ namespace ChocolArm64.Instructions
return result;
}
public static double FPRecipEstimate(double value, CpuThreadState state)
{
Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat64.FPRecipEstimate: state.Fpcr = 0x{state.Fpcr:X8}");
value.FPUnpack(out FpType type, out bool sign, out ulong op, state);
double result;
if (type == FpType.SNaN || type == FpType.QNaN)
{
result = FPProcessNaN(type, op, state);
}
else if (type == FpType.Infinity)
{
result = FPZero(sign);
}
else if (type == FpType.Zero)
{
result = FPInfinity(sign);
FPProcessException(FpExc.DivideByZero, state);
}
else if (Math.Abs(value) < Math.Pow(2d, -1024))
{
bool overflowToInf;
switch (state.FPRoundingMode())
{
default:
case RoundMode.ToNearest: overflowToInf = true; break;
case RoundMode.TowardsPlusInfinity: overflowToInf = !sign; break;
case RoundMode.TowardsMinusInfinity: overflowToInf = sign; break;
case RoundMode.TowardsZero: overflowToInf = false; break;
}
result = overflowToInf ? FPInfinity(sign) : FPMaxNormal(sign);
FPProcessException(FpExc.Overflow, state);
FPProcessException(FpExc.Inexact, state);
}
else if (state.GetFpcrFlag(Fpcr.Fz) && (Math.Abs(value) >= Math.Pow(2d, 1022)))
{
result = FPZero(sign);
state.SetFpsrFlag(Fpsr.Ufc);
}
else
{
ulong fraction = op & 0x000FFFFFFFFFFFFFul;
uint exp = (uint)((op & 0x7FF0000000000000ul) >> 52);
if (exp == 0u)
{
if ((fraction & 0x0008000000000000ul) == 0ul)
{
fraction = (fraction & 0x0003FFFFFFFFFFFFul) << 2;
exp -= 1u;
}
else
{
fraction = (fraction & 0x0007FFFFFFFFFFFFul) << 1;
}
}
uint scaled = (uint)(((fraction & 0x000FF00000000000ul) | 0x0010000000000000ul) >> 44);
uint resultExp = 2045u - exp;
uint estimate = SoftFloat.RecipEstimateTable[scaled - 256u] + 256u;
fraction = (ulong)(estimate & 0xFFu) << 44;
if (resultExp == 0u)
{
fraction = ((fraction & 0x000FFFFFFFFFFFFEul) | 0x0010000000000000ul) >> 1;
}
else if (resultExp + 1u == 0u)
{
fraction = ((fraction & 0x000FFFFFFFFFFFFCul) | 0x0010000000000000ul) >> 2;
resultExp = 0u;
}
result = BitConverter.Int64BitsToDouble(
(long)((sign ? 1ul : 0ul) << 63 | (resultExp & 0x7FFul) << 52 | (fraction & 0x000FFFFFFFFFFFFFul)));
}
return result;
}
public static double FPRecipStepFused(double value1, double value2, CpuThreadState state)
{
Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat64.FPRecipStepFused: state.Fpcr = 0x{state.Fpcr:X8}");
@ -2268,6 +2397,71 @@ namespace ChocolArm64.Instructions
return result;
}
public static double FPRSqrtEstimate(double value, CpuThreadState state)
{
Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat64.FPRSqrtEstimate: state.Fpcr = 0x{state.Fpcr:X8}");
value.FPUnpack(out FpType type, out bool sign, out ulong op, state);
double result;
if (type == FpType.SNaN || type == FpType.QNaN)
{
result = FPProcessNaN(type, op, state);
}
else if (type == FpType.Zero)
{
result = FPInfinity(sign);
FPProcessException(FpExc.DivideByZero, state);
}
else if (sign)
{
result = FPDefaultNaN();
FPProcessException(FpExc.InvalidOp, state);
}
else if (type == FpType.Infinity)
{
result = FPZero(false);
}
else
{
ulong fraction = op & 0x000FFFFFFFFFFFFFul;
uint exp = (uint)((op & 0x7FF0000000000000ul) >> 52);
if (exp == 0u)
{
while ((fraction & 0x0008000000000000ul) == 0ul)
{
fraction = (fraction & 0x0007FFFFFFFFFFFFul) << 1;
exp -= 1u;
}
fraction = (fraction & 0x0007FFFFFFFFFFFFul) << 1;
}
uint scaled;
if ((exp & 1u) == 0u)
{
scaled = (uint)(((fraction & 0x000FF00000000000ul) | 0x0010000000000000ul) >> 44);
}
else
{
scaled = (uint)(((fraction & 0x000FE00000000000ul) | 0x0010000000000000ul) >> 45);
}
uint resultExp = (3068u - exp) >> 1;
uint estimate = SoftFloat.InvSqrtEstimateTable[scaled - 128u] + 256u;
result = BitConverter.Int64BitsToDouble((long)((resultExp & 0x7FFul) << 52 | (estimate & 0xFFul) << 44));
}
return result;
}
public static double FPRSqrtStepFused(double value1, double value2, CpuThreadState state)
{
Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat64.FPRSqrtStepFused: state.Fpcr = 0x{state.Fpcr:X8}");
@ -2415,6 +2609,11 @@ namespace ChocolArm64.Instructions
return sign ? -0d : +0d;
}
private static double FPMaxNormal(bool sign)
{
return sign ? double.MinValue : double.MaxValue;
}
private static double FPTwo(bool sign)
{
return sign ? -2d : +2d;