Re: cfe-dev Digest, Vol 161, Issue 22

classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

Re: cfe-dev Digest, Vol 161, Issue 22

David Blaikie via cfe-dev
Is it also C++ which will trigger the special case in GetX86_64ByValArgumentPair?

Here the comments state:

// In order to correctly satisfy the ABI, we need to the high part to start
// at offset 8.  If the high and low parts we inferred are both 4-byte types
// (e.g. i32 and i32) then the resultant struct type ({i32,i32}) won't have
// the second element at offset 8.  Check for this:

Here I am suspecting that struct Foo { int a; struct { } b; int c; } will create this situation in C++ but not in C. Is that a correct assumption?

Christoffer
AEGIK / www.aegik.se

Date: Fri, 13 Nov 2020 09:44:54 +0000
From: David Chisnall via cfe-dev <[hidden email]>
To: [hidden email]
Subject: Re: [cfe-dev] The x64 ABI lowering, direct pass of upper
8-bytes, can it happen?
Message-ID: <[hidden email]>
Content-Type: text/plain; charset=utf-8; format=flowed

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


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