diff options
| author | emmauss <emmausssss@gmail.com> | 2018-02-20 22:09:23 +0200 |
|---|---|---|
| committer | gdkchan <gab.dark.100@gmail.com> | 2018-02-20 17:09:23 -0300 |
| commit | 62b827f474f0aa2152dd339fcc7cf31084e16a0b (patch) | |
| tree | 0e5c55b341aee4db0ccb841a084f253ec5e05657 /ChocolArm64/Instruction | |
| parent | cb665bb715834526d73c9469d16114b287faaecd (diff) | |
Split main project into core,graphics and chocolarm4 subproject (#29)
Diffstat (limited to 'ChocolArm64/Instruction')
24 files changed, 4774 insertions, 0 deletions
diff --git a/ChocolArm64/Instruction/AInst.cs b/ChocolArm64/Instruction/AInst.cs new file mode 100644 index 00000000..cab597d6 --- /dev/null +++ b/ChocolArm64/Instruction/AInst.cs @@ -0,0 +1,18 @@ +using System; + +namespace ChocolArm64.Instruction +{ + struct AInst + { + public AInstEmitter Emitter { get; private set; } + public Type Type { get; private set; } + + public static AInst Undefined => new AInst(AInstEmit.Und, null); + + public AInst(AInstEmitter Emitter, Type Type) + { + this.Emitter = Emitter; + this.Type = Type; + } + } +}
\ No newline at end of file diff --git a/ChocolArm64/Instruction/AInstEmitAlu.cs b/ChocolArm64/Instruction/AInstEmitAlu.cs new file mode 100644 index 00000000..72903f5b --- /dev/null +++ b/ChocolArm64/Instruction/AInstEmitAlu.cs @@ -0,0 +1,364 @@ +using ChocolArm64.Decoder; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System; +using System.Reflection; +using System.Reflection.Emit; + +using static ChocolArm64.Instruction.AInstEmitAluHelper; + +namespace ChocolArm64.Instruction +{ + static partial class AInstEmit + { + public static void Adc(AILEmitterCtx Context) + { + EmitDataLoadOpers(Context); + + Context.Emit(OpCodes.Add); + + Context.EmitLdflg((int)APState.CBit); + + Type[] MthdTypes = new Type[] { typeof(bool) }; + + MethodInfo MthdInfo = typeof(Convert).GetMethod(nameof(Convert.ToInt32), MthdTypes); + + Context.EmitCall(MthdInfo); + + if (Context.CurrOp.RegisterSize != ARegisterSize.Int32) + { + Context.Emit(OpCodes.Conv_I8); + } + + Context.Emit(OpCodes.Add); + + EmitDataStore(Context); + } + + public static void Add(AILEmitterCtx Context) => EmitDataOp(Context, OpCodes.Add); + + public static void Adds(AILEmitterCtx Context) + { + Context.TryOptMarkCondWithoutCmp(); + + EmitDataLoadOpers(Context); + + Context.Emit(OpCodes.Add); + + Context.EmitZNFlagCheck(); + + EmitAddsCCheck(Context); + EmitAddsVCheck(Context); + EmitDataStoreS(Context); + } + + public static void And(AILEmitterCtx Context) => EmitDataOp(Context, OpCodes.And); + + public static void Ands(AILEmitterCtx Context) + { + EmitDataLoadOpers(Context); + + Context.Emit(OpCodes.And); + + EmitZeroCVFlags(Context); + + Context.EmitZNFlagCheck(); + + EmitDataStoreS(Context); + } + + public static void Asrv(AILEmitterCtx Context) => EmitDataOpShift(Context, OpCodes.Shr); + + public static void Bic(AILEmitterCtx Context) => EmitBic(Context, false); + public static void Bics(AILEmitterCtx Context) => EmitBic(Context, true); + + private static void EmitBic(AILEmitterCtx Context, bool SetFlags) + { + EmitDataLoadOpers(Context); + + Context.Emit(OpCodes.Not); + Context.Emit(OpCodes.And); + + if (SetFlags) + { + EmitZeroCVFlags(Context); + + Context.EmitZNFlagCheck(); + } + + EmitDataStore(Context, SetFlags); + } + + public static void Clz(AILEmitterCtx Context) + { + AOpCodeAlu Op = (AOpCodeAlu)Context.CurrOp; + + Context.EmitLdintzr(Op.Rn); + + if (Op.RegisterSize == ARegisterSize.Int32) + { + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.CountLeadingZeros32)); + } + else + { + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.CountLeadingZeros64)); + } + + Context.EmitStintzr(Op.Rd); + } + + public static void Eon(AILEmitterCtx Context) + { + EmitDataLoadOpers(Context); + + Context.Emit(OpCodes.Not); + Context.Emit(OpCodes.Xor); + + EmitDataStore(Context); + } + + public static void Eor(AILEmitterCtx Context) => EmitDataOp(Context, OpCodes.Xor); + + public static void Extr(AILEmitterCtx Context) + { + //TODO: Ensure that the Shift is valid for the Is64Bits. + AOpCodeAluRs Op = (AOpCodeAluRs)Context.CurrOp; + + Context.EmitLdintzr(Op.Rm); + + if (Op.Shift > 0) + { + Context.EmitLdc_I4(Op.Shift); + + Context.Emit(OpCodes.Shr_Un); + + Context.EmitLdintzr(Op.Rn); + Context.EmitLdc_I4(Op.GetBitsCount() - Op.Shift); + + Context.Emit(OpCodes.Shl); + Context.Emit(OpCodes.Or); + } + + EmitDataStore(Context); + } + + public static void Lslv(AILEmitterCtx Context) => EmitDataOpShift(Context, OpCodes.Shl); + public static void Lsrv(AILEmitterCtx Context) => EmitDataOpShift(Context, OpCodes.Shr_Un); + + public static void Sbc(AILEmitterCtx Context) + { + EmitDataLoadOpers(Context); + + Context.Emit(OpCodes.Sub); + + Context.EmitLdflg((int)APState.CBit); + + Type[] MthdTypes = new Type[] { typeof(bool) }; + + MethodInfo MthdInfo = typeof(Convert).GetMethod(nameof(Convert.ToInt32), MthdTypes); + + Context.EmitCall(MthdInfo); + + Context.EmitLdc_I4(1); + + Context.Emit(OpCodes.Xor); + + if (Context.CurrOp.RegisterSize != ARegisterSize.Int32) + { + Context.Emit(OpCodes.Conv_I8); + } + + Context.Emit(OpCodes.Sub); + + EmitDataStore(Context); + } + + public static void Sub(AILEmitterCtx Context) => EmitDataOp(Context, OpCodes.Sub); + + public static void Subs(AILEmitterCtx Context) + { + Context.TryOptMarkCondWithoutCmp(); + + EmitDataLoadOpers(Context); + + Context.Emit(OpCodes.Sub); + + Context.EmitZNFlagCheck(); + + EmitSubsCCheck(Context); + EmitSubsVCheck(Context); + EmitDataStoreS(Context); + } + + public static void Orn(AILEmitterCtx Context) + { + EmitDataLoadOpers(Context); + + Context.Emit(OpCodes.Not); + Context.Emit(OpCodes.Or); + + EmitDataStore(Context); + } + + public static void Orr(AILEmitterCtx Context) => EmitDataOp(Context, OpCodes.Or); + + public static void Rbit(AILEmitterCtx Context) => EmitFallback32_64(Context, + nameof(ASoftFallback.ReverseBits32), + nameof(ASoftFallback.ReverseBits64)); + + public static void Rev16(AILEmitterCtx Context) => EmitFallback32_64(Context, + nameof(ASoftFallback.ReverseBytes16_32), + nameof(ASoftFallback.ReverseBytes16_64)); + + public static void Rev32(AILEmitterCtx Context) => EmitFallback32_64(Context, + nameof(ASoftFallback.ReverseBytes32_32), + nameof(ASoftFallback.ReverseBytes32_64)); + + public static void EmitFallback32_64(AILEmitterCtx Context, string Name32, string Name64) + { + AOpCodeAlu Op = (AOpCodeAlu)Context.CurrOp; + + Context.EmitLdintzr(Op.Rn); + + if (Op.RegisterSize == ARegisterSize.Int32) + { + ASoftFallback.EmitCall(Context, Name32); + } + else + { + ASoftFallback.EmitCall(Context, Name64); + } + + Context.EmitStintzr(Op.Rd); + } + + public static void Rev64(AILEmitterCtx Context) + { + AOpCodeAlu Op = (AOpCodeAlu)Context.CurrOp; + + Context.EmitLdintzr(Op.Rn); + + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.ReverseBytes64)); + + Context.EmitStintzr(Op.Rd); + } + + public static void Rorv(AILEmitterCtx Context) + { + EmitDataLoadRn(Context); + EmitDataLoadShift(Context); + + Context.Emit(OpCodes.Shr_Un); + + EmitDataLoadRn(Context); + + Context.EmitLdc_I4(Context.CurrOp.GetBitsCount()); + + EmitDataLoadShift(Context); + + Context.Emit(OpCodes.Sub); + Context.Emit(OpCodes.Shl); + Context.Emit(OpCodes.Or); + + EmitDataStore(Context); + } + + public static void Sdiv(AILEmitterCtx Context) => EmitDiv(Context, OpCodes.Div); + public static void Udiv(AILEmitterCtx Context) => EmitDiv(Context, OpCodes.Div_Un); + + private static void EmitDiv(AILEmitterCtx Context, OpCode ILOp) + { + //If Rm == 0, Rd = 0 (division by zero). + Context.EmitLdc_I(0); + + EmitDataLoadRm(Context); + + Context.EmitLdc_I(0); + + AILLabel BadDiv = new AILLabel(); + + Context.Emit(OpCodes.Beq_S, BadDiv); + Context.Emit(OpCodes.Pop); + + if (ILOp == OpCodes.Div) + { + //If Rn == INT_MIN && Rm == -1, Rd = INT_MIN (overflow). + long IntMin = 1L << (Context.CurrOp.GetBitsCount() - 1); + + Context.EmitLdc_I(IntMin); + + EmitDataLoadRn(Context); + + Context.EmitLdc_I(IntMin); + + Context.Emit(OpCodes.Ceq); + + EmitDataLoadRm(Context); + + Context.EmitLdc_I(-1); + + Context.Emit(OpCodes.Ceq); + Context.Emit(OpCodes.And); + Context.Emit(OpCodes.Brtrue_S, BadDiv); + Context.Emit(OpCodes.Pop); + } + + EmitDataLoadRn(Context); + EmitDataLoadRm(Context); + + Context.Emit(ILOp); + + Context.MarkLabel(BadDiv); + + EmitDataStore(Context); + } + + private static void EmitDataOp(AILEmitterCtx Context, OpCode ILOp) + { + EmitDataLoadOpers(Context); + + Context.Emit(ILOp); + + EmitDataStore(Context); + } + + private static void EmitDataOpShift(AILEmitterCtx Context, OpCode ILOp) + { + EmitDataLoadRn(Context); + EmitDataLoadShift(Context); + + Context.Emit(ILOp); + + EmitDataStore(Context); + } + + private static void EmitDataLoadShift(AILEmitterCtx Context) + { + EmitDataLoadRm(Context); + + Context.EmitLdc_I(Context.CurrOp.GetBitsCount() - 1); + + Context.Emit(OpCodes.And); + + //Note: Only 32-bits shift values are valid, so when the value is 64-bits + //we need to cast it to a 32-bits integer. This is fine because we + //AND the value and only keep the lower 5 or 6 bits anyway -- it + //could very well fit on a byte. + if (Context.CurrOp.RegisterSize != ARegisterSize.Int32) + { + Context.Emit(OpCodes.Conv_I4); + } + } + + private static void EmitZeroCVFlags(AILEmitterCtx Context) + { + Context.EmitLdc_I4(0); + + Context.EmitStflg((int)APState.VBit); + + Context.EmitLdc_I4(0); + + Context.EmitStflg((int)APState.CBit); + } + } +}
\ No newline at end of file diff --git a/ChocolArm64/Instruction/AInstEmitAluHelper.cs b/ChocolArm64/Instruction/AInstEmitAluHelper.cs new file mode 100644 index 00000000..e848742d --- /dev/null +++ b/ChocolArm64/Instruction/AInstEmitAluHelper.cs @@ -0,0 +1,168 @@ +using ChocolArm64.Decoder; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System.Reflection.Emit; + +namespace ChocolArm64.Instruction +{ + static class AInstEmitAluHelper + { + public static void EmitAddsCCheck(AILEmitterCtx Context) + { + //C = Rd < Rn + Context.Emit(OpCodes.Dup); + + EmitDataLoadRn(Context); + + Context.Emit(OpCodes.Clt_Un); + + Context.EmitStflg((int)APState.CBit); + } + + public static void EmitAddsVCheck(AILEmitterCtx Context) + { + //V = (Rd ^ Rn) & ~(Rn ^ Rm) < 0 + Context.Emit(OpCodes.Dup); + + EmitDataLoadRn(Context); + + Context.Emit(OpCodes.Xor); + + EmitDataLoadOpers(Context); + + Context.Emit(OpCodes.Xor); + Context.Emit(OpCodes.Not); + Context.Emit(OpCodes.And); + + Context.EmitLdc_I(0); + + Context.Emit(OpCodes.Clt); + + Context.EmitStflg((int)APState.VBit); + } + + public static void EmitSubsCCheck(AILEmitterCtx Context) + { + //C = Rn == Rm || Rn > Rm = !(Rn < Rm) + EmitDataLoadOpers(Context); + + Context.Emit(OpCodes.Clt_Un); + + Context.EmitLdc_I4(1); + + Context.Emit(OpCodes.Xor); + + Context.EmitStflg((int)APState.CBit); + } + + public static void EmitSubsVCheck(AILEmitterCtx Context) + { + //V = (Rd ^ Rn) & (Rn ^ Rm) < 0 + Context.Emit(OpCodes.Dup); + + EmitDataLoadRn(Context); + + Context.Emit(OpCodes.Xor); + + EmitDataLoadOpers(Context); + + Context.Emit(OpCodes.Xor); + Context.Emit(OpCodes.And); + + Context.EmitLdc_I(0); + + Context.Emit(OpCodes.Clt); + + Context.EmitStflg((int)APState.VBit); + } + + public static void EmitDataLoadRm(AILEmitterCtx Context) + { + Context.EmitLdintzr(((IAOpCodeAluRs)Context.CurrOp).Rm); + } + + public static void EmitDataLoadOpers(AILEmitterCtx Context) + { + EmitDataLoadRn(Context); + EmitDataLoadOper2(Context); + } + + public static void EmitDataLoadRn(AILEmitterCtx Context) + { + IAOpCodeAlu Op = (IAOpCodeAlu)Context.CurrOp; + + if (Op.DataOp == ADataOp.Logical || Op is IAOpCodeAluRs) + { + Context.EmitLdintzr(Op.Rn); + } + else + { + Context.EmitLdint(Op.Rn); + } + } + + public static void EmitDataLoadOper2(AILEmitterCtx Context) + { + switch (Context.CurrOp) + { + case IAOpCodeAluImm Op: + Context.EmitLdc_I(Op.Imm); + break; + + case IAOpCodeAluRs Op: + Context.EmitLdintzr(Op.Rm); + + switch (Op.ShiftType) + { + case AShiftType.Lsl: Context.EmitLsl(Op.Shift); break; + case AShiftType.Lsr: Context.EmitLsr(Op.Shift); break; + case AShiftType.Asr: Context.EmitAsr(Op.Shift); break; + case AShiftType.Ror: Context.EmitRor(Op.Shift); break; + } + break; + + case IAOpCodeAluRx Op: + Context.EmitLdintzr(Op.Rm); + Context.EmitCast(Op.IntType); + Context.EmitLsl(Op.Shift); + break; + } + } + + public static void EmitDataStore(AILEmitterCtx Context) => EmitDataStore(Context, false); + public static void EmitDataStoreS(AILEmitterCtx Context) => EmitDataStore(Context, true); + + public static void EmitDataStore(AILEmitterCtx Context, bool SetFlags) + { + IAOpCodeAlu Op = (IAOpCodeAlu)Context.CurrOp; + + if (SetFlags || Op is IAOpCodeAluRs) + { + Context.EmitStintzr(Op.Rd); + } + else + { + Context.EmitStint(Op.Rd); + } + } + + public static void EmitSetNZCV(AILEmitterCtx Context, int NZCV) + { + Context.EmitLdc_I4((NZCV >> 0) & 1); + + Context.EmitStflg((int)APState.VBit); + + Context.EmitLdc_I4((NZCV >> 1) & 1); + + Context.EmitStflg((int)APState.CBit); + + Context.EmitLdc_I4((NZCV >> 2) & 1); + + Context.EmitStflg((int)APState.ZBit); + + Context.EmitLdc_I4((NZCV >> 3) & 1); + + Context.EmitStflg((int)APState.NBit); + } + } +}
\ No newline at end of file diff --git a/ChocolArm64/Instruction/AInstEmitBfm.cs b/ChocolArm64/Instruction/AInstEmitBfm.cs new file mode 100644 index 00000000..823af738 --- /dev/null +++ b/ChocolArm64/Instruction/AInstEmitBfm.cs @@ -0,0 +1,217 @@ +using ChocolArm64.Decoder; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System.Reflection.Emit; + +namespace ChocolArm64.Instruction +{ + static partial class AInstEmit + { + public static void Bfm(AILEmitterCtx Context) + { + AOpCodeBfm Op = (AOpCodeBfm)Context.CurrOp; + + EmitBfmLoadRn(Context); + + Context.EmitLdintzr(Op.Rd); + Context.EmitLdc_I(~Op.WMask & Op.TMask); + + Context.Emit(OpCodes.And); + Context.Emit(OpCodes.Or); + + Context.EmitLdintzr(Op.Rd); + Context.EmitLdc_I(~Op.TMask); + + Context.Emit(OpCodes.And); + Context.Emit(OpCodes.Or); + + Context.EmitStintzr(Op.Rd); + } + + public static void Sbfm(AILEmitterCtx Context) + { + AOpCodeBfm Op = (AOpCodeBfm)Context.CurrOp; + + int BitsCount = Op.GetBitsCount(); + + if (Op.Pos + 1 == BitsCount) + { + EmitSbfmShift(Context); + } + else if (Op.Pos < Op.Shift) + { + EmitSbfiz(Context); + } + else if (Op.Pos == 7 && Op.Shift == 0) + { + EmitSbfmCast(Context, OpCodes.Conv_I1); + } + else if (Op.Pos == 15 && Op.Shift == 0) + { + EmitSbfmCast(Context, OpCodes.Conv_I2); + } + else if (Op.Pos == 31 && Op.Shift == 0) + { + EmitSbfmCast(Context, OpCodes.Conv_I4); + } + else if (Op.Shift == 0) + { + Context.EmitLdintzr(Op.Rn); + + Context.EmitLsl(BitsCount - 1 - Op.Pos); + Context.EmitAsr(BitsCount - 1); + + Context.EmitStintzr(Op.Rd); + } + else + { + EmitBfmLoadRn(Context); + + Context.EmitLdintzr(Op.Rn); + + Context.EmitLsl(BitsCount - 1 - Op.Pos); + Context.EmitAsr(BitsCount - 1); + + Context.EmitLdc_I(~Op.TMask); + + Context.Emit(OpCodes.And); + Context.Emit(OpCodes.Or); + + Context.EmitStintzr(Op.Rd); + } + } + + public static void Ubfm(AILEmitterCtx Context) + { + AOpCodeBfm Op = (AOpCodeBfm)Context.CurrOp; + + if (Op.Pos + 1 == Op.GetBitsCount()) + { + EmitUbfmShift(Context); + } + else if (Op.Pos < Op.Shift) + { + EmitUbfiz(Context); + } + else if (Op.Pos + 1 == Op.Shift) + { + EmitBfmLsl(Context); + } + else if (Op.Pos == 7 && Op.Shift == 0) + { + EmitUbfmCast(Context, OpCodes.Conv_U1); + } + else if (Op.Pos == 15 && Op.Shift == 0) + { + EmitUbfmCast(Context, OpCodes.Conv_U2); + } + else + { + EmitBfmLoadRn(Context); + + Context.EmitStintzr(Op.Rd); + } + } + + private static void EmitSbfiz(AILEmitterCtx Context) => EmitBfiz(Context, true); + private static void EmitUbfiz(AILEmitterCtx Context) => EmitBfiz(Context, false); + + private static void EmitBfiz(AILEmitterCtx Context, bool Signed) + { + AOpCodeBfm Op = (AOpCodeBfm)Context.CurrOp; + + int Width = Op.Pos + 1; + + Context.EmitLdintzr(Op.Rn); + + Context.EmitLsl(Op.GetBitsCount() - Width); + + if (Signed) + { + Context.EmitAsr(Op.Shift - Width); + } + else + { + Context.EmitLsr(Op.Shift - Width); + } + + Context.EmitStintzr(Op.Rd); + } + + private static void EmitSbfmCast(AILEmitterCtx Context, OpCode ILOp) + { + EmitBfmCast(Context, ILOp, true); + } + + private static void EmitUbfmCast(AILEmitterCtx Context, OpCode ILOp) + { + EmitBfmCast(Context, ILOp, false); + } + + private static void EmitBfmCast(AILEmitterCtx Context, OpCode ILOp, bool Signed) + { + AOpCodeBfm Op = (AOpCodeBfm)Context.CurrOp; + + Context.EmitLdintzr(Op.Rn); + + Context.Emit(ILOp); + + if (Op.RegisterSize != ARegisterSize.Int32) + { + Context.Emit(Signed + ? OpCodes.Conv_I8 + : OpCodes.Conv_U8); + } + + Context.EmitStintzr(Op.Rd); + } + + private static void EmitSbfmShift(AILEmitterCtx Context) + { + EmitBfmShift(Context, true); + } + + private static void EmitUbfmShift(AILEmitterCtx Context) + { + EmitBfmShift(Context, false); + } + + private static void EmitBfmShift(AILEmitterCtx Context, bool Signed) + { + AOpCodeBfm Op = (AOpCodeBfm)Context.CurrOp; + + Context.EmitLdintzr(Op.Rn); + Context.EmitLdc_I4(Op.Shift); + + Context.Emit(Signed + ? OpCodes.Shr + : OpCodes.Shr_Un); + + Context.EmitStintzr(Op.Rd); + } + + private static void EmitBfmLsl(AILEmitterCtx Context) + { + AOpCodeBfm Op = (AOpCodeBfm)Context.CurrOp; + + Context.EmitLdintzr(Op.Rn); + + Context.EmitLsl(Op.GetBitsCount() - Op.Shift); + + Context.EmitStintzr(Op.Rd); + } + + private static void EmitBfmLoadRn(AILEmitterCtx Context) + { + AOpCodeBfm Op = (AOpCodeBfm)Context.CurrOp; + + Context.EmitLdintzr(Op.Rn); + + Context.EmitRor(Op.Shift); + + Context.EmitLdc_I(Op.WMask & Op.TMask); + + Context.Emit(OpCodes.And); + } + } +}
\ No newline at end of file diff --git a/ChocolArm64/Instruction/AInstEmitCcmp.cs b/ChocolArm64/Instruction/AInstEmitCcmp.cs new file mode 100644 index 00000000..7153a6a0 --- /dev/null +++ b/ChocolArm64/Instruction/AInstEmitCcmp.cs @@ -0,0 +1,81 @@ +using ChocolArm64.Decoder; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System; +using System.Reflection.Emit; + +using static ChocolArm64.Instruction.AInstEmitAluHelper; + +namespace ChocolArm64.Instruction +{ + static partial class AInstEmit + { + private enum CcmpOp + { + Cmp, + Cmn + } + + public static void Ccmn(AILEmitterCtx Context) => EmitCcmp(Context, CcmpOp.Cmn); + public static void Ccmp(AILEmitterCtx Context) => EmitCcmp(Context, CcmpOp.Cmp); + + private static void EmitCcmp(AILEmitterCtx Context, CcmpOp CmpOp) + { + AOpCodeCcmp Op = (AOpCodeCcmp)Context.CurrOp; + + AILLabel LblTrue = new AILLabel(); + AILLabel LblEnd = new AILLabel(); + + Context.EmitCondBranch(LblTrue, Op.Cond); + + Context.EmitLdc_I4((Op.NZCV >> 0) & 1); + + Context.EmitStflg((int)APState.VBit); + + Context.EmitLdc_I4((Op.NZCV >> 1) & 1); + + Context.EmitStflg((int)APState.CBit); + + Context.EmitLdc_I4((Op.NZCV >> 2) & 1); + + Context.EmitStflg((int)APState.ZBit); + + Context.EmitLdc_I4((Op.NZCV >> 3) & 1); + + Context.EmitStflg((int)APState.NBit); + + Context.Emit(OpCodes.Br_S, LblEnd); + + Context.MarkLabel(LblTrue); + + EmitDataLoadOpers(Context); + + if (CmpOp == CcmpOp.Cmp) + { + Context.Emit(OpCodes.Sub); + + Context.EmitZNFlagCheck(); + + EmitSubsCCheck(Context); + EmitSubsVCheck(Context); + } + else if (CmpOp == CcmpOp.Cmn) + { + Context.Emit(OpCodes.Add); + + Context.EmitZNFlagCheck(); + + EmitAddsCCheck(Context); + EmitAddsVCheck(Context); + } + else + { + throw new ArgumentException(nameof(CmpOp)); + } + + Context.Emit(OpCodes.Pop); + + Context.MarkLabel(LblEnd); + } + } +}
\ No newline at end of file diff --git a/ChocolArm64/Instruction/AInstEmitCsel.cs b/ChocolArm64/Instruction/AInstEmitCsel.cs new file mode 100644 index 00000000..33080980 --- /dev/null +++ b/ChocolArm64/Instruction/AInstEmitCsel.cs @@ -0,0 +1,59 @@ +using ChocolArm64.Decoder; +using ChocolArm64.Translation; +using System.Reflection.Emit; + +namespace ChocolArm64.Instruction +{ + static partial class AInstEmit + { + private enum CselOperation + { + None, + Increment, + Invert, + Negate + } + + public static void Csel(AILEmitterCtx Context) => EmitCsel(Context, CselOperation.None); + public static void Csinc(AILEmitterCtx Context) => EmitCsel(Context, CselOperation.Increment); + public static void Csinv(AILEmitterCtx Context) => EmitCsel(Context, CselOperation.Invert); + public static void Csneg(AILEmitterCtx Context) => EmitCsel(Context, CselOperation.Negate); + + private static void EmitCsel(AILEmitterCtx Context, CselOperation CselOp) + { + AOpCodeCsel Op = (AOpCodeCsel)Context.CurrOp; + + AILLabel LblTrue = new AILLabel(); + AILLabel LblEnd = new AILLabel(); + + Context.EmitCondBranch(LblTrue, Op.Cond); + Context.EmitLdintzr(Op.Rm); + + if (CselOp == CselOperation.Increment) + { + Context.EmitLdc_I(1); + + Context.Emit(OpCodes.Add); + } + else if (CselOp == CselOperation.Invert) + { + Context.Emit(OpCodes.Not); + } + else if (CselOp == CselOperation.Negate) + { + Context.Emit(OpCodes.Neg); + } + + Context.EmitStintzr(Op.Rd); + + Context.Emit(OpCodes.Br_S, LblEnd); + + Context.MarkLabel(LblTrue); + + Context.EmitLdintzr(Op.Rn); + Context.EmitStintzr(Op.Rd); + + Context.MarkLabel(LblEnd); + } + } +}
\ No newline at end of file diff --git a/ChocolArm64/Instruction/AInstEmitException.cs b/ChocolArm64/Instruction/AInstEmitException.cs new file mode 100644 index 00000000..209ba56f --- /dev/null +++ b/ChocolArm64/Instruction/AInstEmitException.cs @@ -0,0 +1,65 @@ +using ChocolArm64.Decoder; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System.Reflection; + +namespace ChocolArm64.Instruction +{ + static partial class AInstEmit + { + private const BindingFlags Binding = BindingFlags.NonPublic | BindingFlags.Instance; + + public static void Brk(AILEmitterCtx Context) + { + EmitExceptionCall(Context, nameof(AThreadState.OnBreak)); + } + + public static void Svc(AILEmitterCtx Context) + { + EmitExceptionCall(Context, nameof(AThreadState.OnSvcCall)); + } + + private static void EmitExceptionCall(AILEmitterCtx Context, string MthdName) + { + AOpCodeException Op = (AOpCodeException)Context.CurrOp; + + Context.EmitStoreState(); + + Context.EmitLdarg(ATranslatedSub.StateArgIdx); + + Context.EmitLdc_I4(Op.Id); + + MethodInfo MthdInfo = typeof(AThreadState).GetMethod(MthdName, Binding); + + Context.EmitCall(MthdInfo); + + if (Context.CurrBlock.Next != null) + { + Context.EmitLoadState(Context.CurrBlock.Next); + } + } + + public static void Und(AILEmitterCtx Context) + { + AOpCode Op = Context.CurrOp; + + Context.EmitStoreState(); + + Context.EmitLdarg(ATranslatedSub.StateArgIdx); + + Context.EmitLdc_I8(Op.Position); + Context.EmitLdc_I4(Op.RawOpCode); + + string MthdName = nameof(AThreadState.OnUndefined); + + MethodInfo MthdInfo = typeof(AThreadState).GetMethod(MthdName, Binding); + + Context.EmitCall(MthdInfo); + + if (Context.CurrBlock.Next != null) + { + Context.EmitLoadState(Context.CurrBlock.Next); + } + } + } +}
\ No newline at end of file diff --git a/ChocolArm64/Instruction/AInstEmitFlow.cs b/ChocolArm64/Instruction/AInstEmitFlow.cs new file mode 100644 index 00000000..be68aa6c --- /dev/null +++ b/ChocolArm64/Instruction/AInstEmitFlow.cs @@ -0,0 +1,124 @@ +using ChocolArm64.Decoder; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System.Reflection.Emit; + +namespace ChocolArm64.Instruction +{ + static partial class AInstEmit + { + public static void B(AILEmitterCtx Context) + { + AOpCodeBImmAl Op = (AOpCodeBImmAl)Context.CurrOp; + + Context.Emit(OpCodes.Br, Context.GetLabel(Op.Imm)); + } + + public static void B_Cond(AILEmitterCtx Context) + { + AOpCodeBImmCond Op = (AOpCodeBImmCond)Context.CurrOp; + + Context.EmitCondBranch(Context.GetLabel(Op.Imm), Op.Cond); + } + + public static void Bl(AILEmitterCtx Context) + { + AOpCodeBImmAl Op = (AOpCodeBImmAl)Context.CurrOp; + + Context.EmitLdc_I(Op.Position + 4); + Context.EmitStint(AThreadState.LRIndex); + Context.EmitStoreState(); + + if (Context.TryOptEmitSubroutineCall()) + { + //Note: the return value of the called method will be placed + //at the Stack, the return value is always a Int64 with the + //return address of the function. We check if the address is + //correct, if it isn't we keep returning until we reach the dispatcher. + Context.Emit(OpCodes.Dup); + + Context.EmitLdc_I8(Op.Position + 4); + + AILLabel LblContinue = new AILLabel(); + + Context.Emit(OpCodes.Beq_S, LblContinue); + Context.Emit(OpCodes.Ret); + + Context.MarkLabel(LblContinue); + + Context.Emit(OpCodes.Pop); + + if (Context.CurrBlock.Next != null) + { + Context.EmitLoadState(Context.CurrBlock.Next); + } + } + else + { + Context.EmitLdc_I8(Op.Imm); + + Context.Emit(OpCodes.Ret); + } + } + + public static void Blr(AILEmitterCtx Context) + { + AOpCodeBReg Op = (AOpCodeBReg)Context.CurrOp; + + Context.EmitLdc_I(Op.Position + 4); + Context.EmitStint(AThreadState.LRIndex); + Context.EmitStoreState(); + Context.EmitLdintzr(Op.Rn); + + Context.Emit(OpCodes.Ret); + } + + public static void Br(AILEmitterCtx Context) + { + AOpCodeBReg Op = (AOpCodeBReg)Context.CurrOp; + + Context.EmitStoreState(); + Context.EmitLdintzr(Op.Rn); + + Context.Emit(OpCodes.Ret); + } + + public static void Cbnz(AILEmitterCtx Context) => EmitCb(Context, OpCodes.Bne_Un); + public static void Cbz(AILEmitterCtx Context) => EmitCb(Context, OpCodes.Beq); + + private static void EmitCb(AILEmitterCtx Context, OpCode ILOp) + { + AOpCodeBImmCmp Op = (AOpCodeBImmCmp)Context.CurrOp; + + Context.EmitLdintzr(Op.Rt); + Context.EmitLdc_I(0); + + Context.Emit(ILOp, Context.GetLabel(Op.Imm)); + } + + public static void Ret(AILEmitterCtx Context) + { + Context.EmitStoreState(); + Context.EmitLdint(AThreadState.LRIndex); + + Context.Emit(OpCodes.Ret); + } + + public static void Tbnz(AILEmitterCtx Context) => EmitTb(Context, OpCodes.Bne_Un); + public static void Tbz(AILEmitterCtx Context) => EmitTb(Context, OpCodes.Beq); + + private static void EmitTb(AILEmitterCtx Context, OpCode ILOp) + { + AOpCodeBImmTest Op = (AOpCodeBImmTest)Context.CurrOp; + + Context.EmitLdintzr(Op.Rt); + Context.EmitLdc_I(1L << Op.Pos); + + Context.Emit(OpCodes.And); + + Context.EmitLdc_I(0); + + Context.Emit(ILOp, Context.GetLabel(Op.Imm)); + } + } +}
\ No newline at end of file diff --git a/ChocolArm64/Instruction/AInstEmitMemory.cs b/ChocolArm64/Instruction/AInstEmitMemory.cs new file mode 100644 index 00000000..af7de3ba --- /dev/null +++ b/ChocolArm64/Instruction/AInstEmitMemory.cs @@ -0,0 +1,252 @@ +using ChocolArm64.Decoder; +using ChocolArm64.Translation; +using System.Reflection.Emit; + +using static ChocolArm64.Instruction.AInstEmitMemoryHelper; + +namespace ChocolArm64.Instruction +{ + static partial class AInstEmit + { + public static void Adr(AILEmitterCtx Context) + { + AOpCodeAdr Op = (AOpCodeAdr)Context.CurrOp; + + Context.EmitLdc_I(Op.Position + Op.Imm); + Context.EmitStintzr(Op.Rd); + } + + public static void Adrp(AILEmitterCtx Context) + { + AOpCodeAdr Op = (AOpCodeAdr)Context.CurrOp; + + Context.EmitLdc_I((Op.Position & ~0xfffL) + (Op.Imm << 12)); + Context.EmitStintzr(Op.Rd); + } + + public static void Ldr(AILEmitterCtx Context) => EmitLdr(Context, false); + public static void Ldrs(AILEmitterCtx Context) => EmitLdr(Context, true); + + public static void EmitLdr(AILEmitterCtx Context, bool Signed) + { + AOpCodeMem Op = (AOpCodeMem)Context.CurrOp; + + Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); + + EmitLoadAddress(Context); + + if (Signed && Op.Extend64) + { + EmitReadSx64Call(Context, Op.Size); + } + else if (Signed) + { + EmitReadSx32Call(Context, Op.Size); + } + else + { + EmitReadZxCall(Context, Op.Size); + } + + if (Op is IAOpCodeSimd) + { + Context.EmitStvec(Op.Rt); + } + else + { + Context.EmitStintzr(Op.Rt); + } + + EmitWBackIfNeeded(Context); + } + + public static void LdrLit(AILEmitterCtx Context) + { + IAOpCodeLit Op = (IAOpCodeLit)Context.CurrOp; + + if (Op.Prefetch) + { + return; + } + + Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); + Context.EmitLdc_I8(Op.Imm); + + if (Op.Signed) + { + EmitReadSx64Call(Context, Op.Size); + } + else + { + EmitReadZxCall(Context, Op.Size); + } + + if (Op is IAOpCodeSimd) + { + Context.EmitStvec(Op.Rt); + } + else + { + Context.EmitStint(Op.Rt); + } + } + + public static void Ldp(AILEmitterCtx Context) + { + AOpCodeMemPair Op = (AOpCodeMemPair)Context.CurrOp; + + void EmitReadAndStore(int Rt) + { + if (Op.Extend64) + { + EmitReadSx64Call(Context, Op.Size); + } + else + { + EmitReadZxCall(Context, Op.Size); + } + + if (Op is IAOpCodeSimd) + { + Context.EmitStvec(Rt); + } + else + { + Context.EmitStintzr(Rt); + } + } + + Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); + + EmitLoadAddress(Context); + + EmitReadAndStore(Op.Rt); + + Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); + Context.EmitLdtmp(); + Context.EmitLdc_I8(1 << Op.Size); + + Context.Emit(OpCodes.Add); + + EmitReadAndStore(Op.Rt2); + + EmitWBackIfNeeded(Context); + } + + public static void Str(AILEmitterCtx Context) + { + AOpCodeMem Op = (AOpCodeMem)Context.CurrOp; + + Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); + + EmitLoadAddress(Context); + + if (Op is IAOpCodeSimd) + { + Context.EmitLdvec(Op.Rt); + } + else + { + Context.EmitLdintzr(Op.Rt); + } + + EmitWriteCall(Context, Op.Size); + + EmitWBackIfNeeded(Context); + } + + public static void Stp(AILEmitterCtx Context) + { + AOpCodeMemPair Op = (AOpCodeMemPair)Context.CurrOp; + + Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); + + EmitLoadAddress(Context); + + if (Op is IAOpCodeSimd) + { + Context.EmitLdvec(Op.Rt); + } + else + { + Context.EmitLdintzr(Op.Rt); + } + + EmitWriteCall(Context, Op.Size); + + Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); + Context.EmitLdtmp(); + Context.EmitLdc_I8(1 << Op.Size); + + Context.Emit(OpCodes.Add); + + if (Op is IAOpCodeSimd) + { + Context.EmitLdvec(Op.Rt2); + } + else + { + Context.EmitLdintzr(Op.Rt2); + } + + EmitWriteCall(Context, Op.Size); + + EmitWBackIfNeeded(Context); + } + + private static void EmitLoadAddress(AILEmitterCtx Context) + { + switch (Context.CurrOp) + { + case AOpCodeMemImm Op: + Context.EmitLdint(Op.Rn); + + if (!Op.PostIdx) + { + //Pre-indexing. + Context.EmitLdc_I(Op.Imm); + + Context.Emit(OpCodes.Add); + } + break; + + case AOpCodeMemReg Op: + Context.EmitLdint(Op.Rn); + Context.EmitLdintzr(Op.Rm); + Context.EmitCast(Op.IntType); + + if (Op.Shift) + { + Context.EmitLsl(Op.Size); + } + + Context.Emit(OpCodes.Add); + break; + } + + //Save address to Scratch var since the register value may change. + Context.Emit(OpCodes.Dup); + + Context.EmitSttmp(); + } + + private static void EmitWBackIfNeeded(AILEmitterCtx Context) + { + //Check whenever the current OpCode has post-indexed write back, if so write it. + //Note: AOpCodeMemPair inherits from AOpCodeMemImm, so this works for both. + if (Context.CurrOp is AOpCodeMemImm Op && Op.WBack) + { + Context.EmitLdtmp(); + + if (Op.PostIdx) + { + Context.EmitLdc_I(Op.Imm); + + Context.Emit(OpCodes.Add); + } + + Context.EmitStint(Op.Rn); + } + } + } +}
\ No newline at end of file diff --git a/ChocolArm64/Instruction/AInstEmitMemoryEx.cs b/ChocolArm64/Instruction/AInstEmitMemoryEx.cs new file mode 100644 index 00000000..a339bb69 --- /dev/null +++ b/ChocolArm64/Instruction/AInstEmitMemoryEx.cs @@ -0,0 +1,180 @@ +using ChocolArm64.Decoder; +using ChocolArm64.Memory; +using ChocolArm64.Translation; +using System; +using System.Reflection.Emit; +using System.Threading; + +using static ChocolArm64.Instruction.AInstEmitMemoryHelper; + +namespace ChocolArm64.Instruction +{ + static partial class AInstEmit + { + [Flags] + private enum AccessType + { + None = 0, + Ordered = 1, + Exclusive = 2, + OrderedEx = Ordered | Exclusive + } + + public static void Clrex(AILEmitterCtx Context) + { + EmitMemoryCall(Context, nameof(AMemory.ClearExclusive)); + } + + public static void Dmb(AILEmitterCtx Context) => EmitBarrier(Context); + public static void Dsb(AILEmitterCtx Context) => EmitBarrier(Context); + + public static void Ldar(AILEmitterCtx Context) => EmitLdr(Context, AccessType.Ordered); + public static void Ldaxr(AILEmitterCtx Context) => EmitLdr(Context, AccessType.OrderedEx); + public static void Ldxr(AILEmitterCtx Context) => EmitLdr(Context, AccessType.Exclusive); + public static void Ldxp(AILEmitterCtx Context) => EmitLdp(Context, AccessType.Exclusive); + public static void Ldaxp(AILEmitterCtx Context) => EmitLdp(Context, AccessType.OrderedEx); + + private static void EmitLdr(AILEmitterCtx Context, AccessType AccType) + { + EmitLoad(Context, AccType, false); + } + + private static void EmitLdp(AILEmitterCtx Context, AccessType AccType) + { + EmitLoad(Context, AccType, true); + } + + private static void EmitLoad(AILEmitterCtx Context, AccessType AccType, bool Pair) + { + AOpCodeMemEx Op = (AOpCodeMemEx)Context.CurrOp; + + Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); + Context.EmitLdint(Op.Rn); + + EmitReadZxCall(Context, Op.Size); + + Context.EmitStintzr(Op.Rt); + + if (Pair) + { + Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); + Context.EmitLdint(Op.Rn); + Context.EmitLdc_I(8 << Op.Size); + + Context.Emit(OpCodes.Add); + + EmitReadZxCall(Context, Op.Size); + + Context.EmitStintzr(Op.Rt2); + } + + if (AccType.HasFlag(AccessType.Exclusive)) + { + EmitMemoryCall(Context, nameof(AMemory.SetExclusive), Op.Rn); + } + + if (AccType.HasFlag(AccessType.Ordered)) + { + EmitBarrier(Context); + } + } + + public static void Pfrm(AILEmitterCtx Context) + { + //Memory Prefetch, execute as no-op. + } + + public static void Stlr(AILEmitterCtx Context) => EmitStr(Context, AccessType.Ordered); + public static void Stlxr(AILEmitterCtx Context) => EmitStr(Context, AccessType.OrderedEx); + public static void Stxr(AILEmitterCtx Context) => EmitStr(Context, AccessType.Exclusive); + public static void Stxp(AILEmitterCtx Context) => EmitStp(Context, AccessType.Exclusive); + public static void Stlxp(AILEmitterCtx Context) => EmitStp(Context, AccessType.OrderedEx); + + private static void EmitStr(AILEmitterCtx Context, AccessType AccType) + { + EmitStore(Context, AccType, false); + } + + private static void EmitStp(AILEmitterCtx Context, AccessType AccType) + { + EmitStore(Context, AccType, true); + } + + private static void EmitStore(AILEmitterCtx Context, AccessType AccType, bool Pair) + { + AOpCodeMemEx Op = (AOpCodeMemEx)Context.CurrOp; + + if (AccType.HasFlag(AccessType.Ordered)) + { + EmitBarrier(Context); + } + + AILLabel LblEx = new AILLabel(); + AILLabel LblEnd = new AILLabel(); + + if (AccType.HasFlag(AccessType.Exclusive)) + { + EmitMemoryCall(Context, nameof(AMemory.TestExclusive), Op.Rn); + + Context.Emit(OpCodes.Brtrue_S, LblEx); + + Context.EmitLdc_I8(1); + Context.EmitStintzr(Op.Rs); + + Context.Emit(OpCodes.Br_S, LblEnd); + } + + Context.MarkLabel(LblEx); + + Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); + Context.EmitLdint(Op.Rn); + Context.EmitLdintzr(Op.Rt); + + EmitWriteCall(Context, Op.Size); + + if (Pair) + { + Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); + Context.EmitLdint(Op.Rn); + Context.EmitLdc_I(8 << Op.Size); + + Context.Emit(OpCodes.Add); + + Context.EmitLdintzr(Op.Rt2); + + EmitWriteCall(Context, Op.Size); + } + + if (AccType.HasFlag(AccessType.Exclusive)) + { + Context.EmitLdc_I8(0); + Context.EmitStintzr(Op.Rs); + + Clrex(Context); + } + + Context.MarkLabel(LblEnd); + } + + private static void EmitMemoryCall(AILEmitterCtx Context, string Name, int Rn = -1) + { + Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); + Context.EmitLdarg(ATranslatedSub.StateArgIdx); + + if (Rn != -1) + { + Context.EmitLdint(Rn); + } + + Context.EmitCall(typeof(AMemory), Name); + } + + private static void EmitBarrier(AILEmitterCtx Context) + { + //Note: This barrier is most likely not necessary, and probably + //doesn't make any difference since we need to do a ton of stuff + //(software MMU emulation) to read or write anything anyway. + Context.EmitCall(typeof(Thread), nameof(Thread.MemoryBarrier)); + } + } +}
\ No newline at end of file diff --git a/ChocolArm64/Instruction/AInstEmitMemoryHelper.cs b/ChocolArm64/Instruction/AInstEmitMemoryHelper.cs new file mode 100644 index 00000000..d5a0051b --- /dev/null +++ b/ChocolArm64/Instruction/AInstEmitMemoryHelper.cs @@ -0,0 +1,138 @@ +using ChocolArm64.Decoder; +using ChocolArm64.Memory; +using ChocolArm64.Translation; +using System; +using System.Reflection.Emit; + +namespace ChocolArm64.Instruction +{ + static class AInstEmitMemoryHelper + { + private enum Extension + { + Zx, + Sx32, + Sx64 + } + + public static void EmitReadZxCall(AILEmitterCtx Context, int Size) + { + EmitReadCall(Context, Extension.Zx, Size); + } + + public static void EmitReadSx32Call(AILEmitterCtx Context, int Size) + { + EmitReadCall(Context, Extension.Sx32, Size); + } + + public static void EmitReadSx64Call(AILEmitterCtx Context, int Size) + { + EmitReadCall(Context, Extension.Sx64, Size); + } + + private static void EmitReadCall(AILEmitterCtx Context, Extension Ext, int Size) + { + bool IsSimd = GetIsSimd(Context); + + string Name = null; + + if (Size < 0 || Size > (IsSimd ? 4 : 3)) + { + throw new ArgumentOutOfRangeException(nameof(Size)); + } + + if (IsSimd) + { + switch (Size) + { + case 0: Name = nameof(AMemory.ReadVector8); break; + case 1: Name = nameof(AMemory.ReadVector16); break; + case 2: Name = nameof(AMemory.ReadVector32); break; + case 3: Name = nameof(AMemory.ReadVector64); break; + case 4: Name = nameof(AMemory.ReadVector128); break; + } + } + else + { + switch (Size) + { + case 0: Name = nameof(AMemory.ReadByte); break; + case 1: Name = nameof(AMemory.ReadUInt16); break; + case 2: Name = nameof(AMemory.ReadUInt32); break; + case 3: Name = nameof(AMemory.ReadUInt64); break; + } + } + + Context.EmitCall(typeof(AMemory), Name); + + if (!IsSimd) + { + if (Ext == Extension.Sx32 || + Ext == Extension.Sx64) + { + switch (Size) + { + case 0: Context.Emit(OpCodes.Conv_I1); break; + case 1: Context.Emit(OpCodes.Conv_I2); break; + case 2: Context.Emit(OpCodes.Conv_I4); break; + } + } + + if (Size < 3) + { + Context.Emit(Ext == Extension.Sx64 + ? OpCodes.Conv_I8 + : OpCodes.Conv_U8); + } + } + } + + public static void EmitWriteCall(AILEmitterCtx Context, int Size) + { + bool IsSimd = GetIsSimd(Context); + + string Name = null; + + if (Size < 0 || Size > (IsSimd ? 4 : 3)) + { + throw new ArgumentOutOfRangeException(nameof(Size)); + } + + if (Size < 3 && !IsSimd) + { + Context.Emit(OpCodes.Conv_I4); + } + + if (IsSimd) + { + switch (Size) + { + case 0: Name = nameof(AMemory.WriteVector8); break; + case 1: Name = nameof(AMemory.WriteVector16); break; + case 2: Name = nameof(AMemory.WriteVector32); break; + case 3: Name = nameof(AMemory.WriteVector64); break; + case 4: Name = nameof(AMemory.WriteVector128); break; + } + } + else + { + switch (Size) + { + case 0: Name = nameof(AMemory.WriteByte); break; + case 1: Name = nameof(AMemory.WriteUInt16); break; + case 2: Name = nameof(AMemory.WriteUInt32); break; + case 3: Name = nameof(AMemory.WriteUInt64); break; + } + } + + Context.EmitCall(typeof(AMemory), Name); + } + + private static bool GetIsSimd(AILEmitterCtx Context) + { + return Context.CurrOp is IAOpCodeSimd && + !(Context.CurrOp is AOpCodeSimdMemMs || + Context.CurrOp is AOpCodeSimdMemSs); + } + } +}
\ No newline at end of file diff --git a/ChocolArm64/Instruction/AInstEmitMove.cs b/ChocolArm64/Instruction/AInstEmitMove.cs new file mode 100644 index 00000000..719b53d5 --- /dev/null +++ b/ChocolArm64/Instruction/AInstEmitMove.cs @@ -0,0 +1,41 @@ +using ChocolArm64.Decoder; +using ChocolArm64.Translation; +using System.Reflection.Emit; + +namespace ChocolArm64.Instruction +{ + static partial class AInstEmit + { + public static void Movk(AILEmitterCtx Context) + { + AOpCodeMov Op = (AOpCodeMov)Context.CurrOp; + + Context.EmitLdintzr(Op.Rd); + Context.EmitLdc_I(~(0xffffL << Op.Pos)); + + Context.Emit(OpCodes.And); + + Context.EmitLdc_I(Op.Imm); + + Context.Emit(OpCodes.Or); + + Context.EmitStintzr(Op.Rd); + } + + public static void Movn(AILEmitterCtx Context) + { + AOpCodeMov Op = (AOpCodeMov)Context.CurrOp; + + Context.EmitLdc_I(~Op.Imm); + Context.EmitStintzr(Op.Rd); + } + + public static void Movz(AILEmitterCtx Context) + { + AOpCodeMov Op = (AOpCodeMov)Context.CurrOp; + + Context.EmitLdc_I(Op.Imm); + Context.EmitStintzr(Op.Rd); + } + } +}
\ No newline at end of file diff --git a/ChocolArm64/Instruction/AInstEmitMul.cs b/ChocolArm64/Instruction/AInstEmitMul.cs new file mode 100644 index 00000000..3713c81f --- /dev/null +++ b/ChocolArm64/Instruction/AInstEmitMul.cs @@ -0,0 +1,80 @@ +using ChocolArm64.Decoder; +using ChocolArm64.Translation; +using System.Reflection.Emit; + +namespace ChocolArm64.Instruction +{ + static partial class AInstEmit + { + public static void Madd(AILEmitterCtx Context) => EmitMul(Context, OpCodes.Add); + public static void Msub(AILEmitterCtx Context) => EmitMul(Context, OpCodes.Sub); + + private static void EmitMul(AILEmitterCtx Context, OpCode ILOp) + { + AOpCodeMul Op = (AOpCodeMul)Context.CurrOp; + + Context.EmitLdintzr(Op.Ra); + Context.EmitLdintzr(Op.Rn); + Context.EmitLdintzr(Op.Rm); + + Context.Emit(OpCodes.Mul); + Context.Emit(ILOp); + + Context.EmitStintzr(Op.Rd); + } + + public static void Smaddl(AILEmitterCtx Context) => EmitMull(Context, OpCodes.Add, true); + public static void Smsubl(AILEmitterCtx Context) => EmitMull(Context, OpCodes.Sub, true); + public static void Umaddl(AILEmitterCtx Context) => EmitMull(Context, OpCodes.Add, false); + public static void Umsubl(AILEmitterCtx Context) => EmitMull(Context, OpCodes.Sub, false); + + private static void EmitMull(AILEmitterCtx Context, OpCode AddSubOp, bool Signed) + { + AOpCodeMul Op = (AOpCodeMul)Context.CurrOp; + + OpCode CastOp = Signed + ? OpCodes.Conv_I8 + : OpCodes.Conv_U8; + + Context.EmitLdintzr(Op.Ra); + Context.EmitLdintzr(Op.Rn); + + Context.Emit(OpCodes.Conv_I4); + Context.Emit(CastOp); + + Context.EmitLdintzr(Op.Rm); + + Context.Emit(OpCodes.Conv_I4); + Context.Emit(CastOp); + Context.Emit(OpCodes.Mul); + + Context.Emit(AddSubOp); + + Context.EmitStintzr(Op.Rd); + } + + public static void Smulh(AILEmitterCtx Context) + { + AOpCodeMul Op = (AOpCodeMul)Context.CurrOp; + + Context.EmitLdintzr(Op.Rn); + Context.EmitLdintzr(Op.Rm); + + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SMulHi128)); + + Context.EmitStintzr(Op.Rd); + } + + public static void Umulh(AILEmitterCtx Context) + { + AOpCodeMul Op = (AOpCodeMul)Context.CurrOp; + + Context.EmitLdintzr(Op.Rn); + Context.EmitLdintzr(Op.Rm); + + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.UMulHi128)); + + Context.EmitStintzr(Op.Rd); + } + } +}
\ No newline at end of file diff --git a/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs b/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs new file mode 100644 index 00000000..21ec11a7 --- /dev/null +++ b/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs @@ -0,0 +1,367 @@ +using ChocolArm64.Decoder; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System; +using System.Reflection; +using System.Reflection.Emit; + +using static ChocolArm64.Instruction.AInstEmitSimdHelper; + +namespace ChocolArm64.Instruction +{ + static partial class AInstEmit + { + public static void Add_V(AILEmitterCtx Context) + { + EmitVectorBinaryOpZx(Context, () => Context.Emit(OpCodes.Add)); + } + + public static void Addp_S(AILEmitterCtx Context) + { + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + EmitVectorExtractZx(Context, Op.Rn, 0, Op.Size); + EmitVectorExtractZx(Context, Op.Rn, 1, Op.Size); + + Context.Emit(OpCodes.Add); + + EmitScalarSet(Context, Op.Rd, Op.Size); + } + + public static void Addp_V(AILEmitterCtx Context) + { + AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; + + int Bytes = Context.CurrOp.GetBitsCount() >> 3; + + int Elems = Bytes >> Op.Size; + int Half = Elems >> 1; + + for (int Index = 0; Index < Elems; Index++) + { + int Elem = (Index & (Half - 1)) << 1; + + EmitVectorExtractZx(Context, Index < Half ? Op.Rn : Op.Rm, Elem + 0, Op.Size); + EmitVectorExtractZx(Context, Index < Half ? Op.Rn : Op.Rm, Elem + 1, Op.Size); + + Context.Emit(OpCodes.Add); + + EmitVectorInsertTmp(Context, Index, Op.Size); + } + + Context.EmitLdvectmp(); + Context.EmitStvec(Op.Rd); + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + + public static void Addv_V(AILEmitterCtx Context) + { + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + int Bytes = Context.CurrOp.GetBitsCount() >> 3; + + EmitVectorExtractZx(Context, Op.Rn, 0, Op.Size); + + for (int Index = 1; Index < (Bytes >> Op.Size); Index++) + { + EmitVectorExtractZx(Context, Op.Rn, Index, Op.Size); + + Context.Emit(OpCodes.Add); + } + + EmitScalarSet(Context, Op.Rd, Op.Size); + } + + public static void Cnt_V(AILEmitterCtx Context) + { + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + int Elems = Op.RegisterSize == ARegisterSize.SIMD128 ? 16 : 8; + + for (int Index = 0; Index < Elems; Index++) + { + EmitVectorExtractZx(Context, Op.Rn, Index, 0); + + Context.Emit(OpCodes.Conv_U1); + + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.CountSetBits8)); + + Context.Emit(OpCodes.Conv_U8); + + EmitVectorInsert(Context, Op.Rd, Index, 0); + } + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + + public static void Fabs_S(AILEmitterCtx Context) + { + EmitScalarUnaryOpF(Context, () => + { + EmitUnaryMathCall(Context, nameof(Math.Abs)); + }); + } + + public static void Fadd_S(AILEmitterCtx Context) + { + EmitScalarBinaryOpF(Context, () => Context.Emit(OpCodes.Add)); + } + + public static void Fadd_V(AILEmitterCtx Context) + { + EmitVectorBinaryOpF(Context, () => Context.Emit(OpCodes.Add)); + } + + public static void Fdiv_S(AILEmitterCtx Context) + { + EmitScalarBinaryOpF(Context, () => Context.Emit(OpCodes.Div)); + } + + public static void Fdiv_V(AILEmitterCtx Context) + { + EmitVectorBinaryOpF(Context, () => Context.Emit(OpCodes.Div)); + } + + public static void Fmadd_S(AILEmitterCtx Context) + { + EmitScalarTernaryRaOpF(Context, () => + { + Context.Emit(OpCodes.Mul); + Context.Emit(OpCodes.Add); + }); + } + + public static void Fmax_S(AILEmitterCtx Context) + { + EmitScalarBinaryOpF(Context, () => + { + EmitBinaryMathCall(Context, nameof(Math.Max)); + }); + } + + public static void Fmin_S(AILEmitterCtx Context) + { + EmitScalarBinaryOpF(Context, () => + { + EmitBinaryMathCall(Context, nameof(Math.Min)); + }); + } + + public static void Fmaxnm_S(AILEmitterCtx Context) + { + Fmax_S(Context); + } + + public static void Fminnm_S(AILEmitterCtx Context) + { + Fmin_S(Context); + } + + public static void Fmla_V(AILEmitterCtx Context) + { + EmitVectorTernaryOpF(Context, () => + { + Context.Emit(OpCodes.Mul); + Context.Emit(OpCodes.Add); + }); + } + + public static void Fmla_Ve(AILEmitterCtx Context) + { + EmitVectorTernaryOpByElemF(Context, () => + { + Context.Emit(OpCodes.Mul); + Context.Emit(OpCodes.Add); + }); + } + + public static void Fmsub_S(AILEmitterCtx Context) + { + EmitScalarTernaryRaOpF(Context, () => + { + Context.Emit(OpCodes.Mul); + Context.Emit(OpCodes.Sub); + }); + } + + public static void Fmul_S(AILEmitterCtx Context) + { + EmitScalarBinaryOpF(Context, () => Context.Emit(OpCodes.Mul)); + } + + public static void Fmul_V(AILEmitterCtx Context) + { + EmitVectorBinaryOpF(Context, () => Context.Emit(OpCodes.Mul)); + } + + public static void Fmul_Ve(AILEmitterCtx Context) + { + EmitVectorBinaryOpByElemF(Context, () => Context.Emit(OpCodes.Mul)); + } + + public static void Fneg_S(AILEmitterCtx Context) + { + EmitScalarUnaryOpF(Context, () => Context.Emit(OpCodes.Neg)); + } + + public static void Fnmul_S(AILEmitterCtx Context) + { + EmitScalarBinaryOpF(Context, () => + { + Context.Emit(OpCodes.Mul); + Context.Emit(OpCodes.Neg); + }); + } + + public static void Fnmsub_S(AILEmitterCtx Context) + { + AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; + + int SizeF = Op.Size & 1; + + EmitVectorExtractF(Context, Op.Rn, 0, SizeF); + EmitVectorExtractF(Context, Op.Rm, 0, SizeF); + + Context.Emit(OpCodes.Mul); + + EmitVectorExtractF(Context, Op.Ra, 0, SizeF); + + Context.Emit(OpCodes.Sub); + + EmitScalarSetF(Context, Op.Rd, SizeF); + } + + public static void Frinta_S(AILEmitterCtx Context) + { + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + EmitVectorExtractF(Context, Op.Rn, 0, Op.Size); + + EmitRoundMathCall(Context, MidpointRounding.AwayFromZero); + + EmitScalarSetF(Context, Op.Rd, Op.Size); + } + + public static void Frintm_S(AILEmitterCtx Context) + { + EmitScalarUnaryOpF(Context, () => + { + EmitUnaryMathCall(Context, nameof(Math.Floor)); + }); + } + + public static void Fsqrt_S(AILEmitterCtx Context) + { + EmitScalarUnaryOpF(Context, () => + { + EmitUnaryMathCall(Context, nameof(Math.Sqrt)); + }); + } + + public static void Fsub_S(AILEmitterCtx Context) + { + EmitScalarBinaryOpF(Context, () => Context.Emit(OpCodes.Sub)); + } + + public static void Fsub_V(AILEmitterCtx Context) + { + EmitVectorBinaryOpF(Context, () => Context.Emit(OpCodes.Sub)); + } + + public static void Mla_V(AILEmitterCtx Context) + { + EmitVectorTernaryOpZx(Context, () => + { + Context.Emit(OpCodes.Mul); + Context.Emit(OpCodes.Add); + }); + } + + public static void Mls_V(AILEmitterCtx Context) + { + EmitVectorTernaryOpZx(Context, () => + { + Context.Emit(OpCodes.Mul); + Context.Emit(OpCodes.Sub); + }); + } + + public static void Mul_V(AILEmitterCtx Context) + { + EmitVectorBinaryOpZx(Context, () => Context.Emit(OpCodes.Mul)); + } + + public static void Neg_V(AILEmitterCtx Context) + { + EmitVectorUnaryOpSx(Context, () => Context.Emit(OpCodes.Neg)); + } + + public static void Saddw_V(AILEmitterCtx Context) + { + EmitVectorWidenRmBinaryOpSx(Context, () => Context.Emit(OpCodes.Add)); + } + + public static void Smax_V(AILEmitterCtx Context) + { + Type[] Types = new Type[] { typeof(long), typeof(long) }; + + MethodInfo MthdInfo = typeof(Math).GetMethod(nameof(Math.Max), Types); + + EmitVectorBinaryOpSx(Context, () => Context.EmitCall(MthdInfo)); + } + + public static void Smin_V(AILEmitterCtx Context) + { + Type[] Types = new Type[] { typeof(long), typeof(long) }; + + MethodInfo MthdInfo = typeof(Math).GetMethod(nameof(Math.Min), Types); + + EmitVectorBinaryOpSx(Context, () => Context.EmitCall(MthdInfo)); + } + + public static void Smull_V(AILEmitterCtx Context) + { + EmitVectorWidenRnRmBinaryOpSx(Context, () => Context.Emit(OpCodes.Mul)); + } + + public static void Sub_S(AILEmitterCtx Context) + { + EmitScalarBinaryOpZx(Context, () => Context.Emit(OpCodes.Sub)); + } + + public static void Sub_V(AILEmitterCtx Context) + { + EmitVectorBinaryOpZx(Context, () => Context.Emit(OpCodes.Sub)); + } + + public static void Uaddlv_V(AILEmitterCtx Context) + { + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + int Bytes = Context.CurrOp.GetBitsCount() >> 3; + + EmitVectorExtractZx(Context, Op.Rn, 0, Op.Size); + + for (int Index = 1; Index < (Bytes >> Op.Size); Index++) + { + EmitVectorExtractZx(Context, Op.Rn, Index, Op.Size); + + Context.Emit(OpCodes.Add); + } + + EmitScalarSet(Context, Op.Rd, Op.Size + 1); + } + + public static void Uaddw_V(AILEmitterCtx Context) + { + EmitVectorWidenRmBinaryOpZx(Context, () => Context.Emit(OpCodes.Add)); + } + } +}
\ No newline at end of file diff --git a/ChocolArm64/Instruction/AInstEmitSimdCmp.cs b/ChocolArm64/Instruction/AInstEmitSimdCmp.cs new file mode 100644 index 00000000..97ccf0ab --- /dev/null +++ b/ChocolArm64/Instruction/AInstEmitSimdCmp.cs @@ -0,0 +1,233 @@ +using ChocolArm64.Decoder; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System; +using System.Reflection.Emit; + +using static ChocolArm64.Instruction.AInstEmitAluHelper; +using static ChocolArm64.Instruction.AInstEmitSimdHelper; + +namespace ChocolArm64.Instruction +{ + static partial class AInstEmit + { + public static void Cmeq_V(AILEmitterCtx Context) + { + EmitVectorCmp(Context, OpCodes.Beq_S); + } + + public static void Cmge_V(AILEmitterCtx Context) + { + EmitVectorCmp(Context, OpCodes.Bge_S); + } + + public static void Cmgt_V(AILEmitterCtx Context) + { + EmitVectorCmp(Context, OpCodes.Bgt_S); + } + + public static void Cmhi_V(AILEmitterCtx Context) + { + EmitVectorCmp(Context, OpCodes.Bgt_Un_S); + } + + public static void Cmhs_V(AILEmitterCtx Context) + { + EmitVectorCmp(Context, OpCodes.Bge_Un_S); + } + + public static void Cmle_V(AILEmitterCtx Context) + { + EmitVectorCmp(Context, OpCodes.Ble_S); + } + + public static void Cmlt_V(AILEmitterCtx Context) + { + EmitVectorCmp(Context, OpCodes.Blt_S); + } + + public static void Fccmp_S(AILEmitterCtx Context) + { + AOpCodeSimdFcond Op = (AOpCodeSimdFcond)Context.CurrOp; + + AILLabel LblTrue = new AILLabel(); + AILLabel LblEnd = new AILLabel(); + + Context.EmitCondBranch(LblTrue, Op.Cond); + + EmitSetNZCV(Context, Op.NZCV); + + Context.Emit(OpCodes.Br, LblEnd); + + Context.MarkLabel(LblTrue); + + Fcmp_S(Context); + + Context.MarkLabel(LblEnd); + } + + public static void Fccmpe_S(AILEmitterCtx Context) + { + Fccmp_S(Context); + } + + public static void Fcmp_S(AILEmitterCtx Context) + { + AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; + + bool CmpWithZero = !(Op is AOpCodeSimdFcond) ? Op.Bit3 : false; + + //Handle NaN case. If any number is NaN, then NZCV = 0011. + if (CmpWithZero) + { + EmitNaNCheck(Context, Op.Rn); + } + else + { + EmitNaNCheck(Context, Op.Rn); + EmitNaNCheck(Context, Op.Rm); + + Context.Emit(OpCodes.Or); + } + + AILLabel LblNaN = new AILLabel(); + AILLabel LblEnd = new AILLabel(); + + Context.Emit(OpCodes.Brtrue_S, LblNaN); + + void EmitLoadOpers() + { + EmitVectorExtractF(Context, Op.Rn, 0, Op.Size); + + if (CmpWithZero) + { + EmitLdcImmF(Context, 0, Op.Size); + } + else + { + EmitVectorExtractF(Context, Op.Rm, 0, Op.Size); + } + } + + //Z = Rn == Rm + EmitLoadOpers(); + + Context.Emit(OpCodes.Ceq); + Context.Emit(OpCodes.Dup); + + Context.EmitStflg((int)APState.ZBit); + + //C = Rn >= Rm + EmitLoadOpers(); + + Context.Emit(OpCodes.Cgt); + Context.Emit(OpCodes.Or); + + Context.EmitStflg((int)APState.CBit); + + //N = Rn < Rm + EmitLoadOpers(); + + Context.Emit(OpCodes.Clt); + + Context.EmitStflg((int)APState.NBit); + + //V = 0 + Context.EmitLdc_I4(0); + + Context.EmitStflg((int)APState.VBit); + + Context.Emit(OpCodes.Br_S, LblEnd); + + Context.MarkLabel(LblNaN); + + EmitSetNZCV(Context, 0b0011); + + Context.MarkLabel(LblEnd); + } + + public static void Fcmpe_S(AILEmitterCtx Context) + { + Fcmp_S(Context); + } + + private static void EmitLdcImmF(AILEmitterCtx Context, double ImmF, int Size) + { + if (Size == 0) + { + Context.EmitLdc_R4((float)ImmF); + } + else if (Size == 1) + { + Context.EmitLdc_R8(ImmF); + } + else + { + throw new ArgumentOutOfRangeException(nameof(Size)); + } + } + + private static void EmitNaNCheck(AILEmitterCtx Context, int Reg) + { + IAOpCodeSimd Op = (IAOpCodeSimd)Context.CurrOp; + + EmitVectorExtractF(Context, Reg, 0, Op.Size); + + if (Op.Size == 0) + { + Context.EmitCall(typeof(float), nameof(float.IsNaN)); + } + else if (Op.Size == 1) + { + Context.EmitCall(typeof(double), nameof(double.IsNaN)); + } + else + { + throw new InvalidOperationException(); + } + } + + private static void EmitVectorCmp(AILEmitterCtx Context, OpCode ILOp) + { + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + int Bytes = Context.CurrOp.GetBitsCount() >> 3; + + ulong SzMask = ulong.MaxValue >> (64 - (8 << Op.Size)); + + for (int Index = 0; Index < (Bytes >> Op.Size); Index++) + { + EmitVectorExtractSx(Context, Op.Rn, Index, Op.Size); + + if (Op is AOpCodeSimdReg BinOp) + { + EmitVectorExtractSx(Context, BinOp.Rm, Index, Op.Size); + } + else + { + Context.EmitLdc_I8(0); + } + + AILLabel LblTrue = new AILLabel(); + AILLabel LblEnd = new AILLabel(); + + Context.Emit(ILOp, LblTrue); + + EmitVectorInsert(Context, Op.Rd, Index, Op.Size, 0); + + Context.Emit(OpCodes.Br_S, LblEnd); + + Context.MarkLabel(LblTrue); + + EmitVectorInsert(Context, Op.Rd, Index, Op.Size, (long)SzMask); + + Context.MarkLabel(LblEnd); + } + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + } +}
\ No newline at end of file diff --git a/ChocolArm64/Instruction/AInstEmitSimdCvt.cs b/ChocolArm64/Instruction/AInstEmitSimdCvt.cs new file mode 100644 index 00000000..00c2fe9b --- /dev/null +++ b/ChocolArm64/Instruction/AInstEmitSimdCvt.cs @@ -0,0 +1,444 @@ +using ChocolArm64.Decoder; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System; +using System.Reflection.Emit; + +using static ChocolArm64.Instruction.AInstEmitSimdHelper; + +namespace ChocolArm64.Instruction +{ + static partial class AInstEmit + { + public static void Fcvt_S(AILEmitterCtx Context) + { + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + EmitVectorExtractF(Context, Op.Rn, 0, Op.Size); + + EmitFloatCast(Context, Op.Opc); + + EmitScalarSetF(Context, Op.Rd, Op.Opc); + } + + public static void Fcvtas_Gp(AILEmitterCtx Context) + { + Fcvta__Gp(Context, Signed: true); + } + + public static void Fcvtau_Gp(AILEmitterCtx Context) + { + Fcvta__Gp(Context, Signed: false); + } + + public static void Fcvtms_Gp(AILEmitterCtx Context) + { + EmitFcvt_s_Gp(Context, nameof(Math.Floor)); + } + + public static void Fcvtps_Gp(AILEmitterCtx Context) + { + EmitFcvt_s_Gp(Context, nameof(Math.Ceiling)); + } + + public static void Fcvtzs_Gp(AILEmitterCtx Context) + { + EmitFcvtz__Gp(Context, Signed: true); + } + + public static void Fcvtzs_Gp_Fix(AILEmitterCtx Context) + { + EmitFcvtz__Gp_Fix(Context, Signed: true); + } + + public static void Fcvtzs_V(AILEmitterCtx Context) + { + EmitVectorFcvt(Context, Signed: true); + } + + public static void Fcvtzu_Gp(AILEmitterCtx Context) + { + EmitFcvtz__Gp(Context, Signed: false); + } + + public static void Fcvtzu_Gp_Fix(AILEmitterCtx Context) + { + EmitFcvtz__Gp_Fix(Context, Signed: false); + } + + public static void Fcvtzu_V(AILEmitterCtx Context) + { + EmitVectorFcvt(Context, Signed: false); + } + + public static void Scvtf_Gp(AILEmitterCtx Context) + { + AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp; + + Context.EmitLdintzr(Op.Rn); + + if (Context.CurrOp.RegisterSize == ARegisterSize.Int32) + { + Context.Emit(OpCodes.Conv_U4); + } + + EmitFloatCast(Context, Op.Size); + + EmitScalarSetF(Context, Op.Rd, Op.Size); + } + + public static void Scvtf_S(AILEmitterCtx Context) + { + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + EmitVectorExtractSx(Context, Op.Rn, 0, Op.Size + 2); + + EmitFloatCast(Context, Op.Size); + + EmitScalarSetF(Context, Op.Rd, Op.Size); + } + + public static void Scvtf_V(AILEmitterCtx Context) + { + EmitVectorCvtf(Context, Signed: true); + } + + public static void Ucvtf_Gp(AILEmitterCtx Context) + { + AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp; + + Context.EmitLdintzr(Op.Rn); + + if (Context.CurrOp.RegisterSize == ARegisterSize.Int32) + { + Context.Emit(OpCodes.Conv_U4); + } + + Context.Emit(OpCodes.Conv_R_Un); + + EmitFloatCast(Context, Op.Size); + + EmitScalarSetF(Context, Op.Rd, Op.Size); + } + + public static void Ucvtf_S(AILEmitterCtx Context) + { + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + EmitVectorExtractZx(Context, Op.Rn, 0, Op.Size + 2); + + Context.Emit(OpCodes.Conv_R_Un); + + EmitFloatCast(Context, Op.Size); + + EmitScalarSetF(Context, Op.Rd, Op.Size); + } + + public static void Ucvtf_V(AILEmitterCtx Context) + { + EmitVectorCvtf(Context, Signed: false); + } + + private static int GetFBits(AILEmitterCtx Context) + { + if (Context.CurrOp is AOpCodeSimdShImm Op) + { + return GetImmShr(Op); + } + + return 0; + } + + private static void EmitFloatCast(AILEmitterCtx Context, int Size) + { + if (Size == 0) + { + Context.Emit(OpCodes.Conv_R4); + } + else if (Size == 1) + { + Context.Emit(OpCodes.Conv_R8); + } + else + { + throw new ArgumentOutOfRangeException(nameof(Size)); + } + } + + private static void Fcvta__Gp(AILEmitterCtx Context, bool Signed) + { + AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp; + + EmitVectorExtractF(Context, Op.Rn, 0, Op.Size); + + EmitRoundMathCall(Context, MidpointRounding.AwayFromZero); + + if (Signed) + { + EmitScalarFcvts(Context, Op.Size, 0); + } + else + { + EmitScalarFcvtu(Context, Op.Size, 0); + } + + if (Context.CurrOp.RegisterSize == ARegisterSize.Int32) + { + Context.Emit(OpCodes.Conv_U8); + } + + Context.EmitStintzr(Op.Rd); + } + + private static void EmitFcvt_s_Gp(AILEmitterCtx Context, string Name) + { + AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp; + + EmitVectorExtractF(Context, Op.Rn, 0, Op.Size); + + EmitUnaryMathCall(Context, Name); + + EmitScalarFcvts(Context, Op.Size, 0); + + if (Context.CurrOp.RegisterSize == ARegisterSize.Int32) + { + Context.Emit(OpCodes.Conv_U8); + } + + Context.EmitStintzr(Op.Rd); + } + + private static void EmitFcvtz__Gp(AILEmitterCtx Context, bool Signed) + { + AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp; + + EmitVectorExtractF(Context, Op.Rn, 0, Op.Size); + + if (Signed) + { + EmitScalarFcvts(Context, Op.Size, 0); + } + else + { + EmitScalarFcvtu(Context, Op.Size, 0); + } + + if (Context.CurrOp.RegisterSize == ARegisterSize.Int32) + { + Context.Emit(OpCodes.Conv_U8); + } + + Context.EmitStintzr(Op.Rd); + } + + private static void EmitFcvtz__Gp_Fix(AILEmitterCtx Context, bool Signed) + { + AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp; + + EmitVectorExtractF(Context, Op.Rn, 0, Op.Size); + + if (Signed) + { + EmitScalarFcvts(Context, Op.Size, Op.FBits); + } + else + { + EmitScalarFcvtu(Context, Op.Size, Op.FBits); + } + + if (Context.CurrOp.RegisterSize == ARegisterSize.Int32) + { + Context.Emit(OpCodes.Conv_U8); + } + + Context.EmitStintzr(Op.Rd); + } + + private static void EmitVectorCvtf(AILEmitterCtx Context, bool Signed) + { + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + int SizeF = Op.Size & 1; + int SizeI = SizeF + 2; + + int FBits = GetFBits(Context); + + int Bytes = Context.CurrOp.GetBitsCount() >> 3; + + for (int Index = 0; Index < (Bytes >> SizeI); Index++) + { + EmitVectorExtract(Context, Op.Rn, Index, SizeI, Signed); + + if (!Signed) + { + Context.Emit(OpCodes.Conv_R_Un); + } + + Context.Emit(SizeF == 0 + ? OpCodes.Conv_R4 + : OpCodes.Conv_R8); + + EmitI2fFBitsMul(Context, SizeF, FBits); + + EmitVectorInsertF(Context, Op.Rd, Index, SizeF); + } + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + + private static void EmitVectorFcvt(AILEmitterCtx Context, bool Signed) + { + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + int SizeF = Op.Size & 1; + int SizeI = SizeF + 2; + + int FBits = GetFBits(Context); + + int Bytes = Context.CurrOp.GetBitsCount() >> 3; + + for (int Index = 0; Index < (Bytes >> SizeI); Index++) + { + EmitVectorExtractF(Context, Op.Rn, Index, SizeF); + + EmitF2iFBitsMul(Context, SizeF, FBits); + + if (SizeF == 0) + { + ASoftFallback.EmitCall(Context, Signed + ? nameof(ASoftFallback.SatF32ToS32) + : nameof(ASoftFallback.SatF32ToU32)); + } + else /* if (SizeF == 1) */ + { + ASoftFallback.EmitCall(Context, Signed + ? nameof(ASoftFallback.SatF64ToS64) + : nameof(ASoftFallback.SatF64ToU64)); + } + + if (SizeF == 0) + { + Context.Emit(OpCodes.Conv_U8); + } + + EmitVectorInsert(Context, Op.Rd, Index, SizeI); + } + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + + private static void EmitScalarFcvts(AILEmitterCtx Context, int Size, int FBits) + { + if (Size < 0 || Size > 1) + { + throw new ArgumentOutOfRangeException(nameof(Size)); + } + + EmitF2iFBitsMul(Context, Size, FBits); + + if (Context.CurrOp.RegisterSize == ARegisterSize.Int32) + { + if (Size == 0) + { + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SatF32ToS32)); + } + else /* if (Size == 1) */ + { + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SatF64ToS32)); + } + } + else + { + if (Size == 0) + { + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SatF32ToS64)); + } + else /* if (Size == 1) */ + { + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SatF64ToS64)); + } + } + } + + private static void EmitScalarFcvtu(AILEmitterCtx Context, int Size, int FBits) + { + if (Size < 0 || Size > 1) + { + throw new ArgumentOutOfRangeException(nameof(Size)); + } + + EmitF2iFBitsMul(Context, Size, FBits); + + if (Context.CurrOp.RegisterSize == ARegisterSize.Int32) + { + if (Size == 0) + { + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SatF32ToU32)); + } + else /* if (Size == 1) */ + { + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SatF64ToU32)); + } + } + else + { + if (Size == 0) + { + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SatF32ToU64)); + } + else /* if (Size == 1) */ + { + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SatF64ToU64)); + } + } + } + + private static void EmitF2iFBitsMul(AILEmitterCtx Context, int Size, int FBits) + { + if (FBits != 0) + { + if (Size == 0) + { + Context.EmitLdc_R4(MathF.Pow(2, FBits)); + } + else if (Size == 1) + { + Context.EmitLdc_R8(Math.Pow(2, FBits)); + } + else + { + throw new ArgumentOutOfRangeException(nameof(Size)); + } + + Context.Emit(OpCodes.Mul); + } + } + + private static void EmitI2fFBitsMul(AILEmitterCtx Context, int Size, int FBits) + { + if (FBits != 0) + { + if (Size == 0) + { + Context.EmitLdc_R4(1f / MathF.Pow(2, FBits)); + } + else if (Size == 1) + { + Context.EmitLdc_R8(1 / Math.Pow(2, FBits)); + } + else + { + throw new ArgumentOutOfRangeException(nameof(Size)); + } + + Context.Emit(OpCodes.Mul); + } + } + } +}
\ No newline at end of file diff --git a/ChocolArm64/Instruction/AInstEmitSimdHelper.cs b/ChocolArm64/Instruction/AInstEmitSimdHelper.cs new file mode 100644 index 00000000..20c8be26 --- /dev/null +++ b/ChocolArm64/Instruction/AInstEmitSimdHelper.cs @@ -0,0 +1,616 @@ +using ChocolArm64.Decoder; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System; +using System.Reflection; + +namespace ChocolArm64.Instruction +{ + static class AInstEmitSimdHelper + { + [Flags] + public enum OperFlags + { + Rd = 1 << 0, + Rn = 1 << 1, + Rm = 1 << 2, + Ra = 1 << 3, + + RnRm = Rn | Rm, + RdRn = Rd | Rn, + RaRnRm = Ra | Rn | Rm, + RdRnRm = Rd | Rn | Rm + } + + public static int GetImmShl(AOpCodeSimdShImm Op) + { + return Op.Imm - (8 << Op.Size); + } + + public static int GetImmShr(AOpCodeSimdShImm Op) + { + return (8 << (Op.Size + 1)) - Op.Imm; + } + + public static void EmitUnaryMathCall(AILEmitterCtx Context, string Name) + { + IAOpCodeSimd Op = (IAOpCodeSimd)Context.CurrOp; + + MethodInfo MthdInfo; + + if (Op.Size == 0) + { + MthdInfo = typeof(MathF).GetMethod(Name, new Type[] { typeof(float) }); + } + else if (Op.Size == 1) + { + MthdInfo = typeof(Math).GetMethod(Name, new Type[] { typeof(double) }); + } + else + { + throw new InvalidOperationException(); + } + + Context.EmitCall(MthdInfo); + } + + public static void EmitBinaryMathCall(AILEmitterCtx Context, string Name) + { + IAOpCodeSimd Op = (IAOpCodeSimd)Context.CurrOp; + + MethodInfo MthdInfo; + + if (Op.Size == 0) + { + MthdInfo = typeof(MathF).GetMethod(Name, new Type[] { typeof(float), typeof(float) }); + } + else if (Op.Size == 1) + { + MthdInfo = typeof(Math).GetMethod(Name, new Type[] { typeof(double), typeof(double) }); + } + else + { + throw new InvalidOperationException(); + } + + Context.EmitCall(MthdInfo); + } + + public static void EmitRoundMathCall(AILEmitterCtx Context, MidpointRounding RoundMode) + { + IAOpCodeSimd Op = (IAOpCodeSimd)Context.CurrOp; + + Context.EmitLdc_I4((int)RoundMode); + + MethodInfo MthdInfo; + + Type[] Types = new Type[] { null, typeof(MidpointRounding) }; + + Types[0] = Op.Size == 0 + ? typeof(float) + : typeof(double); + + if (Op.Size == 0) + { + MthdInfo = typeof(MathF).GetMethod(nameof(MathF.Round), Types); + } + else if (Op.Size == 1) + { + MthdInfo = typeof(Math).GetMethod(nameof(Math.Round), Types); + } + else + { + throw new InvalidOperationException(); + } + + Context.EmitCall(MthdInfo); + } + + public static void EmitScalarUnaryOpSx(AILEmitterCtx Context, Action Emit) + { + EmitScalarOp(Context, Emit, OperFlags.Rn, true); + } + + public static void EmitScalarBinaryOpSx(AILEmitterCtx Context, Action Emit) + { + EmitScalarOp(Context, Emit, OperFlags.RnRm, true); + } + + public static void EmitScalarUnaryOpZx(AILEmitterCtx Context, Action Emit) + { + EmitScalarOp(Context, Emit, OperFlags.Rn, false); + } + + public static void EmitScalarBinaryOpZx(AILEmitterCtx Context, Action Emit) + { + EmitScalarOp(Context, Emit, OperFlags.RnRm, false); + } + + public static void EmitScalarTernaryOpZx(AILEmitterCtx Context, Action Emit) + { + EmitScalarOp(Context, Emit, OperFlags.RdRnRm, false); + } + + public static void EmitScalarOp(AILEmitterCtx Context, Action Emit, OperFlags Opers, bool Signed) + { + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + if (Opers.HasFlag(OperFlags.Rd)) + { + EmitVectorExtract(Context, Op.Rd, 0, Op.Size, Signed); + } + + if (Opers.HasFlag(OperFlags.Rn)) + { + EmitVectorExtract(Context, Op.Rn, 0, Op.Size, Signed); + } + + if (Opers.HasFlag(OperFlags.Rm)) + { + EmitVectorExtract(Context, ((AOpCodeSimdReg)Op).Rm, 0, Op.Size, Signed); + } + + Emit(); + + EmitScalarSet(Context, Op.Rd, Op.Size); + } + + public static void EmitScalarUnaryOpF(AILEmitterCtx Context, Action Emit) + { + EmitScalarOpF(Context, Emit, OperFlags.Rn); + } + + public static void EmitScalarBinaryOpF(AILEmitterCtx Context, Action Emit) + { + EmitScalarOpF(Context, Emit, OperFlags.RnRm); + } + + public static void EmitScalarTernaryRaOpF(AILEmitterCtx Context, Action Emit) + { + EmitScalarOpF(Context, Emit, OperFlags.RaRnRm); + } + + public static void EmitScalarOpF(AILEmitterCtx Context, Action Emit, OperFlags Opers) + { + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + int SizeF = Op.Size & 1; + + if (Opers.HasFlag(OperFlags.Ra)) + { + EmitVectorExtractF(Context, ((AOpCodeSimdReg)Op).Ra, 0, SizeF); + } + + if (Opers.HasFlag(OperFlags.Rn)) + { + EmitVectorExtractF(Context, Op.Rn, 0, SizeF); + } + + if (Opers.HasFlag(OperFlags.Rm)) + { + EmitVectorExtractF(Context, ((AOpCodeSimdReg)Op).Rm, 0, SizeF); + } + + Emit(); + + EmitScalarSetF(Context, Op.Rd, SizeF); + } + + public static void EmitVectorBinaryOpF(AILEmitterCtx Context, Action Emit) + { + EmitVectorOpF(Context, Emit, OperFlags.RnRm); + } + + public static void EmitVectorTernaryOpF(AILEmitterCtx Context, Action Emit) + { + EmitVectorOpF(Context, Emit, OperFlags.RdRnRm); + } + + public static void EmitVectorBinaryOpByElemF(AILEmitterCtx Context, Action Emit) + { + AOpCodeSimdRegElem Op = (AOpCodeSimdRegElem)Context.CurrOp; + + EmitVectorOpByElemF(Context, Emit, Op.Index, Ternary: false); + } + + public static void EmitVectorTernaryOpByElemF(AILEmitterCtx Context, Action Emit) + { + AOpCodeSimdRegElem Op = (AOpCodeSimdRegElem)Context.CurrOp; + + EmitVectorOpByElemF(Context, Emit, Op.Index, Ternary: true); + } + + public static void EmitVectorOpF(AILEmitterCtx Context, Action Emit, OperFlags Opers) + { + AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; + + int SizeF = Op.Size & 1; + + int Bytes = Context.CurrOp.GetBitsCount() >> 3; + + for (int Index = 0; Index < (Bytes >> SizeF + 2); Index++) + { + if (Opers.HasFlag(OperFlags.Rd)) + { + EmitVectorExtractF(Context, Op.Rd, Index, SizeF); + } + + if (Opers.HasFlag(OperFlags.Rn)) + { + EmitVectorExtractF(Context, Op.Rn, Index, SizeF); + } + + if (Opers.HasFlag(OperFlags.Rm)) + { + EmitVectorExtractF(Context, Op.Rm, Index, SizeF); + } + + Emit(); + + EmitVectorInsertF(Context, Op.Rd, Index, SizeF); + } + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + + public static void EmitVectorOpByElemF(AILEmitterCtx Context, Action Emit, int Elem, bool Ternary) + { + AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; + + int SizeF = Op.Size & 1; + + int Bytes = Context.CurrOp.GetBitsCount() >> 3; + + for (int Index = 0; Index < (Bytes >> SizeF + 2); Index++) + { + if (Ternary) + { + EmitVectorExtractF(Context, Op.Rd, Index, SizeF); + } + + EmitVectorExtractF(Context, Op.Rn, Index, SizeF); + EmitVectorExtractF(Context, Op.Rm, Elem, SizeF); + + Emit(); + + EmitVectorInsertTmpF(Context, Index, SizeF); + } + + Context.EmitLdvectmp(); + Context.EmitStvec(Op.Rd); + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + + public static void EmitVectorUnaryOpSx(AILEmitterCtx Context, Action Emit) + { + EmitVectorOp(Context, Emit, OperFlags.Rn, true); + } + + public static void EmitVectorBinaryOpSx(AILEmitterCtx Context, Action Emit) + { + EmitVectorOp(Context, Emit, OperFlags.RnRm, true); + } + + public static void EmitVectorUnaryOpZx(AILEmitterCtx Context, Action Emit) + { + EmitVectorOp(Context, Emit, OperFlags.Rn, false); + } + + public static void EmitVectorBinaryOpZx(AILEmitterCtx Context, Action Emit) + { + EmitVectorOp(Context, Emit, OperFlags.RnRm, false); + } + + public static void EmitVectorTernaryOpZx(AILEmitterCtx Context, Action Emit) + { + EmitVectorOp(Context, Emit, OperFlags.RdRnRm, false); + } + + public static void EmitVectorOp(AILEmitterCtx Context, Action Emit, OperFlags Opers, bool Signed) + { + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + int Bytes = Context.CurrOp.GetBitsCount() >> 3; + + for (int Index = 0; Index < (Bytes >> Op.Size); Index++) + { + if (Opers.HasFlag(OperFlags.Rd)) + { + EmitVectorExtract(Context, Op.Rd, Index, Op.Size, Signed); + } + + if (Opers.HasFlag(OperFlags.Rn)) + { + EmitVectorExtract(Context, Op.Rn, Index, Op.Size, Signed); + } + + if (Opers.HasFlag(OperFlags.Rm)) + { + EmitVectorExtract(Context, ((AOpCodeSimdReg)Op).Rm, Index, Op.Size, Signed); + } + + Emit(); + + EmitVectorInsert(Context, Op.Rd, Index, Op.Size); + } + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + + public static void EmitVectorImmUnaryOp(AILEmitterCtx Context, Action Emit) + { + EmitVectorImmOp(Context, Emit, false); + } + + public static void EmitVectorImmBinaryOp(AILEmitterCtx Context, Action Emit) + { + EmitVectorImmOp(Context, Emit, true); + } + + public static void EmitVectorImmOp(AILEmitterCtx Context, Action Emit, bool Binary) + { + AOpCodeSimdImm Op = (AOpCodeSimdImm)Context.CurrOp; + + int Bytes = Context.CurrOp.GetBitsCount() >> 3; + + for (int Index = 0; Index < (Bytes >> Op.Size); Index++) + { + if (Binary) + { + EmitVectorExtractZx(Context, Op.Rd, Index, Op.Size); + } + + Context.EmitLdc_I8(Op.Imm); + + Emit(); + + EmitVectorInsert(Context, Op.Rd, Index, Op.Size); + } + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + + public static void EmitVectorWidenRmBinaryOpSx(AILEmitterCtx Context, Action Emit) + { + EmitVectorWidenRmBinaryOp(Context, Emit, true); + } + + public static void EmitVectorWidenRmBinaryOpZx(AILEmitterCtx Context, Action Emit) + { + EmitVectorWidenRmBinaryOp(Context, Emit, false); + } + + public static void EmitVectorWidenRmBinaryOp(AILEmitterCtx Context, Action Emit, bool Signed) + { + AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; + + int Elems = 8 >> Op.Size; + + int Part = Op.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0; + + for (int Index = 0; Index < Elems; Index++) + { + EmitVectorExtract(Context, Op.Rn, Index, Op.Size + 1, Signed); + EmitVectorExtract(Context, Op.Rm, Part + Index, Op.Size, Signed); + + Emit(); + + EmitVectorInsertTmp(Context, Index, Op.Size + 1); + } + + Context.EmitLdvectmp(); + Context.EmitStvec(Op.Rd); + } + + public static void EmitVectorWidenRnRmBinaryOpSx(AILEmitterCtx Context, Action Emit) + { + EmitVectorWidenRnRmBinaryOp(Context, Emit, true); + } + + public static void EmitVectorWidenRnRmBinaryOpZx(AILEmitterCtx Context, Action Emit) + { + EmitVectorWidenRnRmBinaryOp(Context, Emit, false); + } + + public static void EmitVectorWidenRnRmBinaryOp(AILEmitterCtx Context, Action Emit, bool Signed) + { + AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; + + int Elems = 8 >> Op.Size; + + int Part = Op.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0; + + for (int Index = 0; Index < Elems; Index++) + { + EmitVectorExtract(Context, Op.Rn, Part + Index, Op.Size, Signed); + EmitVectorExtract(Context, Op.Rm, Part + Index, Op.Size, Signed); + + Emit(); + + EmitVectorInsertTmp(Context, Index, Op.Size + 1); + } + + Context.EmitLdvectmp(); + Context.EmitStvec(Op.Rd); + } + + public static void EmitScalarSet(AILEmitterCtx Context, int Reg, int Size) + { + EmitVectorZeroAll(Context, Reg); + EmitVectorInsert(Context, Reg, 0, Size); + } + + public static void EmitScalarSetF(AILEmitterCtx Context, int Reg, int Size) + { + EmitVectorZeroAll(Context, Reg); + EmitVectorInsertF(Context, Reg, 0, Size); + } + + public static void EmitVectorExtractSx(AILEmitterCtx Context, int Reg, int Index, int Size) + { + EmitVectorExtract(Context, Reg, Index, Size, true); + } + + public static void EmitVectorExtractZx(AILEmitterCtx Context, int Reg, int Index, int Size) + { + EmitVectorExtract(Context, Reg, Index, Size, false); + } + + public static void EmitVectorExtract(AILEmitterCtx Context, int Reg, int Index, int Size, bool Signed) + { + if (Size < 0 || Size > 3) + { + throw new ArgumentOutOfRangeException(nameof(Size)); + } + + IAOpCodeSimd Op = (IAOpCodeSimd)Context.CurrOp; + + Context.EmitLdvec(Reg); + Context.EmitLdc_I4(Index); + Context.EmitLdc_I4(Size); + + ASoftFallback.EmitCall(Context, Signed + ? nameof(ASoftFallback.VectorExtractIntSx) + : nameof(ASoftFallback.VectorExtractIntZx)); + } + + public static void EmitVectorExtractF(AILEmitterCtx Context, int Reg, int Index, int Size) + { + Context.EmitLdvec(Reg); + Context.EmitLdc_I4(Index); + + if (Size == 0) + { + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.VectorExtractSingle)); + } + else if (Size == 1) + { + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.VectorExtractDouble)); + } + else + { + throw new ArgumentOutOfRangeException(nameof(Size)); + } + } + + public static void EmitVectorZeroAll(AILEmitterCtx Context, int Rd) + { + EmitVectorZeroLower(Context, Rd); + EmitVectorZeroUpper(Context, Rd); + } + + public static void EmitVectorZeroLower(AILEmitterCtx Context, int Rd) + { + EmitVectorInsert(Context, Rd, 0, 3, 0); + } + + public static void EmitVectorZeroUpper(AILEmitterCtx Context, int Rd) + { + EmitVectorInsert(Context, Rd, 1, 3, 0); + } + + public static void EmitVectorInsert(AILEmitterCtx Context, int Reg, int Index, int Size) + { + if (Size < 0 || Size > 3) + { + throw new ArgumentOutOfRangeException(nameof(Size)); + } + + Context.EmitLdvec(Reg); + Context.EmitLdc_I4(Index); + Context.EmitLdc_I4(Size); + + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.VectorInsertInt)); + + Context.EmitStvec(Reg); + } + + public static void EmitVectorInsertTmp(AILEmitterCtx Context, int Index, int Size) + { + if (Size < 0 || Size > 3) + { + throw new ArgumentOutOfRangeException(nameof(Size)); + } + + Context.EmitLdvectmp(); + Context.EmitLdc_I4(Index); + Context.EmitLdc_I4(Size); + + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.VectorInsertInt)); + + Context.EmitStvectmp(); + } + + public static void EmitVectorInsert(AILEmitterCtx Context, int Reg, int Index, int Size, long Value) + { + if (Size < 0 || Size > 3) + { + throw new ArgumentOutOfRangeException(nameof(Size)); + } + + Context.EmitLdc_I8(Value); + Context.EmitLdvec(Reg); + Context.EmitLdc_I4(Index); + Context.EmitLdc_I4(Size); + + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.VectorInsertInt)); + + Context.EmitStvec(Reg); + } + + public static void EmitVectorInsertF(AILEmitterCtx Context, int Reg, int Index, int Size) + { + Context.EmitLdvec(Reg); + Context.EmitLdc_I4(Index); + + if (Size == 0) + { + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.VectorInsertSingle)); + } + else if (Size == 1) + { + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.VectorInsertDouble)); + } + else + { + throw new ArgumentOutOfRangeException(nameof(Size)); + } + + Context.EmitStvec(Reg); + } + + public static void EmitVectorInsertTmpF(AILEmitterCtx Context, int Index, int Size) + { + Context.EmitLdvectmp(); + Context.EmitLdc_I4(Index); + + if (Size == 0) + { + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.VectorInsertSingle)); + } + else if (Size == 1) + { + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.VectorInsertDouble)); + } + else + { + throw new ArgumentOutOfRangeException(nameof(Size)); + } + + Context.EmitStvectmp(); + } + } +}
\ No newline at end of file diff --git a/ChocolArm64/Instruction/AInstEmitSimdLogical.cs b/ChocolArm64/Instruction/AInstEmitSimdLogical.cs new file mode 100644 index 00000000..ea4b17b3 --- /dev/null +++ b/ChocolArm64/Instruction/AInstEmitSimdLogical.cs @@ -0,0 +1,69 @@ +using ChocolArm64.Translation; +using System.Reflection.Emit; + +using static ChocolArm64.Instruction.AInstEmitSimdHelper; + +namespace ChocolArm64.Instruction +{ + static partial class AInstEmit + { + public static void And_V(AILEmitterCtx Context) + { + EmitVectorBinaryOpZx(Context, () => Context.Emit(OpCodes.And)); + } + + public static void Bic_V(AILEmitterCtx Context) + { + EmitVectorBinaryOpZx(Context, () => + { + Context.Emit(OpCodes.Not); + Context.Emit(OpCodes.And); + }); + } + + public static void Bic_Vi(AILEmitterCtx Context) + { + EmitVectorImmBinaryOp(Context, () => + { + Context.Emit(OpCodes.Not); + Context.Emit(OpCodes.And); + }); + } + + public static void Bsl_V(AILEmitterCtx Context) + { + EmitVectorTernaryOpZx(Context, () => + { + Context.EmitSttmp(); + Context.EmitLdtmp(); + + Context.Emit(OpCodes.Xor); + Context.Emit(OpCodes.And); + + Context.EmitLdtmp(); + + Context.Emit(OpCodes.Xor); + }); + } + + public static void Eor_V(AILEmitterCtx Context) + { + EmitVectorBinaryOpZx(Context, () => Context.Emit(OpCodes.Xor)); + } + + public static void Not_V(AILEmitterCtx Context) + { + EmitVectorUnaryOpZx(Context, () => Context.Emit(OpCodes.Not)); + } + + public static void Orr_V(AILEmitterCtx Context) + { + EmitVectorBinaryOpZx(Context, () => Context.Emit(OpCodes.Or)); + } + + public static void Orr_Vi(AILEmitterCtx Context) + { + EmitVectorImmBinaryOp(Context, () => Context.Emit(OpCodes.Or)); + } + } +}
\ No newline at end of file diff --git a/ChocolArm64/Instruction/AInstEmitSimdMemory.cs b/ChocolArm64/Instruction/AInstEmitSimdMemory.cs new file mode 100644 index 00000000..d98ec012 --- /dev/null +++ b/ChocolArm64/Instruction/AInstEmitSimdMemory.cs @@ -0,0 +1,184 @@ +using ChocolArm64.Decoder; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System; +using System.Reflection.Emit; + +using static ChocolArm64.Instruction.AInstEmitMemoryHelper; +using static ChocolArm64.Instruction.AInstEmitSimdHelper; + +namespace ChocolArm64.Instruction +{ + static partial class AInstEmit + { + public static void Ld__Vms(AILEmitterCtx Context) + { + EmitSimdMemMs(Context, IsLoad: true); + } + + public static void Ld__Vss(AILEmitterCtx Context) + { + EmitSimdMemSs(Context, IsLoad: true); + } + + public static void St__Vms(AILEmitterCtx Context) + { + EmitSimdMemMs(Context, IsLoad: false); + } + + public static void St__Vss(AILEmitterCtx Context) + { + EmitSimdMemSs(Context, IsLoad: false); + } + + private static void EmitSimdMemMs(AILEmitterCtx Context, bool IsLoad) + { + AOpCodeSimdMemMs Op = (AOpCodeSimdMemMs)Context.CurrOp; + + int Offset = 0; + + for (int Rep = 0; Rep < Op.Reps; Rep++) + for (int Elem = 0; Elem < Op.Elems; Elem++) + for (int SElem = 0; SElem < Op.SElems; SElem++) + { + int Rtt = (Op.Rt + Rep + SElem) & 0x1f; + + if (IsLoad) + { + Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); + Context.EmitLdint(Op.Rn); + Context.EmitLdc_I8(Offset); + + Context.Emit(OpCodes.Add); + + EmitReadZxCall(Context, Op.Size); + + EmitVectorInsert(Context, Rtt, Elem, Op.Size); + + if (Op.RegisterSize == ARegisterSize.SIMD64 && Elem == Op.Elems - 1) + { + EmitVectorZeroUpper(Context, Rtt); + } + } + else + { + Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); + Context.EmitLdint(Op.Rn); + Context.EmitLdc_I8(Offset); + + Context.Emit(OpCodes.Add); + + EmitVectorExtractZx(Context, Rtt, Elem, Op.Size); + + EmitWriteCall(Context, Op.Size); + } + + Offset += 1 << Op.Size; + } + + if (Op.WBack) + { + EmitSimdMemWBack(Context, Offset); + } + } + + private static void EmitSimdMemSs(AILEmitterCtx Context, bool IsLoad) + { + AOpCodeSimdMemSs Op = (AOpCodeSimdMemSs)Context.CurrOp; + + int Offset = 0; + + void EmitMemAddress() + { + Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); + Context.EmitLdint(Op.Rn); + Context.EmitLdc_I8(Offset); + + Context.Emit(OpCodes.Add); + } + + if (Op.Replicate) + { + //Only loads uses the replicate mode. + if (!IsLoad) + { + throw new InvalidOperationException(); + } + + int Bytes = Context.CurrOp.GetBitsCount() >> 3; + + for (int SElem = 0; SElem < Op.SElems; SElem++) + { + int Rt = (Op.Rt + SElem) & 0x1f; + + for (int Index = 0; Index < (Bytes >> Op.Size); Index++) + { + EmitMemAddress(); + + EmitReadZxCall(Context, Op.Size); + + EmitVectorInsert(Context, Rt, Index, Op.Size); + } + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Rt); + } + + Offset += 1 << Op.Size; + } + } + else + { + for (int SElem = 0; SElem < Op.SElems; SElem++) + { + int Rt = (Op.Rt + SElem) & 0x1f; + + if (IsLoad) + { + EmitMemAddress(); + + EmitReadZxCall(Context, Op.Size); + + EmitVectorInsert(Context, Rt, Op.Index, Op.Size); + } + else + { + EmitMemAddress(); + + EmitVectorExtractZx(Context, Rt, Op.Index, Op.Size); + + EmitWriteCall(Context, Op.Size); + } + + Offset += 1 << Op.Size; + } + } + + if (Op.WBack) + { + EmitSimdMemWBack(Context, Offset); + } + } + + private static void EmitSimdMemWBack(AILEmitterCtx Context, int Offset) + { + AOpCodeMemReg Op = (AOpCodeMemReg)Context.CurrOp; + + Context.EmitLdint(Op.Rn); + + if (Op.Rm != AThreadState.ZRIndex) + { + Context.EmitLdint(Op.Rm); + } + else + { + Context.EmitLdc_I8(Offset); + } + + Context.Emit(OpCodes.Add); + + Context.EmitStint(Op.Rn); + } + } +}
\ No newline at end of file diff --git a/ChocolArm64/Instruction/AInstEmitSimdMove.cs b/ChocolArm64/Instruction/AInstEmitSimdMove.cs new file mode 100644 index 00000000..aabb8f34 --- /dev/null +++ b/ChocolArm64/Instruction/AInstEmitSimdMove.cs @@ -0,0 +1,333 @@ +using ChocolArm64.Decoder; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System; +using System.Reflection.Emit; + +using static ChocolArm64.Instruction.AInstEmitSimdHelper; + +namespace ChocolArm64.Instruction +{ + static partial class AInstEmit + { + public static void Dup_Gp(AILEmitterCtx Context) + { + AOpCodeSimdIns Op = (AOpCodeSimdIns)Context.CurrOp; + + int Bytes = Context.CurrOp.GetBitsCount() >> 3; + + for (int Index = 0; Index < (Bytes >> Op.Size); Index++) + { + Context.EmitLdintzr(Op.Rn); + + EmitVectorInsert(Context, Op.Rd, Index, Op.Size); + } + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + + public static void Dup_S(AILEmitterCtx Context) + { + AOpCodeSimdIns Op = (AOpCodeSimdIns)Context.CurrOp; + + EmitVectorExtractZx(Context, Op.Rn, Op.DstIndex, Op.Size); + + EmitScalarSet(Context, Op.Rd, Op.Size); + } + + public static void Dup_V(AILEmitterCtx Context) + { + AOpCodeSimdIns Op = (AOpCodeSimdIns)Context.CurrOp; + + int Bytes = Context.CurrOp.GetBitsCount() >> 3; + + for (int Index = 0; Index < (Bytes >> Op.Size); Index++) + { + EmitVectorExtractZx(Context, Op.Rn, Op.DstIndex, Op.Size); + + EmitVectorInsert(Context, Op.Rd, Index, Op.Size); + } + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + + public static void Fcsel_S(AILEmitterCtx Context) + { + AOpCodeSimdFcond Op = (AOpCodeSimdFcond)Context.CurrOp; + + AILLabel LblTrue = new AILLabel(); + AILLabel LblEnd = new AILLabel(); + + Context.EmitCondBranch(LblTrue, Op.Cond); + + EmitVectorExtractF(Context, Op.Rm, 0, Op.Size); + + Context.Emit(OpCodes.Br_S, LblEnd); + + Context.MarkLabel(LblTrue); + + EmitVectorExtractF(Context, Op.Rn, 0, Op.Size); + + Context.MarkLabel(LblEnd); + + EmitScalarSetF(Context, Op.Rd, Op.Size); + } + + public static void Fmov_Ftoi(AILEmitterCtx Context) + { + AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp; + + EmitVectorExtractZx(Context, Op.Rn, 0, 3); + + EmitIntZeroHigherIfNeeded(Context); + + Context.EmitStintzr(Op.Rd); + } + + public static void Fmov_Ftoi1(AILEmitterCtx Context) + { + AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp; + + EmitVectorExtractZx(Context, Op.Rn, 1, 3); + + EmitIntZeroHigherIfNeeded(Context); + + Context.EmitStintzr(Op.Rd); + } + + public static void Fmov_Itof(AILEmitterCtx Context) + { + AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp; + + Context.EmitLdintzr(Op.Rn); + + EmitIntZeroHigherIfNeeded(Context); + + EmitScalarSet(Context, Op.Rd, 3); + } + + public static void Fmov_Itof1(AILEmitterCtx Context) + { + AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp; + + Context.EmitLdintzr(Op.Rn); + + EmitIntZeroHigherIfNeeded(Context); + + EmitVectorInsert(Context, Op.Rd, 1, 3); + } + + public static void Fmov_S(AILEmitterCtx Context) + { + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + EmitVectorExtractF(Context, Op.Rn, 0, Op.Size); + + EmitScalarSetF(Context, Op.Rd, Op.Size); + } + + public static void Fmov_Si(AILEmitterCtx Context) + { + AOpCodeSimdFmov Op = (AOpCodeSimdFmov)Context.CurrOp; + + Context.EmitLdc_I8(Op.Imm); + + EmitScalarSet(Context, Op.Rd, Op.Size + 2); + } + + public static void Fmov_V(AILEmitterCtx Context) + { + AOpCodeSimdImm Op = (AOpCodeSimdImm)Context.CurrOp; + + int Elems = Op.RegisterSize == ARegisterSize.SIMD128 ? 4 : 2; + + for (int Index = 0; Index < (Elems >> Op.Size); Index++) + { + Context.EmitLdc_I8(Op.Imm); + + EmitVectorInsert(Context, Op.Rd, Index, Op.Size + 2); + } + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + + public static void Ins_Gp(AILEmitterCtx Context) + { + AOpCodeSimdIns Op = (AOpCodeSimdIns)Context.CurrOp; + + Context.EmitLdintzr(Op.Rn); + + EmitVectorInsert(Context, Op.Rd, Op.DstIndex, Op.Size); + } + + public static void Ins_V(AILEmitterCtx Context) + { + AOpCodeSimdIns Op = (AOpCodeSimdIns)Context.CurrOp; + + EmitVectorExtractZx(Context, Op.Rn, Op.SrcIndex, Op.Size); + + EmitVectorInsert(Context, Op.Rd, Op.DstIndex, Op.Size); + } + + public static void Movi_V(AILEmitterCtx Context) + { + EmitVectorImmUnaryOp(Context, () => { }); + } + + public static void Mvni_V(AILEmitterCtx Context) + { + EmitVectorImmUnaryOp(Context, () => Context.Emit(OpCodes.Not)); + } + + public static void Tbl_V(AILEmitterCtx Context) + { + AOpCodeSimdTbl Op = (AOpCodeSimdTbl)Context.CurrOp; + + Context.EmitLdvec(Op.Rm); + + for (int Index = 0; Index < Op.Size; Index++) + { + Context.EmitLdvec((Op.Rn + Index) & 0x1f); + } + + switch (Op.Size) + { + case 1: ASoftFallback.EmitCall(Context, + nameof(ASoftFallback.Tbl1_V64), + nameof(ASoftFallback.Tbl1_V128)); break; + + case 2: ASoftFallback.EmitCall(Context, + nameof(ASoftFallback.Tbl2_V64), + nameof(ASoftFallback.Tbl2_V128)); break; + + case 3: ASoftFallback.EmitCall(Context, + nameof(ASoftFallback.Tbl3_V64), + nameof(ASoftFallback.Tbl3_V128)); break; + + case 4: ASoftFallback.EmitCall(Context, + nameof(ASoftFallback.Tbl4_V64), + nameof(ASoftFallback.Tbl4_V128)); break; + + default: throw new InvalidOperationException(); + } + + Context.EmitStvec(Op.Rd); + } + + public static void Umov_S(AILEmitterCtx Context) + { + AOpCodeSimdIns Op = (AOpCodeSimdIns)Context.CurrOp; + + EmitVectorExtractZx(Context, Op.Rn, Op.DstIndex, Op.Size); + + Context.EmitStintzr(Op.Rd); + } + + public static void Uzp1_V(AILEmitterCtx Context) + { + EmitVectorUnzip(Context, Part: 0); + } + + public static void Uzp2_V(AILEmitterCtx Context) + { + EmitVectorUnzip(Context, Part: 1); + } + + public static void Xtn_V(AILEmitterCtx Context) + { + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + int Elems = 8 >> Op.Size; + + int Part = Op.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0; + + for (int Index = 0; Index < Elems; Index++) + { + EmitVectorExtractZx(Context, Op.Rn, Index, Op.Size + 1); + + EmitVectorInsert(Context, Op.Rd, Part + Index, Op.Size); + } + + if (Part == 0) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + + public static void Zip1_V(AILEmitterCtx Context) + { + EmitVectorZip(Context, Part: 0); + } + + public static void Zip2_V(AILEmitterCtx Context) + { + EmitVectorZip(Context, Part: 1); + } + + private static void EmitIntZeroHigherIfNeeded(AILEmitterCtx Context) + { + if (Context.CurrOp.RegisterSize == ARegisterSize.Int32) + { + Context.Emit(OpCodes.Conv_U4); + Context.Emit(OpCodes.Conv_U8); + } + } + + private static void EmitVectorUnzip(AILEmitterCtx Context, int Part) + { + AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; + + int Bytes = Context.CurrOp.GetBitsCount() >> 3; + + int Elems = Bytes >> Op.Size; + int Half = Elems >> 1; + + for (int Index = 0; Index < Elems; Index++) + { + int Elem = Part + ((Index & (Half - 1)) << 1); + + EmitVectorExtractZx(Context, Index < Half ? Op.Rn : Op.Rm, Elem, Op.Size); + + EmitVectorInsert(Context, Op.Rd, Index, Op.Size); + } + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + + private static void EmitVectorZip(AILEmitterCtx Context, int Part) + { + AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; + + int Bytes = Context.CurrOp.GetBitsCount() >> 3; + + int Elems = Bytes >> Op.Size; + int Half = Elems >> 1; + + for (int Index = 0; Index < Elems; Index++) + { + int Elem = Part * Half + (Index >> 1); + + EmitVectorExtractZx(Context, (Index & 1) == 0 ? Op.Rn : Op.Rm, Elem, Op.Size); + + EmitVectorInsert(Context, Op.Rd, Index, Op.Size); + } + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + } +}
\ No newline at end of file diff --git a/ChocolArm64/Instruction/AInstEmitSimdShift.cs b/ChocolArm64/Instruction/AInstEmitSimdShift.cs new file mode 100644 index 00000000..8740ba4d --- /dev/null +++ b/ChocolArm64/Instruction/AInstEmitSimdShift.cs @@ -0,0 +1,297 @@ +using ChocolArm64.Decoder; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System; +using System.Reflection.Emit; + +using static ChocolArm64.Instruction.AInstEmitSimdHelper; + +namespace ChocolArm64.Instruction +{ + static partial class AInstEmit + { + public static void Shl_S(AILEmitterCtx Context) + { + AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; + + EmitVectorExtractZx(Context, Op.Rn, 0, Op.Size); + + Context.EmitLdc_I4(GetImmShl(Op)); + + Context.Emit(OpCodes.Shl); + + EmitScalarSet(Context, Op.Rd, Op.Size); + } + + public static void Shl_V(AILEmitterCtx Context) + { + AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; + + int Shift = Op.Imm - (8 << Op.Size); + + EmitVectorShImmBinaryZx(Context, () => Context.Emit(OpCodes.Shl), Shift); + } + + public static void Shrn_V(AILEmitterCtx Context) + { + AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; + + int Shift = (8 << (Op.Size + 1)) - Op.Imm; + + EmitVectorShImmNarrowBinaryZx(Context, () => Context.Emit(OpCodes.Shr_Un), Shift); + } + + public static void Sshl_V(AILEmitterCtx Context) + { + EmitVectorShl(Context, Signed: true); + } + + public static void Sshll_V(AILEmitterCtx Context) + { + AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; + + int Shift = Op.Imm - (8 << Op.Size); + + EmitVectorShImmWidenBinarySx(Context, () => Context.Emit(OpCodes.Shl), Shift); + } + + public static void Sshr_S(AILEmitterCtx Context) + { + AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; + + EmitVectorExtractSx(Context, Op.Rn, 0, Op.Size); + + Context.EmitLdc_I4(GetImmShr(Op)); + + Context.Emit(OpCodes.Shr); + + EmitScalarSet(Context, Op.Rd, Op.Size); + } + + public static void Sshr_V(AILEmitterCtx Context) + { + AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; + + int Shift = (8 << (Op.Size + 1)) - Op.Imm; + + EmitVectorShImmBinarySx(Context, () => Context.Emit(OpCodes.Shr), Shift); + } + + public static void Ushl_V(AILEmitterCtx Context) + { + EmitVectorShl(Context, Signed: false); + } + + public static void Ushll_V(AILEmitterCtx Context) + { + AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; + + int Shift = Op.Imm - (8 << Op.Size); + + EmitVectorShImmWidenBinaryZx(Context, () => Context.Emit(OpCodes.Shl), Shift); + } + + public static void Ushr_S(AILEmitterCtx Context) + { + AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; + + EmitScalarUnaryOpZx(Context, () => + { + Context.EmitLdc_I4(GetImmShr(Op)); + + Context.Emit(OpCodes.Shr_Un); + }); + } + + public static void Ushr_V(AILEmitterCtx Context) + { + AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; + + EmitVectorUnaryOpZx(Context, () => + { + Context.EmitLdc_I4(GetImmShr(Op)); + + Context.Emit(OpCodes.Shr_Un); + }); + } + + public static void Usra_V(AILEmitterCtx Context) + { + AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; + + Action Emit = () => + { + Context.EmitLdc_I4(GetImmShr(Op)); + + Context.Emit(OpCodes.Shr_Un); + Context.Emit(OpCodes.Add); + }; + + EmitVectorOp(Context, Emit, OperFlags.RdRn, Signed: false); + } + + private static void EmitVectorShl(AILEmitterCtx Context, bool Signed) + { + //This instruction shifts the value on vector A by the number of bits + //specified on the signed, lower 8 bits of vector B. If the shift value + //is greater or equal to the data size of each lane, then the result is zero. + //Additionally, negative shifts produces right shifts by the negated shift value. + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + int MaxShift = 8 << Op.Size; + + Action Emit = () => + { + AILLabel LblShl = new AILLabel(); + AILLabel LblZero = new AILLabel(); + AILLabel LblEnd = new AILLabel(); + + void EmitShift(OpCode ILOp) + { + Context.Emit(OpCodes.Dup); + + Context.EmitLdc_I4(MaxShift); + + Context.Emit(OpCodes.Bge_S, LblZero); + Context.Emit(ILOp); + Context.Emit(OpCodes.Br_S, LblEnd); + } + + Context.Emit(OpCodes.Conv_I1); + Context.Emit(OpCodes.Dup); + + Context.EmitLdc_I4(0); + + Context.Emit(OpCodes.Bge_S, LblShl); + Context.Emit(OpCodes.Neg); + + EmitShift(Signed + ? OpCodes.Shr + : OpCodes.Shr_Un); + + Context.MarkLabel(LblShl); + + EmitShift(OpCodes.Shl); + + Context.MarkLabel(LblZero); + + Context.Emit(OpCodes.Pop); + Context.Emit(OpCodes.Pop); + + Context.EmitLdc_I8(0); + + Context.MarkLabel(LblEnd); + }; + + if (Signed) + { + EmitVectorBinaryOpSx(Context, Emit); + } + else + { + EmitVectorBinaryOpZx(Context, Emit); + } + } + + private static void EmitVectorShImmBinarySx(AILEmitterCtx Context, Action Emit, int Imm) + { + EmitVectorShImmBinaryOp(Context, Emit, Imm, true); + } + + private static void EmitVectorShImmBinaryZx(AILEmitterCtx Context, Action Emit, int Imm) + { + EmitVectorShImmBinaryOp(Context, Emit, Imm, false); + } + + private static void EmitVectorShImmBinaryOp(AILEmitterCtx Context, Action Emit, int Imm, bool Signed) + { + AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; + + int Bytes = Context.CurrOp.GetBitsCount() >> 3; + + for (int Index = 0; Index < (Bytes >> Op.Size); Index++) + { + EmitVectorExtract(Context, Op.Rn, Index, Op.Size, Signed); + + Context.EmitLdc_I4(Imm); + + Emit(); + + EmitVectorInsert(Context, Op.Rd, Index, Op.Size); + } + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + + private static void EmitVectorShImmNarrowBinarySx(AILEmitterCtx Context, Action Emit, int Imm) + { + EmitVectorShImmNarrowBinaryOp(Context, Emit, Imm, true); + } + + private static void EmitVectorShImmNarrowBinaryZx(AILEmitterCtx Context, Action Emit, int Imm) + { + EmitVectorShImmNarrowBinaryOp(Context, Emit, Imm, false); + } + + private static void EmitVectorShImmNarrowBinaryOp(AILEmitterCtx Context, Action Emit, int Imm, bool Signed) + { + AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; + + int Elems = 8 >> Op.Size; + + int Part = Op.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0; + + for (int Index = 0; Index < Elems; Index++) + { + EmitVectorExtract(Context, Op.Rn, Index, Op.Size + 1, Signed); + + Context.EmitLdc_I4(Imm); + + Emit(); + + EmitVectorInsert(Context, Op.Rd, Part + Index, Op.Size); + } + + if (Part == 0) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + + private static void EmitVectorShImmWidenBinarySx(AILEmitterCtx Context, Action Emit, int Imm) + { + EmitVectorShImmWidenBinaryOp(Context, Emit, Imm, true); + } + + private static void EmitVectorShImmWidenBinaryZx(AILEmitterCtx Context, Action Emit, int Imm) + { + EmitVectorShImmWidenBinaryOp(Context, Emit, Imm, false); + } + + private static void EmitVectorShImmWidenBinaryOp(AILEmitterCtx Context, Action Emit, int Imm, bool Signed) + { + AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; + + int Elems = 8 >> Op.Size; + + int Part = Op.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0; + + for (int Index = 0; Index < Elems; Index++) + { + EmitVectorExtract(Context, Op.Rn, Part + Index, Op.Size, Signed); + + Context.EmitLdc_I4(Imm); + + Emit(); + + EmitVectorInsertTmp(Context, Index, Op.Size + 1); + } + + Context.EmitLdvectmp(); + Context.EmitStvec(Op.Rd); + } + } +}
\ No newline at end of file diff --git a/ChocolArm64/Instruction/AInstEmitSystem.cs b/ChocolArm64/Instruction/AInstEmitSystem.cs new file mode 100644 index 00000000..6754be7a --- /dev/null +++ b/ChocolArm64/Instruction/AInstEmitSystem.cs @@ -0,0 +1,122 @@ +using ChocolArm64.Decoder; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System; +using System.Reflection; +using System.Reflection.Emit; + +namespace ChocolArm64.Instruction +{ + static partial class AInstEmit + { + public static void Mrs(AILEmitterCtx Context) + { + AOpCodeSystem Op = (AOpCodeSystem)Context.CurrOp; + + Context.EmitLdarg(ATranslatedSub.StateArgIdx); + + string PropName; + + switch (GetPackedId(Op)) + { + case 0b11_011_0000_0000_001: PropName = nameof(AThreadState.CtrEl0); break; + case 0b11_011_0000_0000_111: PropName = nameof(AThreadState.DczidEl0); break; + case 0b11_011_0100_0100_000: PropName = nameof(AThreadState.Fpcr); break; + case 0b11_011_0100_0100_001: PropName = nameof(AThreadState.Fpsr); break; + case 0b11_011_1101_0000_010: PropName = nameof(AThreadState.TpidrEl0); break; + case 0b11_011_1101_0000_011: PropName = nameof(AThreadState.Tpidr); break; + case 0b11_011_1110_0000_001: PropName = nameof(AThreadState.CntpctEl0); break; + + default: throw new NotImplementedException($"Unknown MRS at {Op.Position:x16}"); + } + + Context.EmitCallPropGet(typeof(AThreadState), PropName); + + PropertyInfo PropInfo = typeof(AThreadState).GetProperty(PropName); + + if (PropInfo.PropertyType != typeof(long) && + PropInfo.PropertyType != typeof(ulong)) + { + Context.Emit(OpCodes.Conv_U8); + } + + Context.EmitStintzr(Op.Rt); + } + + public static void Msr(AILEmitterCtx Context) + { + AOpCodeSystem Op = (AOpCodeSystem)Context.CurrOp; + + Context.EmitLdarg(ATranslatedSub.StateArgIdx); + Context.EmitLdintzr(Op.Rt); + + string PropName; + + switch (GetPackedId(Op)) + { + case 0b11_011_0100_0100_000: PropName = nameof(AThreadState.Fpcr); break; + case 0b11_011_0100_0100_001: PropName = nameof(AThreadState.Fpsr); break; + case 0b11_011_1101_0000_010: PropName = nameof(AThreadState.TpidrEl0); break; + + default: throw new NotImplementedException($"Unknown MSR at {Op.Position:x16}"); + } + + PropertyInfo PropInfo = typeof(AThreadState).GetProperty(PropName); + + if (PropInfo.PropertyType != typeof(long) && + PropInfo.PropertyType != typeof(ulong)) + { + Context.Emit(OpCodes.Conv_U4); + } + + Context.EmitCallPropSet(typeof(AThreadState), PropName); + } + + public static void Nop(AILEmitterCtx Context) + { + //Do nothing. + } + + public static void Sys(AILEmitterCtx Context) + { + //This instruction is used to do some operations on the CPU like cache invalidation, + //address translation and the like. + //We treat it as no-op here since we don't have any cache being emulated anyway. + AOpCodeSystem Op = (AOpCodeSystem)Context.CurrOp; + + switch (GetPackedId(Op)) + { + case 0b11_011_0111_0100_001: + { + //DC ZVA + for (int Offs = 0; Offs < (4 << AThreadState.DczSizeLog2); Offs += 8) + { + Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); + Context.EmitLdint(Op.Rt); + Context.EmitLdc_I(Offs); + + Context.Emit(OpCodes.Add); + + Context.EmitLdc_I8(0); + + AInstEmitMemoryHelper.EmitWriteCall(Context, 3); + } + break; + } + } + } + + private static int GetPackedId(AOpCodeSystem Op) + { + int Id; + + Id = Op.Op2 << 0; + Id |= Op.CRm << 3; + Id |= Op.CRn << 7; + Id |= Op.Op1 << 11; + Id |= Op.Op0 << 14; + + return Id; + } + } +}
\ No newline at end of file diff --git a/ChocolArm64/Instruction/AInstEmitter.cs b/ChocolArm64/Instruction/AInstEmitter.cs new file mode 100644 index 00000000..8712a736 --- /dev/null +++ b/ChocolArm64/Instruction/AInstEmitter.cs @@ -0,0 +1,6 @@ +using ChocolArm64.Translation; + +namespace ChocolArm64.Instruction +{ + delegate void AInstEmitter(AILEmitterCtx Context); +}
\ No newline at end of file diff --git a/ChocolArm64/Instruction/ASoftFallback.cs b/ChocolArm64/Instruction/ASoftFallback.cs new file mode 100644 index 00000000..a57966ba --- /dev/null +++ b/ChocolArm64/Instruction/ASoftFallback.cs @@ -0,0 +1,316 @@ +using ChocolArm64.State; +using ChocolArm64.Translation; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace ChocolArm64.Instruction +{ + static class ASoftFallback + { + public static void EmitCall(AILEmitterCtx Context, string Name64, string Name128) + { + bool IsSimd64 = Context.CurrOp.RegisterSize == ARegisterSize.SIMD64; + + Context.EmitCall(typeof(ASoftFallback), IsSimd64 ? Name64 : Name128); + } + + public static void EmitCall(AILEmitterCtx Context, string MthdName) + { + Context.EmitCall(typeof(ASoftFallback), MthdName); + } + + public static uint CountLeadingZeros32(uint Value) => (uint)CountLeadingZeros(Value, 32); + public static ulong CountLeadingZeros64(ulong Value) => (ulong)CountLeadingZeros(Value, 64); + + private static ulong CountLeadingZeros(ulong Value, int Size) + { + int HighBit = Size - 1; + + for (int Bit = HighBit; Bit >= 0; Bit--) + { + if (((Value >> Bit) & 1) != 0) + { + return (ulong)(HighBit - Bit); + } + } + + return (ulong)Size; + } + + public static uint ReverseBits32(uint Value) + { + Value = ((Value & 0xaaaaaaaa) >> 1) | ((Value & 0x55555555) << 1); + Value = ((Value & 0xcccccccc) >> 2) | ((Value & 0x33333333) << 2); + Value = ((Value & 0xf0f0f0f0) >> 4) | ((Value & 0x0f0f0f0f) << 4); + Value = ((Value & 0xff00ff00) >> 8) | ((Value & 0x00ff00ff) << 8); + + return (Value >> 16) | (Value << 16); + } + + public static ulong ReverseBits64(ulong Value) + { + Value = ((Value & 0xaaaaaaaaaaaaaaaa) >> 1) | ((Value & 0x5555555555555555) << 1); + Value = ((Value & 0xcccccccccccccccc) >> 2) | ((Value & 0x3333333333333333) << 2); + Value = ((Value & 0xf0f0f0f0f0f0f0f0) >> 4) | ((Value & 0x0f0f0f0f0f0f0f0f) << 4); + Value = ((Value & 0xff00ff00ff00ff00) >> 8) | ((Value & 0x00ff00ff00ff00ff) << 8); + Value = ((Value & 0xffff0000ffff0000) >> 16) | ((Value & 0x0000ffff0000ffff) << 16); + + return (Value >> 32) | (Value << 32); + } + + public static uint ReverseBytes16_32(uint Value) => (uint)ReverseBytes16_64(Value); + public static uint ReverseBytes32_32(uint Value) => (uint)ReverseBytes32_64(Value); + + public static ulong ReverseBytes16_64(ulong Value) => ReverseBytes(Value, RevSize.Rev16); + public static ulong ReverseBytes32_64(ulong Value) => ReverseBytes(Value, RevSize.Rev32); + public static ulong ReverseBytes64(ulong Value) => ReverseBytes(Value, RevSize.Rev64); + + private enum RevSize + { + Rev16, + Rev32, + Rev64 + } + + private static ulong ReverseBytes(ulong Value, RevSize Size) + { + Value = ((Value & 0xff00ff00ff00ff00) >> 8) | ((Value & 0x00ff00ff00ff00ff) << 8); + + if (Size == RevSize.Rev16) + { + return Value; + } + + Value = ((Value & 0xffff0000ffff0000) >> 16) | ((Value & 0x0000ffff0000ffff) << 16); + + if (Size == RevSize.Rev32) + { + return Value; + } + + Value = ((Value & 0xffffffff00000000) >> 32) | ((Value & 0x00000000ffffffff) << 32); + + if (Size == RevSize.Rev64) + { + return Value; + } + + throw new ArgumentException(nameof(Size)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int SatF32ToS32(float Value) + { + if (float.IsNaN(Value)) return 0; + + return Value > int.MaxValue ? int.MaxValue : + Value < int.MinValue ? int.MinValue : (int)Value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static long SatF32ToS64(float Value) + { + if (float.IsNaN(Value)) return 0; + + return Value > long.MaxValue ? long.MaxValue : + Value < long.MinValue ? long.MinValue : (long)Value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint SatF32ToU32(float Value) + { + if (float.IsNaN(Value)) return 0; + + return Value > uint.MaxValue ? uint.MaxValue : + Value < uint.MinValue ? uint.MinValue : (uint)Value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ulong SatF32ToU64(float Value) + { + if (float.IsNaN(Value)) return 0; + + return Value > ulong.MaxValue ? ulong.MaxValue : + Value < ulong.MinValue ? ulong.MinValue : (ulong)Value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int SatF64ToS32(double Value) + { + if (double.IsNaN(Value)) return 0; + + return Value > int.MaxValue ? int.MaxValue : + Value < int.MinValue ? int.MinValue : (int)Value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static long SatF64ToS64(double Value) + { + if (double.IsNaN(Value)) return 0; + + return Value > long.MaxValue ? long.MaxValue : + Value < long.MinValue ? long.MinValue : (long)Value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint SatF64ToU32(double Value) + { + if (double.IsNaN(Value)) return 0; + + return Value > uint.MaxValue ? uint.MaxValue : + Value < uint.MinValue ? uint.MinValue : (uint)Value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ulong SatF64ToU64(double Value) + { + if (double.IsNaN(Value)) return 0; + + return Value > ulong.MaxValue ? ulong.MaxValue : + Value < ulong.MinValue ? ulong.MinValue : (ulong)Value; + } + + public static long SMulHi128(long LHS, long RHS) + { + return (long)(BigInteger.Multiply(LHS, RHS) >> 64); + } + + public static ulong UMulHi128(ulong LHS, ulong RHS) + { + return (ulong)(BigInteger.Multiply(LHS, RHS) >> 64); + } + + public static int CountSetBits8(byte Value) + { + return (Value >> 0) & 1 + (Value >> 1) & 1 + + (Value >> 2) & 1 + (Value >> 3) & 1 + + (Value >> 4) & 1 + (Value >> 5) & 1 + + (Value >> 6) & 1 + (Value >> 7); + } + + public static AVec Tbl1_V64(AVec Vector, AVec Tb0) + { + return Tbl(Vector, 8, Tb0); + } + + public static AVec Tbl1_V128(AVec Vector, AVec Tb0) + { + return Tbl(Vector, 16, Tb0); + } + + public static AVec Tbl2_V64(AVec Vector, AVec Tb0, AVec Tb1) + { + return Tbl(Vector, 8, Tb0, Tb1); + } + + public static AVec Tbl2_V128(AVec Vector, AVec Tb0, AVec Tb1) + { + return Tbl(Vector, 16, Tb0, Tb1); + } + + public static AVec Tbl3_V64(AVec Vector, AVec Tb0, AVec Tb1, AVec Tb2) + { + return Tbl(Vector, 8, Tb0, Tb1, Tb2); + } + + public static AVec Tbl3_V128(AVec Vector, AVec Tb0, AVec Tb1, AVec Tb2) + { + return Tbl(Vector, 16, Tb0, Tb1, Tb2); + } + + public static AVec Tbl4_V64(AVec Vector, AVec Tb0, AVec Tb1, AVec Tb2, AVec Tb3) + { + return Tbl(Vector, 8, Tb0, Tb1, Tb2, Tb3); + } + + public static AVec Tbl4_V128(AVec Vector, AVec Tb0, AVec Tb1, AVec Tb2, AVec Tb3) + { + return Tbl(Vector, 16, Tb0, Tb1, Tb2, Tb3); + } + + private static AVec Tbl(AVec Vector, int Bytes, params AVec[] Tb) + { + AVec Res = new AVec(); + + byte[] Table = new byte[Tb.Length * 16]; + + for (int Index = 0; Index < Tb.Length; Index++) + for (int Index2 = 0; Index2 < 16; Index2++) + { + Table[Index * 16 + Index2] = (byte)VectorExtractIntZx(Tb[Index], Index2, 0); + } + + for (int Index = 0; Index < Bytes; Index++) + { + byte TblIdx = (byte)VectorExtractIntZx(Vector, Index, 0); + + if (TblIdx < Table.Length) + { + Res = VectorInsertInt(Table[TblIdx], Res, Index, 0); + } + } + + return Res; + } + + public static ulong VectorExtractIntZx(AVec Vector, int Index, int Size) + { + switch (Size) + { + case 0: return Vector.ExtractByte (Index); + case 1: return Vector.ExtractUInt16(Index); + case 2: return Vector.ExtractUInt32(Index); + case 3: return Vector.ExtractUInt64(Index); + } + + throw new ArgumentOutOfRangeException(nameof(Size)); + } + + public static long VectorExtractIntSx(AVec Vector, int Index, int Size) + { + switch (Size) + { + case 0: return (sbyte)Vector.ExtractByte (Index); + case 1: return (short)Vector.ExtractUInt16(Index); + case 2: return (int)Vector.ExtractUInt32(Index); + case 3: return (long)Vector.ExtractUInt64(Index); + } + + throw new ArgumentOutOfRangeException(nameof(Size)); + } + + public static float VectorExtractSingle(AVec Vector, int Index) + { + return Vector.ExtractSingle(Index); + } + + public static double VectorExtractDouble(AVec Vector, int Index) + { + return Vector.ExtractDouble(Index); + } + + public static AVec VectorInsertSingle(float Value, AVec Vector, int Index) + { + return AVec.InsertSingle(Vector, Index, Value); + } + + public static AVec VectorInsertDouble(double Value, AVec Vector, int Index) + { + return AVec.InsertDouble(Vector, Index, Value); + } + + public static AVec VectorInsertInt(ulong Value, AVec Vector, int Index, int Size) + { + switch (Size) + { + case 0: return AVec.InsertByte (Vector, Index, (byte)Value); + case 1: return AVec.InsertUInt16(Vector, Index, (ushort)Value); + case 2: return AVec.InsertUInt32(Vector, Index, (uint)Value); + case 3: return AVec.InsertUInt64(Vector, Index, (ulong)Value); + } + + throw new ArgumentOutOfRangeException(nameof(Size)); + } + } +}
\ No newline at end of file |
