]> granicus.if.org Git - clang/commitdiff
Extend the ExternalASTSource interface to allow the AST source to
authorDouglas Gregor <dgregor@apple.com>
Thu, 26 Jan 2012 07:55:45 +0000 (07:55 +0000)
committerDouglas Gregor <dgregor@apple.com>
Thu, 26 Jan 2012 07:55:45 +0000 (07:55 +0000)
provide the layout of records, rather than letting Clang compute
the layout itself. LLDB provides the motivation for this feature:
because various layout-altering attributes (packed, aligned, etc.)
don't get reliably get placed into DWARF, the record layouts computed
by LLDB from the reconstructed records differ from the actual layouts,
and badness occurs. This interface lets the DWARF data drive layout,
so we don't need the attributes preserved to get the answer write.

The testing methodology for this change is fun. I've introduced a
variant of -fdump-record-layouts called -fdump-record-layouts-simple
that always has the simple C format and provides size/alignment/field
offsets. There is also a -cc1 option -foverride-record-layout=<file>
to take the output of -fdump-record-layouts-simple and parse it to
produce a set of overridden layouts, which is introduced into the AST
via a testing-only ExternalASTSource (called
LayoutOverrideSource). Each test contains a number of records to lay
out, which use various layout-changing attributes, and then dumps the
layouts. We then run the test again, using the preprocessor to
eliminate the layout-changing attributes entirely (which would give us
different layouts for the records), but supplying the
previously-computed record layouts. Finally, we diff the layouts
produced from the two runs to be sure that they are identical.

Note that this code makes the assumption that we don't *have* to
provide the offsets of bases or virtual bases to get the layout right,
because the alignment attributes don't affect it. I believe this
assumption holds, but if it does not, we can extend
LayoutOverrideSource to also provide base offset information.

Fixes the Clang side of <rdar://problem/10169539>.

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

13 files changed:
include/clang/AST/ASTContext.h
include/clang/AST/ExternalASTSource.h
include/clang/Basic/LangOptions.def
include/clang/Driver/CC1Options.td
include/clang/Frontend/FrontendOptions.h
include/clang/Frontend/LayoutOverrideSource.h [new file with mode: 0644]
lib/AST/RecordLayoutBuilder.cpp
lib/Frontend/CMakeLists.txt
lib/Frontend/CompilerInvocation.cpp
lib/Frontend/FrontendAction.cpp
lib/Frontend/LayoutOverrideSource.cpp [new file with mode: 0644]
test/CodeGen/override-layout.c [new file with mode: 0644]
test/CodeGenCXX/override-layout.cpp [new file with mode: 0644]

index 8d59de10242a2b74571e83d0cbbf0f030ad176ca..ebbbc7276eb72500c952a15018dff953528fe0ae 100644 (file)
@@ -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
index 8fea568c17b766b65d55140b530c244f9439e6c6..18a1432b706e470e04887ab2881e967040cf299a 100644 (file)
@@ -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.
   //===--------------------------------------------------------------------===//
index e6530afceb76bbd3fe564a07c095285ac99a5834..4f107e55a6d72d4c1938c28a7fc6bfb7348ece84 100644 (file)
@@ -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")
index bf7b915e3d9075b8b91dda5b5ad93fa0f122ea00..b58c105af5898eac2b9645a1d9e9d101008ac862 100644 (file)
@@ -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
 //===----------------------------------------------------------------------===//
index 080e1435033123633d9505b25df52bd0d9dbac5b..08c683e2da940b10f9e82017dbca79f8b197e2d9 100644 (file)
@@ -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 (file)
index 0000000..225efe6
--- /dev/null
@@ -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
index d094461dc3536c2e333e7f27098fe5a14c7d4030..d6f3d5fea3b57dcbd4d61968b7d8c9c50a21dc8a 100644 (file)
@@ -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";
index 40e813968a1d3243f14641677af6b2f6ea31ba8b..1148bc472135aae8770a0639a119c238019ec667 100644 (file)
@@ -27,6 +27,7 @@ add_clang_library(clangFrontend
   InitHeaderSearch.cpp
   InitPreprocessor.cpp
   LangStandards.cpp
+  LayoutOverrideSource.cpp
   LogDiagnosticPrinter.cpp
   MultiplexConsumer.cpp
   PrintPreprocessedOutput.cpp
index 7e5af12d51771312a733a8ed556c874c795de0ef..5a0117af5a96d7b33b61964f7d63b772dcd3f85c 100644 (file)
@@ -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);
index 42ba4d5a09e07f27b280036b05a4a473d4746831..5a15847aea0cbbdd698d23a4c29fc46b4c30b6a1 100644 (file)
@@ -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 (file)
index 0000000..3af2cc2
--- /dev/null
@@ -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 (file)
index 0000000..44b58f0
--- /dev/null
@@ -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;
+  unsigned int z PACKED;
+};
+
+// CHECK: Type: struct X10
+struct X10 {
+  unsigned int x[2] PACKED;
+  unsigned int y PACKED;
+  unsigned int z PACKED;
+};
+
+// CHECK: Type: struct X11
+struct PACKED X11 {
+  unsigned int x[2];
+  unsigned int y;
+  unsigned int z;
+};
+
+// CHECK: Type: struct X12
+struct PACKED X12 {
+  int x : 24;
+};
+
+// CHECK: Type: struct X13
+struct PACKED X13 {
+  signed x : 10;
+  signed y : 10;
+};
+
+// CHECK: Type: union X14
+union PACKED X14 {
+  unsigned long long x : 3;
+};
+
+// CHECK: Type: struct X15
+struct X15 {
+  unsigned x : 16;
+  unsigned y : 28 PACKED;
+};
+
+// CHECK: Type: struct X16
+struct ALIGNED16 X16 {
+  int a, b, c;
+  int x : 5;
+  int y : 29;
+};
+
+void use_structs() {
+  struct X0 x0;
+  x0.x[5] = sizeof(struct X0);
+
+  struct X1 x1;
+  x1.x[5] = sizeof(struct X1);
+
+  struct X2 x2;
+  x2.y = sizeof(struct X2);
+
+  struct X3 x3;
+  x3.y = sizeof(struct X3);
+
+  struct X4 x4;
+  x4.y = sizeof(struct X4);
+
+  struct X5 x5;
+  x5.b = sizeof(struct X5);
+
+  struct X6 x6;
+  x6.b = sizeof(struct X6);
+
+  struct X7 x7;
+  x7.x = sizeof(struct X7);
+
+  union X8 x8;
+  x8.y = sizeof(union X8);
+
+  struct X9 x9;
+  x9.y = sizeof(struct X9);
+
+  struct X10 x10;
+  x10.y = sizeof(struct X10);
+
+  struct X11 x11;
+  x11.y = sizeof(struct X11);
+
+  struct X12 x12;
+  x12.x = sizeof(struct X12);
+
+  struct X13 x13;
+  x13.x = sizeof(struct X13);
+
+  union X14 x14;
+  x14.x = sizeof(union X14);
+
+  struct X15 x15;
+  x15.x = sizeof(struct X15);
+
+  struct X16 x16;
+  x16.x = sizeof(struct X16);
+}
diff --git a/test/CodeGenCXX/override-layout.cpp b/test/CodeGenCXX/override-layout.cpp
new file mode 100644 (file)
index 0000000..c09dbef
--- /dev/null
@@ -0,0 +1,50 @@
+// RUN: %clang_cc1 -fdump-record-layouts-simple %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
+
+struct Empty1 { };
+struct Empty2 { };
+
+// CHECK: Type: struct X0
+struct X0 : public Empty1 { 
+  int x[6] PACKED; 
+};
+
+// CHECK: Type: struct X1
+struct X1 : public X0, public Empty2 { 
+  char x[13]; 
+  struct X0 y; 
+} PACKED;
+
+// CHECK: Type: struct X2
+struct PACKED X2 :  public X1, public X0, public Empty1 {
+  short x;
+  int y;
+};
+
+// CHECK: Type: struct X3
+struct PACKED X3 : virtual public X1, public X0 {
+  short x;
+  int y;
+};
+
+void use_structs() {
+  struct X0 x0;
+  x0.x[5] = sizeof(struct X0);
+
+  struct X1 x1;
+  x1.x[5] = sizeof(struct X1);
+
+  struct X2 x2;
+  x2.y = sizeof(struct X2);
+
+  struct X3 x3;
+  x3.y = sizeof(struct X3);
+}