diff mbox

[SH,committed] Improve utilization of zero-displacement conditional branches

Message ID 1462585972.3864.24.camel@t-online.de
State New
Headers show

Commit Message

Oleg Endo May 7, 2016, 1:52 a.m. UTC
Hi,

On SH a conditional branch with a (physical) zero displacement jumps
over the next instruction.  On some SH hardware implementations these
branches are handled in a special way which allows using it for
conditional execution.  A while ago I've added some hardcoded asm
patterns to utilize this.  It seems there has been an attempt to do
something about it a long time ago with the "branch_zero" attribute. 
 However I could not find any uses thereof and wasn't sure how to
utilize it.

The attached patch disables the delay slot (for the DBR pass) of
conditional branches which branch over only one 2-byte instruction. 
 This results in more zero-displacement branches to be emitted.

Having this in place, it would also be possible to simplify those
hardcoded asm patterns (e.g. SH abs patterns).  However, while that
works, doing so exposes the actual CFG to other passes and in quite
some cases basic blocks get reordered in a very counterproductive way
resulting in worse code.

Tested on sh-elf with
make -k check RUNTESTFLAGS="--target_board=sh-sim\{-m2/-ml,-m2/-mb,
-m2a/-mb,-m4/-ml,-m4/-mb,-m4a/-ml,-m4a/-mb}"

Committed as r235993.

Cheers,
Oleg

gcc/ChangeLog:
	* config/sh/sh-protos.h (sh_cbranch_distance): Declare new function.
	* config/sh/sh.c (sh_cbranch_distance): Implement it.
	* config/sh/sh.md (branch_zero): Remove define_attr.
	(define_delay): Disable delay slot if branch distance is one insn.
diff mbox

Patch

diff --git a/gcc/config/sh/sh-protos.h b/gcc/config/sh/sh-protos.h
index c47e2ea..d302394 100644
--- a/gcc/config/sh/sh-protos.h
+++ b/gcc/config/sh/sh-protos.h
@@ -348,6 +348,18 @@  private:
 
 extern sh_treg_insns sh_split_treg_set_expr (rtx x, rtx_insn* curr_insn);
 
+enum
+{
+  /* An effective conditional branch distance of zero bytes is impossible.
+     Hence we can use it to designate an unknown value.  */
+  unknown_cbranch_distance = 0u,
+  infinite_cbranch_distance = ~0u
+};
+
+unsigned int
+sh_cbranch_distance (rtx_insn* cbranch_insn,
+		     unsigned int max_dist = infinite_cbranch_distance);
+
 #endif /* RTX_CODE */
 
 extern void sh_cpu_cpp_builtins (cpp_reader* pfile);
diff --git a/gcc/config/sh/sh.c b/gcc/config/sh/sh.c
index 809f679..6d1d1a3 100644
--- a/gcc/config/sh/sh.c
+++ b/gcc/config/sh/sh.c
@@ -1928,6 +1928,52 @@  sh_fixed_condition_code_regs (unsigned int* p1, unsigned int* p2)
   return true;
 }
 
+/* Try to calculate the branch distance of a conditional branch in bytes.
+
+   FIXME: Because of PR 59189 we can't use the CFG here.  Instead just
+   walk from this insn into the next (fall-through) basic block and see if
+   we hit the label.  */
+unsigned int
+sh_cbranch_distance (rtx_insn* _cbranch_insn, unsigned int max_dist)
+{
+  rtx_jump_insn* cbranch_insn = safe_as_a<rtx_jump_insn*> (_cbranch_insn);
+
+  if (dump_file)
+    {
+      fprintf (dump_file, "sh_cbranch_distance insn = \n");
+      print_rtl_single (dump_file, cbranch_insn);
+    }
+
+  unsigned int dist = 0;
+
+  for (rtx_insn* i = next_nonnote_insn (cbranch_insn);
+       i != NULL && dist < max_dist; i = next_nonnote_insn (i))
+    {
+      const unsigned int i_len = get_attr_length (i);
+      dist += i_len;
+
+      if (dump_file)
+	fprintf (dump_file, "  insn %d  length = %u  dist = %u\n",
+		 INSN_UID (i), i_len, dist);
+
+      if (rtx_code_label* l = dyn_cast<rtx_code_label*> (i))
+	{
+	  if (l == cbranch_insn->jump_target ())
+	    {
+	      if (dump_file)
+		fprintf (dump_file, "  cbranch dist = %u\n", dist);
+	      return dist;
+	    }
+	  break;
+	}
+    }
+
+  if (dump_file)
+    fprintf (dump_file, "  cbranch dist = unknown\n");
+
+  return unknown_cbranch_distance;
+}
+
 enum rtx_code
 prepare_cbranch_operands (rtx *operands, machine_mode mode,
 			  enum rtx_code comparison)
diff --git a/gcc/config/sh/sh.md b/gcc/config/sh/sh.md
index 39270ce..406721d 100644
--- a/gcc/config/sh/sh.md
+++ b/gcc/config/sh/sh.md
@@ -477,16 +477,6 @@ 
 (define_attr "is_sfunc" ""
   (if_then_else (eq_attr "type" "sfunc") (const_int 1) (const_int 0)))
 
-(define_attr "branch_zero" "yes,no"
-  (cond [(eq_attr "type" "!cbranch") (const_string "no")
-	 (ne (symbol_ref "(next_active_insn (insn)\
-			   == (prev_active_insn\
-			       (XEXP (SET_SRC (PATTERN (insn)), 1))))\
-			  && get_attr_length (next_active_insn (insn)) == 2")
-	     (const_int 0))
-	 (const_string "yes")]
-	(const_string "no")))
-
 ;; SH4 Double-precision computation with double-precision result -
 ;; the two halves are ready at different times.
 (define_attr "dfp_comp" "yes,no"
@@ -539,8 +529,13 @@ 
 	(eq_attr "type" "!pstore,prget")) (nil) (nil)])
 
 ;; Conditional branches with delay slots are available starting with SH2.
+;; If zero displacement conditional branches are fast, disable the delay
+;; slot if the branch jumps over only one 2-byte insn.
 (define_delay
-  (and (eq_attr "type" "cbranch") (match_test "TARGET_SH2"))
+  (and (eq_attr "type" "cbranch")
+       (match_test "TARGET_SH2")
+       (not (and (match_test "TARGET_ZDCBRANCH")
+		 (match_test "sh_cbranch_distance (insn, 4) == 2"))))
   [(eq_attr "cond_delay_slot" "yes") (nil) (nil)])
 
 ;; -------------------------------------------------------------------------