aboutsummaryrefslogtreecommitdiff
path: root/llvm/lib/CAS/ActionCaches.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'llvm/lib/CAS/ActionCaches.cpp')
-rw-r--r--llvm/lib/CAS/ActionCaches.cpp182
1 files changed, 182 insertions, 0 deletions
diff --git a/llvm/lib/CAS/ActionCaches.cpp b/llvm/lib/CAS/ActionCaches.cpp
new file mode 100644
index 0000000..6482f5bf
--- /dev/null
+++ b/llvm/lib/CAS/ActionCaches.cpp
@@ -0,0 +1,182 @@
+//===- ActionCaches.cpp -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "BuiltinCAS.h"
+#include "llvm/ADT/TrieRawHashMap.h"
+#include "llvm/CAS/ActionCache.h"
+#include "llvm/CAS/OnDiskKeyValueDB.h"
+#include "llvm/Support/BLAKE3.h"
+#include "llvm/Support/Path.h"
+
+#define DEBUG_TYPE "action-caches"
+
+using namespace llvm;
+using namespace llvm::cas;
+
+namespace {
+
+using HasherT = BLAKE3;
+using HashType = decltype(HasherT::hash(std::declval<ArrayRef<uint8_t> &>()));
+
+template <size_t Size> class CacheEntry {
+public:
+ CacheEntry() = default;
+ CacheEntry(ArrayRef<uint8_t> Hash) { llvm::copy(Hash, Value.data()); }
+ CacheEntry(const CacheEntry &Entry) { llvm::copy(Entry.Value, Value.data()); }
+ ArrayRef<uint8_t> getValue() const { return Value; }
+
+private:
+ std::array<uint8_t, Size> Value;
+};
+
+class InMemoryActionCache final : public ActionCache {
+public:
+ InMemoryActionCache()
+ : ActionCache(builtin::BuiltinCASContext::getDefaultContext()) {}
+
+ Error putImpl(ArrayRef<uint8_t> ActionKey, const CASID &Result,
+ bool Globally) final;
+ Expected<std::optional<CASID>> getImpl(ArrayRef<uint8_t> ActionKey,
+ bool Globally) const final;
+
+private:
+ using DataT = CacheEntry<sizeof(HashType)>;
+ using InMemoryCacheT = ThreadSafeTrieRawHashMap<DataT, sizeof(HashType)>;
+
+ InMemoryCacheT Cache;
+};
+
+class OnDiskActionCache final : public ActionCache {
+public:
+ Error putImpl(ArrayRef<uint8_t> ActionKey, const CASID &Result,
+ bool Globally) final;
+ Expected<std::optional<CASID>> getImpl(ArrayRef<uint8_t> ActionKey,
+ bool Globally) const final;
+
+ static Expected<std::unique_ptr<OnDiskActionCache>> create(StringRef Path);
+
+private:
+ static StringRef getHashName() { return "BLAKE3"; }
+
+ OnDiskActionCache(std::unique_ptr<ondisk::OnDiskKeyValueDB> DB);
+
+ std::unique_ptr<ondisk::OnDiskKeyValueDB> DB;
+ using DataT = CacheEntry<sizeof(HashType)>;
+};
+
+} // end namespace
+
+static std::string hashToString(ArrayRef<uint8_t> Hash) {
+ SmallString<64> Str;
+ toHex(Hash, /*LowerCase=*/true, Str);
+ return Str.str().str();
+}
+
+static Error createResultCachePoisonedError(StringRef Key,
+ const CASContext &Context,
+ CASID Output,
+ ArrayRef<uint8_t> ExistingOutput) {
+ std::string Existing =
+ CASID::create(&Context, toStringRef(ExistingOutput)).toString();
+ return createStringError(std::make_error_code(std::errc::invalid_argument),
+ "cache poisoned for '" + Key + "' (new='" +
+ Output.toString() + "' vs. existing '" +
+ Existing + "')");
+}
+
+Expected<std::optional<CASID>>
+InMemoryActionCache::getImpl(ArrayRef<uint8_t> Key, bool /*Globally*/) const {
+ auto Result = Cache.find(Key);
+ if (!Result)
+ return std::nullopt;
+ return CASID::create(&getContext(), toStringRef(Result->Data.getValue()));
+}
+
+Error InMemoryActionCache::putImpl(ArrayRef<uint8_t> Key, const CASID &Result,
+ bool /*Globally*/) {
+ DataT Expected(Result.getHash());
+ const InMemoryCacheT::value_type &Cached = *Cache.insertLazy(
+ Key, [&](auto ValueConstructor) { ValueConstructor.emplace(Expected); });
+
+ const DataT &Observed = Cached.Data;
+ if (Expected.getValue() == Observed.getValue())
+ return Error::success();
+
+ return createResultCachePoisonedError(hashToString(Key), getContext(), Result,
+ Observed.getValue());
+}
+
+static constexpr StringLiteral DefaultName = "actioncache";
+
+namespace llvm::cas {
+
+std::unique_ptr<ActionCache> createInMemoryActionCache() {
+ return std::make_unique<InMemoryActionCache>();
+}
+
+std::string getDefaultOnDiskActionCachePath() {
+ SmallString<128> Path;
+ if (!llvm::sys::path::cache_directory(Path))
+ report_fatal_error("cannot get default cache directory");
+ llvm::sys::path::append(Path, builtin::DefaultDir, DefaultName);
+ return Path.str().str();
+}
+
+} // namespace llvm::cas
+
+OnDiskActionCache::OnDiskActionCache(
+ std::unique_ptr<ondisk::OnDiskKeyValueDB> DB)
+ : ActionCache(builtin::BuiltinCASContext::getDefaultContext()),
+ DB(std::move(DB)) {}
+
+Expected<std::unique_ptr<OnDiskActionCache>>
+OnDiskActionCache::create(StringRef AbsPath) {
+ std::unique_ptr<ondisk::OnDiskKeyValueDB> DB;
+ if (Error E = ondisk::OnDiskKeyValueDB::open(AbsPath, getHashName(),
+ sizeof(HashType), getHashName(),
+ sizeof(DataT))
+ .moveInto(DB))
+ return std::move(E);
+ return std::unique_ptr<OnDiskActionCache>(
+ new OnDiskActionCache(std::move(DB)));
+}
+
+Expected<std::optional<CASID>>
+OnDiskActionCache::getImpl(ArrayRef<uint8_t> Key, bool /*Globally*/) const {
+ std::optional<ArrayRef<char>> Val;
+ if (Error E = DB->get(Key).moveInto(Val))
+ return std::move(E);
+ if (!Val)
+ return std::nullopt;
+ return CASID::create(&getContext(), toStringRef(*Val));
+}
+
+Error OnDiskActionCache::putImpl(ArrayRef<uint8_t> Key, const CASID &Result,
+ bool /*Globally*/) {
+ auto ResultHash = Result.getHash();
+ ArrayRef Expected((const char *)ResultHash.data(), ResultHash.size());
+ ArrayRef<char> Observed;
+ if (Error E = DB->put(Key, Expected).moveInto(Observed))
+ return E;
+
+ if (Expected == Observed)
+ return Error::success();
+
+ return createResultCachePoisonedError(
+ hashToString(Key), getContext(), Result,
+ ArrayRef((const uint8_t *)Observed.data(), Observed.size()));
+}
+
+Expected<std::unique_ptr<ActionCache>>
+cas::createOnDiskActionCache(StringRef Path) {
+#if LLVM_ENABLE_ONDISK_CAS
+ return OnDiskActionCache::create(Path);
+#else
+ return createStringError(inconvertibleErrorCode(), "OnDiskCache is disabled");
+#endif
+}