[arm] early split most DImode comparison operations.
authorRichard Earnshaw <rearnsha@arm.com>
Fri, 18 Oct 2019 19:03:43 +0000 (19:03 +0000)
committerRichard Earnshaw <rearnsha@gcc.gnu.org>
Fri, 18 Oct 2019 19:03:43 +0000 (19:03 +0000)
This patch does most of the work for early splitting the DImode
comparisons.  We now handle EQ, NE, LT, GE, LTU and GEU during early
expansion, in addition to EQ and NE, for which the expansion has now
been reworked to use a standard conditional-compare pattern already in
the back-end.

To handle this we introduce two new condition flag modes that are used
when comparing the upper words of decomposed DImode values: one for
signed, and one for unsigned comparisons.  CC_Bmode (B for Borrow) is
essentially the inverse of CC_Cmode and is used when the carry flag is
set by a subtraction of unsigned values.

* config/arm/arm-modes.def (CC_NV, CC_B): New CC modes.
* config/arm/arm.c (arm_select_cc_mode): Recognize constructs that
need these modes.
(arm_gen_dicompare_reg): New code to early expand the sub-operations
of EQ, NE, LT, GE, LTU and GEU.
* config/arm/iterators.md (CC_EXTEND): New code attribute.
* config/arm/predicates.md (arm_adcimm_operand): New predicate..
* config/arm/arm.md (cmpsi3_carryin_<CC_EXTEND>out): New pattern.
(cmpsi3_imm_carryin_<CC_EXTEND>out): Likewise.
(cmpsi3_0_carryin_<CC_EXTEND>out): Likewise.

From-SVN: r277179

gcc/ChangeLog
gcc/config/arm/arm-modes.def
gcc/config/arm/arm.c
gcc/config/arm/arm.md
gcc/config/arm/iterators.md
gcc/config/arm/predicates.md

index 52d14e92e64557456ecfc40d4a7ec5df09162771..aa489bd9c985d92cc7b32711f1fc9710e8a7af98 100644 (file)
@@ -1,3 +1,16 @@
+2019-10-18  Richard Earnshaw  <rearnsha@arm.com>
+
+       * config/arm/arm-modes.def (CC_NV, CC_B): New CC modes.
+       * config/arm/arm.c (arm_select_cc_mode): Recognize constructs that
+       need these modes.
+       (arm_gen_dicompare_reg): New code to early expand the sub-operations
+       of EQ, NE, LT, GE, LTU and GEU.
+       * config/arm/iterators.md (CC_EXTEND): New code attribute.
+       * config/arm/predicates.md (arm_adcimm_operand): New predicate..
+       * config/arm/arm.md (cmpsi3_carryin_<CC_EXTEND>out): New pattern.
+       (cmpsi3_imm_carryin_<CC_EXTEND>out): Likewise.
+       (cmpsi3_0_carryin_<CC_EXTEND>out): Likewise.
+
 2019-10-18  Richard Earnshaw  <rearnsha@arm.com>
 
        * config/arm/arm.md (cbranchdi4): Accept reg_or_int_operand for
index 4fa7f1b43e50e22e0296b0fbcc727de81d28ee7a..65cddf68cdbedb4e513b97c08b5cfdd3c644a216 100644 (file)
@@ -34,12 +34,16 @@ ADJUST_FLOAT_FORMAT (HF, ((arm_fp16_format == ARM_FP16_FORMAT_ALTERNATIVE)
    CC_Cmode should be used if only the C flag is set correctly, after an
      addition.
    CC_Nmode should be used if only the N (sign) flag is set correctly
+   CC_NVmode should be used if only the N and V bits are set correctly,
+     (used for signed comparisons when the carry is propagated in).
    CC_CZmode should be used if only the C and Z flags are correct
    (used for DImode unsigned comparisons).
    CC_RSBmode should be used where the comparison is set by an RSB immediate,
      or NEG instruction.  The form of the comparison for (const - reg) will
      be (COMPARE (not (reg)) (~const)).
    CC_NCVmode should be used if only the N, C, and V flags are correct
+   CC_Bmode should be used if only the C flag is correct after a subtract
+     (eg after an unsigned borrow with carry-in propagation).
    (used for DImode signed comparisons).
    CCmode should be used otherwise.  */
 
@@ -47,6 +51,7 @@ CC_MODE (CC_NOOV);
 CC_MODE (CC_Z);
 CC_MODE (CC_CZ);
 CC_MODE (CC_NCV);
+CC_MODE (CC_NV);
 CC_MODE (CC_SWP);
 CC_MODE (CC_RSB);
 CC_MODE (CCFP);
@@ -62,6 +67,7 @@ CC_MODE (CC_DLTU);
 CC_MODE (CC_DGEU);
 CC_MODE (CC_DGTU);
 CC_MODE (CC_C);
+CC_MODE (CC_B);
 CC_MODE (CC_N);
 CC_MODE (CC_V);
 
index e7924df9b809d0f52e27159f8c64c69e17b852b5..74c8eca7a646d454abc1796a498b8c7144f1fab0 100644 (file)
@@ -15348,6 +15348,22 @@ arm_select_cc_mode (enum rtx_code op, rtx x, rtx y)
       && (rtx_equal_p (XEXP (x, 0), y) || rtx_equal_p (XEXP (x, 1), y)))
     return CC_Cmode;
 
+  if (GET_MODE (x) == DImode
+      && (op == GE || op == LT)
+      && GET_CODE (x) == SIGN_EXTEND
+      && ((GET_CODE (y) == PLUS
+          && arm_borrow_operation (XEXP (y, 0), DImode))
+         || arm_borrow_operation (y, DImode)))
+    return CC_NVmode;
+
+  if (GET_MODE (x) == DImode
+      && (op == GEU || op == LTU)
+      && GET_CODE (x) == ZERO_EXTEND
+      && ((GET_CODE (y) == PLUS
+          && arm_borrow_operation (XEXP (y, 0), DImode))
+         || arm_borrow_operation (y, DImode)))
+    return CC_Bmode;
+
   if (GET_MODE (x) == DImode || GET_MODE (y) == DImode)
     {
       switch (op)
@@ -15410,16 +15426,198 @@ arm_select_cc_mode (enum rtx_code op, rtx x, rtx y)
 static rtx
 arm_gen_dicompare_reg (rtx_code code, rtx x, rtx y, rtx scratch)
 {
-  /* We don't currently handle DImode in thumb1, but rely on libgcc.  */
+  machine_mode mode;
+  rtx cc_reg;
+
+    /* We don't currently handle DImode in thumb1, but rely on libgcc.  */
   gcc_assert (TARGET_32BIT);
 
+  rtx x_lo = simplify_gen_subreg (SImode, x, DImode,
+                                 subreg_lowpart_offset (SImode, DImode));
+  rtx x_hi = simplify_gen_subreg (SImode, x, DImode,
+                                 subreg_highpart_offset (SImode, DImode));
+  rtx y_lo = simplify_gen_subreg (SImode, y, DImode,
+                                 subreg_lowpart_offset (SImode, DImode));
+  rtx y_hi = simplify_gen_subreg (SImode, y, DImode,
+                                 subreg_highpart_offset (SImode, DImode));
+  switch (code)
+    {
+    case EQ:
+    case NE:
+      {
+       /* We should never have X as a const_int in this case.  */
+       gcc_assert (!CONST_INT_P (x));
+
+       if (y_lo == const0_rtx || y_hi == const0_rtx)
+         {
+           if (y_lo != const0_rtx)
+             {
+               rtx scratch2 = scratch ? scratch : gen_reg_rtx (SImode);
+
+               gcc_assert (y_hi == const0_rtx);
+               y_lo = gen_int_mode (-INTVAL (y_lo), SImode);
+               if (!arm_add_operand (y_lo, SImode))
+                 y_lo = force_reg (SImode, y_lo);
+               emit_insn (gen_addsi3 (scratch2, x_lo, y_lo));
+               x_lo = scratch2;
+             }
+           else if (y_hi != const0_rtx)
+             {
+               rtx scratch2 = scratch ? scratch : gen_reg_rtx (SImode);
+
+               y_hi = gen_int_mode (-INTVAL (y_hi), SImode);
+               if (!arm_add_operand (y_hi, SImode))
+                 y_hi = force_reg (SImode, y_hi);
+               emit_insn (gen_addsi3 (scratch2, x_hi, y_hi));
+               x_hi = scratch2;
+             }
+
+           if (!scratch)
+             {
+               gcc_assert (!reload_completed);
+               scratch = gen_rtx_SCRATCH (SImode);
+             }
+
+           rtx clobber = gen_rtx_CLOBBER (VOIDmode, scratch);
+           cc_reg = gen_rtx_REG (CC_NOOVmode, CC_REGNUM);
+
+           rtx set
+             = gen_rtx_SET (cc_reg,
+                            gen_rtx_COMPARE (CC_NOOVmode,
+                                             gen_rtx_IOR (SImode, x_lo, x_hi),
+                                             const0_rtx));
+           emit_insn (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, set,
+                                                             clobber)));
+           return cc_reg;
+         }
+
+       if (!arm_add_operand (y_lo, SImode))
+         y_lo = force_reg (SImode, y_lo);
+
+       if (!arm_add_operand (y_hi, SImode))
+         y_hi = force_reg (SImode, y_hi);
+
+       rtx cmp1 = gen_rtx_NE (SImode, x_lo, y_lo);
+       rtx cmp2 = gen_rtx_NE (SImode, x_hi, y_hi);
+       rtx conjunction = gen_rtx_IOR (SImode, cmp1, cmp2);
+       mode = SELECT_CC_MODE (code, conjunction, const0_rtx);
+       cc_reg = gen_rtx_REG (mode, CC_REGNUM);
+
+       emit_insn (gen_rtx_SET (cc_reg,
+                               gen_rtx_COMPARE (VOIDmode, conjunction,
+                                                const0_rtx)));
+       return cc_reg;
+      }
+
+    case LT:
+    case GE:
+      {
+       if (y_lo == const0_rtx)
+         {
+           /* If the low word of y is 0, then this is simply a normal
+              compare of the upper words.  */
+           if (!arm_add_operand (y_hi, SImode))
+             y_hi = force_reg (SImode, y_hi);
+
+           return arm_gen_compare_reg (code, x_hi, y_hi, NULL_RTX);
+         }
+
+       if (!arm_add_operand (y_lo, SImode))
+         y_lo = force_reg (SImode, y_lo);
+
+       /* Just for now.  */
+       if (!register_operand (x_lo, SImode))
+         x_lo = force_reg (SImode, x_lo);
+
+       rtx cmp1
+         = gen_rtx_LTU (DImode,
+                        arm_gen_compare_reg (LTU, x_lo, y_lo, NULL_RTX),
+                        const0_rtx);
+
+       if (!scratch)
+         scratch = gen_rtx_SCRATCH (SImode);
+       if (!arm_not_operand (y_hi, SImode))
+         y_hi = force_reg (SImode, y_hi);
+
+       /* Just for now.  */
+       if (!register_operand (x_hi, SImode))
+         x_hi = force_reg (SImode, x_hi);
+
+       rtx_insn *insn;
+       if (y_hi == const0_rtx)
+         insn = emit_insn (gen_cmpsi3_0_carryin_CC_NVout (scratch, x_hi,
+                                                          cmp1));
+       else if (CONST_INT_P (y_hi))
+         insn = emit_insn (gen_cmpsi3_imm_carryin_CC_NVout (scratch, x_hi,
+                                                            y_hi, cmp1));
+       else
+         insn = emit_insn (gen_cmpsi3_carryin_CC_NVout (scratch, x_hi, y_hi,
+                                                        cmp1));
+       return SET_DEST (single_set (insn));
+      }
+
+    case LTU:
+    case GEU:
+      {
+       if (y_lo == const0_rtx)
+         {
+           /* If the low word of y is 0, then this is simply a normal
+              compare of the upper words.  */
+           if (!arm_add_operand (y_hi, SImode))
+             y_hi = force_reg (SImode, y_hi);
+
+           return arm_gen_compare_reg (code, x_hi, y_hi, NULL_RTX);
+         }
+
+       if (!arm_add_operand (y_lo, SImode))
+         y_lo = force_reg (SImode, y_lo);
+
+       /* Just for now.  */
+       if (!register_operand (x_lo, SImode))
+         x_lo = force_reg (SImode, x_lo);
+
+       rtx cmp1
+         = gen_rtx_LTU (DImode,
+                        arm_gen_compare_reg (LTU, x_lo, y_lo, NULL_RTX),
+                        const0_rtx);
+
+       if (!scratch)
+         scratch = gen_rtx_SCRATCH (SImode);
+       if (!arm_not_operand (y_hi, SImode))
+         y_hi = force_reg (SImode, y_hi);
+
+       /* Just for now.  */
+       if (!register_operand (x_hi, SImode))
+         x_hi = force_reg (SImode, x_hi);
+
+       rtx_insn *insn;
+       if (y_hi == const0_rtx)
+         insn = emit_insn (gen_cmpsi3_0_carryin_CC_Bout (scratch, x_hi,
+                                                         cmp1));
+       else if (CONST_INT_P (y_hi))
+         {
+           /* Constant is viewed as unsigned when zero-extended.  */
+           y_hi = GEN_INT (UINTVAL (y_hi) & 0xffffffffULL);
+           insn = emit_insn (gen_cmpsi3_imm_carryin_CC_Bout (scratch, x_hi,
+                                                             y_hi, cmp1));
+         }
+       else
+         insn = emit_insn (gen_cmpsi3_carryin_CC_Bout (scratch, x_hi, y_hi,
+                                                       cmp1));
+       return SET_DEST (single_set (insn));
+      }
+
+    default:
+      break;
+    }
+
   /* We might have X as a constant, Y as a register because of the predicates
      used for cmpdi.  If so, force X to a register here.  */
   if (!REG_P (x))
     x = force_reg (DImode, x);
 
-  machine_mode mode = SELECT_CC_MODE (code, x, y);
-  rtx cc_reg = gen_rtx_REG (mode, CC_REGNUM);
+  mode = SELECT_CC_MODE (code, x, y);
+  cc_reg = gen_rtx_REG (mode, CC_REGNUM);
 
   if (mode != CC_CZmode)
     {
@@ -23803,6 +24001,22 @@ maybe_get_arm_condition_code (rtx comparison)
        default: return ARM_NV;
        }
 
+    case E_CC_NVmode:
+      switch (comp_code)
+       {
+       case GE: return ARM_GE;
+       case LT: return ARM_LT;
+       default: return ARM_NV;
+       }
+
+    case E_CC_Bmode:
+      switch (comp_code)
+       {
+       case GEU: return ARM_CS;
+       case LTU: return ARM_CC;
+       default: return ARM_NV;
+       }
+
     case E_CC_Vmode:
       switch (comp_code)
        {
index 9d8b137651f30957dd0b2dd656dd11b792157448..f0ff4dda39679751258df1b3ff0b3dba04ba458c 100644 (file)
    (set_attr "type" "adc_reg,adc_imm,alu_shift_imm")]
 )
 
+(define_insn "cmpsi3_carryin_<CC_EXTEND>out"
+  [(set (reg:<CC_EXTEND> CC_REGNUM)
+       (compare:<CC_EXTEND>
+        (SE:DI (match_operand:SI 1 "s_register_operand" "0,r"))
+        (plus:DI (match_operand:DI 3 "arm_borrow_operation" "")
+                 (SE:DI (match_operand:SI 2 "s_register_operand" "l,r")))))
+   (clobber (match_scratch:SI 0 "=l,r"))]
+  "TARGET_32BIT"
+  "sbcs\\t%0, %1, %2"
+  [(set_attr "conds" "set")
+   (set_attr "arch" "t2,*")
+   (set_attr "length" "2,4")
+   (set_attr "type" "adc_reg")]
+)
+
+;; Similar to the above, but handling a constant which has a different
+;; canonicalization.
+(define_insn "cmpsi3_imm_carryin_<CC_EXTEND>out"
+  [(set (reg:<CC_EXTEND> CC_REGNUM)
+       (compare:<CC_EXTEND>
+        (SE:DI (match_operand:SI 1 "s_register_operand" "r,r"))
+        (plus:DI (match_operand:DI 3 "arm_borrow_operation" "")
+                 (match_operand:DI 2 "arm_adcimm_operand" "I,K"))))
+   (clobber (match_scratch:SI 0 "=l,r"))]
+  "TARGET_32BIT"
+  "@
+   sbcs\\t%0, %1, %2
+   adcs\\t%0, %1, #%B2"
+  [(set_attr "conds" "set")
+   (set_attr "type" "adc_imm")]
+)
+
+;; Further canonicalization when the constant is zero.
+(define_insn "cmpsi3_0_carryin_<CC_EXTEND>out"
+  [(set (reg:<CC_EXTEND> CC_REGNUM)
+       (compare:<CC_EXTEND>
+        (SE:DI (match_operand:SI 1 "s_register_operand" "r,r"))
+        (match_operand:DI 2 "arm_borrow_operation" "")))
+   (clobber (match_scratch:SI 0 "=l,r"))]
+  "TARGET_32BIT"
+  "sbcs\\t%0, %1, #0"
+  [(set_attr "conds" "set")
+   (set_attr "type" "adc_imm")]
+)
+
 (define_insn "*subsi3_carryin_const"
   [(set (match_operand:SI 0 "s_register_operand" "=r")
        (minus:SI (plus:SI
index 77e1645083fa1605bf542f8ec507b519ef37d72b..5f1c833ad80ab6c85006338292f947dc141ed52f 100644 (file)
 ;; Code attributes
 ;;----------------------------------------------------------------------------
 
+;; Determine the mode of a 'wide compare', ie where the carry flag is
+;; propagated into the comparison.
+(define_code_attr CC_EXTEND [(sign_extend "CC_NV") (zero_extend "CC_B")])
+
 ;; Assembler mnemonics for vqh_ops and vqhs_ops iterators.
 (define_code_attr VQH_mnem [(plus "vadd") (smin "vmin") (smax "vmax")
                 (umin "vmin") (umax "vmax")])
index ed7495b69fc430d944ac0ced1c0341bb80e9707b..d9470df80933b50490c40d2beeddb67eefdae695 100644 (file)
   (ior (match_operand 0 "arm_rhs_operand")
        (match_operand 0 "arm_not_immediate_operand")))
 
+;; A constant that can be used with ADC(SBC) or SBC(ADC) when bit-wise
+;; inverted.  Similar to arm_not_operand, but excludes registers.
+(define_predicate "arm_adcimm_operand"
+  (ior (match_operand 0 "arm_immediate_operand")
+       (match_operand 0 "arm_not_immediate_operand")))
+
 (define_predicate "arm_di_operand"
   (ior (match_operand 0 "s_register_operand")
        (match_operand 0 "arm_immediate_di_operand")))