On 22/03/2022 16:51, Jakub Jelinek via Gcc wrote:
On Tue, Mar 22, 2022 at 04:28:08PM +0000, Richard Earnshaw wrote:
Unless I've missed something subtle here, the layout of

   struct S { float a; int : 0; float b;};

is going to identical to

   struct T { float a; float b;};

on pretty much every architecture I can think of, so this is purely about
parameter passing rules for the former and whether the two cases above
should behave the same way.

Layout is always done with the int : 0; bitfields in TYPE_FIELDS and
only after that is done C++ FE used to remove them.
So yes, it only can affect the passing of parameters and return values
in registers (or partially in registers, partially in memory).

The AAPCS and AAPCS64 both contain the same statement as part of the
definition of an HFA:

| The test for homogeneity is applied after data layout is
| completed and without regard to access control or other source
| language restrictions.

The access control and source language restrictions was intended to cover
c++-style features such as public/private, so aren't really relevant to this
discussion (though you might plausibly read 'source language restriction' to
cover this).  However, the fact that the test is applied after layout has
been done and because a zero-sized bit-field neither
- adds an accessible member
- changes the layout in any case I can think of that would potentially be an
HFA.
my preliminary conclusion is that for Arm and AArch64 we still have a duck
here (if it walks like one and quacks like one...).

I'm still awaiting final confirmation of this from our internal ABI group,
but I'm pretty confident that this will be our final position.

PS.  It looks like llvm and llvm++ are inconsistent on this one as well.

At least on x86_64 clang and clang++ consistently honored the zero width
bitfields during structure layout and ignored them during parameter passing
decisions (i.e. what the x86_64 psABI chose to clarify).

I was looking at aarch32 (arm).
Compiling

struct S { float a; int : 0; float b; };

struct S
foo (struct S x)
{
  x.b += 1.0f;
  return x;
}

with clang-10 I get

foo:
        .fnstart
        vmov.f32        s0, #1.000000e+00
        str     r1, [r0]
        vmov    s2, r2
        vadd.f32        s0, s2, s0
        vstr    s0, [r0, #4]
        bx      lr

while clang++10 gives

_Z3foo1S:
        .fnstart
        vmov.f32        s2, #1.000000e+00
        vadd.f32        s1, s1, s2
        bx      lr

Both with the options
-S -O2 abi-bf.c -o - --target=arm-none-eabi -march=armv8-a -mfpu=neon -mfloat-abi=hard

So for C it has passed the object in r1/r2 and returned it in memory, while for C++ it has passed it as an HFA.

But for AArch64 it doesn't look right either:

clang-10

foo:                                    // @foo
// %bb.0:
        lsr     x8, x0, #32
        fmov    s0, #1.00000000
        fmov    s1, w8
        fadd    s0, s1, s0
        fmov    w8, s0
        bfi     x0, x8, #32, #32
        ret

clang++-10:


_Z3foo1S:                               // @_Z3foo1S
// %bb.0:
        fmov    s2, #1.00000000
        fadd    s1, s1, s2
        ret

I guess it would be nice to include the testcases we are talking about,
like { float x; int : 0; float y; } and { float x; int : 0; } and
{ int : 0; float x; } into compat.exp testsuite so that we see ABI
differences in compat testing.

        Jakub


Yes, we might also add some specific Arm ABI tests as well.

R.

Reply via email to