aboutsummaryrefslogtreecommitdiff
path: root/gcc/config/mips/frame-header-opt.cc
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/config/mips/frame-header-opt.cc')
-rw-r--r--gcc/config/mips/frame-header-opt.cc292
1 files changed, 292 insertions, 0 deletions
diff --git a/gcc/config/mips/frame-header-opt.cc b/gcc/config/mips/frame-header-opt.cc
new file mode 100644
index 0000000..a29b823
--- /dev/null
+++ b/gcc/config/mips/frame-header-opt.cc
@@ -0,0 +1,292 @@
+/* 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-2022 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/>. */
+
+
+#define IN_TARGET_CODE 1
+
+#include "config.h"
+#include "system.h"
+#include "context.h"
+#include "coretypes.h"
+#include "backend.h"
+#include "tree.h"
+#include "tree-core.h"
+#include "tree-pass.h"
+#include "target.h"
+#include "target-globals.h"
+#include "profile-count.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 && optimize > 0;
+ }
+
+ 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);
+ 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 has inline assembly code or if we cannot
+ be certain that it does not. False if we know that there is no inline
+ assembly. */
+
+static bool
+has_inlined_assembly (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 may have inline assembly. */
+ 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))
+ if (gimple_code (gsi_stmt (gsi)) == GIMPLE_ASM)
+ return true;
+
+ return false;
+}
+
+/* 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)
+ return true;
+
+ for (t = DECL_ARGUMENTS (fn->decl); t; t = TREE_CHAIN (t))
+ {
+ if (!use_register_for_decl (t))
+ return true;
+
+ /* Some 64-bit types may get copied to general registers using the frame
+ header, see mips_output_64bit_xfer. Checking for SImode only may be
+ overly restrictive but it is guaranteed to be safe. */
+ if (DECL_MODE (t) != SImode)
+ return true;
+ }
+
+ return false;
+}
+
+/* Return true if the argument stack space allocated by function FN is used.
+ Return 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)
+ || has_inlined_assembly (called_fn)
+ || !is_leaf_function (called_fn)
+ || !called_fn->machine->does_not_use_frame_header)
+ return true;
+ }
+ else
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+/* Set the callers_may_not_allocate_frame flag for any function which
+ function FN calls because FN may not allocate a frame header. */
+
+static void
+set_callers_may_not_allocate_frame (function *fn)
+{
+ basic_block bb;
+ gimple_stmt_iterator gsi;
+ gimple *stmt;
+ tree called_fn_tree;
+ function *called_fn;
+
+ if (fn->cfg == NULL)
+ return;
+
+ 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)
+ called_fn->machine->callers_may_not_allocate_frame = true;
+ }
+ }
+ }
+ }
+ return;
+}
+
+/* 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) && !is_leaf_function (fn);
+ }
+
+ FOR_EACH_DEFINED_FUNCTION (node)
+ {
+ fn = node->get_fun ();
+ if (fn != NULL && fn->machine->optimize_call_stack)
+ set_callers_may_not_allocate_frame (fn);
+ }
+
+ return 0;
+}