Index: lib/Tooling/InterpolatingCompilationDatabase.cpp =================================================================== --- lib/Tooling/InterpolatingCompilationDatabase.cpp +++ lib/Tooling/InterpolatingCompilationDatabase.cpp @@ -57,6 +57,7 @@ #include "llvm/Support/StringSaver.h" #include "llvm/Support/raw_ostream.h" #include +#include namespace clang { namespace tooling { @@ -121,85 +122,57 @@ // A CompileCommand that can be applied to another file. struct TransferableCommand { - // Flags that should not apply to all files are stripped from CommandLine. - CompileCommand Cmd; - // Language detected from -x or the filename. - types::ID Type = types::TY_INVALID; - // Standard specified by -std. - LangStandard::Kind Std = LangStandard::lang_unspecified; - TransferableCommand(CompileCommand C) - : Cmd(std::move(C)), Type(guessType(Cmd.Filename)) { - std::vector NewArgs = {Cmd.CommandLine.front()}; - // Parse the old args in order to strip out and record unwanted flags. - auto OptTable = clang::driver::createDriverOptTable(); - std::vector Argv; - for (unsigned I = 1; I < Cmd.CommandLine.size(); ++I) - Argv.push_back(Cmd.CommandLine[I].c_str()); - unsigned MissingI, MissingC; - auto ArgList = OptTable->ParseArgs(Argv, MissingI, MissingC); - for (const auto *Arg : ArgList) { - const auto &option = Arg->getOption(); - // Strip input and output files. - if (option.matches(clang::driver::options::OPT_INPUT) || - option.matches(clang::driver::options::OPT_o)) { - continue; - } - // Strip -x, but record the overridden language. - if (option.matches(clang::driver::options::OPT_x)) { - for (const char *Value : Arg->getValues()) - Type = types::lookupTypeForTypeSpecifier(Value); - continue; - } - // Strip --std, but record the value. - if (option.matches(clang::driver::options::OPT_std_EQ)) { - for (const char *Value : Arg->getValues()) { - Std = llvm::StringSwitch(Value) -#define LANGSTANDARD(id, name, lang, desc, features) \ - .Case(name, LangStandard::lang_##id) -#define LANGSTANDARD_ALIAS(id, alias) .Case(alias, LangStandard::lang_##id) -#include "clang/Frontend/LangStandards.def" - .Default(Std); - } - continue; - } - llvm::opt::ArgStringList ArgStrs; - Arg->render(ArgList, ArgStrs); - NewArgs.insert(NewArgs.end(), ArgStrs.begin(), ArgStrs.end()); - } - Cmd.CommandLine = std::move(NewArgs); - - if (Std != LangStandard::lang_unspecified) // -std take precedence over -x - Type = toType(LangStandard::getLangStandardForKind(Std).getLanguage()); - Type = foldType(Type); - } + : OriginalCmd(std::move(C)), + Traits(nullptr) {} // Produce a CompileCommand for \p filename, based on this one. CompileCommand transferTo(StringRef Filename) const { - CompileCommand Result = Cmd; + const CommandTraits& T = getTraits(); + CompileCommand Result = T.Cmd; Result.Filename = Filename; bool TypeCertain; auto TargetType = guessType(Filename, &TypeCertain); // If the filename doesn't determine the language (.h), transfer with -x. if (!TypeCertain) { TargetType = types::onlyPrecompileType(TargetType) // header? - ? types::lookupHeaderTypeForSourceType(Type) - : Type; + ? types::lookupHeaderTypeForSourceType(T.Type) + : T.Type; Result.CommandLine.push_back("-x"); Result.CommandLine.push_back(types::getTypeName(TargetType)); } // --std flag may only be transferred if the language is the same. // We may consider "translating" these, e.g. c++11 -> c11. - if (Std != LangStandard::lang_unspecified && foldType(TargetType) == Type) { + if (T.Std != LangStandard::lang_unspecified && + foldType(TargetType) == T.Type) { Result.CommandLine.push_back( "-std=" + - std::string(LangStandard::getLangStandardForKind(Std).getName())); + std::string(LangStandard::getLangStandardForKind(T.Std).getName())); } Result.CommandLine.push_back(Filename); return Result; } + llvm::StringRef filename() const { + return OriginalCmd.Filename; + } + + types::ID type() const { + return getTraits().Type; + } + private: + // Extra information that cannot be inferred solely by the filename. Slow to + // compute, so we only compute on first access. + struct CommandTraits { + // Flags that should not apply to all files are stripped from CommandLine. + CompileCommand Cmd; + // Language detected from -x or the filename. + types::ID Type = types::TY_INVALID; + // Standard specified by -std. + LangStandard::Kind Std = LangStandard::lang_unspecified; + }; + // Map the language from the --std flag to that of the -x flag. static types::ID toType(InputKind::Language Lang) { switch (Lang) { @@ -215,6 +188,75 @@ return types::TY_INVALID; } } + + const CommandTraits& getTraits() const { + std::shared_ptr ComputedTraits = std::atomic_load(&Traits); + if (!ComputedTraits) { + ComputedTraits = std::make_shared(computeTraits()); + std::atomic_store(&Traits, ComputedTraits); + } + return *ComputedTraits; + } + + CommandTraits computeTraits() const { + CommandTraits Result; + Result.Type = guessType(OriginalCmd.Filename); + + std::vector NewArgs = {OriginalCmd.CommandLine.front()}; + // Parse the old args in order to strip out and record unwanted flags. + // Note that we reuse the OptTable because it's slow to create. + static const auto *OptTable = + clang::driver::createDriverOptTable().release(); + + std::vector Argv; + for (unsigned I = 1; I < OriginalCmd.CommandLine.size(); ++I) + Argv.push_back(OriginalCmd.CommandLine[I].c_str()); + unsigned MissingI, MissingC; + auto ArgList = OptTable->ParseArgs(Argv, MissingI, MissingC); + for (const auto *Arg : ArgList) { + const auto &option = Arg->getOption(); + // Strip input and output files. + if (option.matches(clang::driver::options::OPT_INPUT) || + option.matches(clang::driver::options::OPT_o)) { + continue; + } + // Strip -x, but record the overridden language. + if (option.matches(clang::driver::options::OPT_x)) { + for (const char *Value : Arg->getValues()) + Result.Type = types::lookupTypeForTypeSpecifier(Value); + continue; + } + // Strip --std, but record the value. + if (option.matches(clang::driver::options::OPT_std_EQ)) { + for (const char *Value : Arg->getValues()) { + Result.Std = llvm::StringSwitch(Value) +#define LANGSTANDARD(id, name, lang, desc, features) \ + .Case(name, LangStandard::lang_##id) +#define LANGSTANDARD_ALIAS(id, alias) .Case(alias, LangStandard::lang_##id) +#include "clang/Frontend/LangStandards.def" + .Default(Result.Std); + } + continue; + } + llvm::opt::ArgStringList ArgStrs; + Arg->render(ArgList, ArgStrs); + NewArgs.insert(NewArgs.end(), ArgStrs.begin(), ArgStrs.end()); + } + Result.Cmd.CommandLine = std::move(NewArgs); + + if (Result.Std != LangStandard::lang_unspecified) // -std take precedence over -x + Result.Type = toType(LangStandard::getLangStandardForKind(Result.Std).getLanguage()); + Result.Type = foldType(Result.Type); + + return Result; + } + + /// The original command we were created with, used as input to compute the + /// Traits field. + CompileCommand OriginalCmd; + /// Null before Traits were computed, non-null otherwise. All accesses to this + /// must be atomic. + mutable std::shared_ptr Traits; }; // CommandIndex does the real work: given a filename, it produces the best @@ -233,11 +275,11 @@ llvm::sort( Commands.begin(), Commands.end(), [](const TransferableCommand &Left, const TransferableCommand &Right) { - return Left.Cmd.Filename < Right.Cmd.Filename; + return Left.filename() < Right.filename(); }); for (size_t I = 0; I < Commands.size(); ++I) { StringRef Path = - Strings.save(StringRef(Commands[I].Cmd.Filename).lower()); + Strings.save(StringRef(Commands[I].filename()).lower()); Paths.push_back({Path, I}); Stems.emplace_back(sys::path::stem(Path), I); auto Dir = ++sys::path::rbegin(Path), DirEnd = sys::path::rend(Path); @@ -266,7 +308,7 @@ DEBUG_WITH_TYPE("interpolate", llvm::dbgs() << "interpolate: chose " - << Commands[Best.first].Cmd.Filename << " as proxy for " + << Commands[Best.first].filename() << " as proxy for " << OriginalFilename << " preferring " << (PreferLanguage == types::TY_INVALID ? "none" @@ -338,7 +380,7 @@ ScoredCandidate S; S.Index = Candidate.first; S.Preferred = PreferredLanguage == types::TY_INVALID || - PreferredLanguage == Commands[S.Index].Type; + PreferredLanguage == Commands[S.Index].type(); S.Points = Candidate.second; if (!S.Preferred && Best.Preferred) continue; @@ -435,11 +477,8 @@ private: std::vector allCommands() { std::vector Result; - for (auto Command : Inner->getAllCompileCommands()) { + for (auto Command : Inner->getAllCompileCommands()) Result.emplace_back(std::move(Command)); - if (Result.back().Type == types::TY_INVALID) - Result.pop_back(); - } return Result; }