[v7] Provide new GCC builtin __builtin_counted_by_ref [PR116016]

Message ID 20240927180550.303571-1-qing.zhao@oracle.com
State New
Headers
Series [v7] 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_check--master-arm success Test passed
linaro-tcwg-bot/tcwg_gcc_build--master-aarch64 success Build passed
linaro-tcwg-bot/tcwg_gcc_check--master-aarch64 success Test passed

Commit Message

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

Compare to the 6th version, the major changes are several style issues
raised by Jakub for the 6th version of the patchs.

The 6th version is at:
https://gcc.gnu.org/pipermail/gcc-patches/2024-September/663992.html

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                             |  79 ++++++++++
 gcc/c/c-tree.h                                |   1 +
 gcc/c/c-typeck.cc                             |  33 +++--
 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, 358 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

Joseph Myers Oct. 11, 2024, 5:16 p.m. UTC | #1
On Fri, 27 Sep 2024, Qing Zhao wrote:

> +	    if (TREE_CODE (TREE_TYPE (ref)) != ARRAY_TYPE)
> +	      {
> +		error_at (loc, "the argument must be an array"
> +			       "%<__builtin_counted_by_ref%>");

This diagnostic is missing a space before %<__builtin_counted_by_ref%>.  
It's also ungrammatical; something better would be

"the argument to %<__builtin_counted_by_ref%> must be an array"

or similar.

> +@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 should be "null pointer" (describing the value returned rather than 
referring to the macro NULL which would be @code{NULL} in the manual).

> +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

"is a pointer type" rather than "must be"; this is describing the type 
returned by a built-in function, not a requirement on a type passed by the 
user.

> +corresponding type of the counted-by object or a VOID pointer type in case
> +of a NULL pointer being returned.

"null pointer", and "void" not "VOID".

> +returns a void NULL pointer.

Likewise, "null pointer", maybe in the form "null pointer to @code{void}".
  

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..c38b51057ee 100644
--- a/gcc/c/c-parser.cc
+++ b/gcc/c/c-parser.cc
@@ -10647,6 +10647,37 @@  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 +11772,54 @@  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 generated 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 = null_pointer_node;
+
+	    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..7f514989261 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -2766,6 +2766,27 @@  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 +2870,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 +2892,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;
+}