On Wed, Sep 11, 2024 at 2:13 PM Qing Zhao <qing.z...@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 >