-
Notifications
You must be signed in to change notification settings - Fork 15.2k
fix: C++ empty record with align lead to va_list out of sync #72197
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
base: main
Are you sure you want to change the base?
Conversation
@llvm/pr-subscribers-backend-aarch64 @llvm/pr-subscribers-clang-codegen Author: None (hstk30-hw) ChangesAbout #69872 , just for compatible C++ empty record with align UB with gcc https://godbolt.org/z/qsze8fqra Full diff: https://github.com/llvm/llvm-project/pull/72197.diff 2 Files Affected:
diff --git a/clang/lib/CodeGen/ABIInfoImpl.cpp b/clang/lib/CodeGen/ABIInfoImpl.cpp
index 2b20d5a13346d34..bc8ac937be37399 100644
--- a/clang/lib/CodeGen/ABIInfoImpl.cpp
+++ b/clang/lib/CodeGen/ABIInfoImpl.cpp
@@ -296,10 +296,16 @@ bool CodeGen::isEmptyRecord(ASTContext &Context, QualType T, bool AllowArrays,
return false;
// If this is a C++ record, check the bases first.
- if (const CXXRecordDecl *CXXRD = dyn_cast<CXXRecordDecl>(RD))
+ if (const CXXRecordDecl *CXXRD = dyn_cast<CXXRecordDecl>(RD)) {
for (const auto &I : CXXRD->bases())
if (!isEmptyRecord(Context, I.getType(), true, AsIfNoUniqueAddr))
return false;
+ // C++ object size >= 1 byte, empty struct is 1 byte.
+ // FIXME: an alignment on a empty record is a UB, may just warning it,
+ // this code just want to compatible gcc.
+ if (Context.getTypeSize(T) > 8)
+ return false;
+ }
for (const auto *I : RD->fields())
if (!isEmptyField(Context, I, AllowArrays, AsIfNoUniqueAddr))
diff --git a/clang/test/CodeGen/aarch64-args.cpp b/clang/test/CodeGen/aarch64-args.cpp
index fe1298cc683a404..4794f0ae7c903dc 100644
--- a/clang/test/CodeGen/aarch64-args.cpp
+++ b/clang/test/CodeGen/aarch64-args.cpp
@@ -65,3 +65,9 @@ EXTERNC struct SortOfEmpty sort_of_empty_ret(void) {
struct SortOfEmpty e;
return e;
}
+
+// CHECK-GNU-CXX: define{{.*}} i32 @empty_align_arg(i128 %a.coerce, i32 noundef %b)
+struct EmptyAlign { long long int __attribute__((aligned)) : 0; };
+EXTERNC int empty_align_arg(struct EmptyAlign a, int b) {
+ return b;
+}
\ No newline at end of file
|
@AaronBallman @erichkeane Check it plz. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for this! Please be sure to add a release note to clang/docs/ReleaseNotes.rst
as well so users know about the change in behavior.
First off, the change here actually applies to all over-aligned empty structs, not just to those with aligned bit-fields. Maybe we can say that aligned zero-width bit-fields are ill-formed, but I don't think we can say that all aligned empty classes are ill-formed, among other things because I'm pretty sure they're explicitly allowed in C++. So let's just set the aligned bit-field question aside for a moment. Second, I don't understand why the fix is to Third, it does look like we have a real ABI bug here. I haven't looked at other targets, but at least on AArch64, a 16-byte-aligned empty class should definitely be taking up two registers. For some reason, we are generating IR to pass this class as a single byte rather than as a number of bytes equal to its size. Finally, as usual, some platforms may want to opt out of this ABI break; I'll ask around at Apple. |
Oh, the Apple AArch64 calling convention diverges from AAPCS and ignores empty classes as parameters. We appear to consider this an empty class regardless of alignment, which as discussed I believe is correct. So Apple does not want this change on either level: we do not want our ABI to change, and we should not be considering this an empty class. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Marking as changes requested so that this doesn't get committed.
This is the code I debug located. Seem the comment is out of date? This issue 1711cc9 point that when pass the empty class the
It confuse me whether an empty class with alignment (size > 1) is an empty class in C++? |
The proper fix here is probably to just delete the Alignment shouldn't affect whether a class is empty. The issue here is just that according to aarch64 AAPCS rules, there isn't supposed to be a special case for empty classes; they're supposed to be passed exactly the same way as non-empty classes. If there's no alignment involved, that's the same as an i8; if there's alignment, though, that increases the size of the struct, and therefore the calling convention. It looks like whoever wrote it wasn't considering that an empty struct can have size greater than one byte if alignment is involved. The code you noted is supposed to handle two cases, neither of which are relevant to your testcase:
|
That seems like the right place to fix the issue, specifically the place below where it always passes a single The comment isn't wrong, but it could be clearer. Let me suggest this:
That seems like something we should fix, yeah. Generally Edit: sorry about the close/reopen below, I mis-clicked |
84472ee
to
76b8601
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good to see this works without any unexpected side-effects.
Please also add a test for 32-byte alignment (since that generates different code)
76b8601
to
9a00824
Compare
For now, Before, |
9a00824
to
18c2151
Compare
18c2151
to
2c8d9c8
Compare
OK, just delete the
from the empty struct codepath, let it fall through to the main path. |
I guess the
and in gcppabi
I guess I should fix in |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
In terms of va_list etc., we first need to make sure calls are correct (compare against gcc etc.), then we need to make sure va_list is consistent with that.
Is this patch ready to merge? |
Ping |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have no problem with this either, is this ready to merge?
Sorry for delay. I reread the historical comment and will fix it in the next few days :) |
2c8d9c8
to
f7104c0
Compare
303fd45
to
4c356e1
Compare
About #69872 , just for compatible C++ empty record with align UB with gcc
https://godbolt.org/z/qsze8fqra
rel commit 1711cc9