blob: 1dcc4f84650f083464416b8e88b1f282df968f9d [file] [log] [blame]
Anders Carlsson79474332009-07-18 20:20:21 +00001//=== ASTRecordLayoutBuilder.cpp - Helper class for building record layouts ==//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#include "RecordLayoutBuilder.h"
11
12#include "clang/AST/Attr.h"
13#include "clang/AST/Decl.h"
Anders Carlsson6d9f6f32009-07-19 00:18:47 +000014#include "clang/AST/DeclCXX.h"
Anders Carlsson4f516282009-07-18 20:50:59 +000015#include "clang/AST/DeclObjC.h"
Anders Carlsson79474332009-07-18 20:20:21 +000016#include "clang/AST/Expr.h"
17#include "clang/AST/RecordLayout.h"
18#include "clang/Basic/TargetInfo.h"
19#include <llvm/Support/MathExtras.h>
20
21using namespace clang;
22
23ASTRecordLayoutBuilder::ASTRecordLayoutBuilder(ASTContext &Ctx)
Anders Carlsson6d9f6f32009-07-19 00:18:47 +000024 : Ctx(Ctx), Size(0), Alignment(8), StructPacking(0), NextOffset(0),
Anders Carlsson79474332009-07-18 20:20:21 +000025 IsUnion(false) {}
26
Anders Carlsson6d9f6f32009-07-19 00:18:47 +000027void
28ASTRecordLayoutBuilder::LayoutNonVirtualBases(const CXXRecordDecl *RD) {
29 assert(!RD->isPolymorphic() &&
30 "FIXME: We don't support polymorphic classes yet!");
31
32 for (CXXRecordDecl::base_class_const_iterator i = RD->bases_begin(),
33 e = RD->bases_end(); i != e; ++i) {
34 if (!i->isVirtual()) {
35 const CXXRecordDecl *Base =
36 cast<CXXRecordDecl>(i->getType()->getAsRecordType()->getDecl());
37 LayoutNonVirtualBase(Base);
38 }
39 }
40}
41
42void ASTRecordLayoutBuilder::LayoutNonVirtualBase(const CXXRecordDecl *RD) {
43 const ASTRecordLayout &BaseInfo = Ctx.getASTRecordLayout(RD);
44 assert(BaseInfo.getDataSize() > 0 &&
45 "FIXME: Handle empty classes.");
46
47 // FIXME: Should get the non-virtual alignment of the base.
48 unsigned BaseAlign = BaseInfo.getAlignment();
49
50 // FIXME: Should get the non-virtual size of the base.
51 uint64_t BaseSize = BaseInfo.getDataSize();
52
53 // Round up the current record size to the base's alignment boundary.
54 Size = (Size + (BaseAlign-1)) & ~(BaseAlign-1);
55
56 // Reserve space for this base.
57 Size += BaseSize;
58
59 // Remember the next available offset.
60 NextOffset = Size;
61
62 // Remember max struct/class alignment.
63 UpdateAlignment(BaseAlign);
64}
65
Anders Carlsson79474332009-07-18 20:20:21 +000066void ASTRecordLayoutBuilder::Layout(const RecordDecl *D) {
67 IsUnion = D->isUnion();
68
69 if (const PackedAttr* PA = D->getAttr<PackedAttr>())
70 StructPacking = PA->getAlignment();
71
72 if (const AlignedAttr *AA = D->getAttr<AlignedAttr>())
73 UpdateAlignment(AA->getAlignment());
Anders Carlsson6d9f6f32009-07-19 00:18:47 +000074
75 // If this is a C++ class, lay out the nonvirtual bases.
76 if (Ctx.getLangOptions().CPlusPlus)
77 LayoutNonVirtualBases(cast<CXXRecordDecl>(D));
78
Anders Carlsson118ce162009-07-18 21:48:39 +000079 LayoutFields(D);
Anders Carlsson79474332009-07-18 20:20:21 +000080
81 // Finally, round the size of the total struct up to the alignment of the
82 // struct itself.
83 FinishLayout();
84}
85
Anders Carlsson4f516282009-07-18 20:50:59 +000086void ASTRecordLayoutBuilder::Layout(const ObjCInterfaceDecl *D,
87 const ObjCImplementationDecl *Impl) {
88 if (ObjCInterfaceDecl *SD = D->getSuperClass()) {
89 const ASTRecordLayout &SL = Ctx.getASTObjCInterfaceLayout(SD);
90
91 UpdateAlignment(SL.getAlignment());
92
93 // We start laying out ivars not at the end of the superclass
94 // structure, but at the next byte following the last field.
Anders Carlsson27b50132009-07-18 21:26:44 +000095 Size = llvm::RoundUpToAlignment(SL.getDataSize(), 8);
Anders Carlsson4f516282009-07-18 20:50:59 +000096 NextOffset = Size;
97 }
98
99 if (const PackedAttr *PA = D->getAttr<PackedAttr>())
100 StructPacking = PA->getAlignment();
101
102 if (const AlignedAttr *AA = D->getAttr<AlignedAttr>())
103 UpdateAlignment(AA->getAlignment());
104
105 // Layout each ivar sequentially.
106 llvm::SmallVector<ObjCIvarDecl*, 16> Ivars;
107 Ctx.ShallowCollectObjCIvars(D, Ivars, Impl);
108 for (unsigned i = 0, e = Ivars.size(); i != e; ++i)
109 LayoutField(Ivars[i]);
110
111 // Finally, round the size of the total struct up to the alignment of the
112 // struct itself.
113 FinishLayout();
114}
115
Anders Carlsson118ce162009-07-18 21:48:39 +0000116void ASTRecordLayoutBuilder::LayoutFields(const RecordDecl *D) {
117 // Layout each field, for now, just sequentially, respecting alignment. In
118 // the future, this will need to be tweakable by targets.
119 for (RecordDecl::field_iterator Field = D->field_begin(),
120 FieldEnd = D->field_end(); Field != FieldEnd; ++Field)
121 LayoutField(*Field);
122}
123
Anders Carlsson79474332009-07-18 20:20:21 +0000124void ASTRecordLayoutBuilder::LayoutField(const FieldDecl *D) {
125 unsigned FieldPacking = StructPacking;
126 uint64_t FieldOffset = IsUnion ? 0 : Size;
127 uint64_t FieldSize;
128 unsigned FieldAlign;
129
130 // FIXME: Should this override struct packing? Probably we want to
131 // take the minimum?
132 if (const PackedAttr *PA = D->getAttr<PackedAttr>())
133 FieldPacking = PA->getAlignment();
134
135 if (const Expr *BitWidthExpr = D->getBitWidth()) {
136 // TODO: Need to check this algorithm on other targets!
137 // (tested on Linux-X86)
138 FieldSize = BitWidthExpr->EvaluateAsInt(Ctx).getZExtValue();
139
140 std::pair<uint64_t, unsigned> FieldInfo = Ctx.getTypeInfo(D->getType());
141 uint64_t TypeSize = FieldInfo.first;
142
143 // Determine the alignment of this bitfield. The packing
144 // attributes define a maximum and the alignment attribute defines
145 // a minimum.
146 // FIXME: What is the right behavior when the specified alignment
147 // is smaller than the specified packing?
148 FieldAlign = FieldInfo.second;
149 if (FieldPacking)
150 FieldAlign = std::min(FieldAlign, FieldPacking);
151 if (const AlignedAttr *AA = D->getAttr<AlignedAttr>())
152 FieldAlign = std::max(FieldAlign, AA->getAlignment());
153
154 // Check if we need to add padding to give the field the correct
155 // alignment.
156 if (FieldSize == 0 || (FieldOffset & (FieldAlign-1)) + FieldSize > TypeSize)
157 FieldOffset = (FieldOffset + (FieldAlign-1)) & ~(FieldAlign-1);
158
159 // Padding members don't affect overall alignment
160 if (!D->getIdentifier())
161 FieldAlign = 1;
162 } else {
163 if (D->getType()->isIncompleteArrayType()) {
164 // This is a flexible array member; we can't directly
165 // query getTypeInfo about these, so we figure it out here.
166 // Flexible array members don't have any size, but they
167 // have to be aligned appropriately for their element type.
168 FieldSize = 0;
169 const ArrayType* ATy = Ctx.getAsArrayType(D->getType());
170 FieldAlign = Ctx.getTypeAlign(ATy->getElementType());
171 } else if (const ReferenceType *RT = D->getType()->getAsReferenceType()) {
172 unsigned AS = RT->getPointeeType().getAddressSpace();
173 FieldSize = Ctx.Target.getPointerWidth(AS);
174 FieldAlign = Ctx.Target.getPointerAlign(AS);
175 } else {
176 std::pair<uint64_t, unsigned> FieldInfo = Ctx.getTypeInfo(D->getType());
177 FieldSize = FieldInfo.first;
178 FieldAlign = FieldInfo.second;
179 }
180
181 // Determine the alignment of this bitfield. The packing
182 // attributes define a maximum and the alignment attribute defines
183 // a minimum. Additionally, the packing alignment must be at least
184 // a byte for non-bitfields.
185 //
186 // FIXME: What is the right behavior when the specified alignment
187 // is smaller than the specified packing?
188 if (FieldPacking)
189 FieldAlign = std::min(FieldAlign, std::max(8U, FieldPacking));
190 if (const AlignedAttr *AA = D->getAttr<AlignedAttr>())
191 FieldAlign = std::max(FieldAlign, AA->getAlignment());
192
193 // Round up the current record size to the field's alignment boundary.
194 FieldOffset = (FieldOffset + (FieldAlign-1)) & ~(FieldAlign-1);
195 }
196
197 // Place this field at the current location.
198 FieldOffsets.push_back(FieldOffset);
199
200 // Reserve space for this field.
201 if (IsUnion)
202 Size = std::max(Size, FieldSize);
203 else
204 Size = FieldOffset + FieldSize;
205
206 // Remember the next available offset.
207 NextOffset = Size;
208
209 // Remember max struct/class alignment.
210 UpdateAlignment(FieldAlign);
211}
212
213void ASTRecordLayoutBuilder::FinishLayout() {
214 // In C++, records cannot be of size 0.
215 if (Ctx.getLangOptions().CPlusPlus && Size == 0)
216 Size = 8;
217 // Finally, round the size of the record up to the alignment of the
218 // record itself.
219 Size = (Size + (Alignment-1)) & ~(Alignment-1);
220}
221
222void ASTRecordLayoutBuilder::UpdateAlignment(unsigned NewAlignment) {
223 if (NewAlignment <= Alignment)
224 return;
225
226 assert(llvm::isPowerOf2_32(NewAlignment && "Alignment not a power of 2"));
227
228 Alignment = NewAlignment;
229}
230
231const ASTRecordLayout *
232ASTRecordLayoutBuilder::ComputeLayout(ASTContext &Ctx,
233 const RecordDecl *D) {
234 ASTRecordLayoutBuilder Builder(Ctx);
235
236 Builder.Layout(D);
237
Anders Carlsson6d9f6f32009-07-19 00:18:47 +0000238 bool IsPODForThePurposeOfLayout;
239 if (!Ctx.getLangOptions().CPlusPlus) {
240 // In C, all record types are POD.
241 IsPODForThePurposeOfLayout = true;
242 } else {
243 // FIXME: This is not always correct. See the part about bitfields at
244 // http://www.codesourcery.com/public/cxx-abi/abi.html#POD for more info.
245 IsPODForThePurposeOfLayout = cast<CXXRecordDecl>(D)->isPOD();
246 }
247
248 uint64_t DataSize =
249 IsPODForThePurposeOfLayout ? Builder.Size : Builder.NextOffset;
250
251 return new ASTRecordLayout(Builder.Size, Builder.Alignment, DataSize,
Anders Carlsson4f516282009-07-18 20:50:59 +0000252 Builder.FieldOffsets.data(),
253 Builder.FieldOffsets.size());
254}
255
256const ASTRecordLayout *
257ASTRecordLayoutBuilder::ComputeLayout(ASTContext &Ctx,
258 const ObjCInterfaceDecl *D,
259 const ObjCImplementationDecl *Impl) {
260 ASTRecordLayoutBuilder Builder(Ctx);
261
262 Builder.Layout(D, Impl);
263
264 return new ASTRecordLayout(Builder.Size, Builder.Alignment,
265 Builder.NextOffset,
Anders Carlsson79474332009-07-18 20:20:21 +0000266 Builder.FieldOffsets.data(),
267 Builder.FieldOffsets.size());
268}