mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2024-11-14 21:37:52 -07:00
Jits: Discard registers which we know will be overwritten
This commit adds a new "discarded" state for registers. Discarding a register is like flushing it, but without actually writing its value back to memory. We can discard a register only when it is guaranteed that no instruction will read from the register before it is next written to. Discarding reduces the register pressure a little, and can also let us skip a few flushes on interpreter fallbacks.
This commit is contained in:
parent
901170e299
commit
62ce1c7653
@ -85,51 +85,51 @@ static std::array<GekkoOPTemplate, 54> primarytable =
|
||||
{54, Interpreter::stfd, {"stfd", OpType::StoreFP, FL_IN_FLOAT_S | FL_IN_A0 | FL_USE_FPU | FL_LOADSTORE, 1, 0, 0, 0}},
|
||||
{55, Interpreter::stfdu, {"stfdu", OpType::StoreFP, FL_IN_FLOAT_S | FL_OUT_A | FL_IN_A | FL_USE_FPU | FL_LOADSTORE, 1, 0, 0, 0}},
|
||||
|
||||
{56, Interpreter::psq_l, {"psq_l", OpType::LoadPS, FL_OUT_FLOAT_D | FL_IN_A0 | FL_USE_FPU | FL_LOADSTORE, 1, 0, 0, 0}},
|
||||
{57, Interpreter::psq_lu, {"psq_lu", OpType::LoadPS, FL_OUT_FLOAT_D | FL_OUT_A | FL_IN_A | FL_USE_FPU | FL_LOADSTORE, 1, 0, 0, 0}},
|
||||
{60, Interpreter::psq_st, {"psq_st", OpType::StorePS, FL_IN_FLOAT_S | FL_IN_A0 | FL_USE_FPU | FL_LOADSTORE, 1, 0, 0, 0}},
|
||||
{61, Interpreter::psq_stu, {"psq_stu", OpType::StorePS, FL_IN_FLOAT_S | FL_OUT_A | FL_IN_A | FL_USE_FPU | FL_LOADSTORE, 1, 0, 0, 0}},
|
||||
{56, Interpreter::psq_l, {"psq_l", OpType::LoadPS, FL_OUT_FLOAT_D | FL_IN_A0 | FL_USE_FPU | FL_LOADSTORE | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}},
|
||||
{57, Interpreter::psq_lu, {"psq_lu", OpType::LoadPS, FL_OUT_FLOAT_D | FL_OUT_A | FL_IN_A | FL_USE_FPU | FL_LOADSTORE | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}},
|
||||
{60, Interpreter::psq_st, {"psq_st", OpType::StorePS, FL_IN_FLOAT_S | FL_IN_A0 | FL_USE_FPU | FL_LOADSTORE | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}},
|
||||
{61, Interpreter::psq_stu, {"psq_stu", OpType::StorePS, FL_IN_FLOAT_S | FL_OUT_A | FL_IN_A | FL_USE_FPU | FL_LOADSTORE | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}},
|
||||
|
||||
//missing: 0, 1, 2, 5, 6, 9, 22, 30, 62, 58
|
||||
}};
|
||||
|
||||
static std::array<GekkoOPTemplate, 13> table4 =
|
||||
{{ //SUBOP10
|
||||
{0, Interpreter::ps_cmpu0, {"ps_cmpu0", OpType::PS, FL_IN_FLOAT_AB | FL_SET_CRn | FL_USE_FPU | FL_READ_FPRF | FL_SET_FPRF, 1, 0, 0, 0}},
|
||||
{32, Interpreter::ps_cmpo0, {"ps_cmpo0", OpType::PS, FL_IN_FLOAT_AB | FL_SET_CRn | FL_USE_FPU | FL_READ_FPRF | FL_SET_FPRF, 1, 0, 0, 0}},
|
||||
{40, Interpreter::ps_neg, {"ps_neg", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_B | FL_IN_FLOAT_B_BITEXACT | FL_RC_BIT_F | FL_USE_FPU, 1, 0, 0, 0}},
|
||||
{136, Interpreter::ps_nabs, {"ps_nabs", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_B | FL_IN_FLOAT_B_BITEXACT | FL_RC_BIT_F | FL_USE_FPU, 1, 0, 0, 0}},
|
||||
{264, Interpreter::ps_abs, {"ps_abs", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_B | FL_IN_FLOAT_B_BITEXACT | FL_RC_BIT_F | FL_USE_FPU, 1, 0, 0, 0}},
|
||||
{64, Interpreter::ps_cmpu1, {"ps_cmpu1", OpType::PS, FL_IN_FLOAT_AB | FL_SET_CRn | FL_USE_FPU | FL_READ_FPRF | FL_SET_FPRF, 1, 0, 0, 0}},
|
||||
{72, Interpreter::ps_mr, {"ps_mr", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_B | FL_IN_FLOAT_B_BITEXACT | FL_RC_BIT_F | FL_USE_FPU, 1, 0, 0, 0}},
|
||||
{96, Interpreter::ps_cmpo1, {"ps_cmpo1", OpType::PS, FL_IN_FLOAT_AB | FL_SET_CRn | FL_USE_FPU | FL_READ_FPRF | FL_SET_FPRF, 1, 0, 0, 0}},
|
||||
{528, Interpreter::ps_merge00, {"ps_merge00", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_AB | FL_IN_FLOAT_AB_BITEXACT | FL_RC_BIT_F | FL_USE_FPU, 1, 0, 0, 0}},
|
||||
{560, Interpreter::ps_merge01, {"ps_merge01", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_AB | FL_IN_FLOAT_AB_BITEXACT | FL_RC_BIT_F | FL_USE_FPU, 1, 0, 0, 0}},
|
||||
{592, Interpreter::ps_merge10, {"ps_merge10", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_AB | FL_IN_FLOAT_AB_BITEXACT | FL_RC_BIT_F | FL_USE_FPU, 1, 0, 0, 0}},
|
||||
{624, Interpreter::ps_merge11, {"ps_merge11", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_AB | FL_IN_FLOAT_AB_BITEXACT | FL_RC_BIT_F | FL_USE_FPU, 1, 0, 0, 0}},
|
||||
{0, Interpreter::ps_cmpu0, {"ps_cmpu0", OpType::PS, FL_IN_FLOAT_AB | FL_SET_CRn | FL_USE_FPU | FL_READ_FPRF | FL_SET_FPRF | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}},
|
||||
{32, Interpreter::ps_cmpo0, {"ps_cmpo0", OpType::PS, FL_IN_FLOAT_AB | FL_SET_CRn | FL_USE_FPU | FL_READ_FPRF | FL_SET_FPRF | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}},
|
||||
{40, Interpreter::ps_neg, {"ps_neg", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_B | FL_IN_FLOAT_B_BITEXACT | FL_RC_BIT_F | FL_USE_FPU | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}},
|
||||
{136, Interpreter::ps_nabs, {"ps_nabs", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_B | FL_IN_FLOAT_B_BITEXACT | FL_RC_BIT_F | FL_USE_FPU | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}},
|
||||
{264, Interpreter::ps_abs, {"ps_abs", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_B | FL_IN_FLOAT_B_BITEXACT | FL_RC_BIT_F | FL_USE_FPU | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}},
|
||||
{64, Interpreter::ps_cmpu1, {"ps_cmpu1", OpType::PS, FL_IN_FLOAT_AB | FL_SET_CRn | FL_USE_FPU | FL_READ_FPRF | FL_SET_FPRF | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}},
|
||||
{72, Interpreter::ps_mr, {"ps_mr", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_B | FL_IN_FLOAT_B_BITEXACT | FL_RC_BIT_F | FL_USE_FPU | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}},
|
||||
{96, Interpreter::ps_cmpo1, {"ps_cmpo1", OpType::PS, FL_IN_FLOAT_AB | FL_SET_CRn | FL_USE_FPU | FL_READ_FPRF | FL_SET_FPRF | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}},
|
||||
{528, Interpreter::ps_merge00, {"ps_merge00", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_AB | FL_IN_FLOAT_AB_BITEXACT | FL_RC_BIT_F | FL_USE_FPU | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}},
|
||||
{560, Interpreter::ps_merge01, {"ps_merge01", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_AB | FL_IN_FLOAT_AB_BITEXACT | FL_RC_BIT_F | FL_USE_FPU | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}},
|
||||
{592, Interpreter::ps_merge10, {"ps_merge10", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_AB | FL_IN_FLOAT_AB_BITEXACT | FL_RC_BIT_F | FL_USE_FPU | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}},
|
||||
{624, Interpreter::ps_merge11, {"ps_merge11", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_AB | FL_IN_FLOAT_AB_BITEXACT | FL_RC_BIT_F | FL_USE_FPU | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}},
|
||||
|
||||
{1014, Interpreter::dcbz_l, {"dcbz_l", OpType::System, FL_IN_A0B | FL_LOADSTORE, 1, 0, 0, 0}},
|
||||
{1014, Interpreter::dcbz_l, {"dcbz_l", OpType::System, FL_IN_A0B | FL_LOADSTORE | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}},
|
||||
}};
|
||||
|
||||
static std::array<GekkoOPTemplate, 17> table4_2 =
|
||||
{{
|
||||
{10, Interpreter::ps_sum0, {"ps_sum0", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF, 1, 0, 0, 0}},
|
||||
{11, Interpreter::ps_sum1, {"ps_sum1", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF, 1, 0, 0, 0}},
|
||||
{12, Interpreter::ps_muls0, {"ps_muls0", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_AC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF, 1, 0, 0, 0}},
|
||||
{13, Interpreter::ps_muls1, {"ps_muls1", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_AC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF, 1, 0, 0, 0}},
|
||||
{14, Interpreter::ps_madds0, {"ps_madds0", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF, 1, 0, 0, 0}},
|
||||
{15, Interpreter::ps_madds1, {"ps_madds1", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF, 1, 0, 0, 0}},
|
||||
{18, Interpreter::ps_div, {"ps_div", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_AB | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF, 17, 0, 0, 0}},
|
||||
{20, Interpreter::ps_sub, {"ps_sub", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_AB | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF, 1, 0, 0, 0}},
|
||||
{21, Interpreter::ps_add, {"ps_add", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_AB | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF, 1, 0, 0, 0}},
|
||||
{23, Interpreter::ps_sel, {"ps_sel", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_IN_FLOAT_BC_BITEXACT | FL_RC_BIT_F | FL_USE_FPU, 1, 0, 0, 0}},
|
||||
{24, Interpreter::ps_res, {"ps_res", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_B | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF, 1, 0, 0, 0}},
|
||||
{25, Interpreter::ps_mul, {"ps_mul", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_AC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF, 1, 0, 0, 0}},
|
||||
{26, Interpreter::ps_rsqrte, {"ps_rsqrte", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_B | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF, 2, 0, 0, 0}},
|
||||
{28, Interpreter::ps_msub, {"ps_msub", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF, 1, 0, 0, 0}},
|
||||
{29, Interpreter::ps_madd, {"ps_madd", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF, 1, 0, 0, 0}},
|
||||
{30, Interpreter::ps_nmsub, {"ps_nmsub", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF, 1, 0, 0, 0}},
|
||||
{31, Interpreter::ps_nmadd, {"ps_nmadd", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF, 1, 0, 0, 0}},
|
||||
{10, Interpreter::ps_sum0, {"ps_sum0", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}},
|
||||
{11, Interpreter::ps_sum1, {"ps_sum1", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}},
|
||||
{12, Interpreter::ps_muls0, {"ps_muls0", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_AC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}},
|
||||
{13, Interpreter::ps_muls1, {"ps_muls1", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_AC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}},
|
||||
{14, Interpreter::ps_madds0, {"ps_madds0", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}},
|
||||
{15, Interpreter::ps_madds1, {"ps_madds1", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}},
|
||||
{18, Interpreter::ps_div, {"ps_div", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_AB | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_PROGRAMEXCEPTION, 17, 0, 0, 0}},
|
||||
{20, Interpreter::ps_sub, {"ps_sub", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_AB | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}},
|
||||
{21, Interpreter::ps_add, {"ps_add", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_AB | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}},
|
||||
{23, Interpreter::ps_sel, {"ps_sel", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_IN_FLOAT_BC_BITEXACT | FL_RC_BIT_F | FL_USE_FPU | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}},
|
||||
{24, Interpreter::ps_res, {"ps_res", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_B | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}},
|
||||
{25, Interpreter::ps_mul, {"ps_mul", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_AC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}},
|
||||
{26, Interpreter::ps_rsqrte, {"ps_rsqrte", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_B | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_PROGRAMEXCEPTION, 2, 0, 0, 0}},
|
||||
{28, Interpreter::ps_msub, {"ps_msub", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}},
|
||||
{29, Interpreter::ps_madd, {"ps_madd", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}},
|
||||
{30, Interpreter::ps_nmsub, {"ps_nmsub", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}},
|
||||
{31, Interpreter::ps_nmadd, {"ps_nmadd", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}},
|
||||
}};
|
||||
|
||||
|
||||
@ -157,7 +157,7 @@ static std::array<GekkoOPTemplate, 13> table19 =
|
||||
{150, Interpreter::isync, {"isync", OpType::InstructionCache, FL_EVIL, 1, 0, 0, 0}},
|
||||
{0, Interpreter::mcrf, {"mcrf", OpType::System, FL_EVIL | FL_SET_CRn, 1, 0, 0, 0}},
|
||||
|
||||
{50, Interpreter::rfi, {"rfi", OpType::System, FL_ENDBLOCK | FL_CHECKEXCEPTIONS, 2, 0, 0, 0}},
|
||||
{50, Interpreter::rfi, {"rfi", OpType::System, FL_ENDBLOCK | FL_CHECKEXCEPTIONS | FL_PROGRAMEXCEPTION, 2, 0, 0, 0}},
|
||||
}};
|
||||
|
||||
static std::array<GekkoOPTemplate, 107> table31 =
|
||||
@ -215,7 +215,7 @@ static std::array<GekkoOPTemplate, 107> table31 =
|
||||
{86, Interpreter::dcbf, {"dcbf", OpType::DataCache, FL_IN_A0B | FL_LOADSTORE, 5, 0, 0, 0}},
|
||||
{246, Interpreter::dcbtst, {"dcbtst", OpType::DataCache, 0, 2, 0, 0, 0}},
|
||||
{278, Interpreter::dcbt, {"dcbt", OpType::DataCache, 0, 2, 0, 0, 0}},
|
||||
{470, Interpreter::dcbi, {"dcbi", OpType::DataCache, FL_IN_A0B | FL_LOADSTORE, 5, 0, 0, 0}},
|
||||
{470, Interpreter::dcbi, {"dcbi", OpType::DataCache, FL_IN_A0B | FL_LOADSTORE | FL_PROGRAMEXCEPTION, 5, 0, 0, 0}},
|
||||
{758, Interpreter::dcba, {"dcba", OpType::DataCache, 0, 5, 0, 0, 0}},
|
||||
{1014, Interpreter::dcbz, {"dcbz", OpType::DataCache, FL_IN_A0B | FL_LOADSTORE, 5, 0, 0, 0}},
|
||||
|
||||
@ -279,17 +279,17 @@ static std::array<GekkoOPTemplate, 107> table31 =
|
||||
{983, Interpreter::stfiwx, {"stfiwx", OpType::StoreFP, FL_IN_FLOAT_S | FL_IN_A0B | FL_USE_FPU | FL_LOADSTORE, 1, 0, 0, 0}},
|
||||
|
||||
{19, Interpreter::mfcr, {"mfcr", OpType::System, FL_OUT_D, 1, 0, 0, 0}},
|
||||
{83, Interpreter::mfmsr, {"mfmsr", OpType::System, FL_OUT_D, 1, 0, 0, 0}},
|
||||
{83, Interpreter::mfmsr, {"mfmsr", OpType::System, FL_OUT_D | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}},
|
||||
{144, Interpreter::mtcrf, {"mtcrf", OpType::System, FL_IN_S | FL_SET_CRn, 1, 0, 0, 0}},
|
||||
{146, Interpreter::mtmsr, {"mtmsr", OpType::System, FL_IN_S | FL_ENDBLOCK, 1, 0, 0, 0}},
|
||||
{210, Interpreter::mtsr, {"mtsr", OpType::System, FL_IN_S, 1, 0, 0, 0}},
|
||||
{242, Interpreter::mtsrin, {"mtsrin", OpType::System, FL_IN_SB, 1, 0, 0, 0}},
|
||||
{339, Interpreter::mfspr, {"mfspr", OpType::SPR, FL_OUT_D, 1, 0, 0, 0}},
|
||||
{467, Interpreter::mtspr, {"mtspr", OpType::SPR, FL_IN_S, 2, 0, 0, 0}},
|
||||
{371, Interpreter::mftb, {"mftb", OpType::System, FL_OUT_D | FL_TIMER, 1, 0, 0, 0}},
|
||||
{146, Interpreter::mtmsr, {"mtmsr", OpType::System, FL_IN_S | FL_ENDBLOCK | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}},
|
||||
{210, Interpreter::mtsr, {"mtsr", OpType::System, FL_IN_S | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}},
|
||||
{242, Interpreter::mtsrin, {"mtsrin", OpType::System, FL_IN_SB | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}},
|
||||
{339, Interpreter::mfspr, {"mfspr", OpType::SPR, FL_OUT_D | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}},
|
||||
{467, Interpreter::mtspr, {"mtspr", OpType::SPR, FL_IN_S | FL_PROGRAMEXCEPTION, 2, 0, 0, 0}},
|
||||
{371, Interpreter::mftb, {"mftb", OpType::System, FL_OUT_D | FL_TIMER | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}},
|
||||
{512, Interpreter::mcrxr, {"mcrxr", OpType::System, FL_SET_CRn | FL_READ_CA | FL_SET_CA, 1, 0, 0, 0}},
|
||||
{595, Interpreter::mfsr, {"mfsr", OpType::System, FL_OUT_D, 3, 0, 0, 0}},
|
||||
{659, Interpreter::mfsrin, {"mfsrin", OpType::System, FL_OUT_D | FL_IN_B, 3, 0, 0, 0}},
|
||||
{595, Interpreter::mfsr, {"mfsr", OpType::System, FL_OUT_D | FL_PROGRAMEXCEPTION, 3, 0, 0, 0}},
|
||||
{659, Interpreter::mfsrin, {"mfsrin", OpType::System, FL_OUT_D | FL_IN_B | FL_PROGRAMEXCEPTION, 3, 0, 0, 0}},
|
||||
|
||||
{4, Interpreter::tw, {"tw", OpType::System, FL_IN_AB | FL_ENDBLOCK, 2, 0, 0, 0}},
|
||||
{598, Interpreter::sync, {"sync", OpType::System, 0, 3, 0, 0, 0}},
|
||||
@ -299,8 +299,8 @@ static std::array<GekkoOPTemplate, 107> table31 =
|
||||
{310, Interpreter::eciwx, {"eciwx", OpType::System, FL_IN_A0B | FL_OUT_D | FL_LOADSTORE, 1, 0, 0, 0}},
|
||||
{438, Interpreter::ecowx, {"ecowx", OpType::System, FL_IN_A0B | FL_IN_S | FL_LOADSTORE, 1, 0, 0, 0}},
|
||||
{854, Interpreter::eieio, {"eieio", OpType::System, 0, 1, 0, 0, 0}},
|
||||
{306, Interpreter::tlbie, {"tlbie", OpType::System, FL_IN_B, 1, 0, 0, 0}},
|
||||
{566, Interpreter::tlbsync, {"tlbsync", OpType::System, 0, 1, 0, 0, 0}},
|
||||
{306, Interpreter::tlbie, {"tlbie", OpType::System, FL_IN_B | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}},
|
||||
{566, Interpreter::tlbsync, {"tlbsync", OpType::System, FL_PROGRAMEXCEPTION, 1, 0, 0, 0}},
|
||||
}};
|
||||
|
||||
static std::array<GekkoOPTemplate, 9> table59 =
|
||||
|
@ -1104,8 +1104,8 @@ bool Jit64::DoJit(u32 em_address, JitBlock* b, u32 nextPC)
|
||||
// output, which needs to be bound in the actual instruction compilation.
|
||||
// TODO: make this smarter in the case that we're actually register-starved, i.e.
|
||||
// prioritize the more important registers.
|
||||
gpr.PreloadRegisters(op.regsIn & op.gprInReg);
|
||||
fpr.PreloadRegisters(op.fregsIn & op.fprInXmm);
|
||||
gpr.PreloadRegisters(op.regsIn & op.gprInUse & ~op.gprDiscardable);
|
||||
fpr.PreloadRegisters(op.fregsIn & op.fprInXmm & ~op.fprDiscardable);
|
||||
}
|
||||
|
||||
CompileInstruction(op);
|
||||
@ -1151,7 +1151,12 @@ bool Jit64::DoJit(u32 em_address, JitBlock* b, u32 nextPC)
|
||||
gpr.Commit();
|
||||
fpr.Commit();
|
||||
|
||||
// If we have a register that will never be used again, flush it.
|
||||
// If we have a register that will never be used again, discard or flush it.
|
||||
if (!SConfig::GetInstance().bJITRegisterCacheOff)
|
||||
{
|
||||
gpr.Discard(op.gprDiscardable);
|
||||
fpr.Discard(op.fprDiscardable);
|
||||
}
|
||||
gpr.Flush(~op.gprInUse);
|
||||
fpr.Flush(~op.fprInUse);
|
||||
|
||||
|
@ -298,11 +298,11 @@ void Jit64::reg_imm(UGeckoInstruction inst)
|
||||
{
|
||||
case 14: // addi
|
||||
// occasionally used as MOV - emulate, with immediate propagation
|
||||
if (gpr.IsImm(a) && d != a && a != 0)
|
||||
if (a != 0 && d != a && gpr.IsImm(a))
|
||||
{
|
||||
gpr.SetImmediate32(d, gpr.Imm32(a) + (u32)(s32)inst.SIMM_16);
|
||||
}
|
||||
else if (inst.SIMM_16 == 0 && d != a && a != 0)
|
||||
else if (a != 0 && d != a && inst.SIMM_16 == 0)
|
||||
{
|
||||
RCOpArg Ra = gpr.Use(a, RCMode::Read);
|
||||
RCX64Reg Rd = gpr.Bind(d, RCMode::Write);
|
||||
|
@ -5,6 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <optional>
|
||||
|
||||
#include "Common/Assert.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
@ -20,6 +21,8 @@ public:
|
||||
{
|
||||
/// Value is currently at its default location
|
||||
Default,
|
||||
/// Value is not stored anywhere because we know it won't be read before the next write
|
||||
Discarded,
|
||||
/// Value is currently bound to a x64 register
|
||||
Bound,
|
||||
/// Value is known as an immediate and has not been written back to its default location
|
||||
@ -35,26 +38,30 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
const Gen::OpArg& Location() const { return location; }
|
||||
const std::optional<Gen::OpArg>& Location() const { return location; }
|
||||
|
||||
LocationType GetLocationType() const
|
||||
{
|
||||
if (!location.has_value())
|
||||
return LocationType::Discarded;
|
||||
|
||||
if (!away)
|
||||
{
|
||||
ASSERT(!revertable);
|
||||
|
||||
if (location.IsImm())
|
||||
if (location->IsImm())
|
||||
return LocationType::SpeculativeImmediate;
|
||||
|
||||
ASSERT(location == default_location);
|
||||
return LocationType::Default;
|
||||
}
|
||||
|
||||
ASSERT(location.IsImm() || location.IsSimpleReg());
|
||||
return location.IsImm() ? LocationType::Immediate : LocationType::Bound;
|
||||
ASSERT(location->IsImm() || location->IsSimpleReg());
|
||||
return location->IsImm() ? LocationType::Immediate : LocationType::Bound;
|
||||
}
|
||||
|
||||
bool IsAway() const { return away; }
|
||||
bool IsDiscarded() const { return !location.has_value(); }
|
||||
bool IsBound() const { return GetLocationType() == LocationType::Bound; }
|
||||
|
||||
void SetBoundTo(Gen::X64Reg xreg)
|
||||
@ -63,6 +70,13 @@ public:
|
||||
location = Gen::R(xreg);
|
||||
}
|
||||
|
||||
void SetDiscarded()
|
||||
{
|
||||
ASSERT(!revertable);
|
||||
away = false;
|
||||
location = std::nullopt;
|
||||
}
|
||||
|
||||
void SetFlushed()
|
||||
{
|
||||
ASSERT(!revertable);
|
||||
@ -104,7 +118,7 @@ public:
|
||||
|
||||
private:
|
||||
Gen::OpArg default_location{};
|
||||
Gen::OpArg location{};
|
||||
std::optional<Gen::OpArg> location{};
|
||||
bool away = false; // value not in source register
|
||||
bool revertable = false;
|
||||
size_t locked = 0;
|
||||
@ -122,7 +136,7 @@ public:
|
||||
dirty = dirty_;
|
||||
}
|
||||
|
||||
void SetFlushed()
|
||||
void Unbind()
|
||||
{
|
||||
ppcReg = static_cast<preg_t>(Gen::INVALID_REG);
|
||||
free = true;
|
||||
|
@ -16,12 +16,14 @@ FPURegCache::FPURegCache(Jit64& jit) : RegCache{jit}
|
||||
|
||||
void FPURegCache::StoreRegister(preg_t preg, const OpArg& new_loc)
|
||||
{
|
||||
m_emitter->MOVAPD(new_loc, m_regs[preg].Location().GetSimpleReg());
|
||||
ASSERT_MSG(DYNA_REC, m_regs[preg].IsBound(), "Unbound register - %zu", preg);
|
||||
m_emitter->MOVAPD(new_loc, m_regs[preg].Location()->GetSimpleReg());
|
||||
}
|
||||
|
||||
void FPURegCache::LoadRegister(preg_t preg, X64Reg new_loc)
|
||||
{
|
||||
m_emitter->MOVAPD(new_loc, m_regs[preg].Location());
|
||||
ASSERT_MSG(DYNA_REC, !m_regs[preg].IsDiscarded(), "Discarded register - %zu", preg);
|
||||
m_emitter->MOVAPD(new_loc, m_regs[preg].Location().value());
|
||||
}
|
||||
|
||||
const X64Reg* FPURegCache::GetAllocationOrder(size_t* count) const
|
||||
|
@ -16,12 +16,14 @@ GPRRegCache::GPRRegCache(Jit64& jit) : RegCache{jit}
|
||||
|
||||
void GPRRegCache::StoreRegister(preg_t preg, const OpArg& new_loc)
|
||||
{
|
||||
m_emitter->MOV(32, new_loc, m_regs[preg].Location());
|
||||
ASSERT_MSG(DYNA_REC, !m_regs[preg].IsDiscarded(), "Discarded register - %zu", preg);
|
||||
m_emitter->MOV(32, new_loc, m_regs[preg].Location().value());
|
||||
}
|
||||
|
||||
void GPRRegCache::LoadRegister(preg_t preg, X64Reg new_loc)
|
||||
{
|
||||
m_emitter->MOV(32, ::Gen::R(new_loc), m_regs[preg].Location());
|
||||
ASSERT_MSG(DYNA_REC, !m_regs[preg].IsDiscarded(), "Discarded register - %zu", preg);
|
||||
m_emitter->MOV(32, ::Gen::R(new_loc), m_regs[preg].Location().value());
|
||||
}
|
||||
|
||||
OpArg GPRRegCache::GetDefaultLocation(preg_t preg) const
|
||||
@ -56,7 +58,7 @@ void GPRRegCache::SetImmediate32(preg_t preg, u32 imm_value, bool dirty)
|
||||
|
||||
BitSet32 GPRRegCache::GetRegUtilization() const
|
||||
{
|
||||
return m_jit.js.op->gprInReg;
|
||||
return m_jit.js.op->gprInUse;
|
||||
}
|
||||
|
||||
BitSet32 GPRRegCache::CountRegsIn(preg_t preg, u32 lookahead) const
|
||||
|
@ -314,6 +314,7 @@ bool RegCache::SanityCheck() const
|
||||
switch (m_regs[i].GetLocationType())
|
||||
{
|
||||
case PPCCachedReg::LocationType::Default:
|
||||
case PPCCachedReg::LocationType::Discarded:
|
||||
case PPCCachedReg::LocationType::SpeculativeImmediate:
|
||||
case PPCCachedReg::LocationType::Immediate:
|
||||
break;
|
||||
@ -322,7 +323,7 @@ bool RegCache::SanityCheck() const
|
||||
if (m_regs[i].IsLocked() || m_regs[i].IsRevertable())
|
||||
return false;
|
||||
|
||||
Gen::X64Reg xr = m_regs[i].Location().GetSimpleReg();
|
||||
Gen::X64Reg xr = m_regs[i].Location()->GetSimpleReg();
|
||||
if (m_xregs[xr].IsLocked())
|
||||
return false;
|
||||
if (m_xregs[xr].Contents() != i)
|
||||
@ -380,6 +381,29 @@ RCForkGuard RegCache::Fork()
|
||||
return RCForkGuard{*this};
|
||||
}
|
||||
|
||||
void RegCache::Discard(BitSet32 pregs)
|
||||
{
|
||||
ASSERT_MSG(
|
||||
DYNA_REC,
|
||||
std::none_of(m_xregs.begin(), m_xregs.end(), [](const auto& x) { return x.IsLocked(); }),
|
||||
"Someone forgot to unlock a X64 reg");
|
||||
|
||||
for (preg_t i : pregs)
|
||||
{
|
||||
ASSERT_MSG(DYNA_REC, !m_regs[i].IsLocked(),
|
||||
"Someone forgot to unlock PPC reg %zu (X64 reg %i).", i, RX(i));
|
||||
ASSERT_MSG(DYNA_REC, !m_regs[i].IsRevertable(), "Register transaction is in progress!");
|
||||
|
||||
if (m_regs[i].IsBound())
|
||||
{
|
||||
X64Reg xr = RX(i);
|
||||
m_xregs[xr].Unbind();
|
||||
}
|
||||
|
||||
m_regs[i].SetDiscarded();
|
||||
}
|
||||
}
|
||||
|
||||
void RegCache::Flush(BitSet32 pregs)
|
||||
{
|
||||
ASSERT_MSG(
|
||||
@ -396,6 +420,7 @@ void RegCache::Flush(BitSet32 pregs)
|
||||
switch (m_regs[i].GetLocationType())
|
||||
{
|
||||
case PPCCachedReg::LocationType::Default:
|
||||
case PPCCachedReg::LocationType::Discarded:
|
||||
break;
|
||||
case PPCCachedReg::LocationType::SpeculativeImmediate:
|
||||
// We can have a cached value without a host register through speculative constants.
|
||||
@ -474,8 +499,8 @@ void RegCache::DiscardRegContentsIfCached(preg_t preg)
|
||||
{
|
||||
if (m_regs[preg].IsBound())
|
||||
{
|
||||
X64Reg xr = m_regs[preg].Location().GetSimpleReg();
|
||||
m_xregs[xr].SetFlushed();
|
||||
X64Reg xr = m_regs[preg].Location()->GetSimpleReg();
|
||||
m_xregs[xr].Unbind();
|
||||
m_regs[preg].SetFlushed();
|
||||
}
|
||||
}
|
||||
@ -494,12 +519,15 @@ void RegCache::BindToRegister(preg_t i, bool doLoad, bool makeDirty)
|
||||
|
||||
if (doLoad)
|
||||
{
|
||||
ASSERT_MSG(DYNA_REC, !m_regs[i].IsDiscarded(), "Attempted to load a discarded value");
|
||||
LoadRegister(i, xr);
|
||||
}
|
||||
|
||||
ASSERT_MSG(DYNA_REC,
|
||||
std::none_of(m_regs.begin(), m_regs.end(),
|
||||
[xr](const auto& r) { return r.Location().IsSimpleReg(xr); }),
|
||||
[xr](const auto& r) {
|
||||
return r.Location().has_value() && r.Location()->IsSimpleReg(xr);
|
||||
}),
|
||||
"Xreg %i already bound", xr);
|
||||
|
||||
m_regs[i].SetBoundTo(xr);
|
||||
@ -525,6 +553,7 @@ void RegCache::StoreFromRegister(preg_t i, FlushMode mode)
|
||||
switch (m_regs[i].GetLocationType())
|
||||
{
|
||||
case PPCCachedReg::LocationType::Default:
|
||||
case PPCCachedReg::LocationType::Discarded:
|
||||
case PPCCachedReg::LocationType::SpeculativeImmediate:
|
||||
return;
|
||||
case PPCCachedReg::LocationType::Bound:
|
||||
@ -532,7 +561,7 @@ void RegCache::StoreFromRegister(preg_t i, FlushMode mode)
|
||||
X64Reg xr = RX(i);
|
||||
doStore = m_xregs[xr].IsDirty();
|
||||
if (mode == FlushMode::Full)
|
||||
m_xregs[xr].SetFlushed();
|
||||
m_xregs[xr].Unbind();
|
||||
break;
|
||||
}
|
||||
case PPCCachedReg::LocationType::Immediate:
|
||||
@ -635,13 +664,14 @@ float RegCache::ScoreRegister(X64Reg xreg) const
|
||||
|
||||
const OpArg& RegCache::R(preg_t preg) const
|
||||
{
|
||||
return m_regs[preg].Location();
|
||||
ASSERT_MSG(DYNA_REC, !m_regs[preg].IsDiscarded(), "Discarded register - %zu", preg);
|
||||
return m_regs[preg].Location().value();
|
||||
}
|
||||
|
||||
X64Reg RegCache::RX(preg_t preg) const
|
||||
{
|
||||
ASSERT_MSG(DYNA_REC, m_regs[preg].IsBound(), "Unbound register - %zu", preg);
|
||||
return m_regs[preg].Location().GetSimpleReg();
|
||||
return m_regs[preg].Location()->GetSimpleReg();
|
||||
}
|
||||
|
||||
void RegCache::Lock(preg_t preg)
|
||||
@ -707,6 +737,7 @@ void RegCache::Realize(preg_t preg)
|
||||
}
|
||||
m_constraints[preg].Realized(RCConstraint::RealizedLoc::Mem);
|
||||
return;
|
||||
case PPCCachedReg::LocationType::Discarded:
|
||||
case PPCCachedReg::LocationType::Bound:
|
||||
do_bind();
|
||||
return;
|
||||
|
@ -169,6 +169,7 @@ public:
|
||||
RCX64Reg Scratch(Gen::X64Reg xr);
|
||||
|
||||
RCForkGuard Fork();
|
||||
void Discard(BitSet32 pregs);
|
||||
void Flush(BitSet32 pregs = BitSet32::AllTrue(32));
|
||||
void Revert();
|
||||
void Commit();
|
||||
|
@ -834,7 +834,12 @@ void JitArm64::DoJit(u32 em_address, JitBlock* b, u32 nextPC)
|
||||
if (!CanMergeNextInstructions(1) || js.op[1].opinfo->type != ::OpType::Integer)
|
||||
FlushCarry();
|
||||
|
||||
// If we have a register that will never be used again, flush it.
|
||||
// If we have a register that will never be used again, discard or flush it.
|
||||
if (!SConfig::GetInstance().bJITRegisterCacheOff)
|
||||
{
|
||||
gpr.DiscardRegisters(op.gprDiscardable);
|
||||
fpr.DiscardRegisters(op.fprDiscardable);
|
||||
}
|
||||
gpr.StoreRegisters(~op.gprInUse);
|
||||
fpr.StoreRegisters(~op.fprInUse);
|
||||
|
||||
|
@ -24,6 +24,12 @@ void Arm64RegCache::Init(ARM64XEmitter* emitter)
|
||||
GetAllocationOrder();
|
||||
}
|
||||
|
||||
void Arm64RegCache::DiscardRegisters(BitSet32 regs)
|
||||
{
|
||||
for (int j : regs)
|
||||
DiscardRegister(j);
|
||||
}
|
||||
|
||||
ARM64Reg Arm64RegCache::GetReg()
|
||||
{
|
||||
// If we have no registers left, dump the most stale register first
|
||||
@ -96,8 +102,8 @@ void Arm64RegCache::FlushMostStaleRegister()
|
||||
const auto& reg = m_guest_registers[i];
|
||||
const u32 last_used = reg.GetLastUsed();
|
||||
|
||||
if (last_used > most_stale_amount &&
|
||||
(reg.GetType() != RegType::NotLoaded && reg.GetType() != RegType::Immediate))
|
||||
if (last_used > most_stale_amount && reg.GetType() != RegType::NotLoaded &&
|
||||
reg.GetType() != RegType::Discarded && reg.GetType() != RegType::Immediate)
|
||||
{
|
||||
most_stale_preg = i;
|
||||
most_stale_amount = last_used;
|
||||
@ -107,6 +113,16 @@ void Arm64RegCache::FlushMostStaleRegister()
|
||||
FlushRegister(most_stale_preg, false);
|
||||
}
|
||||
|
||||
void Arm64RegCache::DiscardRegister(size_t preg)
|
||||
{
|
||||
OpArg& reg = m_guest_registers[preg];
|
||||
ARM64Reg host_reg = reg.GetReg();
|
||||
|
||||
reg.Discard();
|
||||
if (host_reg != ARM64Reg::INVALID_REG)
|
||||
UnlockRegister(host_reg);
|
||||
}
|
||||
|
||||
// GPR Cache
|
||||
constexpr size_t GUEST_GPR_COUNT = 32;
|
||||
constexpr size_t GUEST_CR_COUNT = 8;
|
||||
@ -284,6 +300,9 @@ ARM64Reg Arm64GPRCache::R(const GuestRegInfo& guest_reg)
|
||||
return host_reg;
|
||||
}
|
||||
break;
|
||||
case RegType::Discarded:
|
||||
ASSERT_MSG(DYNA_REC, false, "Attempted to read discarded register");
|
||||
break;
|
||||
case RegType::NotLoaded: // Register isn't loaded at /all/
|
||||
{
|
||||
// This is a bit annoying. We try to keep these preloaded as much as possible
|
||||
@ -318,14 +337,18 @@ void Arm64GPRCache::BindToRegister(const GuestRegInfo& guest_reg, bool do_load)
|
||||
const size_t bitsize = guest_reg.bitsize;
|
||||
|
||||
reg.ResetLastUsed();
|
||||
|
||||
reg.SetDirty(true);
|
||||
if (reg.GetType() == RegType::NotLoaded)
|
||||
|
||||
const RegType reg_type = reg.GetType();
|
||||
if (reg_type == RegType::NotLoaded || reg_type == RegType::Discarded)
|
||||
{
|
||||
const ARM64Reg host_reg = bitsize != 64 ? GetReg() : EncodeRegTo64(GetReg());
|
||||
reg.Load(host_reg);
|
||||
if (do_load)
|
||||
{
|
||||
ASSERT_MSG(DYNA_REC, reg_type != RegType::Discarded, "Attempted to load a discarded value");
|
||||
m_emit->LDR(IndexType::Unsigned, host_reg, PPC_REG, u32(guest_reg.ppc_offset));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -407,10 +430,9 @@ void Arm64FPRCache::Flush(FlushMode mode, PPCAnalyst::CodeOp* op)
|
||||
{
|
||||
const RegType reg_type = m_guest_registers[i].GetType();
|
||||
|
||||
if (reg_type != RegType::NotLoaded && reg_type != RegType::Immediate)
|
||||
if (reg_type != RegType::NotLoaded && reg_type != RegType::Discarded &&
|
||||
reg_type != RegType::Immediate)
|
||||
{
|
||||
// XXX: Determine if we can keep a register in the lower 64bits
|
||||
// Which will allow it to be callee saved.
|
||||
FlushRegister(i, mode == FlushMode::MaintainState);
|
||||
}
|
||||
}
|
||||
@ -497,6 +519,9 @@ ARM64Reg Arm64FPRCache::R(size_t preg, RegType type)
|
||||
}
|
||||
return host_reg;
|
||||
}
|
||||
case RegType::Discarded:
|
||||
ASSERT_MSG(DYNA_REC, false, "Attempted to read discarded register");
|
||||
break;
|
||||
case RegType::NotLoaded: // Register isn't loaded at /all/
|
||||
{
|
||||
host_reg = GetReg();
|
||||
@ -536,7 +561,7 @@ ARM64Reg Arm64FPRCache::RW(size_t preg, RegType type)
|
||||
reg.SetDirty(true);
|
||||
|
||||
// If not loaded at all, just alloc a new one.
|
||||
if (reg.GetType() == RegType::NotLoaded)
|
||||
if (reg.GetType() == RegType::NotLoaded || reg.GetType() == RegType::Discarded)
|
||||
{
|
||||
reg.Load(GetReg(), type);
|
||||
return reg.GetReg();
|
||||
@ -637,8 +662,8 @@ void Arm64FPRCache::FlushByHost(ARM64Reg host_reg)
|
||||
const OpArg& reg = m_guest_registers[i];
|
||||
const RegType reg_type = reg.GetType();
|
||||
|
||||
if ((reg_type != RegType::NotLoaded && reg_type != RegType::Immediate) &&
|
||||
reg.GetReg() == host_reg)
|
||||
if (reg_type != RegType::NotLoaded && reg_type != RegType::Discarded &&
|
||||
reg_type != RegType::Immediate && reg.GetReg() == host_reg)
|
||||
{
|
||||
FlushRegister(i, false);
|
||||
return;
|
||||
|
@ -47,6 +47,7 @@ static_assert(PPCSTATE_OFF(xer_so_ov) < 4096, "STRB can't store xer_so_ov!");
|
||||
enum class RegType
|
||||
{
|
||||
NotLoaded,
|
||||
Discarded, // Reg is not loaded because we know it won't be read before the next write
|
||||
Register, // Reg type is register
|
||||
Immediate, // Reg is really a IMM
|
||||
LowerPair, // Only the lower pair of a paired register
|
||||
@ -86,6 +87,15 @@ public:
|
||||
|
||||
m_reg = Arm64Gen::ARM64Reg::INVALID_REG;
|
||||
}
|
||||
void Discard()
|
||||
{
|
||||
// Invalidate any previous information
|
||||
m_type = RegType::Discarded;
|
||||
m_reg = Arm64Gen::ARM64Reg::INVALID_REG;
|
||||
|
||||
// Arbitrarily large value that won't roll over on a lot of increments
|
||||
m_last_used = 0xFFFF;
|
||||
}
|
||||
void Flush()
|
||||
{
|
||||
// Invalidate any previous information
|
||||
@ -143,6 +153,7 @@ public:
|
||||
void Init(Arm64Gen::ARM64XEmitter* emitter);
|
||||
|
||||
virtual void Start(PPCAnalyst::BlockRegStats& stats) {}
|
||||
void DiscardRegisters(BitSet32 regs);
|
||||
// Flushes the register cache in different ways depending on the mode
|
||||
virtual void Flush(FlushMode mode, PPCAnalyst::CodeOp* op) = 0;
|
||||
|
||||
@ -194,6 +205,7 @@ protected:
|
||||
// Flushes a guest register by host provided
|
||||
virtual void FlushByHost(Arm64Gen::ARM64Reg host_reg) = 0;
|
||||
|
||||
void DiscardRegister(size_t preg);
|
||||
virtual void FlushRegister(size_t preg, bool maintain_state) = 0;
|
||||
|
||||
// Get available host registers
|
||||
|
@ -551,6 +551,10 @@ void PPCAnalyzer::SetInstructionStats(CodeBlock* block, CodeOp* code, const Gekk
|
||||
code->outputFPRF = (opinfo->flags & FL_SET_FPRF) != 0;
|
||||
code->canEndBlock = (opinfo->flags & FL_ENDBLOCK) != 0;
|
||||
|
||||
// TODO: Is it possible to determine that some FPU instructions never cause exceptions?
|
||||
code->canCauseException =
|
||||
(opinfo->flags & (FL_LOADSTORE | FL_USE_FPU | FL_PROGRAMEXCEPTION)) != 0;
|
||||
|
||||
code->wantsCA = (opinfo->flags & FL_READ_CA) != 0;
|
||||
code->outputCA = (opinfo->flags & FL_SET_CA) != 0;
|
||||
|
||||
@ -916,7 +920,7 @@ u32 PPCAnalyzer::Analyze(u32 address, CodeBlock* block, CodeBuffer* buffer, std:
|
||||
// Scan for flag dependencies; assume the next block (or any branch that can leave the block)
|
||||
// wants flags, to be safe.
|
||||
bool wantsCR0 = true, wantsCR1 = true, wantsFPRF = true, wantsCA = true;
|
||||
BitSet32 fprInUse, gprInUse, gprInReg, fprInXmm;
|
||||
BitSet32 fprInUse, gprInUse, gprDiscardable, fprDiscardable, fprInXmm;
|
||||
for (int i = block->m_num_instructions - 1; i >= 0; i--)
|
||||
{
|
||||
CodeOp& op = code[i];
|
||||
@ -939,21 +943,26 @@ u32 PPCAnalyzer::Analyze(u32 address, CodeBlock* block, CodeBuffer* buffer, std:
|
||||
wantsCA &= !op.outputCA || opWantsCA;
|
||||
op.gprInUse = gprInUse;
|
||||
op.fprInUse = fprInUse;
|
||||
op.gprInReg = gprInReg;
|
||||
op.gprDiscardable = gprDiscardable;
|
||||
op.fprDiscardable = fprDiscardable;
|
||||
op.fprInXmm = fprInXmm;
|
||||
// TODO: if there's no possible endblocks or exceptions in between, tell the regcache
|
||||
// we can throw away a register if it's going to be overwritten later.
|
||||
gprInUse |= op.regsIn;
|
||||
gprInReg |= op.regsIn;
|
||||
fprInUse |= op.fregsIn;
|
||||
if (op.canEndBlock || op.canCauseException)
|
||||
{
|
||||
gprDiscardable = BitSet32{};
|
||||
fprDiscardable = BitSet32{};
|
||||
}
|
||||
else
|
||||
{
|
||||
gprDiscardable |= op.regsOut;
|
||||
gprDiscardable &= ~op.regsIn;
|
||||
if (op.fregOut >= 0)
|
||||
fprDiscardable[op.fregOut] = true;
|
||||
fprDiscardable &= ~op.fregsIn;
|
||||
}
|
||||
if (strncmp(op.opinfo->opname, "stfd", 4))
|
||||
fprInXmm |= op.fregsIn;
|
||||
// For now, we need to count output registers as "used" though; otherwise the flush
|
||||
// will result in a redundant store (e.g. store to regcache, then store again to
|
||||
// the same location later).
|
||||
gprInUse |= op.regsOut;
|
||||
if (op.fregOut >= 0)
|
||||
fprInUse[op.fregOut] = true;
|
||||
}
|
||||
|
||||
// Forward scan, for flags that need the other direction for calculation.
|
||||
|
@ -45,13 +45,15 @@ struct CodeOp // 16B
|
||||
bool outputFPRF;
|
||||
bool outputCA;
|
||||
bool canEndBlock;
|
||||
bool canCauseException;
|
||||
bool skipLRStack;
|
||||
bool skip; // followed BL-s for example
|
||||
// which registers are still needed after this instruction in this block
|
||||
BitSet32 fprInUse;
|
||||
BitSet32 gprInUse;
|
||||
// just because a register is in use doesn't mean we actually need or want it in an x86 register.
|
||||
BitSet32 gprInReg;
|
||||
// which registers have values which are known to be unused after this instruction
|
||||
BitSet32 gprDiscardable;
|
||||
BitSet32 fprDiscardable;
|
||||
// we do double stores from GPRs, so we don't want to load a PowerPC floating point register into
|
||||
// an XMM only to move it again to a GPR afterwards.
|
||||
BitSet32 fprInXmm;
|
||||
|
@ -65,6 +65,7 @@ enum InstructionFlags : u64
|
||||
FL_IN_FLOAT_C_BITEXACT = (1ull << 31), // The output is based on the exact bits in frC.
|
||||
FL_IN_FLOAT_AB_BITEXACT = FL_IN_FLOAT_A_BITEXACT | FL_IN_FLOAT_B_BITEXACT,
|
||||
FL_IN_FLOAT_BC_BITEXACT = FL_IN_FLOAT_B_BITEXACT | FL_IN_FLOAT_C_BITEXACT,
|
||||
FL_PROGRAMEXCEPTION = (1ull << 32), // May generate a system exception.
|
||||
};
|
||||
|
||||
enum class OpType
|
||||
|
Loading…
Reference in New Issue
Block a user