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]

tree_rest_of_compilation


The following patch unifies the C and C++ front ends in how we go
about emitting rtl.  It's not 100% yet, but its better.  

There are a couple of points that still want cleaning up:

  (1) The expand_deferred_fns change reproduces code in 
      wrapup_global_declarations.  I have to do this because the
      code in tree_rest_of_compilation forces the rtl inliner to
      not run -- something that C++ did and that C should have
      been doing.

      Both of these are redundant with the IMO better code in cgraph.
      But this is good enough for now, to avoid introducing regressions
      in our inlining.

  (2) There's a bunch of code in C++ that got moved around that
      saves and restores cfun->lang.  All of this can now go away.

  (3) Moving how global constructors are built for C++ down into
      common code would allow C/ObjC to use it as well, which would
      eliminate that difference.

  (4) Need to do Java, but I don't recall if all of the patches I would
      need are present on mainline, as opposed to tree-ssa branch.

Tested all of the patches committed this afternoon as a unit
on i686-linux.  Scream if I broke someone else...



r~



gcc/
        * tree-optimize.c: New file.
        * Makefile.in (OBJS-archive): Add tree-optimize.o.
        (tree-optimize.o): New.
        * c-decl.c (store_parm_decls): Use allocate_struct_function.
        (finish_function): Don't free_after_parsing or free_after_compilation.
        (set_save_expr_context): Move to tree-optimize.c.
        (c_expand_body_1): Use tree_rest_of_compilation.
        * c-lang.c (LANG_HOOKS_RTL_EXPAND_STMT): New.
        * objc/objc-lang.c (LANG_HOOKS_RTL_EXPAND_STMT): New.
        * c-objc-common.c (expand_deferred_fns): Don't emit unused inlines;
        iterate until closure.
        * langhooks-def.h (LANG_HOOKS_RTL_EXPAND_START,
        LANG_HOOKS_RTL_EXPAND_STMT, LANG_HOOKS_RTL_EXPAND_END): New.
        (LANG_HOOKS_RTL_EXPAND_INITIALIZER): New.
        * langhooks.h (struct lang_hooks_for_rtl_expansion): New.
        * toplev.h (tree_rest_of_compilation): Declare it.

gcc/cp/
        * cp-lang.c (LANG_HOOKS_RTL_EXPAND_START): New.
        (LANG_HOOKS_RTL_EXPAND_STMT): New.
        * cp-tree.h (cxx_expand_function_start): Declare.
        * decl.c (start_function): Use allocate_struct_function.
        Move stmts_are_full_exprs_p assertion from expand_body.
        Do not free_after_parsing or free_after_compilation.
        (cxx_push_function_context): Move code to set struct function
        data from genrtl_start_function.
        * optimize.c (optimize_function): Don't inc/dec function_depth.
        * semantics.c (expand_body): Use tree_rest_of_compilation.
        (cxx_expand_function_start): Rename from genrtl_start_function,
        omit bits done by tree_rest_of_compilation.
        (genrtl_finish_function): Remove.
        (clear_decl_rtl): Move to ../tree-optimize.c.

Index: Makefile.in
===================================================================
RCS file: /cvs/gcc/gcc/gcc/Makefile.in,v
retrieving revision 1.1144
diff -u -p -r1.1144 Makefile.in
--- Makefile.in	29 Aug 2003 18:16:59 -0000	1.1144
+++ Makefile.in	29 Aug 2003 22:51:50 -0000
@@ -845,7 +845,7 @@ OBJS-common = \
 
 OBJS-md = $(out_object_file)
 OBJS-archive = $(EXTRA_OBJS) $(host_hook_obj) hashtable.o tree-inline.o	   \
-  cgraph.o cgraphunit.o
+  tree-optimize.o cgraph.o cgraphunit.o
 
 OBJS = $(OBJS-common) $(out_object_file) $(OBJS-archive)
 
@@ -1466,10 +1466,13 @@ tree.o : tree.c $(CONFIG_H) $(SYSTEM_H) 
 tree-dump.o: tree-dump.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(TREE_H) \
    $(C_TREE_H) flags.h langhooks.h toplev.h output.h c-pragma.h $(RTL_H) $(GGC_H) \
    $(EXPR_H) $(SPLAY_TREE_H) tree-dump.h
-tree-inline.o : tree-inline.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(TREE_H) \
-   $(RTL_H) $(EXPR_H) flags.h $(PARAMS_H) input.h insn-config.h $(INTEGRATE_H) \
-   $(VARRAY_H) $(HASHTAB_H) $(SPLAY_TREE_H) toplev.h langhooks.h \
-   $(C_COMMON_H) tree-inline.h cgraph.h
+tree-inline.o : tree-inline.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \
+   $(TREE_H) $(RTL_H) $(EXPR_H) flags.h $(PARAMS_H) input.h insn-config.h \
+   $(INTEGRATE_H) $(VARRAY_H) $(HASHTAB_H) $(SPLAY_TREE_H) toplev.h \
+   langhooks.h $(C_COMMON_H) tree-inline.h cgraph.h
+tree-optimize.o : tree-optimize.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \
+   $(TREE_H) toplev.h langhooks.h cgraph.h $(TIMEVAR_H) function.h $(GGC_H)
+
 print-tree.o : print-tree.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(TREE_H) \
    $(GGC_H) langhooks.h real.h
 stor-layout.o : stor-layout.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(TREE_H) \
Index: c-decl.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/c-decl.c,v
retrieving revision 1.436
diff -u -p -r1.436 c-decl.c
--- c-decl.c	29 Aug 2003 21:57:05 -0000	1.436
+++ c-decl.c	29 Aug 2003 22:51:54 -0000
@@ -6032,7 +6032,7 @@ store_parm_decls (void)
   gen_aux_info_record (fndecl, 1, 0, prototype);
 
   /* Initialize the RTL code for the function.  */
-  init_function_start (fndecl);
+  allocate_struct_function (fndecl);
 
   /* Begin the statement tree for this function.  */
   begin_stmt_tree (&DECL_SAVED_TREE (fndecl));
@@ -6142,11 +6142,8 @@ finish_function (int nested, int can_def
       && DECL_INLINE (fndecl))
     warning ("no return statement in function returning non-void");
 
-  /* Clear out memory we no longer need.  */
-  free_after_parsing (cfun);
-  /* Since we never call rest_of_compilation, we never clear
-     CFUN.  Do so explicitly.  */
-  free_after_compilation (cfun);
+  /* We're leaving the context of this function, so zap cfun.  It's still in
+     DECL_SAVED_INSNS, and we'll restore it in tree_rest_of_compilation.  */
   cfun = NULL;
 
   if (flag_unit_at_a_time && can_defer_p)
@@ -6239,25 +6236,6 @@ c_expand_deferred_function (tree fndecl)
     }
 }
 
-/* Called to move the SAVE_EXPRs for parameter declarations in a
-   nested function into the nested function.  DATA is really the
-   nested FUNCTION_DECL.  */
-
-static tree
-set_save_expr_context (tree *tp,
-		       int *walk_subtrees,
-		       void *data)
-{
-  if (TREE_CODE (*tp) == SAVE_EXPR && !SAVE_EXPR_CONTEXT (*tp))
-    SAVE_EXPR_CONTEXT (*tp) = (tree) data;
-  /* Do not walk back into the SAVE_EXPR_CONTEXT; that will cause
-     circularity.  */
-  else if (DECL_P (*tp))
-    *walk_subtrees = 0;
-
-  return NULL_TREE;
-}
-
 /* Generate the RTL for the body of FNDECL.  If NESTED_P is nonzero,
    then we are already in the process of generating RTL for another
    function.  If can_defer_p is zero, we won't attempt to defer the
@@ -6266,78 +6244,14 @@ set_save_expr_context (tree *tp,
 static void
 c_expand_body_1 (tree fndecl, int nested_p)
 {
-  timevar_push (TV_EXPAND);
-
   if (nested_p)
     {
       /* Make sure that we will evaluate variable-sized types involved
 	 in our function's type.  */
       expand_pending_sizes (DECL_LANG_SPECIFIC (fndecl)->pending_sizes);
-      /* Squirrel away our current state.  */
-      push_function_context ();
     }
 
-  /* Initialize the RTL code for the function.  */
-  current_function_decl = fndecl;
-  input_location = DECL_SOURCE_LOCATION (fndecl);
-  init_function_start (fndecl);
-
-  /* This function is being processed in whole-function mode.  */
-  cfun->x_whole_function_mode_p = 1;
-
-  /* Even though we're inside a function body, we still don't want to
-     call expand_expr to calculate the size of a variable-sized array.
-     We haven't necessarily assigned RTL to all variables yet, so it's
-     not safe to try to expand expressions involving them.  */
-  immediate_size_expand = 0;
-  cfun->x_dont_save_pending_sizes_p = 1;
-
-  /* Set up parameters and prepare for return, for the function.  */
-  expand_function_start (fndecl, 0);
-
-  /* If the function has a variably modified type, there may be
-     SAVE_EXPRs in the parameter types.  Their context must be set to
-     refer to this function; they cannot be expanded in the containing
-     function.  */
-  if (decl_function_context (fndecl)
-      && variably_modified_type_p (TREE_TYPE (fndecl)))
-    walk_tree (&TREE_TYPE (fndecl), set_save_expr_context, fndecl,
-	       NULL);
-
-  /* If this function is `main', emit a call to `__main'
-     to run global initializers, etc.  */
-  if (DECL_NAME (fndecl)
-      && MAIN_NAME_P (DECL_NAME (fndecl))
-      && DECL_FILE_SCOPE_P (fndecl))
-    expand_main_function ();
-
-  /* Generate the RTL for this function.  */
-  expand_stmt (DECL_SAVED_TREE (fndecl));
-
-  /* We hard-wired immediate_size_expand to zero above.
-     expand_function_end will decrement this variable.  So, we set the
-     variable to one here, so that after the decrement it will remain
-     zero.  */
-  immediate_size_expand = 1;
-
-  /* Allow language dialects to perform special processing.  */
-  if (lang_expand_function_end)
-    (*lang_expand_function_end) ();
-
-  /* Generate rtl for function exit.  */
-  expand_function_end ();
-
-  /* If this is a nested function, protect the local variables in the stack
-     above us from being collected while we're compiling this function.  */
-  if (nested_p)
-    ggc_push_context ();
-
-  /* Run the optimizers and output the assembler code for this function.  */
-  rest_of_compilation (fndecl);
-
-  /* Undo the GC context switch.  */
-  if (nested_p)
-    ggc_pop_context ();
+  tree_rest_of_compilation (fndecl);
 
   /* With just -Wextra, complain only if function returns both with
      and without a value.  */
@@ -6346,46 +6260,6 @@ c_expand_body_1 (tree fndecl, int nested
       && current_function_returns_null)
     warning ("this function may return with or without a value");
 
-  /* If requested, warn about function definitions where the function will
-     return a value (usually of some struct or union type) which itself will
-     take up a lot of stack space.  */
-
-  if (warn_larger_than && !DECL_EXTERNAL (fndecl) && TREE_TYPE (fndecl))
-    {
-      tree ret_type = TREE_TYPE (TREE_TYPE (fndecl));
-
-      if (ret_type && TYPE_SIZE_UNIT (ret_type)
-	  && TREE_CODE (TYPE_SIZE_UNIT (ret_type)) == INTEGER_CST
-	  && 0 < compare_tree_int (TYPE_SIZE_UNIT (ret_type),
-				   larger_than_size))
-	{
-          const location_t *locus = &DECL_SOURCE_LOCATION (fndecl);
-	  unsigned int size_as_int
-	    = TREE_INT_CST_LOW (TYPE_SIZE_UNIT (ret_type));
-
-	  if (compare_tree_int (TYPE_SIZE_UNIT (ret_type), size_as_int) == 0)
-	    warning ("%Hsize of return value of '%D' is %u bytes",
-                     locus, fndecl, size_as_int);
-	  else
-	    warning ("%Hsize of return value of '%D' is larger than %wd bytes",
-                     locus, fndecl, larger_than_size);
-	}
-    }
-
-  if (DECL_SAVED_INSNS (fndecl) == 0 && ! nested_p
-      && ! flag_inline_trees)
-    {
-      /* Stop pointing to the local nodes about to be freed.
-	 But DECL_INITIAL must remain nonzero so we know this
-	 was an actual function definition.
-	 For a nested function, this is done in c_pop_function_context.
-	 If rest_of_compilation set this to 0, leave it 0.  */
-      if (DECL_INITIAL (fndecl) != 0)
-	DECL_INITIAL (fndecl) = error_mark_node;
-
-      DECL_ARGUMENTS (fndecl) = 0;
-    }
-
   if (DECL_STATIC_CONSTRUCTOR (fndecl))
     {
       if (targetm.have_ctors_dtors)
@@ -6403,11 +6277,6 @@ c_expand_body_1 (tree fndecl, int nested
       else
 	static_dtors = tree_cons (NULL_TREE, fndecl, static_dtors);
     }
-
-  if (nested_p)
-    /* Return to the enclosing function.  */
-    pop_function_context ();
-  timevar_pop (TV_EXPAND);
 }
 
 /* Like c_expand_body_1 but only for unnested functions.  */
Index: c-lang.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/c-lang.c,v
retrieving revision 1.116
diff -u -p -r1.116 c-lang.c
--- c-lang.c	25 Aug 2003 19:10:47 -0000	1.116
+++ c-lang.c	29 Aug 2003 22:51:55 -0000
@@ -90,6 +90,9 @@ enum c_language_kind c_language = clk_c;
 #undef LANG_HOOKS_DECL_UNINIT
 #define LANG_HOOKS_DECL_UNINIT c_decl_uninit
 
+#undef LANG_HOOKS_RTL_EXPAND_STMT
+#define LANG_HOOKS_RTL_EXPAND_STMT expand_stmt
+
 /* Attribute hooks.  */
 #undef LANG_HOOKS_COMMON_ATTRIBUTE_TABLE
 #define LANG_HOOKS_COMMON_ATTRIBUTE_TABLE c_common_attribute_table
Index: c-objc-common.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/c-objc-common.c,v
retrieving revision 1.31
diff -u -p -r1.31 c-objc-common.c
--- c-objc-common.c	29 Aug 2003 21:57:05 -0000	1.31
+++ c-objc-common.c	29 Aug 2003 22:51:55 -0000
@@ -289,20 +289,43 @@ static void
 expand_deferred_fns (void)
 {
   unsigned int i;
+  bool reconsider;
 
-  for (i = 0; i < VARRAY_ACTIVE_SIZE (deferred_fns); i++)
+  do
     {
-      tree decl = VARRAY_TREE (deferred_fns, i);
-
-      if (! TREE_ASM_WRITTEN (decl))
+      reconsider = false;
+      for (i = 0; i < VARRAY_ACTIVE_SIZE (deferred_fns); i++)
 	{
-	  /* For static inline functions, delay the decision whether to
-	     emit them or not until wrapup_global_declarations.  */
-	  if (! TREE_PUBLIC (decl))
-	    DECL_DEFER_OUTPUT (decl) = 1;
+	  tree decl = VARRAY_TREE (deferred_fns, i);
+
+	  if (TREE_ASM_WRITTEN (decl))
+	    continue;
+
+	  /* "extern inline" says the symbol exists externally,
+	      which means we should *never* expand it locally 
+	      unless we're actually inlining it.  */
+	  /* ??? Why did we queue these in the first place?  */
+	  if (DECL_DECLARED_INLINE_P (decl) && DECL_EXTERNAL (decl))
+	    continue;
+	      
+	  /* With flag_keep_inline_functions, we're emitting everything,
+	     so we never need to reconsider.  */
+	  if (flag_keep_inline_functions)
+	    ;
+	  /* Must emit all public functions.  C doesn't have COMDAT
+	     functions, so we don't need to check that, like C++.  */
+	  else if (TREE_PUBLIC (decl))
+	    reconsider = true;
+	  /* Must emit if the symbol is referenced.  */
+	  else if (TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (decl)))
+	    reconsider = true;
+	  else
+	    continue;
+
 	  c_expand_deferred_function (decl);
 	}
     }
+  while (reconsider);
 
   deferred_fns = 0;
 }
Index: langhooks-def.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/langhooks-def.h,v
retrieving revision 1.62
diff -u -p -r1.62 langhooks-def.h
--- langhooks-def.h	19 Aug 2003 23:04:36 -0000	1.62
+++ langhooks-def.h	29 Aug 2003 22:51:59 -0000
@@ -123,6 +123,10 @@ extern void lhd_initialize_diagnostics (
 #define LANG_HOOKS_FUNCTION_ENTER_NESTED lhd_do_nothing_f
 #define LANG_HOOKS_FUNCTION_LEAVE_NESTED lhd_do_nothing_f
 
+#define LANG_HOOKS_RTL_EXPAND_START	lhd_do_nothing
+#define LANG_HOOKS_RTL_EXPAND_STMT	(void *) abort
+#define LANG_HOOKS_RTL_EXPAND_END	lhd_do_nothing
+
 /* Attribute hooks.  */
 #define LANG_HOOKS_ATTRIBUTE_TABLE		NULL
 #define LANG_HOOKS_COMMON_ATTRIBUTE_TABLE	NULL
@@ -186,6 +190,12 @@ extern void lhd_initialize_diagnostics (
   LANG_HOOKS_FUNCTION_LEAVE_NESTED		\
 }
 
+#define LANG_HOOKS_RTL_EXPAND_INITIALIZER {	\
+  LANG_HOOKS_RTL_EXPAND_START,			\
+  LANG_HOOKS_RTL_EXPAND_STMT,			\
+  LANG_HOOKS_RTL_EXPAND_END			\
+}
+
 /* Tree dump hooks.  */
 extern bool lhd_tree_dump_dump_tree (void *, tree);
 extern int lhd_tree_dump_type_quals (tree);
@@ -289,7 +299,8 @@ extern int lhd_tree_dump_type_quals (tre
   LANG_HOOKS_CALLGRAPH_INITIALIZER, \
   LANG_HOOKS_TREE_DUMP_INITIALIZER, \
   LANG_HOOKS_DECLS, \
-  LANG_HOOKS_FOR_TYPES_INITIALIZER \
+  LANG_HOOKS_FOR_TYPES_INITIALIZER, \
+  LANG_HOOKS_RTL_EXPAND_INITIALIZER \
 }
 
 #endif /* GCC_LANG_HOOKS_DEF_H */
Index: langhooks.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/langhooks.h,v
retrieving revision 1.71
diff -u -p -r1.71 langhooks.h
--- langhooks.h	22 Aug 2003 06:25:08 -0000	1.71
+++ langhooks.h	29 Aug 2003 22:52:00 -0000
@@ -77,6 +77,19 @@ struct lang_hooks_for_functions
   void (*leave_nested) (struct function *);
 };
 
+/* Lang hooks for rtl code generation.  */
+struct lang_hooks_for_rtl_expansion
+{
+  /* Called after expand_function_start, but before expanding the body.  */
+  void (*start) (void);
+
+  /* Called to expand each statement.  */
+  void (*stmt) (tree);
+
+  /* Called after expanding the body but before expand_function_end.  */
+  void (*end) (void);
+};
+
 /* The following hooks are used by tree-dump.c.  */
 
 struct lang_hooks_for_tree_dump
@@ -386,6 +399,8 @@ struct lang_hooks
   struct lang_hooks_for_decls decls;
 
   struct lang_hooks_for_types types;
+
+  struct lang_hooks_for_rtl_expansion rtl_expand;
 
   /* Whenever you add entries here, make sure you adjust langhooks-def.h
      and langhooks.c accordingly.  */
Index: toplev.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/toplev.h,v
retrieving revision 1.112
diff -u -p -r1.112 toplev.h
--- toplev.h	8 Aug 2003 19:22:42 -0000	1.112
+++ toplev.h	29 Aug 2003 22:52:03 -0000
@@ -66,6 +66,7 @@ extern void inform (const char *, ...) A
 extern void rest_of_decl_compilation (tree, const char *, int, int);
 extern void rest_of_type_compilation (tree, int);
 extern void rest_of_compilation (tree);
+extern void tree_rest_of_compilation (tree);
 
 extern void announce_function (tree);
 
Index: tree-optimize.c
===================================================================
RCS file: tree-optimize.c
diff -N tree-optimize.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tree-optimize.c	29 Aug 2003 22:52:03 -0000
@@ -0,0 +1,250 @@
+/* Control and data flow functions for trees.
+   Copyright 2001, 2002, 2003 Free Software Foundation, Inc.
+
+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 "toplev.h"
+#include "tree.h"
+#include "tree-inline.h"
+#include "flags.h"
+#include "langhooks.h"
+#include "cgraph.h"
+#include "timevar.h"
+#include "tm.h"
+#include "function.h"
+#include "ggc.h"
+
+
+/* Called to move the SAVE_EXPRs for parameter declarations in a
+   nested function into the nested function.  DATA is really the
+   nested FUNCTION_DECL.  */
+
+static tree
+set_save_expr_context (tree *tp,
+		       int *walk_subtrees,
+		       void *data)
+{
+  if (TREE_CODE (*tp) == SAVE_EXPR && !SAVE_EXPR_CONTEXT (*tp))
+    SAVE_EXPR_CONTEXT (*tp) = (tree) data;
+  /* Do not walk back into the SAVE_EXPR_CONTEXT; that will cause
+     circularity.  */
+  else if (DECL_P (*tp))
+    *walk_subtrees = 0;
+
+  return NULL;
+}
+
+/* Clear out the DECL_RTL for the non-static local variables in BLOCK and
+   its sub-blocks.  DATA is the decl of the function being processed.  */
+
+static tree
+clear_decl_rtl (tree *tp, int *walk_subtrees ATTRIBUTE_UNUSED, void *data)
+{
+  bool nonstatic_p, local_p;
+  tree t = *tp;
+
+  switch (TREE_CODE (t))
+    {
+    case VAR_DECL:
+      nonstatic_p = !TREE_STATIC (t) && !DECL_EXTERNAL (t);
+      local_p = DECL_CONTEXT (t) == data;
+      break;
+
+    case PARM_DECL:
+    case LABEL_DECL:
+      nonstatic_p = true;
+      local_p = DECL_CONTEXT (t) == data;
+      break;
+
+    case RESULT_DECL:
+      nonstatic_p = local_p = true;
+      break;
+
+    default:
+      nonstatic_p = local_p = false;
+      break;
+    }
+
+  if (nonstatic_p && local_p)
+    SET_DECL_RTL (t, NULL);
+
+  return NULL;
+}
+
+/* For functions-as-trees languages, this performs all optimization and
+   compilation for FNDECL.  */
+
+void
+tree_rest_of_compilation (tree fndecl)
+{
+  static int nesting = -1;
+
+  timevar_push (TV_EXPAND);
+
+  ++nesting;
+
+  if (flag_unit_at_a_time && !cgraph_global_info_ready)
+    abort ();
+
+  if (nesting > 0)
+    /* Squirrel away our current state.  */
+    push_function_context ();
+
+  /* Initialize the RTL code for the function.  */
+  current_function_decl = fndecl;
+  input_location = DECL_SOURCE_LOCATION (fndecl);
+  init_function_start (fndecl);
+
+  /* This function is being processed in whole-function mode.  */
+  cfun->x_whole_function_mode_p = 1;
+
+  /* Even though we're inside a function body, we still don't want to
+     call expand_expr to calculate the size of a variable-sized array.
+     We haven't necessarily assigned RTL to all variables yet, so it's
+     not safe to try to expand expressions involving them.  */
+  immediate_size_expand = 0;
+  cfun->x_dont_save_pending_sizes_p = 1;
+
+  /* If the function has a variably modified type, there may be
+     SAVE_EXPRs in the parameter types.  Their context must be set to
+     refer to this function; they cannot be expanded in the containing
+     function.  */
+  if (decl_function_context (fndecl)
+      && variably_modified_type_p (TREE_TYPE (fndecl)))
+    walk_tree (&TREE_TYPE (fndecl), set_save_expr_context, fndecl,
+	       NULL);
+
+  /* Set up parameters and prepare for return, for the function.  */
+  expand_function_start (fndecl, 0);
+
+  /* Allow language dialects to perform special processing.  */
+  (*lang_hooks.rtl_expand.start) ();
+
+  /* If this function is `main', emit a call to `__main'
+     to run global initializers, etc.  */
+  if (DECL_NAME (fndecl)
+      && MAIN_NAME_P (DECL_NAME (fndecl))
+      && DECL_FILE_SCOPE_P (fndecl))
+    expand_main_function ();
+
+  /* Generate the RTL for this function.  */
+  (*lang_hooks.rtl_expand.stmt) (DECL_SAVED_TREE (fndecl));
+
+  /* We hard-wired immediate_size_expand to zero above.
+     expand_function_end will decrement this variable.  So, we set the
+     variable to one here, so that after the decrement it will remain
+     zero.  */
+  immediate_size_expand = 1;
+
+  /* Allow language dialects to perform special processing.  */
+  (*lang_hooks.rtl_expand.end) ();
+
+  /* Generate rtl for function exit.  */
+  expand_function_end ();
+
+  /* If this is a nested function, protect the local variables in the stack
+     above us from being collected while we're compiling this function.  */
+  if (nesting > 0)
+    ggc_push_context ();
+
+  /* There's no need to defer outputting this function any more; we
+     know we want to output it.  */
+  DECL_DEFER_OUTPUT (fndecl) = 0;
+
+  /* Run the optimizers and output the assembler code for this function.  */
+  rest_of_compilation (fndecl);
+
+  /* Undo the GC context switch.  */
+  if (nesting > 0)
+    ggc_pop_context ();
+
+  /* If requested, warn about function definitions where the function will
+     return a value (usually of some struct or union type) which itself will
+     take up a lot of stack space.  */
+  if (warn_larger_than && !DECL_EXTERNAL (fndecl) && TREE_TYPE (fndecl))
+    {
+      tree ret_type = TREE_TYPE (TREE_TYPE (fndecl));
+
+      if (ret_type && TYPE_SIZE_UNIT (ret_type)
+	  && TREE_CODE (TYPE_SIZE_UNIT (ret_type)) == INTEGER_CST
+	  && 0 < compare_tree_int (TYPE_SIZE_UNIT (ret_type),
+				   larger_than_size))
+	{
+          const location_t *locus = &DECL_SOURCE_LOCATION (fndecl);
+	  unsigned int size_as_int
+	    = TREE_INT_CST_LOW (TYPE_SIZE_UNIT (ret_type));
+
+	  if (compare_tree_int (TYPE_SIZE_UNIT (ret_type), size_as_int) == 0)
+	    warning ("%Hsize of return value of '%D' is %u bytes",
+                     locus, fndecl, size_as_int);
+	  else
+	    warning ("%Hsize of return value of '%D' is larger than %wd bytes",
+                     locus, fndecl, larger_than_size);
+	}
+    }
+
+  /* ??? Looks like some of this could be combined.  */
+
+  /* If possible, obliterate the body of the function so that it can
+     be garbage collected.  */
+  if (dump_enabled_p (TDI_all))
+    /* Keep the body; we're going to dump it.  */
+    ;
+  else if (DECL_INLINE (fndecl) && flag_inline_trees)
+    /* We might need the body of this function so that we can expand
+       it inline somewhere else.  */
+    ;
+  else
+    /* We don't need the body; blow it away.  */
+    DECL_SAVED_TREE (fndecl) = NULL;
+
+  /* Since we don't need the RTL for this function anymore, stop pointing to
+     it.  That's especially important for LABEL_DECLs, since you can reach all
+     the instructions in the function from the CODE_LABEL stored in the
+     DECL_RTL for the LABEL_DECL.  Walk the BLOCK-tree, clearing DECL_RTL for
+     LABEL_DECLs and non-static local variables.  Note that we must check the
+     context of the variables, otherwise processing a nested function can kill
+     the rtl of a variable from an outer function.  */
+  walk_tree_without_duplicates (&DECL_SAVED_TREE (fndecl),
+				clear_decl_rtl,
+				fndecl);
+
+  if (DECL_SAVED_INSNS (fndecl) == 0 && ! nesting && ! flag_inline_trees)
+    {
+      /* Stop pointing to the local nodes about to be freed.
+	 But DECL_INITIAL must remain nonzero so we know this
+	 was an actual function definition.
+	 For a nested function, this is done in c_pop_function_context.
+	 If rest_of_compilation set this to 0, leave it 0.  */
+      if (DECL_INITIAL (fndecl) != 0)
+	DECL_INITIAL (fndecl) = error_mark_node;
+
+      DECL_ARGUMENTS (fndecl) = 0;
+    }
+
+  if (nesting > 0)
+    /* Return to the enclosing function.  */
+    pop_function_context ();
+
+  --nesting;
+
+  timevar_pop (TV_EXPAND);
+}
Index: cp/cp-lang.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/cp-lang.c,v
retrieving revision 1.62
diff -u -p -r1.62 cp-lang.c
--- cp/cp-lang.c	19 Aug 2003 20:29:01 -0000	1.62
+++ cp/cp-lang.c	29 Aug 2003 22:52:06 -0000
@@ -115,6 +115,11 @@ static void cxx_initialize_diagnostics (
 #undef LANG_HOOKS_FUNCTION_FINAL
 #define LANG_HOOKS_FUNCTION_FINAL cxx_pop_function_context
 
+#undef LANG_HOOKS_RTL_EXPAND_START
+#define LANG_HOOKS_RTL_EXPAND_START cxx_expand_function_start
+#undef LANG_HOOKS_RTL_EXPAND_STMT
+#define LANG_HOOKS_RTL_EXPAND_STMT expand_stmt
+
 /* Attribute hooks.  */
 #undef LANG_HOOKS_COMMON_ATTRIBUTE_TABLE
 #define LANG_HOOKS_COMMON_ATTRIBUTE_TABLE c_common_attribute_table
Index: cp/cp-tree.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/cp-tree.h,v
retrieving revision 1.907
diff -u -p -r1.907 cp-tree.h
--- cp/cp-tree.h	25 Aug 2003 15:47:38 -0000	1.907
+++ cp/cp-tree.h	29 Aug 2003 22:52:11 -0000
@@ -4137,6 +4137,7 @@ extern tree finish_alignof			(tree);
 extern void finish_decl_cleanup                 (tree, tree);
 extern void finish_eh_cleanup                   (tree);
 extern void expand_body                         (tree);
+extern void cxx_expand_function_start		(void);
 extern tree nullify_returns_r		      (tree *, int *, void *);
 extern void do_pushlevel                        (scope_kind);
 extern tree do_poplevel                         (void);
Index: cp/decl.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/decl.c,v
retrieving revision 1.1118
diff -u -p -r1.1118 decl.c
--- cp/decl.c	26 Aug 2003 09:16:15 -0000	1.1118
+++ cp/decl.c	29 Aug 2003 22:52:22 -0000
@@ -13539,7 +13539,7 @@ start_function (tree declspecs, tree dec
      CFUN set up, and our per-function variables initialized.
      FIXME factor out the non-RTL stuff.  */
   bl = current_binding_level;
-  init_function_start (decl1);
+  allocate_struct_function (decl1);
   current_binding_level = bl;
 
   /* Even though we're inside a function body, we still don't want to
@@ -14084,6 +14084,10 @@ finish_function (int flags)
     }
   poplevel (1, 0, 1);
 
+  /* Statements should always be full-expressions at the outermost set
+     of curly braces for a function.  */
+  my_friendly_assert (stmts_are_full_exprs_p (), 19990831);
+
   /* Set up the named return value optimization, if we can.  Here, we
      eliminate the copy from the nrv into the RESULT_DECL and any cleanup
      for the nrv.  genrtl_start_function and declare_return_variable
@@ -14154,12 +14158,9 @@ finish_function (int flags)
 	 inline function, as we might never be compiled separately.  */
       && (DECL_INLINE (fndecl) || processing_template_decl))
     warning ("no return statement in function returning non-void");
-    
-  /* Clear out memory we no longer need.  */
-  free_after_parsing (cfun);
-  /* Since we never call rest_of_compilation, we never clear
-     CFUN.  Do so explicitly.  */
-  free_after_compilation (cfun);
+
+  /* We're leaving the context of this function, so zap cfun.  It's still in
+     DECL_SAVED_INSNS, and we'll restore it in tree_rest_of_compilation.  */
   cfun = NULL;
 
   /* If this is an in-class inline definition, we may have to pop the
@@ -14479,6 +14480,31 @@ cxx_push_function_context (struct functi
   /* Whenever we start a new function, we destroy temporaries in the
      usual way.  */
   current_stmt_tree ()->stmts_are_full_exprs_p = 1;
+
+  if (f->decl)
+    {
+      tree fn = f->decl;
+
+      current_function_is_thunk = DECL_THUNK_P (fn);
+
+      if (DECL_SAVED_FUNCTION_DATA (fn))
+	{
+	  /* If we already parsed this function, and we're just expanding it
+	     now, restore saved state.  */
+	  *cp_function_chain = *DECL_SAVED_FUNCTION_DATA (fn);
+
+	  /* If we decided that we didn't want to inline this function,
+	     make sure the back-end knows that.  */
+	  if (!current_function_cannot_inline)
+	    current_function_cannot_inline = cp_function_chain->cannot_inline;
+
+	  /* We don't need the saved data anymore.  Unless this is an inline
+	     function; we need the named return value info for
+	     cp_copy_res_decl_for_inlining.  */
+	  if (! DECL_INLINE (fn))
+	    DECL_SAVED_FUNCTION_DATA (fn) = NULL;
+	}
+    }
 }
 
 /* Free the language-specific parts of F, now that we've finished
Index: cp/optimize.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/optimize.c,v
retrieving revision 1.94
diff -u -p -r1.94 optimize.c
--- cp/optimize.c	5 Aug 2003 14:11:53 -0000	1.94
+++ cp/optimize.c	29 Aug 2003 22:52:22 -0000
@@ -49,17 +49,6 @@ optimize_function (tree fn)
 {
   dump_function (TDI_original, fn);
 
-  /* While in this function, we may choose to go off and compile
-     another function.  For example, we might instantiate a function
-     in the hopes of inlining it.  Normally, that wouldn't trigger any
-     actual RTL code-generation -- but it will if the template is
-     actually needed.  (For example, if it's address is taken, or if
-     some other function already refers to the template.)  If
-     code-generation occurs, then garbage collection will occur, so we
-     must protect ourselves, just as we do while building up the body
-     of the function.  */
-  ++function_depth;
-
   if (flag_inline_trees
       /* We do not inline thunks, as (a) the backend tries to optimize
          the call to the thunkee, (b) tree based inlining breaks that
@@ -71,9 +60,6 @@ optimize_function (tree fn)
 
       dump_function (TDI_inlined, fn);
     }
-  
-  /* Undo the call to ggc_push_context above.  */
-  --function_depth;
   
   dump_function (TDI_optimized, fn);
 }
Index: cp/semantics.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/semantics.c,v
retrieving revision 1.351
diff -u -p -r1.351 semantics.c
--- cp/semantics.c	26 Aug 2003 08:51:34 -0000	1.351
+++ cp/semantics.c	29 Aug 2003 22:52:25 -0000
@@ -59,9 +59,7 @@ static void genrtl_try_block (tree);
 static void genrtl_eh_spec_block (tree);
 static void genrtl_handler (tree);
 static void cp_expand_stmt (tree);
-static void genrtl_start_function (tree);
-static void genrtl_finish_function (tree);
-static tree clear_decl_rtl (tree *, int *, void *);
+
 
 /* Finish processing the COND, the SUBSTMT condition for STMT.  */
 
@@ -2859,9 +2857,6 @@ expand_body (tree fn)
   location_t saved_loc;
   tree saved_function;
   
-  if (flag_unit_at_a_time && !cgraph_global_info_ready)
-    abort ();
-
   /* Compute the appropriate object-file linkage for inline
      functions.  */
   if (DECL_DECLARED_INLINE_P (fn))
@@ -2875,61 +2870,35 @@ expand_body (tree fn)
   if (DECL_EXTERNAL (fn))
     return;
 
-  /* Save the current file name and line number.  When we expand the
-     body of the function, we'll set INPUT_LOCATION so that
-     error-messages come out in the right places.  */
+  /* ??? When is this needed?  */
   saved_loc = input_location;
   saved_function = current_function_decl;
-  input_location = DECL_SOURCE_LOCATION (fn);
-  current_function_decl = fn;
 
   timevar_push (TV_INTEGRATION);
-
-  /* Optimize the body of the function before expanding it.  */
   optimize_function (fn);
-
   timevar_pop (TV_INTEGRATION);
-  timevar_push (TV_EXPAND);
-
-  genrtl_start_function (fn);
-  current_function_is_thunk = DECL_THUNK_P (fn);
-
-  /* Expand the body.  */
-  expand_stmt (DECL_SAVED_TREE (fn));
-
-  /* Statements should always be full-expressions at the outermost set
-     of curly braces for a function.  */
-  my_friendly_assert (stmts_are_full_exprs_p (), 19990831);
-
-  /* The outermost statement for a function contains the line number
-     recorded when we finished processing the function.  */
-  input_line = STMT_LINENO (DECL_SAVED_TREE (fn));
 
-  /* Generate code for the function.  */
-  genrtl_finish_function (fn);
+  tree_rest_of_compilation (fn);
 
-  /* If possible, obliterate the body of the function so that it can
-     be garbage collected.  */
-  if (dump_enabled_p (TDI_all))
-    /* Keep the body; we're going to dump it.  */
-    ;
-  else if (DECL_INLINE (fn) && flag_inline_trees)
-    /* We might need the body of this function so that we can expand
-       it inline somewhere else.  */
-    ;
-  else
-    /* We don't need the body; blow it away.  */
-    DECL_SAVED_TREE (fn) = NULL_TREE;
-
-  /* And restore the current source position.  */
   current_function_decl = saved_function;
   input_location = saved_loc;
-  extract_interface_info ();
 
-  timevar_pop (TV_EXPAND);
+  extract_interface_info ();
 
   /* Emit any thunks that should be emitted at the same time as FN.  */
   emit_associated_thunks (fn);
+
+  /* If this function is marked with the constructor attribute, add it
+     to the list of functions to be called along with constructors
+     from static duration objects.  */
+  if (DECL_STATIC_CONSTRUCTOR (fn))
+    static_ctors = tree_cons (NULL_TREE, fn, static_ctors);
+
+  /* If this function is marked with the destructor attribute, add it
+     to the list of functions to be called along with destructors from
+     static duration objects.  */
+  if (DECL_STATIC_DESTRUCTOR (fn))
+    static_dtors = tree_cons (NULL_TREE, fn, static_dtors);
 }
 
 /* Generate RTL for FN.  */
@@ -3058,197 +3027,16 @@ nullify_returns_r (tree* tp, int* walk_s
 
 /* Start generating the RTL for FN.  */
 
-static void
-genrtl_start_function (tree fn)
+void
+cxx_expand_function_start (void)
 {
-  /* Tell everybody what function we're processing.  */
-  current_function_decl = fn;
-  /* Get the RTL machinery going for this function.  */
-  init_function_start (fn);
   /* Let everybody know that we're expanding this function, not doing
      semantic analysis.  */
   expanding_p = 1;
 
-  /* Even though we're inside a function body, we still don't want to
-     call expand_expr to calculate the size of a variable-sized array.
-     We haven't necessarily assigned RTL to all variables yet, so it's
-     not safe to try to expand expressions involving them.  */
-  immediate_size_expand = 0;
-  cfun->x_dont_save_pending_sizes_p = 1;
-
-  /* Let the user know we're compiling this function.  */
-  announce_function (fn);
-
-  /* Initialize the per-function data.  */
-  my_friendly_assert (!DECL_PENDING_INLINE_P (fn), 20000911);
-  if (DECL_SAVED_FUNCTION_DATA (fn))
-    {
-      /* If we already parsed this function, and we're just expanding it
-	 now, restore saved state.  */
-      *cp_function_chain = *DECL_SAVED_FUNCTION_DATA (fn);
-
-      /* This function is being processed in whole-function mode; we
-	 already did semantic analysis.  */
-      cfun->x_whole_function_mode_p = 1;
-
-      /* If we decided that we didn't want to inline this function,
-	 make sure the back-end knows that.  */
-      if (!current_function_cannot_inline)
-	current_function_cannot_inline = cp_function_chain->cannot_inline;
-
-      /* We don't need the saved data anymore.  Unless this is an inline
-         function; we need the named return value info for
-         cp_copy_res_decl_for_inlining.  */
-      if (! DECL_INLINE (fn))
-	DECL_SAVED_FUNCTION_DATA (fn) = NULL;
-    }
-
-  /* Keep track of how many functions we're presently expanding.  */
-  ++function_depth;
-
-  /* Create a binding level for the parameters.  */
-  expand_function_start (fn, /*parms_have_cleanups=*/0);
-  /* If this function is `main'.  */
-  if (DECL_MAIN_P (fn))
-    expand_main_function ();
-
   /* Give our named return value the same RTL as our RESULT_DECL.  */
   if (current_function_return_value)
-    COPY_DECL_RTL (DECL_RESULT (fn), current_function_return_value);
-}
-
-/* Finish generating the RTL for FN.  */
-
-static void
-genrtl_finish_function (tree fn)
-{
-  tree t;
-
-#if 0
-  if (write_symbols != NO_DEBUG)
-    {
-      /* Keep this code around in case we later want to control debug info
-	 based on whether a type is "used".  (jason 1999-11-11) */
-
-      tree ttype = target_type (fntype);
-      tree parmdecl;
-
-      if (IS_AGGR_TYPE (ttype))
-	/* Let debugger know it should output info for this type.  */
-	note_debug_info_needed (ttype);
-
-      for (parmdecl = DECL_ARGUMENTS (fndecl); parmdecl; parmdecl = TREE_CHAIN (parmdecl))
-	{
-	  ttype = target_type (TREE_TYPE (parmdecl));
-	  if (IS_AGGR_TYPE (ttype))
-	    /* Let debugger know it should output info for this type.  */
-	    note_debug_info_needed (ttype);
-	}
-    }
-#endif
-
-  /* Clean house because we will need to reorder insns here.  */
-  do_pending_stack_adjust ();
-
-  /* If we have a named return value, we need to force a return so that
-     the return register is USEd.  */
-  if (DECL_NAME (DECL_RESULT (fn)))
-    emit_jump (return_label);
-
-  /* We hard-wired immediate_size_expand to zero in start_function.
-     Expand_function_end will decrement this variable.  So, we set the
-     variable to one here, so that after the decrement it will remain
-     zero.  */
-  immediate_size_expand = 1;
-
-  /* Generate rtl for function exit.  */
-  expand_function_end ();
-
-  /* If this is a nested function (like a template instantiation that
-     we're compiling in the midst of compiling something else), push a
-     new GC context.  That will keep local variables on the stack from
-     being collected while we're doing the compilation of this
-     function.  */
-  if (function_depth > 1)
-    ggc_push_context ();
-
-  /* There's no need to defer outputting this function any more; we
-     know we want to output it.  */
-  DECL_DEFER_OUTPUT (fn) = 0;
-
-  /* Run the optimizers and output the assembler code for this
-     function.  */
-  rest_of_compilation (fn);
-
-  /* Undo the call to ggc_push_context above.  */
-  if (function_depth > 1)
-    ggc_pop_context ();
-
-#if 0
-  /* Keep this code around in case we later want to control debug info
-     based on whether a type is "used".  (jason 1999-11-11) */
-
-  if (ctype && TREE_ASM_WRITTEN (fn))
-    note_debug_info_needed (ctype);
-#endif
-
-  /* If this function is marked with the constructor attribute, add it
-     to the list of functions to be called along with constructors
-     from static duration objects.  */
-  if (DECL_STATIC_CONSTRUCTOR (fn))
-    static_ctors = tree_cons (NULL_TREE, fn, static_ctors);
-
-  /* If this function is marked with the destructor attribute, add it
-     to the list of functions to be called along with destructors from
-     static duration objects.  */
-  if (DECL_STATIC_DESTRUCTOR (fn))
-    static_dtors = tree_cons (NULL_TREE, fn, static_dtors);
-
-  --function_depth;
-
-  /* In C++, we should never be saving RTL for the function.  */
-  my_friendly_assert (!DECL_SAVED_INSNS (fn), 20010903);
-
-  /* Since we don't need the RTL for this function anymore, stop
-     pointing to it.  That's especially important for LABEL_DECLs,
-     since you can reach all the instructions in the function from the
-     CODE_LABEL stored in the DECL_RTL for the LABEL_DECL.  Walk the
-     BLOCK-tree, clearing DECL_RTL for LABEL_DECLs and non-static
-     local variables.  */
-  walk_tree_without_duplicates (&DECL_SAVED_TREE (fn),
-				clear_decl_rtl,
-				NULL);
-
-  /* Clear out the RTL for the arguments.  */
-  for (t = DECL_ARGUMENTS (fn); t; t = TREE_CHAIN (t))
-    {
-      SET_DECL_RTL (t, NULL_RTX);
-      DECL_INCOMING_RTL (t) = NULL_RTX;
-    }
-
-  if (!(flag_inline_trees && DECL_INLINE (fn)))
-    /* DECL_INITIAL must remain nonzero so we know this was an
-       actual function definition.  */
-    DECL_INITIAL (fn) = error_mark_node;
-  
-  /* Let the error reporting routines know that we're outside a
-     function.  For a nested function, this value is used in
-     pop_cp_function_context and then reset via pop_function_context.  */
-  current_function_decl = NULL_TREE;
-}
-
-/* Clear out the DECL_RTL for the non-static variables in BLOCK and
-   its sub-blocks.  */
-
-static tree
-clear_decl_rtl (tree* tp, 
-                int* walk_subtrees ATTRIBUTE_UNUSED , 
-                void* data ATTRIBUTE_UNUSED )
-{
-  if (nonstatic_local_decl_p (*tp)) 
-    SET_DECL_RTL (*tp, NULL_RTX);
-    
-  return NULL_TREE;
+    COPY_DECL_RTL (DECL_RESULT (cfun->decl), current_function_return_value);
 }
 
 /* Perform initialization related to this module.  */
Index: objc/objc-lang.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/objc/objc-lang.c,v
retrieving revision 1.38
diff -u -p -r1.38 objc-lang.c
--- objc/objc-lang.c	19 Aug 2003 20:29:00 -0000	1.38
+++ objc/objc-lang.c	29 Aug 2003 22:52:25 -0000
@@ -87,6 +87,9 @@ enum c_language_kind c_language = clk_ob
 #undef LANG_HOOKS_FUNCTION_LEAVE_NESTED
 #define LANG_HOOKS_FUNCTION_LEAVE_NESTED c_pop_function_context
 
+#undef LANG_HOOKS_RTL_EXPAND_STMT
+#define LANG_HOOKS_RTL_EXPAND_STMT expand_stmt
+
 /* Attribute hooks.  */
 #undef LANG_HOOKS_COMMON_ATTRIBUTE_TABLE
 #define LANG_HOOKS_COMMON_ATTRIBUTE_TABLE c_common_attribute_table


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