Message ID | 20240216194723.391359-3-qing.zhao@oracle.com |
---|---|
State | New |
Headers | show |
Series | New attribute "counted_by" to annotate bounds for C99 FAM(PR108896) | expand |
On 2024-02-16 14:47, 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 routines initializer_constant_valid_p_1 > and output_constant in varasm.cc are updated to handle calls to > .ACCESS_WITH_SIZE. > (initializer_constant_valid_p_1 and output_constant in varasm.c) > > 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) > * Adjust alias analysis to exclude the new internal from clobbering anything. > (ref_maybe_used_by_call_p_1 and call_may_clobber_ref_p_1 in tree-ssa-alias.cc) > * Adjust dead code elimination to eliminate the call to .ACCESS_WITH_SIZE when > it's LHS is eliminated as dead code. > (eliminate_unnecessary_stmts in tree-ssa-dce.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) > > 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. > > gcc/ChangeLog: > > * internal-fn.cc (expand_ACCESS_WITH_SIZE): New function. > * internal-fn.def (ACCESS_WITH_SIZE): New internal function. > * tree-ssa-alias.cc (ref_maybe_used_by_call_p_1): Special case > IFN_ACCESS_WITH_SIZE. > (call_may_clobber_ref_p_1): Special case IFN_ACCESS_WITH_SIZE. > * tree-ssa-dce.cc (eliminate_unnecessary_stmts): Eliminate the call > to .ACCESS_WITH_SIZE when its LHS is dead. > * tree.cc (process_call_operands): Adjust side effect for function > .ACCESS_WITH_SIZE. > (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. > * varasm.cc (initializer_constant_valid_p_1): Handle call to > .ACCESS_WITH_SIZE. > (output_constant): Handle call to .ACCESS_WITH_SIZE. > > 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 | 128 +++++++++++++++++- > gcc/internal-fn.cc | 36 +++++ > gcc/internal-fn.def | 4 + > .../gcc.dg/flex-array-counted-by-2.c | 112 +++++++++++++++ > gcc/tree-ssa-alias.cc | 2 + > gcc/tree-ssa-dce.cc | 5 +- > gcc/tree.cc | 25 +++- > gcc/tree.h | 8 ++ > gcc/varasm.cc | 10 ++ > 11 files changed, 332 insertions(+), 10 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 c31349dae2ff..a6ed5ac43bb1 100644 > --- a/gcc/c/c-parser.cc > +++ b/gcc/c/c-parser.cc > @@ -10850,9 +10850,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, > @@ -10879,11 +10882,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 c7c23edc4840..59b69ca54f0f 100644 > --- a/gcc/c/c-tree.h > +++ b/gcc/c/c-tree.h > @@ -778,7 +778,7 @@ extern void mark_exp_read (tree); > extern tree composite_type (tree, tree); > extern tree lookup_field (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 cead0a055068..a29a7d7ec029 100644 > --- a/gcc/c/c-typeck.cc > +++ b/gcc/c/c-typeck.cc > @@ -2546,15 +2546,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; > +} Build the second arg, i.e. the address of the counted_by field. OK. > + > +/* 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 orignal 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 > + 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); > @@ -2626,7 +2727,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; > > @@ -2645,6 +2752,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; > @@ -5048,7 +5161,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 > @@ -5195,6 +5312,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; > } > diff --git a/gcc/internal-fn.cc b/gcc/internal-fn.cc > index a07f25f3aee3..331dfe57879b 100644 > --- a/gcc/internal-fn.cc > +++ b/gcc/internal-fn.cc > @@ -3393,6 +3393,42 @@ 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: unknown; > + 1: the number of the elements of the object type; > + 2: the number of bytes; Is there value in emitting ACCESS_WITH_SIZE for CLASS_OF_SIZE == 0, since it'll just be ignored? > + 4th argument TYPE_OF_SIZE: A constant 0 with the TYPE of the object > + refed 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); > + else > + emit_insn (expand_normal (ref_to_obj)); > +} > + > /* The size of an OpenACC compute dimension. */ > > static void > diff --git a/gcc/internal-fn.def b/gcc/internal-fn.def > index c14d30365c14..0801c8bfe61d 100644 > --- a/gcc/internal-fn.def > +++ b/gcc/internal-fn.def > @@ -510,6 +510,10 @@ 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. */ > +DEF_INTERNAL_FN (ACCESS_WITH_SIZE, 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..84899ae620f3 > --- /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-ssa-alias.cc b/gcc/tree-ssa-alias.cc > index e7c1c1aa6243..8c070e173bdd 100644 > --- a/gcc/tree-ssa-alias.cc > +++ b/gcc/tree-ssa-alias.cc > @@ -2823,6 +2823,7 @@ ref_maybe_used_by_call_p_1 (gcall *call, ao_ref *ref, bool tbaa_p) > return false; > case IFN_MASK_STORE_LANES: > case IFN_MASK_LEN_STORE_LANES: > + case IFN_ACCESS_WITH_SIZE: > goto process_args; > case IFN_MASK_LOAD: > case IFN_LEN_LOAD: > @@ -3073,6 +3074,7 @@ call_may_clobber_ref_p_1 (gcall *call, ao_ref *ref, bool tbaa_p) > case IFN_UBSAN_OBJECT_SIZE: > case IFN_UBSAN_PTR: > case IFN_ASAN_CHECK: > + case IFN_ACCESS_WITH_SIZE: > return false; > case IFN_MASK_STORE: > case IFN_LEN_STORE: > diff --git a/gcc/tree-ssa-dce.cc b/gcc/tree-ssa-dce.cc > index 636c471d4c89..a54fb1b754dd 100644 > --- a/gcc/tree-ssa-dce.cc > +++ b/gcc/tree-ssa-dce.cc > @@ -1459,8 +1459,8 @@ eliminate_unnecessary_stmts (bool aggressive) > update_stmt (stmt); > release_ssa_name (name); > > - /* GOMP_SIMD_LANE (unless three argument) or ASAN_POISON > - without lhs is not needed. */ > + /* GOMP_SIMD_LANE (unless three argument), ASAN_POISON > + or .ACCESS_WITH_SIZE without lhs is not needed. */ > if (gimple_call_internal_p (stmt)) > switch (gimple_call_internal_fn (stmt)) > { > @@ -1470,6 +1470,7 @@ eliminate_unnecessary_stmts (bool aggressive) > break; > /* FALLTHRU */ > case IFN_ASAN_POISON: > + case IFN_ACCESS_WITH_SIZE: > remove_dead_stmt (&gsi, bb, to_remove_edges); > break; > default: > diff --git a/gcc/tree.cc b/gcc/tree.cc > index 3dff8c510832..18b4e729a5bc 100644 > --- a/gcc/tree.cc > +++ b/gcc/tree.cc > @@ -4068,7 +4068,8 @@ process_call_operands (tree t) > int i = call_expr_flags (t); > > /* Calls have side-effects, except those to const or pure functions. */ > - if ((i & ECF_LOOPING_CONST_OR_PURE) || !(i & (ECF_CONST | ECF_PURE))) > + if ((i & ECF_LOOPING_CONST_OR_PURE) > + || (!(i & (ECF_CONST | ECF_PURE)) && !is_access_with_size_p (t))) > side_effects = true; > /* Propagate TREE_READONLY of arguments for const functions. */ > if (i & ECF_CONST) > @@ -13362,6 +13363,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 972a067a1f7a..73464d3e3ae3 100644 > --- a/gcc/tree.h > +++ b/gcc/tree.h > @@ -5760,6 +5760,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 *); > diff --git a/gcc/varasm.cc b/gcc/varasm.cc > index fa17eff551e8..0e6e44e0420c 100644 > --- a/gcc/varasm.cc > +++ b/gcc/varasm.cc > @@ -5082,6 +5082,11 @@ initializer_constant_valid_p_1 (tree value, tree endtype, tree *cache) > } > return ret; > > + case CALL_EXPR: > + /* for a call to .ACCESS_WITH_SIZE, check the first argument. */ > + if (tree ref = get_ref_from_access_with_size (value)) > + return initializer_constant_valid_p_1 (ref, endtype, cache); > + /* FALLTHROUGH. */ > default: > break; > } > @@ -5276,6 +5281,11 @@ output_constant (tree exp, unsigned HOST_WIDE_INT size, unsigned int align, > exp = TREE_OPERAND (exp, 0); > } > > + /* for a call to .ACCESS_WITH_SIZE, check the first argument. */ > + if (TREE_CODE (exp) == CALL_EXPR) > + if (tree ref = get_ref_from_access_with_size (exp)) > + exp = ref; > + > code = TREE_CODE (TREE_TYPE (exp)); > thissize = int_size_in_bytes (TREE_TYPE (exp)); >
> On Mar 11, 2024, at 13:09, Siddhesh Poyarekar <siddhesh@gotplt.org> wrote: > > > > On 2024-02-16 14:47, 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 routines initializer_constant_valid_p_1 >> and output_constant in varasm.cc are updated to handle calls to >> .ACCESS_WITH_SIZE. >> (initializer_constant_valid_p_1 and output_constant in varasm.c) >> 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) >> * Adjust alias analysis to exclude the new internal from clobbering anything. >> (ref_maybe_used_by_call_p_1 and call_may_clobber_ref_p_1 in tree-ssa-alias.cc) >> * Adjust dead code elimination to eliminate the call to .ACCESS_WITH_SIZE when >> it's LHS is eliminated as dead code. >> (eliminate_unnecessary_stmts in tree-ssa-dce.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) >> 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. >> gcc/ChangeLog: >> * internal-fn.cc (expand_ACCESS_WITH_SIZE): New function. >> * internal-fn.def (ACCESS_WITH_SIZE): New internal function. >> * tree-ssa-alias.cc (ref_maybe_used_by_call_p_1): Special case >> IFN_ACCESS_WITH_SIZE. >> (call_may_clobber_ref_p_1): Special case IFN_ACCESS_WITH_SIZE. >> * tree-ssa-dce.cc (eliminate_unnecessary_stmts): Eliminate the call >> to .ACCESS_WITH_SIZE when its LHS is dead. >> * tree.cc (process_call_operands): Adjust side effect for function >> .ACCESS_WITH_SIZE. >> (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. >> * varasm.cc (initializer_constant_valid_p_1): Handle call to >> .ACCESS_WITH_SIZE. >> (output_constant): Handle call to .ACCESS_WITH_SIZE. >> 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 | 128 +++++++++++++++++- >> gcc/internal-fn.cc | 36 +++++ >> gcc/internal-fn.def | 4 + >> .../gcc.dg/flex-array-counted-by-2.c | 112 +++++++++++++++ >> gcc/tree-ssa-alias.cc | 2 + >> gcc/tree-ssa-dce.cc | 5 +- >> gcc/tree.cc | 25 +++- >> gcc/tree.h | 8 ++ >> gcc/varasm.cc | 10 ++ >> 11 files changed, 332 insertions(+), 10 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 c31349dae2ff..a6ed5ac43bb1 100644 >> --- a/gcc/c/c-parser.cc >> +++ b/gcc/c/c-parser.cc >> @@ -10850,9 +10850,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, >> @@ -10879,11 +10882,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 c7c23edc4840..59b69ca54f0f 100644 >> --- a/gcc/c/c-tree.h >> +++ b/gcc/c/c-tree.h >> @@ -778,7 +778,7 @@ extern void mark_exp_read (tree); >> extern tree composite_type (tree, tree); >> extern tree lookup_field (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 cead0a055068..a29a7d7ec029 100644 >> --- a/gcc/c/c-typeck.cc >> +++ b/gcc/c/c-typeck.cc >> @@ -2546,15 +2546,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; >> +} > > Build the second arg, i.e. the address of the counted_by field. OK. > >> + >> +/* 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 orignal 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 >> + 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); >> @@ -2626,7 +2727,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; >> @@ -2645,6 +2752,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; >> @@ -5048,7 +5161,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 >> @@ -5195,6 +5312,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; >> } >> diff --git a/gcc/internal-fn.cc b/gcc/internal-fn.cc >> index a07f25f3aee3..331dfe57879b 100644 >> --- a/gcc/internal-fn.cc >> +++ b/gcc/internal-fn.cc >> @@ -3393,6 +3393,42 @@ 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: unknown; >> + 1: the number of the elements of the object type; >> + 2: the number of bytes; > > Is there value in emitting ACCESS_WITH_SIZE for CLASS_OF_SIZE == 0, since it'll just be ignored? Yes, you are right, looks like there is no need for the “unknown” type. Maybe it’s better to change this argument as the following: + 3rd argument IS_SIZE_NUM_ELEMENTS: TRUE if the size referenced by the REF_TO_SIZE represents + the number of the elements of the object type; otherwise, it’s the number of bytes. Thanks. Qing > >> + 4th argument TYPE_OF_SIZE: A constant 0 with the TYPE of the object >> + refed 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); >> + else >> + emit_insn (expand_normal (ref_to_obj)); >> +} >> + >> /* The size of an OpenACC compute dimension. */ >> static void >> diff --git a/gcc/internal-fn.def b/gcc/internal-fn.def >> index c14d30365c14..0801c8bfe61d 100644 >> --- a/gcc/internal-fn.def >> +++ b/gcc/internal-fn.def >> @@ -510,6 +510,10 @@ 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. */ >> +DEF_INTERNAL_FN (ACCESS_WITH_SIZE, 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..84899ae620f3 >> --- /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-ssa-alias.cc b/gcc/tree-ssa-alias.cc >> index e7c1c1aa6243..8c070e173bdd 100644 >> --- a/gcc/tree-ssa-alias.cc >> +++ b/gcc/tree-ssa-alias.cc >> @@ -2823,6 +2823,7 @@ ref_maybe_used_by_call_p_1 (gcall *call, ao_ref *ref, bool tbaa_p) >> return false; >> case IFN_MASK_STORE_LANES: >> case IFN_MASK_LEN_STORE_LANES: >> + case IFN_ACCESS_WITH_SIZE: >> goto process_args; >> case IFN_MASK_LOAD: >> case IFN_LEN_LOAD: >> @@ -3073,6 +3074,7 @@ call_may_clobber_ref_p_1 (gcall *call, ao_ref *ref, bool tbaa_p) >> case IFN_UBSAN_OBJECT_SIZE: >> case IFN_UBSAN_PTR: >> case IFN_ASAN_CHECK: >> + case IFN_ACCESS_WITH_SIZE: >> return false; >> case IFN_MASK_STORE: >> case IFN_LEN_STORE: >> diff --git a/gcc/tree-ssa-dce.cc b/gcc/tree-ssa-dce.cc >> index 636c471d4c89..a54fb1b754dd 100644 >> --- a/gcc/tree-ssa-dce.cc >> +++ b/gcc/tree-ssa-dce.cc >> @@ -1459,8 +1459,8 @@ eliminate_unnecessary_stmts (bool aggressive) >> update_stmt (stmt); >> release_ssa_name (name); >> - /* GOMP_SIMD_LANE (unless three argument) or ASAN_POISON >> - without lhs is not needed. */ >> + /* GOMP_SIMD_LANE (unless three argument), ASAN_POISON >> + or .ACCESS_WITH_SIZE without lhs is not needed. */ >> if (gimple_call_internal_p (stmt)) >> switch (gimple_call_internal_fn (stmt)) >> { >> @@ -1470,6 +1470,7 @@ eliminate_unnecessary_stmts (bool aggressive) >> break; >> /* FALLTHRU */ >> case IFN_ASAN_POISON: >> + case IFN_ACCESS_WITH_SIZE: >> remove_dead_stmt (&gsi, bb, to_remove_edges); >> break; >> default: >> diff --git a/gcc/tree.cc b/gcc/tree.cc >> index 3dff8c510832..18b4e729a5bc 100644 >> --- a/gcc/tree.cc >> +++ b/gcc/tree.cc >> @@ -4068,7 +4068,8 @@ process_call_operands (tree t) >> int i = call_expr_flags (t); >> /* Calls have side-effects, except those to const or pure functions. */ >> - if ((i & ECF_LOOPING_CONST_OR_PURE) || !(i & (ECF_CONST | ECF_PURE))) >> + if ((i & ECF_LOOPING_CONST_OR_PURE) >> + || (!(i & (ECF_CONST | ECF_PURE)) && !is_access_with_size_p (t))) >> side_effects = true; >> /* Propagate TREE_READONLY of arguments for const functions. */ >> if (i & ECF_CONST) >> @@ -13362,6 +13363,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 972a067a1f7a..73464d3e3ae3 100644 >> --- a/gcc/tree.h >> +++ b/gcc/tree.h >> @@ -5760,6 +5760,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 *); >> diff --git a/gcc/varasm.cc b/gcc/varasm.cc >> index fa17eff551e8..0e6e44e0420c 100644 >> --- a/gcc/varasm.cc >> +++ b/gcc/varasm.cc >> @@ -5082,6 +5082,11 @@ initializer_constant_valid_p_1 (tree value, tree endtype, tree *cache) >> } >> return ret; >> + case CALL_EXPR: >> + /* for a call to .ACCESS_WITH_SIZE, check the first argument. */ >> + if (tree ref = get_ref_from_access_with_size (value)) >> + return initializer_constant_valid_p_1 (ref, endtype, cache); >> + /* FALLTHROUGH. */ >> default: >> break; >> } >> @@ -5276,6 +5281,11 @@ output_constant (tree exp, unsigned HOST_WIDE_INT size, unsigned int align, >> exp = TREE_OPERAND (exp, 0); >> } >> + /* for a call to .ACCESS_WITH_SIZE, check the first argument. */ >> + if (TREE_CODE (exp) == CALL_EXPR) >> + if (tree ref = get_ref_from_access_with_size (exp)) >> + exp = ref; >> + >> code = TREE_CODE (TREE_TYPE (exp)); >> thissize = int_size_in_bytes (TREE_TYPE (exp)); >>
diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc index c31349dae2ff..a6ed5ac43bb1 100644 --- a/gcc/c/c-parser.cc +++ b/gcc/c/c-parser.cc @@ -10850,9 +10850,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, @@ -10879,11 +10882,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 c7c23edc4840..59b69ca54f0f 100644 --- a/gcc/c/c-tree.h +++ b/gcc/c/c-tree.h @@ -778,7 +778,7 @@ extern void mark_exp_read (tree); extern tree composite_type (tree, tree); extern tree lookup_field (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 cead0a055068..a29a7d7ec029 100644 --- a/gcc/c/c-typeck.cc +++ b/gcc/c/c-typeck.cc @@ -2546,15 +2546,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 orignal 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 + 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); @@ -2626,7 +2727,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; @@ -2645,6 +2752,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; @@ -5048,7 +5161,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 @@ -5195,6 +5312,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; } diff --git a/gcc/internal-fn.cc b/gcc/internal-fn.cc index a07f25f3aee3..331dfe57879b 100644 --- a/gcc/internal-fn.cc +++ b/gcc/internal-fn.cc @@ -3393,6 +3393,42 @@ 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: unknown; + 1: the number of the elements of the object type; + 2: the number of bytes; + 4th argument TYPE_OF_SIZE: A constant 0 with the TYPE of the object + refed 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); + else + emit_insn (expand_normal (ref_to_obj)); +} + /* The size of an OpenACC compute dimension. */ static void diff --git a/gcc/internal-fn.def b/gcc/internal-fn.def index c14d30365c14..0801c8bfe61d 100644 --- a/gcc/internal-fn.def +++ b/gcc/internal-fn.def @@ -510,6 +510,10 @@ 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. */ +DEF_INTERNAL_FN (ACCESS_WITH_SIZE, 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..84899ae620f3 --- /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-ssa-alias.cc b/gcc/tree-ssa-alias.cc index e7c1c1aa6243..8c070e173bdd 100644 --- a/gcc/tree-ssa-alias.cc +++ b/gcc/tree-ssa-alias.cc @@ -2823,6 +2823,7 @@ ref_maybe_used_by_call_p_1 (gcall *call, ao_ref *ref, bool tbaa_p) return false; case IFN_MASK_STORE_LANES: case IFN_MASK_LEN_STORE_LANES: + case IFN_ACCESS_WITH_SIZE: goto process_args; case IFN_MASK_LOAD: case IFN_LEN_LOAD: @@ -3073,6 +3074,7 @@ call_may_clobber_ref_p_1 (gcall *call, ao_ref *ref, bool tbaa_p) case IFN_UBSAN_OBJECT_SIZE: case IFN_UBSAN_PTR: case IFN_ASAN_CHECK: + case IFN_ACCESS_WITH_SIZE: return false; case IFN_MASK_STORE: case IFN_LEN_STORE: diff --git a/gcc/tree-ssa-dce.cc b/gcc/tree-ssa-dce.cc index 636c471d4c89..a54fb1b754dd 100644 --- a/gcc/tree-ssa-dce.cc +++ b/gcc/tree-ssa-dce.cc @@ -1459,8 +1459,8 @@ eliminate_unnecessary_stmts (bool aggressive) update_stmt (stmt); release_ssa_name (name); - /* GOMP_SIMD_LANE (unless three argument) or ASAN_POISON - without lhs is not needed. */ + /* GOMP_SIMD_LANE (unless three argument), ASAN_POISON + or .ACCESS_WITH_SIZE without lhs is not needed. */ if (gimple_call_internal_p (stmt)) switch (gimple_call_internal_fn (stmt)) { @@ -1470,6 +1470,7 @@ eliminate_unnecessary_stmts (bool aggressive) break; /* FALLTHRU */ case IFN_ASAN_POISON: + case IFN_ACCESS_WITH_SIZE: remove_dead_stmt (&gsi, bb, to_remove_edges); break; default: diff --git a/gcc/tree.cc b/gcc/tree.cc index 3dff8c510832..18b4e729a5bc 100644 --- a/gcc/tree.cc +++ b/gcc/tree.cc @@ -4068,7 +4068,8 @@ process_call_operands (tree t) int i = call_expr_flags (t); /* Calls have side-effects, except those to const or pure functions. */ - if ((i & ECF_LOOPING_CONST_OR_PURE) || !(i & (ECF_CONST | ECF_PURE))) + if ((i & ECF_LOOPING_CONST_OR_PURE) + || (!(i & (ECF_CONST | ECF_PURE)) && !is_access_with_size_p (t))) side_effects = true; /* Propagate TREE_READONLY of arguments for const functions. */ if (i & ECF_CONST) @@ -13362,6 +13363,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 972a067a1f7a..73464d3e3ae3 100644 --- a/gcc/tree.h +++ b/gcc/tree.h @@ -5760,6 +5760,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 *); diff --git a/gcc/varasm.cc b/gcc/varasm.cc index fa17eff551e8..0e6e44e0420c 100644 --- a/gcc/varasm.cc +++ b/gcc/varasm.cc @@ -5082,6 +5082,11 @@ initializer_constant_valid_p_1 (tree value, tree endtype, tree *cache) } return ret; + case CALL_EXPR: + /* for a call to .ACCESS_WITH_SIZE, check the first argument. */ + if (tree ref = get_ref_from_access_with_size (value)) + return initializer_constant_valid_p_1 (ref, endtype, cache); + /* FALLTHROUGH. */ default: break; } @@ -5276,6 +5281,11 @@ output_constant (tree exp, unsigned HOST_WIDE_INT size, unsigned int align, exp = TREE_OPERAND (exp, 0); } + /* for a call to .ACCESS_WITH_SIZE, check the first argument. */ + if (TREE_CODE (exp) == CALL_EXPR) + if (tree ref = get_ref_from_access_with_size (exp)) + exp = ref; + code = TREE_CODE (TREE_TYPE (exp)); thissize = int_size_in_bytes (TREE_TYPE (exp));