Jason Merrill <ja...@redhat.com> writes: > On 11/29/19 5:59 AM, Richard Sandiford wrote: >> Ping >> >> Richard Sandiford <richard.sandif...@arm.com> writes: >>> This is the C++ equivalent of r277950, which prevented the >>> use of the GNU vector extensions with SVE vector types for C. >>> [https://gcc.gnu.org/viewcvs/gcc?view=revision&revision=277950]. >>> I've copied the rationale below for reference. >>> >>> The changes here are very similar to the C ones. Perhaps the only >>> noteworthy thing (that I know of) is that the patch continues to treat >>> !gnu_vector_type_p vector types as literal types/potential constexprs. >>> Disabling the GNU vector extensions shouldn't in itself stop the types >>> from being literal types, since whatever the target provides instead >>> might be constexpr material. >>> >>> Tested on aarch64-linux-gnu and x86_64-linux-gnu. OK to install? >>> >>> Richard >>> >>> ------------------------------------------------------------------------- >>> The AArch64 port defines built-in SVE types at start-up under names >>> like __SVInt8_t. These types are represented in the front end and >>> gimple as normal VECTOR_TYPEs and are code-generated as normal vectors. >>> However, we'd like to stop the frontends from treating them in the >>> same way as GNU-style ("vector_size") vectors, for several reasons: >>> >>> (1) We allowed the GNU vector extensions to be mixed with Advanced SIMD >>> vector types and it ended up causing a lot of confusion on big-endian >>> targets. Although SVE handles big-endian vectors differently from >>> Advanced SIMD, there are still potential surprises; see the block >>> comment near the head of aarch64-sve.md for details. >>> >>> (2) One of the SVE vectors is a packed one-bit-per-element boolean vector. >>> That isn't a combination the GNU vector extensions have supported >>> before. E.g. it means that vectors can no longer decompose to >>> arrays for indexing, and that not all elements are individually >>> addressable. It also makes it less clear which order the initialiser >>> should be in (lsb first, or bitfield ordering?). We could define >>> all that of course, but it seems a bit weird to go to the effort >>> for this case when, given all the other reasons, we don't want the >>> extensions anyway. >>> >>> (3) The GNU vector extensions only provide full-vector operations, >>> which is a very artifical limitation on a predicated architecture >>> like SVE. >>> >>> (4) The set of operations provided by the GNU vector extensions is >>> relatively small, whereas the SVE intrinsics provide many more. >>> >>> (5) It makes it easier to ensure that (with default options) code is >>> portable between compilers without the GNU vector extensions having >>> to become an official part of the SVE intrinsics spec. >>> >>> (6) The length of the SVE types is usually not fixed at compile time, >>> whereas the GNU vector extension is geared around fixed-length >>> vectors. >>> >>> It's possible to specify the length of an SVE vector using the >>> command-line option -msve-vector-bits=N, but in principle it should >>> be possible to have functions compiled for different N in the same >>> translation unit. This isn't supported yet but would be very useful >>> for implementing ifuncs. Once mixing lengths in a translation unit >>> is supported, the SVE types should represent the same type throughout >>> the translation unit, just as GNU vector types do. >>> >>> However, when -msve-vector-bits=N is in effect, we do allow conversions >>> between explicit GNU vector types of N bits and the corresponding SVE >>> types. This doesn't undermine the intent of (5) because in this case >>> the use of GNU vector types is explicit and intentional. It also doesn't >>> undermine the intent of (6) because converting between the types is just >>> a conditionally-supported operation. In other words, the types still >>> represent the same types throughout the translation unit, it's just that >>> conversions between them are valid in cases where a certain precondition >>> is known to hold. It's similar to the way that the SVE vector types are >>> defined throughout the translation unit but can only be used in functions >>> for which SVE is enabled. >>> ------------------------------------------------------------------------- >> >> 2019-11-08 Richard Sandiford <richard.sandif...@arm.com> >> >> gcc/cp/ >> * cp-tree.h (CP_AGGREGATE_TYPE_P): Check for gnu_vector_type_p >> instead of VECTOR_TYPE. >> * call.c (build_conditional_expr_1): Restrict vector handling >> to vectors that satisfy gnu_vector_type_p. Don't treat the >> "then" and "else" types as equivalent if they have the same >> vector shape but differ in whether they're GNU vectors. >> * cvt.c (ocp_convert): Only allow vectors to be converted >> to bool if they satisfy gnu_vector_type_p. >> (build_expr_type_conversion): Only allow conversions from >> vectors if they satisfy gnu_vector_type_p. >> * typeck.c (cp_build_binary_op): Only allow binary operators to be >> applied to vectors if they satisfy gnu_vector_type_p. >> (cp_build_unary_op): Likewise unary operators. >> (build_reinterpret_cast_1): >> >> Index: gcc/cp/call.c >> =================================================================== >> --- gcc/cp/call.c 2019-11-08 08:31:19.000000000 +0000 >> +++ gcc/cp/call.c 2019-11-08 17:43:07.172264122 +0000 >> @@ -5397,6 +5401,7 @@ build_conditional_expr_1 (const op_locat >> value category. */ >> if (((lvalue_p (arg2) && lvalue_p (arg3)) >> || (xvalue_p (arg2) && xvalue_p (arg3))) >> + && gnu_vector_type_p (arg2_type) == gnu_vector_type_p (arg3_type) >> && same_type_p (arg2_type, arg3_type)) > > If the GNU-vector-ness differs, surely same_type_p should be false? > >> { >> result_type = arg2_type; >> @@ -5500,7 +5505,8 @@ build_conditional_expr_1 (const op_locat >> >> --The second and third operands have the same type; the result is of >> that type. */ >> - if (same_type_p (arg2_type, arg3_type)) >> + if (gnu_vector_type_p (arg2_type) == gnu_vector_type_p (arg3_type) >> + && same_type_p (arg2_type, arg3_type)) > > Here too.
Although the types aren't supposed to support GNU-style vector operations directly, they're still supposed to be structurally equivalent to GNU vectors with the same size and element type. E.g.: typedef uint8_t gnu_uint8_t __attribute__ ((vector_size (32))); is structurally equivalent to svuint8_t for -msve-vector-bits=256. svuint8_t and gnu_uint8_t (need to) have separate identities though, since svuint8_t has an ABI-defined mangling that doesn't depend on -msve-vector-bits, whereas GNU vector types have their established target-independent mangling. So the SVE types are built like this: vectype = build_distinct_type_copy (vectype); gcc_assert (vectype == TYPE_MAIN_VARIANT (vectype)); SET_TYPE_STRUCTURAL_EQUALITY (vectype); TYPE_ARTIFICIAL (vectype) = 1; TYPE_INDIVISIBLE_P (vectype) = 1; same_type_p seems to be operating at the structural level and doesn't care whether the types are different enough to need different mangling. I assumed it also shouldn't care whether the types had the same restrictions on the operators they support. If we do make same_type_p return false, we'd need checks elsewhere to keep the test working. E.g.: gnu_uint8_t init_gnu_u3 = { sve_u1 }; gnu_uint8_t init_gnu_u4 = { gnu_u1 }; needs reference_related_p to be true and requires: /* If the constructor already has the array type, it's been through digest_init, so we shouldn't try to do anything more. */ bool digested = same_type_p (atype, TREE_TYPE (init)); (build_vec_init) to be true. ISTR there are similar digest_init checks elsewhere. The pointer checks: svuint8_t *sve_ptr1 = &sve_u1; svuint8_t *sve_ptr2 = &gnu_u1; need comp_ptr_ttypes_real to be true. I can try doing it that way instead if that seems better. The reason for calling out gnu_vector_type_p above is that it isn't clear whether x ? y : z should return typeof(y) or typeof(z) if y and z are structurally equivalent but distinct. I guess that's a problem even before the patch though. E.g., leaving SVE to one side, the AArch64 code: #include <arm_neon.h> typedef uint8_t gnu_uint8_t __attribute__ ((vector_size (16))); void f(gnu_uint8_t x) {} void g(uint8x16_t y) {} correctly mangles f as _Z1fDv16_h and g as _Z1g12__Uint8x16_t. But: void h1(decltype(*(int *)nullptr ? *(gnu_uint8_t *)nullptr : *(uint8x16_t *)nullptr)) {} void h2(decltype(*(int *)nullptr ? *(uint8x16_t *)nullptr : *(gnu_uint8_t *)nullptr)) {} mangles h1 as _Z2h1RDv16_h and h2 as _Z2h2R12__Uint8x16_t. If that's the expected behaviour, maybe we should just embrace it for SVE too. But if it isn't, is there some other check we should be using here instead? Thanks, Richard