target/microblaze: Unwind properly when raising divide-by-zero

Restore the correct pc when raising divide-by-zero.  Also, the
MSR[DZO] bit is sticky -- it is not cleared with a successful divide.

Tested-by: Edgar E. Iglesias <edgar.iglesias@xilinx.com>
Reviewed-by: Edgar E. Iglesias <edgar.iglesias@xilinx.com>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
Richard Henderson 2020-08-17 23:12:14 -07:00
parent b1354342c1
commit e98651d9ca
2 changed files with 14 additions and 13 deletions

View file

@ -1,7 +1,7 @@
DEF_HELPER_FLAGS_2(raise_exception, TCG_CALL_NO_WG, noreturn, env, i32)
DEF_HELPER_3(divs, i32, env, i32, i32)
DEF_HELPER_3(divu, i32, env, i32, i32)
DEF_HELPER_FLAGS_3(divs, TCG_CALL_NO_WG, i32, env, i32, i32)
DEF_HELPER_FLAGS_3(divu, TCG_CALL_NO_WG, i32, env, i32, i32)
DEF_HELPER_3(fadd, i32, env, i32, i32)
DEF_HELPER_3(frsub, i32, env, i32, i32)

View file

@ -69,26 +69,27 @@ void helper_raise_exception(CPUMBState *env, uint32_t index)
cpu_loop_exit(cs);
}
static inline int div_prepare(CPUMBState *env, uint32_t a, uint32_t b)
static bool check_divz(CPUMBState *env, uint32_t a, uint32_t b, uintptr_t ra)
{
MicroBlazeCPU *cpu = env_archcpu(env);
if (b == 0) {
if (unlikely(b == 0)) {
env->msr |= MSR_DZ;
if ((env->msr & MSR_EE) && cpu->cfg.div_zero_exception) {
if ((env->msr & MSR_EE) &&
env_archcpu(env)->cfg.div_zero_exception) {
CPUState *cs = env_cpu(env);
env->esr = ESR_EC_DIVZERO;
helper_raise_exception(env, EXCP_HW_EXCP);
cs->exception_index = EXCP_HW_EXCP;
cpu_loop_exit_restore(cs, ra);
}
return 0;
return false;
}
env->msr &= ~MSR_DZ;
return 1;
return true;
}
uint32_t helper_divs(CPUMBState *env, uint32_t a, uint32_t b)
{
if (!div_prepare(env, a, b)) {
if (!check_divz(env, a, b, GETPC())) {
return 0;
}
return (int32_t)a / (int32_t)b;
@ -96,7 +97,7 @@ uint32_t helper_divs(CPUMBState *env, uint32_t a, uint32_t b)
uint32_t helper_divu(CPUMBState *env, uint32_t a, uint32_t b)
{
if (!div_prepare(env, a, b)) {
if (!check_divz(env, a, b, GETPC())) {
return 0;
}
return a / b;