ilya-biryukov created this revision.
ilya-biryukov added a reviewer: sammccall.
Herald added a subscriber: jfb.
This greatly reduces the time to read 'compile_commands.json'.
For Chromium on my machine it's now 5 secs vs 30 secs before the
change.
Repository:
rC Clang
https://reviews.llvm.org/D51314
Files:
lib/Tooling/InterpolatingCompilationDatabase.cpp
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 <memory>
+#include <atomic>
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<std::string> NewArgs = {Cmd.CommandLine.front()};
- // Parse the old args in order to strip out and record unwanted flags.
- auto OptTable = clang::driver::createDriverOptTable();
- std::vector<const char *> 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<LangStandard::Kind>(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<CommandTraits> ComputedTraits = std::atomic_load(&Traits);
+ if (!ComputedTraits) {
+ ComputedTraits = std::make_shared<CommandTraits>(computeTraits());
+ std::atomic_store(&Traits, ComputedTraits);
+ }
+ return *ComputedTraits;
+ }
+
+ CommandTraits computeTraits() const {
+ CommandTraits Result;
+ Result.Type = guessType(OriginalCmd.Filename);
+
+ std::vector<std::string> 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<const char *> 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<LangStandard::Kind>(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<CommandTraits> 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<TransferableCommand> allCommands() {
std::vector<TransferableCommand> 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;
}
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits