]> granicus.if.org Git - clang/commitdiff
Don't build the entire vtable when all we want is the index of a virtual method.
authorAnders Carlsson <andersca@mac.com>
Fri, 27 Nov 2009 20:47:55 +0000 (20:47 +0000)
committerAnders Carlsson <andersca@mac.com>
Fri, 27 Nov 2009 20:47:55 +0000 (20:47 +0000)
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@90017 91177308-0d34-0410-b5e6-96231b3b80d8

lib/CodeGen/CGCXX.cpp
lib/CodeGen/CGExprConstant.cpp
lib/CodeGen/CGVtable.cpp
lib/CodeGen/CGVtable.h

index a848b1269b9cb051f85f939398f9790fbe491e48..9cd72fe215259e7eefbf4bfcbdd0018889de4493 100644 (file)
@@ -1055,7 +1055,7 @@ CodeGenFunction::GetVirtualCXXBaseClassOffset(llvm::Value *This,
   return VBaseOffset;
 }
 
-static llvm::Value *BuildVirtualCall(CodeGenFunction &CGF, int64_t VtableIndex, 
+static llvm::Value *BuildVirtualCall(CodeGenFunction &CGF, uint64_t VtableIndex, 
                                      llvm::Value *This, const llvm::Type *Ty) {
   Ty = Ty->getPointerTo()->getPointerTo()->getPointerTo();
   
@@ -1071,7 +1071,7 @@ llvm::Value *
 CodeGenFunction::BuildVirtualCall(const CXXMethodDecl *MD, llvm::Value *This,
                                   const llvm::Type *Ty) {
   MD = MD->getCanonicalDecl();
-  int64_t VtableIndex = CGM.getVtableInfo().getMethodVtableIndex(MD);
+  uint64_t VtableIndex = CGM.getVtableInfo().getMethodVtableIndex(MD);
   
   return ::BuildVirtualCall(*this, VtableIndex, This, Ty);
 }
@@ -1080,7 +1080,7 @@ llvm::Value *
 CodeGenFunction::BuildVirtualCall(const CXXDestructorDecl *DD, CXXDtorType Type, 
                                   llvm::Value *&This, const llvm::Type *Ty) {
   DD = cast<CXXDestructorDecl>(DD->getCanonicalDecl());
-  int64_t VtableIndex = 
+  uint64_t VtableIndex = 
     CGM.getVtableInfo().getMethodVtableIndex(GlobalDecl(DD, Type));
 
   return ::BuildVirtualCall(*this, VtableIndex, This, Ty);
index d0475dd8df1f832eac90632acd4b33e0283e73b8..54e3fa4744d5549171ecec900beaa2a630f3f60e 100644 (file)
@@ -413,9 +413,10 @@ public:
     
     // Get the function pointer (or index if this is a virtual function).
     if (MD->isVirtual()) {
-      int64_t Index = CGM.getVtableInfo().getMethodVtableIndex(MD);
+      uint64_t Index = CGM.getVtableInfo().getMethodVtableIndex(MD);
       
-      Values[0] = llvm::ConstantInt::get(PtrDiffTy, Index + 1);
+      // The pointer is 1 + the virtual table offset in bytes.
+      Values[0] = llvm::ConstantInt::get(PtrDiffTy, (Index * 8) + 1);
     } else {
       llvm::Constant *FuncPtr = CGM.GetAddrOfFunction(MD);
 
index fb3e6be3e6b233e7d0e249dd73099b9a8c8fa8e8..4669515a4e7ebdf8028d5f381a21c41cc88d585b 100644 (file)
@@ -14,6 +14,7 @@
 #include "CodeGenModule.h"
 #include "CodeGenFunction.h"
 
+#include "clang/AST/CXXInheritance.h"
 #include "clang/AST/RecordLayout.h"
 #include "llvm/ADT/DenseSet.h"
 #include <cstdio>
@@ -781,24 +782,243 @@ VtableBuilder::Index_t VtableBuilder::VBlookup(CXXRecordDecl *D,
   return CGM.getVtableInfo().getVirtualBaseOffsetIndex(D, B);
 }
 
-int64_t CGVtableInfo::getMethodVtableIndex(GlobalDecl GD) {
+/// TypeConversionRequiresAdjustment - Returns whether conversion from a 
+/// derived type to a base type requires adjustment.
+static bool
+TypeConversionRequiresAdjustment(ASTContext &Ctx,
+                                 const CXXRecordDecl *DerivedDecl,
+                                 const CXXRecordDecl *BaseDecl) {
+  CXXBasePaths Paths(/*FindAmbiguities=*/false,
+                     /*RecordPaths=*/true, /*DetectVirtual=*/true);
+  if (!const_cast<CXXRecordDecl *>(DerivedDecl)->
+      isDerivedFrom(const_cast<CXXRecordDecl *>(BaseDecl), Paths)) {
+    assert(false && "Class must be derived from the passed in base class!");
+    return false;
+  }
+  
+  const CXXBasePath &Path = Paths.front();
+  
+  size_t Start = 0, End = Path.size();
+  
+  // Check if we have a virtual base.
+  if (const RecordType *RT = Paths.getDetectedVirtual()) {
+    const CXXRecordDecl *VirtualBase = cast<CXXRecordDecl>(RT->getDecl());
+    assert(VirtualBase->isCanonicalDecl() && "Must have canonical decl!");
+    
+    // Check the virtual base class offset.
+    const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(DerivedDecl);
+
+    if (Layout.getVBaseClassOffset(VirtualBase) != 0) {
+      // This requires an adjustment.
+      return true;
+    }
+    
+    // Now ignore all the path elements up to the virtual base.
+    // FIXME: It would be nice if CXXBasePaths could return an index to the
+    // CXXElementSpecifier that corresponded to the virtual base.
+    for (; Start != End; ++Start) {
+      const CXXBasePathElement& Element = Path[Start];
+      
+      if (Element.Class == VirtualBase)
+        break;
+    }
+  }
+
+  for (; Start != End; ++Start) {
+    const CXXBasePathElement &Element = Path[Start];
+
+    // Check the base class offset.
+    const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(Element.Class);
+    
+    const RecordType *BaseType = Element.Base->getType()->getAs<RecordType>();
+    const CXXRecordDecl *Base = cast<CXXRecordDecl>(BaseType->getDecl());
+
+    if (Layout.getBaseClassOffset(Base) != 0) {
+      // This requires an adjustment.
+      return true;
+    }
+  }
+  
+  return false;
+}
+
+static bool 
+TypeConversionRequiresAdjustment(ASTContext &Ctx,
+                                 QualType DerivedType, QualType BaseType) {
+  // Canonicalize the types.
+  QualType CanDerivedType = Ctx.getCanonicalType(DerivedType);
+  QualType CanBaseType = Ctx.getCanonicalType(BaseType);
+  
+  assert(CanDerivedType->getTypeClass() == CanBaseType->getTypeClass() && 
+         "Types must have same type class!");
+  
+  if (CanDerivedType == CanBaseType) {
+    // No adjustment needed.
+    return false;
+  }
+
+  if (const ReferenceType *RT = dyn_cast<ReferenceType>(CanDerivedType)) {
+    CanDerivedType = RT->getPointeeType();
+    CanBaseType = cast<ReferenceType>(CanBaseType)->getPointeeType();
+  } else if (const PointerType *PT = dyn_cast<PointerType>(CanDerivedType)) {
+    CanDerivedType = PT->getPointeeType();
+    CanBaseType = cast<PointerType>(CanBaseType)->getPointeeType();
+  } else {
+    assert(false && "Unexpected return type!");
+  }
+
+  if (CanDerivedType == CanBaseType) {
+    // No adjustment needed.
+    return false;
+  }
+  
+  const CXXRecordDecl *DerivedDecl = 
+    cast<CXXRecordDecl>(cast<RecordType>(CanDerivedType)->getDecl());
+
+  const CXXRecordDecl *BaseDecl = 
+    cast<CXXRecordDecl>(cast<RecordType>(CanBaseType)->getDecl());
+
+  return TypeConversionRequiresAdjustment(Ctx, DerivedDecl, BaseDecl);
+}
+
+void CGVtableInfo::ComputeMethodVtableIndices(const CXXRecordDecl *RD) {
+  
+  // Itanium C++ ABI 2.5.2:
+  // The order of the virtual function pointers in a virtual table is the 
+  // order of declaration of the corresponding member functions in the class.
+  //
+  // There is an entry for any virtual function declared in a class, 
+  // whether it is a new function or overrides a base class function, 
+  // unless it overrides a function from the primary base, and conversion
+  // between their return types does not require an adjustment. 
+
+  int64_t CurrentIndex = 0;
+  
+  const ASTRecordLayout &Layout = CGM.getContext().getASTRecordLayout(RD);
+  const CXXRecordDecl *PrimaryBase = Layout.getPrimaryBase();
+  
+  if (PrimaryBase) {
+    assert(PrimaryBase->isCanonicalDecl() && 
+           "Should have the canonical decl of the primary base!");
+
+    // Since the record decl shares its vtable pointer with the primary base
+    // we need to start counting at the end of the primary base's vtable.
+    CurrentIndex = getNumVirtualFunctionPointers(PrimaryBase);
+  }
+  
+  const CXXDestructorDecl *ImplicitVirtualDtor = 0;
+  
+  for (CXXRecordDecl::method_iterator i = RD->method_begin(),
+       e = RD->method_end(); i != e; ++i) {
+    const CXXMethodDecl *MD = *i;
+
+    // We only want virtual methods.
+    if (!MD->isVirtual())
+      continue;
+
+    bool ShouldAddEntryForMethod = true;
+    
+    // Check if this method overrides a method in the primary base.
+    for (CXXMethodDecl::method_iterator i = MD->begin_overridden_methods(),
+         e = MD->end_overridden_methods(); i != e; ++i) {
+      const CXXMethodDecl *OverriddenMD = *i;
+      const CXXRecordDecl *OverriddenRD = OverriddenMD->getParent();
+      assert(OverriddenMD->isCanonicalDecl() &&
+             "Should have the canonical decl of the overridden RD!");
+      
+      if (OverriddenRD == PrimaryBase) {
+        // Check if converting from the return type of the method to the 
+        // return type of the overridden method requires conversion.
+        QualType ReturnType = 
+          MD->getType()->getAs<FunctionType>()->getResultType();
+        QualType OverriddenReturnType =
+          OverriddenMD->getType()->getAs<FunctionType>()->getResultType();
+        
+        if (!TypeConversionRequiresAdjustment(CGM.getContext(), 
+                                            ReturnType, OverriddenReturnType)) {
+          // This index is shared between the index in the vtable of the primary
+          // base class.
+          if (const CXXDestructorDecl *DD = dyn_cast<CXXDestructorDecl>(MD)) {
+            const CXXDestructorDecl *OverriddenDD = 
+              cast<CXXDestructorDecl>(OverriddenMD);
+            
+            // Add both the complete and deleting entries.
+            MethodVtableIndices[GlobalDecl(DD, Dtor_Complete)] = 
+              getMethodVtableIndex(GlobalDecl(OverriddenDD, Dtor_Complete));
+            MethodVtableIndices[GlobalDecl(DD, Dtor_Deleting)] = 
+              getMethodVtableIndex(GlobalDecl(OverriddenDD, Dtor_Deleting));
+          } else {
+            MethodVtableIndices[MD] = getMethodVtableIndex(OverriddenMD);
+          }
+          
+          // We don't need to add an entry for this method.
+          ShouldAddEntryForMethod = false;
+          break;
+        }        
+      }
+    }
+    
+    if (!ShouldAddEntryForMethod)
+      continue;
+    
+    if (const CXXDestructorDecl *DD = dyn_cast<CXXDestructorDecl>(MD)) {
+      if (MD->isImplicit()) {
+        assert(!ImplicitVirtualDtor && 
+               "Did already see an implicit virtual dtor!");
+        ImplicitVirtualDtor = DD;
+        continue;
+      } 
+
+      // Add the complete dtor.
+      MethodVtableIndices[GlobalDecl(DD, Dtor_Complete)] = CurrentIndex++;
+      
+      // Add the deleting dtor.
+      MethodVtableIndices[GlobalDecl(DD, Dtor_Deleting)] = CurrentIndex++;
+    } else {
+      // Add the entry.
+      MethodVtableIndices[MD] = CurrentIndex++;
+    }
+  }
+
+  if (ImplicitVirtualDtor) {
+    // Itanium C++ ABI 2.5.2:
+    // If a class has an implicitly-defined virtual destructor, 
+    // its entries come after the declared virtual function pointers.
+
+    // Add the complete dtor.
+    MethodVtableIndices[GlobalDecl(ImplicitVirtualDtor, Dtor_Complete)] = 
+      CurrentIndex++;
+    
+    // Add the deleting dtor.
+    MethodVtableIndices[GlobalDecl(ImplicitVirtualDtor, Dtor_Deleting)] = 
+      CurrentIndex++;
+  }
+  
+  NumVirtualFunctionPointers[RD] = CurrentIndex;
+}
+
+uint64_t CGVtableInfo::getNumVirtualFunctionPointers(const CXXRecordDecl *RD) {
+  llvm::DenseMap<const CXXRecordDecl *, uint64_t>::iterator I = 
+    NumVirtualFunctionPointers.find(RD);
+  if (I != NumVirtualFunctionPointers.end())
+    return I->second;
+
+  ComputeMethodVtableIndices(RD);
+
+  I = NumVirtualFunctionPointers.find(RD);
+  assert(I != NumVirtualFunctionPointers.end() && "Did not find entry!");
+  return I->second;
+}
+      
+uint64_t CGVtableInfo::getMethodVtableIndex(GlobalDecl GD) {
   MethodVtableIndicesTy::iterator I = MethodVtableIndices.find(GD);
   if (I != MethodVtableIndices.end())
     return I->second;
   
   const CXXRecordDecl *RD = cast<CXXMethodDecl>(GD.getDecl())->getParent();
-  
-  std::vector<llvm::Constant *> methods;
-  // FIXME: This seems expensive.  Can we do a partial job to get
-  // just this data.
-  VtableBuilder b(methods, RD, RD, 0, CGM);
-  D1(printf("vtable %s\n", RD->getNameAsCString()));
-  b.GenerateVtableForBase(RD);
-  b.GenerateVtableForVBases(RD);
-  
-  MethodVtableIndices.insert(b.getIndex().begin(),
-                             b.getIndex().end());
-  
+
+  ComputeMethodVtableIndices(RD);
+
   I = MethodVtableIndices.find(GD);
   assert(I != MethodVtableIndices.end() && "Did not find index!");
   return I->second;
index daf49d31b3aa1fb4b5d3eaa013ccd632642f8735..ac3cdee63becf9322440827a4b0cf3ccbe6ef338 100644 (file)
@@ -80,6 +80,17 @@ class CGVtableInfo {
   VirtualBaseClassIndiciesTy VirtualBaseClassIndicies;
 
   llvm::DenseMap<const CXXRecordDecl *, llvm::Constant *> Vtables;
+  
+  /// NumVirtualFunctionPointers - Contains the number of virtual function 
+  /// pointers in the vtable for a given record decl.
+  llvm::DenseMap<const CXXRecordDecl *, uint64_t> NumVirtualFunctionPointers;
+
+  /// getNumVirtualFunctionPointers - Return the number of virtual function
+  /// pointers in the vtable for a given record decl.
+  uint64_t getNumVirtualFunctionPointers(const CXXRecordDecl *RD);
+  
+  void ComputeMethodVtableIndices(const CXXRecordDecl *RD);
+  
 public:
   CGVtableInfo(CodeGenModule &CGM)
     : CGM(CGM) { }
@@ -87,7 +98,7 @@ public:
   /// getMethodVtableIndex - Return the index (relative to the vtable address
   /// point) where the function pointer for the given virtual function is
   /// stored.
-  int64_t getMethodVtableIndex(GlobalDecl GD);
+  uint64_t getMethodVtableIndex(GlobalDecl GD);
 
   /// getVirtualBaseOffsetIndex - Return the index (relative to the vtable
   /// address point) where the offset of the virtual base that contains the