Commit 2f9d35fc authored by Richard Henderson's avatar Richard Henderson
Browse files

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: default avatarMark Cave-Ayland <mark.cave-ayland@ilande.co.uk>
Signed-off-by: default avatarRichard Henderson <rth@twiddle.net>
parent 80883227
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -549,6 +549,7 @@ void QEMU_NORETURN sparc_cpu_do_unaligned_access(CPUState *cpu, vaddr addr,
                                                 MMUAccessType access_type,
                                                 int mmu_idx,
                                                 uintptr_t retaddr);
void cpu_raise_exception_ra(CPUSPARCState *, int, uintptr_t) QEMU_NORETURN;

#ifndef NO_CPU_IO_DEFS
/* cpu_init.c */
+23 −29
Original line number Diff line number Diff line
@@ -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
+39 −46
Original line number Diff line number Diff line
@@ -301,17 +301,23 @@ static inline target_ulong asi_address_mask(CPUSPARCState *env,
}
#endif

void helper_check_align(CPUSPARCState *env, target_ulong addr, uint32_t align)
static void do_check_align(CPUSPARCState *env, target_ulong addr,
                           uint32_t align, uintptr_t ra)
{
    if (addr & align) {
#ifdef DEBUG_UNALIGNED
        printf("Unaligned access to 0x" TARGET_FMT_lx " from 0x" TARGET_FMT_lx
               "\n", addr, env->pc);
#endif
        helper_raise_exception(env, TT_UNALIGNED);
        cpu_raise_exception_ra(env, TT_UNALIGNED, ra);
    }
}

void helper_check_align(CPUSPARCState *env, target_ulong addr, uint32_t align)
{
    do_check_align(env, addr, align, GETPC());
}

#if !defined(TARGET_SPARC64) && !defined(CONFIG_USER_ONLY) &&   \
    defined(DEBUG_MXCC)
static void dump_mxcc(CPUSPARCState *env)
@@ -440,7 +446,7 @@ uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong addr,
    uint32_t last_addr = addr;
#endif

    helper_check_align(env, addr, size - 1);
    do_check_align(env, addr, size - 1, GETPC());
    switch (asi) {
    case ASI_M_MXCC: /* SuperSparc MXCC registers, or... */
    /* case ASI_LEON_CACHEREGS:  Leon3 cache control */
@@ -708,7 +714,7 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, uint64_t val,
    SPARCCPU *cpu = sparc_env_get_cpu(env);
    CPUState *cs = CPU(cpu);

    helper_check_align(env, addr, size - 1);
    do_check_align(env, addr, size - 1, GETPC());
    switch (asi) {
    case ASI_M_MXCC: /* SuperSparc MXCC registers, or... */
    /* case ASI_LEON_CACHEREGS:  Leon3 cache control */
@@ -1112,10 +1118,10 @@ uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong addr,
#endif

    if (asi < 0x80) {
        helper_raise_exception(env, TT_PRIV_ACT);
        cpu_raise_exception_ra(env, TT_PRIV_ACT, GETPC());
    }

    helper_check_align(env, addr, size - 1);
    do_check_align(env, addr, size - 1, GETPC());
    addr = asi_address_mask(env, asi, addr);

    switch (asi) {
@@ -1218,10 +1224,9 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, target_ulong val,
    dump_asi("write", addr, asi, size, val);
#endif
    if (asi < 0x80) {
        helper_raise_exception(env, TT_PRIV_ACT);
        cpu_raise_exception_ra(env, TT_PRIV_ACT, GETPC());
    }

    helper_check_align(env, addr, size - 1);
    do_check_align(env, addr, size - 1, GETPC());
    addr = asi_address_mask(env, asi, addr);

    /* Convert to little endian */
@@ -1276,7 +1281,7 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, target_ulong val,
    case ASI_PNFL: /* Primary no-fault LE, RO */
    case ASI_SNFL: /* Secondary no-fault LE, RO */
    default:
        helper_raise_exception(env, TT_DATA_ACCESS);
        cpu_raise_exception_ra(env, TT_DATA_ACCESS, GETPC());
        return;
    }
}
@@ -1300,10 +1305,10 @@ uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong addr,
        || (cpu_has_hypervisor(env)
            && asi >= 0x30 && asi < 0x80
            && !(env->hpstate & HS_PRIV))) {
        helper_raise_exception(env, TT_PRIV_ACT);
        cpu_raise_exception_ra(env, TT_PRIV_ACT, GETPC());
    }

    helper_check_align(env, addr, size - 1);
    do_check_align(env, addr, size - 1, GETPC());
    addr = asi_address_mask(env, asi, addr);

    /* process nonfaulting loads first */
@@ -1322,7 +1327,7 @@ uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong addr,
            dump_asi("read ", last_addr, asi, size, ret);
#endif
            /* env->exception_index is set in get_physical_address_data(). */
            helper_raise_exception(env, cs->exception_index);
            cpu_raise_exception_ra(env, cs->exception_index, GETPC());
        }

        /* convert nonfaulting load ASIs to normal load ASIs */
@@ -1614,7 +1619,7 @@ uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong addr,
    case ASI_TWINX_S:  /* Secondary, twinx */
    case ASI_TWINX_SL: /* Secondary, twinx, LE */
        /* These are all 128-bit atomic; only ldda (now ldtxa) allowed */
        helper_raise_exception(env, TT_ILL_INSN);
        cpu_raise_exception_ra(env, TT_ILL_INSN, GETPC());
        return 0;
    }

@@ -1683,10 +1688,10 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, target_ulong val,
        || (cpu_has_hypervisor(env)
            && asi >= 0x30 && asi < 0x80
            && !(env->hpstate & HS_PRIV))) {
        helper_raise_exception(env, TT_PRIV_ACT);
        cpu_raise_exception_ra(env, TT_PRIV_ACT, GETPC());
    }

    helper_check_align(env, addr, size - 1);
    do_check_align(env, addr, size - 1, GETPC());
    addr = asi_address_mask(env, asi, addr);

    /* Convert to little endian */
@@ -2032,7 +2037,7 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, target_ulong val,
    case ASI_TWINX_S:  /* Secondary, twinx */
    case ASI_TWINX_SL: /* Secondary, twinx, LE */
        /* Only stda allowed */
        helper_raise_exception(env, TT_ILL_INSN);
        cpu_raise_exception_ra(env, TT_ILL_INSN, GETPC());
        return;
    case ASI_DCACHE_DATA: /* D-cache data */
    case ASI_DCACHE_TAG: /* D-cache tag access */
@@ -2076,7 +2081,7 @@ void helper_ldda_asi(CPUSPARCState *env, target_ulong addr, int asi)
        || (cpu_has_hypervisor(env)
            && asi >= 0x30 && asi < 0x80
            && !(env->hpstate & HS_PRIV))) {
        helper_raise_exception(env, TT_PRIV_ACT);
        cpu_raise_exception_ra(env, TT_PRIV_ACT, GETPC());
    }

    addr = asi_address_mask(env, asi, addr);
@@ -2085,19 +2090,19 @@ void helper_ldda_asi(CPUSPARCState *env, target_ulong addr, int asi)
#if !defined(CONFIG_USER_ONLY)
    case ASI_TWINX_AIUP:   /* As if user primary, twinx */
    case ASI_TWINX_AIUP_L: /* As if user primary, twinx, LE */
        helper_check_align(env, addr, 0xf);
        do_check_align(env, addr, 0xf, GETPC());
        h = cpu_ldq_user(env, addr);
        l = cpu_ldq_user(env, addr + 8);
        break;
    case ASI_TWINX_AIUS:   /* As if user secondary, twinx */
    case ASI_TWINX_AIUS_L: /* As if user secondary, twinx, LE */
        helper_check_align(env, addr, 0xf);
        do_check_align(env, addr, 0xf, GETPC());
        h = cpu_ldq_user_secondary(env, addr);
        l = cpu_ldq_user_secondary(env, addr + 8);
        break;
    case ASI_TWINX_REAL:   /* Real address, twinx */
    case ASI_TWINX_REAL_L: /* Real address, twinx, LE */
        helper_check_align(env, addr, 0xf);
        do_check_align(env, addr, 0xf, GETPC());
        {
            CPUState *cs = CPU(sparc_env_get_cpu(env));
            h = ldq_phys(cs->as, addr);
@@ -2108,14 +2113,14 @@ void helper_ldda_asi(CPUSPARCState *env, target_ulong addr, int asi)
    case ASI_NUCLEUS_QUAD_LDD_L:
    case ASI_TWINX_N:  /* Nucleus, twinx */
    case ASI_TWINX_NL: /* Nucleus, twinx, LE */
        helper_check_align(env, addr, 0xf);
        do_check_align(env, addr, 0xf, GETPC());
        h = cpu_ldq_nucleus(env, addr);
        l = cpu_ldq_nucleus(env, addr + 8);
        break;
    case ASI_TWINX_S: /* Secondary, twinx */
    case ASI_TWINX_SL: /* Secondary, twinx, LE */
        if (!cpu_hypervisor_mode(env)) {
            helper_check_align(env, addr, 0xf);
            do_check_align(env, addr, 0xf, GETPC());
            if (env->pstate & PS_PRIV) {
                h = cpu_ldq_kernel_secondary(env, addr);
                l = cpu_ldq_kernel_secondary(env, addr + 8);
@@ -2128,7 +2133,7 @@ void helper_ldda_asi(CPUSPARCState *env, target_ulong addr, int asi)
        /* fallthru */
    case ASI_TWINX_P:  /* Primary, twinx */
    case ASI_TWINX_PL: /* Primary, twinx, LE */
        helper_check_align(env, addr, 0xf);
        do_check_align(env, addr, 0xf, GETPC());
        h = cpu_ldq_data(env, addr);
        l = cpu_ldq_data(env, addr + 8);
        break;
@@ -2139,7 +2144,7 @@ void helper_ldda_asi(CPUSPARCState *env, target_ulong addr, int asi)
    case ASI_TWINX_SL: /* Primary, twinx, LE */
        /* ??? Should be available, but we need to implement
           an atomic 128-bit load.  */
        helper_raise_exception(env, TT_PRIV_ACT);
        cpu_raise_exception_ra(env, TT_PRIV_ACT, GETPC());
#endif
    default:
        /* Non-twinx asi, so this is the legacy ldda insn, which
@@ -2147,7 +2152,7 @@ void helper_ldda_asi(CPUSPARCState *env, target_ulong addr, int asi)
        /* ??? The UA2011 manual recommends emulating this with
           a single 64-bit load.  However, LE asis *are* treated
           as two 32-bit loads individually byte swapped.  */
        helper_check_align(env, addr, 0x7);
        do_check_align(env, addr, 7, GETPC());
        QT0.high = (uint32_t)helper_ld_asi(env, addr, asi, MO_UL);
        QT0.low = (uint32_t)helper_ld_asi(env, addr + 4, asi, MO_UL);
        return;
@@ -2196,7 +2201,7 @@ void helper_ldqf(CPUSPARCState *env, target_ulong addr, int mem_idx)
    /* XXX add 128 bit load */
    CPU_QuadU u;

    helper_check_align(env, addr, 7);
    do_check_align(env, addr, 7, GETPC());
#if !defined(CONFIG_USER_ONLY)
    switch (mem_idx) {
    case MMU_USER_IDX:
@@ -2232,7 +2237,7 @@ void helper_stqf(CPUSPARCState *env, target_ulong addr, int mem_idx)
    /* XXX add 128 bit store */
    CPU_QuadU u;

    helper_check_align(env, addr, 7);
    do_check_align(env, addr, 7, GETPC());
#if !defined(CONFIG_USER_ONLY)
    switch (mem_idx) {
    case MMU_USER_IDX:
@@ -2314,11 +2319,8 @@ void sparc_cpu_unassigned_access(CPUState *cs, hwaddr addr,
    }

    if ((env->mmuregs[0] & MMU_E) && !(env->mmuregs[0] & MMU_NF)) {
        if (is_exec) {
            helper_raise_exception(env, TT_CODE_ACCESS);
        } else {
            helper_raise_exception(env, TT_DATA_ACCESS);
        }
        int tt = is_exec ? TT_CODE_ACCESS : TT_DATA_ACCESS;
        cpu_raise_exception_ra(env, tt, GETPC());
    }

    /* flush neverland mappings created during no-fault mode,
@@ -2334,17 +2336,14 @@ void sparc_cpu_unassigned_access(CPUState *cs, hwaddr addr,
{
    SPARCCPU *cpu = SPARC_CPU(cs);
    CPUSPARCState *env = &cpu->env;
    int tt = is_exec ? TT_CODE_ACCESS : TT_DATA_ACCESS;

#ifdef DEBUG_UNASSIGNED
    printf("Unassigned mem access to " TARGET_FMT_plx " from " TARGET_FMT_lx
           "\n", addr, env->pc);
#endif

    if (is_exec) {
        helper_raise_exception(env, TT_CODE_ACCESS);
    } else {
        helper_raise_exception(env, TT_DATA_ACCESS);
    }
    cpu_raise_exception_ra(env, tt, GETPC());
}
#endif
#endif
@@ -2362,10 +2361,7 @@ void QEMU_NORETURN sparc_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
    printf("Unaligned access to 0x" TARGET_FMT_lx " from 0x" TARGET_FMT_lx
           "\n", addr, env->pc);
#endif
    if (retaddr) {
        cpu_restore_state(CPU(cpu), retaddr);
    }
    helper_raise_exception(env, TT_UNALIGNED);
    cpu_raise_exception_ra(env, TT_UNALIGNED, retaddr);
}

/* try to fill the TLB and return an exception if error. If retaddr is
@@ -2379,10 +2375,7 @@ void tlb_fill(CPUState *cs, target_ulong addr, MMUAccessType access_type,

    ret = sparc_cpu_handle_mmu_fault(cs, addr, access_type, mmu_idx);
    if (ret) {
        if (retaddr) {
            cpu_restore_state(cs, retaddr);
        }
        cpu_loop_exit(cs);
        cpu_loop_exit_restore(cs, retaddr);
    }
}
#endif
+0 −3
Original line number Diff line number Diff line
@@ -3458,7 +3458,6 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn)
                break;
            } else if (xop == 0x2b) { /* rdtbr / V9 flushw */
#ifdef TARGET_SPARC64
                save_state(dc);
                gen_helper_flushw(cpu_env);
#else
                if (!supervisor(dc))
@@ -5062,12 +5061,10 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn)
                    /* nop */
                    break;
                case 0x3c:      /* save */
                    save_state(dc);
                    gen_helper_save(cpu_env);
                    gen_store_gpr(dc, rd, cpu_tmp0);
                    break;
                case 0x3d:      /* restore */
                    save_state(dc);
                    gen_helper_restore(cpu_env);
                    gen_store_gpr(dc, rd, cpu_tmp0);
                    break;
+19 −18
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@

#include "qemu/osdep.h"
#include "cpu.h"
#include "exec/exec-all.h"
#include "exec/helper-proto.h"
#include "trace.h"

@@ -111,13 +112,13 @@ void helper_rett(CPUSPARCState *env)
    unsigned int cwp;

    if (env->psret == 1) {
        helper_raise_exception(env, TT_ILL_INSN);
        cpu_raise_exception_ra(env, TT_ILL_INSN, GETPC());
    }

    env->psret = 1;
    cwp = cpu_cwp_inc(env, env->cwp + 1) ;
    if (env->wim & (1 << cwp)) {
        helper_raise_exception(env, TT_WIN_UNF);
        cpu_raise_exception_ra(env, TT_WIN_UNF, GETPC());
    }
    cpu_set_cwp(env, cwp);
    env->psrs = env->psrps;
@@ -131,7 +132,7 @@ void helper_save(CPUSPARCState *env)

    cwp = cpu_cwp_dec(env, env->cwp - 1);
    if (env->wim & (1 << cwp)) {
        helper_raise_exception(env, TT_WIN_OVF);
        cpu_raise_exception_ra(env, TT_WIN_OVF, GETPC());
    }
    cpu_set_cwp(env, cwp);
}
@@ -142,7 +143,7 @@ void helper_restore(CPUSPARCState *env)

    cwp = cpu_cwp_inc(env, env->cwp + 1);
    if (env->wim & (1 << cwp)) {
        helper_raise_exception(env, TT_WIN_UNF);
        cpu_raise_exception_ra(env, TT_WIN_UNF, GETPC());
    }
    cpu_set_cwp(env, cwp);
}
@@ -150,7 +151,7 @@ void helper_restore(CPUSPARCState *env)
void helper_wrpsr(CPUSPARCState *env, target_ulong new_psr)
{
    if ((new_psr & PSR_CWP) >= env->nwindows) {
        helper_raise_exception(env, TT_ILL_INSN);
        cpu_raise_exception_ra(env, TT_ILL_INSN, GETPC());
    } else {
        cpu_put_psr(env, new_psr);
    }
@@ -170,14 +171,14 @@ void helper_save(CPUSPARCState *env)

    cwp = cpu_cwp_dec(env, env->cwp - 1);
    if (env->cansave == 0) {
        helper_raise_exception(env, TT_SPILL | (env->otherwin != 0 ?
                                                (TT_WOTHER |
                                                 ((env->wstate & 0x38) >> 1)) :
                                                ((env->wstate & 0x7) << 2)));
        int tt = TT_SPILL | (env->otherwin != 0
                             ? (TT_WOTHER | ((env->wstate & 0x38) >> 1))
                             : ((env->wstate & 0x7) << 2));
        cpu_raise_exception_ra(env, tt, GETPC());
    } else {
        if (env->cleanwin - env->canrestore == 0) {
            /* XXX Clean windows without trap */
            helper_raise_exception(env, TT_CLRWIN);
            cpu_raise_exception_ra(env, TT_CLRWIN, GETPC());
        } else {
            env->cansave--;
            env->canrestore++;
@@ -192,10 +193,10 @@ void helper_restore(CPUSPARCState *env)

    cwp = cpu_cwp_inc(env, env->cwp + 1);
    if (env->canrestore == 0) {
        helper_raise_exception(env, TT_FILL | (env->otherwin != 0 ?
                                               (TT_WOTHER |
                                                ((env->wstate & 0x38) >> 1)) :
                                               ((env->wstate & 0x7) << 2)));
        int tt = TT_FILL | (env->otherwin != 0
                            ? (TT_WOTHER | ((env->wstate & 0x38) >> 1))
                            : ((env->wstate & 0x7) << 2));
        cpu_raise_exception_ra(env, tt, GETPC());
    } else {
        env->cansave++;
        env->canrestore--;
@@ -206,10 +207,10 @@ void helper_restore(CPUSPARCState *env)
void helper_flushw(CPUSPARCState *env)
{
    if (env->cansave != env->nwindows - 2) {
        helper_raise_exception(env, TT_SPILL | (env->otherwin != 0 ?
                                                (TT_WOTHER |
                                                 ((env->wstate & 0x38) >> 1)) :
                                                ((env->wstate & 0x7) << 2)));
        int tt = TT_SPILL | (env->otherwin != 0
                             ? (TT_WOTHER | ((env->wstate & 0x38) >> 1))
                             : ((env->wstate & 0x7) << 2));
        cpu_raise_exception_ra(env, tt, GETPC());
    }
}