On 19-02-15 14:03, Richard Biener wrote:
On Thu, 19 Feb 2015, Tom de Vries wrote:

On 19-02-15 11:29, Tom de Vries wrote:
Hi,

I'm posting this patch series for stage1:
- 0001-Disable-lang_hooks.gimplify_expr-in-free_lang_data.patch
- 0002-Add-gimple_find_sub_bbs.patch
- 0003-Factor-optimize_va_list_gpr_fpr_size-out-of-pass_std.patch
- 0004-Handle-internal_fn-in-operand_equal_p.patch
- 0005-Postpone-expanding-va_arg-until-pass_stdarg.patch

The patch series - based on Michael's initial patch - postpones expanding
va_arg
until pass_stdarg, which makes pass_stdarg more robust.

Bootstrapped and reg-tested on x86_64 using all languages, with unix/ and
unix/-m32 testing.

I'll post the patches in reply to this email.


This patch postpones expanding va_arg until pass_stdarg.

We add a new internal function IFN_VA_ARG. During gimplification, we map
VA_ARG_EXPR onto a CALL_EXPR with IFN_VA_ARG, which is then gimplified in to a
gimple_call. At pass_stdarg, we expand the IFN_VA_ARG gimple_call into actual
code.

There are a few implementation details worth mentioning:
- passing the type beyond gimplification is done by adding a NULL pointer-
   to-type to IFN_VA_ARG.
- there is special handling for IFN_VA_ARG that would be most suited to be
   placed in gimplify_va_arg_expr. However, that function lacks the scope for
   the special handling, so it's placed awkwardly in gimplify_modify_expr.
- there's special handling in case the va_arg type is variable-sized.
   gimplify_modify_expr adds a WITH_SIZE_EXPR to the CALL_EXPR IFN_VA_ARG for
   variable-sized types. However, this is gimplified into a gimple_call which
   does not have the possibility to wrap it's result in a WITH_SIZE_EXPR. So
   we're adding the size argument of the WITH_SIZE_EXPR as argument to
   IFN_VA_ARG, and at expansion in pass_stdarg, wrap the result of the
   gimplification of IFN_VA_ARG in a WITH_SIZE_EXPR, such that the subsequent
   gimplify_assign will generate a memcpy if necessary.
- when gimplifying the va_arg argument ap, it may not be addressable. So
   gimplification will generate a copy ap.1 = ap, and use &ap.1 as argument.
   This means that we have to copy back the ap.1 value to ap after IFN_VA_ARG.
   The copy is classified by the va_list_gpr/fpr_size optimization as an
   escape,  so it inhibits optimization. The tree-ssa/stdarg-2.c f15 update is
   because of that.

OK for stage1?

Looks mostly good, though it looks like with -O0 this doesn't delay
lowering of va-arg and thus won't "fix" offloading.  Can you instead
introduce a PROP_gimple_lva, provide it by the stdarg pass and add
a pass_lower_vaarg somewhere where pass_lower_complex_O0 is run
that runs of !PROP_gimple_lva (and also provides it), and require
PROP_gimple_lva by pass_expand?  (just look for PROP_gimple_lcx for
the complex stuff to get an idea what needs to be touched)


Updated according to comments.

Furthermore (having updated the patch series to recent trunk), I'm dropping the ACCEL_COMPILER bit in pass_stdarg::gate. AFAIU the comment there relates to this patch.

Retested as before.

OK for stage1?

Btw, I'm wondering if as run-time optimization we can tentatively set PROP_gimple_lva at the start of the gimple pass, and unset it in gimplify_va_arg_expr. That way we would avoid the loop in expand_ifn_va_arg_1 (over all bbs and gimples) in functions without va_arg.

Thanks,
- Tom
2015-02-17  Tom de Vries  <t...@codesourcery.com>
	    Michael Matz  <m...@suse.de>

	* gimple-iterator.c (update_modified_stmts): Remove static.
	* gimple-iterator.h (update_modified_stmts): Declare.
	* gimplify.c (gimplify_modify_expr): Handle IFN_VA_ARG.
	(gimplify_va_arg_internal): New function.
	(gimplify_va_arg_expr): Use IFN_VA_ARG.
	* gimplify.h (gimplify_va_arg_internal): Declare.
	* internal-fn.c (expand_VA_ARG): New unreachable function.
	* internal-fn.def (VA_ARG): New DEF_INTERNAL_FN.
	* tree-stdarg.c (gimple_call_ifn_va_arg_p, expand_ifn_va_arg_1)
	(expand_ifn_va_arg): New function.
	(pass_data_stdarg): Add PROP_gimple_lva to properties_provided field.
	(pass_stdarg::execute): Call expand_ifn_va_arg.
	(pass_data_lower_vaarg): New pass_data.
	(pass_lower_vaarg): New gimple_opt_pass.
	(pass_lower_vaarg::gate, pass_lower_vaarg::execute)
	(make_pass_lower_vaarg): New function.
	* cfgexpand.c (pass_data_expand): Add PROP_gimple_lva to
	properties_required field.
	* passes.def (all_passes): Add pass_lower_vaarg.
	* tree-pass.h (PROP_gimple_lva): Add define.
	(make_pass_lower_vaarg): Declare.

	* gcc.dg/tree-ssa/stdarg-2.c: Change f15 scan-tree-dump for target
	x86_64-*-*.
---
 gcc/cfgexpand.c                          |   3 +-
 gcc/gimple-iterator.c                    |   2 +-
 gcc/gimple-iterator.h                    |   1 +
 gcc/gimplify.c                           | 111 ++++++++++++++-----
 gcc/gimplify.h                           |   2 +
 gcc/internal-fn.c                        |   9 ++
 gcc/internal-fn.def                      |   1 +
 gcc/passes.def                           |   1 +
 gcc/testsuite/gcc.dg/tree-ssa/stdarg-2.c |   4 +-
 gcc/tree-pass.h                          |   2 +
 gcc/tree-stdarg.c                        | 184 ++++++++++++++++++++++++++++---
 11 files changed, 273 insertions(+), 47 deletions(-)

diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c
index 7dfe1f6..af5a652 100644
--- a/gcc/cfgexpand.c
+++ b/gcc/cfgexpand.c
@@ -5860,7 +5860,8 @@ const pass_data pass_data_expand =
   TV_EXPAND, /* tv_id */
   ( PROP_ssa | PROP_gimple_leh | PROP_cfg
     | PROP_gimple_lcx
-    | PROP_gimple_lvec ), /* properties_required */
+    | PROP_gimple_lvec
+    | PROP_gimple_lva), /* properties_required */
   PROP_rtl, /* properties_provided */
   ( PROP_ssa | PROP_trees ), /* properties_destroyed */
   0, /* todo_flags_start */
diff --git a/gcc/gimple-iterator.c b/gcc/gimple-iterator.c
index a322390..df29123 100644
--- a/gcc/gimple-iterator.c
+++ b/gcc/gimple-iterator.c
@@ -72,7 +72,7 @@ update_modified_stmt (gimple stmt)
 
 /* Mark the statements in SEQ as modified, and update them.  */
 
-static void
+void
 update_modified_stmts (gimple_seq seq)
 {
   gimple_stmt_iterator gsi;
diff --git a/gcc/gimple-iterator.h b/gcc/gimple-iterator.h
index 6be88dd..ab5759e 100644
--- a/gcc/gimple-iterator.h
+++ b/gcc/gimple-iterator.h
@@ -90,6 +90,7 @@ extern basic_block gsi_insert_seq_on_edge_immediate (edge, gimple_seq);
 extern void gsi_commit_edge_inserts (void);
 extern void gsi_commit_one_edge_insert (edge, basic_block *);
 extern gphi_iterator gsi_start_phis (basic_block);
+extern void update_modified_stmts (gimple_seq);
 
 /* Return a new iterator pointing to GIMPLE_SEQ's first statement.  */
 
diff --git a/gcc/gimplify.c b/gcc/gimplify.c
index 1353ada..8ac6a35 100644
--- a/gcc/gimplify.c
+++ b/gcc/gimplify.c
@@ -4564,6 +4564,7 @@ gimplify_modify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p,
   gimple assign;
   location_t loc = EXPR_LOCATION (*expr_p);
   gimple_stmt_iterator gsi;
+  tree ap = NULL_TREE, ap_copy = NULL_TREE;
 
   gcc_assert (TREE_CODE (*expr_p) == MODIFY_EXPR
 	      || TREE_CODE (*expr_p) == INIT_EXPR);
@@ -4640,6 +4641,27 @@ gimplify_modify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p,
   if (ret == GS_ERROR)
     return ret;
 
+  /* In case of va_arg internal fn wrappped in a WITH_SIZE_EXPR, add the type
+     size as argument to the the call.  */
+  if (TREE_CODE (*from_p) == WITH_SIZE_EXPR)
+    {
+      tree call = TREE_OPERAND (*from_p, 0);
+      tree vlasize = TREE_OPERAND (*from_p, 1);
+
+      if (TREE_CODE (call) == CALL_EXPR
+	  && CALL_EXPR_IFN (call) == IFN_VA_ARG)
+	{
+	  tree type = TREE_TYPE (call);
+	  tree ap = CALL_EXPR_ARG (call, 0);
+	  tree tag = CALL_EXPR_ARG (call, 1);
+	  tree newcall = build_call_expr_internal_loc (EXPR_LOCATION (call),
+						       IFN_VA_ARG, type, 3, ap,
+						       tag, vlasize);
+	  tree *call_p = &(TREE_OPERAND (*from_p, 0));
+	  *call_p = newcall;
+	}
+    }
+
   /* Now see if the above changed *from_p to something we handle specially.  */
   ret = gimplify_modify_expr_rhs (expr_p, from_p, to_p, pre_p, post_p,
 				  want_value);
@@ -4703,12 +4725,16 @@ gimplify_modify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p,
 	  enum internal_fn ifn = CALL_EXPR_IFN (*from_p);
 	  auto_vec<tree> vargs (nargs);
 
+	  if (ifn == IFN_VA_ARG)
+	    ap = unshare_expr (CALL_EXPR_ARG (*from_p, 0));
 	  for (i = 0; i < nargs; i++)
 	    {
 	      gimplify_arg (&CALL_EXPR_ARG (*from_p, i), pre_p,
 			    EXPR_LOCATION (*from_p));
 	      vargs.quick_push (CALL_EXPR_ARG (*from_p, i));
 	    }
+	  if (ifn == IFN_VA_ARG)
+	    ap_copy = CALL_EXPR_ARG (*from_p, 0);
 	  call_stmt = gimple_build_call_internal_vec (ifn, vargs);
 	  gimple_set_location (call_stmt, EXPR_LOCATION (*expr_p));
 	}
@@ -4753,6 +4779,17 @@ gimplify_modify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p,
   gsi = gsi_last (*pre_p);
   maybe_fold_stmt (&gsi);
 
+  /* When gimplifying the &ap argument of va_arg, we might end up with
+       ap.1 = ap
+       va_arg (&ap.1, 0B)
+     We need to assign ap.1 back to ap, otherwise va_arg has no effect on
+     ap.  */
+  if (ap != NULL_TREE
+      && TREE_CODE (ap) == ADDR_EXPR
+      && TREE_CODE (ap_copy) == ADDR_EXPR
+      && TREE_OPERAND (ap, 0) != TREE_OPERAND (ap_copy, 0))
+    gimplify_assign (TREE_OPERAND (ap, 0), TREE_OPERAND (ap_copy, 0), pre_p);
+
   if (want_value)
     {
       *expr_p = TREE_THIS_VOLATILE (*to_p) ? *from_p : unshare_expr (*to_p);
@@ -9290,16 +9327,53 @@ dummy_object (tree type)
   return build2 (MEM_REF, type, t, t);
 }
 
+/* Call the target expander for evaluating a va_arg call of VALIST
+   and TYPE.  */
+
+tree
+gimplify_va_arg_internal (tree valist, tree type, location_t loc,
+			  gimple_seq *pre_p, gimple_seq *post_p)
+{
+  tree have_va_type = TREE_TYPE (valist);
+  tree cano_type = targetm.canonical_va_list_type (have_va_type);
+
+  if (cano_type != NULL_TREE)
+    have_va_type = cano_type;
+
+  /* Make it easier for the backends by protecting the valist argument
+     from multiple evaluations.  */
+  if (TREE_CODE (have_va_type) == ARRAY_TYPE)
+    {
+      /* For this case, the backends will be expecting a pointer to
+	 TREE_TYPE (abi), but it's possible we've
+	 actually been given an array (an actual TARGET_FN_ABI_VA_LIST).
+	 So fix it.  */
+      if (TREE_CODE (TREE_TYPE (valist)) == ARRAY_TYPE)
+	{
+	  tree p1 = build_pointer_type (TREE_TYPE (have_va_type));
+	  valist = fold_convert_loc (loc, p1,
+				     build_fold_addr_expr_loc (loc, valist));
+	}
+
+      gimplify_expr (&valist, pre_p, post_p, is_gimple_val, fb_rvalue);
+    }
+  else
+    gimplify_expr (&valist, pre_p, post_p, is_gimple_min_lval, fb_lvalue);
+
+  return targetm.gimplify_va_arg_expr (valist, type, pre_p, post_p);
+}
+
 /* Gimplify __builtin_va_arg, aka VA_ARG_EXPR, which is not really a
    builtin function, but a very special sort of operator.  */
 
 enum gimplify_status
-gimplify_va_arg_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
+gimplify_va_arg_expr (tree *expr_p, gimple_seq *pre_p,
+		      gimple_seq *post_p ATTRIBUTE_UNUSED)
 {
   tree promoted_type, have_va_type;
   tree valist = TREE_OPERAND (*expr_p, 0);
   tree type = TREE_TYPE (*expr_p);
-  tree t;
+  tree t, tag, ap;
   location_t loc = EXPR_LOCATION (*expr_p);
 
   /* Verify that valist is of the proper type.  */
@@ -9351,36 +9425,13 @@ gimplify_va_arg_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
       *expr_p = dummy_object (type);
       return GS_ALL_DONE;
     }
-  else
-    {
-      /* Make it easier for the backends by protecting the valist argument
-	 from multiple evaluations.  */
-      if (TREE_CODE (have_va_type) == ARRAY_TYPE)
-	{
-	  /* For this case, the backends will be expecting a pointer to
-	     TREE_TYPE (abi), but it's possible we've
-	     actually been given an array (an actual TARGET_FN_ABI_VA_LIST).
-	     So fix it.  */
-	  if (TREE_CODE (TREE_TYPE (valist)) == ARRAY_TYPE)
-	    {
-	      tree p1 = build_pointer_type (TREE_TYPE (have_va_type));
-	      valist = fold_convert_loc (loc, p1,
-					 build_fold_addr_expr_loc (loc, valist));
-	    }
-
-	  gimplify_expr (&valist, pre_p, post_p, is_gimple_val, fb_rvalue);
-	}
-      else
-	gimplify_expr (&valist, pre_p, post_p, is_gimple_min_lval, fb_lvalue);
 
-      if (!targetm.gimplify_va_arg_expr)
-	/* FIXME: Once most targets are converted we should merely
-	   assert this is non-null.  */
-	return GS_ALL_DONE;
+  /* Transform a VA_ARG_EXPR into an VA_ARG internal function.  */
+  ap = build_fold_addr_expr_loc (loc, valist);
+  tag = build_int_cst (build_pointer_type (type), 0);
+  *expr_p = build_call_expr_internal_loc (loc, IFN_VA_ARG, type, 2, ap, tag);
 
-      *expr_p = targetm.gimplify_va_arg_expr (valist, type, pre_p, post_p);
-      return GS_OK;
-    }
+  return GS_OK;
 }
 
 /* Build a new GIMPLE_ASSIGN tuple and append it to the end of *SEQ_P.
diff --git a/gcc/gimplify.h b/gcc/gimplify.h
index 615925c..bad8e0f 100644
--- a/gcc/gimplify.h
+++ b/gcc/gimplify.h
@@ -82,6 +82,8 @@ extern void gimplify_function_tree (tree);
 extern enum gimplify_status gimplify_va_arg_expr (tree *, gimple_seq *,
 						  gimple_seq *);
 gimple gimplify_assign (tree, tree, gimple_seq *);
+extern tree gimplify_va_arg_internal (tree, tree, location_t, gimple_seq *,
+				      gimple_seq *);
 
 /* Return true if gimplify_one_sizepos doesn't need to gimplify
    expr (when in TYPE_SIZE{,_UNIT} and similar type/decl size/bitsize
diff --git a/gcc/internal-fn.c b/gcc/internal-fn.c
index e402825..0053ed9 100644
--- a/gcc/internal-fn.c
+++ b/gcc/internal-fn.c
@@ -1972,6 +1972,15 @@ expand_BUILTIN_EXPECT (gcall *stmt)
     emit_move_insn (target, val);
 }
 
+/* IFN_VA_ARG is supposed to be expanded at pass_stdarg.  So this dummy function
+   should never be called.  */
+
+static void
+expand_VA_ARG (gcall *stmt ATTRIBUTE_UNUSED)
+{
+  gcc_unreachable ();
+}
+
 /* Routines to expand each internal function, indexed by function number.
    Each routine has the prototype:
 
diff --git a/gcc/internal-fn.def b/gcc/internal-fn.def
index 032ce6c..f557c64 100644
--- a/gcc/internal-fn.def
+++ b/gcc/internal-fn.def
@@ -62,3 +62,4 @@ DEF_INTERNAL_FN (ADD_OVERFLOW, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
 DEF_INTERNAL_FN (SUB_OVERFLOW, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
 DEF_INTERNAL_FN (MUL_OVERFLOW, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
 DEF_INTERNAL_FN (TSAN_FUNC_EXIT, ECF_NOVOPS | ECF_LEAF | ECF_NOTHROW, NULL)
+DEF_INTERNAL_FN (VA_ARG, 0, NULL)
diff --git a/gcc/passes.def b/gcc/passes.def
index 2bc5dcd..b9d396f 100644
--- a/gcc/passes.def
+++ b/gcc/passes.def
@@ -342,6 +342,7 @@ along with GCC; see the file COPYING3.  If not see
       NEXT_PASS (pass_tm_edges);
   POP_INSERT_PASSES ()
   NEXT_PASS (pass_vtable_verify);
+  NEXT_PASS (pass_lower_vaarg);
   NEXT_PASS (pass_lower_vector);
   NEXT_PASS (pass_lower_complex_O0);
   NEXT_PASS (pass_asan_O0);
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/stdarg-2.c b/gcc/testsuite/gcc.dg/tree-ssa/stdarg-2.c
index fe39da3..5a74280 100644
--- a/gcc/testsuite/gcc.dg/tree-ssa/stdarg-2.c
+++ b/gcc/testsuite/gcc.dg/tree-ssa/stdarg-2.c
@@ -288,9 +288,9 @@ f15 (int i, ...)
   f15_1 (ap);
   va_end (ap);
 }
-/* { dg-final { scan-tree-dump "f15: va_list escapes 0, needs to save \[148\] GPR units and \[1-9\]\[0-9\]* FPR units" "stdarg" { target { { i?86-*-* x86_64-*-* } && { ! { ia32 || llp64 } } } } } } */
+/* { dg-final { scan-tree-dump "f15: va_list escapes 0, needs to save \[148\] GPR units and \[1-9\]\[0-9\]* FPR units" "stdarg" { target { { i?86-*-* } && { ! { ia32 || llp64 } } } } } } */
 /* { dg-final { scan-tree-dump "f15: va_list escapes 0, needs to save \[148\] GPR units and \[1-9\]\[0-9\]* FPR units" "stdarg" { target { powerpc*-*-linux* && { powerpc_fprs && ilp32 } } } } } */
-/* { dg-final { scan-tree-dump "f15: va_list escapes 1, needs to save all GPR units and all FPR units" "stdarg" { target alpha*-*-linux* } } } */
+/* { dg-final { scan-tree-dump "f15: va_list escapes 1, needs to save all GPR units and all FPR units" "stdarg" { target { { alpha*-*-linux* } || { { x86_64-*-* } && { ! { ia32 || llp64 } } } } } } } */
 /* { dg-final { scan-tree-dump "f15: va_list escapes 0, needs to save 1 GPR units and 2 FPR units" "stdarg" { target s390*-*-linux* } } } */
 /* { dg-final { scan-tree-dump-not "f15: va_list escapes 0, needs to save 0 GPR units" "stdarg" { target { { i?86-*-* x86_64-*-* } && ia32 } } } } */
 /* { dg-final { scan-tree-dump-not "f15: va_list escapes 0, needs to save 0 GPR units" "stdarg" { target ia64-*-* } } } */
diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h
index b59ae7a..bcefb3e 100644
--- a/gcc/tree-pass.h
+++ b/gcc/tree-pass.h
@@ -221,6 +221,7 @@ protected:
 #define PROP_loops		(1 << 11)	/* preserve loop structures */
 #define PROP_gimple_lvec	(1 << 12)       /* lowered vector */
 #define PROP_gimple_eomp	(1 << 13)       /* no OpenMP directives */
+#define PROP_gimple_lva		(1 << 14)       /* No va_arg internal function.  */
 
 #define PROP_trees \
   (PROP_gimple_any | PROP_gimple_lcf | PROP_gimple_leh | PROP_gimple_lomp)
@@ -588,6 +589,7 @@ extern gimple_opt_pass *make_pass_early_inline (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_inline_parameters (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_update_address_taken (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_convert_switch (gcc::context *ctxt);
+extern gimple_opt_pass *make_pass_lower_vaarg (gcc::context *ctxt);
 
 /* Current optimization pass.  */
 extern opt_pass *current_pass;
diff --git a/gcc/tree-stdarg.c b/gcc/tree-stdarg.c
index 8d221a4..16a9e2c 100644
--- a/gcc/tree-stdarg.c
+++ b/gcc/tree-stdarg.c
@@ -52,11 +52,14 @@ along with GCC; see the file COPYING3.  If not see
 #include "gimple-iterator.h"
 #include "gimple-walk.h"
 #include "gimple-ssa.h"
+#include "gimplify.h"
 #include "tree-phinodes.h"
 #include "ssa-iterators.h"
 #include "stringpool.h"
 #include "tree-ssanames.h"
+#include "tree-into-ssa.h"
 #include "sbitmap.h"
+#include "tree-cfg.h"
 #include "tree-pass.h"
 #include "tree-stdarg.h"
 
@@ -1016,6 +1019,112 @@ finish:
     }
 }
 
+/* Return true if STMT is IFN_VA_ARG.  */
+
+static bool
+gimple_call_ifn_va_arg_p (gimple stmt)
+{
+  return (is_gimple_call (stmt)
+	  && gimple_call_internal_p (stmt)
+	  && gimple_call_internal_fn (stmt) == IFN_VA_ARG);
+}
+
+/* Expand IFN_VA_ARGs in FUN.  */
+
+static void
+expand_ifn_va_arg_1 (function *fun)
+{
+  bool modified = false;
+  basic_block bb;
+  gimple_stmt_iterator i;
+
+  FOR_EACH_BB_FN (bb, fun)
+    for (i = gsi_start_bb (bb); !gsi_end_p (i); gsi_next (&i))
+      {
+	gimple stmt = gsi_stmt (i);
+	tree ap, expr, lhs, type;
+	gimple_seq pre = NULL, post = NULL;
+
+	if (!gimple_call_ifn_va_arg_p (stmt))
+	  continue;
+
+	modified = true;
+
+	type = TREE_TYPE (TREE_TYPE (gimple_call_arg (stmt, 1)));
+	ap = gimple_call_arg (stmt, 0);
+	ap = build_fold_indirect_ref (ap);
+
+	push_gimplify_context (false);
+
+	expr = gimplify_va_arg_internal (ap, type, gimple_location (stmt),
+					 &pre, &post);
+
+	lhs = gimple_call_lhs (stmt);
+	if (lhs != NULL_TREE)
+	  {
+	    gcc_assert (useless_type_conversion_p (TREE_TYPE (lhs), type));
+
+	    if (gimple_call_num_args (stmt) == 3)
+	      {
+		/* We've transported the size of with WITH_SIZE_EXPR here as
+		   the 3rd argument of the internal fn call.  Now reinstate
+		   it.  */
+		tree size = gimple_call_arg (stmt, 2);
+		expr = build2 (WITH_SIZE_EXPR, TREE_TYPE (expr), expr, size);
+	      }
+
+	    /* We use gimplify_assign here, rather than gimple_build_assign,
+	       because gimple_assign knows how to deal with variable-sized
+	       types.  */
+	    gimplify_assign (lhs, expr, &pre);
+	  }
+
+	pop_gimplify_context (NULL);
+
+	gimple_seq_add_seq (&pre, post);
+	update_modified_stmts (pre);
+
+	/* Add the sequence after IFN_VA_ARG.  This splits the bb right
+	   after IFN_VA_ARG, and adds the sequence in one or more new bbs
+	   inbetween.  */
+	gimple_find_sub_bbs (pre, &i);
+
+	/* Remove the IFN_VA_ARG gimple_call.  It's the last stmt in the
+	   bb.  */
+	gsi_remove (&i, true);
+	gcc_assert (gsi_end_p (i));
+
+	/* We're walking here into the bbs which contain the expansion of
+	   IFN_VA_ARG, and will not contain another IFN_VA_ARG that needs
+	   expanding.  We could try to skip walking these bbs, perhaps by
+	   walking backwards over gimples and bbs.  */
+	break;
+      }
+
+  if (!modified)
+    return;
+
+  free_dominance_info (CDI_DOMINATORS);
+  update_ssa (TODO_update_ssa);
+}
+
+/* Expand IFN_VA_ARGs in FUN, if necessary.  */
+
+static void
+expand_ifn_va_arg (function *fun)
+{
+  if ((fun->curr_properties & PROP_gimple_lva) == 0)
+    expand_ifn_va_arg_1 (fun);
+
+#if ENABLE_CHECKING
+  basic_block bb;
+  gimple_stmt_iterator i;
+  FOR_EACH_BB_FN (bb, fun)
+    for (i = gsi_start_bb (bb); !gsi_end_p (i); gsi_next (&i))
+      gcc_assert (!gimple_call_ifn_va_arg_p (gsi_stmt (i)));
+#endif
+}
+
 namespace {
 
 const pass_data pass_data_stdarg =
@@ -1025,7 +1134,7 @@ const pass_data pass_data_stdarg =
   OPTGROUP_NONE, /* optinfo_flags */
   TV_NONE, /* tv_id */
   ( PROP_cfg | PROP_ssa ), /* properties_required */
-  0, /* properties_provided */
+  PROP_gimple_lva, /* properties_provided */
   0, /* properties_destroyed */
   0, /* todo_flags_start */
   0, /* todo_flags_finish */
@@ -1039,18 +1148,13 @@ public:
   {}
 
   /* opt_pass methods: */
-  virtual bool gate (function *fun)
+  virtual bool gate (function *)
     {
-      return (flag_stdarg_opt
-#ifdef ACCEL_COMPILER
-	      /* Disable for GCC5 in the offloading compilers, as
-		 va_list and gpr/fpr counter fields are not merged.
-		 In GCC6 when stdarg is lowered late this shouldn't be
-		 an issue.  */
-	      && !in_lto_p
-#endif
-	      /* This optimization is only for stdarg functions.  */
-	      && fun->stdarg != 0);
+      /* Always run this pass, in order to expand va_arg internal_fns.  We
+	 also need to do that if fun->stdarg == 0, because a va_arg may also
+	 occur in a function without varargs, f.i. if when passing a va_list to
+	 another function.  */
+      return true;
     }
 
   virtual unsigned int execute (function *);
@@ -1060,7 +1164,14 @@ public:
 unsigned int
 pass_stdarg::execute (function *fun)
 {
-  optimize_va_list_gpr_fpr_size (fun);
+  /* TODO: Postpone expand_ifn_va_arg till after
+     optimize_va_list_gpr_fpr_size.  */
+  expand_ifn_va_arg (fun);
+
+  if (flag_stdarg_opt
+      /* This optimization is only for stdarg functions.  */
+      && fun->stdarg != 0)
+    optimize_va_list_gpr_fpr_size (fun);
 
   return 0;
 }
@@ -1072,3 +1183,50 @@ make_pass_stdarg (gcc::context *ctxt)
 {
   return new pass_stdarg (ctxt);
 }
+
+namespace {
+
+const pass_data pass_data_lower_vaarg =
+{
+  GIMPLE_PASS, /* type */
+  "lower_vaarg", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  TV_NONE, /* tv_id */
+  ( PROP_cfg | PROP_ssa ), /* properties_required */
+  PROP_gimple_lva, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  0, /* todo_flags_finish */
+};
+
+class pass_lower_vaarg : public gimple_opt_pass
+{
+public:
+  pass_lower_vaarg (gcc::context *ctxt)
+    : gimple_opt_pass (pass_data_lower_vaarg, ctxt)
+  {}
+
+  /* opt_pass methods: */
+  virtual bool gate (function *)
+    {
+      return (cfun->curr_properties & PROP_gimple_lva) == 0;
+    }
+
+  virtual unsigned int execute (function *);
+
+}; // class pass_lower_vaarg
+
+unsigned int
+pass_lower_vaarg::execute (function *fun)
+{
+  expand_ifn_va_arg (fun);
+  return 0;
+}
+
+} // anon namespace
+
+gimple_opt_pass *
+make_pass_lower_vaarg (gcc::context *ctxt)
+{
+  return new pass_lower_vaarg (ctxt);
+}
-- 
1.9.1

Reply via email to