diff --git a/ABitUtils.cs b/ABitUtils.cs deleted file mode 100644 index a2654c2..0000000 --- a/ABitUtils.cs +++ /dev/null @@ -1,49 +0,0 @@ -namespace ChocolArm64 -{ - static class ABitUtils - { - public static int HighestBitSet32(int Value) - { - for (int Bit = 31; Bit >= 0; Bit--) - { - if (((Value >> Bit) & 1) != 0) - { - return Bit; - } - } - - return -1; - } - - private static readonly sbyte[] HbsNibbleTbl = { -1, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3 }; - - public static int HighestBitSetNibble(int Value) => HbsNibbleTbl[Value & 0b1111]; - - public static long Replicate(long Bits, int Size) - { - long Output = 0; - - for (int Bit = 0; Bit < 64; Bit += Size) - { - Output |= Bits << Bit; - } - - return Output; - } - - public static long FillWithOnes(int Bits) - { - return Bits == 64 ? -1L : (1L << Bits) - 1; - } - - public static long RotateRight(long Bits, int Shift, int Size) - { - return (long)RotateRight((ulong)Bits, Shift, Size); - } - - public static ulong RotateRight(ulong Bits, int Shift, int Size) - { - return (Bits >> Shift) | (Bits << (Size - Shift)); - } - } -} diff --git a/AOpCodeTable.cs b/AOpCodeTable.cs deleted file mode 100644 index cbdff47..0000000 --- a/AOpCodeTable.cs +++ /dev/null @@ -1,713 +0,0 @@ -using ChocolArm64.Decoder; -using ChocolArm64.Decoder32; -using ChocolArm64.Instruction; -using ChocolArm64.Instruction32; -using ChocolArm64.State; -using System; -using System.Collections.Generic; - -namespace ChocolArm64 -{ - static class AOpCodeTable - { - static AOpCodeTable() - { -#region "OpCode Table (AArch32)" - //Integer - SetA32("<<<<1010xxxxxxxxxxxxxxxxxxxxxxxx", A32InstInterpret.B, typeof(A32OpCodeBImmAl)); - SetA32("<<<<1011xxxxxxxxxxxxxxxxxxxxxxxx", A32InstInterpret.Bl, typeof(A32OpCodeBImmAl)); - SetA32("1111101xxxxxxxxxxxxxxxxxxxxxxxxx", A32InstInterpret.Blx, typeof(A32OpCodeBImmAl)); -#endregion - -#region "OpCode Table (AArch64)" - //Integer - SetA64("x0011010000xxxxx000000xxxxxxxxxx", AInstEmit.Adc, typeof(AOpCodeAluRs)); - SetA64("x0111010000xxxxx000000xxxxxxxxxx", AInstEmit.Adcs, typeof(AOpCodeAluRs)); - SetA64("x00100010xxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Add, typeof(AOpCodeAluImm)); - SetA64("00001011<<0xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Add, typeof(AOpCodeAluRs)); - SetA64("10001011<<0xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Add, typeof(AOpCodeAluRs)); - SetA64("x0001011001xxxxxxxx0xxxxxxxxxxxx", AInstEmit.Add, typeof(AOpCodeAluRx)); - SetA64("x0001011001xxxxxxxx100xxxxxxxxxx", AInstEmit.Add, typeof(AOpCodeAluRx)); - SetA64("x01100010xxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Adds, typeof(AOpCodeAluImm)); - SetA64("00101011<<0xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Adds, typeof(AOpCodeAluRs)); - SetA64("10101011<<0xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Adds, typeof(AOpCodeAluRs)); - SetA64("x0101011001xxxxxxxx0xxxxxxxxxxxx", AInstEmit.Adds, typeof(AOpCodeAluRx)); - SetA64("x0101011001xxxxxxxx100xxxxxxxxxx", AInstEmit.Adds, typeof(AOpCodeAluRx)); - SetA64("0xx10000xxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Adr, typeof(AOpCodeAdr)); - SetA64("1xx10000xxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Adrp, typeof(AOpCodeAdr)); - SetA64("0001001000xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.And, typeof(AOpCodeAluImm)); - SetA64("100100100xxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.And, typeof(AOpCodeAluImm)); - SetA64("00001010xx0xxxxx0xxxxxxxxxxxxxxx", AInstEmit.And, typeof(AOpCodeAluRs)); - SetA64("10001010xx0xxxxxxxxxxxxxxxxxxxxx", AInstEmit.And, typeof(AOpCodeAluRs)); - SetA64("0111001000xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ands, typeof(AOpCodeAluImm)); - SetA64("111100100xxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ands, typeof(AOpCodeAluImm)); - SetA64("01101010xx0xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Ands, typeof(AOpCodeAluRs)); - SetA64("11101010xx0xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ands, typeof(AOpCodeAluRs)); - SetA64("x0011010110xxxxx001010xxxxxxxxxx", AInstEmit.Asrv, typeof(AOpCodeAluRs)); - SetA64("000101xxxxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.B, typeof(AOpCodeBImmAl)); - SetA64("01010100xxxxxxxxxxxxxxxxxxx0xxxx", AInstEmit.B_Cond, typeof(AOpCodeBImmCond)); - SetA64("00110011000xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Bfm, typeof(AOpCodeBfm)); - SetA64("1011001101xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Bfm, typeof(AOpCodeBfm)); - SetA64("00001010xx1xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Bic, typeof(AOpCodeAluRs)); - SetA64("10001010xx1xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Bic, typeof(AOpCodeAluRs)); - SetA64("01101010xx1xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Bics, typeof(AOpCodeAluRs)); - SetA64("11101010xx1xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Bics, typeof(AOpCodeAluRs)); - SetA64("100101xxxxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Bl, typeof(AOpCodeBImmAl)); - SetA64("1101011000111111000000xxxxx00000", AInstEmit.Blr, typeof(AOpCodeBReg)); - SetA64("1101011000011111000000xxxxx00000", AInstEmit.Br, typeof(AOpCodeBReg)); - SetA64("11010100001xxxxxxxxxxxxxxxx00000", AInstEmit.Brk, typeof(AOpCodeException)); - SetA64("x0110101xxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Cbnz, typeof(AOpCodeBImmCmp)); - SetA64("x0110100xxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Cbz, typeof(AOpCodeBImmCmp)); - SetA64("x0111010010xxxxxxxxx10xxxxx0xxxx", AInstEmit.Ccmn, typeof(AOpCodeCcmpImm)); - SetA64("x0111010010xxxxxxxxx00xxxxx0xxxx", AInstEmit.Ccmn, typeof(AOpCodeCcmpReg)); - SetA64("x1111010010xxxxxxxxx10xxxxx0xxxx", AInstEmit.Ccmp, typeof(AOpCodeCcmpImm)); - SetA64("x1111010010xxxxxxxxx00xxxxx0xxxx", AInstEmit.Ccmp, typeof(AOpCodeCcmpReg)); - SetA64("11010101000000110011xxxx01011111", AInstEmit.Clrex, typeof(AOpCodeSystem)); - SetA64("x101101011000000000101xxxxxxxxxx", AInstEmit.Cls, typeof(AOpCodeAlu)); - SetA64("x101101011000000000100xxxxxxxxxx", AInstEmit.Clz, typeof(AOpCodeAlu)); - SetA64("00011010110xxxxx010000xxxxxxxxxx", AInstEmit.Crc32b, typeof(AOpCodeAluRs)); - SetA64("00011010110xxxxx010001xxxxxxxxxx", AInstEmit.Crc32h, typeof(AOpCodeAluRs)); - SetA64("00011010110xxxxx010010xxxxxxxxxx", AInstEmit.Crc32w, typeof(AOpCodeAluRs)); - SetA64("10011010110xxxxx010011xxxxxxxxxx", AInstEmit.Crc32x, typeof(AOpCodeAluRs)); - SetA64("00011010110xxxxx010100xxxxxxxxxx", AInstEmit.Crc32cb, typeof(AOpCodeAluRs)); - SetA64("00011010110xxxxx010101xxxxxxxxxx", AInstEmit.Crc32ch, typeof(AOpCodeAluRs)); - SetA64("00011010110xxxxx010110xxxxxxxxxx", AInstEmit.Crc32cw, typeof(AOpCodeAluRs)); - SetA64("10011010110xxxxx010111xxxxxxxxxx", AInstEmit.Crc32cx, typeof(AOpCodeAluRs)); - SetA64("x0011010100xxxxxxxxx00xxxxxxxxxx", AInstEmit.Csel, typeof(AOpCodeCsel)); - SetA64("x0011010100xxxxxxxxx01xxxxxxxxxx", AInstEmit.Csinc, typeof(AOpCodeCsel)); - SetA64("x1011010100xxxxxxxxx00xxxxxxxxxx", AInstEmit.Csinv, typeof(AOpCodeCsel)); - SetA64("x1011010100xxxxxxxxx01xxxxxxxxxx", AInstEmit.Csneg, typeof(AOpCodeCsel)); - SetA64("11010101000000110011xxxx10111111", AInstEmit.Dmb, typeof(AOpCodeSystem)); - SetA64("11010101000000110011xxxx10011111", AInstEmit.Dsb, typeof(AOpCodeSystem)); - SetA64("01001010xx1xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Eon, typeof(AOpCodeAluRs)); - SetA64("11001010xx1xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Eon, typeof(AOpCodeAluRs)); - SetA64("0101001000xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Eor, typeof(AOpCodeAluImm)); - SetA64("110100100xxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Eor, typeof(AOpCodeAluImm)); - SetA64("01001010xx0xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Eor, typeof(AOpCodeAluRs)); - SetA64("11001010xx0xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Eor, typeof(AOpCodeAluRs)); - SetA64("00010011100xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Extr, typeof(AOpCodeAluRs)); - SetA64("10010011110xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Extr, typeof(AOpCodeAluRs)); - SetA64("11010101000000110010xxxxxxx11111", AInstEmit.Hint, typeof(AOpCodeSystem)); - SetA64("11010101000000110011xxxx11011111", AInstEmit.Isb, typeof(AOpCodeSystem)); - SetA64("xx001000110xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Ldar, typeof(AOpCodeMemEx)); - SetA64("1x001000011xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Ldaxp, typeof(AOpCodeMemEx)); - SetA64("xx001000010xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Ldaxr, typeof(AOpCodeMemEx)); - SetA64("<<10100xx1xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ldp, typeof(AOpCodeMemPair)); - SetA64("xx111000010xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ldr, typeof(AOpCodeMemImm)); - SetA64("xx11100101xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ldr, typeof(AOpCodeMemImm)); - SetA64("xx111000011xxxxxxxxx10xxxxxxxxxx", AInstEmit.Ldr, typeof(AOpCodeMemReg)); - SetA64("xx011000xxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.LdrLit, typeof(AOpCodeMemLit)); - SetA64("0x1110001x0xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ldrs, typeof(AOpCodeMemImm)); - SetA64("0x1110011xxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ldrs, typeof(AOpCodeMemImm)); - SetA64("10111000100xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ldrs, typeof(AOpCodeMemImm)); - SetA64("1011100110xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ldrs, typeof(AOpCodeMemImm)); - SetA64("0x1110001x1xxxxxxxxx10xxxxxxxxxx", AInstEmit.Ldrs, typeof(AOpCodeMemReg)); - SetA64("10111000101xxxxxxxxx10xxxxxxxxxx", AInstEmit.Ldrs, typeof(AOpCodeMemReg)); - SetA64("xx001000010xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Ldxr, typeof(AOpCodeMemEx)); - SetA64("1x001000011xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Ldxp, typeof(AOpCodeMemEx)); - SetA64("x0011010110xxxxx001000xxxxxxxxxx", AInstEmit.Lslv, typeof(AOpCodeAluRs)); - SetA64("x0011010110xxxxx001001xxxxxxxxxx", AInstEmit.Lsrv, typeof(AOpCodeAluRs)); - SetA64("x0011011000xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Madd, typeof(AOpCodeMul)); - SetA64("0111001010xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Movk, typeof(AOpCodeMov)); - SetA64("111100101xxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Movk, typeof(AOpCodeMov)); - SetA64("0001001010xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Movn, typeof(AOpCodeMov)); - SetA64("100100101xxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Movn, typeof(AOpCodeMov)); - SetA64("0101001010xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Movz, typeof(AOpCodeMov)); - SetA64("110100101xxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Movz, typeof(AOpCodeMov)); - SetA64("110101010011xxxxxxxxxxxxxxxxxxxx", AInstEmit.Mrs, typeof(AOpCodeSystem)); - SetA64("110101010001xxxxxxxxxxxxxxxxxxxx", AInstEmit.Msr, typeof(AOpCodeSystem)); - SetA64("x0011011000xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Msub, typeof(AOpCodeMul)); - SetA64("11010101000000110010000000011111", AInstEmit.Nop, typeof(AOpCodeSystem)); - SetA64("00101010xx1xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Orn, typeof(AOpCodeAluRs)); - SetA64("10101010xx1xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Orn, typeof(AOpCodeAluRs)); - SetA64("0011001000xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Orr, typeof(AOpCodeAluImm)); - SetA64("101100100xxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Orr, typeof(AOpCodeAluImm)); - SetA64("00101010xx0xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Orr, typeof(AOpCodeAluRs)); - SetA64("10101010xx0xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Orr, typeof(AOpCodeAluRs)); - SetA64("1111100110xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Pfrm, typeof(AOpCodeMemImm)); - SetA64("11111000100xxxxxxxxx00xxxxxxxxxx", AInstEmit.Pfrm, typeof(AOpCodeMemImm)); - SetA64("11011000xxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Pfrm, typeof(AOpCodeMemLit)); - SetA64("x101101011000000000000xxxxxxxxxx", AInstEmit.Rbit, typeof(AOpCodeAlu)); - SetA64("1101011001011111000000xxxxx00000", AInstEmit.Ret, typeof(AOpCodeBReg)); - SetA64("x101101011000000000001xxxxxxxxxx", AInstEmit.Rev16, typeof(AOpCodeAlu)); - SetA64("x101101011000000000010xxxxxxxxxx", AInstEmit.Rev32, typeof(AOpCodeAlu)); - SetA64("1101101011000000000011xxxxxxxxxx", AInstEmit.Rev64, typeof(AOpCodeAlu)); - SetA64("x0011010110xxxxx001011xxxxxxxxxx", AInstEmit.Rorv, typeof(AOpCodeAluRs)); - SetA64("x1011010000xxxxx000000xxxxxxxxxx", AInstEmit.Sbc, typeof(AOpCodeAluRs)); - SetA64("x1111010000xxxxx000000xxxxxxxxxx", AInstEmit.Sbcs, typeof(AOpCodeAluRs)); - SetA64("00010011000xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Sbfm, typeof(AOpCodeBfm)); - SetA64("1001001101xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Sbfm, typeof(AOpCodeBfm)); - SetA64("x0011010110xxxxx000011xxxxxxxxxx", AInstEmit.Sdiv, typeof(AOpCodeAluRs)); - SetA64("10011011001xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Smaddl, typeof(AOpCodeMul)); - SetA64("10011011001xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Smsubl, typeof(AOpCodeMul)); - SetA64("10011011010xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Smulh, typeof(AOpCodeMul)); - SetA64("xx001000100xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Stlr, typeof(AOpCodeMemEx)); - SetA64("1x001000001xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Stlxp, typeof(AOpCodeMemEx)); - SetA64("xx001000000xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Stlxr, typeof(AOpCodeMemEx)); - SetA64("x010100xx0xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Stp, typeof(AOpCodeMemPair)); - SetA64("xx111000000xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Str, typeof(AOpCodeMemImm)); - SetA64("xx11100100xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Str, typeof(AOpCodeMemImm)); - SetA64("xx111000001xxxxxxxxx10xxxxxxxxxx", AInstEmit.Str, typeof(AOpCodeMemReg)); - SetA64("1x001000001xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Stxp, typeof(AOpCodeMemEx)); - SetA64("xx001000000xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Stxr, typeof(AOpCodeMemEx)); - SetA64("x10100010xxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Sub, typeof(AOpCodeAluImm)); - SetA64("01001011<<0xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Sub, typeof(AOpCodeAluRs)); - SetA64("11001011<<0xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Sub, typeof(AOpCodeAluRs)); - SetA64("x1001011001xxxxxxxx0xxxxxxxxxxxx", AInstEmit.Sub, typeof(AOpCodeAluRx)); - SetA64("x1001011001xxxxxxxx100xxxxxxxxxx", AInstEmit.Sub, typeof(AOpCodeAluRx)); - SetA64("x11100010xxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Subs, typeof(AOpCodeAluImm)); - SetA64("01101011<<0xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Subs, typeof(AOpCodeAluRs)); - SetA64("11101011<<0xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Subs, typeof(AOpCodeAluRs)); - SetA64("x1101011001xxxxxxxx0xxxxxxxxxxxx", AInstEmit.Subs, typeof(AOpCodeAluRx)); - SetA64("x1101011001xxxxxxxx100xxxxxxxxxx", AInstEmit.Subs, typeof(AOpCodeAluRx)); - SetA64("11010100000xxxxxxxxxxxxxxxx00001", AInstEmit.Svc, typeof(AOpCodeException)); - SetA64("1101010100001xxxxxxxxxxxxxxxxxxx", AInstEmit.Sys, typeof(AOpCodeSystem)); - SetA64("x0110111xxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Tbnz, typeof(AOpCodeBImmTest)); - SetA64("x0110110xxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Tbz, typeof(AOpCodeBImmTest)); - SetA64("01010011000xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Ubfm, typeof(AOpCodeBfm)); - SetA64("1101001101xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ubfm, typeof(AOpCodeBfm)); - SetA64("x0011010110xxxxx000010xxxxxxxxxx", AInstEmit.Udiv, typeof(AOpCodeAluRs)); - SetA64("10011011101xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Umaddl, typeof(AOpCodeMul)); - SetA64("10011011101xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Umsubl, typeof(AOpCodeMul)); - SetA64("10011011110xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Umulh, typeof(AOpCodeMul)); - - //Vector - SetA64("0101111011100000101110xxxxxxxxxx", AInstEmit.Abs_S, typeof(AOpCodeSimd)); - SetA64("0>001110<<100000101110xxxxxxxxxx", AInstEmit.Abs_V, typeof(AOpCodeSimd)); - SetA64("01011110111xxxxx100001xxxxxxxxxx", AInstEmit.Add_S, typeof(AOpCodeSimdReg)); - SetA64("0>001110<<1xxxxx100001xxxxxxxxxx", AInstEmit.Add_V, typeof(AOpCodeSimdReg)); - SetA64("0x001110<<1xxxxx010000xxxxxxxxxx", AInstEmit.Addhn_V, typeof(AOpCodeSimdReg)); - SetA64("0101111011110001101110xxxxxxxxxx", AInstEmit.Addp_S, typeof(AOpCodeSimd)); - SetA64("0>001110<<1xxxxx101111xxxxxxxxxx", AInstEmit.Addp_V, typeof(AOpCodeSimdReg)); - SetA64("000011100x110001101110xxxxxxxxxx", AInstEmit.Addv_V, typeof(AOpCodeSimd)); - SetA64("01001110<<110001101110xxxxxxxxxx", AInstEmit.Addv_V, typeof(AOpCodeSimd)); - SetA64("0100111000101000010110xxxxxxxxxx", AInstEmit.Aesd_V, typeof(AOpCodeSimd)); - SetA64("0100111000101000010010xxxxxxxxxx", AInstEmit.Aese_V, typeof(AOpCodeSimd)); - SetA64("0100111000101000011110xxxxxxxxxx", AInstEmit.Aesimc_V, typeof(AOpCodeSimd)); - SetA64("0100111000101000011010xxxxxxxxxx", AInstEmit.Aesmc_V, typeof(AOpCodeSimd)); - SetA64("0x001110001xxxxx000111xxxxxxxxxx", AInstEmit.And_V, typeof(AOpCodeSimdReg)); - SetA64("0x001110011xxxxx000111xxxxxxxxxx", AInstEmit.Bic_V, typeof(AOpCodeSimdReg)); - SetA64("0x10111100000xxx<101110<<1xxxxx100011xxxxxxxxxx", AInstEmit.Cmeq_V, typeof(AOpCodeSimdReg)); - SetA64("0>001110<<100000100110xxxxxxxxxx", AInstEmit.Cmeq_V, typeof(AOpCodeSimd)); - SetA64("01011110111xxxxx001111xxxxxxxxxx", AInstEmit.Cmge_S, typeof(AOpCodeSimdReg)); - SetA64("0111111011100000100010xxxxxxxxxx", AInstEmit.Cmge_S, typeof(AOpCodeSimd)); - SetA64("0>001110<<1xxxxx001111xxxxxxxxxx", AInstEmit.Cmge_V, typeof(AOpCodeSimdReg)); - SetA64("0>101110<<100000100010xxxxxxxxxx", AInstEmit.Cmge_V, typeof(AOpCodeSimd)); - SetA64("01011110111xxxxx001101xxxxxxxxxx", AInstEmit.Cmgt_S, typeof(AOpCodeSimdReg)); - SetA64("0101111011100000100010xxxxxxxxxx", AInstEmit.Cmgt_S, typeof(AOpCodeSimd)); - SetA64("0>001110<<1xxxxx001101xxxxxxxxxx", AInstEmit.Cmgt_V, typeof(AOpCodeSimdReg)); - SetA64("0>001110<<100000100010xxxxxxxxxx", AInstEmit.Cmgt_V, typeof(AOpCodeSimd)); - SetA64("01111110111xxxxx001101xxxxxxxxxx", AInstEmit.Cmhi_S, typeof(AOpCodeSimdReg)); - SetA64("0>101110<<1xxxxx001101xxxxxxxxxx", AInstEmit.Cmhi_V, typeof(AOpCodeSimdReg)); - SetA64("01111110111xxxxx001111xxxxxxxxxx", AInstEmit.Cmhs_S, typeof(AOpCodeSimdReg)); - SetA64("0>101110<<1xxxxx001111xxxxxxxxxx", AInstEmit.Cmhs_V, typeof(AOpCodeSimdReg)); - SetA64("0111111011100000100110xxxxxxxxxx", AInstEmit.Cmle_S, typeof(AOpCodeSimd)); - SetA64("0>101110<<100000100110xxxxxxxxxx", AInstEmit.Cmle_V, typeof(AOpCodeSimd)); - SetA64("0101111011100000101010xxxxxxxxxx", AInstEmit.Cmlt_S, typeof(AOpCodeSimd)); - SetA64("0>001110<<100000101010xxxxxxxxxx", AInstEmit.Cmlt_V, typeof(AOpCodeSimd)); - SetA64("01011110111xxxxx100011xxxxxxxxxx", AInstEmit.Cmtst_S, typeof(AOpCodeSimdReg)); - SetA64("0>001110<<1xxxxx100011xxxxxxxxxx", AInstEmit.Cmtst_V, typeof(AOpCodeSimdReg)); - SetA64("0x00111000100000010110xxxxxxxxxx", AInstEmit.Cnt_V, typeof(AOpCodeSimd)); - SetA64("0x001110000xxxxx000011xxxxxxxxxx", AInstEmit.Dup_Gp, typeof(AOpCodeSimdIns)); - SetA64("01011110000xxxxx000001xxxxxxxxxx", AInstEmit.Dup_S, typeof(AOpCodeSimdIns)); - SetA64("0x001110000xxxxx000001xxxxxxxxxx", AInstEmit.Dup_V, typeof(AOpCodeSimdIns)); - SetA64("0x101110001xxxxx000111xxxxxxxxxx", AInstEmit.Eor_V, typeof(AOpCodeSimdReg)); - SetA64("0>101110000xxxxx00011101<100000111110xxxxxxxxxx", AInstEmit.Fabs_V, typeof(AOpCodeSimd)); - SetA64("000111100x1xxxxx001010xxxxxxxxxx", AInstEmit.Fadd_S, typeof(AOpCodeSimdReg)); - SetA64("0>0011100<1xxxxx110101xxxxxxxxxx", AInstEmit.Fadd_V, typeof(AOpCodeSimdReg)); - SetA64("011111100x110000110110xxxxxxxxxx", AInstEmit.Faddp_S, typeof(AOpCodeSimd)); - SetA64("0>1011100<1xxxxx110101xxxxxxxxxx", AInstEmit.Faddp_V, typeof(AOpCodeSimdReg)); - SetA64("000111100x1xxxxxxxxx01xxxxx0xxxx", AInstEmit.Fccmp_S, typeof(AOpCodeSimdFcond)); - SetA64("000111100x1xxxxxxxxx01xxxxx1xxxx", AInstEmit.Fccmpe_S, typeof(AOpCodeSimdFcond)); - SetA64("010111100x1xxxxx111001xxxxxxxxxx", AInstEmit.Fcmeq_S, typeof(AOpCodeSimdReg)); - SetA64("010111101x100000110110xxxxxxxxxx", AInstEmit.Fcmeq_S, typeof(AOpCodeSimd)); - SetA64("0>0011100<1xxxxx111001xxxxxxxxxx", AInstEmit.Fcmeq_V, typeof(AOpCodeSimdReg)); - SetA64("0>0011101<100000110110xxxxxxxxxx", AInstEmit.Fcmeq_V, typeof(AOpCodeSimd)); - SetA64("011111100x1xxxxx111001xxxxxxxxxx", AInstEmit.Fcmge_S, typeof(AOpCodeSimdReg)); - SetA64("011111101x100000110010xxxxxxxxxx", AInstEmit.Fcmge_S, typeof(AOpCodeSimd)); - SetA64("0>1011100<1xxxxx111001xxxxxxxxxx", AInstEmit.Fcmge_V, typeof(AOpCodeSimdReg)); - SetA64("0>1011101<100000110010xxxxxxxxxx", AInstEmit.Fcmge_V, typeof(AOpCodeSimd)); - SetA64("011111101x1xxxxx111001xxxxxxxxxx", AInstEmit.Fcmgt_S, typeof(AOpCodeSimdReg)); - SetA64("010111101x100000110010xxxxxxxxxx", AInstEmit.Fcmgt_S, typeof(AOpCodeSimd)); - SetA64("0>1011101<1xxxxx111001xxxxxxxxxx", AInstEmit.Fcmgt_V, typeof(AOpCodeSimdReg)); - SetA64("0>0011101<100000110010xxxxxxxxxx", AInstEmit.Fcmgt_V, typeof(AOpCodeSimd)); - SetA64("011111101x100000110110xxxxxxxxxx", AInstEmit.Fcmle_S, typeof(AOpCodeSimd)); - SetA64("0>1011101<100000110110xxxxxxxxxx", AInstEmit.Fcmle_V, typeof(AOpCodeSimd)); - SetA64("010111101x100000111010xxxxxxxxxx", AInstEmit.Fcmlt_S, typeof(AOpCodeSimd)); - SetA64("0>0011101<100000111010xxxxxxxxxx", AInstEmit.Fcmlt_V, typeof(AOpCodeSimd)); - SetA64("000111100x1xxxxx001000xxxxx0x000", AInstEmit.Fcmp_S, typeof(AOpCodeSimdReg)); - SetA64("000111100x1xxxxx001000xxxxx1x000", AInstEmit.Fcmpe_S, typeof(AOpCodeSimdReg)); - SetA64("000111100x1xxxxxxxxx11xxxxxxxxxx", AInstEmit.Fcsel_S, typeof(AOpCodeSimdFcond)); - SetA64("000111100x10001xx10000xxxxxxxxxx", AInstEmit.Fcvt_S, typeof(AOpCodeSimd)); - SetA64("x00111100x100100000000xxxxxxxxxx", AInstEmit.Fcvtas_Gp, typeof(AOpCodeSimdCvt)); - SetA64("x00111100x100101000000xxxxxxxxxx", AInstEmit.Fcvtau_Gp, typeof(AOpCodeSimdCvt)); - SetA64("0x0011100x100001011110xxxxxxxxxx", AInstEmit.Fcvtl_V, typeof(AOpCodeSimd)); - SetA64("x00111100x110000000000xxxxxxxxxx", AInstEmit.Fcvtms_Gp, typeof(AOpCodeSimdCvt)); - SetA64("x00111100x110001000000xxxxxxxxxx", AInstEmit.Fcvtmu_Gp, typeof(AOpCodeSimdCvt)); - SetA64("0x0011100x100001011010xxxxxxxxxx", AInstEmit.Fcvtn_V, typeof(AOpCodeSimd)); - SetA64("010111100x100001101010xxxxxxxxxx", AInstEmit.Fcvtns_S, typeof(AOpCodeSimd)); - SetA64("0>0011100<100001101010xxxxxxxxxx", AInstEmit.Fcvtns_V, typeof(AOpCodeSimd)); - SetA64("011111100x100001101010xxxxxxxxxx", AInstEmit.Fcvtnu_S, typeof(AOpCodeSimd)); - SetA64("0>1011100<100001101010xxxxxxxxxx", AInstEmit.Fcvtnu_V, typeof(AOpCodeSimd)); - SetA64("x00111100x101000000000xxxxxxxxxx", AInstEmit.Fcvtps_Gp, typeof(AOpCodeSimdCvt)); - SetA64("x00111100x101001000000xxxxxxxxxx", AInstEmit.Fcvtpu_Gp, typeof(AOpCodeSimdCvt)); - SetA64("x00111100x111000000000xxxxxxxxxx", AInstEmit.Fcvtzs_Gp, typeof(AOpCodeSimdCvt)); - SetA64("x00111100x011000xxxxxxxxxxxxxxxx", AInstEmit.Fcvtzs_Gp_Fix, typeof(AOpCodeSimdCvt)); - SetA64("010111101x100001101110xxxxxxxxxx", AInstEmit.Fcvtzs_S, typeof(AOpCodeSimd)); - SetA64("0>0011101<100001101110xxxxxxxxxx", AInstEmit.Fcvtzs_V, typeof(AOpCodeSimd)); - SetA64("0x0011110>>xxxxx111111xxxxxxxxxx", AInstEmit.Fcvtzs_V, typeof(AOpCodeSimdShImm)); - SetA64("x00111100x111001000000xxxxxxxxxx", AInstEmit.Fcvtzu_Gp, typeof(AOpCodeSimdCvt)); - SetA64("x00111100x011001xxxxxxxxxxxxxxxx", AInstEmit.Fcvtzu_Gp_Fix, typeof(AOpCodeSimdCvt)); - SetA64("011111101x100001101110xxxxxxxxxx", AInstEmit.Fcvtzu_S, typeof(AOpCodeSimd)); - SetA64("0>1011101<100001101110xxxxxxxxxx", AInstEmit.Fcvtzu_V, typeof(AOpCodeSimd)); - SetA64("0x1011110>>xxxxx111111xxxxxxxxxx", AInstEmit.Fcvtzu_V, typeof(AOpCodeSimdShImm)); - SetA64("000111100x1xxxxx000110xxxxxxxxxx", AInstEmit.Fdiv_S, typeof(AOpCodeSimdReg)); - SetA64("0>1011100<1xxxxx111111xxxxxxxxxx", AInstEmit.Fdiv_V, typeof(AOpCodeSimdReg)); - SetA64("000111110x0xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Fmadd_S, typeof(AOpCodeSimdReg)); - SetA64("000111100x1xxxxx010010xxxxxxxxxx", AInstEmit.Fmax_S, typeof(AOpCodeSimdReg)); - SetA64("0>0011100<1xxxxx111101xxxxxxxxxx", AInstEmit.Fmax_V, typeof(AOpCodeSimdReg)); - SetA64("000111100x1xxxxx011010xxxxxxxxxx", AInstEmit.Fmaxnm_S, typeof(AOpCodeSimdReg)); - SetA64("0>0011100<1xxxxx110001xxxxxxxxxx", AInstEmit.Fmaxnm_V, typeof(AOpCodeSimdReg)); - SetA64("0>1011100<1xxxxx111101xxxxxxxxxx", AInstEmit.Fmaxp_V, typeof(AOpCodeSimdReg)); - SetA64("000111100x1xxxxx010110xxxxxxxxxx", AInstEmit.Fmin_S, typeof(AOpCodeSimdReg)); - SetA64("0>0011101<1xxxxx111101xxxxxxxxxx", AInstEmit.Fmin_V, typeof(AOpCodeSimdReg)); - SetA64("000111100x1xxxxx011110xxxxxxxxxx", AInstEmit.Fminnm_S, typeof(AOpCodeSimdReg)); - SetA64("0>0011101<1xxxxx110001xxxxxxxxxx", AInstEmit.Fminnm_V, typeof(AOpCodeSimdReg)); - SetA64("0>1011101<1xxxxx111101xxxxxxxxxx", AInstEmit.Fminp_V, typeof(AOpCodeSimdReg)); - SetA64("010111111xxxxxxx0001x0xxxxxxxxxx", AInstEmit.Fmla_Se, typeof(AOpCodeSimdRegElemF)); - SetA64("0>0011100<1xxxxx110011xxxxxxxxxx", AInstEmit.Fmla_V, typeof(AOpCodeSimdReg)); - SetA64("0>00111110011101<1xxxxx110011xxxxxxxxxx", AInstEmit.Fmls_V, typeof(AOpCodeSimdReg)); - SetA64("0>00111111011100<1xxxxx110111xxxxxxxxxx", AInstEmit.Fmul_V, typeof(AOpCodeSimdReg)); - SetA64("0>00111110011100<1xxxxx110111xxxxxxxxxx", AInstEmit.Fmulx_V, typeof(AOpCodeSimdReg)); - SetA64("0>10111111011101<100000111110xxxxxxxxxx", AInstEmit.Fneg_V, typeof(AOpCodeSimd)); - SetA64("000111110x1xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Fnmadd_S, typeof(AOpCodeSimdReg)); - SetA64("000111110x1xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Fnmsub_S, typeof(AOpCodeSimdReg)); - SetA64("000111100x1xxxxx100010xxxxxxxxxx", AInstEmit.Fnmul_S, typeof(AOpCodeSimdReg)); - SetA64("010111101x100001110110xxxxxxxxxx", AInstEmit.Frecpe_S, typeof(AOpCodeSimd)); - SetA64("0>0011101<100001110110xxxxxxxxxx", AInstEmit.Frecpe_V, typeof(AOpCodeSimd)); - SetA64("010111100x1xxxxx111111xxxxxxxxxx", AInstEmit.Frecps_S, typeof(AOpCodeSimdReg)); - SetA64("0>0011100<1xxxxx111111xxxxxxxxxx", AInstEmit.Frecps_V, typeof(AOpCodeSimdReg)); - SetA64("010111101x100001111110xxxxxxxxxx", AInstEmit.Frecpx_S, typeof(AOpCodeSimd)); - SetA64("000111100x100110010000xxxxxxxxxx", AInstEmit.Frinta_S, typeof(AOpCodeSimd)); - SetA64("0>1011100<100001100010xxxxxxxxxx", AInstEmit.Frinta_V, typeof(AOpCodeSimd)); - SetA64("000111100x100111110000xxxxxxxxxx", AInstEmit.Frinti_S, typeof(AOpCodeSimd)); - SetA64("0>1011101<100001100110xxxxxxxxxx", AInstEmit.Frinti_V, typeof(AOpCodeSimd)); - SetA64("000111100x100101010000xxxxxxxxxx", AInstEmit.Frintm_S, typeof(AOpCodeSimd)); - SetA64("0>0011100<100001100110xxxxxxxxxx", AInstEmit.Frintm_V, typeof(AOpCodeSimd)); - SetA64("000111100x100100010000xxxxxxxxxx", AInstEmit.Frintn_S, typeof(AOpCodeSimd)); - SetA64("0>0011100<100001100010xxxxxxxxxx", AInstEmit.Frintn_V, typeof(AOpCodeSimd)); - SetA64("000111100x100100110000xxxxxxxxxx", AInstEmit.Frintp_S, typeof(AOpCodeSimd)); - SetA64("0>0011101<100001100010xxxxxxxxxx", AInstEmit.Frintp_V, typeof(AOpCodeSimd)); - SetA64("000111100x100111010000xxxxxxxxxx", AInstEmit.Frintx_S, typeof(AOpCodeSimd)); - SetA64("0>1011100<100001100110xxxxxxxxxx", AInstEmit.Frintx_V, typeof(AOpCodeSimd)); - SetA64("011111101x100001110110xxxxxxxxxx", AInstEmit.Frsqrte_S, typeof(AOpCodeSimd)); - SetA64("0>1011101<100001110110xxxxxxxxxx", AInstEmit.Frsqrte_V, typeof(AOpCodeSimd)); - SetA64("010111101x1xxxxx111111xxxxxxxxxx", AInstEmit.Frsqrts_S, typeof(AOpCodeSimdReg)); - SetA64("0>0011101<1xxxxx111111xxxxxxxxxx", AInstEmit.Frsqrts_V, typeof(AOpCodeSimdReg)); - SetA64("000111100x100001110000xxxxxxxxxx", AInstEmit.Fsqrt_S, typeof(AOpCodeSimd)); - SetA64("0>1011101<100001111110xxxxxxxxxx", AInstEmit.Fsqrt_V, typeof(AOpCodeSimd)); - SetA64("000111100x1xxxxx001110xxxxxxxxxx", AInstEmit.Fsub_S, typeof(AOpCodeSimdReg)); - SetA64("0>0011101<1xxxxx110101xxxxxxxxxx", AInstEmit.Fsub_V, typeof(AOpCodeSimdReg)); - SetA64("01001110000xxxxx000111xxxxxxxxxx", AInstEmit.Ins_Gp, typeof(AOpCodeSimdIns)); - SetA64("01101110000xxxxx0xxxx1xxxxxxxxxx", AInstEmit.Ins_V, typeof(AOpCodeSimdIns)); - SetA64("0x00110001000000xxxxxxxxxxxxxxxx", AInstEmit.Ld__Vms, typeof(AOpCodeSimdMemMs)); - SetA64("0x001100110xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ld__Vms, typeof(AOpCodeSimdMemMs)); - SetA64("0x00110101x00000xxxxxxxxxxxxxxxx", AInstEmit.Ld__Vss, typeof(AOpCodeSimdMemSs)); - SetA64("0x00110111xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ld__Vss, typeof(AOpCodeSimdMemSs)); - SetA64("xx10110xx1xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ldp, typeof(AOpCodeSimdMemPair)); - SetA64("xx111100x10xxxxxxxxx00xxxxxxxxxx", AInstEmit.Ldr, typeof(AOpCodeSimdMemImm)); - SetA64("xx111100x10xxxxxxxxx01xxxxxxxxxx", AInstEmit.Ldr, typeof(AOpCodeSimdMemImm)); - SetA64("xx111100x10xxxxxxxxx11xxxxxxxxxx", AInstEmit.Ldr, typeof(AOpCodeSimdMemImm)); - SetA64("xx111101x1xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ldr, typeof(AOpCodeSimdMemImm)); - SetA64("xx111100x11xxxxxxxxx10xxxxxxxxxx", AInstEmit.Ldr, typeof(AOpCodeSimdMemReg)); - SetA64("xx011100xxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.LdrLit, typeof(AOpCodeSimdMemLit)); - SetA64("0x001110<<1xxxxx100101xxxxxxxxxx", AInstEmit.Mla_V, typeof(AOpCodeSimdReg)); - SetA64("0x101111xxxxxxxx0000x0xxxxxxxxxx", AInstEmit.Mla_Ve, typeof(AOpCodeSimdRegElem)); - SetA64("0x101110<<1xxxxx100101xxxxxxxxxx", AInstEmit.Mls_V, typeof(AOpCodeSimdReg)); - SetA64("0x101111xxxxxxxx0100x0xxxxxxxxxx", AInstEmit.Mls_Ve, typeof(AOpCodeSimdRegElem)); - SetA64("0x00111100000xxx0xx001xxxxxxxxxx", AInstEmit.Movi_V, typeof(AOpCodeSimdImm)); - SetA64("0x00111100000xxx10x001xxxxxxxxxx", AInstEmit.Movi_V, typeof(AOpCodeSimdImm)); - SetA64("0x00111100000xxx110x01xxxxxxxxxx", AInstEmit.Movi_V, typeof(AOpCodeSimdImm)); - SetA64("0xx0111100000xxx111001xxxxxxxxxx", AInstEmit.Movi_V, typeof(AOpCodeSimdImm)); - SetA64("0x001110<<1xxxxx100111xxxxxxxxxx", AInstEmit.Mul_V, typeof(AOpCodeSimdReg)); - SetA64("0x001111xxxxxxxx1000x0xxxxxxxxxx", AInstEmit.Mul_Ve, typeof(AOpCodeSimdRegElem)); - SetA64("0x10111100000xxx0xx001xxxxxxxxxx", AInstEmit.Mvni_V, typeof(AOpCodeSimdImm)); - SetA64("0x10111100000xxx10x001xxxxxxxxxx", AInstEmit.Mvni_V, typeof(AOpCodeSimdImm)); - SetA64("0x10111100000xxx110x01xxxxxxxxxx", AInstEmit.Mvni_V, typeof(AOpCodeSimdImm)); - SetA64("0111111011100000101110xxxxxxxxxx", AInstEmit.Neg_S, typeof(AOpCodeSimd)); - SetA64("0>101110<<100000101110xxxxxxxxxx", AInstEmit.Neg_V, typeof(AOpCodeSimd)); - SetA64("0x10111000100000010110xxxxxxxxxx", AInstEmit.Not_V, typeof(AOpCodeSimd)); - SetA64("0x001110111xxxxx000111xxxxxxxxxx", AInstEmit.Orn_V, typeof(AOpCodeSimdReg)); - SetA64("0x001110101xxxxx000111xxxxxxxxxx", AInstEmit.Orr_V, typeof(AOpCodeSimdReg)); - SetA64("0x00111100000xxx<>>xxx100011xxxxxxxxxx", AInstEmit.Rshrn_V, typeof(AOpCodeSimdShImm)); - SetA64("0x101110<<1xxxxx011000xxxxxxxxxx", AInstEmit.Rsubhn_V, typeof(AOpCodeSimdReg)); - SetA64("0x001110<<1xxxxx011111xxxxxxxxxx", AInstEmit.Saba_V, typeof(AOpCodeSimdReg)); - SetA64("0x001110<<1xxxxx010100xxxxxxxxxx", AInstEmit.Sabal_V, typeof(AOpCodeSimdReg)); - SetA64("0x001110<<1xxxxx011101xxxxxxxxxx", AInstEmit.Sabd_V, typeof(AOpCodeSimdReg)); - SetA64("0x001110<<1xxxxx011100xxxxxxxxxx", AInstEmit.Sabdl_V, typeof(AOpCodeSimdReg)); - SetA64("0x001110<<100000011010xxxxxxxxxx", AInstEmit.Sadalp_V, typeof(AOpCodeSimd)); - SetA64("0x001110<<1xxxxx000000xxxxxxxxxx", AInstEmit.Saddl_V, typeof(AOpCodeSimdReg)); - SetA64("0x001110<<100000001010xxxxxxxxxx", AInstEmit.Saddlp_V, typeof(AOpCodeSimd)); - SetA64("0x001110<<1xxxxx000100xxxxxxxxxx", AInstEmit.Saddw_V, typeof(AOpCodeSimdReg)); - SetA64("x0011110xx100010000000xxxxxxxxxx", AInstEmit.Scvtf_Gp, typeof(AOpCodeSimdCvt)); - SetA64("010111100x100001110110xxxxxxxxxx", AInstEmit.Scvtf_S, typeof(AOpCodeSimd)); - SetA64("0x0011100x100001110110xxxxxxxxxx", AInstEmit.Scvtf_V, typeof(AOpCodeSimd)); - SetA64("01011110000xxxxx000000xxxxxxxxxx", AInstEmit.Sha1c_V, typeof(AOpCodeSimdReg)); - SetA64("0101111000101000000010xxxxxxxxxx", AInstEmit.Sha1h_V, typeof(AOpCodeSimd)); - SetA64("01011110000xxxxx001000xxxxxxxxxx", AInstEmit.Sha1m_V, typeof(AOpCodeSimdReg)); - SetA64("01011110000xxxxx000100xxxxxxxxxx", AInstEmit.Sha1p_V, typeof(AOpCodeSimdReg)); - SetA64("01011110000xxxxx001100xxxxxxxxxx", AInstEmit.Sha1su0_V, typeof(AOpCodeSimdReg)); - SetA64("0101111000101000000110xxxxxxxxxx", AInstEmit.Sha1su1_V, typeof(AOpCodeSimd)); - SetA64("01011110000xxxxx010000xxxxxxxxxx", AInstEmit.Sha256h_V, typeof(AOpCodeSimdReg)); - SetA64("01011110000xxxxx010100xxxxxxxxxx", AInstEmit.Sha256h2_V, typeof(AOpCodeSimdReg)); - SetA64("0101111000101000001010xxxxxxxxxx", AInstEmit.Sha256su0_V, typeof(AOpCodeSimd)); - SetA64("01011110000xxxxx011000xxxxxxxxxx", AInstEmit.Sha256su1_V, typeof(AOpCodeSimdReg)); - SetA64("0x001110<<1xxxxx000001xxxxxxxxxx", AInstEmit.Shadd_V, typeof(AOpCodeSimdReg)); - SetA64("0101111101xxxxxx010101xxxxxxxxxx", AInstEmit.Shl_S, typeof(AOpCodeSimdShImm)); - SetA64("0x00111100>>>xxx010101xxxxxxxxxx", AInstEmit.Shl_V, typeof(AOpCodeSimdShImm)); - SetA64("0100111101xxxxxx010101xxxxxxxxxx", AInstEmit.Shl_V, typeof(AOpCodeSimdShImm)); - SetA64("0x101110<<100001001110xxxxxxxxxx", AInstEmit.Shll_V, typeof(AOpCodeSimd)); - SetA64("0x00111100>>>xxx100001xxxxxxxxxx", AInstEmit.Shrn_V, typeof(AOpCodeSimdShImm)); - SetA64("0x001110<<1xxxxx001001xxxxxxxxxx", AInstEmit.Shsub_V, typeof(AOpCodeSimdReg)); - SetA64("0x1011110>>>>xxx010101xxxxxxxxxx", AInstEmit.Sli_V, typeof(AOpCodeSimdShImm)); - SetA64("0x001110<<1xxxxx011001xxxxxxxxxx", AInstEmit.Smax_V, typeof(AOpCodeSimdReg)); - SetA64("0x001110<<1xxxxx101001xxxxxxxxxx", AInstEmit.Smaxp_V, typeof(AOpCodeSimdReg)); - SetA64("0x001110<<1xxxxx011011xxxxxxxxxx", AInstEmit.Smin_V, typeof(AOpCodeSimdReg)); - SetA64("0x001110<<1xxxxx101011xxxxxxxxxx", AInstEmit.Sminp_V, typeof(AOpCodeSimdReg)); - SetA64("0x001110<<1xxxxx100000xxxxxxxxxx", AInstEmit.Smlal_V, typeof(AOpCodeSimdReg)); - SetA64("0x001110<<1xxxxx101000xxxxxxxxxx", AInstEmit.Smlsl_V, typeof(AOpCodeSimdReg)); - SetA64("0x001110000xxxxx001011xxxxxxxxxx", AInstEmit.Smov_S, typeof(AOpCodeSimdIns)); - SetA64("0x001110<<1xxxxx110000xxxxxxxxxx", AInstEmit.Smull_V, typeof(AOpCodeSimdReg)); - SetA64("01011110xx100000011110xxxxxxxxxx", AInstEmit.Sqabs_S, typeof(AOpCodeSimd)); - SetA64("0>001110<<100000011110xxxxxxxxxx", AInstEmit.Sqabs_V, typeof(AOpCodeSimd)); - SetA64("01011110xx1xxxxx000011xxxxxxxxxx", AInstEmit.Sqadd_S, typeof(AOpCodeSimdReg)); - SetA64("0>001110<<1xxxxx000011xxxxxxxxxx", AInstEmit.Sqadd_V, typeof(AOpCodeSimdReg)); - SetA64("01011110011xxxxx101101xxxxxxxxxx", AInstEmit.Sqdmulh_S, typeof(AOpCodeSimdReg)); - SetA64("01011110101xxxxx101101xxxxxxxxxx", AInstEmit.Sqdmulh_S, typeof(AOpCodeSimdReg)); - SetA64("0x001110011xxxxx101101xxxxxxxxxx", AInstEmit.Sqdmulh_V, typeof(AOpCodeSimdReg)); - SetA64("0x001110101xxxxx101101xxxxxxxxxx", AInstEmit.Sqdmulh_V, typeof(AOpCodeSimdReg)); - SetA64("01111110xx100000011110xxxxxxxxxx", AInstEmit.Sqneg_S, typeof(AOpCodeSimd)); - SetA64("0>101110<<100000011110xxxxxxxxxx", AInstEmit.Sqneg_V, typeof(AOpCodeSimd)); - SetA64("01111110011xxxxx101101xxxxxxxxxx", AInstEmit.Sqrdmulh_S, typeof(AOpCodeSimdReg)); - SetA64("01111110101xxxxx101101xxxxxxxxxx", AInstEmit.Sqrdmulh_S, typeof(AOpCodeSimdReg)); - SetA64("0x101110011xxxxx101101xxxxxxxxxx", AInstEmit.Sqrdmulh_V, typeof(AOpCodeSimdReg)); - SetA64("0x101110101xxxxx101101xxxxxxxxxx", AInstEmit.Sqrdmulh_V, typeof(AOpCodeSimdReg)); - SetA64("0101111100>>>xxx100111xxxxxxxxxx", AInstEmit.Sqrshrn_S, typeof(AOpCodeSimdShImm)); - SetA64("0x00111100>>>xxx100111xxxxxxxxxx", AInstEmit.Sqrshrn_V, typeof(AOpCodeSimdShImm)); - SetA64("0111111100>>>xxx100011xxxxxxxxxx", AInstEmit.Sqrshrun_S, typeof(AOpCodeSimdShImm)); - SetA64("0x10111100>>>xxx100011xxxxxxxxxx", AInstEmit.Sqrshrun_V, typeof(AOpCodeSimdShImm)); - SetA64("0101111100>>>xxx100101xxxxxxxxxx", AInstEmit.Sqshrn_S, typeof(AOpCodeSimdShImm)); - SetA64("0x00111100>>>xxx100101xxxxxxxxxx", AInstEmit.Sqshrn_V, typeof(AOpCodeSimdShImm)); - SetA64("0111111100>>>xxx100001xxxxxxxxxx", AInstEmit.Sqshrun_S, typeof(AOpCodeSimdShImm)); - SetA64("0x10111100>>>xxx100001xxxxxxxxxx", AInstEmit.Sqshrun_V, typeof(AOpCodeSimdShImm)); - SetA64("01011110xx1xxxxx001011xxxxxxxxxx", AInstEmit.Sqsub_S, typeof(AOpCodeSimdReg)); - SetA64("0>001110<<1xxxxx001011xxxxxxxxxx", AInstEmit.Sqsub_V, typeof(AOpCodeSimdReg)); - SetA64("01011110<<100001010010xxxxxxxxxx", AInstEmit.Sqxtn_S, typeof(AOpCodeSimd)); - SetA64("0x001110<<100001010010xxxxxxxxxx", AInstEmit.Sqxtn_V, typeof(AOpCodeSimd)); - SetA64("01111110<<100001001010xxxxxxxxxx", AInstEmit.Sqxtun_S, typeof(AOpCodeSimd)); - SetA64("0x101110<<100001001010xxxxxxxxxx", AInstEmit.Sqxtun_V, typeof(AOpCodeSimd)); - SetA64("0x001110<<1xxxxx000101xxxxxxxxxx", AInstEmit.Srhadd_V, typeof(AOpCodeSimdReg)); - SetA64("0101111101xxxxxx001001xxxxxxxxxx", AInstEmit.Srshr_S, typeof(AOpCodeSimdShImm)); - SetA64("0x00111100>>>xxx001001xxxxxxxxxx", AInstEmit.Srshr_V, typeof(AOpCodeSimdShImm)); - SetA64("0100111101xxxxxx001001xxxxxxxxxx", AInstEmit.Srshr_V, typeof(AOpCodeSimdShImm)); - SetA64("0101111101xxxxxx001101xxxxxxxxxx", AInstEmit.Srsra_S, typeof(AOpCodeSimdShImm)); - SetA64("0x00111100>>>xxx001101xxxxxxxxxx", AInstEmit.Srsra_V, typeof(AOpCodeSimdShImm)); - SetA64("0100111101xxxxxx001101xxxxxxxxxx", AInstEmit.Srsra_V, typeof(AOpCodeSimdShImm)); - SetA64("0>001110<<1xxxxx010001xxxxxxxxxx", AInstEmit.Sshl_V, typeof(AOpCodeSimdReg)); - SetA64("0x00111100>>>xxx101001xxxxxxxxxx", AInstEmit.Sshll_V, typeof(AOpCodeSimdShImm)); - SetA64("0101111101xxxxxx000001xxxxxxxxxx", AInstEmit.Sshr_S, typeof(AOpCodeSimdShImm)); - SetA64("0x00111100>>>xxx000001xxxxxxxxxx", AInstEmit.Sshr_V, typeof(AOpCodeSimdShImm)); - SetA64("0100111101xxxxxx000001xxxxxxxxxx", AInstEmit.Sshr_V, typeof(AOpCodeSimdShImm)); - SetA64("0101111101xxxxxx000101xxxxxxxxxx", AInstEmit.Ssra_S, typeof(AOpCodeSimdShImm)); - SetA64("0x00111100>>>xxx000101xxxxxxxxxx", AInstEmit.Ssra_V, typeof(AOpCodeSimdShImm)); - SetA64("0100111101xxxxxx000101xxxxxxxxxx", AInstEmit.Ssra_V, typeof(AOpCodeSimdShImm)); - SetA64("0x001110<<1xxxxx001000xxxxxxxxxx", AInstEmit.Ssubl_V, typeof(AOpCodeSimdReg)); - SetA64("0x001110<<1xxxxx001100xxxxxxxxxx", AInstEmit.Ssubw_V, typeof(AOpCodeSimdReg)); - SetA64("0x00110000000000xxxxxxxxxxxxxxxx", AInstEmit.St__Vms, typeof(AOpCodeSimdMemMs)); - SetA64("0x001100100xxxxxxxxxxxxxxxxxxxxx", AInstEmit.St__Vms, typeof(AOpCodeSimdMemMs)); - SetA64("0x00110100x00000xxxxxxxxxxxxxxxx", AInstEmit.St__Vss, typeof(AOpCodeSimdMemSs)); - SetA64("0x00110110xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.St__Vss, typeof(AOpCodeSimdMemSs)); - SetA64("xx10110xx0xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Stp, typeof(AOpCodeSimdMemPair)); - SetA64("xx111100x00xxxxxxxxx00xxxxxxxxxx", AInstEmit.Str, typeof(AOpCodeSimdMemImm)); - SetA64("xx111100x00xxxxxxxxx01xxxxxxxxxx", AInstEmit.Str, typeof(AOpCodeSimdMemImm)); - SetA64("xx111100x00xxxxxxxxx11xxxxxxxxxx", AInstEmit.Str, typeof(AOpCodeSimdMemImm)); - SetA64("xx111101x0xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Str, typeof(AOpCodeSimdMemImm)); - SetA64("xx111100x01xxxxxxxxx10xxxxxxxxxx", AInstEmit.Str, typeof(AOpCodeSimdMemReg)); - SetA64("01111110111xxxxx100001xxxxxxxxxx", AInstEmit.Sub_S, typeof(AOpCodeSimdReg)); - SetA64("0>101110<<1xxxxx100001xxxxxxxxxx", AInstEmit.Sub_V, typeof(AOpCodeSimdReg)); - SetA64("0x001110<<1xxxxx011000xxxxxxxxxx", AInstEmit.Subhn_V, typeof(AOpCodeSimdReg)); - SetA64("01011110xx100000001110xxxxxxxxxx", AInstEmit.Suqadd_S, typeof(AOpCodeSimd)); - SetA64("0>001110<<100000001110xxxxxxxxxx", AInstEmit.Suqadd_V, typeof(AOpCodeSimd)); - SetA64("0x001110000xxxxx0xx000xxxxxxxxxx", AInstEmit.Tbl_V, typeof(AOpCodeSimdTbl)); - SetA64("0>001110<<0xxxxx001010xxxxxxxxxx", AInstEmit.Trn1_V, typeof(AOpCodeSimdReg)); - SetA64("0>001110<<0xxxxx011010xxxxxxxxxx", AInstEmit.Trn2_V, typeof(AOpCodeSimdReg)); - SetA64("0x101110<<1xxxxx011111xxxxxxxxxx", AInstEmit.Uaba_V, typeof(AOpCodeSimdReg)); - SetA64("0x101110<<1xxxxx010100xxxxxxxxxx", AInstEmit.Uabal_V, typeof(AOpCodeSimdReg)); - SetA64("0x101110<<1xxxxx011101xxxxxxxxxx", AInstEmit.Uabd_V, typeof(AOpCodeSimdReg)); - SetA64("0x101110<<1xxxxx011100xxxxxxxxxx", AInstEmit.Uabdl_V, typeof(AOpCodeSimdReg)); - SetA64("0x101110<<100000011010xxxxxxxxxx", AInstEmit.Uadalp_V, typeof(AOpCodeSimd)); - SetA64("0x101110<<1xxxxx000000xxxxxxxxxx", AInstEmit.Uaddl_V, typeof(AOpCodeSimdReg)); - SetA64("0x101110<<100000001010xxxxxxxxxx", AInstEmit.Uaddlp_V, typeof(AOpCodeSimd)); - SetA64("001011100x110000001110xxxxxxxxxx", AInstEmit.Uaddlv_V, typeof(AOpCodeSimd)); - SetA64("01101110<<110000001110xxxxxxxxxx", AInstEmit.Uaddlv_V, typeof(AOpCodeSimd)); - SetA64("0x101110<<1xxxxx000100xxxxxxxxxx", AInstEmit.Uaddw_V, typeof(AOpCodeSimdReg)); - SetA64("x0011110xx100011000000xxxxxxxxxx", AInstEmit.Ucvtf_Gp, typeof(AOpCodeSimdCvt)); - SetA64("011111100x100001110110xxxxxxxxxx", AInstEmit.Ucvtf_S, typeof(AOpCodeSimd)); - SetA64("0x1011100x100001110110xxxxxxxxxx", AInstEmit.Ucvtf_V, typeof(AOpCodeSimd)); - SetA64("0x101110<<1xxxxx000001xxxxxxxxxx", AInstEmit.Uhadd_V, typeof(AOpCodeSimdReg)); - SetA64("0x101110<<1xxxxx001001xxxxxxxxxx", AInstEmit.Uhsub_V, typeof(AOpCodeSimdReg)); - SetA64("0x101110<<1xxxxx011001xxxxxxxxxx", AInstEmit.Umax_V, typeof(AOpCodeSimdReg)); - SetA64("0x101110<<1xxxxx101001xxxxxxxxxx", AInstEmit.Umaxp_V, typeof(AOpCodeSimdReg)); - SetA64("0x101110<<1xxxxx011011xxxxxxxxxx", AInstEmit.Umin_V, typeof(AOpCodeSimdReg)); - SetA64("0x101110<<1xxxxx101011xxxxxxxxxx", AInstEmit.Uminp_V, typeof(AOpCodeSimdReg)); - SetA64("0x101110<<1xxxxx100000xxxxxxxxxx", AInstEmit.Umlal_V, typeof(AOpCodeSimdReg)); - SetA64("0x101110<<1xxxxx101000xxxxxxxxxx", AInstEmit.Umlsl_V, typeof(AOpCodeSimdReg)); - SetA64("0x001110000xxxxx001111xxxxxxxxxx", AInstEmit.Umov_S, typeof(AOpCodeSimdIns)); - SetA64("0x101110<<1xxxxx110000xxxxxxxxxx", AInstEmit.Umull_V, typeof(AOpCodeSimdReg)); - SetA64("01111110xx1xxxxx000011xxxxxxxxxx", AInstEmit.Uqadd_S, typeof(AOpCodeSimdReg)); - SetA64("0>101110<<1xxxxx000011xxxxxxxxxx", AInstEmit.Uqadd_V, typeof(AOpCodeSimdReg)); - SetA64("0111111100>>>xxx100111xxxxxxxxxx", AInstEmit.Uqrshrn_S, typeof(AOpCodeSimdShImm)); - SetA64("0x10111100>>>xxx100111xxxxxxxxxx", AInstEmit.Uqrshrn_V, typeof(AOpCodeSimdShImm)); - SetA64("0111111100>>>xxx100101xxxxxxxxxx", AInstEmit.Uqshrn_S, typeof(AOpCodeSimdShImm)); - SetA64("0x10111100>>>xxx100101xxxxxxxxxx", AInstEmit.Uqshrn_V, typeof(AOpCodeSimdShImm)); - SetA64("01111110xx1xxxxx001011xxxxxxxxxx", AInstEmit.Uqsub_S, typeof(AOpCodeSimdReg)); - SetA64("0>101110<<1xxxxx001011xxxxxxxxxx", AInstEmit.Uqsub_V, typeof(AOpCodeSimdReg)); - SetA64("01111110<<100001010010xxxxxxxxxx", AInstEmit.Uqxtn_S, typeof(AOpCodeSimd)); - SetA64("0x101110<<100001010010xxxxxxxxxx", AInstEmit.Uqxtn_V, typeof(AOpCodeSimd)); - SetA64("0x101110<<1xxxxx000101xxxxxxxxxx", AInstEmit.Urhadd_V, typeof(AOpCodeSimdReg)); - SetA64("0111111101xxxxxx001001xxxxxxxxxx", AInstEmit.Urshr_S, typeof(AOpCodeSimdShImm)); - SetA64("0x10111100>>>xxx001001xxxxxxxxxx", AInstEmit.Urshr_V, typeof(AOpCodeSimdShImm)); - SetA64("0110111101xxxxxx001001xxxxxxxxxx", AInstEmit.Urshr_V, typeof(AOpCodeSimdShImm)); - SetA64("0111111101xxxxxx001101xxxxxxxxxx", AInstEmit.Ursra_S, typeof(AOpCodeSimdShImm)); - SetA64("0x10111100>>>xxx001101xxxxxxxxxx", AInstEmit.Ursra_V, typeof(AOpCodeSimdShImm)); - SetA64("0110111101xxxxxx001101xxxxxxxxxx", AInstEmit.Ursra_V, typeof(AOpCodeSimdShImm)); - SetA64("0>101110<<1xxxxx010001xxxxxxxxxx", AInstEmit.Ushl_V, typeof(AOpCodeSimdReg)); - SetA64("0x10111100>>>xxx101001xxxxxxxxxx", AInstEmit.Ushll_V, typeof(AOpCodeSimdShImm)); - SetA64("0111111101xxxxxx000001xxxxxxxxxx", AInstEmit.Ushr_S, typeof(AOpCodeSimdShImm)); - SetA64("0x10111100>>>xxx000001xxxxxxxxxx", AInstEmit.Ushr_V, typeof(AOpCodeSimdShImm)); - SetA64("0110111101xxxxxx000001xxxxxxxxxx", AInstEmit.Ushr_V, typeof(AOpCodeSimdShImm)); - SetA64("01111110xx100000001110xxxxxxxxxx", AInstEmit.Usqadd_S, typeof(AOpCodeSimd)); - SetA64("0>101110<<100000001110xxxxxxxxxx", AInstEmit.Usqadd_V, typeof(AOpCodeSimd)); - SetA64("0111111101xxxxxx000101xxxxxxxxxx", AInstEmit.Usra_S, typeof(AOpCodeSimdShImm)); - SetA64("0x10111100>>>xxx000101xxxxxxxxxx", AInstEmit.Usra_V, typeof(AOpCodeSimdShImm)); - SetA64("0110111101xxxxxx000101xxxxxxxxxx", AInstEmit.Usra_V, typeof(AOpCodeSimdShImm)); - SetA64("0x101110<<1xxxxx001000xxxxxxxxxx", AInstEmit.Usubl_V, typeof(AOpCodeSimdReg)); - SetA64("0x101110<<1xxxxx001100xxxxxxxxxx", AInstEmit.Usubw_V, typeof(AOpCodeSimdReg)); - SetA64("0>001110<<0xxxxx000110xxxxxxxxxx", AInstEmit.Uzp1_V, typeof(AOpCodeSimdReg)); - SetA64("0>001110<<0xxxxx010110xxxxxxxxxx", AInstEmit.Uzp2_V, typeof(AOpCodeSimdReg)); - SetA64("0x001110<<100001001010xxxxxxxxxx", AInstEmit.Xtn_V, typeof(AOpCodeSimd)); - SetA64("0>001110<<0xxxxx001110xxxxxxxxxx", AInstEmit.Zip1_V, typeof(AOpCodeSimdReg)); - SetA64("0>001110<<0xxxxx011110xxxxxxxxxx", AInstEmit.Zip2_V, typeof(AOpCodeSimdReg)); -#endregion - -#region "Generate InstA64FastLookup Table (AArch64)" - var Tmp = new List[FastLookupSize]; - for (int i = 0; i < FastLookupSize; i++) - { - Tmp[i] = new List(); - } - - foreach (var Inst in AllInstA64) - { - int Mask = ToFastLookupIndex(Inst.Mask); - int Value = ToFastLookupIndex(Inst.Value); - - for (int i = 0; i < FastLookupSize; i++) - { - if ((i & Mask) == Value) - { - Tmp[i].Add(Inst); - } - } - } - - for (int i = 0; i < FastLookupSize; i++) - { - InstA64FastLookup[i] = Tmp[i].ToArray(); - } -#endregion - } - - private class InstInfo - { - public int Mask; - public int Value; - - public AInst Inst; - - public InstInfo(int Mask, int Value, AInst Inst) - { - this.Mask = Mask; - this.Value = Value; - this.Inst = Inst; - } - } - - private static List AllInstA32 = new List(); - private static List AllInstA64 = new List(); - - private static int FastLookupSize = 0x1000; - private static InstInfo[][] InstA64FastLookup = new InstInfo[FastLookupSize][]; - - private static void SetA32(string Encoding, AInstInterpreter Interpreter, Type Type) - { - Set(Encoding, new AInst(Interpreter, null, Type), AExecutionMode.AArch32); - } - - private static void SetA64(string Encoding, AInstEmitter Emitter, Type Type) - { - Set(Encoding, new AInst(null, Emitter, Type), AExecutionMode.AArch64); - } - - private static void Set(string Encoding, AInst Inst, AExecutionMode Mode) - { - int Bit = Encoding.Length - 1; - int Value = 0; - int XMask = 0; - int XBits = 0; - - int[] XPos = new int[Encoding.Length]; - - int Blacklisted = 0; - - for (int Index = 0; Index < Encoding.Length; Index++, Bit--) - { - //Note: < and > are used on special encodings. - //The < means that we should never have ALL bits with the '<' set. - //So, when the encoding has <<, it means that 00, 01, and 10 are valid, - //but not 11. <<< is 000, 001, ..., 110 but NOT 111, and so on... - //For >, the invalid value is zero. So, for >> 01, 10 and 11 are valid, - //but 00 isn't. - char Chr = Encoding[Index]; - - if (Chr == '1') - { - Value |= 1 << Bit; - } - else if (Chr == 'x') - { - XMask |= 1 << Bit; - } - else if (Chr == '>') - { - XPos[XBits++] = Bit; - } - else if (Chr == '<') - { - XPos[XBits++] = Bit; - - Blacklisted |= 1 << Bit; - } - else if (Chr != '0') - { - throw new ArgumentException(nameof(Encoding)); - } - } - - XMask = ~XMask; - - if (XBits == 0) - { - InsertInst(XMask, Value, Inst, Mode); - - return; - } - - for (int Index = 0; Index < (1 << XBits); Index++) - { - int Mask = 0; - - for (int X = 0; X < XBits; X++) - { - Mask |= ((Index >> X) & 1) << XPos[X]; - } - - if (Mask != Blacklisted) - { - InsertInst(XMask, Value | Mask, Inst, Mode); - } - } - } - - private static void InsertInst( - int XMask, - int Value, - AInst Inst, - AExecutionMode Mode) - { - InstInfo Info = new InstInfo(XMask, Value, Inst); - - if (Mode == AExecutionMode.AArch64) - { - AllInstA64.Add(Info); - } - else - { - AllInstA32.Add(Info); - } - } - - public static AInst GetInstA32(int OpCode) - { - return GetInstFromList(AllInstA32, OpCode); - } - - public static AInst GetInstA64(int OpCode) - { - return GetInstFromList(InstA64FastLookup[ToFastLookupIndex(OpCode)], OpCode); - } - - private static int ToFastLookupIndex(int Value) - { - return ((Value >> 10) & 0x00F) | ((Value >> 18) & 0xFF0); - } - - private static AInst GetInstFromList(IEnumerable InstList, int OpCode) - { - foreach (var Node in InstList) - { - if ((OpCode & Node.Mask) == Node.Value) - { - return Node.Inst; - } - } - - return AInst.Undefined; - } - } -} diff --git a/AOptimizations.cs b/AOptimizations.cs deleted file mode 100644 index 1720548..0000000 --- a/AOptimizations.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Runtime.Intrinsics.X86; - -public static class AOptimizations -{ - internal static bool FastFP = true; - - private static bool UseAllSseIfAvailable = true; - - private static bool UseSseIfAvailable = true; - private static bool UseSse2IfAvailable = true; - private static bool UseSse41IfAvailable = true; - private static bool UseSse42IfAvailable = true; - - internal static bool UseSse = (UseAllSseIfAvailable && UseSseIfAvailable) && Sse.IsSupported; - internal static bool UseSse2 = (UseAllSseIfAvailable && UseSse2IfAvailable) && Sse2.IsSupported; - internal static bool UseSse41 = (UseAllSseIfAvailable && UseSse41IfAvailable) && Sse41.IsSupported; - internal static bool UseSse42 = (UseAllSseIfAvailable && UseSse42IfAvailable) && Sse42.IsSupported; -} diff --git a/ATranslatedSub.cs b/ATranslatedSub.cs deleted file mode 100644 index a11da26..0000000 --- a/ATranslatedSub.cs +++ /dev/null @@ -1,150 +0,0 @@ -using ChocolArm64.Memory; -using ChocolArm64.State; -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using System.Reflection; -using System.Reflection.Emit; - -namespace ChocolArm64 -{ - class ATranslatedSub - { - private delegate long AA64Subroutine(AThreadState Register, AMemory Memory); - - private const int MinCallCountForReJit = 250; - - private AA64Subroutine ExecDelegate; - - public static int StateArgIdx { get; private set; } - public static int MemoryArgIdx { get; private set; } - - public static Type[] FixedArgTypes { get; private set; } - - public DynamicMethod Method { get; private set; } - - public ReadOnlyCollection Params { get; private set; } - - private HashSet Callers; - - private ATranslatedSubType Type; - - private int CallCount; - - private bool NeedsReJit; - - public ATranslatedSub(DynamicMethod Method, List Params) - { - if (Method == null) - { - throw new ArgumentNullException(nameof(Method)); - } - - if (Params == null) - { - throw new ArgumentNullException(nameof(Params)); - } - - this.Method = Method; - this.Params = Params.AsReadOnly(); - - Callers = new HashSet(); - - PrepareDelegate(); - } - - static ATranslatedSub() - { - MethodInfo MthdInfo = typeof(AA64Subroutine).GetMethod("Invoke"); - - ParameterInfo[] Params = MthdInfo.GetParameters(); - - FixedArgTypes = new Type[Params.Length]; - - for (int Index = 0; Index < Params.Length; Index++) - { - Type ParamType = Params[Index].ParameterType; - - FixedArgTypes[Index] = ParamType; - - if (ParamType == typeof(AThreadState)) - { - StateArgIdx = Index; - } - else if (ParamType == typeof(AMemory)) - { - MemoryArgIdx = Index; - } - } - } - - private void PrepareDelegate() - { - string Name = $"{Method.Name}_Dispatch"; - - DynamicMethod Mthd = new DynamicMethod(Name, typeof(long), FixedArgTypes); - - ILGenerator Generator = Mthd.GetILGenerator(); - - Generator.EmitLdargSeq(FixedArgTypes.Length); - - foreach (ARegister Reg in Params) - { - Generator.EmitLdarg(StateArgIdx); - - Generator.Emit(OpCodes.Ldfld, Reg.GetField()); - } - - Generator.Emit(OpCodes.Call, Method); - Generator.Emit(OpCodes.Ret); - - ExecDelegate = (AA64Subroutine)Mthd.CreateDelegate(typeof(AA64Subroutine)); - } - - public bool ShouldReJit() - { - if (NeedsReJit && CallCount < MinCallCountForReJit) - { - CallCount++; - - return false; - } - - return NeedsReJit; - } - - public long Execute(AThreadState ThreadState, AMemory Memory) - { - return ExecDelegate(ThreadState, Memory); - } - - public void AddCaller(long Position) - { - lock (Callers) - { - Callers.Add(Position); - } - } - - public long[] GetCallerPositions() - { - lock (Callers) - { - return Callers.ToArray(); - } - } - - public void SetType(ATranslatedSubType Type) - { - this.Type = Type; - - if (Type == ATranslatedSubType.SubTier0) - { - NeedsReJit = true; - } - } - - public void MarkForReJit() => NeedsReJit = true; - } -} \ No newline at end of file diff --git a/ATranslator.cs b/ATranslator.cs deleted file mode 100644 index 5be41d3..0000000 --- a/ATranslator.cs +++ /dev/null @@ -1,165 +0,0 @@ -using ChocolArm64.Decoder; -using ChocolArm64.Events; -using ChocolArm64.Memory; -using ChocolArm64.State; -using ChocolArm64.Translation; -using System; -using System.Reflection.Emit; - -namespace ChocolArm64 -{ - public class ATranslator - { - private ATranslatorCache Cache; - - public event EventHandler CpuTrace; - - public bool EnableCpuTrace { get; set; } - - public ATranslator() - { - Cache = new ATranslatorCache(); - } - - internal void ExecuteSubroutine(AThread Thread, long Position) - { - //TODO: Both the execute A32/A64 methods should be merged on the future, - //when both ISAs are implemented with the interpreter and JIT. - //As of now, A32 only has a interpreter and A64 a JIT. - AThreadState State = Thread.ThreadState; - AMemory Memory = Thread.Memory; - - if (State.ExecutionMode == AExecutionMode.AArch32) - { - ExecuteSubroutineA32(State, Memory); - } - else - { - ExecuteSubroutineA64(State, Memory, Position); - } - } - - private void ExecuteSubroutineA32(AThreadState State, AMemory Memory) - { - do - { - AOpCode OpCode = ADecoder.DecodeOpCode(State, Memory, State.R15); - - OpCode.Interpreter(State, Memory, OpCode); - } - while (State.R15 != 0 && State.Running); - } - - private void ExecuteSubroutineA64(AThreadState State, AMemory Memory, long Position) - { - do - { - if (EnableCpuTrace) - { - CpuTrace?.Invoke(this, new ACpuTraceEventArgs(Position)); - } - - if (!Cache.TryGetSubroutine(Position, out ATranslatedSub Sub)) - { - Sub = TranslateTier0(State, Memory, Position); - } - - if (Sub.ShouldReJit()) - { - TranslateTier1(State, Memory, Position); - } - - Position = Sub.Execute(State, Memory); - } - while (Position != 0 && State.Running); - } - - internal bool HasCachedSub(long Position) - { - return Cache.HasSubroutine(Position); - } - - private ATranslatedSub TranslateTier0(AThreadState State, AMemory Memory, long Position) - { - ABlock Block = ADecoder.DecodeBasicBlock(State, Memory, Position); - - ABlock[] Graph = new ABlock[] { Block }; - - string SubName = GetSubroutineName(Position); - - AILEmitterCtx Context = new AILEmitterCtx(Cache, Graph, Block, SubName); - - do - { - Context.EmitOpCode(); - } - while (Context.AdvanceOpCode()); - - ATranslatedSub Subroutine = Context.GetSubroutine(); - - Subroutine.SetType(ATranslatedSubType.SubTier0); - - Cache.AddOrUpdate(Position, Subroutine, Block.OpCodes.Count); - - AOpCode LastOp = Block.GetLastOp(); - - return Subroutine; - } - - private void TranslateTier1(AThreadState State, AMemory Memory, long Position) - { - (ABlock[] Graph, ABlock Root) = ADecoder.DecodeSubroutine(Cache, State, Memory, Position); - - string SubName = GetSubroutineName(Position); - - AILEmitterCtx Context = new AILEmitterCtx(Cache, Graph, Root, SubName); - - if (Context.CurrBlock.Position != Position) - { - Context.Emit(OpCodes.Br, Context.GetLabel(Position)); - } - - do - { - Context.EmitOpCode(); - } - while (Context.AdvanceOpCode()); - - //Mark all methods that calls this method for ReJiting, - //since we can now call it directly which is faster. - if (Cache.TryGetSubroutine(Position, out ATranslatedSub OldSub)) - { - foreach (long CallerPos in OldSub.GetCallerPositions()) - { - if (Cache.TryGetSubroutine(Position, out ATranslatedSub CallerSub)) - { - CallerSub.MarkForReJit(); - } - } - } - - ATranslatedSub Subroutine = Context.GetSubroutine(); - - Subroutine.SetType(ATranslatedSubType.SubTier1); - - Cache.AddOrUpdate(Position, Subroutine, GetGraphInstCount(Graph)); - } - - private string GetSubroutineName(long Position) - { - return $"Sub{Position:x16}"; - } - - private int GetGraphInstCount(ABlock[] Graph) - { - int Size = 0; - - foreach (ABlock Block in Graph) - { - Size += Block.OpCodes.Count; - } - - return Size; - } - } -} \ No newline at end of file diff --git a/ATranslatorCache.cs b/ATranslatorCache.cs deleted file mode 100644 index 199b44f..0000000 --- a/ATranslatorCache.cs +++ /dev/null @@ -1,165 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Diagnostics; -using System.Runtime.CompilerServices; -using System.Threading; - -namespace ChocolArm64 -{ - class ATranslatorCache - { - //Maximum size of the cache, in bytes, measured in ARM code size. - private const int MaxTotalSize = 4 * 1024 * 256; - - //Minimum time required in milliseconds for a method to be eligible for deletion. - private const int MinTimeDelta = 2 * 60000; - - //Minimum number of calls required to update the timestamp. - private const int MinCallCountForUpdate = 250; - - private class CacheBucket - { - public ATranslatedSub Subroutine { get; private set; } - - public LinkedListNode Node { get; private set; } - - public int CallCount { get; set; } - - public int Size { get; private set; } - - public long Timestamp { get; private set; } - - public CacheBucket(ATranslatedSub Subroutine, LinkedListNode Node, int Size) - { - this.Subroutine = Subroutine; - this.Size = Size; - - UpdateNode(Node); - } - - public void UpdateNode(LinkedListNode Node) - { - this.Node = Node; - - Timestamp = GetTimestamp(); - } - } - - private ConcurrentDictionary Cache; - - private LinkedList SortedCache; - - private int TotalSize; - - public ATranslatorCache() - { - Cache = new ConcurrentDictionary(); - - SortedCache = new LinkedList(); - } - - public void AddOrUpdate(long Position, ATranslatedSub Subroutine, int Size) - { - ClearCacheIfNeeded(); - - TotalSize += Size; - - lock (SortedCache) - { - LinkedListNode Node = SortedCache.AddLast(Position); - - CacheBucket NewBucket = new CacheBucket(Subroutine, Node, Size); - - Cache.AddOrUpdate(Position, NewBucket, (Key, Bucket) => - { - TotalSize -= Bucket.Size; - - SortedCache.Remove(Bucket.Node); - - return NewBucket; - }); - } - } - - public bool HasSubroutine(long Position) - { - return Cache.ContainsKey(Position); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool TryGetSubroutine(long Position, out ATranslatedSub Subroutine) - { - if (Cache.TryGetValue(Position, out CacheBucket Bucket)) - { - if (Bucket.CallCount++ > MinCallCountForUpdate) - { - if (Monitor.TryEnter(SortedCache)) - { - try - { - Bucket.CallCount = 0; - - SortedCache.Remove(Bucket.Node); - - Bucket.UpdateNode(SortedCache.AddLast(Position)); - } - finally - { - Monitor.Exit(SortedCache); - } - } - } - - Subroutine = Bucket.Subroutine; - - return true; - } - - Subroutine = default(ATranslatedSub); - - return false; - } - - private void ClearCacheIfNeeded() - { - long Timestamp = GetTimestamp(); - - while (TotalSize > MaxTotalSize) - { - lock (SortedCache) - { - LinkedListNode Node = SortedCache.First; - - if (Node == null) - { - break; - } - - CacheBucket Bucket = Cache[Node.Value]; - - long TimeDelta = Timestamp - Bucket.Timestamp; - - if (TimeDelta <= MinTimeDelta) - { - break; - } - - if (Cache.TryRemove(Node.Value, out Bucket)) - { - TotalSize -= Bucket.Size; - - SortedCache.Remove(Bucket.Node); - } - } - } - } - - private static long GetTimestamp() - { - long timestamp = Stopwatch.GetTimestamp(); - - return timestamp / (Stopwatch.Frequency / 1000); - } - } -} \ No newline at end of file diff --git a/BitUtils.cs b/BitUtils.cs new file mode 100644 index 0000000..881ee24 --- /dev/null +++ b/BitUtils.cs @@ -0,0 +1,49 @@ +namespace ChocolArm64 +{ + static class BitUtils + { + public static int HighestBitSet32(int value) + { + for (int bit = 31; bit >= 0; bit--) + { + if (((value >> bit) & 1) != 0) + { + return bit; + } + } + + return -1; + } + + private static readonly sbyte[] HbsNibbleTbl = { -1, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3 }; + + public static int HighestBitSetNibble(int value) => HbsNibbleTbl[value & 0b1111]; + + public static long Replicate(long bits, int size) + { + long output = 0; + + for (int bit = 0; bit < 64; bit += size) + { + output |= bits << bit; + } + + return output; + } + + public static long FillWithOnes(int bits) + { + return bits == 64 ? -1L : (1L << bits) - 1; + } + + public static long RotateRight(long bits, int shift, int size) + { + return (long)RotateRight((ulong)bits, shift, size); + } + + public static ulong RotateRight(ulong bits, int shift, int size) + { + return (bits >> shift) | (bits << (size - shift)); + } + } +} diff --git a/AThread.cs b/CpuThread.cs similarity index 56% rename from AThread.cs rename to CpuThread.cs index 38c2b53..11f4123 100644 --- a/AThread.cs +++ b/CpuThread.cs @@ -5,35 +5,35 @@ using System.Threading; namespace ChocolArm64 { - public class AThread + public class CpuThread { - public AThreadState ThreadState { get; private set; } - public AMemory Memory { get; private set; } + public CpuThreadState ThreadState { get; private set; } + public MemoryManager Memory { get; private set; } - private ATranslator Translator; + private Translator _translator; public Thread Work; public event EventHandler WorkFinished; - private int IsExecuting; + private int _isExecuting; - public AThread(ATranslator Translator, AMemory Memory, long EntryPoint) + public CpuThread(Translator translator, MemoryManager memory, long entryPoint) { - this.Translator = Translator; - this.Memory = Memory; + _translator = translator; + Memory = memory; - ThreadState = new AThreadState(); + ThreadState = new CpuThreadState(); - ThreadState.ExecutionMode = AExecutionMode.AArch64; + ThreadState.ExecutionMode = ExecutionMode.AArch64; ThreadState.Running = true; Work = new Thread(delegate() { - Translator.ExecuteSubroutine(this, EntryPoint); + translator.ExecuteSubroutine(this, entryPoint); - Memory.RemoveMonitor(ThreadState.Core); + memory.RemoveMonitor(ThreadState.Core); WorkFinished?.Invoke(this, EventArgs.Empty); }); @@ -41,7 +41,7 @@ namespace ChocolArm64 public bool Execute() { - if (Interlocked.Exchange(ref IsExecuting, 1) == 1) + if (Interlocked.Exchange(ref _isExecuting, 1) == 1) { return false; } diff --git a/Decoder/ABlock.cs b/Decoder/ABlock.cs deleted file mode 100644 index 7a0fc60..0000000 --- a/Decoder/ABlock.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Collections.Generic; - -namespace ChocolArm64.Decoder -{ - class ABlock - { - public long Position { get; set; } - public long EndPosition { get; set; } - - public ABlock Next { get; set; } - public ABlock Branch { get; set; } - - public List OpCodes { get; private set; } - - public ABlock() - { - OpCodes = new List(); - } - - public ABlock(long Position) : this() - { - this.Position = Position; - } - - public AOpCode GetLastOp() - { - if (OpCodes.Count > 0) - { - return OpCodes[OpCodes.Count - 1]; - } - - return null; - } - } -} \ No newline at end of file diff --git a/Decoder/ACond.cs b/Decoder/ACond.cs deleted file mode 100644 index f2da8bd..0000000 --- a/Decoder/ACond.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace ChocolArm64.Decoder -{ - enum ACond - { - Eq = 0, - Ne = 1, - Ge_Un = 2, - Lt_Un = 3, - Mi = 4, - Pl = 5, - Vs = 6, - Vc = 7, - Gt_Un = 8, - Le_Un = 9, - Ge = 10, - Lt = 11, - Gt = 12, - Le = 13, - Al = 14, - Nv = 15 - } -} \ No newline at end of file diff --git a/Decoder/ADecoder.cs b/Decoder/ADecoder.cs deleted file mode 100644 index 64beebd..0000000 --- a/Decoder/ADecoder.cs +++ /dev/null @@ -1,239 +0,0 @@ -using ChocolArm64.Instruction; -using ChocolArm64.Memory; -using ChocolArm64.State; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Reflection.Emit; - -namespace ChocolArm64.Decoder -{ - static class ADecoder - { - private delegate object OpActivator(AInst Inst, long Position, int OpCode); - - private static ConcurrentDictionary OpActivators; - - static ADecoder() - { - OpActivators = new ConcurrentDictionary(); - } - - public static ABlock DecodeBasicBlock(AThreadState State, AMemory Memory, long Start) - { - ABlock Block = new ABlock(Start); - - FillBlock(State, Memory, Block); - - return Block; - } - - public static (ABlock[] Graph, ABlock Root) DecodeSubroutine( - ATranslatorCache Cache, - AThreadState State, - AMemory Memory, - long Start) - { - Dictionary Visited = new Dictionary(); - Dictionary VisitedEnd = new Dictionary(); - - Queue Blocks = new Queue(); - - ABlock Enqueue(long Position) - { - if (!Visited.TryGetValue(Position, out ABlock Output)) - { - Output = new ABlock(Position); - - Blocks.Enqueue(Output); - - Visited.Add(Position, Output); - } - - return Output; - } - - ABlock Root = Enqueue(Start); - - while (Blocks.Count > 0) - { - ABlock Current = Blocks.Dequeue(); - - FillBlock(State, Memory, Current); - - //Set child blocks. "Branch" is the block the branch instruction - //points to (when taken), "Next" is the block at the next address, - //executed when the branch is not taken. For Unconditional Branches - //(except BL/BLR that are sub calls) or end of executable, Next is null. - if (Current.OpCodes.Count > 0) - { - bool HasCachedSub = false; - - AOpCode LastOp = Current.GetLastOp(); - - if (LastOp is AOpCodeBImm Op) - { - if (Op.Emitter == AInstEmit.Bl) - { - HasCachedSub = Cache.HasSubroutine(Op.Imm); - } - else - { - Current.Branch = Enqueue(Op.Imm); - } - } - - if (!((LastOp is AOpCodeBImmAl) || - (LastOp is AOpCodeBReg)) || HasCachedSub) - { - Current.Next = Enqueue(Current.EndPosition); - } - } - - //If we have on the graph two blocks with the same end position, - //then we need to split the bigger block and have two small blocks, - //the end position of the bigger "Current" block should then be == to - //the position of the "Smaller" block. - while (VisitedEnd.TryGetValue(Current.EndPosition, out ABlock Smaller)) - { - if (Current.Position > Smaller.Position) - { - ABlock Temp = Smaller; - - Smaller = Current; - Current = Temp; - } - - Current.EndPosition = Smaller.Position; - Current.Next = Smaller; - Current.Branch = null; - - Current.OpCodes.RemoveRange( - Current.OpCodes.Count - Smaller.OpCodes.Count, - Smaller.OpCodes.Count); - - VisitedEnd[Smaller.EndPosition] = Smaller; - } - - VisitedEnd.Add(Current.EndPosition, Current); - } - - //Make and sort Graph blocks array by position. - ABlock[] Graph = new ABlock[Visited.Count]; - - while (Visited.Count > 0) - { - ulong FirstPos = ulong.MaxValue; - - foreach (ABlock Block in Visited.Values) - { - if (FirstPos > (ulong)Block.Position) - FirstPos = (ulong)Block.Position; - } - - ABlock Current = Visited[(long)FirstPos]; - - do - { - Graph[Graph.Length - Visited.Count] = Current; - - Visited.Remove(Current.Position); - - Current = Current.Next; - } - while (Current != null); - } - - return (Graph, Root); - } - - private static void FillBlock(AThreadState State, AMemory Memory, ABlock Block) - { - long Position = Block.Position; - - AOpCode OpCode; - - do - { - //TODO: This needs to be changed to support both AArch32 and AArch64, - //once JIT support is introduced on AArch32 aswell. - OpCode = DecodeOpCode(State, Memory, Position); - - Block.OpCodes.Add(OpCode); - - Position += 4; - } - while (!(IsBranch(OpCode) || IsException(OpCode))); - - Block.EndPosition = Position; - } - - private static bool IsBranch(AOpCode OpCode) - { - return OpCode is AOpCodeBImm || - OpCode is AOpCodeBReg; - } - - private static bool IsException(AOpCode OpCode) - { - return OpCode.Emitter == AInstEmit.Brk || - OpCode.Emitter == AInstEmit.Svc || - OpCode.Emitter == AInstEmit.Und; - } - - public static AOpCode DecodeOpCode(AThreadState State, AMemory Memory, long Position) - { - int OpCode = Memory.ReadInt32(Position); - - AInst Inst; - - if (State.ExecutionMode == AExecutionMode.AArch64) - { - Inst = AOpCodeTable.GetInstA64(OpCode); - } - else - { - //TODO: Thumb support. - Inst = AOpCodeTable.GetInstA32(OpCode); - } - - AOpCode DecodedOpCode = new AOpCode(AInst.Undefined, Position, OpCode); - - if (Inst.Type != null) - { - DecodedOpCode = MakeOpCode(Inst.Type, Inst, Position, OpCode); - } - - return DecodedOpCode; - } - - private static AOpCode MakeOpCode(Type Type, AInst Inst, long Position, int OpCode) - { - if (Type == null) - { - throw new ArgumentNullException(nameof(Type)); - } - - OpActivator CreateInstance = OpActivators.GetOrAdd(Type, CacheOpActivator); - - return (AOpCode)CreateInstance(Inst, Position, OpCode); - } - - private static OpActivator CacheOpActivator(Type Type) - { - Type[] ArgTypes = new Type[] { typeof(AInst), typeof(long), typeof(int) }; - - DynamicMethod Mthd = new DynamicMethod($"Make{Type.Name}", Type, ArgTypes); - - ILGenerator Generator = Mthd.GetILGenerator(); - - Generator.Emit(OpCodes.Ldarg_0); - Generator.Emit(OpCodes.Ldarg_1); - Generator.Emit(OpCodes.Ldarg_2); - Generator.Emit(OpCodes.Newobj, Type.GetConstructor(ArgTypes)); - Generator.Emit(OpCodes.Ret); - - return (OpActivator)Mthd.CreateDelegate(typeof(OpActivator)); - } - } -} \ No newline at end of file diff --git a/Decoder/ADecoderHelper.cs b/Decoder/ADecoderHelper.cs deleted file mode 100644 index a2179f4..0000000 --- a/Decoder/ADecoderHelper.cs +++ /dev/null @@ -1,107 +0,0 @@ -using System; - -namespace ChocolArm64.Decoder -{ - static class ADecoderHelper - { - public struct BitMask - { - public long WMask; - public long TMask; - public int Pos; - public int Shift; - public bool IsUndefined; - - public static BitMask Invalid => new BitMask { IsUndefined = true }; - } - - public static BitMask DecodeBitMask(int OpCode, bool Immediate) - { - int ImmS = (OpCode >> 10) & 0x3f; - int ImmR = (OpCode >> 16) & 0x3f; - - int N = (OpCode >> 22) & 1; - int SF = (OpCode >> 31) & 1; - - int Length = ABitUtils.HighestBitSet32((~ImmS & 0x3f) | (N << 6)); - - if (Length < 1 || (SF == 0 && N != 0)) - { - return BitMask.Invalid; - } - - int Size = 1 << Length; - - int Levels = Size - 1; - - int S = ImmS & Levels; - int R = ImmR & Levels; - - if (Immediate && S == Levels) - { - return BitMask.Invalid; - } - - long WMask = ABitUtils.FillWithOnes(S + 1); - long TMask = ABitUtils.FillWithOnes(((S - R) & Levels) + 1); - - if (R > 0) - { - WMask = ABitUtils.RotateRight(WMask, R, Size); - WMask &= ABitUtils.FillWithOnes(Size); - } - - return new BitMask() - { - WMask = ABitUtils.Replicate(WMask, Size), - TMask = ABitUtils.Replicate(TMask, Size), - - Pos = ImmS, - Shift = ImmR - }; - } - - public static long DecodeImm8Float(long Imm, int Size) - { - int E = 0, F = 0; - - switch (Size) - { - case 0: E = 8; F = 23; break; - case 1: E = 11; F = 52; break; - - default: throw new ArgumentOutOfRangeException(nameof(Size)); - } - - long Value = (Imm & 0x3f) << F - 4; - - long EBit = (Imm >> 6) & 1; - long SBit = (Imm >> 7) & 1; - - if (EBit != 0) - { - Value |= (1L << E - 3) - 1 << F + 2; - } - - Value |= (EBit ^ 1) << F + E - 1; - Value |= SBit << F + E; - - return Value; - } - - public static long DecodeImm26_2(int OpCode) - { - return ((long)OpCode << 38) >> 36; - } - - public static long DecodeImmS19_2(int OpCode) - { - return (((long)OpCode << 40) >> 43) & ~3; - } - - public static long DecodeImmS14_2(int OpCode) - { - return (((long)OpCode << 45) >> 48) & ~3; - } - } -} \ No newline at end of file diff --git a/Decoder/AOpCode.cs b/Decoder/AOpCode.cs deleted file mode 100644 index bdc8f13..0000000 --- a/Decoder/AOpCode.cs +++ /dev/null @@ -1,40 +0,0 @@ -using ChocolArm64.Instruction; -using ChocolArm64.State; -using System; - -namespace ChocolArm64.Decoder -{ - class AOpCode : IAOpCode - { - public long Position { get; private set; } - public int RawOpCode { get; private set; } - - public AInstEmitter Emitter { get; protected set; } - public AInstInterpreter Interpreter { get; protected set; } - public ARegisterSize RegisterSize { get; protected set; } - - public AOpCode(AInst Inst, long Position, int OpCode) - { - this.Position = Position; - this.RawOpCode = OpCode; - - RegisterSize = ARegisterSize.Int64; - - Emitter = Inst.Emitter; - Interpreter = Inst.Interpreter; - } - - public int GetBitsCount() - { - switch (RegisterSize) - { - case ARegisterSize.Int32: return 32; - case ARegisterSize.Int64: return 64; - case ARegisterSize.SIMD64: return 64; - case ARegisterSize.SIMD128: return 128; - } - - throw new InvalidOperationException(); - } - } -} \ No newline at end of file diff --git a/Decoder/AOpCodeAdr.cs b/Decoder/AOpCodeAdr.cs deleted file mode 100644 index 3396281..0000000 --- a/Decoder/AOpCodeAdr.cs +++ /dev/null @@ -1,18 +0,0 @@ -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder -{ - class AOpCodeAdr : AOpCode - { - public int Rd { get; private set; } - public long Imm { get; private set; } - - public AOpCodeAdr(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - Rd = OpCode & 0x1f; - - Imm = ADecoderHelper.DecodeImmS19_2(OpCode); - Imm |= ((long)OpCode >> 29) & 3; - } - } -} \ No newline at end of file diff --git a/Decoder/AOpCodeAlu.cs b/Decoder/AOpCodeAlu.cs deleted file mode 100644 index e1a44f0..0000000 --- a/Decoder/AOpCodeAlu.cs +++ /dev/null @@ -1,24 +0,0 @@ -using ChocolArm64.Instruction; -using ChocolArm64.State; - -namespace ChocolArm64.Decoder -{ - class AOpCodeAlu : AOpCode, IAOpCodeAlu - { - public int Rd { get; protected set; } - public int Rn { get; private set; } - - public ADataOp DataOp { get; private set; } - - public AOpCodeAlu(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - Rd = (OpCode >> 0) & 0x1f; - Rn = (OpCode >> 5) & 0x1f; - DataOp = (ADataOp)((OpCode >> 24) & 0x3); - - RegisterSize = (OpCode >> 31) != 0 - ? ARegisterSize.Int64 - : ARegisterSize.Int32; - } - } -} \ No newline at end of file diff --git a/Decoder/AOpCodeAluImm.cs b/Decoder/AOpCodeAluImm.cs deleted file mode 100644 index e913475..0000000 --- a/Decoder/AOpCodeAluImm.cs +++ /dev/null @@ -1,39 +0,0 @@ -using ChocolArm64.Instruction; -using System; - -namespace ChocolArm64.Decoder -{ - class AOpCodeAluImm : AOpCodeAlu, IAOpCodeAluImm - { - public long Imm { get; private set; } - - public AOpCodeAluImm(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - if (DataOp == ADataOp.Arithmetic) - { - Imm = (OpCode >> 10) & 0xfff; - - int Shift = (OpCode >> 22) & 3; - - Imm <<= Shift * 12; - } - else if (DataOp == ADataOp.Logical) - { - var BM = ADecoderHelper.DecodeBitMask(OpCode, true); - - if (BM.IsUndefined) - { - Emitter = AInstEmit.Und; - - return; - } - - Imm = BM.WMask; - } - else - { - throw new ArgumentException(nameof(OpCode)); - } - } - } -} \ No newline at end of file diff --git a/Decoder/AOpCodeAluRs.cs b/Decoder/AOpCodeAluRs.cs deleted file mode 100644 index 9c215be..0000000 --- a/Decoder/AOpCodeAluRs.cs +++ /dev/null @@ -1,29 +0,0 @@ -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder -{ - class AOpCodeAluRs : AOpCodeAlu, IAOpCodeAluRs - { - public int Shift { get; private set; } - public int Rm { get; private set; } - - public AShiftType ShiftType { get; private set; } - - public AOpCodeAluRs(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - int Shift = (OpCode >> 10) & 0x3f; - - if (Shift >= GetBitsCount()) - { - Emitter = AInstEmit.Und; - - return; - } - - this.Shift = Shift; - - Rm = (OpCode >> 16) & 0x1f; - ShiftType = (AShiftType)((OpCode >> 22) & 0x3); - } - } -} \ No newline at end of file diff --git a/Decoder/AOpCodeAluRx.cs b/Decoder/AOpCodeAluRx.cs deleted file mode 100644 index 7dd72a6..0000000 --- a/Decoder/AOpCodeAluRx.cs +++ /dev/null @@ -1,19 +0,0 @@ -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder -{ - class AOpCodeAluRx : AOpCodeAlu, IAOpCodeAluRx - { - public int Shift { get; private set; } - public int Rm { get; private set; } - - public AIntType IntType { get; private set; } - - public AOpCodeAluRx(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - Shift = (OpCode >> 10) & 0x7; - IntType = (AIntType)((OpCode >> 13) & 0x7); - Rm = (OpCode >> 16) & 0x1f; - } - } -} \ No newline at end of file diff --git a/Decoder/AOpCodeBImm.cs b/Decoder/AOpCodeBImm.cs deleted file mode 100644 index 6519d28..0000000 --- a/Decoder/AOpCodeBImm.cs +++ /dev/null @@ -1,11 +0,0 @@ -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder -{ - class AOpCodeBImm : AOpCode - { - public long Imm { get; protected set; } - - public AOpCodeBImm(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) { } - } -} \ No newline at end of file diff --git a/Decoder/AOpCodeBImmAl.cs b/Decoder/AOpCodeBImmAl.cs deleted file mode 100644 index a4ff686..0000000 --- a/Decoder/AOpCodeBImmAl.cs +++ /dev/null @@ -1,12 +0,0 @@ -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder -{ - class AOpCodeBImmAl : AOpCodeBImm - { - public AOpCodeBImmAl(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - Imm = Position + ADecoderHelper.DecodeImm26_2(OpCode); - } - } -} \ No newline at end of file diff --git a/Decoder/AOpCodeBImmCmp.cs b/Decoder/AOpCodeBImmCmp.cs deleted file mode 100644 index 0f16b73..0000000 --- a/Decoder/AOpCodeBImmCmp.cs +++ /dev/null @@ -1,21 +0,0 @@ -using ChocolArm64.Instruction; -using ChocolArm64.State; - -namespace ChocolArm64.Decoder -{ - class AOpCodeBImmCmp : AOpCodeBImm - { - public int Rt { get; private set; } - - public AOpCodeBImmCmp(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - Rt = OpCode & 0x1f; - - Imm = Position + ADecoderHelper.DecodeImmS19_2(OpCode); - - RegisterSize = (OpCode >> 31) != 0 - ? ARegisterSize.Int64 - : ARegisterSize.Int32; - } - } -} \ No newline at end of file diff --git a/Decoder/AOpCodeBImmCond.cs b/Decoder/AOpCodeBImmCond.cs deleted file mode 100644 index 1310feb..0000000 --- a/Decoder/AOpCodeBImmCond.cs +++ /dev/null @@ -1,25 +0,0 @@ -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder -{ - class AOpCodeBImmCond : AOpCodeBImm, IAOpCodeCond - { - public ACond Cond { get; private set; } - - public AOpCodeBImmCond(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - int O0 = (OpCode >> 4) & 1; - - if (O0 != 0) - { - Emitter = AInstEmit.Und; - - return; - } - - Cond = (ACond)(OpCode & 0xf); - - Imm = Position + ADecoderHelper.DecodeImmS19_2(OpCode); - } - } -} \ No newline at end of file diff --git a/Decoder/AOpCodeBImmTest.cs b/Decoder/AOpCodeBImmTest.cs deleted file mode 100644 index 73e57b7..0000000 --- a/Decoder/AOpCodeBImmTest.cs +++ /dev/null @@ -1,20 +0,0 @@ -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder -{ - class AOpCodeBImmTest : AOpCodeBImm - { - public int Rt { get; private set; } - public int Pos { get; private set; } - - public AOpCodeBImmTest(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - Rt = OpCode & 0x1f; - - Imm = Position + ADecoderHelper.DecodeImmS14_2(OpCode); - - Pos = (OpCode >> 19) & 0x1f; - Pos |= (OpCode >> 26) & 0x20; - } - } -} \ No newline at end of file diff --git a/Decoder/AOpCodeBReg.cs b/Decoder/AOpCodeBReg.cs deleted file mode 100644 index c9c600a..0000000 --- a/Decoder/AOpCodeBReg.cs +++ /dev/null @@ -1,24 +0,0 @@ -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder -{ - class AOpCodeBReg : AOpCode - { - public int Rn { get; private set; } - - public AOpCodeBReg(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - int Op4 = (OpCode >> 0) & 0x1f; - int Op2 = (OpCode >> 16) & 0x1f; - - if (Op2 != 0b11111 || Op4 != 0b00000) - { - Emitter = AInstEmit.Und; - - return; - } - - Rn = (OpCode >> 5) & 0x1f; - } - } -} \ No newline at end of file diff --git a/Decoder/AOpCodeBfm.cs b/Decoder/AOpCodeBfm.cs deleted file mode 100644 index 6498d8e..0000000 --- a/Decoder/AOpCodeBfm.cs +++ /dev/null @@ -1,29 +0,0 @@ -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder -{ - class AOpCodeBfm : AOpCodeAlu - { - public long WMask { get; private set; } - public long TMask { get; private set; } - public int Pos { get; private set; } - public int Shift { get; private set; } - - public AOpCodeBfm(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - var BM = ADecoderHelper.DecodeBitMask(OpCode, false); - - if (BM.IsUndefined) - { - Emitter = AInstEmit.Und; - - return; - } - - WMask = BM.WMask; - TMask = BM.TMask; - Pos = BM.Pos; - Shift = BM.Shift; - } - } -} \ No newline at end of file diff --git a/Decoder/AOpCodeCcmp.cs b/Decoder/AOpCodeCcmp.cs deleted file mode 100644 index d0c7f77..0000000 --- a/Decoder/AOpCodeCcmp.cs +++ /dev/null @@ -1,31 +0,0 @@ -using ChocolArm64.Instruction; -using ChocolArm64.State; - -namespace ChocolArm64.Decoder -{ - class AOpCodeCcmp : AOpCodeAlu, IAOpCodeCond - { - public int NZCV { get; private set; } - protected int RmImm; - - public ACond Cond { get; private set; } - - public AOpCodeCcmp(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - int O3 = (OpCode >> 4) & 1; - - if (O3 != 0) - { - Emitter = AInstEmit.Und; - - return; - } - - NZCV = (OpCode >> 0) & 0xf; - Cond = (ACond)((OpCode >> 12) & 0xf); - RmImm = (OpCode >> 16) & 0x1f; - - Rd = AThreadState.ZRIndex; - } - } -} \ No newline at end of file diff --git a/Decoder/AOpCodeCcmpImm.cs b/Decoder/AOpCodeCcmpImm.cs deleted file mode 100644 index 803eefc..0000000 --- a/Decoder/AOpCodeCcmpImm.cs +++ /dev/null @@ -1,11 +0,0 @@ -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder -{ - class AOpCodeCcmpImm : AOpCodeCcmp, IAOpCodeAluImm - { - public long Imm => RmImm; - - public AOpCodeCcmpImm(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) { } - } -} \ No newline at end of file diff --git a/Decoder/AOpCodeCcmpReg.cs b/Decoder/AOpCodeCcmpReg.cs deleted file mode 100644 index c364ae6..0000000 --- a/Decoder/AOpCodeCcmpReg.cs +++ /dev/null @@ -1,15 +0,0 @@ -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder -{ - class AOpCodeCcmpReg : AOpCodeCcmp, IAOpCodeAluRs - { - public int Rm => RmImm; - - public int Shift => 0; - - public AShiftType ShiftType => AShiftType.Lsl; - - public AOpCodeCcmpReg(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) { } - } -} \ No newline at end of file diff --git a/Decoder/AOpCodeCsel.cs b/Decoder/AOpCodeCsel.cs deleted file mode 100644 index cdef3e7..0000000 --- a/Decoder/AOpCodeCsel.cs +++ /dev/null @@ -1,17 +0,0 @@ -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder -{ - class AOpCodeCsel : AOpCodeAlu, IAOpCodeCond - { - public int Rm { get; private set; } - - public ACond Cond { get; private set; } - - public AOpCodeCsel(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - Rm = (OpCode >> 16) & 0x1f; - Cond = (ACond)((OpCode >> 12) & 0xf); - } - } -} \ No newline at end of file diff --git a/Decoder/AOpCodeException.cs b/Decoder/AOpCodeException.cs deleted file mode 100644 index 4579c1a..0000000 --- a/Decoder/AOpCodeException.cs +++ /dev/null @@ -1,14 +0,0 @@ -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder -{ - class AOpCodeException : AOpCode - { - public int Id { get; private set; } - - public AOpCodeException(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - Id = (OpCode >> 5) & 0xffff; - } - } -} \ No newline at end of file diff --git a/Decoder/AOpCodeMem.cs b/Decoder/AOpCodeMem.cs deleted file mode 100644 index be5367c..0000000 --- a/Decoder/AOpCodeMem.cs +++ /dev/null @@ -1,19 +0,0 @@ -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder -{ - class AOpCodeMem : AOpCode - { - public int Rt { get; protected set; } - public int Rn { get; protected set; } - public int Size { get; protected set; } - public bool Extend64 { get; protected set; } - - public AOpCodeMem(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - Rt = (OpCode >> 0) & 0x1f; - Rn = (OpCode >> 5) & 0x1f; - Size = (OpCode >> 30) & 0x3; - } - } -} \ No newline at end of file diff --git a/Decoder/AOpCodeMemEx.cs b/Decoder/AOpCodeMemEx.cs deleted file mode 100644 index 3a28cfd..0000000 --- a/Decoder/AOpCodeMemEx.cs +++ /dev/null @@ -1,16 +0,0 @@ -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder -{ - class AOpCodeMemEx : AOpCodeMem - { - public int Rt2 { get; private set; } - public int Rs { get; private set; } - - public AOpCodeMemEx(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - Rt2 = (OpCode >> 10) & 0x1f; - Rs = (OpCode >> 16) & 0x1f; - } - } -} \ No newline at end of file diff --git a/Decoder/AOpCodeMemPair.cs b/Decoder/AOpCodeMemPair.cs deleted file mode 100644 index ec866c8..0000000 --- a/Decoder/AOpCodeMemPair.cs +++ /dev/null @@ -1,25 +0,0 @@ -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder -{ - class AOpCodeMemPair : AOpCodeMemImm - { - public int Rt2 { get; private set; } - - public AOpCodeMemPair(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - Rt2 = (OpCode >> 10) & 0x1f; - WBack = ((OpCode >> 23) & 0x1) != 0; - PostIdx = ((OpCode >> 23) & 0x3) == 1; - Extend64 = ((OpCode >> 30) & 0x3) == 1; - Size = ((OpCode >> 31) & 0x1) | 2; - - DecodeImm(OpCode); - } - - protected void DecodeImm(int OpCode) - { - Imm = ((long)(OpCode >> 15) << 57) >> (57 - Size); - } - } -} \ No newline at end of file diff --git a/Decoder/AOpCodeMemReg.cs b/Decoder/AOpCodeMemReg.cs deleted file mode 100644 index 9892712..0000000 --- a/Decoder/AOpCodeMemReg.cs +++ /dev/null @@ -1,20 +0,0 @@ -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder -{ - class AOpCodeMemReg : AOpCodeMem - { - public bool Shift { get; private set; } - public int Rm { get; private set; } - - public AIntType IntType { get; private set; } - - public AOpCodeMemReg(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - Shift = ((OpCode >> 12) & 0x1) != 0; - IntType = (AIntType)((OpCode >> 13) & 0x7); - Rm = (OpCode >> 16) & 0x1f; - Extend64 = ((OpCode >> 22) & 0x3) == 2; - } - } -} \ No newline at end of file diff --git a/Decoder/AOpCodeMov.cs b/Decoder/AOpCodeMov.cs deleted file mode 100644 index d539864..0000000 --- a/Decoder/AOpCodeMov.cs +++ /dev/null @@ -1,36 +0,0 @@ -using ChocolArm64.Instruction; -using ChocolArm64.State; - -namespace ChocolArm64.Decoder -{ - class AOpCodeMov : AOpCode - { - public int Rd { get; private set; } - public long Imm { get; private set; } - public int Pos { get; private set; } - - public AOpCodeMov(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - int P1 = (OpCode >> 22) & 1; - int SF = (OpCode >> 31) & 1; - - if (SF == 0 && P1 != 0) - { - Emitter = AInstEmit.Und; - - return; - } - - Rd = (OpCode >> 0) & 0x1f; - Imm = (OpCode >> 5) & 0xffff; - Pos = (OpCode >> 21) & 0x3; - - Pos <<= 4; - Imm <<= Pos; - - RegisterSize = (OpCode >> 31) != 0 - ? ARegisterSize.Int64 - : ARegisterSize.Int32; - } - } -} \ No newline at end of file diff --git a/Decoder/AOpCodeMul.cs b/Decoder/AOpCodeMul.cs deleted file mode 100644 index ca2b0cd..0000000 --- a/Decoder/AOpCodeMul.cs +++ /dev/null @@ -1,16 +0,0 @@ -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder -{ - class AOpCodeMul : AOpCodeAlu - { - public int Rm { get; private set; } - public int Ra { get; private set; } - - public AOpCodeMul(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - Ra = (OpCode >> 10) & 0x1f; - Rm = (OpCode >> 16) & 0x1f; - } - } -} \ No newline at end of file diff --git a/Decoder/AOpCodeSimd.cs b/Decoder/AOpCodeSimd.cs deleted file mode 100644 index 4170851..0000000 --- a/Decoder/AOpCodeSimd.cs +++ /dev/null @@ -1,25 +0,0 @@ -using ChocolArm64.Instruction; -using ChocolArm64.State; - -namespace ChocolArm64.Decoder -{ - class AOpCodeSimd : AOpCode, IAOpCodeSimd - { - public int Rd { get; private set; } - public int Rn { get; private set; } - public int Opc { get; private set; } - public int Size { get; protected set; } - - public AOpCodeSimd(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - Rd = (OpCode >> 0) & 0x1f; - Rn = (OpCode >> 5) & 0x1f; - Opc = (OpCode >> 15) & 0x3; - Size = (OpCode >> 22) & 0x3; - - RegisterSize = ((OpCode >> 30) & 1) != 0 - ? ARegisterSize.SIMD128 - : ARegisterSize.SIMD64; - } - } -} \ No newline at end of file diff --git a/Decoder/AOpCodeSimdCvt.cs b/Decoder/AOpCodeSimdCvt.cs deleted file mode 100644 index 41f4d3b..0000000 --- a/Decoder/AOpCodeSimdCvt.cs +++ /dev/null @@ -1,31 +0,0 @@ -using ChocolArm64.Instruction; -using ChocolArm64.State; - -namespace ChocolArm64.Decoder -{ - class AOpCodeSimdCvt : AOpCodeSimd - { - public int FBits { get; private set; } - - public AOpCodeSimdCvt(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - //TODO: - //Und of Fixed Point variants. - int Scale = (OpCode >> 10) & 0x3f; - int SF = (OpCode >> 31) & 0x1; - - /*if (Type != SF && !(Type == 2 && SF == 1)) - { - Emitter = AInstEmit.Und; - - return; - }*/ - - FBits = 64 - Scale; - - RegisterSize = SF != 0 - ? ARegisterSize.Int64 - : ARegisterSize.Int32; - } - } -} \ No newline at end of file diff --git a/Decoder/AOpCodeSimdExt.cs b/Decoder/AOpCodeSimdExt.cs deleted file mode 100644 index 888e447..0000000 --- a/Decoder/AOpCodeSimdExt.cs +++ /dev/null @@ -1,14 +0,0 @@ -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder -{ - class AOpCodeSimdExt : AOpCodeSimdReg - { - public int Imm4 { get; private set; } - - public AOpCodeSimdExt(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - Imm4 = (OpCode >> 11) & 0xf; - } - } -} \ No newline at end of file diff --git a/Decoder/AOpCodeSimdFcond.cs b/Decoder/AOpCodeSimdFcond.cs deleted file mode 100644 index e38e742..0000000 --- a/Decoder/AOpCodeSimdFcond.cs +++ /dev/null @@ -1,17 +0,0 @@ -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder -{ - class AOpCodeSimdFcond : AOpCodeSimdReg, IAOpCodeCond - { - public int NZCV { get; private set; } - - public ACond Cond { get; private set; } - - public AOpCodeSimdFcond(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - NZCV = (OpCode >> 0) & 0xf; - Cond = (ACond)((OpCode >> 12) & 0xf); - } - } -} \ No newline at end of file diff --git a/Decoder/AOpCodeSimdFmov.cs b/Decoder/AOpCodeSimdFmov.cs deleted file mode 100644 index 3f88895..0000000 --- a/Decoder/AOpCodeSimdFmov.cs +++ /dev/null @@ -1,33 +0,0 @@ -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder -{ - class AOpCodeSimdFmov : AOpCode, IAOpCodeSimd - { - public int Rd { get; private set; } - public long Imm { get; private set; } - public int Size { get; private set; } - - public AOpCodeSimdFmov(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - int Imm5 = (OpCode >> 5) & 0x1f; - int Type = (OpCode >> 22) & 0x3; - - if (Imm5 != 0b00000 || Type > 1) - { - Emitter = AInstEmit.Und; - - return; - } - - Size = Type; - - long Imm; - - Rd = (OpCode >> 0) & 0x1f; - Imm = (OpCode >> 13) & 0xff; - - this.Imm = ADecoderHelper.DecodeImm8Float(Imm, Type); - } - } -} \ No newline at end of file diff --git a/Decoder/AOpCodeSimdImm.cs b/Decoder/AOpCodeSimdImm.cs deleted file mode 100644 index e7dfe62..0000000 --- a/Decoder/AOpCodeSimdImm.cs +++ /dev/null @@ -1,101 +0,0 @@ -using ChocolArm64.Instruction; -using ChocolArm64.State; - -namespace ChocolArm64.Decoder -{ - class AOpCodeSimdImm : AOpCode, IAOpCodeSimd - { - public int Rd { get; private set; } - public long Imm { get; private set; } - public int Size { get; private set; } - - public AOpCodeSimdImm(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - Rd = OpCode & 0x1f; - - int CMode = (OpCode >> 12) & 0xf; - int Op = (OpCode >> 29) & 0x1; - - int ModeLow = CMode & 1; - int ModeHigh = CMode >> 1; - - long Imm; - - Imm = ((uint)OpCode >> 5) & 0x1f; - Imm |= ((uint)OpCode >> 11) & 0xe0; - - if (ModeHigh == 0b111) - { - Size = ModeLow != 0 ? Op : 3; - - switch (Op | (ModeLow << 1)) - { - case 0: - //64-bits Immediate. - //Transform abcd efgh into abcd efgh abcd efgh ... - Imm = (long)((ulong)Imm * 0x0101010101010101); - break; - - case 1: - //64-bits Immediate. - //Transform abcd efgh into aaaa aaaa bbbb bbbb ... - Imm = (Imm & 0xf0) >> 4 | (Imm & 0x0f) << 4; - Imm = (Imm & 0xcc) >> 2 | (Imm & 0x33) << 2; - Imm = (Imm & 0xaa) >> 1 | (Imm & 0x55) << 1; - - Imm = (long)((ulong)Imm * 0x8040201008040201); - Imm = (long)((ulong)Imm & 0x8080808080808080); - - Imm |= Imm >> 4; - Imm |= Imm >> 2; - Imm |= Imm >> 1; - break; - - case 2: - case 3: - //Floating point Immediate. - Imm = ADecoderHelper.DecodeImm8Float(Imm, Size); - break; - } - } - else if ((ModeHigh & 0b110) == 0b100) - { - //16-bits shifted Immediate. - Size = 1; Imm <<= (ModeHigh & 1) << 3; - } - else if ((ModeHigh & 0b100) == 0b000) - { - //32-bits shifted Immediate. - Size = 2; Imm <<= ModeHigh << 3; - } - else if ((ModeHigh & 0b111) == 0b110) - { - //32-bits shifted Immediate (fill with ones). - Size = 2; Imm = ShlOnes(Imm, 8 << ModeLow); - } - else - { - //8 bits without shift. - Size = 0; - } - - this.Imm = Imm; - - RegisterSize = ((OpCode >> 30) & 1) != 0 - ? ARegisterSize.SIMD128 - : ARegisterSize.SIMD64; - } - - private static long ShlOnes(long Value, int Shift) - { - if (Shift != 0) - { - return Value << Shift | (long)(ulong.MaxValue >> (64 - Shift)); - } - else - { - return Value; - } - } - } -} \ No newline at end of file diff --git a/Decoder/AOpCodeSimdIns.cs b/Decoder/AOpCodeSimdIns.cs deleted file mode 100644 index 0b60bbe..0000000 --- a/Decoder/AOpCodeSimdIns.cs +++ /dev/null @@ -1,36 +0,0 @@ -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder -{ - class AOpCodeSimdIns : AOpCodeSimd - { - public int SrcIndex { get; private set; } - public int DstIndex { get; private set; } - - public AOpCodeSimdIns(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - int Imm4 = (OpCode >> 11) & 0xf; - int Imm5 = (OpCode >> 16) & 0x1f; - - if (Imm5 == 0b10000) - { - Emitter = AInstEmit.Und; - - return; - } - - Size = Imm5 & -Imm5; - - switch (Size) - { - case 1: Size = 0; break; - case 2: Size = 1; break; - case 4: Size = 2; break; - case 8: Size = 3; break; - } - - SrcIndex = Imm4 >> Size; - DstIndex = Imm5 >> (Size + 1); - } - } -} \ No newline at end of file diff --git a/Decoder/AOpCodeSimdMemImm.cs b/Decoder/AOpCodeSimdMemImm.cs deleted file mode 100644 index 1ef19a5..0000000 --- a/Decoder/AOpCodeSimdMemImm.cs +++ /dev/null @@ -1,19 +0,0 @@ -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder -{ - class AOpCodeSimdMemImm : AOpCodeMemImm, IAOpCodeSimd - { - public AOpCodeSimdMemImm(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - Size |= (OpCode >> 21) & 4; - - if (!WBack && !Unscaled && Size >= 4) - { - Imm <<= 4; - } - - Extend64 = false; - } - } -} \ No newline at end of file diff --git a/Decoder/AOpCodeSimdMemLit.cs b/Decoder/AOpCodeSimdMemLit.cs deleted file mode 100644 index ea6fe00..0000000 --- a/Decoder/AOpCodeSimdMemLit.cs +++ /dev/null @@ -1,31 +0,0 @@ -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder -{ - class AOpCodeSimdMemLit : AOpCode, IAOpCodeSimd, IAOpCodeLit - { - public int Rt { get; private set; } - public long Imm { get; private set; } - public int Size { get; private set; } - public bool Signed => false; - public bool Prefetch => false; - - public AOpCodeSimdMemLit(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - int Opc = (OpCode >> 30) & 3; - - if (Opc == 3) - { - Emitter = AInstEmit.Und; - - return; - } - - Rt = OpCode & 0x1f; - - Imm = Position + ADecoderHelper.DecodeImmS19_2(OpCode); - - Size = Opc + 2; - } - } -} \ No newline at end of file diff --git a/Decoder/AOpCodeSimdMemPair.cs b/Decoder/AOpCodeSimdMemPair.cs deleted file mode 100644 index db99e3d..0000000 --- a/Decoder/AOpCodeSimdMemPair.cs +++ /dev/null @@ -1,16 +0,0 @@ -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder -{ - class AOpCodeSimdMemPair : AOpCodeMemPair, IAOpCodeSimd - { - public AOpCodeSimdMemPair(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - Size = ((OpCode >> 30) & 3) + 2; - - Extend64 = false; - - DecodeImm(OpCode); - } - } -} \ No newline at end of file diff --git a/Decoder/AOpCodeSimdMemReg.cs b/Decoder/AOpCodeSimdMemReg.cs deleted file mode 100644 index aabf484..0000000 --- a/Decoder/AOpCodeSimdMemReg.cs +++ /dev/null @@ -1,14 +0,0 @@ -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder -{ - class AOpCodeSimdMemReg : AOpCodeMemReg, IAOpCodeSimd - { - public AOpCodeSimdMemReg(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - Size |= (OpCode >> 21) & 4; - - Extend64 = false; - } - } -} \ No newline at end of file diff --git a/Decoder/AOpCodeSimdMemSs.cs b/Decoder/AOpCodeSimdMemSs.cs deleted file mode 100644 index c8794ff..0000000 --- a/Decoder/AOpCodeSimdMemSs.cs +++ /dev/null @@ -1,98 +0,0 @@ -using ChocolArm64.Instruction; -using ChocolArm64.State; - -namespace ChocolArm64.Decoder -{ - class AOpCodeSimdMemSs : AOpCodeMemReg, IAOpCodeSimd - { - public int SElems { get; private set; } - public int Index { get; private set; } - public bool Replicate { get; private set; } - public bool WBack { get; private set; } - - public AOpCodeSimdMemSs(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - int Size = (OpCode >> 10) & 3; - int S = (OpCode >> 12) & 1; - int SElems = (OpCode >> 12) & 2; - int Scale = (OpCode >> 14) & 3; - int L = (OpCode >> 22) & 1; - int Q = (OpCode >> 30) & 1; - - SElems |= (OpCode >> 21) & 1; - - SElems++; - - int Index = (Q << 3) | (S << 2) | Size; - - switch (Scale) - { - case 1: - { - if ((Size & 1) != 0) - { - Inst = AInst.Undefined; - - return; - } - - Index >>= 1; - - break; - } - - case 2: - { - if ((Size & 2) != 0 || - ((Size & 1) != 0 && S != 0)) - { - Inst = AInst.Undefined; - - return; - } - - if ((Size & 1) != 0) - { - Index >>= 3; - - Scale = 3; - } - else - { - Index >>= 2; - } - - break; - } - - case 3: - { - if (L == 0 || S != 0) - { - Inst = AInst.Undefined; - - return; - } - - Scale = Size; - - Replicate = true; - - break; - } - } - - this.Index = Index; - this.SElems = SElems; - this.Size = Scale; - - Extend64 = false; - - WBack = ((OpCode >> 23) & 1) != 0; - - RegisterSize = Q != 0 - ? ARegisterSize.SIMD128 - : ARegisterSize.SIMD64; - } - } -} \ No newline at end of file diff --git a/Decoder/AOpCodeSimdReg.cs b/Decoder/AOpCodeSimdReg.cs deleted file mode 100644 index 702ffed..0000000 --- a/Decoder/AOpCodeSimdReg.cs +++ /dev/null @@ -1,18 +0,0 @@ -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder -{ - class AOpCodeSimdReg : AOpCodeSimd - { - public bool Bit3 { get; private set; } - public int Ra { get; private set; } - public int Rm { get; protected set; } - - public AOpCodeSimdReg(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - Bit3 = ((OpCode >> 3) & 0x1) != 0; - Ra = (OpCode >> 10) & 0x1f; - Rm = (OpCode >> 16) & 0x1f; - } - } -} \ No newline at end of file diff --git a/Decoder/AOpCodeSimdRegElem.cs b/Decoder/AOpCodeSimdRegElem.cs deleted file mode 100644 index d6dc4bd..0000000 --- a/Decoder/AOpCodeSimdRegElem.cs +++ /dev/null @@ -1,31 +0,0 @@ -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder -{ - class AOpCodeSimdRegElem : AOpCodeSimdReg - { - public int Index { get; private set; } - - public AOpCodeSimdRegElem(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - switch (Size) - { - case 1: - Index = (OpCode >> 20) & 3 | - (OpCode >> 9) & 4; - - Rm &= 0xf; - - break; - - case 2: - Index = (OpCode >> 21) & 1 | - (OpCode >> 10) & 2; - - break; - - default: Emitter = AInstEmit.Und; return; - } - } - } -} \ No newline at end of file diff --git a/Decoder/AOpCodeSimdRegElemF.cs b/Decoder/AOpCodeSimdRegElemF.cs deleted file mode 100644 index e0670de..0000000 --- a/Decoder/AOpCodeSimdRegElemF.cs +++ /dev/null @@ -1,33 +0,0 @@ -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder -{ - class AOpCodeSimdRegElemF : AOpCodeSimdReg - { - public int Index { get; private set; } - - public AOpCodeSimdRegElemF(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - switch ((OpCode >> 21) & 3) // sz:L - { - case 0: // H:0 - Index = (OpCode >> 10) & 2; // 0, 2 - - break; - - case 1: // H:1 - Index = (OpCode >> 10) & 2; - Index++; // 1, 3 - - break; - - case 2: // H - Index = (OpCode >> 11) & 1; // 0, 1 - - break; - - default: Emitter = AInstEmit.Und; return; - } - } - } -} diff --git a/Decoder/AOpCodeSimdShImm.cs b/Decoder/AOpCodeSimdShImm.cs deleted file mode 100644 index e6d5210..0000000 --- a/Decoder/AOpCodeSimdShImm.cs +++ /dev/null @@ -1,16 +0,0 @@ -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder -{ - class AOpCodeSimdShImm : AOpCodeSimd - { - public int Imm { get; private set; } - - public AOpCodeSimdShImm(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - Imm = (OpCode >> 16) & 0x7f; - - Size = ABitUtils.HighestBitSetNibble(Imm >> 3); - } - } -} diff --git a/Decoder/AOpCodeSimdTbl.cs b/Decoder/AOpCodeSimdTbl.cs deleted file mode 100644 index c8ae5ba..0000000 --- a/Decoder/AOpCodeSimdTbl.cs +++ /dev/null @@ -1,12 +0,0 @@ -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder -{ - class AOpCodeSimdTbl : AOpCodeSimdReg - { - public AOpCodeSimdTbl(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - Size = ((OpCode >> 13) & 3) + 1; - } - } -} \ No newline at end of file diff --git a/Decoder/AOpCodeSystem.cs b/Decoder/AOpCodeSystem.cs deleted file mode 100644 index 3d81a5d..0000000 --- a/Decoder/AOpCodeSystem.cs +++ /dev/null @@ -1,24 +0,0 @@ -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder -{ - class AOpCodeSystem : AOpCode - { - public int Rt { get; private set; } - public int Op2 { get; private set; } - public int CRm { get; private set; } - public int CRn { get; private set; } - public int Op1 { get; private set; } - public int Op0 { get; private set; } - - public AOpCodeSystem(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - Rt = (OpCode >> 0) & 0x1f; - Op2 = (OpCode >> 5) & 0x7; - CRm = (OpCode >> 8) & 0xf; - CRn = (OpCode >> 12) & 0xf; - Op1 = (OpCode >> 16) & 0x7; - Op0 = ((OpCode >> 19) & 0x1) | 2; - } - } -} \ No newline at end of file diff --git a/Decoder/IAOpCode.cs b/Decoder/IAOpCode.cs deleted file mode 100644 index 44bf9cb..0000000 --- a/Decoder/IAOpCode.cs +++ /dev/null @@ -1,13 +0,0 @@ -using ChocolArm64.Instruction; -using ChocolArm64.State; - -namespace ChocolArm64.Decoder -{ - interface IAOpCode - { - long Position { get; } - - AInstEmitter Emitter { get; } - ARegisterSize RegisterSize { get; } - } -} \ No newline at end of file diff --git a/Decoder/IAOpCodeAlu.cs b/Decoder/IAOpCodeAlu.cs deleted file mode 100644 index 22af4c8..0000000 --- a/Decoder/IAOpCodeAlu.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace ChocolArm64.Decoder -{ - interface IAOpCodeAlu : IAOpCode - { - int Rd { get; } - int Rn { get; } - - ADataOp DataOp { get; } - } -} \ No newline at end of file diff --git a/Decoder/IAOpCodeAluImm.cs b/Decoder/IAOpCodeAluImm.cs deleted file mode 100644 index 04b5c5f..0000000 --- a/Decoder/IAOpCodeAluImm.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace ChocolArm64.Decoder -{ - interface IAOpCodeAluImm : IAOpCodeAlu - { - long Imm { get; } - } -} \ No newline at end of file diff --git a/Decoder/IAOpCodeAluRs.cs b/Decoder/IAOpCodeAluRs.cs deleted file mode 100644 index 5ca9de4..0000000 --- a/Decoder/IAOpCodeAluRs.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace ChocolArm64.Decoder -{ - interface IAOpCodeAluRs : IAOpCodeAlu - { - int Shift { get; } - int Rm { get; } - - AShiftType ShiftType { get; } - } -} \ No newline at end of file diff --git a/Decoder/IAOpCodeAluRx.cs b/Decoder/IAOpCodeAluRx.cs deleted file mode 100644 index b49d532..0000000 --- a/Decoder/IAOpCodeAluRx.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace ChocolArm64.Decoder -{ - interface IAOpCodeAluRx : IAOpCodeAlu - { - int Shift { get; } - int Rm { get; } - - AIntType IntType { get; } - } -} \ No newline at end of file diff --git a/Decoder/IAOpCodeCond.cs b/Decoder/IAOpCodeCond.cs deleted file mode 100644 index 1655aba..0000000 --- a/Decoder/IAOpCodeCond.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace ChocolArm64.Decoder -{ - interface IAOpCodeCond : IAOpCode - { - ACond Cond { get; } - } -} \ No newline at end of file diff --git a/Decoder/IAOpCodeSimd.cs b/Decoder/IAOpCodeSimd.cs deleted file mode 100644 index 19032ad..0000000 --- a/Decoder/IAOpCodeSimd.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace ChocolArm64.Decoder -{ - interface IAOpCodeSimd : IAOpCode - { - int Size { get; } - } -} \ No newline at end of file diff --git a/Decoder32/A32OpCode.cs b/Decoder32/A32OpCode.cs deleted file mode 100644 index 56f870d..0000000 --- a/Decoder32/A32OpCode.cs +++ /dev/null @@ -1,15 +0,0 @@ -using ChocolArm64.Decoder; -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder32 -{ - class A32OpCode : AOpCode - { - public ACond Cond { get; private set; } - - public A32OpCode(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - Cond = (ACond)((uint)OpCode >> 28); - } - } -} \ No newline at end of file diff --git a/Decoder32/A32OpCodeBImmAl.cs b/Decoder32/A32OpCodeBImmAl.cs deleted file mode 100644 index 71bca7f..0000000 --- a/Decoder32/A32OpCodeBImmAl.cs +++ /dev/null @@ -1,16 +0,0 @@ -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder32 -{ - class A32OpCodeBImmAl : A32OpCode - { - public int Imm; - public int H; - - public A32OpCodeBImmAl(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - Imm = (OpCode << 8) >> 6; - H = (OpCode >> 23) & 2; - } - } -} \ No newline at end of file diff --git a/Decoders/Block.cs b/Decoders/Block.cs new file mode 100644 index 0000000..c89ea7c --- /dev/null +++ b/Decoders/Block.cs @@ -0,0 +1,35 @@ +using System.Collections.Generic; + +namespace ChocolArm64.Decoders +{ + class Block + { + public long Position { get; set; } + public long EndPosition { get; set; } + + public Block Next { get; set; } + public Block Branch { get; set; } + + public List OpCodes { get; private set; } + + public Block() + { + OpCodes = new List(); + } + + public Block(long position) : this() + { + Position = position; + } + + public OpCode64 GetLastOp() + { + if (OpCodes.Count > 0) + { + return OpCodes[OpCodes.Count - 1]; + } + + return null; + } + } +} \ No newline at end of file diff --git a/Decoders/Cond.cs b/Decoders/Cond.cs new file mode 100644 index 0000000..57e12cd --- /dev/null +++ b/Decoders/Cond.cs @@ -0,0 +1,22 @@ +namespace ChocolArm64.Decoders +{ + enum Cond + { + Eq = 0, + Ne = 1, + GeUn = 2, + LtUn = 3, + Mi = 4, + Pl = 5, + Vs = 6, + Vc = 7, + GtUn = 8, + LeUn = 9, + Ge = 10, + Lt = 11, + Gt = 12, + Le = 13, + Al = 14, + Nv = 15 + } +} \ No newline at end of file diff --git a/Decoder/ADataOp.cs b/Decoders/DataOp.cs similarity index 69% rename from Decoder/ADataOp.cs rename to Decoders/DataOp.cs index a5601a3..b7768bb 100644 --- a/Decoder/ADataOp.cs +++ b/Decoders/DataOp.cs @@ -1,6 +1,6 @@ -namespace ChocolArm64.Decoder +namespace ChocolArm64.Decoders { - enum ADataOp + enum DataOp { Adr = 0, Arithmetic = 1, diff --git a/Decoders/Decoder.cs b/Decoders/Decoder.cs new file mode 100644 index 0000000..db43ac4 --- /dev/null +++ b/Decoders/Decoder.cs @@ -0,0 +1,239 @@ +using ChocolArm64.Instructions; +using ChocolArm64.Memory; +using ChocolArm64.State; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Reflection.Emit; + +namespace ChocolArm64.Decoders +{ + static class Decoder + { + private delegate object OpActivator(Inst inst, long position, int opCode); + + private static ConcurrentDictionary _opActivators; + + static Decoder() + { + _opActivators = new ConcurrentDictionary(); + } + + public static Block DecodeBasicBlock(CpuThreadState state, MemoryManager memory, long start) + { + Block block = new Block(start); + + FillBlock(state, memory, block); + + return block; + } + + public static (Block[] Graph, Block Root) DecodeSubroutine( + TranslatorCache cache, + CpuThreadState state, + MemoryManager memory, + long start) + { + Dictionary visited = new Dictionary(); + Dictionary visitedEnd = new Dictionary(); + + Queue blocks = new Queue(); + + Block Enqueue(long position) + { + if (!visited.TryGetValue(position, out Block output)) + { + output = new Block(position); + + blocks.Enqueue(output); + + visited.Add(position, output); + } + + return output; + } + + Block root = Enqueue(start); + + while (blocks.Count > 0) + { + Block current = blocks.Dequeue(); + + FillBlock(state, memory, current); + + //Set child blocks. "Branch" is the block the branch instruction + //points to (when taken), "Next" is the block at the next address, + //executed when the branch is not taken. For Unconditional Branches + //(except BL/BLR that are sub calls) or end of executable, Next is null. + if (current.OpCodes.Count > 0) + { + bool hasCachedSub = false; + + OpCode64 lastOp = current.GetLastOp(); + + if (lastOp is OpCodeBImm64 op) + { + if (op.Emitter == InstEmit.Bl) + { + hasCachedSub = cache.HasSubroutine(op.Imm); + } + else + { + current.Branch = Enqueue(op.Imm); + } + } + + if (!((lastOp is OpCodeBImmAl64) || + (lastOp is OpCodeBReg64)) || hasCachedSub) + { + current.Next = Enqueue(current.EndPosition); + } + } + + //If we have on the graph two blocks with the same end position, + //then we need to split the bigger block and have two small blocks, + //the end position of the bigger "Current" block should then be == to + //the position of the "Smaller" block. + while (visitedEnd.TryGetValue(current.EndPosition, out Block smaller)) + { + if (current.Position > smaller.Position) + { + Block temp = smaller; + + smaller = current; + current = temp; + } + + current.EndPosition = smaller.Position; + current.Next = smaller; + current.Branch = null; + + current.OpCodes.RemoveRange( + current.OpCodes.Count - smaller.OpCodes.Count, + smaller.OpCodes.Count); + + visitedEnd[smaller.EndPosition] = smaller; + } + + visitedEnd.Add(current.EndPosition, current); + } + + //Make and sort Graph blocks array by position. + Block[] graph = new Block[visited.Count]; + + while (visited.Count > 0) + { + ulong firstPos = ulong.MaxValue; + + foreach (Block block in visited.Values) + { + if (firstPos > (ulong)block.Position) + firstPos = (ulong)block.Position; + } + + Block current = visited[(long)firstPos]; + + do + { + graph[graph.Length - visited.Count] = current; + + visited.Remove(current.Position); + + current = current.Next; + } + while (current != null); + } + + return (graph, root); + } + + private static void FillBlock(CpuThreadState state, MemoryManager memory, Block block) + { + long position = block.Position; + + OpCode64 opCode; + + do + { + //TODO: This needs to be changed to support both AArch32 and AArch64, + //once JIT support is introduced on AArch32 aswell. + opCode = DecodeOpCode(state, memory, position); + + block.OpCodes.Add(opCode); + + position += 4; + } + while (!(IsBranch(opCode) || IsException(opCode))); + + block.EndPosition = position; + } + + private static bool IsBranch(OpCode64 opCode) + { + return opCode is OpCodeBImm64 || + opCode is OpCodeBReg64; + } + + private static bool IsException(OpCode64 opCode) + { + return opCode.Emitter == InstEmit.Brk || + opCode.Emitter == InstEmit.Svc || + opCode.Emitter == InstEmit.Und; + } + + public static OpCode64 DecodeOpCode(CpuThreadState state, MemoryManager memory, long position) + { + int opCode = memory.ReadInt32(position); + + Inst inst; + + if (state.ExecutionMode == ExecutionMode.AArch64) + { + inst = OpCodeTable.GetInstA64(opCode); + } + else + { + //TODO: Thumb support. + inst = OpCodeTable.GetInstA32(opCode); + } + + OpCode64 decodedOpCode = new OpCode64(Inst.Undefined, position, opCode); + + if (inst.Type != null) + { + decodedOpCode = MakeOpCode(inst.Type, inst, position, opCode); + } + + return decodedOpCode; + } + + private static OpCode64 MakeOpCode(Type type, Inst inst, long position, int opCode) + { + if (type == null) + { + throw new ArgumentNullException(nameof(type)); + } + + OpActivator createInstance = _opActivators.GetOrAdd(type, CacheOpActivator); + + return (OpCode64)createInstance(inst, position, opCode); + } + + private static OpActivator CacheOpActivator(Type type) + { + Type[] argTypes = new Type[] { typeof(Inst), typeof(long), typeof(int) }; + + DynamicMethod mthd = new DynamicMethod($"Make{type.Name}", type, argTypes); + + ILGenerator generator = mthd.GetILGenerator(); + + generator.Emit(OpCodes.Ldarg_0); + generator.Emit(OpCodes.Ldarg_1); + generator.Emit(OpCodes.Ldarg_2); + generator.Emit(OpCodes.Newobj, type.GetConstructor(argTypes)); + generator.Emit(OpCodes.Ret); + + return (OpActivator)mthd.CreateDelegate(typeof(OpActivator)); + } + } +} \ No newline at end of file diff --git a/Decoders/DecoderHelper.cs b/Decoders/DecoderHelper.cs new file mode 100644 index 0000000..6ee279d --- /dev/null +++ b/Decoders/DecoderHelper.cs @@ -0,0 +1,107 @@ +using System; + +namespace ChocolArm64.Decoders +{ + static class DecoderHelper + { + public struct BitMask + { + public long WMask; + public long TMask; + public int Pos; + public int Shift; + public bool IsUndefined; + + public static BitMask Invalid => new BitMask { IsUndefined = true }; + } + + public static BitMask DecodeBitMask(int opCode, bool immediate) + { + int immS = (opCode >> 10) & 0x3f; + int immR = (opCode >> 16) & 0x3f; + + int n = (opCode >> 22) & 1; + int sf = (opCode >> 31) & 1; + + int length = BitUtils.HighestBitSet32((~immS & 0x3f) | (n << 6)); + + if (length < 1 || (sf == 0 && n != 0)) + { + return BitMask.Invalid; + } + + int size = 1 << length; + + int levels = size - 1; + + int s = immS & levels; + int r = immR & levels; + + if (immediate && s == levels) + { + return BitMask.Invalid; + } + + long wMask = BitUtils.FillWithOnes(s + 1); + long tMask = BitUtils.FillWithOnes(((s - r) & levels) + 1); + + if (r > 0) + { + wMask = BitUtils.RotateRight(wMask, r, size); + wMask &= BitUtils.FillWithOnes(size); + } + + return new BitMask() + { + WMask = BitUtils.Replicate(wMask, size), + TMask = BitUtils.Replicate(tMask, size), + + Pos = immS, + Shift = immR + }; + } + + public static long DecodeImm8Float(long imm, int size) + { + int e = 0, f = 0; + + switch (size) + { + case 0: e = 8; f = 23; break; + case 1: e = 11; f = 52; break; + + default: throw new ArgumentOutOfRangeException(nameof(size)); + } + + long value = (imm & 0x3f) << f - 4; + + long eBit = (imm >> 6) & 1; + long sBit = (imm >> 7) & 1; + + if (eBit != 0) + { + value |= (1L << e - 3) - 1 << f + 2; + } + + value |= (eBit ^ 1) << f + e - 1; + value |= sBit << f + e; + + return value; + } + + public static long DecodeImm26_2(int opCode) + { + return ((long)opCode << 38) >> 36; + } + + public static long DecodeImmS19_2(int opCode) + { + return (((long)opCode << 40) >> 43) & ~3; + } + + public static long DecodeImmS14_2(int opCode) + { + return (((long)opCode << 45) >> 48) & ~3; + } + } +} \ No newline at end of file diff --git a/Decoders/IOpCode64.cs b/Decoders/IOpCode64.cs new file mode 100644 index 0000000..e940712 --- /dev/null +++ b/Decoders/IOpCode64.cs @@ -0,0 +1,13 @@ +using ChocolArm64.Instructions; +using ChocolArm64.State; + +namespace ChocolArm64.Decoders +{ + interface IOpCode64 + { + long Position { get; } + + InstEmitter Emitter { get; } + RegisterSize RegisterSize { get; } + } +} \ No newline at end of file diff --git a/Decoders/IOpCodeAlu64.cs b/Decoders/IOpCodeAlu64.cs new file mode 100644 index 0000000..b9a5fe9 --- /dev/null +++ b/Decoders/IOpCodeAlu64.cs @@ -0,0 +1,10 @@ +namespace ChocolArm64.Decoders +{ + interface IOpCodeAlu64 : IOpCode64 + { + int Rd { get; } + int Rn { get; } + + DataOp DataOp { get; } + } +} \ No newline at end of file diff --git a/Decoders/IOpCodeAluImm64.cs b/Decoders/IOpCodeAluImm64.cs new file mode 100644 index 0000000..4b305e2 --- /dev/null +++ b/Decoders/IOpCodeAluImm64.cs @@ -0,0 +1,7 @@ +namespace ChocolArm64.Decoders +{ + interface IOpCodeAluImm64 : IOpCodeAlu64 + { + long Imm { get; } + } +} \ No newline at end of file diff --git a/Decoders/IOpCodeAluRs64.cs b/Decoders/IOpCodeAluRs64.cs new file mode 100644 index 0000000..df503ae --- /dev/null +++ b/Decoders/IOpCodeAluRs64.cs @@ -0,0 +1,10 @@ +namespace ChocolArm64.Decoders +{ + interface IOpCodeAluRs64 : IOpCodeAlu64 + { + int Shift { get; } + int Rm { get; } + + ShiftType ShiftType { get; } + } +} \ No newline at end of file diff --git a/Decoders/IOpCodeAluRx64.cs b/Decoders/IOpCodeAluRx64.cs new file mode 100644 index 0000000..f41fc4d --- /dev/null +++ b/Decoders/IOpCodeAluRx64.cs @@ -0,0 +1,10 @@ +namespace ChocolArm64.Decoders +{ + interface IOpCodeAluRx64 : IOpCodeAlu64 + { + int Shift { get; } + int Rm { get; } + + IntType IntType { get; } + } +} \ No newline at end of file diff --git a/Decoders/IOpCodeCond64.cs b/Decoders/IOpCodeCond64.cs new file mode 100644 index 0000000..9c39d63 --- /dev/null +++ b/Decoders/IOpCodeCond64.cs @@ -0,0 +1,7 @@ +namespace ChocolArm64.Decoders +{ + interface IOpCodeCond64 : IOpCode64 + { + Cond Cond { get; } + } +} \ No newline at end of file diff --git a/Decoder/IAOpCodeLit.cs b/Decoders/IOpCodeLit64.cs similarity index 70% rename from Decoder/IAOpCodeLit.cs rename to Decoders/IOpCodeLit64.cs index 0f5092d..c6dc2c7 100644 --- a/Decoder/IAOpCodeLit.cs +++ b/Decoders/IOpCodeLit64.cs @@ -1,6 +1,6 @@ -namespace ChocolArm64.Decoder +namespace ChocolArm64.Decoders { - interface IAOpCodeLit : IAOpCode + interface IOpCodeLit64 : IOpCode64 { int Rt { get; } long Imm { get; } diff --git a/Decoders/IOpCodeSimd64.cs b/Decoders/IOpCodeSimd64.cs new file mode 100644 index 0000000..fc8f54d --- /dev/null +++ b/Decoders/IOpCodeSimd64.cs @@ -0,0 +1,7 @@ +namespace ChocolArm64.Decoders +{ + interface IOpCodeSimd64 : IOpCode64 + { + int Size { get; } + } +} \ No newline at end of file diff --git a/Decoder/AIntType.cs b/Decoders/IntType.cs similarity index 77% rename from Decoder/AIntType.cs rename to Decoders/IntType.cs index 242fdad..70f833e 100644 --- a/Decoder/AIntType.cs +++ b/Decoders/IntType.cs @@ -1,6 +1,6 @@ -namespace ChocolArm64.Decoder +namespace ChocolArm64.Decoders { - enum AIntType + enum IntType { UInt8 = 0, UInt16 = 1, diff --git a/Decoders/OpCode64.cs b/Decoders/OpCode64.cs new file mode 100644 index 0000000..b2dc363 --- /dev/null +++ b/Decoders/OpCode64.cs @@ -0,0 +1,40 @@ +using ChocolArm64.Instructions; +using ChocolArm64.State; +using System; + +namespace ChocolArm64.Decoders +{ + class OpCode64 : IOpCode64 + { + public long Position { get; private set; } + public int RawOpCode { get; private set; } + + public InstEmitter Emitter { get; protected set; } + public InstInterpreter Interpreter { get; protected set; } + public RegisterSize RegisterSize { get; protected set; } + + public OpCode64(Inst inst, long position, int opCode) + { + Position = position; + RawOpCode = opCode; + + RegisterSize = RegisterSize.Int64; + + Emitter = inst.Emitter; + Interpreter = inst.Interpreter; + } + + public int GetBitsCount() + { + switch (RegisterSize) + { + case RegisterSize.Int32: return 32; + case RegisterSize.Int64: return 64; + case RegisterSize.Simd64: return 64; + case RegisterSize.Simd128: return 128; + } + + throw new InvalidOperationException(); + } + } +} \ No newline at end of file diff --git a/Decoders/OpCodeAdr64.cs b/Decoders/OpCodeAdr64.cs new file mode 100644 index 0000000..98b2f07 --- /dev/null +++ b/Decoders/OpCodeAdr64.cs @@ -0,0 +1,18 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeAdr64 : OpCode64 + { + public int Rd { get; private set; } + public long Imm { get; private set; } + + public OpCodeAdr64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + Rd = opCode & 0x1f; + + Imm = DecoderHelper.DecodeImmS19_2(opCode); + Imm |= ((long)opCode >> 29) & 3; + } + } +} \ No newline at end of file diff --git a/Decoders/OpCodeAlu64.cs b/Decoders/OpCodeAlu64.cs new file mode 100644 index 0000000..5f09457 --- /dev/null +++ b/Decoders/OpCodeAlu64.cs @@ -0,0 +1,24 @@ +using ChocolArm64.Instructions; +using ChocolArm64.State; + +namespace ChocolArm64.Decoders +{ + class OpCodeAlu64 : OpCode64, IOpCodeAlu64 + { + public int Rd { get; protected set; } + public int Rn { get; private set; } + + public DataOp DataOp { get; private set; } + + public OpCodeAlu64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + Rd = (opCode >> 0) & 0x1f; + Rn = (opCode >> 5) & 0x1f; + DataOp = (DataOp)((opCode >> 24) & 0x3); + + RegisterSize = (opCode >> 31) != 0 + ? State.RegisterSize.Int64 + : State.RegisterSize.Int32; + } + } +} \ No newline at end of file diff --git a/Decoders/OpCodeAluImm64.cs b/Decoders/OpCodeAluImm64.cs new file mode 100644 index 0000000..64ac08a --- /dev/null +++ b/Decoders/OpCodeAluImm64.cs @@ -0,0 +1,39 @@ +using ChocolArm64.Instructions; +using System; + +namespace ChocolArm64.Decoders +{ + class OpCodeAluImm64 : OpCodeAlu64, IOpCodeAluImm64 + { + public long Imm { get; private set; } + + public OpCodeAluImm64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + if (DataOp == DataOp.Arithmetic) + { + Imm = (opCode >> 10) & 0xfff; + + int shift = (opCode >> 22) & 3; + + Imm <<= shift * 12; + } + else if (DataOp == DataOp.Logical) + { + var bm = DecoderHelper.DecodeBitMask(opCode, true); + + if (bm.IsUndefined) + { + Emitter = InstEmit.Und; + + return; + } + + Imm = bm.WMask; + } + else + { + throw new ArgumentException(nameof(opCode)); + } + } + } +} \ No newline at end of file diff --git a/Decoders/OpCodeAluRs64.cs b/Decoders/OpCodeAluRs64.cs new file mode 100644 index 0000000..f24c7f3 --- /dev/null +++ b/Decoders/OpCodeAluRs64.cs @@ -0,0 +1,29 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeAluRs64 : OpCodeAlu64, IOpCodeAluRs64 + { + public int Shift { get; private set; } + public int Rm { get; private set; } + + public ShiftType ShiftType { get; private set; } + + public OpCodeAluRs64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + int shift = (opCode >> 10) & 0x3f; + + if (shift >= GetBitsCount()) + { + Emitter = InstEmit.Und; + + return; + } + + Shift = shift; + + Rm = (opCode >> 16) & 0x1f; + ShiftType = (ShiftType)((opCode >> 22) & 0x3); + } + } +} \ No newline at end of file diff --git a/Decoders/OpCodeAluRx64.cs b/Decoders/OpCodeAluRx64.cs new file mode 100644 index 0000000..a36f94c --- /dev/null +++ b/Decoders/OpCodeAluRx64.cs @@ -0,0 +1,19 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeAluRx64 : OpCodeAlu64, IOpCodeAluRx64 + { + public int Shift { get; private set; } + public int Rm { get; private set; } + + public IntType IntType { get; private set; } + + public OpCodeAluRx64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + Shift = (opCode >> 10) & 0x7; + IntType = (IntType)((opCode >> 13) & 0x7); + Rm = (opCode >> 16) & 0x1f; + } + } +} \ No newline at end of file diff --git a/Decoders/OpCodeBImm64.cs b/Decoders/OpCodeBImm64.cs new file mode 100644 index 0000000..71c61ba --- /dev/null +++ b/Decoders/OpCodeBImm64.cs @@ -0,0 +1,11 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeBImm64 : OpCode64 + { + public long Imm { get; protected set; } + + public OpCodeBImm64(Inst inst, long position, int opCode) : base(inst, position, opCode) { } + } +} \ No newline at end of file diff --git a/Decoders/OpCodeBImmAl64.cs b/Decoders/OpCodeBImmAl64.cs new file mode 100644 index 0000000..f419ffa --- /dev/null +++ b/Decoders/OpCodeBImmAl64.cs @@ -0,0 +1,12 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeBImmAl64 : OpCodeBImm64 + { + public OpCodeBImmAl64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + Imm = position + DecoderHelper.DecodeImm26_2(opCode); + } + } +} \ No newline at end of file diff --git a/Decoders/OpCodeBImmCmp64.cs b/Decoders/OpCodeBImmCmp64.cs new file mode 100644 index 0000000..6f43319 --- /dev/null +++ b/Decoders/OpCodeBImmCmp64.cs @@ -0,0 +1,21 @@ +using ChocolArm64.Instructions; +using ChocolArm64.State; + +namespace ChocolArm64.Decoders +{ + class OpCodeBImmCmp64 : OpCodeBImm64 + { + public int Rt { get; private set; } + + public OpCodeBImmCmp64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + Rt = opCode & 0x1f; + + Imm = position + DecoderHelper.DecodeImmS19_2(opCode); + + RegisterSize = (opCode >> 31) != 0 + ? State.RegisterSize.Int64 + : State.RegisterSize.Int32; + } + } +} \ No newline at end of file diff --git a/Decoders/OpCodeBImmCond64.cs b/Decoders/OpCodeBImmCond64.cs new file mode 100644 index 0000000..2270230 --- /dev/null +++ b/Decoders/OpCodeBImmCond64.cs @@ -0,0 +1,25 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeBImmCond64 : OpCodeBImm64, IOpCodeCond64 + { + public Cond Cond { get; private set; } + + public OpCodeBImmCond64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + int o0 = (opCode >> 4) & 1; + + if (o0 != 0) + { + Emitter = InstEmit.Und; + + return; + } + + Cond = (Cond)(opCode & 0xf); + + Imm = position + DecoderHelper.DecodeImmS19_2(opCode); + } + } +} \ No newline at end of file diff --git a/Decoders/OpCodeBImmTest64.cs b/Decoders/OpCodeBImmTest64.cs new file mode 100644 index 0000000..a2e8bae --- /dev/null +++ b/Decoders/OpCodeBImmTest64.cs @@ -0,0 +1,20 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeBImmTest64 : OpCodeBImm64 + { + public int Rt { get; private set; } + public int Pos { get; private set; } + + public OpCodeBImmTest64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + Rt = opCode & 0x1f; + + Imm = position + DecoderHelper.DecodeImmS14_2(opCode); + + Pos = (opCode >> 19) & 0x1f; + Pos |= (opCode >> 26) & 0x20; + } + } +} \ No newline at end of file diff --git a/Decoders/OpCodeBReg64.cs b/Decoders/OpCodeBReg64.cs new file mode 100644 index 0000000..74dbff5 --- /dev/null +++ b/Decoders/OpCodeBReg64.cs @@ -0,0 +1,24 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeBReg64 : OpCode64 + { + public int Rn { get; private set; } + + public OpCodeBReg64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + int op4 = (opCode >> 0) & 0x1f; + int op2 = (opCode >> 16) & 0x1f; + + if (op2 != 0b11111 || op4 != 0b00000) + { + Emitter = InstEmit.Und; + + return; + } + + Rn = (opCode >> 5) & 0x1f; + } + } +} \ No newline at end of file diff --git a/Decoders/OpCodeBfm64.cs b/Decoders/OpCodeBfm64.cs new file mode 100644 index 0000000..6891a8f --- /dev/null +++ b/Decoders/OpCodeBfm64.cs @@ -0,0 +1,29 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeBfm64 : OpCodeAlu64 + { + public long WMask { get; private set; } + public long TMask { get; private set; } + public int Pos { get; private set; } + public int Shift { get; private set; } + + public OpCodeBfm64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + var bm = DecoderHelper.DecodeBitMask(opCode, false); + + if (bm.IsUndefined) + { + Emitter = InstEmit.Und; + + return; + } + + WMask = bm.WMask; + TMask = bm.TMask; + Pos = bm.Pos; + Shift = bm.Shift; + } + } +} \ No newline at end of file diff --git a/Decoders/OpCodeCcmp64.cs b/Decoders/OpCodeCcmp64.cs new file mode 100644 index 0000000..e2aae96 --- /dev/null +++ b/Decoders/OpCodeCcmp64.cs @@ -0,0 +1,31 @@ +using ChocolArm64.Instructions; +using ChocolArm64.State; + +namespace ChocolArm64.Decoders +{ + class OpCodeCcmp64 : OpCodeAlu64, IOpCodeCond64 + { + public int Nzcv { get; private set; } + protected int RmImm; + + public Cond Cond { get; private set; } + + public OpCodeCcmp64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + int o3 = (opCode >> 4) & 1; + + if (o3 != 0) + { + Emitter = InstEmit.Und; + + return; + } + + Nzcv = (opCode >> 0) & 0xf; + Cond = (Cond)((opCode >> 12) & 0xf); + RmImm = (opCode >> 16) & 0x1f; + + Rd = CpuThreadState.ZrIndex; + } + } +} \ No newline at end of file diff --git a/Decoders/OpCodeCcmpImm64.cs b/Decoders/OpCodeCcmpImm64.cs new file mode 100644 index 0000000..78d5de5 --- /dev/null +++ b/Decoders/OpCodeCcmpImm64.cs @@ -0,0 +1,11 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeCcmpImm64 : OpCodeCcmp64, IOpCodeAluImm64 + { + public long Imm => RmImm; + + public OpCodeCcmpImm64(Inst inst, long position, int opCode) : base(inst, position, opCode) { } + } +} \ No newline at end of file diff --git a/Decoders/OpCodeCcmpReg64.cs b/Decoders/OpCodeCcmpReg64.cs new file mode 100644 index 0000000..a0544d9 --- /dev/null +++ b/Decoders/OpCodeCcmpReg64.cs @@ -0,0 +1,15 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeCcmpReg64 : OpCodeCcmp64, IOpCodeAluRs64 + { + public int Rm => RmImm; + + public int Shift => 0; + + public ShiftType ShiftType => ShiftType.Lsl; + + public OpCodeCcmpReg64(Inst inst, long position, int opCode) : base(inst, position, opCode) { } + } +} \ No newline at end of file diff --git a/Decoders/OpCodeCsel64.cs b/Decoders/OpCodeCsel64.cs new file mode 100644 index 0000000..d085a82 --- /dev/null +++ b/Decoders/OpCodeCsel64.cs @@ -0,0 +1,17 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeCsel64 : OpCodeAlu64, IOpCodeCond64 + { + public int Rm { get; private set; } + + public Cond Cond { get; private set; } + + public OpCodeCsel64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + Rm = (opCode >> 16) & 0x1f; + Cond = (Cond)((opCode >> 12) & 0xf); + } + } +} \ No newline at end of file diff --git a/Decoders/OpCodeException64.cs b/Decoders/OpCodeException64.cs new file mode 100644 index 0000000..2554124 --- /dev/null +++ b/Decoders/OpCodeException64.cs @@ -0,0 +1,14 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeException64 : OpCode64 + { + public int Id { get; private set; } + + public OpCodeException64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + Id = (opCode >> 5) & 0xffff; + } + } +} \ No newline at end of file diff --git a/Decoders/OpCodeMem64.cs b/Decoders/OpCodeMem64.cs new file mode 100644 index 0000000..36e6758 --- /dev/null +++ b/Decoders/OpCodeMem64.cs @@ -0,0 +1,19 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeMem64 : OpCode64 + { + public int Rt { get; protected set; } + public int Rn { get; protected set; } + public int Size { get; protected set; } + public bool Extend64 { get; protected set; } + + public OpCodeMem64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + Rt = (opCode >> 0) & 0x1f; + Rn = (opCode >> 5) & 0x1f; + Size = (opCode >> 30) & 0x3; + } + } +} \ No newline at end of file diff --git a/Decoders/OpCodeMemEx64.cs b/Decoders/OpCodeMemEx64.cs new file mode 100644 index 0000000..39935eb --- /dev/null +++ b/Decoders/OpCodeMemEx64.cs @@ -0,0 +1,16 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeMemEx64 : OpCodeMem64 + { + public int Rt2 { get; private set; } + public int Rs { get; private set; } + + public OpCodeMemEx64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + Rt2 = (opCode >> 10) & 0x1f; + Rs = (opCode >> 16) & 0x1f; + } + } +} \ No newline at end of file diff --git a/Decoder/AOpCodeMemImm.cs b/Decoders/OpCodeMemImm64.cs similarity index 62% rename from Decoder/AOpCodeMemImm.cs rename to Decoders/OpCodeMemImm64.cs index 14edc51..edaa497 100644 --- a/Decoder/AOpCodeMemImm.cs +++ b/Decoders/OpCodeMemImm64.cs @@ -1,8 +1,8 @@ -using ChocolArm64.Instruction; +using ChocolArm64.Instructions; -namespace ChocolArm64.Decoder +namespace ChocolArm64.Decoders { - class AOpCodeMemImm : AOpCodeMem + class OpCodeMemImm64 : OpCodeMem64 { public long Imm { get; protected set; } public bool WBack { get; protected set; } @@ -18,18 +18,18 @@ namespace ChocolArm64.Decoder Unsigned } - public AOpCodeMemImm(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) + public OpCodeMemImm64(Inst inst, long position, int opCode) : base(inst, position, opCode) { - Extend64 = ((OpCode >> 22) & 3) == 2; - WBack = ((OpCode >> 24) & 1) == 0; + Extend64 = ((opCode >> 22) & 3) == 2; + WBack = ((opCode >> 24) & 1) == 0; //The type is not valid for the Unsigned Immediate 12-bits encoding, //because the bits 11:10 are used for the larger Immediate offset. - MemOp Type = WBack ? (MemOp)((OpCode >> 10) & 3) : MemOp.Unsigned; + MemOp type = WBack ? (MemOp)((opCode >> 10) & 3) : MemOp.Unsigned; - PostIdx = Type == MemOp.PostIndexed; - Unscaled = Type == MemOp.Unscaled || - Type == MemOp.Unprivileged; + PostIdx = type == MemOp.PostIndexed; + Unscaled = type == MemOp.Unscaled || + type == MemOp.Unprivileged; //Unscaled and Unprivileged doesn't write back, //but they do use the 9-bits Signed Immediate. @@ -41,12 +41,12 @@ namespace ChocolArm64.Decoder if (WBack || Unscaled) { //9-bits Signed Immediate. - Imm = (OpCode << 43) >> 55; + Imm = (opCode << 43) >> 55; } else { //12-bits Unsigned Immediate. - Imm = ((OpCode >> 10) & 0xfff) << Size; + Imm = ((opCode >> 10) & 0xfff) << Size; } } } diff --git a/Decoder/AOpCodeMemLit.cs b/Decoders/OpCodeMemLit64.cs similarity index 63% rename from Decoder/AOpCodeMemLit.cs rename to Decoders/OpCodeMemLit64.cs index ad719a1..29bfeee 100644 --- a/Decoder/AOpCodeMemLit.cs +++ b/Decoders/OpCodeMemLit64.cs @@ -1,8 +1,8 @@ -using ChocolArm64.Instruction; +using ChocolArm64.Instructions; -namespace ChocolArm64.Decoder +namespace ChocolArm64.Decoders { - class AOpCodeMemLit : AOpCode, IAOpCodeLit + class OpCodeMemLit64 : OpCode64, IOpCodeLit64 { public int Rt { get; private set; } public long Imm { get; private set; } @@ -10,13 +10,13 @@ namespace ChocolArm64.Decoder public bool Signed { get; private set; } public bool Prefetch { get; private set; } - public AOpCodeMemLit(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) + public OpCodeMemLit64(Inst inst, long position, int opCode) : base(inst, position, opCode) { - Rt = OpCode & 0x1f; + Rt = opCode & 0x1f; - Imm = Position + ADecoderHelper.DecodeImmS19_2(OpCode); + Imm = position + DecoderHelper.DecodeImmS19_2(opCode); - switch ((OpCode >> 30) & 3) + switch ((opCode >> 30) & 3) { case 0: Size = 2; Signed = false; Prefetch = false; break; case 1: Size = 3; Signed = false; Prefetch = false; break; diff --git a/Decoders/OpCodeMemPair64.cs b/Decoders/OpCodeMemPair64.cs new file mode 100644 index 0000000..5b81c75 --- /dev/null +++ b/Decoders/OpCodeMemPair64.cs @@ -0,0 +1,25 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeMemPair64 : OpCodeMemImm64 + { + public int Rt2 { get; private set; } + + public OpCodeMemPair64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + Rt2 = (opCode >> 10) & 0x1f; + WBack = ((opCode >> 23) & 0x1) != 0; + PostIdx = ((opCode >> 23) & 0x3) == 1; + Extend64 = ((opCode >> 30) & 0x3) == 1; + Size = ((opCode >> 31) & 0x1) | 2; + + DecodeImm(opCode); + } + + protected void DecodeImm(int opCode) + { + Imm = ((long)(opCode >> 15) << 57) >> (57 - Size); + } + } +} \ No newline at end of file diff --git a/Decoders/OpCodeMemReg64.cs b/Decoders/OpCodeMemReg64.cs new file mode 100644 index 0000000..3dd210f --- /dev/null +++ b/Decoders/OpCodeMemReg64.cs @@ -0,0 +1,20 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeMemReg64 : OpCodeMem64 + { + public bool Shift { get; private set; } + public int Rm { get; private set; } + + public IntType IntType { get; private set; } + + public OpCodeMemReg64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + Shift = ((opCode >> 12) & 0x1) != 0; + IntType = (IntType)((opCode >> 13) & 0x7); + Rm = (opCode >> 16) & 0x1f; + Extend64 = ((opCode >> 22) & 0x3) == 2; + } + } +} \ No newline at end of file diff --git a/Decoders/OpCodeMov64.cs b/Decoders/OpCodeMov64.cs new file mode 100644 index 0000000..f969785 --- /dev/null +++ b/Decoders/OpCodeMov64.cs @@ -0,0 +1,36 @@ +using ChocolArm64.Instructions; +using ChocolArm64.State; + +namespace ChocolArm64.Decoders +{ + class OpCodeMov64 : OpCode64 + { + public int Rd { get; private set; } + public long Imm { get; private set; } + public int Pos { get; private set; } + + public OpCodeMov64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + int p1 = (opCode >> 22) & 1; + int sf = (opCode >> 31) & 1; + + if (sf == 0 && p1 != 0) + { + Emitter = InstEmit.Und; + + return; + } + + Rd = (opCode >> 0) & 0x1f; + Imm = (opCode >> 5) & 0xffff; + Pos = (opCode >> 21) & 0x3; + + Pos <<= 4; + Imm <<= Pos; + + RegisterSize = (opCode >> 31) != 0 + ? State.RegisterSize.Int64 + : State.RegisterSize.Int32; + } + } +} \ No newline at end of file diff --git a/Decoders/OpCodeMul64.cs b/Decoders/OpCodeMul64.cs new file mode 100644 index 0000000..176b7b9 --- /dev/null +++ b/Decoders/OpCodeMul64.cs @@ -0,0 +1,16 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeMul64 : OpCodeAlu64 + { + public int Rm { get; private set; } + public int Ra { get; private set; } + + public OpCodeMul64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + Ra = (opCode >> 10) & 0x1f; + Rm = (opCode >> 16) & 0x1f; + } + } +} \ No newline at end of file diff --git a/Decoders/OpCodeSimd64.cs b/Decoders/OpCodeSimd64.cs new file mode 100644 index 0000000..a705e48 --- /dev/null +++ b/Decoders/OpCodeSimd64.cs @@ -0,0 +1,25 @@ +using ChocolArm64.Instructions; +using ChocolArm64.State; + +namespace ChocolArm64.Decoders +{ + class OpCodeSimd64 : OpCode64, IOpCodeSimd64 + { + public int Rd { get; private set; } + public int Rn { get; private set; } + public int Opc { get; private set; } + public int Size { get; protected set; } + + public OpCodeSimd64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + Rd = (opCode >> 0) & 0x1f; + Rn = (opCode >> 5) & 0x1f; + Opc = (opCode >> 15) & 0x3; + Size = (opCode >> 22) & 0x3; + + RegisterSize = ((opCode >> 30) & 1) != 0 + ? RegisterSize.Simd128 + : RegisterSize.Simd64; + } + } +} \ No newline at end of file diff --git a/Decoders/OpCodeSimdCvt64.cs b/Decoders/OpCodeSimdCvt64.cs new file mode 100644 index 0000000..6c68a3a --- /dev/null +++ b/Decoders/OpCodeSimdCvt64.cs @@ -0,0 +1,31 @@ +using ChocolArm64.Instructions; +using ChocolArm64.State; + +namespace ChocolArm64.Decoders +{ + class OpCodeSimdCvt64 : OpCodeSimd64 + { + public int FBits { get; private set; } + + public OpCodeSimdCvt64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + //TODO: + //Und of Fixed Point variants. + int scale = (opCode >> 10) & 0x3f; + int sf = (opCode >> 31) & 0x1; + + /*if (Type != SF && !(Type == 2 && SF == 1)) + { + Emitter = AInstEmit.Und; + + return; + }*/ + + FBits = 64 - scale; + + RegisterSize = sf != 0 + ? State.RegisterSize.Int64 + : State.RegisterSize.Int32; + } + } +} \ No newline at end of file diff --git a/Decoders/OpCodeSimdExt64.cs b/Decoders/OpCodeSimdExt64.cs new file mode 100644 index 0000000..1c57f19 --- /dev/null +++ b/Decoders/OpCodeSimdExt64.cs @@ -0,0 +1,14 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeSimdExt64 : OpCodeSimdReg64 + { + public int Imm4 { get; private set; } + + public OpCodeSimdExt64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + Imm4 = (opCode >> 11) & 0xf; + } + } +} \ No newline at end of file diff --git a/Decoders/OpCodeSimdFcond64.cs b/Decoders/OpCodeSimdFcond64.cs new file mode 100644 index 0000000..b0f1c0e --- /dev/null +++ b/Decoders/OpCodeSimdFcond64.cs @@ -0,0 +1,17 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeSimdFcond64 : OpCodeSimdReg64, IOpCodeCond64 + { + public int Nzcv { get; private set; } + + public Cond Cond { get; private set; } + + public OpCodeSimdFcond64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + Nzcv = (opCode >> 0) & 0xf; + Cond = (Cond)((opCode >> 12) & 0xf); + } + } +} \ No newline at end of file diff --git a/Decoders/OpCodeSimdFmov64.cs b/Decoders/OpCodeSimdFmov64.cs new file mode 100644 index 0000000..6752e18 --- /dev/null +++ b/Decoders/OpCodeSimdFmov64.cs @@ -0,0 +1,33 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeSimdFmov64 : OpCode64, IOpCodeSimd64 + { + public int Rd { get; private set; } + public long Imm { get; private set; } + public int Size { get; private set; } + + public OpCodeSimdFmov64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + int imm5 = (opCode >> 5) & 0x1f; + int type = (opCode >> 22) & 0x3; + + if (imm5 != 0b00000 || type > 1) + { + Emitter = InstEmit.Und; + + return; + } + + Size = type; + + long imm; + + Rd = (opCode >> 0) & 0x1f; + imm = (opCode >> 13) & 0xff; + + Imm = DecoderHelper.DecodeImm8Float(imm, type); + } + } +} \ No newline at end of file diff --git a/Decoders/OpCodeSimdImm64.cs b/Decoders/OpCodeSimdImm64.cs new file mode 100644 index 0000000..3ef6a8c --- /dev/null +++ b/Decoders/OpCodeSimdImm64.cs @@ -0,0 +1,101 @@ +using ChocolArm64.Instructions; +using ChocolArm64.State; + +namespace ChocolArm64.Decoders +{ + class OpCodeSimdImm64 : OpCode64, IOpCodeSimd64 + { + public int Rd { get; private set; } + public long Imm { get; private set; } + public int Size { get; private set; } + + public OpCodeSimdImm64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + Rd = opCode & 0x1f; + + int cMode = (opCode >> 12) & 0xf; + int op = (opCode >> 29) & 0x1; + + int modeLow = cMode & 1; + int modeHigh = cMode >> 1; + + long imm; + + imm = ((uint)opCode >> 5) & 0x1f; + imm |= ((uint)opCode >> 11) & 0xe0; + + if (modeHigh == 0b111) + { + Size = modeLow != 0 ? op : 3; + + switch (op | (modeLow << 1)) + { + case 0: + //64-bits Immediate. + //Transform abcd efgh into abcd efgh abcd efgh ... + imm = (long)((ulong)imm * 0x0101010101010101); + break; + + case 1: + //64-bits Immediate. + //Transform abcd efgh into aaaa aaaa bbbb bbbb ... + imm = (imm & 0xf0) >> 4 | (imm & 0x0f) << 4; + imm = (imm & 0xcc) >> 2 | (imm & 0x33) << 2; + imm = (imm & 0xaa) >> 1 | (imm & 0x55) << 1; + + imm = (long)((ulong)imm * 0x8040201008040201); + imm = (long)((ulong)imm & 0x8080808080808080); + + imm |= imm >> 4; + imm |= imm >> 2; + imm |= imm >> 1; + break; + + case 2: + case 3: + //Floating point Immediate. + imm = DecoderHelper.DecodeImm8Float(imm, Size); + break; + } + } + else if ((modeHigh & 0b110) == 0b100) + { + //16-bits shifted Immediate. + Size = 1; imm <<= (modeHigh & 1) << 3; + } + else if ((modeHigh & 0b100) == 0b000) + { + //32-bits shifted Immediate. + Size = 2; imm <<= modeHigh << 3; + } + else if ((modeHigh & 0b111) == 0b110) + { + //32-bits shifted Immediate (fill with ones). + Size = 2; imm = ShlOnes(imm, 8 << modeLow); + } + else + { + //8 bits without shift. + Size = 0; + } + + Imm = imm; + + RegisterSize = ((opCode >> 30) & 1) != 0 + ? State.RegisterSize.Simd128 + : State.RegisterSize.Simd64; + } + + private static long ShlOnes(long value, int shift) + { + if (shift != 0) + { + return value << shift | (long)(ulong.MaxValue >> (64 - shift)); + } + else + { + return value; + } + } + } +} \ No newline at end of file diff --git a/Decoders/OpCodeSimdIns64.cs b/Decoders/OpCodeSimdIns64.cs new file mode 100644 index 0000000..3b25fae --- /dev/null +++ b/Decoders/OpCodeSimdIns64.cs @@ -0,0 +1,36 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeSimdIns64 : OpCodeSimd64 + { + public int SrcIndex { get; private set; } + public int DstIndex { get; private set; } + + public OpCodeSimdIns64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + int imm4 = (opCode >> 11) & 0xf; + int imm5 = (opCode >> 16) & 0x1f; + + if (imm5 == 0b10000) + { + Emitter = InstEmit.Und; + + return; + } + + Size = imm5 & -imm5; + + switch (Size) + { + case 1: Size = 0; break; + case 2: Size = 1; break; + case 4: Size = 2; break; + case 8: Size = 3; break; + } + + SrcIndex = imm4 >> Size; + DstIndex = imm5 >> (Size + 1); + } + } +} \ No newline at end of file diff --git a/Decoders/OpCodeSimdMemImm64.cs b/Decoders/OpCodeSimdMemImm64.cs new file mode 100644 index 0000000..9fbab56 --- /dev/null +++ b/Decoders/OpCodeSimdMemImm64.cs @@ -0,0 +1,19 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeSimdMemImm64 : OpCodeMemImm64, IOpCodeSimd64 + { + public OpCodeSimdMemImm64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + Size |= (opCode >> 21) & 4; + + if (!WBack && !Unscaled && Size >= 4) + { + Imm <<= 4; + } + + Extend64 = false; + } + } +} \ No newline at end of file diff --git a/Decoders/OpCodeSimdMemLit64.cs b/Decoders/OpCodeSimdMemLit64.cs new file mode 100644 index 0000000..c98ffd0 --- /dev/null +++ b/Decoders/OpCodeSimdMemLit64.cs @@ -0,0 +1,31 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeSimdMemLit64 : OpCode64, IOpCodeSimd64, IOpCodeLit64 + { + public int Rt { get; private set; } + public long Imm { get; private set; } + public int Size { get; private set; } + public bool Signed => false; + public bool Prefetch => false; + + public OpCodeSimdMemLit64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + int opc = (opCode >> 30) & 3; + + if (opc == 3) + { + Emitter = InstEmit.Und; + + return; + } + + Rt = opCode & 0x1f; + + Imm = position + DecoderHelper.DecodeImmS19_2(opCode); + + Size = opc + 2; + } + } +} \ No newline at end of file diff --git a/Decoder/AOpCodeSimdMemMs.cs b/Decoders/OpCodeSimdMemMs64.cs similarity index 55% rename from Decoder/AOpCodeSimdMemMs.cs rename to Decoders/OpCodeSimdMemMs64.cs index a54e236..0748ef4 100644 --- a/Decoder/AOpCodeSimdMemMs.cs +++ b/Decoders/OpCodeSimdMemMs64.cs @@ -1,18 +1,18 @@ -using ChocolArm64.Instruction; +using ChocolArm64.Instructions; using ChocolArm64.State; -namespace ChocolArm64.Decoder +namespace ChocolArm64.Decoders { - class AOpCodeSimdMemMs : AOpCodeMemReg, IAOpCodeSimd + class OpCodeSimdMemMs64 : OpCodeMemReg64, IOpCodeSimd64 { public int Reps { get; private set; } public int SElems { get; private set; } public int Elems { get; private set; } public bool WBack { get; private set; } - public AOpCodeSimdMemMs(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) + public OpCodeSimdMemMs64(Inst inst, long position, int opCode) : base(inst, position, opCode) { - switch ((OpCode >> 12) & 0xf) + switch ((opCode >> 12) & 0xf) { case 0b0000: Reps = 1; SElems = 4; break; case 0b0010: Reps = 4; SElems = 1; break; @@ -22,26 +22,26 @@ namespace ChocolArm64.Decoder case 0b1000: Reps = 1; SElems = 2; break; case 0b1010: Reps = 2; SElems = 1; break; - default: Inst = AInst.Undefined; return; + default: inst = Inst.Undefined; return; } - Size = (OpCode >> 10) & 3; - WBack = ((OpCode >> 23) & 1) != 0; + Size = (opCode >> 10) & 3; + WBack = ((opCode >> 23) & 1) != 0; - bool Q = ((OpCode >> 30) & 1) != 0; + bool q = ((opCode >> 30) & 1) != 0; - if (!Q && Size == 3 && SElems != 1) + if (!q && Size == 3 && SElems != 1) { - Inst = AInst.Undefined; + inst = Inst.Undefined; return; } Extend64 = false; - RegisterSize = Q - ? ARegisterSize.SIMD128 - : ARegisterSize.SIMD64; + RegisterSize = q + ? State.RegisterSize.Simd128 + : State.RegisterSize.Simd64; Elems = (GetBitsCount() >> 3) >> Size; } diff --git a/Decoders/OpCodeSimdMemPair64.cs b/Decoders/OpCodeSimdMemPair64.cs new file mode 100644 index 0000000..1b79674 --- /dev/null +++ b/Decoders/OpCodeSimdMemPair64.cs @@ -0,0 +1,16 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeSimdMemPair64 : OpCodeMemPair64, IOpCodeSimd64 + { + public OpCodeSimdMemPair64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + Size = ((opCode >> 30) & 3) + 2; + + Extend64 = false; + + DecodeImm(opCode); + } + } +} \ No newline at end of file diff --git a/Decoders/OpCodeSimdMemReg64.cs b/Decoders/OpCodeSimdMemReg64.cs new file mode 100644 index 0000000..4ccbbed --- /dev/null +++ b/Decoders/OpCodeSimdMemReg64.cs @@ -0,0 +1,14 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeSimdMemReg64 : OpCodeMemReg64, IOpCodeSimd64 + { + public OpCodeSimdMemReg64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + Size |= (opCode >> 21) & 4; + + Extend64 = false; + } + } +} \ No newline at end of file diff --git a/Decoders/OpCodeSimdMemSs64.cs b/Decoders/OpCodeSimdMemSs64.cs new file mode 100644 index 0000000..07ec8ab --- /dev/null +++ b/Decoders/OpCodeSimdMemSs64.cs @@ -0,0 +1,98 @@ +using ChocolArm64.Instructions; +using ChocolArm64.State; + +namespace ChocolArm64.Decoders +{ + class OpCodeSimdMemSs64 : OpCodeMemReg64, IOpCodeSimd64 + { + public int SElems { get; private set; } + public int Index { get; private set; } + public bool Replicate { get; private set; } + public bool WBack { get; private set; } + + public OpCodeSimdMemSs64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + int size = (opCode >> 10) & 3; + int s = (opCode >> 12) & 1; + int sElems = (opCode >> 12) & 2; + int scale = (opCode >> 14) & 3; + int l = (opCode >> 22) & 1; + int q = (opCode >> 30) & 1; + + sElems |= (opCode >> 21) & 1; + + sElems++; + + int index = (q << 3) | (s << 2) | size; + + switch (scale) + { + case 1: + { + if ((size & 1) != 0) + { + inst = Inst.Undefined; + + return; + } + + index >>= 1; + + break; + } + + case 2: + { + if ((size & 2) != 0 || + ((size & 1) != 0 && s != 0)) + { + inst = Inst.Undefined; + + return; + } + + if ((size & 1) != 0) + { + index >>= 3; + + scale = 3; + } + else + { + index >>= 2; + } + + break; + } + + case 3: + { + if (l == 0 || s != 0) + { + inst = Inst.Undefined; + + return; + } + + scale = size; + + Replicate = true; + + break; + } + } + + Index = index; + SElems = sElems; + Size = scale; + + Extend64 = false; + + WBack = ((opCode >> 23) & 1) != 0; + + RegisterSize = q != 0 + ? State.RegisterSize.Simd128 + : State.RegisterSize.Simd64; + } + } +} \ No newline at end of file diff --git a/Decoders/OpCodeSimdReg64.cs b/Decoders/OpCodeSimdReg64.cs new file mode 100644 index 0000000..4bf462d --- /dev/null +++ b/Decoders/OpCodeSimdReg64.cs @@ -0,0 +1,18 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeSimdReg64 : OpCodeSimd64 + { + public bool Bit3 { get; private set; } + public int Ra { get; private set; } + public int Rm { get; protected set; } + + public OpCodeSimdReg64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + Bit3 = ((opCode >> 3) & 0x1) != 0; + Ra = (opCode >> 10) & 0x1f; + Rm = (opCode >> 16) & 0x1f; + } + } +} \ No newline at end of file diff --git a/Decoders/OpCodeSimdRegElem64.cs b/Decoders/OpCodeSimdRegElem64.cs new file mode 100644 index 0000000..04e9586 --- /dev/null +++ b/Decoders/OpCodeSimdRegElem64.cs @@ -0,0 +1,31 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeSimdRegElem64 : OpCodeSimdReg64 + { + public int Index { get; private set; } + + public OpCodeSimdRegElem64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + switch (Size) + { + case 1: + Index = (opCode >> 20) & 3 | + (opCode >> 9) & 4; + + Rm &= 0xf; + + break; + + case 2: + Index = (opCode >> 21) & 1 | + (opCode >> 10) & 2; + + break; + + default: Emitter = InstEmit.Und; return; + } + } + } +} \ No newline at end of file diff --git a/Decoders/OpCodeSimdRegElemF64.cs b/Decoders/OpCodeSimdRegElemF64.cs new file mode 100644 index 0000000..b5db734 --- /dev/null +++ b/Decoders/OpCodeSimdRegElemF64.cs @@ -0,0 +1,33 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeSimdRegElemF64 : OpCodeSimdReg64 + { + public int Index { get; private set; } + + public OpCodeSimdRegElemF64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + switch ((opCode >> 21) & 3) // sz:L + { + case 0: // H:0 + Index = (opCode >> 10) & 2; // 0, 2 + + break; + + case 1: // H:1 + Index = (opCode >> 10) & 2; + Index++; // 1, 3 + + break; + + case 2: // H + Index = (opCode >> 11) & 1; // 0, 1 + + break; + + default: Emitter = InstEmit.Und; return; + } + } + } +} diff --git a/Decoders/OpCodeSimdShImm64.cs b/Decoders/OpCodeSimdShImm64.cs new file mode 100644 index 0000000..d4cd88d --- /dev/null +++ b/Decoders/OpCodeSimdShImm64.cs @@ -0,0 +1,16 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeSimdShImm64 : OpCodeSimd64 + { + public int Imm { get; private set; } + + public OpCodeSimdShImm64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + Imm = (opCode >> 16) & 0x7f; + + Size = BitUtils.HighestBitSetNibble(Imm >> 3); + } + } +} diff --git a/Decoders/OpCodeSimdTbl64.cs b/Decoders/OpCodeSimdTbl64.cs new file mode 100644 index 0000000..13cc909 --- /dev/null +++ b/Decoders/OpCodeSimdTbl64.cs @@ -0,0 +1,12 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeSimdTbl64 : OpCodeSimdReg64 + { + public OpCodeSimdTbl64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + Size = ((opCode >> 13) & 3) + 1; + } + } +} \ No newline at end of file diff --git a/Decoders/OpCodeSystem64.cs b/Decoders/OpCodeSystem64.cs new file mode 100644 index 0000000..41c5156 --- /dev/null +++ b/Decoders/OpCodeSystem64.cs @@ -0,0 +1,24 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeSystem64 : OpCode64 + { + public int Rt { get; private set; } + public int Op2 { get; private set; } + public int CRm { get; private set; } + public int CRn { get; private set; } + public int Op1 { get; private set; } + public int Op0 { get; private set; } + + public OpCodeSystem64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + Rt = (opCode >> 0) & 0x1f; + Op2 = (opCode >> 5) & 0x7; + CRm = (opCode >> 8) & 0xf; + CRn = (opCode >> 12) & 0xf; + Op1 = (opCode >> 16) & 0x7; + Op0 = ((opCode >> 19) & 0x1) | 2; + } + } +} \ No newline at end of file diff --git a/Decoder/AShiftType.cs b/Decoders/ShiftType.cs similarity index 56% rename from Decoder/AShiftType.cs rename to Decoders/ShiftType.cs index 34ceea2..5f6a7a4 100644 --- a/Decoder/AShiftType.cs +++ b/Decoders/ShiftType.cs @@ -1,6 +1,6 @@ -namespace ChocolArm64.Decoder +namespace ChocolArm64.Decoders { - enum AShiftType + enum ShiftType { Lsl, Lsr, diff --git a/Decoders32/A32OpCode.cs b/Decoders32/A32OpCode.cs new file mode 100644 index 0000000..f0177a4 --- /dev/null +++ b/Decoders32/A32OpCode.cs @@ -0,0 +1,15 @@ +using ChocolArm64.Decoders; +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders32 +{ + class A32OpCode : OpCode64 + { + public Cond Cond { get; private set; } + + public A32OpCode(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + Cond = (Cond)((uint)opCode >> 28); + } + } +} \ No newline at end of file diff --git a/Decoders32/A32OpCodeBImmAl.cs b/Decoders32/A32OpCodeBImmAl.cs new file mode 100644 index 0000000..c4a196b --- /dev/null +++ b/Decoders32/A32OpCodeBImmAl.cs @@ -0,0 +1,16 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders32 +{ + class A32OpCodeBImmAl : A32OpCode + { + public int Imm; + public int H; + + public A32OpCodeBImmAl(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + Imm = (opCode << 8) >> 6; + H = (opCode >> 23) & 2; + } + } +} \ No newline at end of file diff --git a/Events/ACpuTraceEventArgs.cs b/Events/ACpuTraceEventArgs.cs deleted file mode 100644 index 0284f4e..0000000 --- a/Events/ACpuTraceEventArgs.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; - -namespace ChocolArm64.Events -{ - public class ACpuTraceEventArgs : EventArgs - { - public long Position { get; private set; } - - public ACpuTraceEventArgs(long Position) - { - this.Position = Position; - } - } -} \ No newline at end of file diff --git a/Events/AInstExceptionEventArgs.cs b/Events/AInstExceptionEventArgs.cs deleted file mode 100644 index a6853ea..0000000 --- a/Events/AInstExceptionEventArgs.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; - -namespace ChocolArm64.Events -{ - public class AInstExceptionEventArgs : EventArgs - { - public long Position { get; private set; } - public int Id { get; private set; } - - public AInstExceptionEventArgs(long Position, int Id) - { - this.Position = Position; - this.Id = Id; - } - } -} \ No newline at end of file diff --git a/Events/AInstUndefinedEventArgs.cs b/Events/AInstUndefinedEventArgs.cs deleted file mode 100644 index cdc1728..0000000 --- a/Events/AInstUndefinedEventArgs.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; - -namespace ChocolArm64.Events -{ - public class AInstUndefinedEventArgs : EventArgs - { - public long Position { get; private set; } - public int RawOpCode { get; private set; } - - public AInstUndefinedEventArgs(long Position, int RawOpCode) - { - this.Position = Position; - this.RawOpCode = RawOpCode; - } - } -} \ No newline at end of file diff --git a/Events/AInvalidAccessEventArgs.cs b/Events/AInvalidAccessEventArgs.cs deleted file mode 100644 index a5c472a..0000000 --- a/Events/AInvalidAccessEventArgs.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; - -namespace ChocolArm64.Events -{ - public class AInvalidAccessEventArgs : EventArgs - { - public long Position { get; private set; } - - public AInvalidAccessEventArgs(long Position) - { - this.Position = Position; - } - } -} \ No newline at end of file diff --git a/Events/CpuTraceEventArgs.cs b/Events/CpuTraceEventArgs.cs new file mode 100644 index 0000000..c12781e --- /dev/null +++ b/Events/CpuTraceEventArgs.cs @@ -0,0 +1,14 @@ +using System; + +namespace ChocolArm64.Events +{ + public class CpuTraceEventArgs : EventArgs + { + public long Position { get; private set; } + + public CpuTraceEventArgs(long position) + { + Position = position; + } + } +} \ No newline at end of file diff --git a/Events/InstExceptionEventArgs.cs b/Events/InstExceptionEventArgs.cs new file mode 100644 index 0000000..e3cc0ba --- /dev/null +++ b/Events/InstExceptionEventArgs.cs @@ -0,0 +1,16 @@ +using System; + +namespace ChocolArm64.Events +{ + public class InstExceptionEventArgs : EventArgs + { + public long Position { get; private set; } + public int Id { get; private set; } + + public InstExceptionEventArgs(long position, int id) + { + Position = position; + Id = id; + } + } +} \ No newline at end of file diff --git a/Events/InstUndefinedEventArgs.cs b/Events/InstUndefinedEventArgs.cs new file mode 100644 index 0000000..3ad7ea8 --- /dev/null +++ b/Events/InstUndefinedEventArgs.cs @@ -0,0 +1,16 @@ +using System; + +namespace ChocolArm64.Events +{ + public class InstUndefinedEventArgs : EventArgs + { + public long Position { get; private set; } + public int RawOpCode { get; private set; } + + public InstUndefinedEventArgs(long position, int rawOpCode) + { + Position = position; + RawOpCode = rawOpCode; + } + } +} \ No newline at end of file diff --git a/Events/InvalidAccessEventArgs.cs b/Events/InvalidAccessEventArgs.cs new file mode 100644 index 0000000..a8046d7 --- /dev/null +++ b/Events/InvalidAccessEventArgs.cs @@ -0,0 +1,14 @@ +using System; + +namespace ChocolArm64.Events +{ + public class InvalidAccessEventArgs : EventArgs + { + public long Position { get; private set; } + + public InvalidAccessEventArgs(long position) + { + Position = position; + } + } +} \ No newline at end of file diff --git a/Exceptions/VmmOutOfMemoryException.cs b/Exceptions/VmmOutOfMemoryException.cs index 4a03b65..d6ddf75 100644 --- a/Exceptions/VmmOutOfMemoryException.cs +++ b/Exceptions/VmmOutOfMemoryException.cs @@ -8,6 +8,6 @@ namespace ChocolArm64.Exceptions public VmmAccessException() { } - public VmmAccessException(long Position, long Size) : base(string.Format(ExMsg, Position, Size)) { } + public VmmAccessException(long position, long size) : base(string.Format(ExMsg, position, size)) { } } } \ No newline at end of file diff --git a/Exceptions/VmmPageFaultException.cs b/Exceptions/VmmPageFaultException.cs index d55c2c1..f33aafc 100644 --- a/Exceptions/VmmPageFaultException.cs +++ b/Exceptions/VmmPageFaultException.cs @@ -8,6 +8,6 @@ namespace ChocolArm64.Exceptions public VmmPageFaultException() { } - public VmmPageFaultException(long Position) : base(string.Format(ExMsg, Position)) { } + public VmmPageFaultException(long position) : base(string.Format(ExMsg, position)) { } } } \ No newline at end of file diff --git a/Instruction/AInst.cs b/Instruction/AInst.cs deleted file mode 100644 index 7409353..0000000 --- a/Instruction/AInst.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; - -namespace ChocolArm64.Instruction -{ - struct AInst - { - public AInstInterpreter Interpreter { get; private set; } - public AInstEmitter Emitter { get; private set; } - public Type Type { get; private set; } - - public static AInst Undefined => new AInst(null, AInstEmit.Und, null); - - public AInst(AInstInterpreter Interpreter, AInstEmitter Emitter, Type Type) - { - this.Interpreter = Interpreter; - this.Emitter = Emitter; - this.Type = Type; - } - } -} \ No newline at end of file diff --git a/Instruction/AInstEmitAlu.cs b/Instruction/AInstEmitAlu.cs deleted file mode 100644 index 4551346..0000000 --- a/Instruction/AInstEmitAlu.cs +++ /dev/null @@ -1,402 +0,0 @@ -using ChocolArm64.Decoder; -using ChocolArm64.State; -using ChocolArm64.Translation; -using System; -using System.Reflection; -using System.Reflection.Emit; -using System.Runtime.Intrinsics.X86; - -using static ChocolArm64.Instruction.AInstEmitAluHelper; - -namespace ChocolArm64.Instruction -{ - static partial class AInstEmit - { - public static void Adc(AILEmitterCtx Context) => EmitAdc(Context, false); - public static void Adcs(AILEmitterCtx Context) => EmitAdc(Context, true); - - private static void EmitAdc(AILEmitterCtx Context, bool SetFlags) - { - 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_U8); - } - - Context.Emit(OpCodes.Add); - - if (SetFlags) - { - Context.EmitZNFlagCheck(); - - EmitAdcsCCheck(Context); - EmitAddsVCheck(Context); - } - - EmitDataStore(Context); - } - - public static void Add(AILEmitterCtx Context) => EmitDataOp(Context, OpCodes.Add); - - public static void Adds(AILEmitterCtx Context) - { - 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 Cls(AILEmitterCtx Context) - { - AOpCodeAlu Op = (AOpCodeAlu)Context.CurrOp; - - Context.EmitLdintzr(Op.Rn); - - Context.EmitLdc_I4(Op.RegisterSize == ARegisterSize.Int32 ? 32 : 64); - - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.CountLeadingSigns)); - - Context.EmitStintzr(Op.Rd); - } - - public static void Clz(AILEmitterCtx Context) - { - AOpCodeAlu Op = (AOpCodeAlu)Context.CurrOp; - - Context.EmitLdintzr(Op.Rn); - - if (Lzcnt.IsSupported) - { - Type TValue = Op.RegisterSize == ARegisterSize.Int32 ? typeof(uint) : typeof(ulong); - - Context.EmitCall(typeof(Lzcnt).GetMethod(nameof(Lzcnt.LeadingZeroCount), new Type[] { TValue })); - } - else - { - Context.EmitLdc_I4(Op.RegisterSize == ARegisterSize.Int32 ? 32 : 64); - - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.CountLeadingZeros)); - } - - 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) => EmitSbc(Context, false); - public static void Sbcs(AILEmitterCtx Context) => EmitSbc(Context, true); - - private static void EmitSbc(AILEmitterCtx Context, bool SetFlags) - { - 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_U8); - } - - Context.Emit(OpCodes.Sub); - - if (SetFlags) - { - Context.EmitZNFlagCheck(); - - EmitSbcsCCheck(Context); - EmitSubsVCheck(Context); - } - - 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)); - - private 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); - } - } -} diff --git a/Instruction/AInstEmitAluHelper.cs b/Instruction/AInstEmitAluHelper.cs deleted file mode 100644 index ef9dd7a..0000000 --- a/Instruction/AInstEmitAluHelper.cs +++ /dev/null @@ -1,212 +0,0 @@ -using ChocolArm64.Decoder; -using ChocolArm64.State; -using ChocolArm64.Translation; -using System.Reflection.Emit; - -namespace ChocolArm64.Instruction -{ - static class AInstEmitAluHelper - { - public static void EmitAdcsCCheck(AILEmitterCtx Context) - { - //C = (Rd == Rn && CIn) || Rd < Rn - Context.EmitSttmp(); - Context.EmitLdtmp(); - Context.EmitLdtmp(); - - EmitDataLoadRn(Context); - - Context.Emit(OpCodes.Ceq); - - Context.EmitLdflg((int)APState.CBit); - - Context.Emit(OpCodes.And); - - Context.EmitLdtmp(); - - EmitDataLoadRn(Context); - - Context.Emit(OpCodes.Clt_Un); - Context.Emit(OpCodes.Or); - - Context.EmitStflg((int)APState.CBit); - } - - 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 EmitSbcsCCheck(AILEmitterCtx Context) - { - //C = (Rn == Rm && CIn) || Rn > Rm - EmitDataLoadOpers(Context); - - Context.Emit(OpCodes.Ceq); - - Context.EmitLdflg((int)APState.CBit); - - Context.Emit(OpCodes.And); - - EmitDataLoadOpers(Context); - - Context.Emit(OpCodes.Cgt_Un); - Context.Emit(OpCodes.Or); - - Context.EmitStflg((int)APState.CBit); - } - - 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/Instruction/AInstEmitBfm.cs b/Instruction/AInstEmitBfm.cs deleted file mode 100644 index 2e8f250..0000000 --- a/Instruction/AInstEmitBfm.cs +++ /dev/null @@ -1,208 +0,0 @@ -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 - { - 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/Instruction/AInstEmitCcmp.cs b/Instruction/AInstEmitCcmp.cs deleted file mode 100644 index 7153a6a..0000000 --- a/Instruction/AInstEmitCcmp.cs +++ /dev/null @@ -1,81 +0,0 @@ -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/Instruction/AInstEmitCsel.cs b/Instruction/AInstEmitCsel.cs deleted file mode 100644 index 2187675..0000000 --- a/Instruction/AInstEmitCsel.cs +++ /dev/null @@ -1,58 +0,0 @@ -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.Emit(OpCodes.Br_S, LblEnd); - - Context.MarkLabel(LblTrue); - - Context.EmitLdintzr(Op.Rn); - - Context.MarkLabel(LblEnd); - - Context.EmitStintzr(Op.Rd); - } - } -} \ No newline at end of file diff --git a/Instruction/AInstEmitException.cs b/Instruction/AInstEmitException.cs deleted file mode 100644 index 73d2096..0000000 --- a/Instruction/AInstEmitException.cs +++ /dev/null @@ -1,86 +0,0 @@ -using ChocolArm64.Decoder; -using ChocolArm64.State; -using ChocolArm64.Translation; -using System.Reflection.Emit; - -namespace ChocolArm64.Instruction -{ - static partial class AInstEmit - { - 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_I8(Op.Position); - Context.EmitLdc_I4(Op.Id); - - Context.EmitPrivateCall(typeof(AThreadState), MthdName); - - //Check if the thread should still be running, if it isn't then we return 0 - //to force a return to the dispatcher and then exit the thread. - Context.EmitLdarg(ATranslatedSub.StateArgIdx); - - Context.EmitCallPropGet(typeof(AThreadState), nameof(AThreadState.Running)); - - AILLabel LblEnd = new AILLabel(); - - Context.Emit(OpCodes.Brtrue_S, LblEnd); - - Context.EmitLdc_I8(0); - - Context.Emit(OpCodes.Ret); - - Context.MarkLabel(LblEnd); - - if (Context.CurrBlock.Next != null) - { - Context.EmitLoadState(Context.CurrBlock.Next); - } - else - { - Context.EmitLdc_I8(Op.Position + 4); - - Context.Emit(OpCodes.Ret); - } - } - - 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); - - Context.EmitPrivateCall(typeof(AThreadState), nameof(AThreadState.OnUndefined)); - - if (Context.CurrBlock.Next != null) - { - Context.EmitLoadState(Context.CurrBlock.Next); - } - else - { - Context.EmitLdc_I8(Op.Position + 4); - - Context.Emit(OpCodes.Ret); - } - } - } -} \ No newline at end of file diff --git a/Instruction/AInstEmitFlow.cs b/Instruction/AInstEmitFlow.cs deleted file mode 100644 index 9126283..0000000 --- a/Instruction/AInstEmitFlow.cs +++ /dev/null @@ -1,189 +0,0 @@ -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; - - if (Context.CurrBlock.Branch != null) - { - Context.Emit(OpCodes.Br, Context.GetLabel(Op.Imm)); - } - else - { - Context.EmitStoreState(); - Context.EmitLdc_I8(Op.Imm); - - Context.Emit(OpCodes.Ret); - } - } - - public static void B_Cond(AILEmitterCtx Context) - { - AOpCodeBImmCond Op = (AOpCodeBImmCond)Context.CurrOp; - - EmitBranch(Context, 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); - - 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); - - EmitBranch(Context, ILOp); - } - - 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); - - EmitBranch(Context, ILOp); - } - - private static void EmitBranch(AILEmitterCtx Context, ACond Cond) - { - AOpCodeBImm Op = (AOpCodeBImm)Context.CurrOp; - - if (Context.CurrBlock.Next != null && - Context.CurrBlock.Branch != null) - { - Context.EmitCondBranch(Context.GetLabel(Op.Imm), Cond); - } - else - { - Context.EmitStoreState(); - - AILLabel LblTaken = new AILLabel(); - - Context.EmitCondBranch(LblTaken, Cond); - - Context.EmitLdc_I8(Op.Position + 4); - - Context.Emit(OpCodes.Ret); - - Context.MarkLabel(LblTaken); - - Context.EmitLdc_I8(Op.Imm); - - Context.Emit(OpCodes.Ret); - } - } - - private static void EmitBranch(AILEmitterCtx Context, OpCode ILOp) - { - AOpCodeBImm Op = (AOpCodeBImm)Context.CurrOp; - - if (Context.CurrBlock.Next != null && - Context.CurrBlock.Branch != null) - { - Context.Emit(ILOp, Context.GetLabel(Op.Imm)); - } - else - { - Context.EmitStoreState(); - - AILLabel LblTaken = new AILLabel(); - - Context.Emit(ILOp, LblTaken); - - Context.EmitLdc_I8(Op.Position + 4); - - Context.Emit(OpCodes.Ret); - - Context.MarkLabel(LblTaken); - - Context.EmitLdc_I8(Op.Imm); - - Context.Emit(OpCodes.Ret); - } - } - } -} \ No newline at end of file diff --git a/Instruction/AInstEmitHash.cs b/Instruction/AInstEmitHash.cs deleted file mode 100644 index 69bdbc4..0000000 --- a/Instruction/AInstEmitHash.cs +++ /dev/null @@ -1,115 +0,0 @@ -using ChocolArm64.Decoder; -using ChocolArm64.State; -using ChocolArm64.Translation; -using System; -using System.Reflection.Emit; -using System.Runtime.Intrinsics.X86; - -namespace ChocolArm64.Instruction -{ - static partial class AInstEmit - { - public static void Crc32b(AILEmitterCtx Context) - { - EmitCrc32(Context, nameof(ASoftFallback.Crc32b)); - } - - public static void Crc32h(AILEmitterCtx Context) - { - EmitCrc32(Context, nameof(ASoftFallback.Crc32h)); - } - - public static void Crc32w(AILEmitterCtx Context) - { - EmitCrc32(Context, nameof(ASoftFallback.Crc32w)); - } - - public static void Crc32x(AILEmitterCtx Context) - { - EmitCrc32(Context, nameof(ASoftFallback.Crc32x)); - } - - public static void Crc32cb(AILEmitterCtx Context) - { - if (AOptimizations.UseSse42) - { - EmitSse42Crc32(Context, typeof(uint), typeof(byte)); - } - else - { - EmitCrc32(Context, nameof(ASoftFallback.Crc32cb)); - } - } - - public static void Crc32ch(AILEmitterCtx Context) - { - if (AOptimizations.UseSse42) - { - EmitSse42Crc32(Context, typeof(uint), typeof(ushort)); - } - else - { - EmitCrc32(Context, nameof(ASoftFallback.Crc32ch)); - } - } - - public static void Crc32cw(AILEmitterCtx Context) - { - if (AOptimizations.UseSse42) - { - EmitSse42Crc32(Context, typeof(uint), typeof(uint)); - } - else - { - EmitCrc32(Context, nameof(ASoftFallback.Crc32cw)); - } - } - - public static void Crc32cx(AILEmitterCtx Context) - { - if (AOptimizations.UseSse42) - { - EmitSse42Crc32(Context, typeof(ulong), typeof(ulong)); - } - else - { - EmitCrc32(Context, nameof(ASoftFallback.Crc32cx)); - } - } - - private static void EmitSse42Crc32(AILEmitterCtx Context, Type TCrc, Type TData) - { - AOpCodeAluRs Op = (AOpCodeAluRs)Context.CurrOp; - - Context.EmitLdintzr(Op.Rn); - Context.EmitLdintzr(Op.Rm); - - Context.EmitCall(typeof(Sse42).GetMethod(nameof(Sse42.Crc32), new Type[] { TCrc, TData })); - - Context.EmitStintzr(Op.Rd); - } - - private static void EmitCrc32(AILEmitterCtx Context, string Name) - { - AOpCodeAluRs Op = (AOpCodeAluRs)Context.CurrOp; - - Context.EmitLdintzr(Op.Rn); - - if (Op.RegisterSize != ARegisterSize.Int32) - { - Context.Emit(OpCodes.Conv_U4); - } - - Context.EmitLdintzr(Op.Rm); - - ASoftFallback.EmitCall(Context, Name); - - if (Op.RegisterSize != ARegisterSize.Int32) - { - Context.Emit(OpCodes.Conv_U8); - } - - Context.EmitStintzr(Op.Rd); - } - } -} diff --git a/Instruction/AInstEmitMemory.cs b/Instruction/AInstEmitMemory.cs deleted file mode 100644 index 67653ed..0000000 --- a/Instruction/AInstEmitMemory.cs +++ /dev/null @@ -1,252 +0,0 @@ -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); - - private 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/Instruction/AInstEmitMemoryEx.cs b/Instruction/AInstEmitMemoryEx.cs deleted file mode 100644 index cf19b4a..0000000 --- a/Instruction/AInstEmitMemoryEx.cs +++ /dev/null @@ -1,192 +0,0 @@ -using ChocolArm64.Decoder; -using ChocolArm64.Memory; -using ChocolArm64.State; -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; - - bool Ordered = (AccType & AccessType.Ordered) != 0; - bool Exclusive = (AccType & AccessType.Exclusive) != 0; - - if (Ordered) - { - EmitBarrier(Context); - } - - if (Exclusive) - { - EmitMemoryCall(Context, nameof(AMemory.SetExclusive), Op.Rn); - } - - Context.EmitLdint(Op.Rn); - Context.EmitSttmp(); - - Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); - Context.EmitLdtmp(); - - EmitReadZxCall(Context, Op.Size); - - Context.EmitStintzr(Op.Rt); - - if (Pair) - { - Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); - Context.EmitLdtmp(); - Context.EmitLdc_I8(1 << Op.Size); - - Context.Emit(OpCodes.Add); - - EmitReadZxCall(Context, Op.Size); - - Context.EmitStintzr(Op.Rt2); - } - } - - 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; - - bool Ordered = (AccType & AccessType.Ordered) != 0; - bool Exclusive = (AccType & AccessType.Exclusive) != 0; - - if (Ordered) - { - EmitBarrier(Context); - } - - AILLabel LblEx = new AILLabel(); - AILLabel LblEnd = new AILLabel(); - - if (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_I8(1 << Op.Size); - - Context.Emit(OpCodes.Add); - - Context.EmitLdintzr(Op.Rt2); - - EmitWriteCall(Context, Op.Size); - } - - if (Exclusive) - { - Context.EmitLdc_I8(0); - Context.EmitStintzr(Op.Rs); - - EmitMemoryCall(Context, nameof(AMemory.ClearExclusiveForStore)); - } - - Context.MarkLabel(LblEnd); - } - - private static void EmitMemoryCall(AILEmitterCtx Context, string Name, int Rn = -1) - { - Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); - Context.EmitLdarg(ATranslatedSub.StateArgIdx); - - Context.EmitCallPropGet(typeof(AThreadState), nameof(AThreadState.Core)); - - 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/Instruction/AInstEmitMemoryHelper.cs b/Instruction/AInstEmitMemoryHelper.cs deleted file mode 100644 index b10551f..0000000 --- a/Instruction/AInstEmitMemoryHelper.cs +++ /dev/null @@ -1,138 +0,0 @@ -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/Instruction/AInstEmitMove.cs b/Instruction/AInstEmitMove.cs deleted file mode 100644 index 719b53d..0000000 --- a/Instruction/AInstEmitMove.cs +++ /dev/null @@ -1,41 +0,0 @@ -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/Instruction/AInstEmitMul.cs b/Instruction/AInstEmitMul.cs deleted file mode 100644 index 3713c81..0000000 --- a/Instruction/AInstEmitMul.cs +++ /dev/null @@ -1,80 +0,0 @@ -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/Instruction/AInstEmitSimdArithmetic.cs b/Instruction/AInstEmitSimdArithmetic.cs deleted file mode 100644 index 1bd4836..0000000 --- a/Instruction/AInstEmitSimdArithmetic.cs +++ /dev/null @@ -1,2387 +0,0 @@ -// https://github.com/intel/ARM_NEON_2_x86_SSE/blob/master/NEON_2_SSE.h - -using ChocolArm64.Decoder; -using ChocolArm64.State; -using ChocolArm64.Translation; -using System; -using System.Reflection; -using System.Reflection.Emit; -using System.Runtime.Intrinsics; -using System.Runtime.Intrinsics.X86; - -using static ChocolArm64.Instruction.AInstEmitSimdHelper; - -namespace ChocolArm64.Instruction -{ - static partial class AInstEmit - { - public static void Abs_S(AILEmitterCtx Context) - { - EmitScalarUnaryOpSx(Context, () => EmitAbs(Context)); - } - - public static void Abs_V(AILEmitterCtx Context) - { - EmitVectorUnaryOpSx(Context, () => EmitAbs(Context)); - } - - public static void Add_S(AILEmitterCtx Context) - { - EmitScalarBinaryOpZx(Context, () => Context.Emit(OpCodes.Add)); - } - - public static void Add_V(AILEmitterCtx Context) - { - if (AOptimizations.UseSse2) - { - EmitSse2Op(Context, nameof(Sse2.Add)); - } - else - { - EmitVectorBinaryOpZx(Context, () => Context.Emit(OpCodes.Add)); - } - } - - public static void Addhn_V(AILEmitterCtx Context) - { - EmitHighNarrow(Context, () => Context.Emit(OpCodes.Add), Round: false); - } - - 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) - { - EmitVectorPairwiseOpZx(Context, () => Context.Emit(OpCodes.Add)); - } - - public static void Addv_V(AILEmitterCtx Context) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - int Bytes = Op.GetBitsCount() >> 3; - int Elems = Bytes >> Op.Size; - - EmitVectorExtractZx(Context, Op.Rn, 0, Op.Size); - - for (int Index = 1; Index < Elems; Index++) - { - EmitVectorExtractZx(Context, Op.Rn, Index, Op.Size); - - Context.Emit(OpCodes.Add); - } - - EmitScalarSet(Context, Op.Rd, Op.Size); - } - - public static void Cls_V(AILEmitterCtx Context) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - int Bytes = Op.GetBitsCount() >> 3; - int Elems = Bytes >> Op.Size; - - int ESize = 8 << Op.Size; - - for (int Index = 0; Index < Elems; Index++) - { - EmitVectorExtractZx(Context, Op.Rn, Index, Op.Size); - - Context.EmitLdc_I4(ESize); - - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.CountLeadingSigns)); - - EmitVectorInsert(Context, Op.Rd, Index, Op.Size); - } - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - public static void Clz_V(AILEmitterCtx Context) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - int Bytes = Op.GetBitsCount() >> 3; - int Elems = Bytes >> Op.Size; - - int ESize = 8 << Op.Size; - - for (int Index = 0; Index < Elems; Index++) - { - EmitVectorExtractZx(Context, Op.Rn, Index, Op.Size); - - if (Lzcnt.IsSupported && ESize == 32) - { - Context.Emit(OpCodes.Conv_U4); - - Context.EmitCall(typeof(Lzcnt).GetMethod(nameof(Lzcnt.LeadingZeroCount), new Type[] { typeof(uint) })); - - Context.Emit(OpCodes.Conv_U8); - } - else - { - Context.EmitLdc_I4(ESize); - - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.CountLeadingZeros)); - } - - EmitVectorInsert(Context, Op.Rd, Index, Op.Size); - } - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - 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); - - if (Popcnt.IsSupported) - { - Context.EmitCall(typeof(Popcnt).GetMethod(nameof(Popcnt.PopCount), new Type[] { typeof(ulong) })); - } - else - { - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.CountSetBits8)); - } - - EmitVectorInsert(Context, Op.Rd, Index, 0); - } - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - public static void Fabd_S(AILEmitterCtx Context) - { - EmitScalarBinaryOpF(Context, () => - { - Context.Emit(OpCodes.Sub); - - EmitUnaryMathCall(Context, nameof(Math.Abs)); - }); - } - - public static void Fabs_S(AILEmitterCtx Context) - { - EmitScalarUnaryOpF(Context, () => - { - EmitUnaryMathCall(Context, nameof(Math.Abs)); - }); - } - - public static void Fabs_V(AILEmitterCtx Context) - { - EmitVectorUnaryOpF(Context, () => - { - EmitUnaryMathCall(Context, nameof(Math.Abs)); - }); - } - - public static void Fadd_S(AILEmitterCtx Context) - { - if (AOptimizations.FastFP && AOptimizations.UseSse - && AOptimizations.UseSse2) - { - EmitScalarSseOrSse2OpF(Context, nameof(Sse.AddScalar)); - } - else - { - EmitScalarBinaryOpF(Context, () => - { - EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPAdd)); - }); - } - } - - public static void Fadd_V(AILEmitterCtx Context) - { - if (AOptimizations.FastFP && AOptimizations.UseSse - && AOptimizations.UseSse2) - { - EmitVectorSseOrSse2OpF(Context, nameof(Sse.Add)); - } - else - { - EmitVectorBinaryOpF(Context, () => - { - EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPAdd)); - }); - } - } - - public static void Faddp_S(AILEmitterCtx Context) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - int SizeF = Op.Size & 1; - - EmitVectorExtractF(Context, Op.Rn, 0, SizeF); - EmitVectorExtractF(Context, Op.Rn, 1, SizeF); - - Context.Emit(OpCodes.Add); - - EmitScalarSetF(Context, Op.Rd, SizeF); - } - - public static void Faddp_V(AILEmitterCtx Context) - { - EmitVectorPairwiseOpF(Context, () => Context.Emit(OpCodes.Add)); - } - - public static void Fdiv_S(AILEmitterCtx Context) - { - if (AOptimizations.FastFP && AOptimizations.UseSse - && AOptimizations.UseSse2) - { - EmitScalarSseOrSse2OpF(Context, nameof(Sse.DivideScalar)); - } - else - { - EmitScalarBinaryOpF(Context, () => - { - EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPDiv)); - }); - } - } - - public static void Fdiv_V(AILEmitterCtx Context) - { - if (AOptimizations.FastFP && AOptimizations.UseSse - && AOptimizations.UseSse2) - { - EmitVectorSseOrSse2OpF(Context, nameof(Sse.Divide)); - } - else - { - EmitVectorBinaryOpF(Context, () => - { - EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPDiv)); - }); - } - } - - public static void Fmadd_S(AILEmitterCtx Context) - { - if (AOptimizations.FastFP && AOptimizations.UseSse2) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - if (Op.Size == 0) - { - Type[] TypesMulAdd = new Type[] { typeof(Vector128), typeof(Vector128) }; - - Context.EmitLdvec(Op.Ra); - Context.EmitLdvec(Op.Rn); - Context.EmitLdvec(Op.Rm); - - Context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.MultiplyScalar), TypesMulAdd)); - Context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.AddScalar), TypesMulAdd)); - - Context.EmitStvec(Op.Rd); - - EmitVectorZero32_128(Context, Op.Rd); - } - else /* if (Op.Size == 1) */ - { - Type[] TypesMulAdd = new Type[] { typeof(Vector128), typeof(Vector128) }; - - EmitLdvecWithCastToDouble(Context, Op.Ra); - EmitLdvecWithCastToDouble(Context, Op.Rn); - EmitLdvecWithCastToDouble(Context, Op.Rm); - - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.MultiplyScalar), TypesMulAdd)); - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.AddScalar), TypesMulAdd)); - - EmitStvecWithCastFromDouble(Context, Op.Rd); - - EmitVectorZeroUpper(Context, Op.Rd); - } - } - else - { - EmitScalarTernaryRaOpF(Context, () => - { - EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPMulAdd)); - }); - } - } - - public static void Fmax_S(AILEmitterCtx Context) - { - if (AOptimizations.FastFP && AOptimizations.UseSse - && AOptimizations.UseSse2) - { - EmitScalarSseOrSse2OpF(Context, nameof(Sse.MaxScalar)); - } - else - { - EmitScalarBinaryOpF(Context, () => - { - EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPMax)); - }); - } - } - - public static void Fmax_V(AILEmitterCtx Context) - { - if (AOptimizations.FastFP && AOptimizations.UseSse - && AOptimizations.UseSse2) - { - EmitVectorSseOrSse2OpF(Context, nameof(Sse.Max)); - } - else - { - EmitVectorBinaryOpF(Context, () => - { - EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPMax)); - }); - } - } - - public static void Fmaxnm_S(AILEmitterCtx Context) - { - EmitScalarBinaryOpF(Context, () => - { - EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPMaxNum)); - }); - } - - public static void Fmaxnm_V(AILEmitterCtx Context) - { - EmitVectorBinaryOpF(Context, () => - { - EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPMaxNum)); - }); - } - - public static void Fmaxp_V(AILEmitterCtx Context) - { - EmitVectorPairwiseOpF(Context, () => - { - EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPMax)); - }); - } - - public static void Fmin_S(AILEmitterCtx Context) - { - if (AOptimizations.FastFP && AOptimizations.UseSse - && AOptimizations.UseSse2) - { - EmitScalarSseOrSse2OpF(Context, nameof(Sse.MinScalar)); - } - else - { - EmitScalarBinaryOpF(Context, () => - { - EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPMin)); - }); - } - } - - public static void Fmin_V(AILEmitterCtx Context) - { - if (AOptimizations.FastFP && AOptimizations.UseSse - && AOptimizations.UseSse2) - { - EmitVectorSseOrSse2OpF(Context, nameof(Sse.Min)); - } - else - { - EmitVectorBinaryOpF(Context, () => - { - EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPMin)); - }); - } - } - - public static void Fminnm_S(AILEmitterCtx Context) - { - EmitScalarBinaryOpF(Context, () => - { - EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPMinNum)); - }); - } - - public static void Fminnm_V(AILEmitterCtx Context) - { - EmitVectorBinaryOpF(Context, () => - { - EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPMinNum)); - }); - } - - public static void Fminp_V(AILEmitterCtx Context) - { - EmitVectorPairwiseOpF(Context, () => - { - EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPMin)); - }); - } - - public static void Fmla_Se(AILEmitterCtx Context) - { - EmitScalarTernaryOpByElemF(Context, () => - { - Context.Emit(OpCodes.Mul); - Context.Emit(OpCodes.Add); - }); - } - - 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 Fmls_Se(AILEmitterCtx Context) - { - EmitScalarTernaryOpByElemF(Context, () => - { - Context.Emit(OpCodes.Mul); - Context.Emit(OpCodes.Sub); - }); - } - - public static void Fmls_V(AILEmitterCtx Context) - { - EmitVectorTernaryOpF(Context, () => - { - Context.Emit(OpCodes.Mul); - Context.Emit(OpCodes.Sub); - }); - } - - public static void Fmls_Ve(AILEmitterCtx Context) - { - EmitVectorTernaryOpByElemF(Context, () => - { - Context.Emit(OpCodes.Mul); - Context.Emit(OpCodes.Sub); - }); - } - - public static void Fmsub_S(AILEmitterCtx Context) - { - if (AOptimizations.FastFP && AOptimizations.UseSse2) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - if (Op.Size == 0) - { - Type[] TypesMulSub = new Type[] { typeof(Vector128), typeof(Vector128) }; - - Context.EmitLdvec(Op.Ra); - Context.EmitLdvec(Op.Rn); - Context.EmitLdvec(Op.Rm); - - Context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.MultiplyScalar), TypesMulSub)); - Context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.SubtractScalar), TypesMulSub)); - - Context.EmitStvec(Op.Rd); - - EmitVectorZero32_128(Context, Op.Rd); - } - else /* if (Op.Size == 1) */ - { - Type[] TypesMulSub = new Type[] { typeof(Vector128), typeof(Vector128) }; - - EmitLdvecWithCastToDouble(Context, Op.Ra); - EmitLdvecWithCastToDouble(Context, Op.Rn); - EmitLdvecWithCastToDouble(Context, Op.Rm); - - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.MultiplyScalar), TypesMulSub)); - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SubtractScalar), TypesMulSub)); - - EmitStvecWithCastFromDouble(Context, Op.Rd); - - EmitVectorZeroUpper(Context, Op.Rd); - } - } - else - { - EmitScalarTernaryRaOpF(Context, () => - { - EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPMulSub)); - }); - } - } - - public static void Fmul_S(AILEmitterCtx Context) - { - if (AOptimizations.FastFP && AOptimizations.UseSse - && AOptimizations.UseSse2) - { - EmitScalarSseOrSse2OpF(Context, nameof(Sse.MultiplyScalar)); - } - else - { - EmitScalarBinaryOpF(Context, () => - { - EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPMul)); - }); - } - } - - public static void Fmul_Se(AILEmitterCtx Context) - { - EmitScalarBinaryOpByElemF(Context, () => Context.Emit(OpCodes.Mul)); - } - - public static void Fmul_V(AILEmitterCtx Context) - { - if (AOptimizations.FastFP && AOptimizations.UseSse - && AOptimizations.UseSse2) - { - EmitVectorSseOrSse2OpF(Context, nameof(Sse.Multiply)); - } - else - { - EmitVectorBinaryOpF(Context, () => - { - EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPMul)); - }); - } - } - - public static void Fmul_Ve(AILEmitterCtx Context) - { - EmitVectorBinaryOpByElemF(Context, () => Context.Emit(OpCodes.Mul)); - } - - public static void Fmulx_S(AILEmitterCtx Context) - { - EmitScalarBinaryOpF(Context, () => - { - EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPMulX)); - }); - } - - public static void Fmulx_Se(AILEmitterCtx Context) - { - EmitScalarBinaryOpByElemF(Context, () => - { - EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPMulX)); - }); - } - - public static void Fmulx_V(AILEmitterCtx Context) - { - EmitVectorBinaryOpF(Context, () => - { - EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPMulX)); - }); - } - - public static void Fmulx_Ve(AILEmitterCtx Context) - { - EmitVectorBinaryOpByElemF(Context, () => - { - EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPMulX)); - }); - } - - public static void Fneg_S(AILEmitterCtx Context) - { - EmitScalarUnaryOpF(Context, () => Context.Emit(OpCodes.Neg)); - } - - public static void Fneg_V(AILEmitterCtx Context) - { - EmitVectorUnaryOpF(Context, () => Context.Emit(OpCodes.Neg)); - } - - public static void Fnmadd_S(AILEmitterCtx Context) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - int SizeF = Op.Size & 1; - - EmitVectorExtractF(Context, Op.Rn, 0, SizeF); - - Context.Emit(OpCodes.Neg); - - 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 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 Fnmul_S(AILEmitterCtx Context) - { - EmitScalarBinaryOpF(Context, () => - { - Context.Emit(OpCodes.Mul); - Context.Emit(OpCodes.Neg); - }); - } - - public static void Frecpe_S(AILEmitterCtx Context) - { - EmitScalarUnaryOpF(Context, () => - { - EmitUnarySoftFloatCall(Context, nameof(ASoftFloat.RecipEstimate)); - }); - } - - public static void Frecpe_V(AILEmitterCtx Context) - { - EmitVectorUnaryOpF(Context, () => - { - EmitUnarySoftFloatCall(Context, nameof(ASoftFloat.RecipEstimate)); - }); - } - - public static void Frecps_S(AILEmitterCtx Context) - { - if (AOptimizations.FastFP && AOptimizations.UseSse2) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - int SizeF = Op.Size & 1; - - if (SizeF == 0) - { - Type[] TypesSsv = new Type[] { typeof(float) }; - Type[] TypesMulSub = new Type[] { typeof(Vector128), typeof(Vector128) }; - - Context.EmitLdc_R4(2f); - Context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.SetScalarVector128), TypesSsv)); - - Context.EmitLdvec(Op.Rn); - Context.EmitLdvec(Op.Rm); - - Context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.MultiplyScalar), TypesMulSub)); - Context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.SubtractScalar), TypesMulSub)); - - Context.EmitStvec(Op.Rd); - - EmitVectorZero32_128(Context, Op.Rd); - } - else /* if (SizeF == 1) */ - { - Type[] TypesSsv = new Type[] { typeof(double) }; - Type[] TypesMulSub = new Type[] { typeof(Vector128), typeof(Vector128) }; - - Context.EmitLdc_R8(2d); - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetScalarVector128), TypesSsv)); - - EmitLdvecWithCastToDouble(Context, Op.Rn); - EmitLdvecWithCastToDouble(Context, Op.Rm); - - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.MultiplyScalar), TypesMulSub)); - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SubtractScalar), TypesMulSub)); - - EmitStvecWithCastFromDouble(Context, Op.Rd); - - EmitVectorZeroUpper(Context, Op.Rd); - } - } - else - { - EmitScalarBinaryOpF(Context, () => - { - EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPRecipStepFused)); - }); - } - } - - public static void Frecps_V(AILEmitterCtx Context) - { - if (AOptimizations.FastFP && AOptimizations.UseSse2) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - int SizeF = Op.Size & 1; - - if (SizeF == 0) - { - Type[] TypesSav = new Type[] { typeof(float) }; - Type[] TypesMulSub = new Type[] { typeof(Vector128), typeof(Vector128) }; - - Context.EmitLdc_R4(2f); - Context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.SetAllVector128), TypesSav)); - - Context.EmitLdvec(Op.Rn); - Context.EmitLdvec(Op.Rm); - - Context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.Multiply), TypesMulSub)); - Context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.Subtract), TypesMulSub)); - - Context.EmitStvec(Op.Rd); - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - else /* if (SizeF == 1) */ - { - Type[] TypesSav = new Type[] { typeof(double) }; - Type[] TypesMulSub = new Type[] { typeof(Vector128), typeof(Vector128) }; - - Context.EmitLdc_R8(2d); - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetAllVector128), TypesSav)); - - EmitLdvecWithCastToDouble(Context, Op.Rn); - EmitLdvecWithCastToDouble(Context, Op.Rm); - - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Multiply), TypesMulSub)); - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Subtract), TypesMulSub)); - - EmitStvecWithCastFromDouble(Context, Op.Rd); - } - } - else - { - EmitVectorBinaryOpF(Context, () => - { - EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPRecipStepFused)); - }); - } - } - - public static void Frecpx_S(AILEmitterCtx Context) - { - EmitScalarUnaryOpF(Context, () => - { - EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPRecpX)); - }); - } - - 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 Frinta_V(AILEmitterCtx Context) - { - EmitVectorUnaryOpF(Context, () => - { - EmitRoundMathCall(Context, MidpointRounding.AwayFromZero); - }); - } - - public static void Frinti_S(AILEmitterCtx Context) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - EmitScalarUnaryOpF(Context, () => - { - Context.EmitLdarg(ATranslatedSub.StateArgIdx); - - if (Op.Size == 0) - { - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.RoundF)); - } - else if (Op.Size == 1) - { - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.Round)); - } - else - { - throw new InvalidOperationException(); - } - }); - } - - public static void Frinti_V(AILEmitterCtx Context) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - int SizeF = Op.Size & 1; - - EmitVectorUnaryOpF(Context, () => - { - Context.EmitLdarg(ATranslatedSub.StateArgIdx); - - if (SizeF == 0) - { - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.RoundF)); - } - else if (SizeF == 1) - { - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.Round)); - } - else - { - throw new InvalidOperationException(); - } - }); - } - - public static void Frintm_S(AILEmitterCtx Context) - { - EmitScalarUnaryOpF(Context, () => - { - EmitUnaryMathCall(Context, nameof(Math.Floor)); - }); - } - - public static void Frintm_V(AILEmitterCtx Context) - { - EmitVectorUnaryOpF(Context, () => - { - EmitUnaryMathCall(Context, nameof(Math.Floor)); - }); - } - - public static void Frintn_S(AILEmitterCtx Context) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - EmitVectorExtractF(Context, Op.Rn, 0, Op.Size); - - EmitRoundMathCall(Context, MidpointRounding.ToEven); - - EmitScalarSetF(Context, Op.Rd, Op.Size); - } - - public static void Frintn_V(AILEmitterCtx Context) - { - EmitVectorUnaryOpF(Context, () => - { - EmitRoundMathCall(Context, MidpointRounding.ToEven); - }); - } - - public static void Frintp_S(AILEmitterCtx Context) - { - EmitScalarUnaryOpF(Context, () => - { - EmitUnaryMathCall(Context, nameof(Math.Ceiling)); - }); - } - - public static void Frintp_V(AILEmitterCtx Context) - { - EmitVectorUnaryOpF(Context, () => - { - EmitUnaryMathCall(Context, nameof(Math.Ceiling)); - }); - } - - public static void Frintx_S(AILEmitterCtx Context) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - EmitScalarUnaryOpF(Context, () => - { - Context.EmitLdarg(ATranslatedSub.StateArgIdx); - - if (Op.Size == 0) - { - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.RoundF)); - } - else if (Op.Size == 1) - { - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.Round)); - } - else - { - throw new InvalidOperationException(); - } - }); - } - - public static void Frintx_V(AILEmitterCtx Context) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - EmitVectorUnaryOpF(Context, () => - { - Context.EmitLdarg(ATranslatedSub.StateArgIdx); - - if (Op.Size == 0) - { - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.RoundF)); - } - else if (Op.Size == 1) - { - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.Round)); - } - else - { - throw new InvalidOperationException(); - } - }); - } - - public static void Frsqrte_S(AILEmitterCtx Context) - { - EmitScalarUnaryOpF(Context, () => - { - EmitUnarySoftFloatCall(Context, nameof(ASoftFloat.InvSqrtEstimate)); - }); - } - - public static void Frsqrte_V(AILEmitterCtx Context) - { - EmitVectorUnaryOpF(Context, () => - { - EmitUnarySoftFloatCall(Context, nameof(ASoftFloat.InvSqrtEstimate)); - }); - } - - public static void Frsqrts_S(AILEmitterCtx Context) - { - if (AOptimizations.FastFP && AOptimizations.UseSse2) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - int SizeF = Op.Size & 1; - - if (SizeF == 0) - { - Type[] TypesSsv = new Type[] { typeof(float) }; - Type[] TypesMulSub = new Type[] { typeof(Vector128), typeof(Vector128) }; - - Context.EmitLdc_R4(0.5f); - Context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.SetScalarVector128), TypesSsv)); - - Context.EmitLdc_R4(3f); - Context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.SetScalarVector128), TypesSsv)); - - Context.EmitLdvec(Op.Rn); - Context.EmitLdvec(Op.Rm); - - Context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.MultiplyScalar), TypesMulSub)); - Context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.SubtractScalar), TypesMulSub)); - Context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.MultiplyScalar), TypesMulSub)); - - Context.EmitStvec(Op.Rd); - - EmitVectorZero32_128(Context, Op.Rd); - } - else /* if (SizeF == 1) */ - { - Type[] TypesSsv = new Type[] { typeof(double) }; - Type[] TypesMulSub = new Type[] { typeof(Vector128), typeof(Vector128) }; - - Context.EmitLdc_R8(0.5d); - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetScalarVector128), TypesSsv)); - - Context.EmitLdc_R8(3d); - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetScalarVector128), TypesSsv)); - - EmitLdvecWithCastToDouble(Context, Op.Rn); - EmitLdvecWithCastToDouble(Context, Op.Rm); - - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.MultiplyScalar), TypesMulSub)); - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SubtractScalar), TypesMulSub)); - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.MultiplyScalar), TypesMulSub)); - - EmitStvecWithCastFromDouble(Context, Op.Rd); - - EmitVectorZeroUpper(Context, Op.Rd); - } - } - else - { - EmitScalarBinaryOpF(Context, () => - { - EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPRSqrtStepFused)); - }); - } - } - - public static void Frsqrts_V(AILEmitterCtx Context) - { - if (AOptimizations.FastFP && AOptimizations.UseSse2) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - int SizeF = Op.Size & 1; - - if (SizeF == 0) - { - Type[] TypesSav = new Type[] { typeof(float) }; - Type[] TypesMulSub = new Type[] { typeof(Vector128), typeof(Vector128) }; - - Context.EmitLdc_R4(0.5f); - Context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.SetAllVector128), TypesSav)); - - Context.EmitLdc_R4(3f); - Context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.SetAllVector128), TypesSav)); - - Context.EmitLdvec(Op.Rn); - Context.EmitLdvec(Op.Rm); - - Context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.Multiply), TypesMulSub)); - Context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.Subtract), TypesMulSub)); - Context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.Multiply), TypesMulSub)); - - Context.EmitStvec(Op.Rd); - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - else /* if (SizeF == 1) */ - { - Type[] TypesSav = new Type[] { typeof(double) }; - Type[] TypesMulSub = new Type[] { typeof(Vector128), typeof(Vector128) }; - - Context.EmitLdc_R8(0.5d); - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetAllVector128), TypesSav)); - - Context.EmitLdc_R8(3d); - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetAllVector128), TypesSav)); - - EmitLdvecWithCastToDouble(Context, Op.Rn); - EmitLdvecWithCastToDouble(Context, Op.Rm); - - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Multiply), TypesMulSub)); - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Subtract), TypesMulSub)); - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Multiply), TypesMulSub)); - - EmitStvecWithCastFromDouble(Context, Op.Rd); - } - } - else - { - EmitVectorBinaryOpF(Context, () => - { - EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPRSqrtStepFused)); - }); - } - } - - public static void Fsqrt_S(AILEmitterCtx Context) - { - if (AOptimizations.FastFP && AOptimizations.UseSse - && AOptimizations.UseSse2) - { - EmitScalarSseOrSse2OpF(Context, nameof(Sse.SqrtScalar)); - } - else - { - EmitScalarUnaryOpF(Context, () => - { - EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPSqrt)); - }); - } - } - - public static void Fsqrt_V(AILEmitterCtx Context) - { - if (AOptimizations.FastFP && AOptimizations.UseSse - && AOptimizations.UseSse2) - { - EmitVectorSseOrSse2OpF(Context, nameof(Sse.Sqrt)); - } - else - { - EmitVectorUnaryOpF(Context, () => - { - EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPSqrt)); - }); - } - } - - public static void Fsub_S(AILEmitterCtx Context) - { - if (AOptimizations.FastFP && AOptimizations.UseSse - && AOptimizations.UseSse2) - { - EmitScalarSseOrSse2OpF(Context, nameof(Sse.SubtractScalar)); - } - else - { - EmitScalarBinaryOpF(Context, () => - { - EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPSub)); - }); - } - } - - public static void Fsub_V(AILEmitterCtx Context) - { - if (AOptimizations.FastFP && AOptimizations.UseSse - && AOptimizations.UseSse2) - { - EmitVectorSseOrSse2OpF(Context, nameof(Sse.Subtract)); - } - else - { - EmitVectorBinaryOpF(Context, () => - { - EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPSub)); - }); - } - } - - public static void Mla_V(AILEmitterCtx Context) - { - EmitVectorTernaryOpZx(Context, () => - { - Context.Emit(OpCodes.Mul); - Context.Emit(OpCodes.Add); - }); - } - - public static void Mla_Ve(AILEmitterCtx Context) - { - EmitVectorTernaryOpByElemZx(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 Mls_Ve(AILEmitterCtx Context) - { - EmitVectorTernaryOpByElemZx(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 Mul_Ve(AILEmitterCtx Context) - { - EmitVectorBinaryOpByElemZx(Context, () => Context.Emit(OpCodes.Mul)); - } - - public static void Neg_S(AILEmitterCtx Context) - { - EmitScalarUnaryOpSx(Context, () => Context.Emit(OpCodes.Neg)); - } - - public static void Neg_V(AILEmitterCtx Context) - { - EmitVectorUnaryOpSx(Context, () => Context.Emit(OpCodes.Neg)); - } - - public static void Raddhn_V(AILEmitterCtx Context) - { - EmitHighNarrow(Context, () => Context.Emit(OpCodes.Add), Round: true); - } - - public static void Rsubhn_V(AILEmitterCtx Context) - { - EmitHighNarrow(Context, () => Context.Emit(OpCodes.Sub), Round: true); - } - - public static void Saba_V(AILEmitterCtx Context) - { - EmitVectorTernaryOpSx(Context, () => - { - Context.Emit(OpCodes.Sub); - EmitAbs(Context); - - Context.Emit(OpCodes.Add); - }); - } - - public static void Sabal_V(AILEmitterCtx Context) - { - EmitVectorWidenRnRmTernaryOpSx(Context, () => - { - Context.Emit(OpCodes.Sub); - EmitAbs(Context); - - Context.Emit(OpCodes.Add); - }); - } - - public static void Sabd_V(AILEmitterCtx Context) - { - EmitVectorBinaryOpSx(Context, () => - { - Context.Emit(OpCodes.Sub); - EmitAbs(Context); - }); - } - - public static void Sabdl_V(AILEmitterCtx Context) - { - EmitVectorWidenRnRmBinaryOpSx(Context, () => - { - Context.Emit(OpCodes.Sub); - EmitAbs(Context); - }); - } - - public static void Sadalp_V(AILEmitterCtx Context) - { - EmitAddLongPairwise(Context, Signed: true, Accumulate: true); - } - - public static void Saddl_V(AILEmitterCtx Context) - { - if (AOptimizations.UseSse41) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - Type[] TypesSrl = new Type[] { VectorIntTypesPerSizeLog2[Op.Size], typeof(byte) }; - Type[] TypesCvt = new Type[] { VectorIntTypesPerSizeLog2[Op.Size] }; - Type[] TypesAdd = new Type[] { VectorIntTypesPerSizeLog2[Op.Size + 1], - VectorIntTypesPerSizeLog2[Op.Size + 1] }; - - string[] NamesCvt = new string[] { nameof(Sse41.ConvertToVector128Int16), - nameof(Sse41.ConvertToVector128Int32), - nameof(Sse41.ConvertToVector128Int64) }; - - int NumBytes = Op.RegisterSize == ARegisterSize.SIMD128 ? 8 : 0; - - EmitLdvecWithSignedCast(Context, Op.Rn, Op.Size); - - Context.EmitLdc_I4(NumBytes); - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), TypesSrl)); - - Context.EmitCall(typeof(Sse41).GetMethod(NamesCvt[Op.Size], TypesCvt)); - - EmitLdvecWithUnsignedCast(Context, Op.Rm, Op.Size); - - Context.EmitLdc_I4(NumBytes); - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), TypesSrl)); - - Context.EmitCall(typeof(Sse41).GetMethod(NamesCvt[Op.Size], TypesCvt)); - - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), TypesAdd)); - - EmitStvecWithSignedCast(Context, Op.Rd, Op.Size + 1); - } - else - { - EmitVectorWidenRnRmBinaryOpSx(Context, () => Context.Emit(OpCodes.Add)); - } - } - - public static void Saddlp_V(AILEmitterCtx Context) - { - EmitAddLongPairwise(Context, Signed: true, Accumulate: false); - } - - public static void Saddw_V(AILEmitterCtx Context) - { - EmitVectorWidenRmBinaryOpSx(Context, () => Context.Emit(OpCodes.Add)); - } - - public static void Shadd_V(AILEmitterCtx Context) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - if (AOptimizations.UseSse2 && Op.Size > 0) - { - Type[] TypesSra = new Type[] { VectorIntTypesPerSizeLog2[Op.Size], typeof(byte) }; - Type[] TypesAndXorAdd = new Type[] { VectorIntTypesPerSizeLog2[Op.Size], VectorIntTypesPerSizeLog2[Op.Size] }; - - EmitLdvecWithSignedCast(Context, Op.Rn, Op.Size); - - Context.Emit(OpCodes.Dup); - Context.EmitStvectmp(); - - EmitLdvecWithSignedCast(Context, Op.Rm, Op.Size); - - Context.Emit(OpCodes.Dup); - Context.EmitStvectmp2(); - - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.And), TypesAndXorAdd)); - - Context.EmitLdvectmp(); - Context.EmitLdvectmp2(); - - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Xor), TypesAndXorAdd)); - - Context.EmitLdc_I4(1); - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightArithmetic), TypesSra)); - - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), TypesAndXorAdd)); - - EmitStvecWithSignedCast(Context, Op.Rd, Op.Size); - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - else - { - EmitVectorBinaryOpSx(Context, () => - { - Context.Emit(OpCodes.Add); - - Context.Emit(OpCodes.Ldc_I4_1); - Context.Emit(OpCodes.Shr); - }); - } - } - - public static void Shsub_V(AILEmitterCtx Context) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - if (AOptimizations.UseSse2 && Op.Size < 2) - { - Type[] TypesSav = new Type[] { IntTypesPerSizeLog2[Op.Size] }; - Type[] TypesAddSub = new Type[] { VectorIntTypesPerSizeLog2 [Op.Size], VectorIntTypesPerSizeLog2 [Op.Size] }; - Type[] TypesAvg = new Type[] { VectorUIntTypesPerSizeLog2[Op.Size], VectorUIntTypesPerSizeLog2[Op.Size] }; - - Context.EmitLdc_I4(Op.Size == 0 ? sbyte.MinValue : short.MinValue); - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetAllVector128), TypesSav)); - - Context.EmitStvectmp(); - - EmitLdvecWithSignedCast(Context, Op.Rn, Op.Size); - Context.EmitLdvectmp(); - - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), TypesAddSub)); - - Context.Emit(OpCodes.Dup); - - EmitLdvecWithSignedCast(Context, Op.Rm, Op.Size); - Context.EmitLdvectmp(); - - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), TypesAddSub)); - - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Average), TypesAvg)); - - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Subtract), TypesAddSub)); - - EmitStvecWithSignedCast(Context, Op.Rd, Op.Size); - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - else - { - EmitVectorBinaryOpSx(Context, () => - { - Context.Emit(OpCodes.Sub); - - Context.Emit(OpCodes.Ldc_I4_1); - Context.Emit(OpCodes.Shr); - }); - } - } - - 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 Smaxp_V(AILEmitterCtx Context) - { - Type[] Types = new Type[] { typeof(long), typeof(long) }; - - MethodInfo MthdInfo = typeof(Math).GetMethod(nameof(Math.Max), Types); - - EmitVectorPairwiseOpSx(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 Sminp_V(AILEmitterCtx Context) - { - Type[] Types = new Type[] { typeof(long), typeof(long) }; - - MethodInfo MthdInfo = typeof(Math).GetMethod(nameof(Math.Min), Types); - - EmitVectorPairwiseOpSx(Context, () => Context.EmitCall(MthdInfo)); - } - - public static void Smlal_V(AILEmitterCtx Context) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - if (AOptimizations.UseSse41 && Op.Size < 2) - { - Type[] TypesSrl = new Type[] { VectorIntTypesPerSizeLog2[Op.Size], typeof(byte) }; - Type[] TypesCvt = new Type[] { VectorIntTypesPerSizeLog2[Op.Size] }; - Type[] TypesMulAdd = new Type[] { VectorIntTypesPerSizeLog2[Op.Size + 1], - VectorIntTypesPerSizeLog2[Op.Size + 1] }; - - Type TypeMul = Op.Size == 0 ? typeof(Sse2) : typeof(Sse41); - - string NameCvt = Op.Size == 0 - ? nameof(Sse41.ConvertToVector128Int16) - : nameof(Sse41.ConvertToVector128Int32); - - int NumBytes = Op.RegisterSize == ARegisterSize.SIMD128 ? 8 : 0; - - EmitLdvecWithSignedCast(Context, Op.Rd, Op.Size + 1); - - EmitLdvecWithSignedCast(Context, Op.Rn, Op.Size); - - Context.EmitLdc_I4(NumBytes); - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), TypesSrl)); - - Context.EmitCall(typeof(Sse41).GetMethod(NameCvt, TypesCvt)); - - EmitLdvecWithSignedCast(Context, Op.Rm, Op.Size); - - Context.EmitLdc_I4(NumBytes); - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), TypesSrl)); - - Context.EmitCall(typeof(Sse41).GetMethod(NameCvt, TypesCvt)); - - Context.EmitCall(TypeMul.GetMethod(nameof(Sse2.MultiplyLow), TypesMulAdd)); - - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), TypesMulAdd)); - - EmitStvecWithSignedCast(Context, Op.Rd, Op.Size + 1); - } - else - { - EmitVectorWidenRnRmTernaryOpSx(Context, () => - { - Context.Emit(OpCodes.Mul); - Context.Emit(OpCodes.Add); - }); - } - } - - public static void Smlsl_V(AILEmitterCtx Context) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - if (AOptimizations.UseSse41 && Op.Size < 2) - { - Type[] TypesSrl = new Type[] { VectorIntTypesPerSizeLog2[Op.Size], typeof(byte) }; - Type[] TypesCvt = new Type[] { VectorIntTypesPerSizeLog2[Op.Size] }; - Type[] TypesMulSub = new Type[] { VectorIntTypesPerSizeLog2[Op.Size + 1], - VectorIntTypesPerSizeLog2[Op.Size + 1] }; - - Type TypeMul = Op.Size == 0 ? typeof(Sse2) : typeof(Sse41); - - string NameCvt = Op.Size == 0 - ? nameof(Sse41.ConvertToVector128Int16) - : nameof(Sse41.ConvertToVector128Int32); - - int NumBytes = Op.RegisterSize == ARegisterSize.SIMD128 ? 8 : 0; - - EmitLdvecWithSignedCast(Context, Op.Rd, Op.Size + 1); - - EmitLdvecWithSignedCast(Context, Op.Rn, Op.Size); - - Context.EmitLdc_I4(NumBytes); - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), TypesSrl)); - - Context.EmitCall(typeof(Sse41).GetMethod(NameCvt, TypesCvt)); - - EmitLdvecWithSignedCast(Context, Op.Rm, Op.Size); - - Context.EmitLdc_I4(NumBytes); - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), TypesSrl)); - - Context.EmitCall(typeof(Sse41).GetMethod(NameCvt, TypesCvt)); - - Context.EmitCall(TypeMul.GetMethod(nameof(Sse2.MultiplyLow), TypesMulSub)); - - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Subtract), TypesMulSub)); - - EmitStvecWithSignedCast(Context, Op.Rd, Op.Size + 1); - } - else - { - EmitVectorWidenRnRmTernaryOpSx(Context, () => - { - Context.Emit(OpCodes.Mul); - Context.Emit(OpCodes.Sub); - }); - } - } - - public static void Smull_V(AILEmitterCtx Context) - { - EmitVectorWidenRnRmBinaryOpSx(Context, () => Context.Emit(OpCodes.Mul)); - } - - public static void Sqabs_S(AILEmitterCtx Context) - { - EmitScalarSaturatingUnaryOpSx(Context, () => EmitAbs(Context)); - } - - public static void Sqabs_V(AILEmitterCtx Context) - { - EmitVectorSaturatingUnaryOpSx(Context, () => EmitAbs(Context)); - } - - public static void Sqadd_S(AILEmitterCtx Context) - { - EmitScalarSaturatingBinaryOpSx(Context, SaturatingFlags.Add); - } - - public static void Sqadd_V(AILEmitterCtx Context) - { - EmitVectorSaturatingBinaryOpSx(Context, SaturatingFlags.Add); - } - - public static void Sqdmulh_S(AILEmitterCtx Context) - { - EmitSaturatingBinaryOp(Context, () => EmitDoublingMultiplyHighHalf(Context, Round: false), SaturatingFlags.ScalarSx); - } - - public static void Sqdmulh_V(AILEmitterCtx Context) - { - EmitSaturatingBinaryOp(Context, () => EmitDoublingMultiplyHighHalf(Context, Round: false), SaturatingFlags.VectorSx); - } - - public static void Sqneg_S(AILEmitterCtx Context) - { - EmitScalarSaturatingUnaryOpSx(Context, () => Context.Emit(OpCodes.Neg)); - } - - public static void Sqneg_V(AILEmitterCtx Context) - { - EmitVectorSaturatingUnaryOpSx(Context, () => Context.Emit(OpCodes.Neg)); - } - - public static void Sqrdmulh_S(AILEmitterCtx Context) - { - EmitSaturatingBinaryOp(Context, () => EmitDoublingMultiplyHighHalf(Context, Round: true), SaturatingFlags.ScalarSx); - } - - public static void Sqrdmulh_V(AILEmitterCtx Context) - { - EmitSaturatingBinaryOp(Context, () => EmitDoublingMultiplyHighHalf(Context, Round: true), SaturatingFlags.VectorSx); - } - - public static void Sqsub_S(AILEmitterCtx Context) - { - EmitScalarSaturatingBinaryOpSx(Context, SaturatingFlags.Sub); - } - - public static void Sqsub_V(AILEmitterCtx Context) - { - EmitVectorSaturatingBinaryOpSx(Context, SaturatingFlags.Sub); - } - - public static void Sqxtn_S(AILEmitterCtx Context) - { - EmitSaturatingNarrowOp(Context, SaturatingNarrowFlags.ScalarSxSx); - } - - public static void Sqxtn_V(AILEmitterCtx Context) - { - EmitSaturatingNarrowOp(Context, SaturatingNarrowFlags.VectorSxSx); - } - - public static void Sqxtun_S(AILEmitterCtx Context) - { - EmitSaturatingNarrowOp(Context, SaturatingNarrowFlags.ScalarSxZx); - } - - public static void Sqxtun_V(AILEmitterCtx Context) - { - EmitSaturatingNarrowOp(Context, SaturatingNarrowFlags.VectorSxZx); - } - - public static void Srhadd_V(AILEmitterCtx Context) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - if (AOptimizations.UseSse2 && Op.Size < 2) - { - Type[] TypesSav = new Type[] { IntTypesPerSizeLog2[Op.Size] }; - Type[] TypesSubAdd = new Type[] { VectorIntTypesPerSizeLog2 [Op.Size], VectorIntTypesPerSizeLog2 [Op.Size] }; - Type[] TypesAvg = new Type[] { VectorUIntTypesPerSizeLog2[Op.Size], VectorUIntTypesPerSizeLog2[Op.Size] }; - - Context.EmitLdc_I4(Op.Size == 0 ? sbyte.MinValue : short.MinValue); - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetAllVector128), TypesSav)); - - Context.Emit(OpCodes.Dup); - Context.EmitStvectmp(); - - EmitLdvecWithSignedCast(Context, Op.Rn, Op.Size); - Context.EmitLdvectmp(); - - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Subtract), TypesSubAdd)); - - EmitLdvecWithSignedCast(Context, Op.Rm, Op.Size); - Context.EmitLdvectmp(); - - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Subtract), TypesSubAdd)); - - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Average), TypesAvg)); - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), TypesSubAdd)); - - EmitStvecWithSignedCast(Context, Op.Rd, Op.Size); - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - else - { - EmitVectorBinaryOpSx(Context, () => - { - Context.Emit(OpCodes.Add); - - Context.Emit(OpCodes.Ldc_I4_1); - Context.Emit(OpCodes.Add); - - Context.Emit(OpCodes.Ldc_I4_1); - Context.Emit(OpCodes.Shr); - }); - } - } - - public static void Ssubl_V(AILEmitterCtx Context) - { - if (AOptimizations.UseSse41) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - Type[] TypesSrl = new Type[] { VectorIntTypesPerSizeLog2[Op.Size], typeof(byte) }; - Type[] TypesCvt = new Type[] { VectorIntTypesPerSizeLog2[Op.Size] }; - Type[] TypesSub = new Type[] { VectorIntTypesPerSizeLog2[Op.Size + 1], - VectorIntTypesPerSizeLog2[Op.Size + 1] }; - - string[] NamesCvt = new string[] { nameof(Sse41.ConvertToVector128Int16), - nameof(Sse41.ConvertToVector128Int32), - nameof(Sse41.ConvertToVector128Int64) }; - - int NumBytes = Op.RegisterSize == ARegisterSize.SIMD128 ? 8 : 0; - - EmitLdvecWithSignedCast(Context, Op.Rn, Op.Size); - - Context.EmitLdc_I4(NumBytes); - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), TypesSrl)); - - Context.EmitCall(typeof(Sse41).GetMethod(NamesCvt[Op.Size], TypesCvt)); - - EmitLdvecWithUnsignedCast(Context, Op.Rm, Op.Size); - - Context.EmitLdc_I4(NumBytes); - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), TypesSrl)); - - Context.EmitCall(typeof(Sse41).GetMethod(NamesCvt[Op.Size], TypesCvt)); - - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Subtract), TypesSub)); - - EmitStvecWithSignedCast(Context, Op.Rd, Op.Size + 1); - } - else - { - EmitVectorWidenRnRmBinaryOpSx(Context, () => Context.Emit(OpCodes.Sub)); - } - } - - public static void Ssubw_V(AILEmitterCtx Context) - { - EmitVectorWidenRmBinaryOpSx(Context, () => Context.Emit(OpCodes.Sub)); - } - - public static void Sub_S(AILEmitterCtx Context) - { - EmitScalarBinaryOpZx(Context, () => Context.Emit(OpCodes.Sub)); - } - - public static void Sub_V(AILEmitterCtx Context) - { - if (AOptimizations.UseSse2) - { - EmitSse2Op(Context, nameof(Sse2.Subtract)); - } - else - { - EmitVectorBinaryOpZx(Context, () => Context.Emit(OpCodes.Sub)); - } - } - - public static void Subhn_V(AILEmitterCtx Context) - { - EmitHighNarrow(Context, () => Context.Emit(OpCodes.Sub), Round: false); - } - - public static void Suqadd_S(AILEmitterCtx Context) - { - EmitScalarSaturatingBinaryOpSx(Context, SaturatingFlags.Accumulate); - } - - public static void Suqadd_V(AILEmitterCtx Context) - { - EmitVectorSaturatingBinaryOpSx(Context, SaturatingFlags.Accumulate); - } - - public static void Uaba_V(AILEmitterCtx Context) - { - EmitVectorTernaryOpZx(Context, () => - { - Context.Emit(OpCodes.Sub); - EmitAbs(Context); - - Context.Emit(OpCodes.Add); - }); - } - - public static void Uabal_V(AILEmitterCtx Context) - { - EmitVectorWidenRnRmTernaryOpZx(Context, () => - { - Context.Emit(OpCodes.Sub); - EmitAbs(Context); - - Context.Emit(OpCodes.Add); - }); - } - - public static void Uabd_V(AILEmitterCtx Context) - { - EmitVectorBinaryOpZx(Context, () => - { - Context.Emit(OpCodes.Sub); - EmitAbs(Context); - }); - } - - public static void Uabdl_V(AILEmitterCtx Context) - { - EmitVectorWidenRnRmBinaryOpZx(Context, () => - { - Context.Emit(OpCodes.Sub); - EmitAbs(Context); - }); - } - - public static void Uadalp_V(AILEmitterCtx Context) - { - EmitAddLongPairwise(Context, Signed: false, Accumulate: true); - } - - public static void Uaddl_V(AILEmitterCtx Context) - { - if (AOptimizations.UseSse41) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - Type[] TypesSrl = new Type[] { VectorUIntTypesPerSizeLog2[Op.Size], typeof(byte) }; - Type[] TypesCvt = new Type[] { VectorUIntTypesPerSizeLog2[Op.Size] }; - Type[] TypesAdd = new Type[] { VectorUIntTypesPerSizeLog2[Op.Size + 1], - VectorUIntTypesPerSizeLog2[Op.Size + 1] }; - - string[] NamesCvt = new string[] { nameof(Sse41.ConvertToVector128Int16), - nameof(Sse41.ConvertToVector128Int32), - nameof(Sse41.ConvertToVector128Int64) }; - - int NumBytes = Op.RegisterSize == ARegisterSize.SIMD128 ? 8 : 0; - - EmitLdvecWithUnsignedCast(Context, Op.Rn, Op.Size); - - Context.EmitLdc_I4(NumBytes); - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), TypesSrl)); - - Context.EmitCall(typeof(Sse41).GetMethod(NamesCvt[Op.Size], TypesCvt)); - - EmitLdvecWithUnsignedCast(Context, Op.Rm, Op.Size); - - Context.EmitLdc_I4(NumBytes); - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), TypesSrl)); - - Context.EmitCall(typeof(Sse41).GetMethod(NamesCvt[Op.Size], TypesCvt)); - - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), TypesAdd)); - - EmitStvecWithUnsignedCast(Context, Op.Rd, Op.Size + 1); - } - else - { - EmitVectorWidenRnRmBinaryOpZx(Context, () => Context.Emit(OpCodes.Add)); - } - } - - public static void Uaddlp_V(AILEmitterCtx Context) - { - EmitAddLongPairwise(Context, Signed: false, Accumulate: false); - } - - public static void Uaddlv_V(AILEmitterCtx Context) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - int Bytes = Op.GetBitsCount() >> 3; - int Elems = Bytes >> Op.Size; - - EmitVectorExtractZx(Context, Op.Rn, 0, Op.Size); - - for (int Index = 1; Index < Elems; 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)); - } - - public static void Uhadd_V(AILEmitterCtx Context) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - if (AOptimizations.UseSse2 && Op.Size > 0) - { - Type[] TypesSrl = new Type[] { VectorUIntTypesPerSizeLog2[Op.Size], typeof(byte) }; - Type[] TypesAndXorAdd = new Type[] { VectorUIntTypesPerSizeLog2[Op.Size], VectorUIntTypesPerSizeLog2[Op.Size] }; - - EmitLdvecWithUnsignedCast(Context, Op.Rn, Op.Size); - - Context.Emit(OpCodes.Dup); - Context.EmitStvectmp(); - - EmitLdvecWithUnsignedCast(Context, Op.Rm, Op.Size); - - Context.Emit(OpCodes.Dup); - Context.EmitStvectmp2(); - - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.And), TypesAndXorAdd)); - - Context.EmitLdvectmp(); - Context.EmitLdvectmp2(); - - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Xor), TypesAndXorAdd)); - - Context.EmitLdc_I4(1); - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical), TypesSrl)); - - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), TypesAndXorAdd)); - - EmitStvecWithUnsignedCast(Context, Op.Rd, Op.Size); - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - else - { - EmitVectorBinaryOpZx(Context, () => - { - Context.Emit(OpCodes.Add); - - Context.Emit(OpCodes.Ldc_I4_1); - Context.Emit(OpCodes.Shr_Un); - }); - } - } - - public static void Uhsub_V(AILEmitterCtx Context) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - if (AOptimizations.UseSse2 && Op.Size < 2) - { - Type[] TypesAvgSub = new Type[] { VectorUIntTypesPerSizeLog2[Op.Size], VectorUIntTypesPerSizeLog2[Op.Size] }; - - EmitLdvecWithUnsignedCast(Context, Op.Rn, Op.Size); - Context.Emit(OpCodes.Dup); - - EmitLdvecWithUnsignedCast(Context, Op.Rm, Op.Size); - - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Average), TypesAvgSub)); - - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Subtract), TypesAvgSub)); - - EmitStvecWithUnsignedCast(Context, Op.Rd, Op.Size); - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - else - { - EmitVectorBinaryOpZx(Context, () => - { - Context.Emit(OpCodes.Sub); - - Context.Emit(OpCodes.Ldc_I4_1); - Context.Emit(OpCodes.Shr_Un); - }); - } - } - - public static void Umax_V(AILEmitterCtx Context) - { - Type[] Types = new Type[] { typeof(ulong), typeof(ulong) }; - - MethodInfo MthdInfo = typeof(Math).GetMethod(nameof(Math.Max), Types); - - EmitVectorBinaryOpZx(Context, () => Context.EmitCall(MthdInfo)); - } - - public static void Umaxp_V(AILEmitterCtx Context) - { - Type[] Types = new Type[] { typeof(ulong), typeof(ulong) }; - - MethodInfo MthdInfo = typeof(Math).GetMethod(nameof(Math.Max), Types); - - EmitVectorPairwiseOpZx(Context, () => Context.EmitCall(MthdInfo)); - } - - public static void Umin_V(AILEmitterCtx Context) - { - Type[] Types = new Type[] { typeof(ulong), typeof(ulong) }; - - MethodInfo MthdInfo = typeof(Math).GetMethod(nameof(Math.Min), Types); - - EmitVectorBinaryOpZx(Context, () => Context.EmitCall(MthdInfo)); - } - - public static void Uminp_V(AILEmitterCtx Context) - { - Type[] Types = new Type[] { typeof(ulong), typeof(ulong) }; - - MethodInfo MthdInfo = typeof(Math).GetMethod(nameof(Math.Min), Types); - - EmitVectorPairwiseOpZx(Context, () => Context.EmitCall(MthdInfo)); - } - - public static void Umlal_V(AILEmitterCtx Context) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - if (AOptimizations.UseSse41 && Op.Size < 2) - { - Type[] TypesSrl = new Type[] { VectorUIntTypesPerSizeLog2[Op.Size], typeof(byte) }; - Type[] TypesCvt = new Type[] { VectorUIntTypesPerSizeLog2[Op.Size] }; - Type[] TypesMulAdd = new Type[] { VectorIntTypesPerSizeLog2 [Op.Size + 1], - VectorIntTypesPerSizeLog2 [Op.Size + 1] }; - - Type TypeMul = Op.Size == 0 ? typeof(Sse2) : typeof(Sse41); - - string NameCvt = Op.Size == 0 - ? nameof(Sse41.ConvertToVector128Int16) - : nameof(Sse41.ConvertToVector128Int32); - - int NumBytes = Op.RegisterSize == ARegisterSize.SIMD128 ? 8 : 0; - - EmitLdvecWithUnsignedCast(Context, Op.Rd, Op.Size + 1); - - EmitLdvecWithUnsignedCast(Context, Op.Rn, Op.Size); - - Context.EmitLdc_I4(NumBytes); - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), TypesSrl)); - - Context.EmitCall(typeof(Sse41).GetMethod(NameCvt, TypesCvt)); - - EmitLdvecWithUnsignedCast(Context, Op.Rm, Op.Size); - - Context.EmitLdc_I4(NumBytes); - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), TypesSrl)); - - Context.EmitCall(typeof(Sse41).GetMethod(NameCvt, TypesCvt)); - - Context.EmitCall(TypeMul.GetMethod(nameof(Sse2.MultiplyLow), TypesMulAdd)); - - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), TypesMulAdd)); - - EmitStvecWithUnsignedCast(Context, Op.Rd, Op.Size + 1); - } - else - { - EmitVectorWidenRnRmTernaryOpZx(Context, () => - { - Context.Emit(OpCodes.Mul); - Context.Emit(OpCodes.Add); - }); - } - } - - public static void Umlsl_V(AILEmitterCtx Context) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - if (AOptimizations.UseSse41 && Op.Size < 2) - { - Type[] TypesSrl = new Type[] { VectorUIntTypesPerSizeLog2[Op.Size], typeof(byte) }; - Type[] TypesCvt = new Type[] { VectorUIntTypesPerSizeLog2[Op.Size] }; - Type[] TypesMulSub = new Type[] { VectorIntTypesPerSizeLog2 [Op.Size + 1], - VectorIntTypesPerSizeLog2 [Op.Size + 1] }; - - Type TypeMul = Op.Size == 0 ? typeof(Sse2) : typeof(Sse41); - - string NameCvt = Op.Size == 0 - ? nameof(Sse41.ConvertToVector128Int16) - : nameof(Sse41.ConvertToVector128Int32); - - int NumBytes = Op.RegisterSize == ARegisterSize.SIMD128 ? 8 : 0; - - EmitLdvecWithUnsignedCast(Context, Op.Rd, Op.Size + 1); - - EmitLdvecWithUnsignedCast(Context, Op.Rn, Op.Size); - - Context.EmitLdc_I4(NumBytes); - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), TypesSrl)); - - Context.EmitCall(typeof(Sse41).GetMethod(NameCvt, TypesCvt)); - - EmitLdvecWithUnsignedCast(Context, Op.Rm, Op.Size); - - Context.EmitLdc_I4(NumBytes); - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), TypesSrl)); - - Context.EmitCall(typeof(Sse41).GetMethod(NameCvt, TypesCvt)); - - Context.EmitCall(TypeMul.GetMethod(nameof(Sse2.MultiplyLow), TypesMulSub)); - - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Subtract), TypesMulSub)); - - EmitStvecWithUnsignedCast(Context, Op.Rd, Op.Size + 1); - } - else - { - EmitVectorWidenRnRmTernaryOpZx(Context, () => - { - Context.Emit(OpCodes.Mul); - Context.Emit(OpCodes.Sub); - }); - } - } - - public static void Umull_V(AILEmitterCtx Context) - { - EmitVectorWidenRnRmBinaryOpZx(Context, () => Context.Emit(OpCodes.Mul)); - } - - public static void Uqadd_S(AILEmitterCtx Context) - { - EmitScalarSaturatingBinaryOpZx(Context, SaturatingFlags.Add); - } - - public static void Uqadd_V(AILEmitterCtx Context) - { - EmitVectorSaturatingBinaryOpZx(Context, SaturatingFlags.Add); - } - - public static void Uqsub_S(AILEmitterCtx Context) - { - EmitScalarSaturatingBinaryOpZx(Context, SaturatingFlags.Sub); - } - - public static void Uqsub_V(AILEmitterCtx Context) - { - EmitVectorSaturatingBinaryOpZx(Context, SaturatingFlags.Sub); - } - - public static void Uqxtn_S(AILEmitterCtx Context) - { - EmitSaturatingNarrowOp(Context, SaturatingNarrowFlags.ScalarZxZx); - } - - public static void Uqxtn_V(AILEmitterCtx Context) - { - EmitSaturatingNarrowOp(Context, SaturatingNarrowFlags.VectorZxZx); - } - - public static void Urhadd_V(AILEmitterCtx Context) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - if (AOptimizations.UseSse2 && Op.Size < 2) - { - Type[] TypesAvg = new Type[] { VectorUIntTypesPerSizeLog2[Op.Size], VectorUIntTypesPerSizeLog2[Op.Size] }; - - EmitLdvecWithUnsignedCast(Context, Op.Rn, Op.Size); - EmitLdvecWithUnsignedCast(Context, Op.Rm, Op.Size); - - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Average), TypesAvg)); - - EmitStvecWithUnsignedCast(Context, Op.Rd, Op.Size); - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - else - { - EmitVectorBinaryOpZx(Context, () => - { - Context.Emit(OpCodes.Add); - - Context.Emit(OpCodes.Ldc_I4_1); - Context.Emit(OpCodes.Add); - - Context.Emit(OpCodes.Ldc_I4_1); - Context.Emit(OpCodes.Shr_Un); - }); - } - } - - public static void Usqadd_S(AILEmitterCtx Context) - { - EmitScalarSaturatingBinaryOpZx(Context, SaturatingFlags.Accumulate); - } - - public static void Usqadd_V(AILEmitterCtx Context) - { - EmitVectorSaturatingBinaryOpZx(Context, SaturatingFlags.Accumulate); - } - - public static void Usubl_V(AILEmitterCtx Context) - { - if (AOptimizations.UseSse41) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - Type[] TypesSrl = new Type[] { VectorUIntTypesPerSizeLog2[Op.Size], typeof(byte) }; - Type[] TypesCvt = new Type[] { VectorUIntTypesPerSizeLog2[Op.Size] }; - Type[] TypesSub = new Type[] { VectorUIntTypesPerSizeLog2[Op.Size + 1], - VectorUIntTypesPerSizeLog2[Op.Size + 1] }; - - string[] NamesCvt = new string[] { nameof(Sse41.ConvertToVector128Int16), - nameof(Sse41.ConvertToVector128Int32), - nameof(Sse41.ConvertToVector128Int64) }; - - int NumBytes = Op.RegisterSize == ARegisterSize.SIMD128 ? 8 : 0; - - EmitLdvecWithUnsignedCast(Context, Op.Rn, Op.Size); - - Context.EmitLdc_I4(NumBytes); - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), TypesSrl)); - - Context.EmitCall(typeof(Sse41).GetMethod(NamesCvt[Op.Size], TypesCvt)); - - EmitLdvecWithUnsignedCast(Context, Op.Rm, Op.Size); - - Context.EmitLdc_I4(NumBytes); - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), TypesSrl)); - - Context.EmitCall(typeof(Sse41).GetMethod(NamesCvt[Op.Size], TypesCvt)); - - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Subtract), TypesSub)); - - EmitStvecWithUnsignedCast(Context, Op.Rd, Op.Size + 1); - } - else - { - EmitVectorWidenRnRmBinaryOpZx(Context, () => Context.Emit(OpCodes.Sub)); - } - } - - public static void Usubw_V(AILEmitterCtx Context) - { - EmitVectorWidenRmBinaryOpZx(Context, () => Context.Emit(OpCodes.Sub)); - } - - private static void EmitAbs(AILEmitterCtx Context) - { - AILLabel LblTrue = new AILLabel(); - - Context.Emit(OpCodes.Dup); - Context.Emit(OpCodes.Ldc_I4_0); - Context.Emit(OpCodes.Bge_S, LblTrue); - - Context.Emit(OpCodes.Neg); - - Context.MarkLabel(LblTrue); - } - - private static void EmitAddLongPairwise(AILEmitterCtx Context, bool Signed, bool Accumulate) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - int Words = Op.GetBitsCount() >> 4; - int Pairs = Words >> Op.Size; - - for (int Index = 0; Index < Pairs; Index++) - { - int Idx = Index << 1; - - EmitVectorExtract(Context, Op.Rn, Idx, Op.Size, Signed); - EmitVectorExtract(Context, Op.Rn, Idx + 1, Op.Size, Signed); - - Context.Emit(OpCodes.Add); - - if (Accumulate) - { - EmitVectorExtract(Context, Op.Rd, Index, Op.Size + 1, Signed); - - Context.Emit(OpCodes.Add); - } - - EmitVectorInsertTmp(Context, Index, Op.Size + 1); - } - - Context.EmitLdvectmp(); - Context.EmitStvec(Op.Rd); - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - private static void EmitDoublingMultiplyHighHalf(AILEmitterCtx Context, bool Round) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - int ESize = 8 << Op.Size; - - Context.Emit(OpCodes.Mul); - - if (!Round) - { - Context.EmitAsr(ESize - 1); - } - else - { - long RoundConst = 1L << (ESize - 1); - - AILLabel LblTrue = new AILLabel(); - - Context.EmitLsl(1); - - Context.EmitLdc_I8(RoundConst); - - Context.Emit(OpCodes.Add); - - Context.EmitAsr(ESize); - - Context.Emit(OpCodes.Dup); - Context.EmitLdc_I8((long)int.MinValue); - Context.Emit(OpCodes.Bne_Un_S, LblTrue); - - Context.Emit(OpCodes.Neg); - - Context.MarkLabel(LblTrue); - } - } - - private static void EmitHighNarrow(AILEmitterCtx Context, Action Emit, bool Round) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - int Elems = 8 >> Op.Size; - - int ESize = 8 << Op.Size; - - int Part = Op.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0; - - long RoundConst = 1L << (ESize - 1); - - if (Part != 0) - { - Context.EmitLdvec(Op.Rd); - Context.EmitStvectmp(); - } - - for (int Index = 0; Index < Elems; Index++) - { - EmitVectorExtractZx(Context, Op.Rn, Index, Op.Size + 1); - EmitVectorExtractZx(Context, Op.Rm, Index, Op.Size + 1); - - Emit(); - - if (Round) - { - Context.EmitLdc_I8(RoundConst); - - Context.Emit(OpCodes.Add); - } - - Context.EmitLsr(ESize); - - EmitVectorInsertTmp(Context, Part + Index, Op.Size); - } - - Context.EmitLdvectmp(); - Context.EmitStvec(Op.Rd); - - if (Part == 0) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - } -} diff --git a/Instruction/AInstEmitSimdCmp.cs b/Instruction/AInstEmitSimdCmp.cs deleted file mode 100644 index cd3480e..0000000 --- a/Instruction/AInstEmitSimdCmp.cs +++ /dev/null @@ -1,526 +0,0 @@ -using ChocolArm64.Decoder; -using ChocolArm64.State; -using ChocolArm64.Translation; -using System; -using System.Reflection.Emit; -using System.Runtime.Intrinsics.X86; - -using static ChocolArm64.Instruction.AInstEmitAluHelper; -using static ChocolArm64.Instruction.AInstEmitSimdHelper; - -namespace ChocolArm64.Instruction -{ - static partial class AInstEmit - { - public static void Cmeq_S(AILEmitterCtx Context) - { - EmitCmp(Context, OpCodes.Beq_S, Scalar: true); - } - - public static void Cmeq_V(AILEmitterCtx Context) - { - if (Context.CurrOp is AOpCodeSimdReg Op) - { - if (Op.Size < 3 && AOptimizations.UseSse2) - { - EmitSse2Op(Context, nameof(Sse2.CompareEqual)); - } - else if (Op.Size == 3 && AOptimizations.UseSse41) - { - EmitSse41Op(Context, nameof(Sse41.CompareEqual)); - } - else - { - EmitCmp(Context, OpCodes.Beq_S, Scalar: false); - } - } - else - { - EmitCmp(Context, OpCodes.Beq_S, Scalar: false); - } - } - - public static void Cmge_S(AILEmitterCtx Context) - { - EmitCmp(Context, OpCodes.Bge_S, Scalar: true); - } - - public static void Cmge_V(AILEmitterCtx Context) - { - EmitCmp(Context, OpCodes.Bge_S, Scalar: false); - } - - public static void Cmgt_S(AILEmitterCtx Context) - { - EmitCmp(Context, OpCodes.Bgt_S, Scalar: true); - } - - public static void Cmgt_V(AILEmitterCtx Context) - { - if (Context.CurrOp is AOpCodeSimdReg Op) - { - if (Op.Size < 3 && AOptimizations.UseSse2) - { - EmitSse2Op(Context, nameof(Sse2.CompareGreaterThan)); - } - else if (Op.Size == 3 && AOptimizations.UseSse42) - { - EmitSse42Op(Context, nameof(Sse42.CompareGreaterThan)); - } - else - { - EmitCmp(Context, OpCodes.Bgt_S, Scalar: false); - } - } - else - { - EmitCmp(Context, OpCodes.Bgt_S, Scalar: false); - } - } - - public static void Cmhi_S(AILEmitterCtx Context) - { - EmitCmp(Context, OpCodes.Bgt_Un_S, Scalar: true); - } - - public static void Cmhi_V(AILEmitterCtx Context) - { - EmitCmp(Context, OpCodes.Bgt_Un_S, Scalar: false); - } - - public static void Cmhs_S(AILEmitterCtx Context) - { - EmitCmp(Context, OpCodes.Bge_Un_S, Scalar: true); - } - - public static void Cmhs_V(AILEmitterCtx Context) - { - EmitCmp(Context, OpCodes.Bge_Un_S, Scalar: false); - } - - public static void Cmle_S(AILEmitterCtx Context) - { - EmitCmp(Context, OpCodes.Ble_S, Scalar: true); - } - - public static void Cmle_V(AILEmitterCtx Context) - { - EmitCmp(Context, OpCodes.Ble_S, Scalar: false); - } - - public static void Cmlt_S(AILEmitterCtx Context) - { - EmitCmp(Context, OpCodes.Blt_S, Scalar: true); - } - - public static void Cmlt_V(AILEmitterCtx Context) - { - EmitCmp(Context, OpCodes.Blt_S, Scalar: false); - } - - public static void Cmtst_S(AILEmitterCtx Context) - { - EmitCmtst(Context, Scalar: true); - } - - public static void Cmtst_V(AILEmitterCtx Context) - { - EmitCmtst(Context, Scalar: false); - } - - 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 Fcmeq_S(AILEmitterCtx Context) - { - if (Context.CurrOp is AOpCodeSimdReg && AOptimizations.UseSse - && AOptimizations.UseSse2) - { - EmitScalarSseOrSse2OpF(Context, nameof(Sse.CompareEqualScalar)); - } - else - { - EmitScalarFcmp(Context, OpCodes.Beq_S); - } - } - - public static void Fcmeq_V(AILEmitterCtx Context) - { - if (Context.CurrOp is AOpCodeSimdReg && AOptimizations.UseSse - && AOptimizations.UseSse2) - { - EmitVectorSseOrSse2OpF(Context, nameof(Sse.CompareEqual)); - } - else - { - EmitVectorFcmp(Context, OpCodes.Beq_S); - } - } - - public static void Fcmge_S(AILEmitterCtx Context) - { - if (Context.CurrOp is AOpCodeSimdReg && AOptimizations.UseSse - && AOptimizations.UseSse2) - { - EmitScalarSseOrSse2OpF(Context, nameof(Sse.CompareGreaterThanOrEqualScalar)); - } - else - { - EmitScalarFcmp(Context, OpCodes.Bge_S); - } - } - - public static void Fcmge_V(AILEmitterCtx Context) - { - if (Context.CurrOp is AOpCodeSimdReg && AOptimizations.UseSse - && AOptimizations.UseSse2) - { - EmitVectorSseOrSse2OpF(Context, nameof(Sse.CompareGreaterThanOrEqual)); - } - else - { - EmitVectorFcmp(Context, OpCodes.Bge_S); - } - } - - public static void Fcmgt_S(AILEmitterCtx Context) - { - if (Context.CurrOp is AOpCodeSimdReg && AOptimizations.UseSse - && AOptimizations.UseSse2) - { - EmitScalarSseOrSse2OpF(Context, nameof(Sse.CompareGreaterThanScalar)); - } - else - { - EmitScalarFcmp(Context, OpCodes.Bgt_S); - } - } - - public static void Fcmgt_V(AILEmitterCtx Context) - { - if (Context.CurrOp is AOpCodeSimdReg && AOptimizations.UseSse - && AOptimizations.UseSse2) - { - EmitVectorSseOrSse2OpF(Context, nameof(Sse.CompareGreaterThan)); - } - else - { - EmitVectorFcmp(Context, OpCodes.Bgt_S); - } - } - - public static void Fcmle_S(AILEmitterCtx Context) - { - EmitScalarFcmp(Context, OpCodes.Ble_S); - } - - public static void Fcmle_V(AILEmitterCtx Context) - { - EmitVectorFcmp(Context, OpCodes.Ble_S); - } - - public static void Fcmlt_S(AILEmitterCtx Context) - { - EmitScalarFcmp(Context, OpCodes.Blt_S); - } - - public static void Fcmlt_V(AILEmitterCtx Context) - { - EmitVectorFcmp(Context, OpCodes.Blt_S); - } - - 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) - { - if (Op.Size == 0) - { - Context.EmitLdc_R4(0f); - } - else /* if (Op.Size == 1) */ - { - Context.EmitLdc_R8(0d); - } - } - 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 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 EmitCmp(AILEmitterCtx Context, OpCode ILOp, bool Scalar) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - int Bytes = Op.GetBitsCount() >> 3; - int Elems = !Scalar ? Bytes >> Op.Size : 1; - - ulong SzMask = ulong.MaxValue >> (64 - (8 << Op.Size)); - - for (int Index = 0; Index < Elems; Index++) - { - EmitVectorExtractSx(Context, Op.Rn, Index, Op.Size); - - if (Op is AOpCodeSimdReg BinOp) - { - EmitVectorExtractSx(Context, BinOp.Rm, Index, Op.Size); - } - else - { - Context.EmitLdc_I8(0L); - } - - 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) || Scalar) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - private static void EmitCmtst(AILEmitterCtx Context, bool Scalar) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - int Bytes = Op.GetBitsCount() >> 3; - int Elems = !Scalar ? Bytes >> Op.Size : 1; - - ulong SzMask = ulong.MaxValue >> (64 - (8 << Op.Size)); - - for (int Index = 0; Index < Elems; Index++) - { - EmitVectorExtractZx(Context, Op.Rn, Index, Op.Size); - EmitVectorExtractZx(Context, Op.Rm, Index, Op.Size); - - AILLabel LblTrue = new AILLabel(); - AILLabel LblEnd = new AILLabel(); - - Context.Emit(OpCodes.And); - - Context.EmitLdc_I8(0L); - - Context.Emit(OpCodes.Bne_Un_S, 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) || Scalar) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - private static void EmitScalarFcmp(AILEmitterCtx Context, OpCode ILOp) - { - EmitFcmp(Context, ILOp, 0, Scalar: true); - } - - private static void EmitVectorFcmp(AILEmitterCtx Context, OpCode ILOp) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - int SizeF = Op.Size & 1; - - int Bytes = Op.GetBitsCount() >> 3; - int Elems = Bytes >> SizeF + 2; - - for (int Index = 0; Index < Elems; Index++) - { - EmitFcmp(Context, ILOp, Index, Scalar: false); - } - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - private static void EmitFcmp(AILEmitterCtx Context, OpCode ILOp, int Index, bool Scalar) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - int SizeF = Op.Size & 1; - - ulong SzMask = ulong.MaxValue >> (64 - (32 << SizeF)); - - EmitVectorExtractF(Context, Op.Rn, Index, SizeF); - - if (Op is AOpCodeSimdReg BinOp) - { - EmitVectorExtractF(Context, BinOp.Rm, Index, SizeF); - } - else if (SizeF == 0) - { - Context.EmitLdc_R4(0f); - } - else /* if (SizeF == 1) */ - { - Context.EmitLdc_R8(0d); - } - - AILLabel LblTrue = new AILLabel(); - AILLabel LblEnd = new AILLabel(); - - Context.Emit(ILOp, LblTrue); - - if (Scalar) - { - EmitVectorZeroAll(Context, Op.Rd); - } - else - { - EmitVectorInsert(Context, Op.Rd, Index, SizeF + 2, 0); - } - - Context.Emit(OpCodes.Br_S, LblEnd); - - Context.MarkLabel(LblTrue); - - if (Scalar) - { - EmitVectorInsert(Context, Op.Rd, Index, 3, (long)SzMask); - - EmitVectorZeroUpper(Context, Op.Rd); - } - else - { - EmitVectorInsert(Context, Op.Rd, Index, SizeF + 2, (long)SzMask); - } - - Context.MarkLabel(LblEnd); - } - } -} diff --git a/Instruction/AInstEmitSimdCrypto.cs b/Instruction/AInstEmitSimdCrypto.cs deleted file mode 100644 index b2680a5..0000000 --- a/Instruction/AInstEmitSimdCrypto.cs +++ /dev/null @@ -1,54 +0,0 @@ -using ChocolArm64.Decoder; -using ChocolArm64.Translation; - -namespace ChocolArm64.Instruction -{ - static partial class AInstEmit - { - public static void Aesd_V(AILEmitterCtx Context) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - Context.EmitLdvec(Op.Rd); - Context.EmitLdvec(Op.Rn); - - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.Decrypt)); - - Context.EmitStvec(Op.Rd); - } - - public static void Aese_V(AILEmitterCtx Context) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - Context.EmitLdvec(Op.Rd); - Context.EmitLdvec(Op.Rn); - - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.Encrypt)); - - Context.EmitStvec(Op.Rd); - } - - public static void Aesimc_V(AILEmitterCtx Context) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - Context.EmitLdvec(Op.Rn); - - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.InverseMixColumns)); - - Context.EmitStvec(Op.Rd); - } - - public static void Aesmc_V(AILEmitterCtx Context) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - Context.EmitLdvec(Op.Rn); - - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.MixColumns)); - - Context.EmitStvec(Op.Rd); - } - } -} diff --git a/Instruction/AInstEmitSimdCvt.cs b/Instruction/AInstEmitSimdCvt.cs deleted file mode 100644 index f277069..0000000 --- a/Instruction/AInstEmitSimdCvt.cs +++ /dev/null @@ -1,697 +0,0 @@ -using ChocolArm64.Decoder; -using ChocolArm64.State; -using ChocolArm64.Translation; -using System; -using System.Reflection.Emit; -using System.Runtime.Intrinsics; -using System.Runtime.Intrinsics.X86; - -using static ChocolArm64.Instruction.AInstEmitSimdHelper; - -namespace ChocolArm64.Instruction -{ - static partial class AInstEmit - { - public static void Fcvt_S(AILEmitterCtx Context) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - if (AOptimizations.UseSse2) - { - if (Op.Size == 1 && Op.Opc == 0) - { - //Double -> Single. - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorSingleZero)); - - EmitLdvecWithCastToDouble(Context, Op.Rn); - - Type[] Types = new Type[] { typeof(Vector128), typeof(Vector128) }; - - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ConvertScalarToVector128Single), Types)); - - Context.EmitStvec(Op.Rd); - } - else if (Op.Size == 0 && Op.Opc == 1) - { - //Single -> Double. - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorDoubleZero)); - - Context.EmitLdvec(Op.Rn); - - Type[] Types = new Type[] { typeof(Vector128), typeof(Vector128) }; - - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ConvertScalarToVector128Double), Types)); - - EmitStvecWithCastFromDouble(Context, Op.Rd); - } - else - { - //Invalid encoding. - throw new InvalidOperationException(); - } - } - else - { - EmitVectorExtractF(Context, Op.Rn, 0, Op.Size); - - EmitFloatCast(Context, Op.Opc); - - EmitScalarSetF(Context, Op.Rd, Op.Opc); - } - } - - public static void Fcvtas_Gp(AILEmitterCtx Context) - { - EmitFcvt_s_Gp(Context, () => EmitRoundMathCall(Context, MidpointRounding.AwayFromZero)); - } - - public static void Fcvtau_Gp(AILEmitterCtx Context) - { - EmitFcvt_u_Gp(Context, () => EmitRoundMathCall(Context, MidpointRounding.AwayFromZero)); - } - - public static void Fcvtl_V(AILEmitterCtx Context) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - int SizeF = Op.Size & 1; - - int Elems = 4 >> SizeF; - - int Part = Op.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0; - - for (int Index = 0; Index < Elems; Index++) - { - if (SizeF == 0) - { - EmitVectorExtractZx(Context, Op.Rn, Part + Index, 1); - Context.Emit(OpCodes.Conv_U2); - - Context.EmitLdarg(ATranslatedSub.StateArgIdx); - - Context.EmitCall(typeof(ASoftFloat16_32), nameof(ASoftFloat16_32.FPConvert)); - } - else /* if (SizeF == 1) */ - { - EmitVectorExtractF(Context, Op.Rn, Part + Index, 0); - - Context.Emit(OpCodes.Conv_R8); - } - - EmitVectorInsertTmpF(Context, Index, SizeF); - } - - Context.EmitLdvectmp(); - Context.EmitStvec(Op.Rd); - } - - public static void Fcvtms_Gp(AILEmitterCtx Context) - { - EmitFcvt_s_Gp(Context, () => EmitUnaryMathCall(Context, nameof(Math.Floor))); - } - - public static void Fcvtmu_Gp(AILEmitterCtx Context) - { - EmitFcvt_u_Gp(Context, () => EmitUnaryMathCall(Context, nameof(Math.Floor))); - } - - public static void Fcvtn_V(AILEmitterCtx Context) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - int SizeF = Op.Size & 1; - - int Elems = 4 >> SizeF; - - int Part = Op.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0; - - if (Part != 0) - { - Context.EmitLdvec(Op.Rd); - Context.EmitStvectmp(); - } - - for (int Index = 0; Index < Elems; Index++) - { - EmitVectorExtractF(Context, Op.Rn, Index, SizeF); - - if (SizeF == 0) - { - Context.EmitLdarg(ATranslatedSub.StateArgIdx); - - Context.EmitCall(typeof(ASoftFloat32_16), nameof(ASoftFloat32_16.FPConvert)); - - Context.Emit(OpCodes.Conv_U8); - EmitVectorInsertTmp(Context, Part + Index, 1); - } - else /* if (SizeF == 1) */ - { - Context.Emit(OpCodes.Conv_R4); - - EmitVectorInsertTmpF(Context, Part + Index, 0); - } - } - - Context.EmitLdvectmp(); - Context.EmitStvec(Op.Rd); - - if (Part == 0) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - public static void Fcvtns_S(AILEmitterCtx Context) - { - EmitFcvtn(Context, Signed: true, Scalar: true); - } - - public static void Fcvtns_V(AILEmitterCtx Context) - { - EmitFcvtn(Context, Signed: true, Scalar: false); - } - - public static void Fcvtnu_S(AILEmitterCtx Context) - { - EmitFcvtn(Context, Signed: false, Scalar: true); - } - - public static void Fcvtnu_V(AILEmitterCtx Context) - { - EmitFcvtn(Context, Signed: false, Scalar: false); - } - - public static void Fcvtps_Gp(AILEmitterCtx Context) - { - EmitFcvt_s_Gp(Context, () => EmitUnaryMathCall(Context, nameof(Math.Ceiling))); - } - - public static void Fcvtpu_Gp(AILEmitterCtx Context) - { - EmitFcvt_u_Gp(Context, () => EmitUnaryMathCall(Context, nameof(Math.Ceiling))); - } - - public static void Fcvtzs_Gp(AILEmitterCtx Context) - { - EmitFcvt_s_Gp(Context, () => { }); - } - - public static void Fcvtzs_Gp_Fix(AILEmitterCtx Context) - { - EmitFcvtzs_Gp_Fix(Context); - } - - public static void Fcvtzs_S(AILEmitterCtx Context) - { - EmitScalarFcvtzs(Context); - } - - public static void Fcvtzs_V(AILEmitterCtx Context) - { - EmitVectorFcvtzs(Context); - } - - public static void Fcvtzu_Gp(AILEmitterCtx Context) - { - EmitFcvt_u_Gp(Context, () => { }); - } - - public static void Fcvtzu_Gp_Fix(AILEmitterCtx Context) - { - EmitFcvtzu_Gp_Fix(Context); - } - - public static void Fcvtzu_S(AILEmitterCtx Context) - { - EmitScalarFcvtzu(Context); - } - - public static void Fcvtzu_V(AILEmitterCtx Context) - { - EmitVectorFcvtzu(Context); - } - - 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 EmitFcvtn(AILEmitterCtx Context, bool Signed, bool Scalar) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - int SizeF = Op.Size & 1; - int SizeI = SizeF + 2; - - int Bytes = Op.GetBitsCount() >> 3; - int Elems = !Scalar ? Bytes >> SizeI : 1; - - if (Scalar && (SizeF == 0)) - { - EmitVectorZeroLowerTmp(Context); - } - - for (int Index = 0; Index < Elems; Index++) - { - EmitVectorExtractF(Context, Op.Rn, Index, SizeF); - - EmitRoundMathCall(Context, MidpointRounding.ToEven); - - if (SizeF == 0) - { - AVectorHelper.EmitCall(Context, Signed - ? nameof(AVectorHelper.SatF32ToS32) - : nameof(AVectorHelper.SatF32ToU32)); - - Context.Emit(OpCodes.Conv_U8); - } - else /* if (SizeF == 1) */ - { - AVectorHelper.EmitCall(Context, Signed - ? nameof(AVectorHelper.SatF64ToS64) - : nameof(AVectorHelper.SatF64ToU64)); - } - - EmitVectorInsertTmp(Context, Index, SizeI); - } - - Context.EmitLdvectmp(); - Context.EmitStvec(Op.Rd); - - if ((Op.RegisterSize == ARegisterSize.SIMD64) || Scalar) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - private static void EmitFcvt_s_Gp(AILEmitterCtx Context, Action Emit) - { - EmitFcvt___Gp(Context, Emit, true); - } - - private static void EmitFcvt_u_Gp(AILEmitterCtx Context, Action Emit) - { - EmitFcvt___Gp(Context, Emit, false); - } - - private static void EmitFcvt___Gp(AILEmitterCtx Context, Action Emit, bool Signed) - { - AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp; - - EmitVectorExtractF(Context, Op.Rn, 0, Op.Size); - - Emit(); - - 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 EmitFcvtzs_Gp_Fix(AILEmitterCtx Context) - { - EmitFcvtz__Gp_Fix(Context, true); - } - - private static void EmitFcvtzu_Gp_Fix(AILEmitterCtx Context) - { - EmitFcvtz__Gp_Fix(Context, false); - } - - 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 EmitVectorScvtf(AILEmitterCtx Context) - { - EmitVectorCvtf(Context, true); - } - - private static void EmitVectorUcvtf(AILEmitterCtx Context) - { - EmitVectorCvtf(Context, false); - } - - 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 = Op.GetBitsCount() >> 3; - int Elems = Bytes >> SizeI; - - for (int Index = 0; Index < Elems; 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 EmitScalarFcvtzs(AILEmitterCtx Context) - { - EmitScalarFcvtz(Context, true); - } - - private static void EmitScalarFcvtzu(AILEmitterCtx Context) - { - EmitScalarFcvtz(Context, false); - } - - private static void EmitScalarFcvtz(AILEmitterCtx Context, bool Signed) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - int SizeF = Op.Size & 1; - int SizeI = SizeF + 2; - - int FBits = GetFBits(Context); - - EmitVectorExtractF(Context, Op.Rn, 0, SizeF); - - EmitF2iFBitsMul(Context, SizeF, FBits); - - if (SizeF == 0) - { - AVectorHelper.EmitCall(Context, Signed - ? nameof(AVectorHelper.SatF32ToS32) - : nameof(AVectorHelper.SatF32ToU32)); - } - else /* if (SizeF == 1) */ - { - AVectorHelper.EmitCall(Context, Signed - ? nameof(AVectorHelper.SatF64ToS64) - : nameof(AVectorHelper.SatF64ToU64)); - } - - if (SizeF == 0) - { - Context.Emit(OpCodes.Conv_U8); - } - - EmitScalarSet(Context, Op.Rd, SizeI); - } - - private static void EmitVectorFcvtzs(AILEmitterCtx Context) - { - EmitVectorFcvtz(Context, true); - } - - private static void EmitVectorFcvtzu(AILEmitterCtx Context) - { - EmitVectorFcvtz(Context, false); - } - - private static void EmitVectorFcvtz(AILEmitterCtx Context, bool Signed) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - int SizeF = Op.Size & 1; - int SizeI = SizeF + 2; - - int FBits = GetFBits(Context); - - int Bytes = Op.GetBitsCount() >> 3; - int Elems = Bytes >> SizeI; - - for (int Index = 0; Index < Elems; Index++) - { - EmitVectorExtractF(Context, Op.Rn, Index, SizeF); - - EmitF2iFBitsMul(Context, SizeF, FBits); - - if (SizeF == 0) - { - AVectorHelper.EmitCall(Context, Signed - ? nameof(AVectorHelper.SatF32ToS32) - : nameof(AVectorHelper.SatF32ToU32)); - } - else /* if (SizeF == 1) */ - { - AVectorHelper.EmitCall(Context, Signed - ? nameof(AVectorHelper.SatF64ToS64) - : nameof(AVectorHelper.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) - { - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.SatF32ToS32)); - } - else /* if (Size == 1) */ - { - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.SatF64ToS32)); - } - } - else - { - if (Size == 0) - { - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.SatF32ToS64)); - } - else /* if (Size == 1) */ - { - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.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) - { - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.SatF32ToU32)); - } - else /* if (Size == 1) */ - { - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.SatF64ToU32)); - } - } - else - { - if (Size == 0) - { - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.SatF32ToU64)); - } - else /* if (Size == 1) */ - { - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.SatF64ToU64)); - } - } - } - - private static void EmitF2iFBitsMul(AILEmitterCtx Context, int Size, int FBits) - { - if (FBits != 0) - { - if (Size == 0) - { - Context.EmitLdc_R4(MathF.Pow(2f, FBits)); - } - else if (Size == 1) - { - Context.EmitLdc_R8(Math.Pow(2d, 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(2f, FBits)); - } - else if (Size == 1) - { - Context.EmitLdc_R8(1d / Math.Pow(2d, FBits)); - } - else - { - throw new ArgumentOutOfRangeException(nameof(Size)); - } - - Context.Emit(OpCodes.Mul); - } - } - } -} diff --git a/Instruction/AInstEmitSimdHash.cs b/Instruction/AInstEmitSimdHash.cs deleted file mode 100644 index 5a59e77..0000000 --- a/Instruction/AInstEmitSimdHash.cs +++ /dev/null @@ -1,140 +0,0 @@ -using ChocolArm64.Decoder; -using ChocolArm64.Translation; - -using static ChocolArm64.Instruction.AInstEmitSimdHelper; - -namespace ChocolArm64.Instruction -{ - static partial class AInstEmit - { -#region "Sha1" - public static void Sha1c_V(AILEmitterCtx Context) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - Context.EmitLdvec(Op.Rd); - EmitVectorExtractZx(Context, Op.Rn, 0, 2); - Context.EmitLdvec(Op.Rm); - - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.HashChoose)); - - Context.EmitStvec(Op.Rd); - } - - public static void Sha1h_V(AILEmitterCtx Context) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - EmitVectorExtractZx(Context, Op.Rn, 0, 2); - - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.FixedRotate)); - - EmitScalarSet(Context, Op.Rd, 2); - } - - public static void Sha1m_V(AILEmitterCtx Context) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - Context.EmitLdvec(Op.Rd); - EmitVectorExtractZx(Context, Op.Rn, 0, 2); - Context.EmitLdvec(Op.Rm); - - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.HashMajority)); - - Context.EmitStvec(Op.Rd); - } - - public static void Sha1p_V(AILEmitterCtx Context) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - Context.EmitLdvec(Op.Rd); - EmitVectorExtractZx(Context, Op.Rn, 0, 2); - Context.EmitLdvec(Op.Rm); - - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.HashParity)); - - Context.EmitStvec(Op.Rd); - } - - public static void Sha1su0_V(AILEmitterCtx Context) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - Context.EmitLdvec(Op.Rd); - Context.EmitLdvec(Op.Rn); - Context.EmitLdvec(Op.Rm); - - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.Sha1SchedulePart1)); - - Context.EmitStvec(Op.Rd); - } - - public static void Sha1su1_V(AILEmitterCtx Context) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - Context.EmitLdvec(Op.Rd); - Context.EmitLdvec(Op.Rn); - - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.Sha1SchedulePart2)); - - Context.EmitStvec(Op.Rd); - } -#endregion - -#region "Sha256" - public static void Sha256h_V(AILEmitterCtx Context) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - Context.EmitLdvec(Op.Rd); - Context.EmitLdvec(Op.Rn); - Context.EmitLdvec(Op.Rm); - - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.HashLower)); - - Context.EmitStvec(Op.Rd); - } - - public static void Sha256h2_V(AILEmitterCtx Context) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - Context.EmitLdvec(Op.Rd); - Context.EmitLdvec(Op.Rn); - Context.EmitLdvec(Op.Rm); - - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.HashUpper)); - - Context.EmitStvec(Op.Rd); - } - - public static void Sha256su0_V(AILEmitterCtx Context) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - Context.EmitLdvec(Op.Rd); - Context.EmitLdvec(Op.Rn); - - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.Sha256SchedulePart1)); - - Context.EmitStvec(Op.Rd); - } - - public static void Sha256su1_V(AILEmitterCtx Context) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - Context.EmitLdvec(Op.Rd); - Context.EmitLdvec(Op.Rn); - Context.EmitLdvec(Op.Rm); - - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.Sha256SchedulePart2)); - - Context.EmitStvec(Op.Rd); - } -#endregion - } -} diff --git a/Instruction/AInstEmitSimdHelper.cs b/Instruction/AInstEmitSimdHelper.cs deleted file mode 100644 index ff08283..0000000 --- a/Instruction/AInstEmitSimdHelper.cs +++ /dev/null @@ -1,1495 +0,0 @@ -using ChocolArm64.Decoder; -using ChocolArm64.State; -using ChocolArm64.Translation; -using System; -using System.Reflection; -using System.Reflection.Emit; -using System.Runtime.Intrinsics; -using System.Runtime.Intrinsics.X86; - -namespace ChocolArm64.Instruction -{ - static class AInstEmitSimdHelper - { - public static readonly Type[] IntTypesPerSizeLog2 = new Type[] - { - typeof(sbyte), - typeof(short), - typeof(int), - typeof(long) - }; - - public static readonly Type[] UIntTypesPerSizeLog2 = new Type[] - { - typeof(byte), - typeof(ushort), - typeof(uint), - typeof(ulong) - }; - - public static readonly Type[] VectorIntTypesPerSizeLog2 = new Type[] - { - typeof(Vector128), - typeof(Vector128), - typeof(Vector128), - typeof(Vector128) - }; - - public static readonly Type[] VectorUIntTypesPerSizeLog2 = new Type[] - { - typeof(Vector128), - typeof(Vector128), - typeof(Vector128), - typeof(Vector128) - }; - - [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 EmitSse2Op(AILEmitterCtx Context, string Name) - { - EmitSseOp(Context, Name, typeof(Sse2)); - } - - public static void EmitSse41Op(AILEmitterCtx Context, string Name) - { - EmitSseOp(Context, Name, typeof(Sse41)); - } - - public static void EmitSse42Op(AILEmitterCtx Context, string Name) - { - EmitSseOp(Context, Name, typeof(Sse42)); - } - - private static void EmitSseOp(AILEmitterCtx Context, string Name, Type Type) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - EmitLdvecWithSignedCast(Context, Op.Rn, Op.Size); - - Type BaseType = VectorIntTypesPerSizeLog2[Op.Size]; - - if (Op is AOpCodeSimdReg BinOp) - { - EmitLdvecWithSignedCast(Context, BinOp.Rm, Op.Size); - - Context.EmitCall(Type.GetMethod(Name, new Type[] { BaseType, BaseType })); - } - else - { - Context.EmitCall(Type.GetMethod(Name, new Type[] { BaseType })); - } - - EmitStvecWithSignedCast(Context, Op.Rd, Op.Size); - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - public static void EmitLdvecWithSignedCast(AILEmitterCtx Context, int Reg, int Size) - { - Context.EmitLdvec(Reg); - - switch (Size) - { - case 0: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorSingleToSByte)); break; - case 1: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorSingleToInt16)); break; - case 2: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorSingleToInt32)); break; - case 3: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorSingleToInt64)); break; - - default: throw new ArgumentOutOfRangeException(nameof(Size)); - } - } - - public static void EmitLdvecWithCastToDouble(AILEmitterCtx Context, int Reg) - { - Context.EmitLdvec(Reg); - - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorSingleToDouble)); - } - - public static void EmitStvecWithCastFromDouble(AILEmitterCtx Context, int Reg) - { - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorDoubleToSingle)); - - Context.EmitStvec(Reg); - } - - public static void EmitLdvecWithUnsignedCast(AILEmitterCtx Context, int Reg, int Size) - { - Context.EmitLdvec(Reg); - - switch (Size) - { - case 0: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorSingleToByte)); break; - case 1: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorSingleToUInt16)); break; - case 2: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorSingleToUInt32)); break; - case 3: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorSingleToUInt64)); break; - - default: throw new ArgumentOutOfRangeException(nameof(Size)); - } - } - - public static void EmitStvecWithSignedCast(AILEmitterCtx Context, int Reg, int Size) - { - switch (Size) - { - case 0: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorSByteToSingle)); break; - case 1: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorInt16ToSingle)); break; - case 2: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorInt32ToSingle)); break; - case 3: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorInt64ToSingle)); break; - - default: throw new ArgumentOutOfRangeException(nameof(Size)); - } - - Context.EmitStvec(Reg); - } - - public static void EmitStvecWithUnsignedCast(AILEmitterCtx Context, int Reg, int Size) - { - switch (Size) - { - case 0: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorByteToSingle)); break; - case 1: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorUInt16ToSingle)); break; - case 2: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorUInt32ToSingle)); break; - case 3: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorUInt64ToSingle)); break; - - default: throw new ArgumentOutOfRangeException(nameof(Size)); - } - - Context.EmitStvec(Reg); - } - - public static void EmitScalarSseOrSse2OpF(AILEmitterCtx Context, string Name) - { - EmitSseOrSse2OpF(Context, Name, true); - } - - public static void EmitVectorSseOrSse2OpF(AILEmitterCtx Context, string Name) - { - EmitSseOrSse2OpF(Context, Name, false); - } - - public static void EmitSseOrSse2OpF(AILEmitterCtx Context, string Name, bool Scalar) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - int SizeF = Op.Size & 1; - - void Ldvec(int Reg) - { - Context.EmitLdvec(Reg); - - if (SizeF == 1) - { - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorSingleToDouble)); - } - } - - Ldvec(Op.Rn); - - Type Type; - Type BaseType; - - if (SizeF == 0) - { - Type = typeof(Sse); - BaseType = typeof(Vector128); - } - else /* if (SizeF == 1) */ - { - Type = typeof(Sse2); - BaseType = typeof(Vector128); - } - - if (Op is AOpCodeSimdReg BinOp) - { - Ldvec(BinOp.Rm); - - Context.EmitCall(Type.GetMethod(Name, new Type[] { BaseType, BaseType })); - } - else - { - Context.EmitCall(Type.GetMethod(Name, new Type[] { BaseType })); - } - - if (SizeF == 1) - { - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorDoubleToSingle)); - } - - Context.EmitStvec(Op.Rd); - - if (Scalar) - { - if (SizeF == 0) - { - EmitVectorZero32_128(Context, Op.Rd); - } - else /* if (SizeF == 1) */ - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - else if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - public static void EmitUnaryMathCall(AILEmitterCtx Context, string Name) - { - IAOpCodeSimd Op = (IAOpCodeSimd)Context.CurrOp; - - int SizeF = Op.Size & 1; - - MethodInfo MthdInfo; - - if (SizeF == 0) - { - MthdInfo = typeof(MathF).GetMethod(Name, new Type[] { typeof(float) }); - } - else /* if (SizeF == 1) */ - { - MthdInfo = typeof(Math).GetMethod(Name, new Type[] { typeof(double) }); - } - - Context.EmitCall(MthdInfo); - } - - public static void EmitBinaryMathCall(AILEmitterCtx Context, string Name) - { - IAOpCodeSimd Op = (IAOpCodeSimd)Context.CurrOp; - - int SizeF = Op.Size & 1; - - MethodInfo MthdInfo; - - if (SizeF == 0) - { - MthdInfo = typeof(MathF).GetMethod(Name, new Type[] { typeof(float), typeof(float) }); - } - else /* if (SizeF == 1) */ - { - MthdInfo = typeof(Math).GetMethod(Name, new Type[] { typeof(double), typeof(double) }); - } - - Context.EmitCall(MthdInfo); - } - - public static void EmitRoundMathCall(AILEmitterCtx Context, MidpointRounding RoundMode) - { - IAOpCodeSimd Op = (IAOpCodeSimd)Context.CurrOp; - - int SizeF = Op.Size & 1; - - MethodInfo MthdInfo; - - if (SizeF == 0) - { - MthdInfo = typeof(MathF).GetMethod(nameof(MathF.Round), new Type[] { typeof(float), typeof(MidpointRounding) }); - } - else /* if (SizeF == 1) */ - { - MthdInfo = typeof(Math).GetMethod(nameof(Math.Round), new Type[] { typeof(double), typeof(MidpointRounding) }); - } - - Context.EmitLdc_I4((int)RoundMode); - - Context.EmitCall(MthdInfo); - } - - public static void EmitUnarySoftFloatCall(AILEmitterCtx Context, string Name) - { - IAOpCodeSimd Op = (IAOpCodeSimd)Context.CurrOp; - - int SizeF = Op.Size & 1; - - MethodInfo MthdInfo; - - if (SizeF == 0) - { - MthdInfo = typeof(ASoftFloat).GetMethod(Name, new Type[] { typeof(float) }); - } - else /* if (SizeF == 1) */ - { - MthdInfo = typeof(ASoftFloat).GetMethod(Name, new Type[] { typeof(double) }); - } - - Context.EmitCall(MthdInfo); - } - - public static void EmitSoftFloatCall(AILEmitterCtx Context, string Name) - { - IAOpCodeSimd Op = (IAOpCodeSimd)Context.CurrOp; - - Type Type = (Op.Size & 1) == 0 - ? typeof(ASoftFloat_32) - : typeof(ASoftFloat_64); - - Context.EmitLdarg(ATranslatedSub.StateArgIdx); - - Context.EmitCall(Type, Name); - } - - public static void EmitScalarBinaryOpByElemF(AILEmitterCtx Context, Action Emit) - { - AOpCodeSimdRegElemF Op = (AOpCodeSimdRegElemF)Context.CurrOp; - - EmitScalarOpByElemF(Context, Emit, Op.Index, Ternary: false); - } - - public static void EmitScalarTernaryOpByElemF(AILEmitterCtx Context, Action Emit) - { - AOpCodeSimdRegElemF Op = (AOpCodeSimdRegElemF)Context.CurrOp; - - EmitScalarOpByElemF(Context, Emit, Op.Index, Ternary: true); - } - - public static void EmitScalarOpByElemF(AILEmitterCtx Context, Action Emit, int Elem, bool Ternary) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - int SizeF = Op.Size & 1; - - if (Ternary) - { - EmitVectorExtractF(Context, Op.Rd, 0, SizeF); - } - - EmitVectorExtractF(Context, Op.Rn, 0, SizeF); - EmitVectorExtractF(Context, Op.Rm, Elem, SizeF); - - Emit(); - - EmitScalarSetF(Context, Op.Rd, SizeF); - } - - 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; - - bool Rd = (Opers & OperFlags.Rd) != 0; - bool Rn = (Opers & OperFlags.Rn) != 0; - bool Rm = (Opers & OperFlags.Rm) != 0; - - if (Rd) - { - EmitVectorExtract(Context, Op.Rd, 0, Op.Size, Signed); - } - - if (Rn) - { - EmitVectorExtract(Context, Op.Rn, 0, Op.Size, Signed); - } - - if (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; - - bool Ra = (Opers & OperFlags.Ra) != 0; - bool Rn = (Opers & OperFlags.Rn) != 0; - bool Rm = (Opers & OperFlags.Rm) != 0; - - if (Ra) - { - EmitVectorExtractF(Context, ((AOpCodeSimdReg)Op).Ra, 0, SizeF); - } - - if (Rn) - { - EmitVectorExtractF(Context, Op.Rn, 0, SizeF); - } - - if (Rm) - { - EmitVectorExtractF(Context, ((AOpCodeSimdReg)Op).Rm, 0, SizeF); - } - - Emit(); - - EmitScalarSetF(Context, Op.Rd, SizeF); - } - - public static void EmitVectorUnaryOpF(AILEmitterCtx Context, Action Emit) - { - EmitVectorOpF(Context, Emit, OperFlags.Rn); - } - - 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 EmitVectorOpF(AILEmitterCtx Context, Action Emit, OperFlags Opers) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - int SizeF = Op.Size & 1; - - int Bytes = Op.GetBitsCount() >> 3; - int Elems = Bytes >> SizeF + 2; - - bool Rd = (Opers & OperFlags.Rd) != 0; - bool Rn = (Opers & OperFlags.Rn) != 0; - bool Rm = (Opers & OperFlags.Rm) != 0; - - for (int Index = 0; Index < Elems; Index++) - { - if (Rd) - { - EmitVectorExtractF(Context, Op.Rd, Index, SizeF); - } - - if (Rn) - { - EmitVectorExtractF(Context, Op.Rn, Index, SizeF); - } - - if (Rm) - { - EmitVectorExtractF(Context, ((AOpCodeSimdReg)Op).Rm, Index, SizeF); - } - - Emit(); - - EmitVectorInsertF(Context, Op.Rd, Index, SizeF); - } - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - public static void EmitVectorBinaryOpByElemF(AILEmitterCtx Context, Action Emit) - { - AOpCodeSimdRegElemF Op = (AOpCodeSimdRegElemF)Context.CurrOp; - - EmitVectorOpByElemF(Context, Emit, Op.Index, Ternary: false); - } - - public static void EmitVectorTernaryOpByElemF(AILEmitterCtx Context, Action Emit) - { - AOpCodeSimdRegElemF Op = (AOpCodeSimdRegElemF)Context.CurrOp; - - EmitVectorOpByElemF(Context, Emit, Op.Index, Ternary: true); - } - - public static void EmitVectorOpByElemF(AILEmitterCtx Context, Action Emit, int Elem, bool Ternary) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - int SizeF = Op.Size & 1; - - int Bytes = Op.GetBitsCount() >> 3; - int Elems = Bytes >> SizeF + 2; - - for (int Index = 0; Index < Elems; 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 EmitVectorTernaryOpSx(AILEmitterCtx Context, Action Emit) - { - EmitVectorOp(Context, Emit, OperFlags.RdRnRm, 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 = Op.GetBitsCount() >> 3; - int Elems = Bytes >> Op.Size; - - bool Rd = (Opers & OperFlags.Rd) != 0; - bool Rn = (Opers & OperFlags.Rn) != 0; - bool Rm = (Opers & OperFlags.Rm) != 0; - - for (int Index = 0; Index < Elems; Index++) - { - if (Rd) - { - EmitVectorExtract(Context, Op.Rd, Index, Op.Size, Signed); - } - - if (Rn) - { - EmitVectorExtract(Context, Op.Rn, Index, Op.Size, Signed); - } - - if (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 EmitVectorBinaryOpByElemSx(AILEmitterCtx Context, Action Emit) - { - AOpCodeSimdRegElem Op = (AOpCodeSimdRegElem)Context.CurrOp; - - EmitVectorOpByElem(Context, Emit, Op.Index, false, true); - } - - public static void EmitVectorBinaryOpByElemZx(AILEmitterCtx Context, Action Emit) - { - AOpCodeSimdRegElem Op = (AOpCodeSimdRegElem)Context.CurrOp; - - EmitVectorOpByElem(Context, Emit, Op.Index, false, false); - } - - public static void EmitVectorTernaryOpByElemZx(AILEmitterCtx Context, Action Emit) - { - AOpCodeSimdRegElem Op = (AOpCodeSimdRegElem)Context.CurrOp; - - EmitVectorOpByElem(Context, Emit, Op.Index, true, false); - } - - public static void EmitVectorOpByElem(AILEmitterCtx Context, Action Emit, int Elem, bool Ternary, bool Signed) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - int Bytes = Op.GetBitsCount() >> 3; - int Elems = Bytes >> Op.Size; - - EmitVectorExtract(Context, Op.Rm, Elem, Op.Size, Signed); - Context.EmitSttmp(); - - for (int Index = 0; Index < Elems; Index++) - { - if (Ternary) - { - EmitVectorExtract(Context, Op.Rd, Index, Op.Size, Signed); - } - - EmitVectorExtract(Context, Op.Rn, Index, Op.Size, Signed); - Context.EmitLdtmp(); - - Emit(); - - EmitVectorInsertTmp(Context, Index, Op.Size); - } - - Context.EmitLdvectmp(); - Context.EmitStvec(Op.Rd); - - 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 = Op.GetBitsCount() >> 3; - int Elems = Bytes >> Op.Size; - - for (int Index = 0; Index < Elems; 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) - { - EmitVectorWidenRnRmOp(Context, Emit, false, true); - } - - public static void EmitVectorWidenRnRmBinaryOpZx(AILEmitterCtx Context, Action Emit) - { - EmitVectorWidenRnRmOp(Context, Emit, false, false); - } - - public static void EmitVectorWidenRnRmTernaryOpSx(AILEmitterCtx Context, Action Emit) - { - EmitVectorWidenRnRmOp(Context, Emit, true, true); - } - - public static void EmitVectorWidenRnRmTernaryOpZx(AILEmitterCtx Context, Action Emit) - { - EmitVectorWidenRnRmOp(Context, Emit, true, false); - } - - public static void EmitVectorWidenRnRmOp(AILEmitterCtx Context, Action Emit, bool Ternary, 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++) - { - if (Ternary) - { - EmitVectorExtract(Context, Op.Rd, Index, Op.Size + 1, Signed); - } - - 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 EmitVectorPairwiseOpSx(AILEmitterCtx Context, Action Emit) - { - EmitVectorPairwiseOp(Context, Emit, true); - } - - public static void EmitVectorPairwiseOpZx(AILEmitterCtx Context, Action Emit) - { - EmitVectorPairwiseOp(Context, Emit, false); - } - - public static void EmitVectorPairwiseOp(AILEmitterCtx Context, Action Emit, bool Signed) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - int Words = Op.GetBitsCount() >> 4; - int Pairs = Words >> Op.Size; - - for (int Index = 0; Index < Pairs; Index++) - { - int Idx = Index << 1; - - EmitVectorExtract(Context, Op.Rn, Idx, Op.Size, Signed); - EmitVectorExtract(Context, Op.Rn, Idx + 1, Op.Size, Signed); - - Emit(); - - EmitVectorExtract(Context, Op.Rm, Idx, Op.Size, Signed); - EmitVectorExtract(Context, Op.Rm, Idx + 1, Op.Size, Signed); - - Emit(); - - EmitVectorInsertTmp(Context, Pairs + Index, Op.Size); - EmitVectorInsertTmp(Context, Index, Op.Size); - } - - Context.EmitLdvectmp(); - Context.EmitStvec(Op.Rd); - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - public static void EmitVectorPairwiseOpF(AILEmitterCtx Context, Action Emit) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - int SizeF = Op.Size & 1; - - int Words = Op.GetBitsCount() >> 4; - int Pairs = Words >> SizeF + 2; - - for (int Index = 0; Index < Pairs; Index++) - { - int Idx = Index << 1; - - EmitVectorExtractF(Context, Op.Rn, Idx, SizeF); - EmitVectorExtractF(Context, Op.Rn, Idx + 1, SizeF); - - Emit(); - - EmitVectorExtractF(Context, Op.Rm, Idx, SizeF); - EmitVectorExtractF(Context, Op.Rm, Idx + 1, SizeF); - - Emit(); - - EmitVectorInsertTmpF(Context, Pairs + Index, SizeF); - EmitVectorInsertTmpF(Context, Index, SizeF); - } - - Context.EmitLdvectmp(); - Context.EmitStvec(Op.Rd); - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - [Flags] - public enum SaturatingFlags - { - Scalar = 1 << 0, - Signed = 1 << 1, - - Add = 1 << 2, - Sub = 1 << 3, - - Accumulate = 1 << 4, - - ScalarSx = Scalar | Signed, - ScalarZx = Scalar, - - VectorSx = Signed, - VectorZx = 0 - } - - public static void EmitScalarSaturatingUnaryOpSx(AILEmitterCtx Context, Action Emit) - { - EmitSaturatingUnaryOpSx(Context, Emit, SaturatingFlags.ScalarSx); - } - - public static void EmitVectorSaturatingUnaryOpSx(AILEmitterCtx Context, Action Emit) - { - EmitSaturatingUnaryOpSx(Context, Emit, SaturatingFlags.VectorSx); - } - - public static void EmitSaturatingUnaryOpSx(AILEmitterCtx Context, Action Emit, SaturatingFlags Flags) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - bool Scalar = (Flags & SaturatingFlags.Scalar) != 0; - - int Bytes = Op.GetBitsCount() >> 3; - int Elems = !Scalar ? Bytes >> Op.Size : 1; - - if (Scalar) - { - EmitVectorZeroLowerTmp(Context); - } - - for (int Index = 0; Index < Elems; Index++) - { - EmitVectorExtractSx(Context, Op.Rn, Index, Op.Size); - - Emit(); - - if (Op.Size <= 2) - { - EmitSatQ(Context, Op.Size, true, true); - } - else /* if (Op.Size == 3) */ - { - EmitUnarySignedSatQAbsOrNeg(Context); - } - - EmitVectorInsertTmp(Context, Index, Op.Size); - } - - Context.EmitLdvectmp(); - Context.EmitStvec(Op.Rd); - - if ((Op.RegisterSize == ARegisterSize.SIMD64) || Scalar) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - public static void EmitScalarSaturatingBinaryOpSx(AILEmitterCtx Context, SaturatingFlags Flags) - { - EmitSaturatingBinaryOp(Context, () => { }, SaturatingFlags.ScalarSx | Flags); - } - - public static void EmitScalarSaturatingBinaryOpZx(AILEmitterCtx Context, SaturatingFlags Flags) - { - EmitSaturatingBinaryOp(Context, () => { }, SaturatingFlags.ScalarZx | Flags); - } - - public static void EmitVectorSaturatingBinaryOpSx(AILEmitterCtx Context, SaturatingFlags Flags) - { - EmitSaturatingBinaryOp(Context, () => { }, SaturatingFlags.VectorSx | Flags); - } - - public static void EmitVectorSaturatingBinaryOpZx(AILEmitterCtx Context, SaturatingFlags Flags) - { - EmitSaturatingBinaryOp(Context, () => { }, SaturatingFlags.VectorZx | Flags); - } - - public static void EmitSaturatingBinaryOp(AILEmitterCtx Context, Action Emit, SaturatingFlags Flags) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - bool Scalar = (Flags & SaturatingFlags.Scalar) != 0; - bool Signed = (Flags & SaturatingFlags.Signed) != 0; - - bool Add = (Flags & SaturatingFlags.Add) != 0; - bool Sub = (Flags & SaturatingFlags.Sub) != 0; - - bool Accumulate = (Flags & SaturatingFlags.Accumulate) != 0; - - int Bytes = Op.GetBitsCount() >> 3; - int Elems = !Scalar ? Bytes >> Op.Size : 1; - - if (Scalar) - { - EmitVectorZeroLowerTmp(Context); - } - - if (Add || Sub) - { - for (int Index = 0; Index < Elems; Index++) - { - EmitVectorExtract(Context, Op.Rn, Index, Op.Size, Signed); - EmitVectorExtract(Context, ((AOpCodeSimdReg)Op).Rm, Index, Op.Size, Signed); - - if (Op.Size <= 2) - { - Context.Emit(Add ? OpCodes.Add : OpCodes.Sub); - - EmitSatQ(Context, Op.Size, true, Signed); - } - else /* if (Op.Size == 3) */ - { - if (Add) - { - EmitBinarySatQAdd(Context, Signed); - } - else /* if (Sub) */ - { - EmitBinarySatQSub(Context, Signed); - } - } - - EmitVectorInsertTmp(Context, Index, Op.Size); - } - } - else if (Accumulate) - { - for (int Index = 0; Index < Elems; Index++) - { - EmitVectorExtract(Context, Op.Rn, Index, Op.Size, !Signed); - EmitVectorExtract(Context, Op.Rd, Index, Op.Size, Signed); - - if (Op.Size <= 2) - { - Context.Emit(OpCodes.Add); - - EmitSatQ(Context, Op.Size, true, Signed); - } - else /* if (Op.Size == 3) */ - { - EmitBinarySatQAccumulate(Context, Signed); - } - - EmitVectorInsertTmp(Context, Index, Op.Size); - } - } - else - { - for (int Index = 0; Index < Elems; Index++) - { - EmitVectorExtract(Context, Op.Rn, Index, Op.Size, Signed); - EmitVectorExtract(Context, ((AOpCodeSimdReg)Op).Rm, Index, Op.Size, Signed); - - Emit(); - - EmitSatQ(Context, Op.Size, true, Signed); - - EmitVectorInsertTmp(Context, Index, Op.Size); - } - } - - Context.EmitLdvectmp(); - Context.EmitStvec(Op.Rd); - - if ((Op.RegisterSize == ARegisterSize.SIMD64) || Scalar) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - [Flags] - public enum SaturatingNarrowFlags - { - Scalar = 1 << 0, - SignedSrc = 1 << 1, - SignedDst = 1 << 2, - - ScalarSxSx = Scalar | SignedSrc | SignedDst, - ScalarSxZx = Scalar | SignedSrc, - ScalarZxZx = Scalar, - - VectorSxSx = SignedSrc | SignedDst, - VectorSxZx = SignedSrc, - VectorZxZx = 0 - } - - public static void EmitSaturatingNarrowOp(AILEmitterCtx Context, SaturatingNarrowFlags Flags) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - bool Scalar = (Flags & SaturatingNarrowFlags.Scalar) != 0; - bool SignedSrc = (Flags & SaturatingNarrowFlags.SignedSrc) != 0; - bool SignedDst = (Flags & SaturatingNarrowFlags.SignedDst) != 0; - - int Elems = !Scalar ? 8 >> Op.Size : 1; - - int Part = !Scalar && (Op.RegisterSize == ARegisterSize.SIMD128) ? Elems : 0; - - if (Scalar) - { - EmitVectorZeroLowerTmp(Context); - } - - if (Part != 0) - { - Context.EmitLdvec(Op.Rd); - Context.EmitStvectmp(); - } - - for (int Index = 0; Index < Elems; Index++) - { - EmitVectorExtract(Context, Op.Rn, Index, Op.Size + 1, SignedSrc); - - EmitSatQ(Context, Op.Size, SignedSrc, SignedDst); - - EmitVectorInsertTmp(Context, Part + Index, Op.Size); - } - - Context.EmitLdvectmp(); - Context.EmitStvec(Op.Rd); - - if (Part == 0) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - // TSrc (16bit, 32bit, 64bit; signed, unsigned) > TDst (8bit, 16bit, 32bit; signed, unsigned). - public static void EmitSatQ( - AILEmitterCtx Context, - int SizeDst, - bool SignedSrc, - bool SignedDst) - { - if (SizeDst > 2) - { - throw new ArgumentOutOfRangeException(nameof(SizeDst)); - } - - Context.EmitLdc_I4(SizeDst); - Context.EmitLdarg(ATranslatedSub.StateArgIdx); - - if (SignedSrc) - { - ASoftFallback.EmitCall(Context, SignedDst - ? nameof(ASoftFallback.SignedSrcSignedDstSatQ) - : nameof(ASoftFallback.SignedSrcUnsignedDstSatQ)); - } - else - { - ASoftFallback.EmitCall(Context, SignedDst - ? nameof(ASoftFallback.UnsignedSrcSignedDstSatQ) - : nameof(ASoftFallback.UnsignedSrcUnsignedDstSatQ)); - } - } - - // TSrc (64bit) == TDst (64bit); signed. - public static void EmitUnarySignedSatQAbsOrNeg(AILEmitterCtx Context) - { - if (((AOpCodeSimd)Context.CurrOp).Size < 3) - { - throw new InvalidOperationException(); - } - - Context.EmitLdarg(ATranslatedSub.StateArgIdx); - - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.UnarySignedSatQAbsOrNeg)); - } - - // TSrcs (64bit) == TDst (64bit); signed, unsigned. - public static void EmitBinarySatQAdd(AILEmitterCtx Context, bool Signed) - { - if (((AOpCodeSimdReg)Context.CurrOp).Size < 3) - { - throw new InvalidOperationException(); - } - - Context.EmitLdarg(ATranslatedSub.StateArgIdx); - - ASoftFallback.EmitCall(Context, Signed - ? nameof(ASoftFallback.BinarySignedSatQAdd) - : nameof(ASoftFallback.BinaryUnsignedSatQAdd)); - } - - // TSrcs (64bit) == TDst (64bit); signed, unsigned. - public static void EmitBinarySatQSub(AILEmitterCtx Context, bool Signed) - { - if (((AOpCodeSimdReg)Context.CurrOp).Size < 3) - { - throw new InvalidOperationException(); - } - - Context.EmitLdarg(ATranslatedSub.StateArgIdx); - - ASoftFallback.EmitCall(Context, Signed - ? nameof(ASoftFallback.BinarySignedSatQSub) - : nameof(ASoftFallback.BinaryUnsignedSatQSub)); - } - - // TSrcs (64bit) == TDst (64bit); signed, unsigned. - public static void EmitBinarySatQAccumulate(AILEmitterCtx Context, bool Signed) - { - if (((AOpCodeSimd)Context.CurrOp).Size < 3) - { - throw new InvalidOperationException(); - } - - Context.EmitLdarg(ATranslatedSub.StateArgIdx); - - ASoftFallback.EmitCall(Context, Signed - ? nameof(ASoftFallback.BinarySignedSatQAcc) - : nameof(ASoftFallback.BinaryUnsignedSatQAcc)); - } - - public static void 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) - { - if (AOptimizations.UseSse41 && Size == 0) - { - //If the type is float, we can perform insertion and - //zero the upper bits with a single instruction (INSERTPS); - Context.EmitLdvec(Reg); - - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.Sse41VectorInsertScalarSingle)); - - Context.EmitStvec(Reg); - } - else - { - 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) - { - ThrowIfInvalid(Index, Size); - - Context.EmitLdvec(Reg); - Context.EmitLdc_I4(Index); - Context.EmitLdc_I4(Size); - - AVectorHelper.EmitCall(Context, Signed - ? nameof(AVectorHelper.VectorExtractIntSx) - : nameof(AVectorHelper.VectorExtractIntZx)); - } - - public static void EmitVectorExtractF(AILEmitterCtx Context, int Reg, int Index, int Size) - { - ThrowIfInvalidF(Index, Size); - - Context.EmitLdvec(Reg); - Context.EmitLdc_I4(Index); - - if (Size == 0) - { - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorExtractSingle)); - } - else if (Size == 1) - { - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorExtractDouble)); - } - else - { - throw new ArgumentOutOfRangeException(nameof(Size)); - } - } - - public static void EmitVectorZeroAll(AILEmitterCtx Context, int Rd) - { - if (AOptimizations.UseSse2) - { - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorSingleZero)); - - Context.EmitStvec(Rd); - } - else - { - EmitVectorZeroLower(Context, Rd); - EmitVectorZeroUpper(Context, Rd); - } - } - - public static void EmitVectorZeroLower(AILEmitterCtx Context, int Rd) - { - EmitVectorInsert(Context, Rd, 0, 3, 0); - } - - public static void EmitVectorZeroLowerTmp(AILEmitterCtx Context) - { - EmitVectorInsertTmp(Context, 0, 3, 0); - } - - public static void EmitVectorZeroUpper(AILEmitterCtx Context, int Reg) - { - if (AOptimizations.UseSse2) - { - //TODO: Use MoveScalar once it is fixed, as of the - //time of writing it just crashes the JIT. - EmitLdvecWithUnsignedCast(Context, Reg, 3); - - Type[] Types = new Type[] { typeof(Vector128), typeof(byte) }; - - //Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.MoveScalar), Types)); - - Context.EmitLdc_I4(8); - - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftLeftLogical128BitLane), Types)); - - Context.EmitLdc_I4(8); - - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), Types)); - - EmitStvecWithUnsignedCast(Context, Reg, 3); - } - else - { - EmitVectorInsert(Context, Reg, 1, 3, 0); - } - } - - public static void EmitVectorZero32_128(AILEmitterCtx Context, int Reg) - { - Context.EmitLdvec(Reg); - - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorZero32_128)); - - Context.EmitStvec(Reg); - } - - public static void EmitVectorInsert(AILEmitterCtx Context, int Reg, int Index, int Size) - { - ThrowIfInvalid(Index, Size); - - Context.EmitLdvec(Reg); - Context.EmitLdc_I4(Index); - Context.EmitLdc_I4(Size); - - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorInsertInt)); - - Context.EmitStvec(Reg); - } - - public static void EmitVectorInsertTmp(AILEmitterCtx Context, int Index, int Size) - { - ThrowIfInvalid(Index, Size); - - Context.EmitLdvectmp(); - Context.EmitLdc_I4(Index); - Context.EmitLdc_I4(Size); - - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorInsertInt)); - - Context.EmitStvectmp(); - } - - public static void EmitVectorInsert(AILEmitterCtx Context, int Reg, int Index, int Size, long Value) - { - ThrowIfInvalid(Index, Size); - - Context.EmitLdc_I8(Value); - Context.EmitLdvec(Reg); - Context.EmitLdc_I4(Index); - Context.EmitLdc_I4(Size); - - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorInsertInt)); - - Context.EmitStvec(Reg); - } - - public static void EmitVectorInsertTmp(AILEmitterCtx Context, int Index, int Size, long Value) - { - ThrowIfInvalid(Index, Size); - - Context.EmitLdc_I8(Value); - Context.EmitLdvectmp(); - Context.EmitLdc_I4(Index); - Context.EmitLdc_I4(Size); - - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorInsertInt)); - - Context.EmitStvectmp(); - } - - public static void EmitVectorInsertF(AILEmitterCtx Context, int Reg, int Index, int Size) - { - ThrowIfInvalidF(Index, Size); - - Context.EmitLdvec(Reg); - Context.EmitLdc_I4(Index); - - if (Size == 0) - { - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorInsertSingle)); - } - else if (Size == 1) - { - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorInsertDouble)); - } - else - { - throw new ArgumentOutOfRangeException(nameof(Size)); - } - - Context.EmitStvec(Reg); - } - - public static void EmitVectorInsertTmpF(AILEmitterCtx Context, int Index, int Size) - { - ThrowIfInvalidF(Index, Size); - - Context.EmitLdvectmp(); - Context.EmitLdc_I4(Index); - - if (Size == 0) - { - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorInsertSingle)); - } - else if (Size == 1) - { - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorInsertDouble)); - } - else - { - throw new ArgumentOutOfRangeException(nameof(Size)); - } - - Context.EmitStvectmp(); - } - - private static void ThrowIfInvalid(int Index, int Size) - { - if ((uint)Size > 3u) - { - throw new ArgumentOutOfRangeException(nameof(Size)); - } - - if ((uint)Index >= 16u >> Size) - { - throw new ArgumentOutOfRangeException(nameof(Index)); - } - } - - private static void ThrowIfInvalidF(int Index, int Size) - { - if ((uint)Size > 1u) - { - throw new ArgumentOutOfRangeException(nameof(Size)); - } - - if ((uint)Index >= 4u >> Size) - { - throw new ArgumentOutOfRangeException(nameof(Index)); - } - } - } -} diff --git a/Instruction/AInstEmitSimdLogical.cs b/Instruction/AInstEmitSimdLogical.cs deleted file mode 100644 index 1aa8981..0000000 --- a/Instruction/AInstEmitSimdLogical.cs +++ /dev/null @@ -1,311 +0,0 @@ -using ChocolArm64.Decoder; -using ChocolArm64.State; -using ChocolArm64.Translation; -using System; -using System.Reflection.Emit; -using System.Runtime.Intrinsics.X86; - -using static ChocolArm64.Instruction.AInstEmitSimdHelper; - -namespace ChocolArm64.Instruction -{ - static partial class AInstEmit - { - public static void And_V(AILEmitterCtx Context) - { - if (AOptimizations.UseSse2) - { - EmitSse2Op(Context, nameof(Sse2.And)); - } - else - { - EmitVectorBinaryOpZx(Context, () => Context.Emit(OpCodes.And)); - } - } - - public static void Bic_V(AILEmitterCtx Context) - { - if (AOptimizations.UseSse2) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - EmitLdvecWithUnsignedCast(Context, Op.Rm, Op.Size); - EmitLdvecWithUnsignedCast(Context, Op.Rn, Op.Size); - - Type[] Types = new Type[] - { - VectorUIntTypesPerSizeLog2[Op.Size], - VectorUIntTypesPerSizeLog2[Op.Size] - }; - - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.AndNot), Types)); - - EmitStvecWithUnsignedCast(Context, Op.Rd, Op.Size); - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - else - { - 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 Bif_V(AILEmitterCtx Context) - { - EmitBitBif(Context, true); - } - - public static void Bit_V(AILEmitterCtx Context) - { - EmitBitBif(Context, false); - } - - private static void EmitBitBif(AILEmitterCtx Context, bool NotRm) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - if (AOptimizations.UseSse2) - { - Type[] Types = new Type[] - { - VectorUIntTypesPerSizeLog2[Op.Size], - VectorUIntTypesPerSizeLog2[Op.Size] - }; - - EmitLdvecWithUnsignedCast(Context, Op.Rm, Op.Size); - EmitLdvecWithUnsignedCast(Context, Op.Rd, Op.Size); - EmitLdvecWithUnsignedCast(Context, Op.Rn, Op.Size); - - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Xor), Types)); - - string Name = NotRm ? nameof(Sse2.AndNot) : nameof(Sse2.And); - - Context.EmitCall(typeof(Sse2).GetMethod(Name, Types)); - - EmitLdvecWithUnsignedCast(Context, Op.Rd, Op.Size); - - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Xor), Types)); - - EmitStvecWithUnsignedCast(Context, Op.Rd, Op.Size); - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - else - { - int Bytes = Op.GetBitsCount() >> 3; - int Elems = Bytes >> Op.Size; - - for (int Index = 0; Index < Elems; Index++) - { - EmitVectorExtractZx(Context, Op.Rd, Index, Op.Size); - EmitVectorExtractZx(Context, Op.Rn, Index, Op.Size); - - Context.Emit(OpCodes.Xor); - - EmitVectorExtractZx(Context, Op.Rm, Index, Op.Size); - - if (NotRm) - { - Context.Emit(OpCodes.Not); - } - - Context.Emit(OpCodes.And); - - EmitVectorExtractZx(Context, Op.Rd, Index, Op.Size); - - Context.Emit(OpCodes.Xor); - - EmitVectorInsert(Context, Op.Rd, Index, Op.Size); - } - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - } - - public static void Bsl_V(AILEmitterCtx Context) - { - if (AOptimizations.UseSse2) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - Type[] Types = new Type[] - { - VectorUIntTypesPerSizeLog2[Op.Size], - VectorUIntTypesPerSizeLog2[Op.Size] - }; - - EmitLdvecWithUnsignedCast(Context, Op.Rn, Op.Size); - EmitLdvecWithUnsignedCast(Context, Op.Rm, Op.Size); - - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Xor), Types)); - - EmitLdvecWithUnsignedCast(Context, Op.Rd, Op.Size); - - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.And), Types)); - - EmitLdvecWithUnsignedCast(Context, Op.Rm, Op.Size); - - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Xor), Types)); - - EmitStvecWithUnsignedCast(Context, Op.Rd, Op.Size); - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - else - { - 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) - { - if (AOptimizations.UseSse2) - { - EmitSse2Op(Context, nameof(Sse2.Xor)); - } - else - { - EmitVectorBinaryOpZx(Context, () => Context.Emit(OpCodes.Xor)); - } - } - - public static void Not_V(AILEmitterCtx Context) - { - EmitVectorUnaryOpZx(Context, () => Context.Emit(OpCodes.Not)); - } - - public static void Orn_V(AILEmitterCtx Context) - { - EmitVectorBinaryOpZx(Context, () => - { - Context.Emit(OpCodes.Not); - Context.Emit(OpCodes.Or); - }); - } - - public static void Orr_V(AILEmitterCtx Context) - { - if (AOptimizations.UseSse2) - { - EmitSse2Op(Context, nameof(Sse2.Or)); - } - else - { - EmitVectorBinaryOpZx(Context, () => Context.Emit(OpCodes.Or)); - } - } - - public static void Orr_Vi(AILEmitterCtx Context) - { - EmitVectorImmBinaryOp(Context, () => Context.Emit(OpCodes.Or)); - } - - public static void Rbit_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_U4); - - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.ReverseBits8)); - - Context.Emit(OpCodes.Conv_U8); - - EmitVectorInsert(Context, Op.Rd, Index, 0); - } - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - public static void Rev16_V(AILEmitterCtx Context) - { - EmitRev_V(Context, ContainerSize: 1); - } - - public static void Rev32_V(AILEmitterCtx Context) - { - EmitRev_V(Context, ContainerSize: 2); - } - - public static void Rev64_V(AILEmitterCtx Context) - { - EmitRev_V(Context, ContainerSize: 3); - } - - private static void EmitRev_V(AILEmitterCtx Context, int ContainerSize) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - if (Op.Size >= ContainerSize) - { - throw new InvalidOperationException(); - } - - int Bytes = Op.GetBitsCount() >> 3; - int Elems = Bytes >> Op.Size; - - int ContainerMask = (1 << (ContainerSize - Op.Size)) - 1; - - for (int Index = 0; Index < Elems; Index++) - { - int RevIndex = Index ^ ContainerMask; - - EmitVectorExtractZx(Context, Op.Rn, RevIndex, Op.Size); - - EmitVectorInsertTmp(Context, Index, Op.Size); - } - - Context.EmitLdvectmp(); - Context.EmitStvec(Op.Rd); - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - } -} diff --git a/Instruction/AInstEmitSimdMemory.cs b/Instruction/AInstEmitSimdMemory.cs deleted file mode 100644 index 368b014..0000000 --- a/Instruction/AInstEmitSimdMemory.cs +++ /dev/null @@ -1,185 +0,0 @@ -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 = Op.GetBitsCount() >> 3; - int Elems = Bytes >> Op.Size; - - for (int SElem = 0; SElem < Op.SElems; SElem++) - { - int Rt = (Op.Rt + SElem) & 0x1f; - - for (int Index = 0; Index < Elems; 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/Instruction/AInstEmitSimdMove.cs b/Instruction/AInstEmitSimdMove.cs deleted file mode 100644 index 6001f48..0000000 --- a/Instruction/AInstEmitSimdMove.cs +++ /dev/null @@ -1,562 +0,0 @@ -using ChocolArm64.Decoder; -using ChocolArm64.State; -using ChocolArm64.Translation; -using System; -using System.Reflection.Emit; -using System.Runtime.Intrinsics.X86; - -using static ChocolArm64.Instruction.AInstEmitSimdHelper; - -namespace ChocolArm64.Instruction -{ - static partial class AInstEmit - { - public static void Dup_Gp(AILEmitterCtx Context) - { - AOpCodeSimdIns Op = (AOpCodeSimdIns)Context.CurrOp; - - if (AOptimizations.UseSse2) - { - Context.EmitLdintzr(Op.Rn); - - switch (Op.Size) - { - case 0: Context.Emit(OpCodes.Conv_U1); break; - case 1: Context.Emit(OpCodes.Conv_U2); break; - case 2: Context.Emit(OpCodes.Conv_U4); break; - } - - Type[] Types = new Type[] { UIntTypesPerSizeLog2[Op.Size] }; - - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetAllVector128), Types)); - - EmitStvecWithUnsignedCast(Context, Op.Rd, Op.Size); - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - else - { - int Bytes = Op.GetBitsCount() >> 3; - int Elems = Bytes >> Op.Size; - - for (int Index = 0; Index < Elems; 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 = Op.GetBitsCount() >> 3; - int Elems = Bytes >> Op.Size; - - for (int Index = 0; Index < Elems; 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 Ext_V(AILEmitterCtx Context) - { - AOpCodeSimdExt Op = (AOpCodeSimdExt)Context.CurrOp; - - Context.EmitLdvec(Op.Rd); - Context.EmitStvectmp(); - - int Bytes = Op.GetBitsCount() >> 3; - - int Position = Op.Imm4; - - for (int Index = 0; Index < Bytes; Index++) - { - int Reg = Op.Imm4 + Index < Bytes ? Op.Rn : Op.Rm; - - if (Position == Bytes) - { - Position = 0; - } - - EmitVectorExtractZx(Context, Reg, Position++, 0); - EmitVectorInsertTmp(Context, Index, 0); - } - - Context.EmitLdvectmp(); - Context.EmitStvec(Op.Rd); - - 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); - - EmitIntZeroUpperIfNeeded(Context); - - Context.EmitStintzr(Op.Rd); - } - - public static void Fmov_Ftoi1(AILEmitterCtx Context) - { - AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp; - - EmitVectorExtractZx(Context, Op.Rn, 1, 3); - - EmitIntZeroUpperIfNeeded(Context); - - Context.EmitStintzr(Op.Rd); - } - - public static void Fmov_Itof(AILEmitterCtx Context) - { - AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp; - - Context.EmitLdintzr(Op.Rn); - - EmitIntZeroUpperIfNeeded(Context); - - EmitScalarSet(Context, Op.Rd, 3); - } - - public static void Fmov_Itof1(AILEmitterCtx Context) - { - AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp; - - Context.EmitLdintzr(Op.Rn); - - EmitIntZeroUpperIfNeeded(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 Smov_S(AILEmitterCtx Context) - { - AOpCodeSimdIns Op = (AOpCodeSimdIns)Context.CurrOp; - - EmitVectorExtractSx(Context, Op.Rn, Op.DstIndex, Op.Size); - - EmitIntZeroUpperIfNeeded(Context); - - Context.EmitStintzr(Op.Rd); - } - - 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: AVectorHelper.EmitCall(Context, - nameof(AVectorHelper.Tbl1_V64), - nameof(AVectorHelper.Tbl1_V128)); break; - - case 2: AVectorHelper.EmitCall(Context, - nameof(AVectorHelper.Tbl2_V64), - nameof(AVectorHelper.Tbl2_V128)); break; - - case 3: AVectorHelper.EmitCall(Context, - nameof(AVectorHelper.Tbl3_V64), - nameof(AVectorHelper.Tbl3_V128)); break; - - case 4: AVectorHelper.EmitCall(Context, - nameof(AVectorHelper.Tbl4_V64), - nameof(AVectorHelper.Tbl4_V128)); break; - - default: throw new InvalidOperationException(); - } - - Context.EmitStvec(Op.Rd); - } - - public static void Trn1_V(AILEmitterCtx Context) - { - EmitVectorTranspose(Context, Part: 0); - } - - public static void Trn2_V(AILEmitterCtx Context) - { - EmitVectorTranspose(Context, Part: 1); - } - - 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; - - if (AOptimizations.UseSse41 && Op.Size < 2) - { - void EmitZeroVector() - { - switch (Op.Size) - { - case 0: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorInt16Zero)); break; - case 1: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorInt32Zero)); break; - } - } - - //For XTN, first operand is source, second operand is 0. - //For XTN2, first operand is 0, second operand is source. - if (Part != 0) - { - EmitZeroVector(); - } - - EmitLdvecWithSignedCast(Context, Op.Rn, Op.Size + 1); - - //Set mask to discard the upper half of the wide elements. - switch (Op.Size) - { - case 0: Context.EmitLdc_I4(0x00ff); break; - case 1: Context.EmitLdc_I4(0x0000ffff); break; - } - - Type WideType = IntTypesPerSizeLog2[Op.Size + 1]; - - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetAllVector128), new Type[] { WideType })); - - WideType = VectorIntTypesPerSizeLog2[Op.Size + 1]; - - Type[] WideTypes = new Type[] { WideType, WideType }; - - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.And), WideTypes)); - - if (Part == 0) - { - EmitZeroVector(); - } - - //Pack values with signed saturation, the signed saturation shouldn't - //saturate anything since the upper bits were masked off. - Type SseType = Op.Size == 0 ? typeof(Sse2) : typeof(Sse41); - - Context.EmitCall(SseType.GetMethod(nameof(Sse2.PackUnsignedSaturate), WideTypes)); - - if (Part != 0) - { - //For XTN2, we additionally need to discard the upper bits - //of the target register and OR the result with it. - EmitVectorZeroUpper(Context, Op.Rd); - - EmitLdvecWithUnsignedCast(Context, Op.Rd, Op.Size); - - Type NarrowType = VectorUIntTypesPerSizeLog2[Op.Size]; - - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Or), new Type[] { NarrowType, NarrowType })); - } - - EmitStvecWithUnsignedCast(Context, Op.Rd, Op.Size); - } - else - { - if (Part != 0) - { - Context.EmitLdvec(Op.Rd); - Context.EmitStvectmp(); - } - - for (int Index = 0; Index < Elems; Index++) - { - EmitVectorExtractZx(Context, Op.Rn, Index, Op.Size + 1); - - EmitVectorInsertTmp(Context, Part + Index, Op.Size); - } - - Context.EmitLdvectmp(); - Context.EmitStvec(Op.Rd); - - 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 EmitIntZeroUpperIfNeeded(AILEmitterCtx Context) - { - if (Context.CurrOp.RegisterSize == ARegisterSize.Int32 || - Context.CurrOp.RegisterSize == ARegisterSize.SIMD64) - { - Context.Emit(OpCodes.Conv_U4); - Context.Emit(OpCodes.Conv_U8); - } - } - - private static void EmitVectorTranspose(AILEmitterCtx Context, int Part) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - int Words = Op.GetBitsCount() >> 4; - int Pairs = Words >> Op.Size; - - for (int Index = 0; Index < Pairs; Index++) - { - int Idx = Index << 1; - - EmitVectorExtractZx(Context, Op.Rn, Idx + Part, Op.Size); - EmitVectorExtractZx(Context, Op.Rm, Idx + Part, Op.Size); - - EmitVectorInsertTmp(Context, Idx + 1, Op.Size); - EmitVectorInsertTmp(Context, Idx, Op.Size); - } - - Context.EmitLdvectmp(); - Context.EmitStvec(Op.Rd); - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - private static void EmitVectorUnzip(AILEmitterCtx Context, int Part) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - int Words = Op.GetBitsCount() >> 4; - int Pairs = Words >> Op.Size; - - for (int Index = 0; Index < Pairs; Index++) - { - int Idx = Index << 1; - - EmitVectorExtractZx(Context, Op.Rn, Idx + Part, Op.Size); - EmitVectorExtractZx(Context, Op.Rm, Idx + Part, Op.Size); - - EmitVectorInsertTmp(Context, Pairs + Index, Op.Size); - EmitVectorInsertTmp(Context, Index, Op.Size); - } - - Context.EmitLdvectmp(); - Context.EmitStvec(Op.Rd); - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - private static void EmitVectorZip(AILEmitterCtx Context, int Part) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - if (AOptimizations.UseSse2) - { - EmitLdvecWithUnsignedCast(Context, Op.Rn, Op.Size); - EmitLdvecWithUnsignedCast(Context, Op.Rm, Op.Size); - - Type[] Types = new Type[] - { - VectorUIntTypesPerSizeLog2[Op.Size], - VectorUIntTypesPerSizeLog2[Op.Size] - }; - - string Name = Part == 0 || (Part != 0 && Op.RegisterSize == ARegisterSize.SIMD64) - ? nameof(Sse2.UnpackLow) - : nameof(Sse2.UnpackHigh); - - Context.EmitCall(typeof(Sse2).GetMethod(Name, Types)); - - if (Op.RegisterSize == ARegisterSize.SIMD64 && Part != 0) - { - Context.EmitLdc_I4(8); - - Type[] ShTypes = new Type[] { VectorUIntTypesPerSizeLog2[Op.Size], typeof(byte) }; - - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), ShTypes)); - } - - EmitStvecWithUnsignedCast(Context, Op.Rd, Op.Size); - - if (Op.RegisterSize == ARegisterSize.SIMD64 && Part == 0) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - else - { - int Words = Op.GetBitsCount() >> 4; - int Pairs = Words >> Op.Size; - - int Base = Part != 0 ? Pairs : 0; - - for (int Index = 0; Index < Pairs; Index++) - { - int Idx = Index << 1; - - EmitVectorExtractZx(Context, Op.Rn, Base + Index, Op.Size); - EmitVectorExtractZx(Context, Op.Rm, Base + Index, Op.Size); - - EmitVectorInsertTmp(Context, Idx + 1, Op.Size); - EmitVectorInsertTmp(Context, Idx, Op.Size); - } - - Context.EmitLdvectmp(); - Context.EmitStvec(Op.Rd); - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - } - } -} diff --git a/Instruction/AInstEmitSimdShift.cs b/Instruction/AInstEmitSimdShift.cs deleted file mode 100644 index 4f828cf..0000000 --- a/Instruction/AInstEmitSimdShift.cs +++ /dev/null @@ -1,865 +0,0 @@ -// https://github.com/intel/ARM_NEON_2_x86_SSE/blob/master/NEON_2_SSE.h - -using ChocolArm64.Decoder; -using ChocolArm64.State; -using ChocolArm64.Translation; -using System; -using System.Reflection.Emit; -using System.Runtime.Intrinsics.X86; - -using static ChocolArm64.Instruction.AInstEmitSimdHelper; - -namespace ChocolArm64.Instruction -{ - static partial class AInstEmit - { - public static void Rshrn_V(AILEmitterCtx Context) - { - EmitVectorShrImmNarrowOpZx(Context, Round: true); - } - - public static void Shl_S(AILEmitterCtx Context) - { - AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; - - EmitScalarUnaryOpZx(Context, () => - { - Context.EmitLdc_I4(GetImmShl(Op)); - - Context.Emit(OpCodes.Shl); - }); - } - - public static void Shl_V(AILEmitterCtx Context) - { - AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; - - if (AOptimizations.UseSse2 && Op.Size > 0) - { - Type[] TypesSll = new Type[] { VectorUIntTypesPerSizeLog2[Op.Size], typeof(byte) }; - - EmitLdvecWithUnsignedCast(Context, Op.Rn, Op.Size); - - Context.EmitLdc_I4(GetImmShl(Op)); - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftLeftLogical), TypesSll)); - - EmitStvecWithUnsignedCast(Context, Op.Rd, Op.Size); - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - else - { - EmitVectorUnaryOpZx(Context, () => - { - Context.EmitLdc_I4(GetImmShl(Op)); - - Context.Emit(OpCodes.Shl); - }); - } - } - - public static void Shll_V(AILEmitterCtx Context) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - int Shift = 8 << Op.Size; - - EmitVectorShImmWidenBinaryZx(Context, () => Context.Emit(OpCodes.Shl), Shift); - } - - public static void Shrn_V(AILEmitterCtx Context) - { - EmitVectorShrImmNarrowOpZx(Context, Round: false); - } - - public static void Sli_V(AILEmitterCtx Context) - { - AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; - - int Bytes = Op.GetBitsCount() >> 3; - int Elems = Bytes >> Op.Size; - - int Shift = GetImmShl(Op); - - ulong Mask = Shift != 0 ? ulong.MaxValue >> (64 - Shift) : 0; - - for (int Index = 0; Index < Elems; Index++) - { - EmitVectorExtractZx(Context, Op.Rn, Index, Op.Size); - - Context.EmitLdc_I4(Shift); - - Context.Emit(OpCodes.Shl); - - EmitVectorExtractZx(Context, Op.Rd, Index, Op.Size); - - Context.EmitLdc_I8((long)Mask); - - Context.Emit(OpCodes.And); - Context.Emit(OpCodes.Or); - - EmitVectorInsert(Context, Op.Rd, Index, Op.Size); - } - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - public static void Sqrshrn_S(AILEmitterCtx Context) - { - EmitRoundShrImmSaturatingNarrowOp(Context, ShrImmSaturatingNarrowFlags.ScalarSxSx); - } - - public static void Sqrshrn_V(AILEmitterCtx Context) - { - EmitRoundShrImmSaturatingNarrowOp(Context, ShrImmSaturatingNarrowFlags.VectorSxSx); - } - - public static void Sqrshrun_S(AILEmitterCtx Context) - { - EmitRoundShrImmSaturatingNarrowOp(Context, ShrImmSaturatingNarrowFlags.ScalarSxZx); - } - - public static void Sqrshrun_V(AILEmitterCtx Context) - { - EmitRoundShrImmSaturatingNarrowOp(Context, ShrImmSaturatingNarrowFlags.VectorSxZx); - } - - public static void Sqshrn_S(AILEmitterCtx Context) - { - EmitShrImmSaturatingNarrowOp(Context, ShrImmSaturatingNarrowFlags.ScalarSxSx); - } - - public static void Sqshrn_V(AILEmitterCtx Context) - { - EmitShrImmSaturatingNarrowOp(Context, ShrImmSaturatingNarrowFlags.VectorSxSx); - } - - public static void Sqshrun_S(AILEmitterCtx Context) - { - EmitShrImmSaturatingNarrowOp(Context, ShrImmSaturatingNarrowFlags.ScalarSxZx); - } - - public static void Sqshrun_V(AILEmitterCtx Context) - { - EmitShrImmSaturatingNarrowOp(Context, ShrImmSaturatingNarrowFlags.VectorSxZx); - } - - public static void Srshr_S(AILEmitterCtx Context) - { - EmitScalarShrImmOpSx(Context, ShrImmFlags.Round); - } - - public static void Srshr_V(AILEmitterCtx Context) - { - AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; - - if (AOptimizations.UseSse2 && Op.Size > 0 - && Op.Size < 3) - { - Type[] TypesShs = new Type[] { VectorIntTypesPerSizeLog2[Op.Size], typeof(byte) }; - Type[] TypesAdd = new Type[] { VectorIntTypesPerSizeLog2[Op.Size], VectorIntTypesPerSizeLog2[Op.Size] }; - - int Shift = GetImmShr(Op); - int ESize = 8 << Op.Size; - - EmitLdvecWithSignedCast(Context, Op.Rn, Op.Size); - - Context.Emit(OpCodes.Dup); - Context.EmitStvectmp(); - - Context.EmitLdc_I4(ESize - Shift); - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftLeftLogical), TypesShs)); - - Context.EmitLdc_I4(ESize - 1); - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical), TypesShs)); - - Context.EmitLdvectmp(); - - Context.EmitLdc_I4(Shift); - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightArithmetic), TypesShs)); - - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), TypesAdd)); - - EmitStvecWithSignedCast(Context, Op.Rd, Op.Size); - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - else - { - EmitVectorShrImmOpSx(Context, ShrImmFlags.Round); - } - } - - public static void Srsra_S(AILEmitterCtx Context) - { - EmitScalarShrImmOpSx(Context, ShrImmFlags.Round | ShrImmFlags.Accumulate); - } - - public static void Srsra_V(AILEmitterCtx Context) - { - AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; - - if (AOptimizations.UseSse2 && Op.Size > 0 - && Op.Size < 3) - { - Type[] TypesShs = new Type[] { VectorIntTypesPerSizeLog2[Op.Size], typeof(byte) }; - Type[] TypesAdd = new Type[] { VectorIntTypesPerSizeLog2[Op.Size], VectorIntTypesPerSizeLog2[Op.Size] }; - - int Shift = GetImmShr(Op); - int ESize = 8 << Op.Size; - - EmitLdvecWithSignedCast(Context, Op.Rd, Op.Size); - EmitLdvecWithSignedCast(Context, Op.Rn, Op.Size); - - Context.Emit(OpCodes.Dup); - Context.EmitStvectmp(); - - Context.EmitLdc_I4(ESize - Shift); - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftLeftLogical), TypesShs)); - - Context.EmitLdc_I4(ESize - 1); - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical), TypesShs)); - - Context.EmitLdvectmp(); - - Context.EmitLdc_I4(Shift); - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightArithmetic), TypesShs)); - - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), TypesAdd)); - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), TypesAdd)); - - EmitStvecWithSignedCast(Context, Op.Rd, Op.Size); - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - else - { - EmitVectorShrImmOpSx(Context, ShrImmFlags.Round | ShrImmFlags.Accumulate); - } - } - - public static void Sshl_V(AILEmitterCtx Context) - { - EmitVectorShl(Context, Signed: true); - } - - public static void Sshll_V(AILEmitterCtx Context) - { - AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; - - EmitVectorShImmWidenBinarySx(Context, () => Context.Emit(OpCodes.Shl), GetImmShl(Op)); - } - - public static void Sshr_S(AILEmitterCtx Context) - { - EmitShrImmOp(Context, ShrImmFlags.ScalarSx); - } - - public static void Sshr_V(AILEmitterCtx Context) - { - AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; - - if (AOptimizations.UseSse2 && Op.Size > 0 - && Op.Size < 3) - { - Type[] TypesSra = new Type[] { VectorIntTypesPerSizeLog2[Op.Size], typeof(byte) }; - - EmitLdvecWithSignedCast(Context, Op.Rn, Op.Size); - - Context.EmitLdc_I4(GetImmShr(Op)); - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightArithmetic), TypesSra)); - - EmitStvecWithSignedCast(Context, Op.Rd, Op.Size); - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - else - { - EmitShrImmOp(Context, ShrImmFlags.VectorSx); - } - } - - public static void Ssra_S(AILEmitterCtx Context) - { - EmitScalarShrImmOpSx(Context, ShrImmFlags.Accumulate); - } - - public static void Ssra_V(AILEmitterCtx Context) - { - AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; - - if (AOptimizations.UseSse2 && Op.Size > 0 - && Op.Size < 3) - { - Type[] TypesSra = new Type[] { VectorIntTypesPerSizeLog2[Op.Size], typeof(byte) }; - Type[] TypesAdd = new Type[] { VectorIntTypesPerSizeLog2[Op.Size], VectorIntTypesPerSizeLog2[Op.Size] }; - - EmitLdvecWithSignedCast(Context, Op.Rd, Op.Size); - EmitLdvecWithSignedCast(Context, Op.Rn, Op.Size); - - Context.EmitLdc_I4(GetImmShr(Op)); - - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightArithmetic), TypesSra)); - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), TypesAdd)); - - EmitStvecWithSignedCast(Context, Op.Rd, Op.Size); - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - else - { - EmitVectorShrImmOpSx(Context, ShrImmFlags.Accumulate); - } - } - - public static void Uqrshrn_S(AILEmitterCtx Context) - { - EmitRoundShrImmSaturatingNarrowOp(Context, ShrImmSaturatingNarrowFlags.ScalarZxZx); - } - - public static void Uqrshrn_V(AILEmitterCtx Context) - { - EmitRoundShrImmSaturatingNarrowOp(Context, ShrImmSaturatingNarrowFlags.VectorZxZx); - } - - public static void Uqshrn_S(AILEmitterCtx Context) - { - EmitShrImmSaturatingNarrowOp(Context, ShrImmSaturatingNarrowFlags.ScalarZxZx); - } - - public static void Uqshrn_V(AILEmitterCtx Context) - { - EmitShrImmSaturatingNarrowOp(Context, ShrImmSaturatingNarrowFlags.VectorZxZx); - } - - public static void Urshr_S(AILEmitterCtx Context) - { - EmitScalarShrImmOpZx(Context, ShrImmFlags.Round); - } - - public static void Urshr_V(AILEmitterCtx Context) - { - AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; - - if (AOptimizations.UseSse2 && Op.Size > 0) - { - Type[] TypesShs = new Type[] { VectorUIntTypesPerSizeLog2[Op.Size], typeof(byte) }; - Type[] TypesAdd = new Type[] { VectorUIntTypesPerSizeLog2[Op.Size], VectorUIntTypesPerSizeLog2[Op.Size] }; - - int Shift = GetImmShr(Op); - int ESize = 8 << Op.Size; - - EmitLdvecWithUnsignedCast(Context, Op.Rn, Op.Size); - - Context.Emit(OpCodes.Dup); - Context.EmitStvectmp(); - - Context.EmitLdc_I4(ESize - Shift); - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftLeftLogical), TypesShs)); - - Context.EmitLdc_I4(ESize - 1); - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical), TypesShs)); - - Context.EmitLdvectmp(); - - Context.EmitLdc_I4(Shift); - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical), TypesShs)); - - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), TypesAdd)); - - EmitStvecWithUnsignedCast(Context, Op.Rd, Op.Size); - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - else - { - EmitVectorShrImmOpZx(Context, ShrImmFlags.Round); - } - } - - public static void Ursra_S(AILEmitterCtx Context) - { - EmitScalarShrImmOpZx(Context, ShrImmFlags.Round | ShrImmFlags.Accumulate); - } - - public static void Ursra_V(AILEmitterCtx Context) - { - AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; - - if (AOptimizations.UseSse2 && Op.Size > 0) - { - Type[] TypesShs = new Type[] { VectorUIntTypesPerSizeLog2[Op.Size], typeof(byte) }; - Type[] TypesAdd = new Type[] { VectorUIntTypesPerSizeLog2[Op.Size], VectorUIntTypesPerSizeLog2[Op.Size] }; - - int Shift = GetImmShr(Op); - int ESize = 8 << Op.Size; - - EmitLdvecWithUnsignedCast(Context, Op.Rd, Op.Size); - EmitLdvecWithUnsignedCast(Context, Op.Rn, Op.Size); - - Context.Emit(OpCodes.Dup); - Context.EmitStvectmp(); - - Context.EmitLdc_I4(ESize - Shift); - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftLeftLogical), TypesShs)); - - Context.EmitLdc_I4(ESize - 1); - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical), TypesShs)); - - Context.EmitLdvectmp(); - - Context.EmitLdc_I4(Shift); - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical), TypesShs)); - - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), TypesAdd)); - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), TypesAdd)); - - EmitStvecWithUnsignedCast(Context, Op.Rd, Op.Size); - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - else - { - EmitVectorShrImmOpZx(Context, ShrImmFlags.Round | ShrImmFlags.Accumulate); - } - } - - public static void Ushl_V(AILEmitterCtx Context) - { - EmitVectorShl(Context, Signed: false); - } - - public static void Ushll_V(AILEmitterCtx Context) - { - AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; - - EmitVectorShImmWidenBinaryZx(Context, () => Context.Emit(OpCodes.Shl), GetImmShl(Op)); - } - - public static void Ushr_S(AILEmitterCtx Context) - { - EmitShrImmOp(Context, ShrImmFlags.ScalarZx); - } - - public static void Ushr_V(AILEmitterCtx Context) - { - AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; - - if (AOptimizations.UseSse2 && Op.Size > 0) - { - Type[] TypesSrl = new Type[] { VectorUIntTypesPerSizeLog2[Op.Size], typeof(byte) }; - - EmitLdvecWithUnsignedCast(Context, Op.Rn, Op.Size); - - Context.EmitLdc_I4(GetImmShr(Op)); - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical), TypesSrl)); - - EmitStvecWithUnsignedCast(Context, Op.Rd, Op.Size); - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - else - { - EmitShrImmOp(Context, ShrImmFlags.VectorZx); - } - } - - public static void Usra_S(AILEmitterCtx Context) - { - EmitScalarShrImmOpZx(Context, ShrImmFlags.Accumulate); - } - - public static void Usra_V(AILEmitterCtx Context) - { - AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; - - if (AOptimizations.UseSse2 && Op.Size > 0) - { - Type[] TypesSrl = new Type[] { VectorUIntTypesPerSizeLog2[Op.Size], typeof(byte) }; - Type[] TypesAdd = new Type[] { VectorUIntTypesPerSizeLog2[Op.Size], VectorUIntTypesPerSizeLog2[Op.Size] }; - - EmitLdvecWithUnsignedCast(Context, Op.Rd, Op.Size); - EmitLdvecWithUnsignedCast(Context, Op.Rn, Op.Size); - - Context.EmitLdc_I4(GetImmShr(Op)); - - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical), TypesSrl)); - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), TypesAdd)); - - EmitStvecWithUnsignedCast(Context, Op.Rd, Op.Size); - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - else - { - EmitVectorShrImmOpZx(Context, ShrImmFlags.Accumulate); - } - } - - 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); - } - } - - [Flags] - private enum ShrImmFlags - { - Scalar = 1 << 0, - Signed = 1 << 1, - - Round = 1 << 2, - Accumulate = 1 << 3, - - ScalarSx = Scalar | Signed, - ScalarZx = Scalar, - - VectorSx = Signed, - VectorZx = 0 - } - - private static void EmitScalarShrImmOpSx(AILEmitterCtx Context, ShrImmFlags Flags) - { - EmitShrImmOp(Context, ShrImmFlags.ScalarSx | Flags); - } - - private static void EmitScalarShrImmOpZx(AILEmitterCtx Context, ShrImmFlags Flags) - { - EmitShrImmOp(Context, ShrImmFlags.ScalarZx | Flags); - } - - private static void EmitVectorShrImmOpSx(AILEmitterCtx Context, ShrImmFlags Flags) - { - EmitShrImmOp(Context, ShrImmFlags.VectorSx | Flags); - } - - private static void EmitVectorShrImmOpZx(AILEmitterCtx Context, ShrImmFlags Flags) - { - EmitShrImmOp(Context, ShrImmFlags.VectorZx | Flags); - } - - private static void EmitShrImmOp(AILEmitterCtx Context, ShrImmFlags Flags) - { - AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; - - bool Scalar = (Flags & ShrImmFlags.Scalar) != 0; - bool Signed = (Flags & ShrImmFlags.Signed) != 0; - bool Round = (Flags & ShrImmFlags.Round) != 0; - bool Accumulate = (Flags & ShrImmFlags.Accumulate) != 0; - - int Shift = GetImmShr(Op); - - long RoundConst = 1L << (Shift - 1); - - int Bytes = Op.GetBitsCount() >> 3; - int Elems = !Scalar ? Bytes >> Op.Size : 1; - - for (int Index = 0; Index < Elems; Index++) - { - EmitVectorExtract(Context, Op.Rn, Index, Op.Size, Signed); - - if (Op.Size <= 2) - { - if (Round) - { - Context.EmitLdc_I8(RoundConst); - - Context.Emit(OpCodes.Add); - } - - Context.EmitLdc_I4(Shift); - - Context.Emit(Signed ? OpCodes.Shr : OpCodes.Shr_Un); - } - else /* if (Op.Size == 3) */ - { - EmitShrImm_64(Context, Signed, Round ? RoundConst : 0L, Shift); - } - - if (Accumulate) - { - EmitVectorExtract(Context, Op.Rd, Index, Op.Size, Signed); - - Context.Emit(OpCodes.Add); - } - - EmitVectorInsertTmp(Context, Index, Op.Size); - } - - Context.EmitLdvectmp(); - Context.EmitStvec(Op.Rd); - - if ((Op.RegisterSize == ARegisterSize.SIMD64) || Scalar) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - private static void EmitVectorShrImmNarrowOpZx(AILEmitterCtx Context, bool Round) - { - AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; - - int Shift = GetImmShr(Op); - - long RoundConst = 1L << (Shift - 1); - - int Elems = 8 >> Op.Size; - - int Part = Op.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0; - - if (Part != 0) - { - Context.EmitLdvec(Op.Rd); - Context.EmitStvectmp(); - } - - for (int Index = 0; Index < Elems; Index++) - { - EmitVectorExtractZx(Context, Op.Rn, Index, Op.Size + 1); - - if (Round) - { - Context.EmitLdc_I8(RoundConst); - - Context.Emit(OpCodes.Add); - } - - Context.EmitLdc_I4(Shift); - - Context.Emit(OpCodes.Shr_Un); - - EmitVectorInsertTmp(Context, Part + Index, Op.Size); - } - - Context.EmitLdvectmp(); - Context.EmitStvec(Op.Rd); - - if (Part == 0) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - [Flags] - private enum ShrImmSaturatingNarrowFlags - { - Scalar = 1 << 0, - SignedSrc = 1 << 1, - SignedDst = 1 << 2, - - Round = 1 << 3, - - ScalarSxSx = Scalar | SignedSrc | SignedDst, - ScalarSxZx = Scalar | SignedSrc, - ScalarZxZx = Scalar, - - VectorSxSx = SignedSrc | SignedDst, - VectorSxZx = SignedSrc, - VectorZxZx = 0 - } - - private static void EmitRoundShrImmSaturatingNarrowOp(AILEmitterCtx Context, ShrImmSaturatingNarrowFlags Flags) - { - EmitShrImmSaturatingNarrowOp(Context, ShrImmSaturatingNarrowFlags.Round | Flags); - } - - private static void EmitShrImmSaturatingNarrowOp(AILEmitterCtx Context, ShrImmSaturatingNarrowFlags Flags) - { - AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; - - bool Scalar = (Flags & ShrImmSaturatingNarrowFlags.Scalar) != 0; - bool SignedSrc = (Flags & ShrImmSaturatingNarrowFlags.SignedSrc) != 0; - bool SignedDst = (Flags & ShrImmSaturatingNarrowFlags.SignedDst) != 0; - bool Round = (Flags & ShrImmSaturatingNarrowFlags.Round) != 0; - - int Shift = GetImmShr(Op); - - long RoundConst = 1L << (Shift - 1); - - int Elems = !Scalar ? 8 >> Op.Size : 1; - - int Part = !Scalar && (Op.RegisterSize == ARegisterSize.SIMD128) ? Elems : 0; - - if (Scalar) - { - EmitVectorZeroLowerTmp(Context); - } - - if (Part != 0) - { - Context.EmitLdvec(Op.Rd); - Context.EmitStvectmp(); - } - - for (int Index = 0; Index < Elems; Index++) - { - EmitVectorExtract(Context, Op.Rn, Index, Op.Size + 1, SignedSrc); - - if (Op.Size <= 1 || !Round) - { - if (Round) - { - Context.EmitLdc_I8(RoundConst); - - Context.Emit(OpCodes.Add); - } - - Context.EmitLdc_I4(Shift); - - Context.Emit(SignedSrc ? OpCodes.Shr : OpCodes.Shr_Un); - } - else /* if (Op.Size == 2 && Round) */ - { - EmitShrImm_64(Context, SignedSrc, RoundConst, Shift); // Shift <= 32 - } - - EmitSatQ(Context, Op.Size, SignedSrc, SignedDst); - - EmitVectorInsertTmp(Context, Part + Index, Op.Size); - } - - Context.EmitLdvectmp(); - Context.EmitStvec(Op.Rd); - - if (Part == 0) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - // Dst_64 = (Int(Src_64, Signed) + RoundConst) >> Shift; - private static void EmitShrImm_64( - AILEmitterCtx Context, - bool Signed, - long RoundConst, - int Shift) - { - Context.EmitLdc_I8(RoundConst); - Context.EmitLdc_I4(Shift); - - ASoftFallback.EmitCall(Context, Signed - ? nameof(ASoftFallback.SignedShrImm_64) - : nameof(ASoftFallback.UnsignedShrImm_64)); - } - - 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) - { - 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++) - { - 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); - } - } -} diff --git a/Instruction/AInstEmitSystem.cs b/Instruction/AInstEmitSystem.cs deleted file mode 100644 index a365398..0000000 --- a/Instruction/AInstEmitSystem.cs +++ /dev/null @@ -1,138 +0,0 @@ -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 Hint(AILEmitterCtx Context) - { - //Execute as no-op. - } - - public static void Isb(AILEmitterCtx Context) - { - //Execute as no-op. - } - - 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_000: PropName = nameof(AThreadState.CntfrqEl0); 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.EmitLdintzr(Op.Rt); - Context.EmitLdc_I(Offs); - - Context.Emit(OpCodes.Add); - - Context.EmitLdc_I8(0); - - AInstEmitMemoryHelper.EmitWriteCall(Context, 3); - } - - break; - } - - //No-op - case 0b11_011_0111_1110_001: //DC CIVAC - 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; - } - } -} diff --git a/Instruction/AInstEmitter.cs b/Instruction/AInstEmitter.cs deleted file mode 100644 index 8712a73..0000000 --- a/Instruction/AInstEmitter.cs +++ /dev/null @@ -1,6 +0,0 @@ -using ChocolArm64.Translation; - -namespace ChocolArm64.Instruction -{ - delegate void AInstEmitter(AILEmitterCtx Context); -} \ No newline at end of file diff --git a/Instruction/AInstInterpreter.cs b/Instruction/AInstInterpreter.cs deleted file mode 100644 index 6a855ae..0000000 --- a/Instruction/AInstInterpreter.cs +++ /dev/null @@ -1,8 +0,0 @@ -using ChocolArm64.Decoder; -using ChocolArm64.Memory; -using ChocolArm64.State; - -namespace ChocolArm64.Instruction -{ - delegate void AInstInterpreter(AThreadState State, AMemory Memory, AOpCode OpCode); -} \ No newline at end of file diff --git a/Instruction/ASoftFallback.cs b/Instruction/ASoftFallback.cs deleted file mode 100644 index d643fb6..0000000 --- a/Instruction/ASoftFallback.cs +++ /dev/null @@ -1,922 +0,0 @@ -using ChocolArm64.State; -using ChocolArm64.Translation; -using System; -using System.Runtime.CompilerServices; -using System.Runtime.Intrinsics; -using System.Runtime.Intrinsics.X86; - -namespace ChocolArm64.Instruction -{ - using static AVectorHelper; - - static class ASoftFallback - { - public static void EmitCall(AILEmitterCtx Context, string MthdName) - { - Context.EmitCall(typeof(ASoftFallback), MthdName); - } - -#region "ShrImm_64" - public static long SignedShrImm_64(long Value, long RoundConst, int Shift) - { - if (RoundConst == 0L) - { - if (Shift <= 63) - { - return Value >> Shift; - } - else /* if (Shift == 64) */ - { - if (Value < 0L) - { - return -1L; - } - else - { - return 0L; - } - } - } - else /* if (RoundConst == 1L << (Shift - 1)) */ - { - if (Shift <= 63) - { - long Add = Value + RoundConst; - - if ((~Value & (Value ^ Add)) < 0L) - { - return (long)((ulong)Add >> Shift); - } - else - { - return Add >> Shift; - } - } - else /* if (Shift == 64) */ - { - return 0L; - } - } - } - - public static ulong UnsignedShrImm_64(ulong Value, long RoundConst, int Shift) - { - if (RoundConst == 0L) - { - if (Shift <= 63) - { - return Value >> Shift; - } - else /* if (Shift == 64) */ - { - return 0UL; - } - } - else /* if (RoundConst == 1L << (Shift - 1)) */ - { - ulong Add = Value + (ulong)RoundConst; - - if ((Add < Value) && (Add < (ulong)RoundConst)) - { - if (Shift <= 63) - { - return (Add >> Shift) | (0x8000000000000000UL >> (Shift - 1)); - } - else /* if (Shift == 64) */ - { - return 1UL; - } - } - else - { - if (Shift <= 63) - { - return Add >> Shift; - } - else /* if (Shift == 64) */ - { - return 0UL; - } - } - } - } -#endregion - -#region "Saturating" - public static long SignedSrcSignedDstSatQ(long Op, int Size, AThreadState State) - { - int ESize = 8 << Size; - - long TMaxValue = (1L << (ESize - 1)) - 1L; - long TMinValue = -(1L << (ESize - 1)); - - if (Op > TMaxValue) - { - State.SetFpsrFlag(FPSR.QC); - - return TMaxValue; - } - else if (Op < TMinValue) - { - State.SetFpsrFlag(FPSR.QC); - - return TMinValue; - } - else - { - return Op; - } - } - - public static ulong SignedSrcUnsignedDstSatQ(long Op, int Size, AThreadState State) - { - int ESize = 8 << Size; - - ulong TMaxValue = (1UL << ESize) - 1UL; - ulong TMinValue = 0UL; - - if (Op > (long)TMaxValue) - { - State.SetFpsrFlag(FPSR.QC); - - return TMaxValue; - } - else if (Op < (long)TMinValue) - { - State.SetFpsrFlag(FPSR.QC); - - return TMinValue; - } - else - { - return (ulong)Op; - } - } - - public static long UnsignedSrcSignedDstSatQ(ulong Op, int Size, AThreadState State) - { - int ESize = 8 << Size; - - long TMaxValue = (1L << (ESize - 1)) - 1L; - - if (Op > (ulong)TMaxValue) - { - State.SetFpsrFlag(FPSR.QC); - - return TMaxValue; - } - else - { - return (long)Op; - } - } - - public static ulong UnsignedSrcUnsignedDstSatQ(ulong Op, int Size, AThreadState State) - { - int ESize = 8 << Size; - - ulong TMaxValue = (1UL << ESize) - 1UL; - - if (Op > TMaxValue) - { - State.SetFpsrFlag(FPSR.QC); - - return TMaxValue; - } - else - { - return Op; - } - } - - public static long UnarySignedSatQAbsOrNeg(long Op, AThreadState State) - { - if (Op == long.MinValue) - { - State.SetFpsrFlag(FPSR.QC); - - return long.MaxValue; - } - else - { - return Op; - } - } - - public static long BinarySignedSatQAdd(long Op1, long Op2, AThreadState State) - { - long Add = Op1 + Op2; - - if ((~(Op1 ^ Op2) & (Op1 ^ Add)) < 0L) - { - State.SetFpsrFlag(FPSR.QC); - - if (Op1 < 0L) - { - return long.MinValue; - } - else - { - return long.MaxValue; - } - } - else - { - return Add; - } - } - - public static ulong BinaryUnsignedSatQAdd(ulong Op1, ulong Op2, AThreadState State) - { - ulong Add = Op1 + Op2; - - if ((Add < Op1) && (Add < Op2)) - { - State.SetFpsrFlag(FPSR.QC); - - return ulong.MaxValue; - } - else - { - return Add; - } - } - - public static long BinarySignedSatQSub(long Op1, long Op2, AThreadState State) - { - long Sub = Op1 - Op2; - - if (((Op1 ^ Op2) & (Op1 ^ Sub)) < 0L) - { - State.SetFpsrFlag(FPSR.QC); - - if (Op1 < 0L) - { - return long.MinValue; - } - else - { - return long.MaxValue; - } - } - else - { - return Sub; - } - } - - public static ulong BinaryUnsignedSatQSub(ulong Op1, ulong Op2, AThreadState State) - { - ulong Sub = Op1 - Op2; - - if (Op1 < Op2) - { - State.SetFpsrFlag(FPSR.QC); - - return ulong.MinValue; - } - else - { - return Sub; - } - } - - public static long BinarySignedSatQAcc(ulong Op1, long Op2, AThreadState State) - { - if (Op1 <= (ulong)long.MaxValue) - { - // Op1 from ulong.MinValue to (ulong)long.MaxValue - // Op2 from long.MinValue to long.MaxValue - - long Add = (long)Op1 + Op2; - - if ((~Op2 & Add) < 0L) - { - State.SetFpsrFlag(FPSR.QC); - - return long.MaxValue; - } - else - { - return Add; - } - } - else if (Op2 >= 0L) - { - // Op1 from (ulong)long.MaxValue + 1UL to ulong.MaxValue - // Op2 from (long)ulong.MinValue to long.MaxValue - - State.SetFpsrFlag(FPSR.QC); - - return long.MaxValue; - } - else - { - // Op1 from (ulong)long.MaxValue + 1UL to ulong.MaxValue - // Op2 from long.MinValue to (long)ulong.MinValue - 1L - - ulong Add = Op1 + (ulong)Op2; - - if (Add > (ulong)long.MaxValue) - { - State.SetFpsrFlag(FPSR.QC); - - return long.MaxValue; - } - else - { - return (long)Add; - } - } - } - - public static ulong BinaryUnsignedSatQAcc(long Op1, ulong Op2, AThreadState State) - { - if (Op1 >= 0L) - { - // Op1 from (long)ulong.MinValue to long.MaxValue - // Op2 from ulong.MinValue to ulong.MaxValue - - ulong Add = (ulong)Op1 + Op2; - - if ((Add < (ulong)Op1) && (Add < Op2)) - { - State.SetFpsrFlag(FPSR.QC); - - return ulong.MaxValue; - } - else - { - return Add; - } - } - else if (Op2 > (ulong)long.MaxValue) - { - // Op1 from long.MinValue to (long)ulong.MinValue - 1L - // Op2 from (ulong)long.MaxValue + 1UL to ulong.MaxValue - - return (ulong)Op1 + Op2; - } - else - { - // Op1 from long.MinValue to (long)ulong.MinValue - 1L - // Op2 from ulong.MinValue to (ulong)long.MaxValue - - long Add = Op1 + (long)Op2; - - if (Add < (long)ulong.MinValue) - { - State.SetFpsrFlag(FPSR.QC); - - return ulong.MinValue; - } - else - { - return (ulong)Add; - } - } - } -#endregion - -#region "Count" - public static ulong CountLeadingSigns(ulong Value, int Size) // Size is 8, 16, 32 or 64 (SIMD&FP or Base Inst.). - { - Value ^= Value >> 1; - - int HighBit = Size - 2; - - for (int Bit = HighBit; Bit >= 0; Bit--) - { - if (((Value >> Bit) & 0b1) != 0) - { - return (ulong)(HighBit - Bit); - } - } - - return (ulong)(Size - 1); - } - - private static readonly byte[] ClzNibbleTbl = { 4, 3, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 }; - - public static ulong CountLeadingZeros(ulong Value, int Size) // Size is 8, 16, 32 or 64 (SIMD&FP or Base Inst.). - { - if (Value == 0ul) - { - return (ulong)Size; - } - - int NibbleIdx = Size; - int PreCount, Count = 0; - - do - { - NibbleIdx -= 4; - PreCount = ClzNibbleTbl[(Value >> NibbleIdx) & 0b1111]; - Count += PreCount; - } - while (PreCount == 4); - - return (ulong)Count; - } - - public static ulong CountSetBits8(ulong Value) // "Size" is 8 (SIMD&FP Inst.). - { - if (Value == 0xfful) - { - return 8ul; - } - - Value = ((Value >> 1) & 0x55ul) + (Value & 0x55ul); - Value = ((Value >> 2) & 0x33ul) + (Value & 0x33ul); - - return (Value >> 4) + (Value & 0x0ful); - } -#endregion - -#region "Crc32" - private const uint Crc32RevPoly = 0xedb88320; - private const uint Crc32cRevPoly = 0x82f63b78; - - public static uint Crc32b(uint Crc, byte Val) => Crc32 (Crc, Crc32RevPoly, Val); - public static uint Crc32h(uint Crc, ushort Val) => Crc32h(Crc, Crc32RevPoly, Val); - public static uint Crc32w(uint Crc, uint Val) => Crc32w(Crc, Crc32RevPoly, Val); - public static uint Crc32x(uint Crc, ulong Val) => Crc32x(Crc, Crc32RevPoly, Val); - - public static uint Crc32cb(uint Crc, byte Val) => Crc32 (Crc, Crc32cRevPoly, Val); - public static uint Crc32ch(uint Crc, ushort Val) => Crc32h(Crc, Crc32cRevPoly, Val); - public static uint Crc32cw(uint Crc, uint Val) => Crc32w(Crc, Crc32cRevPoly, Val); - public static uint Crc32cx(uint Crc, ulong Val) => Crc32x(Crc, Crc32cRevPoly, Val); - - private static uint Crc32h(uint Crc, uint Poly, ushort Val) - { - Crc = Crc32(Crc, Poly, (byte)(Val >> 0)); - Crc = Crc32(Crc, Poly, (byte)(Val >> 8)); - - return Crc; - } - - private static uint Crc32w(uint Crc, uint Poly, uint Val) - { - Crc = Crc32(Crc, Poly, (byte)(Val >> 0 )); - Crc = Crc32(Crc, Poly, (byte)(Val >> 8 )); - Crc = Crc32(Crc, Poly, (byte)(Val >> 16)); - Crc = Crc32(Crc, Poly, (byte)(Val >> 24)); - - return Crc; - } - - private static uint Crc32x(uint Crc, uint Poly, ulong Val) - { - Crc = Crc32(Crc, Poly, (byte)(Val >> 0 )); - Crc = Crc32(Crc, Poly, (byte)(Val >> 8 )); - Crc = Crc32(Crc, Poly, (byte)(Val >> 16)); - Crc = Crc32(Crc, Poly, (byte)(Val >> 24)); - Crc = Crc32(Crc, Poly, (byte)(Val >> 32)); - Crc = Crc32(Crc, Poly, (byte)(Val >> 40)); - Crc = Crc32(Crc, Poly, (byte)(Val >> 48)); - Crc = Crc32(Crc, Poly, (byte)(Val >> 56)); - - return Crc; - } - - private static uint Crc32(uint Crc, uint Poly, byte Val) - { - Crc ^= Val; - - for (int Bit = 7; Bit >= 0; Bit--) - { - uint Mask = (uint)(-(int)(Crc & 1)); - - Crc = (Crc >> 1) ^ (Poly & Mask); - } - - return Crc; - } -#endregion - -#region "Aes" - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 Decrypt(Vector128 value, Vector128 roundKey) - { - if (!Sse.IsSupported) - { - throw new PlatformNotSupportedException(); - } - - return ACryptoHelper.AESInvSubBytes(ACryptoHelper.AESInvShiftRows(Sse.Xor(value, roundKey))); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 Encrypt(Vector128 value, Vector128 roundKey) - { - if (!Sse.IsSupported) - { - throw new PlatformNotSupportedException(); - } - - return ACryptoHelper.AESSubBytes(ACryptoHelper.AESShiftRows(Sse.Xor(value, roundKey))); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 InverseMixColumns(Vector128 value) - { - return ACryptoHelper.AESInvMixColumns(value); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 MixColumns(Vector128 value) - { - return ACryptoHelper.AESMixColumns(value); - } -#endregion - -#region "Sha1" - public static Vector128 HashChoose(Vector128 hash_abcd, uint hash_e, Vector128 wk) - { - for (int e = 0; e <= 3; e++) - { - uint t = SHAchoose((uint)VectorExtractIntZx(hash_abcd, (byte)1, 2), - (uint)VectorExtractIntZx(hash_abcd, (byte)2, 2), - (uint)VectorExtractIntZx(hash_abcd, (byte)3, 2)); - - hash_e += Rol((uint)VectorExtractIntZx(hash_abcd, (byte)0, 2), 5) + t; - hash_e += (uint)VectorExtractIntZx(wk, (byte)e, 2); - - t = Rol((uint)VectorExtractIntZx(hash_abcd, (byte)1, 2), 30); - hash_abcd = VectorInsertInt((ulong)t, hash_abcd, (byte)1, 2); - - Rol32_160(ref hash_e, ref hash_abcd); - } - - return hash_abcd; - } - - public static uint FixedRotate(uint hash_e) - { - return hash_e.Rol(30); - } - - public static Vector128 HashMajority(Vector128 hash_abcd, uint hash_e, Vector128 wk) - { - for (int e = 0; e <= 3; e++) - { - uint t = SHAmajority((uint)VectorExtractIntZx(hash_abcd, (byte)1, 2), - (uint)VectorExtractIntZx(hash_abcd, (byte)2, 2), - (uint)VectorExtractIntZx(hash_abcd, (byte)3, 2)); - - hash_e += Rol((uint)VectorExtractIntZx(hash_abcd, (byte)0, 2), 5) + t; - hash_e += (uint)VectorExtractIntZx(wk, (byte)e, 2); - - t = Rol((uint)VectorExtractIntZx(hash_abcd, (byte)1, 2), 30); - hash_abcd = VectorInsertInt((ulong)t, hash_abcd, (byte)1, 2); - - Rol32_160(ref hash_e, ref hash_abcd); - } - - return hash_abcd; - } - - public static Vector128 HashParity(Vector128 hash_abcd, uint hash_e, Vector128 wk) - { - for (int e = 0; e <= 3; e++) - { - uint t = SHAparity((uint)VectorExtractIntZx(hash_abcd, (byte)1, 2), - (uint)VectorExtractIntZx(hash_abcd, (byte)2, 2), - (uint)VectorExtractIntZx(hash_abcd, (byte)3, 2)); - - hash_e += Rol((uint)VectorExtractIntZx(hash_abcd, (byte)0, 2), 5) + t; - hash_e += (uint)VectorExtractIntZx(wk, (byte)e, 2); - - t = Rol((uint)VectorExtractIntZx(hash_abcd, (byte)1, 2), 30); - hash_abcd = VectorInsertInt((ulong)t, hash_abcd, (byte)1, 2); - - Rol32_160(ref hash_e, ref hash_abcd); - } - - return hash_abcd; - } - - public static Vector128 Sha1SchedulePart1(Vector128 w0_3, Vector128 w4_7, Vector128 w8_11) - { - if (!Sse.IsSupported) - { - throw new PlatformNotSupportedException(); - } - - Vector128 result = new Vector128(); - - ulong t2 = VectorExtractIntZx(w4_7, (byte)0, 3); - ulong t1 = VectorExtractIntZx(w0_3, (byte)1, 3); - - result = VectorInsertInt((ulong)t1, result, (byte)0, 3); - result = VectorInsertInt((ulong)t2, result, (byte)1, 3); - - return Sse.Xor(result, Sse.Xor(w0_3, w8_11)); - } - - public static Vector128 Sha1SchedulePart2(Vector128 tw0_3, Vector128 w12_15) - { - if (!Sse2.IsSupported) - { - throw new PlatformNotSupportedException(); - } - - Vector128 result = new Vector128(); - - Vector128 T = Sse.Xor(tw0_3, Sse.StaticCast( - Sse2.ShiftRightLogical128BitLane(Sse.StaticCast(w12_15), (byte)4))); - - uint tE0 = (uint)VectorExtractIntZx(T, (byte)0, 2); - uint tE1 = (uint)VectorExtractIntZx(T, (byte)1, 2); - uint tE2 = (uint)VectorExtractIntZx(T, (byte)2, 2); - uint tE3 = (uint)VectorExtractIntZx(T, (byte)3, 2); - - result = VectorInsertInt((ulong)tE0.Rol(1), result, (byte)0, 2); - result = VectorInsertInt((ulong)tE1.Rol(1), result, (byte)1, 2); - result = VectorInsertInt((ulong)tE2.Rol(1), result, (byte)2, 2); - - return VectorInsertInt((ulong)(tE3.Rol(1) ^ tE0.Rol(2)), result, (byte)3, 2); - } - - private static void Rol32_160(ref uint y, ref Vector128 X) - { - if (!Sse2.IsSupported) - { - throw new PlatformNotSupportedException(); - } - - uint xE3 = (uint)VectorExtractIntZx(X, (byte)3, 2); - - X = Sse.StaticCast(Sse2.ShiftLeftLogical128BitLane(Sse.StaticCast(X), (byte)4)); - X = VectorInsertInt((ulong)y, X, (byte)0, 2); - - y = xE3; - } - - private static uint SHAchoose(uint x, uint y, uint z) - { - return ((y ^ z) & x) ^ z; - } - - private static uint SHAmajority(uint x, uint y, uint z) - { - return (x & y) | ((x | y) & z); - } - - private static uint SHAparity(uint x, uint y, uint z) - { - return x ^ y ^ z; - } - - private static uint Rol(this uint value, int count) - { - return (value << count) | (value >> (32 - count)); - } -#endregion - -#region "Sha256" - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 HashLower(Vector128 hash_abcd, Vector128 hash_efgh, Vector128 wk) - { - return SHA256hash(hash_abcd, hash_efgh, wk, true); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 HashUpper(Vector128 hash_efgh, Vector128 hash_abcd, Vector128 wk) - { - return SHA256hash(hash_abcd, hash_efgh, wk, false); - } - - public static Vector128 Sha256SchedulePart1(Vector128 w0_3, Vector128 w4_7) - { - Vector128 result = new Vector128(); - - for (int e = 0; e <= 3; e++) - { - uint elt = (uint)VectorExtractIntZx(e <= 2 ? w0_3 : w4_7, (byte)(e <= 2 ? e + 1 : 0), 2); - - elt = elt.Ror(7) ^ elt.Ror(18) ^ elt.Lsr(3); - - elt += (uint)VectorExtractIntZx(w0_3, (byte)e, 2); - - result = VectorInsertInt((ulong)elt, result, (byte)e, 2); - } - - return result; - } - - public static Vector128 Sha256SchedulePart2(Vector128 w0_3, Vector128 w8_11, Vector128 w12_15) - { - Vector128 result = new Vector128(); - - ulong T1 = VectorExtractIntZx(w12_15, (byte)1, 3); - - for (int e = 0; e <= 1; e++) - { - uint elt = T1.ULongPart(e); - - elt = elt.Ror(17) ^ elt.Ror(19) ^ elt.Lsr(10); - - elt += (uint)VectorExtractIntZx(w0_3, (byte)e, 2); - elt += (uint)VectorExtractIntZx(w8_11, (byte)(e + 1), 2); - - result = VectorInsertInt((ulong)elt, result, (byte)e, 2); - } - - T1 = VectorExtractIntZx(result, (byte)0, 3); - - for (int e = 2; e <= 3; e++) - { - uint elt = T1.ULongPart(e - 2); - - elt = elt.Ror(17) ^ elt.Ror(19) ^ elt.Lsr(10); - - elt += (uint)VectorExtractIntZx(w0_3, (byte)e, 2); - elt += (uint)VectorExtractIntZx(e == 2 ? w8_11 : w12_15, (byte)(e == 2 ? 3 : 0), 2); - - result = VectorInsertInt((ulong)elt, result, (byte)e, 2); - } - - return result; - } - - private static Vector128 SHA256hash(Vector128 X, Vector128 Y, Vector128 W, bool part1) - { - for (int e = 0; e <= 3; e++) - { - uint chs = SHAchoose((uint)VectorExtractIntZx(Y, (byte)0, 2), - (uint)VectorExtractIntZx(Y, (byte)1, 2), - (uint)VectorExtractIntZx(Y, (byte)2, 2)); - - uint maj = SHAmajority((uint)VectorExtractIntZx(X, (byte)0, 2), - (uint)VectorExtractIntZx(X, (byte)1, 2), - (uint)VectorExtractIntZx(X, (byte)2, 2)); - - uint t1 = (uint)VectorExtractIntZx(Y, (byte)3, 2); - t1 += SHAhashSIGMA1((uint)VectorExtractIntZx(Y, (byte)0, 2)) + chs; - t1 += (uint)VectorExtractIntZx(W, (byte)e, 2); - - uint t2 = t1 + (uint)VectorExtractIntZx(X, (byte)3, 2); - X = VectorInsertInt((ulong)t2, X, (byte)3, 2); - t2 = t1 + SHAhashSIGMA0((uint)VectorExtractIntZx(X, (byte)0, 2)) + maj; - Y = VectorInsertInt((ulong)t2, Y, (byte)3, 2); - - Rol32_256(ref Y, ref X); - } - - return part1 ? X : Y; - } - - private static void Rol32_256(ref Vector128 Y, ref Vector128 X) - { - if (!Sse2.IsSupported) - { - throw new PlatformNotSupportedException(); - } - - uint yE3 = (uint)VectorExtractIntZx(Y, (byte)3, 2); - uint xE3 = (uint)VectorExtractIntZx(X, (byte)3, 2); - - Y = Sse.StaticCast(Sse2.ShiftLeftLogical128BitLane(Sse.StaticCast(Y), (byte)4)); - X = Sse.StaticCast(Sse2.ShiftLeftLogical128BitLane(Sse.StaticCast(X), (byte)4)); - - Y = VectorInsertInt((ulong)xE3, Y, (byte)0, 2); - X = VectorInsertInt((ulong)yE3, X, (byte)0, 2); - } - - private static uint SHAhashSIGMA0(uint x) - { - return x.Ror(2) ^ x.Ror(13) ^ x.Ror(22); - } - - private static uint SHAhashSIGMA1(uint x) - { - return x.Ror(6) ^ x.Ror(11) ^ x.Ror(25); - } - - private static uint Ror(this uint value, int count) - { - return (value >> count) | (value << (32 - count)); - } - - private static uint Lsr(this uint value, int count) - { - return value >> count; - } - - private static uint ULongPart(this ulong value, int part) - { - return part == 0 - ? (uint)(value & 0xFFFFFFFFUL) - : (uint)(value >> 32); - } -#endregion - -#region "Reverse" - public static uint ReverseBits8(uint Value) - { - Value = ((Value & 0xaa) >> 1) | ((Value & 0x55) << 1); - Value = ((Value & 0xcc) >> 2) | ((Value & 0x33) << 2); - - return (Value >> 4) | ((Value & 0x0f) << 4); - } - - 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)); - } -#endregion - -#region "MultiplyHigh" - public static long SMulHi128(long Left, long Right) - { - long Result = (long)UMulHi128((ulong)Left, (ulong)Right); - - if (Left < 0) - { - Result -= Right; - } - - if (Right < 0) - { - Result -= Left; - } - - return Result; - } - - public static ulong UMulHi128(ulong Left, ulong Right) - { - ulong LHigh = Left >> 32; - ulong LLow = Left & 0xFFFFFFFF; - ulong RHigh = Right >> 32; - ulong RLow = Right & 0xFFFFFFFF; - - ulong Z2 = LLow * RLow; - ulong T = LHigh * RLow + (Z2 >> 32); - ulong Z1 = T & 0xFFFFFFFF; - ulong Z0 = T >> 32; - - Z1 += LLow * RHigh; - - return LHigh * RHigh + Z0 + (Z1 >> 32); - } -#endregion - } -} diff --git a/Instruction/ASoftFloat.cs b/Instruction/ASoftFloat.cs deleted file mode 100644 index 0912257..0000000 --- a/Instruction/ASoftFloat.cs +++ /dev/null @@ -1,2127 +0,0 @@ -using ChocolArm64.State; -using System; -using System.Diagnostics; -using System.Runtime.CompilerServices; - -namespace ChocolArm64.Instruction -{ - static class ASoftFloat - { - static ASoftFloat() - { - RecipEstimateTable = BuildRecipEstimateTable(); - InvSqrtEstimateTable = BuildInvSqrtEstimateTable(); - } - - private static readonly byte[] RecipEstimateTable; - private static readonly byte[] InvSqrtEstimateTable; - - private static byte[] BuildRecipEstimateTable() - { - byte[] Table = new byte[256]; - for (ulong index = 0; index < 256; index++) - { - ulong a = index | 0x100; - - a = (a << 1) + 1; - ulong b = 0x80000 / a; - b = (b + 1) >> 1; - - Table[index] = (byte)(b & 0xFF); - } - return Table; - } - - private static byte[] BuildInvSqrtEstimateTable() - { - byte[] Table = new byte[512]; - for (ulong index = 128; index < 512; index++) - { - ulong a = index; - if (a < 256) - { - a = (a << 1) + 1; - } - else - { - a = (a | 1) << 1; - } - - ulong b = 256; - while (a * (b + 1) * (b + 1) < (1ul << 28)) - { - b++; - } - b = (b + 1) >> 1; - - Table[index] = (byte)(b & 0xFF); - } - return Table; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float RecipEstimate(float x) - { - return (float)RecipEstimate((double)x); - } - - public static double RecipEstimate(double x) - { - ulong x_bits = (ulong)BitConverter.DoubleToInt64Bits(x); - ulong x_sign = x_bits & 0x8000000000000000; - ulong x_exp = (x_bits >> 52) & 0x7FF; - ulong scaled = x_bits & ((1ul << 52) - 1); - - if (x_exp >= 2045) - { - if (x_exp == 0x7ff && scaled != 0) - { - // NaN - return BitConverter.Int64BitsToDouble((long)(x_bits | 0x0008000000000000)); - } - - // Infinity, or Out of range -> Zero - return BitConverter.Int64BitsToDouble((long)x_sign); - } - - if (x_exp == 0) - { - if (scaled == 0) - { - // Zero -> Infinity - return BitConverter.Int64BitsToDouble((long)(x_sign | 0x7FF0000000000000)); - } - - // Denormal - if ((scaled & (1ul << 51)) == 0) - { - x_exp = ~0ul; - scaled <<= 2; - } - else - { - scaled <<= 1; - } - } - - scaled >>= 44; - scaled &= 0xFF; - - ulong result_exp = (2045 - x_exp) & 0x7FF; - ulong estimate = (ulong)RecipEstimateTable[scaled]; - ulong fraction = estimate << 44; - - if (result_exp == 0) - { - fraction >>= 1; - fraction |= 1ul << 51; - } - else if (result_exp == 0x7FF) - { - result_exp = 0; - fraction >>= 2; - fraction |= 1ul << 50; - } - - ulong result = x_sign | (result_exp << 52) | fraction; - return BitConverter.Int64BitsToDouble((long)result); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float InvSqrtEstimate(float x) - { - return (float)InvSqrtEstimate((double)x); - } - - public static double InvSqrtEstimate(double x) - { - ulong x_bits = (ulong)BitConverter.DoubleToInt64Bits(x); - ulong x_sign = x_bits & 0x8000000000000000; - long x_exp = (long)((x_bits >> 52) & 0x7FF); - ulong scaled = x_bits & ((1ul << 52) - 1); - - if (x_exp == 0x7FF && scaled != 0) - { - // NaN - return BitConverter.Int64BitsToDouble((long)(x_bits | 0x0008000000000000)); - } - - if (x_exp == 0) - { - if (scaled == 0) - { - // Zero -> Infinity - return BitConverter.Int64BitsToDouble((long)(x_sign | 0x7FF0000000000000)); - } - - // Denormal - while ((scaled & (1 << 51)) == 0) - { - scaled <<= 1; - x_exp--; - } - scaled <<= 1; - } - - if (x_sign != 0) - { - // Negative -> NaN - return BitConverter.Int64BitsToDouble((long)0x7FF8000000000000); - } - - if (x_exp == 0x7ff && scaled == 0) - { - // Infinity -> Zero - return BitConverter.Int64BitsToDouble((long)x_sign); - } - - if (((ulong)x_exp & 1) == 1) - { - scaled >>= 45; - scaled &= 0xFF; - scaled |= 0x80; - } - else - { - scaled >>= 44; - scaled &= 0xFF; - scaled |= 0x100; - } - - ulong result_exp = ((ulong)(3068 - x_exp) / 2) & 0x7FF; - ulong estimate = (ulong)InvSqrtEstimateTable[scaled]; - ulong fraction = estimate << 44; - - ulong result = x_sign | (result_exp << 52) | fraction; - return BitConverter.Int64BitsToDouble((long)result); - } - } - - static class ASoftFloat16_32 - { - public static float FPConvert(ushort ValueBits, AThreadState State) - { - Debug.WriteLineIf(State.Fpcr != 0, $"ASoftFloat16_32.FPConvert: State.Fpcr = 0x{State.Fpcr:X8}"); - - double Real = ValueBits.FPUnpackCV(out FPType Type, out bool Sign, State); - - float Result; - - if (Type == FPType.SNaN || Type == FPType.QNaN) - { - if (State.GetFpcrFlag(FPCR.DN)) - { - Result = FPDefaultNaN(); - } - else - { - Result = FPConvertNaN(ValueBits); - } - - if (Type == FPType.SNaN) - { - FPProcessException(FPExc.InvalidOp, State); - } - } - else if (Type == FPType.Infinity) - { - Result = FPInfinity(Sign); - } - else if (Type == FPType.Zero) - { - Result = FPZero(Sign); - } - else - { - Result = FPRoundCV(Real, State); - } - - return Result; - } - - private static float FPDefaultNaN() - { - return -float.NaN; - } - - private static float FPInfinity(bool Sign) - { - return Sign ? float.NegativeInfinity : float.PositiveInfinity; - } - - private static float FPZero(bool Sign) - { - return Sign ? -0f : +0f; - } - - private static float FPMaxNormal(bool Sign) - { - return Sign ? float.MinValue : float.MaxValue; - } - - private static double FPUnpackCV(this ushort ValueBits, out FPType Type, out bool Sign, AThreadState State) - { - Sign = (~(uint)ValueBits & 0x8000u) == 0u; - - uint Exp16 = ((uint)ValueBits & 0x7C00u) >> 10; - uint Frac16 = (uint)ValueBits & 0x03FFu; - - double Real; - - if (Exp16 == 0u) - { - if (Frac16 == 0u) - { - Type = FPType.Zero; - Real = 0d; - } - else - { - Type = FPType.Nonzero; // Subnormal. - Real = Math.Pow(2d, -14) * ((double)Frac16 * Math.Pow(2d, -10)); - } - } - else if (Exp16 == 0x1Fu && !State.GetFpcrFlag(FPCR.AHP)) - { - if (Frac16 == 0u) - { - Type = FPType.Infinity; - Real = Math.Pow(2d, 1000); - } - else - { - Type = (~Frac16 & 0x0200u) == 0u ? FPType.QNaN : FPType.SNaN; - Real = 0d; - } - } - else - { - Type = FPType.Nonzero; // Normal. - Real = Math.Pow(2d, (int)Exp16 - 15) * (1d + (double)Frac16 * Math.Pow(2d, -10)); - } - - return Sign ? -Real : Real; - } - - private static float FPRoundCV(double Real, AThreadState State) - { - const int MinimumExp = -126; - - const int E = 8; - const int F = 23; - - bool Sign; - double Mantissa; - - if (Real < 0d) - { - Sign = true; - Mantissa = -Real; - } - else - { - Sign = false; - Mantissa = Real; - } - - int Exponent = 0; - - while (Mantissa < 1d) - { - Mantissa *= 2d; - Exponent--; - } - - while (Mantissa >= 2d) - { - Mantissa /= 2d; - Exponent++; - } - - if (State.GetFpcrFlag(FPCR.FZ) && Exponent < MinimumExp) - { - State.SetFpsrFlag(FPSR.UFC); - - return FPZero(Sign); - } - - uint BiasedExp = (uint)Math.Max(Exponent - MinimumExp + 1, 0); - - if (BiasedExp == 0u) - { - Mantissa /= Math.Pow(2d, MinimumExp - Exponent); - } - - uint IntMant = (uint)Math.Floor(Mantissa * Math.Pow(2d, F)); - double Error = Mantissa * Math.Pow(2d, F) - (double)IntMant; - - if (BiasedExp == 0u && (Error != 0d || State.GetFpcrFlag(FPCR.UFE))) - { - FPProcessException(FPExc.Underflow, State); - } - - bool OverflowToInf; - bool RoundUp; - - switch (State.FPRoundingMode()) - { - default: - case ARoundMode.ToNearest: - RoundUp = (Error > 0.5d || (Error == 0.5d && (IntMant & 1u) == 1u)); - OverflowToInf = true; - break; - - case ARoundMode.TowardsPlusInfinity: - RoundUp = (Error != 0d && !Sign); - OverflowToInf = !Sign; - break; - - case ARoundMode.TowardsMinusInfinity: - RoundUp = (Error != 0d && Sign); - OverflowToInf = Sign; - break; - - case ARoundMode.TowardsZero: - RoundUp = false; - OverflowToInf = false; - break; - } - - if (RoundUp) - { - IntMant++; - - if (IntMant == (uint)Math.Pow(2d, F)) - { - BiasedExp = 1u; - } - - if (IntMant == (uint)Math.Pow(2d, F + 1)) - { - BiasedExp++; - IntMant >>= 1; - } - } - - float Result; - - if (BiasedExp >= (uint)Math.Pow(2d, E) - 1u) - { - Result = OverflowToInf ? FPInfinity(Sign) : FPMaxNormal(Sign); - - FPProcessException(FPExc.Overflow, State); - - Error = 1d; - } - else - { - Result = BitConverter.Int32BitsToSingle( - (int)((Sign ? 1u : 0u) << 31 | (BiasedExp & 0xFFu) << 23 | (IntMant & 0x007FFFFFu))); - } - - if (Error != 0d) - { - FPProcessException(FPExc.Inexact, State); - } - - return Result; - } - - private static float FPConvertNaN(ushort ValueBits) - { - return BitConverter.Int32BitsToSingle( - (int)(((uint)ValueBits & 0x8000u) << 16 | 0x7FC00000u | ((uint)ValueBits & 0x01FFu) << 13)); - } - - private static void FPProcessException(FPExc Exc, AThreadState State) - { - int Enable = (int)Exc + 8; - - if ((State.Fpcr & (1 << Enable)) != 0) - { - throw new NotImplementedException("floating-point trap handling"); - } - else - { - State.Fpsr |= 1 << (int)Exc; - } - } - } - - static class ASoftFloat32_16 - { - public static ushort FPConvert(float Value, AThreadState State) - { - Debug.WriteLineIf(State.Fpcr != 0, $"ASoftFloat32_16.FPConvert: State.Fpcr = 0x{State.Fpcr:X8}"); - - double Real = Value.FPUnpackCV(out FPType Type, out bool Sign, State, out uint ValueBits); - - bool AltHp = State.GetFpcrFlag(FPCR.AHP); - - ushort ResultBits; - - if (Type == FPType.SNaN || Type == FPType.QNaN) - { - if (AltHp) - { - ResultBits = FPZero(Sign); - } - else if (State.GetFpcrFlag(FPCR.DN)) - { - ResultBits = FPDefaultNaN(); - } - else - { - ResultBits = FPConvertNaN(ValueBits); - } - - if (Type == FPType.SNaN || AltHp) - { - FPProcessException(FPExc.InvalidOp, State); - } - } - else if (Type == FPType.Infinity) - { - if (AltHp) - { - ResultBits = (ushort)((Sign ? 1u : 0u) << 15 | 0x7FFFu); - - FPProcessException(FPExc.InvalidOp, State); - } - else - { - ResultBits = FPInfinity(Sign); - } - } - else if (Type == FPType.Zero) - { - ResultBits = FPZero(Sign); - } - else - { - ResultBits = FPRoundCV(Real, State); - } - - return ResultBits; - } - - private static ushort FPDefaultNaN() - { - return (ushort)0x7E00u; - } - - private static ushort FPInfinity(bool Sign) - { - return Sign ? (ushort)0xFC00u : (ushort)0x7C00u; - } - - private static ushort FPZero(bool Sign) - { - return Sign ? (ushort)0x8000u : (ushort)0x0000u; - } - - private static ushort FPMaxNormal(bool Sign) - { - return Sign ? (ushort)0xFBFFu : (ushort)0x7BFFu; - } - - private static double FPUnpackCV(this float Value, out FPType Type, out bool Sign, AThreadState State, out uint ValueBits) - { - ValueBits = (uint)BitConverter.SingleToInt32Bits(Value); - - Sign = (~ValueBits & 0x80000000u) == 0u; - - uint Exp32 = (ValueBits & 0x7F800000u) >> 23; - uint Frac32 = ValueBits & 0x007FFFFFu; - - double Real; - - if (Exp32 == 0u) - { - if (Frac32 == 0u || State.GetFpcrFlag(FPCR.FZ)) - { - Type = FPType.Zero; - Real = 0d; - - if (Frac32 != 0u) FPProcessException(FPExc.InputDenorm, State); - } - else - { - Type = FPType.Nonzero; // Subnormal. - Real = Math.Pow(2d, -126) * ((double)Frac32 * Math.Pow(2d, -23)); - } - } - else if (Exp32 == 0xFFu) - { - if (Frac32 == 0u) - { - Type = FPType.Infinity; - Real = Math.Pow(2d, 1000); - } - else - { - Type = (~Frac32 & 0x00400000u) == 0u ? FPType.QNaN : FPType.SNaN; - Real = 0d; - } - } - else - { - Type = FPType.Nonzero; // Normal. - Real = Math.Pow(2d, (int)Exp32 - 127) * (1d + (double)Frac32 * Math.Pow(2d, -23)); - } - - return Sign ? -Real : Real; - } - - private static ushort FPRoundCV(double Real, AThreadState State) - { - const int MinimumExp = -14; - - const int E = 5; - const int F = 10; - - bool Sign; - double Mantissa; - - if (Real < 0d) - { - Sign = true; - Mantissa = -Real; - } - else - { - Sign = false; - Mantissa = Real; - } - - int Exponent = 0; - - while (Mantissa < 1d) - { - Mantissa *= 2d; - Exponent--; - } - - while (Mantissa >= 2d) - { - Mantissa /= 2d; - Exponent++; - } - - uint BiasedExp = (uint)Math.Max(Exponent - MinimumExp + 1, 0); - - if (BiasedExp == 0u) - { - Mantissa /= Math.Pow(2d, MinimumExp - Exponent); - } - - uint IntMant = (uint)Math.Floor(Mantissa * Math.Pow(2d, F)); - double Error = Mantissa * Math.Pow(2d, F) - (double)IntMant; - - if (BiasedExp == 0u && (Error != 0d || State.GetFpcrFlag(FPCR.UFE))) - { - FPProcessException(FPExc.Underflow, State); - } - - bool OverflowToInf; - bool RoundUp; - - switch (State.FPRoundingMode()) - { - default: - case ARoundMode.ToNearest: - RoundUp = (Error > 0.5d || (Error == 0.5d && (IntMant & 1u) == 1u)); - OverflowToInf = true; - break; - - case ARoundMode.TowardsPlusInfinity: - RoundUp = (Error != 0d && !Sign); - OverflowToInf = !Sign; - break; - - case ARoundMode.TowardsMinusInfinity: - RoundUp = (Error != 0d && Sign); - OverflowToInf = Sign; - break; - - case ARoundMode.TowardsZero: - RoundUp = false; - OverflowToInf = false; - break; - } - - if (RoundUp) - { - IntMant++; - - if (IntMant == (uint)Math.Pow(2d, F)) - { - BiasedExp = 1u; - } - - if (IntMant == (uint)Math.Pow(2d, F + 1)) - { - BiasedExp++; - IntMant >>= 1; - } - } - - ushort ResultBits; - - if (!State.GetFpcrFlag(FPCR.AHP)) - { - if (BiasedExp >= (uint)Math.Pow(2d, E) - 1u) - { - ResultBits = OverflowToInf ? FPInfinity(Sign) : FPMaxNormal(Sign); - - FPProcessException(FPExc.Overflow, State); - - Error = 1d; - } - else - { - ResultBits = (ushort)((Sign ? 1u : 0u) << 15 | (BiasedExp & 0x1Fu) << 10 | (IntMant & 0x03FFu)); - } - } - else - { - if (BiasedExp >= (uint)Math.Pow(2d, E)) - { - ResultBits = (ushort)((Sign ? 1u : 0u) << 15 | 0x7FFFu); - - FPProcessException(FPExc.InvalidOp, State); - - Error = 0d; - } - else - { - ResultBits = (ushort)((Sign ? 1u : 0u) << 15 | (BiasedExp & 0x1Fu) << 10 | (IntMant & 0x03FFu)); - } - } - - if (Error != 0d) - { - FPProcessException(FPExc.Inexact, State); - } - - return ResultBits; - } - - private static ushort FPConvertNaN(uint ValueBits) - { - return (ushort)((ValueBits & 0x80000000u) >> 16 | 0x7E00u | (ValueBits & 0x003FE000u) >> 13); - } - - private static void FPProcessException(FPExc Exc, AThreadState State) - { - int Enable = (int)Exc + 8; - - if ((State.Fpcr & (1 << Enable)) != 0) - { - throw new NotImplementedException("floating-point trap handling"); - } - else - { - State.Fpsr |= 1 << (int)Exc; - } - } - } - - static class ASoftFloat_32 - { - public static float FPAdd(float Value1, float Value2, AThreadState State) - { - Debug.WriteLineIf(State.Fpcr != 0, $"ASoftFloat_32.FPAdd: State.Fpcr = 0x{State.Fpcr:X8}"); - - Value1 = Value1.FPUnpack(out FPType Type1, out bool Sign1, out uint Op1); - Value2 = Value2.FPUnpack(out FPType Type2, out bool Sign2, out uint Op2); - - float Result = FPProcessNaNs(Type1, Type2, Op1, Op2, State, out bool Done); - - if (!Done) - { - bool Inf1 = Type1 == FPType.Infinity; bool Zero1 = Type1 == FPType.Zero; - bool Inf2 = Type2 == FPType.Infinity; bool Zero2 = Type2 == FPType.Zero; - - if (Inf1 && Inf2 && Sign1 == !Sign2) - { - Result = FPDefaultNaN(); - - FPProcessException(FPExc.InvalidOp, State); - } - else if ((Inf1 && !Sign1) || (Inf2 && !Sign2)) - { - Result = FPInfinity(false); - } - else if ((Inf1 && Sign1) || (Inf2 && Sign2)) - { - Result = FPInfinity(true); - } - else if (Zero1 && Zero2 && Sign1 == Sign2) - { - Result = FPZero(Sign1); - } - else - { - Result = Value1 + Value2; - } - } - - return Result; - } - - public static float FPDiv(float Value1, float Value2, AThreadState State) - { - Debug.WriteLineIf(State.Fpcr != 0, $"ASoftFloat_32.FPDiv: State.Fpcr = 0x{State.Fpcr:X8}"); - - Value1 = Value1.FPUnpack(out FPType Type1, out bool Sign1, out uint Op1); - Value2 = Value2.FPUnpack(out FPType Type2, out bool Sign2, out uint Op2); - - float Result = FPProcessNaNs(Type1, Type2, Op1, Op2, State, out bool Done); - - if (!Done) - { - bool Inf1 = Type1 == FPType.Infinity; bool Zero1 = Type1 == FPType.Zero; - bool Inf2 = Type2 == FPType.Infinity; bool Zero2 = Type2 == FPType.Zero; - - if ((Inf1 && Inf2) || (Zero1 && Zero2)) - { - Result = FPDefaultNaN(); - - FPProcessException(FPExc.InvalidOp, State); - } - else if (Inf1 || Zero2) - { - Result = FPInfinity(Sign1 ^ Sign2); - - if (!Inf1) FPProcessException(FPExc.DivideByZero, State); - } - else if (Zero1 || Inf2) - { - Result = FPZero(Sign1 ^ Sign2); - } - else - { - Result = Value1 / Value2; - } - } - - return Result; - } - - public static float FPMax(float Value1, float Value2, AThreadState State) - { - Debug.WriteLineIf(State.Fpcr != 0, $"ASoftFloat_32.FPMax: State.Fpcr = 0x{State.Fpcr:X8}"); - - Value1 = Value1.FPUnpack(out FPType Type1, out bool Sign1, out uint Op1); - Value2 = Value2.FPUnpack(out FPType Type2, out bool Sign2, out uint Op2); - - float Result = FPProcessNaNs(Type1, Type2, Op1, Op2, State, out bool Done); - - if (!Done) - { - if (Value1 > Value2) - { - if (Type1 == FPType.Infinity) - { - Result = FPInfinity(Sign1); - } - else if (Type1 == FPType.Zero) - { - Result = FPZero(Sign1 && Sign2); - } - else - { - Result = Value1; - } - } - else - { - if (Type2 == FPType.Infinity) - { - Result = FPInfinity(Sign2); - } - else if (Type2 == FPType.Zero) - { - Result = FPZero(Sign1 && Sign2); - } - else - { - Result = Value2; - } - } - } - - return Result; - } - - public static float FPMaxNum(float Value1, float Value2, AThreadState State) - { - Debug.WriteIf(State.Fpcr != 0, "ASoftFloat_32.FPMaxNum: "); - - Value1.FPUnpack(out FPType Type1, out _, out _); - Value2.FPUnpack(out FPType Type2, out _, out _); - - if (Type1 == FPType.QNaN && Type2 != FPType.QNaN) - { - Value1 = FPInfinity(true); - } - else if (Type1 != FPType.QNaN && Type2 == FPType.QNaN) - { - Value2 = FPInfinity(true); - } - - return FPMax(Value1, Value2, State); - } - - public static float FPMin(float Value1, float Value2, AThreadState State) - { - Debug.WriteLineIf(State.Fpcr != 0, $"ASoftFloat_32.FPMin: State.Fpcr = 0x{State.Fpcr:X8}"); - - Value1 = Value1.FPUnpack(out FPType Type1, out bool Sign1, out uint Op1); - Value2 = Value2.FPUnpack(out FPType Type2, out bool Sign2, out uint Op2); - - float Result = FPProcessNaNs(Type1, Type2, Op1, Op2, State, out bool Done); - - if (!Done) - { - if (Value1 < Value2) - { - if (Type1 == FPType.Infinity) - { - Result = FPInfinity(Sign1); - } - else if (Type1 == FPType.Zero) - { - Result = FPZero(Sign1 || Sign2); - } - else - { - Result = Value1; - } - } - else - { - if (Type2 == FPType.Infinity) - { - Result = FPInfinity(Sign2); - } - else if (Type2 == FPType.Zero) - { - Result = FPZero(Sign1 || Sign2); - } - else - { - Result = Value2; - } - } - } - - return Result; - } - - public static float FPMinNum(float Value1, float Value2, AThreadState State) - { - Debug.WriteIf(State.Fpcr != 0, "ASoftFloat_32.FPMinNum: "); - - Value1.FPUnpack(out FPType Type1, out _, out _); - Value2.FPUnpack(out FPType Type2, out _, out _); - - if (Type1 == FPType.QNaN && Type2 != FPType.QNaN) - { - Value1 = FPInfinity(false); - } - else if (Type1 != FPType.QNaN && Type2 == FPType.QNaN) - { - Value2 = FPInfinity(false); - } - - return FPMin(Value1, Value2, State); - } - - public static float FPMul(float Value1, float Value2, AThreadState State) - { - Debug.WriteLineIf(State.Fpcr != 0, $"ASoftFloat_32.FPMul: State.Fpcr = 0x{State.Fpcr:X8}"); - - Value1 = Value1.FPUnpack(out FPType Type1, out bool Sign1, out uint Op1); - Value2 = Value2.FPUnpack(out FPType Type2, out bool Sign2, out uint Op2); - - float Result = FPProcessNaNs(Type1, Type2, Op1, Op2, State, out bool Done); - - if (!Done) - { - bool Inf1 = Type1 == FPType.Infinity; bool Zero1 = Type1 == FPType.Zero; - bool Inf2 = Type2 == FPType.Infinity; bool Zero2 = Type2 == FPType.Zero; - - if ((Inf1 && Zero2) || (Zero1 && Inf2)) - { - Result = FPDefaultNaN(); - - FPProcessException(FPExc.InvalidOp, State); - } - else if (Inf1 || Inf2) - { - Result = FPInfinity(Sign1 ^ Sign2); - } - else if (Zero1 || Zero2) - { - Result = FPZero(Sign1 ^ Sign2); - } - else - { - Result = Value1 * Value2; - } - } - - return Result; - } - - public static float FPMulAdd(float ValueA, float Value1, float Value2, AThreadState State) - { - Debug.WriteLineIf(State.Fpcr != 0, $"ASoftFloat_32.FPMulAdd: State.Fpcr = 0x{State.Fpcr:X8}"); - - ValueA = ValueA.FPUnpack(out FPType TypeA, out bool SignA, out uint Addend); - Value1 = Value1.FPUnpack(out FPType Type1, out bool Sign1, out uint Op1); - Value2 = Value2.FPUnpack(out FPType Type2, out bool Sign2, out uint Op2); - - bool Inf1 = Type1 == FPType.Infinity; bool Zero1 = Type1 == FPType.Zero; - bool Inf2 = Type2 == FPType.Infinity; bool Zero2 = Type2 == FPType.Zero; - - float Result = FPProcessNaNs3(TypeA, Type1, Type2, Addend, Op1, Op2, State, out bool Done); - - if (TypeA == FPType.QNaN && ((Inf1 && Zero2) || (Zero1 && Inf2))) - { - Result = FPDefaultNaN(); - - FPProcessException(FPExc.InvalidOp, State); - } - - if (!Done) - { - bool InfA = TypeA == FPType.Infinity; bool ZeroA = TypeA == FPType.Zero; - - bool SignP = Sign1 ^ Sign2; - bool InfP = Inf1 || Inf2; - bool ZeroP = Zero1 || Zero2; - - if ((Inf1 && Zero2) || (Zero1 && Inf2) || (InfA && InfP && SignA != SignP)) - { - Result = FPDefaultNaN(); - - FPProcessException(FPExc.InvalidOp, State); - } - else if ((InfA && !SignA) || (InfP && !SignP)) - { - Result = FPInfinity(false); - } - else if ((InfA && SignA) || (InfP && SignP)) - { - Result = FPInfinity(true); - } - else if (ZeroA && ZeroP && SignA == SignP) - { - Result = FPZero(SignA); - } - else - { - // TODO: When available, use: T MathF.FusedMultiplyAdd(T, T, T); - // https://github.com/dotnet/corefx/issues/31903 - - Result = ValueA + (Value1 * Value2); - } - } - - return Result; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float FPMulSub(float ValueA, float Value1, float Value2, AThreadState State) - { - Debug.WriteIf(State.Fpcr != 0, "ASoftFloat_32.FPMulSub: "); - - Value1 = Value1.FPNeg(); - - return FPMulAdd(ValueA, Value1, Value2, State); - } - - public static float FPMulX(float Value1, float Value2, AThreadState State) - { - Debug.WriteLineIf(State.Fpcr != 0, $"ASoftFloat_32.FPMulX: State.Fpcr = 0x{State.Fpcr:X8}"); - - Value1 = Value1.FPUnpack(out FPType Type1, out bool Sign1, out uint Op1); - Value2 = Value2.FPUnpack(out FPType Type2, out bool Sign2, out uint Op2); - - float Result = FPProcessNaNs(Type1, Type2, Op1, Op2, State, out bool Done); - - if (!Done) - { - bool Inf1 = Type1 == FPType.Infinity; bool Zero1 = Type1 == FPType.Zero; - bool Inf2 = Type2 == FPType.Infinity; bool Zero2 = Type2 == FPType.Zero; - - if ((Inf1 && Zero2) || (Zero1 && Inf2)) - { - Result = FPTwo(Sign1 ^ Sign2); - } - else if (Inf1 || Inf2) - { - Result = FPInfinity(Sign1 ^ Sign2); - } - else if (Zero1 || Zero2) - { - Result = FPZero(Sign1 ^ Sign2); - } - else - { - Result = Value1 * Value2; - } - } - - return Result; - } - - public static float FPRecipStepFused(float Value1, float Value2, AThreadState State) - { - Debug.WriteLineIf(State.Fpcr != 0, $"ASoftFloat_32.FPRecipStepFused: State.Fpcr = 0x{State.Fpcr:X8}"); - - Value1 = Value1.FPNeg(); - - Value1 = Value1.FPUnpack(out FPType Type1, out bool Sign1, out uint Op1); - Value2 = Value2.FPUnpack(out FPType Type2, out bool Sign2, out uint Op2); - - float Result = FPProcessNaNs(Type1, Type2, Op1, Op2, State, out bool Done); - - if (!Done) - { - bool Inf1 = Type1 == FPType.Infinity; bool Zero1 = Type1 == FPType.Zero; - bool Inf2 = Type2 == FPType.Infinity; bool Zero2 = Type2 == FPType.Zero; - - if ((Inf1 && Zero2) || (Zero1 && Inf2)) - { - Result = FPTwo(false); - } - else if (Inf1 || Inf2) - { - Result = FPInfinity(Sign1 ^ Sign2); - } - else - { - // TODO: When available, use: T MathF.FusedMultiplyAdd(T, T, T); - // https://github.com/dotnet/corefx/issues/31903 - - Result = 2f + (Value1 * Value2); - } - } - - return Result; - } - - public static float FPRecpX(float Value, AThreadState State) - { - Debug.WriteLineIf(State.Fpcr != 0, $"ASoftFloat_32.FPRecpX: State.Fpcr = 0x{State.Fpcr:X8}"); - - Value.FPUnpack(out FPType Type, out bool Sign, out uint Op); - - float Result; - - if (Type == FPType.SNaN || Type == FPType.QNaN) - { - Result = FPProcessNaN(Type, Op, State); - } - else - { - uint NotExp = (~Op >> 23) & 0xFFu; - uint MaxExp = 0xFEu; - - Result = BitConverter.Int32BitsToSingle( - (int)((Sign ? 1u : 0u) << 31 | (NotExp == 0xFFu ? MaxExp : NotExp) << 23)); - } - - return Result; - } - - public static float FPRSqrtStepFused(float Value1, float Value2, AThreadState State) - { - Debug.WriteLineIf(State.Fpcr != 0, $"ASoftFloat_32.FPRSqrtStepFused: State.Fpcr = 0x{State.Fpcr:X8}"); - - Value1 = Value1.FPNeg(); - - Value1 = Value1.FPUnpack(out FPType Type1, out bool Sign1, out uint Op1); - Value2 = Value2.FPUnpack(out FPType Type2, out bool Sign2, out uint Op2); - - float Result = FPProcessNaNs(Type1, Type2, Op1, Op2, State, out bool Done); - - if (!Done) - { - bool Inf1 = Type1 == FPType.Infinity; bool Zero1 = Type1 == FPType.Zero; - bool Inf2 = Type2 == FPType.Infinity; bool Zero2 = Type2 == FPType.Zero; - - if ((Inf1 && Zero2) || (Zero1 && Inf2)) - { - Result = FPOnePointFive(false); - } - else if (Inf1 || Inf2) - { - Result = FPInfinity(Sign1 ^ Sign2); - } - else - { - // TODO: When available, use: T MathF.FusedMultiplyAdd(T, T, T); - // https://github.com/dotnet/corefx/issues/31903 - - Result = (3f + (Value1 * Value2)) / 2f; - } - } - - return Result; - } - - public static float FPSqrt(float Value, AThreadState State) - { - Debug.WriteLineIf(State.Fpcr != 0, $"ASoftFloat_32.FPSqrt: State.Fpcr = 0x{State.Fpcr:X8}"); - - Value = Value.FPUnpack(out FPType Type, out bool Sign, out uint Op); - - float Result; - - if (Type == FPType.SNaN || Type == FPType.QNaN) - { - Result = FPProcessNaN(Type, Op, State); - } - else if (Type == FPType.Zero) - { - Result = FPZero(Sign); - } - else if (Type == FPType.Infinity && !Sign) - { - Result = FPInfinity(Sign); - } - else if (Sign) - { - Result = FPDefaultNaN(); - - FPProcessException(FPExc.InvalidOp, State); - } - else - { - Result = MathF.Sqrt(Value); - } - - return Result; - } - - public static float FPSub(float Value1, float Value2, AThreadState State) - { - Debug.WriteLineIf(State.Fpcr != 0, $"ASoftFloat_32.FPSub: State.Fpcr = 0x{State.Fpcr:X8}"); - - Value1 = Value1.FPUnpack(out FPType Type1, out bool Sign1, out uint Op1); - Value2 = Value2.FPUnpack(out FPType Type2, out bool Sign2, out uint Op2); - - float Result = FPProcessNaNs(Type1, Type2, Op1, Op2, State, out bool Done); - - if (!Done) - { - bool Inf1 = Type1 == FPType.Infinity; bool Zero1 = Type1 == FPType.Zero; - bool Inf2 = Type2 == FPType.Infinity; bool Zero2 = Type2 == FPType.Zero; - - if (Inf1 && Inf2 && Sign1 == Sign2) - { - Result = FPDefaultNaN(); - - FPProcessException(FPExc.InvalidOp, State); - } - else if ((Inf1 && !Sign1) || (Inf2 && Sign2)) - { - Result = FPInfinity(false); - } - else if ((Inf1 && Sign1) || (Inf2 && !Sign2)) - { - Result = FPInfinity(true); - } - else if (Zero1 && Zero2 && Sign1 == !Sign2) - { - Result = FPZero(Sign1); - } - else - { - Result = Value1 - Value2; - } - } - - return Result; - } - - private static float FPDefaultNaN() - { - return -float.NaN; - } - - private static float FPInfinity(bool Sign) - { - return Sign ? float.NegativeInfinity : float.PositiveInfinity; - } - - private static float FPZero(bool Sign) - { - return Sign ? -0f : +0f; - } - - private static float FPTwo(bool Sign) - { - return Sign ? -2f : +2f; - } - - private static float FPOnePointFive(bool Sign) - { - return Sign ? -1.5f : +1.5f; - } - - private static float FPNeg(this float Value) - { - return -Value; - } - - private static float FPUnpack(this float Value, out FPType Type, out bool Sign, out uint ValueBits) - { - ValueBits = (uint)BitConverter.SingleToInt32Bits(Value); - - Sign = (~ValueBits & 0x80000000u) == 0u; - - if ((ValueBits & 0x7F800000u) == 0u) - { - if ((ValueBits & 0x007FFFFFu) == 0u) - { - Type = FPType.Zero; - } - else - { - Type = FPType.Nonzero; - } - } - else if ((~ValueBits & 0x7F800000u) == 0u) - { - if ((ValueBits & 0x007FFFFFu) == 0u) - { - Type = FPType.Infinity; - } - else - { - Type = (~ValueBits & 0x00400000u) == 0u - ? FPType.QNaN - : FPType.SNaN; - - return FPZero(Sign); - } - } - else - { - Type = FPType.Nonzero; - } - - return Value; - } - - private static float FPProcessNaNs( - FPType Type1, - FPType Type2, - uint Op1, - uint Op2, - AThreadState State, - out bool Done) - { - Done = true; - - if (Type1 == FPType.SNaN) - { - return FPProcessNaN(Type1, Op1, State); - } - else if (Type2 == FPType.SNaN) - { - return FPProcessNaN(Type2, Op2, State); - } - else if (Type1 == FPType.QNaN) - { - return FPProcessNaN(Type1, Op1, State); - } - else if (Type2 == FPType.QNaN) - { - return FPProcessNaN(Type2, Op2, State); - } - - Done = false; - - return FPZero(false); - } - - private static float FPProcessNaNs3( - FPType Type1, - FPType Type2, - FPType Type3, - uint Op1, - uint Op2, - uint Op3, - AThreadState State, - out bool Done) - { - Done = true; - - if (Type1 == FPType.SNaN) - { - return FPProcessNaN(Type1, Op1, State); - } - else if (Type2 == FPType.SNaN) - { - return FPProcessNaN(Type2, Op2, State); - } - else if (Type3 == FPType.SNaN) - { - return FPProcessNaN(Type3, Op3, State); - } - else if (Type1 == FPType.QNaN) - { - return FPProcessNaN(Type1, Op1, State); - } - else if (Type2 == FPType.QNaN) - { - return FPProcessNaN(Type2, Op2, State); - } - else if (Type3 == FPType.QNaN) - { - return FPProcessNaN(Type3, Op3, State); - } - - Done = false; - - return FPZero(false); - } - - private static float FPProcessNaN(FPType Type, uint Op, AThreadState State) - { - if (Type == FPType.SNaN) - { - Op |= 1u << 22; - - FPProcessException(FPExc.InvalidOp, State); - } - - if (State.GetFpcrFlag(FPCR.DN)) - { - return FPDefaultNaN(); - } - - return BitConverter.Int32BitsToSingle((int)Op); - } - - private static void FPProcessException(FPExc Exc, AThreadState State) - { - int Enable = (int)Exc + 8; - - if ((State.Fpcr & (1 << Enable)) != 0) - { - throw new NotImplementedException("floating-point trap handling"); - } - else - { - State.Fpsr |= 1 << (int)Exc; - } - } - } - - static class ASoftFloat_64 - { - public static double FPAdd(double Value1, double Value2, AThreadState State) - { - Debug.WriteLineIf(State.Fpcr != 0, $"ASoftFloat_64.FPAdd: State.Fpcr = 0x{State.Fpcr:X8}"); - - Value1 = Value1.FPUnpack(out FPType Type1, out bool Sign1, out ulong Op1); - Value2 = Value2.FPUnpack(out FPType Type2, out bool Sign2, out ulong Op2); - - double Result = FPProcessNaNs(Type1, Type2, Op1, Op2, State, out bool Done); - - if (!Done) - { - bool Inf1 = Type1 == FPType.Infinity; bool Zero1 = Type1 == FPType.Zero; - bool Inf2 = Type2 == FPType.Infinity; bool Zero2 = Type2 == FPType.Zero; - - if (Inf1 && Inf2 && Sign1 == !Sign2) - { - Result = FPDefaultNaN(); - - FPProcessException(FPExc.InvalidOp, State); - } - else if ((Inf1 && !Sign1) || (Inf2 && !Sign2)) - { - Result = FPInfinity(false); - } - else if ((Inf1 && Sign1) || (Inf2 && Sign2)) - { - Result = FPInfinity(true); - } - else if (Zero1 && Zero2 && Sign1 == Sign2) - { - Result = FPZero(Sign1); - } - else - { - Result = Value1 + Value2; - } - } - - return Result; - } - - public static double FPDiv(double Value1, double Value2, AThreadState State) - { - Debug.WriteLineIf(State.Fpcr != 0, $"ASoftFloat_64.FPDiv: State.Fpcr = 0x{State.Fpcr:X8}"); - - Value1 = Value1.FPUnpack(out FPType Type1, out bool Sign1, out ulong Op1); - Value2 = Value2.FPUnpack(out FPType Type2, out bool Sign2, out ulong Op2); - - double Result = FPProcessNaNs(Type1, Type2, Op1, Op2, State, out bool Done); - - if (!Done) - { - bool Inf1 = Type1 == FPType.Infinity; bool Zero1 = Type1 == FPType.Zero; - bool Inf2 = Type2 == FPType.Infinity; bool Zero2 = Type2 == FPType.Zero; - - if ((Inf1 && Inf2) || (Zero1 && Zero2)) - { - Result = FPDefaultNaN(); - - FPProcessException(FPExc.InvalidOp, State); - } - else if (Inf1 || Zero2) - { - Result = FPInfinity(Sign1 ^ Sign2); - - if (!Inf1) FPProcessException(FPExc.DivideByZero, State); - } - else if (Zero1 || Inf2) - { - Result = FPZero(Sign1 ^ Sign2); - } - else - { - Result = Value1 / Value2; - } - } - - return Result; - } - - public static double FPMax(double Value1, double Value2, AThreadState State) - { - Debug.WriteLineIf(State.Fpcr != 0, $"ASoftFloat_64.FPMax: State.Fpcr = 0x{State.Fpcr:X8}"); - - Value1 = Value1.FPUnpack(out FPType Type1, out bool Sign1, out ulong Op1); - Value2 = Value2.FPUnpack(out FPType Type2, out bool Sign2, out ulong Op2); - - double Result = FPProcessNaNs(Type1, Type2, Op1, Op2, State, out bool Done); - - if (!Done) - { - if (Value1 > Value2) - { - if (Type1 == FPType.Infinity) - { - Result = FPInfinity(Sign1); - } - else if (Type1 == FPType.Zero) - { - Result = FPZero(Sign1 && Sign2); - } - else - { - Result = Value1; - } - } - else - { - if (Type2 == FPType.Infinity) - { - Result = FPInfinity(Sign2); - } - else if (Type2 == FPType.Zero) - { - Result = FPZero(Sign1 && Sign2); - } - else - { - Result = Value2; - } - } - } - - return Result; - } - - public static double FPMaxNum(double Value1, double Value2, AThreadState State) - { - Debug.WriteIf(State.Fpcr != 0, "ASoftFloat_64.FPMaxNum: "); - - Value1.FPUnpack(out FPType Type1, out _, out _); - Value2.FPUnpack(out FPType Type2, out _, out _); - - if (Type1 == FPType.QNaN && Type2 != FPType.QNaN) - { - Value1 = FPInfinity(true); - } - else if (Type1 != FPType.QNaN && Type2 == FPType.QNaN) - { - Value2 = FPInfinity(true); - } - - return FPMax(Value1, Value2, State); - } - - public static double FPMin(double Value1, double Value2, AThreadState State) - { - Debug.WriteLineIf(State.Fpcr != 0, $"ASoftFloat_64.FPMin: State.Fpcr = 0x{State.Fpcr:X8}"); - - Value1 = Value1.FPUnpack(out FPType Type1, out bool Sign1, out ulong Op1); - Value2 = Value2.FPUnpack(out FPType Type2, out bool Sign2, out ulong Op2); - - double Result = FPProcessNaNs(Type1, Type2, Op1, Op2, State, out bool Done); - - if (!Done) - { - if (Value1 < Value2) - { - if (Type1 == FPType.Infinity) - { - Result = FPInfinity(Sign1); - } - else if (Type1 == FPType.Zero) - { - Result = FPZero(Sign1 || Sign2); - } - else - { - Result = Value1; - } - } - else - { - if (Type2 == FPType.Infinity) - { - Result = FPInfinity(Sign2); - } - else if (Type2 == FPType.Zero) - { - Result = FPZero(Sign1 || Sign2); - } - else - { - Result = Value2; - } - } - } - - return Result; - } - - public static double FPMinNum(double Value1, double Value2, AThreadState State) - { - Debug.WriteIf(State.Fpcr != 0, "ASoftFloat_64.FPMinNum: "); - - Value1.FPUnpack(out FPType Type1, out _, out _); - Value2.FPUnpack(out FPType Type2, out _, out _); - - if (Type1 == FPType.QNaN && Type2 != FPType.QNaN) - { - Value1 = FPInfinity(false); - } - else if (Type1 != FPType.QNaN && Type2 == FPType.QNaN) - { - Value2 = FPInfinity(false); - } - - return FPMin(Value1, Value2, State); - } - - public static double FPMul(double Value1, double Value2, AThreadState State) - { - Debug.WriteLineIf(State.Fpcr != 0, $"ASoftFloat_64.FPMul: State.Fpcr = 0x{State.Fpcr:X8}"); - - Value1 = Value1.FPUnpack(out FPType Type1, out bool Sign1, out ulong Op1); - Value2 = Value2.FPUnpack(out FPType Type2, out bool Sign2, out ulong Op2); - - double Result = FPProcessNaNs(Type1, Type2, Op1, Op2, State, out bool Done); - - if (!Done) - { - bool Inf1 = Type1 == FPType.Infinity; bool Zero1 = Type1 == FPType.Zero; - bool Inf2 = Type2 == FPType.Infinity; bool Zero2 = Type2 == FPType.Zero; - - if ((Inf1 && Zero2) || (Zero1 && Inf2)) - { - Result = FPDefaultNaN(); - - FPProcessException(FPExc.InvalidOp, State); - } - else if (Inf1 || Inf2) - { - Result = FPInfinity(Sign1 ^ Sign2); - } - else if (Zero1 || Zero2) - { - Result = FPZero(Sign1 ^ Sign2); - } - else - { - Result = Value1 * Value2; - } - } - - return Result; - } - - public static double FPMulAdd(double ValueA, double Value1, double Value2, AThreadState State) - { - Debug.WriteLineIf(State.Fpcr != 0, $"ASoftFloat_64.FPMulAdd: State.Fpcr = 0x{State.Fpcr:X8}"); - - ValueA = ValueA.FPUnpack(out FPType TypeA, out bool SignA, out ulong Addend); - Value1 = Value1.FPUnpack(out FPType Type1, out bool Sign1, out ulong Op1); - Value2 = Value2.FPUnpack(out FPType Type2, out bool Sign2, out ulong Op2); - - bool Inf1 = Type1 == FPType.Infinity; bool Zero1 = Type1 == FPType.Zero; - bool Inf2 = Type2 == FPType.Infinity; bool Zero2 = Type2 == FPType.Zero; - - double Result = FPProcessNaNs3(TypeA, Type1, Type2, Addend, Op1, Op2, State, out bool Done); - - if (TypeA == FPType.QNaN && ((Inf1 && Zero2) || (Zero1 && Inf2))) - { - Result = FPDefaultNaN(); - - FPProcessException(FPExc.InvalidOp, State); - } - - if (!Done) - { - bool InfA = TypeA == FPType.Infinity; bool ZeroA = TypeA == FPType.Zero; - - bool SignP = Sign1 ^ Sign2; - bool InfP = Inf1 || Inf2; - bool ZeroP = Zero1 || Zero2; - - if ((Inf1 && Zero2) || (Zero1 && Inf2) || (InfA && InfP && SignA != SignP)) - { - Result = FPDefaultNaN(); - - FPProcessException(FPExc.InvalidOp, State); - } - else if ((InfA && !SignA) || (InfP && !SignP)) - { - Result = FPInfinity(false); - } - else if ((InfA && SignA) || (InfP && SignP)) - { - Result = FPInfinity(true); - } - else if (ZeroA && ZeroP && SignA == SignP) - { - Result = FPZero(SignA); - } - else - { - // TODO: When available, use: T Math.FusedMultiplyAdd(T, T, T); - // https://github.com/dotnet/corefx/issues/31903 - - Result = ValueA + (Value1 * Value2); - } - } - - return Result; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double FPMulSub(double ValueA, double Value1, double Value2, AThreadState State) - { - Debug.WriteIf(State.Fpcr != 0, "ASoftFloat_64.FPMulSub: "); - - Value1 = Value1.FPNeg(); - - return FPMulAdd(ValueA, Value1, Value2, State); - } - - public static double FPMulX(double Value1, double Value2, AThreadState State) - { - Debug.WriteLineIf(State.Fpcr != 0, $"ASoftFloat_64.FPMulX: State.Fpcr = 0x{State.Fpcr:X8}"); - - Value1 = Value1.FPUnpack(out FPType Type1, out bool Sign1, out ulong Op1); - Value2 = Value2.FPUnpack(out FPType Type2, out bool Sign2, out ulong Op2); - - double Result = FPProcessNaNs(Type1, Type2, Op1, Op2, State, out bool Done); - - if (!Done) - { - bool Inf1 = Type1 == FPType.Infinity; bool Zero1 = Type1 == FPType.Zero; - bool Inf2 = Type2 == FPType.Infinity; bool Zero2 = Type2 == FPType.Zero; - - if ((Inf1 && Zero2) || (Zero1 && Inf2)) - { - Result = FPTwo(Sign1 ^ Sign2); - } - else if (Inf1 || Inf2) - { - Result = FPInfinity(Sign1 ^ Sign2); - } - else if (Zero1 || Zero2) - { - Result = FPZero(Sign1 ^ Sign2); - } - else - { - Result = Value1 * Value2; - } - } - - return Result; - } - - public static double FPRecipStepFused(double Value1, double Value2, AThreadState State) - { - Debug.WriteLineIf(State.Fpcr != 0, $"ASoftFloat_64.FPRecipStepFused: State.Fpcr = 0x{State.Fpcr:X8}"); - - Value1 = Value1.FPNeg(); - - Value1 = Value1.FPUnpack(out FPType Type1, out bool Sign1, out ulong Op1); - Value2 = Value2.FPUnpack(out FPType Type2, out bool Sign2, out ulong Op2); - - double Result = FPProcessNaNs(Type1, Type2, Op1, Op2, State, out bool Done); - - if (!Done) - { - bool Inf1 = Type1 == FPType.Infinity; bool Zero1 = Type1 == FPType.Zero; - bool Inf2 = Type2 == FPType.Infinity; bool Zero2 = Type2 == FPType.Zero; - - if ((Inf1 && Zero2) || (Zero1 && Inf2)) - { - Result = FPTwo(false); - } - else if (Inf1 || Inf2) - { - Result = FPInfinity(Sign1 ^ Sign2); - } - else - { - // TODO: When available, use: T Math.FusedMultiplyAdd(T, T, T); - // https://github.com/dotnet/corefx/issues/31903 - - Result = 2d + (Value1 * Value2); - } - } - - return Result; - } - - public static double FPRecpX(double Value, AThreadState State) - { - Debug.WriteLineIf(State.Fpcr != 0, $"ASoftFloat_64.FPRecpX: State.Fpcr = 0x{State.Fpcr:X8}"); - - Value.FPUnpack(out FPType Type, out bool Sign, out ulong Op); - - double Result; - - if (Type == FPType.SNaN || Type == FPType.QNaN) - { - Result = FPProcessNaN(Type, Op, State); - } - else - { - ulong NotExp = (~Op >> 52) & 0x7FFul; - ulong MaxExp = 0x7FEul; - - Result = BitConverter.Int64BitsToDouble( - (long)((Sign ? 1ul : 0ul) << 63 | (NotExp == 0x7FFul ? MaxExp : NotExp) << 52)); - } - - return Result; - } - - public static double FPRSqrtStepFused(double Value1, double Value2, AThreadState State) - { - Debug.WriteLineIf(State.Fpcr != 0, $"ASoftFloat_64.FPRSqrtStepFused: State.Fpcr = 0x{State.Fpcr:X8}"); - - Value1 = Value1.FPNeg(); - - Value1 = Value1.FPUnpack(out FPType Type1, out bool Sign1, out ulong Op1); - Value2 = Value2.FPUnpack(out FPType Type2, out bool Sign2, out ulong Op2); - - double Result = FPProcessNaNs(Type1, Type2, Op1, Op2, State, out bool Done); - - if (!Done) - { - bool Inf1 = Type1 == FPType.Infinity; bool Zero1 = Type1 == FPType.Zero; - bool Inf2 = Type2 == FPType.Infinity; bool Zero2 = Type2 == FPType.Zero; - - if ((Inf1 && Zero2) || (Zero1 && Inf2)) - { - Result = FPOnePointFive(false); - } - else if (Inf1 || Inf2) - { - Result = FPInfinity(Sign1 ^ Sign2); - } - else - { - // TODO: When available, use: T Math.FusedMultiplyAdd(T, T, T); - // https://github.com/dotnet/corefx/issues/31903 - - Result = (3d + (Value1 * Value2)) / 2d; - } - } - - return Result; - } - - public static double FPSqrt(double Value, AThreadState State) - { - Debug.WriteLineIf(State.Fpcr != 0, $"ASoftFloat_64.FPSqrt: State.Fpcr = 0x{State.Fpcr:X8}"); - - Value = Value.FPUnpack(out FPType Type, out bool Sign, out ulong Op); - - double Result; - - if (Type == FPType.SNaN || Type == FPType.QNaN) - { - Result = FPProcessNaN(Type, Op, State); - } - else if (Type == FPType.Zero) - { - Result = FPZero(Sign); - } - else if (Type == FPType.Infinity && !Sign) - { - Result = FPInfinity(Sign); - } - else if (Sign) - { - Result = FPDefaultNaN(); - - FPProcessException(FPExc.InvalidOp, State); - } - else - { - Result = Math.Sqrt(Value); - } - - return Result; - } - - public static double FPSub(double Value1, double Value2, AThreadState State) - { - Debug.WriteLineIf(State.Fpcr != 0, $"ASoftFloat_64.FPSub: State.Fpcr = 0x{State.Fpcr:X8}"); - - Value1 = Value1.FPUnpack(out FPType Type1, out bool Sign1, out ulong Op1); - Value2 = Value2.FPUnpack(out FPType Type2, out bool Sign2, out ulong Op2); - - double Result = FPProcessNaNs(Type1, Type2, Op1, Op2, State, out bool Done); - - if (!Done) - { - bool Inf1 = Type1 == FPType.Infinity; bool Zero1 = Type1 == FPType.Zero; - bool Inf2 = Type2 == FPType.Infinity; bool Zero2 = Type2 == FPType.Zero; - - if (Inf1 && Inf2 && Sign1 == Sign2) - { - Result = FPDefaultNaN(); - - FPProcessException(FPExc.InvalidOp, State); - } - else if ((Inf1 && !Sign1) || (Inf2 && Sign2)) - { - Result = FPInfinity(false); - } - else if ((Inf1 && Sign1) || (Inf2 && !Sign2)) - { - Result = FPInfinity(true); - } - else if (Zero1 && Zero2 && Sign1 == !Sign2) - { - Result = FPZero(Sign1); - } - else - { - Result = Value1 - Value2; - } - } - - return Result; - } - - private static double FPDefaultNaN() - { - return -double.NaN; - } - - private static double FPInfinity(bool Sign) - { - return Sign ? double.NegativeInfinity : double.PositiveInfinity; - } - - private static double FPZero(bool Sign) - { - return Sign ? -0d : +0d; - } - - private static double FPTwo(bool Sign) - { - return Sign ? -2d : +2d; - } - - private static double FPOnePointFive(bool Sign) - { - return Sign ? -1.5d : +1.5d; - } - - private static double FPNeg(this double Value) - { - return -Value; - } - - private static double FPUnpack(this double Value, out FPType Type, out bool Sign, out ulong ValueBits) - { - ValueBits = (ulong)BitConverter.DoubleToInt64Bits(Value); - - Sign = (~ValueBits & 0x8000000000000000ul) == 0ul; - - if ((ValueBits & 0x7FF0000000000000ul) == 0ul) - { - if ((ValueBits & 0x000FFFFFFFFFFFFFul) == 0ul) - { - Type = FPType.Zero; - } - else - { - Type = FPType.Nonzero; - } - } - else if ((~ValueBits & 0x7FF0000000000000ul) == 0ul) - { - if ((ValueBits & 0x000FFFFFFFFFFFFFul) == 0ul) - { - Type = FPType.Infinity; - } - else - { - Type = (~ValueBits & 0x0008000000000000ul) == 0ul - ? FPType.QNaN - : FPType.SNaN; - - return FPZero(Sign); - } - } - else - { - Type = FPType.Nonzero; - } - - return Value; - } - - private static double FPProcessNaNs( - FPType Type1, - FPType Type2, - ulong Op1, - ulong Op2, - AThreadState State, - out bool Done) - { - Done = true; - - if (Type1 == FPType.SNaN) - { - return FPProcessNaN(Type1, Op1, State); - } - else if (Type2 == FPType.SNaN) - { - return FPProcessNaN(Type2, Op2, State); - } - else if (Type1 == FPType.QNaN) - { - return FPProcessNaN(Type1, Op1, State); - } - else if (Type2 == FPType.QNaN) - { - return FPProcessNaN(Type2, Op2, State); - } - - Done = false; - - return FPZero(false); - } - - private static double FPProcessNaNs3( - FPType Type1, - FPType Type2, - FPType Type3, - ulong Op1, - ulong Op2, - ulong Op3, - AThreadState State, - out bool Done) - { - Done = true; - - if (Type1 == FPType.SNaN) - { - return FPProcessNaN(Type1, Op1, State); - } - else if (Type2 == FPType.SNaN) - { - return FPProcessNaN(Type2, Op2, State); - } - else if (Type3 == FPType.SNaN) - { - return FPProcessNaN(Type3, Op3, State); - } - else if (Type1 == FPType.QNaN) - { - return FPProcessNaN(Type1, Op1, State); - } - else if (Type2 == FPType.QNaN) - { - return FPProcessNaN(Type2, Op2, State); - } - else if (Type3 == FPType.QNaN) - { - return FPProcessNaN(Type3, Op3, State); - } - - Done = false; - - return FPZero(false); - } - - private static double FPProcessNaN(FPType Type, ulong Op, AThreadState State) - { - if (Type == FPType.SNaN) - { - Op |= 1ul << 51; - - FPProcessException(FPExc.InvalidOp, State); - } - - if (State.GetFpcrFlag(FPCR.DN)) - { - return FPDefaultNaN(); - } - - return BitConverter.Int64BitsToDouble((long)Op); - } - - private static void FPProcessException(FPExc Exc, AThreadState State) - { - int Enable = (int)Exc + 8; - - if ((State.Fpcr & (1 << Enable)) != 0) - { - throw new NotImplementedException("floating-point trap handling"); - } - else - { - State.Fpsr |= 1 << (int)Exc; - } - } - } -} diff --git a/Instruction32/A32InstInterpretFlow.cs b/Instruction32/A32InstInterpretFlow.cs deleted file mode 100644 index 223fd18..0000000 --- a/Instruction32/A32InstInterpretFlow.cs +++ /dev/null @@ -1,70 +0,0 @@ -using ChocolArm64.Decoder; -using ChocolArm64.Decoder32; -using ChocolArm64.Memory; -using ChocolArm64.State; - -using static ChocolArm64.Instruction32.A32InstInterpretHelper; - -namespace ChocolArm64.Instruction32 -{ - static partial class A32InstInterpret - { - public static void B(AThreadState State, AMemory Memory, AOpCode OpCode) - { - A32OpCodeBImmAl Op = (A32OpCodeBImmAl)OpCode; - - if (IsConditionTrue(State, Op.Cond)) - { - BranchWritePc(State, GetPc(State) + (uint)Op.Imm); - } - } - - public static void Bl(AThreadState State, AMemory Memory, AOpCode OpCode) - { - Blx(State, Memory, OpCode, false); - } - - public static void Blx(AThreadState State, AMemory Memory, AOpCode OpCode) - { - Blx(State, Memory, OpCode, true); - } - - public static void Blx(AThreadState State, AMemory Memory, AOpCode OpCode, bool X) - { - A32OpCodeBImmAl Op = (A32OpCodeBImmAl)OpCode; - - if (IsConditionTrue(State, Op.Cond)) - { - uint Pc = GetPc(State); - - if (State.Thumb) - { - State.R14 = Pc | 1; - } - else - { - State.R14 = Pc - 4U; - } - - if (X) - { - State.Thumb = !State.Thumb; - } - - if (!State.Thumb) - { - Pc &= ~3U; - } - - BranchWritePc(State, Pc + (uint)Op.Imm); - } - } - - private static void BranchWritePc(AThreadState State, uint Pc) - { - State.R15 = State.Thumb - ? Pc & ~1U - : Pc & ~3U; - } - } -} \ No newline at end of file diff --git a/Instruction32/A32InstInterpretHelper.cs b/Instruction32/A32InstInterpretHelper.cs deleted file mode 100644 index 9c3c098..0000000 --- a/Instruction32/A32InstInterpretHelper.cs +++ /dev/null @@ -1,65 +0,0 @@ -using ChocolArm64.Decoder; -using ChocolArm64.State; -using System; - -namespace ChocolArm64.Instruction32 -{ - static class A32InstInterpretHelper - { - public static bool IsConditionTrue(AThreadState State, ACond Cond) - { - switch (Cond) - { - case ACond.Eq: return State.Zero; - case ACond.Ne: return !State.Zero; - case ACond.Ge_Un: return State.Carry; - case ACond.Lt_Un: return !State.Carry; - case ACond.Mi: return State.Negative; - case ACond.Pl: return !State.Negative; - case ACond.Vs: return State.Overflow; - case ACond.Vc: return !State.Overflow; - case ACond.Gt_Un: return State.Carry && !State.Zero; - case ACond.Le_Un: return !State.Carry && State.Zero; - case ACond.Ge: return State.Negative == State.Overflow; - case ACond.Lt: return State.Negative != State.Overflow; - case ACond.Gt: return State.Negative == State.Overflow && !State.Zero; - case ACond.Le: return State.Negative != State.Overflow && State.Zero; - } - - return true; - } - - public unsafe static uint GetReg(AThreadState State, int Reg) - { - if ((uint)Reg > 15) - { - throw new ArgumentOutOfRangeException(nameof(Reg)); - } - - fixed (uint* Ptr = &State.R0) - { - return *(Ptr + Reg); - } - } - - public unsafe static void SetReg(AThreadState State, int Reg, uint Value) - { - if ((uint)Reg > 15) - { - throw new ArgumentOutOfRangeException(nameof(Reg)); - } - - fixed (uint* Ptr = &State.R0) - { - *(Ptr + Reg) = Value; - } - } - - public static uint GetPc(AThreadState State) - { - //Due to the old fetch-decode-execute pipeline of old ARM CPUs, - //the PC is 4 or 8 bytes (2 instructions) ahead of the current instruction. - return State.R15 + (State.Thumb ? 2U : 4U); - } - } -} \ No newline at end of file diff --git a/Instruction/ACryptoHelper.cs b/Instructions/CryptoHelper.cs similarity index 75% rename from Instruction/ACryptoHelper.cs rename to Instructions/CryptoHelper.cs index 2dc6597..bb9a22a 100644 --- a/Instruction/ACryptoHelper.cs +++ b/Instructions/CryptoHelper.cs @@ -4,12 +4,12 @@ using System; using System.Runtime.Intrinsics; using System.Runtime.Intrinsics.X86; -namespace ChocolArm64.Instruction +namespace ChocolArm64.Instructions { - static class ACryptoHelper + static class CryptoHelper { #region "LookUp Tables" - private static byte[] SBox = + private static byte[] _sBox = { 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, @@ -29,7 +29,7 @@ namespace ChocolArm64.Instruction 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 }; - private static byte[] InvSBox = + private static byte[] _invSBox = { 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, @@ -49,7 +49,7 @@ namespace ChocolArm64.Instruction 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d }; - private static byte[] GFMul_02 = + private static byte[] _gfMul02 = { 0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e, 0x20, 0x22, 0x24, 0x26, 0x28, 0x2a, 0x2c, 0x2e, 0x30, 0x32, 0x34, 0x36, 0x38, 0x3a, 0x3c, 0x3e, @@ -69,7 +69,7 @@ namespace ChocolArm64.Instruction 0xfb, 0xf9, 0xff, 0xfd, 0xf3, 0xf1, 0xf7, 0xf5, 0xeb, 0xe9, 0xef, 0xed, 0xe3, 0xe1, 0xe7, 0xe5 }; - private static byte[] GFMul_03 = + private static byte[] _gfMul03 = { 0x00, 0x03, 0x06, 0x05, 0x0c, 0x0f, 0x0a, 0x09, 0x18, 0x1b, 0x1e, 0x1d, 0x14, 0x17, 0x12, 0x11, 0x30, 0x33, 0x36, 0x35, 0x3c, 0x3f, 0x3a, 0x39, 0x28, 0x2b, 0x2e, 0x2d, 0x24, 0x27, 0x22, 0x21, @@ -89,7 +89,7 @@ namespace ChocolArm64.Instruction 0x0b, 0x08, 0x0d, 0x0e, 0x07, 0x04, 0x01, 0x02, 0x13, 0x10, 0x15, 0x16, 0x1f, 0x1c, 0x19, 0x1a }; - private static byte[] GFMul_09 = + private static byte[] _gfMul09 = { 0x00, 0x09, 0x12, 0x1b, 0x24, 0x2d, 0x36, 0x3f, 0x48, 0x41, 0x5a, 0x53, 0x6c, 0x65, 0x7e, 0x77, 0x90, 0x99, 0x82, 0x8b, 0xb4, 0xbd, 0xa6, 0xaf, 0xd8, 0xd1, 0xca, 0xc3, 0xfc, 0xf5, 0xee, 0xe7, @@ -109,7 +109,7 @@ namespace ChocolArm64.Instruction 0x31, 0x38, 0x23, 0x2a, 0x15, 0x1c, 0x07, 0x0e, 0x79, 0x70, 0x6b, 0x62, 0x5d, 0x54, 0x4f, 0x46 }; - private static byte[] GFMul_0B = + private static byte[] _gfMul0B = { 0x00, 0x0b, 0x16, 0x1d, 0x2c, 0x27, 0x3a, 0x31, 0x58, 0x53, 0x4e, 0x45, 0x74, 0x7f, 0x62, 0x69, 0xb0, 0xbb, 0xa6, 0xad, 0x9c, 0x97, 0x8a, 0x81, 0xe8, 0xe3, 0xfe, 0xf5, 0xc4, 0xcf, 0xd2, 0xd9, @@ -129,7 +129,7 @@ namespace ChocolArm64.Instruction 0xca, 0xc1, 0xdc, 0xd7, 0xe6, 0xed, 0xf0, 0xfb, 0x92, 0x99, 0x84, 0x8f, 0xbe, 0xb5, 0xa8, 0xa3 }; - private static byte[] GFMul_0D = + private static byte[] _gfMul0D = { 0x00, 0x0d, 0x1a, 0x17, 0x34, 0x39, 0x2e, 0x23, 0x68, 0x65, 0x72, 0x7f, 0x5c, 0x51, 0x46, 0x4b, 0xd0, 0xdd, 0xca, 0xc7, 0xe4, 0xe9, 0xfe, 0xf3, 0xb8, 0xb5, 0xa2, 0xaf, 0x8c, 0x81, 0x96, 0x9b, @@ -149,7 +149,7 @@ namespace ChocolArm64.Instruction 0xdc, 0xd1, 0xc6, 0xcb, 0xe8, 0xe5, 0xf2, 0xff, 0xb4, 0xb9, 0xae, 0xa3, 0x80, 0x8d, 0x9a, 0x97 }; - private static byte[] GFMul_0E = + private static byte[] _gfMul0E = { 0x00, 0x0e, 0x1c, 0x12, 0x38, 0x36, 0x24, 0x2a, 0x70, 0x7e, 0x6c, 0x62, 0x48, 0x46, 0x54, 0x5a, 0xe0, 0xee, 0xfc, 0xf2, 0xd8, 0xd6, 0xc4, 0xca, 0x90, 0x9e, 0x8c, 0x82, 0xa8, 0xa6, 0xb4, 0xba, @@ -169,149 +169,149 @@ namespace ChocolArm64.Instruction 0xd7, 0xd9, 0xcb, 0xc5, 0xef, 0xe1, 0xf3, 0xfd, 0xa7, 0xa9, 0xbb, 0xb5, 0x9f, 0x91, 0x83, 0x8d }; - private static byte[] SRPerm = { 0, 13, 10, 7, 4, 1, 14, 11, 8, 5, 2, 15, 12, 9, 6, 3 }; + private static byte[] _srPerm = { 0, 13, 10, 7, 4, 1, 14, 11, 8, 5, 2, 15, 12, 9, 6, 3 }; - private static byte[] ISRPerm = { 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12, 1, 6, 11 }; + private static byte[] _isrPerm = { 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12, 1, 6, 11 }; #endregion - public static Vector128 AESInvMixColumns(Vector128 op) + public static Vector128 AesInvMixColumns(Vector128 op) { - byte[] InState = new byte[16]; - byte[] OutState = new byte[16]; + byte[] inState = new byte[16]; + byte[] outState = new byte[16]; - FromVectorToByteArray(InState, ref op); + FromVectorToByteArray(inState, ref op); - for (int Columns = 0; Columns <= 3; Columns++) + for (int columns = 0; columns <= 3; columns++) { - int Idx = Columns << 2; + int idx = columns << 2; - byte Row0 = InState[Idx + 0]; // A, E, I, M: [Row0, Col0-Col3] - byte Row1 = InState[Idx + 1]; // B, F, J, N: [Row1, Col0-Col3] - byte Row2 = InState[Idx + 2]; // C, G, K, O: [Row2, Col0-Col3] - byte Row3 = InState[Idx + 3]; // D, H, L, P: [Row3, Col0-Col3] + byte row0 = inState[idx + 0]; // A, E, I, M: [Row0, Col0-Col3] + byte row1 = inState[idx + 1]; // B, F, J, N: [Row1, Col0-Col3] + byte row2 = inState[idx + 2]; // C, G, K, O: [Row2, Col0-Col3] + byte row3 = inState[idx + 3]; // D, H, L, P: [Row3, Col0-Col3] - OutState[Idx + 0] = (byte)((uint)GFMul_0E[Row0] ^ GFMul_0B[Row1] ^ GFMul_0D[Row2] ^ GFMul_09[Row3]); - OutState[Idx + 1] = (byte)((uint)GFMul_09[Row0] ^ GFMul_0E[Row1] ^ GFMul_0B[Row2] ^ GFMul_0D[Row3]); - OutState[Idx + 2] = (byte)((uint)GFMul_0D[Row0] ^ GFMul_09[Row1] ^ GFMul_0E[Row2] ^ GFMul_0B[Row3]); - OutState[Idx + 3] = (byte)((uint)GFMul_0B[Row0] ^ GFMul_0D[Row1] ^ GFMul_09[Row2] ^ GFMul_0E[Row3]); + outState[idx + 0] = (byte)((uint)_gfMul0E[row0] ^ _gfMul0B[row1] ^ _gfMul0D[row2] ^ _gfMul09[row3]); + outState[idx + 1] = (byte)((uint)_gfMul09[row0] ^ _gfMul0E[row1] ^ _gfMul0B[row2] ^ _gfMul0D[row3]); + outState[idx + 2] = (byte)((uint)_gfMul0D[row0] ^ _gfMul09[row1] ^ _gfMul0E[row2] ^ _gfMul0B[row3]); + outState[idx + 3] = (byte)((uint)_gfMul0B[row0] ^ _gfMul0D[row1] ^ _gfMul09[row2] ^ _gfMul0E[row3]); } - FromByteArrayToVector(OutState, ref op); + FromByteArrayToVector(outState, ref op); return op; } - public static Vector128 AESInvShiftRows(Vector128 op) + public static Vector128 AesInvShiftRows(Vector128 op) { - byte[] InState = new byte[16]; - byte[] OutState = new byte[16]; + byte[] inState = new byte[16]; + byte[] outState = new byte[16]; - FromVectorToByteArray(InState, ref op); + FromVectorToByteArray(inState, ref op); - for (int Idx = 0; Idx <= 15; Idx++) + for (int idx = 0; idx <= 15; idx++) { - OutState[ISRPerm[Idx]] = InState[Idx]; + outState[_isrPerm[idx]] = inState[idx]; } - FromByteArrayToVector(OutState, ref op); + FromByteArrayToVector(outState, ref op); return op; } - public static Vector128 AESInvSubBytes(Vector128 op) + public static Vector128 AesInvSubBytes(Vector128 op) { - byte[] InState = new byte[16]; - byte[] OutState = new byte[16]; + byte[] inState = new byte[16]; + byte[] outState = new byte[16]; - FromVectorToByteArray(InState, ref op); + FromVectorToByteArray(inState, ref op); - for (int Idx = 0; Idx <= 15; Idx++) + for (int idx = 0; idx <= 15; idx++) { - OutState[Idx] = InvSBox[InState[Idx]]; + outState[idx] = _invSBox[inState[idx]]; } - FromByteArrayToVector(OutState, ref op); + FromByteArrayToVector(outState, ref op); return op; } - public static Vector128 AESMixColumns(Vector128 op) + public static Vector128 AesMixColumns(Vector128 op) { - byte[] InState = new byte[16]; - byte[] OutState = new byte[16]; + byte[] inState = new byte[16]; + byte[] outState = new byte[16]; - FromVectorToByteArray(InState, ref op); + FromVectorToByteArray(inState, ref op); - for (int Columns = 0; Columns <= 3; Columns++) + for (int columns = 0; columns <= 3; columns++) { - int Idx = Columns << 2; + int idx = columns << 2; - byte Row0 = InState[Idx + 0]; // A, E, I, M: [Row0, Col0-Col3] - byte Row1 = InState[Idx + 1]; // B, F, J, N: [Row1, Col0-Col3] - byte Row2 = InState[Idx + 2]; // C, G, K, O: [Row2, Col0-Col3] - byte Row3 = InState[Idx + 3]; // D, H, L, P: [Row3, Col0-Col3] + byte row0 = inState[idx + 0]; // A, E, I, M: [Row0, Col0-Col3] + byte row1 = inState[idx + 1]; // B, F, J, N: [Row1, Col0-Col3] + byte row2 = inState[idx + 2]; // C, G, K, O: [Row2, Col0-Col3] + byte row3 = inState[idx + 3]; // D, H, L, P: [Row3, Col0-Col3] - OutState[Idx + 0] = (byte)((uint)GFMul_02[Row0] ^ GFMul_03[Row1] ^ Row2 ^ Row3); - OutState[Idx + 1] = (byte)((uint)Row0 ^ GFMul_02[Row1] ^ GFMul_03[Row2] ^ Row3); - OutState[Idx + 2] = (byte)((uint)Row0 ^ Row1 ^ GFMul_02[Row2] ^ GFMul_03[Row3]); - OutState[Idx + 3] = (byte)((uint)GFMul_03[Row0] ^ Row1 ^ Row2 ^ GFMul_02[Row3]); + outState[idx + 0] = (byte)((uint)_gfMul02[row0] ^ _gfMul03[row1] ^ row2 ^ row3); + outState[idx + 1] = (byte)((uint)row0 ^ _gfMul02[row1] ^ _gfMul03[row2] ^ row3); + outState[idx + 2] = (byte)((uint)row0 ^ row1 ^ _gfMul02[row2] ^ _gfMul03[row3]); + outState[idx + 3] = (byte)((uint)_gfMul03[row0] ^ row1 ^ row2 ^ _gfMul02[row3]); } - FromByteArrayToVector(OutState, ref op); + FromByteArrayToVector(outState, ref op); return op; } - public static Vector128 AESShiftRows(Vector128 op) + public static Vector128 AesShiftRows(Vector128 op) { - byte[] InState = new byte[16]; - byte[] OutState = new byte[16]; + byte[] inState = new byte[16]; + byte[] outState = new byte[16]; - FromVectorToByteArray(InState, ref op); + FromVectorToByteArray(inState, ref op); - for (int Idx = 0; Idx <= 15; Idx++) + for (int idx = 0; idx <= 15; idx++) { - OutState[SRPerm[Idx]] = InState[Idx]; + outState[_srPerm[idx]] = inState[idx]; } - FromByteArrayToVector(OutState, ref op); + FromByteArrayToVector(outState, ref op); return op; } - public static Vector128 AESSubBytes(Vector128 op) + public static Vector128 AesSubBytes(Vector128 op) { - byte[] InState = new byte[16]; - byte[] OutState = new byte[16]; + byte[] inState = new byte[16]; + byte[] outState = new byte[16]; - FromVectorToByteArray(InState, ref op); + FromVectorToByteArray(inState, ref op); - for (int Idx = 0; Idx <= 15; Idx++) + for (int idx = 0; idx <= 15; idx++) { - OutState[Idx] = SBox[InState[Idx]]; + outState[idx] = _sBox[inState[idx]]; } - FromByteArrayToVector(OutState, ref op); + FromByteArrayToVector(outState, ref op); return op; } - private static void FromVectorToByteArray(byte[] State, ref Vector128 op) + private static void FromVectorToByteArray(byte[] state, ref Vector128 op) { - ulong ULongLow = AVectorHelper.VectorExtractIntZx((op), (byte)0, 3); - ulong ULongHigh = AVectorHelper.VectorExtractIntZx((op), (byte)1, 3); + ulong uLongLow = VectorHelper.VectorExtractIntZx((op), (byte)0, 3); + ulong uLongHigh = VectorHelper.VectorExtractIntZx((op), (byte)1, 3); - for (int Idx = 0; Idx <= 7; Idx++) + for (int idx = 0; idx <= 7; idx++) { - State[Idx + 0] = (byte)(ULongLow & 0xFFUL); - State[Idx + 8] = (byte)(ULongHigh & 0xFFUL); + state[idx + 0] = (byte)(uLongLow & 0xFFUL); + state[idx + 8] = (byte)(uLongHigh & 0xFFUL); - ULongLow >>= 8; - ULongHigh >>= 8; + uLongLow >>= 8; + uLongHigh >>= 8; } } - private static void FromByteArrayToVector(byte[] State, ref Vector128 op) + private static void FromByteArrayToVector(byte[] state, ref Vector128 op) { if (!Sse2.IsSupported) { @@ -319,10 +319,10 @@ namespace ChocolArm64.Instruction } op = Sse.StaticCast(Sse2.SetVector128( - State[15], State[14], State[13], State[12], - State[11], State[10], State[9], State[8], - State[7], State[6], State[5], State[4], - State[3], State[2], State[1], State[0])); + state[15], state[14], state[13], state[12], + state[11], state[10], state[9], state[8], + state[7], state[6], state[5], state[4], + state[3], state[2], state[1], state[0])); } } } diff --git a/Instructions/Inst.cs b/Instructions/Inst.cs new file mode 100644 index 0000000..5f6740c --- /dev/null +++ b/Instructions/Inst.cs @@ -0,0 +1,20 @@ +using System; + +namespace ChocolArm64.Instructions +{ + struct Inst + { + public InstInterpreter Interpreter { get; private set; } + public InstEmitter Emitter { get; private set; } + public Type Type { get; private set; } + + public static Inst Undefined => new Inst(null, InstEmit.Und, null); + + public Inst(InstInterpreter interpreter, InstEmitter emitter, Type type) + { + Interpreter = interpreter; + Emitter = emitter; + Type = type; + } + } +} \ No newline at end of file diff --git a/Instructions/InstEmitAlu.cs b/Instructions/InstEmitAlu.cs new file mode 100644 index 0000000..c0258ed --- /dev/null +++ b/Instructions/InstEmitAlu.cs @@ -0,0 +1,402 @@ +using ChocolArm64.Decoders; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.Intrinsics.X86; + +using static ChocolArm64.Instructions.InstEmitAluHelper; + +namespace ChocolArm64.Instructions +{ + static partial class InstEmit + { + public static void Adc(ILEmitterCtx context) => EmitAdc(context, false); + public static void Adcs(ILEmitterCtx context) => EmitAdc(context, true); + + private static void EmitAdc(ILEmitterCtx context, bool setFlags) + { + EmitDataLoadOpers(context); + + context.Emit(OpCodes.Add); + + context.EmitLdflg((int)PState.CBit); + + Type[] mthdTypes = new Type[] { typeof(bool) }; + + MethodInfo mthdInfo = typeof(Convert).GetMethod(nameof(Convert.ToInt32), mthdTypes); + + context.EmitCall(mthdInfo); + + if (context.CurrOp.RegisterSize != RegisterSize.Int32) + { + context.Emit(OpCodes.Conv_U8); + } + + context.Emit(OpCodes.Add); + + if (setFlags) + { + context.EmitZnFlagCheck(); + + EmitAdcsCCheck(context); + EmitAddsVCheck(context); + } + + EmitDataStore(context); + } + + public static void Add(ILEmitterCtx context) => EmitDataOp(context, OpCodes.Add); + + public static void Adds(ILEmitterCtx context) + { + EmitDataLoadOpers(context); + + context.Emit(OpCodes.Add); + + context.EmitZnFlagCheck(); + + EmitAddsCCheck(context); + EmitAddsVCheck(context); + EmitDataStoreS(context); + } + + public static void And(ILEmitterCtx context) => EmitDataOp(context, OpCodes.And); + + public static void Ands(ILEmitterCtx context) + { + EmitDataLoadOpers(context); + + context.Emit(OpCodes.And); + + EmitZeroCvFlags(context); + + context.EmitZnFlagCheck(); + + EmitDataStoreS(context); + } + + public static void Asrv(ILEmitterCtx context) => EmitDataOpShift(context, OpCodes.Shr); + + public static void Bic(ILEmitterCtx context) => EmitBic(context, false); + public static void Bics(ILEmitterCtx context) => EmitBic(context, true); + + private static void EmitBic(ILEmitterCtx 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 Cls(ILEmitterCtx context) + { + OpCodeAlu64 op = (OpCodeAlu64)context.CurrOp; + + context.EmitLdintzr(op.Rn); + + context.EmitLdc_I4(op.RegisterSize == RegisterSize.Int32 ? 32 : 64); + + SoftFallback.EmitCall(context, nameof(SoftFallback.CountLeadingSigns)); + + context.EmitStintzr(op.Rd); + } + + public static void Clz(ILEmitterCtx context) + { + OpCodeAlu64 op = (OpCodeAlu64)context.CurrOp; + + context.EmitLdintzr(op.Rn); + + if (Lzcnt.IsSupported) + { + Type tValue = op.RegisterSize == RegisterSize.Int32 ? typeof(uint) : typeof(ulong); + + context.EmitCall(typeof(Lzcnt).GetMethod(nameof(Lzcnt.LeadingZeroCount), new Type[] { tValue })); + } + else + { + context.EmitLdc_I4(op.RegisterSize == RegisterSize.Int32 ? 32 : 64); + + SoftFallback.EmitCall(context, nameof(SoftFallback.CountLeadingZeros)); + } + + context.EmitStintzr(op.Rd); + } + + public static void Eon(ILEmitterCtx context) + { + EmitDataLoadOpers(context); + + context.Emit(OpCodes.Not); + context.Emit(OpCodes.Xor); + + EmitDataStore(context); + } + + public static void Eor(ILEmitterCtx context) => EmitDataOp(context, OpCodes.Xor); + + public static void Extr(ILEmitterCtx context) + { + //TODO: Ensure that the Shift is valid for the Is64Bits. + OpCodeAluRs64 op = (OpCodeAluRs64)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(ILEmitterCtx context) => EmitDataOpShift(context, OpCodes.Shl); + public static void Lsrv(ILEmitterCtx context) => EmitDataOpShift(context, OpCodes.Shr_Un); + + public static void Sbc(ILEmitterCtx context) => EmitSbc(context, false); + public static void Sbcs(ILEmitterCtx context) => EmitSbc(context, true); + + private static void EmitSbc(ILEmitterCtx context, bool setFlags) + { + EmitDataLoadOpers(context); + + context.Emit(OpCodes.Sub); + + context.EmitLdflg((int)PState.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 != RegisterSize.Int32) + { + context.Emit(OpCodes.Conv_U8); + } + + context.Emit(OpCodes.Sub); + + if (setFlags) + { + context.EmitZnFlagCheck(); + + EmitSbcsCCheck(context); + EmitSubsVCheck(context); + } + + EmitDataStore(context); + } + + public static void Sub(ILEmitterCtx context) => EmitDataOp(context, OpCodes.Sub); + + public static void Subs(ILEmitterCtx context) + { + context.TryOptMarkCondWithoutCmp(); + + EmitDataLoadOpers(context); + + context.Emit(OpCodes.Sub); + + context.EmitZnFlagCheck(); + + EmitSubsCCheck(context); + EmitSubsVCheck(context); + EmitDataStoreS(context); + } + + public static void Orn(ILEmitterCtx context) + { + EmitDataLoadOpers(context); + + context.Emit(OpCodes.Not); + context.Emit(OpCodes.Or); + + EmitDataStore(context); + } + + public static void Orr(ILEmitterCtx context) => EmitDataOp(context, OpCodes.Or); + + public static void Rbit(ILEmitterCtx context) => EmitFallback32_64(context, + nameof(SoftFallback.ReverseBits32), + nameof(SoftFallback.ReverseBits64)); + + public static void Rev16(ILEmitterCtx context) => EmitFallback32_64(context, + nameof(SoftFallback.ReverseBytes16_32), + nameof(SoftFallback.ReverseBytes16_64)); + + public static void Rev32(ILEmitterCtx context) => EmitFallback32_64(context, + nameof(SoftFallback.ReverseBytes32_32), + nameof(SoftFallback.ReverseBytes32_64)); + + private static void EmitFallback32_64(ILEmitterCtx context, string name32, string name64) + { + OpCodeAlu64 op = (OpCodeAlu64)context.CurrOp; + + context.EmitLdintzr(op.Rn); + + if (op.RegisterSize == RegisterSize.Int32) + { + SoftFallback.EmitCall(context, name32); + } + else + { + SoftFallback.EmitCall(context, name64); + } + + context.EmitStintzr(op.Rd); + } + + public static void Rev64(ILEmitterCtx context) + { + OpCodeAlu64 op = (OpCodeAlu64)context.CurrOp; + + context.EmitLdintzr(op.Rn); + + SoftFallback.EmitCall(context, nameof(SoftFallback.ReverseBytes64)); + + context.EmitStintzr(op.Rd); + } + + public static void Rorv(ILEmitterCtx 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(ILEmitterCtx context) => EmitDiv(context, OpCodes.Div); + public static void Udiv(ILEmitterCtx context) => EmitDiv(context, OpCodes.Div_Un); + + private static void EmitDiv(ILEmitterCtx context, OpCode ilOp) + { + //If Rm == 0, Rd = 0 (division by zero). + context.EmitLdc_I(0); + + EmitDataLoadRm(context); + + context.EmitLdc_I(0); + + ILLabel badDiv = new ILLabel(); + + 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(ILEmitterCtx context, OpCode ilOp) + { + EmitDataLoadOpers(context); + + context.Emit(ilOp); + + EmitDataStore(context); + } + + private static void EmitDataOpShift(ILEmitterCtx context, OpCode ilOp) + { + EmitDataLoadRn(context); + EmitDataLoadShift(context); + + context.Emit(ilOp); + + EmitDataStore(context); + } + + private static void EmitDataLoadShift(ILEmitterCtx 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 != RegisterSize.Int32) + { + context.Emit(OpCodes.Conv_I4); + } + } + + private static void EmitZeroCvFlags(ILEmitterCtx context) + { + context.EmitLdc_I4(0); + + context.EmitStflg((int)PState.VBit); + + context.EmitLdc_I4(0); + + context.EmitStflg((int)PState.CBit); + } + } +} diff --git a/Instructions/InstEmitAluHelper.cs b/Instructions/InstEmitAluHelper.cs new file mode 100644 index 0000000..613dd23 --- /dev/null +++ b/Instructions/InstEmitAluHelper.cs @@ -0,0 +1,212 @@ +using ChocolArm64.Decoders; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System.Reflection.Emit; + +namespace ChocolArm64.Instructions +{ + static class InstEmitAluHelper + { + public static void EmitAdcsCCheck(ILEmitterCtx context) + { + //C = (Rd == Rn && CIn) || Rd < Rn + context.EmitSttmp(); + context.EmitLdtmp(); + context.EmitLdtmp(); + + EmitDataLoadRn(context); + + context.Emit(OpCodes.Ceq); + + context.EmitLdflg((int)PState.CBit); + + context.Emit(OpCodes.And); + + context.EmitLdtmp(); + + EmitDataLoadRn(context); + + context.Emit(OpCodes.Clt_Un); + context.Emit(OpCodes.Or); + + context.EmitStflg((int)PState.CBit); + } + + public static void EmitAddsCCheck(ILEmitterCtx context) + { + //C = Rd < Rn + context.Emit(OpCodes.Dup); + + EmitDataLoadRn(context); + + context.Emit(OpCodes.Clt_Un); + + context.EmitStflg((int)PState.CBit); + } + + public static void EmitAddsVCheck(ILEmitterCtx 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)PState.VBit); + } + + public static void EmitSbcsCCheck(ILEmitterCtx context) + { + //C = (Rn == Rm && CIn) || Rn > Rm + EmitDataLoadOpers(context); + + context.Emit(OpCodes.Ceq); + + context.EmitLdflg((int)PState.CBit); + + context.Emit(OpCodes.And); + + EmitDataLoadOpers(context); + + context.Emit(OpCodes.Cgt_Un); + context.Emit(OpCodes.Or); + + context.EmitStflg((int)PState.CBit); + } + + public static void EmitSubsCCheck(ILEmitterCtx 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)PState.CBit); + } + + public static void EmitSubsVCheck(ILEmitterCtx 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)PState.VBit); + } + + public static void EmitDataLoadRm(ILEmitterCtx context) + { + context.EmitLdintzr(((IOpCodeAluRs64)context.CurrOp).Rm); + } + + public static void EmitDataLoadOpers(ILEmitterCtx context) + { + EmitDataLoadRn(context); + EmitDataLoadOper2(context); + } + + public static void EmitDataLoadRn(ILEmitterCtx context) + { + IOpCodeAlu64 op = (IOpCodeAlu64)context.CurrOp; + + if (op.DataOp == DataOp.Logical || op is IOpCodeAluRs64) + { + context.EmitLdintzr(op.Rn); + } + else + { + context.EmitLdint(op.Rn); + } + } + + public static void EmitDataLoadOper2(ILEmitterCtx context) + { + switch (context.CurrOp) + { + case IOpCodeAluImm64 op: + context.EmitLdc_I(op.Imm); + break; + + case IOpCodeAluRs64 op: + context.EmitLdintzr(op.Rm); + + switch (op.ShiftType) + { + case ShiftType.Lsl: context.EmitLsl(op.Shift); break; + case ShiftType.Lsr: context.EmitLsr(op.Shift); break; + case ShiftType.Asr: context.EmitAsr(op.Shift); break; + case ShiftType.Ror: context.EmitRor(op.Shift); break; + } + break; + + case IOpCodeAluRx64 op: + context.EmitLdintzr(op.Rm); + context.EmitCast(op.IntType); + context.EmitLsl(op.Shift); + break; + } + } + + public static void EmitDataStore(ILEmitterCtx context) => EmitDataStore(context, false); + public static void EmitDataStoreS(ILEmitterCtx context) => EmitDataStore(context, true); + + public static void EmitDataStore(ILEmitterCtx context, bool setFlags) + { + IOpCodeAlu64 op = (IOpCodeAlu64)context.CurrOp; + + if (setFlags || op is IOpCodeAluRs64) + { + context.EmitStintzr(op.Rd); + } + else + { + context.EmitStint(op.Rd); + } + } + + public static void EmitSetNzcv(ILEmitterCtx context, int nzcv) + { + context.EmitLdc_I4((nzcv >> 0) & 1); + + context.EmitStflg((int)PState.VBit); + + context.EmitLdc_I4((nzcv >> 1) & 1); + + context.EmitStflg((int)PState.CBit); + + context.EmitLdc_I4((nzcv >> 2) & 1); + + context.EmitStflg((int)PState.ZBit); + + context.EmitLdc_I4((nzcv >> 3) & 1); + + context.EmitStflg((int)PState.NBit); + } + } +} \ No newline at end of file diff --git a/Instructions/InstEmitBfm.cs b/Instructions/InstEmitBfm.cs new file mode 100644 index 0000000..d25af8b --- /dev/null +++ b/Instructions/InstEmitBfm.cs @@ -0,0 +1,208 @@ +using ChocolArm64.Decoders; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System.Reflection.Emit; + +namespace ChocolArm64.Instructions +{ + static partial class InstEmit + { + public static void Bfm(ILEmitterCtx context) + { + OpCodeBfm64 op = (OpCodeBfm64)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(ILEmitterCtx context) + { + OpCodeBfm64 op = (OpCodeBfm64)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 + { + 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(ILEmitterCtx context) + { + OpCodeBfm64 op = (OpCodeBfm64)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(ILEmitterCtx context) => EmitBfiz(context, true); + private static void EmitUbfiz(ILEmitterCtx context) => EmitBfiz(context, false); + + private static void EmitBfiz(ILEmitterCtx context, bool signed) + { + OpCodeBfm64 op = (OpCodeBfm64)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(ILEmitterCtx context, OpCode ilOp) + { + EmitBfmCast(context, ilOp, true); + } + + private static void EmitUbfmCast(ILEmitterCtx context, OpCode ilOp) + { + EmitBfmCast(context, ilOp, false); + } + + private static void EmitBfmCast(ILEmitterCtx context, OpCode ilOp, bool signed) + { + OpCodeBfm64 op = (OpCodeBfm64)context.CurrOp; + + context.EmitLdintzr(op.Rn); + + context.Emit(ilOp); + + if (op.RegisterSize != RegisterSize.Int32) + { + context.Emit(signed + ? OpCodes.Conv_I8 + : OpCodes.Conv_U8); + } + + context.EmitStintzr(op.Rd); + } + + private static void EmitSbfmShift(ILEmitterCtx context) + { + EmitBfmShift(context, true); + } + + private static void EmitUbfmShift(ILEmitterCtx context) + { + EmitBfmShift(context, false); + } + + private static void EmitBfmShift(ILEmitterCtx context, bool signed) + { + OpCodeBfm64 op = (OpCodeBfm64)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(ILEmitterCtx context) + { + OpCodeBfm64 op = (OpCodeBfm64)context.CurrOp; + + context.EmitLdintzr(op.Rn); + + context.EmitLsl(op.GetBitsCount() - op.Shift); + + context.EmitStintzr(op.Rd); + } + + private static void EmitBfmLoadRn(ILEmitterCtx context) + { + OpCodeBfm64 op = (OpCodeBfm64)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/Instructions/InstEmitCcmp.cs b/Instructions/InstEmitCcmp.cs new file mode 100644 index 0000000..b91104c --- /dev/null +++ b/Instructions/InstEmitCcmp.cs @@ -0,0 +1,81 @@ +using ChocolArm64.Decoders; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System; +using System.Reflection.Emit; + +using static ChocolArm64.Instructions.InstEmitAluHelper; + +namespace ChocolArm64.Instructions +{ + static partial class InstEmit + { + private enum CcmpOp + { + Cmp, + Cmn + } + + public static void Ccmn(ILEmitterCtx context) => EmitCcmp(context, CcmpOp.Cmn); + public static void Ccmp(ILEmitterCtx context) => EmitCcmp(context, CcmpOp.Cmp); + + private static void EmitCcmp(ILEmitterCtx context, CcmpOp cmpOp) + { + OpCodeCcmp64 op = (OpCodeCcmp64)context.CurrOp; + + ILLabel lblTrue = new ILLabel(); + ILLabel lblEnd = new ILLabel(); + + context.EmitCondBranch(lblTrue, op.Cond); + + context.EmitLdc_I4((op.Nzcv >> 0) & 1); + + context.EmitStflg((int)PState.VBit); + + context.EmitLdc_I4((op.Nzcv >> 1) & 1); + + context.EmitStflg((int)PState.CBit); + + context.EmitLdc_I4((op.Nzcv >> 2) & 1); + + context.EmitStflg((int)PState.ZBit); + + context.EmitLdc_I4((op.Nzcv >> 3) & 1); + + context.EmitStflg((int)PState.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/Instructions/InstEmitCsel.cs b/Instructions/InstEmitCsel.cs new file mode 100644 index 0000000..19b073c --- /dev/null +++ b/Instructions/InstEmitCsel.cs @@ -0,0 +1,58 @@ +using ChocolArm64.Decoders; +using ChocolArm64.Translation; +using System.Reflection.Emit; + +namespace ChocolArm64.Instructions +{ + static partial class InstEmit + { + private enum CselOperation + { + None, + Increment, + Invert, + Negate + } + + public static void Csel(ILEmitterCtx context) => EmitCsel(context, CselOperation.None); + public static void Csinc(ILEmitterCtx context) => EmitCsel(context, CselOperation.Increment); + public static void Csinv(ILEmitterCtx context) => EmitCsel(context, CselOperation.Invert); + public static void Csneg(ILEmitterCtx context) => EmitCsel(context, CselOperation.Negate); + + private static void EmitCsel(ILEmitterCtx context, CselOperation cselOp) + { + OpCodeCsel64 op = (OpCodeCsel64)context.CurrOp; + + ILLabel lblTrue = new ILLabel(); + ILLabel lblEnd = new ILLabel(); + + 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.Emit(OpCodes.Br_S, lblEnd); + + context.MarkLabel(lblTrue); + + context.EmitLdintzr(op.Rn); + + context.MarkLabel(lblEnd); + + context.EmitStintzr(op.Rd); + } + } +} \ No newline at end of file diff --git a/Instructions/InstEmitException.cs b/Instructions/InstEmitException.cs new file mode 100644 index 0000000..8325a39 --- /dev/null +++ b/Instructions/InstEmitException.cs @@ -0,0 +1,86 @@ +using ChocolArm64.Decoders; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System.Reflection.Emit; + +namespace ChocolArm64.Instructions +{ + static partial class InstEmit + { + public static void Brk(ILEmitterCtx context) + { + EmitExceptionCall(context, nameof(CpuThreadState.OnBreak)); + } + + public static void Svc(ILEmitterCtx context) + { + EmitExceptionCall(context, nameof(CpuThreadState.OnSvcCall)); + } + + private static void EmitExceptionCall(ILEmitterCtx context, string mthdName) + { + OpCodeException64 op = (OpCodeException64)context.CurrOp; + + context.EmitStoreState(); + + context.EmitLdarg(TranslatedSub.StateArgIdx); + + context.EmitLdc_I8(op.Position); + context.EmitLdc_I4(op.Id); + + context.EmitPrivateCall(typeof(CpuThreadState), mthdName); + + //Check if the thread should still be running, if it isn't then we return 0 + //to force a return to the dispatcher and then exit the thread. + context.EmitLdarg(TranslatedSub.StateArgIdx); + + context.EmitCallPropGet(typeof(CpuThreadState), nameof(CpuThreadState.Running)); + + ILLabel lblEnd = new ILLabel(); + + context.Emit(OpCodes.Brtrue_S, lblEnd); + + context.EmitLdc_I8(0); + + context.Emit(OpCodes.Ret); + + context.MarkLabel(lblEnd); + + if (context.CurrBlock.Next != null) + { + context.EmitLoadState(context.CurrBlock.Next); + } + else + { + context.EmitLdc_I8(op.Position + 4); + + context.Emit(OpCodes.Ret); + } + } + + public static void Und(ILEmitterCtx context) + { + OpCode64 op = context.CurrOp; + + context.EmitStoreState(); + + context.EmitLdarg(TranslatedSub.StateArgIdx); + + context.EmitLdc_I8(op.Position); + context.EmitLdc_I4(op.RawOpCode); + + context.EmitPrivateCall(typeof(CpuThreadState), nameof(CpuThreadState.OnUndefined)); + + if (context.CurrBlock.Next != null) + { + context.EmitLoadState(context.CurrBlock.Next); + } + else + { + context.EmitLdc_I8(op.Position + 4); + + context.Emit(OpCodes.Ret); + } + } + } +} \ No newline at end of file diff --git a/Instructions/InstEmitFlow.cs b/Instructions/InstEmitFlow.cs new file mode 100644 index 0000000..7d0897c --- /dev/null +++ b/Instructions/InstEmitFlow.cs @@ -0,0 +1,189 @@ +using ChocolArm64.Decoders; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System.Reflection.Emit; + +namespace ChocolArm64.Instructions +{ + static partial class InstEmit + { + public static void B(ILEmitterCtx context) + { + OpCodeBImmAl64 op = (OpCodeBImmAl64)context.CurrOp; + + if (context.CurrBlock.Branch != null) + { + context.Emit(OpCodes.Br, context.GetLabel(op.Imm)); + } + else + { + context.EmitStoreState(); + context.EmitLdc_I8(op.Imm); + + context.Emit(OpCodes.Ret); + } + } + + public static void B_Cond(ILEmitterCtx context) + { + OpCodeBImmCond64 op = (OpCodeBImmCond64)context.CurrOp; + + EmitBranch(context, op.Cond); + } + + public static void Bl(ILEmitterCtx context) + { + OpCodeBImmAl64 op = (OpCodeBImmAl64)context.CurrOp; + + context.EmitLdc_I(op.Position + 4); + context.EmitStint(CpuThreadState.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); + + ILLabel lblContinue = new ILLabel(); + + context.Emit(OpCodes.Beq_S, lblContinue); + context.Emit(OpCodes.Ret); + + context.MarkLabel(lblContinue); + + context.Emit(OpCodes.Pop); + + context.EmitLoadState(context.CurrBlock.Next); + } + else + { + context.EmitLdc_I8(op.Imm); + + context.Emit(OpCodes.Ret); + } + } + + public static void Blr(ILEmitterCtx context) + { + OpCodeBReg64 op = (OpCodeBReg64)context.CurrOp; + + context.EmitLdc_I(op.Position + 4); + context.EmitStint(CpuThreadState.LrIndex); + context.EmitStoreState(); + context.EmitLdintzr(op.Rn); + + context.Emit(OpCodes.Ret); + } + + public static void Br(ILEmitterCtx context) + { + OpCodeBReg64 op = (OpCodeBReg64)context.CurrOp; + + context.EmitStoreState(); + context.EmitLdintzr(op.Rn); + + context.Emit(OpCodes.Ret); + } + + public static void Cbnz(ILEmitterCtx context) => EmitCb(context, OpCodes.Bne_Un); + public static void Cbz(ILEmitterCtx context) => EmitCb(context, OpCodes.Beq); + + private static void EmitCb(ILEmitterCtx context, OpCode ilOp) + { + OpCodeBImmCmp64 op = (OpCodeBImmCmp64)context.CurrOp; + + context.EmitLdintzr(op.Rt); + context.EmitLdc_I(0); + + EmitBranch(context, ilOp); + } + + public static void Ret(ILEmitterCtx context) + { + context.EmitStoreState(); + context.EmitLdint(CpuThreadState.LrIndex); + + context.Emit(OpCodes.Ret); + } + + public static void Tbnz(ILEmitterCtx context) => EmitTb(context, OpCodes.Bne_Un); + public static void Tbz(ILEmitterCtx context) => EmitTb(context, OpCodes.Beq); + + private static void EmitTb(ILEmitterCtx context, OpCode ilOp) + { + OpCodeBImmTest64 op = (OpCodeBImmTest64)context.CurrOp; + + context.EmitLdintzr(op.Rt); + context.EmitLdc_I(1L << op.Pos); + + context.Emit(OpCodes.And); + + context.EmitLdc_I(0); + + EmitBranch(context, ilOp); + } + + private static void EmitBranch(ILEmitterCtx context, Cond cond) + { + OpCodeBImm64 op = (OpCodeBImm64)context.CurrOp; + + if (context.CurrBlock.Next != null && + context.CurrBlock.Branch != null) + { + context.EmitCondBranch(context.GetLabel(op.Imm), cond); + } + else + { + context.EmitStoreState(); + + ILLabel lblTaken = new ILLabel(); + + context.EmitCondBranch(lblTaken, cond); + + context.EmitLdc_I8(op.Position + 4); + + context.Emit(OpCodes.Ret); + + context.MarkLabel(lblTaken); + + context.EmitLdc_I8(op.Imm); + + context.Emit(OpCodes.Ret); + } + } + + private static void EmitBranch(ILEmitterCtx context, OpCode ilOp) + { + OpCodeBImm64 op = (OpCodeBImm64)context.CurrOp; + + if (context.CurrBlock.Next != null && + context.CurrBlock.Branch != null) + { + context.Emit(ilOp, context.GetLabel(op.Imm)); + } + else + { + context.EmitStoreState(); + + ILLabel lblTaken = new ILLabel(); + + context.Emit(ilOp, lblTaken); + + context.EmitLdc_I8(op.Position + 4); + + context.Emit(OpCodes.Ret); + + context.MarkLabel(lblTaken); + + context.EmitLdc_I8(op.Imm); + + context.Emit(OpCodes.Ret); + } + } + } +} \ No newline at end of file diff --git a/Instructions/InstEmitHash.cs b/Instructions/InstEmitHash.cs new file mode 100644 index 0000000..7e21a88 --- /dev/null +++ b/Instructions/InstEmitHash.cs @@ -0,0 +1,115 @@ +using ChocolArm64.Decoders; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System; +using System.Reflection.Emit; +using System.Runtime.Intrinsics.X86; + +namespace ChocolArm64.Instructions +{ + static partial class InstEmit + { + public static void Crc32b(ILEmitterCtx context) + { + EmitCrc32(context, nameof(SoftFallback.Crc32B)); + } + + public static void Crc32h(ILEmitterCtx context) + { + EmitCrc32(context, nameof(SoftFallback.Crc32H)); + } + + public static void Crc32w(ILEmitterCtx context) + { + EmitCrc32(context, nameof(SoftFallback.Crc32W)); + } + + public static void Crc32x(ILEmitterCtx context) + { + EmitCrc32(context, nameof(SoftFallback.Crc32X)); + } + + public static void Crc32cb(ILEmitterCtx context) + { + if (Optimizations.UseSse42) + { + EmitSse42Crc32(context, typeof(uint), typeof(byte)); + } + else + { + EmitCrc32(context, nameof(SoftFallback.Crc32Cb)); + } + } + + public static void Crc32ch(ILEmitterCtx context) + { + if (Optimizations.UseSse42) + { + EmitSse42Crc32(context, typeof(uint), typeof(ushort)); + } + else + { + EmitCrc32(context, nameof(SoftFallback.Crc32Ch)); + } + } + + public static void Crc32cw(ILEmitterCtx context) + { + if (Optimizations.UseSse42) + { + EmitSse42Crc32(context, typeof(uint), typeof(uint)); + } + else + { + EmitCrc32(context, nameof(SoftFallback.Crc32Cw)); + } + } + + public static void Crc32cx(ILEmitterCtx context) + { + if (Optimizations.UseSse42) + { + EmitSse42Crc32(context, typeof(ulong), typeof(ulong)); + } + else + { + EmitCrc32(context, nameof(SoftFallback.Crc32Cx)); + } + } + + private static void EmitSse42Crc32(ILEmitterCtx context, Type tCrc, Type tData) + { + OpCodeAluRs64 op = (OpCodeAluRs64)context.CurrOp; + + context.EmitLdintzr(op.Rn); + context.EmitLdintzr(op.Rm); + + context.EmitCall(typeof(Sse42).GetMethod(nameof(Sse42.Crc32), new Type[] { tCrc, tData })); + + context.EmitStintzr(op.Rd); + } + + private static void EmitCrc32(ILEmitterCtx context, string name) + { + OpCodeAluRs64 op = (OpCodeAluRs64)context.CurrOp; + + context.EmitLdintzr(op.Rn); + + if (op.RegisterSize != RegisterSize.Int32) + { + context.Emit(OpCodes.Conv_U4); + } + + context.EmitLdintzr(op.Rm); + + SoftFallback.EmitCall(context, name); + + if (op.RegisterSize != RegisterSize.Int32) + { + context.Emit(OpCodes.Conv_U8); + } + + context.EmitStintzr(op.Rd); + } + } +} diff --git a/Instructions/InstEmitMemory.cs b/Instructions/InstEmitMemory.cs new file mode 100644 index 0000000..96e45b3 --- /dev/null +++ b/Instructions/InstEmitMemory.cs @@ -0,0 +1,252 @@ +using ChocolArm64.Decoders; +using ChocolArm64.Translation; +using System.Reflection.Emit; + +using static ChocolArm64.Instructions.InstEmitMemoryHelper; + +namespace ChocolArm64.Instructions +{ + static partial class InstEmit + { + public static void Adr(ILEmitterCtx context) + { + OpCodeAdr64 op = (OpCodeAdr64)context.CurrOp; + + context.EmitLdc_I(op.Position + op.Imm); + context.EmitStintzr(op.Rd); + } + + public static void Adrp(ILEmitterCtx context) + { + OpCodeAdr64 op = (OpCodeAdr64)context.CurrOp; + + context.EmitLdc_I((op.Position & ~0xfffL) + (op.Imm << 12)); + context.EmitStintzr(op.Rd); + } + + public static void Ldr(ILEmitterCtx context) => EmitLdr(context, false); + public static void Ldrs(ILEmitterCtx context) => EmitLdr(context, true); + + private static void EmitLdr(ILEmitterCtx context, bool signed) + { + OpCodeMem64 op = (OpCodeMem64)context.CurrOp; + + context.EmitLdarg(TranslatedSub.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 IOpCodeSimd64) + { + context.EmitStvec(op.Rt); + } + else + { + context.EmitStintzr(op.Rt); + } + + EmitWBackIfNeeded(context); + } + + public static void LdrLit(ILEmitterCtx context) + { + IOpCodeLit64 op = (IOpCodeLit64)context.CurrOp; + + if (op.Prefetch) + { + return; + } + + context.EmitLdarg(TranslatedSub.MemoryArgIdx); + context.EmitLdc_I8(op.Imm); + + if (op.Signed) + { + EmitReadSx64Call(context, op.Size); + } + else + { + EmitReadZxCall(context, op.Size); + } + + if (op is IOpCodeSimd64) + { + context.EmitStvec(op.Rt); + } + else + { + context.EmitStint(op.Rt); + } + } + + public static void Ldp(ILEmitterCtx context) + { + OpCodeMemPair64 op = (OpCodeMemPair64)context.CurrOp; + + void EmitReadAndStore(int rt) + { + if (op.Extend64) + { + EmitReadSx64Call(context, op.Size); + } + else + { + EmitReadZxCall(context, op.Size); + } + + if (op is IOpCodeSimd64) + { + context.EmitStvec(rt); + } + else + { + context.EmitStintzr(rt); + } + } + + context.EmitLdarg(TranslatedSub.MemoryArgIdx); + + EmitLoadAddress(context); + + EmitReadAndStore(op.Rt); + + context.EmitLdarg(TranslatedSub.MemoryArgIdx); + context.EmitLdtmp(); + context.EmitLdc_I8(1 << op.Size); + + context.Emit(OpCodes.Add); + + EmitReadAndStore(op.Rt2); + + EmitWBackIfNeeded(context); + } + + public static void Str(ILEmitterCtx context) + { + OpCodeMem64 op = (OpCodeMem64)context.CurrOp; + + context.EmitLdarg(TranslatedSub.MemoryArgIdx); + + EmitLoadAddress(context); + + if (op is IOpCodeSimd64) + { + context.EmitLdvec(op.Rt); + } + else + { + context.EmitLdintzr(op.Rt); + } + + EmitWriteCall(context, op.Size); + + EmitWBackIfNeeded(context); + } + + public static void Stp(ILEmitterCtx context) + { + OpCodeMemPair64 op = (OpCodeMemPair64)context.CurrOp; + + context.EmitLdarg(TranslatedSub.MemoryArgIdx); + + EmitLoadAddress(context); + + if (op is IOpCodeSimd64) + { + context.EmitLdvec(op.Rt); + } + else + { + context.EmitLdintzr(op.Rt); + } + + EmitWriteCall(context, op.Size); + + context.EmitLdarg(TranslatedSub.MemoryArgIdx); + context.EmitLdtmp(); + context.EmitLdc_I8(1 << op.Size); + + context.Emit(OpCodes.Add); + + if (op is IOpCodeSimd64) + { + context.EmitLdvec(op.Rt2); + } + else + { + context.EmitLdintzr(op.Rt2); + } + + EmitWriteCall(context, op.Size); + + EmitWBackIfNeeded(context); + } + + private static void EmitLoadAddress(ILEmitterCtx context) + { + switch (context.CurrOp) + { + case OpCodeMemImm64 op: + context.EmitLdint(op.Rn); + + if (!op.PostIdx) + { + //Pre-indexing. + context.EmitLdc_I(op.Imm); + + context.Emit(OpCodes.Add); + } + break; + + case OpCodeMemReg64 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(ILEmitterCtx 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 OpCodeMemImm64 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/Instructions/InstEmitMemoryEx.cs b/Instructions/InstEmitMemoryEx.cs new file mode 100644 index 0000000..42daca6 --- /dev/null +++ b/Instructions/InstEmitMemoryEx.cs @@ -0,0 +1,192 @@ +using ChocolArm64.Decoders; +using ChocolArm64.Memory; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System; +using System.Reflection.Emit; +using System.Threading; + +using static ChocolArm64.Instructions.InstEmitMemoryHelper; + +namespace ChocolArm64.Instructions +{ + static partial class InstEmit + { + [Flags] + private enum AccessType + { + None = 0, + Ordered = 1, + Exclusive = 2, + OrderedEx = Ordered | Exclusive + } + + public static void Clrex(ILEmitterCtx context) + { + EmitMemoryCall(context, nameof(MemoryManager.ClearExclusive)); + } + + public static void Dmb(ILEmitterCtx context) => EmitBarrier(context); + public static void Dsb(ILEmitterCtx context) => EmitBarrier(context); + + public static void Ldar(ILEmitterCtx context) => EmitLdr(context, AccessType.Ordered); + public static void Ldaxr(ILEmitterCtx context) => EmitLdr(context, AccessType.OrderedEx); + public static void Ldxr(ILEmitterCtx context) => EmitLdr(context, AccessType.Exclusive); + public static void Ldxp(ILEmitterCtx context) => EmitLdp(context, AccessType.Exclusive); + public static void Ldaxp(ILEmitterCtx context) => EmitLdp(context, AccessType.OrderedEx); + + private static void EmitLdr(ILEmitterCtx context, AccessType accType) + { + EmitLoad(context, accType, false); + } + + private static void EmitLdp(ILEmitterCtx context, AccessType accType) + { + EmitLoad(context, accType, true); + } + + private static void EmitLoad(ILEmitterCtx context, AccessType accType, bool pair) + { + OpCodeMemEx64 op = (OpCodeMemEx64)context.CurrOp; + + bool ordered = (accType & AccessType.Ordered) != 0; + bool exclusive = (accType & AccessType.Exclusive) != 0; + + if (ordered) + { + EmitBarrier(context); + } + + if (exclusive) + { + EmitMemoryCall(context, nameof(MemoryManager.SetExclusive), op.Rn); + } + + context.EmitLdint(op.Rn); + context.EmitSttmp(); + + context.EmitLdarg(TranslatedSub.MemoryArgIdx); + context.EmitLdtmp(); + + EmitReadZxCall(context, op.Size); + + context.EmitStintzr(op.Rt); + + if (pair) + { + context.EmitLdarg(TranslatedSub.MemoryArgIdx); + context.EmitLdtmp(); + context.EmitLdc_I8(1 << op.Size); + + context.Emit(OpCodes.Add); + + EmitReadZxCall(context, op.Size); + + context.EmitStintzr(op.Rt2); + } + } + + public static void Pfrm(ILEmitterCtx context) + { + //Memory Prefetch, execute as no-op. + } + + public static void Stlr(ILEmitterCtx context) => EmitStr(context, AccessType.Ordered); + public static void Stlxr(ILEmitterCtx context) => EmitStr(context, AccessType.OrderedEx); + public static void Stxr(ILEmitterCtx context) => EmitStr(context, AccessType.Exclusive); + public static void Stxp(ILEmitterCtx context) => EmitStp(context, AccessType.Exclusive); + public static void Stlxp(ILEmitterCtx context) => EmitStp(context, AccessType.OrderedEx); + + private static void EmitStr(ILEmitterCtx context, AccessType accType) + { + EmitStore(context, accType, false); + } + + private static void EmitStp(ILEmitterCtx context, AccessType accType) + { + EmitStore(context, accType, true); + } + + private static void EmitStore(ILEmitterCtx context, AccessType accType, bool pair) + { + OpCodeMemEx64 op = (OpCodeMemEx64)context.CurrOp; + + bool ordered = (accType & AccessType.Ordered) != 0; + bool exclusive = (accType & AccessType.Exclusive) != 0; + + if (ordered) + { + EmitBarrier(context); + } + + ILLabel lblEx = new ILLabel(); + ILLabel lblEnd = new ILLabel(); + + if (exclusive) + { + EmitMemoryCall(context, nameof(MemoryManager.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(TranslatedSub.MemoryArgIdx); + context.EmitLdint(op.Rn); + context.EmitLdintzr(op.Rt); + + EmitWriteCall(context, op.Size); + + if (pair) + { + context.EmitLdarg(TranslatedSub.MemoryArgIdx); + context.EmitLdint(op.Rn); + context.EmitLdc_I8(1 << op.Size); + + context.Emit(OpCodes.Add); + + context.EmitLdintzr(op.Rt2); + + EmitWriteCall(context, op.Size); + } + + if (exclusive) + { + context.EmitLdc_I8(0); + context.EmitStintzr(op.Rs); + + EmitMemoryCall(context, nameof(MemoryManager.ClearExclusiveForStore)); + } + + context.MarkLabel(lblEnd); + } + + private static void EmitMemoryCall(ILEmitterCtx context, string name, int rn = -1) + { + context.EmitLdarg(TranslatedSub.MemoryArgIdx); + context.EmitLdarg(TranslatedSub.StateArgIdx); + + context.EmitCallPropGet(typeof(CpuThreadState), nameof(CpuThreadState.Core)); + + if (rn != -1) + { + context.EmitLdint(rn); + } + + context.EmitCall(typeof(MemoryManager), name); + } + + private static void EmitBarrier(ILEmitterCtx 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/Instructions/InstEmitMemoryHelper.cs b/Instructions/InstEmitMemoryHelper.cs new file mode 100644 index 0000000..f953564 --- /dev/null +++ b/Instructions/InstEmitMemoryHelper.cs @@ -0,0 +1,138 @@ +using ChocolArm64.Decoders; +using ChocolArm64.Memory; +using ChocolArm64.Translation; +using System; +using System.Reflection.Emit; + +namespace ChocolArm64.Instructions +{ + static class InstEmitMemoryHelper + { + private enum Extension + { + Zx, + Sx32, + Sx64 + } + + public static void EmitReadZxCall(ILEmitterCtx context, int size) + { + EmitReadCall(context, Extension.Zx, size); + } + + public static void EmitReadSx32Call(ILEmitterCtx context, int size) + { + EmitReadCall(context, Extension.Sx32, size); + } + + public static void EmitReadSx64Call(ILEmitterCtx context, int size) + { + EmitReadCall(context, Extension.Sx64, size); + } + + private static void EmitReadCall(ILEmitterCtx 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(MemoryManager.ReadVector8); break; + case 1: name = nameof(MemoryManager.ReadVector16); break; + case 2: name = nameof(MemoryManager.ReadVector32); break; + case 3: name = nameof(MemoryManager.ReadVector64); break; + case 4: name = nameof(MemoryManager.ReadVector128); break; + } + } + else + { + switch (size) + { + case 0: name = nameof(MemoryManager.ReadByte); break; + case 1: name = nameof(MemoryManager.ReadUInt16); break; + case 2: name = nameof(MemoryManager.ReadUInt32); break; + case 3: name = nameof(MemoryManager.ReadUInt64); break; + } + } + + context.EmitCall(typeof(MemoryManager), 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(ILEmitterCtx 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(MemoryManager.WriteVector8); break; + case 1: name = nameof(MemoryManager.WriteVector16); break; + case 2: name = nameof(MemoryManager.WriteVector32); break; + case 3: name = nameof(MemoryManager.WriteVector64); break; + case 4: name = nameof(MemoryManager.WriteVector128); break; + } + } + else + { + switch (size) + { + case 0: name = nameof(MemoryManager.WriteByte); break; + case 1: name = nameof(MemoryManager.WriteUInt16); break; + case 2: name = nameof(MemoryManager.WriteUInt32); break; + case 3: name = nameof(MemoryManager.WriteUInt64); break; + } + } + + context.EmitCall(typeof(MemoryManager), name); + } + + private static bool GetIsSimd(ILEmitterCtx context) + { + return context.CurrOp is IOpCodeSimd64 && + !(context.CurrOp is OpCodeSimdMemMs64 || + context.CurrOp is OpCodeSimdMemSs64); + } + } +} \ No newline at end of file diff --git a/Instructions/InstEmitMove.cs b/Instructions/InstEmitMove.cs new file mode 100644 index 0000000..be3e8e2 --- /dev/null +++ b/Instructions/InstEmitMove.cs @@ -0,0 +1,41 @@ +using ChocolArm64.Decoders; +using ChocolArm64.Translation; +using System.Reflection.Emit; + +namespace ChocolArm64.Instructions +{ + static partial class InstEmit + { + public static void Movk(ILEmitterCtx context) + { + OpCodeMov64 op = (OpCodeMov64)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(ILEmitterCtx context) + { + OpCodeMov64 op = (OpCodeMov64)context.CurrOp; + + context.EmitLdc_I(~op.Imm); + context.EmitStintzr(op.Rd); + } + + public static void Movz(ILEmitterCtx context) + { + OpCodeMov64 op = (OpCodeMov64)context.CurrOp; + + context.EmitLdc_I(op.Imm); + context.EmitStintzr(op.Rd); + } + } +} \ No newline at end of file diff --git a/Instructions/InstEmitMul.cs b/Instructions/InstEmitMul.cs new file mode 100644 index 0000000..b7418e6 --- /dev/null +++ b/Instructions/InstEmitMul.cs @@ -0,0 +1,80 @@ +using ChocolArm64.Decoders; +using ChocolArm64.Translation; +using System.Reflection.Emit; + +namespace ChocolArm64.Instructions +{ + static partial class InstEmit + { + public static void Madd(ILEmitterCtx context) => EmitMul(context, OpCodes.Add); + public static void Msub(ILEmitterCtx context) => EmitMul(context, OpCodes.Sub); + + private static void EmitMul(ILEmitterCtx context, OpCode ilOp) + { + OpCodeMul64 op = (OpCodeMul64)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(ILEmitterCtx context) => EmitMull(context, OpCodes.Add, true); + public static void Smsubl(ILEmitterCtx context) => EmitMull(context, OpCodes.Sub, true); + public static void Umaddl(ILEmitterCtx context) => EmitMull(context, OpCodes.Add, false); + public static void Umsubl(ILEmitterCtx context) => EmitMull(context, OpCodes.Sub, false); + + private static void EmitMull(ILEmitterCtx context, OpCode addSubOp, bool signed) + { + OpCodeMul64 op = (OpCodeMul64)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(ILEmitterCtx context) + { + OpCodeMul64 op = (OpCodeMul64)context.CurrOp; + + context.EmitLdintzr(op.Rn); + context.EmitLdintzr(op.Rm); + + SoftFallback.EmitCall(context, nameof(SoftFallback.SMulHi128)); + + context.EmitStintzr(op.Rd); + } + + public static void Umulh(ILEmitterCtx context) + { + OpCodeMul64 op = (OpCodeMul64)context.CurrOp; + + context.EmitLdintzr(op.Rn); + context.EmitLdintzr(op.Rm); + + SoftFallback.EmitCall(context, nameof(SoftFallback.UMulHi128)); + + context.EmitStintzr(op.Rd); + } + } +} \ No newline at end of file diff --git a/Instructions/InstEmitSimdArithmetic.cs b/Instructions/InstEmitSimdArithmetic.cs new file mode 100644 index 0000000..9217de5 --- /dev/null +++ b/Instructions/InstEmitSimdArithmetic.cs @@ -0,0 +1,2387 @@ +// https://github.com/intel/ARM_NEON_2_x86_SSE/blob/master/NEON_2_SSE.h + +using ChocolArm64.Decoders; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; + +using static ChocolArm64.Instructions.InstEmitSimdHelper; + +namespace ChocolArm64.Instructions +{ + static partial class InstEmit + { + public static void Abs_S(ILEmitterCtx context) + { + EmitScalarUnaryOpSx(context, () => EmitAbs(context)); + } + + public static void Abs_V(ILEmitterCtx context) + { + EmitVectorUnaryOpSx(context, () => EmitAbs(context)); + } + + public static void Add_S(ILEmitterCtx context) + { + EmitScalarBinaryOpZx(context, () => context.Emit(OpCodes.Add)); + } + + public static void Add_V(ILEmitterCtx context) + { + if (Optimizations.UseSse2) + { + EmitSse2Op(context, nameof(Sse2.Add)); + } + else + { + EmitVectorBinaryOpZx(context, () => context.Emit(OpCodes.Add)); + } + } + + public static void Addhn_V(ILEmitterCtx context) + { + EmitHighNarrow(context, () => context.Emit(OpCodes.Add), round: false); + } + + public static void Addp_S(ILEmitterCtx context) + { + OpCodeSimd64 op = (OpCodeSimd64)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(ILEmitterCtx context) + { + EmitVectorPairwiseOpZx(context, () => context.Emit(OpCodes.Add)); + } + + public static void Addv_V(ILEmitterCtx context) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + int bytes = op.GetBitsCount() >> 3; + int elems = bytes >> op.Size; + + EmitVectorExtractZx(context, op.Rn, 0, op.Size); + + for (int index = 1; index < elems; index++) + { + EmitVectorExtractZx(context, op.Rn, index, op.Size); + + context.Emit(OpCodes.Add); + } + + EmitScalarSet(context, op.Rd, op.Size); + } + + public static void Cls_V(ILEmitterCtx context) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + int bytes = op.GetBitsCount() >> 3; + int elems = bytes >> op.Size; + + int eSize = 8 << op.Size; + + for (int index = 0; index < elems; index++) + { + EmitVectorExtractZx(context, op.Rn, index, op.Size); + + context.EmitLdc_I4(eSize); + + SoftFallback.EmitCall(context, nameof(SoftFallback.CountLeadingSigns)); + + EmitVectorInsert(context, op.Rd, index, op.Size); + } + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + public static void Clz_V(ILEmitterCtx context) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + int bytes = op.GetBitsCount() >> 3; + int elems = bytes >> op.Size; + + int eSize = 8 << op.Size; + + for (int index = 0; index < elems; index++) + { + EmitVectorExtractZx(context, op.Rn, index, op.Size); + + if (Lzcnt.IsSupported && eSize == 32) + { + context.Emit(OpCodes.Conv_U4); + + context.EmitCall(typeof(Lzcnt).GetMethod(nameof(Lzcnt.LeadingZeroCount), new Type[] { typeof(uint) })); + + context.Emit(OpCodes.Conv_U8); + } + else + { + context.EmitLdc_I4(eSize); + + SoftFallback.EmitCall(context, nameof(SoftFallback.CountLeadingZeros)); + } + + EmitVectorInsert(context, op.Rd, index, op.Size); + } + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + public static void Cnt_V(ILEmitterCtx context) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + int elems = op.RegisterSize == RegisterSize.Simd128 ? 16 : 8; + + for (int index = 0; index < elems; index++) + { + EmitVectorExtractZx(context, op.Rn, index, 0); + + if (Popcnt.IsSupported) + { + context.EmitCall(typeof(Popcnt).GetMethod(nameof(Popcnt.PopCount), new Type[] { typeof(ulong) })); + } + else + { + SoftFallback.EmitCall(context, nameof(SoftFallback.CountSetBits8)); + } + + EmitVectorInsert(context, op.Rd, index, 0); + } + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + public static void Fabd_S(ILEmitterCtx context) + { + EmitScalarBinaryOpF(context, () => + { + context.Emit(OpCodes.Sub); + + EmitUnaryMathCall(context, nameof(Math.Abs)); + }); + } + + public static void Fabs_S(ILEmitterCtx context) + { + EmitScalarUnaryOpF(context, () => + { + EmitUnaryMathCall(context, nameof(Math.Abs)); + }); + } + + public static void Fabs_V(ILEmitterCtx context) + { + EmitVectorUnaryOpF(context, () => + { + EmitUnaryMathCall(context, nameof(Math.Abs)); + }); + } + + public static void Fadd_S(ILEmitterCtx context) + { + if (Optimizations.FastFP && Optimizations.UseSse + && Optimizations.UseSse2) + { + EmitScalarSseOrSse2OpF(context, nameof(Sse.AddScalar)); + } + else + { + EmitScalarBinaryOpF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FPAdd)); + }); + } + } + + public static void Fadd_V(ILEmitterCtx context) + { + if (Optimizations.FastFP && Optimizations.UseSse + && Optimizations.UseSse2) + { + EmitVectorSseOrSse2OpF(context, nameof(Sse.Add)); + } + else + { + EmitVectorBinaryOpF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FPAdd)); + }); + } + } + + public static void Faddp_S(ILEmitterCtx context) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + int sizeF = op.Size & 1; + + EmitVectorExtractF(context, op.Rn, 0, sizeF); + EmitVectorExtractF(context, op.Rn, 1, sizeF); + + context.Emit(OpCodes.Add); + + EmitScalarSetF(context, op.Rd, sizeF); + } + + public static void Faddp_V(ILEmitterCtx context) + { + EmitVectorPairwiseOpF(context, () => context.Emit(OpCodes.Add)); + } + + public static void Fdiv_S(ILEmitterCtx context) + { + if (Optimizations.FastFP && Optimizations.UseSse + && Optimizations.UseSse2) + { + EmitScalarSseOrSse2OpF(context, nameof(Sse.DivideScalar)); + } + else + { + EmitScalarBinaryOpF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FPDiv)); + }); + } + } + + public static void Fdiv_V(ILEmitterCtx context) + { + if (Optimizations.FastFP && Optimizations.UseSse + && Optimizations.UseSse2) + { + EmitVectorSseOrSse2OpF(context, nameof(Sse.Divide)); + } + else + { + EmitVectorBinaryOpF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FPDiv)); + }); + } + } + + public static void Fmadd_S(ILEmitterCtx context) + { + if (Optimizations.FastFP && Optimizations.UseSse2) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + if (op.Size == 0) + { + Type[] typesMulAdd = new Type[] { typeof(Vector128), typeof(Vector128) }; + + context.EmitLdvec(op.Ra); + context.EmitLdvec(op.Rn); + context.EmitLdvec(op.Rm); + + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.MultiplyScalar), typesMulAdd)); + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.AddScalar), typesMulAdd)); + + context.EmitStvec(op.Rd); + + EmitVectorZero32_128(context, op.Rd); + } + else /* if (Op.Size == 1) */ + { + Type[] typesMulAdd = new Type[] { typeof(Vector128), typeof(Vector128) }; + + EmitLdvecWithCastToDouble(context, op.Ra); + EmitLdvecWithCastToDouble(context, op.Rn); + EmitLdvecWithCastToDouble(context, op.Rm); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.MultiplyScalar), typesMulAdd)); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.AddScalar), typesMulAdd)); + + EmitStvecWithCastFromDouble(context, op.Rd); + + EmitVectorZeroUpper(context, op.Rd); + } + } + else + { + EmitScalarTernaryRaOpF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FPMulAdd)); + }); + } + } + + public static void Fmax_S(ILEmitterCtx context) + { + if (Optimizations.FastFP && Optimizations.UseSse + && Optimizations.UseSse2) + { + EmitScalarSseOrSse2OpF(context, nameof(Sse.MaxScalar)); + } + else + { + EmitScalarBinaryOpF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FPMax)); + }); + } + } + + public static void Fmax_V(ILEmitterCtx context) + { + if (Optimizations.FastFP && Optimizations.UseSse + && Optimizations.UseSse2) + { + EmitVectorSseOrSse2OpF(context, nameof(Sse.Max)); + } + else + { + EmitVectorBinaryOpF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FPMax)); + }); + } + } + + public static void Fmaxnm_S(ILEmitterCtx context) + { + EmitScalarBinaryOpF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FPMaxNum)); + }); + } + + public static void Fmaxnm_V(ILEmitterCtx context) + { + EmitVectorBinaryOpF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FPMaxNum)); + }); + } + + public static void Fmaxp_V(ILEmitterCtx context) + { + EmitVectorPairwiseOpF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FPMax)); + }); + } + + public static void Fmin_S(ILEmitterCtx context) + { + if (Optimizations.FastFP && Optimizations.UseSse + && Optimizations.UseSse2) + { + EmitScalarSseOrSse2OpF(context, nameof(Sse.MinScalar)); + } + else + { + EmitScalarBinaryOpF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FPMin)); + }); + } + } + + public static void Fmin_V(ILEmitterCtx context) + { + if (Optimizations.FastFP && Optimizations.UseSse + && Optimizations.UseSse2) + { + EmitVectorSseOrSse2OpF(context, nameof(Sse.Min)); + } + else + { + EmitVectorBinaryOpF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FPMin)); + }); + } + } + + public static void Fminnm_S(ILEmitterCtx context) + { + EmitScalarBinaryOpF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FPMinNum)); + }); + } + + public static void Fminnm_V(ILEmitterCtx context) + { + EmitVectorBinaryOpF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FPMinNum)); + }); + } + + public static void Fminp_V(ILEmitterCtx context) + { + EmitVectorPairwiseOpF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FPMin)); + }); + } + + public static void Fmla_Se(ILEmitterCtx context) + { + EmitScalarTernaryOpByElemF(context, () => + { + context.Emit(OpCodes.Mul); + context.Emit(OpCodes.Add); + }); + } + + public static void Fmla_V(ILEmitterCtx context) + { + EmitVectorTernaryOpF(context, () => + { + context.Emit(OpCodes.Mul); + context.Emit(OpCodes.Add); + }); + } + + public static void Fmla_Ve(ILEmitterCtx context) + { + EmitVectorTernaryOpByElemF(context, () => + { + context.Emit(OpCodes.Mul); + context.Emit(OpCodes.Add); + }); + } + + public static void Fmls_Se(ILEmitterCtx context) + { + EmitScalarTernaryOpByElemF(context, () => + { + context.Emit(OpCodes.Mul); + context.Emit(OpCodes.Sub); + }); + } + + public static void Fmls_V(ILEmitterCtx context) + { + EmitVectorTernaryOpF(context, () => + { + context.Emit(OpCodes.Mul); + context.Emit(OpCodes.Sub); + }); + } + + public static void Fmls_Ve(ILEmitterCtx context) + { + EmitVectorTernaryOpByElemF(context, () => + { + context.Emit(OpCodes.Mul); + context.Emit(OpCodes.Sub); + }); + } + + public static void Fmsub_S(ILEmitterCtx context) + { + if (Optimizations.FastFP && Optimizations.UseSse2) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + if (op.Size == 0) + { + Type[] typesMulSub = new Type[] { typeof(Vector128), typeof(Vector128) }; + + context.EmitLdvec(op.Ra); + context.EmitLdvec(op.Rn); + context.EmitLdvec(op.Rm); + + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.MultiplyScalar), typesMulSub)); + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.SubtractScalar), typesMulSub)); + + context.EmitStvec(op.Rd); + + EmitVectorZero32_128(context, op.Rd); + } + else /* if (Op.Size == 1) */ + { + Type[] typesMulSub = new Type[] { typeof(Vector128), typeof(Vector128) }; + + EmitLdvecWithCastToDouble(context, op.Ra); + EmitLdvecWithCastToDouble(context, op.Rn); + EmitLdvecWithCastToDouble(context, op.Rm); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.MultiplyScalar), typesMulSub)); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SubtractScalar), typesMulSub)); + + EmitStvecWithCastFromDouble(context, op.Rd); + + EmitVectorZeroUpper(context, op.Rd); + } + } + else + { + EmitScalarTernaryRaOpF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FPMulSub)); + }); + } + } + + public static void Fmul_S(ILEmitterCtx context) + { + if (Optimizations.FastFP && Optimizations.UseSse + && Optimizations.UseSse2) + { + EmitScalarSseOrSse2OpF(context, nameof(Sse.MultiplyScalar)); + } + else + { + EmitScalarBinaryOpF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FPMul)); + }); + } + } + + public static void Fmul_Se(ILEmitterCtx context) + { + EmitScalarBinaryOpByElemF(context, () => context.Emit(OpCodes.Mul)); + } + + public static void Fmul_V(ILEmitterCtx context) + { + if (Optimizations.FastFP && Optimizations.UseSse + && Optimizations.UseSse2) + { + EmitVectorSseOrSse2OpF(context, nameof(Sse.Multiply)); + } + else + { + EmitVectorBinaryOpF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FPMul)); + }); + } + } + + public static void Fmul_Ve(ILEmitterCtx context) + { + EmitVectorBinaryOpByElemF(context, () => context.Emit(OpCodes.Mul)); + } + + public static void Fmulx_S(ILEmitterCtx context) + { + EmitScalarBinaryOpF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FPMulX)); + }); + } + + public static void Fmulx_Se(ILEmitterCtx context) + { + EmitScalarBinaryOpByElemF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FPMulX)); + }); + } + + public static void Fmulx_V(ILEmitterCtx context) + { + EmitVectorBinaryOpF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FPMulX)); + }); + } + + public static void Fmulx_Ve(ILEmitterCtx context) + { + EmitVectorBinaryOpByElemF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FPMulX)); + }); + } + + public static void Fneg_S(ILEmitterCtx context) + { + EmitScalarUnaryOpF(context, () => context.Emit(OpCodes.Neg)); + } + + public static void Fneg_V(ILEmitterCtx context) + { + EmitVectorUnaryOpF(context, () => context.Emit(OpCodes.Neg)); + } + + public static void Fnmadd_S(ILEmitterCtx context) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + int sizeF = op.Size & 1; + + EmitVectorExtractF(context, op.Rn, 0, sizeF); + + context.Emit(OpCodes.Neg); + + 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 Fnmsub_S(ILEmitterCtx context) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)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 Fnmul_S(ILEmitterCtx context) + { + EmitScalarBinaryOpF(context, () => + { + context.Emit(OpCodes.Mul); + context.Emit(OpCodes.Neg); + }); + } + + public static void Frecpe_S(ILEmitterCtx context) + { + EmitScalarUnaryOpF(context, () => + { + EmitUnarySoftFloatCall(context, nameof(SoftFloat.RecipEstimate)); + }); + } + + public static void Frecpe_V(ILEmitterCtx context) + { + EmitVectorUnaryOpF(context, () => + { + EmitUnarySoftFloatCall(context, nameof(SoftFloat.RecipEstimate)); + }); + } + + public static void Frecps_S(ILEmitterCtx context) + { + if (Optimizations.FastFP && Optimizations.UseSse2) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + int sizeF = op.Size & 1; + + if (sizeF == 0) + { + Type[] typesSsv = new Type[] { typeof(float) }; + Type[] typesMulSub = new Type[] { typeof(Vector128), typeof(Vector128) }; + + context.EmitLdc_R4(2f); + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.SetScalarVector128), typesSsv)); + + context.EmitLdvec(op.Rn); + context.EmitLdvec(op.Rm); + + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.MultiplyScalar), typesMulSub)); + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.SubtractScalar), typesMulSub)); + + context.EmitStvec(op.Rd); + + EmitVectorZero32_128(context, op.Rd); + } + else /* if (SizeF == 1) */ + { + Type[] typesSsv = new Type[] { typeof(double) }; + Type[] typesMulSub = new Type[] { typeof(Vector128), typeof(Vector128) }; + + context.EmitLdc_R8(2d); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetScalarVector128), typesSsv)); + + EmitLdvecWithCastToDouble(context, op.Rn); + EmitLdvecWithCastToDouble(context, op.Rm); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.MultiplyScalar), typesMulSub)); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SubtractScalar), typesMulSub)); + + EmitStvecWithCastFromDouble(context, op.Rd); + + EmitVectorZeroUpper(context, op.Rd); + } + } + else + { + EmitScalarBinaryOpF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FPRecipStepFused)); + }); + } + } + + public static void Frecps_V(ILEmitterCtx context) + { + if (Optimizations.FastFP && Optimizations.UseSse2) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + int sizeF = op.Size & 1; + + if (sizeF == 0) + { + Type[] typesSav = new Type[] { typeof(float) }; + Type[] typesMulSub = new Type[] { typeof(Vector128), typeof(Vector128) }; + + context.EmitLdc_R4(2f); + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.SetAllVector128), typesSav)); + + context.EmitLdvec(op.Rn); + context.EmitLdvec(op.Rm); + + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.Multiply), typesMulSub)); + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.Subtract), typesMulSub)); + + context.EmitStvec(op.Rd); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + else /* if (SizeF == 1) */ + { + Type[] typesSav = new Type[] { typeof(double) }; + Type[] typesMulSub = new Type[] { typeof(Vector128), typeof(Vector128) }; + + context.EmitLdc_R8(2d); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetAllVector128), typesSav)); + + EmitLdvecWithCastToDouble(context, op.Rn); + EmitLdvecWithCastToDouble(context, op.Rm); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Multiply), typesMulSub)); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Subtract), typesMulSub)); + + EmitStvecWithCastFromDouble(context, op.Rd); + } + } + else + { + EmitVectorBinaryOpF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FPRecipStepFused)); + }); + } + } + + public static void Frecpx_S(ILEmitterCtx context) + { + EmitScalarUnaryOpF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FPRecpX)); + }); + } + + public static void Frinta_S(ILEmitterCtx context) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + EmitVectorExtractF(context, op.Rn, 0, op.Size); + + EmitRoundMathCall(context, MidpointRounding.AwayFromZero); + + EmitScalarSetF(context, op.Rd, op.Size); + } + + public static void Frinta_V(ILEmitterCtx context) + { + EmitVectorUnaryOpF(context, () => + { + EmitRoundMathCall(context, MidpointRounding.AwayFromZero); + }); + } + + public static void Frinti_S(ILEmitterCtx context) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + EmitScalarUnaryOpF(context, () => + { + context.EmitLdarg(TranslatedSub.StateArgIdx); + + if (op.Size == 0) + { + VectorHelper.EmitCall(context, nameof(VectorHelper.RoundF)); + } + else if (op.Size == 1) + { + VectorHelper.EmitCall(context, nameof(VectorHelper.Round)); + } + else + { + throw new InvalidOperationException(); + } + }); + } + + public static void Frinti_V(ILEmitterCtx context) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + int sizeF = op.Size & 1; + + EmitVectorUnaryOpF(context, () => + { + context.EmitLdarg(TranslatedSub.StateArgIdx); + + if (sizeF == 0) + { + VectorHelper.EmitCall(context, nameof(VectorHelper.RoundF)); + } + else if (sizeF == 1) + { + VectorHelper.EmitCall(context, nameof(VectorHelper.Round)); + } + else + { + throw new InvalidOperationException(); + } + }); + } + + public static void Frintm_S(ILEmitterCtx context) + { + EmitScalarUnaryOpF(context, () => + { + EmitUnaryMathCall(context, nameof(Math.Floor)); + }); + } + + public static void Frintm_V(ILEmitterCtx context) + { + EmitVectorUnaryOpF(context, () => + { + EmitUnaryMathCall(context, nameof(Math.Floor)); + }); + } + + public static void Frintn_S(ILEmitterCtx context) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + EmitVectorExtractF(context, op.Rn, 0, op.Size); + + EmitRoundMathCall(context, MidpointRounding.ToEven); + + EmitScalarSetF(context, op.Rd, op.Size); + } + + public static void Frintn_V(ILEmitterCtx context) + { + EmitVectorUnaryOpF(context, () => + { + EmitRoundMathCall(context, MidpointRounding.ToEven); + }); + } + + public static void Frintp_S(ILEmitterCtx context) + { + EmitScalarUnaryOpF(context, () => + { + EmitUnaryMathCall(context, nameof(Math.Ceiling)); + }); + } + + public static void Frintp_V(ILEmitterCtx context) + { + EmitVectorUnaryOpF(context, () => + { + EmitUnaryMathCall(context, nameof(Math.Ceiling)); + }); + } + + public static void Frintx_S(ILEmitterCtx context) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + EmitScalarUnaryOpF(context, () => + { + context.EmitLdarg(TranslatedSub.StateArgIdx); + + if (op.Size == 0) + { + VectorHelper.EmitCall(context, nameof(VectorHelper.RoundF)); + } + else if (op.Size == 1) + { + VectorHelper.EmitCall(context, nameof(VectorHelper.Round)); + } + else + { + throw new InvalidOperationException(); + } + }); + } + + public static void Frintx_V(ILEmitterCtx context) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + EmitVectorUnaryOpF(context, () => + { + context.EmitLdarg(TranslatedSub.StateArgIdx); + + if (op.Size == 0) + { + VectorHelper.EmitCall(context, nameof(VectorHelper.RoundF)); + } + else if (op.Size == 1) + { + VectorHelper.EmitCall(context, nameof(VectorHelper.Round)); + } + else + { + throw new InvalidOperationException(); + } + }); + } + + public static void Frsqrte_S(ILEmitterCtx context) + { + EmitScalarUnaryOpF(context, () => + { + EmitUnarySoftFloatCall(context, nameof(SoftFloat.InvSqrtEstimate)); + }); + } + + public static void Frsqrte_V(ILEmitterCtx context) + { + EmitVectorUnaryOpF(context, () => + { + EmitUnarySoftFloatCall(context, nameof(SoftFloat.InvSqrtEstimate)); + }); + } + + public static void Frsqrts_S(ILEmitterCtx context) + { + if (Optimizations.FastFP && Optimizations.UseSse2) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + int sizeF = op.Size & 1; + + if (sizeF == 0) + { + Type[] typesSsv = new Type[] { typeof(float) }; + Type[] typesMulSub = new Type[] { typeof(Vector128), typeof(Vector128) }; + + context.EmitLdc_R4(0.5f); + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.SetScalarVector128), typesSsv)); + + context.EmitLdc_R4(3f); + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.SetScalarVector128), typesSsv)); + + context.EmitLdvec(op.Rn); + context.EmitLdvec(op.Rm); + + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.MultiplyScalar), typesMulSub)); + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.SubtractScalar), typesMulSub)); + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.MultiplyScalar), typesMulSub)); + + context.EmitStvec(op.Rd); + + EmitVectorZero32_128(context, op.Rd); + } + else /* if (SizeF == 1) */ + { + Type[] typesSsv = new Type[] { typeof(double) }; + Type[] typesMulSub = new Type[] { typeof(Vector128), typeof(Vector128) }; + + context.EmitLdc_R8(0.5d); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetScalarVector128), typesSsv)); + + context.EmitLdc_R8(3d); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetScalarVector128), typesSsv)); + + EmitLdvecWithCastToDouble(context, op.Rn); + EmitLdvecWithCastToDouble(context, op.Rm); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.MultiplyScalar), typesMulSub)); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SubtractScalar), typesMulSub)); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.MultiplyScalar), typesMulSub)); + + EmitStvecWithCastFromDouble(context, op.Rd); + + EmitVectorZeroUpper(context, op.Rd); + } + } + else + { + EmitScalarBinaryOpF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FprSqrtStepFused)); + }); + } + } + + public static void Frsqrts_V(ILEmitterCtx context) + { + if (Optimizations.FastFP && Optimizations.UseSse2) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + int sizeF = op.Size & 1; + + if (sizeF == 0) + { + Type[] typesSav = new Type[] { typeof(float) }; + Type[] typesMulSub = new Type[] { typeof(Vector128), typeof(Vector128) }; + + context.EmitLdc_R4(0.5f); + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.SetAllVector128), typesSav)); + + context.EmitLdc_R4(3f); + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.SetAllVector128), typesSav)); + + context.EmitLdvec(op.Rn); + context.EmitLdvec(op.Rm); + + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.Multiply), typesMulSub)); + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.Subtract), typesMulSub)); + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.Multiply), typesMulSub)); + + context.EmitStvec(op.Rd); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + else /* if (SizeF == 1) */ + { + Type[] typesSav = new Type[] { typeof(double) }; + Type[] typesMulSub = new Type[] { typeof(Vector128), typeof(Vector128) }; + + context.EmitLdc_R8(0.5d); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetAllVector128), typesSav)); + + context.EmitLdc_R8(3d); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetAllVector128), typesSav)); + + EmitLdvecWithCastToDouble(context, op.Rn); + EmitLdvecWithCastToDouble(context, op.Rm); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Multiply), typesMulSub)); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Subtract), typesMulSub)); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Multiply), typesMulSub)); + + EmitStvecWithCastFromDouble(context, op.Rd); + } + } + else + { + EmitVectorBinaryOpF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FprSqrtStepFused)); + }); + } + } + + public static void Fsqrt_S(ILEmitterCtx context) + { + if (Optimizations.FastFP && Optimizations.UseSse + && Optimizations.UseSse2) + { + EmitScalarSseOrSse2OpF(context, nameof(Sse.SqrtScalar)); + } + else + { + EmitScalarUnaryOpF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FPSqrt)); + }); + } + } + + public static void Fsqrt_V(ILEmitterCtx context) + { + if (Optimizations.FastFP && Optimizations.UseSse + && Optimizations.UseSse2) + { + EmitVectorSseOrSse2OpF(context, nameof(Sse.Sqrt)); + } + else + { + EmitVectorUnaryOpF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FPSqrt)); + }); + } + } + + public static void Fsub_S(ILEmitterCtx context) + { + if (Optimizations.FastFP && Optimizations.UseSse + && Optimizations.UseSse2) + { + EmitScalarSseOrSse2OpF(context, nameof(Sse.SubtractScalar)); + } + else + { + EmitScalarBinaryOpF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FPSub)); + }); + } + } + + public static void Fsub_V(ILEmitterCtx context) + { + if (Optimizations.FastFP && Optimizations.UseSse + && Optimizations.UseSse2) + { + EmitVectorSseOrSse2OpF(context, nameof(Sse.Subtract)); + } + else + { + EmitVectorBinaryOpF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FPSub)); + }); + } + } + + public static void Mla_V(ILEmitterCtx context) + { + EmitVectorTernaryOpZx(context, () => + { + context.Emit(OpCodes.Mul); + context.Emit(OpCodes.Add); + }); + } + + public static void Mla_Ve(ILEmitterCtx context) + { + EmitVectorTernaryOpByElemZx(context, () => + { + context.Emit(OpCodes.Mul); + context.Emit(OpCodes.Add); + }); + } + + public static void Mls_V(ILEmitterCtx context) + { + EmitVectorTernaryOpZx(context, () => + { + context.Emit(OpCodes.Mul); + context.Emit(OpCodes.Sub); + }); + } + + public static void Mls_Ve(ILEmitterCtx context) + { + EmitVectorTernaryOpByElemZx(context, () => + { + context.Emit(OpCodes.Mul); + context.Emit(OpCodes.Sub); + }); + } + + public static void Mul_V(ILEmitterCtx context) + { + EmitVectorBinaryOpZx(context, () => context.Emit(OpCodes.Mul)); + } + + public static void Mul_Ve(ILEmitterCtx context) + { + EmitVectorBinaryOpByElemZx(context, () => context.Emit(OpCodes.Mul)); + } + + public static void Neg_S(ILEmitterCtx context) + { + EmitScalarUnaryOpSx(context, () => context.Emit(OpCodes.Neg)); + } + + public static void Neg_V(ILEmitterCtx context) + { + EmitVectorUnaryOpSx(context, () => context.Emit(OpCodes.Neg)); + } + + public static void Raddhn_V(ILEmitterCtx context) + { + EmitHighNarrow(context, () => context.Emit(OpCodes.Add), round: true); + } + + public static void Rsubhn_V(ILEmitterCtx context) + { + EmitHighNarrow(context, () => context.Emit(OpCodes.Sub), round: true); + } + + public static void Saba_V(ILEmitterCtx context) + { + EmitVectorTernaryOpSx(context, () => + { + context.Emit(OpCodes.Sub); + EmitAbs(context); + + context.Emit(OpCodes.Add); + }); + } + + public static void Sabal_V(ILEmitterCtx context) + { + EmitVectorWidenRnRmTernaryOpSx(context, () => + { + context.Emit(OpCodes.Sub); + EmitAbs(context); + + context.Emit(OpCodes.Add); + }); + } + + public static void Sabd_V(ILEmitterCtx context) + { + EmitVectorBinaryOpSx(context, () => + { + context.Emit(OpCodes.Sub); + EmitAbs(context); + }); + } + + public static void Sabdl_V(ILEmitterCtx context) + { + EmitVectorWidenRnRmBinaryOpSx(context, () => + { + context.Emit(OpCodes.Sub); + EmitAbs(context); + }); + } + + public static void Sadalp_V(ILEmitterCtx context) + { + EmitAddLongPairwise(context, signed: true, accumulate: true); + } + + public static void Saddl_V(ILEmitterCtx context) + { + if (Optimizations.UseSse41) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + Type[] typesSrl = new Type[] { VectorIntTypesPerSizeLog2[op.Size], typeof(byte) }; + Type[] typesCvt = new Type[] { VectorIntTypesPerSizeLog2[op.Size] }; + Type[] typesAdd = new Type[] { VectorIntTypesPerSizeLog2[op.Size + 1], + VectorIntTypesPerSizeLog2[op.Size + 1] }; + + string[] namesCvt = new string[] { nameof(Sse41.ConvertToVector128Int16), + nameof(Sse41.ConvertToVector128Int32), + nameof(Sse41.ConvertToVector128Int64) }; + + int numBytes = op.RegisterSize == RegisterSize.Simd128 ? 8 : 0; + + EmitLdvecWithSignedCast(context, op.Rn, op.Size); + + context.EmitLdc_I4(numBytes); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + + context.EmitCall(typeof(Sse41).GetMethod(namesCvt[op.Size], typesCvt)); + + EmitLdvecWithUnsignedCast(context, op.Rm, op.Size); + + context.EmitLdc_I4(numBytes); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + + context.EmitCall(typeof(Sse41).GetMethod(namesCvt[op.Size], typesCvt)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), typesAdd)); + + EmitStvecWithSignedCast(context, op.Rd, op.Size + 1); + } + else + { + EmitVectorWidenRnRmBinaryOpSx(context, () => context.Emit(OpCodes.Add)); + } + } + + public static void Saddlp_V(ILEmitterCtx context) + { + EmitAddLongPairwise(context, signed: true, accumulate: false); + } + + public static void Saddw_V(ILEmitterCtx context) + { + EmitVectorWidenRmBinaryOpSx(context, () => context.Emit(OpCodes.Add)); + } + + public static void Shadd_V(ILEmitterCtx context) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + if (Optimizations.UseSse2 && op.Size > 0) + { + Type[] typesSra = new Type[] { VectorIntTypesPerSizeLog2[op.Size], typeof(byte) }; + Type[] typesAndXorAdd = new Type[] { VectorIntTypesPerSizeLog2[op.Size], VectorIntTypesPerSizeLog2[op.Size] }; + + EmitLdvecWithSignedCast(context, op.Rn, op.Size); + + context.Emit(OpCodes.Dup); + context.EmitStvectmp(); + + EmitLdvecWithSignedCast(context, op.Rm, op.Size); + + context.Emit(OpCodes.Dup); + context.EmitStvectmp2(); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.And), typesAndXorAdd)); + + context.EmitLdvectmp(); + context.EmitLdvectmp2(); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Xor), typesAndXorAdd)); + + context.EmitLdc_I4(1); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightArithmetic), typesSra)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), typesAndXorAdd)); + + EmitStvecWithSignedCast(context, op.Rd, op.Size); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + else + { + EmitVectorBinaryOpSx(context, () => + { + context.Emit(OpCodes.Add); + + context.Emit(OpCodes.Ldc_I4_1); + context.Emit(OpCodes.Shr); + }); + } + } + + public static void Shsub_V(ILEmitterCtx context) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + if (Optimizations.UseSse2 && op.Size < 2) + { + Type[] typesSav = new Type[] { IntTypesPerSizeLog2[op.Size] }; + Type[] typesAddSub = new Type[] { VectorIntTypesPerSizeLog2 [op.Size], VectorIntTypesPerSizeLog2 [op.Size] }; + Type[] typesAvg = new Type[] { VectorUIntTypesPerSizeLog2[op.Size], VectorUIntTypesPerSizeLog2[op.Size] }; + + context.EmitLdc_I4(op.Size == 0 ? sbyte.MinValue : short.MinValue); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetAllVector128), typesSav)); + + context.EmitStvectmp(); + + EmitLdvecWithSignedCast(context, op.Rn, op.Size); + context.EmitLdvectmp(); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), typesAddSub)); + + context.Emit(OpCodes.Dup); + + EmitLdvecWithSignedCast(context, op.Rm, op.Size); + context.EmitLdvectmp(); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), typesAddSub)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Average), typesAvg)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Subtract), typesAddSub)); + + EmitStvecWithSignedCast(context, op.Rd, op.Size); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + else + { + EmitVectorBinaryOpSx(context, () => + { + context.Emit(OpCodes.Sub); + + context.Emit(OpCodes.Ldc_I4_1); + context.Emit(OpCodes.Shr); + }); + } + } + + public static void Smax_V(ILEmitterCtx 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 Smaxp_V(ILEmitterCtx context) + { + Type[] types = new Type[] { typeof(long), typeof(long) }; + + MethodInfo mthdInfo = typeof(Math).GetMethod(nameof(Math.Max), types); + + EmitVectorPairwiseOpSx(context, () => context.EmitCall(mthdInfo)); + } + + public static void Smin_V(ILEmitterCtx 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 Sminp_V(ILEmitterCtx context) + { + Type[] types = new Type[] { typeof(long), typeof(long) }; + + MethodInfo mthdInfo = typeof(Math).GetMethod(nameof(Math.Min), types); + + EmitVectorPairwiseOpSx(context, () => context.EmitCall(mthdInfo)); + } + + public static void Smlal_V(ILEmitterCtx context) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + if (Optimizations.UseSse41 && op.Size < 2) + { + Type[] typesSrl = new Type[] { VectorIntTypesPerSizeLog2[op.Size], typeof(byte) }; + Type[] typesCvt = new Type[] { VectorIntTypesPerSizeLog2[op.Size] }; + Type[] typesMulAdd = new Type[] { VectorIntTypesPerSizeLog2[op.Size + 1], + VectorIntTypesPerSizeLog2[op.Size + 1] }; + + Type typeMul = op.Size == 0 ? typeof(Sse2) : typeof(Sse41); + + string nameCvt = op.Size == 0 + ? nameof(Sse41.ConvertToVector128Int16) + : nameof(Sse41.ConvertToVector128Int32); + + int numBytes = op.RegisterSize == RegisterSize.Simd128 ? 8 : 0; + + EmitLdvecWithSignedCast(context, op.Rd, op.Size + 1); + + EmitLdvecWithSignedCast(context, op.Rn, op.Size); + + context.EmitLdc_I4(numBytes); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + + context.EmitCall(typeof(Sse41).GetMethod(nameCvt, typesCvt)); + + EmitLdvecWithSignedCast(context, op.Rm, op.Size); + + context.EmitLdc_I4(numBytes); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + + context.EmitCall(typeof(Sse41).GetMethod(nameCvt, typesCvt)); + + context.EmitCall(typeMul.GetMethod(nameof(Sse2.MultiplyLow), typesMulAdd)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), typesMulAdd)); + + EmitStvecWithSignedCast(context, op.Rd, op.Size + 1); + } + else + { + EmitVectorWidenRnRmTernaryOpSx(context, () => + { + context.Emit(OpCodes.Mul); + context.Emit(OpCodes.Add); + }); + } + } + + public static void Smlsl_V(ILEmitterCtx context) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + if (Optimizations.UseSse41 && op.Size < 2) + { + Type[] typesSrl = new Type[] { VectorIntTypesPerSizeLog2[op.Size], typeof(byte) }; + Type[] typesCvt = new Type[] { VectorIntTypesPerSizeLog2[op.Size] }; + Type[] typesMulSub = new Type[] { VectorIntTypesPerSizeLog2[op.Size + 1], + VectorIntTypesPerSizeLog2[op.Size + 1] }; + + Type typeMul = op.Size == 0 ? typeof(Sse2) : typeof(Sse41); + + string nameCvt = op.Size == 0 + ? nameof(Sse41.ConvertToVector128Int16) + : nameof(Sse41.ConvertToVector128Int32); + + int numBytes = op.RegisterSize == RegisterSize.Simd128 ? 8 : 0; + + EmitLdvecWithSignedCast(context, op.Rd, op.Size + 1); + + EmitLdvecWithSignedCast(context, op.Rn, op.Size); + + context.EmitLdc_I4(numBytes); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + + context.EmitCall(typeof(Sse41).GetMethod(nameCvt, typesCvt)); + + EmitLdvecWithSignedCast(context, op.Rm, op.Size); + + context.EmitLdc_I4(numBytes); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + + context.EmitCall(typeof(Sse41).GetMethod(nameCvt, typesCvt)); + + context.EmitCall(typeMul.GetMethod(nameof(Sse2.MultiplyLow), typesMulSub)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Subtract), typesMulSub)); + + EmitStvecWithSignedCast(context, op.Rd, op.Size + 1); + } + else + { + EmitVectorWidenRnRmTernaryOpSx(context, () => + { + context.Emit(OpCodes.Mul); + context.Emit(OpCodes.Sub); + }); + } + } + + public static void Smull_V(ILEmitterCtx context) + { + EmitVectorWidenRnRmBinaryOpSx(context, () => context.Emit(OpCodes.Mul)); + } + + public static void Sqabs_S(ILEmitterCtx context) + { + EmitScalarSaturatingUnaryOpSx(context, () => EmitAbs(context)); + } + + public static void Sqabs_V(ILEmitterCtx context) + { + EmitVectorSaturatingUnaryOpSx(context, () => EmitAbs(context)); + } + + public static void Sqadd_S(ILEmitterCtx context) + { + EmitScalarSaturatingBinaryOpSx(context, SaturatingFlags.Add); + } + + public static void Sqadd_V(ILEmitterCtx context) + { + EmitVectorSaturatingBinaryOpSx(context, SaturatingFlags.Add); + } + + public static void Sqdmulh_S(ILEmitterCtx context) + { + EmitSaturatingBinaryOp(context, () => EmitDoublingMultiplyHighHalf(context, round: false), SaturatingFlags.ScalarSx); + } + + public static void Sqdmulh_V(ILEmitterCtx context) + { + EmitSaturatingBinaryOp(context, () => EmitDoublingMultiplyHighHalf(context, round: false), SaturatingFlags.VectorSx); + } + + public static void Sqneg_S(ILEmitterCtx context) + { + EmitScalarSaturatingUnaryOpSx(context, () => context.Emit(OpCodes.Neg)); + } + + public static void Sqneg_V(ILEmitterCtx context) + { + EmitVectorSaturatingUnaryOpSx(context, () => context.Emit(OpCodes.Neg)); + } + + public static void Sqrdmulh_S(ILEmitterCtx context) + { + EmitSaturatingBinaryOp(context, () => EmitDoublingMultiplyHighHalf(context, round: true), SaturatingFlags.ScalarSx); + } + + public static void Sqrdmulh_V(ILEmitterCtx context) + { + EmitSaturatingBinaryOp(context, () => EmitDoublingMultiplyHighHalf(context, round: true), SaturatingFlags.VectorSx); + } + + public static void Sqsub_S(ILEmitterCtx context) + { + EmitScalarSaturatingBinaryOpSx(context, SaturatingFlags.Sub); + } + + public static void Sqsub_V(ILEmitterCtx context) + { + EmitVectorSaturatingBinaryOpSx(context, SaturatingFlags.Sub); + } + + public static void Sqxtn_S(ILEmitterCtx context) + { + EmitSaturatingNarrowOp(context, SaturatingNarrowFlags.ScalarSxSx); + } + + public static void Sqxtn_V(ILEmitterCtx context) + { + EmitSaturatingNarrowOp(context, SaturatingNarrowFlags.VectorSxSx); + } + + public static void Sqxtun_S(ILEmitterCtx context) + { + EmitSaturatingNarrowOp(context, SaturatingNarrowFlags.ScalarSxZx); + } + + public static void Sqxtun_V(ILEmitterCtx context) + { + EmitSaturatingNarrowOp(context, SaturatingNarrowFlags.VectorSxZx); + } + + public static void Srhadd_V(ILEmitterCtx context) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + if (Optimizations.UseSse2 && op.Size < 2) + { + Type[] typesSav = new Type[] { IntTypesPerSizeLog2[op.Size] }; + Type[] typesSubAdd = new Type[] { VectorIntTypesPerSizeLog2 [op.Size], VectorIntTypesPerSizeLog2 [op.Size] }; + Type[] typesAvg = new Type[] { VectorUIntTypesPerSizeLog2[op.Size], VectorUIntTypesPerSizeLog2[op.Size] }; + + context.EmitLdc_I4(op.Size == 0 ? sbyte.MinValue : short.MinValue); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetAllVector128), typesSav)); + + context.Emit(OpCodes.Dup); + context.EmitStvectmp(); + + EmitLdvecWithSignedCast(context, op.Rn, op.Size); + context.EmitLdvectmp(); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Subtract), typesSubAdd)); + + EmitLdvecWithSignedCast(context, op.Rm, op.Size); + context.EmitLdvectmp(); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Subtract), typesSubAdd)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Average), typesAvg)); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), typesSubAdd)); + + EmitStvecWithSignedCast(context, op.Rd, op.Size); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + else + { + EmitVectorBinaryOpSx(context, () => + { + context.Emit(OpCodes.Add); + + context.Emit(OpCodes.Ldc_I4_1); + context.Emit(OpCodes.Add); + + context.Emit(OpCodes.Ldc_I4_1); + context.Emit(OpCodes.Shr); + }); + } + } + + public static void Ssubl_V(ILEmitterCtx context) + { + if (Optimizations.UseSse41) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + Type[] typesSrl = new Type[] { VectorIntTypesPerSizeLog2[op.Size], typeof(byte) }; + Type[] typesCvt = new Type[] { VectorIntTypesPerSizeLog2[op.Size] }; + Type[] typesSub = new Type[] { VectorIntTypesPerSizeLog2[op.Size + 1], + VectorIntTypesPerSizeLog2[op.Size + 1] }; + + string[] namesCvt = new string[] { nameof(Sse41.ConvertToVector128Int16), + nameof(Sse41.ConvertToVector128Int32), + nameof(Sse41.ConvertToVector128Int64) }; + + int numBytes = op.RegisterSize == RegisterSize.Simd128 ? 8 : 0; + + EmitLdvecWithSignedCast(context, op.Rn, op.Size); + + context.EmitLdc_I4(numBytes); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + + context.EmitCall(typeof(Sse41).GetMethod(namesCvt[op.Size], typesCvt)); + + EmitLdvecWithUnsignedCast(context, op.Rm, op.Size); + + context.EmitLdc_I4(numBytes); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + + context.EmitCall(typeof(Sse41).GetMethod(namesCvt[op.Size], typesCvt)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Subtract), typesSub)); + + EmitStvecWithSignedCast(context, op.Rd, op.Size + 1); + } + else + { + EmitVectorWidenRnRmBinaryOpSx(context, () => context.Emit(OpCodes.Sub)); + } + } + + public static void Ssubw_V(ILEmitterCtx context) + { + EmitVectorWidenRmBinaryOpSx(context, () => context.Emit(OpCodes.Sub)); + } + + public static void Sub_S(ILEmitterCtx context) + { + EmitScalarBinaryOpZx(context, () => context.Emit(OpCodes.Sub)); + } + + public static void Sub_V(ILEmitterCtx context) + { + if (Optimizations.UseSse2) + { + EmitSse2Op(context, nameof(Sse2.Subtract)); + } + else + { + EmitVectorBinaryOpZx(context, () => context.Emit(OpCodes.Sub)); + } + } + + public static void Subhn_V(ILEmitterCtx context) + { + EmitHighNarrow(context, () => context.Emit(OpCodes.Sub), round: false); + } + + public static void Suqadd_S(ILEmitterCtx context) + { + EmitScalarSaturatingBinaryOpSx(context, SaturatingFlags.Accumulate); + } + + public static void Suqadd_V(ILEmitterCtx context) + { + EmitVectorSaturatingBinaryOpSx(context, SaturatingFlags.Accumulate); + } + + public static void Uaba_V(ILEmitterCtx context) + { + EmitVectorTernaryOpZx(context, () => + { + context.Emit(OpCodes.Sub); + EmitAbs(context); + + context.Emit(OpCodes.Add); + }); + } + + public static void Uabal_V(ILEmitterCtx context) + { + EmitVectorWidenRnRmTernaryOpZx(context, () => + { + context.Emit(OpCodes.Sub); + EmitAbs(context); + + context.Emit(OpCodes.Add); + }); + } + + public static void Uabd_V(ILEmitterCtx context) + { + EmitVectorBinaryOpZx(context, () => + { + context.Emit(OpCodes.Sub); + EmitAbs(context); + }); + } + + public static void Uabdl_V(ILEmitterCtx context) + { + EmitVectorWidenRnRmBinaryOpZx(context, () => + { + context.Emit(OpCodes.Sub); + EmitAbs(context); + }); + } + + public static void Uadalp_V(ILEmitterCtx context) + { + EmitAddLongPairwise(context, signed: false, accumulate: true); + } + + public static void Uaddl_V(ILEmitterCtx context) + { + if (Optimizations.UseSse41) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + Type[] typesSrl = new Type[] { VectorUIntTypesPerSizeLog2[op.Size], typeof(byte) }; + Type[] typesCvt = new Type[] { VectorUIntTypesPerSizeLog2[op.Size] }; + Type[] typesAdd = new Type[] { VectorUIntTypesPerSizeLog2[op.Size + 1], + VectorUIntTypesPerSizeLog2[op.Size + 1] }; + + string[] namesCvt = new string[] { nameof(Sse41.ConvertToVector128Int16), + nameof(Sse41.ConvertToVector128Int32), + nameof(Sse41.ConvertToVector128Int64) }; + + int numBytes = op.RegisterSize == RegisterSize.Simd128 ? 8 : 0; + + EmitLdvecWithUnsignedCast(context, op.Rn, op.Size); + + context.EmitLdc_I4(numBytes); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + + context.EmitCall(typeof(Sse41).GetMethod(namesCvt[op.Size], typesCvt)); + + EmitLdvecWithUnsignedCast(context, op.Rm, op.Size); + + context.EmitLdc_I4(numBytes); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + + context.EmitCall(typeof(Sse41).GetMethod(namesCvt[op.Size], typesCvt)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), typesAdd)); + + EmitStvecWithUnsignedCast(context, op.Rd, op.Size + 1); + } + else + { + EmitVectorWidenRnRmBinaryOpZx(context, () => context.Emit(OpCodes.Add)); + } + } + + public static void Uaddlp_V(ILEmitterCtx context) + { + EmitAddLongPairwise(context, signed: false, accumulate: false); + } + + public static void Uaddlv_V(ILEmitterCtx context) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + int bytes = op.GetBitsCount() >> 3; + int elems = bytes >> op.Size; + + EmitVectorExtractZx(context, op.Rn, 0, op.Size); + + for (int index = 1; index < elems; index++) + { + EmitVectorExtractZx(context, op.Rn, index, op.Size); + + context.Emit(OpCodes.Add); + } + + EmitScalarSet(context, op.Rd, op.Size + 1); + } + + public static void Uaddw_V(ILEmitterCtx context) + { + EmitVectorWidenRmBinaryOpZx(context, () => context.Emit(OpCodes.Add)); + } + + public static void Uhadd_V(ILEmitterCtx context) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + if (Optimizations.UseSse2 && op.Size > 0) + { + Type[] typesSrl = new Type[] { VectorUIntTypesPerSizeLog2[op.Size], typeof(byte) }; + Type[] typesAndXorAdd = new Type[] { VectorUIntTypesPerSizeLog2[op.Size], VectorUIntTypesPerSizeLog2[op.Size] }; + + EmitLdvecWithUnsignedCast(context, op.Rn, op.Size); + + context.Emit(OpCodes.Dup); + context.EmitStvectmp(); + + EmitLdvecWithUnsignedCast(context, op.Rm, op.Size); + + context.Emit(OpCodes.Dup); + context.EmitStvectmp2(); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.And), typesAndXorAdd)); + + context.EmitLdvectmp(); + context.EmitLdvectmp2(); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Xor), typesAndXorAdd)); + + context.EmitLdc_I4(1); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical), typesSrl)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), typesAndXorAdd)); + + EmitStvecWithUnsignedCast(context, op.Rd, op.Size); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + else + { + EmitVectorBinaryOpZx(context, () => + { + context.Emit(OpCodes.Add); + + context.Emit(OpCodes.Ldc_I4_1); + context.Emit(OpCodes.Shr_Un); + }); + } + } + + public static void Uhsub_V(ILEmitterCtx context) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + if (Optimizations.UseSse2 && op.Size < 2) + { + Type[] typesAvgSub = new Type[] { VectorUIntTypesPerSizeLog2[op.Size], VectorUIntTypesPerSizeLog2[op.Size] }; + + EmitLdvecWithUnsignedCast(context, op.Rn, op.Size); + context.Emit(OpCodes.Dup); + + EmitLdvecWithUnsignedCast(context, op.Rm, op.Size); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Average), typesAvgSub)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Subtract), typesAvgSub)); + + EmitStvecWithUnsignedCast(context, op.Rd, op.Size); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + else + { + EmitVectorBinaryOpZx(context, () => + { + context.Emit(OpCodes.Sub); + + context.Emit(OpCodes.Ldc_I4_1); + context.Emit(OpCodes.Shr_Un); + }); + } + } + + public static void Umax_V(ILEmitterCtx context) + { + Type[] types = new Type[] { typeof(ulong), typeof(ulong) }; + + MethodInfo mthdInfo = typeof(Math).GetMethod(nameof(Math.Max), types); + + EmitVectorBinaryOpZx(context, () => context.EmitCall(mthdInfo)); + } + + public static void Umaxp_V(ILEmitterCtx context) + { + Type[] types = new Type[] { typeof(ulong), typeof(ulong) }; + + MethodInfo mthdInfo = typeof(Math).GetMethod(nameof(Math.Max), types); + + EmitVectorPairwiseOpZx(context, () => context.EmitCall(mthdInfo)); + } + + public static void Umin_V(ILEmitterCtx context) + { + Type[] types = new Type[] { typeof(ulong), typeof(ulong) }; + + MethodInfo mthdInfo = typeof(Math).GetMethod(nameof(Math.Min), types); + + EmitVectorBinaryOpZx(context, () => context.EmitCall(mthdInfo)); + } + + public static void Uminp_V(ILEmitterCtx context) + { + Type[] types = new Type[] { typeof(ulong), typeof(ulong) }; + + MethodInfo mthdInfo = typeof(Math).GetMethod(nameof(Math.Min), types); + + EmitVectorPairwiseOpZx(context, () => context.EmitCall(mthdInfo)); + } + + public static void Umlal_V(ILEmitterCtx context) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + if (Optimizations.UseSse41 && op.Size < 2) + { + Type[] typesSrl = new Type[] { VectorUIntTypesPerSizeLog2[op.Size], typeof(byte) }; + Type[] typesCvt = new Type[] { VectorUIntTypesPerSizeLog2[op.Size] }; + Type[] typesMulAdd = new Type[] { VectorIntTypesPerSizeLog2 [op.Size + 1], + VectorIntTypesPerSizeLog2 [op.Size + 1] }; + + Type typeMul = op.Size == 0 ? typeof(Sse2) : typeof(Sse41); + + string nameCvt = op.Size == 0 + ? nameof(Sse41.ConvertToVector128Int16) + : nameof(Sse41.ConvertToVector128Int32); + + int numBytes = op.RegisterSize == RegisterSize.Simd128 ? 8 : 0; + + EmitLdvecWithUnsignedCast(context, op.Rd, op.Size + 1); + + EmitLdvecWithUnsignedCast(context, op.Rn, op.Size); + + context.EmitLdc_I4(numBytes); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + + context.EmitCall(typeof(Sse41).GetMethod(nameCvt, typesCvt)); + + EmitLdvecWithUnsignedCast(context, op.Rm, op.Size); + + context.EmitLdc_I4(numBytes); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + + context.EmitCall(typeof(Sse41).GetMethod(nameCvt, typesCvt)); + + context.EmitCall(typeMul.GetMethod(nameof(Sse2.MultiplyLow), typesMulAdd)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), typesMulAdd)); + + EmitStvecWithUnsignedCast(context, op.Rd, op.Size + 1); + } + else + { + EmitVectorWidenRnRmTernaryOpZx(context, () => + { + context.Emit(OpCodes.Mul); + context.Emit(OpCodes.Add); + }); + } + } + + public static void Umlsl_V(ILEmitterCtx context) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + if (Optimizations.UseSse41 && op.Size < 2) + { + Type[] typesSrl = new Type[] { VectorUIntTypesPerSizeLog2[op.Size], typeof(byte) }; + Type[] typesCvt = new Type[] { VectorUIntTypesPerSizeLog2[op.Size] }; + Type[] typesMulSub = new Type[] { VectorIntTypesPerSizeLog2 [op.Size + 1], + VectorIntTypesPerSizeLog2 [op.Size + 1] }; + + Type typeMul = op.Size == 0 ? typeof(Sse2) : typeof(Sse41); + + string nameCvt = op.Size == 0 + ? nameof(Sse41.ConvertToVector128Int16) + : nameof(Sse41.ConvertToVector128Int32); + + int numBytes = op.RegisterSize == RegisterSize.Simd128 ? 8 : 0; + + EmitLdvecWithUnsignedCast(context, op.Rd, op.Size + 1); + + EmitLdvecWithUnsignedCast(context, op.Rn, op.Size); + + context.EmitLdc_I4(numBytes); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + + context.EmitCall(typeof(Sse41).GetMethod(nameCvt, typesCvt)); + + EmitLdvecWithUnsignedCast(context, op.Rm, op.Size); + + context.EmitLdc_I4(numBytes); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + + context.EmitCall(typeof(Sse41).GetMethod(nameCvt, typesCvt)); + + context.EmitCall(typeMul.GetMethod(nameof(Sse2.MultiplyLow), typesMulSub)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Subtract), typesMulSub)); + + EmitStvecWithUnsignedCast(context, op.Rd, op.Size + 1); + } + else + { + EmitVectorWidenRnRmTernaryOpZx(context, () => + { + context.Emit(OpCodes.Mul); + context.Emit(OpCodes.Sub); + }); + } + } + + public static void Umull_V(ILEmitterCtx context) + { + EmitVectorWidenRnRmBinaryOpZx(context, () => context.Emit(OpCodes.Mul)); + } + + public static void Uqadd_S(ILEmitterCtx context) + { + EmitScalarSaturatingBinaryOpZx(context, SaturatingFlags.Add); + } + + public static void Uqadd_V(ILEmitterCtx context) + { + EmitVectorSaturatingBinaryOpZx(context, SaturatingFlags.Add); + } + + public static void Uqsub_S(ILEmitterCtx context) + { + EmitScalarSaturatingBinaryOpZx(context, SaturatingFlags.Sub); + } + + public static void Uqsub_V(ILEmitterCtx context) + { + EmitVectorSaturatingBinaryOpZx(context, SaturatingFlags.Sub); + } + + public static void Uqxtn_S(ILEmitterCtx context) + { + EmitSaturatingNarrowOp(context, SaturatingNarrowFlags.ScalarZxZx); + } + + public static void Uqxtn_V(ILEmitterCtx context) + { + EmitSaturatingNarrowOp(context, SaturatingNarrowFlags.VectorZxZx); + } + + public static void Urhadd_V(ILEmitterCtx context) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + if (Optimizations.UseSse2 && op.Size < 2) + { + Type[] typesAvg = new Type[] { VectorUIntTypesPerSizeLog2[op.Size], VectorUIntTypesPerSizeLog2[op.Size] }; + + EmitLdvecWithUnsignedCast(context, op.Rn, op.Size); + EmitLdvecWithUnsignedCast(context, op.Rm, op.Size); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Average), typesAvg)); + + EmitStvecWithUnsignedCast(context, op.Rd, op.Size); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + else + { + EmitVectorBinaryOpZx(context, () => + { + context.Emit(OpCodes.Add); + + context.Emit(OpCodes.Ldc_I4_1); + context.Emit(OpCodes.Add); + + context.Emit(OpCodes.Ldc_I4_1); + context.Emit(OpCodes.Shr_Un); + }); + } + } + + public static void Usqadd_S(ILEmitterCtx context) + { + EmitScalarSaturatingBinaryOpZx(context, SaturatingFlags.Accumulate); + } + + public static void Usqadd_V(ILEmitterCtx context) + { + EmitVectorSaturatingBinaryOpZx(context, SaturatingFlags.Accumulate); + } + + public static void Usubl_V(ILEmitterCtx context) + { + if (Optimizations.UseSse41) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + Type[] typesSrl = new Type[] { VectorUIntTypesPerSizeLog2[op.Size], typeof(byte) }; + Type[] typesCvt = new Type[] { VectorUIntTypesPerSizeLog2[op.Size] }; + Type[] typesSub = new Type[] { VectorUIntTypesPerSizeLog2[op.Size + 1], + VectorUIntTypesPerSizeLog2[op.Size + 1] }; + + string[] namesCvt = new string[] { nameof(Sse41.ConvertToVector128Int16), + nameof(Sse41.ConvertToVector128Int32), + nameof(Sse41.ConvertToVector128Int64) }; + + int numBytes = op.RegisterSize == RegisterSize.Simd128 ? 8 : 0; + + EmitLdvecWithUnsignedCast(context, op.Rn, op.Size); + + context.EmitLdc_I4(numBytes); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + + context.EmitCall(typeof(Sse41).GetMethod(namesCvt[op.Size], typesCvt)); + + EmitLdvecWithUnsignedCast(context, op.Rm, op.Size); + + context.EmitLdc_I4(numBytes); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + + context.EmitCall(typeof(Sse41).GetMethod(namesCvt[op.Size], typesCvt)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Subtract), typesSub)); + + EmitStvecWithUnsignedCast(context, op.Rd, op.Size + 1); + } + else + { + EmitVectorWidenRnRmBinaryOpZx(context, () => context.Emit(OpCodes.Sub)); + } + } + + public static void Usubw_V(ILEmitterCtx context) + { + EmitVectorWidenRmBinaryOpZx(context, () => context.Emit(OpCodes.Sub)); + } + + private static void EmitAbs(ILEmitterCtx context) + { + ILLabel lblTrue = new ILLabel(); + + context.Emit(OpCodes.Dup); + context.Emit(OpCodes.Ldc_I4_0); + context.Emit(OpCodes.Bge_S, lblTrue); + + context.Emit(OpCodes.Neg); + + context.MarkLabel(lblTrue); + } + + private static void EmitAddLongPairwise(ILEmitterCtx context, bool signed, bool accumulate) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + int words = op.GetBitsCount() >> 4; + int pairs = words >> op.Size; + + for (int index = 0; index < pairs; index++) + { + int idx = index << 1; + + EmitVectorExtract(context, op.Rn, idx, op.Size, signed); + EmitVectorExtract(context, op.Rn, idx + 1, op.Size, signed); + + context.Emit(OpCodes.Add); + + if (accumulate) + { + EmitVectorExtract(context, op.Rd, index, op.Size + 1, signed); + + context.Emit(OpCodes.Add); + } + + EmitVectorInsertTmp(context, index, op.Size + 1); + } + + context.EmitLdvectmp(); + context.EmitStvec(op.Rd); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + private static void EmitDoublingMultiplyHighHalf(ILEmitterCtx context, bool round) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + int eSize = 8 << op.Size; + + context.Emit(OpCodes.Mul); + + if (!round) + { + context.EmitAsr(eSize - 1); + } + else + { + long roundConst = 1L << (eSize - 1); + + ILLabel lblTrue = new ILLabel(); + + context.EmitLsl(1); + + context.EmitLdc_I8(roundConst); + + context.Emit(OpCodes.Add); + + context.EmitAsr(eSize); + + context.Emit(OpCodes.Dup); + context.EmitLdc_I8((long)int.MinValue); + context.Emit(OpCodes.Bne_Un_S, lblTrue); + + context.Emit(OpCodes.Neg); + + context.MarkLabel(lblTrue); + } + } + + private static void EmitHighNarrow(ILEmitterCtx context, Action emit, bool round) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + int elems = 8 >> op.Size; + + int eSize = 8 << op.Size; + + int part = op.RegisterSize == RegisterSize.Simd128 ? elems : 0; + + long roundConst = 1L << (eSize - 1); + + if (part != 0) + { + context.EmitLdvec(op.Rd); + context.EmitStvectmp(); + } + + for (int index = 0; index < elems; index++) + { + EmitVectorExtractZx(context, op.Rn, index, op.Size + 1); + EmitVectorExtractZx(context, op.Rm, index, op.Size + 1); + + emit(); + + if (round) + { + context.EmitLdc_I8(roundConst); + + context.Emit(OpCodes.Add); + } + + context.EmitLsr(eSize); + + EmitVectorInsertTmp(context, part + index, op.Size); + } + + context.EmitLdvectmp(); + context.EmitStvec(op.Rd); + + if (part == 0) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + } +} diff --git a/Instructions/InstEmitSimdCmp.cs b/Instructions/InstEmitSimdCmp.cs new file mode 100644 index 0000000..c473c0a --- /dev/null +++ b/Instructions/InstEmitSimdCmp.cs @@ -0,0 +1,526 @@ +using ChocolArm64.Decoders; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System; +using System.Reflection.Emit; +using System.Runtime.Intrinsics.X86; + +using static ChocolArm64.Instructions.InstEmitAluHelper; +using static ChocolArm64.Instructions.InstEmitSimdHelper; + +namespace ChocolArm64.Instructions +{ + static partial class InstEmit + { + public static void Cmeq_S(ILEmitterCtx context) + { + EmitCmp(context, OpCodes.Beq_S, scalar: true); + } + + public static void Cmeq_V(ILEmitterCtx context) + { + if (context.CurrOp is OpCodeSimdReg64 op) + { + if (op.Size < 3 && Optimizations.UseSse2) + { + EmitSse2Op(context, nameof(Sse2.CompareEqual)); + } + else if (op.Size == 3 && Optimizations.UseSse41) + { + EmitSse41Op(context, nameof(Sse41.CompareEqual)); + } + else + { + EmitCmp(context, OpCodes.Beq_S, scalar: false); + } + } + else + { + EmitCmp(context, OpCodes.Beq_S, scalar: false); + } + } + + public static void Cmge_S(ILEmitterCtx context) + { + EmitCmp(context, OpCodes.Bge_S, scalar: true); + } + + public static void Cmge_V(ILEmitterCtx context) + { + EmitCmp(context, OpCodes.Bge_S, scalar: false); + } + + public static void Cmgt_S(ILEmitterCtx context) + { + EmitCmp(context, OpCodes.Bgt_S, scalar: true); + } + + public static void Cmgt_V(ILEmitterCtx context) + { + if (context.CurrOp is OpCodeSimdReg64 op) + { + if (op.Size < 3 && Optimizations.UseSse2) + { + EmitSse2Op(context, nameof(Sse2.CompareGreaterThan)); + } + else if (op.Size == 3 && Optimizations.UseSse42) + { + EmitSse42Op(context, nameof(Sse42.CompareGreaterThan)); + } + else + { + EmitCmp(context, OpCodes.Bgt_S, scalar: false); + } + } + else + { + EmitCmp(context, OpCodes.Bgt_S, scalar: false); + } + } + + public static void Cmhi_S(ILEmitterCtx context) + { + EmitCmp(context, OpCodes.Bgt_Un_S, scalar: true); + } + + public static void Cmhi_V(ILEmitterCtx context) + { + EmitCmp(context, OpCodes.Bgt_Un_S, scalar: false); + } + + public static void Cmhs_S(ILEmitterCtx context) + { + EmitCmp(context, OpCodes.Bge_Un_S, scalar: true); + } + + public static void Cmhs_V(ILEmitterCtx context) + { + EmitCmp(context, OpCodes.Bge_Un_S, scalar: false); + } + + public static void Cmle_S(ILEmitterCtx context) + { + EmitCmp(context, OpCodes.Ble_S, scalar: true); + } + + public static void Cmle_V(ILEmitterCtx context) + { + EmitCmp(context, OpCodes.Ble_S, scalar: false); + } + + public static void Cmlt_S(ILEmitterCtx context) + { + EmitCmp(context, OpCodes.Blt_S, scalar: true); + } + + public static void Cmlt_V(ILEmitterCtx context) + { + EmitCmp(context, OpCodes.Blt_S, scalar: false); + } + + public static void Cmtst_S(ILEmitterCtx context) + { + EmitCmtst(context, scalar: true); + } + + public static void Cmtst_V(ILEmitterCtx context) + { + EmitCmtst(context, scalar: false); + } + + public static void Fccmp_S(ILEmitterCtx context) + { + OpCodeSimdFcond64 op = (OpCodeSimdFcond64)context.CurrOp; + + ILLabel lblTrue = new ILLabel(); + ILLabel lblEnd = new ILLabel(); + + 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(ILEmitterCtx context) + { + Fccmp_S(context); + } + + public static void Fcmeq_S(ILEmitterCtx context) + { + if (context.CurrOp is OpCodeSimdReg64 && Optimizations.UseSse + && Optimizations.UseSse2) + { + EmitScalarSseOrSse2OpF(context, nameof(Sse.CompareEqualScalar)); + } + else + { + EmitScalarFcmp(context, OpCodes.Beq_S); + } + } + + public static void Fcmeq_V(ILEmitterCtx context) + { + if (context.CurrOp is OpCodeSimdReg64 && Optimizations.UseSse + && Optimizations.UseSse2) + { + EmitVectorSseOrSse2OpF(context, nameof(Sse.CompareEqual)); + } + else + { + EmitVectorFcmp(context, OpCodes.Beq_S); + } + } + + public static void Fcmge_S(ILEmitterCtx context) + { + if (context.CurrOp is OpCodeSimdReg64 && Optimizations.UseSse + && Optimizations.UseSse2) + { + EmitScalarSseOrSse2OpF(context, nameof(Sse.CompareGreaterThanOrEqualScalar)); + } + else + { + EmitScalarFcmp(context, OpCodes.Bge_S); + } + } + + public static void Fcmge_V(ILEmitterCtx context) + { + if (context.CurrOp is OpCodeSimdReg64 && Optimizations.UseSse + && Optimizations.UseSse2) + { + EmitVectorSseOrSse2OpF(context, nameof(Sse.CompareGreaterThanOrEqual)); + } + else + { + EmitVectorFcmp(context, OpCodes.Bge_S); + } + } + + public static void Fcmgt_S(ILEmitterCtx context) + { + if (context.CurrOp is OpCodeSimdReg64 && Optimizations.UseSse + && Optimizations.UseSse2) + { + EmitScalarSseOrSse2OpF(context, nameof(Sse.CompareGreaterThanScalar)); + } + else + { + EmitScalarFcmp(context, OpCodes.Bgt_S); + } + } + + public static void Fcmgt_V(ILEmitterCtx context) + { + if (context.CurrOp is OpCodeSimdReg64 && Optimizations.UseSse + && Optimizations.UseSse2) + { + EmitVectorSseOrSse2OpF(context, nameof(Sse.CompareGreaterThan)); + } + else + { + EmitVectorFcmp(context, OpCodes.Bgt_S); + } + } + + public static void Fcmle_S(ILEmitterCtx context) + { + EmitScalarFcmp(context, OpCodes.Ble_S); + } + + public static void Fcmle_V(ILEmitterCtx context) + { + EmitVectorFcmp(context, OpCodes.Ble_S); + } + + public static void Fcmlt_S(ILEmitterCtx context) + { + EmitScalarFcmp(context, OpCodes.Blt_S); + } + + public static void Fcmlt_V(ILEmitterCtx context) + { + EmitVectorFcmp(context, OpCodes.Blt_S); + } + + public static void Fcmp_S(ILEmitterCtx context) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + bool cmpWithZero = !(op is OpCodeSimdFcond64) ? 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); + } + + ILLabel lblNaN = new ILLabel(); + ILLabel lblEnd = new ILLabel(); + + context.Emit(OpCodes.Brtrue_S, lblNaN); + + void EmitLoadOpers() + { + EmitVectorExtractF(context, op.Rn, 0, op.Size); + + if (cmpWithZero) + { + if (op.Size == 0) + { + context.EmitLdc_R4(0f); + } + else /* if (Op.Size == 1) */ + { + context.EmitLdc_R8(0d); + } + } + else + { + EmitVectorExtractF(context, op.Rm, 0, op.Size); + } + } + + //Z = Rn == Rm + EmitLoadOpers(); + + context.Emit(OpCodes.Ceq); + context.Emit(OpCodes.Dup); + + context.EmitStflg((int)PState.ZBit); + + //C = Rn >= Rm + EmitLoadOpers(); + + context.Emit(OpCodes.Cgt); + context.Emit(OpCodes.Or); + + context.EmitStflg((int)PState.CBit); + + //N = Rn < Rm + EmitLoadOpers(); + + context.Emit(OpCodes.Clt); + + context.EmitStflg((int)PState.NBit); + + //V = 0 + context.EmitLdc_I4(0); + + context.EmitStflg((int)PState.VBit); + + context.Emit(OpCodes.Br_S, lblEnd); + + context.MarkLabel(lblNaN); + + EmitSetNzcv(context, 0b0011); + + context.MarkLabel(lblEnd); + } + + public static void Fcmpe_S(ILEmitterCtx context) + { + Fcmp_S(context); + } + + private static void EmitNaNCheck(ILEmitterCtx context, int reg) + { + IOpCodeSimd64 op = (IOpCodeSimd64)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 EmitCmp(ILEmitterCtx context, OpCode ilOp, bool scalar) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + int bytes = op.GetBitsCount() >> 3; + int elems = !scalar ? bytes >> op.Size : 1; + + ulong szMask = ulong.MaxValue >> (64 - (8 << op.Size)); + + for (int index = 0; index < elems; index++) + { + EmitVectorExtractSx(context, op.Rn, index, op.Size); + + if (op is OpCodeSimdReg64 binOp) + { + EmitVectorExtractSx(context, binOp.Rm, index, op.Size); + } + else + { + context.EmitLdc_I8(0L); + } + + ILLabel lblTrue = new ILLabel(); + ILLabel lblEnd = new ILLabel(); + + 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 == RegisterSize.Simd64) || scalar) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + private static void EmitCmtst(ILEmitterCtx context, bool scalar) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + int bytes = op.GetBitsCount() >> 3; + int elems = !scalar ? bytes >> op.Size : 1; + + ulong szMask = ulong.MaxValue >> (64 - (8 << op.Size)); + + for (int index = 0; index < elems; index++) + { + EmitVectorExtractZx(context, op.Rn, index, op.Size); + EmitVectorExtractZx(context, op.Rm, index, op.Size); + + ILLabel lblTrue = new ILLabel(); + ILLabel lblEnd = new ILLabel(); + + context.Emit(OpCodes.And); + + context.EmitLdc_I8(0L); + + context.Emit(OpCodes.Bne_Un_S, 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 == RegisterSize.Simd64) || scalar) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + private static void EmitScalarFcmp(ILEmitterCtx context, OpCode ilOp) + { + EmitFcmp(context, ilOp, 0, scalar: true); + } + + private static void EmitVectorFcmp(ILEmitterCtx context, OpCode ilOp) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + int sizeF = op.Size & 1; + + int bytes = op.GetBitsCount() >> 3; + int elems = bytes >> sizeF + 2; + + for (int index = 0; index < elems; index++) + { + EmitFcmp(context, ilOp, index, scalar: false); + } + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + private static void EmitFcmp(ILEmitterCtx context, OpCode ilOp, int index, bool scalar) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + int sizeF = op.Size & 1; + + ulong szMask = ulong.MaxValue >> (64 - (32 << sizeF)); + + EmitVectorExtractF(context, op.Rn, index, sizeF); + + if (op is OpCodeSimdReg64 binOp) + { + EmitVectorExtractF(context, binOp.Rm, index, sizeF); + } + else if (sizeF == 0) + { + context.EmitLdc_R4(0f); + } + else /* if (SizeF == 1) */ + { + context.EmitLdc_R8(0d); + } + + ILLabel lblTrue = new ILLabel(); + ILLabel lblEnd = new ILLabel(); + + context.Emit(ilOp, lblTrue); + + if (scalar) + { + EmitVectorZeroAll(context, op.Rd); + } + else + { + EmitVectorInsert(context, op.Rd, index, sizeF + 2, 0); + } + + context.Emit(OpCodes.Br_S, lblEnd); + + context.MarkLabel(lblTrue); + + if (scalar) + { + EmitVectorInsert(context, op.Rd, index, 3, (long)szMask); + + EmitVectorZeroUpper(context, op.Rd); + } + else + { + EmitVectorInsert(context, op.Rd, index, sizeF + 2, (long)szMask); + } + + context.MarkLabel(lblEnd); + } + } +} diff --git a/Instructions/InstEmitSimdCrypto.cs b/Instructions/InstEmitSimdCrypto.cs new file mode 100644 index 0000000..33c81aa --- /dev/null +++ b/Instructions/InstEmitSimdCrypto.cs @@ -0,0 +1,54 @@ +using ChocolArm64.Decoders; +using ChocolArm64.Translation; + +namespace ChocolArm64.Instructions +{ + static partial class InstEmit + { + public static void Aesd_V(ILEmitterCtx context) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + context.EmitLdvec(op.Rd); + context.EmitLdvec(op.Rn); + + SoftFallback.EmitCall(context, nameof(SoftFallback.Decrypt)); + + context.EmitStvec(op.Rd); + } + + public static void Aese_V(ILEmitterCtx context) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + context.EmitLdvec(op.Rd); + context.EmitLdvec(op.Rn); + + SoftFallback.EmitCall(context, nameof(SoftFallback.Encrypt)); + + context.EmitStvec(op.Rd); + } + + public static void Aesimc_V(ILEmitterCtx context) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + context.EmitLdvec(op.Rn); + + SoftFallback.EmitCall(context, nameof(SoftFallback.InverseMixColumns)); + + context.EmitStvec(op.Rd); + } + + public static void Aesmc_V(ILEmitterCtx context) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + context.EmitLdvec(op.Rn); + + SoftFallback.EmitCall(context, nameof(SoftFallback.MixColumns)); + + context.EmitStvec(op.Rd); + } + } +} diff --git a/Instructions/InstEmitSimdCvt.cs b/Instructions/InstEmitSimdCvt.cs new file mode 100644 index 0000000..fa17c09 --- /dev/null +++ b/Instructions/InstEmitSimdCvt.cs @@ -0,0 +1,697 @@ +using ChocolArm64.Decoders; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System; +using System.Reflection.Emit; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; + +using static ChocolArm64.Instructions.InstEmitSimdHelper; + +namespace ChocolArm64.Instructions +{ + static partial class InstEmit + { + public static void Fcvt_S(ILEmitterCtx context) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + if (Optimizations.UseSse2) + { + if (op.Size == 1 && op.Opc == 0) + { + //Double -> Single. + VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleZero)); + + EmitLdvecWithCastToDouble(context, op.Rn); + + Type[] types = new Type[] { typeof(Vector128), typeof(Vector128) }; + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ConvertScalarToVector128Single), types)); + + context.EmitStvec(op.Rd); + } + else if (op.Size == 0 && op.Opc == 1) + { + //Single -> Double. + VectorHelper.EmitCall(context, nameof(VectorHelper.VectorDoubleZero)); + + context.EmitLdvec(op.Rn); + + Type[] types = new Type[] { typeof(Vector128), typeof(Vector128) }; + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ConvertScalarToVector128Double), types)); + + EmitStvecWithCastFromDouble(context, op.Rd); + } + else + { + //Invalid encoding. + throw new InvalidOperationException(); + } + } + else + { + EmitVectorExtractF(context, op.Rn, 0, op.Size); + + EmitFloatCast(context, op.Opc); + + EmitScalarSetF(context, op.Rd, op.Opc); + } + } + + public static void Fcvtas_Gp(ILEmitterCtx context) + { + EmitFcvt_s_Gp(context, () => EmitRoundMathCall(context, MidpointRounding.AwayFromZero)); + } + + public static void Fcvtau_Gp(ILEmitterCtx context) + { + EmitFcvt_u_Gp(context, () => EmitRoundMathCall(context, MidpointRounding.AwayFromZero)); + } + + public static void Fcvtl_V(ILEmitterCtx context) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + int sizeF = op.Size & 1; + + int elems = 4 >> sizeF; + + int part = op.RegisterSize == RegisterSize.Simd128 ? elems : 0; + + for (int index = 0; index < elems; index++) + { + if (sizeF == 0) + { + EmitVectorExtractZx(context, op.Rn, part + index, 1); + context.Emit(OpCodes.Conv_U2); + + context.EmitLdarg(TranslatedSub.StateArgIdx); + + context.EmitCall(typeof(SoftFloat1632), nameof(SoftFloat1632.FPConvert)); + } + else /* if (SizeF == 1) */ + { + EmitVectorExtractF(context, op.Rn, part + index, 0); + + context.Emit(OpCodes.Conv_R8); + } + + EmitVectorInsertTmpF(context, index, sizeF); + } + + context.EmitLdvectmp(); + context.EmitStvec(op.Rd); + } + + public static void Fcvtms_Gp(ILEmitterCtx context) + { + EmitFcvt_s_Gp(context, () => EmitUnaryMathCall(context, nameof(Math.Floor))); + } + + public static void Fcvtmu_Gp(ILEmitterCtx context) + { + EmitFcvt_u_Gp(context, () => EmitUnaryMathCall(context, nameof(Math.Floor))); + } + + public static void Fcvtn_V(ILEmitterCtx context) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + int sizeF = op.Size & 1; + + int elems = 4 >> sizeF; + + int part = op.RegisterSize == RegisterSize.Simd128 ? elems : 0; + + if (part != 0) + { + context.EmitLdvec(op.Rd); + context.EmitStvectmp(); + } + + for (int index = 0; index < elems; index++) + { + EmitVectorExtractF(context, op.Rn, index, sizeF); + + if (sizeF == 0) + { + context.EmitLdarg(TranslatedSub.StateArgIdx); + + context.EmitCall(typeof(SoftFloat3216), nameof(SoftFloat3216.FPConvert)); + + context.Emit(OpCodes.Conv_U8); + EmitVectorInsertTmp(context, part + index, 1); + } + else /* if (SizeF == 1) */ + { + context.Emit(OpCodes.Conv_R4); + + EmitVectorInsertTmpF(context, part + index, 0); + } + } + + context.EmitLdvectmp(); + context.EmitStvec(op.Rd); + + if (part == 0) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + public static void Fcvtns_S(ILEmitterCtx context) + { + EmitFcvtn(context, signed: true, scalar: true); + } + + public static void Fcvtns_V(ILEmitterCtx context) + { + EmitFcvtn(context, signed: true, scalar: false); + } + + public static void Fcvtnu_S(ILEmitterCtx context) + { + EmitFcvtn(context, signed: false, scalar: true); + } + + public static void Fcvtnu_V(ILEmitterCtx context) + { + EmitFcvtn(context, signed: false, scalar: false); + } + + public static void Fcvtps_Gp(ILEmitterCtx context) + { + EmitFcvt_s_Gp(context, () => EmitUnaryMathCall(context, nameof(Math.Ceiling))); + } + + public static void Fcvtpu_Gp(ILEmitterCtx context) + { + EmitFcvt_u_Gp(context, () => EmitUnaryMathCall(context, nameof(Math.Ceiling))); + } + + public static void Fcvtzs_Gp(ILEmitterCtx context) + { + EmitFcvt_s_Gp(context, () => { }); + } + + public static void Fcvtzs_Gp_Fix(ILEmitterCtx context) + { + EmitFcvtzs_Gp_Fix(context); + } + + public static void Fcvtzs_S(ILEmitterCtx context) + { + EmitScalarFcvtzs(context); + } + + public static void Fcvtzs_V(ILEmitterCtx context) + { + EmitVectorFcvtzs(context); + } + + public static void Fcvtzu_Gp(ILEmitterCtx context) + { + EmitFcvt_u_Gp(context, () => { }); + } + + public static void Fcvtzu_Gp_Fix(ILEmitterCtx context) + { + EmitFcvtzu_Gp_Fix(context); + } + + public static void Fcvtzu_S(ILEmitterCtx context) + { + EmitScalarFcvtzu(context); + } + + public static void Fcvtzu_V(ILEmitterCtx context) + { + EmitVectorFcvtzu(context); + } + + public static void Scvtf_Gp(ILEmitterCtx context) + { + OpCodeSimdCvt64 op = (OpCodeSimdCvt64)context.CurrOp; + + context.EmitLdintzr(op.Rn); + + if (context.CurrOp.RegisterSize == RegisterSize.Int32) + { + context.Emit(OpCodes.Conv_U4); + } + + EmitFloatCast(context, op.Size); + + EmitScalarSetF(context, op.Rd, op.Size); + } + + public static void Scvtf_S(ILEmitterCtx context) + { + OpCodeSimd64 op = (OpCodeSimd64)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(ILEmitterCtx context) + { + EmitVectorCvtf(context, signed: true); + } + + public static void Ucvtf_Gp(ILEmitterCtx context) + { + OpCodeSimdCvt64 op = (OpCodeSimdCvt64)context.CurrOp; + + context.EmitLdintzr(op.Rn); + + if (context.CurrOp.RegisterSize == RegisterSize.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(ILEmitterCtx context) + { + OpCodeSimd64 op = (OpCodeSimd64)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(ILEmitterCtx context) + { + EmitVectorCvtf(context, signed: false); + } + + private static int GetFBits(ILEmitterCtx context) + { + if (context.CurrOp is OpCodeSimdShImm64 op) + { + return GetImmShr(op); + } + + return 0; + } + + private static void EmitFloatCast(ILEmitterCtx 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 EmitFcvtn(ILEmitterCtx context, bool signed, bool scalar) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + int sizeF = op.Size & 1; + int sizeI = sizeF + 2; + + int bytes = op.GetBitsCount() >> 3; + int elems = !scalar ? bytes >> sizeI : 1; + + if (scalar && (sizeF == 0)) + { + EmitVectorZeroLowerTmp(context); + } + + for (int index = 0; index < elems; index++) + { + EmitVectorExtractF(context, op.Rn, index, sizeF); + + EmitRoundMathCall(context, MidpointRounding.ToEven); + + if (sizeF == 0) + { + VectorHelper.EmitCall(context, signed + ? nameof(VectorHelper.SatF32ToS32) + : nameof(VectorHelper.SatF32ToU32)); + + context.Emit(OpCodes.Conv_U8); + } + else /* if (SizeF == 1) */ + { + VectorHelper.EmitCall(context, signed + ? nameof(VectorHelper.SatF64ToS64) + : nameof(VectorHelper.SatF64ToU64)); + } + + EmitVectorInsertTmp(context, index, sizeI); + } + + context.EmitLdvectmp(); + context.EmitStvec(op.Rd); + + if ((op.RegisterSize == RegisterSize.Simd64) || scalar) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + private static void EmitFcvt_s_Gp(ILEmitterCtx context, Action emit) + { + EmitFcvt___Gp(context, emit, true); + } + + private static void EmitFcvt_u_Gp(ILEmitterCtx context, Action emit) + { + EmitFcvt___Gp(context, emit, false); + } + + private static void EmitFcvt___Gp(ILEmitterCtx context, Action emit, bool signed) + { + OpCodeSimdCvt64 op = (OpCodeSimdCvt64)context.CurrOp; + + EmitVectorExtractF(context, op.Rn, 0, op.Size); + + emit(); + + if (signed) + { + EmitScalarFcvts(context, op.Size, 0); + } + else + { + EmitScalarFcvtu(context, op.Size, 0); + } + + if (context.CurrOp.RegisterSize == RegisterSize.Int32) + { + context.Emit(OpCodes.Conv_U8); + } + + context.EmitStintzr(op.Rd); + } + + private static void EmitFcvtzs_Gp_Fix(ILEmitterCtx context) + { + EmitFcvtz__Gp_Fix(context, true); + } + + private static void EmitFcvtzu_Gp_Fix(ILEmitterCtx context) + { + EmitFcvtz__Gp_Fix(context, false); + } + + private static void EmitFcvtz__Gp_Fix(ILEmitterCtx context, bool signed) + { + OpCodeSimdCvt64 op = (OpCodeSimdCvt64)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 == RegisterSize.Int32) + { + context.Emit(OpCodes.Conv_U8); + } + + context.EmitStintzr(op.Rd); + } + + private static void EmitVectorScvtf(ILEmitterCtx context) + { + EmitVectorCvtf(context, true); + } + + private static void EmitVectorUcvtf(ILEmitterCtx context) + { + EmitVectorCvtf(context, false); + } + + private static void EmitVectorCvtf(ILEmitterCtx context, bool signed) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + int sizeF = op.Size & 1; + int sizeI = sizeF + 2; + + int fBits = GetFBits(context); + + int bytes = op.GetBitsCount() >> 3; + int elems = bytes >> sizeI; + + for (int index = 0; index < elems; 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 == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + private static void EmitScalarFcvtzs(ILEmitterCtx context) + { + EmitScalarFcvtz(context, true); + } + + private static void EmitScalarFcvtzu(ILEmitterCtx context) + { + EmitScalarFcvtz(context, false); + } + + private static void EmitScalarFcvtz(ILEmitterCtx context, bool signed) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + int sizeF = op.Size & 1; + int sizeI = sizeF + 2; + + int fBits = GetFBits(context); + + EmitVectorExtractF(context, op.Rn, 0, sizeF); + + EmitF2iFBitsMul(context, sizeF, fBits); + + if (sizeF == 0) + { + VectorHelper.EmitCall(context, signed + ? nameof(VectorHelper.SatF32ToS32) + : nameof(VectorHelper.SatF32ToU32)); + } + else /* if (SizeF == 1) */ + { + VectorHelper.EmitCall(context, signed + ? nameof(VectorHelper.SatF64ToS64) + : nameof(VectorHelper.SatF64ToU64)); + } + + if (sizeF == 0) + { + context.Emit(OpCodes.Conv_U8); + } + + EmitScalarSet(context, op.Rd, sizeI); + } + + private static void EmitVectorFcvtzs(ILEmitterCtx context) + { + EmitVectorFcvtz(context, true); + } + + private static void EmitVectorFcvtzu(ILEmitterCtx context) + { + EmitVectorFcvtz(context, false); + } + + private static void EmitVectorFcvtz(ILEmitterCtx context, bool signed) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + int sizeF = op.Size & 1; + int sizeI = sizeF + 2; + + int fBits = GetFBits(context); + + int bytes = op.GetBitsCount() >> 3; + int elems = bytes >> sizeI; + + for (int index = 0; index < elems; index++) + { + EmitVectorExtractF(context, op.Rn, index, sizeF); + + EmitF2iFBitsMul(context, sizeF, fBits); + + if (sizeF == 0) + { + VectorHelper.EmitCall(context, signed + ? nameof(VectorHelper.SatF32ToS32) + : nameof(VectorHelper.SatF32ToU32)); + } + else /* if (SizeF == 1) */ + { + VectorHelper.EmitCall(context, signed + ? nameof(VectorHelper.SatF64ToS64) + : nameof(VectorHelper.SatF64ToU64)); + } + + if (sizeF == 0) + { + context.Emit(OpCodes.Conv_U8); + } + + EmitVectorInsert(context, op.Rd, index, sizeI); + } + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + private static void EmitScalarFcvts(ILEmitterCtx context, int size, int fBits) + { + if (size < 0 || size > 1) + { + throw new ArgumentOutOfRangeException(nameof(size)); + } + + EmitF2iFBitsMul(context, size, fBits); + + if (context.CurrOp.RegisterSize == RegisterSize.Int32) + { + if (size == 0) + { + VectorHelper.EmitCall(context, nameof(VectorHelper.SatF32ToS32)); + } + else /* if (Size == 1) */ + { + VectorHelper.EmitCall(context, nameof(VectorHelper.SatF64ToS32)); + } + } + else + { + if (size == 0) + { + VectorHelper.EmitCall(context, nameof(VectorHelper.SatF32ToS64)); + } + else /* if (Size == 1) */ + { + VectorHelper.EmitCall(context, nameof(VectorHelper.SatF64ToS64)); + } + } + } + + private static void EmitScalarFcvtu(ILEmitterCtx context, int size, int fBits) + { + if (size < 0 || size > 1) + { + throw new ArgumentOutOfRangeException(nameof(size)); + } + + EmitF2iFBitsMul(context, size, fBits); + + if (context.CurrOp.RegisterSize == RegisterSize.Int32) + { + if (size == 0) + { + VectorHelper.EmitCall(context, nameof(VectorHelper.SatF32ToU32)); + } + else /* if (Size == 1) */ + { + VectorHelper.EmitCall(context, nameof(VectorHelper.SatF64ToU32)); + } + } + else + { + if (size == 0) + { + VectorHelper.EmitCall(context, nameof(VectorHelper.SatF32ToU64)); + } + else /* if (Size == 1) */ + { + VectorHelper.EmitCall(context, nameof(VectorHelper.SatF64ToU64)); + } + } + } + + private static void EmitF2iFBitsMul(ILEmitterCtx context, int size, int fBits) + { + if (fBits != 0) + { + if (size == 0) + { + context.EmitLdc_R4(MathF.Pow(2f, fBits)); + } + else if (size == 1) + { + context.EmitLdc_R8(Math.Pow(2d, fBits)); + } + else + { + throw new ArgumentOutOfRangeException(nameof(size)); + } + + context.Emit(OpCodes.Mul); + } + } + + private static void EmitI2fFBitsMul(ILEmitterCtx context, int size, int fBits) + { + if (fBits != 0) + { + if (size == 0) + { + context.EmitLdc_R4(1f / MathF.Pow(2f, fBits)); + } + else if (size == 1) + { + context.EmitLdc_R8(1d / Math.Pow(2d, fBits)); + } + else + { + throw new ArgumentOutOfRangeException(nameof(size)); + } + + context.Emit(OpCodes.Mul); + } + } + } +} diff --git a/Instructions/InstEmitSimdHash.cs b/Instructions/InstEmitSimdHash.cs new file mode 100644 index 0000000..bb767fe --- /dev/null +++ b/Instructions/InstEmitSimdHash.cs @@ -0,0 +1,140 @@ +using ChocolArm64.Decoders; +using ChocolArm64.Translation; + +using static ChocolArm64.Instructions.InstEmitSimdHelper; + +namespace ChocolArm64.Instructions +{ + static partial class InstEmit + { +#region "Sha1" + public static void Sha1c_V(ILEmitterCtx context) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + context.EmitLdvec(op.Rd); + EmitVectorExtractZx(context, op.Rn, 0, 2); + context.EmitLdvec(op.Rm); + + SoftFallback.EmitCall(context, nameof(SoftFallback.HashChoose)); + + context.EmitStvec(op.Rd); + } + + public static void Sha1h_V(ILEmitterCtx context) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + EmitVectorExtractZx(context, op.Rn, 0, 2); + + SoftFallback.EmitCall(context, nameof(SoftFallback.FixedRotate)); + + EmitScalarSet(context, op.Rd, 2); + } + + public static void Sha1m_V(ILEmitterCtx context) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + context.EmitLdvec(op.Rd); + EmitVectorExtractZx(context, op.Rn, 0, 2); + context.EmitLdvec(op.Rm); + + SoftFallback.EmitCall(context, nameof(SoftFallback.HashMajority)); + + context.EmitStvec(op.Rd); + } + + public static void Sha1p_V(ILEmitterCtx context) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + context.EmitLdvec(op.Rd); + EmitVectorExtractZx(context, op.Rn, 0, 2); + context.EmitLdvec(op.Rm); + + SoftFallback.EmitCall(context, nameof(SoftFallback.HashParity)); + + context.EmitStvec(op.Rd); + } + + public static void Sha1su0_V(ILEmitterCtx context) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + context.EmitLdvec(op.Rd); + context.EmitLdvec(op.Rn); + context.EmitLdvec(op.Rm); + + SoftFallback.EmitCall(context, nameof(SoftFallback.Sha1SchedulePart1)); + + context.EmitStvec(op.Rd); + } + + public static void Sha1su1_V(ILEmitterCtx context) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + context.EmitLdvec(op.Rd); + context.EmitLdvec(op.Rn); + + SoftFallback.EmitCall(context, nameof(SoftFallback.Sha1SchedulePart2)); + + context.EmitStvec(op.Rd); + } +#endregion + +#region "Sha256" + public static void Sha256h_V(ILEmitterCtx context) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + context.EmitLdvec(op.Rd); + context.EmitLdvec(op.Rn); + context.EmitLdvec(op.Rm); + + SoftFallback.EmitCall(context, nameof(SoftFallback.HashLower)); + + context.EmitStvec(op.Rd); + } + + public static void Sha256h2_V(ILEmitterCtx context) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + context.EmitLdvec(op.Rd); + context.EmitLdvec(op.Rn); + context.EmitLdvec(op.Rm); + + SoftFallback.EmitCall(context, nameof(SoftFallback.HashUpper)); + + context.EmitStvec(op.Rd); + } + + public static void Sha256su0_V(ILEmitterCtx context) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + context.EmitLdvec(op.Rd); + context.EmitLdvec(op.Rn); + + SoftFallback.EmitCall(context, nameof(SoftFallback.Sha256SchedulePart1)); + + context.EmitStvec(op.Rd); + } + + public static void Sha256su1_V(ILEmitterCtx context) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + context.EmitLdvec(op.Rd); + context.EmitLdvec(op.Rn); + context.EmitLdvec(op.Rm); + + SoftFallback.EmitCall(context, nameof(SoftFallback.Sha256SchedulePart2)); + + context.EmitStvec(op.Rd); + } +#endregion + } +} diff --git a/Instructions/InstEmitSimdHelper.cs b/Instructions/InstEmitSimdHelper.cs new file mode 100644 index 0000000..fad5151 --- /dev/null +++ b/Instructions/InstEmitSimdHelper.cs @@ -0,0 +1,1495 @@ +using ChocolArm64.Decoders; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; + +namespace ChocolArm64.Instructions +{ + static class InstEmitSimdHelper + { + public static readonly Type[] IntTypesPerSizeLog2 = new Type[] + { + typeof(sbyte), + typeof(short), + typeof(int), + typeof(long) + }; + + public static readonly Type[] UIntTypesPerSizeLog2 = new Type[] + { + typeof(byte), + typeof(ushort), + typeof(uint), + typeof(ulong) + }; + + public static readonly Type[] VectorIntTypesPerSizeLog2 = new Type[] + { + typeof(Vector128), + typeof(Vector128), + typeof(Vector128), + typeof(Vector128) + }; + + public static readonly Type[] VectorUIntTypesPerSizeLog2 = new Type[] + { + typeof(Vector128), + typeof(Vector128), + typeof(Vector128), + typeof(Vector128) + }; + + [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(OpCodeSimdShImm64 op) + { + return op.Imm - (8 << op.Size); + } + + public static int GetImmShr(OpCodeSimdShImm64 op) + { + return (8 << (op.Size + 1)) - op.Imm; + } + + public static void EmitSse2Op(ILEmitterCtx context, string name) + { + EmitSseOp(context, name, typeof(Sse2)); + } + + public static void EmitSse41Op(ILEmitterCtx context, string name) + { + EmitSseOp(context, name, typeof(Sse41)); + } + + public static void EmitSse42Op(ILEmitterCtx context, string name) + { + EmitSseOp(context, name, typeof(Sse42)); + } + + private static void EmitSseOp(ILEmitterCtx context, string name, Type type) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + EmitLdvecWithSignedCast(context, op.Rn, op.Size); + + Type baseType = VectorIntTypesPerSizeLog2[op.Size]; + + if (op is OpCodeSimdReg64 binOp) + { + EmitLdvecWithSignedCast(context, binOp.Rm, op.Size); + + context.EmitCall(type.GetMethod(name, new Type[] { baseType, baseType })); + } + else + { + context.EmitCall(type.GetMethod(name, new Type[] { baseType })); + } + + EmitStvecWithSignedCast(context, op.Rd, op.Size); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + public static void EmitLdvecWithSignedCast(ILEmitterCtx context, int reg, int size) + { + context.EmitLdvec(reg); + + switch (size) + { + case 0: VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleToSByte)); break; + case 1: VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleToInt16)); break; + case 2: VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleToInt32)); break; + case 3: VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleToInt64)); break; + + default: throw new ArgumentOutOfRangeException(nameof(size)); + } + } + + public static void EmitLdvecWithCastToDouble(ILEmitterCtx context, int reg) + { + context.EmitLdvec(reg); + + VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleToDouble)); + } + + public static void EmitStvecWithCastFromDouble(ILEmitterCtx context, int reg) + { + VectorHelper.EmitCall(context, nameof(VectorHelper.VectorDoubleToSingle)); + + context.EmitStvec(reg); + } + + public static void EmitLdvecWithUnsignedCast(ILEmitterCtx context, int reg, int size) + { + context.EmitLdvec(reg); + + switch (size) + { + case 0: VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleToByte)); break; + case 1: VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleToUInt16)); break; + case 2: VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleToUInt32)); break; + case 3: VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleToUInt64)); break; + + default: throw new ArgumentOutOfRangeException(nameof(size)); + } + } + + public static void EmitStvecWithSignedCast(ILEmitterCtx context, int reg, int size) + { + switch (size) + { + case 0: VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSByteToSingle)); break; + case 1: VectorHelper.EmitCall(context, nameof(VectorHelper.VectorInt16ToSingle)); break; + case 2: VectorHelper.EmitCall(context, nameof(VectorHelper.VectorInt32ToSingle)); break; + case 3: VectorHelper.EmitCall(context, nameof(VectorHelper.VectorInt64ToSingle)); break; + + default: throw new ArgumentOutOfRangeException(nameof(size)); + } + + context.EmitStvec(reg); + } + + public static void EmitStvecWithUnsignedCast(ILEmitterCtx context, int reg, int size) + { + switch (size) + { + case 0: VectorHelper.EmitCall(context, nameof(VectorHelper.VectorByteToSingle)); break; + case 1: VectorHelper.EmitCall(context, nameof(VectorHelper.VectorUInt16ToSingle)); break; + case 2: VectorHelper.EmitCall(context, nameof(VectorHelper.VectorUInt32ToSingle)); break; + case 3: VectorHelper.EmitCall(context, nameof(VectorHelper.VectorUInt64ToSingle)); break; + + default: throw new ArgumentOutOfRangeException(nameof(size)); + } + + context.EmitStvec(reg); + } + + public static void EmitScalarSseOrSse2OpF(ILEmitterCtx context, string name) + { + EmitSseOrSse2OpF(context, name, true); + } + + public static void EmitVectorSseOrSse2OpF(ILEmitterCtx context, string name) + { + EmitSseOrSse2OpF(context, name, false); + } + + public static void EmitSseOrSse2OpF(ILEmitterCtx context, string name, bool scalar) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + int sizeF = op.Size & 1; + + void Ldvec(int reg) + { + context.EmitLdvec(reg); + + if (sizeF == 1) + { + VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleToDouble)); + } + } + + Ldvec(op.Rn); + + Type type; + Type baseType; + + if (sizeF == 0) + { + type = typeof(Sse); + baseType = typeof(Vector128); + } + else /* if (SizeF == 1) */ + { + type = typeof(Sse2); + baseType = typeof(Vector128); + } + + if (op is OpCodeSimdReg64 binOp) + { + Ldvec(binOp.Rm); + + context.EmitCall(type.GetMethod(name, new Type[] { baseType, baseType })); + } + else + { + context.EmitCall(type.GetMethod(name, new Type[] { baseType })); + } + + if (sizeF == 1) + { + VectorHelper.EmitCall(context, nameof(VectorHelper.VectorDoubleToSingle)); + } + + context.EmitStvec(op.Rd); + + if (scalar) + { + if (sizeF == 0) + { + EmitVectorZero32_128(context, op.Rd); + } + else /* if (SizeF == 1) */ + { + EmitVectorZeroUpper(context, op.Rd); + } + } + else if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + public static void EmitUnaryMathCall(ILEmitterCtx context, string name) + { + IOpCodeSimd64 op = (IOpCodeSimd64)context.CurrOp; + + int sizeF = op.Size & 1; + + MethodInfo mthdInfo; + + if (sizeF == 0) + { + mthdInfo = typeof(MathF).GetMethod(name, new Type[] { typeof(float) }); + } + else /* if (SizeF == 1) */ + { + mthdInfo = typeof(Math).GetMethod(name, new Type[] { typeof(double) }); + } + + context.EmitCall(mthdInfo); + } + + public static void EmitBinaryMathCall(ILEmitterCtx context, string name) + { + IOpCodeSimd64 op = (IOpCodeSimd64)context.CurrOp; + + int sizeF = op.Size & 1; + + MethodInfo mthdInfo; + + if (sizeF == 0) + { + mthdInfo = typeof(MathF).GetMethod(name, new Type[] { typeof(float), typeof(float) }); + } + else /* if (SizeF == 1) */ + { + mthdInfo = typeof(Math).GetMethod(name, new Type[] { typeof(double), typeof(double) }); + } + + context.EmitCall(mthdInfo); + } + + public static void EmitRoundMathCall(ILEmitterCtx context, MidpointRounding roundMode) + { + IOpCodeSimd64 op = (IOpCodeSimd64)context.CurrOp; + + int sizeF = op.Size & 1; + + MethodInfo mthdInfo; + + if (sizeF == 0) + { + mthdInfo = typeof(MathF).GetMethod(nameof(MathF.Round), new Type[] { typeof(float), typeof(MidpointRounding) }); + } + else /* if (SizeF == 1) */ + { + mthdInfo = typeof(Math).GetMethod(nameof(Math.Round), new Type[] { typeof(double), typeof(MidpointRounding) }); + } + + context.EmitLdc_I4((int)roundMode); + + context.EmitCall(mthdInfo); + } + + public static void EmitUnarySoftFloatCall(ILEmitterCtx context, string name) + { + IOpCodeSimd64 op = (IOpCodeSimd64)context.CurrOp; + + int sizeF = op.Size & 1; + + MethodInfo mthdInfo; + + if (sizeF == 0) + { + mthdInfo = typeof(SoftFloat).GetMethod(name, new Type[] { typeof(float) }); + } + else /* if (SizeF == 1) */ + { + mthdInfo = typeof(SoftFloat).GetMethod(name, new Type[] { typeof(double) }); + } + + context.EmitCall(mthdInfo); + } + + public static void EmitSoftFloatCall(ILEmitterCtx context, string name) + { + IOpCodeSimd64 op = (IOpCodeSimd64)context.CurrOp; + + Type type = (op.Size & 1) == 0 + ? typeof(SoftFloat32) + : typeof(SoftFloat64); + + context.EmitLdarg(TranslatedSub.StateArgIdx); + + context.EmitCall(type, name); + } + + public static void EmitScalarBinaryOpByElemF(ILEmitterCtx context, Action emit) + { + OpCodeSimdRegElemF64 op = (OpCodeSimdRegElemF64)context.CurrOp; + + EmitScalarOpByElemF(context, emit, op.Index, ternary: false); + } + + public static void EmitScalarTernaryOpByElemF(ILEmitterCtx context, Action emit) + { + OpCodeSimdRegElemF64 op = (OpCodeSimdRegElemF64)context.CurrOp; + + EmitScalarOpByElemF(context, emit, op.Index, ternary: true); + } + + public static void EmitScalarOpByElemF(ILEmitterCtx context, Action emit, int elem, bool ternary) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + int sizeF = op.Size & 1; + + if (ternary) + { + EmitVectorExtractF(context, op.Rd, 0, sizeF); + } + + EmitVectorExtractF(context, op.Rn, 0, sizeF); + EmitVectorExtractF(context, op.Rm, elem, sizeF); + + emit(); + + EmitScalarSetF(context, op.Rd, sizeF); + } + + public static void EmitScalarUnaryOpSx(ILEmitterCtx context, Action emit) + { + EmitScalarOp(context, emit, OperFlags.Rn, true); + } + + public static void EmitScalarBinaryOpSx(ILEmitterCtx context, Action emit) + { + EmitScalarOp(context, emit, OperFlags.RnRm, true); + } + + public static void EmitScalarUnaryOpZx(ILEmitterCtx context, Action emit) + { + EmitScalarOp(context, emit, OperFlags.Rn, false); + } + + public static void EmitScalarBinaryOpZx(ILEmitterCtx context, Action emit) + { + EmitScalarOp(context, emit, OperFlags.RnRm, false); + } + + public static void EmitScalarTernaryOpZx(ILEmitterCtx context, Action emit) + { + EmitScalarOp(context, emit, OperFlags.RdRnRm, false); + } + + public static void EmitScalarOp(ILEmitterCtx context, Action emit, OperFlags opers, bool signed) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + bool rd = (opers & OperFlags.Rd) != 0; + bool rn = (opers & OperFlags.Rn) != 0; + bool rm = (opers & OperFlags.Rm) != 0; + + if (rd) + { + EmitVectorExtract(context, op.Rd, 0, op.Size, signed); + } + + if (rn) + { + EmitVectorExtract(context, op.Rn, 0, op.Size, signed); + } + + if (rm) + { + EmitVectorExtract(context, ((OpCodeSimdReg64)op).Rm, 0, op.Size, signed); + } + + emit(); + + EmitScalarSet(context, op.Rd, op.Size); + } + + public static void EmitScalarUnaryOpF(ILEmitterCtx context, Action emit) + { + EmitScalarOpF(context, emit, OperFlags.Rn); + } + + public static void EmitScalarBinaryOpF(ILEmitterCtx context, Action emit) + { + EmitScalarOpF(context, emit, OperFlags.RnRm); + } + + public static void EmitScalarTernaryRaOpF(ILEmitterCtx context, Action emit) + { + EmitScalarOpF(context, emit, OperFlags.RaRnRm); + } + + public static void EmitScalarOpF(ILEmitterCtx context, Action emit, OperFlags opers) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + int sizeF = op.Size & 1; + + bool ra = (opers & OperFlags.Ra) != 0; + bool rn = (opers & OperFlags.Rn) != 0; + bool rm = (opers & OperFlags.Rm) != 0; + + if (ra) + { + EmitVectorExtractF(context, ((OpCodeSimdReg64)op).Ra, 0, sizeF); + } + + if (rn) + { + EmitVectorExtractF(context, op.Rn, 0, sizeF); + } + + if (rm) + { + EmitVectorExtractF(context, ((OpCodeSimdReg64)op).Rm, 0, sizeF); + } + + emit(); + + EmitScalarSetF(context, op.Rd, sizeF); + } + + public static void EmitVectorUnaryOpF(ILEmitterCtx context, Action emit) + { + EmitVectorOpF(context, emit, OperFlags.Rn); + } + + public static void EmitVectorBinaryOpF(ILEmitterCtx context, Action emit) + { + EmitVectorOpF(context, emit, OperFlags.RnRm); + } + + public static void EmitVectorTernaryOpF(ILEmitterCtx context, Action emit) + { + EmitVectorOpF(context, emit, OperFlags.RdRnRm); + } + + public static void EmitVectorOpF(ILEmitterCtx context, Action emit, OperFlags opers) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + int sizeF = op.Size & 1; + + int bytes = op.GetBitsCount() >> 3; + int elems = bytes >> sizeF + 2; + + bool rd = (opers & OperFlags.Rd) != 0; + bool rn = (opers & OperFlags.Rn) != 0; + bool rm = (opers & OperFlags.Rm) != 0; + + for (int index = 0; index < elems; index++) + { + if (rd) + { + EmitVectorExtractF(context, op.Rd, index, sizeF); + } + + if (rn) + { + EmitVectorExtractF(context, op.Rn, index, sizeF); + } + + if (rm) + { + EmitVectorExtractF(context, ((OpCodeSimdReg64)op).Rm, index, sizeF); + } + + emit(); + + EmitVectorInsertF(context, op.Rd, index, sizeF); + } + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + public static void EmitVectorBinaryOpByElemF(ILEmitterCtx context, Action emit) + { + OpCodeSimdRegElemF64 op = (OpCodeSimdRegElemF64)context.CurrOp; + + EmitVectorOpByElemF(context, emit, op.Index, ternary: false); + } + + public static void EmitVectorTernaryOpByElemF(ILEmitterCtx context, Action emit) + { + OpCodeSimdRegElemF64 op = (OpCodeSimdRegElemF64)context.CurrOp; + + EmitVectorOpByElemF(context, emit, op.Index, ternary: true); + } + + public static void EmitVectorOpByElemF(ILEmitterCtx context, Action emit, int elem, bool ternary) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + int sizeF = op.Size & 1; + + int bytes = op.GetBitsCount() >> 3; + int elems = bytes >> sizeF + 2; + + for (int index = 0; index < elems; 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 == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + public static void EmitVectorUnaryOpSx(ILEmitterCtx context, Action emit) + { + EmitVectorOp(context, emit, OperFlags.Rn, true); + } + + public static void EmitVectorBinaryOpSx(ILEmitterCtx context, Action emit) + { + EmitVectorOp(context, emit, OperFlags.RnRm, true); + } + + public static void EmitVectorTernaryOpSx(ILEmitterCtx context, Action emit) + { + EmitVectorOp(context, emit, OperFlags.RdRnRm, true); + } + + public static void EmitVectorUnaryOpZx(ILEmitterCtx context, Action emit) + { + EmitVectorOp(context, emit, OperFlags.Rn, false); + } + + public static void EmitVectorBinaryOpZx(ILEmitterCtx context, Action emit) + { + EmitVectorOp(context, emit, OperFlags.RnRm, false); + } + + public static void EmitVectorTernaryOpZx(ILEmitterCtx context, Action emit) + { + EmitVectorOp(context, emit, OperFlags.RdRnRm, false); + } + + public static void EmitVectorOp(ILEmitterCtx context, Action emit, OperFlags opers, bool signed) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + int bytes = op.GetBitsCount() >> 3; + int elems = bytes >> op.Size; + + bool rd = (opers & OperFlags.Rd) != 0; + bool rn = (opers & OperFlags.Rn) != 0; + bool rm = (opers & OperFlags.Rm) != 0; + + for (int index = 0; index < elems; index++) + { + if (rd) + { + EmitVectorExtract(context, op.Rd, index, op.Size, signed); + } + + if (rn) + { + EmitVectorExtract(context, op.Rn, index, op.Size, signed); + } + + if (rm) + { + EmitVectorExtract(context, ((OpCodeSimdReg64)op).Rm, index, op.Size, signed); + } + + emit(); + + EmitVectorInsert(context, op.Rd, index, op.Size); + } + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + public static void EmitVectorBinaryOpByElemSx(ILEmitterCtx context, Action emit) + { + OpCodeSimdRegElem64 op = (OpCodeSimdRegElem64)context.CurrOp; + + EmitVectorOpByElem(context, emit, op.Index, false, true); + } + + public static void EmitVectorBinaryOpByElemZx(ILEmitterCtx context, Action emit) + { + OpCodeSimdRegElem64 op = (OpCodeSimdRegElem64)context.CurrOp; + + EmitVectorOpByElem(context, emit, op.Index, false, false); + } + + public static void EmitVectorTernaryOpByElemZx(ILEmitterCtx context, Action emit) + { + OpCodeSimdRegElem64 op = (OpCodeSimdRegElem64)context.CurrOp; + + EmitVectorOpByElem(context, emit, op.Index, true, false); + } + + public static void EmitVectorOpByElem(ILEmitterCtx context, Action emit, int elem, bool ternary, bool signed) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + int bytes = op.GetBitsCount() >> 3; + int elems = bytes >> op.Size; + + EmitVectorExtract(context, op.Rm, elem, op.Size, signed); + context.EmitSttmp(); + + for (int index = 0; index < elems; index++) + { + if (ternary) + { + EmitVectorExtract(context, op.Rd, index, op.Size, signed); + } + + EmitVectorExtract(context, op.Rn, index, op.Size, signed); + context.EmitLdtmp(); + + emit(); + + EmitVectorInsertTmp(context, index, op.Size); + } + + context.EmitLdvectmp(); + context.EmitStvec(op.Rd); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + public static void EmitVectorImmUnaryOp(ILEmitterCtx context, Action emit) + { + EmitVectorImmOp(context, emit, false); + } + + public static void EmitVectorImmBinaryOp(ILEmitterCtx context, Action emit) + { + EmitVectorImmOp(context, emit, true); + } + + public static void EmitVectorImmOp(ILEmitterCtx context, Action emit, bool binary) + { + OpCodeSimdImm64 op = (OpCodeSimdImm64)context.CurrOp; + + int bytes = op.GetBitsCount() >> 3; + int elems = bytes >> op.Size; + + for (int index = 0; index < elems; 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 == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + public static void EmitVectorWidenRmBinaryOpSx(ILEmitterCtx context, Action emit) + { + EmitVectorWidenRmBinaryOp(context, emit, true); + } + + public static void EmitVectorWidenRmBinaryOpZx(ILEmitterCtx context, Action emit) + { + EmitVectorWidenRmBinaryOp(context, emit, false); + } + + public static void EmitVectorWidenRmBinaryOp(ILEmitterCtx context, Action emit, bool signed) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + int elems = 8 >> op.Size; + + int part = op.RegisterSize == RegisterSize.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(ILEmitterCtx context, Action emit) + { + EmitVectorWidenRnRmOp(context, emit, false, true); + } + + public static void EmitVectorWidenRnRmBinaryOpZx(ILEmitterCtx context, Action emit) + { + EmitVectorWidenRnRmOp(context, emit, false, false); + } + + public static void EmitVectorWidenRnRmTernaryOpSx(ILEmitterCtx context, Action emit) + { + EmitVectorWidenRnRmOp(context, emit, true, true); + } + + public static void EmitVectorWidenRnRmTernaryOpZx(ILEmitterCtx context, Action emit) + { + EmitVectorWidenRnRmOp(context, emit, true, false); + } + + public static void EmitVectorWidenRnRmOp(ILEmitterCtx context, Action emit, bool ternary, bool signed) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + int elems = 8 >> op.Size; + + int part = op.RegisterSize == RegisterSize.Simd128 ? elems : 0; + + for (int index = 0; index < elems; index++) + { + if (ternary) + { + EmitVectorExtract(context, op.Rd, index, op.Size + 1, signed); + } + + 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 EmitVectorPairwiseOpSx(ILEmitterCtx context, Action emit) + { + EmitVectorPairwiseOp(context, emit, true); + } + + public static void EmitVectorPairwiseOpZx(ILEmitterCtx context, Action emit) + { + EmitVectorPairwiseOp(context, emit, false); + } + + public static void EmitVectorPairwiseOp(ILEmitterCtx context, Action emit, bool signed) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + int words = op.GetBitsCount() >> 4; + int pairs = words >> op.Size; + + for (int index = 0; index < pairs; index++) + { + int idx = index << 1; + + EmitVectorExtract(context, op.Rn, idx, op.Size, signed); + EmitVectorExtract(context, op.Rn, idx + 1, op.Size, signed); + + emit(); + + EmitVectorExtract(context, op.Rm, idx, op.Size, signed); + EmitVectorExtract(context, op.Rm, idx + 1, op.Size, signed); + + emit(); + + EmitVectorInsertTmp(context, pairs + index, op.Size); + EmitVectorInsertTmp(context, index, op.Size); + } + + context.EmitLdvectmp(); + context.EmitStvec(op.Rd); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + public static void EmitVectorPairwiseOpF(ILEmitterCtx context, Action emit) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + int sizeF = op.Size & 1; + + int words = op.GetBitsCount() >> 4; + int pairs = words >> sizeF + 2; + + for (int index = 0; index < pairs; index++) + { + int idx = index << 1; + + EmitVectorExtractF(context, op.Rn, idx, sizeF); + EmitVectorExtractF(context, op.Rn, idx + 1, sizeF); + + emit(); + + EmitVectorExtractF(context, op.Rm, idx, sizeF); + EmitVectorExtractF(context, op.Rm, idx + 1, sizeF); + + emit(); + + EmitVectorInsertTmpF(context, pairs + index, sizeF); + EmitVectorInsertTmpF(context, index, sizeF); + } + + context.EmitLdvectmp(); + context.EmitStvec(op.Rd); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + [Flags] + public enum SaturatingFlags + { + Scalar = 1 << 0, + Signed = 1 << 1, + + Add = 1 << 2, + Sub = 1 << 3, + + Accumulate = 1 << 4, + + ScalarSx = Scalar | Signed, + ScalarZx = Scalar, + + VectorSx = Signed, + VectorZx = 0 + } + + public static void EmitScalarSaturatingUnaryOpSx(ILEmitterCtx context, Action emit) + { + EmitSaturatingUnaryOpSx(context, emit, SaturatingFlags.ScalarSx); + } + + public static void EmitVectorSaturatingUnaryOpSx(ILEmitterCtx context, Action emit) + { + EmitSaturatingUnaryOpSx(context, emit, SaturatingFlags.VectorSx); + } + + public static void EmitSaturatingUnaryOpSx(ILEmitterCtx context, Action emit, SaturatingFlags flags) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + bool scalar = (flags & SaturatingFlags.Scalar) != 0; + + int bytes = op.GetBitsCount() >> 3; + int elems = !scalar ? bytes >> op.Size : 1; + + if (scalar) + { + EmitVectorZeroLowerTmp(context); + } + + for (int index = 0; index < elems; index++) + { + EmitVectorExtractSx(context, op.Rn, index, op.Size); + + emit(); + + if (op.Size <= 2) + { + EmitSatQ(context, op.Size, true, true); + } + else /* if (Op.Size == 3) */ + { + EmitUnarySignedSatQAbsOrNeg(context); + } + + EmitVectorInsertTmp(context, index, op.Size); + } + + context.EmitLdvectmp(); + context.EmitStvec(op.Rd); + + if ((op.RegisterSize == RegisterSize.Simd64) || scalar) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + public static void EmitScalarSaturatingBinaryOpSx(ILEmitterCtx context, SaturatingFlags flags) + { + EmitSaturatingBinaryOp(context, () => { }, SaturatingFlags.ScalarSx | flags); + } + + public static void EmitScalarSaturatingBinaryOpZx(ILEmitterCtx context, SaturatingFlags flags) + { + EmitSaturatingBinaryOp(context, () => { }, SaturatingFlags.ScalarZx | flags); + } + + public static void EmitVectorSaturatingBinaryOpSx(ILEmitterCtx context, SaturatingFlags flags) + { + EmitSaturatingBinaryOp(context, () => { }, SaturatingFlags.VectorSx | flags); + } + + public static void EmitVectorSaturatingBinaryOpZx(ILEmitterCtx context, SaturatingFlags flags) + { + EmitSaturatingBinaryOp(context, () => { }, SaturatingFlags.VectorZx | flags); + } + + public static void EmitSaturatingBinaryOp(ILEmitterCtx context, Action emit, SaturatingFlags flags) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + bool scalar = (flags & SaturatingFlags.Scalar) != 0; + bool signed = (flags & SaturatingFlags.Signed) != 0; + + bool add = (flags & SaturatingFlags.Add) != 0; + bool sub = (flags & SaturatingFlags.Sub) != 0; + + bool accumulate = (flags & SaturatingFlags.Accumulate) != 0; + + int bytes = op.GetBitsCount() >> 3; + int elems = !scalar ? bytes >> op.Size : 1; + + if (scalar) + { + EmitVectorZeroLowerTmp(context); + } + + if (add || sub) + { + for (int index = 0; index < elems; index++) + { + EmitVectorExtract(context, op.Rn, index, op.Size, signed); + EmitVectorExtract(context, ((OpCodeSimdReg64)op).Rm, index, op.Size, signed); + + if (op.Size <= 2) + { + context.Emit(add ? OpCodes.Add : OpCodes.Sub); + + EmitSatQ(context, op.Size, true, signed); + } + else /* if (Op.Size == 3) */ + { + if (add) + { + EmitBinarySatQAdd(context, signed); + } + else /* if (Sub) */ + { + EmitBinarySatQSub(context, signed); + } + } + + EmitVectorInsertTmp(context, index, op.Size); + } + } + else if (accumulate) + { + for (int index = 0; index < elems; index++) + { + EmitVectorExtract(context, op.Rn, index, op.Size, !signed); + EmitVectorExtract(context, op.Rd, index, op.Size, signed); + + if (op.Size <= 2) + { + context.Emit(OpCodes.Add); + + EmitSatQ(context, op.Size, true, signed); + } + else /* if (Op.Size == 3) */ + { + EmitBinarySatQAccumulate(context, signed); + } + + EmitVectorInsertTmp(context, index, op.Size); + } + } + else + { + for (int index = 0; index < elems; index++) + { + EmitVectorExtract(context, op.Rn, index, op.Size, signed); + EmitVectorExtract(context, ((OpCodeSimdReg64)op).Rm, index, op.Size, signed); + + emit(); + + EmitSatQ(context, op.Size, true, signed); + + EmitVectorInsertTmp(context, index, op.Size); + } + } + + context.EmitLdvectmp(); + context.EmitStvec(op.Rd); + + if ((op.RegisterSize == RegisterSize.Simd64) || scalar) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + [Flags] + public enum SaturatingNarrowFlags + { + Scalar = 1 << 0, + SignedSrc = 1 << 1, + SignedDst = 1 << 2, + + ScalarSxSx = Scalar | SignedSrc | SignedDst, + ScalarSxZx = Scalar | SignedSrc, + ScalarZxZx = Scalar, + + VectorSxSx = SignedSrc | SignedDst, + VectorSxZx = SignedSrc, + VectorZxZx = 0 + } + + public static void EmitSaturatingNarrowOp(ILEmitterCtx context, SaturatingNarrowFlags flags) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + bool scalar = (flags & SaturatingNarrowFlags.Scalar) != 0; + bool signedSrc = (flags & SaturatingNarrowFlags.SignedSrc) != 0; + bool signedDst = (flags & SaturatingNarrowFlags.SignedDst) != 0; + + int elems = !scalar ? 8 >> op.Size : 1; + + int part = !scalar && (op.RegisterSize == RegisterSize.Simd128) ? elems : 0; + + if (scalar) + { + EmitVectorZeroLowerTmp(context); + } + + if (part != 0) + { + context.EmitLdvec(op.Rd); + context.EmitStvectmp(); + } + + for (int index = 0; index < elems; index++) + { + EmitVectorExtract(context, op.Rn, index, op.Size + 1, signedSrc); + + EmitSatQ(context, op.Size, signedSrc, signedDst); + + EmitVectorInsertTmp(context, part + index, op.Size); + } + + context.EmitLdvectmp(); + context.EmitStvec(op.Rd); + + if (part == 0) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + // TSrc (16bit, 32bit, 64bit; signed, unsigned) > TDst (8bit, 16bit, 32bit; signed, unsigned). + public static void EmitSatQ( + ILEmitterCtx context, + int sizeDst, + bool signedSrc, + bool signedDst) + { + if (sizeDst > 2) + { + throw new ArgumentOutOfRangeException(nameof(sizeDst)); + } + + context.EmitLdc_I4(sizeDst); + context.EmitLdarg(TranslatedSub.StateArgIdx); + + if (signedSrc) + { + SoftFallback.EmitCall(context, signedDst + ? nameof(SoftFallback.SignedSrcSignedDstSatQ) + : nameof(SoftFallback.SignedSrcUnsignedDstSatQ)); + } + else + { + SoftFallback.EmitCall(context, signedDst + ? nameof(SoftFallback.UnsignedSrcSignedDstSatQ) + : nameof(SoftFallback.UnsignedSrcUnsignedDstSatQ)); + } + } + + // TSrc (64bit) == TDst (64bit); signed. + public static void EmitUnarySignedSatQAbsOrNeg(ILEmitterCtx context) + { + if (((OpCodeSimd64)context.CurrOp).Size < 3) + { + throw new InvalidOperationException(); + } + + context.EmitLdarg(TranslatedSub.StateArgIdx); + + SoftFallback.EmitCall(context, nameof(SoftFallback.UnarySignedSatQAbsOrNeg)); + } + + // TSrcs (64bit) == TDst (64bit); signed, unsigned. + public static void EmitBinarySatQAdd(ILEmitterCtx context, bool signed) + { + if (((OpCodeSimdReg64)context.CurrOp).Size < 3) + { + throw new InvalidOperationException(); + } + + context.EmitLdarg(TranslatedSub.StateArgIdx); + + SoftFallback.EmitCall(context, signed + ? nameof(SoftFallback.BinarySignedSatQAdd) + : nameof(SoftFallback.BinaryUnsignedSatQAdd)); + } + + // TSrcs (64bit) == TDst (64bit); signed, unsigned. + public static void EmitBinarySatQSub(ILEmitterCtx context, bool signed) + { + if (((OpCodeSimdReg64)context.CurrOp).Size < 3) + { + throw new InvalidOperationException(); + } + + context.EmitLdarg(TranslatedSub.StateArgIdx); + + SoftFallback.EmitCall(context, signed + ? nameof(SoftFallback.BinarySignedSatQSub) + : nameof(SoftFallback.BinaryUnsignedSatQSub)); + } + + // TSrcs (64bit) == TDst (64bit); signed, unsigned. + public static void EmitBinarySatQAccumulate(ILEmitterCtx context, bool signed) + { + if (((OpCodeSimd64)context.CurrOp).Size < 3) + { + throw new InvalidOperationException(); + } + + context.EmitLdarg(TranslatedSub.StateArgIdx); + + SoftFallback.EmitCall(context, signed + ? nameof(SoftFallback.BinarySignedSatQAcc) + : nameof(SoftFallback.BinaryUnsignedSatQAcc)); + } + + public static void EmitScalarSet(ILEmitterCtx context, int reg, int size) + { + EmitVectorZeroAll(context, reg); + EmitVectorInsert(context, reg, 0, size); + } + + public static void EmitScalarSetF(ILEmitterCtx context, int reg, int size) + { + if (Optimizations.UseSse41 && size == 0) + { + //If the type is float, we can perform insertion and + //zero the upper bits with a single instruction (INSERTPS); + context.EmitLdvec(reg); + + VectorHelper.EmitCall(context, nameof(VectorHelper.Sse41VectorInsertScalarSingle)); + + context.EmitStvec(reg); + } + else + { + EmitVectorZeroAll(context, reg); + EmitVectorInsertF(context, reg, 0, size); + } + } + + public static void EmitVectorExtractSx(ILEmitterCtx context, int reg, int index, int size) + { + EmitVectorExtract(context, reg, index, size, true); + } + + public static void EmitVectorExtractZx(ILEmitterCtx context, int reg, int index, int size) + { + EmitVectorExtract(context, reg, index, size, false); + } + + public static void EmitVectorExtract(ILEmitterCtx context, int reg, int index, int size, bool signed) + { + ThrowIfInvalid(index, size); + + context.EmitLdvec(reg); + context.EmitLdc_I4(index); + context.EmitLdc_I4(size); + + VectorHelper.EmitCall(context, signed + ? nameof(VectorHelper.VectorExtractIntSx) + : nameof(VectorHelper.VectorExtractIntZx)); + } + + public static void EmitVectorExtractF(ILEmitterCtx context, int reg, int index, int size) + { + ThrowIfInvalidF(index, size); + + context.EmitLdvec(reg); + context.EmitLdc_I4(index); + + if (size == 0) + { + VectorHelper.EmitCall(context, nameof(VectorHelper.VectorExtractSingle)); + } + else if (size == 1) + { + VectorHelper.EmitCall(context, nameof(VectorHelper.VectorExtractDouble)); + } + else + { + throw new ArgumentOutOfRangeException(nameof(size)); + } + } + + public static void EmitVectorZeroAll(ILEmitterCtx context, int rd) + { + if (Optimizations.UseSse2) + { + VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleZero)); + + context.EmitStvec(rd); + } + else + { + EmitVectorZeroLower(context, rd); + EmitVectorZeroUpper(context, rd); + } + } + + public static void EmitVectorZeroLower(ILEmitterCtx context, int rd) + { + EmitVectorInsert(context, rd, 0, 3, 0); + } + + public static void EmitVectorZeroLowerTmp(ILEmitterCtx context) + { + EmitVectorInsertTmp(context, 0, 3, 0); + } + + public static void EmitVectorZeroUpper(ILEmitterCtx context, int reg) + { + if (Optimizations.UseSse2) + { + //TODO: Use MoveScalar once it is fixed, as of the + //time of writing it just crashes the JIT. + EmitLdvecWithUnsignedCast(context, reg, 3); + + Type[] types = new Type[] { typeof(Vector128), typeof(byte) }; + + //Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.MoveScalar), Types)); + + context.EmitLdc_I4(8); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftLeftLogical128BitLane), types)); + + context.EmitLdc_I4(8); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), types)); + + EmitStvecWithUnsignedCast(context, reg, 3); + } + else + { + EmitVectorInsert(context, reg, 1, 3, 0); + } + } + + public static void EmitVectorZero32_128(ILEmitterCtx context, int reg) + { + context.EmitLdvec(reg); + + VectorHelper.EmitCall(context, nameof(VectorHelper.VectorZero32_128)); + + context.EmitStvec(reg); + } + + public static void EmitVectorInsert(ILEmitterCtx context, int reg, int index, int size) + { + ThrowIfInvalid(index, size); + + context.EmitLdvec(reg); + context.EmitLdc_I4(index); + context.EmitLdc_I4(size); + + VectorHelper.EmitCall(context, nameof(VectorHelper.VectorInsertInt)); + + context.EmitStvec(reg); + } + + public static void EmitVectorInsertTmp(ILEmitterCtx context, int index, int size) + { + ThrowIfInvalid(index, size); + + context.EmitLdvectmp(); + context.EmitLdc_I4(index); + context.EmitLdc_I4(size); + + VectorHelper.EmitCall(context, nameof(VectorHelper.VectorInsertInt)); + + context.EmitStvectmp(); + } + + public static void EmitVectorInsert(ILEmitterCtx context, int reg, int index, int size, long value) + { + ThrowIfInvalid(index, size); + + context.EmitLdc_I8(value); + context.EmitLdvec(reg); + context.EmitLdc_I4(index); + context.EmitLdc_I4(size); + + VectorHelper.EmitCall(context, nameof(VectorHelper.VectorInsertInt)); + + context.EmitStvec(reg); + } + + public static void EmitVectorInsertTmp(ILEmitterCtx context, int index, int size, long value) + { + ThrowIfInvalid(index, size); + + context.EmitLdc_I8(value); + context.EmitLdvectmp(); + context.EmitLdc_I4(index); + context.EmitLdc_I4(size); + + VectorHelper.EmitCall(context, nameof(VectorHelper.VectorInsertInt)); + + context.EmitStvectmp(); + } + + public static void EmitVectorInsertF(ILEmitterCtx context, int reg, int index, int size) + { + ThrowIfInvalidF(index, size); + + context.EmitLdvec(reg); + context.EmitLdc_I4(index); + + if (size == 0) + { + VectorHelper.EmitCall(context, nameof(VectorHelper.VectorInsertSingle)); + } + else if (size == 1) + { + VectorHelper.EmitCall(context, nameof(VectorHelper.VectorInsertDouble)); + } + else + { + throw new ArgumentOutOfRangeException(nameof(size)); + } + + context.EmitStvec(reg); + } + + public static void EmitVectorInsertTmpF(ILEmitterCtx context, int index, int size) + { + ThrowIfInvalidF(index, size); + + context.EmitLdvectmp(); + context.EmitLdc_I4(index); + + if (size == 0) + { + VectorHelper.EmitCall(context, nameof(VectorHelper.VectorInsertSingle)); + } + else if (size == 1) + { + VectorHelper.EmitCall(context, nameof(VectorHelper.VectorInsertDouble)); + } + else + { + throw new ArgumentOutOfRangeException(nameof(size)); + } + + context.EmitStvectmp(); + } + + private static void ThrowIfInvalid(int index, int size) + { + if ((uint)size > 3u) + { + throw new ArgumentOutOfRangeException(nameof(size)); + } + + if ((uint)index >= 16u >> size) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + } + + private static void ThrowIfInvalidF(int index, int size) + { + if ((uint)size > 1u) + { + throw new ArgumentOutOfRangeException(nameof(size)); + } + + if ((uint)index >= 4u >> size) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + } + } +} diff --git a/Instructions/InstEmitSimdLogical.cs b/Instructions/InstEmitSimdLogical.cs new file mode 100644 index 0000000..f51568e --- /dev/null +++ b/Instructions/InstEmitSimdLogical.cs @@ -0,0 +1,311 @@ +using ChocolArm64.Decoders; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System; +using System.Reflection.Emit; +using System.Runtime.Intrinsics.X86; + +using static ChocolArm64.Instructions.InstEmitSimdHelper; + +namespace ChocolArm64.Instructions +{ + static partial class InstEmit + { + public static void And_V(ILEmitterCtx context) + { + if (Optimizations.UseSse2) + { + EmitSse2Op(context, nameof(Sse2.And)); + } + else + { + EmitVectorBinaryOpZx(context, () => context.Emit(OpCodes.And)); + } + } + + public static void Bic_V(ILEmitterCtx context) + { + if (Optimizations.UseSse2) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + EmitLdvecWithUnsignedCast(context, op.Rm, op.Size); + EmitLdvecWithUnsignedCast(context, op.Rn, op.Size); + + Type[] types = new Type[] + { + VectorUIntTypesPerSizeLog2[op.Size], + VectorUIntTypesPerSizeLog2[op.Size] + }; + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.AndNot), types)); + + EmitStvecWithUnsignedCast(context, op.Rd, op.Size); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + else + { + EmitVectorBinaryOpZx(context, () => + { + context.Emit(OpCodes.Not); + context.Emit(OpCodes.And); + }); + } + } + + public static void Bic_Vi(ILEmitterCtx context) + { + EmitVectorImmBinaryOp(context, () => + { + context.Emit(OpCodes.Not); + context.Emit(OpCodes.And); + }); + } + + public static void Bif_V(ILEmitterCtx context) + { + EmitBitBif(context, true); + } + + public static void Bit_V(ILEmitterCtx context) + { + EmitBitBif(context, false); + } + + private static void EmitBitBif(ILEmitterCtx context, bool notRm) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + if (Optimizations.UseSse2) + { + Type[] types = new Type[] + { + VectorUIntTypesPerSizeLog2[op.Size], + VectorUIntTypesPerSizeLog2[op.Size] + }; + + EmitLdvecWithUnsignedCast(context, op.Rm, op.Size); + EmitLdvecWithUnsignedCast(context, op.Rd, op.Size); + EmitLdvecWithUnsignedCast(context, op.Rn, op.Size); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Xor), types)); + + string name = notRm ? nameof(Sse2.AndNot) : nameof(Sse2.And); + + context.EmitCall(typeof(Sse2).GetMethod(name, types)); + + EmitLdvecWithUnsignedCast(context, op.Rd, op.Size); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Xor), types)); + + EmitStvecWithUnsignedCast(context, op.Rd, op.Size); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + else + { + int bytes = op.GetBitsCount() >> 3; + int elems = bytes >> op.Size; + + for (int index = 0; index < elems; index++) + { + EmitVectorExtractZx(context, op.Rd, index, op.Size); + EmitVectorExtractZx(context, op.Rn, index, op.Size); + + context.Emit(OpCodes.Xor); + + EmitVectorExtractZx(context, op.Rm, index, op.Size); + + if (notRm) + { + context.Emit(OpCodes.Not); + } + + context.Emit(OpCodes.And); + + EmitVectorExtractZx(context, op.Rd, index, op.Size); + + context.Emit(OpCodes.Xor); + + EmitVectorInsert(context, op.Rd, index, op.Size); + } + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + } + + public static void Bsl_V(ILEmitterCtx context) + { + if (Optimizations.UseSse2) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + Type[] types = new Type[] + { + VectorUIntTypesPerSizeLog2[op.Size], + VectorUIntTypesPerSizeLog2[op.Size] + }; + + EmitLdvecWithUnsignedCast(context, op.Rn, op.Size); + EmitLdvecWithUnsignedCast(context, op.Rm, op.Size); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Xor), types)); + + EmitLdvecWithUnsignedCast(context, op.Rd, op.Size); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.And), types)); + + EmitLdvecWithUnsignedCast(context, op.Rm, op.Size); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Xor), types)); + + EmitStvecWithUnsignedCast(context, op.Rd, op.Size); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + else + { + 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(ILEmitterCtx context) + { + if (Optimizations.UseSse2) + { + EmitSse2Op(context, nameof(Sse2.Xor)); + } + else + { + EmitVectorBinaryOpZx(context, () => context.Emit(OpCodes.Xor)); + } + } + + public static void Not_V(ILEmitterCtx context) + { + EmitVectorUnaryOpZx(context, () => context.Emit(OpCodes.Not)); + } + + public static void Orn_V(ILEmitterCtx context) + { + EmitVectorBinaryOpZx(context, () => + { + context.Emit(OpCodes.Not); + context.Emit(OpCodes.Or); + }); + } + + public static void Orr_V(ILEmitterCtx context) + { + if (Optimizations.UseSse2) + { + EmitSse2Op(context, nameof(Sse2.Or)); + } + else + { + EmitVectorBinaryOpZx(context, () => context.Emit(OpCodes.Or)); + } + } + + public static void Orr_Vi(ILEmitterCtx context) + { + EmitVectorImmBinaryOp(context, () => context.Emit(OpCodes.Or)); + } + + public static void Rbit_V(ILEmitterCtx context) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + int elems = op.RegisterSize == RegisterSize.Simd128 ? 16 : 8; + + for (int index = 0; index < elems; index++) + { + EmitVectorExtractZx(context, op.Rn, index, 0); + + context.Emit(OpCodes.Conv_U4); + + SoftFallback.EmitCall(context, nameof(SoftFallback.ReverseBits8)); + + context.Emit(OpCodes.Conv_U8); + + EmitVectorInsert(context, op.Rd, index, 0); + } + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + public static void Rev16_V(ILEmitterCtx context) + { + EmitRev_V(context, containerSize: 1); + } + + public static void Rev32_V(ILEmitterCtx context) + { + EmitRev_V(context, containerSize: 2); + } + + public static void Rev64_V(ILEmitterCtx context) + { + EmitRev_V(context, containerSize: 3); + } + + private static void EmitRev_V(ILEmitterCtx context, int containerSize) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + if (op.Size >= containerSize) + { + throw new InvalidOperationException(); + } + + int bytes = op.GetBitsCount() >> 3; + int elems = bytes >> op.Size; + + int containerMask = (1 << (containerSize - op.Size)) - 1; + + for (int index = 0; index < elems; index++) + { + int revIndex = index ^ containerMask; + + EmitVectorExtractZx(context, op.Rn, revIndex, op.Size); + + EmitVectorInsertTmp(context, index, op.Size); + } + + context.EmitLdvectmp(); + context.EmitStvec(op.Rd); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + } +} diff --git a/Instructions/InstEmitSimdMemory.cs b/Instructions/InstEmitSimdMemory.cs new file mode 100644 index 0000000..eb05325 --- /dev/null +++ b/Instructions/InstEmitSimdMemory.cs @@ -0,0 +1,185 @@ +using ChocolArm64.Decoders; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System; +using System.Reflection.Emit; + +using static ChocolArm64.Instructions.InstEmitMemoryHelper; +using static ChocolArm64.Instructions.InstEmitSimdHelper; + +namespace ChocolArm64.Instructions +{ + static partial class InstEmit + { + public static void Ld__Vms(ILEmitterCtx context) + { + EmitSimdMemMs(context, isLoad: true); + } + + public static void Ld__Vss(ILEmitterCtx context) + { + EmitSimdMemSs(context, isLoad: true); + } + + public static void St__Vms(ILEmitterCtx context) + { + EmitSimdMemMs(context, isLoad: false); + } + + public static void St__Vss(ILEmitterCtx context) + { + EmitSimdMemSs(context, isLoad: false); + } + + private static void EmitSimdMemMs(ILEmitterCtx context, bool isLoad) + { + OpCodeSimdMemMs64 op = (OpCodeSimdMemMs64)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(TranslatedSub.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 == RegisterSize.Simd64 && elem == op.Elems - 1) + { + EmitVectorZeroUpper(context, rtt); + } + } + else + { + context.EmitLdarg(TranslatedSub.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(ILEmitterCtx context, bool isLoad) + { + OpCodeSimdMemSs64 op = (OpCodeSimdMemSs64)context.CurrOp; + + int offset = 0; + + void EmitMemAddress() + { + context.EmitLdarg(TranslatedSub.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 = op.GetBitsCount() >> 3; + int elems = bytes >> op.Size; + + for (int sElem = 0; sElem < op.SElems; sElem++) + { + int rt = (op.Rt + sElem) & 0x1f; + + for (int index = 0; index < elems; index++) + { + EmitMemAddress(); + + EmitReadZxCall(context, op.Size); + + EmitVectorInsert(context, rt, index, op.Size); + } + + if (op.RegisterSize == RegisterSize.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(ILEmitterCtx context, int offset) + { + OpCodeMemReg64 op = (OpCodeMemReg64)context.CurrOp; + + context.EmitLdint(op.Rn); + + if (op.Rm != CpuThreadState.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/Instructions/InstEmitSimdMove.cs b/Instructions/InstEmitSimdMove.cs new file mode 100644 index 0000000..3f539b8 --- /dev/null +++ b/Instructions/InstEmitSimdMove.cs @@ -0,0 +1,562 @@ +using ChocolArm64.Decoders; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System; +using System.Reflection.Emit; +using System.Runtime.Intrinsics.X86; + +using static ChocolArm64.Instructions.InstEmitSimdHelper; + +namespace ChocolArm64.Instructions +{ + static partial class InstEmit + { + public static void Dup_Gp(ILEmitterCtx context) + { + OpCodeSimdIns64 op = (OpCodeSimdIns64)context.CurrOp; + + if (Optimizations.UseSse2) + { + context.EmitLdintzr(op.Rn); + + switch (op.Size) + { + case 0: context.Emit(OpCodes.Conv_U1); break; + case 1: context.Emit(OpCodes.Conv_U2); break; + case 2: context.Emit(OpCodes.Conv_U4); break; + } + + Type[] types = new Type[] { UIntTypesPerSizeLog2[op.Size] }; + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetAllVector128), types)); + + EmitStvecWithUnsignedCast(context, op.Rd, op.Size); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + else + { + int bytes = op.GetBitsCount() >> 3; + int elems = bytes >> op.Size; + + for (int index = 0; index < elems; index++) + { + context.EmitLdintzr(op.Rn); + + EmitVectorInsert(context, op.Rd, index, op.Size); + } + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + } + + public static void Dup_S(ILEmitterCtx context) + { + OpCodeSimdIns64 op = (OpCodeSimdIns64)context.CurrOp; + + EmitVectorExtractZx(context, op.Rn, op.DstIndex, op.Size); + + EmitScalarSet(context, op.Rd, op.Size); + } + + public static void Dup_V(ILEmitterCtx context) + { + OpCodeSimdIns64 op = (OpCodeSimdIns64)context.CurrOp; + + int bytes = op.GetBitsCount() >> 3; + int elems = bytes >> op.Size; + + for (int index = 0; index < elems; index++) + { + EmitVectorExtractZx(context, op.Rn, op.DstIndex, op.Size); + + EmitVectorInsert(context, op.Rd, index, op.Size); + } + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + public static void Ext_V(ILEmitterCtx context) + { + OpCodeSimdExt64 op = (OpCodeSimdExt64)context.CurrOp; + + context.EmitLdvec(op.Rd); + context.EmitStvectmp(); + + int bytes = op.GetBitsCount() >> 3; + + int position = op.Imm4; + + for (int index = 0; index < bytes; index++) + { + int reg = op.Imm4 + index < bytes ? op.Rn : op.Rm; + + if (position == bytes) + { + position = 0; + } + + EmitVectorExtractZx(context, reg, position++, 0); + EmitVectorInsertTmp(context, index, 0); + } + + context.EmitLdvectmp(); + context.EmitStvec(op.Rd); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + public static void Fcsel_S(ILEmitterCtx context) + { + OpCodeSimdFcond64 op = (OpCodeSimdFcond64)context.CurrOp; + + ILLabel lblTrue = new ILLabel(); + ILLabel lblEnd = new ILLabel(); + + 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(ILEmitterCtx context) + { + OpCodeSimdCvt64 op = (OpCodeSimdCvt64)context.CurrOp; + + EmitVectorExtractZx(context, op.Rn, 0, 3); + + EmitIntZeroUpperIfNeeded(context); + + context.EmitStintzr(op.Rd); + } + + public static void Fmov_Ftoi1(ILEmitterCtx context) + { + OpCodeSimdCvt64 op = (OpCodeSimdCvt64)context.CurrOp; + + EmitVectorExtractZx(context, op.Rn, 1, 3); + + EmitIntZeroUpperIfNeeded(context); + + context.EmitStintzr(op.Rd); + } + + public static void Fmov_Itof(ILEmitterCtx context) + { + OpCodeSimdCvt64 op = (OpCodeSimdCvt64)context.CurrOp; + + context.EmitLdintzr(op.Rn); + + EmitIntZeroUpperIfNeeded(context); + + EmitScalarSet(context, op.Rd, 3); + } + + public static void Fmov_Itof1(ILEmitterCtx context) + { + OpCodeSimdCvt64 op = (OpCodeSimdCvt64)context.CurrOp; + + context.EmitLdintzr(op.Rn); + + EmitIntZeroUpperIfNeeded(context); + + EmitVectorInsert(context, op.Rd, 1, 3); + } + + public static void Fmov_S(ILEmitterCtx context) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + EmitVectorExtractF(context, op.Rn, 0, op.Size); + + EmitScalarSetF(context, op.Rd, op.Size); + } + + public static void Fmov_Si(ILEmitterCtx context) + { + OpCodeSimdFmov64 op = (OpCodeSimdFmov64)context.CurrOp; + + context.EmitLdc_I8(op.Imm); + + EmitScalarSet(context, op.Rd, op.Size + 2); + } + + public static void Fmov_V(ILEmitterCtx context) + { + OpCodeSimdImm64 op = (OpCodeSimdImm64)context.CurrOp; + + int elems = op.RegisterSize == RegisterSize.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 == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + public static void Ins_Gp(ILEmitterCtx context) + { + OpCodeSimdIns64 op = (OpCodeSimdIns64)context.CurrOp; + + context.EmitLdintzr(op.Rn); + + EmitVectorInsert(context, op.Rd, op.DstIndex, op.Size); + } + + public static void Ins_V(ILEmitterCtx context) + { + OpCodeSimdIns64 op = (OpCodeSimdIns64)context.CurrOp; + + EmitVectorExtractZx(context, op.Rn, op.SrcIndex, op.Size); + + EmitVectorInsert(context, op.Rd, op.DstIndex, op.Size); + } + + public static void Movi_V(ILEmitterCtx context) + { + EmitVectorImmUnaryOp(context, () => { }); + } + + public static void Mvni_V(ILEmitterCtx context) + { + EmitVectorImmUnaryOp(context, () => context.Emit(OpCodes.Not)); + } + + public static void Smov_S(ILEmitterCtx context) + { + OpCodeSimdIns64 op = (OpCodeSimdIns64)context.CurrOp; + + EmitVectorExtractSx(context, op.Rn, op.DstIndex, op.Size); + + EmitIntZeroUpperIfNeeded(context); + + context.EmitStintzr(op.Rd); + } + + public static void Tbl_V(ILEmitterCtx context) + { + OpCodeSimdTbl64 op = (OpCodeSimdTbl64)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: VectorHelper.EmitCall(context, + nameof(VectorHelper.Tbl1_V64), + nameof(VectorHelper.Tbl1_V128)); break; + + case 2: VectorHelper.EmitCall(context, + nameof(VectorHelper.Tbl2_V64), + nameof(VectorHelper.Tbl2_V128)); break; + + case 3: VectorHelper.EmitCall(context, + nameof(VectorHelper.Tbl3_V64), + nameof(VectorHelper.Tbl3_V128)); break; + + case 4: VectorHelper.EmitCall(context, + nameof(VectorHelper.Tbl4_V64), + nameof(VectorHelper.Tbl4_V128)); break; + + default: throw new InvalidOperationException(); + } + + context.EmitStvec(op.Rd); + } + + public static void Trn1_V(ILEmitterCtx context) + { + EmitVectorTranspose(context, part: 0); + } + + public static void Trn2_V(ILEmitterCtx context) + { + EmitVectorTranspose(context, part: 1); + } + + public static void Umov_S(ILEmitterCtx context) + { + OpCodeSimdIns64 op = (OpCodeSimdIns64)context.CurrOp; + + EmitVectorExtractZx(context, op.Rn, op.DstIndex, op.Size); + + context.EmitStintzr(op.Rd); + } + + public static void Uzp1_V(ILEmitterCtx context) + { + EmitVectorUnzip(context, part: 0); + } + + public static void Uzp2_V(ILEmitterCtx context) + { + EmitVectorUnzip(context, part: 1); + } + + public static void Xtn_V(ILEmitterCtx context) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + int elems = 8 >> op.Size; + + int part = op.RegisterSize == RegisterSize.Simd128 ? elems : 0; + + if (Optimizations.UseSse41 && op.Size < 2) + { + void EmitZeroVector() + { + switch (op.Size) + { + case 0: VectorHelper.EmitCall(context, nameof(VectorHelper.VectorInt16Zero)); break; + case 1: VectorHelper.EmitCall(context, nameof(VectorHelper.VectorInt32Zero)); break; + } + } + + //For XTN, first operand is source, second operand is 0. + //For XTN2, first operand is 0, second operand is source. + if (part != 0) + { + EmitZeroVector(); + } + + EmitLdvecWithSignedCast(context, op.Rn, op.Size + 1); + + //Set mask to discard the upper half of the wide elements. + switch (op.Size) + { + case 0: context.EmitLdc_I4(0x00ff); break; + case 1: context.EmitLdc_I4(0x0000ffff); break; + } + + Type wideType = IntTypesPerSizeLog2[op.Size + 1]; + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetAllVector128), new Type[] { wideType })); + + wideType = VectorIntTypesPerSizeLog2[op.Size + 1]; + + Type[] wideTypes = new Type[] { wideType, wideType }; + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.And), wideTypes)); + + if (part == 0) + { + EmitZeroVector(); + } + + //Pack values with signed saturation, the signed saturation shouldn't + //saturate anything since the upper bits were masked off. + Type sseType = op.Size == 0 ? typeof(Sse2) : typeof(Sse41); + + context.EmitCall(sseType.GetMethod(nameof(Sse2.PackUnsignedSaturate), wideTypes)); + + if (part != 0) + { + //For XTN2, we additionally need to discard the upper bits + //of the target register and OR the result with it. + EmitVectorZeroUpper(context, op.Rd); + + EmitLdvecWithUnsignedCast(context, op.Rd, op.Size); + + Type narrowType = VectorUIntTypesPerSizeLog2[op.Size]; + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Or), new Type[] { narrowType, narrowType })); + } + + EmitStvecWithUnsignedCast(context, op.Rd, op.Size); + } + else + { + if (part != 0) + { + context.EmitLdvec(op.Rd); + context.EmitStvectmp(); + } + + for (int index = 0; index < elems; index++) + { + EmitVectorExtractZx(context, op.Rn, index, op.Size + 1); + + EmitVectorInsertTmp(context, part + index, op.Size); + } + + context.EmitLdvectmp(); + context.EmitStvec(op.Rd); + + if (part == 0) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + } + + public static void Zip1_V(ILEmitterCtx context) + { + EmitVectorZip(context, part: 0); + } + + public static void Zip2_V(ILEmitterCtx context) + { + EmitVectorZip(context, part: 1); + } + + private static void EmitIntZeroUpperIfNeeded(ILEmitterCtx context) + { + if (context.CurrOp.RegisterSize == RegisterSize.Int32 || + context.CurrOp.RegisterSize == RegisterSize.Simd64) + { + context.Emit(OpCodes.Conv_U4); + context.Emit(OpCodes.Conv_U8); + } + } + + private static void EmitVectorTranspose(ILEmitterCtx context, int part) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + int words = op.GetBitsCount() >> 4; + int pairs = words >> op.Size; + + for (int index = 0; index < pairs; index++) + { + int idx = index << 1; + + EmitVectorExtractZx(context, op.Rn, idx + part, op.Size); + EmitVectorExtractZx(context, op.Rm, idx + part, op.Size); + + EmitVectorInsertTmp(context, idx + 1, op.Size); + EmitVectorInsertTmp(context, idx, op.Size); + } + + context.EmitLdvectmp(); + context.EmitStvec(op.Rd); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + private static void EmitVectorUnzip(ILEmitterCtx context, int part) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + int words = op.GetBitsCount() >> 4; + int pairs = words >> op.Size; + + for (int index = 0; index < pairs; index++) + { + int idx = index << 1; + + EmitVectorExtractZx(context, op.Rn, idx + part, op.Size); + EmitVectorExtractZx(context, op.Rm, idx + part, op.Size); + + EmitVectorInsertTmp(context, pairs + index, op.Size); + EmitVectorInsertTmp(context, index, op.Size); + } + + context.EmitLdvectmp(); + context.EmitStvec(op.Rd); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + private static void EmitVectorZip(ILEmitterCtx context, int part) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + if (Optimizations.UseSse2) + { + EmitLdvecWithUnsignedCast(context, op.Rn, op.Size); + EmitLdvecWithUnsignedCast(context, op.Rm, op.Size); + + Type[] types = new Type[] + { + VectorUIntTypesPerSizeLog2[op.Size], + VectorUIntTypesPerSizeLog2[op.Size] + }; + + string name = part == 0 || (part != 0 && op.RegisterSize == RegisterSize.Simd64) + ? nameof(Sse2.UnpackLow) + : nameof(Sse2.UnpackHigh); + + context.EmitCall(typeof(Sse2).GetMethod(name, types)); + + if (op.RegisterSize == RegisterSize.Simd64 && part != 0) + { + context.EmitLdc_I4(8); + + Type[] shTypes = new Type[] { VectorUIntTypesPerSizeLog2[op.Size], typeof(byte) }; + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), shTypes)); + } + + EmitStvecWithUnsignedCast(context, op.Rd, op.Size); + + if (op.RegisterSize == RegisterSize.Simd64 && part == 0) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + else + { + int words = op.GetBitsCount() >> 4; + int pairs = words >> op.Size; + + int Base = part != 0 ? pairs : 0; + + for (int index = 0; index < pairs; index++) + { + int idx = index << 1; + + EmitVectorExtractZx(context, op.Rn, Base + index, op.Size); + EmitVectorExtractZx(context, op.Rm, Base + index, op.Size); + + EmitVectorInsertTmp(context, idx + 1, op.Size); + EmitVectorInsertTmp(context, idx, op.Size); + } + + context.EmitLdvectmp(); + context.EmitStvec(op.Rd); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + } + } +} diff --git a/Instructions/InstEmitSimdShift.cs b/Instructions/InstEmitSimdShift.cs new file mode 100644 index 0000000..3c24ff2 --- /dev/null +++ b/Instructions/InstEmitSimdShift.cs @@ -0,0 +1,865 @@ +// https://github.com/intel/ARM_NEON_2_x86_SSE/blob/master/NEON_2_SSE.h + +using ChocolArm64.Decoders; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System; +using System.Reflection.Emit; +using System.Runtime.Intrinsics.X86; + +using static ChocolArm64.Instructions.InstEmitSimdHelper; + +namespace ChocolArm64.Instructions +{ + static partial class InstEmit + { + public static void Rshrn_V(ILEmitterCtx context) + { + EmitVectorShrImmNarrowOpZx(context, round: true); + } + + public static void Shl_S(ILEmitterCtx context) + { + OpCodeSimdShImm64 op = (OpCodeSimdShImm64)context.CurrOp; + + EmitScalarUnaryOpZx(context, () => + { + context.EmitLdc_I4(GetImmShl(op)); + + context.Emit(OpCodes.Shl); + }); + } + + public static void Shl_V(ILEmitterCtx context) + { + OpCodeSimdShImm64 op = (OpCodeSimdShImm64)context.CurrOp; + + if (Optimizations.UseSse2 && op.Size > 0) + { + Type[] typesSll = new Type[] { VectorUIntTypesPerSizeLog2[op.Size], typeof(byte) }; + + EmitLdvecWithUnsignedCast(context, op.Rn, op.Size); + + context.EmitLdc_I4(GetImmShl(op)); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftLeftLogical), typesSll)); + + EmitStvecWithUnsignedCast(context, op.Rd, op.Size); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + else + { + EmitVectorUnaryOpZx(context, () => + { + context.EmitLdc_I4(GetImmShl(op)); + + context.Emit(OpCodes.Shl); + }); + } + } + + public static void Shll_V(ILEmitterCtx context) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + int shift = 8 << op.Size; + + EmitVectorShImmWidenBinaryZx(context, () => context.Emit(OpCodes.Shl), shift); + } + + public static void Shrn_V(ILEmitterCtx context) + { + EmitVectorShrImmNarrowOpZx(context, round: false); + } + + public static void Sli_V(ILEmitterCtx context) + { + OpCodeSimdShImm64 op = (OpCodeSimdShImm64)context.CurrOp; + + int bytes = op.GetBitsCount() >> 3; + int elems = bytes >> op.Size; + + int shift = GetImmShl(op); + + ulong mask = shift != 0 ? ulong.MaxValue >> (64 - shift) : 0; + + for (int index = 0; index < elems; index++) + { + EmitVectorExtractZx(context, op.Rn, index, op.Size); + + context.EmitLdc_I4(shift); + + context.Emit(OpCodes.Shl); + + EmitVectorExtractZx(context, op.Rd, index, op.Size); + + context.EmitLdc_I8((long)mask); + + context.Emit(OpCodes.And); + context.Emit(OpCodes.Or); + + EmitVectorInsert(context, op.Rd, index, op.Size); + } + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + public static void Sqrshrn_S(ILEmitterCtx context) + { + EmitRoundShrImmSaturatingNarrowOp(context, ShrImmSaturatingNarrowFlags.ScalarSxSx); + } + + public static void Sqrshrn_V(ILEmitterCtx context) + { + EmitRoundShrImmSaturatingNarrowOp(context, ShrImmSaturatingNarrowFlags.VectorSxSx); + } + + public static void Sqrshrun_S(ILEmitterCtx context) + { + EmitRoundShrImmSaturatingNarrowOp(context, ShrImmSaturatingNarrowFlags.ScalarSxZx); + } + + public static void Sqrshrun_V(ILEmitterCtx context) + { + EmitRoundShrImmSaturatingNarrowOp(context, ShrImmSaturatingNarrowFlags.VectorSxZx); + } + + public static void Sqshrn_S(ILEmitterCtx context) + { + EmitShrImmSaturatingNarrowOp(context, ShrImmSaturatingNarrowFlags.ScalarSxSx); + } + + public static void Sqshrn_V(ILEmitterCtx context) + { + EmitShrImmSaturatingNarrowOp(context, ShrImmSaturatingNarrowFlags.VectorSxSx); + } + + public static void Sqshrun_S(ILEmitterCtx context) + { + EmitShrImmSaturatingNarrowOp(context, ShrImmSaturatingNarrowFlags.ScalarSxZx); + } + + public static void Sqshrun_V(ILEmitterCtx context) + { + EmitShrImmSaturatingNarrowOp(context, ShrImmSaturatingNarrowFlags.VectorSxZx); + } + + public static void Srshr_S(ILEmitterCtx context) + { + EmitScalarShrImmOpSx(context, ShrImmFlags.Round); + } + + public static void Srshr_V(ILEmitterCtx context) + { + OpCodeSimdShImm64 op = (OpCodeSimdShImm64)context.CurrOp; + + if (Optimizations.UseSse2 && op.Size > 0 + && op.Size < 3) + { + Type[] typesShs = new Type[] { VectorIntTypesPerSizeLog2[op.Size], typeof(byte) }; + Type[] typesAdd = new Type[] { VectorIntTypesPerSizeLog2[op.Size], VectorIntTypesPerSizeLog2[op.Size] }; + + int shift = GetImmShr(op); + int eSize = 8 << op.Size; + + EmitLdvecWithSignedCast(context, op.Rn, op.Size); + + context.Emit(OpCodes.Dup); + context.EmitStvectmp(); + + context.EmitLdc_I4(eSize - shift); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftLeftLogical), typesShs)); + + context.EmitLdc_I4(eSize - 1); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical), typesShs)); + + context.EmitLdvectmp(); + + context.EmitLdc_I4(shift); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightArithmetic), typesShs)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), typesAdd)); + + EmitStvecWithSignedCast(context, op.Rd, op.Size); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + else + { + EmitVectorShrImmOpSx(context, ShrImmFlags.Round); + } + } + + public static void Srsra_S(ILEmitterCtx context) + { + EmitScalarShrImmOpSx(context, ShrImmFlags.Round | ShrImmFlags.Accumulate); + } + + public static void Srsra_V(ILEmitterCtx context) + { + OpCodeSimdShImm64 op = (OpCodeSimdShImm64)context.CurrOp; + + if (Optimizations.UseSse2 && op.Size > 0 + && op.Size < 3) + { + Type[] typesShs = new Type[] { VectorIntTypesPerSizeLog2[op.Size], typeof(byte) }; + Type[] typesAdd = new Type[] { VectorIntTypesPerSizeLog2[op.Size], VectorIntTypesPerSizeLog2[op.Size] }; + + int shift = GetImmShr(op); + int eSize = 8 << op.Size; + + EmitLdvecWithSignedCast(context, op.Rd, op.Size); + EmitLdvecWithSignedCast(context, op.Rn, op.Size); + + context.Emit(OpCodes.Dup); + context.EmitStvectmp(); + + context.EmitLdc_I4(eSize - shift); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftLeftLogical), typesShs)); + + context.EmitLdc_I4(eSize - 1); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical), typesShs)); + + context.EmitLdvectmp(); + + context.EmitLdc_I4(shift); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightArithmetic), typesShs)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), typesAdd)); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), typesAdd)); + + EmitStvecWithSignedCast(context, op.Rd, op.Size); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + else + { + EmitVectorShrImmOpSx(context, ShrImmFlags.Round | ShrImmFlags.Accumulate); + } + } + + public static void Sshl_V(ILEmitterCtx context) + { + EmitVectorShl(context, signed: true); + } + + public static void Sshll_V(ILEmitterCtx context) + { + OpCodeSimdShImm64 op = (OpCodeSimdShImm64)context.CurrOp; + + EmitVectorShImmWidenBinarySx(context, () => context.Emit(OpCodes.Shl), GetImmShl(op)); + } + + public static void Sshr_S(ILEmitterCtx context) + { + EmitShrImmOp(context, ShrImmFlags.ScalarSx); + } + + public static void Sshr_V(ILEmitterCtx context) + { + OpCodeSimdShImm64 op = (OpCodeSimdShImm64)context.CurrOp; + + if (Optimizations.UseSse2 && op.Size > 0 + && op.Size < 3) + { + Type[] typesSra = new Type[] { VectorIntTypesPerSizeLog2[op.Size], typeof(byte) }; + + EmitLdvecWithSignedCast(context, op.Rn, op.Size); + + context.EmitLdc_I4(GetImmShr(op)); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightArithmetic), typesSra)); + + EmitStvecWithSignedCast(context, op.Rd, op.Size); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + else + { + EmitShrImmOp(context, ShrImmFlags.VectorSx); + } + } + + public static void Ssra_S(ILEmitterCtx context) + { + EmitScalarShrImmOpSx(context, ShrImmFlags.Accumulate); + } + + public static void Ssra_V(ILEmitterCtx context) + { + OpCodeSimdShImm64 op = (OpCodeSimdShImm64)context.CurrOp; + + if (Optimizations.UseSse2 && op.Size > 0 + && op.Size < 3) + { + Type[] typesSra = new Type[] { VectorIntTypesPerSizeLog2[op.Size], typeof(byte) }; + Type[] typesAdd = new Type[] { VectorIntTypesPerSizeLog2[op.Size], VectorIntTypesPerSizeLog2[op.Size] }; + + EmitLdvecWithSignedCast(context, op.Rd, op.Size); + EmitLdvecWithSignedCast(context, op.Rn, op.Size); + + context.EmitLdc_I4(GetImmShr(op)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightArithmetic), typesSra)); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), typesAdd)); + + EmitStvecWithSignedCast(context, op.Rd, op.Size); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + else + { + EmitVectorShrImmOpSx(context, ShrImmFlags.Accumulate); + } + } + + public static void Uqrshrn_S(ILEmitterCtx context) + { + EmitRoundShrImmSaturatingNarrowOp(context, ShrImmSaturatingNarrowFlags.ScalarZxZx); + } + + public static void Uqrshrn_V(ILEmitterCtx context) + { + EmitRoundShrImmSaturatingNarrowOp(context, ShrImmSaturatingNarrowFlags.VectorZxZx); + } + + public static void Uqshrn_S(ILEmitterCtx context) + { + EmitShrImmSaturatingNarrowOp(context, ShrImmSaturatingNarrowFlags.ScalarZxZx); + } + + public static void Uqshrn_V(ILEmitterCtx context) + { + EmitShrImmSaturatingNarrowOp(context, ShrImmSaturatingNarrowFlags.VectorZxZx); + } + + public static void Urshr_S(ILEmitterCtx context) + { + EmitScalarShrImmOpZx(context, ShrImmFlags.Round); + } + + public static void Urshr_V(ILEmitterCtx context) + { + OpCodeSimdShImm64 op = (OpCodeSimdShImm64)context.CurrOp; + + if (Optimizations.UseSse2 && op.Size > 0) + { + Type[] typesShs = new Type[] { VectorUIntTypesPerSizeLog2[op.Size], typeof(byte) }; + Type[] typesAdd = new Type[] { VectorUIntTypesPerSizeLog2[op.Size], VectorUIntTypesPerSizeLog2[op.Size] }; + + int shift = GetImmShr(op); + int eSize = 8 << op.Size; + + EmitLdvecWithUnsignedCast(context, op.Rn, op.Size); + + context.Emit(OpCodes.Dup); + context.EmitStvectmp(); + + context.EmitLdc_I4(eSize - shift); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftLeftLogical), typesShs)); + + context.EmitLdc_I4(eSize - 1); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical), typesShs)); + + context.EmitLdvectmp(); + + context.EmitLdc_I4(shift); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical), typesShs)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), typesAdd)); + + EmitStvecWithUnsignedCast(context, op.Rd, op.Size); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + else + { + EmitVectorShrImmOpZx(context, ShrImmFlags.Round); + } + } + + public static void Ursra_S(ILEmitterCtx context) + { + EmitScalarShrImmOpZx(context, ShrImmFlags.Round | ShrImmFlags.Accumulate); + } + + public static void Ursra_V(ILEmitterCtx context) + { + OpCodeSimdShImm64 op = (OpCodeSimdShImm64)context.CurrOp; + + if (Optimizations.UseSse2 && op.Size > 0) + { + Type[] typesShs = new Type[] { VectorUIntTypesPerSizeLog2[op.Size], typeof(byte) }; + Type[] typesAdd = new Type[] { VectorUIntTypesPerSizeLog2[op.Size], VectorUIntTypesPerSizeLog2[op.Size] }; + + int shift = GetImmShr(op); + int eSize = 8 << op.Size; + + EmitLdvecWithUnsignedCast(context, op.Rd, op.Size); + EmitLdvecWithUnsignedCast(context, op.Rn, op.Size); + + context.Emit(OpCodes.Dup); + context.EmitStvectmp(); + + context.EmitLdc_I4(eSize - shift); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftLeftLogical), typesShs)); + + context.EmitLdc_I4(eSize - 1); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical), typesShs)); + + context.EmitLdvectmp(); + + context.EmitLdc_I4(shift); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical), typesShs)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), typesAdd)); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), typesAdd)); + + EmitStvecWithUnsignedCast(context, op.Rd, op.Size); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + else + { + EmitVectorShrImmOpZx(context, ShrImmFlags.Round | ShrImmFlags.Accumulate); + } + } + + public static void Ushl_V(ILEmitterCtx context) + { + EmitVectorShl(context, signed: false); + } + + public static void Ushll_V(ILEmitterCtx context) + { + OpCodeSimdShImm64 op = (OpCodeSimdShImm64)context.CurrOp; + + EmitVectorShImmWidenBinaryZx(context, () => context.Emit(OpCodes.Shl), GetImmShl(op)); + } + + public static void Ushr_S(ILEmitterCtx context) + { + EmitShrImmOp(context, ShrImmFlags.ScalarZx); + } + + public static void Ushr_V(ILEmitterCtx context) + { + OpCodeSimdShImm64 op = (OpCodeSimdShImm64)context.CurrOp; + + if (Optimizations.UseSse2 && op.Size > 0) + { + Type[] typesSrl = new Type[] { VectorUIntTypesPerSizeLog2[op.Size], typeof(byte) }; + + EmitLdvecWithUnsignedCast(context, op.Rn, op.Size); + + context.EmitLdc_I4(GetImmShr(op)); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical), typesSrl)); + + EmitStvecWithUnsignedCast(context, op.Rd, op.Size); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + else + { + EmitShrImmOp(context, ShrImmFlags.VectorZx); + } + } + + public static void Usra_S(ILEmitterCtx context) + { + EmitScalarShrImmOpZx(context, ShrImmFlags.Accumulate); + } + + public static void Usra_V(ILEmitterCtx context) + { + OpCodeSimdShImm64 op = (OpCodeSimdShImm64)context.CurrOp; + + if (Optimizations.UseSse2 && op.Size > 0) + { + Type[] typesSrl = new Type[] { VectorUIntTypesPerSizeLog2[op.Size], typeof(byte) }; + Type[] typesAdd = new Type[] { VectorUIntTypesPerSizeLog2[op.Size], VectorUIntTypesPerSizeLog2[op.Size] }; + + EmitLdvecWithUnsignedCast(context, op.Rd, op.Size); + EmitLdvecWithUnsignedCast(context, op.Rn, op.Size); + + context.EmitLdc_I4(GetImmShr(op)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical), typesSrl)); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), typesAdd)); + + EmitStvecWithUnsignedCast(context, op.Rd, op.Size); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + else + { + EmitVectorShrImmOpZx(context, ShrImmFlags.Accumulate); + } + } + + private static void EmitVectorShl(ILEmitterCtx 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. + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + int maxShift = 8 << op.Size; + + Action emit = () => + { + ILLabel lblShl = new ILLabel(); + ILLabel lblZero = new ILLabel(); + ILLabel lblEnd = new ILLabel(); + + 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); + } + } + + [Flags] + private enum ShrImmFlags + { + Scalar = 1 << 0, + Signed = 1 << 1, + + Round = 1 << 2, + Accumulate = 1 << 3, + + ScalarSx = Scalar | Signed, + ScalarZx = Scalar, + + VectorSx = Signed, + VectorZx = 0 + } + + private static void EmitScalarShrImmOpSx(ILEmitterCtx context, ShrImmFlags flags) + { + EmitShrImmOp(context, ShrImmFlags.ScalarSx | flags); + } + + private static void EmitScalarShrImmOpZx(ILEmitterCtx context, ShrImmFlags flags) + { + EmitShrImmOp(context, ShrImmFlags.ScalarZx | flags); + } + + private static void EmitVectorShrImmOpSx(ILEmitterCtx context, ShrImmFlags flags) + { + EmitShrImmOp(context, ShrImmFlags.VectorSx | flags); + } + + private static void EmitVectorShrImmOpZx(ILEmitterCtx context, ShrImmFlags flags) + { + EmitShrImmOp(context, ShrImmFlags.VectorZx | flags); + } + + private static void EmitShrImmOp(ILEmitterCtx context, ShrImmFlags flags) + { + OpCodeSimdShImm64 op = (OpCodeSimdShImm64)context.CurrOp; + + bool scalar = (flags & ShrImmFlags.Scalar) != 0; + bool signed = (flags & ShrImmFlags.Signed) != 0; + bool round = (flags & ShrImmFlags.Round) != 0; + bool accumulate = (flags & ShrImmFlags.Accumulate) != 0; + + int shift = GetImmShr(op); + + long roundConst = 1L << (shift - 1); + + int bytes = op.GetBitsCount() >> 3; + int elems = !scalar ? bytes >> op.Size : 1; + + for (int index = 0; index < elems; index++) + { + EmitVectorExtract(context, op.Rn, index, op.Size, signed); + + if (op.Size <= 2) + { + if (round) + { + context.EmitLdc_I8(roundConst); + + context.Emit(OpCodes.Add); + } + + context.EmitLdc_I4(shift); + + context.Emit(signed ? OpCodes.Shr : OpCodes.Shr_Un); + } + else /* if (Op.Size == 3) */ + { + EmitShrImm_64(context, signed, round ? roundConst : 0L, shift); + } + + if (accumulate) + { + EmitVectorExtract(context, op.Rd, index, op.Size, signed); + + context.Emit(OpCodes.Add); + } + + EmitVectorInsertTmp(context, index, op.Size); + } + + context.EmitLdvectmp(); + context.EmitStvec(op.Rd); + + if ((op.RegisterSize == RegisterSize.Simd64) || scalar) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + private static void EmitVectorShrImmNarrowOpZx(ILEmitterCtx context, bool round) + { + OpCodeSimdShImm64 op = (OpCodeSimdShImm64)context.CurrOp; + + int shift = GetImmShr(op); + + long roundConst = 1L << (shift - 1); + + int elems = 8 >> op.Size; + + int part = op.RegisterSize == RegisterSize.Simd128 ? elems : 0; + + if (part != 0) + { + context.EmitLdvec(op.Rd); + context.EmitStvectmp(); + } + + for (int index = 0; index < elems; index++) + { + EmitVectorExtractZx(context, op.Rn, index, op.Size + 1); + + if (round) + { + context.EmitLdc_I8(roundConst); + + context.Emit(OpCodes.Add); + } + + context.EmitLdc_I4(shift); + + context.Emit(OpCodes.Shr_Un); + + EmitVectorInsertTmp(context, part + index, op.Size); + } + + context.EmitLdvectmp(); + context.EmitStvec(op.Rd); + + if (part == 0) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + [Flags] + private enum ShrImmSaturatingNarrowFlags + { + Scalar = 1 << 0, + SignedSrc = 1 << 1, + SignedDst = 1 << 2, + + Round = 1 << 3, + + ScalarSxSx = Scalar | SignedSrc | SignedDst, + ScalarSxZx = Scalar | SignedSrc, + ScalarZxZx = Scalar, + + VectorSxSx = SignedSrc | SignedDst, + VectorSxZx = SignedSrc, + VectorZxZx = 0 + } + + private static void EmitRoundShrImmSaturatingNarrowOp(ILEmitterCtx context, ShrImmSaturatingNarrowFlags flags) + { + EmitShrImmSaturatingNarrowOp(context, ShrImmSaturatingNarrowFlags.Round | flags); + } + + private static void EmitShrImmSaturatingNarrowOp(ILEmitterCtx context, ShrImmSaturatingNarrowFlags flags) + { + OpCodeSimdShImm64 op = (OpCodeSimdShImm64)context.CurrOp; + + bool scalar = (flags & ShrImmSaturatingNarrowFlags.Scalar) != 0; + bool signedSrc = (flags & ShrImmSaturatingNarrowFlags.SignedSrc) != 0; + bool signedDst = (flags & ShrImmSaturatingNarrowFlags.SignedDst) != 0; + bool round = (flags & ShrImmSaturatingNarrowFlags.Round) != 0; + + int shift = GetImmShr(op); + + long roundConst = 1L << (shift - 1); + + int elems = !scalar ? 8 >> op.Size : 1; + + int part = !scalar && (op.RegisterSize == RegisterSize.Simd128) ? elems : 0; + + if (scalar) + { + EmitVectorZeroLowerTmp(context); + } + + if (part != 0) + { + context.EmitLdvec(op.Rd); + context.EmitStvectmp(); + } + + for (int index = 0; index < elems; index++) + { + EmitVectorExtract(context, op.Rn, index, op.Size + 1, signedSrc); + + if (op.Size <= 1 || !round) + { + if (round) + { + context.EmitLdc_I8(roundConst); + + context.Emit(OpCodes.Add); + } + + context.EmitLdc_I4(shift); + + context.Emit(signedSrc ? OpCodes.Shr : OpCodes.Shr_Un); + } + else /* if (Op.Size == 2 && Round) */ + { + EmitShrImm_64(context, signedSrc, roundConst, shift); // Shift <= 32 + } + + EmitSatQ(context, op.Size, signedSrc, signedDst); + + EmitVectorInsertTmp(context, part + index, op.Size); + } + + context.EmitLdvectmp(); + context.EmitStvec(op.Rd); + + if (part == 0) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + // Dst_64 = (Int(Src_64, Signed) + RoundConst) >> Shift; + private static void EmitShrImm_64( + ILEmitterCtx context, + bool signed, + long roundConst, + int shift) + { + context.EmitLdc_I8(roundConst); + context.EmitLdc_I4(shift); + + SoftFallback.EmitCall(context, signed + ? nameof(SoftFallback.SignedShrImm_64) + : nameof(SoftFallback.UnsignedShrImm_64)); + } + + private static void EmitVectorShImmWidenBinarySx(ILEmitterCtx context, Action emit, int imm) + { + EmitVectorShImmWidenBinaryOp(context, emit, imm, true); + } + + private static void EmitVectorShImmWidenBinaryZx(ILEmitterCtx context, Action emit, int imm) + { + EmitVectorShImmWidenBinaryOp(context, emit, imm, false); + } + + private static void EmitVectorShImmWidenBinaryOp(ILEmitterCtx context, Action emit, int imm, bool signed) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + int elems = 8 >> op.Size; + + int part = op.RegisterSize == RegisterSize.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); + } + } +} diff --git a/Instructions/InstEmitSystem.cs b/Instructions/InstEmitSystem.cs new file mode 100644 index 0000000..0e61d5b --- /dev/null +++ b/Instructions/InstEmitSystem.cs @@ -0,0 +1,138 @@ +using ChocolArm64.Decoders; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System; +using System.Reflection; +using System.Reflection.Emit; + +namespace ChocolArm64.Instructions +{ + static partial class InstEmit + { + public static void Hint(ILEmitterCtx context) + { + //Execute as no-op. + } + + public static void Isb(ILEmitterCtx context) + { + //Execute as no-op. + } + + public static void Mrs(ILEmitterCtx context) + { + OpCodeSystem64 op = (OpCodeSystem64)context.CurrOp; + + context.EmitLdarg(TranslatedSub.StateArgIdx); + + string propName; + + switch (GetPackedId(op)) + { + case 0b11_011_0000_0000_001: propName = nameof(CpuThreadState.CtrEl0); break; + case 0b11_011_0000_0000_111: propName = nameof(CpuThreadState.DczidEl0); break; + case 0b11_011_0100_0100_000: propName = nameof(CpuThreadState.Fpcr); break; + case 0b11_011_0100_0100_001: propName = nameof(CpuThreadState.Fpsr); break; + case 0b11_011_1101_0000_010: propName = nameof(CpuThreadState.TpidrEl0); break; + case 0b11_011_1101_0000_011: propName = nameof(CpuThreadState.Tpidr); break; + case 0b11_011_1110_0000_000: propName = nameof(CpuThreadState.CntfrqEl0); break; + case 0b11_011_1110_0000_001: propName = nameof(CpuThreadState.CntpctEl0); break; + + default: throw new NotImplementedException($"Unknown MRS at {op.Position:x16}"); + } + + context.EmitCallPropGet(typeof(CpuThreadState), propName); + + PropertyInfo propInfo = typeof(CpuThreadState).GetProperty(propName); + + if (propInfo.PropertyType != typeof(long) && + propInfo.PropertyType != typeof(ulong)) + { + context.Emit(OpCodes.Conv_U8); + } + + context.EmitStintzr(op.Rt); + } + + public static void Msr(ILEmitterCtx context) + { + OpCodeSystem64 op = (OpCodeSystem64)context.CurrOp; + + context.EmitLdarg(TranslatedSub.StateArgIdx); + context.EmitLdintzr(op.Rt); + + string propName; + + switch (GetPackedId(op)) + { + case 0b11_011_0100_0100_000: propName = nameof(CpuThreadState.Fpcr); break; + case 0b11_011_0100_0100_001: propName = nameof(CpuThreadState.Fpsr); break; + case 0b11_011_1101_0000_010: propName = nameof(CpuThreadState.TpidrEl0); break; + + default: throw new NotImplementedException($"Unknown MSR at {op.Position:x16}"); + } + + PropertyInfo propInfo = typeof(CpuThreadState).GetProperty(propName); + + if (propInfo.PropertyType != typeof(long) && + propInfo.PropertyType != typeof(ulong)) + { + context.Emit(OpCodes.Conv_U4); + } + + context.EmitCallPropSet(typeof(CpuThreadState), propName); + } + + public static void Nop(ILEmitterCtx context) + { + //Do nothing. + } + + public static void Sys(ILEmitterCtx 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. + OpCodeSystem64 op = (OpCodeSystem64)context.CurrOp; + + switch (GetPackedId(op)) + { + case 0b11_011_0111_0100_001: + { + //DC ZVA + for (int offs = 0; offs < (4 << CpuThreadState.DczSizeLog2); offs += 8) + { + context.EmitLdarg(TranslatedSub.MemoryArgIdx); + context.EmitLdintzr(op.Rt); + context.EmitLdc_I(offs); + + context.Emit(OpCodes.Add); + + context.EmitLdc_I8(0); + + InstEmitMemoryHelper.EmitWriteCall(context, 3); + } + + break; + } + + //No-op + case 0b11_011_0111_1110_001: //DC CIVAC + break; + } + } + + private static int GetPackedId(OpCodeSystem64 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; + } + } +} diff --git a/Instructions/InstEmitter.cs b/Instructions/InstEmitter.cs new file mode 100644 index 0000000..db6e860 --- /dev/null +++ b/Instructions/InstEmitter.cs @@ -0,0 +1,6 @@ +using ChocolArm64.Translation; + +namespace ChocolArm64.Instructions +{ + delegate void InstEmitter(ILEmitterCtx context); +} \ No newline at end of file diff --git a/Instructions/InstInterpreter.cs b/Instructions/InstInterpreter.cs new file mode 100644 index 0000000..e6354fd --- /dev/null +++ b/Instructions/InstInterpreter.cs @@ -0,0 +1,8 @@ +using ChocolArm64.Decoders; +using ChocolArm64.Memory; +using ChocolArm64.State; + +namespace ChocolArm64.Instructions +{ + delegate void InstInterpreter(CpuThreadState state, MemoryManager memory, OpCode64 opCode); +} \ No newline at end of file diff --git a/Instructions/SoftFallback.cs b/Instructions/SoftFallback.cs new file mode 100644 index 0000000..a31aa34 --- /dev/null +++ b/Instructions/SoftFallback.cs @@ -0,0 +1,922 @@ +using ChocolArm64.State; +using ChocolArm64.Translation; +using System; +using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; + +namespace ChocolArm64.Instructions +{ + using static VectorHelper; + + static class SoftFallback + { + public static void EmitCall(ILEmitterCtx context, string mthdName) + { + context.EmitCall(typeof(SoftFallback), mthdName); + } + +#region "ShrImm_64" + public static long SignedShrImm_64(long value, long roundConst, int shift) + { + if (roundConst == 0L) + { + if (shift <= 63) + { + return value >> shift; + } + else /* if (Shift == 64) */ + { + if (value < 0L) + { + return -1L; + } + else + { + return 0L; + } + } + } + else /* if (RoundConst == 1L << (Shift - 1)) */ + { + if (shift <= 63) + { + long add = value + roundConst; + + if ((~value & (value ^ add)) < 0L) + { + return (long)((ulong)add >> shift); + } + else + { + return add >> shift; + } + } + else /* if (Shift == 64) */ + { + return 0L; + } + } + } + + public static ulong UnsignedShrImm_64(ulong value, long roundConst, int shift) + { + if (roundConst == 0L) + { + if (shift <= 63) + { + return value >> shift; + } + else /* if (Shift == 64) */ + { + return 0UL; + } + } + else /* if (RoundConst == 1L << (Shift - 1)) */ + { + ulong add = value + (ulong)roundConst; + + if ((add < value) && (add < (ulong)roundConst)) + { + if (shift <= 63) + { + return (add >> shift) | (0x8000000000000000UL >> (shift - 1)); + } + else /* if (Shift == 64) */ + { + return 1UL; + } + } + else + { + if (shift <= 63) + { + return add >> shift; + } + else /* if (Shift == 64) */ + { + return 0UL; + } + } + } + } +#endregion + +#region "Saturating" + public static long SignedSrcSignedDstSatQ(long op, int size, CpuThreadState state) + { + int eSize = 8 << size; + + long tMaxValue = (1L << (eSize - 1)) - 1L; + long tMinValue = -(1L << (eSize - 1)); + + if (op > tMaxValue) + { + state.SetFpsrFlag(Fpsr.Qc); + + return tMaxValue; + } + else if (op < tMinValue) + { + state.SetFpsrFlag(Fpsr.Qc); + + return tMinValue; + } + else + { + return op; + } + } + + public static ulong SignedSrcUnsignedDstSatQ(long op, int size, CpuThreadState state) + { + int eSize = 8 << size; + + ulong tMaxValue = (1UL << eSize) - 1UL; + ulong tMinValue = 0UL; + + if (op > (long)tMaxValue) + { + state.SetFpsrFlag(Fpsr.Qc); + + return tMaxValue; + } + else if (op < (long)tMinValue) + { + state.SetFpsrFlag(Fpsr.Qc); + + return tMinValue; + } + else + { + return (ulong)op; + } + } + + public static long UnsignedSrcSignedDstSatQ(ulong op, int size, CpuThreadState state) + { + int eSize = 8 << size; + + long tMaxValue = (1L << (eSize - 1)) - 1L; + + if (op > (ulong)tMaxValue) + { + state.SetFpsrFlag(Fpsr.Qc); + + return tMaxValue; + } + else + { + return (long)op; + } + } + + public static ulong UnsignedSrcUnsignedDstSatQ(ulong op, int size, CpuThreadState state) + { + int eSize = 8 << size; + + ulong tMaxValue = (1UL << eSize) - 1UL; + + if (op > tMaxValue) + { + state.SetFpsrFlag(Fpsr.Qc); + + return tMaxValue; + } + else + { + return op; + } + } + + public static long UnarySignedSatQAbsOrNeg(long op, CpuThreadState state) + { + if (op == long.MinValue) + { + state.SetFpsrFlag(Fpsr.Qc); + + return long.MaxValue; + } + else + { + return op; + } + } + + public static long BinarySignedSatQAdd(long op1, long op2, CpuThreadState state) + { + long add = op1 + op2; + + if ((~(op1 ^ op2) & (op1 ^ add)) < 0L) + { + state.SetFpsrFlag(Fpsr.Qc); + + if (op1 < 0L) + { + return long.MinValue; + } + else + { + return long.MaxValue; + } + } + else + { + return add; + } + } + + public static ulong BinaryUnsignedSatQAdd(ulong op1, ulong op2, CpuThreadState state) + { + ulong add = op1 + op2; + + if ((add < op1) && (add < op2)) + { + state.SetFpsrFlag(Fpsr.Qc); + + return ulong.MaxValue; + } + else + { + return add; + } + } + + public static long BinarySignedSatQSub(long op1, long op2, CpuThreadState state) + { + long sub = op1 - op2; + + if (((op1 ^ op2) & (op1 ^ sub)) < 0L) + { + state.SetFpsrFlag(Fpsr.Qc); + + if (op1 < 0L) + { + return long.MinValue; + } + else + { + return long.MaxValue; + } + } + else + { + return sub; + } + } + + public static ulong BinaryUnsignedSatQSub(ulong op1, ulong op2, CpuThreadState state) + { + ulong sub = op1 - op2; + + if (op1 < op2) + { + state.SetFpsrFlag(Fpsr.Qc); + + return ulong.MinValue; + } + else + { + return sub; + } + } + + public static long BinarySignedSatQAcc(ulong op1, long op2, CpuThreadState state) + { + if (op1 <= (ulong)long.MaxValue) + { + // Op1 from ulong.MinValue to (ulong)long.MaxValue + // Op2 from long.MinValue to long.MaxValue + + long add = (long)op1 + op2; + + if ((~op2 & add) < 0L) + { + state.SetFpsrFlag(Fpsr.Qc); + + return long.MaxValue; + } + else + { + return add; + } + } + else if (op2 >= 0L) + { + // Op1 from (ulong)long.MaxValue + 1UL to ulong.MaxValue + // Op2 from (long)ulong.MinValue to long.MaxValue + + state.SetFpsrFlag(Fpsr.Qc); + + return long.MaxValue; + } + else + { + // Op1 from (ulong)long.MaxValue + 1UL to ulong.MaxValue + // Op2 from long.MinValue to (long)ulong.MinValue - 1L + + ulong add = op1 + (ulong)op2; + + if (add > (ulong)long.MaxValue) + { + state.SetFpsrFlag(Fpsr.Qc); + + return long.MaxValue; + } + else + { + return (long)add; + } + } + } + + public static ulong BinaryUnsignedSatQAcc(long op1, ulong op2, CpuThreadState state) + { + if (op1 >= 0L) + { + // Op1 from (long)ulong.MinValue to long.MaxValue + // Op2 from ulong.MinValue to ulong.MaxValue + + ulong add = (ulong)op1 + op2; + + if ((add < (ulong)op1) && (add < op2)) + { + state.SetFpsrFlag(Fpsr.Qc); + + return ulong.MaxValue; + } + else + { + return add; + } + } + else if (op2 > (ulong)long.MaxValue) + { + // Op1 from long.MinValue to (long)ulong.MinValue - 1L + // Op2 from (ulong)long.MaxValue + 1UL to ulong.MaxValue + + return (ulong)op1 + op2; + } + else + { + // Op1 from long.MinValue to (long)ulong.MinValue - 1L + // Op2 from ulong.MinValue to (ulong)long.MaxValue + + long add = op1 + (long)op2; + + if (add < (long)ulong.MinValue) + { + state.SetFpsrFlag(Fpsr.Qc); + + return ulong.MinValue; + } + else + { + return (ulong)add; + } + } + } +#endregion + +#region "Count" + public static ulong CountLeadingSigns(ulong value, int size) // Size is 8, 16, 32 or 64 (SIMD&FP or Base Inst.). + { + value ^= value >> 1; + + int highBit = size - 2; + + for (int bit = highBit; bit >= 0; bit--) + { + if (((value >> bit) & 0b1) != 0) + { + return (ulong)(highBit - bit); + } + } + + return (ulong)(size - 1); + } + + private static readonly byte[] ClzNibbleTbl = { 4, 3, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 }; + + public static ulong CountLeadingZeros(ulong value, int size) // Size is 8, 16, 32 or 64 (SIMD&FP or Base Inst.). + { + if (value == 0ul) + { + return (ulong)size; + } + + int nibbleIdx = size; + int preCount, count = 0; + + do + { + nibbleIdx -= 4; + preCount = ClzNibbleTbl[(value >> nibbleIdx) & 0b1111]; + count += preCount; + } + while (preCount == 4); + + return (ulong)count; + } + + public static ulong CountSetBits8(ulong value) // "Size" is 8 (SIMD&FP Inst.). + { + if (value == 0xfful) + { + return 8ul; + } + + value = ((value >> 1) & 0x55ul) + (value & 0x55ul); + value = ((value >> 2) & 0x33ul) + (value & 0x33ul); + + return (value >> 4) + (value & 0x0ful); + } +#endregion + +#region "Crc32" + private const uint Crc32RevPoly = 0xedb88320; + private const uint Crc32CRevPoly = 0x82f63b78; + + public static uint Crc32B(uint crc, byte val) => Crc32 (crc, Crc32RevPoly, val); + public static uint Crc32H(uint crc, ushort val) => Crc32H(crc, Crc32RevPoly, val); + public static uint Crc32W(uint crc, uint val) => Crc32W(crc, Crc32RevPoly, val); + public static uint Crc32X(uint crc, ulong val) => Crc32X(crc, Crc32RevPoly, val); + + public static uint Crc32Cb(uint crc, byte val) => Crc32 (crc, Crc32CRevPoly, val); + public static uint Crc32Ch(uint crc, ushort val) => Crc32H(crc, Crc32CRevPoly, val); + public static uint Crc32Cw(uint crc, uint val) => Crc32W(crc, Crc32CRevPoly, val); + public static uint Crc32Cx(uint crc, ulong val) => Crc32X(crc, Crc32CRevPoly, val); + + private static uint Crc32H(uint crc, uint poly, ushort val) + { + crc = Crc32(crc, poly, (byte)(val >> 0)); + crc = Crc32(crc, poly, (byte)(val >> 8)); + + return crc; + } + + private static uint Crc32W(uint crc, uint poly, uint val) + { + crc = Crc32(crc, poly, (byte)(val >> 0 )); + crc = Crc32(crc, poly, (byte)(val >> 8 )); + crc = Crc32(crc, poly, (byte)(val >> 16)); + crc = Crc32(crc, poly, (byte)(val >> 24)); + + return crc; + } + + private static uint Crc32X(uint crc, uint poly, ulong val) + { + crc = Crc32(crc, poly, (byte)(val >> 0 )); + crc = Crc32(crc, poly, (byte)(val >> 8 )); + crc = Crc32(crc, poly, (byte)(val >> 16)); + crc = Crc32(crc, poly, (byte)(val >> 24)); + crc = Crc32(crc, poly, (byte)(val >> 32)); + crc = Crc32(crc, poly, (byte)(val >> 40)); + crc = Crc32(crc, poly, (byte)(val >> 48)); + crc = Crc32(crc, poly, (byte)(val >> 56)); + + return crc; + } + + private static uint Crc32(uint crc, uint poly, byte val) + { + crc ^= val; + + for (int bit = 7; bit >= 0; bit--) + { + uint mask = (uint)(-(int)(crc & 1)); + + crc = (crc >> 1) ^ (poly & mask); + } + + return crc; + } +#endregion + +#region "Aes" + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 Decrypt(Vector128 value, Vector128 roundKey) + { + if (!Sse.IsSupported) + { + throw new PlatformNotSupportedException(); + } + + return CryptoHelper.AesInvSubBytes(CryptoHelper.AesInvShiftRows(Sse.Xor(value, roundKey))); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 Encrypt(Vector128 value, Vector128 roundKey) + { + if (!Sse.IsSupported) + { + throw new PlatformNotSupportedException(); + } + + return CryptoHelper.AesSubBytes(CryptoHelper.AesShiftRows(Sse.Xor(value, roundKey))); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 InverseMixColumns(Vector128 value) + { + return CryptoHelper.AesInvMixColumns(value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 MixColumns(Vector128 value) + { + return CryptoHelper.AesMixColumns(value); + } +#endregion + +#region "Sha1" + public static Vector128 HashChoose(Vector128 hashAbcd, uint hashE, Vector128 wk) + { + for (int e = 0; e <= 3; e++) + { + uint t = ShaChoose((uint)VectorExtractIntZx(hashAbcd, (byte)1, 2), + (uint)VectorExtractIntZx(hashAbcd, (byte)2, 2), + (uint)VectorExtractIntZx(hashAbcd, (byte)3, 2)); + + hashE += Rol((uint)VectorExtractIntZx(hashAbcd, (byte)0, 2), 5) + t; + hashE += (uint)VectorExtractIntZx(wk, (byte)e, 2); + + t = Rol((uint)VectorExtractIntZx(hashAbcd, (byte)1, 2), 30); + hashAbcd = VectorInsertInt((ulong)t, hashAbcd, (byte)1, 2); + + Rol32_160(ref hashE, ref hashAbcd); + } + + return hashAbcd; + } + + public static uint FixedRotate(uint hashE) + { + return hashE.Rol(30); + } + + public static Vector128 HashMajority(Vector128 hashAbcd, uint hashE, Vector128 wk) + { + for (int e = 0; e <= 3; e++) + { + uint t = ShaMajority((uint)VectorExtractIntZx(hashAbcd, (byte)1, 2), + (uint)VectorExtractIntZx(hashAbcd, (byte)2, 2), + (uint)VectorExtractIntZx(hashAbcd, (byte)3, 2)); + + hashE += Rol((uint)VectorExtractIntZx(hashAbcd, (byte)0, 2), 5) + t; + hashE += (uint)VectorExtractIntZx(wk, (byte)e, 2); + + t = Rol((uint)VectorExtractIntZx(hashAbcd, (byte)1, 2), 30); + hashAbcd = VectorInsertInt((ulong)t, hashAbcd, (byte)1, 2); + + Rol32_160(ref hashE, ref hashAbcd); + } + + return hashAbcd; + } + + public static Vector128 HashParity(Vector128 hashAbcd, uint hashE, Vector128 wk) + { + for (int e = 0; e <= 3; e++) + { + uint t = ShaParity((uint)VectorExtractIntZx(hashAbcd, (byte)1, 2), + (uint)VectorExtractIntZx(hashAbcd, (byte)2, 2), + (uint)VectorExtractIntZx(hashAbcd, (byte)3, 2)); + + hashE += Rol((uint)VectorExtractIntZx(hashAbcd, (byte)0, 2), 5) + t; + hashE += (uint)VectorExtractIntZx(wk, (byte)e, 2); + + t = Rol((uint)VectorExtractIntZx(hashAbcd, (byte)1, 2), 30); + hashAbcd = VectorInsertInt((ulong)t, hashAbcd, (byte)1, 2); + + Rol32_160(ref hashE, ref hashAbcd); + } + + return hashAbcd; + } + + public static Vector128 Sha1SchedulePart1(Vector128 w03, Vector128 w47, Vector128 w811) + { + if (!Sse.IsSupported) + { + throw new PlatformNotSupportedException(); + } + + Vector128 result = new Vector128(); + + ulong t2 = VectorExtractIntZx(w47, (byte)0, 3); + ulong t1 = VectorExtractIntZx(w03, (byte)1, 3); + + result = VectorInsertInt((ulong)t1, result, (byte)0, 3); + result = VectorInsertInt((ulong)t2, result, (byte)1, 3); + + return Sse.Xor(result, Sse.Xor(w03, w811)); + } + + public static Vector128 Sha1SchedulePart2(Vector128 tw03, Vector128 w1215) + { + if (!Sse2.IsSupported) + { + throw new PlatformNotSupportedException(); + } + + Vector128 result = new Vector128(); + + Vector128 t = Sse.Xor(tw03, Sse.StaticCast( + Sse2.ShiftRightLogical128BitLane(Sse.StaticCast(w1215), (byte)4))); + + uint tE0 = (uint)VectorExtractIntZx(t, (byte)0, 2); + uint tE1 = (uint)VectorExtractIntZx(t, (byte)1, 2); + uint tE2 = (uint)VectorExtractIntZx(t, (byte)2, 2); + uint tE3 = (uint)VectorExtractIntZx(t, (byte)3, 2); + + result = VectorInsertInt((ulong)tE0.Rol(1), result, (byte)0, 2); + result = VectorInsertInt((ulong)tE1.Rol(1), result, (byte)1, 2); + result = VectorInsertInt((ulong)tE2.Rol(1), result, (byte)2, 2); + + return VectorInsertInt((ulong)(tE3.Rol(1) ^ tE0.Rol(2)), result, (byte)3, 2); + } + + private static void Rol32_160(ref uint y, ref Vector128 x) + { + if (!Sse2.IsSupported) + { + throw new PlatformNotSupportedException(); + } + + uint xE3 = (uint)VectorExtractIntZx(x, (byte)3, 2); + + x = Sse.StaticCast(Sse2.ShiftLeftLogical128BitLane(Sse.StaticCast(x), (byte)4)); + x = VectorInsertInt((ulong)y, x, (byte)0, 2); + + y = xE3; + } + + private static uint ShaChoose(uint x, uint y, uint z) + { + return ((y ^ z) & x) ^ z; + } + + private static uint ShaMajority(uint x, uint y, uint z) + { + return (x & y) | ((x | y) & z); + } + + private static uint ShaParity(uint x, uint y, uint z) + { + return x ^ y ^ z; + } + + private static uint Rol(this uint value, int count) + { + return (value << count) | (value >> (32 - count)); + } +#endregion + +#region "Sha256" + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 HashLower(Vector128 hashAbcd, Vector128 hashEfgh, Vector128 wk) + { + return Sha256Hash(hashAbcd, hashEfgh, wk, true); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 HashUpper(Vector128 hashEfgh, Vector128 hashAbcd, Vector128 wk) + { + return Sha256Hash(hashAbcd, hashEfgh, wk, false); + } + + public static Vector128 Sha256SchedulePart1(Vector128 w03, Vector128 w47) + { + Vector128 result = new Vector128(); + + for (int e = 0; e <= 3; e++) + { + uint elt = (uint)VectorExtractIntZx(e <= 2 ? w03 : w47, (byte)(e <= 2 ? e + 1 : 0), 2); + + elt = elt.Ror(7) ^ elt.Ror(18) ^ elt.Lsr(3); + + elt += (uint)VectorExtractIntZx(w03, (byte)e, 2); + + result = VectorInsertInt((ulong)elt, result, (byte)e, 2); + } + + return result; + } + + public static Vector128 Sha256SchedulePart2(Vector128 w03, Vector128 w811, Vector128 w1215) + { + Vector128 result = new Vector128(); + + ulong t1 = VectorExtractIntZx(w1215, (byte)1, 3); + + for (int e = 0; e <= 1; e++) + { + uint elt = t1.ULongPart(e); + + elt = elt.Ror(17) ^ elt.Ror(19) ^ elt.Lsr(10); + + elt += (uint)VectorExtractIntZx(w03, (byte)e, 2); + elt += (uint)VectorExtractIntZx(w811, (byte)(e + 1), 2); + + result = VectorInsertInt((ulong)elt, result, (byte)e, 2); + } + + t1 = VectorExtractIntZx(result, (byte)0, 3); + + for (int e = 2; e <= 3; e++) + { + uint elt = t1.ULongPart(e - 2); + + elt = elt.Ror(17) ^ elt.Ror(19) ^ elt.Lsr(10); + + elt += (uint)VectorExtractIntZx(w03, (byte)e, 2); + elt += (uint)VectorExtractIntZx(e == 2 ? w811 : w1215, (byte)(e == 2 ? 3 : 0), 2); + + result = VectorInsertInt((ulong)elt, result, (byte)e, 2); + } + + return result; + } + + private static Vector128 Sha256Hash(Vector128 x, Vector128 y, Vector128 w, bool part1) + { + for (int e = 0; e <= 3; e++) + { + uint chs = ShaChoose((uint)VectorExtractIntZx(y, (byte)0, 2), + (uint)VectorExtractIntZx(y, (byte)1, 2), + (uint)VectorExtractIntZx(y, (byte)2, 2)); + + uint maj = ShaMajority((uint)VectorExtractIntZx(x, (byte)0, 2), + (uint)VectorExtractIntZx(x, (byte)1, 2), + (uint)VectorExtractIntZx(x, (byte)2, 2)); + + uint t1 = (uint)VectorExtractIntZx(y, (byte)3, 2); + t1 += ShaHashSigma1((uint)VectorExtractIntZx(y, (byte)0, 2)) + chs; + t1 += (uint)VectorExtractIntZx(w, (byte)e, 2); + + uint t2 = t1 + (uint)VectorExtractIntZx(x, (byte)3, 2); + x = VectorInsertInt((ulong)t2, x, (byte)3, 2); + t2 = t1 + ShaHashSigma0((uint)VectorExtractIntZx(x, (byte)0, 2)) + maj; + y = VectorInsertInt((ulong)t2, y, (byte)3, 2); + + Rol32_256(ref y, ref x); + } + + return part1 ? x : y; + } + + private static void Rol32_256(ref Vector128 y, ref Vector128 x) + { + if (!Sse2.IsSupported) + { + throw new PlatformNotSupportedException(); + } + + uint yE3 = (uint)VectorExtractIntZx(y, (byte)3, 2); + uint xE3 = (uint)VectorExtractIntZx(x, (byte)3, 2); + + y = Sse.StaticCast(Sse2.ShiftLeftLogical128BitLane(Sse.StaticCast(y), (byte)4)); + x = Sse.StaticCast(Sse2.ShiftLeftLogical128BitLane(Sse.StaticCast(x), (byte)4)); + + y = VectorInsertInt((ulong)xE3, y, (byte)0, 2); + x = VectorInsertInt((ulong)yE3, x, (byte)0, 2); + } + + private static uint ShaHashSigma0(uint x) + { + return x.Ror(2) ^ x.Ror(13) ^ x.Ror(22); + } + + private static uint ShaHashSigma1(uint x) + { + return x.Ror(6) ^ x.Ror(11) ^ x.Ror(25); + } + + private static uint Ror(this uint value, int count) + { + return (value >> count) | (value << (32 - count)); + } + + private static uint Lsr(this uint value, int count) + { + return value >> count; + } + + private static uint ULongPart(this ulong value, int part) + { + return part == 0 + ? (uint)(value & 0xFFFFFFFFUL) + : (uint)(value >> 32); + } +#endregion + +#region "Reverse" + public static uint ReverseBits8(uint value) + { + value = ((value & 0xaa) >> 1) | ((value & 0x55) << 1); + value = ((value & 0xcc) >> 2) | ((value & 0x33) << 2); + + return (value >> 4) | ((value & 0x0f) << 4); + } + + 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)); + } +#endregion + +#region "MultiplyHigh" + public static long SMulHi128(long left, long right) + { + long result = (long)UMulHi128((ulong)left, (ulong)right); + + if (left < 0) + { + result -= right; + } + + if (right < 0) + { + result -= left; + } + + return result; + } + + public static ulong UMulHi128(ulong left, ulong right) + { + ulong lHigh = left >> 32; + ulong lLow = left & 0xFFFFFFFF; + ulong rHigh = right >> 32; + ulong rLow = right & 0xFFFFFFFF; + + ulong z2 = lLow * rLow; + ulong t = lHigh * rLow + (z2 >> 32); + ulong z1 = t & 0xFFFFFFFF; + ulong z0 = t >> 32; + + z1 += lLow * rHigh; + + return lHigh * rHigh + z0 + (z1 >> 32); + } +#endregion + } +} diff --git a/Instructions/SoftFloat.cs b/Instructions/SoftFloat.cs new file mode 100644 index 0000000..79dbe95 --- /dev/null +++ b/Instructions/SoftFloat.cs @@ -0,0 +1,2127 @@ +using ChocolArm64.State; +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; + +namespace ChocolArm64.Instructions +{ + static class SoftFloat + { + static SoftFloat() + { + RecipEstimateTable = BuildRecipEstimateTable(); + InvSqrtEstimateTable = BuildInvSqrtEstimateTable(); + } + + private static readonly byte[] RecipEstimateTable; + private static readonly byte[] InvSqrtEstimateTable; + + private static byte[] BuildRecipEstimateTable() + { + byte[] table = new byte[256]; + for (ulong index = 0; index < 256; index++) + { + ulong a = index | 0x100; + + a = (a << 1) + 1; + ulong b = 0x80000 / a; + b = (b + 1) >> 1; + + table[index] = (byte)(b & 0xFF); + } + return table; + } + + private static byte[] BuildInvSqrtEstimateTable() + { + byte[] table = new byte[512]; + for (ulong index = 128; index < 512; index++) + { + ulong a = index; + if (a < 256) + { + a = (a << 1) + 1; + } + else + { + a = (a | 1) << 1; + } + + ulong b = 256; + while (a * (b + 1) * (b + 1) < (1ul << 28)) + { + b++; + } + b = (b + 1) >> 1; + + table[index] = (byte)(b & 0xFF); + } + return table; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float RecipEstimate(float x) + { + return (float)RecipEstimate((double)x); + } + + public static double RecipEstimate(double x) + { + ulong xBits = (ulong)BitConverter.DoubleToInt64Bits(x); + ulong xSign = xBits & 0x8000000000000000; + ulong xExp = (xBits >> 52) & 0x7FF; + ulong scaled = xBits & ((1ul << 52) - 1); + + if (xExp >= 2045) + { + if (xExp == 0x7ff && scaled != 0) + { + // NaN + return BitConverter.Int64BitsToDouble((long)(xBits | 0x0008000000000000)); + } + + // Infinity, or Out of range -> Zero + return BitConverter.Int64BitsToDouble((long)xSign); + } + + if (xExp == 0) + { + if (scaled == 0) + { + // Zero -> Infinity + return BitConverter.Int64BitsToDouble((long)(xSign | 0x7FF0000000000000)); + } + + // Denormal + if ((scaled & (1ul << 51)) == 0) + { + xExp = ~0ul; + scaled <<= 2; + } + else + { + scaled <<= 1; + } + } + + scaled >>= 44; + scaled &= 0xFF; + + ulong resultExp = (2045 - xExp) & 0x7FF; + ulong estimate = (ulong)RecipEstimateTable[scaled]; + ulong fraction = estimate << 44; + + if (resultExp == 0) + { + fraction >>= 1; + fraction |= 1ul << 51; + } + else if (resultExp == 0x7FF) + { + resultExp = 0; + fraction >>= 2; + fraction |= 1ul << 50; + } + + ulong result = xSign | (resultExp << 52) | fraction; + return BitConverter.Int64BitsToDouble((long)result); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float InvSqrtEstimate(float x) + { + return (float)InvSqrtEstimate((double)x); + } + + public static double InvSqrtEstimate(double x) + { + ulong xBits = (ulong)BitConverter.DoubleToInt64Bits(x); + ulong xSign = xBits & 0x8000000000000000; + long xExp = (long)((xBits >> 52) & 0x7FF); + ulong scaled = xBits & ((1ul << 52) - 1); + + if (xExp == 0x7FF && scaled != 0) + { + // NaN + return BitConverter.Int64BitsToDouble((long)(xBits | 0x0008000000000000)); + } + + if (xExp == 0) + { + if (scaled == 0) + { + // Zero -> Infinity + return BitConverter.Int64BitsToDouble((long)(xSign | 0x7FF0000000000000)); + } + + // Denormal + while ((scaled & (1 << 51)) == 0) + { + scaled <<= 1; + xExp--; + } + scaled <<= 1; + } + + if (xSign != 0) + { + // Negative -> NaN + return BitConverter.Int64BitsToDouble((long)0x7FF8000000000000); + } + + if (xExp == 0x7ff && scaled == 0) + { + // Infinity -> Zero + return BitConverter.Int64BitsToDouble((long)xSign); + } + + if (((ulong)xExp & 1) == 1) + { + scaled >>= 45; + scaled &= 0xFF; + scaled |= 0x80; + } + else + { + scaled >>= 44; + scaled &= 0xFF; + scaled |= 0x100; + } + + ulong resultExp = ((ulong)(3068 - xExp) / 2) & 0x7FF; + ulong estimate = (ulong)InvSqrtEstimateTable[scaled]; + ulong fraction = estimate << 44; + + ulong result = xSign | (resultExp << 52) | fraction; + return BitConverter.Int64BitsToDouble((long)result); + } + } + + static class SoftFloat1632 + { + public static float FPConvert(ushort valueBits, CpuThreadState state) + { + Debug.WriteLineIf(state.Fpcr != 0, $"ASoftFloat16_32.FPConvert: State.Fpcr = 0x{state.Fpcr:X8}"); + + double real = valueBits.FPUnpackCv(out FpType type, out bool sign, state); + + float result; + + if (type == FpType.SNaN || type == FpType.QNaN) + { + if (state.GetFpcrFlag(Fpcr.Dn)) + { + result = FPDefaultNaN(); + } + else + { + result = FPConvertNaN(valueBits); + } + + if (type == FpType.SNaN) + { + FPProcessException(FpExc.InvalidOp, state); + } + } + else if (type == FpType.Infinity) + { + result = FPInfinity(sign); + } + else if (type == FpType.Zero) + { + result = FPZero(sign); + } + else + { + result = FPRoundCv(real, state); + } + + return result; + } + + private static float FPDefaultNaN() + { + return -float.NaN; + } + + private static float FPInfinity(bool sign) + { + return sign ? float.NegativeInfinity : float.PositiveInfinity; + } + + private static float FPZero(bool sign) + { + return sign ? -0f : +0f; + } + + private static float FPMaxNormal(bool sign) + { + return sign ? float.MinValue : float.MaxValue; + } + + private static double FPUnpackCv(this ushort valueBits, out FpType type, out bool sign, CpuThreadState state) + { + sign = (~(uint)valueBits & 0x8000u) == 0u; + + uint exp16 = ((uint)valueBits & 0x7C00u) >> 10; + uint frac16 = (uint)valueBits & 0x03FFu; + + double real; + + if (exp16 == 0u) + { + if (frac16 == 0u) + { + type = FpType.Zero; + real = 0d; + } + else + { + type = FpType.Nonzero; // Subnormal. + real = Math.Pow(2d, -14) * ((double)frac16 * Math.Pow(2d, -10)); + } + } + else if (exp16 == 0x1Fu && !state.GetFpcrFlag(Fpcr.Ahp)) + { + if (frac16 == 0u) + { + type = FpType.Infinity; + real = Math.Pow(2d, 1000); + } + else + { + type = (~frac16 & 0x0200u) == 0u ? FpType.QNaN : FpType.SNaN; + real = 0d; + } + } + else + { + type = FpType.Nonzero; // Normal. + real = Math.Pow(2d, (int)exp16 - 15) * (1d + (double)frac16 * Math.Pow(2d, -10)); + } + + return sign ? -real : real; + } + + private static float FPRoundCv(double real, CpuThreadState state) + { + const int minimumExp = -126; + + const int e = 8; + const int f = 23; + + bool sign; + double mantissa; + + if (real < 0d) + { + sign = true; + mantissa = -real; + } + else + { + sign = false; + mantissa = real; + } + + int exponent = 0; + + while (mantissa < 1d) + { + mantissa *= 2d; + exponent--; + } + + while (mantissa >= 2d) + { + mantissa /= 2d; + exponent++; + } + + if (state.GetFpcrFlag(Fpcr.Fz) && exponent < minimumExp) + { + state.SetFpsrFlag(Fpsr.Ufc); + + return FPZero(sign); + } + + uint biasedExp = (uint)Math.Max(exponent - minimumExp + 1, 0); + + if (biasedExp == 0u) + { + mantissa /= Math.Pow(2d, minimumExp - exponent); + } + + uint intMant = (uint)Math.Floor(mantissa * Math.Pow(2d, f)); + double error = mantissa * Math.Pow(2d, f) - (double)intMant; + + if (biasedExp == 0u && (error != 0d || state.GetFpcrFlag(Fpcr.Ufe))) + { + FPProcessException(FpExc.Underflow, state); + } + + bool overflowToInf; + bool roundUp; + + switch (state.FPRoundingMode()) + { + default: + case RoundMode.ToNearest: + roundUp = (error > 0.5d || (error == 0.5d && (intMant & 1u) == 1u)); + overflowToInf = true; + break; + + case RoundMode.TowardsPlusInfinity: + roundUp = (error != 0d && !sign); + overflowToInf = !sign; + break; + + case RoundMode.TowardsMinusInfinity: + roundUp = (error != 0d && sign); + overflowToInf = sign; + break; + + case RoundMode.TowardsZero: + roundUp = false; + overflowToInf = false; + break; + } + + if (roundUp) + { + intMant++; + + if (intMant == (uint)Math.Pow(2d, f)) + { + biasedExp = 1u; + } + + if (intMant == (uint)Math.Pow(2d, f + 1)) + { + biasedExp++; + intMant >>= 1; + } + } + + float result; + + if (biasedExp >= (uint)Math.Pow(2d, e) - 1u) + { + result = overflowToInf ? FPInfinity(sign) : FPMaxNormal(sign); + + FPProcessException(FpExc.Overflow, state); + + error = 1d; + } + else + { + result = BitConverter.Int32BitsToSingle( + (int)((sign ? 1u : 0u) << 31 | (biasedExp & 0xFFu) << 23 | (intMant & 0x007FFFFFu))); + } + + if (error != 0d) + { + FPProcessException(FpExc.Inexact, state); + } + + return result; + } + + private static float FPConvertNaN(ushort valueBits) + { + return BitConverter.Int32BitsToSingle( + (int)(((uint)valueBits & 0x8000u) << 16 | 0x7FC00000u | ((uint)valueBits & 0x01FFu) << 13)); + } + + private static void FPProcessException(FpExc exc, CpuThreadState state) + { + int enable = (int)exc + 8; + + if ((state.Fpcr & (1 << enable)) != 0) + { + throw new NotImplementedException("floating-point trap handling"); + } + else + { + state.Fpsr |= 1 << (int)exc; + } + } + } + + static class SoftFloat3216 + { + public static ushort FPConvert(float value, CpuThreadState state) + { + Debug.WriteLineIf(state.Fpcr != 0, $"ASoftFloat32_16.FPConvert: State.Fpcr = 0x{state.Fpcr:X8}"); + + double real = value.FPUnpackCv(out FpType type, out bool sign, state, out uint valueBits); + + bool altHp = state.GetFpcrFlag(Fpcr.Ahp); + + ushort resultBits; + + if (type == FpType.SNaN || type == FpType.QNaN) + { + if (altHp) + { + resultBits = FPZero(sign); + } + else if (state.GetFpcrFlag(Fpcr.Dn)) + { + resultBits = FPDefaultNaN(); + } + else + { + resultBits = FPConvertNaN(valueBits); + } + + if (type == FpType.SNaN || altHp) + { + FPProcessException(FpExc.InvalidOp, state); + } + } + else if (type == FpType.Infinity) + { + if (altHp) + { + resultBits = (ushort)((sign ? 1u : 0u) << 15 | 0x7FFFu); + + FPProcessException(FpExc.InvalidOp, state); + } + else + { + resultBits = FPInfinity(sign); + } + } + else if (type == FpType.Zero) + { + resultBits = FPZero(sign); + } + else + { + resultBits = FPRoundCv(real, state); + } + + return resultBits; + } + + private static ushort FPDefaultNaN() + { + return (ushort)0x7E00u; + } + + private static ushort FPInfinity(bool sign) + { + return sign ? (ushort)0xFC00u : (ushort)0x7C00u; + } + + private static ushort FPZero(bool sign) + { + return sign ? (ushort)0x8000u : (ushort)0x0000u; + } + + private static ushort FPMaxNormal(bool sign) + { + return sign ? (ushort)0xFBFFu : (ushort)0x7BFFu; + } + + private static double FPUnpackCv(this float value, out FpType type, out bool sign, CpuThreadState state, out uint valueBits) + { + valueBits = (uint)BitConverter.SingleToInt32Bits(value); + + sign = (~valueBits & 0x80000000u) == 0u; + + uint exp32 = (valueBits & 0x7F800000u) >> 23; + uint frac32 = valueBits & 0x007FFFFFu; + + double real; + + if (exp32 == 0u) + { + if (frac32 == 0u || state.GetFpcrFlag(Fpcr.Fz)) + { + type = FpType.Zero; + real = 0d; + + if (frac32 != 0u) FPProcessException(FpExc.InputDenorm, state); + } + else + { + type = FpType.Nonzero; // Subnormal. + real = Math.Pow(2d, -126) * ((double)frac32 * Math.Pow(2d, -23)); + } + } + else if (exp32 == 0xFFu) + { + if (frac32 == 0u) + { + type = FpType.Infinity; + real = Math.Pow(2d, 1000); + } + else + { + type = (~frac32 & 0x00400000u) == 0u ? FpType.QNaN : FpType.SNaN; + real = 0d; + } + } + else + { + type = FpType.Nonzero; // Normal. + real = Math.Pow(2d, (int)exp32 - 127) * (1d + (double)frac32 * Math.Pow(2d, -23)); + } + + return sign ? -real : real; + } + + private static ushort FPRoundCv(double real, CpuThreadState state) + { + const int minimumExp = -14; + + const int e = 5; + const int f = 10; + + bool sign; + double mantissa; + + if (real < 0d) + { + sign = true; + mantissa = -real; + } + else + { + sign = false; + mantissa = real; + } + + int exponent = 0; + + while (mantissa < 1d) + { + mantissa *= 2d; + exponent--; + } + + while (mantissa >= 2d) + { + mantissa /= 2d; + exponent++; + } + + uint biasedExp = (uint)Math.Max(exponent - minimumExp + 1, 0); + + if (biasedExp == 0u) + { + mantissa /= Math.Pow(2d, minimumExp - exponent); + } + + uint intMant = (uint)Math.Floor(mantissa * Math.Pow(2d, f)); + double error = mantissa * Math.Pow(2d, f) - (double)intMant; + + if (biasedExp == 0u && (error != 0d || state.GetFpcrFlag(Fpcr.Ufe))) + { + FPProcessException(FpExc.Underflow, state); + } + + bool overflowToInf; + bool roundUp; + + switch (state.FPRoundingMode()) + { + default: + case RoundMode.ToNearest: + roundUp = (error > 0.5d || (error == 0.5d && (intMant & 1u) == 1u)); + overflowToInf = true; + break; + + case RoundMode.TowardsPlusInfinity: + roundUp = (error != 0d && !sign); + overflowToInf = !sign; + break; + + case RoundMode.TowardsMinusInfinity: + roundUp = (error != 0d && sign); + overflowToInf = sign; + break; + + case RoundMode.TowardsZero: + roundUp = false; + overflowToInf = false; + break; + } + + if (roundUp) + { + intMant++; + + if (intMant == (uint)Math.Pow(2d, f)) + { + biasedExp = 1u; + } + + if (intMant == (uint)Math.Pow(2d, f + 1)) + { + biasedExp++; + intMant >>= 1; + } + } + + ushort resultBits; + + if (!state.GetFpcrFlag(Fpcr.Ahp)) + { + if (biasedExp >= (uint)Math.Pow(2d, e) - 1u) + { + resultBits = overflowToInf ? FPInfinity(sign) : FPMaxNormal(sign); + + FPProcessException(FpExc.Overflow, state); + + error = 1d; + } + else + { + resultBits = (ushort)((sign ? 1u : 0u) << 15 | (biasedExp & 0x1Fu) << 10 | (intMant & 0x03FFu)); + } + } + else + { + if (biasedExp >= (uint)Math.Pow(2d, e)) + { + resultBits = (ushort)((sign ? 1u : 0u) << 15 | 0x7FFFu); + + FPProcessException(FpExc.InvalidOp, state); + + error = 0d; + } + else + { + resultBits = (ushort)((sign ? 1u : 0u) << 15 | (biasedExp & 0x1Fu) << 10 | (intMant & 0x03FFu)); + } + } + + if (error != 0d) + { + FPProcessException(FpExc.Inexact, state); + } + + return resultBits; + } + + private static ushort FPConvertNaN(uint valueBits) + { + return (ushort)((valueBits & 0x80000000u) >> 16 | 0x7E00u | (valueBits & 0x003FE000u) >> 13); + } + + private static void FPProcessException(FpExc exc, CpuThreadState state) + { + int enable = (int)exc + 8; + + if ((state.Fpcr & (1 << enable)) != 0) + { + throw new NotImplementedException("floating-point trap handling"); + } + else + { + state.Fpsr |= 1 << (int)exc; + } + } + } + + static class SoftFloat32 + { + public static float FPAdd(float value1, float value2, CpuThreadState state) + { + Debug.WriteLineIf(state.Fpcr != 0, $"ASoftFloat_32.FPAdd: State.Fpcr = 0x{state.Fpcr:X8}"); + + value1 = value1.FPUnpack(out FpType type1, out bool sign1, out uint op1); + value2 = value2.FPUnpack(out FpType type2, out bool sign2, out uint op2); + + float result = FPProcessNaNs(type1, type2, op1, op2, state, out bool done); + + if (!done) + { + bool inf1 = type1 == FpType.Infinity; bool zero1 = type1 == FpType.Zero; + bool inf2 = type2 == FpType.Infinity; bool zero2 = type2 == FpType.Zero; + + if (inf1 && inf2 && sign1 == !sign2) + { + result = FPDefaultNaN(); + + FPProcessException(FpExc.InvalidOp, state); + } + else if ((inf1 && !sign1) || (inf2 && !sign2)) + { + result = FPInfinity(false); + } + else if ((inf1 && sign1) || (inf2 && sign2)) + { + result = FPInfinity(true); + } + else if (zero1 && zero2 && sign1 == sign2) + { + result = FPZero(sign1); + } + else + { + result = value1 + value2; + } + } + + return result; + } + + public static float FPDiv(float value1, float value2, CpuThreadState state) + { + Debug.WriteLineIf(state.Fpcr != 0, $"ASoftFloat_32.FPDiv: State.Fpcr = 0x{state.Fpcr:X8}"); + + value1 = value1.FPUnpack(out FpType type1, out bool sign1, out uint op1); + value2 = value2.FPUnpack(out FpType type2, out bool sign2, out uint op2); + + float result = FPProcessNaNs(type1, type2, op1, op2, state, out bool done); + + if (!done) + { + bool inf1 = type1 == FpType.Infinity; bool zero1 = type1 == FpType.Zero; + bool inf2 = type2 == FpType.Infinity; bool zero2 = type2 == FpType.Zero; + + if ((inf1 && inf2) || (zero1 && zero2)) + { + result = FPDefaultNaN(); + + FPProcessException(FpExc.InvalidOp, state); + } + else if (inf1 || zero2) + { + result = FPInfinity(sign1 ^ sign2); + + if (!inf1) FPProcessException(FpExc.DivideByZero, state); + } + else if (zero1 || inf2) + { + result = FPZero(sign1 ^ sign2); + } + else + { + result = value1 / value2; + } + } + + return result; + } + + public static float FPMax(float value1, float value2, CpuThreadState state) + { + Debug.WriteLineIf(state.Fpcr != 0, $"ASoftFloat_32.FPMax: State.Fpcr = 0x{state.Fpcr:X8}"); + + value1 = value1.FPUnpack(out FpType type1, out bool sign1, out uint op1); + value2 = value2.FPUnpack(out FpType type2, out bool sign2, out uint op2); + + float result = FPProcessNaNs(type1, type2, op1, op2, state, out bool done); + + if (!done) + { + if (value1 > value2) + { + if (type1 == FpType.Infinity) + { + result = FPInfinity(sign1); + } + else if (type1 == FpType.Zero) + { + result = FPZero(sign1 && sign2); + } + else + { + result = value1; + } + } + else + { + if (type2 == FpType.Infinity) + { + result = FPInfinity(sign2); + } + else if (type2 == FpType.Zero) + { + result = FPZero(sign1 && sign2); + } + else + { + result = value2; + } + } + } + + return result; + } + + public static float FPMaxNum(float value1, float value2, CpuThreadState state) + { + Debug.WriteIf(state.Fpcr != 0, "ASoftFloat_32.FPMaxNum: "); + + value1.FPUnpack(out FpType type1, out _, out _); + value2.FPUnpack(out FpType type2, out _, out _); + + if (type1 == FpType.QNaN && type2 != FpType.QNaN) + { + value1 = FPInfinity(true); + } + else if (type1 != FpType.QNaN && type2 == FpType.QNaN) + { + value2 = FPInfinity(true); + } + + return FPMax(value1, value2, state); + } + + public static float FPMin(float value1, float value2, CpuThreadState state) + { + Debug.WriteLineIf(state.Fpcr != 0, $"ASoftFloat_32.FPMin: State.Fpcr = 0x{state.Fpcr:X8}"); + + value1 = value1.FPUnpack(out FpType type1, out bool sign1, out uint op1); + value2 = value2.FPUnpack(out FpType type2, out bool sign2, out uint op2); + + float result = FPProcessNaNs(type1, type2, op1, op2, state, out bool done); + + if (!done) + { + if (value1 < value2) + { + if (type1 == FpType.Infinity) + { + result = FPInfinity(sign1); + } + else if (type1 == FpType.Zero) + { + result = FPZero(sign1 || sign2); + } + else + { + result = value1; + } + } + else + { + if (type2 == FpType.Infinity) + { + result = FPInfinity(sign2); + } + else if (type2 == FpType.Zero) + { + result = FPZero(sign1 || sign2); + } + else + { + result = value2; + } + } + } + + return result; + } + + public static float FPMinNum(float value1, float value2, CpuThreadState state) + { + Debug.WriteIf(state.Fpcr != 0, "ASoftFloat_32.FPMinNum: "); + + value1.FPUnpack(out FpType type1, out _, out _); + value2.FPUnpack(out FpType type2, out _, out _); + + if (type1 == FpType.QNaN && type2 != FpType.QNaN) + { + value1 = FPInfinity(false); + } + else if (type1 != FpType.QNaN && type2 == FpType.QNaN) + { + value2 = FPInfinity(false); + } + + return FPMin(value1, value2, state); + } + + public static float FPMul(float value1, float value2, CpuThreadState state) + { + Debug.WriteLineIf(state.Fpcr != 0, $"ASoftFloat_32.FPMul: State.Fpcr = 0x{state.Fpcr:X8}"); + + value1 = value1.FPUnpack(out FpType type1, out bool sign1, out uint op1); + value2 = value2.FPUnpack(out FpType type2, out bool sign2, out uint op2); + + float result = FPProcessNaNs(type1, type2, op1, op2, state, out bool done); + + if (!done) + { + bool inf1 = type1 == FpType.Infinity; bool zero1 = type1 == FpType.Zero; + bool inf2 = type2 == FpType.Infinity; bool zero2 = type2 == FpType.Zero; + + if ((inf1 && zero2) || (zero1 && inf2)) + { + result = FPDefaultNaN(); + + FPProcessException(FpExc.InvalidOp, state); + } + else if (inf1 || inf2) + { + result = FPInfinity(sign1 ^ sign2); + } + else if (zero1 || zero2) + { + result = FPZero(sign1 ^ sign2); + } + else + { + result = value1 * value2; + } + } + + return result; + } + + public static float FPMulAdd(float valueA, float value1, float value2, CpuThreadState state) + { + Debug.WriteLineIf(state.Fpcr != 0, $"ASoftFloat_32.FPMulAdd: State.Fpcr = 0x{state.Fpcr:X8}"); + + valueA = valueA.FPUnpack(out FpType typeA, out bool signA, out uint addend); + value1 = value1.FPUnpack(out FpType type1, out bool sign1, out uint op1); + value2 = value2.FPUnpack(out FpType type2, out bool sign2, out uint op2); + + bool inf1 = type1 == FpType.Infinity; bool zero1 = type1 == FpType.Zero; + bool inf2 = type2 == FpType.Infinity; bool zero2 = type2 == FpType.Zero; + + float result = FPProcessNaNs3(typeA, type1, type2, addend, op1, op2, state, out bool done); + + if (typeA == FpType.QNaN && ((inf1 && zero2) || (zero1 && inf2))) + { + result = FPDefaultNaN(); + + FPProcessException(FpExc.InvalidOp, state); + } + + if (!done) + { + bool infA = typeA == FpType.Infinity; bool zeroA = typeA == FpType.Zero; + + bool signP = sign1 ^ sign2; + bool infP = inf1 || inf2; + bool zeroP = zero1 || zero2; + + if ((inf1 && zero2) || (zero1 && inf2) || (infA && infP && signA != signP)) + { + result = FPDefaultNaN(); + + FPProcessException(FpExc.InvalidOp, state); + } + else if ((infA && !signA) || (infP && !signP)) + { + result = FPInfinity(false); + } + else if ((infA && signA) || (infP && signP)) + { + result = FPInfinity(true); + } + else if (zeroA && zeroP && signA == signP) + { + result = FPZero(signA); + } + else + { + // TODO: When available, use: T MathF.FusedMultiplyAdd(T, T, T); + // https://github.com/dotnet/corefx/issues/31903 + + result = valueA + (value1 * value2); + } + } + + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float FPMulSub(float valueA, float value1, float value2, CpuThreadState state) + { + Debug.WriteIf(state.Fpcr != 0, "ASoftFloat_32.FPMulSub: "); + + value1 = value1.FPNeg(); + + return FPMulAdd(valueA, value1, value2, state); + } + + public static float FPMulX(float value1, float value2, CpuThreadState state) + { + Debug.WriteLineIf(state.Fpcr != 0, $"ASoftFloat_32.FPMulX: State.Fpcr = 0x{state.Fpcr:X8}"); + + value1 = value1.FPUnpack(out FpType type1, out bool sign1, out uint op1); + value2 = value2.FPUnpack(out FpType type2, out bool sign2, out uint op2); + + float result = FPProcessNaNs(type1, type2, op1, op2, state, out bool done); + + if (!done) + { + bool inf1 = type1 == FpType.Infinity; bool zero1 = type1 == FpType.Zero; + bool inf2 = type2 == FpType.Infinity; bool zero2 = type2 == FpType.Zero; + + if ((inf1 && zero2) || (zero1 && inf2)) + { + result = FPTwo(sign1 ^ sign2); + } + else if (inf1 || inf2) + { + result = FPInfinity(sign1 ^ sign2); + } + else if (zero1 || zero2) + { + result = FPZero(sign1 ^ sign2); + } + else + { + result = value1 * value2; + } + } + + return result; + } + + public static float FPRecipStepFused(float value1, float value2, CpuThreadState state) + { + Debug.WriteLineIf(state.Fpcr != 0, $"ASoftFloat_32.FPRecipStepFused: State.Fpcr = 0x{state.Fpcr:X8}"); + + value1 = value1.FPNeg(); + + value1 = value1.FPUnpack(out FpType type1, out bool sign1, out uint op1); + value2 = value2.FPUnpack(out FpType type2, out bool sign2, out uint op2); + + float result = FPProcessNaNs(type1, type2, op1, op2, state, out bool done); + + if (!done) + { + bool inf1 = type1 == FpType.Infinity; bool zero1 = type1 == FpType.Zero; + bool inf2 = type2 == FpType.Infinity; bool zero2 = type2 == FpType.Zero; + + if ((inf1 && zero2) || (zero1 && inf2)) + { + result = FPTwo(false); + } + else if (inf1 || inf2) + { + result = FPInfinity(sign1 ^ sign2); + } + else + { + // TODO: When available, use: T MathF.FusedMultiplyAdd(T, T, T); + // https://github.com/dotnet/corefx/issues/31903 + + result = 2f + (value1 * value2); + } + } + + return result; + } + + public static float FPRecpX(float value, CpuThreadState state) + { + Debug.WriteLineIf(state.Fpcr != 0, $"ASoftFloat_32.FPRecpX: State.Fpcr = 0x{state.Fpcr:X8}"); + + value.FPUnpack(out FpType type, out bool sign, out uint op); + + float result; + + if (type == FpType.SNaN || type == FpType.QNaN) + { + result = FPProcessNaN(type, op, state); + } + else + { + uint notExp = (~op >> 23) & 0xFFu; + uint maxExp = 0xFEu; + + result = BitConverter.Int32BitsToSingle( + (int)((sign ? 1u : 0u) << 31 | (notExp == 0xFFu ? maxExp : notExp) << 23)); + } + + return result; + } + + public static float FprSqrtStepFused(float value1, float value2, CpuThreadState state) + { + Debug.WriteLineIf(state.Fpcr != 0, $"ASoftFloat_32.FPRSqrtStepFused: State.Fpcr = 0x{state.Fpcr:X8}"); + + value1 = value1.FPNeg(); + + value1 = value1.FPUnpack(out FpType type1, out bool sign1, out uint op1); + value2 = value2.FPUnpack(out FpType type2, out bool sign2, out uint op2); + + float result = FPProcessNaNs(type1, type2, op1, op2, state, out bool done); + + if (!done) + { + bool inf1 = type1 == FpType.Infinity; bool zero1 = type1 == FpType.Zero; + bool inf2 = type2 == FpType.Infinity; bool zero2 = type2 == FpType.Zero; + + if ((inf1 && zero2) || (zero1 && inf2)) + { + result = FPOnePointFive(false); + } + else if (inf1 || inf2) + { + result = FPInfinity(sign1 ^ sign2); + } + else + { + // TODO: When available, use: T MathF.FusedMultiplyAdd(T, T, T); + // https://github.com/dotnet/corefx/issues/31903 + + result = (3f + (value1 * value2)) / 2f; + } + } + + return result; + } + + public static float FPSqrt(float value, CpuThreadState state) + { + Debug.WriteLineIf(state.Fpcr != 0, $"ASoftFloat_32.FPSqrt: State.Fpcr = 0x{state.Fpcr:X8}"); + + value = value.FPUnpack(out FpType type, out bool sign, out uint op); + + float result; + + if (type == FpType.SNaN || type == FpType.QNaN) + { + result = FPProcessNaN(type, op, state); + } + else if (type == FpType.Zero) + { + result = FPZero(sign); + } + else if (type == FpType.Infinity && !sign) + { + result = FPInfinity(sign); + } + else if (sign) + { + result = FPDefaultNaN(); + + FPProcessException(FpExc.InvalidOp, state); + } + else + { + result = MathF.Sqrt(value); + } + + return result; + } + + public static float FPSub(float value1, float value2, CpuThreadState state) + { + Debug.WriteLineIf(state.Fpcr != 0, $"ASoftFloat_32.FPSub: State.Fpcr = 0x{state.Fpcr:X8}"); + + value1 = value1.FPUnpack(out FpType type1, out bool sign1, out uint op1); + value2 = value2.FPUnpack(out FpType type2, out bool sign2, out uint op2); + + float result = FPProcessNaNs(type1, type2, op1, op2, state, out bool done); + + if (!done) + { + bool inf1 = type1 == FpType.Infinity; bool zero1 = type1 == FpType.Zero; + bool inf2 = type2 == FpType.Infinity; bool zero2 = type2 == FpType.Zero; + + if (inf1 && inf2 && sign1 == sign2) + { + result = FPDefaultNaN(); + + FPProcessException(FpExc.InvalidOp, state); + } + else if ((inf1 && !sign1) || (inf2 && sign2)) + { + result = FPInfinity(false); + } + else if ((inf1 && sign1) || (inf2 && !sign2)) + { + result = FPInfinity(true); + } + else if (zero1 && zero2 && sign1 == !sign2) + { + result = FPZero(sign1); + } + else + { + result = value1 - value2; + } + } + + return result; + } + + private static float FPDefaultNaN() + { + return -float.NaN; + } + + private static float FPInfinity(bool sign) + { + return sign ? float.NegativeInfinity : float.PositiveInfinity; + } + + private static float FPZero(bool sign) + { + return sign ? -0f : +0f; + } + + private static float FPTwo(bool sign) + { + return sign ? -2f : +2f; + } + + private static float FPOnePointFive(bool sign) + { + return sign ? -1.5f : +1.5f; + } + + private static float FPNeg(this float value) + { + return -value; + } + + private static float FPUnpack(this float value, out FpType type, out bool sign, out uint valueBits) + { + valueBits = (uint)BitConverter.SingleToInt32Bits(value); + + sign = (~valueBits & 0x80000000u) == 0u; + + if ((valueBits & 0x7F800000u) == 0u) + { + if ((valueBits & 0x007FFFFFu) == 0u) + { + type = FpType.Zero; + } + else + { + type = FpType.Nonzero; + } + } + else if ((~valueBits & 0x7F800000u) == 0u) + { + if ((valueBits & 0x007FFFFFu) == 0u) + { + type = FpType.Infinity; + } + else + { + type = (~valueBits & 0x00400000u) == 0u + ? FpType.QNaN + : FpType.SNaN; + + return FPZero(sign); + } + } + else + { + type = FpType.Nonzero; + } + + return value; + } + + private static float FPProcessNaNs( + FpType type1, + FpType type2, + uint op1, + uint op2, + CpuThreadState state, + out bool done) + { + done = true; + + if (type1 == FpType.SNaN) + { + return FPProcessNaN(type1, op1, state); + } + else if (type2 == FpType.SNaN) + { + return FPProcessNaN(type2, op2, state); + } + else if (type1 == FpType.QNaN) + { + return FPProcessNaN(type1, op1, state); + } + else if (type2 == FpType.QNaN) + { + return FPProcessNaN(type2, op2, state); + } + + done = false; + + return FPZero(false); + } + + private static float FPProcessNaNs3( + FpType type1, + FpType type2, + FpType type3, + uint op1, + uint op2, + uint op3, + CpuThreadState state, + out bool done) + { + done = true; + + if (type1 == FpType.SNaN) + { + return FPProcessNaN(type1, op1, state); + } + else if (type2 == FpType.SNaN) + { + return FPProcessNaN(type2, op2, state); + } + else if (type3 == FpType.SNaN) + { + return FPProcessNaN(type3, op3, state); + } + else if (type1 == FpType.QNaN) + { + return FPProcessNaN(type1, op1, state); + } + else if (type2 == FpType.QNaN) + { + return FPProcessNaN(type2, op2, state); + } + else if (type3 == FpType.QNaN) + { + return FPProcessNaN(type3, op3, state); + } + + done = false; + + return FPZero(false); + } + + private static float FPProcessNaN(FpType type, uint op, CpuThreadState state) + { + if (type == FpType.SNaN) + { + op |= 1u << 22; + + FPProcessException(FpExc.InvalidOp, state); + } + + if (state.GetFpcrFlag(Fpcr.Dn)) + { + return FPDefaultNaN(); + } + + return BitConverter.Int32BitsToSingle((int)op); + } + + private static void FPProcessException(FpExc exc, CpuThreadState state) + { + int enable = (int)exc + 8; + + if ((state.Fpcr & (1 << enable)) != 0) + { + throw new NotImplementedException("floating-point trap handling"); + } + else + { + state.Fpsr |= 1 << (int)exc; + } + } + } + + static class SoftFloat64 + { + public static double FPAdd(double value1, double value2, CpuThreadState state) + { + Debug.WriteLineIf(state.Fpcr != 0, $"ASoftFloat_64.FPAdd: State.Fpcr = 0x{state.Fpcr:X8}"); + + value1 = value1.FPUnpack(out FpType type1, out bool sign1, out ulong op1); + value2 = value2.FPUnpack(out FpType type2, out bool sign2, out ulong op2); + + double result = FPProcessNaNs(type1, type2, op1, op2, state, out bool done); + + if (!done) + { + bool inf1 = type1 == FpType.Infinity; bool zero1 = type1 == FpType.Zero; + bool inf2 = type2 == FpType.Infinity; bool zero2 = type2 == FpType.Zero; + + if (inf1 && inf2 && sign1 == !sign2) + { + result = FPDefaultNaN(); + + FPProcessException(FpExc.InvalidOp, state); + } + else if ((inf1 && !sign1) || (inf2 && !sign2)) + { + result = FPInfinity(false); + } + else if ((inf1 && sign1) || (inf2 && sign2)) + { + result = FPInfinity(true); + } + else if (zero1 && zero2 && sign1 == sign2) + { + result = FPZero(sign1); + } + else + { + result = value1 + value2; + } + } + + return result; + } + + public static double FPDiv(double value1, double value2, CpuThreadState state) + { + Debug.WriteLineIf(state.Fpcr != 0, $"ASoftFloat_64.FPDiv: State.Fpcr = 0x{state.Fpcr:X8}"); + + value1 = value1.FPUnpack(out FpType type1, out bool sign1, out ulong op1); + value2 = value2.FPUnpack(out FpType type2, out bool sign2, out ulong op2); + + double result = FPProcessNaNs(type1, type2, op1, op2, state, out bool done); + + if (!done) + { + bool inf1 = type1 == FpType.Infinity; bool zero1 = type1 == FpType.Zero; + bool inf2 = type2 == FpType.Infinity; bool zero2 = type2 == FpType.Zero; + + if ((inf1 && inf2) || (zero1 && zero2)) + { + result = FPDefaultNaN(); + + FPProcessException(FpExc.InvalidOp, state); + } + else if (inf1 || zero2) + { + result = FPInfinity(sign1 ^ sign2); + + if (!inf1) FPProcessException(FpExc.DivideByZero, state); + } + else if (zero1 || inf2) + { + result = FPZero(sign1 ^ sign2); + } + else + { + result = value1 / value2; + } + } + + return result; + } + + public static double FPMax(double value1, double value2, CpuThreadState state) + { + Debug.WriteLineIf(state.Fpcr != 0, $"ASoftFloat_64.FPMax: State.Fpcr = 0x{state.Fpcr:X8}"); + + value1 = value1.FPUnpack(out FpType type1, out bool sign1, out ulong op1); + value2 = value2.FPUnpack(out FpType type2, out bool sign2, out ulong op2); + + double result = FPProcessNaNs(type1, type2, op1, op2, state, out bool done); + + if (!done) + { + if (value1 > value2) + { + if (type1 == FpType.Infinity) + { + result = FPInfinity(sign1); + } + else if (type1 == FpType.Zero) + { + result = FPZero(sign1 && sign2); + } + else + { + result = value1; + } + } + else + { + if (type2 == FpType.Infinity) + { + result = FPInfinity(sign2); + } + else if (type2 == FpType.Zero) + { + result = FPZero(sign1 && sign2); + } + else + { + result = value2; + } + } + } + + return result; + } + + public static double FPMaxNum(double value1, double value2, CpuThreadState state) + { + Debug.WriteIf(state.Fpcr != 0, "ASoftFloat_64.FPMaxNum: "); + + value1.FPUnpack(out FpType type1, out _, out _); + value2.FPUnpack(out FpType type2, out _, out _); + + if (type1 == FpType.QNaN && type2 != FpType.QNaN) + { + value1 = FPInfinity(true); + } + else if (type1 != FpType.QNaN && type2 == FpType.QNaN) + { + value2 = FPInfinity(true); + } + + return FPMax(value1, value2, state); + } + + public static double FPMin(double value1, double value2, CpuThreadState state) + { + Debug.WriteLineIf(state.Fpcr != 0, $"ASoftFloat_64.FPMin: State.Fpcr = 0x{state.Fpcr:X8}"); + + value1 = value1.FPUnpack(out FpType type1, out bool sign1, out ulong op1); + value2 = value2.FPUnpack(out FpType type2, out bool sign2, out ulong op2); + + double result = FPProcessNaNs(type1, type2, op1, op2, state, out bool done); + + if (!done) + { + if (value1 < value2) + { + if (type1 == FpType.Infinity) + { + result = FPInfinity(sign1); + } + else if (type1 == FpType.Zero) + { + result = FPZero(sign1 || sign2); + } + else + { + result = value1; + } + } + else + { + if (type2 == FpType.Infinity) + { + result = FPInfinity(sign2); + } + else if (type2 == FpType.Zero) + { + result = FPZero(sign1 || sign2); + } + else + { + result = value2; + } + } + } + + return result; + } + + public static double FPMinNum(double value1, double value2, CpuThreadState state) + { + Debug.WriteIf(state.Fpcr != 0, "ASoftFloat_64.FPMinNum: "); + + value1.FPUnpack(out FpType type1, out _, out _); + value2.FPUnpack(out FpType type2, out _, out _); + + if (type1 == FpType.QNaN && type2 != FpType.QNaN) + { + value1 = FPInfinity(false); + } + else if (type1 != FpType.QNaN && type2 == FpType.QNaN) + { + value2 = FPInfinity(false); + } + + return FPMin(value1, value2, state); + } + + public static double FPMul(double value1, double value2, CpuThreadState state) + { + Debug.WriteLineIf(state.Fpcr != 0, $"ASoftFloat_64.FPMul: State.Fpcr = 0x{state.Fpcr:X8}"); + + value1 = value1.FPUnpack(out FpType type1, out bool sign1, out ulong op1); + value2 = value2.FPUnpack(out FpType type2, out bool sign2, out ulong op2); + + double result = FPProcessNaNs(type1, type2, op1, op2, state, out bool done); + + if (!done) + { + bool inf1 = type1 == FpType.Infinity; bool zero1 = type1 == FpType.Zero; + bool inf2 = type2 == FpType.Infinity; bool zero2 = type2 == FpType.Zero; + + if ((inf1 && zero2) || (zero1 && inf2)) + { + result = FPDefaultNaN(); + + FPProcessException(FpExc.InvalidOp, state); + } + else if (inf1 || inf2) + { + result = FPInfinity(sign1 ^ sign2); + } + else if (zero1 || zero2) + { + result = FPZero(sign1 ^ sign2); + } + else + { + result = value1 * value2; + } + } + + return result; + } + + public static double FPMulAdd(double valueA, double value1, double value2, CpuThreadState state) + { + Debug.WriteLineIf(state.Fpcr != 0, $"ASoftFloat_64.FPMulAdd: State.Fpcr = 0x{state.Fpcr:X8}"); + + valueA = valueA.FPUnpack(out FpType typeA, out bool signA, out ulong addend); + value1 = value1.FPUnpack(out FpType type1, out bool sign1, out ulong op1); + value2 = value2.FPUnpack(out FpType type2, out bool sign2, out ulong op2); + + bool inf1 = type1 == FpType.Infinity; bool zero1 = type1 == FpType.Zero; + bool inf2 = type2 == FpType.Infinity; bool zero2 = type2 == FpType.Zero; + + double result = FPProcessNaNs3(typeA, type1, type2, addend, op1, op2, state, out bool done); + + if (typeA == FpType.QNaN && ((inf1 && zero2) || (zero1 && inf2))) + { + result = FPDefaultNaN(); + + FPProcessException(FpExc.InvalidOp, state); + } + + if (!done) + { + bool infA = typeA == FpType.Infinity; bool zeroA = typeA == FpType.Zero; + + bool signP = sign1 ^ sign2; + bool infP = inf1 || inf2; + bool zeroP = zero1 || zero2; + + if ((inf1 && zero2) || (zero1 && inf2) || (infA && infP && signA != signP)) + { + result = FPDefaultNaN(); + + FPProcessException(FpExc.InvalidOp, state); + } + else if ((infA && !signA) || (infP && !signP)) + { + result = FPInfinity(false); + } + else if ((infA && signA) || (infP && signP)) + { + result = FPInfinity(true); + } + else if (zeroA && zeroP && signA == signP) + { + result = FPZero(signA); + } + else + { + // TODO: When available, use: T Math.FusedMultiplyAdd(T, T, T); + // https://github.com/dotnet/corefx/issues/31903 + + result = valueA + (value1 * value2); + } + } + + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double FPMulSub(double valueA, double value1, double value2, CpuThreadState state) + { + Debug.WriteIf(state.Fpcr != 0, "ASoftFloat_64.FPMulSub: "); + + value1 = value1.FPNeg(); + + return FPMulAdd(valueA, value1, value2, state); + } + + public static double FPMulX(double value1, double value2, CpuThreadState state) + { + Debug.WriteLineIf(state.Fpcr != 0, $"ASoftFloat_64.FPMulX: State.Fpcr = 0x{state.Fpcr:X8}"); + + value1 = value1.FPUnpack(out FpType type1, out bool sign1, out ulong op1); + value2 = value2.FPUnpack(out FpType type2, out bool sign2, out ulong op2); + + double result = FPProcessNaNs(type1, type2, op1, op2, state, out bool done); + + if (!done) + { + bool inf1 = type1 == FpType.Infinity; bool zero1 = type1 == FpType.Zero; + bool inf2 = type2 == FpType.Infinity; bool zero2 = type2 == FpType.Zero; + + if ((inf1 && zero2) || (zero1 && inf2)) + { + result = FPTwo(sign1 ^ sign2); + } + else if (inf1 || inf2) + { + result = FPInfinity(sign1 ^ sign2); + } + else if (zero1 || zero2) + { + result = FPZero(sign1 ^ sign2); + } + else + { + result = value1 * value2; + } + } + + return result; + } + + public static double FPRecipStepFused(double value1, double value2, CpuThreadState state) + { + Debug.WriteLineIf(state.Fpcr != 0, $"ASoftFloat_64.FPRecipStepFused: State.Fpcr = 0x{state.Fpcr:X8}"); + + value1 = value1.FPNeg(); + + value1 = value1.FPUnpack(out FpType type1, out bool sign1, out ulong op1); + value2 = value2.FPUnpack(out FpType type2, out bool sign2, out ulong op2); + + double result = FPProcessNaNs(type1, type2, op1, op2, state, out bool done); + + if (!done) + { + bool inf1 = type1 == FpType.Infinity; bool zero1 = type1 == FpType.Zero; + bool inf2 = type2 == FpType.Infinity; bool zero2 = type2 == FpType.Zero; + + if ((inf1 && zero2) || (zero1 && inf2)) + { + result = FPTwo(false); + } + else if (inf1 || inf2) + { + result = FPInfinity(sign1 ^ sign2); + } + else + { + // TODO: When available, use: T Math.FusedMultiplyAdd(T, T, T); + // https://github.com/dotnet/corefx/issues/31903 + + result = 2d + (value1 * value2); + } + } + + return result; + } + + public static double FPRecpX(double value, CpuThreadState state) + { + Debug.WriteLineIf(state.Fpcr != 0, $"ASoftFloat_64.FPRecpX: State.Fpcr = 0x{state.Fpcr:X8}"); + + value.FPUnpack(out FpType type, out bool sign, out ulong op); + + double result; + + if (type == FpType.SNaN || type == FpType.QNaN) + { + result = FPProcessNaN(type, op, state); + } + else + { + ulong notExp = (~op >> 52) & 0x7FFul; + ulong maxExp = 0x7FEul; + + result = BitConverter.Int64BitsToDouble( + (long)((sign ? 1ul : 0ul) << 63 | (notExp == 0x7FFul ? maxExp : notExp) << 52)); + } + + return result; + } + + public static double FprSqrtStepFused(double value1, double value2, CpuThreadState state) + { + Debug.WriteLineIf(state.Fpcr != 0, $"ASoftFloat_64.FPRSqrtStepFused: State.Fpcr = 0x{state.Fpcr:X8}"); + + value1 = value1.FPNeg(); + + value1 = value1.FPUnpack(out FpType type1, out bool sign1, out ulong op1); + value2 = value2.FPUnpack(out FpType type2, out bool sign2, out ulong op2); + + double result = FPProcessNaNs(type1, type2, op1, op2, state, out bool done); + + if (!done) + { + bool inf1 = type1 == FpType.Infinity; bool zero1 = type1 == FpType.Zero; + bool inf2 = type2 == FpType.Infinity; bool zero2 = type2 == FpType.Zero; + + if ((inf1 && zero2) || (zero1 && inf2)) + { + result = FPOnePointFive(false); + } + else if (inf1 || inf2) + { + result = FPInfinity(sign1 ^ sign2); + } + else + { + // TODO: When available, use: T Math.FusedMultiplyAdd(T, T, T); + // https://github.com/dotnet/corefx/issues/31903 + + result = (3d + (value1 * value2)) / 2d; + } + } + + return result; + } + + public static double FPSqrt(double value, CpuThreadState state) + { + Debug.WriteLineIf(state.Fpcr != 0, $"ASoftFloat_64.FPSqrt: State.Fpcr = 0x{state.Fpcr:X8}"); + + value = value.FPUnpack(out FpType type, out bool sign, out ulong op); + + double result; + + if (type == FpType.SNaN || type == FpType.QNaN) + { + result = FPProcessNaN(type, op, state); + } + else if (type == FpType.Zero) + { + result = FPZero(sign); + } + else if (type == FpType.Infinity && !sign) + { + result = FPInfinity(sign); + } + else if (sign) + { + result = FPDefaultNaN(); + + FPProcessException(FpExc.InvalidOp, state); + } + else + { + result = Math.Sqrt(value); + } + + return result; + } + + public static double FPSub(double value1, double value2, CpuThreadState state) + { + Debug.WriteLineIf(state.Fpcr != 0, $"ASoftFloat_64.FPSub: State.Fpcr = 0x{state.Fpcr:X8}"); + + value1 = value1.FPUnpack(out FpType type1, out bool sign1, out ulong op1); + value2 = value2.FPUnpack(out FpType type2, out bool sign2, out ulong op2); + + double result = FPProcessNaNs(type1, type2, op1, op2, state, out bool done); + + if (!done) + { + bool inf1 = type1 == FpType.Infinity; bool zero1 = type1 == FpType.Zero; + bool inf2 = type2 == FpType.Infinity; bool zero2 = type2 == FpType.Zero; + + if (inf1 && inf2 && sign1 == sign2) + { + result = FPDefaultNaN(); + + FPProcessException(FpExc.InvalidOp, state); + } + else if ((inf1 && !sign1) || (inf2 && sign2)) + { + result = FPInfinity(false); + } + else if ((inf1 && sign1) || (inf2 && !sign2)) + { + result = FPInfinity(true); + } + else if (zero1 && zero2 && sign1 == !sign2) + { + result = FPZero(sign1); + } + else + { + result = value1 - value2; + } + } + + return result; + } + + private static double FPDefaultNaN() + { + return -double.NaN; + } + + private static double FPInfinity(bool sign) + { + return sign ? double.NegativeInfinity : double.PositiveInfinity; + } + + private static double FPZero(bool sign) + { + return sign ? -0d : +0d; + } + + private static double FPTwo(bool sign) + { + return sign ? -2d : +2d; + } + + private static double FPOnePointFive(bool sign) + { + return sign ? -1.5d : +1.5d; + } + + private static double FPNeg(this double value) + { + return -value; + } + + private static double FPUnpack(this double value, out FpType type, out bool sign, out ulong valueBits) + { + valueBits = (ulong)BitConverter.DoubleToInt64Bits(value); + + sign = (~valueBits & 0x8000000000000000ul) == 0ul; + + if ((valueBits & 0x7FF0000000000000ul) == 0ul) + { + if ((valueBits & 0x000FFFFFFFFFFFFFul) == 0ul) + { + type = FpType.Zero; + } + else + { + type = FpType.Nonzero; + } + } + else if ((~valueBits & 0x7FF0000000000000ul) == 0ul) + { + if ((valueBits & 0x000FFFFFFFFFFFFFul) == 0ul) + { + type = FpType.Infinity; + } + else + { + type = (~valueBits & 0x0008000000000000ul) == 0ul + ? FpType.QNaN + : FpType.SNaN; + + return FPZero(sign); + } + } + else + { + type = FpType.Nonzero; + } + + return value; + } + + private static double FPProcessNaNs( + FpType type1, + FpType type2, + ulong op1, + ulong op2, + CpuThreadState state, + out bool done) + { + done = true; + + if (type1 == FpType.SNaN) + { + return FPProcessNaN(type1, op1, state); + } + else if (type2 == FpType.SNaN) + { + return FPProcessNaN(type2, op2, state); + } + else if (type1 == FpType.QNaN) + { + return FPProcessNaN(type1, op1, state); + } + else if (type2 == FpType.QNaN) + { + return FPProcessNaN(type2, op2, state); + } + + done = false; + + return FPZero(false); + } + + private static double FPProcessNaNs3( + FpType type1, + FpType type2, + FpType type3, + ulong op1, + ulong op2, + ulong op3, + CpuThreadState state, + out bool done) + { + done = true; + + if (type1 == FpType.SNaN) + { + return FPProcessNaN(type1, op1, state); + } + else if (type2 == FpType.SNaN) + { + return FPProcessNaN(type2, op2, state); + } + else if (type3 == FpType.SNaN) + { + return FPProcessNaN(type3, op3, state); + } + else if (type1 == FpType.QNaN) + { + return FPProcessNaN(type1, op1, state); + } + else if (type2 == FpType.QNaN) + { + return FPProcessNaN(type2, op2, state); + } + else if (type3 == FpType.QNaN) + { + return FPProcessNaN(type3, op3, state); + } + + done = false; + + return FPZero(false); + } + + private static double FPProcessNaN(FpType type, ulong op, CpuThreadState state) + { + if (type == FpType.SNaN) + { + op |= 1ul << 51; + + FPProcessException(FpExc.InvalidOp, state); + } + + if (state.GetFpcrFlag(Fpcr.Dn)) + { + return FPDefaultNaN(); + } + + return BitConverter.Int64BitsToDouble((long)op); + } + + private static void FPProcessException(FpExc exc, CpuThreadState state) + { + int enable = (int)exc + 8; + + if ((state.Fpcr & (1 << enable)) != 0) + { + throw new NotImplementedException("floating-point trap handling"); + } + else + { + state.Fpsr |= 1 << (int)exc; + } + } + } +} diff --git a/Instruction/AVectorHelper.cs b/Instructions/VectorHelper.cs similarity index 53% rename from Instruction/AVectorHelper.cs rename to Instructions/VectorHelper.cs index 41e865b..8ef1581 100644 --- a/Instruction/AVectorHelper.cs +++ b/Instructions/VectorHelper.cs @@ -5,287 +5,287 @@ using System.Runtime.CompilerServices; using System.Runtime.Intrinsics; using System.Runtime.Intrinsics.X86; -namespace ChocolArm64.Instruction +namespace ChocolArm64.Instructions { - static class AVectorHelper + static class VectorHelper { - private static readonly Vector128 Zero32_128Mask; + private static readonly Vector128 Zero32128Mask; - static AVectorHelper() + static VectorHelper() { if (!Sse2.IsSupported) { throw new PlatformNotSupportedException(); } - Zero32_128Mask = Sse.StaticCast(Sse2.SetVector128(0, 0, 0, 0xffffffff)); + Zero32128Mask = Sse.StaticCast(Sse2.SetVector128(0, 0, 0, 0xffffffff)); } - public static void EmitCall(AILEmitterCtx Context, string Name64, string Name128) + public static void EmitCall(ILEmitterCtx context, string name64, string name128) { - bool IsSimd64 = Context.CurrOp.RegisterSize == ARegisterSize.SIMD64; + bool isSimd64 = context.CurrOp.RegisterSize == RegisterSize.Simd64; - Context.EmitCall(typeof(AVectorHelper), IsSimd64 ? Name64 : Name128); + context.EmitCall(typeof(VectorHelper), isSimd64 ? name64 : name128); } - public static void EmitCall(AILEmitterCtx Context, string MthdName) + public static void EmitCall(ILEmitterCtx context, string mthdName) { - Context.EmitCall(typeof(AVectorHelper), MthdName); + context.EmitCall(typeof(VectorHelper), mthdName); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int SatF32ToS32(float Value) + public static int SatF32ToS32(float value) { - if (float.IsNaN(Value)) return 0; + if (float.IsNaN(value)) return 0; - return Value > int.MaxValue ? int.MaxValue : - Value < int.MinValue ? int.MinValue : (int)Value; + return value > int.MaxValue ? int.MaxValue : + value < int.MinValue ? int.MinValue : (int)value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static long SatF32ToS64(float Value) + public static long SatF32ToS64(float value) { - if (float.IsNaN(Value)) return 0; + if (float.IsNaN(value)) return 0; - return Value > long.MaxValue ? long.MaxValue : - Value < long.MinValue ? long.MinValue : (long)Value; + return value > long.MaxValue ? long.MaxValue : + value < long.MinValue ? long.MinValue : (long)value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static uint SatF32ToU32(float Value) + public static uint SatF32ToU32(float value) { - if (float.IsNaN(Value)) return 0; + if (float.IsNaN(value)) return 0; - return Value > uint.MaxValue ? uint.MaxValue : - Value < uint.MinValue ? uint.MinValue : (uint)Value; + return value > uint.MaxValue ? uint.MaxValue : + value < uint.MinValue ? uint.MinValue : (uint)value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ulong SatF32ToU64(float Value) + public static ulong SatF32ToU64(float value) { - if (float.IsNaN(Value)) return 0; + if (float.IsNaN(value)) return 0; - return Value > ulong.MaxValue ? ulong.MaxValue : - Value < ulong.MinValue ? ulong.MinValue : (ulong)Value; + return value > ulong.MaxValue ? ulong.MaxValue : + value < ulong.MinValue ? ulong.MinValue : (ulong)value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int SatF64ToS32(double Value) + public static int SatF64ToS32(double value) { - if (double.IsNaN(Value)) return 0; + if (double.IsNaN(value)) return 0; - return Value > int.MaxValue ? int.MaxValue : - Value < int.MinValue ? int.MinValue : (int)Value; + return value > int.MaxValue ? int.MaxValue : + value < int.MinValue ? int.MinValue : (int)value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static long SatF64ToS64(double Value) + public static long SatF64ToS64(double value) { - if (double.IsNaN(Value)) return 0; + if (double.IsNaN(value)) return 0; - return Value > long.MaxValue ? long.MaxValue : - Value < long.MinValue ? long.MinValue : (long)Value; + return value > long.MaxValue ? long.MaxValue : + value < long.MinValue ? long.MinValue : (long)value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static uint SatF64ToU32(double Value) + public static uint SatF64ToU32(double value) { - if (double.IsNaN(Value)) return 0; + if (double.IsNaN(value)) return 0; - return Value > uint.MaxValue ? uint.MaxValue : - Value < uint.MinValue ? uint.MinValue : (uint)Value; + return value > uint.MaxValue ? uint.MaxValue : + value < uint.MinValue ? uint.MinValue : (uint)value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ulong SatF64ToU64(double Value) + public static ulong SatF64ToU64(double value) { - if (double.IsNaN(Value)) return 0; + if (double.IsNaN(value)) return 0; - return Value > ulong.MaxValue ? ulong.MaxValue : - Value < ulong.MinValue ? ulong.MinValue : (ulong)Value; + return value > ulong.MaxValue ? ulong.MaxValue : + value < ulong.MinValue ? ulong.MinValue : (ulong)value; } - public static double Round(double Value, AThreadState State) + public static double Round(double value, CpuThreadState state) { - switch (State.FPRoundingMode()) + switch (state.FPRoundingMode()) { - case ARoundMode.ToNearest: return Math.Round (Value); - case ARoundMode.TowardsPlusInfinity: return Math.Ceiling (Value); - case ARoundMode.TowardsMinusInfinity: return Math.Floor (Value); - case ARoundMode.TowardsZero: return Math.Truncate(Value); + case RoundMode.ToNearest: return Math.Round (value); + case RoundMode.TowardsPlusInfinity: return Math.Ceiling (value); + case RoundMode.TowardsMinusInfinity: return Math.Floor (value); + case RoundMode.TowardsZero: return Math.Truncate(value); } throw new InvalidOperationException(); } - public static float RoundF(float Value, AThreadState State) + public static float RoundF(float value, CpuThreadState state) { - switch (State.FPRoundingMode()) + switch (state.FPRoundingMode()) { - case ARoundMode.ToNearest: return MathF.Round (Value); - case ARoundMode.TowardsPlusInfinity: return MathF.Ceiling (Value); - case ARoundMode.TowardsMinusInfinity: return MathF.Floor (Value); - case ARoundMode.TowardsZero: return MathF.Truncate(Value); + case RoundMode.ToNearest: return MathF.Round (value); + case RoundMode.TowardsPlusInfinity: return MathF.Ceiling (value); + case RoundMode.TowardsMinusInfinity: return MathF.Floor (value); + case RoundMode.TowardsZero: return MathF.Truncate(value); } throw new InvalidOperationException(); } public static Vector128 Tbl1_V64( - Vector128 Vector, - Vector128 Tb0) + Vector128 vector, + Vector128 tb0) { - return Tbl(Vector, 8, Tb0); + return Tbl(vector, 8, tb0); } public static Vector128 Tbl1_V128( - Vector128 Vector, - Vector128 Tb0) + Vector128 vector, + Vector128 tb0) { - return Tbl(Vector, 16, Tb0); + return Tbl(vector, 16, tb0); } public static Vector128 Tbl2_V64( - Vector128 Vector, - Vector128 Tb0, - Vector128 Tb1) + Vector128 vector, + Vector128 tb0, + Vector128 tb1) { - return Tbl(Vector, 8, Tb0, Tb1); + return Tbl(vector, 8, tb0, tb1); } public static Vector128 Tbl2_V128( - Vector128 Vector, - Vector128 Tb0, - Vector128 Tb1) + Vector128 vector, + Vector128 tb0, + Vector128 tb1) { - return Tbl(Vector, 16, Tb0, Tb1); + return Tbl(vector, 16, tb0, tb1); } public static Vector128 Tbl3_V64( - Vector128 Vector, - Vector128 Tb0, - Vector128 Tb1, - Vector128 Tb2) + Vector128 vector, + Vector128 tb0, + Vector128 tb1, + Vector128 tb2) { - return Tbl(Vector, 8, Tb0, Tb1, Tb2); + return Tbl(vector, 8, tb0, tb1, tb2); } public static Vector128 Tbl3_V128( - Vector128 Vector, - Vector128 Tb0, - Vector128 Tb1, - Vector128 Tb2) + Vector128 vector, + Vector128 tb0, + Vector128 tb1, + Vector128 tb2) { - return Tbl(Vector, 16, Tb0, Tb1, Tb2); + return Tbl(vector, 16, tb0, tb1, tb2); } public static Vector128 Tbl4_V64( - Vector128 Vector, - Vector128 Tb0, - Vector128 Tb1, - Vector128 Tb2, - Vector128 Tb3) + Vector128 vector, + Vector128 tb0, + Vector128 tb1, + Vector128 tb2, + Vector128 tb3) { - return Tbl(Vector, 8, Tb0, Tb1, Tb2, Tb3); + return Tbl(vector, 8, tb0, tb1, tb2, tb3); } public static Vector128 Tbl4_V128( - Vector128 Vector, - Vector128 Tb0, - Vector128 Tb1, - Vector128 Tb2, - Vector128 Tb3) + Vector128 vector, + Vector128 tb0, + Vector128 tb1, + Vector128 tb2, + Vector128 tb3) { - return Tbl(Vector, 16, Tb0, Tb1, Tb2, Tb3); + return Tbl(vector, 16, tb0, tb1, tb2, tb3); } - private static Vector128 Tbl(Vector128 Vector, int Bytes, params Vector128[] Tb) + private static Vector128 Tbl(Vector128 vector, int bytes, params Vector128[] tb) { - Vector128 Res = new Vector128(); + Vector128 res = new Vector128(); - byte[] Table = new byte[Tb.Length * 16]; + byte[] table = new byte[tb.Length * 16]; - for (byte Index = 0; Index < Tb.Length; Index++) - for (byte Index2 = 0; Index2 < 16; Index2++) + for (byte index = 0; index < tb.Length; index++) + for (byte index2 = 0; index2 < 16; index2++) { - Table[Index * 16 + Index2] = (byte)VectorExtractIntZx(Tb[Index], Index2, 0); + table[index * 16 + index2] = (byte)VectorExtractIntZx(tb[index], index2, 0); } - for (byte Index = 0; Index < Bytes; Index++) + for (byte index = 0; index < bytes; index++) { - byte TblIdx = (byte)VectorExtractIntZx(Vector, Index, 0); + byte tblIdx = (byte)VectorExtractIntZx(vector, index, 0); - if (TblIdx < Table.Length) + if (tblIdx < table.Length) { - Res = VectorInsertInt(Table[TblIdx], Res, Index, 0); + res = VectorInsertInt(table[tblIdx], res, index, 0); } } - return Res; + return res; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double VectorExtractDouble(Vector128 Vector, byte Index) + public static double VectorExtractDouble(Vector128 vector, byte index) { if (Sse41.IsSupported) { - return BitConverter.Int64BitsToDouble(Sse41.Extract(Sse.StaticCast(Vector), Index)); + return BitConverter.Int64BitsToDouble(Sse41.Extract(Sse.StaticCast(vector), index)); } else if (Sse2.IsSupported) { - return BitConverter.Int64BitsToDouble((long)VectorExtractIntZx(Vector, Index, 3)); + return BitConverter.Int64BitsToDouble((long)VectorExtractIntZx(vector, index, 3)); } throw new PlatformNotSupportedException(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static long VectorExtractIntSx(Vector128 Vector, byte Index, int Size) + public static long VectorExtractIntSx(Vector128 vector, byte index, int size) { if (Sse41.IsSupported) { - if (Size == 0) + if (size == 0) { - return (sbyte)Sse41.Extract(Sse.StaticCast(Vector), Index); + return (sbyte)Sse41.Extract(Sse.StaticCast(vector), index); } - else if (Size == 1) + else if (size == 1) { - return (short)Sse2.Extract(Sse.StaticCast(Vector), Index); + return (short)Sse2.Extract(Sse.StaticCast(vector), index); } - else if (Size == 2) + else if (size == 2) { - return Sse41.Extract(Sse.StaticCast(Vector), Index); + return Sse41.Extract(Sse.StaticCast(vector), index); } - else if (Size == 3) + else if (size == 3) { - return Sse41.Extract(Sse.StaticCast(Vector), Index); + return Sse41.Extract(Sse.StaticCast(vector), index); } else { - throw new ArgumentOutOfRangeException(nameof(Size)); + throw new ArgumentOutOfRangeException(nameof(size)); } } else if (Sse2.IsSupported) { - if (Size == 0) + if (size == 0) { - return (sbyte)VectorExtractIntZx(Vector, Index, Size); + return (sbyte)VectorExtractIntZx(vector, index, size); } - else if (Size == 1) + else if (size == 1) { - return (short)VectorExtractIntZx(Vector, Index, Size); + return (short)VectorExtractIntZx(vector, index, size); } - else if (Size == 2) + else if (size == 2) { - return (int)VectorExtractIntZx(Vector, Index, Size); + return (int)VectorExtractIntZx(vector, index, size); } - else if (Size == 3) + else if (size == 3) { - return (long)VectorExtractIntZx(Vector, Index, Size); + return (long)VectorExtractIntZx(vector, index, size); } else { - throw new ArgumentOutOfRangeException(nameof(Size)); + throw new ArgumentOutOfRangeException(nameof(size)); } } @@ -293,67 +293,67 @@ namespace ChocolArm64.Instruction } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ulong VectorExtractIntZx(Vector128 Vector, byte Index, int Size) + public static ulong VectorExtractIntZx(Vector128 vector, byte index, int size) { if (Sse41.IsSupported) { - if (Size == 0) + if (size == 0) { - return Sse41.Extract(Sse.StaticCast(Vector), Index); + return Sse41.Extract(Sse.StaticCast(vector), index); } - else if (Size == 1) + else if (size == 1) { - return Sse2.Extract(Sse.StaticCast(Vector), Index); + return Sse2.Extract(Sse.StaticCast(vector), index); } - else if (Size == 2) + else if (size == 2) { - return Sse41.Extract(Sse.StaticCast(Vector), Index); + return Sse41.Extract(Sse.StaticCast(vector), index); } - else if (Size == 3) + else if (size == 3) { - return Sse41.Extract(Sse.StaticCast(Vector), Index); + return Sse41.Extract(Sse.StaticCast(vector), index); } else { - throw new ArgumentOutOfRangeException(nameof(Size)); + throw new ArgumentOutOfRangeException(nameof(size)); } } else if (Sse2.IsSupported) { - int ShortIdx = Size == 0 - ? Index >> 1 - : Index << (Size - 1); + int shortIdx = size == 0 + ? index >> 1 + : index << (size - 1); - ushort Value = Sse2.Extract(Sse.StaticCast(Vector), (byte)ShortIdx); + ushort value = Sse2.Extract(Sse.StaticCast(vector), (byte)shortIdx); - if (Size == 0) + if (size == 0) { - return (byte)(Value >> (Index & 1) * 8); + return (byte)(value >> (index & 1) * 8); } - else if (Size == 1) + else if (size == 1) { - return Value; + return value; } - else if (Size == 2 || Size == 3) + else if (size == 2 || size == 3) { - ushort Value1 = Sse2.Extract(Sse.StaticCast(Vector), (byte)(ShortIdx + 1)); + ushort value1 = Sse2.Extract(Sse.StaticCast(vector), (byte)(shortIdx + 1)); - if (Size == 2) + if (size == 2) { - return (uint)(Value | (Value1 << 16)); + return (uint)(value | (value1 << 16)); } - ushort Value2 = Sse2.Extract(Sse.StaticCast(Vector), (byte)(ShortIdx + 2)); - ushort Value3 = Sse2.Extract(Sse.StaticCast(Vector), (byte)(ShortIdx + 3)); + ushort value2 = Sse2.Extract(Sse.StaticCast(vector), (byte)(shortIdx + 2)); + ushort value3 = Sse2.Extract(Sse.StaticCast(vector), (byte)(shortIdx + 3)); - return ((ulong)Value << 0) | - ((ulong)Value1 << 16) | - ((ulong)Value2 << 32) | - ((ulong)Value3 << 48); + return ((ulong)value << 0) | + ((ulong)value1 << 16) | + ((ulong)value2 << 32) | + ((ulong)value3 << 48); } else { - throw new ArgumentOutOfRangeException(nameof(Size)); + throw new ArgumentOutOfRangeException(nameof(size)); } } @@ -361,97 +361,97 @@ namespace ChocolArm64.Instruction } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float VectorExtractSingle(Vector128 Vector, byte Index) + public static float VectorExtractSingle(Vector128 vector, byte index) { if (Sse41.IsSupported) { - return Sse41.Extract(Vector, Index); + return Sse41.Extract(vector, index); } else if (Sse2.IsSupported) { - Vector128 ShortVector = Sse.StaticCast(Vector); + Vector128 shortVector = Sse.StaticCast(vector); - int Low = Sse2.Extract(ShortVector, (byte)(Index * 2 + 0)); - int High = Sse2.Extract(ShortVector, (byte)(Index * 2 + 1)); + int low = Sse2.Extract(shortVector, (byte)(index * 2 + 0)); + int high = Sse2.Extract(shortVector, (byte)(index * 2 + 1)); - return BitConverter.Int32BitsToSingle(Low | (High << 16)); + return BitConverter.Int32BitsToSingle(low | (high << 16)); } throw new PlatformNotSupportedException(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 VectorInsertDouble(double Value, Vector128 Vector, byte Index) + public static Vector128 VectorInsertDouble(double value, Vector128 vector, byte index) { - return VectorInsertInt((ulong)BitConverter.DoubleToInt64Bits(Value), Vector, Index, 3); + return VectorInsertInt((ulong)BitConverter.DoubleToInt64Bits(value), vector, index, 3); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 VectorInsertInt(ulong Value, Vector128 Vector, byte Index, int Size) + public static Vector128 VectorInsertInt(ulong value, Vector128 vector, byte index, int size) { if (Sse41.IsSupported) { - if (Size == 0) + if (size == 0) { - return Sse.StaticCast(Sse41.Insert(Sse.StaticCast(Vector), (byte)Value, Index)); + return Sse.StaticCast(Sse41.Insert(Sse.StaticCast(vector), (byte)value, index)); } - else if (Size == 1) + else if (size == 1) { - return Sse.StaticCast(Sse2.Insert(Sse.StaticCast(Vector), (ushort)Value, Index)); + return Sse.StaticCast(Sse2.Insert(Sse.StaticCast(vector), (ushort)value, index)); } - else if (Size == 2) + else if (size == 2) { - return Sse.StaticCast(Sse41.Insert(Sse.StaticCast(Vector), (uint)Value, Index)); + return Sse.StaticCast(Sse41.Insert(Sse.StaticCast(vector), (uint)value, index)); } - else if (Size == 3) + else if (size == 3) { - return Sse.StaticCast(Sse41.Insert(Sse.StaticCast(Vector), Value, Index)); + return Sse.StaticCast(Sse41.Insert(Sse.StaticCast(vector), value, index)); } else { - throw new ArgumentOutOfRangeException(nameof(Size)); + throw new ArgumentOutOfRangeException(nameof(size)); } } else if (Sse2.IsSupported) { - Vector128 ShortVector = Sse.StaticCast(Vector); + Vector128 shortVector = Sse.StaticCast(vector); - int ShortIdx = Size == 0 - ? Index >> 1 - : Index << (Size - 1); + int shortIdx = size == 0 + ? index >> 1 + : index << (size - 1); - if (Size == 0) + if (size == 0) { - ushort ShortVal = Sse2.Extract(Sse.StaticCast(Vector), (byte)ShortIdx); + ushort shortVal = Sse2.Extract(Sse.StaticCast(vector), (byte)shortIdx); - int Shift = (Index & 1) * 8; + int shift = (index & 1) * 8; - ShortVal &= (ushort)(0xff00 >> Shift); + shortVal &= (ushort)(0xff00 >> shift); - ShortVal |= (ushort)((byte)Value << Shift); + shortVal |= (ushort)((byte)value << shift); - return Sse.StaticCast(Sse2.Insert(ShortVector, ShortVal, (byte)ShortIdx)); + return Sse.StaticCast(Sse2.Insert(shortVector, shortVal, (byte)shortIdx)); } - else if (Size == 1) + else if (size == 1) { - return Sse.StaticCast(Sse2.Insert(Sse.StaticCast(Vector), (ushort)Value, Index)); + return Sse.StaticCast(Sse2.Insert(Sse.StaticCast(vector), (ushort)value, index)); } - else if (Size == 2 || Size == 3) + else if (size == 2 || size == 3) { - ShortVector = Sse2.Insert(ShortVector, (ushort)(Value >> 0), (byte)(ShortIdx + 0)); - ShortVector = Sse2.Insert(ShortVector, (ushort)(Value >> 16), (byte)(ShortIdx + 1)); + shortVector = Sse2.Insert(shortVector, (ushort)(value >> 0), (byte)(shortIdx + 0)); + shortVector = Sse2.Insert(shortVector, (ushort)(value >> 16), (byte)(shortIdx + 1)); - if (Size == 3) + if (size == 3) { - ShortVector = Sse2.Insert(ShortVector, (ushort)(Value >> 32), (byte)(ShortIdx + 2)); - ShortVector = Sse2.Insert(ShortVector, (ushort)(Value >> 48), (byte)(ShortIdx + 3)); + shortVector = Sse2.Insert(shortVector, (ushort)(value >> 32), (byte)(shortIdx + 2)); + shortVector = Sse2.Insert(shortVector, (ushort)(value >> 48), (byte)(shortIdx + 3)); } - return Sse.StaticCast(ShortVector); + return Sse.StaticCast(shortVector); } else { - throw new ArgumentOutOfRangeException(nameof(Size)); + throw new ArgumentOutOfRangeException(nameof(size)); } } @@ -459,57 +459,57 @@ namespace ChocolArm64.Instruction } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 VectorInsertSingle(float Value, Vector128 Vector, byte Index) + public static Vector128 VectorInsertSingle(float value, Vector128 vector, byte index) { if (Sse41.IsSupported) { //Note: The if/else if is necessary to enable the JIT to //produce a single INSERTPS instruction instead of the //jump table fallback. - if (Index == 0) + if (index == 0) { - return Sse41.Insert(Vector, Value, 0x00); + return Sse41.Insert(vector, value, 0x00); } - else if (Index == 1) + else if (index == 1) { - return Sse41.Insert(Vector, Value, 0x10); + return Sse41.Insert(vector, value, 0x10); } - else if (Index == 2) + else if (index == 2) { - return Sse41.Insert(Vector, Value, 0x20); + return Sse41.Insert(vector, value, 0x20); } - else if (Index == 3) + else if (index == 3) { - return Sse41.Insert(Vector, Value, 0x30); + return Sse41.Insert(vector, value, 0x30); } else { - throw new ArgumentOutOfRangeException(nameof(Index)); + throw new ArgumentOutOfRangeException(nameof(index)); } } else if (Sse2.IsSupported) { - int IntValue = BitConverter.SingleToInt32Bits(Value); + int intValue = BitConverter.SingleToInt32Bits(value); - ushort Low = (ushort)(IntValue >> 0); - ushort High = (ushort)(IntValue >> 16); + ushort low = (ushort)(intValue >> 0); + ushort high = (ushort)(intValue >> 16); - Vector128 ShortVector = Sse.StaticCast(Vector); + Vector128 shortVector = Sse.StaticCast(vector); - ShortVector = Sse2.Insert(ShortVector, Low, (byte)(Index * 2 + 0)); - ShortVector = Sse2.Insert(ShortVector, High, (byte)(Index * 2 + 1)); + shortVector = Sse2.Insert(shortVector, low, (byte)(index * 2 + 0)); + shortVector = Sse2.Insert(shortVector, high, (byte)(index * 2 + 1)); - return Sse.StaticCast(ShortVector); + return Sse.StaticCast(shortVector); } throw new PlatformNotSupportedException(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 Sse41VectorInsertScalarSingle(float Value, Vector128 Vector) + public static Vector128 Sse41VectorInsertScalarSingle(float value, Vector128 vector) { //Note: 0b1110 is the mask to zero the upper bits. - return Sse41.Insert(Vector, Value, 0b1110); + return Sse41.Insert(vector, value, 0b1110); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -579,209 +579,209 @@ namespace ChocolArm64.Instruction } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 VectorZero32_128(Vector128 Vector) + public static Vector128 VectorZero32_128(Vector128 vector) { if (Sse.IsSupported) { - return Sse.And(Vector, Zero32_128Mask); + return Sse.And(vector, Zero32128Mask); } throw new PlatformNotSupportedException(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 VectorSingleToSByte(Vector128 Vector) + public static Vector128 VectorSingleToSByte(Vector128 vector) { if (Sse.IsSupported) { - return Sse.StaticCast(Vector); + return Sse.StaticCast(vector); } throw new PlatformNotSupportedException(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 VectorSingleToInt16(Vector128 Vector) + public static Vector128 VectorSingleToInt16(Vector128 vector) { if (Sse.IsSupported) { - return Sse.StaticCast(Vector); + return Sse.StaticCast(vector); } throw new PlatformNotSupportedException(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 VectorSingleToInt32(Vector128 Vector) + public static Vector128 VectorSingleToInt32(Vector128 vector) { if (Sse.IsSupported) { - return Sse.StaticCast(Vector); + return Sse.StaticCast(vector); } throw new PlatformNotSupportedException(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 VectorSingleToInt64(Vector128 Vector) + public static Vector128 VectorSingleToInt64(Vector128 vector) { if (Sse.IsSupported) { - return Sse.StaticCast(Vector); + return Sse.StaticCast(vector); } throw new PlatformNotSupportedException(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 VectorSingleToByte(Vector128 Vector) + public static Vector128 VectorSingleToByte(Vector128 vector) { if (Sse.IsSupported) { - return Sse.StaticCast(Vector); + return Sse.StaticCast(vector); } throw new PlatformNotSupportedException(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 VectorSingleToUInt16(Vector128 Vector) + public static Vector128 VectorSingleToUInt16(Vector128 vector) { if (Sse.IsSupported) { - return Sse.StaticCast(Vector); + return Sse.StaticCast(vector); } throw new PlatformNotSupportedException(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 VectorSingleToUInt32(Vector128 Vector) + public static Vector128 VectorSingleToUInt32(Vector128 vector) { if (Sse.IsSupported) { - return Sse.StaticCast(Vector); + return Sse.StaticCast(vector); } throw new PlatformNotSupportedException(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 VectorSingleToUInt64(Vector128 Vector) + public static Vector128 VectorSingleToUInt64(Vector128 vector) { if (Sse.IsSupported) { - return Sse.StaticCast(Vector); + return Sse.StaticCast(vector); } throw new PlatformNotSupportedException(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 VectorSingleToDouble(Vector128 Vector) + public static Vector128 VectorSingleToDouble(Vector128 vector) { if (Sse.IsSupported) { - return Sse.StaticCast(Vector); + return Sse.StaticCast(vector); } throw new PlatformNotSupportedException(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 VectorSByteToSingle(Vector128 Vector) + public static Vector128 VectorSByteToSingle(Vector128 vector) { if (Sse.IsSupported) { - return Sse.StaticCast(Vector); + return Sse.StaticCast(vector); } throw new PlatformNotSupportedException(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 VectorInt16ToSingle(Vector128 Vector) + public static Vector128 VectorInt16ToSingle(Vector128 vector) { if (Sse.IsSupported) { - return Sse.StaticCast(Vector); + return Sse.StaticCast(vector); } throw new PlatformNotSupportedException(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 VectorInt32ToSingle(Vector128 Vector) + public static Vector128 VectorInt32ToSingle(Vector128 vector) { if (Sse.IsSupported) { - return Sse.StaticCast(Vector); + return Sse.StaticCast(vector); } throw new PlatformNotSupportedException(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 VectorInt64ToSingle(Vector128 Vector) + public static Vector128 VectorInt64ToSingle(Vector128 vector) { if (Sse.IsSupported) { - return Sse.StaticCast(Vector); + return Sse.StaticCast(vector); } throw new PlatformNotSupportedException(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 VectorByteToSingle(Vector128 Vector) + public static Vector128 VectorByteToSingle(Vector128 vector) { if (Sse.IsSupported) { - return Sse.StaticCast(Vector); + return Sse.StaticCast(vector); } throw new PlatformNotSupportedException(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 VectorUInt16ToSingle(Vector128 Vector) + public static Vector128 VectorUInt16ToSingle(Vector128 vector) { if (Sse.IsSupported) { - return Sse.StaticCast(Vector); + return Sse.StaticCast(vector); } throw new PlatformNotSupportedException(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 VectorUInt32ToSingle(Vector128 Vector) + public static Vector128 VectorUInt32ToSingle(Vector128 vector) { if (Sse.IsSupported) { - return Sse.StaticCast(Vector); + return Sse.StaticCast(vector); } throw new PlatformNotSupportedException(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 VectorUInt64ToSingle(Vector128 Vector) + public static Vector128 VectorUInt64ToSingle(Vector128 vector) { if (Sse.IsSupported) { - return Sse.StaticCast(Vector); + return Sse.StaticCast(vector); } throw new PlatformNotSupportedException(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 VectorDoubleToSingle(Vector128 Vector) + public static Vector128 VectorDoubleToSingle(Vector128 vector) { if (Sse.IsSupported) { - return Sse.StaticCast(Vector); + return Sse.StaticCast(vector); } throw new PlatformNotSupportedException(); diff --git a/Instruction32/A32InstInterpretAlu.cs b/Instructions32/A32InstInterpretAlu.cs similarity index 60% rename from Instruction32/A32InstInterpretAlu.cs rename to Instructions32/A32InstInterpretAlu.cs index 41b9d22..f3be823 100644 --- a/Instruction32/A32InstInterpretAlu.cs +++ b/Instructions32/A32InstInterpretAlu.cs @@ -1,4 +1,4 @@ -namespace ChocolArm64.Instruction32 +namespace ChocolArm64.Instructions32 { static partial class A32InstInterpret { diff --git a/Instructions32/A32InstInterpretFlow.cs b/Instructions32/A32InstInterpretFlow.cs new file mode 100644 index 0000000..cdf7e4c --- /dev/null +++ b/Instructions32/A32InstInterpretFlow.cs @@ -0,0 +1,70 @@ +using ChocolArm64.Decoders; +using ChocolArm64.Decoders32; +using ChocolArm64.Memory; +using ChocolArm64.State; + +using static ChocolArm64.Instructions32.A32InstInterpretHelper; + +namespace ChocolArm64.Instructions32 +{ + static partial class A32InstInterpret + { + public static void B(CpuThreadState state, MemoryManager memory, OpCode64 opCode) + { + A32OpCodeBImmAl op = (A32OpCodeBImmAl)opCode; + + if (IsConditionTrue(state, op.Cond)) + { + BranchWritePc(state, GetPc(state) + (uint)op.Imm); + } + } + + public static void Bl(CpuThreadState state, MemoryManager memory, OpCode64 opCode) + { + Blx(state, memory, opCode, false); + } + + public static void Blx(CpuThreadState state, MemoryManager memory, OpCode64 opCode) + { + Blx(state, memory, opCode, true); + } + + public static void Blx(CpuThreadState state, MemoryManager memory, OpCode64 opCode, bool x) + { + A32OpCodeBImmAl op = (A32OpCodeBImmAl)opCode; + + if (IsConditionTrue(state, op.Cond)) + { + uint pc = GetPc(state); + + if (state.Thumb) + { + state.R14 = pc | 1; + } + else + { + state.R14 = pc - 4U; + } + + if (x) + { + state.Thumb = !state.Thumb; + } + + if (!state.Thumb) + { + pc &= ~3U; + } + + BranchWritePc(state, pc + (uint)op.Imm); + } + } + + private static void BranchWritePc(CpuThreadState state, uint pc) + { + state.R15 = state.Thumb + ? pc & ~1U + : pc & ~3U; + } + } +} \ No newline at end of file diff --git a/Instructions32/A32InstInterpretHelper.cs b/Instructions32/A32InstInterpretHelper.cs new file mode 100644 index 0000000..b08e129 --- /dev/null +++ b/Instructions32/A32InstInterpretHelper.cs @@ -0,0 +1,65 @@ +using ChocolArm64.Decoders; +using ChocolArm64.State; +using System; + +namespace ChocolArm64.Instructions32 +{ + static class A32InstInterpretHelper + { + public static bool IsConditionTrue(CpuThreadState state, Cond cond) + { + switch (cond) + { + case Cond.Eq: return state.Zero; + case Cond.Ne: return !state.Zero; + case Cond.GeUn: return state.Carry; + case Cond.LtUn: return !state.Carry; + case Cond.Mi: return state.Negative; + case Cond.Pl: return !state.Negative; + case Cond.Vs: return state.Overflow; + case Cond.Vc: return !state.Overflow; + case Cond.GtUn: return state.Carry && !state.Zero; + case Cond.LeUn: return !state.Carry && state.Zero; + case Cond.Ge: return state.Negative == state.Overflow; + case Cond.Lt: return state.Negative != state.Overflow; + case Cond.Gt: return state.Negative == state.Overflow && !state.Zero; + case Cond.Le: return state.Negative != state.Overflow && state.Zero; + } + + return true; + } + + public unsafe static uint GetReg(CpuThreadState state, int reg) + { + if ((uint)reg > 15) + { + throw new ArgumentOutOfRangeException(nameof(reg)); + } + + fixed (uint* ptr = &state.R0) + { + return *(ptr + reg); + } + } + + public unsafe static void SetReg(CpuThreadState state, int reg, uint value) + { + if ((uint)reg > 15) + { + throw new ArgumentOutOfRangeException(nameof(reg)); + } + + fixed (uint* ptr = &state.R0) + { + *(ptr + reg) = value; + } + } + + public static uint GetPc(CpuThreadState state) + { + //Due to the old fetch-decode-execute pipeline of old ARM CPUs, + //the PC is 4 or 8 bytes (2 instructions) ahead of the current instruction. + return state.R15 + (state.Thumb ? 2U : 4U); + } + } +} \ No newline at end of file diff --git a/Memory/AMemory.cs b/Memory/AMemory.cs deleted file mode 100644 index 2854871..0000000 --- a/Memory/AMemory.cs +++ /dev/null @@ -1,745 +0,0 @@ -using ChocolArm64.Events; -using ChocolArm64.Exceptions; -using ChocolArm64.State; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Runtime.Intrinsics; -using System.Runtime.Intrinsics.X86; -using System.Threading; - -namespace ChocolArm64.Memory -{ - public unsafe class AMemory : IAMemory, IDisposable - { - private const int PTLvl0Bits = 13; - private const int PTLvl1Bits = 14; - private const int PTPageBits = 12; - - private const int PTLvl0Size = 1 << PTLvl0Bits; - private const int PTLvl1Size = 1 << PTLvl1Bits; - public const int PageSize = 1 << PTPageBits; - - private const int PTLvl0Mask = PTLvl0Size - 1; - private const int PTLvl1Mask = PTLvl1Size - 1; - public const int PageMask = PageSize - 1; - - private const int PTLvl0Bit = PTPageBits + PTLvl1Bits; - private const int PTLvl1Bit = PTPageBits; - - private const long ErgMask = (4 << AThreadState.ErgSizeLog2) - 1; - - private class ArmMonitor - { - public long Position; - public bool ExState; - - public bool HasExclusiveAccess(long Position) - { - return this.Position == Position && ExState; - } - } - - private Dictionary Monitors; - - private ConcurrentDictionary ObservedPages; - - public IntPtr Ram { get; private set; } - - private byte* RamPtr; - - private byte*** PageTable; - - public event EventHandler InvalidAccess; - - public AMemory(IntPtr Ram) - { - Monitors = new Dictionary(); - - ObservedPages = new ConcurrentDictionary(); - - this.Ram = Ram; - - RamPtr = (byte*)Ram; - - PageTable = (byte***)Marshal.AllocHGlobal(PTLvl0Size * IntPtr.Size); - - for (int L0 = 0; L0 < PTLvl0Size; L0++) - { - PageTable[L0] = null; - } - } - - public void RemoveMonitor(int Core) - { - lock (Monitors) - { - ClearExclusive(Core); - - Monitors.Remove(Core); - } - } - - public void SetExclusive(int Core, long Position) - { - Position &= ~ErgMask; - - lock (Monitors) - { - foreach (ArmMonitor Mon in Monitors.Values) - { - if (Mon.Position == Position && Mon.ExState) - { - Mon.ExState = false; - } - } - - if (!Monitors.TryGetValue(Core, out ArmMonitor ThreadMon)) - { - ThreadMon = new ArmMonitor(); - - Monitors.Add(Core, ThreadMon); - } - - ThreadMon.Position = Position; - ThreadMon.ExState = true; - } - } - - public bool TestExclusive(int Core, long Position) - { - //Note: Any call to this method also should be followed by a - //call to ClearExclusiveForStore if this method returns true. - Position &= ~ErgMask; - - Monitor.Enter(Monitors); - - if (!Monitors.TryGetValue(Core, out ArmMonitor ThreadMon)) - { - return false; - } - - bool ExState = ThreadMon.HasExclusiveAccess(Position); - - if (!ExState) - { - Monitor.Exit(Monitors); - } - - return ExState; - } - - public void ClearExclusiveForStore(int Core) - { - if (Monitors.TryGetValue(Core, out ArmMonitor ThreadMon)) - { - ThreadMon.ExState = false; - } - - Monitor.Exit(Monitors); - } - - public void ClearExclusive(int Core) - { - lock (Monitors) - { - if (Monitors.TryGetValue(Core, out ArmMonitor ThreadMon)) - { - ThreadMon.ExState = false; - } - } - } - - public void WriteInt32ToSharedAddr(long Position, int Value) - { - long MaskedPosition = Position & ~ErgMask; - - lock (Monitors) - { - foreach (ArmMonitor Mon in Monitors.Values) - { - if (Mon.Position == MaskedPosition && Mon.ExState) - { - Mon.ExState = false; - } - } - - WriteInt32(Position, Value); - } - } - - public sbyte ReadSByte(long Position) - { - return (sbyte)ReadByte(Position); - } - - public short ReadInt16(long Position) - { - return (short)ReadUInt16(Position); - } - - public int ReadInt32(long Position) - { - return (int)ReadUInt32(Position); - } - - public long ReadInt64(long Position) - { - return (long)ReadUInt64(Position); - } - - public byte ReadByte(long Position) - { - return *((byte*)Translate(Position)); - } - - public ushort ReadUInt16(long Position) - { - return *((ushort*)Translate(Position)); - } - - public uint ReadUInt32(long Position) - { - return *((uint*)Translate(Position)); - } - - public ulong ReadUInt64(long Position) - { - return *((ulong*)Translate(Position)); - } - - public Vector128 ReadVector8(long Position) - { - if (Sse2.IsSupported) - { - return Sse.StaticCast(Sse2.SetVector128(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ReadByte(Position))); - } - else - { - throw new PlatformNotSupportedException(); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector128 ReadVector16(long Position) - { - if (Sse2.IsSupported) - { - return Sse.StaticCast(Sse2.Insert(Sse2.SetZeroVector128(), ReadUInt16(Position), 0)); - } - else - { - throw new PlatformNotSupportedException(); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector128 ReadVector32(long Position) - { - if (Sse.IsSupported) - { - return Sse.LoadScalarVector128((float*)Translate(Position)); - } - else - { - throw new PlatformNotSupportedException(); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector128 ReadVector64(long Position) - { - if (Sse2.IsSupported) - { - return Sse.StaticCast(Sse2.LoadScalarVector128((double*)Translate(Position))); - } - else - { - throw new PlatformNotSupportedException(); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector128 ReadVector128(long Position) - { - if (Sse.IsSupported) - { - return Sse.LoadVector128((float*)Translate(Position)); - } - else - { - throw new PlatformNotSupportedException(); - } - } - - public byte[] ReadBytes(long Position, long Size) - { - if ((uint)Size > int.MaxValue) - { - throw new ArgumentOutOfRangeException(nameof(Size)); - } - - EnsureRangeIsValid(Position, Size); - - byte[] Data = new byte[Size]; - - Marshal.Copy((IntPtr)Translate(Position), Data, 0, (int)Size); - - return Data; - } - - public void ReadBytes(long Position, byte[] Data, int StartIndex, int Size) - { - //Note: This will be moved later. - EnsureRangeIsValid(Position, (uint)Size); - - Marshal.Copy((IntPtr)Translate(Position), Data, StartIndex, Size); - } - - public void WriteSByte(long Position, sbyte Value) - { - WriteByte(Position, (byte)Value); - } - - public void WriteInt16(long Position, short Value) - { - WriteUInt16(Position, (ushort)Value); - } - - public void WriteInt32(long Position, int Value) - { - WriteUInt32(Position, (uint)Value); - } - - public void WriteInt64(long Position, long Value) - { - WriteUInt64(Position, (ulong)Value); - } - - public void WriteByte(long Position, byte Value) - { - *((byte*)TranslateWrite(Position)) = Value; - } - - public void WriteUInt16(long Position, ushort Value) - { - *((ushort*)TranslateWrite(Position)) = Value; - } - - public void WriteUInt32(long Position, uint Value) - { - *((uint*)TranslateWrite(Position)) = Value; - } - - public void WriteUInt64(long Position, ulong Value) - { - *((ulong*)TranslateWrite(Position)) = Value; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteVector8(long Position, Vector128 Value) - { - if (Sse41.IsSupported) - { - WriteByte(Position, Sse41.Extract(Sse.StaticCast(Value), 0)); - } - else if (Sse2.IsSupported) - { - WriteByte(Position, (byte)Sse2.Extract(Sse.StaticCast(Value), 0)); - } - else - { - throw new PlatformNotSupportedException(); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteVector16(long Position, Vector128 Value) - { - if (Sse2.IsSupported) - { - WriteUInt16(Position, Sse2.Extract(Sse.StaticCast(Value), 0)); - } - else - { - throw new PlatformNotSupportedException(); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteVector32(long Position, Vector128 Value) - { - if (Sse.IsSupported) - { - Sse.StoreScalar((float*)TranslateWrite(Position), Value); - } - else - { - throw new PlatformNotSupportedException(); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteVector64(long Position, Vector128 Value) - { - if (Sse2.IsSupported) - { - Sse2.StoreScalar((double*)TranslateWrite(Position), Sse.StaticCast(Value)); - } - else - { - throw new PlatformNotSupportedException(); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteVector128(long Position, Vector128 Value) - { - if (Sse.IsSupported) - { - Sse.Store((float*)TranslateWrite(Position), Value); - } - else - { - throw new PlatformNotSupportedException(); - } - } - - public void WriteBytes(long Position, byte[] Data) - { - EnsureRangeIsValid(Position, (uint)Data.Length); - - Marshal.Copy(Data, 0, (IntPtr)TranslateWrite(Position), Data.Length); - } - - public void WriteBytes(long Position, byte[] Data, int StartIndex, int Size) - { - //Note: This will be moved later. - //Using Translate instead of TranslateWrite is on purpose. - EnsureRangeIsValid(Position, (uint)Size); - - Marshal.Copy(Data, StartIndex, (IntPtr)Translate(Position), Size); - } - - public void CopyBytes(long Src, long Dst, long Size) - { - //Note: This will be moved later. - EnsureRangeIsValid(Src, Size); - EnsureRangeIsValid(Dst, Size); - - byte* SrcPtr = Translate(Src); - byte* DstPtr = TranslateWrite(Dst); - - Buffer.MemoryCopy(SrcPtr, DstPtr, Size, Size); - } - - public void Map(long VA, long PA, long Size) - { - SetPTEntries(VA, RamPtr + PA, Size); - } - - public void Unmap(long Position, long Size) - { - SetPTEntries(Position, null, Size); - - StopObservingRegion(Position, Size); - } - - public bool IsMapped(long Position) - { - if (!(IsValidPosition(Position))) - { - return false; - } - - long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask; - long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask; - - if (PageTable[L0] == null) - { - return false; - } - - return PageTable[L0][L1] != null || ObservedPages.ContainsKey(Position >> PTPageBits); - } - - public long GetPhysicalAddress(long VirtualAddress) - { - byte* Ptr = Translate(VirtualAddress); - - return (long)(Ptr - RamPtr); - } - - internal byte* Translate(long Position) - { - long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask; - long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask; - - long Old = Position; - - byte** Lvl1 = PageTable[L0]; - - if ((Position >> (PTLvl0Bit + PTLvl0Bits)) != 0) - { - goto Unmapped; - } - - if (Lvl1 == null) - { - goto Unmapped; - } - - Position &= PageMask; - - byte* Ptr = Lvl1[L1]; - - if (Ptr == null) - { - goto Unmapped; - } - - return Ptr + Position; - -Unmapped: - return HandleNullPte(Old); - } - - private byte* HandleNullPte(long Position) - { - long Key = Position >> PTPageBits; - - if (ObservedPages.TryGetValue(Key, out IntPtr Ptr)) - { - return (byte*)Ptr + (Position & PageMask); - } - - InvalidAccess?.Invoke(this, new AInvalidAccessEventArgs(Position)); - - throw new VmmPageFaultException(Position); - } - - internal byte* TranslateWrite(long Position) - { - long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask; - long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask; - - long Old = Position; - - byte** Lvl1 = PageTable[L0]; - - if ((Position >> (PTLvl0Bit + PTLvl0Bits)) != 0) - { - goto Unmapped; - } - - if (Lvl1 == null) - { - goto Unmapped; - } - - Position &= PageMask; - - byte* Ptr = Lvl1[L1]; - - if (Ptr == null) - { - goto Unmapped; - } - - return Ptr + Position; - -Unmapped: - return HandleNullPteWrite(Old); - } - - private byte* HandleNullPteWrite(long Position) - { - long Key = Position >> PTPageBits; - - if (ObservedPages.TryGetValue(Key, out IntPtr Ptr)) - { - SetPTEntry(Position, (byte*)Ptr); - - return (byte*)Ptr + (Position & PageMask); - } - - InvalidAccess?.Invoke(this, new AInvalidAccessEventArgs(Position)); - - throw new VmmPageFaultException(Position); - } - - private void SetPTEntries(long VA, byte* Ptr, long Size) - { - long EndPosition = (VA + Size + PageMask) & ~PageMask; - - while ((ulong)VA < (ulong)EndPosition) - { - SetPTEntry(VA, Ptr); - - VA += PageSize; - - if (Ptr != null) - { - Ptr += PageSize; - } - } - } - - private void SetPTEntry(long Position, byte* Ptr) - { - if (!IsValidPosition(Position)) - { - throw new ArgumentOutOfRangeException(nameof(Position)); - } - - long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask; - long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask; - - if (PageTable[L0] == null) - { - byte** Lvl1 = (byte**)Marshal.AllocHGlobal(PTLvl1Size * IntPtr.Size); - - for (int ZL1 = 0; ZL1 < PTLvl1Size; ZL1++) - { - Lvl1[ZL1] = null; - } - - Thread.MemoryBarrier(); - - PageTable[L0] = Lvl1; - } - - PageTable[L0][L1] = Ptr; - } - - public (bool[], int) IsRegionModified(long Position, long Size) - { - long EndPosition = (Position + Size + PageMask) & ~PageMask; - - Position &= ~PageMask; - - Size = EndPosition - Position; - - bool[] Modified = new bool[Size >> PTPageBits]; - - int Count = 0; - - lock (ObservedPages) - { - for (int Page = 0; Page < Modified.Length; Page++) - { - byte* Ptr = Translate(Position); - - if (ObservedPages.TryAdd(Position >> PTPageBits, (IntPtr)Ptr)) - { - Modified[Page] = true; - - Count++; - } - else - { - long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask; - long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask; - - byte** Lvl1 = PageTable[L0]; - - if (Lvl1 != null) - { - if (Modified[Page] = Lvl1[L1] != null) - { - Count++; - } - } - } - - SetPTEntry(Position, null); - - Position += PageSize; - } - } - - return (Modified, Count); - } - - public void StopObservingRegion(long Position, long Size) - { - long EndPosition = (Position + Size + PageMask) & ~PageMask; - - while (Position < EndPosition) - { - lock (ObservedPages) - { - if (ObservedPages.TryRemove(Position >> PTPageBits, out IntPtr Ptr)) - { - SetPTEntry(Position, (byte*)Ptr); - } - } - - Position += PageSize; - } - } - - public IntPtr GetHostAddress(long Position, long Size) - { - EnsureRangeIsValid(Position, Size); - - return (IntPtr)Translate(Position); - } - - internal void EnsureRangeIsValid(long Position, long Size) - { - long EndPos = Position + Size; - - Position &= ~PageMask; - - long ExpectedPA = GetPhysicalAddress(Position); - - while ((ulong)Position < (ulong)EndPos) - { - long PA = GetPhysicalAddress(Position); - - if (PA != ExpectedPA) - { - throw new VmmAccessException(Position, Size); - } - - Position += PageSize; - ExpectedPA += PageSize; - } - } - - public bool IsValidPosition(long Position) - { - return Position >> (PTLvl0Bits + PTLvl1Bits + PTPageBits) == 0; - } - - public void Dispose() - { - Dispose(true); - } - - protected virtual void Dispose(bool disposing) - { - if (PageTable == null) - { - return; - } - - for (int L0 = 0; L0 < PTLvl0Size; L0++) - { - if (PageTable[L0] != null) - { - Marshal.FreeHGlobal((IntPtr)PageTable[L0]); - } - - PageTable[L0] = null; - } - - Marshal.FreeHGlobal((IntPtr)PageTable); - - PageTable = null; - } - } -} \ No newline at end of file diff --git a/Memory/AMemoryHelper.cs b/Memory/AMemoryHelper.cs deleted file mode 100644 index ea87783..0000000 --- a/Memory/AMemoryHelper.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System; -using System.IO; -using System.Runtime.InteropServices; -using System.Text; - -namespace ChocolArm64.Memory -{ - public static class AMemoryHelper - { - public static void FillWithZeros(AMemory Memory, long Position, int Size) - { - int Size8 = Size & ~(8 - 1); - - for (int Offs = 0; Offs < Size8; Offs += 8) - { - Memory.WriteInt64(Position + Offs, 0); - } - - for (int Offs = Size8; Offs < (Size - Size8); Offs++) - { - Memory.WriteByte(Position + Offs, 0); - } - } - - public unsafe static T Read(AMemory Memory, long Position) where T : struct - { - long Size = Marshal.SizeOf(); - - Memory.EnsureRangeIsValid(Position, Size); - - IntPtr Ptr = (IntPtr)Memory.Translate(Position); - - return Marshal.PtrToStructure(Ptr); - } - - public unsafe static void Write(AMemory Memory, long Position, T Value) where T : struct - { - long Size = Marshal.SizeOf(); - - Memory.EnsureRangeIsValid(Position, Size); - - IntPtr Ptr = (IntPtr)Memory.TranslateWrite(Position); - - Marshal.StructureToPtr(Value, Ptr, false); - } - - public static string ReadAsciiString(AMemory Memory, long Position, long MaxSize = -1) - { - using (MemoryStream MS = new MemoryStream()) - { - for (long Offs = 0; Offs < MaxSize || MaxSize == -1; Offs++) - { - byte Value = (byte)Memory.ReadByte(Position + Offs); - - if (Value == 0) - { - break; - } - - MS.WriteByte(Value); - } - - return Encoding.ASCII.GetString(MS.ToArray()); - } - } - } -} \ No newline at end of file diff --git a/Memory/IAMemory.cs b/Memory/IAMemory.cs deleted file mode 100644 index 5b7d17b..0000000 --- a/Memory/IAMemory.cs +++ /dev/null @@ -1,37 +0,0 @@ -namespace ChocolArm64.Memory -{ - public interface IAMemory - { - sbyte ReadSByte(long Position); - - short ReadInt16(long Position); - - int ReadInt32(long Position); - - long ReadInt64(long Position); - - byte ReadByte(long Position); - - ushort ReadUInt16(long Position); - - uint ReadUInt32(long Position); - - ulong ReadUInt64(long Position); - - void WriteSByte(long Position, sbyte Value); - - void WriteInt16(long Position, short Value); - - void WriteInt32(long Position, int Value); - - void WriteInt64(long Position, long Value); - - void WriteByte(long Position, byte Value); - - void WriteUInt16(long Position, ushort Value); - - void WriteUInt32(long Position, uint Value); - - void WriteUInt64(long Position, ulong Value); - } -} \ No newline at end of file diff --git a/Memory/IMemory.cs b/Memory/IMemory.cs new file mode 100644 index 0000000..dc58215 --- /dev/null +++ b/Memory/IMemory.cs @@ -0,0 +1,37 @@ +namespace ChocolArm64.Memory +{ + public interface IMemory + { + sbyte ReadSByte(long position); + + short ReadInt16(long position); + + int ReadInt32(long position); + + long ReadInt64(long position); + + byte ReadByte(long position); + + ushort ReadUInt16(long position); + + uint ReadUInt32(long position); + + ulong ReadUInt64(long position); + + void WriteSByte(long position, sbyte value); + + void WriteInt16(long position, short value); + + void WriteInt32(long position, int value); + + void WriteInt64(long position, long value); + + void WriteByte(long position, byte value); + + void WriteUInt16(long position, ushort value); + + void WriteUInt32(long position, uint value); + + void WriteUInt64(long position, ulong value); + } +} \ No newline at end of file diff --git a/Memory/MemoryHelper.cs b/Memory/MemoryHelper.cs new file mode 100644 index 0000000..2e721af --- /dev/null +++ b/Memory/MemoryHelper.cs @@ -0,0 +1,67 @@ +using System; +using System.IO; +using System.Runtime.InteropServices; +using System.Text; + +namespace ChocolArm64.Memory +{ + public static class MemoryHelper + { + public static void FillWithZeros(MemoryManager memory, long position, int size) + { + int size8 = size & ~(8 - 1); + + for (int offs = 0; offs < size8; offs += 8) + { + memory.WriteInt64(position + offs, 0); + } + + for (int offs = size8; offs < (size - size8); offs++) + { + memory.WriteByte(position + offs, 0); + } + } + + public unsafe static T Read(MemoryManager memory, long position) where T : struct + { + long size = Marshal.SizeOf(); + + memory.EnsureRangeIsValid(position, size); + + IntPtr ptr = (IntPtr)memory.Translate(position); + + return Marshal.PtrToStructure(ptr); + } + + public unsafe static void Write(MemoryManager memory, long position, T value) where T : struct + { + long size = Marshal.SizeOf(); + + memory.EnsureRangeIsValid(position, size); + + IntPtr ptr = (IntPtr)memory.TranslateWrite(position); + + Marshal.StructureToPtr(value, ptr, false); + } + + public static string ReadAsciiString(MemoryManager memory, long position, long maxSize = -1) + { + using (MemoryStream ms = new MemoryStream()) + { + for (long offs = 0; offs < maxSize || maxSize == -1; offs++) + { + byte value = (byte)memory.ReadByte(position + offs); + + if (value == 0) + { + break; + } + + ms.WriteByte(value); + } + + return Encoding.ASCII.GetString(ms.ToArray()); + } + } + } +} \ No newline at end of file diff --git a/Memory/MemoryManager.cs b/Memory/MemoryManager.cs new file mode 100644 index 0000000..308dd17 --- /dev/null +++ b/Memory/MemoryManager.cs @@ -0,0 +1,745 @@ +using ChocolArm64.Events; +using ChocolArm64.Exceptions; +using ChocolArm64.State; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; +using System.Threading; + +namespace ChocolArm64.Memory +{ + public unsafe class MemoryManager : IMemory, IDisposable + { + private const int PtLvl0Bits = 13; + private const int PtLvl1Bits = 14; + private const int PtPageBits = 12; + + private const int PtLvl0Size = 1 << PtLvl0Bits; + private const int PtLvl1Size = 1 << PtLvl1Bits; + public const int PageSize = 1 << PtPageBits; + + private const int PtLvl0Mask = PtLvl0Size - 1; + private const int PtLvl1Mask = PtLvl1Size - 1; + public const int PageMask = PageSize - 1; + + private const int PtLvl0Bit = PtPageBits + PtLvl1Bits; + private const int PtLvl1Bit = PtPageBits; + + private const long ErgMask = (4 << CpuThreadState.ErgSizeLog2) - 1; + + private class ArmMonitor + { + public long Position; + public bool ExState; + + public bool HasExclusiveAccess(long position) + { + return Position == position && ExState; + } + } + + private Dictionary _monitors; + + private ConcurrentDictionary _observedPages; + + public IntPtr Ram { get; private set; } + + private byte* _ramPtr; + + private byte*** _pageTable; + + public event EventHandler InvalidAccess; + + public MemoryManager(IntPtr ram) + { + _monitors = new Dictionary(); + + _observedPages = new ConcurrentDictionary(); + + Ram = ram; + + _ramPtr = (byte*)ram; + + _pageTable = (byte***)Marshal.AllocHGlobal(PtLvl0Size * IntPtr.Size); + + for (int l0 = 0; l0 < PtLvl0Size; l0++) + { + _pageTable[l0] = null; + } + } + + public void RemoveMonitor(int core) + { + lock (_monitors) + { + ClearExclusive(core); + + _monitors.Remove(core); + } + } + + public void SetExclusive(int core, long position) + { + position &= ~ErgMask; + + lock (_monitors) + { + foreach (ArmMonitor mon in _monitors.Values) + { + if (mon.Position == position && mon.ExState) + { + mon.ExState = false; + } + } + + if (!_monitors.TryGetValue(core, out ArmMonitor threadMon)) + { + threadMon = new ArmMonitor(); + + _monitors.Add(core, threadMon); + } + + threadMon.Position = position; + threadMon.ExState = true; + } + } + + public bool TestExclusive(int core, long position) + { + //Note: Any call to this method also should be followed by a + //call to ClearExclusiveForStore if this method returns true. + position &= ~ErgMask; + + Monitor.Enter(_monitors); + + if (!_monitors.TryGetValue(core, out ArmMonitor threadMon)) + { + return false; + } + + bool exState = threadMon.HasExclusiveAccess(position); + + if (!exState) + { + Monitor.Exit(_monitors); + } + + return exState; + } + + public void ClearExclusiveForStore(int core) + { + if (_monitors.TryGetValue(core, out ArmMonitor threadMon)) + { + threadMon.ExState = false; + } + + Monitor.Exit(_monitors); + } + + public void ClearExclusive(int core) + { + lock (_monitors) + { + if (_monitors.TryGetValue(core, out ArmMonitor threadMon)) + { + threadMon.ExState = false; + } + } + } + + public void WriteInt32ToSharedAddr(long position, int value) + { + long maskedPosition = position & ~ErgMask; + + lock (_monitors) + { + foreach (ArmMonitor mon in _monitors.Values) + { + if (mon.Position == maskedPosition && mon.ExState) + { + mon.ExState = false; + } + } + + WriteInt32(position, value); + } + } + + public sbyte ReadSByte(long position) + { + return (sbyte)ReadByte(position); + } + + public short ReadInt16(long position) + { + return (short)ReadUInt16(position); + } + + public int ReadInt32(long position) + { + return (int)ReadUInt32(position); + } + + public long ReadInt64(long position) + { + return (long)ReadUInt64(position); + } + + public byte ReadByte(long position) + { + return *((byte*)Translate(position)); + } + + public ushort ReadUInt16(long position) + { + return *((ushort*)Translate(position)); + } + + public uint ReadUInt32(long position) + { + return *((uint*)Translate(position)); + } + + public ulong ReadUInt64(long position) + { + return *((ulong*)Translate(position)); + } + + public Vector128 ReadVector8(long position) + { + if (Sse2.IsSupported) + { + return Sse.StaticCast(Sse2.SetVector128(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ReadByte(position))); + } + else + { + throw new PlatformNotSupportedException(); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector128 ReadVector16(long position) + { + if (Sse2.IsSupported) + { + return Sse.StaticCast(Sse2.Insert(Sse2.SetZeroVector128(), ReadUInt16(position), 0)); + } + else + { + throw new PlatformNotSupportedException(); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector128 ReadVector32(long position) + { + if (Sse.IsSupported) + { + return Sse.LoadScalarVector128((float*)Translate(position)); + } + else + { + throw new PlatformNotSupportedException(); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector128 ReadVector64(long position) + { + if (Sse2.IsSupported) + { + return Sse.StaticCast(Sse2.LoadScalarVector128((double*)Translate(position))); + } + else + { + throw new PlatformNotSupportedException(); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector128 ReadVector128(long position) + { + if (Sse.IsSupported) + { + return Sse.LoadVector128((float*)Translate(position)); + } + else + { + throw new PlatformNotSupportedException(); + } + } + + public byte[] ReadBytes(long position, long size) + { + if ((uint)size > int.MaxValue) + { + throw new ArgumentOutOfRangeException(nameof(size)); + } + + EnsureRangeIsValid(position, size); + + byte[] data = new byte[size]; + + Marshal.Copy((IntPtr)Translate(position), data, 0, (int)size); + + return data; + } + + public void ReadBytes(long position, byte[] data, int startIndex, int size) + { + //Note: This will be moved later. + EnsureRangeIsValid(position, (uint)size); + + Marshal.Copy((IntPtr)Translate(position), data, startIndex, size); + } + + public void WriteSByte(long position, sbyte value) + { + WriteByte(position, (byte)value); + } + + public void WriteInt16(long position, short value) + { + WriteUInt16(position, (ushort)value); + } + + public void WriteInt32(long position, int value) + { + WriteUInt32(position, (uint)value); + } + + public void WriteInt64(long position, long value) + { + WriteUInt64(position, (ulong)value); + } + + public void WriteByte(long position, byte value) + { + *((byte*)TranslateWrite(position)) = value; + } + + public void WriteUInt16(long position, ushort value) + { + *((ushort*)TranslateWrite(position)) = value; + } + + public void WriteUInt32(long position, uint value) + { + *((uint*)TranslateWrite(position)) = value; + } + + public void WriteUInt64(long position, ulong value) + { + *((ulong*)TranslateWrite(position)) = value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteVector8(long position, Vector128 value) + { + if (Sse41.IsSupported) + { + WriteByte(position, Sse41.Extract(Sse.StaticCast(value), 0)); + } + else if (Sse2.IsSupported) + { + WriteByte(position, (byte)Sse2.Extract(Sse.StaticCast(value), 0)); + } + else + { + throw new PlatformNotSupportedException(); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteVector16(long position, Vector128 value) + { + if (Sse2.IsSupported) + { + WriteUInt16(position, Sse2.Extract(Sse.StaticCast(value), 0)); + } + else + { + throw new PlatformNotSupportedException(); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteVector32(long position, Vector128 value) + { + if (Sse.IsSupported) + { + Sse.StoreScalar((float*)TranslateWrite(position), value); + } + else + { + throw new PlatformNotSupportedException(); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteVector64(long position, Vector128 value) + { + if (Sse2.IsSupported) + { + Sse2.StoreScalar((double*)TranslateWrite(position), Sse.StaticCast(value)); + } + else + { + throw new PlatformNotSupportedException(); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteVector128(long position, Vector128 value) + { + if (Sse.IsSupported) + { + Sse.Store((float*)TranslateWrite(position), value); + } + else + { + throw new PlatformNotSupportedException(); + } + } + + public void WriteBytes(long position, byte[] data) + { + EnsureRangeIsValid(position, (uint)data.Length); + + Marshal.Copy(data, 0, (IntPtr)TranslateWrite(position), data.Length); + } + + public void WriteBytes(long position, byte[] data, int startIndex, int size) + { + //Note: This will be moved later. + //Using Translate instead of TranslateWrite is on purpose. + EnsureRangeIsValid(position, (uint)size); + + Marshal.Copy(data, startIndex, (IntPtr)Translate(position), size); + } + + public void CopyBytes(long src, long dst, long size) + { + //Note: This will be moved later. + EnsureRangeIsValid(src, size); + EnsureRangeIsValid(dst, size); + + byte* srcPtr = Translate(src); + byte* dstPtr = TranslateWrite(dst); + + Buffer.MemoryCopy(srcPtr, dstPtr, size, size); + } + + public void Map(long va, long pa, long size) + { + SetPtEntries(va, _ramPtr + pa, size); + } + + public void Unmap(long position, long size) + { + SetPtEntries(position, null, size); + + StopObservingRegion(position, size); + } + + public bool IsMapped(long position) + { + if (!(IsValidPosition(position))) + { + return false; + } + + long l0 = (position >> PtLvl0Bit) & PtLvl0Mask; + long l1 = (position >> PtLvl1Bit) & PtLvl1Mask; + + if (_pageTable[l0] == null) + { + return false; + } + + return _pageTable[l0][l1] != null || _observedPages.ContainsKey(position >> PtPageBits); + } + + public long GetPhysicalAddress(long virtualAddress) + { + byte* ptr = Translate(virtualAddress); + + return (long)(ptr - _ramPtr); + } + + internal byte* Translate(long position) + { + long l0 = (position >> PtLvl0Bit) & PtLvl0Mask; + long l1 = (position >> PtLvl1Bit) & PtLvl1Mask; + + long old = position; + + byte** lvl1 = _pageTable[l0]; + + if ((position >> (PtLvl0Bit + PtLvl0Bits)) != 0) + { + goto Unmapped; + } + + if (lvl1 == null) + { + goto Unmapped; + } + + position &= PageMask; + + byte* ptr = lvl1[l1]; + + if (ptr == null) + { + goto Unmapped; + } + + return ptr + position; + +Unmapped: + return HandleNullPte(old); + } + + private byte* HandleNullPte(long position) + { + long key = position >> PtPageBits; + + if (_observedPages.TryGetValue(key, out IntPtr ptr)) + { + return (byte*)ptr + (position & PageMask); + } + + InvalidAccess?.Invoke(this, new InvalidAccessEventArgs(position)); + + throw new VmmPageFaultException(position); + } + + internal byte* TranslateWrite(long position) + { + long l0 = (position >> PtLvl0Bit) & PtLvl0Mask; + long l1 = (position >> PtLvl1Bit) & PtLvl1Mask; + + long old = position; + + byte** lvl1 = _pageTable[l0]; + + if ((position >> (PtLvl0Bit + PtLvl0Bits)) != 0) + { + goto Unmapped; + } + + if (lvl1 == null) + { + goto Unmapped; + } + + position &= PageMask; + + byte* ptr = lvl1[l1]; + + if (ptr == null) + { + goto Unmapped; + } + + return ptr + position; + +Unmapped: + return HandleNullPteWrite(old); + } + + private byte* HandleNullPteWrite(long position) + { + long key = position >> PtPageBits; + + if (_observedPages.TryGetValue(key, out IntPtr ptr)) + { + SetPtEntry(position, (byte*)ptr); + + return (byte*)ptr + (position & PageMask); + } + + InvalidAccess?.Invoke(this, new InvalidAccessEventArgs(position)); + + throw new VmmPageFaultException(position); + } + + private void SetPtEntries(long va, byte* ptr, long size) + { + long endPosition = (va + size + PageMask) & ~PageMask; + + while ((ulong)va < (ulong)endPosition) + { + SetPtEntry(va, ptr); + + va += PageSize; + + if (ptr != null) + { + ptr += PageSize; + } + } + } + + private void SetPtEntry(long position, byte* ptr) + { + if (!IsValidPosition(position)) + { + throw new ArgumentOutOfRangeException(nameof(position)); + } + + long l0 = (position >> PtLvl0Bit) & PtLvl0Mask; + long l1 = (position >> PtLvl1Bit) & PtLvl1Mask; + + if (_pageTable[l0] == null) + { + byte** lvl1 = (byte**)Marshal.AllocHGlobal(PtLvl1Size * IntPtr.Size); + + for (int zl1 = 0; zl1 < PtLvl1Size; zl1++) + { + lvl1[zl1] = null; + } + + Thread.MemoryBarrier(); + + _pageTable[l0] = lvl1; + } + + _pageTable[l0][l1] = ptr; + } + + public (bool[], int) IsRegionModified(long position, long size) + { + long endPosition = (position + size + PageMask) & ~PageMask; + + position &= ~PageMask; + + size = endPosition - position; + + bool[] modified = new bool[size >> PtPageBits]; + + int count = 0; + + lock (_observedPages) + { + for (int page = 0; page < modified.Length; page++) + { + byte* ptr = Translate(position); + + if (_observedPages.TryAdd(position >> PtPageBits, (IntPtr)ptr)) + { + modified[page] = true; + + count++; + } + else + { + long l0 = (position >> PtLvl0Bit) & PtLvl0Mask; + long l1 = (position >> PtLvl1Bit) & PtLvl1Mask; + + byte** lvl1 = _pageTable[l0]; + + if (lvl1 != null) + { + if (modified[page] = lvl1[l1] != null) + { + count++; + } + } + } + + SetPtEntry(position, null); + + position += PageSize; + } + } + + return (modified, count); + } + + public void StopObservingRegion(long position, long size) + { + long endPosition = (position + size + PageMask) & ~PageMask; + + while (position < endPosition) + { + lock (_observedPages) + { + if (_observedPages.TryRemove(position >> PtPageBits, out IntPtr ptr)) + { + SetPtEntry(position, (byte*)ptr); + } + } + + position += PageSize; + } + } + + public IntPtr GetHostAddress(long position, long size) + { + EnsureRangeIsValid(position, size); + + return (IntPtr)Translate(position); + } + + internal void EnsureRangeIsValid(long position, long size) + { + long endPos = position + size; + + position &= ~PageMask; + + long expectedPa = GetPhysicalAddress(position); + + while ((ulong)position < (ulong)endPos) + { + long pa = GetPhysicalAddress(position); + + if (pa != expectedPa) + { + throw new VmmAccessException(position, size); + } + + position += PageSize; + expectedPa += PageSize; + } + } + + public bool IsValidPosition(long position) + { + return position >> (PtLvl0Bits + PtLvl1Bits + PtPageBits) == 0; + } + + public void Dispose() + { + Dispose(true); + } + + protected virtual void Dispose(bool disposing) + { + if (_pageTable == null) + { + return; + } + + for (int l0 = 0; l0 < PtLvl0Size; l0++) + { + if (_pageTable[l0] != null) + { + Marshal.FreeHGlobal((IntPtr)_pageTable[l0]); + } + + _pageTable[l0] = null; + } + + Marshal.FreeHGlobal((IntPtr)_pageTable); + + _pageTable = null; + } + } +} \ No newline at end of file diff --git a/OpCodeTable.cs b/OpCodeTable.cs new file mode 100644 index 0000000..b2a75bb --- /dev/null +++ b/OpCodeTable.cs @@ -0,0 +1,713 @@ +using ChocolArm64.Decoders; +using ChocolArm64.Decoders32; +using ChocolArm64.Instructions; +using ChocolArm64.Instructions32; +using ChocolArm64.State; +using System; +using System.Collections.Generic; + +namespace ChocolArm64 +{ + static class OpCodeTable + { + static OpCodeTable() + { +#region "OpCode Table (AArch32)" + //Integer + SetA32("<<<<1010xxxxxxxxxxxxxxxxxxxxxxxx", A32InstInterpret.B, typeof(A32OpCodeBImmAl)); + SetA32("<<<<1011xxxxxxxxxxxxxxxxxxxxxxxx", A32InstInterpret.Bl, typeof(A32OpCodeBImmAl)); + SetA32("1111101xxxxxxxxxxxxxxxxxxxxxxxxx", A32InstInterpret.Blx, typeof(A32OpCodeBImmAl)); +#endregion + +#region "OpCode Table (AArch64)" + //Integer + SetA64("x0011010000xxxxx000000xxxxxxxxxx", InstEmit.Adc, typeof(OpCodeAluRs64)); + SetA64("x0111010000xxxxx000000xxxxxxxxxx", InstEmit.Adcs, typeof(OpCodeAluRs64)); + SetA64("x00100010xxxxxxxxxxxxxxxxxxxxxxx", InstEmit.Add, typeof(OpCodeAluImm64)); + SetA64("00001011<<0xxxxx0xxxxxxxxxxxxxxx", InstEmit.Add, typeof(OpCodeAluRs64)); + SetA64("10001011<<0xxxxxxxxxxxxxxxxxxxxx", InstEmit.Add, typeof(OpCodeAluRs64)); + SetA64("x0001011001xxxxxxxx0xxxxxxxxxxxx", InstEmit.Add, typeof(OpCodeAluRx64)); + SetA64("x0001011001xxxxxxxx100xxxxxxxxxx", InstEmit.Add, typeof(OpCodeAluRx64)); + SetA64("x01100010xxxxxxxxxxxxxxxxxxxxxxx", InstEmit.Adds, typeof(OpCodeAluImm64)); + SetA64("00101011<<0xxxxx0xxxxxxxxxxxxxxx", InstEmit.Adds, typeof(OpCodeAluRs64)); + SetA64("10101011<<0xxxxxxxxxxxxxxxxxxxxx", InstEmit.Adds, typeof(OpCodeAluRs64)); + SetA64("x0101011001xxxxxxxx0xxxxxxxxxxxx", InstEmit.Adds, typeof(OpCodeAluRx64)); + SetA64("x0101011001xxxxxxxx100xxxxxxxxxx", InstEmit.Adds, typeof(OpCodeAluRx64)); + SetA64("0xx10000xxxxxxxxxxxxxxxxxxxxxxxx", InstEmit.Adr, typeof(OpCodeAdr64)); + SetA64("1xx10000xxxxxxxxxxxxxxxxxxxxxxxx", InstEmit.Adrp, typeof(OpCodeAdr64)); + SetA64("0001001000xxxxxxxxxxxxxxxxxxxxxx", InstEmit.And, typeof(OpCodeAluImm64)); + SetA64("100100100xxxxxxxxxxxxxxxxxxxxxxx", InstEmit.And, typeof(OpCodeAluImm64)); + SetA64("00001010xx0xxxxx0xxxxxxxxxxxxxxx", InstEmit.And, typeof(OpCodeAluRs64)); + SetA64("10001010xx0xxxxxxxxxxxxxxxxxxxxx", InstEmit.And, typeof(OpCodeAluRs64)); + SetA64("0111001000xxxxxxxxxxxxxxxxxxxxxx", InstEmit.Ands, typeof(OpCodeAluImm64)); + SetA64("111100100xxxxxxxxxxxxxxxxxxxxxxx", InstEmit.Ands, typeof(OpCodeAluImm64)); + SetA64("01101010xx0xxxxx0xxxxxxxxxxxxxxx", InstEmit.Ands, typeof(OpCodeAluRs64)); + SetA64("11101010xx0xxxxxxxxxxxxxxxxxxxxx", InstEmit.Ands, typeof(OpCodeAluRs64)); + SetA64("x0011010110xxxxx001010xxxxxxxxxx", InstEmit.Asrv, typeof(OpCodeAluRs64)); + SetA64("000101xxxxxxxxxxxxxxxxxxxxxxxxxx", InstEmit.B, typeof(OpCodeBImmAl64)); + SetA64("01010100xxxxxxxxxxxxxxxxxxx0xxxx", InstEmit.B_Cond, typeof(OpCodeBImmCond64)); + SetA64("00110011000xxxxx0xxxxxxxxxxxxxxx", InstEmit.Bfm, typeof(OpCodeBfm64)); + SetA64("1011001101xxxxxxxxxxxxxxxxxxxxxx", InstEmit.Bfm, typeof(OpCodeBfm64)); + SetA64("00001010xx1xxxxx0xxxxxxxxxxxxxxx", InstEmit.Bic, typeof(OpCodeAluRs64)); + SetA64("10001010xx1xxxxxxxxxxxxxxxxxxxxx", InstEmit.Bic, typeof(OpCodeAluRs64)); + SetA64("01101010xx1xxxxx0xxxxxxxxxxxxxxx", InstEmit.Bics, typeof(OpCodeAluRs64)); + SetA64("11101010xx1xxxxxxxxxxxxxxxxxxxxx", InstEmit.Bics, typeof(OpCodeAluRs64)); + SetA64("100101xxxxxxxxxxxxxxxxxxxxxxxxxx", InstEmit.Bl, typeof(OpCodeBImmAl64)); + SetA64("1101011000111111000000xxxxx00000", InstEmit.Blr, typeof(OpCodeBReg64)); + SetA64("1101011000011111000000xxxxx00000", InstEmit.Br, typeof(OpCodeBReg64)); + SetA64("11010100001xxxxxxxxxxxxxxxx00000", InstEmit.Brk, typeof(OpCodeException64)); + SetA64("x0110101xxxxxxxxxxxxxxxxxxxxxxxx", InstEmit.Cbnz, typeof(OpCodeBImmCmp64)); + SetA64("x0110100xxxxxxxxxxxxxxxxxxxxxxxx", InstEmit.Cbz, typeof(OpCodeBImmCmp64)); + SetA64("x0111010010xxxxxxxxx10xxxxx0xxxx", InstEmit.Ccmn, typeof(OpCodeCcmpImm64)); + SetA64("x0111010010xxxxxxxxx00xxxxx0xxxx", InstEmit.Ccmn, typeof(OpCodeCcmpReg64)); + SetA64("x1111010010xxxxxxxxx10xxxxx0xxxx", InstEmit.Ccmp, typeof(OpCodeCcmpImm64)); + SetA64("x1111010010xxxxxxxxx00xxxxx0xxxx", InstEmit.Ccmp, typeof(OpCodeCcmpReg64)); + SetA64("11010101000000110011xxxx01011111", InstEmit.Clrex, typeof(OpCodeSystem64)); + SetA64("x101101011000000000101xxxxxxxxxx", InstEmit.Cls, typeof(OpCodeAlu64)); + SetA64("x101101011000000000100xxxxxxxxxx", InstEmit.Clz, typeof(OpCodeAlu64)); + SetA64("00011010110xxxxx010000xxxxxxxxxx", InstEmit.Crc32b, typeof(OpCodeAluRs64)); + SetA64("00011010110xxxxx010001xxxxxxxxxx", InstEmit.Crc32h, typeof(OpCodeAluRs64)); + SetA64("00011010110xxxxx010010xxxxxxxxxx", InstEmit.Crc32w, typeof(OpCodeAluRs64)); + SetA64("10011010110xxxxx010011xxxxxxxxxx", InstEmit.Crc32x, typeof(OpCodeAluRs64)); + SetA64("00011010110xxxxx010100xxxxxxxxxx", InstEmit.Crc32cb, typeof(OpCodeAluRs64)); + SetA64("00011010110xxxxx010101xxxxxxxxxx", InstEmit.Crc32ch, typeof(OpCodeAluRs64)); + SetA64("00011010110xxxxx010110xxxxxxxxxx", InstEmit.Crc32cw, typeof(OpCodeAluRs64)); + SetA64("10011010110xxxxx010111xxxxxxxxxx", InstEmit.Crc32cx, typeof(OpCodeAluRs64)); + SetA64("x0011010100xxxxxxxxx00xxxxxxxxxx", InstEmit.Csel, typeof(OpCodeCsel64)); + SetA64("x0011010100xxxxxxxxx01xxxxxxxxxx", InstEmit.Csinc, typeof(OpCodeCsel64)); + SetA64("x1011010100xxxxxxxxx00xxxxxxxxxx", InstEmit.Csinv, typeof(OpCodeCsel64)); + SetA64("x1011010100xxxxxxxxx01xxxxxxxxxx", InstEmit.Csneg, typeof(OpCodeCsel64)); + SetA64("11010101000000110011xxxx10111111", InstEmit.Dmb, typeof(OpCodeSystem64)); + SetA64("11010101000000110011xxxx10011111", InstEmit.Dsb, typeof(OpCodeSystem64)); + SetA64("01001010xx1xxxxx0xxxxxxxxxxxxxxx", InstEmit.Eon, typeof(OpCodeAluRs64)); + SetA64("11001010xx1xxxxxxxxxxxxxxxxxxxxx", InstEmit.Eon, typeof(OpCodeAluRs64)); + SetA64("0101001000xxxxxxxxxxxxxxxxxxxxxx", InstEmit.Eor, typeof(OpCodeAluImm64)); + SetA64("110100100xxxxxxxxxxxxxxxxxxxxxxx", InstEmit.Eor, typeof(OpCodeAluImm64)); + SetA64("01001010xx0xxxxx0xxxxxxxxxxxxxxx", InstEmit.Eor, typeof(OpCodeAluRs64)); + SetA64("11001010xx0xxxxxxxxxxxxxxxxxxxxx", InstEmit.Eor, typeof(OpCodeAluRs64)); + SetA64("00010011100xxxxx0xxxxxxxxxxxxxxx", InstEmit.Extr, typeof(OpCodeAluRs64)); + SetA64("10010011110xxxxxxxxxxxxxxxxxxxxx", InstEmit.Extr, typeof(OpCodeAluRs64)); + SetA64("11010101000000110010xxxxxxx11111", InstEmit.Hint, typeof(OpCodeSystem64)); + SetA64("11010101000000110011xxxx11011111", InstEmit.Isb, typeof(OpCodeSystem64)); + SetA64("xx001000110xxxxx1xxxxxxxxxxxxxxx", InstEmit.Ldar, typeof(OpCodeMemEx64)); + SetA64("1x001000011xxxxx1xxxxxxxxxxxxxxx", InstEmit.Ldaxp, typeof(OpCodeMemEx64)); + SetA64("xx001000010xxxxx1xxxxxxxxxxxxxxx", InstEmit.Ldaxr, typeof(OpCodeMemEx64)); + SetA64("<<10100xx1xxxxxxxxxxxxxxxxxxxxxx", InstEmit.Ldp, typeof(OpCodeMemPair64)); + SetA64("xx111000010xxxxxxxxxxxxxxxxxxxxx", InstEmit.Ldr, typeof(OpCodeMemImm64)); + SetA64("xx11100101xxxxxxxxxxxxxxxxxxxxxx", InstEmit.Ldr, typeof(OpCodeMemImm64)); + SetA64("xx111000011xxxxxxxxx10xxxxxxxxxx", InstEmit.Ldr, typeof(OpCodeMemReg64)); + SetA64("xx011000xxxxxxxxxxxxxxxxxxxxxxxx", InstEmit.LdrLit, typeof(OpCodeMemLit64)); + SetA64("0x1110001x0xxxxxxxxxxxxxxxxxxxxx", InstEmit.Ldrs, typeof(OpCodeMemImm64)); + SetA64("0x1110011xxxxxxxxxxxxxxxxxxxxxxx", InstEmit.Ldrs, typeof(OpCodeMemImm64)); + SetA64("10111000100xxxxxxxxxxxxxxxxxxxxx", InstEmit.Ldrs, typeof(OpCodeMemImm64)); + SetA64("1011100110xxxxxxxxxxxxxxxxxxxxxx", InstEmit.Ldrs, typeof(OpCodeMemImm64)); + SetA64("0x1110001x1xxxxxxxxx10xxxxxxxxxx", InstEmit.Ldrs, typeof(OpCodeMemReg64)); + SetA64("10111000101xxxxxxxxx10xxxxxxxxxx", InstEmit.Ldrs, typeof(OpCodeMemReg64)); + SetA64("xx001000010xxxxx0xxxxxxxxxxxxxxx", InstEmit.Ldxr, typeof(OpCodeMemEx64)); + SetA64("1x001000011xxxxx0xxxxxxxxxxxxxxx", InstEmit.Ldxp, typeof(OpCodeMemEx64)); + SetA64("x0011010110xxxxx001000xxxxxxxxxx", InstEmit.Lslv, typeof(OpCodeAluRs64)); + SetA64("x0011010110xxxxx001001xxxxxxxxxx", InstEmit.Lsrv, typeof(OpCodeAluRs64)); + SetA64("x0011011000xxxxx0xxxxxxxxxxxxxxx", InstEmit.Madd, typeof(OpCodeMul64)); + SetA64("0111001010xxxxxxxxxxxxxxxxxxxxxx", InstEmit.Movk, typeof(OpCodeMov64)); + SetA64("111100101xxxxxxxxxxxxxxxxxxxxxxx", InstEmit.Movk, typeof(OpCodeMov64)); + SetA64("0001001010xxxxxxxxxxxxxxxxxxxxxx", InstEmit.Movn, typeof(OpCodeMov64)); + SetA64("100100101xxxxxxxxxxxxxxxxxxxxxxx", InstEmit.Movn, typeof(OpCodeMov64)); + SetA64("0101001010xxxxxxxxxxxxxxxxxxxxxx", InstEmit.Movz, typeof(OpCodeMov64)); + SetA64("110100101xxxxxxxxxxxxxxxxxxxxxxx", InstEmit.Movz, typeof(OpCodeMov64)); + SetA64("110101010011xxxxxxxxxxxxxxxxxxxx", InstEmit.Mrs, typeof(OpCodeSystem64)); + SetA64("110101010001xxxxxxxxxxxxxxxxxxxx", InstEmit.Msr, typeof(OpCodeSystem64)); + SetA64("x0011011000xxxxx1xxxxxxxxxxxxxxx", InstEmit.Msub, typeof(OpCodeMul64)); + SetA64("11010101000000110010000000011111", InstEmit.Nop, typeof(OpCodeSystem64)); + SetA64("00101010xx1xxxxx0xxxxxxxxxxxxxxx", InstEmit.Orn, typeof(OpCodeAluRs64)); + SetA64("10101010xx1xxxxxxxxxxxxxxxxxxxxx", InstEmit.Orn, typeof(OpCodeAluRs64)); + SetA64("0011001000xxxxxxxxxxxxxxxxxxxxxx", InstEmit.Orr, typeof(OpCodeAluImm64)); + SetA64("101100100xxxxxxxxxxxxxxxxxxxxxxx", InstEmit.Orr, typeof(OpCodeAluImm64)); + SetA64("00101010xx0xxxxx0xxxxxxxxxxxxxxx", InstEmit.Orr, typeof(OpCodeAluRs64)); + SetA64("10101010xx0xxxxxxxxxxxxxxxxxxxxx", InstEmit.Orr, typeof(OpCodeAluRs64)); + SetA64("1111100110xxxxxxxxxxxxxxxxxxxxxx", InstEmit.Pfrm, typeof(OpCodeMemImm64)); + SetA64("11111000100xxxxxxxxx00xxxxxxxxxx", InstEmit.Pfrm, typeof(OpCodeMemImm64)); + SetA64("11011000xxxxxxxxxxxxxxxxxxxxxxxx", InstEmit.Pfrm, typeof(OpCodeMemLit64)); + SetA64("x101101011000000000000xxxxxxxxxx", InstEmit.Rbit, typeof(OpCodeAlu64)); + SetA64("1101011001011111000000xxxxx00000", InstEmit.Ret, typeof(OpCodeBReg64)); + SetA64("x101101011000000000001xxxxxxxxxx", InstEmit.Rev16, typeof(OpCodeAlu64)); + SetA64("x101101011000000000010xxxxxxxxxx", InstEmit.Rev32, typeof(OpCodeAlu64)); + SetA64("1101101011000000000011xxxxxxxxxx", InstEmit.Rev64, typeof(OpCodeAlu64)); + SetA64("x0011010110xxxxx001011xxxxxxxxxx", InstEmit.Rorv, typeof(OpCodeAluRs64)); + SetA64("x1011010000xxxxx000000xxxxxxxxxx", InstEmit.Sbc, typeof(OpCodeAluRs64)); + SetA64("x1111010000xxxxx000000xxxxxxxxxx", InstEmit.Sbcs, typeof(OpCodeAluRs64)); + SetA64("00010011000xxxxx0xxxxxxxxxxxxxxx", InstEmit.Sbfm, typeof(OpCodeBfm64)); + SetA64("1001001101xxxxxxxxxxxxxxxxxxxxxx", InstEmit.Sbfm, typeof(OpCodeBfm64)); + SetA64("x0011010110xxxxx000011xxxxxxxxxx", InstEmit.Sdiv, typeof(OpCodeAluRs64)); + SetA64("10011011001xxxxx0xxxxxxxxxxxxxxx", InstEmit.Smaddl, typeof(OpCodeMul64)); + SetA64("10011011001xxxxx1xxxxxxxxxxxxxxx", InstEmit.Smsubl, typeof(OpCodeMul64)); + SetA64("10011011010xxxxx0xxxxxxxxxxxxxxx", InstEmit.Smulh, typeof(OpCodeMul64)); + SetA64("xx001000100xxxxx1xxxxxxxxxxxxxxx", InstEmit.Stlr, typeof(OpCodeMemEx64)); + SetA64("1x001000001xxxxx1xxxxxxxxxxxxxxx", InstEmit.Stlxp, typeof(OpCodeMemEx64)); + SetA64("xx001000000xxxxx1xxxxxxxxxxxxxxx", InstEmit.Stlxr, typeof(OpCodeMemEx64)); + SetA64("x010100xx0xxxxxxxxxxxxxxxxxxxxxx", InstEmit.Stp, typeof(OpCodeMemPair64)); + SetA64("xx111000000xxxxxxxxxxxxxxxxxxxxx", InstEmit.Str, typeof(OpCodeMemImm64)); + SetA64("xx11100100xxxxxxxxxxxxxxxxxxxxxx", InstEmit.Str, typeof(OpCodeMemImm64)); + SetA64("xx111000001xxxxxxxxx10xxxxxxxxxx", InstEmit.Str, typeof(OpCodeMemReg64)); + SetA64("1x001000001xxxxx0xxxxxxxxxxxxxxx", InstEmit.Stxp, typeof(OpCodeMemEx64)); + SetA64("xx001000000xxxxx0xxxxxxxxxxxxxxx", InstEmit.Stxr, typeof(OpCodeMemEx64)); + SetA64("x10100010xxxxxxxxxxxxxxxxxxxxxxx", InstEmit.Sub, typeof(OpCodeAluImm64)); + SetA64("01001011<<0xxxxx0xxxxxxxxxxxxxxx", InstEmit.Sub, typeof(OpCodeAluRs64)); + SetA64("11001011<<0xxxxxxxxxxxxxxxxxxxxx", InstEmit.Sub, typeof(OpCodeAluRs64)); + SetA64("x1001011001xxxxxxxx0xxxxxxxxxxxx", InstEmit.Sub, typeof(OpCodeAluRx64)); + SetA64("x1001011001xxxxxxxx100xxxxxxxxxx", InstEmit.Sub, typeof(OpCodeAluRx64)); + SetA64("x11100010xxxxxxxxxxxxxxxxxxxxxxx", InstEmit.Subs, typeof(OpCodeAluImm64)); + SetA64("01101011<<0xxxxx0xxxxxxxxxxxxxxx", InstEmit.Subs, typeof(OpCodeAluRs64)); + SetA64("11101011<<0xxxxxxxxxxxxxxxxxxxxx", InstEmit.Subs, typeof(OpCodeAluRs64)); + SetA64("x1101011001xxxxxxxx0xxxxxxxxxxxx", InstEmit.Subs, typeof(OpCodeAluRx64)); + SetA64("x1101011001xxxxxxxx100xxxxxxxxxx", InstEmit.Subs, typeof(OpCodeAluRx64)); + SetA64("11010100000xxxxxxxxxxxxxxxx00001", InstEmit.Svc, typeof(OpCodeException64)); + SetA64("1101010100001xxxxxxxxxxxxxxxxxxx", InstEmit.Sys, typeof(OpCodeSystem64)); + SetA64("x0110111xxxxxxxxxxxxxxxxxxxxxxxx", InstEmit.Tbnz, typeof(OpCodeBImmTest64)); + SetA64("x0110110xxxxxxxxxxxxxxxxxxxxxxxx", InstEmit.Tbz, typeof(OpCodeBImmTest64)); + SetA64("01010011000xxxxx0xxxxxxxxxxxxxxx", InstEmit.Ubfm, typeof(OpCodeBfm64)); + SetA64("1101001101xxxxxxxxxxxxxxxxxxxxxx", InstEmit.Ubfm, typeof(OpCodeBfm64)); + SetA64("x0011010110xxxxx000010xxxxxxxxxx", InstEmit.Udiv, typeof(OpCodeAluRs64)); + SetA64("10011011101xxxxx0xxxxxxxxxxxxxxx", InstEmit.Umaddl, typeof(OpCodeMul64)); + SetA64("10011011101xxxxx1xxxxxxxxxxxxxxx", InstEmit.Umsubl, typeof(OpCodeMul64)); + SetA64("10011011110xxxxx0xxxxxxxxxxxxxxx", InstEmit.Umulh, typeof(OpCodeMul64)); + + //Vector + SetA64("0101111011100000101110xxxxxxxxxx", InstEmit.Abs_S, typeof(OpCodeSimd64)); + SetA64("0>001110<<100000101110xxxxxxxxxx", InstEmit.Abs_V, typeof(OpCodeSimd64)); + SetA64("01011110111xxxxx100001xxxxxxxxxx", InstEmit.Add_S, typeof(OpCodeSimdReg64)); + SetA64("0>001110<<1xxxxx100001xxxxxxxxxx", InstEmit.Add_V, typeof(OpCodeSimdReg64)); + SetA64("0x001110<<1xxxxx010000xxxxxxxxxx", InstEmit.Addhn_V, typeof(OpCodeSimdReg64)); + SetA64("0101111011110001101110xxxxxxxxxx", InstEmit.Addp_S, typeof(OpCodeSimd64)); + SetA64("0>001110<<1xxxxx101111xxxxxxxxxx", InstEmit.Addp_V, typeof(OpCodeSimdReg64)); + SetA64("000011100x110001101110xxxxxxxxxx", InstEmit.Addv_V, typeof(OpCodeSimd64)); + SetA64("01001110<<110001101110xxxxxxxxxx", InstEmit.Addv_V, typeof(OpCodeSimd64)); + SetA64("0100111000101000010110xxxxxxxxxx", InstEmit.Aesd_V, typeof(OpCodeSimd64)); + SetA64("0100111000101000010010xxxxxxxxxx", InstEmit.Aese_V, typeof(OpCodeSimd64)); + SetA64("0100111000101000011110xxxxxxxxxx", InstEmit.Aesimc_V, typeof(OpCodeSimd64)); + SetA64("0100111000101000011010xxxxxxxxxx", InstEmit.Aesmc_V, typeof(OpCodeSimd64)); + SetA64("0x001110001xxxxx000111xxxxxxxxxx", InstEmit.And_V, typeof(OpCodeSimdReg64)); + SetA64("0x001110011xxxxx000111xxxxxxxxxx", InstEmit.Bic_V, typeof(OpCodeSimdReg64)); + SetA64("0x10111100000xxx<101110<<1xxxxx100011xxxxxxxxxx", InstEmit.Cmeq_V, typeof(OpCodeSimdReg64)); + SetA64("0>001110<<100000100110xxxxxxxxxx", InstEmit.Cmeq_V, typeof(OpCodeSimd64)); + SetA64("01011110111xxxxx001111xxxxxxxxxx", InstEmit.Cmge_S, typeof(OpCodeSimdReg64)); + SetA64("0111111011100000100010xxxxxxxxxx", InstEmit.Cmge_S, typeof(OpCodeSimd64)); + SetA64("0>001110<<1xxxxx001111xxxxxxxxxx", InstEmit.Cmge_V, typeof(OpCodeSimdReg64)); + SetA64("0>101110<<100000100010xxxxxxxxxx", InstEmit.Cmge_V, typeof(OpCodeSimd64)); + SetA64("01011110111xxxxx001101xxxxxxxxxx", InstEmit.Cmgt_S, typeof(OpCodeSimdReg64)); + SetA64("0101111011100000100010xxxxxxxxxx", InstEmit.Cmgt_S, typeof(OpCodeSimd64)); + SetA64("0>001110<<1xxxxx001101xxxxxxxxxx", InstEmit.Cmgt_V, typeof(OpCodeSimdReg64)); + SetA64("0>001110<<100000100010xxxxxxxxxx", InstEmit.Cmgt_V, typeof(OpCodeSimd64)); + SetA64("01111110111xxxxx001101xxxxxxxxxx", InstEmit.Cmhi_S, typeof(OpCodeSimdReg64)); + SetA64("0>101110<<1xxxxx001101xxxxxxxxxx", InstEmit.Cmhi_V, typeof(OpCodeSimdReg64)); + SetA64("01111110111xxxxx001111xxxxxxxxxx", InstEmit.Cmhs_S, typeof(OpCodeSimdReg64)); + SetA64("0>101110<<1xxxxx001111xxxxxxxxxx", InstEmit.Cmhs_V, typeof(OpCodeSimdReg64)); + SetA64("0111111011100000100110xxxxxxxxxx", InstEmit.Cmle_S, typeof(OpCodeSimd64)); + SetA64("0>101110<<100000100110xxxxxxxxxx", InstEmit.Cmle_V, typeof(OpCodeSimd64)); + SetA64("0101111011100000101010xxxxxxxxxx", InstEmit.Cmlt_S, typeof(OpCodeSimd64)); + SetA64("0>001110<<100000101010xxxxxxxxxx", InstEmit.Cmlt_V, typeof(OpCodeSimd64)); + SetA64("01011110111xxxxx100011xxxxxxxxxx", InstEmit.Cmtst_S, typeof(OpCodeSimdReg64)); + SetA64("0>001110<<1xxxxx100011xxxxxxxxxx", InstEmit.Cmtst_V, typeof(OpCodeSimdReg64)); + SetA64("0x00111000100000010110xxxxxxxxxx", InstEmit.Cnt_V, typeof(OpCodeSimd64)); + SetA64("0x001110000xxxxx000011xxxxxxxxxx", InstEmit.Dup_Gp, typeof(OpCodeSimdIns64)); + SetA64("01011110000xxxxx000001xxxxxxxxxx", InstEmit.Dup_S, typeof(OpCodeSimdIns64)); + SetA64("0x001110000xxxxx000001xxxxxxxxxx", InstEmit.Dup_V, typeof(OpCodeSimdIns64)); + SetA64("0x101110001xxxxx000111xxxxxxxxxx", InstEmit.Eor_V, typeof(OpCodeSimdReg64)); + SetA64("0>101110000xxxxx00011101<100000111110xxxxxxxxxx", InstEmit.Fabs_V, typeof(OpCodeSimd64)); + SetA64("000111100x1xxxxx001010xxxxxxxxxx", InstEmit.Fadd_S, typeof(OpCodeSimdReg64)); + SetA64("0>0011100<1xxxxx110101xxxxxxxxxx", InstEmit.Fadd_V, typeof(OpCodeSimdReg64)); + SetA64("011111100x110000110110xxxxxxxxxx", InstEmit.Faddp_S, typeof(OpCodeSimd64)); + SetA64("0>1011100<1xxxxx110101xxxxxxxxxx", InstEmit.Faddp_V, typeof(OpCodeSimdReg64)); + SetA64("000111100x1xxxxxxxxx01xxxxx0xxxx", InstEmit.Fccmp_S, typeof(OpCodeSimdFcond64)); + SetA64("000111100x1xxxxxxxxx01xxxxx1xxxx", InstEmit.Fccmpe_S, typeof(OpCodeSimdFcond64)); + SetA64("010111100x1xxxxx111001xxxxxxxxxx", InstEmit.Fcmeq_S, typeof(OpCodeSimdReg64)); + SetA64("010111101x100000110110xxxxxxxxxx", InstEmit.Fcmeq_S, typeof(OpCodeSimd64)); + SetA64("0>0011100<1xxxxx111001xxxxxxxxxx", InstEmit.Fcmeq_V, typeof(OpCodeSimdReg64)); + SetA64("0>0011101<100000110110xxxxxxxxxx", InstEmit.Fcmeq_V, typeof(OpCodeSimd64)); + SetA64("011111100x1xxxxx111001xxxxxxxxxx", InstEmit.Fcmge_S, typeof(OpCodeSimdReg64)); + SetA64("011111101x100000110010xxxxxxxxxx", InstEmit.Fcmge_S, typeof(OpCodeSimd64)); + SetA64("0>1011100<1xxxxx111001xxxxxxxxxx", InstEmit.Fcmge_V, typeof(OpCodeSimdReg64)); + SetA64("0>1011101<100000110010xxxxxxxxxx", InstEmit.Fcmge_V, typeof(OpCodeSimd64)); + SetA64("011111101x1xxxxx111001xxxxxxxxxx", InstEmit.Fcmgt_S, typeof(OpCodeSimdReg64)); + SetA64("010111101x100000110010xxxxxxxxxx", InstEmit.Fcmgt_S, typeof(OpCodeSimd64)); + SetA64("0>1011101<1xxxxx111001xxxxxxxxxx", InstEmit.Fcmgt_V, typeof(OpCodeSimdReg64)); + SetA64("0>0011101<100000110010xxxxxxxxxx", InstEmit.Fcmgt_V, typeof(OpCodeSimd64)); + SetA64("011111101x100000110110xxxxxxxxxx", InstEmit.Fcmle_S, typeof(OpCodeSimd64)); + SetA64("0>1011101<100000110110xxxxxxxxxx", InstEmit.Fcmle_V, typeof(OpCodeSimd64)); + SetA64("010111101x100000111010xxxxxxxxxx", InstEmit.Fcmlt_S, typeof(OpCodeSimd64)); + SetA64("0>0011101<100000111010xxxxxxxxxx", InstEmit.Fcmlt_V, typeof(OpCodeSimd64)); + SetA64("000111100x1xxxxx001000xxxxx0x000", InstEmit.Fcmp_S, typeof(OpCodeSimdReg64)); + SetA64("000111100x1xxxxx001000xxxxx1x000", InstEmit.Fcmpe_S, typeof(OpCodeSimdReg64)); + SetA64("000111100x1xxxxxxxxx11xxxxxxxxxx", InstEmit.Fcsel_S, typeof(OpCodeSimdFcond64)); + SetA64("000111100x10001xx10000xxxxxxxxxx", InstEmit.Fcvt_S, typeof(OpCodeSimd64)); + SetA64("x00111100x100100000000xxxxxxxxxx", InstEmit.Fcvtas_Gp, typeof(OpCodeSimdCvt64)); + SetA64("x00111100x100101000000xxxxxxxxxx", InstEmit.Fcvtau_Gp, typeof(OpCodeSimdCvt64)); + SetA64("0x0011100x100001011110xxxxxxxxxx", InstEmit.Fcvtl_V, typeof(OpCodeSimd64)); + SetA64("x00111100x110000000000xxxxxxxxxx", InstEmit.Fcvtms_Gp, typeof(OpCodeSimdCvt64)); + SetA64("x00111100x110001000000xxxxxxxxxx", InstEmit.Fcvtmu_Gp, typeof(OpCodeSimdCvt64)); + SetA64("0x0011100x100001011010xxxxxxxxxx", InstEmit.Fcvtn_V, typeof(OpCodeSimd64)); + SetA64("010111100x100001101010xxxxxxxxxx", InstEmit.Fcvtns_S, typeof(OpCodeSimd64)); + SetA64("0>0011100<100001101010xxxxxxxxxx", InstEmit.Fcvtns_V, typeof(OpCodeSimd64)); + SetA64("011111100x100001101010xxxxxxxxxx", InstEmit.Fcvtnu_S, typeof(OpCodeSimd64)); + SetA64("0>1011100<100001101010xxxxxxxxxx", InstEmit.Fcvtnu_V, typeof(OpCodeSimd64)); + SetA64("x00111100x101000000000xxxxxxxxxx", InstEmit.Fcvtps_Gp, typeof(OpCodeSimdCvt64)); + SetA64("x00111100x101001000000xxxxxxxxxx", InstEmit.Fcvtpu_Gp, typeof(OpCodeSimdCvt64)); + SetA64("x00111100x111000000000xxxxxxxxxx", InstEmit.Fcvtzs_Gp, typeof(OpCodeSimdCvt64)); + SetA64("x00111100x011000xxxxxxxxxxxxxxxx", InstEmit.Fcvtzs_Gp_Fix, typeof(OpCodeSimdCvt64)); + SetA64("010111101x100001101110xxxxxxxxxx", InstEmit.Fcvtzs_S, typeof(OpCodeSimd64)); + SetA64("0>0011101<100001101110xxxxxxxxxx", InstEmit.Fcvtzs_V, typeof(OpCodeSimd64)); + SetA64("0x0011110>>xxxxx111111xxxxxxxxxx", InstEmit.Fcvtzs_V, typeof(OpCodeSimdShImm64)); + SetA64("x00111100x111001000000xxxxxxxxxx", InstEmit.Fcvtzu_Gp, typeof(OpCodeSimdCvt64)); + SetA64("x00111100x011001xxxxxxxxxxxxxxxx", InstEmit.Fcvtzu_Gp_Fix, typeof(OpCodeSimdCvt64)); + SetA64("011111101x100001101110xxxxxxxxxx", InstEmit.Fcvtzu_S, typeof(OpCodeSimd64)); + SetA64("0>1011101<100001101110xxxxxxxxxx", InstEmit.Fcvtzu_V, typeof(OpCodeSimd64)); + SetA64("0x1011110>>xxxxx111111xxxxxxxxxx", InstEmit.Fcvtzu_V, typeof(OpCodeSimdShImm64)); + SetA64("000111100x1xxxxx000110xxxxxxxxxx", InstEmit.Fdiv_S, typeof(OpCodeSimdReg64)); + SetA64("0>1011100<1xxxxx111111xxxxxxxxxx", InstEmit.Fdiv_V, typeof(OpCodeSimdReg64)); + SetA64("000111110x0xxxxx0xxxxxxxxxxxxxxx", InstEmit.Fmadd_S, typeof(OpCodeSimdReg64)); + SetA64("000111100x1xxxxx010010xxxxxxxxxx", InstEmit.Fmax_S, typeof(OpCodeSimdReg64)); + SetA64("0>0011100<1xxxxx111101xxxxxxxxxx", InstEmit.Fmax_V, typeof(OpCodeSimdReg64)); + SetA64("000111100x1xxxxx011010xxxxxxxxxx", InstEmit.Fmaxnm_S, typeof(OpCodeSimdReg64)); + SetA64("0>0011100<1xxxxx110001xxxxxxxxxx", InstEmit.Fmaxnm_V, typeof(OpCodeSimdReg64)); + SetA64("0>1011100<1xxxxx111101xxxxxxxxxx", InstEmit.Fmaxp_V, typeof(OpCodeSimdReg64)); + SetA64("000111100x1xxxxx010110xxxxxxxxxx", InstEmit.Fmin_S, typeof(OpCodeSimdReg64)); + SetA64("0>0011101<1xxxxx111101xxxxxxxxxx", InstEmit.Fmin_V, typeof(OpCodeSimdReg64)); + SetA64("000111100x1xxxxx011110xxxxxxxxxx", InstEmit.Fminnm_S, typeof(OpCodeSimdReg64)); + SetA64("0>0011101<1xxxxx110001xxxxxxxxxx", InstEmit.Fminnm_V, typeof(OpCodeSimdReg64)); + SetA64("0>1011101<1xxxxx111101xxxxxxxxxx", InstEmit.Fminp_V, typeof(OpCodeSimdReg64)); + SetA64("010111111xxxxxxx0001x0xxxxxxxxxx", InstEmit.Fmla_Se, typeof(OpCodeSimdRegElemF64)); + SetA64("0>0011100<1xxxxx110011xxxxxxxxxx", InstEmit.Fmla_V, typeof(OpCodeSimdReg64)); + SetA64("0>00111110011101<1xxxxx110011xxxxxxxxxx", InstEmit.Fmls_V, typeof(OpCodeSimdReg64)); + SetA64("0>00111111011100<1xxxxx110111xxxxxxxxxx", InstEmit.Fmul_V, typeof(OpCodeSimdReg64)); + SetA64("0>00111110011100<1xxxxx110111xxxxxxxxxx", InstEmit.Fmulx_V, typeof(OpCodeSimdReg64)); + SetA64("0>10111111011101<100000111110xxxxxxxxxx", InstEmit.Fneg_V, typeof(OpCodeSimd64)); + SetA64("000111110x1xxxxx0xxxxxxxxxxxxxxx", InstEmit.Fnmadd_S, typeof(OpCodeSimdReg64)); + SetA64("000111110x1xxxxx1xxxxxxxxxxxxxxx", InstEmit.Fnmsub_S, typeof(OpCodeSimdReg64)); + SetA64("000111100x1xxxxx100010xxxxxxxxxx", InstEmit.Fnmul_S, typeof(OpCodeSimdReg64)); + SetA64("010111101x100001110110xxxxxxxxxx", InstEmit.Frecpe_S, typeof(OpCodeSimd64)); + SetA64("0>0011101<100001110110xxxxxxxxxx", InstEmit.Frecpe_V, typeof(OpCodeSimd64)); + SetA64("010111100x1xxxxx111111xxxxxxxxxx", InstEmit.Frecps_S, typeof(OpCodeSimdReg64)); + SetA64("0>0011100<1xxxxx111111xxxxxxxxxx", InstEmit.Frecps_V, typeof(OpCodeSimdReg64)); + SetA64("010111101x100001111110xxxxxxxxxx", InstEmit.Frecpx_S, typeof(OpCodeSimd64)); + SetA64("000111100x100110010000xxxxxxxxxx", InstEmit.Frinta_S, typeof(OpCodeSimd64)); + SetA64("0>1011100<100001100010xxxxxxxxxx", InstEmit.Frinta_V, typeof(OpCodeSimd64)); + SetA64("000111100x100111110000xxxxxxxxxx", InstEmit.Frinti_S, typeof(OpCodeSimd64)); + SetA64("0>1011101<100001100110xxxxxxxxxx", InstEmit.Frinti_V, typeof(OpCodeSimd64)); + SetA64("000111100x100101010000xxxxxxxxxx", InstEmit.Frintm_S, typeof(OpCodeSimd64)); + SetA64("0>0011100<100001100110xxxxxxxxxx", InstEmit.Frintm_V, typeof(OpCodeSimd64)); + SetA64("000111100x100100010000xxxxxxxxxx", InstEmit.Frintn_S, typeof(OpCodeSimd64)); + SetA64("0>0011100<100001100010xxxxxxxxxx", InstEmit.Frintn_V, typeof(OpCodeSimd64)); + SetA64("000111100x100100110000xxxxxxxxxx", InstEmit.Frintp_S, typeof(OpCodeSimd64)); + SetA64("0>0011101<100001100010xxxxxxxxxx", InstEmit.Frintp_V, typeof(OpCodeSimd64)); + SetA64("000111100x100111010000xxxxxxxxxx", InstEmit.Frintx_S, typeof(OpCodeSimd64)); + SetA64("0>1011100<100001100110xxxxxxxxxx", InstEmit.Frintx_V, typeof(OpCodeSimd64)); + SetA64("011111101x100001110110xxxxxxxxxx", InstEmit.Frsqrte_S, typeof(OpCodeSimd64)); + SetA64("0>1011101<100001110110xxxxxxxxxx", InstEmit.Frsqrte_V, typeof(OpCodeSimd64)); + SetA64("010111101x1xxxxx111111xxxxxxxxxx", InstEmit.Frsqrts_S, typeof(OpCodeSimdReg64)); + SetA64("0>0011101<1xxxxx111111xxxxxxxxxx", InstEmit.Frsqrts_V, typeof(OpCodeSimdReg64)); + SetA64("000111100x100001110000xxxxxxxxxx", InstEmit.Fsqrt_S, typeof(OpCodeSimd64)); + SetA64("0>1011101<100001111110xxxxxxxxxx", InstEmit.Fsqrt_V, typeof(OpCodeSimd64)); + SetA64("000111100x1xxxxx001110xxxxxxxxxx", InstEmit.Fsub_S, typeof(OpCodeSimdReg64)); + SetA64("0>0011101<1xxxxx110101xxxxxxxxxx", InstEmit.Fsub_V, typeof(OpCodeSimdReg64)); + SetA64("01001110000xxxxx000111xxxxxxxxxx", InstEmit.Ins_Gp, typeof(OpCodeSimdIns64)); + SetA64("01101110000xxxxx0xxxx1xxxxxxxxxx", InstEmit.Ins_V, typeof(OpCodeSimdIns64)); + SetA64("0x00110001000000xxxxxxxxxxxxxxxx", InstEmit.Ld__Vms, typeof(OpCodeSimdMemMs64)); + SetA64("0x001100110xxxxxxxxxxxxxxxxxxxxx", InstEmit.Ld__Vms, typeof(OpCodeSimdMemMs64)); + SetA64("0x00110101x00000xxxxxxxxxxxxxxxx", InstEmit.Ld__Vss, typeof(OpCodeSimdMemSs64)); + SetA64("0x00110111xxxxxxxxxxxxxxxxxxxxxx", InstEmit.Ld__Vss, typeof(OpCodeSimdMemSs64)); + SetA64("xx10110xx1xxxxxxxxxxxxxxxxxxxxxx", InstEmit.Ldp, typeof(OpCodeSimdMemPair64)); + SetA64("xx111100x10xxxxxxxxx00xxxxxxxxxx", InstEmit.Ldr, typeof(OpCodeSimdMemImm64)); + SetA64("xx111100x10xxxxxxxxx01xxxxxxxxxx", InstEmit.Ldr, typeof(OpCodeSimdMemImm64)); + SetA64("xx111100x10xxxxxxxxx11xxxxxxxxxx", InstEmit.Ldr, typeof(OpCodeSimdMemImm64)); + SetA64("xx111101x1xxxxxxxxxxxxxxxxxxxxxx", InstEmit.Ldr, typeof(OpCodeSimdMemImm64)); + SetA64("xx111100x11xxxxxxxxx10xxxxxxxxxx", InstEmit.Ldr, typeof(OpCodeSimdMemReg64)); + SetA64("xx011100xxxxxxxxxxxxxxxxxxxxxxxx", InstEmit.LdrLit, typeof(OpCodeSimdMemLit64)); + SetA64("0x001110<<1xxxxx100101xxxxxxxxxx", InstEmit.Mla_V, typeof(OpCodeSimdReg64)); + SetA64("0x101111xxxxxxxx0000x0xxxxxxxxxx", InstEmit.Mla_Ve, typeof(OpCodeSimdRegElem64)); + SetA64("0x101110<<1xxxxx100101xxxxxxxxxx", InstEmit.Mls_V, typeof(OpCodeSimdReg64)); + SetA64("0x101111xxxxxxxx0100x0xxxxxxxxxx", InstEmit.Mls_Ve, typeof(OpCodeSimdRegElem64)); + SetA64("0x00111100000xxx0xx001xxxxxxxxxx", InstEmit.Movi_V, typeof(OpCodeSimdImm64)); + SetA64("0x00111100000xxx10x001xxxxxxxxxx", InstEmit.Movi_V, typeof(OpCodeSimdImm64)); + SetA64("0x00111100000xxx110x01xxxxxxxxxx", InstEmit.Movi_V, typeof(OpCodeSimdImm64)); + SetA64("0xx0111100000xxx111001xxxxxxxxxx", InstEmit.Movi_V, typeof(OpCodeSimdImm64)); + SetA64("0x001110<<1xxxxx100111xxxxxxxxxx", InstEmit.Mul_V, typeof(OpCodeSimdReg64)); + SetA64("0x001111xxxxxxxx1000x0xxxxxxxxxx", InstEmit.Mul_Ve, typeof(OpCodeSimdRegElem64)); + SetA64("0x10111100000xxx0xx001xxxxxxxxxx", InstEmit.Mvni_V, typeof(OpCodeSimdImm64)); + SetA64("0x10111100000xxx10x001xxxxxxxxxx", InstEmit.Mvni_V, typeof(OpCodeSimdImm64)); + SetA64("0x10111100000xxx110x01xxxxxxxxxx", InstEmit.Mvni_V, typeof(OpCodeSimdImm64)); + SetA64("0111111011100000101110xxxxxxxxxx", InstEmit.Neg_S, typeof(OpCodeSimd64)); + SetA64("0>101110<<100000101110xxxxxxxxxx", InstEmit.Neg_V, typeof(OpCodeSimd64)); + SetA64("0x10111000100000010110xxxxxxxxxx", InstEmit.Not_V, typeof(OpCodeSimd64)); + SetA64("0x001110111xxxxx000111xxxxxxxxxx", InstEmit.Orn_V, typeof(OpCodeSimdReg64)); + SetA64("0x001110101xxxxx000111xxxxxxxxxx", InstEmit.Orr_V, typeof(OpCodeSimdReg64)); + SetA64("0x00111100000xxx<>>xxx100011xxxxxxxxxx", InstEmit.Rshrn_V, typeof(OpCodeSimdShImm64)); + SetA64("0x101110<<1xxxxx011000xxxxxxxxxx", InstEmit.Rsubhn_V, typeof(OpCodeSimdReg64)); + SetA64("0x001110<<1xxxxx011111xxxxxxxxxx", InstEmit.Saba_V, typeof(OpCodeSimdReg64)); + SetA64("0x001110<<1xxxxx010100xxxxxxxxxx", InstEmit.Sabal_V, typeof(OpCodeSimdReg64)); + SetA64("0x001110<<1xxxxx011101xxxxxxxxxx", InstEmit.Sabd_V, typeof(OpCodeSimdReg64)); + SetA64("0x001110<<1xxxxx011100xxxxxxxxxx", InstEmit.Sabdl_V, typeof(OpCodeSimdReg64)); + SetA64("0x001110<<100000011010xxxxxxxxxx", InstEmit.Sadalp_V, typeof(OpCodeSimd64)); + SetA64("0x001110<<1xxxxx000000xxxxxxxxxx", InstEmit.Saddl_V, typeof(OpCodeSimdReg64)); + SetA64("0x001110<<100000001010xxxxxxxxxx", InstEmit.Saddlp_V, typeof(OpCodeSimd64)); + SetA64("0x001110<<1xxxxx000100xxxxxxxxxx", InstEmit.Saddw_V, typeof(OpCodeSimdReg64)); + SetA64("x0011110xx100010000000xxxxxxxxxx", InstEmit.Scvtf_Gp, typeof(OpCodeSimdCvt64)); + SetA64("010111100x100001110110xxxxxxxxxx", InstEmit.Scvtf_S, typeof(OpCodeSimd64)); + SetA64("0x0011100x100001110110xxxxxxxxxx", InstEmit.Scvtf_V, typeof(OpCodeSimd64)); + SetA64("01011110000xxxxx000000xxxxxxxxxx", InstEmit.Sha1c_V, typeof(OpCodeSimdReg64)); + SetA64("0101111000101000000010xxxxxxxxxx", InstEmit.Sha1h_V, typeof(OpCodeSimd64)); + SetA64("01011110000xxxxx001000xxxxxxxxxx", InstEmit.Sha1m_V, typeof(OpCodeSimdReg64)); + SetA64("01011110000xxxxx000100xxxxxxxxxx", InstEmit.Sha1p_V, typeof(OpCodeSimdReg64)); + SetA64("01011110000xxxxx001100xxxxxxxxxx", InstEmit.Sha1su0_V, typeof(OpCodeSimdReg64)); + SetA64("0101111000101000000110xxxxxxxxxx", InstEmit.Sha1su1_V, typeof(OpCodeSimd64)); + SetA64("01011110000xxxxx010000xxxxxxxxxx", InstEmit.Sha256h_V, typeof(OpCodeSimdReg64)); + SetA64("01011110000xxxxx010100xxxxxxxxxx", InstEmit.Sha256h2_V, typeof(OpCodeSimdReg64)); + SetA64("0101111000101000001010xxxxxxxxxx", InstEmit.Sha256su0_V, typeof(OpCodeSimd64)); + SetA64("01011110000xxxxx011000xxxxxxxxxx", InstEmit.Sha256su1_V, typeof(OpCodeSimdReg64)); + SetA64("0x001110<<1xxxxx000001xxxxxxxxxx", InstEmit.Shadd_V, typeof(OpCodeSimdReg64)); + SetA64("0101111101xxxxxx010101xxxxxxxxxx", InstEmit.Shl_S, typeof(OpCodeSimdShImm64)); + SetA64("0x00111100>>>xxx010101xxxxxxxxxx", InstEmit.Shl_V, typeof(OpCodeSimdShImm64)); + SetA64("0100111101xxxxxx010101xxxxxxxxxx", InstEmit.Shl_V, typeof(OpCodeSimdShImm64)); + SetA64("0x101110<<100001001110xxxxxxxxxx", InstEmit.Shll_V, typeof(OpCodeSimd64)); + SetA64("0x00111100>>>xxx100001xxxxxxxxxx", InstEmit.Shrn_V, typeof(OpCodeSimdShImm64)); + SetA64("0x001110<<1xxxxx001001xxxxxxxxxx", InstEmit.Shsub_V, typeof(OpCodeSimdReg64)); + SetA64("0x1011110>>>>xxx010101xxxxxxxxxx", InstEmit.Sli_V, typeof(OpCodeSimdShImm64)); + SetA64("0x001110<<1xxxxx011001xxxxxxxxxx", InstEmit.Smax_V, typeof(OpCodeSimdReg64)); + SetA64("0x001110<<1xxxxx101001xxxxxxxxxx", InstEmit.Smaxp_V, typeof(OpCodeSimdReg64)); + SetA64("0x001110<<1xxxxx011011xxxxxxxxxx", InstEmit.Smin_V, typeof(OpCodeSimdReg64)); + SetA64("0x001110<<1xxxxx101011xxxxxxxxxx", InstEmit.Sminp_V, typeof(OpCodeSimdReg64)); + SetA64("0x001110<<1xxxxx100000xxxxxxxxxx", InstEmit.Smlal_V, typeof(OpCodeSimdReg64)); + SetA64("0x001110<<1xxxxx101000xxxxxxxxxx", InstEmit.Smlsl_V, typeof(OpCodeSimdReg64)); + SetA64("0x001110000xxxxx001011xxxxxxxxxx", InstEmit.Smov_S, typeof(OpCodeSimdIns64)); + SetA64("0x001110<<1xxxxx110000xxxxxxxxxx", InstEmit.Smull_V, typeof(OpCodeSimdReg64)); + SetA64("01011110xx100000011110xxxxxxxxxx", InstEmit.Sqabs_S, typeof(OpCodeSimd64)); + SetA64("0>001110<<100000011110xxxxxxxxxx", InstEmit.Sqabs_V, typeof(OpCodeSimd64)); + SetA64("01011110xx1xxxxx000011xxxxxxxxxx", InstEmit.Sqadd_S, typeof(OpCodeSimdReg64)); + SetA64("0>001110<<1xxxxx000011xxxxxxxxxx", InstEmit.Sqadd_V, typeof(OpCodeSimdReg64)); + SetA64("01011110011xxxxx101101xxxxxxxxxx", InstEmit.Sqdmulh_S, typeof(OpCodeSimdReg64)); + SetA64("01011110101xxxxx101101xxxxxxxxxx", InstEmit.Sqdmulh_S, typeof(OpCodeSimdReg64)); + SetA64("0x001110011xxxxx101101xxxxxxxxxx", InstEmit.Sqdmulh_V, typeof(OpCodeSimdReg64)); + SetA64("0x001110101xxxxx101101xxxxxxxxxx", InstEmit.Sqdmulh_V, typeof(OpCodeSimdReg64)); + SetA64("01111110xx100000011110xxxxxxxxxx", InstEmit.Sqneg_S, typeof(OpCodeSimd64)); + SetA64("0>101110<<100000011110xxxxxxxxxx", InstEmit.Sqneg_V, typeof(OpCodeSimd64)); + SetA64("01111110011xxxxx101101xxxxxxxxxx", InstEmit.Sqrdmulh_S, typeof(OpCodeSimdReg64)); + SetA64("01111110101xxxxx101101xxxxxxxxxx", InstEmit.Sqrdmulh_S, typeof(OpCodeSimdReg64)); + SetA64("0x101110011xxxxx101101xxxxxxxxxx", InstEmit.Sqrdmulh_V, typeof(OpCodeSimdReg64)); + SetA64("0x101110101xxxxx101101xxxxxxxxxx", InstEmit.Sqrdmulh_V, typeof(OpCodeSimdReg64)); + SetA64("0101111100>>>xxx100111xxxxxxxxxx", InstEmit.Sqrshrn_S, typeof(OpCodeSimdShImm64)); + SetA64("0x00111100>>>xxx100111xxxxxxxxxx", InstEmit.Sqrshrn_V, typeof(OpCodeSimdShImm64)); + SetA64("0111111100>>>xxx100011xxxxxxxxxx", InstEmit.Sqrshrun_S, typeof(OpCodeSimdShImm64)); + SetA64("0x10111100>>>xxx100011xxxxxxxxxx", InstEmit.Sqrshrun_V, typeof(OpCodeSimdShImm64)); + SetA64("0101111100>>>xxx100101xxxxxxxxxx", InstEmit.Sqshrn_S, typeof(OpCodeSimdShImm64)); + SetA64("0x00111100>>>xxx100101xxxxxxxxxx", InstEmit.Sqshrn_V, typeof(OpCodeSimdShImm64)); + SetA64("0111111100>>>xxx100001xxxxxxxxxx", InstEmit.Sqshrun_S, typeof(OpCodeSimdShImm64)); + SetA64("0x10111100>>>xxx100001xxxxxxxxxx", InstEmit.Sqshrun_V, typeof(OpCodeSimdShImm64)); + SetA64("01011110xx1xxxxx001011xxxxxxxxxx", InstEmit.Sqsub_S, typeof(OpCodeSimdReg64)); + SetA64("0>001110<<1xxxxx001011xxxxxxxxxx", InstEmit.Sqsub_V, typeof(OpCodeSimdReg64)); + SetA64("01011110<<100001010010xxxxxxxxxx", InstEmit.Sqxtn_S, typeof(OpCodeSimd64)); + SetA64("0x001110<<100001010010xxxxxxxxxx", InstEmit.Sqxtn_V, typeof(OpCodeSimd64)); + SetA64("01111110<<100001001010xxxxxxxxxx", InstEmit.Sqxtun_S, typeof(OpCodeSimd64)); + SetA64("0x101110<<100001001010xxxxxxxxxx", InstEmit.Sqxtun_V, typeof(OpCodeSimd64)); + SetA64("0x001110<<1xxxxx000101xxxxxxxxxx", InstEmit.Srhadd_V, typeof(OpCodeSimdReg64)); + SetA64("0101111101xxxxxx001001xxxxxxxxxx", InstEmit.Srshr_S, typeof(OpCodeSimdShImm64)); + SetA64("0x00111100>>>xxx001001xxxxxxxxxx", InstEmit.Srshr_V, typeof(OpCodeSimdShImm64)); + SetA64("0100111101xxxxxx001001xxxxxxxxxx", InstEmit.Srshr_V, typeof(OpCodeSimdShImm64)); + SetA64("0101111101xxxxxx001101xxxxxxxxxx", InstEmit.Srsra_S, typeof(OpCodeSimdShImm64)); + SetA64("0x00111100>>>xxx001101xxxxxxxxxx", InstEmit.Srsra_V, typeof(OpCodeSimdShImm64)); + SetA64("0100111101xxxxxx001101xxxxxxxxxx", InstEmit.Srsra_V, typeof(OpCodeSimdShImm64)); + SetA64("0>001110<<1xxxxx010001xxxxxxxxxx", InstEmit.Sshl_V, typeof(OpCodeSimdReg64)); + SetA64("0x00111100>>>xxx101001xxxxxxxxxx", InstEmit.Sshll_V, typeof(OpCodeSimdShImm64)); + SetA64("0101111101xxxxxx000001xxxxxxxxxx", InstEmit.Sshr_S, typeof(OpCodeSimdShImm64)); + SetA64("0x00111100>>>xxx000001xxxxxxxxxx", InstEmit.Sshr_V, typeof(OpCodeSimdShImm64)); + SetA64("0100111101xxxxxx000001xxxxxxxxxx", InstEmit.Sshr_V, typeof(OpCodeSimdShImm64)); + SetA64("0101111101xxxxxx000101xxxxxxxxxx", InstEmit.Ssra_S, typeof(OpCodeSimdShImm64)); + SetA64("0x00111100>>>xxx000101xxxxxxxxxx", InstEmit.Ssra_V, typeof(OpCodeSimdShImm64)); + SetA64("0100111101xxxxxx000101xxxxxxxxxx", InstEmit.Ssra_V, typeof(OpCodeSimdShImm64)); + SetA64("0x001110<<1xxxxx001000xxxxxxxxxx", InstEmit.Ssubl_V, typeof(OpCodeSimdReg64)); + SetA64("0x001110<<1xxxxx001100xxxxxxxxxx", InstEmit.Ssubw_V, typeof(OpCodeSimdReg64)); + SetA64("0x00110000000000xxxxxxxxxxxxxxxx", InstEmit.St__Vms, typeof(OpCodeSimdMemMs64)); + SetA64("0x001100100xxxxxxxxxxxxxxxxxxxxx", InstEmit.St__Vms, typeof(OpCodeSimdMemMs64)); + SetA64("0x00110100x00000xxxxxxxxxxxxxxxx", InstEmit.St__Vss, typeof(OpCodeSimdMemSs64)); + SetA64("0x00110110xxxxxxxxxxxxxxxxxxxxxx", InstEmit.St__Vss, typeof(OpCodeSimdMemSs64)); + SetA64("xx10110xx0xxxxxxxxxxxxxxxxxxxxxx", InstEmit.Stp, typeof(OpCodeSimdMemPair64)); + SetA64("xx111100x00xxxxxxxxx00xxxxxxxxxx", InstEmit.Str, typeof(OpCodeSimdMemImm64)); + SetA64("xx111100x00xxxxxxxxx01xxxxxxxxxx", InstEmit.Str, typeof(OpCodeSimdMemImm64)); + SetA64("xx111100x00xxxxxxxxx11xxxxxxxxxx", InstEmit.Str, typeof(OpCodeSimdMemImm64)); + SetA64("xx111101x0xxxxxxxxxxxxxxxxxxxxxx", InstEmit.Str, typeof(OpCodeSimdMemImm64)); + SetA64("xx111100x01xxxxxxxxx10xxxxxxxxxx", InstEmit.Str, typeof(OpCodeSimdMemReg64)); + SetA64("01111110111xxxxx100001xxxxxxxxxx", InstEmit.Sub_S, typeof(OpCodeSimdReg64)); + SetA64("0>101110<<1xxxxx100001xxxxxxxxxx", InstEmit.Sub_V, typeof(OpCodeSimdReg64)); + SetA64("0x001110<<1xxxxx011000xxxxxxxxxx", InstEmit.Subhn_V, typeof(OpCodeSimdReg64)); + SetA64("01011110xx100000001110xxxxxxxxxx", InstEmit.Suqadd_S, typeof(OpCodeSimd64)); + SetA64("0>001110<<100000001110xxxxxxxxxx", InstEmit.Suqadd_V, typeof(OpCodeSimd64)); + SetA64("0x001110000xxxxx0xx000xxxxxxxxxx", InstEmit.Tbl_V, typeof(OpCodeSimdTbl64)); + SetA64("0>001110<<0xxxxx001010xxxxxxxxxx", InstEmit.Trn1_V, typeof(OpCodeSimdReg64)); + SetA64("0>001110<<0xxxxx011010xxxxxxxxxx", InstEmit.Trn2_V, typeof(OpCodeSimdReg64)); + SetA64("0x101110<<1xxxxx011111xxxxxxxxxx", InstEmit.Uaba_V, typeof(OpCodeSimdReg64)); + SetA64("0x101110<<1xxxxx010100xxxxxxxxxx", InstEmit.Uabal_V, typeof(OpCodeSimdReg64)); + SetA64("0x101110<<1xxxxx011101xxxxxxxxxx", InstEmit.Uabd_V, typeof(OpCodeSimdReg64)); + SetA64("0x101110<<1xxxxx011100xxxxxxxxxx", InstEmit.Uabdl_V, typeof(OpCodeSimdReg64)); + SetA64("0x101110<<100000011010xxxxxxxxxx", InstEmit.Uadalp_V, typeof(OpCodeSimd64)); + SetA64("0x101110<<1xxxxx000000xxxxxxxxxx", InstEmit.Uaddl_V, typeof(OpCodeSimdReg64)); + SetA64("0x101110<<100000001010xxxxxxxxxx", InstEmit.Uaddlp_V, typeof(OpCodeSimd64)); + SetA64("001011100x110000001110xxxxxxxxxx", InstEmit.Uaddlv_V, typeof(OpCodeSimd64)); + SetA64("01101110<<110000001110xxxxxxxxxx", InstEmit.Uaddlv_V, typeof(OpCodeSimd64)); + SetA64("0x101110<<1xxxxx000100xxxxxxxxxx", InstEmit.Uaddw_V, typeof(OpCodeSimdReg64)); + SetA64("x0011110xx100011000000xxxxxxxxxx", InstEmit.Ucvtf_Gp, typeof(OpCodeSimdCvt64)); + SetA64("011111100x100001110110xxxxxxxxxx", InstEmit.Ucvtf_S, typeof(OpCodeSimd64)); + SetA64("0x1011100x100001110110xxxxxxxxxx", InstEmit.Ucvtf_V, typeof(OpCodeSimd64)); + SetA64("0x101110<<1xxxxx000001xxxxxxxxxx", InstEmit.Uhadd_V, typeof(OpCodeSimdReg64)); + SetA64("0x101110<<1xxxxx001001xxxxxxxxxx", InstEmit.Uhsub_V, typeof(OpCodeSimdReg64)); + SetA64("0x101110<<1xxxxx011001xxxxxxxxxx", InstEmit.Umax_V, typeof(OpCodeSimdReg64)); + SetA64("0x101110<<1xxxxx101001xxxxxxxxxx", InstEmit.Umaxp_V, typeof(OpCodeSimdReg64)); + SetA64("0x101110<<1xxxxx011011xxxxxxxxxx", InstEmit.Umin_V, typeof(OpCodeSimdReg64)); + SetA64("0x101110<<1xxxxx101011xxxxxxxxxx", InstEmit.Uminp_V, typeof(OpCodeSimdReg64)); + SetA64("0x101110<<1xxxxx100000xxxxxxxxxx", InstEmit.Umlal_V, typeof(OpCodeSimdReg64)); + SetA64("0x101110<<1xxxxx101000xxxxxxxxxx", InstEmit.Umlsl_V, typeof(OpCodeSimdReg64)); + SetA64("0x001110000xxxxx001111xxxxxxxxxx", InstEmit.Umov_S, typeof(OpCodeSimdIns64)); + SetA64("0x101110<<1xxxxx110000xxxxxxxxxx", InstEmit.Umull_V, typeof(OpCodeSimdReg64)); + SetA64("01111110xx1xxxxx000011xxxxxxxxxx", InstEmit.Uqadd_S, typeof(OpCodeSimdReg64)); + SetA64("0>101110<<1xxxxx000011xxxxxxxxxx", InstEmit.Uqadd_V, typeof(OpCodeSimdReg64)); + SetA64("0111111100>>>xxx100111xxxxxxxxxx", InstEmit.Uqrshrn_S, typeof(OpCodeSimdShImm64)); + SetA64("0x10111100>>>xxx100111xxxxxxxxxx", InstEmit.Uqrshrn_V, typeof(OpCodeSimdShImm64)); + SetA64("0111111100>>>xxx100101xxxxxxxxxx", InstEmit.Uqshrn_S, typeof(OpCodeSimdShImm64)); + SetA64("0x10111100>>>xxx100101xxxxxxxxxx", InstEmit.Uqshrn_V, typeof(OpCodeSimdShImm64)); + SetA64("01111110xx1xxxxx001011xxxxxxxxxx", InstEmit.Uqsub_S, typeof(OpCodeSimdReg64)); + SetA64("0>101110<<1xxxxx001011xxxxxxxxxx", InstEmit.Uqsub_V, typeof(OpCodeSimdReg64)); + SetA64("01111110<<100001010010xxxxxxxxxx", InstEmit.Uqxtn_S, typeof(OpCodeSimd64)); + SetA64("0x101110<<100001010010xxxxxxxxxx", InstEmit.Uqxtn_V, typeof(OpCodeSimd64)); + SetA64("0x101110<<1xxxxx000101xxxxxxxxxx", InstEmit.Urhadd_V, typeof(OpCodeSimdReg64)); + SetA64("0111111101xxxxxx001001xxxxxxxxxx", InstEmit.Urshr_S, typeof(OpCodeSimdShImm64)); + SetA64("0x10111100>>>xxx001001xxxxxxxxxx", InstEmit.Urshr_V, typeof(OpCodeSimdShImm64)); + SetA64("0110111101xxxxxx001001xxxxxxxxxx", InstEmit.Urshr_V, typeof(OpCodeSimdShImm64)); + SetA64("0111111101xxxxxx001101xxxxxxxxxx", InstEmit.Ursra_S, typeof(OpCodeSimdShImm64)); + SetA64("0x10111100>>>xxx001101xxxxxxxxxx", InstEmit.Ursra_V, typeof(OpCodeSimdShImm64)); + SetA64("0110111101xxxxxx001101xxxxxxxxxx", InstEmit.Ursra_V, typeof(OpCodeSimdShImm64)); + SetA64("0>101110<<1xxxxx010001xxxxxxxxxx", InstEmit.Ushl_V, typeof(OpCodeSimdReg64)); + SetA64("0x10111100>>>xxx101001xxxxxxxxxx", InstEmit.Ushll_V, typeof(OpCodeSimdShImm64)); + SetA64("0111111101xxxxxx000001xxxxxxxxxx", InstEmit.Ushr_S, typeof(OpCodeSimdShImm64)); + SetA64("0x10111100>>>xxx000001xxxxxxxxxx", InstEmit.Ushr_V, typeof(OpCodeSimdShImm64)); + SetA64("0110111101xxxxxx000001xxxxxxxxxx", InstEmit.Ushr_V, typeof(OpCodeSimdShImm64)); + SetA64("01111110xx100000001110xxxxxxxxxx", InstEmit.Usqadd_S, typeof(OpCodeSimd64)); + SetA64("0>101110<<100000001110xxxxxxxxxx", InstEmit.Usqadd_V, typeof(OpCodeSimd64)); + SetA64("0111111101xxxxxx000101xxxxxxxxxx", InstEmit.Usra_S, typeof(OpCodeSimdShImm64)); + SetA64("0x10111100>>>xxx000101xxxxxxxxxx", InstEmit.Usra_V, typeof(OpCodeSimdShImm64)); + SetA64("0110111101xxxxxx000101xxxxxxxxxx", InstEmit.Usra_V, typeof(OpCodeSimdShImm64)); + SetA64("0x101110<<1xxxxx001000xxxxxxxxxx", InstEmit.Usubl_V, typeof(OpCodeSimdReg64)); + SetA64("0x101110<<1xxxxx001100xxxxxxxxxx", InstEmit.Usubw_V, typeof(OpCodeSimdReg64)); + SetA64("0>001110<<0xxxxx000110xxxxxxxxxx", InstEmit.Uzp1_V, typeof(OpCodeSimdReg64)); + SetA64("0>001110<<0xxxxx010110xxxxxxxxxx", InstEmit.Uzp2_V, typeof(OpCodeSimdReg64)); + SetA64("0x001110<<100001001010xxxxxxxxxx", InstEmit.Xtn_V, typeof(OpCodeSimd64)); + SetA64("0>001110<<0xxxxx001110xxxxxxxxxx", InstEmit.Zip1_V, typeof(OpCodeSimdReg64)); + SetA64("0>001110<<0xxxxx011110xxxxxxxxxx", InstEmit.Zip2_V, typeof(OpCodeSimdReg64)); +#endregion + +#region "Generate InstA64FastLookup Table (AArch64)" + var tmp = new List[_fastLookupSize]; + for (int i = 0; i < _fastLookupSize; i++) + { + tmp[i] = new List(); + } + + foreach (var inst in _allInstA64) + { + int mask = ToFastLookupIndex(inst.Mask); + int value = ToFastLookupIndex(inst.Value); + + for (int i = 0; i < _fastLookupSize; i++) + { + if ((i & mask) == value) + { + tmp[i].Add(inst); + } + } + } + + for (int i = 0; i < _fastLookupSize; i++) + { + _instA64FastLookup[i] = tmp[i].ToArray(); + } +#endregion + } + + private class InstInfo + { + public int Mask; + public int Value; + + public Inst Inst; + + public InstInfo(int mask, int value, Inst inst) + { + Mask = mask; + Value = value; + Inst = inst; + } + } + + private static List _allInstA32 = new List(); + private static List _allInstA64 = new List(); + + private static int _fastLookupSize = 0x1000; + private static InstInfo[][] _instA64FastLookup = new InstInfo[_fastLookupSize][]; + + private static void SetA32(string encoding, InstInterpreter interpreter, Type type) + { + Set(encoding, new Inst(interpreter, null, type), ExecutionMode.AArch32); + } + + private static void SetA64(string encoding, InstEmitter emitter, Type type) + { + Set(encoding, new Inst(null, emitter, type), ExecutionMode.AArch64); + } + + private static void Set(string encoding, Inst inst, ExecutionMode mode) + { + int bit = encoding.Length - 1; + int value = 0; + int xMask = 0; + int xBits = 0; + + int[] xPos = new int[encoding.Length]; + + int blacklisted = 0; + + for (int index = 0; index < encoding.Length; index++, bit--) + { + //Note: < and > are used on special encodings. + //The < means that we should never have ALL bits with the '<' set. + //So, when the encoding has <<, it means that 00, 01, and 10 are valid, + //but not 11. <<< is 000, 001, ..., 110 but NOT 111, and so on... + //For >, the invalid value is zero. So, for >> 01, 10 and 11 are valid, + //but 00 isn't. + char chr = encoding[index]; + + if (chr == '1') + { + value |= 1 << bit; + } + else if (chr == 'x') + { + xMask |= 1 << bit; + } + else if (chr == '>') + { + xPos[xBits++] = bit; + } + else if (chr == '<') + { + xPos[xBits++] = bit; + + blacklisted |= 1 << bit; + } + else if (chr != '0') + { + throw new ArgumentException(nameof(encoding)); + } + } + + xMask = ~xMask; + + if (xBits == 0) + { + InsertInst(xMask, value, inst, mode); + + return; + } + + for (int index = 0; index < (1 << xBits); index++) + { + int mask = 0; + + for (int x = 0; x < xBits; x++) + { + mask |= ((index >> x) & 1) << xPos[x]; + } + + if (mask != blacklisted) + { + InsertInst(xMask, value | mask, inst, mode); + } + } + } + + private static void InsertInst( + int xMask, + int value, + Inst inst, + ExecutionMode mode) + { + InstInfo info = new InstInfo(xMask, value, inst); + + if (mode == ExecutionMode.AArch64) + { + _allInstA64.Add(info); + } + else + { + _allInstA32.Add(info); + } + } + + public static Inst GetInstA32(int opCode) + { + return GetInstFromList(_allInstA32, opCode); + } + + public static Inst GetInstA64(int opCode) + { + return GetInstFromList(_instA64FastLookup[ToFastLookupIndex(opCode)], opCode); + } + + private static int ToFastLookupIndex(int value) + { + return ((value >> 10) & 0x00F) | ((value >> 18) & 0xFF0); + } + + private static Inst GetInstFromList(IEnumerable instList, int opCode) + { + foreach (var node in instList) + { + if ((opCode & node.Mask) == node.Value) + { + return node.Inst; + } + } + + return Inst.Undefined; + } + } +} diff --git a/Optimizations.cs b/Optimizations.cs new file mode 100644 index 0000000..f2b0ffb --- /dev/null +++ b/Optimizations.cs @@ -0,0 +1,18 @@ +using System.Runtime.Intrinsics.X86; + +public static class Optimizations +{ + internal static bool FastFP = true; + + private static bool _useAllSseIfAvailable = true; + + private static bool _useSseIfAvailable = true; + private static bool _useSse2IfAvailable = true; + private static bool _useSse41IfAvailable = true; + private static bool _useSse42IfAvailable = true; + + internal static bool UseSse = (_useAllSseIfAvailable && _useSseIfAvailable) && Sse.IsSupported; + internal static bool UseSse2 = (_useAllSseIfAvailable && _useSse2IfAvailable) && Sse2.IsSupported; + internal static bool UseSse41 = (_useAllSseIfAvailable && _useSse41IfAvailable) && Sse41.IsSupported; + internal static bool UseSse42 = (_useAllSseIfAvailable && _useSse42IfAvailable) && Sse42.IsSupported; +} diff --git a/State/ARegister.cs b/State/ARegister.cs deleted file mode 100644 index 5861db8..0000000 --- a/State/ARegister.cs +++ /dev/null @@ -1,142 +0,0 @@ -using System; -using System.Reflection; - -namespace ChocolArm64.State -{ - struct ARegister - { - public int Index; - - public ARegisterType Type; - - public ARegister(int Index, ARegisterType Type) - { - this.Index = Index; - this.Type = Type; - } - - public override int GetHashCode() - { - return (ushort)Index | ((ushort)Type << 16); - } - - public override bool Equals(object Obj) - { - return Obj is ARegister Reg && - Reg.Index == Index && - Reg.Type == Type; - } - - public FieldInfo GetField() - { - switch (Type) - { - case ARegisterType.Flag: return GetFieldFlag(); - case ARegisterType.Int: return GetFieldInt(); - case ARegisterType.Vector: return GetFieldVector(); - } - - throw new InvalidOperationException(); - } - - private FieldInfo GetFieldFlag() - { - switch ((APState)Index) - { - case APState.VBit: return GetField(nameof(AThreadState.Overflow)); - case APState.CBit: return GetField(nameof(AThreadState.Carry)); - case APState.ZBit: return GetField(nameof(AThreadState.Zero)); - case APState.NBit: return GetField(nameof(AThreadState.Negative)); - } - - throw new InvalidOperationException(); - } - - private FieldInfo GetFieldInt() - { - switch (Index) - { - case 0: return GetField(nameof(AThreadState.X0)); - case 1: return GetField(nameof(AThreadState.X1)); - case 2: return GetField(nameof(AThreadState.X2)); - case 3: return GetField(nameof(AThreadState.X3)); - case 4: return GetField(nameof(AThreadState.X4)); - case 5: return GetField(nameof(AThreadState.X5)); - case 6: return GetField(nameof(AThreadState.X6)); - case 7: return GetField(nameof(AThreadState.X7)); - case 8: return GetField(nameof(AThreadState.X8)); - case 9: return GetField(nameof(AThreadState.X9)); - case 10: return GetField(nameof(AThreadState.X10)); - case 11: return GetField(nameof(AThreadState.X11)); - case 12: return GetField(nameof(AThreadState.X12)); - case 13: return GetField(nameof(AThreadState.X13)); - case 14: return GetField(nameof(AThreadState.X14)); - case 15: return GetField(nameof(AThreadState.X15)); - case 16: return GetField(nameof(AThreadState.X16)); - case 17: return GetField(nameof(AThreadState.X17)); - case 18: return GetField(nameof(AThreadState.X18)); - case 19: return GetField(nameof(AThreadState.X19)); - case 20: return GetField(nameof(AThreadState.X20)); - case 21: return GetField(nameof(AThreadState.X21)); - case 22: return GetField(nameof(AThreadState.X22)); - case 23: return GetField(nameof(AThreadState.X23)); - case 24: return GetField(nameof(AThreadState.X24)); - case 25: return GetField(nameof(AThreadState.X25)); - case 26: return GetField(nameof(AThreadState.X26)); - case 27: return GetField(nameof(AThreadState.X27)); - case 28: return GetField(nameof(AThreadState.X28)); - case 29: return GetField(nameof(AThreadState.X29)); - case 30: return GetField(nameof(AThreadState.X30)); - case 31: return GetField(nameof(AThreadState.X31)); - } - - throw new InvalidOperationException(); - } - - private FieldInfo GetFieldVector() - { - switch (Index) - { - case 0: return GetField(nameof(AThreadState.V0)); - case 1: return GetField(nameof(AThreadState.V1)); - case 2: return GetField(nameof(AThreadState.V2)); - case 3: return GetField(nameof(AThreadState.V3)); - case 4: return GetField(nameof(AThreadState.V4)); - case 5: return GetField(nameof(AThreadState.V5)); - case 6: return GetField(nameof(AThreadState.V6)); - case 7: return GetField(nameof(AThreadState.V7)); - case 8: return GetField(nameof(AThreadState.V8)); - case 9: return GetField(nameof(AThreadState.V9)); - case 10: return GetField(nameof(AThreadState.V10)); - case 11: return GetField(nameof(AThreadState.V11)); - case 12: return GetField(nameof(AThreadState.V12)); - case 13: return GetField(nameof(AThreadState.V13)); - case 14: return GetField(nameof(AThreadState.V14)); - case 15: return GetField(nameof(AThreadState.V15)); - case 16: return GetField(nameof(AThreadState.V16)); - case 17: return GetField(nameof(AThreadState.V17)); - case 18: return GetField(nameof(AThreadState.V18)); - case 19: return GetField(nameof(AThreadState.V19)); - case 20: return GetField(nameof(AThreadState.V20)); - case 21: return GetField(nameof(AThreadState.V21)); - case 22: return GetField(nameof(AThreadState.V22)); - case 23: return GetField(nameof(AThreadState.V23)); - case 24: return GetField(nameof(AThreadState.V24)); - case 25: return GetField(nameof(AThreadState.V25)); - case 26: return GetField(nameof(AThreadState.V26)); - case 27: return GetField(nameof(AThreadState.V27)); - case 28: return GetField(nameof(AThreadState.V28)); - case 29: return GetField(nameof(AThreadState.V29)); - case 30: return GetField(nameof(AThreadState.V30)); - case 31: return GetField(nameof(AThreadState.V31)); - } - - throw new InvalidOperationException(); - } - - private FieldInfo GetField(string Name) - { - return typeof(AThreadState).GetField(Name); - } - } -} \ No newline at end of file diff --git a/State/AThreadState.cs b/State/CpuThreadState.cs similarity index 57% rename from State/AThreadState.cs rename to State/CpuThreadState.cs index fbfac5b..ed106f7 100644 --- a/State/AThreadState.cs +++ b/State/CpuThreadState.cs @@ -6,17 +6,17 @@ using System.Runtime.Intrinsics; namespace ChocolArm64.State { - public class AThreadState + public class CpuThreadState { - internal const int LRIndex = 30; - internal const int ZRIndex = 31; + internal const int LrIndex = 30; + internal const int ZrIndex = 31; internal const int ErgSizeLog2 = 4; internal const int DczSizeLog2 = 4; private const int MinInstForCheck = 4000000; - internal AExecutionMode ExecutionMode; + internal ExecutionMode ExecutionMode; //AArch32 state. public uint R0, R1, R2, R3, @@ -45,9 +45,9 @@ namespace ChocolArm64.State public bool Running { get; set; } public int Core { get; set; } - private bool Interrupted; + private bool _interrupted; - private int SyncCount; + private int _syncCount; public long TpidrEl0 { get; set; } public long Tpidr { get; set; } @@ -59,10 +59,10 @@ namespace ChocolArm64.State { get { - return (Negative ? (int)APState.N : 0) | - (Zero ? (int)APState.Z : 0) | - (Carry ? (int)APState.C : 0) | - (Overflow ? (int)APState.V : 0); + return (Negative ? (int)PState.N : 0) | + (Zero ? (int)PState.Z : 0) | + (Carry ? (int)PState.C : 0) | + (Overflow ? (int)PState.V : 0); } } @@ -74,38 +74,38 @@ namespace ChocolArm64.State { get { - double Ticks = TickCounter.ElapsedTicks * HostTickFreq; + double ticks = _tickCounter.ElapsedTicks * _hostTickFreq; - return (ulong)(Ticks * CntfrqEl0); + return (ulong)(ticks * CntfrqEl0); } } public event EventHandler Interrupt; - public event EventHandler Break; - public event EventHandler SvcCall; - public event EventHandler Undefined; + public event EventHandler Break; + public event EventHandler SvcCall; + public event EventHandler Undefined; - private static Stopwatch TickCounter; + private static Stopwatch _tickCounter; - private static double HostTickFreq; + private static double _hostTickFreq; - static AThreadState() + static CpuThreadState() { - HostTickFreq = 1.0 / Stopwatch.Frequency; + _hostTickFreq = 1.0 / Stopwatch.Frequency; - TickCounter = new Stopwatch(); + _tickCounter = new Stopwatch(); - TickCounter.Start(); + _tickCounter.Start(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal bool Synchronize(int BbWeight) + internal bool Synchronize(int bbWeight) { //Firing a interrupt frequently is expensive, so we only //do it after a given number of instructions has executed. - SyncCount += BbWeight; + _syncCount += bbWeight; - if (SyncCount >= MinInstForCheck) + if (_syncCount >= MinInstForCheck) { CheckInterrupt(); } @@ -115,50 +115,50 @@ namespace ChocolArm64.State internal void RequestInterrupt() { - Interrupted = true; + _interrupted = true; } [MethodImpl(MethodImplOptions.NoInlining)] private void CheckInterrupt() { - SyncCount = 0; + _syncCount = 0; - if (Interrupted) + if (_interrupted) { - Interrupted = false; + _interrupted = false; Interrupt?.Invoke(this, EventArgs.Empty); } } - internal void OnBreak(long Position, int Imm) + internal void OnBreak(long position, int imm) { - Break?.Invoke(this, new AInstExceptionEventArgs(Position, Imm)); + Break?.Invoke(this, new InstExceptionEventArgs(position, imm)); } - internal void OnSvcCall(long Position, int Imm) + internal void OnSvcCall(long position, int imm) { - SvcCall?.Invoke(this, new AInstExceptionEventArgs(Position, Imm)); + SvcCall?.Invoke(this, new InstExceptionEventArgs(position, imm)); } - internal void OnUndefined(long Position, int RawOpCode) + internal void OnUndefined(long position, int rawOpCode) { - Undefined?.Invoke(this, new AInstUndefinedEventArgs(Position, RawOpCode)); + Undefined?.Invoke(this, new InstUndefinedEventArgs(position, rawOpCode)); } - internal bool GetFpcrFlag(FPCR Flag) + internal bool GetFpcrFlag(Fpcr flag) { - return (Fpcr & (1 << (int)Flag)) != 0; + return (Fpcr & (1 << (int)flag)) != 0; } - internal void SetFpsrFlag(FPSR Flag) + internal void SetFpsrFlag(Fpsr flag) { - Fpsr |= 1 << (int)Flag; + Fpsr |= 1 << (int)flag; } - internal ARoundMode FPRoundingMode() + internal RoundMode FPRoundingMode() { - return (ARoundMode)((Fpcr >> (int)FPCR.RMode) & 3); + return (RoundMode)((Fpcr >> (int)State.Fpcr.RMode) & 3); } } } diff --git a/State/AExecutionMode.cs b/State/ExecutionMode.cs similarity index 75% rename from State/AExecutionMode.cs rename to State/ExecutionMode.cs index 8632da7..4b8c17c 100644 --- a/State/AExecutionMode.cs +++ b/State/ExecutionMode.cs @@ -1,6 +1,6 @@ namespace ChocolArm64.State { - enum AExecutionMode + enum ExecutionMode { AArch32, AArch64 diff --git a/State/FPCR.cs b/State/FPCR.cs deleted file mode 100644 index 8f47cf9..0000000 --- a/State/FPCR.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace ChocolArm64.State -{ - enum FPCR - { - UFE = 11, - RMode = 22, - FZ = 24, - DN = 25, - AHP = 26 - } -} diff --git a/State/FPSR.cs b/State/FPSR.cs deleted file mode 100644 index d71cde7..0000000 --- a/State/FPSR.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace ChocolArm64.State -{ - enum FPSR - { - UFC = 3, - QC = 27 - } -} diff --git a/State/FPExc.cs b/State/FpExc.cs similarity index 92% rename from State/FPExc.cs rename to State/FpExc.cs index a665957..5cb7a40 100644 --- a/State/FPExc.cs +++ b/State/FpExc.cs @@ -1,6 +1,6 @@ namespace ChocolArm64.State { - enum FPExc + enum FpExc { InvalidOp = 0, DivideByZero = 1, diff --git a/State/FPType.cs b/State/FpType.cs similarity index 88% rename from State/FPType.cs rename to State/FpType.cs index b00f5fe..fc27910 100644 --- a/State/FPType.cs +++ b/State/FpType.cs @@ -1,6 +1,6 @@ namespace ChocolArm64.State { - enum FPType + enum FpType { Nonzero, Zero, diff --git a/State/Fpcr.cs b/State/Fpcr.cs new file mode 100644 index 0000000..908faee --- /dev/null +++ b/State/Fpcr.cs @@ -0,0 +1,11 @@ +namespace ChocolArm64.State +{ + enum Fpcr + { + Ufe = 11, + RMode = 22, + Fz = 24, + Dn = 25, + Ahp = 26 + } +} diff --git a/State/Fpsr.cs b/State/Fpsr.cs new file mode 100644 index 0000000..ba551ee --- /dev/null +++ b/State/Fpsr.cs @@ -0,0 +1,8 @@ +namespace ChocolArm64.State +{ + enum Fpsr + { + Ufc = 3, + Qc = 27 + } +} diff --git a/State/APState.cs b/State/PState.cs similarity index 75% rename from State/APState.cs rename to State/PState.cs index aaf0ff0..40636c8 100644 --- a/State/APState.cs +++ b/State/PState.cs @@ -3,7 +3,7 @@ using System; namespace ChocolArm64.State { [Flags] - enum APState + enum PState { VBit = 28, CBit = 29, @@ -15,9 +15,9 @@ namespace ChocolArm64.State Z = 1 << ZBit, N = 1 << NBit, - NZ = N | Z, - CV = C | V, + Nz = N | Z, + Cv = C | V, - NZCV = NZ | CV + Nzcv = Nz | Cv } } diff --git a/State/Register.cs b/State/Register.cs new file mode 100644 index 0000000..ea29e7b --- /dev/null +++ b/State/Register.cs @@ -0,0 +1,142 @@ +using System; +using System.Reflection; + +namespace ChocolArm64.State +{ + struct Register + { + public int Index; + + public RegisterType Type; + + public Register(int index, RegisterType type) + { + Index = index; + Type = type; + } + + public override int GetHashCode() + { + return (ushort)Index | ((ushort)Type << 16); + } + + public override bool Equals(object obj) + { + return obj is Register reg && + reg.Index == Index && + reg.Type == Type; + } + + public FieldInfo GetField() + { + switch (Type) + { + case RegisterType.Flag: return GetFieldFlag(); + case RegisterType.Int: return GetFieldInt(); + case RegisterType.Vector: return GetFieldVector(); + } + + throw new InvalidOperationException(); + } + + private FieldInfo GetFieldFlag() + { + switch ((PState)Index) + { + case PState.VBit: return GetField(nameof(CpuThreadState.Overflow)); + case PState.CBit: return GetField(nameof(CpuThreadState.Carry)); + case PState.ZBit: return GetField(nameof(CpuThreadState.Zero)); + case PState.NBit: return GetField(nameof(CpuThreadState.Negative)); + } + + throw new InvalidOperationException(); + } + + private FieldInfo GetFieldInt() + { + switch (Index) + { + case 0: return GetField(nameof(CpuThreadState.X0)); + case 1: return GetField(nameof(CpuThreadState.X1)); + case 2: return GetField(nameof(CpuThreadState.X2)); + case 3: return GetField(nameof(CpuThreadState.X3)); + case 4: return GetField(nameof(CpuThreadState.X4)); + case 5: return GetField(nameof(CpuThreadState.X5)); + case 6: return GetField(nameof(CpuThreadState.X6)); + case 7: return GetField(nameof(CpuThreadState.X7)); + case 8: return GetField(nameof(CpuThreadState.X8)); + case 9: return GetField(nameof(CpuThreadState.X9)); + case 10: return GetField(nameof(CpuThreadState.X10)); + case 11: return GetField(nameof(CpuThreadState.X11)); + case 12: return GetField(nameof(CpuThreadState.X12)); + case 13: return GetField(nameof(CpuThreadState.X13)); + case 14: return GetField(nameof(CpuThreadState.X14)); + case 15: return GetField(nameof(CpuThreadState.X15)); + case 16: return GetField(nameof(CpuThreadState.X16)); + case 17: return GetField(nameof(CpuThreadState.X17)); + case 18: return GetField(nameof(CpuThreadState.X18)); + case 19: return GetField(nameof(CpuThreadState.X19)); + case 20: return GetField(nameof(CpuThreadState.X20)); + case 21: return GetField(nameof(CpuThreadState.X21)); + case 22: return GetField(nameof(CpuThreadState.X22)); + case 23: return GetField(nameof(CpuThreadState.X23)); + case 24: return GetField(nameof(CpuThreadState.X24)); + case 25: return GetField(nameof(CpuThreadState.X25)); + case 26: return GetField(nameof(CpuThreadState.X26)); + case 27: return GetField(nameof(CpuThreadState.X27)); + case 28: return GetField(nameof(CpuThreadState.X28)); + case 29: return GetField(nameof(CpuThreadState.X29)); + case 30: return GetField(nameof(CpuThreadState.X30)); + case 31: return GetField(nameof(CpuThreadState.X31)); + } + + throw new InvalidOperationException(); + } + + private FieldInfo GetFieldVector() + { + switch (Index) + { + case 0: return GetField(nameof(CpuThreadState.V0)); + case 1: return GetField(nameof(CpuThreadState.V1)); + case 2: return GetField(nameof(CpuThreadState.V2)); + case 3: return GetField(nameof(CpuThreadState.V3)); + case 4: return GetField(nameof(CpuThreadState.V4)); + case 5: return GetField(nameof(CpuThreadState.V5)); + case 6: return GetField(nameof(CpuThreadState.V6)); + case 7: return GetField(nameof(CpuThreadState.V7)); + case 8: return GetField(nameof(CpuThreadState.V8)); + case 9: return GetField(nameof(CpuThreadState.V9)); + case 10: return GetField(nameof(CpuThreadState.V10)); + case 11: return GetField(nameof(CpuThreadState.V11)); + case 12: return GetField(nameof(CpuThreadState.V12)); + case 13: return GetField(nameof(CpuThreadState.V13)); + case 14: return GetField(nameof(CpuThreadState.V14)); + case 15: return GetField(nameof(CpuThreadState.V15)); + case 16: return GetField(nameof(CpuThreadState.V16)); + case 17: return GetField(nameof(CpuThreadState.V17)); + case 18: return GetField(nameof(CpuThreadState.V18)); + case 19: return GetField(nameof(CpuThreadState.V19)); + case 20: return GetField(nameof(CpuThreadState.V20)); + case 21: return GetField(nameof(CpuThreadState.V21)); + case 22: return GetField(nameof(CpuThreadState.V22)); + case 23: return GetField(nameof(CpuThreadState.V23)); + case 24: return GetField(nameof(CpuThreadState.V24)); + case 25: return GetField(nameof(CpuThreadState.V25)); + case 26: return GetField(nameof(CpuThreadState.V26)); + case 27: return GetField(nameof(CpuThreadState.V27)); + case 28: return GetField(nameof(CpuThreadState.V28)); + case 29: return GetField(nameof(CpuThreadState.V29)); + case 30: return GetField(nameof(CpuThreadState.V30)); + case 31: return GetField(nameof(CpuThreadState.V31)); + } + + throw new InvalidOperationException(); + } + + private FieldInfo GetField(string name) + { + return typeof(CpuThreadState).GetField(name); + } + } +} \ No newline at end of file diff --git a/State/ARegisterSize.cs b/State/RegisterSize.cs similarity index 56% rename from State/ARegisterSize.cs rename to State/RegisterSize.cs index 144f36b..7cc9959 100644 --- a/State/ARegisterSize.cs +++ b/State/RegisterSize.cs @@ -1,10 +1,10 @@ namespace ChocolArm64.State { - enum ARegisterSize + enum RegisterSize { Int32, Int64, - SIMD64, - SIMD128 + Simd64, + Simd128 } } \ No newline at end of file diff --git a/State/ARegisterType.cs b/State/RegisterType.cs similarity index 77% rename from State/ARegisterType.cs rename to State/RegisterType.cs index f9776bb..4476d04 100644 --- a/State/ARegisterType.cs +++ b/State/RegisterType.cs @@ -1,6 +1,6 @@ namespace ChocolArm64.State { - enum ARegisterType + enum RegisterType { Flag, Int, diff --git a/State/ARoundMode.cs b/State/RoundMode.cs similarity index 89% rename from State/ARoundMode.cs rename to State/RoundMode.cs index 297d013..b687cc8 100644 --- a/State/ARoundMode.cs +++ b/State/RoundMode.cs @@ -1,6 +1,6 @@ namespace ChocolArm64.State { - enum ARoundMode + enum RoundMode { ToNearest = 0, TowardsPlusInfinity = 1, diff --git a/TranslatedSub.cs b/TranslatedSub.cs new file mode 100644 index 0000000..8b3ec3f --- /dev/null +++ b/TranslatedSub.cs @@ -0,0 +1,150 @@ +using ChocolArm64.Memory; +using ChocolArm64.State; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Reflection; +using System.Reflection.Emit; + +namespace ChocolArm64 +{ + class TranslatedSub + { + private delegate long Aa64Subroutine(CpuThreadState register, MemoryManager memory); + + private const int MinCallCountForReJit = 250; + + private Aa64Subroutine _execDelegate; + + public static int StateArgIdx { get; private set; } + public static int MemoryArgIdx { get; private set; } + + public static Type[] FixedArgTypes { get; private set; } + + public DynamicMethod Method { get; private set; } + + public ReadOnlyCollection Params { get; private set; } + + private HashSet _callers; + + private TranslatedSubType _type; + + private int _callCount; + + private bool _needsReJit; + + public TranslatedSub(DynamicMethod method, List Params) + { + if (method == null) + { + throw new ArgumentNullException(nameof(method)); + } + + if (Params == null) + { + throw new ArgumentNullException(nameof(Params)); + } + + Method = method; + this.Params = Params.AsReadOnly(); + + _callers = new HashSet(); + + PrepareDelegate(); + } + + static TranslatedSub() + { + MethodInfo mthdInfo = typeof(Aa64Subroutine).GetMethod("Invoke"); + + ParameterInfo[] Params = mthdInfo.GetParameters(); + + FixedArgTypes = new Type[Params.Length]; + + for (int index = 0; index < Params.Length; index++) + { + Type paramType = Params[index].ParameterType; + + FixedArgTypes[index] = paramType; + + if (paramType == typeof(CpuThreadState)) + { + StateArgIdx = index; + } + else if (paramType == typeof(MemoryManager)) + { + MemoryArgIdx = index; + } + } + } + + private void PrepareDelegate() + { + string name = $"{Method.Name}_Dispatch"; + + DynamicMethod mthd = new DynamicMethod(name, typeof(long), FixedArgTypes); + + ILGenerator generator = mthd.GetILGenerator(); + + generator.EmitLdargSeq(FixedArgTypes.Length); + + foreach (Register reg in Params) + { + generator.EmitLdarg(StateArgIdx); + + generator.Emit(OpCodes.Ldfld, reg.GetField()); + } + + generator.Emit(OpCodes.Call, Method); + generator.Emit(OpCodes.Ret); + + _execDelegate = (Aa64Subroutine)mthd.CreateDelegate(typeof(Aa64Subroutine)); + } + + public bool ShouldReJit() + { + if (_needsReJit && _callCount < MinCallCountForReJit) + { + _callCount++; + + return false; + } + + return _needsReJit; + } + + public long Execute(CpuThreadState threadState, MemoryManager memory) + { + return _execDelegate(threadState, memory); + } + + public void AddCaller(long position) + { + lock (_callers) + { + _callers.Add(position); + } + } + + public long[] GetCallerPositions() + { + lock (_callers) + { + return _callers.ToArray(); + } + } + + public void SetType(TranslatedSubType type) + { + _type = type; + + if (type == TranslatedSubType.SubTier0) + { + _needsReJit = true; + } + } + + public void MarkForReJit() => _needsReJit = true; + } +} \ No newline at end of file diff --git a/ATranslatedSubType.cs b/TranslatedSubType.cs similarity index 71% rename from ATranslatedSubType.cs rename to TranslatedSubType.cs index 14893ab..f57aea9 100644 --- a/ATranslatedSubType.cs +++ b/TranslatedSubType.cs @@ -1,6 +1,6 @@ namespace ChocolArm64 { - enum ATranslatedSubType + enum TranslatedSubType { SubTier0, SubTier1 diff --git a/Translation/AILBarrier.cs b/Translation/AILBarrier.cs deleted file mode 100644 index 25b08de..0000000 --- a/Translation/AILBarrier.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace ChocolArm64.Translation -{ - struct AILBarrier : IAILEmit - { - public void Emit(AILEmitter Context) { } - } -} \ No newline at end of file diff --git a/Translation/AILBlock.cs b/Translation/AILBlock.cs deleted file mode 100644 index e580e09..0000000 --- a/Translation/AILBlock.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System.Collections.Generic; - -namespace ChocolArm64.Translation -{ - class AILBlock : IAILEmit - { - public long IntInputs { get; private set; } - public long IntOutputs { get; private set; } - public long IntAwOutputs { get; private set; } - - public long VecInputs { get; private set; } - public long VecOutputs { get; private set; } - public long VecAwOutputs { get; private set; } - - public bool HasStateStore { get; private set; } - - public List ILEmitters { get; private set; } - - public AILBlock Next { get; set; } - public AILBlock Branch { get; set; } - - public AILBlock() - { - ILEmitters = new List(); - } - - public void Add(IAILEmit ILEmitter) - { - if (ILEmitter is AILBarrier) - { - //Those barriers are used to separate the groups of CIL - //opcodes emitted by each ARM instruction. - //We can only consider the new outputs for doing input elimination - //after all the CIL opcodes used by the instruction being emitted. - IntAwOutputs = IntOutputs; - VecAwOutputs = VecOutputs; - } - else if (ILEmitter is AILOpCodeLoad Ld && AILEmitter.IsRegIndex(Ld.Index)) - { - switch (Ld.IoType) - { - case AIoType.Flag: IntInputs |= ((1L << Ld.Index) << 32) & ~IntAwOutputs; break; - case AIoType.Int: IntInputs |= (1L << Ld.Index) & ~IntAwOutputs; break; - case AIoType.Vector: VecInputs |= (1L << Ld.Index) & ~VecAwOutputs; break; - } - } - else if (ILEmitter is AILOpCodeStore St) - { - if (AILEmitter.IsRegIndex(St.Index)) - { - switch (St.IoType) - { - case AIoType.Flag: IntOutputs |= (1L << St.Index) << 32; break; - case AIoType.Int: IntOutputs |= 1L << St.Index; break; - case AIoType.Vector: VecOutputs |= 1L << St.Index; break; - } - } - - if (St.IoType == AIoType.Fields) - { - HasStateStore = true; - } - } - - ILEmitters.Add(ILEmitter); - } - - public void Emit(AILEmitter Context) - { - foreach (IAILEmit ILEmitter in ILEmitters) - { - ILEmitter.Emit(Context); - } - } - } -} \ No newline at end of file diff --git a/Translation/AILEmitter.cs b/Translation/AILEmitter.cs deleted file mode 100644 index 8c78053..0000000 --- a/Translation/AILEmitter.cs +++ /dev/null @@ -1,188 +0,0 @@ -using ChocolArm64.Decoder; -using ChocolArm64.State; -using System; -using System.Collections.Generic; -using System.Reflection.Emit; -using System.Runtime.Intrinsics; - -namespace ChocolArm64.Translation -{ - class AILEmitter - { - public ALocalAlloc LocalAlloc { get; private set; } - - public ILGenerator Generator { get; private set; } - - private Dictionary Locals; - - private AILBlock[] ILBlocks; - - private AILBlock Root; - - private ATranslatedSub Subroutine; - - private string SubName; - - private int LocalsCount; - - public AILEmitter(ABlock[] Graph, ABlock Root, string SubName) - { - this.SubName = SubName; - - Locals = new Dictionary(); - - ILBlocks = new AILBlock[Graph.Length]; - - AILBlock GetBlock(int Index) - { - if (Index < 0 || Index >= ILBlocks.Length) - { - return null; - } - - if (ILBlocks[Index] == null) - { - ILBlocks[Index] = new AILBlock(); - } - - return ILBlocks[Index]; - } - - for (int Index = 0; Index < ILBlocks.Length; Index++) - { - AILBlock Block = GetBlock(Index); - - Block.Next = GetBlock(Array.IndexOf(Graph, Graph[Index].Next)); - Block.Branch = GetBlock(Array.IndexOf(Graph, Graph[Index].Branch)); - } - - this.Root = ILBlocks[Array.IndexOf(Graph, Root)]; - } - - public AILBlock GetILBlock(int Index) => ILBlocks[Index]; - - public ATranslatedSub GetSubroutine() - { - LocalAlloc = new ALocalAlloc(ILBlocks, Root); - - InitSubroutine(); - InitLocals(); - - foreach (AILBlock ILBlock in ILBlocks) - { - ILBlock.Emit(this); - } - - return Subroutine; - } - - private void InitSubroutine() - { - List Params = new List(); - - void SetParams(long Inputs, ARegisterType BaseType) - { - for (int Bit = 0; Bit < 64; Bit++) - { - long Mask = 1L << Bit; - - if ((Inputs & Mask) != 0) - { - Params.Add(GetRegFromBit(Bit, BaseType)); - } - } - } - - SetParams(LocalAlloc.GetIntInputs(Root), ARegisterType.Int); - SetParams(LocalAlloc.GetVecInputs(Root), ARegisterType.Vector); - - DynamicMethod Mthd = new DynamicMethod(SubName, typeof(long), GetParamTypes(Params)); - - Generator = Mthd.GetILGenerator(); - - Subroutine = new ATranslatedSub(Mthd, Params); - } - - private void InitLocals() - { - int ParamsStart = ATranslatedSub.FixedArgTypes.Length; - - Locals = new Dictionary(); - - for (int Index = 0; Index < Subroutine.Params.Count; Index++) - { - ARegister Reg = Subroutine.Params[Index]; - - Generator.EmitLdarg(Index + ParamsStart); - Generator.EmitStloc(GetLocalIndex(Reg)); - } - } - - private Type[] GetParamTypes(IList Params) - { - Type[] FixedArgs = ATranslatedSub.FixedArgTypes; - - Type[] Output = new Type[Params.Count + FixedArgs.Length]; - - FixedArgs.CopyTo(Output, 0); - - int TypeIdx = FixedArgs.Length; - - for (int Index = 0; Index < Params.Count; Index++) - { - Output[TypeIdx++] = GetFieldType(Params[Index].Type); - } - - return Output; - } - - public int GetLocalIndex(ARegister Reg) - { - if (!Locals.TryGetValue(Reg, out int Index)) - { - Generator.DeclareLocal(GetLocalType(Reg)); - - Index = LocalsCount++; - - Locals.Add(Reg, Index); - } - - return Index; - } - - public Type GetLocalType(ARegister Reg) => GetFieldType(Reg.Type); - - public Type GetFieldType(ARegisterType RegType) - { - switch (RegType) - { - case ARegisterType.Flag: return typeof(bool); - case ARegisterType.Int: return typeof(ulong); - case ARegisterType.Vector: return typeof(Vector128); - } - - throw new ArgumentException(nameof(RegType)); - } - - public static ARegister GetRegFromBit(int Bit, ARegisterType BaseType) - { - if (Bit < 32) - { - return new ARegister(Bit, BaseType); - } - else if (BaseType == ARegisterType.Int) - { - return new ARegister(Bit & 0x1f, ARegisterType.Flag); - } - else - { - throw new ArgumentOutOfRangeException(nameof(Bit)); - } - } - - public static bool IsRegIndex(int Index) - { - return Index >= 0 && Index < 32; - } - } -} \ No newline at end of file diff --git a/Translation/AILEmitterCtx.cs b/Translation/AILEmitterCtx.cs deleted file mode 100644 index e5288bc..0000000 --- a/Translation/AILEmitterCtx.cs +++ /dev/null @@ -1,554 +0,0 @@ -using ChocolArm64.Decoder; -using ChocolArm64.Instruction; -using ChocolArm64.State; -using System; -using System.Collections.Generic; -using System.Reflection; -using System.Reflection.Emit; - -namespace ChocolArm64.Translation -{ - class AILEmitterCtx - { - private ATranslatorCache Cache; - - private Dictionary Labels; - - private int BlkIndex; - private int OpcIndex; - - private ABlock[] Graph; - private ABlock Root; - public ABlock CurrBlock => Graph[BlkIndex]; - public AOpCode CurrOp => Graph[BlkIndex].OpCodes[OpcIndex]; - - private AILEmitter Emitter; - - private AILBlock ILBlock; - - private AOpCode OptOpLastCompare; - private AOpCode OptOpLastFlagSet; - - //This is the index of the temporary register, used to store temporary - //values needed by some functions, since IL doesn't have a swap instruction. - //You can use any value here as long it doesn't conflict with the indices - //for the other registers. Any value >= 64 or < 0 will do. - private const int Tmp1Index = -1; - private const int Tmp2Index = -2; - private const int Tmp3Index = -3; - private const int Tmp4Index = -4; - private const int Tmp5Index = -5; - private const int Tmp6Index = -6; - - public AILEmitterCtx( - ATranslatorCache Cache, - ABlock[] Graph, - ABlock Root, - string SubName) - { - this.Cache = Cache ?? throw new ArgumentNullException(nameof(Cache)); - this.Graph = Graph ?? throw new ArgumentNullException(nameof(Graph)); - this.Root = Root ?? throw new ArgumentNullException(nameof(Root)); - - Labels = new Dictionary(); - - Emitter = new AILEmitter(Graph, Root, SubName); - - ILBlock = Emitter.GetILBlock(0); - - OpcIndex = -1; - - if (Graph.Length == 0 || !AdvanceOpCode()) - { - throw new ArgumentException(nameof(Graph)); - } - } - - public ATranslatedSub GetSubroutine() - { - return Emitter.GetSubroutine(); - } - - public bool AdvanceOpCode() - { - if (OpcIndex + 1 == CurrBlock.OpCodes.Count && - BlkIndex + 1 == Graph.Length) - { - return false; - } - - while (++OpcIndex >= (CurrBlock?.OpCodes.Count ?? 0)) - { - BlkIndex++; - OpcIndex = -1; - - OptOpLastFlagSet = null; - OptOpLastCompare = null; - - ILBlock = Emitter.GetILBlock(BlkIndex); - } - - return true; - } - - public void EmitOpCode() - { - if (OpcIndex == 0) - { - MarkLabel(GetLabel(CurrBlock.Position)); - - EmitSynchronization(); - } - - CurrOp.Emitter(this); - - ILBlock.Add(new AILBarrier()); - } - - private void EmitSynchronization() - { - EmitLdarg(ATranslatedSub.StateArgIdx); - - EmitLdc_I4(CurrBlock.OpCodes.Count); - - EmitPrivateCall(typeof(AThreadState), nameof(AThreadState.Synchronize)); - - EmitLdc_I4(0); - - AILLabel LblContinue = new AILLabel(); - - Emit(OpCodes.Bne_Un_S, LblContinue); - - EmitLdc_I8(0); - - Emit(OpCodes.Ret); - - MarkLabel(LblContinue); - } - - public bool TryOptEmitSubroutineCall() - { - if (CurrBlock.Next == null) - { - return false; - } - - if (CurrOp.Emitter != AInstEmit.Bl) - { - return false; - } - - if (!Cache.TryGetSubroutine(((AOpCodeBImmAl)CurrOp).Imm, out ATranslatedSub Subroutine)) - { - return false; - } - - for (int Index = 0; Index < ATranslatedSub.FixedArgTypes.Length; Index++) - { - EmitLdarg(Index); - } - - foreach (ARegister Reg in Subroutine.Params) - { - switch (Reg.Type) - { - case ARegisterType.Flag: Ldloc(Reg.Index, AIoType.Flag); break; - case ARegisterType.Int: Ldloc(Reg.Index, AIoType.Int); break; - case ARegisterType.Vector: Ldloc(Reg.Index, AIoType.Vector); break; - } - } - - EmitCall(Subroutine.Method); - - Subroutine.AddCaller(Root.Position); - - return true; - } - - public void TryOptMarkCondWithoutCmp() - { - OptOpLastCompare = CurrOp; - - AInstEmitAluHelper.EmitDataLoadOpers(this); - - Stloc(Tmp4Index, AIoType.Int); - Stloc(Tmp3Index, AIoType.Int); - } - - private Dictionary BranchOps = new Dictionary() - { - { ACond.Eq, OpCodes.Beq }, - { ACond.Ne, OpCodes.Bne_Un }, - { ACond.Ge_Un, OpCodes.Bge_Un }, - { ACond.Lt_Un, OpCodes.Blt_Un }, - { ACond.Gt_Un, OpCodes.Bgt_Un }, - { ACond.Le_Un, OpCodes.Ble_Un }, - { ACond.Ge, OpCodes.Bge }, - { ACond.Lt, OpCodes.Blt }, - { ACond.Gt, OpCodes.Bgt }, - { ACond.Le, OpCodes.Ble } - }; - - public void EmitCondBranch(AILLabel Target, ACond Cond) - { - OpCode ILOp; - - int IntCond = (int)Cond; - - if (OptOpLastCompare != null && - OptOpLastCompare == OptOpLastFlagSet && BranchOps.ContainsKey(Cond)) - { - Ldloc(Tmp3Index, AIoType.Int, OptOpLastCompare.RegisterSize); - Ldloc(Tmp4Index, AIoType.Int, OptOpLastCompare.RegisterSize); - - ILOp = BranchOps[Cond]; - } - else if (IntCond < 14) - { - int CondTrue = IntCond >> 1; - - switch (CondTrue) - { - case 0: EmitLdflg((int)APState.ZBit); break; - case 1: EmitLdflg((int)APState.CBit); break; - case 2: EmitLdflg((int)APState.NBit); break; - case 3: EmitLdflg((int)APState.VBit); break; - - case 4: - EmitLdflg((int)APState.CBit); - EmitLdflg((int)APState.ZBit); - - Emit(OpCodes.Not); - Emit(OpCodes.And); - break; - - case 5: - case 6: - EmitLdflg((int)APState.NBit); - EmitLdflg((int)APState.VBit); - - Emit(OpCodes.Ceq); - - if (CondTrue == 6) - { - EmitLdflg((int)APState.ZBit); - - Emit(OpCodes.Not); - Emit(OpCodes.And); - } - break; - } - - ILOp = (IntCond & 1) != 0 - ? OpCodes.Brfalse - : OpCodes.Brtrue; - } - else - { - ILOp = OpCodes.Br; - } - - Emit(ILOp, Target); - } - - public void EmitCast(AIntType IntType) - { - switch (IntType) - { - case AIntType.UInt8: Emit(OpCodes.Conv_U1); break; - case AIntType.UInt16: Emit(OpCodes.Conv_U2); break; - case AIntType.UInt32: Emit(OpCodes.Conv_U4); break; - case AIntType.UInt64: Emit(OpCodes.Conv_U8); break; - case AIntType.Int8: Emit(OpCodes.Conv_I1); break; - case AIntType.Int16: Emit(OpCodes.Conv_I2); break; - case AIntType.Int32: Emit(OpCodes.Conv_I4); break; - case AIntType.Int64: Emit(OpCodes.Conv_I8); break; - } - - bool Sz64 = CurrOp.RegisterSize != ARegisterSize.Int32; - - if (Sz64 == (IntType == AIntType.UInt64 || - IntType == AIntType.Int64)) - { - return; - } - - if (Sz64) - { - Emit(IntType >= AIntType.Int8 - ? OpCodes.Conv_I8 - : OpCodes.Conv_U8); - } - else - { - Emit(OpCodes.Conv_U4); - } - } - - public void EmitLsl(int Amount) => EmitILShift(Amount, OpCodes.Shl); - public void EmitLsr(int Amount) => EmitILShift(Amount, OpCodes.Shr_Un); - public void EmitAsr(int Amount) => EmitILShift(Amount, OpCodes.Shr); - - private void EmitILShift(int Amount, OpCode ILOp) - { - if (Amount > 0) - { - EmitLdc_I4(Amount); - - Emit(ILOp); - } - } - - public void EmitRor(int Amount) - { - if (Amount > 0) - { - Stloc(Tmp2Index, AIoType.Int); - Ldloc(Tmp2Index, AIoType.Int); - - EmitLdc_I4(Amount); - - Emit(OpCodes.Shr_Un); - - Ldloc(Tmp2Index, AIoType.Int); - - EmitLdc_I4(CurrOp.GetBitsCount() - Amount); - - Emit(OpCodes.Shl); - Emit(OpCodes.Or); - } - } - - public AILLabel GetLabel(long Position) - { - if (!Labels.TryGetValue(Position, out AILLabel Output)) - { - Output = new AILLabel(); - - Labels.Add(Position, Output); - } - - return Output; - } - - public void MarkLabel(AILLabel Label) - { - ILBlock.Add(Label); - } - - public void Emit(OpCode ILOp) - { - ILBlock.Add(new AILOpCode(ILOp)); - } - - public void Emit(OpCode ILOp, AILLabel Label) - { - ILBlock.Add(new AILOpCodeBranch(ILOp, Label)); - } - - public void Emit(string Text) - { - ILBlock.Add(new AILOpCodeLog(Text)); - } - - public void EmitLdarg(int Index) - { - ILBlock.Add(new AILOpCodeLoad(Index, AIoType.Arg)); - } - - public void EmitLdintzr(int Index) - { - if (Index != AThreadState.ZRIndex) - { - EmitLdint(Index); - } - else - { - EmitLdc_I(0); - } - } - - public void EmitStintzr(int Index) - { - if (Index != AThreadState.ZRIndex) - { - EmitStint(Index); - } - else - { - Emit(OpCodes.Pop); - } - } - - public void EmitLoadState(ABlock RetBlk) - { - ILBlock.Add(new AILOpCodeLoad(Array.IndexOf(Graph, RetBlk), AIoType.Fields)); - } - - public void EmitStoreState() - { - ILBlock.Add(new AILOpCodeStore(Array.IndexOf(Graph, CurrBlock), AIoType.Fields)); - } - - public void EmitLdtmp() => EmitLdint(Tmp1Index); - public void EmitSttmp() => EmitStint(Tmp1Index); - - public void EmitLdvectmp() => EmitLdvec(Tmp5Index); - public void EmitStvectmp() => EmitStvec(Tmp5Index); - - public void EmitLdvectmp2() => EmitLdvec(Tmp6Index); - public void EmitStvectmp2() => EmitStvec(Tmp6Index); - - public void EmitLdint(int Index) => Ldloc(Index, AIoType.Int); - public void EmitStint(int Index) => Stloc(Index, AIoType.Int); - - public void EmitLdvec(int Index) => Ldloc(Index, AIoType.Vector); - public void EmitStvec(int Index) => Stloc(Index, AIoType.Vector); - - public void EmitLdflg(int Index) => Ldloc(Index, AIoType.Flag); - public void EmitStflg(int Index) - { - OptOpLastFlagSet = CurrOp; - - Stloc(Index, AIoType.Flag); - } - - private void Ldloc(int Index, AIoType IoType) - { - ILBlock.Add(new AILOpCodeLoad(Index, IoType, CurrOp.RegisterSize)); - } - - private void Ldloc(int Index, AIoType IoType, ARegisterSize RegisterSize) - { - ILBlock.Add(new AILOpCodeLoad(Index, IoType, RegisterSize)); - } - - private void Stloc(int Index, AIoType IoType) - { - ILBlock.Add(new AILOpCodeStore(Index, IoType, CurrOp.RegisterSize)); - } - - public void EmitCallPropGet(Type ObjType, string PropName) - { - if (ObjType == null) - { - throw new ArgumentNullException(nameof(ObjType)); - } - - if (PropName == null) - { - throw new ArgumentNullException(nameof(PropName)); - } - - EmitCall(ObjType.GetMethod($"get_{PropName}")); - } - - public void EmitCallPropSet(Type ObjType, string PropName) - { - if (ObjType == null) - { - throw new ArgumentNullException(nameof(ObjType)); - } - - if (PropName == null) - { - throw new ArgumentNullException(nameof(PropName)); - } - - EmitCall(ObjType.GetMethod($"set_{PropName}")); - } - - public void EmitCall(Type ObjType, string MthdName) - { - if (ObjType == null) - { - throw new ArgumentNullException(nameof(ObjType)); - } - - if (MthdName == null) - { - throw new ArgumentNullException(nameof(MthdName)); - } - - EmitCall(ObjType.GetMethod(MthdName)); - } - - public void EmitPrivateCall(Type ObjType, string MthdName) - { - if (ObjType == null) - { - throw new ArgumentNullException(nameof(ObjType)); - } - - if (MthdName == null) - { - throw new ArgumentNullException(nameof(MthdName)); - } - - EmitCall(ObjType.GetMethod(MthdName, BindingFlags.Instance | BindingFlags.NonPublic)); - } - - public void EmitCall(MethodInfo MthdInfo) - { - if (MthdInfo == null) - { - throw new ArgumentNullException(nameof(MthdInfo)); - } - - ILBlock.Add(new AILOpCodeCall(MthdInfo)); - } - - public void EmitLdc_I(long Value) - { - if (CurrOp.RegisterSize == ARegisterSize.Int32) - { - EmitLdc_I4((int)Value); - } - else - { - EmitLdc_I8(Value); - } - } - - public void EmitLdc_I4(int Value) - { - ILBlock.Add(new AILOpCodeConst(Value)); - } - - public void EmitLdc_I8(long Value) - { - ILBlock.Add(new AILOpCodeConst(Value)); - } - - public void EmitLdc_R4(float Value) - { - ILBlock.Add(new AILOpCodeConst(Value)); - } - - public void EmitLdc_R8(double Value) - { - ILBlock.Add(new AILOpCodeConst(Value)); - } - - public void EmitZNFlagCheck() - { - EmitZNCheck(OpCodes.Ceq, (int)APState.ZBit); - EmitZNCheck(OpCodes.Clt, (int)APState.NBit); - } - - private void EmitZNCheck(OpCode ILCmpOp, int Flag) - { - Emit(OpCodes.Dup); - Emit(OpCodes.Ldc_I4_0); - - if (CurrOp.RegisterSize != ARegisterSize.Int32) - { - Emit(OpCodes.Conv_I8); - } - - Emit(ILCmpOp); - - EmitStflg(Flag); - } - } -} diff --git a/Translation/AILLabel.cs b/Translation/AILLabel.cs deleted file mode 100644 index 0ee39ad..0000000 --- a/Translation/AILLabel.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Reflection.Emit; - -namespace ChocolArm64.Translation -{ - class AILLabel : IAILEmit - { - private bool HasLabel; - - private Label Lbl; - - public void Emit(AILEmitter Context) - { - Context.Generator.MarkLabel(GetLabel(Context)); - } - - public Label GetLabel(AILEmitter Context) - { - if (!HasLabel) - { - Lbl = Context.Generator.DefineLabel(); - - HasLabel = true; - } - - return Lbl; - } - } -} \ No newline at end of file diff --git a/Translation/AILOpCode.cs b/Translation/AILOpCode.cs deleted file mode 100644 index a4bc93a..0000000 --- a/Translation/AILOpCode.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.Reflection.Emit; - -namespace ChocolArm64.Translation -{ - struct AILOpCode : IAILEmit - { - private OpCode ILOp; - - public AILOpCode(OpCode ILOp) - { - this.ILOp = ILOp; - } - - public void Emit(AILEmitter Context) - { - Context.Generator.Emit(ILOp); - } - } -} \ No newline at end of file diff --git a/Translation/AILOpCodeBranch.cs b/Translation/AILOpCodeBranch.cs deleted file mode 100644 index e4caad1..0000000 --- a/Translation/AILOpCodeBranch.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Reflection.Emit; - -namespace ChocolArm64.Translation -{ - struct AILOpCodeBranch : IAILEmit - { - private OpCode ILOp; - private AILLabel Label; - - public AILOpCodeBranch(OpCode ILOp, AILLabel Label) - { - this.ILOp = ILOp; - this.Label = Label; - } - - public void Emit(AILEmitter Context) - { - Context.Generator.Emit(ILOp, Label.GetLabel(Context)); - } - } -} \ No newline at end of file diff --git a/Translation/AILOpCodeCall.cs b/Translation/AILOpCodeCall.cs deleted file mode 100644 index 8cd944e..0000000 --- a/Translation/AILOpCodeCall.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.Reflection; -using System.Reflection.Emit; - -namespace ChocolArm64.Translation -{ - struct AILOpCodeCall : IAILEmit - { - private MethodInfo MthdInfo; - - public AILOpCodeCall(MethodInfo MthdInfo) - { - this.MthdInfo = MthdInfo; - } - - public void Emit(AILEmitter Context) - { - Context.Generator.Emit(OpCodes.Call, MthdInfo); - } - } -} \ No newline at end of file diff --git a/Translation/AILOpCodeConst.cs b/Translation/AILOpCodeConst.cs deleted file mode 100644 index fee8640..0000000 --- a/Translation/AILOpCodeConst.cs +++ /dev/null @@ -1,65 +0,0 @@ -using System.Reflection.Emit; -using System.Runtime.InteropServices; - -namespace ChocolArm64.Translation -{ - class AILOpCodeConst : IAILEmit - { - [StructLayout(LayoutKind.Explicit, Size = 8)] - private struct ImmVal - { - [FieldOffset(0)] public int I4; - [FieldOffset(0)] public long I8; - [FieldOffset(0)] public float R4; - [FieldOffset(0)] public double R8; - } - - private ImmVal Value; - - private enum ConstType - { - Int32, - Int64, - Single, - Double - } - - private ConstType Type; - - private AILOpCodeConst(ConstType Type) - { - this.Type = Type; - } - - public AILOpCodeConst(int Value) : this(ConstType.Int32) - { - this.Value = new ImmVal { I4 = Value }; - } - - public AILOpCodeConst(long Value) : this(ConstType.Int64) - { - this.Value = new ImmVal { I8 = Value }; - } - - public AILOpCodeConst(float Value) : this(ConstType.Single) - { - this.Value = new ImmVal { R4 = Value }; - } - - public AILOpCodeConst(double Value) : this(ConstType.Double) - { - this.Value = new ImmVal { R8 = Value }; - } - - public void Emit(AILEmitter Context) - { - switch (Type) - { - case ConstType.Int32: Context.Generator.EmitLdc_I4(Value.I4); break; - case ConstType.Int64: Context.Generator.Emit(OpCodes.Ldc_I8, Value.I8); break; - case ConstType.Single: Context.Generator.Emit(OpCodes.Ldc_R4, Value.R4); break; - case ConstType.Double: Context.Generator.Emit(OpCodes.Ldc_R8, Value.R8); break; - } - } - } -} \ No newline at end of file diff --git a/Translation/AILOpCodeLoad.cs b/Translation/AILOpCodeLoad.cs deleted file mode 100644 index d60ce53..0000000 --- a/Translation/AILOpCodeLoad.cs +++ /dev/null @@ -1,75 +0,0 @@ -using ChocolArm64.State; -using System.Reflection.Emit; - -namespace ChocolArm64.Translation -{ - struct AILOpCodeLoad : IAILEmit - { - public int Index { get; private set; } - - public AIoType IoType { get; private set; } - - public ARegisterSize RegisterSize { get; private set; } - - public AILOpCodeLoad(int Index, AIoType IoType, ARegisterSize RegisterSize = 0) - { - this.Index = Index; - this.IoType = IoType; - this.RegisterSize = RegisterSize; - } - - public void Emit(AILEmitter Context) - { - switch (IoType) - { - case AIoType.Arg: Context.Generator.EmitLdarg(Index); break; - - case AIoType.Fields: - { - long IntInputs = Context.LocalAlloc.GetIntInputs(Context.GetILBlock(Index)); - long VecInputs = Context.LocalAlloc.GetVecInputs(Context.GetILBlock(Index)); - - LoadLocals(Context, IntInputs, ARegisterType.Int); - LoadLocals(Context, VecInputs, ARegisterType.Vector); - - break; - } - - case AIoType.Flag: EmitLdloc(Context, Index, ARegisterType.Flag); break; - case AIoType.Int: EmitLdloc(Context, Index, ARegisterType.Int); break; - case AIoType.Vector: EmitLdloc(Context, Index, ARegisterType.Vector); break; - } - } - - private void LoadLocals(AILEmitter Context, long Inputs, ARegisterType BaseType) - { - for (int Bit = 0; Bit < 64; Bit++) - { - long Mask = 1L << Bit; - - if ((Inputs & Mask) != 0) - { - ARegister Reg = AILEmitter.GetRegFromBit(Bit, BaseType); - - Context.Generator.EmitLdarg(ATranslatedSub.StateArgIdx); - Context.Generator.Emit(OpCodes.Ldfld, Reg.GetField()); - - Context.Generator.EmitStloc(Context.GetLocalIndex(Reg)); - } - } - } - - private void EmitLdloc(AILEmitter Context, int Index, ARegisterType RegisterType) - { - ARegister Reg = new ARegister(Index, RegisterType); - - Context.Generator.EmitLdloc(Context.GetLocalIndex(Reg)); - - if (RegisterType == ARegisterType.Int && - RegisterSize == ARegisterSize.Int32) - { - Context.Generator.Emit(OpCodes.Conv_U4); - } - } - } -} \ No newline at end of file diff --git a/Translation/AILOpCodeLog.cs b/Translation/AILOpCodeLog.cs deleted file mode 100644 index 1338ca1..0000000 --- a/Translation/AILOpCodeLog.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace ChocolArm64.Translation -{ - struct AILOpCodeLog : IAILEmit - { - private string Text; - - public AILOpCodeLog(string Text) - { - this.Text = Text; - } - - public void Emit(AILEmitter Context) - { - Context.Generator.EmitWriteLine(Text); - } - } -} \ No newline at end of file diff --git a/Translation/AILOpCodeStore.cs b/Translation/AILOpCodeStore.cs deleted file mode 100644 index a0feb43..0000000 --- a/Translation/AILOpCodeStore.cs +++ /dev/null @@ -1,75 +0,0 @@ -using ChocolArm64.State; -using System.Reflection.Emit; - -namespace ChocolArm64.Translation -{ - struct AILOpCodeStore : IAILEmit - { - public int Index { get; private set; } - - public AIoType IoType { get; private set; } - - public ARegisterSize RegisterSize { get; private set; } - - public AILOpCodeStore(int Index, AIoType IoType, ARegisterSize RegisterSize = 0) - { - this.Index = Index; - this.IoType = IoType; - this.RegisterSize = RegisterSize; - } - - public void Emit(AILEmitter Context) - { - switch (IoType) - { - case AIoType.Arg: Context.Generator.EmitStarg(Index); break; - - case AIoType.Fields: - { - long IntOutputs = Context.LocalAlloc.GetIntOutputs(Context.GetILBlock(Index)); - long VecOutputs = Context.LocalAlloc.GetVecOutputs(Context.GetILBlock(Index)); - - StoreLocals(Context, IntOutputs, ARegisterType.Int); - StoreLocals(Context, VecOutputs, ARegisterType.Vector); - - break; - } - - case AIoType.Flag: EmitStloc(Context, Index, ARegisterType.Flag); break; - case AIoType.Int: EmitStloc(Context, Index, ARegisterType.Int); break; - case AIoType.Vector: EmitStloc(Context, Index, ARegisterType.Vector); break; - } - } - - private void StoreLocals(AILEmitter Context, long Outputs, ARegisterType BaseType) - { - for (int Bit = 0; Bit < 64; Bit++) - { - long Mask = 1L << Bit; - - if ((Outputs & Mask) != 0) - { - ARegister Reg = AILEmitter.GetRegFromBit(Bit, BaseType); - - Context.Generator.EmitLdarg(ATranslatedSub.StateArgIdx); - Context.Generator.EmitLdloc(Context.GetLocalIndex(Reg)); - - Context.Generator.Emit(OpCodes.Stfld, Reg.GetField()); - } - } - } - - private void EmitStloc(AILEmitter Context, int Index, ARegisterType RegisterType) - { - ARegister Reg = new ARegister(Index, RegisterType); - - if (RegisterType == ARegisterType.Int && - RegisterSize == ARegisterSize.Int32) - { - Context.Generator.Emit(OpCodes.Conv_U8); - } - - Context.Generator.EmitStloc(Context.GetLocalIndex(Reg)); - } - } -} \ No newline at end of file diff --git a/Translation/ALocalAlloc.cs b/Translation/ALocalAlloc.cs deleted file mode 100644 index 8e90478..0000000 --- a/Translation/ALocalAlloc.cs +++ /dev/null @@ -1,229 +0,0 @@ -using System.Collections.Generic; - -namespace ChocolArm64.Translation -{ - class ALocalAlloc - { - private class PathIo - { - private Dictionary AllInputs; - private Dictionary CmnOutputs; - - private long AllOutputs; - - public PathIo() - { - AllInputs = new Dictionary(); - CmnOutputs = new Dictionary(); - } - - public PathIo(AILBlock Root, long Inputs, long Outputs) : this() - { - Set(Root, Inputs, Outputs); - } - - public void Set(AILBlock Root, long Inputs, long Outputs) - { - if (!AllInputs.TryAdd(Root, Inputs)) - { - AllInputs[Root] |= Inputs; - } - - if (!CmnOutputs.TryAdd(Root, Outputs)) - { - CmnOutputs[Root] &= Outputs; - } - - AllOutputs |= Outputs; - } - - public long GetInputs(AILBlock Root) - { - if (AllInputs.TryGetValue(Root, out long Inputs)) - { - return Inputs | (AllOutputs & ~CmnOutputs[Root]); - } - - return 0; - } - - public long GetOutputs() - { - return AllOutputs; - } - } - - private Dictionary IntPaths; - private Dictionary VecPaths; - - private struct BlockIo - { - public AILBlock Block; - public AILBlock Entry; - - public long IntInputs; - public long VecInputs; - public long IntOutputs; - public long VecOutputs; - } - - private const int MaxOptGraphLength = 40; - - public ALocalAlloc(AILBlock[] Graph, AILBlock Root) - { - IntPaths = new Dictionary(); - VecPaths = new Dictionary(); - - if (Graph.Length > 1 && - Graph.Length < MaxOptGraphLength) - { - InitializeOptimal(Graph, Root); - } - else - { - InitializeFast(Graph); - } - } - - private void InitializeOptimal(AILBlock[] Graph, AILBlock Root) - { - //This will go through all possible paths on the graph, - //and store all inputs/outputs for each block. A register - //that was previously written to already is not considered an input. - //When a block can be reached by more than one path, then the - //output from all paths needs to be set for this block, and - //only outputs present in all of the parent blocks can be considered - //when doing input elimination. Each block chain have a root, that's where - //the code starts executing. They are present on the subroutine start point, - //and on call return points too (address written to X30 by BL). - HashSet Visited = new HashSet(); - - Queue Unvisited = new Queue(); - - void Enqueue(BlockIo Block) - { - if (!Visited.Contains(Block)) - { - Unvisited.Enqueue(Block); - - Visited.Add(Block); - } - } - - Enqueue(new BlockIo() - { - Block = Root, - Entry = Root - }); - - while (Unvisited.Count > 0) - { - BlockIo Current = Unvisited.Dequeue(); - - Current.IntInputs |= Current.Block.IntInputs & ~Current.IntOutputs; - Current.VecInputs |= Current.Block.VecInputs & ~Current.VecOutputs; - Current.IntOutputs |= Current.Block.IntOutputs; - Current.VecOutputs |= Current.Block.VecOutputs; - - //Check if this is a exit block - //(a block that returns or calls another sub). - if ((Current.Block.Next == null && - Current.Block.Branch == null) || Current.Block.HasStateStore) - { - if (!IntPaths.TryGetValue(Current.Block, out PathIo IntPath)) - { - IntPaths.Add(Current.Block, IntPath = new PathIo()); - } - - if (!VecPaths.TryGetValue(Current.Block, out PathIo VecPath)) - { - VecPaths.Add(Current.Block, VecPath = new PathIo()); - } - - IntPath.Set(Current.Entry, Current.IntInputs, Current.IntOutputs); - VecPath.Set(Current.Entry, Current.VecInputs, Current.VecOutputs); - } - - void EnqueueFromCurrent(AILBlock Block, bool RetTarget) - { - BlockIo BlkIO = new BlockIo() { Block = Block }; - - if (RetTarget) - { - BlkIO.Entry = Block; - } - else - { - BlkIO.Entry = Current.Entry; - BlkIO.IntInputs = Current.IntInputs; - BlkIO.VecInputs = Current.VecInputs; - BlkIO.IntOutputs = Current.IntOutputs; - BlkIO.VecOutputs = Current.VecOutputs; - } - - Enqueue(BlkIO); - } - - if (Current.Block.Next != null) - { - EnqueueFromCurrent(Current.Block.Next, Current.Block.HasStateStore); - } - - if (Current.Block.Branch != null) - { - EnqueueFromCurrent(Current.Block.Branch, false); - } - } - } - - private void InitializeFast(AILBlock[] Graph) - { - //This is WAY faster than InitializeOptimal, but results in - //uneeded loads and stores, so the resulting code will be slower. - long IntInputs = 0, IntOutputs = 0; - long VecInputs = 0, VecOutputs = 0; - - foreach (AILBlock Block in Graph) - { - IntInputs |= Block.IntInputs; - IntOutputs |= Block.IntOutputs; - VecInputs |= Block.VecInputs; - VecOutputs |= Block.VecOutputs; - } - - //It's possible that not all code paths writes to those output registers, - //in those cases if we attempt to write an output registers that was - //not written, we will be just writing zero and messing up the old register value. - //So we just need to ensure that all outputs are loaded. - if (Graph.Length > 1) - { - IntInputs |= IntOutputs; - VecInputs |= VecOutputs; - } - - foreach (AILBlock Block in Graph) - { - IntPaths.Add(Block, new PathIo(Block, IntInputs, IntOutputs)); - VecPaths.Add(Block, new PathIo(Block, VecInputs, VecOutputs)); - } - } - - public long GetIntInputs(AILBlock Root) => GetInputsImpl(Root, IntPaths.Values); - public long GetVecInputs(AILBlock Root) => GetInputsImpl(Root, VecPaths.Values); - - private long GetInputsImpl(AILBlock Root, IEnumerable Values) - { - long Inputs = 0; - - foreach (PathIo Path in Values) - { - Inputs |= Path.GetInputs(Root); - } - - return Inputs; - } - - public long GetIntOutputs(AILBlock Block) => IntPaths[Block].GetOutputs(); - public long GetVecOutputs(AILBlock Block) => VecPaths[Block].GetOutputs(); - } -} \ No newline at end of file diff --git a/Translation/IAILEmit.cs b/Translation/IAILEmit.cs deleted file mode 100644 index 6e4e9a7..0000000 --- a/Translation/IAILEmit.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace ChocolArm64.Translation -{ - interface IAILEmit - { - void Emit(AILEmitter Context); - } -} \ No newline at end of file diff --git a/Translation/IILEmit.cs b/Translation/IILEmit.cs new file mode 100644 index 0000000..3c3925e --- /dev/null +++ b/Translation/IILEmit.cs @@ -0,0 +1,7 @@ +namespace ChocolArm64.Translation +{ + interface IILEmit + { + void Emit(ILEmitter context); + } +} \ No newline at end of file diff --git a/Translation/ILBarrier.cs b/Translation/ILBarrier.cs new file mode 100644 index 0000000..f931e99 --- /dev/null +++ b/Translation/ILBarrier.cs @@ -0,0 +1,7 @@ +namespace ChocolArm64.Translation +{ + struct ILBarrier : IILEmit + { + public void Emit(ILEmitter context) { } + } +} \ No newline at end of file diff --git a/Translation/ILBlock.cs b/Translation/ILBlock.cs new file mode 100644 index 0000000..d51e8d9 --- /dev/null +++ b/Translation/ILBlock.cs @@ -0,0 +1,76 @@ +using System.Collections.Generic; + +namespace ChocolArm64.Translation +{ + class ILBlock : IILEmit + { + public long IntInputs { get; private set; } + public long IntOutputs { get; private set; } + public long IntAwOutputs { get; private set; } + + public long VecInputs { get; private set; } + public long VecOutputs { get; private set; } + public long VecAwOutputs { get; private set; } + + public bool HasStateStore { get; private set; } + + public List IlEmitters { get; private set; } + + public ILBlock Next { get; set; } + public ILBlock Branch { get; set; } + + public ILBlock() + { + IlEmitters = new List(); + } + + public void Add(IILEmit ilEmitter) + { + if (ilEmitter is ILBarrier) + { + //Those barriers are used to separate the groups of CIL + //opcodes emitted by each ARM instruction. + //We can only consider the new outputs for doing input elimination + //after all the CIL opcodes used by the instruction being emitted. + IntAwOutputs = IntOutputs; + VecAwOutputs = VecOutputs; + } + else if (ilEmitter is IlOpCodeLoad ld && ILEmitter.IsRegIndex(ld.Index)) + { + switch (ld.IoType) + { + case IoType.Flag: IntInputs |= ((1L << ld.Index) << 32) & ~IntAwOutputs; break; + case IoType.Int: IntInputs |= (1L << ld.Index) & ~IntAwOutputs; break; + case IoType.Vector: VecInputs |= (1L << ld.Index) & ~VecAwOutputs; break; + } + } + else if (ilEmitter is IlOpCodeStore st) + { + if (ILEmitter.IsRegIndex(st.Index)) + { + switch (st.IoType) + { + case IoType.Flag: IntOutputs |= (1L << st.Index) << 32; break; + case IoType.Int: IntOutputs |= 1L << st.Index; break; + case IoType.Vector: VecOutputs |= 1L << st.Index; break; + } + } + + if (st.IoType == IoType.Fields) + { + HasStateStore = true; + } + } + + IlEmitters.Add(ilEmitter); + } + + public void Emit(ILEmitter context) + { + foreach (IILEmit ilEmitter in IlEmitters) + { + ilEmitter.Emit(context); + } + } + } +} \ No newline at end of file diff --git a/Translation/ILEmitter.cs b/Translation/ILEmitter.cs new file mode 100644 index 0000000..543528d --- /dev/null +++ b/Translation/ILEmitter.cs @@ -0,0 +1,188 @@ +using ChocolArm64.Decoders; +using ChocolArm64.State; +using System; +using System.Collections.Generic; +using System.Reflection.Emit; +using System.Runtime.Intrinsics; + +namespace ChocolArm64.Translation +{ + class ILEmitter + { + public LocalAlloc LocalAlloc { get; private set; } + + public ILGenerator Generator { get; private set; } + + private Dictionary _locals; + + private ILBlock[] _ilBlocks; + + private ILBlock _root; + + private TranslatedSub _subroutine; + + private string _subName; + + private int _localsCount; + + public ILEmitter(Block[] graph, Block root, string subName) + { + _subName = subName; + + _locals = new Dictionary(); + + _ilBlocks = new ILBlock[graph.Length]; + + ILBlock GetBlock(int index) + { + if (index < 0 || index >= _ilBlocks.Length) + { + return null; + } + + if (_ilBlocks[index] == null) + { + _ilBlocks[index] = new ILBlock(); + } + + return _ilBlocks[index]; + } + + for (int index = 0; index < _ilBlocks.Length; index++) + { + ILBlock block = GetBlock(index); + + block.Next = GetBlock(Array.IndexOf(graph, graph[index].Next)); + block.Branch = GetBlock(Array.IndexOf(graph, graph[index].Branch)); + } + + _root = _ilBlocks[Array.IndexOf(graph, root)]; + } + + public ILBlock GetIlBlock(int index) => _ilBlocks[index]; + + public TranslatedSub GetSubroutine() + { + LocalAlloc = new LocalAlloc(_ilBlocks, _root); + + InitSubroutine(); + InitLocals(); + + foreach (ILBlock ilBlock in _ilBlocks) + { + ilBlock.Emit(this); + } + + return _subroutine; + } + + private void InitSubroutine() + { + List Params = new List(); + + void SetParams(long inputs, RegisterType baseType) + { + for (int bit = 0; bit < 64; bit++) + { + long mask = 1L << bit; + + if ((inputs & mask) != 0) + { + Params.Add(GetRegFromBit(bit, baseType)); + } + } + } + + SetParams(LocalAlloc.GetIntInputs(_root), RegisterType.Int); + SetParams(LocalAlloc.GetVecInputs(_root), RegisterType.Vector); + + DynamicMethod mthd = new DynamicMethod(_subName, typeof(long), GetParamTypes(Params)); + + Generator = mthd.GetILGenerator(); + + _subroutine = new TranslatedSub(mthd, Params); + } + + private void InitLocals() + { + int paramsStart = TranslatedSub.FixedArgTypes.Length; + + _locals = new Dictionary(); + + for (int index = 0; index < _subroutine.Params.Count; index++) + { + Register reg = _subroutine.Params[index]; + + Generator.EmitLdarg(index + paramsStart); + Generator.EmitStloc(GetLocalIndex(reg)); + } + } + + private Type[] GetParamTypes(IList Params) + { + Type[] fixedArgs = TranslatedSub.FixedArgTypes; + + Type[] output = new Type[Params.Count + fixedArgs.Length]; + + fixedArgs.CopyTo(output, 0); + + int typeIdx = fixedArgs.Length; + + for (int index = 0; index < Params.Count; index++) + { + output[typeIdx++] = GetFieldType(Params[index].Type); + } + + return output; + } + + public int GetLocalIndex(Register reg) + { + if (!_locals.TryGetValue(reg, out int index)) + { + Generator.DeclareLocal(GetLocalType(reg)); + + index = _localsCount++; + + _locals.Add(reg, index); + } + + return index; + } + + public Type GetLocalType(Register reg) => GetFieldType(reg.Type); + + public Type GetFieldType(RegisterType regType) + { + switch (regType) + { + case RegisterType.Flag: return typeof(bool); + case RegisterType.Int: return typeof(ulong); + case RegisterType.Vector: return typeof(Vector128); + } + + throw new ArgumentException(nameof(regType)); + } + + public static Register GetRegFromBit(int bit, RegisterType baseType) + { + if (bit < 32) + { + return new Register(bit, baseType); + } + else if (baseType == RegisterType.Int) + { + return new Register(bit & 0x1f, RegisterType.Flag); + } + else + { + throw new ArgumentOutOfRangeException(nameof(bit)); + } + } + + public static bool IsRegIndex(int index) + { + return index >= 0 && index < 32; + } + } +} \ No newline at end of file diff --git a/Translation/ILEmitterCtx.cs b/Translation/ILEmitterCtx.cs new file mode 100644 index 0000000..c1d1be3 --- /dev/null +++ b/Translation/ILEmitterCtx.cs @@ -0,0 +1,554 @@ +using ChocolArm64.Decoders; +using ChocolArm64.Instructions; +using ChocolArm64.State; +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Reflection.Emit; + +namespace ChocolArm64.Translation +{ + class ILEmitterCtx + { + private TranslatorCache _cache; + + private Dictionary _labels; + + private int _blkIndex; + private int _opcIndex; + + private Block[] _graph; + private Block _root; + public Block CurrBlock => _graph[_blkIndex]; + public OpCode64 CurrOp => _graph[_blkIndex].OpCodes[_opcIndex]; + + private ILEmitter _emitter; + + private ILBlock _ilBlock; + + private OpCode64 _optOpLastCompare; + private OpCode64 _optOpLastFlagSet; + + //This is the index of the temporary register, used to store temporary + //values needed by some functions, since IL doesn't have a swap instruction. + //You can use any value here as long it doesn't conflict with the indices + //for the other registers. Any value >= 64 or < 0 will do. + private const int Tmp1Index = -1; + private const int Tmp2Index = -2; + private const int Tmp3Index = -3; + private const int Tmp4Index = -4; + private const int Tmp5Index = -5; + private const int Tmp6Index = -6; + + public ILEmitterCtx( + TranslatorCache cache, + Block[] graph, + Block root, + string subName) + { + _cache = cache ?? throw new ArgumentNullException(nameof(cache)); + _graph = graph ?? throw new ArgumentNullException(nameof(graph)); + _root = root ?? throw new ArgumentNullException(nameof(root)); + + _labels = new Dictionary(); + + _emitter = new ILEmitter(graph, root, subName); + + _ilBlock = _emitter.GetIlBlock(0); + + _opcIndex = -1; + + if (graph.Length == 0 || !AdvanceOpCode()) + { + throw new ArgumentException(nameof(graph)); + } + } + + public TranslatedSub GetSubroutine() + { + return _emitter.GetSubroutine(); + } + + public bool AdvanceOpCode() + { + if (_opcIndex + 1 == CurrBlock.OpCodes.Count && + _blkIndex + 1 == _graph.Length) + { + return false; + } + + while (++_opcIndex >= (CurrBlock?.OpCodes.Count ?? 0)) + { + _blkIndex++; + _opcIndex = -1; + + _optOpLastFlagSet = null; + _optOpLastCompare = null; + + _ilBlock = _emitter.GetIlBlock(_blkIndex); + } + + return true; + } + + public void EmitOpCode() + { + if (_opcIndex == 0) + { + MarkLabel(GetLabel(CurrBlock.Position)); + + EmitSynchronization(); + } + + CurrOp.Emitter(this); + + _ilBlock.Add(new ILBarrier()); + } + + private void EmitSynchronization() + { + EmitLdarg(TranslatedSub.StateArgIdx); + + EmitLdc_I4(CurrBlock.OpCodes.Count); + + EmitPrivateCall(typeof(CpuThreadState), nameof(CpuThreadState.Synchronize)); + + EmitLdc_I4(0); + + ILLabel lblContinue = new ILLabel(); + + Emit(OpCodes.Bne_Un_S, lblContinue); + + EmitLdc_I8(0); + + Emit(OpCodes.Ret); + + MarkLabel(lblContinue); + } + + public bool TryOptEmitSubroutineCall() + { + if (CurrBlock.Next == null) + { + return false; + } + + if (CurrOp.Emitter != InstEmit.Bl) + { + return false; + } + + if (!_cache.TryGetSubroutine(((OpCodeBImmAl64)CurrOp).Imm, out TranslatedSub subroutine)) + { + return false; + } + + for (int index = 0; index < TranslatedSub.FixedArgTypes.Length; index++) + { + EmitLdarg(index); + } + + foreach (Register reg in subroutine.Params) + { + switch (reg.Type) + { + case RegisterType.Flag: Ldloc(reg.Index, IoType.Flag); break; + case RegisterType.Int: Ldloc(reg.Index, IoType.Int); break; + case RegisterType.Vector: Ldloc(reg.Index, IoType.Vector); break; + } + } + + EmitCall(subroutine.Method); + + subroutine.AddCaller(_root.Position); + + return true; + } + + public void TryOptMarkCondWithoutCmp() + { + _optOpLastCompare = CurrOp; + + InstEmitAluHelper.EmitDataLoadOpers(this); + + Stloc(Tmp4Index, IoType.Int); + Stloc(Tmp3Index, IoType.Int); + } + + private Dictionary _branchOps = new Dictionary() + { + { Cond.Eq, OpCodes.Beq }, + { Cond.Ne, OpCodes.Bne_Un }, + { Cond.GeUn, OpCodes.Bge_Un }, + { Cond.LtUn, OpCodes.Blt_Un }, + { Cond.GtUn, OpCodes.Bgt_Un }, + { Cond.LeUn, OpCodes.Ble_Un }, + { Cond.Ge, OpCodes.Bge }, + { Cond.Lt, OpCodes.Blt }, + { Cond.Gt, OpCodes.Bgt }, + { Cond.Le, OpCodes.Ble } + }; + + public void EmitCondBranch(ILLabel target, Cond cond) + { + System.Reflection.Emit.OpCode ilOp; + + int intCond = (int)cond; + + if (_optOpLastCompare != null && + _optOpLastCompare == _optOpLastFlagSet && _branchOps.ContainsKey(cond)) + { + Ldloc(Tmp3Index, IoType.Int, _optOpLastCompare.RegisterSize); + Ldloc(Tmp4Index, IoType.Int, _optOpLastCompare.RegisterSize); + + ilOp = _branchOps[cond]; + } + else if (intCond < 14) + { + int condTrue = intCond >> 1; + + switch (condTrue) + { + case 0: EmitLdflg((int)PState.ZBit); break; + case 1: EmitLdflg((int)PState.CBit); break; + case 2: EmitLdflg((int)PState.NBit); break; + case 3: EmitLdflg((int)PState.VBit); break; + + case 4: + EmitLdflg((int)PState.CBit); + EmitLdflg((int)PState.ZBit); + + Emit(OpCodes.Not); + Emit(OpCodes.And); + break; + + case 5: + case 6: + EmitLdflg((int)PState.NBit); + EmitLdflg((int)PState.VBit); + + Emit(OpCodes.Ceq); + + if (condTrue == 6) + { + EmitLdflg((int)PState.ZBit); + + Emit(OpCodes.Not); + Emit(OpCodes.And); + } + break; + } + + ilOp = (intCond & 1) != 0 + ? OpCodes.Brfalse + : OpCodes.Brtrue; + } + else + { + ilOp = OpCodes.Br; + } + + Emit(ilOp, target); + } + + public void EmitCast(IntType intType) + { + switch (intType) + { + case IntType.UInt8: Emit(OpCodes.Conv_U1); break; + case IntType.UInt16: Emit(OpCodes.Conv_U2); break; + case IntType.UInt32: Emit(OpCodes.Conv_U4); break; + case IntType.UInt64: Emit(OpCodes.Conv_U8); break; + case IntType.Int8: Emit(OpCodes.Conv_I1); break; + case IntType.Int16: Emit(OpCodes.Conv_I2); break; + case IntType.Int32: Emit(OpCodes.Conv_I4); break; + case IntType.Int64: Emit(OpCodes.Conv_I8); break; + } + + bool sz64 = CurrOp.RegisterSize != RegisterSize.Int32; + + if (sz64 == (intType == IntType.UInt64 || + intType == IntType.Int64)) + { + return; + } + + if (sz64) + { + Emit(intType >= IntType.Int8 + ? OpCodes.Conv_I8 + : OpCodes.Conv_U8); + } + else + { + Emit(OpCodes.Conv_U4); + } + } + + public void EmitLsl(int amount) => EmitIlShift(amount, OpCodes.Shl); + public void EmitLsr(int amount) => EmitIlShift(amount, OpCodes.Shr_Un); + public void EmitAsr(int amount) => EmitIlShift(amount, OpCodes.Shr); + + private void EmitIlShift(int amount, System.Reflection.Emit.OpCode ilOp) + { + if (amount > 0) + { + EmitLdc_I4(amount); + + Emit(ilOp); + } + } + + public void EmitRor(int amount) + { + if (amount > 0) + { + Stloc(Tmp2Index, IoType.Int); + Ldloc(Tmp2Index, IoType.Int); + + EmitLdc_I4(amount); + + Emit(OpCodes.Shr_Un); + + Ldloc(Tmp2Index, IoType.Int); + + EmitLdc_I4(CurrOp.GetBitsCount() - amount); + + Emit(OpCodes.Shl); + Emit(OpCodes.Or); + } + } + + public ILLabel GetLabel(long position) + { + if (!_labels.TryGetValue(position, out ILLabel output)) + { + output = new ILLabel(); + + _labels.Add(position, output); + } + + return output; + } + + public void MarkLabel(ILLabel label) + { + _ilBlock.Add(label); + } + + public void Emit(System.Reflection.Emit.OpCode ilOp) + { + _ilBlock.Add(new ILOpCode(ilOp)); + } + + public void Emit(System.Reflection.Emit.OpCode ilOp, ILLabel label) + { + _ilBlock.Add(new ILOpCodeBranch(ilOp, label)); + } + + public void Emit(string text) + { + _ilBlock.Add(new ILOpCodeLog(text)); + } + + public void EmitLdarg(int index) + { + _ilBlock.Add(new IlOpCodeLoad(index, IoType.Arg)); + } + + public void EmitLdintzr(int index) + { + if (index != CpuThreadState.ZrIndex) + { + EmitLdint(index); + } + else + { + EmitLdc_I(0); + } + } + + public void EmitStintzr(int index) + { + if (index != CpuThreadState.ZrIndex) + { + EmitStint(index); + } + else + { + Emit(OpCodes.Pop); + } + } + + public void EmitLoadState(Block retBlk) + { + _ilBlock.Add(new IlOpCodeLoad(Array.IndexOf(_graph, retBlk), IoType.Fields)); + } + + public void EmitStoreState() + { + _ilBlock.Add(new IlOpCodeStore(Array.IndexOf(_graph, CurrBlock), IoType.Fields)); + } + + public void EmitLdtmp() => EmitLdint(Tmp1Index); + public void EmitSttmp() => EmitStint(Tmp1Index); + + public void EmitLdvectmp() => EmitLdvec(Tmp5Index); + public void EmitStvectmp() => EmitStvec(Tmp5Index); + + public void EmitLdvectmp2() => EmitLdvec(Tmp6Index); + public void EmitStvectmp2() => EmitStvec(Tmp6Index); + + public void EmitLdint(int index) => Ldloc(index, IoType.Int); + public void EmitStint(int index) => Stloc(index, IoType.Int); + + public void EmitLdvec(int index) => Ldloc(index, IoType.Vector); + public void EmitStvec(int index) => Stloc(index, IoType.Vector); + + public void EmitLdflg(int index) => Ldloc(index, IoType.Flag); + public void EmitStflg(int index) + { + _optOpLastFlagSet = CurrOp; + + Stloc(index, IoType.Flag); + } + + private void Ldloc(int index, IoType ioType) + { + _ilBlock.Add(new IlOpCodeLoad(index, ioType, CurrOp.RegisterSize)); + } + + private void Ldloc(int index, IoType ioType, RegisterSize registerSize) + { + _ilBlock.Add(new IlOpCodeLoad(index, ioType, registerSize)); + } + + private void Stloc(int index, IoType ioType) + { + _ilBlock.Add(new IlOpCodeStore(index, ioType, CurrOp.RegisterSize)); + } + + public void EmitCallPropGet(Type objType, string propName) + { + if (objType == null) + { + throw new ArgumentNullException(nameof(objType)); + } + + if (propName == null) + { + throw new ArgumentNullException(nameof(propName)); + } + + EmitCall(objType.GetMethod($"get_{propName}")); + } + + public void EmitCallPropSet(Type objType, string propName) + { + if (objType == null) + { + throw new ArgumentNullException(nameof(objType)); + } + + if (propName == null) + { + throw new ArgumentNullException(nameof(propName)); + } + + EmitCall(objType.GetMethod($"set_{propName}")); + } + + public void EmitCall(Type objType, string mthdName) + { + if (objType == null) + { + throw new ArgumentNullException(nameof(objType)); + } + + if (mthdName == null) + { + throw new ArgumentNullException(nameof(mthdName)); + } + + EmitCall(objType.GetMethod(mthdName)); + } + + public void EmitPrivateCall(Type objType, string mthdName) + { + if (objType == null) + { + throw new ArgumentNullException(nameof(objType)); + } + + if (mthdName == null) + { + throw new ArgumentNullException(nameof(mthdName)); + } + + EmitCall(objType.GetMethod(mthdName, BindingFlags.Instance | BindingFlags.NonPublic)); + } + + public void EmitCall(MethodInfo mthdInfo) + { + if (mthdInfo == null) + { + throw new ArgumentNullException(nameof(mthdInfo)); + } + + _ilBlock.Add(new ILOpCodeCall(mthdInfo)); + } + + public void EmitLdc_I(long value) + { + if (CurrOp.RegisterSize == RegisterSize.Int32) + { + EmitLdc_I4((int)value); + } + else + { + EmitLdc_I8(value); + } + } + + public void EmitLdc_I4(int value) + { + _ilBlock.Add(new ILOpCodeConst(value)); + } + + public void EmitLdc_I8(long value) + { + _ilBlock.Add(new ILOpCodeConst(value)); + } + + public void EmitLdc_R4(float value) + { + _ilBlock.Add(new ILOpCodeConst(value)); + } + + public void EmitLdc_R8(double value) + { + _ilBlock.Add(new ILOpCodeConst(value)); + } + + public void EmitZnFlagCheck() + { + EmitZnCheck(OpCodes.Ceq, (int)PState.ZBit); + EmitZnCheck(OpCodes.Clt, (int)PState.NBit); + } + + private void EmitZnCheck(System.Reflection.Emit.OpCode ilCmpOp, int flag) + { + Emit(OpCodes.Dup); + Emit(OpCodes.Ldc_I4_0); + + if (CurrOp.RegisterSize != RegisterSize.Int32) + { + Emit(OpCodes.Conv_I8); + } + + Emit(ilCmpOp); + + EmitStflg(flag); + } + } +} diff --git a/Translation/ILGeneratorEx.cs b/Translation/ILGeneratorEx.cs index 40c6efa..318098c 100644 --- a/Translation/ILGeneratorEx.cs +++ b/Translation/ILGeneratorEx.cs @@ -6,123 +6,123 @@ namespace ChocolArm64 static class ILGeneratorEx { - public static void EmitLdc_I4(this ILGenerator Generator, int Value) + public static void EmitLdc_I4(this ILGenerator generator, int value) { - switch (Value) + switch (value) { - case 0: Generator.Emit(OpCodes.Ldc_I4_0); break; - case 1: Generator.Emit(OpCodes.Ldc_I4_1); break; - case 2: Generator.Emit(OpCodes.Ldc_I4_2); break; - case 3: Generator.Emit(OpCodes.Ldc_I4_3); break; - case 4: Generator.Emit(OpCodes.Ldc_I4_4); break; - case 5: Generator.Emit(OpCodes.Ldc_I4_5); break; - case 6: Generator.Emit(OpCodes.Ldc_I4_6); break; - case 7: Generator.Emit(OpCodes.Ldc_I4_7); break; - case 8: Generator.Emit(OpCodes.Ldc_I4_8); break; - case -1: Generator.Emit(OpCodes.Ldc_I4_M1); break; - default: Generator.Emit(OpCodes.Ldc_I4, Value); break; + case 0: generator.Emit(OpCodes.Ldc_I4_0); break; + case 1: generator.Emit(OpCodes.Ldc_I4_1); break; + case 2: generator.Emit(OpCodes.Ldc_I4_2); break; + case 3: generator.Emit(OpCodes.Ldc_I4_3); break; + case 4: generator.Emit(OpCodes.Ldc_I4_4); break; + case 5: generator.Emit(OpCodes.Ldc_I4_5); break; + case 6: generator.Emit(OpCodes.Ldc_I4_6); break; + case 7: generator.Emit(OpCodes.Ldc_I4_7); break; + case 8: generator.Emit(OpCodes.Ldc_I4_8); break; + case -1: generator.Emit(OpCodes.Ldc_I4_M1); break; + default: generator.Emit(OpCodes.Ldc_I4, value); break; } } - public static void EmitLdarg(this ILGenerator Generator, int Index) + public static void EmitLdarg(this ILGenerator generator, int index) { - switch (Index) + switch (index) { - case 0: Generator.Emit(OpCodes.Ldarg_0); break; - case 1: Generator.Emit(OpCodes.Ldarg_1); break; - case 2: Generator.Emit(OpCodes.Ldarg_2); break; - case 3: Generator.Emit(OpCodes.Ldarg_3); break; + case 0: generator.Emit(OpCodes.Ldarg_0); break; + case 1: generator.Emit(OpCodes.Ldarg_1); break; + case 2: generator.Emit(OpCodes.Ldarg_2); break; + case 3: generator.Emit(OpCodes.Ldarg_3); break; default: - if ((uint)Index <= byte.MaxValue) + if ((uint)index <= byte.MaxValue) { - Generator.Emit(OpCodes.Ldarg_S, (byte)Index); + generator.Emit(OpCodes.Ldarg_S, (byte)index); } - else if ((uint)Index < ushort.MaxValue) + else if ((uint)index < ushort.MaxValue) { - Generator.Emit(OpCodes.Ldarg, (short)Index); + generator.Emit(OpCodes.Ldarg, (short)index); } else { - throw new ArgumentOutOfRangeException(nameof(Index)); + throw new ArgumentOutOfRangeException(nameof(index)); } break; } } - public static void EmitStarg(this ILGenerator Generator, int Index) + public static void EmitStarg(this ILGenerator generator, int index) { - if ((uint)Index <= byte.MaxValue) + if ((uint)index <= byte.MaxValue) { - Generator.Emit(OpCodes.Starg_S, (byte)Index); + generator.Emit(OpCodes.Starg_S, (byte)index); } - else if ((uint)Index < ushort.MaxValue) + else if ((uint)index < ushort.MaxValue) { - Generator.Emit(OpCodes.Starg, (short)Index); + generator.Emit(OpCodes.Starg, (short)index); } else { - throw new ArgumentOutOfRangeException(nameof(Index)); + throw new ArgumentOutOfRangeException(nameof(index)); } } - public static void EmitLdloc(this ILGenerator Generator, int Index) + public static void EmitLdloc(this ILGenerator generator, int index) { - switch (Index) + switch (index) { - case 0: Generator.Emit(OpCodes.Ldloc_0); break; - case 1: Generator.Emit(OpCodes.Ldloc_1); break; - case 2: Generator.Emit(OpCodes.Ldloc_2); break; - case 3: Generator.Emit(OpCodes.Ldloc_3); break; + case 0: generator.Emit(OpCodes.Ldloc_0); break; + case 1: generator.Emit(OpCodes.Ldloc_1); break; + case 2: generator.Emit(OpCodes.Ldloc_2); break; + case 3: generator.Emit(OpCodes.Ldloc_3); break; default: - if ((uint)Index <= byte.MaxValue) + if ((uint)index <= byte.MaxValue) { - Generator.Emit(OpCodes.Ldloc_S, (byte)Index); + generator.Emit(OpCodes.Ldloc_S, (byte)index); } - else if ((uint)Index < ushort.MaxValue) + else if ((uint)index < ushort.MaxValue) { - Generator.Emit(OpCodes.Ldloc, (short)Index); + generator.Emit(OpCodes.Ldloc, (short)index); } else { - throw new ArgumentOutOfRangeException(nameof(Index)); + throw new ArgumentOutOfRangeException(nameof(index)); } break; } } - public static void EmitStloc(this ILGenerator Generator, int Index) + public static void EmitStloc(this ILGenerator generator, int index) { - switch (Index) + switch (index) { - case 0: Generator.Emit(OpCodes.Stloc_0); break; - case 1: Generator.Emit(OpCodes.Stloc_1); break; - case 2: Generator.Emit(OpCodes.Stloc_2); break; - case 3: Generator.Emit(OpCodes.Stloc_3); break; + case 0: generator.Emit(OpCodes.Stloc_0); break; + case 1: generator.Emit(OpCodes.Stloc_1); break; + case 2: generator.Emit(OpCodes.Stloc_2); break; + case 3: generator.Emit(OpCodes.Stloc_3); break; default: - if ((uint)Index <= byte.MaxValue) + if ((uint)index <= byte.MaxValue) { - Generator.Emit(OpCodes.Stloc_S, (byte)Index); + generator.Emit(OpCodes.Stloc_S, (byte)index); } - else if ((uint)Index < ushort.MaxValue) + else if ((uint)index < ushort.MaxValue) { - Generator.Emit(OpCodes.Stloc, (short)Index); + generator.Emit(OpCodes.Stloc, (short)index); } else { - throw new ArgumentOutOfRangeException(nameof(Index)); + throw new ArgumentOutOfRangeException(nameof(index)); } break; } } - public static void EmitLdargSeq(this ILGenerator Generator, int Count) + public static void EmitLdargSeq(this ILGenerator generator, int count) { - for (int Index = 0; Index < Count; Index++) + for (int index = 0; index < count; index++) { - Generator.EmitLdarg(Index); + generator.EmitLdarg(index); } } } diff --git a/Translation/ILLabel.cs b/Translation/ILLabel.cs new file mode 100644 index 0000000..4f96edc --- /dev/null +++ b/Translation/ILLabel.cs @@ -0,0 +1,28 @@ +using System.Reflection.Emit; + +namespace ChocolArm64.Translation +{ + class ILLabel : IILEmit + { + private bool _hasLabel; + + private Label _lbl; + + public void Emit(ILEmitter context) + { + context.Generator.MarkLabel(GetLabel(context)); + } + + public Label GetLabel(ILEmitter context) + { + if (!_hasLabel) + { + _lbl = context.Generator.DefineLabel(); + + _hasLabel = true; + } + + return _lbl; + } + } +} \ No newline at end of file diff --git a/Translation/ILOpCode.cs b/Translation/ILOpCode.cs new file mode 100644 index 0000000..eb91639 --- /dev/null +++ b/Translation/ILOpCode.cs @@ -0,0 +1,19 @@ +using System.Reflection.Emit; + +namespace ChocolArm64.Translation +{ + struct ILOpCode : IILEmit + { + private OpCode _ilOp; + + public ILOpCode(OpCode ilOp) + { + _ilOp = ilOp; + } + + public void Emit(ILEmitter context) + { + context.Generator.Emit(_ilOp); + } + } +} \ No newline at end of file diff --git a/Translation/ILOpCodeBranch.cs b/Translation/ILOpCodeBranch.cs new file mode 100644 index 0000000..b0ba233 --- /dev/null +++ b/Translation/ILOpCodeBranch.cs @@ -0,0 +1,21 @@ +using System.Reflection.Emit; + +namespace ChocolArm64.Translation +{ + struct ILOpCodeBranch : IILEmit + { + private OpCode _ilOp; + private ILLabel _label; + + public ILOpCodeBranch(OpCode ilOp, ILLabel label) + { + _ilOp = ilOp; + _label = label; + } + + public void Emit(ILEmitter context) + { + context.Generator.Emit(_ilOp, _label.GetLabel(context)); + } + } +} \ No newline at end of file diff --git a/Translation/ILOpCodeCall.cs b/Translation/ILOpCodeCall.cs new file mode 100644 index 0000000..0b591d4 --- /dev/null +++ b/Translation/ILOpCodeCall.cs @@ -0,0 +1,20 @@ +using System.Reflection; +using System.Reflection.Emit; + +namespace ChocolArm64.Translation +{ + struct ILOpCodeCall : IILEmit + { + private MethodInfo _mthdInfo; + + public ILOpCodeCall(MethodInfo mthdInfo) + { + _mthdInfo = mthdInfo; + } + + public void Emit(ILEmitter context) + { + context.Generator.Emit(OpCodes.Call, _mthdInfo); + } + } +} \ No newline at end of file diff --git a/Translation/ILOpCodeConst.cs b/Translation/ILOpCodeConst.cs new file mode 100644 index 0000000..5497eba --- /dev/null +++ b/Translation/ILOpCodeConst.cs @@ -0,0 +1,65 @@ +using System.Reflection.Emit; +using System.Runtime.InteropServices; + +namespace ChocolArm64.Translation +{ + class ILOpCodeConst : IILEmit + { + [StructLayout(LayoutKind.Explicit, Size = 8)] + private struct ImmVal + { + [FieldOffset(0)] public int I4; + [FieldOffset(0)] public long I8; + [FieldOffset(0)] public float R4; + [FieldOffset(0)] public double R8; + } + + private ImmVal _value; + + private enum ConstType + { + Int32, + Int64, + Single, + Double + } + + private ConstType _type; + + private ILOpCodeConst(ConstType type) + { + _type = type; + } + + public ILOpCodeConst(int value) : this(ConstType.Int32) + { + _value = new ImmVal { I4 = value }; + } + + public ILOpCodeConst(long value) : this(ConstType.Int64) + { + _value = new ImmVal { I8 = value }; + } + + public ILOpCodeConst(float value) : this(ConstType.Single) + { + _value = new ImmVal { R4 = value }; + } + + public ILOpCodeConst(double value) : this(ConstType.Double) + { + _value = new ImmVal { R8 = value }; + } + + public void Emit(ILEmitter context) + { + switch (_type) + { + case ConstType.Int32: context.Generator.EmitLdc_I4(_value.I4); break; + case ConstType.Int64: context.Generator.Emit(OpCodes.Ldc_I8, _value.I8); break; + case ConstType.Single: context.Generator.Emit(OpCodes.Ldc_R4, _value.R4); break; + case ConstType.Double: context.Generator.Emit(OpCodes.Ldc_R8, _value.R8); break; + } + } + } +} \ No newline at end of file diff --git a/Translation/ILOpCodeLoad.cs b/Translation/ILOpCodeLoad.cs new file mode 100644 index 0000000..9dae10c --- /dev/null +++ b/Translation/ILOpCodeLoad.cs @@ -0,0 +1,75 @@ +using ChocolArm64.State; +using System.Reflection.Emit; + +namespace ChocolArm64.Translation +{ + struct IlOpCodeLoad : IILEmit + { + public int Index { get; private set; } + + public IoType IoType { get; private set; } + + public RegisterSize RegisterSize { get; private set; } + + public IlOpCodeLoad(int index, IoType ioType, RegisterSize registerSize = 0) + { + Index = index; + IoType = ioType; + RegisterSize = registerSize; + } + + public void Emit(ILEmitter context) + { + switch (IoType) + { + case IoType.Arg: context.Generator.EmitLdarg(Index); break; + + case IoType.Fields: + { + long intInputs = context.LocalAlloc.GetIntInputs(context.GetIlBlock(Index)); + long vecInputs = context.LocalAlloc.GetVecInputs(context.GetIlBlock(Index)); + + LoadLocals(context, intInputs, RegisterType.Int); + LoadLocals(context, vecInputs, RegisterType.Vector); + + break; + } + + case IoType.Flag: EmitLdloc(context, Index, RegisterType.Flag); break; + case IoType.Int: EmitLdloc(context, Index, RegisterType.Int); break; + case IoType.Vector: EmitLdloc(context, Index, RegisterType.Vector); break; + } + } + + private void LoadLocals(ILEmitter context, long inputs, RegisterType baseType) + { + for (int bit = 0; bit < 64; bit++) + { + long mask = 1L << bit; + + if ((inputs & mask) != 0) + { + Register reg = ILEmitter.GetRegFromBit(bit, baseType); + + context.Generator.EmitLdarg(TranslatedSub.StateArgIdx); + context.Generator.Emit(OpCodes.Ldfld, reg.GetField()); + + context.Generator.EmitStloc(context.GetLocalIndex(reg)); + } + } + } + + private void EmitLdloc(ILEmitter context, int index, RegisterType registerType) + { + Register reg = new Register(index, registerType); + + context.Generator.EmitLdloc(context.GetLocalIndex(reg)); + + if (registerType == RegisterType.Int && + RegisterSize == RegisterSize.Int32) + { + context.Generator.Emit(OpCodes.Conv_U4); + } + } + } +} \ No newline at end of file diff --git a/Translation/ILOpCodeLog.cs b/Translation/ILOpCodeLog.cs new file mode 100644 index 0000000..2c77021 --- /dev/null +++ b/Translation/ILOpCodeLog.cs @@ -0,0 +1,17 @@ +namespace ChocolArm64.Translation +{ + struct ILOpCodeLog : IILEmit + { + private string _text; + + public ILOpCodeLog(string text) + { + _text = text; + } + + public void Emit(ILEmitter context) + { + context.Generator.EmitWriteLine(_text); + } + } +} \ No newline at end of file diff --git a/Translation/ILOpCodeStore.cs b/Translation/ILOpCodeStore.cs new file mode 100644 index 0000000..41326fe --- /dev/null +++ b/Translation/ILOpCodeStore.cs @@ -0,0 +1,75 @@ +using ChocolArm64.State; +using System.Reflection.Emit; + +namespace ChocolArm64.Translation +{ + struct IlOpCodeStore : IILEmit + { + public int Index { get; private set; } + + public IoType IoType { get; private set; } + + public RegisterSize RegisterSize { get; private set; } + + public IlOpCodeStore(int index, IoType ioType, RegisterSize registerSize = 0) + { + Index = index; + IoType = ioType; + RegisterSize = registerSize; + } + + public void Emit(ILEmitter context) + { + switch (IoType) + { + case IoType.Arg: context.Generator.EmitStarg(Index); break; + + case IoType.Fields: + { + long intOutputs = context.LocalAlloc.GetIntOutputs(context.GetIlBlock(Index)); + long vecOutputs = context.LocalAlloc.GetVecOutputs(context.GetIlBlock(Index)); + + StoreLocals(context, intOutputs, RegisterType.Int); + StoreLocals(context, vecOutputs, RegisterType.Vector); + + break; + } + + case IoType.Flag: EmitStloc(context, Index, RegisterType.Flag); break; + case IoType.Int: EmitStloc(context, Index, RegisterType.Int); break; + case IoType.Vector: EmitStloc(context, Index, RegisterType.Vector); break; + } + } + + private void StoreLocals(ILEmitter context, long outputs, RegisterType baseType) + { + for (int bit = 0; bit < 64; bit++) + { + long mask = 1L << bit; + + if ((outputs & mask) != 0) + { + Register reg = ILEmitter.GetRegFromBit(bit, baseType); + + context.Generator.EmitLdarg(TranslatedSub.StateArgIdx); + context.Generator.EmitLdloc(context.GetLocalIndex(reg)); + + context.Generator.Emit(OpCodes.Stfld, reg.GetField()); + } + } + } + + private void EmitStloc(ILEmitter context, int index, RegisterType registerType) + { + Register reg = new Register(index, registerType); + + if (registerType == RegisterType.Int && + RegisterSize == RegisterSize.Int32) + { + context.Generator.Emit(OpCodes.Conv_U8); + } + + context.Generator.EmitStloc(context.GetLocalIndex(reg)); + } + } +} \ No newline at end of file diff --git a/Translation/AIoType.cs b/Translation/IoType.cs similarity index 89% rename from Translation/AIoType.cs rename to Translation/IoType.cs index 94f8908..290b159 100644 --- a/Translation/AIoType.cs +++ b/Translation/IoType.cs @@ -3,7 +3,7 @@ using System; namespace ChocolArm64.Translation { [Flags] - enum AIoType + enum IoType { Arg, Fields, diff --git a/Translation/LocalAlloc.cs b/Translation/LocalAlloc.cs new file mode 100644 index 0000000..8237bd5 --- /dev/null +++ b/Translation/LocalAlloc.cs @@ -0,0 +1,229 @@ +using System.Collections.Generic; + +namespace ChocolArm64.Translation +{ + class LocalAlloc + { + private class PathIo + { + private Dictionary _allInputs; + private Dictionary _cmnOutputs; + + private long _allOutputs; + + public PathIo() + { + _allInputs = new Dictionary(); + _cmnOutputs = new Dictionary(); + } + + public PathIo(ILBlock root, long inputs, long outputs) : this() + { + Set(root, inputs, outputs); + } + + public void Set(ILBlock root, long inputs, long outputs) + { + if (!_allInputs.TryAdd(root, inputs)) + { + _allInputs[root] |= inputs; + } + + if (!_cmnOutputs.TryAdd(root, outputs)) + { + _cmnOutputs[root] &= outputs; + } + + _allOutputs |= outputs; + } + + public long GetInputs(ILBlock root) + { + if (_allInputs.TryGetValue(root, out long inputs)) + { + return inputs | (_allOutputs & ~_cmnOutputs[root]); + } + + return 0; + } + + public long GetOutputs() + { + return _allOutputs; + } + } + + private Dictionary _intPaths; + private Dictionary _vecPaths; + + private struct BlockIo + { + public ILBlock Block; + public ILBlock Entry; + + public long IntInputs; + public long VecInputs; + public long IntOutputs; + public long VecOutputs; + } + + private const int MaxOptGraphLength = 40; + + public LocalAlloc(ILBlock[] graph, ILBlock root) + { + _intPaths = new Dictionary(); + _vecPaths = new Dictionary(); + + if (graph.Length > 1 && + graph.Length < MaxOptGraphLength) + { + InitializeOptimal(graph, root); + } + else + { + InitializeFast(graph); + } + } + + private void InitializeOptimal(ILBlock[] graph, ILBlock root) + { + //This will go through all possible paths on the graph, + //and store all inputs/outputs for each block. A register + //that was previously written to already is not considered an input. + //When a block can be reached by more than one path, then the + //output from all paths needs to be set for this block, and + //only outputs present in all of the parent blocks can be considered + //when doing input elimination. Each block chain have a root, that's where + //the code starts executing. They are present on the subroutine start point, + //and on call return points too (address written to X30 by BL). + HashSet visited = new HashSet(); + + Queue unvisited = new Queue(); + + void Enqueue(BlockIo block) + { + if (!visited.Contains(block)) + { + unvisited.Enqueue(block); + + visited.Add(block); + } + } + + Enqueue(new BlockIo() + { + Block = root, + Entry = root + }); + + while (unvisited.Count > 0) + { + BlockIo current = unvisited.Dequeue(); + + current.IntInputs |= current.Block.IntInputs & ~current.IntOutputs; + current.VecInputs |= current.Block.VecInputs & ~current.VecOutputs; + current.IntOutputs |= current.Block.IntOutputs; + current.VecOutputs |= current.Block.VecOutputs; + + //Check if this is a exit block + //(a block that returns or calls another sub). + if ((current.Block.Next == null && + current.Block.Branch == null) || current.Block.HasStateStore) + { + if (!_intPaths.TryGetValue(current.Block, out PathIo intPath)) + { + _intPaths.Add(current.Block, intPath = new PathIo()); + } + + if (!_vecPaths.TryGetValue(current.Block, out PathIo vecPath)) + { + _vecPaths.Add(current.Block, vecPath = new PathIo()); + } + + intPath.Set(current.Entry, current.IntInputs, current.IntOutputs); + vecPath.Set(current.Entry, current.VecInputs, current.VecOutputs); + } + + void EnqueueFromCurrent(ILBlock block, bool retTarget) + { + BlockIo blkIO = new BlockIo() { Block = block }; + + if (retTarget) + { + blkIO.Entry = block; + } + else + { + blkIO.Entry = current.Entry; + blkIO.IntInputs = current.IntInputs; + blkIO.VecInputs = current.VecInputs; + blkIO.IntOutputs = current.IntOutputs; + blkIO.VecOutputs = current.VecOutputs; + } + + Enqueue(blkIO); + } + + if (current.Block.Next != null) + { + EnqueueFromCurrent(current.Block.Next, current.Block.HasStateStore); + } + + if (current.Block.Branch != null) + { + EnqueueFromCurrent(current.Block.Branch, false); + } + } + } + + private void InitializeFast(ILBlock[] graph) + { + //This is WAY faster than InitializeOptimal, but results in + //uneeded loads and stores, so the resulting code will be slower. + long intInputs = 0, intOutputs = 0; + long vecInputs = 0, vecOutputs = 0; + + foreach (ILBlock block in graph) + { + intInputs |= block.IntInputs; + intOutputs |= block.IntOutputs; + vecInputs |= block.VecInputs; + vecOutputs |= block.VecOutputs; + } + + //It's possible that not all code paths writes to those output registers, + //in those cases if we attempt to write an output registers that was + //not written, we will be just writing zero and messing up the old register value. + //So we just need to ensure that all outputs are loaded. + if (graph.Length > 1) + { + intInputs |= intOutputs; + vecInputs |= vecOutputs; + } + + foreach (ILBlock block in graph) + { + _intPaths.Add(block, new PathIo(block, intInputs, intOutputs)); + _vecPaths.Add(block, new PathIo(block, vecInputs, vecOutputs)); + } + } + + public long GetIntInputs(ILBlock root) => GetInputsImpl(root, _intPaths.Values); + public long GetVecInputs(ILBlock root) => GetInputsImpl(root, _vecPaths.Values); + + private long GetInputsImpl(ILBlock root, IEnumerable values) + { + long inputs = 0; + + foreach (PathIo path in values) + { + inputs |= path.GetInputs(root); + } + + return inputs; + } + + public long GetIntOutputs(ILBlock block) => _intPaths[block].GetOutputs(); + public long GetVecOutputs(ILBlock block) => _vecPaths[block].GetOutputs(); + } +} \ No newline at end of file diff --git a/Translator.cs b/Translator.cs new file mode 100644 index 0000000..3bf06dc --- /dev/null +++ b/Translator.cs @@ -0,0 +1,165 @@ +using ChocolArm64.Decoders; +using ChocolArm64.Events; +using ChocolArm64.Memory; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System; +using System.Reflection.Emit; + +namespace ChocolArm64 +{ + public class Translator + { + private TranslatorCache _cache; + + public event EventHandler CpuTrace; + + public bool EnableCpuTrace { get; set; } + + public Translator() + { + _cache = new TranslatorCache(); + } + + internal void ExecuteSubroutine(CpuThread thread, long position) + { + //TODO: Both the execute A32/A64 methods should be merged on the future, + //when both ISAs are implemented with the interpreter and JIT. + //As of now, A32 only has a interpreter and A64 a JIT. + CpuThreadState state = thread.ThreadState; + MemoryManager memory = thread.Memory; + + if (state.ExecutionMode == ExecutionMode.AArch32) + { + ExecuteSubroutineA32(state, memory); + } + else + { + ExecuteSubroutineA64(state, memory, position); + } + } + + private void ExecuteSubroutineA32(CpuThreadState state, MemoryManager memory) + { + do + { + OpCode64 opCode = Decoder.DecodeOpCode(state, memory, state.R15); + + opCode.Interpreter(state, memory, opCode); + } + while (state.R15 != 0 && state.Running); + } + + private void ExecuteSubroutineA64(CpuThreadState state, MemoryManager memory, long position) + { + do + { + if (EnableCpuTrace) + { + CpuTrace?.Invoke(this, new CpuTraceEventArgs(position)); + } + + if (!_cache.TryGetSubroutine(position, out TranslatedSub sub)) + { + sub = TranslateTier0(state, memory, position); + } + + if (sub.ShouldReJit()) + { + TranslateTier1(state, memory, position); + } + + position = sub.Execute(state, memory); + } + while (position != 0 && state.Running); + } + + internal bool HasCachedSub(long position) + { + return _cache.HasSubroutine(position); + } + + private TranslatedSub TranslateTier0(CpuThreadState state, MemoryManager memory, long position) + { + Block block = Decoder.DecodeBasicBlock(state, memory, position); + + Block[] graph = new Block[] { block }; + + string subName = GetSubroutineName(position); + + ILEmitterCtx context = new ILEmitterCtx(_cache, graph, block, subName); + + do + { + context.EmitOpCode(); + } + while (context.AdvanceOpCode()); + + TranslatedSub subroutine = context.GetSubroutine(); + + subroutine.SetType(TranslatedSubType.SubTier0); + + _cache.AddOrUpdate(position, subroutine, block.OpCodes.Count); + + OpCode64 lastOp = block.GetLastOp(); + + return subroutine; + } + + private void TranslateTier1(CpuThreadState state, MemoryManager memory, long position) + { + (Block[] graph, Block root) = Decoder.DecodeSubroutine(_cache, state, memory, position); + + string subName = GetSubroutineName(position); + + ILEmitterCtx context = new ILEmitterCtx(_cache, graph, root, subName); + + if (context.CurrBlock.Position != position) + { + context.Emit(OpCodes.Br, context.GetLabel(position)); + } + + do + { + context.EmitOpCode(); + } + while (context.AdvanceOpCode()); + + //Mark all methods that calls this method for ReJiting, + //since we can now call it directly which is faster. + if (_cache.TryGetSubroutine(position, out TranslatedSub oldSub)) + { + foreach (long callerPos in oldSub.GetCallerPositions()) + { + if (_cache.TryGetSubroutine(position, out TranslatedSub callerSub)) + { + callerSub.MarkForReJit(); + } + } + } + + TranslatedSub subroutine = context.GetSubroutine(); + + subroutine.SetType(TranslatedSubType.SubTier1); + + _cache.AddOrUpdate(position, subroutine, GetGraphInstCount(graph)); + } + + private string GetSubroutineName(long position) + { + return $"Sub{position:x16}"; + } + + private int GetGraphInstCount(Block[] graph) + { + int size = 0; + + foreach (Block block in graph) + { + size += block.OpCodes.Count; + } + + return size; + } + } +} \ No newline at end of file diff --git a/TranslatorCache.cs b/TranslatorCache.cs new file mode 100644 index 0000000..7d65035 --- /dev/null +++ b/TranslatorCache.cs @@ -0,0 +1,165 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Threading; + +namespace ChocolArm64 +{ + class TranslatorCache + { + //Maximum size of the cache, in bytes, measured in ARM code size. + private const int MaxTotalSize = 4 * 1024 * 256; + + //Minimum time required in milliseconds for a method to be eligible for deletion. + private const int MinTimeDelta = 2 * 60000; + + //Minimum number of calls required to update the timestamp. + private const int MinCallCountForUpdate = 250; + + private class CacheBucket + { + public TranslatedSub Subroutine { get; private set; } + + public LinkedListNode Node { get; private set; } + + public int CallCount { get; set; } + + public int Size { get; private set; } + + public long Timestamp { get; private set; } + + public CacheBucket(TranslatedSub subroutine, LinkedListNode node, int size) + { + Subroutine = subroutine; + Size = size; + + UpdateNode(node); + } + + public void UpdateNode(LinkedListNode node) + { + Node = node; + + Timestamp = GetTimestamp(); + } + } + + private ConcurrentDictionary _cache; + + private LinkedList _sortedCache; + + private int _totalSize; + + public TranslatorCache() + { + _cache = new ConcurrentDictionary(); + + _sortedCache = new LinkedList(); + } + + public void AddOrUpdate(long position, TranslatedSub subroutine, int size) + { + ClearCacheIfNeeded(); + + _totalSize += size; + + lock (_sortedCache) + { + LinkedListNode node = _sortedCache.AddLast(position); + + CacheBucket newBucket = new CacheBucket(subroutine, node, size); + + _cache.AddOrUpdate(position, newBucket, (key, bucket) => + { + _totalSize -= bucket.Size; + + _sortedCache.Remove(bucket.Node); + + return newBucket; + }); + } + } + + public bool HasSubroutine(long position) + { + return _cache.ContainsKey(position); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryGetSubroutine(long position, out TranslatedSub subroutine) + { + if (_cache.TryGetValue(position, out CacheBucket bucket)) + { + if (bucket.CallCount++ > MinCallCountForUpdate) + { + if (Monitor.TryEnter(_sortedCache)) + { + try + { + bucket.CallCount = 0; + + _sortedCache.Remove(bucket.Node); + + bucket.UpdateNode(_sortedCache.AddLast(position)); + } + finally + { + Monitor.Exit(_sortedCache); + } + } + } + + subroutine = bucket.Subroutine; + + return true; + } + + subroutine = default(TranslatedSub); + + return false; + } + + private void ClearCacheIfNeeded() + { + long timestamp = GetTimestamp(); + + while (_totalSize > MaxTotalSize) + { + lock (_sortedCache) + { + LinkedListNode node = _sortedCache.First; + + if (node == null) + { + break; + } + + CacheBucket bucket = _cache[node.Value]; + + long timeDelta = timestamp - bucket.Timestamp; + + if (timeDelta <= MinTimeDelta) + { + break; + } + + if (_cache.TryRemove(node.Value, out bucket)) + { + _totalSize -= bucket.Size; + + _sortedCache.Remove(bucket.Node); + } + } + } + } + + private static long GetTimestamp() + { + long timestamp = Stopwatch.GetTimestamp(); + + return timestamp / (Stopwatch.Frequency / 1000); + } + } +} \ No newline at end of file