+
+ if (call && CALL_EXPR_VA_ARG_PACK (call) && id->call_expr)
+ {
+ /* __builtin_va_arg_pack () should be replaced by
+ all arguments corresponding to ... in the caller. */
+ tree p, *argarray, new_call, *call_ptr;
+ int nargs = call_expr_nargs (id->call_expr);
+
+ for (p = DECL_ARGUMENTS (id->src_fn); p; p = TREE_CHAIN (p))
+ nargs--;
+
+ argarray = (tree *) alloca ((nargs + call_expr_nargs (call))
+ * sizeof (tree));
+
+ memcpy (argarray, CALL_EXPR_ARGP (call),
+ call_expr_nargs (call) * sizeof (*argarray));
+ memcpy (argarray + call_expr_nargs (call),
+ CALL_EXPR_ARGP (id->call_expr)
+ + (call_expr_nargs (id->call_expr) - nargs),
+ nargs * sizeof (*argarray));
+
+ new_call = build_call_array (TREE_TYPE (call),
+ CALL_EXPR_FN (call),
+ nargs + call_expr_nargs (call),
+ argarray);
+ /* Copy all CALL_EXPR flags, locus and block, except
+ CALL_EXPR_VA_ARG_PACK flag. */
+ CALL_EXPR_STATIC_CHAIN (new_call)
+ = CALL_EXPR_STATIC_CHAIN (call);
+ CALL_EXPR_TAILCALL (new_call) = CALL_EXPR_TAILCALL (call);
+ CALL_EXPR_RETURN_SLOT_OPT (new_call)
+ = CALL_EXPR_RETURN_SLOT_OPT (call);
+ CALL_FROM_THUNK_P (new_call) = CALL_FROM_THUNK_P (call);
+ CALL_CANNOT_INLINE_P (new_call)
+ = CALL_CANNOT_INLINE_P (call);
+ TREE_NOTHROW (new_call) = TREE_NOTHROW (call);
+ SET_EXPR_LOCUS (new_call, EXPR_LOCUS (call));
+ TREE_BLOCK (new_call) = TREE_BLOCK (call);
+
+ call_ptr = stmtp;
+ if (TREE_CODE (*call_ptr) == GIMPLE_MODIFY_STMT)
+ call_ptr = &GIMPLE_STMT_OPERAND (*call_ptr, 1);
+ if (TREE_CODE (*call_ptr) == WITH_SIZE_EXPR)
+ call_ptr = &TREE_OPERAND (*call_ptr, 0);
+ gcc_assert (*call_ptr == call);
+ if (call_ptr == stmtp)
+ {
+ bsi_replace (©_bsi, new_call, true);
+ stmtp = bsi_stmt_ptr (copy_bsi);
+ stmt = *stmtp;
+ }
+ else
+ {
+ *call_ptr = new_call;
+ stmt = *stmtp;
+ update_stmt (stmt);
+ }
+ }
+ else if (call
+ && id->call_expr
+ && (decl = get_callee_fndecl (call))
+ && DECL_BUILT_IN_CLASS (decl) == BUILT_IN_NORMAL
+ && DECL_FUNCTION_CODE (decl)
+ == BUILT_IN_VA_ARG_PACK_LEN)
+ {
+ /* __builtin_va_arg_pack_len () should be replaced by
+ the number of anonymous arguments. */
+ int nargs = call_expr_nargs (id->call_expr);
+ tree count, *call_ptr, p;
+
+ for (p = DECL_ARGUMENTS (id->src_fn); p; p = TREE_CHAIN (p))
+ nargs--;
+
+ count = build_int_cst (integer_type_node, nargs);
+ call_ptr = stmtp;
+ if (TREE_CODE (*call_ptr) == GIMPLE_MODIFY_STMT)
+ call_ptr = &GIMPLE_STMT_OPERAND (*call_ptr, 1);
+ if (TREE_CODE (*call_ptr) == WITH_SIZE_EXPR)
+ call_ptr = &TREE_OPERAND (*call_ptr, 0);
+ gcc_assert (*call_ptr == call && call_ptr != stmtp);
+ *call_ptr = count;
+ stmt = *stmtp;
+ update_stmt (stmt);
+ call = NULL_TREE;
+ }
+
+ /* Statements produced by inlining can be unfolded, especially
+ when we constant propagated some operands. We can't fold
+ them right now for two reasons:
+ 1) folding require SSA_NAME_DEF_STMTs to be correct
+ 2) we can't change function calls to builtins.
+ So we just mark statement for later folding. We mark
+ all new statements, instead just statements that has changed
+ by some nontrivial substitution so even statements made
+ foldable indirectly are updated. If this turns out to be
+ expensive, copy_body can be told to watch for nontrivial
+ changes. */
+ if (id->statements_to_fold)
+ pointer_set_insert (id->statements_to_fold, stmt);