This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[C++ patch] final? unit-at-a-time patch
- From: Jan Hubicka <hubicka at ucw dot cz>
- To: gcc-patches at gcc dot gnu dot org, mark at codesourcery dot com
- Date: Sun, 22 Jun 2003 00:31:23 +0200
- Subject: [C++ patch] final? unit-at-a-time patch
Hi,
this patch finally implements unit-at-a-time mode in C++. I
regtested/bootstrapped it in both unit-at-a-time and non-unit-at-a-time mode.
In unit-at-a-time mode it still fails with testcase pretty1.c/pretty2.c, but I
can trigger same failure by deferring the functions via inline keyword, so it
is latent bug I will try to fix shortly too.
The libstdc++ is about 11K smaller than without unit-at-a-time and compiles in
the same time. I got similar results on eon benchmark (2% code size decrease)
and few other C++ programs I compiled so I think it can be enabled by default
at -O3 level unless we hit some inexpected problems. (I still expect
some but I will try to take care to fix these)
OK for mainline?
Sun Jun 22 02:21:27 CEST 2003 Jan Hubicka <jh@suse.cz>
* cp-tree.h (DECL_NEEDED_P): Support unit-at-a-time
(expand_body): Declare.
* decl2.c: Include cgraph.h and varpool.h
(comdat_linkage): Make explicit instantations as needed.
(finish_file): Do unit-at-a-time.
* semantics.c: Include cgraph.h
(really_expand_body): Break out from ...
(expand_body): ... here; deal with unit-at-a-time.
diff -Nrc3p gcc.tmp/cp/cp-tree.h gcc/cp/cp-tree.h
*** gcc.tmp/cp/cp-tree.h Sun Jun 22 01:50:33 2003
--- gcc/cp/cp-tree.h Fri Jun 20 23:09:59 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 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 gcc.tmp/cp/decl2.c gcc/cp/decl2.c
*** gcc.tmp/cp/decl2.c Sun Jun 22 01:58:32 2003
--- gcc/cp/decl2.c Sat Jun 21 23:28:01 2003
*************** Boston, MA 02111-1307, USA. */
*** 46,51 ****
--- 46,53 ----
#include "cpplib.h"
#include "target.h"
#include "c-common.h"
+ #include "cgraph.h"
+ #include "varpool.h"
extern cpp_reader *parse_in;
/* This structure contains information about the initializations
*************** mark_vtable_entries (tree decl)
*** 1422,1427 ****
--- 1425,1432 ----
void
comdat_linkage (tree decl)
{
+ bool needed = TREE_PUBLIC (decl) && !DECL_COMDAT (decl);
+
if (flag_weak)
make_decl_one_only (decl);
else if (TREE_CODE (decl) == FUNCTION_DECL
*************** comdat_linkage (tree decl)
*** 1466,1471 ****
--- 1471,1485 ----
if (DECL_LANG_SPECIFIC (decl))
DECL_COMDAT (decl) = 1;
+
+ /* Explicit instantiation: inform middle end that the decl is needed
+ even tought it is COMDAT.
+ ??? This would be cleaner if we had a flag expressing that decl
+ is weak, but it is not COMDAT in a sense that it can be emit
+ when not needed. */
+ if (flag_unit_at_a_time && at_eof && needed
+ && TREE_CODE (decl) == VAR_DECL)
+ varpool_mark_needed_node (varpool_node (decl));
}
/* For win32 we also want to put explicit instantiations in
*************** finish_file ()
*** 2873,2878 ****
--- 2914,2925 ----
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);
diff -Nrc3p gcc.tmp/cp/semantics.c gcc/cp/semantics.c
*** gcc.tmp/cp/semantics.c Sun Jun 22 01:50:33 2003
--- gcc/cp/semantics.c Sat Jun 21 21:47:06 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,2567 ----
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)
+ {
+ /* Compute the appropriate object-file linkage for inline
+ functions. */
+ if (DECL_DECLARED_INLINE_P (fn))
+ import_export_decl (fn);
+ 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. */
*** gcc.tmp/cp/cp-lang.c Sun Jun 22 01:50:33 2003
--- gcc/cp/cp-lang.c Fri Jun 20 23:09:59 2003
*************** static bool cp_var_mod_type_p (tree);
*** 145,150 ****
--- 145,153 ----
#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_MAKE_TYPE
#define LANG_HOOKS_MAKE_TYPE cxx_make_type
#undef LANG_HOOKS_TYPE_FOR_MODE
*** gcc.tmp/cp/Make-lang.in Sun Jun 22 01:50:31 2003
--- gcc/cp/Make-lang.in Sun Jun 22 02:32:16 2003
*************** cp/decl.o: cp/decl.c $(CXX_TREE_H) $(TM_
*** 242,248 ****
cp/operators.def $(TM_P_H) tree-inline.h diagnostic.h c-pragma.h \
debug.h gt-cp-decl.h gtype-cp.h timevar.h
cp/decl2.o: cp/decl2.c $(CXX_TREE_H) $(TM_H) flags.h cp/lex.h cp/decl.h $(EXPR_H) \
! output.h except.h toplev.h $(RTL_H) c-common.h gt-cp-decl2.h
cp/typeck2.o: cp/typeck2.c $(CXX_TREE_H) $(TM_H) flags.h toplev.h output.h $(TM_P_H) \
diagnostic.h
cp/typeck.o: cp/typeck.c $(CXX_TREE_H) $(TM_H) flags.h $(RTL_H) $(EXPR_H) toplev.h \
--- 242,248 ----
cp/operators.def $(TM_P_H) tree-inline.h diagnostic.h c-pragma.h \
debug.h gt-cp-decl.h gtype-cp.h timevar.h
cp/decl2.o: cp/decl2.c $(CXX_TREE_H) $(TM_H) flags.h cp/lex.h cp/decl.h $(EXPR_H) \
! output.h except.h toplev.h $(RTL_H) c-common.h gt-cp-decl2.h varpool.h cgraph.h
cp/typeck2.o: cp/typeck2.c $(CXX_TREE_H) $(TM_H) flags.h toplev.h output.h $(TM_P_H) \
diagnostic.h
cp/typeck.o: cp/typeck.c $(CXX_TREE_H) $(TM_H) flags.h $(RTL_H) $(EXPR_H) toplev.h \
*************** cp/repo.o: cp/repo.c $(CXX_TREE_H) $(TM_
*** 273,279 ****
gt-cp-repo.h
cp/semantics.o: cp/semantics.c $(CXX_TREE_H) $(TM_H) cp/lex.h except.h toplev.h \
flags.h debug.h output.h $(RTL_H) $(TIMEVAR_H) $(EXPR_H) \
! tree-inline.h
cp/dump.o: cp/dump.c $(CXX_TREE_H) $(TM_H) tree-dump.h
cp/optimize.o: cp/optimize.c $(CXX_TREE_H) $(TM_H) rtl.h integrate.h insn-config.h \
input.h $(PARAMS_H) debug.h tree-inline.h
--- 273,279 ----
gt-cp-repo.h
cp/semantics.o: cp/semantics.c $(CXX_TREE_H) $(TM_H) cp/lex.h except.h toplev.h \
flags.h debug.h output.h $(RTL_H) $(TIMEVAR_H) $(EXPR_H) \
! tree-inline.h cgraph.h
cp/dump.o: cp/dump.c $(CXX_TREE_H) $(TM_H) tree-dump.h
cp/optimize.o: cp/optimize.c $(CXX_TREE_H) $(TM_H) rtl.h integrate.h insn-config.h \
input.h $(PARAMS_H) debug.h tree-inline.h