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));
      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


Reply via email to