- Notifications
You must be signed in to change notification settings - Fork 15.3k
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
fix: C++ empty record with align lead to va_list out of sync #72197
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. |
AaronBallman left a comment
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. |
rjmccall left a comment
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
efriedma-quic left a comment
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 |
efriedma-quic left a comment
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 |
erichkeane left a comment
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 :) |
303fd45 to 4c356e1 Compare 1e45d11 to e683539 Compare | I think it's ready to merge. Please review it again all. |
| ping... |
| Ping |
AaronBallman left a comment
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.
The changes look reasonable to me, but I'd appreciate if @rjmccall or @efriedma-quic could make the final sign-off.
efriedma-quic left a comment
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'm still fine with this. And I think @rjmccall's concerns were addressed by the latest version.
clang/docs/ReleaseNotes.rst Outdated
| - Let C++ empty record fall through to the standard argument-handling path instead of | ||
| always pass a single ``i8`` according to aarch64 AAPCS rules. |
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.
Maybe tweak the release note a little; the reference to i8 doesn't really make sense from the perspective of a clang user.
| - Let C++ empty record fall through to the standard argument-handling path instead of | |
| always pass a single ``i8`` according to aarch64 AAPCS rules. | |
| - Fix AArch64 argument passing for C++ empty classes with large explicitly specified | |
| alignment. |
e5d24bc to d902a4b Compare | I think this patch had meet @rjmccall 's suggestion. Let us merge it :) |
) Fix AArch64 argument passing for C++ empty classes with large explicitly specified alignment reproducer: https://godbolt.org/z/qsze8fqra rel issue: llvm#69872 rel commit: llvm@1711cc9
About #69872 , just for compatible C++ empty record with align UB with gcc
https://godbolt.org/z/qsze8fqra
rel commit 1711cc9