https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87631

--- Comment #2 from Matthias Kretz <kretz at kde dot org> ---
My (current) use case is structures (nested) of builtin types and vector types.
These structures have a trivial copy constructor.


Generalization
---------------

I believe generalization of this approach should be possible, but I'm not sure
how useful it would be. E.g.

  struct [[gnu::pass_via_register]] A {
    int a;
    std::vector<int> b;
  };

  void f(A);

could call f by "unpacking" A and call f'(int, std::vector<int>). I believe the
effort of supporting types with non-trivial copy ctor is not worth the effort
(such types are typically passed via const-ref anyway).


What I believe is worthwhile
-----------------------------

pass_via_register (max_registers)
  This attribute, attached to a struct type definition, specifies that function
arguments and function return values are passed via up to max_registers
registers, thus potentially using a different calling convention.

  If the number of registers required for passing a value exceeds
max_registers, the default calling convention is used instead. Specifically,
`struct S { int a, b, c; } __attribute__((pass_via_register(1)));` may still
pass via two registers  if it would do so without the attribute.

  If a structure has a single non-static data member of a type declared with
the pass_via_register attribute, the attribute is also applied to the outer
structure:

    struct S { ... } __attribute__((pass_via_register(4)));
    struct inherited { S x; };  // implicit pass_via_register(4)

If a structure has two or more non-static data members the resulting type does
not inherit the pass_via_register attribute.

  You may only specify this attribute on the definition of a struct, not on a
typedef that does not also define the structure.


Example from std::experimental::simd
-------------------------------------

using V = simd<float, __sse_x<2>>;

This essentially asks for { __m128[2] }, similar to `float
attribute((vector_size(32)))` when AVX is not available, except that I'd like
to pass arguments and return values via registers:

V f(V x, V y);

Function f reads x from %xmm0 and %xmm1, y from %xmm2 and %xmm3, and returns
via %xmm0 and %xmm1.

The simd class would be defined like this (note that `simd` itself would not
have the attribute):

template <class T, class Abi> class member_type;
template <int N>
class [[gnu::pass_via_register(4)]] member_type<float, __sse_x<N>> {
  using V [[gnu::vector_size(16)]] = float;
  V data[N];
};

template <class T, class Abi> class simd {
  member_type<T, Abi> data;
};

simd inherits the pass_via_register(4) attribute from its data member because
it has only one data member.


ill-formed
-----------

I'd make the following ill-formed:

struct [[gnu::pass_via_register]] A {
  A(const A &);
};

The non-trivial copy ctor clashes with pass_via_register.


dropping the attribute
-----------------------

Example:

struct X {
  simd<float, __sse_x<2>> a;
  int b;
};

a is pass_via_register, b in principle is pass_via_register (on x86_64), but X
is not (two or more non-static data members). The default calling convention
applies.


implementation strategy
------------------------

I don't see how the frontend could reliably implement the attribute. Does the
frontend know whether a certain type is passed via register (and how many)?
E.g. `void f(int)` passes via the stack on i686. `struct S { int a, b; };`
passes via a single register on x86_64, unpacking `f(S)` to `f(int, int)` would
be suboptimal.

Reply via email to