]> granicus.if.org Git - clang/commitdiff
Introduce a module visitation function that starts at the top-level
authorDouglas Gregor <dgregor@apple.com>
Sat, 20 Aug 2011 04:39:52 +0000 (04:39 +0000)
committerDouglas Gregor <dgregor@apple.com>
Sat, 20 Aug 2011 04:39:52 +0000 (04:39 +0000)
modules (those that no other module depends on) and performs a search
over all of the modules, visiting a new module only when all of the
modules that depend on it have already been visited. The visitor can
abort the search for all modules that a module depends on, which
allows us to minimize the number of lookups necessary when performing
a search.

Switch identifier lookup from a linear walk over the set of modules to
this module visitation operation. The behavior is the same for simple
PCH and chained PCH, but provides the proper search order for
modules. Verified with printf debugging, since we don't have enough in
place to actually test this.

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

include/clang/Serialization/ASTReader.h
lib/Serialization/ASTReader.cpp
test/Modules/Inputs/diamond_left.h
test/Modules/Inputs/diamond_top.h
test/Modules/diamond.c

index 5223bd8d157654455c2d05a716d6fb859e7c002d..3948ea424b1a4ed23bca50e381f8d359f7fc70f6 100644 (file)
@@ -515,6 +515,26 @@ public:
   
   /// \brief Add an in-memory buffer the list of known buffers
   void addInMemoryBuffer(StringRef FileName, llvm::MemoryBuffer *Buffer);
+
+  /// \brief Visit each of the modules.
+  ///
+  /// This routine visits each of the modules, starting with the
+  /// "root" modules that no other loaded modules depend on, and
+  /// proceeding to the leaf modules, visiting each module only once
+  /// during the traversal.
+  ///
+  /// This traversal is intended to support various "lookup"
+  /// operations that can find data in any of the loaded modules.
+  ///
+  /// \param Visitor A visitor function that will be invoked with each
+  /// module and the given user data pointer. The return value must be
+  /// convertible to bool; when false, the visitation continues to
+  /// modules that the current module depends on. When true, the
+  /// visitation skips any modules that the current module depends on.
+  ///
+  /// \param UserData User data associated with the visitor object, which
+  /// will be passed along to the visitor.
+  void visit(bool (*Visitor)(Module &M, void *UserData), void *UserData);
 };
 
 } // end namespace serialization
index cc239c35eade29872f88264c48067004a06aff2d..d3400bae5aeb52379b274196d53f37cc086701c4 100644 (file)
@@ -49,7 +49,7 @@
 #include <iterator>
 #include <cstdio>
 #include <sys/stat.h>
-#include <iostream>
+#include <queue>
 
 using namespace clang;
 using namespace clang::serialization;
@@ -4574,25 +4574,47 @@ void ASTReader::InitializeSema(Sema &S) {
   }
 }
 
-IdentifierInfo* ASTReader::get(const char *NameStart, const char *NameEnd) {
-  // Try to find this name within our on-disk hash tables. We start with the
-  // most recent one, since that one contains the most up-to-date info.
-  for (ModuleIterator I = ModuleMgr.begin(), E = ModuleMgr.end(); I != E; ++I) {
-    ASTIdentifierLookupTable *IdTable
-        = (ASTIdentifierLookupTable *)(*I)->IdentifierLookupTable;
-    if (!IdTable)
-      continue;
-    std::pair<const char*, unsigned> Key(NameStart, NameEnd - NameStart);
-    ASTIdentifierLookupTable::iterator Pos = IdTable->find(Key);
-    if (Pos == IdTable->end())
-      continue;
+namespace {
+  /// \brief Visitor class used to look up identifirs in 
+  class IdentifierLookupVisitor {
+    StringRef Name;
+    IdentifierInfo *Found;
+  public:
+    explicit IdentifierLookupVisitor(StringRef Name) : Name(Name), Found() { }
 
-    // Dereferencing the iterator has the effect of building the
-    // IdentifierInfo node and populating it with the various
-    // declarations it needs.
-    return *Pos;
-  }
-  return 0;
+    static bool visit(Module &M, void *UserData) {
+      IdentifierLookupVisitor *This
+        = static_cast<IdentifierLookupVisitor *>(UserData);
+      
+      ASTIdentifierLookupTable *IdTable
+        = (ASTIdentifierLookupTable *)M.IdentifierLookupTable;
+      if (!IdTable)
+        return false;
+
+      std::pair<const char*, unsigned> Key(This->Name.begin(), 
+                                           This->Name.size());
+      ASTIdentifierLookupTable::iterator Pos = IdTable->find(Key);
+      if (Pos == IdTable->end())
+        return false;
+
+      // Dereferencing the iterator has the effect of building the
+      // IdentifierInfo node and populating it with the various
+      // declarations it needs.
+      This->Found = *Pos;
+      return true;
+    }
+
+    // \brief Retrieve the identifier info found within the module
+    // files.
+    IdentifierInfo *getIdentifierInfo() const { return Found; }
+  };
+}
+
+IdentifierInfo* ASTReader::get(const char *NameStart, const char *NameEnd) {
+  std::string Name(NameStart, NameEnd);
+  IdentifierLookupVisitor Visitor(StringRef(NameStart, NameEnd - NameStart));
+  ModuleMgr.visit(IdentifierLookupVisitor::visit, &Visitor);
+  return Visitor.getIdentifierInfo();
 }
 
 namespace clang {
@@ -5606,6 +5628,9 @@ ASTReader::~ASTReader() {
   }
 }
 
+//===----------------------------------------------------------------------===//
+// Module implementation
+//===----------------------------------------------------------------------===//
 Module::Module(ModuleKind Kind)
   : Kind(Kind), DirectlyImported(false), SizeInBits(0), 
     LocalNumSLocEntries(0), SLocEntryBaseID(0),
@@ -5695,6 +5720,10 @@ void Module::dump() {
   dumpLocalRemap("Decl ID local -> global map", DeclRemap);
 }
 
+//===----------------------------------------------------------------------===//
+// Module manager implementation
+//===----------------------------------------------------------------------===//
+
 Module *ModuleManager::lookup(StringRef Name) {
   const FileEntry *Entry = FileMgr.getFile(Name);
   return Modules[Entry];
@@ -5772,3 +5801,71 @@ ModuleManager::~ModuleManager() {
   for (unsigned i = 0, e = Chain.size(); i != e; ++i)
     delete Chain[e - i - 1];
 }
+
+void ModuleManager::visit(bool (*Visitor)(Module &M, void *UserData), 
+                          void *UserData) {
+  unsigned N = size();
+
+  // Record the number of incoming edges for each module. When we
+  // encounter a module with no incoming edges, push it into the queue
+  // to seed the queue.
+  SmallVector<Module *, 4> Queue;
+  Queue.reserve(N);
+  llvm::DenseMap<Module *, unsigned> UnusedIncomingEdges; 
+  for (ModuleIterator M = begin(), MEnd = end(); M != MEnd; ++M) {
+    if (unsigned Size = (*M)->ImportedBy.size())
+      UnusedIncomingEdges[*M] = Size;
+    else
+      Queue.push_back(*M);
+  }
+
+  llvm::SmallPtrSet<Module *, 4> Skipped;
+  unsigned QueueStart = 0;
+  while (QueueStart < Queue.size()) {
+    Module *CurrentModule = Queue[QueueStart++];
+
+    // Check whether this module should be skipped.
+    if (Skipped.count(CurrentModule))
+      continue;
+
+    if (Visitor(*CurrentModule, UserData)) {
+      // The visitor has requested that cut off visitation of any
+      // module that the current module depends on. To indicate this
+      // behavior, we mark all of the reachable modules as having N
+      // incoming edges (which is impossible otherwise).
+      SmallVector<Module *, 4> Stack;
+      Stack.push_back(CurrentModule);
+      Skipped.insert(CurrentModule);
+      while (!Stack.empty()) {
+        Module *NextModule = Stack.back();
+        Stack.pop_back();
+
+        // For any module that this module depends on, push it on the
+        // stack (if it hasn't already been marked as visited).
+        for (llvm::SetVector<Module *>::iterator 
+                  M = NextModule->Imports.begin(),
+               MEnd = NextModule->Imports.end();
+             M != MEnd; ++M) {
+          if (Skipped.insert(*M))
+            Stack.push_back(*M);
+        }
+      }
+      continue;
+    }
+
+    // For any module that this module depends on, push it on the
+    // stack (if it hasn't already been marked as visited).
+    for (llvm::SetVector<Module *>::iterator M = CurrentModule->Imports.begin(),
+                                          MEnd = CurrentModule->Imports.end();
+         M != MEnd; ++M) {
+
+      // Remove our current module as an impediment to visiting the
+      // module we depend on. If we were the last unvisited module
+      // that depends on this particular module, push it into the
+      // queue to be visited.
+      unsigned &NumUnusedEdges = UnusedIncomingEdges[*M];
+      if (NumUnusedEdges && (--NumUnusedEdges == 0))
+        Queue.push_back(*M);
+    }
+  }
+}
index dfdd803a0ad798eb9169805474749ea753972f34..3a97094c08f79194a37fa9c96349a9dedab65ec0 100644 (file)
@@ -1 +1,4 @@
 float left(float *);
+
+int top_left(char *c);
+
index 189687aab12fe7b755d19632dbbab77be473bc6e..34998cd4324b94abf9c5dc3cc6ebb07e956c56fa 100644 (file)
@@ -1 +1,4 @@
 int top(int *);
+
+int top_left(char *c);
+
index fdec7b3aab3ee784d85287ddb606616a1c45389f..220a27903800a36c1d5d9a7e585a81e1e396b014 100644 (file)
@@ -5,6 +5,9 @@ void test_diamond(int i, float f, double d, char c) {
   right(&d);
   bottom(&c);
   bottom(&d); // expected-warning{{incompatible pointer types passing 'double *' to parameter of type 'char *'}}
+
+  // Names in multiple places in the diamond.
+  top_left(&c);
 }
 
 // RUN: %clang_cc1 -emit-pch -o %t_top.h.pch %S/Inputs/diamond_top.h