LLVM 23.0.0git
ActionCaches.cpp
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8///
9/// \file This file implements the underlying ActionCache implementations.
10///
11//===----------------------------------------------------------------------===//
12
13#include "BuiltinCAS.h"
19#include "llvm/Config/llvm-config.h"
20#include "llvm/Support/BLAKE3.h"
21#include "llvm/Support/Errc.h"
22
23#define DEBUG_TYPE "cas-action-caches"
24
25using namespace llvm;
26using namespace llvm::cas;
27
28namespace {
29
30using HasherT = BLAKE3;
31using HashType = decltype(HasherT::hash(std::declval<ArrayRef<uint8_t> &>()));
32
33template <size_t Size> class CacheEntry {
34public:
35 CacheEntry() = default;
36 CacheEntry(ArrayRef<uint8_t> Hash) { llvm::copy(Hash, Value.data()); }
37 CacheEntry(const CacheEntry &Entry) { llvm::copy(Entry.Value, Value.data()); }
38 ArrayRef<uint8_t> getValue() const { return Value; }
39
40private:
41 std::array<uint8_t, Size> Value;
42};
43
44/// Builtin InMemory ActionCache that stores the mapping in memory.
45class InMemoryActionCache final : public ActionCache {
46public:
47 InMemoryActionCache()
49
50 Error putImpl(ArrayRef<uint8_t> ActionKey, const CASID &Result,
51 bool CanBeDistributed) final;
53 bool CanBeDistributed) const final;
54
55 Error validate() const final {
56 return createStringError("InMemoryActionCache doesn't support validate()");
57 }
58
59private:
60 using DataT = CacheEntry<sizeof(HashType)>;
61 using InMemoryCacheT = ThreadSafeTrieRawHashMap<DataT, sizeof(HashType)>;
62
63 InMemoryCacheT Cache;
64};
65
66/// Builtin basic OnDiskActionCache that uses one underlying OnDiskKeyValueDB.
67class OnDiskActionCache final : public ActionCache {
68public:
69 Error putImpl(ArrayRef<uint8_t> ActionKey, const CASID &Result,
70 bool CanBeDistributed) final;
72 bool CanBeDistributed) const final;
73
75
76 Error validate() const final;
77
78private:
79 static StringRef getHashName() { return "BLAKE3"; }
80
81 OnDiskActionCache(std::unique_ptr<ondisk::OnDiskKeyValueDB> DB);
82
83 std::unique_ptr<ondisk::OnDiskKeyValueDB> DB;
84 using DataT = CacheEntry<sizeof(HashType)>;
85};
86
87/// Builtin unified ActionCache that wraps around UnifiedOnDiskCache to provide
88/// access to its ActionCache.
89class UnifiedOnDiskActionCache final : public ActionCache {
90public:
91 Error putImpl(ArrayRef<uint8_t> ActionKey, const CASID &Result,
92 bool CanBeDistributed) final;
94 bool CanBeDistributed) const final;
95
96 UnifiedOnDiskActionCache(std::shared_ptr<ondisk::UnifiedOnDiskCache> UniDB);
97
98 Error validate() const final;
99
100private:
101 std::shared_ptr<ondisk::UnifiedOnDiskCache> UniDB;
102};
103} // end namespace
104
106 const CASContext &Context,
107 CASID Output,
108 ArrayRef<uint8_t> ExistingOutput) {
109 std::string Existing =
110 CASID::create(&Context, toStringRef(ExistingOutput)).toString();
112 toHex(KeyHash, /*LowerCase=*/true, Key);
113 return createStringError(std::make_error_code(std::errc::invalid_argument),
114 "cache poisoned for '" + Key + "' (new='" +
115 Output.toString() + "' vs. existing '" +
116 Existing + "')");
117}
118
120InMemoryActionCache::getImpl(ArrayRef<uint8_t> Key,
121 bool /*CanBeDistributed*/) const {
122 auto Result = Cache.find(Key);
123 if (!Result)
124 return std::nullopt;
125 return CASID::create(&getContext(), toStringRef(Result->Data.getValue()));
126}
127
128Error InMemoryActionCache::putImpl(ArrayRef<uint8_t> Key, const CASID &Result,
129 bool /*CanBeDistributed*/) {
130 DataT Expected(Result.getHash());
131 const InMemoryCacheT::value_type &Cached = *Cache.insertLazy(
132 Key, [&](auto ValueConstructor) { ValueConstructor.emplace(Expected); });
133
134 const DataT &Observed = Cached.Data;
135 if (Expected.getValue() == Observed.getValue())
136 return Error::success();
137
139 Observed.getValue());
140}
141
142namespace llvm::cas {
143
144std::unique_ptr<ActionCache> createInMemoryActionCache() {
145 return std::make_unique<InMemoryActionCache>();
146}
147
148} // namespace llvm::cas
149
150OnDiskActionCache::OnDiskActionCache(
151 std::unique_ptr<ondisk::OnDiskKeyValueDB> DB)
152 : ActionCache(builtin::BuiltinCASContext::getDefaultContext()),
153 DB(std::move(DB)) {}
154
156OnDiskActionCache::create(StringRef AbsPath) {
157 std::shared_ptr<ondisk::OnDiskCASLogger> Logger;
158#ifndef _WIN32
159 if (Error E =
161 return std::move(E);
162#endif
163 std::unique_ptr<ondisk::OnDiskKeyValueDB> DB;
165 AbsPath, getHashName(), sizeof(HashType), getHashName(),
166 sizeof(DataT), /*UnifiedCache=*/nullptr, std::move(Logger))
167 .moveInto(DB))
168 return std::move(E);
169 return std::unique_ptr<OnDiskActionCache>(
170 new OnDiskActionCache(std::move(DB)));
171}
172
174OnDiskActionCache::getImpl(ArrayRef<uint8_t> Key,
175 bool /*CanBeDistributed*/) const {
176 std::optional<ArrayRef<char>> Val;
177 if (Error E = DB->get(Key).moveInto(Val))
178 return std::move(E);
179 if (!Val)
180 return std::nullopt;
181 return CASID::create(&getContext(), toStringRef(*Val));
182}
183
184Error OnDiskActionCache::putImpl(ArrayRef<uint8_t> Key, const CASID &Result,
185 bool /*CanBeDistributed*/) {
186 auto ResultHash = Result.getHash();
187 ArrayRef Expected((const char *)ResultHash.data(), ResultHash.size());
188 ArrayRef<char> Observed;
189 if (Error E = DB->put(Key, Expected).moveInto(Observed))
190 return E;
191
192 if (Expected == Observed)
193 return Error::success();
194
196 Key, getContext(), Result,
197 ArrayRef((const uint8_t *)Observed.data(), Observed.size()));
198}
199
200Error OnDiskActionCache::validate() const {
201 // FIXME: without the matching CAS there is nothing we can check about the
202 // cached values. The hash size is already validated by the DB validator.
203 return DB->validate(nullptr);
204}
205
206UnifiedOnDiskActionCache::UnifiedOnDiskActionCache(
207 std::shared_ptr<ondisk::UnifiedOnDiskCache> UniDB)
208 : ActionCache(builtin::BuiltinCASContext::getDefaultContext()),
209 UniDB(std::move(UniDB)) {}
210
212UnifiedOnDiskActionCache::getImpl(ArrayRef<uint8_t> Key,
213 bool /*CanBeDistributed*/) const {
214 std::optional<ArrayRef<char>> Val;
215 if (Error E = UniDB->getKeyValueDB().get(Key).moveInto(Val))
216 return std::move(E);
217 if (!Val)
218 return std::nullopt;
220 return CASID::create(&getContext(),
221 toStringRef(UniDB->getGraphDB().getDigest(ID)));
222}
223
224Error UnifiedOnDiskActionCache::putImpl(ArrayRef<uint8_t> Key,
225 const CASID &Result,
226 bool /*CanBeDistributed*/) {
227 auto Expected = UniDB->getGraphDB().getReference(Result.getHash());
229 return Expected.takeError();
230
232 std::optional<ArrayRef<char>> Observed;
233 if (Error E = UniDB->getKeyValueDB().put(Key, Value).moveInto(Observed))
234 return E;
235
236 auto ObservedID = ondisk::UnifiedOnDiskCache::getObjectIDFromValue(*Observed);
237 if (*Expected == ObservedID)
238 return Error::success();
239
241 Key, getContext(), Result, UniDB->getGraphDB().getDigest(ObservedID));
242}
243
244Error UnifiedOnDiskActionCache::validate() const {
245 auto ValidateRef = [this](FileOffset Offset, ArrayRef<char> Value) -> Error {
247 auto formatError = [&](Twine Msg) {
248 return createStringError(
250 "bad record at 0x" +
251 utohexstr((unsigned)Offset.get(), /*LowerCase=*/true) + ": " +
252 Msg.str());
253 };
254 if (Error E = UniDB->getGraphDB().validateObjectID(ID))
255 return formatError(llvm::toString(std::move(E)));
256 return Error::success();
257 };
258 return UniDB->getKeyValueDB().validate(ValidateRef);
259}
260
263#if LLVM_ENABLE_ONDISK_CAS
264 return OnDiskActionCache::create(Path);
265#else
266 return createStringError(inconvertibleErrorCode(), "OnDiskCache is disabled");
267#endif
268}
269
270std::unique_ptr<ActionCache>
272 std::shared_ptr<ondisk::UnifiedOnDiskCache> UniDB) {
273 return std::make_unique<UnifiedOnDiskActionCache>(std::move(UniDB));
274}
This file contains the declaration of the ActionCache class, which is the base class for ActionCache ...
static Error createResultCachePoisonedError(ArrayRef< uint8_t > KeyHash, const CASContext &Context, CASID Output, ArrayRef< uint8_t > ExistingOutput)
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
#define LLVM_UNLIKELY(EXPR)
Definition Compiler.h:336
This file declares interface for OnDiskCASLogger, an interface that can be used to log CAS events to ...
This declares OnDiskKeyValueDB, a key value storage database of fixed size key and value.
ArrayRef - Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition ArrayRef.h:40
size_t size() const
size - Get the array size.
Definition ArrayRef.h:142
const T * data() const
Definition ArrayRef.h:139
A class that wraps the BLAKE3 algorithm.
Definition BLAKE3.h:38
Lightweight error class with error context and mandatory checking.
Definition Error.h:159
static ErrorSuccess success()
Create a success value.
Definition Error.h:336
Tagged union holding either a T or a Error.
Definition Error.h:485
Error takeError()
Take ownership of the stored error.
Definition Error.h:612
Logging utility - given an ordered specification of features, and assuming a scalar reward,...
SmallString - A SmallString is just a SmallVector with methods and accessors that make it work better...
Definition SmallString.h:26
StringRef - Represent a constant reference to a string, i.e.
Definition StringRef.h:55
Lock-free thread-safe hash-mapped trie.
pointer find(ArrayRef< uint8_t > Hash)
pointer insertLazy(const_pointer Hint, ArrayRef< uint8_t > Hash, function_ref< void(LazyValueConstructor)> OnConstruct)
Insert with a hint.
Twine - A lightweight data structure for efficiently representing the concatenation of temporary valu...
Definition Twine.h:82
LLVM Value Representation.
Definition Value.h:75
A cache from a key (that describes an action) to the result of performing that action.
Definition ActionCache.h:49
Context for CAS identifiers.
Definition CASID.h:28
Unique identifier for a CAS object.
Definition CASID.h:58
LLVM_ABI std::string toString() const
Return a printable string for CASID.
static CASID create(const CASContext *Context, StringRef Hash)
Create CASID from CASContext and raw hash bytes.
Definition CASID.h:116
static const BuiltinCASContext & getDefaultContext()
static LLVM_ABI Expected< std::unique_ptr< OnDiskCASLogger > > openIfEnabled(const Twine &Path)
Create or append to a log file inside the given CAS directory Path if logging is enabled by the envir...
static LLVM_ABI_FOR_TEST Expected< std::unique_ptr< OnDiskKeyValueDB > > open(StringRef Path, StringRef HashName, unsigned KeySize, StringRef ValueName, size_t ValueSize, UnifiedOnDiskCache *UnifiedCache=nullptr, std::shared_ptr< OnDiskCASLogger > Logger=nullptr)
Open the on-disk store from a directory.
static LLVM_ABI_FOR_TEST ValueBytes getValueFromObjectID(ObjectID ID)
static LLVM_ABI_FOR_TEST ObjectID getObjectIDFromValue(ArrayRef< char > Value)
Helper function to convert the value stored in KeyValueDB and ObjectID.
LLVM_ABI sandboxir::Value * getValue(llvm::Value *V) const
Definition Context.cpp:629
unsigned ID
LLVM IR allows to use arbitrary numbers as calling convention identifiers.
Definition CallingConv.h:24
std::unique_ptr< ActionCache > createActionCacheFromUnifiedOnDiskCache(std::shared_ptr< ondisk::UnifiedOnDiskCache > UniDB)
LLVM_ABI Expected< std::unique_ptr< ActionCache > > createOnDiskActionCache(StringRef Path)
Create an action cache on disk.
LLVM_ABI std::unique_ptr< ActionCache > createInMemoryActionCache()
Create an action cache in memory.
Context & getContext() const
Definition BasicBlock.h:99
This is an optimization pass for GlobalISel generic memory operations.
Definition Types.h:26
@ Offset
Definition DWP.cpp:532
LLVM_ABI std::error_code inconvertibleErrorCode()
The value returned by this function can be returned from convertToErrorCode for Error values where no...
Definition Error.cpp:94
std::string utohexstr(uint64_t X, bool LowerCase=false, unsigned Width=0)
Error createStringError(std::error_code EC, char const *Fmt, const Ts &... Vals)
Create formatted StringError object.
Definition Error.h:1305
@ illegal_byte_sequence
Definition Errc.h:52
LLVM_ATTRIBUTE_VISIBILITY_DEFAULT AnalysisKey InnerAnalysisManagerProxy< AnalysisManagerT, IRUnitT, ExtraArgTs... >::Key
ArrayRef(const T &OneElt) -> ArrayRef< T >
std::string toString(const APInt &I, unsigned Radix, bool Signed, bool formatAsCLiteral=false, bool UpperCase=true, bool InsertSeparators=false)
OutputIt copy(R &&Range, OutputIt Out)
Definition STLExtras.h:1883
void toHex(ArrayRef< uint8_t > Input, bool LowerCase, SmallVectorImpl< char > &Output)
Convert buffer Input to its hexadecimal representation. The returned string is double the size of Inp...
OutputIt move(R &&Range, OutputIt Out)
Provide wrappers to std::move which take ranges instead of having to pass begin/end explicitly.
Definition STLExtras.h:1915
StringRef toStringRef(bool B)
Construct a string ref from a boolean.
Implement std::hash so that hash_code can be used in STL containers.
Definition BitVector.h:870