This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[SH] PR 54089 - Logical right shifts
- From: Oleg Endo <oleg dot endo at t-online dot de>
- To: gcc-patches <gcc-patches at gcc dot gnu dot org>
- Date: Wed, 22 Aug 2012 10:48:37 +0200
- Subject: [SH] PR 54089 - Logical right shifts
Hello,
This adapts SH's logical right shift patterns to work/look the same way
as left shift patterns. It mainly fixes a few issues with dynamic shift
insn selection.
Tested on rev 190580 with
make -k check RUNTESTFLAGS="--target_board=sh-sim
\{-m2/-ml,-m2/-mb,-m2a/-mb,-m4/-ml,-m4/-mb,-m4a/-ml,-m4a/-mb}"
and no new failures.
OK?
Cheers,
Oleg
ChangeLog:
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.
testsuite/ChangeLog:
PR target/54089
* gcc.target/sh/pr54089-2.c: New.
Index: gcc/config/sh/predicates.md
===================================================================
--- gcc/config/sh/predicates.md (revision 190580)
+++ gcc/config/sh/predicates.md (working copy)
@@ -825,6 +825,8 @@
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)")))
@@ -833,6 +835,19 @@
(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: gcc/config/sh/sh.c
===================================================================
--- gcc/config/sh/sh.c (revision 190580)
+++ gcc/config/sh/sh.c (working copy)
@@ -2840,6 +2840,11 @@
{ 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 @@
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 @@
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 @@
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 @@
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: gcc/config/sh/sh.md
===================================================================
--- gcc/config/sh/sh.md (revision 190580)
+++ gcc/config/sh/sh.md (working copy)
@@ -4058,7 +4058,7 @@
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 @@
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 @@
&& 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 @@
"&& 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 @@
;; . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
;; 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 @@
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;
+
+ 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;
+ }
+
+ /* 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_d"
+(define_insn "lshrsi3_k"
[(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")])
-
-(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)))]
+ (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: gcc/testsuite/gcc.target/sh/pr54089-2.c
===================================================================
--- gcc/testsuite/gcc.target/sh/pr54089-2.c (revision 0)
+++ gcc/testsuite/gcc.target/sh/pr54089-2.c (revision 0)
@@ -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));
+}