Since we're stuck with realpath for the header <-> module mapping,
authorDouglas Gregor <dgregor@apple.com>
Sat, 26 Jan 2013 00:55:12 +0000 (00:55 +0000)
committerDouglas Gregor <dgregor@apple.com>
Sat, 26 Jan 2013 00:55:12 +0000 (00:55 +0000)
factor the realpath calls into FileManager::getCanonicalName() so we
can cache the results of this epically slow operation. 5% speedup on
my modules test, and realpath drops out of the profile.

llvm-svn: 173542

clang/include/clang/Basic/FileManager.h
clang/lib/Basic/FileManager.cpp
clang/lib/Lex/HeaderSearch.cpp
clang/lib/Lex/ModuleMap.cpp

index 5914e16..6d9e53b 100644 (file)
@@ -17,6 +17,7 @@
 
 #include "clang/Basic/FileSystemOptions.h"
 #include "clang/Basic/LLVM.h"
+#include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/IntrusiveRefCntPtr.h"
 #include "llvm/ADT/OwningPtr.h"
 #include "llvm/ADT/SmallVector.h"
@@ -152,6 +153,12 @@ class FileManager : public RefCountedBase<FileManager> {
   /// \see SeenDirEntries
   llvm::StringMap<FileEntry*, llvm::BumpPtrAllocator> SeenFileEntries;
 
+  /// \brief The canonical names of directories.
+  llvm::DenseMap<const DirectoryEntry *, llvm::StringRef> CanonicalDirNames;
+
+  /// \brief Storage for canonical names that we have computed.
+  llvm::BumpPtrAllocator CanonicalNameStorage;
+
   /// \brief Each FileEntry we create is assigned a unique ID #.
   ///
   unsigned NextFileUID;
@@ -257,6 +264,13 @@ public:
   static void modifyFileEntry(FileEntry *File, off_t Size,
                               time_t ModificationTime);
 
+  /// \brief Retrieve the canonical name for a given directory.
+  ///
+  /// This is a very expensive operation, despite its results being cached,
+  /// and should only be used when the physical layout of the file system is
+  /// required, which is (almost) never.
+  StringRef getCanonicalName(const DirectoryEntry *Dir);
+
   void PrintStats() const;
 };
 
index 3a7bdef..19f170e 100644 (file)
 #define S_ISFIFO(x) (0)
 #endif
 #endif
+#if defined(LLVM_ON_UNIX)
+#if defined(__linux__)
+#include <linux/limits.h>
+#endif
+#endif
 using namespace clang;
 
 // FIXME: Enhance libsystem to support inode and other fields.
@@ -620,6 +625,29 @@ void FileManager::modifyFileEntry(FileEntry *File,
   File->ModTime = ModificationTime;
 }
 
+StringRef FileManager::getCanonicalName(const DirectoryEntry *Dir) {
+  // FIXME: use llvm::sys::fs::canonical() when it gets implemented
+#ifdef LLVM_ON_UNIX
+  llvm::DenseMap<const DirectoryEntry *, llvm::StringRef>::iterator Known
+    = CanonicalDirNames.find(Dir);
+  if (Known != CanonicalDirNames.end())
+    return Known->second;
+
+  StringRef CanonicalName(Dir->getName());
+  char CanonicalNameBuf[PATH_MAX];
+  if (realpath(Dir->getName(), CanonicalNameBuf)) {
+    unsigned Len = strlen(CanonicalNameBuf);
+    char *Mem = static_cast<char *>(CanonicalNameStorage.Allocate(Len, 1));
+    memcpy(Mem, CanonicalNameBuf, Len);
+    CanonicalName = StringRef(Mem, Len);
+  }
+
+  CanonicalDirNames.insert(std::make_pair(Dir, CanonicalName));
+  return CanonicalName;
+#else
+  return StringRef(Dir->getName());
+#endif
+}
 
 void FileManager::PrintStats() const {
   llvm::errs() << "\n*** File Manager Stats:\n";
index 14d44cc..6432925 100644 (file)
@@ -268,6 +268,10 @@ const FileEntry *DirectoryLookup::LookupFile(
   return Result;
 }
 
+/// FIXME: HACK HACK HACK!
+static llvm::DenseMap<const DirectoryEntry *, const DirectoryEntry *>
+  TopFrameworkDirs;
+
 /// \brief Given a framework directory, find the top-most framework directory.
 ///
 /// \param FileMgr The file manager to use for directory lookups.
@@ -280,7 +284,6 @@ getTopFrameworkDir(FileManager &FileMgr, StringRef DirName,
   assert(llvm::sys::path::extension(DirName) == ".framework" &&
          "Not a framework directory");
 
-#ifdef LLVM_ON_UNIX
   // Note: as an egregious but useful hack we use the real path here, because
   // frameworks moving between top-level frameworks to embedded frameworks tend
   // to be symlinked, and we base the logical structure of modules on the
@@ -295,12 +298,8 @@ getTopFrameworkDir(FileManager &FileMgr, StringRef DirName,
   //
   // Similar issues occur when a top-level framework has moved into an
   // embedded framework.
-  char RealDirName[PATH_MAX];
-  if (realpath(DirName.str().c_str(), RealDirName))
-    DirName = RealDirName;
-#endif
-
   const DirectoryEntry *TopFrameworkDir = FileMgr.getDirectory(DirName);
+  DirName = FileMgr.getCanonicalName(TopFrameworkDir);
   do {
     // Get the parent directory name.
     DirName = llvm::sys::path::parent_path(DirName);
index 25e5bee..7ad42be 100644 (file)
@@ -163,20 +163,12 @@ Module *ModuleMap::findModuleForHeader(const FileEntry *File) {
   
   const DirectoryEntry *Dir = File->getDir();
   SmallVector<const DirectoryEntry *, 2> SkippedDirs;
-#ifdef LLVM_ON_UNIX
+
   // Note: as an egregious but useful hack we use the real path here, because
   // frameworks moving from top-level frameworks to embedded frameworks tend
   // to be symlinked from the top-level location to the embedded location,
   // and we need to resolve lookups as if we had found the embedded location.
-  char RealDirName[PATH_MAX];
-  StringRef DirName;
-  if (realpath(Dir->getName(), RealDirName))
-    DirName = RealDirName;
-  else
-    DirName = Dir->getName();
-#else
-  StringRef DirName = Dir->getName();
-#endif
+  StringRef DirName = SourceMgr->getFileManager().getCanonicalName(Dir);
 
   // Keep walking up the directory hierarchy, looking for a directory with
   // an umbrella header.
@@ -420,16 +412,13 @@ ModuleMap::inferFrameworkModule(StringRef ModuleName,
   // a framework module, do so.
   if (!Parent) {
     // Determine whether we're allowed to infer a module map.
-    StringRef FrameworkDirName = FrameworkDir->getName();
-#ifdef LLVM_ON_UNIX
+
     // Note: as an egregious but useful hack we use the real path here, because
     // we might be looking at an embedded framework that symlinks out to a
     // top-level framework, and we need to infer as if we were naming the
     // top-level framework.
-    char RealFrameworkDirName[PATH_MAX];
-    if (realpath(FrameworkDir->getName(), RealFrameworkDirName))
-      FrameworkDirName = RealFrameworkDirName;
-#endif
+    StringRef FrameworkDirName
+      = SourceMgr->getFileManager().getCanonicalName(FrameworkDir);
 
     bool canInfer = false;
     if (llvm::sys::path::has_parent_path(FrameworkDirName)) {
@@ -527,29 +516,23 @@ ModuleMap::inferFrameworkModule(StringRef ModuleName,
       // check whether it is actually a subdirectory of the parent directory.
       // This will not be the case if the 'subframework' is actually a symlink
       // out to a top-level framework.
-#ifdef LLVM_ON_UNIX
-      char RealSubframeworkDirName[PATH_MAX];
-      if (realpath(Dir->path().c_str(), RealSubframeworkDirName)) {
-        StringRef SubframeworkDirName = RealSubframeworkDirName;
-
-        bool FoundParent = false;
-        do {
-          // Get the parent directory name.
-          SubframeworkDirName
-            = llvm::sys::path::parent_path(SubframeworkDirName);
-          if (SubframeworkDirName.empty())
-            break;
-
-          if (FileMgr.getDirectory(SubframeworkDirName) == FrameworkDir) {
-            FoundParent = true;
-            break;
-          }
-        } while (true);
+      StringRef SubframeworkDirName = FileMgr.getCanonicalName(SubframeworkDir);
+      bool FoundParent = false;
+      do {
+        // Get the parent directory name.
+        SubframeworkDirName
+          = llvm::sys::path::parent_path(SubframeworkDirName);
+        if (SubframeworkDirName.empty())
+          break;
+
+        if (FileMgr.getDirectory(SubframeworkDirName) == FrameworkDir) {
+          FoundParent = true;
+          break;
+        }
+      } while (true);
 
-        if (!FoundParent)
-          continue;
-      }
-#endif
+      if (!FoundParent)
+        continue;
 
       // FIXME: Do we want to warn about subframeworks without umbrella headers?
       SmallString<32> NameBuf;