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


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

Re: [tree-profiling] fix some side corners of finalization machinery


If I remove my checks for the flags and add your patch, I still cannot compile the java library. Here is the patch that will get you there.

just start clean and type make and let it go. I will crash at the right spot.

kenny

Jan Hubicka wrote:

Jan,

I think that I (actually you) found the problem. I added the code to copy my info that you suggested to the clone routine.
The problem went away. It seems that the code that I had in the remove_cgraph_node was only triggered if the master_clone had been set.


I also added code to set the master_clone to cgraph_node and to clone_cgraph_node.

Yan I will send you this patch when I am finished testing it. I also needed to add code so that I only look at nodes that have been either finalized, external or externally_visible since I hit an orphan one of the in the java libraries.



externally_visible nodes should be all finalized or external, so you only need the first two classes I would say. I also wonder how the orphan node look like? It might be fixed by the patch I sent (I also noticed little inconsistency where instead of "finalized" test I had DECL_SAVED_TREE test that is not equivalent for C++/Java)

Honza


/* Callgraph based analysis of static variables.
   Copyright (C) 2004 Free Software Foundation, Inc.
   Contributed by Kenneth Zadeck <zadeck@naturalbridge.com>

This file is part of GCC.

GCC 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.

GCC 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 GCC; see the file COPYING.  If not, write to the Free
Software Foundation, 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA.  */

/* This file gathers information about how statics that are local to
   the compilation unit are used.  It is to be run after the call
   graph is built and after inlining decisions have been made.

   First each function is analyzed to determine which local static
   variables are either read or written or have their address taken.
   Any local static that has its address taken is removed from
   consideration.  Once the local read and writes are determined, a
   transitive closure of this information is performed over the call
   graph to determine the worst case set of side effects of each call.
   In later parts of the compiler, these local and global sets are
   examined to make the call clobbering less traumatic, promote some
   statics to registers, and improve aliasing information.

   This must be run after inlining decisions have been made since
   otherwise, the local sets will not contain information that is
   consistent with post inlined state.  The global sets are less prone
   to problems since they are by definition transitive.  */

/* The code in this module is called by the ipa pass manager. It
   should be one of the later passes since it's information is used by
   the rest of the compilation. */

#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "tree.h"
#include "tree-flow.h"
#include "tree-inline.h"
#include "tree-pass.h"
#include "langhooks.h"
#include "pointer-set.h"
#include "ggc.h"
#include "ipa-static.h"
#include "c-common.h"
#include "tree-gimple.h"
#include "cgraph.h"
#include "output.h"
#include "flags.h"

/* FIXME -- PROFILE-RESTRUCTURE: change comment from DECL_UID to var-ann. */    
/* This splay tree contains all of the static variables that are
   being considered by the compilation level alias analysis.  For
   module_at_a_time compilation, this is the set of static but not
   public variables.  Any variables that either have their address
   taken or participate in otherwise unsavory operations are deleted
   from this list.  */
static GTY((param1_is(int), param2_is(tree)))
     splay_tree static_vars_to_consider_by_uid;

/* This bitmap is used to knock out the module static variables whose
   addresses have been taken and passed around.  This is indexed by
   uid.  */
static bitmap module_statics_escape;

/* FIXME -- PROFILE-RESTRUCTURE: change comment from DECL_UID to var-ann. */    
/* A bit is set for every module static we are considering and is
   indexed by DECL_UID.  This is ored into the local info when asm
   code is found that clobbers all memory. */
static bitmap all_module_statics;

/* Records tree nodes seen in cgraph_create_edges.  Simply using
   walk_tree_without_duplicates doesn't guarantee each node is visited
   once because it gets a new htab upon each recursive call from
   scan_for_static_refs.  */
static struct pointer_set_t *visited_nodes;

tree memory_identifier_string;

/* Debugging function for postorder and inorder code. NOTE is a string
   that is printed before the nodes are printed.  ORDER is an array of
   cgraph_nodes that has COUNT useful nodes in it.  */

static void 
print_order (FILE* out, 
	     const char * note, 
	     struct cgraph_node** order, 
	     int count) 
{
  int i;
  struct cgraph_node* node;
  fprintf (out, "\n\n ordered call graph: %s\n", note);
  
  for (i = count - 1; i >= 0; i--)
    {
      struct cgraph_edge *edge;

      fprintf (out, "\n  %s->(", cgraph_node_name (order[i]));

      for (edge = order[i]->callees; edge; edge = edge->next_callee)
	fprintf (out, " %s", cgraph_node_name (edge->callee));
      fprintf (out, ")");

      fprintf (out, "\n  %s<->[", cgraph_node_name (order[i]));

      for (node = order[i]->next_cycle; node; node = node->next_cycle)
	fprintf (out, " %s", cgraph_node_name (node));
      fprintf (out, "]");
    }
  fprintf (out, "\n");
  fflush(out);
}

/* FIXME -- PROFILE-RESTRUCTURE: Remove this function, it becomes a nop. */    
/* Convert IN_DECL bitmap which is indexed by DECL_UID to IN_ANN, a
   bitmap indexed by var_ann (VAR_DECL)->uid.  */

static void 
convert_UIDs_in_bitmap (bitmap in_ann, bitmap in_decl) 
{
  int index;
  bitmap_iterator bi;

  EXECUTE_IF_SET_IN_BITMAP(in_decl, 0, index, bi)
    {
      splay_tree_node n = 
	      splay_tree_lookup (static_vars_to_consider_by_uid, index);
      if (n != NULL) 
	{
	  tree t = (tree)n->value;
	  var_ann_t va = var_ann (t);
	  if (va) 
	    bitmap_set_bit(in_ann, va->uid);
	}
    }
}


/* FIXME -- PROFILE-RESTRUCTURE: Remove this function, it becomes a
   nop. */    
/* The bitmaps used to represent the static global variables are
   indexed by DECL_UID however, this is not used inside of functions
   to index the ssa variables.  The denser var_ann (VAR_DECL)->uid is
   used there.  This function is called from
   tree_dfa:find_referenced_vars after the denser representation is
   built.  This function invalidates any cached indexes.  */ 

void
ipa_static_reset_maps (void) 
{
  struct cgraph_node *node;
  
  for (node = cgraph_nodes; node; node = node->next)
    {
      ipa_static_vars_info_t info = node->static_vars_info;

      if (info) 
	{
	  ipa_local_static_vars_info_t l = info->local;
	  ipa_global_static_vars_info_t g = info->global;
	  if (l->var_anns_valid) 
	    {
	      bitmap_clear (l->statics_read_by_ann_uid);
	      bitmap_clear (l->statics_written_by_ann_uid);
	      l->var_anns_valid = false;
	    }

	  if (g->var_anns_valid) 
	    {
	      bitmap_clear (g->statics_read_by_ann_uid);
	      bitmap_clear (g->statics_written_by_ann_uid);
	      bitmap_clear (g->statics_not_read_by_ann_uid);
	      bitmap_clear (g->statics_not_written_by_ann_uid);
	      g->var_anns_valid = false;
	    }
	}
    }
}

/* Get the cgraph_node for the local function and make sure the
   var_ann bitmaps are properly converted.  */
 
static ipa_local_static_vars_info_t
get_local_static_vars_info (tree fn) 
{
  ipa_local_static_vars_info_t l;

  /* Was not compiled -O2 or higher.  */ 
  ipa_static_vars_info_t info = get_var_ann (fn)->static_vars_info;

  if (!info)
    return NULL;

  l = info->local;
  if (!l->var_anns_valid) 
    {
      convert_UIDs_in_bitmap (l->statics_read_by_ann_uid, 
			      l->statics_read_by_decl_uid);
      convert_UIDs_in_bitmap (l->statics_written_by_ann_uid, 
			      l->statics_written_by_decl_uid);
      l->var_anns_valid = true;
    }
  return l;
}

/* Get the global static_vars_info structure for the function FN and
   make sure the ann_uid's bitmaps are properly converted.  */
 
static ipa_global_static_vars_info_t
get_global_static_vars_info (tree fn) 
{
  ipa_global_static_vars_info_t g;

  /* Was not compiled -O2 or higher.  */ 
  ipa_static_vars_info_t info = get_var_ann (fn)->static_vars_info;

  if (!info)
    return NULL;

  g = info->global;
  if (!g->var_anns_valid) 
    {
      convert_UIDs_in_bitmap (g->statics_read_by_ann_uid, 
			      g->statics_read_by_decl_uid);
      convert_UIDs_in_bitmap (g->statics_written_by_ann_uid, 
			      g->statics_written_by_decl_uid);
      convert_UIDs_in_bitmap (g->statics_not_read_by_ann_uid, 
			      g->statics_not_read_by_decl_uid);
      convert_UIDs_in_bitmap (g->statics_not_written_by_ann_uid, 
			      g->statics_not_written_by_decl_uid);
      g->var_anns_valid = true;
    }
  return g;
}

/* Return a bitmap indexed by var_ann (VAR_DECL)->uid for the static
   variables that may be read locally by the execution of the function
   fn.  Returns NULL if no data is available, such as it was not
   compiled with -O2 or higher.  */

bitmap 
ipa_get_statics_read_local (tree fn)
{
  ipa_local_static_vars_info_t l = get_local_static_vars_info (fn);
  if (l) 
    return l->statics_read_by_ann_uid;
  else
    return NULL;
}

/* Return a bitmap indexed by var_ann (VAR_DECL)->uid for the static
   variables that may be written locally by the execution of the function
   fn.  Returns NULL if no data is available, such as it was not
   compiled with -O2 or higher.  */

bitmap 
ipa_get_statics_written_local (tree fn)
{
  ipa_local_static_vars_info_t l = get_local_static_vars_info (fn);
  if (l) 
    return l->statics_written_by_ann_uid;
  else
    return NULL;
}

/* Return a bitmap indexed by var_ann (VAR_DECL)->uid for the static
   variables that are read during the execution of the function
   FN.  Returns NULL if no data is available, such as it was not
   compiled with -O2 or higher.  */

bitmap 
ipa_get_statics_read_global (tree fn) 
{
  ipa_global_static_vars_info_t g = get_global_static_vars_info (fn);
  if (g) 
    return g->statics_read_by_ann_uid;
  else
    return NULL;
}

/* Return a bitmap indexed by var_ann (VAR_DECL)->uid for the static
   variables that are written during the execution of the function
   FN.  Note that variables written may or may not be read during the
   function call.  Returns NULL if no data is available, such as it
   was not compiled with -O2 or higher.  */

bitmap 
ipa_get_statics_written_global (tree fn) 
{
  ipa_global_static_vars_info_t g = get_global_static_vars_info (fn);
  if (g) 
    return g->statics_written_by_ann_uid;
  else
    return NULL;
}

/* Return a bitmap indexed by var_ann (VAR_DECL)->uid for the static
   variables that are not read during the execution of the function
   FN.  Returns NULL if no data is available, such as it was not
   compiled with -O2 or higher.  */

bitmap 
ipa_get_statics_not_read_global (tree fn) 
{
  ipa_global_static_vars_info_t g = get_global_static_vars_info (fn);
  if (g) 
    return g->statics_not_read_by_ann_uid;
  else
    return NULL;
}

/* Return a bitmap indexed by var_ann (VAR_DECL)->uid for the static
   variables that are not written during the execution of the function
   FN.  Note that variables written may or may not be read during the
   function call.  Returns NULL if no data is available, such as it
   was not compiled with -O2 or higher.  */

bitmap 
ipa_get_statics_not_written_global (tree fn) 
{
  ipa_global_static_vars_info_t g = get_global_static_vars_info (fn);
  if (g) 
    return g->statics_not_written_by_ann_uid;
  else
    return NULL;
}

struct searchc_env {
  struct cgraph_node **stack;
  int stack_size;
  struct cgraph_node **result;
  int order_pos;
  splay_tree nodes_marked_new;
  bool reduce;
  int count;
};

struct dfs_info {
  int dfn_number;
  int low_link;
  bool new;
  bool on_stack;
};

/* This is an implementation of Tarjan's strongly connected region
   finder as reprinted in Aho Hopcraft and Ullman's The Design and
   Analysis of Computer Programs (1975) pages 192-193.  This version
   has been customized for cgraph_nodes.  The env parameter is because
   it is recursive and there are no nested functions here.  This
   function should only be called from itself or
   cgraph_reduced_inorder.  ENV is a stack env and would be
   unnecessary if C had nested functions.  V is the node to start
   searching from.  */

static void
searchc (struct searchc_env* env, struct cgraph_node *v) 
{
  struct cgraph_edge *edge;
  struct dfs_info *v_info = v->aux;
  
  /* mark node as old */
  v_info->new = false;
  splay_tree_remove (env->nodes_marked_new, v->uid);
  
  v_info->dfn_number = env->count;
  v_info->low_link = env->count;
  env->count++;
  env->stack[(env->stack_size)++] = v;
  v_info->on_stack = true;
  
  for (edge = v->callees; edge; edge = edge->next_callee)
    {
      struct dfs_info * w_info;
      struct cgraph_node *w = edge->callee;
      /* Bypass the clones and only look at the master node.  Skip
	 external nodes.  */
      w = cgraph_master_clone (w);
      if (w) 
	{
	  w_info = w->aux;
	  if (w_info->new) 
	    {
	      searchc (env, w);
	      v_info->low_link =
		(v_info->low_link < w_info->low_link) ?
		v_info->low_link : w_info->low_link;
	    } 
	  else 
	    if ((w_info->dfn_number < v_info->dfn_number) 
		&& (w_info->on_stack)) 
	      v_info->low_link =
		(w_info->dfn_number < v_info->low_link) ?
		w_info->dfn_number : v_info->low_link;
	}
    }


  if (v_info->low_link == v_info->dfn_number) 
    {
      struct cgraph_node *last = NULL;
      struct cgraph_node *x;
      struct dfs_info *x_info;
      do {
	x = env->stack[--(env->stack_size)];
	x_info = x->aux;
	x_info->on_stack = false;
	
	if (env->reduce) 
	  {
	    x->next_cycle = last;
	    last = x;
	  } 
	else 
	  env->result[env->order_pos++] = x;
      } 
      while (v != x);
      if (env->reduce) 
	env->result[env->order_pos++] = v;
    }
}

/* Topsort the call graph by caller relation.  Put the result in ORDER.

   The REDUCE flag is true if you want the cycles reduced to single
   nodes.  Only consider nodes that have the output bit set. */

static int
reduced_inorder (struct cgraph_node **order, bool reduce)
{
  struct cgraph_node *node;
  struct searchc_env env;
  splay_tree_node result;
  env.stack = xcalloc (cgraph_n_nodes, sizeof (struct cgraph_node *));
  env.stack_size = 0;
  env.result = order;
  env.order_pos = 0;
  env.nodes_marked_new = splay_tree_new (splay_tree_compare_ints, 0, 0);
  env.count = 1;
  env.reduce = reduce;
  
  for (node = cgraph_nodes; node; node = node->next) 
    if (cgraph_is_master_clone (node)
/* 	&& (node->local.finalized  */
/* 	    || node->local.external  */
/* 	    || node->local.externally_visible) */
) 
      {
	struct dfs_info *info = xcalloc (1, sizeof (struct dfs_info));
	info->new = true;
	info->on_stack = false;
	node->aux = info;
	node->next_cycle = NULL;
	
	splay_tree_insert (env.nodes_marked_new,
			   node->uid, (splay_tree_value)node);
      } 
    else 
      node->aux = NULL;
  result = splay_tree_min (env.nodes_marked_new);
  while (result)
    {
      node = (struct cgraph_node *)result->value;
      searchc (&env, node);
      result = splay_tree_min (env.nodes_marked_new);
    }
  splay_tree_delete (env.nodes_marked_new);
  free (env.stack);

  for (node = cgraph_nodes; node; node = node->next)
    if (node->aux)
      {
	free (node->aux);
	node->aux = NULL;
      }
  return env.order_pos;
}

/* Add VAR to all_module_statics and the two static_vars_to_consider*
   sets.  */

static inline
void add_static_var (tree var) 
{
  /* FIXME -- PROFILE-RESTRUCTURE: Change the call from
     DECL_UID to get the uid from the var_ann field. */    
  splay_tree_insert (static_vars_to_consider_by_uid,
		     DECL_UID (var), (splay_tree_value)var);
  
  if (dump_file)
    fprintf (dump_file, "\nConsidering var:%s",
	     lang_hooks.decl_printable_name (var, 2));
  /* FIXME -- PROFILE-RESTRUCTURE: Change the call from
     DECL_UID to get the uid from the var_ann field. */    
  bitmap_set_bit (all_module_statics, DECL_UID (var));
}

/* FIXME this needs to be enhanced.  If we are compiling a single
   module this returns true if the variable is a module level static,
   but if we are doing whole program compilation, this could return
   true if TREE_PUBLIC is true. */
/* Return true if the variable T is the right kind of static variable to
   perform compilation unit scope escape analysis.  */

static inline
bool has_proper_scope_for_analysis (tree t)
{
  bool result = (TREE_STATIC (t)) && !(TREE_PUBLIC (t)) && !(TREE_THIS_VOLATILE (t));
  if ((result) && !bitmap_bit_p (all_module_statics, DECL_UID (t)))
    add_static_var (t);
  return result;
}

/* If T is a VAR_DECL for a static that we are interrested in, add the
   uid to the bitmap.  */

static void
check_operand (tree t, bitmap bm)
{
  if (!t) return;

  /* FIXME -- PROFILE-RESTRUCTURE: Change the call from DECL_UID to
     get the uid from the var_ann field. */    
  if ((TREE_CODE (t) == VAR_DECL) && has_proper_scope_for_analysis (t))
    bitmap_set_bit (bm, DECL_UID (t));
}

/* Examine tree T for references to static variables. All internal
   references like array references or indirect references are added
   to the INTERNAL_BM. Direct references are added to BASE_BM.  When
   this is called from the rhs or recursively, both bitmap operands
   are for the read bitmap.  When called from the lhs, the BASE_BM is
   the write bitmap.  */

static void
check_tree (tree t, bitmap base_bm, bitmap internal_bm)
{
  if ((TREE_CODE (t) == EXC_PTR_EXPR) || (TREE_CODE (t) == FILTER_EXPR))
    return;

  while (TREE_CODE (t) == REALPART_EXPR 
	 || TREE_CODE (t) == IMAGPART_EXPR
	 || handled_component_p (t))
    {
      if (TREE_CODE (t) == ARRAY_REF)
	check_operand (TREE_OPERAND (t, 1), internal_bm);
      t = TREE_OPERAND (t, 0);
    }

  /* The bottom of an indirect reference can only be read, not
     written.  So just recurse and but what ever we find, check it
     against the read bitmaps.  */
  if (INDIRECT_REF_P (t))
    check_tree (TREE_OPERAND (t, 0), internal_bm, internal_bm);

  if (SSA_VAR_P (t))
    check_operand (t, base_bm);
}

/* Scan tree T to see if there are any addresses taken in within T.  */

static void 
look_for_address_of (tree t)
{
  if (TREE_CODE (t) == ADDR_EXPR)
    {
      tree x = get_base_var (t);
      if ((TREE_CODE (x) == VAR_DECL) && has_proper_scope_for_analysis (x))
	{
	  if (dump_file)
	    fprintf (dump_file, "\nadding address:%s",
		     lang_hooks.decl_printable_name (x, 2));
	  
	  /* FIXME -- PROFILE-RESTRUCTURE: Change the call from
	     DECL_UID to get the uid from the var_ann field. */    
	  bitmap_set_bit (module_statics_escape, DECL_UID (x));
	}
    }
}


/* Check to see if T is a read or address of operation on a static var
   we are interested in analyzing.  FN is passed in to get access to
   its bit vectors.  */

static void
check_rhs_var (struct cgraph_node *fn, tree t)
{
  look_for_address_of (t);

  if (fn == NULL) 
    return;

  /* FIXME -- PROFILE-RESTRUCTURE: Change the call from DECL_UID to
     get the uid from the var_ann field. */    
  check_tree(t,
	     fn->static_vars_info->local->statics_read_by_decl_uid,
	     fn->static_vars_info->local->statics_read_by_decl_uid);
}

/* Check to see if T is an assignment to a static var we are
   interrested in analyzing.  FN is passed in to get access to its bit
   vectors.
*/

static void
check_lhs_var (struct cgraph_node *fn, tree t)
{
  if (fn == NULL) 
    return;

  /* FIXME -- PROFILE-RESTRUCTURE: Change the call from DECL_UID to
     get the uid from the var_ann field. */    
  check_tree(t, 
	     fn->static_vars_info->local->statics_written_by_decl_uid,
	     fn->static_vars_info->local->statics_read_by_decl_uid);
}

/* This is a scaled down version of get_asm_expr_operands from
   tree_ssa_operands.c.  The version there runs much later and assumes
   that aliasing information is already available. Here we are just
   trying to find if the set of inputs and outputs contain references
   or address of operations to local static variables.  FN is the
   function being analyzed and STMT is the actual asm statement.  */

static void
get_asm_expr_operands (struct cgraph_node * fn, tree stmt)
{
  int noutputs = list_length (ASM_OUTPUTS (stmt));
  const char **oconstraints
    = (const char **) alloca ((noutputs) * sizeof (const char *));
  int i;
  tree link;
  const char *constraint;
  bool allows_mem, allows_reg, is_inout;
  
  for (i=0, link = ASM_OUTPUTS (stmt); link; ++i, link = TREE_CHAIN (link))
    {
      oconstraints[i] = constraint
	= TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (link)));
      parse_output_constraint (&constraint, i, 0, 0,
			       &allows_mem, &allows_reg, &is_inout);
      
      /* Memory operands are addressable.  Note that STMT needs the
	 address of this operand.  */
      if (!allows_reg && allows_mem) 
	check_lhs_var (fn, TREE_VALUE (link));
    }

  for (link = ASM_INPUTS (stmt); link; link = TREE_CHAIN (link))
    {
      constraint
	= TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (link)));
      parse_input_constraint (&constraint, 0, 0, noutputs, 0,
			      oconstraints, &allows_mem, &allows_reg);
      
      /* Memory operands are addressable.  Note that STMT needs the
	 address of this operand.  */
      if (!allows_reg && allows_mem) 
	check_rhs_var (fn, TREE_VALUE (link));
    }
  
  for (link = ASM_CLOBBERS (stmt); link; link = TREE_CHAIN (link))
    if (simple_cst_equal(TREE_VALUE (link), memory_identifier_string) == 1) 
      {
	/* Abandon all hope, ye who enter here. */
	fn->local.calls_read_all = true;
	fn->local.calls_write_all = true;
      }      
}

/* Check the parameters of a function call from CALLER to CALL_EXPR to
   see if any of them are static vars.  Also check to see if this is
   either an indirect call, a call outside the compilation unit, or
   has special attributes that effect the clobbers.  The caller
   parameter is the tree node for the caller and the second operand is
   the tree node for the entire call expression.  */

static void
process_call_for_static_vars(struct cgraph_node * caller, tree call_expr) 
{
  int flags = call_expr_flags(call_expr);
  tree operandList = TREE_OPERAND (call_expr, 1);
  tree operand;

  for (operand = operandList;
       operand != NULL_TREE;
       operand = TREE_CHAIN (operand))
    {
      tree argument = TREE_VALUE (operand);
      check_rhs_var (caller, argument);
    }

  /* Const and pure functions have less clobber effects than other
     functions so we process these first.  Otherwise if it is a call
     outside the compilation unit or an indirect call we punt.  This
     leaves local calls which will be processed by following the call
     graph.  */  
  if (flags & ECF_CONST) 
    return;
  else if (flags & ECF_PURE) 
    caller->local.calls_write_all = true;
  else 
    {
      tree callee_t = get_callee_fndecl (call_expr);
      if (callee_t == NULL) 
	{
	  /* Indirect call. */
	  caller->local.calls_read_all = true;
	  caller->local.calls_write_all = true;
	}
      else 
	{
 	  struct cgraph_node* callee = cgraph_node(callee_t);

	  if (callee->local.external) 
	    {
	      caller->local.calls_read_all = true;
	      caller->local.calls_write_all = true;
	    }
	}
    }
}

/* FIXME -- PROFILE-RESTRUCTURE: Change to walk by explicitly walking
   the basic blocks rather than calling walktree.  */    

/* Walk tree and record all calls.  Called via walk_tree.  FIXME When
   this is moved into the tree-profiling-branch, and is dealing with
   low GIMPLE, this routine should be changed to use tree iterators
   rather than being a walk_tree callback.  The data is the function
   that is being scanned.  */
/* TP is the part of the tree currently under the
   microscope. WALK_SUBTREES is part of the walk_tree api but is
   unused here.  DATA is cgraph_node of the function being walked.  */

static tree
scan_for_static_refs (tree *tp, 
		      int *walk_subtrees ATTRIBUTE_UNUSED, 
		      void *data)
{
  struct cgraph_node *fn = data;
  tree t = *tp;
  
  switch (TREE_CODE (t))  
    {
    case VAR_DECL:
      if (DECL_INITIAL (t))
	walk_tree (&DECL_INITIAL (t), scan_for_static_refs, fn, visited_nodes);
      break;

    case MODIFY_EXPR:
      {
	/* First look on the lhs and see what variable is stored to */
	tree rhs = TREE_OPERAND (t, 1);
	check_lhs_var (fn, TREE_OPERAND (t, 0));
	/* Next check the operands on the rhs to see if they are ok. */
	switch (TREE_CODE_CLASS (TREE_CODE (rhs))) 
	  {
	  case tcc_binary:
	    check_rhs_var (fn, TREE_OPERAND (rhs, 0));
	    check_rhs_var (fn, TREE_OPERAND (rhs, 1));
	    break;
	  case tcc_unary:
	  case tcc_reference:
	    check_rhs_var (fn, TREE_OPERAND (rhs, 0));
	    break;
	  case tcc_declaration:
	    check_rhs_var (fn, rhs);
	    break;
	  case tcc_expression:
	    switch (TREE_CODE (rhs)) 
	      {
	      case ADDR_EXPR:
		check_rhs_var (fn, rhs);
		break;
	      case CALL_EXPR: 
		process_call_for_static_vars (fn, rhs);
		break;
	      default:
		break;
	      }
	    break;
	  default:
	    break;
	  }
      }
      break;
      
      
    case CALL_EXPR: 
      process_call_for_static_vars (fn, t);
      break;
      
    case ASM_EXPR:
      get_asm_expr_operands (fn, t);
      break;
      
    default:
      break;
    }
  return NULL;
}

/* Lookup the tree node for the static variable that has UID and
   conver the name to a string for debugging.  */

static const char *
get_static_name_by_uid (int index)
{
  splay_tree_node stn = splay_tree_lookup (static_vars_to_consider_by_uid, index);
  if (stn)
    return lang_hooks.decl_printable_name ((tree)(stn->value), 2);
  return NULL;
}

/* FIXME -- PROFILE-RESTRUCTURE: Change all *_decl_uid to *_ann_uid.  */

/* Or in all of the bits from every callee into X, the caller's, bit
   vector.  There are several cases to check to avoid the sparse
   bitmap oring.  */

static void
propagate_bits (struct cgraph_node *x)
{
  ipa_static_vars_info_t x_info = x->static_vars_info;
  ipa_global_static_vars_info_t x_global = x_info->global;

  struct cgraph_edge *e;
  for (e = x->callees; e; e = e->next_callee) 
    {
      struct cgraph_node *y = e->callee;

      /* Only look at the master nodes and skip external nodes.  */
      y = cgraph_master_clone (y);
      if (y)
	{
	  ipa_static_vars_info_t y_info; 
	  ipa_global_static_vars_info_t y_global;
	  y_info = y->static_vars_info;
	  y_global = y_info->global;

	  if (x_global->statics_read_by_decl_uid != all_module_statics)
	    {
	      if (y_global->statics_read_by_decl_uid == all_module_statics) 
		x_global->statics_read_by_decl_uid = all_module_statics;
	      /* Skip bitmaps that are pointer equal to node's bitmap
		 (no reason to spin within the cycle).  */
	      else if (x_global->statics_read_by_decl_uid 
		       != y_global->statics_read_by_decl_uid)
		bitmap_a_or_b (x_global->statics_read_by_decl_uid,
			       x_global->statics_read_by_decl_uid,
			       y_global->statics_read_by_decl_uid);
	    }

	  if (x_global->statics_written_by_decl_uid != all_module_statics)
	    {
	      if (y_global->statics_written_by_decl_uid == all_module_statics) 
		x_global->statics_written_by_decl_uid = all_module_statics;
	      /* Skip bitmaps that are pointer equal to node's bitmap
		 (no reason to spin within the cycle).  */
	      else if (x_global->statics_written_by_decl_uid 
		       != y_global->statics_written_by_decl_uid)
		bitmap_a_or_b (x_global->statics_written_by_decl_uid,
			       x_global->statics_written_by_decl_uid,
			       y_global->statics_written_by_decl_uid);
	    }
	}
    }
}

/* FIXME -- PROFILE-RESTRUCTURE: Change all *_decl_uid to *_ann_uid.  */

/* Look at all of the callees of X to see which ones represent inlined
   calls.  For each of these callees, merge their local info into TARGET
   and check their children recursively.  */

static void 
merge_callee_local_info (struct cgraph_node *target, 
			 struct cgraph_node *x)
{
  struct cgraph_edge *e;
  ipa_local_static_vars_info_t info = target->static_vars_info->local;

  /* Make the world safe for tail recursion.  */
  if (x->aux) 
    return;

  x->aux = x;

  for (e = x->callees; e; e = e->next_callee) 
    {
      struct cgraph_node *y = e->callee;
      if (y->global.inlined_to) 
	{
	  ipa_static_vars_info_t y_info;
	  ipa_local_static_vars_info_t y_l;
	  y = cgraph_master_clone (y);
	  y_info = y->static_vars_info;
	  y_l = y_info->local;
	  bitmap_a_or_b (info->statics_read_by_decl_uid,
			 info->statics_read_by_decl_uid,
			 y_l->statics_read_by_decl_uid);
	  bitmap_a_or_b (info->statics_written_by_decl_uid,
			 info->statics_written_by_decl_uid,
			 y_l->statics_written_by_decl_uid);
	  target->local.calls_read_all |= y->local.calls_read_all;
	  target->local.calls_write_all |= y->local.calls_write_all;
	  merge_callee_local_info (target, y);
	}
    }

  x->aux = NULL;
}

/* The init routine for analyzing global static variable usage. See
   comments at top for description.  */

static void 
ipa_init (void) 
{
  memory_identifier_string = build_string(7, "memory");

  static_vars_to_consider_by_uid =
    splay_tree_new_ggc (splay_tree_compare_ints);

  module_statics_escape = BITMAP_XMALLOC ();
  all_module_statics = BITMAP_XMALLOC ();

  /* There are some shared nodes, in particular the initializers on
     static declarations.  We do not need to scan them more than once
     since all we would be interrested in are the addressof
     operations.  */
  visited_nodes = pointer_set_create ();
}

/* Check out the rhs of a static or global initialization VNODE to see
   if any of them contain addressof operations.  Note that some of
   these variables may not even be referenced in the code in this
   compilation unit but their right hand sides may contain references
   to variables defined within this unit.  */

static void 
analyze_variable (struct cgraph_varpool_node *vnode)
{
  tree global = vnode->decl;
  if (!memory_identifier_string) ipa_init();

  if (TREE_CODE (global) == VAR_DECL)
    if (DECL_INITIAL (global)) 
      walk_tree (&DECL_INITIAL (global), scan_for_static_refs, 
		 NULL, visited_nodes);
}

/* This is the main routine for finding the reference patterns for
   global variables within a function FN.  */

static void
analyze_function (struct cgraph_node *fn)
{
  tree decl = fn->decl;
  ipa_static_vars_info_t info 
    = xcalloc (1, sizeof (struct ipa_static_vars_info_d));
  ipa_local_static_vars_info_t l
    = xcalloc (1, sizeof (struct ipa_local_static_vars_info_d));

  if (!memory_identifier_string) ipa_init();

  /* Add the info to the tree's annotation.  */
  var_ann_t var_ann = get_var_ann (fn->decl);
  fn->static_vars_info = info;
  var_ann->static_vars_info = info;

  info->local = l;
  l->statics_read_by_decl_uid = BITMAP_XMALLOC ();
  l->statics_written_by_decl_uid = BITMAP_XMALLOC ();
  l->statics_read_by_ann_uid = BITMAP_XMALLOC ();
  l->statics_written_by_ann_uid = BITMAP_XMALLOC ();

  if (dump_file)
    fprintf (dump_file, "\n local analysis of %s", cgraph_node_name (fn));
  
  walk_tree (&DECL_SAVED_TREE (decl), scan_for_static_refs, fn, visited_nodes);
}

/* Produce the global information by preforming a transitive closure
   on the local information that was produced by ipa_analyze_function
   and ipa_analyze_variable.  */


static void
static_execute (void)
{
  struct cgraph_node *node;
  struct cgraph_node *w;
  struct cgraph_node **order =
    xcalloc (cgraph_n_nodes, sizeof (struct cgraph_node *));
  int order_pos = 0;
  int i;

  if (!memory_identifier_string) ipa_init();
  pointer_set_destroy (visited_nodes);
  visited_nodes = NULL;

  /* Prune out the variables that were found to behave badly
     (i.e. have there address taken).  */
  {
    int index;
    bitmap_iterator bi;

    EXECUTE_IF_SET_IN_BITMAP (module_statics_escape, 0, index, bi)
      {
	splay_tree_remove (static_vars_to_consider_by_uid, index);
      }
    bitmap_operation (all_module_statics, all_module_statics,
		      module_statics_escape, BITMAP_AND_COMPL);

    BITMAP_XFREE(module_statics_escape);

/*     {   */
/*       FILE *ok_statics_file = fopen("/home/zadeck/ok_statics", "r");   */
/*       char line[100];   */
/*       int value;  */
/*       fgets(line, sizeof(line), ok_statics_file);   */
/*       sscanf(line, "%d", &value);   */
/*       for (i = 0; i < value; i++ )   */
/*   	{   */
/*   	  int j = bitmap_first_set_bit(all_module_statics);   */
/* 	  if (j == -1) break;   */
/*   	  fprintf(stderr, "  not considering %s\n",    */
/*   		  get_static_name_by_uid (j));   */
/*   	  splay_tree_remove (static_vars_to_consider_by_uid, j);   */
/*   	  bitmap_clear_bit(all_module_statics, j);   */
/*   	}   */
/*     }   */

    if (dump_file)
      EXECUTE_IF_SET_IN_BITMAP (all_module_statics, 0, index, bi)
	{
	  fprintf (dump_file, "\nPromotable global:%s",
		   get_static_name_by_uid (index));
	}

    for (i = 0; i < order_pos; i++ )
      {
	ipa_local_static_vars_info_t l;
	node = order[i];
	l = node->static_vars_info->local;

	bitmap_a_and_b (l->statics_read_by_decl_uid, 
			l->statics_read_by_decl_uid,
			all_module_statics);
	bitmap_a_and_b (l->statics_written_by_decl_uid, 
			l->statics_written_by_decl_uid,
			all_module_statics);
      }
  }

  if (dump_file)
    {
      for (i = 0; i < order_pos; i++ )
	{
	  int index;
	  ipa_local_static_vars_info_t l;
	  bitmap_iterator bi;

	  node = order[i];
	  l = node->static_vars_info->local;
	  fprintf (dump_file, 
		   "\nFunction name:%s/%i:", 
		   cgraph_node_name (node), node->uid);
	  fprintf (dump_file, "\n  locals read: ");
	  EXECUTE_IF_SET_IN_BITMAP (l->statics_read_by_decl_uid,
				    0, index, bi)
	    {
	      fprintf (dump_file, "%s ",
		       get_static_name_by_uid (index));
	    }
	  fprintf (dump_file, "\n  locals written: ");
	  EXECUTE_IF_SET_IN_BITMAP (l->statics_written_by_decl_uid,
				    0, index, bi)
	    {
	      fprintf(dump_file, "%s ",
		      get_static_name_by_uid (index));
	    }
	}
    }

  /* Propagate the local information thru the call graph to produce
     the global information.  All the nodes within a cycle will have
     the same info so we collapse cycles first.  Then we can do the
     propagation in one pass from the leaves to the roots.  */
  order_pos = reduced_inorder (order, true);
  if (dump_file)
    print_order(stderr, "reduced", order, order_pos);

  for (i = 0; i < order_pos; i++ )
    {
      ipa_static_vars_info_t node_info;
      ipa_global_static_vars_info_t node_g = 
	xcalloc (1, sizeof (struct ipa_global_static_vars_info_d));
      ipa_local_static_vars_info_t node_l;
      
      bool read_all;
      bool write_all;

      node = order[i];
      node_info = node->static_vars_info;
      if (!node_info) 
	{
	  printf(stderr, "bad cgraph node\n");
	  dump_cgraph_node (stderr, node);
	  printf(stderr, "==================\n");
	  dump_cgraph (stderr);
	  abort ();
	}

      node_info->global = node_g;
      node_l = node_info->local;

      read_all = node->local.calls_read_all;
      write_all = node->local.calls_write_all;

      /* If any node in a cycle is calls_read_all or calls_write_all
	 they all are. */
      w = node->next_cycle;
      while (w)
	{
	  read_all |= w->local.calls_read_all;
	  write_all |= w->local.calls_write_all;
	  w = w->next_cycle;
	}

      /* Initialized the bitmaps for the reduced nodes */
      if (read_all) 
	node_g->statics_read_by_decl_uid = all_module_statics;
      else 
	{
	  node_g->statics_read_by_decl_uid = BITMAP_XMALLOC ();
	  bitmap_copy (node_g->statics_read_by_decl_uid, 
		       node_l->statics_read_by_decl_uid);
	}

      if (write_all) 
	node_g->statics_written_by_decl_uid = all_module_statics;
      else
	{
	  node_g->statics_written_by_decl_uid = BITMAP_XMALLOC ();
	  bitmap_copy (node_g->statics_written_by_decl_uid, 
		       node_l->statics_written_by_decl_uid);
	}

      w = node->next_cycle;
      while (w)
	{
	  /* All nodes within a cycle share the same global info bitmaps.  */
	  ipa_static_vars_info_t w_info = w->static_vars_info;
	  ipa_local_static_vars_info_t w_l;

	  w_info->global = node_g;
	  w_l = w_info->local;
	  
	  /* These global bitmaps are initialized from the local info
	     of all of the nodes in the region.  However there is no
	     need to do any work if the bitmaps were set to
	     all_module_statics.  */
	  if (!read_all)
	    bitmap_a_or_b (node_g->statics_read_by_decl_uid,
			   node_g->statics_read_by_decl_uid,
			   w_l->statics_read_by_decl_uid);
	  if (!write_all)
	    bitmap_a_or_b (node_g->statics_written_by_decl_uid,
			   node_g->statics_written_by_decl_uid,
			   w_l->statics_written_by_decl_uid);
	  w = w->next_cycle;
	}

      propagate_bits (node);

      w = node->next_cycle;
      while (w)
	{
	  propagate_bits (w);
	  w = w->next_cycle;
	}
    }

  if (dump_file)
    {
      for (i = 0; i < order_pos; i++ )
	{
	  ipa_static_vars_info_t node_info;
	  ipa_global_static_vars_info_t node_g;
	  int index;
	  bitmap_iterator bi;

	  node = order[i];
	  node_info = node->static_vars_info;
	  node_g = node_info->global;
	  fprintf (dump_file, 
		   "\nFunction name:%s/%i:", 
		   cgraph_node_name (node), node->uid);
	  w = node->next_cycle;
	  while (w) 
	    {
	      fprintf (dump_file, "\n  next cycle: %s/%i ",
		       cgraph_node_name (w), w->uid);
	      w = w->next_cycle;
	    }
	  fprintf (dump_file, "\n  globals read: ");
	  EXECUTE_IF_SET_IN_BITMAP (node_g->statics_read_by_decl_uid,
				    0, index, bi)
	    {
	      fprintf (dump_file, "%s ",
		       get_static_name_by_uid (index));
	    }
	  fprintf (dump_file, "\n  globals written: ");
	  EXECUTE_IF_SET_IN_BITMAP (node_g->statics_written_by_decl_uid,
				    0, index, bi)
	    {
	      fprintf (dump_file, "%s ",
		       get_static_name_by_uid (index));
	    }
	}
    }

  /* Need to fix up the local information sets.  The information that
     has been gathered so far is preinlining.  However, the
     compilation will progress post inlining so the local sets for the
     inlined calls need to be merged into the callers.  */
  for (i = 0; i < order_pos; i++ )
    {
      node = order[i];
      if (cgraph_is_immortal_master_clone (node))
	merge_callee_local_info (node, node);
    }

  /* Cleanup. */
  for (i = 0; i < order_pos; i++ )
    {
      ipa_static_vars_info_t node_info;
      ipa_global_static_vars_info_t node_g;
      node = order[i];
      node_info = node->static_vars_info;
      node_g = node_info->global;
      
      node_g->var_anns_valid = false;

      /* Create the complimentary sets.  These are more useful for
	 certain apis.  */
      node_g->statics_not_read_by_decl_uid = BITMAP_XMALLOC ();
      node_g->statics_not_written_by_decl_uid = BITMAP_XMALLOC ();

      /* FIXME -- PROFILE-RESTRUCTURE: Delete next 4 assignments.  */
      node_g->statics_read_by_ann_uid = BITMAP_XMALLOC ();
      node_g->statics_written_by_ann_uid = BITMAP_XMALLOC ();
      node_g->statics_not_read_by_ann_uid = BITMAP_XMALLOC ();
      node_g->statics_not_written_by_ann_uid = BITMAP_XMALLOC ();

      if (node_g->statics_read_by_decl_uid != all_module_statics) 
	{
	  bitmap_operation (node_g->statics_not_read_by_decl_uid, 
			    all_module_statics,
			    node_g->statics_read_by_decl_uid,
			    BITMAP_AND_COMPL);
	}

      if (node_g->statics_written_by_decl_uid != all_module_statics) 
	bitmap_operation (node_g->statics_not_written_by_decl_uid, 
			  all_module_statics,
			  node_g->statics_written_by_decl_uid,
			  BITMAP_AND_COMPL);

      w = node;

      while (w)
	{
	  struct cgraph_node * last = w;
	  ipa_static_vars_info_t w_info = w->static_vars_info;
	  ipa_local_static_vars_info_t w_l = w_info->local;
	  w_l->var_anns_valid = false;

	  w = w->next_cycle;
	  last->next_cycle = NULL;
	}
    }

  free (order);
}


static bool
gate_static_vars (void)
{
  return flag_unit_at_a_time != 0;
}

struct tree_opt_pass pass_ipa_static =
{
  "static-var",				/* name */
  gate_static_vars,			/* gate */
  analyze_function,                     /* IPA function */
  analyze_variable,		        /* IPA variable */
  static_execute,			/* execute */
  NULL, NULL,				/* IPA modification */
  NULL,					/* sub */
  NULL,					/* next */
  0,					/* static_pass_number */
  0,				        /* tv_id */
  0,	                                /* properties_required */
  0,					/* properties_provided */
  0,					/* properties_destroyed */
  0,					/* todo_flags_start */
  0,                                    /* todo_flags_finish */
  0					/* letter */
};


#include "gt-ipa-static-vars-anal.h"
/* Callgraph handling code.
   Copyright (C) 2004 Free Software Foundation, Inc.
   Contributed by Kenneth Zadeck <zadeck@naturalbridge.com>

This file is part of GCC.

GCC 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.

GCC 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 GCC; see the file COPYING.  If not, write to the Free
Software Foundation, 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA.  */

#ifndef GCC_IPA_STATIC_H
#define GCC_IPA_STATIC_H
#include "bitmap.h"
#include "tree.h"

/* The static variables defined within the compilation unit that are
   loaded or stored directly by function that owns this structure.  */ 

struct ipa_local_static_vars_info_d 
{
  bitmap statics_read_by_decl_uid;
  bitmap statics_written_by_decl_uid;
  bitmap statics_read_by_ann_uid;
  bitmap statics_written_by_ann_uid;

  /* Var_anns_valid is reset at the start of compilation for each
     function because the indexing that the "_var_anns" is based
     on is invalidated between function compilations.  This allows for
     lazy creation of the "_var_ann" variables.  */
  bool var_anns_valid;
};

struct ipa_global_static_vars_info_d
{
  bitmap statics_read_by_decl_uid;
  bitmap statics_written_by_decl_uid;
  bitmap statics_read_by_ann_uid;
  bitmap statics_written_by_ann_uid;
  bitmap statics_not_read_by_decl_uid;
  bitmap statics_not_written_by_decl_uid;
  bitmap statics_not_read_by_ann_uid;
  bitmap statics_not_written_by_ann_uid;

  /* Var_anns_valid is reset at the start of compilation for each
     function because the indexing that the "_var_anns" is based
     on is invalidated between function compilations.  This allows for
     lazy creation of the "_var_ann" variables.  */
  bool var_anns_valid;
};

/* Statics that are read and written by some set of functions. The
   local ones are based on the loads and stores local to the function.
   The global ones are based on the local info as well as the
   transitive closure of the functions that are called.  The
   structures are separated to allow the global structures to be
   shared between several functions since every function within a
   strongly connected component will have the same information.  This
   sharing saves both time and space in the computation of the vectors
   as well as their translation from decl_uid form to ann_uid
   form.  */ 

typedef struct ipa_local_static_vars_info_d *ipa_local_static_vars_info_t;
typedef struct ipa_global_static_vars_info_d *ipa_global_static_vars_info_t;

struct ipa_static_vars_info_d 
{
  ipa_local_static_vars_info_t local;
  ipa_global_static_vars_info_t global;
};

typedef struct ipa_static_vars_info_d *ipa_static_vars_info_t;

/* The cgraph data structure.
   Each function decl has assigned cgraph_node listing callees and callers.  */

/* In ipa-static-vars-anal.c  */
extern tree memory_identifier_string;

void   ipa_static_reset_maps (void);
bitmap ipa_get_statics_read_local (tree fn);
bitmap ipa_get_statics_written_local (tree fn);
bitmap ipa_get_statics_read_global (tree fn);
bitmap ipa_get_statics_written_global (tree fn);
bitmap ipa_get_statics_not_read_global (tree fn);
bitmap ipa_get_statics_not_written_global (tree fn);

#endif  /* GCC_IPA_STATIC_H  */
/* Promotion of static variables to ssa registers
   Copyright (C) 2004 Free Software Foundation, Inc.
   Contributed by Kenneth Zadeck <zadeck@naturalbridge.com>

This file is part of GCC.

GCC 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.

GCC 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 GCC; see the file COPYING.  If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.  */

#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "tree.h"
#include "basic-block.h"
#include "tree-flow.h"
#include "ipa-static.h"
#include "bitmap.h"
#include "tree-pass.h"
#include "flags.h"

/*
The main idea is to promote some static variables from memory to ssa
registers.  This transformation is only applied to those static
variables for which the effects of subroutine calls can be understood.
Such infomation is provided by functions in cgraphunit.c.

The following table shows the actions that are taken to promote
variables.  The analysis in cgraphunit constructs information about
both local usage and the effect of any particular call.  Variables are
broken into 4 categories: only-read, only-write, read-write, and no
information.  (No information variables are never promoted.)  

All information is of the "may" variety: if a function is marked read,
it means the call may read the variable, but it also may not read the
variable.

There are two possible ways to perform the promotion: assume that the
static is live everywhere or compute the minimal live range for the
static variable.

The minimal live range path has a lot of problems:

1) live variables and upwards exposed uses must be first comuputed.
2) new machiney must be invented to prevent code motion algorithms
from floating a use of the suragate register across a register
function call that clobbers the variable, but was not in any minimal
live range at the time of this analysis.

While the first problem is simply a lot of code, the second problem
requires a new mechanism for pinning code and teaching all passes that
can move code to obey this new fenceposts.  

The maximum live range path has the problem that this technique can
create many false live ranges where the register is loaded after on
call only to be stored back right before the next call.  This will eat
a certain amount of space and requires special smarts to get rid of them.

There are really 7 situations to cover in the following table.  

action             read                    write                   read-write

             -+---------------------------------------------------------------

entry         |  load                    load                    load
              |
load          |  getfromreg              xxxxx                   getfromreg
              |
store         |  xxxx                    puttoreg                puttoreg
              |
call-read     |  noaction                store before            store before
              |
call-write    |  load after              load after              load after
              |
call-readwrite|  load after              store before            store before
              |                          load after              load after  
              |
return        |  no action               store                   store         


l-r	l-w	c-r	c-w	store-b	load-a

0	0	0	0	0	0
0	0	0	1	0	0
0	0       1      	0	0	0
0	0	1	1	0	0
0	1	0	0	0	0
0	1	0	1	1	1	 
0	1       1      	0	1	0
0	1	1	1	1	1
1	0	0	0	0	0
1	0	0	1       0       1
1	0       1      	0	0	0	
1	0	1	1	0	1
1	1	0	0	0	0
1	1	0	1	1	1
1	1       1      	0	1	0
1	1	1	1	1	1

store_before = local_written & (callee_read | callee_written)
load_after = (local_read | local_written) & callee_written


*/


/* All of the static variables under consideration by this pass that
   do reads or writes withing this function.   */
static bitmap local_read;
static bitmap local_written;
static bitmap local_all;

/* Map indexed by the var_ann uid that contains the shadow register
   for each static variable to be promoted. */
static tree *static_map;


/* Return true if the asm STMT clobbers memory.  */
static bool
asm_clobbers_mem (tree stmt)
{
  tree link;
  for (link = ASM_CLOBBERS (stmt); link; link = TREE_CHAIN (link))
    if (simple_cst_equal(TREE_VALUE (link), memory_identifier_string) == 1) 
      return true;

  return false;
}

/* Return a INPUT_BITMAP for the asm inputs and OUTPUT_BITMAP for the
   asm outputs of static variables written by the asm STMT.  */
static void
get_asm_read_and_write (bitmap input_bitmap, bitmap output_bitmap, tree stmt) 
{
  int noutputs = list_length (ASM_OUTPUTS (stmt));
  const char **oconstraints
    = (const char **) alloca ((noutputs) * sizeof (const char *));
  int i;
  tree link;
  const char *constraint;
  bool allows_mem, allows_reg, is_inout;

  for (i=0, link = ASM_OUTPUTS (stmt); link; ++i, link = TREE_CHAIN (link))
    {
      oconstraints[i] = constraint
	= TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (link)));
      parse_output_constraint (&constraint, i, 0, 0,
	  &allows_mem, &allows_reg, &is_inout);

      /* Do not actually check if the variable is a static variable,
         since it will be anded with the set of variables we are
         interrested in later.  */
      if (!allows_reg && allows_mem)
	{
	  tree var = TREE_VALUE (link);
	  var = get_base_address (var);
	  if (TREE_CODE (var) == VAR_DECL)
	    {
	      var_ann_t va = var_ann (var);
	      if (va) 
		bitmap_set_bit(output_bitmap, va->uid);
	    }
	}
    }

  for (link = ASM_INPUTS (stmt); link; link = TREE_CHAIN (link))
    {
      constraint
	= TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (link)));
      parse_input_constraint (&constraint, 0, 0, noutputs, 0,
			      oconstraints, &allows_mem, &allows_reg);
      
      if (!allows_reg && allows_mem)
	{
	  tree var = TREE_VALUE (link);
	  var = get_base_address (var);
	  if (TREE_CODE (var) == VAR_DECL)
	    {
	      var_ann_t va = var_ann (var);
	      if (va) 
		bitmap_set_bit(input_bitmap, va->uid);
	    }
	}
    }
}

/* Generate a series of loads from the static variables pointed to by
   b1 && b2 or just b1 (if b2 is NULL) and insert them after
   bsi).  */
static void
gen_loads (bitmap b1, bitmap b2, block_stmt_iterator *bsi) 
{
  bitmap result;
  bitmap_iterator bi;
  int index;
  tree list = NULL;

  if (b2) 
    {
      result = BITMAP_XMALLOC ();
      bitmap_a_and_b (result, b1, b2);
    }
  else 
    result = b1;

  EXECUTE_IF_SET_IN_BITMAP(result, 0, index, bi) 
      {
	tree src = referenced_var (index);
	tree dest = static_map[index];
	tree stmt = build (MODIFY_EXPR, TREE_TYPE (src), dest, src);
	append_to_statement_list (stmt, &list);
      }

  if (list)
    sra_insert_after (bsi, list);
			   
  if (b2)
    BITMAP_XFREE (result);
}

/* Generate a series of stores to the static variables pointed to by
   b1 && b2 or just b1 (if b2 is NULL) and insert them before
   bsi).  */
static void
gen_stores (bitmap b1, bitmap b2, block_stmt_iterator *bsi) 
{
  bitmap result;
  bitmap_iterator bi;
  int index;
  tree list = NULL;

  if (b2) 
    {
      result = BITMAP_XMALLOC ();
      bitmap_a_and_b (result, b1, b2);
    }
  else 
    result = b1;

  EXECUTE_IF_SET_IN_BITMAP(result, 0, index, bi) 
      {
	tree src = static_map[index];
	tree dest = referenced_var (index);
	tree stmt = build (MODIFY_EXPR, TREE_TYPE (src), dest, src);
	append_to_statement_list (stmt, &list);
      }

  if (list)
    sra_insert_before (bsi, list);
			   
  if (b2)
    BITMAP_XFREE (result);
}

/* Replace the static references if it exists in the TPTR.  */

static void 
try_replace_operand(tree * tptr)
{
  tree t = *tptr;
  if (TREE_CODE (t) == VAR_DECL)
    {
      var_ann_t va = var_ann (t);
      tree replacement = static_map[va->uid];
      if (replacement)
	*tptr = replacement;
    }
}

/* Walk an expression TPTR replacing all of the static references.  */
static void
try_replace (tree *tptr) 
{
  tree t = *tptr;
  if ((TREE_CODE (t) == EXC_PTR_EXPR) || (TREE_CODE (t) == FILTER_EXPR))
    return;

  /* The INTEGER_CST is because some people use cute things like &0->a
     for offsetof.  */
  while (!SSA_VAR_P (t) 
	 && (!CONSTANT_CLASS_P (t)) 
	 && TREE_CODE (t) != LABEL_DECL
	 && TREE_CODE (t) != FUNCTION_DECL)
    {
      if (TREE_CODE (t) == ARRAY_REF)
	try_replace_operand (&TREE_OPERAND (t, 1));

      tptr = &TREE_OPERAND (t, 0);
      t = *tptr;
    }
  try_replace_operand (tptr);
}

/* Replace all the static references in the operand list of
   CALL_EXPR.  */

static void
try_replace_call_operands (tree call_expr) 
{
  tree operandList = TREE_OPERAND (call_expr, 1);
  tree operand;

  for (operand = operandList;
       operand != NULL_TREE;
       operand = TREE_CHAIN (operand))
 
    if (TREE_CODE(TREE_VALUE (operand)) != FUNCTION_DECL)
      try_replace (&TREE_VALUE (operand));
}

/* Generate loads and stores and replace all the static references in
   function FN using statement iterator SI. This form is used when
   there is not info available about the caller.  */
static void 
gen_dumb_call (tree fn, block_stmt_iterator si) 
{
  gen_stores (local_written, NULL, &si);
  try_replace (&TREE_OPERAND (fn, 0));
  try_replace_call_operands (fn);
  gen_loads (local_all, NULL, &si);
}


/* Generate loads and stores and replace all the static references in
   function FN using statement iterator SI.  */
static void 
try_replace_call (tree fn, block_stmt_iterator si)
{
  /* Store intersection of call_read and local_written
     registers back to memory before calling.  */
  /* int call_flags = call_expr_flags (fn); */
  tree callee = get_callee_fndecl (fn);
  if (callee) 
    {
      bitmap callee_all = BITMAP_XMALLOC ();
      bitmap callee_written = ipa_get_statics_written_global (callee);
      if (callee_written) 
	{
	  bitmap_a_or_b (callee_all, 
			 ipa_get_statics_read_global (callee), 
			 callee_written);
	  
	  gen_stores (local_written, callee_all, &si);
	  
	  if (TREE_CODE (callee) != FUNCTION_DECL)
	    try_replace (&TREE_OPERAND (fn, 0));
	  try_replace_call_operands (fn);
	  
	  /* This is a hack required because the call_flags are set on a
	     function by function basis during compilation.  Thus these
	     flags are only set if the callee has already been compiled.  */
	  /* if (!(call_flags & (ECF_PURE | ECF_CONST | ECF_NORETURN))) */
	  gen_loads (local_all, callee_written, &si);
	  BITMAP_XFREE (callee_all);
	}
      else gen_dumb_call (fn, si);
    }
  else
    gen_dumb_call (fn, si);
}


/* Walk the entire function looking uses or stores to global variables
and changing them to use ssa shadow registers.  */ 
static void
walk_function (void)
{
  basic_block bb;
  block_stmt_iterator si, ni;

  FOR_EACH_BB (bb)
    for (si = bsi_start (bb); !bsi_end_p (si); si = ni)
      {
	tree stmt = bsi_stmt (si);

	ni = si;
	bsi_next (&ni);

	switch (TREE_CODE (stmt))
	  {
	  case RETURN_EXPR:
	    /* Store all of the local_written registers back to memory
	       before returning. */
	    gen_stores (local_written, NULL, &si);
	    break;

	  case MODIFY_EXPR:
	    /* Change load of static to use of reg.  Change store of
	       static to store of reg.  */
	    {
	      tree t = TREE_OPERAND (stmt, 1);
	      tree *tptr = &TREE_OPERAND (stmt, 1);
	      
	      /* Check to see if the bottom of the right hand side is
		 a reference to a static variable that we care about.
		 If it is replace the static part with the ssa
		 register.  */
	      
	      /* Next check the operands on the rhs to see if they are ok. */
	      switch (TREE_CODE_CLASS (TREE_CODE (t))) 
		{
		case tcc_binary:
		  try_replace (&TREE_OPERAND (t, 0));
		  try_replace (&TREE_OPERAND (t, 1));
		  break;
		case tcc_unary:
		case tcc_reference:
		  try_replace (&TREE_OPERAND (t, 0));
		  break;
		case tcc_declaration:
		  try_replace (tptr);
		  break;
		case tcc_expression:
		  switch (TREE_CODE (t)) {
		  case ADDR_EXPR:
		    try_replace (tptr);
		    break;
		  case CALL_EXPR: 
		    try_replace_call (t, si);
		    break;
		  default:
		    break;
		  }
		  break;
		default:
		  break;
		}

	      /* Check to see if the bottom of the left hand side is a
		 reference to a static variable that we care about.
		 If it is replace the static part with the ssa
		 register.  */
	      try_replace(&TREE_OPERAND (stmt, 0));
	    }
	    break;
	  case CALL_EXPR:
	    try_replace_call (stmt, si);
   
	    break;
	  case ASM_EXPR:
	    /* If the asm clobbers memory, just store everything and
	       load it back.  */
	    if (asm_clobbers_mem (stmt)) 
	      { 
		gen_stores (local_written, NULL, &si);
		gen_loads (local_all, NULL, &si);
	      }
	    else 
	      {
		bitmap store_bitmap = BITMAP_XMALLOC ();
		bitmap load_bitmap = BITMAP_XMALLOC ();
		bitmap all_bitmap = BITMAP_XMALLOC ();
		/* The asm read generates a stores before, and the asm
		   write generates loads after.  */
		get_asm_read_and_write (store_bitmap, load_bitmap, stmt);
		bitmap_a_or_b (all_bitmap, store_bitmap, load_bitmap);
		
		gen_stores (local_written, all_bitmap , &si);
		gen_loads (local_all, load_bitmap, &si);
		
		BITMAP_XFREE (store_bitmap);
		BITMAP_XFREE (load_bitmap);
		BITMAP_XFREE (all_bitmap);
	      } 
	    break;
	    
	  default:
	    break;
	  }
      }
}


/* Main entry point for the promotion of statics to ssa regsisters. */

static void
execute_promote_statics (void)
{
  int index;
  bitmap_iterator bi;
  bitmap tb = ipa_get_statics_read_local (current_function_decl);

  /* There are some options that cause this pass to run even if file
     at a time is not set.  */
  if (!tb)
    return;

  sra_init_cache ();

  local_read = BITMAP_XMALLOC ();
  bitmap_copy (local_read, tb);
  tb = ipa_get_statics_written_local (current_function_decl);
  local_written = BITMAP_XMALLOC ();
  bitmap_copy (local_written, tb);

  /* Create the shadow registers for each static variable that is
     either read or written in this function.  */
  static_map = xcalloc (num_referenced_vars, sizeof (tree));

  local_all = BITMAP_XMALLOC ();
  tb = BITMAP_XMALLOC ();
  bitmap_a_or_b (local_all, local_read, local_written);
  EXECUTE_IF_SET_IN_BITMAP (local_all, 0, index, bi) 
      {
	tree svar = referenced_var (index);
	tree type = TREE_TYPE (svar);
	/* We only promote variables that are either scalars or if
	   they are aggregrates, they must be a type that sra is
	   willing to scalarize.  Otherwise there is no reason to
	   promote it a register.  

	   We also do not promote anything that is marked READONLY
	   since there is little gain.  The optimizations should
	   generally be able to look thru the operations and find the
	   constants.  */
	if ((!TREE_READONLY(svar)) 
	    && ((!AGGREGATE_TYPE_P (type)) 
		|| (sra_type_can_be_decomposed_p (type))))
	  {
	    tree tmp = create_tmp_var (type, get_name (svar));
	    add_referenced_tmp_var (tmp);
	    static_map[index] = tmp;

	    /* Insert loads from all read statics in the entry
	       block.  */
	    insert_edge_copies (build (MODIFY_EXPR, TREE_TYPE (svar), 
				       tmp, svar), 
				ENTRY_BLOCK_PTR);
	  }
	else 
	  {
	    /* There is nothing to be done with this variable.  */ 
	    bitmap_set_bit (tb, index);
	  }
      }

  /* Clear the to be ignored variables from the local maps.  */
  bitmap_operation (local_read, local_read, tb, BITMAP_AND_COMPL);
  bitmap_operation (local_written, local_written, tb, BITMAP_AND_COMPL);
  bitmap_operation (local_all, local_all, tb, BITMAP_AND_COMPL);

  walk_function ();
  bsi_commit_edge_inserts (NULL);

  BITMAP_XFREE (tb);
  BITMAP_XFREE (local_read);
  BITMAP_XFREE (local_written);
  BITMAP_XFREE (local_all);
  free (static_map);
}

static bool
gate_promote_statics (void)
{
  return flag_unit_at_a_time != 0;
}

struct tree_opt_pass pass_promote_statics = 
{
  "pstatic",				/* name */
  gate_promote_statics,			/* gate */
  NULL, NULL,				/* IPA analysis */
  execute_promote_statics,		/* execute */
  NULL, NULL,				/* IPA modification */
  NULL,					/* sub */
  NULL,					/* next */
  0,					/* static_pass_number */
  0,					/* tv_id */
  PROP_cfg ,                    	/* properties_required */
  0,					/* properties_provided */
  0,					/* properties_destroyed */
  0,					/* todo_flags_start */
  TODO_dump_func | TODO_rename_vars,	/* todo_flags_finish */
  0                                     /* letter */
};

? forJan.patch
? ipa-static-vars-anal.c
? ipa-static.h
? jan.patch
? tree-promote-statics.c
? testsuite/gcc.c-torture/execute/20041007-1.c
Index: Makefile.in
===================================================================
RCS file: /cvs/gcc/gcc/gcc/Makefile.in,v
retrieving revision 1.903.2.179.2.22
diff -c -3 -p -r1.903.2.179.2.22 Makefile.in
*** Makefile.in	26 Oct 2004 14:06:40 -0000	1.903.2.179.2.22
--- Makefile.in	29 Oct 2004 21:40:19 -0000
*************** RA_H = ra.h bitmap.h sbitmap.h hard-reg-
*** 701,709 ****
  RESOURCE_H = resource.h hard-reg-set.h
  SCHED_INT_H = sched-int.h $(INSN_ATTR_H) $(BASIC_BLOCK_H) $(RTL_H)
  INTEGRATE_H = integrate.h varray.h
  CFGLAYOUT_H = cfglayout.h $(BASIC_BLOCK_H)
  CFGLOOP_H = cfgloop.h $(BASIC_BLOCK_H) $(RTL_H)
! CGRAPH_H = cgraph.h tree.h 
  DF_H = df.h bitmap.h sbitmap.h $(BASIC_BLOCK_H)
  DDG_H = ddg.h sbitmap.h $(DF_H)
  GCC_H = gcc.h version.h
--- 701,710 ----
  RESOURCE_H = resource.h hard-reg-set.h
  SCHED_INT_H = sched-int.h $(INSN_ATTR_H) $(BASIC_BLOCK_H) $(RTL_H)
  INTEGRATE_H = integrate.h varray.h
+ IPA_STATIC_H = ipa-static.h bitmap.h $(TREE_H)  
  CFGLAYOUT_H = cfglayout.h $(BASIC_BLOCK_H)
  CFGLOOP_H = cfgloop.h $(BASIC_BLOCK_H) $(RTL_H)
! CGRAPH_H = cgraph.h $(IPA_STATIC_H) 
  DF_H = df.h bitmap.h sbitmap.h $(BASIC_BLOCK_H)
  DDG_H = ddg.h sbitmap.h $(DF_H)
  GCC_H = gcc.h version.h
*************** TREE_DUMP_H = tree-dump.h $(SPLAY_TREE_H
*** 724,730 ****
  TREE_GIMPLE_H = tree-gimple.h tree-iterator.h
  TREE_FLOW_H = tree-flow.h tree-flow-inline.h tree-ssa-operands.h \
  		bitmap.h $(BASIC_BLOCK_H) hard-reg-set.h $(TREE_GIMPLE_H) \
! 		$(HASHTAB_H) $(CGRAPH_H)
  TREE_SSA_LIVE_H = tree-ssa-live.h $(PARTITION_H)
  PRETTY_PRINT_H = pretty-print.h input.h $(OBSTACK_H)
  DIAGNOSTIC_H = diagnostic.h diagnostic.def $(PRETTY_PRINT_H)
--- 725,731 ----
  TREE_GIMPLE_H = tree-gimple.h tree-iterator.h
  TREE_FLOW_H = tree-flow.h tree-flow-inline.h tree-ssa-operands.h \
  		bitmap.h $(BASIC_BLOCK_H) hard-reg-set.h $(TREE_GIMPLE_H) \
! 		$(HASHTAB_H) $(IPA_STATIC_H)
  TREE_SSA_LIVE_H = tree-ssa-live.h $(PARTITION_H)
  PRETTY_PRINT_H = pretty-print.h input.h $(OBSTACK_H)
  DIAGNOSTIC_H = diagnostic.h diagnostic.def $(PRETTY_PRINT_H)
*************** OBJS-common = \
*** 925,935 ****
   varasm.o varray.o vec.o version.o vmsdbgout.o xcoffout.o alloc-pool.o	   \
   et-forest.o cfghooks.o bt-load.o pretty-print.o $(GGC) web.o passes.o	   \
   rtl-profile.o tree-profile.o rtlhooks.o cfgexpand.o lambda-mat.o          \
!  lambda-trans.o	lambda-code.o tree-loop-linear.o
  
  OBJS-md = $(out_object_file)
  OBJS-archive = $(EXTRA_OBJS) $(host_hook_obj) tree-inline.o		   \
!   cgraph.o ipa_prop.o cgraphunit.o tree-nomudflap.o
  
  OBJS = $(OBJS-common) $(out_object_file) $(OBJS-archive)
  
--- 926,936 ----
   varasm.o varray.o vec.o version.o vmsdbgout.o xcoffout.o alloc-pool.o	   \
   et-forest.o cfghooks.o bt-load.o pretty-print.o $(GGC) web.o passes.o	   \
   rtl-profile.o tree-profile.o rtlhooks.o cfgexpand.o lambda-mat.o          \
!  lambda-trans.o	lambda-code.o tree-loop-linear.o tree-promote-statics.o
  
  OBJS-md = $(out_object_file)
  OBJS-archive = $(EXTRA_OBJS) $(host_hook_obj) tree-inline.o		   \
!   cgraph.o ipa_prop.o ipa-static-vars-anal.o cgraphunit.o tree-nomudflap.o
  
  OBJS = $(OBJS-common) $(out_object_file) $(OBJS-archive)
  
*************** tree-dfa.o : tree-dfa.c $(TREE_FLOW_H) $
*** 1688,1696 ****
     $(RTL_H) $(TREE_H) $(TM_P_H) $(EXPR_H) $(GGC_H) output.h diagnostic.h \
     errors.h tree-inline.h $(HASHTAB_H) pointer-set.h $(FLAGS_H) function.h $(TIMEVAR_H) \
     convert.h $(TM_H) coretypes.h langhooks.h \
!    $(TREE_DUMP_H) tree-pass.h params.h $(CGRAPH_H)
  tree-ssa-operands.o : tree-ssa-operands.c $(TREE_FLOW_H) $(CONFIG_H) \
!    $(SYSTEM_H) $(TREE_H) $(TM_P_H) $(GGC_H) $(CGRAPH_H) diagnostic.h errors.h \
     tree-inline.h $(FLAGS_H) function.h $(TM_H) $(TIMEVAR_H) tree-pass.h
  tree-eh.o : tree-eh.c $(TREE_FLOW_H) $(CONFIG_H) $(SYSTEM_H) \
     $(RTL_H) $(TREE_H) $(TM_H) $(FLAGS_H) function.h except.h langhooks.h \
--- 1689,1697 ----
     $(RTL_H) $(TREE_H) $(TM_P_H) $(EXPR_H) $(GGC_H) output.h diagnostic.h \
     errors.h tree-inline.h $(HASHTAB_H) pointer-set.h $(FLAGS_H) function.h $(TIMEVAR_H) \
     convert.h $(TM_H) coretypes.h langhooks.h \
!    $(TREE_DUMP_H) tree-pass.h params.h $(IPA_STATIC_H)
  tree-ssa-operands.o : tree-ssa-operands.c $(TREE_FLOW_H) $(CONFIG_H) \
!    $(SYSTEM_H) $(TREE_H) $(TM_P_H) $(GGC_H) $(IPA_STATIC_H) diagnostic.h errors.h \
     tree-inline.h $(FLAGS_H) function.h $(TM_H) $(TIMEVAR_H) tree-pass.h
  tree-eh.o : tree-eh.c $(TREE_FLOW_H) $(CONFIG_H) $(SYSTEM_H) \
     $(RTL_H) $(TREE_H) $(TM_H) $(FLAGS_H) function.h except.h langhooks.h \
*************** cgraph.o : cgraph.c $(CONFIG_H) $(SYSTEM
*** 1924,1929 ****
--- 1925,1934 ----
  ipa_prop.o : ipa_prop.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(TREE_H) \
     langhooks.h toplev.h $(FLAGS_H) $(GGC_H)  $(TARGET_H) cgraph.h gt-cgraph.h \
     output.h intl.h ipa_prop.h tree.h tree-iterator.h tree-gimple.h tree-inline.h
+ ipa_static-vars-anal.o : ipa-static-vars-anal.c $(CONFIG_H) $(SYSTEM_H) \
+    coretypes.h $(TM_H) $(TREE_H) $(TREE_FLOW_H) tree-inline.h langhooks.h \
+    pointer-set.h $(GGC_H) $(IPA_STATIC_H) $(C_COMMON_H) $(TREE_GIMPLE_H) \
+    $(CGRAPH_H) output.h $(FLAGS_H) tree-pass.h  
  cgraphunit.o : cgraphunit.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(TREE_H) \
     langhooks.h tree-inline.h toplev.h $(FLAGS_H) $(GGC_H)  $(TARGET_H) $(CGRAPH_H) intl.h \
     pointer-set.h function.h $(TREE_GIMPLE_H) $(TREE_FLOW_H) ipa_prop.h
*************** tree-ssa-ccp.o : tree-ssa-ccp.c $(TREE_F
*** 1959,1964 ****
--- 1964,1972 ----
     diagnostic.h errors.h function.h $(TIMEVAR_H) $(TM_H) coretypes.h \
     $(TREE_DUMP_H) $(BASIC_BLOCK_H) tree-pass.h langhooks.h \
     tree-ssa-propagate.h
+ tree-promote-statics.o : tree-promote-statics.c $(CONFIG_H) system.h \
+    $(TREE_H) $(TM_H) $(BASIC_BLOCK_H) $(TREE_FLOW_H) $(IPA_STATIC_H) \
+    bitmap.h tree-pass.h $(FLAGS_H) 
  tree-sra.o : tree-sra.c $(CONFIG_H) system.h errors.h $(TREE_H) $(RTL_H) \
      $(TM_P_H) $(TREE_FLOW_H) diagnostic.h tree-inline.h \
      $(TIMEVAR_H) $(TM_H) coretypes.h $(TREE_DUMP_H) $(TREE_GIMPLE_H) \
*************** insn-preds.o : insn-preds.c $(CONFIG_H) 
*** 2404,2409 ****
--- 2412,2418 ----
  GTFILES = $(srcdir)/input.h $(srcdir)/coretypes.h \
    $(CPP_ID_DATA_H) $(host_xm_file_list) \
    $(tm_file_list) $(HASHTAB_H) $(SPLAY_TREE_H) $(srcdir)/bitmap.h \
+   $(srcdir)/ipa-static.h \
    $(srcdir)/coverage.c $(srcdir)/function.h $(srcdir)/rtl.h \
    $(srcdir)/optabs.h $(srcdir)/tree.h $(srcdir)/libfuncs.h $(SYMTAB_H) \
    $(srcdir)/real.h $(srcdir)/varray.h $(srcdir)/insn-addr.h $(srcdir)/hwint.h \
*************** GTFILES = $(srcdir)/input.h $(srcdir)/co
*** 2429,2434 ****
--- 2438,2444 ----
    $(srcdir)/tree-chrec.h \
    $(srcdir)/tree-ssa-operands.h $(srcdir)/tree-ssa-operands.c \
    $(srcdir)/tree-profile.c $(srcdir)/rtl-profile.c $(srcdir)/tree-nested.c \
+   $(srcdir)/ipa-static-vars-anal.c \
    $(out_file) \
    @all_gtfiles@
  
Index: cgraph.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cgraph.c,v
retrieving revision 1.4.4.18.2.12
diff -c -3 -p -r1.4.4.18.2.12 cgraph.c
*** cgraph.c	26 Oct 2004 14:06:51 -0000	1.4.4.18.2.12
--- cgraph.c	29 Oct 2004 21:40:19 -0000
*************** cgraph_create_node (void)
*** 163,168 ****
--- 163,169 ----
    if (cgraph_nodes)
      cgraph_nodes->previous = node;
    node->previous = NULL;
+   node->static_vars_info = NULL;
    cgraph_nodes = node;
    cgraph_n_nodes++;
    return node;
*************** cgraph_node (tree decl)
*** 184,190 ****
    slot = (struct cgraph_node **) htab_find_slot (cgraph_hash, &key, INSERT);
  
    if (*slot)
!     return *slot;
  
    node = cgraph_create_node ();
    node->decl = decl;
--- 185,196 ----
    slot = (struct cgraph_node **) htab_find_slot (cgraph_hash, &key, INSERT);
  
    if (*slot)
!     {
!       node = *slot;
!       if (!node->master_clone)
! 	node->master_clone = node;
!       return node;
!     }
  
    node = cgraph_create_node ();
    node->decl = decl;
*************** cgraph_node (tree decl)
*** 194,199 ****
--- 200,206 ----
        node->origin = cgraph_node (DECL_CONTEXT (decl));
        node->next_nested = node->origin->nested;
        node->origin->nested = node;
+       node->master_clone = node;
      }
    return node;
  }
*************** cgraph_remove_node (struct cgraph_node *
*** 316,328 ****
      node->previous->next = node->next;
    else
      cgraph_nodes = node->next;
    if (node->next)
      node->next->previous = node->previous;
    slot = htab_find_slot (cgraph_hash, node, NO_INSERT);
    if (*slot == node)
      {
        if (node->next_clone)
! 	*slot = node->next_clone;
        else
  	{
            htab_clear_slot (cgraph_hash, slot);
--- 323,346 ----
      node->previous->next = node->next;
    else
      cgraph_nodes = node->next;
+ 
    if (node->next)
      node->next->previous = node->previous;
    slot = htab_find_slot (cgraph_hash, node, NO_INSERT);
    if (*slot == node)
      {
        if (node->next_clone)
! 	{
! 	  struct cgraph_node *new_node = node->next_clone;
! 	  struct cgraph_node *n;
! 	  *slot = new_node;
! 	  
! 	  /* Make the next clone be the master clone */
! 	  for (n = new_node; n; n = n->next_clone) 
! 	    n->master_clone = new_node;
! 	
! 	  new_node->master_clone = new_node;
! 	}
        else
  	{
            htab_clear_slot (cgraph_hash, slot);
*************** dump_cgraph_node (FILE *f, struct cgraph
*** 477,486 ****
--- 495,516 ----
      fprintf (f, " output");
    if (node->local.local)
      fprintf (f, " local");
+   if (node->local.external)
+     fprintf (f, " external");
+   if (node->local.externally_visible)
+     fprintf (f, " externally_visible");
+   if (node->local.finalized)
+     fprintf (f, " finalized");
+   if (node->local.calls_read_all)
+     fprintf (f, " calls_read_all");
+   if (node->local.calls_write_all)
+     fprintf (f, " calls_write_all");
    if (node->local.disregard_inline_limits)
      fprintf (f, " always_inline");
    else if (node->local.inlinable)
      fprintf (f, " inlinable");
+   if (node->local.redefined_extern_inline)
+     fprintf (f, " redefined_extern_inline");
    if (TREE_ASM_WRITTEN (node->decl))
      fprintf (f, " asm_written");
  
*************** cgraph_clone_node (struct cgraph_node *n
*** 712,720 ****
--- 742,752 ----
        new->origin->nested = new;
      }
    new->analyzed = n->analyzed;
+   new->static_vars_info = n->static_vars_info;
    new->local = n->local;
    new->global = n->global;
    new->rtl = n->rtl;
+   new->master_clone = n->master_clone;
  
    for (e = n->callees;e; e=e->next_callee)
      cgraph_clone_edge (e, new, e->call_expr);
*************** cgraph_clone_node (struct cgraph_node *n
*** 725,730 ****
--- 757,821 ----
    return new;
  }
  
+ /* Return true if N is an master_clone, (see cgraph_master_clone).  */
+ 
+ bool
+ cgraph_is_master_clone (struct cgraph_node *n)
+ {
+   return (n == cgraph_master_clone (n));
+ }
+ 
+ /* Return true if N is an immortal_master_clone, (see
+    cgraph_immortal_master_clone).  */
+ 
+ bool
+ cgraph_is_immortal_master_clone (struct cgraph_node *n)
+ {
+   return (n == cgraph_immortal_master_clone (n));
+ }
+ 
+ /* Return pointer to the callgraph node presenting master clone of N.
+    For functions defined outside the compilation unit, NULL is
+    returned.  For inline functions that have no out of line clone to
+    be output this the choice of which is the master is random and this
+    node will not exist after inlining has happened.  For inline
+    functions that do have an out of line clone to be output, the
+    master clone is a cgraph node that will not be released before any
+    of the clones.  */
+ 
+ struct cgraph_node *
+ cgraph_master_clone (struct cgraph_node *n)
+ {
+   if (n->local.external) 
+     return NULL;
+ 
+   if (!n->master_clone) 
+     n->master_clone = cgraph_node (n->decl);
+   
+   return n->master_clone;
+ }
+ 
+ /* Return pointer to the callgraph node presenting immortal master
+    clone of N, if one exists, NULL otherwise.  The immortal master
+    clone is a cgraph node that will not be released before any of the
+    clones and thus it is usefull for holding information about
+    function body that is the same for all clones.  Inline functions
+    that have no out of line clone to be output have a master node that
+    is not immortal.  For functions defined outside the compilation
+    unit, NULL is returned.  */
+ 
+ struct cgraph_node *
+ cgraph_immortal_master_clone (struct cgraph_node *n)
+ {
+   struct cgraph_node *master = cgraph_master_clone (n);
+   if (!master) 
+     return NULL;
+ 
+   if (master->global.inlined_to)
+     return NULL;
+   return master;
+ }
+ 
  /* NODE is no longer nested function; update cgraph accordingly.  */
  void
  cgraph_unnest_node (struct cgraph_node *node)
Index: cgraph.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cgraph.h,v
retrieving revision 1.1.4.16.2.10
diff -c -3 -p -r1.1.4.16.2.10 cgraph.h
*** cgraph.h	26 Oct 2004 14:06:51 -0000	1.1.4.16.2.10
--- cgraph.h	29 Oct 2004 21:40:19 -0000
*************** Software Foundation, 59 Temple Place - S
*** 21,27 ****
  
  #ifndef GCC_CGRAPH_H
  #define GCC_CGRAPH_H
! #include "tree.h"
  
  /* Information about the function collected locally.
     Available after function is analyzed.  */
--- 21,27 ----
  
  #ifndef GCC_CGRAPH_H
  #define GCC_CGRAPH_H
! #include "ipa-static.h"
  
  /* Information about the function collected locally.
     Available after function is analyzed.  */
*************** struct cgraph_local_info GTY(())
*** 38,43 ****
--- 38,54 ----
    /* Set when function is visible by other units.  */
    bool externally_visible;
  
+   /* Set when this function calls another function external to the
+      compilation unit or if the function has a asm clobber of memory.
+      In general, such calls are modeled as reading and writing all
+      variables (both bits on) but sometime there are attributes on the
+      called function so we can do better.  */
+   bool calls_read_all;
+   bool calls_write_all;
+ 
+   /* Set when function is defined in another compilation unit.  */
+   bool external;
+ 
    /* Set once it has been finalized so we consider it to be output.  */
    bool finalized;
  
*************** struct cgraph_local_info GTY(())
*** 50,59 ****
    /* True when the function has been originally extern inline, but it is
       redefined now.  */
    bool redefined_extern_inline;
- 
-   /* True if statics_read_for_function and
-      statics_written_for_function contain valid data.  */
-   bool for_functions_valid;
  };
  
  /* Information about the function that needs to be computed globally
--- 61,66 ----
*************** struct cgraph_node GTY((chain_next ("%h.
*** 101,112 ****
--- 108,129 ----
    struct cgraph_node *next_needed;
    /* Pointer to the next clone.  */
    struct cgraph_node *next_clone;
+   /* Pointer to next node in a recursive call graph cycle; */
+   struct cgraph_node *next_cycle;
+   /* Pointer to a single unique cgraph node for this function.  If the
+      function is to be output, this is the copy that will survive.  */
+   struct cgraph_node *master_clone;
+ 
    PTR GTY ((skip)) aux;
  
    struct cgraph_local_info local;
    struct cgraph_global_info global;
    struct cgraph_rtl_info rtl;
    
+   /* Pointer to the structure that contains the sets of global
+      variables modified by function calls.  */
+   ipa_static_vars_info_t GTY ((skip)) static_vars_info;
+ 
    /* Unique id of the node.  */
    int uid;
    /* Set when function must be output - it is externally visible
*************** void cgraph_redirect_edge_callee (struct
*** 194,199 ****
--- 211,220 ----
  
  bool cgraph_function_possibly_inlined_p (tree);
  void cgraph_unnest_node (struct cgraph_node *node);
+ bool cgraph_is_master_clone (struct cgraph_node *);
+ bool cgraph_is_immortal_master_clone (struct cgraph_node *);
+ struct cgraph_node *cgraph_master_clone (struct cgraph_node *);
+ struct cgraph_node *cgraph_immortal_master_clone (struct cgraph_node *);
  
  /* In cgraphunit.c  */
  bool cgraph_assemble_pending_functions (void);
Index: cgraphunit.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cgraphunit.c,v
retrieving revision 1.1.4.35.2.22
diff -c -3 -p -r1.1.4.35.2.22 cgraphunit.c
*** cgraphunit.c	26 Oct 2004 14:06:51 -0000	1.1.4.35.2.22
--- cgraphunit.c	29 Oct 2004 21:40:20 -0000
*************** cgraph_analyze_function_inlinability (st
*** 353,358 ****
--- 353,402 ----
    node->global.insns = node->local.self_insns;
  }
  
+ /* As an GCC extension we allow redefinition of the function.  The
+    semantics when both copies of bodies differ is not well defined.
+    We replace the old body with new body so in unit at a time mode
+    we always use new body, while in normal mode we may end up with
+    old body inlined into some functions and new body expanded and
+    inlined in others.
+ 
+    ??? It may make more sense to use one body for inlining and other
+    body for expanding the function but this is difficult to do.  */
+ 
+ static void
+ cgraph_reset_node (struct cgraph_node *node)
+ {
+   /* If node->output is set, then this is a unit-at-a-time compilation
+      and we have already begun whole-unit analysis.  This is *not*
+      testing for whether we've already emitted the function.  That
+      case can be sort-of legitimately seen with real function 
+      redefinition errors.  I would argue that the front end should
+      never present us with such a case, but don't enforce that for now.  */
+   gcc_assert (!node->output);
+ 
+   /* Reset our data structures so we can analyze the function again.  */
+   memset (&node->local, 0, sizeof (node->local));
+   memset (&node->global, 0, sizeof (node->global));
+   memset (&node->rtl, 0, sizeof (node->rtl));
+   node->analyzed = node->local.finalized = false;
+   node->local.redefined_extern_inline = true;
+   while (node->callees)
+     cgraph_remove_edge (node->callees);
+ 
+   /* We may need to re-queue the node for assembling in case
+      we already proceeded it and ignored as not needed.  */
+   if (node->reachable && !flag_unit_at_a_time)
+     {
+       struct cgraph_node *n;
+ 
+       for (n = cgraph_nodes_queue; n; n = n->next_needed)
+ 	if (n == node)
+ 	  break;
+       if (!n)
+ 	node->reachable = 0;
+     }
+ }
+ 
  /* DECL has been parsed.  Take it, queue it, compile it at the whim of the
     logic in effect.  If NESTED is true, then our caller cannot stand to have
     the garbage collector run at the moment.  We would need to either create
*************** cgraph_finalize_function (tree decl, boo
*** 364,410 ****
    struct cgraph_node *node = cgraph_node (decl);
  
    if (node->local.finalized)
!     {
!       /* As an GCC extension we allow redefinition of the function.  The
! 	 semantics when both copies of bodies differ is not well defined.
! 	 We replace the old body with new body so in unit at a time mode
! 	 we always use new body, while in normal mode we may end up with
! 	 old body inlined into some functions and new body expanded and
! 	 inlined in others.
! 	 
! 	 ??? It may make more sense to use one body for inlining and other
! 	 body for expanding the function but this is difficult to do.  */
! 
!       /* If node->output is set, then this is a unit-at-a-time compilation
! 	 and we have already begun whole-unit analysis.  This is *not*
! 	 testing for whether we've already emitted the function.  That
! 	 case can be sort-of legitimately seen with real function 
! 	 redefinition errors.  I would argue that the front end should
! 	 never present us with such a case, but don't enforce that for now.  */
!       gcc_assert (!node->output);
! 
!       /* Reset our data structures so we can analyze the function again.  */
!       memset (&node->local, 0, sizeof (node->local));
!       memset (&node->global, 0, sizeof (node->global));
!       memset (&node->rtl, 0, sizeof (node->rtl));
!       node->analyzed = false;
!       node->local.redefined_extern_inline = true;
!       while (node->callees)
! 	cgraph_remove_edge (node->callees);
! 
!       /* We may need to re-queue the node for assembling in case
!          we already proceeded it and ignored as not needed.  */
!       if (node->reachable && !flag_unit_at_a_time)
! 	{
! 	  struct cgraph_node *n;
! 
! 	  for (n = cgraph_nodes_queue; n; n = n->next_needed)
! 	    if (n == node)
! 	      break;
! 	  if (!n)
! 	    node->reachable = 0;
! 	}
!     }
  
    notice_global_symbol (decl);
    node->decl = decl;
--- 408,414 ----
    struct cgraph_node *node = cgraph_node (decl);
  
    if (node->local.finalized)
!     cgraph_reset_node (node);
  
    notice_global_symbol (decl);
    node->decl = decl;
*************** cgraph_finalize_compilation_unit (void)
*** 798,804 ****
  	 weak alas attribute to kill its body. See
  	 gcc.c-torture/compile/20011119-1.c  */
        if (!DECL_SAVED_TREE (decl))
! 	continue;
  
        gcc_assert (!node->analyzed && node->reachable);
        gcc_assert (DECL_SAVED_TREE (decl));
--- 802,811 ----
  	 weak alas attribute to kill its body. See
  	 gcc.c-torture/compile/20011119-1.c  */
        if (!DECL_SAVED_TREE (decl))
! 	{
! 	  cgraph_reset_node (node);
! 	  continue;
! 	}
  
        gcc_assert (!node->analyzed && node->reachable);
        gcc_assert (DECL_SAVED_TREE (decl));
*************** cgraph_finalize_compilation_unit (void)
*** 831,844 ****
      {
        tree decl = node->decl;
  
!       if (!node->reachable && DECL_SAVED_TREE (decl))
  	{
  	  if (cgraph_dump_file)
  	    fprintf (cgraph_dump_file, " %s", cgraph_node_name (node));
  	  cgraph_remove_node (node);
  	}
        else
! 	node->next_needed = NULL;
      }
    if (cgraph_dump_file)
      {
--- 838,861 ----
      {
        tree decl = node->decl;
  
!       if (node->local.finalized && !DECL_SAVED_TREE (decl))
!         cgraph_reset_node (node);
! 
!       if (!node->reachable && node->local.finalized)
  	{
  	  if (cgraph_dump_file)
  	    fprintf (cgraph_dump_file, " %s", cgraph_node_name (node));
  	  cgraph_remove_node (node);
+ 	  continue;
  	}
        else
! 	{
! 	  node->next_needed = NULL;
! 	  if (!node->local.finalized)
! 	    DECL_SAVED_TREE (decl) = NULL;
! 	}
!       gcc_assert (!node->local.finalized || DECL_SAVED_TREE (decl));
!       gcc_assert (node->analyzed == node->local.finalized);
      }
    if (cgraph_dump_file)
      {
*************** cgraph_function_and_variable_visibility 
*** 1879,1884 ****
--- 1896,1903 ----
        node->local.local = (!node->needed
  			   && node->analyzed
  			   && !TREE_PUBLIC (node->decl));
+       node->local.external = (!DECL_SAVED_TREE (node->decl)
+ 			   && TREE_PUBLIC (node->decl));
      }
    for (vnode = cgraph_varpool_nodes_queue; vnode; vnode = vnode->next_needed)
      {
*************** cgraph_function_and_variable_visibility 
*** 1893,1904 ****
  	}
      }
  
!   /* Because we have to be conservative on the boundaries of source level units,
!      it is possible that we marked some functions in reachable just because they
!      might be used later via external linkage, but after making them local they
!      are really unreachable now.  */
    if (flag_whole_program)
      cgraph_remove_unreachable_nodes (true);
    if (cgraph_dump_file)
      {
        fprintf (cgraph_dump_file, "\nMarking local functions:");
--- 1912,1925 ----
  	}
      }
  
!   /* Because we have to be conservative on the boundaries of source
!      level units, it is possible that we marked some functions in
!      reachable just because they might be used later via external
!      linkage, but after making them local they are really unreachable
!      now.  */
    if (flag_whole_program)
      cgraph_remove_unreachable_nodes (true);
+ 
    if (cgraph_dump_file)
      {
        fprintf (cgraph_dump_file, "\nMarking local functions:");
*************** cgraph_function_and_variable_visibility 
*** 1906,1911 ****
--- 1927,1942 ----
  	if (node->local.local)
  	  fprintf (cgraph_dump_file, " %s", cgraph_node_name (node));
        fprintf (cgraph_dump_file, "\n\n");
+       fprintf (cgraph_dump_file, "\nMarking external functions:");
+       for (node = cgraph_nodes; node; node = node->next)
+ 	if (node->local.external)
+ 	  fprintf (cgraph_dump_file, " %s", cgraph_node_name (node));
+       fprintf (cgraph_dump_file, "\n\n");
+       fprintf (cgraph_dump_file, "\nMarking externally visible functions:");
+       for (node = cgraph_nodes; node; node = node->next)
+ 	if (node->local.externally_visible)
+ 	  fprintf (cgraph_dump_file, " %s", cgraph_node_name (node));
+       fprintf (cgraph_dump_file, "\n\n");
      }
  }
  
Index: regclass.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/regclass.c,v
retrieving revision 1.152.2.19.2.7
diff -c -3 -p -r1.152.2.19.2.7 regclass.c
*** regclass.c	17 Oct 2004 18:30:50 -0000	1.152.2.19.2.7
--- regclass.c	29 Oct 2004 21:40:21 -0000
*************** reg_classes_intersect_p (enum reg_class 
*** 2588,2594 ****
  void
  regset_release_memory (void)
  {
!   bitmap_release_memory ();
  }
  
  #ifdef CANNOT_CHANGE_MODE_CLASS
--- 2588,2594 ----
  void
  regset_release_memory (void)
  {
!   /*  bitmap_release_memory (); */
  }
  
  #ifdef CANNOT_CHANGE_MODE_CLASS
Index: tree-dfa.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/tree-dfa.c,v
retrieving revision 1.1.4.217.2.14
diff -c -3 -p -r1.1.4.217.2.14 tree-dfa.c
*** tree-dfa.c	26 Oct 2004 14:06:51 -0000	1.1.4.217.2.14
--- tree-dfa.c	29 Oct 2004 21:40:21 -0000
*************** Boston, MA 02111-1307, USA.  */
*** 46,52 ****
  #include "tree-pass.h"
  #include "convert.h"
  #include "params.h"
! #include "cgraph.h"
  
  /* Build and maintain data flow information for trees.  */
  
--- 46,52 ----
  #include "tree-pass.h"
  #include "convert.h"
  #include "params.h"
! #include "ipa-static.h"
  
  /* Build and maintain data flow information for trees.  */
  
*************** find_referenced_vars (void)
*** 109,114 ****
--- 109,116 ----
    block_stmt_iterator si;
    struct walk_state walk_state;
  
+   ipa_static_reset_maps ();
+ 
    vars_found = htab_create (50, htab_hash_pointer, htab_eq_pointer, NULL);
    memset (&walk_state, 0, sizeof (walk_state));
    walk_state.vars_found = vars_found;
Index: tree-flow.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/tree-flow.h,v
retrieving revision 1.1.4.187.2.19
diff -c -3 -p -r1.1.4.187.2.19 tree-flow.h
*** tree-flow.h	26 Oct 2004 14:06:52 -0000	1.1.4.187.2.19
--- tree-flow.h	29 Oct 2004 21:40:21 -0000
*************** Boston, MA 02111-1307, USA.  */
*** 29,35 ****
  #include "hashtab.h"
  #include "tree-gimple.h"
  #include "tree-ssa-operands.h"
! #include "cgraph.h"
  
  /* Forward declare structures for the garbage collector GTY markers.  */
  #ifndef GCC_BASIC_BLOCK_H
--- 29,35 ----
  #include "hashtab.h"
  #include "tree-gimple.h"
  #include "tree-ssa-operands.h"
! #include "ipa-static.h"
  
  /* Forward declare structures for the garbage collector GTY markers.  */
  #ifndef GCC_BASIC_BLOCK_H
*************** struct var_ann_d GTY(())
*** 201,206 ****
--- 201,211 ----
       live at the same time and this can happen for each call to the
       dominator optimizer.  */
    tree current_def;
+ 
+   /* Pointer to the structure that contains the sets of global
+      variables modified by function calls.  This field is only used
+      for FUNCTION_DECLs.  */
+   ipa_static_vars_info_t GTY ((skip)) static_vars_info;
  };
  
  
*************** extern void bsi_insert_after (block_stmt
*** 448,453 ****
--- 453,464 ----
  
  extern void bsi_replace (const block_stmt_iterator *, tree, bool);
  
+ /* In tree-sra.c  */
+ void sra_insert_before (block_stmt_iterator *, tree);
+ void sra_insert_after (block_stmt_iterator *, tree);
+ void sra_init_cache (void);
+ bool sra_type_can_be_decomposed_p (tree);
+ 
  /*---------------------------------------------------------------------------
  			      Function prototypes
  ---------------------------------------------------------------------------*/
Index: tree-gimple.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/tree-gimple.c,v
retrieving revision 2.1.2.8
diff -c -3 -p -r2.1.2.8 tree-gimple.c
*** tree-gimple.c	17 Oct 2004 18:31:00 -0000	2.1.2.8
--- tree-gimple.c	29 Oct 2004 21:40:21 -0000
*************** get_call_expr_in (tree t)
*** 429,434 ****
--- 429,454 ----
    return NULL_TREE;
  }
  
+ /* Given a memory reference T, will return the variable at the bottom
+    of the access.  Unlike get_base_address below, this will recurse
+    thru INDIRECT_REFS.  */
+ 
+ tree
+ get_base_var (tree t)
+ {
+   if ((TREE_CODE (t) == EXC_PTR_EXPR) || (TREE_CODE (t) == FILTER_EXPR))
+     return t;
+ 
+   while (!SSA_VAR_P (t) 
+ 	 && (!CONSTANT_CLASS_P (t))
+ 	 && TREE_CODE (t) != LABEL_DECL
+ 	 && TREE_CODE (t) != FUNCTION_DECL)
+     {
+       t = TREE_OPERAND (t, 0);
+     }
+   return t;
+ } 
+ 
  /* Given a memory reference expression, return the base address.  Note that,
     in contrast with get_base_var, this will not recurse inside INDIRECT_REF
     expressions.  Therefore, given the reference PTR->FIELD, this function
Index: tree-optimize.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/tree-optimize.c,v
retrieving revision 1.1.4.122.2.23
diff -c -3 -p -r1.1.4.122.2.23 tree-optimize.c
*** tree-optimize.c	25 Oct 2004 09:15:36 -0000	1.1.4.122.2.23
--- tree-optimize.c	29 Oct 2004 21:40:22 -0000
*************** init_tree_optimization_passes (void)
*** 392,397 ****
--- 392,398 ----
  
    p = &pass_all_optimizations.sub;
    NEXT_PASS (pass_referenced_vars);
+   NEXT_PASS (pass_promote_statics);
    NEXT_PASS (pass_build_ssa);
    NEXT_PASS (pass_may_alias);
    NEXT_PASS (pass_rename_ssa_copies);
*************** init_tree_optimization_passes (void)
*** 447,454 ****
--- 448,458 ----
    NEXT_PASS (pass_complete_unroll);
    NEXT_PASS (pass_iv_optimize);
    NEXT_PASS (pass_loop_done);
+   *p = NULL;
+ 
    p = &all_ipa_passes;
    NEXT_PASS (pass_ipa_inline);
+   NEXT_PASS (pass_ipa_static);
    *p = NULL;
  
  #undef NEXT_PASS
Index: tree-pass.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/tree-pass.h,v
retrieving revision 1.1.2.12.2.9
diff -c -3 -p -r1.1.2.12.2.9 tree-pass.h
*** tree-pass.h	25 Oct 2004 09:15:36 -0000	1.1.2.12.2.9
--- tree-pass.h	29 Oct 2004 21:40:22 -0000
*************** extern struct tree_opt_pass pass_lower_c
*** 145,152 ****
  extern struct tree_opt_pass pass_lower_eh;
  extern struct tree_opt_pass pass_build_cfg;
  extern struct tree_opt_pass pass_tree_profile;
! extern struct tree_opt_pass pass_referenced_vars;
  extern struct tree_opt_pass pass_sra;
  extern struct tree_opt_pass pass_tail_recursion;
  extern struct tree_opt_pass pass_tail_calls;
  extern struct tree_opt_pass pass_loop;
--- 145,153 ----
  extern struct tree_opt_pass pass_lower_eh;
  extern struct tree_opt_pass pass_build_cfg;
  extern struct tree_opt_pass pass_tree_profile;
! extern struct tree_opt_pass pass_promote_statics;
  extern struct tree_opt_pass pass_sra;
+ extern struct tree_opt_pass pass_referenced_vars;
  extern struct tree_opt_pass pass_tail_recursion;
  extern struct tree_opt_pass pass_tail_calls;
  extern struct tree_opt_pass pass_loop;
*************** extern struct tree_opt_pass pass_fre;
*** 190,194 ****
--- 191,196 ----
  extern struct tree_opt_pass pass_linear_transform;
  
  extern struct tree_opt_pass pass_ipa_inline;
+ extern struct tree_opt_pass pass_ipa_static;
  
  #endif /* GCC_TREE_PASS_H */
Index: tree-sra.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/tree-sra.c,v
retrieving revision 1.1.2.20.2.12
diff -c -3 -p -r1.1.2.20.2.12 tree-sra.c
*** tree-sra.c	25 Oct 2004 09:15:37 -0000	1.1.2.20.2.12
--- tree-sra.c	29 Oct 2004 21:40:22 -0000
*************** is_sra_scalar_type (tree type)
*** 171,178 ****
     instantiated, just that if we decide to break up the type into
     separate pieces that it can be done.  */
  
! static bool
! type_can_be_decomposed_p (tree type)
  {
    unsigned int cache = TYPE_UID (TYPE_MAIN_VARIANT (type)) * 2;
    tree t;
--- 171,178 ----
     instantiated, just that if we decide to break up the type into
     separate pieces that it can be done.  */
  
! bool
! sra_type_can_be_decomposed_p (tree type)
  {
    unsigned int cache = TYPE_UID (TYPE_MAIN_VARIANT (type)) * 2;
    tree t;
*************** decl_can_be_decomposed_p (tree var)
*** 273,279 ****
      }
  
    /* We must be able to decompose the variable's type.  */
!   if (!type_can_be_decomposed_p (TREE_TYPE (var)))
      {
        if (dump_file && (dump_flags & TDF_DETAILS))
  	{
--- 273,279 ----
      }
  
    /* We must be able to decompose the variable's type.  */
!   if (!sra_type_can_be_decomposed_p (TREE_TYPE (var)))
      {
        if (dump_file && (dump_flags & TDF_DETAILS))
  	{
*************** type_can_instantiate_all_elements (tree 
*** 294,300 ****
  {
    if (is_sra_scalar_type (type))
      return true;
!   if (!type_can_be_decomposed_p (type))
      return false;
  
    switch (TREE_CODE (type))
--- 294,300 ----
  {
    if (is_sra_scalar_type (type))
      return true;
!   if (!sra_type_can_be_decomposed_p (type))
      return false;
  
    switch (TREE_CODE (type))
*************** insert_edge_copies (tree stmt, basic_blo
*** 1665,1671 ****
  
  /* Helper function to insert LIST before BSI, and set up line number info.  */
  
! static void
  sra_insert_before (block_stmt_iterator *bsi, tree list)
  {
    tree stmt = bsi_stmt (*bsi);
--- 1665,1671 ----
  
  /* Helper function to insert LIST before BSI, and set up line number info.  */
  
! void
  sra_insert_before (block_stmt_iterator *bsi, tree list)
  {
    tree stmt = bsi_stmt (*bsi);
*************** sra_insert_before (block_stmt_iterator *
*** 1677,1683 ****
  
  /* Similarly, but insert after BSI.  Handles insertion onto edges as well.  */
  
! static void
  sra_insert_after (block_stmt_iterator *bsi, tree list)
  {
    tree stmt = bsi_stmt (*bsi);
--- 1677,1683 ----
  
  /* Similarly, but insert after BSI.  Handles insertion onto edges as well.  */
  
! void
  sra_insert_after (block_stmt_iterator *bsi, tree list)
  {
    tree stmt = bsi_stmt (*bsi);
*************** debug_sra_elt_name (struct sra_elt *elt)
*** 2025,2030 ****
--- 2025,2040 ----
    fputc ('\n', stderr);
  }
  
+ void 
+ sra_init_cache (void)
+ {
+   if (sra_type_decomp_cache) 
+     return;
+ 
+   sra_type_decomp_cache = BITMAP_XMALLOC ();
+   sra_type_inst_cache = BITMAP_XMALLOC ();
+ }
+ 
  /* Main entry point.  */
  
  static void
*************** tree_sra (void)
*** 2034,2041 ****
    gcc_obstack_init (&sra_obstack);
    sra_candidates = BITMAP_XMALLOC ();
    needs_copy_in = BITMAP_XMALLOC ();
!   sra_type_decomp_cache = BITMAP_XMALLOC ();
!   sra_type_inst_cache = BITMAP_XMALLOC ();
    sra_map = htab_create (101, sra_elt_hash, sra_elt_eq, NULL);
  
    /* Scan.  If we find anything, instantiate and scalarize.  */
--- 2044,2050 ----
    gcc_obstack_init (&sra_obstack);
    sra_candidates = BITMAP_XMALLOC ();
    needs_copy_in = BITMAP_XMALLOC ();
!   sra_init_cache ();
    sra_map = htab_create (101, sra_elt_hash, sra_elt_eq, NULL);
  
    /* Scan.  If we find anything, instantiate and scalarize.  */
Index: tree-ssa-operands.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/tree-ssa-operands.c,v
retrieving revision 1.1.2.9.2.15
diff -c -3 -p -r1.1.2.9.2.15 tree-ssa-operands.c
*** tree-ssa-operands.c	26 Oct 2004 14:06:52 -0000	1.1.2.9.2.15
--- tree-ssa-operands.c	29 Oct 2004 21:40:23 -0000
*************** Boston, MA 02111-1307, USA.  */
*** 32,37 ****
--- 32,38 ----
  #include "tree-pass.h"
  #include "ggc.h"
  #include "timevar.h"
+ #include "ipa-static.h"
  
  #include "langhooks.h"
  
*************** static inline void append_def (tree *);
*** 134,141 ****
  static inline void append_use (tree *);
  static void append_v_may_def (tree);
  static void append_v_must_def (tree);
! static void add_call_clobber_ops (tree);
! static void add_call_read_ops (tree);
  static void add_stmt_operand (tree *, tree, int);
  
  /* Return a vector of contiguous memory for NUM def operands.  */
--- 135,142 ----
  static inline void append_use (tree *);
  static void append_v_may_def (tree);
  static void append_v_must_def (tree);
! static void add_call_clobber_ops (tree, tree);
! static void add_call_read_ops (tree, tree);
  static void add_stmt_operand (tree *, tree, int);
  
  /* Return a vector of contiguous memory for NUM def operands.  */
*************** get_call_expr_operands (tree stmt, tree 
*** 1398,1403 ****
--- 1399,1405 ----
  {
    tree op;
    int call_flags = call_expr_flags (expr);
+   tree callee = get_callee_fndecl (expr);
  
    /* Find uses in the called function.  */
    get_expr_operands (stmt, &TREE_OPERAND (expr, 0), opf_none);
*************** get_call_expr_operands (tree stmt, tree 
*** 1414,1422 ****
  	 there is no point in recording that.  */ 
        if (TREE_SIDE_EFFECTS (expr)
  	  && !(call_flags & (ECF_PURE | ECF_CONST | ECF_NORETURN)))
! 	add_call_clobber_ops (stmt);
        else if (!(call_flags & ECF_CONST))
! 	add_call_read_ops (stmt);
      }
  }
  
--- 1416,1424 ----
  	 there is no point in recording that.  */ 
        if (TREE_SIDE_EFFECTS (expr)
  	  && !(call_flags & (ECF_PURE | ECF_CONST | ECF_NORETURN)))
! 	add_call_clobber_ops (stmt, callee);
        else if (!(call_flags & ECF_CONST))
! 	add_call_read_ops (stmt, callee);
      }
  }
  
*************** note_addressable (tree var, stmt_ann_t s
*** 1580,1586 ****
     clobbered variables in the function.  */
  
  static void
! add_call_clobber_ops (tree stmt)
  {
    /* Functions that are not const, pure or never return may clobber
       call-clobbered variables.  */
--- 1582,1588 ----
     clobbered variables in the function.  */
  
  static void
! add_call_clobber_ops (tree stmt, tree callee)
  {
    /* Functions that are not const, pure or never return may clobber
       call-clobbered variables.  */
*************** add_call_clobber_ops (tree stmt)
*** 1598,1611 ****
        size_t i;
        bitmap_iterator bi;
  
        EXECUTE_IF_SET_IN_BITMAP (call_clobbered_vars, 0, i, bi)
  	{
  	  tree var = referenced_var (i);
! 	  if (TREE_READONLY (var)
! 	      && (TREE_STATIC (var) || DECL_EXTERNAL (var)))
! 	    add_stmt_operand (&var, stmt, opf_none);
  	  else
! 	    add_stmt_operand (&var, stmt, opf_is_def);
  	}
      }
  }
--- 1600,1653 ----
        size_t i;
        bitmap_iterator bi;
  
+       /* Get info for local and module level statics.  There is a bit
+ 	 set for each static if the call being processed does not read
+ 	 or write that variable.  */
+ 
+       bitmap not_read_b = callee 
+ 	? ipa_get_statics_not_read_global (callee) : NULL; 
+       bitmap not_written_b = callee 
+ 	? ipa_get_statics_not_written_global (callee) : NULL; 
+ 
        EXECUTE_IF_SET_IN_BITMAP (call_clobbered_vars, 0, i, bi)
  	{
  	  tree var = referenced_var (i);
! 
! 	  bool not_read
! 	    = not_read_b ? bitmap_bit_p (not_read_b, i) : false;
! 	  bool not_written
! 	    = not_written_b ? bitmap_bit_p (not_written_b, i) : false;
! 
! 	  if (not_read)
! 	    {
! 	      /* The var is not read during the call.  */
! 	      if (!not_written)
! 		add_stmt_operand (&var, stmt, opf_is_def);
! 	    }
  	  else
! 	    {
! 	      /* The var is read during the call.  */
! 	      if (not_written) 
! 		add_stmt_operand (&var, stmt, opf_none);
! 
! 	      /* The not_read and not_written bits are only set for module
! 		 static variables.  Neither is set here, so we may be dealing
! 		 with a module static or we may not.  So we still must look
! 		 anywhere else we can (such as the TREE_READONLY) to get
! 		 better info.  */
! 
! 	      /* If VAR is read-only, don't add a V_MAY_DEF, just a
! 		 VUSE operand.  FIXME, this is quirky.  TREE_READONLY
! 		 by itself is not enough here.  We can only decide
! 		 that the call will not affect VAR if all these
! 		 conditions are met.  One would think that
! 		 TREE_READONLY should be sufficient.  */
! 	      else if (TREE_READONLY (var)
! 		       && (TREE_STATIC (var) || DECL_EXTERNAL (var)))
! 		add_stmt_operand (&var, stmt, opf_none);
! 	      else
! 		add_stmt_operand (&var, stmt, opf_is_def);
! 	    }
  	}
      }
  }
*************** add_call_clobber_ops (tree stmt)
*** 1615,1621 ****
     function.  */
  
  static void
! add_call_read_ops (tree stmt)
  {
    bitmap_iterator bi;
  
--- 1657,1663 ----
     function.  */
  
  static void
! add_call_read_ops (tree stmt, tree callee)
  {
    bitmap_iterator bi;
  
*************** add_call_read_ops (tree stmt)
*** 1628,1638 ****
    else
      {
        size_t i;
        
        EXECUTE_IF_SET_IN_BITMAP (call_clobbered_vars, 0, i, bi)
  	{
  	  tree var = referenced_var (i);
! 	  add_stmt_operand (&var, stmt, opf_none);
  	}
      }
  }
--- 1670,1685 ----
    else
      {
        size_t i;
+       bitmap not_read_b = callee 
+ 	? ipa_get_statics_not_read_global (callee) : NULL; 
        
        EXECUTE_IF_SET_IN_BITMAP (call_clobbered_vars, 0, i, bi)
  	{
  	  tree var = referenced_var (i);
! 	  bool not_read = not_read_b 
! 	    ? bitmap_bit_p(not_read_b, i) : false;
! 	  if (!not_read)
! 	    add_stmt_operand (&var, stmt, opf_none);
  	}
      }
  }
Index: tree.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/tree.h,v
retrieving revision 1.342.2.169.2.16
diff -c -3 -p -r1.342.2.169.2.16 tree.h
*** tree.h	17 Oct 2004 18:31:08 -0000	1.342.2.169.2.16
--- tree.h	29 Oct 2004 21:40:24 -0000
*************** tree upper_bound_in_type (tree, tree);
*** 3938,3943 ****
--- 3938,3944 ----
  extern bool thread_through_all_blocks (void);
  
  /* In tree-gimple.c.  */
+ extern tree get_base_var (tree t);
  extern tree get_base_address (tree t);
  
  #endif  /* GCC_TREE_H  */

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