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:
parent
b1354342c1
commit
e98651d9ca
|
@ -1,7 +1,7 @@
|
||||||
DEF_HELPER_FLAGS_2(raise_exception, TCG_CALL_NO_WG, noreturn, env, i32)
|
DEF_HELPER_FLAGS_2(raise_exception, TCG_CALL_NO_WG, noreturn, env, i32)
|
||||||
|
|
||||||
DEF_HELPER_3(divs, i32, env, i32, i32)
|
DEF_HELPER_FLAGS_3(divs, TCG_CALL_NO_WG, i32, env, i32, i32)
|
||||||
DEF_HELPER_3(divu, 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(fadd, i32, env, i32, i32)
|
||||||
DEF_HELPER_3(frsub, i32, env, i32, i32)
|
DEF_HELPER_3(frsub, i32, env, i32, i32)
|
||||||
|
|
|
@ -69,26 +69,27 @@ void helper_raise_exception(CPUMBState *env, uint32_t index)
|
||||||
cpu_loop_exit(cs);
|
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 (unlikely(b == 0)) {
|
||||||
|
|
||||||
if (b == 0) {
|
|
||||||
env->msr |= MSR_DZ;
|
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;
|
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 true;
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t helper_divs(CPUMBState *env, uint32_t a, uint32_t b)
|
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 0;
|
||||||
}
|
}
|
||||||
return (int32_t)a / (int32_t)b;
|
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)
|
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 0;
|
||||||
}
|
}
|
||||||
return a / b;
|
return a / b;
|
||||||
|
|
Loading…
Reference in a new issue