[Clang] `APPLE` specific ABI Issue with Reuse of Tail Padding
- Dominant language
- LLVM
- Stars
- 40.5k
- Forks
- 18.7k
- PR merge metrics
- PR metrics pending
Description
This may act as a follow-up on [Bug 16537 - ABI issue with the C++11 definition of POD and tail padding](https://bugs.llvm.org/show_bug.cgi?id=16537), but it is currently reproducible on `homebrew/clang` (`apple-clang` should also have the same issue). Below I have attached the test case:
```cpp
// Compiled with clang++ --std=c++23
#include
typedef unsigned long long int64;
typedef unsigned char int8;
// non-POD in 03
struct pod_in_11_only {
private:
int64 x;
};
// non-POD in 03
struct tail_padded_pod_in_11_only {
pod_in_11_only pod11;
int8 tail_padding;
};
struct might_use_tail_padding : public tail_padded_pod_in_11_only {
int8 may_go_into_tail_padding;
};
int main() {
printf("sizeof(might_use_tail_padding) == %lu\n", sizeof(might_use_tail_padding));
printf("%s\n", sizeof(might_use_tail_padding) == 16 ? "Using tail padding"
: "NOT using tail padding");
return 0;
}
```
This case as of now, on **Homebrew clang version 21.1.8** still does not reuse the tail padding, which is at odds with the [Itanium C++ ABI](https://itanium-cxx-abi.github.io/cxx-abi/abi.html#POD).
> There have been multiple published revisions to the ISO C++ standard, and each one has included a different definition of POD. To ensure interoperation of code compiled according to different revisions of the standard, it is necessary to settle on a single definition for a platform. A platform vendor may choose to follow a different revision of the standard, but by default, the definition of POD under this ABI is the definition from the 2003 revision (TC1).
The difference seems to stem from the difference of POD definition used when determining the layout. After tweaking the test case so that it is no longer a POD by the C++11 definition, this reuses the tail-padding as expected.
```cpp
// Compiled with clang++ --std=c++23
#include
typedef unsigned long long int64;
typedef unsigned char int8;
// non-POD in 11
struct non_pod_in_11 {
~non_pod_in_11() {}
private:
int64 x;
};
// non-POD in 11
struct tail_padded_non_pod_in_11 {
non_pod_in_11 pod11;
int8 tail_padding;
};
struct might_use_tail_padding : public tail_padded_non_pod_in_11 {
int8 may_go_into_tail_padding;
};
int main() {
printf("sizeof(might_use_tail_padding) == %lu\n", sizeof(might_use_tail_padding));
printf("%s\n", sizeof(might_use_tail_padding) == 16 ? "Using tail padding"
: "NOT using tail padding");
return 0;
}
```
I suspect this is also why the behaviour of the following code differs (does not reuse tail padding on `homebrew/clang`):
```cpp
// Compiled with clang++ --std=c++23
struct AllowOverlapMixin {};
struct Foo: AllowOverlapMixin {
long long foo_val;
bool foo_val2;
};
template
struct MaybeDeleted {
[[no_unique_address]] T val;
bool deleted;
};
static_assert(sizeof(Foo) == 16);
static_assert(alignof(Foo) == 8);
static_assert(sizeof(MaybeDeleted) == 16); // 24 on homebrew/clang, does not compile
```
Here are the details of the running environment if needed.
```
$ clang++ --version
Homebrew clang version 21.1.8
Target: arm64-apple-darwin22.4.0
Thread model: posix
InstalledDir: /opt/homebrew/Cellar/llvm/21.1.8/bin
Configuration file: /opt/homebrew/etc/clang/arm64-apple-darwin22.cfg
$ sysctl -n machdep.cpu.brand_string
Apple M1 Pro
```
Contributor guide
Research direction
Start by reproducing the attached C++ cases with clang++ --std=c++23 on the reported Apple target, including the sizeof and static_assert checks. Read the Itanium C++ ABI tail-padding and POD rules alongside Clang's record-layout and ABI handling. Done means the Apple-specific layouts are resolved consistently with the intended ABI behavior, with regression coverage for the reproductions.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100