aboutsummaryrefslogtreecommitdiff
path: root/target-sparc/helper.c
diff options
context:
space:
mode:
authorRichard Henderson <rth@twiddle.net>2016-07-12 13:12:50 -0700
committerRichard Henderson <rth@twiddle.net>2016-10-31 09:46:25 -0600
commit2f9d35fc4006122bad33f9ae3e2e51d2263e98ee (patch)
treef6499fba9d4ce0cc5c0860fff9ea939784257ded /target-sparc/helper.c
parent808832277af11dafee5a55da2b9e41d019b879ca (diff)
target-sparc: Introduce cpu_raise_exception_ra
Several helpers call helper_raise_exception directly, which requires in turn that their callers have performed save_state. The new function allows a TCG return address to be passed in so that we can restore PC + NPC + flags data from that. This fixes a bug in the usage of helper_check_align, whose callers had not been calling save_state. It fixes another bug in which the divide helpers used GETPC at a level other than the direct callee from TCG. This allows the translator to avoid save_state prior to SAVE, RESTORE, and FLUSHW instructions. Tested-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk> Signed-off-by: Richard Henderson <rth@twiddle.net>
Diffstat (limited to 'target-sparc/helper.c')
-rw-r--r--target-sparc/helper.c52
1 files changed, 23 insertions, 29 deletions
diff --git a/target-sparc/helper.c b/target-sparc/helper.c
index bedc6722a1..359b0b15ed 100644
--- a/target-sparc/helper.c
+++ b/target-sparc/helper.c
@@ -24,6 +24,14 @@
#include "exec/helper-proto.h"
#include "sysemu/sysemu.h"
+void cpu_raise_exception_ra(CPUSPARCState *env, int tt, uintptr_t ra)
+{
+ CPUState *cs = CPU(sparc_env_get_cpu(env));
+
+ cs->exception_index = tt;
+ cpu_loop_exit_restore(cs, ra);
+}
+
void helper_raise_exception(CPUSPARCState *env, int tt)
{
CPUState *cs = CPU(sparc_env_get_cpu(env));
@@ -59,7 +67,7 @@ uint64_t helper_tick_get_count(CPUSPARCState *env, void *opaque, int mem_idx)
CPUTimer *timer = opaque;
if (timer->npt && mem_idx < MMU_KERNEL_IDX) {
- helper_raise_exception(env, TT_PRIV_INSN);
+ cpu_raise_exception_ra(env, TT_PRIV_INSN, GETPC());
}
return cpu_tick_get_count(timer);
@@ -76,10 +84,9 @@ void helper_tick_set_limit(void *opaque, uint64_t limit)
}
#endif
-static target_ulong helper_udiv_common(CPUSPARCState *env, target_ulong a,
- target_ulong b, int cc)
+static target_ulong do_udiv(CPUSPARCState *env, target_ulong a,
+ target_ulong b, int cc, uintptr_t ra)
{
- SPARCCPU *cpu = sparc_env_get_cpu(env);
int overflow = 0;
uint64_t x0;
uint32_t x1;
@@ -88,8 +95,7 @@ static target_ulong helper_udiv_common(CPUSPARCState *env, target_ulong a,
x1 = (b & 0xffffffff);
if (x1 == 0) {
- cpu_restore_state(CPU(cpu), GETPC());
- helper_raise_exception(env, TT_DIV_ZERO);
+ cpu_raise_exception_ra(env, TT_DIV_ZERO, ra);
}
x0 = x0 / x1;
@@ -108,18 +114,17 @@ static target_ulong helper_udiv_common(CPUSPARCState *env, target_ulong a,
target_ulong helper_udiv(CPUSPARCState *env, target_ulong a, target_ulong b)
{
- return helper_udiv_common(env, a, b, 0);
+ return do_udiv(env, a, b, 0, GETPC());
}
target_ulong helper_udiv_cc(CPUSPARCState *env, target_ulong a, target_ulong b)
{
- return helper_udiv_common(env, a, b, 1);
+ return do_udiv(env, a, b, 1, GETPC());
}
-static target_ulong helper_sdiv_common(CPUSPARCState *env, target_ulong a,
- target_ulong b, int cc)
+static target_ulong do_sdiv(CPUSPARCState *env, target_ulong a,
+ target_ulong b, int cc, uintptr_t ra)
{
- SPARCCPU *cpu = sparc_env_get_cpu(env);
int overflow = 0;
int64_t x0;
int32_t x1;
@@ -128,8 +133,7 @@ static target_ulong helper_sdiv_common(CPUSPARCState *env, target_ulong a,
x1 = (b & 0xffffffff);
if (x1 == 0) {
- cpu_restore_state(CPU(cpu), GETPC());
- helper_raise_exception(env, TT_DIV_ZERO);
+ cpu_raise_exception_ra(env, TT_DIV_ZERO, ra);
} else if (x1 == -1 && x0 == INT64_MIN) {
x0 = INT32_MAX;
overflow = 1;
@@ -151,12 +155,12 @@ static target_ulong helper_sdiv_common(CPUSPARCState *env, target_ulong a,
target_ulong helper_sdiv(CPUSPARCState *env, target_ulong a, target_ulong b)
{
- return helper_sdiv_common(env, a, b, 0);
+ return do_sdiv(env, a, b, 0, GETPC());
}
target_ulong helper_sdiv_cc(CPUSPARCState *env, target_ulong a, target_ulong b)
{
- return helper_sdiv_common(env, a, b, 1);
+ return do_sdiv(env, a, b, 1, GETPC());
}
#ifdef TARGET_SPARC64
@@ -164,10 +168,7 @@ int64_t helper_sdivx(CPUSPARCState *env, int64_t a, int64_t b)
{
if (b == 0) {
/* Raise divide by zero trap. */
- SPARCCPU *cpu = sparc_env_get_cpu(env);
-
- cpu_restore_state(CPU(cpu), GETPC());
- helper_raise_exception(env, TT_DIV_ZERO);
+ cpu_raise_exception_ra(env, TT_DIV_ZERO, GETPC());
} else if (b == -1) {
/* Avoid overflow trap with i386 divide insn. */
return -a;
@@ -180,10 +181,7 @@ uint64_t helper_udivx(CPUSPARCState *env, uint64_t a, uint64_t b)
{
if (b == 0) {
/* Raise divide by zero trap. */
- SPARCCPU *cpu = sparc_env_get_cpu(env);
-
- cpu_restore_state(CPU(cpu), GETPC());
- helper_raise_exception(env, TT_DIV_ZERO);
+ cpu_raise_exception_ra(env, TT_DIV_ZERO, GETPC());
}
return a / b;
}
@@ -192,7 +190,6 @@ uint64_t helper_udivx(CPUSPARCState *env, uint64_t a, uint64_t b)
target_ulong helper_taddcctv(CPUSPARCState *env, target_ulong src1,
target_ulong src2)
{
- SPARCCPU *cpu = sparc_env_get_cpu(env);
target_ulong dst;
/* Tag overflow occurs if either input has bits 0 or 1 set. */
@@ -215,14 +212,12 @@ target_ulong helper_taddcctv(CPUSPARCState *env, target_ulong src1,
return dst;
tag_overflow:
- cpu_restore_state(CPU(cpu), GETPC());
- helper_raise_exception(env, TT_TOVF);
+ cpu_raise_exception_ra(env, TT_TOVF, GETPC());
}
target_ulong helper_tsubcctv(CPUSPARCState *env, target_ulong src1,
target_ulong src2)
{
- SPARCCPU *cpu = sparc_env_get_cpu(env);
target_ulong dst;
/* Tag overflow occurs if either input has bits 0 or 1 set. */
@@ -245,8 +240,7 @@ target_ulong helper_tsubcctv(CPUSPARCState *env, target_ulong src1,
return dst;
tag_overflow:
- cpu_restore_state(CPU(cpu), GETPC());
- helper_raise_exception(env, TT_TOVF);
+ cpu_raise_exception_ra(env, TT_TOVF, GETPC());
}
#ifndef TARGET_SPARC64