diff options
| author | Tilka <tilkax@gmail.com> | 2023-12-16 21:15:31 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-12-16 21:15:31 +0000 |
| commit | 773ffd04b8ce3695e2676db66ac2a2bf2ba73494 (patch) | |
| tree | 64d79a5c4e238cee05128e0c61b7e2e4e9ca71bb /Source/Core/Common | |
| parent | 190c4e8cda7a78dd7b1ae184425b49d363734c7e (diff) | |
| parent | c8c9928eb11cea81808127bae9894fc6cfbac770 (diff) | |
Merge pull request #11497 from vyuuui/debugger_assembler_ui
Built-in assembler for debugger interface
Diffstat (limited to 'Source/Core/Common')
| -rw-r--r-- | Source/Core/Common/Assembler/AssemblerShared.cpp | 26 | ||||
| -rw-r--r-- | Source/Core/Common/Assembler/AssemblerShared.h | 545 | ||||
| -rw-r--r-- | Source/Core/Common/Assembler/AssemblerTables.cpp | 1482 | ||||
| -rw-r--r-- | Source/Core/Common/Assembler/AssemblerTables.h | 152 | ||||
| -rw-r--r-- | Source/Core/Common/Assembler/CaseInsensitiveDict.h | 126 | ||||
| -rw-r--r-- | Source/Core/Common/Assembler/GekkoAssembler.cpp | 189 | ||||
| -rw-r--r-- | Source/Core/Common/Assembler/GekkoAssembler.h | 29 | ||||
| -rw-r--r-- | Source/Core/Common/Assembler/GekkoIRGen.cpp | 832 | ||||
| -rw-r--r-- | Source/Core/Common/Assembler/GekkoIRGen.h | 50 | ||||
| -rw-r--r-- | Source/Core/Common/Assembler/GekkoLexer.cpp | 794 | ||||
| -rw-r--r-- | Source/Core/Common/Assembler/GekkoLexer.h | 188 | ||||
| -rw-r--r-- | Source/Core/Common/Assembler/GekkoParser.cpp | 885 | ||||
| -rw-r--r-- | Source/Core/Common/Assembler/GekkoParser.h | 124 | ||||
| -rw-r--r-- | Source/Core/Common/CMakeLists.txt | 12 | ||||
| -rw-r--r-- | Source/Core/Common/CommonPaths.h | 1 | ||||
| -rw-r--r-- | Source/Core/Common/FileUtil.cpp | 2 | ||||
| -rw-r--r-- | Source/Core/Common/FileUtil.h | 1 |
17 files changed, 5438 insertions, 0 deletions
diff --git a/Source/Core/Common/Assembler/AssemblerShared.cpp b/Source/Core/Common/Assembler/AssemblerShared.cpp new file mode 100644 index 0000000000..77d7e737f9 --- /dev/null +++ b/Source/Core/Common/Assembler/AssemblerShared.cpp @@ -0,0 +1,26 @@ +// Copyright 2023 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "Common/Assembler/AssemblerShared.h" + +#include <fmt/format.h> + +namespace Common::GekkoAssembler +{ +std::string AssemblerError::FormatError() const +{ + const char* space_char = col == 0 ? "" : " "; + + std::string_view line_str = error_line; + if (line_str.back() == '\n') + { + line_str = line_str.substr(0, line_str.length() - 1); + } + + return fmt::format("Error on line {0} col {1}:\n" + " {2}\n" + " {3:{4}}{5:^^{6}}\n" + "{7}", + line + 1, col + 1, line_str, space_char, col, '^', len, message); +} +} // namespace Common::GekkoAssembler diff --git a/Source/Core/Common/Assembler/AssemblerShared.h b/Source/Core/Common/Assembler/AssemblerShared.h new file mode 100644 index 0000000000..23bf5be0ce --- /dev/null +++ b/Source/Core/Common/Assembler/AssemblerShared.h @@ -0,0 +1,545 @@ +// Copyright 2023 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <cstddef> +#include <cstdint> +#include <string> +#include <string_view> +#include <variant> + +namespace Common::GekkoAssembler +{ +struct Interval +{ + size_t begin; + size_t len; + constexpr size_t End() const { return begin + len; } +}; + +struct AssemblerError +{ + std::string message; + std::string_view error_line; + size_t line; + size_t col; + size_t len; + + std::string FormatError() const; +}; + +template <typename Tag, typename T> +using Tagged = std::pair<Tag, T>; +template <typename Tag, typename T> +constexpr const Tag& TagOf(const Tagged<Tag, T>& val) +{ + return std::get<0>(val); +} +template <typename Tag, typename T> +constexpr Tag& TagOf(Tagged<Tag, T>& val) +{ + return std::get<0>(val); +} +template <typename Tag, typename T> +constexpr const T& ValueOf(const Tagged<Tag, T>& val) +{ + return std::get<1>(val); +} +template <typename Tag, typename T> +constexpr T& ValueOf(Tagged<Tag, T>& val) +{ + return std::get<1>(val); +} + +template <typename T> +using FailureOr = std::variant<AssemblerError, T>; +template <typename T> +constexpr bool IsFailure(const FailureOr<T>& var) +{ + return std::holds_alternative<AssemblerError>(var); +} +template <typename T> +constexpr AssemblerError& GetFailure(FailureOr<T>& var) +{ + return std::get<AssemblerError>(var); +} +template <typename T> +constexpr const AssemblerError& GetFailure(const FailureOr<T>& var) +{ + return std::get<AssemblerError>(var); +} +template <typename T> +constexpr const T& GetT(const FailureOr<T>& var) +{ + return std::get<T>(var); +} +template <typename T> +constexpr T& GetT(FailureOr<T>& var) +{ + return std::get<T>(var); +} + +enum class GekkoDirective +{ + Byte, + _2byte, + _4byte, + _8byte, + Float, + Double, + Locate, + PadAlign, + Align, + Zeros, + Skip, + DefVar, + Ascii, + Asciz +}; + +enum class GekkoMnemonic : size_t +{ + Add, + Addc, + Adde, + Addi, + Addic, + AddicDot, + Addis, + Addme, + Addze, + Divw, + Divwu, + Mulhw, + Mulhwu, + Mulli, + Mullw, + Neg, + Subf, + Subfc, + Subfe, + Subfic, + Subfme, + Subfze, + Cmp, + Cmpi, + Cmpl, + Cmpli, + And, + Andc, + AndiDot, + AndisDot, + Cntlzw, + Eqv, + Extsb, + Extsh, + Nand, + Nor, + Or, + Orc, + Ori, + Oris, + Xor, + Xori, + Xoris, + Rlwimi, + Rlwinm, + Rlwnm, + Slw, + Sraw, + Srawi, + Srw, + Fadd, + Fadds, + Fdiv, + Fdivs, + Fmul, + Fmuls, + Fres, + Frsqrte, + Fsub, + Fsubs, + Fsel, + Fmadd, + Fmadds, + Fmsub, + Fmsubs, + Fnmadd, + Fnmadds, + Fnmsub, + Fnmsubs, + Fctiw, + Fctiwz, + Frsp, + Fcmpo, + Fcmpu, + Mcrfs, + Mffs, + Mtfsb0, + Mtfsb1, + Mtfsf, + Mtfsfi, + Lbz, + Lbzu, + Lbzux, + Lbzx, + Lha, + Lhau, + Lhaux, + Lhax, + Lhz, + Lhzu, + Lhzux, + Lhzx, + Lwz, + Lwzu, + Lwzux, + Lwzx, + Stb, + Stbu, + Stbux, + Stbx, + Sth, + Sthu, + Sthux, + Sthx, + Stw, + Stwu, + Stwux, + Stwx, + Lhbrx, + Lwbrx, + Sthbrx, + Stwbrx, + Lmw, + Stmw, + Lswi, + Lswx, + Stswi, + Stswx, + Eieio, + Isync, + Lwarx, + StwcxDot, + Sync, + Lfd, + Lfdu, + Lfdux, + Lfdx, + Lfs, + Lfsu, + Lfsux, + Lfsx, + Stfd, + Stfdu, + Stfdux, + Stfdx, + Stfiwx, + Stfs, + Stfsu, + Stfsux, + Stfsx, + Fabs, + Fmr, + Fnabs, + Fneg, + B, + Bc, + Bcctr, + Bclr, + Crand, + Crandc, + Creqv, + Crnand, + Crnor, + Cror, + Crorc, + Crxor, + Mcrf, + Rfi, + Sc, + Tw, + Twi, + Mcrxr, + Mfcr, + Mfmsr, + Mfspr_nobitswap, + Mftb_nobitswap, + Mtcrf, + Mtmsr, + Mtspr_nobitswap, + Dcbf, + Dcbi, + Dcbst, + Dcbt, + Dcbtst, + Dcbz, + Icbi, + Mfsr, + Mfsrin, + Mtsr, + Mtsrin, + Tlbie, + Tlbsync, + Eciwx, + Ecowx, + Psq_lx, + Psq_stx, + Psq_lux, + Psq_stux, + Psq_l, + Psq_lu, + Psq_st, + Psq_stu, + Ps_div, + Ps_sub, + Ps_add, + Ps_sel, + Ps_res, + Ps_mul, + Ps_rsqrte, + Ps_msub, + Ps_madd, + Ps_nmsub, + Ps_nmadd, + Ps_neg, + Ps_mr, + Ps_nabs, + Ps_abs, + Ps_sum0, + Ps_sum1, + Ps_muls0, + Ps_muls1, + Ps_madds0, + Ps_madds1, + Ps_cmpu0, + Ps_cmpo0, + Ps_cmpu1, + Ps_cmpo1, + Ps_merge00, + Ps_merge01, + Ps_merge10, + Ps_merge11, + Dcbz_l, + LastMnemonic = Dcbz_l, + InvalidMnemonic, +}; + +enum class ExtendedGekkoMnemonic : size_t +{ + Subi, + Subis, + Subic, + SubicDot, + Sub, + Subc, + Cmpwi, + Cmpw, + Cmplwi, + Cmplw, + Extlwi, + Extrwi, + Inslwi, + Insrwi, + Rotlwi, + Rotrwi, + Rotlw, + Slwi, + Srwi, + Clrlwi, + Clrrwi, + Clrlslwi, + Bt, + Bf, + Bdnz, + Bdnzt, + Bdnzf, + Bdz, + Bdzt, + Bdzf, + BtPredict, + BfPredict, + BdnzPredict, + BdnztPredict, + BdnzfPredict, + BdzPredict, + BdztPredict, + BdzfPredict, + Blr, + Btlr, + Bflr, + Bdnzlr, + Bdnztlr, + Bdnzflr, + Bdzlr, + Bdztlr, + Bdzflr, + BtlrPredict, + BflrPredict, + BdnzlrPredict, + BdnztlrPredict, + BdnzflrPredict, + BdzlrPredict, + BdztlrPredict, + BdzflrPredict, + Bctr, + Btctr, + Bfctr, + BtctrPredict, + BfctrPredict, + Blt, + Ble, + Beq, + Bge, + Bgt, + Bnl, + Bne, + Bng, + Bso, + Bns, + Bun, + Bnu, + BltPredict, + BlePredict, + BeqPredict, + BgePredict, + BgtPredict, + BnlPredict, + BnePredict, + BngPredict, + BsoPredict, + BnsPredict, + BunPredict, + BnuPredict, + Bltlr, + Blelr, + Beqlr, + Bgelr, + Bgtlr, + Bnllr, + Bnelr, + Bnglr, + Bsolr, + Bnslr, + Bunlr, + Bnulr, + BltlrPredict, + BlelrPredict, + BeqlrPredict, + BgelrPredict, + BgtlrPredict, + BnllrPredict, + BnelrPredict, + BnglrPredict, + BsolrPredict, + BnslrPredict, + BunlrPredict, + BnulrPredict, + Bltctr, + Blectr, + Beqctr, + Bgectr, + Bgtctr, + Bnlctr, + Bnectr, + Bngctr, + Bsoctr, + Bnsctr, + Bunctr, + Bnuctr, + BltctrPredict, + BlectrPredict, + BeqctrPredict, + BgectrPredict, + BgtctrPredict, + BnlctrPredict, + BnectrPredict, + BngctrPredict, + BsoctrPredict, + BnsctrPredict, + BunctrPredict, + BnuctrPredict, + Crset, + Crclr, + Crmove, + Crnot, + Twlt, + Twlti, + Twle, + Twlei, + Tweq, + Tweqi, + Twge, + Twgei, + Twgt, + Twgti, + Twnl, + Twnli, + Twne, + Twnei, + Twng, + Twngi, + Twllt, + Twllti, + Twlle, + Twllei, + Twlge, + Twlgei, + Twlgt, + Twlgti, + Twlnl, + Twlnli, + Twlng, + Twlngi, + Trap, + Mtxer, + Mfxer, + Mtlr, + Mflr, + Mtctr, + Mfctr, + Mtdsisr, + Mfdsisr, + Mtdar, + Mfdar, + Mtdec, + Mfdec, + Mtsdr1, + Mfsdr1, + Mtsrr0, + Mfsrr0, + Mtsrr1, + Mfsrr1, + Mtasr, + Mfasr, + Mtear, + Mfear, + Mttbl, + Mftbl, + Mttbu, + Mftbu, + Mtsprg, + Mfsprg, + Mtibatu, + Mfibatu, + Mtibatl, + Mfibatl, + Mtdbatu, + Mfdbatu, + Mtdbatl, + Mfdbatl, + Nop, + Li, + Lis, + La, + Mr, + Not, + Mtcr, + Mfspr, + Mftb, + Mtspr, + LastMnemonic = Mtspr, + InvalidMnemonic +}; +} // namespace Common::GekkoAssembler diff --git a/Source/Core/Common/Assembler/AssemblerTables.cpp b/Source/Core/Common/Assembler/AssemblerTables.cpp new file mode 100644 index 0000000000..ba6c8ad5d4 --- /dev/null +++ b/Source/Core/Common/Assembler/AssemblerTables.cpp @@ -0,0 +1,1482 @@ +// Copyright 2023 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "Common/Assembler/AssemblerTables.h" + +#include "Common/Assembler/AssemblerShared.h" +#include "Common/Assembler/CaseInsensitiveDict.h" +#include "Common/CommonTypes.h" + +namespace Common::GekkoAssembler::detail +{ +namespace +{ +constexpr size_t PLAIN_MNEMONIC = 0x0; +constexpr size_t RECORD_BIT = 0x1; +constexpr size_t OVERFLOW_EXCEPTION = 0x2; +// Since RC/OE are mutually exclusive from LK/AA, they can occupy the same slot +constexpr size_t LINK_BIT = 0x1; +constexpr size_t ABSOLUTE_ADDRESS_BIT = 0x2; + +// Compile-time helpers for mnemonic generation +// Generate inclusive mask [left, right] -- MSB=0 LSB=31 +constexpr u32 Mask(u32 left, u32 right) +{ + return static_cast<u32>(((u64{1} << (32 - left)) - 1) & ~((u64{1} << (31 - right)) - 1)); +} +constexpr u32 InsertVal(u32 val, u32 left, u32 right) +{ + return val << (31 - right) & Mask(left, right); +} +constexpr u32 InsertOpcode(u32 opcode) +{ + return InsertVal(opcode, 0, 5); +} +constexpr u32 SprBitswap(u32 spr) +{ + return ((spr & 0b0000011111) << 5) | ((spr & 0b1111100000) >> 5); +} + +constexpr MnemonicDesc INVALID_MNEMONIC = {0, 0, {}}; +constexpr ExtendedMnemonicDesc INVALID_EXT_MNEMONIC = {0, nullptr}; + +// All operands as referenced by the Gekko/Broadway user manual +// See section 12.1.2 under Chapter 12 +constexpr OperandDesc _A = OperandDesc{Mask(11, 15), {16, false}}; +constexpr OperandDesc _B = OperandDesc{Mask(16, 20), {11, false}}; +constexpr OperandDesc _BD = OperandDesc{Mask(16, 29), {0, true}}; +constexpr OperandDesc _BI = OperandDesc{Mask(11, 15), {16, false}}; +constexpr OperandDesc _BO = OperandDesc{Mask(6, 10), {21, false}}; +constexpr OperandDesc _C = OperandDesc{Mask(21, 25), {6, false}}; +constexpr OperandDesc _Crba = OperandDesc{Mask(11, 15), {16, false}}; +constexpr OperandDesc _Crbb = OperandDesc{Mask(16, 20), {11, false}}; +constexpr OperandDesc _Crbd = OperandDesc{Mask(6, 10), {21, false}}; +constexpr OperandDesc _Crfd = OperandDesc{Mask(6, 8), {23, false}}; +constexpr OperandDesc _Crfs = OperandDesc{Mask(11, 13), {18, false}}; +constexpr OperandDesc _CRM = OperandDesc{Mask(12, 19), {12, false}}; +constexpr OperandDesc _D = OperandDesc{Mask(6, 10), {21, false}}; +constexpr OperandDesc _FM = OperandDesc{Mask(7, 14), {17, false}}; +constexpr OperandDesc _W1 = OperandDesc{Mask(16, 16), {15, false}}; +constexpr OperandDesc _W2 = OperandDesc{Mask(21, 21), {10, false}}; +constexpr OperandDesc _IMM = OperandDesc{Mask(16, 19), {12, false}}; +constexpr OperandDesc _L = OperandDesc{Mask(10, 10), {21, false}}; +constexpr OperandDesc _LI = OperandDesc{Mask(6, 29), {0, true}}; +constexpr OperandDesc _MB = OperandDesc{Mask(21, 25), {6, false}}; +constexpr OperandDesc _ME = OperandDesc{Mask(26, 30), {1, false}}; +constexpr OperandDesc _NB = OperandDesc{Mask(16, 20), {11, false}}; +constexpr OperandDesc _Offd = OperandDesc{Mask(16, 31), {0, true}}; +constexpr OperandDesc _OffdPs = OperandDesc{Mask(20, 31), {0, true}}; +constexpr OperandDesc _S = OperandDesc{Mask(6, 10), {21, false}}; +constexpr OperandDesc _SH = OperandDesc{Mask(16, 20), {11, false}}; +constexpr OperandDesc _SIMM = OperandDesc{Mask(16, 31), {0, true}}; +constexpr OperandDesc _SPR = OperandDesc{Mask(11, 20), {11, false}}; +constexpr OperandDesc _SR = OperandDesc{Mask(12, 15), {16, false}}; +constexpr OperandDesc _TO = OperandDesc{Mask(6, 10), {21, false}}; +constexpr OperandDesc _TPR = OperandDesc{Mask(11, 20), {11, false}}; +constexpr OperandDesc _UIMM = OperandDesc{Mask(16, 31), {0, false}}; +constexpr OperandDesc _I1 = OperandDesc{Mask(17, 19), {12, false}}; +constexpr OperandDesc _I2 = OperandDesc{Mask(22, 24), {7, false}}; +} // namespace + +void OperandList::Insert(size_t before, u32 val) +{ + overfill = count == MAX_OPERANDS; + for (size_t i = before + 1; i <= count && i < MAX_OPERANDS; i++) + { + std::swap(list[i], list[before]); + } + + list[before] = Tagged<Interval, u32>({0, 0}, val); + if (!overfill) + { + count++; + } +} + +// OperandDesc holds the shift position for an operand, as well as the mask +// Whether the user provided a valid input for an operand can be determined by the mask +u32 OperandDesc::MaxVal() const +{ + const u32 mask_sh = mask >> shift; + if (is_signed) + { + const u32 mask_hibit = (mask_sh & (mask_sh ^ (mask_sh >> 1))); + return mask_hibit - 1; + } + return mask_sh; +} + +u32 OperandDesc::MinVal() const +{ + if (is_signed) + { + return ~MaxVal(); + } + return 0; +} + +u32 OperandDesc::TruncBits() const +{ + const u32 mask_sh = mask >> shift; + const u32 mask_lobit = mask_sh & (mask_sh ^ (mask_sh << 1)); + return mask_lobit - 1; +} + +bool OperandDesc::Fits(u32 val) const +{ + const u32 mask_sh = mask >> shift; + if (is_signed) + { + // Get high bit and low bit from a range mask + const u32 mask_hibit = mask_sh & (mask_sh ^ (mask_sh >> 1)); + const u32 mask_lobit = mask_sh & (mask_sh ^ (mask_sh << 1)); + // Positive max is (signbit - 1) + // Negative min is ~(Positive Max) + const u32 positive_max = mask_hibit - 1; + const u32 negative_max = ~positive_max; + // Truncated bits are any bits right of the mask that are 0 after shifting + const u32 truncate_bits = mask_lobit - 1; + return (val <= positive_max || val >= negative_max) && !(val & truncate_bits); + } + return (mask_sh & val) == val; +} + +u32 OperandDesc::Fit(u32 val) const +{ + return (val << shift) & mask; +} + +/////////////////// +// PARSER TABLES // +/////////////////// + +extern const CaseInsensitiveDict<u32, '_'> sprg_map = { + {"xer", 1}, {"lr", 8}, {"ctr", 9}, {"dsisr", 18}, {"dar", 19}, + {"dec", 22}, {"sdr1", 25}, {"srr0", 26}, {"srr1", 27}, {"sprg0", 272}, + {"sprg1", 273}, {"sprg2", 274}, {"sprg3", 275}, {"ear", 282}, {"tbl", 284}, + {"tbu", 285}, {"ibat0u", 528}, {"ibat0l", 529}, {"ibat1u", 530}, {"ibat1l", 531}, + {"ibat2u", 532}, {"ibat2l", 533}, {"ibat3u", 534}, {"ibat3l", 535}, {"dbat0u", 536}, + {"dbat0l", 537}, {"dbat1u", 538}, {"dbat1l", 539}, {"dbat2u", 540}, {"dbat2l", 541}, + {"dbat3u", 542}, {"dbat3l", 543}, {"gqr0", 912}, {"gqr1", 913}, {"gqr2", 914}, + {"gqr3", 915}, {"gqr4", 916}, {"gqr5", 917}, {"gqr6", 918}, {"gqr7", 919}, + {"hid2", 920}, {"wpar", 921}, {"dma_u", 922}, {"dma_l", 923}, {"ummcr0", 936}, + {"upmc1", 937}, {"upmc2", 938}, {"usia", 939}, {"ummcr1", 940}, {"upmc3", 941}, + {"upmc4", 942}, {"usda", 943}, {"mmcr0", 952}, {"pmc1", 953}, {"pmc2", 954}, + {"sia", 955}, {"mmcr1", 956}, {"pmc3", 957}, {"pmc4", 958}, {"sda", 959}, + {"hid0", 1008}, {"hid1", 1009}, {"iabr", 1010}, {"dabr", 1013}, {"l2cr", 1017}, + {"ictc", 1019}, {"thrm1", 1020}, {"thrm2", 1021}, {"thrm3", 1022}}; + +extern const CaseInsensitiveDict<GekkoDirective> directives_map = { + {"byte", GekkoDirective::Byte}, {"2byte", GekkoDirective::_2byte}, + {"4byte", GekkoDirective::_4byte}, {"8byte", GekkoDirective::_8byte}, + {"float", GekkoDirective::Float}, {"double", GekkoDirective::Double}, + {"locate", GekkoDirective::Locate}, {"padalign", GekkoDirective::PadAlign}, + {"align", GekkoDirective::Align}, {"zeros", GekkoDirective::Zeros}, + {"skip", GekkoDirective::Skip}, {"defvar", GekkoDirective::DefVar}, + {"ascii", GekkoDirective::Ascii}, {"asciz", GekkoDirective::Asciz}, +}; + +#define MNEMONIC(mnemonic_str, mnemonic_enum, variant_bits, alg) \ + { \ + mnemonic_str, \ + { \ + static_cast<size_t>(mnemonic_enum) * VARIANT_PERMUTATIONS + (variant_bits), alg \ + } \ + } +#define PLAIN_MNEMONIC(mnemonic_str, mnemonic_enum, alg) \ + MNEMONIC(mnemonic_str, mnemonic_enum, PLAIN_MNEMONIC, alg) +#define RC_MNEMONIC(mnemonic_str, mnemonic_enum, alg) \ + MNEMONIC(mnemonic_str, mnemonic_enum, PLAIN_MNEMONIC, alg), \ + MNEMONIC(mnemonic_str ".", mnemonic_enum, RECORD_BIT, alg) +#define OERC_MNEMONIC(mnemonic_str, mnemonic_enum, alg) \ + MNEMONIC(mnemonic_str, mnemonic_enum, PLAIN_MNEMONIC, alg), \ + MNEMONIC(mnemonic_str ".", mnemonic_enum, RECORD_BIT, alg), \ + MNEMONIC(mnemonic_str "o", mnemonic_enum, OVERFLOW_EXCEPTION, alg), \ + MNEMONIC(mnemonic_str "o.", mnemonic_enum, (RECORD_BIT | OVERFLOW_EXCEPTION), alg) +#define LK_MNEMONIC(mnemonic_str, mnemonic_enum, alg) \ + MNEMONIC(mnemonic_str, mnemonic_enum, PLAIN_MNEMONIC, alg), \ + MNEMONIC(mnemonic_str "l", mnemonic_enum, LINK_BIT, alg) +#define AALK_MNEMONIC(mnemonic_str, mnemonic_enum, alg) \ + MNEMONIC(mnemonic_str, mnemonic_enum, PLAIN_MNEMONIC, alg), \ + MNEMONIC(mnemonic_str "l", mnemonic_enum, LINK_BIT, alg), \ + MNEMONIC(mnemonic_str "a", mnemonic_enum, ABSOLUTE_ADDRESS_BIT, alg), \ + MNEMONIC(mnemonic_str "la", mnemonic_enum, (LINK_BIT | ABSOLUTE_ADDRESS_BIT), alg) + +extern const CaseInsensitiveDict<ParseInfo, '.', '_'> mnemonic_tokens = { + OERC_MNEMONIC("add", GekkoMnemonic::Add, ParseAlg::Op3), + OERC_MNEMONIC("addc", GekkoMnemonic::Addc, ParseAlg::Op3), + OERC_MNEMONIC("adde", GekkoMnemonic::Adde, ParseAlg::Op3), + PLAIN_MNEMONIC("addi", GekkoMnemonic::Addi, ParseAlg::Op3), + PLAIN_MNEMONIC("addic", GekkoMnemonic::Addic, ParseAlg::Op3), + PLAIN_MNEMONIC("addic.", GekkoMnemonic::AddicDot, ParseAlg::Op3), + PLAIN_MNEMONIC("addis", GekkoMnemonic::Addis, ParseAlg::Op3), + OERC_MNEMONIC("addme", GekkoMnemonic::Addme, ParseAlg::Op2), + OERC_MNEMONIC("addze", GekkoMnemonic::Addze, ParseAlg::Op2), + RC_MNEMONIC("and", GekkoMnemonic::And, ParseAlg::Op3), + RC_MNEMONIC("andc", GekkoMnemonic::Andc, ParseAlg::Op3), + PLAIN_MNEMONIC("andi.", GekkoMnemonic::AndiDot, ParseAlg::Op3), + PLAIN_MNEMONIC("andis.", GekkoMnemonic::AndisDot, ParseAlg::Op3), + AALK_MNEMONIC("b", GekkoMnemonic::B, ParseAlg::Op1), + AALK_MNEMONIC("bc", GekkoMnemonic::Bc, ParseAlg::Op3), + LK_MNEMONIC("bcctr", GekkoMnemonic::Bcctr, ParseAlg::Op2), + LK_MNEMONIC("bclr", GekkoMnemonic::Bclr, ParseAlg::Op2), + PLAIN_MNEMONIC("cmp", GekkoMnemonic::Cmp, ParseAlg::Op4), + PLAIN_MNEMONIC("cmpi", GekkoMnemonic::Cmpi, ParseAlg::Op4), + PLAIN_MNEMONIC("cmpl", GekkoMnemonic::Cmpl, ParseAlg::Op4), + PLAIN_MNEMONIC("cmpli", GekkoMnemonic::Cmpli, ParseAlg::Op4), + RC_MNEMONIC("cntlzw", GekkoMnemonic::Cntlzw, ParseAlg::Op2), + PLAIN_MNEMONIC("crand", GekkoMnemonic::Crand, ParseAlg::Op3), + PLAIN_MNEMONIC("crandc", GekkoMnemonic::Crandc, ParseAlg::Op3), + PLAIN_MNEMONIC("creqv", GekkoMnemonic::Creqv, ParseAlg::Op3), + PLAIN_MNEMONIC("crnand", GekkoMnemonic::Crnand, ParseAlg::Op3), + PLAIN_MNEMONIC("crnor", GekkoMnemonic::Crnor, ParseAlg::Op3), + PLAIN_MNEMONIC("cror", GekkoMnemonic::Cror, ParseAlg::Op3), + PLAIN_MNEMONIC("crorc", GekkoMnemonic::Crorc, ParseAlg::Op3), + PLAIN_MNEMONIC("crxor", GekkoMnemonic::Crxor, ParseAlg::Op3), + PLAIN_MNEMONIC("dcbf", GekkoMnemonic::Dcbf, ParseAlg::Op2), + PLAIN_MNEMONIC("dcbi", GekkoMnemonic::Dcbi, ParseAlg::Op2), + PLAIN_MNEMONIC("dcbst", GekkoMnemonic::Dcbst, ParseAlg::Op2), + PLAIN_MNEMONIC("dcbt", GekkoMnemonic::Dcbt, ParseAlg::Op2), + PLAIN_MNEMONIC("dcbtst", GekkoMnemonic::Dcbtst, ParseAlg::Op2), + PLAIN_MNEMONIC("dcbz", GekkoMnemonic::Dcbz, ParseAlg::Op2), + PLAIN_MNEMONIC("dcbz_l", GekkoMnemonic::Dcbz_l, ParseAlg::Op2), + OERC_MNEMONIC("divw", GekkoMnemonic::Divw, ParseAlg::Op3), + OERC_MNEMONIC("divwu", GekkoMnemonic::Divwu, ParseAlg::Op3), + PLAIN_MNEMONIC("eciwx", GekkoMnemonic::Eciwx, ParseAlg::Op3), + PLAIN_MNEMONIC("ecowx", GekkoMnemonic::Ecowx, ParseAlg::Op3), + PLAIN_MNEMONIC("eieio", GekkoMnemonic::Eieio, ParseAlg::None), + RC_MNEMONIC("eqv", GekkoMnemonic::Eqv, ParseAlg::Op3), + RC_MNEMONIC("extsb", GekkoMnemonic::Extsb, ParseAlg::Op2), + RC_MNEMONIC("extsh", GekkoMnemonic::Extsh, ParseAlg::Op2), + RC_MNEMONIC("fabs", GekkoMnemonic::Fabs, ParseAlg::Op2), + RC_MNEMONIC("fadd", GekkoMnemonic::Fadd, ParseAlg::Op3), + RC_MNEMONIC("fadds", GekkoMnemonic::Fadds, ParseAlg::Op3), + PLAIN_MNEMONIC("fcmpo", GekkoMnemonic::Fcmpo, ParseAlg::Op3), + PLAIN_MNEMONIC("fcmpu", GekkoMnemonic::Fcmpu, ParseAlg::Op3), + RC_MNEMONIC("fctiw", GekkoMnemonic::Fctiw, ParseAlg::Op2), + RC_MNEMONIC("fctiwz", GekkoMnemonic::Fctiwz, ParseAlg::Op2), + RC_MNEMONIC("fdiv", GekkoMnemonic::Fdiv, ParseAlg::Op3), + RC_MNEMONIC("fdivs", GekkoMnemonic::Fdivs, ParseAlg::Op3), + RC_MNEMONIC("fmadd", GekkoMnemonic::Fmadd, ParseAlg::Op4), + RC_MNEMONIC("fmadds", GekkoMnemonic::Fmadds, ParseAlg::Op4), + RC_MNEMONIC("fmr", GekkoMnemonic::Fmr, ParseAlg::Op2), + RC_MNEMONIC("fmsub", GekkoMnemonic::Fmsub, ParseAlg::Op4), + RC_MNEMONIC("fmsubs", GekkoMnemonic::Fmsubs, ParseAlg::Op4), + RC_MNEMONIC("fmul", GekkoMnemonic::Fmul, ParseAlg::Op3), + RC_MNEMONIC("fmuls", GekkoMnemonic::Fmuls, ParseAlg::Op3), + RC_MNEMONIC("fnabs", GekkoMnemonic::Fnabs, ParseAlg::Op2), + RC_MNEMONIC("fneg", GekkoMnemonic::Fneg, ParseAlg::Op2), + RC_MNEMONIC("fnmadd", GekkoMnemonic::Fnmadd, ParseAlg::Op4), + RC_MNEMONIC("fnmadds", GekkoMnemonic::Fnmadds, ParseAlg::Op4), + RC_MNEMONIC("fnmsub", GekkoMnemonic::Fnmsub, ParseAlg::Op4), + RC_MNEMONIC("fnmsubs", GekkoMnemonic::Fnmsubs, ParseAlg::Op4), + RC_MNEMONIC("fres", GekkoMnemonic::Fres, ParseAlg::Op2), + RC_MNEMONIC("frsp", GekkoMnemonic::Frsp, ParseAlg::Op2), + RC_MNEMONIC("frsqrte", GekkoMnemonic::Frsqrte, ParseAlg::Op2), + RC_MNEMONIC("fsel", GekkoMnemonic::Fsel, ParseAlg::Op4), + RC_MNEMONIC("fsub", GekkoMnemonic::Fsub, ParseAlg::Op3), + RC_MNEMONIC("fsubs", GekkoMnemonic::Fsubs, ParseAlg::Op3), + PLAIN_MNEMONIC("icbi", GekkoMnemonic::Icbi, ParseAlg::Op2), + PLAIN_MNEMONIC("isync", GekkoMnemonic::Isync, ParseAlg::None), + PLAIN_MNEMONIC("lbz", GekkoMnemonic::Lbz, ParseAlg::Op1Off1), + PLAIN_MNEMONIC("lbzu", GekkoMnemonic::Lbzu, ParseAlg::Op1Off1), + PLAIN_MNEMONIC("lbzux", GekkoMnemonic::Lbzux, ParseAlg::Op3), + PLAIN_MNEMONIC("lbzx", GekkoMnemonic::Lbzx, ParseAlg::Op3), + PLAIN_MNEMONIC("lfd", GekkoMnemonic::Lfd, ParseAlg::Op1Off1), + PLAIN_MNEMONIC("lfdu", GekkoMnemonic::Lfdu, ParseAlg::Op1Off1), + PLAIN_MNEMONIC("lfdux", GekkoMnemonic::Lfdux, ParseAlg::Op3), + PLAIN_MNEMONIC("lfdx", GekkoMnemonic::Lfdx, ParseAlg::Op3), + PLAIN_MNEMONIC("lfs", GekkoMnemonic::Lfs, ParseAlg::Op1Off1), + PLAIN_MNEMONIC("lfsu", GekkoMnemonic::Lfsu, ParseAlg::Op1Off1), + PLAIN_MNEMONIC("lfsux", GekkoMnemonic::Lfsux, ParseAlg::Op3), + PLAIN_MNEMONIC("lfsx", GekkoMnemonic::Lfsx, ParseAlg::Op3), + PLAIN_MNEMONIC("lha", GekkoMnemonic::Lha, ParseAlg::Op1Off1), + PLAIN_MNEMONIC("lhau", GekkoMnemonic::Lhau, ParseAlg::Op1Off1), + PLAIN_MNEMONIC("lhaux", GekkoMnemonic::Lhaux, ParseAlg::Op3), + PLAIN_MNEMONIC("lhax", GekkoMnemonic::Lhax, ParseAlg::Op3), + PLAIN_MNEMONIC("lhbrx", GekkoMnemonic::Lhbrx, ParseAlg::Op3), + PLAIN_MNEMONIC("lhz", GekkoMnemonic::Lhz, ParseAlg::Op1Off1), + PLAIN_MNEMONIC("lhzu", GekkoMnemonic::Lhzu, ParseAlg::Op1Off1), + PLAIN_MNEMONIC("lhzux", GekkoMnemonic::Lhzux, ParseAlg::Op3), + PLAIN_MNEMONIC("lhzx", GekkoMnemonic::Lhzx, ParseAlg::Op3), + PLAIN_MNEMONIC("lmw", GekkoMnemonic::Lmw, ParseAlg::Op1Off1), + PLAIN_MNEMONIC("lswi", GekkoMnemonic::Lswi, ParseAlg::Op3), + PLAIN_MNEMONIC("lswx", GekkoMnemonic::Lswx, ParseAlg::Op3), + PLAIN_MNEMONIC("lwarx", GekkoMnemonic::Lwarx, ParseAlg::Op3), + PLAIN_MNEMONIC("lwbrx", GekkoMnemonic::Lwbrx, ParseAlg::Op3), + PLAIN_MNEMONIC("lwz", GekkoMnemonic::Lwz, ParseAlg::Op1Off1), + PLAIN_MNEMONIC("lwzu", GekkoMnemonic::Lwzu, ParseAlg::Op1Off1), + PLAIN_MNEMONIC("lwzux", GekkoMnemonic::Lwzux, ParseAlg::Op3), + PLAIN_MNEMONIC("lwzx", GekkoMnemonic::Lwzx, ParseAlg::Op3), + PLAIN_MNEMONIC("mcrf", GekkoMnemonic::Mcrf, ParseAlg::Op2), + PLAIN_MNEMONIC("mcrfs", GekkoMnemonic::Mcrfs, ParseAlg::Op2), + PLAIN_MNEMONIC("mcrxr", GekkoMnemonic::Mcrxr, ParseAlg::Op1), + PLAIN_MNEMONIC("mfcr", GekkoMnemonic::Mfcr, ParseAlg::Op1), + RC_MNEMONIC("mffs", GekkoMnemonic::Mffs, ParseAlg::Op1), + PLAIN_MNEMONIC("mfmsr", GekkoMnemonic::Mfmsr, ParseAlg::Op1), + PLAIN_MNEMONIC("mfspr_nobitswap", GekkoMnemonic::Mfspr_nobitswap, ParseAlg::Op2), + PLAIN_MNEMONIC("mfsr", GekkoMnemonic::Mfsr, ParseAlg::Op2), + PLAIN_MNEMONIC("mfsrin", GekkoMnemonic::Mfsrin, ParseAlg::Op2), + PLAIN_MNEMONIC("mftb_nobitswap", GekkoMnemonic::Mftb_nobitswap, ParseAlg::Op2), + PLAIN_MNEMONIC("mtcrf", GekkoMnemonic::Mtcrf, ParseAlg::Op2), + RC_MNEMONIC("mtfsb0", GekkoMnemonic::Mtfsb0, ParseAlg::Op1), + RC_MNEMONIC("mtfsb1", GekkoMnemonic::Mtfsb1, ParseAlg::Op1), + RC_MNEMONIC("mtfsf", GekkoMnemonic::Mtfsf, ParseAlg::Op2), + RC_MNEMONIC("mtfsfi", GekkoMnemonic::Mtfsfi, ParseAlg::Op2), + PLAIN_MNEMONIC("mtmsr", GekkoMnemonic::Mtmsr, ParseAlg::Op1), + PLAIN_MNEMONIC("mtspr_nobitswap", GekkoMnemonic::Mtspr_nobitswap, ParseAlg::Op2), + PLAIN_MNEMONIC("mtsr", GekkoMnemonic::Mtsr, ParseAlg::Op2), + PLAIN_MNEMONIC("mtsrin", GekkoMnemonic::Mtsrin, ParseAlg::Op2), + RC_MNEMONIC("mulhw", GekkoMnemonic::Mulhw, ParseAlg::Op3), + RC_MNEMONIC("mulhwu", GekkoMnemonic::Mulhwu, ParseAlg::Op3), + PLAIN_MNEMONIC("mulli", GekkoMnemonic::Mulli, ParseAlg::Op3), + OERC_MNEMONIC("mullw", GekkoMnemonic::Mullw, ParseAlg::Op3), + RC_MNEMONIC("nand", GekkoMnemonic::Nand, ParseAlg::Op3), + OERC_MNEMONIC("neg", GekkoMnemonic::Neg, ParseAlg::Op2), + RC_MNEMONIC("nor", GekkoMnemonic::Nor, ParseAlg::Op3), + RC_MNEMONIC("or", GekkoMnemonic::Or, ParseAlg::Op3), + RC_MNEMONIC("orc", GekkoMnemonic::Orc, ParseAlg::Op3), + PLAIN_MNEMONIC("ori", GekkoMnemonic::Ori, ParseAlg::Op3), + PLAIN_MNEMONIC("oris", GekkoMnemonic::Oris, ParseAlg::Op3), + PLAIN_MNEMONIC("psq_l", GekkoMnemonic::Psq_l, ParseAlg::Op1Off1Op2), + PLAIN_MNEMONIC("psq_lu", GekkoMnemonic::Psq_lu, ParseAlg::Op1Off1Op2), + PLAIN_MNEMONIC("psq_lux", GekkoMnemonic::Psq_lux, ParseAlg::Op5), + PLAIN_MNEMONIC("psq_lx", GekkoMnemonic::Psq_lx, ParseAlg::Op5), + PLAIN_MNEMONIC("psq_st", GekkoMnemonic::Psq_st, ParseAlg::Op1Off1Op2), + PLAIN_MNEMONIC("psq_stu", GekkoMnemonic::Psq_stu, ParseAlg::Op1Off1Op2), + PLAIN_MNEMONIC("psq_stux", GekkoMnemonic::Psq_stux, ParseAlg::Op5), + PLAIN_MNEMONIC("psq_stx", GekkoMnemonic::Psq_stx, ParseAlg::Op5), + RC_MNEMONIC("ps_abs", GekkoMnemonic::Ps_abs, ParseAlg::Op2), + RC_MNEMONIC("ps_add", GekkoMnemonic::Ps_add, ParseAlg::Op3), + PLAIN_MNEMONIC("ps_cmpo0", GekkoMnemonic::Ps_cmpo0, ParseAlg::Op3), + PLAIN_MNEMONIC("ps_cmpo1", GekkoMnemonic::Ps_cmpo1, ParseAlg::Op3), + PLAIN_MNEMONIC("ps_cmpu0", GekkoMnemonic::Ps_cmpu0, ParseAlg::Op3), + PLAIN_MNEMONIC("ps_cmpu1", GekkoMnemonic::Ps_cmpu1, ParseAlg::Op3), + RC_MNEMONIC("ps_div", GekkoMnemonic::Ps_div, ParseAlg::Op3), + RC_MNEMONIC("ps_madd", GekkoMnemonic::Ps_madd, ParseAlg::Op4), + RC_MNEMONIC("ps_madds0", GekkoMnemonic::Ps_madds0, ParseAlg::Op4), + RC_MNEMONIC("ps_madds1", GekkoMnemonic::Ps_madds1, ParseAlg::Op4), + RC_MNEMONIC("ps_merge00", GekkoMnemonic::Ps_merge00, ParseAlg::Op3), + RC_MNEMONIC("ps_merge01", GekkoMnemonic::Ps_merge01, ParseAlg::Op3), + RC_MNEMONIC("ps_merge10", GekkoMnemonic::Ps_merge10, ParseAlg::Op3), + RC_MNEMONIC("ps_merge11", GekkoMnemonic::Ps_merge11, ParseAlg::Op3), + RC_MNEMONIC("ps_mr", GekkoMnemonic::Ps_mr, ParseAlg::Op2), + RC_MNEMONIC("ps_msub", GekkoMnemonic::Ps_msub, ParseAlg::Op4), + RC_MNEMONIC("ps_mul", GekkoMnemonic::Ps_mul, ParseAlg::Op3), + RC_MNEMONIC("ps_muls0", GekkoMnemonic::Ps_muls0, ParseAlg::Op3), + RC_MNEMONIC("ps_muls1", GekkoMnemonic::Ps_muls1, ParseAlg::Op3), + RC_MNEMONIC("ps_nabs", GekkoMnemonic::Ps_nabs, ParseAlg::Op2), + RC_MNEMONIC("ps_neg", GekkoMnemonic::Ps_neg, ParseAlg::Op2), + RC_MNEMONIC("ps_nmadd", GekkoMnemonic::Ps_nmadd, ParseAlg::Op4), + RC_MNEMONIC("ps_nmsub", GekkoMnemonic::Ps_nmsub, ParseAlg::Op4), + RC_MNEMONIC("ps_res", GekkoMnemonic::Ps_res, ParseAlg::Op2), + RC_MNEMONIC("ps_rsqrte", GekkoMnemonic::Ps_rsqrte, ParseAlg::Op2), + RC_MNEMONIC("ps_sel", GekkoMnemonic::Ps_sel, ParseAlg::Op4), + RC_MNEMONIC("ps_sub", GekkoMnemonic::Ps_sub, ParseAlg::Op3), + RC_MNEMONIC("ps_sum0", GekkoMnemonic::Ps_sum0, ParseAlg::Op4), + RC_MNEMONIC("ps_sum1", GekkoMnemonic::Ps_sum1, ParseAlg::Op4), + PLAIN_MNEMONIC("rfi", GekkoMnemonic::Rfi, ParseAlg::None), + RC_MNEMONIC("rlwimi", GekkoMnemonic::Rlwimi, ParseAlg::Op5), + RC_MNEMONIC("rlwinm", GekkoMnemonic::Rlwinm, ParseAlg::Op5), + RC_MNEMONIC("rlwnm", GekkoMnemonic::Rlwnm, ParseAlg::Op5), + PLAIN_MNEMONIC("sc", GekkoMnemonic::Sc, ParseAlg::None), + RC_MNEMONIC("slw", GekkoMnemonic::Slw, ParseAlg::Op3), + RC_MNEMONIC("sraw", GekkoMnemonic::Sraw, ParseAlg::Op3), + RC_MNEMONIC("srawi", GekkoMnemonic::Srawi, ParseAlg::Op3), + RC_MNEMONIC("srw", GekkoMnemonic::Srw, ParseAlg::Op3), + PLAIN_MNEMONIC("stb", GekkoMnemonic::Stb, ParseAlg::Op1Off1), + PLAIN_MNEMONIC("stbu", GekkoMnemonic::Stbu, ParseAlg::Op1Off1), + PLAIN_MNEMONIC("stbux", GekkoMnemonic::Stbux, ParseAlg::Op3), + PLAIN_MNEMONIC("stbx", GekkoMnemonic::Stbx, ParseAlg::Op3), + PLAIN_MNEMONIC("stfd", GekkoMnemonic::Stfd, ParseAlg::Op1Off1), + PLAIN_MNEMONIC("stfdu", GekkoMnemonic::Stfdu, ParseAlg::Op1Off1), + PLAIN_MNEMONIC("stfdux", GekkoMnemonic::Stfdux, ParseAlg::Op3), + PLAIN_MNEMONIC("stfdx", GekkoMnemonic::Stfdx, ParseAlg::Op3), + PLAIN_MNEMONIC("stfiwx", GekkoMnemonic::Stfiwx, ParseAlg::Op3), + PLAIN_MNEMONIC("stfs", GekkoMnemonic::Stfs, ParseAlg::Op1Off1), + PLAIN_MNEMONIC("stfsu", GekkoMnemonic::Stfsu, ParseAlg::Op1Off1), + PLAIN_MNEMONIC("stfsux", GekkoMnemonic::Stfsux, ParseAlg::Op3), + PLAIN_MNEMONIC("stfsx", GekkoMnemonic::Stfsx, ParseAlg::Op3), + PLAIN_MNEMONIC("sth", GekkoMnemonic::Sth, ParseAlg::Op1Off1), + PLAIN_MNEMONIC("sthbrx", GekkoMnemonic::Sthbrx, ParseAlg::Op3), + PLAIN_MNEMONIC("sthu", GekkoMnemonic::Sthu, ParseAlg::Op1Off1), + PLAIN_MNEMONIC("sthux", GekkoMnemonic::Sthux, ParseAlg::Op3), + PLAIN_MNEMONIC("sthx", GekkoMnemonic::Sthx, ParseAlg::Op3), + PLAIN_MNEMONIC("stmw", GekkoMnemonic::Stmw, ParseAlg::Op1Off1), + PLAIN_MNEMONIC("stswi", GekkoMnemonic::Stswi, ParseAlg::Op3), + PLAIN_MNEMONIC("stswx", GekkoMnemonic::Stswx, ParseAlg::Op3), + PLAIN_MNEMONIC("stw", GekkoMnemonic::Stw, ParseAlg::Op1Off1), + PLAIN_MNEMONIC("stwbrx", GekkoMnemonic::Stwbrx, ParseAlg::Op3), + PLAIN_MNEMONIC("stwcx.", GekkoMnemonic::StwcxDot, ParseAlg::Op3), + PLAIN_MNEMONIC("stwu", GekkoMnemonic::Stwu, ParseAlg::Op1Off1), + PLAIN_MNEMONIC("stwux", GekkoMnemonic::Stwux, ParseAlg::Op3), + PLAIN_MNEMONIC("stwx", GekkoMnemonic::Stwx, ParseAlg::Op3), + OERC_MNEMONIC("subf", GekkoMnemonic::Subf, ParseAlg::Op3), + OERC_MNEMONIC("subfc", GekkoMnemonic::Subfc, ParseAlg::Op3), + OERC_MNEMONIC("subfe", GekkoMnemonic::Subfe, ParseAlg::Op3), + PLAIN_MNEMONIC("subfic", GekkoMnemonic::Subfic, ParseAlg::Op3), + OERC_MNEMONIC("subfme", GekkoMnemonic::Subfme, ParseAlg::Op2), + OERC_MNEMONIC("subfze", GekkoMnemonic::Subfze, ParseAlg::Op2), + PLAIN_MNEMONIC("sync", GekkoMnemonic::Sync, ParseAlg::None), + PLAIN_MNEMONIC("tlbie", GekkoMnemonic::Tlbie, ParseAlg::Op1), + PLAIN_MNEMONIC("tlbsync", GekkoMnemonic::Tlbsync, ParseAlg::None), + PLAIN_MNEMONIC("tw", GekkoMnemonic::Tw, ParseAlg::Op3), + PLAIN_MNEMONIC("twi", GekkoMnemonic::Twi, ParseAlg::Op3), + RC_MNEMONIC("xor", GekkoMnemonic::Xor, ParseAlg::Op3), + PLAIN_MNEMONIC("xori", GekkoMnemonic::Xori, ParseAlg::Op3), + PLAIN_MNEMONIC("xoris", GekkoMnemonic::Xoris, ParseAlg::Op3), +}; + +#define PSEUDO(mnemonic, base, variant_bits, alg) \ + { \ + mnemonic, { static_cast<size_t>(base) * VARIANT_PERMUTATIONS + (variant_bits), alg } \ + } +#define PLAIN_PSEUDO(mnemonic, base, alg) PSEUDO(mnemonic, base, PLAIN_MNEMONIC, alg) +#define RC_PSEUDO(mnemonic, base, alg) \ + PSEUDO(mnemonic, base, PLAIN_MNEMONIC, alg), PSEUDO(mnemonic ".", base, RECORD_BIT, alg) +#define OERC_PSEUDO(mnemonic, base, alg) \ + PSEUDO(mnemonic, base, PLAIN_MNEMONIC, alg), PSEUDO(mnemonic ".", base, RECORD_BIT, alg), \ + PSEUDO(mnemonic "o", base, OVERFLOW_EXCEPTION, alg), \ + PSEUDO(mnemonic "o.", base, (RECORD_BIT | OVERFLOW_EXCEPTION), alg) +#define LK_PSEUDO(mnemonic, base, alg) \ + PSEUDO(mnemonic, base, PLAIN_MNEMONIC, alg), PSEUDO(mnemonic "l", base, LINK_BIT, alg) +#define LKAA_PSEUDO(mnemonic, base, alg) \ + PSEUDO(mnemonic, base, PLAIN_MNEMONIC, alg), PSEUDO(mnemonic "l", base, LINK_BIT, alg), \ + PSEUDO(mnemonic "a", base, ABSOLUTE_ADDRESS_BIT, alg), \ + PSEUDO(mnemonic "la", base, (LINK_BIT | ABSOLUTE_ADDRESS_BIT), alg) +#define LKPRED_PSEUDO(mnemonic, base, alg) \ + PSEUDO(mnemonic, base, PLAIN_MNEMONIC, alg), PSEUDO(mnemonic "l", base, LINK_BIT, alg), \ + PSEUDO(mnemonic "-", base, PLAIN_MNEMONIC, alg), PSEUDO(mnemonic "l-", base, LINK_BIT, alg), \ + PSEUDO(mnemonic "+", base##Predict, PLAIN_MNEMONIC, alg), \ + PSEUDO(mnemonic "l+", base##Predict, LINK_BIT, alg) +#define LKAAPRED_PSEUDO(mnemonic, base, alg) \ + PSEUDO(mnemonic, base, PLAIN_MNEMONIC, alg), PSEUDO(mnemonic "l", base, LINK_BIT, alg), \ + PSEUDO(mnemonic "a", base, ABSOLUTE_ADDRESS_BIT, alg), \ + PSEUDO(mnemonic "la", base, (LINK_BIT | ABSOLUTE_ADDRESS_BIT), alg), \ + PSEUDO(mnemonic "-", base, PLAIN_MNEMONIC, alg), PSEUDO(mnemonic "l-", base, LINK_BIT, alg), \ + PSEUDO(mnemonic "a-", base, ABSOLUTE_ADDRESS_BIT, alg), \ + PSEUDO(mnemonic "la-", base, (LINK_BIT | ABSOLUTE_ADDRESS_BIT), alg), \ + PSEUDO(mnemonic "+", base##Predict, PLAIN_MNEMONIC, alg), \ + PSEUDO(mnemonic "l+", base##Predict, LINK_BIT, alg), \ + PSEUDO(mnemonic "a+", base##Predict, ABSOLUTE_ADDRESS_BIT, alg), \ + PSEUDO(mnemonic "la+", base##Predict, (LINK_BIT | ABSOLUTE_ADDRESS_BIT), alg) + +extern const CaseInsensitiveDict<ParseInfo, '.', '_', '+', '-'> extended_mnemonic_tokens = { + PLAIN_PSEUDO("subi", ExtendedGekkoMnemonic::Subi, ParseAlg::Op3), + PLAIN_PSEUDO("subis", ExtendedGekkoMnemonic::Subis, ParseAlg::Op3), + PLAIN_PSEUDO("subic", ExtendedGekkoMnemonic::Subic, ParseAlg::Op3), + PLAIN_PSEUDO("subic.", ExtendedGekkoMnemonic::SubicDot, ParseAlg::Op3), + OERC_PSEUDO("sub", ExtendedGekkoMnemonic::Sub, ParseAlg::Op3), + OERC_PSEUDO("subc", ExtendedGekkoMnemonic::Subc, ParseAlg::Op3), + PLAIN_PSEUDO("cmpwi", ExtendedGekkoMnemonic::Cmpwi, ParseAlg::Op2Or3), + PLAIN_PSEUDO("cmpw", ExtendedGekkoMnemonic::Cmpw, ParseAlg::Op2Or3), + PLAIN_PSEUDO("cmplwi", ExtendedGekkoMnemonic::Cmplwi, ParseAlg::Op2Or3), + PLAIN_PSEUDO("cmplw", ExtendedGekkoMnemonic::Cmplw, ParseAlg::Op2Or3), + RC_PSEUDO("extlwi", ExtendedGekkoMnemonic::Extlwi, ParseAlg::Op4), + RC_PSEUDO("extrwi", ExtendedGekkoMnemonic::Extrwi, ParseAlg::Op4), + RC_PSEUDO("inslwi", ExtendedGekkoMnemonic::Inslwi, ParseAlg::Op4), + RC_PSEUDO("insrwi", ExtendedGekkoMnemonic::Insrwi, ParseAlg::Op4), + RC_PSEUDO("rotlwi", ExtendedGekkoMnemonic::Rotlwi, ParseAlg::Op3), + RC_PSEUDO("rotrwi", ExtendedGekkoMnemonic::Rotrwi, ParseAlg::Op3), + RC_PSEUDO("rotlw", ExtendedGekkoMnemonic::Rotlw, ParseAlg::Op3), + RC_PSEUDO("slwi", ExtendedGekkoMnemonic::Slwi, ParseAlg::Op3), + RC_PSEUDO("srwi", ExtendedGekkoMnemonic::Srwi, ParseAlg::Op3), + RC_PSEUDO("clrlwi", ExtendedGekkoMnemonic::Clrlwi, ParseAlg::Op3), + RC_PSEUDO("clrrwi", ExtendedGekkoMnemonic::Clrrwi, ParseAlg::Op3), + RC_PSEUDO("clrlslwi", ExtendedGekkoMnemonic::Clrlslwi, ParseAlg::Op4), + LKAAPRED_PSEUDO("bt", ExtendedGekkoMnemonic::Bt, ParseAlg::Op2), + LKAAPRED_PSEUDO("bf", ExtendedGekkoMnemonic::Bf, ParseAlg::Op2), + LKAAPRED_PSEUDO("bdnz", ExtendedGekkoMnemonic::Bdnz, ParseAlg::Op1), + LKAAPRED_PSEUDO("bdnzt", ExtendedGekkoMnemonic::Bdnzt, ParseAlg::Op2), + LKAAPRED_PSEUDO("bdnzf", ExtendedGekkoMnemonic::Bdnzf, ParseAlg::Op2), + LKAAPRED_PSEUDO("bdz", ExtendedGekkoMnemonic::Bdz, ParseAlg::Op1), + LKAAPRED_PSEUDO("bdzt", ExtendedGekkoMnemonic::Bdzt, ParseAlg::Op2), + LKAAPRED_PSEUDO("bdzf", ExtendedGekkoMnemonic::Bdzf, ParseAlg::Op2), + LK_PSEUDO("blr", ExtendedGekkoMnemonic::Blr, ParseAlg::None), + LK_PSEUDO("bctr", ExtendedGekkoMnemonic::Bctr, ParseAlg::None), + LKPRED_PSEUDO("btlr", ExtendedGekkoMnemonic::Btlr, ParseAlg::Op1), + LKPRED_PSEUDO("btctr", ExtendedGekkoMnemonic::Btctr, ParseAlg::Op1), + LKPRED_PSEUDO("bflr", ExtendedGekkoMnemonic::Bflr, ParseAlg::Op1), + LKPRED_PSEUDO("bfctr", ExtendedGekkoMnemonic::Bfctr, ParseAlg::Op1), + LKPRED_PSEUDO("bdnzlr", ExtendedGekkoMnemonic::Bdnzlr, ParseAlg::None), + LKPRED_PSEUDO("bdnztlr", ExtendedGekkoMnemonic::Bdnztlr, ParseAlg::Op1), + LKPRED_PSEUDO("bdnzflr", ExtendedGekkoMnemonic::Bdnzflr, ParseAlg::Op1), + LKPRED_PSEUDO("bdzlr", ExtendedGekkoMnemonic::Bdzlr, ParseAlg::None), + LKPRED_PSEUDO("bdztlr", ExtendedGekkoMnemonic::Bdztlr, ParseAlg::Op1), + LKPRED_PSEUDO("bdzflr", ExtendedGekkoMnemonic::Bdzflr, ParseAlg::Op1), + LKAAPRED_PSEUDO("blt", ExtendedGekkoMnemonic::Blt, ParseAlg::Op1Or2), + LKAAPRED_PSEUDO("ble", ExtendedGekkoMnemonic::Ble, ParseAlg::Op1Or2), + LKAAPRED_PSEUDO("beq", ExtendedGekkoMnemonic::Beq, ParseAlg::Op1Or2), + LKAAPRED_PSEUDO("bge", ExtendedGekkoMnemonic::Bge, ParseAlg::Op1Or2), + LKAAPRED_PSEUDO("bgt", ExtendedGekkoMnemonic::Bgt, ParseAlg::Op1Or2), + LKAAPRED_PSEUDO("bnl", ExtendedGekkoMnemonic::Bnl, ParseAlg::Op1Or2), + LKAAPRED_PSEUDO("bne", ExtendedGekkoMnemonic::Bne, ParseAlg::Op1Or2), + LKAAPRED_PSEUDO("bng", ExtendedGekkoMnemonic::Bng, ParseAlg::Op1Or2), + LKAAPRED_PSEUDO("bso", ExtendedGekkoMnemonic::Bso, ParseAlg::Op1Or2), + LKAAPRED_PSEUDO("bns", ExtendedGekkoMnemonic::Bns, ParseAlg::Op1Or2), + LKAAPRED_PSEUDO("bun", ExtendedGekkoMnemonic::Bun, ParseAlg::Op1Or2), + LKAAPRED_PSEUDO("bnu", ExtendedGekkoMnemonic::Bnu, ParseAlg::Op1Or2), + LKPRED_PSEUDO("bltlr", ExtendedGekkoMnemonic::Bltlr, ParseAlg::NoneOrOp1), + LKPRED_PSEUDO("bltctr", ExtendedGekkoMnemonic::Bltctr, ParseAlg::NoneOrOp1), + LKPRED_PSEUDO("blelr", ExtendedGekkoMnemonic::Blelr, ParseAlg::NoneOrOp1), + LKPRED_PSEUDO("blectr", ExtendedGekkoMnemonic::Blectr, ParseAlg::NoneOrOp1), + LKPRED_PSEUDO("beqlr", ExtendedGekkoMnemonic::Beqlr, ParseAlg::NoneOrOp1), + LKPRED_PSEUDO("beqctr", ExtendedGekkoMnemonic::Beqctr, ParseAlg::NoneOrOp1), + LKPRED_PSEUDO("bgelr", ExtendedGekkoMnemonic::Bgelr, ParseAlg::NoneOrOp1), + LKPRED_PSEUDO("bgectr", ExtendedGekkoMnemonic::Bgectr, ParseAlg::NoneOrOp1), + LKPRED_PSEUDO("bgtlr", ExtendedGekkoMnemonic::Bgtlr, ParseAlg::NoneOrOp1), + LKPRED_PSEUDO("bgtctr", ExtendedGekkoMnemonic::Bgtctr, ParseAlg::NoneOrOp1), + LKPRED_PSEUDO("bnllr", ExtendedGekkoMnemonic::Bnllr, ParseAlg::NoneOrOp1), + LKPRED_PSEUDO("bnlctr", ExtendedGekkoMnemonic::Bnlctr, ParseAlg::NoneOrOp1), + LKPRED_PSEUDO("bnelr", ExtendedGekkoMnemonic::Bnelr, ParseAlg::NoneOrOp1), + LKPRED_PSEUDO("bnectr", ExtendedGekkoMnemonic::Bnectr, ParseAlg::NoneOrOp1), + LKPRED_PSEUDO("bnglr", ExtendedGekkoMnemonic::Bnglr, ParseAlg::NoneOrOp1), + LKPRED_PSEUDO("bngctr", ExtendedGekkoMnemonic::Bngctr, ParseAlg::NoneOrOp1), + LKPRED_PSEUDO("bsolr", ExtendedGekkoMnemonic::Bsolr, ParseAlg::NoneOrOp1), + LKPRED_PSEUDO("bsoctr", ExtendedGekkoMnemonic::Bsoctr, ParseAlg::NoneOrOp1), + LKPRED_PSEUDO("bnslr", ExtendedGekkoMnemonic::Bnslr, ParseAlg::NoneOrOp1), + LKPRED_PSEUDO("bnsctr", ExtendedGekkoMnemonic::Bnsctr, ParseAlg::NoneOrOp1), + LKPRED_PSEUDO("bunlr", ExtendedGekkoMnemonic::Bunlr, ParseAlg::NoneOrOp1), + LKPRED_PSEUDO("bunctr", ExtendedGekkoMnemonic::Bunctr, ParseAlg::NoneOrOp1), + LKPRED_PSEUDO("bnulr", ExtendedGekkoMnemonic::Bnulr, ParseAlg::NoneOrOp1), + LKPRED_PSEUDO("bnuctr", ExtendedGekkoMnemonic::Bnuctr, ParseAlg::NoneOrOp1), + PLAIN_PSEUDO("crset", ExtendedGekkoMnemonic::Crset, ParseAlg::Op1), + PLAIN_PSEUDO("crclr", ExtendedGekkoMnemonic::Crclr, ParseAlg::Op1), + PLAIN_PSEUDO("crmove", ExtendedGekkoMnemonic::Crmove, ParseAlg::Op2), + PLAIN_PSEUDO("crnot", ExtendedGekkoMnemonic::Crnot, ParseAlg::Op2), + PLAIN_PSEUDO("twlt", ExtendedGekkoMnemonic::Twlt, ParseAlg::Op2), + PLAIN_PSEUDO("twlti", ExtendedGekkoMnemonic::Twlti, ParseAlg::Op2), + PLAIN_PSEUDO("twle", ExtendedGekkoMnemonic::Twle, ParseAlg::Op2), + PLAIN_PSEUDO("twlei", ExtendedGekkoMnemonic::Twlei, ParseAlg::Op2), + PLAIN_PSEUDO("tweq", ExtendedGekkoMnemonic::Tweq, ParseAlg::Op2), + PLAIN_PSEUDO("tweqi", ExtendedGekkoMnemonic::Tweqi, ParseAlg::Op2), + PLAIN_PSEUDO("twge", ExtendedGekkoMnemonic::Twge, ParseAlg::Op2), + PLAIN_PSEUDO("twgei", ExtendedGekkoMnemonic::Twgei, ParseAlg::Op2), + PLAIN_PSEUDO("twgt", ExtendedGekkoMnemonic::Twgt, ParseAlg::Op2), + PLAIN_PSEUDO("twgti", ExtendedGekkoMnemonic::Twgti, ParseAlg::Op2), + PLAIN_PSEUDO("twnl", ExtendedGekkoMnemonic::Twnl, ParseAlg::Op2), + PLAIN_PSEUDO("twnli", ExtendedGekkoMnemonic::Twnli, ParseAlg::Op2), + PLAIN_PSEUDO("twne", ExtendedGekkoMnemonic::Twne, ParseAlg::Op2), + PLAIN_PSEUDO("twnei", ExtendedGekkoMnemonic::Twnei, ParseAlg::Op2), + PLAIN_PSEUDO("twng", ExtendedGekkoMnemonic::Twng, ParseAlg::Op2), + PLAIN_PSEUDO("twngi", ExtendedGekkoMnemonic::Twngi, ParseAlg::Op2), + PLAIN_PSEUDO("twllt", ExtendedGekkoMnemonic::Twllt, ParseAlg::Op2), + PLAIN_PSEUDO("twllti", ExtendedGekkoMnemonic::Twllti, ParseAlg::Op2), + PLAIN_PSEUDO("twlle", ExtendedGekkoMnemonic::Twlle, ParseAlg::Op2), + PLAIN_PSEUDO("twllei", ExtendedGekkoMnemonic::Twllei, ParseAlg::Op2), + PLAIN_PSEUDO("twlge", ExtendedGekkoMnemonic::Twlge, ParseAlg::Op2), + PLAIN_PSEUDO("twlgei", ExtendedGekkoMnemonic::Twlgei, ParseAlg::Op2), + PLAIN_PSEUDO("twlgt", ExtendedGekkoMnemonic::Twlgt, ParseAlg::Op2), + PLAIN_PSEUDO("twlgti", ExtendedGekkoMnemonic::Twlgti, ParseAlg::Op2), + PLAIN_PSEUDO("twlnl", ExtendedGekkoMnemonic::Twlnl, ParseAlg::Op2), + PLAIN_PSEUDO("twlnli", ExtendedGekkoMnemonic::Twlnli, ParseAlg::Op2), + PLAIN_PSEUDO("twlng", ExtendedGekkoMnemonic::Twlng, ParseAlg::Op2), + PLAIN_PSEUDO("twlngi", ExtendedGekkoMnemonic::Twlngi, ParseAlg::Op2), + PLAIN_PSEUDO("trap", ExtendedGekkoMnemonic::Trap, ParseAlg::None), + PLAIN_PSEUDO("mtxer", ExtendedGekkoMnemonic::Mtxer, ParseAlg::Op1), + PLAIN_PSEUDO("mfxer", ExtendedGekkoMnemonic::Mfxer, ParseAlg::Op1), + PLAIN_PSEUDO("mtlr", ExtendedGekkoMnemonic::Mtlr, ParseAlg::Op1), + PLAIN_PSEUDO("mflr", ExtendedGekkoMnemonic::Mflr, ParseAlg::Op1), + PLAIN_PSEUDO("mtctr", ExtendedGekkoMnemonic::Mtctr, ParseAlg::Op1), + PLAIN_PSEUDO("mfctr", ExtendedGekkoMnemonic::Mfctr, ParseAlg::Op1), + PLAIN_PSEUDO("mtdsisr", ExtendedGekkoMnemonic::Mtdsisr, ParseAlg::Op1), + PLAIN_PSEUDO("mfdsisr", ExtendedGekkoMnemonic::Mfdsisr, ParseAlg::Op1), + PLAIN_PSEUDO("mtdar", ExtendedGekkoMnemonic::Mtdar, ParseAlg::Op1), + PLAIN_PSEUDO("mfdar", ExtendedGekkoMnemonic::Mfdar, ParseAlg::Op1), + PLAIN_PSEUDO("mtdec", ExtendedGekkoMnemonic::Mtdec, ParseAlg::Op1), + PLAIN_PSEUDO("mfdec", ExtendedGekkoMnemonic::Mfdec, ParseAlg::Op1), + PLAIN_PSEUDO("mtsdr1", ExtendedGekkoMnemonic::Mtsdr1, ParseAlg::Op1), + PLAIN_PSEUDO("mfsdr1", ExtendedGekkoMnemonic::Mfsdr1, ParseAlg::Op1), + PLAIN_PSEUDO("mtsrr0", ExtendedGekkoMnemonic::Mtsrr0, ParseAlg::Op1), + PLAIN_PSEUDO("mfsrr0", ExtendedGekkoMnemonic::Mfsrr0, ParseAlg::Op1), + PLAIN_PSEUDO("mtsrr1", ExtendedGekkoMnemonic::Mtsrr1, ParseAlg::Op1), + PLAIN_PSEUDO("mfsrr1", ExtendedGekkoMnemonic::Mfsrr1, ParseAlg::Op1), + PLAIN_PSEUDO("mtasr", ExtendedGekkoMnemonic::Mtasr, ParseAlg::Op1), + PLAIN_PSEUDO("mfasr", ExtendedGekkoMnemonic::Mfasr, ParseAlg::Op1), + PLAIN_PSEUDO("mtear", ExtendedGekkoMnemonic::Mtear, ParseAlg::Op1), + PLAIN_PSEUDO("mfear", ExtendedGekkoMnemonic::Mfear, ParseAlg::Op1), + PLAIN_PSEUDO("mttbl", ExtendedGekkoMnemonic::Mttbl, ParseAlg::Op1), + PLAIN_PSEUDO("mftbl", ExtendedGekkoMnemonic::Mftbl, ParseAlg::Op1), + PLAIN_PSEUDO("mttbu", ExtendedGekkoMnemonic::Mttbu, ParseAlg::Op1), + PLAIN_PSEUDO("mftbu", ExtendedGekkoMnemonic::Mftbu, ParseAlg::Op1), + PLAIN_PSEUDO("mtsprg", ExtendedGekkoMnemonic::Mtsprg, ParseAlg::Op2), + PLAIN_PSEUDO("mfsprg", ExtendedGekkoMnemonic::Mfsprg, ParseAlg::Op2), + PLAIN_PSEUDO("mtibatu", ExtendedGekkoMnemonic::Mtibatu, ParseAlg::Op2), + PLAIN_PSEUDO("mfibatu", ExtendedGekkoMnemonic::Mfibatu, ParseAlg::Op2), + PLAIN_PSEUDO("mtibatl", ExtendedGekkoMnemonic::Mtibatl, ParseAlg::Op2), + PLAIN_PSEUDO("mfibatl", ExtendedGekkoMnemonic::Mfibatl, ParseAlg::Op2), + PLAIN_PSEUDO("mtdbatu", ExtendedGekkoMnemonic::Mtdbatu, ParseAlg::Op2), + PLAIN_PSEUDO("mfdbatu", ExtendedGekkoMnemonic::Mfdbatu, ParseAlg::Op2), + PLAIN_PSEUDO("mtdbatl", ExtendedGekkoMnemonic::Mtdbatl, ParseAlg::Op2), + PLAIN_PSEUDO("mfdbatl", ExtendedGekkoMnemonic::Mfdbatl, ParseAlg::Op2), + PLAIN_PSEUDO("nop", ExtendedGekkoMnemonic::Nop, ParseAlg::None), + PLAIN_PSEUDO("li", ExtendedGekkoMnemonic::Li, ParseAlg::Op2), + PLAIN_PSEUDO("lis", ExtendedGekkoMnemonic::Lis, ParseAlg::Op2), + PLAIN_PSEUDO("la", ExtendedGekkoMnemonic::La, ParseAlg::Op1Off1), + RC_PSEUDO("mr", ExtendedGekkoMnemonic::Mr, ParseAlg::Op2), + RC_PSEUDO("not", ExtendedGekkoMnemonic::Not, ParseAlg::Op2), + PLAIN_PSEUDO("mtcr", ExtendedGekkoMnemonic::Mtcr, ParseAlg::Op1), + PLAIN_PSEUDO("mfspr", ExtendedGekkoMnemonic::Mfspr, ParseAlg::Op2), + PLAIN_PSEUDO("mftb", ExtendedGekkoMnemonic::Mftb, ParseAlg::Op2), + PLAIN_PSEUDO("mtspr", ExtendedGekkoMnemonic::Mtspr, ParseAlg::Op2), +}; + +#undef MNEMONIC +#undef PLAIN_MNEMONIC +#undef RC_MNEMONIC +#undef OERC_MNEMONIC +#undef LK_MNEMONIC +#undef AALK_MNEMONIC +#undef PSEUDO +#undef PLAIN_PSEUDO +#undef RC_PSEUDO +#undef OERC_PSEUDO +#undef LK_PSEUDO +#undef LKAA_PSEUDO +#undef LKPRED_PSEUDO +#undef LKAAPRED_PSEUDO + +////////////////////// +// ASSEMBLER TABLES // +////////////////////// +#define EMIT_MNEMONIC_ENTRY(opcode_val, extra_bits, ...) \ + MnemonicDesc \ + { \ + InsertOpcode(opcode_val) | (extra_bits), \ + static_cast<u32>(std::initializer_list<OperandDesc>{__VA_ARGS__}.size()), \ + { \ + __VA_ARGS__ \ + } \ + } +#define MNEMONIC(opcode_val, extra_bits, ...) \ + EMIT_MNEMONIC_ENTRY(opcode_val, extra_bits, __VA_ARGS__), INVALID_MNEMONIC, INVALID_MNEMONIC, \ + INVALID_MNEMONIC +#define BASIC_MNEMONIC(opcode_val, ...) MNEMONIC(opcode_val, 0, __VA_ARGS__) +#define RC_MNEMONIC(opcode_val, extra_bits, ...) \ + EMIT_MNEMONIC_ENTRY(opcode_val, extra_bits, __VA_ARGS__), \ + EMIT_MNEMONIC_ENTRY(opcode_val, ((extra_bits) | InsertVal(1, 31, 31)), __VA_ARGS__), \ + INVALID_MNEMONIC, INVALID_MNEMONIC +#define OERC_MNEMONIC(opcode_val, extra_bits, ...) \ + EMIT_MNEMONIC_ENTRY(opcode_val, extra_bits, __VA_ARGS__), \ + EMIT_MNEMONIC_ENTRY(opcode_val, ((extra_bits) | InsertVal(1, 31, 31)), __VA_ARGS__), \ + EMIT_MNEMONIC_ENTRY(opcode_val, ((extra_bits) | InsertVal(1, 21, 21)), __VA_ARGS__), \ + EMIT_MNEMONIC_ENTRY( \ + opcode_val, ((extra_bits) | InsertVal(1, 31, 31) | InsertVal(1, 21, 21)), __VA_ARGS__) +#define LK_MNEMONIC(opcode_val, extra_bits, ...) \ + EMIT_MNEMONIC_ENTRY(opcode_val, extra_bits, __VA_ARGS__), \ + EMIT_MNEMONIC_ENTRY(opcode_val, ((extra_bits) | InsertVal(1, 31, 31)), __VA_ARGS__), \ + INVALID_MNEMONIC, INVALID_MNEMONIC +#define AALK_MNEMONIC(opcode_val, extra_bits, ...) \ + EMIT_MNEMONIC_ENTRY(opcode_val, extra_bits, __VA_ARGS__), \ + EMIT_MNEMONIC_ENTRY(opcode_val, ((extra_bits) | InsertVal(0b01, 30, 31)), __VA_ARGS__), \ + EMIT_MNEMONIC_ENTRY(opcode_val, ((extra_bits) | InsertVal(0b10, 30, 31)), __VA_ARGS__), \ + EMIT_MNEMONIC_ENTRY(opcode_val, ((extra_bits) | InsertVal(0b11, 30, 31)), __VA_ARGS__) + +// Defines all basic mnemonics that Broadway/Gekko supports +extern const std::array<MnemonicDesc, NUM_MNEMONICS* VARIANT_PERMUTATIONS> mnemonics = { + // A-2 + OERC_MNEMONIC(31, InsertVal(266, 22, 30), _D, _A, _B), // add + OERC_MNEMONIC(31, InsertVal(10, 22, 30), _D, _A, _B), // addc + OERC_MNEMONIC(31, InsertVal(138, 22, 30), _D, _A, _B), // adde + BASIC_MNEMONIC(14, _D, _A, _SIMM), // addi + BASIC_MNEMONIC(12, _D, _A, _SIMM), // addic + BASIC_MNEMONIC(13, _D, _A, _SIMM), // addic. + BASIC_MNEMONIC(15, _D, _A, _SIMM), // addis + OERC_MNEMONIC(31, InsertVal(234, 22, 30), _D, _A), // addme + OERC_MNEMONIC(31, InsertVal(202, 22, 30), _D, _A), // addze + OERC_MNEMONIC(31, InsertVal(491, 22, 30), _D, _A, _B), // divw + OERC_MNEMONIC(31, InsertVal(459, 22, 30), _D, _A, _B), // divwu + RC_MNEMONIC(31, InsertVal(75, 22, 30), _D, _A, _B), // mulhw + RC_MNEMONIC(31, InsertVal(11, 22, 30), _D, _A, _B), // mulhwu + BASIC_MNEMONIC(7, _D, _A, _SIMM), // mulli + OERC_MNEMONIC(31, InsertVal(235, 22, 30), _D, _A, _B), // mullw + OERC_MNEMONIC(31, InsertVal(104, 22, 30), _D, _A), // neg + OERC_MNEMONIC(31, InsertVal(40, 22, 30), _D, _A, _B), // subf + OERC_MNEMONIC(31, InsertVal(8, 22, 30), _D, _A, _B), // subfc + OERC_MNEMONIC(31, InsertVal(136, 22, 30), _D, _A, _B), // subfe + BASIC_MNEMONIC(8, _D, _A, _SIMM), // subfic + OERC_MNEMONIC(31, InsertVal(232, 22, 30), _D, _A), // subfme + OERC_MNEMONIC(31, InsertVal(200, 22, 30), _D, _A), // subfze + + // A-3 + MNEMONIC(31, InsertVal(0, 21, 30), _Crfd, _L, _A, _B), // cmp + BASIC_MNEMONIC(11, _Crfd, _L, _A, _SIMM), // cmpi + MNEMONIC(31, InsertVal(32, 21, 30), _Crfd, _L, _A, _B), // cmpl + BASIC_MNEMONIC(10, _Crfd, _L, _A, _UIMM), // cmpli + + // A-4 + RC_MNEMONIC(31, InsertVal(28, 21, 30), _A, _S, _B), // and + RC_MNEMONIC(31, InsertVal(60, 21, 30), _A, _S, _B), // andc + BASIC_MNEMONIC(28, _A, _S, _UIMM), // andi. + BASIC_MNEMONIC(29, _A, _S, _UIMM), // andis. + RC_MNEMONIC(31, InsertVal(26, 21, 30), _A, _S), // cntlzw + RC_MNEMONIC(31, InsertVal(284, 21, 30), _A, _S, _B), // eqv + RC_MNEMONIC(31, InsertVal(954, 21, 30), _A, _S), // extsb + RC_MNEMONIC(31, InsertVal(922, 21, 30), _A, _S), // extsh + RC_MNEMONIC(31, InsertVal(476, 21, 30), _A, _S, _B), // nand + RC_MNEMONIC(31, InsertVal(124, 21, 30), _A, _S, _B), // nor + RC_MNEMONIC(31, InsertVal(444, 21, 30), _A, _S, _B), // or + RC_MNEMONIC(31, InsertVal(412, 21, 30), _A, _S, _B), // orc + BASIC_MNEMONIC(24, _A, _S, _UIMM), // ori + BASIC_MNEMONIC(25, _A, _S, _UIMM), // oris + RC_MNEMONIC(31, InsertVal(316, 21, 30), _A, _S, _B), // xor + BASIC_MNEMONIC(26, _A, _S, _UIMM), // xori + BASIC_MNEMONIC(27, _A, _S, _UIMM), // xoris + + // A-5 + RC_MNEMONIC(20, 0, _A, _S, _SH, _MB, _ME), // rlwimi + RC_MNEMONIC(21, 0, _A, _S, _SH, _MB, _ME), // rlwinm + RC_MNEMONIC(23, 0, _A, _S, _B, _MB, _ME), // rlwnm + + // A-6 + RC_MNEMONIC(31, InsertVal(24, 21, 30), _A, _S, _B), // slw + RC_MNEMONIC(31, InsertVal(792, 21, 30), _A, _S, _B), // sraw + RC_MNEMONIC(31, InsertVal(824, 21, 30), _A, _S, _SH), // srawi + RC_MNEMONIC(31, InsertVal(536, 21, 30), _A, _S, _B), // srw + + // A-7 + RC_MNEMONIC(63, InsertVal(21, 26, 30), _D, _A, _B), // fadd + RC_MNEMONIC(59, InsertVal(21, 26, 30), _D, _A, _B), // fadds + RC_MNEMONIC(63, InsertVal(18, 26, 30), _D, _A, _B), // fdiv + RC_MNEMONIC(59, InsertVal(18, 26, 30), _D, _A, _B), // fdivs + RC_MNEMONIC(63, InsertVal(25, 26, 30), _D, _A, _C), // fmul + RC_MNEMONIC(59, InsertVal(25, 26, 30), _D, _A, _C), // fmuls + RC_MNEMONIC(59, InsertVal(24, 26, 30), _D, _B), // fres + RC_MNEMONIC(63, InsertVal(26, 26, 30), _D, _B), // frsqrte + RC_MNEMONIC(63, InsertVal(20, 26, 30), _D, _A, _B), // fsub + RC_MNEMONIC(59, InsertVal(20, 26, 30), _D, _A, _B), // fsubs + RC_MNEMONIC(63, InsertVal(23, 26, 30), _D, _A, _C, _B), // fsel + + // A-8 + RC_MNEMONIC(63, InsertVal(29, 26, 30), _D, _A, _C, _B), // fmadd + RC_MNEMONIC(59, InsertVal(29, 26, 30), _D, _A, _C, _B), // fmadds + RC_MNEMONIC(63, InsertVal(28, 26, 30), _D, _A, _C, _B), // fmsub + RC_MNEMONIC(59, InsertVal(28, 26, 30), _D, _A, _C, _B), // fmsubs + RC_MNEMONIC(63, InsertVal(31, 26, 30), _D, _A, _C, _B), // fnmadd + RC_MNEMONIC(59, InsertVal(31, 26, 30), _D, _A, _C, _B), // fnmadds + RC_MNEMONIC(63, InsertVal(30, 26, 30), _D, _A, _C, _B), // fnmsub + RC_MNEMONIC(59, InsertVal(30, 26, 30), _D, _A, _C, _B), // fnmsubs + + // A-9 + RC_MNEMONIC(63, InsertVal(14, 21, 30), _D, _B), // fctiw + RC_MNEMONIC(63, InsertVal(15, 21, 30), _D, _B), // fctiwz + RC_MNEMONIC(63, InsertVal(12, 21, 30), _D, _B), // frsp + + // A-10 + MNEMONIC(63, InsertVal(32, 21, 30), _Crfd, _A, _B), // fcmpo + MNEMONIC(63, InsertVal(0, 21, 30), _Crfd, _A, _B), // fcmpu + + // A-11 + MNEMONIC(63, InsertVal(64, 21, 30), _Crfd, _Crfs), // mcrfs + RC_MNEMONIC(63, InsertVal(583, 21, 30), _D), // mffs + RC_MNEMONIC(63, InsertVal(70, 21, 30), _Crbd), // mtfsb0 + RC_MNEMONIC(63, InsertVal(38, 21, 30), _Crbd), // mtfsb1 + RC_MNEMONIC(63, InsertVal(711, 21, 30), _FM, _B), // mtfsf + RC_MNEMONIC(63, InsertVal(134, 21, 30), _Crfd, _IMM), // mtfsfi + + // A-12 + BASIC_MNEMONIC(34, _D, _Offd, _A), // lbz + BASIC_MNEMONIC(35, _D, _Offd, _A), // lbzu + MNEMONIC(31, InsertVal(119, 21, 30), _D, _A, _B), // lbzux + MNEMONIC(31, InsertVal(87, 21, 30), _D, _A, _B), // lbzx + BASIC_MNEMONIC(42, _D, _Offd, _A), // lha + BASIC_MNEMONIC(43, _D, _Offd, _A), // lhau + MNEMONIC(31, InsertVal(375, 21, 30), _D, _A, _B), // lhaux + MNEMONIC(31, InsertVal(343, 21, 30), _D, _A, _B), // lhax + BASIC_MNEMONIC(40, _D, _Offd, _A), // lhz + BASIC_MNEMONIC(41, _D, _Offd, _A), // lhzu + MNEMONIC(31, InsertVal(311, 21, 30), _D, _A, _B), // lhzux + MNEMONIC(31, InsertVal(279, 21, 30), _D, _A, _B), // lhzx + BASIC_MNEMONIC(32, _D, _Offd, _A), // lwz + BASIC_MNEMONIC(33, _D, _Offd, _A), // lwzu + MNEMONIC(31, InsertVal(55, 21, 30), _D, _A, _B), // lwzux + MNEMONIC(31, InsertVal(23, 21, 30), _D, _A, _B), // lwzx + + // A-13 + BASIC_MNEMONIC(38, _S, _Offd, _A), // stb + BASIC_MNEMONIC(39, _S, _Offd, _A), // stbu + MNEMONIC(31, InsertVal(247, 21, 30), _S, _A, _B), // stbux + MNEMONIC(31, InsertVal(215, 21, 30), _S, _A, _B), // stbx + BASIC_MNEMONIC(44, _S, _Offd, _A), // sth + BASIC_MNEMONIC(45, _S, _Offd, _A), // sthu + MNEMONIC(31, InsertVal(439, 21, 30), _S, _A, _B), // sthux + MNEMONIC(31, InsertVal(407, 21, 30), _S, _A, _B), // sthx + BASIC_MNEMONIC(36, _S, _Offd, _A), // stw + BASIC_MNEMONIC(37, _S, _Offd, _A), // stwu + MNEMONIC(31, InsertVal(183, 21, 30), _S, _A, _B), // stwux + MNEMONIC(31, InsertVal(151, 21, 30), _S, _A, _B), // stwx + + // A-14 + MNEMONIC(31, InsertVal(790, 21, 30), _D, _A, _B), // lhbrx + MNEMONIC(31, InsertVal(534, 21, 30), _D, _A, _B), // lwbrx + MNEMONIC(31, InsertVal(918, 21, 30), _S, _A, _B), // sthbrx + MNEMONIC(31, InsertVal(662, 21, 30), _S, _A, _B), // stwbrx + + // A-15 + BASIC_MNEMONIC(46, _D, _Offd, _A), // lmw + BASIC_MNEMONIC(47, _S, _Offd, _A), // stmw + + // A-16 + MNEMONIC(31, InsertVal(597, 21, 30), _D, _A, _NB), // lswi + MNEMONIC(31, InsertVal(533, 21, 30), _D, _A, _B), // lswx + MNEMONIC(31, InsertVal(725, 21, 30), _S, _A, _NB), // stswi + MNEMONIC(31, InsertVal(661, 21, 30), _S, _A, _B), // stswx + + // A-17 + MNEMONIC(31, InsertVal(854, 21, 30)), // eieio + MNEMONIC(19, InsertVal(150, 21, 30)), // isync + MNEMONIC(31, InsertVal(20, 21, 30), _D, _A, _B), // lwarx + MNEMONIC(31, InsertVal(150, 21, 30) | InsertVal(1, 31, 31), _S, _A, _B), // stwcx. + MNEMONIC(31, InsertVal(598, 21, 30)), // sync + + // A-18 + BASIC_MNEMONIC(50, _D, _Offd, _A), // lfd + BASIC_MNEMONIC(51, _D, _Offd, _A), // lfdu + MNEMONIC(31, InsertVal(631, 21, 30), _D, _A, _B), // lfdux + MNEMONIC(31, InsertVal(599, 21, 30), _D, _A, _B), // lfdx + BASIC_MNEMONIC(48, _D, _Offd, _A), // lfs + BASIC_MNEMONIC(49, _D, _Offd, _A), // lfsu + MNEMONIC(31, InsertVal(567, 21, 30), _D, _A, _B), // lfsux + MNEMONIC(31, InsertVal(535, 21, 30), _D, _A, _B), // lfsx + + // A-19 + BASIC_MNEMONIC(54, _S, _Offd, _A), // stfd + BASIC_MNEMONIC(55, _S, _Offd, _A), // stfdu + MNEMONIC(31, InsertVal(759, 21, 30), _S, _A, _B), // stfdux + MNEMONIC(31, InsertVal(727, 21, 30), _S, _A, _B), // stfdx + MNEMONIC(31, InsertVal(983, 21, 30), _S, _A, _B), // stfiwx + BASIC_MNEMONIC(52, _S, _Offd, _A), // stfs + BASIC_MNEMONIC(53, _S, _Offd, _A), // stfsu + MNEMONIC(31, InsertVal(695, 21, 30), _S, _A, _B), // stfsux + MNEMONIC(31, InsertVal(663, 21, 30), _S, _A, _B), // stfsx + + // A-20 + RC_MNEMONIC(63, InsertVal(264, 21, 30), _D, _B), // fabs + RC_MNEMONIC(63, InsertVal(72, 21, 30), _D, _B), // fmr + RC_MNEMONIC(63, InsertVal(136, 21, 30), _D, _B), // fnabs + RC_MNEMONIC(63, InsertVal(40, 21, 30), _D, _B), // fneg + + // A-21 + AALK_MNEMONIC(18, 0, _LI), // b + AALK_MNEMONIC(16, 0, _BO, _BI, _BD), // bc + LK_MNEMONIC(19, InsertVal(528, 21, 30), _BO, _BI), // bcctr + LK_MNEMONIC(19, InsertVal(16, 21, 30), _BO, _BI), // bclr + + // A-22 + MNEMONIC(19, InsertVal(257, 21, 30), _Crbd, _Crba, _Crbb), // crand + MNEMONIC(19, InsertVal(129, 21, 30), _Crbd, _Crba, _Crbb), // crandc + MNEMONIC(19, InsertVal(289, 21, 30), _Crbd, _Crba, _Crbb), // creqv + MNEMONIC(19, InsertVal(225, 21, 30), _Crbd, _Crba, _Crbb), // crnand + MNEMONIC(19, InsertVal(33, 21, 30), _Crbd, _Crba, _Crbb), // crnor + MNEMONIC(19, InsertVal(449, 21, 30), _Crbd, _Crba, _Crbb), // cror + MNEMONIC(19, InsertVal(417, 21, 30), _Crbd, _Crba, _Crbb), // crorc + MNEMONIC(19, InsertVal(193, 21, 30), _Crbd, _Crba, _Crbb), // crxor + MNEMONIC(19, InsertVal(0, 21, 30), _Crfd, _Crfs), // mcrf + + // A-23 + MNEMONIC(19, InsertVal(50, 21, 30)), // rfi + MNEMONIC(17, InsertVal(1, 30, 30)), // sc + + // A-24 + MNEMONIC(31, InsertVal(4, 21, 30), _TO, _A, _B), // tw + BASIC_MNEMONIC(3, _TO, _A, _SIMM), // twi + + // A-25 + MNEMONIC(31, InsertVal(512, 21, 30), _Crfd), // mcrxr + MNEMONIC(31, InsertVal(19, 21, 30), _D), // mfcr + MNEMONIC(31, InsertVal(83, 21, 30), _D), // mfmsr + MNEMONIC(31, InsertVal(339, 21, 30), _D, _SPR), // mfspr + MNEMONIC(31, InsertVal(371, 21, 30), _D, _TPR), // mftb + MNEMONIC(31, InsertVal(144, 21, 30), _CRM, _S), // mtcrf + MNEMONIC(31, InsertVal(146, 21, 30), _S), // mtmsr + MNEMONIC(31, InsertVal(467, 21, 30), _SPR, _D), // mtspr + + // A-26 + MNEMONIC(31, InsertVal(86, 21, 30), _A, _B), // dcbf + MNEMONIC(31, InsertVal(470, 21, 30), _A, _B), // dcbi + MNEMONIC(31, InsertVal(54, 21, 30), _A, _B), // dcbst + MNEMONIC(31, InsertVal(278, 21, 30), _A, _B), // dcbt + MNEMONIC(31, InsertVal(246, 21, 30), _A, _B), // dcbtst + MNEMONIC(31, InsertVal(1014, 21, 30), _A, _B), // dcbz + MNEMONIC(31, InsertVal(982, 21, 30), _A, _B), // icbi + + // A-27 + MNEMONIC(31, InsertVal(595, 21, 30), _D, _SR), // mfsr + MNEMONIC(31, InsertVal(659, 21, 30), _D, _B), // mfsrin + MNEMONIC(31, InsertVal(210, 21, 30), _SR, _S), // mtsr + MNEMONIC(31, InsertVal(242, 21, 30), _S, _B), // mtsrin + + // A-28 + MNEMONIC(31, InsertVal(306, 21, 30), _B), // tlbie + MNEMONIC(31, InsertVal(566, 21, 30)), // tlbsync + + // A-29 + MNEMONIC(31, InsertVal(310, 21, 30), _D, _A, _B), // eciwx + MNEMONIC(31, InsertVal(438, 21, 30), _S, _A, _B), // ecowx + + // A-30 + MNEMONIC(4, InsertVal(6, 25, 30), _D, _A, _B, _W2, _I2), // psq_lx + MNEMONIC(4, InsertVal(7, 25, 30), _S, _A, _B, _W2, _I2), // psq_stx + MNEMONIC(4, InsertVal(38, 25, 30), _D, _A, _B, _W2, _I2), // psq_lux + MNEMONIC(4, InsertVal(39, 25, 30), _S, _A, _B, _W2, _I2), // psq_stux + BASIC_MNEMONIC(56, _D, _OffdPs, _A, _W1, _I1), // psq_l + BASIC_MNEMONIC(57, _D, _OffdPs, _A, _W1, _I1), // psq_lu + BASIC_MNEMONIC(60, _S, _OffdPs, _A, _W1, _I1), // psq_st + BASIC_MNEMONIC(61, _S, _OffdPs, _A, _W1, _I1), // psq_stu + + // A-31 + RC_MNEMONIC(4, InsertVal(18, 26, 30), _D, _A, _B), // ps_div + RC_MNEMONIC(4, InsertVal(20, 26, 30), _D, _A, _B), // ps_sub + RC_MNEMONIC(4, InsertVal(21, 26, 30), _D, _A, _B), // ps_add + RC_MNEMONIC(4, InsertVal(23, 26, 30), _D, _A, _C, _B), // ps_sel + RC_MNEMONIC(4, InsertVal(24, 26, 30), _D, _B), // ps_res + RC_MNEMONIC(4, InsertVal(25, 26, 30), _D, _A, _C), // ps_mul + RC_MNEMONIC(4, InsertVal(26, 26, 30), _D, _B), // ps_rsqrte + RC_MNEMONIC(4, InsertVal(28, 26, 30), _D, _A, _C, _B), // ps_msub + RC_MNEMONIC(4, InsertVal(29, 26, 30), _D, _A, _C, _B), // ps_madd + RC_MNEMONIC(4, InsertVal(30, 26, 30), _D, _A, _C, _B), // ps_nmsub + RC_MNEMONIC(4, InsertVal(31, 26, 30), _D, _A, _C, _B), // ps_nmadd + RC_MNEMONIC(4, InsertVal(40, 21, 30), _D, _B), // ps_neg + RC_MNEMONIC(4, InsertVal(72, 21, 30), _D, _B), // ps_mr + RC_MNEMONIC(4, InsertVal(136, 21, 30), _D, _B), // ps_nabs + RC_MNEMONIC(4, InsertVal(264, 21, 30), _D, _B), // ps_abs + + // A-32 + RC_MNEMONIC(4, InsertVal(10, 26, 30), _D, _A, _C, _B), // ps_sum0 + RC_MNEMONIC(4, InsertVal(11, 26, 30), _D, _A, _C, _B), // ps_sum1 + RC_MNEMONIC(4, InsertVal(12, 26, 30), _D, _A, _C), // ps_muls0 + RC_MNEMONIC(4, InsertVal(13, 26, 30), _D, _A, _C), // ps_muls1 + RC_MNEMONIC(4, InsertVal(14, 26, 30), _D, _A, _C, _B), // ps_madds0 + RC_MNEMONIC(4, InsertVal(15, 26, 30), _D, _A, _C, _B), // ps_madds1 + MNEMONIC(4, InsertVal(0, 21, 30), _Crfd, _A, _B), // ps_cmpu0 + MNEMONIC(4, InsertVal(32, 21, 30), _Crfd, _A, _B), // ps_cmpo0 + MNEMONIC(4, InsertVal(64, 21, 30), _Crfd, _A, _B), // ps_cmpu1 + MNEMONIC(4, InsertVal(96, 21, 30), _Crfd, _A, _B), // ps_cmpo1 + RC_MNEMONIC(4, InsertVal(528, 21, 30), _D, _A, _B), // ps_merge00 + RC_MNEMONIC(4, InsertVal(560, 21, 30), _D, _A, _B), // ps_merge01 + RC_MNEMONIC(4, InsertVal(592, 21, 30), _D, _A, _B), // ps_merge10 + RC_MNEMONIC(4, InsertVal(624, 21, 30), _D, _A, _B), // ps_merge11 + MNEMONIC(4, InsertVal(1014, 21, 30), _A, _B), // dcbz_l +}; + +namespace +{ +// Reused operand translators for extended mnemonics +void NegateSIMM(OperandList& operands) +{ + operands[2] = static_cast<u32>(-static_cast<s32>(operands[2])); +} + +void SwapOps1And2(OperandList& operands) +{ + std::swap(operands[1], operands[2]); +} + +void SetCompareWordMode(OperandList& operands) +{ + if (operands.count == 2) + { + operands.Insert(0, 0); + } + operands.Insert(1, 0); +} + +template <u32 BO, u32 BI> +void FillBOBI(OperandList& operands) +{ + operands.Insert(0, BO); + operands.Insert(1, BI); +} + +template <size_t Idx> +void BitswapIdx(OperandList& operands) +{ + operands[Idx] = SprBitswap(operands[Idx]); +} + +template <u32 BO, u32 Cond, u32 ParamCount> +void FillBOBICond(OperandList& operands) +{ + if (operands.count < ParamCount) + { + operands.Insert(0, 0); + } + operands[0] = (operands[0] << 2) | Cond; + operands.Insert(0, BO); +} + +template <u32 BO> +void FillBO(OperandList& operands) +{ + operands.Insert(0, BO); +} + +template <u32 TO> +void TrapSetTO(OperandList& operands) +{ + operands.Insert(0, TO); +} + +template <u32 SPRG> +void FillMtspr(OperandList& operands) +{ + operands.Insert(0, SPRG); +} + +template <u32 SPRG> +void FillMfspr(OperandList& operands) +{ + operands.Insert(1, SPRG); +} + +template <u32 SPRG> +void FillMtsprBatAndBitswap(OperandList& operands) +{ + operands[0] = SprBitswap(2 * operands[0] + SPRG); +} + +template <u32 SPRG> +void FillMfsprBatAndBitswap(OperandList& operands) +{ + operands[1] = SprBitswap(2 * operands[1] + SPRG); +} +} // namespace + +#define PSEUDO(base, variant_bits, cb) \ + ExtendedMnemonicDesc { static_cast<size_t>(base) * VARIANT_PERMUTATIONS + variant_bits, cb } +#define PLAIN_PSEUDO(base, cb) \ + PSEUDO(base, PLAIN_MNEMONIC, cb), INVALID_EXT_MNEMONIC, INVALID_EXT_MNEMONIC, INVALID_EXT_MNEMONIC +#define RC_PSEUDO(base, cb) \ + PSEUDO(base, PLAIN_MNEMONIC, cb), PSEUDO(base, RECORD_BIT, cb), INVALID_EXT_MNEMONIC, \ + INVALID_EXT_MNEMONIC +#define OERC_PSEUDO(base, cb) \ + PSEUDO(base, PLAIN_MNEMONIC, cb), PSEUDO(base, RECORD_BIT, cb), \ + PSEUDO(base, OVERFLOW_EXCEPTION, cb), PSEUDO(base, (RECORD_BIT | OVERFLOW_EXCEPTION), cb) +#define LK_PSEUDO(base, cb) \ + PSEUDO(base, PLAIN_MNEMONIC, cb), PSEUDO(base, LINK_BIT, cb), INVALID_EXT_MNEMONIC, \ + INVALID_EXT_MNEMONIC +#define LKAA_PSEUDO(base, cb) \ + PSEUDO(base, PLAIN_MNEMONIC, cb), PSEUDO(base, LINK_BIT, cb), \ + PSEUDO(base, ABSOLUTE_ADDRESS_BIT, cb), PSEUDO(base, (LINK_BIT | ABSOLUTE_ADDRESS_BIT), cb) + +extern const std::array<ExtendedMnemonicDesc, NUM_EXT_MNEMONICS* VARIANT_PERMUTATIONS> + extended_mnemonics = { + // E.2.1 + PLAIN_PSEUDO(GekkoMnemonic::Addi, NegateSIMM), // subi + PLAIN_PSEUDO(GekkoMnemonic::Addis, NegateSIMM), // subis + PLAIN_PSEUDO(GekkoMnemonic::Addic, NegateSIMM), // subic + PLAIN_PSEUDO(GekkoMnemonic::AddicDot, NegateSIMM), // subic. + + // E.2.2 + OERC_PSEUDO(GekkoMnemonic::Subf, SwapOps1And2), // sub + OERC_PSEUDO(GekkoMnemonic::Subfc, SwapOps1And2), // subc + + // E.3.2 + PLAIN_PSEUDO(GekkoMnemonic::Cmpi, SetCompareWordMode), // cmpwi + PLAIN_PSEUDO(GekkoMnemonic::Cmp, SetCompareWordMode), // cmpw + PLAIN_PSEUDO(GekkoMnemonic::Cmpli, SetCompareWordMode), // cmplwi + PLAIN_PSEUDO(GekkoMnemonic::Cmpl, SetCompareWordMode), // cmplw + + // E.4.2 + RC_PSEUDO(GekkoMnemonic::Rlwinm, ([](OperandList& operands) { + const u32 n = operands[2], b = operands[3]; + operands[2] = b; + operands[3] = 0; + operands.Insert(4, n - 1); + })), // extlwi + RC_PSEUDO(GekkoMnemonic::Rlwinm, ([](OperandList& operands) { + const u32 n = operands[2], b = operands[3]; + operands[2] = b + n; + operands[3] = 32 - n; + operands.Insert(4, 31); + })), // extrwi + RC_PSEUDO(GekkoMnemonic::Rlwimi, ([](OperandList& operands) { + const u32 n = operands[2], b = operands[3]; + operands[2] = 32 - b; + operands[3] = b; + operands.Insert(4, b + n - 1); + })), // inslwi + RC_PSEUDO(GekkoMnemonic::Rlwimi, ([](OperandList& operands) { + const u32 n = operands[2], b = operands[3]; + operands[2] = 32 - (b + n); + operands[3] = b; + operands.Insert(4, b + n - 1); + })), // insrwi + RC_PSEUDO(GekkoMnemonic::Rlwinm, ([](OperandList& operands) { + operands.Insert(3, 0); + operands.Insert(4, 31); + })), // rotlwi + RC_PSEUDO(GekkoMnemonic::Rlwinm, ([](OperandList& operands) { + const u32 n = operands[2]; + operands[2] = 32 - n; + operands.Insert(3, 0); + operands.Insert(4, 31); + })), // rotrwi + RC_PSEUDO(GekkoMnemonic::Rlwnm, ([](OperandList& operands) { + operands.Insert(3, 0); + operands.Insert(4, 31); + })), // rotlw + RC_PSEUDO(GekkoMnemonic::Rlwinm, ([](OperandList& operands) { + const u32 n = operands[2]; + operands.Insert(3, 0); + operands.Insert(4, 31 - n); + })), // slwi + RC_PSEUDO(GekkoMnemonic::Rlwinm, ([](OperandList& operands) { + const u32 n = operands[2]; + operands[2] = 32 - n; + operands.Insert(3, n); + operands.Insert(4, 31); + })), // srwi + RC_PSEUDO(GekkoMnemonic::Rlwinm, ([](OperandList& operands) { + const u32 n = operands[2]; + operands[2] = 0; + operands.Insert(3, n); + operands.Insert(4, 31); + })), // clrlwi + RC_PSEUDO(GekkoMnemonic::Rlwinm, ([](OperandList& operands) { + const u32 n = operands[2]; + operands[2] = 0; + operands.Insert(3, 0); + operands.Insert(4, 31 - n); + })), // clrrwi + RC_PSEUDO(GekkoMnemonic::Rlwinm, ([](OperandList& operands) { + const u32 b = operands[2], n = operands[3]; + operands[2] = n; + operands[3] = b - n; + operands.Insert(4, 31 - n); + })), // clrlslwi + + // E.5.2 + LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBO<12>)), // bt + LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBO<4>)), // bf + LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBOBI<16, 0>)), // bdnz + LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBO<8>)), // bdnzt + LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBO<0>)), // bdnzf + LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBOBI<18, 0>)), // bdz + LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBO<10>)), // bdzt + LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBO<2>)), // bdzf + LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBO<13>)), // bt+ + LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBO<5>)), // bf+ + LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBOBI<17, 0>)), // bdnz+ + LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBO<9>)), // bdnzt+ + LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBO<1>)), // bdnzf+ + LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBOBI<19, 0>)), // bdz+ + LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBO<11>)), // bdzt+ + LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBO<3>)), // bdzf+ + + LK_PSEUDO(GekkoMnemonic::Bclr, (FillBOBI<20, 0>)), // blr + LK_PSEUDO(GekkoMnemonic::Bclr, (FillBO<12>)), // btlr + LK_PSEUDO(GekkoMnemonic::Bclr, (FillBO<4>)), // bflr + LK_PSEUDO(GekkoMnemonic::Bclr, (FillBOBI<16, 0>)), // bdnzlr + LK_PSEUDO(GekkoMnemonic::Bclr, (FillBO<8>)), // bdnztlr + LK_PSEUDO(GekkoMnemonic::Bclr, (FillBO<0>)), // bdnzflr + LK_PSEUDO(GekkoMnemonic::Bclr, (FillBOBI<18, 0>)), // bdzlr + LK_PSEUDO(GekkoMnemonic::Bclr, (FillBO<10>)), // bdztlr + LK_PSEUDO(GekkoMnemonic::Bclr, (FillBO<2>)), // bdzflr + + LK_PSEUDO(GekkoMnemonic::Bclr, (FillBO<13>)), // btlr+ + LK_PSEUDO(GekkoMnemonic::Bclr, (FillBO<5>)), // bflr+ + LK_PSEUDO(GekkoMnemonic::Bclr, (FillBOBI<17, 0>)), // bdnzlr+ + LK_PSEUDO(GekkoMnemonic::Bclr, (FillBO<9>)), // bdnztlr+ + LK_PSEUDO(GekkoMnemonic::Bclr, (FillBO<1>)), // bdnzflr+ + LK_PSEUDO(GekkoMnemonic::Bclr, (FillBOBI<19, 0>)), // bdzlr+ + LK_PSEUDO(GekkoMnemonic::Bclr, (FillBO<11>)), // bdztlr+ + LK_PSEUDO(GekkoMnemonic::Bclr, (FillBO<3>)), // bdzflr+ + + LK_PSEUDO(GekkoMnemonic::Bcctr, (FillBOBI<20, 0>)), // bctr + LK_PSEUDO(GekkoMnemonic::Bcctr, (FillBO<12>)), // btctr + LK_PSEUDO(GekkoMnemonic::Bcctr, (FillBO<4>)), // bfctr + LK_PSEUDO(GekkoMnemonic::Bcctr, (FillBO<13>)), // btctr+ + LK_PSEUDO(GekkoMnemonic::Bcctr, (FillBO<5>)), // bfctr+ + + // E.5.3 + LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBOBICond<12, 0, 2>)), // blt + LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBOBICond<4, 1, 2>)), // ble + LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBOBICond<12, 2, 2>)), // beq + LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBOBICond<4, 0, 2>)), // bge + LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBOBICond<12, 1, 2>)), // bgt + LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBOBICond<4, 0, 2>)), // bnl + LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBOBICond<4, 2, 2>)), // bne + LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBOBICond<4, 1, 2>)), // bng + LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBOBICond<12, 3, 2>)), // bso + LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBOBICond<4, 3, 2>)), // bns + LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBOBICond<12, 3, 2>)), // bun + LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBOBICond<4, 3, 2>)), // bnu + + LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBOBICond<13, 0, 2>)), // blt+ + LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBOBICond<5, 1, 2>)), // ble+ + LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBOBICond<13, 2, 2>)), // beq+ + LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBOBICond<5, 0, 2>)), // bge+ + LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBOBICond<13, 1, 2>)), // bgt+ + LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBOBICond<5, 0, 2>)), // bnl+ + LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBOBICond<5, 2, 2>)), // bne+ + LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBOBICond<5, 1, 2>)), // bng+ + LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBOBICond<13, 3, 2>)), // bso+ + LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBOBICond<5, 3, 2>)), // bns+ + LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBOBICond<13, 3, 2>)), // bun+ + LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBOBICond<5, 3, 2>)), // bnu+ + + LK_PSEUDO(GekkoMnemonic::Bclr, (FillBOBICond<12, 0, 1>)), // bltlr + LK_PSEUDO(GekkoMnemonic::Bclr, (FillBOBICond<4, 1, 1>)), // blelr + LK_PSEUDO(GekkoMnemonic::Bclr, (FillBOBICond<12, 2, 1>)), // beqlr + LK_PSEUDO(GekkoMnemonic::Bclr, (FillBOBICond<4, 0, 1>)), // bgelr + LK_PSEUDO(GekkoMnemonic::Bclr, (FillBOBICond<12, 1, 1>)), // bgtlr + LK_PSEUDO(GekkoMnemonic::Bclr, (FillBOBICond<4, 0, 1>)), // bnllr + LK_PSEUDO(GekkoMnemonic::Bclr, (FillBOBICond<4, 2, 1>)), // bnelr + LK_PSEUDO(GekkoMnemonic::Bclr, (FillBOBICond<4, 1, 1>)), // bnglr + LK_PSEUDO(GekkoMnemonic::Bclr, (FillBOBICond<12, 3, 1>)), // bsolr + LK_PSEUDO(GekkoMnemonic::Bclr, (FillBOBICond<4, 3, 1>)), // bnslr + LK_PSEUDO(GekkoMnemonic::Bclr, (FillBOBICond<12, 3, 1>)), // bunlr + LK_PSEUDO(GekkoMnemonic::Bclr, (FillBOBICond<4, 3, 1>)), // bnulr + + LK_PSEUDO(GekkoMnemonic::Bclr, (FillBOBICond<13, 0, 1>)), // bltlr+ + LK_PSEUDO(GekkoMnemonic::Bclr, (FillBOBICond<5, 1, 1>)), // blelr+ + LK_PSEUDO(GekkoMnemonic::Bclr, (FillBOBICond<13, 2, 1>)), // beqlr+ + LK_PSEUDO(GekkoMnemonic::Bclr, (FillBOBICond<5, 0, 1>)), // bgelr+ + LK_PSEUDO(GekkoMnemonic::Bclr, (FillBOBICond<13, 1, 1>)), // bgtlr+ + LK_PSEUDO(GekkoMnemonic::Bclr, (FillBOBICond<5, 0, 1>)), // bnllr+ + LK_PSEUDO(GekkoMnemonic::Bclr, (FillBOBICond<5, 2, 1>)), // bnelr+ + LK_PSEUDO(GekkoMnemonic::Bclr, (FillBOBICond<5, 1, 1>)), // bnglr+ + LK_PSEUDO(GekkoMnemonic::Bclr, (FillBOBICond<13, 3, 1>)), // bsolr+ + LK_PSEUDO(GekkoMnemonic::Bclr, (FillBOBICond<5, 3, 1>)), // bnslr+ + LK_PSEUDO(GekkoMnemonic::Bclr, (FillBOBICond<13, 3, 1>)), // bunlr+ + LK_PSEUDO(GekkoMnemonic::Bclr, (FillBOBICond<5, 3, 1>)), // bnulr+ + + LK_PSEUDO(GekkoMnemonic::Bcctr, (FillBOBICond<12, 0, 1>)), // bltctr + LK_PSEUDO(GekkoMnemonic::Bcctr, (FillBOBICond<4, 1, 1>)), // blectr + LK_PSEUDO(GekkoMnemonic::Bcctr, (FillBOBICond<12, 2, 1>)), // beqctr + LK_PSEUDO(GekkoMnemonic::Bcctr, (FillBOBICond<4, 0, 1>)), // bgectr + LK_PSEUDO(GekkoMnemonic::Bcctr, (FillBOBICond<12, 1, 1>)), // bgtctr + LK_PSEUDO(GekkoMnemonic::Bcctr, (FillBOBICond<4, 0, 1>)), // bnlctr + LK_PSEUDO(GekkoMnemonic::Bcctr, (FillBOBICond<4, 2, 1>)), // bnectr + LK_PSEUDO(GekkoMnemonic::Bcctr, (FillBOBICond<4, 1, 1>)), // bngctr + LK_PSEUDO(GekkoMnemonic::Bcctr, (FillBOBICond<12, 3, 1>)), // bsoctr + LK_PSEUDO(GekkoMnemonic::Bcctr, (FillBOBICond<4, 3, 1>)), // bnsctr + LK_PSEUDO(GekkoMnemonic::Bcctr, (FillBOBICond<12, 3, 1>)), // bunctr + LK_PSEUDO(GekkoMnemonic::Bcctr, (FillBOBICond<4, 3, 1>)), // bnuctr + + LK_PSEUDO(GekkoMnemonic::Bcctr, (FillBOBICond<13, 0, 1>)), // bltctr+ + LK_PSEUDO(GekkoMnemonic::Bcctr, (FillBOBICond<5, 1, 1>)), // blectr+ + LK_PSEUDO(GekkoMnemonic::Bcctr, (FillBOBICond<13, 2, 1>)), // beqctr+ + LK_PSEUDO(GekkoMnemonic::Bcctr, (FillBOBICond<5, 0, 1>)), // bgectr+ + LK_PSEUDO(GekkoMnemonic::Bcctr, (FillBOBICond<13, 1, 1>)), // bgtctr+ + LK_PSEUDO(GekkoMnemonic::Bcctr, (FillBOBICond<5, 0, 1>)), // bnlctr+ + LK_PSEUDO(GekkoMnemonic::Bcctr, (FillBOBICond<5, 2, 1>)), // bnectr+ + LK_PSEUDO(GekkoMnemonic::Bcctr, (FillBOBICond<5, 1, 1>)), // bngctr+ + LK_PSEUDO(GekkoMnemonic::Bcctr, (FillBOBICond<13, 3, 1>)), // bsoctr+ + LK_PSEUDO(GekkoMnemonic::Bcctr, (FillBOBICond<5, 3, 1>)), // bnsctr+ + LK_PSEUDO(GekkoMnemonic::Bcctr, (FillBOBICond<13, 3, 1>)), // bunctr+ + LK_PSEUDO(GekkoMnemonic::Bcctr, (FillBOBICond<5, 3, 1>)), // bnuctr+ + + // E.6 + PLAIN_PSEUDO(GekkoMnemonic::Creqv, + [](OperandList& operands) { + operands.Insert(1, operands[0]); + operands.Insert(2, operands[0]); + }), // crset + PLAIN_PSEUDO(GekkoMnemonic::Crxor, + [](OperandList& operands) { + operands.Insert(1, operands[0]); + operands.Insert(2, operands[0]); + }), // crclr + PLAIN_PSEUDO(GekkoMnemonic::Cror, + [](OperandList& operands) { operands.Insert(2, operands[1]); }), // crmove + PLAIN_PSEUDO(GekkoMnemonic::Crnor, + [](OperandList& operands) { operands.Insert(2, operands[1]); }), // crnot + + // E.7 + PLAIN_PSEUDO(GekkoMnemonic::Tw, TrapSetTO<16>), // twlt + PLAIN_PSEUDO(GekkoMnemonic::Twi, TrapSetTO<16>), // twlti + PLAIN_PSEUDO(GekkoMnemonic::Tw, TrapSetTO<20>), // twle + PLAIN_PSEUDO(GekkoMnemonic::Twi, TrapSetTO<20>), // twlei + PLAIN_PSEUDO(GekkoMnemonic::Tw, TrapSetTO<4>), // tweq + PLAIN_PSEUDO(GekkoMnemonic::Twi, TrapSetTO<4>), // tweqi + PLAIN_PSEUDO(GekkoMnemonic::Tw, TrapSetTO<12>), // twge + PLAIN_PSEUDO(GekkoMnemonic::Twi, TrapSetTO<12>), // twgei + PLAIN_PSEUDO(GekkoMnemonic::Tw, TrapSetTO<8>), // twgt + PLAIN_PSEUDO(GekkoMnemonic::Twi, TrapSetTO<8>), // twgti + PLAIN_PSEUDO(GekkoMnemonic::Tw, TrapSetTO<12>), // twnl + PLAIN_PSEUDO(GekkoMnemonic::Twi, TrapSetTO<12>), // twnli + PLAIN_PSEUDO(GekkoMnemonic::Tw, TrapSetTO<24>), // twne + PLAIN_PSEUDO(GekkoMnemonic::Twi, TrapSetTO<24>), // twnei + PLAIN_PSEUDO(GekkoMnemonic::Tw, TrapSetTO<20>), // twng + PLAIN_PSEUDO(GekkoMnemonic::Twi, TrapSetTO<20>), // twngi + PLAIN_PSEUDO(GekkoMnemonic::Tw, TrapSetTO<2>), // twllt + PLAIN_PSEUDO(GekkoMnemonic::Twi, TrapSetTO<2>), // twllti + PLAIN_PSEUDO(GekkoMnemonic::Tw, TrapSetTO<6>), // twlle + PLAIN_PSEUDO(GekkoMnemonic::Twi, TrapSetTO<6>), // twllei + PLAIN_PSEUDO(GekkoMnemonic::Tw, TrapSetTO<5>), // twlge + PLAIN_PSEUDO(GekkoMnemonic::Twi, TrapSetTO<5>), // twlgei + PLAIN_PSEUDO(GekkoMnemonic::Tw, TrapSetTO<1>), // twlgt + PLAIN_PSEUDO(GekkoMnemonic::Twi, TrapSetTO<1>), // twlgti + PLAIN_PSEUDO(GekkoMnemonic::Tw, TrapSetTO<5>), // twlnl + PLAIN_PSEUDO(GekkoMnemonic::Twi, TrapSetTO<5>), // twlnli + PLAIN_PSEUDO(GekkoMnemonic::Tw, TrapSetTO<6>), // twlng + PLAIN_PSEUDO(GekkoMnemonic::Twi, TrapSetTO<6>), // twlngi + PLAIN_PSEUDO(GekkoMnemonic::Tw, + [](OperandList& operands) { + operands.Insert(0, 31); + operands.Insert(1, 0); + operands.Insert(2, 0); + }), // trap + + // E.8 + PLAIN_PSEUDO(GekkoMnemonic::Mtspr_nobitswap, FillMtspr<SprBitswap(1)>), // mtxer + PLAIN_PSEUDO(GekkoMnemonic::Mfspr_nobitswap, FillMfspr<SprBitswap(1)>), // mfxer + PLAIN_PSEUDO(GekkoMnemonic::Mtspr_nobitswap, FillMtspr<SprBitswap(8)>), // mtlr + PLAIN_PSEUDO(GekkoMnemonic::Mfspr_nobitswap, FillMfspr<SprBitswap(8)>), // mflr + PLAIN_PSEUDO(GekkoMnemonic::Mtspr_nobitswap, FillMtspr<SprBitswap(9)>), // mtctr + PLAIN_PSEUDO(GekkoMnemonic::Mfspr_nobitswap, FillMfspr<SprBitswap(9)>), // mfctr + PLAIN_PSEUDO(GekkoMnemonic::Mtspr_nobitswap, FillMtspr<SprBitswap(18)>), // mtdsisr + PLAIN_PSEUDO(GekkoMnemonic::Mfspr_nobitswap, FillMfspr<SprBitswap(18)>), // mfdsisr + PLAIN_PSEUDO(GekkoMnemonic::Mtspr_nobitswap, FillMtspr<SprBitswap(19)>), // mtdar + PLAIN_PSEUDO(GekkoMnemonic::Mfspr_nobitswap, FillMfspr<SprBitswap(19)>), // mfdar + PLAIN_PSEUDO(GekkoMnemonic::Mtspr_nobitswap, FillMtspr<SprBitswap(22)>), // mtdec + PLAIN_PSEUDO(GekkoMnemonic::Mfspr_nobitswap, FillMfspr<SprBitswap(22)>), // mfdec + PLAIN_PSEUDO(GekkoMnemonic::Mtspr_nobitswap, FillMtspr<SprBitswap(25)>), // mtsdr1 + PLAIN_PSEUDO(GekkoMnemonic::Mfspr_nobitswap, FillMfspr<SprBitswap(25)>), // mfsdr1 + PLAIN_PSEUDO(GekkoMnemonic::Mtspr_nobitswap, FillMtspr<SprBitswap(26)>), // mtsrr0 + PLAIN_PSEUDO(GekkoMnemonic::Mfspr_nobitswap, FillMfspr<SprBitswap(26)>), // mfsrr0 + PLAIN_PSEUDO(GekkoMnemonic::Mtspr_nobitswap, FillMtspr<SprBitswap(27)>), // mtsrr1 + PLAIN_PSEUDO(GekkoMnemonic::Mfspr_nobitswap, FillMfspr<SprBitswap(27)>), // mfsrr1 + PLAIN_PSEUDO(GekkoMnemonic::Mtspr_nobitswap, FillMtspr<SprBitswap(280)>), // mtasr + PLAIN_PSEUDO(GekkoMnemonic::Mfspr_nobitswap, FillMfspr<SprBitswap(280)>), // mfasr + PLAIN_PSEUDO(GekkoMnemonic::Mtspr_nobitswap, FillMtspr<SprBitswap(282)>), // mtear + PLAIN_PSEUDO(GekkoMnemonic::Mfspr_nobitswap, FillMfspr<SprBitswap(282)>), // mfear + PLAIN_PSEUDO(GekkoMnemonic::Mtspr_nobitswap, FillMtspr<SprBitswap(284)>), // mttbl + PLAIN_PSEUDO(GekkoMnemonic::Mftb_nobitswap, FillMfspr<SprBitswap(268)>), // mftbl + PLAIN_PSEUDO(GekkoMnemonic::Mtspr_nobitswap, FillMtspr<SprBitswap(285)>), // mttbu + PLAIN_PSEUDO(GekkoMnemonic::Mftb_nobitswap, FillMfspr<SprBitswap(269)>), // mftbu + PLAIN_PSEUDO( + GekkoMnemonic::Mtspr_nobitswap, + [](OperandList& operands) { operands[0] = SprBitswap(operands[0] + 272); }), // mtsprg + PLAIN_PSEUDO( + GekkoMnemonic::Mfspr_nobitswap, + [](OperandList& operands) { operands[1] = SprBitswap(operands[1] + 272); }), // mfsprg + PLAIN_PSEUDO(GekkoMnemonic::Mtspr_nobitswap, FillMtsprBatAndBitswap<528>), // mtibatu + PLAIN_PSEUDO(GekkoMnemonic::Mfspr_nobitswap, FillMfsprBatAndBitswap<528>), // mfibatu + PLAIN_PSEUDO(GekkoMnemonic::Mtspr_nobitswap, FillMtsprBatAndBitswap<529>), // mtibatl + PLAIN_PSEUDO(GekkoMnemonic::Mfspr_nobitswap, FillMfsprBatAndBitswap<529>), // mfibatl + PLAIN_PSEUDO(GekkoMnemonic::Mtspr_nobitswap, FillMtsprBatAndBitswap<536>), // mtdbatu + PLAIN_PSEUDO(GekkoMnemonic::Mfspr_nobitswap, FillMfsprBatAndBitswap<536>), // mfdbatu + PLAIN_PSEUDO(GekkoMnemonic::Mtspr_nobitswap, FillMtsprBatAndBitswap<537>), // mtdbatl + PLAIN_PSEUDO(GekkoMnemonic::Mfspr_nobitswap, FillMfsprBatAndBitswap<537>), // mfdbatl + + // E.9 + PLAIN_PSEUDO(GekkoMnemonic::Ori, + [](OperandList& operands) { + operands.Insert(0, 0); + operands.Insert(1, 0); + operands.Insert(2, 0); + }), // nop + PLAIN_PSEUDO(GekkoMnemonic::Addi, + [](OperandList& operands) { operands.Insert(1, 0); }), // li + PLAIN_PSEUDO(GekkoMnemonic::Addis, + [](OperandList& operands) { operands.Insert(1, 0); }), // lis + PLAIN_PSEUDO(GekkoMnemonic::Addi, SwapOps1And2), // la + RC_PSEUDO(GekkoMnemonic::Or, + ([](OperandList& operands) { operands.Insert(2, operands[1]); })), // mr + RC_PSEUDO(GekkoMnemonic::Nor, + ([](OperandList& operands) { operands.Insert(2, operands[1]); })), // not + PLAIN_PSEUDO(GekkoMnemonic::Mtcrf, + [](OperandList& operands) { operands.Insert(0, 0xff); }), // mtcr + + // Additional mnemonics + PLAIN_PSEUDO(GekkoMnemonic::Mfspr_nobitswap, BitswapIdx<1>), // mfspr + PLAIN_PSEUDO(GekkoMnemonic::Mftb_nobitswap, BitswapIdx<1>), // mfspr + PLAIN_PSEUDO(GekkoMnemonic::Mtspr_nobitswap, BitswapIdx<0>), // mtspr +}; + +#undef EMIT_MNEMONIC_ENTRY +#undef MNEMONIC +#undef BASIC_MNEMONIC +#undef RC_MNEMONIC +#undef OERC_MNEMONIC +#undef LK_MNEMONIC +#undef AALK_MNEMONIC +#undef PSEUDO +#undef PLAIN_PSEUDO +#undef RC_PSEUDO +#undef OERC_PSEUDO +#undef LK_PSEUDO +#undef LKAA_PSEUDO + +////////////////// +// LEXER TABLES // +////////////////// + +namespace +{ +constexpr TransitionF HasPlusOrMinus = [](char c) { return c == '+' || c == '-'; }; +constexpr TransitionF HasDigit = [](char c) -> bool { return std::isdigit(c); }; +constexpr TransitionF HasE = [](char c) { return c == 'e'; }; +constexpr TransitionF HasDot = [](char c) { return c == '.'; }; + +// Normal string characters +constexpr TransitionF HasNormal = [](char c) { return c != '\n' && c != '"' && c != '\\'; }; +// Invalid characters in string +constexpr TransitionF HasInvalid = [](char c) { return c == '\n'; }; +// Octal digits +constexpr TransitionF HasOctal = [](char c) { return c >= '0' && c <= '7'; }; +// Hex digits +constexpr TransitionF HasHex = [](char c) -> bool { return std::isxdigit(c); }; +// Normal - octal +constexpr TransitionF HasNormalMinusOctal = [](char c) { return HasNormal(c) && !HasOctal(c); }; +// Normal - hex +constexpr TransitionF HasNormalMinusHex = [](char c) { return HasNormal(c) && !HasHex(c); }; +// Escape start +constexpr TransitionF HasEscape = [](char c) { return c == '\\'; }; +// All single-character escapes +constexpr TransitionF HasSCE = [](char c) { return !HasOctal(c) && c != 'x' && c != '\n'; }; +// Hex escape +constexpr TransitionF HasHexStart = [](char c) { return c == 'x'; }; +constexpr TransitionF HasQuote = [](char c) { return c == '"'; }; +} // namespace + +extern const std::vector<DfaNode> float_dfa = { + {{DfaEdge(HasPlusOrMinus, 1), DfaEdge(HasDigit, 2), DfaEdge(HasDot, 5)}, + "Invalid float: No numeric value"}, + + {{DfaEdge(HasDigit, 2), DfaEdge(HasDot, 5)}, "Invalid float: No numeric value"}, + + {{DfaEdge(HasDigit, 2), DfaEdge(HasDot, 3), DfaEdge(HasE, 7)}, std::nullopt}, + {{DfaEdge(HasDigit, 4)}, "Invalid float: No numeric value after decimal point"}, + {{DfaEdge(HasDigit, 4), DfaEdge(HasE, 7)}, std::nullopt}, + + {{DfaEdge(HasDigit, 6)}, "Invalid float: No numeric value after decimal point"}, + {{DfaEdge(HasDigit, 6), DfaEdge(HasE, 7)}, std::nullopt}, + + {{DfaEdge(HasDigit, 9), DfaEdge(HasPlusOrMinus, 8)}, + "Invalid float: No numeric value following exponent signifier"}, + {{DfaEdge(HasDigit, 9)}, "Invalid float: No numeric value following exponent signifier"}, + {{DfaEdge(HasDigit, 9)}, std::nullopt}, +}; + +extern const std::vector<DfaNode> string_dfa = { + // Base character check + {{DfaEdge(HasNormal, 0), DfaEdge(HasInvalid, 1), DfaEdge(HasQuote, 2), DfaEdge(HasEscape, 3)}, + "Invalid string: No terminating \""}, + + // Invalid (unescaped newline) + {{}, "Invalid string: No terminating \""}, + // String end + {{}, std::nullopt}, + + // Escape character breakout + {{DfaEdge(HasSCE, 0), DfaEdge(HasInvalid, 1), DfaEdge(HasOctal, 4), DfaEdge(HasHexStart, 6)}, + "Invalid string: No terminating \""}, + + // Octal characters, at most 3 + {{DfaEdge(HasNormalMinusOctal, 0), DfaEdge(HasInvalid, 1), DfaEdge(HasQuote, 2), + DfaEdge(HasEscape, 3), DfaEdge(HasOctal, 5)}, + "Invalid string: No terminating \""}, + {{DfaEdge(HasNormal, 0), DfaEdge(HasInvalid, 1), DfaEdge(HasQuote, 2), DfaEdge(HasEscape, 3)}, + "Invalid string: No terminating \""}, + + // Hex characters, 1 or more + {{DfaEdge(HasHex, 7)}, "Invalid string: bad hex escape"}, + {{DfaEdge(HasNormalMinusHex, 0), DfaEdge(HasInvalid, 1), DfaEdge(HasQuote, 2), + DfaEdge(HasEscape, 3), DfaEdge(HasHex, 7)}, + "Invalid string: No terminating \""}, +}; +} // namespace Common::GekkoAssembler::detail diff --git a/Source/Core/Common/Assembler/AssemblerTables.h b/Source/Core/Common/Assembler/AssemblerTables.h new file mode 100644 index 0000000000..ed084208e4 --- /dev/null +++ b/Source/Core/Common/Assembler/AssemblerTables.h @@ -0,0 +1,152 @@ +// Copyright 2023 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <array> +#include <cstddef> +#include <optional> +#include <string_view> +#include <vector> + +#include "Common/Assembler/AssemblerShared.h" +#include "Common/Assembler/CaseInsensitiveDict.h" +#include "Common/CommonTypes.h" + +namespace Common::GekkoAssembler::detail +{ +/////////////////// +// PARSER TABLES // +/////////////////// +enum class ParseAlg +{ + None, + Op1, + NoneOrOp1, + Op1Off1, + Op2, + Op1Or2, + Op3, + Op2Or3, + Op4, + Op5, + Op1Off1Op2, +}; + +struct ParseInfo +{ + size_t mnemonic_index; + ParseAlg parse_algorithm; +}; + +// Mapping of SPRG names to values +extern const CaseInsensitiveDict<u32, '_'> sprg_map; +// Mapping of directive names to an enumeration +extern const CaseInsensitiveDict<GekkoDirective> directives_map; +// Mapping of normal Gekko mnemonics to their index and argument form +extern const CaseInsensitiveDict<ParseInfo, '.', '_'> mnemonic_tokens; +// Mapping of extended Gekko mnemonics to their index and argument form +extern const CaseInsensitiveDict<ParseInfo, '.', '_', '+', '-'> extended_mnemonic_tokens; + +////////////////////// +// ASSEMBLER TABLES // +////////////////////// +constexpr size_t MAX_OPERANDS = 5; + +struct OperandList +{ + std::array<Tagged<Interval, u32>, MAX_OPERANDS> list; + u32 count; + bool overfill; + + constexpr u32 operator[](size_t index) const { return ValueOf(list[index]); } + constexpr u32& operator[](size_t index) { return ValueOf(list[index]); } + + void Insert(size_t before, u32 val); + + template <typename It> + void Copy(It begin, It end) + { + count = 0; + for (auto& i : list) + { + if (begin == end) + { + break; + } + i = *begin; + begin++; + count++; + } + overfill = begin != end; + } +}; + +struct OperandDesc +{ + u32 mask; + struct + { + u32 shift : 31; + bool is_signed : 1; + }; + u32 MaxVal() const; + u32 MinVal() const; + u32 TruncBits() const; + + bool Fits(u32 val) const; + u32 Fit(u32 val) const; +}; + +// MnemonicDesc holds the machine-code template for mnemonics +struct MnemonicDesc +{ + // Initial value for a given mnemonic (opcode, func code, LK, AA, OE) + const u32 initial_value; + const u32 operand_count; + // Masks for operands + std::array<OperandDesc, MAX_OPERANDS> operand_masks; +}; + +// ExtendedMnemonicDesc holds the name of the mnemonic it transforms to as well as a +// transformer callback to translate the operands into the correct form for the base mnemonic +struct ExtendedMnemonicDesc +{ + size_t mnemonic_index; + void (*transform_operands)(OperandList&); +}; + +static constexpr size_t NUM_MNEMONICS = static_cast<size_t>(GekkoMnemonic::LastMnemonic) + 1; +static constexpr size_t NUM_EXT_MNEMONICS = + static_cast<size_t>(ExtendedGekkoMnemonic::LastMnemonic) + 1; +static constexpr size_t VARIANT_PERMUTATIONS = 4; + +// Table for mapping mnemonic+variants to their descriptors +extern const std::array<MnemonicDesc, NUM_MNEMONICS * VARIANT_PERMUTATIONS> mnemonics; +// Table for mapping extended mnemonic+variants to their descriptors +extern const std::array<ExtendedMnemonicDesc, NUM_EXT_MNEMONICS * VARIANT_PERMUTATIONS> + extended_mnemonics; + +////////////////// +// LEXER TABLES // +////////////////// + +// In place of the reliace on std::regex, DFAs will be defined for matching sufficiently complex +// tokens This gives an extra benefit of providing reasons for match failures +using TransitionF = bool (*)(char c); +using DfaEdge = std::pair<TransitionF, size_t>; +struct DfaNode +{ + std::vector<DfaEdge> edges; + // If nullopt: this is a final node + // If string: invalid reason + std::optional<std::string_view> match_failure_reason; +}; + +// Floating point strings that will be accepted by std::stof/std::stod +// regex: [\+-]?(\d+(\.\d+)?|\.\d+)(e[\+-]?\d+)? +extern const std::vector<DfaNode> float_dfa; +// C-style strings +// regex: "([^\\\n]|\\([0-7]{1,3}|x[0-9a-fA-F]+|[^x0-7\n]))*" +extern const std::vector<DfaNode> string_dfa; +} // namespace Common::GekkoAssembler::detail diff --git a/Source/Core/Common/Assembler/CaseInsensitiveDict.h b/Source/Core/Common/Assembler/CaseInsensitiveDict.h new file mode 100644 index 0000000000..8f6acccc8f --- /dev/null +++ b/Source/Core/Common/Assembler/CaseInsensitiveDict.h @@ -0,0 +1,126 @@ +// Copyright 2023 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <array> +#include <optional> +#include <string> +#include <string_view> +#include <type_traits> +#include <utility> +#include <vector> + +namespace Common::GekkoAssembler::detail +{ +// Hacky implementation of a case insensitive alphanumeric trie supporting extended entries +// Standing in for std::map to support case-insensitive lookups while allowing string_views in +// lookups +template <typename V, char... ExtraMatches> +class CaseInsensitiveDict +{ +public: + CaseInsensitiveDict(const std::initializer_list<std::pair<std::string_view, V>>& il) + { + for (auto&& [k, v] : il) + { + Add(k, v); + } + } + + template <typename T> + V const* Find(const T& key) const + { + auto&& [last_e, it] = TryFind(key); + if (it == key.cend() && last_e->_val) + { + return &*last_e->_val; + } + return nullptr; + } + static constexpr size_t NUM_CONNS = 36 + sizeof...(ExtraMatches); + static constexpr uint32_t INVALID_CONN = static_cast<uint32_t>(-1); + +private: + struct TrieEntry + { + std::array<uint32_t, 36 + sizeof...(ExtraMatches)> _conns; + std::optional<V> _val; + + TrieEntry() { std::fill(_conns.begin(), _conns.end(), INVALID_CONN); } + }; + + constexpr size_t IndexOf(char c) const + { + size_t idx; + if (std::isalpha(c)) + { + idx = std::tolower(c) - 'a'; + } + else if (std::isdigit(c)) + { + idx = c - '0' + 26; + } + else + { + idx = 36; + // Expands to an equivalent for loop over ExtraMatches + if constexpr (sizeof...(ExtraMatches) > 0) + { + (void)((c != ExtraMatches ? ++idx, true : false) && ...); + } + } + return idx; + } + + template <typename T> + auto TryFind(const T& key) const -> std::pair<TrieEntry const*, decltype(key.cbegin())> + { + std::pair<TrieEntry const*, decltype(key.cbegin())> ret(&m_root_entry, key.cbegin()); + const auto k_end = key.cend(); + + for (; ret.second != k_end; ret.second++) + { + const size_t idx = IndexOf(*ret.second); + if (idx >= NUM_CONNS || ret.first->_conns[idx] == INVALID_CONN) + { + break; + } + + ret.first = &m_entry_pool[ret.first->_conns[idx]]; + } + + return ret; + } + + template <typename T> + auto TryFind(const T& key) -> std::pair<TrieEntry*, decltype(key.cbegin())> + { + auto&& [e_const, it] = + const_cast<CaseInsensitiveDict<V, ExtraMatches...> const*>(this)->TryFind(key); + return {const_cast<TrieEntry*>(e_const), it}; + } + + void Add(std::string_view key, const V& val) + { + auto&& [last_e, it] = TryFind(key); + if (it != key.cend()) + { + for (; it != key.cend(); it++) + { + const size_t idx = IndexOf(*it); + if (idx >= NUM_CONNS) + { + break; + } + last_e->_conns[idx] = static_cast<uint32_t>(m_entry_pool.size()); + last_e = &m_entry_pool.emplace_back(); + } + } + last_e->_val = val; + } + + TrieEntry m_root_entry; + std::vector<TrieEntry> m_entry_pool; +}; +} // namespace Common::GekkoAssembler::detail diff --git a/Source/Core/Common/Assembler/GekkoAssembler.cpp b/Source/Core/Common/Assembler/GekkoAssembler.cpp new file mode 100644 index 0000000000..4ee97c59d9 --- /dev/null +++ b/Source/Core/Common/Assembler/GekkoAssembler.cpp @@ -0,0 +1,189 @@ +// Copyright 2023 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "Common/Assembler/GekkoAssembler.h" + +#include <algorithm> +#include <array> +#include <string> +#include <vector> + +#include <fmt/format.h> + +#include "Common/Assembler/AssemblerShared.h" +#include "Common/Assembler/AssemblerTables.h" +#include "Common/Assembler/GekkoIRGen.h" +#include "Common/Assert.h" +#include "Common/CommonTypes.h" + +namespace Common::GekkoAssembler +{ +namespace +{ +using namespace Common::GekkoAssembler::detail; + +FailureOr<u32> FillInstruction(const MnemonicDesc& desc, const OperandList& operands, + std::string_view inst_line) +{ + // Parser shouldn't allow this to pass + ASSERT_MSG(COMMON, desc.operand_count == operands.count && !operands.overfill, + "Unexpected operand count mismatch for instruction {}. Expected {} but found {}", + inst_line, desc.operand_count, operands.overfill ? 6 : operands.count); + + u32 instruction = desc.initial_value; + for (u32 i = 0; i < operands.count; i++) + { + if (!desc.operand_masks[i].Fits(operands[i])) + { + std::string message; + const u32 trunc_bits = desc.operand_masks[i].TruncBits(); + if (trunc_bits == 0) + { + if (desc.operand_masks[i].is_signed) + { + message = fmt::format("{:#x} not between {:#x} and {:#x}", static_cast<s32>(operands[i]), + static_cast<s32>(desc.operand_masks[i].MinVal()), + static_cast<s32>(desc.operand_masks[i].MaxVal())); + } + else + { + message = fmt::format("{:#x} not between {:#x} and {:#x}", operands[i], + desc.operand_masks[i].MinVal(), desc.operand_masks[i].MaxVal()); + } + } + else + { + if (desc.operand_masks[i].is_signed) + { + message = fmt::format("{:#x} not between {:#x} and {:#x} or not aligned to {}", + static_cast<s32>(operands[i]), + static_cast<s32>(desc.operand_masks[i].MinVal()), + static_cast<s32>(desc.operand_masks[i].MaxVal()), trunc_bits + 1); + } + else + { + message = fmt::format("{:#x} not between {:#x} and {:#x} or not aligned to {}", + operands[i], desc.operand_masks[i].MinVal(), + desc.operand_masks[i].MaxVal(), trunc_bits + 1); + } + } + return AssemblerError{std::move(message), "", 0, TagOf(operands.list[i]).begin, + TagOf(operands.list[i]).len}; + } + instruction |= desc.operand_masks[i].Fit(operands[i]); + } + return instruction; +} + +void AdjustOperandsForGas(GekkoMnemonic mnemonic, OperandList& ops_list) +{ + switch (mnemonic) + { + case GekkoMnemonic::Cmp: + case GekkoMnemonic::Cmpl: + case GekkoMnemonic::Cmpi: + case GekkoMnemonic::Cmpli: + if (ops_list.count < 4) + { + ops_list.Insert(0, 0); + } + break; + + case GekkoMnemonic::Addis: + // Because GAS wants to allow for addis and lis to work nice with absolute addresses, the + // immediate operand should also "fit" into the _UIMM field, so just turn a valid UIMM into a + // SIMM + if (ops_list[2] >= 0x8000 && ops_list[2] <= 0xffff) + { + ops_list[2] = ops_list[2] - 0x10000; + } + break; + + default: + break; + } +} + +} // namespace + +void CodeBlock::PushBigEndian(u32 val) +{ + instructions.push_back((val >> 24) & 0xff); + instructions.push_back((val >> 16) & 0xff); + instructions.push_back((val >> 8) & 0xff); + instructions.push_back(val & 0xff); +} + +FailureOr<std::vector<CodeBlock>> Assemble(std::string_view instruction, + u32 current_instruction_address) +{ + FailureOr<detail::GekkoIR> parse_result = + detail::ParseToIR(instruction, current_instruction_address); + if (IsFailure(parse_result)) + { + return GetFailure(parse_result); + } + + const auto& parsed_blocks = GetT(parse_result).blocks; + const auto& operands = GetT(parse_result).operand_pool; + std::vector<CodeBlock> out_blocks; + + for (const detail::IRBlock& parsed_block : parsed_blocks) + { + CodeBlock new_block(parsed_block.block_address); + for (const detail::ChunkVariant& chunk : parsed_block.chunks) + { + if (std::holds_alternative<detail::InstChunk>(chunk)) + { + for (const detail::GekkoInstruction& parsed_inst : std::get<detail::InstChunk>(chunk)) + { + OperandList adjusted_ops; + ASSERT(parsed_inst.op_interval.len <= MAX_OPERANDS); + adjusted_ops.Copy(operands.begin() + parsed_inst.op_interval.begin, + operands.begin() + parsed_inst.op_interval.End()); + + size_t idx = parsed_inst.mnemonic_index; + if (parsed_inst.is_extended) + { + extended_mnemonics[idx].transform_operands(adjusted_ops); + idx = extended_mnemonics[idx].mnemonic_index; + } + + AdjustOperandsForGas(static_cast<GekkoMnemonic>(idx >> 2), adjusted_ops); + + FailureOr<u32> inst = FillInstruction(mnemonics[idx], adjusted_ops, parsed_inst.raw_text); + if (IsFailure(inst)) + { + GetFailure(inst).error_line = parsed_inst.raw_text; + GetFailure(inst).line = parsed_inst.line_number; + return GetFailure(inst); + } + + new_block.PushBigEndian(GetT(inst)); + } + } + else if (std::holds_alternative<detail::ByteChunk>(chunk)) + { + detail::ByteChunk byte_arr = std::get<detail::ByteChunk>(chunk); + new_block.instructions.insert(new_block.instructions.end(), byte_arr.begin(), + byte_arr.end()); + } + else if (std::holds_alternative<detail::PadChunk>(chunk)) + { + detail::PadChunk pad_len = std::get<detail::PadChunk>(chunk); + new_block.instructions.insert(new_block.instructions.end(), pad_len, 0); + } + else + { + ASSERT(false); + } + } + + if (!new_block.instructions.empty()) + { + out_blocks.emplace_back(std::move(new_block)); + } + } + return out_blocks; +} +} // namespace Common::GekkoAssembler diff --git a/Source/Core/Common/Assembler/GekkoAssembler.h b/Source/Core/Common/Assembler/GekkoAssembler.h new file mode 100644 index 0000000000..ede99532f3 --- /dev/null +++ b/Source/Core/Common/Assembler/GekkoAssembler.h @@ -0,0 +1,29 @@ +// Copyright 2023 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <string_view> +#include <vector> + +#include "Common/Assembler/AssemblerShared.h" +#include "Common/CommonTypes.h" + +namespace Common::GekkoAssembler +{ +struct CodeBlock +{ + CodeBlock(u32 address) : block_address(address) {} + + void PushBigEndian(u32 val); + + u32 block_address; + std::vector<u8> instructions; +}; + +// Common::GekkoAssember::Assemble - Core routine for assembling Gekko/Broadway instructions +// Supports the full Gekko ISA, as well as the extended mnemonics defined by the book "PowerPC +// Microprocessor Family: The Programming Environments" The input assembly is fully parsed and +// assembled with a base address specified by the base_virtual_address +FailureOr<std::vector<CodeBlock>> Assemble(std::string_view assembly, u32 base_virtual_address); +} // namespace Common::GekkoAssembler diff --git a/Source/Core/Common/Assembler/GekkoIRGen.cpp b/Source/Core/Common/Assembler/GekkoIRGen.cpp new file mode 100644 index 0000000000..72c284a15e --- /dev/null +++ b/Source/Core/Common/Assembler/GekkoIRGen.cpp @@ -0,0 +1,832 @@ +// Copyright 2023 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "Common/Assembler/GekkoIRGen.h" + +#include <functional> +#include <map> +#include <numeric> +#include <set> +#include <stack> +#include <variant> +#include <vector> + +#include <fmt/format.h> + +#include "Common/Assembler/AssemblerShared.h" +#include "Common/Assembler/GekkoParser.h" +#include "Common/Assert.h" +#include "Common/BitUtils.h" + +namespace Common::GekkoAssembler::detail +{ +namespace +{ +class GekkoIRPlugin : public ParsePlugin +{ +public: + GekkoIRPlugin(GekkoIR& result, u32 base_addr) + : m_output_result(result), m_active_var(nullptr), m_operand_scan_begin(0) + { + m_active_block = &m_output_result.blocks.emplace_back(base_addr); + } + virtual ~GekkoIRPlugin() = default; + + void OnDirectivePre(GekkoDirective directive) override; + void OnDirectivePost(GekkoDirective directive) override; + void OnInstructionPre(const ParseInfo& mnemonic_info, bool extended) override; + void OnInstructionPost(const ParseInfo& mnemonic_info, bool extended) override; + void OnOperandPre() override; + void OnOperandPost() override; + void OnResolvedExprPost() override; + void OnOperator(AsmOp operation) override; + void OnTerminal(Terminal type, const AssemblerToken& val) override; + void OnHiaddr(std::string_view id) override; + void OnLoaddr(std::string_view id) override; + void OnCloseParen(ParenType type) override; + void OnLabelDecl(std::string_view name) override; + void OnVarDecl(std::string_view name) override; + void PostParseAction() override; + + u32 CurrentAddress() const; + std::optional<u64> LookupVar(std::string_view lab); + std::optional<u32> LookupLabel(std::string_view lab); + + template <typename T> + T& GetChunk(); + + template <typename T> + void AddBytes(T val); + + void AddStringBytes(std::string_view str, bool null_term); + + void PadAlign(u32 bits); + void PadSpace(size_t space); + + void StartBlock(u32 address); + void StartBlockAlign(u32 bits); + void StartInstruction(size_t mnemonic_index, bool extended); + void FinishInstruction(); + void SaveOperandFixup(size_t str_left, size_t str_right); + + void AddBinaryEvaluator(u32 (*evaluator)(u32, u32)); + void AddUnaryEvaluator(u32 (*evaluator)(u32)); + void AddAbsoluteAddressConv(); + void AddLiteral(u32 lit); + void AddSymbolResolve(std::string_view sym, bool absolute); + + void RunFixups(); + + void EvalOperatorRel(AsmOp operation); + void EvalOperatorAbs(AsmOp operation); + void EvalTerminalRel(Terminal type, const AssemblerToken& tok); + void EvalTerminalAbs(Terminal type, const AssemblerToken& tok); + +private: + enum class EvalMode + { + RelAddrDoublePass, + AbsAddrSinglePass, + }; + + GekkoIR& m_output_result; + + IRBlock* m_active_block; + GekkoInstruction m_build_inst; + u64* m_active_var; + size_t m_operand_scan_begin; + + std::map<std::string, u32, std::less<>> m_labels; + std::map<std::string, u64, std::less<>> m_constants; + std::set<std::string> m_symset; + + EvalMode m_evaluation_mode; + + // For operand parsing + std::stack<std::function<u32()>> m_fixup_stack; + std::vector<std::function<u32()>> m_operand_fixups; + size_t m_operand_str_start; + + // For directive parsing + std::vector<u64> m_eval_stack; + std::variant<std::vector<float>, std::vector<double>> m_floats_list; + std::string_view m_string_lit; + GekkoDirective m_active_directive; +}; + +/////////////// +// OVERRIDES // +/////////////// + +void GekkoIRPlugin::OnDirectivePre(GekkoDirective directive) +{ + m_evaluation_mode = EvalMode::AbsAddrSinglePass; + m_active_directive = directive; + m_eval_stack = std::vector<u64>{}; + + switch (directive) + { + case GekkoDirective::Float: + m_floats_list = std::vector<float>{}; + break; + case GekkoDirective::Double: + m_floats_list = std::vector<double>{}; + break; + default: + break; + } +} + +void GekkoIRPlugin::OnDirectivePost(GekkoDirective directive) +{ + switch (directive) + { + // .nbyte directives are handled by OnResolvedExprPost + default: + break; + + case GekkoDirective::Float: + case GekkoDirective::Double: + std::visit( + [this](auto&& vec) { + for (auto&& val : vec) + { + AddBytes(val); + } + }, + m_floats_list); + break; + + case GekkoDirective::DefVar: + ASSERT(m_active_var != nullptr); + *m_active_var = m_eval_stack.back(); + m_active_var = nullptr; + break; + + case GekkoDirective::Locate: + StartBlock(static_cast<u32>(m_eval_stack.back())); + break; + + case GekkoDirective::Zeros: + PadSpace(static_cast<u32>(m_eval_stack.back())); + break; + + case GekkoDirective::Skip: + { + const u32 skip_len = static_cast<u32>(m_eval_stack.back()); + if (skip_len > 0) + { + StartBlock(CurrentAddress() + skip_len); + } + break; + } + + case GekkoDirective::PadAlign: + PadAlign(static_cast<u32>(m_eval_stack.back())); + break; + + case GekkoDirective::Align: + StartBlockAlign(static_cast<u32>(m_eval_stack.back())); + break; + + case GekkoDirective::Ascii: + AddStringBytes(m_string_lit, false); + break; + + case GekkoDirective::Asciz: + AddStringBytes(m_string_lit, true); + break; + } + m_eval_stack = {}; +} + +void GekkoIRPlugin::OnInstructionPre(const ParseInfo& mnemonic_info, bool extended) +{ + m_evaluation_mode = EvalMode::RelAddrDoublePass; + StartInstruction(mnemonic_info.mnemonic_index, extended); +} + +void GekkoIRPlugin::OnInstructionPost(const ParseInfo&, bool) +{ + FinishInstruction(); +} + +void GekkoIRPlugin::OnOperandPre() +{ + m_operand_str_start = m_owner->lexer.ColNumber(); +} + +void GekkoIRPlugin::OnOperandPost() +{ + SaveOperandFixup(m_operand_str_start, m_owner->lexer.ColNumber()); +} + +void GekkoIRPlugin::OnResolvedExprPost() +{ + switch (m_active_directive) + { + case GekkoDirective::Byte: + AddBytes<u8>(static_cast<u8>(m_eval_stack.back())); + break; + case GekkoDirective::_2byte: + AddBytes<u16>(static_cast<u16>(m_eval_stack.back())); + break; + case GekkoDirective::_4byte: + AddBytes<u32>(static_cast<u32>(m_eval_stack.back())); + break; + case GekkoDirective::_8byte: + AddBytes<u64>(static_cast<u64>(m_eval_stack.back())); + break; + default: + return; + } + m_eval_stack.clear(); +} + +void GekkoIRPlugin::OnOperator(AsmOp operation) +{ + if (m_evaluation_mode == EvalMode::RelAddrDoublePass) + { + EvalOperatorRel(operation); + } + else + { + EvalOperatorAbs(operation); + } +} + +void GekkoIRPlugin::OnTerminal(Terminal type, const AssemblerToken& val) +{ + if (type == Terminal::Str) + { + m_string_lit = val.token_val; + } + else if (m_evaluation_mode == EvalMode::RelAddrDoublePass) + { + EvalTerminalRel(type, val); + } + else + { + EvalTerminalAbs(type, val); + } +} + +void GekkoIRPlugin::OnHiaddr(std::string_view id) +{ + if (m_evaluation_mode == EvalMode::RelAddrDoublePass) + { + AddSymbolResolve(id, true); + AddLiteral(16); + AddBinaryEvaluator([](u32 lhs, u32 rhs) { return lhs >> rhs; }); + AddLiteral(0xffff); + AddBinaryEvaluator([](u32 lhs, u32 rhs) { return lhs & rhs; }); + } + else + { + u32 base; + if (auto lbl = LookupLabel(id); lbl) + { + base = *lbl; + } + else if (auto var = LookupVar(id); var) + { + base = *var; + } + else + { + m_owner->EmitErrorHere(fmt::format("Undefined reference to Label/Constant '{}'", id)); + return; + } + m_eval_stack.push_back((base >> 16) & 0xffff); + } +} + +void GekkoIRPlugin::OnLoaddr(std::string_view id) +{ + if (m_evaluation_mode == EvalMode::RelAddrDoublePass) + { + AddSymbolResolve(id, true); + AddLiteral(0xffff); + AddBinaryEvaluator([](u32 lhs, u32 rhs) { return lhs & rhs; }); + } + else + { + u32 base; + if (auto lbl = LookupLabel(id); lbl) + { + base = *lbl; + } + else if (auto var = LookupVar(id); var) + { + base = *var; + } + else + { + m_owner->EmitErrorHere(fmt::format("Undefined reference to Label/Constant '{}'", id)); + return; + } + + m_eval_stack.push_back(base & 0xffff); + } +} + +void GekkoIRPlugin::OnCloseParen(ParenType type) +{ + if (type != ParenType::RelConv) + { + return; + } + + if (m_evaluation_mode == EvalMode::RelAddrDoublePass) + { + AddAbsoluteAddressConv(); + } + else + { + m_eval_stack.push_back(CurrentAddress()); + EvalOperatorAbs(AsmOp::Sub); + } +} + +void GekkoIRPlugin::OnLabelDecl(std::string_view name) +{ + const std::string name_str(name); + if (m_symset.contains(name_str)) + { + m_owner->EmitErrorHere(fmt::format("Label/Constant {} is already defined", name)); + return; + } + + m_labels[name_str] = m_active_block->BlockEndAddress(); + m_symset.insert(name_str); +} + +void GekkoIRPlugin::OnVarDecl(std::string_view name) +{ + const std::string name_str(name); + if (m_symset.contains(name_str)) + { + m_owner->EmitErrorHere(fmt::format("Label/Constant {} is already defined", name)); + return; + } + + m_active_var = &m_constants[name_str]; + m_symset.insert(name_str); +} + +void GekkoIRPlugin::PostParseAction() +{ + RunFixups(); +} + +////////////////////// +// HELPER FUNCTIONS // +////////////////////// + +u32 GekkoIRPlugin::CurrentAddress() const +{ + return m_active_block->BlockEndAddress(); +} + +std::optional<u64> GekkoIRPlugin::LookupVar(std::string_view var) +{ + auto var_it = m_constants.find(var); + return var_it == m_constants.end() ? std::nullopt : std::optional(var_it->second); +} + +std::optional<u32> GekkoIRPlugin::LookupLabel(std::string_view lab) +{ + auto label_it = m_labels.find(lab); + return label_it == m_labels.end() ? std::nullopt : std::optional(label_it->second); +} + +void GekkoIRPlugin::AddStringBytes(std::string_view str, bool null_term) +{ + ByteChunk& bytes = GetChunk<ByteChunk>(); + ConvertStringLiteral(str, &bytes); + if (null_term) + { + bytes.push_back('\0'); + } +} + +template <typename T> +T& GekkoIRPlugin::GetChunk() +{ + if (!m_active_block->chunks.empty() && std::holds_alternative<T>(m_active_block->chunks.back())) + { + return std::get<T>(m_active_block->chunks.back()); + } + + return std::get<T>(m_active_block->chunks.emplace_back(T{})); +} + +template <typename T> +void GekkoIRPlugin::AddBytes(T val) +{ + if constexpr (std::is_integral_v<T>) + { + ByteChunk& bytes = GetChunk<ByteChunk>(); + for (size_t i = sizeof(T) - 1; i > 0; i--) + { + bytes.push_back((val >> (8 * i)) & 0xff); + } + bytes.push_back(val & 0xff); + } + else if constexpr (std::is_same_v<T, float>) + { + static_assert(sizeof(double) == sizeof(u64)); + AddBytes(BitCast<u32>(val)); + } + else + { + // std::is_same_v<T, double> + static_assert(sizeof(double) == sizeof(u64)); + AddBytes(BitCast<u64>(val)); + } +} + +void GekkoIRPlugin::PadAlign(u32 bits) +{ + const u32 align_mask = (1 << bits) - 1; + const u32 current_addr = m_active_block->BlockEndAddress(); + if (current_addr & align_mask) + { + PadChunk& current_pad = GetChunk<PadChunk>(); + current_pad += (1 << bits) - (current_addr & align_mask); + } +} + +void GekkoIRPlugin::PadSpace(size_t space) +{ + GetChunk<PadChunk>() += space; +} + +void GekkoIRPlugin::StartBlock(u32 address) +{ + m_active_block = &m_output_result.blocks.emplace_back(address); +} + +void GekkoIRPlugin::StartBlockAlign(u32 bits) +{ + const u32 align_mask = (1 << bits) - 1; + const u32 current_addr = m_active_block->BlockEndAddress(); + if (current_addr & align_mask) + { + StartBlock((1 << bits) + (current_addr & ~align_mask)); + } +} + +void GekkoIRPlugin::StartInstruction(size_t mnemonic_index, bool extended) +{ + m_build_inst = GekkoInstruction{ + .mnemonic_index = mnemonic_index, + .raw_text = m_owner->lexer.CurrentLine(), + .line_number = m_owner->lexer.LineNumber(), + .is_extended = extended, + }; + m_operand_scan_begin = m_output_result.operand_pool.size(); +} + +void GekkoIRPlugin::AddBinaryEvaluator(u32 (*evaluator)(u32, u32)) +{ + std::function<u32()> rhs = std::move(m_fixup_stack.top()); + m_fixup_stack.pop(); + std::function<u32()> lhs = std::move(m_fixup_stack.top()); + m_fixup_stack.pop(); + m_fixup_stack.emplace([evaluator, lhs = std::move(lhs), rhs = std::move(rhs)]() { + return evaluator(lhs(), rhs()); + }); +} + +void GekkoIRPlugin::AddUnaryEvaluator(u32 (*evaluator)(u32)) +{ + std::function<u32()> sub = std::move(m_fixup_stack.top()); + m_fixup_stack.pop(); + m_fixup_stack.emplace([evaluator, sub = std::move(sub)]() { return evaluator(sub()); }); +} + +void GekkoIRPlugin::AddAbsoluteAddressConv() +{ + const u32 inst_address = m_active_block->BlockEndAddress(); + std::function<u32()> sub = std::move(m_fixup_stack.top()); + m_fixup_stack.pop(); + m_fixup_stack.emplace([inst_address, sub = std::move(sub)] { return sub() - inst_address; }); +} + +void GekkoIRPlugin::AddLiteral(u32 lit) +{ + m_fixup_stack.emplace([lit] { return lit; }); +} + +void GekkoIRPlugin::AddSymbolResolve(std::string_view sym, bool absolute) +{ + const u32 source_address = m_active_block->BlockEndAddress(); + AssemblerError err_on_fail = AssemblerError{ + fmt::format("Unresolved symbol '{}'", sym), + m_owner->lexer.CurrentLine(), + m_owner->lexer.LineNumber(), + // Lexer should currently point to the label, as it hasn't been eaten yet + m_owner->lexer.ColNumber(), + sym.size(), + }; + + m_fixup_stack.emplace( + [this, sym, absolute, source_address, err_on_fail = std::move(err_on_fail)] { + auto label_it = m_labels.find(sym); + if (label_it != m_labels.end()) + { + if (absolute) + { + return label_it->second; + } + return label_it->second - source_address; + } + + auto var_it = m_constants.find(sym); + if (var_it != m_constants.end()) + { + return static_cast<u32>(var_it->second); + } + + m_owner->error = std::move(err_on_fail); + return u32{0}; + }); +} + +void GekkoIRPlugin::SaveOperandFixup(size_t str_left, size_t str_right) +{ + m_operand_fixups.emplace_back(std::move(m_fixup_stack.top())); + m_fixup_stack.pop(); + m_output_result.operand_pool.emplace_back(Interval{str_left, str_right - str_left}, 0); +} + +void GekkoIRPlugin::RunFixups() +{ + for (size_t i = 0; i < m_operand_fixups.size(); i++) + { + ValueOf(m_output_result.operand_pool[i]) = m_operand_fixups[i](); + if (m_owner->error) + { + return; + } + } +} + +void GekkoIRPlugin::FinishInstruction() +{ + m_build_inst.op_interval.begin = m_operand_scan_begin; + m_build_inst.op_interval.len = m_output_result.operand_pool.size() - m_operand_scan_begin; + GetChunk<InstChunk>().emplace_back(m_build_inst); + m_operand_scan_begin = 0; +} + +void GekkoIRPlugin::EvalOperatorAbs(AsmOp operation) +{ +#define EVAL_BINARY_OP(OPERATOR) \ + { \ + u64 rhs = m_eval_stack.back(); \ + m_eval_stack.pop_back(); \ + m_eval_stack.back() = m_eval_stack.back() OPERATOR rhs; \ + } + + switch (operation) + { + case AsmOp::Or: + EVAL_BINARY_OP(|); + break; + case AsmOp::Xor: + EVAL_BINARY_OP(^); + break; + case AsmOp::And: + EVAL_BINARY_OP(&); + break; + case AsmOp::Lsh: + EVAL_BINARY_OP(<<); + break; + case AsmOp::Rsh: + EVAL_BINARY_OP(>>); + break; + case AsmOp::Add: + EVAL_BINARY_OP(+); + break; + case AsmOp::Sub: + EVAL_BINARY_OP(-); + break; + case AsmOp::Mul: + EVAL_BINARY_OP(*); + break; + case AsmOp::Div: + EVAL_BINARY_OP(/); + break; + case AsmOp::Neg: + m_eval_stack.back() = static_cast<u32>(-static_cast<s32>(m_eval_stack.back())); + break; + case AsmOp::Not: + m_eval_stack.back() = ~m_eval_stack.back(); + break; + } +#undef EVAL_BINARY_OP +#undef EVAL_UNARY_OP +} + +void GekkoIRPlugin::EvalOperatorRel(AsmOp operation) +{ + switch (operation) + { + case AsmOp::Or: + AddBinaryEvaluator([](u32 lhs, u32 rhs) { return lhs | rhs; }); + break; + case AsmOp::Xor: + AddBinaryEvaluator([](u32 lhs, u32 rhs) { return lhs ^ rhs; }); + break; + case AsmOp::And: + AddBinaryEvaluator([](u32 lhs, u32 rhs) { return lhs & rhs; }); + break; + case AsmOp::Lsh: + AddBinaryEvaluator([](u32 lhs, u32 rhs) { return lhs << rhs; }); + break; + case AsmOp::Rsh: + AddBinaryEvaluator([](u32 lhs, u32 rhs) { return lhs >> rhs; }); + break; + case AsmOp::Add: + AddBinaryEvaluator([](u32 lhs, u32 rhs) { return lhs + rhs; }); + break; + case AsmOp::Sub: + AddBinaryEvaluator([](u32 lhs, u32 rhs) { return lhs - rhs; }); + break; + case AsmOp::Mul: + AddBinaryEvaluator([](u32 lhs, u32 rhs) { return lhs * rhs; }); + break; + case AsmOp::Div: + AddBinaryEvaluator([](u32 lhs, u32 rhs) { return lhs / rhs; }); + break; + case AsmOp::Neg: + AddUnaryEvaluator([](u32 val) { return static_cast<u32>(-static_cast<s32>(val)); }); + break; + case AsmOp::Not: + AddUnaryEvaluator([](u32 val) { return ~val; }); + break; + } +} + +void GekkoIRPlugin::EvalTerminalRel(Terminal type, const AssemblerToken& tok) +{ + switch (type) + { + case Terminal::Hex: + case Terminal::Dec: + case Terminal::Oct: + case Terminal::Bin: + case Terminal::GPR: + case Terminal::FPR: + case Terminal::SPR: + case Terminal::CRField: + case Terminal::Lt: + case Terminal::Gt: + case Terminal::Eq: + case Terminal::So: + { + std::optional<u32> val = tok.EvalToken<u32>(); + ASSERT(val.has_value()); + AddLiteral(*val); + break; + } + + case Terminal::Dot: + AddLiteral(CurrentAddress()); + break; + + case Terminal::Id: + { + if (auto label_it = m_labels.find(tok.token_val); label_it != m_labels.end()) + { + AddLiteral(label_it->second - CurrentAddress()); + } + else if (auto var_it = m_constants.find(tok.token_val); var_it != m_constants.end()) + { + AddLiteral(var_it->second); + } + else + { + AddSymbolResolve(tok.token_val, false); + } + break; + } + + // Parser should disallow this from happening + default: + ASSERT(false); + break; + } +} + +void GekkoIRPlugin::EvalTerminalAbs(Terminal type, const AssemblerToken& tok) +{ + switch (type) + { + case Terminal::Hex: + case Terminal::Dec: + case Terminal::Oct: + case Terminal::Bin: + case Terminal::GPR: + case Terminal::FPR: + case Terminal::SPR: + case Terminal::CRField: + case Terminal::Lt: + case Terminal::Gt: + case Terminal::Eq: + case Terminal::So: + { + std::optional<u64> val = tok.EvalToken<u64>(); + ASSERT(val.has_value()); + m_eval_stack.push_back(*val); + break; + } + + case Terminal::Flt: + { + std::visit( + [&tok](auto&& vec) { + auto opt = tok.EvalToken<typename std::decay_t<decltype(vec)>::value_type>(); + ASSERT(opt.has_value()); + vec.push_back(*opt); + }, + m_floats_list); + break; + } + + case Terminal::Dot: + m_eval_stack.push_back(static_cast<u64>(CurrentAddress())); + break; + + case Terminal::Id: + { + if (auto label_it = m_labels.find(tok.token_val); label_it != m_labels.end()) + { + m_eval_stack.push_back(label_it->second); + } + else if (auto var_it = m_constants.find(tok.token_val); var_it != m_constants.end()) + { + m_eval_stack.push_back(var_it->second); + } + else + { + m_owner->EmitErrorHere( + fmt::format("Undefined reference to Label/Constant '{}'", tok.ValStr())); + return; + } + break; + } + + // Parser should disallow this from happening + default: + ASSERT(false); + break; + } +} +} // namespace + +u32 IRBlock::BlockEndAddress() const +{ + return std::accumulate(chunks.begin(), chunks.end(), block_address, + [](u32 acc, const ChunkVariant& chunk) { + size_t size; + if (std::holds_alternative<InstChunk>(chunk)) + { + size = std::get<InstChunk>(chunk).size() * 4; + } + else if (std::holds_alternative<ByteChunk>(chunk)) + { + size = std::get<ByteChunk>(chunk).size(); + } + else if (std::holds_alternative<PadChunk>(chunk)) + { + size = std::get<PadChunk>(chunk); + } + else + { + ASSERT(false); + size = 0; + } + + return acc + static_cast<u32>(size); + }); +} + +FailureOr<GekkoIR> ParseToIR(std::string_view assembly, u32 base_virtual_address) +{ + GekkoIR ret; + GekkoIRPlugin plugin(ret, base_virtual_address); + + ParseWithPlugin(&plugin, assembly); + + if (plugin.Error()) + { + return FailureOr<GekkoIR>(std::move(*plugin.Error())); + } + + return std::move(ret); +} + +} // namespace Common::GekkoAssembler::detail diff --git a/Source/Core/Common/Assembler/GekkoIRGen.h b/Source/Core/Common/Assembler/GekkoIRGen.h new file mode 100644 index 0000000000..4a1a56fa66 --- /dev/null +++ b/Source/Core/Common/Assembler/GekkoIRGen.h @@ -0,0 +1,50 @@ +// Copyright 2023 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <string_view> +#include <vector> + +#include "Common/Assembler/AssemblerShared.h" +#include "Common/Assembler/GekkoLexer.h" +#include "Common/CommonTypes.h" + +namespace Common::GekkoAssembler::detail +{ +struct GekkoInstruction +{ + // Combination of a mnemonic index and variant: + // (<GekkoMnemonic> << 2) | (<variant bits>) + size_t mnemonic_index = 0; + // Below refers to GekkoParseResult::operand_pool + Interval op_interval = Interval{0, 0}; + // Literal text of this instruction + std::string_view raw_text; + size_t line_number = 0; + bool is_extended = false; +}; + +using InstChunk = std::vector<GekkoInstruction>; +using ByteChunk = std::vector<u8>; +using PadChunk = size_t; +using ChunkVariant = std::variant<InstChunk, ByteChunk, PadChunk>; + +struct IRBlock +{ + explicit IRBlock(u32 address) : block_address(address) {} + + u32 BlockEndAddress() const; + + std::vector<ChunkVariant> chunks; + u32 block_address; +}; + +struct GekkoIR +{ + std::vector<IRBlock> blocks; + std::vector<Tagged<Interval, u32>> operand_pool; +}; + +FailureOr<GekkoIR> ParseToIR(std::string_view assembly, u32 base_virtual_address); +} // namespace Common::GekkoAssembler::detail diff --git a/Source/Core/Common/Assembler/GekkoLexer.cpp b/Source/Core/Common/Assembler/GekkoLexer.cpp new file mode 100644 index 0000000000..8947802ecb --- /dev/null +++ b/Source/Core/Common/Assembler/GekkoLexer.cpp @@ -0,0 +1,794 @@ +// Copyright 2023 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "Common/Assembler/GekkoLexer.h" + +#include "Common/Assert.h" + +#include <iterator> +#include <numeric> + +namespace Common::GekkoAssembler::detail +{ +namespace +{ +constexpr bool IsOctal(char c) +{ + return c >= '0' && c <= '7'; +} + +constexpr bool IsBinary(char c) +{ + return c == '0' || c == '1'; +} + +template <typename T> +constexpr T ConvertNib(char c) +{ + if (c >= 'a' && c <= 'f') + { + return static_cast<T>(c - 'a' + 10); + } + if (c >= 'A' && c <= 'F') + { + return static_cast<T>(c - 'A' + 10); + } + return static_cast<T>(c - '0'); +} + +constexpr TokenType SingleCharToken(char ch) +{ + switch (ch) + { + case ',': + return TokenType::Comma; + case '(': + return TokenType::Lparen; + case ')': + return TokenType::Rparen; + case '|': + return TokenType::Pipe; + case '^': + return TokenType::Caret; + case '&': + return TokenType::Ampersand; + case '+': + return TokenType::Plus; + case '-': + return TokenType::Minus; + case '*': + return TokenType::Star; + case '/': + return TokenType::Slash; + case '~': + return TokenType::Tilde; + case '@': + return TokenType::At; + case ':': + return TokenType::Colon; + case '`': + return TokenType::Grave; + case '.': + return TokenType::Dot; + case '\0': + return TokenType::Eof; + case '\n': + return TokenType::Eol; + default: + return TokenType::Invalid; + } +} + +// Convert a string literal into its raw-data form +template <typename Cont> +void ConvertStringLiteral(std::string_view literal, std::back_insert_iterator<Cont> out_it) +{ + for (size_t i = 1; i < literal.size() - 1;) + { + if (literal[i] == '\\') + { + ++i; + if (IsOctal(literal[i])) + { + // Octal escape + char octal_escape = 0; + for (char c = literal[i]; IsOctal(c); c = literal[++i]) + { + octal_escape = (octal_escape << 3) + (c - '0'); + } + out_it = static_cast<u8>(octal_escape); + } + else if (literal[i] == 'x') + { + // Hex escape + char hex_escape = 0; + for (char c = literal[++i]; std::isxdigit(c); c = literal[++i]) + { + hex_escape = (hex_escape << 4) + ConvertNib<char>(c); + } + out_it = static_cast<u8>(hex_escape); + } + else + { + char simple_escape; + switch (literal[i]) + { + case '\'': + simple_escape = '\x27'; + break; + case '"': + simple_escape = '\x22'; + break; + case '?': + simple_escape = '\x3f'; + break; + case '\\': + simple_escape = '\x5c'; + break; + case 'a': + simple_escape = '\x07'; + break; + case 'b': + simple_escape = '\x08'; + break; + case 'f': + simple_escape = '\x0c'; + break; + case 'n': + simple_escape = '\x0a'; + break; + case 'r': + simple_escape = '\x0d'; + break; + case 't': + simple_escape = '\x09'; + break; + case 'v': + simple_escape = '\x0b'; + break; + default: + simple_escape = literal[i]; + break; + } + out_it = static_cast<u8>(simple_escape); + ++i; + } + } + else + { + out_it = static_cast<u8>(literal[i]); + ++i; + } + } +} + +template <typename T> +std::optional<T> EvalIntegral(TokenType tp, std::string_view val) +{ + constexpr auto hex_step = [](T acc, char c) { return acc << 4 | ConvertNib<T>(c); }; + constexpr auto dec_step = [](T acc, char c) { return acc * 10 + (c - '0'); }; + constexpr auto oct_step = [](T acc, char c) { return acc << 3 | (c - '0'); }; + constexpr auto bin_step = [](T acc, char c) { return acc << 1 | (c - '0'); }; + + switch (tp) + { + case TokenType::HexadecimalLit: + return std::accumulate(val.begin() + 2, val.end(), T{0}, hex_step); + case TokenType::DecimalLit: + return std::accumulate(val.begin(), val.end(), T{0}, dec_step); + case TokenType::OctalLit: + return std::accumulate(val.begin() + 1, val.end(), T{0}, oct_step); + case TokenType::BinaryLit: + return std::accumulate(val.begin() + 2, val.end(), T{0}, bin_step); + case TokenType::GPR: + case TokenType::FPR: + return std::accumulate(val.begin() + 1, val.end(), T{0}, dec_step); + case TokenType::CRField: + return std::accumulate(val.begin() + 2, val.end(), T{0}, dec_step); + case TokenType::SPR: + return static_cast<T>(*sprg_map.Find(val)); + case TokenType::Lt: + return T{0}; + case TokenType::Gt: + return T{1}; + case TokenType::Eq: + return T{2}; + case TokenType::So: + return T{3}; + default: + return std::nullopt; + } +} +} // namespace + +void ConvertStringLiteral(std::string_view literal, std::vector<u8>* out_vec) +{ + ConvertStringLiteral(literal, std::back_inserter(*out_vec)); +} + +std::string_view TokenTypeToStr(TokenType tp) +{ + switch (tp) + { + case TokenType::GPR: + return "GPR"; + case TokenType::FPR: + return "FPR"; + case TokenType::SPR: + return "SPR"; + case TokenType::CRField: + return "CR Field"; + case TokenType::Lt: + case TokenType::Gt: + case TokenType::Eq: + case TokenType::So: + return "CR Bit"; + case TokenType::Identifier: + return "Identifier"; + case TokenType::StringLit: + return "String Literal"; + case TokenType::DecimalLit: + return "Decimal Literal"; + case TokenType::BinaryLit: + return "Binary Literal"; + case TokenType::HexadecimalLit: + return "Hexadecimal Literal"; + case TokenType::OctalLit: + return "Octal Literal"; + case TokenType::FloatLit: + return "Float Literal"; + case TokenType::Invalid: + return "Invalid"; + case TokenType::Lsh: + return "<<"; + case TokenType::Rsh: + return ">>"; + case TokenType::Comma: + return ","; + case TokenType::Lparen: + return "("; + case TokenType::Rparen: + return ")"; + case TokenType::Pipe: + return "|"; + case TokenType::Caret: + return "^"; + case TokenType::Ampersand: + return "&"; + case TokenType::Plus: + return "+"; + case TokenType::Minus: + return "-"; + case TokenType::Star: + return "*"; + case TokenType::Slash: + return "/"; + case TokenType::Tilde: + return "~"; + case TokenType::At: + return "@"; + case TokenType::Colon: + return ":"; + case TokenType::Grave: + return "`"; + case TokenType::Dot: + return "."; + case TokenType::Eof: + return "End of File"; + case TokenType::Eol: + return "End of Line"; + default: + return ""; + } +} + +std::string_view AssemblerToken::TypeStr() const +{ + return TokenTypeToStr(token_type); +} + +std::string_view AssemblerToken::ValStr() const +{ + switch (token_type) + { + case TokenType::Eol: + return "<EOL>"; + case TokenType::Eof: + return "<EOF>"; + default: + return token_val; + } +} + +template <> +std::optional<float> AssemblerToken::EvalToken() const +{ + if (token_type == TokenType::FloatLit) + { + return std::stof(std::string(token_val)); + } + return std::nullopt; +} + +template <> +std::optional<double> AssemblerToken::EvalToken() const +{ + if (token_type == TokenType::FloatLit) + { + return std::stod(std::string(token_val)); + } + return std::nullopt; +} + +template <> +std::optional<u8> AssemblerToken::EvalToken() const +{ + return EvalIntegral<u8>(token_type, token_val); +} + +template <> +std::optional<u16> AssemblerToken::EvalToken() const +{ + return EvalIntegral<u16>(token_type, token_val); +} + +template <> +std::optional<u32> AssemblerToken::EvalToken() const +{ + return EvalIntegral<u32>(token_type, token_val); +} + +template <> +std::optional<u64> AssemblerToken::EvalToken() const +{ + return EvalIntegral<u64>(token_type, token_val); +} + +size_t Lexer::LineNumber() const +{ + return m_lexed_tokens.empty() ? m_pos.line : TagOf(m_lexed_tokens.front()).line; +} + +size_t Lexer::ColNumber() const +{ + return m_lexed_tokens.empty() ? m_pos.col : TagOf(m_lexed_tokens.front()).col; +} + +std::string_view Lexer::CurrentLine() const +{ + const size_t line_index = + m_lexed_tokens.empty() ? m_pos.index : TagOf(m_lexed_tokens.front()).index; + size_t begin_index = line_index == 0 ? 0 : line_index - 1; + for (; begin_index > 0; begin_index--) + { + if (m_lex_string[begin_index] == '\n') + { + begin_index++; + break; + } + } + size_t end_index = begin_index; + for (; end_index < m_lex_string.size(); end_index++) + { + if (m_lex_string[end_index] == '\n') + { + end_index++; + break; + } + } + return m_lex_string.substr(begin_index, end_index - begin_index); +} + +void Lexer::SetIdentifierMatchRule(IdentifierMatchRule set) +{ + FeedbackTokens(); + m_match_rule = set; +} + +const Tagged<CursorPosition, AssemblerToken>& Lexer::LookaheadTagRef(size_t num_fwd) const +{ + while (m_lexed_tokens.size() < num_fwd) + { + LookaheadRef(); + } + return m_lexed_tokens[num_fwd]; +} + +AssemblerToken Lexer::Lookahead() const +{ + if (m_lexed_tokens.empty()) + { + CursorPosition pos_pre = m_pos; + m_lexed_tokens.emplace_back(pos_pre, LexSingle()); + } + return ValueOf(m_lexed_tokens.front()); +} + +const AssemblerToken& Lexer::LookaheadRef() const +{ + if (m_lexed_tokens.empty()) + { + CursorPosition pos_pre = m_pos; + m_lexed_tokens.emplace_back(pos_pre, LexSingle()); + } + return ValueOf(m_lexed_tokens.front()); +} + +TokenType Lexer::LookaheadType() const +{ + return LookaheadRef().token_type; +} + +AssemblerToken Lexer::LookaheadFloat() const +{ + FeedbackTokens(); + SkipWs(); + + CursorPosition pos_pre = m_pos; + ScanStart(); + + std::optional<std::string_view> failure_reason = RunDfa(float_dfa); + + // Special case: lex at least a single char for no matches for errors to make sense + if (m_scan_pos.index == pos_pre.index) + { + Step(); + } + + std::string_view tok_str = ScanFinishOut(); + AssemblerToken tok; + if (!failure_reason) + { + tok = AssemblerToken{ + TokenType::FloatLit, + tok_str, + "", + Interval{0, 0}, + }; + } + else + { + tok = AssemblerToken{ + TokenType::Invalid, + tok_str, + *failure_reason, + Interval{0, tok_str.length()}, + }; + } + + m_lexed_tokens.emplace_back(pos_pre, tok); + return tok; +} + +void Lexer::Eat() +{ + if (m_lexed_tokens.empty()) + { + LexSingle(); + } + else + { + m_lexed_tokens.pop_front(); + } +} + +void Lexer::EatAndReset() +{ + Eat(); + SetIdentifierMatchRule(IdentifierMatchRule::Typical); +} + +std::optional<std::string_view> Lexer::RunDfa(const std::vector<DfaNode>& dfa) const +{ + size_t dfa_index = 0; + bool transition_found; + do + { + transition_found = false; + if (Peek() == '\0') + { + break; + } + + const DfaNode& n = dfa[dfa_index]; + for (auto&& edge : n.edges) + { + if (edge.first(Peek())) + { + transition_found = true; + dfa_index = edge.second; + break; + } + } + + if (transition_found) + { + Step(); + } + } while (transition_found); + + return dfa[dfa_index].match_failure_reason; +} + +void Lexer::SkipWs() const +{ + ScanStart(); + for (char c = Peek(); std::isspace(c) && c != '\n'; c = Step().Peek()) + { + } + if (Peek() == '#') + { + while (Peek() != '\n' && Peek() != '\0') + { + Step(); + } + } + ScanFinish(); +} + +void Lexer::FeedbackTokens() const +{ + if (m_lexed_tokens.empty()) + { + return; + } + m_pos = m_scan_pos = TagOf(m_lexed_tokens.front()); + m_lexed_tokens.clear(); +} + +bool Lexer::IdentifierHeadExtra(char h) const +{ + switch (m_match_rule) + { + case IdentifierMatchRule::Typical: + case IdentifierMatchRule::Mnemonic: + return false; + case IdentifierMatchRule::Directive: + return std::isdigit(h); + } + return false; +} + +bool Lexer::IdentifierExtra(char c) const +{ + switch (m_match_rule) + { + case IdentifierMatchRule::Typical: + case IdentifierMatchRule::Directive: + return false; + case IdentifierMatchRule::Mnemonic: + return c == '+' || c == '-' || c == '.'; + } + return false; +} + +void Lexer::ScanStart() const +{ + m_scan_pos = m_pos; +} + +void Lexer::ScanFinish() const +{ + m_pos = m_scan_pos; +} + +std::string_view Lexer::ScanFinishOut() const +{ + const size_t start = m_pos.index; + m_pos = m_scan_pos; + return m_lex_string.substr(start, m_scan_pos.index - start); +} + +char Lexer::Peek() const +{ + if (m_scan_pos.index >= m_lex_string.length()) + { + return 0; + } + return m_lex_string[m_scan_pos.index]; +} + +const Lexer& Lexer::Step() const +{ + if (m_scan_pos.index >= m_lex_string.length()) + { + return *this; + } + + if (Peek() == '\n') + { + m_scan_pos.line++; + m_scan_pos.col = 0; + } + else + { + m_scan_pos.col++; + } + m_scan_pos.index++; + return *this; +} + +TokenType Lexer::LexStringLit(std::string_view& invalid_reason, Interval& invalid_region) const +{ + // The open quote has alread been matched + const size_t string_start = m_scan_pos.index - 1; + TokenType token_type = TokenType::StringLit; + + std::optional<std::string_view> failure_reason = RunDfa(string_dfa); + + if (failure_reason) + { + token_type = TokenType::Invalid; + invalid_reason = *failure_reason; + invalid_region = Interval{0, m_scan_pos.index - string_start}; + } + + return token_type; +} + +TokenType Lexer::ClassifyAlnum() const +{ + const std::string_view alnum = m_lex_string.substr(m_pos.index, m_scan_pos.index - m_pos.index); + constexpr auto valid_regnum = [](std::string_view rn) { + if (rn.length() == 1 && std::isdigit(rn[0])) + { + return true; + } + else if (rn.length() == 2 && std::isdigit(rn[0]) && std::isdigit(rn[1])) + { + if (rn[0] == '1' || rn[0] == '2') + { + return true; + } + + if (rn[0] == '3') + { + return rn[1] <= '2'; + } + } + + return false; + }; + constexpr auto eq_nocase = [](std::string_view str, std::string_view lwr) { + auto it_l = str.cbegin(), it_r = lwr.cbegin(); + for (; it_l != str.cend() && it_r != lwr.cend(); it_l++, it_r++) + { + if (std::tolower(*it_l) != *it_r) + { + return false; + } + } + return it_l == str.end() && it_r == lwr.end(); + }; + + if (std::tolower(alnum[0]) == 'r' && valid_regnum(alnum.substr(1))) + { + return TokenType::GPR; + } + else if (std::tolower(alnum[0]) == 'f' && valid_regnum(alnum.substr(1))) + { + return TokenType::FPR; + } + else if (alnum.length() == 3 && eq_nocase(alnum.substr(0, 2), "cr") && alnum[2] >= '0' && + alnum[2] <= '7') + { + return TokenType::CRField; + } + else if (eq_nocase(alnum, "lt")) + { + return TokenType::Lt; + } + else if (eq_nocase(alnum, "gt")) + { + return TokenType::Gt; + } + else if (eq_nocase(alnum, "eq")) + { + return TokenType::Eq; + } + else if (eq_nocase(alnum, "so")) + { + return TokenType::So; + } + else if (sprg_map.Find(alnum) != nullptr) + { + return TokenType::SPR; + } + return TokenType::Identifier; +} + +AssemblerToken Lexer::LexSingle() const +{ + SkipWs(); + + ScanStart(); + const char h = Peek(); + + TokenType token_type; + std::string_view invalid_reason = ""; + Interval invalid_region = Interval{0, 0}; + + Step(); + + if (std::isalpha(h) || h == '_' || IdentifierHeadExtra(h)) + { + for (char c = Peek(); std::isalnum(c) || c == '_' || IdentifierExtra(c); c = Step().Peek()) + { + } + + token_type = ClassifyAlnum(); + } + else if (h == '"') + { + token_type = LexStringLit(invalid_reason, invalid_region); + } + else if (h == '0') + { + const char imm_type = Peek(); + + if (imm_type == 'x') + { + token_type = TokenType::HexadecimalLit; + Step(); + for (char c = Peek(); std::isxdigit(c); c = Step().Peek()) + { + } + } + else if (imm_type == 'b') + { + token_type = TokenType::BinaryLit; + Step(); + for (char c = Peek(); IsBinary(c); c = Step().Peek()) + { + } + } + else if (IsOctal(imm_type)) + { + token_type = TokenType::OctalLit; + for (char c = Peek(); IsOctal(c); c = Step().Peek()) + { + } + } + else + { + token_type = TokenType::DecimalLit; + } + } + else if (std::isdigit(h)) + { + for (char c = Peek(); std::isdigit(c); c = Step().Peek()) + { + } + token_type = TokenType::DecimalLit; + } + else if (h == '<' || h == '>') + { + // Special case for two-character operators + const char second_ch = Peek(); + if (second_ch == h) + { + Step(); + token_type = second_ch == '<' ? TokenType::Lsh : TokenType::Rsh; + } + else + { + token_type = TokenType::Invalid; + invalid_reason = "Unrecognized character"; + invalid_region = Interval{0, 1}; + } + } + else + { + token_type = SingleCharToken(h); + if (token_type == TokenType::Invalid) + { + invalid_reason = "Unrecognized character"; + invalid_region = Interval{0, 1}; + } + } + + AssemblerToken new_tok = {token_type, ScanFinishOut(), invalid_reason, invalid_region}; + SkipWs(); + return new_tok; +} +} // namespace Common::GekkoAssembler::detail diff --git a/Source/Core/Common/Assembler/GekkoLexer.h b/Source/Core/Common/Assembler/GekkoLexer.h new file mode 100644 index 0000000000..9ff78c04d0 --- /dev/null +++ b/Source/Core/Common/Assembler/GekkoLexer.h @@ -0,0 +1,188 @@ +// Copyright 2023 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <algorithm> +#include <array> +#include <deque> +#include <optional> +#include <string_view> +#include <type_traits> +#include <vector> + +#include "Common/Assembler/AssemblerShared.h" +#include "Common/Assembler/AssemblerTables.h" +#include "Common/CommonTypes.h" + +namespace Common::GekkoAssembler::detail +{ +void ConvertStringLiteral(std::string_view literal, std::vector<u8>* out_vec); + +enum class TokenType +{ + Invalid, + Identifier, + StringLit, + HexadecimalLit, + DecimalLit, + OctalLit, + BinaryLit, + FloatLit, + GPR, + FPR, + CRField, + SPR, + Lt, + Gt, + Eq, + So, + // EOL signifies boundaries between instructions, a la ';' + Eol, + Eof, + + Dot, + Colon, + Comma, + Lparen, + Rparen, + Pipe, + Caret, + Ampersand, + Lsh, + Rsh, + Plus, + Minus, + Star, + Slash, + Tilde, + Grave, + At, + + OperatorBegin = Dot, + LastToken = At, +}; + +std::string_view TokenTypeToStr(TokenType); + +struct AssemblerToken +{ + TokenType token_type; + std::string_view token_val; + std::string_view invalid_reason; + // Within an invalid token, specifies the erroneous region + Interval invalid_region; + + std::string_view TypeStr() const; + std::string_view ValStr() const; + + // Supported Templates: + // u8, u16, u32, u64, float, double + template <typename T> + std::optional<T> EvalToken() const; +}; + +struct CursorPosition +{ + size_t index = 0; + size_t line = 0; + size_t col = 0; +}; + +class Lexer +{ +public: + enum class IdentifierMatchRule + { + Typical, + Mnemonic, // Mnemonics can contain +, -, or . to specify branch prediction rules and link bit + Directive, // Directives can start with a digit + }; + +public: + explicit Lexer(std::string_view str) + : m_lex_string(str), m_match_rule(IdentifierMatchRule::Typical) + { + } + + size_t LineNumber() const; + size_t ColNumber() const; + std::string_view CurrentLine() const; + + // Since there's only one place floats get lexed, it's 'okay' to have an explicit + // "lex a float token" function + void SetIdentifierMatchRule(IdentifierMatchRule set); + const Tagged<CursorPosition, AssemblerToken>& LookaheadTagRef(size_t num_fwd) const; + AssemblerToken Lookahead() const; + const AssemblerToken& LookaheadRef() const; + TokenType LookaheadType() const; + // Since there's only one place floats get lexed, it's 'okay' to have an explicit + // "lex a float token" function + AssemblerToken LookaheadFloat() const; + void Eat(); + void EatAndReset(); + + template <size_t N> + void LookaheadTaggedN(std::array<Tagged<CursorPosition, AssemblerToken>, N>* tokens_out) const + { + const size_t filled_amt = std::min(m_lexed_tokens.size(), N); + + std::copy_n(m_lexed_tokens.begin(), filled_amt, tokens_out->begin()); + + std::generate_n(tokens_out->begin() + filled_amt, N - filled_amt, [this] { + CursorPosition p = m_pos; + return m_lexed_tokens.emplace_back(p, LexSingle()); + }); + } + + template <size_t N> + void LookaheadN(std::array<AssemblerToken, N>* tokens_out) const + { + const size_t filled_amt = std::min(m_lexed_tokens.size(), N); + + auto _it = m_lexed_tokens.begin(); + std::generate_n(tokens_out->begin(), filled_amt, [&_it] { return ValueOf(*_it++); }); + + std::generate_n(tokens_out->begin() + filled_amt, N - filled_amt, [this] { + CursorPosition p = m_pos; + return ValueOf(m_lexed_tokens.emplace_back(p, LexSingle())); + }); + } + + template <size_t N> + void EatN() + { + size_t consumed = 0; + while (m_lexed_tokens.size() > 0 && consumed < N) + { + m_lexed_tokens.pop_front(); + consumed++; + } + for (size_t i = consumed; i < N; i++) + { + LexSingle(); + } + } + +private: + std::optional<std::string_view> RunDfa(const std::vector<DfaNode>& dfa) const; + void SkipWs() const; + void FeedbackTokens() const; + bool IdentifierHeadExtra(char h) const; + bool IdentifierExtra(char c) const; + void ScanStart() const; + void ScanFinish() const; + std::string_view ScanFinishOut() const; + char Peek() const; + const Lexer& Step() const; + TokenType LexStringLit(std::string_view& invalid_reason, Interval& invalid_region) const; + TokenType ClassifyAlnum() const; + AssemblerToken LexSingle() const; + + std::string_view m_lex_string; + mutable CursorPosition m_pos; + mutable CursorPosition m_scan_pos; + mutable std::deque<Tagged<CursorPosition, AssemblerToken>> m_lexed_tokens; + IdentifierMatchRule m_match_rule; +}; +} // namespace Common::GekkoAssembler::detail diff --git a/Source/Core/Common/Assembler/GekkoParser.cpp b/Source/Core/Common/Assembler/GekkoParser.cpp new file mode 100644 index 0000000000..26b5bb1082 --- /dev/null +++ b/Source/Core/Common/Assembler/GekkoParser.cpp @@ -0,0 +1,885 @@ +// Copyright 2023 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "Common/Assembler/GekkoParser.h" + +#include <algorithm> +#include <array> +#include <functional> + +#include <fmt/format.h> + +#include "Common/Assembler/AssemblerShared.h" +#include "Common/Assembler/AssemblerTables.h" +#include "Common/Assembler/GekkoLexer.h" +#include "Common/Assert.h" + +namespace Common::GekkoAssembler::detail +{ +namespace +{ +bool MatchOperandFirst(const AssemblerToken& tok) +{ + switch (tok.token_type) + { + case TokenType::Minus: + case TokenType::Tilde: + case TokenType::Lparen: + case TokenType::Grave: + case TokenType::Identifier: + case TokenType::DecimalLit: + case TokenType::OctalLit: + case TokenType::HexadecimalLit: + case TokenType::BinaryLit: + case TokenType::Dot: + return true; + default: + return false; + } +} + +void ParseImm(ParseState* state) +{ + AssemblerToken tok = state->lexer.Lookahead(); + switch (tok.token_type) + { + case TokenType::HexadecimalLit: + state->plugin.OnTerminal(Terminal::Hex, tok); + break; + case TokenType::DecimalLit: + state->plugin.OnTerminal(Terminal::Dec, tok); + break; + case TokenType::OctalLit: + state->plugin.OnTerminal(Terminal::Oct, tok); + break; + case TokenType::BinaryLit: + state->plugin.OnTerminal(Terminal::Bin, tok); + break; + default: + state->EmitErrorHere(fmt::format("Invalid {} with value '{}'", tok.TypeStr(), tok.ValStr())); + return; + } + if (state->error) + { + return; + } + state->lexer.Eat(); +} + +void ParseId(ParseState* state) +{ + AssemblerToken tok = state->lexer.Lookahead(); + if (tok.token_type == TokenType::Identifier) + { + state->plugin.OnTerminal(Terminal::Id, tok); + if (state->error) + { + return; + } + state->lexer.Eat(); + } + else + { + state->EmitErrorHere(fmt::format("Expected an identifier, but found '{}'", tok.ValStr())); + } +} + +void ParseIdLocation(ParseState* state) +{ + std::array<AssemblerToken, 3> toks; + state->lexer.LookaheadN(&toks); + + if (toks[1].token_type == TokenType::At) + { + if (toks[2].token_val == "ha") + { + state->plugin.OnHiaddr(toks[0].token_val); + if (state->error) + { + return; + } + state->lexer.EatN<3>(); + return; + } + else if (toks[2].token_val == "l") + { + state->plugin.OnLoaddr(toks[0].token_val); + if (state->error) + { + return; + } + state->lexer.EatN<3>(); + return; + } + } + + ParseId(state); +} + +void ParsePpcBuiltin(ParseState* state) +{ + AssemblerToken tok = state->lexer.Lookahead(); + switch (tok.token_type) + { + case TokenType::GPR: + state->plugin.OnTerminal(Terminal::GPR, tok); + break; + case TokenType::FPR: + state->plugin.OnTerminal(Terminal::FPR, tok); + break; + case TokenType::SPR: + state->plugin.OnTerminal(Terminal::SPR, tok); + break; + case TokenType::CRField: + state->plugin.OnTerminal(Terminal::CRField, tok); + break; + case TokenType::Lt: + state->plugin.OnTerminal(Terminal::Lt, tok); + break; + case TokenType::Gt: + state->plugin.OnTerminal(Terminal::Gt, tok); + break; + case TokenType::Eq: + state->plugin.OnTerminal(Terminal::Eq, tok); + break; + case TokenType::So: + state->plugin.OnTerminal(Terminal::So, tok); + break; + default: + state->EmitErrorHere( + fmt::format("Unexpected token '{}' in ppc builtin", state->lexer.LookaheadRef().ValStr())); + break; + } + if (state->error) + { + return; + } + state->lexer.Eat(); +} + +void ParseBaseexpr(ParseState* state) +{ + TokenType tok = state->lexer.LookaheadType(); + switch (tok) + { + case TokenType::HexadecimalLit: + case TokenType::DecimalLit: + case TokenType::OctalLit: + case TokenType::BinaryLit: + ParseImm(state); + break; + + case TokenType::Identifier: + ParseIdLocation(state); + break; + + case TokenType::GPR: + case TokenType::FPR: + case TokenType::SPR: + case TokenType::CRField: + case TokenType::Lt: + case TokenType::Gt: + case TokenType::Eq: + case TokenType::So: + ParsePpcBuiltin(state); + break; + + case TokenType::Dot: + state->plugin.OnTerminal(Terminal::Dot, state->lexer.Lookahead()); + if (state->error) + { + return; + } + state->lexer.Eat(); + break; + + default: + state->EmitErrorHere( + fmt::format("Unexpected token '{}' in expression", state->lexer.LookaheadRef().ValStr())); + break; + } +} + +void ParseBitor(ParseState* state); +void ParseParen(ParseState* state) +{ + if (state->HasToken(TokenType::Lparen)) + { + state->plugin.OnOpenParen(ParenType::Normal); + if (state->error) + { + return; + } + + state->lexer.Eat(); + ParseBitor(state); + if (state->error) + { + return; + } + + if (state->HasToken(TokenType::Rparen)) + { + state->plugin.OnCloseParen(ParenType::Normal); + } + state->ParseToken(TokenType::Rparen); + } + else if (state->HasToken(TokenType::Grave)) + { + state->plugin.OnOpenParen(ParenType::RelConv); + + state->lexer.Eat(); + ParseBitor(state); + if (state->error) + { + return; + } + + if (state->HasToken(TokenType::Grave)) + { + state->plugin.OnCloseParen(ParenType::RelConv); + } + state->ParseToken(TokenType::Grave); + } + else + { + ParseBaseexpr(state); + } +} + +void ParseUnary(ParseState* state) +{ + TokenType tok = state->lexer.LookaheadType(); + if (tok == TokenType::Minus || tok == TokenType::Tilde) + { + state->lexer.Eat(); + ParseUnary(state); + if (state->error) + { + return; + } + + if (tok == TokenType::Minus) + { + state->plugin.OnOperator(AsmOp::Neg); + } + else + { + state->plugin.OnOperator(AsmOp::Not); + } + } + else + { + ParseParen(state); + } +} + +void ParseMultiplication(ParseState* state) +{ + ParseUnary(state); + if (state->error) + { + return; + } + + TokenType tok = state->lexer.LookaheadType(); + while (tok == TokenType::Star || tok == TokenType::Slash) + { + state->lexer.Eat(); + ParseUnary(state); + if (state->error) + { + return; + } + + if (tok == TokenType::Star) + { + state->plugin.OnOperator(AsmOp::Mul); + } + else + { + state->plugin.OnOperator(AsmOp::Div); + } + tok = state->lexer.LookaheadType(); + } +} + +void ParseAddition(ParseState* state) +{ + ParseMultiplication(state); + if (state->error) + { + return; + } + + TokenType tok = state->lexer.LookaheadType(); + while (tok == TokenType::Plus || tok == TokenType::Minus) + { + state->lexer.Eat(); + ParseMultiplication(state); + if (state->error) + { + return; + } + + if (tok == TokenType::Plus) + { + state->plugin.OnOperator(AsmOp::Add); + } + else + { + state->plugin.OnOperator(AsmOp::Sub); + } + tok = state->lexer.LookaheadType(); + } +} + +void ParseShift(ParseState* state) +{ + ParseAddition(state); + if (state->error) + { + return; + } + + TokenType tok = state->lexer.LookaheadType(); + while (tok == TokenType::Lsh || tok == TokenType::Rsh) + { + state->lexer.Eat(); + ParseAddition(state); + if (state->error) + { + return; + } + + if (tok == TokenType::Lsh) + { + state->plugin.OnOperator(AsmOp::Lsh); + } + else + { + state->plugin.OnOperator(AsmOp::Rsh); + } + tok = state->lexer.LookaheadType(); + } +} + +void ParseBitand(ParseState* state) +{ + ParseShift(state); + if (state->error) + { + return; + } + + while (state->HasToken(TokenType::Ampersand)) + { + state->lexer.Eat(); + ParseShift(state); + if (state->error) + { + return; + } + + state->plugin.OnOperator(AsmOp::And); + } +} + +void ParseBitxor(ParseState* state) +{ + ParseBitand(state); + if (state->error) + { + return; + } + + while (state->HasToken(TokenType::Caret)) + { + state->lexer.Eat(); + ParseBitand(state); + if (state->error) + { + return; + } + + state->plugin.OnOperator(AsmOp::Xor); + } +} + +void ParseBitor(ParseState* state) +{ + ParseBitxor(state); + if (state->error) + { + return; + } + + while (state->HasToken(TokenType::Pipe)) + { + state->lexer.Eat(); + ParseBitxor(state); + if (state->error) + { + return; + } + + state->plugin.OnOperator(AsmOp::Or); + } +} + +void ParseOperand(ParseState* state) +{ + state->plugin.OnOperandPre(); + ParseBitor(state); + if (state->error) + { + return; + } + state->plugin.OnOperandPost(); +} + +void ParseOperandList(ParseState* state, ParseAlg alg) +{ + if (alg == ParseAlg::None) + { + return; + } + if (alg == ParseAlg::NoneOrOp1) + { + if (MatchOperandFirst(state->lexer.Lookahead())) + { + ParseOperand(state); + } + return; + } + + enum ParseStep + { + _Operand, + _Comma, + _Lparen, + _Rparen, + _OptComma + }; + std::vector<ParseStep> steps; + + switch (alg) + { + case ParseAlg::Op1: + steps = {_Operand}; + break; + case ParseAlg::Op1Or2: + steps = {_Operand, _OptComma, _Operand}; + break; + case ParseAlg::Op2Or3: + steps = {_Operand, _Comma, _Operand, _OptComma, _Operand}; + break; + case ParseAlg::Op1Off1: + steps = {_Operand, _Comma, _Operand, _Lparen, _Operand, _Rparen}; + break; + case ParseAlg::Op2: + steps = {_Operand, _Comma, _Operand}; + break; + case ParseAlg::Op3: + steps = {_Operand, _Comma, _Operand, _Comma, _Operand}; + break; + case ParseAlg::Op4: + steps = {_Operand, _Comma, _Operand, _Comma, _Operand, _Comma, _Operand}; + break; + case ParseAlg::Op5: + steps = {_Operand, _Comma, _Operand, _Comma, _Operand, _Comma, _Operand, _Comma, _Operand}; + break; + case ParseAlg::Op1Off1Op2: + steps = {_Operand, _Comma, _Operand, _Lparen, _Operand, + _Rparen, _Comma, _Operand, _Comma, _Operand}; + break; + default: + ASSERT(false); + return; + } + + for (ParseStep step : steps) + { + bool stop_parse = false; + switch (step) + { + case _Operand: + ParseOperand(state); + break; + case _Comma: + state->ParseToken(TokenType::Comma); + break; + case _Lparen: + state->ParseToken(TokenType::Lparen); + break; + case _Rparen: + state->ParseToken(TokenType::Rparen); + break; + case _OptComma: + if (state->HasToken(TokenType::Comma)) + { + state->ParseToken(TokenType::Comma); + } + else + { + stop_parse = true; + } + break; + } + if (state->error) + { + return; + } + if (stop_parse) + { + break; + } + } +} + +void ParseInstruction(ParseState* state) +{ + state->lexer.SetIdentifierMatchRule(Lexer::IdentifierMatchRule::Mnemonic); + + AssemblerToken mnemonic_token = state->lexer.Lookahead(); + if (mnemonic_token.token_type != TokenType::Identifier) + { + state->lexer.SetIdentifierMatchRule(Lexer::IdentifierMatchRule::Typical); + return; + } + + ParseInfo const* parse_info = mnemonic_tokens.Find(mnemonic_token.token_val); + bool is_extended = false; + if (parse_info == nullptr) + { + parse_info = extended_mnemonic_tokens.Find(mnemonic_token.token_val); + if (parse_info == nullptr) + { + state->EmitErrorHere( + fmt::format("Unknown or unsupported mnemonic '{}'", mnemonic_token.ValStr())); + return; + } + is_extended = true; + } + + state->plugin.OnInstructionPre(*parse_info, is_extended); + + state->lexer.EatAndReset(); + + ParseOperandList(state, parse_info->parse_algorithm); + if (state->error) + { + return; + } + + state->plugin.OnInstructionPost(*parse_info, is_extended); +} + +void ParseLabel(ParseState* state) +{ + std::array<AssemblerToken, 2> tokens; + state->lexer.LookaheadN(&tokens); + + if (tokens[0].token_type == TokenType::Identifier && tokens[1].token_type == TokenType::Colon) + { + state->plugin.OnLabelDecl(tokens[0].token_val); + if (state->error) + { + return; + } + state->lexer.EatN<2>(); + } +} + +void ParseResolvedExpr(ParseState* state) +{ + state->plugin.OnResolvedExprPre(); + ParseBitor(state); + if (state->error) + { + return; + } + state->plugin.OnResolvedExprPost(); +} + +void ParseExpressionList(ParseState* state) +{ + ParseResolvedExpr(state); + if (state->error) + { + return; + } + + while (state->HasToken(TokenType::Comma)) + { + state->lexer.Eat(); + ParseResolvedExpr(state); + if (state->error) + { + return; + } + } +} + +void ParseFloat(ParseState* state) +{ + AssemblerToken flt_token = state->lexer.LookaheadFloat(); + if (flt_token.token_type != TokenType::FloatLit) + { + state->EmitErrorHere("Invalid floating point literal"); + return; + } + state->plugin.OnTerminal(Terminal::Flt, flt_token); + state->lexer.Eat(); +} + +void ParseFloatList(ParseState* state) +{ + ParseFloat(state); + if (state->error) + { + return; + } + + while (state->HasToken(TokenType::Comma)) + { + state->lexer.Eat(); + ParseFloat(state); + if (state->error) + { + return; + } + } +} + +void ParseDefvar(ParseState* state) +{ + AssemblerToken tok = state->lexer.Lookahead(); + if (tok.token_type == TokenType::Identifier) + { + state->plugin.OnVarDecl(tok.token_val); + if (state->error) + { + return; + } + state->lexer.Eat(); + + state->ParseToken(TokenType::Comma); + if (state->error) + { + return; + } + + ParseResolvedExpr(state); + } + else + { + state->EmitErrorHere(fmt::format("Expected an identifier, but found '{}'", tok.ValStr())); + } +} + +void ParseString(ParseState* state) +{ + AssemblerToken tok = state->lexer.Lookahead(); + if (tok.token_type == TokenType::StringLit) + { + state->plugin.OnTerminal(Terminal::Str, tok); + state->lexer.Eat(); + } + else + { + state->EmitErrorHere(fmt::format("Expected a string literal, but found '{}'", tok.ValStr())); + } +} + +void ParseDirective(ParseState* state) +{ + // TODO: test directives + state->lexer.SetIdentifierMatchRule(Lexer::IdentifierMatchRule::Directive); + AssemblerToken tok = state->lexer.Lookahead(); + if (tok.token_type != TokenType::Identifier) + { + state->EmitErrorHere(fmt::format("Unexpected token '{}' in directive type", tok.ValStr())); + return; + } + + GekkoDirective const* directive_enum = directives_map.Find(tok.token_val); + if (directive_enum == nullptr) + { + state->EmitErrorHere(fmt::format("Unknown assembler directive '{}'", tok.ValStr())); + return; + } + + state->plugin.OnDirectivePre(*directive_enum); + + state->lexer.EatAndReset(); + switch (*directive_enum) + { + case GekkoDirective::Byte: + case GekkoDirective::_2byte: + case GekkoDirective::_4byte: + case GekkoDirective::_8byte: + ParseExpressionList(state); + break; + + case GekkoDirective::Float: + case GekkoDirective::Double: + ParseFloatList(state); + break; + + case GekkoDirective::Locate: + case GekkoDirective::Zeros: + case GekkoDirective::Skip: + ParseResolvedExpr(state); + break; + + case GekkoDirective::PadAlign: + case GekkoDirective::Align: + ParseImm(state); + break; + + case GekkoDirective::DefVar: + ParseDefvar(state); + break; + + case GekkoDirective::Ascii: + case GekkoDirective::Asciz: + ParseString(state); + break; + } + + if (state->error) + { + return; + } + + state->plugin.OnDirectivePost(*directive_enum); +} + +void ParseLine(ParseState* state) +{ + if (state->HasToken(TokenType::Dot)) + { + state->ParseToken(TokenType::Dot); + ParseDirective(state); + } + else + { + ParseInstruction(state); + } +} + +void ParseProgram(ParseState* state) +{ + AssemblerToken tok = state->lexer.Lookahead(); + if (tok.token_type == TokenType::Eof) + { + state->eof = true; + return; + } + ParseLabel(state); + if (state->error) + { + return; + } + ParseLine(state); + if (state->error) + { + return; + } + + while (!state->eof && !state->error) + { + tok = state->lexer.Lookahead(); + if (tok.token_type == TokenType::Eof) + { + state->eof = true; + } + else if (tok.token_type == TokenType::Eol) + { + state->lexer.Eat(); + ParseLabel(state); + if (state->error) + { + return; + } + ParseLine(state); + } + else + { + state->EmitErrorHere( + fmt::format("Unexpected token '{}' where line should have ended", tok.ValStr())); + } + } +} +} // namespace + +ParseState::ParseState(std::string_view input_str, ParsePlugin& p) + : lexer(input_str), plugin(p), eof(false) +{ +} + +bool ParseState::HasToken(TokenType tp) const +{ + return lexer.LookaheadType() == tp; +} + +void ParseState::ParseToken(TokenType tp) +{ + AssemblerToken tok = lexer.LookaheadRef(); + if (tok.token_type == tp) + { + lexer.Eat(); + } + else + { + EmitErrorHere(fmt::format("Expected '{}' but found '{}'", TokenTypeToStr(tp), tok.ValStr())); + } +} + +void ParseState::EmitErrorHere(std::string&& message) +{ + AssemblerToken cur_token = lexer.Lookahead(); + if (cur_token.token_type == TokenType::Invalid) + { + error = AssemblerError{ + std::string(cur_token.invalid_reason), + lexer.CurrentLine(), + lexer.LineNumber(), + lexer.ColNumber() + cur_token.invalid_region.begin, + cur_token.invalid_region.len, + }; + } + else + { + error = AssemblerError{ + std::move(message), lexer.CurrentLine(), lexer.LineNumber(), + lexer.ColNumber(), cur_token.token_val.size(), + }; + } +} + +void ParseWithPlugin(ParsePlugin* plugin, std::string_view input) +{ + ParseState parse_state = ParseState(input, *plugin); + plugin->SetOwner(&parse_state); + ParseProgram(&parse_state); + + if (parse_state.error) + { + plugin->OnError(); + plugin->ForwardError(std::move(*parse_state.error)); + } + else + { + plugin->PostParseAction(); + if (parse_state.error) + { + plugin->OnError(); + plugin->ForwardError(std::move(*parse_state.error)); + } + } + + plugin->SetOwner(nullptr); +} +} // namespace Common::GekkoAssembler::detail diff --git a/Source/Core/Common/Assembler/GekkoParser.h b/Source/Core/Common/Assembler/GekkoParser.h new file mode 100644 index 0000000000..4258b5d582 --- /dev/null +++ b/Source/Core/Common/Assembler/GekkoParser.h @@ -0,0 +1,124 @@ +// Copyright 2023 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <optional> +#include <string> +#include <string_view> + +#include "Common/Assembler/AssemblerShared.h" +#include "Common/Assembler/GekkoLexer.h" +#include "Common/CommonTypes.h" + +namespace Common::GekkoAssembler::detail +{ +class ParsePlugin; + +struct ParseState +{ + ParseState(std::string_view input_str, ParsePlugin& plugin); + + bool HasToken(TokenType tp) const; + void ParseToken(TokenType tp); + void EmitErrorHere(std::string&& message); + + Lexer lexer; + ParsePlugin& plugin; + + std::optional<AssemblerError> error; + bool eof; +}; + +enum class AsmOp +{ + Or, + Xor, + And, + Lsh, + Rsh, + Add, + Sub, + Mul, + Div, + Neg, + Not +}; + +enum class Terminal +{ + Hex, + Dec, + Oct, + Bin, + Flt, + Str, + Id, + GPR, + FPR, + SPR, + CRField, + Lt, + Gt, + Eq, + So, + Dot, +}; + +enum class ParenType +{ + Normal, + RelConv, +}; + +// Overridable plugin class supporting a series of skeleton functions which get called when +// the parser parses a given point of interest +class ParsePlugin +{ +public: + ParsePlugin() : m_owner(nullptr) {} + virtual ~ParsePlugin() = default; + + void SetOwner(ParseState* o) { m_owner = o; } + void ForwardError(AssemblerError&& err) { m_owner_error = std::move(err); } + std::optional<AssemblerError>& Error() { return m_owner_error; } + + virtual void PostParseAction() {} + + // Nonterminal callouts + // Pre occurs prior to the head nonterminal being parsed + // Post occurs after the nonterminal has been fully parsed + virtual void OnDirectivePre(GekkoDirective directive) {} + virtual void OnDirectivePost(GekkoDirective directive) {} + virtual void OnInstructionPre(const ParseInfo& mnemonic_info, bool extended) {} + virtual void OnInstructionPost(const ParseInfo& mnemonic_info, bool extended) {} + virtual void OnOperandPre() {} + virtual void OnOperandPost() {} + virtual void OnResolvedExprPre() {} + virtual void OnResolvedExprPost() {} + + // Operator callouts + // All occur after the relevant operands have been parsed + virtual void OnOperator(AsmOp operation) {} + + // Individual token callouts + // All occur prior to the token being parsed + // Due to ambiguity of some tokens, an explicit operation is provided + virtual void OnTerminal(Terminal type, const AssemblerToken& val) {} + virtual void OnHiaddr(std::string_view id) {} + virtual void OnLoaddr(std::string_view id) {} + virtual void OnOpenParen(ParenType type) {} + virtual void OnCloseParen(ParenType type) {} + virtual void OnError() {} + virtual void OnLabelDecl(std::string_view name) {} + virtual void OnVarDecl(std::string_view name) {} + +protected: + ParseState* m_owner; + std::optional<AssemblerError> m_owner_error; +}; + +// Parse the provided input with a plugin to handle what to do with certain points of interest +// e.g. Convert to an IR for generating final machine code, picking up syntactical information +void ParseWithPlugin(ParsePlugin* plugin, std::string_view input); +} // namespace Common::GekkoAssembler::detail diff --git a/Source/Core/Common/CMakeLists.txt b/Source/Core/Common/CMakeLists.txt index 2c4ec7a6f1..a4a761276b 100644 --- a/Source/Core/Common/CMakeLists.txt +++ b/Source/Core/Common/CMakeLists.txt @@ -1,6 +1,18 @@ add_library(common Analytics.cpp Analytics.h + Assembler/AssemblerShared.cpp + Assembler/AssemblerShared.h + Assembler/AssemblerTables.cpp + Assembler/AssemblerTables.h + Assembler/GekkoAssembler.cpp + Assembler/GekkoAssembler.h + Assembler/GekkoIRGen.cpp + Assembler/GekkoIRGen.h + Assembler/GekkoLexer.cpp + Assembler/GekkoLexer.h + Assembler/GekkoParser.cpp + Assembler/GekkoParser.h Assert.h BitField.h BitSet.h diff --git a/Source/Core/Common/CommonPaths.h b/Source/Core/Common/CommonPaths.h index 4510842089..bf30fb98a9 100644 --- a/Source/Core/Common/CommonPaths.h +++ b/Source/Core/Common/CommonPaths.h @@ -94,6 +94,7 @@ #define DYNAMICINPUT_DIR "DynamicInputTextures" #define GRAPHICSMOD_DIR "GraphicMods" #define WIISDSYNC_DIR "WiiSDSync" +#define ASSEMBLY_DIR "SavedAssembly" // This one is only used to remove it if it was present #define SHADERCACHE_LEGACY_DIR "ShaderCache" diff --git a/Source/Core/Common/FileUtil.cpp b/Source/Core/Common/FileUtil.cpp index 4b211fec5e..562e66ff56 100644 --- a/Source/Core/Common/FileUtil.cpp +++ b/Source/Core/Common/FileUtil.cpp @@ -896,6 +896,8 @@ static void RebuildUserDirectories(unsigned int dir_index) s_user_paths[D_GBASAVES_IDX] = s_user_paths[D_GBAUSER_IDX] + GBASAVES_DIR DIR_SEP; s_user_paths[F_GBABIOS_IDX] = s_user_paths[D_GBAUSER_IDX] + GBA_BIOS; + s_user_paths[D_ASM_ROOT_IDX] = s_user_paths[D_USER_IDX] + ASSEMBLY_DIR DIR_SEP; + // The shader cache has moved to the cache directory, so remove the old one. // TODO: remove that someday. File::DeleteDirRecursively(s_user_paths[D_USER_IDX] + SHADERCACHE_LEGACY_DIR DIR_SEP); diff --git a/Source/Core/Common/FileUtil.h b/Source/Core/Common/FileUtil.h index 276dd6caa8..94491cecfd 100644 --- a/Source/Core/Common/FileUtil.h +++ b/Source/Core/Common/FileUtil.h @@ -71,6 +71,7 @@ enum D_GPU_DRIVERS_TMP, D_GPU_DRIVERS_HOOKS, D_GPU_DRIVERS_FILE_REDIRECT, + D_ASM_ROOT_IDX, FIRST_FILE_USER_PATH_IDX, F_DOLPHINCONFIG_IDX = FIRST_FILE_USER_PATH_IDX, F_GCPADCONFIG_IDX, |
