From e98651d9ca475259a6721f6f1cff5da1ad4f0cc1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 17 Aug 2020 23:12:14 -0700 Subject: [PATCH] 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 Reviewed-by: Edgar E. Iglesias Signed-off-by: Richard Henderson --- target/microblaze/helper.h | 4 ++-- target/microblaze/op_helper.c | 23 ++++++++++++----------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/target/microblaze/helper.h b/target/microblaze/helper.h index 6f7f96421f..79e1e8ecc7 100644 --- a/target/microblaze/helper.h +++ b/target/microblaze/helper.h @@ -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) diff --git a/target/microblaze/op_helper.c b/target/microblaze/op_helper.c index f976d112eb..d99d98051a 100644 --- a/target/microblaze/op_helper.c +++ b/target/microblaze/op_helper.c @@ -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; -- 2.11.4.GIT