> On Aug 21, 2024, at 10:34, Martin Uecker <uec...@tugraz.at> wrote: > > Am Mittwoch, dem 21.08.2024 um 14:12 +0000 schrieb Qing Zhao: > > ... >> >>> >>>> + 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. > > Does it matter? > >>> 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. >> >> Yes, you are right. When I wrote the testing case, I felt wield too. (:- >> >> And another issue with the returned value of __builtin_get_counted_by(PTR) >> is, since it returns a pointer, the TYPE of the pointee matters, especially >> when returns a NULL pointer, I used a pointer type pointing to the size_t in >> case of a NULL pointer being returned to avoid some strict-aliasing issue. >> please see PR116316, https://gcc.gnu.org/bugzilla/show_bug.cgi?id=116316) >> for details. > > It needs to return a pointer to the actual type of the count field. > For NULL, it probably needs to be size_t* for above assignment > to work.
Yes, that’s what I did for the patch after understanding PR116316. -:) The current doc of the returned type is: 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. > > But if we changed it to return a void pointer, we could make this > a compile-time check: > > auto ret = __builtin_get_counted_by(__p->FAM); > > _Generic(ret, void*: (void)0, default: *ret = COUNT); Is there any benefit to return a void pointer than a SIZE_T pointer for the NULL pointer? > > >> >> Yes, I do feel that the approach __builtin_get_counted_by is not very good. >> Maybe it’s better to provide >> A. __builtin_set_counted_by >> or >> B. The unary operator __counted_by(PTR) to return a Lvalue, in this case, >> we need a __builtin_has_attribute first to check whether PTR has the >> counted_by attribute first. > > You could potentially do the same __counted_by and test for type void. > > _Generic(typeof(__counted_by(PTR)), void: (void)0, __counted_by(PTR) = COUNT); Oh, so, is there any benefit for the unary operator __counted_by(PTR) than the current __builtin_get_counted_by? Thanks. Qing > > Martin > >> >> Any suggestion? >> >> thanks. >> >> Qing >> >> >>> >>> 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 >> >> > > -- > Univ.-Prof. Dr. rer. nat. Martin Uecker > Graz University of Technology > Institute of Biomedical Imaging