Commit 6ef5231b by Jakub Jelinek

builtins.def (BUILT_IN_VA_ARG_PACK): New built-in.

	* builtins.def (BUILT_IN_VA_ARG_PACK): New built-in.
	* tree.h (CALL_EXPR_VA_ARG_PACK): Define.
	* tree-inline.h (copy_body_data): Add call_expr field.
	* tree-inline.c (expand_call_inline): Initialize call_expr.
	(copy_bb): Append anonymous inline fn arguments to arguments
	when inlining a CALL_EXPR_VA_ARG_PACK call.
	* builtins.c (expand_builtin): Issue an error if
	BUILT_IN_VA_ARG_PACK is seen during expand.
	(fold_call_expr, fold_builtin_call_array): Don't fold
	CALL_EXPR_VA_ARG_PACK CALL_EXPRs or calls with
	__builtin_va_arg_pack () call as last argument.
	* gimplify.c (gimplify_call_expr): If last argument to a vararg
	function is __builtin_va_arg_pack (), decrease number of call
	arguments and instead set CALL_EXPR_VA_ARG_PACK on the CALL_EXPR.
	* expr.c (expand_expr_real_1): Issue an error if
	CALL_EXPR_VA_ARG_PACK CALL_EXPR is seen during expand.
	* tree-pretty-print.c (dump_generic_node): Handle printing
	CALL_EXPR_VA_ARG_PACK bit on CALL_EXPRs.
	* doc/extend.texi (__builtin_va_arg_pack): Document.

	* gcc.c-torture/execute/va-arg-pack-1.c: New test.
	* gcc.dg/va-arg-pack-1.c: New test.

From-SVN: r128150
parent e050d795
......@@ -6270,6 +6270,12 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode,
case BUILT_IN_ARGS_INFO:
return expand_builtin_args_info (exp);
case BUILT_IN_VA_ARG_PACK:
/* All valid uses of __builtin_va_arg_pack () are removed during
inlining. */
error ("invalid use of %<__builtin_va_arg_pack ()%>");
return const0_rtx;
/* Return the address of the first anonymous stack arg. */
case BUILT_IN_NEXT_ARG:
if (fold_builtin_next_arg (exp, false))
......@@ -10472,14 +10478,32 @@ fold_call_expr (tree exp, bool ignore)
tree fndecl = get_callee_fndecl (exp);
if (fndecl
&& TREE_CODE (fndecl) == FUNCTION_DECL
&& DECL_BUILT_IN (fndecl))
{
&& DECL_BUILT_IN (fndecl)
/* If CALL_EXPR_VA_ARG_PACK is set, the arguments aren't finalized
yet. Defer folding until we see all the arguments
(after inlining). */
&& !CALL_EXPR_VA_ARG_PACK (exp))
{
int nargs = call_expr_nargs (exp);
/* Before gimplification CALL_EXPR_VA_ARG_PACK is not set, but
instead last argument is __builtin_va_arg_pack (). Defer folding
even in that case, until arguments are finalized. */
if (nargs && TREE_CODE (CALL_EXPR_ARG (exp, nargs - 1)) == CALL_EXPR)
{
tree fndecl2 = get_callee_fndecl (CALL_EXPR_ARG (exp, nargs - 1));
if (fndecl2
&& TREE_CODE (fndecl2) == FUNCTION_DECL
&& DECL_BUILT_IN_CLASS (fndecl2) == BUILT_IN_NORMAL
&& DECL_FUNCTION_CODE (fndecl2) == BUILT_IN_VA_ARG_PACK)
return NULL_TREE;
}
/* FIXME: Don't use a list in this interface. */
if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD)
return targetm.fold_builtin (fndecl, CALL_EXPR_ARGS (exp), ignore);
else
{
int nargs = call_expr_nargs (exp);
if (nargs <= MAX_ARGS_TO_FOLD_BUILTIN)
{
tree *args = CALL_EXPR_ARGP (exp);
......@@ -10565,6 +10589,17 @@ fold_builtin_call_array (tree type,
if (TREE_CODE (fndecl) == FUNCTION_DECL
&& DECL_BUILT_IN (fndecl))
{
/* If last argument is __builtin_va_arg_pack (), arguments to this
function are not finalized yet. Defer folding until they are. */
if (n && TREE_CODE (argarray[n - 1]) == CALL_EXPR)
{
tree fndecl2 = get_callee_fndecl (argarray[n - 1]);
if (fndecl2
&& TREE_CODE (fndecl2) == FUNCTION_DECL
&& DECL_BUILT_IN_CLASS (fndecl2) == BUILT_IN_NORMAL
&& DECL_FUNCTION_CODE (fndecl2) == BUILT_IN_VA_ARG_PACK)
return build_call_array (type, fn, n, argarray);
}
if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD)
{
tree arglist = NULL_TREE;
......
......@@ -701,6 +701,7 @@ DEF_GCC_BUILTIN (BUILT_IN_UPDATE_SETJMP_BUF, "update_setjmp_buf", BT_FN_V
DEF_GCC_BUILTIN (BUILT_IN_VA_COPY, "va_copy", BT_FN_VOID_VALIST_REF_VALIST_ARG, ATTR_NULL)
DEF_GCC_BUILTIN (BUILT_IN_VA_END, "va_end", BT_FN_VOID_VALIST_REF, ATTR_NULL)
DEF_GCC_BUILTIN (BUILT_IN_VA_START, "va_start", BT_FN_VOID_VALIST_REF_VAR, ATTR_NULL)
DEF_GCC_BUILTIN (BUILT_IN_VA_ARG_PACK, "va_arg_pack", BT_FN_INT, ATTR_PURE_NOTHROW_LIST)
DEF_EXT_LIB_BUILTIN (BUILT_IN__EXIT, "_exit", BT_FN_VOID_INT, ATTR_NORETURN_NOTHROW_LIST)
DEF_C99_BUILTIN (BUILT_IN__EXIT2, "_Exit", BT_FN_VOID_INT, ATTR_NORETURN_NOTHROW_LIST)
......
......@@ -556,6 +556,32 @@ the containing function. You should specify, for @var{result}, a value
returned by @code{__builtin_apply}.
@end deftypefn
@deftypefn {Built-in Function} __builtin_va_arg_pack ()
This built-in function represents all anonymous arguments of an inline
function. It can be used only in inline functions which will be always
inlined, never compiled as a separate function, such as those using
@code{__attribute__ ((__always_inline__))} or
@code{__attribute__ ((__gnu_inline__))} extern inline functions.
It must be only passed as last argument to some other function
with variable arguments. This is useful for writing small wrapper
inlines for variable argument functions, when using preprocessor
macros is undesirable. For example:
@smallexample
extern int myprintf (FILE *f, const char *format, ...);
extern inline __attribute__ ((__gnu_inline__)) int
myprintf (FILE *f, const char *format, ...)
@{
int r = fprintf (f, "myprintf: ");
if (r < 0)
return r;
int s = fprintf (f, format, __builtin_va_arg_pack ());
if (s < 0)
return s;
return r + s;
@}
@end smallexample
@end deftypefn
@node Typeof
@section Referring to a Type with @code{typeof}
@findex typeof
......
......@@ -7935,6 +7935,10 @@ expand_expr_real_1 (tree exp, rtx target, enum machine_mode tmode,
return expand_expr (OBJ_TYPE_REF_EXPR (exp), target, tmode, modifier);
case CALL_EXPR:
/* All valid uses of __builtin_va_arg_pack () are removed during
inlining. */
if (CALL_EXPR_VA_ARG_PACK (exp))
error ("invalid use of %<__builtin_va_arg_pack ()%>");
/* Check for a built-in function. */
if (TREE_CODE (CALL_EXPR_FN (exp)) == ADDR_EXPR
&& (TREE_CODE (TREE_OPERAND (CALL_EXPR_FN (exp), 0))
......
......@@ -2170,8 +2170,50 @@ gimplify_call_expr (tree *expr_p, tree *pre_p, bool want_value)
}
}
}
else if (nargs != 0)
CALL_CANNOT_INLINE_P (*expr_p) = 1;
else
{
if (nargs != 0)
CALL_CANNOT_INLINE_P (*expr_p) = 1;
i = 0;
p = NULL_TREE;
}
/* If the last argument is __builtin_va_arg_pack () and it is not
passed as a named argument, decrease the number of CALL_EXPR
arguments and set instead the CALL_EXPR_VA_ARG_PACK flag. */
if (!p
&& i < nargs
&& TREE_CODE (CALL_EXPR_ARG (*expr_p, nargs - 1)) == CALL_EXPR)
{
tree last_arg = CALL_EXPR_ARG (*expr_p, nargs - 1);
tree last_arg_fndecl = get_callee_fndecl (last_arg);
if (last_arg_fndecl
&& TREE_CODE (last_arg_fndecl) == FUNCTION_DECL
&& DECL_BUILT_IN_CLASS (last_arg_fndecl) == BUILT_IN_NORMAL
&& DECL_FUNCTION_CODE (last_arg_fndecl) == BUILT_IN_VA_ARG_PACK)
{
tree call = *expr_p;
--nargs;
*expr_p = build_call_array (TREE_TYPE (call), CALL_EXPR_FN (call),
nargs, CALL_EXPR_ARGP (call));
/* Copy all CALL_EXPR flags, locus and block, except
CALL_EXPR_VA_ARG_PACK flag. */
CALL_EXPR_STATIC_CHAIN (*expr_p) = CALL_EXPR_STATIC_CHAIN (call);
CALL_EXPR_TAILCALL (*expr_p) = CALL_EXPR_TAILCALL (call);
CALL_EXPR_RETURN_SLOT_OPT (*expr_p)
= CALL_EXPR_RETURN_SLOT_OPT (call);
CALL_FROM_THUNK_P (*expr_p) = CALL_FROM_THUNK_P (call);
CALL_CANNOT_INLINE_P (*expr_p)
= CALL_CANNOT_INLINE_P (call);
TREE_NOTHROW (*expr_p) = TREE_NOTHROW (call);
SET_EXPR_LOCUS (*expr_p, EXPR_LOCUS (call));
TREE_BLOCK (*expr_p) = TREE_BLOCK (call);
/* Set CALL_EXPR_VA_ARG_PACK. */
CALL_EXPR_VA_ARG_PACK (*expr_p) = 1;
}
}
/* Finally, gimplify the function arguments. */
for (i = (PUSH_ARGS_REVERSED ? nargs - 1 : 0);
......
2007-09-05 Jakub Jelinek <jakub@redhat.com>
* gcc.c-torture/execute/va-arg-pack-1.c: New test.
* gcc.dg/va-arg-pack-1.c: New test.
2007-09-05 Adam Nemet <anemet@caviumnetworks.com>
* gcc.dg/tree-ssa/builtin-expect-1.c: New test.
/* __builtin_va_arg_pack () builtin tests. */
#include <stdarg.h>
extern void abort (void);
int v1 = 8;
long int v2 = 3;
void *v3 = (void *) &v2;
struct A { char c[16]; } v4 = { "foo" };
long double v5 = 40;
char seen[20];
int cnt;
__attribute__ ((noinline)) int
foo1 (int x, int y, ...)
{
int i;
long int l;
void *v;
struct A a;
long double ld;
va_list ap;
va_start (ap, y);
if (x < 0 || x >= 20 || seen[x])
abort ();
seen[x] = ++cnt;
if (y != 6)
abort ();
i = va_arg (ap, int);
if (i != 5)
abort ();
switch (x)
{
case 0:
i = va_arg (ap, int);
if (i != 9 || v1 != 9)
abort ();
a = va_arg (ap, struct A);
if (__builtin_memcmp (a.c, v4.c, sizeof (a.c)) != 0)
abort ();
v = (void *) va_arg (ap, struct A *);
if (v != (void *) &v4)
abort ();
l = va_arg (ap, long int);
if (l != 3 || v2 != 4)
abort ();
break;
case 1:
ld = va_arg (ap, long double);
if (ld != 41 || v5 != ld)
abort ();
i = va_arg (ap, int);
if (i != 8)
abort ();
v = va_arg (ap, void *);
if (v != &v2)
abort ();
break;
case 2:
break;
default:
abort ();
}
va_end (ap);
return x;
}
__attribute__ ((noinline)) int
foo2 (int x, int y, ...)
{
long long int ll;
void *v;
struct A a, b;
long double ld;
va_list ap;
va_start (ap, y);
if (x < 0 || x >= 20 || seen[x])
abort ();
seen[x] = ++cnt | 64;
if (y != 10)
abort ();
switch (x)
{
case 11:
break;
case 12:
ld = va_arg (ap, long double);
if (ld != 41 || v5 != 40)
abort ();
a = va_arg (ap, struct A);
if (__builtin_memcmp (a.c, v4.c, sizeof (a.c)) != 0)
abort ();
b = va_arg (ap, struct A);
if (__builtin_memcmp (b.c, v4.c, sizeof (b.c)) != 0)
abort ();
v = va_arg (ap, void *);
if (v != &v2)
abort ();
ll = va_arg (ap, long long int);
if (ll != 16LL)
abort ();
break;
case 2:
break;
default:
abort ();
}
va_end (ap);
return x + 8;
}
__attribute__ ((noinline)) int
foo3 (void)
{
return 6;
}
extern inline __attribute__ ((always_inline, gnu_inline)) int
bar (int x, ...)
{
if (x < 10)
return foo1 (x, foo3 (), 5, __builtin_va_arg_pack ());
return foo2 (x, foo3 () + 4, __builtin_va_arg_pack ());
}
int
main (void)
{
if (bar (0, ++v1, v4, &v4, v2++) != 0)
abort ();
if (bar (1, ++v5, 8, v3) != 1)
abort ();
if (bar (2) != 2)
abort ();
if (bar (v1 + 2) != 19)
abort ();
if (bar (v1 + 3, v5--, v4, v4, v3, 16LL) != 20)
abort ();
return 0;
}
/* { dg-do compile } */
/* { dg-options "-O2" } */
int bar (int, const char *, int, ...);
int baz (int, const char *, long int);
int
f1 (int x, ...)
{
return bar (5, "", 6, __builtin_va_arg_pack ()); /* { dg-error "invalid use of" } */
}
extern inline __attribute__((always_inline)) int
f2 (int y, ...)
{
return bar (y, "", __builtin_va_arg_pack ()); /* { dg-error "invalid use of" } */
}
extern inline __attribute__((always_inline)) int
f3 (int y, ...)
{
return bar (y, "", 5, __builtin_va_arg_pack ());
}
extern inline __attribute__((always_inline)) int
f4 (int y, ...)
{
return bar (y, "", 4, __builtin_va_arg_pack (), 6); /* { dg-error "invalid use of" } */
}
extern inline __attribute__((always_inline)) int
f5 (int y, ...)
{
return baz (y, "", __builtin_va_arg_pack ()); /* { dg-error "invalid use of" } */
}
extern inline __attribute__((always_inline)) int
f6 (int y, ...)
{
return __builtin_va_arg_pack (); /* { dg-error "invalid use of" } */
}
int
test (void)
{
int a = f2 (5, "a", 6);
a += f3 (6, "ab", 17LL);
a += f4 (7, 1, 2, 3);
a += f5 (8, 7L);
a += f6 (9);
return a;
}
......@@ -815,9 +815,59 @@ copy_bb (copy_body_data *id, basic_block bb, int frequency_scale, int count_scal
into multiple statements, we need to process all of them. */
while (!bsi_end_p (copy_bsi))
{
stmt = bsi_stmt (copy_bsi);
tree *stmtp = bsi_stmt_ptr (copy_bsi);
tree stmt = *stmtp;
call = get_call_expr_in (stmt);
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);
*call_ptr = new_call;
stmt = *stmtp;
update_stmt (stmt);
}
/* Statements produced by inlining can be unfolded, especially
when we constant propagated some operands. We can't fold
them right now for two reasons:
......@@ -2518,6 +2568,7 @@ expand_call_inline (basic_block bb, tree stmt, tree *tp, void *data)
id->src_fn = fn;
id->src_node = cg_edge->callee;
id->src_cfun = DECL_STRUCT_FUNCTION (fn);
id->call_expr = t;
initialize_inlined_parameters (id, t, fn, bb);
......
......@@ -56,6 +56,10 @@ typedef struct copy_body_data
/* Current BLOCK. */
tree block;
/* CALL_EXPR if va arg parameter packs should be expanded or NULL
is not. */
tree call_expr;
/* Exception region the inlined call lie in. */
int eh_region;
/* Take region number in the function being copied, add this value and
......
......@@ -1228,6 +1228,15 @@ dump_generic_node (pretty_printer *buffer, tree node, int spc, int flags,
}
}
}
if (CALL_EXPR_VA_ARG_PACK (node))
{
if (call_expr_nargs (node) > 0)
{
pp_character (buffer, ',');
pp_space (buffer);
}
pp_string (buffer, "__builtin_va_arg_pack ()");
}
pp_character (buffer, ')');
op1 = CALL_EXPR_STATIC_CHAIN (node);
......
......@@ -464,6 +464,8 @@ struct gimple_stmt GTY(())
VAR_DECL or FUNCTION_DECL or IDENTIFIER_NODE
ASM_VOLATILE_P in
ASM_EXPR
CALL_EXPR_VA_ARG_PACK in
CALL_EXPR
TYPE_CACHED_VALUES_P in
..._TYPE
SAVE_EXPR_RESOLVED_P in
......@@ -1222,6 +1224,11 @@ extern void omp_clause_range_check_failed (const_tree, const char *, int,
#define SAVE_EXPR_RESOLVED_P(NODE) \
(TREE_CHECK (NODE, SAVE_EXPR)->base.public_flag)
/* Set on a CALL_EXPR if this stdarg call should be passed the argument
pack. */
#define CALL_EXPR_VA_ARG_PACK(NODE) \
(CALL_EXPR_CHECK(NODE)->base.public_flag)
/* In any expression, decl, or constant, nonzero means it has side effects or
reevaluation of the whole expression could produce a different value.
This is set if any subexpression is a function call, a side effect or a
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment