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]

[patch] C++ unit-at-a-time try 2


Hi,
This is updated version of unit-at-a-time patch for C++.  It removes the major
kludges dealing with the function deffering.  Now in the unit-at-a-time mode
all produced functions are formed as trees and passed to cgraphunit code.

This is not done for datastructures as at the moment there is no way to do dead
datastructures removal.  Instead of keeps the original code to output only
datastructures referenced, but the referenced flag is not set by RTL expansion
but by the lowering callback that is called from cgraphunit for reachable
functions only.

I would like to change this into code that directly emits needed
datastructures, but it requires more of reorganization and it can be done step
by step (virtual tables first, followed by the others).  That should avoid
quadratic behaviour of the whole loop.

Also the templates are not all instanciated at the moment.  I am having
problems with performance of the compiler when I do so, so I would like to do
that as incremental step. (and also I don't quite understand all the
various conditionals causing templates to be deferred)

The patch bootstraps and passes testsuite, however when unit-at-a-time is
forced to be enabled at -O2 it causes testcases pretty1/pretty2 to fail.  I
have no idea why. Hope that it is something trivial.

I am not quite sure whether the patch can be applicable for mainline as it
accomplishes less than I originally intended, but thing is more dificult for me
than I've expected and I believe it is possible to continue from this patch
incrementally.  Mark, what do you think about it?

Honza

Thu May 22 18:43:27 CEST 2003  Jan Hubicka  <jh@suse.cz>
	* toplev.c (wrapup_global_declarations): Allow NULL pointers in the decl array.
	* cp-lang.c (LANG_HOOKS_CALLGRAPH_EXPAND_FUNCTION,
	LANG_HOOKS_CALLGRAPH_LOWER_FUNCTION,
	LANG_HOOKS_CALLGRAPH_COLLECT_FUNCTIONS): Define hooks.
	* cp-tree.h (lower_function, really_expand_body, collect_functions):
	Declare.
	* decl2.c: Include cgraph.h and tree-inline.h
	(eof_locus): New global variable.
	(finish_objects): Mark node as needed.
	(collect_functions_1): Break out from ...
	(produce_final_constructors): Break out from ...
	(finish_file): ... here; use static eof_locus; deal with unit-at-a-time
	(collect_functions): New function.
	(mark_used_decls): New functions.
	(lower_function): New function.
	* semantic.c: Include cgraph.h.
	(really_expand_body): Break out from ...; deal with unit-at-a-time
	(finish_file): ... here.
diff -Nrc3p ../cp.old/cp-lang.c cp/cp-lang.c
*** ../cp.old/cp-lang.c	Sat Jun  7 15:03:48 2003
--- cp/cp-lang.c	Wed Jun 18 19:56:56 2003
*************** static bool cp_var_mod_type_p (tree);
*** 145,150 ****
--- 145,157 ----
  #undef LANG_HOOKS_EXPR_SIZE
  #define LANG_HOOKS_EXPR_SIZE cp_expr_size
  
+ #undef LANG_HOOKS_CALLGRAPH_EXPAND_FUNCTION
+ #define LANG_HOOKS_CALLGRAPH_EXPAND_FUNCTION really_expand_body
+ #undef LANG_HOOKS_CALLGRAPH_LOWER_FUNCTION
+ #define LANG_HOOKS_CALLGRAPH_LOWER_FUNCTION lower_function
+ #undef LANG_HOOKS_CALLGRAPH_COLLECT_FUNCTIONS
+ #define LANG_HOOKS_CALLGRAPH_COLLECT_FUNCTIONS collect_functions
+ 
  #undef LANG_HOOKS_MAKE_TYPE
  #define LANG_HOOKS_MAKE_TYPE cxx_make_type
  #undef LANG_HOOKS_TYPE_FOR_MODE
diff -Nrc3p ../cp.old/cp-tree.h cp/cp-tree.h
*** ../cp.old/cp-tree.h	Sat Jun  7 15:03:48 2003
--- cp/cp-tree.h	Wed Jun 18 22:55:30 2003
*************** extern tree build_artificial_parm (tree,
*** 3791,3796 ****
--- 3791,3797 ----
  extern tree get_guard (tree);
  extern tree get_guard_cond (tree);
  extern tree set_guard (tree);
+ extern void lower_function (tree);
  
  extern void cp_error_at		(const char *msgid, ...);
  extern void cp_warning_at	(const char *msgid, ...);
*************** extern const char *cp_file_of			(tree);
*** 3815,3820 ****
--- 3816,3822 ----
  extern int cp_line_of				(tree);
  extern const char *language_to_string           (enum languages, int);
  extern void print_instantiation_context         (void);
+ extern bool cp_finalize_function		(tree);
  
  /* in except.c */
  extern void init_exception_processing		(void);
*************** extern void clear_out_block             
*** 4146,4151 ****
--- 4148,4155 ----
  extern tree begin_global_stmt_expr              (void);
  extern tree finish_global_stmt_expr             (tree);
  extern tree check_template_template_default_arg (tree);
+ extern void really_expand_body			(tree);
+ extern bool collect_functions			(void);
  
  /* in tree.c */
  extern void lang_check_failed			(const char *, int,
diff -Nrc3p ../cp.old/decl2.c cp/decl2.c
*** ../cp.old/decl2.c	Thu May 22 01:27:50 2003
--- cp/decl2.c	Wed Jun 18 22:56:17 2003
*************** Boston, MA 02111-1307, USA.  */
*** 46,51 ****
--- 46,53 ----
  #include "cpplib.h"
  #include "target.h"
  #include "c-common.h"
+ #include "tree-inline.h"
+ #include "cgraph.h"
  extern cpp_reader *parse_in;
  
  /* This structure contains information about the initializations
*************** tree static_dtors;
*** 117,122 ****
--- 119,127 ----
  /* The :: namespace.  */
  
  tree global_namespace;
+ 
+ /* Location at the end of file.  */
+ static location_t eof_locus;
  
  /* Incorporate `const' and `volatile' qualifiers for member functions.
     FUNCTION is a TYPE_DECL or a FUNCTION_DECL.
*************** generate_ctor_and_dtor_functions_for_pri
*** 2536,2574 ****
    return 0;
  }
  
! /* This routine is called from the last rule in yyparse ().
!    Its job is to create all the code needed to initialize and
!    destroy the global aggregates.  We do the destruction
!    first, since that way we only need to reverse the decls once.  */
! 
! void
! finish_file ()
  {
!   tree vars;
!   bool reconsider;
!   size_t i;
!   location_t locus;
!   unsigned ssdf_count = 0;
  
!   locus = input_location;
!   at_eof = 1;
  
!   /* Bad parse errors.  Just forget about it.  */
!   if (! global_bindings_p () || current_class_type || decl_namespace_list)
!     return;
  
!   if (pch_file)
!     c_common_write_pch ();
  
!   /* Otherwise, GDB can get confused, because in only knows
!      about source for LINENO-1 lines.  */
!   input_line -= 1;
  
!   interface_unknown = 1;
!   interface_only = 0;
  
!   /* We now have to write out all the stuff we put off writing out.
!      These include:
  
         o Template specializations that we have not yet instantiated,
           but which are needed.
--- 2541,2648 ----
    return 0;
  }
  
! /* Mark all references to static data as used.  Called via walk_tree.  This is
!    needed to get virtual table methods output properly.  The functions are
!    dealt with in the cgraphunit.c.  */
! static tree
! mark_used_decls (tree *tp, int *walk_subtrees ATTRIBUTE_UNUSED,
! 		 void *data ATTRIBUTE_UNUSED)
  {
!   if (DECL_P (*tp) && TREE_STATIC (*tp) && DECL_ASSEMBLER_NAME_SET_P (*tp)
!       && TREE_CODE (*tp) != FUNCTION_DECL)
!      TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (*tp)) = 1;
!   return 0;
! }
  
! /* Everything needed to expand DECL is known.  Expand it if needed.  */
  
! bool
! cp_finalize_function (tree decl)
! {
!   input_location = eof_locus;
!   /* Does it need synthesizing?  */
!   if (DECL_ARTIFICIAL (decl) && !DECL_INITIAL (decl)
!       && (!DECL_REALLY_EXTERN (decl) || DECL_INLINE (decl)))
!     {
!       if (TREE_USED (decl))
! 	{
! 	  /* Even though we're already at the top-level, we push
! 	     there again.  That way, when we pop back a few lines
! 	     hence, all of our state is restored.  Otherwise,
! 	     finish_function doesn't clean things up, and we end
! 	     up with CURRENT_FUNCTION_DECL set.  */
! 	  push_to_top_level ();
! 	  synthesize_method (decl);
! 	  pop_from_top_level ();
! 	}
!       else
! 	return false;
!     }
  
!   /* If the function has no body, avoid calling
!      import_export_decl.  On a system without weak symbols,
!      calling import_export_decl will make an inline template
!      instantiation "static", which will result in errors about
!      the use of undefined functions if there is no body for
!      the function.  */
!   if (!DECL_SAVED_TREE (decl)
!       || (!flag_unit_at_a_time && DECL_DEFER_OUTPUT (decl)))
!     return false;
  
!   import_export_decl (decl);
  
!   /* We lie to the back-end, pretending that some functions
!      are not defined when they really are.  This keeps these
!      functions from being put out unnecessarily.  But, we must
!      stop lying when the functions are referenced, or if they
!      are not comdat since they need to be put out now.  This
!      is done in a separate for cycle, because if some deferred
!      function is contained in another deferred function later
!      in deferred_fns varray, rest_of_compilation would skip
!      this function and we really cannot expand the same
!      function twice.  */
!   if (DECL_NOT_REALLY_EXTERN (decl) && DECL_INITIAL (decl))
!     DECL_EXTERNAL (decl) = 0;
! 
!   /* If we're going to need to write this function out, and
!      there's already a body for it, create RTL for it now.
!      (There might be no body if this is a method we haven't
!      gotten around to synthesizing yet.)  */
!   if (!DECL_EXTERNAL (decl)
!       && !flag_unit_at_a_time
!       && DECL_NEEDED_P (decl)
!       && DECL_SAVED_TREE (decl) && !TREE_ASM_WRITTEN (decl))
!     {
!       int saved_not_really_extern;
! 
!       /* When we call finish_function in expand_body, it will
!          try to reset DECL_NOT_REALLY_EXTERN so we save and
!          restore it here.  */
!       saved_not_really_extern = DECL_NOT_REALLY_EXTERN (decl);
!       /* Generate RTL for this function now that we know we
!          need it.  */
!       expand_body (decl);
!       /* Undo the damage done by finish_function.  */
!       DECL_EXTERNAL (decl) = 0;
!       DECL_NOT_REALLY_EXTERN (decl) = saved_not_really_extern;
!       /* If we're compiling -fsyntax-only pretend that this
!          function has been written out so that we don't try to
!          expand it again.  */
!       if (flag_syntax_only)
! 	TREE_ASM_WRITTEN (decl) = 1;
!       return true;
!     }
!   if (flag_unit_at_a_time)
!     {
!       cgraph_finalize_function (decl, DECL_SAVED_TREE (decl));
!       return true;
!     }
!   return false;
! }
!    
  
! /* Write out all the stuff we put off writing out except for the function
!    bodies themselves.  These include:
  
         o Template specializations that we have not yet instantiated,
           but which are needed.
*************** finish_file ()
*** 2578,2864 ****
  	 and are cleaned up via atexit.)
         o Virtual function tables.  
  
!      All of these may cause others to be needed.  For example,
!      instantiating one function may cause another to be needed, and
!      generating the initializer for an object may cause templates to be
!      instantiated, etc., etc.  */
  
!   timevar_push (TV_VARCONST);
! 
!   emit_support_tinfos ();
!   
!   do 
!     {
!       tree t;
!       size_t n_old, n_new;
  
!       reconsider = false;
  
!       /* If there are templates that we've put off instantiating, do
! 	 them now.  */
!       instantiate_pending_templates ();
! 
!       /* Write out virtual tables as required.  Note that writing out
!   	 the virtual table for a template class may cause the
!  	 instantiation of members of that class.  If we write out
!  	 vtables then we remove the class from our list so we don't
!  	 have to look at it again. */
!  
!       while (keyed_classes != NULL_TREE
!  	     && maybe_emit_vtables (TREE_VALUE (keyed_classes)))
!  	{
!   	  reconsider = true;
!  	  keyed_classes = TREE_CHAIN (keyed_classes);
!  	}
!  
!       t = keyed_classes;
!       if (t != NULL_TREE)
!  	{
!  	  tree next = TREE_CHAIN (t);
!  
!  	  while (next)
!  	    {
!  	      if (maybe_emit_vtables (TREE_VALUE (next)))
!  		{
!  		  reconsider = true;
!  		  TREE_CHAIN (t) = TREE_CHAIN (next);
!  		}
!  	      else
!  		t = next;
!  
!  	      next = TREE_CHAIN (t);
!  	    }
!  	}
!        
!       /* Write out needed type info variables.  We have to be careful
!  	 looping through unemitted decls, because emit_tinfo_decl may
!  	 cause other variables to be needed.  We stick new elements
!  	 (and old elements that we may need to reconsider) at the end
!  	 of the array, then shift them back to the beginning once we're
!  	 done. */
!   
!       n_old = VARRAY_ACTIVE_SIZE (unemitted_tinfo_decls);
!       for (i = 0; i < n_old; ++i)
!   	{
!   	  tree tinfo_decl = VARRAY_TREE (unemitted_tinfo_decls, i);
!   	  if (emit_tinfo_decl (tinfo_decl))
!  	    reconsider = true;
!   	  else
!   	    VARRAY_PUSH_TREE (unemitted_tinfo_decls, tinfo_decl);
!   	}
!   
!       /* The only elements we want to keep are the new ones.  Copy
!   	 them to the beginning of the array, then get rid of the
!   	 leftovers. */
!       n_new = VARRAY_ACTIVE_SIZE (unemitted_tinfo_decls) - n_old;
!       memmove (&VARRAY_TREE (unemitted_tinfo_decls, 0),
!   	       &VARRAY_TREE (unemitted_tinfo_decls, n_old),
!   	       n_new * sizeof (tree));
!       memset (&VARRAY_TREE (unemitted_tinfo_decls, n_new),
!   	      0,
!   	      n_old * sizeof (tree));
!       VARRAY_ACTIVE_SIZE (unemitted_tinfo_decls) = n_new;
! 
!       /* The list of objects with static storage duration is built up
! 	 in reverse order.  We clear STATIC_AGGREGATES so that any new
! 	 aggregates added during the initialization of these will be
! 	 initialized in the correct order when we next come around the
! 	 loop.  */
!       vars = prune_vars_needing_no_initialization (&static_aggregates);
! 
!       if (vars)
! 	{
! 	  tree v;
! 
! 	  /* We need to start a new initialization function each time
! 	     through the loop.  That's because we need to know which
! 	     vtables have been referenced, and TREE_SYMBOL_REFERENCED
! 	     isn't computed until a function is finished, and written
! 	     out.  That's a deficiency in the back-end.  When this is
! 	     fixed, these initialization functions could all become
! 	     inline, with resulting performance improvements.  */
! 	  tree ssdf_body;
! 
! 	  /* Set the line and file, so that it is obviously not from
! 	     the source file.  */
! 	  input_location = locus;
! 	  ssdf_body = start_static_storage_duration_function (ssdf_count);
  
! 	  /* Make sure the back end knows about all the variables.  */
! 	  write_out_vars (vars);
  
! 	  /* First generate code to do all the initializations.  */
! 	  for (v = vars; v; v = TREE_CHAIN (v))
! 	    do_static_initialization (TREE_VALUE (v),
! 				      TREE_PURPOSE (v));
  
! 	  /* Then, generate code to do all the destructions.  Do these
! 	     in reverse order so that the most recently constructed
! 	     variable is the first destroyed.  If we're using
! 	     __cxa_atexit, then we don't need to do this; functions
! 	     were registered at initialization time to destroy the
! 	     local statics.  */
! 	  if (!flag_use_cxa_atexit)
  	    {
! 	      vars = nreverse (vars);
! 	      for (v = vars; v; v = TREE_CHAIN (v))
! 		do_static_destruction (TREE_VALUE (v));
  	    }
  	  else
! 	    vars = NULL_TREE;
  
! 	  /* Finish up the static storage duration function for this
! 	     round.  */
! 	  input_location = locus;
! 	  finish_static_storage_duration_function (ssdf_body);
! 
! 	  /* All those initializations and finalizations might cause
! 	     us to need more inline functions, more template
! 	     instantiations, etc.  */
! 	  reconsider = true;
! 	  ssdf_count++;
! 	  locus.line++;
  	}
!       
!       for (i = 0; i < deferred_fns_used; ++i)
! 	{
! 	  tree decl = VARRAY_TREE (deferred_fns, i);
! 
! 	  /* Does it need synthesizing?  */
! 	  if (DECL_ARTIFICIAL (decl) && ! DECL_INITIAL (decl)
! 	      && TREE_USED (decl)
! 	      && (! DECL_REALLY_EXTERN (decl) || DECL_INLINE (decl)))
! 	    {
! 	      /* Even though we're already at the top-level, we push
! 		 there again.  That way, when we pop back a few lines
! 		 hence, all of our state is restored.  Otherwise,
! 		 finish_function doesn't clean things up, and we end
! 		 up with CURRENT_FUNCTION_DECL set.  */
! 	      push_to_top_level ();
! 	      synthesize_method (decl);
! 	      pop_from_top_level ();
! 	      reconsider = true;
! 	    }
! 
! 	  /* If the function has no body, avoid calling
! 	     import_export_decl.  On a system without weak symbols,
! 	     calling import_export_decl will make an inline template
! 	     instantiation "static", which will result in errors about
! 	     the use of undefined functions if there is no body for
! 	     the function.  */
! 	  if (!DECL_SAVED_TREE (decl))
! 	    continue;
! 
! 	  import_export_decl (decl);
! 
! 	  /* We lie to the back-end, pretending that some functions
! 	     are not defined when they really are.  This keeps these
! 	     functions from being put out unnecessarily.  But, we must
! 	     stop lying when the functions are referenced, or if they
! 	     are not comdat since they need to be put out now.  This
! 	     is done in a separate for cycle, because if some deferred
! 	     function is contained in another deferred function later
! 	     in deferred_fns varray, rest_of_compilation would skip
! 	     this function and we really cannot expand the same
! 	     function twice.  */
! 	  if (DECL_NOT_REALLY_EXTERN (decl)
! 	      && DECL_INITIAL (decl)
! 	      && DECL_NEEDED_P (decl))
! 	    DECL_EXTERNAL (decl) = 0;
! 
! 	  /* If we're going to need to write this function out, and
! 	     there's already a body for it, create RTL for it now.
! 	     (There might be no body if this is a method we haven't
! 	     gotten around to synthesizing yet.)  */
! 	  if (!DECL_EXTERNAL (decl)
! 	      && DECL_NEEDED_P (decl)
! 	      && DECL_SAVED_TREE (decl)
! 	      && !TREE_ASM_WRITTEN (decl))
! 	    {
! 	      int saved_not_really_extern;
  
! 	      /* When we call finish_function in expand_body, it will
! 		 try to reset DECL_NOT_REALLY_EXTERN so we save and
! 		 restore it here.  */
! 	      saved_not_really_extern = DECL_NOT_REALLY_EXTERN (decl);
! 	      /* Generate RTL for this function now that we know we
! 		 need it.  */
! 	      expand_body (decl);
! 	      /* Undo the damage done by finish_function.  */
! 	      DECL_EXTERNAL (decl) = 0;
! 	      DECL_NOT_REALLY_EXTERN (decl) = saved_not_really_extern;
! 	      /* If we're compiling -fsyntax-only pretend that this
! 		 function has been written out so that we don't try to
! 		 expand it again.  */
! 	      if (flag_syntax_only)
! 		TREE_ASM_WRITTEN (decl) = 1;
! 	      reconsider = true;
! 	    }
! 	}
  
!       if (deferred_fns_used
! 	  && wrapup_global_declarations (&VARRAY_TREE (deferred_fns, 0),
! 					 deferred_fns_used))
! 	reconsider = true;
!       if (walk_namespaces (wrapup_globals_for_namespace, /*data=*/0))
  	reconsider = true;
  
!       /* Static data members are just like namespace-scope globals.  */
!       for (i = 0; i < pending_statics_used; ++i) 
  	{
! 	  tree decl = VARRAY_TREE (pending_statics, i);
! 	  if (TREE_ASM_WRITTEN (decl))
! 	    continue;
! 	  import_export_decl (decl);
! 	  if (DECL_NOT_REALLY_EXTERN (decl) && ! DECL_IN_AGGR_P (decl))
! 	    DECL_EXTERNAL (decl) = 0;
! 	}
!       if (pending_statics
! 	  && wrapup_global_declarations (&VARRAY_TREE (pending_statics, 0),
! 					 pending_statics_used))
! 	reconsider = true;
!     } 
!   while (reconsider);
  
!   /* All used inline functions must have a definition at this point. */
!   for (i = 0; i < deferred_fns_used; ++i)
      {
!       tree decl = VARRAY_TREE (deferred_fns, i);
  
!       if (TREE_USED (decl) && DECL_DECLARED_INLINE_P (decl)
! 	  && !(TREE_ASM_WRITTEN (decl) || DECL_SAVED_TREE (decl)
! 	       /* An explicit instantiation can be used to specify
! 	          that the body is in another unit. It will have
! 	          already verified there was a definition.  */
! 	       || DECL_EXPLICIT_INSTANTIATION (decl)))
! 	{
! 	  cp_warning_at ("inline function `%D' used but never defined", decl);
! 	  /* This symbol is effectively an "extern" declaration now.
! 	     This is not strictly necessary, but removes a duplicate
! 	     warning.  */
! 	  TREE_PUBLIC (decl) = 1;
! 	}
!       
      }
!   
    /* We give C linkage to static constructors and destructors.  */
    push_lang_context (lang_name_c);
  
-   /* Generate initialization and destruction functions for all
-      priorities for which they are required.  */
    if (priority_info_map)
!     splay_tree_foreach (priority_info_map, 
  			generate_ctor_and_dtor_functions_for_priority,
! 			/*data=*/&locus);
    else
      {
-       
        if (static_ctors)
! 	generate_ctor_or_dtor_function (/*constructor_p=*/true,
! 					DEFAULT_INIT_PRIORITY, &locus);
        if (static_dtors)
! 	generate_ctor_or_dtor_function (/*constructor_p=*/false,
! 					DEFAULT_INIT_PRIORITY, &locus);
      }
  
    /* We're done with the splay-tree now.  */
--- 2652,2841 ----
  	 and are cleaned up via atexit.)
         o Virtual function tables.  
  
!    All of these may cause others to be needed.  For example,
!    instantiating one function may cause another to be needed, and
!    generating the initializer for an object may cause templates to be
!    instantiated, etc., etc.  */
  
! static bool
! collect_functions_1 (void)
! {
!   bool reconsider = false;
!   tree t;
!   tree vars;
!   unsigned int i;
!   static unsigned ssdf_count = 0;
!   size_t n_old, n_new;
  
!   /* If there are templates that we've put off instantiating, do
!      them now.  */
!   instantiate_pending_templates ();
  
!   /* Write out virtual tables as required.  Note that writing out
!      the virtual table for a template class may cause the
!      instantiation of members of that class.  If we write out
!      vtables then we remove the class from our list so we don't
!      have to look at it again. */
  
!   while (keyed_classes != NULL_TREE
! 	 && maybe_emit_vtables (TREE_VALUE (keyed_classes)))
!     {
!       reconsider = 1;
!       keyed_classes = TREE_CHAIN (keyed_classes);
!     }
  
!   t = keyed_classes;
!   if (t != NULL_TREE)
!     {
!       tree next = TREE_CHAIN (t);
  
!       while (next)
! 	{
! 	  if (maybe_emit_vtables (TREE_VALUE (next)))
  	    {
! 	      reconsider = 1;
! 	      TREE_CHAIN (t) = TREE_CHAIN (next);
  	    }
  	  else
! 	    t = next;
  
! 	  next = TREE_CHAIN (t);
  	}
!     }
  
!   /* Write out needed type info variables.  We have to be careful
!      looping through unemitted decls, because emit_tinfo_decl may
!      cause other variables to be needed.  We stick new elements
!      (and old elements that we may need to reconsider) at the end
!      of the array, then shift them back to the beginning once we're
!      done. */
  
!   n_old = VARRAY_ACTIVE_SIZE (unemitted_tinfo_decls);
!   for (i = 0; i < n_old; ++i)
!     {
!       tree tinfo_decl = VARRAY_TREE (unemitted_tinfo_decls, i);
!       if (emit_tinfo_decl (tinfo_decl))
  	reconsider = true;
+       else
+ 	VARRAY_PUSH_TREE (unemitted_tinfo_decls, tinfo_decl);
+     }
  
!   /* The only elements we want to keep are the new ones.  Copy
!      them to the beginning of the array, then get rid of the
!      leftovers. */
!   n_new = VARRAY_ACTIVE_SIZE (unemitted_tinfo_decls) - n_old;
!   memmove (&VARRAY_TREE (unemitted_tinfo_decls, 0),
! 	   &VARRAY_TREE (unemitted_tinfo_decls, n_old),
! 	   n_new * sizeof (tree));
!   memset (&VARRAY_TREE (unemitted_tinfo_decls, n_new),
! 	  0, n_old * sizeof (tree));
!   VARRAY_ACTIVE_SIZE (unemitted_tinfo_decls) = n_new;
! 
!   /* The list of objects with static storage duration is built up
!      in reverse order.  We clear STATIC_AGGREGATES so that any new
!      aggregates added during the initialization of these will be
!      initialized in the correct order when we next come around the
!      loop.  */
!   vars = prune_vars_needing_no_initialization (&static_aggregates);
! 
!   if (vars)
!     {
!       tree v;
! 
!       /* We need to start a new initialization function each time
! 	 through the loop.  That's because we need to know which
! 	 vtables have been referenced, and TREE_SYMBOL_REFERENCED
! 	 isn't computed until a function is finished, and written
! 	 out.  That's a deficiency in the back-end.  When this is
! 	 fixed, these initialization functions could all become
! 	 inline, with resulting performance improvements.  */
!       tree ssdf_body;
! 
!       /* Set the line and file, so that it is obviously not from
! 	 the source file.  */
!       input_location = eof_locus;
!       ssdf_body = start_static_storage_duration_function (ssdf_count);
! 
!       /* Make sure the back end knows about all the variables.  */
!       write_out_vars (vars);
! 
!       /* First generate code to do all the initializations.  */
!       for (v = vars; v; v = TREE_CHAIN (v))
! 	do_static_initialization (TREE_VALUE (v), TREE_PURPOSE (v));
! 
!       /* Then, generate code to do all the destructions.  Do these
! 	 in reverse order so that the most recently constructed
! 	 variable is the first destroyed.  If we're using
! 	 __cxa_atexit, then we don't need to do this; functions
! 	 were registered at initialization time to destroy the
! 	 local statics.  */
!       if (!flag_use_cxa_atexit)
  	{
! 	  vars = nreverse (vars);
! 	  for (v = vars; v; v = TREE_CHAIN (v))
! 	    do_static_destruction (TREE_VALUE (v));
! 	}
!       else
! 	vars = NULL_TREE;
  
!       /* Finish up the static storage duration function for this
! 	 round.  */
!       input_location = eof_locus;
!       finish_static_storage_duration_function (ssdf_body);
! 
!       /* All those initializations and finalizations might cause
! 	 us to need more inline functions, more template
! 	 instantiations, etc.  */
!       reconsider = true;
!       ssdf_count++;
!       eof_locus.line++;
!     }
! 
!   if (deferred_fns_used
!       && wrapup_global_declarations (&VARRAY_TREE (deferred_fns, 0),
! 				     deferred_fns_used))
!     reconsider = true;
!   if (walk_namespaces (wrapup_globals_for_namespace, /*data= */ 0))
!     reconsider = true;
!   /* Static data members are just like namespace-scope globals.  */
!   for (i = 0; i < pending_statics_used; ++i)
      {
!       tree decl = VARRAY_TREE (pending_statics, i);
  
!       if (TREE_ASM_WRITTEN (decl))
! 	continue;
!       import_export_decl (decl);
!       if (DECL_NOT_REALLY_EXTERN (decl) && !DECL_IN_AGGR_P (decl))
! 	DECL_EXTERNAL (decl) = 0;
      }
!   if (pending_statics
!       && wrapup_global_declarations (&VARRAY_TREE (pending_statics, 0),
! 				     pending_statics_used))
!     reconsider = true;
! 
!   return reconsider;
! }
! 
! /* Generate initialization and destruction functions for all
!    priorities for which they are required.  */
! static void
! produce_final_constructors (void)
! {
    /* We give C linkage to static constructors and destructors.  */
    push_lang_context (lang_name_c);
  
    if (priority_info_map)
!     splay_tree_foreach (priority_info_map,
  			generate_ctor_and_dtor_functions_for_priority,
! 			/*data= */ &eof_locus);
    else
      {
        if (static_ctors)
! 	generate_ctor_or_dtor_function ( /*constructor_p= */ true,
! 					DEFAULT_INIT_PRIORITY, &eof_locus);
        if (static_dtors)
! 	generate_ctor_or_dtor_function ( /*constructor_p= */ false,
! 					DEFAULT_INIT_PRIORITY, &eof_locus);
      }
  
    /* We're done with the splay-tree now.  */
*************** finish_file ()
*** 2868,2873 ****
--- 2845,2961 ----
    /* We're done with static constructors, so we can go back to "C++"
       linkage now.  */
    pop_lang_context ();
+ }
+ 
+ /* Used by cgraphunit code; look for new functions to output and return true,
+    if any.  In case no new functions are found, output the constructors.  */
+ bool
+ collect_functions ()
+ {
+   if (collect_functions_1 ())
+     return true;
+   produce_final_constructors ();
+   return false;
+ }
+ 
+ /* Called via LANGHOOK_CALLGRAPH_LOWER_FUNCTION.  Make all calls to function explicit
+    and mark all data references by the function to be output.  */
+ void
+ lower_function (tree fn)
+ {
+   /* Assume that all referenced static variables will actually be used.
+      This is overactive and will need to be improved using AST.  */
+   walk_tree (&DECL_SAVED_TREE (fn), mark_used_decls, NULL, NULL);
+ }
+ 
+ /* This routine is called from the last rule in yyparse ().
+    Its job is to create all the code needed to initialize and
+    destroy the global aggregates.  We do the destruction
+    first, since that way we only need to reverse the decls once.  */
+ 
+ void
+ finish_file ()
+ {
+   bool reconsider = 0;
+ 
+   eof_locus = input_location;
+   at_eof = 1;
+ 
+   /* Bad parse errors.  Just forget about it.  */
+   if (! global_bindings_p () || current_class_type || decl_namespace_list)
+     return;
+ 
+   if (pch_file)
+     c_common_write_pch ();
+ 
+   /* Otherwise, GDB can get confused, because in only knows
+      about source for LINENO-1 lines.  */
+   input_line -= 1;
+ 
+   interface_unknown = 1;
+   interface_only = 0;
+ 
+   timevar_push (TV_VARCONST);
+ 
+   emit_support_tinfos ();
+   
+   if (!flag_unit_at_a_time)
+     {
+       unsigned int i;
+       bool reconsider;
+ 
+       do
+ 	{
+ 	  unsigned int fn;
+ 
+ 	  reconsider = collect_functions_1 ();
+ 	  /* Output all deferred functions.  */
+ 	  for (fn = 0; fn < deferred_fns_used; ++fn)
+ 	    {
+ 	      tree decl = VARRAY_TREE (deferred_fns, fn);
+ 
+ 	      if (cp_finalize_function (decl))
+ 		reconsider = true;
+ 	    }
+ 	}
+       while (reconsider);
+ 
+       /* All used inline functions must have a definition at this point. */
+       for (i = 0; i < deferred_fns_used; ++i)
+ 	{
+ 	  tree decl = VARRAY_TREE (deferred_fns, i);
+ 
+ 	  if (TREE_USED (decl) && DECL_DECLARED_INLINE_P (decl)
+ 	      && !(TREE_ASM_WRITTEN (decl) || DECL_SAVED_TREE (decl)
+ 		   /* An explicit instantiation can be used to specify
+ 		      that the body is in another unit. It will have
+ 		      already verified there was a definition.  */
+ 		   || DECL_EXPLICIT_INSTANTIATION (decl)))
+ 	    {
+ 	      cp_warning_at ("inline function `%D' used but never defined", decl);
+ 	      /* This symbol is effectively an "extern" declaration now.
+ 		 This is not strictly necessary, but removes a duplicate
+ 		 warning.  */
+ 	      TREE_PUBLIC (decl) = 1;
+ 	    }
+ 	}
+ 
+       produce_final_constructors ();
+     }
+   else
+     {
+       unsigned int fn;
+ 
+       /* Output all deferred functions.  */
+       for (fn = 0; fn < deferred_fns_used; ++fn)
+ 	{
+ 	  tree decl = VARRAY_TREE (deferred_fns, fn);
+ 
+ 	  cp_finalize_function (decl);
+ 	}
+       cgraph_finalize_compilation_unit ();
+       cgraph_optimize ();
+     }
  
    /* Now, issue warnings about static, but not defined, functions,
       etc., and emit debugging information.  */
*************** finish_file ()
*** 2898,2904 ****
        dump_tree_statistics ();
        dump_time_statistics ();
      }
!   input_location = locus;
  }
  
  /* T is the parse tree for an expression.  Return the expression after
--- 2986,2992 ----
        dump_tree_statistics ();
        dump_time_statistics ();
      }
!   input_location = eof_locus;
  }
  
  /* T is the parse tree for an expression.  Return the expression after
diff -Nrc3p ../cp.old/semantics.c cp/semantics.c
*** ../cp.old/semantics.c	Sun May 18 11:42:09 2003
--- cp/semantics.c	Wed Jun 18 21:27:46 2003
***************
*** 41,46 ****
--- 41,47 ----
  #include "output.h"
  #include "timevar.h"
  #include "debug.h"
+ #include "cgraph.h"
  
  /* There routines provide a modular interface to perform many parsing
     operations.  They may therefore be used during actual parsing, or
*************** emit_associated_thunks (fn)
*** 2380,2453 ****
  /* Generate RTL for FN.  */
  
  void
! expand_body (fn)
       tree fn;
  {
    location_t saved_loc;
    tree saved_function;
  
!   /* When the parser calls us after finishing the body of a template
!      function, we don't really want to expand the body.  When we're
!      processing an in-class definition of an inline function,
!      PROCESSING_TEMPLATE_DECL will no longer be set here, so we have
!      to look at the function itself.  */
!   if (processing_template_decl
!       || (DECL_LANG_SPECIFIC (fn) 
! 	  && DECL_TEMPLATE_INFO (fn)
! 	  && uses_template_parms (DECL_TI_ARGS (fn))))
!     {
!       /* Normally, collection only occurs in rest_of_compilation.  So,
! 	 if we don't collect here, we never collect junk generated
! 	 during the processing of templates until we hit a
! 	 non-template function.  */
!       ggc_collect ();
!       return;
!     }
! 
!   /* Replace AGGR_INIT_EXPRs with appropriate CALL_EXPRs.  */
!   walk_tree_without_duplicates (&DECL_SAVED_TREE (fn),
! 				simplify_aggr_init_exprs_r,
! 				NULL);
! 
!   /* If this is a constructor or destructor body, we have to clone
!      it.  */
!   if (maybe_clone_body (fn))
!     {
!       /* We don't want to process FN again, so pretend we've written
! 	 it out, even though we haven't.  */
!       TREE_ASM_WRITTEN (fn) = 1;
!       return;
!     }
! 
!   /* There's no reason to do any of the work here if we're only doing
!      semantic analysis; this code just generates RTL.  */
!   if (flag_syntax_only)
!     return;
! 
!   /* If possible, avoid generating RTL for this function.  Instead,
!      just record it as an inline function, and wait until end-of-file
!      to decide whether to write it out or not.  */
!   if (/* We have to generate RTL if it's not an inline function.  */
!       (DECL_INLINE (fn) || DECL_COMDAT (fn))
!       /* Or if we have to emit code for inline functions anyhow.  */
!       && !flag_keep_inline_functions
!       /* Or if we actually have a reference to the function.  */
!       && !DECL_NEEDED_P (fn))
!     {
!       /* Set DECL_EXTERNAL so that assemble_external will be called as
! 	 necessary.  We'll clear it again in finish_file.  */
!       if (!DECL_EXTERNAL (fn))
! 	{
! 	  DECL_NOT_REALLY_EXTERN (fn) = 1;
! 	  DECL_EXTERNAL (fn) = 1;
! 	}
!       /* Remember this function.  In finish_file we'll decide if
! 	 we actually need to write this function out.  */
!       defer_fn (fn);
!       /* Let the back-end know that this function exists.  */
!       (*debug_hooks->deferred_inline_function) (fn);
!       return;
!     }
  
    /* Compute the appropriate object-file linkage for inline
       functions.  */
--- 2381,2394 ----
  /* Generate RTL for FN.  */
  
  void
! really_expand_body (fn)
       tree fn;
  {
    location_t saved_loc;
    tree saved_function;
  
!   if (flag_unit_at_a_time && !cgraph_global_info_ready)
!     abort ();
  
    /* Compute the appropriate object-file linkage for inline
       functions.  */
*************** expand_body (fn)
*** 2519,2524 ****
--- 2460,2566 ----
    emit_associated_thunks (fn);
  }
  
+ /* Generate RTL for FN.  */
+ 
+ void
+ expand_body (fn)
+      tree fn;
+ {
+   /* When the parser calls us after finishing the body of a template
+      function, we don't really want to expand the body.  When we're
+      processing an in-class definition of an inline function,
+      PROCESSING_TEMPLATE_DECL will no longer be set here, so we have
+      to look at the function itself.  */
+   if (processing_template_decl
+       || (DECL_LANG_SPECIFIC (fn) 
+ 	  && DECL_TEMPLATE_INFO (fn)
+ 	  && uses_template_parms (DECL_TI_ARGS (fn))))
+     {
+       /* Normally, collection only occurs in rest_of_compilation.  So,
+ 	 if we don't collect here, we never collect junk generated
+ 	 during the processing of templates until we hit a
+ 	 non-template function.  */
+       ggc_collect ();
+       return;
+     }
+ 
+   /* Replace AGGR_INIT_EXPRs with appropriate CALL_EXPRs.  */
+   walk_tree_without_duplicates (&DECL_SAVED_TREE (fn),
+ 				simplify_aggr_init_exprs_r,
+ 				NULL);
+ 
+   /* If this is a constructor or destructor body, we have to clone
+      it.  */
+   if (maybe_clone_body (fn))
+     {
+       /* We don't want to process FN again, so pretend we've written
+ 	 it out, even though we haven't.  */
+       TREE_ASM_WRITTEN (fn) = 1;
+       return;
+     }
+ 
+   /* There's no reason to do any of the work here if we're only doing
+      semantic analysis; this code just generates RTL.  */
+   if (flag_syntax_only)
+     return;
+ 
+   if (flag_unit_at_a_time && cgraph_global_info_ready)
+     abort ();
+ 
+   if (flag_unit_at_a_time)
+     {
+       /* Let the back-end know that this function exists.  */
+       (*debug_hooks->deferred_inline_function) (fn);
+       if (at_eof)
+ 	{
+ 	  if (DECL_DEFER_OUTPUT (fn))
+ 	    abort ();
+ 	  cp_finalize_function (fn);
+ 	  return;
+ 	}
+       else
+ 	{
+ 	  if (!DECL_EXTERNAL (fn))
+ 	    {
+ 	      DECL_NOT_REALLY_EXTERN (fn) = 1;
+ 	      DECL_EXTERNAL (fn) = 1;
+ 	    }
+ 	  /* Remember this function.  In finish_file we'll decide if
+ 	     we actually need to write this function out.  */
+ 	  defer_fn (fn);
+ 	  return;
+ 	}
+     }
+ 
+ 
+   /* If possible, avoid generating RTL for this function.  Instead,
+      just record it as an inline function, and wait until end-of-file
+      to decide whether to write it out or not.  */
+   if (/* We have to generate RTL if it's not an inline function.  */
+       (DECL_INLINE (fn) || DECL_COMDAT (fn))
+       /* Or if we have to emit code for inline functions anyhow.  */
+       && !flag_keep_inline_functions
+       /* Or if we actually have a reference to the function.  */
+       && !DECL_NEEDED_P (fn))
+     {
+       /* Set DECL_EXTERNAL so that assemble_external will be called as
+ 	 necessary.  We'll clear it again in finish_file.  */
+       if (!DECL_EXTERNAL (fn))
+ 	{
+ 	  DECL_NOT_REALLY_EXTERN (fn) = 1;
+ 	  DECL_EXTERNAL (fn) = 1;
+ 	}
+       /* Remember this function.  In finish_file we'll decide if
+ 	 we actually need to write this function out.  */
+       defer_fn (fn);
+       /* Let the back-end know that this function exists.  */
+       (*debug_hooks->deferred_inline_function) (fn);
+       return;
+     }
+ 
+   really_expand_body (fn);
+ }
+ 
  /* Helper function for walk_tree, used by finish_function to override all
     the RETURN_STMTs and pertinent CLEANUP_STMTs for the named return
     value optimization.  */


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