]> granicus.if.org Git - clang/commitdiff
ARM/APCS: Only "integer like" aggregates should be returned in r0 (following
authorDaniel Dunbar <daniel@zuster.org>
Sun, 13 Sep 2009 08:03:58 +0000 (08:03 +0000)
committerDaniel Dunbar <daniel@zuster.org>
Sun, 13 Sep 2009 08:03:58 +0000 (08:03 +0000)
gcc's interpretation of APCS' somewhat loose specification).

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@81671 91177308-0d34-0410-b5e6-96231b3b80d8

lib/CodeGen/TargetABIInfo.cpp
test/CodeGen/arm-apcs-arguments.c

index c85504e64cc3bf977f059fd84529111f367ffb92..892994af107678693aa8f4b689bf848a844aed24 100644 (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.
-  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<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)) {
-    // 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,
index f64680649049f8dd9b10889e504771f5e9fa590f..d427dd144a24ab4ef321a5486fb3858f376bb34f 100644 (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) {}