versioner: cache -cc1 flags generated by the clang Driver.

Profiling showed that majority of time spent by versioner was being
spent in the x86 and x86_64 driver statting random files in /usr/lib,
looking for a toolchain. Hardcode a list of per-target flags which
correspond to a subset of the ones generated by clang, and use those
instead of calling out to Driver.

This changes the result of `time versioner` from:
    versioner  156.57s user 1180.08s system 4109% cpu 32.525 total
to:
    versioner  109.12s user 17.43s system 2433% cpu 5.201 total

Bug: http://b/32748936
Test: python run_tests.py
Change-Id: I7d254a105bf1a283cbba0546321b04e308e703d1
diff --git a/tools/versioner/src/Android.mk b/tools/versioner/src/Android.mk
index aaf5589..713d6c1 100644
--- a/tools/versioner/src/Android.mk
+++ b/tools/versioner/src/Android.mk
@@ -26,7 +26,9 @@
 LOCAL_SRC_FILES := \
   versioner.cpp \
   Arch.cpp \
+  CompilationType.cpp \
   DeclarationDatabase.cpp \
+  Driver.cpp \
   Preprocessor.cpp \
   SymbolDatabase.cpp \
   Utils.cpp
diff --git a/tools/versioner/src/CompilationType.cpp b/tools/versioner/src/CompilationType.cpp
new file mode 100644
index 0000000..e9d31cb
--- /dev/null
+++ b/tools/versioner/src/CompilationType.cpp
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "CompilationType.h"
+
+#include <sstream>
+#include <string>
+
+std::string to_string(const CompilationType& type) {
+  std::stringstream ss;
+  ss << to_string(type.arch) << "-" << type.api_level << " [fob = " << type.file_offset_bits << "]";
+  return ss.str();
+}
diff --git a/tools/versioner/src/CompilationType.h b/tools/versioner/src/CompilationType.h
new file mode 100644
index 0000000..7d516b2
--- /dev/null
+++ b/tools/versioner/src/CompilationType.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+#include <functional>
+#include <utility>
+
+#include "Arch.h"
+
+struct CompilationType {
+  Arch arch;
+  int api_level;
+  int file_offset_bits;
+
+ private:
+  auto tie() const {
+    return std::tie(arch, api_level, file_offset_bits);
+  }
+
+ public:
+  bool operator<(const CompilationType& other) const {
+    return tie() < other.tie();
+  }
+
+  bool operator==(const CompilationType& other) const {
+    return tie() == other.tie();
+  }
+};
+
+namespace std {
+template <>
+struct hash<CompilationType> {
+  size_t operator()(CompilationType type) const {
+    struct {
+      int32_t arch : 3;
+      int32_t api_level : 6;
+      int32_t file_offset_bits : 1;
+      int32_t padding : 22;
+    } packed;
+    packed.arch = static_cast<int32_t>(type.arch);
+    packed.api_level = type.api_level;
+    packed.file_offset_bits = (type.file_offset_bits == 64);
+    packed.padding = 0;
+    int32_t value;
+    memcpy(&value, &packed, sizeof(value));
+    return std::hash<int32_t>()(value);
+  }
+};
+}
+
+std::string to_string(const CompilationType& type);
diff --git a/tools/versioner/src/DeclarationDatabase.cpp b/tools/versioner/src/DeclarationDatabase.cpp
index af4e1e8..e9ba37e 100644
--- a/tools/versioner/src/DeclarationDatabase.cpp
+++ b/tools/versioner/src/DeclarationDatabase.cpp
@@ -290,12 +290,6 @@
   visitor.TraverseDecl(ctx.getTranslationUnitDecl());
 }
 
-std::string to_string(const CompilationType& type) {
-  std::stringstream ss;
-  ss << to_string(type.arch) << "-" << type.api_level << " [fob = " << type.file_offset_bits << "]";
-  return ss.str();
-}
-
 std::string to_string(const AvailabilityValues& av) {
   std::stringstream ss;
 
diff --git a/tools/versioner/src/DeclarationDatabase.h b/tools/versioner/src/DeclarationDatabase.h
index 4ec15d9..6f577a5 100644
--- a/tools/versioner/src/DeclarationDatabase.h
+++ b/tools/versioner/src/DeclarationDatabase.h
@@ -27,6 +27,7 @@
 #include <llvm/ADT/StringRef.h>
 
 #include "Arch.h"
+#include "CompilationType.h"
 #include "Utils.h"
 
 namespace clang {
@@ -40,28 +41,6 @@
   inconsistent,
 };
 
-struct CompilationType {
-  Arch arch;
-  int api_level;
-  int file_offset_bits;
-
- private:
-  auto tie() const {
-    return std::tie(arch, api_level, file_offset_bits);
-  }
-
- public:
-  bool operator<(const CompilationType& other) const {
-    return tie() < other.tie();
-  }
-
-  bool operator==(const CompilationType& other) const {
-    return tie() == other.tie();
-  }
-};
-
-std::string to_string(const CompilationType& type);
-
 struct AvailabilityValues {
   bool future = false;
   int introduced = 0;
diff --git a/tools/versioner/src/Driver.cpp b/tools/versioner/src/Driver.cpp
new file mode 100644
index 0000000..dad8a1e
--- /dev/null
+++ b/tools/versioner/src/Driver.cpp
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Driver.h"
+
+#include <err.h>
+#include <string.h>
+
+#include <chrono>
+#include <mutex>
+#include <string>
+#include <thread>
+#include <unordered_map>
+#include <vector>
+
+#include <clang/AST/ASTConsumer.h>
+#include <clang/Basic/Diagnostic.h>
+#include <clang/Basic/TargetInfo.h>
+#include <clang/Driver/Compilation.h>
+#include <clang/Driver/Driver.h>
+#include <clang/Frontend/CompilerInstance.h>
+#include <clang/Frontend/CompilerInvocation.h>
+#include <clang/Frontend/FrontendAction.h>
+#include <clang/Frontend/FrontendActions.h>
+#include <clang/Frontend/TextDiagnosticPrinter.h>
+#include <clang/Frontend/Utils.h>
+#include <clang/FrontendTool/Utils.h>
+#include <llvm/ADT/IntrusiveRefCntPtr.h>
+#include <llvm/ADT/SmallVector.h>
+#include <llvm/ADT/StringRef.h>
+
+#include "Arch.h"
+#include "DeclarationDatabase.h"
+#include "versioner.h"
+
+using namespace std::chrono_literals;
+using namespace std::string_literals;
+
+using namespace clang;
+
+class VersionerASTConsumer : public clang::ASTConsumer {
+ public:
+  HeaderDatabase* header_database;
+  CompilationType type;
+
+  VersionerASTConsumer(HeaderDatabase* header_database, CompilationType type)
+      : header_database(header_database), type(type) {
+  }
+
+  virtual void HandleTranslationUnit(ASTContext& ctx) override {
+    header_database->parseAST(type, ctx);
+  }
+};
+
+class VersionerASTAction : public clang::ASTFrontendAction {
+ public:
+  HeaderDatabase* header_database;
+  CompilationType type;
+
+  VersionerASTAction(HeaderDatabase* header_database, CompilationType type)
+      : header_database(header_database), type(type) {
+  }
+
+  std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance&, llvm::StringRef) override {
+    return std::make_unique<VersionerASTConsumer>(header_database, type);
+  }
+};
+
+static IntrusiveRefCntPtr<DiagnosticsEngine> constructDiags() {
+  IntrusiveRefCntPtr<DiagnosticOptions> diag_opts(new DiagnosticOptions());
+  auto diag_printer = std::make_unique<TextDiagnosticPrinter>(llvm::errs(), diag_opts.get());
+  IntrusiveRefCntPtr<DiagnosticIDs> diag_ids(new DiagnosticIDs());
+  IntrusiveRefCntPtr<DiagnosticsEngine> diags(
+      new DiagnosticsEngine(diag_ids.get(), diag_opts.get(), diag_printer.release()));
+  return diags;
+}
+
+// clang's driver is slow compared to the work it performs to compile our headers.
+// Run it once to generate flags for each target, and memoize the results.
+static std::unordered_map<CompilationType, std::vector<std::string>> cc1_flags;
+static const char* filename_placeholder = "__VERSIONER_PLACEHOLDER__";
+static void generateTargetCC1Flags(CompilationType type,
+                                   const std::vector<std::string>& include_dirs) {
+  std::vector<std::string> cmd = { "versioner" };
+  cmd.push_back("-std=c11");
+  cmd.push_back("-x");
+  cmd.push_back("c-header");
+  cmd.push_back("-fsyntax-only");
+
+  cmd.push_back("-Wall");
+  cmd.push_back("-Wextra");
+  cmd.push_back("-Werror");
+  cmd.push_back("-Wundef");
+  cmd.push_back("-Wno-unused-macros");
+  cmd.push_back("-Wno-unused-function");
+  cmd.push_back("-Wno-unused-variable");
+  cmd.push_back("-Wno-unknown-attributes");
+  cmd.push_back("-Wno-pragma-once-outside-header");
+
+  cmd.push_back("-target");
+  cmd.push_back(arch_targets[type.arch]);
+
+  cmd.push_back("-DANDROID");
+  cmd.push_back("-D__ANDROID_API__="s + std::to_string(type.api_level));
+  cmd.push_back("-D_FORTIFY_SOURCE=2");
+  cmd.push_back("-D_GNU_SOURCE");
+  cmd.push_back("-D_FILE_OFFSET_BITS="s + std::to_string(type.file_offset_bits));
+
+  cmd.push_back("-nostdinc");
+
+  if (add_include) {
+    const char* top = getenv("ANDROID_BUILD_TOP");
+    if (!top) {
+      errx(1, "-i passed, but ANDROID_BUILD_TOP is unset");
+    }
+    cmd.push_back("-include");
+    cmd.push_back(to_string(top) + "/bionic/libc/include/android/versioning.h");
+  }
+
+  for (const auto& dir : include_dirs) {
+    cmd.push_back("-isystem");
+    cmd.push_back(dir);
+  }
+
+  cmd.push_back(filename_placeholder);
+
+  auto diags = constructDiags();
+  driver::Driver driver("versioner", llvm::sys::getDefaultTargetTriple(), *diags);
+  driver.setCheckInputsExist(false);
+
+  llvm::SmallVector<const char*, 32> driver_args;
+  for (const std::string& str : cmd) {
+    driver_args.push_back(str.c_str());
+  }
+
+  std::unique_ptr<driver::Compilation> Compilation(driver.BuildCompilation(driver_args));
+  const driver::JobList& jobs = Compilation->getJobs();
+  if (jobs.size() != 1) {
+    errx(1, "driver returned %zu jobs for %s", jobs.size(), to_string(type).c_str());
+  }
+
+  const driver::Command& driver_cmd = llvm::cast<driver::Command>(*jobs.begin());
+  const driver::ArgStringList& cc_args = driver_cmd.getArguments();
+
+  if (cc_args.size() == 0) {
+    errx(1, "driver returned empty command for %s", to_string(type).c_str());
+  }
+
+  std::vector<std::string> result(cc_args.begin(), cc_args.end());
+
+  {
+    static std::mutex cc1_init_mutex;
+    std::unique_lock<std::mutex> lock(cc1_init_mutex);
+    if (cc1_flags.count(type) > 0) {
+      errx(1, "attemped to generate cc1 flags for existing CompilationType %s",
+           to_string(type).c_str());
+    }
+
+    cc1_flags.emplace(std::make_pair(type, std::move(result)));
+  }
+}
+
+static std::vector<const char*> getCC1Command(CompilationType type, const std::string& filename) {
+  const auto& target_flag_it = cc1_flags.find(type);
+  if (target_flag_it == cc1_flags.end()) {
+    errx(1, "failed to find target flags for CompilationType %s", to_string(type).c_str());
+  }
+
+  std::vector<const char*> result;
+  for (const std::string& flag : target_flag_it->second) {
+    if (flag == "-disable-free") {
+      continue;
+    } else if (flag == filename_placeholder) {
+      result.push_back(filename.c_str());
+    } else {
+      result.push_back(flag.c_str());
+    }
+  }
+  return result;
+}
+
+void initializeTargetCC1FlagCache(const std::set<CompilationType>& types,
+                                  const std::unordered_map<Arch, CompilationRequirements>& reqs) {
+  if (!cc1_flags.empty()) {
+    errx(1, "reinitializing target CC1 flag cache?");
+  }
+
+  auto start = std::chrono::high_resolution_clock::now();
+  std::vector<std::thread> threads;
+  for (const CompilationType type : types) {
+    threads.emplace_back([type, &reqs]() {
+      const auto& arch_req_it = reqs.find(type.arch);
+      if (arch_req_it == reqs.end()) {
+        errx(1, "CompilationRequirement map missing entry for CompilationType %s",
+             to_string(type).c_str());
+      }
+
+      generateTargetCC1Flags(type, arch_req_it->second.dependencies);
+    });
+  }
+  for (auto& thread : threads) {
+    thread.join();
+  }
+  auto end = std::chrono::high_resolution_clock::now();
+
+  if (verbose) {
+    auto diff = (end - start) / 1.0ms;
+    printf("Generated compiler flags for %zu targets in %0.2Lfms\n", types.size(), diff);
+  }
+
+  if (cc1_flags.empty()) {
+    errx(1, "failed to initialize target CC1 flag cache");
+  }
+}
+
+void compileHeader(HeaderDatabase* header_database, CompilationType type,
+                   const std::string& filename) {
+  auto diags = constructDiags();
+  std::vector<const char*> cc1_flags = getCC1Command(type, filename);
+  auto invocation = std::make_unique<CompilerInvocation>();
+  if (!CompilerInvocation::CreateFromArgs(*invocation.get(), &cc1_flags.front(),
+                                          &cc1_flags.front() + cc1_flags.size(), *diags)) {
+    errx(1, "failed to create CompilerInvocation");
+  }
+
+  clang::CompilerInstance Compiler;
+  Compiler.setInvocation(invocation.release());
+  Compiler.setDiagnostics(diags.get());
+
+  VersionerASTAction versioner_action(header_database, type);
+  if (!Compiler.ExecuteAction(versioner_action)) {
+    errx(1, "compilation generated warnings or errors");
+  }
+
+  if (diags->getNumWarnings() || diags->hasErrorOccurred()) {
+    errx(1, "compilation generated warnings or errors");
+  }
+}
diff --git a/tools/versioner/src/Driver.h b/tools/versioner/src/Driver.h
new file mode 100644
index 0000000..c1a4b24
--- /dev/null
+++ b/tools/versioner/src/Driver.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <set>
+#include <string>
+#include <unordered_map>
+
+#include "Arch.h"
+#include "DeclarationDatabase.h"
+
+struct CompilationRequirements {
+  std::vector<std::string> headers;
+  std::vector<std::string> dependencies;
+};
+
+void initializeTargetCC1FlagCache(const std::set<CompilationType>& types,
+                                  const std::unordered_map<Arch, CompilationRequirements>& reqs);
+
+void compileHeader(HeaderDatabase* header_database, CompilationType type,
+                   const std::string& filename);
diff --git a/tools/versioner/src/versioner.cpp b/tools/versioner/src/versioner.cpp
index 807ef16..f0a2339 100644
--- a/tools/versioner/src/versioner.cpp
+++ b/tools/versioner/src/versioner.cpp
@@ -34,167 +34,30 @@
 #include <unordered_map>
 #include <vector>
 
-#include <clang/AST/ASTConsumer.h>
-#include <clang/Basic/TargetInfo.h>
-#include <clang/Driver/Compilation.h>
-#include <clang/Driver/Driver.h>
-#include <clang/Frontend/CompilerInstance.h>
-#include <clang/Frontend/CompilerInvocation.h>
-#include <clang/Frontend/FrontendAction.h>
-#include <clang/Frontend/FrontendActions.h>
-#include <clang/Frontend/TextDiagnosticPrinter.h>
-#include <clang/Frontend/Utils.h>
-#include <clang/FrontendTool/Utils.h>
-#include <clang/Tooling/Tooling.h>
 #include <llvm/ADT/StringRef.h>
 
 #include <android-base/parseint.h>
 
 #include "Arch.h"
 #include "DeclarationDatabase.h"
+#include "Driver.h"
 #include "Preprocessor.h"
 #include "SymbolDatabase.h"
 #include "Utils.h"
 #include "versioner.h"
 
 using namespace std::string_literals;
-using namespace clang;
-using namespace tooling;
 
+bool add_include;
 bool verbose;
-static bool add_include;
 static int max_thread_count = 48;
 
-static std::vector<std::string> generateCompileCommand(CompilationType& type,
-                                                       const std::string& filename,
-                                                       const std::string& header_dir,
-                                                       const std::vector<std::string>& include_dirs) {
-  std::vector<std::string> cmd = { "versioner" };
-  cmd.push_back("-std=c11");
-
-  cmd.push_back("-Wall");
-  cmd.push_back("-Wextra");
-  cmd.push_back("-Werror");
-  cmd.push_back("-Wundef");
-  cmd.push_back("-Wno-unused-macros");
-  cmd.push_back("-Wno-unused-function");
-  cmd.push_back("-Wno-unused-variable");
-  cmd.push_back("-Wno-unknown-attributes");
-  cmd.push_back("-Wno-pragma-once-outside-header");
-
-  cmd.push_back("-target");
-  cmd.push_back(arch_targets[type.arch]);
-
-  cmd.push_back("-DANDROID");
-  cmd.push_back("-D__ANDROID_API__="s + std::to_string(type.api_level));
-  cmd.push_back("-D_FORTIFY_SOURCE=2");
-  cmd.push_back("-D_GNU_SOURCE");
-  cmd.push_back("-D_FILE_OFFSET_BITS="s + std::to_string(type.file_offset_bits));
-
-  cmd.push_back("-nostdinc");
-  std::string header_path;
-  if (add_include) {
-    const char* top = getenv("ANDROID_BUILD_TOP");
-    header_path = to_string(top) + "/bionic/libc/include/android/versioning.h";
-    cmd.push_back("-include");
-    cmd.push_back(header_path);
-  }
-
-  for (const auto& dir : include_dirs) {
-    cmd.push_back("-isystem");
-    cmd.push_back(dir);
-  }
-
-  cmd.push_back(filename);
-
-  return cmd;
-}
-
-class VersionerASTConsumer : public clang::ASTConsumer {
- public:
-  HeaderDatabase* header_database;
-  CompilationType type;
-
-  VersionerASTConsumer(HeaderDatabase* header_database, CompilationType type)
-      : header_database(header_database), type(type) {
-  }
-
-  virtual void HandleTranslationUnit(ASTContext& ctx) override {
-    header_database->parseAST(type, ctx);
-  }
-};
-
-class VersionerASTAction : public clang::ASTFrontendAction {
- public:
-  HeaderDatabase* header_database;
-  CompilationType type;
-
-  VersionerASTAction(HeaderDatabase* header_database, CompilationType type)
-      : header_database(header_database), type(type) {
-  }
-
-  virtual std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance& Compiler,
-                                                         llvm::StringRef InFile) override {
-    return std::make_unique<VersionerASTConsumer>(header_database, type);
-  }
-};
-
-static void compileHeader(HeaderDatabase* header_database, CompilationType type,
-                          const std::string& filename, const std::string& header_dir,
-                          const std::vector<std::string>& include_dirs) {
-  DiagnosticOptions diagnostic_options;
-  auto diagnostic_printer = new TextDiagnosticPrinter(llvm::errs(), &diagnostic_options);
-
-  IntrusiveRefCntPtr<DiagnosticIDs> diagnostic_ids(new DiagnosticIDs());
-  IntrusiveRefCntPtr<DiagnosticsEngine> diags(
-      new DiagnosticsEngine(diagnostic_ids, &diagnostic_options, diagnostic_printer, false));
-  driver::Driver Driver("versioner", llvm::sys::getDefaultTargetTriple(), *diags);
-
-  std::vector<std::string> cmd = generateCompileCommand(type, filename, header_dir, include_dirs);
-  llvm::SmallVector<const char*, 32> Args;
-
-  for (const std::string& str : cmd) {
-    Args.push_back(str.c_str());
-  }
-
-  std::unique_ptr<driver::Compilation> Compilation(Driver.BuildCompilation(Args));
-
-  const driver::Command &Cmd = llvm::cast<driver::Command>(*Compilation->getJobs().begin());
-  const driver::ArgStringList &CCArgs = Cmd.getArguments();
-
-  auto invocation = std::make_unique<CompilerInvocation>();
-  if (!CompilerInvocation::CreateFromArgs(
-          *invocation.get(), const_cast<const char**>(CCArgs.data()),
-          const_cast<const char**>(CCArgs.data()) + CCArgs.size(), *diags)) {
-    errx(1, "failed to create CompilerInvocation");
-  }
-
-  clang::CompilerInstance Compiler;
-  Compiler.setInvocation(invocation.release());
-  Compiler.setDiagnostics(diags.get());
-
-  VersionerASTAction versioner_action(header_database, type);
-  if (!Compiler.ExecuteAction(versioner_action)) {
-    errx(1, "compilation generated warnings or errors");
-  }
-
-  if (diags->getNumWarnings() || diags->hasErrorOccurred()) {
-    errx(1, "compilation generated warnings or errors");
-  }
-}
-
-struct CompilationRequirements {
-  std::vector<std::string> headers;
-  std::vector<std::string> dependencies;
-};
-
 static CompilationRequirements collectRequirements(const Arch& arch, const std::string& header_dir,
                                                    const std::string& dependency_dir) {
   std::vector<std::string> headers = collectFiles(header_dir);
-
   std::vector<std::string> dependencies = { header_dir };
   if (!dependency_dir.empty()) {
-    auto collect_children = [&dependencies](const std::string& dir_path) {
+    auto collect_children = [&dependencies, &dependency_dir](const std::string& dir_path) {
       DIR* dir = opendir(dir_path.c_str());
       if (!dir) {
         err(1, "failed to open dependency directory '%s'", dir_path.c_str());
@@ -268,15 +131,6 @@
   return result;
 }
 
-struct Job {
-  Job(CompilationType type, const std::string& header, const std::vector<std::string>& dependencies)
-      : type(type), header(header), dependencies(dependencies) {
-  }
-  CompilationType type;
-  const std::string& header;
-  const std::vector<std::string>& dependencies;
-};
-
 static std::unique_ptr<HeaderDatabase> compileHeaders(const std::set<CompilationType>& types,
                                                       const std::string& header_dir,
                                                       const std::string& dependency_dir) {
@@ -286,49 +140,52 @@
 
   size_t thread_count = max_thread_count;
   std::vector<std::thread> threads;
-  std::vector<Job> jobs;
 
   std::map<CompilationType, HeaderDatabase> header_databases;
   std::unordered_map<Arch, CompilationRequirements> requirements;
 
-  std::string cwd = getWorkingDir();
-
   auto result = std::make_unique<HeaderDatabase>();
-  auto spawn_threads = [&]() {
-    thread_count = std::min(thread_count, jobs.size());
-    for (size_t i = 0; i < thread_count; ++i) {
-      threads.emplace_back([&jobs, &result, &header_dir, thread_count, i]() {
-        size_t index = i;
-        while (index < jobs.size()) {
-          const auto& job = jobs[index];
-          compileHeader(result.get(), job.type, job.header, header_dir, job.dependencies);
-          index += thread_count;
-        }
-      });
-    }
-  };
-  auto reap_threads = [&]() {
-    for (auto& thread : threads) {
-      thread.join();
-    }
-    threads.clear();
-  };
-
   for (const auto& type : types) {
     if (requirements.count(type.arch) == 0) {
       requirements[type.arch] = collectRequirements(type.arch, header_dir, dependency_dir);
     }
   }
 
+  initializeTargetCC1FlagCache(types, requirements);
+
+  std::vector<std::pair<CompilationType, const std::string&>> jobs;
   for (CompilationType type : types) {
     CompilationRequirements& req = requirements[type.arch];
     for (const std::string& header : req.headers) {
-      jobs.emplace_back(type, header, req.dependencies);
+      jobs.emplace_back(type, header);
     }
   }
 
-  spawn_threads();
-  reap_threads();
+  thread_count = std::min(thread_count, jobs.size());
+
+  if (thread_count == 1) {
+    for (const auto& job : jobs) {
+      compileHeader(result.get(), job.first, job.second);
+    }
+  } else {
+    // Spawn threads.
+    for (size_t i = 0; i < thread_count; ++i) {
+      threads.emplace_back([&jobs, &result, &header_dir, thread_count, i]() {
+        size_t index = i;
+        while (index < jobs.size()) {
+          const auto& job = jobs[index];
+          compileHeader(result.get(), job.first, job.second);
+          index += thread_count;
+        }
+      });
+    }
+
+    // Reap them.
+    for (auto& thread : threads) {
+      thread.join();
+    }
+    threads.clear();
+  }
 
   return result;
 }
diff --git a/tools/versioner/src/versioner.h b/tools/versioner/src/versioner.h
index 9547f2c..97b1509 100644
--- a/tools/versioner/src/versioner.h
+++ b/tools/versioner/src/versioner.h
@@ -23,6 +23,7 @@
 #include <unordered_set>
 
 extern bool verbose;
+extern bool add_include;
 
 #define D(...)             \
   do {                     \