xazax.hun created this revision.
Herald added a subscriber: mgorny.

This patch introduces a class that can help to build tools that require cross 
translation unit facilities.
This class allows function definitions to be loaded from external AST files 
based on an index.
In order to use this functionality an index is required. USRs are used as names 
to look up the functions. 
This class also does caching to avoid redundant loading of AST files.

Right now only function defnitions can be loaded using this API, because this 
is what the Static Analyzer requires.
In to future this could be extended to classes, types etc.

Note that, there is no tests right now for this functionality, but this is 
temporary.
Tets will come together with the first user once it is accepted: 
https://reviews.llvm.org/D30691


Repository:
  rL LLVM

https://reviews.llvm.org/D34512

Files:
  include/clang/Tooling/CrossTranslationUnit.h
  lib/Tooling/CMakeLists.txt
  lib/Tooling/CrossTranslationUnit.cpp

Index: lib/Tooling/CrossTranslationUnit.cpp
===================================================================
--- /dev/null
+++ lib/Tooling/CrossTranslationUnit.cpp
@@ -0,0 +1,162 @@
+//===--- CrossTranslationUnit.cpp - -----------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+//  This file provides an interface to load binary AST dumps on demand. This
+//  feature can be utilized for tools that require cross translation unit
+//  support.
+//
+//===----------------------------------------------------------------------===//
+#include "clang/Tooling/CrossTranslationUnit.h"
+#include "clang/AST/ASTImporter.h"
+#include "clang/AST/Decl.h"
+#include "clang/Basic/TargetInfo.h"
+#include "clang/Frontend/ASTUnit.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/TextDiagnosticPrinter.h"
+#include "clang/Index/USRGeneration.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/raw_ostream.h"
+#include <fstream>
+
+namespace clang {
+namespace tooling {
+
+CrossTranslationUnit::CrossTranslationUnit(CompilerInstance &CI)
+    : CI(CI), Context(CI.getASTContext()) {}
+
+CrossTranslationUnit::~CrossTranslationUnit() {}
+
+std::string CrossTranslationUnit::getLookupName(const NamedDecl *ND) {
+  SmallString<128> DeclUSR;
+  bool Ret = index::generateUSRForDecl(ND, DeclUSR);
+  assert(!Ret);
+  llvm::raw_svector_ostream OS(DeclUSR);
+  // To support cross compilation.
+  llvm::Triple::ArchType T = Context.getTargetInfo().getTriple().getArch();
+  if (T == llvm::Triple::thumb)
+    T = llvm::Triple::arm;
+  OS << "@" << Context.getTargetInfo().getTriple().getArchTypeName(T);
+  return OS.str();
+}
+
+/// Recursively visit the funtion decls of a DeclContext, and looks up a
+/// function based on mangled name.
+const FunctionDecl *
+CrossTranslationUnit::findFunctionInDeclContext(const DeclContext *DC,
+                                                StringRef LookupFnName) {
+  if (!DC)
+    return nullptr;
+  for (const Decl *D : DC->decls()) {
+    const auto *SubDC = dyn_cast<DeclContext>(D);
+    if (const auto *FD = findFunctionInDeclContext(SubDC, LookupFnName))
+      return FD;
+
+    const auto *ND = dyn_cast<FunctionDecl>(D);
+    const FunctionDecl *ResultDecl;
+    if (!ND || !ND->hasBody(ResultDecl))
+      continue;
+    // We are already sure that the triple is correct here.
+    if (getLookupName(ResultDecl) != LookupFnName)
+      continue;
+    return ResultDecl;
+  }
+  return nullptr;
+}
+
+const FunctionDecl *
+CrossTranslationUnit::getCTUDefinition(const FunctionDecl *FD, StringRef CTUDir,
+                                       StringRef IndexName) {
+  assert(!FD->hasBody() && "FD has a definition in current translation unit!");
+
+  std::string LookupFnName = getLookupName(FD);
+  if (LookupFnName.empty())
+    return nullptr;
+  ASTUnit *Unit = nullptr;
+  auto FnUnitCacheEntry = FunctionAstUnitMap.find(LookupFnName);
+  if (FnUnitCacheEntry == FunctionAstUnitMap.end()) {
+    if (FunctionFileMap.empty()) {
+      SmallString<256> ExternalFunctionMap = CTUDir;
+      llvm::sys::path::append(ExternalFunctionMap, IndexName);
+      std::ifstream ExternalFnMapFile(ExternalFunctionMap.c_str());
+      if (!ExternalFnMapFile) {
+        llvm::errs() << "error: '" << ExternalFunctionMap
+                     << "' cannot be opened: falling back to non-CTU mode\n";
+        return nullptr;
+      }
+
+      std::string FunctionName, FileName;
+      std::string line;
+      while (std::getline(ExternalFnMapFile, line)) {
+        size_t pos = line.find(" ");
+        FunctionName = line.substr(0, pos);
+        FileName = line.substr(pos + 1);
+        SmallString<256> FilePath = CTUDir;
+        llvm::sys::path::append(FilePath, FileName);
+        FunctionFileMap[FunctionName] = FilePath.str().str();
+      }
+    }
+
+    StringRef ASTFileName;
+    auto It = FunctionFileMap.find(LookupFnName);
+    if (It == FunctionFileMap.end())
+      return nullptr; // No definition found even in some other build unit.
+    ASTFileName = It->second;
+    auto ASTCacheEntry = FileASTUnitMap.find(ASTFileName);
+    if (ASTCacheEntry == FileASTUnitMap.end()) {
+      IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
+      TextDiagnosticPrinter *DiagClient =
+          new TextDiagnosticPrinter(llvm::errs(), &*DiagOpts);
+      IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
+      IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
+          new DiagnosticsEngine(DiagID, &*DiagOpts, DiagClient));
+
+      std::unique_ptr<ASTUnit> LoadedUnit(ASTUnit::LoadFromASTFile(
+          ASTFileName, CI.getPCHContainerOperations()->getRawReader(), Diags,
+          CI.getFileSystemOpts()));
+      Unit = LoadedUnit.get();
+      FileASTUnitMap[ASTFileName] = std::move(LoadedUnit);
+    } else {
+      Unit = ASTCacheEntry->second.get();
+    }
+    FunctionAstUnitMap[LookupFnName] = Unit;
+  } else {
+    Unit = FnUnitCacheEntry->second;
+  }
+
+  if (!Unit)
+    return nullptr;
+  assert(&Unit->getFileManager() ==
+         &Unit->getASTContext().getSourceManager().getFileManager());
+  ASTImporter &Importer = getOrCreateASTImporter(Unit->getASTContext());
+  TranslationUnitDecl *TU = Unit->getASTContext().getTranslationUnitDecl();
+  if (const FunctionDecl *ResultDecl =
+          findFunctionInDeclContext(TU, LookupFnName)) {
+    auto *ToDecl = cast<FunctionDecl>(
+        Importer.Import(const_cast<FunctionDecl *>(ResultDecl)));
+    assert(ToDecl->hasBody());
+    assert(FD->hasBody() && "Functions already imported should have body.");
+    return ToDecl;
+  }
+  return nullptr;
+}
+
+ASTImporter &CrossTranslationUnit::getOrCreateASTImporter(ASTContext &From) {
+  auto I = ASTUnitImporterMap.find(From.getTranslationUnitDecl());
+  if (I != ASTUnitImporterMap.end())
+    return *I->second;
+  ASTImporter *NewImporter =
+      new ASTImporter(Context, Context.getSourceManager().getFileManager(),
+                      From, From.getSourceManager().getFileManager(), false);
+  ASTUnitImporterMap[From.getTranslationUnitDecl()].reset(NewImporter);
+  return *NewImporter;
+}
+
+} // namespace tooling
+} // namespace clang
Index: lib/Tooling/CMakeLists.txt
===================================================================
--- lib/Tooling/CMakeLists.txt
+++ lib/Tooling/CMakeLists.txt
@@ -10,6 +10,7 @@
   ArgumentsAdjusters.cpp
   CommonOptionsParser.cpp
   CompilationDatabase.cpp
+  CrossTranslationUnit.cpp
   FileMatchTrie.cpp
   FixIt.cpp
   JSONCompilationDatabase.cpp
@@ -27,6 +28,7 @@
   clangDriver
   clangFormat
   clangFrontend
+  clangIndex
   clangLex
   clangRewrite
   clangToolingCore
Index: include/clang/Tooling/CrossTranslationUnit.h
===================================================================
--- /dev/null
+++ include/clang/Tooling/CrossTranslationUnit.h
@@ -0,0 +1,68 @@
+//===--- CrossTranslationUnit.h - -------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+//  This file implements the CrossTranslationUnit interface.
+//
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_CLANG_TOOLING_CROSSTRANSLATIONUNIT_H
+#define LLVM_CLANG_TOOLING_CROSSTRANSLATIONUNIT_H
+
+#include "clang/Basic/LLVM.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/StringMap.h"
+
+namespace clang {
+class CompilerInstance;
+class ASTContext;
+class ASTImporter;
+class ASTUnit;
+class DeclContext;
+class FunctionDecl;
+class NamedDecl;
+class TranslationUnitDecl;
+
+namespace tooling {
+
+/// \brief This class can be used for tools that requires cross translation
+///        unit capability.
+///
+/// This class can load function definitions from external AST files.
+/// The loaded definition will be merged back to the original AST using the
+/// AST Importer.
+/// In order to use this class, an index file is required that describes
+/// the locations of the AST files for each function definition.
+///
+/// Note that this class also implements caching.
+class CrossTranslationUnit {
+public:
+  CrossTranslationUnit(CompilerInstance &CI);
+  ~CrossTranslationUnit();
+
+  const FunctionDecl *getCTUDefinition(const FunctionDecl *FD, StringRef CTUDir,
+                                       StringRef IndexName);
+
+private:
+  ASTImporter &getOrCreateASTImporter(ASTContext &From);
+  std::string getLookupName(const NamedDecl *ND);
+  const FunctionDecl *findFunctionInDeclContext(const DeclContext *DC,
+                                                StringRef LookupFnName);
+
+  llvm::StringMap<std::unique_ptr<clang::ASTUnit>> FileASTUnitMap;
+  llvm::StringMap<clang::ASTUnit *> FunctionAstUnitMap;
+  llvm::StringMap<std::string> FunctionFileMap;
+  llvm::DenseMap<TranslationUnitDecl *, std::unique_ptr<ASTImporter>>
+      ASTUnitImporterMap;
+  CompilerInstance &CI;
+  ASTContext &Context;
+};
+
+} // namespace tooling
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLING_CROSSTRANSLATIONUNIT_H
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to