Fix Frecpe_S/V and Frsqrte_S/V (full FP emu.). Add Sse Opt. & SoftFloat Impl. for Fcmeq/ge/gt/le/lt_S/V (Reg & Zero), Faddp_S/V, Fmaxp_V, Fminp_V Inst.; add Sse Opt. for Shll_V, S/Ushll_V Inst.; improve Sse Opt. for Xtn_V Inst.. Add Tests. (#543)
* Update Optimizations.cs * Update InstEmitSimdShift.cs * Update InstEmitSimdHelper.cs * Update InstEmitSimdArithmetic.cs * Update InstEmitSimdMove.cs * Update SoftFloat.cs * Update InstEmitSimdCmp.cs * Update CpuTestSimdShImm.cs * Update CpuTestSimd.cs * Update CpuTestSimdReg.cs * Nit. * Update SoftFloat.cs * Update InstEmitSimdArithmetic.cs * Update InstEmitSimdHelper.cs * Update CpuTestSimd.cs * Explicit some implicit casts. * Simplify some powers; nits. * Update OpCodeTable.cs * Update InstEmitSimdArithmetic.cs * Update CpuTestSimdReg.cs * Update InstEmitSimdArithmetic.cs
This commit is contained in:
parent
d8f2497f15
commit
0f5b6dfbe8
11 changed files with 1808 additions and 441 deletions
|
@ -9,191 +9,72 @@ namespace ChocolArm64.Instructions
|
|||
{
|
||||
static SoftFloat()
|
||||
{
|
||||
RecipEstimateTable = BuildRecipEstimateTable();
|
||||
InvSqrtEstimateTable = BuildInvSqrtEstimateTable();
|
||||
RecipEstimateTable = BuildRecipEstimateTable();
|
||||
RecipSqrtEstimateTable = BuildRecipSqrtEstimateTable();
|
||||
}
|
||||
|
||||
private static readonly byte[] RecipEstimateTable;
|
||||
private static readonly byte[] InvSqrtEstimateTable;
|
||||
internal static readonly byte[] RecipEstimateTable;
|
||||
internal static readonly byte[] RecipSqrtEstimateTable;
|
||||
|
||||
private static byte[] BuildRecipEstimateTable()
|
||||
{
|
||||
byte[] table = new byte[256];
|
||||
for (ulong index = 0; index < 256; index++)
|
||||
byte[] tbl = new byte[256];
|
||||
|
||||
for (int idx = 0; idx < 256; idx++)
|
||||
{
|
||||
ulong a = index | 0x100;
|
||||
uint src = (uint)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 = (1u << 19) / 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()
|
||||
private static byte[] BuildRecipSqrtEstimateTable()
|
||||
{
|
||||
byte[] table = new byte[512];
|
||||
for (ulong index = 128; index < 512; index++)
|
||||
byte[] tbl = new byte[384];
|
||||
|
||||
for (int idx = 0; idx < 384; idx++)
|
||||
{
|
||||
ulong a = index;
|
||||
if (a < 256)
|
||||
uint src = (uint)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) < (1u << 28))
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -395,12 +276,12 @@ namespace ChocolArm64.Instructions
|
|||
{
|
||||
intMant++;
|
||||
|
||||
if (intMant == (uint)Math.Pow(2d, f))
|
||||
if (intMant == 1u << f)
|
||||
{
|
||||
biasedExp = 1u;
|
||||
}
|
||||
|
||||
if (intMant == (uint)Math.Pow(2d, f + 1))
|
||||
if (intMant == 1u << (f + 1))
|
||||
{
|
||||
biasedExp++;
|
||||
intMant >>= 1;
|
||||
|
@ -409,7 +290,7 @@ namespace ChocolArm64.Instructions
|
|||
|
||||
float result;
|
||||
|
||||
if (biasedExp >= (uint)Math.Pow(2d, e) - 1u)
|
||||
if (biasedExp >= (1u << e) - 1u)
|
||||
{
|
||||
result = overflowToInf ? FPInfinity(sign) : FPMaxNormal(sign);
|
||||
|
||||
|
@ -666,12 +547,12 @@ namespace ChocolArm64.Instructions
|
|||
{
|
||||
intMant++;
|
||||
|
||||
if (intMant == (uint)Math.Pow(2d, f))
|
||||
if (intMant == 1u << f)
|
||||
{
|
||||
biasedExp = 1u;
|
||||
}
|
||||
|
||||
if (intMant == (uint)Math.Pow(2d, f + 1))
|
||||
if (intMant == 1u << (f + 1))
|
||||
{
|
||||
biasedExp++;
|
||||
intMant >>= 1;
|
||||
|
@ -682,7 +563,7 @@ namespace ChocolArm64.Instructions
|
|||
|
||||
if (!state.GetFpcrFlag(Fpcr.Ahp))
|
||||
{
|
||||
if (biasedExp >= (uint)Math.Pow(2d, e) - 1u)
|
||||
if (biasedExp >= (1u << e) - 1u)
|
||||
{
|
||||
resultBits = overflowToInf ? FPInfinity(sign) : FPMaxNormal(sign);
|
||||
|
||||
|
@ -697,7 +578,7 @@ namespace ChocolArm64.Instructions
|
|||
}
|
||||
else
|
||||
{
|
||||
if (biasedExp >= (uint)Math.Pow(2d, e))
|
||||
if (biasedExp >= 1u << e)
|
||||
{
|
||||
resultBits = (ushort)((sign ? 1u : 0u) << 15 | 0x7FFFu);
|
||||
|
||||
|
@ -826,6 +707,94 @@ namespace ChocolArm64.Instructions
|
|||
return result;
|
||||
}
|
||||
|
||||
public static float FPCompareEQ(float value1, float value2, CpuThreadState state)
|
||||
{
|
||||
Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat32.FPCompareEQ: state.Fpcr = 0x{state.Fpcr:X8}");
|
||||
|
||||
value1 = value1.FPUnpack(out FpType type1, out _, out _, state);
|
||||
value2 = value2.FPUnpack(out FpType type2, out _, out _, state);
|
||||
|
||||
float result;
|
||||
|
||||
if (type1 == FpType.SNaN || type1 == FpType.QNaN || type2 == FpType.SNaN || type2 == FpType.QNaN)
|
||||
{
|
||||
result = ZerosOrOnes(false);
|
||||
|
||||
if (type1 == FpType.SNaN || type2 == FpType.SNaN)
|
||||
{
|
||||
FPProcessException(FpExc.InvalidOp, state);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result = ZerosOrOnes(value1 == value2);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static float FPCompareGE(float value1, float value2, CpuThreadState state)
|
||||
{
|
||||
Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat32.FPCompareGE: state.Fpcr = 0x{state.Fpcr:X8}");
|
||||
|
||||
value1 = value1.FPUnpack(out FpType type1, out _, out _, state);
|
||||
value2 = value2.FPUnpack(out FpType type2, out _, out _, state);
|
||||
|
||||
float result;
|
||||
|
||||
if (type1 == FpType.SNaN || type1 == FpType.QNaN || type2 == FpType.SNaN || type2 == FpType.QNaN)
|
||||
{
|
||||
result = ZerosOrOnes(false);
|
||||
|
||||
FPProcessException(FpExc.InvalidOp, state);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = ZerosOrOnes(value1 >= value2);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static float FPCompareGT(float value1, float value2, CpuThreadState state)
|
||||
{
|
||||
Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat32.FPCompareGT: state.Fpcr = 0x{state.Fpcr:X8}");
|
||||
|
||||
value1 = value1.FPUnpack(out FpType type1, out _, out _, state);
|
||||
value2 = value2.FPUnpack(out FpType type2, out _, out _, state);
|
||||
|
||||
float result;
|
||||
|
||||
if (type1 == FpType.SNaN || type1 == FpType.QNaN || type2 == FpType.SNaN || type2 == FpType.QNaN)
|
||||
{
|
||||
result = ZerosOrOnes(false);
|
||||
|
||||
FPProcessException(FpExc.InvalidOp, state);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = ZerosOrOnes(value1 > value2);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float FPCompareLE(float value1, float value2, CpuThreadState state)
|
||||
{
|
||||
Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat32.FPCompareLE: state.Fpcr = 0x{state.Fpcr:X8}");
|
||||
|
||||
return FPCompareGE(value2, value1, state);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float FPCompareLT(float value1, float value2, CpuThreadState state)
|
||||
{
|
||||
Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat32.FPCompareLT: state.Fpcr = 0x{state.Fpcr:X8}");
|
||||
|
||||
return FPCompareGT(value2, value1, state);
|
||||
}
|
||||
|
||||
public static float FPDiv(float value1, float value2, CpuThreadState state)
|
||||
{
|
||||
Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat32.FPDiv: state.Fpcr = 0x{state.Fpcr:X8}");
|
||||
|
@ -1188,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 = (uint)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}");
|
||||
|
@ -1255,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 = (uint)SoftFloat.RecipSqrtEstimateTable[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}");
|
||||
|
@ -1402,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;
|
||||
|
@ -1417,6 +1545,11 @@ namespace ChocolArm64.Instructions
|
|||
return -value;
|
||||
}
|
||||
|
||||
private static float ZerosOrOnes(bool zeros)
|
||||
{
|
||||
return BitConverter.Int32BitsToSingle(!zeros ? 0 : -1);
|
||||
}
|
||||
|
||||
private static float FPUnpack(
|
||||
this float value,
|
||||
out FpType type,
|
||||
|
@ -1658,6 +1791,94 @@ namespace ChocolArm64.Instructions
|
|||
return result;
|
||||
}
|
||||
|
||||
public static double FPCompareEQ(double value1, double value2, CpuThreadState state)
|
||||
{
|
||||
Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat64.FPCompareEQ: state.Fpcr = 0x{state.Fpcr:X8}");
|
||||
|
||||
value1 = value1.FPUnpack(out FpType type1, out _, out _, state);
|
||||
value2 = value2.FPUnpack(out FpType type2, out _, out _, state);
|
||||
|
||||
double result;
|
||||
|
||||
if (type1 == FpType.SNaN || type1 == FpType.QNaN || type2 == FpType.SNaN || type2 == FpType.QNaN)
|
||||
{
|
||||
result = ZerosOrOnes(false);
|
||||
|
||||
if (type1 == FpType.SNaN || type2 == FpType.SNaN)
|
||||
{
|
||||
FPProcessException(FpExc.InvalidOp, state);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result = ZerosOrOnes(value1 == value2);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static double FPCompareGE(double value1, double value2, CpuThreadState state)
|
||||
{
|
||||
Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat64.FPCompareGE: state.Fpcr = 0x{state.Fpcr:X8}");
|
||||
|
||||
value1 = value1.FPUnpack(out FpType type1, out _, out _, state);
|
||||
value2 = value2.FPUnpack(out FpType type2, out _, out _, state);
|
||||
|
||||
double result;
|
||||
|
||||
if (type1 == FpType.SNaN || type1 == FpType.QNaN || type2 == FpType.SNaN || type2 == FpType.QNaN)
|
||||
{
|
||||
result = ZerosOrOnes(false);
|
||||
|
||||
FPProcessException(FpExc.InvalidOp, state);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = ZerosOrOnes(value1 >= value2);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static double FPCompareGT(double value1, double value2, CpuThreadState state)
|
||||
{
|
||||
Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat64.FPCompareGT: state.Fpcr = 0x{state.Fpcr:X8}");
|
||||
|
||||
value1 = value1.FPUnpack(out FpType type1, out _, out _, state);
|
||||
value2 = value2.FPUnpack(out FpType type2, out _, out _, state);
|
||||
|
||||
double result;
|
||||
|
||||
if (type1 == FpType.SNaN || type1 == FpType.QNaN || type2 == FpType.SNaN || type2 == FpType.QNaN)
|
||||
{
|
||||
result = ZerosOrOnes(false);
|
||||
|
||||
FPProcessException(FpExc.InvalidOp, state);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = ZerosOrOnes(value1 > value2);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static double FPCompareLE(double value1, double value2, CpuThreadState state)
|
||||
{
|
||||
Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat64.FPCompareLE: state.Fpcr = 0x{state.Fpcr:X8}");
|
||||
|
||||
return FPCompareGE(value2, value1, state);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static double FPCompareLT(double value1, double value2, CpuThreadState state)
|
||||
{
|
||||
Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat64.FPCompareLT: state.Fpcr = 0x{state.Fpcr:X8}");
|
||||
|
||||
return FPCompareGT(value2, value1, state);
|
||||
}
|
||||
|
||||
public static double FPDiv(double value1, double value2, CpuThreadState state)
|
||||
{
|
||||
Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat64.FPDiv: state.Fpcr = 0x{state.Fpcr:X8}");
|
||||
|
@ -2020,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 = (uint)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}");
|
||||
|
@ -2087,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 = (uint)SoftFloat.RecipSqrtEstimateTable[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}");
|
||||
|
@ -2234,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;
|
||||
|
@ -2249,6 +2629,11 @@ namespace ChocolArm64.Instructions
|
|||
return -value;
|
||||
}
|
||||
|
||||
private static double ZerosOrOnes(bool zeros)
|
||||
{
|
||||
return BitConverter.Int64BitsToDouble(!zeros ? 0L : -1L);
|
||||
}
|
||||
|
||||
private static double FPUnpack(
|
||||
this double value,
|
||||
out FpType type,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue