diff options
author | hubicka <hubicka@138bc75d-0d04-0410-961f-82ee72b054a4> | 2003-02-12 21:48:59 +0000 |
---|---|---|
committer | hubicka <hubicka@138bc75d-0d04-0410-961f-82ee72b054a4> | 2003-02-12 21:48:59 +0000 |
commit | 833eb72438b0a66929179b0402b48da4632d6af5 (patch) | |
tree | 145028e0c53cc6c7857dc24db0335535b3ff24ab /gcc | |
parent | 919d789fe70b97397da681856f8269a4e7967b6a (diff) | |
download | gcc-833eb72438b0a66929179b0402b48da4632d6af5.tar.gz |
* Makefile.in (CRTSTUFF_CFLAGS): Add -fno-unit-at-a-time
(OBJS): Add callgraph.o
(callgraph.o): New.
* c-decl.c (expand_body_1): Break out from ...
(expand_body): This one; change calling convention
(finish_function): Move some of expand_body logic here.
(c_expand_deferred_function): Update call of expand_body
(c_expand_stmt): Use c_expand_body_1.
* c-lang.c (LANG_HOOKS_CALLGRAPH_EXPAND_FUNCTION): Define.
* c-objc-commin.c (c_objc_common_finish_file): Use callgraph code.
* c-tree.h (c_expand_body): Declare.
* callgraph.c: New file.
* flags.h (flag_unit_at_a_time): Declare.
* langhooks.h (LANG_HOOKS_CALLGRAPH_LOWER_FUNCTION,
LANG_HOOKS_CALLGRAPH_EXPAND_FUNCTION,
LANG_HOOKS_CALLGRAPH_INITIALIZER): New macros.
* langhooks.h (struct lang_hooks_for_callgraph): New.
(struct lang_hooks): Add callgraph field.
* toplev.c (flag_unit_at_a_time): New.
(lang_independent_options): Add flag_unit_at_a_time.
(process_options): Disable unit-at-a-time mode for frontends not
supporting callgraph.
* tree-inline.c (typedef struct inline_data): Add "decl"
(expand_call_inline): Update callgraph.
(optimize_inline_calls): Set id.decl.
* tree.h (cgraph_finalize_function, cgraph_finalize_compilation_unit,
cgraph_create_edges, dump_cgraph, cgraph_optimize, cgraph_remove_call
cgraph_calls_p): Declare.
* invoke.texi (-funit-at-a-time): Document
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@62789 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/ChangeLog | 32 | ||||
-rw-r--r-- | gcc/Makefile.in | 6 | ||||
-rw-r--r-- | gcc/c-decl.c | 118 | ||||
-rw-r--r-- | gcc/c-lang.c | 3 | ||||
-rw-r--r-- | gcc/c-objc-common.c | 8 | ||||
-rw-r--r-- | gcc/c-tree.h | 1 | ||||
-rw-r--r-- | gcc/cgraph.c | 563 | ||||
-rw-r--r-- | gcc/doc/invoke.texi | 7 | ||||
-rw-r--r-- | gcc/flags.h | 2 | ||||
-rw-r--r-- | gcc/langhooks-def.h | 9 | ||||
-rw-r--r-- | gcc/langhooks.h | 11 | ||||
-rw-r--r-- | gcc/toplev.c | 11 | ||||
-rw-r--r-- | gcc/tree-inline.c | 10 | ||||
-rw-r--r-- | gcc/tree.h | 9 |
14 files changed, 742 insertions, 48 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index d2cd82a3ea1..9ee4179a54b 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,35 @@ +Wed Feb 12 22:47:18 CET 2003 Jan Hubicka <jh@suse.cz> + + * Makefile.in (CRTSTUFF_CFLAGS): Add -fno-unit-at-a-time + (OBJS): Add callgraph.o + (callgraph.o): New. + * c-decl.c (expand_body_1): Break out from ... + (expand_body): This one; change calling convention + (finish_function): Move some of expand_body logic here. + (c_expand_deferred_function): Update call of expand_body + (c_expand_stmt): Use c_expand_body_1. + * c-lang.c (LANG_HOOKS_CALLGRAPH_EXPAND_FUNCTION): Define. + * c-objc-commin.c (c_objc_common_finish_file): Use callgraph code. + * c-tree.h (c_expand_body): Declare. + * callgraph.c: New file. + * flags.h (flag_unit_at_a_time): Declare. + * langhooks.h (LANG_HOOKS_CALLGRAPH_LOWER_FUNCTION, + LANG_HOOKS_CALLGRAPH_EXPAND_FUNCTION, + LANG_HOOKS_CALLGRAPH_INITIALIZER): New macros. + * langhooks.h (struct lang_hooks_for_callgraph): New. + (struct lang_hooks): Add callgraph field. + * toplev.c (flag_unit_at_a_time): New. + (lang_independent_options): Add flag_unit_at_a_time. + (process_options): Disable unit-at-a-time mode for frontends not + supporting callgraph. + * tree-inline.c (typedef struct inline_data): Add "decl" + (expand_call_inline): Update callgraph. + (optimize_inline_calls): Set id.decl. + * tree.h (cgraph_finalize_function, cgraph_finalize_compilation_unit, + cgraph_create_edges, dump_cgraph, cgraph_optimize, cgraph_remove_call + cgraph_calls_p): Declare. + * invoke.texi (-funit-at-a-time): Document + 2003-02-12 Aldy Hernandez <aldyh@redhat.com> * config/rs6000/spe.h: Fix misc formatting. diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 8aaa6991e2a..d528378214b 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -416,7 +416,7 @@ TARGET_LIBGCC2_CFLAGS = # Options to use when compiling crtbegin/end. CRTSTUFF_CFLAGS = -O2 $(GCC_CFLAGS) $(INCLUDES) $(MULTILIB_CFLAGS) -g0 \ -finhibit-size-directive -fno-inline-functions -fno-exceptions \ - -fno-zero-initialized-in-bss + -fno-zero-initialized-in-bss -fno-unit-at-a-time # Additional sources to handle exceptions; overridden by targets as needed. LIB2ADDEH = $(srcdir)/unwind-dw2.c $(srcdir)/unwind-dw2-fde.c \ @@ -785,7 +785,7 @@ OBJS = alias.o bb-reorder.o bitmap.o builtins.o caller-save.o calls.o \ sibcall.o simplify-rtx.o sreal.o ssa.o ssa-ccp.o ssa-dce.o stmt.o \ stor-layout.o stringpool.o timevar.o toplev.o tracer.o tree.o tree-dump.o \ tree-inline.o unroll.o varasm.o varray.o version.o vmsdbgout.o xcoffout.o \ - alloc-pool.o et-forest.o \ + alloc-pool.o et-forest.o cgraph.o \ $(GGC) $(out_object_file) $(EXTRA_OBJS) $(host_hook_obj) BACKEND = main.o libbackend.a @@ -1530,6 +1530,8 @@ jump.o : jump.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) flags.h \ simplify-rtx.o : simplify-rtx.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ $(REGS_H) hard-reg-set.h flags.h real.h insn-config.h $(RECOG_H) $(EXPR_H) toplev.h \ output.h function.h $(GGC_H) $(OBSTACK_H) $(TM_P_H) $(TREE_H) $(TARGET_H) +cgraph.o : cgraph.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(TREE_H) \ + langhooks.h tree-inline.h toplev.h flags.h ggc.h $(TARGET_H) cselib.o : cselib.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) $(REGS_H) \ hard-reg-set.h flags.h real.h insn-config.h $(RECOG_H) $(EXPR_H) toplev.h \ output.h function.h cselib.h $(GGC_H) $(TM_P_H) gt-cselib.h diff --git a/gcc/c-decl.c b/gcc/c-decl.c index 01a35874ecb..a0c1d56c131 100644 --- a/gcc/c-decl.c +++ b/gcc/c-decl.c @@ -282,7 +282,7 @@ static tree grokdeclarator PARAMS ((tree, tree, enum decl_context, static tree grokparms PARAMS ((tree, int)); static void layout_array_type PARAMS ((tree)); static tree c_make_fname_decl PARAMS ((tree, int)); -static void c_expand_body PARAMS ((tree, int, int)); +static void c_expand_body_1 PARAMS ((tree, int)); static void warn_if_shadowing PARAMS ((tree, tree)); static bool flexible_array_type_p PARAMS ((tree)); @@ -6412,10 +6412,62 @@ finish_function (nested, can_defer_p) free_after_compilation (cfun); cfun = NULL; + if (flag_unit_at_a_time) + { + cgraph_finalize_function (fndecl, DECL_SAVED_TREE (fndecl)); + current_function_decl = NULL; + return; + } + if (! nested) { - /* Generate RTL for the body of this function. */ - c_expand_body (fndecl, nested, can_defer_p); + /* Function is parsed. + Generate RTL for the body of this function or defer + it for later expansion. */ + int uninlinable = 1; + + /* 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) + { + current_function_decl = NULL; + DECL_SAVED_TREE (fndecl) = NULL_TREE; + return; + } + + if (flag_inline_trees) + { + /* First, cache whether the current function is inlinable. Some + predicates depend on cfun and current_function_decl to + function completely. */ + timevar_push (TV_INTEGRATION); + uninlinable = ! tree_inlinable_function_p (fndecl); + + if (! uninlinable && can_defer_p + /* Save function tree for inlining. Should return 0 if the + language does not support function deferring or the + function could not be deferred. */ + && defer_fn (fndecl)) + { + /* Let the back-end know that this function exists. */ + (*debug_hooks->deferred_inline_function) (fndecl); + timevar_pop (TV_INTEGRATION); + current_function_decl = NULL; + return; + } + + /* Then, inline any functions called in it. */ + optimize_inline_calls (fndecl); + timevar_pop (TV_INTEGRATION); + } + + c_expand_body (fndecl); + + if (uninlinable) + { + /* Allow the body of the function to be garbage collected. */ + DECL_SAVED_TREE (fndecl) = NULL_TREE; + } /* Let the error reporting routines know that we're outside a function. For a nested function, this value is used in @@ -6434,7 +6486,13 @@ c_expand_deferred_function (fndecl) function was deferred, e.g. in duplicate_decls. */ if (DECL_INLINE (fndecl) && DECL_RESULT (fndecl)) { - c_expand_body (fndecl, 0, 0); + if (flag_inline_trees) + { + timevar_push (TV_INTEGRATION); + optimize_inline_calls (fndecl); + timevar_pop (TV_INTEGRATION); + } + c_expand_body (fndecl); current_function_decl = NULL; } } @@ -6445,42 +6503,10 @@ c_expand_deferred_function (fndecl) generation of RTL. */ static void -c_expand_body (fndecl, nested_p, can_defer_p) +c_expand_body_1 (fndecl, nested_p) tree fndecl; - int nested_p, can_defer_p; + int nested_p; { - int uninlinable = 1; - - /* 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_inline_trees) - { - /* First, cache whether the current function is inlinable. Some - predicates depend on cfun and current_function_decl to - function completely. */ - timevar_push (TV_INTEGRATION); - uninlinable = ! tree_inlinable_function_p (fndecl); - - if (! uninlinable && can_defer_p - /* Save function tree for inlining. Should return 0 if the - language does not support function deferring or the - function could not be deferred. */ - && defer_fn (fndecl)) - { - /* Let the back-end know that this function exists. */ - (*debug_hooks->deferred_inline_function) (fndecl); - timevar_pop (TV_INTEGRATION); - return; - } - - /* Then, inline any functions called in it. */ - optimize_inline_calls (fndecl); - timevar_pop (TV_INTEGRATION); - } - timevar_push (TV_EXPAND); if (nested_p) @@ -6519,11 +6545,6 @@ c_expand_body (fndecl, nested_p, can_defer_p) /* Generate the RTL for this function. */ expand_stmt (DECL_SAVED_TREE (fndecl)); - if (uninlinable) - { - /* Allow the body of the function to be garbage collected. */ - DECL_SAVED_TREE (fndecl) = NULL_TREE; - } /* We hard-wired immediate_size_expand to zero above. expand_function_end will decrement this variable. So, we set the @@ -6621,6 +6642,15 @@ c_expand_body (fndecl, nested_p, can_defer_p) pop_function_context (); timevar_pop (TV_EXPAND); } + +/* Like c_expand_body_1 but only for unnested functions. */ + +void +c_expand_body (fndecl) + tree fndecl; +{ + c_expand_body_1 (fndecl, 0); +} /* Check the declarations given in a for-loop for satisfying the C99 constraints. */ @@ -6854,7 +6884,7 @@ c_expand_decl_stmt (t) if (TREE_CODE (decl) == FUNCTION_DECL && DECL_CONTEXT (decl) == current_function_decl && DECL_SAVED_TREE (decl)) - c_expand_body (decl, /*nested_p=*/1, /*can_defer_p=*/0); + c_expand_body_1 (decl, 1); } /* Return the IDENTIFIER_GLOBAL_VALUE of T, for use in common code, since diff --git a/gcc/c-lang.c b/gcc/c-lang.c index 03488976470..116561e9d07 100644 --- a/gcc/c-lang.c +++ b/gcc/c-lang.c @@ -99,6 +99,9 @@ static void c_init_options PARAMS ((void)); #undef LANG_HOOKS_TREE_DUMP_DUMP_TREE_FN #define LANG_HOOKS_TREE_DUMP_DUMP_TREE_FN c_dump_tree +#undef LANG_HOOKS_CALLGRAPH_EXPAND_FUNCTION +#define LANG_HOOKS_CALLGRAPH_EXPAND_FUNCTION c_expand_body + #undef LANG_HOOKS_TYPE_FOR_MODE #define LANG_HOOKS_TYPE_FOR_MODE c_common_type_for_mode #undef LANG_HOOKS_TYPE_FOR_SIZE diff --git a/gcc/c-objc-common.c b/gcc/c-objc-common.c index c91e635b410..a84ddc8fd70 100644 --- a/gcc/c-objc-common.c +++ b/gcc/c-objc-common.c @@ -361,7 +361,13 @@ c_objc_common_finish_file () if (pch_file) c_common_write_pch (); - expand_deferred_fns (); + if (flag_unit_at_a_time) + { + cgraph_finalize_compilation_unit (); + cgraph_optimize (); + } + else + expand_deferred_fns (); if (static_ctors) { diff --git a/gcc/c-tree.h b/gcc/c-tree.h index 159c235224e..c69838692ac 100644 --- a/gcc/c-tree.h +++ b/gcc/c-tree.h @@ -172,6 +172,7 @@ extern void finish_file PARAMS ((void)); extern int objc_comptypes PARAMS ((tree, tree, int)); extern tree objc_message_selector PARAMS ((void)); extern tree lookup_objc_ivar PARAMS ((tree)); +extern void c_expand_body PARAMS ((tree)); /* in c-parse.in */ diff --git a/gcc/cgraph.c b/gcc/cgraph.c new file mode 100644 index 00000000000..a199fe6617f --- /dev/null +++ b/gcc/cgraph.c @@ -0,0 +1,563 @@ +/* Callgraph handling code. + Copyright (C) 2003 Free Software Foundation, Inc. + Contributed by Jan Hubicka + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA +02111-1307, USA. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "tree.h" +#include "tree-inline.h" +#include "langhooks.h" +#include "hashtab.h" +#include "toplev.h" +#include "flags.h" +#include "ggc.h" +#include "debug.h" +#include "target.h" + +/* The cgraph data strutcture. + Each function decl has assigned cgraph_node listing calees and callers. */ + +struct cgraph_node +{ + tree decl; + struct cgraph_edge *callees; + struct cgraph_edge *callers; + struct cgraph_node *next; + /* For nested functions points to function the node is nested in. */ + struct cgraph_node *origin; + /* Points to first nested function, if any. */ + struct cgraph_node *nested; + /* Pointer to the next function with same origin, if any. */ + struct cgraph_node *next_nested; + void *aux; + + /* Set when function must be output - it is externally visible + or it's address is taken. */ + bool needed; + /* Set when function is reachable by call from other function + that is eighter reachable or needed. */ + bool reachable; + /* Set when the frontend has been asked to lower representation of this + function into trees. Callees lists are not available when lowered + is not set. */ + bool lowered; + /* Set when function is scheduled to be assembled. */ + bool output; +}; + +struct cgraph_edge +{ + struct cgraph_node *caller, *callee; + struct cgraph_edge *next_caller; + struct cgraph_edge *next_callee; +}; + +/* Hash table used to convert declarations into nodes. */ +static htab_t cgraph_hash = 0; + +/* The linked list of cgraph nodes. */ +static struct cgraph_node *cgraph_nodes; + +/* Number of nodes in existence. */ +static int cgraph_n_nodes; + +static struct cgraph_node *cgraph_node PARAMS ((tree decl)); +static struct cgraph_edge *create_edge PARAMS ((struct cgraph_node *, + struct cgraph_node *)); +static void remove_edge PARAMS ((struct cgraph_node *, struct cgraph_node *)); +static struct cgraph_edge *record_call PARAMS ((tree, tree)); +static tree record_call_1 PARAMS ((tree *, int *, void *)); +static hashval_t hash_node PARAMS ((const PTR)); +static int eq_node PARAMS ((const PTR, const PTR)); +static struct cgraph_node *cgraph_node PARAMS ((tree)); +static void cgraph_expand_functions PARAMS ((void)); +static void cgraph_mark_functions_to_output PARAMS ((void)); +static void cgraph_expand_function PARAMS ((struct cgraph_node *)); +static void cgraph_mark_needed_node PARAMS ((struct cgraph_node *, int)); + +/* Returns a hash code for P. */ + +static hashval_t +hash_node (p) + const PTR p; +{ + return (hashval_t) + htab_hash_pointer (DECL_ASSEMBLER_NAME + (((struct cgraph_node *) p)->decl)); +} + +/* Returns non-zero if P1 and P2 are equal. */ + +static int +eq_node (p1, p2) + const PTR p1; + const PTR p2; +{ + return ((DECL_ASSEMBLER_NAME (((struct cgraph_node *) p1)->decl)) == + DECL_ASSEMBLER_NAME ((tree) p2)); +} + +/* Return cgraph node assigned to DECL. Create new one when needed. */ +static struct cgraph_node * +cgraph_node (decl) + tree decl; +{ + struct cgraph_node *node; + struct cgraph_node **slot; + + if (!cgraph_hash) + cgraph_hash = htab_create (10, hash_node, eq_node, NULL); + + slot = + (struct cgraph_node **) htab_find_slot_with_hash (cgraph_hash, decl, + htab_hash_pointer + (DECL_ASSEMBLER_NAME + (decl)), 1); + if (*slot) + return *slot; + node = xcalloc (sizeof (*node), 1); + node->decl = decl; + node->next = cgraph_nodes; + cgraph_nodes = node; + cgraph_n_nodes++; + *slot = node; + if (DECL_CONTEXT (decl)) + { + node->origin = cgraph_node (DECL_CONTEXT (decl)); + node->next_nested = node->origin->nested; + node->origin->nested = node; + } + return node; +} + +/* Create edge from CALLER to CALLEE in the cgraph. */ + +static struct cgraph_edge * +create_edge (caller, callee) + struct cgraph_node *caller, *callee; +{ + struct cgraph_edge *edge = xmalloc (sizeof (struct cgraph_edge)); + + edge->caller = caller; + edge->callee = callee; + edge->next_caller = callee->callers; + edge->next_callee = caller->callees; + caller->callees = edge; + callee->callers = edge; + return edge; +} + +/* Remove the edge from CALLER to CALLEE in the cgraph. */ + +static void +remove_edge (caller, callee) + struct cgraph_node *caller, *callee; +{ + struct cgraph_edge **edge, **edge2; + + for (edge = &callee->callers; *edge && (*edge)->caller != caller; + edge = &((*edge)->next_caller)) + continue; + if (!*edge) + abort (); + *edge = (*edge)->next_caller; + for (edge2 = &caller->callees; *edge2 && (*edge2)->callee != callee; + edge2 = &(*edge2)->next_callee) + continue; + if (!*edge2) + abort (); + *edge2 = (*edge2)->next_callee; +} + +/* Record call from CALLER to CALLEE */ + +static struct cgraph_edge * +record_call (caller, callee) + tree caller, callee; +{ + return create_edge (cgraph_node (caller), cgraph_node (callee)); +} + +void +cgraph_remove_call (caller, callee) + tree caller, callee; +{ + remove_edge (cgraph_node (caller), cgraph_node (callee)); +} + +/* Return true when CALLER_DECL calls CALLEE_DECL. */ + +bool +cgraph_calls_p (caller_decl, callee_decl) + tree caller_decl, callee_decl; +{ + struct cgraph_node *caller = cgraph_node (caller_decl); + struct cgraph_node *callee = cgraph_node (callee_decl); + struct cgraph_edge *edge; + + for (edge = callee->callers; edge && (edge)->caller != caller; + edge = (edge->next_caller)) + continue; + return edge != NULL; +} + +/* Walk tree and record all calls. Called via walk_tree. */ +static tree +record_call_1 (tp, walk_subtrees, data) + tree *tp; + int *walk_subtrees; + void *data; +{ + /* Record dereferences to the functions. This makes the functions + reachable unconditionally. */ + if (TREE_CODE (*tp) == ADDR_EXPR) + { + tree decl = TREE_OPERAND (*tp, 0); + if (TREE_CODE (decl) == FUNCTION_DECL) + cgraph_mark_needed_node (cgraph_node (decl), 1); + } + else if (TREE_CODE (*tp) == CALL_EXPR) + { + tree decl = TREE_OPERAND (*tp, 0); + if (TREE_CODE (decl) == ADDR_EXPR) + decl = TREE_OPERAND (decl, 0); + if (TREE_CODE (decl) == FUNCTION_DECL) + { + if (DECL_BUILT_IN (decl)) + return NULL; + record_call (data, decl); + walk_tree (&TREE_OPERAND (*tp, 1), record_call_1, data, NULL); + *walk_subtrees = 0; + } + } + return NULL; +} + +/* Create cgraph edges for function calles via BODY. */ + +void +cgraph_create_edges (decl, body) + tree decl; + tree body; +{ + walk_tree (&body, record_call_1, decl, NULL); +} + +/* Analyze function once it is parsed. Set up the local information + available - create cgraph edges for function calles via BODY. */ + +void +cgraph_finalize_function (decl, body) + tree decl; + tree body ATTRIBUTE_UNUSED; +{ + struct cgraph_node *node = cgraph_node (decl); + + node->decl = decl; + + /* Set TREE_UNINLINABLE flag. */ + tree_inlinable_function_p (decl); + + (*debug_hooks->deferred_inline_function) (decl); +} + +/* Dump the callgraph. */ + +void +dump_cgraph (f) + FILE *f; +{ + struct cgraph_node *node; + + fprintf (f, "\nCallgraph:\n\n"); + for (node = cgraph_nodes; node; node = node->next) + { + struct cgraph_edge *edge; + fprintf (f, "%s", IDENTIFIER_POINTER (DECL_NAME (node->decl))); + if (node->origin) + fprintf (f, " nested in: %s", + IDENTIFIER_POINTER (DECL_NAME (node->origin->decl))); + if (node->needed) + fprintf (f, " needed"); + else if (node->reachable) + fprintf (f, " reachable"); + if (DECL_SAVED_TREE (node->decl)) + fprintf (f, " tree"); + + fprintf (f, "\n called by :"); + for (edge = node->callers; edge; edge = edge->next_caller) + fprintf (f, "%s ", + IDENTIFIER_POINTER (DECL_NAME (edge->caller->decl))); + + fprintf (f, "\n calls: "); + for (edge = node->callees; edge; edge = edge->next_callee) + fprintf (f, "%s ", + IDENTIFIER_POINTER (DECL_NAME (edge->callee->decl))); + fprintf (f, "\n"); + } +} + +static struct cgraph_node *queue = NULL; + +/* Notify finalize_compilation_unit that given node is reachable + or needed. */ +static void +cgraph_mark_needed_node (node, needed) + struct cgraph_node *node; + int needed; +{ + if (needed) + { + if (DECL_SAVED_TREE (node->decl)) + announce_function (node->decl); + node->needed = 1; + } + if (!node->reachable) + { + node->reachable = 1; + if (DECL_SAVED_TREE (node->decl)) + { + node->aux = queue; + queue = node; + } + } +} + +/* Analyze the whole compilation unit once it is parsed completely. */ + +void +cgraph_finalize_compilation_unit () +{ + struct cgraph_node *node; + struct cgraph_edge *edge; + + /* Collect entry points to the unit. */ + + if (!quiet_flag) + fprintf (stderr, "\n\nUnit entry points:"); + + for (node = cgraph_nodes; node; node = node->next) + { + tree decl = node->decl; + + if (!DECL_SAVED_TREE (decl)) + continue; + if ((TREE_PUBLIC (decl) && !DECL_COMDAT (decl) && !DECL_EXTERNAL (decl)) + || (DECL_ASSEMBLER_NAME_SET_P (decl) + && TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (decl)))) + { + cgraph_mark_needed_node (node, 1); + } + } + + /* Propagate reachability flag and lower representation of all reachable + functions. In the future, lowering will introduce new functions and + new entry points on the way (by template instantiation and virtual + method table generation for instance). */ + while (queue) + { + tree decl = queue->decl; + + node = queue; + queue = queue->aux; + if (node->lowered || !node->reachable || !DECL_SAVED_TREE (decl)) + abort (); + + /* At the moment frontend automatically emits all nested functions. */ + if (node->nested) + { + struct cgraph_node *node2; + + for (node2 = node->nested; node2; node2 = node2->next_nested) + if (!node2->reachable) + cgraph_mark_needed_node (node2, 0); + } + + if (lang_hooks.callgraph.lower_function) + (*lang_hooks.callgraph.lower_function) (decl); + /* First kill forward declaration so reverse inling works properly. */ + cgraph_create_edges (decl, DECL_SAVED_TREE (decl)); + + for (edge = node->callees; edge; edge = edge->next_callee) + { + if (!edge->callee->reachable) + cgraph_mark_needed_node (edge->callee, 0); + } + node->lowered = true; + } + if (!quiet_flag) + fprintf (stderr, "\n\nReclaiming functions:"); + + for (node = cgraph_nodes; node; node = node->next) + { + tree decl = node->decl; + + if (!node->reachable && DECL_SAVED_TREE (decl)) + { + DECL_SAVED_TREE (decl) = NULL; + announce_function (decl); + } + } + ggc_collect (); +} + +/* Expand all functions that must be output. */ + +#define NPREDECESORS(node) (size_t)((node)->aux) +#define SET_NPREDECESORS(node,n) (node)->aux = (void *) (n); + +/* Figure out what functions we want to assemble. */ + +static void +cgraph_mark_functions_to_output () +{ + struct cgraph_node *node; + + /* Figure out functions we want to assemble. */ + for (node = cgraph_nodes; node; node = node->next) + { + tree decl = node->decl; + + if (DECL_SAVED_TREE (decl) + && (node->needed + || (DECL_UNINLINABLE (decl) && node->reachable) + || TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (decl))) + && !TREE_ASM_WRITTEN (decl) && !node->origin + && !DECL_EXTERNAL (decl)) + node->output = 1; + } +} + +/* Expand function specified by NODE. */ +static void +cgraph_expand_function (node) + struct cgraph_node *node; +{ + tree decl = node->decl; + + announce_function (decl); + if (flag_inline_trees) + optimize_inline_calls (decl); + (*lang_hooks.callgraph.expand_function) (decl); + if (DECL_UNINLINABLE (decl)) + DECL_SAVED_TREE (decl) = NULL; + current_function_decl = NULL; +} + + +/* Expand all functions that must be output. + + Attempt to topologically sort the nodes so function is output when + all called functions are already assembled to allow data to be propagated + accross the callgraph. Use stack to get smaller distance between function + and it's callees (later we may use more sophisticated algorithm for + function reordering, we will likely want to use subsections to make output + functions to appear in top-down order, not bottom-up they are assembled). */ + +static void +cgraph_expand_functions () +{ + struct cgraph_node *node; + struct cgraph_node **stack = + xcalloc (sizeof (struct cgraph_node *), cgraph_n_nodes); + int stack_size = 0; + struct cgraph_edge *edge; + + cgraph_mark_functions_to_output (); + + for (node = cgraph_nodes; node; node = node->next) + if (node->output) + { + int n = 0; + for (edge = node->callees; edge; edge = edge->next_callee) + if (edge->callee->output) + n++; + SET_NPREDECESORS (node, n); + if (n == 0) + stack[stack_size++] = node; + } + while (1) + { + struct cgraph_node *minnode; + while (stack_size) + { + node = stack[--stack_size]; + node->output = 0; + + for (edge = node->callers; edge; edge = edge->next_caller) + if (edge->caller->output) + { + SET_NPREDECESORS (edge->caller, + NPREDECESORS (edge->caller) - 1); + if (!NPREDECESORS (edge->caller)) + stack[stack_size++] = edge->caller; + } + if (!node->reachable) + abort (); + cgraph_expand_function (node); + } + minnode = NULL; + /* We found cycle. Break it and try again. */ + for (node = cgraph_nodes; node; node = node->next) + if (node->output + && (!minnode + || NPREDECESORS (minnode) > NPREDECESORS (node))) + minnode = node; + if (!minnode) + return; + stack[stack_size++] = minnode; + } +} + +/* Perform simple optimizations based on callgraph. */ + +void +cgraph_optimize () +{ + struct cgraph_node *node; + bool changed = true; + struct cgraph_edge *edge; + + if (!quiet_flag) + fprintf (stderr, "\n\nAssembling functions:"); + + /* Output everything. + ??? Our inline heuristic may decide to not inline functions previously + marked as inlinable thus adding new function bodies that must be output. + Later we should move all inlining decisions to callgraph code to make + this impossible. */ + cgraph_expand_functions (); + while (changed) + { + changed = false; + for (node = cgraph_nodes; node; node = node->next) + { + if (!node->needed) + continue; + + for (edge = node->callees; edge; edge = edge->next_callee) + if (!edge->callee->needed) + changed = edge->callee->needed = true; + } + } + cgraph_expand_functions (); +} diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index 0e0a0ea91a1..a21f1bb69c3 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -290,7 +290,7 @@ in the following sections. -fsched-spec-load-dangerous -fsignaling-nans @gol -fsingle-precision-constant -fssa -fssa-ccp -fssa-dce @gol -fstrength-reduce -fstrict-aliasing -ftracer -fthread-jumps @gol --funroll-all-loops -funroll-loops -funswitch-loops @gol +-funit-at-a-time -funroll-all-loops -funroll-loops -funswitch-loops @gol --param @var{name}=@var{value} -O -O0 -O1 -O2 -O3 -Os} @@ -4261,6 +4261,11 @@ Perform tail duplication to enlarge superblock size. This transformation simplifies the control flow of the function allowing other optimizations to do better job. +@item -funit-at-a-time +@opindex funit-at-a-time +Parse the whole compilation unit before starting to produce code. This allows some +extra optimizations to take place but consumes more memory. + @item -funroll-loops @opindex funroll-loops Unroll loops whose number of iterations can be determined at compile diff --git a/gcc/flags.h b/gcc/flags.h index 80ee6a6bbbf..18cffaa479b 100644 --- a/gcc/flags.h +++ b/gcc/flags.h @@ -652,6 +652,8 @@ extern int flag_zero_initialized_in_bss; /* Nonzero means disable transformations observable by signaling NaNs. */ extern int flag_signaling_nans; +extern int flag_unit_at_a_time; + /* True if the given mode has a NaN representation and the treatment of NaN operands is important. Certain optimizations, such as folding x * 0 into x, are not correct for NaN operands, and are normally diff --git a/gcc/langhooks-def.h b/gcc/langhooks-def.h index 458baf58123..c38fdd8c1f6 100644 --- a/gcc/langhooks-def.h +++ b/gcc/langhooks-def.h @@ -164,6 +164,14 @@ tree lhd_tree_inlining_convert_parm_for_inlining PARAMS ((tree, tree, tree)); LANG_HOOKS_TREE_INLINING_CONVERT_PARM_FOR_INLINING \ } \ +#define LANG_HOOKS_CALLGRAPH_LOWER_FUNCTION NULL +#define LANG_HOOKS_CALLGRAPH_EXPAND_FUNCTION NULL + +#define LANG_HOOKS_CALLGRAPH_INITIALIZER { \ + LANG_HOOKS_CALLGRAPH_LOWER_FUNCTION, \ + LANG_HOOKS_CALLGRAPH_EXPAND_FUNCTION, \ +} \ + #define LANG_HOOKS_FUNCTION_INITIALIZER { \ LANG_HOOKS_FUNCTION_INIT, \ LANG_HOOKS_FUNCTION_FINAL, \ @@ -261,6 +269,7 @@ int lhd_tree_dump_type_quals PARAMS ((tree)); LANG_HOOKS_FORMAT_ATTRIBUTE_TABLE, \ LANG_HOOKS_FUNCTION_INITIALIZER, \ LANG_HOOKS_TREE_INLINING_INITIALIZER, \ + LANG_HOOKS_CALLGRAPH_INITIALIZER, \ LANG_HOOKS_TREE_DUMP_INITIALIZER, \ LANG_HOOKS_DECLS, \ LANG_HOOKS_FOR_TYPES_INITIALIZER \ diff --git a/gcc/langhooks.h b/gcc/langhooks.h index 32782c1722c..3118d2cfc69 100644 --- a/gcc/langhooks.h +++ b/gcc/langhooks.h @@ -58,6 +58,15 @@ struct lang_hooks_for_tree_inlining union tree_node *)); }; +struct lang_hooks_for_callgraph +{ + /* Function passed as argument is needed and will be compiled. + Lower the representation so the calls are explicit. */ + void (*lower_function) PARAMS ((union tree_node *)); + /* Produce RTL for function passed as argument. */ + void (*expand_function) PARAMS ((union tree_node *)); +}; + /* Lang hooks for management of language-specific data or status when entering / leaving functions etc. */ struct lang_hooks_for_functions @@ -353,6 +362,8 @@ struct lang_hooks 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; diff --git a/gcc/toplev.c b/gcc/toplev.c index 38cff5b12d7..0e05323503e 100644 --- a/gcc/toplev.c +++ b/gcc/toplev.c @@ -883,6 +883,10 @@ int flag_new_regalloc = 0; int flag_tracer = 0; +/* Nonzero if we perform whole unit at a time compilation. */ + +int flag_unit_at_a_time = 0; + /* Values of the -falign-* flags: how much to align labels in code. 0 means `use default', 1 means `don't align'. For each variable, there is an _log variant which is the power @@ -989,6 +993,8 @@ static const lang_independent_options f_options[] = N_("Optimize sibling and tail recursive calls") }, {"tracer", &flag_tracer, 1, N_("Perform superblock formation via tail duplication") }, + {"unit-at-a-time", &flag_unit_at_a_time, 1, + N_("Compile whole compilation unit at a time") }, {"cse-follow-jumps", &flag_cse_follow_jumps, 1, N_("When running CSE, follow jumps to their targets") }, {"cse-skip-blocks", &flag_cse_skip_blocks, 1, @@ -5124,6 +5130,11 @@ process_options () if (flag_asynchronous_unwind_tables) flag_unwind_tables = 1; + /* Disable unit-at-a-time mode for frontends not supporting callgraph + interface. */ + if (flag_unit_at_a_time && ! lang_hooks.callgraph.expand_function) + flag_unit_at_a_time = 0; + /* Warn about options that are not supported on this machine. */ #ifndef INSN_SCHEDULING if (flag_schedule_insns || flag_schedule_insns_after_reload) diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c index e4bdf12e52f..19b7a4f8fc8 100644 --- a/gcc/tree-inline.c +++ b/gcc/tree-inline.c @@ -103,6 +103,8 @@ typedef struct inline_data /* Hash table used to prevent walk_tree from visiting the same node umpteen million times. */ htab_t tree_pruner; + /* Decl of function we are inlining into. */ + tree decl; } inline_data; /* Prototypes. */ @@ -1368,6 +1370,13 @@ expand_call_inline (tp, walk_subtrees, data) /* For accounting, subtract one for the saved call/ret. */ id->inlined_stmts += DECL_NUM_STMTS (fn) - 1; + /* Update callgraph if needed. */ + if (id->decl && flag_unit_at_a_time) + { + cgraph_remove_call (id->decl, fn); + cgraph_create_edges (id->decl, *inlined_body); + } + /* Recurse into the body of the just inlined function. */ expand_calls_inline (inlined_body, id); VARRAY_POP (id->fns); @@ -1414,6 +1423,7 @@ optimize_inline_calls (fn) /* Clear out ID. */ memset (&id, 0, sizeof (id)); + id.decl = fn; /* Don't allow recursion into FN. */ VARRAY_TREE_INIT (id.fns, 32, "fns"); VARRAY_PUSH_TREE (id.fns, fn); diff --git a/gcc/tree.h b/gcc/tree.h index 45238fddd82..e5344782fb8 100644 --- a/gcc/tree.h +++ b/gcc/tree.h @@ -3164,6 +3164,15 @@ extern const char *dump_flag_name PARAMS ((enum tree_dump_index)); extern void set_decl_rtl PARAMS ((tree, rtx)); +/* In callgraph.c */ +void cgraph_finalize_function PARAMS ((tree, tree)); +void cgraph_finalize_compilation_unit PARAMS ((void)); +void cgraph_create_edges PARAMS ((tree, tree)); +void dump_cgraph PARAMS ((FILE *)); +void cgraph_optimize PARAMS ((void)); +void cgraph_remove_call PARAMS ((tree, tree)); +bool cgraph_calls_p PARAMS ((tree, tree)); + /* Redefine abort to report an internal error w/o coredump, and reporting the location of the error in the source file. This logic |