Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
[skip-ci][cxxmodules][cling] Avoid loading some unnecessary modules
When we run into an unkown identifier that is a namespace, we don't
really need to load its corresponding modules. Instead, we create a new
module that forward declared all namespaces and always load it first. By
doing so, we can avoid loading a lot of unnecessary modules.
Signed-off-by: Jun Zhang <[email protected]>
  • Loading branch information
junaire committed Oct 23, 2022
commit 6c4c17f82fd5e12d42a4c9358127e07ce9f026fb
5 changes: 5 additions & 0 deletions build/unix/module.modulemap
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ module ROOT_Foundation_C {
export *
}

module ROOT_FwdNamespaces {
module "AutoGeneratedForwardDeclaredNamespacesForGMI" { header "AutoGeneratedForwardDeclaredNamespacesForGMI.h" export *}
export *
}

// This module contains header files from module Core which are used as
// configuration for ROOT. They contain a lot of macro definitions which are
// supposed to be textually expanded in each TU.
Expand Down
83 changes: 76 additions & 7 deletions core/metacling/src/TCling.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ clang/LLVM technology.
#include "llvm/Support/DynamicLibrary.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Program.h"
#include "llvm/Support/Process.h"
#include "llvm/Object/ELFObjectFile.h"
#include "llvm/Object/ObjectFile.h"
Expand Down Expand Up @@ -1084,6 +1085,8 @@ static bool HasASTFileOnDisk(clang::Module *M, const clang::Preprocessor &PP, st
}

static bool HaveFullGlobalModuleIndex = false;
static const std::string GeneratedFwdNamespaces = (TROOT::GetIncludeDir() + "AutoGeneratedForwardDeclaredNamespacesForGMI.h").Data();

static GlobalModuleIndex *loadGlobalModuleIndex(cling::Interpreter &interp)
{
CompilerInstance &CI = *interp.getCI();
Expand Down Expand Up @@ -1124,6 +1127,8 @@ static GlobalModuleIndex *loadGlobalModuleIndex(cling::Interpreter &interp)
TraverseDecl(TU);
}
bool VisitNamedDecl(NamedDecl *ND) {
if (ND->isInStdNamespace())
return true;
if (!ND->isFromASTFile())
return true;
if (!ND->getIdentifier())
Expand All @@ -1135,17 +1140,22 @@ static GlobalModuleIndex *loadGlobalModuleIndex(cling::Interpreter &interp)
if (TagDecl *TD = llvm::dyn_cast<TagDecl>(ND)) {
if (TD->isCompleteDefinition())
Register(TD);
} else if (NamespaceDecl *NSD = llvm::dyn_cast<NamespaceDecl>(ND)) {
Register(NSD, /*AddSingleEntry=*/ false);
}
else if (TypedefNameDecl *TND = dyn_cast<TypedefNameDecl>(ND))
} else if (TypedefNameDecl *TND = dyn_cast<TypedefNameDecl>(ND))
Register(TND);
else if (TypeAliasDecl *TAD = dyn_cast<TypeAliasDecl>(ND))
Register(TAD);
else if (isa<FunctionDecl>(ND) && !isa<CXXMethodDecl>(ND))
Register(ND);
// FIXME: Add the rest...
return true; // continue decending
}

private:
clang::GlobalModuleIndex::UserDefinedInterestingIDs &DefinitionIDs;
void Register(const NamedDecl* ND, bool AddSingleEntry = true) {
llvm::StringMap<llvm::SmallVector<const Decl *, 8>> Canonicals;

void Register(const NamedDecl *ND)
{
assert(ND->isFromASTFile());
// FIXME: All decls should have an owning module once rootcling
// updates its generated decls from within the LookupHelper & co.
Expand All @@ -1163,17 +1173,74 @@ static GlobalModuleIndex *loadGlobalModuleIndex(cling::Interpreter &interp)
Module *OwningModule = ND->getOwningModule()->getTopLevelModule();
assert(OwningModule);
assert(!ND->getName().empty() && "Empty name");
if (AddSingleEntry && DefinitionIDs.count(ND->getName()))
return;
if (Canonicals.count(ND->getName())) {
if (llvm::is_contained(Canonicals[ND->getName()], ND->getCanonicalDecl()))
return;
}

// FIXME: The FileEntry in not stable to serialize.
// FIXME: We might end up with many times with the same module.
// FIXME: We might end up two modules containing a definition.
// FIXME: What do we do if no definition is found.
DefinitionIDs[ND->getName()].push_back(OwningModule->getASTFile());
Canonicals[ND->getName()].push_back(ND->getCanonicalDecl());
}
};
DefinitionFinder defFinder(IDs, CI.getASTContext().getTranslationUnitDecl());

struct NamespaceDeclarer : public RecursiveASTVisitor<NamespaceDeclarer> {
std::string Fwds;
std::unordered_set<const NamespaceDecl *> Namespaces;

NamespaceDeclarer(clang::TranslationUnitDecl *TU) { TraverseDecl(TU); }
std::string PrintNamespaceOpen(NamespaceDecl *NSD)
{
std::string result;
if (NSD->isInline())
result += "inline ";
result += "namespace " + NSD->getNameAsString() + "{";
return result;
}
bool VisitNamespaceDecl(NamespaceDecl *NSD)
{
if (NSD->isAnonymousNamespace() || NSD->isInline() || NSD->isInStdNamespace())
return true;
if (Namespaces.count(NSD->getCanonicalDecl()))
return true;

std::vector<NamespaceDecl *> Parents;
// NS1::NS2::NS3-> namespace NS1 { namespace NS2 {namespace NS3{}}}
for (DeclContext *DC = NSD; DC && !DC->isTranslationUnit(); DC = DC->getParent()) {
if (auto D = dyn_cast<NamespaceDecl>(DC)) {
if (D->isAnonymousNamespace() || D->isInline() || D->isInStdNamespace())
continue;
Namespaces.insert(D);
Parents.push_back(D);
}
}
std::reverse(Parents.begin(), Parents.end());

for (auto *D : Parents)
Fwds += PrintNamespaceOpen(D);
Fwds += std::string(Parents.size(), '}');
Fwds += "\n";

Namespaces.insert(NSD->getCanonicalDecl());
for (Decl *D : NSD->decls()) {
if (!isa<NamespaceDecl>(D))
continue;
TraverseDecl(D);
}
return true;
}
};

if (!llvm::sys::fs::exists(GeneratedFwdNamespaces)) {
NamespaceDeclarer NSDeclarer(CI.getASTContext().getTranslationUnitDecl());

llvm::sys::writeFileWithEncoding(GeneratedFwdNamespaces, NSDeclarer.Fwds);
}

llvm::cantFail(GlobalModuleIndex::writeIndex(CI.getFileManager(),
CI.getPCHContainerReader(),
ModuleIndexPath,
Expand All @@ -1196,6 +1263,8 @@ static void RegisterCxxModules(cling::Interpreter &clingInterp)
cling::Interpreter::PushTransactionRAII deserRAII(&clingInterp);

// Setup core C++ modules if we have any to setup.
if (llvm::sys::fs::exists(GeneratedFwdNamespaces))
LoadModule("ROOT_FwdNamespaces", clingInterp);

// Load libc and stl first.
// Load vcruntime module for windows
Expand Down