summaryrefslogtreecommitdiff
path: root/gcc/passes.c
diff options
context:
space:
mode:
authorrth <rth@138bc75d-0d04-0410-961f-82ee72b054a4>2004-02-27 08:54:31 +0000
committerrth <rth@138bc75d-0d04-0410-961f-82ee72b054a4>2004-02-27 08:54:31 +0000
commita49a878ff6716082594ee19b7a9aa083afb0b272 (patch)
treef404cfb28fb7f34eab07157ad76acbaa562772ed /gcc/passes.c
parentb2e44f5046f3b5d5964e85dd09501fa06601dd8b (diff)
downloadgcc-a49a878ff6716082594ee19b7a9aa083afb0b272.tar.gz
* passes.c: New file.
* Makefile.in (OBJS-common): Add it. * diagnostic.c (rtl_dump_and_exit): Move decl ... * flags.h (rtl_dump_and_exit): ... here. * output.h (size_directive_output, last_assemble_variable_decl): Move from toplev.c. * rtl.h (reg_alloc): Move from toplev.c. * toplev.c (HAVE_conditional_execution, DUMPFILE_FORMAT, struct dump_file_info, enum dump_file_index, dump_file_tbl, open_dump_file, close_dump_file, rest_of_decl_compilation, rest_of_type_compilation, rest_of_handle_final, rest_of_handle_delay_slots, rest_of_handle_stack_regs, rest_of_handle_variable_tracking, rest_of_handle_machine_reorg, rest_of_handle_new_regalloc, rest_of_handle_old_regalloc, rest_of_handle_regrename, rest_of_handle_reorder_blocks, rest_of_handle_sched, rest_of_handle_sched2, rest_of_handle_regmove, rest_of_handle_tracer, rest_of_handle_if_conversion, rest_of_handle_if_after_combine, rest_of_handle_web, rest_of_handle_branch_prob, rest_of_handle_value_profile_transformations, rest_of_handle_cfg, rest_of_handle_addressof, rest_of_handle_sibling_calls, rest_of_handle_jump_bypass, rest_of_handle_inlining, rest_of_handle_null_pointer, rest_of_handle_combine, rest_of_handle_life, rest_of_handle_cse, rest_of_handle_cse2, rest_of_handle_gcse, rest_of_handle_loop_optimize, rest_of_handle_loop2, rest_of_compilation): Move to passes.c. (decode_d_option): Use enable_rtl_dump_file. (compile_file, finalize, do_compile): Move profile+combine+graph cleanup to finish_optimization_passes. * toplev.h (init_optimization_passes, finish_optimization_passes, enable_rtl_dump_file): Declare. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@78561 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/passes.c')
-rw-r--r--gcc/passes.c2177
1 files changed, 2177 insertions, 0 deletions
diff --git a/gcc/passes.c b/gcc/passes.c
new file mode 100644
index 00000000000..59de3af40e7
--- /dev/null
+++ b/gcc/passes.c
@@ -0,0 +1,2177 @@
+/* Top level of GCC compilers (cc1, cc1plus, etc.)
+ Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
+ 1999, 2000, 2001, 2002, 2003, 2004 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. */
+
+/* This is the top level of cc1/c++.
+ It parses command args, opens files, invokes the various passes
+ in the proper order, and counts the time used by each.
+ Error messages and low-level interface to malloc also handled here. */
+
+#include "config.h"
+#undef FLOAT /* This is for hpux. They should change hpux. */
+#undef FFS /* Some systems define this in param.h. */
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include <signal.h>
+
+#ifdef HAVE_SYS_RESOURCE_H
+# include <sys/resource.h>
+#endif
+
+#ifdef HAVE_SYS_TIMES_H
+# include <sys/times.h>
+#endif
+
+#include "line-map.h"
+#include "input.h"
+#include "tree.h"
+#include "rtl.h"
+#include "tm_p.h"
+#include "flags.h"
+#include "insn-attr.h"
+#include "insn-config.h"
+#include "insn-flags.h"
+#include "hard-reg-set.h"
+#include "recog.h"
+#include "output.h"
+#include "except.h"
+#include "function.h"
+#include "toplev.h"
+#include "expr.h"
+#include "basic-block.h"
+#include "intl.h"
+#include "ggc.h"
+#include "graph.h"
+#include "loop.h"
+#include "regs.h"
+#include "timevar.h"
+#include "diagnostic.h"
+#include "params.h"
+#include "reload.h"
+#include "dwarf2asm.h"
+#include "integrate.h"
+#include "real.h"
+#include "debug.h"
+#include "target.h"
+#include "langhooks.h"
+#include "cfglayout.h"
+#include "cfgloop.h"
+#include "hosthooks.h"
+#include "cgraph.h"
+#include "opts.h"
+#include "coverage.h"
+#include "value-prof.h"
+#include "alloc-pool.h"
+
+#if defined (DWARF2_UNWIND_INFO) || defined (DWARF2_DEBUGGING_INFO)
+#include "dwarf2out.h"
+#endif
+
+#if defined(DBX_DEBUGGING_INFO) || defined(XCOFF_DEBUGGING_INFO)
+#include "dbxout.h"
+#endif
+
+#ifdef SDB_DEBUGGING_INFO
+#include "sdbout.h"
+#endif
+
+#ifdef XCOFF_DEBUGGING_INFO
+#include "xcoffout.h" /* Needed for external data
+ declarations for e.g. AIX 4.x. */
+#endif
+
+#ifndef HAVE_conditional_execution
+#define HAVE_conditional_execution 0
+#endif
+
+/* Format to use to print dumpfile index value */
+#ifndef DUMPFILE_FORMAT
+#define DUMPFILE_FORMAT ".%02d."
+#endif
+
+/* Describes a dump file. */
+
+struct dump_file_info
+{
+ /* The unique extension to apply, e.g. ".jump". */
+ const char *const extension;
+
+ /* The -d<c> character that enables this dump file. */
+ char const debug_switch;
+
+ /* True if there is a corresponding graph dump file. */
+ char const graph_dump_p;
+
+ /* True if the user selected this dump. */
+ char enabled;
+
+ /* True if the files have been initialized (ie truncated). */
+ char initialized;
+};
+
+/* Enumerate the extant dump files. */
+
+enum dump_file_index
+{
+ DFI_cgraph,
+ DFI_rtl,
+ DFI_sibling,
+ DFI_eh,
+ DFI_jump,
+ DFI_null,
+ DFI_cse,
+ DFI_addressof,
+ DFI_gcse,
+ DFI_loop,
+ DFI_bypass,
+ DFI_cfg,
+ DFI_bp,
+ DFI_vpt,
+ DFI_ce1,
+ DFI_tracer,
+ DFI_loop2,
+ DFI_web,
+ DFI_cse2,
+ DFI_life,
+ DFI_combine,
+ DFI_ce2,
+ DFI_regmove,
+ DFI_sched,
+ DFI_lreg,
+ DFI_greg,
+ DFI_postreload,
+ DFI_flow2,
+ DFI_peephole2,
+ DFI_ce3,
+ DFI_rnreg,
+ DFI_bbro,
+ DFI_branch_target_load,
+ DFI_sched2,
+ DFI_stack,
+ DFI_vartrack,
+ DFI_mach,
+ DFI_dbr,
+ DFI_MAX
+};
+
+/* Describes all the dump files. Should be kept in order of the
+ pass and in sync with dump_file_index above.
+
+ Remaining -d letters:
+
+ " e m q "
+ " JK O Q WXY "
+*/
+
+static struct dump_file_info dump_file_tbl[DFI_MAX] =
+{
+ { "cgraph", 'U', 0, 0, 0 },
+ { "rtl", 'r', 0, 0, 0 },
+ { "sibling", 'i', 0, 0, 0 },
+ { "eh", 'h', 0, 0, 0 },
+ { "jump", 'j', 0, 0, 0 },
+ { "null", 'u', 0, 0, 0 },
+ { "cse", 's', 0, 0, 0 },
+ { "addressof", 'F', 0, 0, 0 },
+ { "gcse", 'G', 1, 0, 0 },
+ { "loop", 'L', 1, 0, 0 },
+ { "bypass", 'G', 1, 0, 0 }, /* Yes, duplicate enable switch. */
+ { "cfg", 'f', 1, 0, 0 },
+ { "bp", 'b', 1, 0, 0 },
+ { "vpt", 'V', 1, 0, 0 },
+ { "ce1", 'C', 1, 0, 0 },
+ { "tracer", 'T', 1, 0, 0 },
+ { "loop2", 'L', 1, 0, 0 },
+ { "web", 'Z', 0, 0, 0 },
+ { "cse2", 't', 1, 0, 0 },
+ { "life", 'f', 1, 0, 0 }, /* Yes, duplicate enable switch. */
+ { "combine", 'c', 1, 0, 0 },
+ { "ce2", 'C', 1, 0, 0 },
+ { "regmove", 'N', 1, 0, 0 },
+ { "sched", 'S', 1, 0, 0 },
+ { "lreg", 'l', 1, 0, 0 },
+ { "greg", 'g', 1, 0, 0 },
+ { "postreload", 'o', 1, 0, 0 },
+ { "flow2", 'w', 1, 0, 0 },
+ { "peephole2", 'z', 1, 0, 0 },
+ { "ce3", 'E', 1, 0, 0 },
+ { "rnreg", 'n', 1, 0, 0 },
+ { "bbro", 'B', 1, 0, 0 },
+ { "btl", 'd', 1, 0, 0 }, /* Yes, duplicate enable switch. */
+ { "sched2", 'R', 1, 0, 0 },
+ { "stack", 'k', 1, 0, 0 },
+ { "vartrack", 'V', 1, 0, 0 }, /* Yes, duplicate enable switch. */
+ { "mach", 'M', 1, 0, 0 },
+ { "dbr", 'd', 0, 0, 0 },
+};
+
+/* Routine to open a dump file. Return true if the dump file is enabled. */
+
+static int
+open_dump_file (enum dump_file_index index, tree decl)
+{
+ char *dump_name;
+ const char *open_arg;
+ char seq[16];
+
+ if (! dump_file_tbl[index].enabled)
+ return 0;
+
+ timevar_push (TV_DUMP);
+ if (dump_file != NULL)
+ fclose (dump_file);
+
+ sprintf (seq, DUMPFILE_FORMAT, index);
+
+ if (! dump_file_tbl[index].initialized)
+ {
+ /* If we've not initialized the files, do so now. */
+ if (graph_dump_format != no_graph
+ && dump_file_tbl[index].graph_dump_p)
+ {
+ dump_name = concat (seq, dump_file_tbl[index].extension, NULL);
+ clean_graph_dump_file (dump_base_name, dump_name);
+ free (dump_name);
+ }
+ dump_file_tbl[index].initialized = 1;
+ open_arg = "w";
+ }
+ else
+ open_arg = "a";
+
+ dump_name = concat (dump_base_name, seq,
+ dump_file_tbl[index].extension, NULL);
+
+ dump_file = fopen (dump_name, open_arg);
+ if (dump_file == NULL)
+ fatal_error ("can't open %s: %m", dump_name);
+
+ free (dump_name);
+
+ if (decl)
+ fprintf (dump_file, "\n;; Function %s%s\n\n",
+ (*lang_hooks.decl_printable_name) (decl, 2),
+ cfun->function_frequency == FUNCTION_FREQUENCY_HOT
+ ? " (hot)"
+ : cfun->function_frequency == FUNCTION_FREQUENCY_UNLIKELY_EXECUTED
+ ? " (unlikely executed)"
+ : "");
+
+ timevar_pop (TV_DUMP);
+ return 1;
+}
+
+/* Routine to close a dump file. */
+
+static void
+close_dump_file (enum dump_file_index index,
+ void (*func) (FILE *, rtx),
+ rtx insns)
+{
+ if (! dump_file)
+ return;
+
+ timevar_push (TV_DUMP);
+ if (insns
+ && graph_dump_format != no_graph
+ && dump_file_tbl[index].graph_dump_p)
+ {
+ char seq[16];
+ char *suffix;
+
+ sprintf (seq, DUMPFILE_FORMAT, index);
+ suffix = concat (seq, dump_file_tbl[index].extension, NULL);
+ print_rtl_graph_with_bb (dump_base_name, suffix, insns);
+ free (suffix);
+ }
+
+ if (func && insns)
+ func (dump_file, insns);
+
+ fflush (dump_file);
+ fclose (dump_file);
+
+ dump_file = NULL;
+ timevar_pop (TV_DUMP);
+}
+
+/* This is called from various places for FUNCTION_DECL, VAR_DECL,
+ and TYPE_DECL nodes.
+
+ This does nothing for local (non-static) variables, unless the
+ variable is a register variable with an ASMSPEC. In that case, or
+ if the variable is not an automatic, it sets up the RTL and
+ outputs any assembler code (label definition, storage allocation
+ and initialization).
+
+ DECL is the declaration. If ASMSPEC is nonzero, it specifies
+ the assembler symbol name to be used. TOP_LEVEL is nonzero
+ if this declaration is not within a function. */
+
+void
+rest_of_decl_compilation (tree decl,
+ const char *asmspec,
+ int top_level,
+ int at_end)
+{
+ /* We deferred calling assemble_alias so that we could collect
+ other attributes such as visibility. Emit the alias now. */
+ {
+ tree alias;
+ alias = lookup_attribute ("alias", DECL_ATTRIBUTES (decl));
+ if (alias)
+ {
+ alias = TREE_VALUE (TREE_VALUE (alias));
+ alias = get_identifier (TREE_STRING_POINTER (alias));
+ assemble_alias (decl, alias);
+ }
+ }
+
+ /* Forward declarations for nested functions are not "external",
+ but we need to treat them as if they were. */
+ if (TREE_STATIC (decl) || DECL_EXTERNAL (decl)
+ || TREE_CODE (decl) == FUNCTION_DECL)
+ {
+ timevar_push (TV_VARCONST);
+
+ if (asmspec)
+ make_decl_rtl (decl, asmspec);
+
+ /* Don't output anything when a tentative file-scope definition
+ is seen. But at end of compilation, do output code for them.
+
+ We do output all variables when unit-at-a-time is active and rely on
+ callgraph code to defer them except for forward declarations
+ (see gcc.c-torture/compile/920624-1.c) */
+ if ((at_end
+ || !DECL_DEFER_OUTPUT (decl)
+ || (flag_unit_at_a_time && DECL_INITIAL (decl)))
+ && !DECL_EXTERNAL (decl))
+ {
+ if (flag_unit_at_a_time && !cgraph_global_info_ready
+ && TREE_CODE (decl) != FUNCTION_DECL && top_level)
+ cgraph_varpool_finalize_decl (decl);
+ else
+ assemble_variable (decl, top_level, at_end, 0);
+ }
+
+#ifdef ASM_FINISH_DECLARE_OBJECT
+ if (decl == last_assemble_variable_decl)
+ {
+ ASM_FINISH_DECLARE_OBJECT (asm_out_file, decl,
+ top_level, at_end);
+ }
+#endif
+
+ timevar_pop (TV_VARCONST);
+ }
+ else if (DECL_REGISTER (decl) && asmspec != 0)
+ {
+ if (decode_reg_name (asmspec) >= 0)
+ {
+ SET_DECL_RTL (decl, NULL_RTX);
+ make_decl_rtl (decl, asmspec);
+ }
+ else
+ {
+ error ("invalid register name `%s' for register variable", asmspec);
+ DECL_REGISTER (decl) = 0;
+ if (!top_level)
+ expand_decl (decl);
+ }
+ }
+ else if (TREE_CODE (decl) == TYPE_DECL)
+ {
+ timevar_push (TV_SYMOUT);
+ debug_hooks->type_decl (decl, !top_level);
+ timevar_pop (TV_SYMOUT);
+ }
+}
+
+/* Called after finishing a record, union or enumeral type. */
+
+void
+rest_of_type_compilation (tree type, int toplev)
+{
+ /* Avoid confusing the debug information machinery when there are
+ errors. */
+ if (errorcount != 0 || sorrycount != 0)
+ return;
+
+ timevar_push (TV_SYMOUT);
+ debug_hooks->type_decl (TYPE_STUB_DECL (type), !toplev);
+ timevar_pop (TV_SYMOUT);
+}
+
+/* Turn the RTL into assembly. */
+static void
+rest_of_handle_final (tree decl, rtx insns)
+{
+ timevar_push (TV_FINAL);
+ {
+ rtx x;
+ const char *fnname;
+
+ /* Get the function's name, as described by its RTL. This may be
+ different from the DECL_NAME name used in the source file. */
+
+ x = DECL_RTL (decl);
+ if (GET_CODE (x) != MEM)
+ abort ();
+ x = XEXP (x, 0);
+ if (GET_CODE (x) != SYMBOL_REF)
+ abort ();
+ fnname = XSTR (x, 0);
+
+ assemble_start_function (decl, fnname);
+ final_start_function (insns, asm_out_file, optimize);
+ final (insns, asm_out_file, optimize, 0);
+ final_end_function ();
+
+#ifdef IA64_UNWIND_INFO
+ /* ??? The IA-64 ".handlerdata" directive must be issued before
+ the ".endp" directive that closes the procedure descriptor. */
+ output_function_exception_table ();
+#endif
+
+ assemble_end_function (decl, fnname);
+
+#ifndef IA64_UNWIND_INFO
+ /* Otherwise, it feels unclean to switch sections in the middle. */
+ output_function_exception_table ();
+#endif
+
+ if (! quiet_flag)
+ fflush (asm_out_file);
+
+ /* Release all memory allocated by flow. */
+ free_basic_block_vars (0);
+
+ /* Release all memory held by regsets now. */
+ regset_release_memory ();
+ }
+ timevar_pop (TV_FINAL);
+
+ ggc_collect ();
+}
+
+#ifdef DELAY_SLOTS
+/* Run delay slot optimization. */
+static void
+rest_of_handle_delay_slots (tree decl, rtx insns)
+{
+ timevar_push (TV_DBR_SCHED);
+ open_dump_file (DFI_dbr, decl);
+
+ dbr_schedule (insns, dump_file);
+
+ close_dump_file (DFI_dbr, print_rtl, insns);
+ timevar_pop (TV_DBR_SCHED);
+
+ ggc_collect ();
+}
+#endif
+
+#ifdef STACK_REGS
+/* Convert register usage from flat register file usage to a stack
+ register file. */
+static void
+rest_of_handle_stack_regs (tree decl, rtx insns)
+{
+#if defined (HAVE_ATTR_length)
+ /* If flow2 creates new instructions which need splitting
+ and scheduling after reload is not done, they might not be
+ split until final which doesn't allow splitting
+ if HAVE_ATTR_length. */
+#ifdef INSN_SCHEDULING
+ if (optimize && !flag_schedule_insns_after_reload)
+#else
+ if (optimize)
+#endif
+ {
+ timevar_push (TV_SHORTEN_BRANCH);
+ split_all_insns (1);
+ timevar_pop (TV_SHORTEN_BRANCH);
+ }
+#endif
+
+ timevar_push (TV_REG_STACK);
+ open_dump_file (DFI_stack, decl);
+
+ if (reg_to_stack (insns, dump_file) && optimize)
+ {
+ if (cleanup_cfg (CLEANUP_EXPENSIVE | CLEANUP_POST_REGSTACK
+ | (flag_crossjumping ? CLEANUP_CROSSJUMP : 0))
+ && flag_reorder_blocks)
+ {
+ reorder_basic_blocks ();
+ cleanup_cfg (CLEANUP_EXPENSIVE | CLEANUP_POST_REGSTACK);
+ }
+ }
+
+ close_dump_file (DFI_stack, print_rtl_with_bb, insns);
+ timevar_pop (TV_REG_STACK);
+
+ ggc_collect ();
+}
+#endif
+
+/* Track the variables, ie. compute where the variable is stored at each position in function. */
+static void
+rest_of_handle_variable_tracking (tree decl, rtx insns)
+{
+ timevar_push (TV_VAR_TRACKING);
+ open_dump_file (DFI_vartrack, decl);
+
+ variable_tracking_main ();
+
+ close_dump_file (DFI_vartrack, print_rtl_with_bb, insns);
+ timevar_pop (TV_VAR_TRACKING);
+}
+
+/* Machine independent reorg pass. */
+static void
+rest_of_handle_machine_reorg (tree decl, rtx insns)
+{
+ timevar_push (TV_MACH_DEP);
+ open_dump_file (DFI_mach, decl);
+
+ (*targetm.machine_dependent_reorg) ();
+
+ close_dump_file (DFI_mach, print_rtl, insns);
+ timevar_pop (TV_MACH_DEP);
+
+ ggc_collect ();
+}
+
+
+/* Run new register allocator. Return TRUE if we must exit
+ rest_of_compilation upon return. */
+static bool
+rest_of_handle_new_regalloc (tree decl, rtx insns)
+{
+ int failure;
+
+ delete_trivially_dead_insns (insns, max_reg_num ());
+ reg_alloc ();
+
+ timevar_pop (TV_LOCAL_ALLOC);
+ if (dump_file_tbl[DFI_lreg].enabled)
+ {
+ timevar_push (TV_DUMP);
+
+ close_dump_file (DFI_lreg, NULL, NULL);
+ timevar_pop (TV_DUMP);
+ }
+
+ /* XXX clean up the whole mess to bring live info in shape again. */
+ timevar_push (TV_GLOBAL_ALLOC);
+ open_dump_file (DFI_greg, decl);
+
+ build_insn_chain (insns);
+ failure = reload (insns, 0);
+
+ timevar_pop (TV_GLOBAL_ALLOC);
+
+ if (dump_file_tbl[DFI_greg].enabled)
+ {
+ timevar_push (TV_DUMP);
+
+ dump_global_regs (dump_file);
+
+ close_dump_file (DFI_greg, print_rtl_with_bb, insns);
+ timevar_pop (TV_DUMP);
+ }
+
+ if (failure)
+ return true;
+
+ reload_completed = 1;
+
+ return false;
+}
+
+/* Run old register allocator. Return TRUE if we must exit
+ rest_of_compilation upon return. */
+static bool
+rest_of_handle_old_regalloc (tree decl, rtx insns)
+{
+ int failure;
+ int rebuild_notes;
+
+ /* Allocate the reg_renumber array. */
+ allocate_reg_info (max_regno, FALSE, TRUE);
+
+ /* And the reg_equiv_memory_loc array. */
+ reg_equiv_memory_loc = xcalloc (max_regno, sizeof (rtx));
+
+ allocate_initial_values (reg_equiv_memory_loc);
+
+ regclass (insns, max_reg_num (), dump_file);
+ rebuild_notes = local_alloc ();
+
+ timevar_pop (TV_LOCAL_ALLOC);
+
+ /* Local allocation may have turned an indirect jump into a direct
+ jump. If so, we must rebuild the JUMP_LABEL fields of jumping
+ instructions. */
+ if (rebuild_notes)
+ {
+ timevar_push (TV_JUMP);
+
+ rebuild_jump_labels (insns);
+ purge_all_dead_edges (0);
+
+ timevar_pop (TV_JUMP);
+ }
+
+ if (dump_file_tbl[DFI_lreg].enabled)
+ {
+ timevar_push (TV_DUMP);
+
+ dump_flow_info (dump_file);
+ dump_local_alloc (dump_file);
+
+ close_dump_file (DFI_lreg, print_rtl_with_bb, insns);
+ timevar_pop (TV_DUMP);
+ }
+
+ ggc_collect ();
+
+ timevar_push (TV_GLOBAL_ALLOC);
+ open_dump_file (DFI_greg, decl);
+
+ /* If optimizing, allocate remaining pseudo-regs. Do the reload
+ pass fixing up any insns that are invalid. */
+
+ if (optimize)
+ failure = global_alloc (dump_file);
+ else
+ {
+ build_insn_chain (insns);
+ failure = reload (insns, 0);
+ }
+
+ timevar_pop (TV_GLOBAL_ALLOC);
+
+ if (dump_file_tbl[DFI_greg].enabled)
+ {
+ timevar_push (TV_DUMP);
+
+ dump_global_regs (dump_file);
+
+ close_dump_file (DFI_greg, print_rtl_with_bb, insns);
+ timevar_pop (TV_DUMP);
+ }
+
+ return failure;
+}
+
+/* Run the regrename and cprop passes. */
+static void
+rest_of_handle_regrename (tree decl, rtx insns)
+{
+ timevar_push (TV_RENAME_REGISTERS);
+ open_dump_file (DFI_rnreg, decl);
+
+ if (flag_rename_registers)
+ regrename_optimize ();
+ if (flag_cprop_registers)
+ copyprop_hardreg_forward ();
+
+ close_dump_file (DFI_rnreg, print_rtl_with_bb, insns);
+ timevar_pop (TV_RENAME_REGISTERS);
+}
+
+/* Reorder basic blocks. */
+static void
+rest_of_handle_reorder_blocks (tree decl, rtx insns)
+{
+ bool changed;
+ open_dump_file (DFI_bbro, decl);
+
+ /* Last attempt to optimize CFG, as scheduling, peepholing and insn
+ splitting possibly introduced more crossjumping opportunities. */
+ changed = cleanup_cfg (CLEANUP_EXPENSIVE
+ | (!HAVE_conditional_execution
+ ? CLEANUP_UPDATE_LIFE : 0));
+
+ if (flag_sched2_use_traces && flag_schedule_insns_after_reload)
+ tracer ();
+ if (flag_reorder_blocks)
+ reorder_basic_blocks ();
+ if (flag_reorder_blocks
+ || (flag_sched2_use_traces && flag_schedule_insns_after_reload))
+ changed |= cleanup_cfg (CLEANUP_EXPENSIVE
+ | (!HAVE_conditional_execution
+ ? CLEANUP_UPDATE_LIFE : 0));
+
+ /* On conditional execution targets we can not update the life cheaply, so
+ we deffer the updating to after both cleanups. This may lose some cases
+ but should not be terribly bad. */
+ if (changed && HAVE_conditional_execution)
+ update_life_info (NULL, UPDATE_LIFE_GLOBAL_RM_NOTES,
+ PROP_DEATH_NOTES);
+ close_dump_file (DFI_bbro, print_rtl_with_bb, insns);
+}
+
+#ifdef INSN_SCHEDULING
+/* Run instruction scheduler. */
+static void
+rest_of_handle_sched (tree decl, rtx insns)
+{
+ timevar_push (TV_SCHED);
+
+ /* Print function header into sched dump now
+ because doing the sched analysis makes some of the dump. */
+ if (optimize > 0 && flag_schedule_insns)
+ {
+ open_dump_file (DFI_sched, decl);
+
+ /* Do control and data sched analysis,
+ and write some of the results to dump file. */
+
+ schedule_insns (dump_file);
+
+ close_dump_file (DFI_sched, print_rtl_with_bb, insns);
+ }
+ timevar_pop (TV_SCHED);
+
+ ggc_collect ();
+}
+
+/* Run second scheduling pass after reload. */
+static void
+rest_of_handle_sched2 (tree decl, rtx insns)
+{
+ timevar_push (TV_SCHED2);
+ open_dump_file (DFI_sched2, decl);
+
+ /* Do control and data sched analysis again,
+ and write some more of the results to dump file. */
+
+ split_all_insns (1);
+
+ if (flag_sched2_use_superblocks || flag_sched2_use_traces)
+ {
+ schedule_ebbs (dump_file);
+ /* No liveness updating code yet, but it should be easy to do.
+ reg-stack recompute the liveness when needed for now. */
+ count_or_remove_death_notes (NULL, 1);
+ cleanup_cfg (CLEANUP_EXPENSIVE);
+ }
+ else
+ schedule_insns (dump_file);
+
+ close_dump_file (DFI_sched2, print_rtl_with_bb, insns);
+ timevar_pop (TV_SCHED2);
+
+ ggc_collect ();
+}
+#endif
+
+/* Register allocation pre-pass, to reduce number of moves necessary
+ for two-address machines. */
+static void
+rest_of_handle_regmove (tree decl, rtx insns)
+{
+ timevar_push (TV_REGMOVE);
+ open_dump_file (DFI_regmove, decl);
+
+ regmove_optimize (insns, max_reg_num (), dump_file);
+
+ cleanup_cfg (CLEANUP_EXPENSIVE | CLEANUP_UPDATE_LIFE);
+ close_dump_file (DFI_regmove, print_rtl_with_bb, insns);
+ timevar_pop (TV_REGMOVE);
+
+ ggc_collect ();
+}
+
+/* Run tracer. */
+static void
+rest_of_handle_tracer (tree decl, rtx insns)
+{
+ open_dump_file (DFI_tracer, decl);
+ if (dump_file)
+ dump_flow_info (dump_file);
+ tracer ();
+ cleanup_cfg (CLEANUP_EXPENSIVE);
+ reg_scan (insns, max_reg_num (), 0);
+ close_dump_file (DFI_tracer, print_rtl_with_bb, get_insns ());
+}
+
+/* If-conversion and CFG cleanup. */
+static void
+rest_of_handle_if_conversion (tree decl, rtx insns)
+{
+ open_dump_file (DFI_ce1, decl);
+ if (flag_if_conversion)
+ {
+ timevar_push (TV_IFCVT);
+ if (dump_file)
+ dump_flow_info (dump_file);
+ cleanup_cfg (CLEANUP_EXPENSIVE);
+ reg_scan (insns, max_reg_num (), 0);
+ if_convert (0);
+ timevar_pop (TV_IFCVT);
+ }
+ timevar_push (TV_JUMP);
+ cleanup_cfg (CLEANUP_EXPENSIVE);
+ reg_scan (insns, max_reg_num (), 0);
+ timevar_pop (TV_JUMP);
+ close_dump_file (DFI_ce1, print_rtl_with_bb, get_insns ());
+}
+
+/* Rerun if-conversion, as combine may have simplified things enough
+ to now meet sequence length restrictions. */
+static void
+rest_of_handle_if_after_combine (tree decl, rtx insns)
+{
+ timevar_push (TV_IFCVT);
+ open_dump_file (DFI_ce2, decl);
+
+ no_new_pseudos = 0;
+ if_convert (1);
+ no_new_pseudos = 1;
+
+ close_dump_file (DFI_ce2, print_rtl_with_bb, insns);
+ timevar_pop (TV_IFCVT);
+}
+
+static void
+rest_of_handle_web (tree decl, rtx insns)
+{
+ open_dump_file (DFI_web, decl);
+ timevar_push (TV_WEB);
+ web_main ();
+ delete_trivially_dead_insns (insns, max_reg_num ());
+ cleanup_cfg (CLEANUP_EXPENSIVE);
+
+ timevar_pop (TV_WEB);
+ close_dump_file (DFI_web, print_rtl_with_bb, insns);
+ reg_scan (get_insns (), max_reg_num (), 0);
+}
+
+/* Do branch profiling and static profile estimation passes. */
+static void
+rest_of_handle_branch_prob (tree decl, rtx insns)
+{
+ struct loops loops;
+
+ timevar_push (TV_BRANCH_PROB);
+ open_dump_file (DFI_bp, decl);
+
+ if (profile_arc_flag || flag_test_coverage || flag_branch_probabilities)
+ branch_prob ();
+
+ /* Discover and record the loop depth at the head of each basic
+ block. The loop infrastructure does the real job for us. */
+ flow_loops_find (&loops, LOOP_TREE);
+
+ if (dump_file)
+ flow_loops_dump (&loops, dump_file, NULL, 0);
+
+ /* Estimate using heuristics if no profiling info is available. */
+ if (flag_guess_branch_prob)
+ estimate_probability (&loops);
+
+ flow_loops_free (&loops);
+ free_dominance_info (CDI_DOMINATORS);
+ close_dump_file (DFI_bp, print_rtl_with_bb, insns);
+ timevar_pop (TV_BRANCH_PROB);
+}
+
+/* Do optimizations based on expression value profiles. */
+static void
+rest_of_handle_value_profile_transformations (tree decl, rtx insns)
+{
+ open_dump_file (DFI_vpt, decl);
+ timevar_push (TV_VPT);
+
+ if (value_profile_transformations ())
+ cleanup_cfg (CLEANUP_EXPENSIVE);
+
+ timevar_pop (TV_VPT);
+ close_dump_file (DFI_vpt, print_rtl_with_bb, insns);
+}
+
+/* Do control and data flow analysis; write some of the results to the
+ dump file. */
+static void
+rest_of_handle_cfg (tree decl, rtx insns)
+{
+ open_dump_file (DFI_cfg, decl);
+ if (dump_file)
+ dump_flow_info (dump_file);
+ if (optimize)
+ cleanup_cfg (CLEANUP_EXPENSIVE
+ | (flag_thread_jumps ? CLEANUP_THREADING : 0));
+
+ /* It may make more sense to mark constant functions after dead code is
+ eliminated by life_analysis, but we need to do it early, as -fprofile-arcs
+ may insert code making function non-constant, but we still must consider
+ it as constant, otherwise -fbranch-probabilities will not read data back.
+
+ life_analysis rarely eliminates modification of external memory.
+ */
+ if (optimize)
+ {
+ /* Alias analysis depends on this information and mark_constant_function
+ depends on alias analysis. */
+ reg_scan (insns, max_reg_num (), 1);
+ mark_constant_function ();
+ }
+
+ close_dump_file (DFI_cfg, print_rtl_with_bb, insns);
+}
+
+/* Purge addressofs. */
+static void
+rest_of_handle_addressof (tree decl, rtx insns)
+{
+ open_dump_file (DFI_addressof, decl);
+
+ purge_addressof (insns);
+ if (optimize && purge_all_dead_edges (0))
+ delete_unreachable_blocks ();
+ reg_scan (insns, max_reg_num (), 1);
+
+ close_dump_file (DFI_addressof, print_rtl, insns);
+}
+
+/* We may have potential sibling or tail recursion sites. Select one
+ (of possibly multiple) methods of performing the call. */
+static void
+rest_of_handle_sibling_calls (rtx insns)
+{
+ rtx insn;
+ optimize_sibling_and_tail_recursive_calls ();
+
+ /* Recompute the CFG as sibling optimization clobbers it randomly. */
+ free_bb_for_insn ();
+ find_exception_handler_labels ();
+ rebuild_jump_labels (insns);
+ find_basic_blocks (insns, max_reg_num (), dump_file);
+
+ /* There is pass ordering problem - we must lower NOTE_INSN_PREDICTION
+ notes before simplifying cfg and we must do lowering after sibcall
+ that unhides parts of RTL chain and cleans up the CFG.
+
+ Until sibcall is replaced by tree-level optimizer, lets just
+ sweep away the NOTE_INSN_PREDICTION notes that leaked out. */
+ for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+ if (GET_CODE (insn) == NOTE
+ && NOTE_LINE_NUMBER (insn) == NOTE_INSN_PREDICTION)
+ delete_insn (insn);
+
+ close_dump_file (DFI_sibling, print_rtl, get_insns ());
+}
+
+/* Perform jump bypassing and control flow optimizations. */
+static void
+rest_of_handle_jump_bypass (tree decl, rtx insns)
+{
+ timevar_push (TV_BYPASS);
+ open_dump_file (DFI_bypass, decl);
+
+ cleanup_cfg (CLEANUP_EXPENSIVE);
+ reg_scan (insns, max_reg_num (), 1);
+
+ if (bypass_jumps (dump_file))
+ {
+ rebuild_jump_labels (insns);
+ cleanup_cfg (CLEANUP_EXPENSIVE);
+ delete_trivially_dead_insns (insns, max_reg_num ());
+ }
+
+ close_dump_file (DFI_bypass, print_rtl_with_bb, insns);
+ timevar_pop (TV_BYPASS);
+
+ ggc_collect ();
+
+#ifdef ENABLE_CHECKING
+ verify_flow_info ();
+#endif
+}
+
+/* Handle inlining of functions in rest_of_compilation. Return TRUE
+ if we must exit rest_of_compilation upon return. */
+static bool
+rest_of_handle_inlining (tree decl)
+{
+ rtx insns;
+ int inlinable = 0;
+ tree parent;
+ const char *lose;
+
+ /* If we are reconsidering an inline function at the end of
+ compilation, skip the stuff for making it inline. */
+ if (cfun->rtl_inline_init)
+ return 0;
+ cfun->rtl_inline_init = 1;
+
+ /* If this is nested inside an inlined external function, pretend
+ it was only declared. Since we cannot inline such functions,
+ generating code for this one is not only not necessary but will
+ confuse some debugging output writers. */
+ for (parent = DECL_CONTEXT (current_function_decl);
+ parent != NULL_TREE;
+ parent = get_containing_scope (parent))
+ if (TREE_CODE (parent) == FUNCTION_DECL
+ && DECL_INLINE (parent) && DECL_EXTERNAL (parent))
+ {
+ DECL_INITIAL (decl) = 0;
+ return true;
+ }
+ else if (TYPE_P (parent))
+ /* A function in a local class should be treated normally. */
+ break;
+
+ /* If requested, consider whether to make this function inline. */
+ if ((DECL_INLINE (decl) && !flag_no_inline)
+ || flag_inline_functions)
+ {
+ timevar_push (TV_INTEGRATION);
+ lose = function_cannot_inline_p (decl);
+ timevar_pop (TV_INTEGRATION);
+ if (lose || ! optimize)
+ {
+ if (warn_inline && lose && DECL_INLINE (decl))
+ {
+ char *msg = concat ("%J", lose, NULL);
+ warning (msg, decl);
+ free (msg);
+ }
+ DECL_ABSTRACT_ORIGIN (decl) = 0;
+ /* Don't really compile an extern inline function.
+ If we can't make it inline, pretend
+ it was only declared. */
+ if (DECL_EXTERNAL (decl))
+ {
+ DECL_INITIAL (decl) = 0;
+ return true;
+ }
+ }
+ else
+ inlinable = DECL_INLINE (decl) = 1;
+ }
+
+ insns = get_insns ();
+
+ /* Dump the rtl code if we are dumping rtl. */
+
+ if (open_dump_file (DFI_rtl, decl))
+ {
+ if (DECL_STRUCT_FUNCTION (decl)
+ && DECL_STRUCT_FUNCTION (decl)->saved_for_inline)
+ fprintf (dump_file, ";; (integrable)\n\n");
+ close_dump_file (DFI_rtl, print_rtl, insns);
+ }
+
+ /* Convert from NOTE_INSN_EH_REGION style notes, and do other
+ sorts of eh initialization. Delay this until after the
+ initial rtl dump so that we can see the original nesting. */
+ convert_from_eh_region_ranges ();
+
+ /* If function is inline, and we don't yet know whether to
+ compile it by itself, defer decision till end of compilation.
+ wrapup_global_declarations will (indirectly) call
+ rest_of_compilation again for those functions that need to
+ be output. Also defer those functions that we are supposed
+ to defer. */
+
+ if (inlinable
+ || (DECL_INLINE (decl)
+ /* Egad. This RTL deferral test conflicts with Fortran assumptions
+ for unreferenced symbols. See g77.f-torture/execute/980520-1.f.
+ But removing this line from the check breaks all languages that
+ use the call graph to output symbols. This hard-coded check is
+ the least invasive work-around. */
+ && (flag_inline_functions
+ || strcmp (lang_hooks.name, "GNU F77") == 0)
+ && ((! TREE_PUBLIC (decl) && ! TREE_ADDRESSABLE (decl)
+ && ! TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (decl))
+ && ! flag_keep_inline_functions)
+ || DECL_EXTERNAL (decl))))
+ DECL_DEFER_OUTPUT (decl) = 1;
+
+ if (DECL_INLINE (decl))
+ /* DWARF wants separate debugging info for abstract and
+ concrete instances of all inline functions, including those
+ declared inline but not inlined, and those inlined even
+ though they weren't declared inline. Conveniently, that's
+ what DECL_INLINE means at this point. */
+ (*debug_hooks->deferred_inline_function) (decl);
+
+ if (DECL_DEFER_OUTPUT (decl))
+ {
+ /* If -Wreturn-type, we have to do a bit of compilation. We just
+ want to call cleanup the cfg to figure out whether or not we can
+ fall off the end of the function; we do the minimum amount of
+ work necessary to make that safe. */
+ if (warn_return_type)
+ {
+ int saved_optimize = optimize;
+
+ optimize = 0;
+ rebuild_jump_labels (insns);
+ find_exception_handler_labels ();
+ find_basic_blocks (insns, max_reg_num (), dump_file);
+ cleanup_cfg (CLEANUP_PRE_SIBCALL | CLEANUP_PRE_LOOP);
+ optimize = saved_optimize;
+
+ /* CFG is no longer maintained up-to-date. */
+ free_bb_for_insn ();
+ }
+
+ set_nothrow_function_flags ();
+ if (current_function_nothrow)
+ /* Now we know that this can't throw; set the flag for the benefit
+ of other functions later in this translation unit. */
+ TREE_NOTHROW (current_function_decl) = 1;
+
+ timevar_push (TV_INTEGRATION);
+ save_for_inline (decl);
+ timevar_pop (TV_INTEGRATION);
+ DECL_STRUCT_FUNCTION (decl)->inlinable = inlinable;
+ return true;
+ }
+
+ /* If specified extern inline but we aren't inlining it, we are
+ done. This goes for anything that gets here with DECL_EXTERNAL
+ set, not just things with DECL_INLINE. */
+ return (bool) DECL_EXTERNAL (decl);
+}
+
+/* Try to identify useless null pointer tests and delete them. */
+static void
+rest_of_handle_null_pointer (tree decl, rtx insns)
+{
+ open_dump_file (DFI_null, decl);
+ if (dump_file)
+ dump_flow_info (dump_file);
+
+ if (delete_null_pointer_checks (insns))
+ cleanup_cfg (CLEANUP_EXPENSIVE | CLEANUP_PRE_LOOP);
+
+ close_dump_file (DFI_null, print_rtl_with_bb, insns);
+}
+
+/* Try combining insns through substitution. */
+static void
+rest_of_handle_combine (tree decl, rtx insns)
+{
+ int rebuild_jump_labels_after_combine = 0;
+
+ timevar_push (TV_COMBINE);
+ open_dump_file (DFI_combine, decl);
+
+ rebuild_jump_labels_after_combine
+ = combine_instructions (insns, max_reg_num ());
+
+ /* Combining insns may have turned an indirect jump into a
+ direct jump. Rebuild the JUMP_LABEL fields of jumping
+ instructions. */
+ if (rebuild_jump_labels_after_combine)
+ {
+ timevar_push (TV_JUMP);
+ rebuild_jump_labels (insns);
+ timevar_pop (TV_JUMP);
+
+ cleanup_cfg (CLEANUP_EXPENSIVE | CLEANUP_UPDATE_LIFE);
+ }
+
+ close_dump_file (DFI_combine, print_rtl_with_bb, insns);
+ timevar_pop (TV_COMBINE);
+
+ ggc_collect ();
+}
+
+/* Perform life analysis. */
+static void
+rest_of_handle_life (tree decl, rtx insns)
+{
+ open_dump_file (DFI_life, decl);
+ regclass_init ();
+
+#ifdef ENABLE_CHECKING
+ verify_flow_info ();
+#endif
+ life_analysis (insns, dump_file, PROP_FINAL);
+ if (optimize)
+ cleanup_cfg ((optimize ? CLEANUP_EXPENSIVE : 0) | CLEANUP_UPDATE_LIFE
+ | CLEANUP_LOG_LINKS
+ | (flag_thread_jumps ? CLEANUP_THREADING : 0));
+ timevar_pop (TV_FLOW);
+
+ if (warn_uninitialized)
+ {
+ uninitialized_vars_warning (DECL_INITIAL (decl));
+ if (extra_warnings)
+ setjmp_args_warning ();
+ }
+
+ if (optimize)
+ {
+ if (!flag_new_regalloc && initialize_uninitialized_subregs ())
+ {
+ /* Insns were inserted, and possibly pseudos created, so
+ things might look a bit different. */
+ insns = get_insns ();
+ allocate_reg_life_data ();
+ update_life_info (NULL, UPDATE_LIFE_GLOBAL_RM_NOTES,
+ PROP_LOG_LINKS | PROP_REG_INFO | PROP_DEATH_NOTES);
+ }
+ }
+
+ no_new_pseudos = 1;
+
+ close_dump_file (DFI_life, print_rtl_with_bb, insns);
+
+ ggc_collect ();
+}
+
+/* Perform common subexpression elimination. Nonzero value from
+ `cse_main' means that jumps were simplified and some code may now
+ be unreachable, so do jump optimization again. */
+static void
+rest_of_handle_cse (tree decl, rtx insns)
+{
+ int tem;
+
+ open_dump_file (DFI_cse, decl);
+ if (dump_file)
+ dump_flow_info (dump_file);
+ timevar_push (TV_CSE);
+
+ reg_scan (insns, max_reg_num (), 1);
+
+ tem = cse_main (insns, max_reg_num (), 0, dump_file);
+ if (tem)
+ rebuild_jump_labels (insns);
+ if (purge_all_dead_edges (0))
+ delete_unreachable_blocks ();
+
+ delete_trivially_dead_insns (insns, max_reg_num ());
+
+ /* If we are not running more CSE passes, then we are no longer
+ expecting CSE to be run. But always rerun it in a cheap mode. */
+ cse_not_expected = !flag_rerun_cse_after_loop && !flag_gcse;
+
+ if (tem || optimize > 1)
+ cleanup_cfg (CLEANUP_EXPENSIVE | CLEANUP_PRE_LOOP);
+ /* Try to identify useless null pointer tests and delete them. */
+ if (flag_delete_null_pointer_checks)
+ {
+ timevar_push (TV_JUMP);
+
+ if (delete_null_pointer_checks (insns))
+ cleanup_cfg (CLEANUP_EXPENSIVE | CLEANUP_PRE_LOOP);
+ timevar_pop (TV_JUMP);
+ }
+
+ /* The second pass of jump optimization is likely to have
+ removed a bunch more instructions. */
+ renumber_insns (dump_file);
+
+ timevar_pop (TV_CSE);
+ close_dump_file (DFI_cse, print_rtl_with_bb, insns);
+}
+
+/* Run second CSE pass after loop optimizations. */
+static void
+rest_of_handle_cse2 (tree decl, rtx insns)
+{
+ int tem;
+
+ timevar_push (TV_CSE2);
+ open_dump_file (DFI_cse2, decl);
+ if (dump_file)
+ dump_flow_info (dump_file);
+ /* CFG is no longer maintained up-to-date. */
+ tem = cse_main (insns, max_reg_num (), 1, dump_file);
+
+ /* Run a pass to eliminate duplicated assignments to condition code
+ registers. We have to run this after bypass_jumps, because it
+ makes it harder for that pass to determine whether a jump can be
+ bypassed safely. */
+ cse_condition_code_reg ();
+
+ purge_all_dead_edges (0);
+ delete_trivially_dead_insns (insns, max_reg_num ());
+
+ if (tem)
+ {
+ timevar_push (TV_JUMP);
+ rebuild_jump_labels (insns);
+ cleanup_cfg (CLEANUP_EXPENSIVE);
+ timevar_pop (TV_JUMP);
+ }
+ reg_scan (insns, max_reg_num (), 0);
+ close_dump_file (DFI_cse2, print_rtl_with_bb, insns);
+ ggc_collect ();
+ timevar_pop (TV_CSE2);
+}
+
+/* Perform global cse. */
+static void
+rest_of_handle_gcse (tree decl, rtx insns)
+{
+ int save_csb, save_cfj;
+ int tem2 = 0, tem;
+
+ timevar_push (TV_GCSE);
+ open_dump_file (DFI_gcse, decl);
+
+ tem = gcse_main (insns, dump_file);
+ rebuild_jump_labels (insns);
+ delete_trivially_dead_insns (insns, max_reg_num ());
+
+ save_csb = flag_cse_skip_blocks;
+ save_cfj = flag_cse_follow_jumps;
+ flag_cse_skip_blocks = flag_cse_follow_jumps = 0;
+
+ /* Instantiate any remaining CONSTANT_P_RTX nodes. */
+ if (current_function_calls_constant_p)
+ purge_builtin_constant_p ();
+
+ /* If -fexpensive-optimizations, re-run CSE to clean up things done
+ by gcse. */
+ if (flag_expensive_optimizations)
+ {
+ timevar_push (TV_CSE);
+ reg_scan (insns, max_reg_num (), 1);
+ tem2 = cse_main (insns, max_reg_num (), 0, dump_file);
+ purge_all_dead_edges (0);
+ delete_trivially_dead_insns (insns, max_reg_num ());
+ timevar_pop (TV_CSE);
+ cse_not_expected = !flag_rerun_cse_after_loop;
+ }
+
+ /* If gcse or cse altered any jumps, rerun jump optimizations to clean
+ things up. Then possibly re-run CSE again. */
+ while (tem || tem2)
+ {
+ tem = tem2 = 0;
+ timevar_push (TV_JUMP);
+ rebuild_jump_labels (insns);
+ cleanup_cfg (CLEANUP_EXPENSIVE | CLEANUP_PRE_LOOP);
+ timevar_pop (TV_JUMP);
+
+ if (flag_expensive_optimizations)
+ {
+ timevar_push (TV_CSE);
+ reg_scan (insns, max_reg_num (), 1);
+ tem2 = cse_main (insns, max_reg_num (), 0, dump_file);
+ purge_all_dead_edges (0);
+ delete_trivially_dead_insns (insns, max_reg_num ());
+ timevar_pop (TV_CSE);
+ }
+ }
+
+ close_dump_file (DFI_gcse, print_rtl_with_bb, insns);
+ timevar_pop (TV_GCSE);
+
+ ggc_collect ();
+ flag_cse_skip_blocks = save_csb;
+ flag_cse_follow_jumps = save_cfj;
+#ifdef ENABLE_CHECKING
+ verify_flow_info ();
+#endif
+}
+
+/* Move constant computations out of loops. */
+static void
+rest_of_handle_loop_optimize (tree decl, rtx insns)
+{
+ int do_unroll, do_prefetch;
+
+ timevar_push (TV_LOOP);
+ delete_dead_jumptables ();
+ cleanup_cfg (CLEANUP_EXPENSIVE | CLEANUP_PRE_LOOP);
+ open_dump_file (DFI_loop, decl);
+
+ /* CFG is no longer maintained up-to-date. */
+ free_bb_for_insn ();
+
+ if (flag_unroll_loops)
+ do_unroll = LOOP_AUTO_UNROLL; /* Having two unrollers is useless. */
+ else
+ do_unroll = flag_old_unroll_loops ? LOOP_UNROLL : LOOP_AUTO_UNROLL;
+ do_prefetch = flag_prefetch_loop_arrays ? LOOP_PREFETCH : 0;
+
+ if (flag_rerun_loop_opt)
+ {
+ cleanup_barriers ();
+
+ /* We only want to perform unrolling once. */
+ loop_optimize (insns, dump_file, do_unroll);
+ do_unroll = 0;
+
+ /* The first call to loop_optimize makes some instructions
+ trivially dead. We delete those instructions now in the
+ hope that doing so will make the heuristics in loop work
+ better and possibly speed up compilation. */
+ delete_trivially_dead_insns (insns, max_reg_num ());
+
+ /* The regscan pass is currently necessary as the alias
+ analysis code depends on this information. */
+ reg_scan (insns, max_reg_num (), 1);
+ }
+ cleanup_barriers ();
+ loop_optimize (insns, dump_file, do_unroll | LOOP_BCT | do_prefetch);
+
+ /* Loop can create trivially dead instructions. */
+ delete_trivially_dead_insns (insns, max_reg_num ());
+ close_dump_file (DFI_loop, print_rtl, insns);
+ timevar_pop (TV_LOOP);
+ find_basic_blocks (insns, max_reg_num (), dump_file);
+
+ ggc_collect ();
+}
+
+/* Perform loop optimizations. It might be better to do them a bit
+ sooner, but we want the profile feedback to work more
+ efficiently. */
+static void
+rest_of_handle_loop2 (tree decl, rtx insns)
+{
+ struct loops *loops;
+ basic_block bb;
+
+ timevar_push (TV_LOOP);
+ open_dump_file (DFI_loop2, decl);
+ if (dump_file)
+ dump_flow_info (dump_file);
+
+ /* Initialize structures for layout changes. */
+ cfg_layout_initialize ();
+
+ loops = loop_optimizer_init (dump_file);
+
+ if (loops)
+ {
+ /* The optimizations: */
+ if (flag_unswitch_loops)
+ unswitch_loops (loops);
+
+ if (flag_peel_loops || flag_unroll_loops)
+ unroll_and_peel_loops (loops,
+ (flag_peel_loops ? UAP_PEEL : 0) |
+ (flag_unroll_loops ? UAP_UNROLL : 0) |
+ (flag_unroll_all_loops ? UAP_UNROLL_ALL : 0));
+
+ loop_optimizer_finalize (loops, dump_file);
+ }
+
+ /* Finalize layout changes. */
+ FOR_EACH_BB (bb)
+ if (bb->next_bb != EXIT_BLOCK_PTR)
+ bb->rbi->next = bb->next_bb;
+ cfg_layout_finalize ();
+
+ cleanup_cfg (CLEANUP_EXPENSIVE);
+ delete_trivially_dead_insns (insns, max_reg_num ());
+ reg_scan (insns, max_reg_num (), 0);
+ if (dump_file)
+ dump_flow_info (dump_file);
+ close_dump_file (DFI_loop2, print_rtl_with_bb, get_insns ());
+ timevar_pop (TV_LOOP);
+ ggc_collect ();
+}
+
+/* This is called from finish_function (within langhooks.parse_file)
+ after each top-level definition is parsed.
+ It is supposed to compile that function or variable
+ and output the assembler code for it.
+ After we return, the tree storage is freed. */
+
+void
+rest_of_compilation (tree decl)
+{
+ rtx insns;
+
+ timevar_push (TV_REST_OF_COMPILATION);
+
+ /* Register rtl specific functions for cfg. */
+ rtl_register_cfg_hooks ();
+
+ /* Now that we're out of the frontend, we shouldn't have any more
+ CONCATs anywhere. */
+ generating_concat_p = 0;
+
+ /* When processing delayed functions, prepare_function_start() won't
+ have been run to re-initialize it. */
+ cse_not_expected = ! optimize;
+
+ /* First, make sure that NOTE_BLOCK is set correctly for each
+ NOTE_INSN_BLOCK_BEG/NOTE_INSN_BLOCK_END note. */
+ if (!cfun->x_whole_function_mode_p)
+ identify_blocks ();
+
+ /* In function-at-a-time mode, we do not attempt to keep the BLOCK
+ tree in sensible shape. So, we just recalculate it here. */
+ if (cfun->x_whole_function_mode_p)
+ reorder_blocks ();
+
+ init_flow ();
+
+ if (rest_of_handle_inlining (decl))
+ goto exit_rest_of_compilation;
+
+ /* If we're emitting a nested function, make sure its parent gets
+ emitted as well. Doing otherwise confuses debug info. */
+ {
+ tree parent;
+ for (parent = DECL_CONTEXT (current_function_decl);
+ parent != NULL_TREE;
+ parent = get_containing_scope (parent))
+ if (TREE_CODE (parent) == FUNCTION_DECL)
+ TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (parent)) = 1;
+ }
+
+ /* We are now committed to emitting code for this function. Do any
+ preparation, such as emitting abstract debug info for the inline
+ before it gets mangled by optimization. */
+ if (cgraph_function_possibly_inlined_p (decl))
+ (*debug_hooks->outlining_inline_function) (decl);
+
+ /* Remove any notes we don't need. That will make iterating
+ over the instruction sequence faster, and allow the garbage
+ collector to reclaim the memory used by the notes. */
+ remove_unnecessary_notes ();
+ reorder_blocks ();
+
+ ggc_collect ();
+
+ /* Initialize some variables used by the optimizers. */
+ init_function_for_compilation ();
+
+ if (! DECL_DEFER_OUTPUT (decl))
+ TREE_ASM_WRITTEN (decl) = 1;
+
+ /* Now that integrate will no longer see our rtl, we need not
+ distinguish between the return value of this function and the
+ return value of called functions. Also, we can remove all SETs
+ of subregs of hard registers; they are only here because of
+ integrate. Also, we can now initialize pseudos intended to
+ carry magic hard reg data throughout the function. */
+ rtx_equal_function_value_matters = 0;
+ purge_hard_subreg_sets (get_insns ());
+
+ /* Early return if there were errors. We can run afoul of our
+ consistency checks, and there's not really much point in fixing them.
+ Don't return yet if -Wreturn-type; we need to do cleanup_cfg. */
+ if (((rtl_dump_and_exit || flag_syntax_only) && !warn_return_type)
+ || errorcount || sorrycount)
+ goto exit_rest_of_compilation;
+
+ timevar_push (TV_JUMP);
+ open_dump_file (DFI_sibling, decl);
+ insns = get_insns ();
+ rebuild_jump_labels (insns);
+ find_exception_handler_labels ();
+ find_basic_blocks (insns, max_reg_num (), dump_file);
+
+ delete_unreachable_blocks ();
+
+ /* Turn NOTE_INSN_PREDICTIONs into branch predictions. */
+ if (flag_guess_branch_prob)
+ {
+ timevar_push (TV_BRANCH_PROB);
+ note_prediction_to_br_prob ();
+ timevar_pop (TV_BRANCH_PROB);
+ }
+
+ if (flag_optimize_sibling_calls)
+ rest_of_handle_sibling_calls (insns);
+
+ /* We have to issue these warnings now already, because CFG cleanups
+ further down may destroy the required information. However, this
+ must be done after the sibcall optimization pass because the barrier
+ emitted for noreturn calls that are candidate for the optimization
+ is folded into the CALL_PLACEHOLDER until after this pass, so the
+ CFG is inaccurate. */
+ check_function_return_warnings ();
+
+ timevar_pop (TV_JUMP);
+
+ insn_locators_initialize ();
+ /* Complete generation of exception handling code. */
+ if (doing_eh (0))
+ {
+ timevar_push (TV_JUMP);
+ open_dump_file (DFI_eh, decl);
+
+ finish_eh_generation ();
+
+ close_dump_file (DFI_eh, print_rtl, get_insns ());
+ timevar_pop (TV_JUMP);
+ }
+
+ /* Delay emitting hard_reg_initial_value sets until after EH landing pad
+ generation, which might create new sets. */
+ emit_initial_value_sets ();
+
+#ifdef FINALIZE_PIC
+ /* If we are doing position-independent code generation, now
+ is the time to output special prologues and epilogues.
+ We do not want to do this earlier, because it just clutters
+ up inline functions with meaningless insns. */
+ if (flag_pic)
+ FINALIZE_PIC;
+#endif
+
+ insns = get_insns ();
+
+ /* Copy any shared structure that should not be shared. */
+ unshare_all_rtl (current_function_decl, insns);
+
+#ifdef SETJMP_VIA_SAVE_AREA
+ /* This must be performed before virtual register instantiation.
+ Please be aware the everything in the compiler that can look
+ at the RTL up to this point must understand that REG_SAVE_AREA
+ is just like a use of the REG contained inside. */
+ if (current_function_calls_alloca)
+ optimize_save_area_alloca (insns);
+#endif
+
+ /* Instantiate all virtual registers. */
+ instantiate_virtual_regs (current_function_decl, insns);
+
+ open_dump_file (DFI_jump, decl);
+
+ /* Always do one jump optimization pass to ensure that JUMP_LABEL fields
+ are initialized and to compute whether control can drop off the end
+ of the function. */
+
+ timevar_push (TV_JUMP);
+ /* Turn NOTE_INSN_EXPECTED_VALUE into REG_BR_PROB. Do this
+ before jump optimization switches branch directions. */
+ if (flag_guess_branch_prob)
+ expected_value_to_br_prob ();
+
+ reg_scan (insns, max_reg_num (), 0);
+ rebuild_jump_labels (insns);
+ find_basic_blocks (insns, max_reg_num (), dump_file);
+ delete_trivially_dead_insns (insns, max_reg_num ());
+ if (dump_file)
+ dump_flow_info (dump_file);
+ cleanup_cfg ((optimize ? CLEANUP_EXPENSIVE : 0) | CLEANUP_PRE_LOOP
+ | (flag_thread_jumps ? CLEANUP_THREADING : 0));
+
+ if (optimize)
+ {
+ free_bb_for_insn ();
+ copy_loop_headers (insns);
+ find_basic_blocks (insns, max_reg_num (), dump_file);
+ }
+ purge_line_number_notes (insns);
+
+ timevar_pop (TV_JUMP);
+ close_dump_file (DFI_jump, print_rtl, insns);
+
+ /* Now is when we stop if -fsyntax-only and -Wreturn-type. */
+ if (rtl_dump_and_exit || flag_syntax_only || DECL_DEFER_OUTPUT (decl))
+ goto exit_rest_of_compilation;
+
+ timevar_push (TV_JUMP);
+
+ if (optimize)
+ cleanup_cfg (CLEANUP_EXPENSIVE | CLEANUP_PRE_LOOP);
+
+ if (flag_delete_null_pointer_checks)
+ rest_of_handle_null_pointer (decl, insns);
+
+ /* Jump optimization, and the removal of NULL pointer checks, may
+ have reduced the number of instructions substantially. CSE, and
+ future passes, allocate arrays whose dimensions involve the
+ maximum instruction UID, so if we can reduce the maximum UID
+ we'll save big on memory. */
+ renumber_insns (dump_file);
+ timevar_pop (TV_JUMP);
+
+ close_dump_file (DFI_jump, print_rtl_with_bb, insns);
+
+ ggc_collect ();
+
+ if (optimize > 0)
+ rest_of_handle_cse (decl, insns);
+
+ rest_of_handle_addressof (decl, insns);
+
+ ggc_collect ();
+
+ if (optimize > 0)
+ {
+ if (flag_gcse)
+ rest_of_handle_gcse (decl, insns);
+
+ if (flag_loop_optimize)
+ rest_of_handle_loop_optimize (decl, insns);
+
+ if (flag_gcse)
+ rest_of_handle_jump_bypass (decl, insns);
+ }
+
+ timevar_push (TV_FLOW);
+
+ rest_of_handle_cfg (decl, insns);
+
+ if (optimize > 0
+ || profile_arc_flag || flag_test_coverage || flag_branch_probabilities)
+ {
+ rest_of_handle_branch_prob (decl, insns);
+
+ if (flag_branch_probabilities
+ && flag_profile_values
+ && flag_value_profile_transformations)
+ rest_of_handle_value_profile_transformations (decl, insns);
+
+ /* Remove the death notes created for vpt. */
+ if (flag_profile_values)
+ count_or_remove_death_notes (NULL, 1);
+ }
+
+ if (optimize > 0)
+ rest_of_handle_if_conversion (decl, insns);
+
+ if (flag_tracer)
+ rest_of_handle_tracer (decl, insns);
+
+ if (optimize > 0
+ && (flag_unswitch_loops
+ || flag_peel_loops
+ || flag_unroll_loops))
+ rest_of_handle_loop2 (decl, insns);
+
+ if (flag_web)
+ rest_of_handle_web (decl, insns);
+
+ if (flag_rerun_cse_after_loop)
+ rest_of_handle_cse2 (decl, insns);
+
+ cse_not_expected = 1;
+
+ rest_of_handle_life (decl, insns);
+
+ if (optimize > 0)
+ rest_of_handle_combine (decl, insns);
+
+ if (flag_if_conversion)
+ rest_of_handle_if_after_combine (decl, insns);
+
+ if (optimize > 0 && (flag_regmove || flag_expensive_optimizations))
+ rest_of_handle_regmove (decl, insns);
+
+ /* Do unconditional splitting before register allocation to allow machine
+ description to add extra information not needed previously. */
+ split_all_insns (1);
+
+#ifdef OPTIMIZE_MODE_SWITCHING
+ timevar_push (TV_MODE_SWITCH);
+
+ no_new_pseudos = 0;
+ optimize_mode_switching (NULL);
+ no_new_pseudos = 1;
+
+ timevar_pop (TV_MODE_SWITCH);
+#endif
+
+ /* Any of the several passes since flow1 will have munged register
+ lifetime data a bit. We need it to be up to date for scheduling
+ (see handling of reg_known_equiv in init_alias_analysis). */
+ recompute_reg_usage (insns, !optimize_size);
+
+#ifdef INSN_SCHEDULING
+ rest_of_handle_sched (decl, insns);
+#endif
+
+ /* Determine if the current function is a leaf before running reload
+ since this can impact optimizations done by the prologue and
+ epilogue thus changing register elimination offsets. */
+ current_function_is_leaf = leaf_function_p ();
+
+ timevar_push (TV_LOCAL_ALLOC);
+ open_dump_file (DFI_lreg, decl);
+
+ if (flag_new_regalloc)
+ {
+ if (rest_of_handle_new_regalloc (decl, insns))
+ goto exit_rest_of_compilation;
+ }
+ else
+ {
+ if (rest_of_handle_old_regalloc (decl, insns))
+ goto exit_rest_of_compilation;
+ }
+
+ ggc_collect ();
+
+ open_dump_file (DFI_postreload, decl);
+
+ /* Do a very simple CSE pass over just the hard registers. */
+ if (optimize > 0)
+ {
+ timevar_push (TV_RELOAD_CSE_REGS);
+ reload_cse_regs (insns);
+ /* reload_cse_regs can eliminate potentially-trapping MEMs.
+ Remove any EH edges associated with them. */
+ if (flag_non_call_exceptions)
+ purge_all_dead_edges (0);
+ timevar_pop (TV_RELOAD_CSE_REGS);
+ }
+
+ close_dump_file (DFI_postreload, print_rtl_with_bb, insns);
+
+ /* Re-create the death notes which were deleted during reload. */
+ timevar_push (TV_FLOW2);
+ open_dump_file (DFI_flow2, decl);
+
+#ifdef ENABLE_CHECKING
+ verify_flow_info ();
+#endif
+
+ /* If optimizing, then go ahead and split insns now. */
+#ifndef STACK_REGS
+ if (optimize > 0)
+#endif
+ split_all_insns (0);
+
+ if (flag_branch_target_load_optimize)
+ {
+ open_dump_file (DFI_branch_target_load, decl);
+
+ branch_target_load_optimize (insns, false);
+
+ close_dump_file (DFI_branch_target_load, print_rtl_with_bb, insns);
+
+ ggc_collect ();
+ }
+
+ if (optimize)
+ cleanup_cfg (CLEANUP_EXPENSIVE);
+
+ /* On some machines, the prologue and epilogue code, or parts thereof,
+ can be represented as RTL. Doing so lets us schedule insns between
+ it and the rest of the code and also allows delayed branch
+ scheduling to operate in the epilogue. */
+ thread_prologue_and_epilogue_insns (insns);
+ epilogue_completed = 1;
+
+ if (optimize)
+ {
+ life_analysis (insns, dump_file, PROP_POSTRELOAD);
+ cleanup_cfg (CLEANUP_EXPENSIVE | CLEANUP_UPDATE_LIFE
+ | (flag_crossjumping ? CLEANUP_CROSSJUMP : 0));
+
+ /* This is kind of a heuristic. We need to run combine_stack_adjustments
+ even for machines with possibly nonzero RETURN_POPS_ARGS
+ and ACCUMULATE_OUTGOING_ARGS. We expect that only ports having
+ push instructions will have popping returns. */
+#ifndef PUSH_ROUNDING
+ if (!ACCUMULATE_OUTGOING_ARGS)
+#endif
+ combine_stack_adjustments ();
+
+ ggc_collect ();
+ }
+
+ flow2_completed = 1;
+
+ close_dump_file (DFI_flow2, print_rtl_with_bb, insns);
+ timevar_pop (TV_FLOW2);
+
+#ifdef HAVE_peephole2
+ if (optimize > 0 && flag_peephole2)
+ {
+ timevar_push (TV_PEEPHOLE2);
+ open_dump_file (DFI_peephole2, decl);
+
+ peephole2_optimize (dump_file);
+
+ close_dump_file (DFI_peephole2, print_rtl_with_bb, insns);
+ timevar_pop (TV_PEEPHOLE2);
+ }
+#endif
+
+ open_dump_file (DFI_ce3, decl);
+ if (optimize)
+ /* Last attempt to optimize CFG, as scheduling, peepholing and insn
+ splitting possibly introduced more crossjumping opportunities. */
+ cleanup_cfg (CLEANUP_EXPENSIVE
+ | CLEANUP_UPDATE_LIFE
+ | (flag_crossjumping ? CLEANUP_CROSSJUMP : 0));
+ if (flag_if_conversion2)
+ {
+ timevar_push (TV_IFCVT2);
+
+ if_convert (1);
+
+ timevar_pop (TV_IFCVT2);
+ }
+ close_dump_file (DFI_ce3, print_rtl_with_bb, insns);
+
+ if (optimize > 0)
+ {
+ if (flag_rename_registers || flag_cprop_registers)
+ rest_of_handle_regrename (decl, insns);
+
+ rest_of_handle_reorder_blocks (decl, insns);
+ }
+
+ if (flag_branch_target_load_optimize2)
+ {
+ /* Leave this a warning for now so that it is possible to experiment
+ with running this pass twice. In 3.6, we should either make this
+ an error, or use separate dump files. */
+ if (flag_branch_target_load_optimize)
+ warning ("branch target register load optimization is not intended "
+ "to be run twice");
+
+ open_dump_file (DFI_branch_target_load, decl);
+
+ branch_target_load_optimize (insns, true);
+
+ close_dump_file (DFI_branch_target_load, print_rtl_with_bb, insns);
+
+ ggc_collect ();
+ }
+
+#ifdef INSN_SCHEDULING
+ if (optimize > 0 && flag_schedule_insns_after_reload)
+ rest_of_handle_sched2 (decl, insns);
+#endif
+
+#ifdef LEAF_REGISTERS
+ current_function_uses_only_leaf_regs
+ = optimize > 0 && only_leaf_regs_used () && leaf_function_p ();
+#endif
+
+#ifdef STACK_REGS
+ rest_of_handle_stack_regs (decl, insns);
+#endif
+
+ compute_alignments ();
+
+ if (flag_var_tracking)
+ rest_of_handle_variable_tracking (decl, insns);
+
+ /* CFG is no longer maintained up-to-date. */
+ free_bb_for_insn ();
+
+ if (targetm.machine_dependent_reorg != 0)
+ rest_of_handle_machine_reorg (decl, insns);
+
+ purge_line_number_notes (insns);
+ cleanup_barriers ();
+
+#ifdef DELAY_SLOTS
+ if (optimize > 0 && flag_delayed_branch)
+ rest_of_handle_delay_slots (decl, insns);
+#endif
+
+#if defined (HAVE_ATTR_length) && !defined (STACK_REGS)
+ timevar_push (TV_SHORTEN_BRANCH);
+ split_all_insns_noflow ();
+ timevar_pop (TV_SHORTEN_BRANCH);
+#endif
+
+ convert_to_eh_region_ranges ();
+
+ /* Shorten branches. */
+ timevar_push (TV_SHORTEN_BRANCH);
+ shorten_branches (get_insns ());
+ timevar_pop (TV_SHORTEN_BRANCH);
+
+ set_nothrow_function_flags ();
+ if (current_function_nothrow)
+ /* Now we know that this can't throw; set the flag for the benefit
+ of other functions later in this translation unit. */
+ TREE_NOTHROW (current_function_decl) = 1;
+
+ rest_of_handle_final (decl, insns);
+
+ /* Write DBX symbols if requested. */
+
+ /* Note that for those inline functions where we don't initially
+ know for certain that we will be generating an out-of-line copy,
+ the first invocation of this routine (rest_of_compilation) will
+ skip over this code by doing a `goto exit_rest_of_compilation;'.
+ Later on, wrapup_global_declarations will (indirectly) call
+ rest_of_compilation again for those inline functions that need
+ to have out-of-line copies generated. During that call, we
+ *will* be routed past here. */
+
+ timevar_push (TV_SYMOUT);
+ (*debug_hooks->function_decl) (decl);
+ timevar_pop (TV_SYMOUT);
+
+ exit_rest_of_compilation:
+
+ coverage_end_function ();
+
+ /* In case the function was not output,
+ don't leave any temporary anonymous types
+ queued up for sdb output. */
+#ifdef SDB_DEBUGGING_INFO
+ if (write_symbols == SDB_DEBUG)
+ sdbout_types (NULL_TREE);
+#endif
+
+ reload_completed = 0;
+ epilogue_completed = 0;
+ flow2_completed = 0;
+ no_new_pseudos = 0;
+
+ timevar_push (TV_FINAL);
+
+ /* Clear out the insn_length contents now that they are no
+ longer valid. */
+ init_insn_lengths ();
+
+ /* Show no temporary slots allocated. */
+ init_temp_slots ();
+
+ free_basic_block_vars (0);
+ free_bb_for_insn ();
+
+ timevar_pop (TV_FINAL);
+
+ if ((*targetm.binds_local_p) (current_function_decl))
+ {
+ int pref = cfun->preferred_stack_boundary;
+ if (cfun->recursive_call_emit
+ && cfun->stack_alignment_needed > cfun->preferred_stack_boundary)
+ pref = cfun->stack_alignment_needed;
+ cgraph_rtl_info (current_function_decl)->preferred_incoming_stack_boundary
+ = pref;
+ }
+
+ /* Make sure volatile mem refs aren't considered valid operands for
+ arithmetic insns. We must call this here if this is a nested inline
+ function, since the above code leaves us in the init_recog state
+ (from final.c), and the function context push/pop code does not
+ save/restore volatile_ok.
+
+ ??? Maybe it isn't necessary for expand_start_function to call this
+ anymore if we do it here? */
+
+ init_recog_no_volatile ();
+
+ /* We're done with this function. Free up memory if we can. */
+ free_after_parsing (cfun);
+ if (! DECL_DEFER_OUTPUT (decl))
+ {
+ free_after_compilation (cfun);
+ DECL_STRUCT_FUNCTION (decl) = 0;
+ }
+ cfun = 0;
+
+ ggc_collect ();
+
+ timevar_pop (TV_REST_OF_COMPILATION);
+}
+
+void
+init_optimization_passes (void)
+{
+ if (flag_unit_at_a_time)
+ {
+ open_dump_file (DFI_cgraph, NULL);
+ cgraph_dump_file = dump_file;
+ dump_file = NULL;
+ }
+}
+
+void
+finish_optimization_passes (void)
+{
+ if (profile_arc_flag || flag_test_coverage || flag_branch_probabilities)
+ {
+ timevar_push (TV_DUMP);
+ open_dump_file (DFI_bp, NULL);
+
+ end_branch_prob ();
+
+ close_dump_file (DFI_bp, NULL, NULL_RTX);
+ timevar_pop (TV_DUMP);
+ }
+
+ if (optimize > 0 && open_dump_file (DFI_combine, NULL))
+ {
+ timevar_push (TV_DUMP);
+ dump_combine_total_stats (dump_file);
+ close_dump_file (DFI_combine, NULL, NULL_RTX);
+ timevar_pop (TV_DUMP);
+ }
+
+ if (flag_unit_at_a_time)
+ {
+ dump_file = cgraph_dump_file;
+ cgraph_dump_file = NULL;
+ close_dump_file (DFI_cgraph, NULL, NULL_RTX);
+ }
+
+ /* Do whatever is necessary to finish printing the graphs. */
+ if (graph_dump_format != no_graph)
+ {
+ int i;
+
+ for (i = 0; i < (int) DFI_MAX; ++i)
+ if (dump_file_tbl[i].initialized && dump_file_tbl[i].graph_dump_p)
+ {
+ char seq[16];
+ char *suffix;
+
+ sprintf (seq, DUMPFILE_FORMAT, i);
+ suffix = concat (seq, dump_file_tbl[i].extension, NULL);
+ finish_graph_dump_file (dump_base_name, suffix);
+ free (suffix);
+ }
+ }
+
+}
+
+bool
+enable_rtl_dump_file (int letter)
+{
+ bool matched = false;
+ int i;
+
+ if (letter == 'a')
+ {
+ for (i = 0; i < (int) DFI_MAX; ++i)
+ dump_file_tbl[i].enabled = 1;
+ matched = true;
+ }
+ else
+ {
+ for (i = 0; i < (int) DFI_MAX; ++i)
+ if (letter == dump_file_tbl[i].debug_switch)
+ {
+ dump_file_tbl[i].enabled = 1;
+ matched = true;
+ }
+ }
+
+ return matched;
+}