Makefile.in (CRTSTUFF_CFLAGS): Add -fno-unit-at-a-time
authorJan Hubicka <jh@suse.cz>
Wed, 12 Feb 2003 21:48:59 +0000 (22:48 +0100)
committerJan Hubicka <hubicka@gcc.gnu.org>
Wed, 12 Feb 2003 21:48:59 +0000 (21:48 +0000)
* 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

14 files changed:
gcc/ChangeLog
gcc/Makefile.in
gcc/c-decl.c
gcc/c-lang.c
gcc/c-objc-common.c
gcc/c-tree.h
gcc/cgraph.c [new file with mode: 0644]
gcc/doc/invoke.texi
gcc/flags.h
gcc/langhooks-def.h
gcc/langhooks.h
gcc/toplev.c
gcc/tree-inline.c
gcc/tree.h

index d2cd82a3ea1c2aea765356173282bef0b32a8291..9ee4179a54b882de9a75f969c20667ff06199be8 100644 (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.
index 8aaa6991e2a1e16ffe77417236d0628e539087db..d528378214bad77074006e1cf2051d7a3d14101c 100644 (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
index 01a35874ecb89172b3a391445033b9a819bcf2b0..a0c1d56c131b7c70fa0c133192fd3f7b01582dea 100644 (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));
 \f
@@ -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);
+}
 \f
 /* 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
index 034889764700992f3d3bfd634d19c53cbaf4c3dd..116561e9d0723a033c14517aef7362139910dde4 100644 (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
index c91e635b4102d1f37361874bccdd438d93909e1f..a84ddc8fd70c9aa0f5614270e0fa37298f09d221 100644 (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)
     {
index 159c235224e230fc94b48cc47b1789dd49edd910..c69838692acf64abd239a4886f6c5d3ab03dc7c0 100644 (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));
 
 \f
 /* in c-parse.in */
diff --git a/gcc/cgraph.c b/gcc/cgraph.c
new file mode 100644 (file)
index 0000000..a199fe6
--- /dev/null
@@ -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 ();
+}
index 0e0a0ea91a14f20653cbe51f9a37d0ef42b7ea91..a21f1bb69c35d8c9b2295bfa5afe832e75ebc4bf 100644 (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
index 80ee6a6bbbf439015dbbd8df1aee096642ddab28..18cffaa479bbcb27dcae2cfe35c4d8990b4c25c1 100644 (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
index 458baf5812317a36bd720e11f5312aab5f46bbef..c38fdd8c1f64cd416f2f62dc460e55d3461c04fa 100644 (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 \
index 32782c1722c1c9c30bd9de6b846d16344dbd440a..3118d2cfc696cc3655c5cb33fc9309a292d3c7ab 100644 (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;
index 38cff5b12d7e8d905f0df2e601a8467acb68fe48..0e05323503e81705c9f9078e4dac70fa3ed355f3 100644 (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)
index e4bdf12e52f5a1d56b659a62ede233aed35b9679..19b7a4f8fc8d12e024932f7da714ab87f1fda787 100644 (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);
index 45238fddd8254ccc58d34006a062299f43c6f268..e5344782fb8bbeb315407821bccb5f092bd14536 100644 (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));
+
 \f
 /* Redefine abort to report an internal error w/o coredump, and
    reporting the location of the error in the source file.  This logic