ilya-biryukov updated this revision to Diff 162699.
ilya-biryukov added a comment.

- Add a comment
- Use std::call_once to compute the lazy value


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,8 @@
 #include "llvm/Support/StringSaver.h"
 #include "llvm/Support/raw_ostream.h"
 #include <memory>
+#include <atomic>
+#include <mutex>
 
 namespace clang {
 namespace tooling {
@@ -119,87 +121,64 @@
   }
 }
 
-// A CompileCommand that can be applied to another file.
+// A CompileCommand that can be applied to another file. Any instance of this
+// object is invalid after std::move() from it.
 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)),
+        TraitsComputed(llvm::make_unique<std::once_flag>()),
+        Traits(llvm::None) {}
 
   // Produce a CompileCommand for \p filename, based on this one.
   CompileCommand transferTo(StringRef Filename) const {
-    CompileCommand Result = Cmd;
+    assert(TraitsComputed && "calling transferTo on moved-from object");
+    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 {
+    assert(TraitsComputed && "calling filename on moved-from object");
+    return OriginalCmd.Filename;
+  }
+
+  types::ID type() const {
+    assert(TraitsComputed && "calling type on moved-from object");
+    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 +194,80 @@
       return types::TY_INVALID;
     }
   }
+
+  const CommandTraits& getTraits() const {
+    assert(TraitsComputed && "calling getTraits on moved-from object");
+    std::call_once(*TraitsComputed, [this]() {
+      Traits = computeTraits();
+    });
+    return *Traits;
+  }
+
+  /// Helper function to compute the Traits field. Should only be called from
+  /// getTraits().
+  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;
+  /// A flag indicating if Traits were computed. unique_ptr to allow moving from
+  /// TransferableCommand.
+  mutable std::unique_ptr<std::once_flag> TraitsComputed;
+  /// Lazilly computed information about compile command. Null before Traits
+  /// were computed, non-null otherwise. All accesses to this must be atomic, to
+  /// make const method calls are thread-safe.
+  mutable llvm::Optional<CommandTraits> Traits;
 };
 
 // CommandIndex does the real work: given a filename, it produces the best
@@ -233,11 +286,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 +319,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 +391,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 +488,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
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to