diff mbox

tcg/arm: Fix double-word comparisons

Message ID 1515562748-32287-1-git-send-email-rth@twiddle.net (mailing list archive)
State New, archived
Headers show

Commit Message

Richard Henderson Jan. 10, 2018, 5:39 a.m. UTC
The code sequence we were generating was only good for unsigned
comparisons.  For signed comparisions, use the sequence from gcc.

Fixes booting of ppc64 firmware, with a patch changing the code
sequence for ppc comparisons.

Signed-off-by: Richard Henderson <rth@twiddle.net>
---
 tcg/arm/tcg-target.inc.c | 112 +++++++++++++++++++++++++++++++++--------------
 1 file changed, 80 insertions(+), 32 deletions(-)

Comments

Michael Roth Jan. 10, 2018, 7:26 p.m. UTC | #1
Quoting Richard Henderson (2018-01-09 23:39:08)
> The code sequence we were generating was only good for unsigned
> comparisons.  For signed comparisions, use the sequence from gcc.
> 
> Fixes booting of ppc64 firmware, with a patch changing the code
> sequence for ppc comparisons.
> 
> Signed-off-by: Richard Henderson <rth@twiddle.net>

Tested-by: Michael Roth <mdroth@linux.vnet.ibm.com>

> ---
>  tcg/arm/tcg-target.inc.c | 112 +++++++++++++++++++++++++++++++++--------------
>  1 file changed, 80 insertions(+), 32 deletions(-)
> 
> diff --git a/tcg/arm/tcg-target.inc.c b/tcg/arm/tcg-target.inc.c
> index 98a1253..b9890c8 100644
> --- a/tcg/arm/tcg-target.inc.c
> +++ b/tcg/arm/tcg-target.inc.c
> @@ -239,10 +239,11 @@ static void patch_reloc(tcg_insn_unit *code_ptr, int type,
>      }
>  }
> 
> -#define TCG_CT_CONST_ARM  0x100
> -#define TCG_CT_CONST_INV  0x200
> -#define TCG_CT_CONST_NEG  0x400
> -#define TCG_CT_CONST_ZERO 0x800
> +#define TCG_CT_CONST_ARM     0x0100
> +#define TCG_CT_CONST_INV     0x0200
> +#define TCG_CT_CONST_NEG     0x0400
> +#define TCG_CT_CONST_INVNEG  0x0800
> +#define TCG_CT_CONST_ZERO    0x1000
> 
>  /* parse target specific constraints */
>  static const char *target_parse_constraint(TCGArgConstraint *ct,
> @@ -258,6 +259,9 @@ static const char *target_parse_constraint(TCGArgConstraint *ct,
>      case 'N': /* The gcc constraint letter is L, already used here.  */
>          ct->ct |= TCG_CT_CONST_NEG;
>          break;
> +    case 'M':
> +        ct->ct |= TCG_CT_CONST_INVNEG;
> +        break;
>      case 'Z':
>          ct->ct |= TCG_CT_CONST_ZERO;
>          break;
> @@ -351,8 +355,7 @@ static inline int check_fit_imm(uint32_t imm)
>  static inline int tcg_target_const_match(tcg_target_long val, TCGType type,
>                                           const TCGArgConstraint *arg_ct)
>  {
> -    int ct;
> -    ct = arg_ct->ct;
> +    int ct = arg_ct->ct;
>      if (ct & TCG_CT_CONST) {
>          return 1;
>      } else if ((ct & TCG_CT_CONST_ARM) && check_fit_imm(val)) {
> @@ -361,6 +364,9 @@ static inline int tcg_target_const_match(tcg_target_long val, TCGType type,
>          return 1;
>      } else if ((ct & TCG_CT_CONST_NEG) && check_fit_imm(-val)) {
>          return 1;
> +    } else if ((ct & TCG_CT_CONST_INVNEG)
> +               && check_fit_imm(~val) && check_fit_imm(-val)) {
> +        return 1;
>      } else if ((ct & TCG_CT_CONST_ZERO) && val == 0) {
>          return 1;
>      } else {
> @@ -1103,6 +1109,64 @@ static inline void tcg_out_mb(TCGContext *s, TCGArg a0)
>      }
>  }
> 
> +static TCGCond tcg_out_cmp2(TCGContext *s, const TCGArg *args,
> +                            const int *const_args)
> +{
> +    TCGReg al = args[0];
> +    TCGReg ah = args[1];
> +    TCGArg bl = args[2];
> +    TCGArg bh = args[3];
> +    TCGCond cond = args[4];
> +    int const_bl = const_args[2];
> +    int const_bh = const_args[3];
> +
> +    switch (cond) {
> +    case TCG_COND_EQ:
> +    case TCG_COND_NE:
> +    case TCG_COND_LTU:
> +    case TCG_COND_LEU:
> +    case TCG_COND_GTU:
> +    case TCG_COND_GEU:
> +        /* We perform a conditional comparision.  If the high half is
> +           equal, then overwrite the flags with the comparison of the
> +           low half.  The resulting flags cover the whole.  */
> +        tcg_out_dat_rIN(s, COND_AL, ARITH_CMP, ARITH_CMN, 0, ah, bh, const_bh);
> +        tcg_out_dat_rIN(s, COND_EQ, ARITH_CMP, ARITH_CMN, 0, al, bl, const_bl);
> +        return cond;
> +
> +    case TCG_COND_LT:
> +    case TCG_COND_GE:
> +        /* We perform a double-word subtraction and examine the result.
> +           We do not actually need the result of the subtract, so the
> +           low part "subtract" is a compare.  For the high half we have
> +           no choice but to compute into a temporary.  */
> +        tcg_out_dat_rIN(s, COND_AL, ARITH_CMP, ARITH_CMN, 0, al, bl, const_bl);
> +        tcg_out_dat_rIK(s, COND_AL, ARITH_SBC | TO_CPSR, ARITH_ADC | TO_CPSR,
> +                        TCG_REG_TMP, ah, bh, const_bh);
> +        return cond;
> +
> +    case TCG_COND_LE:
> +    case TCG_COND_GT:
> +        /* Similar, but with swapped arguments.  And of course we must
> +           force the immediates into a register.  */
> +        if (const_bl) {
> +            tcg_out_movi(s, TCG_TYPE_REG, TCG_REG_TMP, bl);
> +            bl = TCG_REG_TMP;
> +        }
> +        tcg_out_dat_rIN(s, COND_AL, ARITH_CMP, ARITH_CMN, 0, bl, al, 0);
> +        if (const_bh) {
> +            tcg_out_movi(s, TCG_TYPE_REG, TCG_REG_TMP, bh);
> +            bh = TCG_REG_TMP;
> +        }
> +        tcg_out_dat_rIK(s, COND_AL, ARITH_SBC | TO_CPSR, ARITH_ADC | TO_CPSR,
> +                        TCG_REG_TMP, bh, ah, 0);
> +        return tcg_swap_cond(cond);
> +
> +    default:
> +        g_assert_not_reached();
> +    }
> +}
> +
>  #ifdef CONFIG_SOFTMMU
>  #include "tcg-ldst.inc.c"
> 
> @@ -1964,22 +2028,6 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc,
>          tcg_out_goto_label(s, tcg_cond_to_arm_cond[args[2]],
>                             arg_label(args[3]));
>          break;
> -    case INDEX_op_brcond2_i32:
> -        /* The resulting conditions are:
> -         * TCG_COND_EQ    -->  a0 == a2 && a1 == a3,
> -         * TCG_COND_NE    --> (a0 != a2 && a1 == a3) ||  a1 != a3,
> -         * TCG_COND_LT(U) --> (a0 <  a2 && a1 == a3) ||  a1 <  a3,
> -         * TCG_COND_GE(U) --> (a0 >= a2 && a1 == a3) || (a1 >= a3 && a1 != a3),
> -         * TCG_COND_LE(U) --> (a0 <= a2 && a1 == a3) || (a1 <= a3 && a1 != a3),
> -         * TCG_COND_GT(U) --> (a0 >  a2 && a1 == a3) ||  a1 >  a3,
> -         */
> -        tcg_out_dat_rIN(s, COND_AL, ARITH_CMP, ARITH_CMN, 0,
> -                        args[1], args[3], const_args[3]);
> -        tcg_out_dat_rIN(s, COND_EQ, ARITH_CMP, ARITH_CMN, 0,
> -                        args[0], args[2], const_args[2]);
> -        tcg_out_goto_label(s, tcg_cond_to_arm_cond[args[4]],
> -                           arg_label(args[5]));
> -        break;
>      case INDEX_op_setcond_i32:
>          tcg_out_dat_rIN(s, COND_AL, ARITH_CMP, ARITH_CMN, 0,
>                          args[1], args[2], const_args[2]);
> @@ -1988,15 +2036,15 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc,
>          tcg_out_dat_imm(s, tcg_cond_to_arm_cond[tcg_invert_cond(args[3])],
>                          ARITH_MOV, args[0], 0, 0);
>          break;
> +
> +    case INDEX_op_brcond2_i32:
> +        c = tcg_out_cmp2(s, args, const_args);
> +        tcg_out_goto_label(s, tcg_cond_to_arm_cond[c], arg_label(args[5]));
> +        break;
>      case INDEX_op_setcond2_i32:
> -        /* See brcond2_i32 comment */
> -        tcg_out_dat_rIN(s, COND_AL, ARITH_CMP, ARITH_CMN, 0,
> -                        args[2], args[4], const_args[4]);
> -        tcg_out_dat_rIN(s, COND_EQ, ARITH_CMP, ARITH_CMN, 0,
> -                        args[1], args[3], const_args[3]);
> -        tcg_out_dat_imm(s, tcg_cond_to_arm_cond[args[5]],
> -                        ARITH_MOV, args[0], 0, 1);
> -        tcg_out_dat_imm(s, tcg_cond_to_arm_cond[tcg_invert_cond(args[5])],
> +        c = tcg_out_cmp2(s, args + 1, const_args + 1);
> +        tcg_out_dat_imm(s, tcg_cond_to_arm_cond[c], ARITH_MOV, args[0], 0, 1);
> +        tcg_out_dat_imm(s, tcg_cond_to_arm_cond[tcg_invert_cond(c)],
>                          ARITH_MOV, args[0], 0, 0);
>          break;
> 
> @@ -2093,9 +2141,9 @@ static const TCGTargetOpDef *tcg_target_op_def(TCGOpcode op)
>      static const TCGTargetOpDef sub2
>          = { .args_ct_str = { "r", "r", "rI", "rI", "rIN", "rIK" } };
>      static const TCGTargetOpDef br2
> -        = { .args_ct_str = { "r", "r", "rIN", "rIN" } };
> +        = { .args_ct_str = { "r", "r", "rIM", "rIM" } };
>      static const TCGTargetOpDef setc2
> -        = { .args_ct_str = { "r", "r", "r", "rIN", "rIN" } };
> +        = { .args_ct_str = { "r", "r", "r", "rIM", "rIM" } };
> 
>      switch (op) {
>      case INDEX_op_goto_ptr:
> -- 
> 1.8.3.1
>
Peter Maydell Jan. 15, 2018, 2:27 p.m. UTC | #2
On 10 January 2018 at 05:39, Richard Henderson <rth@twiddle.net> wrote:
> The code sequence we were generating was only good for unsigned
> comparisons.  For signed comparisions, use the sequence from gcc.
>
> Fixes booting of ppc64 firmware, with a patch changing the code
> sequence for ppc comparisons.
>
> Signed-off-by: Richard Henderson <rth@twiddle.net>
> ---
>  tcg/arm/tcg-target.inc.c | 112 +++++++++++++++++++++++++++++++++--------------
>  1 file changed, 80 insertions(+), 32 deletions(-)
>
> diff --git a/tcg/arm/tcg-target.inc.c b/tcg/arm/tcg-target.inc.c
> index 98a1253..b9890c8 100644
> --- a/tcg/arm/tcg-target.inc.c
> +++ b/tcg/arm/tcg-target.inc.c
> @@ -239,10 +239,11 @@ static void patch_reloc(tcg_insn_unit *code_ptr, int type,
>      }
>  }
>
> -#define TCG_CT_CONST_ARM  0x100
> -#define TCG_CT_CONST_INV  0x200
> -#define TCG_CT_CONST_NEG  0x400
> -#define TCG_CT_CONST_ZERO 0x800
> +#define TCG_CT_CONST_ARM     0x0100
> +#define TCG_CT_CONST_INV     0x0200
> +#define TCG_CT_CONST_NEG     0x0400
> +#define TCG_CT_CONST_INVNEG  0x0800
> +#define TCG_CT_CONST_ZERO    0x1000
>
>  /* parse target specific constraints */
>  static const char *target_parse_constraint(TCGArgConstraint *ct,
> @@ -258,6 +259,9 @@ static const char *target_parse_constraint(TCGArgConstraint *ct,
>      case 'N': /* The gcc constraint letter is L, already used here.  */
>          ct->ct |= TCG_CT_CONST_NEG;
>          break;
> +    case 'M':
> +        ct->ct |= TCG_CT_CONST_INVNEG;
> +        break;

In GCC constraint "M" is "integer in the range 0 to 32", which this clearly
isn't. Can we have a comment saying what it is? (may be worth mentioning
in particular how "rIM" differs from "rINK" -- I think the answer is that
we want to say "valid-immediate OR (valid-if-inverted AND valid-if-negated)",
whereas 'rINK' is "valid-immediate OR valid-if-inverted OR valid-if-negated".)

The code looks OK to me. I'm guessing the sparc guest failure is
just because we now generate more code for some of the comparison
cases and that doesn't fit in the buffer...

We could avoid the annoying "load LE/GE immediates to tempreg"
extra code by having tcg_out_cmp2() return a flag to tell
the caller which way round to put the conditions for its two
conditional ARITH_MOV insns (for setcond2) or which condition
to use for the branch (brcond2), right?

thanks
-- PMM
Richard Henderson Jan. 15, 2018, 4:18 p.m. UTC | #3
On 01/15/2018 06:27 AM, Peter Maydell wrote:
> On 10 January 2018 at 05:39, Richard Henderson <rth@twiddle.net> wrote:
>> The code sequence we were generating was only good for unsigned
>> comparisons.  For signed comparisions, use the sequence from gcc.
>>
>> Fixes booting of ppc64 firmware, with a patch changing the code
>> sequence for ppc comparisons.
>>
>> Signed-off-by: Richard Henderson <rth@twiddle.net>
>> ---
>>  tcg/arm/tcg-target.inc.c | 112 +++++++++++++++++++++++++++++++++--------------
>>  1 file changed, 80 insertions(+), 32 deletions(-)
>>
>> diff --git a/tcg/arm/tcg-target.inc.c b/tcg/arm/tcg-target.inc.c
>> index 98a1253..b9890c8 100644
>> --- a/tcg/arm/tcg-target.inc.c
>> +++ b/tcg/arm/tcg-target.inc.c
>> @@ -239,10 +239,11 @@ static void patch_reloc(tcg_insn_unit *code_ptr, int type,
>>      }
>>  }
>>
>> -#define TCG_CT_CONST_ARM  0x100
>> -#define TCG_CT_CONST_INV  0x200
>> -#define TCG_CT_CONST_NEG  0x400
>> -#define TCG_CT_CONST_ZERO 0x800
>> +#define TCG_CT_CONST_ARM     0x0100
>> +#define TCG_CT_CONST_INV     0x0200
>> +#define TCG_CT_CONST_NEG     0x0400
>> +#define TCG_CT_CONST_INVNEG  0x0800
>> +#define TCG_CT_CONST_ZERO    0x1000
>>
>>  /* parse target specific constraints */
>>  static const char *target_parse_constraint(TCGArgConstraint *ct,
>> @@ -258,6 +259,9 @@ static const char *target_parse_constraint(TCGArgConstraint *ct,
>>      case 'N': /* The gcc constraint letter is L, already used here.  */
>>          ct->ct |= TCG_CT_CONST_NEG;
>>          break;
>> +    case 'M':
>> +        ct->ct |= TCG_CT_CONST_INVNEG;
>> +        break;
> 
> In GCC constraint "M" is "integer in the range 0 to 32", which this clearly
> isn't. Can we have a comment saying what it is? (may be worth mentioning
> in particular how "rIM" differs from "rINK" -- I think the answer is that
> we want to say "valid-immediate OR (valid-if-inverted AND valid-if-negated)",
> whereas 'rINK' is "valid-immediate OR valid-if-inverted OR valid-if-negated".)

Exactly.

> The code looks OK to me. I'm guessing the sparc guest failure is
> just because we now generate more code for some of the comparison
> cases and that doesn't fit in the buffer...

I don't recall having seen a sparc failure...

> We could avoid the annoying "load LE/GE immediates to tempreg"
> extra code by having tcg_out_cmp2() return a flag to tell
> the caller which way round to put the conditions for its two
> conditional ARITH_MOV insns (for setcond2) or which condition
> to use for the branch (brcond2), right?

Um... we do return a condition.  I must have missed a trick or made a mistake
somewhere.


r~
Peter Maydell Jan. 15, 2018, 4:27 p.m. UTC | #4
On 15 January 2018 at 16:18, Richard Henderson <rth@twiddle.net> wrote:
> On 01/15/2018 06:27 AM, Peter Maydell wrote:
>> The code looks OK to me. I'm guessing the sparc guest failure is
>> just because we now generate more code for some of the comparison
>> cases and that doesn't fit in the buffer...
>
> I don't recall having seen a sparc failure...

See the backtrace in my reply to your tcg pullrequest email :-)

>> We could avoid the annoying "load LE/GE immediates to tempreg"
>> extra code by having tcg_out_cmp2() return a flag to tell
>> the caller which way round to put the conditions for its two
>> conditional ARITH_MOV insns (for setcond2) or which condition
>> to use for the branch (brcond2), right?
>
> Um... we do return a condition.  I must have missed a trick or
> made a mistake somewhere.

Oh, yes, rather than returning "condition and flag to tell caller
to invert it" we could just flip which condition we return,
couldn't we.

thanks
-- PMM
Richard Henderson Jan. 15, 2018, 4:46 p.m. UTC | #5
On 01/15/2018 06:27 AM, Peter Maydell wrote:
> (may be worth mentioning
> in particular how "rIM" differs from "rINK" -- I think the answer is that
> we want to say "valid-immediate OR (valid-if-inverted AND valid-if-negated)",
> whereas 'rINK' is "valid-immediate OR valid-if-inverted OR valid-if-negated".)

I also just realized that N & K is only true for -256 thru -1.  Which is
certainly a simpler test to go with that verbiage.


r~
Richard Henderson Jan. 15, 2018, 5:31 p.m. UTC | #6
On 01/15/2018 06:27 AM, Peter Maydell wrote:
> We could avoid the annoying "load LE/GE immediates to tempreg"
> extra code by having tcg_out_cmp2() return a flag to tell
> the caller which way round to put the conditions for its two
> conditional ARITH_MOV insns (for setcond2) or which condition
> to use for the branch (brcond2), right?

No.

We do return a condition to use for the user.  When we do the LE/GT load
immediates to temp path, we do apply tcg_cond_swap(cond), which converts the
condition as for swapped arguments, i.e. to GE/LT.

But we can't *not* swap the arguments to the generated comparison.  Otherwise
we're computing the wrong thing.


r~
Richard Henderson Jan. 15, 2018, 5:39 p.m. UTC | #7
On 01/15/2018 09:31 AM, Richard Henderson wrote:
> But we can't *not* swap the arguments to the generated comparison.  Otherwise
> we're computing the wrong thing.

Bah.  I have of course forgotten about the RSC instruction, which takes an
immediate.  Will fix.


r~
diff mbox

Patch

diff --git a/tcg/arm/tcg-target.inc.c b/tcg/arm/tcg-target.inc.c
index 98a1253..b9890c8 100644
--- a/tcg/arm/tcg-target.inc.c
+++ b/tcg/arm/tcg-target.inc.c
@@ -239,10 +239,11 @@  static void patch_reloc(tcg_insn_unit *code_ptr, int type,
     }
 }
 
-#define TCG_CT_CONST_ARM  0x100
-#define TCG_CT_CONST_INV  0x200
-#define TCG_CT_CONST_NEG  0x400
-#define TCG_CT_CONST_ZERO 0x800
+#define TCG_CT_CONST_ARM     0x0100
+#define TCG_CT_CONST_INV     0x0200
+#define TCG_CT_CONST_NEG     0x0400
+#define TCG_CT_CONST_INVNEG  0x0800
+#define TCG_CT_CONST_ZERO    0x1000
 
 /* parse target specific constraints */
 static const char *target_parse_constraint(TCGArgConstraint *ct,
@@ -258,6 +259,9 @@  static const char *target_parse_constraint(TCGArgConstraint *ct,
     case 'N': /* The gcc constraint letter is L, already used here.  */
         ct->ct |= TCG_CT_CONST_NEG;
         break;
+    case 'M':
+        ct->ct |= TCG_CT_CONST_INVNEG;
+        break;
     case 'Z':
         ct->ct |= TCG_CT_CONST_ZERO;
         break;
@@ -351,8 +355,7 @@  static inline int check_fit_imm(uint32_t imm)
 static inline int tcg_target_const_match(tcg_target_long val, TCGType type,
                                          const TCGArgConstraint *arg_ct)
 {
-    int ct;
-    ct = arg_ct->ct;
+    int ct = arg_ct->ct;
     if (ct & TCG_CT_CONST) {
         return 1;
     } else if ((ct & TCG_CT_CONST_ARM) && check_fit_imm(val)) {
@@ -361,6 +364,9 @@  static inline int tcg_target_const_match(tcg_target_long val, TCGType type,
         return 1;
     } else if ((ct & TCG_CT_CONST_NEG) && check_fit_imm(-val)) {
         return 1;
+    } else if ((ct & TCG_CT_CONST_INVNEG)
+               && check_fit_imm(~val) && check_fit_imm(-val)) {
+        return 1;
     } else if ((ct & TCG_CT_CONST_ZERO) && val == 0) {
         return 1;
     } else {
@@ -1103,6 +1109,64 @@  static inline void tcg_out_mb(TCGContext *s, TCGArg a0)
     }
 }
 
+static TCGCond tcg_out_cmp2(TCGContext *s, const TCGArg *args,
+                            const int *const_args)
+{
+    TCGReg al = args[0];
+    TCGReg ah = args[1];
+    TCGArg bl = args[2];
+    TCGArg bh = args[3];
+    TCGCond cond = args[4];
+    int const_bl = const_args[2];
+    int const_bh = const_args[3];
+
+    switch (cond) {
+    case TCG_COND_EQ:
+    case TCG_COND_NE:
+    case TCG_COND_LTU:
+    case TCG_COND_LEU:
+    case TCG_COND_GTU:
+    case TCG_COND_GEU:
+        /* We perform a conditional comparision.  If the high half is
+           equal, then overwrite the flags with the comparison of the
+           low half.  The resulting flags cover the whole.  */
+        tcg_out_dat_rIN(s, COND_AL, ARITH_CMP, ARITH_CMN, 0, ah, bh, const_bh);
+        tcg_out_dat_rIN(s, COND_EQ, ARITH_CMP, ARITH_CMN, 0, al, bl, const_bl);
+        return cond;
+
+    case TCG_COND_LT:
+    case TCG_COND_GE:
+        /* We perform a double-word subtraction and examine the result.
+           We do not actually need the result of the subtract, so the
+           low part "subtract" is a compare.  For the high half we have
+           no choice but to compute into a temporary.  */
+        tcg_out_dat_rIN(s, COND_AL, ARITH_CMP, ARITH_CMN, 0, al, bl, const_bl);
+        tcg_out_dat_rIK(s, COND_AL, ARITH_SBC | TO_CPSR, ARITH_ADC | TO_CPSR,
+                        TCG_REG_TMP, ah, bh, const_bh);
+        return cond;
+
+    case TCG_COND_LE:
+    case TCG_COND_GT:
+        /* Similar, but with swapped arguments.  And of course we must
+           force the immediates into a register.  */
+        if (const_bl) {
+            tcg_out_movi(s, TCG_TYPE_REG, TCG_REG_TMP, bl);
+            bl = TCG_REG_TMP;
+        }
+        tcg_out_dat_rIN(s, COND_AL, ARITH_CMP, ARITH_CMN, 0, bl, al, 0);
+        if (const_bh) {
+            tcg_out_movi(s, TCG_TYPE_REG, TCG_REG_TMP, bh);
+            bh = TCG_REG_TMP;
+        }
+        tcg_out_dat_rIK(s, COND_AL, ARITH_SBC | TO_CPSR, ARITH_ADC | TO_CPSR,
+                        TCG_REG_TMP, bh, ah, 0);
+        return tcg_swap_cond(cond);
+
+    default:
+        g_assert_not_reached();
+    }
+}
+
 #ifdef CONFIG_SOFTMMU
 #include "tcg-ldst.inc.c"
 
@@ -1964,22 +2028,6 @@  static inline void tcg_out_op(TCGContext *s, TCGOpcode opc,
         tcg_out_goto_label(s, tcg_cond_to_arm_cond[args[2]],
                            arg_label(args[3]));
         break;
-    case INDEX_op_brcond2_i32:
-        /* The resulting conditions are:
-         * TCG_COND_EQ    -->  a0 == a2 && a1 == a3,
-         * TCG_COND_NE    --> (a0 != a2 && a1 == a3) ||  a1 != a3,
-         * TCG_COND_LT(U) --> (a0 <  a2 && a1 == a3) ||  a1 <  a3,
-         * TCG_COND_GE(U) --> (a0 >= a2 && a1 == a3) || (a1 >= a3 && a1 != a3),
-         * TCG_COND_LE(U) --> (a0 <= a2 && a1 == a3) || (a1 <= a3 && a1 != a3),
-         * TCG_COND_GT(U) --> (a0 >  a2 && a1 == a3) ||  a1 >  a3,
-         */
-        tcg_out_dat_rIN(s, COND_AL, ARITH_CMP, ARITH_CMN, 0,
-                        args[1], args[3], const_args[3]);
-        tcg_out_dat_rIN(s, COND_EQ, ARITH_CMP, ARITH_CMN, 0,
-                        args[0], args[2], const_args[2]);
-        tcg_out_goto_label(s, tcg_cond_to_arm_cond[args[4]],
-                           arg_label(args[5]));
-        break;
     case INDEX_op_setcond_i32:
         tcg_out_dat_rIN(s, COND_AL, ARITH_CMP, ARITH_CMN, 0,
                         args[1], args[2], const_args[2]);
@@ -1988,15 +2036,15 @@  static inline void tcg_out_op(TCGContext *s, TCGOpcode opc,
         tcg_out_dat_imm(s, tcg_cond_to_arm_cond[tcg_invert_cond(args[3])],
                         ARITH_MOV, args[0], 0, 0);
         break;
+
+    case INDEX_op_brcond2_i32:
+        c = tcg_out_cmp2(s, args, const_args);
+        tcg_out_goto_label(s, tcg_cond_to_arm_cond[c], arg_label(args[5]));
+        break;
     case INDEX_op_setcond2_i32:
-        /* See brcond2_i32 comment */
-        tcg_out_dat_rIN(s, COND_AL, ARITH_CMP, ARITH_CMN, 0,
-                        args[2], args[4], const_args[4]);
-        tcg_out_dat_rIN(s, COND_EQ, ARITH_CMP, ARITH_CMN, 0,
-                        args[1], args[3], const_args[3]);
-        tcg_out_dat_imm(s, tcg_cond_to_arm_cond[args[5]],
-                        ARITH_MOV, args[0], 0, 1);
-        tcg_out_dat_imm(s, tcg_cond_to_arm_cond[tcg_invert_cond(args[5])],
+        c = tcg_out_cmp2(s, args + 1, const_args + 1);
+        tcg_out_dat_imm(s, tcg_cond_to_arm_cond[c], ARITH_MOV, args[0], 0, 1);
+        tcg_out_dat_imm(s, tcg_cond_to_arm_cond[tcg_invert_cond(c)],
                         ARITH_MOV, args[0], 0, 0);
         break;
 
@@ -2093,9 +2141,9 @@  static const TCGTargetOpDef *tcg_target_op_def(TCGOpcode op)
     static const TCGTargetOpDef sub2
         = { .args_ct_str = { "r", "r", "rI", "rI", "rIN", "rIK" } };
     static const TCGTargetOpDef br2
-        = { .args_ct_str = { "r", "r", "rIN", "rIN" } };
+        = { .args_ct_str = { "r", "r", "rIM", "rIM" } };
     static const TCGTargetOpDef setc2
-        = { .args_ct_str = { "r", "r", "r", "rIN", "rIN" } };
+        = { .args_ct_str = { "r", "r", "r", "rIM", "rIM" } };
 
     switch (op) {
     case INDEX_op_goto_ptr: