diff mbox

[SH] PR 51244 - Improve T bit store and cbranch

Message ID 1349992594.8747.32.camel@yam-132-YW-E178-FTW
State New
Headers show

Commit Message

Oleg Endo Oct. 11, 2012, 9:56 p.m. UTC
Hello,

This one further improves T bit stores and conditional branches on SH
for cases like described in comment #53 in the PR.
Tested on rev 192200 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.

Cheers,
Oleg

gcc/ChangeLog:

	PR target/51244
	* config/sh/sh.md (negsi_cond, negdi_cond, stack_protect_test): 
	Remove get_t_reg_rtx when invoking gen_branch_true or 
	gen_branch_false.
	(*zero_extend<mode>si2_compact): Convert to insn_and_split.  
	Convert zero extensions of T bit stores to reg moves in 
	splitter.  Remove obsolete unnamed peephole2 that caught zero 
	extensions after negc T bit stores.
	(*branch_true_eq, *branch_false_ne): Delete.
	(branch_true, branch_false): Convert insn to expander.  Move 
	actual insn logic to...
	(*cbranch_t): ...this new insn_and_split.  Try to find 
	preceding redundant T bit stores and tests and combine them 
	with the conditional branch if possible in the splitter.
	(movrt_xor, *movt_movrt): New insn_and_split.
	* config/sh/predicates.md (cbranch_treg_value): New predicate.
	* config/sh/sh-protos.h (sh_eval_treg_value): Forward declare...
	* config/sh/sh.c (sh_eval_treg_value): ...this new function.
	(expand_cbranchsi4, expand_cbranchdi4): Remove get_t_reg_rtx 
	when invoking gen_branch_true or gen_branch_false.

testsuite/ChangeLog:

	PR target/51244
	* gcc.target/sh/pr51244-13.c: New.
	* gcc.target/sh/pr51244-14.c: New.
	* gcc.target/sh/pr51244-15.c: New.
	* gcc.target/sh/pr51244-16.c: New.

Comments

Kaz Kojima Oct. 12, 2012, 12:16 a.m. UTC | #1
Oleg Endo <oleg.endo@t-online.de> wrote:
> This one further improves T bit stores and conditional branches on SH
> for cases like described in comment #53 in the PR.
> Tested on rev 192200 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.

The patch is OK.

Regards,
	kaz
diff mbox

Patch

Index: gcc/config/sh/sh.c
===================================================================
--- gcc/config/sh/sh.c	(revision 192200)
+++ gcc/config/sh/sh.c	(working copy)
@@ -2059,7 +2059,7 @@ 
 void
 expand_cbranchsi4 (rtx *operands, enum rtx_code comparison, int probability)
 {
-  rtx (*branch_expander) (rtx, rtx) = gen_branch_true;
+  rtx (*branch_expander) (rtx) = gen_branch_true;
   comparison = prepare_cbranch_operands (operands, SImode, comparison);
   switch (comparison)
     {
@@ -2071,7 +2071,7 @@ 
   emit_insn (gen_rtx_SET (VOIDmode, get_t_reg_rtx (),
                           gen_rtx_fmt_ee (comparison, SImode,
                                           operands[1], operands[2])));
-  rtx jump = emit_jump_insn (branch_expander (operands[3], get_t_reg_rtx ()));
+  rtx jump = emit_jump_insn (branch_expander (operands[3]));
   if (probability >= 0)
     add_reg_note (jump, REG_BR_PROB, GEN_INT (probability));
 }
@@ -2123,7 +2123,7 @@ 
       if (TARGET_CMPEQDI_T)
 	{
 	  emit_insn (gen_cmpeqdi_t (operands[1], operands[2]));
-	  emit_jump_insn (gen_branch_true (operands[3], get_t_reg_rtx ()));
+	  emit_jump_insn (gen_branch_true (operands[3]));
 	  return true;
 	}
       msw_skip = NE;
@@ -2150,7 +2150,7 @@ 
       if (TARGET_CMPEQDI_T)
 	{
 	  emit_insn (gen_cmpeqdi_t (operands[1], operands[2]));
-	  emit_jump_insn (gen_branch_false (operands[3], get_t_reg_rtx ()));
+	  emit_jump_insn (gen_branch_false (operands[3]));
 	  return true;
 	}
       msw_taken = NE;
@@ -2281,6 +2281,43 @@ 
   return true;
 }
 
+/* Given an operand, return 1 if the evaluated operand plugged into an
+   if_then_else will result in a branch_true, 0 if branch_false, or
+   -1 if neither nor applies.  The truth table goes like this:
+
+       op   | cmpval |   code  | result
+   ---------+--------+---------+--------------------
+      T (0) |   0    |  EQ (1) |  0 = 0 ^ (0 == 1)
+      T (0) |   1    |  EQ (1) |  1 = 0 ^ (1 == 1)
+      T (0) |   0    |  NE (0) |  1 = 0 ^ (0 == 0)
+      T (0) |   1    |  NE (0) |  0 = 0 ^ (1 == 0)
+     !T (1) |   0    |  EQ (1) |  1 = 1 ^ (0 == 1)
+     !T (1) |   1    |  EQ (1) |  0 = 1 ^ (1 == 1)
+     !T (1) |   0    |  NE (0) |  0 = 1 ^ (0 == 0)
+     !T (1) |   1    |  NE (0) |  1 = 1 ^ (1 == 0)  */
+int
+sh_eval_treg_value (rtx op)
+{
+  enum rtx_code code = GET_CODE (op);
+  if ((code != EQ && code != NE) || !CONST_INT_P (XEXP (op, 1)))
+    return -1;
+
+  int cmpop = code == EQ ? 1 : 0;
+  int cmpval = INTVAL (XEXP (op, 1));
+  if (cmpval != 0 && cmpval != 1)
+    return -1;
+
+  int t;
+  if (t_reg_operand (XEXP (op, 0), GET_MODE (XEXP (op, 0))))
+    t = 0;
+  else if (negt_reg_operand (XEXP (op, 0), GET_MODE (XEXP (op, 0))))
+    t = 1;
+  else
+    return -1;
+  
+  return t ^ (cmpval == cmpop);
+}
+
 /* Emit INSN, possibly in a PARALLEL with an USE of fpscr for SH4.  */
 
 static void
@@ -2485,9 +2522,9 @@ 
     sh_emit_set_t_insn (gen_ieee_ccmpeqsf_t (op0, op1), mode);
 
   if (branch_code == code)
-    emit_jump_insn (gen_branch_true (operands[3], get_t_reg_rtx ()));
+    emit_jump_insn (gen_branch_true (operands[3]));
   else
-    emit_jump_insn (gen_branch_false (operands[3], get_t_reg_rtx ()));
+    emit_jump_insn (gen_branch_false (operands[3]));
 }
 
 void
@@ -2521,7 +2558,7 @@ 
             {
               lab = gen_label_rtx ();
               sh_emit_scc_to_t (EQ, op0, op1);
-              emit_jump_insn (gen_branch_true (lab, get_t_reg_rtx ()));
+              emit_jump_insn (gen_branch_true (lab));
               code = GT;
            }
           else
Index: gcc/config/sh/sh.md
===================================================================
--- gcc/config/sh/sh.md	(revision 192200)
+++ gcc/config/sh/sh.md	(working copy)
@@ -5322,8 +5322,8 @@ 
   emit_insn (gen_movsi (operands[0], operands[1]));
 
   emit_jump_insn (INTVAL (operands[3])
-		  ? gen_branch_true (skip_neg_label, get_t_reg_rtx ())
-		  : gen_branch_false (skip_neg_label, get_t_reg_rtx ()));
+		  ? gen_branch_true (skip_neg_label)
+		  : gen_branch_false (skip_neg_label));
 
   emit_label_after (skip_neg_label,
 		    emit_insn (gen_negsi2 (operands[0], operands[1])));
@@ -5391,8 +5391,8 @@ 
   emit_insn (gen_movsi (high_dst, high_src));
 
   emit_jump_insn (INTVAL (operands[3]) 
-		  ? gen_branch_true (skip_neg_label, get_t_reg_rtx ())
-		  : gen_branch_false (skip_neg_label, get_t_reg_rtx ()));
+		  ? gen_branch_true (skip_neg_label)
+		  : gen_branch_false (skip_neg_label));
 
   if (!INTVAL (operands[3]))
     emit_insn (gen_clrt ());
@@ -5575,11 +5575,57 @@ 
   [(set (match_operand:SI 0 "arith_reg_dest")
 	(zero_extend:SI (match_operand:QIHI 1 "zero_extend_operand")))])
 
-(define_insn "*zero_extend<mode>si2_compact"
+(define_insn_and_split "*zero_extend<mode>si2_compact"
   [(set (match_operand:SI 0 "arith_reg_dest" "=r")
 	(zero_extend:SI (match_operand:QIHI 1 "arith_reg_operand" "r")))]
   "TARGET_SH1"
   "extu.<bw>	%1,%0"
+  "&& can_create_pseudo_p ()"
+  [(set (match_dup 0) (match_dup 2))]
+{
+  /* Sometimes combine fails to combine a T bit or negated T bit store to a
+     reg with a following zero extension.  In the split pass after combine,
+     try to figure the extended reg was set.  If it originated from the T
+     bit we can replace the zero extension with a reg move, which will be
+     eliminated.  Notice that this also helps the *cbranch_t splitter when
+     it tries to post-combine tests and conditional branches, as it does not
+     check for zero extensions.  */
+  rtx ext_reg;
+  if (REG_P (operands[1]))
+    ext_reg = operands[1];
+  else if (GET_CODE (operands[1]) == SUBREG && REG_P (SUBREG_REG (operands[1])))
+    ext_reg = SUBREG_REG (operands[1]);
+  else
+    FAIL;
+
+  /* Reg moves must be of the same mode.  */
+  if (GET_MODE (ext_reg) != SImode)
+    FAIL;
+
+  operands[2] = NULL_RTX;
+  for (rtx i = prev_nonnote_insn_bb (curr_insn); i != NULL_RTX;
+       i = prev_nonnote_insn_bb (i))
+    {
+      if (LABEL_P (i) || BARRIER_P (i))
+	break;
+      if (!NONJUMP_INSN_P (i))
+	continue;
+
+      if (reg_set_p (ext_reg, i))
+	{
+	  rtx set_op = XEXP (set_of (ext_reg, i), 1);
+	  if (set_op == NULL_RTX)
+	    break;
+	  if (t_reg_operand (set_op, VOIDmode)
+	      || negt_reg_operand (set_op, VOIDmode))
+	    operands[2] = ext_reg;
+	  break;
+	}
+    }
+
+  if (operands[2] == NULL_RTX)
+    FAIL;
+}
   [(set_attr "type" "arith")])
 
 (define_insn "*zero_extendhisi2_media"
@@ -8108,47 +8154,126 @@ 
 ;; Define the real conditional branch instructions.
 ;; ------------------------------------------------------------------------
 
-(define_insn "branch_true"
-  [(set (pc) (if_then_else (ne (match_operand 1 "t_reg_operand" "")
-			       (const_int 0))
-			   (label_ref (match_operand 0 "" ""))
+(define_expand "branch_true"
+  [(set (pc) (if_then_else (ne (reg:SI T_REG) (const_int 0))
+			   (label_ref (match_operand 0))
 			   (pc)))]
-  "TARGET_SH1"
-{
-  return output_branch (1, insn, operands);
-}
-  [(set_attr "type" "cbranch")])
+  "TARGET_SH1")
 
-(define_insn "*branch_true_eq"
-  [(set (pc) (if_then_else (eq (match_operand 1 "t_reg_operand" "")
-			       (const_int 1))
-			   (label_ref (match_operand 0 "" ""))
+(define_expand "branch_false"
+  [(set (pc) (if_then_else (eq (reg:SI T_REG) (const_int 0))
+			   (label_ref (match_operand 0))
 			   (pc)))]
-  "TARGET_SH1"
-{
-  return output_branch (1, insn, operands);
-}
-  [(set_attr "type" "cbranch")])
+  "TARGET_SH1")
 
-(define_insn "branch_false"
-  [(set (pc) (if_then_else (eq (match_operand 1 "t_reg_operand" "")
-			       (const_int 0))
-			   (label_ref (match_operand 0 "" ""))
+(define_insn_and_split "*cbranch_t"
+  [(set (pc) (if_then_else (match_operand 1 "cbranch_treg_value")
+			   (label_ref (match_operand 0))
 			   (pc)))]
   "TARGET_SH1"
 {
-  return output_branch (0, insn, operands);
+  return output_branch (sh_eval_treg_value (operands[1]), insn, operands);
 }
-  [(set_attr "type" "cbranch")])
-
-(define_insn "*branch_false_ne"
-  [(set (pc) (if_then_else (ne (match_operand 1 "t_reg_operand" "")
-			       (const_int 1))
-			   (label_ref (match_operand 0 "" ""))
+  "&& can_create_pseudo_p ()"
+  [(set (pc) (if_then_else (eq (reg:SI T_REG) (match_dup 2))
+			   (label_ref (match_dup 0))
 			   (pc)))]
-  "TARGET_SH1"
 {
-  return output_branch (0, insn, operands);
+  /* Try to find missed test and branch combine opportunities which result
+     in redundant T bit tests before conditional branches.
+     FIXME: Probably this would not be needed if CCmode was used
+     together with TARGET_FIXED_CONDITION_CODE_REGS.  */
+
+  const int treg_value = sh_eval_treg_value (operands[1]);
+  operands[2] = NULL_RTX;
+
+  /* Scan the insns backwards for an insn that sets the T bit by testing a
+     reg against zero like:
+	(set (reg T_REG) (eq (reg) (const_int 0)))  */
+  rtx testing_insn = NULL_RTX;
+  rtx tested_reg = NULL_RTX;
+
+  for (rtx i = prev_nonnote_insn_bb (curr_insn); i != NULL_RTX;
+       i = prev_nonnote_insn_bb (i))
+    {
+      if (LABEL_P (i) || BARRIER_P (i))
+	break;
+      if (!NONJUMP_INSN_P (i))
+	continue;
+
+      rtx p = PATTERN (i);
+      if (p != NULL_RTX
+	  && GET_CODE (p) == SET && t_reg_operand (XEXP (p, 0), VOIDmode)
+	  && GET_CODE (XEXP (p, 1)) == EQ
+	  && REG_P (XEXP (XEXP (p, 1), 0))
+	  && satisfies_constraint_Z (XEXP (XEXP (p, 1), 1)))
+	{
+	  testing_insn = i;
+	  tested_reg = XEXP (XEXP (p, 1), 0);
+	  break;
+	}
+    }
+
+  if (testing_insn == NULL_RTX)
+    FAIL;
+
+  /* Continue scanning the insns backwards and try to find the insn that
+     sets the tested reg which we found above.  If the reg is set by storing
+     the T bit or the negated T bit we can eliminate the test insn before
+     the branch.  Notice that the branch condition has to be inverted if the
+     test is eliminated.  */
+
+  /* If the T bit is used between the testing insn and the brach insn
+     leave it alone.  */
+  if (reg_used_between_p (get_t_reg_rtx (), testing_insn, curr_insn))
+    FAIL;
+
+  for (rtx i = prev_nonnote_insn_bb (testing_insn); i != NULL_RTX;
+       i = prev_nonnote_insn_bb (i))
+    {
+      if (LABEL_P (i) || BARRIER_P (i))
+	break;
+      if (!NONJUMP_INSN_P (i))
+	continue;
+
+      if (reg_set_p (tested_reg, i))
+	{
+	  const_rtx tested_reg_set = set_of (tested_reg, i);
+
+	  /* It could also be a clobber...  */
+	  if (tested_reg_set == NULL_RTX || GET_CODE (tested_reg_set) != SET)
+	    break;
+
+	  rtx set_op1 = XEXP (tested_reg_set, 1);
+	  if (t_reg_operand (set_op1, VOIDmode))
+	    operands[2] = GEN_INT (treg_value ^ 1);
+	  else if (negt_reg_operand (set_op1, VOIDmode))
+	    operands[2] = GEN_INT (treg_value);
+	  else if (REG_P (set_op1))
+	    {
+	      /* If it's a reg-reg copy follow the copied reg.  This can
+		 happen e.g. when T bit store zero-extensions are
+		 eliminated.  */
+	      tested_reg = set_op1;
+	      continue;
+	    }
+
+	  /* It's only safe to remove the testing insn if the T bit is not
+	     modified between the testing insn and the insn that stores the
+	     T bit.  Notice that some T bit stores such as negc also modify
+	     the T bit.  */
+	  if (modified_between_p (get_t_reg_rtx (), i, testing_insn)
+	      || modified_in_p (get_t_reg_rtx (), i))
+	    operands[2] = NULL_RTX;
+
+	  break;
+	}
+    }
+
+  if (operands[2] == NULL_RTX)
+    FAIL;
+
+  set_insn_deleted (testing_insn);
 }
   [(set_attr "type" "cbranch")])
 
@@ -10960,26 +11085,54 @@ 
 	(set (reg:SI T_REG) (const_int 1))
 	(use (match_dup 2))])])
 
-;; In some cases the zero extension does not get combined away and a 
-;; sequence like the following might remain:
-;;	mov	#-1,r2
-;;	tst	r1,r1
-;;	negc	r2,r1
-;;	extu.b	r1,r1
-(define_peephole2
-  [(parallel
-       [(set (match_operand:SI 0 "arith_reg_dest" "")
-	     (xor:SI (match_operand:SI 1 "t_reg_operand" "") (const_int 1)))
-	(set (reg:SI T_REG) (const_int 1))
-	(use (match_operand:SI 2 "arith_reg_operand" ""))])
-   (set (match_dup 0)
-	(zero_extend:SI (match_operand 3 "arith_reg_operand" "")))]
-  "TARGET_SH1 && REGNO (operands[0]) == REGNO (operands[3])"
-  [(parallel
-       [(set (match_dup 0) (xor:SI (match_dup 1) (const_int 1)))
-	(set (reg:SI T_REG) (const_int 1))
-	(use (match_dup 2))])])
+;; Store the negated T bit in a reg using r0 and xor.  This one doesn't
+;; clobber the T bit, which is useful when storing the T bit and the
+;; negated T bit in parallel.  On SH2A the movrt insn can be used for that.
+;; Usually we don't want this insn to be matched, except for cases where the
+;; T bit clobber is really not appreciated.  Hence the extra use on T_REG.
+(define_insn_and_split "movrt_xor"
+  [(set (match_operand:SI 0 "arith_reg_dest" "=z")
+	(xor:SI (match_operand:SI 1 "t_reg_operand") (const_int 1)))
+   (use (reg:SI T_REG))]
+  "TARGET_SH1 && !TARGET_SH2A"
+  "#"
+  "&& reload_completed"
+  [(set (match_dup 0) (reg:SI T_REG))
+   (set (match_dup 0) (xor:SI (match_dup 0) (const_int 1)))])
 
+;; Store the T bit and the negated T bit in two regs in parallel.  There is
+;; no real insn to do that, but specifying this pattern will give combine
+;; some opportunities.
+(define_insn_and_split "*movt_movrt"
+  [(parallel [(set (match_operand:SI 0 "arith_reg_dest")
+		   (match_operand:SI 1 "negt_reg_operand"))
+	      (set (match_operand:SI 2 "arith_reg_dest")
+		   (match_operand:SI 3 "t_reg_operand"))])]
+  "TARGET_SH1"
+  "#"
+  "&& 1"
+  [(const_int 0)]
+{
+  rtx i = TARGET_SH2A
+	  ? gen_movrt (operands[0], get_t_reg_rtx ())
+	  : gen_movrt_xor (operands[0], get_t_reg_rtx ());
+  
+  emit_insn (i);
+  emit_insn (gen_movt (operands[2], get_t_reg_rtx ()));
+  DONE;
+})
+
+(define_insn_and_split "*movt_movrt"
+  [(parallel [(set (match_operand:SI 0 "arith_reg_dest")
+		   (match_operand:SI 1 "t_reg_operand"))
+	      (set (match_operand:SI 2 "arith_reg_dest")
+		   (match_operand:SI 3 "negt_reg_operand"))])]
+  "TARGET_SH1"
+  "#"
+  "&& 1"
+  [(parallel [(set (match_dup 2) (match_dup 3))
+	      (set (match_dup 0) (match_dup 1))])])
+
 ;; Use negc to store the T bit in a MSB of a reg in the following way:
 ;;	T = 1: 0x80000000 -> reg
 ;;	T = 0: 0x7FFFFFFF -> reg
@@ -15165,7 +15318,7 @@ 
   else
     {
       emit_insn (gen_stack_protect_test_si (operands[0], operands[1]));
-      emit_jump_insn (gen_branch_true (operands[2], get_t_reg_rtx ()));
+      emit_jump_insn (gen_branch_true (operands[2]));
     }
 
   DONE;
Index: gcc/config/sh/predicates.md
===================================================================
--- gcc/config/sh/predicates.md	(revision 192200)
+++ gcc/config/sh/predicates.md	(working copy)
@@ -1048,6 +1048,14 @@ 
     }
 })
 
+;; A predicate that returns true if OP is a valid construct around the T bit
+;; that can be used as an operand for conditional branches.
+(define_predicate "cbranch_treg_value"
+  (match_code "eq,ne,reg,subreg,xor,sign_extend,zero_extend")
+{
+  return sh_eval_treg_value (op) >= 0;
+})
+
 ;; Returns true of OP is arith_reg_operand or t_reg_operand.
 (define_predicate "arith_reg_or_t_reg_operand"
   (ior (match_operand 0 "arith_reg_operand")
Index: gcc/config/sh/sh-protos.h
===================================================================
--- gcc/config/sh/sh-protos.h	(revision 192200)
+++ gcc/config/sh/sh-protos.h	(working copy)
@@ -162,6 +162,7 @@ 
 extern void sh_canonicalize_comparison (enum rtx_code&, rtx&, rtx&,
 					enum machine_mode mode = VOIDmode);
 extern rtx sh_find_equiv_gbr_addr (rtx cur_insn, rtx mem);
+extern int sh_eval_treg_value (rtx op);
 #endif /* RTX_CODE */
 
 extern void sh_cpu_cpp_builtins (cpp_reader* pfile);
Index: gcc/testsuite/gcc.target/sh/pr51244-14.c
===================================================================
--- gcc/testsuite/gcc.target/sh/pr51244-14.c	(revision 0)
+++ gcc/testsuite/gcc.target/sh/pr51244-14.c	(revision 0)
@@ -0,0 +1,107 @@ 
+/* This is a case extracted from CSiBE which would sometimes contain the
+   following sequence:
+	cmp/eq	r12,r13
+	movt	r0
+	xor	#1,r0
+	extu.b	r0,r0
+	movt	r3
+	tst	r0,r0
+	bf/s	.L35
+   where the negated T bit store did not combine properly.  Since there are
+   other movt insns we only check for the xor and the extu.  */
+/* { dg-do compile { target "sh*-*-*" } } */
+/* { dg-options "-O2" } */
+/* { dg-skip-if "" { "sh*-*-*" } { "-m5*" } { "" } } */
+/* { dg-final { scan-assembler-not "xor|extu" } } */
+
+typedef struct transaction_s transaction_t;
+
+struct journal_head
+{
+  transaction_t * b_transaction;
+  struct journal_head *b_cpnext, *b_cpprev;
+};
+
+struct transaction_s
+{
+  struct journal_head * t_checkpoint_list;
+  transaction_t *t_cpnext, *t_cpprev;
+};
+
+struct journal_s
+{
+  transaction_t * j_checkpoint_transactions;
+  unsigned long j_first, j_last;
+};
+
+typedef struct journal_s journal_t;
+
+extern int __try_to_free_cp_buf (struct journal_head *jh);
+extern int __cleanup_transaction (journal_t *journal, transaction_t *transaction);
+extern void __flush_batch (void **bhs, int *batch_count);
+extern void* jh2bh (void*);
+
+static int
+__flush_buffer (journal_t *journal, struct journal_head *jh,
+		void **bhs, int *batch_count, int *drop_count)
+{
+  void *bh = jh2bh (jh);
+  int ret = 0;
+  if (bh)
+    {
+      bhs[*batch_count] = bh;
+      (*batch_count)++;
+      if (*batch_count == 64)
+	  ret = 1;
+    }
+  else
+    {
+      int last_buffer = 0;
+      if (jh->b_cpnext == jh)
+	last_buffer = 1;
+      if (__try_to_free_cp_buf (jh))
+	{
+	  (*drop_count)++;
+	  ret = last_buffer;
+	}
+    }
+  return ret;
+}
+
+int
+log_do_checkpoint (journal_t *journal, int nblocks)
+{
+  transaction_t *transaction, *last_transaction, *next_transaction;
+  int batch_count = 0;
+  void *bhs[64];
+
+repeat:
+  transaction = journal->j_checkpoint_transactions;
+  if (transaction == ((void *)0))
+    return 0;
+  last_transaction = transaction->t_cpprev;
+  next_transaction = transaction;
+  do
+    {
+      struct journal_head *jh, *last_jh, *next_jh;
+      int drop_count = 0;
+      int cleanup_ret, retry = 0;
+      transaction = next_transaction;
+      next_transaction = transaction->t_cpnext;
+      jh = transaction->t_checkpoint_list;
+      last_jh = jh->b_cpprev;
+      next_jh = jh;
+      do
+	{
+	  jh = next_jh;
+	  next_jh = jh->b_cpnext;
+	  retry = __flush_buffer(journal, jh, bhs, &batch_count, &drop_count);
+	} while (jh != last_jh && !retry);
+
+      if (retry)
+	goto repeat;
+
+      cleanup_ret = __cleanup_transaction(journal, transaction);
+      goto repeat;
+    } while (transaction != last_transaction);
+}
Index: gcc/testsuite/gcc.target/sh/pr51244-16.c
===================================================================
--- gcc/testsuite/gcc.target/sh/pr51244-16.c	(revision 0)
+++ gcc/testsuite/gcc.target/sh/pr51244-16.c	(revision 0)
@@ -0,0 +1,11 @@ 
+/* Check that the redundant test removal code in the *cbranch_t split works
+   as expected on SH2A targets.  */
+/* { dg-do compile { target "sh*-*-*" } } */
+/* { dg-options "-O2" } */
+/* { dg-skip-if "" { "sh*-*-*" } { "*" } { "-m2a*" } } */
+/* { dg-final { scan-assembler-times "tst" 6 } } */
+/* { dg-final { scan-assembler-times "movt" 3 } } */
+/* { dg-final { scan-assembler-times "movrt" 3 } } */
+/* { dg-final { scan-assembler-not "extu|exts|negc" } } */
+
+#include "pr51244-15.c"
Index: gcc/testsuite/gcc.target/sh/pr51244-13.c
===================================================================
--- gcc/testsuite/gcc.target/sh/pr51244-13.c	(revision 0)
+++ gcc/testsuite/gcc.target/sh/pr51244-13.c	(revision 0)
@@ -0,0 +1,85 @@ 
+/* This is a case extracted from CSiBE which contained the following
+   sequence:
+	shll	r0
+	movt	r0
+	tst	r0,r0
+	bf	.L11
+   where the 'tst r0,r0' before the branch can be omitted by inverting the
+   branch condition.  The tested function contains two other tst insns.  If
+   everything goes as expected we will be seeing only those other two tst
+   insns.  */
+/* { dg-do compile { target "sh*-*-*" } } */
+/* { dg-options "-O2" } */
+/* { dg-skip-if "" { "sh*-*-*" } { "-m5*" } { "" } } */
+/* { dg-final { scan-assembler-times "tst" 2 } } */
+
+static __inline__ int
+__test_bit (unsigned long nr, volatile void * addr)
+{
+  /*  This is on purpose.  */
+  int oldbit;
+  return oldbit & 1;
+}
+
+static __inline__ int
+__constant_test_bit (unsigned long nr, volatile void * addr)
+{
+  return (((volatile char *) addr)[(nr>>3)^7] & (1<<(nr&7))) != 0;
+}
+
+struct list_head
+{
+  struct list_head *next, *prev;
+};
+
+static inline void
+__list_del (struct list_head *prev, struct list_head *next)
+{
+  next->prev = prev;
+  prev->next = next;
+}
+
+static inline void
+list_del (struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+ entry->next = 0;
+ entry->prev = 0;
+}
+
+extern int nr_active_pages;
+extern int nr_inactive_pages;
+extern struct list_head active_list;
+
+typedef struct page
+{
+ unsigned long flags;
+ struct list_head lru;
+} mem_map_t;
+
+void
+activate_page_nolock (struct page * page)
+{
+ if ((__builtin_constant_p((6))
+      ? __constant_test_bit((6),(&(page)->flags))
+      : __test_bit((6),(&(page)->flags)) )
+     && !(__builtin_constant_p((7))
+          ? __constant_test_bit((7),(&(page)->flags))
+          : __test_bit((7),(&(page)->flags)) ))
+    {
+      list_del(&(page)->lru);
+      nr_inactive_pages--;
+      if (!(__builtin_constant_p(6) ? __constant_test_bit((6),(&(page)->flags))
+				    : __test_bit((6),(&(page)->flags))))
+	printk("", "", 43);
+
+      if ((__builtin_constant_p(7) ? __constant_test_bit((7),(&(page)->flags))
+				   : __test_bit((7),(&(page)->flags))))
+	printk("", "", 43);
+
+      (__builtin_constant_p(7) ? __constant_set_bit((7),(&(page)->flags))
+			        : __set_bit((7),(&(page)->flags)) );
+      list_add(&(page)->lru, &active_list);
+      nr_active_pages++;
+    }
+}
Index: gcc/testsuite/gcc.target/sh/pr51244-15.c
===================================================================
--- gcc/testsuite/gcc.target/sh/pr51244-15.c	(revision 0)
+++ gcc/testsuite/gcc.target/sh/pr51244-15.c	(revision 0)
@@ -0,0 +1,71 @@ 
+/* Check that the redundant test removal code in the *cbranch_t split works
+   as expected on non-SH2A targets.  Because on SH2A the movrt instruction
+   is used, this test is re-used and checked differently in pr51244-16.c.  */
+/* { dg-do compile { target "sh*-*-*" } } */
+/* { dg-options "-O2" } */
+/* { dg-skip-if "" { "sh*-*-*" } { "-m5*" "-m2a*" } { "" } } */
+/* { dg-final { scan-assembler-times "tst" 6 } } */
+/* { dg-final { scan-assembler-times "movt" 6 } } */
+/* { dg-final { scan-assembler-times "xor" 3 } } */
+/* { dg-final { scan-assembler-not "extu|exts|negc" } } */
+
+typedef char bool;
+
+int
+test_0 (int a, int b, int c, int* d)
+{
+  /* non SH2A: 1x tst, 1x movt, 1x xor
+         SH2A: 1x tst, 1x movrt  */
+  bool x = a == 0;
+  d[2] = !x;
+  return x ? b : c;
+}
+
+int
+test_1 (int a, int b, int c, int* d)
+{
+  /* 1x tst, 1x movt  */
+  bool x = a != 0;
+  d[2] = !x;
+  return x ? b : c;
+}
+
+int
+test_2 (int a, int b, int c, char* d)
+{
+  /* Check that there is no sign/zero-extension before the store.
+     non SH2A: 1x tst, 1x movt, 1x xor
+         SH2A: 1x tst, 1x movrt  */
+  bool x = a == 0;
+  d[2] = !x;
+  return x ? b : c;
+}
+
+int
+test_3 (int a, int b, int c, char* d)
+{
+  /* Check that there is no sign/zero-extension before the store.
+     1x tst, 1x movt  */
+  bool x = a != 0;
+  d[2] = !x;
+  return x ? b : c;
+}
+
+int
+test_4 (int a, int b, int c, char* d)
+{
+  /* 1x tst, 1x movt  */
+  bool x = a != 0;
+  d[2] = !x;
+  return !x ? b : c;
+}
+
+int
+test_5 (int a, int b, int c, char* d)
+{
+  /* non SH2A: 1x tst, 1x movt, 1x xor
+         SH2A: 1x tst, 1x movrt  */
+  bool x = a == 0;
+  d[2] = !x;
+  return !x ? b : c;
+}