diff options
-rw-r--r-- | include/clang/AST/ASTContext.h | 3 | ||||
-rw-r--r-- | include/clang/AST/ExternalASTSource.h | 40 | ||||
-rw-r--r-- | include/clang/Basic/LangOptions.def | 1 | ||||
-rw-r--r-- | include/clang/Driver/CC1Options.td | 5 | ||||
-rw-r--r-- | include/clang/Frontend/FrontendOptions.h | 4 | ||||
-rw-r--r-- | include/clang/Frontend/LayoutOverrideSource.h | 61 | ||||
-rw-r--r-- | lib/AST/RecordLayoutBuilder.cpp | 177 | ||||
-rw-r--r-- | lib/Frontend/CMakeLists.txt | 1 | ||||
-rw-r--r-- | lib/Frontend/CompilerInvocation.cpp | 13 | ||||
-rw-r--r-- | lib/Frontend/FrontendAction.cpp | 11 | ||||
-rw-r--r-- | lib/Frontend/LayoutOverrideSource.cpp | 206 | ||||
-rw-r--r-- | test/CodeGen/override-layout.c | 167 | ||||
-rw-r--r-- | test/CodeGenCXX/override-layout.cpp | 50 |
13 files changed, 694 insertions, 45 deletions
diff --git a/include/clang/AST/ASTContext.h b/include/clang/AST/ASTContext.h index 8d59de1024..ebbbc7276e 100644 --- a/include/clang/AST/ASTContext.h +++ b/include/clang/AST/ASTContext.h @@ -1300,7 +1300,8 @@ public: const ASTRecordLayout &getASTObjCInterfaceLayout(const ObjCInterfaceDecl *D) const; - void DumpRecordLayout(const RecordDecl *RD, raw_ostream &OS) const; + void DumpRecordLayout(const RecordDecl *RD, raw_ostream &OS, + bool Simple = false) const; /// getASTObjCImplementationLayout - Get or compute information about /// the layout of the specified Objective-C implementation. This may diff --git a/include/clang/AST/ExternalASTSource.h b/include/clang/AST/ExternalASTSource.h index 8fea568c17..18a1432b70 100644 --- a/include/clang/AST/ExternalASTSource.h +++ b/include/clang/AST/ExternalASTSource.h @@ -15,6 +15,8 @@ #define LLVM_CLANG_AST_EXTERNAL_AST_SOURCE_H #include "clang/AST/DeclBase.h" +#include "clang/AST/CharUnits.h" +#include "llvm/ADT/DenseMap.h" namespace clang { @@ -196,6 +198,44 @@ public: /// The default implementation of this method is a no-op. virtual void PrintStats(); + + /// \brief Perform layout on the given record. + /// + /// This routine allows the external AST source to provide an specific + /// layout for a record, overriding the layout that would normally be + /// constructed. It is intended for clients who receive specific layout + /// details rather than source code (such as LLDB). The client is expected + /// to fill in the field offsets, base offsets, virtual base offsets, and + /// complete object size. + /// + /// \param Record The record whose layout is being requested. + /// + /// \param Size The final size of the record, in bits. + /// + /// \param Alignment The final alignment of the record, in bits. + /// + /// \param FieldOffsets The offset of each of the fields within the record, + /// expressed in bits. All of the fields must be provided with offsets. + /// + /// \param BaseOffsets The offset of each of the direct, non-virtual base + /// classes. If any bases are not given offsets, the bases will be laid + /// out according to the ABI. + /// + /// \param VirtualBaseOffsets The offset of each of the virtual base classes + /// (either direct or not). If any bases are not given offsets, the bases will be laid + /// out according to the ABI. + /// + /// \returns true if the record layout was provided, false otherwise. + virtual bool + layoutRecordType(const RecordDecl *Record, + uint64_t &Size, uint64_t &Alignment, + llvm::DenseMap<const FieldDecl *, uint64_t> &FieldOffsets, + llvm::DenseMap<const CXXRecordDecl *, CharUnits> &BaseOffsets, + llvm::DenseMap<const CXXRecordDecl *, CharUnits> &VirtualBaseOffsets) + { + return false; + } + //===--------------------------------------------------------------------===// // Queries for performance analysis. //===--------------------------------------------------------------------===// diff --git a/include/clang/Basic/LangOptions.def b/include/clang/Basic/LangOptions.def index e6530afceb..4f107e55a6 100644 --- a/include/clang/Basic/LangOptions.def +++ b/include/clang/Basic/LangOptions.def @@ -117,6 +117,7 @@ LANGOPT(AssumeSaneOperatorNew , 1, 1, "implicit __attribute__((malloc)) for C++' BENIGN_LANGOPT(ElideConstructors , 1, 1, "C++ copy constructor elision") BENIGN_LANGOPT(CatchUndefined , 1, 0, "catching undefined behavior at run time") BENIGN_LANGOPT(DumpRecordLayouts , 1, 0, "dumping the layout of IRgen'd records") +BENIGN_LANGOPT(DumpRecordLayoutsSimple , 1, 0, "dumping the layout of IRgen'd records in a simple form") BENIGN_LANGOPT(DumpVTableLayouts , 1, 0, "dumping the layouts of emitted vtables") LANGOPT(NoConstantCFStrings , 1, 0, "no constant CoreFoundation strings") BENIGN_LANGOPT(InlineVisibilityHidden , 1, 0, "hidden default visibility for inline C++ methods") diff --git a/include/clang/Driver/CC1Options.td b/include/clang/Driver/CC1Options.td index bf7b915e3d..b58c105af5 100644 --- a/include/clang/Driver/CC1Options.td +++ b/include/clang/Driver/CC1Options.td @@ -451,6 +451,8 @@ def ftime_report : Flag<"-ftime-report">, HelpText<"Print the amount of time each phase of compilation takes">; def fdump_record_layouts : Flag<"-fdump-record-layouts">, HelpText<"Dump record layout information">; +def fdump_record_layouts_simple : Flag<"-fdump-record-layouts-simple">, + HelpText<"Dump record layout information in a simple form used for testing">; def fix_what_you_can : Flag<"-fix-what-you-can">, HelpText<"Apply fix-it advice even in the presence of unfixable errors">; def fix_only_warnings : Flag<"-fix-only-warnings">, @@ -465,6 +467,9 @@ def fixit_to_temp : Flag<"-fixit-to-temporary">, def mllvm : Separate<"-mllvm">, HelpText<"Additional arguments to forward to LLVM's option processing">; +def foverride_record_layout_EQ : Joined<"-foverride-record-layout=">, + HelpText<"Override record layouts with those in the given file">; + //===----------------------------------------------------------------------===// // Language Options //===----------------------------------------------------------------------===// diff --git a/include/clang/Frontend/FrontendOptions.h b/include/clang/Frontend/FrontendOptions.h index 080e143503..08c683e2da 100644 --- a/include/clang/Frontend/FrontendOptions.h +++ b/include/clang/Frontend/FrontendOptions.h @@ -155,6 +155,10 @@ public: /// should only be used for debugging and experimental features. std::vector<std::string> LLVMArgs; + /// \brief File name of the file that will provide record layouts + /// (in the format produced by -fdump-record-layouts). + std::string OverrideRecordLayoutsFile; + public: FrontendOptions() { DisableFree = 0; diff --git a/include/clang/Frontend/LayoutOverrideSource.h b/include/clang/Frontend/LayoutOverrideSource.h new file mode 100644 index 0000000000..225efe690b --- /dev/null +++ b/include/clang/Frontend/LayoutOverrideSource.h @@ -0,0 +1,61 @@ +//===--- LayoutOverrideSource.h --Override Record Layouts -----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_FRONTEND_LAYOUTOVERRIDESOURCE_H +#define LLVM_CLANG_FRONTEND_LAYOUTOVERRIDESOURCE_H + +#include "clang/AST/ExternalASTSource.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" + +namespace clang { + /// \brief An external AST source that overrides the layout of + /// a specified set of record types. + /// + /// This class is used only for testing the ability of external AST sources + /// to override the layout of record types. Its input is the output format + /// of the command-line argument -fdump-record-layouts. + class LayoutOverrideSource : public ExternalASTSource { + /// \brief The layout of a given record. + struct Layout { + /// \brief The size of the record. + uint64_t Size; + + /// \brief The alignment of the record. + uint64_t Align; + + /// \brief The offsets of the fields, in source order. + llvm::SmallVector<uint64_t, 8> FieldOffsets; + }; + + /// \brief The set of layouts that will be overridden. + llvm::StringMap<Layout> Layouts; + + public: + /// \brief Create a new AST source that overrides the layout of some + /// set of record types. + /// + /// The file is the result of passing -fdump-record-layouts to a file. + explicit LayoutOverrideSource(llvm::StringRef Filename); + + /// \brief If this particular record type has an overridden layout, + /// return that layout. + virtual bool + layoutRecordType(const RecordDecl *Record, + uint64_t &Size, uint64_t &Alignment, + llvm::DenseMap<const FieldDecl *, uint64_t> &FieldOffsets, + llvm::DenseMap<const CXXRecordDecl *, CharUnits> &BaseOffsets, + llvm::DenseMap<const CXXRecordDecl *, CharUnits> &VirtualBaseOffsets); + + /// \brief Dump the overridden layouts. + void dump(); + }; +} + +#endif diff --git a/lib/AST/RecordLayoutBuilder.cpp b/lib/AST/RecordLayoutBuilder.cpp index d094461dc3..d6f3d5fea3 100644 --- a/lib/AST/RecordLayoutBuilder.cpp +++ b/lib/AST/RecordLayoutBuilder.cpp @@ -558,6 +558,10 @@ protected: SmallVector<uint64_t, 16> FieldOffsets; + /// \brief Whether the external AST source has provided a layout for this + /// record. + unsigned ExternalLayout : 1; + /// Packed - Whether the record is packed or not. unsigned Packed : 1; @@ -618,11 +622,26 @@ protected: /// avoid visiting virtual bases more than once. llvm::SmallPtrSet<const CXXRecordDecl *, 4> VisitedVirtualBases; + /// \brief Externally-provided size. + uint64_t ExternalSize; + + /// \brief Externally-provided alignment. + uint64_t ExternalAlign; + + /// \brief Externally-provided field offsets. + llvm::DenseMap<const FieldDecl *, uint64_t> ExternalFieldOffsets; + + /// \brief Externally-provided direct, non-virtual base offsets. + llvm::DenseMap<const CXXRecordDecl *, CharUnits> ExternalBaseOffsets; + + /// \brief Externally-provided virtual base offsets. + llvm::DenseMap<const CXXRecordDecl *, CharUnits> ExternalVirtualBaseOffsets; + RecordLayoutBuilder(const ASTContext &Context, EmptySubobjectMap *EmptySubobjects) : Context(Context), EmptySubobjects(EmptySubobjects), Size(0), Alignment(CharUnits::One()), UnpackedAlignment(CharUnits::One()), - Packed(false), IsUnion(false), + ExternalLayout(false), Packed(false), IsUnion(false), IsMac68kAlign(false), IsMsStruct(false), UnfilledBitsInLastByte(0), MaxFieldAlignment(CharUnits::Zero()), DataSize(0), NonVirtualSize(CharUnits::Zero()), @@ -1289,8 +1308,31 @@ void RecordLayoutBuilder::LayoutVirtualBase(const BaseSubobjectInfo *Base) { CharUnits RecordLayoutBuilder::LayoutBase(const BaseSubobjectInfo *Base) { const ASTRecordLayout &Layout = Context.getASTRecordLayout(Base->Class); + + CharUnits Offset; + + // Query the external layout to see if it provides an offset. + bool HasExternalLayout = false; + if (ExternalLayout) { + llvm::DenseMap<const CXXRecordDecl *, CharUnits>::iterator Known; + if (Base->IsVirtual) { + Known = ExternalVirtualBaseOffsets.find(Base->Class); + if (Known != ExternalVirtualBaseOffsets.end()) { + Offset = Known->second; + HasExternalLayout = true; + } + } else { + Known = ExternalBaseOffsets.find(Base->Class); + if (Known != ExternalBaseOffsets.end()) { + Offset = Known->second; + HasExternalLayout = true; + } + } + } + // If we have an empty base class, try to place it at offset 0. if (Base->Class->isEmpty() && + (!HasExternalLayout || Offset == CharUnits::Zero()) && EmptySubobjects->CanPlaceBaseAtOffset(Base, CharUnits::Zero())) { setSize(std::max(getSize(), Layout.getSize())); @@ -1306,13 +1348,19 @@ CharUnits RecordLayoutBuilder::LayoutBase(const BaseSubobjectInfo *Base) { UnpackedBaseAlign = std::min(UnpackedBaseAlign, MaxFieldAlignment); } - // Round up the current record size to the base's alignment boundary. - CharUnits Offset = getDataSize().RoundUpToAlignment(BaseAlign); - - // Try to place the base. - while (!EmptySubobjects->CanPlaceBaseAtOffset(Base, Offset)) - Offset += BaseAlign; + if (!HasExternalLayout) { + // Round up the current record size to the base's alignment boundary. + Offset = getDataSize().RoundUpToAlignment(BaseAlign); + // Try to place the base. + while (!EmptySubobjects->CanPlaceBaseAtOffset(Base, Offset)) + Offset += BaseAlign; + } else { + bool Allowed = EmptySubobjects->CanPlaceBaseAtOffset(Base, Offset); + (void)Allowed; + assert(Allowed && "Base subobject externally placed at overlapping offset"); + } + if (!Base->Class->isEmpty()) { // Update the data size. setDataSize(Offset + Layout.getNonVirtualSize()); @@ -1355,6 +1403,23 @@ void RecordLayoutBuilder::InitializeLayout(const Decl *D) { if (unsigned MaxAlign = D->getMaxAlignment()) UpdateAlignment(Context.toCharUnitsFromBits(MaxAlign)); } + + // If there is an external AST source, ask it for the various offsets. + if (const RecordDecl *RD = dyn_cast<RecordDecl>(D)) + if (ExternalASTSource *External = Context.getExternalSource()) { + ExternalLayout = External->layoutRecordType(RD, + ExternalSize, + ExternalAlign, + ExternalFieldOffsets, + ExternalBaseOffsets, + ExternalVirtualBaseOffsets); + + // Update based on external alignment. + if (ExternalLayout) { + Alignment = Context.toCharUnitsFromBits(ExternalAlign); + UnpackedAlignment = Alignment; + } + } } void RecordLayoutBuilder::Layout(const RecordDecl *D) { @@ -1647,6 +1712,12 @@ void RecordLayoutBuilder::LayoutBitField(const FieldDecl *D) { uint64_t TypeSize = FieldInfo.first; unsigned FieldAlign = FieldInfo.second; + if (ExternalLayout) { + assert(ExternalFieldOffsets.find(D) != ExternalFieldOffsets.end() && + "Field does not have an external offset"); + FieldOffset = ExternalFieldOffsets[D]; + } + // This check is needed for 'long long' in -m32 mode. if (IsMsStruct && (TypeSize > FieldAlign) && (Context.hasSameType(D->getType(), @@ -1707,16 +1778,19 @@ void RecordLayoutBuilder::LayoutBitField(const FieldDecl *D) { UnpackedFieldAlign = std::min(UnpackedFieldAlign, MaxFieldAlignmentInBits); } - // Check if we need to add padding to give the field the correct alignment. - if (FieldSize == 0 || (MaxFieldAlignment.isZero() && - (FieldOffset & (FieldAlign-1)) + FieldSize > TypeSize)) - FieldOffset = llvm::RoundUpToAlignment(FieldOffset, FieldAlign); + if (!ExternalLayout) { + // Check if we need to add padding to give the field the correct alignment. + if (FieldSize == 0 || + (MaxFieldAlignment.isZero() && + (FieldOffset & (FieldAlign-1)) + FieldSize > TypeSize)) + FieldOffset = llvm::RoundUpToAlignment(FieldOffset, FieldAlign); - if (FieldSize == 0 || - (MaxFieldAlignment.isZero() && - (UnpackedFieldOffset & (UnpackedFieldAlign-1)) + FieldSize > TypeSize)) - UnpackedFieldOffset = llvm::RoundUpToAlignment(UnpackedFieldOffset, - UnpackedFieldAlign); + if (FieldSize == 0 || + (MaxFieldAlignment.isZero() && + (UnpackedFieldOffset & (UnpackedFieldAlign-1)) + FieldSize > TypeSize)) + UnpackedFieldOffset = llvm::RoundUpToAlignment(UnpackedFieldOffset, + UnpackedFieldAlign); + } // Padding members don't affect overall alignment, unless zero length bitfield // alignment is enabled. @@ -1729,8 +1803,9 @@ void RecordLayoutBuilder::LayoutBitField(const FieldDecl *D) { // Place this field at the current location. FieldOffsets.push_back(FieldOffset); - CheckFieldPadding(FieldOffset, UnpaddedFieldOffset, UnpackedFieldOffset, - UnpackedFieldAlign, FieldPacked, D); + if (!ExternalLayout) + CheckFieldPadding(FieldOffset, UnpaddedFieldOffset, UnpackedFieldOffset, + UnpackedFieldAlign, FieldPacked, D); // Update DataSize to include the last byte containing (part of) the bitfield. if (IsUnion) { @@ -1752,7 +1827,7 @@ void RecordLayoutBuilder::LayoutBitField(const FieldDecl *D) { Context.toCharUnitsFromBits(UnpackedFieldAlign)); } -void RecordLayoutBuilder::LayoutField(const FieldDecl *D) { +void RecordLayoutBuilder::LayoutField(const FieldDecl *D) { if (D->isBitField()) { LayoutBitField(D); return; @@ -1769,6 +1844,13 @@ void RecordLayoutBuilder::LayoutField(const FieldDecl *D) { CharUnits FieldSize; CharUnits FieldAlign; + if (ExternalLayout) { + assert(ExternalFieldOffsets.find(D) != ExternalFieldOffsets.end() && + "Field does not have an external offset"); + FieldOffset = Context.toCharUnitsFromBits(ExternalFieldOffsets[D]); + } + + if (D->getType()->isIncompleteArrayType()) { // This is a flexible array member; we can't directly // query getTypeInfo about these, so we figure it out here. @@ -1846,25 +1928,33 @@ void RecordLayoutBuilder::LayoutField(const FieldDecl *D) { UnpackedFieldAlign = std::min(UnpackedFieldAlign, MaxFieldAlignment); } - // Round up the current record size to the field's alignment boundary. - FieldOffset = FieldOffset.RoundUpToAlignment(FieldAlign); - UnpackedFieldOffset = - UnpackedFieldOffset.RoundUpToAlignment(UnpackedFieldAlign); - - if (!IsUnion && EmptySubobjects) { - // Check if we can place the field at this offset. - while (!EmptySubobjects->CanPlaceFieldAtOffset(D, FieldOffset)) { - // We couldn't place the field at the offset. Try again at a new offset. - FieldOffset += FieldAlign; + if (!ExternalLayout) { + // Round up the current record size to the field's alignment boundary. + FieldOffset = FieldOffset.RoundUpToAlignment(FieldAlign); + UnpackedFieldOffset = + UnpackedFieldOffset.RoundUpToAlignment(UnpackedFieldAlign); + + if (!IsUnion && EmptySubobjects) { + // Check if we can place the field at this offset. + while (!EmptySubobjects->CanPlaceFieldAtOffset(D, FieldOffset)) { + // We couldn't place the field at the offset. Try again at a new offset. + FieldOffset += FieldAlign; + } } + } else if (!IsUnion && EmptySubobjects) { + // Record the fact that we're placing a field at this offset. + bool Allowed = EmptySubobjects->CanPlaceFieldAtOffset(D, FieldOffset); + (void)Allowed; + assert(Allowed && "Externally-placed field cannot be placed here"); } - + // Place this field at the current location. FieldOffsets.push_back(Context.toBits(FieldOffset)); - CheckFieldPadding(Context.toBits(FieldOffset), UnpaddedFieldOffset, - Context.toBits(UnpackedFieldOffset), - Context.toBits(UnpackedFieldAlign), FieldPacked, D); + if (!ExternalLayout) + CheckFieldPadding(Context.toBits(FieldOffset), UnpaddedFieldOffset, + Context.toBits(UnpackedFieldOffset), + Context.toBits(UnpackedFieldAlign), FieldPacked, D); // Reserve space for this field. uint64_t FieldSizeInBits = Context.toBits(FieldSize); @@ -1936,8 +2026,9 @@ void RecordLayoutBuilder::FinishLayout(const NamedDecl *D) { void RecordLayoutBuilder::UpdateAlignment(CharUnits NewAlignment, CharUnits UnpackedNewAlignment) { - // The alignment is not modified when using 'mac68k' alignment. - if (IsMac68kAlign) + // The alignment is not modified when using 'mac68k' alignment or when + // we have an externally-supplied layout. + if (IsMac68kAlign || ExternalLayout) return; if (NewAlignment > Alignment) { @@ -2136,7 +2227,7 @@ ASTContext::getASTRecordLayout(const RecordDecl *D) const { if (getLangOptions().DumpRecordLayouts) { llvm::errs() << "\n*** Dumping AST Record Layout\n"; - DumpRecordLayout(D, llvm::errs()); + DumpRecordLayout(D, llvm::errs(), getLangOptions().DumpRecordLayoutsSimple); } return *NewEntry; @@ -2329,16 +2420,20 @@ static void DumpCXXRecordLayout(raw_ostream &OS, } void ASTContext::DumpRecordLayout(const RecordDecl *RD, - raw_ostream &OS) const { + raw_ostream &OS, + bool Simple) const { const ASTRecordLayout &Info = getASTRecordLayout(RD); if (const CXXRecordDecl *CXXRD = dyn_cast<CXXRecordDecl>(RD)) - return DumpCXXRecordLayout(OS, CXXRD, *this, CharUnits(), 0, 0, - /*IncludeVirtualBases=*/true); + if (!Simple) + return DumpCXXRecordLayout(OS, CXXRD, *this, CharUnits(), 0, 0, + /*IncludeVirtualBases=*/true); OS << "Type: " << getTypeDeclType(RD).getAsString() << "\n"; - OS << "Record: "; - RD->dump(); + if (!Simple) { + OS << "Record: "; + RD->dump(); + } OS << "\nLayout: "; OS << "<ASTRecordLayout\n"; OS << " Size:" << toBits(Info.getSize()) << "\n"; diff --git a/lib/Frontend/CMakeLists.txt b/lib/Frontend/CMakeLists.txt index 40e813968a..1148bc4721 100644 --- a/lib/Frontend/CMakeLists.txt +++ b/lib/Frontend/CMakeLists.txt @@ -27,6 +27,7 @@ add_clang_library(clangFrontend InitHeaderSearch.cpp InitPreprocessor.cpp LangStandards.cpp + LayoutOverrideSource.cpp LogDiagnosticPrinter.cpp MultiplexConsumer.cpp PrintPreprocessedOutput.cpp diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 7e5af12d51..5a0117af5a 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -551,6 +551,8 @@ static void FrontendOptsToArgs(const FrontendOptions &Opts, Res.push_back("-mllvm"); Res.push_back(Opts.LLVMArgs[i]); } + if (!Opts.OverrideRecordLayoutsFile.empty()) + Res.push_back("-foverride-record-layout=" + Opts.OverrideRecordLayoutsFile); } static void HeaderSearchOptsToArgs(const HeaderSearchOptions &Opts, @@ -747,7 +749,9 @@ static void LangOptsToArgs(const LangOptions &Opts, Res.push_back("-ffast-math"); if (Opts.Static) Res.push_back("-static-define"); - if (Opts.DumpRecordLayouts) + if (Opts.DumpRecordLayoutsSimple) + Res.push_back("-fdump-record-layouts-simple"); + else if (Opts.DumpRecordLayouts) Res.push_back("-fdump-record-layouts"); if (Opts.DumpVTableLayouts) Res.push_back("-fdump-vtable-layouts"); @@ -1410,7 +1414,8 @@ static InputKind ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args, Opts.FixOnlyWarnings = Args.hasArg(OPT_fix_only_warnings); Opts.FixAndRecompile = Args.hasArg(OPT_fixit_recompile); Opts.FixToTemporaries = Args.hasArg(OPT_fixit_to_temp); - + Opts.OverrideRecordLayoutsFile + = Args.getLastArgValue(OPT_foverride_record_layout_EQ); Opts.ARCMTAction = FrontendOptions::ARCMT_None; if (const Arg *A = Args.getLastArg(OPT_arcmt_check, OPT_arcmt_modify, @@ -1863,7 +1868,9 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK, Opts.PackStruct = Args.getLastArgIntValue(OPT_fpack_struct, 0, Diags); Opts.PICLevel = Args.getLastArgIntValue(OPT_pic_level, 0, Diags); Opts.Static = Args.hasArg(OPT_static_define); - Opts.DumpRecordLayouts = Args.hasArg(OPT_fdump_record_layouts); + Opts.DumpRecordLayoutsSimple = Args.hasArg(OPT_fdump_record_layouts_simple); + Opts.DumpRecordLayouts = Opts.DumpRecordLayoutsSimple + || Args.hasArg(OPT_fdump_record_layouts); Opts.DumpVTableLayouts = Args.hasArg(OPT_fdump_vtable_layouts); Opts.SpellChecking = !Args.hasArg(OPT_fno_spell_checking); Opts.NoBitFieldTypeAlign = Args.hasArg(OPT_fno_bitfield_type_align); diff --git a/lib/Frontend/FrontendAction.cpp b/lib/Frontend/FrontendAction.cpp index 42ba4d5a09..5a15847aea 100644 --- a/lib/Frontend/FrontendAction.cpp +++ b/lib/Frontend/FrontendAction.cpp @@ -18,6 +18,7 @@ #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendDiagnostic.h" #include "clang/Frontend/FrontendPluginRegistry.h" +#include "clang/Frontend/LayoutOverrideSource.h" #include "clang/Frontend/MultiplexConsumer.h" #include "clang/Parse/ParseAST.h" #include "clang/Serialization/ASTDeserializationListener.h" @@ -285,6 +286,16 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI, PP.getLangOptions()); } + // If there is a layout overrides file, attach an external AST source that + // provides the layouts from that file. + if (!CI.getFrontendOpts().OverrideRecordLayoutsFile.empty() && + CI.hasASTContext() && !CI.getASTContext().getExternalSource()) { + llvm::OwningPtr<ExternalASTSource> + Override(new LayoutOverrideSource( + CI.getFrontendOpts().OverrideRecordLayoutsFile)); + CI.getASTContext().setExternalSource(Override); + } + return true; // If we failed, reset state since the client will not end up calling the diff --git a/lib/Frontend/LayoutOverrideSource.cpp b/lib/Frontend/LayoutOverrideSource.cpp new file mode 100644 index 0000000000..3af2cc2bbe --- /dev/null +++ b/lib/Frontend/LayoutOverrideSource.cpp @@ -0,0 +1,206 @@ +//===--- LayoutOverrideSource.cpp --Override Record Layouts ---------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#include "clang/Frontend/LayoutOverrideSource.h" +#include "clang/AST/Decl.h" +#include "llvm/Support/raw_ostream.h" +#include <fstream> +#include <string> + +using namespace clang; + +/// \brief Parse a simple identifier. +std::string parseName(StringRef S) { + unsigned Offset = 0; + while (Offset < S.size() && + (isalpha(S[Offset]) || S[Offset] == '_' || + (Offset > 0 && isdigit(S[Offset])))) + ++Offset; + + return S.substr(0, Offset).str(); +} + +LayoutOverrideSource::LayoutOverrideSource(llvm::StringRef Filename) { + std::ifstream Input(Filename.str().c_str()); + if (!Input.is_open()) + return; + + // Parse the output of -fdump-record-layouts. + std::string CurrentType; + Layout CurrentLayout; + bool ExpectingType = false; + + while (Input.good()) { + std::string Line; + getline(Input, Line); + + StringRef LineStr(Line); + + // Determine whether the following line will start a + if (LineStr.find("*** Dumping AST Record Layout") != StringRef::npos) { + // Flush the last type/layout, if there is one. + if (!CurrentType.empty()) + Layouts[CurrentType] = CurrentLayout; + CurrentLayout = Layout(); + + ExpectingType = true; + continue; + } + + // If we're expecting a type, grab it. + if (ExpectingType) { + ExpectingType = false; + + StringRef::size_type Pos; + if ((Pos = LineStr.find("struct ")) != StringRef::npos) + LineStr = LineStr.substr(Pos + strlen("struct ")); + else if ((Pos = LineStr.find("class ")) != StringRef::npos) + LineStr = LineStr.substr(Pos + strlen("class ")); + else if ((Pos = LineStr.find("union ")) != StringRef::npos) + LineStr = LineStr.substr(Pos + strlen("union ")); + else + continue; + + // Find the name of the type. + CurrentType = parseName(LineStr); + CurrentLayout = Layout(); + continue; + } + + // Check for the size of the type. + StringRef::size_type Pos = LineStr.find("Size:"); + if (Pos != StringRef::npos) { + // Skip past the "Size:" prefix. + LineStr = LineStr.substr(Pos + strlen("Size:")); + + unsigned long long Size = 0; + (void)LineStr.getAsInteger(10, Size); + CurrentLayout.Size = Size; + continue; + } + + // Check for the alignment of the type. + Pos = LineStr.find("Alignment:"); + if (Pos != StringRef::npos) { + // Skip past the "Alignment:" prefix. + LineStr = LineStr.substr(Pos + strlen("Alignment:")); + + unsigned long long Alignment = 0; + (void)LineStr.getAsInteger(10, Alignment); + CurrentLayout.Align = Alignment; + continue; + } + + // Check for the size/alignment of the type. + Pos = LineStr.find("sizeof="); + if (Pos != StringRef::npos) { + /* Skip past the sizeof= prefix. */ + LineStr = LineStr.substr(Pos + strlen("sizeof=")); + + // Parse size. + unsigned long long Size = 0; + (void)LineStr.getAsInteger(10, Size); + CurrentLayout.Size = Size; + + Pos = LineStr.find("align="); + if (Pos != StringRef::npos) { + /* Skip past the align= prefix. */ + LineStr = LineStr.substr(Pos + strlen("align=")); + + // Parse alignment. + unsigned long long Alignment = 0; + (void)LineStr.getAsInteger(10, Alignment); + CurrentLayout.Align = Alignment; + } + + continue; + } + + // Check for the field offsets of the type. + Pos = LineStr.find("FieldOffsets: ["); + if (Pos == StringRef::npos) + continue; + + LineStr = LineStr.substr(Pos + strlen("FieldOffsets: [")); + while (!LineStr.empty() && isdigit(LineStr[0])) { + // Parse this offset. + unsigned Idx = 1; + while (Idx < LineStr.size() && isdigit(LineStr[Idx])) + ++Idx; + + unsigned long long Offset = 0; + (void)LineStr.substr(0, Idx).getAsInteger(10, Offset); + + CurrentLayout.FieldOffsets.push_back(Offset); + + // Skip over this offset, the following comma, and any spaces. + LineStr = LineStr.substr(Idx + 1); + while (!LineStr.empty() && isspace(LineStr[0])) + LineStr = LineStr.substr(1); + } + } + + // Flush the last type/layout, if there is one. + if (!CurrentType.empty()) + Layouts[CurrentType] = CurrentLayout; +} + +bool +LayoutOverrideSource::layoutRecordType(const RecordDecl *Record, + uint64_t &Size, uint64_t &Alignment, + llvm::DenseMap<const FieldDecl *, uint64_t> &FieldOffsets, + llvm::DenseMap<const CXXRecordDecl *, CharUnits> &BaseOffsets, + llvm::DenseMap<const CXXRecordDecl *, CharUnits> &VirtualBaseOffsets) +{ + // We can't override unnamed declarations. + if (!Record->getIdentifier()) + return false; + + // Check whether we have a layout for this record. + llvm::StringMap<Layout>::iterator Known = Layouts.find(Record->getName()); + if (Known == Layouts.end()) + return false; + + // Provide field layouts. + unsigned NumFields = 0; + for (RecordDecl::field_iterator F = Record->field_begin(), + FEnd = Record->field_end(); + F != FEnd; ++F, ++NumFields) { + if (NumFields >= Known->second.FieldOffsets.size()) + continue; + + FieldOffsets[*F] = Known->second.FieldOffsets[NumFields]; + } + + // Wrong number of fields. + if (NumFields != Known->second.FieldOffsets.size()) + return false; + + Size = Known->second.Size; + Alignment = Known->second.Align; + return true; +} + +void LayoutOverrideSource::dump() { + llvm::raw_ostream &OS = llvm::errs(); + for (llvm::StringMap<Layout>::iterator L = Layouts.begin(), + LEnd = Layouts.end(); + L != LEnd; ++L) { + OS << "Type: blah " << L->first() << '\n'; + OS << " Size:" << L->second.Size << '\n'; + OS << " Alignment:" << L->second.Align << '\n'; + OS << " FieldOffsets: ["; + for (unsigned I = 0, N = L->second.FieldOffsets.size(); I != N; ++I) { + if (I) + OS << ", "; + OS << L->second.FieldOffsets[I]; + } + OS << "]\n"; + } +} + diff --git a/test/CodeGen/override-layout.c b/test/CodeGen/override-layout.c new file mode 100644 index 0000000000..44b58f0778 --- /dev/null +++ b/test/CodeGen/override-layout.c @@ -0,0 +1,167 @@ +// RUN: %clang_cc1 -fdump-record-layouts %s 2> %t.layouts +// RUN: %clang_cc1 -fdump-record-layouts-simple %s > %t.before 2>&1 +// RUN: %clang_cc1 -DPACKED= -DALIGNED16= -fdump-record-layouts-simple -foverride-record-layout=%t.layouts %s > %t.after 2>&1 +// RUN: diff %t.before %t.after +// RUN: FileCheck %s < %t.after + +// If not explicitly disabled, set PACKED to the packed attribute. +#ifndef PACKED +# define PACKED __attribute__((packed)) +#endif + +// If not explicitly disabled, set ALIGNED16 to 16-byte alignment. +#ifndef ALIGNED16 +# define ALIGNED16 __attribute__((aligned(16))) +#endif + +// CHECK: Type: struct X0 +struct X0 { + int x[6] PACKED; +}; + +// CHECK: Type: struct X1 +struct X1 { + char x[13]; + struct X0 y; +} PACKED; + +// CHECK: Type: struct X2 +struct PACKED X2 { + short x; + int y; +}; + +// CHECK: Type: struct X3 +struct X3 { + short x PACKED; + int y; +}; + +#pragma pack(push,2) +// CHECK: Type: struct X4 +struct X4 { + int x; + int y; +}; +#pragma pack(pop) + +// CHECK: Type: struct X5 +struct PACKED X5 { double a[19]; signed char b; }; + +// CHECK: Type: struct X6 +struct PACKED X6 { long double a; char b; }; + +// CHECK: Type: struct X7 +typedef struct X7 { + unsigned x; + unsigned char y; +} PACKED; + +// CHECK: Type: union X8 +union X8 { + struct X7 x; + unsigned y; +} PACKED; + +// CHECK: Type: struct X9 +struct X9 { + unsigned int x[2] PACKED; + unsigned int y; |