[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
[Qemu-devel] [PATCH RFC v4 09/12] hw/rx: RX Target hardware definition, Yoshinori Sato, 2019/03/20
[Qemu-devel] [PATCH RFC v4 02/12] target/rx: TCG helper, Yoshinori Sato, 2019/03/20
[Qemu-devel] [PATCH RFC v4 07/12] hw/timer: RX62N internal timer modules, Yoshinori Sato, 2019/03/20
[Qemu-devel] [PATCH RFC v4 08/12] hw/char: RX62N serical communication interface (SCI), Yoshinori Sato, 2019/03/20
[Qemu-devel] [PATCH RFC v4 01/12] target/rx: TCG translation, Yoshinori Sato, 2019/03/20
Re: [Qemu-devel] [PATCH RFC v4 01/12] target/rx: TCG translation, Richard Henderson, 2019/03/21
Re: [Qemu-devel] [PATCH RFC v4 01/12] target/rx: TCG translation, Yoshinori Sato, 2019/03/25
Re: [Qemu-devel] [PATCH RFC v4 01/12] target/rx: TCG translation, Richard Henderson, 2019/03/25
Re: [Qemu-devel] [PATCH RFC v4 01/12] target/rx: TCG translation, Yoshinori Sato, 2019/03/26
Re: [Qemu-devel] [PATCH RFC v4 01/12] target/rx: TCG translation, Yoshinori Sato, 2019/03/27