diff mbox series

[20/29,arm] Early expansion of uaddvdi4.

Message ID 20191018194900.34795-21-Richard.Earnshaw@arm.com
State New
Headers show
Series Rewrite DImode arithmetic support | expand

Commit Message

Richard Earnshaw (lists) Oct. 18, 2019, 7:48 p.m. UTC
This code borrows strongly on the uaddvti4 expansion for aarch64 since
the principles are similar.  Firstly, if the one of the low words of
the expansion is 0, we can simply copy the other low word to the
destination and use uaddvsi4 for the upper word.  If that doesn't work
we have to handle three possible cases for the upper work (the lower
word is simply an add-with-carry operation as for adddi3): zero in the
upper word, some other constant and a register (each has a different
canonicalization).  We use CC_ADCmode (a new CC mode variant) to
describe the cases as the introduction of the carry means we can
no-longer use the normal overflow trick of comparing the sum against
one of the operands.

	* config/arm/arm-modes.def (CC_ADC): New CC mode.
	* config/arm/arm.c (arm_select_cc_mode): Detect selection of
	CC_ADCmode.
	(maybe_get_arm_condition_code): Handle CC_ADCmode.
	* config/arm/arm.md (uaddvdi4): Early expansion of unsigned addition
	with overflow.
	(addsi3_cin_cout_reg, addsi3_cin_cout_imm, addsi3_cin_cout_0): New
	expand patterns.
	(addsi3_cin_cout_reg_insn, addsi3_cin_cout_0_insn): New insn patterns
	(addsi3_cin_cout_imm_insn): Likewise.
	(adddi3_compareC): Delete insn.
	* config/arm/predicates.md (arm_carry_operation): Handle CC_ADCmode.
---
 gcc/config/arm/arm-modes.def |   4 +
 gcc/config/arm/arm.c         |  16 ++++
 gcc/config/arm/arm.md        | 171 +++++++++++++++++++++++++++++++----
 gcc/config/arm/predicates.md |   2 +-
 4 files changed, 173 insertions(+), 20 deletions(-)
diff mbox series

Patch

diff --git a/gcc/config/arm/arm-modes.def b/gcc/config/arm/arm-modes.def
index f0eb8415b93..a6b520df32d 100644
--- a/gcc/config/arm/arm-modes.def
+++ b/gcc/config/arm/arm-modes.def
@@ -42,6 +42,9 @@  ADJUST_FLOAT_FORMAT (HF, ((arm_fp16_format == ARM_FP16_FORMAT_ALTERNATIVE)
    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).
+   CC_ADCmode is used when the carry is formed from the output of ADC for an
+     addtion.  In this case we cannot use the trick of comparing the sum
+     against one of the other operands.
    CCmode should be used otherwise.  */
 
 CC_MODE (CC_NOOV);
@@ -65,6 +68,7 @@  CC_MODE (CC_C);
 CC_MODE (CC_B);
 CC_MODE (CC_N);
 CC_MODE (CC_V);
+CC_MODE (CC_ADC);
 
 /* Vector modes.  */
 VECTOR_MODES (INT, 4);        /*            V4QI V2HI */
diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c
index 6da2a368d9f..eebbdc3d9c2 100644
--- a/gcc/config/arm/arm.c
+++ b/gcc/config/arm/arm.c
@@ -15387,6 +15387,14 @@  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
+      && GET_CODE (x) == PLUS
+      && GET_CODE (XEXP (x, 1)) == ZERO_EXTEND
+      && CONST_INT_P (y)
+      && UINTVAL (y) == 0x800000000
+      && (op == GEU || op == LTU))
+    return CC_ADCmode;
+
   if (GET_MODE (x) == DImode
       && (op == GE || op == LT)
       && GET_CODE (x) == SIGN_EXTEND
@@ -23952,6 +23960,14 @@  maybe_get_arm_condition_code (rtx comparison)
 	default: return ARM_NV;
 	}
 
+    case E_CC_ADCmode:
+      switch (comp_code)
+	{
+	case GEU: return ARM_CS;
+	case LTU: return ARM_CC;
+	default: return ARM_NV;
+	}
+
     case E_CCmode:
     case E_CC_RSBmode:
       switch (comp_code)
diff --git a/gcc/config/arm/arm.md b/gcc/config/arm/arm.md
index 4ea6f4b226c..9f0e43571fd 100644
--- a/gcc/config/arm/arm.md
+++ b/gcc/config/arm/arm.md
@@ -517,16 +517,165 @@  (define_expand "uaddvsi4"
 (define_expand "uaddvdi4"
   [(match_operand:DI 0 "s_register_operand")
    (match_operand:DI 1 "s_register_operand")
-   (match_operand:DI 2 "s_register_operand")
+   (match_operand:DI 2 "reg_or_int_operand")
    (match_operand 3 "")]
   "TARGET_32BIT"
 {
-  emit_insn (gen_adddi3_compareC (operands[0], operands[1], operands[2]));
-  arm_gen_unlikely_cbranch (LTU, CC_Cmode, operands[3]);
+  rtx lo_result, hi_result;
+  rtx lo_op1, hi_op1, lo_op2, hi_op2;
+  arm_decompose_di_binop (operands[1], operands[2], &lo_op1, &hi_op1,
+			  &lo_op2, &hi_op2);
+  lo_result = gen_lowpart (SImode, operands[0]);
+  hi_result = gen_highpart (SImode, operands[0]);
+
+  if (lo_op2 == const0_rtx)
+    {
+      emit_move_insn (lo_result, lo_op1);
+      if (!arm_add_operand (hi_op2, SImode))
+	hi_op2 = force_reg (SImode, hi_op2);
+
+      gen_uaddvsi4 (hi_result, hi_op1, hi_op2, operands[3]);
+    }
+  else
+    {
+      if (!arm_add_operand (lo_op2, SImode))
+	lo_op2 = force_reg (SImode, lo_op2);
+      if (!arm_not_operand (hi_op2, SImode))
+	hi_op2 = force_reg (SImode, hi_op2);
+
+      emit_insn (gen_addsi3_compare_op1 (lo_result, lo_op1, lo_op2));
+
+      if (hi_op2 == const0_rtx)
+        emit_insn (gen_addsi3_cin_cout_0 (hi_result, hi_op1));
+      else if (CONST_INT_P (hi_op2))
+        emit_insn (gen_addsi3_cin_cout_imm (hi_result, hi_op1, hi_op2));
+      else
+        emit_insn (gen_addsi3_cin_cout_reg (hi_result, hi_op1, hi_op2));
+
+      arm_gen_unlikely_cbranch (GEU, CC_ADCmode, operands[3]);
+    }
 
   DONE;
 })
 
+(define_expand "addsi3_cin_cout_reg"
+  [(parallel
+    [(set (match_dup 3)
+	  (compare:CC_ADC
+	   (plus:DI
+	    (plus:DI (match_dup 4)
+		     (zero_extend:DI (match_operand:SI 1 "s_register_operand")))
+	    (zero_extend:DI (match_operand:SI 2 "s_register_operand")))
+	   (const_int 4294967296)))
+     (set (match_operand:SI 0 "s_register_operand")
+	  (plus:SI (plus:SI (match_dup 5) (match_dup 1))
+		   (match_dup 2)))])]
+  "TARGET_32BIT"
+  {
+    operands[3] = gen_rtx_REG (CC_ADCmode, CC_REGNUM);
+    rtx ccin = gen_rtx_REG (CC_Cmode, CC_REGNUM);
+    operands[4] = gen_rtx_LTU (DImode, ccin, const0_rtx);
+    operands[5] = gen_rtx_LTU (SImode, ccin, const0_rtx);
+  }
+)
+
+(define_insn "*addsi3_cin_cout_reg_insn"
+  [(set (reg:CC_ADC CC_REGNUM)
+	(compare:CC_ADC
+	 (plus:DI
+	  (plus:DI
+	   (match_operand:DI 3 "arm_carry_operation" "")
+	   (zero_extend:DI (match_operand:SI 1 "s_register_operand" "%0,r")))
+	  (zero_extend:DI (match_operand:SI 2 "s_register_operand" "l,r")))
+	(const_int 4294967296)))
+   (set (match_operand:SI 0 "s_register_operand" "=l,r")
+	(plus:SI (plus:SI (match_operand:SI 4 "arm_carry_operation" "")
+			  (match_dup 1))
+		 (match_dup 2)))]
+  "TARGET_32BIT"
+  "@
+   adcs%?\\t%0, %0, %2
+   adcs%?\\t%0, %1, %2"
+  [(set_attr "type" "alus_sreg")
+   (set_attr "arch" "t2,*")
+   (set_attr "length" "2,4")]
+)
+
+(define_expand "addsi3_cin_cout_imm"
+  [(parallel
+    [(set (match_dup 3)
+	  (compare:CC_ADC
+	   (plus:DI
+	    (plus:DI (match_dup 4)
+		     (zero_extend:DI (match_operand:SI 1 "s_register_operand")))
+	    (match_dup 6))
+	   (const_int 4294967296)))
+     (set (match_operand:SI 0 "s_register_operand")
+	  (plus:SI (plus:SI (match_dup 5) (match_dup 1))
+		   (match_operand:SI 2 "arm_adcimm_operand")))])]
+  "TARGET_32BIT"
+  {
+    operands[3] = gen_rtx_REG (CC_ADCmode, CC_REGNUM);
+    rtx ccin = gen_rtx_REG (CC_Cmode, CC_REGNUM);
+    operands[4] = gen_rtx_LTU (DImode, ccin, const0_rtx);
+    operands[5] = gen_rtx_LTU (SImode, ccin, const0_rtx);
+    operands[6] = GEN_INT (UINTVAL (operands[2]) & 0xffffffff);
+  }
+)
+
+(define_insn "*addsi3_cin_cout_imm_insn"
+  [(set (reg:CC_ADC CC_REGNUM)
+	(compare:CC_ADC
+	 (plus:DI
+	  (plus:DI
+	   (match_operand:DI 3 "arm_carry_operation" "")
+	   (zero_extend:DI (match_operand:SI 1 "s_register_operand" "r,r")))
+	  (match_operand:DI 5 "const_int_operand" "n,n"))
+	(const_int 4294967296)))
+   (set (match_operand:SI 0 "s_register_operand" "=r,r")
+	(plus:SI (plus:SI (match_operand:SI 4 "arm_carry_operation" "")
+			  (match_dup 1))
+		 (match_operand:SI 2 "arm_adcimm_operand" "I,K")))]
+  "TARGET_32BIT
+   && (UINTVAL (operands[2]) & 0xffffffff) == UINTVAL (operands[5])"
+  "@
+   adcs%?\\t%0, %1, %2
+   sbcs%?\\t%0, %1, #%B2"
+  [(set_attr "type" "alus_imm")]
+)
+
+(define_expand "addsi3_cin_cout_0"
+  [(parallel
+    [(set (match_dup 2)
+	  (compare:CC_ADC
+	   (plus:DI (match_dup 3)
+		    (zero_extend:DI (match_operand:SI 1 "s_register_operand")))
+	   (const_int 4294967296)))
+     (set (match_operand:SI 0 "s_register_operand")
+	  (plus:SI (match_dup 4) (match_dup 1)))])]
+  "TARGET_32BIT"
+  {
+    operands[2] = gen_rtx_REG (CC_ADCmode, CC_REGNUM);
+    rtx ccin = gen_rtx_REG (CC_Cmode, CC_REGNUM);
+    operands[3] = gen_rtx_LTU (DImode, ccin, const0_rtx);
+    operands[4] = gen_rtx_LTU (SImode, ccin, const0_rtx);
+  }
+)
+
+(define_insn "*addsi3_cin_cout_0_insn"
+  [(set (reg:CC_ADC CC_REGNUM)
+	(compare:CC_ADC
+	 (plus:DI
+	  (match_operand:DI 2 "arm_carry_operation" "")
+	  (zero_extend:DI (match_operand:SI 1 "s_register_operand" "r")))
+	(const_int 4294967296)))
+   (set (match_operand:SI 0 "s_register_operand" "=r")
+	(plus:SI (match_operand:SI 3 "arm_carry_operation" "") (match_dup 1)))]
+  "TARGET_32BIT"
+  "adcs%?\\t%0, %1, #0"
+  [(set_attr "type" "alus_imm")]
+)
+
 (define_expand "addsi3"
   [(set (match_operand:SI          0 "s_register_operand")
 	(plus:SI (match_operand:SI 1 "s_register_operand")
@@ -636,22 +785,6 @@  (define_insn "addsi3_compareV"
    (set_attr "type" "alus_sreg")]
 )
 
-(define_insn "adddi3_compareC"
-  [(set (reg:CC_C CC_REGNUM)
-	(compare:CC_C
-	  (plus:DI
-	    (match_operand:DI 1 "register_operand" "r")
-	    (match_operand:DI 2 "register_operand" "r"))
-	  (match_dup 1)))
-   (set (match_operand:DI 0 "register_operand" "=&r")
-	(plus:DI (match_dup 1) (match_dup 2)))]
-  "TARGET_32BIT"
-  "adds\\t%Q0, %Q1, %Q2;adcs\\t%R0, %R1, %R2"
- [(set_attr "conds" "set")
-   (set_attr "length" "8")
-   (set_attr "type" "multiple")]
-)
-
 (define_insn "addsi3_compare0"
   [(set (reg:CC_NOOV CC_REGNUM)
 	(compare:CC_NOOV
diff --git a/gcc/config/arm/predicates.md b/gcc/config/arm/predicates.md
index d9470df8093..8a8f10ccb50 100644
--- a/gcc/config/arm/predicates.md
+++ b/gcc/config/arm/predicates.md
@@ -376,7 +376,7 @@  (define_special_predicate "arm_carry_operation"
     machine_mode ccmode = GET_MODE (op0);
     if (ccmode == CC_Cmode)
       return GET_CODE (op) == LTU;
-    else if (ccmode == CCmode || ccmode == CC_RSBmode)
+    else if (ccmode == CCmode || ccmode == CC_RSBmode || ccmode == CC_ADCmode)
       return GET_CODE (op) == GEU;
 
     return false;