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, 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)