qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] [PATCH RFC v4 02/12] target/rx: TCG helper


From: Yoshinori Sato
Subject: Re: [Qemu-devel] [PATCH RFC v4 02/12] target/rx: TCG helper
Date: Mon, 25 Mar 2019 18:12:18 +0900
User-agent: Wanderlust/2.15.9 (Almost Unreal) SEMI-EPG/1.14.7 (Harue) FLIM/1.14.9 (Gojō) APEL/10.8 EasyPG/1.0.0 Emacs/25.1 (x86_64-pc-linux-gnu) MULE/6.0 (HANACHIRUSATO)

On Fri, 22 Mar 2019 02:17:35 +0900,
Richard Henderson wrote:
> 
> On 3/20/19 7:16 AM, Yoshinori Sato wrote:
> > +void rx_cpu_unpack_psw(CPURXState *env, int all)
> > +{
> > +    if (env->psw_pm == 0) {
> > +        env->psw_ipl = extract32(env->psw, PSW_IPL, 4);
> > +        if (all) {
> > +            env->psw_pm = extract32(env->psw, PSW_PM, 1);
> > +        }
> > +        env->psw_u =  extract32(env->psw, PSW_U, 1);
> > +        env->psw_i =  extract32(env->psw, PSW_I, 1);
> > +    }
> > +    env->psw_o = extract32(env->psw, PSW_O, 1) << 31;
> > +    env->psw_s = extract32(env->psw, PSW_S, 1) << 31;
> > +    env->psw_z = extract32(env->psw, PSW_Z, 1) ? 0 : 1;
> > +    env->psw_c = extract32(env->psw, PSW_C, 1);
> > +}
> 
> I think this function should take the psw value to extract as an argument.
> Otherwise, if psw_pm == 1, you're leaving garbage values in env->psw, and I
> think that can be confusing.
> 
> In fact, since pack_psw does not read env->psw at all, I would remove that
> variable.

OK.

> > +uint32_t helper_mvfc(CPURXState *env, uint32_t cr)
> > +{
> > +    switch (cr) {
> > +    case 0:
> > +        return pack_psw(env);
> > +    case 2:
> > +        return env->psw_u ? env->regs[0] : env->usp;
> > +    case 3:
> > +        return env->fpsw;
> > +    case 8:
> > +        return env->bpsw;
> > +    case 9:
> > +        return env->bpc;
> > +    case 10:
> > +        return env->psw_u ? env->isp : env->regs[0];
> > +    case 11:
> > +        return env->fintv;
> > +    case 12:
> > +        return env->intb;
> > +    default:
> > +        g_assert_not_reached();
> > +        return -1;
> 
> This should only assert if it is caught in translate.c and rejected as an
> invalid instruction.  I don't see that happening though.

PC is set to trans_MVFC.
But it difficult. fixed.

> I think the best option is to move all of this into translate.c.  You have
> defined tcg temporaries for (almost) all of these, although many are currently
> unused. It's easy to handle any without tcg temporaries with
> 
>   tcg_gen_ld_i32(tmp, cpu_env, offsetof(CPURXState, field));
> 
> At which point your default case can simply return false to signal an illegal
> instruction.
> 
> I now see that PC is a control register number 1, not handled here, and that
> "MVFC PC, rN" should use ctx->pc.

OK.
mvfc and mvtc body move to translate.c
Since the wrong operand does not become an invalid instruction,
I only output the log.

> 
> > +void helper_mvtc(CPURXState *env, uint32_t cr, uint32_t val)
> > +{
> > +    switch (cr) {
> ...
> > +    default:
> > +        g_assert_not_reached();
> > +    }
> > +}
> 
> Likewise.
> 
> > +void helper_unpack_psw(CPURXState *env)
> > +{
> > +    uint32_t prev_u;
> > +    prev_u = env->psw_u;
> > +    rx_cpu_unpack_psw(env, 1);
> > +    if (prev_u != env->psw_u) {
> > +        if (env->psw_u) {
> > +            env->isp = env->regs[0];
> > +            env->regs[0] = env->usp;
> > +        } else {
> > +            env->usp = env->regs[0];
> > +            env->regs[0] = env->isp;
> > +        }
> > +    }
> > +}
> 
> Again, I think this should take the full psw as an argument.
> 
> > +/* fp operations */
> > +static void update_fpsw(CPURXState *env, float32 ret, uintptr_t retaddr)
> > +{
> > +    int xcpt, cause, enable;
> > +
> > +    env->psw_z = (*((uint32_t *)&ret) == 0); \
> > +    env->psw_s = (*((uint32_t *)&ret) >= 0x80000000UL); \
> 
> (1) You do not need this casting, float32 == uint32_t,
> (2) The result is wrong for -0.0.
> 
>     env->psw_s = ret;               /* sign in bit 31 */
>     env->psw_z = ret & 0x7fffffff;  /* remove sign from -0.0 */

OK.

> > +    xcpt = get_float_exception_flags(&env->fp_status);
> > +
> > +    /* Clear the cause entries */
> > +    env->fpsw = deposit32(env->fpsw, FPSW_CAUSE, 5, 0);
> > +
> > +    if (unlikely(xcpt)) {
> > +        if (xcpt & float_flag_invalid) {
> > +            env->fpsw = deposit32(env->fpsw, FPSW_CAUSE_V, 1, 1);
> > +            env->fpsw = deposit32(env->fpsw, FPSW_FLAG_V, 1, 1);
> > +        }
> > +        if (xcpt & float_flag_divbyzero) {
> > +            env->fpsw = deposit32(env->fpsw, FPSW_CAUSE_Z, 1, 1);
> > +            env->fpsw = deposit32(env->fpsw, FPSW_FLAG_Z, 1, 1);
> > +        }
> > +        if (xcpt & float_flag_overflow) {
> > +            env->fpsw = deposit32(env->fpsw, FPSW_CAUSE_O, 1, 1);
> > +            env->fpsw = deposit32(env->fpsw, FPSW_FLAG_O, 1, 1);
> > +        }
> > +        if (xcpt & float_flag_underflow) {
> > +            env->fpsw = deposit32(env->fpsw, FPSW_CAUSE_U, 1, 1);
> > +            env->fpsw = deposit32(env->fpsw, FPSW_FLAG_U, 1, 1);
> > +        }
> > +        if (xcpt & float_flag_inexact) {
> > +            env->fpsw = deposit32(env->fpsw, FPSW_CAUSE_X, 1, 1);
> > +            env->fpsw = deposit32(env->fpsw, FPSW_FLAG_X, 1, 1);
> > +        }
> 
> What I don't see here is the "unimplemented processing" exception that you get
> for denormal inputs or outputs, and DN = 0.
> 
> There is support for this in softfloat, although it looks funny.
> You must always have
> 
>   set_flush_to_zero(1, &env->fp_status);
>   set_flush_inputs_to_zero(1, &env->fp_status);
> 
> Do this during reset, I think.
> 
> This enables within softfloat the float_flag_{input,output}_denormal bits.
> Here in update_fpsw you would do
> 
>     if ((xcpt & (float_flag_input_denormal
>                  | float_flag_output_denormal))
>         && !(env->fpsw & FPSW_DN)) {
>         env->fpsw = deposit32(env->fpsw, FPSW_CAUSE_E, 1, 1);
>     }
> 
> > +        /* Generate an exception if enabled */
> > +        cause = extract32(env->fpsw, FPSW_CAUSE, 5);
> > +        enable = extract32(env->fpsw, FPSW_ENABLE, 5);
> 
>     /* Cause E, unimplemented processing, cannot be disabled.  */
>     enable |= (1 << 5);
> 
> > +        if (cause & enable) {
> > +            raise_exception(env, 21, retaddr);
> > +        }
> > +    }
> > +}

OK.

> > +
> > +static void set_fpmode(CPURXState *env, uint32_t val)
> > +{
> > +    static const int roundmode[] = {
> > +        float_round_nearest_even,
> > +        float_round_to_zero,
> > +        float_round_up,
> > +        float_round_down,
> > +    };
> > +    env->fpsw = val & FPSW_MASK;
> > +    set_float_rounding_mode(roundmode[val & FPSW_RM_MASK],
> > +                            &env->fp_status);
> > +    set_flush_to_zero((val & FPSW_DN) != 0, &env->fp_status);
> 
> As discussed above, do not set_flush_to_zero here.

OK. Remove it.
Since the handling of fpsw was not correct, I am making some fix.

> > +#define FLOATOP(op, func)                                           \
> > +float32 helper_##op(CPURXState *env, float32 t0, float32 t1) \
> > +{ \
> > +    float32 ret; \
> > +    ret = func(t0, t1, &env->fp_status); \
> > +    update_fpsw(env, *(uint32_t *)&ret, GETPC());        \
> 
> No casting required.

OK.

> > +void helper_fcmp(CPURXState *env, float32 t0, float32 t1)
> > +{
> > +    int st;
> > +    st = float32_compare(t0, t1, &env->fp_status);
> > +    update_fpsw(env, 0, GETPC());
> > +    env->psw_z = env->psw_s = env->psw_o = 0;
> 
> env->psw_z = 1, because Z == !psw_z.

OK.
It seems that I forgot the changes last time.

> > +    switch (st) {
> > +    case float_relation_equal:
> > +        env->psw_z = 0;
> > +        break;
> > +    case float_relation_less:
> > +        env->psw_s = -1;
> > +        break;
> > +    case float_relation_unordered:
> > +        env->psw_o = 1 << 31;
> 
>    -1 works here too.

OK.

> 
> > +void helper_sstr(CPURXState *env, uint32_t sz)
> > +{
> > +    while (env->regs[3] != 0) {
> > +        switch (sz) {
> > +        case 0:
> > +            cpu_stb_data_ra(env, env->regs[1], env->regs[2], GETPC());
> > +            break;
> > +        case 1:
> > +            cpu_stw_data_ra(env, env->regs[1], env->regs[2], GETPC());
> > +            break;
> > +        case 2:
> > +            cpu_stl_data_ra(env, env->regs[1], env->regs[2], GETPC());
> > +            break;
> > +        }
> > +        env->regs[1] += (1 << sz);
> > +        env->regs[3]--;
> > +    }
> 
> This should probably be split into 3 functions, so that the switch does not
> have to be re-evaluated within the loop.

OK.
I made it a jump table.

> > +static void rx_search(uint32_t mode, int sz, CPURXState *env)
> > +{
> > +    uint32_t tmp;
> > +
> > +    while (env->regs[3] != 0) {
> > +        tmp = ld[sz](env, env->regs[1], env->pc);
> > +        env->regs[1] += 1 << (mode % 4);
> 
> This increment doesn't look right for SWHILE.
> 
> > +        env->regs[3]--;
> > +        if ((mode == OP_SWHILE && tmp != env->regs[2]) ||
> > +            (mode == OP_SUNTIL && tmp == env->regs[2])) {
> > +            break;
> > +        }
> > +    }
> > +    env->psw_z = (mode == OP_SUNTIL) ?
> > +        (tmp - env->regs[2]) : env->regs[3];
> > +    env->psw_c = (tmp <= env->regs[2]);
> > +}
> 
> I think this whole function would be clearer split between SWHILE and SUNTIL.
> You're missing the initial R3 == 0 check (if executed with R3 == 0, no effect
> on registers or flags).

OK.
It split helper_suntil and helper_swhile.

> 
> > +/* accumlator operations */
> > +void helper_rmpa(CPURXState *env, uint32_t sz)
> > +{
> > +    uint64_t result_l, prev;
> > +    int32_t result_h;
> > +    int64_t tmp0, tmp1;
> > +
> > +    if (env->regs[3] == 0) {
> > +        return;
> > +    }
> > +    result_l = env->regs[5];
> > +    result_l <<= 32;
> > +    result_l |= env->regs[4];
> > +    result_h = env->regs[6];
> > +    env->psw_o = 0;
> > +
> > +    while (env->regs[3] != 0) {
> > +        tmp0 = ld[sz](env, env->regs[1], env->pc);
> > +        tmp1 = ld[sz](env, env->regs[2], env->pc);
> 
> You need to use signed load functions here.

OK.

> > +void helper_satr(CPURXState *env)
> > +{
> > +    if (env->psw_o >> 31) {
> > +        if ((int)env->psw_s < 0) {
> > +            env->regs[4] = 0x00000000;
> > +            env->regs[5] = 0x7fffffff;
> > +            env->regs[6] = 0xffffffff;
> > +        } else {
> > +            env->regs[4] = 0xffffffff;
> > +            env->regs[5] = 0x80000000;
> > +            env->regs[6] = 0x00000000;
> 
> These constants are in the wrong order.

OK.
It is my mistake.

> > +/* div */
> > +uint32_t helper_div(CPURXState *env, uint32_t num, uint32_t den)
> > +{
> > +    uint32_t ret = num;
> > +    if (den != 0) {
> > +        ret = (int32_t)num / (int32_t)den;
> > +    }
> > +    env->psw_o = ((num == INT_MIN && den == -1) || den == 0) << 31;
> 
> Because of x86 host, which will raise SIGFPE for INT_MIN/-1, this needs to be
> 
>     if (den == 0 || (num == INT_MIN && den == -1)) {
>         ret = num;  /* undefined */
>         env->psw_o = -1;
>     } else {
>         ret = (int32_t)num / (int32_t)den;
>         env->psw_o = 0;
>     }
> 
> 
> r~
> 

OK.
I checked the overflow before divide.

-- 
Yosinori Sato



reply via email to

[Prev in Thread] Current Thread [Next in Thread]