Symbol table 14/many: cleanups of cgraphunit.c

Jan Hubicka hubicka@ucw.cz
Wed Apr 25 16:32:00 GMT 2012


Hi,
this patch updates toplevel comment of cgraphunit.c and cleans it up somewhat.
It also moves cgraph verification bits to cgraph.c where it belongs more and
makes static some functions that are no longer needed to be exported.

Honza

	* cgraphunit.c: Update toplevel comment.
	(tree_rest_of_compilation): Merge into cgraph_expand_function.
	(cgraph_analyze_function): Make static.
	(cgraph_decide_is_function_needed): Make static.
	(cgraph_add_new_function): Use expand_function instead of
	rest_of_compilation.
	(clone_of_p, verify_edge_count_and_frequency, cgraph_debug_gimple_stmt,
	verify_edge_corresponds_to_fndecl, verify_cgraph_node, verify_cgraph):
	Move to cgraph.c
	(cgraph_inline_p): Remove.
	(cgraph_preserve_function_body_p): Move to ipa-inline-transform.
	(init_cgraph): Add comment.
	* cgraphbuild.c (record_reference, mark_address, mark_load,
	mark_store): Do not call analyze_expr hook.
	* cgraph.c: Update toplevel comment.
	(clone_of_p, verify_edge_count_and_frequency, cgraph_debug_gimple_stmt,
	verify_edge_corresponds_to_fndecl, verify_cgraph_node, verify_cgraph):
	Move fere from cgraphunit.c
	(cgraph_mark_force_output_node): Move to cgraph.h
	* cgraph.h: Reorder so the comments match the function placement.
	(cgraph_analyze_function, cgraph_decide_is_function_needed): Remove.
	(cgraph_mark_force_output_node): Move here from cgraph.c
	* tree.c (free_lang_data): Do not clear analyze_expr hook.
	* ipa-inline-transform.c (preserve_function_body_p): New function.
	(inline_transform): Update.
	* langhooks.c (lhd_callgraph_analyze_expr): Remove.
	* langhooks.h (lang_hooks_for_callgraph): Remove.
	(lang_hooks): Remove callgraph.
	* tree-inline.c (expand_call_inline): Do not use cgraph_inline_p.
	* varpool.c: Remove out of date comment.
	* langhooks-def.h (lhd_callgraph_analyze_expr): Remove.
	(LANG_HOOKS_CALLGRAPH_ANALYZE_EXPR): Remove.
Index: cgraphunit.c
===================================================================
*** cgraphunit.c	(revision 186713)
--- cgraphunit.c	(working copy)
***************
*** 1,4 ****
! /* Callgraph based interprocedural optimizations.
     Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
     2011, 2012 Free Software Foundation, Inc.
     Contributed by Jan Hubicka
--- 1,4 ----
! /* Driver of optimization process
     Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
     2011, 2012 Free Software Foundation, Inc.
     Contributed by Jan Hubicka
*************** You should have received a copy of the G
*** 19,29 ****
  along with GCC; see the file COPYING3.  If not see
  <http://www.gnu.org/licenses/>.  */
  
! /* This module implements main driver of compilation process as well as
!    few basic interprocedural optimizers.
  
     The main scope of this file is to act as an interface in between
!    tree based frontends and the backend (and middle end)
  
     The front-end is supposed to use following functionality:
  
--- 19,28 ----
  along with GCC; see the file COPYING3.  If not see
  <http://www.gnu.org/licenses/>.  */
  
! /* This module implements main driver of compilation process.
  
     The main scope of this file is to act as an interface in between
!    tree based frontends and the backend.
  
     The front-end is supposed to use following functionality:
  
*************** along with GCC; see the file COPYING3.
*** 45,110 ****
        This function is called once (source level) compilation unit is finalized
        and it will no longer change.
  
!       In the call-graph construction and local function analysis takes
!       place here.  Bodies of unreachable functions are released to
!       conserve memory usage.
  
        The function can be called multiple times when multiple source level
!       compilation units are combined (such as in C frontend)
  
      - cgraph_optimize
  
!       In this unit-at-a-time compilation the intra procedural analysis takes
!       place here.  In particular the static functions whose address is never
!       taken are marked as local.  Backend can then use this information to
!       modify calling conventions, do better inlining or similar optimizations.
! 
!     - cgraph_mark_needed_node
!     - varpool_mark_needed_node
! 
!       When function or variable is referenced by some hidden way the call-graph
!       data structure must be updated accordingly by this function.
!       There should be little need to call this function and all the references
!       should be made explicit to cgraph code.  At present these functions are
!       used by C++ frontend to explicitly mark the keyed methods.
! 
!     - analyze_expr callback
! 
!       This function is responsible for lowering tree nodes not understood by
!       generic code into understandable ones or alternatively marking
!       callgraph and varpool nodes referenced by the as needed.
! 
!       ??? On the tree-ssa genericizing should take place here and we will avoid
!       need for these hooks (replacing them by genericizing hook)
! 
!         Analyzing of all functions is deferred
! 	to cgraph_finalize_compilation_unit and expansion into cgraph_optimize.
! 
! 	In cgraph_finalize_compilation_unit the reachable functions are
! 	analyzed.  During analysis the call-graph edges from reachable
! 	functions are constructed and their destinations are marked as
! 	reachable.  References to functions and variables are discovered too
! 	and variables found to be needed output to the assembly file.  Via
! 	mark_referenced call in assemble_variable functions referenced by
! 	static variables are noticed too.
! 
! 	The intra-procedural information is produced and its existence
! 	indicated by global_info_ready.  Once this flag is set it is impossible
! 	to change function from !reachable to reachable and thus
! 	assemble_variable no longer call mark_referenced.
! 
! 	Finally the call-graph is topologically sorted and all reachable functions
! 	that has not been completely inlined or are not external are output.
! 
! 	??? It is possible that reference to function or variable is optimized
! 	out.  We can not deal with this nicely because topological order is not
! 	suitable for it.  For tree-ssa we may consider another pass doing
! 	optimization and re-discovering reachable functions.
! 
! 	??? Reorganize code so variables are output very last and only if they
! 	really has been referenced by produced code, so we catch more cases
! 	where reference has been optimized out.  */
  
  
  #include "config.h"
  #include "system.h"
--- 44,158 ----
        This function is called once (source level) compilation unit is finalized
        and it will no longer change.
  
!       The symbol table is constructed starting from the trivially needed
!       symbols finalized by the frontend.  Functions are lowered into
!       GIMPLE representation and callgraph/reference lists are constructed.
!       Those are used to discover other neccesary functions and variables.
! 
!       At the end the bodies of unreachable functions are removed.
  
        The function can be called multiple times when multiple source level
!       compilation units are combined.
  
      - cgraph_optimize
  
!       This passes control to the back-end.  Optimizations are performed and
!       final assembler is generated.  This is done in the following way. Note
!       that with link time optimization the process is split into three
!       stages (compile time, linktime analysis and parallel linktime as
!       indicated bellow).
! 
!       Compile time:
! 
! 	1) Inter-procedural optimization.
! 	   (ipa_passes)
! 
! 	   This part is further split into:
! 
! 	   a) early optimizations. These are local passes executed in
! 	      the topological order on the callgraph.
! 
! 	      The purpose of early optimiations is to optimize away simple
! 	      things that may otherwise confuse IP analysis. Very simple
! 	      propagation across the callgraph is done i.e. to discover
! 	      functions without side effects and simple inlining is performed.
! 
! 	   b) early small interprocedural passes.
! 
! 	      Those are interprocedural passes executed only at compilation
! 	      time.  These include, for exmaple, transational memory lowering,
! 	      unreachable code removal and other simple transformations.
! 
! 	   c) IP analysis stage.  All interprocedural passes do their
! 	      analysis.
! 
! 	      Interprocedural passes differ from small interprocedural
! 	      passes by their ability to operate across whole program
! 	      at linktime.  Their analysis stage is performed early to
! 	      both reduce linking times and linktime memory usage by	
! 	      not having to represent whole program in memory.
! 
! 	   d) LTO sreaming.  When doing LTO, everything important gets
! 	      streamed into the object file.
! 
!        Compile time and or linktime analysis stage (WPA):
! 
! 	      At linktime units gets streamed back and symbol table is
! 	      merged.  Function bodies are not streamed in and not
! 	      available.
! 	   e) IP propagation stage.  All IP passes execute their
! 	      IP propagation. This is done based on the earlier analysis
! 	      without having function bodies at hand.
! 	   f) Ltrans streaming.  When doing WHOPR LTO, the program
! 	      is partitioned and streamed into multple object files.
! 
!        Compile time and/or parallel linktime stage (ltrans)
  
+ 	      Each of the object files is streamed back and compiled
+ 	      separately.  Now the function bodies becomes available
+ 	      again.
+ 
+ 	 2) Virtual clone materialization
+ 	    (cgraph_materialize_clone)
+ 
+ 	    IP passes can produce copies of existing functoins (such
+ 	    as versioned clones or inline clones) without actually
+ 	    manipulating their bodies by creating virtual clones in
+ 	    the callgraph. At this time the virtual clones are
+ 	    turned into real functions
+ 	 3) IP transformation
+ 
+ 	    All IP passes transform function bodies based on earlier
+ 	    decision of the IP propagation.
+ 
+ 	 4) late small IP passes
+ 
+ 	    Simple IP passes working within single program partition.
+ 
+ 	 5) Expansion
+ 	    (cgraph_expand_all_functions)
+ 
+ 	    At this stage functions that needs to be output into
+ 	    assembler are identified and compiled in topological order
+ 	 6) Output of variables and aliases
+ 	    Now it is known what variable references was not optimized
+ 	    out and thus all variables are output to the file.
+ 
+ 	    Note that with -fno-toplevel-reorder passes 5 and 6
+ 	    are combined together in cgraph_output_in_order.  
+ 
+    Finally there are functions to manipulate the callgraph from
+    backend.
+     - cgraph_add_new_function is used to add backend produced
+       functions introduced after the unit is finalized.
+       The functions are enqueue for later processing and inserted
+       into callgraph with cgraph_process_new_functions.
+ 
+     - cgraph_function_versioning
+ 
+       produces a copy of function into new one (a version)
+       and apply simple transformations
+ */
  
  #include "config.h"
  #include "system.h"
*************** static void cgraph_expand_all_functions
*** 154,160 ****
  static void cgraph_mark_functions_to_output (void);
  static void cgraph_expand_function (struct cgraph_node *);
  static void cgraph_output_pending_asms (void);
! static void tree_rest_of_compilation (struct cgraph_node *);
  
  FILE *cgraph_dump_file;
  
--- 202,208 ----
  static void cgraph_mark_functions_to_output (void);
  static void cgraph_expand_function (struct cgraph_node *);
  static void cgraph_output_pending_asms (void);
! static void cgraph_analyze_function (struct cgraph_node *);
  
  FILE *cgraph_dump_file;
  
*************** static GTY (()) tree vtable_entry_type;
*** 166,172 ****
     and differs from later logic removing unnecesary functions that can
     take into account results of analysis, whole program info etc.  */
  
! bool
  cgraph_decide_is_function_needed (struct cgraph_node *node, tree decl)
  {
    /* If the user told us it is used, then it must be so.  */
--- 214,220 ----
     and differs from later logic removing unnecesary functions that can
     take into account results of analysis, whole program info etc.  */
  
! static bool
  cgraph_decide_is_function_needed (struct cgraph_node *node, tree decl)
  {
    /* If the user told us it is used, then it must be so.  */
*************** cgraph_add_new_function (tree fndecl, bo
*** 449,456 ****
  	if (!gimple_in_ssa_p (DECL_STRUCT_FUNCTION (fndecl)))
  	  execute_pass_list (pass_early_local_passes.pass.sub);
  	bitmap_obstack_release (NULL);
- 	tree_rest_of_compilation (node);
  	pop_cfun ();
  	current_function_decl = NULL;
  	break;
  
--- 497,504 ----
  	if (!gimple_in_ssa_p (DECL_STRUCT_FUNCTION (fndecl)))
  	  execute_pass_list (pass_early_local_passes.pass.sub);
  	bitmap_obstack_release (NULL);
  	pop_cfun ();
+ 	cgraph_expand_function (node);
  	current_function_decl = NULL;
  	break;
  
*************** cgraph_add_new_function (tree fndecl, bo
*** 465,868 ****
      DECL_FUNCTION_PERSONALITY (fndecl) = lang_hooks.eh_personality ();
  }
  
- /* Return TRUE if NODE2 is equivalent to NODE or its clone.  */
- static bool
- clone_of_p (struct cgraph_node *node, struct cgraph_node *node2)
- {
-   node = cgraph_function_or_thunk_node (node, NULL);
-   node2 = cgraph_function_or_thunk_node (node2, NULL);
-   while (node != node2 && node2)
-     node2 = node2->clone_of;
-   return node2 != NULL;
- }
- 
- /* Verify edge E count and frequency.  */
- 
- static bool
- verify_edge_count_and_frequency (struct cgraph_edge *e)
- {
-   bool error_found = false;
-   if (e->count < 0)
-     {
-       error ("caller edge count is negative");
-       error_found = true;
-     }
-   if (e->frequency < 0)
-     {
-       error ("caller edge frequency is negative");
-       error_found = true;
-     }
-   if (e->frequency > CGRAPH_FREQ_MAX)
-     {
-       error ("caller edge frequency is too large");
-       error_found = true;
-     }
-   if (gimple_has_body_p (e->caller->symbol.decl)
-       && !e->caller->global.inlined_to
-       /* FIXME: Inline-analysis sets frequency to 0 when edge is optimized out.
- 	 Remove this once edges are actualy removed from the function at that time.  */
-       && (e->frequency
- 	  || (inline_edge_summary_vec
- 	      && ((VEC_length(inline_edge_summary_t, inline_edge_summary_vec)
- 		  <= (unsigned) e->uid)
- 	          || !inline_edge_summary (e)->predicate)))
-       && (e->frequency
- 	  != compute_call_stmt_bb_frequency (e->caller->symbol.decl,
- 					     gimple_bb (e->call_stmt))))
-     {
-       error ("caller edge frequency %i does not match BB frequency %i",
- 	     e->frequency,
- 	     compute_call_stmt_bb_frequency (e->caller->symbol.decl,
- 					     gimple_bb (e->call_stmt)));
-       error_found = true;
-     }
-   return error_found;
- }
- 
- /* Switch to THIS_CFUN if needed and print STMT to stderr.  */
- static void
- cgraph_debug_gimple_stmt (struct function *this_cfun, gimple stmt)
- {
-   /* debug_gimple_stmt needs correct cfun */
-   if (cfun != this_cfun)
-     set_cfun (this_cfun);
-   debug_gimple_stmt (stmt);
- }
- 
- /* Verify that call graph edge E corresponds to DECL from the associated
-    statement.  Return true if the verification should fail.  */
- 
- static bool
- verify_edge_corresponds_to_fndecl (struct cgraph_edge *e, tree decl)
- {
-   struct cgraph_node *node;
- 
-   if (!decl || e->callee->global.inlined_to)
-     return false;
-   node = cgraph_get_node (decl);
- 
-   /* We do not know if a node from a different partition is an alias or what it
-      aliases and therefore cannot do the former_clone_of check reliably.  */
-   if (!node || node->symbol.in_other_partition)
-     return false;
-   node = cgraph_function_or_thunk_node (node, NULL);
- 
-   if ((e->callee->former_clone_of != node->symbol.decl
-        && (!node->same_body_alias
- 	   || e->callee->former_clone_of != node->thunk.alias))
-       /* IPA-CP sometimes redirect edge to clone and then back to the former
- 	 function.  This ping-pong has to go, eventually.  */
-       && (node != cgraph_function_or_thunk_node (e->callee, NULL))
-       && !clone_of_p (node, e->callee)
-       /* If decl is a same body alias of some other decl, allow e->callee to be
- 	 a clone of a clone of that other decl too.  */
-       && (!node->same_body_alias
- 	  || !clone_of_p (cgraph_get_node (node->thunk.alias), e->callee)))
-     return true;
-   else
-     return false;
- }
- 
- /* Verify cgraph nodes of given cgraph node.  */
- DEBUG_FUNCTION void
- verify_cgraph_node (struct cgraph_node *node)
- {
-   struct cgraph_edge *e;
-   struct function *this_cfun = DECL_STRUCT_FUNCTION (node->symbol.decl);
-   basic_block this_block;
-   gimple_stmt_iterator gsi;
-   bool error_found = false;
- 
-   if (seen_error ())
-     return;
- 
-   timevar_push (TV_CGRAPH_VERIFY);
-   error_found |= verify_symtab_base ((symtab_node) node);
-   for (e = node->callees; e; e = e->next_callee)
-     if (e->aux)
-       {
- 	error ("aux field set for edge %s->%s",
- 	       identifier_to_locale (cgraph_node_name (e->caller)),
- 	       identifier_to_locale (cgraph_node_name (e->callee)));
- 	error_found = true;
-       }
-   if (node->count < 0)
-     {
-       error ("execution count is negative");
-       error_found = true;
-     }
-   if (node->global.inlined_to && node->symbol.externally_visible)
-     {
-       error ("externally visible inline clone");
-       error_found = true;
-     }
-   if (node->global.inlined_to && node->symbol.address_taken)
-     {
-       error ("inline clone with address taken");
-       error_found = true;
-     }
-   if (node->global.inlined_to && node->symbol.force_output)
-     {
-       error ("inline clone is forced to output");
-       error_found = true;
-     }
-   for (e = node->indirect_calls; e; e = e->next_callee)
-     {
-       if (e->aux)
- 	{
- 	  error ("aux field set for indirect edge from %s",
- 		 identifier_to_locale (cgraph_node_name (e->caller)));
- 	  error_found = true;
- 	}
-       if (!e->indirect_unknown_callee
- 	  || !e->indirect_info)
- 	{
- 	  error ("An indirect edge from %s is not marked as indirect or has "
- 		 "associated indirect_info, the corresponding statement is: ",
- 		 identifier_to_locale (cgraph_node_name (e->caller)));
- 	  cgraph_debug_gimple_stmt (this_cfun, e->call_stmt);
- 	  error_found = true;
- 	}
-     }
-   for (e = node->callers; e; e = e->next_caller)
-     {
-       if (verify_edge_count_and_frequency (e))
- 	error_found = true;
-       if (!e->inline_failed)
- 	{
- 	  if (node->global.inlined_to
- 	      != (e->caller->global.inlined_to
- 		  ? e->caller->global.inlined_to : e->caller))
- 	    {
- 	      error ("inlined_to pointer is wrong");
- 	      error_found = true;
- 	    }
- 	  if (node->callers->next_caller)
- 	    {
- 	      error ("multiple inline callers");
- 	      error_found = true;
- 	    }
- 	}
-       else
- 	if (node->global.inlined_to)
- 	  {
- 	    error ("inlined_to pointer set for noninline callers");
- 	    error_found = true;
- 	  }
-     }
-   for (e = node->indirect_calls; e; e = e->next_callee)
-     if (verify_edge_count_and_frequency (e))
-       error_found = true;
-   if (!node->callers && node->global.inlined_to)
-     {
-       error ("inlined_to pointer is set but no predecessors found");
-       error_found = true;
-     }
-   if (node->global.inlined_to == node)
-     {
-       error ("inlined_to pointer refers to itself");
-       error_found = true;
-     }
- 
-   if (node->clone_of)
-     {
-       struct cgraph_node *n;
-       for (n = node->clone_of->clones; n; n = n->next_sibling_clone)
-         if (n == node)
- 	  break;
-       if (!n)
- 	{
- 	  error ("node has wrong clone_of");
- 	  error_found = true;
- 	}
-     }
-   if (node->clones)
-     {
-       struct cgraph_node *n;
-       for (n = node->clones; n; n = n->next_sibling_clone)
-         if (n->clone_of != node)
- 	  break;
-       if (n)
- 	{
- 	  error ("node has wrong clone list");
- 	  error_found = true;
- 	}
-     }
-   if ((node->prev_sibling_clone || node->next_sibling_clone) && !node->clone_of)
-     {
-        error ("node is in clone list but it is not clone");
-        error_found = true;
-     }
-   if (!node->prev_sibling_clone && node->clone_of && node->clone_of->clones != node)
-     {
-       error ("node has wrong prev_clone pointer");
-       error_found = true;
-     }
-   if (node->prev_sibling_clone && node->prev_sibling_clone->next_sibling_clone != node)
-     {
-       error ("double linked list of clones corrupted");
-       error_found = true;
-     }
- 
-   if (node->analyzed && node->alias)
-     {
-       bool ref_found = false;
-       int i;
-       struct ipa_ref *ref;
- 
-       if (node->callees)
- 	{
- 	  error ("Alias has call edges");
-           error_found = true;
- 	}
-       for (i = 0; ipa_ref_list_reference_iterate (&node->symbol.ref_list,
- 						  i, ref); i++)
- 	if (ref->use != IPA_REF_ALIAS)
- 	  {
- 	    error ("Alias has non-alias reference");
- 	    error_found = true;
- 	  }
- 	else if (ref_found)
- 	  {
- 	    error ("Alias has more than one alias reference");
- 	    error_found = true;
- 	  }
- 	else
- 	  ref_found = true;
- 	if (!ref_found)
- 	  {
- 	    error ("Analyzed alias has no reference");
- 	    error_found = true;
- 	  }
-     }
-   if (node->analyzed && node->thunk.thunk_p)
-     {
-       if (!node->callees)
- 	{
- 	  error ("No edge out of thunk node");
-           error_found = true;
- 	}
-       else if (node->callees->next_callee)
- 	{
- 	  error ("More than one edge out of thunk node");
-           error_found = true;
- 	}
-       if (gimple_has_body_p (node->symbol.decl))
-         {
- 	  error ("Thunk is not supposed to have body");
-           error_found = true;
-         }
-     }
-   else if (node->analyzed && gimple_has_body_p (node->symbol.decl)
-            && !TREE_ASM_WRITTEN (node->symbol.decl)
-            && (!DECL_EXTERNAL (node->symbol.decl) || node->global.inlined_to)
-            && !flag_wpa)
-     {
-       if (this_cfun->cfg)
- 	{
- 	  /* The nodes we're interested in are never shared, so walk
- 	     the tree ignoring duplicates.  */
- 	  struct pointer_set_t *visited_nodes = pointer_set_create ();
- 	  /* Reach the trees by walking over the CFG, and note the
- 	     enclosing basic-blocks in the call edges.  */
- 	  FOR_EACH_BB_FN (this_block, this_cfun)
- 	    for (gsi = gsi_start_bb (this_block);
-                  !gsi_end_p (gsi);
-                  gsi_next (&gsi))
- 	      {
- 		gimple stmt = gsi_stmt (gsi);
- 		if (is_gimple_call (stmt))
- 		  {
- 		    struct cgraph_edge *e = cgraph_edge (node, stmt);
- 		    tree decl = gimple_call_fndecl (stmt);
- 		    if (e)
- 		      {
- 			if (e->aux)
- 			  {
- 			    error ("shared call_stmt:");
- 			    cgraph_debug_gimple_stmt (this_cfun, stmt);
- 			    error_found = true;
- 			  }
- 			if (!e->indirect_unknown_callee)
- 			  {
- 			    if (verify_edge_corresponds_to_fndecl (e, decl))
- 			      {
- 				error ("edge points to wrong declaration:");
- 				debug_tree (e->callee->symbol.decl);
- 				fprintf (stderr," Instead of:");
- 				debug_tree (decl);
- 				error_found = true;
- 			      }
- 			  }
- 			else if (decl)
- 			  {
- 			    error ("an indirect edge with unknown callee "
- 				   "corresponding to a call_stmt with "
- 				   "a known declaration:");
- 			    error_found = true;
- 			    cgraph_debug_gimple_stmt (this_cfun, e->call_stmt);
- 			  }
- 			e->aux = (void *)1;
- 		      }
- 		    else if (decl)
- 		      {
- 			error ("missing callgraph edge for call stmt:");
- 			cgraph_debug_gimple_stmt (this_cfun, stmt);
- 			error_found = true;
- 		      }
- 		  }
- 	      }
- 	  pointer_set_destroy (visited_nodes);
- 	}
-       else
- 	/* No CFG available?!  */
- 	gcc_unreachable ();
- 
-       for (e = node->callees; e; e = e->next_callee)
- 	{
- 	  if (!e->aux)
- 	    {
- 	      error ("edge %s->%s has no corresponding call_stmt",
- 		     identifier_to_locale (cgraph_node_name (e->caller)),
- 		     identifier_to_locale (cgraph_node_name (e->callee)));
- 	      cgraph_debug_gimple_stmt (this_cfun, e->call_stmt);
- 	      error_found = true;
- 	    }
- 	  e->aux = 0;
- 	}
-       for (e = node->indirect_calls; e; e = e->next_callee)
- 	{
- 	  if (!e->aux)
- 	    {
- 	      error ("an indirect edge from %s has no corresponding call_stmt",
- 		     identifier_to_locale (cgraph_node_name (e->caller)));
- 	      cgraph_debug_gimple_stmt (this_cfun, e->call_stmt);
- 	      error_found = true;
- 	    }
- 	  e->aux = 0;
- 	}
-     }
-   if (error_found)
-     {
-       dump_cgraph_node (stderr, node);
-       internal_error ("verify_cgraph_node failed");
-     }
-   timevar_pop (TV_CGRAPH_VERIFY);
- }
- 
- /* Verify whole cgraph structure.  */
- DEBUG_FUNCTION void
- verify_cgraph (void)
- {
-   struct cgraph_node *node;
- 
-   if (seen_error ())
-     return;
- 
-   FOR_EACH_FUNCTION (node)
-     verify_cgraph_node (node);
- }
- 
  /* Output all asm statements we have stored up to be output.  */
  
  static void
--- 513,518 ----
*************** cgraph_output_pending_asms (void)
*** 879,885 ****
  }
  
  /* Analyze the function scheduled to be output.  */
! void
  cgraph_analyze_function (struct cgraph_node *node)
  {
    tree save = current_function_decl;
--- 529,535 ----
  }
  
  /* Analyze the function scheduled to be output.  */
! static void
  cgraph_analyze_function (struct cgraph_node *node)
  {
    tree save = current_function_decl;
*************** assemble_thunks_and_aliases (struct cgra
*** 1883,1897 ****
        }
  }
  
! /* Perform IPA transforms and all further optimizations and compilation
!    for FNDECL.  */
  
  static void
! tree_rest_of_compilation (struct cgraph_node *node)
  {
!   tree fndecl = node->symbol.decl;
    location_t saved_loc;
  
    timevar_push (TV_REST_OF_COMPILATION);
  
    gcc_assert (cgraph_global_info_ready);
--- 1533,1555 ----
        }
  }
  
! /* Expand function specified by NODE.  */
  
  static void
! cgraph_expand_function (struct cgraph_node *node)
  {
!   tree decl = node->symbol.decl;
    location_t saved_loc;
  
+   /* We ought to not compile any inline clones.  */
+   gcc_assert (!node->global.inlined_to);
+ 
+   announce_function (decl);
+   node->process = 0;
+   gcc_assert (node->lowered);
+ 
+   /* Generate RTL for the body of DECL.  */
+ 
    timevar_push (TV_REST_OF_COMPILATION);
  
    gcc_assert (cgraph_global_info_ready);
*************** tree_rest_of_compilation (struct cgraph_
*** 1900,1909 ****
    bitmap_obstack_initialize (NULL);
  
    /* Initialize the RTL code for the function.  */
!   current_function_decl = fndecl;
    saved_loc = input_location;
!   input_location = DECL_SOURCE_LOCATION (fndecl);
!   init_function_start (fndecl);
  
    gimple_register_cfg_hooks ();
  
--- 1558,1567 ----
    bitmap_obstack_initialize (NULL);
  
    /* Initialize the RTL code for the function.  */
!   current_function_decl = decl;
    saved_loc = input_location;
!   input_location = DECL_SOURCE_LOCATION (decl);
!   init_function_start (decl);
  
    gimple_register_cfg_hooks ();
  
*************** tree_rest_of_compilation (struct cgraph_
*** 1931,1939 ****
    /* If requested, warn about function definitions where the function will
       return a value (usually of some struct or union type) which itself will
       take up a lot of stack space.  */
!   if (warn_larger_than && !DECL_EXTERNAL (fndecl) && TREE_TYPE (fndecl))
      {
!       tree ret_type = TREE_TYPE (TREE_TYPE (fndecl));
  
        if (ret_type && TYPE_SIZE_UNIT (ret_type)
  	  && TREE_CODE (TYPE_SIZE_UNIT (ret_type)) == INTEGER_CST
--- 1589,1597 ----
    /* If requested, warn about function definitions where the function will
       return a value (usually of some struct or union type) which itself will
       take up a lot of stack space.  */
!   if (warn_larger_than && !DECL_EXTERNAL (decl) && TREE_TYPE (decl))
      {
!       tree ret_type = TREE_TYPE (TREE_TYPE (decl));
  
        if (ret_type && TYPE_SIZE_UNIT (ret_type)
  	  && TREE_CODE (TYPE_SIZE_UNIT (ret_type)) == INTEGER_CST
*************** tree_rest_of_compilation (struct cgraph_
*** 1945,1997 ****
  
  	  if (compare_tree_int (TYPE_SIZE_UNIT (ret_type), size_as_int) == 0)
  	    warning (OPT_Wlarger_than_, "size of return value of %q+D is %u bytes",
!                      fndecl, size_as_int);
  	  else
  	    warning (OPT_Wlarger_than_, "size of return value of %q+D is larger than %wd bytes",
!                      fndecl, larger_than_size);
  	}
      }
  
!   gimple_set_body (fndecl, NULL);
!   if (DECL_STRUCT_FUNCTION (fndecl) == 0
!       && !cgraph_get_node (fndecl)->origin)
      {
        /* Stop pointing to the local nodes about to be freed.
  	 But DECL_INITIAL must remain nonzero so we know this
  	 was an actual function definition.
  	 For a nested function, this is done in c_pop_function_context.
  	 If rest_of_compilation set this to 0, leave it 0.  */
!       if (DECL_INITIAL (fndecl) != 0)
! 	DECL_INITIAL (fndecl) = error_mark_node;
      }
  
    input_location = saved_loc;
  
    ggc_collect ();
    timevar_pop (TV_REST_OF_COMPILATION);
- }
- 
- /* Expand function specified by NODE.  */
- 
- static void
- cgraph_expand_function (struct cgraph_node *node)
- {
-   tree decl = node->symbol.decl;
- 
-   /* We ought to not compile any inline clones.  */
-   gcc_assert (!node->global.inlined_to);
- 
-   announce_function (decl);
-   node->process = 0;
-   gcc_assert (node->lowered);
- 
-   /* Generate RTL for the body of DECL.  */
-   tree_rest_of_compilation (node);
  
    /* Make sure that BE didn't give up on compiling.  */
    gcc_assert (TREE_ASM_WRITTEN (decl));
    current_function_decl = NULL;
-   gcc_assert (!cgraph_preserve_function_body_p (node));
  
    /* It would make a lot more sense to output thunks before function body to get more
       forward and lest backwarding jumps.  This is however would need solving problem
--- 1603,1636 ----
  
  	  if (compare_tree_int (TYPE_SIZE_UNIT (ret_type), size_as_int) == 0)
  	    warning (OPT_Wlarger_than_, "size of return value of %q+D is %u bytes",
!                      decl, size_as_int);
  	  else
  	    warning (OPT_Wlarger_than_, "size of return value of %q+D is larger than %wd bytes",
!                      decl, larger_than_size);
  	}
      }
  
!   gimple_set_body (decl, NULL);
!   if (DECL_STRUCT_FUNCTION (decl) == 0
!       && !cgraph_get_node (decl)->origin)
      {
        /* Stop pointing to the local nodes about to be freed.
  	 But DECL_INITIAL must remain nonzero so we know this
  	 was an actual function definition.
  	 For a nested function, this is done in c_pop_function_context.
  	 If rest_of_compilation set this to 0, leave it 0.  */
!       if (DECL_INITIAL (decl) != 0)
! 	DECL_INITIAL (decl) = error_mark_node;
      }
  
    input_location = saved_loc;
  
    ggc_collect ();
    timevar_pop (TV_REST_OF_COMPILATION);
  
    /* Make sure that BE didn't give up on compiling.  */
    gcc_assert (TREE_ASM_WRITTEN (decl));
    current_function_decl = NULL;
  
    /* It would make a lot more sense to output thunks before function body to get more
       forward and lest backwarding jumps.  This is however would need solving problem
*************** cgraph_expand_function (struct cgraph_no
*** 2006,2021 ****
    cgraph_node_remove_callees (node);
  }
  
- /* Return true when CALLER_DECL should be inlined into CALLEE_DECL.  */
- 
- bool
- cgraph_inline_p (struct cgraph_edge *e, cgraph_inline_failed_t *reason)
- {
-   *reason = e->inline_failed;
-   return !e->inline_failed;
- }
- 
- 
  
  /* Expand all functions that must be output.
  
--- 1645,1650 ----
*************** cgraph_output_in_order (void)
*** 2161,2180 ****
    free (nodes);
  }
  
- /* Return true when function body of DECL still needs to be kept around
-    for later re-use.  */
- bool
- cgraph_preserve_function_body_p (struct cgraph_node *node)
- {
-   gcc_assert (cgraph_global_info_ready);
-   gcc_assert (!node->alias && !node->thunk.thunk_p);
- 
-   /* Look if there is any clone around.  */
-   if (node->clones)
-     return true;
-   return false;
- }
- 
  static void
  ipa_passes (void)
  {
--- 1790,1795 ----
*************** output_weakrefs (void)
*** 2272,2278 ****
  		      : get_alias_symbol (vnode->symbol.decl));
  }
  
! 
  
  void
  init_cgraph (void)
--- 1887,1893 ----
  		      : get_alias_symbol (vnode->symbol.decl));
  }
  
! /* Initialize callgraph dump file.  */
  
  void
  init_cgraph (void)
Index: cgraphbuild.c
===================================================================
*** cgraphbuild.c	(revision 186713)
--- cgraphbuild.c	(working copy)
*************** record_reference (tree *tp, int *walk_su
*** 85,92 ****
        if (TREE_CODE (decl) == VAR_DECL)
  	{
  	  struct varpool_node *vnode = varpool_node (decl);
- 	  if (lang_hooks.callgraph.analyze_expr)
- 	    lang_hooks.callgraph.analyze_expr (&decl, walk_subtrees);
  	  ipa_record_reference ((symtab_node)ctx->varpool_node,
  				(symtab_node)vnode,
  				IPA_REF_ADDR, NULL);
--- 85,90 ----
*************** record_reference (tree *tp, int *walk_su
*** 102,110 ****
  	  *walk_subtrees = 0;
  	  break;
  	}
- 
-       if ((unsigned int) TREE_CODE (t) >= LAST_AND_UNUSED_TREE_CODE)
- 	return lang_hooks.callgraph.analyze_expr (tp, walk_subtrees);
        break;
      }
  
--- 100,105 ----
*************** mark_address (gimple stmt, tree addr, vo
*** 241,248 ****
        struct varpool_node *vnode = varpool_node (addr);
        int walk_subtrees;
  
-       if (lang_hooks.callgraph.analyze_expr)
- 	lang_hooks.callgraph.analyze_expr (&addr, &walk_subtrees);
        ipa_record_reference ((symtab_node)data,
  			    (symtab_node)vnode,
  			    IPA_REF_ADDR, stmt);
--- 236,241 ----
*************** mark_load (gimple stmt, tree t, void *da
*** 273,280 ****
        struct varpool_node *vnode = varpool_node (t);
        int walk_subtrees;
  
-       if (lang_hooks.callgraph.analyze_expr)
- 	lang_hooks.callgraph.analyze_expr (&t, &walk_subtrees);
        ipa_record_reference ((symtab_node)data,
  			    (symtab_node)vnode,
  			    IPA_REF_LOAD, stmt);
--- 266,271 ----
*************** mark_store (gimple stmt, tree t, void *d
*** 294,301 ****
        struct varpool_node *vnode = varpool_node (t);
        int walk_subtrees;
  
-       if (lang_hooks.callgraph.analyze_expr)
- 	lang_hooks.callgraph.analyze_expr (&t, &walk_subtrees);
        ipa_record_reference ((symtab_node)data,
  			    (symtab_node)vnode,
  			    IPA_REF_STORE, stmt);
--- 285,290 ----
Index: cgraph.c
===================================================================
*** cgraph.c	(revision 186713)
--- cgraph.c	(working copy)
*************** along with GCC; see the file COPYING3.
*** 21,76 ****
  
  /*  This file contains basic routines manipulating call graph
  
! The callgraph:
! 
!     The call-graph is data structure designed for intra-procedural optimization
!     but it is also used in non-unit-at-a-time compilation to allow easier code
!     sharing.
! 
!     The call-graph consist of nodes and edges represented via linked lists.
!     Each function (external or not) corresponds to the unique node.
! 
!     The mapping from declarations to call-graph nodes is done using hash table
!     based on DECL_UID.  The call-graph nodes are created lazily using
!     cgraph_node function when called for unknown declaration.
! 
!     The callgraph at the moment does not represent all indirect calls or calls
!     from other compilation units.  Flag NEEDED is set for each node that may be
!     accessed in such an invisible way and it shall be considered an entry point
!     to the callgraph.
! 
!     On the other hand, the callgraph currently does contain some edges for
!     indirect calls with unknown callees which can be accessed through
!     indirect_calls field of a node.  It should be noted however that at the
!     moment only calls which are potential candidates for indirect inlining are
!     added there.
! 
!     Interprocedural information:
! 
!       Callgraph is place to store data needed for interprocedural optimization.
!       All data structures are divided into three components: local_info that
!       is produced while analyzing the function, global_info that is result
!       of global walking of the callgraph on the end of compilation and
!       rtl_info used by RTL backend to propagate data from already compiled
!       functions to their callers.
! 
!       Moreover, each node has a uid which can be used to keep information in
!       on-the-side arrays.  UIDs are reused and therefore reasonably dense.
! 
!     Inlining plans:
! 
!       The function inlining information is decided in advance and maintained
!       in the callgraph as so called inline plan.
!       For each inlined call, the callee's node is cloned to represent the
!       new function copy produced by inliner.
!       Each inlined call gets a unique corresponding clone node of the callee
!       and the data structure is updated while inlining is performed, so
!       the clones are eliminated and their callee edges redirected to the
!       caller.
! 
!       Each edge has "inline_failed" field.  When the field is set to NULL,
!       the call will be inlined.  When it is non-NULL it contains a reason
!       why inlining wasn't performed.  */
  
  #include "config.h"
  #include "system.h"
--- 21,28 ----
  
  /*  This file contains basic routines manipulating call graph
  
!     The call-graph is a data structure designed for intra-procedural optimization.
!     It represents a multi-graph where nodes are functions and edges are call sites. */
  
  #include "config.h"
  #include "system.h"
*************** cgraph_remove_node_and_inline_clones (st
*** 1472,1487 ****
    return found;
  }
  
- /* Likewise indicate that a node is needed, i.e. reachable via some
-    external means.  */
- 
- void
- cgraph_mark_force_output_node (struct cgraph_node *node)
- {
-   node->symbol.force_output = 1;
-   gcc_assert (!node->global.inlined_to);
- }
- 
  /* Likewise indicate that a node is having address taken.  */
  
  void
--- 1424,1429 ----
*************** collect_callers_of_node (struct cgraph_n
*** 2672,2675 ****
--- 2614,3014 ----
    return redirect_callers;
  }
  
+ /* Return TRUE if NODE2 is equivalent to NODE or its clone.  */
+ static bool
+ clone_of_p (struct cgraph_node *node, struct cgraph_node *node2)
+ {
+   node = cgraph_function_or_thunk_node (node, NULL);
+   node2 = cgraph_function_or_thunk_node (node2, NULL);
+   while (node != node2 && node2)
+     node2 = node2->clone_of;
+   return node2 != NULL;
+ }
+ 
+ /* Verify edge E count and frequency.  */
+ 
+ static bool
+ verify_edge_count_and_frequency (struct cgraph_edge *e)
+ {
+   bool error_found = false;
+   if (e->count < 0)
+     {
+       error ("caller edge count is negative");
+       error_found = true;
+     }
+   if (e->frequency < 0)
+     {
+       error ("caller edge frequency is negative");
+       error_found = true;
+     }
+   if (e->frequency > CGRAPH_FREQ_MAX)
+     {
+       error ("caller edge frequency is too large");
+       error_found = true;
+     }
+   if (gimple_has_body_p (e->caller->symbol.decl)
+       && !e->caller->global.inlined_to
+       /* FIXME: Inline-analysis sets frequency to 0 when edge is optimized out.
+ 	 Remove this once edges are actualy removed from the function at that time.  */
+       && (e->frequency
+ 	  || (inline_edge_summary_vec
+ 	      && ((VEC_length(inline_edge_summary_t, inline_edge_summary_vec)
+ 		  <= (unsigned) e->uid)
+ 	          || !inline_edge_summary (e)->predicate)))
+       && (e->frequency
+ 	  != compute_call_stmt_bb_frequency (e->caller->symbol.decl,
+ 					     gimple_bb (e->call_stmt))))
+     {
+       error ("caller edge frequency %i does not match BB frequency %i",
+ 	     e->frequency,
+ 	     compute_call_stmt_bb_frequency (e->caller->symbol.decl,
+ 					     gimple_bb (e->call_stmt)));
+       error_found = true;
+     }
+   return error_found;
+ }
+ 
+ /* Switch to THIS_CFUN if needed and print STMT to stderr.  */
+ static void
+ cgraph_debug_gimple_stmt (struct function *this_cfun, gimple stmt)
+ {
+   /* debug_gimple_stmt needs correct cfun */
+   if (cfun != this_cfun)
+     set_cfun (this_cfun);
+   debug_gimple_stmt (stmt);
+ }
+ 
+ /* Verify that call graph edge E corresponds to DECL from the associated
+    statement.  Return true if the verification should fail.  */
+ 
+ static bool
+ verify_edge_corresponds_to_fndecl (struct cgraph_edge *e, tree decl)
+ {
+   struct cgraph_node *node;
+ 
+   if (!decl || e->callee->global.inlined_to)
+     return false;
+   node = cgraph_get_node (decl);
+ 
+   /* We do not know if a node from a different partition is an alias or what it
+      aliases and therefore cannot do the former_clone_of check reliably.  */
+   if (!node || node->symbol.in_other_partition)
+     return false;
+   node = cgraph_function_or_thunk_node (node, NULL);
+ 
+   if ((e->callee->former_clone_of != node->symbol.decl
+        && (!node->same_body_alias
+ 	   || e->callee->former_clone_of != node->thunk.alias))
+       /* IPA-CP sometimes redirect edge to clone and then back to the former
+ 	 function.  This ping-pong has to go, eventually.  */
+       && (node != cgraph_function_or_thunk_node (e->callee, NULL))
+       && !clone_of_p (node, e->callee)
+       /* If decl is a same body alias of some other decl, allow e->callee to be
+ 	 a clone of a clone of that other decl too.  */
+       && (!node->same_body_alias
+ 	  || !clone_of_p (cgraph_get_node (node->thunk.alias), e->callee)))
+     return true;
+   else
+     return false;
+ }
+ 
+ /* Verify cgraph nodes of given cgraph node.  */
+ DEBUG_FUNCTION void
+ verify_cgraph_node (struct cgraph_node *node)
+ {
+   struct cgraph_edge *e;
+   struct function *this_cfun = DECL_STRUCT_FUNCTION (node->symbol.decl);
+   basic_block this_block;
+   gimple_stmt_iterator gsi;
+   bool error_found = false;
+ 
+   if (seen_error ())
+     return;
+ 
+   timevar_push (TV_CGRAPH_VERIFY);
+   error_found |= verify_symtab_base ((symtab_node) node);
+   for (e = node->callees; e; e = e->next_callee)
+     if (e->aux)
+       {
+ 	error ("aux field set for edge %s->%s",
+ 	       identifier_to_locale (cgraph_node_name (e->caller)),
+ 	       identifier_to_locale (cgraph_node_name (e->callee)));
+ 	error_found = true;
+       }
+   if (node->count < 0)
+     {
+       error ("execution count is negative");
+       error_found = true;
+     }
+   if (node->global.inlined_to && node->symbol.externally_visible)
+     {
+       error ("externally visible inline clone");
+       error_found = true;
+     }
+   if (node->global.inlined_to && node->symbol.address_taken)
+     {
+       error ("inline clone with address taken");
+       error_found = true;
+     }
+   if (node->global.inlined_to && node->symbol.force_output)
+     {
+       error ("inline clone is forced to output");
+       error_found = true;
+     }
+   for (e = node->indirect_calls; e; e = e->next_callee)
+     {
+       if (e->aux)
+ 	{
+ 	  error ("aux field set for indirect edge from %s",
+ 		 identifier_to_locale (cgraph_node_name (e->caller)));
+ 	  error_found = true;
+ 	}
+       if (!e->indirect_unknown_callee
+ 	  || !e->indirect_info)
+ 	{
+ 	  error ("An indirect edge from %s is not marked as indirect or has "
+ 		 "associated indirect_info, the corresponding statement is: ",
+ 		 identifier_to_locale (cgraph_node_name (e->caller)));
+ 	  cgraph_debug_gimple_stmt (this_cfun, e->call_stmt);
+ 	  error_found = true;
+ 	}
+     }
+   for (e = node->callers; e; e = e->next_caller)
+     {
+       if (verify_edge_count_and_frequency (e))
+ 	error_found = true;
+       if (!e->inline_failed)
+ 	{
+ 	  if (node->global.inlined_to
+ 	      != (e->caller->global.inlined_to
+ 		  ? e->caller->global.inlined_to : e->caller))
+ 	    {
+ 	      error ("inlined_to pointer is wrong");
+ 	      error_found = true;
+ 	    }
+ 	  if (node->callers->next_caller)
+ 	    {
+ 	      error ("multiple inline callers");
+ 	      error_found = true;
+ 	    }
+ 	}
+       else
+ 	if (node->global.inlined_to)
+ 	  {
+ 	    error ("inlined_to pointer set for noninline callers");
+ 	    error_found = true;
+ 	  }
+     }
+   for (e = node->indirect_calls; e; e = e->next_callee)
+     if (verify_edge_count_and_frequency (e))
+       error_found = true;
+   if (!node->callers && node->global.inlined_to)
+     {
+       error ("inlined_to pointer is set but no predecessors found");
+       error_found = true;
+     }
+   if (node->global.inlined_to == node)
+     {
+       error ("inlined_to pointer refers to itself");
+       error_found = true;
+     }
+ 
+   if (node->clone_of)
+     {
+       struct cgraph_node *n;
+       for (n = node->clone_of->clones; n; n = n->next_sibling_clone)
+         if (n == node)
+ 	  break;
+       if (!n)
+ 	{
+ 	  error ("node has wrong clone_of");
+ 	  error_found = true;
+ 	}
+     }
+   if (node->clones)
+     {
+       struct cgraph_node *n;
+       for (n = node->clones; n; n = n->next_sibling_clone)
+         if (n->clone_of != node)
+ 	  break;
+       if (n)
+ 	{
+ 	  error ("node has wrong clone list");
+ 	  error_found = true;
+ 	}
+     }
+   if ((node->prev_sibling_clone || node->next_sibling_clone) && !node->clone_of)
+     {
+        error ("node is in clone list but it is not clone");
+        error_found = true;
+     }
+   if (!node->prev_sibling_clone && node->clone_of && node->clone_of->clones != node)
+     {
+       error ("node has wrong prev_clone pointer");
+       error_found = true;
+     }
+   if (node->prev_sibling_clone && node->prev_sibling_clone->next_sibling_clone != node)
+     {
+       error ("double linked list of clones corrupted");
+       error_found = true;
+     }
+ 
+   if (node->analyzed && node->alias)
+     {
+       bool ref_found = false;
+       int i;
+       struct ipa_ref *ref;
+ 
+       if (node->callees)
+ 	{
+ 	  error ("Alias has call edges");
+           error_found = true;
+ 	}
+       for (i = 0; ipa_ref_list_reference_iterate (&node->symbol.ref_list,
+ 						  i, ref); i++)
+ 	if (ref->use != IPA_REF_ALIAS)
+ 	  {
+ 	    error ("Alias has non-alias reference");
+ 	    error_found = true;
+ 	  }
+ 	else if (ref_found)
+ 	  {
+ 	    error ("Alias has more than one alias reference");
+ 	    error_found = true;
+ 	  }
+ 	else
+ 	  ref_found = true;
+ 	if (!ref_found)
+ 	  {
+ 	    error ("Analyzed alias has no reference");
+ 	    error_found = true;
+ 	  }
+     }
+   if (node->analyzed && node->thunk.thunk_p)
+     {
+       if (!node->callees)
+ 	{
+ 	  error ("No edge out of thunk node");
+           error_found = true;
+ 	}
+       else if (node->callees->next_callee)
+ 	{
+ 	  error ("More than one edge out of thunk node");
+           error_found = true;
+ 	}
+       if (gimple_has_body_p (node->symbol.decl))
+         {
+ 	  error ("Thunk is not supposed to have body");
+           error_found = true;
+         }
+     }
+   else if (node->analyzed && gimple_has_body_p (node->symbol.decl)
+            && !TREE_ASM_WRITTEN (node->symbol.decl)
+            && (!DECL_EXTERNAL (node->symbol.decl) || node->global.inlined_to)
+            && !flag_wpa)
+     {
+       if (this_cfun->cfg)
+ 	{
+ 	  /* The nodes we're interested in are never shared, so walk
+ 	     the tree ignoring duplicates.  */
+ 	  struct pointer_set_t *visited_nodes = pointer_set_create ();
+ 	  /* Reach the trees by walking over the CFG, and note the
+ 	     enclosing basic-blocks in the call edges.  */
+ 	  FOR_EACH_BB_FN (this_block, this_cfun)
+ 	    for (gsi = gsi_start_bb (this_block);
+                  !gsi_end_p (gsi);
+                  gsi_next (&gsi))
+ 	      {
+ 		gimple stmt = gsi_stmt (gsi);
+ 		if (is_gimple_call (stmt))
+ 		  {
+ 		    struct cgraph_edge *e = cgraph_edge (node, stmt);
+ 		    tree decl = gimple_call_fndecl (stmt);
+ 		    if (e)
+ 		      {
+ 			if (e->aux)
+ 			  {
+ 			    error ("shared call_stmt:");
+ 			    cgraph_debug_gimple_stmt (this_cfun, stmt);
+ 			    error_found = true;
+ 			  }
+ 			if (!e->indirect_unknown_callee)
+ 			  {
+ 			    if (verify_edge_corresponds_to_fndecl (e, decl))
+ 			      {
+ 				error ("edge points to wrong declaration:");
+ 				debug_tree (e->callee->symbol.decl);
+ 				fprintf (stderr," Instead of:");
+ 				debug_tree (decl);
+ 				error_found = true;
+ 			      }
+ 			  }
+ 			else if (decl)
+ 			  {
+ 			    error ("an indirect edge with unknown callee "
+ 				   "corresponding to a call_stmt with "
+ 				   "a known declaration:");
+ 			    error_found = true;
+ 			    cgraph_debug_gimple_stmt (this_cfun, e->call_stmt);
+ 			  }
+ 			e->aux = (void *)1;
+ 		      }
+ 		    else if (decl)
+ 		      {
+ 			error ("missing callgraph edge for call stmt:");
+ 			cgraph_debug_gimple_stmt (this_cfun, stmt);
+ 			error_found = true;
+ 		      }
+ 		  }
+ 	      }
+ 	  pointer_set_destroy (visited_nodes);
+ 	}
+       else
+ 	/* No CFG available?!  */
+ 	gcc_unreachable ();
+ 
+       for (e = node->callees; e; e = e->next_callee)
+ 	{
+ 	  if (!e->aux)
+ 	    {
+ 	      error ("edge %s->%s has no corresponding call_stmt",
+ 		     identifier_to_locale (cgraph_node_name (e->caller)),
+ 		     identifier_to_locale (cgraph_node_name (e->callee)));
+ 	      cgraph_debug_gimple_stmt (this_cfun, e->call_stmt);
+ 	      error_found = true;
+ 	    }
+ 	  e->aux = 0;
+ 	}
+       for (e = node->indirect_calls; e; e = e->next_callee)
+ 	{
+ 	  if (!e->aux)
+ 	    {
+ 	      error ("an indirect edge from %s has no corresponding call_stmt",
+ 		     identifier_to_locale (cgraph_node_name (e->caller)));
+ 	      cgraph_debug_gimple_stmt (this_cfun, e->call_stmt);
+ 	      error_found = true;
+ 	    }
+ 	  e->aux = 0;
+ 	}
+     }
+   if (error_found)
+     {
+       dump_cgraph_node (stderr, node);
+       internal_error ("verify_cgraph_node failed");
+     }
+   timevar_pop (TV_CGRAPH_VERIFY);
+ }
+ 
+ /* Verify whole cgraph structure.  */
+ DEBUG_FUNCTION void
+ verify_cgraph (void)
+ {
+   struct cgraph_node *node;
+ 
+   if (seen_error ())
+     return;
+ 
+   FOR_EACH_FUNCTION (node)
+     verify_cgraph_node (node);
+ }
  #include "gt-cgraph.h"
Index: cgraph.h
===================================================================
*** cgraph.h	(revision 186713)
--- cgraph.h	(working copy)
*************** bool cgraph_for_node_and_aliases (struct
*** 574,610 ****
  		                  bool (*) (struct cgraph_node *, void *),
  			          void *, bool);
  VEC (cgraph_edge_p, heap) * collect_callers_of_node (struct cgraph_node *node);
- 
- 
- /* In cgraphunit.c  */
- extern FILE *cgraph_dump_file;
- void cgraph_finalize_function (tree, bool);
- void cgraph_analyze_function (struct cgraph_node *);
- void cgraph_finalize_compilation_unit (void);
- void cgraph_optimize (void);
- void cgraph_mark_force_output_node (struct cgraph_node *);
- void cgraph_mark_address_taken_node (struct cgraph_node *);
- bool cgraph_inline_p (struct cgraph_edge *, cgraph_inline_failed_t *reason);
- bool cgraph_preserve_function_body_p (struct cgraph_node *);
  void verify_cgraph (void);
  void verify_cgraph_node (struct cgraph_node *);
! void cgraph_build_static_cdtor (char which, tree body, int priority);
! void cgraph_reset_static_var_maps (void);
! void init_cgraph (void);
! struct cgraph_node * cgraph_copy_node_for_versioning (struct cgraph_node *,
! 		tree, VEC(cgraph_edge_p,heap)*, bitmap);
! struct cgraph_node *cgraph_function_versioning (struct cgraph_node *,
! 						VEC(cgraph_edge_p,heap)*,
! 						VEC(ipa_replace_map_p,gc)*,
! 						bitmap, bool, bitmap,
! 						basic_block, const char *);
! void tree_function_versioning (tree, tree, VEC (ipa_replace_map_p,gc)*,
! 			       bool, bitmap, bool, bitmap, basic_block);
! void record_references_in_initializer (tree, bool);
! bool cgraph_process_new_functions (void);
! void cgraph_process_same_body_aliases (void);
! 
! bool cgraph_decide_is_function_needed (struct cgraph_node *, tree);
  
  typedef void (*cgraph_edge_hook)(struct cgraph_edge *, void *);
  typedef void (*cgraph_node_hook)(struct cgraph_node *, void *);
--- 574,582 ----
  		                  bool (*) (struct cgraph_node *, void *),
  			          void *, bool);
  VEC (cgraph_edge_p, heap) * collect_callers_of_node (struct cgraph_node *node);
  void verify_cgraph (void);
  void verify_cgraph_node (struct cgraph_node *);
! void cgraph_mark_address_taken_node (struct cgraph_node *);
  
  typedef void (*cgraph_edge_hook)(struct cgraph_edge *, void *);
  typedef void (*cgraph_node_hook)(struct cgraph_node *, void *);
*************** struct cgraph_2node_hook_list *cgraph_ad
*** 631,640 ****
--- 603,633 ----
  void cgraph_remove_node_duplication_hook (struct cgraph_2node_hook_list *);
  gimple cgraph_redirect_edge_call_stmt_to_callee (struct cgraph_edge *);
  bool cgraph_propagate_frequency (struct cgraph_node *node);
+ 
+ /* In cgraphunit.c  */
+ extern FILE *cgraph_dump_file;
+ void cgraph_finalize_function (tree, bool);
+ void cgraph_finalize_compilation_unit (void);
+ void cgraph_optimize (void);
+ void init_cgraph (void);
+ struct cgraph_node * cgraph_copy_node_for_versioning (struct cgraph_node *,
+ 		tree, VEC(cgraph_edge_p,heap)*, bitmap);
+ struct cgraph_node *cgraph_function_versioning (struct cgraph_node *,
+ 						VEC(cgraph_edge_p,heap)*,
+ 						VEC(ipa_replace_map_p,gc)*,
+ 						bitmap, bool, bitmap,
+ 						basic_block, const char *);
+ void tree_function_versioning (tree, tree, VEC (ipa_replace_map_p,gc)*,
+ 			       bool, bitmap, bool, bitmap, basic_block);
+ bool cgraph_process_new_functions (void);
+ void cgraph_process_same_body_aliases (void);
+ 
+ 
  /* In cgraphbuild.c  */
  unsigned int rebuild_cgraph_edges (void);
  void cgraph_rebuild_references (void);
  int compute_call_stmt_bb_frequency (tree, basic_block bb);
+ void record_references_in_initializer (tree, bool);
  
  /* In ipa.c  */
  bool cgraph_remove_unreachable_nodes (bool, FILE *);
*************** void cgraph_node_set_remove (cgraph_node
*** 646,651 ****
--- 639,645 ----
  void dump_cgraph_node_set (FILE *, cgraph_node_set);
  void debug_cgraph_node_set (cgraph_node_set);
  void free_cgraph_node_set (cgraph_node_set);
+ void cgraph_build_static_cdtor (char which, tree body, int priority);
  
  varpool_node_set varpool_node_set_new (void);
  varpool_node_set_iterator varpool_node_set_find (varpool_node_set,
*************** decl_is_tm_clone (const_tree fndecl)
*** 1284,1287 ****
--- 1278,1291 ----
      return n->tm_clone;
    return false;
  }
+ 
+ /* Likewise indicate that a node is needed, i.e. reachable via some
+    external means.  */
+ 
+ static inline void
+ cgraph_mark_force_output_node (struct cgraph_node *node)
+ {
+   node->symbol.force_output = 1;
+   gcc_checking_assert (!node->global.inlined_to);
+ }
  #endif  /* GCC_CGRAPH_H  */
Index: tree.c
===================================================================
*** tree.c	(revision 186713)
--- tree.c	(working copy)
*************** free_lang_data (void)
*** 5244,5250 ****
  
    /* Reset some langhooks.  Do not reset types_compatible_p, it may
       still be used indirectly via the get_alias_set langhook.  */
-   lang_hooks.callgraph.analyze_expr = NULL;
    lang_hooks.dwarf_name = lhd_dwarf_name;
    lang_hooks.decl_printable_name = gimple_decl_printable_name;
    /* We do not want the default decl_assembler_name implementation,
--- 5244,5249 ----
Index: ipa-inline-transform.c
===================================================================
*** ipa-inline-transform.c	(revision 186713)
--- ipa-inline-transform.c	(working copy)
*************** save_inline_function_body (struct cgraph
*** 353,358 ****
--- 353,371 ----
    return first_clone;
  }
  
+ /* Return true when function body of DECL still needs to be kept around
+    for later re-use.  */
+ bool
+ preserve_function_body_p (struct cgraph_node *node)
+ {
+   gcc_assert (cgraph_global_info_ready);
+   gcc_assert (!node->alias && !node->thunk.thunk_p);
+ 
+   /* Look if there is any clone around.  */
+   if (node->clones)
+     return true;
+   return false;
+ }
  
  /* Apply inline plan to function.  */
  
*************** inline_transform (struct cgraph_node *no
*** 369,375 ****
  
    /* We might need the body of this function so that we can expand
       it inline somewhere else.  */
!   if (cgraph_preserve_function_body_p (node))
      save_inline_function_body (node);
  
    for (e = node->callees; e; e = e->next_callee)
--- 382,388 ----
  
    /* We might need the body of this function so that we can expand
       it inline somewhere else.  */
!   if (preserve_function_body_p (node))
      save_inline_function_body (node);
  
    for (e = node->callees; e; e = e->next_callee)
Index: langhooks.c
===================================================================
*** langhooks.c	(revision 186713)
--- langhooks.c	(working copy)
*************** lhd_print_error_function (diagnostic_con
*** 472,484 ****
  }
  
  tree
- lhd_callgraph_analyze_expr (tree *tp ATTRIBUTE_UNUSED,
- 			    int *walk_subtrees ATTRIBUTE_UNUSED)
- {
-   return NULL;
- }
- 
- tree
  lhd_make_node (enum tree_code code)
  {
    return make_node (code);
--- 472,477 ----
Index: langhooks.h
===================================================================
*** langhooks.h	(revision 186713)
--- langhooks.h	(working copy)
*************** struct lang_hooks_for_tree_inlining
*** 43,55 ****
    bool (*var_mod_type_p) (tree, tree);
  };
  
- struct lang_hooks_for_callgraph
- {
-   /* The node passed is a language-specific tree node.  If its contents
-      are relevant to use of other declarations, mark them.  */
-   tree (*analyze_expr) (tree *, int *);
- };
- 
  /* The following hooks are used by tree-dump.c.  */
  
  struct lang_hooks_for_tree_dump
--- 43,48 ----
*************** struct lang_hooks
*** 407,414 ****
  
    struct lang_hooks_for_tree_inlining tree_inlining;
  
-   struct lang_hooks_for_callgraph callgraph;
- 
    struct lang_hooks_for_tree_dump tree_dump;
  
    struct lang_hooks_for_decls decls;
--- 400,405 ----
Index: tree-inline.c
===================================================================
*** tree-inline.c	(revision 186713)
--- tree-inline.c	(working copy)
*************** expand_call_inline (basic_block bb, gimp
*** 3807,3814 ****
      fn = DECL_ABSTRACT_ORIGIN (fn);
  
    /* Don't try to inline functions that are not well-suited to inlining.  */
!   if (!cgraph_inline_p (cg_edge, &reason))
      {
        /* If this call was originally indirect, we do not want to emit any
  	 inlining related warnings or sorry messages because there are no
  	 guarantees regarding those.  */
--- 3807,3815 ----
      fn = DECL_ABSTRACT_ORIGIN (fn);
  
    /* Don't try to inline functions that are not well-suited to inlining.  */
!   if (cg_edge->inline_failed)
      {
+       reason = cg_edge->inline_failed;
        /* If this call was originally indirect, we do not want to emit any
  	 inlining related warnings or sorry messages because there are no
  	 guarantees regarding those.  */
Index: varpool.c
===================================================================
*** varpool.c	(revision 186713)
--- varpool.c	(working copy)
*************** along with GCC; see the file COPYING3.
*** 37,53 ****
  #include "tree-flow.h"
  #include "flags.h"
  
- /*  This file contains basic routines manipulating variable pool.
- 
-     Varpool acts as interface in between the front-end and middle-end
-     and drives the decision process on what variables and when are
-     going to be compiled.
- 
-     The varpool nodes are allocated lazily for declarations
-     either by frontend or at callgraph construction time.
-     All variables supposed to be output into final file needs to be
-     explicitly marked by frontend via VARPOOL_FINALIZE_DECL function.  */
- 
  /* Return varpool node assigned to DECL.  Create new one when needed.  */
  struct varpool_node *
  varpool_node (tree decl)
--- 37,42 ----
Index: langhooks-def.h
===================================================================
*** langhooks-def.h	(revision 186713)
--- langhooks-def.h	(working copy)
*************** extern void lhd_init_options (unsigned i
*** 69,75 ****
  extern bool lhd_complain_wrong_lang_p (const struct cl_option *);
  extern bool lhd_handle_option (size_t, const char *, int, int, location_t,
  			       const struct cl_option_handlers *);
- extern tree lhd_callgraph_analyze_expr (tree *, int *);
  
  
  /* Declarations for tree gimplification hooks.  */
--- 69,74 ----
*************** extern void lhd_omp_firstprivatize_type_
*** 132,143 ****
    LANG_HOOKS_TREE_INLINING_VAR_MOD_TYPE_P, \
  }
  
- #define LANG_HOOKS_CALLGRAPH_ANALYZE_EXPR lhd_callgraph_analyze_expr
- 
- #define LANG_HOOKS_CALLGRAPH_INITIALIZER { \
-   LANG_HOOKS_CALLGRAPH_ANALYZE_EXPR \
- }
- 
  /* Hooks for tree gimplification.  */
  #define LANG_HOOKS_GIMPLIFY_EXPR lhd_gimplify_expr
  
--- 131,136 ----
*************** extern void lhd_end_section (void);
*** 292,298 ****
    LANG_HOOKS_COMMON_ATTRIBUTE_TABLE, \
    LANG_HOOKS_FORMAT_ATTRIBUTE_TABLE, \
    LANG_HOOKS_TREE_INLINING_INITIALIZER, \
-   LANG_HOOKS_CALLGRAPH_INITIALIZER, \
    LANG_HOOKS_TREE_DUMP_INITIALIZER, \
    LANG_HOOKS_DECLS, \
    LANG_HOOKS_FOR_TYPES_INITIALIZER, \
--- 285,290 ----



More information about the Gcc-patches mailing list