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