[cfe-commits] r120808 - in /cfe/trunk: include/clang/AST/Decl.h include/clang/AST/DeclBase.h include/clang/AST/DeclTemplate.h include/clang/AST/Redeclarable.h lib/AST/Decl.cpp lib/AST/DeclBase.cpp lib/AST/DeclCXX.cpp lib/Serialization/ASTReaderDecl.cpp

Douglas Gregor dgregor at apple.com
Fri Dec 3 09:11:43 PST 2010


Author: dgregor
Date: Fri Dec  3 11:11:42 2010
New Revision: 120808

URL: http://llvm.org/viewvc/llvm-project?rev=120808&view=rev
Log:
Implement caching for the linkage and visibility calculations of
declarations.

The motivation for this patch is that linkage/visibility computations
are linear in the number of redeclarations of an entity, and we've run
into a case where a single translation unit has > 6500 redeclarations
of the same (unused!) external variable. Since each redeclaration
involves a linkage check, the resulting quadratic behavior makes Clang
slow to a crawl. With this change, a simple test with 512
redeclarations of a variable syntax-checks ~20x faster than
before.

That said, I hate this change, and will probably end up reverting it
in a few hours. Reasons to hate it:
  - It makes NamedDecl larger, since we don't have enough free bits in
  Decl to squeeze in the extra information about caching.
  - There are way too many places where we need to invalidate this
  cache, because the visibility of a declaration can change due to
  redeclarations (!). Despite self-hosting and passing the testsuite,
  I have no confidence that I've found all of places where this cache
  needs to be invalidated.

Modified:
    cfe/trunk/include/clang/AST/Decl.h
    cfe/trunk/include/clang/AST/DeclBase.h
    cfe/trunk/include/clang/AST/DeclTemplate.h
    cfe/trunk/include/clang/AST/Redeclarable.h
    cfe/trunk/lib/AST/Decl.cpp
    cfe/trunk/lib/AST/DeclBase.cpp
    cfe/trunk/lib/AST/DeclCXX.cpp
    cfe/trunk/lib/Serialization/ASTReaderDecl.cpp

Modified: cfe/trunk/include/clang/AST/Decl.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/Decl.h?rev=120808&r1=120807&r2=120808&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/Decl.h (original)
+++ cfe/trunk/include/clang/AST/Decl.h Fri Dec  3 11:11:42 2010
@@ -99,10 +99,25 @@
   /// constructor, Objective-C selector, etc.)
   DeclarationName Name;
 
+  /// \brief Whether we have already cached linkage and visibility.
+  mutable unsigned HasLinkageAndVisibilityCached : 1;
+
+  /// \brief The cached visibility, if \c HasLinkageAndVisibilityCached is 
+  /// non-zero.
+  mutable unsigned CachedVisibility : 2;
+
+  /// \brief Whether the cached visibility was explicitly placed on this
+  /// declaration.
+  mutable unsigned CachedVisibilityIsExplicit : 1;
+  
+  /// \brief The cached linkage, if \c HasLinkageAndVisibilityCached is
+  /// non-zero.
+  mutable unsigned CachedLinkage : 2;
+
 protected:
   NamedDecl(Kind DK, DeclContext *DC, SourceLocation L, DeclarationName N)
-    : Decl(DK, DC, L), Name(N) { }
-
+    : Decl(DK, DC, L), Name(N), HasLinkageAndVisibilityCached(0) { }
+  
 public:
   /// getIdentifier - Get the identifier that names this declaration,
   /// if there is one. This will return NULL if this declaration has
@@ -272,6 +287,13 @@
   /// \brief Determines the linkage and visibility of this entity.
   LinkageInfo getLinkageAndVisibility() const;
 
+  /// \brief Clear the linkage and visibility cache in response to a change
+  /// to the declaration. 
+  ///
+  /// \param Redeclarations When true, we also have to clear out the linkage
+  /// and visibility cache for all redeclarations.
+  void ClearLinkageAndVisibilityCache();
+
   /// \brief Looks through UsingDecls and ObjCCompatibleAliasDecls for
   /// the underlying named decl.
   NamedDecl *getUnderlyingDecl();
@@ -625,6 +647,8 @@
   bool NRVOVariable : 1;
   
   friend class StmtIteratorBase;
+  friend class ASTDeclReader;
+  
 protected:
   VarDecl(Kind DK, DeclContext *DC, SourceLocation L, IdentifierInfo *Id,
           QualType T, TypeSourceInfo *TInfo, StorageClass SC,
@@ -660,10 +684,7 @@
   StorageClass getStorageClassAsWritten() const {
     return (StorageClass) SClassAsWritten;
   }
-  void setStorageClass(StorageClass SC) {
-    assert(isLegalForVariable(SC));
-    SClass = SC;
-  }
+  void setStorageClass(StorageClass SC);
   void setStorageClassAsWritten(StorageClass SC) {
     assert(isLegalForVariable(SC));
     SClassAsWritten = SC;
@@ -1478,10 +1499,7 @@
   }
                        
   StorageClass getStorageClass() const { return StorageClass(SClass); }
-  void setStorageClass(StorageClass SC) {
-    assert(isLegalForFunction(SC));
-    SClass = SC;
-  }
+  void setStorageClass(StorageClass SC);
 
   StorageClass getStorageClassAsWritten() const {
     return StorageClass(SClassAsWritten);
@@ -2545,6 +2563,63 @@
   return DB;
 }
 
+template<typename decl_type>
+void Redeclarable<decl_type>::setPreviousDeclaration(decl_type *PrevDecl) {
+  // Note: This routine is implemented here because we need both NamedDecl
+  // and Redeclarable to be defined.
+  decl_type *First;
+  
+  if (PrevDecl) {
+    // Point to previous. Make sure that this is actually the most recent
+    // redeclaration, or we can build invalid chains. If the most recent
+    // redeclaration is invalid, it won't be PrevDecl, but we want it anyway.
+    RedeclLink = PreviousDeclLink(llvm::cast<decl_type>(
+                                                        PrevDecl->getMostRecentDeclaration()));
+    First = PrevDecl->getFirstDeclaration();
+    assert(First->RedeclLink.NextIsLatest() && "Expected first");
+  } else {
+    // Make this first.
+    First = static_cast<decl_type*>(this);
+  }
+  
+  // First one will point to this one as latest.
+  First->RedeclLink = LatestDeclLink(static_cast<decl_type*>(this));
+  
+  // If this declaration has a visibility attribute that differs from the 
+  // previous visibility attribute, or has private extern storage while the
+  // previous declaration merely had extern storage, clear out the linkage and 
+  ///visibility cache. This is required because declarations after the first 
+  // declaration can change the visibility for all previous and future 
+  // declarations.
+  if (NamedDecl *ND = dyn_cast<NamedDecl>(static_cast<decl_type*>(this))) {
+    bool MustClear = false;
+    if (VisibilityAttr *Visibility = ND->getAttr<VisibilityAttr>()) {
+      VisibilityAttr *PrevVisibility 
+                                = PrevDecl->template getAttr<VisibilityAttr>();
+      if (!PrevVisibility || 
+          PrevVisibility->getVisibility() != Visibility->getVisibility())
+        MustClear = true;
+    }
+    
+    if (!MustClear) {
+      if (VarDecl *VD = dyn_cast<VarDecl>(ND)) {
+        if (VD->getStorageClass() != cast<VarDecl>(PrevDecl)->getStorageClass())
+          MustClear = true;
+      } else if (FunctionDecl *FD = dyn_cast<FunctionDecl>(ND)) {
+        FunctionDecl *PrevFD = cast<FunctionDecl>(PrevDecl);
+        if (FD->getStorageClass() != PrevFD->getStorageClass())
+          MustClear = true;
+        else if (FD->isInlineSpecified() && !PrevFD->isInlined())
+          MustClear = true;
+      }
+    }
+    
+    if (MustClear)
+      ND->ClearLinkageAndVisibilityCache();
+  }
+}
+
+
 }  // end namespace clang
 
 #endif

Modified: cfe/trunk/include/clang/AST/DeclBase.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/DeclBase.h?rev=120808&r1=120807&r2=120808&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/DeclBase.h (original)
+++ cfe/trunk/include/clang/AST/DeclBase.h Fri Dec  3 11:11:42 2010
@@ -198,7 +198,7 @@
     return DeclCtx.get<DeclContext*>();
   }
 
-  /// Loc - The location that this decl.
+  /// Loc - The location of this decl.
   SourceLocation Loc;
 
   /// DeclKind - This indicates which class this is.
@@ -316,12 +316,7 @@
   void swapAttrs(Decl *D);
   void dropAttrs();
 
-  void addAttr(Attr *A) {
-    if (hasAttrs())
-      getAttrs().push_back(A);
-    else
-      setAttrs(AttrVec(1, A));
-  }
+  void addAttr(Attr *A);
 
   typedef AttrVec::const_iterator attr_iterator;
 

Modified: cfe/trunk/include/clang/AST/DeclTemplate.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/DeclTemplate.h?rev=120808&r1=120807&r2=120808&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/DeclTemplate.h (original)
+++ cfe/trunk/include/clang/AST/DeclTemplate.h Fri Dec  3 11:11:42 2010
@@ -1241,6 +1241,8 @@
   }
 
   void setSpecializationKind(TemplateSpecializationKind TSK) {
+    if (SpecializationKind != TSK)
+      ClearLinkageAndVisibilityCache();
     SpecializationKind = TSK;
   }
 

Modified: cfe/trunk/include/clang/AST/Redeclarable.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/Redeclarable.h?rev=120808&r1=120807&r2=120808&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/Redeclarable.h (original)
+++ cfe/trunk/include/clang/AST/Redeclarable.h Fri Dec  3 11:11:42 2010
@@ -109,26 +109,8 @@
   
   /// \brief Set the previous declaration. If PrevDecl is NULL, set this as the
   /// first and only declaration.
-  void setPreviousDeclaration(decl_type *PrevDecl) {
-    decl_type *First;
-
-    if (PrevDecl) {
-      // Point to previous. Make sure that this is actually the most recent
-      // redeclaration, or we can build invalid chains. If the most recent
-      // redeclaration is invalid, it won't be PrevDecl, but we want it anyway.
-      RedeclLink = PreviousDeclLink(llvm::cast<decl_type>(
-                                      PrevDecl->getMostRecentDeclaration()));
-      First = PrevDecl->getFirstDeclaration();
-      assert(First->RedeclLink.NextIsLatest() && "Expected first");
-    } else {
-      // Make this first.
-      First = static_cast<decl_type*>(this);
-    }
-
-    // First one will point to this one as latest.
-    First->RedeclLink = LatestDeclLink(static_cast<decl_type*>(this));
-  }
-
+  void setPreviousDeclaration(decl_type *PrevDecl);
+  
   /// \brief Iterates through all the redeclarations of the same decl.
   class redecl_iterator {
     /// Current - The current declaration.

Modified: cfe/trunk/lib/AST/Decl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/Decl.cpp?rev=120808&r1=120807&r2=120808&view=diff
==============================================================================
--- cfe/trunk/lib/AST/Decl.cpp (original)
+++ cfe/trunk/lib/AST/Decl.cpp Fri Dec  3 11:11:42 2010
@@ -526,7 +526,57 @@
 }
 
 LinkageInfo NamedDecl::getLinkageAndVisibility() const {
-  return getLVForDecl(this, LVFlags());
+  // If we have already cached linkage and visibility, just return the
+  // cached information.
+  if (HasLinkageAndVisibilityCached) {
+#ifndef NDEBUG
+    LinkageInfo LI = getLVForDecl(this, LVFlags());
+    assert(LI.visibility() == CachedVisibility);
+    assert(LI.visibilityExplicit() == CachedVisibilityIsExplicit);
+    assert(LI.linkage() == CachedLinkage);
+#endif
+    return LinkageInfo(Linkage(CachedLinkage), Visibility(CachedVisibility),
+                       CachedVisibilityIsExplicit);
+  }
+  
+  LinkageInfo LI = getLVForDecl(this, LVFlags());
+  HasLinkageAndVisibilityCached = 1;
+  CachedVisibility = LI.visibility();
+  CachedVisibilityIsExplicit = LI.visibilityExplicit();
+  CachedLinkage = LI.linkage();
+  return LI;
+}
+
+void NamedDecl::ClearLinkageAndVisibilityCache() {
+  HasLinkageAndVisibilityCached = 0;
+  
+  if (VarDecl *VD = dyn_cast<VarDecl>(this)) {
+    for (VarDecl::redecl_iterator R = VD->redecls_begin(),
+                               REnd = VD->redecls_end();
+         R != REnd; ++R)
+      R->HasLinkageAndVisibilityCached = 0;
+    
+    return;
+  }
+  
+  if (FunctionDecl *FD = dyn_cast<FunctionDecl>(this)) {
+    for (FunctionDecl::redecl_iterator R = FD->redecls_begin(),
+                                    REnd = FD->redecls_end();
+         R != REnd; ++R)
+      R->HasLinkageAndVisibilityCached = 0;
+    
+    return;
+  }
+  
+  // Changing the linkage or visibility of a C++ class affect the linkage and
+  // visibility of all of its members. Clear their caches, too.
+  if (CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(this)) {
+    for (DeclContext::decl_iterator D = RD->decls_begin(),
+                                 DEnd = RD->decls_end();
+         D != DEnd; ++D)
+      if (NamedDecl *ND = dyn_cast<NamedDecl>(*D))
+        ND->ClearLinkageAndVisibilityCache();
+  }
 }
 
 static LinkageInfo getLVForDecl(const NamedDecl *D, LVFlags Flags) {
@@ -878,6 +928,14 @@
   return new (C) VarDecl(Var, DC, L, Id, T, TInfo, S, SCAsWritten);
 }
 
+void VarDecl::setStorageClass(StorageClass SC) {
+  assert(isLegalForVariable(SC));
+  if (SClass != SC)
+    ClearLinkageAndVisibilityCache();
+  
+  SClass = SC;
+}
+
 SourceLocation VarDecl::getInnerLocStart() const {
   SourceLocation Start = getTypeSpecStartLoc();
   if (Start.isInvalid())
@@ -1096,6 +1154,7 @@
       PointOfInstantiation.isValid() &&
       MSI->getPointOfInstantiation().isInvalid())
     MSI->setPointOfInstantiation(PointOfInstantiation);
+  ClearLinkageAndVisibilityCache();
 }
 
 //===----------------------------------------------------------------------===//
@@ -1196,8 +1255,10 @@
 
 void FunctionDecl::setBody(Stmt *B) {
   Body = B;
-  if (B)
+  if (B) {
     EndRangeLoc = B->getLocEnd();
+    ClearLinkageAndVisibilityCache();
+  }
 }
 
 void FunctionDecl::setPure(bool P) {
@@ -1278,6 +1339,14 @@
   return getFirstDeclaration();
 }
 
+void FunctionDecl::setStorageClass(StorageClass SC) {
+  assert(isLegalForFunction(SC));
+  if (SClass != SC)
+    ClearLinkageAndVisibilityCache();
+  
+  SClass = SC;
+}
+
 /// \brief Returns a value indicating whether this function
 /// corresponds to a builtin function.
 ///
@@ -1710,6 +1779,7 @@
       MSInfo->setPointOfInstantiation(PointOfInstantiation);
   } else
     assert(false && "Function cannot have a template specialization kind");
+  ClearLinkageAndVisibilityCache();
 }
 
 SourceLocation FunctionDecl::getPointOfInstantiation() const {
@@ -1788,6 +1858,7 @@
   TypedefDeclOrQualifier = TDD; 
   if (TypeForDecl)
     TypeForDecl->ClearLinkageCache();
+  ClearLinkageAndVisibilityCache();
 }
 
 void TagDecl::startDefinition() {

Modified: cfe/trunk/lib/AST/DeclBase.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/DeclBase.cpp?rev=120808&r1=120807&r2=120808&view=diff
==============================================================================
--- cfe/trunk/lib/AST/DeclBase.cpp (original)
+++ cfe/trunk/lib/AST/DeclBase.cpp Fri Dec  3 11:11:42 2010
@@ -337,6 +337,38 @@
   getASTContext().eraseDeclAttrs(this);
 }
 
+void Decl::addAttr(Attr *A) {
+  if (NamedDecl *ND = dyn_cast<NamedDecl>(this))
+    if (VisibilityAttr *Visibility = dyn_cast<VisibilityAttr>(A)) {
+      bool ClearVisibility = true;
+      if (VarDecl *VD = dyn_cast<VarDecl>(this)) {
+        if (VD->getPreviousDeclaration()) {
+          VisibilityAttr *PrevVisibility 
+            = VD->getPreviousDeclaration()->getAttr<VisibilityAttr>();
+          if  (PrevVisibility &&
+               PrevVisibility->getVisibility() == Visibility->getVisibility())
+            ClearVisibility = false;
+        }
+      } else if (FunctionDecl *FD = dyn_cast<FunctionDecl>(this)) {
+        if (FD->getPreviousDeclaration()) {
+          VisibilityAttr *PrevVisibility 
+            = FD->getPreviousDeclaration()->getAttr<VisibilityAttr>();
+          if  (PrevVisibility &&
+               PrevVisibility->getVisibility() == Visibility->getVisibility())
+            ClearVisibility = false;
+        }
+      }
+      
+      if (ClearVisibility)
+        ND->ClearLinkageAndVisibilityCache();
+    }
+  
+  if (hasAttrs())
+    getAttrs().push_back(A);
+  else
+    setAttrs(AttrVec(1, A));
+}
+
 const AttrVec &Decl::getAttrs() const {
   assert(HasAttrs && "No attrs to get!");
   return getASTContext().getDeclAttrs(this);

Modified: cfe/trunk/lib/AST/DeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/DeclCXX.cpp?rev=120808&r1=120807&r2=120808&view=diff
==============================================================================
--- cfe/trunk/lib/AST/DeclCXX.cpp (original)
+++ cfe/trunk/lib/AST/DeclCXX.cpp Fri Dec  3 11:11:42 2010
@@ -792,6 +792,8 @@
   }
   
   if (MemberSpecializationInfo *MSInfo = getMemberSpecializationInfo()) {
+    if (MSInfo->getTemplateSpecializationKind() != TSK)
+      ClearLinkageAndVisibilityCache();
     MSInfo->setTemplateSpecializationKind(TSK);
     return;
   }

Modified: cfe/trunk/lib/Serialization/ASTReaderDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTReaderDecl.cpp?rev=120808&r1=120807&r2=120808&view=diff
==============================================================================
--- cfe/trunk/lib/Serialization/ASTReaderDecl.cpp (original)
+++ cfe/trunk/lib/Serialization/ASTReaderDecl.cpp Fri Dec  3 11:11:42 2010
@@ -383,7 +383,7 @@
   // FunctionDecl's body is handled last at ASTDeclReader::Visit,
   // after everything else is read.
 
-  FD->setStorageClass((StorageClass)Record[Idx++]);
+  FD->SClass = (StorageClass)Record[Idx++];
   FD->setStorageClassAsWritten((StorageClass)Record[Idx++]);
   FD->setInlineSpecified(Record[Idx++]);
   FD->setVirtualAsWritten(Record[Idx++]);
@@ -650,7 +650,7 @@
 void ASTDeclReader::VisitVarDecl(VarDecl *VD) {
   VisitDeclaratorDecl(VD);
   VisitRedeclarable(VD);
-  VD->setStorageClass((StorageClass)Record[Idx++]);
+  VD->SClass = (StorageClass)Record[Idx++];
   VD->setStorageClassAsWritten((StorageClass)Record[Idx++]);
   VD->setThreadSpecified(Record[Idx++]);
   VD->setCXXDirectInitializer(Record[Idx++]);





More information about the cfe-commits mailing list