gcc/gcc41-builtin-va-arg-pack.p...

723 lines
20 KiB
Diff

2007-09-07 Jakub Jelinek <jakub@redhat.com>
* tree.c (cp_cannot_inline_tree_fn): Don't mark varargs_function_p
as uninlinable.
* g++.dg/ext/va-arg-pack-1.C: New test.
* g++.dg/ext/va-arg-pack-2.C: New test.
2007-09-05 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_builtin_1): Don't fold 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.
* fold-const.c (fold): Don't fold CALL_EXPRs with
CALL_EXPR_VA_ARG_PACK bit set.
* 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.dg/va-arg-pack-1a.c: New test.
--- gcc/builtins.def.jj 2007-09-06 10:19:09.000000000 +0200
+++ gcc/builtins.def 2007-09-06 10:19:24.000000000 +0200
@@ -665,6 +665,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-02-20 22:39:12.000000000 +0100
+++ gcc/tree.h 2007-09-06 11:50:56.000000000 +0200
@@ -411,6 +411,8 @@ struct tree_common 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
@@ -1066,6 +1068,11 @@ extern void omp_clause_range_check_faile
#define SAVE_EXPR_RESOLVED_P(NODE) \
(TREE_CHECK (NODE, SAVE_EXPR)->common.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)->common.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-02-20 22:39:13.000000000 +0100
+++ gcc/tree-inline.h 2007-09-06 10:19:24.000000000 +0200
@@ -57,6 +57,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
--- gcc/tree-inline.c.jj 2007-04-26 09:41:49.000000000 +0200
+++ gcc/tree-inline.c 2007-09-06 12:35:57.000000000 +0200
@@ -725,6 +725,22 @@ copy_bb (copy_body_data *id, basic_block
bsi_insert_after (&copy_bsi, stmt, BSI_NEW_STMT);
call = get_call_expr_in (stmt);
+ if (call && CALL_EXPR_VA_ARG_PACK (call) && id->call_expr)
+ {
+ tree arglist, *a, p;
+ TREE_OPERAND (call, 1) = copy_list (TREE_OPERAND (call, 1));
+
+ for (a = &TREE_OPERAND (call, 1); *a; a = &TREE_CHAIN (*a))
+ ;
+
+ p = DECL_ARGUMENTS (id->src_fn);
+ for (arglist = TREE_OPERAND (id->call_expr, 1);
+ p; p = TREE_CHAIN (p), arglist = TREE_CHAIN (arglist))
+ ;
+
+ *a = copy_list (arglist);
+ CALL_EXPR_VA_ARG_PACK (call) = 0;
+ }
/* We're duplicating a CALL_EXPR. Find any corresponding
callgraph edges and update or duplicate them. */
if (call && (decl = get_callee_fndecl (call)))
@@ -2085,6 +2101,7 @@ expand_call_inline (basic_block bb, tree
/* Record the function we are about to inline. */
id->src_fn = fn;
id->src_node = cg_edge->callee;
+ id->call_expr = t;
initialize_inlined_parameters (id, args, TREE_OPERAND (t, 2), fn, bb);
--- gcc/builtins.c.jj 2007-08-31 09:47:46.000000000 +0200
+++ gcc/builtins.c 2007-09-06 11:04:56.000000000 +0200
@@ -5979,6 +5979,12 @@ expand_builtin (tree exp, rtx target, rt
case BUILT_IN_ARGS_INFO:
return expand_builtin_args_info (arglist);
+ 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 (arglist))
@@ -9017,7 +9023,27 @@ fold_builtin_1 (tree fndecl, tree arglis
{
tree type = TREE_TYPE (TREE_TYPE (fndecl));
enum built_in_function fcode;
+ tree a;
+
+ if (arglist)
+ {
+ for (a = arglist; TREE_CHAIN (a); a = TREE_CHAIN (a))
+ ;
+ /* 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 (TREE_CODE (TREE_VALUE (a)) == CALL_EXPR)
+ {
+ tree fndecl2 = get_callee_fndecl (TREE_VALUE (a));
+ 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;
+ }
+ }
+
if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD)
return targetm.fold_builtin (fndecl, arglist, ignore);
--- gcc/gimplify.c.jj 2007-08-31 09:47:46.000000000 +0200
+++ gcc/gimplify.c 2007-09-06 11:42:01.000000000 +0200
@@ -2018,6 +2018,55 @@ gimplify_call_expr (tree *expr_p, tree *
ret = gimplify_expr (&TREE_OPERAND (*expr_p, 0), pre_p, NULL,
is_gimple_call_addr, fb_rvalue);
+ if (ret != GS_ERROR && TREE_OPERAND (*expr_p, 1))
+ {
+ int nargs;
+
+ for (nargs = 1, arglist = TREE_OPERAND (*expr_p, 1);
+ TREE_CHAIN (arglist);
+ arglist = TREE_CHAIN (arglist))
+ nargs++;
+
+ if (TREE_CODE (TREE_VALUE (arglist)) == CALL_EXPR)
+ {
+ tree last_arg = TREE_VALUE (arglist);
+ 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 p = NULL_TREE, *aptr;
+ int i;
+
+ if (decl && DECL_ARGUMENTS (decl))
+ p = DECL_ARGUMENTS (decl);
+ else if (decl)
+ p = TYPE_ARG_TYPES (TREE_TYPE (decl));
+ else if (POINTER_TYPE_P (TREE_TYPE (TREE_OPERAND (*expr_p, 0))))
+ p = TYPE_ARG_TYPES (TREE_TYPE (TREE_TYPE
+ (TREE_OPERAND (*expr_p, 0))));
+ for (i = 0; p; p = TREE_CHAIN (p))
+ i++;
+
+ if (i < nargs)
+ {
+ TREE_OPERAND (*expr_p, 1)
+ = copy_list (TREE_OPERAND (*expr_p, 1));
+
+ for (aptr = &TREE_OPERAND (*expr_p, 1);
+ TREE_CHAIN (*aptr);
+ aptr = &TREE_CHAIN (*aptr))
+ ;
+
+ *aptr = NULL_TREE;
+ CALL_EXPR_VA_ARG_PACK (*expr_p) = 1;
+ }
+ }
+ }
+ }
+
if (PUSH_ARGS_REVERSED)
TREE_OPERAND (*expr_p, 1) = nreverse (TREE_OPERAND (*expr_p, 1));
for (arglist = TREE_OPERAND (*expr_p, 1); arglist;
@@ -2037,7 +2086,7 @@ gimplify_call_expr (tree *expr_p, tree *
if (ret != GS_ERROR)
{
decl = get_callee_fndecl (*expr_p);
- if (decl && DECL_BUILT_IN (decl))
+ if (decl && DECL_BUILT_IN (decl) && !CALL_EXPR_VA_ARG_PACK (*expr_p))
{
tree arglist = TREE_OPERAND (*expr_p, 1);
tree new = fold_builtin (decl, arglist, !want_value);
--- gcc/fold-const.c.jj 2007-08-31 09:47:39.000000000 +0200
+++ gcc/fold-const.c 2007-09-06 11:40:55.000000000 +0200
@@ -10409,6 +10409,8 @@ fold (tree expr)
op0 = TREE_OPERAND (t, 0);
op1 = TREE_OPERAND (t, 1);
op2 = TREE_OPERAND (t, 2);
+ if (code == CALL_EXPR && CALL_EXPR_VA_ARG_PACK (t))
+ return expr;
tem = fold_ternary (code, type, op0, op1, op2);
return tem ? tem : expr;
default:
--- gcc/expr.c.jj 2007-04-24 22:58:53.000000000 +0200
+++ gcc/expr.c 2007-09-06 10:19:24.000000000 +0200
@@ -7475,6 +7475,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 (TREE_OPERAND (exp, 0)) == ADDR_EXPR
&& (TREE_CODE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0))
--- gcc/doc/extend.texi.jj 2007-08-31 13:00:28.000000000 +0200
+++ gcc/doc/extend.texi 2007-09-06 10:19:25.000000000 +0200
@@ -552,6 +552,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-02-20 22:39:12.000000000 +0100
+++ gcc/tree-pretty-print.c 2007-09-06 10:24:51.000000000 +0200
@@ -1128,6 +1128,15 @@ dump_generic_node (pretty_printer *buffe
op1 = TREE_OPERAND (node, 1);
if (op1)
dump_generic_node (buffer, op1, spc, flags, false);
+ if (CALL_EXPR_VA_ARG_PACK (node))
+ {
+ if (op1)
+ {
+ pp_character (buffer, ',');
+ pp_space (buffer);
+ }
+ pp_string (buffer, "__builtin_va_arg_pack ()");
+ }
pp_character (buffer, ')');
op1 = TREE_OPERAND (node, 2);
--- gcc/cp/tree.c.jj 2007-03-12 08:28:01.000000000 +0100
+++ gcc/cp/tree.c 2007-09-07 09:03:07.000000000 +0200
@@ -2078,12 +2078,6 @@ cp_cannot_inline_tree_fn (tree* fnp)
return 1;
}
- if (varargs_function_p (fn))
- {
- DECL_UNINLINABLE (fn) = 1;
- return 1;
- }
-
if (! function_attribute_inlinable_p (fn))
{
DECL_UNINLINABLE (fn) = 1;
--- gcc/testsuite/gcc.c-torture/execute/va-arg-pack-1.c.jj 2007-09-06 10:19:25.000000000 +0200
+++ gcc/testsuite/gcc.c-torture/execute/va-arg-pack-1.c 2007-09-06 10:19: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-09-06 10:19:25.000000000 +0200
+++ gcc/testsuite/gcc.dg/va-arg-pack-1.c 2007-09-06 12:52:00.000000000 +0200
@@ -0,0 +1,46 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+int bar (int, const char *, int, ...);
+int baz (int, const char *, long int);
+
+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;
+}
--- gcc/testsuite/gcc.dg/va-arg-pack-1a.c.jj 2007-09-06 12:51:51.000000000 +0200
+++ gcc/testsuite/gcc.dg/va-arg-pack-1a.c 2007-09-06 12:52:09.000000000 +0200
@@ -0,0 +1,11 @@
+/* { 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" } */
+}
--- gcc/testsuite/g++.dg/ext/va-arg-pack-1.C.jj 2007-09-07 08:52:55.000000000 +0200
+++ gcc/testsuite/g++.dg/ext/va-arg-pack-1.C 2007-09-07 08:53:45.000000000 +0200
@@ -0,0 +1,145 @@
+// __builtin_va_arg_pack () builtin tests.
+// { dg-do run }
+// { dg-options "-O2" }
+
+#include <stdarg.h>
+
+extern "C" 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/g++.dg/ext/va-arg-pack-2.C.jj 2007-09-07 08:53:23.000000000 +0200
+++ gcc/testsuite/g++.dg/ext/va-arg-pack-2.C 2007-09-07 08:54:25.000000000 +0200
@@ -0,0 +1,46 @@
+// { dg-do compile }
+// { dg-options "-O2" }
+
+int bar (int, const char *, int, ...);
+int baz (int, const char *, long int);
+
+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;
+}