From 1a328a1159c23591435036a55d4970778a3d76db Mon Sep 17 00:00:00 2001 From: LDj3SNuD <35856442+LDj3SNuD@users.noreply.github.com> Date: Mon, 30 Jul 2018 02:28:26 +0200 Subject: [PATCH] Update AInstEmitSimdHelper.cs --- .../Instruction/AInstEmitSimdHelper.cs | 696 ++++++++++++++++-- 1 file changed, 633 insertions(+), 63 deletions(-) diff --git a/ChocolArm64/Instruction/AInstEmitSimdHelper.cs b/ChocolArm64/Instruction/AInstEmitSimdHelper.cs index 0bd1a62926..67a36e5bf2 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,238 @@ 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; + + Context.EmitLdc_I8(0L); + Context.EmitSttmp(); // Saturated = 0 + + 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); + } + + EmitUpdateFpsrQCFlag(Context); + } + + 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; + + Context.EmitLdc_I8(0L); + Context.EmitSttmp(); // Saturated = 0 + + if (Scalar) + { + EmitVectorZeroLowerTmp(Context); + } + + for (int Index = 0; Index < Elems; Index++) + { + if (Add || Sub) + { + if (Op.Size <= 2) + { + EmitVectorExtract(Context, Op.Rn, Index, Op.Size, Signed); + EmitVectorExtract(Context, ((AOpCodeSimdReg)Op).Rm, Index, Op.Size, Signed); + + Context.Emit(Add ? OpCodes.Add : OpCodes.Sub); + + EmitSatQ(Context, Op.Size, true, Signed); + } + else /* if (Op.Size == 3) */ + { + if (Add) + { + EmitBinarySatQAdd(Context, Index, Signed); + } + else /* if (Sub) */ + { + EmitBinarySatQSub(Context, Index, Signed); + } + } + } + + if (Accumulate) + { + if (Op.Size <= 2) + { + EmitVectorExtract(Context, Op.Rn, Index, Op.Size, !Signed); + EmitVectorExtract(Context, Op.Rd, Index, Op.Size, Signed); + + Context.Emit(OpCodes.Add); + + EmitSatQ(Context, Op.Size, true, Signed); + } + else /* if (Op.Size == 3) */ + { + EmitBinarySatQAccumulate(Context, Index, Signed); + } + } + + EmitVectorInsertTmp(Context, Index, Op.Size); + } + + Context.EmitLdvectmp(); + Context.EmitStvec(Op.Rd); + + if ((Op.RegisterSize == ARegisterSize.SIMD64) || Scalar) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + + EmitUpdateFpsrQCFlag(Context); + } + + [Flags] + public enum SaturatingNarrowFlags + { + Scalar = 1 << 0, + SignedSrc = 1 << 1, + SignedDst = 1 << 2, + + ScalarSxSx = Scalar | SignedSrc | SignedDst, + ScalarSxZx = Scalar | SignedSrc, + ScalarZxZx = Scalar, + + VectorSxSx = SignedSrc | SignedDst, + VectorSxZx = SignedSrc, + 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 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 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(); + Context.EmitSttmp(); // Saturated = 0 + + if (Scalar) + { + EmitVectorZeroLowerTmp(Context); + } if (Part != 0) { @@ -840,47 +1030,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); } @@ -893,12 +1047,428 @@ namespace ChocolArm64.Instruction EmitVectorZeroUpper(Context, Op.Rd); } + EmitUpdateFpsrQCFlag(Context); + } + + // TSrc (from 8bit to 64bit) > TDst (from 8bit to 32bit), signed or unsigned. + public static void EmitSatQ( + AILEmitterCtx Context, + int SizeDst, + bool SignedSrc, + bool SignedDst) + { + if (SizeDst > 2) + { + throw new PlatformNotSupportedException(); + } + + int ESize = 8 << SizeDst; + + long TMaxValue = SignedDst ? (1 << (ESize - 1)) - 1 : (1L << ESize) - 1L; + long TMinValue = SignedDst ? -(1 << (ESize - 1)) : 0L; + + AILLabel LblLe = new AILLabel(); + AILLabel LblGeEnd = new AILLabel(); + + 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(1L); + Context.EmitSttmp(); // Saturated = 1 + + Context.EmitLdc_I8(TMaxValue); + + 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(1L); + Context.EmitSttmp(); // Saturated = 1 + + Context.EmitLdc_I8(TMinValue); + + Context.MarkLabel(LblGeEnd); + } + + // TSrc (from 8bit to 64bit) == TDst (from 8bit to 64bit), signed. + public static void EmitUnarySignedSatQAbsOrNeg( + AILEmitterCtx Context, + int Size) + { + int ESize = 8 << Size; + + long TMaxValue = (1L << (ESize - 1)) - 1; + 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); + + Context.EmitLdc_I8(1L); + Context.EmitSttmp(); // Saturated = 1 + + Context.EmitLdc_I8(TMaxValue); + + Context.MarkLabel(LblFalse); + } + + // TSrcs (64bit) == TDst (64bit), signed or unsigned. + public static void EmitBinarySatQAdd( + AILEmitterCtx Context, + int Index, + bool Signed) + { + AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; + + if (Op.Size < 3) + { + throw new NotSupportedException(); + } + + EmitVectorExtract(Context, Op.Rn, Index, 3, Signed); + EmitVectorExtract(Context, Op.Rm, Index, 3, Signed); + + Context.Emit(OpCodes.Add); + + if (Signed) + { + long TMaxValue = long.MaxValue; + long TMinValue = long.MinValue; + + AILLabel LblGeEnd = new AILLabel(); + AILLabel LblGe = new AILLabel(); + + EmitVectorExtractSx(Context, Op.Rn, Index, 3); + EmitVectorExtractSx(Context, Op.Rm, Index, 3); + Context.Emit(OpCodes.Xor); + Context.Emit(OpCodes.Not); + + EmitVectorExtractSx(Context, Op.Rn, Index, 3); + Context.Emit(OpCodes.Dup); + EmitVectorExtractSx(Context, Op.Rm, Index, 3); + Context.Emit(OpCodes.Add); + Context.Emit(OpCodes.Xor); + + Context.Emit(OpCodes.And); + Context.Emit(OpCodes.Ldc_I4_0); + Context.Emit(OpCodes.Bge_S, LblGeEnd); + + Context.Emit(OpCodes.Pop); + + Context.EmitLdc_I8(1L); + Context.EmitSttmp(); // Saturated = 1 + + EmitVectorExtractSx(Context, Op.Rn, Index, 3); + Context.Emit(OpCodes.Ldc_I4_0); + Context.Emit(OpCodes.Bge_S, LblGe); + + Context.EmitLdc_I8(TMinValue); + + Context.Emit(OpCodes.Br_S, LblGeEnd); + + Context.MarkLabel(LblGe); + + Context.EmitLdc_I8(TMaxValue); + + Context.MarkLabel(LblGeEnd); + } + else + { + long TMaxValue = ~0L; + + AILLabel LblGeEnd = new AILLabel(); + + Context.Emit(OpCodes.Dup); + EmitVectorExtractZx(Context, Op.Rn, Index, 3); + Context.Emit(OpCodes.Bge_Un_S, LblGeEnd); + + Context.Emit(OpCodes.Dup); + EmitVectorExtractZx(Context, Op.Rm, Index, 3); + Context.Emit(OpCodes.Bge_Un_S, LblGeEnd); + + Context.Emit(OpCodes.Pop); + + Context.EmitLdc_I8(1L); + Context.EmitSttmp(); // Saturated = 1 + + Context.EmitLdc_I8(TMaxValue); + + Context.MarkLabel(LblGeEnd); + } + } + + // TSrcs (64bit) == TDst (64bit), signed or unsigned. + public static void EmitBinarySatQSub( + AILEmitterCtx Context, + int Index, + bool Signed) + { + AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; + + if (Op.Size < 3) + { + throw new NotSupportedException(); + } + + EmitVectorExtract(Context, Op.Rn, Index, 3, Signed); + EmitVectorExtract(Context, Op.Rm, Index, 3, Signed); + + Context.Emit(OpCodes.Sub); + + if (Signed) + { + long TMaxValue = long.MaxValue; + long TMinValue = long.MinValue; + + AILLabel LblGeEnd = new AILLabel(); + AILLabel LblGe = new AILLabel(); + + EmitVectorExtractSx(Context, Op.Rn, Index, 3); + EmitVectorExtractSx(Context, Op.Rm, Index, 3); + Context.Emit(OpCodes.Xor); + + EmitVectorExtractSx(Context, Op.Rn, Index, 3); + Context.Emit(OpCodes.Dup); + EmitVectorExtractSx(Context, Op.Rm, Index, 3); + Context.Emit(OpCodes.Sub); + Context.Emit(OpCodes.Xor); + + Context.Emit(OpCodes.And); + Context.Emit(OpCodes.Ldc_I4_0); + Context.Emit(OpCodes.Bge_S, LblGeEnd); + + Context.Emit(OpCodes.Pop); + + Context.EmitLdc_I8(1L); + Context.EmitSttmp(); // Saturated = 1 + + EmitVectorExtractSx(Context, Op.Rn, Index, 3); + Context.Emit(OpCodes.Ldc_I4_0); + Context.Emit(OpCodes.Bge_S, LblGe); + + Context.EmitLdc_I8(TMinValue); + + Context.Emit(OpCodes.Br_S, LblGeEnd); + + Context.MarkLabel(LblGe); + + Context.EmitLdc_I8(TMaxValue); + + Context.MarkLabel(LblGeEnd); + } + else + { + long TMinValue = 0L; + + AILLabel LblTrue = new AILLabel(); + + EmitVectorExtractZx(Context, Op.Rn, Index, 3); + EmitVectorExtractZx(Context, Op.Rm, Index, 3); + Context.Emit(OpCodes.Bge_Un_S, LblTrue); + + Context.Emit(OpCodes.Pop); + + Context.EmitLdc_I8(1L); + Context.EmitSttmp(); // Saturated = 1 + + Context.EmitLdc_I8(TMinValue); + + Context.MarkLabel(LblTrue); + } + } + + // TSrcs (64bit) == TDst (64bit), signed or unsigned. + public static void EmitBinarySatQAccumulate( + AILEmitterCtx Context, + int Index, + bool Signed) + { + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + if (Op.Size < 3) + { + throw new NotSupportedException(); + } + + if (Signed) + { + long TMaxValue = long.MaxValue; + + AILLabel LblGt = new AILLabel(); + AILLabel LblLt = new AILLabel(); + AILLabel LblEnd = new AILLabel(); + + EmitVectorExtractZx(Context, Op.Rn, Index, 3); + Context.EmitLdc_I8(TMaxValue); + Context.Emit(OpCodes.Bgt_Un_S, LblGt); + + // op1 from ulong.MinValue to (ulong)long.MaxValue + // op2 from long.MinValue to long.MaxValue + + EmitVectorExtractSx(Context, Op.Rn, Index, 3); + EmitVectorExtractSx(Context, Op.Rd, Index, 3); + Context.Emit(OpCodes.Add); + + Context.Emit(OpCodes.Dup); + EmitVectorExtractSx(Context, Op.Rd, Index, 3); + Context.Emit(OpCodes.Not); + Context.Emit(OpCodes.And); + Context.Emit(OpCodes.Ldc_I4_0); + Context.Emit(OpCodes.Bge_S, LblEnd); + + Context.Emit(OpCodes.Pop); + + Context.EmitLdc_I8(1L); + Context.EmitSttmp(); // Saturated = 1 + + Context.EmitLdc_I8(TMaxValue); + + Context.Emit(OpCodes.Br_S, LblEnd); + + Context.MarkLabel(LblGt); + + // op1 from (ulong)long.MaxValue + 1UL to ulong.MaxValue + // op2 from (long)ulong.MinValue to long.MaxValue + + EmitVectorExtractSx(Context, Op.Rd, Index, 3); + Context.Emit(OpCodes.Ldc_I4_0); + Context.Emit(OpCodes.Blt_S, LblLt); + + Context.EmitLdc_I8(1L); + Context.EmitSttmp(); // Saturated = 1 + + Context.EmitLdc_I8(TMaxValue); + + Context.Emit(OpCodes.Br_S, LblEnd); + + Context.MarkLabel(LblLt); + + // op1 from (ulong)long.MaxValue + 1UL to ulong.MaxValue + // op2 from long.MinValue to (long)ulong.MinValue - 1L + + EmitVectorExtractZx(Context, Op.Rn, Index, 3); + EmitVectorExtractSx(Context, Op.Rd, Index, 3); + Context.Emit(OpCodes.Add); + + Context.Emit(OpCodes.Dup); + Context.EmitLdc_I8(TMaxValue); + Context.Emit(OpCodes.Ble_Un_S, LblEnd); + + Context.Emit(OpCodes.Pop); + + Context.EmitLdc_I8(1L); + Context.EmitSttmp(); // Saturated = 1 + + Context.EmitLdc_I8(TMaxValue); + + Context.MarkLabel(LblEnd); + } + else + { + long TMaxValue = ~0L; + long TMinValue = 0L; + + AILLabel LblLt = new AILLabel(); + AILLabel LblLe = new AILLabel(); + AILLabel LblEnd = new AILLabel(); + + EmitVectorExtractSx(Context, Op.Rn, Index, 3); + Context.Emit(OpCodes.Ldc_I4_0); + Context.Emit(OpCodes.Blt_S, LblLt); + + // op1 from (long)ulong.MinValue to long.MaxValue + // op2 from ulong.MinValue to ulong.MaxValue + + EmitVectorExtractZx(Context, Op.Rn, Index, 3); + EmitVectorExtractZx(Context, Op.Rd, Index, 3); + Context.Emit(OpCodes.Add); + + Context.Emit(OpCodes.Dup); + EmitVectorExtractZx(Context, Op.Rn, Index, 3); + Context.Emit(OpCodes.Bge_Un_S, LblEnd); + + Context.Emit(OpCodes.Dup); + EmitVectorExtractZx(Context, Op.Rd, Index, 3); + Context.Emit(OpCodes.Bge_Un_S, LblEnd); + + Context.Emit(OpCodes.Pop); + + Context.EmitLdc_I8(1L); + Context.EmitSttmp(); // Saturated = 1 + + Context.EmitLdc_I8(TMaxValue); + + Context.Emit(OpCodes.Br_S, LblEnd); + + Context.MarkLabel(LblLt); + + // op1 from long.MinValue to (long)ulong.MinValue - 1L + // op2 from (ulong)long.MaxValue + 1UL to ulong.MaxValue + + EmitVectorExtractZx(Context, Op.Rd, Index, 3); + Context.EmitLdc_I8(long.MaxValue); // TODO: Spostare a variabile? + Context.Emit(OpCodes.Ble_Un_S, LblLe); + + EmitVectorExtractZx(Context, Op.Rn, Index, 3); + EmitVectorExtractZx(Context, Op.Rd, Index, 3); + Context.Emit(OpCodes.Add); + + Context.Emit(OpCodes.Br_S, LblEnd); + + Context.MarkLabel(LblLe); + + // op1 from long.MinValue to (long)ulong.MinValue - 1L + // op2 from ulong.MinValue to (ulong)long.MaxValue + + EmitVectorExtractSx(Context, Op.Rn, Index, 3); + EmitVectorExtractSx(Context, Op.Rd, Index, 3); + Context.Emit(OpCodes.Add); + + Context.Emit(OpCodes.Dup); + Context.EmitLdc_I8(TMinValue); + Context.Emit(OpCodes.Bge_S, LblEnd); + + Context.Emit(OpCodes.Pop); + + Context.EmitLdc_I8(1L); + Context.EmitSttmp(); // Saturated = 1 + + Context.EmitLdc_I8(TMinValue); + + Context.MarkLabel(LblEnd); + } + } + + public static void EmitUpdateFpsrQCFlag(AILEmitterCtx Context) + { + const int QCFlag = 27; + Context.EmitLdarg(ATranslatedSub.StateArgIdx); + Context.EmitLdarg(ATranslatedSub.StateArgIdx); Context.EmitCallPropGet(typeof(AThreadState), nameof(AThreadState.Fpsr)); - Context.EmitLdtmp(); + + Context.EmitLdtmp(); // Saturated == 0 || Saturated == 1 Context.Emit(OpCodes.Conv_I4); + Context.EmitLsl(QCFlag); + Context.Emit(OpCodes.Or); + Context.EmitCallPropSet(typeof(AThreadState), nameof(AThreadState.Fpsr)); }