aboutsummaryrefslogtreecommitdiff
path: root/ChocolArm64
diff options
context:
space:
mode:
authorLDj3SNuD <35856442+LDj3SNuD@users.noreply.github.com>2018-08-04 21:58:54 +0200
committergdkchan <gab.dark.100@gmail.com>2018-08-04 16:58:54 -0300
commit5f34353dce716bca2e3fc1c7e82be6276b95d61a (patch)
treeebd21bf7aa8a67b3b60321789e404c224c66c8f4 /ChocolArm64
parentfa70629fabbab5074640f55cb70f9d7d82cf91cb (diff)
Add SQADD, UQADD, SQSUB, UQSUB, SUQADD, USQADD, SQABS, SQNEG (Scalar, Vector) instructions; add 24 Tests. Most saturation instructions now on ASoftFallback. (#314)
* Update AOpCodeTable.cs * Update AInstEmitSimdHelper.cs * Update AInstEmitSimdArithmetic.cs * Update Pseudocode.cs * Update Instructions.cs * Update CpuTestSimd.cs * Update CpuTestSimdReg.cs * Update AInstEmitSimdHelper.cs * Update AInstEmitSimdHelper.cs * Update AInstEmitSimdHelper.cs * Update AInstEmitSimdHelper.cs * Update ASoftFallback.cs * Update AInstEmitSimdHelper.cs * Update ASoftFallback.cs * Update AInstEmitSimdHelper.cs * Update CpuTestSimd.cs * Update CpuTestSimdReg.cs * Update ASoftFallback.cs * Update AInstEmitSimdHelper.cs * Opt. (retest).
Diffstat (limited to 'ChocolArm64')
-rw-r--r--ChocolArm64/AOpCodeTable.cs16
-rw-r--r--ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs80
-rw-r--r--ChocolArm64/Instruction/AInstEmitSimdHelper.cs378
-rw-r--r--ChocolArm64/Instruction/ASoftFallback.cs268
4 files changed, 685 insertions, 57 deletions
diff --git a/ChocolArm64/AOpCodeTable.cs b/ChocolArm64/AOpCodeTable.cs
index aa8220e3..aa8b1be6 100644
--- a/ChocolArm64/AOpCodeTable.cs
+++ b/ChocolArm64/AOpCodeTable.cs
@@ -374,7 +374,15 @@ namespace ChocolArm64
SetA64("0x001110<<1xxxxx100000xxxxxxxxxx", AInstEmit.Smlal_V, typeof(AOpCodeSimdReg));
SetA64("0x001110<<1xxxxx101000xxxxxxxxxx", AInstEmit.Smlsl_V, typeof(AOpCodeSimdReg));
SetA64("0x001110<<1xxxxx110000xxxxxxxxxx", AInstEmit.Smull_V, typeof(AOpCodeSimdReg));
+ SetA64("01011110xx100000011110xxxxxxxxxx", AInstEmit.Sqabs_S, typeof(AOpCodeSimd));
+ SetA64("0>001110<<100000011110xxxxxxxxxx", AInstEmit.Sqabs_V, typeof(AOpCodeSimd));
+ SetA64("01011110xx1xxxxx000011xxxxxxxxxx", AInstEmit.Sqadd_S, typeof(AOpCodeSimdReg));
+ SetA64("0>001110<<1xxxxx000011xxxxxxxxxx", AInstEmit.Sqadd_V, typeof(AOpCodeSimdReg));
+ SetA64("01111110xx100000011110xxxxxxxxxx", AInstEmit.Sqneg_S, typeof(AOpCodeSimd));
+ SetA64("0>101110<<100000011110xxxxxxxxxx", AInstEmit.Sqneg_V, typeof(AOpCodeSimd));
SetA64("0x00111100>>>xxx100111xxxxxxxxxx", AInstEmit.Sqrshrn_V, typeof(AOpCodeSimdShImm));
+ SetA64("01011110xx1xxxxx001011xxxxxxxxxx", AInstEmit.Sqsub_S, typeof(AOpCodeSimdReg));
+ SetA64("0>001110<<1xxxxx001011xxxxxxxxxx", AInstEmit.Sqsub_V, typeof(AOpCodeSimdReg));
SetA64("01011110<<100001010010xxxxxxxxxx", AInstEmit.Sqxtn_S, typeof(AOpCodeSimd));
SetA64("0x001110<<100001010010xxxxxxxxxx", AInstEmit.Sqxtn_V, typeof(AOpCodeSimd));
SetA64("01111110<<100001001010xxxxxxxxxx", AInstEmit.Sqxtun_S, typeof(AOpCodeSimd));
@@ -402,6 +410,8 @@ namespace ChocolArm64
SetA64("01111110111xxxxx100001xxxxxxxxxx", AInstEmit.Sub_S, typeof(AOpCodeSimdReg));
SetA64("0>101110<<1xxxxx100001xxxxxxxxxx", AInstEmit.Sub_V, typeof(AOpCodeSimdReg));
SetA64("0x001110<<1xxxxx011000xxxxxxxxxx", AInstEmit.Subhn_V, typeof(AOpCodeSimdReg));
+ SetA64("01011110xx100000001110xxxxxxxxxx", AInstEmit.Suqadd_S, typeof(AOpCodeSimd));
+ SetA64("0>001110<<100000001110xxxxxxxxxx", AInstEmit.Suqadd_V, typeof(AOpCodeSimd));
SetA64("0x001110000xxxxx0xx000xxxxxxxxxx", AInstEmit.Tbl_V, typeof(AOpCodeSimdTbl));
SetA64("0>001110<<0xxxxx001010xxxxxxxxxx", AInstEmit.Trn1_V, typeof(AOpCodeSimdReg));
SetA64("0>001110<<0xxxxx011010xxxxxxxxxx", AInstEmit.Trn2_V, typeof(AOpCodeSimdReg));
@@ -423,6 +433,10 @@ namespace ChocolArm64
SetA64("0x101110<<1xxxxx101011xxxxxxxxxx", AInstEmit.Uminp_V, typeof(AOpCodeSimdReg));
SetA64("0x001110000xxxxx001111xxxxxxxxxx", AInstEmit.Umov_S, typeof(AOpCodeSimdIns));
SetA64("0x101110<<1xxxxx110000xxxxxxxxxx", AInstEmit.Umull_V, typeof(AOpCodeSimdReg));
+ SetA64("01111110xx1xxxxx000011xxxxxxxxxx", AInstEmit.Uqadd_S, typeof(AOpCodeSimdReg));
+ SetA64("0>101110<<1xxxxx000011xxxxxxxxxx", AInstEmit.Uqadd_V, typeof(AOpCodeSimdReg));
+ SetA64("01111110xx1xxxxx001011xxxxxxxxxx", AInstEmit.Uqsub_S, typeof(AOpCodeSimdReg));
+ SetA64("0>101110<<1xxxxx001011xxxxxxxxxx", AInstEmit.Uqsub_V, typeof(AOpCodeSimdReg));
SetA64("01111110<<100001010010xxxxxxxxxx", AInstEmit.Uqxtn_S, typeof(AOpCodeSimd));
SetA64("0x101110<<100001010010xxxxxxxxxx", AInstEmit.Uqxtn_V, typeof(AOpCodeSimd));
SetA64("0>101110<<1xxxxx010001xxxxxxxxxx", AInstEmit.Ushl_V, typeof(AOpCodeSimdReg));
@@ -430,6 +444,8 @@ namespace ChocolArm64
SetA64("0111111101xxxxxx000001xxxxxxxxxx", AInstEmit.Ushr_S, typeof(AOpCodeSimdShImm));
SetA64("0x10111100>>>xxx000001xxxxxxxxxx", AInstEmit.Ushr_V, typeof(AOpCodeSimdShImm));
SetA64("0110111101xxxxxx000001xxxxxxxxxx", AInstEmit.Ushr_V, typeof(AOpCodeSimdShImm));
+ SetA64("01111110xx100000001110xxxxxxxxxx", AInstEmit.Usqadd_S, typeof(AOpCodeSimd));
+ SetA64("0>101110<<100000001110xxxxxxxxxx", AInstEmit.Usqadd_V, typeof(AOpCodeSimd));
SetA64("0x10111100>>>xxx000101xxxxxxxxxx", AInstEmit.Usra_V, typeof(AOpCodeSimdShImm));
SetA64("0110111101xxxxxx000101xxxxxxxxxx", AInstEmit.Usra_V, typeof(AOpCodeSimdShImm));
SetA64("0x101110<<1xxxxx001100xxxxxxxxxx", AInstEmit.Usubw_V, typeof(AOpCodeSimdReg));
diff --git a/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs b/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs
index 8e741861..6772fe83 100644
--- a/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs
+++ b/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs
@@ -1052,6 +1052,46 @@ namespace ChocolArm64.Instruction
EmitVectorWidenRnRmBinaryOpSx(Context, () => Context.Emit(OpCodes.Mul));
}
+ public static void Sqabs_S(AILEmitterCtx Context)
+ {
+ EmitScalarSaturatingUnaryOpSx(Context, () => EmitAbs(Context));
+ }
+
+ public static void Sqabs_V(AILEmitterCtx Context)
+ {
+ EmitVectorSaturatingUnaryOpSx(Context, () => EmitAbs(Context));
+ }
+
+ public static void Sqadd_S(AILEmitterCtx Context)
+ {
+ EmitScalarSaturatingBinaryOpSx(Context, SaturatingFlags.Add);
+ }
+
+ public static void Sqadd_V(AILEmitterCtx Context)
+ {
+ EmitVectorSaturatingBinaryOpSx(Context, SaturatingFlags.Add);
+ }
+
+ public static void Sqneg_S(AILEmitterCtx Context)
+ {
+ EmitScalarSaturatingUnaryOpSx(Context, () => Context.Emit(OpCodes.Neg));
+ }
+
+ public static void Sqneg_V(AILEmitterCtx Context)
+ {
+ EmitVectorSaturatingUnaryOpSx(Context, () => Context.Emit(OpCodes.Neg));
+ }
+
+ public static void Sqsub_S(AILEmitterCtx Context)
+ {
+ EmitScalarSaturatingBinaryOpSx(Context, SaturatingFlags.Sub);
+ }
+
+ public static void Sqsub_V(AILEmitterCtx Context)
+ {
+ EmitVectorSaturatingBinaryOpSx(Context, SaturatingFlags.Sub);
+ }
+
public static void Sqxtn_S(AILEmitterCtx Context)
{
EmitScalarSaturatingNarrowOpSxSx(Context, () => { });
@@ -1099,6 +1139,16 @@ namespace ChocolArm64.Instruction
EmitHighNarrow(Context, () => Context.Emit(OpCodes.Sub), Round: false);
}
+ public static void Suqadd_S(AILEmitterCtx Context)
+ {
+ EmitScalarSaturatingBinaryOpSx(Context, SaturatingFlags.Accumulate);
+ }
+
+ public static void Suqadd_V(AILEmitterCtx Context)
+ {
+ EmitVectorSaturatingBinaryOpSx(Context, SaturatingFlags.Accumulate);
+ }
+
public static void Uaba_V(AILEmitterCtx Context)
{
EmitVectorTernaryOpZx(Context, () =>
@@ -1221,6 +1271,26 @@ namespace ChocolArm64.Instruction
EmitVectorWidenRnRmBinaryOpZx(Context, () => Context.Emit(OpCodes.Mul));
}
+ public static void Uqadd_S(AILEmitterCtx Context)
+ {
+ EmitScalarSaturatingBinaryOpZx(Context, SaturatingFlags.Add);
+ }
+
+ public static void Uqadd_V(AILEmitterCtx Context)
+ {
+ EmitVectorSaturatingBinaryOpZx(Context, SaturatingFlags.Add);
+ }
+
+ public static void Uqsub_S(AILEmitterCtx Context)
+ {
+ EmitScalarSaturatingBinaryOpZx(Context, SaturatingFlags.Sub);
+ }
+
+ public static void Uqsub_V(AILEmitterCtx Context)
+ {
+ EmitVectorSaturatingBinaryOpZx(Context, SaturatingFlags.Sub);
+ }
+
public static void Uqxtn_S(AILEmitterCtx Context)
{
EmitScalarSaturatingNarrowOpZxZx(Context, () => { });
@@ -1231,6 +1301,16 @@ namespace ChocolArm64.Instruction
EmitVectorSaturatingNarrowOpZxZx(Context, () => { });
}
+ public static void Usqadd_S(AILEmitterCtx Context)
+ {
+ EmitScalarSaturatingBinaryOpZx(Context, SaturatingFlags.Accumulate);
+ }
+
+ public static void Usqadd_V(AILEmitterCtx Context)
+ {
+ EmitVectorSaturatingBinaryOpZx(Context, SaturatingFlags.Accumulate);
+ }
+
public static void Usubw_V(AILEmitterCtx Context)
{
EmitVectorWidenRmBinaryOpZx(Context, () => Context.Emit(OpCodes.Sub));
diff --git a/ChocolArm64/Instruction/AInstEmitSimdHelper.cs b/ChocolArm64/Instruction/AInstEmitSimdHelper.cs
index 0bd1a629..161c44ea 100644
--- a/ChocolArm64/Instruction/AInstEmitSimdHelper.cs
+++ b/ChocolArm64/Instruction/AInstEmitSimdHelper.cs
@@ -336,17 +336,21 @@ namespace ChocolArm64.Instruction
{
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
- if (Opers.HasFlag(OperFlags.Rd))
+ bool Rd = (Opers & OperFlags.Rd) != 0;
+ bool Rn = (Opers & OperFlags.Rn) != 0;
+ bool Rm = (Opers & OperFlags.Rm) != 0;
+
+ if (Rd)
{
EmitVectorExtract(Context, Op.Rd, 0, Op.Size, Signed);
}
- if (Opers.HasFlag(OperFlags.Rn))
+ if (Rn)
{
EmitVectorExtract(Context, Op.Rn, 0, Op.Size, Signed);
}
- if (Opers.HasFlag(OperFlags.Rm))
+ if (Rm)
{
EmitVectorExtract(Context, ((AOpCodeSimdReg)Op).Rm, 0, Op.Size, Signed);
}
@@ -377,17 +381,21 @@ namespace ChocolArm64.Instruction
int SizeF = Op.Size & 1;
- if (Opers.HasFlag(OperFlags.Ra))
+ bool Ra = (Opers & OperFlags.Ra) != 0;
+ bool Rn = (Opers & OperFlags.Rn) != 0;
+ bool Rm = (Opers & OperFlags.Rm) != 0;
+
+ if (Ra)
{
EmitVectorExtractF(Context, ((AOpCodeSimdReg)Op).Ra, 0, SizeF);
}
- if (Opers.HasFlag(OperFlags.Rn))
+ if (Rn)
{
EmitVectorExtractF(Context, Op.Rn, 0, SizeF);
}
- if (Opers.HasFlag(OperFlags.Rm))
+ if (Rm)
{
EmitVectorExtractF(Context, ((AOpCodeSimdReg)Op).Rm, 0, SizeF);
}
@@ -769,7 +777,7 @@ namespace ChocolArm64.Instruction
Emit();
EmitVectorInsertTmp(Context, Pairs + Index, Op.Size);
- EmitVectorInsertTmp(Context, Index, Op.Size);
+ EmitVectorInsertTmp(Context, Index, Op.Size);
}
Context.EmitLdvectmp();
@@ -781,56 +789,241 @@ namespace ChocolArm64.Instruction
}
}
+ [Flags]
+ public enum SaturatingFlags
+ {
+ Scalar = 1 << 0,
+ Signed = 1 << 1,
+
+ Add = 1 << 2,
+ Sub = 1 << 3,
+
+ Accumulate = 1 << 4,
+
+ ScalarSx = Scalar | Signed,
+ ScalarZx = Scalar,
+
+ VectorSx = Signed,
+ VectorZx = 0,
+ }
+
+ public static void EmitScalarSaturatingUnaryOpSx(AILEmitterCtx Context, Action Emit)
+ {
+ EmitSaturatingUnaryOpSx(Context, Emit, SaturatingFlags.ScalarSx);
+ }
+
+ public static void EmitVectorSaturatingUnaryOpSx(AILEmitterCtx Context, Action Emit)
+ {
+ EmitSaturatingUnaryOpSx(Context, Emit, SaturatingFlags.VectorSx);
+ }
+
+ public static void EmitSaturatingUnaryOpSx(AILEmitterCtx Context, Action Emit, SaturatingFlags Flags)
+ {
+ AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
+
+ bool Scalar = (Flags & SaturatingFlags.Scalar) != 0;
+
+ int Bytes = Op.GetBitsCount() >> 3;
+ int Elems = !Scalar ? Bytes >> Op.Size : 1;
+
+ if (Scalar)
+ {
+ EmitVectorZeroLowerTmp(Context);
+ }
+
+ for (int Index = 0; Index < Elems; Index++)
+ {
+ EmitVectorExtractSx(Context, Op.Rn, Index, Op.Size);
+
+ Emit();
+
+ EmitUnarySignedSatQAbsOrNeg(Context, Op.Size);
+
+ EmitVectorInsertTmp(Context, Index, Op.Size);
+ }
+
+ Context.EmitLdvectmp();
+ Context.EmitStvec(Op.Rd);
+
+ if ((Op.RegisterSize == ARegisterSize.SIMD64) || Scalar)
+ {
+ EmitVectorZeroUpper(Context, Op.Rd);
+ }
+ }
+
+ public static void EmitScalarSaturatingBinaryOpSx(AILEmitterCtx Context, SaturatingFlags Flags)
+ {
+ EmitSaturatingBinaryOp(Context, SaturatingFlags.ScalarSx | Flags);
+ }
+
+ public static void EmitScalarSaturatingBinaryOpZx(AILEmitterCtx Context, SaturatingFlags Flags)
+ {
+ EmitSaturatingBinaryOp(Context, SaturatingFlags.ScalarZx | Flags);
+ }
+
+ public static void EmitVectorSaturatingBinaryOpSx(AILEmitterCtx Context, SaturatingFlags Flags)
+ {
+ EmitSaturatingBinaryOp(Context, SaturatingFlags.VectorSx | Flags);
+ }
+
+ public static void EmitVectorSaturatingBinaryOpZx(AILEmitterCtx Context, SaturatingFlags Flags)
+ {
+ EmitSaturatingBinaryOp(Context, SaturatingFlags.VectorZx | Flags);
+ }
+
+ public static void EmitSaturatingBinaryOp(AILEmitterCtx Context, SaturatingFlags Flags)
+ {
+ AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
+
+ bool Scalar = (Flags & SaturatingFlags.Scalar) != 0;
+ bool Signed = (Flags & SaturatingFlags.Signed) != 0;
+
+ bool Add = (Flags & SaturatingFlags.Add) != 0;
+ bool Sub = (Flags & SaturatingFlags.Sub) != 0;
+
+ bool Accumulate = (Flags & SaturatingFlags.Accumulate) != 0;
+
+ int Bytes = Op.GetBitsCount() >> 3;
+ int Elems = !Scalar ? Bytes >> Op.Size : 1;
+
+ if (Scalar)
+ {
+ EmitVectorZeroLowerTmp(Context);
+ }
+
+ if (Add || Sub)
+ {
+ for (int Index = 0; Index < Elems; Index++)
+ {
+ EmitVectorExtract(Context, Op.Rn, Index, Op.Size, Signed);
+ EmitVectorExtract(Context, ((AOpCodeSimdReg)Op).Rm, Index, Op.Size, Signed);
+
+ if (Op.Size <= 2)
+ {
+ Context.Emit(Add ? OpCodes.Add : OpCodes.Sub);
+
+ EmitSatQ(Context, Op.Size, true, Signed);
+ }
+ else /* if (Op.Size == 3) */
+ {
+ if (Add)
+ {
+ EmitBinarySatQAdd(Context, Signed);
+ }
+ else /* if (Sub) */
+ {
+ EmitBinarySatQSub(Context, Signed);
+ }
+ }
+
+ EmitVectorInsertTmp(Context, Index, Op.Size);
+ }
+ }
+ else if (Accumulate)
+ {
+ for (int Index = 0; Index < Elems; Index++)
+ {
+ EmitVectorExtract(Context, Op.Rn, Index, Op.Size, !Signed);
+ EmitVectorExtract(Context, Op.Rd, Index, Op.Size, Signed);
+
+ if (Op.Size <= 2)
+ {
+ Context.Emit(OpCodes.Add);
+
+ EmitSatQ(Context, Op.Size, true, Signed);
+ }
+ else /* if (Op.Size == 3) */
+ {
+ EmitBinarySatQAccumulate(Context, Signed);
+ }
+
+ EmitVectorInsertTmp(Context, Index, Op.Size);
+ }
+ }
+
+ Context.EmitLdvectmp();
+ Context.EmitStvec(Op.Rd);
+
+ if ((Op.RegisterSize == ARegisterSize.SIMD64) || Scalar)
+ {
+ EmitVectorZeroUpper(Context, Op.Rd);
+ }
+ }
+
+ [Flags]
+ public enum SaturatingNarrowFlags
+ {
+ Scalar = 1 << 0,
+ SignedSrc = 1 << 1,
+ SignedDst = 1 << 2,
+
+ ScalarSxSx = Scalar | SignedSrc | SignedDst,
+ ScalarSxZx = Scalar | SignedSrc,
+ ScalarZxSx = Scalar | SignedDst,
+ ScalarZxZx = Scalar,
+
+ VectorSxSx = SignedSrc | SignedDst,
+ VectorSxZx = SignedSrc,
+ VectorZxSx = SignedDst,
+ VectorZxZx = 0
+ }
+
public static void EmitScalarSaturatingNarrowOpSxSx(AILEmitterCtx Context, Action Emit)
{
- EmitSaturatingNarrowOp(Context, Emit, true, true, true);
+ EmitSaturatingNarrowOp(Context, Emit, SaturatingNarrowFlags.ScalarSxSx);
}
public static void EmitScalarSaturatingNarrowOpSxZx(AILEmitterCtx Context, Action Emit)
{
- EmitSaturatingNarrowOp(Context, Emit, true, false, true);
+ EmitSaturatingNarrowOp(Context, Emit, SaturatingNarrowFlags.ScalarSxZx);
+ }
+
+ public static void EmitScalarSaturatingNarrowOpZxSx(AILEmitterCtx Context, Action Emit)
+ {
+ EmitSaturatingNarrowOp(Context, Emit, SaturatingNarrowFlags.ScalarZxSx);
}
public static void EmitScalarSaturatingNarrowOpZxZx(AILEmitterCtx Context, Action Emit)
{
- EmitSaturatingNarrowOp(Context, Emit, false, false, true);
+ EmitSaturatingNarrowOp(Context, Emit, SaturatingNarrowFlags.ScalarZxZx);
}
public static void EmitVectorSaturatingNarrowOpSxSx(AILEmitterCtx Context, Action Emit)
{
- EmitSaturatingNarrowOp(Context, Emit, true, true, false);
+ EmitSaturatingNarrowOp(Context, Emit, SaturatingNarrowFlags.VectorSxSx);
}
public static void EmitVectorSaturatingNarrowOpSxZx(AILEmitterCtx Context, Action Emit)
{
- EmitSaturatingNarrowOp(Context, Emit, true, false, false);
+ EmitSaturatingNarrowOp(Context, Emit, SaturatingNarrowFlags.VectorSxZx);
+ }
+
+ public static void EmitVectorSaturatingNarrowOpZxSx(AILEmitterCtx Context, Action Emit)
+ {
+ EmitSaturatingNarrowOp(Context, Emit, SaturatingNarrowFlags.VectorZxSx);
}
public static void EmitVectorSaturatingNarrowOpZxZx(AILEmitterCtx Context, Action Emit)
{
- EmitSaturatingNarrowOp(Context, Emit, false, false, false);
+ EmitSaturatingNarrowOp(Context, Emit, SaturatingNarrowFlags.VectorZxZx);
}
- public static void EmitSaturatingNarrowOp(
- AILEmitterCtx Context,
- Action Emit,
- bool SignedSrc,
- bool SignedDst,
- bool Scalar)
+ public static void EmitSaturatingNarrowOp(AILEmitterCtx Context, Action Emit, SaturatingNarrowFlags Flags)
{
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
- int Elems = !Scalar ? 8 >> Op.Size : 1;
+ bool Scalar = (Flags & SaturatingNarrowFlags.Scalar) != 0;
+ bool SignedSrc = (Flags & SaturatingNarrowFlags.SignedSrc) != 0;
+ bool SignedDst = (Flags & SaturatingNarrowFlags.SignedDst) != 0;
- int ESize = 8 << Op.Size;
+ int Elems = !Scalar ? 8 >> Op.Size : 1;
int Part = !Scalar && (Op.RegisterSize == ARegisterSize.SIMD128) ? Elems : 0;
- long TMaxValue = SignedDst ? (1 << (ESize - 1)) - 1 : (long)(~0UL >> (64 - ESize));
- long TMinValue = SignedDst ? -((1 << (ESize - 1))) : 0;
-
- Context.EmitLdc_I8(0L);
- Context.EmitSttmp();
+ if (Scalar)
+ {
+ EmitVectorZeroLowerTmp(Context);
+ }
if (Part != 0)
{
@@ -840,65 +1033,136 @@ namespace ChocolArm64.Instruction
for (int Index = 0; Index < Elems; Index++)
{
- AILLabel LblLe = new AILLabel();
- AILLabel LblGeEnd = new AILLabel();
-
EmitVectorExtract(Context, Op.Rn, Index, Op.Size + 1, SignedSrc);
Emit();
- Context.Emit(OpCodes.Dup);
+ EmitSatQ(Context, Op.Size, SignedSrc, SignedDst);
+
+ EmitVectorInsertTmp(Context, Part + Index, Op.Size);
+ }
+
+ Context.EmitLdvectmp();
+ Context.EmitStvec(Op.Rd);
+
+ if (Part == 0)
+ {
+ EmitVectorZeroUpper(Context, Op.Rd);
+ }
+ }
+
+ // TSrc (16bit, 32bit, 64bit; signed, unsigned) > TDst (8bit, 16bit, 32bit; signed, unsigned).
+ public static void EmitSatQ(
+ AILEmitterCtx Context,
+ int SizeDst,
+ bool SignedSrc,
+ bool SignedDst)
+ {
+ if (SizeDst > 2)
+ {
+ throw new ArgumentOutOfRangeException(nameof(SizeDst));
+ }
- Context.EmitLdc_I8(TMaxValue);
+ Context.EmitLdc_I4(SizeDst);
+ Context.EmitLdarg(ATranslatedSub.StateArgIdx);
- Context.Emit(SignedSrc ? OpCodes.Ble_S : OpCodes.Ble_Un_S, LblLe);
+ if (SignedSrc)
+ {
+ ASoftFallback.EmitCall(Context, SignedDst
+ ? nameof(ASoftFallback.SignedSrcSignedDstSatQ)
+ : nameof(ASoftFallback.SignedSrcUnsignedDstSatQ));
+ }
+ else
+ {
+ ASoftFallback.EmitCall(Context, SignedDst
+ ? nameof(ASoftFallback.UnsignedSrcSignedDstSatQ)
+ : nameof(ASoftFallback.UnsignedSrcUnsignedDstSatQ));
+ }
+ }
- Context.Emit(OpCodes.Pop);
+ // TSrc (8bit, 16bit, 32bit, 64bit) == TDst (8bit, 16bit, 32bit, 64bit); signed.
+ public static void EmitUnarySignedSatQAbsOrNeg(AILEmitterCtx Context, int Size)
+ {
+ int ESize = 8 << Size;
- Context.EmitLdc_I8(TMaxValue);
- Context.EmitLdc_I8(0x8000000L);
- Context.EmitSttmp();
+ long TMaxValue = (1L << (ESize - 1)) - 1L;
+ long TMinValue = -(1L << (ESize - 1));
- Context.Emit(OpCodes.Br_S, LblGeEnd);
+ AILLabel LblFalse = new AILLabel();
- Context.MarkLabel(LblLe);
+ Context.Emit(OpCodes.Dup);
+ Context.Emit(OpCodes.Neg);
+ Context.EmitLdc_I8(TMinValue);
+ Context.Emit(OpCodes.Ceq);
+ Context.Emit(OpCodes.Brfalse_S, LblFalse);
- Context.Emit(OpCodes.Dup);
+ Context.Emit(OpCodes.Pop);
- Context.EmitLdc_I8(TMinValue);
+ EmitSetFpsrQCFlag(Context);
- Context.Emit(SignedSrc ? OpCodes.Bge_S : OpCodes.Bge_Un_S, LblGeEnd);
+ Context.EmitLdc_I8(TMaxValue);
- Context.Emit(OpCodes.Pop);
+ Context.MarkLabel(LblFalse);
+ }
- Context.EmitLdc_I8(TMinValue);
- Context.EmitLdc_I8(0x8000000L);
- Context.EmitSttmp();
+ // TSrcs (64bit) == TDst (64bit); signed, unsigned.
+ public static void EmitBinarySatQAdd(AILEmitterCtx Context, bool Signed)
+ {
+ if (((AOpCodeSimdReg)Context.CurrOp).Size < 3)
+ {
+ throw new InvalidOperationException();
+ }
- Context.MarkLabel(LblGeEnd);
+ Context.EmitLdarg(ATranslatedSub.StateArgIdx);
- if (Scalar)
- {
- EmitVectorZeroLowerTmp(Context);
- }
+ ASoftFallback.EmitCall(Context, Signed
+ ? nameof(ASoftFallback.BinarySignedSatQAdd)
+ : nameof(ASoftFallback.BinaryUnsignedSatQAdd));
+ }
- EmitVectorInsertTmp(Context, Part + Index, Op.Size);
+ // TSrcs (64bit) == TDst (64bit); signed, unsigned.
+ public static void EmitBinarySatQSub(AILEmitterCtx Context, bool Signed)
+ {
+ if (((AOpCodeSimdReg)Context.CurrOp).Size < 3)
+ {
+ throw new InvalidOperationException();
}
- Context.EmitLdvectmp();
- Context.EmitStvec(Op.Rd);
+ Context.EmitLdarg(ATranslatedSub.StateArgIdx);
- if (Part == 0)
+ ASoftFallback.EmitCall(Context, Signed
+ ? nameof(ASoftFallback.BinarySignedSatQSub)
+ : nameof(ASoftFallback.BinaryUnsignedSatQSub));
+ }
+
+ // TSrcs (64bit) == TDst (64bit); signed, unsigned.
+ public static void EmitBinarySatQAccumulate(AILEmitterCtx Context, bool Signed)
+ {
+ if (((AOpCodeSimd)Context.CurrOp).Size < 3)
{
- EmitVectorZeroUpper(Context, Op.Rd);
+ throw new InvalidOperationException();
}
Context.EmitLdarg(ATranslatedSub.StateArgIdx);
+
+ ASoftFallback.EmitCall(Context, Signed
+ ? nameof(ASoftFallback.BinarySignedSatQAcc)
+ : nameof(ASoftFallback.BinaryUnsignedSatQAcc));
+ }
+
+ public static void EmitSetFpsrQCFlag(AILEmitterCtx Context)
+ {
+ const int QCFlagBit = 27;
+
+ Context.EmitLdarg(ATranslatedSub.StateArgIdx);
+
Context.EmitLdarg(ATranslatedSub.StateArgIdx);
Context.EmitCallPropGet(typeof(AThreadState), nameof(AThreadState.Fpsr));
- Context.EmitLdtmp();
- Context.Emit(OpCodes.Conv_I4);
+
+ Context.EmitLdc_I4(1 << QCFlagBit);
+
Context.Emit(OpCodes.Or);
+
Context.EmitCallPropSet(typeof(AThreadState), nameof(AThreadState.Fpsr));
}
diff --git a/ChocolArm64/Instruction/ASoftFallback.cs b/ChocolArm64/Instruction/ASoftFallback.cs
index ae3994b0..a4d12dd6 100644
--- a/ChocolArm64/Instruction/ASoftFallback.cs
+++ b/ChocolArm64/Instruction/ASoftFallback.cs
@@ -1,3 +1,4 @@
+using ChocolArm64.State;
using ChocolArm64.Translation;
using System;
@@ -10,6 +11,273 @@ namespace ChocolArm64.Instruction
Context.EmitCall(typeof(ASoftFallback), MthdName);
}
+ public static long BinarySignedSatQAdd(long op1, long op2, AThreadState State)
+ {
+ long Add = op1 + op2;
+
+ if ((~(op1 ^ op2) & (op1 ^ Add)) < 0L)
+ {
+ SetFpsrQCFlag(State);
+
+ if (op1 < 0L)
+ {
+ return long.MinValue;
+ }
+ else
+ {
+ return long.MaxValue;
+ }
+ }
+ else
+ {
+ return Add;
+ }
+ }
+
+ public static ulong BinaryUnsignedSatQAdd(ulong op1, ulong op2, AThreadState State)
+ {
+ ulong Add = op1 + op2;
+
+ if ((Add < op1) && (Add < op2))
+ {
+ SetFpsrQCFlag(State);
+
+ return ulong.MaxValue;
+ }
+ else
+ {
+ return Add;
+ }
+ }
+
+ public static long BinarySignedSatQSub(long op1, long op2, AThreadState State)
+ {
+ long Sub = op1 - op2;
+
+ if (((op1 ^ op2) & (op1 ^ Sub)) < 0L)
+ {
+ SetFpsrQCFlag(State);
+
+ if (op1 < 0L)
+ {
+ return long.MinValue;
+ }
+ else
+ {
+ return long.MaxValue;
+ }
+ }
+ else
+ {
+ return Sub;
+ }
+ }
+
+ public static ulong BinaryUnsignedSatQSub(ulong op1, ulong op2, AThreadState State)
+ {
+ ulong Sub = op1 - op2;
+
+ if (op1 < op2)
+ {
+ SetFpsrQCFlag(State);
+
+ return ulong.MinValue;
+ }
+ else
+ {
+ return Sub;
+ }
+ }
+
+ public static long BinarySignedSatQAcc(ulong op1, long op2, AThreadState State)
+ {
+ if (op1 <= (ulong)long.MaxValue)
+ {
+ // op1 from ulong.MinValue to (ulong)long.MaxValue
+ // op2 from long.MinValue to long.MaxValue
+
+ long Add = (long)op1 + op2;
+
+ if ((~op2 & Add) < 0L)
+ {
+ SetFpsrQCFlag(State);
+
+ return long.MaxValue;
+ }
+ else
+ {
+ return Add;
+ }
+ }
+ else if (op2 >= 0L)
+ {
+ // op1 from (ulong)long.MaxValue + 1UL to ulong.MaxValue
+ // op2 from (long)ulong.MinValue to long.MaxValue
+
+ SetFpsrQCFlag(State);
+
+ return long.MaxValue;
+ }
+ else
+ {
+ // op1 from (ulong)long.MaxValue + 1UL to ulong.MaxValue
+ // op2 from long.MinValue to (long)ulong.MinValue - 1L
+
+ ulong Add = op1 + (ulong)op2;
+
+ if (Add > (ulong)long.MaxValue)
+ {
+ SetFpsrQCFlag(State);
+
+ return long.MaxValue;
+ }
+ else
+ {
+ return (long)Add;
+ }
+ }
+ }
+
+ public static ulong BinaryUnsignedSatQAcc(long op1, ulong op2, AThreadState State)
+ {
+ if (op1 >= 0L)
+ {
+ // op1 from (long)ulong.MinValue to long.MaxValue
+ // op2 from ulong.MinValue to ulong.MaxValue
+
+ ulong Add = (ulong)op1 + op2;
+
+ if ((Add < (ulong)op1) && (Add < op2))
+ {
+ SetFpsrQCFlag(State);
+
+ return ulong.MaxValue;
+ }
+ else
+ {
+ return Add;
+ }
+ }
+ else if (op2 > (ulong)long.MaxValue)
+ {
+ // op1 from long.MinValue to (long)ulong.MinValue - 1L
+ // op2 from (ulong)long.MaxValue + 1UL to ulong.MaxValue
+
+ return (ulong)op1 + op2;
+ }
+ else
+ {
+ // op1 from long.MinValue to (long)ulong.MinValue - 1L
+ // op2 from ulong.MinValue to (ulong)long.MaxValue
+
+ long Add = op1 + (long)op2;
+
+ if (Add < (long)ulong.MinValue)
+ {
+ SetFpsrQCFlag(State);
+
+ return ulong.MinValue;
+ }
+ else
+ {
+ return (ulong)Add;
+ }
+ }
+ }
+
+ public static long SignedSrcSignedDstSatQ(long op, int Size, AThreadState State)
+ {
+ int ESize = 8 << Size;
+
+ long TMaxValue = (1L << (ESize - 1)) - 1L;
+ long TMinValue = -(1L << (ESize - 1));
+
+ if (op > TMaxValue)
+ {
+ SetFpsrQCFlag(State);
+
+ return TMaxValue;
+ }
+ else if (op < TMinValue)
+ {
+ SetFpsrQCFlag(State);
+
+ return TMinValue;
+ }
+ else
+ {
+ return op;
+ }
+ }
+
+ public static ulong SignedSrcUnsignedDstSatQ(long op, int Size, AThreadState State)
+ {
+ int ESize = 8 << Size;
+
+ ulong TMaxValue = (1UL << ESize) - 1UL;
+ ulong TMinValue = 0UL;
+
+ if (op > (long)TMaxValue)
+ {
+ SetFpsrQCFlag(State);
+
+ return TMaxValue;
+ }
+ else if (op < (long)TMinValue)
+ {
+ SetFpsrQCFlag(State);
+
+ return TMinValue;
+ }
+ else
+ {
+ return (ulong)op;
+ }
+ }
+
+ public static long UnsignedSrcSignedDstSatQ(ulong op, int Size, AThreadState State)
+ {
+ int ESize = 8 << Size;
+
+ long TMaxValue = (1L << (ESize - 1)) - 1L;
+
+ if (op > (ulong)TMaxValue)
+ {
+ SetFpsrQCFlag(State);
+
+ return TMaxValue;
+ }
+ else
+ {
+ return (long)op;
+ }
+ }
+
+ public static ulong UnsignedSrcUnsignedDstSatQ(ulong op, int Size, AThreadState State)
+ {
+ int ESize = 8 << Size;
+
+ ulong TMaxValue = (1UL << ESize) - 1UL;
+
+ if (op > TMaxValue)
+ {
+ SetFpsrQCFlag(State);
+
+ return TMaxValue;
+ }
+ else
+ {
+ return op;
+ }
+ }
+
+ private static void SetFpsrQCFlag(AThreadState State)
+ {
+ const int QCFlagBit = 27;
+
+ State.Fpsr |= 1 << QCFlagBit;
+ }
+
public static ulong CountLeadingSigns(ulong Value, int Size)
{
Value ^= Value >> 1;