Last year I posted a series of patches that added the concept of
"sizeless" types to the C and C++ frontends, in order to support
the SVE vector and predicate types:

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

That thread generated a lot of useful discussion and feedback, thanks.
This message is a summary of where things stand, with the later part
of the message dealing specifically with some of the points raised
during that thread and on the WG14 reflector.

Before getting to that though, I just wanted to emphasise that the
sizeless type extension isn't needed to enable frontends to compile
correct SVE ACLE code.  It was/is only about disallowing invalid code.
In fact this was supposed to be one of its main selling points: the
frontends can just pass the types through using their natural (native)
representation, and all the type system needs to do is prevent uses that
would make that representation problematic.  (See "Things in favour of (1)"
in the message above for more details.)

To help make this point (perhaps too forcibly), I committed the target
side of the SVE ACLE support a couple of weeks ago.  This means that the
C++ frontend can (as far as I know) already compile correct ACLE code,
even in the default length-agnostic mode.  The C frontend just needs a
simple one-line change to do the same, since currently it rejects valid
variable-length initialisers.  After that, all we're missing is the
diagnostics for invalid code.

When I started implementing sizeless types in GCC, I thought about
doing it in two ways: directly in the frontends, or via target hooks.
The latter approach was inspired by existing target hooks like
TARGET_INVALID_CONVERSION.

Implementing them directly in the frontend seemed best at first, since
it would also allow us to support user-defined sizeless aggregates.
E.g. we could support a "sizeless struct" that can contain any mixture
of sized and sizeless fields.  (Again, this would be about giving
diagnostics for invalid code.  The frontends could treat valid uses
of sizeless structs in just the same way as normal structs.)

However, we've had significant pushback on the idea of user-defined
sizeless aggregates and (on the clang and LLVM side) variable-length
aggregates in general, so I'm happy to drop sizeless structs for now.
The only sizeless types we need to support are therefore the built-in
SVE ones.

That being the case, using a target hook is less invasive than teaching
the frontends about sizeless types.  It arguably gives better error
messages too, since the target can talk specifically about SVE types.

That's what the new implementation does.  I'll post it to gcc-patches
shortly.

If the use of sizeless types does expand beyond SVE built-in types
in future, the places that call the hook are the places that would
need to deal directly with sizeless types.

As mentioned above, I also wanted to summarise where things stand with
the original sizeless type discussion, even though it's hopefully moot
for now.  The rest of this message therefore summarises the status of
the following topics from the original thread:

- Other proposals on the WG14 reflector
- Using a variable sizeof for C
- Using a variable sizeof for C and a wrapper class for C++

Other proposals on the WG14 reflector
-------------------------------------

When I posted the message last year, Joseph asked how sizeless types
would interact with the bignum type that Martin Uecker had proposed
on the WG14 reflector.  At the time I raised this on the reflector,
it seemed like bignum was still in the relatively early stages and that
the semantics hadn't yet been nailed down, but we talked about three
general possibilities:

  (1) a single fixed-size type that refers to separately-allocated storage.
      Properties:
      - "bignum *" is a valid type
      - "sizeof(bignum)" is constant
      - there are (at least) three approaches to storage management:

        - The separate storage is always on the stack, with stack
          deallocation as garbage collection.  bignum objects are only
          guaranteed to live as long as the function call that creates them
          (or that last assigned to them).

        - The separate storage can be on the stack or heap, with something
          like C++ constructor, destructor, assignment and move semantics
          to manage lifetimes.

        - The types use dynamic garbage-collection (which I don't think was
          specifically discussed on-list at the time).

  (2) a single self-contained variable-size type that can hold any value.
      (i.e. all bignum objects still have the same type).  Properties:
      - "bignum *" is a valid type
      - "sizeof(bignum)" is invalid (because the size depends on the value)
      - the size of a bignum type with given properties can be measured
        by a library function but not by sizeof
      - the size of a bignum object can be measured by a library function
        but not by sizeof (since sizeof shouldn't access the object)
      - it needs an extension like the sizeless type one; it doesn't
        fit the existing VLA model, which creates multiple types

  (3) multiple self-contained variable-length types (e.g. one for each
      bit width), with "bignum" being an auto-like keyword that selects
      the appropriate type for a given value.  Properties:
      - "bignum *" is an invalid type; you would need to pick one of the
        underlying types instead
      - "sizeof(bignum)" is invalid; you would need to pick one of the
        underlying types instead
      - the size of an underlying type with given properties could be
        measured by a library function or by sizeof
      - the size of a bignum object could be measured by a library function
        or by sizeof
      - it fits the existing VLA model

(2) is the most similar to what we want for SVE and should fit the sizeless
type model quite well.  (1) isn't a good fit for SVE because the types
should be self-contained, even at function boundaries.  (3) isn't a good
fit for SVE because there's only ever one valid vector type for a given
element type, and the size is determined by the environment rather than
the program itself.

See also:

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

for a summary of other reflector proposals at around the same time.

Using a variable sizeof for C
-----------------------------

I think a lot of the resistance so far to sizeless types has been
that disallowing "sizeof" just isn't C (or C++).  And since C already
supports variable sizeof, the argument is that we might as well just
make sizeof return the (runtime) size of the SVE vector.

One theoretical objection to that is that for:

    svint8_t *ptr;

the size of the object at *ptr is whatever size svint8_t had when the
object was created.  So if:

    sizeof (*ptr)

returns the current size of svint8_t, the value might not be accurate.
Accessing *ptr would be undefined in those cases (at least if *ptr is
smaller than svint8_t is now).  But to me it seems stranger for sizeof
to be "wrong" for a correctly-typed object than for it not to be defined
at all.  I don't think this situation could happen for VLAs.

Another point is that C ensures sizeof(T) is consistent wherever it
appears.  It does this by evaluating the size of variable-length types
at particular points and then carrying that size forward.  Thus the
source language is effectively dictating what the size is and when it
should be evaluated.  In contrast, the size of SVE types is dictated by
the target.  Fixing the value of sizeof at particular evaluation points
would be an artificial construction.

However, my main objection to using variable sizeof for SVE types
is that we can't do that for C++.  Which brings us to...

Using a variable sizeof for C and a wrapper class for C++
---------------------------------------------------------

Joseph suggested that we could get around the C++ problem by turning
the SVE types into C++ classes that have a fixed size and conceptually
refer to separate storage for the vector contents.  We could then map
these types to the ABI vector types during code generation.  This would
be similar to how decimal floats have different representations in C
and C++ but map to the same underlying ABI type.

I think this would be very difficult to do in practice though.
The C++ classes would end up being similar to std::vector, and would
need to have similar memory management.  We'd then need to ensure that
there are no memory leaks when the C++ class is folded to the ABI type
on returning from a function.  We'd also need to ensure that memory is
successfully reallocated when the caller converts the ABI type back
into the C++ class when receiving the returned value.

There's also the problem that sizeof then becomes different between
C and C++, which isn't true for decimal floats.

This kind of encapsulation also isn't necessary for a correct
implementation.  The C++ frontend already copes with correct SVE code,
letting the built-in vector types pass through without modification.
So I think it would be better to avoid the overhead of a wrapper class
if we can.

Thanks,
Richard

Reply via email to