From patchwork Mon Oct 5 17:15:44 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Steve Ellcey X-Patchwork-Id: 526427 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 6059114016A for ; Tue, 6 Oct 2015 04:16:06 +1100 (AEDT) Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=gcc.gnu.org header.i=@gcc.gnu.org header.b=W5dJIwuR; dkim-atps=neutral DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender :message-id:subject:from:reply-to:to:cc:date:in-reply-to :references:content-type:content-transfer-encoding:mime-version; q=dns; s=default; b=Bs8Y+zECFNxsumYGURPwwXWes82/1epDCBEafThn8MQ EBQBB9n9rUVBDFztt0iuZUUyvbVkbgsXhT9ICv+uR7ucL9G4+hILEEDr4qm+Sqcq yGr0yOJ0+MJeQIcybiD3TOVPNe0FyYyAsISYT3wUO0OoW24y/aExaMFAOf8pwW6A = DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender :message-id:subject:from:reply-to:to:cc:date:in-reply-to :references:content-type:content-transfer-encoding:mime-version; s=default; bh=RnwS0Xa1cFuJ9xAjfksssA5CmdY=; b=W5dJIwuRDeo38FQOX GgqyQba424w3STndZKCz9uWvN//5i0NosuMygi+B0YaN9YSwROCi05AWM1bbxUux QWMQrD+yOFMo5c0ZSXXlGjMGto1wiBBYmjKScY8fqoWNXlT2+UlfyYrcbIvcuOmA PLM2uilb0LYm7/mHymQqvty7ws= Received: (qmail 79219 invoked by alias); 5 Oct 2015 17:15:56 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Received: (qmail 79058 invoked by uid 89); 5 Oct 2015 17:15:54 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-1.0 required=5.0 tests=AWL, BAYES_50, RCVD_IN_DNSWL_LOW, SPF_PASS, T_RP_MATCHES_RCVD autolearn=ham version=3.3.2 X-HELO: mailapp01.imgtec.com Received: from mailapp01.imgtec.com (HELO mailapp01.imgtec.com) (195.59.15.196) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Mon, 05 Oct 2015 17:15:50 +0000 Received: from KLMAIL01.kl.imgtec.org (unknown [192.168.5.35]) by Websense Email Security Gateway with ESMTPS id D9049ECF7B921; Mon, 5 Oct 2015 18:15:43 +0100 (IST) Received: from hhmail02.hh.imgtec.org (10.100.10.20) by KLMAIL01.kl.imgtec.org (192.168.5.35) with Microsoft SMTP Server (TLS) id 14.3.195.1; Mon, 5 Oct 2015 18:15:46 +0100 Received: from BAMAIL02.ba.imgtec.org (10.20.40.28) by hhmail02.hh.imgtec.org (10.100.10.20) with Microsoft SMTP Server (TLS) id 14.3.235.1; Mon, 5 Oct 2015 18:15:46 +0100 Received: from [10.20.3.58] (10.20.3.58) by bamail02.ba.imgtec.org (10.20.40.28) with Microsoft SMTP Server (TLS) id 14.3.174.1; Mon, 5 Oct 2015 10:15:44 -0700 Message-ID: <1444065344.8687.224.camel@ubuntu-sellcey> Subject: RE: [PATCH, MIPS] Frame header optimization for MIPS O32 ABI From: Steve Ellcey Reply-To: To: "Moore, Catherine" CC: Matthew Fortune , GCC Patches Date: Mon, 5 Oct 2015 10:15:44 -0700 In-Reply-To: References: <1441315205.26051.58.camel@ubuntu-sellcey> <6D39441BF12EF246A7ABCE6654B02353212620CB@LEMAIL01.le.imgtec.org> <1441994741.16610.6.camel@ubuntu-sellcey> MIME-Version: 1.0 On Mon, 2015-09-28 at 22:10 +0000, Moore, Catherine wrote: > 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. OK, I have updated the comments based on your input and changed the code to compile with the ToT GCC after revision @227941. Here is the new patch. 2015-10-05 Steve Ellcey * 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. diff --git a/gcc/config.gcc b/gcc/config.gcc index 56797bd..7bf66f8 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 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 +. */ + + +#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; +} diff --git a/gcc/config/mips/mips-protos.h b/gcc/config/mips/mips-protos.h index 8a9ae01..43774fa 100644 --- a/gcc/config/mips/mips-protos.h +++ b/gcc/config/mips/mips-protos.h @@ -371,4 +371,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 456db08..a4bb454 100644 --- a/gcc/config/mips/mips.c +++ b/gcc/config/mips/mips.c @@ -328,153 +328,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 @@ -10504,10 +10357,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 @@ -18269,6 +18127,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 25a1e06..be86c10 100644 --- a/gcc/config/mips/mips.h +++ b/gcc/config/mips/mips.h @@ -3119,6 +3119,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 84887d1..b979eb5 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 3a9594c..464f808 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -818,7 +818,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 @@ -18104,6 +18105,17 @@ 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 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. + @end table @node MMIX Options 2015-10-05 Steve Ellcey * 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 new file mode 100644 index 0000000..8495e0f --- /dev/null +++ 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 new file mode 100644 index 0000000..37ea2d1 --- /dev/null +++ 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 new file mode 100644 index 0000000..1cb1547 --- /dev/null +++ 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 42e7fff..0f2d6a2 100644 --- a/gcc/testsuite/gcc.target/mips/mips.exp +++ b/gcc/testsuite/gcc.target/mips/mips.exp @@ -256,6 +256,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 } {