1169689Skan/* Pass computing data for optimizing stdarg functions.
2169689Skan   Copyright (C) 2004, 2005 Free Software Foundation, Inc.
3169689Skan   Contributed by Jakub Jelinek <jakub@redhat.com>
4169689Skan
5169689SkanThis file is part of GCC.
6169689Skan
7169689SkanGCC is free software; you can redistribute it and/or modify
8169689Skanit under the terms of the GNU General Public License as published by
9169689Skanthe Free Software Foundation; either version 2, or (at your option)
10169689Skanany later version.
11169689Skan
12169689SkanGCC is distributed in the hope that it will be useful,
13169689Skanbut WITHOUT ANY WARRANTY; without even the implied warranty of
14169689SkanMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15169689SkanGNU General Public License for more details.
16169689Skan
17169689SkanYou should have received a copy of the GNU General Public License
18169689Skanalong with GCC; see the file COPYING.  If not, write to
19169689Skanthe Free Software Foundation, 51 Franklin Street, Fifth Floor,
20169689SkanBoston, MA 02110-1301, USA.  */
21169689Skan
22169689Skan#include "config.h"
23169689Skan#include "system.h"
24169689Skan#include "coretypes.h"
25169689Skan#include "tm.h"
26169689Skan#include "tree.h"
27169689Skan#include "function.h"
28169689Skan#include "langhooks.h"
29169689Skan#include "diagnostic.h"
30169689Skan#include "target.h"
31169689Skan#include "tree-flow.h"
32169689Skan#include "tree-pass.h"
33169689Skan#include "tree-stdarg.h"
34169689Skan
35169689Skan/* A simple pass that attempts to optimize stdarg functions on architectures
36169689Skan   that need to save register arguments to stack on entry to stdarg functions.
37169689Skan   If the function doesn't use any va_start macros, no registers need to
38169689Skan   be saved.  If va_start macros are used, the va_list variables don't escape
39169689Skan   the function, it is only necessary to save registers that will be used
40169689Skan   in va_arg macros.  E.g. if va_arg is only used with integral types
41169689Skan   in the function, floating point registers don't need to be saved, etc.  */
42169689Skan
43169689Skan
44169689Skan/* Return true if basic block VA_ARG_BB is dominated by VA_START_BB and
45169689Skan   is executed at most as many times as VA_START_BB.  */
46169689Skan
47169689Skanstatic bool
48169689Skanreachable_at_most_once (basic_block va_arg_bb, basic_block va_start_bb)
49169689Skan{
50169689Skan  edge *stack, e;
51169689Skan  edge_iterator ei;
52169689Skan  int sp;
53169689Skan  sbitmap visited;
54169689Skan  bool ret;
55169689Skan
56169689Skan  if (va_arg_bb == va_start_bb)
57169689Skan    return true;
58169689Skan
59169689Skan  if (! dominated_by_p (CDI_DOMINATORS, va_arg_bb, va_start_bb))
60169689Skan    return false;
61169689Skan
62169689Skan  stack = XNEWVEC (edge, n_basic_blocks + 1);
63169689Skan  sp = 0;
64169689Skan
65169689Skan  visited = sbitmap_alloc (last_basic_block);
66169689Skan  sbitmap_zero (visited);
67169689Skan  ret = true;
68169689Skan
69169689Skan  FOR_EACH_EDGE (e, ei, va_arg_bb->preds)
70169689Skan    stack[sp++] = e;
71169689Skan
72169689Skan  while (sp)
73169689Skan    {
74169689Skan      basic_block src;
75169689Skan
76169689Skan      --sp;
77169689Skan      e = stack[sp];
78169689Skan      src = e->src;
79169689Skan
80169689Skan      if (e->flags & EDGE_COMPLEX)
81169689Skan	{
82169689Skan	  ret = false;
83169689Skan	  break;
84169689Skan	}
85169689Skan
86169689Skan      if (src == va_start_bb)
87169689Skan	continue;
88169689Skan
89169689Skan      /* va_arg_bb can be executed more times than va_start_bb.  */
90169689Skan      if (src == va_arg_bb)
91169689Skan	{
92169689Skan	  ret = false;
93169689Skan	  break;
94169689Skan	}
95169689Skan
96169689Skan      gcc_assert (src != ENTRY_BLOCK_PTR);
97169689Skan
98169689Skan      if (! TEST_BIT (visited, src->index))
99169689Skan	{
100169689Skan	  SET_BIT (visited, src->index);
101169689Skan	  FOR_EACH_EDGE (e, ei, src->preds)
102169689Skan	    stack[sp++] = e;
103169689Skan	}
104169689Skan    }
105169689Skan
106169689Skan  free (stack);
107169689Skan  sbitmap_free (visited);
108169689Skan  return ret;
109169689Skan}
110169689Skan
111169689Skan
112169689Skan/* For statement COUNTER = RHS, if RHS is COUNTER + constant,
113169689Skan   return constant, otherwise return (unsigned HOST_WIDE_INT) -1.
114169689Skan   GPR_P is true if this is GPR counter.  */
115169689Skan
116169689Skanstatic unsigned HOST_WIDE_INT
117169689Skanva_list_counter_bump (struct stdarg_info *si, tree counter, tree rhs,
118169689Skan		      bool gpr_p)
119169689Skan{
120169689Skan  tree stmt, lhs, orig_lhs;
121169689Skan  unsigned HOST_WIDE_INT ret = 0, val, counter_val;
122169689Skan  unsigned int max_size;
123169689Skan
124169689Skan  if (si->offsets == NULL)
125169689Skan    {
126169689Skan      unsigned int i;
127169689Skan
128169689Skan      si->offsets = XNEWVEC (int, num_ssa_names);
129169689Skan      for (i = 0; i < num_ssa_names; ++i)
130169689Skan	si->offsets[i] = -1;
131169689Skan    }
132169689Skan
133169689Skan  counter_val = gpr_p ? cfun->va_list_gpr_size : cfun->va_list_fpr_size;
134169689Skan  max_size = gpr_p ? VA_LIST_MAX_GPR_SIZE : VA_LIST_MAX_FPR_SIZE;
135169689Skan  orig_lhs = lhs = rhs;
136169689Skan  while (lhs)
137169689Skan    {
138169689Skan      if (si->offsets[SSA_NAME_VERSION (lhs)] != -1)
139169689Skan	{
140169689Skan	  if (counter_val >= max_size)
141169689Skan	    {
142169689Skan	      ret = max_size;
143169689Skan	      break;
144169689Skan	    }
145169689Skan
146169689Skan	  ret -= counter_val - si->offsets[SSA_NAME_VERSION (lhs)];
147169689Skan	  break;
148169689Skan	}
149169689Skan
150169689Skan      stmt = SSA_NAME_DEF_STMT (lhs);
151169689Skan
152169689Skan      if (TREE_CODE (stmt) != MODIFY_EXPR
153169689Skan	  || TREE_OPERAND (stmt, 0) != lhs)
154169689Skan	return (unsigned HOST_WIDE_INT) -1;
155169689Skan
156169689Skan      rhs = TREE_OPERAND (stmt, 1);
157169689Skan      if (TREE_CODE (rhs) == WITH_SIZE_EXPR)
158169689Skan	rhs = TREE_OPERAND (rhs, 0);
159169689Skan
160169689Skan      if (TREE_CODE (rhs) == SSA_NAME)
161169689Skan	{
162169689Skan	  lhs = rhs;
163169689Skan	  continue;
164169689Skan	}
165169689Skan
166169689Skan      if ((TREE_CODE (rhs) == NOP_EXPR
167169689Skan	   || TREE_CODE (rhs) == CONVERT_EXPR)
168169689Skan	  && TREE_CODE (TREE_OPERAND (rhs, 0)) == SSA_NAME)
169169689Skan	{
170169689Skan	  lhs = TREE_OPERAND (rhs, 0);
171169689Skan	  continue;
172169689Skan	}
173169689Skan
174169689Skan      if (TREE_CODE (rhs) == PLUS_EXPR
175169689Skan	  && TREE_CODE (TREE_OPERAND (rhs, 0)) == SSA_NAME
176169689Skan	  && TREE_CODE (TREE_OPERAND (rhs, 1)) == INTEGER_CST
177169689Skan	  && host_integerp (TREE_OPERAND (rhs, 1), 1))
178169689Skan	{
179169689Skan	  ret += tree_low_cst (TREE_OPERAND (rhs, 1), 1);
180169689Skan	  lhs = TREE_OPERAND (rhs, 0);
181169689Skan	  continue;
182169689Skan	}
183169689Skan
184169689Skan      if (TREE_CODE (counter) != TREE_CODE (rhs))
185169689Skan	return (unsigned HOST_WIDE_INT) -1;
186169689Skan
187169689Skan      if (TREE_CODE (counter) == COMPONENT_REF)
188169689Skan	{
189169689Skan	  if (get_base_address (counter) != get_base_address (rhs)
190169689Skan	      || TREE_CODE (TREE_OPERAND (rhs, 1)) != FIELD_DECL
191169689Skan	      || TREE_OPERAND (counter, 1) != TREE_OPERAND (rhs, 1))
192169689Skan	    return (unsigned HOST_WIDE_INT) -1;
193169689Skan	}
194169689Skan      else if (counter != rhs)
195169689Skan	return (unsigned HOST_WIDE_INT) -1;
196169689Skan
197169689Skan      lhs = NULL;
198169689Skan    }
199169689Skan
200169689Skan  lhs = orig_lhs;
201169689Skan  val = ret + counter_val;
202169689Skan  while (lhs)
203169689Skan    {
204169689Skan      if (si->offsets[SSA_NAME_VERSION (lhs)] != -1)
205169689Skan	break;
206169689Skan
207169689Skan      if (val >= max_size)
208169689Skan	si->offsets[SSA_NAME_VERSION (lhs)] = max_size;
209169689Skan      else
210169689Skan	si->offsets[SSA_NAME_VERSION (lhs)] = val;
211169689Skan
212169689Skan      stmt = SSA_NAME_DEF_STMT (lhs);
213169689Skan
214169689Skan      rhs = TREE_OPERAND (stmt, 1);
215169689Skan      if (TREE_CODE (rhs) == WITH_SIZE_EXPR)
216169689Skan	rhs = TREE_OPERAND (rhs, 0);
217169689Skan
218169689Skan      if (TREE_CODE (rhs) == SSA_NAME)
219169689Skan	{
220169689Skan	  lhs = rhs;
221169689Skan	  continue;
222169689Skan	}
223169689Skan
224169689Skan      if ((TREE_CODE (rhs) == NOP_EXPR
225169689Skan	   || TREE_CODE (rhs) == CONVERT_EXPR)
226169689Skan	  && TREE_CODE (TREE_OPERAND (rhs, 0)) == SSA_NAME)
227169689Skan	{
228169689Skan	  lhs = TREE_OPERAND (rhs, 0);
229169689Skan	  continue;
230169689Skan	}
231169689Skan
232169689Skan      if (TREE_CODE (rhs) == PLUS_EXPR
233169689Skan	  && TREE_CODE (TREE_OPERAND (rhs, 0)) == SSA_NAME
234169689Skan	  && TREE_CODE (TREE_OPERAND (rhs, 1)) == INTEGER_CST
235169689Skan	  && host_integerp (TREE_OPERAND (rhs, 1), 1))
236169689Skan	{
237169689Skan	  val -= tree_low_cst (TREE_OPERAND (rhs, 1), 1);
238169689Skan	  lhs = TREE_OPERAND (rhs, 0);
239169689Skan	  continue;
240169689Skan	}
241169689Skan
242169689Skan      lhs = NULL;
243169689Skan    }
244169689Skan
245169689Skan  return ret;
246169689Skan}
247169689Skan
248169689Skan
249169689Skan/* Called by walk_tree to look for references to va_list variables.  */
250169689Skan
251169689Skanstatic tree
252169689Skanfind_va_list_reference (tree *tp, int *walk_subtrees ATTRIBUTE_UNUSED,
253169689Skan			void *data)
254169689Skan{
255169689Skan  bitmap va_list_vars = (bitmap) data;
256169689Skan  tree var = *tp;
257169689Skan
258169689Skan  if (TREE_CODE (var) == SSA_NAME)
259169689Skan    var = SSA_NAME_VAR (var);
260169689Skan
261169689Skan  if (TREE_CODE (var) == VAR_DECL
262169689Skan      && bitmap_bit_p (va_list_vars, DECL_UID (var)))
263169689Skan    return var;
264169689Skan
265169689Skan  return NULL_TREE;
266169689Skan}
267169689Skan
268169689Skan
269169689Skan/* Helper function of va_list_counter_struct_op.  Compute
270169689Skan   cfun->va_list_{g,f}pr_size.  AP is a va_list GPR/FPR counter,
271169689Skan   if WRITE_P is true, seen in AP = VAR, otherwise seen in VAR = AP
272169689Skan   statement.  GPR_P is true if AP is a GPR counter, false if it is
273169689Skan   a FPR counter.  */
274169689Skan
275169689Skanstatic void
276169689Skanva_list_counter_op (struct stdarg_info *si, tree ap, tree var, bool gpr_p,
277169689Skan		    bool write_p)
278169689Skan{
279169689Skan  unsigned HOST_WIDE_INT increment;
280169689Skan
281169689Skan  if (si->compute_sizes < 0)
282169689Skan    {
283169689Skan      si->compute_sizes = 0;
284169689Skan      if (si->va_start_count == 1
285169689Skan	  && reachable_at_most_once (si->bb, si->va_start_bb))
286169689Skan	si->compute_sizes = 1;
287169689Skan
288169689Skan      if (dump_file && (dump_flags & TDF_DETAILS))
289169689Skan	fprintf (dump_file,
290169689Skan		 "bb%d will %sbe executed at most once for each va_start "
291169689Skan		 "in bb%d\n", si->bb->index, si->compute_sizes ? "" : "not ",
292169689Skan		 si->va_start_bb->index);
293169689Skan    }
294169689Skan
295169689Skan  if (write_p
296169689Skan      && si->compute_sizes
297169689Skan      && (increment = va_list_counter_bump (si, ap, var, gpr_p)) + 1 > 1)
298169689Skan    {
299169689Skan      if (gpr_p && cfun->va_list_gpr_size + increment < VA_LIST_MAX_GPR_SIZE)
300169689Skan	{
301169689Skan	  cfun->va_list_gpr_size += increment;
302169689Skan	  return;
303169689Skan	}
304169689Skan
305169689Skan      if (!gpr_p && cfun->va_list_fpr_size + increment < VA_LIST_MAX_FPR_SIZE)
306169689Skan	{
307169689Skan	  cfun->va_list_fpr_size += increment;
308169689Skan	  return;
309169689Skan	}
310169689Skan    }
311169689Skan
312169689Skan  if (write_p || !si->compute_sizes)
313169689Skan    {
314169689Skan      if (gpr_p)
315169689Skan	cfun->va_list_gpr_size = VA_LIST_MAX_GPR_SIZE;
316169689Skan      else
317169689Skan	cfun->va_list_fpr_size = VA_LIST_MAX_FPR_SIZE;
318169689Skan    }
319169689Skan}
320169689Skan
321169689Skan
322169689Skan/* If AP is a va_list GPR/FPR counter, compute cfun->va_list_{g,f}pr_size.
323169689Skan   If WRITE_P is true, AP has been seen in AP = VAR assignment, if WRITE_P
324169689Skan   is false, AP has been seen in VAR = AP assignment.
325169689Skan   Return true if the AP = VAR (resp. VAR = AP) statement is a recognized
326169689Skan   va_arg operation that doesn't cause the va_list variable to escape
327169689Skan   current function.  */
328169689Skan
329169689Skanstatic bool
330169689Skanva_list_counter_struct_op (struct stdarg_info *si, tree ap, tree var,
331169689Skan			   bool write_p)
332169689Skan{
333169689Skan  tree base;
334169689Skan
335169689Skan  if (TREE_CODE (ap) != COMPONENT_REF
336169689Skan      || TREE_CODE (TREE_OPERAND (ap, 1)) != FIELD_DECL)
337169689Skan    return false;
338169689Skan
339169689Skan  if (TREE_CODE (var) != SSA_NAME
340169689Skan      || bitmap_bit_p (si->va_list_vars, DECL_UID (SSA_NAME_VAR (var))))
341169689Skan    return false;
342169689Skan
343169689Skan  base = get_base_address (ap);
344169689Skan  if (TREE_CODE (base) != VAR_DECL
345169689Skan      || !bitmap_bit_p (si->va_list_vars, DECL_UID (base)))
346169689Skan    return false;
347169689Skan
348169689Skan  if (TREE_OPERAND (ap, 1) == va_list_gpr_counter_field)
349169689Skan    va_list_counter_op (si, ap, var, true, write_p);
350169689Skan  else if (TREE_OPERAND (ap, 1) == va_list_fpr_counter_field)
351169689Skan    va_list_counter_op (si, ap, var, false, write_p);
352169689Skan
353169689Skan  return true;
354169689Skan}
355169689Skan
356169689Skan
357169689Skan/* Check for TEM = AP.  Return true if found and the caller shouldn't
358169689Skan   search for va_list references in the statement.  */
359169689Skan
360169689Skanstatic bool
361169689Skanva_list_ptr_read (struct stdarg_info *si, tree ap, tree tem)
362169689Skan{
363169689Skan  if (TREE_CODE (ap) != VAR_DECL
364169689Skan      || !bitmap_bit_p (si->va_list_vars, DECL_UID (ap)))
365169689Skan    return false;
366169689Skan
367169689Skan  if (TREE_CODE (tem) != SSA_NAME
368169689Skan      || bitmap_bit_p (si->va_list_vars,
369169689Skan		       DECL_UID (SSA_NAME_VAR (tem)))
370169689Skan      || is_global_var (SSA_NAME_VAR (tem)))
371169689Skan    return false;
372169689Skan
373169689Skan  if (si->compute_sizes < 0)
374169689Skan    {
375169689Skan      si->compute_sizes = 0;
376169689Skan      if (si->va_start_count == 1
377169689Skan	  && reachable_at_most_once (si->bb, si->va_start_bb))
378169689Skan	si->compute_sizes = 1;
379169689Skan
380169689Skan      if (dump_file && (dump_flags & TDF_DETAILS))
381169689Skan	fprintf (dump_file,
382169689Skan		 "bb%d will %sbe executed at most once for each va_start "
383169689Skan		 "in bb%d\n", si->bb->index, si->compute_sizes ? "" : "not ",
384169689Skan		 si->va_start_bb->index);
385169689Skan    }
386169689Skan
387169689Skan  /* For void * or char * va_list types, there is just one counter.
388169689Skan     If va_arg is used in a loop, we don't know how many registers need
389169689Skan     saving.  */
390169689Skan  if (! si->compute_sizes)
391169689Skan    return false;
392169689Skan
393169689Skan  if (va_list_counter_bump (si, ap, tem, true) == (unsigned HOST_WIDE_INT) -1)
394169689Skan    return false;
395169689Skan
396169689Skan  /* Note the temporary, as we need to track whether it doesn't escape
397169689Skan     the current function.  */
398169689Skan  bitmap_set_bit (si->va_list_escape_vars,
399169689Skan		  DECL_UID (SSA_NAME_VAR (tem)));
400169689Skan  return true;
401169689Skan}
402169689Skan
403169689Skan
404169689Skan/* Check for:
405169689Skan     tem1 = AP;
406169689Skan     TEM2 = tem1 + CST;
407169689Skan     AP = TEM2;
408169689Skan   sequence and update cfun->va_list_gpr_size.  Return true if found.  */
409169689Skan
410169689Skanstatic bool
411169689Skanva_list_ptr_write (struct stdarg_info *si, tree ap, tree tem2)
412169689Skan{
413169689Skan  unsigned HOST_WIDE_INT increment;
414169689Skan
415169689Skan  if (TREE_CODE (ap) != VAR_DECL
416169689Skan      || !bitmap_bit_p (si->va_list_vars, DECL_UID (ap)))
417169689Skan    return false;
418169689Skan
419169689Skan  if (TREE_CODE (tem2) != SSA_NAME
420169689Skan      || bitmap_bit_p (si->va_list_vars, DECL_UID (SSA_NAME_VAR (tem2))))
421169689Skan    return false;
422169689Skan
423169689Skan  if (si->compute_sizes <= 0)
424169689Skan    return false;
425169689Skan
426169689Skan  increment = va_list_counter_bump (si, ap, tem2, true);
427169689Skan  if (increment + 1 <= 1)
428169689Skan    return false;
429169689Skan
430169689Skan  if (cfun->va_list_gpr_size + increment < VA_LIST_MAX_GPR_SIZE)
431169689Skan    cfun->va_list_gpr_size += increment;
432169689Skan  else
433169689Skan    cfun->va_list_gpr_size = VA_LIST_MAX_GPR_SIZE;
434169689Skan
435169689Skan  return true;
436169689Skan}
437169689Skan
438169689Skan
439169689Skan/* If RHS is X, (some type *) X or X + CST for X a temporary variable
440169689Skan   containing value of some va_list variable plus optionally some constant,
441169689Skan   either set si->va_list_escapes or add LHS to si->va_list_escape_vars,
442169689Skan   depending whether LHS is a function local temporary.  */
443169689Skan
444169689Skanstatic void
445169689Skancheck_va_list_escapes (struct stdarg_info *si, tree lhs, tree rhs)
446169689Skan{
447169689Skan  if (! POINTER_TYPE_P (TREE_TYPE (rhs)))
448169689Skan    return;
449169689Skan
450169689Skan  if ((TREE_CODE (rhs) == PLUS_EXPR
451169689Skan       && TREE_CODE (TREE_OPERAND (rhs, 1)) == INTEGER_CST)
452169689Skan      || TREE_CODE (rhs) == NOP_EXPR
453169689Skan      || TREE_CODE (rhs) == CONVERT_EXPR)
454169689Skan    rhs = TREE_OPERAND (rhs, 0);
455169689Skan
456169689Skan  if (TREE_CODE (rhs) != SSA_NAME
457169689Skan      || ! bitmap_bit_p (si->va_list_escape_vars,
458169689Skan			 DECL_UID (SSA_NAME_VAR (rhs))))
459169689Skan    return;
460169689Skan
461169689Skan  if (TREE_CODE (lhs) != SSA_NAME || is_global_var (SSA_NAME_VAR (lhs)))
462169689Skan    {
463169689Skan      si->va_list_escapes = true;
464169689Skan      return;
465169689Skan    }
466169689Skan
467169689Skan  if (si->compute_sizes < 0)
468169689Skan    {
469169689Skan      si->compute_sizes = 0;
470169689Skan      if (si->va_start_count == 1
471169689Skan	  && reachable_at_most_once (si->bb, si->va_start_bb))
472169689Skan	si->compute_sizes = 1;
473169689Skan
474169689Skan      if (dump_file && (dump_flags & TDF_DETAILS))
475169689Skan	fprintf (dump_file,
476169689Skan		 "bb%d will %sbe executed at most once for each va_start "
477169689Skan		 "in bb%d\n", si->bb->index, si->compute_sizes ? "" : "not ",
478169689Skan		 si->va_start_bb->index);
479169689Skan    }
480169689Skan
481169689Skan  /* For void * or char * va_list types, there is just one counter.
482169689Skan     If va_arg is used in a loop, we don't know how many registers need
483169689Skan     saving.  */
484169689Skan  if (! si->compute_sizes)
485169689Skan    {
486169689Skan      si->va_list_escapes = true;
487169689Skan      return;
488169689Skan    }
489169689Skan
490169689Skan  if (va_list_counter_bump (si, si->va_start_ap, lhs, true)
491169689Skan      == (unsigned HOST_WIDE_INT) -1)
492169689Skan    {
493169689Skan      si->va_list_escapes = true;
494169689Skan      return;
495169689Skan    }
496169689Skan
497169689Skan  bitmap_set_bit (si->va_list_escape_vars,
498169689Skan		  DECL_UID (SSA_NAME_VAR (lhs)));
499169689Skan}
500169689Skan
501169689Skan
502169689Skan/* Check all uses of temporaries from si->va_list_escape_vars bitmap.
503169689Skan   Return true if va_list might be escaping.  */
504169689Skan
505169689Skanstatic bool
506169689Skancheck_all_va_list_escapes (struct stdarg_info *si)
507169689Skan{
508169689Skan  basic_block bb;
509169689Skan
510169689Skan  FOR_EACH_BB (bb)
511169689Skan    {
512169689Skan      block_stmt_iterator i;
513169689Skan
514169689Skan      for (i = bsi_start (bb); !bsi_end_p (i); bsi_next (&i))
515169689Skan	{
516169689Skan	  tree stmt = bsi_stmt (i), use;
517169689Skan	  ssa_op_iter iter;
518169689Skan
519169689Skan	  FOR_EACH_SSA_TREE_OPERAND (use, stmt, iter, SSA_OP_ALL_USES)
520169689Skan	    {
521169689Skan	      if (! bitmap_bit_p (si->va_list_escape_vars,
522169689Skan				  DECL_UID (SSA_NAME_VAR (use))))
523169689Skan		continue;
524169689Skan
525169689Skan	      if (TREE_CODE (stmt) == MODIFY_EXPR)
526169689Skan		{
527169689Skan		  tree lhs = TREE_OPERAND (stmt, 0);
528169689Skan		  tree rhs = TREE_OPERAND (stmt, 1);
529169689Skan
530169689Skan		  if (TREE_CODE (rhs) == WITH_SIZE_EXPR)
531169689Skan		    rhs = TREE_OPERAND (rhs, 0);
532169689Skan
533169689Skan		  /* x = *ap_temp;  */
534169689Skan		  if (TREE_CODE (rhs) == INDIRECT_REF
535169689Skan		      && TREE_OPERAND (rhs, 0) == use
536169689Skan		      && TYPE_SIZE_UNIT (TREE_TYPE (rhs))
537169689Skan		      && host_integerp (TYPE_SIZE_UNIT (TREE_TYPE (rhs)), 1)
538169689Skan		      && si->offsets[SSA_NAME_VERSION (use)] != -1)
539169689Skan		    {
540169689Skan		      unsigned HOST_WIDE_INT gpr_size;
541169689Skan		      tree access_size = TYPE_SIZE_UNIT (TREE_TYPE (rhs));
542169689Skan
543169689Skan		      gpr_size = si->offsets[SSA_NAME_VERSION (use)]
544169689Skan				 + tree_low_cst (access_size, 1);
545169689Skan		      if (gpr_size >= VA_LIST_MAX_GPR_SIZE)
546169689Skan			cfun->va_list_gpr_size = VA_LIST_MAX_GPR_SIZE;
547169689Skan		      else if (gpr_size > cfun->va_list_gpr_size)
548169689Skan			cfun->va_list_gpr_size = gpr_size;
549169689Skan		      continue;
550169689Skan		    }
551169689Skan
552169689Skan		  /* va_arg sequences may contain
553169689Skan		     other_ap_temp = ap_temp;
554169689Skan		     other_ap_temp = ap_temp + constant;
555169689Skan		     other_ap_temp = (some_type *) ap_temp;
556169689Skan		     ap = ap_temp;
557169689Skan		     statements.  */
558169689Skan		  if ((TREE_CODE (rhs) == PLUS_EXPR
559169689Skan		       && TREE_CODE (TREE_OPERAND (rhs, 1)) == INTEGER_CST)
560169689Skan		      || TREE_CODE (rhs) == NOP_EXPR
561169689Skan		      || TREE_CODE (rhs) == CONVERT_EXPR)
562169689Skan		    rhs = TREE_OPERAND (rhs, 0);
563169689Skan
564169689Skan		  if (rhs == use)
565169689Skan		    {
566169689Skan		      if (TREE_CODE (lhs) == SSA_NAME
567169689Skan			  && bitmap_bit_p (si->va_list_escape_vars,
568169689Skan					   DECL_UID (SSA_NAME_VAR (lhs))))
569169689Skan			continue;
570169689Skan
571169689Skan		      if (TREE_CODE (lhs) == VAR_DECL
572169689Skan			  && bitmap_bit_p (si->va_list_vars,
573169689Skan					   DECL_UID (lhs)))
574169689Skan			continue;
575169689Skan		    }
576169689Skan		}
577169689Skan
578169689Skan	      if (dump_file && (dump_flags & TDF_DETAILS))
579169689Skan		{
580169689Skan		  fputs ("va_list escapes in ", dump_file);
581169689Skan		  print_generic_expr (dump_file, stmt, dump_flags);
582169689Skan		  fputc ('\n', dump_file);
583169689Skan		}
584169689Skan	      return true;
585169689Skan	    }
586169689Skan	}
587169689Skan    }
588169689Skan
589169689Skan  return false;
590169689Skan}
591169689Skan
592169689Skan
593169689Skan/* Return true if this optimization pass should be done.
594169689Skan   It makes only sense for stdarg functions.  */
595169689Skan
596169689Skanstatic bool
597169689Skangate_optimize_stdarg (void)
598169689Skan{
599169689Skan  /* This optimization is only for stdarg functions.  */
600169689Skan  return current_function_stdarg != 0;
601169689Skan}
602169689Skan
603169689Skan
604169689Skan/* Entry point to the stdarg optimization pass.  */
605169689Skan
606169689Skanstatic unsigned int
607169689Skanexecute_optimize_stdarg (void)
608169689Skan{
609169689Skan  basic_block bb;
610169689Skan  bool va_list_escapes = false;
611169689Skan  bool va_list_simple_ptr;
612169689Skan  struct stdarg_info si;
613169689Skan  const char *funcname = NULL;
614169689Skan
615169689Skan  cfun->va_list_gpr_size = 0;
616169689Skan  cfun->va_list_fpr_size = 0;
617169689Skan  memset (&si, 0, sizeof (si));
618169689Skan  si.va_list_vars = BITMAP_ALLOC (NULL);
619169689Skan  si.va_list_escape_vars = BITMAP_ALLOC (NULL);
620169689Skan
621169689Skan  if (dump_file)
622169689Skan    funcname = lang_hooks.decl_printable_name (current_function_decl, 2);
623169689Skan
624169689Skan  va_list_simple_ptr = POINTER_TYPE_P (va_list_type_node)
625169689Skan		       && (TREE_TYPE (va_list_type_node) == void_type_node
626169689Skan			   || TREE_TYPE (va_list_type_node) == char_type_node);
627169689Skan  gcc_assert (is_gimple_reg_type (va_list_type_node) == va_list_simple_ptr);
628169689Skan
629169689Skan  FOR_EACH_BB (bb)
630169689Skan    {
631169689Skan      block_stmt_iterator i;
632169689Skan
633169689Skan      for (i = bsi_start (bb); !bsi_end_p (i); bsi_next (&i))
634169689Skan	{
635169689Skan	  tree stmt = bsi_stmt (i);
636169689Skan	  tree call = get_call_expr_in (stmt), callee;
637169689Skan	  tree ap;
638169689Skan
639169689Skan	  if (!call)
640169689Skan	    continue;
641169689Skan
642169689Skan	  callee = get_callee_fndecl (call);
643169689Skan	  if (!callee
644169689Skan	      || DECL_BUILT_IN_CLASS (callee) != BUILT_IN_NORMAL)
645169689Skan	    continue;
646169689Skan
647169689Skan	  switch (DECL_FUNCTION_CODE (callee))
648169689Skan	    {
649169689Skan	    case BUILT_IN_VA_START:
650169689Skan	      break;
651169689Skan	      /* If old style builtins are used, don't optimize anything.  */
652169689Skan	    case BUILT_IN_SAVEREGS:
653169689Skan	    case BUILT_IN_STDARG_START:
654169689Skan	    case BUILT_IN_ARGS_INFO:
655169689Skan	    case BUILT_IN_NEXT_ARG:
656169689Skan	      va_list_escapes = true;
657169689Skan	      continue;
658169689Skan	    default:
659169689Skan	      continue;
660169689Skan	    }
661169689Skan
662169689Skan	  si.va_start_count++;
663169689Skan	  ap = TREE_VALUE (TREE_OPERAND (call, 1));
664169689Skan
665169689Skan	  if (TREE_CODE (ap) != ADDR_EXPR)
666169689Skan	    {
667169689Skan	      va_list_escapes = true;
668169689Skan	      break;
669169689Skan	    }
670169689Skan	  ap = TREE_OPERAND (ap, 0);
671169689Skan	  if (TREE_CODE (ap) == ARRAY_REF)
672169689Skan	    {
673169689Skan	      if (! integer_zerop (TREE_OPERAND (ap, 1)))
674169689Skan	        {
675169689Skan	          va_list_escapes = true;
676169689Skan	          break;
677169689Skan		}
678169689Skan	      ap = TREE_OPERAND (ap, 0);
679169689Skan	    }
680169689Skan	  if (TYPE_MAIN_VARIANT (TREE_TYPE (ap))
681169689Skan	      != TYPE_MAIN_VARIANT (va_list_type_node)
682169689Skan	      || TREE_CODE (ap) != VAR_DECL)
683169689Skan	    {
684169689Skan	      va_list_escapes = true;
685169689Skan	      break;
686169689Skan	    }
687169689Skan
688169689Skan	  if (is_global_var (ap))
689169689Skan	    {
690169689Skan	      va_list_escapes = true;
691169689Skan	      break;
692169689Skan	    }
693169689Skan
694169689Skan	  bitmap_set_bit (si.va_list_vars, DECL_UID (ap));
695169689Skan
696169689Skan	  /* VA_START_BB and VA_START_AP will be only used if there is just
697169689Skan	     one va_start in the function.  */
698169689Skan	  si.va_start_bb = bb;
699169689Skan	  si.va_start_ap = ap;
700169689Skan	}
701169689Skan
702169689Skan      if (va_list_escapes)
703169689Skan	break;
704169689Skan    }
705169689Skan
706169689Skan  /* If there were no va_start uses in the function, there is no need to
707169689Skan     save anything.  */
708169689Skan  if (si.va_start_count == 0)
709169689Skan    goto finish;
710169689Skan
711169689Skan  /* If some va_list arguments weren't local, we can't optimize.  */
712169689Skan  if (va_list_escapes)
713169689Skan    goto finish;
714169689Skan
715169689Skan  /* For void * or char * va_list, something useful can be done only
716169689Skan     if there is just one va_start.  */
717169689Skan  if (va_list_simple_ptr && si.va_start_count > 1)
718169689Skan    {
719169689Skan      va_list_escapes = true;
720169689Skan      goto finish;
721169689Skan    }
722169689Skan
723169689Skan  /* For struct * va_list, if the backend didn't tell us what the counter fields
724169689Skan     are, there is nothing more we can do.  */
725169689Skan  if (!va_list_simple_ptr
726169689Skan      && va_list_gpr_counter_field == NULL_TREE
727169689Skan      && va_list_fpr_counter_field == NULL_TREE)
728169689Skan    {
729169689Skan      va_list_escapes = true;
730169689Skan      goto finish;
731169689Skan    }
732169689Skan
733169689Skan  /* For void * or char * va_list there is just one counter
734169689Skan     (va_list itself).  Use VA_LIST_GPR_SIZE for it.  */
735169689Skan  if (va_list_simple_ptr)
736169689Skan    cfun->va_list_fpr_size = VA_LIST_MAX_FPR_SIZE;
737169689Skan
738169689Skan  calculate_dominance_info (CDI_DOMINATORS);
739169689Skan
740169689Skan  FOR_EACH_BB (bb)
741169689Skan    {
742169689Skan      block_stmt_iterator i;
743169689Skan
744169689Skan      si.compute_sizes = -1;
745169689Skan      si.bb = bb;
746169689Skan
747169689Skan      /* For va_list_simple_ptr, we have to check PHI nodes too.  We treat
748169689Skan	 them as assignments for the purpose of escape analysis.  This is
749169689Skan	 not needed for non-simple va_list because virtual phis don't perform
750169689Skan	 any real data movement.  */
751169689Skan      if (va_list_simple_ptr)
752169689Skan	{
753169689Skan	  tree phi, lhs, rhs;
754169689Skan	  use_operand_p uop;
755169689Skan	  ssa_op_iter soi;
756169689Skan
757169689Skan	  for (phi = phi_nodes (bb); phi; phi = PHI_CHAIN (phi))
758169689Skan	    {
759169689Skan	      lhs = PHI_RESULT (phi);
760169689Skan
761169689Skan	      if (!is_gimple_reg (lhs))
762169689Skan		continue;
763169689Skan
764169689Skan	      FOR_EACH_PHI_ARG (uop, phi, soi, SSA_OP_USE)
765169689Skan		{
766169689Skan		  rhs = USE_FROM_PTR (uop);
767169689Skan		  if (va_list_ptr_read (&si, rhs, lhs))
768169689Skan		    continue;
769169689Skan		  else if (va_list_ptr_write (&si, lhs, rhs))
770169689Skan		    continue;
771169689Skan		  else
772169689Skan		    check_va_list_escapes (&si, lhs, rhs);
773169689Skan
774169689Skan		  if (si.va_list_escapes
775169689Skan		      || walk_tree (&phi, find_va_list_reference,
776169689Skan				    si.va_list_vars, NULL))
777169689Skan		    {
778169689Skan		      if (dump_file && (dump_flags & TDF_DETAILS))
779169689Skan			{
780169689Skan			  fputs ("va_list escapes in ", dump_file);
781169689Skan			  print_generic_expr (dump_file, phi, dump_flags);
782169689Skan			  fputc ('\n', dump_file);
783169689Skan			}
784169689Skan		      va_list_escapes = true;
785169689Skan		    }
786169689Skan		}
787169689Skan	    }
788169689Skan	}
789169689Skan
790169689Skan      for (i = bsi_start (bb);
791169689Skan	   !bsi_end_p (i) && !va_list_escapes;
792169689Skan	   bsi_next (&i))
793169689Skan	{
794169689Skan	  tree stmt = bsi_stmt (i);
795169689Skan	  tree call;
796169689Skan
797169689Skan	  /* Don't look at __builtin_va_{start,end}, they are ok.  */
798169689Skan	  call = get_call_expr_in (stmt);
799169689Skan	  if (call)
800169689Skan	    {
801169689Skan	      tree callee = get_callee_fndecl (call);
802169689Skan
803169689Skan	      if (callee
804169689Skan		  && DECL_BUILT_IN_CLASS (callee) == BUILT_IN_NORMAL
805169689Skan		  && (DECL_FUNCTION_CODE (callee) == BUILT_IN_VA_START
806169689Skan		      || DECL_FUNCTION_CODE (callee) == BUILT_IN_VA_END))
807169689Skan		continue;
808169689Skan	    }
809169689Skan
810169689Skan	  if (TREE_CODE (stmt) == MODIFY_EXPR)
811169689Skan	    {
812169689Skan	      tree lhs = TREE_OPERAND (stmt, 0);
813169689Skan	      tree rhs = TREE_OPERAND (stmt, 1);
814169689Skan
815169689Skan	      if (TREE_CODE (rhs) == WITH_SIZE_EXPR)
816169689Skan		rhs = TREE_OPERAND (rhs, 0);
817169689Skan
818169689Skan	      if (va_list_simple_ptr)
819169689Skan		{
820169689Skan		  /* Check for tem = ap.  */
821169689Skan		  if (va_list_ptr_read (&si, rhs, lhs))
822169689Skan		    continue;
823169689Skan
824169689Skan		  /* Check for the last insn in:
825169689Skan		     tem1 = ap;
826169689Skan		     tem2 = tem1 + CST;
827169689Skan		     ap = tem2;
828169689Skan		     sequence.  */
829169689Skan		  else if (va_list_ptr_write (&si, lhs, rhs))
830169689Skan		    continue;
831169689Skan
832169689Skan		  else
833169689Skan		    check_va_list_escapes (&si, lhs, rhs);
834169689Skan		}
835169689Skan	      else
836169689Skan		{
837169689Skan		  /* Check for ap[0].field = temp.  */
838169689Skan		  if (va_list_counter_struct_op (&si, lhs, rhs, true))
839169689Skan		    continue;
840169689Skan
841169689Skan		  /* Check for temp = ap[0].field.  */
842169689Skan		  else if (va_list_counter_struct_op (&si, rhs, lhs, false))
843169689Skan		    continue;
844169689Skan
845169689Skan		  /* Do any architecture specific checking.  */
846169689Skan		  else if (targetm.stdarg_optimize_hook
847169689Skan			   && targetm.stdarg_optimize_hook (&si, lhs, rhs))
848169689Skan		    continue;
849169689Skan		}
850169689Skan	    }
851169689Skan
852169689Skan	  /* All other uses of va_list are either va_copy (that is not handled
853169689Skan	     in this optimization), taking address of va_list variable or
854169689Skan	     passing va_list to other functions (in that case va_list might
855169689Skan	     escape the function and therefore va_start needs to set it up
856169689Skan	     fully), or some unexpected use of va_list.  None of these should
857169689Skan	     happen in a gimplified VA_ARG_EXPR.  */
858169689Skan	  if (si.va_list_escapes
859169689Skan	      || walk_tree (&stmt, find_va_list_reference,
860169689Skan			    si.va_list_vars, NULL))
861169689Skan	    {
862169689Skan	      if (dump_file && (dump_flags & TDF_DETAILS))
863169689Skan		{
864169689Skan		  fputs ("va_list escapes in ", dump_file);
865169689Skan		  print_generic_expr (dump_file, stmt, dump_flags);
866169689Skan		  fputc ('\n', dump_file);
867169689Skan		}
868169689Skan	      va_list_escapes = true;
869169689Skan	    }
870169689Skan	}
871169689Skan
872169689Skan      if (va_list_escapes)
873169689Skan	break;
874169689Skan    }
875169689Skan
876169689Skan  if (! va_list_escapes
877169689Skan      && va_list_simple_ptr
878169689Skan      && ! bitmap_empty_p (si.va_list_escape_vars)
879169689Skan      && check_all_va_list_escapes (&si))
880169689Skan    va_list_escapes = true;
881169689Skan
882169689Skanfinish:
883169689Skan  if (va_list_escapes)
884169689Skan    {
885169689Skan      cfun->va_list_gpr_size = VA_LIST_MAX_GPR_SIZE;
886169689Skan      cfun->va_list_fpr_size = VA_LIST_MAX_FPR_SIZE;
887169689Skan    }
888169689Skan  BITMAP_FREE (si.va_list_vars);
889169689Skan  BITMAP_FREE (si.va_list_escape_vars);
890169689Skan  free (si.offsets);
891169689Skan  if (dump_file)
892169689Skan    {
893169689Skan      fprintf (dump_file, "%s: va_list escapes %d, needs to save ",
894169689Skan	       funcname, (int) va_list_escapes);
895169689Skan      if (cfun->va_list_gpr_size >= VA_LIST_MAX_GPR_SIZE)
896169689Skan	fputs ("all", dump_file);
897169689Skan      else
898169689Skan	fprintf (dump_file, "%d", cfun->va_list_gpr_size);
899169689Skan      fputs (" GPR units and ", dump_file);
900169689Skan      if (cfun->va_list_fpr_size >= VA_LIST_MAX_FPR_SIZE)
901169689Skan	fputs ("all", dump_file);
902169689Skan      else
903169689Skan	fprintf (dump_file, "%d", cfun->va_list_fpr_size);
904169689Skan      fputs (" FPR units.\n", dump_file);
905169689Skan    }
906169689Skan  return 0;
907169689Skan}
908169689Skan
909169689Skan
910169689Skanstruct tree_opt_pass pass_stdarg =
911169689Skan{
912169689Skan  "stdarg",				/* name */
913169689Skan  gate_optimize_stdarg,			/* gate */
914169689Skan  execute_optimize_stdarg,		/* execute */
915169689Skan  NULL,					/* sub */
916169689Skan  NULL,					/* next */
917169689Skan  0,					/* static_pass_number */
918169689Skan  0,					/* tv_id */
919169689Skan  PROP_cfg | PROP_ssa | PROP_alias,	/* properties_required */
920169689Skan  0,					/* properties_provided */
921169689Skan  0,					/* properties_destroyed */
922169689Skan  0,					/* todo_flags_start */
923169689Skan  TODO_dump_func,			/* todo_flags_finish */
924169689Skan  0					/* letter */
925169689Skan};
926