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. 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