diff mbox series

[v6,2/5] Convert references with "counted_by" attributes to/from .ACCESS_WITH_SIZE.

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

Commit Message

Qing Zhao Feb. 16, 2024, 7:47 p.m. UTC
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

Comments

Siddhesh Poyarekar March 11, 2024, 5:09 p.m. UTC | #1
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));
>
Qing Zhao March 13, 2024, 7:06 p.m. UTC | #2
> 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 mbox series

Patch

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