@@ -201,6 +201,77 @@ static int PrintSupportedCPUs(std::string TargetStr) {
201
201
202
202
namespace {
203
203
204
+ // / Represents a mechanism for storing and retrieving compilation artifacts.
205
+ // / It includes common functionality and extension points for specific backend
206
+ // / implementations.
207
+ class CachingOutputs {
208
+ public:
209
+ using OutputKind = cas::CompileJobCacheResult::OutputKind;
210
+
211
+ CachingOutputs (CompilerInstance &Clang);
212
+ virtual ~CachingOutputs () = default ;
213
+
214
+ // / \returns true if result was found and replayed, false otherwise.
215
+ virtual Expected<bool >
216
+ tryReplayCachedResult (const llvm::cas::CASID &ResultCacheKey) = 0 ;
217
+
218
+ // / \returns true on failure, false on success.
219
+ virtual bool prepareOutputCollection () = 0;
220
+
221
+ // / Finish writing outputs from a computed result, after a cache miss.
222
+ virtual Error
223
+ finishComputedResult (const llvm::cas::CASID &ResultCacheKey) = 0 ;
224
+
225
+ void finishSerializedDiagnostics ();
226
+
227
+ protected:
228
+ StringRef getPathForOutputKind (OutputKind Kind);
229
+
230
+ bool prepareOutputCollectionCommon (
231
+ IntrusiveRefCntPtr<llvm::vfs::OutputBackend> CacheOutputs);
232
+
233
+ CompilerInstance &Clang;
234
+ cas::CompileJobCacheResult::Builder CachedResultBuilder;
235
+ std::string OutputFile;
236
+ std::string SerialDiagsFile;
237
+ std::string DependenciesFile;
238
+ SmallString<256 > ResultDiags;
239
+ std::unique_ptr<llvm::raw_ostream> ResultDiagsOS;
240
+ SmallString<256 > SerialDiagsBuf;
241
+ Optional<llvm::vfs::OutputFile> SerialDiagsOutput;
242
+ };
243
+
244
+ // / Store and retrieve compilation artifacts using \p llvm::cas::ObjectStore and
245
+ // / \p llvm::cas::ActionCache.
246
+ class ObjectStoreCachingOutputs : public CachingOutputs {
247
+ public:
248
+ ObjectStoreCachingOutputs (CompilerInstance &Clang,
249
+ std::shared_ptr<llvm::cas::ObjectStore> DB,
250
+ std::shared_ptr<llvm::cas::ActionCache> Cache)
251
+ : CachingOutputs(Clang), CAS(std::move(DB)), Cache(std::move(Cache)) {
252
+ CASOutputs = llvm::makeIntrusiveRefCnt<llvm::cas::CASOutputBackend>(*CAS);
253
+ }
254
+
255
+ private:
256
+ Expected<bool >
257
+ tryReplayCachedResult (const llvm::cas::CASID &ResultCacheKey) override ;
258
+
259
+ bool prepareOutputCollection () override ;
260
+
261
+ Error finishComputedResult (const llvm::cas::CASID &ResultCacheKey) override ;
262
+
263
+ // / Replay a cache hit.
264
+ // /
265
+ // / Return status if should exit immediately, otherwise None.
266
+ Optional<int > replayCachedResult (llvm::cas::ObjectRef ResultID,
267
+ bool JustComputedResult);
268
+
269
+ std::shared_ptr<llvm::cas::ObjectStore> CAS;
270
+ std::shared_ptr<llvm::cas::ActionCache> Cache;
271
+ IntrusiveRefCntPtr<llvm::cas::CASOutputBackend> CASOutputs;
272
+ Optional<llvm::cas::ObjectRef> DependenciesOutput;
273
+ };
274
+
204
275
// Manage caching and replay of compile jobs.
205
276
//
206
277
// The high-level model is:
@@ -261,32 +332,22 @@ class CompileJobCache {
261
332
void finishComputedResult (CompilerInstance &Clang, bool Success);
262
333
263
334
private:
264
- // / Replay a cache hit.
265
- // /
266
- // / Return status if should exit immediately, otherwise None.
267
- Optional<int > replayCachedResult (CompilerInstance &Clang,
268
- llvm::cas::ObjectRef ResultID,
269
- bool JustComputedResult);
335
+ int reportCachingBackendError (DiagnosticsEngine &Diag, Error &&E) {
336
+ Diag.Report (diag::err_caching_backend_fail) << llvm::toString (std::move (E));
337
+ return 1 ;
338
+ }
270
339
271
340
bool CacheCompileJob = false ;
272
341
273
342
std::shared_ptr<llvm::cas::ObjectStore> CAS;
274
343
std::shared_ptr<llvm::cas::ActionCache> Cache;
275
- SmallString<256 > ResultDiags;
276
344
Optional<llvm::cas::CASID> ResultCacheKey;
277
- std::unique_ptr<llvm::raw_ostream> ResultDiagsOS;
278
- SmallString<256 > SerialDiagsBuf;
279
- IntrusiveRefCntPtr<llvm::cas::CASOutputBackend> CASOutputs;
280
- cas::CompileJobCacheResult::Builder CachedResultBuilder;
281
- std::string OutputFile;
282
- std::string SerialDiagsFile;
283
- std::string DependenciesFile;
284
- Optional<llvm::cas::ObjectRef> DependenciesOutput;
285
- Optional<llvm::vfs::OutputFile> SerialDiagsOutput;
345
+
346
+ std::unique_ptr<CachingOutputs> CacheBackend;
286
347
};
287
348
} // end anonymous namespace
288
349
289
- StringRef CompileJobCache ::getPathForOutputKind (OutputKind Kind) {
350
+ StringRef CachingOutputs ::getPathForOutputKind (OutputKind Kind) {
290
351
switch (Kind) {
291
352
case OutputKind::MainOutput:
292
353
return OutputFile;
@@ -344,6 +405,13 @@ Optional<int> CompileJobCache::initialize(CompilerInstance &Clang) {
344
405
// other outputs during replay.
345
406
FrontendOpts.IncludeTimestamps = false ;
346
407
408
+ CacheBackend = std::make_unique<ObjectStoreCachingOutputs>(Clang, CAS, Cache);
409
+ return None;
410
+ }
411
+
412
+ CachingOutputs::CachingOutputs (CompilerInstance &Clang) : Clang(Clang) {
413
+ CompilerInvocation &Invocation = Clang.getInvocation ();
414
+ FrontendOptions &FrontendOpts = Invocation.getFrontendOpts ();
347
415
if (!Clang.hasFileManager ())
348
416
Clang.createFileManager ();
349
417
FileManager &FM = Clang.getFileManager ();
@@ -352,7 +420,6 @@ Optional<int> CompileJobCache::initialize(CompilerInstance &Clang) {
352
420
Invocation.getDiagnosticOpts ().DiagnosticSerializationFile , FM);
353
421
DependenciesFile =
354
422
fixupRelativePath (Invocation.getDependencyOutputOpts ().OutputFile , FM);
355
- return None;
356
423
}
357
424
358
425
namespace {
@@ -398,6 +465,28 @@ createBinaryOutputFile(CompilerInstance &Clang, StringRef OutputPath) {
398
465
return O;
399
466
}
400
467
468
+ Expected<bool > ObjectStoreCachingOutputs::tryReplayCachedResult (
469
+ const llvm::cas::CASID &ResultCacheKey) {
470
+ DiagnosticsEngine &Diags = Clang.getDiagnostics ();
471
+
472
+ Expected<Optional<llvm::cas::ObjectRef>> Result = Cache->get (ResultCacheKey);
473
+ if (!Result)
474
+ return Result.takeError ();
475
+
476
+ if (Optional<llvm::cas::ObjectRef> ResultRef = *Result) {
477
+ Diags.Report (diag::remark_compile_job_cache_hit)
478
+ << ResultCacheKey.toString () << CAS->getID (*ResultRef).toString ();
479
+ Optional<int > Status =
480
+ replayCachedResult (*ResultRef, /* JustComputedResult=*/ false );
481
+ assert (Status && " Expected a status for a cache hit" );
482
+ assert (*Status == 0 && " Expected success status for a cache hit" );
483
+ return true ;
484
+ }
485
+ Diags.Report (diag::remark_compile_job_cache_miss)
486
+ << ResultCacheKey.toString ();
487
+ return false ;
488
+ }
489
+
401
490
Optional<int > CompileJobCache::tryReplayCachedResult (CompilerInstance &Clang) {
402
491
if (!CacheCompileJob)
403
492
return None;
@@ -409,29 +498,29 @@ Optional<int> CompileJobCache::tryReplayCachedResult(CompilerInstance &Clang) {
409
498
if (!ResultCacheKey)
410
499
return 1 ;
411
500
412
- Optional<llvm::cas::ObjectRef> Result;
413
- if (auto E = Cache->get (*ResultCacheKey).moveInto (Result))
414
- consumeError (std::move (E)); // ignore error and treat it as a cache miss.
501
+ Expected<bool > ReplayedResult =
502
+ CacheBackend->tryReplayCachedResult (*ResultCacheKey);
503
+ if (!ReplayedResult)
504
+ return reportCachingBackendError (Clang.getDiagnostics (),
505
+ ReplayedResult.takeError ());
506
+ if (*ReplayedResult)
507
+ return 0 ;
415
508
416
- if (Result) {
417
- Diags.Report (diag::remark_compile_job_cache_hit)
418
- << ResultCacheKey->toString () << CAS->getID (*Result).toString ();
419
- Optional<int > Status =
420
- replayCachedResult (Clang, *Result, /* JustComputedResult=*/ false );
421
- assert (Status && " Expected a status for a cache hit" );
422
- return *Status;
423
- }
424
- Diags.Report (diag::remark_compile_job_cache_miss)
425
- << ResultCacheKey->toString ();
509
+ if (CacheBackend->prepareOutputCollection ())
510
+ return 1 ;
511
+
512
+ return None;
513
+ }
426
514
515
+ bool CachingOutputs::prepareOutputCollectionCommon (
516
+ IntrusiveRefCntPtr<llvm::vfs::OutputBackend> CacheOutputs) {
427
517
// Create an on-disk backend for streaming the results live if we run the
428
518
// computation. If we're writing the output as a CASID, skip it here, since
429
519
// it'll be handled during replay.
430
520
IntrusiveRefCntPtr<llvm::vfs::OutputBackend> OnDiskOutputs =
431
521
llvm::makeIntrusiveRefCnt<llvm::vfs::OnDiskOutputBackend>();
432
522
433
523
// Set up the output backend so we can save / cache the result after.
434
- CASOutputs = llvm::makeIntrusiveRefCnt<llvm::cas::CASOutputBackend>(*CAS);
435
524
for (OutputKind K : cas::CompileJobCacheResult::getAllOutputKinds ()) {
436
525
StringRef OutPath = getPathForOutputKind (K);
437
526
if (!OutPath.empty ())
@@ -441,7 +530,7 @@ Optional<int> CompileJobCache::tryReplayCachedResult(CompilerInstance &Clang) {
441
530
// Always filter out the dependencies file, since we build a CAS-specific
442
531
// object for it.
443
532
auto FilterBackend = llvm::vfs::makeFilteringOutputBackend (
444
- CASOutputs ,
533
+ CacheOutputs ,
445
534
[&](StringRef Path, Optional<llvm::vfs::OutputConfig> Config) {
446
535
return Path != DependenciesFile;
447
536
});
@@ -451,11 +540,6 @@ Optional<int> CompileJobCache::tryReplayCachedResult(CompilerInstance &Clang) {
451
540
ResultDiagsOS = std::make_unique<raw_mirroring_ostream>(
452
541
llvm::errs (), std::make_unique<llvm::raw_svector_ostream>(ResultDiags));
453
542
454
- if (!Clang.getDependencyOutputOpts ().OutputFile .empty ())
455
- Clang.addDependencyCollector (std::make_shared<CASDependencyCollector>(
456
- Clang.getDependencyOutputOpts (), *CAS,
457
- [this ](Optional<cas::ObjectRef> Deps) { DependenciesOutput = Deps; }));
458
-
459
543
// FIXME: This should be saving/replaying structured diagnostics, not saving
460
544
// stderr and a separate diagnostics file, thus using the current llvm::errs()
461
545
// colour capabilities and making the choice of whether colors are used, or
@@ -478,6 +562,7 @@ Optional<int> CompileJobCache::tryReplayCachedResult(CompilerInstance &Clang) {
478
562
// Notify the existing diagnostic client that all files were processed.
479
563
Clang.getDiagnosticClient ().finish ();
480
564
565
+ DiagnosticsEngine &Diags = Clang.getDiagnostics ();
481
566
DiagnosticOptions &DiagOpts = Clang.getInvocation ().getDiagnosticOpts ();
482
567
Clang.getDiagnostics ().setClient (
483
568
new TextDiagnosticPrinter (*ResultDiagsOS, &DiagOpts),
@@ -515,7 +600,19 @@ Optional<int> CompileJobCache::tryReplayCachedResult(CompilerInstance &Clang) {
515
600
Diags.takeClient (), std::move (SerializedConsumer)));
516
601
}
517
602
518
- return None;
603
+ return false ;
604
+ }
605
+
606
+ bool ObjectStoreCachingOutputs::prepareOutputCollection () {
607
+ if (prepareOutputCollectionCommon (CASOutputs))
608
+ return true ;
609
+
610
+ if (!Clang.getDependencyOutputOpts ().OutputFile .empty ())
611
+ Clang.addDependencyCollector (std::make_shared<CASDependencyCollector>(
612
+ Clang.getDependencyOutputOpts (), *CAS,
613
+ [this ](Optional<cas::ObjectRef> Deps) { DependenciesOutput = Deps; }));
614
+
615
+ return false ;
519
616
}
520
617
521
618
void CompileJobCache::finishComputedResult (CompilerInstance &Clang,
@@ -524,6 +621,30 @@ void CompileJobCache::finishComputedResult(CompilerInstance &Clang,
524
621
if (!CacheCompileJob)
525
622
return ;
526
623
624
+ CacheBackend->finishSerializedDiagnostics ();
625
+
626
+ // Don't cache failed builds.
627
+ //
628
+ // TODO: Consider caching failed builds! Note: when output files are written
629
+ // without a temporary (non-atomically), failure may cause the removal of a
630
+ // preexisting file. That behaviour is not currently modeled by the cache.
631
+ if (!Success)
632
+ return ;
633
+
634
+ // Existing diagnostic client is finished, create a new one in case we need
635
+ // to print more diagnostics.
636
+ Clang.getDiagnostics ().setClient (
637
+ new TextDiagnosticPrinter (llvm::errs (),
638
+ &Clang.getInvocation ().getDiagnosticOpts ()),
639
+ /* ShouldOwnClient=*/ true );
640
+
641
+ if (Error E = CacheBackend->finishComputedResult (*ResultCacheKey)) {
642
+ reportCachingBackendError (Clang.getDiagnostics (), std::move (E));
643
+ Success = false ;
644
+ }
645
+ }
646
+
647
+ void CachingOutputs::finishSerializedDiagnostics () {
527
648
if (SerialDiagsOutput) {
528
649
llvm::handleAllErrors (
529
650
SerialDiagsOutput->keep (),
@@ -537,15 +658,10 @@ void CompileJobCache::finishComputedResult(CompilerInstance &Clang,
537
658
<< E.getOutputPath () << E.convertToErrorCode ().message ();
538
659
});
539
660
}
661
+ }
540
662
541
- // Don't cache failed builds.
542
- //
543
- // TODO: Consider caching failed builds! Note: when output files are written
544
- // without a temporary (non-atomically), failure may cause the removal of a
545
- // preexisting file. That behaviour is not currently modeled by the cache.
546
- if (!Success)
547
- return ;
548
-
663
+ Error ObjectStoreCachingOutputs::finishComputedResult (
664
+ const llvm::cas::CASID &ResultCacheKey) {
549
665
// FIXME: Stop calling report_fatal_error().
550
666
if (!SerialDiagsOutput) {
551
667
// Not requested to get a serialized diagnostics file but we generated it
@@ -583,20 +699,21 @@ void CompileJobCache::finishComputedResult(CompilerInstance &Clang,
583
699
Expected<cas::ObjectRef> Result = CachedResultBuilder.build (*CAS);
584
700
if (!Result)
585
701
llvm::report_fatal_error (Result.takeError ());
586
- if (llvm::Error E = Cache->put (* ResultCacheKey, *Result))
702
+ if (llvm::Error E = Cache->put (ResultCacheKey, *Result))
587
703
llvm::report_fatal_error (std::move (E));
588
704
589
705
// Replay / decanonicalize as necessary.
590
- Optional<int > Status = replayCachedResult (Clang, *Result,
706
+ Optional<int > Status = replayCachedResult (*Result,
591
707
/* JustComputedResult=*/ true );
592
708
(void )Status;
593
709
assert (Status == None);
710
+ return Error::success ();
594
711
}
595
712
596
713
// / Replay a result after a cache hit.
597
- Optional<int > CompileJobCache::replayCachedResult (CompilerInstance &Clang,
598
- llvm::cas::ObjectRef ResultID,
599
- bool JustComputedResult) {
714
+ Optional<int >
715
+ ObjectStoreCachingOutputs::replayCachedResult ( llvm::cas::ObjectRef ResultID,
716
+ bool JustComputedResult) {
600
717
if (JustComputedResult)
601
718
return None;
602
719
0 commit comments