This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH] stdarg optimization that can handle Alpha too


Hi!

Posting just for archival purposes.
Below is latest stdarg optimization patch, which should handle
even the Alpha's weirdo va_arg.  Only x86_64 and ppc32 backends actually
use the computed information to avoid saving unnecessary registers,
for other backends this needs to be added.  Alpha has been tested just
by make check RUNTESTFLAGS=gcc.dg/tree-ssa/tree-ssa.exp in a cross.
I have moved the stdarg_pass right before SRA, so that at the end
of this pass we could later on gimplify __builtin_va_start ()
and __builtin_va_copy () calls (and optimize out __builtin_va_end ()).

2004-10-08  Jakub Jelinek  <jakub@redhat.com>

	* tree.h (enum tree_index): Add TI_VA_LIST_GPR_COUNTER_FIELD
	and TI_VA_LIST_FPR_COUNTER_FIELD.
	(va_list_gpr_counter_field, va_list_fpr_counter_field): Define.
	* tree-pass.h (pass_stdarg): Add.
	* tree-optimize.c (init_tree_optimization_passes): Add pass_stdarg.
	* tree-stdarg.c: New file.
	* tree-stdarg.h: New file.
	* Makefile.in (OBJS-common): Add tree-stdarg.o.
	(tree-stdarg.o): Add dependencies.
	* function.h (struct function): Add va_list_gpr_size and
	va_list_fpr_size fields.
	* function.c (allocate_struct_function): Initialize them.
	* target.h (struct gcc_target): Add stdarg_optimize_hook.
	* target-def.h (TARGET_STDARG_OPTIMIZE_HOOK): Define.
	(TARGET_INITIALIZER): Add it.

	* config/i386/i386.c (ix86_build_builtin_va_list): Initialize
	va_list_{g,f}pr_counter_field.
	(ix86_setup_incoming_varargs): Don't do anything if reg_save
	area will not be used.  Only save registers that tree-stdarg.c
	detected they need saving.
	(ix86_va_start): Don't set up fields that won't be used.

	* config/rs6000/rs6000.c (rs6000_build_builtin_va_list): Initialize
	va_list_{g,f}pr_counter_field.
	(setup_incoming_varargs): Don't do anything if reg_save
	area will not be used.  Only save registers that tree-stdarg.c
	detected they need saving.
	(rs6000_va_start): Don't set up fields that won't be used.

	* config/alpha/alpha.c: Include tree-flow.h and tree-stdarg.h.
	(alpha_build_builtin_va_list): Initialize va_list_gpr_counter_field.
	(va_list_skip_additions, alpha_stdarg_optimize_hook): New functions.
	(TARGET_STDARG_OPTIMIZE_HOOK): Define.

	* gcc.dg/tree-ssa/stdarg-1.c: New test.
	* gcc.dg/tree-ssa/stdarg-2.c: New test.
	* gcc.dg/tree-ssa/stdarg-3.c: New test.
	* gcc.dg/tree-ssa/stdarg-4.c: New test.
	* gcc.dg/tree-ssa/stdarg-5.c: New test.

--- gcc/tree.h.jj	2004-10-01 09:41:50.000000000 +0200
+++ gcc/tree.h	2004-10-08 12:03:22.000000000 +0200
@@ -2574,6 +2574,8 @@ enum tree_index
   TI_PID_TYPE,
   TI_PTRDIFF_TYPE,
   TI_VA_LIST_TYPE,
+  TI_VA_LIST_GPR_COUNTER_FIELD,
+  TI_VA_LIST_FPR_COUNTER_FIELD,
   TI_BOOLEAN_TYPE,
   TI_FILEPTR_TYPE,
 
@@ -2640,6 +2642,8 @@ extern GTY(()) tree global_trees[TI_MAX]
 #define pid_type_node                   global_trees[TI_PID_TYPE]
 #define ptrdiff_type_node		global_trees[TI_PTRDIFF_TYPE]
 #define va_list_type_node		global_trees[TI_VA_LIST_TYPE]
+#define va_list_gpr_counter_field	global_trees[TI_VA_LIST_GPR_COUNTER_FIELD]
+#define va_list_fpr_counter_field	global_trees[TI_VA_LIST_FPR_COUNTER_FIELD]
 /* The C type `FILE *'.  */
 #define fileptr_type_node		global_trees[TI_FILEPTR_TYPE]
 
--- gcc/tree-pass.h.jj	2004-10-01 09:31:27.000000000 +0200
+++ gcc/tree-pass.h	2004-10-08 12:03:22.000000000 +0200
@@ -147,6 +147,7 @@ extern struct tree_opt_pass pass_profile
 extern struct tree_opt_pass pass_pre_expand;
 extern struct tree_opt_pass pass_lower_vector_ssa;
 extern struct tree_opt_pass pass_fold_builtins;
+extern struct tree_opt_pass pass_stdarg;
 extern struct tree_opt_pass pass_early_warn_uninitialized;
 extern struct tree_opt_pass pass_late_warn_uninitialized;
 extern struct tree_opt_pass pass_warn_function_return;
--- gcc/function.c.jj	2004-10-07 21:27:39.000000000 +0200
+++ gcc/function.c	2004-10-08 12:03:22.000000000 +0200
@@ -3802,6 +3802,10 @@ allocate_struct_function (tree fndecl)
        && TYPE_ARG_TYPES (fntype) != 0
        && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype)))
 	   != void_type_node));
+
+  /* Assume all registers in stdarg functions need to be saved.  */
+  cfun->va_list_gpr_size = VA_LIST_MAX_GPR_SIZE;
+  cfun->va_list_fpr_size = VA_LIST_MAX_FPR_SIZE;
 }
 
 /* Reset cfun, and other non-struct-function variables to defaults as
--- gcc/config/rs6000/rs6000.c.jj	2004-10-08 09:24:27.000000000 +0200
+++ gcc/config/rs6000/rs6000.c	2004-10-08 12:08:51.000000000 +0200
@@ -5402,8 +5402,14 @@ setup_incoming_varargs (CUMULATIVE_ARGS 
     }
 
   set = get_varargs_alias_set ();
-  if (! no_rtl && first_reg_offset < GP_ARG_NUM_REG)
+  if (! no_rtl && first_reg_offset < GP_ARG_NUM_REG
+      && cfun->va_list_gpr_size)
     {
+      int nregs = GP_ARG_NUM_REG - first_reg_offset;
+
+      if (nregs > cfun->va_list_gpr_size)
+	nregs = cfun->va_list_gpr_size;
+
       mem = gen_rtx_MEM (BLKmode,
 			 plus_constant (save_area,
 					first_reg_offset * reg_size)),
@@ -5411,16 +5417,17 @@ setup_incoming_varargs (CUMULATIVE_ARGS 
       set_mem_align (mem, BITS_PER_WORD);
 
       rs6000_move_block_from_reg (GP_ARG_MIN_REG + first_reg_offset, mem,
-				  GP_ARG_NUM_REG - first_reg_offset);
+				  nregs);
     }
 
   /* Save FP registers if needed.  */
   if (DEFAULT_ABI == ABI_V4
       && TARGET_HARD_FLOAT && TARGET_FPRS
       && ! no_rtl
-      && next_cum.fregno <= FP_ARG_V4_MAX_REG)
+      && next_cum.fregno <= FP_ARG_V4_MAX_REG
+      && cfun->va_list_fpr_size)
     {
-      int fregno = next_cum.fregno;
+      int fregno = next_cum.fregno, nregs;
       rtx cr1 = gen_rtx_REG (CCmode, CR1_REGNO);
       rtx lab = gen_label_rtx ();
       int off = (GP_ARG_NUM_REG * reg_size) + ((fregno - FP_ARG_MIN_REG) * 8);
@@ -5434,13 +5441,13 @@ setup_incoming_varargs (CUMULATIVE_ARGS 
 					    gen_rtx_LABEL_REF (VOIDmode, lab),
 					    pc_rtx)));
 
-      while (fregno <= FP_ARG_V4_MAX_REG)
+      for (nregs = 0;
+	   fregno <= FP_ARG_V4_MAX_REG && nregs < cfun->va_list_fpr_size;
+	   fregno++, off += 8, nregs++)
 	{
 	  mem = gen_rtx_MEM (DFmode, plus_constant (save_area, off));
 	  set_mem_alias_set (mem, set);
 	  emit_move_insn (mem, gen_rtx_REG (DFmode, fregno));
-	  fregno++;
-	  off += 8;
 	}
 
       emit_label (lab);
@@ -5475,6 +5482,9 @@ rs6000_build_builtin_va_list (void)
   f_sav = build_decl (FIELD_DECL, get_identifier ("reg_save_area"),
 		      ptr_type_node);
 
+  va_list_gpr_counter_field = f_gpr;
+  va_list_fpr_counter_field = f_fpr;
+
   DECL_FIELD_CONTEXT (f_gpr) = record;
   DECL_FIELD_CONTEXT (f_fpr) = record;
   DECL_FIELD_CONTEXT (f_res) = record;
@@ -5533,15 +5543,28 @@ rs6000_va_start (tree valist, rtx nextar
 	     HOST_WIDE_INT_PRINT_DEC", n_fpr = "HOST_WIDE_INT_PRINT_DEC"\n",
 	     words, n_gpr, n_fpr);
 
-  t = build (MODIFY_EXPR, TREE_TYPE (gpr), gpr,
-	     build_int_cst (NULL_TREE, n_gpr));
-  TREE_SIDE_EFFECTS (t) = 1;
-  expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+  if (cfun->va_list_gpr_size)
+    {
+      t = build (MODIFY_EXPR, TREE_TYPE (gpr), gpr,
+		 build_int_cst (NULL_TREE, n_gpr));
+      TREE_SIDE_EFFECTS (t) = 1;
+      expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+    }
 
-  t = build (MODIFY_EXPR, TREE_TYPE (fpr), fpr,
-	     build_int_cst (NULL_TREE, n_fpr));
-  TREE_SIDE_EFFECTS (t) = 1;
-  expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+  if (cfun->va_list_fpr_size)
+    {
+      t = build (MODIFY_EXPR, TREE_TYPE (fpr), fpr,
+		 build_int_cst (NULL_TREE, n_fpr));
+      TREE_SIDE_EFFECTS (t) = 1;
+      expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+    }
+
+  /* If there were no va_arg invocations, don't set up anything.  */
+  if (!cfun->va_list_gpr_size
+      && !cfun->va_list_fpr_size
+      && n_gpr < GP_ARG_NUM_REG
+      && n_fpr < FP_ARG_V4_MAX_REG)
+    return;
 
   /* Find the overflow area.  */
   t = make_tree (TREE_TYPE (ovf), virtual_incoming_args_rtx);
--- gcc/config/alpha/alpha.c.jj	2004-10-01 09:50:23.000000000 +0200
+++ gcc/config/alpha/alpha.c	2004-10-08 17:01:36.310205555 +0200
@@ -53,6 +53,8 @@ Boston, MA 02111-1307, USA.  */
 #include <splay-tree.h>
 #include "cfglayout.h"
 #include "tree-gimple.h"
+#include "tree-flow.h"
+#include "tree-stdarg.h"
 
 /* Specify which cpu to schedule for.  */
 
@@ -5294,9 +5296,156 @@ alpha_build_builtin_va_list (void)
   TYPE_FIELDS (record) = base;
   layout_type (record);
 
+  va_list_gpr_counter_field = ofs;
   return record;
 }
 
+/* Helper function for alpha_stdarg_optimize_hook.  Skip over casts
+   and constant additions.  */
+
+static tree
+va_list_skip_additions (tree lhs)
+{
+  tree rhs, stmt;
+
+  if (TREE_CODE (lhs) != SSA_NAME)
+    return lhs;
+
+  for (;;)
+    {
+      stmt = SSA_NAME_DEF_STMT (lhs);
+
+      if (TREE_CODE (stmt) == PHI_NODE)
+	return stmt;
+
+      if (TREE_CODE (stmt) != MODIFY_EXPR
+	  || TREE_OPERAND (stmt, 0) != lhs)
+	return lhs;
+
+      rhs = TREE_OPERAND (stmt, 1);
+      if (TREE_CODE (rhs) == WITH_SIZE_EXPR)
+	rhs = TREE_OPERAND (rhs, 0);
+
+      if ((TREE_CODE (rhs) != NOP_EXPR
+	   && TREE_CODE (rhs) != CONVERT_EXPR
+	   && (TREE_CODE (rhs) != PLUS_EXPR
+	       || TREE_CODE (TREE_OPERAND (rhs, 1)) != INTEGER_CST
+	       || !host_integerp (TREE_OPERAND (rhs, 1), 1)))
+	  || TREE_CODE (TREE_OPERAND (rhs, 0)) != SSA_NAME)
+	return rhs;
+
+      lhs = TREE_OPERAND (rhs, 0);
+    }
+}
+
+/* Check if LHS = RHS statement is
+   LHS = *(ap.__base + ap.__offset + cst)
+   or
+   LHS = *(ap.__base
+	   + ((ap.__offset + cst <= 47)
+	      ? ap.__offset + cst - 48 : ap.__offset + cst) + cst2).
+   If the former, indicate that GPR registers are needed,
+   if the latter, indicate that FPR registers are needed.
+   On alpha, cfun->va_list_gpr_size is used as size of the needed
+   regs and cfun->va_list_fpr_size is a bitmask, bit 0 set if
+   GPR registers are needed and bit 1 set if FPR registers are needed.
+   Return true if va_list references should not be scanned for the current
+   statement.  */
+
+static bool
+alpha_stdarg_optimize_hook (struct stdarg_info *si, tree lhs, tree rhs)
+{
+  tree base, offset, arg1, arg2;
+  int offset_arg = 1;
+
+  if (TREE_CODE (rhs) != INDIRECT_REF
+      || TREE_CODE (TREE_OPERAND (rhs, 0)) != SSA_NAME)
+    return false;
+
+  lhs = va_list_skip_additions (TREE_OPERAND (rhs, 0));
+  if (lhs == NULL_TREE
+      || TREE_CODE (lhs) != PLUS_EXPR)
+    return false;
+
+  base = TREE_OPERAND (lhs, 0);
+  if (TREE_CODE (base) == SSA_NAME)
+    base = va_list_skip_additions (base);
+
+  if (TREE_CODE (base) != COMPONENT_REF
+      || TREE_OPERAND (base, 1) != TYPE_FIELDS (va_list_type_node))
+    {
+      base = TREE_OPERAND (lhs, 0);
+      if (TREE_CODE (base) == SSA_NAME)
+	base = va_list_skip_additions (base);
+
+      if (TREE_CODE (base) != COMPONENT_REF
+	  || TREE_OPERAND (base, 1) != TYPE_FIELDS (va_list_type_node))
+	return false;
+
+      offset_arg = 0;
+    }
+
+  base = get_base_address (base);
+  if (TREE_CODE (base) != VAR_DECL
+      || !bitmap_bit_p (si->va_list_vars, var_ann (base)->uid))
+    return false;
+
+  offset = TREE_OPERAND (lhs, offset_arg);
+  if (TREE_CODE (offset) == SSA_NAME)
+    offset = va_list_skip_additions (offset);
+
+  if (TREE_CODE (offset) == PHI_NODE)
+    {
+      HOST_WIDE_INT sub;
+
+      if (PHI_NUM_ARGS (offset) != 2)
+	goto escapes;
+
+      arg1 = va_list_skip_additions (PHI_ARG_DEF (offset, 0));
+      arg2 = va_list_skip_additions (PHI_ARG_DEF (offset, 1));
+      if (TREE_CODE (arg1) != COMPONENT_REF)
+	{
+	  tree tem = arg1;
+
+	  arg1 = arg2;
+	  arg2 = tem;
+	}
+
+      if ((TREE_CODE (arg2) != MINUS_EXPR
+	   && TREE_CODE (arg2) != PLUS_EXPR)
+	  || !host_integerp (TREE_OPERAND (arg2, 1), 0))
+	goto escapes;
+
+      sub = tree_low_cst (TREE_OPERAND (arg2, 1), 0);
+      if (TREE_CODE (arg2) == MINUS_EXPR)
+	sub = -sub;
+      if (sub < -48 || sub > -32)
+	goto escapes;
+
+      arg2 = va_list_skip_additions (TREE_OPERAND (arg2, 0));
+      if (arg1 != arg2
+	  || TREE_CODE (arg1) != COMPONENT_REF
+	  || TREE_OPERAND (arg1, 1) != va_list_gpr_counter_field
+	  || get_base_address (arg1) != base)
+	goto escapes;
+
+      /* Need floating point regs.  */
+      cfun->va_list_fpr_size |= 2;
+    }
+  else if (TREE_CODE (offset) != COMPONENT_REF
+	   || TREE_OPERAND (offset, 1) != va_list_gpr_counter_field
+	   || get_base_address (offset) != base)
+    goto escapes;
+  else
+    /* Need general regs.  */
+    cfun->va_list_fpr_size |= 1;
+  return false;
+
+escapes:
+  si->va_list_escapes = true;
+  return false;
+}
+
 /* Perform any needed actions needed for a function that is receiving a
    variable number of arguments.  */
 
@@ -9435,6 +9584,9 @@ alpha_init_libfuncs (void)
 #undef TARGET_VECTORIZE_MISALIGNED_MEM_OK
 #define TARGET_VECTORIZE_MISALIGNED_MEM_OK alpha_vector_mode_supported_p
 
+#undef TARGET_STDARG_OPTIMIZE_HOOK
+#define TARGET_STDARG_OPTIMIZE_HOOK alpha_stdarg_optimize_hook
+
 struct gcc_target targetm = TARGET_INITIALIZER;
 
 
--- gcc/config/i386/i386.c.jj	2004-10-01 09:31:27.000000000 +0200
+++ gcc/config/i386/i386.c	2004-10-08 12:03:22.000000000 +0200
@@ -3097,6 +3097,9 @@ ix86_build_builtin_va_list (void)
   f_sav = build_decl (FIELD_DECL, get_identifier ("reg_save_area"),
 		      ptr_type_node);
 
+  va_list_gpr_counter_field = f_gpr;
+  va_list_fpr_counter_field = f_fpr;
+
   DECL_FIELD_CONTEXT (f_gpr) = record;
   DECL_FIELD_CONTEXT (f_fpr) = record;
   DECL_FIELD_CONTEXT (f_ovf) = record;
@@ -3136,6 +3139,9 @@ ix86_setup_incoming_varargs (CUMULATIVE_
   if (!TARGET_64BIT)
     return;
 
+  if (! cfun->va_list_gpr_size && ! cfun->va_list_fpr_size)
+    return;
+
   /* Indicate to allocate space on the stack for varargs save area.  */
   ix86_save_varrargs_registers = 1;
 
@@ -3157,7 +3163,10 @@ ix86_setup_incoming_varargs (CUMULATIVE_
 
   set = get_varargs_alias_set ();
 
-  for (i = next_cum.regno; i < ix86_regparm; i++)
+  for (i = next_cum.regno;
+       i < ix86_regparm
+       && i < next_cum.regno + cfun->va_list_gpr_size / UNITS_PER_WORD;
+       i++)
     {
       mem = gen_rtx_MEM (Pmode,
 			 plus_constant (save_area, i * UNITS_PER_WORD));
@@ -3166,7 +3175,7 @@ ix86_setup_incoming_varargs (CUMULATIVE_
 					x86_64_int_parameter_registers[i]));
     }
 
-  if (next_cum.sse_nregs)
+  if (next_cum.sse_nregs && cfun->va_list_fpr_size)
     {
       /* Now emit code to save SSE registers.  The AX parameter contains number
 	 of SSE parameter registers used to call this function.  We use
@@ -3249,15 +3258,21 @@ ix86_va_start (tree valist, rtx nextarg)
     fprintf (stderr, "va_start: words = %d, n_gpr = %d, n_fpr = %d\n",
 	     (int) words, (int) n_gpr, (int) n_fpr);
 
-  t = build (MODIFY_EXPR, TREE_TYPE (gpr), gpr,
-	     build_int_cst (NULL_TREE, n_gpr * 8));
-  TREE_SIDE_EFFECTS (t) = 1;
-  expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+  if (cfun->va_list_gpr_size)
+    {
+      t = build (MODIFY_EXPR, TREE_TYPE (gpr), gpr,
+		 build_int_cst (NULL_TREE, n_gpr * 8));
+      TREE_SIDE_EFFECTS (t) = 1;
+      expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+    }
 
-  t = build (MODIFY_EXPR, TREE_TYPE (fpr), fpr,
-	     build_int_cst (NULL_TREE, n_fpr * 16 + 8*REGPARM_MAX));
-  TREE_SIDE_EFFECTS (t) = 1;
-  expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+  if (cfun->va_list_fpr_size)
+    {
+      t = build (MODIFY_EXPR, TREE_TYPE (fpr), fpr,
+		 build_int_cst (NULL_TREE, n_fpr * 16 + 8*REGPARM_MAX));
+      TREE_SIDE_EFFECTS (t) = 1;
+      expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+    }
 
   /* Find the overflow area.  */
   t = make_tree (TREE_TYPE (ovf), virtual_incoming_args_rtx);
@@ -3268,12 +3283,15 @@ ix86_va_start (tree valist, rtx nextarg)
   TREE_SIDE_EFFECTS (t) = 1;
   expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
 
-  /* Find the register save area.
-     Prologue of the function save it right above stack frame.  */
-  t = make_tree (TREE_TYPE (sav), frame_pointer_rtx);
-  t = build (MODIFY_EXPR, TREE_TYPE (sav), sav, t);
-  TREE_SIDE_EFFECTS (t) = 1;
-  expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+  if (cfun->va_list_gpr_size || cfun->va_list_fpr_size)
+    {
+      /* Find the register save area.
+	 Prologue of the function save it right above stack frame.  */
+      t = make_tree (TREE_TYPE (sav), frame_pointer_rtx);
+      t = build (MODIFY_EXPR, TREE_TYPE (sav), sav, t);
+      TREE_SIDE_EFFECTS (t) = 1;
+      expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+    }
 }
 
 /* Implement va_arg.  */
--- gcc/tree-optimize.c.jj	2004-10-02 10:46:08.000000000 +0200
+++ gcc/tree-optimize.c	2004-10-08 16:30:04.524081197 +0200
@@ -359,6 +359,7 @@ init_tree_optimization_passes (void)
   NEXT_PASS (pass_tail_recursion);
   NEXT_PASS (pass_ch);
   NEXT_PASS (pass_profile);
+  NEXT_PASS (pass_stdarg);
   NEXT_PASS (pass_sra);
   NEXT_PASS (pass_rename_ssa_copies);
   NEXT_PASS (pass_dominator);
--- gcc/tree-stdarg.c.jj	2004-10-08 12:03:22.000000000 +0200
+++ gcc/tree-stdarg.c	2004-10-08 16:19:46.080852399 +0200
@@ -0,0 +1,805 @@
+/* Pass computing data for optimizing stdarg functions.
+   Copyright (C) 2004 Free Software Foundation, Inc.
+   Contributed by Jakub Jelinek <jakub@redhat.com>
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GCC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING.  If not, write to
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "tree.h"
+#include "function.h"
+#include "langhooks.h"
+#include "diagnostic.h"
+#include "target.h"
+#include "tree-flow.h"
+#include "tree-pass.h"
+#include "tree-stdarg.h"
+
+/* A simple pass that attempts to optimize stdarg functions on architectures
+   that need to save register arguments to stack on entry to stdarg functions.
+   If the function doesn't use any va_start macros, no registers need to
+   be saved.  If va_start macros are used, the va_list variables don't escape
+   the function, it is only necessary to save registers that will be used
+   in va_arg macros.  E.g. if va_arg is only used with integral types
+   in the function, floating point registers don't need to be saved, etc.  */
+
+
+/* Return true if basic block VA_ARG_BB is dominated by VA_START_BB and
+   is executed at most as many times as VA_START_BB.  */
+
+static bool
+reachable_at_most_once (basic_block va_arg_bb, basic_block va_start_bb)
+{
+  edge *stack, e;
+  edge_iterator ei;
+  int sp;
+  sbitmap visited;
+  bool ret;
+
+  if (va_arg_bb == va_start_bb)
+    return true;
+
+  if (! dominated_by_p (CDI_DOMINATORS, va_arg_bb, va_start_bb))
+    return false;
+
+  stack = xmalloc ((n_basic_blocks + 1) * sizeof (edge));
+  sp = 0;
+
+  visited = sbitmap_alloc (last_basic_block);
+  sbitmap_zero (visited);
+  ret = true;
+
+  FOR_EACH_EDGE (e, ei, va_arg_bb->preds)
+    stack[sp++] = e;
+
+  while (sp)
+    {
+      basic_block src;
+
+      --sp;
+      e = stack[sp];
+      src = e->src;
+
+      if (e->flags & EDGE_COMPLEX)
+	{
+	  ret = false;
+	  break;
+	}
+
+      if (src == va_start_bb)
+	continue;
+
+      /* va_arg_bb can be executed more times than va_start_bb.  */
+      if (src == va_arg_bb)
+	{
+	  ret = false;
+	  break;
+	}
+
+      gcc_assert (src != ENTRY_BLOCK_PTR);
+
+      if (! TEST_BIT (visited, src->index))
+	{
+	  SET_BIT (visited, src->index);
+	  FOR_EACH_EDGE (e, ei, src->preds)
+	    stack[sp++] = e;
+	}
+    }
+
+  free (stack);
+  sbitmap_free (visited);
+  return ret;
+}
+
+
+/* For statement COUNTER = RHS, if RHS is COUNTER + constant,
+   return constant, otherwise return 0.  GPR_P is true if this
+   is GPR counter.  */
+
+static unsigned HOST_WIDE_INT
+va_list_counter_bump (struct stdarg_info *si, tree counter, tree rhs,
+		      bool gpr_p)
+{
+  tree stmt, lhs, orig_lhs;
+  unsigned HOST_WIDE_INT ret = 0, val, counter_val;
+  unsigned int max_size;
+
+  if (si->offsets == NULL)
+    {
+      unsigned int i;
+
+      si->offsets = xmalloc (num_ssa_names * sizeof (int));
+      for (i = 0; i < num_ssa_names; ++i)
+	si->offsets[i] = -1;
+    }
+
+  counter_val = gpr_p ? cfun->va_list_gpr_size : cfun->va_list_fpr_size;
+  max_size = gpr_p ? VA_LIST_MAX_GPR_SIZE : VA_LIST_MAX_FPR_SIZE;
+  orig_lhs = lhs = rhs;
+  while (lhs)
+    {
+      if (si->offsets[SSA_NAME_VERSION (lhs)] != -1)
+	{
+	  gcc_assert (counter_val >= ((unsigned HOST_WIDE_INT)
+				      si->offsets[SSA_NAME_VERSION (lhs)]));
+
+	  if (counter_val >= max_size)
+	    {
+	      ret = max_size;
+	      break;
+	    }
+
+	  gcc_assert (ret
+		      >= counter_val - si->offsets[SSA_NAME_VERSION (lhs)]);
+	  ret -= counter_val - si->offsets[SSA_NAME_VERSION (lhs)];
+	  break;
+	}
+
+      stmt = SSA_NAME_DEF_STMT (lhs);
+
+      if (TREE_CODE (stmt) != MODIFY_EXPR
+	  || TREE_OPERAND (stmt, 0) != lhs)
+	return 0;
+
+      rhs = TREE_OPERAND (stmt, 1);
+      if (TREE_CODE (rhs) == WITH_SIZE_EXPR)
+	rhs = TREE_OPERAND (rhs, 0);
+
+      if ((TREE_CODE (rhs) == NOP_EXPR
+	   || TREE_CODE (rhs) == CONVERT_EXPR)
+	  && TREE_CODE (TREE_OPERAND (rhs, 0)) == SSA_NAME)
+	{
+	  lhs = TREE_OPERAND (rhs, 0);
+	  continue;
+	}
+
+      if (TREE_CODE (rhs) == PLUS_EXPR
+	  && TREE_CODE (TREE_OPERAND (rhs, 0)) == SSA_NAME
+	  && TREE_CODE (TREE_OPERAND (rhs, 1)) == INTEGER_CST
+	  && host_integerp (TREE_OPERAND (rhs, 1), 1))
+	{
+	  ret += tree_low_cst (TREE_OPERAND (rhs, 1), 1);
+	  lhs = TREE_OPERAND (rhs, 0);
+	  continue;
+	}
+
+      if (TREE_CODE (counter) != TREE_CODE (rhs))
+	return 0;
+
+      if (TREE_CODE (counter) == COMPONENT_REF)
+	{
+	  if (get_base_address (counter) != get_base_address (rhs)
+	      || TREE_CODE (TREE_OPERAND (rhs, 1)) != FIELD_DECL
+	      || TREE_OPERAND (counter, 1) != TREE_OPERAND (rhs, 1))
+	    return 0;
+	}
+      else if (counter != rhs)
+	return 0;
+
+      lhs = NULL;
+    }
+
+  lhs = orig_lhs;
+  val = ret + counter_val;
+  while (lhs)
+    {
+      if (si->offsets[SSA_NAME_VERSION (lhs)] != -1)
+	break;
+
+      if (val >= max_size)
+	si->offsets[SSA_NAME_VERSION (lhs)] = max_size;
+      else
+	si->offsets[SSA_NAME_VERSION (lhs)] = val;
+
+      stmt = SSA_NAME_DEF_STMT (lhs);
+
+      rhs = TREE_OPERAND (stmt, 1);
+      if (TREE_CODE (rhs) == WITH_SIZE_EXPR)
+	rhs = TREE_OPERAND (rhs, 0);
+
+      if ((TREE_CODE (rhs) == NOP_EXPR
+	   || TREE_CODE (rhs) == CONVERT_EXPR)
+	  && TREE_CODE (TREE_OPERAND (rhs, 0)) == SSA_NAME)
+	{
+	  lhs = TREE_OPERAND (rhs, 0);
+	  continue;
+	}
+
+      if (TREE_CODE (rhs) == PLUS_EXPR
+	  && TREE_CODE (TREE_OPERAND (rhs, 0)) == SSA_NAME
+	  && TREE_CODE (TREE_OPERAND (rhs, 1)) == INTEGER_CST
+	  && host_integerp (TREE_OPERAND (rhs, 1), 1))
+	{
+	  val -= tree_low_cst (TREE_OPERAND (rhs, 1), 1);
+	  lhs = TREE_OPERAND (rhs, 0);
+	  continue;
+	}
+
+      lhs = NULL;
+    }
+
+  return ret;
+}
+
+
+/* Called by walk_tree to look for references to va_list variables.  */
+
+static tree
+find_va_list_reference (tree *tp, int *walk_subtrees ATTRIBUTE_UNUSED,
+			void *data)
+{
+  bitmap va_list_vars = (bitmap) data;
+  tree var = *tp;
+
+  if (TREE_CODE (var) == SSA_NAME)
+    var = SSA_NAME_VAR (var);
+
+  if (TREE_CODE (var) == VAR_DECL
+      && bitmap_bit_p (va_list_vars, var_ann (var)->uid))
+    return var;
+
+  return NULL_TREE;
+}
+
+
+/* Helper function of va_list_counter_struct_op.  Compute
+  cfun->va_list_{g,f}pr_size.  AP is a va_list GPR/FPR counter,
+  if WRITE_P is true, seen in AP = VAR, otherwise seen in VAR = AP
+  statement.  GPR_P is true if AP is a GPR counter, false if it is
+  a FPR counter.  */
+
+static void
+va_list_counter_op (struct stdarg_info *si, tree ap, tree var, bool gpr_p,
+		    bool write_p)
+{
+  unsigned HOST_WIDE_INT increment;
+
+  if (si->compute_sizes < 0)
+    {
+      si->compute_sizes = 0;
+      if (si->va_start_count == 1
+	  && reachable_at_most_once (si->bb, si->va_start_bb))
+	si->compute_sizes = 1;
+
+      if (dump_file && (dump_flags & TDF_DETAILS))
+	fprintf (dump_file,
+		 "bb%d will %sbe executed at most once for each va_start "
+		 "in bb%d\n", si->bb->index, si->compute_sizes ? "" : "not ",
+		 si->va_start_bb->index);
+    }
+
+  if (write_p
+      && si->compute_sizes
+      && (increment = va_list_counter_bump (si, ap, var, gpr_p)) != 0)
+    {
+      if (gpr_p && cfun->va_list_gpr_size + increment < VA_LIST_MAX_GPR_SIZE)
+	{
+	  cfun->va_list_gpr_size += increment;
+	  return;
+	}
+
+      if (!gpr_p && cfun->va_list_fpr_size + increment < VA_LIST_MAX_FPR_SIZE)
+	{
+	  cfun->va_list_fpr_size += increment;
+	  return;
+	}
+    }
+
+  if (write_p || !si->compute_sizes)
+    {
+      if (gpr_p)
+	cfun->va_list_gpr_size = VA_LIST_MAX_GPR_SIZE;
+      else
+	cfun->va_list_fpr_size = VA_LIST_MAX_FPR_SIZE;
+    }
+}
+
+
+/* If AP is a va_list GPR/FPR counter, compute cfun->va_list_{g,f}pr_size.
+   If WRITE_P is true, AP has been seen in AP = VAR assignment, if WRITE_P
+   is false, AP has been seen in VAR = AP assignment.
+   Return true if the AP = VAR (resp. VAR = AP) statement is a recognized
+   va_arg operation that doesn't cause the va_list variable to escape
+   current function.  */
+
+static bool
+va_list_counter_struct_op (struct stdarg_info *si, tree ap, tree var,
+			   bool write_p)
+{
+  tree base;
+
+  if (TREE_CODE (ap) != COMPONENT_REF
+      || TREE_CODE (TREE_OPERAND (ap, 1)) != FIELD_DECL)
+    return false;
+
+  if (TREE_CODE (var) != SSA_NAME
+      || bitmap_bit_p (si->va_list_vars, var_ann (SSA_NAME_VAR (var))->uid))
+    return false;
+
+  base = get_base_address (ap);
+  if (TREE_CODE (base) != VAR_DECL
+      || !bitmap_bit_p (si->va_list_vars, var_ann (base)->uid))
+    return false;
+
+  if (TREE_OPERAND (ap, 1) == va_list_gpr_counter_field)
+    va_list_counter_op (si, ap, var, true, write_p);
+  else if (TREE_OPERAND (ap, 1) == va_list_fpr_counter_field)
+    va_list_counter_op (si, ap, var, false, write_p);
+
+  return true;
+}
+
+
+/* Check for TEM = AP.  Return true if found and the caller shouldn't
+   search for va_list references in the statement.  */
+
+static bool
+va_list_ptr_read (struct stdarg_info *si, tree ap, tree tem)
+{
+  if (TREE_CODE (ap) != VAR_DECL
+      || !bitmap_bit_p (si->va_list_vars, var_ann (ap)->uid))
+    return false;
+
+  if (TREE_CODE (tem) != SSA_NAME
+      || bitmap_bit_p (si->va_list_vars,
+		       var_ann (SSA_NAME_VAR (tem))->uid)
+      || is_global_var (SSA_NAME_VAR (tem)))
+    return false;
+
+  if (si->compute_sizes < 0)
+    {
+      si->compute_sizes = 0;
+      if (si->va_start_count == 1
+	  && reachable_at_most_once (si->bb, si->va_start_bb))
+	si->compute_sizes = 1;
+
+      if (dump_file && (dump_flags & TDF_DETAILS))
+	fprintf (dump_file,
+		 "bb%d will %sbe executed at most once for each va_start "
+		 "in bb%d\n", si->bb->index, si->compute_sizes ? "" : "not ",
+		 si->va_start_bb->index);
+    }
+
+  /* For void * or char * va_list types, there is just one counter.
+     If va_arg is used in a loop, we don't know how many registers need
+     saving.  */
+  if (! si->compute_sizes)
+    return false;
+
+  /* Note the temporary, as we need to track whether it doesn't escape
+     the current function.  */
+  bitmap_set_bit (si->va_list_escape_vars,
+		  var_ann (SSA_NAME_VAR (tem))->uid);
+  return true;
+}
+
+
+/* Check for:
+     tem1 = AP;
+     TEM2 = tem1 + CST;
+     AP = TEM2;
+   sequence and update cfun->va_list_gpr_size.  Return true if found.  */
+
+static bool
+va_list_ptr_write (struct stdarg_info *si, tree ap, tree tem2)
+{
+  unsigned HOST_WIDE_INT increment;
+
+  if (TREE_CODE (ap) != VAR_DECL
+      || !bitmap_bit_p (si->va_list_vars, var_ann (ap)->uid))
+    return false;
+
+  if (TREE_CODE (tem2) != SSA_NAME
+      || bitmap_bit_p (si->va_list_vars, var_ann (SSA_NAME_VAR (tem2))->uid))
+    return false;
+
+  if (si->compute_sizes <= 0)
+    return false;
+
+  increment = va_list_counter_bump (si, ap, tem2, true);
+  if (increment == 0)
+    return false;
+
+  if (cfun->va_list_gpr_size + increment < VA_LIST_MAX_GPR_SIZE)
+    cfun->va_list_gpr_size += increment;
+  else
+    cfun->va_list_gpr_size = VA_LIST_MAX_GPR_SIZE;
+
+  return true;
+}
+
+
+/* If RHS is X, (some type *) X or X + CST for X a temporary variable
+   containing value of some va_list variable plus optionally some constant,
+   either set si->va_list_escapes or add LHS to si->va_list_escape_vars,
+   depending whether LHS is a function local temporary.  */
+
+static void
+check_va_list_escapes (struct stdarg_info *si, tree lhs, tree rhs)
+{
+  if (! POINTER_TYPE_P (TREE_TYPE (rhs)))
+    return;
+
+  if ((TREE_CODE (rhs) == PLUS_EXPR
+       && TREE_CODE (TREE_OPERAND (rhs, 1)) == INTEGER_CST)
+      || TREE_CODE (rhs) == NOP_EXPR
+      || TREE_CODE (rhs) == CONVERT_EXPR)
+    rhs = TREE_OPERAND (rhs, 0);
+
+  if (TREE_CODE (rhs) != SSA_NAME
+      || ! bitmap_bit_p (si->va_list_escape_vars,
+			 var_ann (SSA_NAME_VAR (rhs))->uid))
+    return;
+
+  if (TREE_CODE (lhs) != SSA_NAME || is_global_var (SSA_NAME_VAR (lhs)))
+    si->va_list_escapes = true;
+  else
+    bitmap_set_bit (si->va_list_escape_vars,
+		    var_ann (SSA_NAME_VAR (lhs))->uid);
+}
+
+
+/* Check all uses of temporaries from si->va_list_escape_vars bitmap.
+   Return true if va_list might be escaping.  */
+
+static bool
+check_all_va_list_escapes (struct stdarg_info *si)
+{
+  basic_block bb;
+
+  FOR_EACH_BB (bb)
+    {
+      block_stmt_iterator i;
+
+      for (i = bsi_start (bb); !bsi_end_p (i); bsi_next (&i))
+	{
+	  tree stmt = bsi_stmt (i), use;
+	  ssa_op_iter iter;
+
+	  FOR_EACH_SSA_TREE_OPERAND (use, stmt, iter, SSA_OP_ALL_USES)
+	    {
+	      if (! bitmap_bit_p (si->va_list_escape_vars,
+				  var_ann (SSA_NAME_VAR (use))->uid))
+		continue;
+
+	      if (TREE_CODE (stmt) == MODIFY_EXPR)
+		{
+		  tree lhs = TREE_OPERAND (stmt, 0);
+		  tree rhs = TREE_OPERAND (stmt, 1);
+
+		  if (TREE_CODE (rhs) == WITH_SIZE_EXPR)
+		    rhs = TREE_OPERAND (rhs, 0);
+
+		  /* x = *ap_temp;  */
+		  if (TREE_CODE (rhs) == INDIRECT_REF
+		      && TREE_OPERAND (rhs, 0) == use)
+		    continue;
+
+		  /* va_arg sequences may contain
+		     other_ap_temp = ap_temp;
+		     other_ap_temp = ap_temp + constant;
+		     other_ap_temp = (some_type *) ap_temp;
+		     ap = ap_temp;
+		     statements.  */
+		  if ((TREE_CODE (rhs) == PLUS_EXPR
+		       && TREE_CODE (TREE_OPERAND (rhs, 1)) == INTEGER_CST)
+		      || TREE_CODE (rhs) == NOP_EXPR
+		      || TREE_CODE (rhs) == CONVERT_EXPR)
+		    rhs = TREE_OPERAND (rhs, 0);
+
+		  if (rhs == use)
+		    {
+		      if (TREE_CODE (lhs) == SSA_NAME
+			  && bitmap_bit_p (si->va_list_escape_vars,
+					   var_ann (SSA_NAME_VAR (lhs))->uid))
+			continue;
+
+		      if (TREE_CODE (lhs) == VAR_DECL
+			  && bitmap_bit_p (si->va_list_vars,
+					   var_ann (lhs)->uid))
+			continue;
+		    }
+		}
+
+	      if (dump_file && (dump_flags & TDF_DETAILS))
+		{
+		  fputs ("va_list escapes in ", dump_file);
+		  print_generic_expr (dump_file, stmt, dump_flags);
+		  fputc ('\n', dump_file);
+		}
+	      return true;
+	    }
+	}
+    }
+
+  return false;
+}
+
+
+/* Return true if this optimization pass should be done.
+   It makes only sense for stdarg functions.  */
+
+static bool
+gate_optimize_stdarg (void)
+{
+  /* This optimization is only for stdarg functions.  */
+  return current_function_stdarg != 0;
+}
+
+
+/* Entry point to the stdarg optimization pass.  */
+
+static void
+execute_optimize_stdarg (void)
+{
+  basic_block bb;
+  bool va_list_escapes = false;
+  bool va_list_simple_ptr;
+  struct stdarg_info si;
+  const char *funcname = NULL;
+
+  cfun->va_list_gpr_size = 0;
+  cfun->va_list_fpr_size = 0;
+  memset (&si, 0, sizeof (si));
+  si.va_list_vars = BITMAP_XMALLOC ();
+  si.va_list_escape_vars = BITMAP_XMALLOC ();
+
+  if (dump_file)
+    funcname = lang_hooks.decl_printable_name (current_function_decl, 2);
+
+  va_list_simple_ptr = POINTER_TYPE_P (va_list_type_node)
+		       && (TREE_TYPE (va_list_type_node) == void_type_node
+			   || TREE_TYPE (va_list_type_node) == char_type_node);
+
+  FOR_EACH_BB (bb)
+    {
+      block_stmt_iterator i;
+
+      for (i = bsi_start (bb); !bsi_end_p (i); bsi_next (&i))
+	{
+	  tree stmt = bsi_stmt (i);
+	  tree call = get_call_expr_in (stmt), callee;
+	  tree ap;
+
+	  if (!call)
+	    continue;
+
+	  callee = get_callee_fndecl (call);
+	  if (!callee
+	      || DECL_BUILT_IN_CLASS (callee) != BUILT_IN_NORMAL)
+	    continue;
+
+	  switch (DECL_FUNCTION_CODE (callee))
+	    {
+	    case BUILT_IN_VA_START:
+	      break;
+	      /* If old style builtins are used, don't optimize anything.  */
+	    case BUILT_IN_SAVEREGS:
+	    case BUILT_IN_STDARG_START:
+	    case BUILT_IN_ARGS_INFO:
+	    case BUILT_IN_NEXT_ARG:
+	      va_list_escapes = true;
+	      continue;
+	    default:
+	      continue;
+	    }
+
+	  si.va_start_count++;
+	  ap = TREE_VALUE (TREE_OPERAND (call, 1));
+	  if (TREE_CODE (ap) != ADDR_EXPR
+	      || TYPE_MAIN_VARIANT (TREE_TYPE (TREE_OPERAND (ap, 0)))
+		 != TYPE_MAIN_VARIANT (va_list_type_node)
+	      || TREE_CODE (TREE_OPERAND (ap, 0)) != VAR_DECL)
+	    {
+	      va_list_escapes = true;
+	      break;
+	    }
+
+	  ap = TREE_OPERAND (ap, 0);
+	  if (is_global_var (ap))
+	    {
+	      va_list_escapes = true;
+	      break;
+	    }
+
+	  bitmap_set_bit (si.va_list_vars, var_ann (ap)->uid);
+
+	  /* VA_START_BB will be only used if there is just one
+	     va_start in the function.  */
+	  si.va_start_bb = bb;
+	}
+
+      if (va_list_escapes)
+	break;
+    }
+
+  /* If there were no va_start uses in the function, there is no need to
+     save anything.  */
+  if (si.va_start_count == 0)
+    goto finish;
+
+  /* If some va_list arguments weren't local, we can't optimize.  */
+  if (va_list_escapes)
+    goto finish;
+
+  /* For void * or char * va_list, something useful can be done only
+     if there is just one va_start.  */
+  if (va_list_simple_ptr && si.va_start_count > 1)
+    {
+      va_list_escapes = true;
+      goto finish;
+    }
+
+  /* For struct * va_list, if the backend didn't tell us what the counter fields
+     are, there is nothing more we can do.  */
+  if (!va_list_simple_ptr
+      && va_list_gpr_counter_field == NULL_TREE
+      && va_list_fpr_counter_field == NULL_TREE)
+    {
+      va_list_escapes = true;
+      goto finish;
+    }
+
+  /* For void * or char * va_list there is just one counter
+     (va_list itself).  Use VA_LIST_GPR_SIZE for it.  */
+  if (va_list_simple_ptr)
+    cfun->va_list_fpr_size = VA_LIST_MAX_FPR_SIZE;
+
+  FOR_EACH_BB (bb)
+    {
+      block_stmt_iterator i;
+
+      si.compute_sizes = -1;
+      si.bb = bb;
+      for (i = bsi_start (bb);
+	   !bsi_end_p (i) && !va_list_escapes;
+	   bsi_next (&i))
+	{
+	  tree stmt = bsi_stmt (i);
+	  tree call;
+
+	  /* Don't look at __builtin_va_{start,end}, they are ok.  */
+	  call = get_call_expr_in (stmt);
+	  if (call)
+	    {
+	      tree callee = get_callee_fndecl (call);
+
+	      if (callee
+		  && DECL_BUILT_IN_CLASS (callee) == BUILT_IN_NORMAL
+		  && (DECL_FUNCTION_CODE (callee) == BUILT_IN_VA_START
+		      || DECL_FUNCTION_CODE (callee) == BUILT_IN_VA_END))
+		continue;
+	    }
+
+	  if (TREE_CODE (stmt) == MODIFY_EXPR)
+	    {
+	      tree lhs = TREE_OPERAND (stmt, 0);
+	      tree rhs = TREE_OPERAND (stmt, 1);
+
+	      if (TREE_CODE (rhs) == WITH_SIZE_EXPR)
+		rhs = TREE_OPERAND (rhs, 0);
+
+	      if (va_list_simple_ptr)
+		{
+		  /* Check for tem = ap.  */
+		  if (va_list_ptr_read (&si, rhs, lhs))
+		    continue;
+
+		  /* Check for the last insn in:
+		     tem1 = ap;
+		     tem2 = tem1 + CST;
+		     ap = tem2;
+		     sequence.  */
+		  else if (va_list_ptr_write (&si, lhs, rhs))
+		    continue;
+
+		  else
+		    check_va_list_escapes (&si, lhs, rhs);
+		}
+	      else
+		{
+		  /* Check for ap[0].field = temp.  */
+		  if (va_list_counter_struct_op (&si, lhs, rhs, true))
+		    continue;
+
+		  /* Check for temp = ap[0].field.  */
+		  else if (va_list_counter_struct_op (&si, rhs, lhs, false))
+		    continue;
+
+		  /* Do any architecture specific checking.  */
+		  else if (targetm.stdarg_optimize_hook
+			   && (*targetm.stdarg_optimize_hook) (&si, lhs, rhs))
+		    continue;
+		}
+	    }
+
+	  /* All other uses of va_list are either va_copy (that is not handled
+	     in this optimization), taking address of va_list variable or
+	     passing va_list to other functions (in that case va_list might
+	     escape the function and therefore va_start needs to set it up
+	     fully), or some unexpected use of va_list.  None of these should
+	     happen in a gimplified VA_ARG_EXPR.  */
+	  if (si.va_list_escapes
+	      || walk_tree (&stmt, find_va_list_reference,
+			    si.va_list_vars, NULL))
+	    {
+	      if (dump_file && (dump_flags & TDF_DETAILS))
+		{
+		  fputs ("va_list escapes in ", dump_file);
+		  print_generic_expr (dump_file, stmt, dump_flags);
+		  fputc ('\n', dump_file);
+		}
+	      va_list_escapes = true;
+	    }
+	}
+
+      if (va_list_escapes)
+	break;
+    }
+
+  if (! va_list_escapes
+      && va_list_simple_ptr
+      && bitmap_first_set_bit (si.va_list_escape_vars) >= 0
+      && check_all_va_list_escapes (&si))
+    va_list_escapes = true;
+
+finish:
+  if (va_list_escapes)
+    {
+      cfun->va_list_gpr_size = VA_LIST_MAX_GPR_SIZE;
+      cfun->va_list_fpr_size = VA_LIST_MAX_FPR_SIZE;
+    }
+  BITMAP_XFREE (si.va_list_vars);
+  BITMAP_XFREE (si.va_list_escape_vars);
+  free (si.offsets);
+  if (dump_file)
+    {
+      fprintf (dump_file, "%s: va_list escapes %d, needs to save ",
+	       funcname, (int) va_list_escapes);
+      if (cfun->va_list_gpr_size >= VA_LIST_MAX_GPR_SIZE)
+	fputs ("all", dump_file);
+      else
+	fprintf (dump_file, "%d", cfun->va_list_gpr_size);
+      fputs (" GPR units and ", dump_file);
+      if (cfun->va_list_fpr_size >= VA_LIST_MAX_FPR_SIZE)
+	fputs ("all", dump_file);
+      else
+	fprintf (dump_file, "%d", cfun->va_list_fpr_size);
+      fputs (" FPR units.\n", dump_file);
+    }
+}
+
+
+struct tree_opt_pass pass_stdarg =
+{
+  "stdarg",				/* name */
+  gate_optimize_stdarg,			/* gate */
+  execute_optimize_stdarg,		/* execute */
+  NULL,					/* sub */
+  NULL,					/* next */
+  0,					/* static_pass_number */
+  0,					/* tv_id */
+  PROP_cfg | PROP_ssa | PROP_alias,	/* properties_required */
+  0,					/* properties_provided */
+  0,					/* properties_destroyed */
+  0,					/* todo_flags_start */
+  TODO_dump_func,			/* todo_flags_finish */
+  0					/* letter */
+};
--- gcc/Makefile.in.jj	2004-10-07 21:27:37.000000000 +0200
+++ gcc/Makefile.in	2004-10-08 16:20:20.047767960 +0200
@@ -925,7 +925,7 @@ OBJS-common = \
  varasm.o varray.o vec.o version.o vmsdbgout.o xcoffout.o alloc-pool.o	   \
  et-forest.o cfghooks.o bt-load.o pretty-print.o $(GGC) web.o passes.o	   \
  rtl-profile.o tree-profile.o rtlhooks.o cfgexpand.o lambda-mat.o          \
- lambda-trans.o	lambda-code.o tree-loop-linear.o
+ lambda-trans.o	lambda-code.o tree-loop-linear.o tree-stdarg.o
 
 OBJS-md = $(out_object_file)
 OBJS-archive = $(EXTRA_OBJS) $(host_hook_obj) tree-inline.o		   \
@@ -1772,6 +1772,9 @@ tree-loop-linear.o: tree-loop-linear.c $
    errors.h $(GGC_H) $(OPTABS_H) $(TREE_H) $(RTL_H) $(BASIC_BLOCK_H) diagnostic.h \
    $(TREE_FLOW_H) $(TREE_DUMP_H) $(TIMEVAR_H) cfgloop.h tree-pass.h \
    $(TREE_DATA_REF_H) $(SCEV_H)
+tree-stdarg.o: tree-stdarg.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \
+   $(TREE_H) function.h diagnostic.h $(TREE_FLOW_H) tree-pass.h tree-stdarg.h \
+   $(TARGET_H)
 tree-gimple.o : tree-gimple.c $(CONFIG_H) $(SYSTEM_H) $(TREE_H) $(EXPR_H) \
 	$(RTL_H) $(TREE_GIMPLE_H) $(TM_H) coretypes.h bitmap.h $(GGC_H)
 tree-mudflap.o : $(CONFIG_H) errors.h $(SYSTEM_H) $(TREE_H) tree-inline.h \
--- gcc/function.h.jj	2004-10-01 09:31:27.000000000 +0200
+++ gcc/function.h	2004-10-08 12:03:22.000000000 +0200
@@ -428,8 +428,22 @@ struct function GTY(())
 
   /* Nonzero if code to initialize arg_pointer_save_area has been emitted.  */
   unsigned int arg_pointer_save_area_init : 1;
+
+  /* Number of units of general registers that need saving in stdarg
+     function.  What unit is depends on the backend, either it is number
+     of bytes, or it can be number of registers.  */
+  unsigned int va_list_gpr_size : 8;
+
+  /* Number of units of floating point registers that need saving in stdarg
+     function.  */
+  unsigned int va_list_fpr_size : 8;
 };
 
+/* If va_list_[gf]pr_size is set to this, it means we don't know how
+   many units need to be saved.  */
+#define VA_LIST_MAX_GPR_SIZE	255
+#define VA_LIST_MAX_FPR_SIZE	255
+
 /* The function currently being compiled.  */
 extern GTY(()) struct function *cfun;
 
--- gcc/target-def.h.jj	2004-10-01 09:49:42.000000000 +0200
+++ gcc/target-def.h	2004-10-08 16:10:39.703706095 +0200
@@ -381,6 +381,8 @@ Foundation, 59 Temple Place - Suite 330,
 
 #define TARGET_DWARF_CALLING_CONVENTION hook_int_tree_0
 
+#define TARGET_STDARG_OPTIMIZE_HOOK 0
+
 #define TARGET_PROMOTE_FUNCTION_ARGS hook_bool_tree_false
 #define TARGET_PROMOTE_FUNCTION_RETURN hook_bool_tree_false
 #define TARGET_PROMOTE_PROTOTYPES hook_bool_tree_false
@@ -526,6 +528,7 @@ Foundation, 59 Temple Place - Suite 330,
   TARGET_BUILTIN_SETJMP_FRAME_VALUE,		\
   TARGET_MD_ASM_CLOBBERS,			\
   TARGET_DWARF_CALLING_CONVENTION,              \
+  TARGET_STDARG_OPTIMIZE_HOOK,			\
   TARGET_CALLS,					\
   TARGET_CXX,					\
   TARGET_HAVE_NAMED_SECTIONS,			\
--- gcc/tree-stdarg.h.jj	2004-10-08 16:00:46.032023670 +0200
+++ gcc/tree-stdarg.h	2004-10-08 16:01:53.057019692 +0200
@@ -0,0 +1,34 @@
+/* Header for a pass computing data for optimizing stdarg functions.
+   Copyright (C) 2004 Free Software Foundation, Inc.
+   Contributed by Jakub Jelinek <jakub@redhat.com>
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GCC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING.  If not, write to
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+#ifndef GCC_TREE_STDARG_H
+#define GCC_TREE_STDARG_H 1
+
+struct stdarg_info
+{
+  bitmap va_list_vars, va_list_escape_vars;
+  basic_block va_start_bb, bb;
+  int compute_sizes, va_start_count;
+  bool va_list_escapes;
+  int *offsets;
+};
+
+#endif
--- gcc/target.h.jj	2004-10-01 09:49:42.000000000 +0200
+++ gcc/target.h	2004-10-08 16:09:56.169501874 +0200
@@ -50,6 +50,8 @@ Foundation, 59 Temple Place - Suite 330,
 #include "tm.h"
 #include "insn-modes.h"
 
+struct stdarg_info;
+
 struct gcc_target
 {
   /* Functions that output assembler for the target.  */
@@ -495,6 +497,12 @@ struct gcc_target
      the function is being declared as an int.  */
   int (* dwarf_calling_convention) (tree);
 
+  /* Perform architecture specific checking of statements gimplified
+     from VA_ARG_EXPR.  LHS is left hand side of MODIFY_EXPR, RHS
+     is right hand side.  Returns true if the statements doesn't need
+     to be checked for va_list references.  */
+  bool (*stdarg_optimize_hook) (struct stdarg_info *ai, tree lhs, tree rhs);
+
   /* Functions relating to calls - argument passing, returns, etc.  */
   struct calls {
     bool (*promote_function_args) (tree fntype);
--- gcc/testsuite/gcc.dg/tree-ssa/stdarg-4.c.jj	2004-10-08 12:03:22.000000000 +0200
+++ gcc/testsuite/gcc.dg/tree-ssa/stdarg-4.c	2004-10-08 17:07:14.290659042 +0200
@@ -0,0 +1,77 @@
+/* First dg-final line after each function is for architectures that use
+   a struct {...} va_list[1] with separate GPR and FPR counters in the
+   structure.  Second dg-final line is for architectures that use void *
+   or char * va_list.  */
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-stdarg" } */
+
+#include <stdarg.h>
+
+extern void foo (int, va_list);
+extern void bar (int);
+long x;
+double d;
+
+/* Here va_arg can be executed more than once for one va_start.  All GPR
+   registers needs to be saved.  */
+void
+f1 (int i, ...)
+{
+  va_list ap;
+  va_start (ap, i);
+  while (i-- > 0)
+    x = va_arg (ap, long);
+  va_end (ap);
+}
+/* { dg-final { scan-tree-dump "f1: va_list escapes 0, needs to save all GPR units and 0 FPR units" "stdarg" { target x86_64-*-* powerpc-*-linux* } } } */
+/* { dg-final { scan-tree-dump "f1: va_list escapes \[01\], needs to save all GPR units" "stdarg" { target ia64-*-* i?86-*-* } } } */
+/* { dg-final { scan-tree-dump "f1: va_list escapes 0, needs to save all GPR units and 1" "stdarg" { target alpha*-*-linux* } } } */
+
+void
+f2 (int i, ...)
+{
+  va_list ap;
+  va_start (ap, i);
+  while (i-- > 0)
+    d = va_arg (ap, double);
+  va_end (ap);
+}
+/* { dg-final { scan-tree-dump "f2: va_list escapes 0, needs to save 0 GPR units and all FPR units" "stdarg" { target x86_64-*-* powerpc-*-linux* } } } */
+/* { dg-final { scan-tree-dump "f2: va_list escapes \[01\], needs to save all GPR units" "stdarg" { target ia64-*-* i?86-*-* } } } */
+/* { dg-final { scan-tree-dump "f2: va_list escapes 0, needs to save all GPR units and 2" "stdarg" { target alpha*-*-linux* } } } */
+
+/* Here va_arg can be executed at most as many times as va_start.
+   Only one GPR needs to be saved.  */
+void
+f3 (int i, ...)
+{
+  va_list ap;
+  int j = i;
+  while (j-- > 0)
+    {
+      va_start (ap, i);
+      x = va_arg (ap, long);
+      va_end (ap);
+      bar (x);
+    }
+}
+/* { dg-final { scan-tree-dump "f3: va_list escapes 0, needs to save \[148\] GPR units and 0 FPR units" "stdarg" { target x86_64-*-* powerpc-*-linux* } } } */
+/* { dg-final { scan-tree-dump "f3: va_list escapes 0, needs to save \[148\] GPR units" "stdarg" { target ia64-*-* i?86-*-* } } } */
+/* { dg-final { scan-tree-dump "f3: va_list escapes 0, needs to save 8 GPR units and 1" "stdarg" { target alpha*-*-linux* } } } */
+
+void
+f4 (int i, ...)
+{
+  va_list ap;
+  int j = i;
+  while (j-- > 0)
+    {
+      va_start (ap, i);
+      d = va_arg (ap, double);
+      va_end (ap);
+      bar (d + 2.5);
+    }
+}
+/* { dg-final { scan-tree-dump "f4: va_list escapes 0, needs to save 0 GPR units and \[1-9\]\[0-9\]* FPR units" "stdarg" { target x86_64-*-* powerpc-*-linux* } } } */
+/* { dg-final { scan-tree-dump "f4: va_list escapes 0, needs to save \[148\] GPR units" "stdarg" { target ia64-*-* i?86-*-* } } } */
+/* { dg-final { scan-tree-dump "f4: va_list escapes 0, needs to save 8 GPR units and 2" "stdarg" { target alpha*-*-linux* } } } */
--- gcc/testsuite/gcc.dg/tree-ssa/stdarg-3.c.jj	2004-10-08 12:03:22.000000000 +0200
+++ gcc/testsuite/gcc.dg/tree-ssa/stdarg-3.c	2004-10-08 17:04:13.253090080 +0200
@@ -0,0 +1,137 @@
+/* First dg-final line after each function is for architectures that use
+   a struct {...} va_list[1] with separate GPR and FPR counters in the
+   structure.  Second dg-final line is for architectures that use void *
+   or char * va_list.  */
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-stdarg" } */
+
+#include <stdarg.h>
+
+extern void foo (int, va_list);
+extern void bar (int);
+long x;
+va_list gap;
+
+/* If va_list is not local variable, it escapes the function.  */
+void
+f1 (int i, ...)
+{
+  va_start (gap, i);
+  x = va_arg (gap, long);
+  va_end (gap);
+}
+/* { dg-final { scan-tree-dump "f1: va_list escapes 1, needs to save all GPR units and all FPR units" "stdarg" { target x86_64-*-* powerpc-*-linux* alpha*-*-linux* } } } */
+/* { dg-final { scan-tree-dump "f1: va_list escapes 1, needs to save all GPR units" "stdarg" { target ia64-*-* i?86-*-* } } } */
+
+void
+f2 (int i, ...)
+{
+  va_start (gap, i);
+  bar (i);
+  va_end (gap);
+}
+/* { dg-final { scan-tree-dump "f2: va_list escapes 1, needs to save all GPR units and all FPR units" "stdarg" { target x86_64-*-* powerpc-*-linux* alpha*-*-linux* } } } */
+/* { dg-final { scan-tree-dump "f2: va_list escapes 1, needs to save all GPR units" "stdarg" { target ia64-*-* i?86-*-* } } } */
+
+/* tree-stdarg.c only handles va_list variables, not arrays of them or
+   va_list fields embedded in structures.  */
+void
+f3 (int i, ...)
+{
+  va_list aps[10];
+  va_start (aps[4], i);
+  x = va_arg (aps[4], long);
+  va_end (aps[4]);
+}
+/* { dg-final { scan-tree-dump "f3: va_list escapes 1, needs to save all GPR units and all FPR units" "stdarg" { target x86_64-*-* powerpc-*-linux* alpha*-*-linux* } } } */
+/* { dg-final { scan-tree-dump "f3: va_list escapes 1, needs to save all GPR units" "stdarg" { target ia64-*-* i?86-*-* } } } */
+
+void
+f4 (int i, ...)
+{
+  va_list aps[10];
+  va_start (aps[4], i);
+  bar (i);
+  va_end (aps[4]);
+}
+/* { dg-final { scan-tree-dump "f4: va_list escapes 1, needs to save all GPR units and all FPR units" "stdarg" { target x86_64-*-* powerpc-*-linux* alpha*-*-linux* } } } */
+/* { dg-final { scan-tree-dump "f4: va_list escapes 1, needs to save all GPR units" "stdarg" { target ia64-*-* i?86-*-* } } } */
+
+void
+f5 (int i, ...)
+{
+  va_list aps[10];
+  va_start (aps[4], i);
+  foo (i, aps[4]);
+  va_end (aps[4]);
+}
+/* { dg-final { scan-tree-dump "f5: va_list escapes 1, needs to save all GPR units and all FPR units" "stdarg" { target x86_64-*-* powerpc-*-linux* alpha*-*-linux* } } } */
+/* { dg-final { scan-tree-dump "f5: va_list escapes 1, needs to save all GPR units" "stdarg" { target ia64-*-* i?86-*-* } } } */
+
+struct A { int i; va_list g; va_list h[2]; };
+
+void
+f6 (int i, ...)
+{
+  struct A a;
+  va_start (a.g, i);
+  x = va_arg (a.g, long);
+  va_end (a.g);
+}
+/* { dg-final { scan-tree-dump "f6: va_list escapes 1, needs to save all GPR units and all FPR units" "stdarg" { target x86_64-*-* powerpc-*-linux* alpha*-*-linux* } } } */
+/* { dg-final { scan-tree-dump "f6: va_list escapes 1, needs to save all GPR units" "stdarg" { target ia64-*-* i?86-*-* } } } */
+
+void
+f7 (int i, ...)
+{
+  struct A a;
+  va_start (a.g, i);
+  bar (i);
+  va_end (a.g);
+}
+/* { dg-final { scan-tree-dump "f7: va_list escapes 1, needs to save all GPR units and all FPR units" "stdarg" { target x86_64-*-* powerpc-*-linux* alpha*-*-linux* } } } */
+/* { dg-final { scan-tree-dump "f7: va_list escapes 1, needs to save all GPR units" "stdarg" { target ia64-*-* i?86-*-* } } } */
+
+void
+f8 (int i, ...)
+{
+  struct A a;
+  va_start (a.g, i);
+  foo (i, a.g);
+  va_end (a.g);
+}
+/* { dg-final { scan-tree-dump "f8: va_list escapes 1, needs to save all GPR units and all FPR units" "stdarg" { target x86_64-*-* powerpc-*-linux* alpha*-*-linux* } } } */
+/* { dg-final { scan-tree-dump "f8: va_list escapes 1, needs to save all GPR units" "stdarg" { target ia64-*-* i?86-*-* } } } */
+
+void
+f10 (int i, ...)
+{
+  struct A a;
+  va_start (a.h[1], i);
+  x = va_arg (a.h[1], long);
+  va_end (a.h[1]);
+}
+/* { dg-final { scan-tree-dump "f10: va_list escapes 1, needs to save all GPR units and all FPR units" "stdarg" { target x86_64-*-* powerpc-*-linux* alpha*-*-linux* } } } */
+/* { dg-final { scan-tree-dump "f10: va_list escapes 1, needs to save all GPR units" "stdarg" { target ia64-*-* i?86-*-* } } } */
+
+void
+f11 (int i, ...)
+{
+  struct A a;
+  va_start (a.h[1], i);
+  bar (i);
+  va_end (a.h[1]);
+}
+/* { dg-final { scan-tree-dump "f11: va_list escapes 1, needs to save all GPR units and all FPR units" "stdarg" { target x86_64-*-* powerpc-*-linux* alpha*-*-linux* } } } */
+/* { dg-final { scan-tree-dump "f11: va_list escapes 1, needs to save all GPR units" "stdarg" { target ia64-*-* i?86-*-* } } } */
+
+void
+f12 (int i, ...)
+{
+  struct A a;
+  va_start (a.h[1], i);
+  foo (i, a.h[1]);
+  va_end (a.h[1]);
+}
+/* { dg-final { scan-tree-dump "f12: va_list escapes 1, needs to save all GPR units and all FPR units" "stdarg" { target x86_64-*-* powerpc-*-linux* alpha*-*-linux* } } } */
+/* { dg-final { scan-tree-dump "f12: va_list escapes 1, needs to save all GPR units" "stdarg" { target ia64-*-* i?86-*-* } } } */
--- gcc/testsuite/gcc.dg/tree-ssa/stdarg-2.c.jj	2004-10-08 12:03:22.000000000 +0200
+++ gcc/testsuite/gcc.dg/tree-ssa/stdarg-2.c	2004-10-08 16:38:42.865238485 +0200
@@ -0,0 +1,210 @@
+/* First dg-final line after each function is for architectures that use
+   a struct {...} va_list[1] with separate GPR and FPR counters in the
+   structure.  Second dg-final line is for architectures that use void *
+   or char * va_list.  */
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-stdarg" } */
+
+#include <stdarg.h>
+
+extern void foo (int, va_list);
+extern void bar (int);
+long x;
+double d;
+va_list gap;
+va_list *pap;
+
+void
+f1 (int i, ...)
+{
+  va_list ap;
+  va_start (ap, i);
+  va_end (ap);
+}
+/* { dg-final { scan-tree-dump "f1: va_list escapes 0, needs to save 0 GPR units and 0 FPR units" "stdarg" { target x86_64-*-* powerpc-*-linux* alpha*-*-linux* } } } */
+/* { dg-final { scan-tree-dump "f1: va_list escapes 0, needs to save 0 GPR units" "stdarg" { target ia64-*-* i?86-*-* } } } */
+
+void
+f2 (int i, ...)
+{
+  va_list ap;
+  va_start (ap, i);
+  bar (d);
+  x = va_arg (ap, long);
+  bar (x);
+  va_end (ap);
+}
+/* Assume the counters can be number of registers or bytes on 32-bit
+   architecture or bytes on 64-bit architecture.  */
+/* { dg-final { scan-tree-dump "f2: va_list escapes 0, needs to save \[148\] GPR units and 0 FPR units" "stdarg" { target x86_64-*-* powerpc-*-linux* } } } */
+/* { dg-final { scan-tree-dump "f2: va_list escapes 0, needs to save \[148\] GPR units" "stdarg" { target ia64-*-* i?86-*-* } } } */
+/* { dg-final { scan-tree-dump "f2: va_list escapes 0, needs to save 8 GPR units and 1" "stdarg" { target alpha*-*-linux* } } } */
+
+void
+f3 (int i, ...)
+{
+  va_list ap;
+  va_start (ap, i);
+  d = va_arg (ap, double);
+  va_end (ap);
+}
+/* { dg-final { scan-tree-dump "f3: va_list escapes 0, needs to save 0 GPR units and \[1-9\]\[0-9\]* FPR units" "stdarg" { target x86_64-*-* powerpc-*-linux* } } } */
+/* { dg-final { scan-tree-dump "f3: va_list escapes 0, needs to save \[1-9\]\[0-9\]* GPR units" "stdarg" { target ia64-*-* i?86-*-* } } } */
+/* { dg-final { scan-tree-dump "f3: va_list escapes 0, needs to save 8 GPR units and 2" "stdarg" { target alpha*-*-linux* } } } */
+
+void
+f4 (int i, ...)
+{
+  va_list ap;
+  va_start (ap, i);
+  x = va_arg (ap, double);
+  foo (i, ap);
+  va_end (ap);
+}
+/* { dg-final { scan-tree-dump "f4: va_list escapes 1, needs to save all GPR units and all FPR units" "stdarg" { target x86_64-*-* powerpc-*-linux* alpha*-*-linux* } } } */
+/* { dg-final { scan-tree-dump "f4: va_list escapes 1, needs to save all GPR units" "stdarg" { target ia64-*-* i?86-*-* } } } */
+
+void
+f5 (int i, ...)
+{
+  va_list ap;
+  va_start (ap, i);
+  va_copy (gap, ap);
+  bar (i);
+  va_end (ap);
+  va_end (gap);
+}
+/* { dg-final { scan-tree-dump "f5: va_list escapes 1, needs to save all GPR units and all FPR units" "stdarg" { target x86_64-*-* powerpc-*-linux* alpha*-*-linux* } } } */
+/* { dg-final { scan-tree-dump "f5: va_list escapes 1, needs to save all GPR units" "stdarg" { target ia64-*-* i?86-*-* } } } */
+
+void
+f6 (int i, ...)
+{
+  va_list ap;
+  va_start (ap, i);
+  bar (d);
+  va_arg (ap, long);
+  va_arg (ap, long);
+  x = va_arg (ap, long);
+  bar (x);
+  va_end (ap);
+}
+/* { dg-final { scan-tree-dump "f6: va_list escapes 0, needs to save (3|12|24) GPR units and 0 FPR units" "stdarg" { target x86_64-*-* powerpc-*-linux* } } } */
+/* { dg-final { scan-tree-dump "f6: va_list escapes 0, needs to save (3|12|24) GPR units" "stdarg" { target ia64-*-* i?86-*-* } } } */
+/* { dg-final { scan-tree-dump "f6: va_list escapes 0, needs to save 24 GPR units and 1" "stdarg" { target alpha*-*-linux* } } } */
+
+void
+f7 (int i, ...)
+{
+  va_list ap;
+  va_start (ap, i);
+  pap = &ap;
+  bar (6);
+  va_end (ap);
+}
+/* { dg-final { scan-tree-dump "f7: va_list escapes 1, needs to save all GPR units and all FPR units" "stdarg" { target x86_64-*-* powerpc-*-linux* alpha*-*-linux* } } } */
+/* { dg-final { scan-tree-dump "f7: va_list escapes 1, needs to save all GPR units" "stdarg" { target ia64-*-* i?86-*-* } } } */
+
+void
+f8 (int i, ...)
+{
+  va_list ap;
+  va_start (ap, i);
+  pap = &ap;
+  bar (d);
+  x = va_arg (ap, long);
+  bar (x);
+  va_end (ap);
+}
+/* { dg-final { scan-tree-dump "f8: va_list escapes 1, needs to save all GPR units and all FPR units" "stdarg" { target x86_64-*-* powerpc-*-linux* alpha*-*-linux* } } } */
+/* { dg-final { scan-tree-dump "f8: va_list escapes 1, needs to save all GPR units" "stdarg" { target ia64-*-* i?86-*-* } } } */
+
+void
+f9 (int i, ...)
+{
+  va_list ap;
+  va_start (ap, i);
+  __asm __volatile ("" : "=r" (pap) : "0" (&ap));
+  bar (6);
+  va_end (ap);
+}
+/* { dg-final { scan-tree-dump "f9: va_list escapes 1, needs to save all GPR units and all FPR units" "stdarg" { target x86_64-*-* powerpc-*-linux* alpha*-*-linux* } } } */
+/* { dg-final { scan-tree-dump "f9: va_list escapes 1, needs to save all GPR units" "stdarg" { target ia64-*-* i?86-*-* } } } */
+
+void
+f10 (int i, ...)
+{
+  va_list ap;
+  va_start (ap, i);
+  __asm __volatile ("" : "=r" (pap) : "0" (&ap));
+  bar (d);
+  x = va_arg (ap, long);
+  bar (x);
+  va_end (ap);
+}
+/* { dg-final { scan-tree-dump "f10: va_list escapes 1, needs to save all GPR units and all FPR units" "stdarg" { target x86_64-*-* powerpc-*-linux* alpha*-*-linux* } } } */
+/* { dg-final { scan-tree-dump "f10: va_list escapes 1, needs to save all GPR units" "stdarg" { target ia64-*-* i?86-*-* } } } */
+
+void
+f11 (int i, ...)
+{
+  va_list ap;
+  va_start (ap, i);
+  bar (d);
+  x = va_arg (ap, long);
+  x += va_arg (ap, long);
+  x += va_arg (ap, long);
+  bar (x);
+  va_end (ap);
+}
+/* { dg-final { scan-tree-dump "f11: va_list escapes 0, needs to save (3|12|24) GPR units and 0 FPR units" "stdarg" { target x86_64-*-* powerpc-*-linux* } } } */
+/* { dg-final { scan-tree-dump "f11: va_list escapes 0, needs to save (3|12|24) GPR units" "stdarg" { target ia64-*-* i?86-*-* } } } */
+/* { dg-final { scan-tree-dump "f11: va_list escapes 0, needs to save 24 GPR units and 1" "stdarg" { target alpha*-*-linux* } } } */
+
+void
+f12 (int i, ...)
+{
+  va_list ap;
+  va_start (ap, i);
+  bar (d);
+  va_arg (ap, double);
+  va_arg (ap, double);
+  x = va_arg (ap, double);
+  bar (x);
+  va_end (ap);
+}
+/* { dg-final { scan-tree-dump "f12: va_list escapes 0, needs to save 0 GPR units and \[1-9\]\[0-9\]* FPR units" "stdarg" { target x86_64-*-* powerpc-*-linux* } } } */
+/* { dg-final { scan-tree-dump "f12: va_list escapes 0, needs to save \[1-9]\[0-9\]* GPR units" "stdarg" { target ia64-*-* i?86-*-* } } } */
+/* { dg-final { scan-tree-dump "f12: va_list escapes 0, needs to save 24 GPR units and 2" "stdarg" { target alpha*-*-linux* } } } */
+
+void
+f13 (int i, ...)
+{
+  va_list ap;
+  va_start (ap, i);
+  bar (d);
+  x = va_arg (ap, double);
+  x += va_arg (ap, double);
+  x += va_arg (ap, double);
+  bar (x);
+  va_end (ap);
+}
+/* { dg-final { scan-tree-dump "f13: va_list escapes 0, needs to save 0 GPR units and \[1-9\]\[0-9\]* FPR units" "stdarg" { target x86_64-*-* powerpc-*-linux* } } } */
+/* { dg-final { scan-tree-dump "f13: va_list escapes 0, needs to save \[1-9]\[0-9\]* GPR units" "stdarg" { target ia64-*-* i?86-*-* } } } */
+/* { dg-final { scan-tree-dump "f13: va_list escapes 0, needs to save 24 GPR units and 2" "stdarg" { target alpha*-*-linux* } } } */
+
+void
+f14 (int i, ...)
+{
+  va_list ap;
+  va_start (ap, i);
+  bar (d);
+  x = va_arg (ap, double);
+  x += va_arg (ap, long);
+  x += va_arg (ap, double);
+  bar (x);
+  va_end (ap);
+}
+/* { dg-final { scan-tree-dump "f14: va_list escapes 0, needs to save \[148\] GPR units and \[1-9\]\[0-9\]* FPR units" "stdarg" { target x86_64-*-* powerpc-*-linux* } } } */
+/* { dg-final { scan-tree-dump "f14: va_list escapes 0, needs to save \[1-9]\[0-9\]* GPR units" "stdarg" { target ia64-*-* i?86-*-* } } } */
+/* { dg-final { scan-tree-dump "f14: va_list escapes 0, needs to save 24 GPR units and 3" "stdarg" { target alpha*-*-linux* } } } */
--- gcc/testsuite/gcc.dg/tree-ssa/stdarg-1.c.jj	2004-10-08 12:03:22.000000000 +0200
+++ gcc/testsuite/gcc.dg/tree-ssa/stdarg-1.c	2004-10-08 12:03:22.000000000 +0200
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-stdarg" } */
+
+#include <stdarg.h>
+
+/* This can be handled on all arches.  If there is no va_start, registers don't need
+   to be saved.  */
+void
+f1 (int i, ...)
+{
+}
+/* { dg-final { scan-tree-dump "f1: va_list escapes 0, needs to save 0 GPR units and 0 FPR units" "stdarg" } } */
--- gcc/testsuite/gcc.dg/tree-ssa/stdarg-5.c.jj	2004-10-08 12:03:22.000000000 +0200
+++ gcc/testsuite/gcc.dg/tree-ssa/stdarg-5.c	2004-10-08 17:03:13.565782579 +0200
@@ -0,0 +1,111 @@
+/* This test has architecture specific function passing details.  */
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-stdarg" } */
+
+#include <stdarg.h>
+
+extern void foo (int, va_list);
+extern void bar (int);
+struct S1 { int i; double d; int j; double e; } s1;
+struct S2 { double d; long i; } s2;
+int y;
+_Complex int ci;
+_Complex double cd;
+
+/* Here va_arg can be executed more than once for one va_start.  */
+void
+f1 (int i, ...)
+{
+  va_list ap;
+  va_start (ap, i);
+  while (i-- > 0)
+    s1 = va_arg (ap, struct S1);
+  va_end (ap);
+}
+/* { dg-final { scan-tree-dump "f1: va_list escapes 0, needs to save 0 GPR units and 0 FPR units" "stdarg" { target x86_64-*-* } } } */
+/* { dg-final { scan-tree-dump "f1: va_list escapes 0, needs to save all GPR units and 1" "stdarg" { target alpha*-*-linux* } } } */
+
+void
+f2 (int i, ...)
+{
+  va_list ap;
+  va_start (ap, i);
+  while (i-- > 0)
+    s2 = va_arg (ap, struct S2);
+  va_end (ap);
+}
+/* { dg-final { scan-tree-dump "f2: va_list escapes 0, needs to save all GPR units and all FPR units" "stdarg" { target x86_64-*-* } } } */
+/* { dg-final { scan-tree-dump "f2: va_list escapes 0, needs to save all GPR units and 1" "stdarg" { target alpha*-*-linux* } } } */
+
+/* Here va_arg can be executed at most as many times as va_start.  */
+void
+f3 (int i, ...)
+{
+  va_list ap;
+  int j = i;
+  while (j-- > 0)
+    {
+      va_start (ap, i);
+      s1 = va_arg (ap, struct S1);
+      va_end (ap);
+      bar (s1.i);
+    }
+}
+/* { dg-final { scan-tree-dump "f3: va_list escapes 0, needs to save 0 GPR units and 0 FPR units" "stdarg" { target x86_64-*-* } } } */
+/* { dg-final { scan-tree-dump "f3: va_list escapes 0, needs to save 32 GPR units and 1" "stdarg" { target alpha*-*-linux* } } } */
+
+void
+f4 (int i, ...)
+{
+  va_list ap;
+  int j = i;
+  while (j-- > 0)
+    {
+      va_start (ap, i);
+      s2 = va_arg (ap, struct S2);
+      y = va_arg (ap, int);
+      va_end (ap);
+      bar (s2.i);
+    }
+}
+/* { dg-final { scan-tree-dump "f4: va_list escapes 0, needs to save 16 GPR units and 16 FPR units" "stdarg" { target x86_64-*-* } } } */
+/* { dg-final { scan-tree-dump "f4: va_list escapes 0, needs to save 24 GPR units and 1" "stdarg" { target alpha*-*-linux* } } } */
+
+void
+f5 (int i, ...)
+{
+  va_list ap;
+  va_start (ap, i);
+  ci = va_arg (ap, _Complex int);
+  ci += va_arg (ap, _Complex int);
+  va_end (ap);
+  bar (__real__ ci + __imag__ ci);
+}
+/* { dg-final { scan-tree-dump "f5: va_list escapes 0, needs to save 16 GPR units and 0 FPR units" "stdarg" { target x86_64-*-* } } } */
+/* { dg-final { scan-tree-dump "f5: va_list escapes 0, needs to save 32 GPR units and 1" "stdarg" { target alpha*-*-linux* } } } */
+
+void
+f6 (int i, ...)
+{
+  va_list ap;
+  va_start (ap, i);
+  ci = va_arg (ap, _Complex int);
+  cd = va_arg (ap, _Complex double);
+  va_end (ap);
+  bar (__real__ ci + __imag__ cd);
+}
+/* { dg-final { scan-tree-dump "f6: va_list escapes 0, needs to save 8 GPR units and 32 FPR units" "stdarg" { target x86_64-*-* } } } */
+/* { dg-final { scan-tree-dump "f6: va_list escapes 0, needs to save 32 GPR units and 3" "stdarg" { target alpha*-*-linux* } } } */
+
+void
+f7 (int i, ...)
+{
+  va_list ap;
+  va_start (ap, i);
+  cd = va_arg (ap, _Complex double);
+  cd += va_arg (ap, _Complex double);
+  va_end (ap);
+  bar (__real__ cd + __imag__ cd);
+}
+/* { dg-final { scan-tree-dump "f7: va_list escapes 0, needs to save 0 GPR units and 64 FPR units" "stdarg" { target x86_64-*-* } } } */
+/* { dg-final { scan-tree-dump "f7: va_list escapes 0, needs to save 32 GPR units and 2" "stdarg" { target alpha*-*-linux* } } } */


	Jakub


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]