[arm] Early split addvdi4
authorRichard Earnshaw <rearnsha@arm.com>
Fri, 18 Oct 2019 19:04:38 +0000 (19:04 +0000)
committerRichard Earnshaw <rearnsha@gcc.gnu.org>
Fri, 18 Oct 2019 19:04:38 +0000 (19:04 +0000)
This patch adds early splitting for addvdi4; it's very similar to the
uaddvdi4 splitter, but the details are just different enough in
places, especially for the patterns that match the splitting, where we
have to compare against the non-widened version to detect if overflow
occurred.

I've also added a testcase to the testsuite for a couple of constants
that caught me out during the development of this patch.  They're
probably arm-specific values, but the test is generic enough that I've
included it for all targets.

[gcc]
* config/arm/arm.c (arm_select_cc_mode): Allow either the first
or second operand of the PLUS inside a DImode equality test to be
sign-extend when selecting CC_Vmode.
* config/arm/arm.md (addvdi4): Early-split the operation into SImode
instructions.
(addsi3_cin_vout_reg, addsi3_cin_vout_imm, addsi3_cin_vout_0): New
expand patterns.
(addsi3_cin_vout_reg_insn, addsi3_cin_vout_imm_insn): New patterns.
(addsi3_cin_vout_0): Likewise.
(adddi3_compareV): Delete.

[gcc/testsuite]
* gcc.dg/builtin-arith-overflow-3.c: New test.

From-SVN: r277186

gcc/ChangeLog
gcc/config/arm/arm.c
gcc/config/arm/arm.md
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/builtin-arith-overflow-3.c [new file with mode: 0644]

index 4a5a139f7ffb6f9a26afb2e26cc3fa68c20b4646..8275411b671eee980fa73744bc41df07de0eb6df 100644 (file)
@@ -1,3 +1,16 @@
+2019-10-18  Richard Earnshaw  <rearnsha@arm.com>
+
+       * config/arm/arm.c (arm_select_cc_mode): Allow either the first
+       or second operand of the PLUS inside a DImode equality test to be
+       sign-extend when selecting CC_Vmode.
+       * config/arm/arm.md (addvdi4): Early-split the operation into SImode
+       instructions.
+       (addsi3_cin_vout_reg, addsi3_cin_vout_imm, addsi3_cin_vout_0): New
+       expand patterns.
+       (addsi3_cin_vout_reg_insn, addsi3_cin_vout_imm_insn): New patterns.
+       (addsi3_cin_vout_0): Likewise.
+       (adddi3_compareV): Delete.
+
 2019-10-18  Richard Earnshaw  <rearnsha@arm.com>
 
        * config/arm/arm.md (addsi3_compareV_reg_nosum): New insn.
index ba23970d060a8e6e5e115062fea63bb1af5b4b2f..fe240b2c1b8e7bee2a0a9d932e8a03590578bd70 100644 (file)
@@ -15414,7 +15414,8 @@ arm_select_cc_mode (enum rtx_code op, rtx x, rtx y)
   if (GET_MODE (x) == DImode
       && (op == EQ || op == NE)
       && GET_CODE (x) == PLUS
-      && GET_CODE (XEXP (x, 0)) == SIGN_EXTEND
+      && (GET_CODE (XEXP (x, 0)) == SIGN_EXTEND
+         || GET_CODE (XEXP (x, 1)) == SIGN_EXTEND)
       && GET_CODE (y) == SIGN_EXTEND
       && GET_CODE (XEXP (y, 0)) == PLUS)
     return CC_Vmode;
index be002f7738258ad8f7c9abd6db02b1e4443af676..e9e0ca925d28060eb431536f8c31e41d37a11ed1 100644 (file)
 })
 
 (define_expand "addvdi4"
-  [(match_operand:DI 0 "register_operand")
-   (match_operand:DI 1 "register_operand")
-   (match_operand:DI 2 "register_operand")
+  [(match_operand:DI 0 "s_register_operand")
+   (match_operand:DI 1 "s_register_operand")
+   (match_operand:DI 2 "reg_or_int_operand")
    (match_operand 3 "")]
   "TARGET_32BIT"
 {
-  emit_insn (gen_adddi3_compareV (operands[0], operands[1], operands[2]));
-  arm_gen_unlikely_cbranch (NE, CC_Vmode, 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);
+
+      emit_insn (gen_addvsi4 (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_vout_0 (hi_result, hi_op1));
+      else if (CONST_INT_P (hi_op2))
+        emit_insn (gen_addsi3_cin_vout_imm (hi_result, hi_op1, hi_op2));
+      else
+        emit_insn (gen_addsi3_cin_vout_reg (hi_result, hi_op1, hi_op2));
+
+      arm_gen_unlikely_cbranch (NE, CC_Vmode, operands[3]);
+    }
 
   DONE;
 })
 
+(define_expand "addsi3_cin_vout_reg"
+  [(parallel
+    [(set (match_dup 3)
+         (compare:CC_V
+          (plus:DI
+           (plus:DI (match_dup 4)
+                    (sign_extend:DI (match_operand:SI 1 "s_register_operand")))
+           (sign_extend:DI (match_operand:SI 2 "s_register_operand")))
+          (sign_extend:DI (plus:SI (plus:SI (match_dup 5) (match_dup 1))
+                                   (match_dup 2)))))
+     (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_Vmode, 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_vout_reg_insn"
+  [(set (reg:CC_V CC_REGNUM)
+       (compare:CC_V
+        (plus:DI
+         (plus:DI
+          (match_operand:DI 3 "arm_carry_operation" "")
+          (sign_extend:DI (match_operand:SI 1 "s_register_operand" "%0,r")))
+         (sign_extend:DI (match_operand:SI 2 "s_register_operand" "l,r")))
+        (sign_extend:DI
+         (plus:SI (plus:SI (match_operand:SI 4 "arm_carry_operation" "")
+                           (match_dup 1))
+                  (match_dup 2)))))
+   (set (match_operand:SI 0 "s_register_operand" "=l,r")
+       (plus:SI (plus:SI (match_dup 4) (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_vout_imm"
+  [(parallel
+    [(set (match_dup 3)
+         (compare:CC_V
+          (plus:DI
+           (plus:DI (match_dup 4)
+                    (sign_extend:DI (match_operand:SI 1 "s_register_operand")))
+           (match_dup 2))
+          (sign_extend:DI (plus:SI (plus:SI (match_dup 5) (match_dup 1))
+                                   (match_dup 2)))))
+     (set (match_operand:SI 0 "s_register_operand")
+         (plus:SI (plus:SI (match_dup 5) (match_dup 1))
+                  (match_operand 2 "arm_adcimm_operand")))])]
+  "TARGET_32BIT"
+  {
+    operands[3] = gen_rtx_REG (CC_Vmode, 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_vout_imm_insn"
+  [(set (reg:CC_V CC_REGNUM)
+       (compare:CC_V
+        (plus:DI
+         (plus:DI
+          (match_operand:DI 3 "arm_carry_operation" "")
+          (sign_extend:DI (match_operand:SI 1 "s_register_operand" "r,r")))
+         (match_operand 2 "arm_adcimm_operand" "I,K"))
+        (sign_extend:DI
+         (plus:SI (plus:SI (match_operand:SI 4 "arm_carry_operation" "")
+                           (match_dup 1))
+                  (match_dup 2)))))
+   (set (match_operand:SI 0 "s_register_operand" "=r,r")
+       (plus:SI (plus:SI (match_dup 4) (match_dup 1))
+                (match_dup 2)))]
+  "TARGET_32BIT"
+  "@
+   adcs%?\\t%0, %1, %2
+   sbcs%?\\t%0, %1, #%B2"
+  [(set_attr "type" "alus_imm")]
+)
+
+(define_expand "addsi3_cin_vout_0"
+  [(parallel
+    [(set (match_dup 2)
+         (compare:CC_V
+          (plus:DI (match_dup 3)
+                   (sign_extend:DI (match_operand:SI 1 "s_register_operand")))
+          (sign_extend:DI (plus:SI (match_dup 4) (match_dup 1)))))
+     (set (match_operand:SI 0 "s_register_operand")
+         (plus:SI (match_dup 4) (match_dup 1)))])]
+  "TARGET_32BIT"
+  {
+    operands[2] = gen_rtx_REG (CC_Vmode, 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_vout_0_insn"
+  [(set (reg:CC_V CC_REGNUM)
+       (compare:CC_V
+        (plus:DI
+         (match_operand:DI 2 "arm_carry_operation" "")
+         (sign_extend:DI (match_operand:SI 1 "s_register_operand" "r")))
+        (sign_extend:DI (plus:SI
+                         (match_operand:SI 3 "arm_carry_operation" "")
+                         (match_dup 1)))))
+   (set (match_operand:SI 0 "s_register_operand" "=r")
+       (plus:SI (match_dup 3) (match_dup 1)))]
+  "TARGET_32BIT"
+  "adcs%?\\t%0, %1, #0"
+  [(set_attr "type" "alus_imm")]
+)
+
 (define_expand "uaddvsi4"
   [(match_operand:SI 0 "s_register_operand")
    (match_operand:SI 1 "s_register_operand")
  ]
 )
 
-(define_insn "adddi3_compareV"
-  [(set (reg:CC_V CC_REGNUM)
-       (ne:CC_V
-         (plus:TI
-           (sign_extend:TI (match_operand:DI 1 "s_register_operand" "r"))
-           (sign_extend:TI (match_operand:DI 2 "s_register_operand" "r")))
-         (sign_extend:TI (plus:DI (match_dup 1) (match_dup 2)))))
-   (set (match_operand:DI 0 "s_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_compareV_reg"
   [(set (reg:CC_V CC_REGNUM)
        (compare:CC_V
index d5d934d1b8623593b4b776b631006d8298a953c9..4267ac48382ff5df4a8018e8237bbd9c487fd15e 100644 (file)
@@ -1,3 +1,7 @@
+2019-10-18  Richard Earnshaw  <rearnsha@arm.com>
+
+       * gcc.dg/builtin-arith-overflow-3.c: New test.
+
 2019-10-18  Richard Earnshaw  <rearnsha@arm.com>
 
        * gcc.target/arm/negdi-3.c: Remove XFAIL markers.
diff --git a/gcc/testsuite/gcc.dg/builtin-arith-overflow-3.c b/gcc/testsuite/gcc.dg/builtin-arith-overflow-3.c
new file mode 100644 (file)
index 0000000..6feae1e
--- /dev/null
@@ -0,0 +1,41 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+static int cnt = 0;
+
+#define LL_MIN ((long long)(-__LONG_LONG_MAX__ - 1))
+
+#define SC1 (LL_MIN + 5)
+#define UC1 ((1ULL << (__LONG_LONG_WIDTH__ - 1)) | 5ULL)
+#define UC2 (~UC1)
+
+long long __attribute__ ((noinline, noclone))
+f1 (long long a)
+{
+  long long x;
+  if (__builtin_add_overflow (a, SC1, &x)) cnt++;
+  return x;
+}
+
+unsigned long long __attribute__ ((noinline, noclone))
+f2 (unsigned long long a)
+{
+  unsigned long long x;
+  if (__builtin_add_overflow (a, UC1, &x))
+    cnt++;
+  return x;
+}
+
+int main ()
+{
+  if (f1 (-5) != LL_MIN) __builtin_abort ();
+  if (cnt != 0) __builtin_abort ();
+  f1 (-6);
+  if (cnt != 1) __builtin_abort ();
+  cnt = 0;
+  if (f2 (UC2) != ~0ULL) __builtin_abort ();
+  if (cnt != 0) __builtin_abort ();
+  if (f2 (UC2 + 1) != 0) __builtin_abort ();
+  if (cnt != 1) __builtin_abort ();
+  return 0;
+}