diff mbox

[5/5] ppc: Improve generation of conditional traps

Message ID 1469941993-27576-5-git-send-email-benh@kernel.crashing.org
State New
Headers show

Commit Message

Benjamin Herrenschmidt July 31, 2016, 5:13 a.m. UTC
Translate most conditions to TCG conditions and avoid the helper
for most of the common cases.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
 target-ppc/translate.c | 168 ++++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 132 insertions(+), 36 deletions(-)

Comments

Richard Henderson Aug. 6, 2016, 9:03 a.m. UTC | #1
On 07/31/2016 10:43 AM, Benjamin Herrenschmidt wrote:
> Translate most conditions to TCG conditions and avoid the helper
> for most of the common cases.
>
> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> ---
>  target-ppc/translate.c | 168 ++++++++++++++++++++++++++++++++++++++-----------
>  1 file changed, 132 insertions(+), 36 deletions(-)
>
> diff --git a/target-ppc/translate.c b/target-ppc/translate.c
> index 47eb9ed..561976f 100644
> --- a/target-ppc/translate.c
> +++ b/target-ppc/translate.c
> @@ -3639,82 +3639,178 @@ static void gen_sc(DisasContext *ctx)
>
>  /***                                Trap                                   ***/
>
> -/* Check for unconditional traps (always or never) */
> -static bool check_unconditional_trap(DisasContext *ctx)
> +static int TO2tcg[32] = {

Should be const.

> +        -1,             /* 0x0f > or = or u> or u< */
> +        -1,             /* 0x13 < or u< or u> -> weird */
> +        -1,             /* 0x19 < or > or u> -> weird */
> +        -1,             /* 0x1a < or > or u< -> weird */
> +        -1,             /* 0x1b < or > or u> or u< -> weird */

Not that it matters much, but when you have both < and >, or both u< and u>, 
you can collapse to NE, no matter what else is there.

> +#define TRAP_UNCOND     (-1)
> +#define TRAP_HELPER     (-2)
> +
> +static int precheck_trap(DisasContext *ctx)
>  {
> -    /* Trap never */
> -    if (TO(ctx->opcode) == 0) {
> -        return true;
> +    int cond = TO2tcg[TO(ctx->opcode)];
> +
> +    /* Weird traps go to helper */
> +    if (cond < 0) {
> +        return TRAP_HELPER;
>      }
> -    /* Trap always */
> -    if (TO(ctx->opcode) == 31) {
> +    /* Unconditionals */
> +    if (cond == TCG_COND_ALWAYS) {
>          gen_exception_err(ctx, POWERPC_EXCP_PROGRAM, POWERPC_EXCP_TRAP);
> -        return true;
> +        return TRAP_UNCOND;
>      }
> -    return false;
> +    if (cond == TCG_COND_NEVER) {
> +        return TRAP_UNCOND;
> +    }
> +    /* Invert the condition as we branch over the exception when the
> +     * condition is *not* met
> +     */
> +    return tcg_invert_cond(cond);
> +}
> +
> +static void gen_trap(DisasContext *ctx)
> +{
> +    TCGv_i32 t0, t1;
> +
> +    t0 = tcg_const_i32(POWERPC_EXCP_PROGRAM);
> +    t1 = tcg_const_i32(POWERPC_EXCP_TRAP);
> +    gen_update_nip(ctx, ctx->nip - 4);
> +    gen_helper_raise_exception_err(cpu_env, t0, t1);

gen_exception_err, as with the unconditional trap?

>  static void gen_tw(DisasContext *ctx)
>  {
> -    TCGv_i32 t0;
> +    int cond = precheck_trap(ctx);
> +    TCGLabel *l1;
> +    TCGv t0;
> +    TCGv t1;
>
> -    if (check_unconditional_trap(ctx)) {
> +    if (cond == TRAP_UNCOND) {
> +        return;
> +    } else if (cond == TRAP_HELPER) {
> +        TCGv_i32 trapop = tcg_const_i32(TO(ctx->opcode));
> +        gen_helper_tw(cpu_env, cpu_gpr[rA(ctx->opcode)],
> +                      cpu_gpr[rB(ctx->opcode)], trapop);
> +        tcg_temp_free_i32(trapop);
>          return;
>      }
> -    t0 = tcg_const_i32(TO(ctx->opcode));
> -    gen_helper_tw(cpu_env, cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)],
> -                  t0);
> -    tcg_temp_free_i32(t0);
> +    l1 = gen_new_label();
> +    t0 = tcg_temp_new();
> +    t1 = tcg_temp_new();
> +    tcg_gen_ext32s_tl(t0, cpu_gpr[rA(ctx->opcode)]);
> +    tcg_gen_ext32s_tl(t1, cpu_gpr[rB(ctx->opcode)]);
> +    tcg_gen_brcond_tl(cond, t0, t1, l1);

It's just as easy, and perhaps better, to truncate to 32 bits instead of 
extending to TL bits.


r~
Benjamin Herrenschmidt Aug. 7, 2016, 12:47 a.m. UTC | #2
On Sat, 2016-08-06 at 14:33 +0530, Richard Henderson wrote:
> On 07/31/2016 10:43 AM, Benjamin Herrenschmidt wrote:

> > 

> > Translate most conditions to TCG conditions and avoid the helper

> > for most of the common cases.

> > 

> > > > Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>

> > ---

> >  target-ppc/translate.c | 168 ++++++++++++++++++++++++++++++++++++++-----------

> >  1 file changed, 132 insertions(+), 36 deletions(-)

> > 

> > diff --git a/target-ppc/translate.c b/target-ppc/translate.c

> > index 47eb9ed..561976f 100644

> > --- a/target-ppc/translate.c

> > +++ b/target-ppc/translate.c

> > @@ -3639,82 +3639,178 @@ static void gen_sc(DisasContext *ctx)

> > 

> >  /***                                Trap                                   ***/

> > 

> > -/* Check for unconditional traps (always or never) */

> > -static bool check_unconditional_trap(DisasContext *ctx)

> > +static int TO2tcg[32] = {

> 

> Should be const.


Indeed.

> > 

> > +        -1,             /* 0x0f > or = or u> or u< */

> > +        -1,             /* 0x13 < or u< or u> -> weird */

> > +        -1,             /* 0x19 < or > or u> -> weird */

> > +        -1,             /* 0x1a < or > or u< -> weird */

> > +        -1,             /* 0x1b < or > or u> or u< -> weird */

> 

> Not that it matters much, but when you have both < and >, or both u< and u>, 

> you can collapse to NE, no matter what else is there.


Not necessarily, if = is there too it beomes always ;-) But as
a rule I bail to the helper for anything that mixes signed and
non-signed, in part bcs I was thinking of tracing them in case
anybody uses such oddball constructs. I expect them to never
happen unless somebody has a bug.

> > 

> > +#define TRAP_UNCOND     (-1)

> > +#define TRAP_HELPER     (-2)

> > +

> > +static int precheck_trap(DisasContext *ctx)

> >  {

> > -    /* Trap never */

> > -    if (TO(ctx->opcode) == 0) {

> > -        return true;

> > +    int cond = TO2tcg[TO(ctx->opcode)];

> > +

> > +    /* Weird traps go to helper */

> > +    if (cond < 0) {

> > +        return TRAP_HELPER;

> >      }

> > -    /* Trap always */

> > -    if (TO(ctx->opcode) == 31) {

> > +    /* Unconditionals */

> > +    if (cond == TCG_COND_ALWAYS) {

> >          gen_exception_err(ctx, POWERPC_EXCP_PROGRAM, POWERPC_EXCP_TRAP);

> > -        return true;

> > +        return TRAP_UNCOND;

> >      }

> > -    return false;

> > +    if (cond == TCG_COND_NEVER) {

> > +        return TRAP_UNCOND;

> > +    }

> > +    /* Invert the condition as we branch over the exception when the

> > +     * condition is *not* met

> > +     */

> > +    return tcg_invert_cond(cond);

> > +}

> > +

> > +static void gen_trap(DisasContext *ctx)

> > +{

> > +    TCGv_i32 t0, t1;

> > +

> > +    t0 = tcg_const_i32(POWERPC_EXCP_PROGRAM);

> > +    t1 = tcg_const_i32(POWERPC_EXCP_TRAP);

> > +    gen_update_nip(ctx, ctx->nip - 4);

> > +    gen_helper_raise_exception_err(cpu_env, t0, t1);

> 

> gen_exception_err, as with the unconditional trap?


No, we call it from gen_tw etc... but we may also branch
around that call, so we should not exit the TB with an
exception in the context.

> > 

> >  static void gen_tw(DisasContext *ctx)

> >  {

> > -    TCGv_i32 t0;

> > +    int cond = precheck_trap(ctx);

> > +    TCGLabel *l1;

> > +    TCGv t0;

> > +    TCGv t1;

> > 

> > -    if (check_unconditional_trap(ctx)) {

> > +    if (cond == TRAP_UNCOND) {

> > +        return;

> > +    } else if (cond == TRAP_HELPER) {

> > +        TCGv_i32 trapop = tcg_const_i32(TO(ctx->opcode));

> > +        gen_helper_tw(cpu_env, cpu_gpr[rA(ctx->opcode)],

> > +                      cpu_gpr[rB(ctx->opcode)], trapop);

> > +        tcg_temp_free_i32(trapop);

> >          return;

> >      }

> > -    t0 = tcg_const_i32(TO(ctx->opcode));

> > -    gen_helper_tw(cpu_env, cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)],

> > -                  t0);

> > -    tcg_temp_free_i32(t0);

> > +    l1 = gen_new_label();

> > +    t0 = tcg_temp_new();

> > +    t1 = tcg_temp_new();

> > +    tcg_gen_ext32s_tl(t0, cpu_gpr[rA(ctx->opcode)]);

> > +    tcg_gen_ext32s_tl(t1, cpu_gpr[rB(ctx->opcode)]);

> > +    tcg_gen_brcond_tl(cond, t0, t1, l1);

> 

> It's just as easy, and perhaps better, to truncate to 32 bits instead of 

> extending to TL bits.


Ok, I'll have a look. I'm not actually *that* familiar with the various
ops in the IR, but I shall figure it out :-)

Cheers,
Ben.

> 

> r~
David Gibson Aug. 9, 2016, 2:07 a.m. UTC | #3
On Sun, Jul 31, 2016 at 03:13:13PM +1000, Benjamin Herrenschmidt wrote:
> Translate most conditions to TCG conditions and avoid the helper
> for most of the common cases.
> 
> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> ---
>  target-ppc/translate.c | 168 ++++++++++++++++++++++++++++++++++++++-----------
>  1 file changed, 132 insertions(+), 36 deletions(-)

I've merged 1-4 of this series into ppc-for-2.8.  I'm not really clear
whether a change is still needed on patch 5, so please resend either
way.

> 
> diff --git a/target-ppc/translate.c b/target-ppc/translate.c
> index 47eb9ed..561976f 100644
> --- a/target-ppc/translate.c
> +++ b/target-ppc/translate.c
> @@ -3639,82 +3639,178 @@ static void gen_sc(DisasContext *ctx)
>  
>  /***                                Trap                                   ***/
>  
> -/* Check for unconditional traps (always or never) */
> -static bool check_unconditional_trap(DisasContext *ctx)
> +static int TO2tcg[32] = {
> +        TCG_COND_NEVER, /* no condition */
> +        TCG_COND_GTU,   /* 0x01 u> */
> +        TCG_COND_LTU,   /* 0x02 u< */
> +        TCG_COND_NE,    /* 0x03 u< or u> -> NE */
> +        TCG_COND_EQ,    /* 0x04 = */
> +        TCG_COND_GEU,   /* 0x05 u> or = */
> +        TCG_COND_LEU,   /* 0x06 u< or = */
> +        TCG_COND_ALWAYS,/* 0x07 u< or u> or = -> ALWAYS */
> +        TCG_COND_GT,    /* 0x08 > */
> +        -1,             /* 0x09 > or u> -> weird */
> +        -1,             /* 0x0a > or u< -> weird */
> +        -1,             /* 0x0b > or u< or u> -> weird */
> +        TCG_COND_GE,    /* 0x0c > or = */
> +        -1,             /* 0x0d > or = or u> */
> +        -1,             /* 0x0e > or = or u< */
> +        -1,             /* 0x0f > or = or u> or u< */
> +        TCG_COND_LT,    /* 0x10 < */
> +        -1,             /* 0x11 < or u> -> weird */
> +        -1,             /* 0x12 < or u< -> weird */
> +        -1,             /* 0x13 < or u< or u> -> weird */
> +        TCG_COND_LE,    /* 0x14 < or = */
> +        -1,             /* 0x15 < or = or u> -> weird */
> +        -1,             /* 0x16 < or = or u< -> weird */
> +        TCG_COND_ALWAYS,/* 0x17 < or = or u< or u> -> ALWAYS */
> +        TCG_COND_NE,    /* 0x18 < or > -> NE */
> +        -1,             /* 0x19 < or > or u> -> weird */
> +        -1,             /* 0x1a < or > or u< -> weird */
> +        -1,             /* 0x1b < or > or u> or u< -> weird */
> +        TCG_COND_ALWAYS,/* 0x1c < or > or = -> ALWAYS */
> +        TCG_COND_ALWAYS,/* 0x1d < or > or = or u> -> ALWAYS */
> +        TCG_COND_ALWAYS,/* 0x1e < or > or = or u< -> ALWAYS */
> +        TCG_COND_ALWAYS,/* 0x1f < or > or = or u< -> ALWAYS */
> +};
> +
> +#define TRAP_UNCOND     (-1)
> +#define TRAP_HELPER     (-2)
> +
> +static int precheck_trap(DisasContext *ctx)
>  {
> -    /* Trap never */
> -    if (TO(ctx->opcode) == 0) {
> -        return true;
> +    int cond = TO2tcg[TO(ctx->opcode)];
> +
> +    /* Weird traps go to helper */
> +    if (cond < 0) {
> +        return TRAP_HELPER;
>      }
> -    /* Trap always */
> -    if (TO(ctx->opcode) == 31) {
> +    /* Unconditionals */
> +    if (cond == TCG_COND_ALWAYS) {
>          gen_exception_err(ctx, POWERPC_EXCP_PROGRAM, POWERPC_EXCP_TRAP);
> -        return true;
> +        return TRAP_UNCOND;
>      }
> -    return false;
> +    if (cond == TCG_COND_NEVER) {
> +        return TRAP_UNCOND;
> +    }
> +    /* Invert the condition as we branch over the exception when the
> +     * condition is *not* met
> +     */
> +    return tcg_invert_cond(cond);
> +}
> +
> +static void gen_trap(DisasContext *ctx)
> +{
> +    TCGv_i32 t0, t1;
> +
> +    t0 = tcg_const_i32(POWERPC_EXCP_PROGRAM);
> +    t1 = tcg_const_i32(POWERPC_EXCP_TRAP);
> +    gen_update_nip(ctx, ctx->nip - 4);
> +    gen_helper_raise_exception_err(cpu_env, t0, t1);
> +    tcg_temp_free_i32(t0);
> +    tcg_temp_free_i32(t1);
>  }
>  
>  /* tw */
>  static void gen_tw(DisasContext *ctx)
>  {
> -    TCGv_i32 t0;
> +    int cond = precheck_trap(ctx);
> +    TCGLabel *l1;
> +    TCGv t0;
> +    TCGv t1;
>  
> -    if (check_unconditional_trap(ctx)) {
> +    if (cond == TRAP_UNCOND) {
> +        return;
> +    } else if (cond == TRAP_HELPER) {
> +        TCGv_i32 trapop = tcg_const_i32(TO(ctx->opcode));
> +        gen_helper_tw(cpu_env, cpu_gpr[rA(ctx->opcode)],
> +                      cpu_gpr[rB(ctx->opcode)], trapop);
> +        tcg_temp_free_i32(trapop);
>          return;
>      }
> -    t0 = tcg_const_i32(TO(ctx->opcode));
> -    gen_helper_tw(cpu_env, cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)],
> -                  t0);
> -    tcg_temp_free_i32(t0);
> +    l1 = gen_new_label();
> +    t0 = tcg_temp_new();
> +    t1 = tcg_temp_new();
> +    tcg_gen_ext32s_tl(t0, cpu_gpr[rA(ctx->opcode)]);
> +    tcg_gen_ext32s_tl(t1, cpu_gpr[rB(ctx->opcode)]);
> +    tcg_gen_brcond_tl(cond, t0, t1, l1);
> +    gen_trap(ctx);
> +    gen_set_label(l1);
> +    tcg_temp_free(t0);
> +    tcg_temp_free(t1);
>  }
>  
>  /* twi */
>  static void gen_twi(DisasContext *ctx)
>  {
> +    int cond = precheck_trap(ctx);
> +    TCGLabel *l1;
>      TCGv t0;
> -    TCGv_i32 t1;
>  
> -    if (check_unconditional_trap(ctx)) {
> +    if (cond == TRAP_UNCOND) {
> +        return;
> +    } else if (cond == TRAP_HELPER) {
> +        TCGv_i32 trapop = tcg_const_i32(TO(ctx->opcode));
> +        t0 = tcg_const_tl(SIMM(ctx->opcode));
> +        gen_helper_tw(cpu_env, cpu_gpr[rA(ctx->opcode)], t0, trapop);
> +        tcg_temp_free_i32(trapop);
> +        tcg_temp_free(t0);
>          return;
>      }
> -    t0 = tcg_const_tl(SIMM(ctx->opcode));
> -    t1 = tcg_const_i32(TO(ctx->opcode));
> -    gen_helper_tw(cpu_env, cpu_gpr[rA(ctx->opcode)], t0, t1);
> +    l1 = gen_new_label();
> +    t0 = tcg_temp_new();
> +    tcg_gen_ext32s_tl(t0, cpu_gpr[rA(ctx->opcode)]);
> +    tcg_gen_brcondi_tl(cond, t0, SIMM(ctx->opcode), l1);
> +    gen_trap(ctx);
> +    gen_set_label(l1);
>      tcg_temp_free(t0);
> -    tcg_temp_free_i32(t1);
>  }
>  
>  #if defined(TARGET_PPC64)
>  /* td */
>  static void gen_td(DisasContext *ctx)
>  {
> -    TCGv_i32 t0;
> +    int cond = precheck_trap(ctx);
> +    TCGLabel *l1;
>  
> -    if (check_unconditional_trap(ctx)) {
> +    if (cond == TRAP_UNCOND) {
> +        return;
> +    } else if (cond == TRAP_HELPER) {
> +        TCGv_i32 trapop = tcg_const_i32(TO(ctx->opcode));
> +        gen_helper_td(cpu_env, cpu_gpr[rA(ctx->opcode)],
> +                      cpu_gpr[rB(ctx->opcode)], trapop);
> +        tcg_temp_free_i32(trapop);
>          return;
>      }
> -    t0 = tcg_const_i32(TO(ctx->opcode));
> -    gen_helper_td(cpu_env, cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)],
> -                  t0);
> -    tcg_temp_free_i32(t0);
> +    l1 = gen_new_label();
> +    tcg_gen_brcond_tl(cond, cpu_gpr[rA(ctx->opcode)],
> +                      cpu_gpr[rB(ctx->opcode)], l1);
> +    gen_trap(ctx);
> +    gen_set_label(l1);
>  }
>  
>  /* tdi */
>  static void gen_tdi(DisasContext *ctx)
>  {
> -    TCGv t0;
> -    TCGv_i32 t1;
> +    int cond = precheck_trap(ctx);
> +    TCGLabel *l1;
>  
> -    if (check_unconditional_trap(ctx)) {
> +    if (cond == TRAP_UNCOND) {
> +        return;
> +    } else if (cond == TRAP_HELPER) {
> +        TCGv_i32 trapop = tcg_const_i32(TO(ctx->opcode));
> +        TCGv t0 = tcg_const_tl(SIMM(ctx->opcode));
> +        gen_helper_td(cpu_env, cpu_gpr[rA(ctx->opcode)], t0, trapop);
> +        tcg_temp_free_i32(trapop);
> +        tcg_temp_free(t0);
>          return;
>      }
> -    t0 = tcg_const_tl(SIMM(ctx->opcode));
> -    t1 = tcg_const_i32(TO(ctx->opcode));
> -    gen_helper_td(cpu_env, cpu_gpr[rA(ctx->opcode)], t0, t1);
> -    tcg_temp_free(t0);
> -    tcg_temp_free_i32(t1);
> +    l1 = gen_new_label();
> +    tcg_gen_brcondi_tl(cond, cpu_gpr[rA(ctx->opcode)], SIMM(ctx->opcode), l1);
> +    gen_trap(ctx);
> +    gen_set_label(l1);
>  }
> -#endif
> +#endif /* defined(TARGET_PPC64) */
>  
>  /***                          Processor control                            ***/
>
Benjamin Herrenschmidt Aug. 9, 2016, 4:03 a.m. UTC | #4
On Tue, 2016-08-09 at 12:07 +1000, David Gibson wrote:
> On Sun, Jul 31, 2016 at 03:13:13PM +1000, Benjamin Herrenschmidt
> wrote:
> > 
> > Translate most conditions to TCG conditions and avoid the helper
> > for most of the common cases.
> > 
> > Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> > ---
> >  target-ppc/translate.c | 168
> > ++++++++++++++++++++++++++++++++++++++-----------
> >  1 file changed, 132 insertions(+), 36 deletions(-)
> 
> I've merged 1-4 of this series into ppc-for-2.8.  I'm not really
> clear
> whether a change is still needed on patch 5, so please resend either
> way.

What you merged works fine. Richard suggestions are refinements
I can do separately and apply on top.

Cheers,
Ben.
diff mbox

Patch

diff --git a/target-ppc/translate.c b/target-ppc/translate.c
index 47eb9ed..561976f 100644
--- a/target-ppc/translate.c
+++ b/target-ppc/translate.c
@@ -3639,82 +3639,178 @@  static void gen_sc(DisasContext *ctx)
 
 /***                                Trap                                   ***/
 
-/* Check for unconditional traps (always or never) */
-static bool check_unconditional_trap(DisasContext *ctx)
+static int TO2tcg[32] = {
+        TCG_COND_NEVER, /* no condition */
+        TCG_COND_GTU,   /* 0x01 u> */
+        TCG_COND_LTU,   /* 0x02 u< */
+        TCG_COND_NE,    /* 0x03 u< or u> -> NE */
+        TCG_COND_EQ,    /* 0x04 = */
+        TCG_COND_GEU,   /* 0x05 u> or = */
+        TCG_COND_LEU,   /* 0x06 u< or = */
+        TCG_COND_ALWAYS,/* 0x07 u< or u> or = -> ALWAYS */
+        TCG_COND_GT,    /* 0x08 > */
+        -1,             /* 0x09 > or u> -> weird */
+        -1,             /* 0x0a > or u< -> weird */
+        -1,             /* 0x0b > or u< or u> -> weird */
+        TCG_COND_GE,    /* 0x0c > or = */
+        -1,             /* 0x0d > or = or u> */
+        -1,             /* 0x0e > or = or u< */
+        -1,             /* 0x0f > or = or u> or u< */
+        TCG_COND_LT,    /* 0x10 < */
+        -1,             /* 0x11 < or u> -> weird */
+        -1,             /* 0x12 < or u< -> weird */
+        -1,             /* 0x13 < or u< or u> -> weird */
+        TCG_COND_LE,    /* 0x14 < or = */
+        -1,             /* 0x15 < or = or u> -> weird */
+        -1,             /* 0x16 < or = or u< -> weird */
+        TCG_COND_ALWAYS,/* 0x17 < or = or u< or u> -> ALWAYS */
+        TCG_COND_NE,    /* 0x18 < or > -> NE */
+        -1,             /* 0x19 < or > or u> -> weird */
+        -1,             /* 0x1a < or > or u< -> weird */
+        -1,             /* 0x1b < or > or u> or u< -> weird */
+        TCG_COND_ALWAYS,/* 0x1c < or > or = -> ALWAYS */
+        TCG_COND_ALWAYS,/* 0x1d < or > or = or u> -> ALWAYS */
+        TCG_COND_ALWAYS,/* 0x1e < or > or = or u< -> ALWAYS */
+        TCG_COND_ALWAYS,/* 0x1f < or > or = or u< -> ALWAYS */
+};
+
+#define TRAP_UNCOND     (-1)
+#define TRAP_HELPER     (-2)
+
+static int precheck_trap(DisasContext *ctx)
 {
-    /* Trap never */
-    if (TO(ctx->opcode) == 0) {
-        return true;
+    int cond = TO2tcg[TO(ctx->opcode)];
+
+    /* Weird traps go to helper */
+    if (cond < 0) {
+        return TRAP_HELPER;
     }
-    /* Trap always */
-    if (TO(ctx->opcode) == 31) {
+    /* Unconditionals */
+    if (cond == TCG_COND_ALWAYS) {
         gen_exception_err(ctx, POWERPC_EXCP_PROGRAM, POWERPC_EXCP_TRAP);
-        return true;
+        return TRAP_UNCOND;
     }
-    return false;
+    if (cond == TCG_COND_NEVER) {
+        return TRAP_UNCOND;
+    }
+    /* Invert the condition as we branch over the exception when the
+     * condition is *not* met
+     */
+    return tcg_invert_cond(cond);
+}
+
+static void gen_trap(DisasContext *ctx)
+{
+    TCGv_i32 t0, t1;
+
+    t0 = tcg_const_i32(POWERPC_EXCP_PROGRAM);
+    t1 = tcg_const_i32(POWERPC_EXCP_TRAP);
+    gen_update_nip(ctx, ctx->nip - 4);
+    gen_helper_raise_exception_err(cpu_env, t0, t1);
+    tcg_temp_free_i32(t0);
+    tcg_temp_free_i32(t1);
 }
 
 /* tw */
 static void gen_tw(DisasContext *ctx)
 {
-    TCGv_i32 t0;
+    int cond = precheck_trap(ctx);
+    TCGLabel *l1;
+    TCGv t0;
+    TCGv t1;
 
-    if (check_unconditional_trap(ctx)) {
+    if (cond == TRAP_UNCOND) {
+        return;
+    } else if (cond == TRAP_HELPER) {
+        TCGv_i32 trapop = tcg_const_i32(TO(ctx->opcode));
+        gen_helper_tw(cpu_env, cpu_gpr[rA(ctx->opcode)],
+                      cpu_gpr[rB(ctx->opcode)], trapop);
+        tcg_temp_free_i32(trapop);
         return;
     }
-    t0 = tcg_const_i32(TO(ctx->opcode));
-    gen_helper_tw(cpu_env, cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)],
-                  t0);
-    tcg_temp_free_i32(t0);
+    l1 = gen_new_label();
+    t0 = tcg_temp_new();
+    t1 = tcg_temp_new();
+    tcg_gen_ext32s_tl(t0, cpu_gpr[rA(ctx->opcode)]);
+    tcg_gen_ext32s_tl(t1, cpu_gpr[rB(ctx->opcode)]);
+    tcg_gen_brcond_tl(cond, t0, t1, l1);
+    gen_trap(ctx);
+    gen_set_label(l1);
+    tcg_temp_free(t0);
+    tcg_temp_free(t1);
 }
 
 /* twi */
 static void gen_twi(DisasContext *ctx)
 {
+    int cond = precheck_trap(ctx);
+    TCGLabel *l1;
     TCGv t0;
-    TCGv_i32 t1;
 
-    if (check_unconditional_trap(ctx)) {
+    if (cond == TRAP_UNCOND) {
+        return;
+    } else if (cond == TRAP_HELPER) {
+        TCGv_i32 trapop = tcg_const_i32(TO(ctx->opcode));
+        t0 = tcg_const_tl(SIMM(ctx->opcode));
+        gen_helper_tw(cpu_env, cpu_gpr[rA(ctx->opcode)], t0, trapop);
+        tcg_temp_free_i32(trapop);
+        tcg_temp_free(t0);
         return;
     }
-    t0 = tcg_const_tl(SIMM(ctx->opcode));
-    t1 = tcg_const_i32(TO(ctx->opcode));
-    gen_helper_tw(cpu_env, cpu_gpr[rA(ctx->opcode)], t0, t1);
+    l1 = gen_new_label();
+    t0 = tcg_temp_new();
+    tcg_gen_ext32s_tl(t0, cpu_gpr[rA(ctx->opcode)]);
+    tcg_gen_brcondi_tl(cond, t0, SIMM(ctx->opcode), l1);
+    gen_trap(ctx);
+    gen_set_label(l1);
     tcg_temp_free(t0);
-    tcg_temp_free_i32(t1);
 }
 
 #if defined(TARGET_PPC64)
 /* td */
 static void gen_td(DisasContext *ctx)
 {
-    TCGv_i32 t0;
+    int cond = precheck_trap(ctx);
+    TCGLabel *l1;
 
-    if (check_unconditional_trap(ctx)) {
+    if (cond == TRAP_UNCOND) {
+        return;
+    } else if (cond == TRAP_HELPER) {
+        TCGv_i32 trapop = tcg_const_i32(TO(ctx->opcode));
+        gen_helper_td(cpu_env, cpu_gpr[rA(ctx->opcode)],
+                      cpu_gpr[rB(ctx->opcode)], trapop);
+        tcg_temp_free_i32(trapop);
         return;
     }
-    t0 = tcg_const_i32(TO(ctx->opcode));
-    gen_helper_td(cpu_env, cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)],
-                  t0);
-    tcg_temp_free_i32(t0);
+    l1 = gen_new_label();
+    tcg_gen_brcond_tl(cond, cpu_gpr[rA(ctx->opcode)],
+                      cpu_gpr[rB(ctx->opcode)], l1);
+    gen_trap(ctx);
+    gen_set_label(l1);
 }
 
 /* tdi */
 static void gen_tdi(DisasContext *ctx)
 {
-    TCGv t0;
-    TCGv_i32 t1;
+    int cond = precheck_trap(ctx);
+    TCGLabel *l1;
 
-    if (check_unconditional_trap(ctx)) {
+    if (cond == TRAP_UNCOND) {
+        return;
+    } else if (cond == TRAP_HELPER) {
+        TCGv_i32 trapop = tcg_const_i32(TO(ctx->opcode));
+        TCGv t0 = tcg_const_tl(SIMM(ctx->opcode));
+        gen_helper_td(cpu_env, cpu_gpr[rA(ctx->opcode)], t0, trapop);
+        tcg_temp_free_i32(trapop);
+        tcg_temp_free(t0);
         return;
     }
-    t0 = tcg_const_tl(SIMM(ctx->opcode));
-    t1 = tcg_const_i32(TO(ctx->opcode));
-    gen_helper_td(cpu_env, cpu_gpr[rA(ctx->opcode)], t0, t1);
-    tcg_temp_free(t0);
-    tcg_temp_free_i32(t1);
+    l1 = gen_new_label();
+    tcg_gen_brcondi_tl(cond, cpu_gpr[rA(ctx->opcode)], SIMM(ctx->opcode), l1);
+    gen_trap(ctx);
+    gen_set_label(l1);
 }
-#endif
+#endif /* defined(TARGET_PPC64) */
 
 /***                          Processor control                            ***/