From 626f1d8c3abed81f814689ce65367209895f3257 Mon Sep 17 00:00:00 2001 From: Daniel Dunbar Date: Sun, 13 Sep 2009 08:03:58 +0000 Subject: [PATCH] ARM/APCS: Only "integer like" aggregates should be returned in r0 (following gcc's interpretation of APCS' somewhat loose specification). llvm-svn: 81671 --- clang/lib/CodeGen/TargetABIInfo.cpp | 149 ++++++++++++++++++++---- clang/test/CodeGen/arm-apcs-arguments.c | 55 ++++++++- 2 files changed, 182 insertions(+), 22 deletions(-) diff --git a/clang/lib/CodeGen/TargetABIInfo.cpp b/clang/lib/CodeGen/TargetABIInfo.cpp index c85504e64cc3..892994af1076 100644 --- a/clang/lib/CodeGen/TargetABIInfo.cpp +++ b/clang/lib/CodeGen/TargetABIInfo.cpp @@ -50,26 +50,29 @@ void ABIArgInfo::dump() const { fprintf(stderr, ")\n"); } -static bool isEmptyRecord(ASTContext &Context, QualType T); +static bool isEmptyRecord(ASTContext &Context, QualType T, bool AllowArrays); /// isEmptyField - Return true iff a the field is "empty", that is it /// is an unnamed bit-field or an (array of) empty record(s). -static bool isEmptyField(ASTContext &Context, const FieldDecl *FD) { +static bool isEmptyField(ASTContext &Context, const FieldDecl *FD, + bool AllowArrays) { if (FD->isUnnamedBitfield()) return true; QualType FT = FD->getType(); - // Constant arrays of empty records count as empty, strip them off. - while (const ConstantArrayType *AT = Context.getAsConstantArrayType(FT)) - FT = AT->getElementType(); - return isEmptyRecord(Context, FT); + // Constant arrays of empty records count as empty, strip them off. + if (AllowArrays) + while (const ConstantArrayType *AT = Context.getAsConstantArrayType(FT)) + FT = AT->getElementType(); + + return isEmptyRecord(Context, FT, AllowArrays); } /// isEmptyRecord - Return true iff a structure contains only empty /// fields. Note that a structure with a flexible array member is not /// considered empty. -static bool isEmptyRecord(ASTContext &Context, QualType T) { +static bool isEmptyRecord(ASTContext &Context, QualType T, bool AllowArrays) { const RecordType *RT = T->getAs(); if (!RT) return 0; @@ -78,7 +81,7 @@ static bool isEmptyRecord(ASTContext &Context, QualType T) { return false; for (RecordDecl::field_iterator i = RD->field_begin(), e = RD->field_end(); i != e; ++i) - if (!isEmptyField(Context, *i)) + if (!isEmptyField(Context, *i, AllowArrays)) return false; return true; } @@ -107,7 +110,7 @@ static const Type *isSingleElementStruct(QualType T, ASTContext &Context) { QualType FT = FD->getType(); // Ignore empty fields. - if (isEmptyField(Context, FD)) + if (isEmptyField(Context, FD, true)) continue; // If we already found an element then this isn't a single-element @@ -286,7 +289,7 @@ bool X86_32ABIInfo::shouldReturnTypeInRegister(QualType Ty, const FieldDecl *FD = *i; // Empty fields are ignored. - if (isEmptyField(Context, FD)) + if (isEmptyField(Context, FD, true)) continue; // Check fields recursively. @@ -1388,10 +1391,10 @@ void ARMABIInfo::computeInfo(CGFunctionInfo &FI, ASTContext &Context, ABIArgInfo ARMABIInfo::classifyArgumentType(QualType Ty, ASTContext &Context, llvm::LLVMContext &VMContext) const { - if (!CodeGenFunction::hasAggregateLLVMType(Ty)) { + if (!CodeGenFunction::hasAggregateLLVMType(Ty)) return (Ty->isPromotableIntegerType() ? ABIArgInfo::getExtend() : ABIArgInfo::getDirect()); - } + // FIXME: This is kind of nasty... but there isn't much choice because the ARM // backend doesn't support byval. // FIXME: This doesn't handle alignment > 64 bits. @@ -1410,22 +1413,126 @@ ABIArgInfo ARMABIInfo::classifyArgumentType(QualType Ty, return ABIArgInfo::getCoerce(STy); } +static bool isIntegerLikeType(QualType Ty, + ASTContext &Context, + llvm::LLVMContext &VMContext) { + // APCS, C Language Calling Conventions, Non-Simple Return Values: A structure + // is called integer-like if its size is less than or equal to one word, and + // the offset of each of its addressable sub-fields is zero. + + uint64_t Size = Context.getTypeSize(Ty); + + // Check that the type fits in a word. + if (Size > 32) + return false; + + // FIXME: Handle vector types! + if (Ty->isVectorType()) + return false; + + // If this is a builtin or pointer type then it is ok. + if (Ty->getAsBuiltinType() || Ty->isPointerType()) + return true; + + // Complex types "should" be ok by the definition above, but they are not. + if (Ty->isAnyComplexType()) + return false; + + // Single element and zero sized arrays should be allowed, by the definition + // above, but they are not. + + // Otherwise, it must be a record type. + const RecordType *RT = Ty->getAs(); + if (!RT) return false; + + // Ignore records with flexible arrays. + const RecordDecl *RD = RT->getDecl(); + if (RD->hasFlexibleArrayMember()) + return false; + + // Check that all sub-fields are at offset 0, and are themselves "integer + // like". + const ASTRecordLayout &Layout = Context.getASTRecordLayout(RD); + + bool HadField = false; + unsigned idx = 0; + for (RecordDecl::field_iterator i = RD->field_begin(), e = RD->field_end(); + i != e; ++i, ++idx) { + const FieldDecl *FD = *i; + + // Check if this field is at offset 0. + uint64_t Offset = Layout.getFieldOffset(idx); + if (Offset != 0) { + // Allow padding bit-fields, but only if they are all at the end of the + // structure (despite the wording above, this matches gcc). + if (FD->isBitField() && + !FD->getBitWidth()->EvaluateAsInt(Context).getZExtValue()) { + for (; i != e; ++i) + if (!i->isBitField() || + i->getBitWidth()->EvaluateAsInt(Context).getZExtValue()) + return false; + + // All remaining fields are padding, allow this. + return true; + } + + return false; + } + + if (!isIntegerLikeType(FD->getType(), Context, VMContext)) + return false; + + // Only allow at most one field in a structure. Again this doesn't match the + // wording above, but follows gcc. + if (!RD->isUnion()) { + if (HadField) + return false; + + HadField = true; + } + } + + return true; +} + ABIArgInfo ARMABIInfo::classifyReturnType(QualType RetTy, ASTContext &Context, llvm::LLVMContext &VMContext) const { - if (RetTy->isVoidType()) { + if (RetTy->isVoidType()) return ABIArgInfo::getIgnore(); - } else if (CodeGenFunction::hasAggregateLLVMType(RetTy)) { - // Aggregates <= 4 bytes are returned in r0; other aggregates - // are returned indirectly. - uint64_t Size = Context.getTypeSize(RetTy); - if (Size <= 32) - return ABIArgInfo::getCoerce(llvm::Type::getInt32Ty(VMContext)); - return ABIArgInfo::getIndirect(0); - } else { + + if (!CodeGenFunction::hasAggregateLLVMType(RetTy)) return (RetTy->isPromotableIntegerType() ? ABIArgInfo::getExtend() : ABIArgInfo::getDirect()); + + // Are we following APCS? + if (getABIKind() == APCS) { + if (isEmptyRecord(Context, RetTy, false)) + return ABIArgInfo::getIgnore(); + + // Integer like structures are returned in r0. + if (isIntegerLikeType(RetTy, Context, VMContext)) { + // Return in the smallest viable integer type. + uint64_t Size = Context.getTypeSize(RetTy); + if (Size <= 8) + return ABIArgInfo::getCoerce(llvm::Type::getInt8Ty(VMContext)); + if (Size <= 16) + return ABIArgInfo::getCoerce(llvm::Type::getInt16Ty(VMContext)); + return ABIArgInfo::getCoerce(llvm::Type::getInt32Ty(VMContext)); + } + + // Otherwise return in memory. + return ABIArgInfo::getIndirect(0); } + + // Otherwise this is an AAPCS variant. + + // Aggregates <= 4 bytes are returned in r0; other aggregates + // are returned indirectly. + uint64_t Size = Context.getTypeSize(RetTy); + if (Size <= 32) + return ABIArgInfo::getCoerce(llvm::Type::getInt32Ty(VMContext)); + return ABIArgInfo::getIndirect(0); } llvm::Value *ARMABIInfo::EmitVAArg(llvm::Value *VAListAddr, QualType Ty, diff --git a/clang/test/CodeGen/arm-apcs-arguments.c b/clang/test/CodeGen/arm-apcs-arguments.c index f64680649049..d427dd144a24 100644 --- a/clang/test/CodeGen/arm-apcs-arguments.c +++ b/clang/test/CodeGen/arm-apcs-arguments.c @@ -1,6 +1,59 @@ -// RUN: clang-cc -triple armv7-apple-darwin9 -emit-llvm -o - %s | FileCheck %s +// RUX: iphone-llvm-gcc -arch armv7 -flto -S -o - %s | FileCheck %s +// RUN: clang-cc -triple armv7-apple-darwin9 -emit-llvm -w -o - %s | FileCheck %s // CHECK: define arm_apcscc signext i8 @f0() char f0(void) { return 0; } + +// CHECK: define arm_apcscc i8 @f1() +struct s1 { char f0; }; +struct s1 f1(void) {} + +// CHECK: define arm_apcscc i16 @f2() +struct s2 { short f0; }; +struct s2 f2(void) {} + +// CHECK: define arm_apcscc i32 @f3() +struct s3 { int f0; }; +struct s3 f3(void) {} + +// CHECK: define arm_apcscc i32 @f4() +struct s4 { struct s4_0 { int f0; } f0; }; +struct s4 f4(void) {} + +// CHECK: define arm_apcscc void @f5( +// CHECK: struct.s5* noalias sret +struct s5 { struct { } f0; int f1; }; +struct s5 f5(void) {} + +// CHECK: define arm_apcscc void @f6( +// CHECK: struct.s6* noalias sret +struct s6 { int f0[1]; }; +struct s6 f6(void) {} + +// CHECK: define arm_apcscc void @f7() +struct s7 { struct { int : 0; } f0; }; +struct s7 f7(void) {} + +// CHECK: define arm_apcscc void @f8( +// CHECK: struct.s8* noalias sret +struct s8 { struct { int : 0; } f0[1]; }; +struct s8 f8(void) {} + +// CHECK: define arm_apcscc i32 @f9() +struct s9 { int f0; int : 0; }; +struct s9 f9(void) {} + +// CHECK: define arm_apcscc i32 @f10() +struct s10 { int f0; int : 0; int : 0; }; +struct s10 f10(void) {} + +// CHECK: define arm_apcscc void @f11( +// CHECK: struct.s10* noalias sret +struct s11 { int : 0; int f0; }; +struct s11 f11(void) {} + +// CHECK: define arm_apcscc i32 @f12() +union u12 { char f0; short f1; int f2; }; +union u12 f12(void) {}