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:
parent
9037b0c71e
commit
e72fcfe8e4
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
118
gcc/c-decl.c
118
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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 ();
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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;
|
||||
|
|
11
gcc/toplev.c
11
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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue