PR target/50489
authorolegendo <olegendo@138bc75d-0d04-0410-961f-82ee72b054a4>
Mon, 20 Aug 2012 20:54:20 +0000 (20:54 +0000)
committerolegendo <olegendo@138bc75d-0d04-0410-961f-82ee72b054a4>
Mon, 20 Aug 2012 20:54:20 +0000 (20:54 +0000)
* config/sh/sh.md (rotcr, *rotcr, shar, shlr): New insns and splits.
(ashrdi3_k, lshrdi3_k): Rewrite as insn_and_split.
* config/sh/sh.c (sh_lshrsi_clobbers_t_reg_p): New function.
* config/sh/sh-protos.h (sh_lshrsi_clobbers_t_reg_p): Declare it.

PR target/50489
* gcc.target/sh/pr54089-1.c: New.

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

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

index 03a2afe..b284ae2 100644 (file)
@@ -1,5 +1,13 @@
 2012-08-20  Oleg Endo  <olegendo@gcc.gnu.org>
 
+       PR target/50489
+       * config/sh/sh.md (rotcr, *rotcr, shar, shlr): New insns and splits.
+       (ashrdi3_k, lshrdi3_k): Rewrite as insn_and_split.
+       * config/sh/sh.c (sh_lshrsi_clobbers_t_reg_p): New function.
+       * config/sh/sh-protos.h (sh_lshrsi_clobbers_t_reg_p): Declare it.
+
+2012-08-20  Oleg Endo  <olegendo@gcc.gnu.org>
+
        PR target/51244
        * config/sh/sh.md (*cset_zero): New insns.
 
index db5f975..8cc5cc6 100644 (file)
@@ -74,6 +74,7 @@ extern rtx sh_emit_cheap_store_flag (enum machine_mode, enum rtx_code, rtx, rtx)
 extern void sh_emit_compare_and_branch (rtx *, enum machine_mode);
 extern void sh_emit_compare_and_set (rtx *, enum machine_mode);
 extern bool sh_ashlsi_clobbers_t_reg_p (rtx);
+extern bool sh_lshrsi_clobbers_t_reg_p (rtx);
 extern void gen_shifty_op (int, rtx *);
 extern void gen_shifty_hi_op (int, rtx *);
 extern bool expand_ashiftrt (rtx *);
index 263ea3a..0760cbc 100644 (file)
@@ -2892,6 +2892,14 @@ sh_ashlsi_clobbers_t_reg_p (rtx shift_amount)
          & ASHL_CLOBBERS_T) != 0;
 }
 
+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
+         & LSHR_CLOBBERS_T) != 0;
+}
+
 /* 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?  */
index 9a58f8a..d38fd20 100644 (file)
@@ -3827,6 +3827,100 @@ label:
     FAIL;
 })
 
+;; The rotcr insn is used primarily in DImode right shifts (arithmetic
+;; and logical).  It can also be used to implement things like
+;;     bool t = a == b;
+;;     int x = (y >> 1) | (t << 31);
+(define_insn "rotcr"
+  [(set (match_operand:SI 0 "arith_reg_dest" "=r")
+       (ior:SI (lshiftrt:SI (match_operand:SI 1 "arith_reg_operand" "0")
+                            (const_int 1))
+               (ashift:SI (match_operand:SI 2 "t_reg_operand")
+                          (const_int 31))))
+   (set (reg:SI T_REG)
+       (and:SI (match_dup 1) (const_int 1)))]
+  "TARGET_SH1"
+  "rotcr       %0"
+  [(set_attr "type" "arith")])
+
+;; Simplified rotcr version for combine, which allows arbitrary shift
+;; amounts for the reg.  If the shift amount is '1' rotcr can be used
+;; directly.  Otherwise we have to insert a shift in between.
+(define_insn_and_split "*rotcr"
+  [(set (match_operand:SI 0 "arith_reg_dest")
+       (ior:SI (lshiftrt:SI (match_operand:SI 1 "arith_reg_operand")
+                            (match_operand:SI 2 "const_int_operand"))
+               (ashift:SI (match_operand:SI 3 "t_reg_operand")
+                          (const_int 31))))
+   (clobber (reg:SI T_REG))]
+  "TARGET_SH1"
+  "#"
+  "&& can_create_pseudo_p ()"
+  [(const_int 0)]
+{
+  if (INTVAL (operands[2]) > 1)
+    {
+      /* use plus_constant function ?? */
+      const int shift_count = INTVAL (operands[2]) - 1;
+      const rtx shift_count_rtx = GEN_INT (shift_count);
+      rtx shift_res = gen_reg_rtx (SImode);
+
+      rtx prev_set_t_insn = NULL_RTX;
+      rtx tmp_t_reg = NULL_RTX;
+
+      /* If we're going to emit a shift sequence that clobbers the T_REG,
+        try to find the previous insn that sets the T_REG and emit the 
+        shift insn before that insn, to remove the T_REG dependency.
+        If the insn that sets the T_REG cannot be found, store the T_REG
+        in a temporary reg and restore it after the shift.  */
+      if (sh_lshrsi_clobbers_t_reg_p (shift_count_rtx)
+         && ! sh_dynamicalize_shift_p (shift_count_rtx))
+       {
+         prev_set_t_insn = prev_nonnote_insn_bb (curr_insn);
+         if (! (prev_set_t_insn != NULL_RTX
+                && reg_set_p (get_t_reg_rtx (), prev_set_t_insn)
+                && ! reg_referenced_p (get_t_reg_rtx (),
+                                       PATTERN (prev_set_t_insn))))
+           {
+             prev_set_t_insn = NULL_RTX;
+             tmp_t_reg = gen_reg_rtx (SImode);
+             emit_insn (gen_move_insn (tmp_t_reg, get_t_reg_rtx ()));
+           } 
+       }
+
+      rtx shift_rtx = gen_lshrsi3 (shift_res, operands[1], shift_count_rtx);
+      operands[1] = shift_res;
+
+      /* Emit the shift insn before the insn that sets T_REG, if possible.  */
+      if (prev_set_t_insn != NULL_RTX)
+       emit_insn_before (shift_rtx, prev_set_t_insn);
+      else
+       emit_insn (shift_rtx);
+
+      /* Restore T_REG if it has been saved before.  */
+      if (tmp_t_reg != NULL_RTX)
+       emit_insn (gen_cmpgtsi_t (tmp_t_reg, const0_rtx));
+    }
+
+  emit_insn (gen_rotcr (operands[0], operands[1], operands[3]));
+  DONE;
+})
+
+;; rotcr combine bridge pattern which will make combine try out more
+;; complex patterns.
+(define_insn_and_split "*rotcr"
+  [(set (match_operand:SI 0 "arith_reg_dest")
+       (ashift:SI (match_operand:SI 1 "t_reg_operand") (const_int 31)))]
+  "TARGET_SH1"
+  "#"
+  "&& 1"
+  [(set (match_dup 0) (match_dup 1))
+   (parallel [(set (match_dup 0)
+                  (ior:SI (lshiftrt:SI (match_dup 0) (const_int 1))
+                          (ashift:SI (match_dup 1) (const_int 31))))
+             (set (reg:SI T_REG)
+                  (and:SI (match_dup 0) (const_int 1)))])])
+
 ;; . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
 ;; SImode shift left
 
@@ -4146,6 +4240,16 @@ label:
     FAIL;
 })
 
+(define_insn "shar"
+  [(set (match_operand:SI 0 "arith_reg_dest" "=r")
+       (ashiftrt: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"
+  "shar        %0"
+  [(set_attr "type" "arith")])
+
 (define_insn "ashrsi3_k"
   [(set (match_operand:SI 0 "arith_reg_dest" "=r")
        (ashiftrt:SI (match_operand:SI 1 "arith_reg_operand" "0")
@@ -4233,16 +4337,22 @@ label:
     FAIL;
 })
 
-;; This should be a define_insn_and_split
-(define_insn "ashrdi3_k"
+(define_insn_and_split "ashrdi3_k"
   [(set (match_operand:DI 0 "arith_reg_dest" "=r")
        (ashiftrt:DI (match_operand:DI 1 "arith_reg_operand" "0")
                     (const_int 1)))
    (clobber (reg:SI T_REG))]
   "TARGET_SH1"
-  "shar        %S0\;rotcr      %R0"
-  [(set_attr "length" "4")
-   (set_attr "type" "arith")])
+  "#"
+  "&& reload_completed"
+  [(const_int 0)]
+{
+  rtx high = gen_highpart (SImode, operands[0]);
+  rtx low = gen_lowpart (SImode, operands[0]);
+  emit_insn (gen_shar (high, high));
+  emit_insn (gen_rotcr (low, low, get_t_reg_rtx ()));
+  DONE;
+})
 
 (define_insn "ashrdi3_media"
   [(set (match_operand:DI 0 "ext_dest_operand" "=r,r")
@@ -4322,6 +4432,16 @@ label:
   "shld        %2,%0"
   [(set_attr "type" "dyn_shift")])
 
+(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_m"
   [(set (match_operand:SI 0 "arith_reg_dest" "=r")
        (lshiftrt:SI (match_operand:SI 1 "arith_reg_operand" "0")
@@ -4384,16 +4504,22 @@ label:
     FAIL;
 })
 
-;; This should be a define_insn_and_split
-(define_insn "lshrdi3_k"
+(define_insn_and_split "lshrdi3_k"
   [(set (match_operand:DI 0 "arith_reg_dest" "=r")
        (lshiftrt:DI (match_operand:DI 1 "arith_reg_operand" "0")
                     (const_int 1)))
    (clobber (reg:SI T_REG))]
   "TARGET_SH1"
-  "shlr        %S0\;rotcr      %R0"
-  [(set_attr "length" "4")
-   (set_attr "type" "arith")])
+  "#"
+  "&& reload_completed"
+  [(const_int 0)]
+{
+  rtx high = gen_highpart (SImode, operands[0]);
+  rtx low = gen_lowpart (SImode, operands[0]);
+  emit_insn (gen_shlr (high, high));
+  emit_insn (gen_rotcr (low, low, get_t_reg_rtx ()));
+  DONE;
+})
 
 (define_insn "lshrdi3_media"
   [(set (match_operand:DI 0 "ext_dest_operand" "=r,r")
index ecff56c..0db9e17 100644 (file)
@@ -1,5 +1,10 @@
 2012-08-20  Oleg Endo  <olegendo@gcc.gnu.org>
 
+       PR target/50489
+       * gcc.target/sh/pr54089-1.c: New.
+
+2012-08-20  Oleg Endo  <olegendo@gcc.gnu.org>
+
        PR target/51244
        * gcc.target/sh/pr51244-11.c: New.
 
diff --git a/gcc/testsuite/gcc.target/sh/pr54089-1.c b/gcc/testsuite/gcc.target/sh/pr54089-1.c
new file mode 100644 (file)
index 0000000..2101c53
--- /dev/null
@@ -0,0 +1,83 @@
+/* Check that the rotcr instruction is generated.  */
+/* { dg-do compile { target "sh*-*-*" } } */
+/* { dg-options "-O1" } */
+/* { dg-skip-if "" { "sh*-*-*" } { "-m5*"} { "" } }  */
+/* { dg-final { scan-assembler-times "rotcr" 11 } } */
+
+typedef char bool;
+
+long long
+test_00 (long long a)
+{
+  return a >> 1;
+}
+
+unsigned int
+test_01 (unsigned int a, int b, int c)
+{
+  bool r = b == c;
+  return ((a >> 1) | (r << 31));
+}
+
+unsigned int
+test_02 (unsigned int a, int b, int c)
+{
+  bool r = b == c;
+  return ((a >> 2) | (r << 31));
+}
+
+unsigned int
+test_03 (unsigned int a, int b, int c)
+{
+  bool r = b == c;
+  return ((a >> 3) | (r << 31));
+}
+
+unsigned int
+test_04 (unsigned int a, int b, int c)
+{
+  bool r = b == c;
+  return ((a >> 4) | (r << 31));
+}
+
+unsigned int
+test_05 (unsigned int a, int b, int c)
+{
+  bool r = b == c;
+  return ((a >> 5) | (r << 31));
+}
+
+unsigned int
+test_06 (unsigned int a, int b, int c)
+{
+  bool r = b == c;
+  return ((a >> 6) | (r << 31));
+}
+
+unsigned int
+test_07 (unsigned int a, int b, int c)
+{
+  bool r = b == c;
+  return ((a >> 7) | (r << 31));
+}
+
+unsigned int
+test_08 (unsigned int a, int b, int c)
+{
+  bool r = b == c;
+  return ((a >> 8) | (r << 31));
+}
+
+unsigned int
+test_09 (unsigned int a, int b, int c)
+{
+  bool r = b == c;
+  return ((a >> 31) | (r << 31));
+}
+
+int
+test_10 (int a, int b)
+{
+  bool r = a == b;
+  return r << 31;
+}