Hi, this is the 6th version of the patch. Compare to the 5th version, the major changes are (Address Jakub's comments)
1. delete the new global "in_builtin_counted_by_ref" 2. split the "counted_by" specific handling from the routne "build_component_ref" as a new routine "handle_counted_by_for_component_ref" 3. when handle "__builtin_counted_by_ref" in c-parser.cc, if in_typeof or in_alignof, call "handle_counted_by_for_component_ref" to generate the call to .ACCESS_WITH_SIZE. 4. Add the following into the testing cases. (in builtin-counted-by-ref-1.c) A. __alignof: __alignof (*__builtin_counted_by_ref (array_annotated->c)) B. __typeof: __typeof (*__builtin_counted_by_ref (array_annotated->c)) __typeof (char[__builtin_counted_by_ref (array_annotated->c) == &array_annotated->b ? 1 : 10]), 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 routine handle_counted_by_for_component_ref. * c-typeck.cc (handle_counted_by_for_component_ref): New routine. (build_component_ref): Call the new routine. 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 | 80 +++++++++++ gcc/c/c-tree.h | 1 + gcc/c/c-typeck.cc | 34 +++-- gcc/doc/extend.texi | 55 +++++++ .../gcc.dg/builtin-counted-by-ref-1.c | 135 ++++++++++++++++++ gcc/testsuite/gcc.dg/builtin-counted-by-ref.c | 61 ++++++++ 9 files changed, 360 insertions(+), 9 deletions(-) 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 ec6a5da892d..8ad9b998e7b 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 a681438cbbe..f8f17e5f6eb 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,57 @@ 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; + + 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 (); + break; + } + if (vec_safe_length (cexpr_list) != 1) + { + error_at (loc, "wrong number of arguments to " + "%<__builtin_counted_by_ref%>"); + expr.set_error (); + break; + } + + e_p = &(*cexpr_list)[0]; + tree ref = e_p->value; + + if (TREE_CODE (TREE_TYPE (ref)) != ARRAY_TYPE) + { + error_at (loc, "the argument must be an array" + "%<__builtin_counted_by_ref%>"); + expr.set_error (); + break; + } + + /* if the array ref is inside TYPEOF or ALIGNOF, the call to + .ACCESS_WITH_SIZE was not genereated by the routine + build_component_ref by default, we should generate it here. */ + if ((in_typeof || in_alignof) + && TREE_CODE (ref) == COMPONENT_REF) + ref = handle_counted_by_for_component_ref (loc, ref); + + if (has_counted_by_object (ref)) + expr.value + = get_counted_by_ref (ref); + else + expr.value + = build_int_cst (build_pointer_type (void_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/c/c-tree.h b/gcc/c/c-tree.h index b3e7bb013b6..9a067c93240 100644 --- a/gcc/c/c-tree.h +++ b/gcc/c/c-tree.h @@ -780,6 +780,7 @@ extern tree composite_type (tree, tree); extern tree lookup_field (const_tree, tree); extern tree build_component_ref (location_t, tree, tree, location_t, location_t, bool = true); +extern tree handle_counted_by_for_component_ref (location_t, tree); extern tree build_array_ref (location_t, tree, tree); extern tree build_omp_array_section (location_t, tree, tree, tree); extern tree build_external_ref (location_t, tree, bool, tree *); diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc index ba6d96d26b2..5ac8688df59 100644 --- a/gcc/c/c-typeck.cc +++ b/gcc/c/c-typeck.cc @@ -2766,6 +2766,28 @@ build_access_with_size_for_counted_by (location_t loc, tree ref, return call; } +/* + * For the COMPONENT_REF ref, check whether it has a counted_by attribute, + * if so, wrap this COMPONENT_REF with the corresponding CALL to the + * function .ACCESS_WITH_SIZE. + * Otherwise, return the ref itself. + */ +tree +handle_counted_by_for_component_ref (location_t loc, tree ref) +{ + gcc_assert (TREE_CODE (ref) == COMPONENT_REF); + tree datum = TREE_OPERAND (ref, 0); + tree subdatum = TREE_OPERAND (ref, 1); + tree counted_by_type = NULL_TREE; + tree counted_by_ref = build_counted_by_ref (datum, subdatum, + &counted_by_type); + if (counted_by_ref) + ref = build_access_with_size_for_counted_by (loc, ref, + counted_by_ref, + counted_by_type); + return ref; +} + /* Make an expression to refer to the COMPONENT field of structure or union value DATUM. COMPONENT is an IDENTIFIER_NODE. LOC is the location of the COMPONENT_REF. COMPONENT_LOC is the location @@ -2849,13 +2871,9 @@ build_component_ref (location_t loc, tree datum, tree component, int quals; tree subtype; 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; - tree counted_by_ref = handle_counted_by - ? build_counted_by_ref (datum, subdatum, - &counted_by_type) - : NULL_TREE; + if (TREE_TYPE (subdatum) == error_mark_node) return error_mark_node; @@ -2875,10 +2893,8 @@ build_component_ref (location_t loc, tree datum, tree component, NULL_TREE); SET_EXPR_LOCATION (ref, loc); - if (counted_by_ref) - ref = build_access_with_size_for_counted_by (loc, ref, - counted_by_ref, - counted_by_type); + if (handle_counted_by) + ref = handle_counted_by_for_component_ref (loc, ref); if (TREE_READONLY (subdatum) || (use_datum_quals && TREE_READONLY (datum))) diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index c95df845634..12495a58952 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..202c8096783 --- /dev/null +++ b/gcc/testsuite/gcc.dg/builtin-counted-by-ref-1.c @@ -0,0 +1,135 @@ +/* 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 (); + if (__alignof (*__builtin_counted_by_ref (array_annotated->c)) + != __alignof (array_annotated->b)) + __builtin_abort (); + if (!__builtin_types_compatible_p + (__typeof (*__builtin_counted_by_ref (array_annotated->c)), + __typeof (array_annotated->b))) + __builtin_abort (); + if (!__builtin_types_compatible_p + (__typeof (char[__builtin_counted_by_ref (array_annotated->c) + == &array_annotated->b ? 1 : 10]), + __typeof (char[1]))) + __builtin_abort (); + + MY_ALLOC(array_flex, c, 20); + if (array_flex->b == 20) + __builtin_abort (); + if (!__builtin_types_compatible_p + (__typeof (char[__builtin_counted_by_ref (array_flex->c) + == &array_flex->b ? 1 : 10]), + __typeof (char[10]))) + __builtin_abort (); + + MY_ALLOC(array_nested_annotated, c, 30); + if (array_nested_annotated->b != 30) + __builtin_abort (); + if (__alignof (*__builtin_counted_by_ref (array_nested_annotated->c)) + != __alignof (array_nested_annotated->b)) + __builtin_abort (); + if (!__builtin_types_compatible_p + (__typeof (*__builtin_counted_by_ref (array_nested_annotated->c)), + __typeof (array_nested_annotated->b))) + __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 (); + if (__alignof (*__builtin_counted_by_ref (annotated_p->c)) + != __alignof (annotated_p->b)) + __builtin_abort (); + if (!__builtin_types_compatible_p + (__typeof (*__builtin_counted_by_ref (annotated_p->c)), + __typeof (annotated_p->b))) + __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 (); + if (__alignof (*__builtin_counted_by_ref (nested_annotated_p->c)) + != __alignof (nested_annotated_p->b)) + __builtin_abort (); + if (!__builtin_types_compatible_p + (__typeof (*__builtin_counted_by_ref (nested_annotated_p->c)), + __typeof (nested_annotated_p->b))) + __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