]> git.ipfire.org Git - thirdparty/gcc.git/blobdiff - gcc/tree-optimize.c
Merge tree-ssa-20020619-branch into mainline.
[thirdparty/gcc.git] / gcc / tree-optimize.c
index f946593dc337b5c71ec6d8ede26f8075b4417728..80a2b41b6a14bf941d05afdab9e28c7919ad908e 100644 (file)
@@ -1,5 +1,6 @@
 /* Control and data flow functions for trees.
    Copyright 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
+   Contributed by Diego Novillo <dnovillo@redhat.com>
 
 This file is part of GCC.
 
@@ -21,18 +22,424 @@ Boston, MA 02111-1307, USA.  */
 #include "config.h"
 #include "system.h"
 #include "coretypes.h"
-#include "toplev.h"
+#include "tm.h"
 #include "tree.h"
-#include "tree-inline.h"
+#include "rtl.h"
+#include "tm_p.h"
+#include "hard-reg-set.h"
+#include "basic-block.h"
+#include "output.h"
+#include "expr.h"
+#include "diagnostic.h"
+#include "basic-block.h"
 #include "flags.h"
-#include "langhooks.h"
-#include "cgraph.h"
+#include "tree-flow.h"
+#include "tree-dump.h"
 #include "timevar.h"
-#include "tm.h"
 #include "function.h"
+#include "langhooks.h"
+#include "toplev.h"
+#include "flags.h"
+#include "cgraph.h"
+#include "tree-inline.h"
+#include "tree-mudflap.h"
+#include "tree-pass.h"
+#include "tree-alias-common.h"
 #include "ggc.h"
+#include "cgraph.h"
+
+
+/* Global variables used to communicate with passes.  */
+int dump_flags;
+bitmap vars_to_rename;
+bool in_gimple_form;
+
+/* The root of the compilation pass tree, once constructed.  */
+static struct tree_opt_pass *all_passes;
+
+/* Pass: gimplify the function if it's not been done.  */
+
+static void
+execute_gimple (void)
+{
+  /* We have this test here rather than as the gate because we always
+     want to dump the original gimplified function.  */
+  if (!lang_hooks.gimple_before_inlining)
+    gimplify_function_tree (current_function_decl);
+}
+
+static struct tree_opt_pass pass_gimple = 
+{
+  "gimple",                            /* name */
+  NULL,                                        /* gate */
+  execute_gimple,                      /* execute */
+  NULL,                                        /* sub */
+  NULL,                                        /* next */
+  0,                                   /* static_pass_number */
+  0,                                   /* tv_id */
+  0,                                   /* properties_required */
+  PROP_gimple_any,                     /* properties_provided */
+  0,                                   /* properties_destroyed */
+  0,                                   /* todo_flags_start */
+  TODO_dump_func                       /* todo_flags_finish */
+};
+
+/* Pass: replace the outermost BIND_EXPR.  We removed all of them while
+   optimizing, but the tree->rtl expander requires it.  */
+
+static void
+execute_rebuild_bind (void)
+{
+  DECL_SAVED_TREE (current_function_decl)
+    = build (BIND_EXPR, void_type_node, NULL_TREE,
+            DECL_SAVED_TREE (current_function_decl), NULL_TREE);
+}
+
+static struct tree_opt_pass pass_rebuild_bind = 
+{
+  NULL,                                        /* name */
+  NULL,                                        /* gate */
+  execute_rebuild_bind,                        /* execute */
+  NULL,                                        /* sub */
+  NULL,                                        /* next */
+  0,                                   /* static_pass_number */
+  0,                                   /* tv_id */
+  0,                                   /* properties_required */
+  0,                                   /* properties_provided */
+  0,                                   /* properties_destroyed */
+  0,                                   /* todo_flags_start */
+  0                                    /* todo_flags_finish */
+};
+
+/* Gate: execute, or not, all of the non-trivial optimizations.  */
+
+static bool
+gate_all_optimizations (void)
+{
+  return (optimize >= 1
+         /* Don't bother doing anything if the program has errors.  */
+         && !(errorcount || sorrycount));
+}
+
+static struct tree_opt_pass pass_all_optimizations =
+{
+  NULL,                                        /* name */
+  gate_all_optimizations,              /* gate */
+  NULL,                                        /* execute */
+  NULL,                                        /* sub */
+  NULL,                                        /* next */
+  0,                                   /* static_pass_number */
+  0,                                   /* tv_id */
+  0,                                   /* properties_required */
+  0,                                   /* properties_provided */
+  0,                                   /* properties_destroyed */
+  0,                                   /* todo_flags_start */
+  0                                    /* todo_flags_finish */
+};
+
+/* Pass: do the actions required to finish with tree-ssa optimization
+   passes.  */
+
+static void
+execute_del_cfg (void)
+{
+  basic_block bb;
+  tree *chain;
+
+  /* ??? This isn't the right place for this.  Worse, it got computed
+     more or less at random in various passes.  */
+  free_dominance_info (CDI_DOMINATORS);
+
+  /* Emit gotos for implicit jumps.  */
+  disband_implicit_edges ();
+
+  /* Remove the ssa structures.  Do it here since this includes statement
+     annotations that need to be intact during disband_implicit_edges.  */
+  delete_tree_ssa ();
+
+  /* Re-chain the statements from the blocks.  */
+  chain = &DECL_SAVED_TREE (current_function_decl);
+  *chain = alloc_stmt_list ();
+  FOR_EACH_BB (bb)
+    {
+      append_to_statement_list_force (bb->stmt_list, chain);
+    }
+
+  /* And get rid of the cfg.  */
+  delete_tree_cfg ();
+}
+
+static struct tree_opt_pass pass_del_cfg =
+{
+  NULL,                                        /* name */
+  NULL,                                        /* gate */
+  execute_del_cfg,                     /* execute */
+  NULL,                                        /* sub */
+  NULL,                                        /* next */
+  0,                                   /* static_pass_number */
+  0,                                   /* tv_id */
+  PROP_cfg,                            /* properties_required */
+  0,                                   /* properties_provided */
+  PROP_cfg,                            /* properties_destroyed */
+  0,                                   /* todo_flags_start */
+  0                                    /* todo_flags_finish */
+};
+
+/* Iterate over the pass tree allocating dump file numbers.  We want
+   to do this depth first, and independent of whether the pass is
+   enabled or not.  */
+
+static void
+register_one_dump_file (struct tree_opt_pass *pass)
+{
+  char *dot_name, *flag_name;
+  char num[10];
+
+  if (!pass->name)
+    return;
+
+  /* See below in dup_pass_1.  */
+  num[0] = '\0';
+  if (pass->static_pass_number)
+    sprintf (num, "%d", ((int) pass->static_pass_number < 0
+                        ? 1 : pass->static_pass_number));
+
+  dot_name = concat (".", pass->name, num, NULL);
+  flag_name = concat ("tree-", pass->name, num, NULL);
+
+  pass->static_pass_number = dump_register (dot_name, flag_name);
+}
+
+static void 
+register_dump_files (struct tree_opt_pass *pass)
+{
+  do
+    {
+      register_one_dump_file (pass);
+      if (pass->sub)
+       register_dump_files (pass->sub);
+      pass = pass->next;
+    }
+  while (pass);
+}
+
+/* Duplicate a pass that's to be run more than once.  */
+
+static struct tree_opt_pass *
+dup_pass_1 (struct tree_opt_pass *pass)
+{
+  struct tree_opt_pass *new;
+
+  new = xmalloc (sizeof (*new));
+  memcpy (new, pass, sizeof (*new));
+
+  /* Indicate to register_dump_files that this pass has duplicates,
+     and so it should rename the dump file.  The first instance will
+     be < 0, and be number of duplicates = -static_pass_number + 1.
+     Subsequent instances will be > 0 and just the duplicate number.  */
+  if (pass->name)
+    {
+      int n, p = pass->static_pass_number;
+       
+      if (p)
+       n = -(--p) + 1;
+      else
+       n = 2, p = -1;
+
+      pass->static_pass_number = p;
+      new->static_pass_number = n;
+    }
+
+  return new;
+}
+
+/* Construct the pass tree.  */
+
+void
+init_tree_optimization_passes (void)
+{
+  struct tree_opt_pass **p;
+
+#define NEXT_PASS(PASS) (*p = &PASS, p = &(*p)->next)
+#define DUP_PASS(PASS)  (*dup_pass_1 (&PASS))
+
+  p = &all_passes;
+  NEXT_PASS (pass_gimple);
+  NEXT_PASS (pass_remove_useless_stmts);
+  NEXT_PASS (pass_mudflap_1);
+  NEXT_PASS (pass_lower_cf);
+  NEXT_PASS (pass_lower_eh);
+  NEXT_PASS (pass_all_optimizations);
+  NEXT_PASS (pass_mudflap_2);
+  NEXT_PASS (pass_rebuild_bind);
+  *p = NULL;
+
+  p = &pass_all_optimizations.sub;
+  NEXT_PASS (pass_build_cfg);
+  NEXT_PASS (pass_tree_profile);
+  NEXT_PASS (pass_referenced_vars);
+  NEXT_PASS (pass_build_pta);
+  NEXT_PASS (pass_build_ssa);
+  NEXT_PASS (pass_rename_ssa_copies);
+  NEXT_PASS (pass_early_warn_uninitialized);
+  NEXT_PASS (pass_dce);
+  NEXT_PASS (pass_dominator);
+  NEXT_PASS (pass_redundant_phi);
+  NEXT_PASS (DUP_PASS (pass_dce));
+  NEXT_PASS (pass_forwprop);
+  NEXT_PASS (pass_phiopt);
+  NEXT_PASS (pass_may_alias);
+  NEXT_PASS (pass_tail_recursion);
+  NEXT_PASS (pass_ch);
+  NEXT_PASS (pass_del_pta);
+  NEXT_PASS (pass_profile);
+  NEXT_PASS (pass_lower_complex);
+  NEXT_PASS (pass_sra);
+  NEXT_PASS (DUP_PASS (pass_rename_ssa_copies));
+  NEXT_PASS (DUP_PASS (pass_dominator));
+  NEXT_PASS (DUP_PASS (pass_redundant_phi));
+  NEXT_PASS (DUP_PASS (pass_dce));
+  NEXT_PASS (pass_dse);
+  NEXT_PASS (DUP_PASS (pass_forwprop));
+  NEXT_PASS (DUP_PASS (pass_phiopt));
+  NEXT_PASS (pass_ccp);
+  NEXT_PASS (DUP_PASS (pass_redundant_phi));
+  NEXT_PASS (pass_fold_builtins);
+  NEXT_PASS (pass_split_crit_edges);
+  NEXT_PASS (pass_pre);
+  NEXT_PASS (DUP_PASS (pass_dominator));
+  NEXT_PASS (DUP_PASS (pass_redundant_phi));
+  NEXT_PASS (pass_cd_dce);
+  NEXT_PASS (DUP_PASS (pass_dse));
+  NEXT_PASS (DUP_PASS (pass_forwprop));
+  NEXT_PASS (DUP_PASS (pass_phiopt));
+  NEXT_PASS (pass_tail_calls);
+  NEXT_PASS (pass_late_warn_uninitialized);
+  NEXT_PASS (pass_warn_function_return);
+  NEXT_PASS (pass_del_ssa);
+  NEXT_PASS (pass_nrv);
+  NEXT_PASS (pass_remove_useless_vars);
+  NEXT_PASS (pass_del_cfg);
+  *p = NULL;
+
+#undef NEXT_PASS
+#undef DUP_PASS
+
+  /* Register the passes with the tree dump code.  */
+  register_dump_files (all_passes);
+}
+
+static void execute_pass_list (struct tree_opt_pass *);
+
+static unsigned int current_properties;
+static unsigned int last_verified;
+
+static void
+execute_todo (unsigned int flags)
+{
+  if (flags & TODO_rename_vars)
+    {
+      if (bitmap_first_set_bit (vars_to_rename) >= 0)
+       rewrite_into_ssa ();
+      BITMAP_XFREE (vars_to_rename);
+    }
+
+  if ((flags & TODO_dump_func) && dump_file)
+    dump_function_to_file (current_function_decl,
+                          dump_file, dump_flags);
+
+  if (flags & TODO_ggc_collect)
+    ggc_collect ();
+
+#ifdef ENABLE_CHECKING
+  if (flags & TODO_verify_ssa)
+    verify_ssa ();
+  if (flags & TODO_verify_flow)
+    verify_flow_info ();
+  if (flags & TODO_verify_stmts)
+    verify_stmts ();
+#endif
+}
+
+static bool
+execute_one_pass (struct tree_opt_pass *pass)
+{
+  unsigned int todo; 
+
+  /* See if we're supposed to run this pass.  */
+  if (pass->gate && !pass->gate ())
+    return false;
+
+  /* Verify that all required properties are present.  */
+  if (pass->properties_required & ~current_properties)
+    abort ();
+
+  /* Run pre-pass verification.  */
+  todo = pass->todo_flags_start & ~last_verified;
+  if (todo)
+    execute_todo (todo);
+
+  /* If a dump file name is present, open it if enabled.  */
+  if (pass->static_pass_number)
+    {
+      dump_file = dump_begin (pass->static_pass_number, &dump_flags);
+      if (dump_file)
+       {
+         const char *dname, *aname;
+         dname = (*lang_hooks.decl_printable_name) (current_function_decl, 2);
+         aname = (IDENTIFIER_POINTER
+                  (DECL_ASSEMBLER_NAME (current_function_decl)));
+         fprintf (dump_file, "\n;; Function %s (%s)\n\n", dname, aname);
+       }
+    }
+
+  /* If a timevar is present, start it.  */
+  if (pass->tv_id)
+    timevar_push (pass->tv_id);
+
+  /* If the pass is requesting ssa variable renaming, allocate the bitmap.  */
+  if (pass->todo_flags_finish & TODO_rename_vars)
+    vars_to_rename = BITMAP_XMALLOC ();
 
+  /* Do it!  */
+  if (pass->execute)
+    pass->execute ();
 
+  /* Run post-pass cleanup and verification.  */
+  todo = pass->todo_flags_finish;
+  last_verified = todo & TODO_verify_all;
+  if (todo)
+    execute_todo (todo);
+
+  /* Update properties.  */
+  current_properties &= ~pass->properties_destroyed;
+  current_properties |= pass->properties_provided;
+
+  /* Close down timevar and dump file.  */
+  if (pass->tv_id)
+    timevar_pop (pass->tv_id);
+  if (dump_file)
+    {
+      dump_end (pass->static_pass_number, dump_file);
+      dump_file = NULL;
+    }
+
+  return true;
+}
+
+static void
+execute_pass_list (struct tree_opt_pass *pass)
+{
+  do
+    {
+      if (execute_one_pass (pass) && pass->sub)
+       execute_pass_list (pass->sub);
+      pass = pass->next;
+    }
+  while (pass);
+}
+
+\f
 /* Called to move the SAVE_EXPRs for parameter declarations in a
    nested function into the nested function.  DATA is really the
    nested FUNCTION_DECL.  */
@@ -52,43 +459,6 @@ set_save_expr_context (tree *tp,
   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_function_context (t) == data;
-      break;
-
-    case PARM_DECL:
-    case LABEL_DECL:
-      nonstatic_p = true;
-      local_p = decl_function_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.  */
 
@@ -96,7 +466,7 @@ void
 tree_rest_of_compilation (tree fndecl, bool nested_p)
 {
   location_t saved_loc;
-  struct cgraph_node *node, *saved_node = NULL;
+  struct cgraph_node *saved_node = NULL, *node;
 
   timevar_push (TV_EXPAND);
 
@@ -152,21 +522,34 @@ tree_rest_of_compilation (tree fndecl, bool nested_p)
        }
     }
 
+  /* Note that the folders should only create gimple expressions.
+     This is a hack until the new folder is ready.  */
+  in_gimple_form = true;
+
+  /* Perform all tree transforms and optimizations.  */
+  execute_pass_list (all_passes);
+
+  /* Note that the folders can create non-gimple expressions again.  */
+  in_gimple_form = false;
+
   /* 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)
+  if (decl_function_context (fndecl) == current_function_decl
       && variably_modified_type_p (TREE_TYPE (fndecl)))
     walk_tree (&TREE_TYPE (fndecl), set_save_expr_context, fndecl,
               NULL);
 
+  /* Expand the variables recorded during gimple lowering.  This must
+     occur before the call to expand_function_start to ensure that
+     all used variables are expanded before we expand anything on the
+     PENDING_SIZES list.  */
+  expand_used_vars ();
+
   /* 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)
@@ -175,7 +558,7 @@ tree_rest_of_compilation (tree fndecl, bool nested_p)
     expand_main_function ();
 
   /* Generate the RTL for this function.  */
-  lang_hooks.rtl_expand.stmt (DECL_SAVED_TREE (fndecl));
+  expand_expr_stmt_value (DECL_SAVED_TREE (fndecl), 0, 0);
 
   /* We hard-wired immediate_size_expand to zero above.
      expand_function_end will decrement this variable.  So, we set the
@@ -183,9 +566,14 @@ tree_rest_of_compilation (tree fndecl, bool nested_p)
      zero.  */
   immediate_size_expand = 1;
 
-  /* Allow language dialects to perform special processing.  */
-  lang_hooks.rtl_expand.end ();
+  /* Make sure the locus is set to the end of the function, so that 
+     epilogue line numbers and warnings are set properly.  */
+  if (cfun->function_end_locus.file)
+    input_location = cfun->function_end_locus;
 
+  /* The following insns belong to the top scope.  */
+  record_block_change (DECL_INITIAL (current_function_decl));
+  
   /* Generate rtl for function exit.  */
   expand_function_end ();
 
@@ -201,10 +589,30 @@ tree_rest_of_compilation (tree fndecl, bool nested_p)
   /* Run the optimizers and output the assembler code for this function.  */
   rest_of_compilation (fndecl);
 
+  /* Restore original body if still needed.  */
+  if (cfun->saved_tree)
+    {
+      DECL_SAVED_TREE (fndecl) = cfun->saved_tree;
+      DECL_ARGUMENTS (fndecl) = cfun->saved_args;
 
-  /* Undo the GC context switch.  */
-  if (nested_p)
-    ggc_pop_context ();
+      /* When not in unit-at-a-time mode, we must preserve out of line copy
+        representing node before inlining.  Restore original outgoing edges
+        using clone we created earlier.  */
+      if (!flag_unit_at_a_time)
+       {
+         struct cgraph_edge *e;
+         while (node->callees)
+           cgraph_remove_edge (node->callees);
+         node->callees = saved_node->callees;
+         saved_node->callees = NULL;
+         for (e = saved_node->callees; e; e = e->next_callee)
+           e->caller = node;
+         cgraph_remove_node (saved_node);
+       }
+    }
+  else
+    DECL_SAVED_TREE (fndecl) = NULL;
+  cfun = 0;
 
   /* If requested, warn about function definitions where the function will
      return a value (usually of some struct or union type) which itself will
@@ -230,41 +638,11 @@ tree_rest_of_compilation (tree fndecl, bool nested_p)
        }
     }
 
-  /* 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);
-  /* Restore original body if still needed.  */
-  if (cfun->saved_tree)
-    {
-      DECL_SAVED_TREE (fndecl) = cfun->saved_tree;
-      DECL_ARGUMENTS (fndecl) = cfun->saved_args;
-
-      /* When not in unit-at-a-time mode, we must preserve out of line copy
-        representing node before inlining.  Restore original outgoing edges
-        using clone we created earlier.  */
-      if (!flag_unit_at_a_time)
-       {
-         struct cgraph_edge *e;
-         while (node->callees)
-           cgraph_remove_edge (node->callees);
-         node->callees = saved_node->callees;
-         saved_node->callees = NULL;
-         for (e = saved_node->callees; e; e = e->next_callee)
-           e->caller = node;
-         cgraph_remove_node (saved_node);
-       }
-    }
-  else
+  if (!nested_p && !flag_inline_trees)
     {
       DECL_SAVED_TREE (fndecl) = NULL;
-      if (cgraph_node (fndecl)->origin)
+      if (DECL_STRUCT_FUNCTION (fndecl) == 0
+         && !cgraph_node (fndecl)->origin)
        {
          /* Stop pointing to the local nodes about to be freed.
             But DECL_INITIAL must remain nonzero so we know this
@@ -275,11 +653,13 @@ tree_rest_of_compilation (tree fndecl, bool nested_p)
            DECL_INITIAL (fndecl) = error_mark_node;
        }
     }
-  free_after_compilation (cfun);
-  cfun = 0;
-  DECL_STRUCT_FUNCTION (fndecl) = 0;
 
   input_location = saved_loc;
 
+  ggc_collect ();
+
+  /* Undo the GC context switch.  */
+  if (nested_p)
+    ggc_pop_context ();
   timevar_pop (TV_EXPAND);
 }