From 45372a6fdcd2b0840704569478db456822e02bae Mon Sep 17 00:00:00 2001 From: Anders Carlsson Date: Thu, 23 Jul 2009 03:17:50 +0000 Subject: [PATCH] Check in CGRecordLayoutBuilder which is a reimplementation of the record layout code. (Yay, no more packed structs unless absolutely necessary). We currently don't use the layouts being built but that will change when the new code is mature enough :) git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@76845 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang.xcodeproj/project.pbxproj | 6 + lib/CodeGen/CGRecordLayoutBuilder.cpp | 272 ++++++++++++++++++++++++++ lib/CodeGen/CGRecordLayoutBuilder.h | 128 ++++++++++++ lib/CodeGen/CodeGenTypes.cpp | 28 ++- lib/CodeGen/CodeGenTypes.h | 7 +- 5 files changed, 429 insertions(+), 12 deletions(-) create mode 100644 lib/CodeGen/CGRecordLayoutBuilder.cpp create mode 100644 lib/CodeGen/CGRecordLayoutBuilder.h diff --git a/clang.xcodeproj/project.pbxproj b/clang.xcodeproj/project.pbxproj index 24f704aee3..137f5e4423 100644 --- a/clang.xcodeproj/project.pbxproj +++ b/clang.xcodeproj/project.pbxproj @@ -43,6 +43,7 @@ 1ABC36940C7A4BDC006DB0AB /* CGBuiltin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1ABC36930C7A4BDC006DB0AB /* CGBuiltin.cpp */; }; 1ADF47AF0F782C3200E48A8A /* SemaTemplateInstantiateDecl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1ADF47AE0F782C3200E48A8A /* SemaTemplateInstantiateDecl.cpp */; }; 1AFEF4070F8A6B2300476F2B /* clang-cc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1AFEF4050F8A6B2300476F2B /* clang-cc.cpp */; }; + 1AFF8AE31012BFC900D248DA /* CGRecordLayoutBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1AFF8AE11012BFC900D248DA /* CGRecordLayoutBuilder.cpp */; }; 3507E4C20E27FE2D00FB7B57 /* CheckObjCInstMethSignature.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3507E4C10E27FE2D00FB7B57 /* CheckObjCInstMethSignature.cpp */; }; 352246E70F5C6BE000D0D279 /* HTMLDiagnostics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 352246E10F5C6BE000D0D279 /* HTMLDiagnostics.cpp */; }; 352246E80F5C6BE000D0D279 /* InitHeaderSearch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 352246E20F5C6BE000D0D279 /* InitHeaderSearch.cpp */; }; @@ -368,6 +369,8 @@ 1ABC36930C7A4BDC006DB0AB /* CGBuiltin.cpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.cpp.cpp; name = CGBuiltin.cpp; path = lib/CodeGen/CGBuiltin.cpp; sourceTree = ""; tabWidth = 2; }; 1ADF47AE0F782C3200E48A8A /* SemaTemplateInstantiateDecl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.cpp.cpp; name = SemaTemplateInstantiateDecl.cpp; path = lib/Sema/SemaTemplateInstantiateDecl.cpp; sourceTree = ""; tabWidth = 2; }; 1AFEF4050F8A6B2300476F2B /* clang-cc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.cpp.cpp; name = "clang-cc.cpp"; path = "tools/clang-cc/clang-cc.cpp"; sourceTree = ""; tabWidth = 2; }; + 1AFF8AE11012BFC900D248DA /* CGRecordLayoutBuilder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.cpp.cpp; name = CGRecordLayoutBuilder.cpp; path = lib/CodeGen/CGRecordLayoutBuilder.cpp; sourceTree = ""; tabWidth = 2; }; + 1AFF8AE21012BFC900D248DA /* CGRecordLayoutBuilder.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.h; name = CGRecordLayoutBuilder.h; path = lib/CodeGen/CGRecordLayoutBuilder.h; sourceTree = ""; tabWidth = 2; }; 3507E4C10E27FE2D00FB7B57 /* CheckObjCInstMethSignature.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CheckObjCInstMethSignature.cpp; path = lib/Analysis/CheckObjCInstMethSignature.cpp; sourceTree = ""; }; 352246E10F5C6BE000D0D279 /* HTMLDiagnostics.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = HTMLDiagnostics.cpp; path = lib/Frontend/HTMLDiagnostics.cpp; sourceTree = ""; }; 352246E20F5C6BE000D0D279 /* InitHeaderSearch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = InitHeaderSearch.cpp; path = lib/Frontend/InitHeaderSearch.cpp; sourceTree = ""; }; @@ -1110,6 +1113,8 @@ DE38CD4E0D794CF900A273B6 /* CGObjCRuntime.h */, DE38CD4F0D794D0100A273B6 /* CGObjCGNU.cpp */, 3552E7580E520DD7003A8CA5 /* CGObjCMac.cpp */, + 1AFF8AE11012BFC900D248DA /* CGRecordLayoutBuilder.cpp */, + 1AFF8AE21012BFC900D248DA /* CGRecordLayoutBuilder.h */, DE4772F90C10EAE5002239E8 /* CGStmt.cpp */, 35475B230E7997680000BFE4 /* CGValue.h */, DE928B800C0A615B00231DA4 /* CodeGenFunction.h */, @@ -1697,6 +1702,7 @@ 1A14D3A70FD78A3F00DA2835 /* DeclPrinter.cpp in Sources */, DE37252E0FE481AD00CF2CC2 /* Builtins.cpp in Sources */, 1AA1D91810125DE30078DEBC /* RecordLayoutBuilder.cpp in Sources */, + 1AFF8AE31012BFC900D248DA /* CGRecordLayoutBuilder.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/lib/CodeGen/CGRecordLayoutBuilder.cpp b/lib/CodeGen/CGRecordLayoutBuilder.cpp new file mode 100644 index 0000000000..6a95566247 --- /dev/null +++ b/lib/CodeGen/CGRecordLayoutBuilder.cpp @@ -0,0 +1,272 @@ +//===--- CGRecordLayoutBuilder.cpp - Record builder helper ------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This is a helper class used to build CGRecordLayout objects and LLVM types. +// +//===----------------------------------------------------------------------===// + +#include "CGRecordLayoutBuilder.h" + +#include "clang/AST/ASTContext.h" +#include "clang/AST/Attr.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/Expr.h" +#include "clang/AST/RecordLayout.h" +#include "CodeGenTypes.h" +#include "llvm/DerivedTypes.h" +#include "llvm/Target/TargetData.h" + + +using namespace clang; +using namespace CodeGen; + +void CGRecordLayoutBuilder::Layout(const RecordDecl *D) { + if (const PackedAttr* PA = D->getAttr()) + StructPacking = PA->getAlignment(); + + if (LayoutFields(D)) + return; + + assert(!StructPacking && + "FIXME: Were not able to lay out a struct with #pragma pack!"); + + // We weren't able to layout the struct. Try again with a packed struct + StructPacking = 1; + AlignmentAsLLVMStruct = 1; + FieldTypes.clear(); + FieldInfos.clear(); + LLVMFields.clear(); + LLVMBitFields.clear(); + + LayoutFields(D); +} + +void CGRecordLayoutBuilder::LayoutBitField(const FieldDecl *D, + uint64_t FieldOffset) { + uint64_t FieldSize = + D->getBitWidth()->EvaluateAsInt(Types.getContext()).getZExtValue(); + + if (FieldSize == 0) + return; + + uint64_t NextFieldOffset = getNextFieldOffsetInBytes() * 8; + unsigned NumBytesToAppend; + + if (FieldOffset < NextFieldOffset) { + assert(BitsAvailableInLastField && "Bitfield size mismatch!"); + assert(!FieldInfos.empty() && "Field infos can't be empty!"); + + // The bitfield begins in the previous bit-field. + NumBytesToAppend = + llvm::RoundUpToAlignment(FieldSize - BitsAvailableInLastField, 8) / 8; + } else { + assert(FieldOffset % 8 == 0 && "Field offset not aligned correctly"); + + // Append padding if necessary. + AppendBytes((FieldOffset - NextFieldOffset) / 8); + + NumBytesToAppend = + llvm::RoundUpToAlignment(FieldSize, 8) / 8; + + assert(NumBytesToAppend && "No bytes to append!"); + } + + const llvm::Type *Ty = Types.ConvertTypeForMemRecursive(D->getType()); + uint64_t TypeSizeInBits = getTypeSizeInBytes(Ty) * 8; + + LLVMFields.push_back(LLVMFieldInfo(D, FieldOffset / TypeSizeInBits)); + LLVMBitFields.push_back(LLVMBitFieldInfo(D, FieldOffset % TypeSizeInBits, + FieldSize)); + + AppendBytes(NumBytesToAppend); + + if (!NumBytesToAppend) + BitsAvailableInLastField -= FieldSize; + else + BitsAvailableInLastField = NumBytesToAppend * 8 - FieldSize; +} + +bool CGRecordLayoutBuilder::LayoutField(const FieldDecl *D, + uint64_t FieldOffset) { + unsigned FieldPacking = StructPacking; + + // FIXME: Should this override struct packing? Probably we want to + // take the minimum? + if (const PackedAttr *PA = D->getAttr()) + FieldPacking = PA->getAlignment(); + + // If the field is packed, then we need a packed struct. + if (!StructPacking && FieldPacking) + return false; + + if (D->isBitField()) { + // We must use packed structs for unnamed bit fields since they + // don't affect the struct alignment. + if (!StructPacking && !D->getDeclName()) + return false; + + LayoutBitField(D, FieldOffset); + return true; + } + + const llvm::Type *Ty = Types.ConvertTypeForMemRecursive(D->getType()); + + // Check if the field is aligned. + if (const AlignedAttr *PA = D->getAttr()) { + unsigned FieldAlign = PA->getAlignment(); + + if (!StructPacking && getTypeAlignment(Ty) > FieldAlign) + return false; + } + + assert(FieldOffset % 8 == 0 && "FieldOffset is not on a byte boundary!"); + + uint64_t FieldOffsetInBytes = FieldOffset / 8; + + // Append padding if necessary. + AppendPadding(FieldOffsetInBytes, Ty); + + uint64_t FieldSizeInBytes = getTypeSizeInBytes(Ty); + + // Now append the field. + LLVMFields.push_back(LLVMFieldInfo(D, FieldTypes.size())); + AppendField(FieldOffsetInBytes, FieldSizeInBytes, Ty); + + return true; +} + +bool CGRecordLayoutBuilder::LayoutFields(const RecordDecl *D) { + assert(!D->isUnion() && "Can't call LayoutFields on a union!"); + + const ASTRecordLayout &Layout = + Types.getContext().getASTRecordLayout(D); + + unsigned FieldNo = 0; + for (RecordDecl::field_iterator Field = D->field_begin(), + FieldEnd = D->field_end(); Field != FieldEnd; ++Field, ++FieldNo) { + if (!LayoutField(*Field, Layout.getFieldOffset(FieldNo))) { + assert(!StructPacking && + "Could not layout fields even with a packed LLVM struct!"); + return false; + } + } + + // Append tail padding if necessary. + if (Layout.getSize() / 8 > getNextFieldOffsetInBytes()) + AppendPadding(Layout.getSize() / 8, AlignmentAsLLVMStruct); + + return true; +} + +void CGRecordLayoutBuilder::AppendField(uint64_t FieldOffsetInBytes, + uint64_t FieldSizeInBytes, + const llvm::Type *FieldTy) { + AlignmentAsLLVMStruct = std::max(AlignmentAsLLVMStruct, + getTypeAlignment(FieldTy)); + + FieldTypes.push_back(FieldTy); + FieldInfos.push_back(FieldInfo(FieldOffsetInBytes, FieldSizeInBytes)); + + BitsAvailableInLastField = 0; +} + +void +CGRecordLayoutBuilder::AppendPadding(uint64_t FieldOffsetInBytes, + const llvm::Type *FieldTy) { + AppendPadding(FieldOffsetInBytes, getTypeAlignment(FieldTy)); +} + +void CGRecordLayoutBuilder::AppendPadding(uint64_t FieldOffsetInBytes, + unsigned FieldAlignment) { + uint64_t NextFieldOffsetInBytes = getNextFieldOffsetInBytes(); + assert(NextFieldOffsetInBytes <= FieldOffsetInBytes && + "Incorrect field layout!"); + + // Round up the field offset to the alignment of the field type. + uint64_t AlignedNextFieldOffsetInBytes = + llvm::RoundUpToAlignment(NextFieldOffsetInBytes, FieldAlignment); + + if (AlignedNextFieldOffsetInBytes < FieldOffsetInBytes) { + // Even with alignment, the field offset is not at the right place, + // insert padding. + uint64_t PaddingInBytes = FieldOffsetInBytes - NextFieldOffsetInBytes; + + AppendBytes(PaddingInBytes); + } +} + +void CGRecordLayoutBuilder::AppendBytes(uint64_t NumBytes) { + if (NumBytes == 0) + return; + + const llvm::Type *Ty = llvm::Type::Int8Ty; + if (NumBytes > 1) { + // FIXME: Use a VMContext. + Ty = llvm::ArrayType::get(Ty, NumBytes); + } + + // Append the padding field + AppendField(getNextFieldOffsetInBytes(), NumBytes, Ty); +} + +uint64_t CGRecordLayoutBuilder::getNextFieldOffsetInBytes() const { + if (FieldInfos.empty()) + return 0; + + const FieldInfo &LastInfo = FieldInfos.back(); + return LastInfo.OffsetInBytes + LastInfo.SizeInBytes; +} + +unsigned CGRecordLayoutBuilder::getTypeAlignment(const llvm::Type *Ty) const { + if (StructPacking) { + assert(StructPacking == 1 && "FIXME: What if StructPacking is not 1 here"); + return 1; + } + + return Types.getTargetData().getABITypeAlignment(Ty); +} + +uint64_t CGRecordLayoutBuilder::getTypeSizeInBytes(const llvm::Type *Ty) const { + return Types.getTargetData().getTypeAllocSize(Ty); +} + +CGRecordLayout * +CGRecordLayoutBuilder::ComputeLayout(CodeGenTypes &Types, + const RecordDecl *D) { + CGRecordLayoutBuilder Builder(Types); + + if (D->isUnion()) + return 0; + + Builder.Layout(D); + + // FIXME: Once this works well enough, enable it. + return 0; + + // FIXME: Use a VMContext. + const llvm::Type *Ty = llvm::StructType::get(Builder.FieldTypes, + Builder.StructPacking); + + // Add all the field numbers. + for (unsigned i = 0, e = Builder.LLVMFields.size(); i != e; ++i) { + const FieldDecl *FD = Builder.LLVMFields[i].first; + unsigned FieldNo = Builder.LLVMFields[i].second; + + Types.addFieldInfo(FD, FieldNo); + } + + // Add bitfield info. + for (unsigned i = 0, e = Builder.LLVMBitFields.size(); i != e; ++i) { + const LLVMBitFieldInfo &Info = Builder.LLVMBitFields[i]; + + Types.addBitFieldInfo(Info.FD, Info.Start, Info.Size); + } + + return new CGRecordLayout(Ty, llvm::SmallSet()); +} diff --git a/lib/CodeGen/CGRecordLayoutBuilder.h b/lib/CodeGen/CGRecordLayoutBuilder.h new file mode 100644 index 0000000000..ac2f53c508 --- /dev/null +++ b/lib/CodeGen/CGRecordLayoutBuilder.h @@ -0,0 +1,128 @@ +//===--- CGRecordLayoutBuilder.h - Record builder helper --------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This is a helper class used to build CGRecordLayout objects and LLVM types. +// +//===----------------------------------------------------------------------===// + +#ifndef CLANG_CODEGEN_CGRECORDLAYOUTBUILDER_H +#define CLANG_CODEGEN_CGRECORDLAYOUTBUILDER_H + +#include "llvm/ADT/SmallVector.h" +#include "llvm/Support/DataTypes.h" +#include + +namespace llvm { + class Type; +} + +namespace clang { + class FieldDecl; + class RecordDecl; + +namespace CodeGen { + class CGRecordLayout; + class CodeGenTypes; + +class CGRecordLayoutBuilder { + CodeGenTypes &Types; + + /// StructPacking - Will be 0 if this struct is not packed. If it is packed, + /// it will have the packing alignment in bits. + /// + unsigned StructPacking; + + /// AlignmentAsLLVMStruct - Will contain the maximum alignment of all the + /// LLVM types. + unsigned AlignmentAsLLVMStruct; + + /// BitsAvailableInLastField - If a bit field spans only part of a LLVM field, + /// this will have the number of bits still available in the field. + char BitsAvailableInLastField; + + /// FieldTypes - Holds the LLVM types that the struct is created from. + std::vector FieldTypes; + + /// FieldInfo - Holds size and offset information about a field. + /// FIXME: I think we can get rid of this. + struct FieldInfo { + FieldInfo(uint64_t OffsetInBytes, uint64_t SizeInBytes) + : OffsetInBytes(OffsetInBytes), SizeInBytes(SizeInBytes) { } + + const uint64_t OffsetInBytes; + const uint64_t SizeInBytes; + }; + llvm::SmallVector FieldInfos; + + /// LLVMFieldInfo - Holds a field and its corresponding LLVM field number. + typedef std::pair LLVMFieldInfo; + llvm::SmallVector LLVMFields; + + /// LLVMBitFieldInfo - Holds location and size information about a bit field. + struct LLVMBitFieldInfo { + LLVMBitFieldInfo(const FieldDecl *FD, unsigned Start, unsigned Size) + : FD(FD), Start(Start), Size(Size) { } + + const FieldDecl *FD; + + unsigned Start; + unsigned Size; + }; + llvm::SmallVector LLVMBitFields; + + CGRecordLayoutBuilder(CodeGenTypes &Types) + : Types(Types), StructPacking(0), AlignmentAsLLVMStruct(1) + , BitsAvailableInLastField(0) { } + + /// Layout - Will layout a RecordDecl. + void Layout(const RecordDecl *D); + + /// LayoutField - try to layout all fields in the record decl. + /// Returns false if the operation failed because the struct is not packed. + bool LayoutFields(const RecordDecl *D); + + /// LayoutField - layout a single field. Returns false if the operation failed + /// because the current struct is not packed. + bool LayoutField(const FieldDecl *D, uint64_t FieldOffset); + + /// LayoutBitField - layout a single bit field. + void LayoutBitField(const FieldDecl *D, uint64_t FieldOffset); + + /// AppendField - Appends a field with the given offset size and type. + void AppendField(uint64_t FieldOffsetInBytes, uint64_t FieldSizeInBytes, + const llvm::Type *FieldTy); + + /// AppendPadding - Appends enough padding bytes so that the total struct + /// size matches the alignment of the passed in type. + void AppendPadding(uint64_t FieldOffsetInBytes, const llvm::Type *FieldTy); + + /// AppendPadding - Appends enough padding bytes so that the total + /// struct size is a multiple of the field alignment. + void AppendPadding(uint64_t FieldOffsetInBytes, unsigned FieldAlignment); + + /// AppendBytes - Append a given number of bytes to the record. + void AppendBytes(uint64_t NumBytes); + + /// getNextFieldOffsetInBytes - returns where the next field offset is. + uint64_t getNextFieldOffsetInBytes() const; + + unsigned getTypeAlignment(const llvm::Type *Ty) const; + uint64_t getTypeSizeInBytes(const llvm::Type *Ty) const; + +public: + /// ComputeLayout - Return the right record layout for a given record decl. + static CGRecordLayout *ComputeLayout(CodeGenTypes &Types, + const RecordDecl *D); +}; + +} // end namespace CodeGen +} // end namespace clang + + +#endif diff --git a/lib/CodeGen/CodeGenTypes.cpp b/lib/CodeGen/CodeGenTypes.cpp index 41d2ba24fe..59463fa24f 100644 --- a/lib/CodeGen/CodeGenTypes.cpp +++ b/lib/CodeGen/CodeGenTypes.cpp @@ -21,6 +21,7 @@ #include "llvm/Target/TargetData.h" #include "CGCall.h" +#include "CGRecordLayoutBuilder.h" using namespace clang; using namespace CodeGen; @@ -456,21 +457,30 @@ const llvm::Type *CodeGenTypes::ConvertTagDeclType(const TagDecl *TD) { ResultType = llvm::StructType::get(std::vector()); } else { // Layout fields. - RecordOrganizer RO(*this, *RD); + CGRecordLayout *Layout = + CGRecordLayoutBuilder::ComputeLayout(*this, RD); - if (TD->isStruct() || TD->isClass()) - RO.layoutStructFields(Context.getASTRecordLayout(RD)); - else { - assert(TD->isUnion() && "unknown tag decl kind!"); - RO.layoutUnionFields(Context.getASTRecordLayout(RD)); + if (!Layout) { + // Layout fields. + RecordOrganizer RO(*this, *RD); + + if (TD->isStruct() || TD->isClass()) + RO.layoutStructFields(Context.getASTRecordLayout(RD)); + else { + assert(TD->isUnion() && "unknown tag decl kind!"); + RO.layoutUnionFields(Context.getASTRecordLayout(RD)); + } + + Layout = new CGRecordLayout(RO.getLLVMType(), + RO.getPaddingFields()); } // Get llvm::StructType. const Type *Key = Context.getTagDeclType(const_cast(TD)).getTypePtr(); - CGRecordLayouts[Key] = new CGRecordLayout(RO.getLLVMType(), - RO.getPaddingFields()); - ResultType = RO.getLLVMType(); + + CGRecordLayouts[Key] = Layout; + ResultType = Layout->getLLVMType(); } // Refine our Opaque type to ResultType. This can invalidate ResultType, so diff --git a/lib/CodeGen/CodeGenTypes.h b/lib/CodeGen/CodeGenTypes.h index b72d8e9201..925b933fca 100644 --- a/lib/CodeGen/CodeGenTypes.h +++ b/lib/CodeGen/CodeGenTypes.h @@ -52,14 +52,15 @@ namespace CodeGen { class CGRecordLayout { CGRecordLayout(); // DO NOT IMPLEMENT public: - CGRecordLayout(llvm::Type *T, llvm::SmallSet &PF) + CGRecordLayout(const llvm::Type *T, + const llvm::SmallSet &PF) : STy(T), PaddingFields(PF) { // FIXME : Collect info about fields that requires adjustments // (i.e. fields that do not directly map to llvm struct fields.) } /// getLLVMType - Return llvm type associated with this record. - llvm::Type *getLLVMType() const { + const llvm::Type *getLLVMType() const { return STy; } @@ -72,7 +73,7 @@ namespace CodeGen { } private: - llvm::Type *STy; + const llvm::Type *STy; llvm::SmallSet PaddingFields; };