On Tue, Aug 20, 2024 at 3:41 PM Qing Zhao <[email protected]> wrote:
>
>
>
> > On Aug 20, 2024, at 05:58, Richard Biener <[email protected]>
> > wrote:
> >
> > On Tue, Aug 13, 2024 at 5:34 PM Qing Zhao <[email protected]> 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)?
>
> Yes, that’s the initial request from Kees. -)
>
> The title of PR116016 is: add __builtin_set_counted_by(P->FAM, COUNT) or
> equivalent
>
> After extensive discussion (Martin Uecker raised the initial idea in
> https://gcc.gnu.org/bugzilla/show_bug.cgi?id=116016#c24, more discussions
> followed, till comments #31). we decided to provide
> __builtin_get_counted_by(PTR) instead of __builtin_set_counted_by(PTR, VALUE)
> due to the following two reasons:
>
> 1. __builtin_get_counted_by should be enough to provide the functionality,
> and even simpler;
> 2. More flexible to be used by the programmer to be able to both WRITE and
> READ the counted-by field.
>
>
>
> >
> > Note that __builtin_get_counted_by to me suggests it returns the
> > value and not a pointer to the value.
>
> The syntax of __builtin_get_counted_by is:
>
> TYPE __builtin_get_counted_by (PTR)
>
> The returned value is:
>
> 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.
>
>
> > A more proper language extension might involve a keyword
> > like __real, so __counted_by X would produce an lvalue, selecting
> > the counted-by member.
>
> Yes, if the returned value could be a LVALUE instead of a Pointer, that’s
> even simpler and cleaner.
> However, then as you mentioned below, another builtin
> “__builtin_has_attribute(PTR, counted_by)” need
> to be queried first to make sure the counted_by field exists.
>
> We have discussed this approach, and I preferred this approach too.
>
> However, the main reason we gave up on that direction is:
>
> There is NO __builtin_has_attribute (PTR, counted_by) been supported by
> CLANG, and not sure how difficult for CLANG to add this new builtin.
>
> In order to provide consistent interface to Linux kernel from both CLANG and
> GCC, I finally decided to provide the current interface in order to be
> consistent with CLANG.
>
> Bill, could you please provide a little bit more info on the possibility of a
> new builtin __builtin_has_attribute() in CLANG?
>
> > 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
>
> Could you please explain this a little bit more? (Not quite understood, a
> small example will be better, -:) thanks a lot.
> > or __builtin_classify_type instead of overloading
> > the value producing builtin might be more fit here?
>
> You mean the following lines from the testing case:
yes
> + if (__builtin_get_counted_by (__p->FAM)) \
> + *(__builtin_get_counted_by(__p->FAM)) = COUNT; \
>
> How to improve it? (Thanks a lot for your suggestion).
There's lack of syntactic guarantee that __builtin_get_counted_by (...) != 0 is
a constant expression. __builtin_set_counted_by (...) would avoid this
when it would be documented to expand to nothing for a type without a counted_by
member. Writing
size_t fake;
__builtin_choose_expr (__builtin_get_counted_by (__p-->FAM) != 0,
*(__builtin_get_counted_by(__p->FAM)), __fake) = COUNT;
would ensure this but of course requiring the __fake lvalue is ugly, too.
Richard.
>
> Qing
>
> >
> > 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
>
>