Postpone expanding va_arg until pass_stdarg
authorTom de Vries <tom@codesourcery.com>
Fri, 17 Apr 2015 09:26:59 +0000 (09:26 +0000)
committerTom de Vries <vries@gcc.gnu.org>
Fri, 17 Apr 2015 09:26:59 +0000 (09:26 +0000)
2015-04-17  Tom de Vries  <tom@codesourcery.com>
    Michael Matz  <matz@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-*-*.

Co-Authored-By: Michael Matz <matz@suse.de>
From-SVN: r222173

13 files changed:
gcc/ChangeLog
gcc/cfgexpand.c
gcc/gimple-iterator.c
gcc/gimple-iterator.h
gcc/gimplify.c
gcc/gimplify.h
gcc/internal-fn.c
gcc/internal-fn.def
gcc/passes.def
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/tree-ssa/stdarg-2.c
gcc/tree-pass.h
gcc/tree-stdarg.c

index 54c3ccfe1635bba48da42e1be28e5ba2e3c798f7..2fb57eae57ca7ff39cf9ec5a1a1187906329a994 100644 (file)
@@ -1,3 +1,28 @@
+2015-04-17  Tom de Vries  <tom@codesourcery.com>
+           Michael Matz  <matz@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.
+
 2015-04-17  Tom de Vries  <tom@codesourcery.com>
 
        * fold-const.c (operand_equal_p): Handle INTERNAL_FNs.
index 97e7a2583ea662d37d6eb8af57c5f14e87fc59d6..ca491a0d1560e1d9955085b9aa647d9ef8675c1f 100644 (file)
@@ -5899,7 +5899,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 */
index a322390c9f591d4ab776ca2141fe95ba92551058..df29123f538b751414a68a78c0c265591d613b2c 100644 (file)
@@ -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;
index 9aa7508c9e1df3ae0502e79cc13b0d98585a2a0f..87e943ae46876be0a2d2fac2c67431e30e21f718 100644 (file)
@@ -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.  */
 
index ff0a225c92c5fc3ff67317503ffaa0dd17312ff4..e1ea204916483b353c459a15e4b42b64fd60ebac 100644 (file)
@@ -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);
@@ -9273,16 +9310,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.  */
@@ -9334,36 +9408,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.
index 615925c1a78136892b3f2e250b64c4224c1b9e16..bad8e0ffd004b34caf2b27dcb1a438e250df8bc4 100644 (file)
@@ -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
index e4028250c2acecb6387eefee44b56efdf3b404a5..0053ed9574910991f21d217b1f2087822b44d64e 100644 (file)
@@ -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:
 
index 032ce6c909e5a4a872e010ac89d82e43f1e449f6..f557c64247ce1c7e252b0f2097a41101c1d9b973 100644 (file)
@@ -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)
index 1d598b29997445c636d045204973aef7f50cf7ad..ffa63b5904d7a1380bfdecfcd987900d20590c26 100644 (file)
@@ -344,6 +344,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);
index b406be4797ff85eafa4c1ca0e645f468a157c597..087f2b9a14a1999be3ca44593d08df41f674a8b4 100644 (file)
@@ -1,3 +1,9 @@
+2015-04-17  Tom de Vries  <tom@codesourcery.com>
+           Michael Matz  <matz@suse.de>
+
+       * gcc.dg/tree-ssa/stdarg-2.c: Change f15 scan-tree-dump for target
+       x86_64-*-*.
+
 2015-04-17  Yury Gribov  <y.gribov@samsung.com>
 
        * c-c++-common/asan/user-section-1.c: New test.
index fe39da3528059173c33f0a8930af9de9c02541f4..5a74280f7281814eff9cf221946744c0f6c8e43f 100644 (file)
@@ -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-*-* } } } */
index bc8763db16563e8baf1543c73c5fdc72a6320ac5..172bd8219028a8bdab8a5e8faf8b36e50ca1f90c 100644 (file)
@@ -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)
@@ -589,6 +590,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;
index 8d221a4b3a5be0a5026283cb5cb1e43ff9df7117..16a9e2cf66f4a1c5ca775fcbbdd288899759c30f 100644 (file)
@@ -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);
+}