-
Notifications
You must be signed in to change notification settings - Fork 12.2k
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
[RISCV] Handle empty structs/unions passing in C++ #97315
Conversation
Why is this in Draft? |
Just waiting for the pre-checkins to complete. I can push it for review if I don't really need to wait for it. Edit: Pushing the patch for review since its late here and I'll need to log off. Also wanted to mention that this change is similar to what LoongArch did in #70320. |
@llvm/pr-subscribers-backend-risc-v @llvm/pr-subscribers-clang-codegen Author: Sudharsan Veeravalli (svs-quic) ChangesAccording to RISC-V integer calling convention empty structs or union arguments or return values are ignored by C compilers which support them as a non-standard extension. This is not the case for C++, which requires them to be sized types. Fixes #97285 Full diff: https://github.com/llvm/llvm-project/pull/97315.diff 2 Files Affected:
diff --git a/clang/lib/CodeGen/Targets/RISCV.cpp b/clang/lib/CodeGen/Targets/RISCV.cpp
index a4c5ec315b8df..f2add9351c03c 100644
--- a/clang/lib/CodeGen/Targets/RISCV.cpp
+++ b/clang/lib/CodeGen/Targets/RISCV.cpp
@@ -361,12 +361,13 @@ ABIArgInfo RISCVABIInfo::classifyArgumentType(QualType Ty, bool IsFixed,
CGCXXABI::RAA_DirectInMemory);
}
- // Ignore empty structs/unions.
- if (isEmptyRecord(getContext(), Ty, true))
- return ABIArgInfo::getIgnore();
-
uint64_t Size = getContext().getTypeSize(Ty);
+ // Ignore empty structs/unions whose size is zero. According to the calling
+ // convention empty structs/unions are required to be sized types in C++.
+ if (isEmptyRecord(getContext(), Ty, true) && Size == 0)
+ return ABIArgInfo::getIgnore();
+
// Pass floating point values via FPRs if possible.
if (IsFixed && Ty->isFloatingType() && !Ty->isComplexType() &&
FLen >= Size && ArgFPRsLeft) {
diff --git a/clang/test/CodeGen/RISCV/abi-empty-structs.c b/clang/test/CodeGen/RISCV/abi-empty-structs.c
index c48a2891627d4..877ed4962d4e8 100644
--- a/clang/test/CodeGen/RISCV/abi-empty-structs.c
+++ b/clang/test/CodeGen/RISCV/abi-empty-structs.c
@@ -167,6 +167,40 @@ struct s9 {
//
void test_s9(struct s9 a) {}
+struct s10 { };
+// CHECK-C-LABEL: define dso_local void @test_s10
+// CHECK-C-SAME: () #[[ATTR0]] {
+// CHECK-C: entry:
+//
+// CHECK32-CXX-LABEL: define dso_local i32 @_Z8test_s103s10
+// CHECK32-CXX-SAME: (i32 [[A_COERCE:%.*]]) #[[ATTR0]] {
+// CHECK32-CXX: entry:
+//
+// CHECK64-CXX-LABEL: define dso_local i64 @_Z8test_s103s10
+// CHECK64-CXX-SAME: (i64 [[A_COERCE:%.*]]) #[[ATTR0]] {
+// CHECK64-CXX: entry:
+//
+struct s10 test_s10(struct s10 a) {
+ return a;
+}
+
+struct s11 { int : 0; };
+// CHECK-C-LABEL: define dso_local void @test_s11
+// CHECK-C-SAME: () #[[ATTR0]] {
+// CHECK-C: entry:
+//
+// CHECK32-CXX-LABEL: define dso_local i32 @_Z8test_s113s11
+// CHECK32-CXX-SAME: (i32 [[A_COERCE:%.*]]) #[[ATTR0]] {
+// CHECK32-CXX: entry:
+//
+// CHECK64-CXX-LABEL: define dso_local i64 @_Z8test_s113s11
+// CHECK64-CXX-SAME: (i64 [[A_COERCE:%.*]]) #[[ATTR0]] {
+// CHECK64-CXX: entry:
+//
+struct s11 test_s11(struct s11 a) {
+ return a;
+}
+
//// NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
// CHECK32-C: {{.*}}
// CHECK64-C: {{.*}}
|
@llvm/pr-subscribers-clang Author: Sudharsan Veeravalli (svs-quic) ChangesAccording to RISC-V integer calling convention empty structs or union arguments or return values are ignored by C compilers which support them as a non-standard extension. This is not the case for C++, which requires them to be sized types. Fixes #97285 Full diff: https://github.com/llvm/llvm-project/pull/97315.diff 2 Files Affected:
diff --git a/clang/lib/CodeGen/Targets/RISCV.cpp b/clang/lib/CodeGen/Targets/RISCV.cpp
index a4c5ec315b8df..f2add9351c03c 100644
--- a/clang/lib/CodeGen/Targets/RISCV.cpp
+++ b/clang/lib/CodeGen/Targets/RISCV.cpp
@@ -361,12 +361,13 @@ ABIArgInfo RISCVABIInfo::classifyArgumentType(QualType Ty, bool IsFixed,
CGCXXABI::RAA_DirectInMemory);
}
- // Ignore empty structs/unions.
- if (isEmptyRecord(getContext(), Ty, true))
- return ABIArgInfo::getIgnore();
-
uint64_t Size = getContext().getTypeSize(Ty);
+ // Ignore empty structs/unions whose size is zero. According to the calling
+ // convention empty structs/unions are required to be sized types in C++.
+ if (isEmptyRecord(getContext(), Ty, true) && Size == 0)
+ return ABIArgInfo::getIgnore();
+
// Pass floating point values via FPRs if possible.
if (IsFixed && Ty->isFloatingType() && !Ty->isComplexType() &&
FLen >= Size && ArgFPRsLeft) {
diff --git a/clang/test/CodeGen/RISCV/abi-empty-structs.c b/clang/test/CodeGen/RISCV/abi-empty-structs.c
index c48a2891627d4..877ed4962d4e8 100644
--- a/clang/test/CodeGen/RISCV/abi-empty-structs.c
+++ b/clang/test/CodeGen/RISCV/abi-empty-structs.c
@@ -167,6 +167,40 @@ struct s9 {
//
void test_s9(struct s9 a) {}
+struct s10 { };
+// CHECK-C-LABEL: define dso_local void @test_s10
+// CHECK-C-SAME: () #[[ATTR0]] {
+// CHECK-C: entry:
+//
+// CHECK32-CXX-LABEL: define dso_local i32 @_Z8test_s103s10
+// CHECK32-CXX-SAME: (i32 [[A_COERCE:%.*]]) #[[ATTR0]] {
+// CHECK32-CXX: entry:
+//
+// CHECK64-CXX-LABEL: define dso_local i64 @_Z8test_s103s10
+// CHECK64-CXX-SAME: (i64 [[A_COERCE:%.*]]) #[[ATTR0]] {
+// CHECK64-CXX: entry:
+//
+struct s10 test_s10(struct s10 a) {
+ return a;
+}
+
+struct s11 { int : 0; };
+// CHECK-C-LABEL: define dso_local void @test_s11
+// CHECK-C-SAME: () #[[ATTR0]] {
+// CHECK-C: entry:
+//
+// CHECK32-CXX-LABEL: define dso_local i32 @_Z8test_s113s11
+// CHECK32-CXX-SAME: (i32 [[A_COERCE:%.*]]) #[[ATTR0]] {
+// CHECK32-CXX: entry:
+//
+// CHECK64-CXX-LABEL: define dso_local i64 @_Z8test_s113s11
+// CHECK64-CXX-SAME: (i64 [[A_COERCE:%.*]]) #[[ATTR0]] {
+// CHECK64-CXX: entry:
+//
+struct s11 test_s11(struct s11 a) {
+ return a;
+}
+
//// NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
// CHECK32-C: {{.*}}
// CHECK64-C: {{.*}}
|
Please add a test for |
Thanks @efriedma-quic. I tried adding a test case for it locally and see that the code produced is different for llvm and gcc: https://godbolt.org/z/vdhGbvj6W Test case:
For llvm this is an empty record of size zero and so it ignores it where as gcc loads 0 into a0. Do we handle this case similar to what gcc does? Or do I add a fixme for now? |
The current spec language is:
So empty structs in C are ignored, empty structs in C++ are treated as non-empty. The "C++ struct containing an empty array" is sort of an edge case, since it's not actually legal C++, but the spec says the distinguishing factor is whether we're in C++ mode. And gcc seems to do that, so let's do the same thing. So instead of checking for Also, please add a release note for this. |
gcc does ignore the empty array case for assigning argument registers in C++. Compare the two cases here https://godbolt.org/z/j4WP89rE8 |
Thanks Eli. I have made these changes. |
Oh, you're right, I didn't actually do any testing myself, I was just assuming @svs-quic did the right test. So I guess the previous size==0 check was actually correct. |
What about the case I have mentioned in https://godbolt.org/z/vdhGbvj6W ? Having a check for both Size==0 and LangOpts.CPlusPlus handles this as well. |
That test doesn't really prove anything useful. The zeroing of a0 doesn't have any ABI significance: "struct s12" doesn't have any data in it, so the caller can't use the value in a0 for anything. At most, it's a hint about how gcc's internal logic works. |
Thanks. I have modified the check and added a relevant test for empty arrays. |
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
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
Could someone please merge this change? I do not have permissions to do so. |
Done |
According to RISC-V integer calling convention empty structs or union arguments or return values are ignored by C compilers which support them as a non-standard extension. This is not the case for C++, which requires them to be sized types. Fixes llvm#97285
According to RISC-V integer calling convention empty structs or union arguments or return values are ignored by C compilers which support them as a non-standard extension. This is not the case for C++, which requires them to be sized types.
Fixes #97285