From: Daniel Dunbar Date: Wed, 1 Apr 2009 07:45:00 +0000 (+0000) Subject: x86-32 Darwin ABI: Handle small structures correctly. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=cf6bde343ff5653744ca782e04d5a9c54b260042;p=clang x86-32 Darwin ABI: Handle small structures correctly. - Small structures are returned in a register if: 1. They fit nicely in a register. 2. All fields fit nicely in a register. (more or less) - We now pass the first 5000 ABITests if unions are disabled. - [irgen] x86-32 ABI compatibility with small structs git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@68197 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/CodeGen/CGCall.cpp b/lib/CodeGen/CGCall.cpp index 945d52faa3..4db36d9140 100644 --- a/lib/CodeGen/CGCall.cpp +++ b/lib/CodeGen/CGCall.cpp @@ -262,6 +262,8 @@ class X86_32ABIInfo : public ABIInfo { return (Size == 8 || Size == 16 || Size == 32 || Size == 64); } + static bool shouldReturnTypeInRegister(QualType Ty, ASTContext &Context); + public: ABIArgInfo classifyReturnType(QualType RetTy, ASTContext &Context) const; @@ -283,6 +285,60 @@ public: }; } + +/// shouldReturnTypeInRegister - Determine if the given type should be +/// passed in a register (for the Darwin ABI). +bool X86_32ABIInfo::shouldReturnTypeInRegister(QualType Ty, + ASTContext &Context) { + uint64_t Size = Context.getTypeSize(Ty); + + // Type must be register sized. + if (!isRegisterSize(Size)) + return false; + + if (Ty->isVectorType()) { + // 64- and 128- bit vectors inside structures are not returned in + // registers. + if (Size == 64 || Size == 128) + return false; + + return true; + } + + // If this is a builtin, pointer, or complex type, it is ok. + if (Ty->getAsBuiltinType() || Ty->isPointerType() || Ty->isAnyComplexType()) + return true; + + // Arrays are treated like records. + if (const ConstantArrayType *AT = Context.getAsConstantArrayType(Ty)) + return shouldReturnTypeInRegister(AT->getElementType(), Context); + + // Otherwise, it must be a record type. + const RecordType *RT = Ty->getAsRecordType(); + if (!RT) return false; + + // Structure types are passed in register if all fields would be + // passed in a register. + for (RecordDecl::field_iterator i = RT->getDecl()->field_begin(), + e = RT->getDecl()->field_end(); i != e; ++i) { + const FieldDecl *FD = *i; + + // FIXME: Reject bitfields wholesale for now; this is incorrect. + if (FD->isBitField()) + return false; + + // Empty structures are ignored. + if (isEmptyRecord(FD->getType())) + continue; + + // Check fields recursively. + if (!shouldReturnTypeInRegister(FD->getType(), Context)) + return false; + } + + return true; +} + ABIArgInfo X86_32ABIInfo::classifyReturnType(QualType RetTy, ASTContext &Context) const { if (RetTy->isVoidType()) { @@ -345,8 +401,18 @@ ABIArgInfo X86_32ABIInfo::classifyReturnType(QualType RetTy, } uint64_t Size = Context.getTypeSize(RetTy); - if (isRegisterSize(Size)) - return ABIArgInfo::getCoerce(llvm::IntegerType::get(Size)); + if (isRegisterSize(Size)) { + // Always return in register for unions for now. + // FIXME: This is wrong, but better than treating as a + // structure. + if (RetTy->isUnionType()) + return ABIArgInfo::getCoerce(llvm::IntegerType::get(Size)); + + // Small structures which are register sized are generally returned + // in a register. + if (X86_32ABIInfo::shouldReturnTypeInRegister(RetTy, Context)) + return ABIArgInfo::getCoerce(llvm::IntegerType::get(Size)); + } return ABIArgInfo::getIndirect(0); } else { diff --git a/test/CodeGen/x86_32-arguments.c b/test/CodeGen/x86_32-arguments.c index 8e8a2f9998..8d1e5dd8ad 100644 --- a/test/CodeGen/x86_32-arguments.c +++ b/test/CodeGen/x86_32-arguments.c @@ -9,11 +9,6 @@ // RUN: grep 'define void @f7(i32 %a0)' %t && // RUN: grep 'define i64 @f8_1()' %t && // RUN: grep 'define void @f8_2(i32 %a0.0, i32 %a0.1)' %t && -// RUN: grep 'define i64 @f9_1()' %t && - -// FIXME: This is wrong, but we want the coverage of the other -// tests. This should be the same as @f8_2. -// RUN: grep 'define void @f9_2(%.truct.s9\* byval %a0)' %t && char f0(void) { } @@ -52,8 +47,13 @@ void f8_2(struct s8 a0) { // This should be passed just as s8. -// FIXME: This is currently broken, but the test case is accepting it -// so we get coverage of the other cases. +// FIXME: This is wrong, but we want the coverage of the other +// tests. This should be the same as @f8_1. +// RUN: grep 'define void @f9_1(%.truct.s9\* noalias sret %agg.result)' %t && + +// FIXME: This is wrong, but we want the coverage of the other +// tests. This should be the same as @f8_2. +// RUN: grep 'define void @f9_2(%.truct.s9\* byval %a0)' %t && struct s9 { int a : 17; int b; @@ -96,11 +96,11 @@ T16 f16(void) {} // 128-bits). // RUN: grep 'i32 @f17()' %t && -// RUN: grep -F 'void @f18(%0* noalias sret %agg.result)' %t && -// RUN: grep -F 'void @f19(%1* noalias sret %agg.result)' %t && -// RUN: grep -F 'void @f20(%2* noalias sret %agg.result)' %t && -// RUN: grep -F 'void @f21(%3* noalias sret %agg.result)' %t && -// RUN: grep -F 'void @f22(%4* noalias sret %agg.result)' %t && +// RUN: grep -F 'void @f18(%3* noalias sret %agg.result)' %t && +// RUN: grep -F 'void @f19(%4* noalias sret %agg.result)' %t && +// RUN: grep -F 'void @f20(%5* noalias sret %agg.result)' %t && +// RUN: grep -F 'void @f21(%6* noalias sret %agg.result)' %t && +// RUN: grep -F 'void @f22(%7* noalias sret %agg.result)' %t && struct { T11 a; } f17(void) {} struct { T12 a; } f18(void) {} struct { T13 a; } f19(void) {} @@ -117,4 +117,10 @@ struct { float a; } f23(void) {} struct { float a[1]; } f24(void) {} struct { struct {} a; struct { float a[1]; } b; } f25(void) {} +// Small structures are handled recursively +// RUN: grep -F 'i32 @f26()' %t && +// RUN: grep 'void @f27(%.truct.s27\* noalias sret %agg.result)' %t && +struct s26 { struct { char a, b; } a; struct { char a, b } b; } f26(void) {} +struct s27 { struct { char a, b, c; } a; struct { char a } b; } f27(void) {} + // RUN: true