From f736be2efbbae40304eb7e1bcf05e3e16cc55013 Mon Sep 17 00:00:00 2001 From: LDj3SNuD <35856442+LDj3SNuD@users.noreply.github.com> Date: Sat, 4 Aug 2018 21:58:54 +0200 Subject: [PATCH] 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). --- AOpCodeTable.cs | 16 + Instruction/AInstEmitSimdArithmetic.cs | 80 +++++ Instruction/AInstEmitSimdHelper.cs | 394 +++++++++++++++++++++---- Instruction/ASoftFallback.cs | 268 +++++++++++++++++ 4 files changed, 693 insertions(+), 65 deletions(-) diff --git a/AOpCodeTable.cs b/AOpCodeTable.cs index aa8220e..aa8b1be 100644 --- a/AOpCodeTable.cs +++ b/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/Instruction/AInstEmitSimdArithmetic.cs b/Instruction/AInstEmitSimdArithmetic.cs index 8e74186..6772fe8 100644 --- a/Instruction/AInstEmitSimdArithmetic.cs +++ b/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/Instruction/AInstEmitSimdHelper.cs b/Instruction/AInstEmitSimdHelper.cs index 0bd1a62..161c44e 100644 --- a/Instruction/AInstEmitSimdHelper.cs +++ b/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,47 +1033,11 @@ 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); - - Context.EmitLdc_I8(TMaxValue); - - Context.Emit(SignedSrc ? OpCodes.Ble_S : OpCodes.Ble_Un_S, LblLe); - - Context.Emit(OpCodes.Pop); - - Context.EmitLdc_I8(TMaxValue); - Context.EmitLdc_I8(0x8000000L); - Context.EmitSttmp(); - - Context.Emit(OpCodes.Br_S, LblGeEnd); - - Context.MarkLabel(LblLe); - - Context.Emit(OpCodes.Dup); - - Context.EmitLdc_I8(TMinValue); - - Context.Emit(SignedSrc ? OpCodes.Bge_S : OpCodes.Bge_Un_S, LblGeEnd); - - Context.Emit(OpCodes.Pop); - - Context.EmitLdc_I8(TMinValue); - Context.EmitLdc_I8(0x8000000L); - Context.EmitSttmp(); - - Context.MarkLabel(LblGeEnd); - - if (Scalar) - { - EmitVectorZeroLowerTmp(Context); - } + EmitSatQ(Context, Op.Size, SignedSrc, SignedDst); EmitVectorInsertTmp(Context, Part + Index, Op.Size); } @@ -892,13 +1049,120 @@ namespace ChocolArm64.Instruction { 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_I4(SizeDst); + Context.EmitLdarg(ATranslatedSub.StateArgIdx); + + if (SignedSrc) + { + ASoftFallback.EmitCall(Context, SignedDst + ? nameof(ASoftFallback.SignedSrcSignedDstSatQ) + : nameof(ASoftFallback.SignedSrcUnsignedDstSatQ)); + } + else + { + ASoftFallback.EmitCall(Context, SignedDst + ? nameof(ASoftFallback.UnsignedSrcSignedDstSatQ) + : nameof(ASoftFallback.UnsignedSrcUnsignedDstSatQ)); + } + } + + // TSrc (8bit, 16bit, 32bit, 64bit) == TDst (8bit, 16bit, 32bit, 64bit); signed. + public static void EmitUnarySignedSatQAbsOrNeg(AILEmitterCtx Context, int Size) + { + int ESize = 8 << Size; + + long TMaxValue = (1L << (ESize - 1)) - 1L; + long TMinValue = -(1L << (ESize - 1)); + + AILLabel LblFalse = new AILLabel(); + + 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.Pop); + + EmitSetFpsrQCFlag(Context); + + Context.EmitLdc_I8(TMaxValue); + + Context.MarkLabel(LblFalse); + } + + // TSrcs (64bit) == TDst (64bit); signed, unsigned. + public static void EmitBinarySatQAdd(AILEmitterCtx Context, bool Signed) + { + if (((AOpCodeSimdReg)Context.CurrOp).Size < 3) + { + throw new InvalidOperationException(); + } Context.EmitLdarg(ATranslatedSub.StateArgIdx); + + ASoftFallback.EmitCall(Context, Signed + ? nameof(ASoftFallback.BinarySignedSatQAdd) + : nameof(ASoftFallback.BinaryUnsignedSatQAdd)); + } + + // TSrcs (64bit) == TDst (64bit); signed, unsigned. + public static void EmitBinarySatQSub(AILEmitterCtx Context, bool Signed) + { + if (((AOpCodeSimdReg)Context.CurrOp).Size < 3) + { + throw new InvalidOperationException(); + } + + Context.EmitLdarg(ATranslatedSub.StateArgIdx); + + 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) + { + 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/Instruction/ASoftFallback.cs b/Instruction/ASoftFallback.cs index ae3994b..a4d12dd 100644 --- a/Instruction/ASoftFallback.cs +++ b/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;