-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[X86_64] fix empty structure vaarg in c++ #77907
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
Conversation
@llvm/pr-subscribers-clang-codegen @llvm/pr-subscribers-backend-x86 Author: None (hstk30-hw) ChangesSizeInBytes of empty structure is 0 in C, while 1 in C++. And empty structure argument of the function is ignored in X86_64 backend.As a result, the value of variable arguments in C++ is incorrect. fix #77036 Full diff: https://github.com/llvm/llvm-project/pull/77907.diff 2 Files Affected:
diff --git a/clang/lib/CodeGen/Targets/X86.cpp b/clang/lib/CodeGen/Targets/X86.cpp
index d053f41ab168f5..c2c11280838b12 100644
--- a/clang/lib/CodeGen/Targets/X86.cpp
+++ b/clang/lib/CodeGen/Targets/X86.cpp
@@ -2989,6 +2989,11 @@ static Address EmitX86_64VAArgFromMemory(CodeGenFunction &CGF,
// an 8 byte boundary.
uint64_t SizeInBytes = (CGF.getContext().getTypeSize(Ty) + 7) / 8;
+
+ if (isEmptyRecord(CGF.getContext(), Ty, true)) {
+ SizeInBytes = 0;
+ }
+
llvm::Value *Offset =
llvm::ConstantInt::get(CGF.Int32Ty, (SizeInBytes + 7) & ~7);
overflow_arg_area = CGF.Builder.CreateGEP(CGF.Int8Ty, overflow_arg_area,
diff --git a/clang/test/CodeGen/X86/x86_64-vaarg.c b/clang/test/CodeGen/X86/x86_64-vaarg.c
new file mode 100644
index 00000000000000..ae201a2d3f3017
--- /dev/null
+++ b/clang/test/CodeGen/X86/x86_64-vaarg.c
@@ -0,0 +1,15 @@
+// RUN: %clang -xc++ -target x86_64-linux-gnu -emit-llvm -S -o - %s | FileCheck %s
+
+struct Empty {};
+
+struct Empty emptyvar;
+
+void take_args(int a, ...) {
+ // CHECK: %overflow_arg_area = load ptr, ptr %overflow_arg_area_p, align 8
+ // CHECK-NEXT: %overflow_arg_area.next = getelementptr i8, ptr %overflow_arg_area, i32 0
+ // CHECK-NEXT: store ptr %overflow_arg_area.next, ptr %overflow_arg_area_p, align 8
+ __builtin_va_list l;
+ __builtin_va_start(l, a);
+ emptyvar = __builtin_va_arg(l, struct Empty);
+ __builtin_va_end(l);
+}
|
@llvm/pr-subscribers-clang Author: None (hstk30-hw) ChangesSizeInBytes of empty structure is 0 in C, while 1 in C++. And empty structure argument of the function is ignored in X86_64 backend.As a result, the value of variable arguments in C++ is incorrect. fix #77036 Full diff: https://github.com/llvm/llvm-project/pull/77907.diff 2 Files Affected:
diff --git a/clang/lib/CodeGen/Targets/X86.cpp b/clang/lib/CodeGen/Targets/X86.cpp
index d053f41ab168f5..c2c11280838b12 100644
--- a/clang/lib/CodeGen/Targets/X86.cpp
+++ b/clang/lib/CodeGen/Targets/X86.cpp
@@ -2989,6 +2989,11 @@ static Address EmitX86_64VAArgFromMemory(CodeGenFunction &CGF,
// an 8 byte boundary.
uint64_t SizeInBytes = (CGF.getContext().getTypeSize(Ty) + 7) / 8;
+
+ if (isEmptyRecord(CGF.getContext(), Ty, true)) {
+ SizeInBytes = 0;
+ }
+
llvm::Value *Offset =
llvm::ConstantInt::get(CGF.Int32Ty, (SizeInBytes + 7) & ~7);
overflow_arg_area = CGF.Builder.CreateGEP(CGF.Int8Ty, overflow_arg_area,
diff --git a/clang/test/CodeGen/X86/x86_64-vaarg.c b/clang/test/CodeGen/X86/x86_64-vaarg.c
new file mode 100644
index 00000000000000..ae201a2d3f3017
--- /dev/null
+++ b/clang/test/CodeGen/X86/x86_64-vaarg.c
@@ -0,0 +1,15 @@
+// RUN: %clang -xc++ -target x86_64-linux-gnu -emit-llvm -S -o - %s | FileCheck %s
+
+struct Empty {};
+
+struct Empty emptyvar;
+
+void take_args(int a, ...) {
+ // CHECK: %overflow_arg_area = load ptr, ptr %overflow_arg_area_p, align 8
+ // CHECK-NEXT: %overflow_arg_area.next = getelementptr i8, ptr %overflow_arg_area, i32 0
+ // CHECK-NEXT: store ptr %overflow_arg_area.next, ptr %overflow_arg_area_p, align 8
+ __builtin_va_list l;
+ __builtin_va_start(l, a);
+ emptyvar = __builtin_va_arg(l, struct Empty);
+ __builtin_va_end(l);
+}
|
✅ With the latest revision this PR passed the C/C++ code formatter. |
fa84978
to
63ed15b
Compare
0cb5f9a
to
8e1d61d
Compare
clang/lib/CodeGen/Targets/X86.cpp
Outdated
uint64_t SizeInBytes = (CGF.getContext().getTypeSize(Ty) + 7) / 8; | ||
|
||
if (isEmptyRecord(CGF.getContext(), Ty, true)) { | ||
SizeInBytes = 0; | ||
} | ||
|
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.
Suggestion:
uint64_t SizeInBytes = 0;
if (!isEmptyRecord(CGF.getContext(), Ty, true))
SizeInBytes = (CGF.getContext().getTypeSize(Ty) + 7) / 8;
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.
done
I checked it locally, the patch doesn't fix the reported problem:
|
It seems that |
I've revised it. Can you check it again? @phoebewang |
I'm not sure the usage of the |
clang/lib/CodeGen/Targets/X86.cpp
Outdated
uint64_t SizeInBytes = (CGF.getContext().getTypeSize(Ty) + 7) / 8; | ||
uint64_t SizeInBytes = 0; | ||
if (!isEmptyRecord(CGF.getContext(), Ty, true, true)) | ||
SizeInBytes = (CGF.getContext().getTypeSize(Ty) + 7) / 8; |
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.
Using isEmptyRecord() like this doesn't seem right. I mean, it's probably pretty close to being correct in most cases, but I suspect there are edge cases where it does something slightly different.
Also, as far as I can tell, we aren't supposed to realign the va_list if an argument is empty (which could matter for an empty struct with an alignment attribute).
I think we should be using the result of classifyArgumentType() here to check what to do here. If the ABIArgInfo is "ignore", the argument isn't passed at all, so we shouldn't access the va_list. We should just make a temporary and return its address.
ping~ |
clang/lib/CodeGen/Targets/X86.cpp
Outdated
llvm::Value *Load = CGF.Builder.CreateLoad(VAListAddr); | ||
llvm::Value *Offset = llvm::ConstantInt::get(CGF.Int32Ty, 0); | ||
Load = CGF.Builder.CreateGEP(CGF.Int8Ty, Load, Offset); | ||
return Address(Load, CGF.ConvertTypeForMem(Ty), Align); |
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 don't think the address here is guaranteed to be appropriately aligned. It's probably better to create a temporary like I suggested before.
Also, a GEP with offset 0 does nothing.
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.
how to create a temporary?
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.
CGF.CreateMemTemp()
ping~ |
clang/lib/CodeGen/Targets/X86.cpp
Outdated
if (AI.isIgnore()) { | ||
return CGF.CreateMemTemp(Ty); | ||
} |
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.
Does it behave differently between C and C++? Maybe adding a C test for it?
Besides, do not use parentheses for single line code.
Checked both pr77036.cpp and pr77036.c get the same result, so looks like a right fix. But I want to wait @efriedma-quic to sign off. |
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.
Code change looks fine; the regression test needs a few tweaks.
|
||
// CHECK-LABEL: define{{.*}} void @{{.*}}empty_record_test{{.*}}() | ||
empty empty_record_test(void) { | ||
// CHECK: [[RET:%[a-z]+]] = alloca %struct.empty, align 1 |
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.
This test doesn't really make it obvious that this is working the way you want; I mean, the existence of TMP sort of hints at it, but it's not comprehensive.
I'd suggest:
- Make the va_list an argument to the function, instead of declaring it as a local.
- Consider autogenerating the CHECK lines using update_cc_test_checks.py
SizeInBytes of empty structure is 0 in C, while 1 in C++. And empty structure argument of the function is ignored in X86_64 backend.As a result, the value of variable arguments in C++ is incorrect.So we should just make a temporary and return its address.
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
SizeInBytes of empty structure is 0 in C, while 1 in C++. And empty structure argument of the function is ignored in X86_64 backend.As a result, the value of variable arguments in C++ is incorrect. fix llvm#77036 Co-authored-by: Longsheng Mou <[email protected]>
SizeInBytes of empty structure is 0 in C, while 1 in C++. And empty structure argument of the function is ignored in X86_64 backend.As a result, the value of variable arguments in C++ is incorrect. fix #77036