ARM/APCS: Only "integer like" aggregates should be returned in r0 (following

gcc's interpretation of APCS' somewhat loose specification).

llvm-svn: 81671
This commit is contained in:
Daniel Dunbar 2009-09-13 08:03:58 +00:00
parent 299d76e901
commit 626f1d8c3a
2 changed files with 182 additions and 22 deletions

View File

@ -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.
if (AllowArrays)
while (const ConstantArrayType *AT = Context.getAsConstantArrayType(FT))
FT = AT->getElementType();
return isEmptyRecord(Context, FT);
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<RecordType>();
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<RecordType>();
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)) {
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);
} else {
return (RetTy->isPromotableIntegerType() ?
ABIArgInfo::getExtend() : ABIArgInfo::getDirect());
}
}
llvm::Value *ARMABIInfo::EmitVAArg(llvm::Value *VAListAddr, QualType Ty,

View File

@ -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) {}