This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: Unit at time compilation mode II


> On Tue, Feb 11, 2003 at 11:37:48AM +0100, Jan Hubicka wrote:
Hi,
here is updated patch I am currently testing.  It should contain changes
you asked for except these:

> > 	* Makefile.in (CRTSTUFF_CFLAGS): Add -fno-unit-at-time
> 
> Why did you need this?  If we failed to emit something, then it
> means that either we're missing an __attribute__((used)), or
> you've a bug in your callgraph code.
> 
> > +   while (changed)
> > +     {
> > +       changed = false;
> > +       for (node = cgraph_nodes; node; node = node->next)
> > + 	{
> 	  ...
> > + 	  changed = true;
> 
> I think you want a work list instead.  Perhaps threaded through the
> nodes themselves, to avoid memory management hassles.  As is, you've
> got O(N**2) traversal.

At the moment, the lowering is not needed for C fronend, so I can simply
add the aux field into nodes and queue them in case you think that being
O^2 here may be important problem.  I am just bit affraid that it will
make turning C++ and objc fronds into unit-at-time more dificult than it
is now.

Honza

Wed Jan 15 02:14:04 CET 2003  Jan Hubicka  <jh@suse.cz>

	* Makefile.in (CRTSTUFF_CFLAGS): Add -fno-unit-at-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_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_time): New.
	(lang_independent_options): Add flag_unit_at_time.
	(parse_options_and_default_flags): Set flag_unit_at_time for -O2.
	(process_options): Disable unit-at-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-time): Document

Index: Makefile.in
===================================================================
RCS file: /cvs/gcc/gcc/gcc/Makefile.in,v
retrieving revision 1.991
diff -c -3 -p -r1.991 Makefile.in
*** Makefile.in	10 Feb 2003 19:18:41 -0000	1.991
--- Makefile.in	11 Feb 2003 22:09:26 -0000
*************** TARGET_LIBGCC2_CFLAGS =
*** 416,422 ****
  # 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
  
  # Additional sources to handle exceptions; overridden by targets as needed.
  LIB2ADDEH = $(srcdir)/unwind-dw2.c $(srcdir)/unwind-dw2-fde.c \
--- 416,422 ----
  # 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-unit-at-time
  
  # Additional sources to handle exceptions; overridden by targets as needed.
  LIB2ADDEH = $(srcdir)/unwind-dw2.c $(srcdir)/unwind-dw2-fde.c \
*************** OBJS = alias.o bb-reorder.o bitmap.o bui
*** 785,791 ****
   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						   \
   $(GGC) $(out_object_file) $(EXTRA_OBJS) $(host_hook_obj)
  
  BACKEND = main.o libbackend.a
--- 785,791 ----
   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 callgraph.o					   \
   $(GGC) $(out_object_file) $(EXTRA_OBJS) $(host_hook_obj)
  
  BACKEND = main.o libbackend.a
*************** jump.o : jump.c $(CONFIG_H) $(SYSTEM_H) 
*** 1530,1535 ****
--- 1530,1537 ----
  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)
+ callgraph.o : callgraph.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: c-decl.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/c-decl.c,v
retrieving revision 1.362
diff -c -3 -p -r1.362 c-decl.c
*** c-decl.c	10 Feb 2003 12:45:52 -0000	1.362
--- c-decl.c	11 Feb 2003 22:09:27 -0000
*************** static tree grokdeclarator		PARAMS ((tre
*** 282,288 ****
  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 warn_if_shadowing		PARAMS ((tree, tree));
  static bool flexible_array_type_p	PARAMS ((tree));
  
--- 282,288 ----
  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_1             PARAMS ((tree, int));
  static void warn_if_shadowing		PARAMS ((tree, tree));
  static bool flexible_array_type_p	PARAMS ((tree));
  
*************** finish_function (nested, can_defer_p)
*** 6412,6421 ****
    free_after_compilation (cfun);
    cfun = NULL;
  
    if (! nested)
      {
!       /* Generate RTL for the body of this function.  */
!       c_expand_body (fndecl, nested, can_defer_p);
  
        /* Let the error reporting routines know that we're outside a
  	 function.  For a nested function, this value is used in
--- 6412,6473 ----
    free_after_compilation (cfun);
    cfun = NULL;
  
+   if (flag_unit_at_time)
+     {
+       cgraph_finalize_function (fndecl, DECL_SAVED_TREE (fndecl));
+       current_function_decl = NULL;
+       return;
+     }
+ 
    if (! nested)
      {
!       /* 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
*************** c_expand_deferred_function (fndecl)
*** 6434,6440 ****
       function was deferred, e.g. in duplicate_decls.  */
    if (DECL_INLINE (fndecl) && DECL_RESULT (fndecl))
      {
!       c_expand_body (fndecl, 0, 0);
        current_function_decl = NULL;
      }
  }
--- 6486,6498 ----
       function was deferred, e.g. in duplicate_decls.  */
    if (DECL_INLINE (fndecl) && DECL_RESULT (fndecl))
      {
!       if (flag_inline_trees)
! 	{
! 	  timevar_push (TV_INTEGRATION);
! 	  optimize_inline_calls (fndecl);
! 	  timevar_pop (TV_INTEGRATION);
! 	}
!       c_expand_body (fndecl);
        current_function_decl = NULL;
      }
  }
*************** c_expand_deferred_function (fndecl)
*** 6445,6486 ****
     generation of RTL.  */
  
  static void
! c_expand_body (fndecl, nested_p, can_defer_p)
       tree fndecl;
!      int nested_p, can_defer_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)
--- 6503,6512 ----
     generation of RTL.  */
  
  static void
! c_expand_body_1 (fndecl, nested_p)
       tree fndecl;
!      int nested_p;
  {
    timevar_push (TV_EXPAND);
  
    if (nested_p)
*************** c_expand_body (fndecl, nested_p, can_def
*** 6519,6529 ****
  
    /* 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
--- 6545,6550 ----
*************** c_expand_body (fndecl, nested_p, can_def
*** 6621,6626 ****
--- 6642,6656 ----
      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.  */
*************** c_expand_decl_stmt (t)
*** 6854,6860 ****
    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);
  }
  
  /* Return the IDENTIFIER_GLOBAL_VALUE of T, for use in common code, since
--- 6884,6890 ----
    if (TREE_CODE (decl) == FUNCTION_DECL
        && DECL_CONTEXT (decl) == current_function_decl
        && DECL_SAVED_TREE (decl))
!     c_expand_body_1 (decl, 1);
  }
  
  /* Return the IDENTIFIER_GLOBAL_VALUE of T, for use in common code, since
Index: c-lang.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/c-lang.c,v
retrieving revision 1.101
diff -c -3 -p -r1.101 c-lang.c
*** c-lang.c	16 Dec 2002 18:19:02 -0000	1.101
--- c-lang.c	11 Feb 2003 22:09:27 -0000
*************** static void c_init_options PARAMS ((void
*** 99,104 ****
--- 99,107 ----
  #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: c-objc-common.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/c-objc-common.c,v
retrieving revision 1.20
diff -c -3 -p -r1.20 c-objc-common.c
*** c-objc-common.c	10 Jan 2003 02:21:58 -0000	1.20
--- c-objc-common.c	11 Feb 2003 22:09:27 -0000
*************** c_objc_common_finish_file ()
*** 361,367 ****
    if (pch_file)
      c_common_write_pch ();
  
!   expand_deferred_fns ();
  
    if (static_ctors)
      {
--- 361,373 ----
    if (pch_file)
      c_common_write_pch ();
  
!   if (flag_unit_at_time)
!     {
!       cgraph_finalize_compilation_unit ();
!       cgraph_optimize ();
!     }
!   else
!     expand_deferred_fns ();
  
    if (static_ctors)
      {
Index: c-tree.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/c-tree.h,v
retrieving revision 1.109
diff -c -3 -p -r1.109 c-tree.h
*** c-tree.h	16 Sep 2002 18:33:18 -0000	1.109
--- c-tree.h	11 Feb 2003 22:09:27 -0000
*************** extern void finish_file				PARAMS ((void
*** 172,177 ****
--- 172,178 ----
  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 */
Index: callgraph.c
===================================================================
RCS file: callgraph.c
diff -N callgraph.c
*** /dev/null	1 Jan 1970 00:00:00 -0000
--- callgraph.c	11 Feb 2003 22:09:27 -0000
***************
*** 0 ****
--- 1,514 ----
+ /* 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;
+   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 *));
+ 
+ /* 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));
+   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)
+ 	{
+ 	  struct cgraph_node *n = cgraph_node (decl);
+ 	  n->needed = n->reachable = true;
+ 	}
+     }
+   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");
+     }
+ }
+ 
+ /* Analyze the whole compilation unit once it is parsed completely.  */
+ 
+ void
+ cgraph_finalize_compilation_unit ()
+ {
+   struct cgraph_node *node;
+   struct cgraph_edge *edge;
+   bool changed = true;
+ 
+   if (!quiet_flag)
+     fprintf (stderr, "\n\nUnit entry points:");
+   /*  Lower representation of all reachable functions.  */
+   while (changed)
+     {
+       changed = false;
+       for (node = cgraph_nodes; node; node = node->next)
+ 	{
+ 	  tree decl = node->decl;
+ 
+ 	  if (node->lowered || !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))))
+ 	    {
+ 	      announce_function (decl);
+ 	      node->needed = node->reachable = true;
+ 	    }
+ 	  /* At the moment frontend automatically emits all nested functions.  */
+ 	  if (node->origin && node->origin->reachable)
+ 	    node->reachable = 1;
+ 	  if (!node->reachable)
+ 	    continue;
+ 
+ 	  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)
+ 	    edge->callee->reachable = true;
+ 	  node->lowered = true;
+ 	  changed = 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);
+   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.  */
+ 
+ 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 ();
+ 
+   /* 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).
+      */
+ 
+   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: flags.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/flags.h,v
retrieving revision 1.97
diff -c -3 -p -r1.97 flags.h
*** flags.h	1 Feb 2003 13:09:39 -0000	1.97
--- flags.h	11 Feb 2003 22:09:27 -0000
*************** extern int flag_zero_initialized_in_bss;
*** 652,657 ****
--- 652,659 ----
  /* Nonzero means disable transformations observable by signaling NaNs.  */
  extern int flag_signaling_nans;
  
+ extern int flag_unit_at_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: langhooks-def.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/langhooks-def.h,v
retrieving revision 1.40
diff -c -3 -p -r1.40 langhooks-def.h
*** langhooks-def.h	16 Dec 2002 18:19:40 -0000	1.40
--- langhooks-def.h	11 Feb 2003 22:09:27 -0000
*************** tree lhd_tree_inlining_convert_parm_for_
*** 164,169 ****
--- 164,177 ----
    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,			\
*************** int lhd_tree_dump_type_quals			PARAMS ((
*** 261,266 ****
--- 269,275 ----
    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: langhooks.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/langhooks.h,v
retrieving revision 1.49
diff -c -3 -p -r1.49 langhooks.h
*** langhooks.h	16 Dec 2002 18:19:41 -0000	1.49
--- langhooks.h	11 Feb 2003 22:09:27 -0000
*************** struct lang_hooks_for_tree_inlining
*** 58,63 ****
--- 58,72 ----
  							 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
*************** struct lang_hooks
*** 352,357 ****
--- 361,368 ----
    struct lang_hooks_for_functions function;
  
    struct lang_hooks_for_tree_inlining tree_inlining;
+ 
+   struct lang_hooks_for_callgraph callgraph;
  
    struct lang_hooks_for_tree_dump tree_dump;
  
Index: toplev.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/toplev.c,v
retrieving revision 1.707
diff -c -3 -p -r1.707 toplev.c
*** toplev.c	9 Feb 2003 19:18:54 -0000	1.707
--- toplev.c	11 Feb 2003 22:09:27 -0000
*************** int flag_new_regalloc = 0;
*** 882,887 ****
--- 882,891 ----
  
  int flag_tracer = 0;
  
+ /* Nonzero if we porform compilation unit at time compilation.  */
+ 
+ int flag_unit_at_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
*************** static const lang_independent_options f_
*** 988,993 ****
--- 992,999 ----
     N_("Optimize sibling and tail recursive calls") },
    {"tracer", &flag_tracer, 1,
     N_("Perform superblock formation via tail duplication") },
+   {"unit-at-time", &flag_unit_at_time, 1,
+    N_("Compile whole compilation unit at 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,
*************** process_options ()
*** 5096,5101 ****
--- 5102,5112 ----
      flag_asynchronous_unwind_tables = 1;
    if (flag_asynchronous_unwind_tables)
      flag_unwind_tables = 1;
+ 
+   /* Disable unit-at-time mode for frontends not supporting callgraph
+      interface.  */
+   if (flag_unit_at_time && ! lang_hooks.callgraph.expand_function)
+     flag_unit_at_time = 0;
  
    /* Warn about options that are not supported on this machine.  */
  #ifndef INSN_SCHEDULING
Index: tree-inline.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/tree-inline.c,v
retrieving revision 1.43
diff -c -3 -p -r1.43 tree-inline.c
*** tree-inline.c	30 Jan 2003 18:09:15 -0000	1.43
--- tree-inline.c	11 Feb 2003 22:09:27 -0000
*************** typedef struct inline_data
*** 103,108 ****
--- 103,110 ----
    /* 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.  */
*************** expand_call_inline (tp, walk_subtrees, d
*** 1368,1373 ****
--- 1370,1382 ----
    /* 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_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);
*************** optimize_inline_calls (fn)
*** 1414,1419 ****
--- 1423,1429 ----
    /* 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: tree.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/tree.h,v
retrieving revision 1.380
diff -c -3 -p -r1.380 tree.h
*** tree.h	10 Feb 2003 17:46:07 -0000	1.380
--- tree.h	11 Feb 2003 22:09:28 -0000
*************** extern const char *dump_flag_name	PARAMS
*** 3164,3169 ****
--- 3164,3178 ----
  
  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
Index: doc/invoke.texi
===================================================================
RCS file: /cvs/gcc/gcc/gcc/doc/invoke.texi,v
retrieving revision 1.236
diff -c -3 -p -r1.236 invoke.texi
*** doc/invoke.texi	10 Feb 2003 11:45:26 -0000	1.236
--- doc/invoke.texi	11 Feb 2003 22:09:29 -0000
*************** in the following sections.
*** 290,296 ****
  -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
  --param @var{name}=@var{value}
  -O  -O0  -O1  -O2  -O3  -Os}
  
--- 290,296 ----
  -fsched-spec-load-dangerous  -fsignaling-nans @gol
  -fsingle-precision-constant  -fssa -fssa-ccp -fssa-dce @gol
  -fstrength-reduce  -fstrict-aliasing  -ftracer -fthread-jumps @gol
! -funit-at-time -funroll-all-loops  -funroll-loops  -funswitch-loops @gol
  --param @var{name}=@var{value}
  -O  -O0  -O1  -O2  -O3  -Os}
  
*************** for testing, so we are interested to hea
*** 4257,4262 ****
--- 4257,4267 ----
  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-time
+ @opindex funit-at-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


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]