This is the mail archive of the gcc@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

scheduling cc0


[The following refers to gcc 3.3.1]

Hi,

I'm adding support for a v850 variant with an instruction that takes
some cycles to set cc0 (normally cc0 results are available immediately).

Normal gcc doesn't allow cc0-using instruction pairs to be scheduled
independently, tightly binding the setter to the user; I guess this
maybe makes sense on many old processors where almost every instruction
affected cc0, but on the v850, many instructions don't (including load,
store, move, etc, and floating-point instructions).

So I added support for scheduling cc0-setters independently from the
cc0-user, using the following method.

  (0) Add a target hook, `targetm.sched.insn_clobbers_cc0' so that the
      target can communicate which instructions are dangerous to cc0 (by
      default, if this hook isn't defined, it uses the old "bind tightly
      together" method).

  (1) If the above hook is defined, _don't_ bind cc0-setters and users
      together when scheduling.

  (2) In the flow-analysis code (sched-deps.c, flow.c) use a fake hard
      register whenever (cc0) is seen, as if it were a normal variable.
      The RTL for this fake register is stored in a global variable
      called `cc0_dep_reg_rtx'.

      This change was very simple, usually just adding a bit of code of
      the form:

        if (GET_CODE (something) == CC0)
          something = cc0_dep_reg_rtx; /* change into a REG */

      in various places in the flow-analysis.  cc0_dep_reg_rtx never
      ever shows up in any actual RTL expression, it's basically just
      used so that its REGNO value can be added to register bitmaps &c.

      This fake hard register must be defined by the target's .h file,
      using the name CC0_DEP_REGNUM -- it would be nice to not require
      that, but to allow this trick to work in the second scheduling
      pass, it seemed that it must be a hard register, and that
      namespace is controlled defined by the target.

  (3) Fixing some places where it's assumed that cc0-setter and user are
      adjacent (e.g., generalizing prev_cc0_setter to scan back past
      multiple instructions looking for it, aborting if an intervening
      cc0-clobbering insn is found).

This approach seems to work well in practice, but can anyone see any
potential problems with it?  Is this a reasonable way to do things?

Thanks,

-Miles


Here's the patch:


diff -ruN -X../diff.xcl gcc-3.3.1-nms-20031017/gcc/emit-rtl.c gcc-3.3.1-nms-20031017-v850e2f-float-20050202/gcc/emit-rtl.c
--- gcc-3.3.1-nms-20031017/gcc/emit-rtl.c	2003-08-26 18:05:37.000000000 +0900
+++ gcc-3.3.1-nms-20031017-v850e2f-float-20050202/gcc/emit-rtl.c	2005-02-02 11:06:50.913143000 +0900
@@ -55,6 +55,7 @@
 #include "ggc.h"
 #include "debug.h"
 #include "langhooks.h"
+#include "target.h"
 
 /* Commonly used modes.  */
 
@@ -3134,18 +3135,28 @@
   if (note)
     return XEXP (note, 0);
 
-  insn = next_nonnote_insn (insn);
-  if (insn && GET_CODE (insn) == INSN && GET_CODE (PATTERN (insn)) == SEQUENCE)
-    insn = XVECEXP (PATTERN (insn), 0, 0);
+  while (insn)
+    {
+      insn = next_nonnote_insn (insn);
+
+      /* Descend into sequences.  */
+      if (insn
+	  && GET_CODE (insn) == INSN && GET_CODE (PATTERN (insn)) == SEQUENCE)
+	insn = XVECEXP (PATTERN (insn), 0, 0);
 
-  if (insn && INSN_P (insn) && reg_mentioned_p (cc0_rtx, PATTERN (insn)))
-    return insn;
+      if (insn && INSN_P (insn) && reg_mentioned_p (cc0_rtx, PATTERN (insn)))
+	return insn;
+      else if (! targetm.sched.insn_clobbers_cc0
+	       || (* targetm.sched.insn_clobbers_cc0) (insn))
+	break;
+    }
 
   return 0;
 }
 
 /* Find the insn that set CC0 for INSN.  Unless INSN has a REG_CC_SETTER
-   note, it is the previous insn.  */
+   note, it is the previous insn which sets CC0 (an intervening instruction
+   which may clobber CC0 is regarded as a compiler bug).  */
 
 rtx
 prev_cc0_setter (insn)
@@ -3156,11 +3167,22 @@
   if (note)
     return XEXP (note, 0);
 
-  insn = prev_nonnote_insn (insn);
-  if (! sets_cc0_p (PATTERN (insn)))
-    abort ();
+  for (;;)
+    {
+      insn = prev_nonnote_insn (insn);
 
-  return insn;
+      if (! insn)
+	break;
+
+      if (sets_cc0_p (PATTERN (insn)))
+	return insn;
+
+      if (! targetm.sched.insn_clobbers_cc0
+	  || (* targetm.sched.insn_clobbers_cc0) (insn))
+	break;
+    }
+
+  abort ();
 }
 #endif
 
diff -ruN -X../diff.xcl gcc-3.3.1-nms-20031017/gcc/final.c gcc-3.3.1-nms-20031017-v850e2f-float-20050202/gcc/final.c
--- gcc-3.3.1-nms-20031017/gcc/final.c	2003-07-17 18:12:12.000000000 +0900
+++ gcc-3.3.1-nms-20031017-v850e2f-float-20050202/gcc/final.c	2005-02-01 20:03:00.641233000 +0900
@@ -2763,7 +2763,7 @@
 	  {
 	    rtx prev;
 
-	    if (prev_nonnote_insn (insn) != last_ignored_compare)
+	    if (prev_cc0_setter (insn) != last_ignored_compare)
 	      abort ();
 	    new_block = 0;
 
diff -ruN -X../diff.xcl gcc-3.3.1-nms-20031017/gcc/flow.c gcc-3.3.1-nms-20031017-v850e2f-float-20050202/gcc/flow.c
--- gcc-3.3.1-nms-20031017/gcc/flow.c	2003-08-26 18:05:51.000000000 +0900
+++ gcc-3.3.1-nms-20031017-v850e2f-float-20050202/gcc/flow.c	2005-02-02 11:07:37.542742000 +0900
@@ -136,6 +136,7 @@
 #include "expr.h"
 #include "ssa.h"
 #include "timevar.h"
+#include "target.h"
 
 #include "obstack.h"
 #include "splay-tree.h"
@@ -2160,7 +2161,14 @@
 
 #ifdef HAVE_cc0
       if (GET_CODE (r) == CC0)
-	return ! pbi->cc0_live;
+	{
+	  /* For simple cc0 tracking, just use our current state.  */
+	  if (! cc0_dep_reg_rtx)
+	    return ! pbi->cc0_live;
+
+	  /* Otherwise pretend it's a register.  */
+	  r = cc0_dep_reg_rtx;
+	}
 #endif
 
       /* A SET that is a subroutine call cannot be dead.  */
@@ -2518,6 +2526,9 @@
   rtx cond = NULL_RTX;
   rtx link;
   enum rtx_code code;
+#ifdef HAVE_cc0
+  int set_cc0 = 0;
+#endif
 
   if (insn)
     for (link = REG_NOTES (insn); link; link = XEXP (link, 1))
@@ -2533,6 +2544,10 @@
     {
     case SET:
     case CLOBBER:
+#ifdef HAVE_cc0
+      if (SET_DEST (x) == cc0_rtx)
+	set_cc0 = 1;
+#endif
       mark_set_1 (pbi, code, SET_DEST (x), cond, insn, pbi->flags);
       return;
 
@@ -2562,6 +2577,10 @@
 
 	      case SET:
 	      case CLOBBER:
+#ifdef HAVE_cc0
+		if (SET_DEST (sub) == cc0_rtx)
+		  set_cc0 = 1;
+#endif
 		mark_set_1 (pbi, code, SET_DEST (sub), cond, insn, pbi->flags);
 		break;
 
@@ -2575,6 +2594,20 @@
     default:
       break;
     }
+
+#ifdef HAVE_cc0
+  if (!set_cc0 && cc0_dep_reg_rtx)
+    {
+      /* If cc0_dep_reg_rtx is non-zero, we must be able to detect
+	 cc0-clobbering insns.  */
+      if (! targetm.sched.insn_clobbers_cc0)
+	abort ();
+
+      if ((* targetm.sched.insn_clobbers_cc0) (insn))
+	/* INSN clobbers cc0.  */
+	mark_set_1 (pbi, CLOBBER, cc0_dep_reg_rtx, NULL_RTX, insn, pbi->flags);
+    }
+#endif
 }
 
 /* Process a single set, which appears in INSN.  REG (which may not
@@ -3772,7 +3805,14 @@
 
 #ifdef HAVE_cc0
     case CC0:
+      /* By default we do simple cc0 tracking where cc0 is always consumed
+	 by the following instruction.  */
       pbi->cc0_live = 1;
+
+      /* If enabled, generate cc0 dependencies with a fake register.  */
+      if (cc0_dep_reg_rtx)
+	mark_used_reg (pbi, cc0_dep_reg_rtx, cond, insn);
+
       return;
 #endif
 
diff -ruN -X../diff.xcl gcc-3.3.1-nms-20031017/gcc/regs.h gcc-3.3.1-nms-20031017-v850e2f-float-20050202/gcc/regs.h
--- gcc-3.3.1-nms-20031017/gcc/regs.h	2003-07-17 18:12:28.000000000 +0900
+++ gcc-3.3.1-nms-20031017-v850e2f-float-20050202/gcc/regs.h	2005-02-01 19:46:00.319451000 +0900
@@ -21,6 +21,7 @@
 
 
 #include "varray.h"
+#include "insn-config.h"
 #include "hard-reg-set.h"
 #include "basic-block.h"
 
@@ -68,6 +69,14 @@
 
 extern bitmap_head subregs_of_mode;
 
+#ifdef HAVE_cc0
+/* This is a register used for dependencies involving cc0 (so it can have a
+   register number to be used in bitmaps, etc).  It does not actually occur
+   in RTL anywhere (cc0_rtx is always used instead).  */
+
+extern rtx cc0_dep_reg_rtx;
+#endif /* HAVE_cc0 */
+
 /* Indexed by n, gives number of times (REG n) is used or set.  */
 
 #define REG_N_REFS(N) (VARRAY_REG (reg_n_info, N)->refs)
diff -ruN -X../diff.xcl gcc-3.3.1-nms-20031017/gcc/sched-deps.c gcc-3.3.1-nms-20031017-v850e2f-float-20050202/gcc/sched-deps.c
--- gcc-3.3.1-nms-20031017/gcc/sched-deps.c	2003-09-16 19:51:35.000000000 +0900
+++ gcc-3.3.1-nms-20031017-v850e2f-float-20050202/gcc/sched-deps.c	2005-02-02 14:09:33.366515000 +0900
@@ -40,6 +40,7 @@
 #include "sched-int.h"
 #include "params.h"
 #include "cselib.h"
+#include "target.h"
 
 extern char *reg_known_equiv_p;
 extern rtx *reg_known_value;
@@ -589,6 +590,11 @@
       dest = XEXP (dest, 0);
     }
 
+#ifdef HAVE_cc0
+  if (GET_CODE (dest) == CC0)
+    dest = cc0_dep_reg_rtx;
+#endif
+
   if (GET_CODE (dest) == REG)
     {
       regno = REGNO (dest);
@@ -733,14 +739,23 @@
 
 #ifdef HAVE_cc0
     case CC0:
-      /* User of CC0 depends on immediately preceding insn.  */
-      set_sched_group_p (insn);
-      /* Don't move CC0 setter or user to another block (it can
-         set up the same flag for previous CC0 users which is safe).  */
-      CANT_MOVE (insn) = 1;
-      CANT_MOVE (prev_nonnote_insn (insn)) = 1;
-      return;
-#endif
+      if (! cc0_dep_reg_rtx)
+	/* User of CC0 depends on immediately preceding insn.  */
+	{
+	  set_sched_group_p (insn);
+	  /* Don't move CC0 setter or user to another block (it can
+	     set up the same flag for previous CC0 users which is safe).  */
+	  CANT_MOVE (prev_nonnote_insn (insn)) = 1;
+	  CANT_MOVE (insn) = 1;
+	  return;
+	}
+
+      /* Pretend cc0 is a register.  */
+      x = cc0_dep_reg_rtx;
+
+      /* fall through */
+      
+#endif /* HAVE_cc0 */
 
     case REG:
       {
@@ -958,6 +973,23 @@
   else
     sched_analyze_2 (deps, x, insn);
 
+#ifdef HAVE_cc0
+  /* If we're recording explicit dependencies for cc0, remember any insn
+     that may clobber it.  */
+  if (targetm.sched.insn_clobbers_cc0
+      && (* targetm.sched.insn_clobbers_cc0) (insn))
+    {
+      int regno = REGNO (cc0_dep_reg_rtx);
+
+      SET_REGNO_REG_SET (reg_pending_clobbers, regno);
+
+      /* Don't let it cross a call after scheduling if it doesn't
+	 already cross one.  */
+      if (REG_N_CALLS_CROSSED (regno) == 0)
+	add_dependence_list (insn, deps->last_function_call, REG_DEP_ANTI);
+    }
+#endif /* HAVE_cc0 */
+
   /* Mark registers CLOBBERED or used by called function.  */
   if (GET_CODE (insn) == CALL_INSN)
     {
diff -ruN -X../diff.xcl gcc-3.3.1-nms-20031017/gcc/sched-rgn.c gcc-3.3.1-nms-20031017-v850e2f-float-20050202/gcc/sched-rgn.c
--- gcc-3.3.1-nms-20031017/gcc/sched-rgn.c	2003-08-26 18:07:08.000000000 +0900
+++ gcc-3.3.1-nms-20031017-v850e2f-float-20050202/gcc/sched-rgn.c	2005-02-02 11:09:28.901828000 +0900
@@ -2349,7 +2349,10 @@
 		 || GET_CODE (PATTERN (insn)) == CLOBBER
 		 || can_throw_internal (insn)
 #ifdef HAVE_cc0
-		 || sets_cc0_p (PATTERN (insn))
+		 || (sets_cc0_p (PATTERN (insn))
+		     /* We only need to worry about cc0-setters if we're
+			not explicitly tracking dependencies for cc0.  */
+		     && !targetm.sched.insn_clobbers_cc0)
 #endif
 		 || (!reload_completed
 		     && sets_likely_spilled (PATTERN (insn)))))
diff -ruN -X../diff.xcl gcc-3.3.1-nms-20031017/gcc/target-def.h gcc-3.3.1-nms-20031017-v850e2f-float-20050202/gcc/target-def.h
--- gcc-3.3.1-nms-20031017/gcc/target-def.h	2003-07-17 18:12:35.000000000 +0900
+++ gcc-3.3.1-nms-20031017-v850e2f-float-20050202/gcc/target-def.h	2005-02-02 11:09:40.371734000 +0900
@@ -198,6 +198,7 @@
 #define TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD 0
 #define TARGET_SCHED_INIT_DFA_BUBBLES 0
 #define TARGET_SCHED_DFA_BUBBLE 0
+#define TARGET_SCHED_INSN_CLOBBERS_CC0 0
 
 #define TARGET_SCHED						\
   {TARGET_SCHED_ADJUST_COST,					\
@@ -215,7 +216,8 @@
    TARGET_SCHED_DFA_POST_CYCLE_INSN,				\
    TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD,		\
    TARGET_SCHED_INIT_DFA_BUBBLES,				\
-   TARGET_SCHED_DFA_BUBBLE}
+   TARGET_SCHED_DFA_BUBBLE,					\
+   TARGET_SCHED_INSN_CLOBBERS_CC0}
 
 /* All in tree.c.  */
 #define TARGET_MERGE_DECL_ATTRIBUTES merge_decl_attributes
diff -ruN -X../diff.xcl gcc-3.3.1-nms-20031017/gcc/target.h gcc-3.3.1-nms-20031017-v850e2f-float-20050202/gcc/target.h
--- gcc-3.3.1-nms-20031017/gcc/target.h	2003-07-17 18:12:35.000000000 +0900
+++ gcc-3.3.1-nms-20031017-v850e2f-float-20050202/gcc/target.h	2005-02-02 14:34:36.185481000 +0900
@@ -215,6 +215,29 @@
        scheduling.  */
     void (* init_dfa_bubbles) PARAMS ((void));
     rtx (* dfa_bubble) PARAMS ((int));
+
+    /* If non-zero, this hook is called with an instruction, and should
+       return non-zero if INSN _may_ change cc0 (when not sure, return 1).
+       This information can be used during scheduling to schedule
+       instruction pairs communicating via cc0 more flexibly.  If this
+       hook is not provided, such instruction pairs are always rigidly
+       bound together for scheduling.
+
+       Note that if this hook _is_ provided, explicit dependencies are
+       used to record any potential conflicts, so it is less efficient
+       than the default case.  As such, it is mostly useful for cc0-using
+       architectures where many instructions don't clobber cc0, and so
+       provide a measure of scheduling flexibility in such cases; on
+       architectures where most instructions may change cc0, it may be
+       best to simply not define it.
+
+       Along with this hook, the target must define CC0_DEP_REGNUM to be
+       the number of a psuedo-hard-register used to represent cc0 when
+       calculating instruction dependencies; CC0_DEP_REGNUM will never
+       occur in real RTL, it is just used for internal bookkeeping (it is
+       defined in the backend because for various reaons it can't be a
+       pseudo-register).  */
+    int (* insn_clobbers_cc0) PARAMS ((rtx insn));
   } sched;
 
   /* Given two decls, merge their attributes and return the result.  */
diff -ruN -X../diff.xcl gcc-3.3.1-nms-20031017/gcc/toplev.c gcc-3.3.1-nms-20031017-v850e2f-float-20050202/gcc/toplev.c
--- gcc-3.3.1-nms-20031017/gcc/toplev.c	2003-08-26 18:07:20.000000000 +0900
+++ gcc-3.3.1-nms-20031017-v850e2f-float-20050202/gcc/toplev.c	2005-02-02 14:10:17.496182000 +0900
@@ -2349,6 +2349,10 @@
   timevar_pop (TV_SYMOUT);
 }
 
+#ifdef HAVE_cc0
+rtx cc0_dep_reg_rtx = 0;
+#endif
+
 /* This is called from finish_function (within langhooks.parse_file)
    after each top-level definition is parsed.
    It is supposed to compile that function or variable
@@ -2365,6 +2369,15 @@
   int rebuild_label_notes_after_reload;
   int register_life_up_to_date;
 
+#if defined (HAVE_cc0) && defined (CC0_DEP_REGNUM)
+  /* If the target can tell us which instructions may clobber cc0, we
+     explicitly track dependencies for it using the fake register pointed
+     to by cc0_dep_reg_rtx.  */
+
+  if (targetm.sched.insn_clobbers_cc0 && !cc0_dep_reg_rtx)
+    cc0_dep_reg_rtx = gen_raw_REG (SImode, CC0_DEP_REGNUM);
+#endif
+
   timevar_push (TV_REST_OF_COMPILATION);
 
   /* Now that we're out of the frontend, we shouldn't have any more



-- 
Ich bin ein Virus. Mach' mit und kopiere mich in Deine .signature.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]