[RFC PATCH] __builtin_va_arg_pack ()
Jakub Jelinek
jakub@redhat.com
Fri Aug 31 10:28:00 GMT 2007
On Fri, Aug 31, 2007 at 11:14:49AM +0200, Richard Guenther wrote:
> I think the idea is sound. Though for instrumenting the standard functions
> like printf couldn't you dispatch to the vprintf variants instead? I
> see we'd need
> to force the compiler to not inline that call then which works by re-declaring
> it like
>
> extern inline __attribute__((__gnu_inline__)) void foo(void) {}
>
> extern inline __attribute__((__gnu_inline__)) void bar(void)
> {
> extern __attribute__((noinline)) void foo(void);
>
> foo();
> }
>
> but unfortunately you get an extra warning for from that and further calls
> to foo() outside of bar() are not inlined anymore. Leaving the only
> other possibility to add a #pragma to mark a specific call as not-to-inline.
The -D_FORTIFY_SOURCE checking relies on inlining the wrappers.
E.g. for sprintf we currently have:
#define sprintf(str, ...) \
__builtin___sprintf_chk (str, __USE_FORTIFY_LEVEL - 1, __bos (str), \
__VA_ARGS__)
which can't be used in C++. With the proposed __builtin_va_arg_pack ()
it can be implemented as
__extern_always_inline int
sprintf (char *__restrict __s, __const char *__restrict __format, ...)
{
return __builtin___sprintf_chk (__s, __USE_FORTIFY_LEVEL - 1, __bos (__s),
__format, __builtin_va_arg_pack ());
}
where
#define __always_inline __inline __attribute__ ((__always_inline__))
#define __extern_always_inline extern __always_inline __attribute__ ((__gnu_inline__))
#define __bos(ptr) __builtin_object_size (ptr, __USE_FORTIFY_LEVEL > 1)
If you instead want to call vsprintf, it couldn't be inlined:
static inline int
sprintf (char *__restrict __s, __const char *__restrict __format, ...)
{
va_list __ap;
int __ret;
va_start (__ap, __format);
__ret = __builtin___vsprintf_chk (__s, __USE_FORTIFY_LEVEL - 1, __bos (__s),
__format, __ap);
va_end (__ap);
return __ret;
}
(at least, hacking up gcc so that it could inline this would be terribly
difficult and extremely arch dependent), which unfortunately means
that __builtin_object_size (__s, __USE_FORTIFY_LEVEL - 1) will
always return -1UL - at least until we have ipa object size pass and cloning
based on that (but the cloning would need to create a special out of
line function for each different value of __bos, or figure it adds an
artificial argument. Even if it does, it is another hop the call to
sprintf has to go through, so at least a few cycles wasted).
But __bos (__s) == -1UL means no buffer overflow checking.
>
> > + new_call = build_call_array (TREE_TYPE (call),
> > + CALL_EXPR_FN (call),
> > + nargs + call_expr_nargs (call),
> > + argarray);
> > + 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);
>
> in tree.h I also find CALL_FROM_THUNK_P, CALL_CANNOT_INLINE_P and
> TREE_NOTHROW.
You are right, fixed. I wonder if there is a better method for copying
the flags one by one, which is error prone. We want to copy all the
flags, except CALL_EXPR_VA_ARG_PACK and guess ann shouldn't be copied over
either. But doing
CALL_EXPR_CHECK (new_call)->base = CALL_EXPR_CHECK (call)->base;
sounds fragile too.
BTW, OT, why isn't
/* Used to mark a CALL_EXPR as not suitable for inlining. */
#define CALL_CANNOT_INLINE_P(NODE) ((NODE)->base.static_flag)
using CALL_EXPR_CHECK?
Here is an updated patch:
2007-08-31 Jakub Jelinek <jakub@redhat.com>
* 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.
--- gcc/builtins.def.jj 2007-08-30 18:38:44.000000000 +0200
+++ gcc/builtins.def 2007-08-30 18:46:59.000000000 +0200
@@ -701,6 +701,7 @@ DEF_GCC_BUILTIN (BUILT_IN_UPDATE_
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)
--- gcc/tree.h.jj 2007-08-30 18:38:44.000000000 +0200
+++ gcc/tree.h 2007-08-30 18:46:59.000000000 +0200
@@ -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_faile
#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
--- gcc/tree-inline.h.jj 2007-08-30 18:38:44.000000000 +0200
+++ gcc/tree-inline.h 2007-08-30 18:46:59.000000000 +0200
@@ -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
+ ig 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
--- gcc/tree-inline.c.jj 2007-08-30 18:38:44.000000000 +0200
+++ gcc/tree-inline.c 2007-08-31 12:07:12.000000000 +0200
@@ -815,9 +815,59 @@ copy_bb (copy_body_data *id, basic_block
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:
@@ -2539,6 +2589,7 @@ expand_call_inline (basic_block bb, tree
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);
--- gcc/builtins.c.jj 2007-08-30 18:38:44.000000000 +0200
+++ gcc/builtins.c 2007-08-31 11:58:02.000000000 +0200
@@ -6270,6 +6270,12 @@ expand_builtin (tree exp, rtx target, rt
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))
@@ -10413,14 +10419,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);
@@ -10506,6 +10530,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;
--- gcc/gimplify.c.jj 2007-08-30 18:38:44.000000000 +0200
+++ gcc/gimplify.c 2007-08-31 11:58:22.000000000 +0200
@@ -2174,8 +2174,36 @@ gimplify_call_expr (tree *expr_p, tree *
}
}
}
- 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)
+ {
+ CALL_EXPR_ARG (*expr_p, nargs - 1) = NULL_TREE;
+ VL_EXP_CHECK (*expr_p)->exp.operands[0]
+ = build_int_cst (sizetype, VL_EXP_OPERAND_LENGTH (*expr_p) - 1);
+ CALL_EXPR_VA_ARG_PACK (*expr_p) = 1;
+ --nargs;
+ }
+ }
/* Finally, gimplify the function arguments. */
for (i = (PUSH_ARGS_REVERSED ? nargs - 1 : 0);
--- gcc/expr.c.jj 2007-08-30 18:38:44.000000000 +0200
+++ gcc/expr.c 2007-08-30 18:46:59.000000000 +0200
@@ -7935,6 +7935,10 @@ expand_expr_real_1 (tree exp, rtx target
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))
--- gcc/doc/extend.texi.jj 2007-08-29 13:47:47.000000000 +0200
+++ gcc/doc/extend.texi 2007-08-31 12:00:19.000000000 +0200
@@ -556,6 +556,32 @@ the containing function. You should spe
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
--- gcc/tree-pretty-print.c.jj 2007-08-30 18:38:44.000000000 +0200
+++ gcc/tree-pretty-print.c 2007-08-30 18:46:59.000000000 +0200
@@ -1218,6 +1218,15 @@ dump_generic_node (pretty_printer *buffe
}
}
}
+ 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);
--- gcc/testsuite/gcc.c-torture/execute/va-arg-pack-1.c.jj 2007-08-30 21:35:14.000000000 +0200
+++ gcc/testsuite/gcc.c-torture/execute/va-arg-pack-1.c 2007-08-30 21:34:25.000000000 +0200
@@ -0,0 +1,143 @@
+/* __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;
+}
--- gcc/testsuite/gcc.dg/va-arg-pack-1.c.jj 2007-08-30 21:42:32.000000000 +0200
+++ gcc/testsuite/gcc.dg/va-arg-pack-1.c 2007-08-30 21:45:31.000000000 +0200
@@ -0,0 +1,52 @@
+/* { 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;
+}
Jakub
More information about the Gcc-patches
mailing list