diff mbox

[MIPS] Frame header optimization for MIPS O32 ABI

Message ID 1441994741.16610.6.camel@ubuntu-sellcey
State New
Headers show

Commit Message

Steve Ellcey Sept. 11, 2015, 6:05 p.m. UTC
On Fri, 2015-09-04 at 01:40 -0700, Matthew Fortune wrote:

> A few comments below. I found some of the comments a bit hard to parse but have
> not attempted any rewording. I'd like Catherine to comment too as I have barely
> any experience at the gimple level to know if this accounts for any necessary
> subtleties.

Catherine said she would look at this next week but I have updated the
patch in the mean time to address your comments and give Catherine a
more up-to-date patch to look over.

The only functional change is in callees_functions_use_frame_header (was
called_functions_use_stack) where I check for weak functions and where I
return true if gimple_call_fndecl returned NULL.  I am not sure in what
exact situations gimple_call_fndecl will return NULL but I did run into
that when testing and so we do the conservative thing in that case and
assume we need the frame header.

I also used your example to add 3 tests in order to verify when the
optimization does and does not happen.

Steve Ellcey
sellcey@imgtec.com


2015-09-11  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.

Comments

Moore, Catherine Sept. 28, 2015, 10:10 p.m. UTC | #1
> -----Original Message-----
> From: Steve Ellcey [mailto:sellcey@imgtec.com]
> Sent: Friday, September 11, 2015 2:06 PM
> To: Matthew Fortune
> Cc: GCC Patches; Moore, Catherine
> Subject: RE: [PATCH, MIPS] Frame header optimization for MIPS O32 ABI
> 
> On Fri, 2015-09-04 at 01:40 -0700, Matthew Fortune wrote:
> 
> > A few comments below. I found some of the comments a bit hard to parse
> but have
> > not attempted any rewording. I'd like Catherine to comment too as I have
> barely
> > any experience at the gimple level to know if this accounts for any
> necessary
> > subtleties.
> 
> Catherine said she would look at this next week but I have updated the
> patch in the mean time to address your comments and give Catherine a
> more up-to-date patch to look over.
> 

Hi Steve, I'm sorry for the delay in reviewing this patch. 
Some changes have been committed upstream (see revision #227941) that will require updates to this patch.
Please post the update for review.  Other comments are embedded.

> 
> diff --git a/gcc/config.gcc b/gcc/config.gcc
> index 5712547..eea97de 100644
> --- a/gcc/config.gcc
> +++ b/gcc/config.gcc
> @@ -420,6 +420,7 @@ microblaze*-*-*)
>  mips*-*-*)
>  	cpu_type=mips
>  	extra_headers="loongson.h"
> +	extra_objs="frame-header-opt.o"
>  	extra_options="${extra_options} g.opt fused-madd.opt mips/mips-
> tables.opt"
>  	;;
>  nds32*)
> diff --git a/gcc/config/mips/frame-header-opt.c b/gcc/config/mips/frame-
> header-opt.c
> index e69de29..5c4e93c 100644
> --- a/gcc/config/mips/frame-header-opt.c
> +++ b/gcc/config/mips/frame-header-opt.c
> @@ -0,0 +1,221 @@
> +/* Analyze functions to determine if calling functions need to allocate
> +   stack space (a frame header) for its called functions to write out their
> +   arguments on to the stack.  This optimization is only applicable to
> +   TARGET_OLDABI targets because calling functions on TARGET_NEWABI
> targets
> +   do not allocate any stack space for arguments (the called function does it
> +   if needed).
> +

Overall, I agree with Matthew regarding the comments being a little hard to parse.
How about:

/* Analyze functions to determine if callers need to allocate a frame header on the stack.  The frame header is used by callees to save its 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.  */


> +
> +/* Look at all the functions this function calls and return true if none of
> +   them need the argument stack space that this function would normally
> +   allocate.  Return false if one or more functions does need this space
> +   or if we cannot determine that all called functions do not need the
> +   space.  */

/* 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
> +}
> +
> +/* This optimization scans all the functions in the compilation unit to find
> +   out which ones do not need the frame header that their caller normally
> +   allocates.  Then it does a second scan of all the functions to determine
> +   which functions can skip the allocation because none of the functions it
> +   calls need the frame header.  */
> +

  /* 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 its callees require the frame header.  */
> +}
> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> index c0ec0fd..8152645 100644
> --- a/gcc/doc/invoke.texi
> +++ b/gcc/doc/invoke.texi
> @@ -17940,6 +17941,18 @@ if @var{ra-address} is nonnull.
> 
>  The default is @option{-mno-mcount-ra-address}.
> 
> +@item -mframe-header-opt
> +@itemx -mno-frame-header-opt
> +@opindex mframe-header-opt
> +Enable (disable) frame header optimization in the o32 ABI.  When using
> +the o32 ABI, calling functions allocate 16 bytes on the stack in case
> +the called function needs to write out register arguments to memory so
> +that their address can be taken.  When enabled, this optimization will
> +cause the calling function to not allocate that space if it can determine
> +that none of its called functions use it.
> +
> +This optimization is off by default at all optimization levels.
> +
>  @end table
> 
>  @node MMIX Options

How about this instead:
Enable (disable) frame header optimization in the o32 ABI.  When using the o32
ABI, calling functions will allocate 16 bytes on the stack for the called function
to write out register arguments.  When enabled, this optimization will suppress the
allocation of the frame header if it can be determined that it is unused.

This optimization is off by default at all optimization levels.

Catherine
diff mbox

Patch

diff --git a/gcc/config.gcc b/gcc/config.gcc
index 5712547..eea97de 100644
--- a/gcc/config.gcc
+++ b/gcc/config.gcc
@@ -420,6 +420,7 @@  microblaze*-*-*)
 mips*-*-*)
 	cpu_type=mips
 	extra_headers="loongson.h"
+	extra_objs="frame-header-opt.o"
 	extra_options="${extra_options} g.opt fused-madd.opt mips/mips-tables.opt"
 	;;
 nds32*)
diff --git a/gcc/config/mips/frame-header-opt.c b/gcc/config/mips/frame-header-opt.c
index e69de29..5c4e93c 100644
--- a/gcc/config/mips/frame-header-opt.c
+++ b/gcc/config/mips/frame-header-opt.c
@@ -0,0 +1,221 @@ 
+/* Analyze functions to determine if calling functions need to allocate
+   stack space (a frame header) for its called functions to write out their
+   arguments on to the stack.  This optimization is only applicable to
+   TARGET_OLDABI targets because calling functions on TARGET_NEWABI targets
+   do not allocate any stack space for arguments (the called function does it
+   if needed).
+
+   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;
+}
+
+/* Look at all the functions this function calls and return true if none of
+   them need the argument stack space that this function would normally
+   allocate.  Return false if one or more functions does need this space
+   or if we cannot determine that all called functions do not need the
+   space.  */
+
+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;
+}
+
+/* This optimization scans all the functions in the compilation unit to find
+   out which ones do not need the frame header that their caller normally
+   allocates.  Then it does a second scan of all the functions to determine
+   which functions can skip the allocation because none of the functions it
+   calls need 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;
+}
diff --git a/gcc/config/mips/mips-protos.h b/gcc/config/mips/mips-protos.h
index d9ad910..de050f4 100644
--- a/gcc/config/mips/mips-protos.h
+++ b/gcc/config/mips/mips-protos.h
@@ -368,4 +368,6 @@  typedef rtx (*mulsidi3_gen_fn) (rtx, rtx, rtx);
 extern mulsidi3_gen_fn mips_mulsidi3_gen_fn (enum rtx_code);
 #endif
 
+extern void mips_register_frame_header_opt (void);
+
 #endif /* ! GCC_MIPS_PROTOS_H */
diff --git a/gcc/config/mips/mips.c b/gcc/config/mips/mips.c
index 0b4a5fa..5a50883 100644
--- a/gcc/config/mips/mips.c
+++ b/gcc/config/mips/mips.c
@@ -327,153 +327,6 @@  static struct {
   bool fast_mult_zero_zero_p;
 } mips_tuning_info;
 
-/* Information about a function's frame layout.  */
-struct GTY(())  mips_frame_info {
-  /* The size of the frame in bytes.  */
-  HOST_WIDE_INT total_size;
-
-  /* The number of bytes allocated to variables.  */
-  HOST_WIDE_INT var_size;
-
-  /* The number of bytes allocated to outgoing function arguments.  */
-  HOST_WIDE_INT args_size;
-
-  /* The number of bytes allocated to the .cprestore slot, or 0 if there
-     is no such slot.  */
-  HOST_WIDE_INT cprestore_size;
-
-  /* Bit X is set if the function saves or restores GPR X.  */
-  unsigned int mask;
-
-  /* Likewise FPR X.  */
-  unsigned int fmask;
-
-  /* Likewise doubleword accumulator X ($acX).  */
-  unsigned int acc_mask;
-
-  /* The number of GPRs, FPRs, doubleword accumulators and COP0
-     registers saved.  */
-  unsigned int num_gp;
-  unsigned int num_fp;
-  unsigned int num_acc;
-  unsigned int num_cop0_regs;
-
-  /* The offset of the topmost GPR, FPR, accumulator and COP0-register
-     save slots from the top of the frame, or zero if no such slots are
-     needed.  */
-  HOST_WIDE_INT gp_save_offset;
-  HOST_WIDE_INT fp_save_offset;
-  HOST_WIDE_INT acc_save_offset;
-  HOST_WIDE_INT cop0_save_offset;
-
-  /* Likewise, but giving offsets from the bottom of the frame.  */
-  HOST_WIDE_INT gp_sp_offset;
-  HOST_WIDE_INT fp_sp_offset;
-  HOST_WIDE_INT acc_sp_offset;
-  HOST_WIDE_INT cop0_sp_offset;
-
-  /* Similar, but the value passed to _mcount.  */
-  HOST_WIDE_INT ra_fp_offset;
-
-  /* The offset of arg_pointer_rtx from the bottom of the frame.  */
-  HOST_WIDE_INT arg_pointer_offset;
-
-  /* The offset of hard_frame_pointer_rtx from the bottom of the frame.  */
-  HOST_WIDE_INT hard_frame_pointer_offset;
-};
-
-/* Enumeration for masked vectored (VI) and non-masked (EIC) interrupts.  */
-enum mips_int_mask
-{
-  INT_MASK_EIC = -1,
-  INT_MASK_SW0 = 0,
-  INT_MASK_SW1 = 1,
-  INT_MASK_HW0 = 2,
-  INT_MASK_HW1 = 3,
-  INT_MASK_HW2 = 4,
-  INT_MASK_HW3 = 5,
-  INT_MASK_HW4 = 6,
-  INT_MASK_HW5 = 7
-};
-
-/* Enumeration to mark the existence of the shadow register set.
-   SHADOW_SET_INTSTACK indicates a shadow register set with a valid stack
-   pointer.  */
-enum mips_shadow_set
-{
-  SHADOW_SET_NO,
-  SHADOW_SET_YES,
-  SHADOW_SET_INTSTACK
-};
-
-struct GTY(())  machine_function {
-  /* The next floating-point condition-code register to allocate
-     for ISA_HAS_8CC targets, relative to ST_REG_FIRST.  */
-  unsigned int next_fcc;
-
-  /* The register returned by mips16_gp_pseudo_reg; see there for details.  */
-  rtx mips16_gp_pseudo_rtx;
-
-  /* The number of extra stack bytes taken up by register varargs.
-     This area is allocated by the callee at the very top of the frame.  */
-  int varargs_size;
-
-  /* The current frame information, calculated by mips_compute_frame_info.  */
-  struct mips_frame_info frame;
-
-  /* The register to use as the function's global pointer, or INVALID_REGNUM
-     if the function doesn't need one.  */
-  unsigned int global_pointer;
-
-  /* How many instructions it takes to load a label into $AT, or 0 if
-     this property hasn't yet been calculated.  */
-  unsigned int load_label_num_insns;
-
-  /* True if mips_adjust_insn_length should ignore an instruction's
-     hazard attribute.  */
-  bool ignore_hazard_length_p;
-
-  /* True if the whole function is suitable for .set noreorder and
-     .set nomacro.  */
-  bool all_noreorder_p;
-
-  /* True if the function has "inflexible" and "flexible" references
-     to the global pointer.  See mips_cfun_has_inflexible_gp_ref_p
-     and mips_cfun_has_flexible_gp_ref_p for details.  */
-  bool has_inflexible_gp_insn_p;
-  bool has_flexible_gp_insn_p;
-
-  /* True if the function's prologue must load the global pointer
-     value into pic_offset_table_rtx and store the same value in
-     the function's cprestore slot (if any).  Even if this value
-     is currently false, we may decide to set it to true later;
-     see mips_must_initialize_gp_p () for details.  */
-  bool must_initialize_gp_p;
-
-  /* True if the current function must restore $gp after any potential
-     clobber.  This value is only meaningful during the first post-epilogue
-     split_insns pass; see mips_must_initialize_gp_p () for details.  */
-  bool must_restore_gp_when_clobbered_p;
-
-  /* True if this is an interrupt handler.  */
-  bool interrupt_handler_p;
-
-  /* Records the way in which interrupts should be masked.  Only used if
-     interrupts are not kept masked.  */
-  enum mips_int_mask int_mask;
-
-  /* Records if this is an interrupt handler that uses shadow registers.  */
-  enum mips_shadow_set use_shadow_register_set;
-
-  /* True if this is an interrupt handler that should keep interrupts
-     masked.  */
-  bool keep_interrupts_masked_p;
-
-  /* True if this is an interrupt handler that should use DERET
-     instead of ERET.  */
-  bool use_debug_exception_return_p;
-};
-
 /* Information about a single argument.  */
 struct mips_arg_info {
   /* True if the argument is passed in a floating-point register, or
@@ -10462,10 +10315,15 @@  mips_compute_frame_info (void)
   cfun->machine->global_pointer = mips_global_pointer ();
 
   /* The first two blocks contain the outgoing argument area and the $gp save
-     slot.  This area isn't needed in leaf functions, but if the
-     target-independent frame size is nonzero, we have already committed to
-     allocating these in STARTING_FRAME_OFFSET for !FRAME_GROWS_DOWNWARD.  */
-  if ((size == 0 || FRAME_GROWS_DOWNWARD) && crtl->is_leaf)
+     slot.  This area isn't needed in leaf functions.  We can also skip it
+     if we know that none of the called functions will use this space.
+
+     But if the target-independent frame size is nonzero, we have already
+     committed to allocating these in STARTING_FRAME_OFFSET for
+     !FRAME_GROWS_DOWNWARD.  */
+
+  if ((size == 0 || FRAME_GROWS_DOWNWARD)
+      && (crtl->is_leaf || (cfun->machine->optimize_call_stack && !flag_pic)))
     {
       /* The MIPS 3.0 linker does not like functions that dynamically
 	 allocate the stack and have 0 for STACK_DYNAMIC_OFFSET, since it
@@ -17980,6 +17838,8 @@  mips_option_override (void)
 
   if (TARGET_HARD_FLOAT_ABI && TARGET_MIPS5900)
     REAL_MODE_FORMAT (SFmode) = &spu_single_format;
+
+  mips_register_frame_header_opt ();
 }
 
 /* Swap the register information for registers I and I + 1, which
diff --git a/gcc/config/mips/mips.h b/gcc/config/mips/mips.h
index 2d44735..3f0fee4 100644
--- a/gcc/config/mips/mips.h
+++ b/gcc/config/mips/mips.h
@@ -3124,6 +3124,161 @@  extern const struct mips_cpu_info *mips_tune_info;
 extern unsigned int mips_base_compression_flags;
 extern GTY(()) struct target_globals *mips16_globals;
 extern GTY(()) struct target_globals *micromips_globals;
+
+/* Information about a function's frame layout.  */
+struct GTY(())  mips_frame_info {
+  /* The size of the frame in bytes.  */
+  HOST_WIDE_INT total_size;
+
+  /* The number of bytes allocated to variables.  */
+  HOST_WIDE_INT var_size;
+
+  /* The number of bytes allocated to outgoing function arguments.  */
+  HOST_WIDE_INT args_size;
+
+  /* The number of bytes allocated to the .cprestore slot, or 0 if there
+     is no such slot.  */
+  HOST_WIDE_INT cprestore_size;
+
+  /* Bit X is set if the function saves or restores GPR X.  */
+  unsigned int mask;
+
+  /* Likewise FPR X.  */
+  unsigned int fmask;
+
+  /* Likewise doubleword accumulator X ($acX).  */
+  unsigned int acc_mask;
+
+  /* The number of GPRs, FPRs, doubleword accumulators and COP0
+     registers saved.  */
+  unsigned int num_gp;
+  unsigned int num_fp;
+  unsigned int num_acc;
+  unsigned int num_cop0_regs;
+
+  /* The offset of the topmost GPR, FPR, accumulator and COP0-register
+     save slots from the top of the frame, or zero if no such slots are
+     needed.  */
+  HOST_WIDE_INT gp_save_offset;
+  HOST_WIDE_INT fp_save_offset;
+  HOST_WIDE_INT acc_save_offset;
+  HOST_WIDE_INT cop0_save_offset;
+
+  /* Likewise, but giving offsets from the bottom of the frame.  */
+  HOST_WIDE_INT gp_sp_offset;
+  HOST_WIDE_INT fp_sp_offset;
+  HOST_WIDE_INT acc_sp_offset;
+  HOST_WIDE_INT cop0_sp_offset;
+
+  /* Similar, but the value passed to _mcount.  */
+  HOST_WIDE_INT ra_fp_offset;
+
+  /* The offset of arg_pointer_rtx from the bottom of the frame.  */
+  HOST_WIDE_INT arg_pointer_offset;
+
+  /* The offset of hard_frame_pointer_rtx from the bottom of the frame.  */
+  HOST_WIDE_INT hard_frame_pointer_offset;
+};
+
+/* Enumeration for masked vectored (VI) and non-masked (EIC) interrupts.  */
+enum mips_int_mask
+{
+  INT_MASK_EIC = -1,
+  INT_MASK_SW0 = 0,
+  INT_MASK_SW1 = 1,
+  INT_MASK_HW0 = 2,
+  INT_MASK_HW1 = 3,
+  INT_MASK_HW2 = 4,
+  INT_MASK_HW3 = 5,
+  INT_MASK_HW4 = 6,
+  INT_MASK_HW5 = 7
+};
+
+/* Enumeration to mark the existence of the shadow register set.
+   SHADOW_SET_INTSTACK indicates a shadow register set with a valid stack
+   pointer.  */
+enum mips_shadow_set
+{
+  SHADOW_SET_NO,
+  SHADOW_SET_YES,
+  SHADOW_SET_INTSTACK
+};
+
+struct GTY(())  machine_function {
+  /* The next floating-point condition-code register to allocate
+     for ISA_HAS_8CC targets, relative to ST_REG_FIRST.  */
+  unsigned int next_fcc;
+
+  /* The register returned by mips16_gp_pseudo_reg; see there for details.  */
+  rtx mips16_gp_pseudo_rtx;
+
+  /* The number of extra stack bytes taken up by register varargs.
+     This area is allocated by the callee at the very top of the frame.  */
+  int varargs_size;
+
+  /* The current frame information, calculated by mips_compute_frame_info.  */
+  struct mips_frame_info frame;
+
+  /* The register to use as the function's global pointer, or INVALID_REGNUM
+     if the function doesn't need one.  */
+  unsigned int global_pointer;
+
+  /* How many instructions it takes to load a label into $AT, or 0 if
+     this property hasn't yet been calculated.  */
+  unsigned int load_label_num_insns;
+
+  /* True if mips_adjust_insn_length should ignore an instruction's
+     hazard attribute.  */
+  bool ignore_hazard_length_p;
+
+  /* True if the whole function is suitable for .set noreorder and
+     .set nomacro.  */
+  bool all_noreorder_p;
+
+  /* True if the function has "inflexible" and "flexible" references
+     to the global pointer.  See mips_cfun_has_inflexible_gp_ref_p
+     and mips_cfun_has_flexible_gp_ref_p for details.  */
+  bool has_inflexible_gp_insn_p;
+  bool has_flexible_gp_insn_p;
+
+  /* True if the function's prologue must load the global pointer
+     value into pic_offset_table_rtx and store the same value in
+     the function's cprestore slot (if any).  Even if this value
+     is currently false, we may decide to set it to true later;
+     see mips_must_initialize_gp_p () for details.  */
+  bool must_initialize_gp_p;
+
+  /* True if the current function must restore $gp after any potential
+     clobber.  This value is only meaningful during the first post-epilogue
+     split_insns pass; see mips_must_initialize_gp_p () for details.  */
+  bool must_restore_gp_when_clobbered_p;
+
+  /* True if this is an interrupt handler.  */
+  bool interrupt_handler_p;
+
+  /* Records the way in which interrupts should be masked.  Only used if
+     interrupts are not kept masked.  */
+  enum mips_int_mask int_mask;
+
+  /* Records if this is an interrupt handler that uses shadow registers.  */
+  enum mips_shadow_set use_shadow_register_set;
+
+  /* True if this is an interrupt handler that should keep interrupts
+     masked.  */
+  bool keep_interrupts_masked_p;
+
+  /* True if this is an interrupt handler that should use DERET
+     instead of ERET.  */
+  bool use_debug_exception_return_p;
+
+  /* True if at least one of the formal parameters to a function must be
+     written to the frame header (probably so its address can be taken).  */
+  bool does_not_use_frame_header;
+
+  /* True if none of the functions that are called by this function need
+     stack space allocated for their arguments.  */
+  bool optimize_call_stack;
+};
 #endif
 
 /* Enable querying of DFA units.  */
diff --git a/gcc/config/mips/mips.opt b/gcc/config/mips/mips.opt
index 348c6e0..3e72936 100644
--- a/gcc/config/mips/mips.opt
+++ b/gcc/config/mips/mips.opt
@@ -412,6 +412,10 @@  modd-spreg
 Target Report Mask(ODD_SPREG)
 Enable use of odd-numbered single-precision registers
 
+mframe-header-opt
+Target Report Var(flag_frame_header_optimization) Optimization
+Optimize frame header
+
 noasmopt
 Driver
 
diff --git a/gcc/config/mips/t-mips b/gcc/config/mips/t-mips
index 01df1ad..a893841 100644
--- a/gcc/config/mips/t-mips
+++ b/gcc/config/mips/t-mips
@@ -20,3 +20,7 @@  $(srcdir)/config/mips/mips-tables.opt: $(srcdir)/config/mips/genopt.sh \
   $(srcdir)/config/mips/mips-cpus.def
 	$(SHELL) $(srcdir)/config/mips/genopt.sh $(srcdir)/config/mips > \
 		$(srcdir)/config/mips/mips-tables.opt
+
+frame-header-opt.o: $(srcdir)/config/mips/frame-header-opt.c
+	$(COMPILE) $<
+	$(POSTCOMPILE)
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index c0ec0fd..8152645 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -814,7 +814,8 @@  Objective-C and Objective-C++ Dialects}.
 -mbranch-cost=@var{num}  -mbranch-likely  -mno-branch-likely @gol
 -mfp-exceptions -mno-fp-exceptions @gol
 -mvr4130-align -mno-vr4130-align -msynci -mno-synci @gol
--mrelax-pic-calls -mno-relax-pic-calls -mmcount-ra-address}
+-mrelax-pic-calls -mno-relax-pic-calls -mmcount-ra-address @gol
+-mframe-header-opt -mno-frame-header-opt}
 
 @emph{MMIX Options}
 @gccoptlist{-mlibfuncs  -mno-libfuncs  -mepsilon  -mno-epsilon  -mabi=gnu @gol
@@ -17940,6 +17941,18 @@  if @var{ra-address} is nonnull.
 
 The default is @option{-mno-mcount-ra-address}.
 
+@item -mframe-header-opt
+@itemx -mno-frame-header-opt
+@opindex mframe-header-opt
+Enable (disable) frame header optimization in the o32 ABI.  When using
+the o32 ABI, calling functions allocate 16 bytes on the stack in case
+the called function needs to write out register arguments to memory so
+that their address can be taken.  When enabled, this optimization will
+cause the calling function to not allocate that space if it can determine
+that none of its called functions use it.
+
+This optimization is off by default at all optimization levels.
+
 @end table
 
 @node MMIX Options




ChangeLog and diff for the new tests:




2015-09-11  Steve Ellcey  <sellcey@imgtec.com>

	* gcc.target/mips/mips.exp (mips_option_groups): Add -mframe-header-opt
	and -mno-frame-header-opt options.
	* gcc.target/mips/frame-header-1.c: New file.
	* gcc.target/mips/frame-header-2.c: New file.
	* gcc.target/mips/frame-header-3.c: New file.

diff --git a/gcc/testsuite/gcc.target/mips/frame-header-1.c b/gcc/testsuite/gcc.target/mips/frame-header-1.c
index e69de29..8495e0f 100644
--- a/gcc/testsuite/gcc.target/mips/frame-header-1.c
+++ b/gcc/testsuite/gcc.target/mips/frame-header-1.c
@@ -0,0 +1,21 @@ 
+/* Verify that we do not optimize away the frame header in foo when using
+   -mno-frame-header-opt by checking the stack pointer increment done in
+   that function.  Without the optimization foo should increment the stack
+   by 32 bytes, with the optimization it would only be 8 bytes.  */
+
+/* { dg-do compile } */
+/* { dg-options "-mno-frame-header-opt" } */
+/* { dg-skip-if "code quality test" { *-*-* } { "-O0" } { "" } } */
+/* { dg-final { scan-assembler "\taddiu\t\\\$sp,\\\$sp,-32" } } */
+
+void __attribute__((noinline))
+bar (int* a)
+{
+  *a = 1;
+}
+
+void
+foo (int a)
+{
+  bar (&a);
+}
diff --git a/gcc/testsuite/gcc.target/mips/frame-header-2.c b/gcc/testsuite/gcc.target/mips/frame-header-2.c
index e69de29..37ea2d1 100644
--- a/gcc/testsuite/gcc.target/mips/frame-header-2.c
+++ b/gcc/testsuite/gcc.target/mips/frame-header-2.c
@@ -0,0 +1,21 @@ 
+/* Verify that we do optimize away the frame header in foo when using
+   -mframe-header-opt by checking the stack pointer increment done in
+   that function.  Without the optimization foo should increment the
+   stack by 32 bytes, with the optimization it would only be 8 bytes.  */
+
+/* { dg-do compile } */
+/* { dg-options "-mframe-header-opt" } */
+/* { dg-skip-if "code quality test" { *-*-* } { "-O0" } { "" } } */
+/* { dg-final { scan-assembler "\taddiu\t\\\$sp,\\\$sp,-8" } } */
+
+void __attribute__((noinline))
+bar (int* a)
+{
+  *a = 1;
+}
+
+void
+foo (int a)
+{
+  bar (&a);
+}
diff --git a/gcc/testsuite/gcc.target/mips/frame-header-3.c b/gcc/testsuite/gcc.target/mips/frame-header-3.c
index e69de29..1cb1547 100644
--- a/gcc/testsuite/gcc.target/mips/frame-header-3.c
+++ b/gcc/testsuite/gcc.target/mips/frame-header-3.c
@@ -0,0 +1,22 @@ 
+/* Verify that we do not optimize away the frame header in foo when using
+   -mframe-header-opt but are calling a weak function that may be overridden
+   by a different function that does need the frame header.  Without the
+   optimization foo should increment the stack by 32 bytes, with the
+   optimization it would only be 8 bytes.  */
+
+/* { dg-do compile } */
+/* { dg-options "-mframe-header-opt" } */
+/* { dg-skip-if "code quality test" { *-*-* } { "-O0" } { "" } } */
+/* { dg-final { scan-assembler "\taddiu\t\\\$sp,\\\$sp,-32" } } */
+
+void __attribute__((noinline, weak))
+bar (int* a)
+{
+  *a = 1;
+}
+
+void
+foo (int a)
+{
+  bar (&a);
+}
diff --git a/gcc/testsuite/gcc.target/mips/mips.exp b/gcc/testsuite/gcc.target/mips/mips.exp
index 55e4223..7186d09 100644
--- a/gcc/testsuite/gcc.target/mips/mips.exp
+++ b/gcc/testsuite/gcc.target/mips/mips.exp
@@ -255,6 +255,7 @@  set mips_option_groups {
     maddps "HAS_MADDPS"
     lsa "(|!)HAS_LSA"
     section_start "-Wl,--section-start=.*"
+    frame-header "-mframe-header-opt|-mno-frame-header-opt"
 }
 
 for { set option 0 } { $option < 32 } { incr option } {