The x64 ABI lowering, direct pass of upper 8-bytes, can it happen?

classic Classic list List threaded Threaded
4 messages Options
Reply | Threaded
Open this post in threaded view
|

The x64 ABI lowering, direct pass of upper 8-bytes, can it happen?

David Blaikie via cfe-dev
I apologize for spamming with so many questions, but this really confusing me.

In X86_64ABIInfo::classifyArgumentType we have this code:

>   case Integer:
>     ++neededInt;
>     // Pick an 8-byte type based on the preferred type.
>     HighPart = GetINTEGERTypeAtOffset(CGT.ConvertType(Ty), 8, Ty, 8);
>
>     if (Lo == NoClass)  // Pass HighPart at offset 8 in memory.
>       return ABIArgInfo::getDirect(HighPart, 8);
>     break;

Zooming in on the ”Lo == NoClass”. As far as I can tell this can only happen in the case where a struct has padding in front, and the first real integer parameter is actually 64 bits in.

I admit I have no idea how to construct such a type in C to trigger this. Since Clang has a non-trivial amount of code to handle this I am wondering what I’m missing. (I’ve looked through the x86 test cases as well but was unable to find a test case for this particular situation. I might have overlooked something though)


/Christoffer
_______________________________________________
cfe-dev mailing list
[hidden email]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev
Reply | Threaded
Open this post in threaded view
|

Re: The x64 ABI lowering, direct pass of upper 8-bytes, can it happen?

David Blaikie via cfe-dev
Adding an assert(0) inside that if fires on this test case from clang/test/CodeGenCXX/x86_64-arguments.cpp

// PR5831
// CHECK-LABEL: define void @_Z2f34s3_1(i64 %x.coerce)
struct s3_0 {};
struct s3_1 { struct s3_0 a; long b; };
void f3(struct s3_1 x) {}

~Craig


On Thu, Nov 12, 2020 at 1:06 PM Christoffer Lernö via cfe-dev <[hidden email]> wrote:
I apologize for spamming with so many questions, but this really confusing me.

In X86_64ABIInfo::classifyArgumentType we have this code:

>   case Integer:
>     ++neededInt;
>     // Pick an 8-byte type based on the preferred type.
>     HighPart = GetINTEGERTypeAtOffset(CGT.ConvertType(Ty), 8, Ty, 8);
>
>     if (Lo == NoClass)  // Pass HighPart at offset 8 in memory.
>       return ABIArgInfo::getDirect(HighPart, 8);
>     break;

Zooming in on the ”Lo == NoClass”. As far as I can tell this can only happen in the case where a struct has padding in front, and the first real integer parameter is actually 64 bits in.

I admit I have no idea how to construct such a type in C to trigger this. Since Clang has a non-trivial amount of code to handle this I am wondering what I’m missing. (I’ve looked through the x86 test cases as well but was unable to find a test case for this particular situation. I might have overlooked something though)


/Christoffer
_______________________________________________
cfe-dev mailing list
[hidden email]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev

_______________________________________________
cfe-dev mailing list
[hidden email]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev
Reply | Threaded
Open this post in threaded view
|

Re: The x64 ABI lowering, direct pass of upper 8-bytes, can it happen?

David Blaikie via cfe-dev
To give a little bit more context:

C and C++ require that every field in a structure has a unique pointer
value (unless explicitly annotated in C++20) and C requires that a
pointer to the first field of an object is equivalent to a pointer to
the object.

This combination of constraints first means that `s3_0` in this example
will report a size of 1, but that byte is purely padding.  When we lay
out `s3_1`, we have a first field that is one byte (purely of padding)
followed by 7 bytes of padding to correctly align `b`.  This adds up to
8 bytes of padding.

No one would write that by hand deliberately, but you can encounter
things like this when you use std::conditional_t in a C++ class to do
something like:

```c++
struct Placeholder {};

template<typename T = void>
struct SomeClass
{
        std::conditional_t<std::is_same_v<T, void>, T, PlaceHolder> extra_data;
        long unconditional_field;
        ...
};
```

This pattern is quite useful for classes where you want to provide an
extension point for carrying some extra state but still support the case
where the instantiating code doesn't want to provide any.  Prior to
C++20 there was no standard way of ensuring that the placeholder didn't
take up any space.  If you happened to pick the first field of the
object for this field then you could end up with something equivalent to
Craig's example, where you have 8 bytes of padding at the start of
`SomeClass` before `unconditional_field`.

In C++20, you can write this:

```c++
struct Placeholder {};

template<typename T = void>
struct SomeClass
{
        [[no_unique_address]]
        std::conditional_t<std::is_same_v<T, void>, T, PlaceHolder> extra_data;
        long unconditional_field;
        ...
};
```

Now, if `extra_data` gets the type `PlaceHolder`, `(char*)&extra_data ==
(char*)&unconditional_Field`.  That's typically what you want, because
any accesses to `extra_data` will be guarded by a `constexpr if` that
checks that `T` is not `void` and so the field will not actually be
referenced at all in instantiations where it is not used to carry extra
state.


David


On 12/11/2020 22:13, Craig Topper via cfe-dev wrote:

> Adding an assert(0) inside that if fires on this test case from
> clang/test/CodeGenCXX/x86_64-arguments.cpp
>
> // PR5831
> // CHECK-LABEL: define void @_Z2f34s3_1(i64 %x.coerce)
> struct s3_0 {};
> struct s3_1 { struct s3_0 a; long b; };
> void f3(struct s3_1 x) {}
>
> ~Craig
>
>
> On Thu, Nov 12, 2020 at 1:06 PM Christoffer Lernö via cfe-dev
> <[hidden email] <mailto:[hidden email]>> wrote:
>
>     I apologize for spamming with so many questions, but this really
>     confusing me.
>
>     In X86_64ABIInfo::classifyArgumentType we have this code:
>
>      >   case Integer:
>      >     ++neededInt;
>      >     // Pick an 8-byte type based on the preferred type.
>      >     HighPart = GetINTEGERTypeAtOffset(CGT.ConvertType(Ty), 8, Ty, 8);
>      >
>      >     if (Lo == NoClass)  // Pass HighPart at offset 8 in memory.
>      >       return ABIArgInfo::getDirect(HighPart, 8);
>      >     break;
>
>     Zooming in on the ”Lo == NoClass”. As far as I can tell this can
>     only happen in the case where a struct has padding in front, and the
>     first real integer parameter is actually 64 bits in.
>
>     I admit I have no idea how to construct such a type in C to trigger
>     this. Since Clang has a non-trivial amount of code to handle this I
>     am wondering what I’m missing. (I’ve looked through the x86 test
>     cases as well but was unable to find a test case for this particular
>     situation. I might have overlooked something though)
>
>
>     /Christoffer
>     _______________________________________________
>     cfe-dev mailing list
>     [hidden email] <mailto:[hidden email]>
>     https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev
>     <https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev>
>
>
> _______________________________________________
> cfe-dev mailing list
> [hidden email]
> https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev
>
_______________________________________________
cfe-dev mailing list
[hidden email]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev
Reply | Threaded
Open this post in threaded view
|

Re: The x64 ABI lowering, direct pass of upper 8-bytes, can it happen?

David Blaikie via cfe-dev
In reply to this post by David Blaikie via cfe-dev
That’s interesting (and slightly confusing) 

I could only see this happening if Layout.getFieldOffset(idx) would return a value greater than zero for the b member. (TargetInfo.cpp, line 3071)

However, that seemed unlikely… What I forgot to do though was to take C++ into account. It appears that for C++ the offset is indeed 8 for b (whereas for C it is 0). So then this behaviour is strictly something that can occur in C++ code.

This could possibly be added to the code as a comment to clarify it to readers.

Christoffer
AEGIK / www.aegik.se

On 12 Nov 2020, at 23:13, Craig Topper <[hidden email]> wrote:

Adding an assert(0) inside that if fires on this test case from clang/test/CodeGenCXX/x86_64-arguments.cpp

// PR5831
// CHECK-LABEL: define void @_Z2f34s3_1(i64 %x.coerce)
struct s3_0 {};
struct s3_1 { struct s3_0 a; long b; };
void f3(struct s3_1 x) {}

~Craig


On Thu, Nov 12, 2020 at 1:06 PM Christoffer Lernö via cfe-dev <[hidden email]> wrote:
I apologize for spamming with so many questions, but this really confusing me.

In X86_64ABIInfo::classifyArgumentType we have this code:

>   case Integer:
>     ++neededInt;
>     // Pick an 8-byte type based on the preferred type.
>     HighPart = GetINTEGERTypeAtOffset(CGT.ConvertType(Ty), 8, Ty, 8);

>     if (Lo == NoClass)  // Pass HighPart at offset 8 in memory.
>       return ABIArgInfo::getDirect(HighPart, 8);
>     break;

Zooming in on the ”Lo == NoClass”. As far as I can tell this can only happen in the case where a struct has padding in front, and the first real integer parameter is actually 64 bits in.

I admit I have no idea how to construct such a type in C to trigger this. Since Clang has a non-trivial amount of code to handle this I am wondering what I’m missing. (I’ve looked through the x86 test cases as well but was unable to find a test case for this particular situation. I might have overlooked something though)


/Christoffer
_______________________________________________
cfe-dev mailing list
[hidden email]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev


_______________________________________________
cfe-dev mailing list
[hidden email]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev