OSDN Git Service

* tree.h (enum tree_index): Add TI_VA_LIST_GPR_COUNTER_FIELD
[pf3gnuchains/gcc-fork.git] / gcc / tree-stdarg.c
1 /* Pass computing data for optimizing stdarg functions.
2    Copyright (C) 2004 Free Software Foundation, Inc.
3    Contributed by Jakub Jelinek <jakub@redhat.com>
4
5 This file is part of GCC.
6
7 GCC is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
10 any later version.
11
12 GCC is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GCC; see the file COPYING.  If not, write to
19 the Free Software Foundation, 59 Temple Place - Suite 330,
20 Boston, MA 02111-1307, USA.  */
21
22 #include "config.h"
23 #include "system.h"
24 #include "coretypes.h"
25 #include "tm.h"
26 #include "tree.h"
27 #include "function.h"
28 #include "langhooks.h"
29 #include "diagnostic.h"
30 #include "tree-flow.h"
31 #include "tree-pass.h"
32
33 /* A simple pass that attempts to optimize stdarg functions on architectures
34    that need to save register arguments to stack on entry to stdarg functions.
35    If the function doesn't use any va_start macros, no registers need to
36    be saved.  If va_start macros are used, the va_list variables don't escape
37    the function, it is only necessary to save registers that will be used
38    in va_arg macros.  E.g. if va_arg is only used with integral types
39    in the function, floating point registers don't need to be saved, etc.  */
40
41 struct stdarg_info
42 {
43   bitmap va_list_vars;
44   basic_block va_start_bb, bb;
45   int compute_sizes, va_start_count;
46 };
47
48 /* Return true if basic block VA_ARG_BB is dominated by VA_START_BB and
49    is executed at most as many times as VA_START_BB.  */
50
51 static bool
52 reachable_at_most_once (basic_block va_arg_bb, basic_block va_start_bb)
53 {
54   edge *stack, e;
55   edge_iterator ei;
56   int sp;
57   sbitmap visited;
58   bool ret;
59
60   if (va_arg_bb == va_start_bb)
61     return true;
62
63   if (! dominated_by_p (CDI_DOMINATORS, va_arg_bb, va_start_bb))
64     return false;
65
66   stack = xmalloc ((n_basic_blocks + 1) * sizeof (edge));
67   sp = 0;
68
69   visited = sbitmap_alloc (last_basic_block);
70   sbitmap_zero (visited);
71   ret = true;
72
73   FOR_EACH_EDGE (e, ei, va_arg_bb->preds)
74     stack[sp++] = e;
75
76   while (sp)
77     {
78       basic_block src;
79
80       --sp;
81       e = stack[sp];
82       src = e->src;
83
84       if (e->flags & EDGE_COMPLEX)
85         {
86           ret = false;
87           break;
88         }
89
90       if (src == va_start_bb)
91         continue;
92
93       /* va_arg_bb can be executed more times than va_start_bb.  */
94       if (src == va_arg_bb)
95         {
96           ret = false;
97           break;
98         }
99
100       gcc_assert (src != ENTRY_BLOCK_PTR);
101
102       if (! TEST_BIT (visited, src->index))
103         {
104           SET_BIT (visited, src->index);
105           FOR_EACH_EDGE (e, ei, src->preds)
106             stack[sp++] = e;
107         }
108     }
109
110   free (stack);
111   sbitmap_free (visited);
112   return ret;
113 }
114
115
116 /* For statement COUNTER = RHS, if RHS is COUNTER + constant,
117    return constant, otherwise return 0.  */
118
119 static unsigned HOST_WIDE_INT
120 va_list_counter_bump (tree counter, tree rhs)
121 {
122   tree plus_stmt = SSA_NAME_DEF_STMT (rhs);
123   tree rhs1, addend, load_stmt, counter1;
124
125   if (TREE_CODE (plus_stmt) != MODIFY_EXPR
126       || TREE_OPERAND (plus_stmt, 0) != rhs)
127     return 0;
128
129   rhs1 = TREE_OPERAND (plus_stmt, 1);
130
131   if (TREE_CODE (rhs1) != PLUS_EXPR
132       || TREE_CODE (TREE_OPERAND (rhs1, 0)) != SSA_NAME
133       || TREE_CODE (TREE_OPERAND (rhs1, 1)) != INTEGER_CST
134       || !host_integerp (TREE_OPERAND (rhs1, 1), 1))
135     return 0;
136
137   addend = TREE_OPERAND (rhs1, 0);
138   load_stmt = SSA_NAME_DEF_STMT (addend);
139
140   if (TREE_CODE (load_stmt) != MODIFY_EXPR
141       || TREE_OPERAND (load_stmt, 0) != addend)
142     return 0;
143
144   counter1 = TREE_OPERAND (load_stmt, 1);
145   if (TREE_CODE (counter) != TREE_CODE (counter1))
146     return 0;
147
148   if (TREE_CODE (counter) == COMPONENT_REF)
149     {
150       if (get_base_address (counter) != get_base_address (counter1)
151           || TREE_CODE (TREE_OPERAND (counter1, 1)) != FIELD_DECL
152           || TREE_OPERAND (counter, 1) != TREE_OPERAND (counter1, 1))
153         return 0;
154     }
155   else
156     return 0;
157
158   return tree_low_cst (TREE_OPERAND (rhs1, 1), 1);
159 }
160
161
162 /* Called by walk_tree to look for references to va_list variables.  */
163
164 static tree
165 find_va_list_reference (tree *tp, int *walk_subtrees ATTRIBUTE_UNUSED,
166                         void *data)
167 {
168   bitmap va_list_vars = (bitmap) data;
169   tree var = *tp;
170
171   if (TREE_CODE (var) == SSA_NAME)
172     var = SSA_NAME_VAR (var);
173
174   if (TREE_CODE (var) == VAR_DECL
175       && bitmap_bit_p (va_list_vars, var_ann (var)->uid))
176     return var;
177
178   return NULL_TREE;
179 }
180
181
182 /* Helper function of va_list_counter_struct_op.  Compute
183   cfun->va_list_{g,f}pr_size.  AP is a va_list GPR/FPR counter,
184   if WRITE_P is true, seen in AP = VAR, otherwise seen in VAR = AP
185   statement.  GPR_P is true if AP is a GPR counter, false if it is
186   a FPR counter.  */
187
188 static void
189 va_list_counter_op (struct stdarg_info *si, tree ap, tree var, bool gpr_p,
190                     bool write_p)
191 {
192   unsigned HOST_WIDE_INT increment;
193
194   if (si->compute_sizes < 0)
195     {
196       si->compute_sizes = 0;
197       if (si->va_start_count == 1
198           && reachable_at_most_once (si->bb, si->va_start_bb))
199         si->compute_sizes = 1;
200
201       if (dump_file && (dump_flags & TDF_DETAILS))
202         fprintf (dump_file,
203                  "bb%d will %sbe executed at most once for each va_start "
204                  "in bb%d\n", si->bb->index, si->compute_sizes ? "" : "not ",
205                  si->va_start_bb->index);
206     }
207
208   if (write_p
209       && si->compute_sizes
210       && (increment = va_list_counter_bump (ap, var)) != 0)
211     {
212       if (gpr_p && cfun->va_list_gpr_size + increment < VA_LIST_MAX_GPR_SIZE)
213         {
214           cfun->va_list_gpr_size += increment;
215           return;
216         }
217
218       if (!gpr_p && cfun->va_list_fpr_size + increment < VA_LIST_MAX_FPR_SIZE)
219         {
220           cfun->va_list_fpr_size += increment;
221           return;
222         }
223     }
224
225   if (write_p || !si->compute_sizes)
226     {
227       if (gpr_p)
228         cfun->va_list_gpr_size = VA_LIST_MAX_GPR_SIZE;
229       else
230         cfun->va_list_fpr_size = VA_LIST_MAX_FPR_SIZE;
231     }
232 }
233
234
235 /* If AP is a va_list GPR/FPR counter, compute cfun->va_list_{g,f}pr_size.
236    If WRITE_P is true, AP has been seen in AP = VAR assignment, if WRITE_P
237    is false, AP has been seen in VAR = AP assignment.
238    Return true if the AP = VAR (resp. VAR = AP) statement is a recognized
239    va_arg operation that doesn't cause the va_list variable to escape
240    current function.  */
241
242 static bool
243 va_list_counter_struct_op (struct stdarg_info *si, tree ap, tree var,
244                            bool write_p)
245 {
246   tree base;
247
248   if (TREE_CODE (ap) != COMPONENT_REF
249       || TREE_CODE (TREE_OPERAND (ap, 1)) != FIELD_DECL)
250     return false;
251
252   if (TREE_CODE (var) != SSA_NAME
253       || bitmap_bit_p (si->va_list_vars, var_ann (SSA_NAME_VAR (var))->uid))
254     return false;
255
256   base = get_base_address (ap);
257   if (TREE_CODE (base) != VAR_DECL
258       || !bitmap_bit_p (si->va_list_vars, var_ann (base)->uid))
259     return false;
260
261   if (TREE_OPERAND (ap, 1) == va_list_gpr_counter_field)
262     va_list_counter_op (si, ap, var, true, write_p);
263   else if (TREE_OPERAND (ap, 1) == va_list_fpr_counter_field)
264     va_list_counter_op (si, ap, var, false, write_p);
265
266   return true;
267 }
268
269
270 /* Return true if this optimization pass should be done.
271    It makes only sense for stdarg functions.  */
272
273 static bool
274 gate_optimize_stdarg (void)
275 {
276   /* This optimization is only for stdarg functions.  */
277   return current_function_stdarg != 0;
278 }  
279
280
281 /* Entry point to the stdarg optimization pass.  */
282
283 static void
284 execute_optimize_stdarg (void)
285 {
286   basic_block bb;
287   bool va_list_escapes = false;
288   struct stdarg_info si;
289   const char *funcname = NULL;
290
291   cfun->va_list_gpr_size = 0;
292   cfun->va_list_fpr_size = 0;
293   memset (&si, 0, sizeof (si));
294   si.va_list_vars = BITMAP_XMALLOC ();
295
296   if (dump_file)
297     funcname = lang_hooks.decl_printable_name (current_function_decl, 2);
298
299   FOR_EACH_BB (bb)
300     {
301       block_stmt_iterator i;
302
303       for (i = bsi_start (bb); !bsi_end_p (i); bsi_next (&i))
304         {
305           tree stmt = bsi_stmt (i);
306           tree call = get_call_expr_in (stmt), callee;
307           tree ap;
308
309           if (!call)
310             continue;
311
312           callee = get_callee_fndecl (call);
313           if (!callee
314               || DECL_BUILT_IN_CLASS (callee) != BUILT_IN_NORMAL
315               || DECL_FUNCTION_CODE (callee) != BUILT_IN_VA_START)
316             continue;
317
318           si.va_start_count++;
319           ap = TREE_VALUE (TREE_OPERAND (call, 1));
320           if (TREE_CODE (ap) != ADDR_EXPR
321               || TYPE_MAIN_VARIANT (TREE_TYPE (TREE_OPERAND (ap, 0)))
322                  != TYPE_MAIN_VARIANT (va_list_type_node)
323               || TREE_CODE (TREE_OPERAND (ap, 0)) != VAR_DECL)
324             {
325               va_list_escapes = true;
326               break;
327             }
328
329           ap = TREE_OPERAND (ap, 0);        
330           if (is_global_var (ap))
331             {
332               va_list_escapes = true;
333               break;
334             }
335
336           bitmap_set_bit (si.va_list_vars, var_ann (ap)->uid);
337
338           /* VA_START_BB will be only used if there is just one
339              va_start in the function.  */
340           si.va_start_bb = bb;
341         }
342
343       if (va_list_escapes)
344         break;
345     }
346
347   /* If there were no va_start uses in the function, there is no need to
348      save anything.  */
349   if (si.va_start_count == 0)
350     goto finish;
351
352   /* If some va_list arguments weren't local, we can't optimize.  */
353   if (va_list_escapes)
354     goto finish;
355
356   /* If the backend didn't tell us what the counter fields are, there is
357      nothing more we can do.  */
358   if (va_list_gpr_counter_field == NULL_TREE
359       && va_list_fpr_counter_field == NULL_TREE)
360     {
361       va_list_escapes = true;
362       goto finish;
363     }
364
365   FOR_EACH_BB (bb)
366     {
367       block_stmt_iterator i;
368
369       si.compute_sizes = -1;
370       si.bb = bb;
371       for (i = bsi_start (bb);
372            !bsi_end_p (i) && !va_list_escapes;
373            bsi_next (&i))
374         {
375           tree stmt = bsi_stmt (i);
376           tree call;
377
378           /* Don't look at __builtin_va_{start,end}, they are ok.  */
379           call = get_call_expr_in (stmt);
380           if (call)
381             {
382               tree callee = get_callee_fndecl (call);
383
384               if (callee
385                   && DECL_BUILT_IN_CLASS (callee) == BUILT_IN_NORMAL
386                   && (DECL_FUNCTION_CODE (callee) == BUILT_IN_VA_START
387                       || DECL_FUNCTION_CODE (callee) == BUILT_IN_VA_END))
388                 continue;
389             }
390
391           if (TREE_CODE (stmt) == MODIFY_EXPR)
392             {
393               tree lhs = TREE_OPERAND (stmt, 0);
394               tree rhs = TREE_OPERAND (stmt, 1);
395
396               if (TREE_CODE (rhs) == WITH_SIZE_EXPR)
397                 rhs = TREE_OPERAND (rhs, 0);
398
399               /* Check for ap[0].field = temp.  */
400               if (va_list_counter_struct_op (&si, lhs, rhs, true))
401                 continue;
402
403               /* Check for temp = ap[0].field.  */
404               else if (va_list_counter_struct_op (&si, rhs, lhs, false))
405                 continue;
406             }
407
408           /* All other uses of va_list are either va_copy (that is not handled
409              in this optimization), taking address of va_list variable or
410              passing va_list to other functions (in that case va_list might
411              escape the function and therefore va_start needs to set it up
412              fully), or some unexpected use of va_list.  None of these should
413              happen in a gimplified VA_ARG_EXPR.  */
414           if (walk_tree (&stmt, find_va_list_reference, si.va_list_vars, NULL))
415             {
416               if (dump_file && (dump_flags & TDF_DETAILS))
417                 {
418                   fputs ("va_list escapes in ", dump_file);
419                   print_generic_expr (dump_file, stmt, dump_flags);
420                   fputc ('\n', dump_file);
421                 }
422               va_list_escapes = true;
423             }
424         }
425
426       if (va_list_escapes)
427         break;
428     }
429
430 finish:
431   if (va_list_escapes)
432     {
433       cfun->va_list_gpr_size = VA_LIST_MAX_GPR_SIZE;
434       cfun->va_list_fpr_size = VA_LIST_MAX_FPR_SIZE;
435     }
436   BITMAP_XFREE (si.va_list_vars);
437   if (dump_file)
438     {
439       fprintf (dump_file, "%s: va_list escapes %d, needs to save ",
440                funcname, (int) va_list_escapes);
441       if (cfun->va_list_gpr_size >= VA_LIST_MAX_GPR_SIZE)
442         fputs ("all", dump_file);
443       else
444         fprintf (dump_file, "%d", cfun->va_list_gpr_size);
445       fputs (" GPR units and ", dump_file);
446       if (cfun->va_list_fpr_size >= VA_LIST_MAX_FPR_SIZE)
447         fputs ("all", dump_file);
448       else
449         fprintf (dump_file, "%d", cfun->va_list_fpr_size);
450       fputs (" FPR units.\n", dump_file);
451     }
452 }
453
454
455 struct tree_opt_pass pass_stdarg =
456 {
457   "stdarg",                             /* name */
458   gate_optimize_stdarg,                 /* gate */
459   execute_optimize_stdarg,              /* execute */
460   NULL,                                 /* sub */
461   NULL,                                 /* next */
462   0,                                    /* static_pass_number */
463   0,                                    /* tv_id */
464   PROP_cfg | PROP_ssa | PROP_alias,     /* properties_required */
465   0,                                    /* properties_provided */
466   0,                                    /* properties_destroyed */
467   0,                                    /* todo_flags_start */
468   TODO_dump_func,                       /* todo_flags_finish */
469   0                                     /* letter */
470 };