Support scheduling across extended basic blocks
authorBernd Schmidt <bernds@redhat.com>
Wed, 20 Dec 2000 17:19:39 +0000 (17:19 +0000)
committerBernd Schmidt <bernds@gcc.gnu.org>
Wed, 20 Dec 2000 17:19:39 +0000 (17:19 +0000)
From-SVN: r38400

gcc/ChangeLog
gcc/Makefile.in
gcc/haifa-sched.c
gcc/rtl.h
gcc/sched-deps.c
gcc/sched-ebb.c [new file with mode: 0644]
gcc/sched-int.h
gcc/sched-rgn.c
gcc/sched-vis.c

index 10f00cb..1f19a80 100644 (file)
@@ -1,5 +1,22 @@
 2000-12-20  Bernd Schmidt  <bernds@redhat.com>
 
+       * Makefile.in (OBJS): Add sched-ebb.o.
+       (sched-ebb.o): New rule.
+       (sched-vis.o): Depend on hard-reg-set.h and $(BASIC_BLOCK_H).
+       (haifa-sched.o): Depend on insn-flags.h.
+       * haifa-sched.c: Include "insn-flags.h".
+       (priority): Don't access BLOCK_NUM, use the new contributes_to_priority
+       callback.
+       * rtl.h (schedule_ebbs): Declare.
+       * sched-int.h (struct sched_info): Add new members
+       contributes_to_priority and compute_jump_reg_dependencies.
+       * sched-rgn.c (contributes_to_priority, compute_jump_reg_dependencies):
+       New functions.
+       (region_sched_info): Add them.
+       * sched-vis.c: Include "hard-reg-set.h" and "basic-block.h".
+       * sched-ebb.c: New file.
+       * sched-deps.c (sched_analyze_insn): Add code to handle JUMP_INSNs.
+
        * flow.c (ior_reg_cond, and_reg_cond, elim_reg_cond): Properly
        handle all relational operators.
 
index eadd5c4..1f6e925 100644 (file)
@@ -738,7 +738,7 @@ OBJS = diagnostic.o version.o tree.o print-tree.o stor-layout.o fold-const.o  \
  mbchar.o splay-tree.o graph.o sbitmap.o resource.o hash.o predict.o         \
  lists.o ggc-common.o $(GGC) stringpool.o simplify-rtx.o ssa.o bb-reorder.o   \
  sibcall.o conflict.o timevar.o ifcvt.o dominance.o dependence.o dce.o \
- sched-vis.o sched-deps.o sched-rgn.o hashtab.o
+ sched-vis.o sched-deps.o sched-rgn.o sched-ebb.o hashtab.o
 
 BACKEND = toplev.o libbackend.a
 
@@ -1457,15 +1457,18 @@ regmove.o : regmove.c $(CONFIG_H) system.h $(RTL_H) insn-config.h \
    $(EXPR_H) insn-flags.h $(BASIC_BLOCK_H) toplev.h
 haifa-sched.o : haifa-sched.c $(CONFIG_H) system.h $(RTL_H) sched-int.h \
    $(BASIC_BLOCK_H) $(REGS_H) hard-reg-set.h flags.h insn-config.h function.h \
-   $(INSN_ATTR_H) toplev.h $(RECOG_H) except.h
+   $(INSN_ATTR_H) insn-flags.h toplev.h $(RECOG_H) except.h
 sched-deps.o : haifa-sched.c $(CONFIG_H) system.h $(RTL_H) sched-int.h \
    $(BASIC_BLOCK_H) $(REGS_H) hard-reg-set.h flags.h insn-config.h function.h \
    $(INSN_ATTR_H) toplev.h $(RECOG_H) except.h
 sched-rgn.o : haifa-sched.c $(CONFIG_H) system.h $(RTL_H) sched-int.h \
    $(BASIC_BLOCK_H) $(REGS_H) hard-reg-set.h flags.h insn-config.h function.h \
    $(INSN_ATTR_H) toplev.h $(RECOG_H) except.h
+sched-ebb.o : sched-ebb.c $(CONFIG_H) system.h $(RTL_H) sched-int.h \
+   $(BASIC_BLOCK_H) $(REGS_H) hard-reg-set.h flags.h insn-config.h function.h \
+   $(INSN_ATTR_H) toplev.h $(RECOG_H) except.h
 sched-vis.o : sched-vis.c $(CONFIG_H) system.h $(RTL_H) sched-int.h \
-   $(INSN_ATTR_H) $(REGS_H)
+   hard-reg-set.h $(BASIC_BLOCK_H) $(INSN_ATTR_H) $(REGS_H)
 final.o : final.c $(CONFIG_H) system.h $(RTL_H) $(TREE_H) flags.h intl.h \
    $(REGS_H) $(RECOG_H) conditions.h insn-config.h $(INSN_ATTR_H) function.h \
    real.h output.h hard-reg-set.h insn-flags.h insn-codes.h gstab.h except.h \
index 32ccf82..3498974 100644 (file)
@@ -144,6 +144,7 @@ the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 #include "flags.h"
 #include "insn-config.h"
 #include "insn-attr.h"
+#include "insn-flags.h"
 #include "except.h"
 #include "toplev.h"
 #include "recog.h"
@@ -740,7 +741,7 @@ priority (insn)
            next = XEXP (link, 0);
 
            /* Critical path is meaningful in block boundaries only.  */
-           if (BLOCK_NUM (next) != BLOCK_NUM (insn))
+           if (! (*current_sched_info->contributes_to_priority) (next, insn))
              continue;
 
            next_priority = insn_cost (insn, link, next) + priority (next);
index 79dd106..ce35cd8 100644 (file)
--- a/gcc/rtl.h
+++ b/gcc/rtl.h
@@ -1754,6 +1754,7 @@ extern void dump_combine_total_stats      PARAMS ((FILE *));
 /* In sched.c. */
 #ifdef BUFSIZ
 extern void schedule_insns             PARAMS ((FILE *));
+extern void schedule_ebbs              PARAMS ((FILE *));
 #endif
 extern void fix_sched_param            PARAMS ((const char *, const char *));
 
index b5f7328..396b519 100644 (file)
@@ -913,6 +913,53 @@ sched_analyze_insn (deps, x, insn, loop_notes)
          sched_analyze_2 (deps, XEXP (link, 0), insn);
       }
 
+  if (GET_CODE (insn) == JUMP_INSN)
+    {
+      rtx next, u, pending, pending_mem;
+      next = next_nonnote_insn (insn);
+      if (next && GET_CODE (next) == BARRIER)
+       {
+         for (i = 0; i < maxreg; i++)
+           {
+             for (u = deps->reg_last_sets[i]; u; u = XEXP (u, 1))
+               add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI);
+             for (u = deps->reg_last_clobbers[i]; u; u = XEXP (u, 1))
+               add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI);
+             for (u = deps->reg_last_uses[i]; u; u = XEXP (u, 1))
+               add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI);
+           }
+       }
+      else
+       {
+         regset_head tmp;
+         INIT_REG_SET (&tmp);
+
+         (*current_sched_info->compute_jump_reg_dependencies) (insn, &tmp);
+         EXECUTE_IF_SET_IN_REG_SET 
+           (&tmp, 0, i,
+           {
+             for (u = deps->reg_last_sets[i]; u; u = XEXP (u, 1))
+               add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI);
+             deps->reg_last_uses[i]
+               = alloc_INSN_LIST (insn, deps->reg_last_uses[i]);
+           });
+
+         CLEAR_REG_SET (&tmp);
+       }
+      pending = deps->pending_write_insns;
+      pending_mem = deps->pending_write_mems;
+      while (pending)
+       {
+         add_dependence (insn, XEXP (pending, 0), 0);
+
+         pending = XEXP (pending, 1);
+         pending_mem = XEXP (pending_mem, 1);
+       }
+
+      for (u = deps->last_pending_memory_flush; u; u = XEXP (u, 1))
+       add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI);
+    }
+
   /* If there is a {LOOP,EHREGION}_{BEG,END} note in the middle of a basic
      block, then we must be sure that no instructions are scheduled across it.
      Otherwise, the reg_n_refs info (which depends on loop_depth) would
diff --git a/gcc/sched-ebb.c b/gcc/sched-ebb.c
new file mode 100644 (file)
index 0000000..1ff7cea
--- /dev/null
@@ -0,0 +1,365 @@
+/* Instruction scheduling pass.
+   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998,
+   1999, 2000 Free Software Foundation, Inc.
+   Contributed by Michael Tiemann (tiemann@cygnus.com) Enhanced by,
+   and currently maintained by, Jim Wilson (wilson@cygnus.com)
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 2, or (at your option) any
+later version.
+
+GNU CC is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING.  If not, write to the Free
+the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+\f
+#include "config.h"
+#include "system.h"
+#include "toplev.h"
+#include "rtl.h"
+#include "tm_p.h"
+#include "hard-reg-set.h"
+#include "basic-block.h"
+#include "regs.h"
+#include "function.h"
+#include "flags.h"
+#include "insn-config.h"
+#include "insn-attr.h"
+#include "except.h"
+#include "toplev.h"
+#include "recog.h"
+#include "sched-int.h"
+\f
+/* The number of insns to be scheduled in total.  */
+static int target_n_insns;
+/* The number of insns scheduled so far.  */
+static int sched_n_insns;
+
+/* Implementations of the sched_info functions for region scheduling.  */
+static void init_ready_list PARAMS ((struct ready_list *));
+static int can_schedule_ready_p PARAMS ((rtx));
+static int new_ready PARAMS ((rtx));
+static int schedule_more_p PARAMS ((void));
+static const char *print_insn PARAMS ((rtx, int));
+static int rank PARAMS ((rtx, rtx));
+static int contributes_to_priority PARAMS ((rtx, rtx));
+static void compute_jump_reg_dependencies PARAMS ((rtx, regset));
+static void schedule_ebb PARAMS ((rtx, rtx));
+
+/* Return nonzero if there are more insns that should be scheduled.  */
+
+static int
+schedule_more_p ()
+{
+  return sched_n_insns < target_n_insns;
+}
+
+/* Add all insns that are initially ready to the ready list READY.  Called
+   once before scheduling a set of insns.  */
+
+static void
+init_ready_list (ready)
+     struct ready_list *ready;
+{
+  rtx prev_head = current_sched_info->prev_head;
+  rtx next_tail = current_sched_info->next_tail;
+  rtx insn;
+
+  target_n_insns = 0;
+  sched_n_insns = 0;
+
+#if 0
+  /* Print debugging information.  */
+  if (sched_verbose >= 5)
+    debug_dependencies ();
+#endif
+
+  /* Initialize ready list with all 'ready' insns in target block.
+     Count number of insns in the target block being scheduled.  */
+  for (insn = NEXT_INSN (prev_head); insn != next_tail; insn = NEXT_INSN (insn))
+    {
+      rtx next;
+
+      if (! INSN_P (insn))
+       continue;
+      next = NEXT_INSN (insn);
+
+      if (INSN_DEP_COUNT (insn) == 0
+         && (SCHED_GROUP_P (next) == 0 || ! INSN_P (next)))
+       ready_add (ready, insn);
+      if (!(SCHED_GROUP_P (insn)))
+       target_n_insns++;
+    }
+}
+
+/* Called after taking INSN from the ready list.  Returns nonzero if this
+   insn can be scheduled, nonzero if we should silently discard it.  */
+
+static int
+can_schedule_ready_p (insn)
+     rtx insn ATTRIBUTE_UNUSED;
+{
+  sched_n_insns++;
+  return 1;
+}
+
+/* Called after INSN has all its dependencies resolved.  Return nonzero
+   if it should be moved to the ready list or the queue, or zero if we
+   should silently discard it.  */
+static int
+new_ready (next)
+     rtx next ATTRIBUTE_UNUSED;
+{
+  return 1;
+}
+
+/* Return a string that contains the insn uid and optionally anything else
+   necessary to identify this insn in an output.  It's valid to use a
+   static buffer for this.  The ALIGNED parameter should cause the string
+   to be formatted so that multiple output lines will line up nicely.  */
+
+static const char *
+print_insn (insn, aligned)
+     rtx insn;
+     int aligned ATTRIBUTE_UNUSED;
+{
+  static char tmp[80];
+
+  sprintf (tmp, "%4d", INSN_UID (insn));
+  return tmp;
+}
+
+/* Compare priority of two insns.  Return a positive number if the second
+   insn is to be preferred for scheduling, and a negative one if the first
+   is to be preferred.  Zero if they are equally good.  */
+
+static int
+rank (insn1, insn2)
+     rtx insn1 ATTRIBUTE_UNUSED, insn2 ATTRIBUTE_UNUSED;
+{
+  return 0;
+}
+
+/* NEXT is an instruction that depends on INSN (a backward dependence);
+   return nonzero if we should include this dependence in priority
+   calculations.  */
+
+static int
+contributes_to_priority (next, insn)
+     rtx next ATTRIBUTE_UNUSED, insn ATTRIBUTE_UNUSED;
+{
+  return 1;
+}
+
+/* INSN is a JUMP_INSN.  Store the set of registers that must be considered
+   to be set by this jump in SET.  */
+
+static void
+compute_jump_reg_dependencies (insn, set)
+     rtx insn;
+     regset set;
+{
+  basic_block b = BLOCK_FOR_INSN (insn);
+  edge e;
+  for (e = b->succ; e; e = e->succ_next)
+    if ((e->flags & EDGE_FALLTHRU) == 0)
+      {
+       bitmap_operation (set, set, e->dest->global_live_at_start,
+                         BITMAP_IOR);
+      }
+}
+
+/* Used in schedule_insns to initialize current_sched_info for scheduling
+   regions (or single basic blocks).  */
+
+static struct sched_info ebb_sched_info =
+{
+  init_ready_list,
+  can_schedule_ready_p,
+  schedule_more_p,
+  new_ready,
+  rank,
+  print_insn,
+  contributes_to_priority,
+  compute_jump_reg_dependencies,
+
+  NULL, NULL,
+  NULL, NULL,
+  0
+};
+\f
+/* Schedule a single extended basic block, defined by the boundaries HEAD
+   and TAIL.  */
+
+static void
+schedule_ebb (head, tail)
+     rtx head, tail;
+{
+  int n_insns;
+  struct deps tmp_deps;
+
+  if (no_real_insns_p (head, tail))
+    return;
+
+  init_deps_global ();
+
+  /* Compute LOG_LINKS.  */
+  init_deps (&tmp_deps);
+  sched_analyze (&tmp_deps, head, tail);
+  free_deps (&tmp_deps);
+
+  /* Compute INSN_DEPEND.  */
+  compute_forward_dependences (head, tail);
+
+  /* Set priorities.  */
+  n_insns = set_priorities (head, tail);
+
+  current_sched_info->prev_head = PREV_INSN (head);
+  current_sched_info->next_tail = NEXT_INSN (tail);
+
+  if (write_symbols != NO_DEBUG)
+    {
+      save_line_notes (0, head, tail);
+      rm_line_notes (head, tail);
+    }
+
+  /* rm_other_notes only removes notes which are _inside_ the
+     block---that is, it won't remove notes before the first real insn
+     or after the last real insn of the block.  So if the first insn
+     has a REG_SAVE_NOTE which would otherwise be emitted before the
+     insn, it is redundant with the note before the start of the
+     block, and so we have to take it out.
+
+     FIXME: Probably the same thing should be done with REG_SAVE_NOTEs
+     referencing NOTE_INSN_SETJMP at the end of the block.  */
+  if (INSN_P (head))
+    {
+      rtx note;
+
+      for (note = REG_NOTES (head); note; note = XEXP (note, 1))
+       if (REG_NOTE_KIND (note) == REG_SAVE_NOTE)
+         {
+           if (INTVAL (XEXP (note, 0)) != NOTE_INSN_SETJMP)
+             {
+               remove_note (head, note);
+               note = XEXP (note, 1);
+               remove_note (head, note);
+             }
+           else
+             note = XEXP (note, 1);
+         }
+    }
+
+  /* Remove remaining note insns from the block, save them in
+     note_list.  These notes are restored at the end of
+     schedule_block ().  */
+  rm_other_notes (head, tail);
+
+  current_sched_info->queue_must_finish_empty = 1;
+
+  schedule_block (-1, n_insns);
+
+  /* Sanity check: verify that all region insns were scheduled.  */
+  if (sched_n_insns != n_insns)
+    abort ();
+  head = current_sched_info->head;
+  tail = current_sched_info->tail;
+
+  if (write_symbols != NO_DEBUG)
+    restore_line_notes (0, head, tail);
+
+  finish_deps_global ();
+}
+
+/* The one entry point in this file.  DUMP_FILE is the dump file for
+   this pass.  */
+
+void
+schedule_ebbs (dump_file)
+     FILE *dump_file;
+{
+  int i;
+
+  /* Taking care of this degenerate case makes the rest of
+     this code simpler.  */
+  if (n_basic_blocks == 0)
+    return;
+
+  sched_init (dump_file);
+
+  current_sched_info = &ebb_sched_info;
+
+  allocate_reg_life_data ();
+  compute_bb_for_insn (get_max_uid ());
+
+  /* Schedule every region in the subroutine.  */
+  for (i = 0; i < n_basic_blocks; i++)
+    { 
+      rtx head = BASIC_BLOCK (i)->head;
+      rtx tail;
+
+      for (;;)
+       {
+         basic_block b = BASIC_BLOCK (i);
+         edge e;
+         tail = b->end;
+         if (i + 1 == n_basic_blocks
+             || GET_CODE (BLOCK_HEAD (i + 1)) == CODE_LABEL)
+           break;
+         for (e = b->succ; e; e = e->succ_next)
+           if ((e->flags & EDGE_FALLTHRU) != 0)
+             break;
+         if (! e)
+           break;
+         if (GET_CODE (tail) == JUMP_INSN)
+           {
+             rtx x = find_reg_note (tail, REG_BR_PROB, 0);
+             if (x)
+               {
+                 int pred_val = INTVAL (XEXP (x, 0));
+                 if (pred_val > REG_BR_PROB_BASE / 2)
+                   break;
+               }
+           }
+
+         i++;
+       }
+
+      /* Blah.  We should fix the rest of the code not to get confused by
+        a note or two.  */
+      while (head != tail)
+       {
+         if (GET_CODE (head) == NOTE)
+           head = NEXT_INSN (head);
+         else if (GET_CODE (tail) == NOTE)
+           tail = PREV_INSN (tail);
+         else if (GET_CODE (head) == CODE_LABEL)
+           head = NEXT_INSN (head);
+         else
+           break;
+       }
+
+      schedule_ebb (head, tail);
+    }
+
+  /* It doesn't make much sense to try and update life information here - we
+     probably messed up even the flow graph.  */
+
+  /* Reposition the prologue and epilogue notes in case we moved the
+     prologue/epilogue insns.  */
+  if (reload_completed)
+    reposition_prologue_and_epilogue_notes (get_insns ());
+
+  if (write_symbols != NO_DEBUG)
+    rm_redundant_line_notes ();
+
+  sched_finish ();
+}
index ffa23a1..f59f3a8 100644 (file)
@@ -115,6 +115,13 @@ struct sched_info
      static buffer for this.  The ALIGNED parameter should cause the string
      to be formatted so that multiple output lines will line up nicely.  */
   const char *(*print_insn) PARAMS ((rtx, int));
+  /* Return nonzero if an insn should be included in priority
+     calculations.  */
+  int (*contributes_to_priority) PARAMS ((rtx, rtx));
+  /* Called when computing dependencies for a JUMP_INSN.  This function
+     should store the set of registers that must be considered as set by
+     the jump in the regset.  */
+  void (*compute_jump_reg_dependencies) PARAMS ((rtx, regset));
 
   /* The boundaries of the set of insns to be scheduled.  */
   rtx prev_head, next_tail;
index 9e1bfd9..db4cc87 100644 (file)
@@ -2042,6 +2042,8 @@ static int new_ready PARAMS ((rtx));
 static int schedule_more_p PARAMS ((void));
 static const char *rgn_print_insn PARAMS ((rtx, int));
 static int rgn_rank PARAMS ((rtx, rtx));
+static int contributes_to_priority PARAMS ((rtx, rtx));
+static void compute_jump_reg_dependencies PARAMS ((rtx, regset));
 
 /* Return nonzero if there are more insns that should be scheduled.  */
 
@@ -2302,6 +2304,29 @@ rgn_rank (insn1, insn2)
   return 0;
 }
 
+/* NEXT is an instruction that depends on INSN (a backward dependence);
+   return nonzero if we should include this dependence in priority
+   calculations.  */
+
+static int
+contributes_to_priority (next, insn)
+     rtx next, insn;
+{
+  return BLOCK_NUM (next) == BLOCK_NUM (insn);
+}
+
+/* INSN is a JUMP_INSN.  Store the set of registers that must be considered
+   to be set by this jump in SET.  */
+
+static void
+compute_jump_reg_dependencies (insn, set)
+     rtx insn ATTRIBUTE_UNUSED;
+     regset set ATTRIBUTE_UNUSED;
+{
+  /* Nothing to do here, since we postprocess jumps in
+     add_branch_dependences.  */
+}
+
 /* Used in schedule_insns to initialize current_sched_info for scheduling
    regions (or single basic blocks).  */
 
@@ -2313,6 +2338,8 @@ static struct sched_info region_sched_info =
   new_ready,
   rgn_rank,
   rgn_print_insn,
+  contributes_to_priority,
+  compute_jump_reg_dependencies,
 
   NULL, NULL,
   NULL, NULL,
index 99489c9..764876c 100644 (file)
@@ -27,6 +27,8 @@ the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 #include "rtl.h"
 #include "tm_p.h"
 #include "regs.h"
+#include "hard-reg-set.h"
+#include "basic-block.h"
 #include "insn-attr.h"
 #include "sched-int.h"