Makefile.in (CRTSTUFF_CFLAGS): Add -fno-unit-at-a-time

* 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

From-SVN: r62789
This commit is contained in:
Jan Hubicka 2003-02-12 22:48:59 +01:00 committed by Jan Hubicka
parent 9037b0c71e
commit e72fcfe8e4
14 changed files with 742 additions and 48 deletions

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)
{

View File

@ -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 */

563
gcc/cgraph.c Normal file
View File

@ -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 ();
}

View File

@ -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

View File

@ -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

View File

@ -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 \

View File

@ -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;

View File

@ -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)

View File

@ -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);

View File

@ -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