Skip to content

Commit b168a63

Browse files
authored
[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.
1 parent a1756e6 commit b168a63

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
@@ -387,48 +387,42 @@ std::unique_ptr<MemoryBuffer> codegenModule(Module &TheModule,
387387
std::move(OutputBuffer), /*RequiresNullTerminator=*/false);
388388
}
389389

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

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

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

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

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

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

538512
std::string getEntryPath() final {
539-
if (!ID)
540-
return "";
541-
542-
return ID->toString();
513+
return ID.toString();
543514
}
544515

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

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

590-
// Cache the Produced object file
558+
// Cache the computed object file.
591559
void write(const MemoryBuffer &OutputBuffer) final {
592-
if (!ID)
593-
return;
594-
595560
std::optional<cas::ObjectProxy> Proxy;
596561
{
597562
ScopedDurationTimer ScopedTime([&](double Seconds) {
@@ -617,14 +582,14 @@ class CASModuleCacheEntry : public ModuleCacheEntry {
617582
}
618583
});
619584

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

624589
private:
625590
cas::ObjectStore &CAS;
626591
cas::ActionCache &Cache;
627-
std::optional<cas::CASID> ID;
592+
cas::CASID ID;
628593
std::function<void(llvm::function_ref<void(raw_ostream &OS)>)> Logger;
629594
};
630595

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

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

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

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

@@ -1002,21 +947,23 @@ std::unique_ptr<ModuleCacheEntry> ThinLTOCodeGenerator::createModuleCacheEntry(
1002947
const GVSummaryMapTy &DefinedGVSummaries, unsigned OptLevel,
1003948
bool Freestanding, const TargetMachineBuilder &TMBuilder,
1004949
std::function<void(llvm::function_ref<void(raw_ostream &OS)>)> Logger) {
950+
std::optional<std::string> Key = ModuleCacheEntry::computeCacheKey(
951+
Index, ModuleID, ImportList, ExportList, ResolvedODR, DefinedGVSummaries,
952+
OptLevel, Freestanding, TMBuilder);
953+
954+
if (!Key)
955+
return std::make_unique<NullModuleCacheEntry>();
956+
1005957
switch (CacheOptions.Type) {
1006958
case CachingOptions::CacheType::CacheDirectory:
1007-
return std::make_unique<FileModuleCacheEntry>(
1008-
CacheOptions.Path, Index, ModuleID, ImportList, ExportList, ResolvedODR,
1009-
DefinedGVSummaries, OptLevel, Freestanding, TMBuilder);
959+
return FileModuleCacheEntry::create(CacheOptions.Path, std::move(*Key));
1010960
case CachingOptions::CacheType::CAS:
1011961
return std::make_unique<CASModuleCacheEntry>(
1012-
*CacheOptions.CAS, *CacheOptions.Cache, Index, ModuleID, ImportList,
1013-
ExportList, ResolvedODR, DefinedGVSummaries, OptLevel, Freestanding,
1014-
TMBuilder, std::move(Logger));
962+
*CacheOptions.CAS, *CacheOptions.Cache, std::move(*Key),
963+
std::move(Logger));
1015964
case CachingOptions::CacheType::RemoteService:
1016965
return std::make_unique<RemoteModuleCacheEntry>(
1017-
*CacheOptions.Service, Index, ModuleID, OutputPath, ImportList,
1018-
ExportList, ResolvedODR, DefinedGVSummaries, OptLevel, Freestanding,
1019-
TMBuilder, std::move(Logger));
966+
*CacheOptions.Service, OutputPath, std::move(*Key), std::move(Logger));
1020967
}
1021968
}
1022969

@@ -1717,7 +1664,7 @@ void ThinLTOCodeGenerator::run() {
17171664
OS << "Update cached result for " << ModuleIdentifier << "\n";
17181665
});
17191666

1720-
// Commit to the cache (if enabled)
1667+
// Commit to the cache.
17211668
CacheEntry->write(*OutputBuffer);
17221669

17231670
if (UseBufferAPI) {

0 commit comments

Comments
 (0)