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}},
|
{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}},
|
{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}},
|
{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, 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, 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, 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
|
//missing: 0, 1, 2, 5, 6, 9, 22, 30, 62, 58
|
||||||
}};
|
}};
|
||||||
|
|
||||||
static std::array<GekkoOPTemplate, 13> table4 =
|
static std::array<GekkoOPTemplate, 13> table4 =
|
||||||
{{ //SUBOP10
|
{{ //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}},
|
{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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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 =
|
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}},
|
{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, 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, 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, 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, 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, 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, 17, 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, 1, 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, 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, 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, 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, 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, 2, 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, 1, 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, 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, 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, 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}},
|
{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}},
|
{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 =
|
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}},
|
{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}},
|
{246, Interpreter::dcbtst, {"dcbtst", OpType::DataCache, 0, 2, 0, 0, 0}},
|
||||||
{278, Interpreter::dcbt, {"dcbt", 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}},
|
{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}},
|
{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}},
|
{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}},
|
{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}},
|
{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}},
|
{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, 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, 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, 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, 2, 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, 1, 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}},
|
{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}},
|
{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, 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}},
|
{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}},
|
{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}},
|
{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}},
|
{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}},
|
{854, Interpreter::eieio, {"eieio", OpType::System, 0, 1, 0, 0, 0}},
|
||||||
{306, Interpreter::tlbie, {"tlbie", OpType::System, FL_IN_B, 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, 0, 1, 0, 0, 0}},
|
{566, Interpreter::tlbsync, {"tlbsync", OpType::System, FL_PROGRAMEXCEPTION, 1, 0, 0, 0}},
|
||||||
}};
|
}};
|
||||||
|
|
||||||
static std::array<GekkoOPTemplate, 9> table59 =
|
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.
|
// 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.
|
// TODO: make this smarter in the case that we're actually register-starved, i.e.
|
||||||
// prioritize the more important registers.
|
// prioritize the more important registers.
|
||||||
gpr.PreloadRegisters(op.regsIn & op.gprInReg);
|
gpr.PreloadRegisters(op.regsIn & op.gprInUse & ~op.gprDiscardable);
|
||||||
fpr.PreloadRegisters(op.fregsIn & op.fprInXmm);
|
fpr.PreloadRegisters(op.fregsIn & op.fprInXmm & ~op.fprDiscardable);
|
||||||
}
|
}
|
||||||
|
|
||||||
CompileInstruction(op);
|
CompileInstruction(op);
|
||||||
|
@ -1151,7 +1151,12 @@ bool Jit64::DoJit(u32 em_address, JitBlock* b, u32 nextPC)
|
||||||
gpr.Commit();
|
gpr.Commit();
|
||||||
fpr.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);
|
gpr.Flush(~op.gprInUse);
|
||||||
fpr.Flush(~op.fprInUse);
|
fpr.Flush(~op.fprInUse);
|
||||||
|
|
||||||
|
|
|
@ -298,11 +298,11 @@ void Jit64::reg_imm(UGeckoInstruction inst)
|
||||||
{
|
{
|
||||||
case 14: // addi
|
case 14: // addi
|
||||||
// occasionally used as MOV - emulate, with immediate propagation
|
// 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);
|
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);
|
RCOpArg Ra = gpr.Use(a, RCMode::Read);
|
||||||
RCX64Reg Rd = gpr.Bind(d, RCMode::Write);
|
RCX64Reg Rd = gpr.Bind(d, RCMode::Write);
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
#include "Common/Assert.h"
|
#include "Common/Assert.h"
|
||||||
#include "Common/CommonTypes.h"
|
#include "Common/CommonTypes.h"
|
||||||
|
@ -20,6 +21,8 @@ public:
|
||||||
{
|
{
|
||||||
/// Value is currently at its default location
|
/// Value is currently at its default location
|
||||||
Default,
|
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
|
/// Value is currently bound to a x64 register
|
||||||
Bound,
|
Bound,
|
||||||
/// Value is known as an immediate and has not been written back to its default location
|
/// 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
|
LocationType GetLocationType() const
|
||||||
{
|
{
|
||||||
|
if (!location.has_value())
|
||||||
|
return LocationType::Discarded;
|
||||||
|
|
||||||
if (!away)
|
if (!away)
|
||||||
{
|
{
|
||||||
ASSERT(!revertable);
|
ASSERT(!revertable);
|
||||||
|
|
||||||
if (location.IsImm())
|
if (location->IsImm())
|
||||||
return LocationType::SpeculativeImmediate;
|
return LocationType::SpeculativeImmediate;
|
||||||
|
|
||||||
ASSERT(location == default_location);
|
ASSERT(location == default_location);
|
||||||
return LocationType::Default;
|
return LocationType::Default;
|
||||||
}
|
}
|
||||||
|
|
||||||
ASSERT(location.IsImm() || location.IsSimpleReg());
|
ASSERT(location->IsImm() || location->IsSimpleReg());
|
||||||
return location.IsImm() ? LocationType::Immediate : LocationType::Bound;
|
return location->IsImm() ? LocationType::Immediate : LocationType::Bound;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsAway() const { return away; }
|
bool IsAway() const { return away; }
|
||||||
|
bool IsDiscarded() const { return !location.has_value(); }
|
||||||
bool IsBound() const { return GetLocationType() == LocationType::Bound; }
|
bool IsBound() const { return GetLocationType() == LocationType::Bound; }
|
||||||
|
|
||||||
void SetBoundTo(Gen::X64Reg xreg)
|
void SetBoundTo(Gen::X64Reg xreg)
|
||||||
|
@ -63,6 +70,13 @@ public:
|
||||||
location = Gen::R(xreg);
|
location = Gen::R(xreg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SetDiscarded()
|
||||||
|
{
|
||||||
|
ASSERT(!revertable);
|
||||||
|
away = false;
|
||||||
|
location = std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
void SetFlushed()
|
void SetFlushed()
|
||||||
{
|
{
|
||||||
ASSERT(!revertable);
|
ASSERT(!revertable);
|
||||||
|
@ -104,7 +118,7 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Gen::OpArg default_location{};
|
Gen::OpArg default_location{};
|
||||||
Gen::OpArg location{};
|
std::optional<Gen::OpArg> location{};
|
||||||
bool away = false; // value not in source register
|
bool away = false; // value not in source register
|
||||||
bool revertable = false;
|
bool revertable = false;
|
||||||
size_t locked = 0;
|
size_t locked = 0;
|
||||||
|
@ -122,7 +136,7 @@ public:
|
||||||
dirty = dirty_;
|
dirty = dirty_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetFlushed()
|
void Unbind()
|
||||||
{
|
{
|
||||||
ppcReg = static_cast<preg_t>(Gen::INVALID_REG);
|
ppcReg = static_cast<preg_t>(Gen::INVALID_REG);
|
||||||
free = true;
|
free = true;
|
||||||
|
|
|
@ -16,12 +16,14 @@ FPURegCache::FPURegCache(Jit64& jit) : RegCache{jit}
|
||||||
|
|
||||||
void FPURegCache::StoreRegister(preg_t preg, const OpArg& new_loc)
|
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)
|
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
|
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)
|
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)
|
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
|
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
|
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
|
BitSet32 GPRRegCache::CountRegsIn(preg_t preg, u32 lookahead) const
|
||||||
|
|
|
@ -314,6 +314,7 @@ bool RegCache::SanityCheck() const
|
||||||
switch (m_regs[i].GetLocationType())
|
switch (m_regs[i].GetLocationType())
|
||||||
{
|
{
|
||||||
case PPCCachedReg::LocationType::Default:
|
case PPCCachedReg::LocationType::Default:
|
||||||
|
case PPCCachedReg::LocationType::Discarded:
|
||||||
case PPCCachedReg::LocationType::SpeculativeImmediate:
|
case PPCCachedReg::LocationType::SpeculativeImmediate:
|
||||||
case PPCCachedReg::LocationType::Immediate:
|
case PPCCachedReg::LocationType::Immediate:
|
||||||
break;
|
break;
|
||||||
|
@ -322,7 +323,7 @@ bool RegCache::SanityCheck() const
|
||||||
if (m_regs[i].IsLocked() || m_regs[i].IsRevertable())
|
if (m_regs[i].IsLocked() || m_regs[i].IsRevertable())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Gen::X64Reg xr = m_regs[i].Location().GetSimpleReg();
|
Gen::X64Reg xr = m_regs[i].Location()->GetSimpleReg();
|
||||||
if (m_xregs[xr].IsLocked())
|
if (m_xregs[xr].IsLocked())
|
||||||
return false;
|
return false;
|
||||||
if (m_xregs[xr].Contents() != i)
|
if (m_xregs[xr].Contents() != i)
|
||||||
|
@ -380,6 +381,29 @@ RCForkGuard RegCache::Fork()
|
||||||
return RCForkGuard{*this};
|
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)
|
void RegCache::Flush(BitSet32 pregs)
|
||||||
{
|
{
|
||||||
ASSERT_MSG(
|
ASSERT_MSG(
|
||||||
|
@ -396,6 +420,7 @@ void RegCache::Flush(BitSet32 pregs)
|
||||||
switch (m_regs[i].GetLocationType())
|
switch (m_regs[i].GetLocationType())
|
||||||
{
|
{
|
||||||
case PPCCachedReg::LocationType::Default:
|
case PPCCachedReg::LocationType::Default:
|
||||||
|
case PPCCachedReg::LocationType::Discarded:
|
||||||
break;
|
break;
|
||||||
case PPCCachedReg::LocationType::SpeculativeImmediate:
|
case PPCCachedReg::LocationType::SpeculativeImmediate:
|
||||||
// We can have a cached value without a host register through speculative constants.
|
// 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())
|
if (m_regs[preg].IsBound())
|
||||||
{
|
{
|
||||||
X64Reg xr = m_regs[preg].Location().GetSimpleReg();
|
X64Reg xr = m_regs[preg].Location()->GetSimpleReg();
|
||||||
m_xregs[xr].SetFlushed();
|
m_xregs[xr].Unbind();
|
||||||
m_regs[preg].SetFlushed();
|
m_regs[preg].SetFlushed();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -494,12 +519,15 @@ void RegCache::BindToRegister(preg_t i, bool doLoad, bool makeDirty)
|
||||||
|
|
||||||
if (doLoad)
|
if (doLoad)
|
||||||
{
|
{
|
||||||
|
ASSERT_MSG(DYNA_REC, !m_regs[i].IsDiscarded(), "Attempted to load a discarded value");
|
||||||
LoadRegister(i, xr);
|
LoadRegister(i, xr);
|
||||||
}
|
}
|
||||||
|
|
||||||
ASSERT_MSG(DYNA_REC,
|
ASSERT_MSG(DYNA_REC,
|
||||||
std::none_of(m_regs.begin(), m_regs.end(),
|
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);
|
"Xreg %i already bound", xr);
|
||||||
|
|
||||||
m_regs[i].SetBoundTo(xr);
|
m_regs[i].SetBoundTo(xr);
|
||||||
|
@ -525,6 +553,7 @@ void RegCache::StoreFromRegister(preg_t i, FlushMode mode)
|
||||||
switch (m_regs[i].GetLocationType())
|
switch (m_regs[i].GetLocationType())
|
||||||
{
|
{
|
||||||
case PPCCachedReg::LocationType::Default:
|
case PPCCachedReg::LocationType::Default:
|
||||||
|
case PPCCachedReg::LocationType::Discarded:
|
||||||
case PPCCachedReg::LocationType::SpeculativeImmediate:
|
case PPCCachedReg::LocationType::SpeculativeImmediate:
|
||||||
return;
|
return;
|
||||||
case PPCCachedReg::LocationType::Bound:
|
case PPCCachedReg::LocationType::Bound:
|
||||||
|
@ -532,7 +561,7 @@ void RegCache::StoreFromRegister(preg_t i, FlushMode mode)
|
||||||
X64Reg xr = RX(i);
|
X64Reg xr = RX(i);
|
||||||
doStore = m_xregs[xr].IsDirty();
|
doStore = m_xregs[xr].IsDirty();
|
||||||
if (mode == FlushMode::Full)
|
if (mode == FlushMode::Full)
|
||||||
m_xregs[xr].SetFlushed();
|
m_xregs[xr].Unbind();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PPCCachedReg::LocationType::Immediate:
|
case PPCCachedReg::LocationType::Immediate:
|
||||||
|
@ -635,13 +664,14 @@ float RegCache::ScoreRegister(X64Reg xreg) const
|
||||||
|
|
||||||
const OpArg& RegCache::R(preg_t preg) 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
|
X64Reg RegCache::RX(preg_t preg) const
|
||||||
{
|
{
|
||||||
ASSERT_MSG(DYNA_REC, m_regs[preg].IsBound(), "Unbound register - %zu", preg);
|
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)
|
void RegCache::Lock(preg_t preg)
|
||||||
|
@ -707,6 +737,7 @@ void RegCache::Realize(preg_t preg)
|
||||||
}
|
}
|
||||||
m_constraints[preg].Realized(RCConstraint::RealizedLoc::Mem);
|
m_constraints[preg].Realized(RCConstraint::RealizedLoc::Mem);
|
||||||
return;
|
return;
|
||||||
|
case PPCCachedReg::LocationType::Discarded:
|
||||||
case PPCCachedReg::LocationType::Bound:
|
case PPCCachedReg::LocationType::Bound:
|
||||||
do_bind();
|
do_bind();
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -169,6 +169,7 @@ public:
|
||||||
RCX64Reg Scratch(Gen::X64Reg xr);
|
RCX64Reg Scratch(Gen::X64Reg xr);
|
||||||
|
|
||||||
RCForkGuard Fork();
|
RCForkGuard Fork();
|
||||||
|
void Discard(BitSet32 pregs);
|
||||||
void Flush(BitSet32 pregs = BitSet32::AllTrue(32));
|
void Flush(BitSet32 pregs = BitSet32::AllTrue(32));
|
||||||
void Revert();
|
void Revert();
|
||||||
void Commit();
|
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)
|
if (!CanMergeNextInstructions(1) || js.op[1].opinfo->type != ::OpType::Integer)
|
||||||
FlushCarry();
|
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);
|
gpr.StoreRegisters(~op.gprInUse);
|
||||||
fpr.StoreRegisters(~op.fprInUse);
|
fpr.StoreRegisters(~op.fprInUse);
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,12 @@ void Arm64RegCache::Init(ARM64XEmitter* emitter)
|
||||||
GetAllocationOrder();
|
GetAllocationOrder();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Arm64RegCache::DiscardRegisters(BitSet32 regs)
|
||||||
|
{
|
||||||
|
for (int j : regs)
|
||||||
|
DiscardRegister(j);
|
||||||
|
}
|
||||||
|
|
||||||
ARM64Reg Arm64RegCache::GetReg()
|
ARM64Reg Arm64RegCache::GetReg()
|
||||||
{
|
{
|
||||||
// If we have no registers left, dump the most stale register first
|
// 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 auto& reg = m_guest_registers[i];
|
||||||
const u32 last_used = reg.GetLastUsed();
|
const u32 last_used = reg.GetLastUsed();
|
||||||
|
|
||||||
if (last_used > most_stale_amount &&
|
if (last_used > most_stale_amount && reg.GetType() != RegType::NotLoaded &&
|
||||||
(reg.GetType() != RegType::NotLoaded && reg.GetType() != RegType::Immediate))
|
reg.GetType() != RegType::Discarded && reg.GetType() != RegType::Immediate)
|
||||||
{
|
{
|
||||||
most_stale_preg = i;
|
most_stale_preg = i;
|
||||||
most_stale_amount = last_used;
|
most_stale_amount = last_used;
|
||||||
|
@ -107,6 +113,16 @@ void Arm64RegCache::FlushMostStaleRegister()
|
||||||
FlushRegister(most_stale_preg, false);
|
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
|
// GPR Cache
|
||||||
constexpr size_t GUEST_GPR_COUNT = 32;
|
constexpr size_t GUEST_GPR_COUNT = 32;
|
||||||
constexpr size_t GUEST_CR_COUNT = 8;
|
constexpr size_t GUEST_CR_COUNT = 8;
|
||||||
|
@ -284,6 +300,9 @@ ARM64Reg Arm64GPRCache::R(const GuestRegInfo& guest_reg)
|
||||||
return host_reg;
|
return host_reg;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case RegType::Discarded:
|
||||||
|
ASSERT_MSG(DYNA_REC, false, "Attempted to read discarded register");
|
||||||
|
break;
|
||||||
case RegType::NotLoaded: // Register isn't loaded at /all/
|
case RegType::NotLoaded: // Register isn't loaded at /all/
|
||||||
{
|
{
|
||||||
// This is a bit annoying. We try to keep these preloaded as much as possible
|
// This is a bit annoying. We try to keep these preloaded as much as possible
|
||||||
|
@ -318,16 +337,20 @@ void Arm64GPRCache::BindToRegister(const GuestRegInfo& guest_reg, bool do_load)
|
||||||
const size_t bitsize = guest_reg.bitsize;
|
const size_t bitsize = guest_reg.bitsize;
|
||||||
|
|
||||||
reg.ResetLastUsed();
|
reg.ResetLastUsed();
|
||||||
|
|
||||||
reg.SetDirty(true);
|
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());
|
const ARM64Reg host_reg = bitsize != 64 ? GetReg() : EncodeRegTo64(GetReg());
|
||||||
reg.Load(host_reg);
|
reg.Load(host_reg);
|
||||||
if (do_load)
|
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));
|
m_emit->LDR(IndexType::Unsigned, host_reg, PPC_REG, u32(guest_reg.ppc_offset));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Arm64GPRCache::GetAllocationOrder()
|
void Arm64GPRCache::GetAllocationOrder()
|
||||||
{
|
{
|
||||||
|
@ -407,10 +430,9 @@ void Arm64FPRCache::Flush(FlushMode mode, PPCAnalyst::CodeOp* op)
|
||||||
{
|
{
|
||||||
const RegType reg_type = m_guest_registers[i].GetType();
|
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);
|
FlushRegister(i, mode == FlushMode::MaintainState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -497,6 +519,9 @@ ARM64Reg Arm64FPRCache::R(size_t preg, RegType type)
|
||||||
}
|
}
|
||||||
return host_reg;
|
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/
|
case RegType::NotLoaded: // Register isn't loaded at /all/
|
||||||
{
|
{
|
||||||
host_reg = GetReg();
|
host_reg = GetReg();
|
||||||
|
@ -536,7 +561,7 @@ ARM64Reg Arm64FPRCache::RW(size_t preg, RegType type)
|
||||||
reg.SetDirty(true);
|
reg.SetDirty(true);
|
||||||
|
|
||||||
// If not loaded at all, just alloc a new one.
|
// 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);
|
reg.Load(GetReg(), type);
|
||||||
return reg.GetReg();
|
return reg.GetReg();
|
||||||
|
@ -637,8 +662,8 @@ void Arm64FPRCache::FlushByHost(ARM64Reg host_reg)
|
||||||
const OpArg& reg = m_guest_registers[i];
|
const OpArg& reg = m_guest_registers[i];
|
||||||
const RegType reg_type = reg.GetType();
|
const RegType reg_type = reg.GetType();
|
||||||
|
|
||||||
if ((reg_type != RegType::NotLoaded && reg_type != RegType::Immediate) &&
|
if (reg_type != RegType::NotLoaded && reg_type != RegType::Discarded &&
|
||||||
reg.GetReg() == host_reg)
|
reg_type != RegType::Immediate && reg.GetReg() == host_reg)
|
||||||
{
|
{
|
||||||
FlushRegister(i, false);
|
FlushRegister(i, false);
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -47,6 +47,7 @@ static_assert(PPCSTATE_OFF(xer_so_ov) < 4096, "STRB can't store xer_so_ov!");
|
||||||
enum class RegType
|
enum class RegType
|
||||||
{
|
{
|
||||||
NotLoaded,
|
NotLoaded,
|
||||||
|
Discarded, // Reg is not loaded because we know it won't be read before the next write
|
||||||
Register, // Reg type is register
|
Register, // Reg type is register
|
||||||
Immediate, // Reg is really a IMM
|
Immediate, // Reg is really a IMM
|
||||||
LowerPair, // Only the lower pair of a paired register
|
LowerPair, // Only the lower pair of a paired register
|
||||||
|
@ -86,6 +87,15 @@ public:
|
||||||
|
|
||||||
m_reg = Arm64Gen::ARM64Reg::INVALID_REG;
|
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()
|
void Flush()
|
||||||
{
|
{
|
||||||
// Invalidate any previous information
|
// Invalidate any previous information
|
||||||
|
@ -143,6 +153,7 @@ public:
|
||||||
void Init(Arm64Gen::ARM64XEmitter* emitter);
|
void Init(Arm64Gen::ARM64XEmitter* emitter);
|
||||||
|
|
||||||
virtual void Start(PPCAnalyst::BlockRegStats& stats) {}
|
virtual void Start(PPCAnalyst::BlockRegStats& stats) {}
|
||||||
|
void DiscardRegisters(BitSet32 regs);
|
||||||
// Flushes the register cache in different ways depending on the mode
|
// Flushes the register cache in different ways depending on the mode
|
||||||
virtual void Flush(FlushMode mode, PPCAnalyst::CodeOp* op) = 0;
|
virtual void Flush(FlushMode mode, PPCAnalyst::CodeOp* op) = 0;
|
||||||
|
|
||||||
|
@ -194,6 +205,7 @@ protected:
|
||||||
// Flushes a guest register by host provided
|
// Flushes a guest register by host provided
|
||||||
virtual void FlushByHost(Arm64Gen::ARM64Reg host_reg) = 0;
|
virtual void FlushByHost(Arm64Gen::ARM64Reg host_reg) = 0;
|
||||||
|
|
||||||
|
void DiscardRegister(size_t preg);
|
||||||
virtual void FlushRegister(size_t preg, bool maintain_state) = 0;
|
virtual void FlushRegister(size_t preg, bool maintain_state) = 0;
|
||||||
|
|
||||||
// Get available host registers
|
// 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->outputFPRF = (opinfo->flags & FL_SET_FPRF) != 0;
|
||||||
code->canEndBlock = (opinfo->flags & FL_ENDBLOCK) != 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->wantsCA = (opinfo->flags & FL_READ_CA) != 0;
|
||||||
code->outputCA = (opinfo->flags & FL_SET_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)
|
// Scan for flag dependencies; assume the next block (or any branch that can leave the block)
|
||||||
// wants flags, to be safe.
|
// wants flags, to be safe.
|
||||||
bool wantsCR0 = true, wantsCR1 = true, wantsFPRF = true, wantsCA = true;
|
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--)
|
for (int i = block->m_num_instructions - 1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
CodeOp& op = code[i];
|
CodeOp& op = code[i];
|
||||||
|
@ -939,21 +943,26 @@ u32 PPCAnalyzer::Analyze(u32 address, CodeBlock* block, CodeBuffer* buffer, std:
|
||||||
wantsCA &= !op.outputCA || opWantsCA;
|
wantsCA &= !op.outputCA || opWantsCA;
|
||||||
op.gprInUse = gprInUse;
|
op.gprInUse = gprInUse;
|
||||||
op.fprInUse = fprInUse;
|
op.fprInUse = fprInUse;
|
||||||
op.gprInReg = gprInReg;
|
op.gprDiscardable = gprDiscardable;
|
||||||
|
op.fprDiscardable = fprDiscardable;
|
||||||
op.fprInXmm = fprInXmm;
|
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;
|
gprInUse |= op.regsIn;
|
||||||
gprInReg |= op.regsIn;
|
|
||||||
fprInUse |= op.fregsIn;
|
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))
|
if (strncmp(op.opinfo->opname, "stfd", 4))
|
||||||
fprInXmm |= op.fregsIn;
|
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.
|
// Forward scan, for flags that need the other direction for calculation.
|
||||||
|
|
|
@ -45,13 +45,15 @@ struct CodeOp // 16B
|
||||||
bool outputFPRF;
|
bool outputFPRF;
|
||||||
bool outputCA;
|
bool outputCA;
|
||||||
bool canEndBlock;
|
bool canEndBlock;
|
||||||
|
bool canCauseException;
|
||||||
bool skipLRStack;
|
bool skipLRStack;
|
||||||
bool skip; // followed BL-s for example
|
bool skip; // followed BL-s for example
|
||||||
// which registers are still needed after this instruction in this block
|
// which registers are still needed after this instruction in this block
|
||||||
BitSet32 fprInUse;
|
BitSet32 fprInUse;
|
||||||
BitSet32 gprInUse;
|
BitSet32 gprInUse;
|
||||||
// just because a register is in use doesn't mean we actually need or want it in an x86 register.
|
// which registers have values which are known to be unused after this instruction
|
||||||
BitSet32 gprInReg;
|
BitSet32 gprDiscardable;
|
||||||
|
BitSet32 fprDiscardable;
|
||||||
// we do double stores from GPRs, so we don't want to load a PowerPC floating point register into
|
// 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.
|
// an XMM only to move it again to a GPR afterwards.
|
||||||
BitSet32 fprInXmm;
|
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_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_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_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
|
enum class OpType
|
||||||
|
|
Loading…
Reference in New Issue