[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