// Copyright 2008 Dolphin Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include "Core/PowerPC/PPCTables.h" #include #include #include #include #include #include #include "Common/Assert.h" #include "Common/CommonTypes.h" #include "Common/FileUtil.h" #include "Common/IOFile.h" #include "Common/Logging/Log.h" #include "Common/StringUtil.h" #include "Common/TypeUtils.h" #include "Core/PowerPC/PowerPC.h" namespace PPCTables { namespace { struct GekkoOPTemplate { u32 opcode; const char* opname; OpType type; u32 num_cycles; u64 flags; }; constexpr GekkoOPTemplate s_unknown_op_info = {0, "unknown_instruction", OpType::Unknown, 0, FL_ENDBLOCK}; constexpr std::array s_primary_table{{ {4, "RunTable4", OpType::Subtable, 0, 0}, {19, "RunTable19", OpType::Subtable, 0, 0}, {31, "RunTable31", OpType::Subtable, 0, 0}, {59, "RunTable59", OpType::Subtable, 0, 0}, {63, "RunTable63", OpType::Subtable, 0, 0}, {16, "bcx", OpType::Branch, 1, FL_ENDBLOCK | FL_READ_CR_BI}, {18, "bx", OpType::Branch, 1, FL_ENDBLOCK}, {3, "twi", OpType::System, 1, FL_IN_A | FL_ENDBLOCK}, {17, "sc", OpType::System, 2, FL_ENDBLOCK}, {7, "mulli", OpType::Integer, 3, FL_OUT_D | FL_IN_A}, {8, "subfic", OpType::Integer, 1, FL_OUT_D | FL_IN_A | FL_SET_CA}, {10, "cmpli", OpType::Integer, 1, FL_IN_A | FL_SET_CRn}, {11, "cmpi", OpType::Integer, 1, FL_IN_A | FL_SET_CRn}, {12, "addic", OpType::Integer, 1, FL_OUT_D | FL_IN_A | FL_SET_CA}, {13, "addic_rc", OpType::Integer, 1, FL_OUT_D | FL_IN_A | FL_SET_CA | FL_SET_CR0}, {14, "addi", OpType::Integer, 1, FL_OUT_D | FL_IN_A0}, {15, "addis", OpType::Integer, 1, FL_OUT_D | FL_IN_A0}, {20, "rlwimix", OpType::Integer, 1, FL_OUT_A | FL_IN_A | FL_IN_S | FL_RC_BIT}, {21, "rlwinmx", OpType::Integer, 1, FL_OUT_A | FL_IN_S | FL_RC_BIT}, {23, "rlwnmx", OpType::Integer, 1, FL_OUT_A | FL_IN_SB | FL_RC_BIT}, {24, "ori", OpType::Integer, 1, FL_OUT_A | FL_IN_S}, {25, "oris", OpType::Integer, 1, FL_OUT_A | FL_IN_S}, {26, "xori", OpType::Integer, 1, FL_OUT_A | FL_IN_S}, {27, "xoris", OpType::Integer, 1, FL_OUT_A | FL_IN_S}, {28, "andi_rc", OpType::Integer, 1, FL_OUT_A | FL_IN_S | FL_SET_CR0}, {29, "andis_rc", OpType::Integer, 1, FL_OUT_A | FL_IN_S | FL_SET_CR0}, {32, "lwz", OpType::Load, 1, FL_OUT_D | FL_IN_A0 | FL_LOADSTORE}, {33, "lwzu", OpType::Load, 1, FL_OUT_D | FL_OUT_A | FL_IN_A | FL_LOADSTORE}, {34, "lbz", OpType::Load, 1, FL_OUT_D | FL_IN_A0 | FL_LOADSTORE}, {35, "lbzu", OpType::Load, 1, FL_OUT_D | FL_OUT_A | FL_IN_A | FL_LOADSTORE}, {40, "lhz", OpType::Load, 1, FL_OUT_D | FL_IN_A0 | FL_LOADSTORE}, {41, "lhzu", OpType::Load, 1, FL_OUT_D | FL_OUT_A | FL_IN_A | FL_LOADSTORE}, {42, "lha", OpType::Load, 1, FL_OUT_D | FL_IN_A0 | FL_LOADSTORE}, {43, "lhau", OpType::Load, 1, FL_OUT_D | FL_OUT_A | FL_IN_A | FL_LOADSTORE}, {44, "sth", OpType::Store, 1, FL_IN_A0 | FL_IN_S | FL_LOADSTORE}, {45, "sthu", OpType::Store, 1, FL_OUT_A | FL_IN_A | FL_IN_S | FL_LOADSTORE}, {36, "stw", OpType::Store, 1, FL_IN_A0 | FL_IN_S | FL_LOADSTORE}, {37, "stwu", OpType::Store, 1, FL_OUT_A | FL_IN_A | FL_IN_S | FL_LOADSTORE}, {38, "stb", OpType::Store, 1, FL_IN_A0 | FL_IN_S | FL_LOADSTORE}, {39, "stbu", OpType::Store, 1, FL_OUT_A | FL_IN_A | FL_IN_S | FL_LOADSTORE}, {46, "lmw", OpType::System, 11, FL_IN_A0 | FL_LOADSTORE}, {47, "stmw", OpType::System, 11, FL_IN_A0 | FL_LOADSTORE}, {48, "lfs", OpType::LoadFP, 1, FL_OUT_FLOAT_D | FL_IN_A | FL_USE_FPU | FL_LOADSTORE}, {49, "lfsu", OpType::LoadFP, 1, FL_OUT_FLOAT_D | FL_OUT_A | FL_IN_A | FL_USE_FPU | FL_LOADSTORE}, {50, "lfd", OpType::LoadFP, 1, FL_INOUT_FLOAT_D | FL_IN_A | FL_USE_FPU | FL_LOADSTORE}, {51, "lfdu", OpType::LoadFP, 1, FL_INOUT_FLOAT_D | FL_OUT_A | FL_IN_A | FL_USE_FPU | FL_LOADSTORE}, {52, "stfs", OpType::StoreFP, 1, FL_IN_FLOAT_S | FL_IN_A0 | FL_USE_FPU | FL_LOADSTORE}, {53, "stfsu", OpType::StoreFP, 1, FL_IN_FLOAT_S | FL_OUT_A | FL_IN_A | FL_USE_FPU | FL_LOADSTORE}, {54, "stfd", OpType::StoreFP, 1, FL_IN_FLOAT_S | FL_IN_A0 | FL_USE_FPU | FL_LOADSTORE}, {55, "stfdu", OpType::StoreFP, 1, FL_IN_FLOAT_S | FL_OUT_A | FL_IN_A | FL_USE_FPU | FL_LOADSTORE}, {56, "psq_l", OpType::LoadPS, 1, FL_OUT_FLOAT_D | FL_IN_A0 | FL_USE_FPU | FL_LOADSTORE | FL_PROGRAMEXCEPTION}, {57, "psq_lu", OpType::LoadPS, 1, FL_OUT_FLOAT_D | FL_OUT_A | FL_IN_A | FL_USE_FPU | FL_LOADSTORE | FL_PROGRAMEXCEPTION}, {60, "psq_st", OpType::StorePS, 1, FL_IN_FLOAT_S | FL_IN_A0 | FL_USE_FPU | FL_LOADSTORE | FL_PROGRAMEXCEPTION}, {61, "psq_stu", OpType::StorePS, 1, FL_IN_FLOAT_S | FL_OUT_A | FL_IN_A | FL_USE_FPU | FL_LOADSTORE | FL_PROGRAMEXCEPTION}, // missing: 0, 1, 2, 5, 6, 9, 22, 30, 62, 58 }}; constexpr std::array s_table4{{ // SUBOP10 {0, "ps_cmpu0", OpType::PS, 1, FL_IN_FLOAT_AB | FL_SET_CRn | FL_USE_FPU | FL_READ_FPRF | FL_SET_FPRF | FL_PROGRAMEXCEPTION | FL_FLOAT_EXCEPTION}, {32, "ps_cmpo0", OpType::PS, 1, FL_IN_FLOAT_AB | FL_SET_CRn | FL_USE_FPU | FL_READ_FPRF | FL_SET_FPRF | FL_PROGRAMEXCEPTION | FL_FLOAT_EXCEPTION}, {40, "ps_neg", OpType::PS, 1, FL_OUT_FLOAT_D | FL_IN_FLOAT_B | FL_IN_FLOAT_B_BITEXACT | FL_RC_BIT_F | FL_USE_FPU | FL_PROGRAMEXCEPTION}, {136, "ps_nabs", OpType::PS, 1, FL_OUT_FLOAT_D | FL_IN_FLOAT_B | FL_IN_FLOAT_B_BITEXACT | FL_RC_BIT_F | FL_USE_FPU | FL_PROGRAMEXCEPTION}, {264, "ps_abs", OpType::PS, 1, FL_OUT_FLOAT_D | FL_IN_FLOAT_B | FL_IN_FLOAT_B_BITEXACT | FL_RC_BIT_F | FL_USE_FPU | FL_PROGRAMEXCEPTION}, {64, "ps_cmpu1", OpType::PS, 1, FL_IN_FLOAT_AB | FL_SET_CRn | FL_USE_FPU | FL_READ_FPRF | FL_SET_FPRF | FL_PROGRAMEXCEPTION | FL_FLOAT_EXCEPTION}, {72, "ps_mr", OpType::PS, 1, FL_OUT_FLOAT_D | FL_IN_FLOAT_B | FL_IN_FLOAT_B_BITEXACT | FL_RC_BIT_F | FL_USE_FPU | FL_PROGRAMEXCEPTION}, {96, "ps_cmpo1", OpType::PS, 1, FL_IN_FLOAT_AB | FL_SET_CRn | FL_USE_FPU | FL_READ_FPRF | FL_SET_FPRF | FL_PROGRAMEXCEPTION | FL_FLOAT_EXCEPTION}, {528, "ps_merge00", OpType::PS, 1, FL_OUT_FLOAT_D | FL_IN_FLOAT_AB | FL_IN_FLOAT_AB_BITEXACT | FL_RC_BIT_F | FL_USE_FPU | FL_PROGRAMEXCEPTION}, {560, "ps_merge01", OpType::PS, 1, FL_OUT_FLOAT_D | FL_IN_FLOAT_AB | FL_IN_FLOAT_AB_BITEXACT | FL_RC_BIT_F | FL_USE_FPU | FL_PROGRAMEXCEPTION}, {592, "ps_merge10", OpType::PS, 1, FL_OUT_FLOAT_D | FL_IN_FLOAT_AB | FL_IN_FLOAT_AB_BITEXACT | FL_RC_BIT_F | FL_USE_FPU | FL_PROGRAMEXCEPTION}, {624, "ps_merge11", OpType::PS, 1, FL_OUT_FLOAT_D | FL_IN_FLOAT_AB | FL_IN_FLOAT_AB_BITEXACT | FL_RC_BIT_F | FL_USE_FPU | FL_PROGRAMEXCEPTION}, {1014, "dcbz_l", OpType::System, 1, FL_IN_A0B | FL_LOADSTORE | FL_PROGRAMEXCEPTION}, }}; constexpr std::array s_table4_2{{ {10, "ps_sum0", OpType::PS, 1, FL_OUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_PROGRAMEXCEPTION | FL_FLOAT_EXCEPTION}, {11, "ps_sum1", OpType::PS, 1, FL_OUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_PROGRAMEXCEPTION | FL_FLOAT_EXCEPTION}, {12, "ps_muls0", OpType::PS, 1, FL_OUT_FLOAT_D | FL_IN_FLOAT_AC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_PROGRAMEXCEPTION | FL_FLOAT_EXCEPTION}, {13, "ps_muls1", OpType::PS, 1, FL_OUT_FLOAT_D | FL_IN_FLOAT_AC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_PROGRAMEXCEPTION | FL_FLOAT_EXCEPTION}, {14, "ps_madds0", OpType::PS, 1, FL_OUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_PROGRAMEXCEPTION | FL_FLOAT_EXCEPTION}, {15, "ps_madds1", OpType::PS, 1, FL_OUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_PROGRAMEXCEPTION | FL_FLOAT_EXCEPTION}, {18, "ps_div", OpType::PS, 17, FL_OUT_FLOAT_D | FL_IN_FLOAT_AB | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_PROGRAMEXCEPTION | FL_FLOAT_EXCEPTION | FL_FLOAT_DIV}, {20, "ps_sub", OpType::PS, 1, FL_OUT_FLOAT_D | FL_IN_FLOAT_AB | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_PROGRAMEXCEPTION | FL_FLOAT_EXCEPTION}, {21, "ps_add", OpType::PS, 1, FL_OUT_FLOAT_D | FL_IN_FLOAT_AB | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_PROGRAMEXCEPTION | FL_FLOAT_EXCEPTION}, {23, "ps_sel", OpType::PS, 1, FL_OUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_IN_FLOAT_BC_BITEXACT | FL_RC_BIT_F | FL_USE_FPU | FL_PROGRAMEXCEPTION}, {24, "ps_res", OpType::PS, 1, FL_OUT_FLOAT_D | FL_IN_FLOAT_B | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_PROGRAMEXCEPTION | FL_FLOAT_EXCEPTION | FL_FLOAT_DIV}, {25, "ps_mul", OpType::PS, 1, FL_OUT_FLOAT_D | FL_IN_FLOAT_AC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_PROGRAMEXCEPTION | FL_FLOAT_EXCEPTION}, {26, "ps_rsqrte", OpType::PS, 2, FL_OUT_FLOAT_D | FL_IN_FLOAT_B | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_PROGRAMEXCEPTION | FL_FLOAT_EXCEPTION | FL_FLOAT_DIV}, {28, "ps_msub", OpType::PS, 1, FL_OUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_PROGRAMEXCEPTION | FL_FLOAT_EXCEPTION}, {29, "ps_madd", OpType::PS, 1, FL_OUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_PROGRAMEXCEPTION | FL_FLOAT_EXCEPTION}, {30, "ps_nmsub", OpType::PS, 1, FL_OUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_PROGRAMEXCEPTION | FL_FLOAT_EXCEPTION}, {31, "ps_nmadd", OpType::PS, 1, FL_OUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_PROGRAMEXCEPTION | FL_FLOAT_EXCEPTION}, }}; constexpr std::array s_table4_3{{ {6, "psq_lx", OpType::LoadPS, 1, FL_OUT_FLOAT_D | FL_IN_A0B | FL_USE_FPU | FL_LOADSTORE}, {7, "psq_stx", OpType::StorePS, 1, FL_IN_FLOAT_S | FL_IN_A0B | FL_USE_FPU | FL_LOADSTORE}, {38, "psq_lux", OpType::LoadPS, 1, FL_OUT_FLOAT_D | FL_OUT_A | FL_IN_AB | FL_USE_FPU | FL_LOADSTORE}, {39, "psq_stux", OpType::StorePS, 1, FL_IN_FLOAT_S | FL_OUT_A | FL_IN_AB | FL_USE_FPU | FL_LOADSTORE}, }}; constexpr std::array s_table19{{ {528, "bcctrx", OpType::Branch, 1, FL_ENDBLOCK | FL_READ_CR_BI}, {16, "bclrx", OpType::Branch, 1, FL_ENDBLOCK | FL_READ_CR_BI}, {257, "crand", OpType::CR, 1, 0}, {129, "crandc", OpType::CR, 1, 0}, {289, "creqv", OpType::CR, 1, 0}, {225, "crnand", OpType::CR, 1, 0}, {33, "crnor", OpType::CR, 1, 0}, {449, "cror", OpType::CR, 1, 0}, {417, "crorc", OpType::CR, 1, 0}, {193, "crxor", OpType::CR, 1, 0}, {150, "isync", OpType::InstructionCache, 1, FL_NO_REORDER}, {0, "mcrf", OpType::System, 1, FL_SET_CRn | FL_READ_CRn}, {50, "rfi", OpType::System, 2, FL_ENDBLOCK | FL_CHECKEXCEPTIONS | FL_PROGRAMEXCEPTION}, }}; constexpr std::array s_table31{{ {266, "addx", OpType::Integer, 1, FL_OUT_D | FL_IN_AB | FL_RC_BIT}, {778, "addox", OpType::Integer, 1, FL_OUT_D | FL_IN_AB | FL_RC_BIT | FL_SET_OE}, {10, "addcx", OpType::Integer, 1, FL_OUT_D | FL_IN_AB | FL_SET_CA | FL_RC_BIT}, {522, "addcox", OpType::Integer, 1, FL_OUT_D | FL_IN_AB | FL_SET_CA | FL_RC_BIT | FL_SET_OE}, {138, "addex", OpType::Integer, 1, FL_OUT_D | FL_IN_AB | FL_READ_CA | FL_SET_CA | FL_RC_BIT}, {650, "addeox", OpType::Integer, 1, FL_OUT_D | FL_IN_AB | FL_READ_CA | FL_SET_CA | FL_RC_BIT | FL_SET_OE}, {234, "addmex", OpType::Integer, 1, FL_OUT_D | FL_IN_A | FL_READ_CA | FL_SET_CA | FL_RC_BIT}, {746, "addmeox", OpType::Integer, 1, FL_OUT_D | FL_IN_A | FL_READ_CA | FL_SET_CA | FL_RC_BIT | FL_SET_OE}, {202, "addzex", OpType::Integer, 1, FL_OUT_D | FL_IN_A | FL_READ_CA | FL_SET_CA | FL_RC_BIT}, {714, "addzeox", OpType::Integer, 1, FL_OUT_D | FL_IN_A | FL_READ_CA | FL_SET_CA | FL_RC_BIT | FL_SET_OE}, {491, "divwx", OpType::Integer, 40, FL_OUT_D | FL_IN_AB | FL_RC_BIT}, {1003, "divwox", OpType::Integer, 40, FL_OUT_D | FL_IN_AB | FL_RC_BIT | FL_SET_OE}, {459, "divwux", OpType::Integer, 40, FL_OUT_D | FL_IN_AB | FL_RC_BIT}, {971, "divwuox", OpType::Integer, 40, FL_OUT_D | FL_IN_AB | FL_RC_BIT | FL_SET_OE}, {75, "mulhwx", OpType::Integer, 5, FL_OUT_D | FL_IN_AB | FL_RC_BIT}, {11, "mulhwux", OpType::Integer, 5, FL_OUT_D | FL_IN_AB | FL_RC_BIT}, {235, "mullwx", OpType::Integer, 5, FL_OUT_D | FL_IN_AB | FL_RC_BIT}, {747, "mullwox", OpType::Integer, 5, FL_OUT_D | FL_IN_AB | FL_RC_BIT | FL_SET_OE}, {104, "negx", OpType::Integer, 1, FL_OUT_D | FL_IN_A | FL_RC_BIT}, {616, "negox", OpType::Integer, 1, FL_OUT_D | FL_IN_A | FL_RC_BIT | FL_SET_OE}, {40, "subfx", OpType::Integer, 1, FL_OUT_D | FL_IN_AB | FL_RC_BIT}, {552, "subfox", OpType::Integer, 1, FL_OUT_D | FL_IN_AB | FL_RC_BIT | FL_SET_OE}, {8, "subfcx", OpType::Integer, 1, FL_OUT_D | FL_IN_AB | FL_SET_CA | FL_RC_BIT}, {520, "subfcox", OpType::Integer, 1, FL_OUT_D | FL_IN_AB | FL_SET_CA | FL_RC_BIT | FL_SET_OE}, {136, "subfex", OpType::Integer, 1, FL_OUT_D | FL_IN_AB | FL_READ_CA | FL_SET_CA | FL_RC_BIT}, {648, "subfeox", OpType::Integer, 1, FL_OUT_D | FL_IN_AB | FL_READ_CA | FL_SET_CA | FL_RC_BIT | FL_SET_OE}, {232, "subfmex", OpType::Integer, 1, FL_OUT_D | FL_IN_A | FL_READ_CA | FL_SET_CA | FL_RC_BIT}, {744, "subfmeox", OpType::Integer, 1, FL_OUT_D | FL_IN_A | FL_READ_CA | FL_SET_CA | FL_RC_BIT | FL_SET_OE}, {200, "subfzex", OpType::Integer, 1, FL_OUT_D | FL_IN_A | FL_READ_CA | FL_SET_CA | FL_RC_BIT}, {712, "subfzeox", OpType::Integer, 1, FL_OUT_D | FL_IN_A | FL_READ_CA | FL_SET_CA | FL_RC_BIT | FL_SET_OE}, {28, "andx", OpType::Integer, 1, FL_OUT_A | FL_IN_SB | FL_RC_BIT}, {60, "andcx", OpType::Integer, 1, FL_OUT_A | FL_IN_SB | FL_RC_BIT}, {444, "orx", OpType::Integer, 1, FL_OUT_A | FL_IN_SB | FL_RC_BIT}, {124, "norx", OpType::Integer, 1, FL_OUT_A | FL_IN_SB | FL_RC_BIT}, {316, "xorx", OpType::Integer, 1, FL_OUT_A | FL_IN_SB | FL_RC_BIT}, {412, "orcx", OpType::Integer, 1, FL_OUT_A | FL_IN_SB | FL_RC_BIT}, {476, "nandx", OpType::Integer, 1, FL_OUT_A | FL_IN_SB | FL_RC_BIT}, {284, "eqvx", OpType::Integer, 1, FL_OUT_A | FL_IN_SB | FL_RC_BIT}, {0, "cmp", OpType::Integer, 1, FL_IN_AB | FL_SET_CRn}, {32, "cmpl", OpType::Integer, 1, FL_IN_AB | FL_SET_CRn}, {26, "cntlzwx", OpType::Integer, 1, FL_OUT_A | FL_IN_S | FL_RC_BIT}, {922, "extshx", OpType::Integer, 1, FL_OUT_A | FL_IN_S | FL_RC_BIT}, {954, "extsbx", OpType::Integer, 1, FL_OUT_A | FL_IN_S | FL_RC_BIT}, {536, "srwx", OpType::Integer, 1, FL_OUT_A | FL_IN_SB | FL_RC_BIT}, {792, "srawx", OpType::Integer, 1, FL_OUT_A | FL_IN_SB | FL_SET_CA | FL_RC_BIT}, {824, "srawix", OpType::Integer, 1, FL_OUT_A | FL_IN_S | FL_SET_CA | FL_RC_BIT}, {24, "slwx", OpType::Integer, 1, FL_OUT_A | FL_IN_SB | FL_RC_BIT}, {54, "dcbst", OpType::DataCache, 5, FL_IN_A0B | FL_LOADSTORE}, {86, "dcbf", OpType::DataCache, 5, FL_IN_A0B | FL_LOADSTORE}, {246, "dcbtst", OpType::DataCache, 2, 0}, {278, "dcbt", OpType::DataCache, 2, 0}, {470, "dcbi", OpType::DataCache, 5, FL_IN_A0B | FL_LOADSTORE | FL_PROGRAMEXCEPTION}, {758, "dcba", OpType::DataCache, 5, 0}, {1014, "dcbz", OpType::DataCache, 5, FL_IN_A0B | FL_LOADSTORE}, // load word {23, "lwzx", OpType::Load, 1, FL_OUT_D | FL_IN_A0B | FL_LOADSTORE}, {55, "lwzux", OpType::Load, 1, FL_OUT_D | FL_OUT_A | FL_IN_AB | FL_LOADSTORE}, // load halfword {279, "lhzx", OpType::Load, 1, FL_OUT_D | FL_IN_A0B | FL_LOADSTORE}, {311, "lhzux", OpType::Load, 1, FL_OUT_D | FL_OUT_A | FL_IN_AB | FL_LOADSTORE}, // load halfword signextend {343, "lhax", OpType::Load, 1, FL_OUT_D | FL_IN_A0B | FL_LOADSTORE}, {375, "lhaux", OpType::Load, 1, FL_OUT_D | FL_OUT_A | FL_IN_AB | FL_LOADSTORE}, // load byte {87, "lbzx", OpType::Load, 1, FL_OUT_D | FL_IN_A0B | FL_LOADSTORE}, {119, "lbzux", OpType::Load, 1, FL_OUT_D | FL_OUT_A | FL_IN_AB | FL_LOADSTORE}, // load byte reverse {534, "lwbrx", OpType::Load, 1, FL_OUT_D | FL_IN_A0B | FL_LOADSTORE}, {790, "lhbrx", OpType::Load, 1, FL_OUT_D | FL_IN_A0B | FL_LOADSTORE}, // Conditional load/store (Wii SMP) {150, "stwcxd", OpType::Store, 1, FL_IN_S | FL_IN_A0B | FL_SET_CR0 | FL_LOADSTORE}, {20, "lwarx", OpType::Load, 1, FL_OUT_D | FL_IN_A0B | FL_SET_CR0 | FL_LOADSTORE}, // load string (Inst these) {533, "lswx", OpType::Load, 1, FL_IN_A0B | FL_OUT_D | FL_LOADSTORE}, {597, "lswi", OpType::Load, 1, FL_IN_A0 | FL_OUT_D | FL_LOADSTORE}, // store word {151, "stwx", OpType::Store, 1, FL_IN_S | FL_IN_A0B | FL_LOADSTORE}, {183, "stwux", OpType::Store, 1, FL_IN_S | FL_OUT_A | FL_IN_AB | FL_LOADSTORE}, // store halfword {407, "sthx", OpType::Store, 1, FL_IN_S | FL_IN_A0B | FL_LOADSTORE}, {439, "sthux", OpType::Store, 1, FL_IN_S | FL_OUT_A | FL_IN_AB | FL_LOADSTORE}, // store byte {215, "stbx", OpType::Store, 1, FL_IN_S | FL_IN_A0B | FL_LOADSTORE}, {247, "stbux", OpType::Store, 1, FL_IN_S | FL_OUT_A | FL_IN_AB | FL_LOADSTORE}, // store bytereverse {662, "stwbrx", OpType::Store, 1, FL_IN_S | FL_IN_A0B | FL_LOADSTORE}, {918, "sthbrx", OpType::Store, 1, FL_IN_S | FL_IN_A0B | FL_LOADSTORE}, {661, "stswx", OpType::Store, 1, FL_IN_A0B | FL_LOADSTORE}, {725, "stswi", OpType::Store, 1, FL_IN_A0 | FL_LOADSTORE}, // fp load/store {535, "lfsx", OpType::LoadFP, 1, FL_OUT_FLOAT_D | FL_IN_A0B | FL_USE_FPU | FL_LOADSTORE}, {567, "lfsux", OpType::LoadFP, 1, FL_OUT_FLOAT_D | FL_IN_AB | FL_OUT_A | FL_USE_FPU | FL_LOADSTORE}, {599, "lfdx", OpType::LoadFP, 1, FL_INOUT_FLOAT_D | FL_IN_A0B | FL_USE_FPU | FL_LOADSTORE}, {631, "lfdux", OpType::LoadFP, 1, FL_INOUT_FLOAT_D | FL_IN_AB | FL_OUT_A | FL_USE_FPU | FL_LOADSTORE}, {663, "stfsx", OpType::StoreFP, 1, FL_IN_FLOAT_S | FL_IN_A0B | FL_USE_FPU | FL_LOADSTORE}, {695, "stfsux", OpType::StoreFP, 1, FL_IN_FLOAT_S | FL_IN_AB | FL_OUT_A | FL_USE_FPU | FL_LOADSTORE}, {727, "stfdx", OpType::StoreFP, 1, FL_IN_FLOAT_S | FL_IN_A0B | FL_USE_FPU | FL_LOADSTORE}, {759, "stfdux", OpType::StoreFP, 1, FL_IN_FLOAT_S | FL_IN_AB | FL_OUT_A | FL_USE_FPU | FL_LOADSTORE}, {983, "stfiwx", OpType::StoreFP, 1, FL_IN_FLOAT_S | FL_IN_A0B | FL_USE_FPU | FL_LOADSTORE}, {19, "mfcr", OpType::System, 1, FL_OUT_D | FL_READ_ALL_CR}, {83, "mfmsr", OpType::System, 1, FL_OUT_D | FL_PROGRAMEXCEPTION}, {144, "mtcrf", OpType::System, 1, FL_IN_S | FL_SET_ALL_CR | FL_READ_ALL_CR}, {146, "mtmsr", OpType::System, 1, FL_IN_S | FL_ENDBLOCK | FL_PROGRAMEXCEPTION | FL_FLOAT_EXCEPTION}, {210, "mtsr", OpType::System, 1, FL_IN_S | FL_PROGRAMEXCEPTION}, {242, "mtsrin", OpType::System, 1, FL_IN_SB | FL_PROGRAMEXCEPTION}, {339, "mfspr", OpType::SPR, 1, FL_OUT_D | FL_PROGRAMEXCEPTION}, {467, "mtspr", OpType::SPR, 2, FL_IN_S | FL_ENDBLOCK | FL_PROGRAMEXCEPTION}, {371, "mftb", OpType::System, 1, FL_OUT_D | FL_TIMER | FL_PROGRAMEXCEPTION}, {512, "mcrxr", OpType::System, 1, FL_SET_CRn | FL_READ_CA | FL_SET_CA}, {595, "mfsr", OpType::System, 3, FL_OUT_D | FL_PROGRAMEXCEPTION}, {659, "mfsrin", OpType::System, 3, FL_OUT_D | FL_IN_B | FL_PROGRAMEXCEPTION}, {4, "tw", OpType::System, 2, FL_IN_AB | FL_ENDBLOCK}, {598, "sync", OpType::System, 3, 0}, {982, "icbi", OpType::System, 4, FL_IN_A0B | FL_ENDBLOCK | FL_LOADSTORE}, // Unused instructions on GC {310, "eciwx", OpType::System, 1, FL_IN_A0B | FL_OUT_D | FL_LOADSTORE}, {438, "ecowx", OpType::System, 1, FL_IN_A0B | FL_IN_S | FL_LOADSTORE}, {854, "eieio", OpType::System, 1, 0}, {306, "tlbie", OpType::System, 1, FL_IN_B | FL_PROGRAMEXCEPTION}, {566, "tlbsync", OpType::System, 1, FL_PROGRAMEXCEPTION}, }}; constexpr std::array s_table59{{ {18, "fdivsx", OpType::SingleFP, 17, FL_OUT_FLOAT_D | FL_IN_FLOAT_AB | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_FLOAT_EXCEPTION | FL_FLOAT_DIV}, // TODO {20, "fsubsx", OpType::SingleFP, 1, FL_OUT_FLOAT_D | FL_IN_FLOAT_AB | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_FLOAT_EXCEPTION}, {21, "faddsx", OpType::SingleFP, 1, FL_OUT_FLOAT_D | FL_IN_FLOAT_AB | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_FLOAT_EXCEPTION}, {24, "fresx", OpType::SingleFP, 1, FL_OUT_FLOAT_D | FL_IN_FLOAT_B | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_FLOAT_EXCEPTION | FL_FLOAT_DIV}, {25, "fmulsx", OpType::SingleFP, 1, FL_OUT_FLOAT_D | FL_IN_FLOAT_AC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_FLOAT_EXCEPTION}, {28, "fmsubsx", OpType::SingleFP, 1, FL_OUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_FLOAT_EXCEPTION}, {29, "fmaddsx", OpType::SingleFP, 1, FL_OUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_FLOAT_EXCEPTION}, {30, "fnmsubsx", OpType::SingleFP, 1, FL_OUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_FLOAT_EXCEPTION}, {31, "fnmaddsx", OpType::SingleFP, 1, FL_OUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_FLOAT_EXCEPTION}, }}; constexpr std::array s_table63{{ {264, "fabsx", OpType::DoubleFP, 1, FL_INOUT_FLOAT_D | FL_IN_FLOAT_B | FL_IN_FLOAT_B_BITEXACT | FL_RC_BIT_F | FL_USE_FPU}, // FIXME: fcmp modifies the FPRF flags, but if the flags are clobbered later, // we don't actually need to calculate or store them here. So FL_READ_FPRF and FL_SET_FPRF is // not an ideal representation of fcmp's effect on FPRF flags and might result in slightly // sub-optimal code. {32, "fcmpo", OpType::DoubleFP, 1, FL_IN_FLOAT_AB | FL_SET_CRn | FL_USE_FPU | FL_READ_FPRF | FL_SET_FPRF | FL_FLOAT_EXCEPTION}, {0, "fcmpu", OpType::DoubleFP, 1, FL_IN_FLOAT_AB | FL_SET_CRn | FL_USE_FPU | FL_READ_FPRF | FL_SET_FPRF | FL_FLOAT_EXCEPTION}, {14, "fctiwx", OpType::DoubleFP, 1, FL_INOUT_FLOAT_D | FL_IN_FLOAT_B | FL_RC_BIT_F | FL_USE_FPU | FL_FLOAT_EXCEPTION}, {15, "fctiwzx", OpType::DoubleFP, 1, FL_INOUT_FLOAT_D | FL_IN_FLOAT_B | FL_RC_BIT_F | FL_USE_FPU | FL_FLOAT_EXCEPTION}, {72, "fmrx", OpType::DoubleFP, 1, FL_INOUT_FLOAT_D | FL_IN_FLOAT_B | FL_IN_FLOAT_B_BITEXACT | FL_RC_BIT_F | FL_USE_FPU}, {136, "fnabsx", OpType::DoubleFP, 1, FL_INOUT_FLOAT_D | FL_IN_FLOAT_B | FL_RC_BIT_F | FL_IN_FLOAT_B_BITEXACT | FL_USE_FPU}, {40, "fnegx", OpType::DoubleFP, 1, FL_INOUT_FLOAT_D | FL_IN_FLOAT_B | FL_RC_BIT_F | FL_IN_FLOAT_B_BITEXACT | FL_USE_FPU}, {12, "frspx", OpType::DoubleFP, 1, FL_OUT_FLOAT_D | FL_IN_FLOAT_B | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_FLOAT_EXCEPTION}, {64, "mcrfs", OpType::SystemFP, 1, FL_SET_CRn | FL_USE_FPU | FL_READ_FPRF}, {583, "mffsx", OpType::SystemFP, 1, FL_RC_BIT_F | FL_INOUT_FLOAT_D | FL_USE_FPU | FL_READ_FPRF}, {70, "mtfsb0x", OpType::SystemFP, 3, FL_RC_BIT_F | FL_USE_FPU | FL_READ_FPRF | FL_SET_FPRF}, {38, "mtfsb1x", OpType::SystemFP, 3, FL_RC_BIT_F | FL_USE_FPU | FL_READ_FPRF | FL_SET_FPRF | FL_FLOAT_EXCEPTION}, {134, "mtfsfix", OpType::SystemFP, 3, FL_RC_BIT_F | FL_USE_FPU | FL_READ_FPRF | FL_SET_FPRF | FL_FLOAT_EXCEPTION}, {711, "mtfsfx", OpType::SystemFP, 3, FL_RC_BIT_F | FL_IN_FLOAT_B | FL_USE_FPU | FL_READ_FPRF | FL_SET_FPRF | FL_FLOAT_EXCEPTION}, }}; constexpr std::array s_table63_2{{ {18, "fdivx", OpType::DoubleFP, 31, FL_INOUT_FLOAT_D | FL_IN_FLOAT_AB | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_FLOAT_EXCEPTION | FL_FLOAT_DIV}, {20, "fsubx", OpType::DoubleFP, 1, FL_INOUT_FLOAT_D | FL_IN_FLOAT_AB | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_FLOAT_EXCEPTION}, {21, "faddx", OpType::DoubleFP, 1, FL_INOUT_FLOAT_D | FL_IN_FLOAT_AB | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_FLOAT_EXCEPTION}, {23, "fselx", OpType::DoubleFP, 1, FL_INOUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_IN_FLOAT_BC_BITEXACT | FL_RC_BIT_F | FL_USE_FPU}, {25, "fmulx", OpType::DoubleFP, 1, FL_INOUT_FLOAT_D | FL_IN_FLOAT_AC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_FLOAT_EXCEPTION}, {26, "frsqrtex", OpType::DoubleFP, 1, FL_INOUT_FLOAT_D | FL_IN_FLOAT_B | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_FLOAT_EXCEPTION | FL_FLOAT_DIV}, {28, "fmsubx", OpType::DoubleFP, 1, FL_INOUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_FLOAT_EXCEPTION}, {29, "fmaddx", OpType::DoubleFP, 1, FL_INOUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_FLOAT_EXCEPTION}, {30, "fnmsubx", OpType::DoubleFP, 1, FL_INOUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_FLOAT_EXCEPTION}, {31, "fnmaddx", OpType::DoubleFP, 1, FL_INOUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_FLOAT_EXCEPTION}, }}; constexpr size_t TOTAL_INSTRUCTION_COUNT = 1 + s_primary_table.size() + s_table4_2.size() + s_table4_3.size() + s_table4.size() + s_table31.size() + s_table19.size() + s_table59.size() + s_table63.size() + s_table63_2.size(); struct Tables { std::array all_instructions{}; u32 unknown_op_info; std::array primary_table{}; std::array table4{}; std::array table19{}; std::array table31{}; std::array table59{}; std::array table63{}; }; } // namespace static std::array s_all_instructions_stats; constexpr Tables s_tables = []() consteval { Tables tables{}; u32 counter = 0; auto make_info = [&](const GekkoOPTemplate& inst) consteval->u32 { ASSERT(counter < TOTAL_INSTRUCTION_COUNT); GekkoOPInfo* info = &tables.all_instructions[counter]; info->opname = inst.opname; info->flags = inst.flags; info->type = inst.type; info->num_cycles = inst.num_cycles; info->stats = &s_all_instructions_stats[counter]; return counter++; }; u32 unknown_op_info = make_info(s_unknown_op_info); tables.unknown_op_info = unknown_op_info; Common::Fill(tables.primary_table, unknown_op_info); for (auto& tpl : s_primary_table) { ASSERT(tables.primary_table[tpl.opcode] == unknown_op_info); tables.primary_table[tpl.opcode] = make_info(tpl); }; Common::Fill(tables.table4, unknown_op_info); for (const auto& tpl : s_table4_2) { u32 info = make_info(tpl); for (u32 i = 0; i < 32; i++) { const u32 fill = i << 5; const u32 op = fill + tpl.opcode; ASSERT(tables.table4[op] == unknown_op_info); tables.table4[op] = info; } } for (const auto& tpl : s_table4_3) { u32 info = make_info(tpl); for (u32 i = 0; i < 16; i++) { const u32 fill = i << 6; const u32 op = fill + tpl.opcode; ASSERT(tables.table4[op] == unknown_op_info); tables.table4[op] = info; } } for (const auto& tpl : s_table4) { const u32 op = tpl.opcode; ASSERT(tables.table4[op] == unknown_op_info); tables.table4[op] = make_info(tpl); } Common::Fill(tables.table19, unknown_op_info); for (auto& tpl : s_table19) { ASSERT(tables.table19[tpl.opcode] == unknown_op_info); tables.table19[tpl.opcode] = make_info(tpl); }; Common::Fill(tables.table31, unknown_op_info); for (auto& tpl : s_table31) { ASSERT(tables.table31[tpl.opcode] == unknown_op_info); tables.table31[tpl.opcode] = make_info(tpl); }; Common::Fill(tables.table59, unknown_op_info); for (auto& tpl : s_table59) { ASSERT(tables.table59[tpl.opcode] == unknown_op_info); tables.table59[tpl.opcode] = make_info(tpl); }; Common::Fill(tables.table63, unknown_op_info); for (auto& tpl : s_table63) { ASSERT(tables.table63[tpl.opcode] == unknown_op_info); tables.table63[tpl.opcode] = make_info(tpl); }; for (const auto& tpl : s_table63_2) { u32 info = make_info(tpl); for (u32 i = 0; i < 32; i++) { const u32 fill = i << 5; const u32 op = fill + tpl.opcode; ASSERT(tables.table63[op] == unknown_op_info); tables.table63[op] = info; } } ASSERT(counter == TOTAL_INSTRUCTION_COUNT); return tables; } (); const GekkoOPInfo* GetOpInfo(UGeckoInstruction inst, u32 pc) { const GekkoOPInfo* info = &s_tables.all_instructions[s_tables.primary_table[inst.OPCD]]; if (info->type == OpType::Subtable) { switch (inst.OPCD) { case 4: return &s_tables.all_instructions[s_tables.table4[inst.SUBOP10]]; case 19: return &s_tables.all_instructions[s_tables.table19[inst.SUBOP10]]; case 31: return &s_tables.all_instructions[s_tables.table31[inst.SUBOP10]]; case 59: return &s_tables.all_instructions[s_tables.table59[inst.SUBOP5]]; case 63: return &s_tables.all_instructions[s_tables.table63[inst.SUBOP10]]; default: ASSERT_MSG(POWERPC, 0, "GetOpInfo - invalid subtable op {:08x} @ {:08x}", inst.hex, pc); return &s_tables.all_instructions[s_tables.unknown_op_info]; } } else { if (info->type == OpType::Invalid) { ASSERT_MSG(POWERPC, 0, "GetOpInfo - invalid op {:08x} @ {:08x}", inst.hex, pc); return &s_tables.all_instructions[s_tables.unknown_op_info]; } return info; } } // #define OPLOG // #define OP_TO_LOG "mtfsb0x" #ifdef OPLOG namespace { std::vector rsplocations; } #endif const char* GetInstructionName(UGeckoInstruction inst, u32 pc) { const GekkoOPInfo* info = GetOpInfo(inst, pc); return info->opname; } bool IsValidInstruction(UGeckoInstruction inst, u32 pc) { const GekkoOPInfo* info = GetOpInfo(inst, pc); return info->type != OpType::Invalid && info->type != OpType::Unknown; } void CountInstruction(UGeckoInstruction inst, u32 pc) { const GekkoOPInfo* info = GetOpInfo(inst, pc); info->stats->run_count++; } void CountInstructionCompile(const GekkoOPInfo* info, u32 pc) { info->stats->compile_count++; info->stats->last_use = pc; #ifdef OPLOG if (!strcmp(info->opname, OP_TO_LOG)) { rsplocations.push_back(pc); } #endif } void PrintInstructionRunCounts() { typedef std::pair OpInfo; std::array temp; for (size_t i = 0; i < TOTAL_INSTRUCTION_COUNT; i++) { const GekkoOPInfo& info = s_tables.all_instructions[i]; temp[i] = std::make_pair(info.opname, info.stats->run_count); } std::sort(temp.begin(), temp.end(), [](const OpInfo& a, const OpInfo& b) { return a.second > b.second; }); for (auto& inst : temp) { if (inst.second == 0) break; INFO_LOG_FMT(POWERPC, "{} : {}", inst.first, inst.second); } } void LogCompiledInstructions() { static unsigned int time = 0; File::IOFile f(fmt::format("{}inst_log{}.txt", File::GetUserPath(D_LOGS_IDX), time), "w"); for (size_t i = 0; i < TOTAL_INSTRUCTION_COUNT; i++) { const GekkoOPInfo& info = s_tables.all_instructions[i]; if (info.stats->compile_count > 0) { f.WriteString(fmt::format("{0}\t{1}\t{2}\t{3:08x}\n", info.opname, info.stats->compile_count, info.stats->run_count, info.stats->last_use)); } } f.Open(fmt::format("{}inst_not{}.txt", File::GetUserPath(D_LOGS_IDX), time), "w"); for (size_t i = 0; i < TOTAL_INSTRUCTION_COUNT; i++) { const GekkoOPInfo& info = s_tables.all_instructions[i]; if (info.stats->compile_count == 0) { f.WriteString(fmt::format("{0}\t{1}\t{2}\n", info.opname, info.stats->compile_count, info.stats->run_count)); } } #ifdef OPLOG f.Open(fmt::format("{}" OP_TO_LOG "_at{}.txt", File::GetUserPath(D_LOGS_IDX), time), "w"); for (auto& rsplocation : rsplocations) { f.WriteString(fmt::format(OP_TO_LOG ": {0:08x}\n", rsplocation)); } #endif ++time; } } // namespace PPCTables