[ARM][3/3] Expand mod by power of 2
authorKyrylo Tkachov <kyrylo.tkachov@arm.com>
Wed, 9 Sep 2015 08:41:41 +0000 (08:41 +0000)
committerKyrylo Tkachov <ktkachov@gcc.gnu.org>
Wed, 9 Sep 2015 08:41:41 +0000 (08:41 +0000)
* config/arm/arm.md (*subsi3_compare0): Rename to...
(subsi3_compare0): ... This.
(modsi3): New define_expand.
* config/arm/arm.c (arm_new_rtx_costs, MOD case): Handle case
when operand is power of 2.

* gcc.target/aarch64/mod_2.x: New file.
* gcc.target/aarch64/mod_256.x: Likewise.
* gcc.target/arm/mod_2.c: New test.
* gcc.target/arm/mod_256.c: Likewise.
* gcc.target/aarch64/mod_2.c: Likewise.
* gcc.target/aarch64/mod_256.c: Likewise.

From-SVN: r227586

gcc/ChangeLog
gcc/config/arm/arm.c
gcc/config/arm/arm.md
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.target/aarch64/mod_2.c [new file with mode: 0644]
gcc/testsuite/gcc.target/aarch64/mod_2.x [new file with mode: 0644]
gcc/testsuite/gcc.target/aarch64/mod_256.c [new file with mode: 0644]
gcc/testsuite/gcc.target/aarch64/mod_256.x [new file with mode: 0644]
gcc/testsuite/gcc.target/arm/mod_2.c [new file with mode: 0644]
gcc/testsuite/gcc.target/arm/mod_256.c [new file with mode: 0644]

index 25ecf5a89a5531dcd4f7ff5053074b43a4678692..618bbe63abc271a8a576acb75dfe4579791466ac 100644 (file)
@@ -1,3 +1,11 @@
+2015-09-09  Kyrylo Tkachov  <kyrylo.tkachov@arm.com>
+
+       * config/arm/arm.md (*subsi3_compare0): Rename to...
+       (subsi3_compare0): ... This.
+       (modsi3): New define_expand.
+       * config/arm/arm.c (arm_new_rtx_costs, MOD case): Handle case
+       when operand is power of 2.
+
 2015-09-09  Kyrylo Tkachov  <kyrylo.tkachov@arm.com>
 
        * config/aarch64/aarch64.md (mod<mode>3): New define_expand.
index f343d533396c60590c8e6e56d1fff725e4287f21..5f3180d38ce4f64828b41737cc2408e62302b4e7 100644 (file)
@@ -9580,6 +9580,24 @@ arm_new_rtx_costs (rtx x, enum rtx_code code, enum rtx_code outer_code,
       return false;    /* All arguments must be in registers.  */
 
     case MOD:
+      /* MOD by a power of 2 can be expanded as:
+        rsbs    r1, r0, #0
+        and     r0, r0, #(n - 1)
+        and     r1, r1, #(n - 1)
+        rsbpl   r0, r1, #0.  */
+      if (CONST_INT_P (XEXP (x, 1))
+         && exact_log2 (INTVAL (XEXP (x, 1))) > 0
+         && mode == SImode)
+       {
+         *cost += COSTS_N_INSNS (3);
+
+         if (speed_p)
+           *cost += 2 * extra_cost->alu.logical
+                    + extra_cost->alu.arith;
+         return true;
+       }
+
+    /* Fall-through.  */
     case UMOD:
       *cost = LIBCALL_COST (2);
       return false;    /* All arguments must be in registers.  */
index b6c20478f9c06e0c692c99643b79cde5a49933e7..775ca25d81671885a12529dd070e2d1af0acab2b 100644 (file)
   ""
 )
 
-(define_insn "*subsi3_compare0"
+(define_insn "subsi3_compare0"
   [(set (reg:CC_NOOV CC_REGNUM)
        (compare:CC_NOOV
         (minus:SI (match_operand:SI 1 "arm_rhs_operand" "r,r,I")
   ""
 )
 
+;; ARM-specific expansion of signed mod by power of 2
+;; using conditional negate.
+;; For r0 % n where n is a power of 2 produce:
+;; rsbs    r1, r0, #0
+;; and     r0, r0, #(n - 1)
+;; and     r1, r1, #(n - 1)
+;; rsbpl   r0, r1, #0
+
+(define_expand "modsi3"
+  [(match_operand:SI 0 "register_operand" "")
+   (match_operand:SI 1 "register_operand" "")
+   (match_operand:SI 2 "const_int_operand" "")]
+  "TARGET_32BIT"
+  {
+    HOST_WIDE_INT val = INTVAL (operands[2]);
+
+    if (val <= 0
+       || exact_log2 (val) <= 0)
+      FAIL;
+
+    rtx mask = GEN_INT (val - 1);
+
+    /* In the special case of x0 % 2 we can do the even shorter:
+       cmp     r0, #0
+       and     r0, r0, #1
+       rsblt   r0, r0, #0.  */
+
+    if (val == 2)
+      {
+       rtx cc_reg = arm_gen_compare_reg (LT,
+                                         operands[1], const0_rtx, NULL_RTX);
+       rtx cond = gen_rtx_LT (SImode, cc_reg, const0_rtx);
+       rtx masked = gen_reg_rtx (SImode);
+
+       emit_insn (gen_andsi3 (masked, operands[1], mask));
+       emit_move_insn (operands[0],
+                       gen_rtx_IF_THEN_ELSE (SImode, cond,
+                                             gen_rtx_NEG (SImode,
+                                                          masked),
+                                             masked));
+       DONE;
+      }
+
+    rtx neg_op = gen_reg_rtx (SImode);
+    rtx_insn *insn = emit_insn (gen_subsi3_compare0 (neg_op, const0_rtx,
+                                                     operands[1]));
+
+    /* Extract the condition register and mode.  */
+    rtx cmp = XVECEXP (PATTERN (insn), 0, 0);
+    rtx cc_reg = SET_DEST (cmp);
+    rtx cond = gen_rtx_GE (SImode, cc_reg, const0_rtx);
+
+    emit_insn (gen_andsi3 (operands[0], operands[1], mask));
+
+    rtx masked_neg = gen_reg_rtx (SImode);
+    emit_insn (gen_andsi3 (masked_neg, neg_op, mask));
+
+    /* We want a conditional negate here, but emitting COND_EXEC rtxes
+       during expand does not always work.  Do an IF_THEN_ELSE instead.  */
+    emit_move_insn (operands[0],
+                   gen_rtx_IF_THEN_ELSE (SImode, cond,
+                                         gen_rtx_NEG (SImode, masked_neg),
+                                         operands[0]));
+
+
+    DONE;
+  }
+)
+
 (define_expand "bswapsi2"
   [(set (match_operand:SI 0 "s_register_operand" "=r")
        (bswap:SI (match_operand:SI 1 "s_register_operand" "r")))]
index 3506d4a3a4023d97e67304bc5b54c4bd01e52017..360fe70b8d09a7f10c2d5c44f10ed6417497182b 100644 (file)
@@ -1,3 +1,12 @@
+2015-09-09  Kyrylo Tkachov  <kyrylo.tkachov@arm.com>
+
+       * gcc.target/aarch64/mod_2.x: New file.
+       * gcc.target/aarch64/mod_256.x: Likewise.
+       * gcc.target/arm/mod_2.c: New test.
+       * gcc.target/arm/mod_256.c: Likewise.
+       * gcc.target/aarch64/mod_2.c: Likewise.
+       * gcc.target/aarch64/mod_256.c: Likewise.
+
 2015-09-09  Jakub Jelinek  <jakub@redhat.com>
 
        PR c++/67504
diff --git a/gcc/testsuite/gcc.target/aarch64/mod_2.c b/gcc/testsuite/gcc.target/aarch64/mod_2.c
new file mode 100644 (file)
index 0000000..2645c18
--- /dev/null
@@ -0,0 +1,7 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mcpu=cortex-a57 -save-temps" } */
+
+#include "mod_2.x"
+
+/* { dg-final { scan-assembler "csneg\t\[wx\]\[0-9\]*" } } */
+/* { dg-final { scan-assembler-times "and\t\[wx\]\[0-9\]*" 1 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mod_2.x b/gcc/testsuite/gcc.target/aarch64/mod_2.x
new file mode 100644 (file)
index 0000000..2b079a4
--- /dev/null
@@ -0,0 +1,5 @@
+int
+f (int x)
+{
+  return x % 2;
+}
diff --git a/gcc/testsuite/gcc.target/aarch64/mod_256.c b/gcc/testsuite/gcc.target/aarch64/mod_256.c
new file mode 100644 (file)
index 0000000..567332c
--- /dev/null
@@ -0,0 +1,6 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mcpu=cortex-a57 -save-temps" } */
+
+#include "mod_256.x"
+
+/* { dg-final { scan-assembler "csneg\t\[wx\]\[0-9\]*" } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mod_256.x b/gcc/testsuite/gcc.target/aarch64/mod_256.x
new file mode 100644 (file)
index 0000000..c1de42c
--- /dev/null
@@ -0,0 +1,5 @@
+int
+f (int x)
+{
+  return x % 256;
+}
diff --git a/gcc/testsuite/gcc.target/arm/mod_2.c b/gcc/testsuite/gcc.target/arm/mod_2.c
new file mode 100644 (file)
index 0000000..93017a1
--- /dev/null
@@ -0,0 +1,8 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target arm32 } */
+/* { dg-options "-O2 -mcpu=cortex-a57 -save-temps" } */
+
+#include "../aarch64/mod_2.x"
+
+/* { dg-final { scan-assembler "rsblt\tr\[0-9\]*" } } */
+/* { dg-final { scan-assembler-times "and\tr\[0-9\].*1" 1 } } */
diff --git a/gcc/testsuite/gcc.target/arm/mod_256.c b/gcc/testsuite/gcc.target/arm/mod_256.c
new file mode 100644 (file)
index 0000000..ccb7f3c
--- /dev/null
@@ -0,0 +1,8 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target arm32 } */
+/* { dg-options "-O2 -mcpu=cortex-a57 -save-temps" } */
+
+#include "../aarch64/mod_256.x"
+
+/* { dg-final { scan-assembler "rsbpl\tr\[0-9\]*" } } */
+