re PR target/54089 ([SH] Refactor shift patterns)
authorOleg Endo <olegendo@gcc.gnu.org>
Wed, 22 Aug 2012 22:52:17 +0000 (22:52 +0000)
committerOleg Endo <olegendo@gcc.gnu.org>
Wed, 22 Aug 2012 22:52:17 +0000 (22:52 +0000)
PR target/54089
* config/sh/predicates (p27_rshift_count_operand,
not_p27_rshift_count_operand): New predicates.
* config/sh/sh.c (sh_ashlsi_clobbers_t_reg_p,
sh_lshrsi_clobbers_t_reg_p, sh_dynamicalize_shift_p): Handle special
case when shift amount is 31.
(gen_ashift): Emit gen_shlr instead of gen_lshrsi3_m.
* config/sh/sh.md (ashlsi3_d): Set type to 'dyn_shift' instead
of 'arith'.
(ashlsi_c): Rename to shll.  Adapt calls to gen_ashlsi_c throughout
the file.
(lshrsi3): Remove clobber from expander.  Use shift_count_operand
instead of nonmemory_operand predicate for second operand.  Add
handling of case lshrsi3_n_clobbers_t.
(lshrsi3_k): Use p27_rshift_count_operand for second operand.
(lshrsi3_d): Make insn_and_split.  Split dynamic shift to constant
shift sequences if beneficial.
(lshrsi3_n): Make insn_and_split.  Split constant shift sequence to
dynamic shift if beneficial.
(lshrsi3_n_clobbers_t): New insn_and_split.
(lshrsi3_m): Delete.

PR target/54089
* gcc.target/sh/pr54089-2.c: New.

From-SVN: r190603

gcc/ChangeLog
gcc/config/sh/predicates.md
gcc/config/sh/sh.c
gcc/config/sh/sh.md
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.target/sh/pr54089-2.c [new file with mode: 0644]

index 527f822ab501a8865670051ee6edf8bd55d43859..3bf9a38c77b8816556c5c760151a647d3f4249bf 100644 (file)
@@ -1,3 +1,27 @@
+2012-08-22  Oleg Endo  <olegendo@gcc.gnu.org>
+
+       PR target/54089
+       * config/sh/predicates (p27_rshift_count_operand,
+       not_p27_rshift_count_operand): New predicates.
+       * config/sh/sh.c (sh_ashlsi_clobbers_t_reg_p,
+       sh_lshrsi_clobbers_t_reg_p, sh_dynamicalize_shift_p): Handle special
+       case when shift amount is 31.
+       (gen_ashift): Emit gen_shlr instead of gen_lshrsi3_m.
+       * config/sh/sh.md (ashlsi3_d): Set type to 'dyn_shift' instead
+       of 'arith'.
+       (ashlsi_c): Rename to shll.  Adapt calls to gen_ashlsi_c throughout
+       the file.
+       (lshrsi3): Remove clobber from expander.  Use shift_count_operand
+       instead of nonmemory_operand predicate for second operand.  Add
+       handling of case lshrsi3_n_clobbers_t.
+       (lshrsi3_k): Use p27_rshift_count_operand for second operand.
+       (lshrsi3_d): Make insn_and_split.  Split dynamic shift to constant
+       shift sequences if beneficial.
+       (lshrsi3_n): Make insn_and_split.  Split constant shift sequence to
+       dynamic shift if beneficial.
+       (lshrsi3_n_clobbers_t): New insn_and_split.
+       (lshrsi3_m): Delete.
+
 2012-08-22  Steven Bosscher  <steven@gcc.gnu.org>
 
        * tracer.c (mark_bb_seen): Use SBITMAP_SIZE.
index 12f69db976ea2d571c01cd8a2bbef03d0ebd13d7..92a7b689c847aaa940665134f75179169100d537 100644 (file)
   return arith_reg_operand (op, mode);
 })
 
+;; Predicates for matching operands that are constant shift
+;; amounts 1, 2, 8, 16.
 (define_predicate "p27_shift_count_operand"
   (and (match_code "const_int")
        (match_test "satisfies_constraint_P27 (op)")))
   (and (match_code "const_int")
        (match_test "! satisfies_constraint_P27 (op)")))
 
+;; For right shifts the constant 1 is a special case because the shlr insn
+;; clobbers the T_REG and is handled by the T_REG clobbering version of the
+;; insn, which is also used for non-P27 shift sequences.
+(define_predicate "p27_rshift_count_operand"
+  (and (match_code "const_int")
+       (match_test "satisfies_constraint_P27 (op)")
+       (match_test "! satisfies_constraint_M (op)")))
+
+(define_predicate "not_p27_rshift_count_operand"
+  (and (match_code "const_int")
+       (ior (match_test "! satisfies_constraint_P27 (op)")
+           (match_test "satisfies_constraint_M (op)"))))
+
 ;; TODO: Add a comment here.
 
 (define_predicate "shift_operator"
index 0760cbcbc4c72b65cfbf5e2940dcbb0b4f8226e9..3851ec6d50f3fed0ca195ad2ca349a6ac2ff2b52 100644 (file)
@@ -2840,6 +2840,11 @@ static const struct ashl_lshr_sequence ashl_lshr_seq[32] =
   { 4, { 16, 8, 2, 2 },            0 },
   { 4, { 16, -1, -2, 16 },  ASHL_CLOBBERS_T },
   { 3, { 16, -2, 16 },     0 },
+
+  /* For a right shift by 31 a 2 insn shll-movt sequence can be used.
+     For a left shift by 31 a 2 insn and-rotl sequences can be used.
+     However, the shift-and combiner code needs this entry here to be in
+     terms of real shift insns.  */
   { 3, { 16, -1, 16 },     ASHL_CLOBBERS_T }
 };
 
@@ -2888,7 +2893,14 @@ bool
 sh_ashlsi_clobbers_t_reg_p (rtx shift_amount)
 {
   gcc_assert (CONST_INT_P (shift_amount));
-  return (ashl_lshr_seq[INTVAL (shift_amount) & 31].clobbers_t
+  
+  const int shift_amount_i = INTVAL (shift_amount) & 31;
+
+  /* Special case for shift count of 31: use and-rotl sequence.  */
+  if (shift_amount_i == 31)
+    return true;
+
+  return (ashl_lshr_seq[shift_amount_i].clobbers_t
          & ASHL_CLOBBERS_T) != 0;
 }
 
@@ -2896,10 +2908,39 @@ bool
 sh_lshrsi_clobbers_t_reg_p (rtx shift_amount)
 {
   gcc_assert (CONST_INT_P (shift_amount));
-  return (ashl_lshr_seq[INTVAL (shift_amount) & 31].clobbers_t
+
+  const int shift_amount_i = INTVAL (shift_amount) & 31;
+  /* Special case for shift count of 31: use shll-movt sequence.  */
+  if (shift_amount_i == 31)
+    return true;
+
+  return (ashl_lshr_seq[shift_amount_i].clobbers_t
          & LSHR_CLOBBERS_T) != 0;
 }
 
+/* Return true if it is potentially beneficial to use a dynamic shift
+   instruction (shad / shar) instead of a combination of 1/2/8/16 
+   shift instructions for the specified shift count.
+   If dynamic shifts are not available, always return false.  */
+bool
+sh_dynamicalize_shift_p (rtx count)
+{
+  gcc_assert (CONST_INT_P (count));
+
+  const int shift_amount_i = INTVAL (count) & 31;
+  int insn_count;
+
+  /* For left and right shifts, there are shorter 2 insn sequences for
+     shift amounts of 31.  */
+  if (shift_amount_i == 31)
+    insn_count = 2;
+  else
+    insn_count = ashl_lshr_seq[shift_amount_i].insn_count;
+
+  return TARGET_DYNSHIFT && (insn_count > 1 + SH_DYNAMIC_SHIFT_COST);
+}
+
 /* Assuming we have a value that has been sign-extended by at least one bit,
    can we use the ext_shift_amounts with the last shift turned to an arithmetic shift
    to shift it by N without data loss, and quicker than by other means?  */
@@ -3385,7 +3426,7 @@ gen_ashift (int type, int n, rtx reg)
       break;
     case LSHIFTRT:
       if (n == 1)
-       emit_insn (gen_lshrsi3_m (reg, reg, n_rtx));
+        emit_insn (gen_shlr (reg, reg));
       else
        emit_insn (gen_lshrsi3_k (reg, reg, n_rtx));
       break;
@@ -3596,19 +3637,6 @@ expand_ashiftrt (rtx *operands)
   return true;
 }
 
-/* Return true if it is potentially beneficial to use a dynamic shift
-   instruction (shad / shar) instead of a combination of 1/2/8/16 
-   shift instructions for the specified shift count.
-   If dynamic shifts are not available, always return false.  */
-bool
-sh_dynamicalize_shift_p (rtx count)
-{
-  int insn_count;
-  gcc_assert (CONST_INT_P (count));
-  insn_count = ashl_lshr_seq[INTVAL (count) & 31].insn_count;
-  return TARGET_DYNSHIFT && (insn_count > 1 + SH_DYNAMIC_SHIFT_COST);
-}
-
 /* Try to find a good way to implement the combiner pattern
   [(set (match_operand:SI 0 "register_operand" "r")
         (and:SI (ashift:SI (match_operand:SI 1 "register_operand" "r")
index 4402df8577778687344f89f3d8f1405f4eba0d1f..ede916effb4a8309c0e434da71042bc2af4c3120 100644 (file)
@@ -4058,7 +4058,7 @@ label:
 
   FAIL;
 }
-  [(set_attr "type" "arith")])
+  [(set_attr "type" "dyn_shift")])
 
 (define_insn_and_split "ashlsi3_n"
   [(set (match_operand:SI 0 "arith_reg_dest" "=r")
@@ -4116,7 +4116,7 @@ label:
   DONE;
 })
 
-(define_insn "ashlsi_c"
+(define_insn "shll"
   [(set (match_operand:SI 0 "arith_reg_dest" "=r")
        (ashift:SI (match_operand:SI 1 "arith_reg_operand" "0") (const_int 1)))
    (set (reg:SI T_REG)
@@ -4142,7 +4142,7 @@ label:
    && peep2_reg_dead_p (2, operands[1])"
   [(const_int 0)]
 {
-  emit_insn (gen_ashlsi_c (operands[1], operands[1]));
+  emit_insn (gen_shll (operands[1], operands[1]));
   DONE;
 })
 
@@ -4349,7 +4349,7 @@ label:
   "&& 1"
   [(const_int 0)]
 {
-  emit_insn (gen_ashlsi_c (operands[0], operands[1]));
+  emit_insn (gen_shll (operands[0], operands[1]));
   emit_insn (gen_mov_neg_si_t (operands[0], get_t_reg_rtx ()));
   DONE;
 })
@@ -4463,12 +4463,10 @@ label:
 ;; . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
 ;; SImode logical shift right
 
-;; Only the single bit shift clobbers the T bit.
 (define_expand "lshrsi3"
-  [(parallel [(set (match_operand:SI 0 "arith_reg_dest" "")
-                  (lshiftrt:SI (match_operand:SI 1 "arith_reg_operand" "")
-                               (match_operand:SI 2 "nonmemory_operand" "")))
-             (clobber (reg:SI T_REG))])]
+  [(set (match_operand:SI 0 "arith_reg_dest" "")
+       (lshiftrt:SI (match_operand:SI 1 "arith_reg_operand" "")
+                    (match_operand:SI 2 "shift_count_operand" "")))]
   ""
 {
   if (TARGET_SHMEDIA)
@@ -4476,71 +4474,147 @@ label:
       emit_insn (gen_lshrsi3_media (operands[0], operands[1], operands[2]));
       DONE;
     }
-  if (CONST_INT_P (operands[2])
-      && sh_dynamicalize_shift_p (operands[2]))
-    operands[2] = force_reg (SImode, operands[2]);
+
+  /* If a dynamic shift is supposed to be used, expand the lshrsi3_d insn
+     here, otherwise the pattern will never match due to the shift amount reg
+     negation.  */
   if (TARGET_DYNSHIFT
-      && arith_reg_operand (operands[2], GET_MODE (operands[2])))
+      && CONST_INT_P (operands[2]) && sh_dynamicalize_shift_p (operands[2]))
     {
-      rtx count = copy_to_mode_reg (SImode, operands[2]);
-      emit_insn (gen_negsi2 (count, count));
-      emit_insn (gen_lshrsi3_d (operands[0], operands[1], count));
+      rtx neg_count = force_reg (SImode,
+                                gen_int_mode (- INTVAL (operands[2]), SImode));
+      emit_insn (gen_lshrsi3_d (operands[0], operands[1], neg_count));
       DONE;
     }
-  if (! immediate_operand (operands[2], GET_MODE (operands[2])))
-    FAIL;
-})
 
-(define_insn "lshrsi3_d"
-  [(set (match_operand:SI 0 "arith_reg_dest" "=r")
-       (lshiftrt:SI (match_operand:SI 1 "arith_reg_operand" "0")
-                    (neg:SI (match_operand:SI 2 "arith_reg_operand" "r"))))]
-  "TARGET_DYNSHIFT"
-  "shld        %2,%0"
-  [(set_attr "type" "dyn_shift")])
+  if (TARGET_DYNSHIFT && ! CONST_INT_P (operands[2]))
+    {
+      rtx neg_count = gen_reg_rtx (SImode);
+      emit_insn (gen_negsi2 (neg_count, operands[2]));
+      emit_insn (gen_lshrsi3_d (operands[0], operands[1], neg_count));
+      DONE;
+    }
 
-(define_insn "shlr"
+  /* If the lshrsi3_* insn is going to clobber the T_REG it must be
+     expanded here.  */
+  if (CONST_INT_P (operands[2])
+      && sh_lshrsi_clobbers_t_reg_p (operands[2])
+      && ! sh_dynamicalize_shift_p (operands[2]))
+    {
+      emit_insn (gen_lshrsi3_n_clobbers_t (operands[0], operands[1],
+                operands[2]));
+      DONE;
+    }
+})
+
+(define_insn "lshrsi3_k"
   [(set (match_operand:SI 0 "arith_reg_dest" "=r")
        (lshiftrt:SI (match_operand:SI 1 "arith_reg_operand" "0")
-                    (const_int 1)))
-   (set (reg:SI T_REG)
-       (and:SI (match_dup 1) (const_int 1)))]
+                    (match_operand:SI 2 "p27_rshift_count_operand" "P27")))]
   "TARGET_SH1"
-  "shlr        %0"
+  "shlr%O2     %0"
   [(set_attr "type" "arith")])
 
-(define_insn "lshrsi3_m"
+(define_insn_and_split "lshrsi3_d"
   [(set (match_operand:SI 0 "arith_reg_dest" "=r")
        (lshiftrt:SI (match_operand:SI 1 "arith_reg_operand" "0")
-                    (match_operand:SI 2 "const_int_operand" "M")))
-   (clobber (reg:SI T_REG))]
-  "TARGET_SH1 && satisfies_constraint_M (operands[2])"
-  "shlr        %0"
-  [(set_attr "type" "arith")])
+                    (neg:SI (match_operand:SI 2 "shift_count_operand" "r"))))]
+  "TARGET_DYNSHIFT"
+  "shld        %2,%0"
+  "&& CONST_INT_P (operands[2]) && ! sh_dynamicalize_shift_p (operands[2])
+   && ! sh_lshrsi_clobbers_t_reg_p (operands[2])"
+  [(const_int 0)]
+{
+  if (satisfies_constraint_P27 (operands[2]))
+    {
+      /* This will not be done for a shift amount of 1, because it would
+        clobber the T_REG.  */
+      emit_insn (gen_lshrsi3_k (operands[0], operands[1], operands[2]));
+      DONE;
+    }
+  else if (! satisfies_constraint_P27 (operands[2]))
+    {
+      /* This must happen before reload, otherwise the constant will be moved
+        into a register due to the "r" constraint, after which this split
+        cannot be done anymore.
+        Unfortunately the move insn will not always be eliminated.
+        Also, here we must not create a shift sequence that clobbers the
+        T_REG.  */
+      emit_move_insn (operands[0], operands[1]);
+      gen_shifty_op (LSHIFTRT, operands);
+      DONE;
+    }
 
-(define_insn "lshrsi3_k"
+  FAIL;
+}
+  [(set_attr "type" "dyn_shift")])
+
+(define_insn_and_split "lshrsi3_n"
   [(set (match_operand:SI 0 "arith_reg_dest" "=r")
        (lshiftrt:SI (match_operand:SI 1 "arith_reg_operand" "0")
-                    (match_operand:SI 2 "const_int_operand" "P27")))]
-  "TARGET_SH1 && satisfies_constraint_P27 (operands[2])
-   && ! satisfies_constraint_M (operands[2])"
-  "shlr%O2     %0"
-  [(set_attr "type" "arith")])
+                    (match_operand:SI 2 "not_p27_rshift_count_operand")))]
+  "TARGET_SH1 && ! sh_lshrsi_clobbers_t_reg_p (operands[2])"
+  "#"
+  "&& (reload_completed
+       || (sh_dynamicalize_shift_p (operands[2]) && can_create_pseudo_p ()))"
+  [(const_int 0)]
+{
+  if (sh_dynamicalize_shift_p (operands[2]) && can_create_pseudo_p ())
+    {
+      /* If this pattern was picked and dynamic shifts are supported, switch
+        to dynamic shift pattern before reload.  */
+      operands[2] = force_reg (SImode,
+                              gen_int_mode (- INTVAL (operands[2]), SImode));
+      emit_insn (gen_lshrsi3_d (operands[0], operands[1], operands[2]));
+    }
+  else
+    gen_shifty_op (LSHIFTRT, operands);
 
-(define_insn_and_split "lshrsi3_n"
+  DONE;
+})
+
+;; The lshrsi3_n_clobbers_t pattern also works as a simplified version of
+;; the shlr pattern.
+(define_insn_and_split "lshrsi3_n_clobbers_t"
   [(set (match_operand:SI 0 "arith_reg_dest" "=r")
        (lshiftrt:SI (match_operand:SI 1 "arith_reg_operand" "0")
-                    (match_operand:SI 2 "const_int_operand" "n")))
+                    (match_operand:SI 2 "not_p27_rshift_count_operand")))
    (clobber (reg:SI T_REG))]
-  "TARGET_SH1 && ! sh_dynamicalize_shift_p (operands[2])"
+  "TARGET_SH1 && sh_lshrsi_clobbers_t_reg_p (operands[2])"
   "#"
-  "TARGET_SH1 && reload_completed"
-  [(use (reg:SI R0_REG))]
+  "&& (reload_completed || INTVAL (operands[2]) == 31
+       || (sh_dynamicalize_shift_p (operands[2]) && can_create_pseudo_p ()))"
+  [(const_int 0)]
 {
-  gen_shifty_op (LSHIFTRT, operands);
+  if (INTVAL (operands[2]) == 31)
+    {
+      emit_insn (gen_shll (operands[0], operands[1]));
+      emit_insn (gen_movt (operands[0], get_t_reg_rtx ()));
+    }
+  else if (sh_dynamicalize_shift_p (operands[2]) && can_create_pseudo_p ())
+    {
+      /* If this pattern was picked and dynamic shifts are supported, switch
+        to dynamic shift pattern before reload.  */
+      operands[2] = force_reg (SImode,
+                              gen_int_mode (- INTVAL (operands[2]), SImode));
+      emit_insn (gen_lshrsi3_d (operands[0], operands[1], operands[2]));
+    }
+  else
+    gen_shifty_op (LSHIFTRT, operands);
+
   DONE;
 })
 
+(define_insn "shlr"
+  [(set (match_operand:SI 0 "arith_reg_dest" "=r")
+       (lshiftrt:SI (match_operand:SI 1 "arith_reg_operand" "0")
+                    (const_int 1)))
+   (set (reg:SI T_REG)
+       (and:SI (match_dup 1) (const_int 1)))]
+  "TARGET_SH1"
+  "shlr        %0"
+  [(set_attr "type" "arith")])
+
 (define_insn "lshrsi3_media"
   [(set (match_operand:SI 0 "arith_reg_dest" "=r,r")
        (lshiftrt:SI (match_operand:SI 1 "extend_reg_operand" "r,r")
index ed45fc03c56e653fd37943d8a368c9fe529f0701..3c4db30985b17ff44674dc2e09b19d9a74a15043 100644 (file)
@@ -1,3 +1,8 @@
+2012-08-22  Oleg Endo  <olegendo@gcc.gnu.org>
+
+       PR target/54089
+       * gcc.target/sh/pr54089-2.c: New.
+
 2012-08-22  H.J. Lu  <hongjiu.lu@intel.com>
 
        * gcc.target/i386/long-double-64-1.c: New file.
diff --git a/gcc/testsuite/gcc.target/sh/pr54089-2.c b/gcc/testsuite/gcc.target/sh/pr54089-2.c
new file mode 100644 (file)
index 0000000..61b703d
--- /dev/null
@@ -0,0 +1,22 @@
+/* Check that for dynamic logical right shifts with a constant the negated
+   constant is loaded directly, instead of loading the postitive constant
+   and negating it separately.  This was a case that happened at optimization
+   level -O2 and looked like:
+       cmp/eq  r6,r5
+       mov     #30,r1
+       neg     r1,r1
+       shld    r1,r4
+       mov     r4,r0
+       rts
+       rotcr   r0  */
+/* { dg-do compile { target "sh*-*-*" } } */
+/* { dg-options "-O2" } */
+/* { dg-skip-if "" { "sh*-*-*" } { "*"} { "-m3* -m2a* -m4*" } }  */
+/* { dg-final { scan-assembler-not "neg" } } */
+
+unsigned int
+test (unsigned int a, int b, int c)
+{
+  unsigned char r = b == c;
+  return ((a >> 31) | (r << 31));
+}