From 65ef05d0b7fb429c5760189e638c441dc3da33f4 Mon Sep 17 00:00:00 2001 From: Richard Sandiford Date: Sat, 30 Nov 2019 18:50:06 +0000 Subject: [PATCH] [C] Add a target hook that allows targets to verify type usage 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 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 --- gcc/ChangeLog | 15 ++ gcc/c-family/ChangeLog | 8 + gcc/c-family/c-common.c | 15 +- gcc/c/ChangeLog | 14 ++ gcc/c/c-decl.c | 29 ++- gcc/c/c-typeck.c | 8 + gcc/config/aarch64/aarch64-protos.h | 3 + gcc/config/aarch64/aarch64-sve-builtins.cc | 49 ++++ gcc/config/aarch64/aarch64.c | 12 + gcc/doc/tm.texi | 13 ++ gcc/doc/tm.texi.in | 2 + gcc/target.def | 16 ++ gcc/target.h | 29 +++ gcc/testsuite/ChangeLog | 5 + .../aarch64/sve/acle/general-c/sizeless-1.c | 217 ++++++++++++++++++ .../aarch64/sve/acle/general-c/sizeless-2.c | 217 ++++++++++++++++++ gcc/tree.c | 15 ++ 17 files changed, 664 insertions(+), 3 deletions(-) create mode 100644 gcc/testsuite/gcc.target/aarch64/sve/acle/general-c/sizeless-1.c create mode 100644 gcc/testsuite/gcc.target/aarch64/sve/acle/general-c/sizeless-2.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 0bfc23996ce..72c8d2e03d4 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,18 @@ +2019-11-30 Richard Sandiford + + * 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 * cgraph.c (cgraph_node::dump): Dump unit_id and merged_extern_inline. diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog index a26b1f28ead..763e5a269f6 100644 --- a/gcc/c-family/ChangeLog +++ b/gcc/c-family/ChangeLog @@ -1,3 +1,11 @@ +2019-11-30 Richard Sandiford + + * 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 * c-cppbuiltin.c (c_cpp_builtins): Update __cpp_deduction_guides. diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c index 5b9af1a34b3..2f389d2895a 100644 --- a/gcc/c-family/c-common.c +++ b/gcc/c-family/c-common.c @@ -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 diff --git a/gcc/c/ChangeLog b/gcc/c/ChangeLog index b69e82d5645..8fee474346f 100644 --- a/gcc/c/ChangeLog +++ b/gcc/c/ChangeLog @@ -1,3 +1,17 @@ +2019-11-30 Richard Sandiford + + * 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 * c-parser.c (struct c_parser): Add members raw_tokens and diff --git a/gcc/c/c-decl.c b/gcc/c/c-decl.c index 292a4cc849b..fa7dea5afb5 100644 --- a/gcc/c/c-decl.c +++ b/gcc/c/c-decl.c @@ -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, diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c index 5f74a3b28d9..f9ab1e38b42 100644 --- a/gcc/c/c-typeck.c +++ b/gcc/c/c-typeck.c @@ -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); diff --git a/gcc/config/aarch64/aarch64-protos.h b/gcc/config/aarch64/aarch64-protos.h index 5b1fc7b7b28..c16b9362ea9 100644 --- a/gcc/config/aarch64/aarch64-protos.h +++ b/gcc/config/aarch64/aarch64-protos.h @@ -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]); diff --git a/gcc/config/aarch64/aarch64-sve-builtins.cc b/gcc/config/aarch64/aarch64-sve-builtins.cc index 27736b99f1b..5dd7ccb74ff 100644 --- a/gcc/config/aarch64/aarch64-sve-builtins.cc +++ b/gcc/config/aarch64/aarch64-sve-builtins.cc @@ -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; diff --git a/gcc/config/aarch64/aarch64.c b/gcc/config/aarch64/aarch64.c index 94e664af52f..d0cbe13273f 100644 --- a/gcc/config/aarch64/aarch64.c +++ b/gcc/config/aarch64/aarch64.c @@ -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 diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi index 7116450f532..5b8b68bd710 100644 --- a/gcc/doc/tm.texi +++ b/gcc/doc/tm.texi @@ -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. diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in index 403468682bf..1b061d70127 100644 --- a/gcc/doc/tm.texi.in +++ b/gcc/doc/tm.texi.in @@ -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. diff --git a/gcc/target.def b/gcc/target.def index cce71cd5b20..e0e856979a9 100644 --- a/gcc/target.def +++ b/gcc/target.def @@ -5286,6 +5286,22 @@ This is currently used only by the C and C++ front ends.", tree, (tree type, tree expr), 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\ diff --git a/gcc/target.h b/gcc/target.h index 2c5b59be851..973d743a371 100644 --- a/gcc/target.h +++ b/gcc/target.h @@ -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; diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 4595d664efa..34d5a75a200 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2019-11-30 Richard Sandiford + + * 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 * 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 index 00000000000..ec892a3fc83 --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/sve/acle/general-c/sizeless-1.c @@ -0,0 +1,217 @@ +/* { dg-options "-std=gnu99" } */ + +#include + +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 index 00000000000..71743930098 --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/sve/acle/general-c/sizeless-2.c @@ -0,0 +1,217 @@ +/* { dg-options "-std=gnu99 -msve-vector-bits=256" } */ + +#include + +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} } */ +} diff --git a/gcc/tree.c b/gcc/tree.c index 789f0a00f41..1bb37679f54 100644 --- a/gcc/tree.c +++ b/gcc/tree.c @@ -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 { -- 2.30.2