Skip to content

Commit 81dc1c1

Browse files
committed
[ThinLTO] Extract optionality out of ModuleCacheEntry (#8083)
All existing kinds of `ModuleCacheEntry` contain the same kind of optionality: their `getEntryPath()`, `tryLoadingBuffer()` and `write()` functions are no-op if the ThinLTO key could not be computed in their constructors. This patch extracts the concept of no-op cache entry into `NullModuleCacheEntry`, simplifying the logic. (cherry picked from commit b168a63)
1 parent 0e5035c commit 81dc1c1

File tree

2 files changed

+68
-121
lines changed

2 files changed

+68
-121
lines changed

llvm/include/llvm/LTO/legacy/ThinLTOCodeGenerator.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,8 @@ class ModuleCacheEntry {
6262
}
6363

6464
virtual ~ModuleCacheEntry() {}
65-
protected:
66-
std::optional<std::string> computeCacheKey(
65+
66+
static std::optional<std::string> computeCacheKey(
6767
const ModuleSummaryIndex &Index, StringRef ModuleID,
6868
const FunctionImporter::ImportMapTy &ImportList,
6969
const FunctionImporter::ExportSetTy &ExportList,

llvm/lib/LTO/ThinLTOCodeGenerator.cpp

Lines changed: 66 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -386,48 +386,42 @@ std::unique_ptr<MemoryBuffer> codegenModule(Module &TheModule,
386386
std::move(OutputBuffer), /*RequiresNullTerminator=*/false);
387387
}
388388

389+
struct NullModuleCacheEntry : ModuleCacheEntry {
390+
std::string getEntryPath() override { return "<null>"; }
391+
ErrorOr<std::unique_ptr<MemoryBuffer>> tryLoadingBuffer() override {
392+
return std::error_code();
393+
}
394+
void write(const MemoryBuffer &OutputBuffer) override {}
395+
};
396+
389397
class FileModuleCacheEntry : public ModuleCacheEntry {
390398
public:
399+
static std::unique_ptr<ModuleCacheEntry> create(StringRef CachePath,
400+
std::string Key) {
401+
if (CachePath.empty())
402+
return std::make_unique<NullModuleCacheEntry>();
403+
return std::make_unique<FileModuleCacheEntry>(CachePath, std::move(Key));
404+
}
405+
391406
// Create a cache entry. This compute a unique hash for the Module considering
392407
// the current list of export/import, and offer an interface to query to
393408
// access the content in the cache.
394-
FileModuleCacheEntry(
395-
StringRef CachePath, const ModuleSummaryIndex &Index, StringRef ModuleID,
396-
const FunctionImporter::ImportMapTy &ImportList,
397-
const FunctionImporter::ExportSetTy &ExportList,
398-
const std::map<GlobalValue::GUID, GlobalValue::LinkageTypes> &ResolvedODR,
399-
const GVSummaryMapTy &DefinedGVSummaries, unsigned OptLevel,
400-
bool Freestanding, const TargetMachineBuilder &TMBuilder) {
401-
if (CachePath.empty())
402-
return;
403-
404-
std::optional<std::string> Key =
405-
computeCacheKey(Index, ModuleID, ImportList, ExportList, ResolvedODR,
406-
DefinedGVSummaries, OptLevel, Freestanding, TMBuilder);
407-
408-
if (!Key)
409-
return;
410-
409+
FileModuleCacheEntry(StringRef CachePath, std::string Key) {
410+
assert(!CachePath.empty());
411411
// This choice of file name allows the cache to be pruned (see pruneCache()
412412
// in include/llvm/Support/CachePruning.h).
413-
sys::path::append(EntryPath, CachePath, "llvmcache-" + *Key);
413+
sys::path::append(EntryPath, CachePath, "llvmcache-" + Key);
414414
}
415415

416416
std::string getEntryPath() final { return EntryPath.str().str(); }
417417

418418
// Try loading the buffer for this cache entry.
419419
ErrorOr<std::unique_ptr<MemoryBuffer>> tryLoadingBuffer() final {
420-
if (EntryPath.empty())
421-
return std::error_code();
422-
423420
return MemoryBuffer::getFile(EntryPath);
424421
}
425422

426423
// Cache the Produced object file
427424
void write(const MemoryBuffer &OutputBuffer) final {
428-
if (EntryPath.empty())
429-
return;
430-
431425
if (auto Err = llvm::writeToOutput(
432426
EntryPath, [&OutputBuffer](llvm::raw_ostream &OS) -> llvm::Error {
433427
OS << OutputBuffer.getBuffer();
@@ -442,30 +436,24 @@ class FileModuleCacheEntry : public ModuleCacheEntry {
442436
StringRef OutputPath) final {
443437
// Clear output file if exists for hard-linking.
444438
sys::fs::remove(OutputPath);
445-
// Cache is enabled, hard-link the entry (or copy if hard-link fails).
446-
std::string CacheEntryPath = getEntryPath();
447-
if (!CacheEntryPath.empty()) {
448-
auto Err = sys::fs::create_hard_link(CacheEntryPath, OutputPath);
449-
if (!Err)
450-
return Error::success();
451-
// Hard linking failed, try to copy.
452-
Err = sys::fs::copy_file(CacheEntryPath, OutputPath);
453-
if (!Err)
454-
return Error::success();
455-
// Copy failed (could be because the CacheEntry was removed from the cache
456-
// in the meantime by another process), fall back and try to write down
457-
// the buffer to the output.
458-
errs() << "remark: can't link or copy from cached entry '"
459-
<< CacheEntryPath << "' to '" << OutputPath << "'\n";
460-
}
439+
// Hard-link the entry (or copy if hard-link fails).
440+
auto Err = sys::fs::create_hard_link(EntryPath, OutputPath);
441+
if (!Err)
442+
return Error::success();
443+
// Hard linking failed, try to copy.
444+
Err = sys::fs::copy_file(EntryPath, OutputPath);
445+
if (!Err)
446+
return Error::success();
447+
// Copy failed (could be because the CacheEntry was removed from the cache
448+
// in the meantime by another process), fall back and try to write down
449+
// the buffer to the output.
450+
errs() << "remark: can't link or copy from cached entry '" << EntryPath
451+
<< "' to '" << OutputPath << "'\n";
461452
// Fallback to default.
462453
return ModuleCacheEntry::writeObject(OutputBuffer, OutputPath);
463454
}
464455

465456
std::optional<std::unique_ptr<MemoryBuffer>> getMappedBuffer() final {
466-
if (getEntryPath().empty())
467-
return std::nullopt;
468-
469457
auto ReloadedBufferOrErr = tryLoadingBuffer();
470458
if (auto EC = ReloadedBufferOrErr.getError()) {
471459
// On error, keep the preexisting buffer and print a diagnostic.
@@ -498,54 +486,34 @@ static void handleCASError(
498486
consumeError(std::move(E));
499487
}
500488

489+
static cas::CASID createCASProxyOrAbort(cas::ObjectStore &CAS, StringRef Key) {
490+
// Create the key by inserting cache key (SHA1) into CAS to create an ID for
491+
// the correct context.
492+
// TODO: We can have an alternative hashing function that doesn't need to
493+
// store the key into CAS to get the CacheKey.
494+
auto CASKey = CAS.createProxy(std::nullopt, Key);
495+
if (!CASKey)
496+
report_fatal_error(CASKey.takeError());
497+
return CASKey->getID();
498+
}
499+
501500
class CASModuleCacheEntry : public ModuleCacheEntry {
502501
public:
503502
// Create a cache entry. This compute a unique hash for the Module considering
504503
// the current list of export/import, and offer an interface to query to
505504
// access the content in the cache.
506505
CASModuleCacheEntry(
507-
cas::ObjectStore &CAS, cas::ActionCache &Cache,
508-
const ModuleSummaryIndex &Index, StringRef ModuleID,
509-
const FunctionImporter::ImportMapTy &ImportList,
510-
const FunctionImporter::ExportSetTy &ExportList,
511-
const std::map<GlobalValue::GUID, GlobalValue::LinkageTypes> &ResolvedODR,
512-
const GVSummaryMapTy &DefinedGVSummaries, unsigned OptLevel,
513-
bool Freestanding, const TargetMachineBuilder &TMBuilder,
506+
cas::ObjectStore &CAS, cas::ActionCache &Cache, std::string Key,
514507
std::function<void(llvm::function_ref<void(raw_ostream &OS)>)> Logger)
515-
: CAS(CAS), Cache(Cache), Logger(std::move(Logger)) {
516-
std::optional<std::string> Key =
517-
computeCacheKey(Index, ModuleID, ImportList, ExportList, ResolvedODR,
518-
DefinedGVSummaries, OptLevel, Freestanding, TMBuilder);
519-
520-
if (!Key)
521-
return;
522-
523-
// Create the key by inserting cache key (SHA1) into CAS to create a ID for
524-
// the correct context.
525-
// TODO: We can have an alternative hashing function that doesn't
526-
// need to store the key into CAS to get the CacheKey.
527-
auto CASKey = CAS.createProxy(std::nullopt, *Key);
528-
if (!CASKey) {
529-
handleCASError(CASKey.takeError(), this->Logger);
530-
// return as if the key doesn't exist, which will be treated as miss.
531-
return;
532-
}
533-
534-
ID = CASKey->getID();
535-
}
508+
: CAS(CAS), Cache(Cache), ID(createCASProxyOrAbort(CAS, Key)),
509+
Logger(std::move(Logger)) {}
536510

537511
std::string getEntryPath() final {
538-
if (!ID)
539-
return "";
540-
541-
return ID->toString();
512+
return ID.toString();
542513
}
543514

544515
// Try loading the buffer for this cache entry.
545516
ErrorOr<std::unique_ptr<MemoryBuffer>> tryLoadingBuffer() final {
546-
if (!ID)
547-
return std::error_code();
548-
549517
std::optional<cas::CASID> MaybeKeyID;
550518
{
551519
ScopedDurationTimer ScopedTime([&](double Seconds) {
@@ -557,7 +525,7 @@ class CASModuleCacheEntry : public ModuleCacheEntry {
557525
}
558526
});
559527

560-
if (Error E = Cache.get(*ID, /*Globally=*/true).moveInto(MaybeKeyID)) {
528+
if (Error E = Cache.get(ID, /*Globally=*/true).moveInto(MaybeKeyID)) {
561529
handleCASError(std::move(E), Logger);
562530
// If handleCASError didn't abort, treat as miss.
563531
return std::error_code();
@@ -586,11 +554,8 @@ class CASModuleCacheEntry : public ModuleCacheEntry {
586554
return MaybeObject->getMemoryBuffer("", /*NullTerminated=*/true);
587555
}
588556

589-
// Cache the Produced object file
557+
// Cache the computed object file.
590558
void write(const MemoryBuffer &OutputBuffer) final {
591-
if (!ID)
592-
return;
593-
594559
std::optional<cas::ObjectProxy> Proxy;
595560
{
596561
ScopedDurationTimer ScopedTime([&](double Seconds) {
@@ -616,14 +581,14 @@ class CASModuleCacheEntry : public ModuleCacheEntry {
616581
}
617582
});
618583

619-
if (auto Err = Cache.put(*ID, Proxy->getID(), /*Globally=*/true))
584+
if (auto Err = Cache.put(ID, Proxy->getID(), /*Globally=*/true))
620585
handleCASError(std::move(Err), Logger);
621586
}
622587

623588
private:
624589
cas::ObjectStore &CAS;
625590
cas::ActionCache &Cache;
626-
std::optional<cas::CASID> ID;
591+
cas::CASID ID;
627592
std::function<void(llvm::function_ref<void(raw_ostream &OS)>)> Logger;
628593
};
629594

@@ -633,33 +598,16 @@ class RemoteModuleCacheEntry : public ModuleCacheEntry {
633598
// the current list of export/import, and offer an interface to query to
634599
// access the content in the cache.
635600
RemoteModuleCacheEntry(
636-
cas::remote::ClientServices &Service, const ModuleSummaryIndex &Index,
637-
StringRef ModuleID, StringRef OutputPath,
638-
const FunctionImporter::ImportMapTy &ImportList,
639-
const FunctionImporter::ExportSetTy &ExportList,
640-
const std::map<GlobalValue::GUID, GlobalValue::LinkageTypes> &ResolvedODR,
641-
const GVSummaryMapTy &DefinedGVSummaries, unsigned OptLevel,
642-
bool Freestanding, const TargetMachineBuilder &TMBuilder,
601+
cas::remote::ClientServices &Service, StringRef OutputPath,
602+
std::string Key,
643603
std::function<void(llvm::function_ref<void(raw_ostream &OS)>)> Logger)
644-
: Service(Service), OutputPath(OutputPath.str()),
645-
Logger(std::move(Logger)) {
646-
std::optional<std::string> Key =
647-
computeCacheKey(Index, ModuleID, ImportList, ExportList, ResolvedODR,
648-
DefinedGVSummaries, OptLevel, Freestanding, TMBuilder);
649-
650-
if (!Key)
651-
return;
652-
653-
ID = *Key;
654-
}
604+
: Service(Service), ID(std::move(Key)), OutputPath(OutputPath.str()),
605+
Logger(std::move(Logger)) {}
655606

656607
std::string getEntryPath() final { return ID; }
657608

658609
// Try loading the buffer for this cache entry.
659610
ErrorOr<std::unique_ptr<MemoryBuffer>> tryLoadingBuffer() final {
660-
if (ID.empty())
661-
return std::error_code();
662-
663611
// Lookup the output value from KVDB.
664612
std::optional<cas::remote::KeyValueDBClient::ValueTy> GetResponse;
665613
{
@@ -718,9 +666,6 @@ class RemoteModuleCacheEntry : public ModuleCacheEntry {
718666

719667
// Cache the Produced object file
720668
void write(const MemoryBuffer &OutputBuffer) final {
721-
if (ID.empty())
722-
return;
723-
724669
if (!ProducedOutput)
725670
cantFail(ModuleCacheEntry::writeObject(OutputBuffer, OutputPath));
726671

@@ -1001,21 +946,23 @@ std::unique_ptr<ModuleCacheEntry> ThinLTOCodeGenerator::createModuleCacheEntry(
1001946
const GVSummaryMapTy &DefinedGVSummaries, unsigned OptLevel,
1002947
bool Freestanding, const TargetMachineBuilder &TMBuilder,
1003948
std::function<void(llvm::function_ref<void(raw_ostream &OS)>)> Logger) {
949+
std::optional<std::string> Key = ModuleCacheEntry::computeCacheKey(
950+
Index, ModuleID, ImportList, ExportList, ResolvedODR, DefinedGVSummaries,
951+
OptLevel, Freestanding, TMBuilder);
952+
953+
if (!Key)
954+
return std::make_unique<NullModuleCacheEntry>();
955+
1004956
switch (CacheOptions.Type) {
1005957
case CachingOptions::CacheType::CacheDirectory:
1006-
return std::make_unique<FileModuleCacheEntry>(
1007-
CacheOptions.Path, Index, ModuleID, ImportList, ExportList, ResolvedODR,
1008-
DefinedGVSummaries, OptLevel, Freestanding, TMBuilder);
958+
return FileModuleCacheEntry::create(CacheOptions.Path, std::move(*Key));
1009959
case CachingOptions::CacheType::CAS:
1010960
return std::make_unique<CASModuleCacheEntry>(
1011-
*CacheOptions.CAS, *CacheOptions.Cache, Index, ModuleID, ImportList,
1012-
ExportList, ResolvedODR, DefinedGVSummaries, OptLevel, Freestanding,
1013-
TMBuilder, std::move(Logger));
961+
*CacheOptions.CAS, *CacheOptions.Cache, std::move(*Key),
962+
std::move(Logger));
1014963
case CachingOptions::CacheType::RemoteService:
1015964
return std::make_unique<RemoteModuleCacheEntry>(
1016-
*CacheOptions.Service, Index, ModuleID, OutputPath, ImportList,
1017-
ExportList, ResolvedODR, DefinedGVSummaries, OptLevel, Freestanding,
1018-
TMBuilder, std::move(Logger));
965+
*CacheOptions.Service, OutputPath, std::move(*Key), std::move(Logger));
1019966
}
1020967
}
1021968

@@ -1714,7 +1661,7 @@ void ThinLTOCodeGenerator::run() {
17141661
OS << "Update cached result for " << ModuleIdentifier << "\n";
17151662
});
17161663

1717-
// Commit to the cache (if enabled)
1664+
// Commit to the cache.
17181665
CacheEntry->write(*OutputBuffer);
17191666

17201667
if (UseBufferAPI) {

0 commit comments

Comments
 (0)