> On May 31, 2024, at 08:58, Richard Biener <rguent...@suse.de> wrote:
>
> On Thu, 30 May 2024, Qing Zhao wrote:
>
>> Including the following changes:
>> * The definition of the new internal function .ACCESS_WITH_SIZE
>> in internal-fn.def.
>> * C FE converts every reference to a FAM with a "counted_by" attribute
>> to a call to the internal function .ACCESS_WITH_SIZE.
>> (build_component_ref in c_typeck.cc)
>>
>> This includes the case when the object is statically allocated and
>> initialized.
>> In order to make this working, the routine digest_init in c-typeck.cc
>> is updated to fold calls to .ACCESS_WITH_SIZE to its first argument
>> when require_constant is TRUE.
>>
>> However, for the reference inside "offsetof", the "counted_by" attribute is
>> ignored since it's not useful at all.
>> (c_parser_postfix_expression in c/c-parser.cc)
>>
>> In addtion to "offsetof", for the reference inside operator "typeof" and
>> "alignof", we ignore counted_by attribute too.
>>
>> When building ADDR_EXPR for the .ACCESS_WITH_SIZE in C FE,
>> replace the call with its first argument.
>>
>> * Convert every call to .ACCESS_WITH_SIZE to its first argument.
>> (expand_ACCESS_WITH_SIZE in internal-fn.cc)
>> * Provide the utility routines to check the call is .ACCESS_WITH_SIZE and
>> get the reference from the call to .ACCESS_WITH_SIZE.
>> (is_access_with_size_p and get_ref_from_access_with_size in tree.cc)
>
> The middle-end parts of this revised patch are OK.
Thanks a lot for the review.
Will commit the patch set soon.
Qing
>
> Thanks,
> Richard.
>
>> gcc/c/ChangeLog:
>>
>> * c-parser.cc (c_parser_postfix_expression): Ignore the counted-by
>> attribute when build_component_ref inside offsetof operator.
>> * c-tree.h (build_component_ref): Add one more parameter.
>> * c-typeck.cc (build_counted_by_ref): New function.
>> (build_access_with_size_for_counted_by): New function.
>> (build_component_ref): Check the counted-by attribute and build
>> call to .ACCESS_WITH_SIZE.
>> (build_unary_op): When building ADDR_EXPR for
>> .ACCESS_WITH_SIZE, use its first argument.
>> (lvalue_p): Accept call to .ACCESS_WITH_SIZE.
>> (digest_init): Fold call to .ACCESS_WITH_SIZE to its first
>> argument when require_constant is TRUE.
>>
>> gcc/ChangeLog:
>>
>> * internal-fn.cc (expand_ACCESS_WITH_SIZE): New function.
>> * internal-fn.def (ACCESS_WITH_SIZE): New internal function.
>> * tree.cc (is_access_with_size_p): New function.
>> (get_ref_from_access_with_size): New function.
>> * tree.h (is_access_with_size_p): New prototype.
>> (get_ref_from_access_with_size): New prototype.
>>
>> gcc/testsuite/ChangeLog:
>>
>> * gcc.dg/flex-array-counted-by-2.c: New test.
>> ---
>> gcc/c/c-parser.cc | 10 +-
>> gcc/c/c-tree.h | 2 +-
>> gcc/c/c-typeck.cc | 142 +++++++++++++++++-
>> gcc/internal-fn.cc | 34 +++++
>> gcc/internal-fn.def | 5 +
>> .../gcc.dg/flex-array-counted-by-2.c | 112 ++++++++++++++
>> gcc/tree.cc | 22 +++
>> gcc/tree.h | 8 +
>> 8 files changed, 328 insertions(+), 7 deletions(-)
>> create mode 100644 gcc/testsuite/gcc.dg/flex-array-counted-by-2.c
>>
>> diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc
>> index 00f8bf4376e5..2d9e9c0969f0 100644
>> --- a/gcc/c/c-parser.cc
>> +++ b/gcc/c/c-parser.cc
>> @@ -10848,9 +10848,12 @@ c_parser_postfix_expression (c_parser *parser)
>> if (c_parser_next_token_is (parser, CPP_NAME))
>> {
>> c_token *comp_tok = c_parser_peek_token (parser);
>> + /* Ignore the counted_by attribute for reference inside
>> + offsetof since the information is not useful at all. */
>> offsetof_ref
>> = build_component_ref (loc, offsetof_ref, comp_tok->value,
>> - comp_tok->location, UNKNOWN_LOCATION);
>> + comp_tok->location, UNKNOWN_LOCATION,
>> + false);
>> c_parser_consume_token (parser);
>> while (c_parser_next_token_is (parser, CPP_DOT)
>> || c_parser_next_token_is (parser,
>> @@ -10877,11 +10880,14 @@ c_parser_postfix_expression (c_parser *parser)
>> break;
>> }
>> c_token *comp_tok = c_parser_peek_token (parser);
>> + /* Ignore the counted_by attribute for reference inside
>> + offsetof since the information is not useful. */
>> offsetof_ref
>> = build_component_ref (loc, offsetof_ref,
>> comp_tok->value,
>> comp_tok->location,
>> - UNKNOWN_LOCATION);
>> + UNKNOWN_LOCATION,
>> + false);
>> c_parser_consume_token (parser);
>> }
>> else
>> diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h
>> index 531a7e8742e3..56a33b8156c6 100644
>> --- a/gcc/c/c-tree.h
>> +++ b/gcc/c/c-tree.h
>> @@ -779,7 +779,7 @@ extern void mark_exp_read (tree);
>> 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);
>> + location_t, bool = true);
>> 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 2375953fdb62..0d9c7a34a0df 100644
>> --- a/gcc/c/c-typeck.cc
>> +++ b/gcc/c/c-typeck.cc
>> @@ -2584,15 +2584,116 @@ should_suggest_deref_p (tree datum_type)
>> return false;
>> }
>>
>> +/* For a SUBDATUM field of a structure or union DATUM, generate a REF to
>> + the object that represents its counted_by per the attribute counted_by
>> + attached to this field if it's a flexible array member field, otherwise
>> + return NULL_TREE.
>> + Set COUNTED_BY_TYPE to the TYPE of the counted_by field.
>> + For example, if:
>> +
>> + struct P {
>> + int k;
>> + int x[] __attribute__ ((counted_by (k)));
>> + } *p;
>> +
>> + for:
>> + p->x
>> +
>> + the ref to the object that represents its element count will be:
>> +
>> + &(p->k)
>> +
>> +*/
>> +static tree
>> +build_counted_by_ref (tree datum, tree subdatum, tree *counted_by_type)
>> +{
>> + tree type = TREE_TYPE (datum);
>> + if (!c_flexible_array_member_type_p (TREE_TYPE (subdatum)))
>> + return NULL_TREE;
>> +
>> + tree attr_counted_by = lookup_attribute ("counted_by",
>> + DECL_ATTRIBUTES (subdatum));
>> + tree counted_by_ref = NULL_TREE;
>> + *counted_by_type = NULL_TREE;
>> + if (attr_counted_by)
>> + {
>> + tree field_id = TREE_VALUE (TREE_VALUE (attr_counted_by));
>> + counted_by_ref
>> + = build_component_ref (UNKNOWN_LOCATION,
>> + datum, field_id,
>> + UNKNOWN_LOCATION, UNKNOWN_LOCATION);
>> + counted_by_ref = build_fold_addr_expr (counted_by_ref);
>> +
>> + /* Get the TYPE of the counted_by field. */
>> + tree counted_by_field = lookup_field (type, field_id);
>> + gcc_assert (counted_by_field);
>> +
>> + do
>> + {
>> + *counted_by_type = TREE_TYPE (TREE_VALUE (counted_by_field));
>> + counted_by_field = TREE_CHAIN (counted_by_field);
>> + }
>> + while (counted_by_field);
>> + }
>> + return counted_by_ref;
>> +}
>> +
>> +/* Given a COMPONENT_REF REF with the location LOC, the corresponding
>> + COUNTED_BY_REF, and the COUNTED_BY_TYPE, generate an INDIRECT_REF
>> + to a call to the internal function .ACCESS_WITH_SIZE.
>> +
>> + REF
>> +
>> + to:
>> +
>> + (*.ACCESS_WITH_SIZE (REF, COUNTED_BY_REF, 1, (TYPE_OF_SIZE)0, -1))
>> +
>> + NOTE: The return type of this function is the POINTER type pointing
>> + to the original flexible array type.
>> + Then the type of the INDIRECT_REF is the original flexible array type.
>> +
>> + The type of the first argument of this function is a POINTER type
>> + to the original flexible array type.
>> +
>> + The 4th argument of the call is a constant 0 with the TYPE of the
>> + object pointed by COUNTED_BY_REF.
>> +
>> + */
>> +static tree
>> +build_access_with_size_for_counted_by (location_t loc, tree ref,
>> + tree counted_by_ref,
>> + tree counted_by_type)
>> +{
>> + gcc_assert (c_flexible_array_member_type_p (TREE_TYPE (ref)));
>> + /* The result type of the call is a pointer to the flexible array type.
>> */
>> + tree result_type = build_pointer_type (TREE_TYPE (ref));
>> +
>> + tree call
>> + = build_call_expr_internal_loc (loc, IFN_ACCESS_WITH_SIZE,
>> + result_type, 5,
>> + array_to_pointer_conversion (loc, ref),
>> + counted_by_ref,
>> + build_int_cst (integer_type_node, 1),
>> + build_int_cst (counted_by_type, 0),
>> + build_int_cst (integer_type_node, -1));
>> + /* Wrap the call with an INDIRECT_REF with the flexible array type. */
>> + call = build1 (INDIRECT_REF, TREE_TYPE (ref), call);
>> + SET_EXPR_LOCATION (call, loc);
>> + return call;
>> +}
>> +
>> /* 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
>> of COMPONENT. ARROW_LOC is the location of the first -> operand if
>> - it is from -> operator. */
>> + it is from -> operator.
>> + If HANDLE_COUNTED_BY is true, check the counted_by attribute and generate
>> + a call to .ACCESS_WITH_SIZE. Otherwise, ignore the attribute. */
>>
>> tree
>> build_component_ref (location_t loc, tree datum, tree component,
>> - location_t component_loc, location_t arrow_loc)
>> + location_t component_loc, location_t arrow_loc,
>> + bool handle_counted_by)
>> {
>> tree type = TREE_TYPE (datum);
>> enum tree_code code = TREE_CODE (type);
>> @@ -2664,7 +2765,13 @@ 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;
>>
>> @@ -2683,6 +2790,12 @@ build_component_ref (location_t loc, tree datum, tree
>> component,
>> ref = build3 (COMPONENT_REF, subtype, datum, subdatum,
>> 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 (TREE_READONLY (subdatum)
>> || (use_datum_quals && TREE_READONLY (datum)))
>> TREE_READONLY (ref) = 1;
>> @@ -5087,7 +5200,11 @@ build_unary_op (location_t location, enum tree_code
>> code, tree xarg,
>> goto return_build_unary_op;
>> }
>>
>> - /* Ordinary case; arg is a COMPONENT_REF or a decl. */
>> + /* Ordinary case; arg is a COMPONENT_REF or a decl, or a call to
>> + .ACCESS_WITH_SIZE. */
>> + if (is_access_with_size_p (arg))
>> + arg = TREE_OPERAND (TREE_OPERAND (CALL_EXPR_ARG (arg, 0), 0), 0);
>> +
>> argtype = TREE_TYPE (arg);
>>
>> /* If the lvalue is const or volatile, merge that into the type
>> @@ -5238,6 +5355,9 @@ lvalue_p (const_tree ref)
>> case BIND_EXPR:
>> return TREE_CODE (TREE_TYPE (ref)) == ARRAY_TYPE;
>>
>> + case CALL_EXPR:
>> + return is_access_with_size_p (ref);
>> +
>> default:
>> return false;
>> }
>> @@ -8525,6 +8645,20 @@ digest_init (location_t init_loc, tree type, tree
>> init, tree origtype,
>>
>> STRIP_TYPE_NOPS (inside_init);
>>
>> + /* If require_constant is TRUE, when the initializer is a call to
>> + .ACCESS_WITH_SIZE, use the first argument as the initializer.
>> + For example:
>> + y = (char *) .ACCESS_WITH_SIZE ((char *) &static_annotated.c,...)
>> + will be converted to
>> + y = &static_annotated.c. */
>> +
>> + if (require_constant
>> + && TREE_CODE (inside_init) == NOP_EXPR
>> + && TREE_CODE (TREE_OPERAND (inside_init, 0)) == CALL_EXPR
>> + && is_access_with_size_p (TREE_OPERAND (inside_init, 0)))
>> + inside_init
>> + = get_ref_from_access_with_size (TREE_OPERAND (inside_init, 0));
>> +
>> if (!c_in_omp_for)
>> {
>> if (TREE_CODE (inside_init) == EXCESS_PRECISION_EXPR)
>> diff --git a/gcc/internal-fn.cc b/gcc/internal-fn.cc
>> index 9c09026793fa..eb2c4cd59048 100644
>> --- a/gcc/internal-fn.cc
>> +++ b/gcc/internal-fn.cc
>> @@ -3438,6 +3438,40 @@ expand_DEFERRED_INIT (internal_fn, gcall *stmt)
>> }
>> }
>>
>> +/* Expand the IFN_ACCESS_WITH_SIZE function:
>> + ACCESS_WITH_SIZE (REF_TO_OBJ, REF_TO_SIZE, CLASS_OF_SIZE,
>> + TYPE_OF_SIZE, ACCESS_MODE)
>> + which returns the REF_TO_OBJ same as the 1st argument;
>> +
>> + 1st argument REF_TO_OBJ: The reference to the object;
>> + 2nd argument REF_TO_SIZE: The reference to the size of the object,
>> + 3rd argument CLASS_OF_SIZE: The size referenced by the REF_TO_SIZE
>> represents
>> + 0: the number of bytes.
>> + 1: the number of the elements of the object type;
>> + 4th argument TYPE_OF_SIZE: A constant 0 with its TYPE being the same as
>> the TYPE
>> + of the object referenced by REF_TO_SIZE
>> + 5th argument ACCESS_MODE:
>> + -1: Unknown access semantics
>> + 0: none
>> + 1: read_only
>> + 2: write_only
>> + 3: read_write
>> +
>> + Both the return type and the type of the first argument of this
>> + function have been converted from the incomplete array type to
>> + the corresponding pointer type.
>> +
>> + For each call to a .ACCESS_WITH_SIZE, replace it with its 1st argument.
>> */
>> +
>> +static void
>> +expand_ACCESS_WITH_SIZE (internal_fn, gcall *stmt)
>> +{
>> + tree lhs = gimple_call_lhs (stmt);
>> + tree ref_to_obj = gimple_call_arg (stmt, 0);
>> + if (lhs)
>> + expand_assignment (lhs, ref_to_obj, false);
>> +}
>> +
>> /* The size of an OpenACC compute dimension. */
>>
>> static void
>> diff --git a/gcc/internal-fn.def b/gcc/internal-fn.def
>> index 25badbb86e56..8de1fa882e95 100644
>> --- a/gcc/internal-fn.def
>> +++ b/gcc/internal-fn.def
>> @@ -512,6 +512,11 @@ DEF_INTERNAL_FN (PHI, 0, NULL)
>> automatic variable. */
>> DEF_INTERNAL_FN (DEFERRED_INIT, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
>>
>> +/* A function to associate the access size and access mode information
>> + with the corresponding reference to an object. It only reads from the
>> + 2nd argument. */
>> +DEF_INTERNAL_FN (ACCESS_WITH_SIZE, ECF_PURE | ECF_LEAF | ECF_NOTHROW, NULL)
>> +
>> /* DIM_SIZE and DIM_POS return the size of a particular compute
>> dimension and the executing thread's position within that
>> dimension. DIM_POS is pure (and not const) so that it isn't
>> diff --git a/gcc/testsuite/gcc.dg/flex-array-counted-by-2.c
>> b/gcc/testsuite/gcc.dg/flex-array-counted-by-2.c
>> new file mode 100644
>> index 000000000000..d4899a63af3c
>> --- /dev/null
>> +++ b/gcc/testsuite/gcc.dg/flex-array-counted-by-2.c
>> @@ -0,0 +1,112 @@
>> +/* Test the code generation for the new attribute counted_by.
>> + And also the offsetof operator on such array. */
>> +/* { dg-do run } */
>> +/* { dg-options "-O2 -fdump-tree-original" } */
>> +
>> +#include <stdlib.h>
>> +
>> +struct annotated {
>> + int b;
>> + char c[] __attribute__ ((counted_by (b)));
>> +} *array_annotated;
>> +
>> +static struct annotated static_annotated = { sizeof "hello", "hello" };
>> +static char *y = static_annotated.c;
>> +
>> +struct flex {
>> + int b;
>> + char c[];
>> +};
>> +
>> +struct nested_annotated {
>> + struct {
>> + union {
>> + int b;
>> + float f;
>> + };
>> + int n;
>> + };
>> + char c[] __attribute__ ((counted_by (b)));
>> +} *array_nested_annotated;
>> +
>> +static struct nested_annotated nested_static_annotated
>> + = { sizeof "hello1", 0, "hello1" };
>> +static char *nested_y = nested_static_annotated.c;
>> +
>> +struct nested_flex {
>> + struct {
>> + union {
>> + int b;
>> + float f;
>> + };
>> + int n;
>> + };
>> + char c[];
>> +};
>> +
>> +void __attribute__((__noinline__)) setup (int normal_count, int attr_count)
>> +{
>> + array_annotated
>> + = (struct annotated *)malloc (sizeof (struct annotated)
>> + + attr_count * sizeof (char));
>> + array_annotated->b = attr_count;
>> +
>> + array_nested_annotated
>> + = (struct nested_annotated *)malloc (sizeof (struct nested_annotated)
>> + + attr_count * sizeof (char));
>> + array_nested_annotated->b = attr_count;
>> +
>> + return;
>> +}
>> +
>> +void __attribute__((__noinline__)) test (char a, char b)
>> +{
>> + if (__builtin_offsetof (struct annotated, c[0])
>> + != __builtin_offsetof (struct flex, c[0]))
>> + abort ();
>> + if (__builtin_offsetof (struct annotated, c[1])
>> + != __builtin_offsetof (struct flex, c[1]))
>> + abort ();
>> + if (__builtin_offsetof (struct nested_annotated, c[0])
>> + != __builtin_offsetof (struct nested_flex, c[0]))
>> + abort ();
>> + if (__builtin_offsetof (struct nested_annotated, c[1])
>> + != __builtin_offsetof (struct nested_flex, c[1]))
>> + abort ();
>> +
>> + if (__builtin_types_compatible_p (typeof (array_annotated->c),
>> + typeof (&(array_annotated->c)[0])))
>> + abort ();
>> + if (__builtin_types_compatible_p (typeof (array_nested_annotated->c),
>> + typeof (&(array_nested_annotated->c)[0])))
>> + abort ();
>> +
>> + if (__alignof (array_annotated->c) != __alignof (char))
>> + abort ();
>> + if (__alignof (array_nested_annotated->c) != __alignof (char))
>> + abort ();
>> +
>> + if ((unsigned long) array_annotated->c != (unsigned long)
>> &array_annotated->c)
>> + abort ();
>> + if ((unsigned long) array_nested_annotated->c
>> + != (unsigned long) &array_nested_annotated->c)
>> + abort ();
>> +
>> + array_annotated->c[2] = a;
>> + array_nested_annotated->c[3] = b;
>> +
>> + if (y[2] != 'l') abort ();
>> + if (nested_y[4] !='o') abort ();
>> +
>> +}
>> +
>> +int main(int argc, char *argv[])
>> +{
>> + setup (10,10);
>> + test ('A', 'B');
>> + if (array_annotated->c[2] != 'A') abort ();
>> + if (array_nested_annotated->c[3] != 'B') abort ();
>> + return 0;
>> +}
>> +
>> +/* { dg-final { scan-tree-dump-times "ACCESS_WITH_SIZE" 8 "original" } } */
>> diff --git a/gcc/tree.cc b/gcc/tree.cc
>> index 6564b002dc1a..01572fe70f72 100644
>> --- a/gcc/tree.cc
>> +++ b/gcc/tree.cc
>> @@ -13405,6 +13405,28 @@ component_ref_size (tree ref, special_array_member
>> *sam /* = NULL */)
>> ? NULL_TREE : size_zero_node);
>> }
>>
>> +/* Return true if the given node CALL is a call to a .ACCESS_WITH_SIZE
>> + function. */
>> +bool
>> +is_access_with_size_p (const_tree call)
>> +{
>> + if (TREE_CODE (call) != CALL_EXPR)
>> + return false;
>> + if (CALL_EXPR_IFN (call) == IFN_ACCESS_WITH_SIZE)
>> + return true;
>> + return false;
>> +}
>> +
>> +/* Get the corresponding reference from the call to a .ACCESS_WITH_SIZE.
>> + * i.e the first argument of this call. Return NULL_TREE otherwise. */
>> +tree
>> +get_ref_from_access_with_size (tree call)
>> +{
>> + if (is_access_with_size_p (call))
>> + return CALL_EXPR_ARG (call, 0);
>> + return NULL_TREE;
>> +}
>> +
>> /* Return the machine mode of T. For vectors, returns the mode of the
>> inner type. The main use case is to feed the result to HONOR_NANS,
>> avoiding the BLKmode that a direct TYPE_MODE (T) might return. */
>> diff --git a/gcc/tree.h b/gcc/tree.h
>> index ee2aae332a41..604885641184 100644
>> --- a/gcc/tree.h
>> +++ b/gcc/tree.h
>> @@ -5772,6 +5772,14 @@ extern special_array_member component_ref_sam_type
>> (tree);
>> cannot be determined. */
>> extern tree component_ref_size (tree, special_array_member * = NULL);
>>
>> +/* Return true if the given node is a call to a .ACCESS_WITH_SIZE
>> + function. */
>> +extern bool is_access_with_size_p (const_tree);
>> +
>> +/* Get the corresponding reference from the call to a .ACCESS_WITH_SIZE,
>> + * i.e. the first argument of this call. Return NULL_TREE otherwise. */
>> +extern tree get_ref_from_access_with_size (tree);
>> +
>> extern int tree_map_base_eq (const void *, const void *);
>> extern unsigned int tree_map_base_hash (const void *);
>> extern bool tree_map_base_marked_p (const void *);
>>
>
> --
> Richard Biener <rguent...@suse.de>
> SUSE Software Solutions Germany GmbH,
> Frankenstrasse 146, 90461 Nuernberg, Germany;
> GF: Ivo Totev, Andrew McDonald, Werner Knoblich; (HRB 36809, AG Nuernberg)