1212
1313#include " swift/Frontend/CachingUtils.h"
1414
15+ #include " swift/AST/DiagnosticEngine.h"
1516#include " swift/AST/DiagnosticsFrontend.h"
1617#include " swift/Basic/Assertions.h"
18+ #include " swift/Basic/Defer.h"
1719#include " swift/Basic/FileTypes.h"
1820#include " swift/Basic/LLVM.h"
1921#include " swift/Frontend/CASOutputBackends.h"
2022#include " swift/Frontend/CompileJobCacheKey.h"
2123#include " swift/Frontend/CompileJobCacheResult.h"
24+ #include " swift/Frontend/DiagnosticHelper.h"
2225#include " swift/Frontend/FrontendOptions.h"
2326#include " swift/Frontend/MakeStyleDependencies.h"
2427#include " swift/Option/Options.h"
2528#include " clang/CAS/CASOptions.h"
2629#include " clang/CAS/IncludeTree.h"
2730#include " clang/Frontend/CompileJobCacheResult.h"
28- #include " llvm/ADT/STLExtras.h"
2931#include " llvm/CAS/BuiltinUnifiedCASDatabases.h"
3032#include " llvm/CAS/CASFileSystem.h"
3133#include " llvm/CAS/HierarchicalTreeBuilder.h"
4042#include " llvm/Support/MemoryBuffer.h"
4143#include " llvm/Support/Path.h"
4244#include " llvm/Support/VirtualFileSystem.h"
45+ #include " llvm/Support/VirtualOutputBackend.h"
4346#include " llvm/Support/VirtualOutputBackends.h"
44- #include " llvm/Support/VirtualOutputFile.h"
4547#include < memory>
4648
4749#define DEBUG_TYPE " cache-util"
@@ -133,10 +135,19 @@ lookupCacheKey(ObjectStore &CAS, ActionCache &Cache, ObjectRef CacheKey) {
133135 return CAS.getReference (**Lookup);
134136}
135137
136- bool replayCachedCompilerOutputs (
137- ObjectStore &CAS, ActionCache &Cache, ObjectRef BaseKey,
138- DiagnosticEngine &Diag, const FrontendOptions &Opts,
139- CachingDiagnosticsProcessor &CDP, bool CacheRemarks, bool UseCASBackend) {
138+ namespace {
139+ struct CacheInputEntry {
140+ const InputFile &Input;
141+ unsigned Index;
142+ ObjectRef OutputRef;
143+ };
144+ } // namespace
145+
146+ static bool replayCachedCompilerOutputsImpl (
147+ ArrayRef<CacheInputEntry> Inputs, ObjectStore &CAS, DiagnosticEngine &Diag,
148+ const FrontendOptions &Opts, CachingDiagnosticsProcessor &CDP,
149+ DiagnosticHelper *DiagHelper, OutputBackend &Backend, bool CacheRemarks,
150+ bool UseCASBackend) {
140151 bool CanReplayAllOutput = true ;
141152 struct OutputEntry {
142153 std::string Path;
@@ -151,34 +162,11 @@ bool replayCachedCompilerOutputs(
151162 auto replayOutputsForInputFile = [&](const InputFile &Input,
152163 const std::string &InputPath,
153164 unsigned InputIndex,
165+ ObjectRef OutputRef,
154166 const DenseMap<file_types::ID,
155167 std::string> &Outputs) {
156- auto lookupFailed = [&CanReplayAllOutput] { CanReplayAllOutput = false ; };
157- auto OutputKey =
158- createCompileJobCacheKeyForOutput (CAS, BaseKey, InputIndex);
159-
160- if (!OutputKey) {
161- Diag.diagnose (SourceLoc (), diag::error_cas,
162- toString (OutputKey.takeError ()));
163- return lookupFailed ();
164- }
165-
166- auto OutID = CAS.getID (*OutputKey);
167- auto OutputRef = lookupCacheKey (CAS, Cache, *OutputKey);
168- if (!OutputRef) {
169- Diag.diagnose (SourceLoc (), diag::error_cas,
170- toString (OutputRef.takeError ()));
171- return lookupFailed ();
172- }
173-
174- if (!*OutputRef) {
175- if (CacheRemarks)
176- Diag.diagnose (SourceLoc (), diag::output_cache_miss, InputPath,
177- OutID.toString ());
178- return lookupFailed ();
179- }
180-
181- CachedResultLoader Loader (CAS, **OutputRef);
168+ CachedResultLoader Loader (CAS, OutputRef);
169+ auto OutID = CAS.getID (OutputRef);
182170 LLVM_DEBUG (llvm::dbgs () << " DEBUG: lookup cache key \' " << OutID.toString ()
183171 << " \' for input \' " << InputPath << " \n " ;);
184172 if (auto Err = Loader.replay ([&](file_types::ID Kind,
@@ -228,12 +216,12 @@ bool replayCachedCompilerOutputs(
228216 })) {
229217 Diag.diagnose (SourceLoc (), diag::cache_replay_failed,
230218 toString (std::move (Err)));
231- return lookupFailed () ;
219+ CanReplayAllOutput = false ;
232220 }
233221 };
234222
235223 auto replayOutputFromInput = [&](const InputFile &Input,
236- unsigned InputIndex) {
224+ unsigned InputIndex, ObjectRef OutputRef ) {
237225 auto InputPath = Input.getFileName ();
238226 DenseMap<file_types::ID, std::string> Outputs;
239227 if (!Input.outputFilename ().empty ())
@@ -251,8 +239,11 @@ bool replayCachedCompilerOutputs(
251239
252240 // If this input doesn't produce any outputs, don't try to look up cache.
253241 // This can be a standalone emitModule action that only one input produces
254- // output.
255- if (Outputs.empty ())
242+ // output. The input can be skipped if it is not the first output producing
243+ // input, where it can have diagnostics and symbol graphs attached.
244+ if (Outputs.empty () &&
245+ InputIndex !=
246+ Opts.InputsAndOutputs .getIndexOfFirstOutputProducingInput ())
256247 return ;
257248
258249 // Add cached diagnostic entry for lookup. Output path doesn't matter here.
@@ -262,53 +253,41 @@ bool replayCachedCompilerOutputs(
262253 // Add symbol graph entry for lookup. Output path doesn't matter here.
263254 Outputs.try_emplace (file_types::ID::TY_SymbolGraphFile, " <symbol-graph>" );
264255
265- return replayOutputsForInputFile (Input, InputPath, InputIndex, Outputs);
256+ return replayOutputsForInputFile (Input, InputPath, InputIndex, OutputRef,
257+ Outputs);
266258 };
267259
268- auto AllInputs = Opts.InputsAndOutputs .getAllInputs ();
269- // If there are primary inputs, look up only the primary input files.
270- // Otherwise, prepare to do cache lookup for all inputs.
271- for (unsigned Index = 0 ; Index < AllInputs.size (); ++Index) {
272- const auto &Input = AllInputs[Index];
273- if (Opts.InputsAndOutputs .hasPrimaryInputs () && !Input.isPrimary ())
274- continue ;
275-
276- replayOutputFromInput (Input, Index);
277- }
260+ for (auto In : Inputs)
261+ replayOutputFromInput (In.Input , In.Index , In.OutputRef );
278262
279263 if (!CanReplayAllOutput)
280264 return false ;
281265
282- // If there is not diagnostic output, this is a job that produces no output
283- // and only diagnostics, like `typecheck-module-from-interface`, look up
284- // diagnostics from first file.
285- if (!DiagnosticsOutput)
286- replayOutputsForInputFile (
287- Opts.InputsAndOutputs .getFirstOutputProducingInput (),
288- " <cached-diagnostics>" ,
289- Opts.InputsAndOutputs .getIndexOfFirstOutputProducingInput (),
290- {{file_types::ID::TY_CachedDiagnostics, " <cached-diagnostics>" }});
291-
292- // Check again to make sure diagnostics is fetched successfully.
293- if (!CanReplayAllOutput)
266+ auto failedReplay = [DiagHelper]() {
267+ if (DiagHelper)
268+ DiagHelper->endMessage (/* retCode=*/ 1 );
294269 return false ;
270+ };
295271
296272 // Replay Diagnostics first so the output failures comes after.
297273 // Also if the diagnostics replay failed, proceed to re-compile.
298- if (auto E = CDP.replayCachedDiagnostics (
299- DiagnosticsOutput->Proxy .getData ())) {
300- Diag.diagnose (SourceLoc (), diag::error_replay_cached_diag,
301- toString (std::move (E)));
302- return false ;
274+ if (DiagnosticsOutput) {
275+ // Only starts message if there are diagnostics.
276+ if (DiagHelper)
277+ DiagHelper->beginMessage ();
278+ if (auto E =
279+ CDP.replayCachedDiagnostics (DiagnosticsOutput->Proxy .getData ())) {
280+ Diag.diagnose (SourceLoc (), diag::error_replay_cached_diag,
281+ toString (std::move (E)));
282+ return failedReplay ();
283+ }
303284 }
304285
305286 if (CacheRemarks)
306287 Diag.diagnose (SourceLoc (), diag::replay_output, " <cached-diagnostics>" ,
307288 DiagnosticsOutput->Key .toString ());
308289
309290 // Replay the result only when everything is resolved.
310- // Use on disk output backend directly here to write to disk.
311- llvm::vfs::OnDiskOutputBackend Backend;
312291 for (auto &Output : OutputProxies) {
313292 auto File = Backend.createFile (Output.Path );
314293 if (!File) {
@@ -321,14 +300,14 @@ bool replayCachedCompilerOutputs(
321300 auto Schema = std::make_unique<llvm::mccasformats::v1::MCSchema>(CAS);
322301 if (auto E = Schema->serializeObjectFile (Output.Proxy , *File)) {
323302 Diag.diagnose (SourceLoc (), diag::error_mccas, toString (std::move (E)));
324- return false ;
303+ return failedReplay () ;
325304 }
326305 } else if (Output.Kind == file_types::ID::TY_Dependencies) {
327306 if (emitMakeDependenciesFromSerializedBuffer (
328307 Output.Proxy .getData (), *File, Opts, Output.Input , Diag)) {
329308 Diag.diagnose (SourceLoc (), diag::cache_replay_failed,
330309 " failed to emit dependency file" );
331- return false ;
310+ return failedReplay () ;
332311 }
333312 } else
334313 *File << Output.Proxy .getData ();
@@ -343,9 +322,83 @@ bool replayCachedCompilerOutputs(
343322 Output.Key .toString ());
344323 }
345324
325+ if (DiagHelper)
326+ DiagHelper->endMessage (/* retCode=*/ 0 );
346327 return true ;
347328}
348329
330+ bool replayCachedCompilerOutputs (
331+ ObjectStore &CAS, ActionCache &Cache, ObjectRef BaseKey,
332+ DiagnosticEngine &Diag, const FrontendOptions &Opts,
333+ CachingDiagnosticsProcessor &CDP, bool CacheRemarks, bool UseCASBackend) {
334+ // Compute all the inputs need replay.
335+ llvm::SmallVector<CacheInputEntry> Inputs;
336+ auto AllInputs = Opts.InputsAndOutputs .getAllInputs ();
337+ auto lookupEntry = [&](unsigned InputIndex,
338+ StringRef InputPath) -> std::optional<ObjectRef> {
339+ auto OutputKey =
340+ createCompileJobCacheKeyForOutput (CAS, BaseKey, InputIndex);
341+
342+ if (!OutputKey) {
343+ Diag.diagnose (SourceLoc (), diag::error_cas,
344+ toString (OutputKey.takeError ()));
345+ return std::nullopt ;
346+ }
347+
348+ auto OutID = CAS.getID (*OutputKey);
349+ auto OutputRef = lookupCacheKey (CAS, Cache, *OutputKey);
350+ if (!OutputRef) {
351+ Diag.diagnose (SourceLoc (), diag::error_cas,
352+ toString (OutputRef.takeError ()));
353+ return std::nullopt ;
354+ }
355+
356+ if (!*OutputRef) {
357+ if (CacheRemarks)
358+ Diag.diagnose (SourceLoc (), diag::output_cache_miss, InputPath,
359+ OutID.toString ());
360+ return std::nullopt ;
361+ }
362+
363+ return *OutputRef;
364+ };
365+
366+ // If there are primary inputs, look up only the primary input files.
367+ // Otherwise, prepare to do cache lookup for all inputs.
368+ for (unsigned Index = 0 ; Index < AllInputs.size (); ++Index) {
369+ const auto &Input = AllInputs[Index];
370+ if (Opts.InputsAndOutputs .hasPrimaryInputs () && !Input.isPrimary ())
371+ continue ;
372+
373+ if (Input.outputFilename ().empty () &&
374+ Input.getPrimarySpecificPaths ().SupplementaryOutputs .empty () &&
375+ Index != Opts.InputsAndOutputs .getIndexOfFirstOutputProducingInput ())
376+ continue ;
377+
378+ if (auto OutputRef = lookupEntry (Index, Input.getFileName ()))
379+ Inputs.push_back ({Input, Index, *OutputRef});
380+ else
381+ return false ;
382+ }
383+
384+ // Use on disk output backend directly here to write to disk.
385+ llvm::vfs::OnDiskOutputBackend Backend;
386+ return replayCachedCompilerOutputsImpl (Inputs, CAS, Diag, Opts, CDP,
387+ /* DiagHelper=*/ nullptr , Backend,
388+ CacheRemarks, UseCASBackend);
389+ }
390+
391+ bool replayCachedCompilerOutputsForInput (
392+ ObjectStore &CAS, ObjectRef OutputRef, const InputFile &Input,
393+ unsigned InputIndex, DiagnosticEngine &Diag, DiagnosticHelper &DiagHelper,
394+ OutputBackend &OutBackend, const FrontendOptions &Opts,
395+ CachingDiagnosticsProcessor &CDP, bool CacheRemarks, bool UseCASBackend) {
396+ llvm::SmallVector<CacheInputEntry> Inputs = {{Input, InputIndex, OutputRef}};
397+ return replayCachedCompilerOutputsImpl (Inputs, CAS, Diag, Opts, CDP,
398+ &DiagHelper, OutBackend, CacheRemarks,
399+ UseCASBackend);
400+ }
401+
349402std::unique_ptr<llvm::MemoryBuffer>
350403loadCachedCompileResultFromCacheKey (ObjectStore &CAS, ActionCache &Cache,
351404 DiagnosticEngine &Diag, StringRef CacheKey,
0 commit comments