https://github.com/ChuanqiXu9 updated https://github.com/llvm/llvm-project/pull/72956
>From 05453bc0da214ad69ab94d901c997c61fae86ab6 Mon Sep 17 00:00:00 2001 From: Chuanqi Xu <yedeng...@linux.alibaba.com> Date: Fri, 17 Nov 2023 14:57:02 +0800 Subject: [PATCH 1/2] [C++20] [Modules] Introduce a tool 'clang-named-modules-querier' and two plugins 'ClangGetUsedFilesFromModulesPlugin' and 'ClangGetDeclsInModulesPlugin' This patch introduces a tool 'clang-named-modules-querier' and two plugins 'ClangGetUsedFilesFromModulesPlugin' and 'ClangGetDeclsInModulesPlugin' to help the build systems to avoid compilations in modules. After building the clang, we should be able to see `clang-named-modules-querier` in `bin` directory and `ClangGetUsedFilesFromModulesPlugin.so` and `ClangGetDeclsInModulesPlugin.so` in the `lib` directory. --- clang/include/clang/Serialization/ASTReader.h | 4 + clang/lib/Serialization/ASTReader.cpp | 25 +- clang/tools/CMakeLists.txt | 2 +- .../CMakeLists.txt | 27 +++ .../ClangNamedModulesQuerier.cpp | 214 ++++++++++++++++++ .../GetDeclsInfoToJson.h | 48 ++++ .../GetUsedDeclActionPlugin.cpp | 169 ++++++++++++++ .../GetUsedFilesFromModulesPlugin.cpp | 131 +++++++++++ 8 files changed, 613 insertions(+), 7 deletions(-) create mode 100644 clang/tools/clang-named-modules-querier/CMakeLists.txt create mode 100644 clang/tools/clang-named-modules-querier/ClangNamedModulesQuerier.cpp create mode 100644 clang/tools/clang-named-modules-querier/GetDeclsInfoToJson.h create mode 100644 clang/tools/clang-named-modules-querier/GetUsedDeclActionPlugin.cpp create mode 100644 clang/tools/clang-named-modules-querier/GetUsedFilesFromModulesPlugin.cpp diff --git a/clang/include/clang/Serialization/ASTReader.h b/clang/include/clang/Serialization/ASTReader.h index 7eefdca6815cdad..9f028e59b9445d4 100644 --- a/clang/include/clang/Serialization/ASTReader.h +++ b/clang/include/clang/Serialization/ASTReader.h @@ -1981,6 +1981,10 @@ class ASTReader /// lookup table as unmaterialized references. bool FindExternalVisibleDeclsByName(const DeclContext *DC, DeclarationName Name) override; + /// Return false if Name is none and Decl Context doesn't come from the reader. + bool FindVisibleDeclsByName(const DeclContext *DC, DeclarationName Name, + SmallVectorImpl<NamedDecl*> &Decls); + /// Read all of the declarations lexically stored in a /// declaration context. diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index 42b48d230af7a97..141df3beffa0ce8 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -7928,11 +7928,9 @@ void ASTReader::FindFileRegionDecls(FileID File, Decls.push_back(GetDecl(getGlobalDeclID(*DInfo.Mod, *DIt))); } -bool -ASTReader::FindExternalVisibleDeclsByName(const DeclContext *DC, - DeclarationName Name) { - assert(DC->hasExternalVisibleStorage() && DC == DC->getPrimaryContext() && - "DeclContext has no visible decls in storage"); +bool ASTReader::FindVisibleDeclsByName(const DeclContext *DC, + DeclarationName Name, + SmallVectorImpl<NamedDecl*> &Decls) { if (!Name) return false; @@ -7943,7 +7941,6 @@ ASTReader::FindExternalVisibleDeclsByName(const DeclContext *DC, Deserializing LookupResults(this); // Load the list of declarations. - SmallVector<NamedDecl *, 64> Decls; llvm::SmallPtrSet<NamedDecl *, 8> Found; for (DeclID ID : It->second.Table.find(Name)) { NamedDecl *ND = cast<NamedDecl>(GetDecl(ID)); @@ -7951,6 +7948,22 @@ ASTReader::FindExternalVisibleDeclsByName(const DeclContext *DC, Decls.push_back(ND); } + return true; +} + +bool +ASTReader::FindExternalVisibleDeclsByName(const DeclContext *DC, + DeclarationName Name) { + assert(DC->hasExternalVisibleStorage() && DC == DC->getPrimaryContext() && + "DeclContext has no visible decls in storage"); + + Deserializing LookupResults(this); + + // Load the list of declarations. + SmallVector<NamedDecl *, 64> Decls; + if (!FindVisibleDeclsByName(DC, Name, Decls)) + return false; + ++NumVisibleDeclContextsRead; SetExternalVisibleDeclsForName(DC, Name, Decls); return !Decls.empty(); diff --git a/clang/tools/CMakeLists.txt b/clang/tools/CMakeLists.txt index f60db6ef0ba3454..d55b79e51dfa0c5 100644 --- a/clang/tools/CMakeLists.txt +++ b/clang/tools/CMakeLists.txt @@ -17,7 +17,7 @@ if(HAVE_CLANG_REPL_SUPPORT) endif() add_clang_subdirectory(c-index-test) - +add_clang_subdirectory(clang-named-modules-querier) add_clang_subdirectory(clang-rename) add_clang_subdirectory(clang-refactor) # For MinGW we only enable shared library if LLVM_LINK_LLVM_DYLIB=ON. diff --git a/clang/tools/clang-named-modules-querier/CMakeLists.txt b/clang/tools/clang-named-modules-querier/CMakeLists.txt new file mode 100644 index 000000000000000..84e57b904129c78 --- /dev/null +++ b/clang/tools/clang-named-modules-querier/CMakeLists.txt @@ -0,0 +1,27 @@ +set(LLVM_LINK_COMPONENTS + Support + ) + +add_clang_tool(clang-named-modules-querier + ClangNamedModulesQuerier.cpp + PARTIAL_SOURCES_INTENDED + DEPENDS + GENERATE_DRIVER + ) + +set(CLANG_NAMED_MODULES_QUERIER + clangAST + clangBasic + clangFrontend + clangSerialization + clangTooling + ) + +clang_target_link_libraries(clang-named-modules-querier + PRIVATE + ${CLANG_NAMED_MODULES_QUERIER} + ) + +add_llvm_library(ClangGetDeclsInModulesPlugin PARTIAL_SOURCES_INTENDED MODULE GetUsedDeclActionPlugin.cpp PLUGIN_TOOL clang) + +add_llvm_library(ClangGetUsedFilesFromModulesPlugin PARTIAL_SOURCES_INTENDED MODULE GetUsedFilesFromModulesPlugin.cpp PLUGIN_TOOL clang) diff --git a/clang/tools/clang-named-modules-querier/ClangNamedModulesQuerier.cpp b/clang/tools/clang-named-modules-querier/ClangNamedModulesQuerier.cpp new file mode 100644 index 000000000000000..13503578c2307b3 --- /dev/null +++ b/clang/tools/clang-named-modules-querier/ClangNamedModulesQuerier.cpp @@ -0,0 +1,214 @@ +//===- ClangNamedModulesQuerier.cppm --------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "GetDeclsInfoToJson.h" + +#include "clang/Frontend/CompilerInstance.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/Frontend/FrontendAction.h" +#include "clang/Serialization/ASTDeserializationListener.h" +#include "clang/Serialization/ASTReader.h" + +#include "llvm/ADT/StringMap.h" +#include "llvm/Support/LLVMDriver.h" +#include "llvm/Support/JSON.h" + +using namespace clang; + +class DeclsQueryAction : public ASTFrontendAction { + std::vector<std::string> QueryingDeclNames; + llvm::json::Array JsonOutput; + +public: + DeclsQueryAction(std::vector<std::string> &&QueryingDeclNames) : + QueryingDeclNames(QueryingDeclNames) {} + + bool BeginInvocation(CompilerInstance &CI) override { + CI.getHeaderSearchOpts().ModuleFormat = "raw"; + return true; + } + + DeclContext *getDeclContextByName(ASTReader *Reader, StringRef Name); + std::optional<SmallVector<NamedDecl *>> getDeclsByName(ASTReader *Reader, StringRef Name); + + std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) override { + return std::make_unique<ASTConsumer>(); + } + + void QueryDecls(ASTReader *Reader, StringRef Name); + + void ExecuteAction() override { + assert(isCurrentFileAST() && "dumping non-AST?"); + + ASTReader *Reader = getCurrentASTUnit().getASTReader().get(); + serialization::ModuleFile &MF = Reader->getModuleManager().getPrimaryModule(); + if (!MF.StandardCXXModule) { + llvm::errs() << "We should only consider standard C++20 Modules.\n"; + return; + } + + for (auto &Name : QueryingDeclNames) + QueryDecls(Reader, Name); + + CompilerInstance &CI = getCompilerInstance(); + std::unique_ptr<raw_pwrite_stream> OS = CI.createDefaultOutputFile(/*Binary=*/false); + if (!OS) { + llvm::errs() << "Failed to create output file\n"; + return; + } + + using namespace llvm::json; + *OS << llvm::formatv("{0:2}\n", Value(std::move(JsonOutput))); + } +}; + +static DeclContext *getDeclContext(NamedDecl *ND) { + if (auto *CTD = dyn_cast<ClassTemplateDecl>(ND)) + return CTD->getTemplatedDecl(); + + return dyn_cast<DeclContext>(ND); +} + +static DeclContext *getDeclContextFor(const SmallVector<NamedDecl *> &DCCandidates) { + DeclContext *Result = nullptr; + + for (auto *ND : DCCandidates) { + auto *DC = getDeclContext(ND); + if (!DC) + continue; + + if (!Result) + Result = DC->getPrimaryContext(); + else if (Result == DC->getPrimaryContext()) + continue; + else { + llvm::errs() << "Found multiple decl context: \n"; + cast<Decl>(Result)->dump(); + cast<Decl>(DC)->dump(); + } + } + + return Result; +} + +DeclContext *DeclsQueryAction::getDeclContextByName(ASTReader *Reader, StringRef Name) { + if (Name.empty()) + return Reader->getContext().getTranslationUnitDecl(); + + std::optional<SmallVector<NamedDecl *>> DCCandidates = getDeclsByName(Reader, Name); + if (!DCCandidates || DCCandidates->empty()) + return nullptr; + + return getDeclContextFor(*DCCandidates); +} + +std::optional<SmallVector<NamedDecl *>> DeclsQueryAction::getDeclsByName(ASTReader *Reader, StringRef Name) { + if (Name.endswith("::")) + return std::nullopt; + + auto [ParentName, UnqualifiedName] = Name.rsplit("::"); + + // This implies that "::" is not in the Name. + if (ParentName == Name) { + UnqualifiedName = Name; + ParentName = StringRef(); + } + + DeclContext *DC = getDeclContextByName(Reader, ParentName); + if (!DC) + return std::nullopt; + + IdentifierInfo *II = Reader->get(UnqualifiedName); + + if (!II) + return std::nullopt; + + llvm::SmallVector<NamedDecl *> Decls; + Reader->FindVisibleDeclsByName(DC, DeclarationName(II), Decls); + + // TODO: Should we filter here? + return Decls; +} + +void DeclsQueryAction::QueryDecls(ASTReader *Reader, StringRef Name) { + using namespace llvm::json; + + std::optional<SmallVector<NamedDecl *>> Decls = getDeclsByName(Reader, Name); + if (!Decls) { + JsonOutput.push_back(Object{{Name, "invalid name"}}); + return; + } + + SourceManager &SMgr = Reader->getSourceManager(); + + // TODO: Handle overloads here. + for (NamedDecl *ND : *Decls) + JsonOutput.push_back(getDeclInJson(ND, SMgr)); +} + +// TODO: Print --help information +// TODO: Add -resource-dir automatically +int clang_named_modules_querier_main(int argc, char **argv, const llvm::ToolContext &) { + IntrusiveRefCntPtr<DiagnosticsEngine> Diags = + CompilerInstance::createDiagnostics(new DiagnosticOptions()); + CreateInvocationOptions CIOpts; + CIOpts.Diags = Diags; + CIOpts.VFS = llvm::vfs::createPhysicalFileSystem(); + + llvm::ArrayRef<const char *> Args(argv, argv + argc); + if (llvm::find_if(Args, [](auto &&Arg) { + return std::strcmp(Arg, "--help") == 0; + }) != Args.end()) { + llvm::outs() << R"cpp( +To query the decls from module files. + +Syntax: + + clang-named-modules-querier module-file - <decl-names-to-be-queried>... + +For example: + + clang-named-modules-querier a.pcm -- a nn::a Templ::get + +The unqualified name are treated as if it is under the global namespace. + +The output information contains kind of the declaration, source file name, +line and col number and the hash value of declaration. + )cpp"; + return 0; + } + + auto DashDashIter = llvm::find_if(Args, [](auto &&V){ + return std::strcmp(V, "--") == 0; + }); + + std::vector<std::string> QueryingDeclNames; + auto Iter = DashDashIter; + // Don't record "--". + if (Iter != Args.end()) + Iter++; + while (Iter != Args.end()) + QueryingDeclNames.push_back(std::string(*Iter++)); + + if (QueryingDeclNames.empty()) { + llvm::errs() << "We need pass the names that need to be queried after '--'"; + return 0; + } + + std::shared_ptr<CompilerInvocation> Invocation = + createInvocation(llvm::ArrayRef<const char *>(argv, DashDashIter), CIOpts); + + CompilerInstance Instance; + Instance.setDiagnostics(Diags.get()); + Instance.setInvocation(Invocation); + DeclsQueryAction Action(std::move(QueryingDeclNames)); + Instance.ExecuteAction(Action); + + return 0; +} diff --git a/clang/tools/clang-named-modules-querier/GetDeclsInfoToJson.h b/clang/tools/clang-named-modules-querier/GetDeclsInfoToJson.h new file mode 100644 index 000000000000000..ff250a87c0e1592 --- /dev/null +++ b/clang/tools/clang-named-modules-querier/GetDeclsInfoToJson.h @@ -0,0 +1,48 @@ +//===- GetDeclsInfoToJson.h -----------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef CLANG_TOOLS_CLANG_NAMED_MODULES_QUERIER_GET_DECLS_INFO_TO_JSON_H +#define CLANG_TOOLS_CLANG_NAMED_MODULES_QUERIER_GET_DECLS_INFO_TO_JSON_H + +#include "clang/AST/Decl.h" +#include "clang/AST/ODRHash.h" +#include "clang/Basic/SourceManager.h" +#include "llvm/Support/JSON.h" + +namespace clang { +inline unsigned getHashValue(const NamedDecl *ND) { + ODRHash Hasher; + + if (auto *FD = dyn_cast<FunctionDecl>(ND)) + Hasher.AddFunctionDecl(FD); + else if (auto *ED = dyn_cast<EnumDecl>(ND)) + Hasher.AddEnumDecl(ED); + else if (auto *CRD = dyn_cast<CXXRecordDecl>(ND)) + Hasher.AddCXXRecordDecl(CRD); + else { + Hasher.AddDecl(ND); + Hasher.AddSubDecl(ND); + } + + return Hasher.CalculateHash(); +} + +inline llvm::json::Object getDeclInJson(const NamedDecl *ND, SourceManager &SMgr) { + llvm::json::Object DeclObject; + DeclObject.try_emplace("kind", ND->getDeclKindName()); + FullSourceLoc FSL(ND->getLocation(), SMgr); + const FileEntry *FE = SMgr.getFileEntryForID(FSL.getFileID()); + DeclObject.try_emplace("source File Name", FE ? FE->getName() : "Unknown Source File"); + DeclObject.try_emplace("line", FSL.getSpellingLineNumber()); + DeclObject.try_emplace("col", FSL.getSpellingColumnNumber()); + DeclObject.try_emplace("Hash", getHashValue(ND)); + return llvm::json::Object({{ND->getQualifiedNameAsString(), std::move(DeclObject)}}); +} +} + +#endif diff --git a/clang/tools/clang-named-modules-querier/GetUsedDeclActionPlugin.cpp b/clang/tools/clang-named-modules-querier/GetUsedDeclActionPlugin.cpp new file mode 100644 index 000000000000000..a66eecbb9423324 --- /dev/null +++ b/clang/tools/clang-named-modules-querier/GetUsedDeclActionPlugin.cpp @@ -0,0 +1,169 @@ +//===- GetUsedDeclActionPlugin.cpp -----------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "GetDeclsInfoToJson.h" + +#include "clang/Frontend/FrontendPluginRegistry.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/Frontend/FrontendAction.h" +#include "clang/Serialization/ASTDeserializationListener.h" + +#include "llvm/ADT/StringMap.h" +#include "llvm/Support/JSON.h" +#include "llvm/Support/Path.h" + +namespace clang { + +class DeclsQuerier : public ASTDeserializationListener { +public: + void DeclRead(serialization::DeclID ID, const Decl *D) override { + // We only cares about function decls, var decls, tag decls (class, struct, enum, union). + if (!isa<NamedDecl>(D)) + return; + + // We only records the template declaration if the declaration is placed in templates. + if (auto *FD = dyn_cast<FunctionDecl>(D); FD && FD->getDescribedFunctionTemplate()) + return; + + if (auto *VD = dyn_cast<VarDecl>(D); VD && VD->getDescribedVarTemplate()) + return; + + if (auto *CRD = dyn_cast<CXXRecordDecl>(D); CRD && CRD->getDescribedClassTemplate()) + return; + + if (isa<TemplateTypeParmDecl, NonTypeTemplateParmDecl, TemplateTemplateParmDecl>(D)) + return; + + // We don't care about declarations in function scope. + if (isa<FunctionDecl>(D->getDeclContext())) + return; + + // Skip implicit declarations. + if (D->isImplicit()) + return; + + Module *M = D->getOwningModule(); + // We only cares about C++20 Named Modules. + if (!M || !M->getTopLevelModule()->isNamedModule()) + return; + + StringRef ModuleName = M->Name; + auto Iter = Names.find(ModuleName); + if (Iter == Names.end()) + Iter = Names.try_emplace(ModuleName, std::vector<const NamedDecl*>()).first; + + Iter->second.push_back(cast<NamedDecl>(D)); + } + + llvm::StringMap<std::vector<const NamedDecl *>> Names; +}; + +class DeclsQuerierConsumer : public ASTConsumer { + CompilerInstance &CI; + StringRef InFile; + std::string OutputFile; + DeclsQuerier Querier; + +public: + DeclsQuerierConsumer(CompilerInstance &CI, StringRef InFile, StringRef OutputFile) + : CI(CI), InFile(InFile), OutputFile(OutputFile) {} + + ASTDeserializationListener *GetASTDeserializationListener() override { + return &Querier; + } + + std::unique_ptr<raw_pwrite_stream> getOutputFile() { + if (OutputFile.empty()) { + llvm::SmallString<256> Path(InFile); + llvm::sys::path::replace_extension(Path, "used_external_decls.json"); + OutputFile = (std::string)Path; + } + + std::error_code EC; + auto OS = std::make_unique<llvm::raw_fd_ostream>(OutputFile, EC); + if (EC) + return nullptr; + + return OS; + } + + void HandleTranslationUnit(ASTContext &Ctx) override { + std::unique_ptr<raw_pwrite_stream> OS = getOutputFile(); + if (!OS) + return; + + using namespace llvm::json; + + Array Modules; + + for (auto &Iter : Querier.Names) { + Object ModulesInfo; + + StringRef ModuleName = Iter.first(); + ModulesInfo.try_emplace("module", ModuleName); + + std::vector<const NamedDecl *> Decls(Iter.second); + Array DeclsInJson; + for (auto *ND : Decls) + DeclsInJson.push_back(getDeclInJson(ND, Ctx.getSourceManager())); + + ModulesInfo.try_emplace("decls", std::move(DeclsInJson)); + Modules.push_back(std::move(ModulesInfo)); + } + + *OS << llvm::formatv("{0:2}\n", Value(std::move(Modules))); + } +}; + +void PrintHelp(); + +class DeclsQueryAction : public PluginASTAction { + std::string OutputFile; + +public: + DeclsQueryAction(StringRef OutputFile) : OutputFile(OutputFile) {} + DeclsQueryAction() = default; + + + std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) override { + return std::make_unique<DeclsQuerierConsumer>(CI, InFile, OutputFile); + } + + ActionType getActionType() override { return AddAfterMainAction; } + + bool ParseArgs(const CompilerInstance &CI, + const std::vector<std::string> &Args) override { + for (auto &Arg : Args) { + if (StringRef(Arg).startswith("output=")) { + OutputFile = StringRef(Arg).split('=').second; + } else { + PrintHelp(); + return false; + } + } + + return true; + } +}; + +void PrintHelp() { + llvm::outs() << R"cpp( +To get used decls from modules. + +The output is printed to the std output by default when use it as a standalone tool. + +If you're using plugin, use -fplugin-arg-decls_query_from_modules-output=<output-file> +to specify the output path of used decls. + )cpp"; +} +} + +static clang::FrontendPluginRegistry::Add<clang::DeclsQueryAction> +X("decls_query_from_modules", "query used decls from modules"); diff --git a/clang/tools/clang-named-modules-querier/GetUsedFilesFromModulesPlugin.cpp b/clang/tools/clang-named-modules-querier/GetUsedFilesFromModulesPlugin.cpp new file mode 100644 index 000000000000000..bf244861e2bfb83 --- /dev/null +++ b/clang/tools/clang-named-modules-querier/GetUsedFilesFromModulesPlugin.cpp @@ -0,0 +1,131 @@ +//===- ClangGetUsedFilesFromModules.cpp -----------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/FrontendPluginRegistry.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/Frontend/FrontendAction.h" +#include "clang/Serialization/ASTDeserializationListener.h" + +#include "llvm/ADT/StringMap.h" +#include "llvm/Support/Path.h" + +namespace clang { + +class DeclsQuerier : public ASTDeserializationListener { +public: + void DeclRead(serialization::DeclID ID, const Decl *D) override { + // Filter Decl's to avoid store too many informations. + if (!D->getLexicalDeclContext()) + return; + + if (!isa<FunctionDecl>(D) && + !D->getLexicalDeclContext()->getRedeclContext()->isFileContext()) + return; + + ASTContext &Ctx = D->getASTContext(); + SourceManager &SMgr = Ctx.getSourceManager(); + FullSourceLoc FSL(D->getLocation(), SMgr); + if (!FSL.isValid()) + return; + + FileIDSet.insert(FSL.getFileID()); + } + + llvm::DenseSet<FileID> FileIDSet; +}; + +class DeclsQuerierConsumer : public ASTConsumer { + CompilerInstance &CI; + StringRef InFile; + std::string OutputFile; + DeclsQuerier Querier; + +public: + DeclsQuerierConsumer(CompilerInstance &CI, StringRef InFile, StringRef OutputFile) + : CI(CI), InFile(InFile), OutputFile(OutputFile) {} + + ASTDeserializationListener *GetASTDeserializationListener() override { + return &Querier; + } + + std::unique_ptr<raw_pwrite_stream> getOutputFile() { + if (OutputFile.empty()) { + llvm::SmallString<256> Path(InFile); + llvm::sys::path::replace_extension(Path, "used_files.txt"); + OutputFile = (std::string)Path; + } + + std::error_code EC; + auto OS = std::make_unique<llvm::raw_fd_ostream>(OutputFile, EC); + if (EC) + return nullptr; + + return OS; + } + + void HandleTranslationUnit(ASTContext &Ctx) override { + std::unique_ptr<raw_pwrite_stream> OS = getOutputFile(); + if (!OS) + return; + + auto &SMgr = Ctx.getSourceManager(); + + for (const auto &FID : Querier.FileIDSet) { + const FileEntry *FE = SMgr.getFileEntryForID(FID); + if (!FE) + continue; + + *OS << FE->getName() << "\n"; + } + } +}; + +void PrintHelp(); + +class DeclsQueryAction : public PluginASTAction { + std::string OutputFile; + +public: + DeclsQueryAction(StringRef OutputFile) : OutputFile(OutputFile) {} + DeclsQueryAction() = default; + + std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) override { + return std::make_unique<DeclsQuerierConsumer>(CI, InFile, OutputFile); + } + + ActionType getActionType() override { return AddAfterMainAction; } + + bool ParseArgs(const CompilerInstance &CI, + const std::vector<std::string> &Args) override { + for (auto &Arg : Args) { + if (StringRef(Arg).startswith("output=")) { + OutputFile = StringRef(Arg).split('=').second.str(); + } else { + PrintHelp(); + return false; + } + } + + return true; + } +}; + +void PrintHelp() { + llvm::outs() << R"cpp( +To get used decls from modules. + +If you're using plugin, use -fplugin-arg-get_used_files_from_modules-output=<output-file> +to specify the output path of used files. + )cpp"; +} +} + +static clang::FrontendPluginRegistry::Add<clang::DeclsQueryAction> +X("get_used_files_from_modules", "get used files from modules"); >From 85afc3a4aaa2d17b7cda318b75b874157f5060eb Mon Sep 17 00:00:00 2001 From: Chuanqi Xu <yedeng...@linux.alibaba.com> Date: Tue, 21 Nov 2023 15:54:33 +0800 Subject: [PATCH 2/2] Don't add more decls when we computes the hash value --- .../CMakeLists.txt | 2 +- .../GetDeclsInfoToJson.h | 9 +++++---- ....cpp => GetUsedDeclsFromModulesPlugin.cpp} | 19 +++++++++++++++---- .../GetUsedFilesFromModulesPlugin.cpp | 6 ++++-- 4 files changed, 25 insertions(+), 11 deletions(-) rename clang/tools/clang-named-modules-querier/{GetUsedDeclActionPlugin.cpp => GetUsedDeclsFromModulesPlugin.cpp} (89%) diff --git a/clang/tools/clang-named-modules-querier/CMakeLists.txt b/clang/tools/clang-named-modules-querier/CMakeLists.txt index 84e57b904129c78..a963e983a66769e 100644 --- a/clang/tools/clang-named-modules-querier/CMakeLists.txt +++ b/clang/tools/clang-named-modules-querier/CMakeLists.txt @@ -22,6 +22,6 @@ clang_target_link_libraries(clang-named-modules-querier ${CLANG_NAMED_MODULES_QUERIER} ) -add_llvm_library(ClangGetDeclsInModulesPlugin PARTIAL_SOURCES_INTENDED MODULE GetUsedDeclActionPlugin.cpp PLUGIN_TOOL clang) +add_llvm_library(ClangGetUsedDeclsFromModulesPlugin PARTIAL_SOURCES_INTENDED MODULE GetUsedDeclsFromModulesPlugin.cpp PLUGIN_TOOL clang) add_llvm_library(ClangGetUsedFilesFromModulesPlugin PARTIAL_SOURCES_INTENDED MODULE GetUsedFilesFromModulesPlugin.cpp PLUGIN_TOOL clang) diff --git a/clang/tools/clang-named-modules-querier/GetDeclsInfoToJson.h b/clang/tools/clang-named-modules-querier/GetDeclsInfoToJson.h index ff250a87c0e1592..ed28ef16ccbc157 100644 --- a/clang/tools/clang-named-modules-querier/GetDeclsInfoToJson.h +++ b/clang/tools/clang-named-modules-querier/GetDeclsInfoToJson.h @@ -10,6 +10,7 @@ #define CLANG_TOOLS_CLANG_NAMED_MODULES_QUERIER_GET_DECLS_INFO_TO_JSON_H #include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" #include "clang/AST/ODRHash.h" #include "clang/Basic/SourceManager.h" #include "llvm/Support/JSON.h" @@ -19,11 +20,11 @@ inline unsigned getHashValue(const NamedDecl *ND) { ODRHash Hasher; if (auto *FD = dyn_cast<FunctionDecl>(ND)) - Hasher.AddFunctionDecl(FD); + return FD->getODRHash(); else if (auto *ED = dyn_cast<EnumDecl>(ND)) - Hasher.AddEnumDecl(ED); - else if (auto *CRD = dyn_cast<CXXRecordDecl>(ND)) - Hasher.AddCXXRecordDecl(CRD); + return const_cast<EnumDecl*>(ED)->getODRHash(); + else if (auto *CRD = dyn_cast<CXXRecordDecl>(ND); CRD && CRD->hasDefinition()) + return CRD->getODRHash(); else { Hasher.AddDecl(ND); Hasher.AddSubDecl(ND); diff --git a/clang/tools/clang-named-modules-querier/GetUsedDeclActionPlugin.cpp b/clang/tools/clang-named-modules-querier/GetUsedDeclsFromModulesPlugin.cpp similarity index 89% rename from clang/tools/clang-named-modules-querier/GetUsedDeclActionPlugin.cpp rename to clang/tools/clang-named-modules-querier/GetUsedDeclsFromModulesPlugin.cpp index a66eecbb9423324..43613ed11d9bbc7 100644 --- a/clang/tools/clang-named-modules-querier/GetUsedDeclActionPlugin.cpp +++ b/clang/tools/clang-named-modules-querier/GetUsedDeclsFromModulesPlugin.cpp @@ -1,4 +1,4 @@ -//===- GetUsedDeclActionPlugin.cpp -----------------------------------------===// +//===- GetUsedDeclsFromModulesPlugin.cpp ----------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -23,10 +23,17 @@ namespace clang { class DeclsQuerier : public ASTDeserializationListener { public: void DeclRead(serialization::DeclID ID, const Decl *D) override { + if (Stopped) + return; + // We only cares about function decls, var decls, tag decls (class, struct, enum, union). if (!isa<NamedDecl>(D)) return; + // Filter Decl's to avoid store too many informations. + if (!D->getLexicalDeclContext()) + return; + // We only records the template declaration if the declaration is placed in templates. if (auto *FD = dyn_cast<FunctionDecl>(D); FD && FD->getDescribedFunctionTemplate()) return; @@ -62,6 +69,7 @@ class DeclsQuerier : public ASTDeserializationListener { } llvm::StringMap<std::vector<const NamedDecl *>> Names; + bool Stopped = false; }; class DeclsQuerierConsumer : public ASTConsumer { @@ -97,6 +105,10 @@ class DeclsQuerierConsumer : public ASTConsumer { std::unique_ptr<raw_pwrite_stream> OS = getOutputFile(); if (!OS) return; + + /// Otherwise the process of computing ODR Hash may involve more decls + /// get deserialized. + Querier.Stopped = true; using namespace llvm::json; @@ -130,7 +142,6 @@ class DeclsQueryAction : public PluginASTAction { DeclsQueryAction(StringRef OutputFile) : OutputFile(OutputFile) {} DeclsQueryAction() = default; - std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, StringRef InFile) override { return std::make_unique<DeclsQuerierConsumer>(CI, InFile, OutputFile); @@ -159,11 +170,11 @@ To get used decls from modules. The output is printed to the std output by default when use it as a standalone tool. -If you're using plugin, use -fplugin-arg-decls_query_from_modules-output=<output-file> +If you're using plugin, use -fplugin-arg-get_used_decls_from_modules-output=<output-file> to specify the output path of used decls. )cpp"; } } static clang::FrontendPluginRegistry::Add<clang::DeclsQueryAction> -X("decls_query_from_modules", "query used decls from modules"); +X("get_used_decls_from_modules", "query used decls from modules"); diff --git a/clang/tools/clang-named-modules-querier/GetUsedFilesFromModulesPlugin.cpp b/clang/tools/clang-named-modules-querier/GetUsedFilesFromModulesPlugin.cpp index bf244861e2bfb83..d1c132f2e6a6aa0 100644 --- a/clang/tools/clang-named-modules-querier/GetUsedFilesFromModulesPlugin.cpp +++ b/clang/tools/clang-named-modules-querier/GetUsedFilesFromModulesPlugin.cpp @@ -20,11 +20,13 @@ namespace clang { class DeclsQuerier : public ASTDeserializationListener { public: void DeclRead(serialization::DeclID ID, const Decl *D) override { - // Filter Decl's to avoid store too many informations. + if (!isa<NamedDecl>(D)) + return; + if (!D->getLexicalDeclContext()) return; - if (!isa<FunctionDecl>(D) && + if (!isa<FunctionDecl>(D) && !D->getLexicalDeclContext()->getRedeclContext()->isFileContext()) return; _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits