================
@@ -194,6 +209,1267 @@ static void applyArgsForStdModuleManifestInputs(
   }
 }
 
+/// Computes the -fmodule-cache-path for this compilation.
+static std::optional<std::string>
+getModuleCachePath(llvm::opt::DerivedArgList &Args) {
+  if (const Arg *A = Args.getLastArg(options::OPT_fmodules_cache_path))
+    return A->getValue();
+
+  if (SmallString<128> Path; Driver::getDefaultModuleCachePath(Path))
+    return std::string(Path);
+
+  return std::nullopt;
+}
+
+/// Returns true if a dependency scan can be performed using \p Job.
+static bool isDependencyScannableJob(const Command &Job) {
+  if (!isCC1Job(Job))
+    return false;
+  const auto &InputInfos = Job.getInputInfos();
+  return !InputInfos.empty() && types::isSrcFile(InputInfos.front().getType());
+}
+
+namespace {
+/// Pool of reusable dependency scanning workers and their contexts with
+/// RAII-based acquire/release.
+class ScanningWorkerPool {
+public:
+  ScanningWorkerPool(size_t NumWorkers,
+                     deps::DependencyScanningService &ScanningService) {
+    for (size_t I = 0; I < NumWorkers; ++I)
+      Slots.emplace_back(ScanningService);
+
+    AvailableSlots.resize(NumWorkers);
+    std::iota(AvailableSlots.begin(), AvailableSlots.end(), 0);
+  }
+
+  /// Acquires a unique pointer to a dependency scanning worker and its
+  /// context.
+  ///
+  /// The worker bundle automatically released back to the pool when the
+  /// pointer is destroyed. The pool has to outlive the leased worker bundle.
+  [[nodiscard]] auto scopedAcquire() {
+    std::unique_lock<std::mutex> UL(Lock);
+    CV.wait(UL, [&] { return !AvailableSlots.empty(); });
+    const size_t Index = AvailableSlots.pop_back_val();
+    auto ReleaseHandle = [this, Index](WorkerBundle *) { release(Index); };
+    return std::unique_ptr<WorkerBundle, decltype(ReleaseHandle)>(
+        &Slots[Index], ReleaseHandle);
+  }
+
+private:
+  /// Releases the worker bundle at \c Index back into the pool.
+  void release(size_t Index) {
+    {
+      std::scoped_lock<std::mutex> SL(Lock);
+      AvailableSlots.push_back(Index);
+    }
+    CV.notify_one();
+  }
+
+  /// A scanning worker with its associated context.
+  struct WorkerBundle {
+    WorkerBundle(deps::DependencyScanningService &ScanningService)
+        : Worker(std::make_unique<deps::DependencyScanningWorker>(
+              ScanningService)) {}
+
+    std::unique_ptr<deps::DependencyScanningWorker> Worker;
+    llvm::DenseSet<deps::ModuleID> SeenModules;
+  };
+
+  std::mutex Lock;
+  std::condition_variable CV;
+  SmallVector<size_t> AvailableSlots;
+  SmallVector<WorkerBundle, 0> Slots;
+};
+} // anonymous namespace
+
+// Creates a ThreadPool and a corresponding ScanningWorkerPool optimized for
+// the configuration of dependency scan inputs.
+static std::pair<std::unique_ptr<llvm::ThreadPoolInterface>,
+                 std::unique_ptr<ScanningWorkerPool>>
+createOptimalThreadAndWorkerPool(
+    size_t NumScanInputs, bool HasStdlibModuleInputs,
+    deps::DependencyScanningService &ScanningService) {
+  // TODO: Benchmark: Determine the optimal number of worker threads for a
+  // given number of inputs. How many inputs are required for multi-threading
+  // to be beneficial? How many inputs should each thread scan at least?
+#if LLVM_ENABLE_THREADS
+  std::unique_ptr<llvm::ThreadPoolInterface> ThreadPool;
+  size_t WorkerCount;
+
+  if (NumScanInputs == 1 || (HasStdlibModuleInputs && NumScanInputs <= 2)) {
+    auto S = llvm::optimal_concurrency(1);
+    ThreadPool = std::make_unique<llvm::SingleThreadExecutor>(std::move(S));
+    WorkerCount = 1;
+  } else {
+    auto ThreadPoolStrategy = llvm::optimal_concurrency(
+        NumScanInputs - static_cast<size_t>(HasStdlibModuleInputs));
+    ThreadPool = std::make_unique<llvm::DefaultThreadPool>(
+        std::move(ThreadPoolStrategy));
+    const size_t MaxConcurrency = ThreadPool->getMaxConcurrency();
+    const size_t MaxConcurrentlyScannedInputs =
+        NumScanInputs -
+        (HasStdlibModuleInputs && NumScanInputs < MaxConcurrency ? 1 : 0);
+    WorkerCount = std::min(MaxConcurrency, MaxConcurrentlyScannedInputs);
+  }
+#else
+  auto ThreadPool = std::make_unique<llvm::SingleThreadExecutor>();
+  size_t WorkerCount = 1;
+#endif
+
+  return {std::move(ThreadPool),
+          std::make_unique<ScanningWorkerPool>(WorkerCount, ScanningService)};
+}
+
+static StringRef getTriple(const Command &Job) {
+  return Job.getCreator().getToolChain().getTriple().getTriple();
+}
+
+using ModuleNameAndTriple = std::pair<StringRef, StringRef>;
+
+namespace {
+/// Helper to schedule on-demand dependency scans for modules originating from
+/// the Standard library module manifest.
+struct StdlibModuleScanScheduler {
+  StdlibModuleScanScheduler(const llvm::DenseMap<ModuleNameAndTriple, size_t>
+                                &StdlibModuleScanIndexByID)
+      : StdlibModuleScanIndexByID(StdlibModuleScanIndexByID) {
+    ScheduledScanInputs.reserve(StdlibModuleScanIndexByID.size());
+  }
+
+  /// Returns the indices of scan inputs corresponding to newly imported
+  /// Standard library modules.
+  ///
+  /// Thread-safe.
+  SmallVector<size_t, 2> getNewScanInputs(ArrayRef<std::string> 
NamedModuleDeps,
+                                          StringRef Triple) {
+    SmallVector<size_t, 2> NewScanInputs;
+    std::scoped_lock<std::mutex> Guard(Lock);
+    for (const auto &ModuleName : NamedModuleDeps) {
+      const auto It = StdlibModuleScanIndexByID.find({ModuleName, Triple});
+      if (It == StdlibModuleScanIndexByID.end())
+        continue;
+      const size_t ScanIndex = It->second;
+      const bool AlreadyScheduled =
+          !ScheduledScanInputs.insert(ScanIndex).second;
+      if (AlreadyScheduled)
+        continue;
+      NewScanInputs.push_back(ScanIndex);
+    }
+    return NewScanInputs;
+  }
+
+private:
+  const llvm::DenseMap<ModuleNameAndTriple, size_t> &StdlibModuleScanIndexByID;
+  llvm::SmallDenseSet<size_t> ScheduledScanInputs;
+  std::mutex Lock;
+};
+
+/// Collects diagnostics in a form that can be retained until after their
+/// associated SourceManager is destroyed.
+class StandaloneDiagCollector : public DiagnosticConsumer {
+public:
+  void BeginSourceFile(const LangOptions &LangOpts,
+                       const Preprocessor *PP = nullptr) override {
+    this->LangOpts = &LangOpts;
+  }
+
+  void HandleDiagnostic(DiagnosticsEngine::Level Level,
+                        const Diagnostic &Info) override {
+    StoredDiagnostic StoredDiag(Level, Info);
+    StandaloneDiags.emplace_back(*LangOpts, StoredDiag);
+    DiagnosticConsumer::HandleDiagnostic(Level, Info);
+  }
+
+  SmallVector<StandaloneDiagnostic, 0> takeDiagnostics() {
+    return std::move(StandaloneDiags);
+  }
+
+private:
+  const LangOptions *LangOpts = nullptr;
+  SmallVector<StandaloneDiagnostic, 0> StandaloneDiags;
+};
+
+/// RAII utility to report collected StandaloneDiagnostic through a
+/// DiagnosticsEngine.
+///
+/// The driver's DiagnosticsEngine usually does not have a SourceManager at
+/// this point of building the compilation, in which case the
+/// StandaloneDiagReporter supplies its own.
+class StandaloneDiagReporter {
+public:
+  explicit StandaloneDiagReporter(DiagnosticsEngine &Diags) : Diags(Diags) {
+    if (!Diags.hasSourceManager()) {
+      FileSystemOptions Opts;
+      Opts.WorkingDir = ".";
+      OwnedFileMgr = llvm::makeIntrusiveRefCnt<FileManager>(std::move(Opts));
+      OwnedSrcMgr =
+          llvm::makeIntrusiveRefCnt<SourceManager>(Diags, *OwnedFileMgr);
+    }
+  }
+
+  /// Emits all diagnostics in \c StandaloneDiags using the associated
+  /// DiagnosticsEngine.
+  void Report(ArrayRef<StandaloneDiagnostic> StandaloneDiags) const {
+    llvm::StringMap<SourceLocation> SrcLocCache;
+    Diags.getClient()->BeginSourceFile(LangOptions(), nullptr);
+    for (const auto &StandaloneDiag : StandaloneDiags) {
+      const auto StoredDiag = translateStandaloneDiag(
+          getFileManager(), getSourceManager(), StandaloneDiag, SrcLocCache);
+      Diags.Report(StoredDiag);
+    }
+    Diags.getClient()->EndSourceFile();
+  }
+
+private:
+  DiagnosticsEngine &Diags;
+  IntrusiveRefCntPtr<FileManager> OwnedFileMgr;
+  IntrusiveRefCntPtr<SourceManager> OwnedSrcMgr;
+
+  FileManager &getFileManager() const {
+    if (OwnedFileMgr)
+      return *OwnedFileMgr;
+    return Diags.getSourceManager().getFileManager();
+  }
+
+  SourceManager &getSourceManager() const {
+    if (OwnedSrcMgr)
+      return *OwnedSrcMgr;
+    return Diags.getSourceManager();
+  }
+};
+} // anonymous namespace
+
+/// Report the diagnostics collected during each dependency scan.
+static void reportAllScanDiagnostics(
+    SmallVectorImpl<SmallVector<StandaloneDiagnostic, 0>> &&AllScanDiags,
+    DiagnosticsEngine &Diags) {
+  StandaloneDiagReporter Reporter(Diags);
+  for (auto &SingleScanDiags : AllScanDiags)
+    Reporter.Report(SingleScanDiags);
+}
+
+/// Construct a path for the explicitly built PCM.
+static std::string constructPCMPath(const deps::ModuleID &ID,
+                                    StringRef OutputDir) {
+  assert(!ID.ModuleName.empty() && !ID.ContextHash.empty() &&
+         "Invalid ModuleID!");
+  SmallString<256> ExplicitPCMPath(OutputDir);
+  llvm::sys::path::append(ExplicitPCMPath, ID.ContextHash,
+                          ID.ModuleName + "-" + ID.ContextHash + ".pcm");
+  return std::string(ExplicitPCMPath);
+}
+
+namespace {
+/// A simple dependency action controller that only provides module lookup for
+/// Clang modules.
+class ModuleLookupController : public deps::DependencyActionController {
+public:
+  ModuleLookupController(StringRef OutputDir) : OutputDir(OutputDir) {}
+
+  std::string lookupModuleOutput(const deps::ModuleDeps &MD,
+                                 deps::ModuleOutputKind Kind) override {
+    if (Kind == deps::ModuleOutputKind::ModuleFile)
+      return constructPCMPath(MD.ID, OutputDir);
+
+    // Driver command lines that trigger lookups for unsupported
+    // ModuleOutputKinds are not supported by the modules driver. Those
+    // command lines should probably be adjusted or rejected in
+    // Driver::handleArguments or Driver::HandleImmediateArgs.
+    llvm::reportFatalInternalError(
+        "call to lookupModuleOutput with unexpected ModuleOutputKind");
+  }
+
+  std::unique_ptr<DependencyActionController> clone() const override {
+    return std::make_unique<ModuleLookupController>(OutputDir);
+  }
+
+private:
+  StringRef OutputDir;
+};
+
+/// The full dependencies for a specific command-line input.
+struct InputDependencies {
+  /// The name of the C++20 module provided by this translation unit.
+  std::string ModuleName;
+
+  /// A list of modules this translation unit directly depends on, not 
including
+  /// transitive dependencies.
+  ///
+  /// This may include modules with a different context hash when it can be
+  /// determined that the differences are benign for this compilation.
+  std::vector<deps::ModuleID> ClangModuleDeps;
+
+  /// A list of the C++20 named modules this translation unit depends on.
+  ///
+  /// All C++20 named module dependencies are expected to target the same 
triple
+  /// as this translation unit.
----------------
Bigcheese wrote:

This should be something like "compatible invocation", more than the triple 
matters.

https://github.com/llvm/llvm-project/pull/152770
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to