xref: /netbsd-src/external/gpl3/gcc.old/dist/gcc/config/mips/frame-header-opt.c (revision 8feb0f0b7eaff0608f8350bbfa3098827b4bb91b)
1 /* Analyze functions to determine if callers need to allocate a frame header
2    on the stack.  The frame header is used by callees to save their arguments.
3    This optimization is specific to TARGET_OLDABI targets.  For TARGET_NEWABI
4    targets, if a frame header is required, it is allocated by the callee.
5 
6 
7    Copyright (C) 2015-2020 Free Software Foundation, Inc.
8 
9 This file is part of GCC.
10 
11 GCC is free software; you can redistribute it and/or modify it
12 under the terms of the GNU General Public License as published by the
13 Free Software Foundation; either version 3, or (at your option) any
14 later version.
15 
16 GCC is distributed in the hope that it will be useful, but WITHOUT
17 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
19 for more details.
20 
21 You should have received a copy of the GNU General Public License
22 along with GCC; see the file COPYING3.  If not see
23 <http://www.gnu.org/licenses/>.  */
24 
25 
26 #define IN_TARGET_CODE 1
27 
28 #include "config.h"
29 #include "system.h"
30 #include "context.h"
31 #include "coretypes.h"
32 #include "backend.h"
33 #include "tree.h"
34 #include "tree-core.h"
35 #include "tree-pass.h"
36 #include "target.h"
37 #include "target-globals.h"
38 #include "profile-count.h"
39 #include "cgraph.h"
40 #include "function.h"
41 #include "basic-block.h"
42 #include "gimple.h"
43 #include "gimple-iterator.h"
44 #include "gimple-walk.h"
45 
46 static unsigned int frame_header_opt (void);
47 
48 namespace {
49 
50 const pass_data pass_data_ipa_frame_header_opt =
51 {
52   IPA_PASS, /* type */
53   "frame-header-opt", /* name */
54   OPTGROUP_NONE, /* optinfo_flags */
55   TV_CGRAPHOPT, /* tv_id */
56   0, /* properties_required */
57   0, /* properties_provided */
58   0, /* properties_destroyed */
59   0, /* todo_flags_start */
60   0, /* todo_flags_finish */
61 };
62 
63 class pass_ipa_frame_header_opt : public ipa_opt_pass_d
64 {
65 public:
pass_ipa_frame_header_opt(gcc::context * ctxt)66   pass_ipa_frame_header_opt (gcc::context *ctxt)
67     : ipa_opt_pass_d (pass_data_ipa_frame_header_opt, ctxt,
68                       NULL, /* generate_summary */
69                       NULL, /* write_summary */
70                       NULL, /* read_summary */
71                       NULL, /* write_optimization_summary */
72                       NULL, /* read_optimization_summary */
73                       NULL, /* stmt_fixup */
74                       0, /* function_transform_todo_flags_start */
75                       NULL, /* function_transform */
76                       NULL) /* variable_transform */
77   {}
78 
79   /* opt_pass methods: */
gate(function *)80   virtual bool gate (function *)
81     {
82       /* This optimization has no affect if TARGET_NEWABI.   If optimize
83          is not at least 1 then the data needed for the optimization is
84          not available and nothing will be done anyway.  */
85       return TARGET_OLDABI && flag_frame_header_optimization && optimize > 0;
86     }
87 
execute(function *)88   virtual unsigned int execute (function *) { return frame_header_opt (); }
89 
90 }; // class pass_ipa_frame_header_opt
91 
92 } // anon namespace
93 
94 static ipa_opt_pass_d *
make_pass_ipa_frame_header_opt(gcc::context * ctxt)95 make_pass_ipa_frame_header_opt (gcc::context *ctxt)
96 {
97   return new pass_ipa_frame_header_opt (ctxt);
98 }
99 
100 void
mips_register_frame_header_opt(void)101 mips_register_frame_header_opt (void)
102 {
103   opt_pass *p = make_pass_ipa_frame_header_opt (g);
104   struct register_pass_info f = { p, "comdats", 1, PASS_POS_INSERT_AFTER };
105   register_pass (&f);
106 }
107 
108 
109 /* Return true if it is certain that this is a leaf function.  False if it is
110    not a leaf function or if it is impossible to tell.  */
111 
112 static bool
is_leaf_function(function * fn)113 is_leaf_function (function *fn)
114 {
115   basic_block bb;
116   gimple_stmt_iterator gsi;
117 
118   /* If we do not have a cfg for this function be conservative and assume
119      it is not a leaf function.  */
120   if (fn->cfg == NULL)
121     return false;
122 
123   FOR_EACH_BB_FN (bb, fn)
124     for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
125       if (is_gimple_call (gsi_stmt (gsi)))
126 	return false;
127   return true;
128 }
129 
130 /* Return true if this function has inline assembly code or if we cannot
131    be certain that it does not.  False if we know that there is no inline
132    assembly.  */
133 
134 static bool
has_inlined_assembly(function * fn)135 has_inlined_assembly (function *fn)
136 {
137   basic_block bb;
138   gimple_stmt_iterator gsi;
139 
140   /* If we do not have a cfg for this function be conservative and assume
141      it is may have inline assembly.  */
142   if (fn->cfg == NULL)
143     return true;
144 
145   FOR_EACH_BB_FN (bb, fn)
146     for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
147       if (gimple_code (gsi_stmt (gsi)) == GIMPLE_ASM)
148 	return true;
149 
150   return false;
151 }
152 
153 /* Return true if this function will use the stack space allocated by its
154    caller or if we cannot determine for certain that it does not.  */
155 
156 static bool
needs_frame_header_p(function * fn)157 needs_frame_header_p (function *fn)
158 {
159   tree t;
160 
161   if (fn->decl == NULL)
162     return true;
163 
164   if (fn->stdarg)
165     return true;
166 
167   for (t = DECL_ARGUMENTS (fn->decl); t; t = TREE_CHAIN (t))
168     {
169       if (!use_register_for_decl (t))
170 	return true;
171 
172       /* Some 64-bit types may get copied to general registers using the frame
173 	 header, see mips_output_64bit_xfer.  Checking for SImode only may be
174          overly restrictive but it is guaranteed to be safe. */
175       if (DECL_MODE (t) != SImode)
176 	return true;
177     }
178 
179   return false;
180 }
181 
182 /* Return true if the argument stack space allocated by function FN is used.
183    Return false if the space is needed or if the need for the space cannot
184    be determined.  */
185 
186 static bool
callees_functions_use_frame_header(function * fn)187 callees_functions_use_frame_header (function *fn)
188 {
189   basic_block bb;
190   gimple_stmt_iterator gsi;
191   gimple *stmt;
192   tree called_fn_tree;
193   function *called_fn;
194 
195   if (fn->cfg == NULL)
196     return true;
197 
198   FOR_EACH_BB_FN (bb, fn)
199     {
200       for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
201 	{
202 	  stmt = gsi_stmt (gsi);
203 	  if (is_gimple_call (stmt))
204 	    {
205 	      called_fn_tree = gimple_call_fndecl (stmt);
206 	      if (called_fn_tree != NULL)
207 	        {
208 	          called_fn = DECL_STRUCT_FUNCTION (called_fn_tree);
209 		  if (called_fn == NULL
210 		      || DECL_WEAK (called_fn_tree)
211 		      || has_inlined_assembly (called_fn)
212 		      || !is_leaf_function (called_fn)
213 		      || !called_fn->machine->does_not_use_frame_header)
214 		    return true;
215 	        }
216 	      else
217 		return true;
218             }
219         }
220     }
221   return false;
222 }
223 
224 /* Set the callers_may_not_allocate_frame flag for any function which
225    function FN calls because FN may not allocate a frame header.  */
226 
227 static void
set_callers_may_not_allocate_frame(function * fn)228 set_callers_may_not_allocate_frame (function *fn)
229 {
230   basic_block bb;
231   gimple_stmt_iterator gsi;
232   gimple *stmt;
233   tree called_fn_tree;
234   function *called_fn;
235 
236   if (fn->cfg == NULL)
237     return;
238 
239   FOR_EACH_BB_FN (bb, fn)
240     {
241       for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
242 	{
243 	  stmt = gsi_stmt (gsi);
244 	  if (is_gimple_call (stmt))
245 	    {
246 	      called_fn_tree = gimple_call_fndecl (stmt);
247 	      if (called_fn_tree != NULL)
248 	        {
249 	          called_fn = DECL_STRUCT_FUNCTION (called_fn_tree);
250 		  if (called_fn != NULL)
251 		    called_fn->machine->callers_may_not_allocate_frame = true;
252 	        }
253             }
254         }
255     }
256   return;
257 }
258 
259 /* Scan each function to determine those that need its frame headers.  Perform
260    a second scan to determine if the allocation can be skipped because none of
261    their callees require the frame header.  */
262 
263 static unsigned int
frame_header_opt()264 frame_header_opt ()
265 {
266   struct cgraph_node *node;
267   function *fn;
268 
269   FOR_EACH_DEFINED_FUNCTION (node)
270     {
271       fn = node->get_fun ();
272       if (fn != NULL)
273 	fn->machine->does_not_use_frame_header = !needs_frame_header_p (fn);
274     }
275 
276   FOR_EACH_DEFINED_FUNCTION (node)
277     {
278       fn = node->get_fun ();
279       if (fn != NULL)
280 	fn->machine->optimize_call_stack
281 	  = !callees_functions_use_frame_header (fn) && !is_leaf_function (fn);
282     }
283 
284   FOR_EACH_DEFINED_FUNCTION (node)
285     {
286       fn = node->get_fun ();
287       if (fn != NULL && fn->machine->optimize_call_stack)
288 	set_callers_may_not_allocate_frame (fn);
289     }
290 
291   return 0;
292 }
293