[C] Add a target hook that allows targets to verify type usage
authorRichard Sandiford <richard.sandiford@arm.com>
Sat, 30 Nov 2019 18:50:06 +0000 (18:50 +0000)
committerRichard Sandiford <rsandifo@gcc.gnu.org>
Sat, 30 Nov 2019 18:50:06 +0000 (18:50 +0000)
This patch adds a new target hook to check whether there are any
target-specific reasons why a type cannot be used in a certain
source-language context.  It works in a similar way to existing
hooks like TARGET_INVALID_CONVERSION and TARGET_INVALID_UNARY_OP.

The reason for adding the hook is to report invalid uses of SVE types.
Throughout a TU, the SVE vector and predicate types represent values
that can be stored in an SVE vector or predicate register.  At certain
points in the TU we might be able to generate code that assumes the
registers have a particular size, but often we can't.  In some cases
we might even make multiple different assumptions in the same TU
(e.g. when implementing an ifunc for multiple vector lengths).

But SVE types themselves are the same type throughout.  The register
size assumptions change how we generate code, but they don't change
the definition of the types.

This means that the types do not have a fixed size at the C level
even when -msve-vector-bits=N is in effect.  It also means that the
size does not work in the same way as for C VLAs, where the abstract
machine evaluates the size at a particular point and then carries that
size forward to later code.

The SVE ACLE deals with this by making it invalid to use C and C++
constructs that depend on the size or layout of SVE types.  The spec
refers to the types as "sizeless" types and defines their semantics as
edits to the standards.  See:

  https://gcc.gnu.org/ml/gcc-patches/2018-10/msg00868.html

for a fuller description and:

  https://gcc.gnu.org/ml/gcc/2019-11/msg00088.html

for a recent update on the status.

However, since all current sizeless types are target-specific built-in
types, there's no real reason for the frontends to handle them directly.
They can just hand off the checks to target code instead.  It's then
possible for the errors to refer to "SVE types" rather than "sizeless
types", which is likely to be more meaningful to users.

There is a slight overlap between the new tests and the ones for
gnu_vector_type_p in r277950, but here the emphasis is on testing
sizelessness.

2019-11-30  Richard Sandiford  <richard.sandiford@arm.com>

gcc/
* target.h (type_context_kind): New enum.
(verify_type_context): Declare.
* target.def (verify_type_context): New target hook.
* doc/tm.texi.in (TARGET_VERIFY_TYPE_CONTEXT): Likewise.
* doc/tm.texi: Regenerate.
* tree.c (verify_type_context): New function.
* config/aarch64/aarch64-protos.h (aarch64_sve::verify_type_context):
Declare.
* config/aarch64/aarch64-sve-builtins.cc (verify_type_context):
New function.
* config/aarch64/aarch64.c (aarch64_verify_type_context): Likewise.
(TARGET_VERIFY_TYPE_CONTEXT): Define.

gcc/c-family/
* c-common.c (pointer_int_sum): Use verify_type_context to check
whether the target allows pointer arithmetic for the types involved.
(c_sizeof_or_alignof_type, c_alignof_expr): Use verify_type_context
to check whether the target allows sizeof and alignof operations
for the types involved.

gcc/c/
* c-decl.c (start_decl): Allow initialization of variables whose
size is a POLY_INT_CST.
(finish_decl): Use verify_type_context to check whether the target
allows variables with a particular type to have static or thread-local
storage duration.  Don't raise a second error if such variables do
not have a constant size.
(grokdeclarator): Use verify_type_context to check whether the
target allows fields or array elements to have a particular type.
* c-typeck.c (pointer_diff): Use verify_type_context to test whether
the target allows pointer difference for the types involved.
(build_unary_op): Likewise for pointer increment and decrement.

gcc/testsuite/
* gcc.target/aarch64/sve/acle/general-c/sizeless-1.c: New test.
* gcc.target/aarch64/sve/acle/general-c/sizeless-2.c: Likewise.

From-SVN: r278877

17 files changed:
gcc/ChangeLog
gcc/c-family/ChangeLog
gcc/c-family/c-common.c
gcc/c/ChangeLog
gcc/c/c-decl.c
gcc/c/c-typeck.c
gcc/config/aarch64/aarch64-protos.h
gcc/config/aarch64/aarch64-sve-builtins.cc
gcc/config/aarch64/aarch64.c
gcc/doc/tm.texi
gcc/doc/tm.texi.in
gcc/target.def
gcc/target.h
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.target/aarch64/sve/acle/general-c/sizeless-1.c [new file with mode: 0644]
gcc/testsuite/gcc.target/aarch64/sve/acle/general-c/sizeless-2.c [new file with mode: 0644]
gcc/tree.c

index 0bfc239..72c8d2e 100644 (file)
@@ -1,3 +1,18 @@
+2019-11-30  Richard Sandiford  <richard.sandiford@arm.com>
+
+       * target.h (type_context_kind): New enum.
+       (verify_type_context): Declare.
+       * target.def (verify_type_context): New target hook.
+       * doc/tm.texi.in (TARGET_VERIFY_TYPE_CONTEXT): Likewise.
+       * doc/tm.texi: Regenerate.
+       * tree.c (verify_type_context): New function.
+       * config/aarch64/aarch64-protos.h (aarch64_sve::verify_type_context):
+       Declare.
+       * config/aarch64/aarch64-sve-builtins.cc (verify_type_context):
+       New function.
+       * config/aarch64/aarch64.c (aarch64_verify_type_context): Likewise.
+       (TARGET_VERIFY_TYPE_CONTEXT): Define.
+
 2019-11-30  Jan Hubicka  <hubicka@ucw.cz>
 
        * cgraph.c (cgraph_node::dump): Dump unit_id and merged_extern_inline.
index a26b1f2..763e5a2 100644 (file)
@@ -1,3 +1,11 @@
+2019-11-30  Richard Sandiford  <richard.sandiford@arm.com>
+
+       * c-common.c (pointer_int_sum): Use verify_type_context to check
+       whether the target allows pointer arithmetic for the types involved.
+       (c_sizeof_or_alignof_type, c_alignof_expr): Use verify_type_context
+       to check whether the target allows sizeof and alignof operations
+       for the types involved.
+
 2019-11-27  Jason Merrill  <jason@redhat.com>
 
        * c-cppbuiltin.c (c_cpp_builtins): Update __cpp_deduction_guides.
index 5b9af1a..2f389d2 100644 (file)
@@ -3146,6 +3146,9 @@ pointer_int_sum (location_t loc, enum tree_code resultcode,
        return error_mark_node;
       size_exp = integer_one_node;
     }
+  else if (!verify_type_context (loc, TCTX_POINTER_ARITH,
+                                TREE_TYPE (result_type)))
+    size_exp = integer_one_node;
   else
     size_exp = size_in_bytes_loc (loc, TREE_TYPE (result_type));
 
@@ -3691,6 +3694,13 @@ c_sizeof_or_alignof_type (location_t loc,
                  "incomplete element type", op_name, type);
       return error_mark_node;
     }
+  else if (!verify_type_context (loc, is_sizeof ? TCTX_SIZEOF : TCTX_ALIGNOF,
+                                type, !complain))
+    {
+      if (!complain)
+       return error_mark_node;
+      value = size_one_node;
+    }
   else
     {
       if (is_sizeof)
@@ -3723,7 +3733,10 @@ c_alignof_expr (location_t loc, tree expr)
 {
   tree t;
 
-  if (VAR_OR_FUNCTION_DECL_P (expr))
+  if (!verify_type_context (loc, TCTX_ALIGNOF, TREE_TYPE (expr)))
+    t = size_one_node;
+
+  else if (VAR_OR_FUNCTION_DECL_P (expr))
     t = size_int (DECL_ALIGN_UNIT (expr));
 
   else if (TREE_CODE (expr) == COMPONENT_REF
index b69e82d..8fee474 100644 (file)
@@ -1,3 +1,17 @@
+2019-11-30  Richard Sandiford  <richard.sandiford@arm.com>
+
+       * c-decl.c (start_decl): Allow initialization of variables whose
+       size is a POLY_INT_CST.
+       (finish_decl): Use verify_type_context to check whether the target
+       allows variables with a particular type to have static or thread-local
+       storage duration.  Don't raise a second error if such variables do
+       not have a constant size.
+       (grokdeclarator): Use verify_type_context to check whether the
+       target allows fields or array elements to have a particular type.
+       * c-typeck.c (pointer_diff): Use verify_type_context to test whether
+       the target allows pointer difference for the types involved.
+       (build_unary_op): Likewise for pointer increment and decrement.
+
 2019-11-29  Joseph Myers  <joseph@codesourcery.com>
 
        * c-parser.c (struct c_parser): Add members raw_tokens and
index 292a4cc..fa7dea5 100644 (file)
@@ -5021,7 +5021,7 @@ start_decl (struct c_declarator *declarator, struct c_declspecs *declspecs,
          {
            /* A complete type is ok if size is fixed.  */
 
-           if (TREE_CODE (TYPE_SIZE (TREE_TYPE (decl))) != INTEGER_CST
+           if (!poly_int_tree_p (TYPE_SIZE (TREE_TYPE (decl)))
                || C_DECL_VARIABLE_SIZE (decl))
              {
                error ("variable-sized object may not be initialized");
@@ -5304,6 +5304,15 @@ finish_decl (tree decl, location_t init_loc, tree init,
 
       complete_flexible_array_elts (DECL_INITIAL (decl));
 
+      if (is_global_var (decl))
+       {
+         type_context_kind context = (DECL_THREAD_LOCAL_P (decl)
+                                      ? TCTX_THREAD_STORAGE
+                                      : TCTX_STATIC_STORAGE);
+         if (!verify_type_context (input_location, context, TREE_TYPE (decl)))
+           TREE_TYPE (decl) = error_mark_node;
+       }
+
       if (DECL_SIZE (decl) == NULL_TREE && TREE_TYPE (decl) != error_mark_node
          && COMPLETE_TYPE_P (TREE_TYPE (decl)))
        layout_decl (decl, 0);
@@ -5333,7 +5342,9 @@ finish_decl (tree decl, location_t init_loc, tree init,
          && TREE_STATIC (decl))
        incomplete_record_decls.safe_push (decl);
 
-      if (is_global_var (decl) && DECL_SIZE (decl) != NULL_TREE)
+      if (is_global_var (decl)
+         && DECL_SIZE (decl) != NULL_TREE
+         && TREE_TYPE (decl) != error_mark_node)
        {
          if (TREE_CODE (DECL_SIZE (decl)) == INTEGER_CST)
            constant_expression_warning (DECL_SIZE (decl));
@@ -5653,6 +5664,10 @@ build_compound_literal (location_t loc, tree type, tree init, bool non_const,
       return error_mark_node;
     }
 
+  if (TREE_STATIC (decl)
+      && !verify_type_context (loc, TCTX_STATIC_STORAGE, type))
+    return error_mark_node;
+
   stmt = build_stmt (DECL_SOURCE_LOCATION (decl), DECL_EXPR, decl);
   complit = build1 (COMPOUND_LITERAL_EXPR, type, stmt);
   TREE_SIDE_EFFECTS (complit) = 1;
@@ -6370,6 +6385,12 @@ grokdeclarator (const struct c_declarator *declarator,
            if (type == error_mark_node)
              continue;
 
+           if (!verify_type_context (loc, TCTX_ARRAY_ELEMENT, type))
+             {
+               type = error_mark_node;
+               continue;
+             }
+
            /* If size was specified, set ITYPE to a range-type for
               that size.  Otherwise, ITYPE remains null.  finish_decl
               may figure it out from an initial value.  */
@@ -7217,6 +7238,10 @@ grokdeclarator (const struct c_declarator *declarator,
            if (orig_qual_indirect == 0)
              orig_qual_type = NULL_TREE;
          }
+       if (type != error_mark_node
+           && !verify_type_context (loc, TCTX_FIELD, type))
+         type = error_mark_node;
+
        type = c_build_qualified_type (type, type_quals, orig_qual_type,
                                       orig_qual_indirect);
        decl = build_decl (declarator->id_loc,
index 5f74a3b..f9ab1e3 100644 (file)
@@ -3892,6 +3892,7 @@ pointer_diff (location_t loc, tree op0, tree op1, tree *instrument_expr)
   addr_space_t as0 = TYPE_ADDR_SPACE (TREE_TYPE (TREE_TYPE (op0)));
   addr_space_t as1 = TYPE_ADDR_SPACE (TREE_TYPE (TREE_TYPE (op1)));
   tree target_type = TREE_TYPE (TREE_TYPE (op0));
+  tree orig_op0 = op0;
   tree orig_op1 = op1;
 
   /* If the operands point into different address spaces, we need to
@@ -3962,6 +3963,10 @@ pointer_diff (location_t loc, tree op0, tree op1, tree *instrument_expr)
   /* This generates an error if op1 is pointer to incomplete type.  */
   if (!COMPLETE_OR_VOID_TYPE_P (TREE_TYPE (TREE_TYPE (orig_op1))))
     error_at (loc, "arithmetic on pointer to an incomplete type");
+  else if (verify_type_context (loc, TCTX_POINTER_ARITH,
+                               TREE_TYPE (TREE_TYPE (orig_op0))))
+    verify_type_context (loc, TCTX_POINTER_ARITH,
+                        TREE_TYPE (TREE_TYPE (orig_op1)));
 
   op1 = c_size_in_bytes (target_type);
 
@@ -4614,6 +4619,9 @@ build_unary_op (location_t location, enum tree_code code, tree xarg,
                  pedwarn (location, OPT_Wpointer_arith,
                           "wrong type argument to decrement");
              }
+           else
+             verify_type_context (location, TCTX_POINTER_ARITH,
+                                  TREE_TYPE (argtype));
 
            inc = c_size_in_bytes (TREE_TYPE (argtype));
            inc = convert_to_ptrofftype_loc (location, inc);
index 5b1fc7b..c16b936 100644 (file)
@@ -715,6 +715,9 @@ namespace aarch64_sve {
                           tree, unsigned int, tree *);
   gimple *gimple_fold_builtin (unsigned int, gimple_stmt_iterator *, gcall *);
   rtx expand_builtin (unsigned int, tree, rtx);
+#ifdef GCC_TARGET_H
+  bool verify_type_context (location_t, type_context_kind, const_tree, bool);
+#endif
 }
 
 extern void aarch64_split_combinev16qi (rtx operands[3]);
index 27736b9..5dd7ccb 100644 (file)
@@ -3296,6 +3296,55 @@ builtin_type_p (const_tree type)
   return svbool_type_p (type) || nvectors_if_data_type (type) > 0;
 }
 
+/* Implement TARGET_VERIFY_TYPE_CONTEXT for SVE types.  */
+bool
+verify_type_context (location_t loc, type_context_kind context,
+                    const_tree type, bool silent_p)
+{
+  if (!builtin_type_p (type))
+    return true;
+
+  switch (context)
+    {
+    case TCTX_SIZEOF:
+    case TCTX_STATIC_STORAGE:
+      if (!silent_p)
+       error_at (loc, "SVE type %qT does not have a fixed size", type);
+      return false;
+
+    case TCTX_ALIGNOF:
+      if (!silent_p)
+       error_at (loc, "SVE type %qT does not have a defined alignment", type);
+      return false;
+
+    case TCTX_THREAD_STORAGE:
+      if (!silent_p)
+       error_at (loc, "variables of type %qT cannot have thread-local"
+                 " storage duration", type);
+      return false;
+
+    case TCTX_POINTER_ARITH:
+      if (!silent_p)
+       error_at (loc, "arithmetic on pointer to SVE type %qT", type);
+      return false;
+
+    case TCTX_FIELD:
+      if (silent_p)
+       ;
+      else if (lang_GNU_CXX ())
+       error_at (loc, "member variables cannot have SVE type %qT", type);
+      else
+       error_at (loc, "fields cannot have SVE type %qT", type);
+      return false;
+
+    case TCTX_ARRAY_ELEMENT:
+      if (!silent_p)
+       error_at (loc, "array elements cannot have SVE type %qT", type);
+      return false;
+    }
+  gcc_unreachable ();
+}
+
 }
 
 using namespace aarch64_sve;
index 94e664a..d0cbe13 100644 (file)
@@ -16200,6 +16200,15 @@ aarch64_mangle_type (const_tree type)
   return NULL;
 }
 
+/* Implement TARGET_VERIFY_TYPE_CONTEXT.  */
+
+static bool
+aarch64_verify_type_context (location_t loc, type_context_kind context,
+                            const_tree type, bool silent_p)
+{
+  return aarch64_sve::verify_type_context (loc, context, type, silent_p);
+}
+
 /* Find the first rtx_insn before insn that will generate an assembly
    instruction.  */
 
@@ -21860,6 +21869,9 @@ aarch64_libgcc_floating_mode_supported_p
 #undef TARGET_MANGLE_TYPE
 #define TARGET_MANGLE_TYPE aarch64_mangle_type
 
+#undef TARGET_VERIFY_TYPE_CONTEXT
+#define TARGET_VERIFY_TYPE_CONTEXT aarch64_verify_type_context
+
 #undef TARGET_MEMORY_MOVE_COST
 #define TARGET_MEMORY_MOVE_COST aarch64_memory_move_cost
 
index 7116450..5b8b68b 100644 (file)
@@ -11976,6 +11976,19 @@ conversion rules.
 This is currently used only by the C and C++ front ends.
 @end deftypefn
 
+@deftypefn {Target Hook} bool TARGET_VERIFY_TYPE_CONTEXT (location_t @var{loc}, type_context_kind @var{context}, const_tree @var{type}, bool @var{silent_p})
+If defined, this hook returns false if there is a target-specific reason
+why type @var{type} cannot be used in the source language context described
+by @var{context}.  When @var{silent_p} is false, the hook also reports an
+error against @var{loc} for invalid uses of @var{type}.
+
+Calls to this hook should be made through the global function
+@code{verify_type_context}, which makes the @var{silent_p} parameter
+default to false and also handles @code{error_mark_node}.
+
+The default implementation always returns true.
+@end deftypefn
+
 @defmac OBJC_JBLEN
 This macro determines the size of the objective C jump buffer for the
 NeXT runtime. By default, OBJC_JBLEN is defined to an innocuous value.
index 4034686..1b061d7 100644 (file)
@@ -8087,6 +8087,8 @@ and scanf formatter settings.
 
 @hook TARGET_CONVERT_TO_TYPE
 
+@hook TARGET_VERIFY_TYPE_CONTEXT
+
 @defmac OBJC_JBLEN
 This macro determines the size of the objective C jump buffer for the
 NeXT runtime. By default, OBJC_JBLEN is defined to an innocuous value.
index cce71cd..e0e8569 100644 (file)
@@ -5287,6 +5287,22 @@ This is currently used only by the C and C++ front ends.",
  hook_tree_tree_tree_null)
 
 DEFHOOK
+(verify_type_context,
+ "If defined, this hook returns false if there is a target-specific reason\n\
+why type @var{type} cannot be used in the source language context described\n\
+by @var{context}.  When @var{silent_p} is false, the hook also reports an\n\
+error against @var{loc} for invalid uses of @var{type}.\n\
+\n\
+Calls to this hook should be made through the global function\n\
+@code{verify_type_context}, which makes the @var{silent_p} parameter\n\
+default to false and also handles @code{error_mark_node}.\n\
+\n\
+The default implementation always returns true.",
+ bool, (location_t loc, type_context_kind context, const_tree type,
+       bool silent_p),
+ NULL)
+
+DEFHOOK
 (can_change_mode_class,
  "This hook returns true if it is possible to bitcast values held in\n\
 registers of class @var{rclass} from mode @var{from} to mode @var{to}\n\
index 2c5b59b..973d743 100644 (file)
@@ -226,6 +226,35 @@ enum omp_device_kind_arch_isa {
        will choose the first mode that works.  */
 const unsigned int VECT_COMPARE_COSTS = 1U << 0;
 
+/* The contexts in which the use of a type T can be checked by
+   TARGET_VERIFY_TYPE_CONTEXT.  */
+enum type_context_kind {
+  /* Directly measuring the size of T.  */
+  TCTX_SIZEOF,
+
+  /* Directly measuring the alignment of T.  */
+  TCTX_ALIGNOF,
+
+  /* Creating objects of type T with static storage duration.  */
+  TCTX_STATIC_STORAGE,
+
+  /* Creating objects of type T with thread-local storage duration.  */
+  TCTX_THREAD_STORAGE,
+
+  /* Creating a field of type T.  */
+  TCTX_FIELD,
+
+  /* Creating an array with elements of type T.  */
+  TCTX_ARRAY_ELEMENT,
+
+  /* Adding to or subtracting from a pointer to T, or computing the
+     difference between two pointers when one of them is a pointer to T.  */
+  TCTX_POINTER_ARITH
+};
+
+extern bool verify_type_context (location_t, type_context_kind, const_tree,
+                                bool = false);
+
 /* The target structure.  This holds all the backend hooks.  */
 #define DEFHOOKPOD(NAME, DOC, TYPE, INIT) TYPE NAME;
 #define DEFHOOK(NAME, DOC, TYPE, PARAMS, INIT) TYPE (* NAME) PARAMS;
index 4595d66..34d5a75 100644 (file)
@@ -1,3 +1,8 @@
+2019-11-30  Richard Sandiford  <richard.sandiford@arm.com>
+
+       * gcc.target/aarch64/sve/acle/general-c/sizeless-1.c: New test.
+       * gcc.target/aarch64/sve/acle/general-c/sizeless-2.c: Likewise.
+
 2019-11-30  Jan Hubicka  <hubicka@ucw.cz>
 
        * g++.dg/lto/inline-crossmodule-1.h: New testcase.
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/acle/general-c/sizeless-1.c b/gcc/testsuite/gcc.target/aarch64/sve/acle/general-c/sizeless-1.c
new file mode 100644 (file)
index 0000000..ec892a3
--- /dev/null
@@ -0,0 +1,217 @@
+/* { dg-options "-std=gnu99" } */
+
+#include <arm_sve.h>
+
+typedef signed char int8x32_t __attribute__((__vector_size__ (32)));
+
+/* Sizeless objects with global scope.  */
+
+svint8_t global_sve_sc; /* { dg-error {SVE type 'svint8_t' does not have a fixed size} } */
+static svint8_t local_sve_sc; /* { dg-error {SVE type 'svint8_t' does not have a fixed size} } */
+extern svint8_t extern_sve_sc; /* { dg-error {SVE type 'svint8_t' does not have a fixed size} } */
+__thread svint8_t tls_sve_sc; /* { dg-error {variables of type 'svint8_t' cannot have thread-local storage duration} } */
+_Atomic svint8_t atomic_sve_sc; /* { dg-error {SVE type 'svint8_t' does not have a fixed size} } */
+
+/* Sizeless arrays.  */
+
+typedef svint8_t array_type[2]; /* { dg-error {array elements cannot have SVE type 'svint8_t'} } */
+extern svint8_t extern_array[]; /* { dg-error {array elements cannot have SVE type 'svint8_t'} } */
+
+/* Sizeless fields.  */
+
+struct struct1 {
+  svint8_t a; /* { dg-error {fields cannot have SVE type 'svint8_t'} } */
+};
+
+union union1 {
+  svint8_t a; /* { dg-error {fields cannot have SVE type 'svint8_t'} } */
+};
+
+/* Pointers to sizeless types.  */
+
+svint8_t *global_sve_sc_ptr;
+svint8_t *invalid_sve_sc_ptr = &(svint8_t) { *global_sve_sc_ptr }; /* { dg-error {initializer element is not constant} } */
+  /* { dg-error {SVE type 'svint8_t' does not have a fixed size} "" { target *-*-* } .-1 } */
+
+/* Sizeless arguments and return values.  */
+
+void ext_consume_sve_sc (svint8_t);
+void ext_consume_varargs (int, ...);
+svint8_t ext_produce_sve_sc ();
+
+/* Main tests for statements and expressions.  */
+
+void
+statements (int n)
+{
+  /* Local declarations.  */
+
+  unsigned char va __attribute__((__vector_size__(2)));
+  svint8_t sve_sc1, sve_sc2;
+  _Atomic svint8_t atomic_sve_sc;
+  int8x32_t gnu_sc1;
+  svint16_t sve_sh1;
+  static svint8_t local_static_sve_sc; /* { dg-error {SVE type 'svint8_t' does not have a fixed size} } */
+
+  /* Layout queries.  */
+
+  sizeof (svint8_t); /* { dg-error {SVE type 'svint8_t' does not have a fixed size} } */
+  sizeof (sve_sc1); /* { dg-error {SVE type 'svint8_t' does not have a fixed size} } */
+  sizeof (ext_produce_sve_sc ()); /* { dg-error {SVE type 'svint8_t' does not have a fixed size} } */
+  _Alignof (svint8_t); /* { dg-error {SVE type 'svint8_t' does not have a defined alignment} } */
+  _Alignof (sve_sc1); /* { dg-error {SVE type 'svint8_t' does not have a defined alignment} } */
+  _Alignof (ext_produce_sve_sc ()); /* { dg-error {SVE type 'svint8_t' does not have a defined alignment} } */
+
+  /* Initialization.  */
+
+  svint8_t init_sve_sc1 = sve_sc1;
+  svint8_t init_sve_sc2 = sve_sh1; /* { dg-error {incompatible types when initializing type 'svint8_t' using type 'svint16_t'} } */
+  svint8_t init_sve_sc3 = {}; /* { dg-error {empty scalar initializer} } */
+
+  int initi_a = sve_sc1; /* { dg-error {incompatible types when initializing type 'int' using type 'svint8_t'} } */
+  int initi_b = { sve_sc1 }; /* { dg-error {incompatible types when initializing type 'int' using type 'svint8_t'} } */
+
+  /* Compound literals.  */
+
+  (svint8_t) {}; /* { dg-error {empty scalar initializer} } */
+  (svint8_t) { sve_sc1 };
+
+  (int) { sve_sc1 }; /* { dg-error {incompatible types when initializing type 'int' using type 'svint8_t'} } */
+
+  /* Arrays.  */
+
+  svint8_t array[2]; /* { dg-error {array elements cannot have SVE type 'svint8_t'} } */
+  svint8_t zero_length_array[0]; /* { dg-error {array elements cannot have SVE type 'svint8_t'} } */
+  svint8_t empty_init_array[] = {}; /* { dg-error {array elements cannot have SVE type 'svint8_t'} } */
+                                   /* { dg-error {empty scalar initializer} "" { target *-*-* } .-1 } */
+  typedef svint8_t vla_type[n]; /* { dg-error {array elements cannot have SVE type 'svint8_t'} } */
+
+  /* Assignment.  */
+
+  n = sve_sc1; /* { dg-error {incompatible types when assigning to type 'int' from type 'svint8_t'} } */
+
+  sve_sc1 = 0; /* { dg-error {incompatible types when assigning to type 'svint8_t' from type 'int'} } */
+  sve_sc1 = sve_sc2;
+  sve_sc1 = sve_sh1; /* { dg-error {incompatible types when assigning to type 'svint8_t' from type 'svint16_t'} } */
+
+  /* Casting.  */
+
+  (void) sve_sc1;
+  (svint8_t) sve_sc1;
+
+  /* Addressing and dereferencing.  */
+
+  svint8_t *sve_sc_ptr = &sve_sc1;
+  int8x32_t *gnu_sc_ptr = &gnu_sc1;
+  sve_sc1 = *sve_sc_ptr;
+
+  /* Pointer assignment.  */
+
+  gnu_sc_ptr = sve_sc_ptr; /* { dg-warning {assignment to [^\n]* from incompatible pointer type} } */
+  sve_sc_ptr = gnu_sc_ptr; /* { dg-warning {assignment to [^\n]* from incompatible pointer type} } */
+
+  /* Pointer arithmetic.  */
+
+  ++sve_sc_ptr; /* { dg-error {arithmetic on pointer to SVE type 'svint8_t'} } */
+  --sve_sc_ptr; /* { dg-error {arithmetic on pointer to SVE type 'svint8_t'} } */
+  sve_sc_ptr++; /* { dg-error {arithmetic on pointer to SVE type 'svint8_t'} } */
+  sve_sc_ptr--; /* { dg-error {arithmetic on pointer to SVE type 'svint8_t'} } */
+  sve_sc_ptr += 0; /* { dg-error {arithmetic on pointer to SVE type 'svint8_t'} } */
+  sve_sc_ptr += 1; /* { dg-error {arithmetic on pointer to SVE type 'svint8_t'} } */
+  sve_sc_ptr -= 0; /* { dg-error {arithmetic on pointer to SVE type 'svint8_t'} } */
+  sve_sc_ptr -= 1; /* { dg-error {arithmetic on pointer to SVE type 'svint8_t'} } */
+  sve_sc_ptr - sve_sc_ptr; /* { dg-error {arithmetic on pointer to SVE type 'svint8_t'} } */
+  gnu_sc_ptr - sve_sc_ptr; /* { dg-error {invalid operands to binary -} } */
+  sve_sc_ptr - gnu_sc_ptr; /* { dg-error {invalid operands to binary -} } */
+  sve_sc1 = sve_sc_ptr[0]; /* { dg-error {arithmetic on pointer to SVE type 'svint8_t'} } */
+  sve_sc1 = sve_sc_ptr[1]; /* { dg-error {arithmetic on pointer to SVE type 'svint8_t'} } */
+
+  /* Pointer comparison.  */
+
+  sve_sc_ptr == &sve_sc1;
+  sve_sc_ptr != &sve_sc1;
+  sve_sc_ptr < &sve_sc1;
+  sve_sc_ptr <= &sve_sc1;
+  sve_sc_ptr > &sve_sc1;
+  sve_sc_ptr >= &sve_sc1;
+  gnu_sc_ptr == sve_sc_ptr; /* { dg-warning {comparison of distinct pointer types lacks a cast} } */
+  gnu_sc_ptr != sve_sc_ptr; /* { dg-warning {comparison of distinct pointer types lacks a cast} } */
+  gnu_sc_ptr < sve_sc_ptr; /* { dg-warning {comparison of distinct pointer types lacks a cast} } */
+  gnu_sc_ptr <= sve_sc_ptr; /* { dg-warning {comparison of distinct pointer types lacks a cast} } */
+  gnu_sc_ptr > sve_sc_ptr; /* { dg-warning {comparison of distinct pointer types lacks a cast} } */
+  gnu_sc_ptr >= sve_sc_ptr; /* { dg-warning {comparison of distinct pointer types lacks a cast} } */
+  sve_sc_ptr == gnu_sc_ptr; /* { dg-warning {comparison of distinct pointer types lacks a cast} } */
+  sve_sc_ptr != gnu_sc_ptr; /* { dg-warning {comparison of distinct pointer types lacks a cast} } */
+  sve_sc_ptr < gnu_sc_ptr; /* { dg-warning {comparison of distinct pointer types lacks a cast} } */
+  sve_sc_ptr <= gnu_sc_ptr; /* { dg-warning {comparison of distinct pointer types lacks a cast} } */
+  sve_sc_ptr > gnu_sc_ptr; /* { dg-warning {comparison of distinct pointer types lacks a cast} } */
+  sve_sc_ptr >= gnu_sc_ptr; /* { dg-warning {comparison of distinct pointer types lacks a cast} } */
+
+  /* Conditional expressions.  */
+
+  0 ? sve_sc1 : sve_sc1;
+  0 ? sve_sc1 : sve_sh1; /* { dg-error {type mismatch in conditional expression} } */
+  0 ? sve_sc1 : 0; /* { dg-error {type mismatch in conditional expression} } */
+  0 ? 0 : sve_sc1; /* { dg-error {type mismatch in conditional expression} } */
+  0 ?: sve_sc1; /* { dg-error {type mismatch in conditional expression} } */
+  0 ? sve_sc_ptr : sve_sc_ptr;
+  0 ? sve_sc_ptr : gnu_sc_ptr; /* { dg-warning {pointer type mismatch in conditional expression} } */
+  0 ? gnu_sc_ptr : sve_sc_ptr; /* { dg-warning {pointer type mismatch in conditional expression} } */
+
+  /* Generic associations.  */
+
+  _Generic (sve_sc1, default: 100);
+  _Generic (1, svint8_t: 10, default: 20);
+
+  /* Function arguments.  */
+
+  ext_consume_sve_sc (sve_sc1);
+  ext_consume_sve_sc (sve_sh1); /* { dg-error {incompatible type for argument 1 of 'ext_consume_sve_sc'} } */
+  ext_consume_varargs (sve_sc1); /* { dg-error {incompatible type for argument 1 of 'ext_consume_varargs'} } */
+  ext_consume_varargs (1, sve_sc1);
+
+  /* Function returns.  */
+
+  ext_produce_sve_sc ();
+  sve_sc1 = ext_produce_sve_sc ();
+  sve_sh1 = ext_produce_sve_sc (); /* { dg-error {incompatible types when assigning to type 'svint16_t' from type 'svint8_t'} } */
+
+  /* Varargs processing.  */
+
+  __builtin_va_list valist;
+  __builtin_va_arg (valist, svint8_t);
+
+  /* Statement expressions.  */
+
+  ({ sve_sc1; });
+  ({ svint8_t another_sve_sc = *sve_sc_ptr; another_sve_sc; });
+}
+
+/* Function parameters in definitions.  */
+
+void
+old_style (input_sve_sc) /* { dg-error {SVE type 'svint8_t' cannot be passed to an unprototyped function} } */
+     svint8_t input_sve_sc;
+{
+  svint8_t sve_sc1 = input_sve_sc;
+}
+
+void
+new_style_param (svint8_t input_sve_sc)
+{
+  svint8_t sve_sc1 = input_sve_sc;
+}
+
+/* Function return values in definitions.  */
+
+svint8_t
+good_return_sve_sc (svint8_t param)
+{
+  return param;
+}
+
+svint8_t
+bad_return_sve_sc (svint16_t param)
+{
+  return param; /* { dg-error {incompatible types when returning type 'svint16_t' but 'svint8_t' was expected} } */
+}
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/acle/general-c/sizeless-2.c b/gcc/testsuite/gcc.target/aarch64/sve/acle/general-c/sizeless-2.c
new file mode 100644 (file)
index 0000000..7174393
--- /dev/null
@@ -0,0 +1,217 @@
+/* { dg-options "-std=gnu99 -msve-vector-bits=256" } */
+
+#include <arm_sve.h>
+
+typedef signed char int8x32_t __attribute__((__vector_size__ (32)));
+
+/* Sizeless objects with global scope.  */
+
+svint8_t global_sve_sc; /* { dg-error {SVE type 'svint8_t' does not have a fixed size} } */
+static svint8_t local_sve_sc; /* { dg-error {SVE type 'svint8_t' does not have a fixed size} } */
+extern svint8_t extern_sve_sc; /* { dg-error {SVE type 'svint8_t' does not have a fixed size} } */
+__thread svint8_t tls_sve_sc; /* { dg-error {variables of type 'svint8_t' cannot have thread-local storage duration} } */
+_Atomic svint8_t atomic_sve_sc; /* { dg-error {SVE type 'svint8_t' does not have a fixed size} } */
+
+/* Sizeless arrays.  */
+
+typedef svint8_t array_type[2]; /* { dg-error {array elements cannot have SVE type 'svint8_t'} } */
+extern svint8_t extern_array[]; /* { dg-error {array elements cannot have SVE type 'svint8_t'} } */
+
+/* Sizeless fields.  */
+
+struct struct1 {
+  svint8_t a; /* { dg-error {fields cannot have SVE type 'svint8_t'} } */
+};
+
+union union1 {
+  svint8_t a; /* { dg-error {fields cannot have SVE type 'svint8_t'} } */
+};
+
+/* Pointers to sizeless types.  */
+
+svint8_t *global_sve_sc_ptr;
+svint8_t *invalid_sve_sc_ptr = &(svint8_t) { *global_sve_sc_ptr }; /* { dg-error {initializer element is not constant} } */
+  /* { dg-error {SVE type 'svint8_t' does not have a fixed size} "" { target *-*-* } .-1 } */
+
+/* Sizeless arguments and return values.  */
+
+void ext_consume_sve_sc (svint8_t);
+void ext_consume_varargs (int, ...);
+svint8_t ext_produce_sve_sc ();
+
+/* Main tests for statements and expressions.  */
+
+void
+statements (int n)
+{
+  /* Local declarations.  */
+
+  unsigned char va __attribute__((__vector_size__(2)));
+  svint8_t sve_sc1, sve_sc2;
+  _Atomic svint8_t atomic_sve_sc;
+  int8x32_t gnu_sc1;
+  svint16_t sve_sh1;
+  static svint8_t local_static_sve_sc; /* { dg-error {SVE type 'svint8_t' does not have a fixed size} } */
+
+  /* Layout queries.  */
+
+  sizeof (svint8_t); /* { dg-error {SVE type 'svint8_t' does not have a fixed size} } */
+  sizeof (sve_sc1); /* { dg-error {SVE type 'svint8_t' does not have a fixed size} } */
+  sizeof (ext_produce_sve_sc ()); /* { dg-error {SVE type 'svint8_t' does not have a fixed size} } */
+  _Alignof (svint8_t); /* { dg-error {SVE type 'svint8_t' does not have a defined alignment} } */
+  _Alignof (sve_sc1); /* { dg-error {SVE type 'svint8_t' does not have a defined alignment} } */
+  _Alignof (ext_produce_sve_sc ()); /* { dg-error {SVE type 'svint8_t' does not have a defined alignment} } */
+
+  /* Initialization.  */
+
+  svint8_t init_sve_sc1 = sve_sc1;
+  svint8_t init_sve_sc2 = sve_sh1; /* { dg-error {incompatible types when initializing type 'svint8_t' using type 'svint16_t'} } */
+  svint8_t init_sve_sc3 = {}; /* { dg-error {empty scalar initializer} } */
+
+  int initi_a = sve_sc1; /* { dg-error {incompatible types when initializing type 'int' using type 'svint8_t'} } */
+  int initi_b = { sve_sc1 }; /* { dg-error {incompatible types when initializing type 'int' using type 'svint8_t'} } */
+
+  /* Compound literals.  */
+
+  (svint8_t) {}; /* { dg-error {empty scalar initializer} } */
+  (svint8_t) { sve_sc1 };
+
+  (int) { sve_sc1 }; /* { dg-error {incompatible types when initializing type 'int' using type 'svint8_t'} } */
+
+  /* Arrays.  */
+
+  svint8_t array[2]; /* { dg-error {array elements cannot have SVE type 'svint8_t'} } */
+  svint8_t zero_length_array[0]; /* { dg-error {array elements cannot have SVE type 'svint8_t'} } */
+  svint8_t empty_init_array[] = {}; /* { dg-error {array elements cannot have SVE type 'svint8_t'} } */
+                                   /* { dg-error {empty scalar initializer} "" { target *-*-* } .-1 } */
+  typedef svint8_t vla_type[n]; /* { dg-error {array elements cannot have SVE type 'svint8_t'} } */
+
+  /* Assignment.  */
+
+  n = sve_sc1; /* { dg-error {incompatible types when assigning to type 'int' from type 'svint8_t'} } */
+
+  sve_sc1 = 0; /* { dg-error {incompatible types when assigning to type 'svint8_t' from type 'int'} } */
+  sve_sc1 = sve_sc2;
+  sve_sc1 = sve_sh1; /* { dg-error {incompatible types when assigning to type 'svint8_t' from type 'svint16_t'} } */
+
+  /* Casting.  */
+
+  (void) sve_sc1;
+  (svint8_t) sve_sc1;
+
+  /* Addressing and dereferencing.  */
+
+  svint8_t *sve_sc_ptr = &sve_sc1;
+  int8x32_t *gnu_sc_ptr = &gnu_sc1;
+  sve_sc1 = *sve_sc_ptr;
+
+  /* Pointer assignment.  */
+
+  gnu_sc_ptr = sve_sc_ptr;
+  sve_sc_ptr = gnu_sc_ptr;
+
+  /* Pointer arithmetic.  */
+
+  ++sve_sc_ptr; /* { dg-error {arithmetic on pointer to SVE type 'svint8_t'} } */
+  --sve_sc_ptr; /* { dg-error {arithmetic on pointer to SVE type 'svint8_t'} } */
+  sve_sc_ptr++; /* { dg-error {arithmetic on pointer to SVE type 'svint8_t'} } */
+  sve_sc_ptr--; /* { dg-error {arithmetic on pointer to SVE type 'svint8_t'} } */
+  sve_sc_ptr += 0; /* { dg-error {arithmetic on pointer to SVE type 'svint8_t'} } */
+  sve_sc_ptr += 1; /* { dg-error {arithmetic on pointer to SVE type 'svint8_t'} } */
+  sve_sc_ptr -= 0; /* { dg-error {arithmetic on pointer to SVE type 'svint8_t'} } */
+  sve_sc_ptr -= 1; /* { dg-error {arithmetic on pointer to SVE type 'svint8_t'} } */
+  sve_sc_ptr - sve_sc_ptr; /* { dg-error {arithmetic on pointer to SVE type 'svint8_t'} } */
+  gnu_sc_ptr - sve_sc_ptr; /* { dg-error {arithmetic on pointer to SVE type 'svint8_t'} } */
+  sve_sc_ptr - gnu_sc_ptr; /* { dg-error {arithmetic on pointer to SVE type 'svint8_t'} } */
+  sve_sc1 = sve_sc_ptr[0]; /* { dg-error {arithmetic on pointer to SVE type 'svint8_t'} } */
+  sve_sc1 = sve_sc_ptr[1]; /* { dg-error {arithmetic on pointer to SVE type 'svint8_t'} } */
+
+  /* Pointer comparison.  */
+
+  sve_sc_ptr == &sve_sc1;
+  sve_sc_ptr != &sve_sc1;
+  sve_sc_ptr < &sve_sc1;
+  sve_sc_ptr <= &sve_sc1;
+  sve_sc_ptr > &sve_sc1;
+  sve_sc_ptr >= &sve_sc1;
+  gnu_sc_ptr == sve_sc_ptr;
+  gnu_sc_ptr != sve_sc_ptr;
+  gnu_sc_ptr < sve_sc_ptr;
+  gnu_sc_ptr <= sve_sc_ptr;
+  gnu_sc_ptr > sve_sc_ptr;
+  gnu_sc_ptr >= sve_sc_ptr;
+  sve_sc_ptr == gnu_sc_ptr;
+  sve_sc_ptr != gnu_sc_ptr;
+  sve_sc_ptr < gnu_sc_ptr;
+  sve_sc_ptr <= gnu_sc_ptr;
+  sve_sc_ptr > gnu_sc_ptr;
+  sve_sc_ptr >= gnu_sc_ptr;
+
+  /* Conditional expressions.  */
+
+  0 ? sve_sc1 : sve_sc1;
+  0 ? sve_sc1 : sve_sh1; /* { dg-error {type mismatch in conditional expression} } */
+  0 ? sve_sc1 : 0; /* { dg-error {type mismatch in conditional expression} } */
+  0 ? 0 : sve_sc1; /* { dg-error {type mismatch in conditional expression} } */
+  0 ?: sve_sc1; /* { dg-error {type mismatch in conditional expression} } */
+  0 ? sve_sc_ptr : sve_sc_ptr;
+  0 ? sve_sc_ptr : gnu_sc_ptr;
+  0 ? gnu_sc_ptr : sve_sc_ptr;
+
+  /* Generic associations.  */
+
+  _Generic (sve_sc1, default: 100);
+  _Generic (1, svint8_t: 10, default: 20);
+
+  /* Function arguments.  */
+
+  ext_consume_sve_sc (sve_sc1);
+  ext_consume_sve_sc (sve_sh1); /* { dg-error {incompatible type for argument 1 of 'ext_consume_sve_sc'} } */
+  ext_consume_varargs (sve_sc1); /* { dg-error {incompatible type for argument 1 of 'ext_consume_varargs'} } */
+  ext_consume_varargs (1, sve_sc1);
+
+  /* Function returns.  */
+
+  ext_produce_sve_sc ();
+  sve_sc1 = ext_produce_sve_sc ();
+  sve_sh1 = ext_produce_sve_sc (); /* { dg-error {incompatible types when assigning to type 'svint16_t' from type 'svint8_t'} } */
+
+  /* Varargs processing.  */
+
+  __builtin_va_list valist;
+  __builtin_va_arg (valist, svint8_t);
+
+  /* Statement expressions.  */
+
+  ({ sve_sc1; });
+  ({ svint8_t another_sve_sc = *sve_sc_ptr; another_sve_sc; });
+}
+
+/* Function parameters in definitions.  */
+
+void
+old_style (input_sve_sc) /* { dg-error {SVE type 'svint8_t' cannot be passed to an unprototyped function} } */
+     svint8_t input_sve_sc;
+{
+  svint8_t sve_sc1 = input_sve_sc;
+}
+
+void
+new_style_param (svint8_t input_sve_sc)
+{
+  svint8_t sve_sc1 = input_sve_sc;
+}
+
+/* Function return values in definitions.  */
+
+svint8_t
+good_return_sve_sc (svint8_t param)
+{
+  return param;
+}
+
+svint8_t
+bad_return_sve_sc (svint16_t param)
+{
+  return param; /* { dg-error {incompatible types when returning type 'svint16_t' but 'svint8_t' was expected} } */
+}
index 789f0a0..1bb3767 100644 (file)
@@ -15123,6 +15123,21 @@ max_object_size (void)
   return TYPE_MAX_VALUE (ptrdiff_type_node);
 }
 
+/* A wrapper around TARGET_VERIFY_TYPE_CONTEXT that makes the silent_p
+   parameter default to false and that weeds out error_mark_node.  */
+
+bool
+verify_type_context (location_t loc, type_context_kind context,
+                    const_tree type, bool silent_p)
+{
+  if (type == error_mark_node)
+    return true;
+
+  gcc_assert (TYPE_P (type));
+  return (!targetm.verify_type_context
+         || targetm.verify_type_context (loc, context, type, silent_p));
+}
+
 #if CHECKING_P
 
 namespace selftest {