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