aboutsummaryrefslogtreecommitdiff
path: root/gcc/config/mips/frame-header-opt.c
diff options
context:
space:
mode:
authorSteve Ellcey <sellcey@imgtec.com>2015-10-09 17:10:42 +0000
committerSteve Ellcey <sje@gcc.gnu.org>2015-10-09 17:10:42 +0000
commitd41c8b4c43d7050f05668f23ad4563ed6e9b41d0 (patch)
treeec2b873ea269f855d61f77eb73a55bc41a5d0dc5 /gcc/config/mips/frame-header-opt.c
parentb867c7ca88776d062e3e62c212fb0a59b6d688c9 (diff)
downloadgcc-d41c8b4c43d7050f05668f23ad4563ed6e9b41d0.zip
gcc-d41c8b4c43d7050f05668f23ad4563ed6e9b41d0.tar.gz
gcc-d41c8b4c43d7050f05668f23ad4563ed6e9b41d0.tar.bz2
config.gcc (mips*-*-*): Add frame-header-opt.o to extra_objs.
2015-10-05 Steve Ellcey <sellcey@imgtec.com> * config.gcc (mips*-*-*): Add frame-header-opt.o to extra_objs. * frame-header-opt.c: New file. * config/mips/mips-proto.h (mips_register_frame_header_opt): Add prototype. * config/mips/mips.c (mips_compute_frame_info): Check optimize_call_stack flag. (mips_option_override): Register new frame_header_opt pass. (mips_frame_info, mips_int_mask, mips_shadow_set, machine_function): Move these types to... * config/mips/mips.h: here. (machine_function): Add does_not_use_frame_header and optimize_call_stack fields. * config/mips/t-mips (frame-header-opt.o): Add new make rule. * doc/invoke.texi (-mframe-header-opt, -mno-frame-header-opt): Document new flags. * config/mips/mips.opt (mframe-header-opt): Add new option. From-SVN: r228666
Diffstat (limited to 'gcc/config/mips/frame-header-opt.c')
-rw-r--r--gcc/config/mips/frame-header-opt.c216
1 files changed, 216 insertions, 0 deletions
diff --git a/gcc/config/mips/frame-header-opt.c b/gcc/config/mips/frame-header-opt.c
new file mode 100644
index 0000000..7c7b1f2
--- /dev/null
+++ b/gcc/config/mips/frame-header-opt.c
@@ -0,0 +1,216 @@
+/* Analyze functions to determine if callers need to allocate a frame header
+ on the stack. The frame header is used by callees to save their arguments.
+ This optimization is specific to TARGET_OLDABI targets. For TARGET_NEWABI
+ targets, if a frame header is required, it is allocated by the callee.
+
+
+ Copyright (C) 2015 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 3, 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 COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+
+#include "config.h"
+#include "system.h"
+#include "context.h"
+#include "coretypes.h"
+#include "tree.h"
+#include "tree-core.h"
+#include "tree-pass.h"
+#include "target.h"
+#include "target-globals.h"
+#include "cfg.h"
+#include "cgraph.h"
+#include "function.h"
+#include "basic-block.h"
+#include "gimple.h"
+#include "gimple-iterator.h"
+#include "gimple-walk.h"
+
+static unsigned int frame_header_opt (void);
+
+namespace {
+
+const pass_data pass_data_ipa_frame_header_opt =
+{
+ IPA_PASS, /* type */
+ "frame-header-opt", /* name */
+ OPTGROUP_NONE, /* optinfo_flags */
+ TV_CGRAPHOPT, /* tv_id */
+ 0, /* properties_required */
+ 0, /* properties_provided */
+ 0, /* properties_destroyed */
+ 0, /* todo_flags_start */
+ 0, /* todo_flags_finish */
+};
+
+class pass_ipa_frame_header_opt : public ipa_opt_pass_d
+{
+public:
+ pass_ipa_frame_header_opt (gcc::context *ctxt)
+ : ipa_opt_pass_d (pass_data_ipa_frame_header_opt, ctxt,
+ NULL, /* generate_summary */
+ NULL, /* write_summary */
+ NULL, /* read_summary */
+ NULL, /* write_optimization_summary */
+ NULL, /* read_optimization_summary */
+ NULL, /* stmt_fixup */
+ 0, /* function_transform_todo_flags_start */
+ NULL, /* function_transform */
+ NULL) /* variable_transform */
+ {}
+
+ /* opt_pass methods: */
+ virtual bool gate (function *)
+ {
+ /* This optimization has no affect if TARGET_NEWABI. If optimize
+ is not at least 1 then the data needed for the optimization is
+ not available and nothing will be done anyway. */
+ return TARGET_OLDABI && flag_frame_header_optimization;
+ }
+
+ virtual unsigned int execute (function *) { return frame_header_opt (); }
+
+}; // class pass_ipa_frame_header_opt
+
+} // anon namespace
+
+static ipa_opt_pass_d *
+make_pass_ipa_frame_header_opt (gcc::context *ctxt)
+{
+ return new pass_ipa_frame_header_opt (ctxt);
+}
+
+void
+mips_register_frame_header_opt (void)
+{
+ opt_pass *p = make_pass_ipa_frame_header_opt (g);
+ static struct register_pass_info f =
+ {p, "comdats", 1, PASS_POS_INSERT_AFTER };
+ register_pass (&f);
+}
+
+
+/* Return true if it is certain that this is a leaf function. False if it is
+ not a leaf function or if it is impossible to tell. */
+
+static bool
+is_leaf_function (function *fn)
+{
+ basic_block bb;
+ gimple_stmt_iterator gsi;
+
+ /* If we do not have a cfg for this function be conservative and assume
+ it is not a leaf function. */
+ if (fn->cfg == NULL)
+ return false;
+
+ FOR_EACH_BB_FN (bb, fn)
+ for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
+ if (is_gimple_call (gsi_stmt (gsi)))
+ return false;
+ return true;
+}
+
+/* Return true if this function will use the stack space allocated by its
+ caller or if we cannot determine for certain that it does not. */
+
+static bool
+needs_frame_header_p (function *fn)
+{
+ tree t;
+
+ if (fn->decl == NULL)
+ return true;
+
+ if (fn->stdarg || !is_leaf_function (fn))
+ return true;
+
+ for (t = DECL_ARGUMENTS (fn->decl); t; t = TREE_CHAIN (t))
+ {
+ if (!use_register_for_decl (t))
+ return true;
+ }
+
+ return false;
+}
+
+/* Returns TRUE if the argument stack space allocated by function FN is used.
+ Returns FALSE if the space is needed or if the need for the space cannot
+ be determined. */
+
+static bool
+callees_functions_use_frame_header (function *fn)
+{
+ basic_block bb;
+ gimple_stmt_iterator gsi;
+ gimple *stmt;
+ tree called_fn_tree;
+ function *called_fn;
+
+ if (fn->cfg == NULL)
+ return true;
+
+ FOR_EACH_BB_FN (bb, fn)
+ {
+ for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
+ {
+ stmt = gsi_stmt (gsi);
+ if (is_gimple_call (stmt))
+ {
+ called_fn_tree = gimple_call_fndecl (stmt);
+ if (called_fn_tree != NULL)
+ {
+ called_fn = DECL_STRUCT_FUNCTION (called_fn_tree);
+ if (called_fn == NULL
+ || DECL_WEAK (called_fn_tree)
+ || !called_fn->machine->does_not_use_frame_header)
+ return true;
+ }
+ else
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+/* Scan each function to determine those that need its frame headers. Perform
+ a second scan to determine if the allocation can be skipped because none of
+ their callees require the frame header. */
+
+static unsigned int
+frame_header_opt ()
+{
+ struct cgraph_node *node;
+ function *fn;
+
+ FOR_EACH_DEFINED_FUNCTION (node)
+ {
+ fn = node->get_fun ();
+ if (fn != NULL)
+ fn->machine->does_not_use_frame_header = !needs_frame_header_p (fn);
+ }
+
+ FOR_EACH_DEFINED_FUNCTION (node)
+ {
+ fn = node->get_fun ();
+ if (fn != NULL)
+ fn->machine->optimize_call_stack
+ = !callees_functions_use_frame_header (fn);
+ }
+ return 0;
+}