On Tue, Aug 13, 2024 at 5:34 PM Qing Zhao <qing.z...@oracle.com> wrote:
>
> With the addition of the 'counted_by' attribute and its wide roll-out
> within the Linux kernel, a use case has been found that would be very
> nice to have for object allocators: being able to set the counted_by
> counter variable without knowing its name.
>
> For example, given:
>
>   struct foo {
>     ...
>     int counter;
>     ...
>     struct bar array[] __attribute__((counted_by (counter)));
>   } *p;
>
> The existing Linux object allocators are roughly:
>
>   #define alloc(P, FAM, COUNT) ({ \
>     size_t __size = sizeof(*P) + sizeof(*P->FAM) * COUNT; \
>     kmalloc(__size, GFP); \
>   })
>
> Right now, any addition of a counted_by annotation must also
> include an open-coded assignment of the counter variable after
> the allocation:
>
>   p = alloc(p, array, how_many);
>   p->counter = how_many;
>
> In order to avoid the tedious and error-prone work of manually adding
> the open-coded counted-by intializations everywhere in the Linux
> kernel, a new GCC builtin __builtin_get_counted_by will be very useful
> to be added to help the adoption of the counted-by attribute.
>
>  -- Built-in Function: TYPE __builtin_get_counted_by (PTR)
>      The built-in function '__builtin_get_counted_by' checks whether the
>      array object pointed by the pointer PTR has another object
>      associated with it that represents the number of elements in the
>      array object through the 'counted_by' attribute (i.e.  the
>      counted-by object).  If so, returns a pointer to the corresponding
>      counted-by object.  If such counted-by object does not exist,
>      returns a NULL pointer.
>
>      This built-in function is only available in C for now.
>
>      The argument PTR must be a pointer to an array.  The TYPE of the
>      returned value must be a pointer type pointing to the corresponding
>      type of the counted-by object or a pointer type pointing to the
>      SIZE_T in case of a NULL pointer being returned.
>
> With this new builtin, the central allocator could be updated to:
>
>   #define alloc(P, FAM, COUNT) ({ \
>     typeof(P) __p; \
>     size_t __size = sizeof(*P) + sizeof(*P->FAM) * COUNT; \
>     __p = kmalloc(__size, GFP); \
>     if (__builtin_get_counted_by (__p->FAM)) \
>       *(__builtin_get_counted_by(__p->FAM)) = COUNT; \
>     __p; \
>   })
>
> And then structs can gain the counted_by attribute without needing
> additional open-coded counter assignments for each struct, and
> unannotated structs could still use the same allocator.

Did you consider a __builtin_set_counted_by (PTR, VALUE)?
Note that __builtin_get_counted_by to me suggests it returns the
value and not a pointer to the value.

A more proper language extension might involve a keyword
like __real, so __counted_by X would produce an lvalue, selecting
the counted-by member.  You'd need another way to query whether
'X' has a counted-by member - the apparent runtime test in your
example is odd for a statically typed language, type selection
like with generics or __builtin_classify_type instead of overloading
the value producing builtin might be more fit here?

No objection to the patch but I wanted to share my thoughts here.

Richard.

> Bootstrapped and regression tested on both X86 and aarch64, no issue.
>
> Okay for trunk?
>
> thanks.
>
> Qing.
>
>
>         PR c/116016
>
> gcc/c-family/ChangeLog:
>
>         * c-common.cc: Add new __builtin_get_counted_by.
>         * c-common.h (enum rid): Add RID_BUILTIN_GET_COUNTED_BY.
>
> gcc/c/ChangeLog:
>
>         * c-decl.cc (names_builtin_p): Add RID_BUILTIN_GET_COUNTED_BY.
>         * c-parser.cc (has_counted_by_object): New routine.
>         (get_counted_by_ref): New routine.
>         (c_parser_postfix_expression): Handle New RID_BUILTIN_GET_COUNTED_BY.
>
> gcc/ChangeLog:
>
>         * doc/extend.texi: Add documentation for __builtin_get_counted_by.
>
> gcc/testsuite/ChangeLog:
>
>         * gcc.dg/builtin-get-counted-by-1.c: New test.
>         * gcc.dg/builtin-get-counted-by.c: New test.
> ---
>  gcc/c-family/c-common.cc                      |  1 +
>  gcc/c-family/c-common.h                       |  1 +
>  gcc/c/c-decl.cc                               |  1 +
>  gcc/c/c-parser.cc                             | 72 +++++++++++++++
>  gcc/doc/extend.texi                           | 55 +++++++++++
>  .../gcc.dg/builtin-get-counted-by-1.c         | 91 +++++++++++++++++++
>  gcc/testsuite/gcc.dg/builtin-get-counted-by.c | 54 +++++++++++
>  7 files changed, 275 insertions(+)
>  create mode 100644 gcc/testsuite/gcc.dg/builtin-get-counted-by-1.c
>  create mode 100644 gcc/testsuite/gcc.dg/builtin-get-counted-by.c
>
> diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
> index e7e371fd26f..4b27c6bfeeb 100644
> --- a/gcc/c-family/c-common.cc
> +++ b/gcc/c-family/c-common.cc
> @@ -430,6 +430,7 @@ const struct c_common_resword c_common_reswords[] =
>    { "__builtin_choose_expr", RID_CHOOSE_EXPR, D_CONLY },
>    { "__builtin_complex", RID_BUILTIN_COMPLEX, D_CONLY },
>    { "__builtin_convertvector", RID_BUILTIN_CONVERTVECTOR, 0 },
> +  { "__builtin_get_counted_by", RID_BUILTIN_GET_COUNTED_BY, D_CONLY },
>    { "__builtin_has_attribute", RID_BUILTIN_HAS_ATTRIBUTE, 0 },
>    { "__builtin_launder", RID_BUILTIN_LAUNDER, D_CXXONLY },
>    { "__builtin_shuffle", RID_BUILTIN_SHUFFLE, 0 },
> diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
> index 2510ee4dbc9..5d5a297012f 100644
> --- a/gcc/c-family/c-common.h
> +++ b/gcc/c-family/c-common.h
> @@ -110,6 +110,7 @@ enum rid
>    RID_TYPES_COMPATIBLE_P,      RID_BUILTIN_COMPLEX,       
> RID_BUILTIN_SHUFFLE,
>    RID_BUILTIN_SHUFFLEVECTOR,   RID_BUILTIN_CONVERTVECTOR,  
> RID_BUILTIN_TGMATH,
>    RID_BUILTIN_HAS_ATTRIBUTE,   RID_BUILTIN_ASSOC_BARRIER,  RID_BUILTIN_STDC,
> +  RID_BUILTIN_GET_COUNTED_BY,
>    RID_DFLOAT32, RID_DFLOAT64, RID_DFLOAT128,
>
>    /* TS 18661-3 keywords, in the same sequence as the TI_* values.  */
> diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
> index 8cef8f2c289..40bc808878b 100644
> --- a/gcc/c/c-decl.cc
> +++ b/gcc/c/c-decl.cc
> @@ -11739,6 +11739,7 @@ names_builtin_p (const char *name)
>      case RID_BUILTIN_SHUFFLE:
>      case RID_BUILTIN_SHUFFLEVECTOR:
>      case RID_BUILTIN_STDC:
> +    case RID_BUILTIN_GET_COUNTED_BY:
>      case RID_CHOOSE_EXPR:
>      case RID_OFFSETOF:
>      case RID_TYPES_COMPATIBLE_P:
> diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc
> index 9b9284b1ba4..7b179449fb0 100644
> --- a/gcc/c/c-parser.cc
> +++ b/gcc/c/c-parser.cc
> @@ -10646,6 +10646,35 @@ c_parser_predefined_identifier (c_parser *parser)
>    return expr;
>  }
>
> +/* Check whether the ARRAY_REF has an counted-by object associated with it
> +   through the "counted_by" attribute.  */
> +static bool
> +has_counted_by_object (tree array_ref)
> +{
> +  /* Currently, only when the array_ref is an indirect_ref to a call to the
> +     .ACCESS_WITH_SIZE, return true.
> +     More cases can be included later when the counted_by attribute is
> +     extended to other situations.  */
> +  if ((TREE_CODE (array_ref) == INDIRECT_REF)
> +      && is_access_with_size_p (TREE_OPERAND (array_ref, 0)))
> +    return true;
> +  return false;
> +}
> +
> +/* Get the reference to the counted-by object associated with the ARRAY_REF. 
>  */
> +static tree
> +get_counted_by_ref (tree array_ref)
> +{
> +  /* Currently, only when the array_ref is an indirect_ref to a call to the
> +     .ACCESS_WITH_SIZE, get the corresponding counted_by ref.
> +     More cases can be included later when the counted_by attribute is
> +     extended to other situations.  */
> +  if ((TREE_CODE (array_ref) == INDIRECT_REF)
> +      && is_access_with_size_p (TREE_OPERAND (array_ref, 0)))
> +    return CALL_EXPR_ARG (TREE_OPERAND (array_ref, 0), 1);
> +  return NULL_TREE;
> +}
> +
>  /* Parse a postfix expression (C90 6.3.1-6.3.2, C99 6.5.1-6.5.2,
>     C11 6.5.1-6.5.2).  Compound literals aren't handled here; callers have to
>     call c_parser_postfix_expression_after_paren_type on encountering them.
> @@ -11740,6 +11769,49 @@ c_parser_postfix_expression (c_parser *parser)
>             set_c_expr_source_range (&expr, loc, close_paren_loc);
>             break;
>           }
> +       case RID_BUILTIN_GET_COUNTED_BY:
> +         {
> +           vec<c_expr_t, va_gc> *cexpr_list;
> +           c_expr_t *e_p;
> +           location_t close_paren_loc;
> +
> +           c_parser_consume_token (parser);
> +           if (!c_parser_get_builtin_args (parser,
> +                                           "__builtin_get_counted_by",
> +                                           &cexpr_list, false,
> +                                           &close_paren_loc))
> +             {
> +               expr.set_error ();
> +               break;
> +             }
> +           if (vec_safe_length (cexpr_list) != 1)
> +             {
> +               error_at (loc, "wrong number of arguments to "
> +                              "%<__builtin_get_counted_by%>");
> +               expr.set_error ();
> +               break;
> +             }
> +
> +           e_p = &(*cexpr_list)[0];
> +
> +           if (TREE_CODE (TREE_TYPE (e_p->value)) != ARRAY_TYPE)
> +             {
> +               error_at (loc, "the argument must be an array"
> +                              "%<__builtin_get_counted_by%>");
> +               expr.set_error ();
> +               break;
> +             }
> +
> +           else if (has_counted_by_object ((*cexpr_list)[0].value))
> +             expr.value
> +               = get_counted_by_ref ((*cexpr_list)[0].value);
> +           else
> +             expr.value
> +               = build_int_cst (build_pointer_type (size_type_node), 0);
> +
> +           set_c_expr_source_range (&expr, loc, close_paren_loc);
> +           break;
> +         }
>         case RID_BUILTIN_SHUFFLE:
>           {
>             vec<c_expr_t, va_gc> *cexpr_list;
> diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
> index 48b27ff9f39..485f6f8dd8d 100644
> --- a/gcc/doc/extend.texi
> +++ b/gcc/doc/extend.texi
> @@ -15198,6 +15198,61 @@ initializers of variables usable in constant 
> expressions.   For more details
>  refer to the latest revision of the C++ standard.
>  @enddefbuiltin
>
> +@defbuiltin{@var{type} __builtin_get_counted_by (@var{ptr})}
> +The built-in function @code{__builtin_get_counted_by} checks whether the 
> array
> +object pointed by the pointer @var{ptr} has another object associated with it
> +that represents the number of elements in the array object through the
> +@code{counted_by} attribute (i.e. the counted-by object). If so, returns a
> +pointer to the corresponding counted-by object.
> +If such counted-by object does not exist, returns a NULL pointer.
> +
> +This built-in function is only available in C for now.
> +
> +The argument @var{ptr} must be a pointer to an array.
> +The @var{type} of the returned value must be a pointer type pointing to the
> +corresponding type of the counted-by object or a pointer type pointing to
> +the @var{size_t} in case of a NULL pointer being returned.
> +
> +For example:
> +
> +@smallexample
> +struct foo1 @{
> +  int counter;
> +  struct bar1 array[] __attribute__((counted_by (counter)));
> +@} *p;
> +
> +struct foo2 @{
> +  int other;
> +  struct bar2 array[];
> +@} *q;
> +@end smallexample
> +
> +@noindent
> +the following call to the built-in
> +
> +@smallexample
> +__builtin_get_counted_by (p->array)
> +@end smallexample
> +
> +@noindent
> +returns:
> +
> +@smallexample
> +&p->counter with type @code{int *}
> +@end smallexample
> +
> +@noindent
> +However, the following call to the built-in
> +
> +@smallexample
> +__builtin_get_counted_by (q->array)
> +@end smallexample
> +
> +@noindent
> +returns a NULL pointer with type @code{size_t *}.
> +
> +@enddefbuiltin
> +
>  @defbuiltin{void __builtin_clear_padding (@var{ptr})}
>  The built-in function @code{__builtin_clear_padding} function clears
>  padding bits inside of the object representation of object pointed by
> diff --git a/gcc/testsuite/gcc.dg/builtin-get-counted-by-1.c 
> b/gcc/testsuite/gcc.dg/builtin-get-counted-by-1.c
> new file mode 100644
> index 00000000000..fb195f03970
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/builtin-get-counted-by-1.c
> @@ -0,0 +1,91 @@
> +/* Test the code generation for the new __builtin_get_counted_by.  */
> +/* { dg-do run } */
> +/* { dg-options "-O2" } */
> +#include <stdio.h>
> +
> +struct annotated {
> +  char b;
> +  int c[] __attribute ((counted_by (b)));
> +} *array_annotated;
> +
> +struct flex {
> +  short b;
> +  int c[];
> +} *array_flex;
> +
> +struct nested_annotated {
> +  struct {
> +    union {
> +      int b;
> +      float f;
> +    };
> +    int n;
> +  };
> +  char c[] __attribute__ ((counted_by (b)));
> +} *array_nested_annotated;
> +
> +struct nested_flex {
> +  struct {
> +    union {
> +      unsigned int b;
> +      float f;
> +    };
> +    int n;
> +  };
> +  char c[];
> +} *array_nested_flex;
> +
> +#define MY_ALLOC(P, FAM, COUNT) ({ \
> +  typeof(P) __p; \
> +  size_t __size = sizeof(*P) + sizeof(*P->FAM) * COUNT; \
> +  __p = (typeof(P)) __builtin_malloc(__size); \
> +  __builtin_memset(__p, 0, __size); \
> +  if (__builtin_get_counted_by (__p->FAM)) \
> +  *(__builtin_get_counted_by(__p->FAM)) = COUNT; \
> +  P = __p; \
> +})
> +
> +int count;
> +
> +int main(int argc, char *argv[])
> +{
> +  MY_ALLOC(array_annotated, c, 10);
> +  if (array_annotated->b != 10)
> +    __builtin_abort ();
> +
> +  MY_ALLOC(array_flex, c, 20);
> +  if (array_flex->b == 20)
> +    __builtin_abort ();
> +
> +  MY_ALLOC(array_nested_annotated, c, 30);
> +  if (array_nested_annotated->b != 30)
> +    __builtin_abort ();
> +
> +  MY_ALLOC(array_nested_flex, c, 40);
> +  if (array_nested_flex->b == 40)
> +    __builtin_abort ();
> +
> +  count = array_annotated->b * 2 + array_nested_annotated->b * 3;
> +  struct annotated * annotated_p;
> +  struct flex * flex_p;
> +  struct nested_annotated * nested_annotated_p;
> +  struct nested_flex * nested_flex_p;
> +
> +  MY_ALLOC(annotated_p, c, count);
> +  if (annotated_p->b != count)
> +    __builtin_abort ();
> +
> +  MY_ALLOC(flex_p, c, count * 2);
> +  if (flex_p->b == count * 2)
> +    __builtin_abort ();
> +
> +  MY_ALLOC(nested_annotated_p, c, count * 3);
> +  if (nested_annotated_p->b != count * 3)
> +    __builtin_abort ();
> +
> +  MY_ALLOC(nested_flex_p, c, count * 4);
> +  if (nested_flex_p->b == count * 4)
> +    __builtin_abort ();
> +
> +  return 0;
> +}
> diff --git a/gcc/testsuite/gcc.dg/builtin-get-counted-by.c 
> b/gcc/testsuite/gcc.dg/builtin-get-counted-by.c
> new file mode 100644
> index 00000000000..5eca12bc992
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/builtin-get-counted-by.c
> @@ -0,0 +1,54 @@
> +/* Testing the correct usage of the new __builtin_get_counted_by.  */
> +/* { dg-do compile } */
> +/* { dg-options "-O" } */
> +
> +#include <stdio.h>
> +
> +struct annotated {
> +  size_t b;
> +  int other;
> +  int c[] __attribute ((counted_by (b)));
> +} *array_annotated;
> +
> +struct flex {
> +  size_t b;
> +  int other;
> +  int c[];
> +} *array_flex;
> +
> +#define MY_ALLOC(P, FAM, COUNT) ({ \
> +  typeof(P) __p; \
> +  size_t __size = sizeof(*P) + sizeof(*P->FAM) * COUNT; \
> +  __p = (typeof(P)) __builtin_malloc(__size); \
> +  if (__builtin_get_counted_by (__p->FAM)) \
> +    *(__builtin_get_counted_by(__p->FAM)) = COUNT; \
> +  __p; \
> +})
> +
> +extern char c_count;
> +extern short s_count;
> +extern int i_count;
> +extern long l_count;
> +extern float f_count;
> +
> +extern int * foo ();
> +
> +int main(int argc, char *argv[])
> +{
> +  /* The good usages.  */
> +  MY_ALLOC(array_annotated, c, 10);
> +  MY_ALLOC(array_flex, c, 20);
> +  MY_ALLOC(array_annotated, c, c_count);
> +  MY_ALLOC(array_flex, c, i_count);
> +  MY_ALLOC(array_annotated, c, l_count);
> +  MY_ALLOC(array_flex, c, c_count * 3);
> +  MY_ALLOC(array_annotated, c, l_count * i_count);
> +
> +  /* The bad usages, issue errors.  */
> +  __builtin_get_counted_by (); /* { dg-error "wrong number of arguments to" 
> } */
> +  __builtin_get_counted_by (array_annotated->c, 10); /* { dg-error "wrong 
> number of arguments to" } */
> +  __builtin_get_counted_by (array_annotated->other); /* { dg-error "the 
> argument must be an array" } */
> +  __builtin_get_counted_by (foo());  /* { dg-error "the argument must be an 
> array" } */
> +
> +  return 0;
> +}
> --
> 2.31.1
>

Reply via email to