-
Notifications
You must be signed in to change notification settings - Fork 13.6k
Clang generates incorrect code for unions of types with padding #76017
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Fix idea 1: lower C/C++ unions into byte arrays of size of the maximum union member. Fix idea 2: lower C/C++ unions into intermediate I can try to implement fix if there are no objections. ? |
I think part of the issue here is a SROA bug (#64081), but part of it is that the ABI is just crazy. Just comparing to what GCC does, it looks like the result is returned in xmm0 and xmm1. For s1 a pair of floats is returned in xmm0. For s2 a float is returned in xmm0 and a double in xmm1. I don't think we even have the capability of representing this ABI in LLVM IR. cc @efriedma-quic |
If the clang ABI code makes the function's return type Briefly glancing at the code, it looks like the bug is in X86_64ABIInfo::GetSSETypeAtOffset. |
Oh yeah, that makes sense. I didn't consider that we can just bitcast the float pair to a double. |
@llvm/issue-subscribers-clang-codegen Author: Afanasyev Ivan (ivafanas)
Reproducer:
#include <cstdio>
#include <cstdint>
struct my_struct_1 {
float a;
float b;
};
struct my_struct_2 {
float x;
double y;
};
union my_union {
my_struct_1 s1;
my_struct_2 s2;
};
my_union my_func() {
my_union u;
u.s1 = my_struct_1{100.f, 200.f};
return u;
}
int main() {
my_union u = my_func();
if (u.s1.a != 100.f)
std::puts("a ooops");
if (u.s1.b != 200.f)
std::puts("b ooops");
return 0;
} Please, note that Bug is reproduced on X86 on clang 16 release and on the latest main branch (clang++-17 is not tested, but I assume its behaviour is the same): # clang 16 release
clang++-16 -O1 example.cpp && ./a.out
b ooops
# clang main 5caae72d1a4f
clang++ -O1 example.cpp && ./a.out
b ooops The problem is that C++ unions are represented in IR level as a struct of member with the largets size:
Information about
As you can see,
Possibly related discussion: Possibly related issue: |
Reproducer:
Please, note that
my_struct_1
has fieldb
which fits into padding ofmy_struct_2
andsizeof(my_struct_2) > sizeof(my_struct_1)
.Bug is reproduced on X86 on clang 16 release and on the latest main branch (clang++-17 is not tested, but I assume its behaviour is the same):
The problem is that C++ unions are represented in IR level as a struct of member with the largets size:
Information about
my_struct_1
layout is lost when unionllvm::StructType
is constructed. SROA pass operates onmy_struct_2
layout only.my_func
IR before SROA:my_func
IR after SROA:As you can see,
200.f
value is lost andundef
value insertion happens here.On the other side, IR before SROA also looks suspicious, because 32bits of double value are undef -> whole double is undef.
gcc
works well on this example.Possibly related discussion:
https://discourse.llvm.org/t/struct-copy/11330
Possibly related issue:
#53710
The text was updated successfully, but these errors were encountered: