summaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorhubicka <hubicka@138bc75d-0d04-0410-961f-82ee72b054a4>2003-02-12 21:48:59 +0000
committerhubicka <hubicka@138bc75d-0d04-0410-961f-82ee72b054a4>2003-02-12 21:48:59 +0000
commit833eb72438b0a66929179b0402b48da4632d6af5 (patch)
tree145028e0c53cc6c7857dc24db0335535b3ff24ab /gcc
parent919d789fe70b97397da681856f8269a4e7967b6a (diff)
downloadgcc-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/ChangeLog32
-rw-r--r--gcc/Makefile.in6
-rw-r--r--gcc/c-decl.c118
-rw-r--r--gcc/c-lang.c3
-rw-r--r--gcc/c-objc-common.c8
-rw-r--r--gcc/c-tree.h1
-rw-r--r--gcc/cgraph.c563
-rw-r--r--gcc/doc/invoke.texi7
-rw-r--r--gcc/flags.h2
-rw-r--r--gcc/langhooks-def.h9
-rw-r--r--gcc/langhooks.h11
-rw-r--r--gcc/toplev.c11
-rw-r--r--gcc/tree-inline.c10
-rw-r--r--gcc/tree.h9
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