[PATCH] noclobber & noescape annotations for function arguments
Chris Lattner
clattner@apple.com
Thu Apr 15 23:22:00 GMT 2010
On Apr 15, 2010, at 8:06 AM, Richard Guenther wrote:
>
> Well, let's re-post this again. This is a user-visible change. It
> introduces the "fnspec" function attribute through which you can
> specify whether a function clobbers or captures an argument and
> how the return value is behaving.
Hi Richard,
While I can understand the value of having this internally, to mark up builtins, is it really a good idea to make this a user visible feature? This seems like the sort of thing that people will get subtly wrong and will have no idea about until a future version of the compiler gets better and starts "miscompiling" their code.
Why not go a simple and more obvious route of attaching the attributes to the arguments? For example, something like:
void foo(int __attribute__((const)) *x)
could designate that the function only reads from X or something?
-Chris
>
> For example on the testcase
>
> char * __attribute__((noinline,noclone,fnspec("1r")))
> foo (char *x)
> {
> if (x[0] != '\0')
> abort ();
> return x;
> }
>
> int main()
> {
> char x[4];
> char *p;
> x[0] = '\0';
> p = foo (x);
> if (x[0] != '\0')
> link_error ();
> *p = '\1';
> if (x[0] != '\1')
> abort ();
> return 0;
> }
>
> we specify that foo returns its first argument and does neither
> capture it nor clobber the contents it points to.
>
> Thus in main we can CSE the load of x[0].
>
> The fnspec syntax is so that it is easy and fast to parse extending
> it with very many extra nuances will likely make the characters
> non-intuitive though. Extending it with specifications for more
> than the return value or arguments has similar issues.
>
> So - any missing annotation kinds that would be useful to glob
> into this same attribute? As you might have noticed the
> attribute is used by points-to and mod/ref analysis. No
> annotations exist at the moment (known builtins are still handled
> explicitly by the oracle).
>
> Well.
>
> Bootstrapped and tested on x86_64-unknown-linux-gnu.
>
> Richard.
>
> 2009-11-23 Richard Guenther <rguenther@suse.de>
>
> * doc/extend.texi (fnspec): Document.
> * c-common.c (struct c_common_attributes): Add fnspec attribute.
> (handle_fnspec_attribute): New function.
> * gimple.h (gimple_call_return_flags): Declare.
> (gimple_call_arg_flags): Likewise.
> * gimple.c (gimple_call_arg_flags): New function.
> (gimple_call_return_flags): Likewise.
> * tree.h (EAF_DIRECT, EAF_NOCLOBBER, EAF_NOESCAPE, EAF_UNUSED):
> New argument flags.
> (ERF_RETURN_ARG_MASK, ERF_RETURNS_ARG, ERF_NOALIAS): New function
> return value flags.
> * tree-ssa-alias.c (ref_maybe_used_by_call_p_1): Skip unused args.
> * tree-ssa-structalias.c (make_constraint_from_heapvar): Split
> main work to ...
> (make_heapvar_for): ... this new function.
> (handle_rhs_call): Handle fnspec attribute argument specifiers.
> (handle_lhs_call): Likewise.
> (find_func_aliases): Adjust.
>
> * gcc.dg/tree-ssa/ipa-fnann-1.c: New testcase.
> * gcc.dg/tree-ssa/ipa-fnann-2.c: Likewise.
>
>
> Index: trunk/gcc/tree-ssa-structalias.c
> ===================================================================
> *** trunk.orig/gcc/tree-ssa-structalias.c 2010-04-15 16:34:55.000000000 +0200
> --- trunk/gcc/tree-ssa-structalias.c 2010-04-15 16:47:17.000000000 +0200
> *************** make_transitive_closure_constraints (var
> *** 3529,3539 ****
> process_constraint (new_constraint (lhs, rhs));
> }
>
> ! /* Create a new artificial heap variable with NAME and make a
> ! constraint from it to LHS. Return the created variable. */
>
> static varinfo_t
> ! make_constraint_from_heapvar (varinfo_t lhs, const char *name)
> {
> varinfo_t vi;
> tree heapvar = heapvar_lookup (lhs->decl, lhs->offset);
> --- 3529,3539 ----
> process_constraint (new_constraint (lhs, rhs));
> }
>
> ! /* Create a new artificial heap variable with NAME.
> ! Return the created variable. */
>
> static varinfo_t
> ! make_heapvar_for (varinfo_t lhs, const char *name)
> {
> varinfo_t vi;
> tree heapvar = heapvar_lookup (lhs->decl, lhs->offset);
> *************** make_constraint_from_heapvar (varinfo_t
> *** 3565,3570 ****
> --- 3565,3580 ----
> vi->is_full_var = true;
> insert_vi_for_tree (heapvar, vi);
>
> + return vi;
> + }
> +
> + /* Create a new artificial heap variable with NAME and make a
> + constraint from it to LHS. Return the created variable. */
> +
> + static varinfo_t
> + make_constraint_from_heapvar (varinfo_t lhs, const char *name)
> + {
> + varinfo_t vi = make_heapvar_for (lhs, name);
> make_constraint_from (lhs, vi->id);
>
> return vi;
> *************** handle_rhs_call (gimple stmt, VEC(ce_s,
> *** 3636,3652 ****
> {
> struct constraint_expr rhsc;
> unsigned i;
>
> for (i = 0; i < gimple_call_num_args (stmt); ++i)
> {
> tree arg = gimple_call_arg (stmt, i);
>
> ! /* Find those pointers being passed, and make sure they end up
> ! pointing to anything. */
> ! if (could_have_pointers (arg))
> make_escape_constraint (arg);
> }
>
> /* The static chain escapes as well. */
> if (gimple_call_chain (stmt))
> make_escape_constraint (gimple_call_chain (stmt));
> --- 3646,3706 ----
> {
> struct constraint_expr rhsc;
> unsigned i;
> + bool returns_uses = false;
>
> for (i = 0; i < gimple_call_num_args (stmt); ++i)
> {
> tree arg = gimple_call_arg (stmt, i);
> + int flags = gimple_call_arg_flags (stmt, i);
>
> ! /* If the argument is not used or it does not contain pointers
> ! we can ignore it. */
> ! if ((flags & EAF_UNUSED)
> ! || !could_have_pointers (arg))
> ! continue;
> !
> ! /* As we compute ESCAPED context-insensitive we do not gain
> ! any precision with just EAF_NOCLOBBER but not EAF_NOESCAPE
> ! set. The argument would still get clobbered through the
> ! escape solution.
> ! ??? We might get away with less (and more precise) constraints
> ! if using a temporary for transitively closing things. */
> ! if ((flags & EAF_NOCLOBBER)
> ! && (flags & EAF_NOESCAPE))
> ! {
> ! varinfo_t uses = get_call_use_vi (stmt);
> ! if (!(flags & EAF_DIRECT))
> ! make_transitive_closure_constraints (uses);
> ! make_constraint_to (uses->id, arg);
> ! returns_uses = true;
> ! }
> ! else if (flags & EAF_NOESCAPE)
> ! {
> ! varinfo_t uses = get_call_use_vi (stmt);
> ! varinfo_t clobbers = get_call_clobber_vi (stmt);
> ! if (!(flags & EAF_DIRECT))
> ! {
> ! make_transitive_closure_constraints (uses);
> ! make_transitive_closure_constraints (clobbers);
> ! }
> ! make_constraint_to (uses->id, arg);
> ! make_constraint_to (clobbers->id, arg);
> ! returns_uses = true;
> ! }
> ! else
> make_escape_constraint (arg);
> }
>
> + /* If we added to the calls uses solution make sure we account for
> + pointers to it to be returned. */
> + if (returns_uses)
> + {
> + rhsc.var = get_call_use_vi (stmt)->id;
> + rhsc.offset = 0;
> + rhsc.type = SCALAR;
> + VEC_safe_push (ce_s, heap, *results, &rhsc);
> + }
> +
> /* The static chain escapes as well. */
> if (gimple_call_chain (stmt))
> make_escape_constraint (gimple_call_chain (stmt));
> *************** handle_rhs_call (gimple stmt, VEC(ce_s,
> *** 3679,3722 ****
> the LHS point to global and escaped variables. */
>
> static void
> ! handle_lhs_call (tree lhs, int flags, VEC(ce_s, heap) *rhsc, tree fndecl)
> {
> VEC(ce_s, heap) *lhsc = NULL;
>
> get_constraint_for (lhs, &lhsc);
> !
> ! if (flags & ECF_MALLOC)
> {
> varinfo_t vi;
> ! vi = make_constraint_from_heapvar (get_vi_for_tree (lhs), "HEAP");
> /* We delay marking allocated storage global until we know if
> it escapes. */
> DECL_EXTERNAL (vi->decl) = 0;
> vi->is_global_var = 0;
> /* If this is not a real malloc call assume the memory was
> ! initialized and thus may point to global memory. All
> builtin functions with the malloc attribute behave in a sane way. */
> if (!fndecl
> || DECL_BUILT_IN_CLASS (fndecl) != BUILT_IN_NORMAL)
> make_constraint_from (vi, nonlocal_id);
> }
> ! else if (VEC_length (ce_s, rhsc) > 0)
> ! {
> ! /* If the store is to a global decl make sure to
> ! add proper escape constraints. */
> ! lhs = get_base_address (lhs);
> ! if (lhs
> ! && DECL_P (lhs)
> ! && is_global_var (lhs))
> ! {
> ! struct constraint_expr tmpc;
> ! tmpc.var = escaped_id;
> ! tmpc.offset = 0;
> ! tmpc.type = SCALAR;
> ! VEC_safe_push (ce_s, heap, lhsc, &tmpc);
> ! }
> ! process_all_all_constraints (lhsc, rhsc);
> ! }
> VEC_free (ce_s, heap, lhsc);
> }
>
> --- 3733,3795 ----
> the LHS point to global and escaped variables. */
>
> static void
> ! handle_lhs_call (gimple stmt, tree lhs, int flags, VEC(ce_s, heap) *rhsc,
> ! tree fndecl)
> {
> VEC(ce_s, heap) *lhsc = NULL;
>
> get_constraint_for (lhs, &lhsc);
> ! /* If the store is to a global decl make sure to
> ! add proper escape constraints. */
> ! lhs = get_base_address (lhs);
> ! if (lhs
> ! && DECL_P (lhs)
> ! && is_global_var (lhs))
> ! {
> ! struct constraint_expr tmpc;
> ! tmpc.var = escaped_id;
> ! tmpc.offset = 0;
> ! tmpc.type = SCALAR;
> ! VEC_safe_push (ce_s, heap, lhsc, &tmpc);
> ! }
> !
> ! /* If the call returns an argument unmodified override the rhs
> ! constraints. */
> ! flags = gimple_call_return_flags (stmt);
> ! if (flags & ERF_RETURNS_ARG
> ! && (flags & ERF_RETURN_ARG_MASK) < gimple_call_num_args (stmt))
> ! {
> ! tree arg;
> ! rhsc = NULL;
> ! arg = gimple_call_arg (stmt, flags & ERF_RETURN_ARG_MASK);
> ! get_constraint_for (arg, &rhsc);
> ! process_all_all_constraints (lhsc, rhsc);
> ! VEC_free (ce_s, heap, rhsc);
> ! }
> ! else if (flags & ERF_NOALIAS)
> {
> varinfo_t vi;
> ! struct constraint_expr tmpc;
> ! rhsc = NULL;
> ! vi = make_heapvar_for (get_vi_for_tree (lhs), "HEAP");
> /* We delay marking allocated storage global until we know if
> it escapes. */
> DECL_EXTERNAL (vi->decl) = 0;
> vi->is_global_var = 0;
> /* If this is not a real malloc call assume the memory was
> ! initialized and thus may point to global memory. All
> builtin functions with the malloc attribute behave in a sane way. */
> if (!fndecl
> || DECL_BUILT_IN_CLASS (fndecl) != BUILT_IN_NORMAL)
> make_constraint_from (vi, nonlocal_id);
> + tmpc.var = vi->id;
> + tmpc.offset = 0;
> + tmpc.type = ADDRESSOF;
> + VEC_safe_push (ce_s, heap, rhsc, &tmpc);
> }
> !
> ! process_all_all_constraints (lhsc, rhsc);
> !
> VEC_free (ce_s, heap, lhsc);
> }
>
> *************** find_func_aliases (gimple origt)
> *** 4129,4135 ****
> handle_rhs_call (t, &rhsc);
> if (gimple_call_lhs (t)
> && could_have_pointers (gimple_call_lhs (t)))
> ! handle_lhs_call (gimple_call_lhs (t), flags, rhsc, fndecl);
> VEC_free (ce_s, heap, rhsc);
> }
> else
> --- 4202,4208 ----
> handle_rhs_call (t, &rhsc);
> if (gimple_call_lhs (t)
> && could_have_pointers (gimple_call_lhs (t)))
> ! handle_lhs_call (t, gimple_call_lhs (t), flags, rhsc, fndecl);
> VEC_free (ce_s, heap, rhsc);
> }
> else
> Index: trunk/gcc/c-common.c
> ===================================================================
> *** trunk.orig/gcc/c-common.c 2010-04-15 12:04:49.000000000 +0200
> --- trunk/gcc/c-common.c 2010-04-15 16:47:17.000000000 +0200
> *************** static tree handle_type_generic_attribut
> *** 530,535 ****
> --- 530,536 ----
> static tree handle_alloc_size_attribute (tree *, tree, tree, int, bool *);
> static tree handle_target_attribute (tree *, tree, tree, int, bool *);
> static tree handle_optimize_attribute (tree *, tree, tree, int, bool *);
> + static tree handle_fnspec_attribute (tree *, tree, tree, int, bool *);
>
> static void check_function_nonnull (tree, int, tree *);
> static void check_nonnull_arg (void *, tree, unsigned HOST_WIDE_INT);
> *************** const struct attribute_spec c_common_att
> *** 829,834 ****
> --- 830,837 ----
> handle_target_attribute },
> { "optimize", 1, -1, true, false, false,
> handle_optimize_attribute },
> + { "fnspec", 1, 1, false, true, true,
> + handle_fnspec_attribute },
> { NULL, 0, 0, false, false, false, NULL }
> };
>
> *************** handle_alloc_size_attribute (tree *node,
> *** 7132,7137 ****
> --- 7135,7158 ----
> }
> return NULL_TREE;
> }
> +
> + /* Handle a "fnspec" attribute; arguments as in
> + struct attribute_spec.handler. */
> +
> + static tree
> + handle_fnspec_attribute (tree *node ATTRIBUTE_UNUSED, tree ARG_UNUSED (name),
> + tree args,
> + int ARG_UNUSED (flags), bool *no_add_attrs)
> + {
> + if (!args
> + || TREE_CODE (TREE_VALUE (args)) != STRING_CST)
> + {
> + warning (OPT_Wattributes,
> + "fnspec attribute without string argument");
> + *no_add_attrs = true;
> + }
> + return NULL_TREE;
> + }
>
> /* Handle a "returns_twice" attribute; arguments as in
> struct attribute_spec.handler. */
> Index: trunk/gcc/gimple.c
> ===================================================================
> *** trunk.orig/gcc/gimple.c 2010-04-13 11:37:03.000000000 +0200
> --- trunk/gcc/gimple.c 2010-04-15 16:47:17.000000000 +0200
> *************** gimple_call_flags (const_gimple stmt)
> *** 1756,1761 ****
> --- 1756,1850 ----
> return flags;
> }
>
> + /* Detects argument flags for argument number ARG on call STMT. */
> +
> + int
> + gimple_call_arg_flags (const_gimple stmt, unsigned arg)
> + {
> + tree decl;
> + tree attr = NULL_TREE;
> +
> + decl = gimple_call_fndecl (stmt);
> + if (decl != NULL_TREE)
> + attr = lookup_attribute ("fnspec", DECL_ATTRIBUTES (decl));
> + if (!attr)
> + {
> + tree type = TREE_TYPE (TREE_TYPE (gimple_call_fn (stmt)));
> + attr = lookup_attribute ("fnspec", TYPE_ATTRIBUTES (type));
> + }
> + if (!attr)
> + return 0;
> +
> + attr = TREE_VALUE (TREE_VALUE (attr));
> + if (1 + arg >= (unsigned) TREE_STRING_LENGTH (attr))
> + return 0;
> +
> + switch (TREE_STRING_POINTER (attr)[1 + arg])
> + {
> + case 'x':
> + case 'X':
> + return EAF_UNUSED;
> +
> + case 'R':
> + return EAF_DIRECT | EAF_NOCLOBBER | EAF_NOESCAPE;
> +
> + case 'r':
> + return EAF_NOCLOBBER | EAF_NOESCAPE;
> +
> + case 'W':
> + return EAF_DIRECT | EAF_NOESCAPE;
> +
> + case 'w':
> + return EAF_NOESCAPE;
> +
> + case '.':
> + default:
> + return 0;
> + }
> + }
> +
> + /* Detects return flags for the call STMT. */
> +
> + int
> + gimple_call_return_flags (const_gimple stmt)
> + {
> + tree decl;
> + tree attr = NULL_TREE;
> +
> + if (gimple_call_flags (stmt) & ECF_MALLOC)
> + return ERF_NOALIAS;
> +
> + decl = gimple_call_fndecl (stmt);
> + if (decl != NULL_TREE)
> + attr = lookup_attribute ("fnspec", DECL_ATTRIBUTES (decl));
> + if (!attr)
> + {
> + tree type = TREE_TYPE (TREE_TYPE (gimple_call_fn (stmt)));
> + attr = lookup_attribute ("fnspec", TYPE_ATTRIBUTES (type));
> + }
> + if (!attr)
> + return 0;
> +
> + attr = TREE_VALUE (TREE_VALUE (attr));
> + if (TREE_STRING_LENGTH (attr) < 1)
> + return 0;
> +
> + switch (TREE_STRING_POINTER (attr)[0])
> + {
> + case '1':
> + case '2':
> + case '3':
> + case '4':
> + return ERF_RETURNS_ARG | (TREE_STRING_POINTER (attr)[0] - '1');
> +
> + case 'm':
> + return ERF_NOALIAS;
> +
> + case '.':
> + default:
> + return 0;
> + }
> + }
>
> /* Return true if GS is a copy assignment. */
>
> Index: trunk/gcc/gimple.h
> ===================================================================
> *** trunk.orig/gcc/gimple.h 2010-04-15 14:56:48.000000000 +0200
> --- trunk/gcc/gimple.h 2010-04-15 16:47:17.000000000 +0200
> *************** void gimple_seq_free (gimple_seq);
> *** 857,862 ****
> --- 857,864 ----
> void gimple_seq_add_seq (gimple_seq *, gimple_seq);
> gimple_seq gimple_seq_copy (gimple_seq);
> int gimple_call_flags (const_gimple);
> + int gimple_call_return_flags (const_gimple);
> + int gimple_call_arg_flags (const_gimple, unsigned);
> void gimple_call_reset_alias_info (gimple);
> bool gimple_assign_copy_p (gimple);
> bool gimple_assign_ssa_name_copy_p (gimple);
> Index: trunk/gcc/tree-ssa-alias.c
> ===================================================================
> *** trunk.orig/gcc/tree-ssa-alias.c 2010-04-15 15:14:05.000000000 +0200
> --- trunk/gcc/tree-ssa-alias.c 2010-04-15 16:47:17.000000000 +0200
> *************** process_args:
> *** 1097,1102 ****
> --- 1097,1106 ----
> for (i = 0; i < gimple_call_num_args (call); ++i)
> {
> tree op = gimple_call_arg (call, i);
> + int flags = gimple_call_arg_flags (call, i);
> +
> + if (flags & EAF_UNUSED)
> + continue;
>
> if (TREE_CODE (op) == WITH_SIZE_EXPR)
> op = TREE_OPERAND (op, 0);
> Index: trunk/gcc/tree.h
> ===================================================================
> *** trunk.orig/gcc/tree.h 2010-04-15 15:14:05.000000000 +0200
> --- trunk/gcc/tree.h 2010-04-15 16:47:17.000000000 +0200
> *************** extern tree build_duplicate_type (tree);
> *** 5079,5084 ****
> --- 5079,5108 ----
> extern int flags_from_decl_or_type (const_tree);
> extern int call_expr_flags (const_tree);
>
> + /* Call argument flags. */
> +
> + /* Nonzero if the argument is not dereferenced recursively, thus only
> + directly reachable memory is read or written. */
> + #define EAF_DIRECT (1 << 0)
> + /* Nonzero if memory reached by the argument is not clobbered. */
> + #define EAF_NOCLOBBER (1 << 1)
> + /* Nonzero if the argument does not escape. */
> + #define EAF_NOESCAPE (1 << 2)
> + /* Nonzero if the argument is not used by the function. */
> + #define EAF_UNUSED (1 << 3)
> +
> + /* Call return flags. */
> +
> + /* Mask for the argument number that is returned. Lower two bits of
> + the return flags, encodes argument slots zero to three. */
> + #define ERF_RETURN_ARG_MASK (3)
> + /* Nonzero if the return value is equal to the argument number
> + flags & ERF_RETURN_ARG_MASK. */
> + #define ERF_RETURNS_ARG (1 << 2)
> + /* Nonzero if the return value does not alias with anything. Functions
> + with the malloc attribute have this set on their return value. */
> + #define ERF_NOALIAS (1 << 3)
> +
> extern int setjmp_call_p (const_tree);
> extern bool gimple_alloca_call_p (const_gimple);
> extern bool alloca_call_p (const_tree);
> Index: trunk/gcc/testsuite/gcc.dg/tree-ssa/ipa-fnann-1.c
> ===================================================================
> *** /dev/null 1970-01-01 00:00:00.000000000 +0000
> --- trunk/gcc/testsuite/gcc.dg/tree-ssa/ipa-fnann-1.c 2010-04-15 16:47:17.000000000 +0200
> ***************
> *** 0 ****
> --- 1,27 ----
> + /* { dg-do run } */
> + /* { dg-options "-O1" } */
> +
> + extern void link_error (void);
> + extern void abort (void);
> +
> + char * __attribute__((noinline,noclone,fnspec("1r")))
> + foo (char *x)
> + {
> + if (x[0] != '\0')
> + abort ();
> + return x;
> + }
> +
> + int main()
> + {
> + char x[4];
> + char *p;
> + x[0] = '\0';
> + p = foo (x);
> + if (x[0] != '\0')
> + link_error ();
> + *p = '\1';
> + if (x[0] != '\1')
> + abort ();
> + return 0;
> + }
> Index: trunk/gcc/testsuite/gcc.dg/tree-ssa/ipa-fnann-2.c
> ===================================================================
> *** /dev/null 1970-01-01 00:00:00.000000000 +0000
> --- trunk/gcc/testsuite/gcc.dg/tree-ssa/ipa-fnann-2.c 2010-04-15 16:47:17.000000000 +0200
> ***************
> *** 0 ****
> --- 1,41 ----
> + /* { dg-do run } */
> + /* { dg-options "-O1 -fdump-tree-optimized" } */
> +
> + extern void link_error (void);
> + extern void abort (void);
> +
> + struct List {
> + struct List *next;
> + struct List *prev;
> + };
> +
> + void __attribute__((noinline,noclone,fnspec(".W")))
> + foo (struct List *x)
> + {
> + x->next = x;
> + }
> +
> + int main()
> + {
> + struct List head, mid, tail;
> + head.prev = (void *)0;
> + head.next = ∣
> + mid.prev = &head;
> + mid.next = &tail;
> + tail.prev = ∣
> + tail.next = (void *)0;
> + foo (&tail);
> + if (mid.next != &tail)
> + link_error ();
> + if (tail.next != &tail)
> + abort ();
> + return 0;
> + }
> +
> + /* We should have DSEd all stores to mid and head as they are not used. */
> +
> + /* { dg-final { scan-tree-dump-not "mid.prev =" "optimized" } } */
> + /* { dg-final { scan-tree-dump-not "mid.next =" "optimized" } } */
> + /* { dg-final { scan-tree-dump-not "head.prev =" "optimized" } } */
> + /* { dg-final { scan-tree-dump-not "head.next =" "optimized" } } */
> + /* { dg-final { cleanup-tree-dump "optimized" } } */
> Index: trunk/gcc/doc/extend.texi
> ===================================================================
> *** trunk.orig/gcc/doc/extend.texi 2010-04-09 11:01:39.000000000 +0200
> --- trunk/gcc/doc/extend.texi 2010-04-15 16:55:23.000000000 +0200
> *************** The @code{thiscall} attribute is intende
> *** 2335,2340 ****
> --- 2335,2383 ----
> As gcc extension this calling convention can be used for C-functions
> and for static member methods.
>
> + @item fnspec
> + @cindex @code{fnspec} function attribute
> + The @code{fnspec} attribute can be used to specify a functions behavior
> + with respect to clobbering and capturing arguments and return values.
> + The attribute takes a single string argument which encodes the
> + information with the first character representing the return value
> + (even if none) and the following characters the arguments in their
> + natural order. Hidden arguments like @code{this} pointers for C++ methods
> + are included.
> +
> + An unannotated return value or parameter should be denoted with @code{.}.
> +
> + The return value may be annotated with @code{m} which is equivalent
> + to adding the @code{malloc} attribute to the function or with
> + @code{1} to @code{4} for specifying that the function returns
> + the argument with the specified number, counting from one and
> + including hidden arguments.
> +
> + Parameters may be annotated as follows.
> +
> + @table @code
> + @item x
> + @item X
> + The parameter is unused.
> +
> + @item r
> + The parameter is neither clobbered nor captured by the function.
> +
> + @item R
> + The parameter is neither clobbered nor captured by the function
> + and is only directly dereferenced.
> +
> + @item w
> + The parameter is not captured by the function.
> +
> + @item W
> + The parameter is not captured by the function and is only
> + directly dereferenced.
> +
> + @end table
> +
> + Undocumented characters are reserved for future use.
> +
> @item format (@var{archetype}, @var{string-index}, @var{first-to-check})
> @cindex @code{format} function attribute
> @opindex Wformat
More information about the Gcc-patches
mailing list