]> granicus.if.org Git - clang/commitdiff
Add a special RecordLayoutBuilder for the Microsoft C++ ABI.
authorCharles Davis <cdavis@mines.edu>
Thu, 19 Aug 2010 00:55:19 +0000 (00:55 +0000)
committerCharles Davis <cdavis@mines.edu>
Thu, 19 Aug 2010 00:55:19 +0000 (00:55 +0000)
All it does right now is add space for two vtable pointers instead of one
when a class has both virtual methods and virtual bases. This is a requirement
of the Microsoft ABI, since it has separate vtables for methods and bases. Other
stuff will come up over time, but we'll cross those bridges when we get to
them.

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

lib/AST/RecordLayoutBuilder.cpp

index f42ba5a29fcd50fba7f7411bf2b4ea046d2316ca..c1cb98b96bc18d2771f62840eefa3232d92e3281 100644 (file)
@@ -73,22 +73,11 @@ class EmptySubobjectMap {
   /// member subobject that is empty.
   void ComputeEmptySubobjectSizes();
   
-  bool CanPlaceSubobjectAtOffset(const CXXRecordDecl *RD, 
-                                 uint64_t Offset) const;
-
   void AddSubobjectAtOffset(const CXXRecordDecl *RD, uint64_t Offset);
   
-  bool CanPlaceBaseSubobjectAtOffset(const BaseSubobjectInfo *Info,
-                                     uint64_t Offset);
   void UpdateEmptyBaseSubobjects(const BaseSubobjectInfo *Info,
                                  uint64_t Offset, bool PlacingEmptyBase);
   
-  bool CanPlaceFieldSubobjectAtOffset(const CXXRecordDecl *RD, 
-                                      const CXXRecordDecl *Class,
-                                      uint64_t Offset) const;
-  bool CanPlaceFieldSubobjectAtOffset(const FieldDecl *FD,
-                                      uint64_t Offset) const;
-  
   void UpdateEmptyFieldSubobjects(const CXXRecordDecl *RD, 
                                   const CXXRecordDecl *Class,
                                   uint64_t Offset);
@@ -100,6 +89,19 @@ class EmptySubobjectMap {
     return Offset <= MaxEmptyClassOffset;
   }
 
+protected:
+  bool CanPlaceSubobjectAtOffset(const CXXRecordDecl *RD, 
+                                 uint64_t Offset) const;
+
+  bool CanPlaceBaseSubobjectAtOffset(const BaseSubobjectInfo *Info,
+                                     uint64_t Offset);
+
+  bool CanPlaceFieldSubobjectAtOffset(const CXXRecordDecl *RD, 
+                                      const CXXRecordDecl *Class,
+                                      uint64_t Offset) const;
+  bool CanPlaceFieldSubobjectAtOffset(const FieldDecl *FD,
+                                      uint64_t Offset) const;
+
 public:
   /// This holds the size of the largest empty subobject (either a base
   /// or a member). Will be zero if the record being built doesn't contain
@@ -513,6 +515,7 @@ void EmptySubobjectMap::UpdateEmptyFieldSubobjects(const FieldDecl *FD,
 }
 
 class RecordLayoutBuilder {
+protected:
   // FIXME: Remove this and make the appropriate fields public.
   friend class clang::ASTContext;
 
@@ -623,12 +626,14 @@ class RecordLayoutBuilder {
 
   void SelectPrimaryVBase(const CXXRecordDecl *RD);
 
+  virtual uint64_t GetVirtualPointersSize(const CXXRecordDecl *RD) const;
+
   /// IdentifyPrimaryBases - Identify all virtual base classes, direct or
   /// indirect, that are primary base classes for some other direct or indirect
   /// base class.
   void IdentifyPrimaryBases(const CXXRecordDecl *RD);
 
-  bool IsNearlyEmpty(const CXXRecordDecl *RD) const;
+  virtual bool IsNearlyEmpty(const CXXRecordDecl *RD) const;
 
   /// LayoutNonVirtualBases - Determines the primary base class (if any) and
   /// lays it out. Will then proceed to lay out all non-virtual base clasess.
@@ -638,7 +643,7 @@ class RecordLayoutBuilder {
   void LayoutNonVirtualBase(const BaseSubobjectInfo *Base);
 
   void AddPrimaryVirtualBaseOffsets(const BaseSubobjectInfo *Info, 
-                                    uint64_t Offset);
+                                            uint64_t Offset);
 
   /// LayoutVirtualBases - Lays out all the virtual bases.
   void LayoutVirtualBases(const CXXRecordDecl *RD,
@@ -734,6 +739,11 @@ RecordLayoutBuilder::SelectPrimaryVBase(const CXXRecordDecl *RD) {
   }
 }
 
+uint64_t
+RecordLayoutBuilder::GetVirtualPointersSize(const CXXRecordDecl *RD) const {
+  return Context.Target.getPointerWidth(0);
+}
+
 /// DeterminePrimaryBase - Determine the primary base of the given class.
 void RecordLayoutBuilder::DeterminePrimaryBase(const CXXRecordDecl *RD) {
   // If the class isn't dynamic, it won't have a primary base.
@@ -794,7 +804,7 @@ void RecordLayoutBuilder::DeterminePrimaryBase(const CXXRecordDecl *RD) {
   assert(DataSize == 0 && "Vtable pointer must be at offset zero!");
 
   // Update the size.
-  Size += Context.Target.getPointerWidth(0);
+  Size += GetVirtualPointersSize(RD);
   DataSize = Size;
 
   // Update the alignment.
@@ -1451,6 +1461,38 @@ RecordLayoutBuilder::ComputeKeyFunction(const CXXRecordDecl *RD) {
   return 0;
 }
 
+// This class implements layout specific to the Microsoft ABI.
+class MSRecordLayoutBuilder: public RecordLayoutBuilder {
+  friend class ASTContext;
+
+  MSRecordLayoutBuilder(ASTContext& Ctx, EmptySubobjectMap *EmptySubobjects):
+    RecordLayoutBuilder(Ctx, EmptySubobjects) {}
+
+  virtual bool IsNearlyEmpty(const CXXRecordDecl *RD) const;
+  virtual uint64_t GetVirtualPointersSize(const CXXRecordDecl *RD) const;
+};
+
+bool MSRecordLayoutBuilder::IsNearlyEmpty(const CXXRecordDecl *RD) const {
+  // FIXME: Audit the corners
+  if (!RD->isDynamicClass())
+    return false;
+  const ASTRecordLayout &BaseInfo = Context.getASTRecordLayout(RD);
+  // In the Microsoft ABI, classes can have one or two vtable pointers.
+  if (BaseInfo.getNonVirtualSize() == Context.Target.getPointerWidth(0) ||
+      BaseInfo.getNonVirtualSize() == Context.Target.getPointerWidth(0) * 2)
+    return true;
+  return false;
+}
+
+uint64_t
+MSRecordLayoutBuilder::GetVirtualPointersSize(const CXXRecordDecl *RD) const {
+  // We should reserve space for two pointers if the class has both
+  // virtual functions and virtual bases.
+  if (RD->isPolymorphic() && RD->getNumVBases() > 0)
+    return 2 * Context.Target.getPointerWidth(0);
+  return Context.Target.getPointerWidth(0);
+}
+
 /// getASTRecordLayout - Get or compute information about the layout of the
 /// specified record (struct/union/class), which indicates its size and field
 /// position information.
@@ -1469,8 +1511,13 @@ const ASTRecordLayout &ASTContext::getASTRecordLayout(const RecordDecl *D) {
   if (const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(D)) {
     EmptySubobjectMap EmptySubobjects(*this, RD);
 
-    RecordLayoutBuilder Builder(*this, &EmptySubobjects);
-    Builder.Layout(RD);
+    // When compiling for Microsoft, use the special MS builder.
+    RecordLayoutBuilder *Builder;
+    if (Target.getCXXABI() == "microsoft")
+      Builder = new MSRecordLayoutBuilder(*this, &EmptySubobjects);
+    else
+      Builder = new RecordLayoutBuilder(*this, &EmptySubobjects);
+    Builder->Layout(RD);
 
     // FIXME: This is not always correct. See the part about bitfields at
     // http://www.codesourcery.com/public/cxx-abi/abi.html#POD for more info.
@@ -1479,20 +1526,21 @@ const ASTRecordLayout &ASTContext::getASTRecordLayout(const RecordDecl *D) {
 
     // FIXME: This should be done in FinalizeLayout.
     uint64_t DataSize =
-      IsPODForThePurposeOfLayout ? Builder.Size : Builder.DataSize;
+      IsPODForThePurposeOfLayout ? Builder->Size : Builder->DataSize;
     uint64_t NonVirtualSize =
-      IsPODForThePurposeOfLayout ? DataSize : Builder.NonVirtualSize;
+      IsPODForThePurposeOfLayout ? DataSize : Builder->NonVirtualSize;
 
     NewEntry =
-      new (*this) ASTRecordLayout(*this, Builder.Size, Builder.Alignment,
-                                  DataSize, Builder.FieldOffsets.data(),
-                                  Builder.FieldOffsets.size(),
+      new (*this) ASTRecordLayout(*this, Builder->Size, Builder->Alignment,
+                                  DataSize, Builder->FieldOffsets.data(),
+                                  Builder->FieldOffsets.size(),
                                   NonVirtualSize,
-                                  Builder.NonVirtualAlignment,
+                                  Builder->NonVirtualAlignment,
                                   EmptySubobjects.SizeOfLargestEmptySubobject,
-                                  Builder.PrimaryBase,
-                                  Builder.PrimaryBaseIsVirtual,
-                                  Builder.Bases, Builder.VBases);
+                                  Builder->PrimaryBase,
+                                  Builder->PrimaryBaseIsVirtual,
+                                  Builder->Bases, Builder->VBases);
+    delete Builder;
   } else {
     RecordLayoutBuilder Builder(*this, /*EmptySubobjects=*/0);
     Builder.Layout(D);