diff mbox series

[v5] Provide new GCC builtin __builtin_counted_by_ref [PR116016]

Message ID 20240911211340.3986604-1-qing.zhao@oracle.com
State New
Headers show
Series [v5] Provide new GCC builtin __builtin_counted_by_ref [PR116016] | expand

Commit Message

Qing Zhao Sept. 11, 2024, 9:13 p.m. UTC
compared to the 4th version, the changes are (address Jacub's concerns):

1. change the global "in_builtin_counted_by_ref" from a boolean to an int;
2. delete the label for the error handling code, and decress the global
   "in_builtin_counted_by_ref" before each break; 

the 4th version compared to the 3rd version, the only change is the 
size calculation in the testing case.

The 3rd version compared to the 2nd version, the major change is:
the update in testing cases per Martin's suggestions.

when the 2nd version is compared to the first version, the major changes are:

1. change the name of the builtin from __builtin_get_counted_by to
__builtin_counted_by_ref in order to reflect the fact that the returned
value of it is a reference to the object.

2. make typeof(__builtin_counted_by_ref) working.

3. update the testing case to use the new builtin inside _Generic.

bootstrapped and regress tested on both X86 and aarch64. no issue.

Okay for the trunk?

thanks.

Qing.

==============================================

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 MAX(A, B) (A > B) ? (A) : (B)
  #define alloc(P, FAM, COUNT) ({ \
    __auto_type __p = &(P); \
    size_t __size = MAX (sizeof(*P),
			 __builtin_offsetof (__typeof(*P), FAM)
			 + sizeof (*(P->FAM)) * COUNT); \
    *__p = kmalloc(__size); \
  })

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_counted_by_ref will be very useful
to be added to help the adoption of the counted-by attribute.

 -- Built-in Function: TYPE __builtin_counted_by_ref (PTR)
     The built-in function '__builtin_counted_by_ref' 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 VOID pointer type in case of a
     NULL pointer being returned.

With this new builtin, the central allocator could be updated to:

  #define MAX(A, B) (A > B) ? (A) : (B)
  #define alloc(P, FAM, COUNT) ({ \
    __auto_type __p = &(P); \
    __auto_type __c = (COUNT); \
    size_t __size = MAX (sizeof (*(*__p)),\
			 __builtin_offsetof (__typeof(*(*__p)),FAM) \
			 + sizeof (*((*__p)->FAM)) * __c); \
    if ((*__p = kmalloc(__size))) { \
      __auto_type ret = __builtin_counted_by_ref((*__p)->FAM); \
      *_Generic(ret, void *: &(size_t){0}, default: ret) = __c; \
    } \
  })

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.

	PR c/116016

gcc/c-family/ChangeLog:

	* c-common.cc: Add new __builtin_counted_by_ref.
	* c-common.h (enum rid): Add RID_BUILTIN_COUNTED_BY_REF.

gcc/c/ChangeLog:

	* c-decl.cc (names_builtin_p): Add RID_BUILTIN_COUNTED_BY_REF.
	* c-parser.cc (has_counted_by_object): New routine.
	(get_counted_by_ref): New routine.
	(c_parser_postfix_expression): Handle New RID_BUILTIN_COUNTED_BY_REF.
	* c-tree.h: New global in_builtin_counted_by_ref.
	* c-typeck.cc (build_component_ref): Enable generating
	.ACCESS_WITH_SIZE inside typeof when inside builtin_counted_by_ref.

gcc/ChangeLog:

	* doc/extend.texi: Add documentation for __builtin_counted_by_ref.

gcc/testsuite/ChangeLog:

	* gcc.dg/builtin-counted-by-ref-1.c: New test.
	* gcc.dg/builtin-counted-by-ref.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                             | 78 +++++++++++++++
 gcc/c/c-tree.h                                |  1 +
 gcc/c/c-typeck.cc                             |  7 +-
 gcc/doc/extend.texi                           | 55 +++++++++++
 .../gcc.dg/builtin-counted-by-ref-1.c         | 97 +++++++++++++++++++
 gcc/testsuite/gcc.dg/builtin-counted-by-ref.c | 61 ++++++++++++
 9 files changed, 301 insertions(+), 1 deletion(-)
 create mode 100644 gcc/testsuite/gcc.dg/builtin-counted-by-ref-1.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-counted-by-ref.c

Comments

Bill Wendling Sept. 11, 2024, 10:16 p.m. UTC | #1
On Wed, Sep 11, 2024 at 2:13 PM Qing Zhao <qing.zhao@oracle.com> wrote:
>
> compared to the 4th version, the changes are (address Jacub's concerns):
>
> 1. change the global "in_builtin_counted_by_ref" from a boolean to an int;
> 2. delete the label for the error handling code, and decress the global
>    "in_builtin_counted_by_ref" before each break;
>
> the 4th version compared to the 3rd version, the only change is the
> size calculation in the testing case.
>
> The 3rd version compared to the 2nd version, the major change is:
> the update in testing cases per Martin's suggestions.
>
> when the 2nd version is compared to the first version, the major changes are:
>
> 1. change the name of the builtin from __builtin_get_counted_by to
> __builtin_counted_by_ref in order to reflect the fact that the returned
> value of it is a reference to the object.
>
> 2. make typeof(__builtin_counted_by_ref) working.
>
> 3. update the testing case to use the new builtin inside _Generic.
>
> bootstrapped and regress tested on both X86 and aarch64. no issue.
>
> Okay for the trunk?
>
> thanks.
>
> Qing.
>
> ==============================================
>
> 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 MAX(A, B) (A > B) ? (A) : (B)
>   #define alloc(P, FAM, COUNT) ({ \
>     __auto_type __p = &(P); \
>     size_t __size = MAX (sizeof(*P),
>                          __builtin_offsetof (__typeof(*P), FAM)
>                          + sizeof (*(P->FAM)) * COUNT); \
>     *__p = kmalloc(__size); \
>   })
>
> 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_counted_by_ref will be very useful
> to be added to help the adoption of the counted-by attribute.
>
>  -- Built-in Function: TYPE __builtin_counted_by_ref (PTR)
>      The built-in function '__builtin_counted_by_ref' 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 VOID pointer type in case of a
>      NULL pointer being returned.
>
> With this new builtin, the central allocator could be updated to:
>
>   #define MAX(A, B) (A > B) ? (A) : (B)
>   #define alloc(P, FAM, COUNT) ({ \
>     __auto_type __p = &(P); \
>     __auto_type __c = (COUNT); \
>     size_t __size = MAX (sizeof (*(*__p)),\
>                          __builtin_offsetof (__typeof(*(*__p)),FAM) \
>                          + sizeof (*((*__p)->FAM)) * __c); \
>     if ((*__p = kmalloc(__size))) { \
>       __auto_type ret = __builtin_counted_by_ref((*__p)->FAM); \
>       *_Generic(ret, void *: &(size_t){0}, default: ret) = __c; \
>     } \
>   })
>
> 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.
>
>         PR c/116016
>
> gcc/c-family/ChangeLog:
>
>         * c-common.cc: Add new __builtin_counted_by_ref.
>         * c-common.h (enum rid): Add RID_BUILTIN_COUNTED_BY_REF.
>
> gcc/c/ChangeLog:
>
>         * c-decl.cc (names_builtin_p): Add RID_BUILTIN_COUNTED_BY_REF.
>         * c-parser.cc (has_counted_by_object): New routine.
>         (get_counted_by_ref): New routine.
>         (c_parser_postfix_expression): Handle New RID_BUILTIN_COUNTED_BY_REF.
>         * c-tree.h: New global in_builtin_counted_by_ref.
>         * c-typeck.cc (build_component_ref): Enable generating
>         .ACCESS_WITH_SIZE inside typeof when inside builtin_counted_by_ref.
>
> gcc/ChangeLog:
>
>         * doc/extend.texi: Add documentation for __builtin_counted_by_ref.
>
> gcc/testsuite/ChangeLog:
>
>         * gcc.dg/builtin-counted-by-ref-1.c: New test.
>         * gcc.dg/builtin-counted-by-ref.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                             | 78 +++++++++++++++
>  gcc/c/c-tree.h                                |  1 +
>  gcc/c/c-typeck.cc                             |  7 +-
>  gcc/doc/extend.texi                           | 55 +++++++++++
>  .../gcc.dg/builtin-counted-by-ref-1.c         | 97 +++++++++++++++++++
>  gcc/testsuite/gcc.dg/builtin-counted-by-ref.c | 61 ++++++++++++
>  9 files changed, 301 insertions(+), 1 deletion(-)
>  create mode 100644 gcc/testsuite/gcc.dg/builtin-counted-by-ref-1.c
>  create mode 100644 gcc/testsuite/gcc.dg/builtin-counted-by-ref.c
>
> diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
> index e7e371fd26f..15b90bae8b5 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_counted_by_ref", RID_BUILTIN_COUNTED_BY_REF, 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 027f077d51b..4400d2c5d43 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_COUNTED_BY_REF,
>    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 aa7f69d1b7b..1145dde9bb1 100644
> --- a/gcc/c/c-decl.cc
> +++ b/gcc/c/c-decl.cc
> @@ -11788,6 +11788,7 @@ names_builtin_p (const char *name)
>      case RID_BUILTIN_SHUFFLE:
>      case RID_BUILTIN_SHUFFLEVECTOR:
>      case RID_BUILTIN_STDC:
> +    case RID_BUILTIN_COUNTED_BY_REF:
>      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 aff5af17430..91bd91f4be1 100644
> --- a/gcc/c/c-parser.cc
> +++ b/gcc/c/c-parser.cc
> @@ -10647,6 +10647,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.
> @@ -11741,6 +11770,55 @@ c_parser_postfix_expression (c_parser *parser)
>             set_c_expr_source_range (&expr, loc, close_paren_loc);
>             break;
>           }
> +       case RID_BUILTIN_COUNTED_BY_REF:
> +         {
> +           vec<c_expr_t, va_gc> *cexpr_list;
> +           c_expr_t *e_p;
> +           location_t close_paren_loc;
> +
> +           in_builtin_counted_by_ref++;
> +
> +           c_parser_consume_token (parser);
> +           if (!c_parser_get_builtin_args (parser,
> +                                           "__builtin_counted_by_ref",
> +                                           &cexpr_list, false,
> +                                           &close_paren_loc))
> +             {
> +               expr.set_error ();
> +               in_builtin_counted_by_ref--;
> +               break;
> +             }
> +           if (vec_safe_length (cexpr_list) != 1)
> +             {
> +               error_at (loc, "wrong number of arguments to "
> +                              "%<__builtin_counted_by_ref%>");
> +               expr.set_error ();
> +               in_builtin_counted_by_ref--;
> +               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_counted_by_ref%>");
> +               expr.set_error ();
> +               in_builtin_counted_by_ref--;
> +               break;
> +             }
> +
> +           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 (void_type_node), 0);
> +
> +           set_c_expr_source_range (&expr, loc, close_paren_loc);
> +           in_builtin_counted_by_ref--;
> +           break;
> +         }
>         case RID_BUILTIN_SHUFFLE:
>           {
>             vec<c_expr_t, va_gc> *cexpr_list;
> diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h
> index 57befb94c08..6f46b2ff5a0 100644
> --- a/gcc/c/c-tree.h
> +++ b/gcc/c/c-tree.h
> @@ -737,6 +737,7 @@ extern int c_type_dwarf_attribute (const_tree, int);
>  extern int in_alignof;
>  extern int in_sizeof;
>  extern int in_typeof;
> +extern int in_builtin_counted_by_ref;
>  extern bool c_in_omp_for;
>  extern bool c_omp_array_section_p;
>
> diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
> index 58b2724b39e..8cb2cb60f6a 100644
> --- a/gcc/c/c-typeck.cc
> +++ b/gcc/c/c-typeck.cc
> @@ -74,6 +74,9 @@ int in_sizeof;
>  /* The level of nesting inside "typeof".  */
>  int in_typeof;
>
> +/* The level of nesting inside "builtin_counted_by_ref".  */
> +int in_builtin_counted_by_ref;
> +
>  /* True when parsing OpenMP loop expressions.  */
>  bool c_in_omp_for;
>
> @@ -2848,7 +2851,9 @@ build_component_ref (location_t loc, tree datum, tree component,
>           bool use_datum_quals;
>           tree counted_by_type = NULL_TREE;
>           /* Do not handle counted_by when in typeof and alignof operator.  */
> -         handle_counted_by = handle_counted_by && !in_typeof && !in_alignof;
> +         handle_counted_by = handle_counted_by
> +                             && !(in_typeof && !in_builtin_counted_by_ref)
> +                             && !in_alignof;
>           tree counted_by_ref = handle_counted_by
>                                 ? build_counted_by_ref (datum, subdatum,
>                                                         &counted_by_type)
> diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
> index 2d795ba7e59..268e50677d7 100644
> --- a/gcc/doc/extend.texi
> +++ b/gcc/doc/extend.texi
> @@ -15287,6 +15287,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_counted_by_ref (@var{ptr})}
> +The built-in function @code{__builtin_counted_by_ref} 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 VOID pointer type in case
> +of a NULL pointer being returned.
> +
Having the return type default to a VOID pointer restricts it from this use:

  if (__builtin_counted_by_ref(p->FAM))
    *__builtin_counted_by_ref(p->FAM) = 42;

The second line will emit a diagnostic. Is VOID pointer just to make
using _Generic a bit cleaner? Is that now the prefered way to use this
builtin?

-bw

> +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_counted_by_ref (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_counted_by_ref (q->array)
> +@end smallexample
> +
> +@noindent
> +returns a void NULL pointer.
> +
> +@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-counted-by-ref-1.c b/gcc/testsuite/gcc.dg/builtin-counted-by-ref-1.c
> new file mode 100644
> index 00000000000..d055cf1defc
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/builtin-counted-by-ref-1.c
> @@ -0,0 +1,97 @@
> +/* Test the code generation for the new __builtin_counted_by_ref.  */
> +/* { 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 MAX(A, B) (A > B) ? (A) : (B)
> +#define MY_ALLOC(P, FAM, COUNT) ({ \
> +  __auto_type __p = &(P); \
> +  __auto_type __c = (COUNT); \
> +  size_t __size = MAX (sizeof (*(*__p)),\
> +                      __builtin_offsetof (__typeof(*(*__p)),FAM) \
> +                      + sizeof (*((*__p)->FAM)) * __c); \
> +  if ((*__p = __builtin_malloc(__size))) { \
> +    __builtin_memset(*__p, 0, __size); \
> +    __auto_type ret = __builtin_counted_by_ref((*__p)->FAM); \
> +    *_Generic(ret, void *: &(size_t){0}, default: ret) = __c; \
> +    if (sizeof (__builtin_counted_by_ref ((*__p)->FAM)) != sizeof (char *)) \
> +      __builtin_abort (); \
> +  } \
> +})
> +
> +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-counted-by-ref.c b/gcc/testsuite/gcc.dg/builtin-counted-by-ref.c
> new file mode 100644
> index 00000000000..4aa57ffb916
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/builtin-counted-by-ref.c
> @@ -0,0 +1,61 @@
> +/* Testing the correct usage of the new __builtin_counted_by_ref.  */
> +/* { 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 MAX(A, B) (A > B) ? (A) : (B)
> +#define MY_ALLOC(P, FAM, COUNT) ({ \
> +  __auto_type __p = &(P); \
> +  __auto_type __c = (COUNT); \
> +  size_t __size = MAX (sizeof (*(*__p)),\
> +                      __builtin_offsetof (__typeof(*(*__p)),FAM) \
> +                      + sizeof (*((*__p)->FAM)) * __c); \
> +  if ((*__p = __builtin_malloc(__size))) { \
> +    __builtin_memset(*__p, 0, __size); \
> +    __auto_type ret = __builtin_counted_by_ref((*__p)->FAM); \
> +    *_Generic(ret, void *: &(size_t){0}, default: ret) = __c; \
> +    if (sizeof (__builtin_counted_by_ref ((*__p)->FAM)) != sizeof (char *)) \
> +      __builtin_abort (); \
> +  } \
> +})
> +
> +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_counted_by_ref (); /* { dg-error "wrong number of arguments to" } */
> +  __builtin_counted_by_ref (array_annotated->c, 10); /* { dg-error "wrong number of arguments to" } */
> +  __builtin_counted_by_ref (array_annotated->other); /* { dg-error "the argument must be an array" } */
> +  __builtin_counted_by_ref (foo());  /* { dg-error "the argument must be an array" } */
> +
> +  return 0;
> +}
> --
> 2.31.1
>
Qing Zhao Sept. 12, 2024, 1:04 a.m. UTC | #2
> On Sep 11, 2024, at 18:16, Bill Wendling <isanbard@gmail.com> wrote:
> 
> On Wed, Sep 11, 2024 at 2:13 PM Qing Zhao <qing.zhao@oracle.com> wrote:
>> 
>> compared to the 4th version, the changes are (address Jacub's concerns):
>> 
>> 1. change the global "in_builtin_counted_by_ref" from a boolean to an int;
>> 2. delete the label for the error handling code, and decress the global
>>   "in_builtin_counted_by_ref" before each break;
>> 
>> the 4th version compared to the 3rd version, the only change is the
>> size calculation in the testing case.
>> 
>> The 3rd version compared to the 2nd version, the major change is:
>> the update in testing cases per Martin's suggestions.
>> 
>> when the 2nd version is compared to the first version, the major changes are:
>> 
>> 1. change the name of the builtin from __builtin_get_counted_by to
>> __builtin_counted_by_ref in order to reflect the fact that the returned
>> value of it is a reference to the object.
>> 
>> 2. make typeof(__builtin_counted_by_ref) working.
>> 
>> 3. update the testing case to use the new builtin inside _Generic.
>> 
>> bootstrapped and regress tested on both X86 and aarch64. no issue.
>> 
>> Okay for the trunk?
>> 
>> thanks.
>> 
>> Qing.
>> 
>> ==============================================
>> 
>> 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 MAX(A, B) (A > B) ? (A) : (B)
>>  #define alloc(P, FAM, COUNT) ({ \
>>    __auto_type __p = &(P); \
>>    size_t __size = MAX (sizeof(*P),
>>                         __builtin_offsetof (__typeof(*P), FAM)
>>                         + sizeof (*(P->FAM)) * COUNT); \
>>    *__p = kmalloc(__size); \
>>  })
>> 
>> 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_counted_by_ref will be very useful
>> to be added to help the adoption of the counted-by attribute.
>> 
>> -- Built-in Function: TYPE __builtin_counted_by_ref (PTR)
>>     The built-in function '__builtin_counted_by_ref' 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 VOID pointer type in case of a
>>     NULL pointer being returned.
>> 
>> With this new builtin, the central allocator could be updated to:
>> 
>>  #define MAX(A, B) (A > B) ? (A) : (B)
>>  #define alloc(P, FAM, COUNT) ({ \
>>    __auto_type __p = &(P); \
>>    __auto_type __c = (COUNT); \
>>    size_t __size = MAX (sizeof (*(*__p)),\
>>                         __builtin_offsetof (__typeof(*(*__p)),FAM) \
>>                         + sizeof (*((*__p)->FAM)) * __c); \
>>    if ((*__p = kmalloc(__size))) { \
>>      __auto_type ret = __builtin_counted_by_ref((*__p)->FAM); \
>>      *_Generic(ret, void *: &(size_t){0}, default: ret) = __c; \
>>    } \
>>  })
>> 
>> 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.
>> 
>>        PR c/116016
>> 
>> gcc/c-family/ChangeLog:
>> 
>>        * c-common.cc: Add new __builtin_counted_by_ref.
>>        * c-common.h (enum rid): Add RID_BUILTIN_COUNTED_BY_REF.
>> 
>> gcc/c/ChangeLog:
>> 
>>        * c-decl.cc (names_builtin_p): Add RID_BUILTIN_COUNTED_BY_REF.
>>        * c-parser.cc (has_counted_by_object): New routine.
>>        (get_counted_by_ref): New routine.
>>        (c_parser_postfix_expression): Handle New RID_BUILTIN_COUNTED_BY_REF.
>>        * c-tree.h: New global in_builtin_counted_by_ref.
>>        * c-typeck.cc (build_component_ref): Enable generating
>>        .ACCESS_WITH_SIZE inside typeof when inside builtin_counted_by_ref.
>> 
>> gcc/ChangeLog:
>> 
>>        * doc/extend.texi: Add documentation for __builtin_counted_by_ref.
>> 
>> gcc/testsuite/ChangeLog:
>> 
>>        * gcc.dg/builtin-counted-by-ref-1.c: New test.
>>        * gcc.dg/builtin-counted-by-ref.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                             | 78 +++++++++++++++
>> gcc/c/c-tree.h                                |  1 +
>> gcc/c/c-typeck.cc                             |  7 +-
>> gcc/doc/extend.texi                           | 55 +++++++++++
>> .../gcc.dg/builtin-counted-by-ref-1.c         | 97 +++++++++++++++++++
>> gcc/testsuite/gcc.dg/builtin-counted-by-ref.c | 61 ++++++++++++
>> 9 files changed, 301 insertions(+), 1 deletion(-)
>> create mode 100644 gcc/testsuite/gcc.dg/builtin-counted-by-ref-1.c
>> create mode 100644 gcc/testsuite/gcc.dg/builtin-counted-by-ref.c
>> 
>> diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
>> index e7e371fd26f..15b90bae8b5 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_counted_by_ref", RID_BUILTIN_COUNTED_BY_REF, 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 027f077d51b..4400d2c5d43 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_COUNTED_BY_REF,
>>   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 aa7f69d1b7b..1145dde9bb1 100644
>> --- a/gcc/c/c-decl.cc
>> +++ b/gcc/c/c-decl.cc
>> @@ -11788,6 +11788,7 @@ names_builtin_p (const char *name)
>>     case RID_BUILTIN_SHUFFLE:
>>     case RID_BUILTIN_SHUFFLEVECTOR:
>>     case RID_BUILTIN_STDC:
>> +    case RID_BUILTIN_COUNTED_BY_REF:
>>     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 aff5af17430..91bd91f4be1 100644
>> --- a/gcc/c/c-parser.cc
>> +++ b/gcc/c/c-parser.cc
>> @@ -10647,6 +10647,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.
>> @@ -11741,6 +11770,55 @@ c_parser_postfix_expression (c_parser *parser)
>>            set_c_expr_source_range (&expr, loc, close_paren_loc);
>>            break;
>>          }
>> +       case RID_BUILTIN_COUNTED_BY_REF:
>> +         {
>> +           vec<c_expr_t, va_gc> *cexpr_list;
>> +           c_expr_t *e_p;
>> +           location_t close_paren_loc;
>> +
>> +           in_builtin_counted_by_ref++;
>> +
>> +           c_parser_consume_token (parser);
>> +           if (!c_parser_get_builtin_args (parser,
>> +                                           "__builtin_counted_by_ref",
>> +                                           &cexpr_list, false,
>> +                                           &close_paren_loc))
>> +             {
>> +               expr.set_error ();
>> +               in_builtin_counted_by_ref--;
>> +               break;
>> +             }
>> +           if (vec_safe_length (cexpr_list) != 1)
>> +             {
>> +               error_at (loc, "wrong number of arguments to "
>> +                              "%<__builtin_counted_by_ref%>");
>> +               expr.set_error ();
>> +               in_builtin_counted_by_ref--;
>> +               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_counted_by_ref%>");
>> +               expr.set_error ();
>> +               in_builtin_counted_by_ref--;
>> +               break;
>> +             }
>> +
>> +           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 (void_type_node), 0);
>> +
>> +           set_c_expr_source_range (&expr, loc, close_paren_loc);
>> +           in_builtin_counted_by_ref--;
>> +           break;
>> +         }
>>        case RID_BUILTIN_SHUFFLE:
>>          {
>>            vec<c_expr_t, va_gc> *cexpr_list;
>> diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h
>> index 57befb94c08..6f46b2ff5a0 100644
>> --- a/gcc/c/c-tree.h
>> +++ b/gcc/c/c-tree.h
>> @@ -737,6 +737,7 @@ extern int c_type_dwarf_attribute (const_tree, int);
>> extern int in_alignof;
>> extern int in_sizeof;
>> extern int in_typeof;
>> +extern int in_builtin_counted_by_ref;
>> extern bool c_in_omp_for;
>> extern bool c_omp_array_section_p;
>> 
>> diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
>> index 58b2724b39e..8cb2cb60f6a 100644
>> --- a/gcc/c/c-typeck.cc
>> +++ b/gcc/c/c-typeck.cc
>> @@ -74,6 +74,9 @@ int in_sizeof;
>> /* The level of nesting inside "typeof".  */
>> int in_typeof;
>> 
>> +/* The level of nesting inside "builtin_counted_by_ref".  */
>> +int in_builtin_counted_by_ref;
>> +
>> /* True when parsing OpenMP loop expressions.  */
>> bool c_in_omp_for;
>> 
>> @@ -2848,7 +2851,9 @@ build_component_ref (location_t loc, tree datum, tree component,
>>          bool use_datum_quals;
>>          tree counted_by_type = NULL_TREE;
>>          /* Do not handle counted_by when in typeof and alignof operator.  */
>> -         handle_counted_by = handle_counted_by && !in_typeof && !in_alignof;
>> +         handle_counted_by = handle_counted_by
>> +                             && !(in_typeof && !in_builtin_counted_by_ref)
>> +                             && !in_alignof;
>>          tree counted_by_ref = handle_counted_by
>>                                ? build_counted_by_ref (datum, subdatum,
>>                                                        &counted_by_type)
>> diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
>> index 2d795ba7e59..268e50677d7 100644
>> --- a/gcc/doc/extend.texi
>> +++ b/gcc/doc/extend.texi
>> @@ -15287,6 +15287,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_counted_by_ref (@var{ptr})}
>> +The built-in function @code{__builtin_counted_by_ref} 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 VOID pointer type in case
>> +of a NULL pointer being returned.
>> +
> Having the return type default to a VOID pointer restricts it from this use:
> 
>  if (__builtin_counted_by_ref(p->FAM))
>    *__builtin_counted_by_ref(p->FAM) = 42;
> 
> The second line will emit a diagnostic.

We have discussion on this during the 1st version of the patch. at that version, the __builtin_counted_by_ref return a size_t * void pointer if no counted_by attribute for the above usage, however, Richard pointed out that "the above usage is odd for a statically typed languages, on the other hand, type selection like generics or __builtin_classify_type instead of overloading the value producing builtin might be more fit”.  please see the following for more details:

https://gcc.gnu.org/pipermail/gcc-patches/2024-August/660871.html
https://gcc.gnu.org/pipermail/gcc-patches/2024-August/660908.html
https://gcc.gnu.org/pipermail/gcc-patches/2024-August/660995.html

> Is VOID pointer just to make
> using _Generic a bit cleaner? Is that now the prefered way to use this
> builtin?

Yes, I think so. __Generic or __builtin_choose_expr is the preferred way to use this builtin. 

Qing


> 
> -bw
> 
>> +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_counted_by_ref (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_counted_by_ref (q->array)
>> +@end smallexample
>> +
>> +@noindent
>> +returns a void NULL pointer.
>> +
>> +@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-counted-by-ref-1.c b/gcc/testsuite/gcc.dg/builtin-counted-by-ref-1.c
>> new file mode 100644
>> index 00000000000..d055cf1defc
>> --- /dev/null
>> +++ b/gcc/testsuite/gcc.dg/builtin-counted-by-ref-1.c
>> @@ -0,0 +1,97 @@
>> +/* Test the code generation for the new __builtin_counted_by_ref.  */
>> +/* { 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 MAX(A, B) (A > B) ? (A) : (B)
>> +#define MY_ALLOC(P, FAM, COUNT) ({ \
>> +  __auto_type __p = &(P); \
>> +  __auto_type __c = (COUNT); \
>> +  size_t __size = MAX (sizeof (*(*__p)),\
>> +                      __builtin_offsetof (__typeof(*(*__p)),FAM) \
>> +                      + sizeof (*((*__p)->FAM)) * __c); \
>> +  if ((*__p = __builtin_malloc(__size))) { \
>> +    __builtin_memset(*__p, 0, __size); \
>> +    __auto_type ret = __builtin_counted_by_ref((*__p)->FAM); \
>> +    *_Generic(ret, void *: &(size_t){0}, default: ret) = __c; \
>> +    if (sizeof (__builtin_counted_by_ref ((*__p)->FAM)) != sizeof (char *)) \
>> +      __builtin_abort (); \
>> +  } \
>> +})
>> +
>> +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-counted-by-ref.c b/gcc/testsuite/gcc.dg/builtin-counted-by-ref.c
>> new file mode 100644
>> index 00000000000..4aa57ffb916
>> --- /dev/null
>> +++ b/gcc/testsuite/gcc.dg/builtin-counted-by-ref.c
>> @@ -0,0 +1,61 @@
>> +/* Testing the correct usage of the new __builtin_counted_by_ref.  */
>> +/* { 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 MAX(A, B) (A > B) ? (A) : (B)
>> +#define MY_ALLOC(P, FAM, COUNT) ({ \
>> +  __auto_type __p = &(P); \
>> +  __auto_type __c = (COUNT); \
>> +  size_t __size = MAX (sizeof (*(*__p)),\
>> +                      __builtin_offsetof (__typeof(*(*__p)),FAM) \
>> +                      + sizeof (*((*__p)->FAM)) * __c); \
>> +  if ((*__p = __builtin_malloc(__size))) { \
>> +    __builtin_memset(*__p, 0, __size); \
>> +    __auto_type ret = __builtin_counted_by_ref((*__p)->FAM); \
>> +    *_Generic(ret, void *: &(size_t){0}, default: ret) = __c; \
>> +    if (sizeof (__builtin_counted_by_ref ((*__p)->FAM)) != sizeof (char *)) \
>> +      __builtin_abort (); \
>> +  } \
>> +})
>> +
>> +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_counted_by_ref (); /* { dg-error "wrong number of arguments to" } */
>> +  __builtin_counted_by_ref (array_annotated->c, 10); /* { dg-error "wrong number of arguments to" } */
>> +  __builtin_counted_by_ref (array_annotated->other); /* { dg-error "the argument must be an array" } */
>> +  __builtin_counted_by_ref (foo());  /* { dg-error "the argument must be an array" } */
>> +
>> +  return 0;
>> +}
>> --
>> 2.31.1
Jakub Jelinek Sept. 14, 2024, 6:58 p.m. UTC | #3
On Wed, Sep 11, 2024 at 09:13:40PM +0000, Qing Zhao wrote:
> @@ -11741,6 +11770,55 @@ c_parser_postfix_expression (c_parser *parser)
>  	    set_c_expr_source_range (&expr, loc, close_paren_loc);
>  	    break;
>  	  }
> +	case RID_BUILTIN_COUNTED_BY_REF:
> +	  {
> +	    vec<c_expr_t, va_gc> *cexpr_list;
> +	    c_expr_t *e_p;
> +	    location_t close_paren_loc;
> +
> +	    in_builtin_counted_by_ref++;

I think this in_builtin_counted_by_ref stuff here plus the whole c-typeck.cc
hunk should be replaced with:

> +	    c_parser_consume_token (parser);
> +	    if (!c_parser_get_builtin_args (parser,
> +					    "__builtin_counted_by_ref",
> +					    &cexpr_list, false,
> +					    &close_paren_loc))
> +	      {
> +		expr.set_error ();
> +		in_builtin_counted_by_ref--;
> +		break;
> +	      }
> +	    if (vec_safe_length (cexpr_list) != 1)
> +	      {
> +		error_at (loc, "wrong number of arguments to "
> +			       "%<__builtin_counted_by_ref%>");
> +		expr.set_error ();
> +		in_builtin_counted_by_ref--;
> +		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_counted_by_ref%>");
> +		expr.set_error ();
> +		in_builtin_counted_by_ref--;
> +		break;
> +	      }
> +

	    if (has_counted_by_object (e_p->value))
	      expr.value = get_counted_by_ref (e_p->value);
	    else if (in_typeof && TREE_CODE (e_p->value) == COMPONENT_REF)
	      {
		tree counted_by_type = NULL_TREE;
		tree arg = (*cexpr_
		if (build_counted_by_ref (TREE_OPERAND (e_p->value, 0),
					  TREE_OPERAND (e_p->value, 1),
					  &counted_by_type))
		  expr.value
		    = build_zero_cst (build_pointer_type (counted_by_type));
		else
		  expr.value = null_pointer_node;
	      }
	    else
	      expr.value = null_pointer_node;

(plus make build_counted_by_ref non-static and add prototype).

Completely untested.

> +	    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 (void_type_node), 0);

	Jakub
Qing Zhao Sept. 15, 2024, 10:01 a.m. UTC | #4
> On Sep 14, 2024, at 8:59 PM, Jakub Jelinek <jakub@redhat.com> wrote:
> 
> On Wed, Sep 11, 2024 at 09:13:40PM +0000, Qing Zhao wrote:
>> @@ -11741,6 +11770,55 @@ c_parser_postfix_expression (c_parser *parser)
>>        set_c_expr_source_range (&expr, loc, close_paren_loc);
>>        break;
>>      }
>> +    case RID_BUILTIN_COUNTED_BY_REF:
>> +      {
>> +        vec<c_expr_t, va_gc> *cexpr_list;
>> +        c_expr_t *e_p;
>> +        location_t close_paren_loc;
>> +
>> +        in_builtin_counted_by_ref++;
> 
> I think this in_builtin_counted_by_ref stuff here plus the whole c-typeck.cc
> hunk should be replaced with:
> 
>> +        c_parser_consume_token (parser);
>> +        if (!c_parser_get_builtin_args (parser,
>> +                        "__builtin_counted_by_ref",
>> +                        &cexpr_list, false,
>> +                        &close_paren_loc))
>> +          {
>> +        expr.set_error ();
>> +        in_builtin_counted_by_ref--;
>> +        break;
>> +          }
>> +        if (vec_safe_length (cexpr_list) != 1)
>> +          {
>> +        error_at (loc, "wrong number of arguments to "
>> +                   "%<__builtin_counted_by_ref%>");
>> +        expr.set_error ();
>> +        in_builtin_counted_by_ref--;
>> +        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_counted_by_ref%>");
>> +        expr.set_error ();
>> +        in_builtin_counted_by_ref--;
>> +        break;
>> +          }
>> +
> 
>        if (has_counted_by_object (e_p->value))
>          expr.value = get_counted_by_ref (e_p->value);
>        else if (in_typeof && TREE_CODE (e_p->value) == COMPONENT_REF)
>          {
>        tree counted_by_type = NULL_TREE;
>        tree arg = (*cexpr_
>        if (build_counted_by_ref (TREE_OPERAND (e_p->value, 0),
>                      TREE_OPERAND (e_p->value, 1),
>                      &counted_by_type))
>          expr.value
>            = build_zero_cst (build_pointer_type (counted_by_type));
>        else
>          expr.value = null_pointer_node;
>          }
>        else
>          expr.value = null_pointer_node;
> 
> (plus make build_counted_by_ref non-static and add prototype).
> 
> Completely untested.

Thanks a lot!will update as you suggested 

Qing


> 
>> +        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 (void_type_node), 0);
> 
>    Jakub
>
Jakub Jelinek Sept. 17, 2024, 9:13 a.m. UTC | #5
On Sat, Sep 14, 2024 at 08:58:28PM +0200, Jakub Jelinek wrote:
> 	    if (has_counted_by_object (e_p->value))
> 	      expr.value = get_counted_by_ref (e_p->value);
> 	    else if (in_typeof && TREE_CODE (e_p->value) == COMPONENT_REF)
> 	      {
> 		tree counted_by_type = NULL_TREE;
> 		tree arg = (*cexpr_
> 		if (build_counted_by_ref (TREE_OPERAND (e_p->value, 0),
> 					  TREE_OPERAND (e_p->value, 1),
> 					  &counted_by_type))
> 		  expr.value
> 		    = build_zero_cst (build_pointer_type (counted_by_type));
> 		else
> 		  expr.value = null_pointer_node;
> 	      }
> 	    else
> 	      expr.value = null_pointer_node;
> 
> (plus make build_counted_by_ref non-static and add prototype).
> 
> Completely untested.

Note, the above I think ought to work with say
__typeof (__builtin_counted_by_ref (obj->fam))
or
__alignof (*__builtin_counted_by_ref (obj->fam))
but will not work with say
struct with_fam obj;
void foo () {
...
__typeof (char [__builtin_counted_by_ref (obj.fam) == &obj.size ? 1 : -1])
...
}
etc. (and similar for alignof; something like that should go into the
testsuite).
So maybe better
	    tree arg = e_p->value;
	    tree f;
	    if ((in_typeof || in_alignof)
		&& TREE_CODE (arg) == COMPONENT_REF
		&& (f = TREE_OPERAND (arg, 1))
		&& TREE_CODE (f) == FIELD_DECL
		&& c_flexible_array_member_type_p (TREE_TYPE (f))
		&& lookup_attribute ("counted_by", DECL_ATTRIBUTES (f))
		&& DECL_NAME (f))
	      arg = build_component_ref (EXPR_LOCATION (arg),
					 TREE_OPERAND (arg, 0),
					 DECL_NAME (f), UNKNOWN_LOCATION,
					 UNKNOWN_LOCATION, true);
	    if (has_counted_by_object (arg))
	      expr.value = get_counted_by_ref (arg);
 	    else
 	      expr.value = null_pointer_node;

	Jakub
Jakub Jelinek Sept. 17, 2024, 11:05 a.m. UTC | #6
On Tue, Sep 17, 2024 at 11:13:09AM +0200, Jakub Jelinek wrote:
> So maybe better
> 	    tree arg = e_p->value;
> 	    tree f;
> 	    if ((in_typeof || in_alignof)
> 		&& TREE_CODE (arg) == COMPONENT_REF
> 		&& (f = TREE_OPERAND (arg, 1))
> 		&& TREE_CODE (f) == FIELD_DECL
> 		&& c_flexible_array_member_type_p (TREE_TYPE (f))
> 		&& lookup_attribute ("counted_by", DECL_ATTRIBUTES (f))
> 		&& DECL_NAME (f))

Of course with
	      {
		auto save_in_typeof = in_typeof;
		auto save_in_alignof = in_alignof;
		in_typeof = 0;
		in_alignof = 0;
> 	      arg = build_component_ref (EXPR_LOCATION (arg),
> 					 TREE_OPERAND (arg, 0),
> 					 DECL_NAME (f), UNKNOWN_LOCATION,
> 					 UNKNOWN_LOCATION, true);
		in_typeof = save_in_typeof;
		in_alignof = save_in_alignof;
	      }
Why don't we have the sentinel classes C++ FE has in the C FE?

Or outline the counted_by specific part of build_component_ref into
a separate function and call just that.

> 	    if (has_counted_by_object (arg))
> 	      expr.value = get_counted_by_ref (arg);
>  	    else
>  	      expr.value = null_pointer_node;

BTW, concerning just counted_by attribute, how does it play together
with _Atomic on the size member?  Perhaps it should be disallowed.
The thing is that _Atomic access lowering is done in the FE, so too
early for tree-object-size, which would either need to reimplement it by
hand (sure, not that difficult, it needs to just atomically load).

	Jakub
Qing Zhao Sept. 25, 2024, 6:59 p.m. UTC | #7
Sorry for the late reply (just came back from LPC after Cauldron). 

> On Sep 17, 2024, at 05:13, Jakub Jelinek <jakub@redhat.com> wrote:
> 
> On Sat, Sep 14, 2024 at 08:58:28PM +0200, Jakub Jelinek wrote:
>>    if (has_counted_by_object (e_p->value))
>>      expr.value = get_counted_by_ref (e_p->value);
>>    else if (in_typeof && TREE_CODE (e_p->value) == COMPONENT_REF)
>>      {
>> tree counted_by_type = NULL_TREE;
>> tree arg = (*cexpr_
>> if (build_counted_by_ref (TREE_OPERAND (e_p->value, 0),
>>  TREE_OPERAND (e_p->value, 1),
>>  &counted_by_type))
>>  expr.value
>>    = build_zero_cst (build_pointer_type (counted_by_type));
>> else
>>  expr.value = null_pointer_node;
>>      }
>>    else
>>      expr.value = null_pointer_node;
>> 
>> (plus make build_counted_by_ref non-static and add prototype).
>> 
>> Completely untested.
> 
> Note, the above I think ought to work with say
> __typeof (__builtin_counted_by_ref (obj->fam))
> or
> __alignof (*__builtin_counted_by_ref (obj->fam))
> but will not work with say
> struct with_fam obj;
> void foo () {
> ...
> __typeof (char [__builtin_counted_by_ref (obj.fam) == &obj.size ? 1 : -1])
> ...
> }
> etc. (and similar for alignof; something like that should go into the
> testsuite).

Sure, will add the above into the testsuite. 

thanks.

Qing

> So maybe better
>    tree arg = e_p->value;
>    tree f;
>    if ((in_typeof || in_alignof)
> && TREE_CODE (arg) == COMPONENT_REF
> && (f = TREE_OPERAND (arg, 1))
> && TREE_CODE (f) == FIELD_DECL
> && c_flexible_array_member_type_p (TREE_TYPE (f))
> && lookup_attribute ("counted_by", DECL_ATTRIBUTES (f))
> && DECL_NAME (f))
>      arg = build_component_ref (EXPR_LOCATION (arg),
> TREE_OPERAND (arg, 0),
> DECL_NAME (f), UNKNOWN_LOCATION,
> UNKNOWN_LOCATION, true);
>    if (has_counted_by_object (arg))
>      expr.value = get_counted_by_ref (arg);
>    else
>      expr.value = null_pointer_node;
> 
> Jakub
>
Qing Zhao Sept. 25, 2024, 7:21 p.m. UTC | #8
> On Sep 17, 2024, at 07:05, Jakub Jelinek <jakub@redhat.com> wrote:
> 
> On Tue, Sep 17, 2024 at 11:13:09AM +0200, Jakub Jelinek wrote:
>> So maybe better
>>    tree arg = e_p->value;
>>    tree f;
>>    if ((in_typeof || in_alignof)
>> && TREE_CODE (arg) == COMPONENT_REF
>> && (f = TREE_OPERAND (arg, 1))
>> && TREE_CODE (f) == FIELD_DECL
>> && c_flexible_array_member_type_p (TREE_TYPE (f))
>> && lookup_attribute ("counted_by", DECL_ATTRIBUTES (f))
>> && DECL_NAME (f))
> 
> Of course with
>      {
> auto save_in_typeof = in_typeof;
> auto save_in_alignof = in_alignof;
> in_typeof = 0;
> in_alignof = 0;
>>      arg = build_component_ref (EXPR_LOCATION (arg),
>> TREE_OPERAND (arg, 0),
>> DECL_NAME (f), UNKNOWN_LOCATION,
>> UNKNOWN_LOCATION, true);
> in_typeof = save_in_typeof;
> in_alignof = save_in_alignof;
>      }

Yeah, the “save_in_typeof/alignof” is needed. -:)

> Why don't we have the sentinel classes C++ FE has in the C FE?

Sorry for the stupid question: could you please explain the above a little bit more?  What’s the sentinel classes? And how should we use it here?
> 
> Or outline the counted_by specific part of build_component_ref into
> a separate function and call just that.

Yeah, this looks like a good idea to me, I will try to split the counted_by specific part of build_component_ref into a separate function, that might make the code cleaner and also get rid of the unnecessary global variable “in_builtin_counted_by_ref”.

>>    if (has_counted_by_object (arg))
>>      expr.value = get_counted_by_ref (arg);
>>    else
>>      expr.value = null_pointer_node;
> 
> BTW, concerning just counted_by attribute, how does it play together
> with _Atomic on the size member?  
> Perhaps it should be disallowed.

Do you mean for  the following:

struct t { 
_Atomic int size;
char array[] __attribute__ ((counted_by (size))
 };

We should emit error and disallow it at this moment?

Thanks.

Qing


> 
> The thing is that _Atomic access lowering is done in the FE, so too
> early for tree-object-size, which would either need to reimplement it by
> hand (sure, not that difficult, it needs to just atomically load).
> 
> Jakub
>
diff mbox series

Patch

diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
index e7e371fd26f..15b90bae8b5 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_counted_by_ref", RID_BUILTIN_COUNTED_BY_REF, 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 027f077d51b..4400d2c5d43 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_COUNTED_BY_REF,
   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 aa7f69d1b7b..1145dde9bb1 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -11788,6 +11788,7 @@  names_builtin_p (const char *name)
     case RID_BUILTIN_SHUFFLE:
     case RID_BUILTIN_SHUFFLEVECTOR:
     case RID_BUILTIN_STDC:
+    case RID_BUILTIN_COUNTED_BY_REF:
     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 aff5af17430..91bd91f4be1 100644
--- a/gcc/c/c-parser.cc
+++ b/gcc/c/c-parser.cc
@@ -10647,6 +10647,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.
@@ -11741,6 +11770,55 @@  c_parser_postfix_expression (c_parser *parser)
 	    set_c_expr_source_range (&expr, loc, close_paren_loc);
 	    break;
 	  }
+	case RID_BUILTIN_COUNTED_BY_REF:
+	  {
+	    vec<c_expr_t, va_gc> *cexpr_list;
+	    c_expr_t *e_p;
+	    location_t close_paren_loc;
+
+	    in_builtin_counted_by_ref++;
+
+	    c_parser_consume_token (parser);
+	    if (!c_parser_get_builtin_args (parser,
+					    "__builtin_counted_by_ref",
+					    &cexpr_list, false,
+					    &close_paren_loc))
+	      {
+		expr.set_error ();
+		in_builtin_counted_by_ref--;
+		break;
+	      }
+	    if (vec_safe_length (cexpr_list) != 1)
+	      {
+		error_at (loc, "wrong number of arguments to "
+			       "%<__builtin_counted_by_ref%>");
+		expr.set_error ();
+		in_builtin_counted_by_ref--;
+		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_counted_by_ref%>");
+		expr.set_error ();
+		in_builtin_counted_by_ref--;
+		break;
+	      }
+
+	    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 (void_type_node), 0);
+
+	    set_c_expr_source_range (&expr, loc, close_paren_loc);
+	    in_builtin_counted_by_ref--;
+	    break;
+	  }
 	case RID_BUILTIN_SHUFFLE:
 	  {
 	    vec<c_expr_t, va_gc> *cexpr_list;
diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h
index 57befb94c08..6f46b2ff5a0 100644
--- a/gcc/c/c-tree.h
+++ b/gcc/c/c-tree.h
@@ -737,6 +737,7 @@  extern int c_type_dwarf_attribute (const_tree, int);
 extern int in_alignof;
 extern int in_sizeof;
 extern int in_typeof;
+extern int in_builtin_counted_by_ref;
 extern bool c_in_omp_for;
 extern bool c_omp_array_section_p;
 
diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index 58b2724b39e..8cb2cb60f6a 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -74,6 +74,9 @@  int in_sizeof;
 /* The level of nesting inside "typeof".  */
 int in_typeof;
 
+/* The level of nesting inside "builtin_counted_by_ref".  */
+int in_builtin_counted_by_ref;
+
 /* True when parsing OpenMP loop expressions.  */
 bool c_in_omp_for;
 
@@ -2848,7 +2851,9 @@  build_component_ref (location_t loc, tree datum, tree component,
 	  bool use_datum_quals;
 	  tree counted_by_type = NULL_TREE;
 	  /* Do not handle counted_by when in typeof and alignof operator.  */
-	  handle_counted_by = handle_counted_by && !in_typeof && !in_alignof;
+	  handle_counted_by = handle_counted_by
+			      && !(in_typeof && !in_builtin_counted_by_ref)
+			      && !in_alignof;
 	  tree counted_by_ref = handle_counted_by
 				? build_counted_by_ref (datum, subdatum,
 							&counted_by_type)
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 2d795ba7e59..268e50677d7 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -15287,6 +15287,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_counted_by_ref (@var{ptr})}
+The built-in function @code{__builtin_counted_by_ref} 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 VOID pointer type 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_counted_by_ref (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_counted_by_ref (q->array)
+@end smallexample
+
+@noindent
+returns a void NULL pointer.
+
+@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-counted-by-ref-1.c b/gcc/testsuite/gcc.dg/builtin-counted-by-ref-1.c
new file mode 100644
index 00000000000..d055cf1defc
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-counted-by-ref-1.c
@@ -0,0 +1,97 @@ 
+/* Test the code generation for the new __builtin_counted_by_ref.  */
+/* { 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 MAX(A, B) (A > B) ? (A) : (B)
+#define MY_ALLOC(P, FAM, COUNT) ({ \
+  __auto_type __p = &(P); \
+  __auto_type __c = (COUNT); \
+  size_t __size = MAX (sizeof (*(*__p)),\
+		       __builtin_offsetof (__typeof(*(*__p)),FAM) \
+		       + sizeof (*((*__p)->FAM)) * __c); \
+  if ((*__p = __builtin_malloc(__size))) { \
+    __builtin_memset(*__p, 0, __size); \
+    __auto_type ret = __builtin_counted_by_ref((*__p)->FAM); \
+    *_Generic(ret, void *: &(size_t){0}, default: ret) = __c; \
+    if (sizeof (__builtin_counted_by_ref ((*__p)->FAM)) != sizeof (char *)) \
+      __builtin_abort (); \
+  } \
+})
+
+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-counted-by-ref.c b/gcc/testsuite/gcc.dg/builtin-counted-by-ref.c
new file mode 100644
index 00000000000..4aa57ffb916
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-counted-by-ref.c
@@ -0,0 +1,61 @@ 
+/* Testing the correct usage of the new __builtin_counted_by_ref.  */
+/* { 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 MAX(A, B) (A > B) ? (A) : (B)
+#define MY_ALLOC(P, FAM, COUNT) ({ \
+  __auto_type __p = &(P); \
+  __auto_type __c = (COUNT); \
+  size_t __size = MAX (sizeof (*(*__p)),\
+		       __builtin_offsetof (__typeof(*(*__p)),FAM) \
+		       + sizeof (*((*__p)->FAM)) * __c); \
+  if ((*__p = __builtin_malloc(__size))) { \
+    __builtin_memset(*__p, 0, __size); \
+    __auto_type ret = __builtin_counted_by_ref((*__p)->FAM); \
+    *_Generic(ret, void *: &(size_t){0}, default: ret) = __c; \
+    if (sizeof (__builtin_counted_by_ref ((*__p)->FAM)) != sizeof (char *)) \
+      __builtin_abort (); \
+  } \
+})
+
+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_counted_by_ref (); /* { dg-error "wrong number of arguments to" } */
+  __builtin_counted_by_ref (array_annotated->c, 10); /* { dg-error "wrong number of arguments to" } */
+  __builtin_counted_by_ref (array_annotated->other); /* { dg-error "the argument must be an array" } */
+  __builtin_counted_by_ref (foo());  /* { dg-error "the argument must be an array" } */
+
+  return 0;
+}