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> FileASTUnitMap; + llvm::StringMap FunctionAstUnitMap; + llvm::StringMap FunctionFileMap; + llvm::DenseMap> + ASTUnitImporterMap; + CompilerInstance &CI; + ASTContext &Context; +}; + +} // namespace tooling +} // namespace clang + +#endif // LLVM_CLANG_TOOLING_CROSSTRANSLATIONUNIT_H 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: 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 + +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(D); + if (const auto *FD = findFunctionInDeclContext(SubDC, LookupFnName)) + return FD; + + const auto *ND = dyn_cast(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 DiagOpts = new DiagnosticOptions(); + TextDiagnosticPrinter *DiagClient = + new TextDiagnosticPrinter(llvm::errs(), &*DiagOpts); + IntrusiveRefCntPtr DiagID(new DiagnosticIDs()); + IntrusiveRefCntPtr Diags( + new DiagnosticsEngine(DiagID, &*DiagOpts, DiagClient)); + + std::unique_ptr 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( + Importer.Import(const_cast(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: tools/clang-func-mapping/CMakeLists.txt =================================================================== --- /dev/null +++ tools/clang-func-mapping/CMakeLists.txt @@ -0,0 +1,21 @@ +set(LLVM_LINK_COMPONENTS + ${LLVM_TARGETS_TO_BUILD} + asmparser + support + mc + ) + +add_clang_executable(clang-func-mapping + ClangFnMapGen.cpp + ) + +target_link_libraries(clang-func-mapping + clangAST + clangBasic + clangFrontend + clangIndex + clangTooling + ) + +install(TARGETS clang-func-mapping + RUNTIME DESTINATION bin) Index: tools/clang-func-mapping/ClangFnMapGen.cpp =================================================================== --- /dev/null +++ tools/clang-func-mapping/ClangFnMapGen.cpp @@ -0,0 +1,127 @@ +//===- ClangFnMapGen.cpp -----------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===--------------------------------------------------------------------===// +// +// Clang tool which creates a list of defined functions and the files in which +// they are defined. +// +//===--------------------------------------------------------------------===// + +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/GlobalDecl.h" +#include "clang/AST/Mangle.h" +#include "clang/AST/StmtVisitor.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendActions.h" +#include "clang/Index/USRGeneration.h" +#include "clang/Tooling/CommonOptionsParser.h" +#include "clang/Tooling/Tooling.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Signals.h" +#include +#include +#include + +using namespace llvm; +using namespace clang; +using namespace clang::tooling; + +static cl::OptionCategory ClangFnMapGenCategory("clang-fnmapgen options"); + +class MapFunctionNamesConsumer : public ASTConsumer { +public: + MapFunctionNamesConsumer(ASTContext &Context) : Ctx(Context) {} + + ~MapFunctionNamesConsumer() { + // Flush results to standard output. + llvm::outs() << DefinedFuncsStr.str(); + } + + virtual void HandleTranslationUnit(ASTContext &Ctx) { + handleDecl(Ctx.getTranslationUnitDecl()); + } + +private: + std::string getLookupName(const FunctionDecl *FD); + void handleDecl(const Decl *D); + + ASTContext &Ctx; + std::stringstream DefinedFuncsStr; + std::string CurrentFileName; +}; + +void MapFunctionNamesConsumer::handleDecl(const Decl *D) { + if (!D) + return; + + if (const auto *FD = dyn_cast(D)) { + if (FD->isThisDeclarationADefinition()) { + if (const Stmt *Body = FD->getBody()) { + SmallString<128> LookupName; + bool Res = index::generateUSRForDecl(D, LookupName); + assert(!Res); + const SourceManager &SM = Ctx.getSourceManager(); + if (CurrentFileName.empty()) { + StringRef SMgrName = + SM.getFileEntryForID(SM.getMainFileID())->getName(); + char *Path = realpath(SMgrName.str().c_str(), nullptr); + CurrentFileName = Path; + free(Path); + } + + switch (FD->getLinkageInternal()) { + case ExternalLinkage: + case VisibleNoLinkage: + case UniqueExternalLinkage: + if (SM.isInMainFile(Body->getLocStart())) + DefinedFuncsStr << LookupName.str().str() << " " << CurrentFileName + << "\n"; + default: + break; + } + } + } + } + + if (const auto *DC = dyn_cast(D)) + for (const Decl *D : DC->decls()) + handleDecl(D); +} + +class MapFunctionNamesAction : public ASTFrontendAction { +protected: + std::unique_ptr CreateASTConsumer(CompilerInstance &CI, + llvm::StringRef) { + std::unique_ptr PFC( + new MapFunctionNamesConsumer(CI.getASTContext())); + return PFC; + } +}; + +static cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage); + +int main(int argc, const char **argv) { + // Print a stack trace if we signal out. + sys::PrintStackTraceOnErrorSignal(argv[0], false); + PrettyStackTraceProgram X(argc, argv); + + const char *Overview = "\nThis tool collects the USR name and location " + "of all functions definitions in the source files " + "(excluding headers).\n"; + CommonOptionsParser OptionsParser(argc, argv, ClangFnMapGenCategory, + cl::ZeroOrMore, Overview); + + ClangTool Tool(OptionsParser.getCompilations(), + OptionsParser.getSourcePathList()); + Tool.run(newFrontendActionFactory().get()); + return 0; +}