]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
[arm] Early split subdi3
authorrearnsha <rearnsha@138bc75d-0d04-0410-961f-82ee72b054a4>
Fri, 18 Oct 2019 19:02:20 +0000 (19:02 +0000)
committerrearnsha <rearnsha@138bc75d-0d04-0410-961f-82ee72b054a4>
Fri, 18 Oct 2019 19:02:20 +0000 (19:02 +0000)
This patch adds early splitting of subdi3 so that the individual
operations can be seen by the optimizers, particuarly combine.  This
should allow us to do at least as good a job as previously, but with
far fewer patterns in the machine description.

This is just the initial patch to add the early splitting.  The
cleanups will follow later.

A special trick is used to handle the 'reverse subtract and compare'
where a register is subtracted from a constant.  The natural
comparison

    (COMPARE (const) (reg))

is not canonical in this case and combine will never correctly
generate it (trying to swap the order of the operands.  To handle this
we write the comparison as

    (COMPARE (NOT (reg)) (~const)),

which has the same result for EQ, NE, LTU, LEU, GTU and GEU, which are
all the cases we are really interested in here.

Finally, we delete the negdi2 pattern.  The generic expanders will use
our new subdi3 expander if this pattern is missing and that can handle
the negate case just fine.

* config/arm/arm-modes.def (CC_RSB): New CC mode.
* config/arm/predicates.md (arm_borrow_operation): Handle CC_RSBmode.
* config/arm/arm.c (arm_select_cc_mode): Detect when we should
return CC_RSBmode.
(maybe_get_arm_condition_code): Handle CC_RSBmode.
* config/arm/arm.md (subsi3_carryin): Make this pattern available to
expand.
(subdi3): Rewrite to early-expand the sub-operations.
(rsb_im_compare): New pattern.
(negdi2): Delete.
(negdi2_insn): Delete.
(arm_negsi2): Correct type attribute to alu_imm.
(negsi2_0compare): New insn pattern.
(negsi2_carryin): New insn pattern.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@277169 138bc75d-0d04-0410-961f-82ee72b054a4

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

index 1c96b17631f1324f3846e92980aa7f20f09f3dbc..0f389a1f3f62bb1a4210c15f7ce469eefe02fa36 100644 (file)
@@ -1,3 +1,20 @@
+2019-10-18  Richard Earnshaw  <rearnsha@arm.com>
+
+       * config/arm/arm-modes.def (CC_RSB): New CC mode.
+       * config/arm/predicates.md (arm_borrow_operation): Handle CC_RSBmode.
+       * config/arm/arm.c (arm_select_cc_mode): Detect when we should
+       return CC_RSBmode.
+       (maybe_get_arm_condition_code): Handle CC_RSBmode.
+       * config/arm/arm.md (subsi3_carryin): Make this pattern available to
+       expand.
+       (subdi3): Rewrite to early-expand the sub-operations.
+       (rsb_im_compare): New pattern.
+       (negdi2): Delete.
+       (negdi2_insn): Delete.
+       (arm_negsi2): Correct type attribute to alu_imm.
+       (negsi2_0compare): New insn pattern.
+       (negsi2_carryin): New insn pattern.
+
 2019-10-18  Richard Earnshaw  <rearnsha@arm.com>
 
        * config/arm/arm.md (addsi3_carryin_alt2): Use arm_not_operand for
index 8f131c369b594c84a9508424c8afd2177aa6e2ee..4fa7f1b43e50e22e0296b0fbcc727de81d28ee7a 100644 (file)
@@ -36,6 +36,9 @@ ADJUST_FLOAT_FORMAT (HF, ((arm_fp16_format == ARM_FP16_FORMAT_ALTERNATIVE)
    CC_Nmode should be used if only the N (sign) flag is set correctly
    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
    (used for DImode signed comparisons).
    CCmode should be used otherwise.  */
@@ -45,6 +48,7 @@ CC_MODE (CC_Z);
 CC_MODE (CC_CZ);
 CC_MODE (CC_NCV);
 CC_MODE (CC_SWP);
+CC_MODE (CC_RSB);
 CC_MODE (CCFP);
 CC_MODE (CCFPE);
 CC_MODE (CC_DNE);
index f26945dbcf00e5e82774e441cdc2bf34a8e3364c..41e4832f2c9a70802b80c17f60cece428ccce907 100644 (file)
@@ -15214,6 +15214,17 @@ arm_select_cc_mode (enum rtx_code op, rtx x, rtx y)
          || (TARGET_32BIT && GET_CODE (x) == ZERO_EXTRACT)))
     return CC_NOOVmode;
 
+  /* An unsigned comparison of ~reg with a const is really a special
+     canoncialization of compare (~const, reg), which is a reverse
+     subtract operation.  We may not get here if CONST is 0, but that
+     doesn't matter because ~0 isn't a valid immediate for RSB.  */
+  if (GET_MODE (x) == SImode
+      && GET_CODE (x) == NOT
+      && CONST_INT_P (y)
+      && (op == EQ || op == NE
+         || op == LTU || op == LEU || op == GEU || op == GTU))
+    return CC_RSBmode;
+
   if (GET_MODE (x) == QImode && (op == EQ || op == NE))
     return CC_Zmode;
 
@@ -23629,6 +23640,18 @@ maybe_get_arm_condition_code (rtx comparison)
        default: return ARM_NV;
        }
 
+    case E_CC_RSBmode:
+      switch (comp_code)
+       {
+       case NE: return ARM_NE;
+       case EQ: return ARM_EQ;
+       case GEU: return ARM_CS;
+       case GTU: return ARM_HI;
+       case LEU: return ARM_LS;
+       case LTU: return ARM_CC;
+       default: return ARM_NV;
+       }
+
     case E_CCmode:
       switch (comp_code)
        {
index fbe154a9873fb20cd60b04ffb71f861ea8e80736..99d931525f85c41c4b4aaff78ed6adb953268a6a 100644 (file)
    (set_attr "type" "alus_sreg")]
 )
 
-(define_insn "*subsi3_carryin"
+(define_insn "subsi3_carryin"
   [(set (match_operand:SI 0 "s_register_operand" "=r,r,r")
        (minus:SI (minus:SI (match_operand:SI 1 "reg_or_int_operand" "r,I,Pz")
                            (match_operand:SI 2 "s_register_operand" "r,r,r"))
 (define_expand "subdi3"
  [(parallel
    [(set (match_operand:DI            0 "s_register_operand")
-         (minus:DI (match_operand:DI 1 "s_register_operand")
+         (minus:DI (match_operand:DI 1 "reg_or_int_operand")
                    (match_operand:DI 2 "s_register_operand")))
     (clobber (reg:CC CC_REGNUM))])]
   "TARGET_EITHER"
   "
-")
+  if (TARGET_THUMB1)
+    {
+      if (!REG_P (operands[1]))
+       operands[1] = force_reg (DImode, operands[1]);
+    }
+  else
+    {
+      rtx lo_result, hi_result, lo_dest, hi_dest;
+      rtx lo_op1, hi_op1, lo_op2, hi_op2;
+      rtx condition;
+
+      /* Since operands[1] may be an integer, pass it second, so that
+        any necessary simplifications will be done on the decomposed
+        constant.  */
+      arm_decompose_di_binop (operands[2], operands[1], &lo_op2, &hi_op2,
+                             &lo_op1, &hi_op1);
+      lo_result = lo_dest = gen_lowpart (SImode, operands[0]);
+      hi_result = hi_dest = gen_highpart (SImode, operands[0]);
+
+      if (!arm_rhs_operand (lo_op1, SImode))
+       lo_op1 = force_reg (SImode, lo_op1);
+
+      if ((TARGET_THUMB2 && ! s_register_operand (hi_op1, SImode))
+         || !arm_rhs_operand (hi_op1, SImode))
+       hi_op1 = force_reg (SImode, hi_op1);
+
+      rtx cc_reg;
+      if (lo_op1 == const0_rtx)
+       {
+         cc_reg = gen_rtx_REG (CC_RSBmode, CC_REGNUM);
+         emit_insn (gen_negsi2_0compare (lo_dest, lo_op2));
+       }
+      else if (CONST_INT_P (lo_op1))
+       {
+         cc_reg = gen_rtx_REG (CC_RSBmode, CC_REGNUM);
+         emit_insn (gen_rsb_imm_compare (lo_dest, lo_op1, lo_op2, 
+                                         GEN_INT (~UINTVAL (lo_op1))));
+       }
+      else
+       {
+         cc_reg = gen_rtx_REG (CCmode, CC_REGNUM);
+         emit_insn (gen_subsi3_compare (lo_dest, lo_op1, lo_op2));
+       }
+
+      condition = gen_rtx_LTU (SImode, cc_reg, const0_rtx);
+
+      if (hi_op1 == const0_rtx)
+        emit_insn (gen_negsi2_carryin (hi_dest, hi_op2, condition));
+      else
+       emit_insn (gen_subsi3_carryin (hi_dest, hi_op1, hi_op2, condition));
+
+      if (lo_result != lo_dest)
+       emit_move_insn (lo_result, lo_dest);
+
+      if (hi_result != hi_dest)
+       emit_move_insn (hi_result, hi_dest);
+
+      DONE;
+    }
+  "
+)
 
 (define_insn "*arm_subdi3"
   [(set (match_operand:DI 0 "arm_general_register_operand" "=&r,&r,&r")
    subs%?\\t%0, %1, %2
    rsbs%?\\t%0, %2, %1"
   [(set_attr "conds" "set")
-   (set_attr "type" "alus_imm,alus_sreg,alus_sreg")]
+   (set_attr "type" "alus_imm,alus_sreg,alus_imm")]
+)
+
+;; To keep the comparison in canonical form we express it as (~reg cmp ~0)
+;; rather than (0 cmp reg).  This gives the same results for unsigned
+;; and equality compares which is what we mostly need here.
+(define_insn "rsb_imm_compare"
+  [(set (reg:CC_RSB CC_REGNUM)
+       (compare:CC_RSB (not:SI (match_operand:SI 2 "s_register_operand" "r"))
+                       (match_operand 3 "const_int_operand" "")))
+   (set (match_operand:SI 0 "s_register_operand" "=r")
+       (minus:SI (match_operand 1 "arm_immediate_operand" "I")
+                 (match_dup 2)))]
+  "TARGET_32BIT && ~UINTVAL (operands[1]) == UINTVAL (operands[3])"
+  "rsbs\\t%0, %2, %1"
+  [(set_attr "conds" "set")
+   (set_attr "type" "alus_imm")]
 )
 
 (define_expand "subsf3"
    (set_attr "type" "multiple")]
 )
 
-(define_expand "negdi2"
- [(parallel
-   [(set (match_operand:DI 0 "s_register_operand")
-        (neg:DI (match_operand:DI 1 "s_register_operand")))
-    (clobber (reg:CC CC_REGNUM))])]
-  "TARGET_EITHER"
-)
-
-;; The constraints here are to prevent a *partial* overlap (where %Q0 == %R1).
-(define_insn "*negdi2_insn"
-  [(set (match_operand:DI 0 "s_register_operand" "=&r,&r")
-       (neg:DI (match_operand:DI 1 "s_register_operand"  "r,r")))
-   (clobber (reg:CC CC_REGNUM))]
-  "TARGET_32BIT"
-  "@
-   rsbs\\t%Q0, %Q1, #0; rsc\\t%R0, %R1, #0
-   negs\\t%Q0, %Q1; sbc\\t%R0, %R1, %R1, lsl #1"
-  [(set_attr "conds" "clob")
-   (set_attr "arch" "a,t2")
-   (set_attr "length" "8")
-   (set_attr "type" "multiple")]
-)
-
 (define_expand "negsi2"
   [(set (match_operand:SI         0 "s_register_operand")
        (neg:SI (match_operand:SI 1 "s_register_operand")))]
    (set_attr "predicable_short_it" "yes,no")
    (set_attr "arch" "t2,*")
    (set_attr "length" "4")
-   (set_attr "type" "alu_sreg")]
+   (set_attr "type" "alu_imm")]
+)
+
+;; To keep the comparison in canonical form we express it as (~reg cmp ~0)
+;; rather than (0 cmp reg).  This gives the same results for unsigned
+;; and equality compares which is what we mostly need here.
+(define_insn "negsi2_0compare"
+  [(set (reg:CC_RSB CC_REGNUM)
+       (compare:CC_RSB (not:SI (match_operand:SI 1 "s_register_operand" "l,r"))
+                       (const_int -1)))
+   (set (match_operand:SI 0 "s_register_operand" "=l,r")
+       (neg:SI (match_dup 1)))]
+  "TARGET_32BIT"
+  "@
+   negs\\t%0, %1
+   rsbs\\t%0, %1, #0"
+  [(set_attr "conds" "set")
+   (set_attr "arch" "t2,*")
+   (set_attr "length" "2,*")
+   (set_attr "type" "alus_imm")]
+)
+
+(define_insn "negsi2_carryin"
+  [(set (match_operand:SI 0 "s_register_operand" "=r,r")
+       (minus:SI (neg:SI (match_operand:SI 1 "s_register_operand" "r,r"))
+                 (match_operand:SI 2 "arm_borrow_operation" "")))]
+  "TARGET_32BIT"
+  "@
+   rsc\\t%0, %1, #0
+   sbc\\t%0, %1, %1, lsl #1"
+  [(set_attr "conds" "use")
+   (set_attr "arch" "a,t2")
+   (set_attr "type" "adc_imm,adc_reg")]
 )
 
 (define_expand "negsf2"
index 8b36e7ee462235ad26e132f1ccf98d28c2487d67..e6766a97fc41356fd5db556f2209cbac6bd98710 100644 (file)
     machine_mode ccmode = GET_MODE (op0);
     if (ccmode == CC_Cmode)
       return GET_CODE (op) == GEU;
-    else if (ccmode == CCmode)
+    else if (ccmode == CCmode || ccmode == CC_RSBmode)
       return GET_CODE (op) == LTU;
     return false;
   }