2007-09-05 Jakub Jelinek * 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 (©_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/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 + +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" } */ +}