xref: /dragonfly/contrib/gcc-4.7/gcc/tree-stdarg.c (revision 04febcfb30580676d3e95f58a16c5137ee478b32)
1 /* Pass computing data for optimizing stdarg functions.
2    Copyright (C) 2004, 2005, 2007, 2008, 2009, 2010, 2011
3    Free Software Foundation, Inc.
4    Contributed by Jakub Jelinek <jakub@redhat.com>
5 
6 This file is part of GCC.
7 
8 GCC is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3, or (at your option)
11 any later version.
12 
13 GCC is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 GNU General Public License for more details.
17 
18 You should have received a copy of the GNU General Public License
19 along with GCC; see the file COPYING3.  If not see
20 <http://www.gnu.org/licenses/>.  */
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 "gimple-pretty-print.h"
30 #include "target.h"
31 #include "tree-flow.h"
32 #include "tree-pass.h"
33 #include "tree-stdarg.h"
34 
35 /* A simple pass that attempts to optimize stdarg functions on architectures
36    that need to save register arguments to stack on entry to stdarg functions.
37    If the function doesn't use any va_start macros, no registers need to
38    be saved.  If va_start macros are used, the va_list variables don't escape
39    the function, it is only necessary to save registers that will be used
40    in va_arg macros.  E.g. if va_arg is only used with integral types
41    in the function, floating point registers don't need to be saved, etc.  */
42 
43 
44 /* Return true if basic block VA_ARG_BB is dominated by VA_START_BB and
45    is executed at most as many times as VA_START_BB.  */
46 
47 static bool
reachable_at_most_once(basic_block va_arg_bb,basic_block va_start_bb)48 reachable_at_most_once (basic_block va_arg_bb, basic_block va_start_bb)
49 {
50   VEC (edge, heap) *stack = NULL;
51   edge e;
52   edge_iterator ei;
53   sbitmap visited;
54   bool ret;
55 
56   if (va_arg_bb == va_start_bb)
57     return true;
58 
59   if (! dominated_by_p (CDI_DOMINATORS, va_arg_bb, va_start_bb))
60     return false;
61 
62   visited = sbitmap_alloc (last_basic_block);
63   sbitmap_zero (visited);
64   ret = true;
65 
66   FOR_EACH_EDGE (e, ei, va_arg_bb->preds)
67     VEC_safe_push (edge, heap, stack, e);
68 
69   while (! VEC_empty (edge, stack))
70     {
71       basic_block src;
72 
73       e = VEC_pop (edge, stack);
74       src = e->src;
75 
76       if (e->flags & EDGE_COMPLEX)
77           {
78             ret = false;
79             break;
80           }
81 
82       if (src == va_start_bb)
83           continue;
84 
85       /* va_arg_bb can be executed more times than va_start_bb.  */
86       if (src == va_arg_bb)
87           {
88             ret = false;
89             break;
90           }
91 
92       gcc_assert (src != ENTRY_BLOCK_PTR);
93 
94       if (! TEST_BIT (visited, src->index))
95           {
96             SET_BIT (visited, src->index);
97             FOR_EACH_EDGE (e, ei, src->preds)
98               VEC_safe_push (edge, heap, stack, e);
99           }
100     }
101 
102   VEC_free (edge, heap, stack);
103   sbitmap_free (visited);
104   return ret;
105 }
106 
107 
108 /* For statement COUNTER = RHS, if RHS is COUNTER + constant,
109    return constant, otherwise return (unsigned HOST_WIDE_INT) -1.
110    GPR_P is true if this is GPR counter.  */
111 
112 static unsigned HOST_WIDE_INT
va_list_counter_bump(struct stdarg_info * si,tree counter,tree rhs,bool gpr_p)113 va_list_counter_bump (struct stdarg_info *si, tree counter, tree rhs,
114                           bool gpr_p)
115 {
116   tree lhs, orig_lhs;
117   gimple stmt;
118   unsigned HOST_WIDE_INT ret = 0, val, counter_val;
119   unsigned int max_size;
120 
121   if (si->offsets == NULL)
122     {
123       unsigned int i;
124 
125       si->offsets = XNEWVEC (int, num_ssa_names);
126       for (i = 0; i < num_ssa_names; ++i)
127           si->offsets[i] = -1;
128     }
129 
130   counter_val = gpr_p ? cfun->va_list_gpr_size : cfun->va_list_fpr_size;
131   max_size = gpr_p ? VA_LIST_MAX_GPR_SIZE : VA_LIST_MAX_FPR_SIZE;
132   orig_lhs = lhs = rhs;
133   while (lhs)
134     {
135       enum tree_code rhs_code;
136       tree rhs1;
137 
138       if (si->offsets[SSA_NAME_VERSION (lhs)] != -1)
139           {
140             if (counter_val >= max_size)
141               {
142                 ret = max_size;
143                 break;
144               }
145 
146             ret -= counter_val - si->offsets[SSA_NAME_VERSION (lhs)];
147             break;
148           }
149 
150       stmt = SSA_NAME_DEF_STMT (lhs);
151 
152       if (!is_gimple_assign (stmt) || gimple_assign_lhs (stmt) != lhs)
153           return (unsigned HOST_WIDE_INT) -1;
154 
155       rhs_code = gimple_assign_rhs_code (stmt);
156       rhs1 = gimple_assign_rhs1 (stmt);
157       if ((get_gimple_rhs_class (rhs_code) == GIMPLE_SINGLE_RHS
158              || gimple_assign_cast_p (stmt))
159             && TREE_CODE (rhs1) == SSA_NAME)
160           {
161             lhs = rhs1;
162             continue;
163           }
164 
165       if ((rhs_code == POINTER_PLUS_EXPR
166              || rhs_code == PLUS_EXPR)
167             && TREE_CODE (rhs1) == SSA_NAME
168             && host_integerp (gimple_assign_rhs2 (stmt), 1))
169           {
170             ret += tree_low_cst (gimple_assign_rhs2 (stmt), 1);
171             lhs = rhs1;
172             continue;
173           }
174 
175       if (rhs_code == ADDR_EXPR
176             && TREE_CODE (TREE_OPERAND (rhs1, 0)) == MEM_REF
177             && TREE_CODE (TREE_OPERAND (TREE_OPERAND (rhs1, 0), 0)) == SSA_NAME
178             && host_integerp (TREE_OPERAND (TREE_OPERAND (rhs1, 0), 1), 1))
179           {
180             ret += tree_low_cst (TREE_OPERAND (TREE_OPERAND (rhs1, 0), 1), 1);
181             lhs = TREE_OPERAND (TREE_OPERAND (rhs1, 0), 0);
182             continue;
183           }
184 
185       if (get_gimple_rhs_class (rhs_code) != GIMPLE_SINGLE_RHS)
186           return (unsigned HOST_WIDE_INT) -1;
187 
188       rhs = gimple_assign_rhs1 (stmt);
189       if (TREE_CODE (counter) != TREE_CODE (rhs))
190           return (unsigned HOST_WIDE_INT) -1;
191 
192       if (TREE_CODE (counter) == COMPONENT_REF)
193           {
194             if (get_base_address (counter) != get_base_address (rhs)
195                 || TREE_CODE (TREE_OPERAND (rhs, 1)) != FIELD_DECL
196                 || TREE_OPERAND (counter, 1) != TREE_OPERAND (rhs, 1))
197               return (unsigned HOST_WIDE_INT) -1;
198           }
199       else if (counter != rhs)
200           return (unsigned HOST_WIDE_INT) -1;
201 
202       lhs = NULL;
203     }
204 
205   lhs = orig_lhs;
206   val = ret + counter_val;
207   while (lhs)
208     {
209       enum tree_code rhs_code;
210       tree rhs1;
211 
212       if (si->offsets[SSA_NAME_VERSION (lhs)] != -1)
213           break;
214 
215       if (val >= max_size)
216           si->offsets[SSA_NAME_VERSION (lhs)] = max_size;
217       else
218           si->offsets[SSA_NAME_VERSION (lhs)] = val;
219 
220       stmt = SSA_NAME_DEF_STMT (lhs);
221 
222       rhs_code = gimple_assign_rhs_code (stmt);
223       rhs1 = gimple_assign_rhs1 (stmt);
224       if ((get_gimple_rhs_class (rhs_code) == GIMPLE_SINGLE_RHS
225              || gimple_assign_cast_p (stmt))
226             && TREE_CODE (rhs1) == SSA_NAME)
227           {
228             lhs = rhs1;
229             continue;
230           }
231 
232       if ((rhs_code == POINTER_PLUS_EXPR
233              || rhs_code == PLUS_EXPR)
234             && TREE_CODE (rhs1) == SSA_NAME
235             && host_integerp (gimple_assign_rhs2 (stmt), 1))
236           {
237             val -= tree_low_cst (gimple_assign_rhs2 (stmt), 1);
238             lhs = rhs1;
239             continue;
240           }
241 
242       if (rhs_code == ADDR_EXPR
243             && TREE_CODE (TREE_OPERAND (rhs1, 0)) == MEM_REF
244             && TREE_CODE (TREE_OPERAND (TREE_OPERAND (rhs1, 0), 0)) == SSA_NAME
245             && host_integerp (TREE_OPERAND (TREE_OPERAND (rhs1, 0), 1), 1))
246           {
247             val -= tree_low_cst (TREE_OPERAND (TREE_OPERAND (rhs1, 0), 1), 1);
248             lhs = TREE_OPERAND (TREE_OPERAND (rhs1, 0), 0);
249             continue;
250           }
251 
252       lhs = NULL;
253     }
254 
255   return ret;
256 }
257 
258 
259 /* Called by walk_tree to look for references to va_list variables.  */
260 
261 static tree
find_va_list_reference(tree * tp,int * walk_subtrees ATTRIBUTE_UNUSED,void * data)262 find_va_list_reference (tree *tp, int *walk_subtrees ATTRIBUTE_UNUSED,
263                               void *data)
264 {
265   bitmap va_list_vars = (bitmap) ((struct walk_stmt_info *) data)->info;
266   tree var = *tp;
267 
268   if (TREE_CODE (var) == SSA_NAME)
269     var = SSA_NAME_VAR (var);
270 
271   if (TREE_CODE (var) == VAR_DECL
272       && bitmap_bit_p (va_list_vars, DECL_UID (var)))
273     return var;
274 
275   return NULL_TREE;
276 }
277 
278 
279 /* Helper function of va_list_counter_struct_op.  Compute
280    cfun->va_list_{g,f}pr_size.  AP is a va_list GPR/FPR counter,
281    if WRITE_P is true, seen in AP = VAR, otherwise seen in VAR = AP
282    statement.  GPR_P is true if AP is a GPR counter, false if it is
283    a FPR counter.  */
284 
285 static void
va_list_counter_op(struct stdarg_info * si,tree ap,tree var,bool gpr_p,bool write_p)286 va_list_counter_op (struct stdarg_info *si, tree ap, tree var, bool gpr_p,
287                         bool write_p)
288 {
289   unsigned HOST_WIDE_INT increment;
290 
291   if (si->compute_sizes < 0)
292     {
293       si->compute_sizes = 0;
294       if (si->va_start_count == 1
295             && reachable_at_most_once (si->bb, si->va_start_bb))
296           si->compute_sizes = 1;
297 
298       if (dump_file && (dump_flags & TDF_DETAILS))
299           fprintf (dump_file,
300                      "bb%d will %sbe executed at most once for each va_start "
301                      "in bb%d\n", si->bb->index, si->compute_sizes ? "" : "not ",
302                      si->va_start_bb->index);
303     }
304 
305   if (write_p
306       && si->compute_sizes
307       && (increment = va_list_counter_bump (si, ap, var, gpr_p)) + 1 > 1)
308     {
309       if (gpr_p && cfun->va_list_gpr_size + increment < VA_LIST_MAX_GPR_SIZE)
310           {
311             cfun->va_list_gpr_size += increment;
312             return;
313           }
314 
315       if (!gpr_p && cfun->va_list_fpr_size + increment < VA_LIST_MAX_FPR_SIZE)
316           {
317             cfun->va_list_fpr_size += increment;
318             return;
319           }
320     }
321 
322   if (write_p || !si->compute_sizes)
323     {
324       if (gpr_p)
325           cfun->va_list_gpr_size = VA_LIST_MAX_GPR_SIZE;
326       else
327           cfun->va_list_fpr_size = VA_LIST_MAX_FPR_SIZE;
328     }
329 }
330 
331 
332 /* If AP is a va_list GPR/FPR counter, compute cfun->va_list_{g,f}pr_size.
333    If WRITE_P is true, AP has been seen in AP = VAR assignment, if WRITE_P
334    is false, AP has been seen in VAR = AP assignment.
335    Return true if the AP = VAR (resp. VAR = AP) statement is a recognized
336    va_arg operation that doesn't cause the va_list variable to escape
337    current function.  */
338 
339 static bool
va_list_counter_struct_op(struct stdarg_info * si,tree ap,tree var,bool write_p)340 va_list_counter_struct_op (struct stdarg_info *si, tree ap, tree var,
341                                  bool write_p)
342 {
343   tree base;
344 
345   if (TREE_CODE (ap) != COMPONENT_REF
346       || TREE_CODE (TREE_OPERAND (ap, 1)) != FIELD_DECL)
347     return false;
348 
349   if (TREE_CODE (var) != SSA_NAME
350       || bitmap_bit_p (si->va_list_vars, DECL_UID (SSA_NAME_VAR (var))))
351     return false;
352 
353   base = get_base_address (ap);
354   if (TREE_CODE (base) != VAR_DECL
355       || !bitmap_bit_p (si->va_list_vars, DECL_UID (base)))
356     return false;
357 
358   if (TREE_OPERAND (ap, 1) == va_list_gpr_counter_field)
359     va_list_counter_op (si, ap, var, true, write_p);
360   else if (TREE_OPERAND (ap, 1) == va_list_fpr_counter_field)
361     va_list_counter_op (si, ap, var, false, write_p);
362 
363   return true;
364 }
365 
366 
367 /* Check for TEM = AP.  Return true if found and the caller shouldn't
368    search for va_list references in the statement.  */
369 
370 static bool
va_list_ptr_read(struct stdarg_info * si,tree ap,tree tem)371 va_list_ptr_read (struct stdarg_info *si, tree ap, tree tem)
372 {
373   if (TREE_CODE (ap) != VAR_DECL
374       || !bitmap_bit_p (si->va_list_vars, DECL_UID (ap)))
375     return false;
376 
377   if (TREE_CODE (tem) != SSA_NAME
378       || bitmap_bit_p (si->va_list_vars,
379                            DECL_UID (SSA_NAME_VAR (tem)))
380       || is_global_var (SSA_NAME_VAR (tem)))
381     return false;
382 
383   if (si->compute_sizes < 0)
384     {
385       si->compute_sizes = 0;
386       if (si->va_start_count == 1
387             && reachable_at_most_once (si->bb, si->va_start_bb))
388           si->compute_sizes = 1;
389 
390       if (dump_file && (dump_flags & TDF_DETAILS))
391           fprintf (dump_file,
392                      "bb%d will %sbe executed at most once for each va_start "
393                      "in bb%d\n", si->bb->index, si->compute_sizes ? "" : "not ",
394                      si->va_start_bb->index);
395     }
396 
397   /* For void * or char * va_list types, there is just one counter.
398      If va_arg is used in a loop, we don't know how many registers need
399      saving.  */
400   if (! si->compute_sizes)
401     return false;
402 
403   if (va_list_counter_bump (si, ap, tem, true) == (unsigned HOST_WIDE_INT) -1)
404     return false;
405 
406   /* Note the temporary, as we need to track whether it doesn't escape
407      the current function.  */
408   bitmap_set_bit (si->va_list_escape_vars,
409                       DECL_UID (SSA_NAME_VAR (tem)));
410   return true;
411 }
412 
413 
414 /* Check for:
415      tem1 = AP;
416      TEM2 = tem1 + CST;
417      AP = TEM2;
418    sequence and update cfun->va_list_gpr_size.  Return true if found.  */
419 
420 static bool
va_list_ptr_write(struct stdarg_info * si,tree ap,tree tem2)421 va_list_ptr_write (struct stdarg_info *si, tree ap, tree tem2)
422 {
423   unsigned HOST_WIDE_INT increment;
424 
425   if (TREE_CODE (ap) != VAR_DECL
426       || !bitmap_bit_p (si->va_list_vars, DECL_UID (ap)))
427     return false;
428 
429   if (TREE_CODE (tem2) != SSA_NAME
430       || bitmap_bit_p (si->va_list_vars, DECL_UID (SSA_NAME_VAR (tem2))))
431     return false;
432 
433   if (si->compute_sizes <= 0)
434     return false;
435 
436   increment = va_list_counter_bump (si, ap, tem2, true);
437   if (increment + 1 <= 1)
438     return false;
439 
440   if (cfun->va_list_gpr_size + increment < VA_LIST_MAX_GPR_SIZE)
441     cfun->va_list_gpr_size += increment;
442   else
443     cfun->va_list_gpr_size = VA_LIST_MAX_GPR_SIZE;
444 
445   return true;
446 }
447 
448 
449 /* If RHS is X, (some type *) X or X + CST for X a temporary variable
450    containing value of some va_list variable plus optionally some constant,
451    either set si->va_list_escapes or add LHS to si->va_list_escape_vars,
452    depending whether LHS is a function local temporary.  */
453 
454 static void
check_va_list_escapes(struct stdarg_info * si,tree lhs,tree rhs)455 check_va_list_escapes (struct stdarg_info *si, tree lhs, tree rhs)
456 {
457   if (! POINTER_TYPE_P (TREE_TYPE (rhs)))
458     return;
459 
460   if (TREE_CODE (rhs) == SSA_NAME)
461     {
462       if (! bitmap_bit_p (si->va_list_escape_vars,
463                                 DECL_UID (SSA_NAME_VAR (rhs))))
464           return;
465     }
466   else if (TREE_CODE (rhs) == ADDR_EXPR
467              && TREE_CODE (TREE_OPERAND (rhs, 0)) == MEM_REF
468              && TREE_CODE (TREE_OPERAND (TREE_OPERAND (rhs, 0), 0)) == SSA_NAME)
469     {
470       if (! bitmap_bit_p (si->va_list_escape_vars,
471                                 DECL_UID (SSA_NAME_VAR (TREE_OPERAND
472                                                               (TREE_OPERAND (rhs, 0), 0)))))
473           return;
474     }
475   else
476     return;
477 
478   if (TREE_CODE (lhs) != SSA_NAME || is_global_var (SSA_NAME_VAR (lhs)))
479     {
480       si->va_list_escapes = true;
481       return;
482     }
483 
484   if (si->compute_sizes < 0)
485     {
486       si->compute_sizes = 0;
487       if (si->va_start_count == 1
488             && reachable_at_most_once (si->bb, si->va_start_bb))
489           si->compute_sizes = 1;
490 
491       if (dump_file && (dump_flags & TDF_DETAILS))
492           fprintf (dump_file,
493                      "bb%d will %sbe executed at most once for each va_start "
494                      "in bb%d\n", si->bb->index, si->compute_sizes ? "" : "not ",
495                      si->va_start_bb->index);
496     }
497 
498   /* For void * or char * va_list types, there is just one counter.
499      If va_arg is used in a loop, we don't know how many registers need
500      saving.  */
501   if (! si->compute_sizes)
502     {
503       si->va_list_escapes = true;
504       return;
505     }
506 
507   if (va_list_counter_bump (si, si->va_start_ap, lhs, true)
508       == (unsigned HOST_WIDE_INT) -1)
509     {
510       si->va_list_escapes = true;
511       return;
512     }
513 
514   bitmap_set_bit (si->va_list_escape_vars,
515                       DECL_UID (SSA_NAME_VAR (lhs)));
516 }
517 
518 
519 /* Check all uses of temporaries from si->va_list_escape_vars bitmap.
520    Return true if va_list might be escaping.  */
521 
522 static bool
check_all_va_list_escapes(struct stdarg_info * si)523 check_all_va_list_escapes (struct stdarg_info *si)
524 {
525   basic_block bb;
526 
527   FOR_EACH_BB (bb)
528     {
529       gimple_stmt_iterator i;
530 
531       for (i = gsi_start_bb (bb); !gsi_end_p (i); gsi_next (&i))
532           {
533             gimple stmt = gsi_stmt (i);
534             tree use;
535             ssa_op_iter iter;
536 
537             if (is_gimple_debug (stmt))
538               continue;
539 
540             FOR_EACH_SSA_TREE_OPERAND (use, stmt, iter, SSA_OP_ALL_USES)
541               {
542                 if (! bitmap_bit_p (si->va_list_escape_vars,
543                                           DECL_UID (SSA_NAME_VAR (use))))
544                     continue;
545 
546                 if (is_gimple_assign (stmt))
547                     {
548                       tree rhs = gimple_assign_rhs1 (stmt);
549                       enum tree_code rhs_code = gimple_assign_rhs_code (stmt);
550 
551                       /* x = *ap_temp;  */
552                       if (rhs_code == MEM_REF
553                           && TREE_OPERAND (rhs, 0) == use
554                           && TYPE_SIZE_UNIT (TREE_TYPE (rhs))
555                           && host_integerp (TYPE_SIZE_UNIT (TREE_TYPE (rhs)), 1)
556                           && si->offsets[SSA_NAME_VERSION (use)] != -1)
557                         {
558                           unsigned HOST_WIDE_INT gpr_size;
559                           tree access_size = TYPE_SIZE_UNIT (TREE_TYPE (rhs));
560 
561                           gpr_size = si->offsets[SSA_NAME_VERSION (use)]
562                                          + tree_low_cst (TREE_OPERAND (rhs, 1), 0)
563                                          + tree_low_cst (access_size, 1);
564                           if (gpr_size >= VA_LIST_MAX_GPR_SIZE)
565                               cfun->va_list_gpr_size = VA_LIST_MAX_GPR_SIZE;
566                           else if (gpr_size > cfun->va_list_gpr_size)
567                               cfun->va_list_gpr_size = gpr_size;
568                           continue;
569                         }
570 
571                       /* va_arg sequences may contain
572                          other_ap_temp = ap_temp;
573                          other_ap_temp = ap_temp + constant;
574                          other_ap_temp = (some_type *) ap_temp;
575                          ap = ap_temp;
576                          statements.  */
577                       if (rhs == use
578                           && ((rhs_code == POINTER_PLUS_EXPR
579                                  && (TREE_CODE (gimple_assign_rhs2 (stmt))
580                                      == INTEGER_CST))
581                                 || gimple_assign_cast_p (stmt)
582                                 || (get_gimple_rhs_class (rhs_code)
583                                     == GIMPLE_SINGLE_RHS)))
584                         {
585                           tree lhs = gimple_assign_lhs (stmt);
586 
587                           if (TREE_CODE (lhs) == SSA_NAME
588                                 && bitmap_bit_p (si->va_list_escape_vars,
589                                                      DECL_UID (SSA_NAME_VAR (lhs))))
590                               continue;
591 
592                           if (TREE_CODE (lhs) == VAR_DECL
593                                 && bitmap_bit_p (si->va_list_vars,
594                                                      DECL_UID (lhs)))
595                               continue;
596                         }
597                       else if (rhs_code == ADDR_EXPR
598                                  && TREE_CODE (TREE_OPERAND (rhs, 0)) == MEM_REF
599                                  && TREE_OPERAND (TREE_OPERAND (rhs, 0), 0) == use)
600                         {
601                           tree lhs = gimple_assign_lhs (stmt);
602 
603                           if (bitmap_bit_p (si->va_list_escape_vars,
604                                                   DECL_UID (SSA_NAME_VAR (lhs))))
605                               continue;
606                         }
607                     }
608 
609                 if (dump_file && (dump_flags & TDF_DETAILS))
610                     {
611                       fputs ("va_list escapes in ", dump_file);
612                       print_gimple_stmt (dump_file, stmt, 0, dump_flags);
613                       fputc ('\n', dump_file);
614                     }
615                 return true;
616               }
617           }
618     }
619 
620   return false;
621 }
622 
623 
624 /* Return true if this optimization pass should be done.
625    It makes only sense for stdarg functions.  */
626 
627 static bool
gate_optimize_stdarg(void)628 gate_optimize_stdarg (void)
629 {
630   /* This optimization is only for stdarg functions.  */
631   return cfun->stdarg != 0;
632 }
633 
634 
635 /* Entry point to the stdarg optimization pass.  */
636 
637 static unsigned int
execute_optimize_stdarg(void)638 execute_optimize_stdarg (void)
639 {
640   basic_block bb;
641   bool va_list_escapes = false;
642   bool va_list_simple_ptr;
643   struct stdarg_info si;
644   struct walk_stmt_info wi;
645   const char *funcname = NULL;
646   tree cfun_va_list;
647 
648   cfun->va_list_gpr_size = 0;
649   cfun->va_list_fpr_size = 0;
650   memset (&si, 0, sizeof (si));
651   si.va_list_vars = BITMAP_ALLOC (NULL);
652   si.va_list_escape_vars = BITMAP_ALLOC (NULL);
653 
654   if (dump_file)
655     funcname = lang_hooks.decl_printable_name (current_function_decl, 2);
656 
657   cfun_va_list = targetm.fn_abi_va_list (cfun->decl);
658   va_list_simple_ptr = POINTER_TYPE_P (cfun_va_list)
659                            && (TREE_TYPE (cfun_va_list) == void_type_node
660                                  || TREE_TYPE (cfun_va_list) == char_type_node);
661   gcc_assert (is_gimple_reg_type (cfun_va_list) == va_list_simple_ptr);
662 
663   FOR_EACH_BB (bb)
664     {
665       gimple_stmt_iterator i;
666 
667       for (i = gsi_start_bb (bb); !gsi_end_p (i); gsi_next (&i))
668           {
669             gimple stmt = gsi_stmt (i);
670             tree callee, ap;
671 
672             if (!is_gimple_call (stmt))
673               continue;
674 
675             callee = gimple_call_fndecl (stmt);
676             if (!callee
677                 || DECL_BUILT_IN_CLASS (callee) != BUILT_IN_NORMAL)
678               continue;
679 
680             switch (DECL_FUNCTION_CODE (callee))
681               {
682               case BUILT_IN_VA_START:
683                 break;
684                 /* If old style builtins are used, don't optimize anything.  */
685               case BUILT_IN_SAVEREGS:
686               case BUILT_IN_NEXT_ARG:
687                 va_list_escapes = true;
688                 continue;
689               default:
690                 continue;
691               }
692 
693             si.va_start_count++;
694             ap = gimple_call_arg (stmt, 0);
695 
696             if (TREE_CODE (ap) != ADDR_EXPR)
697               {
698                 va_list_escapes = true;
699                 break;
700               }
701             ap = TREE_OPERAND (ap, 0);
702             if (TREE_CODE (ap) == ARRAY_REF)
703               {
704                 if (! integer_zerop (TREE_OPERAND (ap, 1)))
705                   {
706                     va_list_escapes = true;
707                     break;
708                     }
709                 ap = TREE_OPERAND (ap, 0);
710               }
711             if (TYPE_MAIN_VARIANT (TREE_TYPE (ap))
712                 != TYPE_MAIN_VARIANT (targetm.fn_abi_va_list (cfun->decl))
713                 || TREE_CODE (ap) != VAR_DECL)
714               {
715                 va_list_escapes = true;
716                 break;
717               }
718 
719             if (is_global_var (ap))
720               {
721                 va_list_escapes = true;
722                 break;
723               }
724 
725             bitmap_set_bit (si.va_list_vars, DECL_UID (ap));
726 
727             /* VA_START_BB and VA_START_AP will be only used if there is just
728                one va_start in the function.  */
729             si.va_start_bb = bb;
730             si.va_start_ap = ap;
731           }
732 
733       if (va_list_escapes)
734           break;
735     }
736 
737   /* If there were no va_start uses in the function, there is no need to
738      save anything.  */
739   if (si.va_start_count == 0)
740     goto finish;
741 
742   /* If some va_list arguments weren't local, we can't optimize.  */
743   if (va_list_escapes)
744     goto finish;
745 
746   /* For void * or char * va_list, something useful can be done only
747      if there is just one va_start.  */
748   if (va_list_simple_ptr && si.va_start_count > 1)
749     {
750       va_list_escapes = true;
751       goto finish;
752     }
753 
754   /* For struct * va_list, if the backend didn't tell us what the counter fields
755      are, there is nothing more we can do.  */
756   if (!va_list_simple_ptr
757       && va_list_gpr_counter_field == NULL_TREE
758       && va_list_fpr_counter_field == NULL_TREE)
759     {
760       va_list_escapes = true;
761       goto finish;
762     }
763 
764   /* For void * or char * va_list there is just one counter
765      (va_list itself).  Use VA_LIST_GPR_SIZE for it.  */
766   if (va_list_simple_ptr)
767     cfun->va_list_fpr_size = VA_LIST_MAX_FPR_SIZE;
768 
769   calculate_dominance_info (CDI_DOMINATORS);
770   memset (&wi, 0, sizeof (wi));
771   wi.info = si.va_list_vars;
772 
773   FOR_EACH_BB (bb)
774     {
775       gimple_stmt_iterator i;
776 
777       si.compute_sizes = -1;
778       si.bb = bb;
779 
780       /* For va_list_simple_ptr, we have to check PHI nodes too.  We treat
781            them as assignments for the purpose of escape analysis.  This is
782            not needed for non-simple va_list because virtual phis don't perform
783            any real data movement.  */
784       if (va_list_simple_ptr)
785           {
786             tree lhs, rhs;
787             use_operand_p uop;
788             ssa_op_iter soi;
789 
790             for (i = gsi_start_phis (bb); !gsi_end_p (i); gsi_next (&i))
791               {
792                 gimple phi = gsi_stmt (i);
793                 lhs = PHI_RESULT (phi);
794 
795                 if (!is_gimple_reg (lhs))
796                     continue;
797 
798                 FOR_EACH_PHI_ARG (uop, phi, soi, SSA_OP_USE)
799                     {
800                       rhs = USE_FROM_PTR (uop);
801                       if (va_list_ptr_read (&si, rhs, lhs))
802                         continue;
803                       else if (va_list_ptr_write (&si, lhs, rhs))
804                         continue;
805                       else
806                         check_va_list_escapes (&si, lhs, rhs);
807 
808                       if (si.va_list_escapes)
809                         {
810                           if (dump_file && (dump_flags & TDF_DETAILS))
811                               {
812                                 fputs ("va_list escapes in ", dump_file);
813                                 print_gimple_stmt (dump_file, phi, 0, dump_flags);
814                                 fputc ('\n', dump_file);
815                               }
816                           va_list_escapes = true;
817                         }
818                     }
819               }
820           }
821 
822       for (i = gsi_start_bb (bb);
823              !gsi_end_p (i) && !va_list_escapes;
824              gsi_next (&i))
825           {
826             gimple stmt = gsi_stmt (i);
827 
828             /* Don't look at __builtin_va_{start,end}, they are ok.  */
829             if (is_gimple_call (stmt))
830               {
831                 tree callee = gimple_call_fndecl (stmt);
832 
833                 if (callee
834                       && DECL_BUILT_IN_CLASS (callee) == BUILT_IN_NORMAL
835                       && (DECL_FUNCTION_CODE (callee) == BUILT_IN_VA_START
836                           || DECL_FUNCTION_CODE (callee) == BUILT_IN_VA_END))
837                     continue;
838               }
839 
840             if (is_gimple_assign (stmt))
841               {
842                 tree lhs = gimple_assign_lhs (stmt);
843                 tree rhs = gimple_assign_rhs1 (stmt);
844 
845                 if (va_list_simple_ptr)
846                     {
847                       if (get_gimple_rhs_class (gimple_assign_rhs_code (stmt))
848                           == GIMPLE_SINGLE_RHS)
849                         {
850                           /* Check for ap ={v} {}.  */
851                           if (TREE_CLOBBER_P (rhs))
852                               continue;
853 
854                           /* Check for tem = ap.  */
855                           else if (va_list_ptr_read (&si, rhs, lhs))
856                               continue;
857 
858                           /* Check for the last insn in:
859                                tem1 = ap;
860                                tem2 = tem1 + CST;
861                                ap = tem2;
862                                sequence.  */
863                           else if (va_list_ptr_write (&si, lhs, rhs))
864                               continue;
865                         }
866 
867                       if ((gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR
868                            && TREE_CODE (gimple_assign_rhs2 (stmt)) == INTEGER_CST)
869                           || CONVERT_EXPR_CODE_P (gimple_assign_rhs_code (stmt))
870                           || (get_gimple_rhs_class (gimple_assign_rhs_code (stmt))
871                                 == GIMPLE_SINGLE_RHS))
872                         check_va_list_escapes (&si, lhs, rhs);
873                     }
874                 else
875                     {
876                       if (get_gimple_rhs_class (gimple_assign_rhs_code (stmt))
877                           == GIMPLE_SINGLE_RHS)
878                         {
879                           /* Check for ap ={v} {}.  */
880                           if (TREE_CLOBBER_P (rhs))
881                               continue;
882 
883                           /* Check for ap[0].field = temp.  */
884                           else if (va_list_counter_struct_op (&si, lhs, rhs, true))
885                               continue;
886 
887                           /* Check for temp = ap[0].field.  */
888                           else if (va_list_counter_struct_op (&si, rhs, lhs,
889                                                                         false))
890                               continue;
891                         }
892 
893                       /* Do any architecture specific checking.  */
894                       if (targetm.stdarg_optimize_hook
895                           && targetm.stdarg_optimize_hook (&si, stmt))
896                         continue;
897                     }
898               }
899             else if (is_gimple_debug (stmt))
900               continue;
901 
902             /* All other uses of va_list are either va_copy (that is not handled
903                in this optimization), taking address of va_list variable or
904                passing va_list to other functions (in that case va_list might
905                escape the function and therefore va_start needs to set it up
906                fully), or some unexpected use of va_list.  None of these should
907                happen in a gimplified VA_ARG_EXPR.  */
908             if (si.va_list_escapes
909                 || walk_gimple_op (stmt, find_va_list_reference, &wi))
910               {
911                 if (dump_file && (dump_flags & TDF_DETAILS))
912                     {
913                       fputs ("va_list escapes in ", dump_file);
914                       print_gimple_stmt (dump_file, stmt, 0, dump_flags);
915                       fputc ('\n', dump_file);
916                     }
917                 va_list_escapes = true;
918               }
919           }
920 
921       if (va_list_escapes)
922           break;
923     }
924 
925   if (! va_list_escapes
926       && va_list_simple_ptr
927       && ! bitmap_empty_p (si.va_list_escape_vars)
928       && check_all_va_list_escapes (&si))
929     va_list_escapes = true;
930 
931 finish:
932   if (va_list_escapes)
933     {
934       cfun->va_list_gpr_size = VA_LIST_MAX_GPR_SIZE;
935       cfun->va_list_fpr_size = VA_LIST_MAX_FPR_SIZE;
936     }
937   BITMAP_FREE (si.va_list_vars);
938   BITMAP_FREE (si.va_list_escape_vars);
939   free (si.offsets);
940   if (dump_file)
941     {
942       fprintf (dump_file, "%s: va_list escapes %d, needs to save ",
943                  funcname, (int) va_list_escapes);
944       if (cfun->va_list_gpr_size >= VA_LIST_MAX_GPR_SIZE)
945           fputs ("all", dump_file);
946       else
947           fprintf (dump_file, "%d", cfun->va_list_gpr_size);
948       fputs (" GPR units and ", dump_file);
949       if (cfun->va_list_fpr_size >= VA_LIST_MAX_FPR_SIZE)
950           fputs ("all", dump_file);
951       else
952           fprintf (dump_file, "%d", cfun->va_list_fpr_size);
953       fputs (" FPR units.\n", dump_file);
954     }
955   return 0;
956 }
957 
958 
959 struct gimple_opt_pass pass_stdarg =
960 {
961  {
962   GIMPLE_PASS,
963   "stdarg",                                       /* name */
964   gate_optimize_stdarg,                           /* gate */
965   execute_optimize_stdarg,              /* execute */
966   NULL,                                           /* sub */
967   NULL,                                           /* next */
968   0,                                              /* static_pass_number */
969   TV_NONE,                                        /* tv_id */
970   PROP_cfg | PROP_ssa,                            /* properties_required */
971   0,                                              /* properties_provided */
972   0,                                              /* properties_destroyed */
973   0,                                              /* todo_flags_start */
974   0                                     /* todo_flags_finish */
975  }
976 };
977