chill created this revision.
chill added reviewers: john.brawn, olista01, eli.friedman, rengolin.
Herald added a reviewer: javed.absar.
Herald added subscribers: chrib, kristof.beyls.

The "Procedure Call Procedure Call Standard for the ARMĀ®  Architecture" 
(https://static.docs.arm.com/ihi0042/f/IHI0042F_aapcs.pdf), specifies that 
composite types are passed  according to their natural alignment:

> 5.5 Parameter Passing
>  ...
>  B.5 If the argument is an alignment adjusted type its value is passed as a 
> copy of the actual value. The
>  copy will have an alignment defined as follows.
> 
> - For a Fundamental Data Type, the alignment is the natural alignment of that 
> type, after any promotions
> - For a Composite Type, the alignment of the copy will have 4-byte alignment 
> if its natural alignment is <= 4 and 8-byte alignment if its natural 
> alignment is >= 8

The "natural alignment" is defined as the maximum of the alignment of the 
top-level components:

> 4.3 Composite Types
>  ...
> 
> - The natural alignment of a composite type is the maximum of each of the 
> member alignments of the 'top-level' members of the composite type i.e. 
> before any alignment adjustment of the entire composite is applied

`clang`,  however, uses the actual alignment of the composite, instead of the 
natural alignment.

This patch fixes passing of composite types to use the natural alignment.  With 
this patch `clang` conforms to AAPCS and is compatible with GCC.


Repository:
  rC Clang

https://reviews.llvm.org/D46013

Files:
  include/clang/AST/ASTContext.h
  include/clang/AST/RecordLayout.h
  lib/AST/ASTContext.cpp
  lib/AST/RecordLayout.cpp
  lib/AST/RecordLayoutBuilder.cpp
  lib/CodeGen/TargetInfo.cpp
  test/CodeGen/aapcs-align.cc
  test/CodeGen/arm-arguments.c

Index: test/CodeGen/arm-arguments.c
===================================================================
--- test/CodeGen/arm-arguments.c
+++ test/CodeGen/arm-arguments.c
@@ -211,10 +211,13 @@
 // APCS-GNU: call void @llvm.memcpy.p0i8.p0i8.i32(i8* align {{[0-9]+}} %[[b]], i8* align {{[0-9]+}} %[[c]]
 // APCS-GNU: %[[d:.*]] = bitcast %struct.s35* %[[a]] to <4 x float>*
 // APCS-GNU: load <4 x float>, <4 x float>* %[[d]], align 16
-// AAPCS-LABEL: define arm_aapcscc <4 x float> @f35(i32 %i, %struct.s35* byval align 8, %struct.s35* byval align 8)
-// AAPCS: %[[a:.*]] = alloca %struct.s35, align 16
-// AAPCS: %[[b:.*]] = bitcast %struct.s35* %[[a]] to i8*
-// AAPCS: %[[c:.*]] = bitcast %struct.s35* %0 to i8*
-// AAPCS: call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 16 %[[b]], i8* align 8 %[[c]]
-// AAPCS: %[[d:.*]] = bitcast %struct.s35* %[[a]] to <4 x float>*
-// AAPCS: load <4 x float>, <4 x float>* %[[d]], align 16
+
+// AAPCS-LABEL: define arm_aapcscc <4 x float> @f35(i32 %i, %struct.s35* byval align 4 %s1, %struct.s35* byval align 4 %s2)
+// AAPCS: %[[a_addr:.*]] = alloca <4 x float>, align 16
+// AAPCS: %[[b_addr:.*]] = alloca <4 x float>, align 16
+// AAPCS: %[[p1:.*]] = bitcast %struct.s35* %s1 to <4 x float>*
+// AAPCS: %[[a:.*]] = load <4 x float>, <4 x float>* %[[p1]], align 4
+// AAPCS: %[[p2:.*]] = bitcast %struct.s35* %s2 to <4 x float>*
+// AAPCS: %[[b:.*]] = load <4 x float>, <4 x float>* %[[p2]], align 4
+// AAPCS: store <4 x float> %[[a]], <4 x float>* %[[a_addr]], align 16
+// AAPCS: store <4 x float> %[[b]], <4 x float>* %[[b_addr]], align 16
Index: test/CodeGen/aapcs-align.cc
===================================================================
--- /dev/null
+++ test/CodeGen/aapcs-align.cc
@@ -0,0 +1,102 @@
+// REQUIRES: arm-registered-target
+// RUN: %clang_cc1 -triple arm-none-none-eabi \
+// RUN:   -O2 \
+// RUN:   -target-cpu cortex-a8 \
+// RUN:   -emit-llvm -o - %s | FileCheck %s
+
+extern "C" {
+
+struct S {
+  int x, y;
+};
+
+// Base case, nothing interesting.
+void f0(int, S);
+void f0m(int, int, int, int, int, S);
+void g0() {
+  S s = {6, 7};
+  f0(1, s);
+  f0m(1, 2, 3, 4, 5, s);
+}
+// CHECK: define void @g0
+// CHECK: call void @f0{{.*}}[2 x i32] [i32 6, i32 7]
+// CHECK: call void @f0m{{.*}}[2 x i32] [i32 6, i32 7]
+// CHECK: declare void @f0(i32, [2 x i32])
+// CHECK: declare void @f0m(i32, i32, i32, i32, i32, [2 x i32])
+
+// Aligned struct, passed according to its natural alignment.
+struct __attribute__((aligned(8))) S8 {
+  int x, y;
+} s8;
+
+void f1(int, S8);
+void f1m(int, int, int, int, int, S8 s);
+void g1() {
+  S8 s = {6, 7};
+  f1(1, s);
+  f1m(1, 2, 3, 4, 5, s);
+}
+// CHECK: define void @g1
+// CHECK: call void @f1{{.*}}[2 x i32] [i32 6, i32 7]
+// CHECK: call void @f1m{{.*}}[2 x i32] [i32 6, i32 7]
+// CHECK: declare void @f1(i32, [2 x i32])
+// CHECK: declare void @f1m(i32, i32, i32, i32, i32, [2 x i32])
+
+// Aligned struct, passed according to its natural alignment.
+struct alignas(16) S16 {
+  int x, y;
+};
+
+extern "C" void f2(int, S16);
+extern "C" void f2m(int, int, int, int, int, S16);
+
+void g2() {
+  S16 s = {6, 7};
+  f2(1, s);
+  f2m(1, 2, 3, 4, 5, s);
+}
+// CHECK: define void @g2
+// CHECK: call void @f2{{.*}}[4 x i32] [i32 6, i32 7,
+// CHECK: call void @f2m{{.*}}[4 x i32] [i32 6, i32 7,
+// CHECK: declare void @f2(i32, [4 x i32])
+// CHECK: declare void @f2m(i32, i32, i32, i32, i32, [4 x i32])
+
+// Increased natural alignment.
+struct SF8 {
+  int x __attribute__((aligned(8)));
+  int y;
+};
+
+void f3(int, SF8);
+void f3m(int, int, int, int, int, SF8);
+void g3() {
+  SF8 s = {6, 7};
+  f3(1, s);
+  f3m(1, 2, 3, 4, 5, s);
+}
+// CHECK: define void @g3
+// CHECK: call void @f3{{.*}}[1 x i64] [i64 30064771078]
+// CHECK: call void @f3m{{.*}}[1 x i64] [i64 30064771078]
+// CHECK: declare void @f3(i32, [1 x i64])
+// CHECK: declare void @f3m(i32, i32, i32, i32, i32, [1 x i64])
+
+// Increased natural alignment, capped to 8 though.
+struct SF16 {
+  int x;
+  int y alignas(16);
+  int z, a, b, c, d, e, f, g, h, i, j, k;
+};
+
+void f4(int, SF16);
+void f4m(int, int, int, int, int, SF16);
+void g4() {
+  SF16 s = {6, 7};
+  f4(1, s);
+  f4m(1, 2, 3, 4, 5, s);
+}
+// CHECK: define void @g4
+// CHECK: call void @f4{{.*}}i32 1, %struct.SF16* byval nonnull align 8
+// CHECK: call void @f4m{{.*}}%struct.SF16* byval nonnull align 8
+// CHECK: declare void @f4(i32, %struct.SF16* byval align 8)
+// CHECK: declare void @f4m(i32, i32, i32, i32, i32, %struct.SF16* byval align 8)
+}
Index: lib/CodeGen/TargetInfo.cpp
===================================================================
--- lib/CodeGen/TargetInfo.cpp
+++ lib/CodeGen/TargetInfo.cpp
@@ -5777,15 +5777,18 @@
   // The ABI alignment for APCS is 4-byte and for AAPCS at least 4-byte and at
   // most 8-byte. We realign the indirect argument if type alignment is bigger
   // than ABI alignment.
-  uint64_t ABIAlign = 4;
-  uint64_t TyAlign = getContext().getTypeAlign(Ty) / 8;
-  if (getABIKind() == ARMABIInfo::AAPCS_VFP ||
-       getABIKind() == ARMABIInfo::AAPCS)
-    ABIAlign = std::min(std::max(TyAlign, (uint64_t)4), (uint64_t)8);
-
+  uint64_t ABIAlign = 32;
+  uint64_t TyAlign;
+    if (getABIKind() == ARMABIInfo::AAPCS_VFP ||
+      getABIKind() == ARMABIInfo::AAPCS) {
+      TyAlign = getContext().getTypeNaturalAlign(Ty);
+      ABIAlign = std::min(std::max(TyAlign, (uint64_t)32), (uint64_t)64);
+  } else {
+      TyAlign = getContext().getTypeAlign(Ty) ;
+  }
   if (getContext().getTypeSizeInChars(Ty) > CharUnits::fromQuantity(64)) {
     assert(getABIKind() != ARMABIInfo::AAPCS16_VFP && "unexpected byval");
-    return ABIArgInfo::getIndirect(CharUnits::fromQuantity(ABIAlign),
+    return ABIArgInfo::getIndirect(CharUnits::fromQuantity(ABIAlign / 8),
                                    /*ByVal=*/true,
                                    /*Realign=*/TyAlign > ABIAlign);
   }
@@ -5801,7 +5804,7 @@
   unsigned SizeRegs;
   // FIXME: Try to match the types of the arguments more accurately where
   // we can.
-  if (getContext().getTypeAlign(Ty) <= 32) {
+  if (TyAlign <= 32) {
     ElemTy = llvm::Type::getInt32Ty(getVMContext());
     SizeRegs = (getContext().getTypeSize(Ty) + 31) / 32;
   } else {
Index: lib/AST/RecordLayoutBuilder.cpp
===================================================================
--- lib/AST/RecordLayoutBuilder.cpp
+++ lib/AST/RecordLayoutBuilder.cpp
@@ -582,6 +582,9 @@
   /// \brief The alignment if attribute packed is not used.
   CharUnits UnpackedAlignment;
 
+  /// \brief The maximum of the alignments of top-level members.
+  CharUnits NaturalAlignment;
+
   SmallVector<uint64_t, 16> FieldOffsets;
 
   /// \brief Whether the external AST source has provided a layout for this
@@ -662,6 +665,7 @@
                              EmptySubobjectMap *EmptySubobjects)
       : Context(Context), EmptySubobjects(EmptySubobjects), Size(0),
         Alignment(CharUnits::One()), UnpackedAlignment(CharUnits::One()),
+        NaturalAlignment(CharUnits::One()),
         UseExternalLayout(false), InferAlignment(false), Packed(false),
         IsUnion(false), IsMac68kAlign(false), IsMsStruct(false),
         UnfilledBitsInLastUnit(0), LastBitfieldTypeSize(0),
@@ -1700,7 +1704,9 @@
   setSize(std::max(getSizeInBits(), getDataSizeInBits()));
 
   // Remember max struct/class alignment.
-  UpdateAlignment(Context.toCharUnitsFromBits(FieldAlign), 
+  NaturalAlignment =
+      std::max(NaturalAlignment, Context.toCharUnitsFromBits(FieldAlign));
+  UpdateAlignment(Context.toCharUnitsFromBits(FieldAlign),
                   Context.toCharUnitsFromBits(UnpackedFieldAlign));
 }
 
@@ -1855,6 +1861,7 @@
   setSize(std::max(getSizeInBits(), getDataSizeInBits()));
 
   // Remember max struct/class alignment.
+  NaturalAlignment = std::max(NaturalAlignment, FieldAlign);
   UpdateAlignment(FieldAlign, UnpackedFieldAlign);
 }
 
@@ -2973,7 +2980,8 @@
     if (const auto *RD = dyn_cast<CXXRecordDecl>(D)) {
       Builder.cxxLayout(RD);
       NewEntry = new (*this) ASTRecordLayout(
-          *this, Builder.Size, Builder.Alignment, Builder.RequiredAlignment,
+          *this, Builder.Size, Builder.Alignment, Builder.Alignment,
+          Builder.RequiredAlignment,
           Builder.HasOwnVFPtr, Builder.HasOwnVFPtr || Builder.PrimaryBase,
           Builder.VBPtrOffset, Builder.DataSize, Builder.FieldOffsets,
           Builder.NonVirtualSize, Builder.Alignment, CharUnits::Zero(),
@@ -2983,7 +2991,8 @@
     } else {
       Builder.layout(D);
       NewEntry = new (*this) ASTRecordLayout(
-          *this, Builder.Size, Builder.Alignment, Builder.RequiredAlignment,
+          *this, Builder.Size, Builder.Alignment, Builder.Alignment,
+          Builder.RequiredAlignment,
           Builder.Size, Builder.FieldOffsets);
     }
   } else {
@@ -3004,7 +3013,7 @@
       CharUnits NonVirtualSize =
           skipTailPadding ? DataSize : Builder.NonVirtualSize;
       NewEntry = new (*this) ASTRecordLayout(
-          *this, Builder.getSize(), Builder.Alignment,
+          *this, Builder.getSize(), Builder.Alignment, Builder.NaturalAlignment,
           /*RequiredAlignment : used by MS-ABI)*/
           Builder.Alignment, Builder.HasOwnVFPtr, RD->isDynamicClass(),
           CharUnits::fromQuantity(-1), DataSize, Builder.FieldOffsets,
@@ -3017,7 +3026,7 @@
       Builder.Layout(D);
 
       NewEntry = new (*this) ASTRecordLayout(
-          *this, Builder.getSize(), Builder.Alignment,
+          *this, Builder.getSize(), Builder.Alignment, Builder.NaturalAlignment,
           /*RequiredAlignment : used by MS-ABI)*/
           Builder.Alignment, Builder.getSize(), Builder.FieldOffsets);
     }
@@ -3171,6 +3180,7 @@
   const ASTRecordLayout *NewEntry =
     new (*this) ASTRecordLayout(*this, Builder.getSize(),
                                 Builder.Alignment,
+                                Builder.NaturalAlignment,
                                 /*RequiredAlignment : used by MS-ABI)*/
                                 Builder.Alignment,
                                 Builder.getDataSize(),
Index: lib/AST/RecordLayout.cpp
===================================================================
--- lib/AST/RecordLayout.cpp
+++ lib/AST/RecordLayout.cpp
@@ -30,17 +30,20 @@
 
 ASTRecordLayout::ASTRecordLayout(const ASTContext &Ctx, CharUnits size,
                                  CharUnits alignment,
+                                 CharUnits naturalAlignment,
                                  CharUnits requiredAlignment,
                                  CharUnits datasize,
                                  ArrayRef<uint64_t> fieldoffsets)
     : Size(size), DataSize(datasize), Alignment(alignment),
+      NaturalAlignment(naturalAlignment),
       RequiredAlignment(requiredAlignment) {
   FieldOffsets.append(Ctx, fieldoffsets.begin(), fieldoffsets.end());
 }
 
 // Constructor for C++ records.
 ASTRecordLayout::ASTRecordLayout(const ASTContext &Ctx,
                                  CharUnits size, CharUnits alignment,
+                                 CharUnits naturalAlignment,
                                  CharUnits requiredAlignment,
                                  bool hasOwnVFPtr, bool hasExtendableVFPtr,
                                  CharUnits vbptroffset,
@@ -57,6 +60,7 @@
                                  const BaseOffsetsMapTy& BaseOffsets,
                                  const VBaseOffsetsMapTy& VBaseOffsets)
   : Size(size), DataSize(datasize), Alignment(alignment),
+    NaturalAlignment(naturalAlignment),
     RequiredAlignment(requiredAlignment), CXXInfo(new (Ctx) CXXRecordLayoutInfo)
 {
   FieldOffsets.append(Ctx, fieldoffsets.begin(), fieldoffsets.end());
Index: lib/AST/ASTContext.cpp
===================================================================
--- lib/AST/ASTContext.cpp
+++ lib/AST/ASTContext.cpp
@@ -1663,6 +1663,7 @@
 TypeInfo ASTContext::getTypeInfoImpl(const Type *T) const {
   uint64_t Width = 0;
   unsigned Align = 8;
+  unsigned NaturalAlign = 0;
   bool AlignIsRequired = false;
   unsigned AS = 0;
   switch (T->getTypeClass()) {
@@ -1873,6 +1874,7 @@
     const ASTRecordLayout &Layout = getASTObjCInterfaceLayout(ObjCI->getDecl());
     Width = toBits(Layout.getSize());
     Align = toBits(Layout.getAlignment());
+    NaturalAlign = toBits(Layout.getNaturalAlignment());
     break;
   }
   case Type::Record:
@@ -1891,6 +1893,7 @@
           getTypeInfo(ED->getIntegerType()->getUnqualifiedDesugaredType());
       if (unsigned AttrAlign = ED->getMaxAlignment()) {
         Info.Align = AttrAlign;
+        Info.NaturalAlign = Info.Align;
         Info.AlignIsRequired = true;
       }
       return Info;
@@ -1901,6 +1904,7 @@
     const ASTRecordLayout &Layout = getASTRecordLayout(RD);
     Width = toBits(Layout.getSize());
     Align = toBits(Layout.getAlignment());
+    NaturalAlign = toBits(Layout.getNaturalAlignment());
     AlignIsRequired = RD->hasAttr<AlignedAttr>();
     break;
   }
@@ -1937,6 +1941,7 @@
       AlignIsRequired = Info.AlignIsRequired;
     }
     Width = Info.Width;
+    NaturalAlign = Info.NaturalAlign;
     break;
   }
 
@@ -1974,7 +1979,9 @@
   }
 
   assert(llvm::isPowerOf2_32(Align) && "Alignment must be power of 2");
-  return TypeInfo(Width, Align, AlignIsRequired);
+  if (NaturalAlign == 0)
+    NaturalAlign = Align;
+  return TypeInfo(Width, Align, NaturalAlign, AlignIsRequired);
 }
 
 unsigned ASTContext::getOpenMPDefaultSimdAlign(QualType T) const {
Index: include/clang/AST/RecordLayout.h
===================================================================
--- include/clang/AST/RecordLayout.h
+++ include/clang/AST/RecordLayout.h
@@ -71,6 +71,10 @@
   // Alignment - Alignment of record in characters.
   CharUnits Alignment;
 
+  // NaturalAlignment - Maximum of the alignments of the record members in
+  // characters.
+  CharUnits NaturalAlignment;
+
   /// RequiredAlignment - The required alignment of the object.  In the MS-ABI
   /// the __declspec(align()) trumps #pramga pack and must always be obeyed.
   CharUnits RequiredAlignment;
@@ -136,14 +140,16 @@
   CXXRecordLayoutInfo *CXXInfo = nullptr;
 
   ASTRecordLayout(const ASTContext &Ctx, CharUnits size, CharUnits alignment,
+                  CharUnits naturalAlignment,
                   CharUnits requiredAlignment, CharUnits datasize,
                   ArrayRef<uint64_t> fieldoffsets);
 
   using BaseOffsetsMapTy = CXXRecordLayoutInfo::BaseOffsetsMapTy;
 
   // Constructor for C++ records.
   ASTRecordLayout(const ASTContext &Ctx,
                   CharUnits size, CharUnits alignment,
+                  CharUnits naturalAlignment,
                   CharUnits requiredAlignment,
                   bool hasOwnVFPtr, bool hasExtendableVFPtr,
                   CharUnits vbptroffset,
@@ -170,6 +176,9 @@
   /// getAlignment - Get the record alignment in characters.
   CharUnits getAlignment() const { return Alignment; }
 
+  /// getNaturalAlignment - Get the natural record alignment in characters.
+  CharUnits getNaturalAlignment() const { return NaturalAlignment; }
+
   /// getSize - Get the record size in characters.
   CharUnits getSize() const { return Size; }
 
Index: include/clang/AST/ASTContext.h
===================================================================
--- include/clang/AST/ASTContext.h
+++ include/clang/AST/ASTContext.h
@@ -137,11 +137,14 @@
 struct TypeInfo {
   uint64_t Width = 0;
   unsigned Align = 0;
+  unsigned NaturalAlign = 0;
   bool AlignIsRequired : 1;
 
   TypeInfo() : AlignIsRequired(false) {}
-  TypeInfo(uint64_t Width, unsigned Align, bool AlignIsRequired)
-      : Width(Width), Align(Align), AlignIsRequired(AlignIsRequired) {}
+  TypeInfo(uint64_t Width, unsigned Align, unsigned NaturalAlign,
+           bool AlignIsRequired)
+      : Width(Width), Align(Align), NaturalAlign(NaturalAlign),
+        AlignIsRequired(AlignIsRequired) {}
 };
 
 /// \brief Holds long-lived AST nodes (such as types and decls) that can be
@@ -2043,6 +2046,15 @@
   unsigned getTypeAlign(QualType T) const { return getTypeInfo(T).Align; }
   unsigned getTypeAlign(const Type *T) const { return getTypeInfo(T).Align; }
 
+  /// \brief Return the ABI-specified natural alignment of a (complete) type \p
+  /// T, before alignment adjustments, in bits.
+  unsigned getTypeNaturalAlign(QualType T) const {
+    return getTypeInfo(T).NaturalAlign;
+  }
+  unsigned getTypeNaturalAlign(const Type *T) const {
+    return getTypeInfo(T).NaturalAlign;
+  }
+
   /// \brief Return the ABI-specified alignment of a type, in bits, or 0 if
   /// the type is incomplete and we cannot determine the alignment (for
   /// example, from alignment attributes).
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to