[v6] Provide new GCC builtin __builtin_counted_by_ref [PR116016]

Message ID 20240927140119.3263285-1-qing.zhao@oracle.com
State New
Headers
Series [v6] Provide new GCC builtin __builtin_counted_by_ref [PR116016] |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gcc_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_gcc_build--master-aarch64 success Build passed
linaro-tcwg-bot/tcwg_gcc_check--master-aarch64 fail Test failed
linaro-tcwg-bot/tcwg_gcc_check--master-arm success Test passed

Commit Message

Qing Zhao Sept. 27, 2024, 2:01 p.m. UTC
  Hi, this is the 6th version of the patch.

Compare to the 5th version, the major changes are (Address Jakub's comments)

1. delete the new global "in_builtin_counted_by_ref"
2. split the "counted_by" specific handling from the routne "build_component_ref"
   as a new routine "handle_counted_by_for_component_ref"
3. when handle "__builtin_counted_by_ref" in c-parser.cc, if in_typeof or
   in_alignof, call "handle_counted_by_for_component_ref" to generate
   the call to .ACCESS_WITH_SIZE.
4. Add the following into the testing cases. (in builtin-counted-by-ref-1.c)
   A. __alignof:
  __alignof (*__builtin_counted_by_ref (array_annotated->c))

   B. __typeof:
  __typeof (*__builtin_counted_by_ref (array_annotated->c)) 
  __typeof (char[__builtin_counted_by_ref (array_annotated->c)
                      == &array_annotated->b ? 1 : 10]),

bootstrapped and regress tested on both X86 and aarch64. no issue.

Okay for the trunk?

thanks.

Qing.

=================================


With the addition of the 'counted_by' attribute and its wide roll-out
within the Linux kernel, a use case has been found that would be very
nice to have for object allocators: being able to set the counted_by
counter variable without knowing its name.

For example, given:

  struct foo {
    ...
    int counter;
    ...
    struct bar array[] __attribute__((counted_by (counter)));
  } *p;

The existing Linux object allocators are roughly:

  #define MAX(A, B) (A > B) ? (A) : (B)
  #define alloc(P, FAM, COUNT) ({ \
    __auto_type __p = &(P); \
    size_t __size = MAX (sizeof(*P),
			 __builtin_offsetof (__typeof(*P), FAM)
			 + sizeof (*(P->FAM)) * COUNT); \
    *__p = kmalloc(__size); \
  })

Right now, any addition of a counted_by annotation must also
include an open-coded assignment of the counter variable after
the allocation:

  p = alloc(p, array, how_many);
  p->counter = how_many;

In order to avoid the tedious and error-prone work of manually adding
the open-coded counted-by intializations everywhere in the Linux
kernel, a new GCC builtin __builtin_counted_by_ref will be very useful
to be added to help the adoption of the counted-by attribute.

 -- Built-in Function: TYPE __builtin_counted_by_ref (PTR)
     The built-in function '__builtin_counted_by_ref' checks whether the
     array object pointed by the pointer PTR has another object
     associated with it that represents the number of elements in the
     array object through the 'counted_by' attribute (i.e.  the
     counted-by object).  If so, returns a pointer to the corresponding
     counted-by object.  If such counted-by object does not exist,
     returns a NULL pointer.

     This built-in function is only available in C for now.

     The argument PTR must be a pointer to an array.  The TYPE of the
     returned value must be a pointer type pointing to the corresponding
     type of the counted-by object or VOID pointer type in case of a
     NULL pointer being returned.

With this new builtin, the central allocator could be updated to:

  #define MAX(A, B) (A > B) ? (A) : (B)
  #define alloc(P, FAM, COUNT) ({ \
    __auto_type __p = &(P); \
    __auto_type __c = (COUNT); \
    size_t __size = MAX (sizeof (*(*__p)),\
			 __builtin_offsetof (__typeof(*(*__p)),FAM) \
			 + sizeof (*((*__p)->FAM)) * __c); \
    if ((*__p = kmalloc(__size))) { \
      __auto_type ret = __builtin_counted_by_ref((*__p)->FAM); \
      *_Generic(ret, void *: &(size_t){0}, default: ret) = __c; \
    } \
  })

And then structs can gain the counted_by attribute without needing
additional open-coded counter assignments for each struct, and
unannotated structs could still use the same allocator.

	PR c/116016

gcc/c-family/ChangeLog:

	* c-common.cc: Add new __builtin_counted_by_ref.
	* c-common.h (enum rid): Add RID_BUILTIN_COUNTED_BY_REF.

gcc/c/ChangeLog:

	* c-decl.cc (names_builtin_p): Add RID_BUILTIN_COUNTED_BY_REF.
	* c-parser.cc (has_counted_by_object): New routine.
	(get_counted_by_ref): New routine.
	(c_parser_postfix_expression): Handle New RID_BUILTIN_COUNTED_BY_REF.
	* c-tree.h: New routine handle_counted_by_for_component_ref.
	* c-typeck.cc (handle_counted_by_for_component_ref): New routine.
	(build_component_ref): Call the new routine.

gcc/ChangeLog:

	* doc/extend.texi: Add documentation for __builtin_counted_by_ref.

gcc/testsuite/ChangeLog:

	* gcc.dg/builtin-counted-by-ref-1.c: New test.
	* gcc.dg/builtin-counted-by-ref.c: New test.
---
 gcc/c-family/c-common.cc                      |   1 +
 gcc/c-family/c-common.h                       |   1 +
 gcc/c/c-decl.cc                               |   1 +
 gcc/c/c-parser.cc                             |  80 +++++++++++
 gcc/c/c-tree.h                                |   1 +
 gcc/c/c-typeck.cc                             |  34 +++--
 gcc/doc/extend.texi                           |  55 +++++++
 .../gcc.dg/builtin-counted-by-ref-1.c         | 135 ++++++++++++++++++
 gcc/testsuite/gcc.dg/builtin-counted-by-ref.c |  61 ++++++++
 9 files changed, 360 insertions(+), 9 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/builtin-counted-by-ref-1.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-counted-by-ref.c
  

Comments

Jakub Jelinek Sept. 27, 2024, 2:18 p.m. UTC | #1
On Fri, Sep 27, 2024 at 02:01:19PM +0000, Qing Zhao wrote:
> +  /* Currently, only when the array_ref is an indirect_ref to a call to the
> +     .ACCESS_WITH_SIZE, return true.
> +     More cases can be included later when the counted_by attribute is
> +     extended to other situations.  */
> +  if ((TREE_CODE (array_ref) == INDIRECT_REF)

The ()s around the == are useless.

> +      && is_access_with_size_p (TREE_OPERAND (array_ref, 0)))
> +    return true;
> +  return false;
> +}
> +
> +/* Get the reference to the counted-by object associated with the ARRAY_REF.  */
> +static tree
> +get_counted_by_ref (tree array_ref)
> +{
> +  /* Currently, only when the array_ref is an indirect_ref to a call to the
> +     .ACCESS_WITH_SIZE, get the corresponding counted_by ref.
> +     More cases can be included later when the counted_by attribute is
> +     extended to other situations.  */
> +  if ((TREE_CODE (array_ref) == INDIRECT_REF)

Again.

> +	    if (TREE_CODE (TREE_TYPE (ref)) != ARRAY_TYPE)
> +	      {
> +		error_at (loc, "the argument must be an array"
> +			       "%<__builtin_counted_by_ref%>");
> +		expr.set_error ();
> +		break;
> +	      }
> +
> +	    /* if the array ref is inside TYPEOF or ALIGNOF, the call to

Comments should start with capital letter, i.e. If

> +	       .ACCESS_WITH_SIZE was not genereated by the routine

s/genereated/generated/

> +	       build_component_ref by default, we should generate it here.  */
> +	    if ((in_typeof || in_alignof)
		&& TREE_CODE (ref) == COMPONENT_REF)

The above && ... fits on the same line as the rest of the condition.

> +	      ref = handle_counted_by_for_component_ref (loc, ref);
> +
> +	    if (has_counted_by_object (ref))
> +	      expr.value
> +		= get_counted_by_ref (ref);

This too.

> +	    else
> +	      expr.value
> +		= build_int_cst (build_pointer_type (void_type_node), 0);

	    else
	      expr.value = null_pointer_node;
instead.

> +/*
> + * For the COMPONENT_REF ref, check whether it has a counted_by attribute,
> + * if so, wrap this COMPONENT_REF with the corresponding CALL to the
> + * function .ACCESS_WITH_SIZE.
> + * Otherwise, return the ref itself.
> + */

We don't use this style of comments.  No *s at the start of each line, /*
should be immediately followed after space with the first line and */
should be right after . and two spaces.

	Jakub
  
Qing Zhao Sept. 27, 2024, 2:41 p.m. UTC | #2
Jakub,

Thanks a lot for your comments, I will fix all these issues in the next version.

Qing

> On Sep 27, 2024, at 10:18, Jakub Jelinek <jakub@redhat.com> wrote:
> 
> On Fri, Sep 27, 2024 at 02:01:19PM +0000, Qing Zhao wrote:
>> +  /* Currently, only when the array_ref is an indirect_ref to a call to the
>> +     .ACCESS_WITH_SIZE, return true.
>> +     More cases can be included later when the counted_by attribute is
>> +     extended to other situations.  */
>> +  if ((TREE_CODE (array_ref) == INDIRECT_REF)
> 
> The ()s around the == are useless.
> 
>> +      && is_access_with_size_p (TREE_OPERAND (array_ref, 0)))
>> +    return true;
>> +  return false;
>> +}
>> +
>> +/* Get the reference to the counted-by object associated with the ARRAY_REF.  */
>> +static tree
>> +get_counted_by_ref (tree array_ref)
>> +{
>> +  /* Currently, only when the array_ref is an indirect_ref to a call to the
>> +     .ACCESS_WITH_SIZE, get the corresponding counted_by ref.
>> +     More cases can be included later when the counted_by attribute is
>> +     extended to other situations.  */
>> +  if ((TREE_CODE (array_ref) == INDIRECT_REF)
> 
> Again.
> 
>> +    if (TREE_CODE (TREE_TYPE (ref)) != ARRAY_TYPE)
>> +      {
>> + error_at (loc, "the argument must be an array"
>> +       "%<__builtin_counted_by_ref%>");
>> + expr.set_error ();
>> + break;
>> +      }
>> +
>> +    /* if the array ref is inside TYPEOF or ALIGNOF, the call to
> 
> Comments should start with capital letter, i.e. If
> 
>> +       .ACCESS_WITH_SIZE was not genereated by the routine
> 
> s/genereated/generated/
> 
>> +       build_component_ref by default, we should generate it here.  */
>> +    if ((in_typeof || in_alignof)
> && TREE_CODE (ref) == COMPONENT_REF)
> 
> The above && ... fits on the same line as the rest of the condition.
> 
>> +      ref = handle_counted_by_for_component_ref (loc, ref);
>> +
>> +    if (has_counted_by_object (ref))
>> +      expr.value
>> + = get_counted_by_ref (ref);
> 
> This too.
> 
>> +    else
>> +      expr.value
>> + = build_int_cst (build_pointer_type (void_type_node), 0);
> 
>    else
>      expr.value = null_pointer_node;
> instead.
> 
>> +/*
>> + * For the COMPONENT_REF ref, check whether it has a counted_by attribute,
>> + * if so, wrap this COMPONENT_REF with the corresponding CALL to the
>> + * function .ACCESS_WITH_SIZE.
>> + * Otherwise, return the ref itself.
>> + */
> 
> We don't use this style of comments.  No *s at the start of each line, /*
> should be immediately followed after space with the first line and */
> should be right after . and two spaces.
> 
> Jakub
>
  

Patch

diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
index ec6a5da892d..8ad9b998e7b 100644
--- a/gcc/c-family/c-common.cc
+++ b/gcc/c-family/c-common.cc
@@ -430,6 +430,7 @@  const struct c_common_resword c_common_reswords[] =
   { "__builtin_choose_expr", RID_CHOOSE_EXPR, D_CONLY },
   { "__builtin_complex", RID_BUILTIN_COMPLEX, D_CONLY },
   { "__builtin_convertvector", RID_BUILTIN_CONVERTVECTOR, 0 },
+  { "__builtin_counted_by_ref", RID_BUILTIN_COUNTED_BY_REF, D_CONLY },
   { "__builtin_has_attribute", RID_BUILTIN_HAS_ATTRIBUTE, 0 },
   { "__builtin_launder", RID_BUILTIN_LAUNDER, D_CXXONLY },
   { "__builtin_shuffle", RID_BUILTIN_SHUFFLE, 0 },
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index 027f077d51b..4400d2c5d43 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -110,6 +110,7 @@  enum rid
   RID_TYPES_COMPATIBLE_P,      RID_BUILTIN_COMPLEX,	   RID_BUILTIN_SHUFFLE,
   RID_BUILTIN_SHUFFLEVECTOR,   RID_BUILTIN_CONVERTVECTOR,  RID_BUILTIN_TGMATH,
   RID_BUILTIN_HAS_ATTRIBUTE,   RID_BUILTIN_ASSOC_BARRIER,  RID_BUILTIN_STDC,
+  RID_BUILTIN_COUNTED_BY_REF,
   RID_DFLOAT32, RID_DFLOAT64, RID_DFLOAT128,
 
   /* TS 18661-3 keywords, in the same sequence as the TI_* values.  */
diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index aa7f69d1b7b..1145dde9bb1 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -11788,6 +11788,7 @@  names_builtin_p (const char *name)
     case RID_BUILTIN_SHUFFLE:
     case RID_BUILTIN_SHUFFLEVECTOR:
     case RID_BUILTIN_STDC:
+    case RID_BUILTIN_COUNTED_BY_REF:
     case RID_CHOOSE_EXPR:
     case RID_OFFSETOF:
     case RID_TYPES_COMPATIBLE_P:
diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc
index a681438cbbe..f8f17e5f6eb 100644
--- a/gcc/c/c-parser.cc
+++ b/gcc/c/c-parser.cc
@@ -10647,6 +10647,35 @@  c_parser_predefined_identifier (c_parser *parser)
   return expr;
 }
 
+/* Check whether the ARRAY_REF has an counted-by object associated with it
+   through the "counted_by" attribute.  */
+static bool
+has_counted_by_object (tree array_ref)
+{
+  /* Currently, only when the array_ref is an indirect_ref to a call to the
+     .ACCESS_WITH_SIZE, return true.
+     More cases can be included later when the counted_by attribute is
+     extended to other situations.  */
+  if ((TREE_CODE (array_ref) == INDIRECT_REF)
+      && is_access_with_size_p (TREE_OPERAND (array_ref, 0)))
+    return true;
+  return false;
+}
+
+/* Get the reference to the counted-by object associated with the ARRAY_REF.  */
+static tree
+get_counted_by_ref (tree array_ref)
+{
+  /* Currently, only when the array_ref is an indirect_ref to a call to the
+     .ACCESS_WITH_SIZE, get the corresponding counted_by ref.
+     More cases can be included later when the counted_by attribute is
+     extended to other situations.  */
+  if ((TREE_CODE (array_ref) == INDIRECT_REF)
+      && is_access_with_size_p (TREE_OPERAND (array_ref, 0)))
+    return CALL_EXPR_ARG (TREE_OPERAND (array_ref, 0), 1);
+  return NULL_TREE;
+}
+
 /* Parse a postfix expression (C90 6.3.1-6.3.2, C99 6.5.1-6.5.2,
    C11 6.5.1-6.5.2).  Compound literals aren't handled here; callers have to
    call c_parser_postfix_expression_after_paren_type on encountering them.
@@ -11741,6 +11770,57 @@  c_parser_postfix_expression (c_parser *parser)
 	    set_c_expr_source_range (&expr, loc, close_paren_loc);
 	    break;
 	  }
+	case RID_BUILTIN_COUNTED_BY_REF:
+	  {
+	    vec<c_expr_t, va_gc> *cexpr_list;
+	    c_expr_t *e_p;
+	    location_t close_paren_loc;
+
+	    c_parser_consume_token (parser);
+	    if (!c_parser_get_builtin_args (parser,
+					    "__builtin_counted_by_ref",
+					    &cexpr_list, false,
+					    &close_paren_loc))
+	      {
+		expr.set_error ();
+		break;
+	      }
+	    if (vec_safe_length (cexpr_list) != 1)
+	      {
+		error_at (loc, "wrong number of arguments to "
+			       "%<__builtin_counted_by_ref%>");
+		expr.set_error ();
+		break;
+	      }
+
+	    e_p = &(*cexpr_list)[0];
+	    tree ref = e_p->value;
+
+	    if (TREE_CODE (TREE_TYPE (ref)) != ARRAY_TYPE)
+	      {
+		error_at (loc, "the argument must be an array"
+			       "%<__builtin_counted_by_ref%>");
+		expr.set_error ();
+		break;
+	      }
+
+	    /* if the array ref is inside TYPEOF or ALIGNOF, the call to
+	       .ACCESS_WITH_SIZE was not genereated by the routine
+	       build_component_ref by default, we should generate it here.  */
+	    if ((in_typeof || in_alignof)
+		&& TREE_CODE (ref) == COMPONENT_REF)
+	      ref = handle_counted_by_for_component_ref (loc, ref);
+
+	    if (has_counted_by_object (ref))
+	      expr.value
+		= get_counted_by_ref (ref);
+	    else
+	      expr.value
+		= build_int_cst (build_pointer_type (void_type_node), 0);
+
+	    set_c_expr_source_range (&expr, loc, close_paren_loc);
+	    break;
+	  }
 	case RID_BUILTIN_SHUFFLE:
 	  {
 	    vec<c_expr_t, va_gc> *cexpr_list;
diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h
index b3e7bb013b6..9a067c93240 100644
--- a/gcc/c/c-tree.h
+++ b/gcc/c/c-tree.h
@@ -780,6 +780,7 @@  extern tree composite_type (tree, tree);
 extern tree lookup_field (const_tree, tree);
 extern tree build_component_ref (location_t, tree, tree, location_t,
 				 location_t, bool = true);
+extern tree handle_counted_by_for_component_ref (location_t, tree);
 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 ba6d96d26b2..5ac8688df59 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -2766,6 +2766,28 @@  build_access_with_size_for_counted_by (location_t loc, tree ref,
   return call;
 }
 
+/*
+ * For the COMPONENT_REF ref, check whether it has a counted_by attribute,
+ * if so, wrap this COMPONENT_REF with the corresponding CALL to the
+ * function .ACCESS_WITH_SIZE.
+ * Otherwise, return the ref itself.
+ */
+tree
+handle_counted_by_for_component_ref (location_t loc, tree ref)
+{
+  gcc_assert (TREE_CODE (ref) == COMPONENT_REF);
+  tree datum = TREE_OPERAND (ref, 0);
+  tree subdatum = TREE_OPERAND (ref, 1);
+  tree counted_by_type = NULL_TREE;
+  tree counted_by_ref = build_counted_by_ref (datum, subdatum,
+					      &counted_by_type);
+  if (counted_by_ref)
+    ref = build_access_with_size_for_counted_by (loc, ref,
+						 counted_by_ref,
+						 counted_by_type);
+  return ref;
+}
+
 /* 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
@@ -2849,13 +2871,9 @@  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;
 
@@ -2875,10 +2893,8 @@  build_component_ref (location_t loc, tree datum, tree component,
 			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 (handle_counted_by)
+	    ref = handle_counted_by_for_component_ref (loc, ref);
 
 	  if (TREE_READONLY (subdatum)
 	      || (use_datum_quals && TREE_READONLY (datum)))
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index c95df845634..12495a58952 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -15287,6 +15287,61 @@  initializers of variables usable in constant expressions.   For more details
 refer to the latest revision of the C++ standard.
 @enddefbuiltin
 
+@defbuiltin{@var{type} __builtin_counted_by_ref (@var{ptr})}
+The built-in function @code{__builtin_counted_by_ref} checks whether the array
+object pointed by the pointer @var{ptr} has another object associated with it
+that represents the number of elements in the array object through the
+@code{counted_by} attribute (i.e. the counted-by object). If so, returns a
+pointer to the corresponding counted-by object.
+If such counted-by object does not exist, returns a NULL pointer.
+
+This built-in function is only available in C for now.
+
+The argument @var{ptr} must be a pointer to an array.
+The @var{type} of the returned value must be a pointer type pointing to the
+corresponding type of the counted-by object or a VOID pointer type in case
+of a NULL pointer being returned.
+
+For example:
+
+@smallexample
+struct foo1 @{
+  int counter;
+  struct bar1 array[] __attribute__((counted_by (counter)));
+@} *p;
+
+struct foo2 @{
+  int other;
+  struct bar2 array[];
+@} *q;
+@end smallexample
+
+@noindent
+the following call to the built-in
+
+@smallexample
+__builtin_counted_by_ref (p->array)
+@end smallexample
+
+@noindent
+returns:
+
+@smallexample
+&p->counter with type @code{int *}.
+@end smallexample
+
+@noindent
+However, the following call to the built-in
+
+@smallexample
+__builtin_counted_by_ref (q->array)
+@end smallexample
+
+@noindent
+returns a void NULL pointer.
+
+@enddefbuiltin
+
 @defbuiltin{void __builtin_clear_padding (@var{ptr})}
 The built-in function @code{__builtin_clear_padding} function clears
 padding bits inside of the object representation of object pointed by
diff --git a/gcc/testsuite/gcc.dg/builtin-counted-by-ref-1.c b/gcc/testsuite/gcc.dg/builtin-counted-by-ref-1.c
new file mode 100644
index 00000000000..202c8096783
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-counted-by-ref-1.c
@@ -0,0 +1,135 @@ 
+/* Test the code generation for the new __builtin_counted_by_ref.  */
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+#include <stdio.h>
+
+struct annotated {
+  char b;
+  int c[] __attribute ((counted_by (b)));
+} *array_annotated; 
+
+struct flex {
+  short b;
+  int c[];
+} *array_flex;
+
+struct nested_annotated {
+  struct {
+    union {
+      int b;
+      float f;
+    };
+    int n;
+  };
+  char c[] __attribute__ ((counted_by (b)));
+} *array_nested_annotated;
+
+struct nested_flex {
+  struct {
+    union {
+      unsigned int b;
+      float f;
+    };
+    int n;
+  };
+  char c[];
+} *array_nested_flex;
+
+#define MAX(A, B) (A > B) ? (A) : (B)
+#define MY_ALLOC(P, FAM, COUNT) ({ \
+  __auto_type __p = &(P); \
+  __auto_type __c = (COUNT); \
+  size_t __size = MAX (sizeof (*(*__p)),\
+		       __builtin_offsetof (__typeof(*(*__p)),FAM) \
+		       + sizeof (*((*__p)->FAM)) * __c); \
+  if ((*__p = __builtin_malloc(__size))) { \
+    __builtin_memset(*__p, 0, __size); \
+    __auto_type ret = __builtin_counted_by_ref((*__p)->FAM); \
+    *_Generic(ret, void *: &(size_t){0}, default: ret) = __c; \
+    if (sizeof (__builtin_counted_by_ref ((*__p)->FAM)) != sizeof (char *)) \
+      __builtin_abort (); \
+  } \
+})
+
+int count;
+
+int main(int argc, char *argv[])
+{
+  MY_ALLOC(array_annotated, c, 10);
+  if (array_annotated->b != 10)
+    __builtin_abort ();
+  if (__alignof (*__builtin_counted_by_ref (array_annotated->c))
+      != __alignof (array_annotated->b))
+    __builtin_abort ();
+  if (!__builtin_types_compatible_p 
+	(__typeof (*__builtin_counted_by_ref (array_annotated->c)),
+	 __typeof (array_annotated->b)))
+    __builtin_abort ();
+  if (!__builtin_types_compatible_p
+	(__typeof (char[__builtin_counted_by_ref (array_annotated->c)
+			== &array_annotated->b ? 1 : 10]),
+	 __typeof (char[1])))
+    __builtin_abort ();
+
+  MY_ALLOC(array_flex, c, 20);
+  if (array_flex->b == 20)
+    __builtin_abort ();
+  if (!__builtin_types_compatible_p
+	(__typeof (char[__builtin_counted_by_ref (array_flex->c)
+			== &array_flex->b ? 1 : 10]),
+	 __typeof (char[10])))
+    __builtin_abort ();
+
+  MY_ALLOC(array_nested_annotated, c, 30);
+  if (array_nested_annotated->b != 30)
+    __builtin_abort ();
+  if (__alignof (*__builtin_counted_by_ref (array_nested_annotated->c))
+      != __alignof (array_nested_annotated->b))
+    __builtin_abort ();
+  if (!__builtin_types_compatible_p
+	(__typeof (*__builtin_counted_by_ref (array_nested_annotated->c)),
+	 __typeof (array_nested_annotated->b)))
+    __builtin_abort ();
+
+  MY_ALLOC(array_nested_flex, c, 40);
+  if (array_nested_flex->b == 40)
+    __builtin_abort ();
+
+  count = array_annotated->b * 2 + array_nested_annotated->b * 3;
+  struct annotated * annotated_p;
+  struct flex * flex_p;
+  struct nested_annotated * nested_annotated_p;
+  struct nested_flex * nested_flex_p;  
+
+  MY_ALLOC(annotated_p, c, count);
+  if (annotated_p->b != count)
+    __builtin_abort ();
+  if (__alignof (*__builtin_counted_by_ref (annotated_p->c))
+      != __alignof (annotated_p->b))
+    __builtin_abort ();
+  if (!__builtin_types_compatible_p
+	(__typeof (*__builtin_counted_by_ref (annotated_p->c)),
+	 __typeof (annotated_p->b)))
+    __builtin_abort ();
+
+  MY_ALLOC(flex_p, c, count * 2);
+  if (flex_p->b == count * 2)
+    __builtin_abort ();
+
+  MY_ALLOC(nested_annotated_p, c, count * 3);
+  if (nested_annotated_p->b != count * 3)
+    __builtin_abort ();
+  if (__alignof (*__builtin_counted_by_ref (nested_annotated_p->c))
+      != __alignof (nested_annotated_p->b))
+    __builtin_abort ();
+  if (!__builtin_types_compatible_p
+	(__typeof (*__builtin_counted_by_ref (nested_annotated_p->c)),
+	 __typeof (nested_annotated_p->b)))
+    __builtin_abort ();
+
+  MY_ALLOC(nested_flex_p, c, count * 4);
+  if (nested_flex_p->b == count * 4)
+    __builtin_abort ();
+
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/builtin-counted-by-ref.c b/gcc/testsuite/gcc.dg/builtin-counted-by-ref.c
new file mode 100644
index 00000000000..4aa57ffb916
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-counted-by-ref.c
@@ -0,0 +1,61 @@ 
+/* Testing the correct usage of the new __builtin_counted_by_ref.  */
+/* { dg-do compile } */
+/* { dg-options "-O" } */
+
+#include <stdio.h>
+
+struct annotated {
+  size_t b;
+  int other;
+  int c[] __attribute ((counted_by (b)));
+} *array_annotated;
+
+struct flex {
+  size_t b;
+  int other;
+  int c[];
+} *array_flex;
+
+#define MAX(A, B) (A > B) ? (A) : (B)
+#define MY_ALLOC(P, FAM, COUNT) ({ \
+  __auto_type __p = &(P); \
+  __auto_type __c = (COUNT); \
+  size_t __size = MAX (sizeof (*(*__p)),\
+		       __builtin_offsetof (__typeof(*(*__p)),FAM) \
+		       + sizeof (*((*__p)->FAM)) * __c); \
+  if ((*__p = __builtin_malloc(__size))) { \
+    __builtin_memset(*__p, 0, __size); \
+    __auto_type ret = __builtin_counted_by_ref((*__p)->FAM); \
+    *_Generic(ret, void *: &(size_t){0}, default: ret) = __c; \
+    if (sizeof (__builtin_counted_by_ref ((*__p)->FAM)) != sizeof (char *)) \
+      __builtin_abort (); \
+  } \
+})
+
+extern char c_count;
+extern short s_count;
+extern int i_count;
+extern long l_count;
+extern float f_count;
+
+extern int * foo ();
+
+int main(int argc, char *argv[])
+{
+  /* The good usages.  */
+  MY_ALLOC(array_annotated, c, 10);
+  MY_ALLOC(array_flex, c, 20);
+  MY_ALLOC(array_annotated, c, c_count);
+  MY_ALLOC(array_flex, c, i_count);
+  MY_ALLOC(array_annotated, c, l_count);
+  MY_ALLOC(array_flex, c, c_count * 3);
+  MY_ALLOC(array_annotated, c, l_count * i_count);
+
+  /* The bad usages, issue errors.  */
+  __builtin_counted_by_ref (); /* { dg-error "wrong number of arguments to" } */
+  __builtin_counted_by_ref (array_annotated->c, 10); /* { dg-error "wrong number of arguments to" } */
+  __builtin_counted_by_ref (array_annotated->other); /* { dg-error "the argument must be an array" } */
+  __builtin_counted_by_ref (foo());  /* { dg-error "the argument must be an array" } */
+
+  return 0;
+}