Skip to content
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

[X86_32][C++] fix 0 sized struct case in vaarg. #86388

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

CoTinker
Copy link
Contributor

@CoTinker CoTinker commented Mar 23, 2024

struct SuperEmpty { struct{ int a[0];} b;};
Such 0 sized structs in c++ mode can not be ignored in i386 for that c++ fields are never empty.But when EmitVAArg, its size is 0, so that va_list not increase.Maybe we can use direct in this case.
Or we can just Ignore this kind of arguments, like X86_64 did. Fix #86385.

@llvmbot llvmbot added clang Clang issues not falling into any other category backend:X86 clang:codegen labels Mar 23, 2024
@llvmbot
Copy link
Collaborator

llvmbot commented Mar 23, 2024

@llvm/pr-subscribers-clang-codegen
@llvm/pr-subscribers-backend-x86

@llvm/pr-subscribers-clang

Author: Longsheng Mou (CoTinker)

Changes

struct SuperEmpty { struct{ int a[0];} b;};
Such 0 sized structs in c++ mode can not be ignored in i386 for that c++ fields are never empty.But when EmitVAArg, its size is 0, so that va_list not increase.Maybe we can use direct in this case.

Address CodeGen::emitVoidPtrVAArg(CodeGenFunction &CGF, Address VAListAddr,
QualType ValueTy, bool IsIndirect,
TypeInfoChars ValueInfo,
CharUnits SlotSizeAndAlign,
bool AllowHigherAlign,
bool ForceRightAdjust) {
// The size and alignment of the value that was passed directly.
CharUnits DirectSize, DirectAlign;
if (IsIndirect) {
DirectSize = CGF.getPointerSize();
DirectAlign = CGF.getPointerAlign();
} else {
DirectSize = ValueInfo.Width;
DirectAlign = ValueInfo.Align;
}

This case use indirect before and DirectSize == 0, now we use direct and DirectSize == 4.
Or we can just Ignore this kind of arguments, like X86_64 did.


Full diff: https://github.com/llvm/llvm-project/pull/86388.diff

2 Files Affected:

  • (modified) clang/lib/CodeGen/Targets/X86.cpp (+3)
  • (modified) clang/test/CodeGenCXX/regparm.cpp (+1-1)
diff --git a/clang/lib/CodeGen/Targets/X86.cpp b/clang/lib/CodeGen/Targets/X86.cpp
index 1ec0f159ebcb8a..8e6e7c17452048 100644
--- a/clang/lib/CodeGen/Targets/X86.cpp
+++ b/clang/lib/CodeGen/Targets/X86.cpp
@@ -805,6 +805,9 @@ ABIArgInfo X86_32ABIInfo::classifyArgumentType(QualType Ty, CCState &State,
     if (!IsWin32StructABI && isEmptyRecord(getContext(), Ty, true))
       return ABIArgInfo::getIgnore();
 
+    if (TI.Width == 0 && getContext().getLangOpts().CPlusPlus)
+      return ABIArgInfo::getDirect();
+
     llvm::LLVMContext &LLVMContext = getVMContext();
     llvm::IntegerType *Int32 = llvm::Type::getInt32Ty(LLVMContext);
     bool NeedsPadding = false;
diff --git a/clang/test/CodeGenCXX/regparm.cpp b/clang/test/CodeGenCXX/regparm.cpp
index 1fd471c2d0727b..559702a84c99e1 100644
--- a/clang/test/CodeGenCXX/regparm.cpp
+++ b/clang/test/CodeGenCXX/regparm.cpp
@@ -32,7 +32,7 @@ struct S3 {
   } a;
 };
 __attribute((regparm(2))) void foo4(S3 a, int b);
-// CHECK: declare void @_Z4foo42S3i(ptr noundef byval(%struct.S3) align 4, i32 inreg noundef)
+// CHECK: declare void @_Z4foo42S3i(%struct.anon, i32 inreg noundef)
 void bar3(S3 a, int b) {
   foo4(a, b);
 }

Copy link

✅ With the latest revision this PR passed the C/C++ code formatter.

Copy link

✅ With the latest revision this PR passed the Python code formatter.

struct SuperEmpty { struct{ int a[0];} b;};
Such 0 sized structs in c++ mode can not be ignored in i386 for
that c++ fields are never empty.But when EmitVAArg, its size is
0, so that va_list not increase.Maybe we can use direct in this
case.
@CoTinker CoTinker changed the title [X86_32] fix weird edge case in vaarg. [X86_32] fix 0 sized struct case in vaarg. Mar 23, 2024
@efriedma-quic
Copy link
Collaborator

(This overlaps with #86075, so I'm going to hold off on reviewing this for now.)

@CoTinker
Copy link
Contributor Author

No, this scenario is different from #86075, this struct parameter is not ignored and the size is 0. But the structure size of #86075 is 1, and it will be ignored during parameter transfer.

@CoTinker
Copy link
Contributor Author

Or maybe we can just ignore this kind of arguments to fix #86385, like X86_64 did.

@CoTinker CoTinker changed the title [X86_32] fix 0 sized struct case in vaarg. [X86_32][C++] fix 0 sized struct case in vaarg. Mar 26, 2024
@efriedma-quic
Copy link
Collaborator

If you implement #86075 like I suggested, the inconsistency here also goes away, I think: if va_arg queries classifyArgumentType, you get the same result as argument lowering, so clang becomes self-consistent. (Whether that's gcc-compatible is a different question...)

@CoTinker
Copy link
Contributor Author

Inconsistency here is inevitable.
Such struct SuperEmpty { struct{ int a[0];} b;} in c++ mode can not be ignored in i386 for that c++ fields are never empty, and its TypeInfo.Width = TypeInfo.Align = 0.

RValue X86_32ABIInfo::EmitVAArg(CodeGenFunction &CGF, Address VAListAddr,
QualType Ty, AggValueSlot Slot) const {
auto TypeInfo = getContext().getTypeInfoInChars(Ty);
CCState State(*const_cast<CGFunctionInfo *>(CGF.CurFnInfo));
ABIArgInfo AI = classifyArgumentType(Ty, State, /*ArgIndex*/ 0);
// Empty records are ignored for parameter passing purposes.
if (AI.isIgnore())
return Slot.asRValue();
// x86-32 changes the alignment of certain arguments on the stack.
//
// Just messing with TypeInfo like this works because we never pass
// anything indirectly.
TypeInfo.Align = CharUnits::fromQuantity(
getTypeStackAlignInBytes(Ty, TypeInfo.Align.getQuantity()));
return emitVoidPtrVAArg(CGF, VAListAddr, Ty, /*Indirect*/ false, TypeInfo,
CharUnits::fromQuantity(4),
/*AllowHigherAlign*/ true, Slot);
}

And Indirect is false, so that DirectSize = DirectAlign = 0, which leads to error.

RValue CodeGen::emitVoidPtrVAArg(CodeGenFunction &CGF, Address VAListAddr,
QualType ValueTy, bool IsIndirect,
TypeInfoChars ValueInfo,
CharUnits SlotSizeAndAlign,
bool AllowHigherAlign, AggValueSlot Slot,
bool ForceRightAdjust) {
// The size and alignment of the value that was passed directly.
CharUnits DirectSize, DirectAlign;
if (IsIndirect) {
DirectSize = CGF.getPointerSize();
DirectAlign = CGF.getPointerAlign();
} else {
DirectSize = ValueInfo.Width;
DirectAlign = ValueInfo.Align;
}
// Cast the address we've calculated to the right type.
llvm::Type *DirectTy = CGF.ConvertTypeForMem(ValueTy), *ElementTy = DirectTy;
if (IsIndirect) {
unsigned AllocaAS = CGF.CGM.getDataLayout().getAllocaAddrSpace();
DirectTy = llvm::PointerType::get(CGF.getLLVMContext(), AllocaAS);
}
Address Addr = emitVoidPtrDirectVAArg(CGF, VAListAddr, DirectTy, DirectSize,
DirectAlign, SlotSizeAndAlign,
AllowHigherAlign, ForceRightAdjust);

How do I fix this? Change the classifyArgumentType or the EmiVAArg? @efriedma-quic

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backend:X86 clang:codegen clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[X86_32] Structure of size 0 in C++ get wrong result when emit vaarg
3 participants