[arm] Improve handling of DImode comparisions against constants.
authorRichard Earnshaw <rearnsha@arm.com>
Fri, 18 Oct 2019 19:03:35 +0000 (19:03 +0000)
committerRichard Earnshaw <rearnsha@gcc.gnu.org>
Fri, 18 Oct 2019 19:03:35 +0000 (19:03 +0000)
In almost all cases it is better to handle inequality handling against constants
by transforming comparisons of the form (reg <GE/LT/GEU/LTU> const) into
(reg <GT/LE/GTU/LEU> (const+1)).  However, there are many cases that we could
handle but currently failed to do so because we forced the constant into a
register too early in the pattern expansion.  To permit this to be done we need
to defer forcing the constant into a register until after we've had the chance
to do the transform - in some cases that may even mean that we no-longer need
to force the constant into a register at all.  For example, on Arm, the case:

_Bool f8 (unsigned long long a) { return a > 0xffffffff; }

previously compiled to

        mov     r3, #0
        cmp     r1, r3
        mvn     r2, #0
        cmpeq   r0, r2
        movhi   r0, #1
        movls   r0, #0
        bx      lr

But now compiles to

        cmp     r1, #1
        cmpeq   r0, #0
        movcs   r0, #1
        movcc   r0, #0
        bx      lr

Which although not yet completely optimal, is certainly better than
previously.

* config/arm/arm.md (cbranchdi4): Accept reg_or_int_operand for
operand 2.
(cstoredi4): Similarly, but for operand 3.
* config/arm/arm.c (arm_canoncialize_comparison): Allow canonicalization
of unsigned compares with a constant on Arm.  Prefer using const+1 and
adjusting the comparison over swapping the operands whenever the
original constant was not valid.
(arm_gen_dicompare_reg): If Y is not a valid operand, force it to a
register here.
(arm_validize_comparison): Do not force invalid DImode operands to
registers here.

From-SVN: r277178

gcc/ChangeLog
gcc/config/arm/arm.c
gcc/config/arm/arm.md

index 51a2c3f2ed083141761785ca35ffc801202606b1..52d14e92e64557456ecfc40d4a7ec5df09162771 100644 (file)
@@ -1,3 +1,17 @@
+2019-10-18  Richard Earnshaw  <rearnsha@arm.com>
+
+       * config/arm/arm.md (cbranchdi4): Accept reg_or_int_operand for
+       operand 2.
+       (cstoredi4): Similarly, but for operand 3.
+       * config/arm/arm.c (arm_canoncialize_comparison): Allow
+       canonicalization of unsigned compares with a constant on Arm.
+       Prefer using const+1 and adjusting the comparison over swapping the
+       operands whenever the original constant was not valid.
+       (arm_gen_dicompare_reg): If Y is not a valid operand, force it to a
+       register here.
+       (arm_validize_comparison): Do not force invalid DImode operands to
+       registers here.
+
 2019-10-18  Richard Earnshaw  <rearnsha@arm.com>
 
        * config/arm/arm.c (arm_select_cc_mode): For DImode equality tests
index 97ebf6389889e8b6484ff66ab391024a869d89f8..e7924df9b809d0f52e27159f8c64c69e17b852b5 100644 (file)
@@ -5372,15 +5372,16 @@ arm_canonicalize_comparison (int *code, rtx *op0, rtx *op1,
 
   maxval = (HOST_WIDE_INT_1U << (GET_MODE_BITSIZE (mode) - 1)) - 1;
 
-  /* For DImode, we have GE/LT/GEU/LTU comparisons.  In ARM mode
-     we can also use cmp/cmpeq for GTU/LEU.  GT/LE must be either
-     reversed or (for constant OP1) adjusted to GE/LT.  Similarly
-     for GTU/LEU in Thumb mode.  */
+  /* For DImode, we have GE/LT/GEU/LTU comparisons (with cmp/sbc).  In
+     ARM mode we can also use cmp/cmpeq for GTU/LEU.  GT/LE must be
+     either reversed or (for constant OP1) adjusted to GE/LT.
+     Similarly for GTU/LEU in Thumb mode.  */
   if (mode == DImode)
     {
 
       if (*code == GT || *code == LE
-         || (!TARGET_ARM && (*code == GTU || *code == LEU)))
+         || ((!TARGET_ARM || CONST_INT_P (*op1))
+             && (*code == GTU || *code == LEU)))
        {
          /* Missing comparison.  First try to use an available
             comparison.  */
@@ -5392,23 +5393,27 @@ arm_canonicalize_comparison (int *code, rtx *op0, rtx *op1,
                case GT:
                case LE:
                  if (i != maxval
-                     && arm_const_double_by_immediates (GEN_INT (i + 1)))
+                     && (!arm_const_double_by_immediates (*op1)
+                         || arm_const_double_by_immediates (GEN_INT (i + 1))))
                    {
                      *op1 = GEN_INT (i + 1);
                      *code = *code == GT ? GE : LT;
                      return;
                    }
                  break;
+
                case GTU:
                case LEU:
                  if (i != ~((unsigned HOST_WIDE_INT) 0)
-                     && arm_const_double_by_immediates (GEN_INT (i + 1)))
+                     && (!arm_const_double_by_immediates (*op1)
+                         || arm_const_double_by_immediates (GEN_INT (i + 1))))
                    {
                      *op1 = GEN_INT (i + 1);
                      *code = *code == GTU ? GEU : LTU;
                      return;
                    }
                  break;
+
                default:
                  gcc_unreachable ();
                }
@@ -15436,7 +15441,7 @@ arm_gen_dicompare_reg (rtx_code code, rtx x, rtx y, rtx scratch)
                scratch = gen_reg_rtx (SImode);
              if (ylo == const0_rtx)
                {
-                 yhi = GEN_INT (-INTVAL(yhi));
+                 yhi = gen_int_mode (-INTVAL (yhi), SImode);
                  if (!arm_add_operand (yhi, SImode))
                    yhi = force_reg (SImode, yhi);
                  emit_insn (gen_addsi3 (scratch, xhi, yhi));
@@ -15445,7 +15450,7 @@ arm_gen_dicompare_reg (rtx_code code, rtx x, rtx y, rtx scratch)
              else
                {
                  gcc_assert (yhi == const0_rtx);
-                 ylo = GEN_INT (-INTVAL(ylo));
+                 ylo = gen_int_mode (-INTVAL (ylo), SImode);
                  if (!arm_add_operand (ylo, SImode))
                    ylo = force_reg (SImode, ylo);
                  emit_insn (gen_addsi3 (scratch, xlo, ylo));
@@ -15458,6 +15463,8 @@ arm_gen_dicompare_reg (rtx_code code, rtx x, rtx y, rtx scratch)
            x = gen_rtx_IOR (SImode, gen_lowpart (SImode, x),
                             gen_highpart (SImode, x));
        }
+      else if (!cmpdi_operand (y, mode))
+       y = force_reg (DImode, y);
 
       /* A scratch register is required.  */
       if (reload_completed)
@@ -15470,7 +15477,12 @@ arm_gen_dicompare_reg (rtx_code code, rtx x, rtx y, rtx scratch)
       emit_insn (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, set, clobber)));
     }
   else
-    emit_set_insn (cc_reg, gen_rtx_COMPARE (mode, x, y));
+    {
+      if (!cmpdi_operand (y, mode))
+       y = force_reg (DImode, y);
+
+      emit_set_insn (cc_reg, gen_rtx_COMPARE (mode, x, y));
+    }
 
   return cc_reg;
 }
@@ -30479,10 +30491,7 @@ arm_validize_comparison (rtx *comparison, rtx * op1, rtx * op2)
       return true;
 
     case E_DImode:
-      if (!cmpdi_operand (*op1, mode))
-       *op1 = force_reg (mode, *op1);
-      if (!cmpdi_operand (*op2, mode))
-       *op2 = force_reg (mode, *op2);
+      /* gen_compare_reg() will sort out any invalid operands.  */
       return true;
 
     case E_HFmode:
index a6b0c196c58ca145b256e20ac233bfa2857401e8..9d8b137651f30957dd0b2dd656dd11b792157448 100644 (file)
   [(set (pc) (if_then_else
              (match_operator 0 "expandable_comparison_operator"
               [(match_operand:DI 1 "s_register_operand")
-               (match_operand:DI 2 "cmpdi_operand")])
+               (match_operand:DI 2 "reg_or_int_operand")])
              (label_ref (match_operand 3 "" ""))
              (pc)))]
   "TARGET_32BIT"
   [(set (match_operand:SI 0 "s_register_operand")
        (match_operator:SI 1 "expandable_comparison_operator"
         [(match_operand:DI 2 "s_register_operand")
-         (match_operand:DI 3 "cmpdi_operand")]))]
+         (match_operand:DI 3 "reg_or_int_operand")]))]
   "TARGET_32BIT"
   "{
      if (!arm_validize_comparison (&operands[1],