diff options
Diffstat (limited to 'gcc')
| -rw-r--r-- | gcc/ChangeLog | 32 | ||||
| -rw-r--r-- | gcc/Makefile.in | 5 | ||||
| -rw-r--r-- | gcc/haifa-sched.c | 1473 | ||||
| -rw-r--r-- | gcc/sched-deps.c | 1399 | ||||
| -rw-r--r-- | gcc/sched-int.h | 161 | 
5 files changed, 1599 insertions, 1471 deletions
| diff --git a/gcc/ChangeLog b/gcc/ChangeLog index efabdb459f5..d38b6e8bf03 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,5 +1,32 @@  2000-12-03  Bernd Schmidt  <bernds@redhat.co.uk> +	* Makefile.in (OBJS): Add sched-deps.o. +	(sched-deps.o): New rule. +	* haifa-sched.c (struct deps, struct haifa_insn_data): Moved to +	sched-int.h. +	(INSN_DEPEND, INSN_LUID, CANT_MOVE, INSN_DEP_COUNT): Macros moved to +	sched-int.h. +	(SIZE_FOR_MODE): Delete unused macro. +	(reg_known_equiv_p, reg_known_value, reg_pending_clobbers, +	reg_pending_sets, reg_pending_sets_all, true_dependency_cache, +	anti_dependency_cache, output_dependency_cache, +	forward_dependency_cache): Variables moved to sched-deps.c. +	(add_dependence, remove_dependence, find_insn_list, +	find_insn_mem_list, add_insn_mem_dependence, flush_pending_lists, +	sched_analyze_insn, sched_analyze_1, sched_analyze_2, +	sched_analyze, group_leader, compute_forward_dependences, +	init_deps, free_deps, init_dependency_caches, free_dependency_caches): +	Functions moved to sched-deps.c. +	(schedule_region): Call init_deps_global and finish_deps_global +	instead of directly manipulating dependency data structures. +	* sched-deps.c: New file. +	(init_deps_global, finish_deps_global): New functions. +	* sched-int.h (struct haifa_insn_data, struct deps): Moved here from +	haifa-sched.c. +	(h_i_d): Declare. +	(INSN_DEPEND, INSN_LUID, CANT_MOVE, INSN_DEP_COUNT): Macros moved here +	from haifa-sched.c. +  	* Makefile.in (OBJS): Add sched-vis.o.  	(sched-vis.o): New rule.  	* haifa-sched.c (get_unit_last_insn): New function. @@ -20,6 +47,11 @@  	print_block_visualization): Lose basic block argument.  All callers  	changed.  	(visualize_scheduled_insns): Use new function get_unit_last_insn. +	* sched-int.h (current_sched_info, sched_dump): Declare. +	(init_target_units, insn_print_units, init_block_visualization, +	print_block_visualization, visualize_scheduled_inns, +	visualize_no_unit, visualize_stall_cycles, visualize_alloc, +	visualize_free): Declare functions.  	* sched-int.h: New file.  	* Makefile.in (haifa-sched.o): Depend on it. diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 6cb0d77a0c7..62f5ae07ede 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -737,7 +737,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 hashtab.o + sched-vis.o sched-deps.o hashtab.o  BACKEND = toplev.o libbackend.a @@ -1455,6 +1455,9 @@ regmove.o : regmove.c $(CONFIG_H) system.h $(RTL_H) insn-config.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 +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-vis.o : sched-vis.c $(CONFIG_H) system.h $(RTL_H) sched-int.h \     $(INSN_ATTR_H) $(REGS_H)  final.o : final.c $(CONFIG_H) system.h $(RTL_H) $(TREE_H) flags.h intl.h \ diff --git a/gcc/haifa-sched.c b/gcc/haifa-sched.c index f89f2a1dd77..d768d85e963 100644 --- a/gcc/haifa-sched.c +++ b/gcc/haifa-sched.c @@ -172,9 +172,6 @@ the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA  #include "recog.h"  #include "sched-int.h" -extern char *reg_known_equiv_p; -extern rtx *reg_known_value; -  #ifdef INSN_SCHEDULING  /* issue_rate is the number of insns that can be scheduled in the same @@ -225,158 +222,9 @@ fix_sched_param (param, val)      warning ("fix_sched_param: unknown param: %s", param);  } -/* Describe state of dependencies used during sched_analyze phase.  */ -struct deps -{ -  /* The *_insns and *_mems are paired lists.  Each pending memory operation -     will have a pointer to the MEM rtx on one list and a pointer to the -     containing insn on the other list in the same place in the list.  */ - -  /* We can't use add_dependence like the old code did, because a single insn -     may have multiple memory accesses, and hence needs to be on the list -     once for each memory access.  Add_dependence won't let you add an insn -     to a list more than once.  */ - -  /* An INSN_LIST containing all insns with pending read operations.  */ -  rtx pending_read_insns; - -  /* An EXPR_LIST containing all MEM rtx's which are pending reads.  */ -  rtx pending_read_mems; - -  /* An INSN_LIST containing all insns with pending write operations.  */ -  rtx pending_write_insns; - -  /* An EXPR_LIST containing all MEM rtx's which are pending writes.  */ -  rtx pending_write_mems; - -  /* Indicates the combined length of the two pending lists.  We must prevent -     these lists from ever growing too large since the number of dependencies -     produced is at least O(N*N), and execution time is at least O(4*N*N), as -     a function of the length of these pending lists.  */ -  int pending_lists_length; - -  /* The last insn upon which all memory references must depend. -     This is an insn which flushed the pending lists, creating a dependency -     between it and all previously pending memory references.  This creates -     a barrier (or a checkpoint) which no memory reference is allowed to cross. - -     This includes all non constant CALL_INSNs.  When we do interprocedural -     alias analysis, this restriction can be relaxed. -     This may also be an INSN that writes memory if the pending lists grow -     too large.  */ -  rtx last_pending_memory_flush; - -  /* The last function call we have seen.  All hard regs, and, of course, -     the last function call, must depend on this.  */ -  rtx last_function_call; - -  /* Used to keep post-call psuedo/hard reg movements together with -     the call.  */ -  int in_post_call_group_p; - -  /* The LOG_LINKS field of this is a list of insns which use a pseudo -     register that does not already cross a call.  We create -     dependencies between each of those insn and the next call insn, -     to ensure that they won't cross a call after scheduling is done.  */ -  rtx sched_before_next_call; - -  /* Element N is the next insn that sets (hard or pseudo) register -     N within the current basic block; or zero, if there is no -     such insn.  Needed for new registers which may be introduced -     by splitting insns.  */ -  rtx *reg_last_uses; -  rtx *reg_last_sets; -  rtx *reg_last_clobbers; -}; - -static regset reg_pending_sets; -static regset reg_pending_clobbers; -static int reg_pending_sets_all; - -/* To speed up the test for duplicate dependency links we keep a -   record of dependencies created by add_dependence when the average -   number of instructions in a basic block is very large. - -   Studies have shown that there is typically around 5 instructions between -   branches for typical C code.  So we can make a guess that the average -   basic block is approximately 5 instructions long; we will choose 100X -   the average size as a very large basic block. - -   Each insn has associated bitmaps for its dependencies.  Each bitmap -   has enough entries to represent a dependency on any other insn in -   the insn chain.  All bitmap for true dependencies cache is -   allocated then the rest two ones are also allocated. */ -static sbitmap *true_dependency_cache; -static sbitmap *anti_dependency_cache; -static sbitmap *output_dependency_cache; - -/* To speed up checking consistency of formed forward insn -   dependencies we use the following cache.  Another possible solution -   could be switching off checking duplication of insns in forward -   dependencies. */ -#ifdef ENABLE_CHECKING -static sbitmap *forward_dependency_cache; -#endif - -/* Indexed by INSN_UID, the collection of all data associated with -   a single instruction.  */ - -struct haifa_insn_data -{ -  /* A list of insns which depend on the instruction.  Unlike LOG_LINKS, -     it represents forward dependancies.  */ -  rtx depend; - -  /* The line number note in effect for each insn.  For line number -     notes, this indicates whether the note may be reused.  */ -  rtx line_note; - -  /* Logical uid gives the original ordering of the insns.  */ -  int luid; - -  /* A priority for each insn.  */ -  int priority; - -  /* The number of incoming edges in the forward dependency graph. -     As scheduling proceds, counts are decreased.  An insn moves to -     the ready queue when its counter reaches zero.  */ -  int dep_count; - -  /* An encoding of the blockage range function.  Both unit and range -     are coded.  */ -  unsigned int blockage; - -  /* Number of instructions referring to this insn.  */ -  int ref_count; - -  /* The minimum clock tick at which the insn becomes ready.  This is -     used to note timing constraints for the insns in the pending list.  */ -  int tick; +struct haifa_insn_data *h_i_d; -  short cost; - -  /* An encoding of the function units used.  */ -  short units; - -  /* This weight is an estimation of the insn's contribution to -     register pressure.  */ -  short reg_weight; - -  /* Some insns (e.g. call) are not allowed to move across blocks.  */ -  unsigned int cant_move : 1; - -  /* Set if there's DEF-USE dependance between some speculatively -     moved load insn and this one.  */ -  unsigned int fed_by_spec_load : 1; -  unsigned int is_load_insn : 1; -}; - -static struct haifa_insn_data *h_i_d; - -#define INSN_DEPEND(INSN)	(h_i_d[INSN_UID (INSN)].depend) -#define INSN_LUID(INSN)		(h_i_d[INSN_UID (INSN)].luid)  #define INSN_PRIORITY(INSN)	(h_i_d[INSN_UID (INSN)].priority) -#define INSN_DEP_COUNT(INSN)	(h_i_d[INSN_UID (INSN)].dep_count)  #define INSN_COST(INSN)		(h_i_d[INSN_UID (INSN)].cost)  #define INSN_UNIT(INSN)		(h_i_d[INSN_UID (INSN)].units)  #define INSN_REG_WEIGHT(INSN)	(h_i_d[INSN_UID (INSN)].reg_weight) @@ -407,7 +255,6 @@ static struct haifa_insn_data *h_i_d;  #define INSN_REF_COUNT(INSN)	(h_i_d[INSN_UID (INSN)].ref_count)  #define LINE_NOTE(INSN)		(h_i_d[INSN_UID (INSN)].line_note)  #define INSN_TICK(INSN)		(h_i_d[INSN_UID (INSN)].tick) -#define CANT_MOVE(insn)		(h_i_d[INSN_UID (insn)].cant_move)  #define FED_BY_SPEC_LOAD(insn)	(h_i_d[INSN_UID (insn)].fed_by_spec_load)  #define IS_LOAD_INSN(insn)	(h_i_d[INSN_UID (insn)].is_load_insn) @@ -489,10 +336,6 @@ struct ready_list  };  /* Forward declarations.  */ -static void add_dependence PARAMS ((rtx, rtx, enum reg_note)); -static void remove_dependence PARAMS ((rtx, rtx)); -static rtx find_insn_list PARAMS ((rtx, rtx)); -static void set_sched_group_p PARAMS ((rtx));  static unsigned int blockage_range PARAMS ((int, rtx));  static void clear_units PARAMS ((void));  static void schedule_unit PARAMS ((int, rtx, int)); @@ -501,13 +344,6 @@ static int potential_hazard PARAMS ((int, rtx, int));  static int insn_cost PARAMS ((rtx, rtx, rtx));  static int priority PARAMS ((rtx));  static void free_pending_lists PARAMS ((void)); -static void add_insn_mem_dependence PARAMS ((struct deps *, rtx *, rtx *, rtx, -					     rtx)); -static void flush_pending_lists PARAMS ((struct deps *, rtx, int)); -static void sched_analyze_1 PARAMS ((struct deps *, rtx, rtx)); -static void sched_analyze_2 PARAMS ((struct deps *, rtx, rtx)); -static void sched_analyze_insn PARAMS ((struct deps *, rtx, rtx, rtx)); -static void sched_analyze PARAMS ((struct deps *, rtx, rtx));  static int rank_for_schedule PARAMS ((const PTR, const PTR));  static void swap_sort PARAMS ((rtx *, int));  static void queue_insn PARAMS ((rtx, int)); @@ -740,8 +576,6 @@ static int haifa_classify_insn PARAMS ((rtx));  static int is_prisky PARAMS ((rtx, int, int));  static int is_exception_free PARAMS ((rtx, int, int)); -static char find_insn_mem_list PARAMS ((rtx, rtx, rtx, rtx)); -static void compute_forward_dependences PARAMS ((rtx, rtx));  static void add_branch_dependences PARAMS ((rtx, rtx));  static void compute_block_backward_dependences PARAMS ((int));  void debug_dependencies PARAMS ((void)); @@ -794,12 +628,7 @@ void debug_reg_vector PARAMS ((regset));  static rtx move_insn1 PARAMS ((rtx, rtx));  static rtx move_insn PARAMS ((rtx, rtx)); -static rtx group_leader PARAMS ((rtx));  static int set_priorities PARAMS ((int)); -static void init_deps PARAMS ((struct deps *)); -static void free_deps PARAMS ((struct deps *)); -static void init_dependency_caches PARAMS ((int)); -static void free_dependency_caches PARAMS ((void));  static void init_regions PARAMS ((void));  static void sched_init PARAMS ((FILE *));  static void schedule_region PARAMS ((int)); @@ -809,307 +638,6 @@ static void propagate_deps PARAMS ((int, struct deps *, int));  /* Point to state used for the current scheduling pass.  */  struct sched_info *current_sched_info; - -#define SIZE_FOR_MODE(X) (GET_MODE_SIZE (GET_MODE (X))) - -/* Add ELEM wrapped in an INSN_LIST with reg note kind DEP_TYPE to the -   LOG_LINKS of INSN, if not already there.  DEP_TYPE indicates the type -   of dependence that this link represents.  */ - -static void -add_dependence (insn, elem, dep_type) -     rtx insn; -     rtx elem; -     enum reg_note dep_type; -{ -  rtx link, next; -  int present_p; -  enum reg_note present_dep_type; - -  /* Don't depend an insn on itself.  */ -  if (insn == elem) -    return; - -  /* We can get a dependency on deleted insns due to optimizations in -     the register allocation and reloading or due to splitting.  Any -     such dependency is useless and can be ignored.  */ -  if (GET_CODE (elem) == NOTE) -    return; - -  /* If elem is part of a sequence that must be scheduled together, then -     make the dependence point to the last insn of the sequence. -     When HAVE_cc0, it is possible for NOTEs to exist between users and -     setters of the condition codes, so we must skip past notes here. -     Otherwise, NOTEs are impossible here.  */ -  next = next_nonnote_insn (elem); -  if (next && SCHED_GROUP_P (next) -      && GET_CODE (next) != CODE_LABEL) -    { -      /* Notes will never intervene here though, so don't bother checking -         for them.  */ -      /* Hah!  Wrong.  */ -      /* We must reject CODE_LABELs, so that we don't get confused by one -         that has LABEL_PRESERVE_P set, which is represented by the same -         bit in the rtl as SCHED_GROUP_P.  A CODE_LABEL can never be -         SCHED_GROUP_P.  */ - -      rtx nnext; -      while ((nnext = next_nonnote_insn (next)) != NULL -	     && SCHED_GROUP_P (nnext) -	     && GET_CODE (nnext) != CODE_LABEL) -	next = nnext; - -      /* Again, don't depend an insn on itself.  */ -      if (insn == next) -	return; - -      /* Make the dependence to NEXT, the last insn of the group, instead -         of the original ELEM.  */ -      elem = next; -    } - -  present_p = 1; -#ifdef INSN_SCHEDULING -  /* (This code is guarded by INSN_SCHEDULING, otherwise INSN_BB is undefined.) -     No need for interblock dependences with calls, since -     calls are not moved between blocks.   Note: the edge where -     elem is a CALL is still required.  */ -  if (GET_CODE (insn) == CALL_INSN -      && (INSN_BB (elem) != INSN_BB (insn))) -    return; - -  /* If we already have a dependency for ELEM, then we do not need to -     do anything.  Avoiding the list walk below can cut compile times -     dramatically for some code.  */ -  if (true_dependency_cache != NULL) -    { -      if (anti_dependency_cache == NULL || output_dependency_cache == NULL) -	abort (); -      if (TEST_BIT (true_dependency_cache[INSN_LUID (insn)], INSN_LUID (elem))) -	present_dep_type = 0; -      else if (TEST_BIT (anti_dependency_cache[INSN_LUID (insn)], -			 INSN_LUID (elem))) -	present_dep_type = REG_DEP_ANTI; -      else if (TEST_BIT (output_dependency_cache[INSN_LUID (insn)], -			 INSN_LUID (elem))) -	present_dep_type = REG_DEP_OUTPUT; -      else  -	present_p = 0; -      if (present_p && (int) dep_type >= (int) present_dep_type) -	return; -    } -#endif - -  /* Check that we don't already have this dependence.  */ -  if (present_p) -    for (link = LOG_LINKS (insn); link; link = XEXP (link, 1)) -      if (XEXP (link, 0) == elem) -	{ -#ifdef INSN_SCHEDULING -	  /* Clear corresponding cache entry because type of the link -             may be changed. */ -	  if (true_dependency_cache != NULL) -	    { -	      if (REG_NOTE_KIND (link) == REG_DEP_ANTI) -		RESET_BIT (anti_dependency_cache[INSN_LUID (insn)], -			   INSN_LUID (elem)); -	      else if (REG_NOTE_KIND (link) == REG_DEP_OUTPUT -		       && output_dependency_cache) -		RESET_BIT (output_dependency_cache[INSN_LUID (insn)], -			   INSN_LUID (elem)); -	      else -		abort (); -	    } -#endif - -	  /* If this is a more restrictive type of dependence than the existing -	     one, then change the existing dependence to this type.  */ -	  if ((int) dep_type < (int) REG_NOTE_KIND (link)) -	    PUT_REG_NOTE_KIND (link, dep_type); -	   -#ifdef INSN_SCHEDULING -	  /* If we are adding a dependency to INSN's LOG_LINKs, then -	     note that in the bitmap caches of dependency information. */ -	  if (true_dependency_cache != NULL) -	    { -	      if ((int)REG_NOTE_KIND (link) == 0) -		SET_BIT (true_dependency_cache[INSN_LUID (insn)], -			 INSN_LUID (elem)); -	      else if (REG_NOTE_KIND (link) == REG_DEP_ANTI) -		SET_BIT (anti_dependency_cache[INSN_LUID (insn)], -			 INSN_LUID (elem)); -	      else if (REG_NOTE_KIND (link) == REG_DEP_OUTPUT) -		SET_BIT (output_dependency_cache[INSN_LUID (insn)], -			 INSN_LUID (elem)); -	    } -#endif -	  return; -      } -  /* Might want to check one level of transitivity to save conses.  */ - -  link = alloc_INSN_LIST (elem, LOG_LINKS (insn)); -  LOG_LINKS (insn) = link; - -  /* Insn dependency, not data dependency.  */ -  PUT_REG_NOTE_KIND (link, dep_type); - -#ifdef INSN_SCHEDULING -  /* If we are adding a dependency to INSN's LOG_LINKs, then note that -     in the bitmap caches of dependency information. */ -  if (true_dependency_cache != NULL) -    { -      if ((int)dep_type == 0) -	SET_BIT (true_dependency_cache[INSN_LUID (insn)], INSN_LUID (elem)); -      else if (dep_type == REG_DEP_ANTI) -	SET_BIT (anti_dependency_cache[INSN_LUID (insn)], INSN_LUID (elem)); -      else if (dep_type == REG_DEP_OUTPUT) -	SET_BIT (output_dependency_cache[INSN_LUID (insn)], INSN_LUID (elem)); -    } -#endif -} - -/* Remove ELEM wrapped in an INSN_LIST from the LOG_LINKS -   of INSN.  Abort if not found.  */ - -static void -remove_dependence (insn, elem) -     rtx insn; -     rtx elem; -{ -  rtx prev, link, next; -  int found = 0; - -  for (prev = 0, link = LOG_LINKS (insn); link; link = next) -    { -      next = XEXP (link, 1); -      if (XEXP (link, 0) == elem) -	{ -	  if (prev) -	    XEXP (prev, 1) = next; -	  else -	    LOG_LINKS (insn) = next; - -#ifdef INSN_SCHEDULING -	  /* If we are removing a dependency from the LOG_LINKS list, -	     make sure to remove it from the cache too.  */ -	  if (true_dependency_cache != NULL) -	    { -	      if (REG_NOTE_KIND (link) == 0) -		RESET_BIT (true_dependency_cache[INSN_LUID (insn)], -			   INSN_LUID (elem)); -	      else if (REG_NOTE_KIND (link) == REG_DEP_ANTI) -		RESET_BIT (anti_dependency_cache[INSN_LUID (insn)], -			   INSN_LUID (elem)); -	      else if (REG_NOTE_KIND (link) == REG_DEP_OUTPUT) -		RESET_BIT (output_dependency_cache[INSN_LUID (insn)], -			   INSN_LUID (elem)); -	    } -#endif - -	  free_INSN_LIST_node (link); - -	  found = 1; -	} -      else -	prev = link; -    } - -  if (!found) -    abort (); -  return; -} - -/* Return the INSN_LIST containing INSN in LIST, or NULL -   if LIST does not contain INSN.  */ - -static inline rtx -find_insn_list (insn, list) -     rtx insn; -     rtx list; -{ -  while (list) -    { -      if (XEXP (list, 0) == insn) -	return list; -      list = XEXP (list, 1); -    } -  return 0; -} - -/* Set SCHED_GROUP_P and care for the rest of the bookkeeping that -   goes along with that.  */ - -static void -set_sched_group_p (insn) -     rtx insn; -{ -  rtx link, prev; - -  SCHED_GROUP_P (insn) = 1; - -  /* There may be a note before this insn now, but all notes will -     be removed before we actually try to schedule the insns, so -     it won't cause a problem later.  We must avoid it here though.  */ -  prev = prev_nonnote_insn (insn); - -  /* Make a copy of all dependencies on the immediately previous insn, -     and add to this insn.  This is so that all the dependencies will -     apply to the group.  Remove an explicit dependence on this insn -     as SCHED_GROUP_P now represents it.  */ - -  if (find_insn_list (prev, LOG_LINKS (insn))) -    remove_dependence (insn, prev); - -  for (link = LOG_LINKS (prev); link; link = XEXP (link, 1)) -    add_dependence (insn, XEXP (link, 0), REG_NOTE_KIND (link)); -} - -/* If it is profitable to use them, initialize caches for tracking -   dependency informatino.  LUID is the number of insns to be scheduled, -   it is used in the estimate of profitability.  */ -static void -init_dependency_caches (luid) -     int luid; -{ -  /* ?!? We could save some memory by computing a per-region luid mapping -     which could reduce both the number of vectors in the cache and the size -     of each vector.  Instead we just avoid the cache entirely unless the -     average number of instructions in a basic block is very high.  See -     the comment before the declaration of true_dependency_cache for -     what we consider "very high".  */ -  if (luid / n_basic_blocks > 100 * 5) -    { -      true_dependency_cache = sbitmap_vector_alloc (luid, luid); -      sbitmap_vector_zero (true_dependency_cache, luid); -      anti_dependency_cache = sbitmap_vector_alloc (luid, luid); -      sbitmap_vector_zero (anti_dependency_cache, luid); -      output_dependency_cache = sbitmap_vector_alloc (luid, luid); -      sbitmap_vector_zero (output_dependency_cache, luid); -#ifdef ENABLE_CHECKING -      forward_dependency_cache = sbitmap_vector_alloc (luid, luid); -      sbitmap_vector_zero (forward_dependency_cache, luid); -#endif -    } -} - -/* Free the caches allocated in init_dependency_caches.  */ -static void -free_dependency_caches () -{ -  if (true_dependency_cache) -    { -      free (true_dependency_cache); -      true_dependency_cache = NULL; -      free (anti_dependency_cache); -      anti_dependency_cache = NULL; -      free (output_dependency_cache); -      output_dependency_cache = NULL; -#ifdef ENABLE_CHECKING -      free (forward_dependency_cache); -      forward_dependency_cache = NULL; -#endif -    } -}  #ifndef INSN_SCHEDULING  void @@ -2853,37 +2381,7 @@ is_exception_free (insn, bb_src, bb_trg)    return flag_schedule_speculative_load_dangerous;  } - -/* Process an insn's memory dependencies.  There are four kinds of -   dependencies: - -   (0) read dependence: read follows read -   (1) true dependence: read follows write -   (2) anti dependence: write follows read -   (3) output dependence: write follows write - -   We are careful to build only dependencies which actually exist, and -   use transitivity to avoid building too many links.  */ -/* Return 1 if the pair (insn, x) is found in (LIST, LIST1), or 0 -   otherwise.  */ - -HAIFA_INLINE static char -find_insn_mem_list (insn, x, list, list1) -     rtx insn, x; -     rtx list, list1; -{ -  while (list) -    { -      if (XEXP (list, 0) == insn -	  && XEXP (list1, 0) == x) -	return 1; -      list = XEXP (list, 1); -      list1 = XEXP (list1, 1); -    } -  return 0; -} -  /* Compute the function units used by INSN.  This caches the value     returned by function_units_used.  A function unit is encoded as the     unit number if the value is non-negative and the compliment of a @@ -3299,836 +2797,6 @@ free_pending_lists ()        free_EXPR_LIST_list (&bb_deps[bb].pending_write_mems);      }  } - -/* Add an INSN and MEM reference pair to a pending INSN_LIST and MEM_LIST. -   The MEM is a memory reference contained within INSN, which we are saving -   so that we can do memory aliasing on it.  */ - -static void -add_insn_mem_dependence (deps, insn_list, mem_list, insn, mem) -     struct deps *deps; -     rtx *insn_list, *mem_list, insn, mem; -{ -  register rtx link; - -  link = alloc_INSN_LIST (insn, *insn_list); -  *insn_list = link; - -  link = alloc_EXPR_LIST (VOIDmode, mem, *mem_list); -  *mem_list = link; - -  deps->pending_lists_length++; -} - -/* Make a dependency between every memory reference on the pending lists -   and INSN, thus flushing the pending lists.  If ONLY_WRITE, don't flush -   the read list.  */ - -static void -flush_pending_lists (deps, insn, only_write) -     struct deps *deps; -     rtx insn; -     int only_write; -{ -  rtx u; -  rtx link; - -  while (deps->pending_read_insns && ! only_write) -    { -      add_dependence (insn, XEXP (deps->pending_read_insns, 0), -		      REG_DEP_ANTI); - -      link = deps->pending_read_insns; -      deps->pending_read_insns = XEXP (deps->pending_read_insns, 1); -      free_INSN_LIST_node (link); - -      link = deps->pending_read_mems; -      deps->pending_read_mems = XEXP (deps->pending_read_mems, 1); -      free_EXPR_LIST_node (link); -    } -  while (deps->pending_write_insns) -    { -      add_dependence (insn, XEXP (deps->pending_write_insns, 0), -		      REG_DEP_ANTI); - -      link = deps->pending_write_insns; -      deps->pending_write_insns = XEXP (deps->pending_write_insns, 1); -      free_INSN_LIST_node (link); - -      link = deps->pending_write_mems; -      deps->pending_write_mems = XEXP (deps->pending_write_mems, 1); -      free_EXPR_LIST_node (link); -    } -  deps->pending_lists_length = 0; - -  /* last_pending_memory_flush is now a list of insns.  */ -  for (u = deps->last_pending_memory_flush; u; u = XEXP (u, 1)) -    add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI); - -  free_INSN_LIST_list (&deps->last_pending_memory_flush); -  deps->last_pending_memory_flush = alloc_INSN_LIST (insn, NULL_RTX); -} - -/* Analyze a single SET, CLOBBER, PRE_DEC, POST_DEC, PRE_INC or POST_INC -   rtx, X, creating all dependencies generated by the write to the -   destination of X, and reads of everything mentioned.  */ - -static void -sched_analyze_1 (deps, x, insn) -     struct deps *deps; -     rtx x; -     rtx insn; -{ -  register int regno; -  register rtx dest = XEXP (x, 0); -  enum rtx_code code = GET_CODE (x); - -  if (dest == 0) -    return; - -  if (GET_CODE (dest) == PARALLEL -      && GET_MODE (dest) == BLKmode) -    { -      register int i; -      for (i = XVECLEN (dest, 0) - 1; i >= 0; i--) -	sched_analyze_1 (deps, XVECEXP (dest, 0, i), insn); -      if (GET_CODE (x) == SET) -	sched_analyze_2 (deps, SET_SRC (x), insn); -      return; -    } - -  while (GET_CODE (dest) == STRICT_LOW_PART || GET_CODE (dest) == SUBREG -	 || GET_CODE (dest) == ZERO_EXTRACT || GET_CODE (dest) == SIGN_EXTRACT) -    { -      if (GET_CODE (dest) == ZERO_EXTRACT || GET_CODE (dest) == SIGN_EXTRACT) -	{ -	  /* The second and third arguments are values read by this insn.  */ -	  sched_analyze_2 (deps, XEXP (dest, 1), insn); -	  sched_analyze_2 (deps, XEXP (dest, 2), insn); -	} -      dest = XEXP (dest, 0); -    } - -  if (GET_CODE (dest) == REG) -    { -      register int i; - -      regno = REGNO (dest); - -      /* A hard reg in a wide mode may really be multiple registers. -         If so, mark all of them just like the first.  */ -      if (regno < FIRST_PSEUDO_REGISTER) -	{ -	  i = HARD_REGNO_NREGS (regno, GET_MODE (dest)); -	  while (--i >= 0) -	    { -	      int r = regno + i; -	      rtx u; - -	      for (u = deps->reg_last_uses[r]; u; u = XEXP (u, 1)) -		add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI); - -	      for (u = deps->reg_last_sets[r]; u; u = XEXP (u, 1)) -		add_dependence (insn, XEXP (u, 0), REG_DEP_OUTPUT); - -	      /* Clobbers need not be ordered with respect to one -		 another, but sets must be ordered with respect to a -		 pending clobber.  */ -	      if (code == SET) -		{ -		  free_INSN_LIST_list (&deps->reg_last_uses[r]); -		  for (u = deps->reg_last_clobbers[r]; u; u = XEXP (u, 1)) -		    add_dependence (insn, XEXP (u, 0), REG_DEP_OUTPUT); -		  SET_REGNO_REG_SET (reg_pending_sets, r); -		} -	      else -		SET_REGNO_REG_SET (reg_pending_clobbers, r); - -	      /* Function calls clobber all call_used regs.  */ -	      if (global_regs[r] || (code == SET && call_used_regs[r])) -		for (u = deps->last_function_call; u; u = XEXP (u, 1)) -		  add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI); -	    } -	} -      else -	{ -	  rtx u; - -	  for (u = deps->reg_last_uses[regno]; u; u = XEXP (u, 1)) -	    add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI); - -	  for (u = deps->reg_last_sets[regno]; u; u = XEXP (u, 1)) -	    add_dependence (insn, XEXP (u, 0), REG_DEP_OUTPUT); - -	  if (code == SET) -	    { -	      free_INSN_LIST_list (&deps->reg_last_uses[regno]); -	      for (u = deps->reg_last_clobbers[regno]; u; u = XEXP (u, 1)) -		add_dependence (insn, XEXP (u, 0), REG_DEP_OUTPUT); -	      SET_REGNO_REG_SET (reg_pending_sets, regno); -	    } -	  else -	    SET_REGNO_REG_SET (reg_pending_clobbers, regno); - -	  /* Pseudos that are REG_EQUIV to something may be replaced -	     by that during reloading.  We need only add dependencies for -	     the address in the REG_EQUIV note.  */ -	  if (!reload_completed -	      && reg_known_equiv_p[regno] -	      && GET_CODE (reg_known_value[regno]) == MEM) -	    sched_analyze_2 (deps, XEXP (reg_known_value[regno], 0), insn); - -	  /* Don't let it cross a call after scheduling if it doesn't -	     already cross one.  */ - -	  if (REG_N_CALLS_CROSSED (regno) == 0) -	    for (u = deps->last_function_call; u; u = XEXP (u, 1)) -	      add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI); -	} -    } -  else if (GET_CODE (dest) == MEM) -    { -      /* Writing memory.  */ - -      if (deps->pending_lists_length > 32) -	{ -	  /* Flush all pending reads and writes to prevent the pending lists -	     from getting any larger.  Insn scheduling runs too slowly when -	     these lists get long.  The number 32 was chosen because it -	     seems like a reasonable number.  When compiling GCC with itself, -	     this flush occurs 8 times for sparc, and 10 times for m88k using -	     the number 32.  */ -	  flush_pending_lists (deps, insn, 0); -	} -      else -	{ -	  rtx u; -	  rtx pending, pending_mem; - -	  pending = deps->pending_read_insns; -	  pending_mem = deps->pending_read_mems; -	  while (pending) -	    { -	      if (anti_dependence (XEXP (pending_mem, 0), dest)) -		add_dependence (insn, XEXP (pending, 0), REG_DEP_ANTI); - -	      pending = XEXP (pending, 1); -	      pending_mem = XEXP (pending_mem, 1); -	    } - -	  pending = deps->pending_write_insns; -	  pending_mem = deps->pending_write_mems; -	  while (pending) -	    { -	      if (output_dependence (XEXP (pending_mem, 0), dest)) -		add_dependence (insn, XEXP (pending, 0), REG_DEP_OUTPUT); - -	      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); - -	  add_insn_mem_dependence (deps, &deps->pending_write_insns, -				   &deps->pending_write_mems, insn, dest); -	} -      sched_analyze_2 (deps, XEXP (dest, 0), insn); -    } - -  /* Analyze reads.  */ -  if (GET_CODE (x) == SET) -    sched_analyze_2 (deps, SET_SRC (x), insn); -} - -/* Analyze the uses of memory and registers in rtx X in INSN.  */ - -static void -sched_analyze_2 (deps, x, insn) -     struct deps *deps; -     rtx x; -     rtx insn; -{ -  register int i; -  register int j; -  register enum rtx_code code; -  register const char *fmt; - -  if (x == 0) -    return; - -  code = GET_CODE (x); - -  switch (code) -    { -    case CONST_INT: -    case CONST_DOUBLE: -    case SYMBOL_REF: -    case CONST: -    case LABEL_REF: -      /* Ignore constants.  Note that we must handle CONST_DOUBLE here -         because it may have a cc0_rtx in its CONST_DOUBLE_CHAIN field, but -         this does not mean that this insn is using cc0.  */ -      return; - -#ifdef HAVE_cc0 -    case CC0: -      /* User of CC0 depends on immediately preceding insn.  */ -      set_sched_group_p (insn); -      return; -#endif - -    case REG: -      { -	rtx u; -	int regno = REGNO (x); -	if (regno < FIRST_PSEUDO_REGISTER) -	  { -	    int i; - -	    i = HARD_REGNO_NREGS (regno, GET_MODE (x)); -	    while (--i >= 0) -	      { -		int r = regno + i; -		deps->reg_last_uses[r] -		  = alloc_INSN_LIST (insn, deps->reg_last_uses[r]); - -		for (u = deps->reg_last_sets[r]; u; u = XEXP (u, 1)) -		  add_dependence (insn, XEXP (u, 0), 0); - -		/* ??? This should never happen.  */ -		for (u = deps->reg_last_clobbers[r]; u; u = XEXP (u, 1)) -		  add_dependence (insn, XEXP (u, 0), 0); - -		if (call_used_regs[r] || global_regs[r]) -		  /* Function calls clobber all call_used regs.  */ -		  for (u = deps->last_function_call; u; u = XEXP (u, 1)) -		    add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI); -	      } -	  } -	else -	  { -	    deps->reg_last_uses[regno] -	      = alloc_INSN_LIST (insn, deps->reg_last_uses[regno]); - -	    for (u = deps->reg_last_sets[regno]; u; u = XEXP (u, 1)) -	      add_dependence (insn, XEXP (u, 0), 0); - -	    /* ??? This should never happen.  */ -	    for (u = deps->reg_last_clobbers[regno]; u; u = XEXP (u, 1)) -	      add_dependence (insn, XEXP (u, 0), 0); - -	    /* Pseudos that are REG_EQUIV to something may be replaced -	       by that during reloading.  We need only add dependencies for -	       the address in the REG_EQUIV note.  */ -	    if (!reload_completed -		&& reg_known_equiv_p[regno] -		&& GET_CODE (reg_known_value[regno]) == MEM) -	      sched_analyze_2 (deps, XEXP (reg_known_value[regno], 0), insn); - -	    /* If the register does not already cross any calls, then add this -	       insn to the sched_before_next_call list so that it will still -	       not cross calls after scheduling.  */ -	    if (REG_N_CALLS_CROSSED (regno) == 0) -	      add_dependence (deps->sched_before_next_call, insn, -			      REG_DEP_ANTI); -	  } -	return; -      } - -    case MEM: -      { -	/* Reading memory.  */ -	rtx u; -	rtx pending, pending_mem; - -	pending = deps->pending_read_insns; -	pending_mem = deps->pending_read_mems; -	while (pending) -	  { -	    if (read_dependence (XEXP (pending_mem, 0), x)) -	      add_dependence (insn, XEXP (pending, 0), REG_DEP_ANTI); - -	    pending = XEXP (pending, 1); -	    pending_mem = XEXP (pending_mem, 1); -	  } - -	pending = deps->pending_write_insns; -	pending_mem = deps->pending_write_mems; -	while (pending) -	  { -	    if (true_dependence (XEXP (pending_mem, 0), VOIDmode, -				 x, rtx_varies_p)) -	      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); - -	/* Always add these dependencies to pending_reads, since -	   this insn may be followed by a write.  */ -	add_insn_mem_dependence (deps, &deps->pending_read_insns, -				 &deps->pending_read_mems, insn, x); - -	/* Take advantage of tail recursion here.  */ -	sched_analyze_2 (deps, XEXP (x, 0), insn); -	return; -      } - -    /* Force pending stores to memory in case a trap handler needs them.  */ -    case TRAP_IF: -      flush_pending_lists (deps, insn, 1); -      break; - -    case ASM_OPERANDS: -    case ASM_INPUT: -    case UNSPEC_VOLATILE: -      { -	rtx u; - -	/* Traditional and volatile asm instructions must be considered to use -	   and clobber all hard registers, all pseudo-registers and all of -	   memory.  So must TRAP_IF and UNSPEC_VOLATILE operations. - -	   Consider for instance a volatile asm that changes the fpu rounding -	   mode.  An insn should not be moved across this even if it only uses -	   pseudo-regs because it might give an incorrectly rounded result.  */ -	if (code != ASM_OPERANDS || MEM_VOLATILE_P (x)) -	  { -	    int max_reg = max_reg_num (); -	    for (i = 0; i < max_reg; i++) -	      { -		for (u = deps->reg_last_uses[i]; u; u = XEXP (u, 1)) -		  add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI); -		free_INSN_LIST_list (&deps->reg_last_uses[i]); - -		for (u = deps->reg_last_sets[i]; u; u = XEXP (u, 1)) -		  add_dependence (insn, XEXP (u, 0), 0); - -		for (u = deps->reg_last_clobbers[i]; u; u = XEXP (u, 1)) -		  add_dependence (insn, XEXP (u, 0), 0); -	      } -	    reg_pending_sets_all = 1; - -	    flush_pending_lists (deps, insn, 0); -	  } - -	/* For all ASM_OPERANDS, we must traverse the vector of input operands. -	   We can not just fall through here since then we would be confused -	   by the ASM_INPUT rtx inside ASM_OPERANDS, which do not indicate -	   traditional asms unlike their normal usage.  */ - -	if (code == ASM_OPERANDS) -	  { -	    for (j = 0; j < ASM_OPERANDS_INPUT_LENGTH (x); j++) -	      sched_analyze_2 (deps, ASM_OPERANDS_INPUT (x, j), insn); -	    return; -	  } -	break; -      } - -    case PRE_DEC: -    case POST_DEC: -    case PRE_INC: -    case POST_INC: -      /* These both read and modify the result.  We must handle them as writes -         to get proper dependencies for following instructions.  We must handle -         them as reads to get proper dependencies from this to previous -         instructions.  Thus we need to pass them to both sched_analyze_1 -         and sched_analyze_2.  We must call sched_analyze_2 first in order -         to get the proper antecedent for the read.  */ -      sched_analyze_2 (deps, XEXP (x, 0), insn); -      sched_analyze_1 (deps, x, insn); -      return; - -    case POST_MODIFY: -    case PRE_MODIFY: -      /* op0 = op0 + op1 */ -      sched_analyze_2 (deps, XEXP (x, 0), insn); -      sched_analyze_2 (deps, XEXP (x, 1), insn); -      sched_analyze_1 (deps, x, insn); -      return; - -    default: -      break; -    } - -  /* Other cases: walk the insn.  */ -  fmt = GET_RTX_FORMAT (code); -  for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) -    { -      if (fmt[i] == 'e') -	sched_analyze_2 (deps, XEXP (x, i), insn); -      else if (fmt[i] == 'E') -	for (j = 0; j < XVECLEN (x, i); j++) -	  sched_analyze_2 (deps, XVECEXP (x, i, j), insn); -    } -} - -/* Analyze an INSN with pattern X to find all dependencies.  */ - -static void -sched_analyze_insn (deps, x, insn, loop_notes) -     struct deps *deps; -     rtx x, insn; -     rtx loop_notes; -{ -  register RTX_CODE code = GET_CODE (x); -  rtx link; -  int maxreg = max_reg_num (); -  int i; - -  if (code == COND_EXEC) -    { -      sched_analyze_2 (deps, COND_EXEC_TEST (x), insn); - -      /* ??? Should be recording conditions so we reduce the number of -	 false dependancies.  */ -      x = COND_EXEC_CODE (x); -      code = GET_CODE (x); -    } -  if (code == SET || code == CLOBBER) -    sched_analyze_1 (deps, x, insn); -  else if (code == PARALLEL) -    { -      register int i; -      for (i = XVECLEN (x, 0) - 1; i >= 0; i--) -	{ -	  rtx sub = XVECEXP (x, 0, i); -	  code = GET_CODE (sub); - -	  if (code == COND_EXEC) -	    { -	      sched_analyze_2 (deps, COND_EXEC_TEST (sub), insn); -	      sub = COND_EXEC_CODE (sub); -	      code = GET_CODE (sub); -	    } -	  if (code == SET || code == CLOBBER) -	    sched_analyze_1 (deps, sub, insn); -	  else -	    sched_analyze_2 (deps, sub, insn); -	} -    } -  else -    sched_analyze_2 (deps, x, insn); - -  /* Mark registers CLOBBERED or used by called function.  */ -  if (GET_CODE (insn) == CALL_INSN) -    for (link = CALL_INSN_FUNCTION_USAGE (insn); link; link = XEXP (link, 1)) -      { -	if (GET_CODE (XEXP (link, 0)) == CLOBBER) -	  sched_analyze_1 (deps, XEXP (link, 0), insn); -	else -	  sched_analyze_2 (deps, XEXP (link, 0), insn); -      } - -  /* 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 -     become incorrect.  */ - -  if (loop_notes) -    { -      int max_reg = max_reg_num (); -      int schedule_barrier_found = 0; -      rtx link; - -      /* Update loop_notes with any notes from this insn.  Also determine -	 if any of the notes on the list correspond to instruction scheduling -	 barriers (loop, eh & setjmp notes, but not range notes.  */ -      link = loop_notes; -      while (XEXP (link, 1)) -	{ -	  if (INTVAL (XEXP (link, 0)) == NOTE_INSN_LOOP_BEG -	      || INTVAL (XEXP (link, 0)) == NOTE_INSN_LOOP_END -	      || INTVAL (XEXP (link, 0)) == NOTE_INSN_EH_REGION_BEG -	      || INTVAL (XEXP (link, 0)) == NOTE_INSN_EH_REGION_END -	      || INTVAL (XEXP (link, 0)) == NOTE_INSN_SETJMP) -	    schedule_barrier_found = 1; - -	  link = XEXP (link, 1); -	} -      XEXP (link, 1) = REG_NOTES (insn); -      REG_NOTES (insn) = loop_notes; - -      /* Add dependencies if a scheduling barrier was found.  */ -      if (schedule_barrier_found) -	{ -	  for (i = 0; i < max_reg; i++) -	    { -	      rtx u; -	      for (u = deps->reg_last_uses[i]; u; u = XEXP (u, 1)) -		add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI); -	      free_INSN_LIST_list (&deps->reg_last_uses[i]); - -	      for (u = deps->reg_last_sets[i]; u; u = XEXP (u, 1)) -		add_dependence (insn, XEXP (u, 0), 0); - -	      for (u = deps->reg_last_clobbers[i]; u; u = XEXP (u, 1)) -		add_dependence (insn, XEXP (u, 0), 0); -	    } -	  reg_pending_sets_all = 1; - -	  flush_pending_lists (deps, insn, 0); -	} - -    } - -  /* Accumulate clobbers until the next set so that it will be output dependent -     on all of them.  At the next set we can clear the clobber list, since -     subsequent sets will be output dependent on it.  */ -  EXECUTE_IF_SET_IN_REG_SET -    (reg_pending_sets, 0, i, -     { -       free_INSN_LIST_list (&deps->reg_last_sets[i]); -       free_INSN_LIST_list (&deps->reg_last_clobbers[i]); -       deps->reg_last_sets[i] = alloc_INSN_LIST (insn, NULL_RTX); -     }); -  EXECUTE_IF_SET_IN_REG_SET -    (reg_pending_clobbers, 0, i, -     { -       deps->reg_last_clobbers[i] -	 = alloc_INSN_LIST (insn, deps->reg_last_clobbers[i]); -     }); -  CLEAR_REG_SET (reg_pending_sets); -  CLEAR_REG_SET (reg_pending_clobbers); - -  if (reg_pending_sets_all) -    { -      for (i = 0; i < maxreg; i++) -	{ -	  free_INSN_LIST_list (&deps->reg_last_sets[i]); -	  free_INSN_LIST_list (&deps->reg_last_clobbers[i]); -	  deps->reg_last_sets[i] = alloc_INSN_LIST (insn, NULL_RTX); -	} - -      reg_pending_sets_all = 0; -    } - -  /* If a post-call group is still open, see if it should remain so. -     This insn must be a simple move of a hard reg to a pseudo or -     vice-versa. - -     We must avoid moving these insns for correctness on -     SMALL_REGISTER_CLASS machines, and for special registers like -     PIC_OFFSET_TABLE_REGNUM.  For simplicity, extend this to all -     hard regs for all targets.  */ - -  if (deps->in_post_call_group_p) -    { -      rtx tmp, set = single_set (insn); -      int src_regno, dest_regno; - -      if (set == NULL) -	goto end_call_group; - -      tmp = SET_DEST (set); -      if (GET_CODE (tmp) == SUBREG) -	tmp = SUBREG_REG (tmp); -      if (GET_CODE (tmp) == REG) -	dest_regno = REGNO (tmp); -      else -	goto end_call_group; - -      tmp = SET_SRC (set); -      if (GET_CODE (tmp) == SUBREG) -	tmp = SUBREG_REG (tmp); -      if (GET_CODE (tmp) == REG) -	src_regno = REGNO (tmp); -      else -	goto end_call_group; - -      if (src_regno < FIRST_PSEUDO_REGISTER -	  || dest_regno < FIRST_PSEUDO_REGISTER) -	{ -	  set_sched_group_p (insn); -	  CANT_MOVE (insn) = 1; -	} -      else -	{ -	end_call_group: -	  deps->in_post_call_group_p = 0; -	} -    } -} - -/* Analyze every insn between HEAD and TAIL inclusive, creating LOG_LINKS -   for every dependency.  */ - -static void -sched_analyze (deps, head, tail) -     struct deps *deps; -     rtx head, tail; -{ -  register rtx insn; -  register rtx u; -  rtx loop_notes = 0; - -  for (insn = head;; insn = NEXT_INSN (insn)) -    { -      if (GET_CODE (insn) == INSN || GET_CODE (insn) == JUMP_INSN) -	{ -	  /* Clear out the stale LOG_LINKS from flow.  */ -	  free_INSN_LIST_list (&LOG_LINKS (insn)); - -	  /* Clear out stale SCHED_GROUP_P.  */ -	  SCHED_GROUP_P (insn) = 0; - -	  /* Make each JUMP_INSN a scheduling barrier for memory -             references.  */ -	  if (GET_CODE (insn) == JUMP_INSN) -	    deps->last_pending_memory_flush -	      = alloc_INSN_LIST (insn, deps->last_pending_memory_flush); -	  sched_analyze_insn (deps, PATTERN (insn), insn, loop_notes); -	  loop_notes = 0; -	} -      else if (GET_CODE (insn) == CALL_INSN) -	{ -	  rtx x; -	  register int i; - -	  /* Clear out stale SCHED_GROUP_P.  */ -	  SCHED_GROUP_P (insn) = 0; - -	  CANT_MOVE (insn) = 1; - -	  /* Clear out the stale LOG_LINKS from flow.  */ -	  free_INSN_LIST_list (&LOG_LINKS (insn)); - -	  /* Any instruction using a hard register which may get clobbered -	     by a call needs to be marked as dependent on this call. -	     This prevents a use of a hard return reg from being moved -	     past a void call (i.e. it does not explicitly set the hard -	     return reg).  */ - -	  /* If this call is followed by a NOTE_INSN_SETJMP, then assume that -	     all registers, not just hard registers, may be clobbered by this -	     call.  */ - -	  /* Insn, being a CALL_INSN, magically depends on -	     `last_function_call' already.  */ - -	  if (NEXT_INSN (insn) && GET_CODE (NEXT_INSN (insn)) == NOTE -	      && NOTE_LINE_NUMBER (NEXT_INSN (insn)) == NOTE_INSN_SETJMP) -	    { -	      int max_reg = max_reg_num (); -	      for (i = 0; i < max_reg; i++) -		{ -		  for (u = deps->reg_last_uses[i]; u; u = XEXP (u, 1)) -		    add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI); -		  free_INSN_LIST_list (&deps->reg_last_uses[i]); - -		  for (u = deps->reg_last_sets[i]; u; u = XEXP (u, 1)) -		    add_dependence (insn, XEXP (u, 0), 0); - -		  for (u = deps->reg_last_clobbers[i]; u; u = XEXP (u, 1)) -		    add_dependence (insn, XEXP (u, 0), 0); -		} -	      reg_pending_sets_all = 1; - -	      /* Add a pair of REG_SAVE_NOTEs which we will later -		 convert back into a NOTE_INSN_SETJMP note.  See -		 reemit_notes for why we use a pair of NOTEs.  */ -	      REG_NOTES (insn) = alloc_EXPR_LIST (REG_SAVE_NOTE, -						  GEN_INT (0), -						  REG_NOTES (insn)); -	      REG_NOTES (insn) = alloc_EXPR_LIST (REG_SAVE_NOTE, -						  GEN_INT (NOTE_INSN_SETJMP), -						  REG_NOTES (insn)); -	    } -	  else -	    { -	      for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) -		if (call_used_regs[i] || global_regs[i]) -		  { -		    for (u = deps->reg_last_uses[i]; u; u = XEXP (u, 1)) -		      add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI); - -		    for (u = deps->reg_last_sets[i]; u; u = XEXP (u, 1)) -		      add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI); - -		    SET_REGNO_REG_SET (reg_pending_clobbers, i); -		  } -	    } - -	  /* For each insn which shouldn't cross a call, add a dependence -	     between that insn and this call insn.  */ -	  x = LOG_LINKS (deps->sched_before_next_call); -	  while (x) -	    { -	      add_dependence (insn, XEXP (x, 0), REG_DEP_ANTI); -	      x = XEXP (x, 1); -	    } -	  free_INSN_LIST_list (&LOG_LINKS (deps->sched_before_next_call)); - -	  sched_analyze_insn (deps, PATTERN (insn), insn, loop_notes); -	  loop_notes = 0; - -	  /* In the absence of interprocedural alias analysis, we must flush -	     all pending reads and writes, and start new dependencies starting -	     from here.  But only flush writes for constant calls (which may -	     be passed a pointer to something we haven't written yet).  */ -	  flush_pending_lists (deps, insn, CONST_CALL_P (insn)); - -	  /* Depend this function call (actually, the user of this -	     function call) on all hard register clobberage.  */ - -	  /* last_function_call is now a list of insns.  */ -	  free_INSN_LIST_list (&deps->last_function_call); -	  deps->last_function_call = alloc_INSN_LIST (insn, NULL_RTX); - -	  /* Before reload, begin a post-call group, so as to keep the -	     lifetimes of hard registers correct.  */ -	  if (! reload_completed) -	    deps->in_post_call_group_p = 1; -	} - -      /* See comments on reemit_notes as to why we do this. -	 ??? Actually, the reemit_notes just say what is done, not why.  */ - -      else if (GET_CODE (insn) == NOTE -	       && (NOTE_LINE_NUMBER (insn) == NOTE_INSN_RANGE_BEG -		   || NOTE_LINE_NUMBER (insn) == NOTE_INSN_RANGE_END)) -	{ -	  loop_notes = alloc_EXPR_LIST (REG_SAVE_NOTE, NOTE_RANGE_INFO (insn), -					loop_notes); -	  loop_notes = alloc_EXPR_LIST (REG_SAVE_NOTE, -					GEN_INT (NOTE_LINE_NUMBER (insn)), -					loop_notes); -	} -      else if (GET_CODE (insn) == NOTE -	       && (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_BEG -		   || NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_END -		   || NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_BEG -		   || NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_END -		   || (NOTE_LINE_NUMBER (insn) == NOTE_INSN_SETJMP -		       && GET_CODE (PREV_INSN (insn)) != CALL_INSN))) -	{ -	  rtx rtx_region; - -	  if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_BEG -	      || NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_END) -	    rtx_region = GEN_INT (NOTE_EH_HANDLER (insn)); -	  else -	    rtx_region = GEN_INT (0); - -	  loop_notes = alloc_EXPR_LIST (REG_SAVE_NOTE, -					rtx_region, -					loop_notes); -	  loop_notes = alloc_EXPR_LIST (REG_SAVE_NOTE, -					GEN_INT (NOTE_LINE_NUMBER (insn)), -					loop_notes); -	  CONST_CALL_P (loop_notes) = CONST_CALL_P (insn); -	} - -      if (insn == tail) -	return; -    } -  abort (); -}  /* Macros and functions for keeping the priority queue sorted, and     dealing with queueing and dequeueing of instructions.  */ @@ -5305,25 +3973,6 @@ move_insn (insn, last)    return retval;  } -/* Return an insn which represents a SCHED_GROUP, which is -   the last insn in the group.  */ - -static rtx -group_leader (insn) -     rtx insn; -{ -  rtx prev; - -  do -    { -      prev = insn; -      insn = next_nonnote_insn (insn); -    } -  while (insn && SCHED_GROUP_P (insn) && (GET_CODE (insn) != CODE_LABEL)); - -  return prev; -} -  /* Use forward list scheduling to rearrange insns of block BB in region RGN,     possibly bringing insns from subsequent blocks in the same region.  */ @@ -5545,117 +4194,6 @@ debug_reg_vector (s)    fprintf (sched_dump, "\n");  } -/* Examine insns in the range [ HEAD, TAIL ] and Use the backward -   dependences from LOG_LINKS to build forward dependences in -   INSN_DEPEND.  */ - -static void -compute_forward_dependences (head, tail) -     rtx head, tail; -{ -  rtx insn, link; -  rtx next_tail; -  enum reg_note dep_type; - -  next_tail = NEXT_INSN (tail); -  for (insn = head; insn != next_tail; insn = NEXT_INSN (insn)) -    { -      if (! INSN_P (insn)) -	continue; - -      insn = group_leader (insn); - -      for (link = LOG_LINKS (insn); link; link = XEXP (link, 1)) -	{ -	  rtx x = group_leader (XEXP (link, 0)); -	  rtx new_link; - -	  if (x != XEXP (link, 0)) -	    continue; - -#ifdef ENABLE_CHECKING -	  /* If add_dependence is working properly there should never -	     be notes, deleted insns or duplicates in the backward -	     links.  Thus we need not check for them here. - -	     However, if we have enabled checking we might as well go -	     ahead and verify that add_dependence worked properly.  */ -	  if (GET_CODE (x) == NOTE -	      || INSN_DELETED_P (x) -	      || (forward_dependency_cache != NULL -		  && TEST_BIT (forward_dependency_cache[INSN_LUID (x)], -			       INSN_LUID (insn))) -	      || (forward_dependency_cache == NULL -		  && find_insn_list (insn, INSN_DEPEND (x)))) -	    abort (); -	  if (forward_dependency_cache != NULL) -	    SET_BIT (forward_dependency_cache[INSN_LUID (x)], -		     INSN_LUID (insn)); -#endif - -	  new_link = alloc_INSN_LIST (insn, INSN_DEPEND (x)); - -	  dep_type = REG_NOTE_KIND (link); -	  PUT_REG_NOTE_KIND (new_link, dep_type); - -	  INSN_DEPEND (x) = new_link; -	  INSN_DEP_COUNT (insn) += 1; -	} -    } -} - -/* Initialize variables for region data dependence analysis. -   n_bbs is the number of region blocks.  */ - -static void -init_deps (deps) -     struct deps *deps; -{ -  int maxreg = max_reg_num (); -  deps->reg_last_uses = (rtx *) xcalloc (maxreg, sizeof (rtx)); -  deps->reg_last_sets = (rtx *) xcalloc (maxreg, sizeof (rtx)); -  deps->reg_last_clobbers = (rtx *) xcalloc (maxreg, sizeof (rtx)); - -  deps->pending_read_insns = 0; -  deps->pending_read_mems = 0; -  deps->pending_write_insns = 0; -  deps->pending_write_mems = 0; -  deps->pending_lists_length = 0; -  deps->last_pending_memory_flush = 0; -  deps->last_function_call = 0; -  deps->in_post_call_group_p = 0; - -  deps->sched_before_next_call -    = gen_rtx_INSN (VOIDmode, 0, NULL_RTX, NULL_RTX, -		    NULL_RTX, 0, NULL_RTX, NULL_RTX); -  LOG_LINKS (deps->sched_before_next_call) = 0; -} - -/* Free insn lists found in DEPS.  */ - -static void -free_deps (deps) -     struct deps *deps; -{ -  int max_reg = max_reg_num (); -  int i; - -  /* Note this loop is executed max_reg * nr_regions times.  It's first -     implementation accounted for over 90% of the calls to free_INSN_LIST_list. -     The list was empty for the vast majority of those calls.  On the PA, not -     calling free_INSN_LIST_list in those cases improves -O2 compile times by -     3-5% on average.  */ -  for (i = 0; i < max_reg; ++i) -    { -      if (deps->reg_last_clobbers[i]) -	free_INSN_LIST_list (&deps->reg_last_clobbers[i]); -      if (deps->reg_last_sets[i]) -	free_INSN_LIST_list (&deps->reg_last_sets[i]); -      if (deps->reg_last_uses[i]) -	free_INSN_LIST_list (&deps->reg_last_uses[i]); -    } -} -  /* Add dependences so that branches are scheduled to run last in their     block.  */ @@ -6051,16 +4589,12 @@ schedule_region (rgn)    int bb;    int rgn_n_insns = 0;    int sched_rgn_n_insns = 0; -  regset_head reg_pending_sets_head; -  regset_head reg_pending_clobbers_head;    /* Set variables for the current region.  */    current_nr_blocks = RGN_NR_BLOCKS (rgn);    current_blocks = RGN_BLOCKS (rgn); -  reg_pending_sets = INITIALIZE_REG_SET (reg_pending_sets_head); -  reg_pending_clobbers = INITIALIZE_REG_SET (reg_pending_clobbers_head); -  reg_pending_sets_all = 0; +  init_deps_global ();    /* Initializations for region data dependence analyisis.  */    bb_deps = (struct deps *) xmalloc (sizeof (struct deps) * current_nr_blocks); @@ -6218,8 +4752,7 @@ schedule_region (rgn)    /* Done with this region.  */    free_pending_lists (); -  FREE_REG_SET (reg_pending_sets); -  FREE_REG_SET (reg_pending_clobbers); +  finish_deps_global ();    free (bb_deps); diff --git a/gcc/sched-deps.c b/gcc/sched-deps.c new file mode 100644 index 00000000000..7f9914cdd82 --- /dev/null +++ b/gcc/sched-deps.c @@ -0,0 +1,1399 @@ +/* Instruction scheduling pass.  This file computes dependencies between +   instructions. +   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.  */ + +#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" + +extern char *reg_known_equiv_p; +extern rtx *reg_known_value; + +static regset_head reg_pending_sets_head; +static regset_head reg_pending_clobbers_head; + +static regset reg_pending_sets; +static regset reg_pending_clobbers; +static int reg_pending_sets_all; + +/* To speed up the test for duplicate dependency links we keep a +   record of dependencies created by add_dependence when the average +   number of instructions in a basic block is very large. + +   Studies have shown that there is typically around 5 instructions between +   branches for typical C code.  So we can make a guess that the average +   basic block is approximately 5 instructions long; we will choose 100X +   the average size as a very large basic block. + +   Each insn has associated bitmaps for its dependencies.  Each bitmap +   has enough entries to represent a dependency on any other insn in +   the insn chain.  All bitmap for true dependencies cache is +   allocated then the rest two ones are also allocated. */ +static sbitmap *true_dependency_cache; +static sbitmap *anti_dependency_cache; +static sbitmap *output_dependency_cache; + +/* To speed up checking consistency of formed forward insn +   dependencies we use the following cache.  Another possible solution +   could be switching off checking duplication of insns in forward +   dependencies. */ +#ifdef ENABLE_CHECKING +static sbitmap *forward_dependency_cache; +#endif + +static void remove_dependence PARAMS ((rtx, rtx)); +static void set_sched_group_p PARAMS ((rtx)); + +static void flush_pending_lists PARAMS ((struct deps *, rtx, int)); +static void sched_analyze_1 PARAMS ((struct deps *, rtx, rtx)); +static void sched_analyze_2 PARAMS ((struct deps *, rtx, rtx)); +static void sched_analyze_insn PARAMS ((struct deps *, rtx, rtx, rtx)); +static rtx group_leader PARAMS ((rtx)); + +/* Return the INSN_LIST containing INSN in LIST, or NULL +   if LIST does not contain INSN.  */ + +HAIFA_INLINE rtx +find_insn_list (insn, list) +     rtx insn; +     rtx list; +{ +  while (list) +    { +      if (XEXP (list, 0) == insn) +	return list; +      list = XEXP (list, 1); +    } +  return 0; +} + +/* Return 1 if the pair (insn, x) is found in (LIST, LIST1), or 0 +   otherwise.  */ + +HAIFA_INLINE int +find_insn_mem_list (insn, x, list, list1) +     rtx insn, x; +     rtx list, list1; +{ +  while (list) +    { +      if (XEXP (list, 0) == insn +	  && XEXP (list1, 0) == x) +	return 1; +      list = XEXP (list, 1); +      list1 = XEXP (list1, 1); +    } +  return 0; +} + +/* Add ELEM wrapped in an INSN_LIST with reg note kind DEP_TYPE to the +   LOG_LINKS of INSN, if not already there.  DEP_TYPE indicates the type +   of dependence that this link represents.  */ + +void +add_dependence (insn, elem, dep_type) +     rtx insn; +     rtx elem; +     enum reg_note dep_type; +{ +  rtx link, next; +  int present_p; +  enum reg_note present_dep_type; + +  /* Don't depend an insn on itself.  */ +  if (insn == elem) +    return; + +  /* We can get a dependency on deleted insns due to optimizations in +     the register allocation and reloading or due to splitting.  Any +     such dependency is useless and can be ignored.  */ +  if (GET_CODE (elem) == NOTE) +    return; + +  /* If elem is part of a sequence that must be scheduled together, then +     make the dependence point to the last insn of the sequence. +     When HAVE_cc0, it is possible for NOTEs to exist between users and +     setters of the condition codes, so we must skip past notes here. +     Otherwise, NOTEs are impossible here.  */ +  next = next_nonnote_insn (elem); +  if (next && SCHED_GROUP_P (next) +      && GET_CODE (next) != CODE_LABEL) +    { +      /* Notes will never intervene here though, so don't bother checking +         for them.  */ +      /* Hah!  Wrong.  */ +      /* We must reject CODE_LABELs, so that we don't get confused by one +         that has LABEL_PRESERVE_P set, which is represented by the same +         bit in the rtl as SCHED_GROUP_P.  A CODE_LABEL can never be +         SCHED_GROUP_P.  */ + +      rtx nnext; +      while ((nnext = next_nonnote_insn (next)) != NULL +	     && SCHED_GROUP_P (nnext) +	     && GET_CODE (nnext) != CODE_LABEL) +	next = nnext; + +      /* Again, don't depend an insn on itself.  */ +      if (insn == next) +	return; + +      /* Make the dependence to NEXT, the last insn of the group, instead +         of the original ELEM.  */ +      elem = next; +    } + +  present_p = 1; +#ifdef INSN_SCHEDULING +  /* ??? No good way to tell from here whether we're doing interblock +     scheduling.  Possibly add another callback.  */ +#if 0 +  /* (This code is guarded by INSN_SCHEDULING, otherwise INSN_BB is undefined.) +     No need for interblock dependences with calls, since +     calls are not moved between blocks.   Note: the edge where +     elem is a CALL is still required.  */ +  if (GET_CODE (insn) == CALL_INSN +      && (INSN_BB (elem) != INSN_BB (insn))) +    return; +#endif + +  /* If we already have a dependency for ELEM, then we do not need to +     do anything.  Avoiding the list walk below can cut compile times +     dramatically for some code.  */ +  if (true_dependency_cache != NULL) +    { +      if (anti_dependency_cache == NULL || output_dependency_cache == NULL) +	abort (); +      if (TEST_BIT (true_dependency_cache[INSN_LUID (insn)], INSN_LUID (elem))) +	present_dep_type = 0; +      else if (TEST_BIT (anti_dependency_cache[INSN_LUID (insn)], +			 INSN_LUID (elem))) +	present_dep_type = REG_DEP_ANTI; +      else if (TEST_BIT (output_dependency_cache[INSN_LUID (insn)], +			 INSN_LUID (elem))) +	present_dep_type = REG_DEP_OUTPUT; +      else  +	present_p = 0; +      if (present_p && (int) dep_type >= (int) present_dep_type) +	return; +    } +#endif + +  /* Check that we don't already have this dependence.  */ +  if (present_p) +    for (link = LOG_LINKS (insn); link; link = XEXP (link, 1)) +      if (XEXP (link, 0) == elem) +	{ +#ifdef INSN_SCHEDULING +	  /* Clear corresponding cache entry because type of the link +             may be changed. */ +	  if (true_dependency_cache != NULL) +	    { +	      if (REG_NOTE_KIND (link) == REG_DEP_ANTI) +		RESET_BIT (anti_dependency_cache[INSN_LUID (insn)], +			   INSN_LUID (elem)); +	      else if (REG_NOTE_KIND (link) == REG_DEP_OUTPUT +		       && output_dependency_cache) +		RESET_BIT (output_dependency_cache[INSN_LUID (insn)], +			   INSN_LUID (elem)); +	      else +		abort (); +	    } +#endif + +	  /* If this is a more restrictive type of dependence than the existing +	     one, then change the existing dependence to this type.  */ +	  if ((int) dep_type < (int) REG_NOTE_KIND (link)) +	    PUT_REG_NOTE_KIND (link, dep_type); +	   +#ifdef INSN_SCHEDULING +	  /* If we are adding a dependency to INSN's LOG_LINKs, then +	     note that in the bitmap caches of dependency information. */ +	  if (true_dependency_cache != NULL) +	    { +	      if ((int)REG_NOTE_KIND (link) == 0) +		SET_BIT (true_dependency_cache[INSN_LUID (insn)], +			 INSN_LUID (elem)); +	      else if (REG_NOTE_KIND (link) == REG_DEP_ANTI) +		SET_BIT (anti_dependency_cache[INSN_LUID (insn)], +			 INSN_LUID (elem)); +	      else if (REG_NOTE_KIND (link) == REG_DEP_OUTPUT) +		SET_BIT (output_dependency_cache[INSN_LUID (insn)], +			 INSN_LUID (elem)); +	    } +#endif +	  return; +      } +  /* Might want to check one level of transitivity to save conses.  */ + +  link = alloc_INSN_LIST (elem, LOG_LINKS (insn)); +  LOG_LINKS (insn) = link; + +  /* Insn dependency, not data dependency.  */ +  PUT_REG_NOTE_KIND (link, dep_type); + +#ifdef INSN_SCHEDULING +  /* If we are adding a dependency to INSN's LOG_LINKs, then note that +     in the bitmap caches of dependency information. */ +  if (true_dependency_cache != NULL) +    { +      if ((int)dep_type == 0) +	SET_BIT (true_dependency_cache[INSN_LUID (insn)], INSN_LUID (elem)); +      else if (dep_type == REG_DEP_ANTI) +	SET_BIT (anti_dependency_cache[INSN_LUID (insn)], INSN_LUID (elem)); +      else if (dep_type == REG_DEP_OUTPUT) +	SET_BIT (output_dependency_cache[INSN_LUID (insn)], INSN_LUID (elem)); +    } +#endif +} + +/* Remove ELEM wrapped in an INSN_LIST from the LOG_LINKS +   of INSN.  Abort if not found.  */ + +static void +remove_dependence (insn, elem) +     rtx insn; +     rtx elem; +{ +  rtx prev, link, next; +  int found = 0; + +  for (prev = 0, link = LOG_LINKS (insn); link; link = next) +    { +      next = XEXP (link, 1); +      if (XEXP (link, 0) == elem) +	{ +	  if (prev) +	    XEXP (prev, 1) = next; +	  else +	    LOG_LINKS (insn) = next; + +#ifdef INSN_SCHEDULING +	  /* If we are removing a dependency from the LOG_LINKS list, +	     make sure to remove it from the cache too.  */ +	  if (true_dependency_cache != NULL) +	    { +	      if (REG_NOTE_KIND (link) == 0) +		RESET_BIT (true_dependency_cache[INSN_LUID (insn)], +			   INSN_LUID (elem)); +	      else if (REG_NOTE_KIND (link) == REG_DEP_ANTI) +		RESET_BIT (anti_dependency_cache[INSN_LUID (insn)], +			   INSN_LUID (elem)); +	      else if (REG_NOTE_KIND (link) == REG_DEP_OUTPUT) +		RESET_BIT (output_dependency_cache[INSN_LUID (insn)], +			   INSN_LUID (elem)); +	    } +#endif + +	  free_INSN_LIST_node (link); + +	  found = 1; +	} +      else +	prev = link; +    } + +  if (!found) +    abort (); +  return; +} + +/* Return an insn which represents a SCHED_GROUP, which is +   the last insn in the group.  */ + +static rtx +group_leader (insn) +     rtx insn; +{ +  rtx prev; + +  do +    { +      prev = insn; +      insn = next_nonnote_insn (insn); +    } +  while (insn && SCHED_GROUP_P (insn) && (GET_CODE (insn) != CODE_LABEL)); + +  return prev; +} + +/* Set SCHED_GROUP_P and care for the rest of the bookkeeping that +   goes along with that.  */ + +static void +set_sched_group_p (insn) +     rtx insn; +{ +  rtx link, prev; + +  SCHED_GROUP_P (insn) = 1; + +  /* There may be a note before this insn now, but all notes will +     be removed before we actually try to schedule the insns, so +     it won't cause a problem later.  We must avoid it here though.  */ +  prev = prev_nonnote_insn (insn); + +  /* Make a copy of all dependencies on the immediately previous insn, +     and add to this insn.  This is so that all the dependencies will +     apply to the group.  Remove an explicit dependence on this insn +     as SCHED_GROUP_P now represents it.  */ + +  if (find_insn_list (prev, LOG_LINKS (insn))) +    remove_dependence (insn, prev); + +  for (link = LOG_LINKS (prev); link; link = XEXP (link, 1)) +    add_dependence (insn, XEXP (link, 0), REG_NOTE_KIND (link)); +} + +/* Process an insn's memory dependencies.  There are four kinds of +   dependencies: + +   (0) read dependence: read follows read +   (1) true dependence: read follows write +   (2) anti dependence: write follows read +   (3) output dependence: write follows write + +   We are careful to build only dependencies which actually exist, and +   use transitivity to avoid building too many links.  */ + +/* Add an INSN and MEM reference pair to a pending INSN_LIST and MEM_LIST. +   The MEM is a memory reference contained within INSN, which we are saving +   so that we can do memory aliasing on it.  */ + +void +add_insn_mem_dependence (deps, insn_list, mem_list, insn, mem) +     struct deps *deps; +     rtx *insn_list, *mem_list, insn, mem; +{ +  register rtx link; + +  link = alloc_INSN_LIST (insn, *insn_list); +  *insn_list = link; + +  link = alloc_EXPR_LIST (VOIDmode, mem, *mem_list); +  *mem_list = link; + +  deps->pending_lists_length++; +} + +/* Make a dependency between every memory reference on the pending lists +   and INSN, thus flushing the pending lists.  If ONLY_WRITE, don't flush +   the read list.  */ + +static void +flush_pending_lists (deps, insn, only_write) +     struct deps *deps; +     rtx insn; +     int only_write; +{ +  rtx u; +  rtx link; + +  while (deps->pending_read_insns && ! only_write) +    { +      add_dependence (insn, XEXP (deps->pending_read_insns, 0), +		      REG_DEP_ANTI); + +      link = deps->pending_read_insns; +      deps->pending_read_insns = XEXP (deps->pending_read_insns, 1); +      free_INSN_LIST_node (link); + +      link = deps->pending_read_mems; +      deps->pending_read_mems = XEXP (deps->pending_read_mems, 1); +      free_EXPR_LIST_node (link); +    } +  while (deps->pending_write_insns) +    { +      add_dependence (insn, XEXP (deps->pending_write_insns, 0), +		      REG_DEP_ANTI); + +      link = deps->pending_write_insns; +      deps->pending_write_insns = XEXP (deps->pending_write_insns, 1); +      free_INSN_LIST_node (link); + +      link = deps->pending_write_mems; +      deps->pending_write_mems = XEXP (deps->pending_write_mems, 1); +      free_EXPR_LIST_node (link); +    } +  deps->pending_lists_length = 0; + +  /* last_pending_memory_flush is now a list of insns.  */ +  for (u = deps->last_pending_memory_flush; u; u = XEXP (u, 1)) +    add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI); + +  free_INSN_LIST_list (&deps->last_pending_memory_flush); +  deps->last_pending_memory_flush = alloc_INSN_LIST (insn, NULL_RTX); +} + +/* Analyze a single SET, CLOBBER, PRE_DEC, POST_DEC, PRE_INC or POST_INC +   rtx, X, creating all dependencies generated by the write to the +   destination of X, and reads of everything mentioned.  */ + +static void +sched_analyze_1 (deps, x, insn) +     struct deps *deps; +     rtx x; +     rtx insn; +{ +  register int regno; +  register rtx dest = XEXP (x, 0); +  enum rtx_code code = GET_CODE (x); + +  if (dest == 0) +    return; + +  if (GET_CODE (dest) == PARALLEL +      && GET_MODE (dest) == BLKmode) +    { +      register int i; +      for (i = XVECLEN (dest, 0) - 1; i >= 0; i--) +	sched_analyze_1 (deps, XVECEXP (dest, 0, i), insn); +      if (GET_CODE (x) == SET) +	sched_analyze_2 (deps, SET_SRC (x), insn); +      return; +    } + +  while (GET_CODE (dest) == STRICT_LOW_PART || GET_CODE (dest) == SUBREG +	 || GET_CODE (dest) == ZERO_EXTRACT || GET_CODE (dest) == SIGN_EXTRACT) +    { +      if (GET_CODE (dest) == ZERO_EXTRACT || GET_CODE (dest) == SIGN_EXTRACT) +	{ +	  /* The second and third arguments are values read by this insn.  */ +	  sched_analyze_2 (deps, XEXP (dest, 1), insn); +	  sched_analyze_2 (deps, XEXP (dest, 2), insn); +	} +      dest = XEXP (dest, 0); +    } + +  if (GET_CODE (dest) == REG) +    { +      register int i; + +      regno = REGNO (dest); + +      /* A hard reg in a wide mode may really be multiple registers. +         If so, mark all of them just like the first.  */ +      if (regno < FIRST_PSEUDO_REGISTER) +	{ +	  i = HARD_REGNO_NREGS (regno, GET_MODE (dest)); +	  while (--i >= 0) +	    { +	      int r = regno + i; +	      rtx u; + +	      for (u = deps->reg_last_uses[r]; u; u = XEXP (u, 1)) +		add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI); + +	      for (u = deps->reg_last_sets[r]; u; u = XEXP (u, 1)) +		add_dependence (insn, XEXP (u, 0), REG_DEP_OUTPUT); + +	      /* Clobbers need not be ordered with respect to one +		 another, but sets must be ordered with respect to a +		 pending clobber.  */ +	      if (code == SET) +		{ +		  free_INSN_LIST_list (&deps->reg_last_uses[r]); +		  for (u = deps->reg_last_clobbers[r]; u; u = XEXP (u, 1)) +		    add_dependence (insn, XEXP (u, 0), REG_DEP_OUTPUT); +		  SET_REGNO_REG_SET (reg_pending_sets, r); +		} +	      else +		SET_REGNO_REG_SET (reg_pending_clobbers, r); + +	      /* Function calls clobber all call_used regs.  */ +	      if (global_regs[r] || (code == SET && call_used_regs[r])) +		for (u = deps->last_function_call; u; u = XEXP (u, 1)) +		  add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI); +	    } +	} +      else +	{ +	  rtx u; + +	  for (u = deps->reg_last_uses[regno]; u; u = XEXP (u, 1)) +	    add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI); + +	  for (u = deps->reg_last_sets[regno]; u; u = XEXP (u, 1)) +	    add_dependence (insn, XEXP (u, 0), REG_DEP_OUTPUT); + +	  if (code == SET) +	    { +	      free_INSN_LIST_list (&deps->reg_last_uses[regno]); +	      for (u = deps->reg_last_clobbers[regno]; u; u = XEXP (u, 1)) +		add_dependence (insn, XEXP (u, 0), REG_DEP_OUTPUT); +	      SET_REGNO_REG_SET (reg_pending_sets, regno); +	    } +	  else +	    SET_REGNO_REG_SET (reg_pending_clobbers, regno); + +	  /* Pseudos that are REG_EQUIV to something may be replaced +	     by that during reloading.  We need only add dependencies for +	     the address in the REG_EQUIV note.  */ +	  if (!reload_completed +	      && reg_known_equiv_p[regno] +	      && GET_CODE (reg_known_value[regno]) == MEM) +	    sched_analyze_2 (deps, XEXP (reg_known_value[regno], 0), insn); + +	  /* Don't let it cross a call after scheduling if it doesn't +	     already cross one.  */ + +	  if (REG_N_CALLS_CROSSED (regno) == 0) +	    for (u = deps->last_function_call; u; u = XEXP (u, 1)) +	      add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI); +	} +    } +  else if (GET_CODE (dest) == MEM) +    { +      /* Writing memory.  */ + +      if (deps->pending_lists_length > 32) +	{ +	  /* Flush all pending reads and writes to prevent the pending lists +	     from getting any larger.  Insn scheduling runs too slowly when +	     these lists get long.  The number 32 was chosen because it +	     seems like a reasonable number.  When compiling GCC with itself, +	     this flush occurs 8 times for sparc, and 10 times for m88k using +	     the number 32.  */ +	  flush_pending_lists (deps, insn, 0); +	} +      else +	{ +	  rtx u; +	  rtx pending, pending_mem; + +	  pending = deps->pending_read_insns; +	  pending_mem = deps->pending_read_mems; +	  while (pending) +	    { +	      if (anti_dependence (XEXP (pending_mem, 0), dest)) +		add_dependence (insn, XEXP (pending, 0), REG_DEP_ANTI); + +	      pending = XEXP (pending, 1); +	      pending_mem = XEXP (pending_mem, 1); +	    } + +	  pending = deps->pending_write_insns; +	  pending_mem = deps->pending_write_mems; +	  while (pending) +	    { +	      if (output_dependence (XEXP (pending_mem, 0), dest)) +		add_dependence (insn, XEXP (pending, 0), REG_DEP_OUTPUT); + +	      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); + +	  add_insn_mem_dependence (deps, &deps->pending_write_insns, +				   &deps->pending_write_mems, insn, dest); +	} +      sched_analyze_2 (deps, XEXP (dest, 0), insn); +    } + +  /* Analyze reads.  */ +  if (GET_CODE (x) == SET) +    sched_analyze_2 (deps, SET_SRC (x), insn); +} + +/* Analyze the uses of memory and registers in rtx X in INSN.  */ + +static void +sched_analyze_2 (deps, x, insn) +     struct deps *deps; +     rtx x; +     rtx insn; +{ +  register int i; +  register int j; +  register enum rtx_code code; +  register const char *fmt; + +  if (x == 0) +    return; + +  code = GET_CODE (x); + +  switch (code) +    { +    case CONST_INT: +    case CONST_DOUBLE: +    case SYMBOL_REF: +    case CONST: +    case LABEL_REF: +      /* Ignore constants.  Note that we must handle CONST_DOUBLE here +         because it may have a cc0_rtx in its CONST_DOUBLE_CHAIN field, but +         this does not mean that this insn is using cc0.  */ +      return; + +#ifdef HAVE_cc0 +    case CC0: +      /* User of CC0 depends on immediately preceding insn.  */ +      set_sched_group_p (insn); +      return; +#endif + +    case REG: +      { +	rtx u; +	int regno = REGNO (x); +	if (regno < FIRST_PSEUDO_REGISTER) +	  { +	    int i; + +	    i = HARD_REGNO_NREGS (regno, GET_MODE (x)); +	    while (--i >= 0) +	      { +		int r = regno + i; +		deps->reg_last_uses[r] +		  = alloc_INSN_LIST (insn, deps->reg_last_uses[r]); + +		for (u = deps->reg_last_sets[r]; u; u = XEXP (u, 1)) +		  add_dependence (insn, XEXP (u, 0), 0); + +		/* ??? This should never happen.  */ +		for (u = deps->reg_last_clobbers[r]; u; u = XEXP (u, 1)) +		  add_dependence (insn, XEXP (u, 0), 0); + +		if (call_used_regs[r] || global_regs[r]) +		  /* Function calls clobber all call_used regs.  */ +		  for (u = deps->last_function_call; u; u = XEXP (u, 1)) +		    add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI); +	      } +	  } +	else +	  { +	    deps->reg_last_uses[regno] +	      = alloc_INSN_LIST (insn, deps->reg_last_uses[regno]); + +	    for (u = deps->reg_last_sets[regno]; u; u = XEXP (u, 1)) +	      add_dependence (insn, XEXP (u, 0), 0); + +	    /* ??? This should never happen.  */ +	    for (u = deps->reg_last_clobbers[regno]; u; u = XEXP (u, 1)) +	      add_dependence (insn, XEXP (u, 0), 0); + +	    /* Pseudos that are REG_EQUIV to something may be replaced +	       by that during reloading.  We need only add dependencies for +	       the address in the REG_EQUIV note.  */ +	    if (!reload_completed +		&& reg_known_equiv_p[regno] +		&& GET_CODE (reg_known_value[regno]) == MEM) +	      sched_analyze_2 (deps, XEXP (reg_known_value[regno], 0), insn); + +	    /* If the register does not already cross any calls, then add this +	       insn to the sched_before_next_call list so that it will still +	       not cross calls after scheduling.  */ +	    if (REG_N_CALLS_CROSSED (regno) == 0) +	      add_dependence (deps->sched_before_next_call, insn, +			      REG_DEP_ANTI); +	  } +	return; +      } + +    case MEM: +      { +	/* Reading memory.  */ +	rtx u; +	rtx pending, pending_mem; + +	pending = deps->pending_read_insns; +	pending_mem = deps->pending_read_mems; +	while (pending) +	  { +	    if (read_dependence (XEXP (pending_mem, 0), x)) +	      add_dependence (insn, XEXP (pending, 0), REG_DEP_ANTI); + +	    pending = XEXP (pending, 1); +	    pending_mem = XEXP (pending_mem, 1); +	  } + +	pending = deps->pending_write_insns; +	pending_mem = deps->pending_write_mems; +	while (pending) +	  { +	    if (true_dependence (XEXP (pending_mem, 0), VOIDmode, +				 x, rtx_varies_p)) +	      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); + +	/* Always add these dependencies to pending_reads, since +	   this insn may be followed by a write.  */ +	add_insn_mem_dependence (deps, &deps->pending_read_insns, +				 &deps->pending_read_mems, insn, x); + +	/* Take advantage of tail recursion here.  */ +	sched_analyze_2 (deps, XEXP (x, 0), insn); +	return; +      } + +    /* Force pending stores to memory in case a trap handler needs them.  */ +    case TRAP_IF: +      flush_pending_lists (deps, insn, 1); +      break; + +    case ASM_OPERANDS: +    case ASM_INPUT: +    case UNSPEC_VOLATILE: +      { +	rtx u; + +	/* Traditional and volatile asm instructions must be considered to use +	   and clobber all hard registers, all pseudo-registers and all of +	   memory.  So must TRAP_IF and UNSPEC_VOLATILE operations. + +	   Consider for instance a volatile asm that changes the fpu rounding +	   mode.  An insn should not be moved across this even if it only uses +	   pseudo-regs because it might give an incorrectly rounded result.  */ +	if (code != ASM_OPERANDS || MEM_VOLATILE_P (x)) +	  { +	    int max_reg = max_reg_num (); +	    for (i = 0; i < max_reg; i++) +	      { +		for (u = deps->reg_last_uses[i]; u; u = XEXP (u, 1)) +		  add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI); +		free_INSN_LIST_list (&deps->reg_last_uses[i]); + +		for (u = deps->reg_last_sets[i]; u; u = XEXP (u, 1)) +		  add_dependence (insn, XEXP (u, 0), 0); + +		for (u = deps->reg_last_clobbers[i]; u; u = XEXP (u, 1)) +		  add_dependence (insn, XEXP (u, 0), 0); +	      } +	    reg_pending_sets_all = 1; + +	    flush_pending_lists (deps, insn, 0); +	  } + +	/* For all ASM_OPERANDS, we must traverse the vector of input operands. +	   We can not just fall through here since then we would be confused +	   by the ASM_INPUT rtx inside ASM_OPERANDS, which do not indicate +	   traditional asms unlike their normal usage.  */ + +	if (code == ASM_OPERANDS) +	  { +	    for (j = 0; j < ASM_OPERANDS_INPUT_LENGTH (x); j++) +	      sched_analyze_2 (deps, ASM_OPERANDS_INPUT (x, j), insn); +	    return; +	  } +	break; +      } + +    case PRE_DEC: +    case POST_DEC: +    case PRE_INC: +    case POST_INC: +      /* These both read and modify the result.  We must handle them as writes +         to get proper dependencies for following instructions.  We must handle +         them as reads to get proper dependencies from this to previous +         instructions.  Thus we need to pass them to both sched_analyze_1 +         and sched_analyze_2.  We must call sched_analyze_2 first in order +         to get the proper antecedent for the read.  */ +      sched_analyze_2 (deps, XEXP (x, 0), insn); +      sched_analyze_1 (deps, x, insn); +      return; + +    case POST_MODIFY: +    case PRE_MODIFY: +      /* op0 = op0 + op1 */ +      sched_analyze_2 (deps, XEXP (x, 0), insn); +      sched_analyze_2 (deps, XEXP (x, 1), insn); +      sched_analyze_1 (deps, x, insn); +      return; + +    default: +      break; +    } + +  /* Other cases: walk the insn.  */ +  fmt = GET_RTX_FORMAT (code); +  for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) +    { +      if (fmt[i] == 'e') +	sched_analyze_2 (deps, XEXP (x, i), insn); +      else if (fmt[i] == 'E') +	for (j = 0; j < XVECLEN (x, i); j++) +	  sched_analyze_2 (deps, XVECEXP (x, i, j), insn); +    } +} + +/* Analyze an INSN with pattern X to find all dependencies.  */ + +static void +sched_analyze_insn (deps, x, insn, loop_notes) +     struct deps *deps; +     rtx x, insn; +     rtx loop_notes; +{ +  register RTX_CODE code = GET_CODE (x); +  rtx link; +  int maxreg = max_reg_num (); +  int i; + +  if (code == COND_EXEC) +    { +      sched_analyze_2 (deps, COND_EXEC_TEST (x), insn); + +      /* ??? Should be recording conditions so we reduce the number of +	 false dependancies.  */ +      x = COND_EXEC_CODE (x); +      code = GET_CODE (x); +    } +  if (code == SET || code == CLOBBER) +    sched_analyze_1 (deps, x, insn); +  else if (code == PARALLEL) +    { +      register int i; +      for (i = XVECLEN (x, 0) - 1; i >= 0; i--) +	{ +	  rtx sub = XVECEXP (x, 0, i); +	  code = GET_CODE (sub); + +	  if (code == COND_EXEC) +	    { +	      sched_analyze_2 (deps, COND_EXEC_TEST (sub), insn); +	      sub = COND_EXEC_CODE (sub); +	      code = GET_CODE (sub); +	    } +	  if (code == SET || code == CLOBBER) +	    sched_analyze_1 (deps, sub, insn); +	  else +	    sched_analyze_2 (deps, sub, insn); +	} +    } +  else +    sched_analyze_2 (deps, x, insn); + +  /* Mark registers CLOBBERED or used by called function.  */ +  if (GET_CODE (insn) == CALL_INSN) +    for (link = CALL_INSN_FUNCTION_USAGE (insn); link; link = XEXP (link, 1)) +      { +	if (GET_CODE (XEXP (link, 0)) == CLOBBER) +	  sched_analyze_1 (deps, XEXP (link, 0), insn); +	else +	  sched_analyze_2 (deps, XEXP (link, 0), insn); +      } + +  /* 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 +     become incorrect.  */ + +  if (loop_notes) +    { +      int max_reg = max_reg_num (); +      int schedule_barrier_found = 0; +      rtx link; + +      /* Update loop_notes with any notes from this insn.  Also determine +	 if any of the notes on the list correspond to instruction scheduling +	 barriers (loop, eh & setjmp notes, but not range notes.  */ +      link = loop_notes; +      while (XEXP (link, 1)) +	{ +	  if (INTVAL (XEXP (link, 0)) == NOTE_INSN_LOOP_BEG +	      || INTVAL (XEXP (link, 0)) == NOTE_INSN_LOOP_END +	      || INTVAL (XEXP (link, 0)) == NOTE_INSN_EH_REGION_BEG +	      || INTVAL (XEXP (link, 0)) == NOTE_INSN_EH_REGION_END +	      || INTVAL (XEXP (link, 0)) == NOTE_INSN_SETJMP) +	    schedule_barrier_found = 1; + +	  link = XEXP (link, 1); +	} +      XEXP (link, 1) = REG_NOTES (insn); +      REG_NOTES (insn) = loop_notes; + +      /* Add dependencies if a scheduling barrier was found.  */ +      if (schedule_barrier_found) +	{ +	  for (i = 0; i < max_reg; i++) +	    { +	      rtx u; +	      for (u = deps->reg_last_uses[i]; u; u = XEXP (u, 1)) +		add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI); +	      free_INSN_LIST_list (&deps->reg_last_uses[i]); + +	      for (u = deps->reg_last_sets[i]; u; u = XEXP (u, 1)) +		add_dependence (insn, XEXP (u, 0), 0); + +	      for (u = deps->reg_last_clobbers[i]; u; u = XEXP (u, 1)) +		add_dependence (insn, XEXP (u, 0), 0); +	    } +	  reg_pending_sets_all = 1; + +	  flush_pending_lists (deps, insn, 0); +	} + +    } + +  /* Accumulate clobbers until the next set so that it will be output dependent +     on all of them.  At the next set we can clear the clobber list, since +     subsequent sets will be output dependent on it.  */ +  EXECUTE_IF_SET_IN_REG_SET +    (reg_pending_sets, 0, i, +     { +       free_INSN_LIST_list (&deps->reg_last_sets[i]); +       free_INSN_LIST_list (&deps->reg_last_clobbers[i]); +       deps->reg_last_sets[i] = alloc_INSN_LIST (insn, NULL_RTX); +     }); +  EXECUTE_IF_SET_IN_REG_SET +    (reg_pending_clobbers, 0, i, +     { +       deps->reg_last_clobbers[i] +	 = alloc_INSN_LIST (insn, deps->reg_last_clobbers[i]); +     }); +  CLEAR_REG_SET (reg_pending_sets); +  CLEAR_REG_SET (reg_pending_clobbers); + +  if (reg_pending_sets_all) +    { +      for (i = 0; i < maxreg; i++) +	{ +	  free_INSN_LIST_list (&deps->reg_last_sets[i]); +	  free_INSN_LIST_list (&deps->reg_last_clobbers[i]); +	  deps->reg_last_sets[i] = alloc_INSN_LIST (insn, NULL_RTX); +	} + +      reg_pending_sets_all = 0; +    } + +  /* If a post-call group is still open, see if it should remain so. +     This insn must be a simple move of a hard reg to a pseudo or +     vice-versa. + +     We must avoid moving these insns for correctness on +     SMALL_REGISTER_CLASS machines, and for special registers like +     PIC_OFFSET_TABLE_REGNUM.  For simplicity, extend this to all +     hard regs for all targets.  */ + +  if (deps->in_post_call_group_p) +    { +      rtx tmp, set = single_set (insn); +      int src_regno, dest_regno; + +      if (set == NULL) +	goto end_call_group; + +      tmp = SET_DEST (set); +      if (GET_CODE (tmp) == SUBREG) +	tmp = SUBREG_REG (tmp); +      if (GET_CODE (tmp) == REG) +	dest_regno = REGNO (tmp); +      else +	goto end_call_group; + +      tmp = SET_SRC (set); +      if (GET_CODE (tmp) == SUBREG) +	tmp = SUBREG_REG (tmp); +      if (GET_CODE (tmp) == REG) +	src_regno = REGNO (tmp); +      else +	goto end_call_group; + +      if (src_regno < FIRST_PSEUDO_REGISTER +	  || dest_regno < FIRST_PSEUDO_REGISTER) +	{ +	  set_sched_group_p (insn); +	  CANT_MOVE (insn) = 1; +	} +      else +	{ +	end_call_group: +	  deps->in_post_call_group_p = 0; +	} +    } +} + +/* Analyze every insn between HEAD and TAIL inclusive, creating LOG_LINKS +   for every dependency.  */ + +void +sched_analyze (deps, head, tail) +     struct deps *deps; +     rtx head, tail; +{ +  register rtx insn; +  register rtx u; +  rtx loop_notes = 0; + +  for (insn = head;; insn = NEXT_INSN (insn)) +    { +      if (GET_CODE (insn) == INSN || GET_CODE (insn) == JUMP_INSN) +	{ +	  /* Clear out the stale LOG_LINKS from flow.  */ +	  free_INSN_LIST_list (&LOG_LINKS (insn)); + +	  /* Clear out stale SCHED_GROUP_P.  */ +	  SCHED_GROUP_P (insn) = 0; + +	  /* Make each JUMP_INSN a scheduling barrier for memory +             references.  */ +	  if (GET_CODE (insn) == JUMP_INSN) +	    deps->last_pending_memory_flush +	      = alloc_INSN_LIST (insn, deps->last_pending_memory_flush); +	  sched_analyze_insn (deps, PATTERN (insn), insn, loop_notes); +	  loop_notes = 0; +	} +      else if (GET_CODE (insn) == CALL_INSN) +	{ +	  rtx x; +	  register int i; + +	  /* Clear out stale SCHED_GROUP_P.  */ +	  SCHED_GROUP_P (insn) = 0; + +	  CANT_MOVE (insn) = 1; + +	  /* Clear out the stale LOG_LINKS from flow.  */ +	  free_INSN_LIST_list (&LOG_LINKS (insn)); + +	  /* Any instruction using a hard register which may get clobbered +	     by a call needs to be marked as dependent on this call. +	     This prevents a use of a hard return reg from being moved +	     past a void call (i.e. it does not explicitly set the hard +	     return reg).  */ + +	  /* If this call is followed by a NOTE_INSN_SETJMP, then assume that +	     all registers, not just hard registers, may be clobbered by this +	     call.  */ + +	  /* Insn, being a CALL_INSN, magically depends on +	     `last_function_call' already.  */ + +	  if (NEXT_INSN (insn) && GET_CODE (NEXT_INSN (insn)) == NOTE +	      && NOTE_LINE_NUMBER (NEXT_INSN (insn)) == NOTE_INSN_SETJMP) +	    { +	      int max_reg = max_reg_num (); +	      for (i = 0; i < max_reg; i++) +		{ +		  for (u = deps->reg_last_uses[i]; u; u = XEXP (u, 1)) +		    add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI); +		  free_INSN_LIST_list (&deps->reg_last_uses[i]); + +		  for (u = deps->reg_last_sets[i]; u; u = XEXP (u, 1)) +		    add_dependence (insn, XEXP (u, 0), 0); + +		  for (u = deps->reg_last_clobbers[i]; u; u = XEXP (u, 1)) +		    add_dependence (insn, XEXP (u, 0), 0); +		} +	      reg_pending_sets_all = 1; + +	      /* Add a pair of REG_SAVE_NOTEs which we will later +		 convert back into a NOTE_INSN_SETJMP note.  See +		 reemit_notes for why we use a pair of NOTEs.  */ +	      REG_NOTES (insn) = alloc_EXPR_LIST (REG_SAVE_NOTE, +						  GEN_INT (0), +						  REG_NOTES (insn)); +	      REG_NOTES (insn) = alloc_EXPR_LIST (REG_SAVE_NOTE, +						  GEN_INT (NOTE_INSN_SETJMP), +						  REG_NOTES (insn)); +	    } +	  else +	    { +	      for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) +		if (call_used_regs[i] || global_regs[i]) +		  { +		    for (u = deps->reg_last_uses[i]; u; u = XEXP (u, 1)) +		      add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI); + +		    for (u = deps->reg_last_sets[i]; u; u = XEXP (u, 1)) +		      add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI); + +		    SET_REGNO_REG_SET (reg_pending_clobbers, i); +		  } +	    } + +	  /* For each insn which shouldn't cross a call, add a dependence +	     between that insn and this call insn.  */ +	  x = LOG_LINKS (deps->sched_before_next_call); +	  while (x) +	    { +	      add_dependence (insn, XEXP (x, 0), REG_DEP_ANTI); +	      x = XEXP (x, 1); +	    } +	  free_INSN_LIST_list (&LOG_LINKS (deps->sched_before_next_call)); + +	  sched_analyze_insn (deps, PATTERN (insn), insn, loop_notes); +	  loop_notes = 0; + +	  /* In the absence of interprocedural alias analysis, we must flush +	     all pending reads and writes, and start new dependencies starting +	     from here.  But only flush writes for constant calls (which may +	     be passed a pointer to something we haven't written yet).  */ +	  flush_pending_lists (deps, insn, CONST_CALL_P (insn)); + +	  /* Depend this function call (actually, the user of this +	     function call) on all hard register clobberage.  */ + +	  /* last_function_call is now a list of insns.  */ +	  free_INSN_LIST_list (&deps->last_function_call); +	  deps->last_function_call = alloc_INSN_LIST (insn, NULL_RTX); + +	  /* Before reload, begin a post-call group, so as to keep the +	     lifetimes of hard registers correct.  */ +	  if (! reload_completed) +	    deps->in_post_call_group_p = 1; +	} + +      /* See comments on reemit_notes as to why we do this. +	 ??? Actually, the reemit_notes just say what is done, not why.  */ + +      else if (GET_CODE (insn) == NOTE +	       && (NOTE_LINE_NUMBER (insn) == NOTE_INSN_RANGE_BEG +		   || NOTE_LINE_NUMBER (insn) == NOTE_INSN_RANGE_END)) +	{ +	  loop_notes = alloc_EXPR_LIST (REG_SAVE_NOTE, NOTE_RANGE_INFO (insn), +					loop_notes); +	  loop_notes = alloc_EXPR_LIST (REG_SAVE_NOTE, +					GEN_INT (NOTE_LINE_NUMBER (insn)), +					loop_notes); +	} +      else if (GET_CODE (insn) == NOTE +	       && (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_BEG +		   || NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_END +		   || NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_BEG +		   || NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_END +		   || (NOTE_LINE_NUMBER (insn) == NOTE_INSN_SETJMP +		       && GET_CODE (PREV_INSN (insn)) != CALL_INSN))) +	{ +	  rtx rtx_region; + +	  if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_BEG +	      || NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_END) +	    rtx_region = GEN_INT (NOTE_EH_HANDLER (insn)); +	  else +	    rtx_region = GEN_INT (0); + +	  loop_notes = alloc_EXPR_LIST (REG_SAVE_NOTE, +					rtx_region, +					loop_notes); +	  loop_notes = alloc_EXPR_LIST (REG_SAVE_NOTE, +					GEN_INT (NOTE_LINE_NUMBER (insn)), +					loop_notes); +	  CONST_CALL_P (loop_notes) = CONST_CALL_P (insn); +	} + +      if (insn == tail) +	return; +    } +  abort (); +} + +/* Examine insns in the range [ HEAD, TAIL ] and Use the backward +   dependences from LOG_LINKS to build forward dependences in +   INSN_DEPEND.  */ + +void +compute_forward_dependences (head, tail) +     rtx head, tail; +{ +  rtx insn, link; +  rtx next_tail; +  enum reg_note dep_type; + +  next_tail = NEXT_INSN (tail); +  for (insn = head; insn != next_tail; insn = NEXT_INSN (insn)) +    { +      if (! INSN_P (insn)) +	continue; + +      insn = group_leader (insn); + +      for (link = LOG_LINKS (insn); link; link = XEXP (link, 1)) +	{ +	  rtx x = group_leader (XEXP (link, 0)); +	  rtx new_link; + +	  if (x != XEXP (link, 0)) +	    continue; + +#ifdef ENABLE_CHECKING +	  /* If add_dependence is working properly there should never +	     be notes, deleted insns or duplicates in the backward +	     links.  Thus we need not check for them here. + +	     However, if we have enabled checking we might as well go +	     ahead and verify that add_dependence worked properly.  */ +	  if (GET_CODE (x) == NOTE +	      || INSN_DELETED_P (x) +	      || (forward_dependency_cache != NULL +		  && TEST_BIT (forward_dependency_cache[INSN_LUID (x)], +			       INSN_LUID (insn))) +	      || (forward_dependency_cache == NULL +		  && find_insn_list (insn, INSN_DEPEND (x)))) +	    abort (); +	  if (forward_dependency_cache != NULL) +	    SET_BIT (forward_dependency_cache[INSN_LUID (x)], +		     INSN_LUID (insn)); +#endif + +	  new_link = alloc_INSN_LIST (insn, INSN_DEPEND (x)); + +	  dep_type = REG_NOTE_KIND (link); +	  PUT_REG_NOTE_KIND (new_link, dep_type); + +	  INSN_DEPEND (x) = new_link; +	  INSN_DEP_COUNT (insn) += 1; +	} +    } +} + +/* Initialize variables for region data dependence analysis. +   n_bbs is the number of region blocks.  */ + +void +init_deps (deps) +     struct deps *deps; +{ +  int maxreg = max_reg_num (); +  deps->reg_last_uses = (rtx *) xcalloc (maxreg, sizeof (rtx)); +  deps->reg_last_sets = (rtx *) xcalloc (maxreg, sizeof (rtx)); +  deps->reg_last_clobbers = (rtx *) xcalloc (maxreg, sizeof (rtx)); + +  deps->pending_read_insns = 0; +  deps->pending_read_mems = 0; +  deps->pending_write_insns = 0; +  deps->pending_write_mems = 0; +  deps->pending_lists_length = 0; +  deps->last_pending_memory_flush = 0; +  deps->last_function_call = 0; +  deps->in_post_call_group_p = 0; + +  deps->sched_before_next_call +    = gen_rtx_INSN (VOIDmode, 0, NULL_RTX, NULL_RTX, +		    NULL_RTX, 0, NULL_RTX, NULL_RTX); +  LOG_LINKS (deps->sched_before_next_call) = 0; +} + +/* Free insn lists found in DEPS.  */ + +void +free_deps (deps) +     struct deps *deps; +{ +  int max_reg = max_reg_num (); +  int i; + +  /* Note this loop is executed max_reg * nr_regions times.  It's first +     implementation accounted for over 90% of the calls to free_INSN_LIST_list. +     The list was empty for the vast majority of those calls.  On the PA, not +     calling free_INSN_LIST_list in those cases improves -O2 compile times by +     3-5% on average.  */ +  for (i = 0; i < max_reg; ++i) +    { +      if (deps->reg_last_clobbers[i]) +	free_INSN_LIST_list (&deps->reg_last_clobbers[i]); +      if (deps->reg_last_sets[i]) +	free_INSN_LIST_list (&deps->reg_last_sets[i]); +      if (deps->reg_last_uses[i]) +	free_INSN_LIST_list (&deps->reg_last_uses[i]); +    } +} + +/* If it is profitable to use them, initialize caches for tracking +   dependency informatino.  LUID is the number of insns to be scheduled, +   it is used in the estimate of profitability.  */ + +void +init_dependency_caches (luid) +     int luid; +{ +  /* ?!? We could save some memory by computing a per-region luid mapping +     which could reduce both the number of vectors in the cache and the size +     of each vector.  Instead we just avoid the cache entirely unless the +     average number of instructions in a basic block is very high.  See +     the comment before the declaration of true_dependency_cache for +     what we consider "very high".  */ +  if (luid / n_basic_blocks > 100 * 5) +    { +      true_dependency_cache = sbitmap_vector_alloc (luid, luid); +      sbitmap_vector_zero (true_dependency_cache, luid); +      anti_dependency_cache = sbitmap_vector_alloc (luid, luid); +      sbitmap_vector_zero (anti_dependency_cache, luid); +      output_dependency_cache = sbitmap_vector_alloc (luid, luid); +      sbitmap_vector_zero (output_dependency_cache, luid); +#ifdef ENABLE_CHECKING +      forward_dependency_cache = sbitmap_vector_alloc (luid, luid); +      sbitmap_vector_zero (forward_dependency_cache, luid); +#endif +    } +} + +/* Free the caches allocated in init_dependency_caches.  */ + +void +free_dependency_caches () +{ +  if (true_dependency_cache) +    { +      free (true_dependency_cache); +      true_dependency_cache = NULL; +      free (anti_dependency_cache); +      anti_dependency_cache = NULL; +      free (output_dependency_cache); +      output_dependency_cache = NULL; +#ifdef ENABLE_CHECKING +      free (forward_dependency_cache); +      forward_dependency_cache = NULL; +#endif +    } +} + +/* Initialize some global variables needed by the dependency analysis +   code.  */ + +void +init_deps_global () +{ +  reg_pending_sets = INITIALIZE_REG_SET (reg_pending_sets_head); +  reg_pending_clobbers = INITIALIZE_REG_SET (reg_pending_clobbers_head); +  reg_pending_sets_all = 0; +} + +/* Free everything used by the dependency analysis code.  */ + +void +finish_deps_global () +{ +  FREE_REG_SET (reg_pending_sets); +  FREE_REG_SET (reg_pending_clobbers); +} diff --git a/gcc/sched-int.h b/gcc/sched-int.h index bf14faa4ca2..faa4a8b788a 100644 --- a/gcc/sched-int.h +++ b/gcc/sched-int.h @@ -23,6 +23,70 @@ the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA  /* Forward declaration.  */  struct ready_list; +/* Describe state of dependencies used during sched_analyze phase.  */ +struct deps +{ +  /* The *_insns and *_mems are paired lists.  Each pending memory operation +     will have a pointer to the MEM rtx on one list and a pointer to the +     containing insn on the other list in the same place in the list.  */ + +  /* We can't use add_dependence like the old code did, because a single insn +     may have multiple memory accesses, and hence needs to be on the list +     once for each memory access.  Add_dependence won't let you add an insn +     to a list more than once.  */ + +  /* An INSN_LIST containing all insns with pending read operations.  */ +  rtx pending_read_insns; + +  /* An EXPR_LIST containing all MEM rtx's which are pending reads.  */ +  rtx pending_read_mems; + +  /* An INSN_LIST containing all insns with pending write operations.  */ +  rtx pending_write_insns; + +  /* An EXPR_LIST containing all MEM rtx's which are pending writes.  */ +  rtx pending_write_mems; + +  /* Indicates the combined length of the two pending lists.  We must prevent +     these lists from ever growing too large since the number of dependencies +     produced is at least O(N*N), and execution time is at least O(4*N*N), as +     a function of the length of these pending lists.  */ +  int pending_lists_length; + +  /* The last insn upon which all memory references must depend. +     This is an insn which flushed the pending lists, creating a dependency +     between it and all previously pending memory references.  This creates +     a barrier (or a checkpoint) which no memory reference is allowed to cross. + +     This includes all non constant CALL_INSNs.  When we do interprocedural +     alias analysis, this restriction can be relaxed. +     This may also be an INSN that writes memory if the pending lists grow +     too large.  */ +  rtx last_pending_memory_flush; + +  /* The last function call we have seen.  All hard regs, and, of course, +     the last function call, must depend on this.  */ +  rtx last_function_call; + +  /* Used to keep post-call psuedo/hard reg movements together with +     the call.  */ +  int in_post_call_group_p; + +  /* The LOG_LINKS field of this is a list of insns which use a pseudo +     register that does not already cross a call.  We create +     dependencies between each of those insn and the next call insn, +     to ensure that they won't cross a call after scheduling is done.  */ +  rtx sched_before_next_call; + +  /* Element N is the next insn that sets (hard or pseudo) register +     N within the current basic block; or zero, if there is no +     such insn.  Needed for new registers which may be introduced +     by splitting insns.  */ +  rtx *reg_last_uses; +  rtx *reg_last_sets; +  rtx *reg_last_clobbers; +}; +  /* This structure holds some state of the current scheduling pass, and     contains some function pointers that abstract out some of the non-generic     functionality from functions such as schedule_block or schedule_insn. @@ -63,6 +127,71 @@ struct sched_info    int queue_must_finish_empty;  }; +extern struct sched_info *current_sched_info; + +/* Indexed by INSN_UID, the collection of all data associated with +   a single instruction.  */ + +struct haifa_insn_data +{ +  /* A list of insns which depend on the instruction.  Unlike LOG_LINKS, +     it represents forward dependancies.  */ +  rtx depend; + +  /* The line number note in effect for each insn.  For line number +     notes, this indicates whether the note may be reused.  */ +  rtx line_note; + +  /* Logical uid gives the original ordering of the insns.  */ +  int luid; + +  /* A priority for each insn.  */ +  int priority; + +  /* The number of incoming edges in the forward dependency graph. +     As scheduling proceds, counts are decreased.  An insn moves to +     the ready queue when its counter reaches zero.  */ +  int dep_count; + +  /* An encoding of the blockage range function.  Both unit and range +     are coded.  */ +  unsigned int blockage; + +  /* Number of instructions referring to this insn.  */ +  int ref_count; + +  /* The minimum clock tick at which the insn becomes ready.  This is +     used to note timing constraints for the insns in the pending list.  */ +  int tick; + +  short cost; + +  /* An encoding of the function units used.  */ +  short units; + +  /* This weight is an estimation of the insn's contribution to +     register pressure.  */ +  short reg_weight; + +  /* Some insns (e.g. call) are not allowed to move across blocks.  */ +  unsigned int cant_move : 1; + +  /* Set if there's DEF-USE dependance between some speculatively +     moved load insn and this one.  */ +  unsigned int fed_by_spec_load : 1; +  unsigned int is_load_insn : 1; +}; + +extern struct haifa_insn_data *h_i_d; + +/* Accessor macros for h_i_d.  There are more in haifa-sched.c.  */ +#define INSN_DEPEND(INSN)	(h_i_d[INSN_UID (INSN)].depend) +#define INSN_LUID(INSN)		(h_i_d[INSN_UID (INSN)].luid) +#define CANT_MOVE(insn)		(h_i_d[INSN_UID (insn)].cant_move) +#define INSN_DEP_COUNT(INSN)	(h_i_d[INSN_UID (INSN)].dep_count) + +extern FILE *sched_dump; +  #ifndef __GNUC__  #define __inline  #endif @@ -70,3 +199,35 @@ struct sched_info  #ifndef HAIFA_INLINE  #define HAIFA_INLINE __inline  #endif + +/* Functions in sched-vis.c.  */ +extern void init_target_units PARAMS ((void)); +extern void insn_print_units PARAMS ((rtx)); +extern void init_block_visualization PARAMS ((void)); +extern void print_block_visualization PARAMS ((const char *)); +extern void visualize_scheduled_insns PARAMS ((int)); +extern void visualize_no_unit PARAMS ((rtx)); +extern void visualize_stall_cycles PARAMS ((int)); +extern void visualize_alloc PARAMS ((void)); +extern void visualize_free PARAMS ((void)); + +/* Functions in sched-deps.c.  */ +extern void add_dependence PARAMS ((rtx, rtx, enum reg_note)); +extern void add_insn_mem_dependence PARAMS ((struct deps *, rtx *, rtx *, rtx, +					     rtx)); +extern void sched_analyze PARAMS ((struct deps *, rtx, rtx)); +extern void init_deps PARAMS ((struct deps *)); +extern void free_deps PARAMS ((struct deps *)); +extern void init_deps_global PARAMS ((void)); +extern void finish_deps_global PARAMS ((void)); +extern void compute_forward_dependences PARAMS ((rtx, rtx)); +extern int find_insn_mem_list PARAMS ((rtx, rtx, rtx, rtx)); +extern rtx find_insn_list PARAMS ((rtx, rtx)); +extern void init_dependency_caches PARAMS ((int)); +extern void free_dependency_caches PARAMS ((void)); + +/* Functions in haifa-sched.c.  */ +extern int insn_unit PARAMS ((rtx)); +extern rtx get_unit_last_insn PARAMS ((int)); +extern int actual_hazard_this_instance PARAMS ((int, int, rtx, int, int)); + | 

