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


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

Re: Unit at a time C++ again


> > > The C++ standard says which classes/functions are instantiated based on
> > > a particular input program, and our template instantiation model
> > > dictates that you instantiate all of the needed ones in every
> > > translation unit that needs them.
> > 
> > Hmm, I don't follow.  If C++ standard dicates what classes/functions are
> > instantiated and we do exactly that, how exactly do you propose to
> > change the current code?
> 
> I don't.  But I want to make sure that all of this instantiation takes
> place before any heuristic decisions about inlining are made and before
> decisions variable emission is done.
> 
> One reason for the latter is that sometimes, if you can do enough
> optimization, you no longer need to emit the variables.
> 
> If we're going to unit-at-a-time, go all the way.  Wait until the entire
> file is processed, all the templates are instantiated, etc., before
> writing out *anything*.
> 
> > I was reffering to the following code:
> > static void
> > output_vtable_inherit (tree vars)
> > {
> 
> I don't even know what that code does, I'm afraid. :-)
> 
> It's for some special stuff that tries to help the linker discard
> unneeded vtables, I think.  It should be postponed until the vtable
> actually gets emitted, via some kind of hook.

Mark,
this is the yet another unit-at-a-time patch :)  This time it works by
modifying DECL_NEEDED to work similary to flag_syntax_only as discussed
earlier. It relies on the cgraphunit and varpool to elliminate again
unneded data.

This appears to work in most cases except for static inintializers that
are reachable from the constructors so they are not elliminated.  We
probably will want to teach middle-end about the initializers later.

It passes majority of testsuite when forced to be enabled at -O2
except for:

FAIL: g++.dg/ext/pretty1.C scan-assembler top level
FAIL: g++.dg/ext/pretty2.C (test for excess errors)
Excess errors:
: undefined reference to `__PRETTY_FUNCTION__'
: undefined reference to `__PRETTY_FUNCTION__'
: undefined reference to `__PRETTY_FUNCTION__'

I am not at all sure how does the pretty function work and why I broke it :( It
appears to be latent problem when deferring functions, but I am not sure about
that.

Another problem appears to be testcase:
FAIL: g++.dg/opt/vtgc1.C scan-assembler-dem .vtable_inherit[    ]*VTT for Multivv3.*0
PASS: g++.dg/opt/vtgc1.C scan-assembler-dem .vtable_inherit[    ]*vtable for Multiss2.*vtable for Base2
PASS: g++.dg/opt/vtgc1.C scan-assembler-dem .vtable_inherit[    ]*vtable for Multivs1.*vtable for Base2
FAIL: g++.dg/opt/vtgc1.C scan-assembler-dem .vtable_inherit[    ]*VTT for Multivs1.*vtable for Base2
PASS: g++.dg/opt/vtgc1.C scan-assembler-dem .vtable_inherit[    ]*vtable for Multisv0.*vtable for Side0
FAIL: g++.dg/opt/vtgc1.C scan-assembler-dem .vtable_inherit[    ]*VTT for Multisv0.*vtable for Side0
PASS: g++.dg/opt/vtgc1.C scan-assembler-dem .vtable_inherit[    ]*vtable for Side0.*0
PASS: g++.dg/opt/vtgc1.C scan-assembler-dem .vtable_inherit[    ]*vtable for VbasedA.*0
FAIL: g++.dg/opt/vtgc1.C scan-assembler-dem .vtable_inherit[    ]*VTT for VbasedA.*0

The reason for failure appears to be the fact that virtual table is not needed
and thus it is not output, so perhaps the testcase is wrong.

I've also bootstrapped/regtested the patch.  Does it look better now?

I will try to do more testing (benchmarking) tomorrow.  I would like to
get it into form applicable to mainline (perhaps disabled by default
before we rework the inlining heuristics and such so it actually brings
measurable speedups)

Honza

Fri Jun 20 01:27:00 CEST 2003  Jan Hubicka  <jh@suse.cz>
	* cp-lang.c (LANG_HOOKS_CALLGRAPH_EXPAND_FUNCTION,
	LANG_HOOKS_PREPARE_ASSEMBLE_VARIABLE): Set macros.
	* cp-tree.h (DECL_NEEDED): In unit-at-a-time do the same trick
	as for TREE_USED.
	(prepare_assemble_variable, really_expand_body): Declare.
	* decl.c (cp_finish_decl): Flush out the declaration in
	unit-at-a-time.
	* decl2.c: Include bitmap.h and cgraph.h
	(output_vtable_inherit): Rename to ...
	(prepare_assemble_variable): ... this; check for operand being vtable.
	(finish_file): Deal with unit-at-a-time
	* rtti.c (emit_tinfo_decl): Emit all tinfos in unit-at-a-time.
	* semantics.c (really_expand_body): Break out from ...
	(expand_body): ... this one.
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	Thu Jun 19 22:17:17 2003
*************** static bool cp_var_mod_type_p (tree);
*** 145,150 ****
--- 145,156 ----
  #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_PREPARE_ASSEMBLE_VARIABLE 
+ #define LANG_HOOKS_PREPARE_ASSEMBLE_VARIABLE prepare_assemble_variable
+ 
  #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	Thu Jun 19 22:17:51 2003
*************** struct lang_decl GTY(())
*** 1745,1751 ****
    ((at_eof && TREE_PUBLIC (DECL) && !DECL_COMDAT (DECL))	\
     || (DECL_ASSEMBLER_NAME_SET_P (DECL)				\
         && TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (DECL)))	\
!    || (flag_syntax_only && TREE_USED (DECL)))
  
  /* For a FUNCTION_DECL or a VAR_DECL, the language linkage for the
     declaration.  Some entities (like a member function in a local
--- 1745,1751 ----
    ((at_eof && TREE_PUBLIC (DECL) && !DECL_COMDAT (DECL))	\
     || (DECL_ASSEMBLER_NAME_SET_P (DECL)				\
         && TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (DECL)))	\
!    || (((flag_syntax_only || flag_unit_at_a_time) && TREE_USED (DECL))))
  
  /* For a FUNCTION_DECL or a VAR_DECL, the language linkage for the
     declaration.  Some entities (like a member function in a local
*************** 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 prepare_assemble_variable (tree);
  
  extern void cp_error_at		(const char *msgid, ...);
  extern void cp_warning_at	(const char *msgid, ...);
*************** extern void clear_out_block             
*** 4146,4151 ****
--- 4147,4153 ----
  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);
  
  /* in tree.c */
  extern void lang_check_failed			(const char *, int,
diff -Nrc3p ../cp.old/decl.c cp/decl.c
*** ../cp.old/decl.c	Sun Jun  8 11:42:41 2003
--- cp/decl.c	Fri Jun 20 01:04:35 2003
*************** cp_finish_decl (tree decl, tree init, tr
*** 8204,8209 ****
--- 8204,8214 ----
  	  if (TREE_STATIC (decl))
  	    expand_static_init (decl, init);
  	}
+       if (TREE_CODE (decl) == VAR_DECL
+ 	  && (TREE_STATIC (decl) || DECL_EXTERNAL (decl))
+ 	  && !DECL_DEFER_OUTPUT (decl)
+ 	  && flag_unit_at_a_time)
+ 	rest_of_decl_compilation (decl, NULL, 0, 0);
      finish_end0:
  
        /* Undo call to `pushclass' that was done in `start_decl'
diff -Nrc3p ../cp.old/decl2.c cp/decl2.c
*** ../cp.old/decl2.c	Thu May 22 01:27:50 2003
--- cp/decl2.c	Thu Jun 19 23:41:28 2003
*************** Boston, MA 02111-1307, USA.  */
*** 46,51 ****
--- 46,53 ----
  #include "cpplib.h"
  #include "target.h"
  #include "c-common.h"
+ #include "cgraph.h"
+ #include "bitmap.h"
  extern cpp_reader *parse_in;
  
  /* This structure contains information about the initializations
*************** static void add_using_namespace (tree, t
*** 66,72 ****
  static cxx_binding *ambiguous_decl (tree, cxx_binding *, cxx_binding *, int);
  static tree build_anon_union_vars (tree);
  static bool acceptable_java_type (tree);
- static void output_vtable_inherit (tree);
  static tree start_objects (int, int);
  static void finish_objects (int, int, tree);
  static tree merge_functions (tree, tree);
--- 68,73 ----
*************** import_export_class (tree ctype)
*** 1604,1614 ****
  /* We need to describe to the assembler the relationship between
     a vtable and the vtable of the parent class.  */
  
! static void
! output_vtable_inherit (tree vars)
  {
    tree parent;
    rtx child_rtx, parent_rtx;
  
    child_rtx = XEXP (DECL_RTL (vars), 0);	  /* strip the mem ref  */
  
--- 1605,1628 ----
  /* We need to describe to the assembler the relationship between
     a vtable and the vtable of the parent class.  */
  
! void
! prepare_assemble_variable (tree vars)
  {
    tree parent;
    rtx child_rtx, parent_rtx;
+   const char *type_name;
+ 
+   /* Recognize virtual tables.  */
+   if (!flag_vtable_gc
+       || TREE_CODE (TREE_TYPE (vars)) != ARRAY_TYPE
+       || TREE_TYPE (TREE_TYPE (vars)) != TREE_TYPE (vtbl_type_node))
+     return;
+ 
+   type_name = IDENTIFIER_POINTER
+ 	        (DECL_NAME (TYPE_NAME (TREE_TYPE (TREE_TYPE (vars)))));
+ 
+   if (strcmp (VTBL_PTR_TYPE, type_name))
+     return;
  
    child_rtx = XEXP (DECL_RTL (vars), 0);	  /* strip the mem ref  */
  
*************** maybe_emit_vtables (tree ctype)
*** 1706,1714 ****
  
        rest_of_decl_compilation (vtbl, NULL, 1, 1);
  
-       if (flag_vtable_gc)
- 	output_vtable_inherit (vtbl);
- 
        /* Because we're only doing syntax-checking, we'll never end up
  	 actually marking the variable as written.  */
        if (flag_syntax_only)
--- 1720,1725 ----
*************** finish_file ()
*** 2549,2554 ****
--- 2560,2566 ----
    size_t i;
    location_t locus;
    unsigned ssdf_count = 0;
+   bitmap_head fn_finalized;
  
    locus = input_location;
    at_eof = 1;
*************** finish_file ()
*** 2557,2562 ****
--- 2569,2576 ----
    if (! global_bindings_p () || current_class_type || decl_namespace_list)
      return;
  
+   bitmap_initialize (&fn_finalized, 0);
+ 
    if (pch_file)
      c_common_write_pch ();
  
*************** finish_file ()
*** 2746,2752 ****
  	     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);
--- 2760,2766 ----
  	     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) || bitmap_bit_p (&fn_finalized, i))
  	    continue;
  
  	  import_export_decl (decl);
*************** finish_file ()
*** 2786,2791 ****
--- 2800,2806 ----
  	      expand_body (decl);
  	      /* Undo the damage done by finish_function.  */
  	      DECL_EXTERNAL (decl) = 0;
+ 	      DECL_DEFER_OUTPUT (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
*************** finish_file ()
*** 2793,2798 ****
--- 2808,2814 ----
  	      if (flag_syntax_only)
  		TREE_ASM_WRITTEN (decl) = 1;
  	      reconsider = true;
+ 	      bitmap_set_bit (&fn_finalized, i);
  	    }
  	}
  
*************** finish_file ()
*** 2869,2874 ****
--- 2885,2896 ----
       linkage now.  */
    pop_lang_context ();
  
+   if (flag_unit_at_a_time)
+     {
+       cgraph_finalize_compilation_unit ();
+       cgraph_optimize ();
+     }
+ 
    /* Now, issue warnings about static, but not defined, functions,
       etc., and emit debugging information.  */
    walk_namespaces (wrapup_globals_for_namespace, /*data=*/&reconsider);
*************** finish_file ()
*** 2899,2904 ****
--- 2921,2928 ----
        dump_time_statistics ();
      }
    input_location = locus;
+ 
+   bitmap_clear (&fn_finalized);
  }
  
  /* T is the parse tree for an expression.  Return the expression after
diff -Nrc3p ../cp.old/rtti.c cp/rtti.c
*** ../cp.old/rtti.c	Sun May 18 00:21:35 2003
--- cp/rtti.c	Fri Jun 20 01:10:58 2003
*************** emit_tinfo_decl (tree decl)
*** 1447,1453 ****
    my_friendly_assert (unemitted_tinfo_decl_p (decl), 20030307); 
    
    import_export_tinfo (decl, type, in_library);
!   if (DECL_REALLY_EXTERN (decl) || !DECL_NEEDED_P (decl))
      return false;
  
    if (!doing_runtime && in_library)
--- 1449,1455 ----
    my_friendly_assert (unemitted_tinfo_decl_p (decl), 20030307); 
    
    import_export_tinfo (decl, type, in_library);
!   if (DECL_REALLY_EXTERN (decl) || (!DECL_NEEDED_P (decl) && !flag_unit_at_a_time))
      return false;
  
    if (!doing_runtime && in_library)
diff -Nrc3p ../cp.old/semantics.c cp/semantics.c
*** ../cp.old/semantics.c	Sun May 18 11:42:09 2003
--- cp/semantics.c	Thu Jun 19 21:22:27 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,2561 ----
    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 && !cgraph_global_info_ready)
+     {
+       if (at_eof)
+ 	cgraph_finalize_function (fn, DECL_SAVED_TREE (fn));
+       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);
+ 	  /* Let the back-end know that this function exists.  */
+ 	  (*debug_hooks->deferred_inline_function) (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]