From: Tom de Vries Date: Fri, 17 Apr 2015 09:26:59 +0000 (+0000) Subject: Postpone expanding va_arg until pass_stdarg X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=f8e89441bc5518f450b6511c59c17c837859d109;p=gcc.git Postpone expanding va_arg until pass_stdarg 2015-04-17 Tom de Vries Michael Matz * 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 From-SVN: r222173 --- diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 54c3ccfe163..2fb57eae57c 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,28 @@ +2015-04-17 Tom de Vries + Michael Matz + + * 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 * fold-const.c (operand_equal_p): Handle INTERNAL_FNs. diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c index 97e7a2583ea..ca491a0d156 100644 --- a/gcc/cfgexpand.c +++ b/gcc/cfgexpand.c @@ -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 */ diff --git a/gcc/gimple-iterator.c b/gcc/gimple-iterator.c index a322390c9f5..df29123f538 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 9aa7508c9e1..87e943ae468 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 ff0a225c92c..e1ea2049164 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 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. diff --git a/gcc/gimplify.h b/gcc/gimplify.h index 615925c1a78..bad8e0ffd00 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 e4028250c2a..0053ed95749 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 032ce6c909e..f557c64247c 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 1d598b29997..ffa63b5904d 100644 --- a/gcc/passes.def +++ b/gcc/passes.def @@ -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); diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index b406be4797f..087f2b9a14a 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,9 @@ +2015-04-17 Tom de Vries + Michael Matz + + * gcc.dg/tree-ssa/stdarg-2.c: Change f15 scan-tree-dump for target + x86_64-*-*. + 2015-04-17 Yury Gribov * c-c++-common/asan/user-section-1.c: New test. diff --git a/gcc/testsuite/gcc.dg/tree-ssa/stdarg-2.c b/gcc/testsuite/gcc.dg/tree-ssa/stdarg-2.c index fe39da35280..5a74280f728 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 bc8763db165..172bd821902 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) @@ -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; diff --git a/gcc/tree-stdarg.c b/gcc/tree-stdarg.c index 8d221a4b3a5..16a9e2cf66f 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); +}