LLVM 24.0.0git
ObjectStore.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
10#include "llvm/ADT/DenseSet.h"
11#include "llvm/ADT/ScopeExit.h"
12#include "llvm/Support/Debug.h"
13#include "llvm/Support/Errc.h"
17#include "llvm/Support/Path.h"
18#include <deque>
19
20using namespace llvm;
21using namespace llvm::cas;
22
23void CASContext::anchor() {}
24void ObjectStore::anchor() {}
25
30
31std::string CASID::toString() const {
32 std::string S;
33 raw_string_ostream(S) << *this;
34 return S;
35}
36
38 uint64_t InternalRef, std::optional<CASID> ID) {
39 OS << Kind << "=" << InternalRef;
40 if (ID)
41 OS << "[" << *ID << "]";
42}
43
44void ReferenceBase::print(raw_ostream &OS, const ObjectHandle &This) const {
45 assert(this == &This);
46 printReferenceBase(OS, "object-handle", InternalRef, std::nullopt);
47}
48
49void ReferenceBase::print(raw_ostream &OS, const ObjectRef &This) const {
50 assert(this == &This);
51
52 std::optional<CASID> ID;
53#if LLVM_ENABLE_ABI_BREAKING_CHECKS
54 if (CAS)
55 ID = CAS->getID(This);
56#endif
57 printReferenceBase(OS, "object-ref", InternalRef, ID);
58}
59
61 std::optional<ObjectHandle> Handle;
62 if (Error E = loadIfExists(Ref).moveInto(Handle))
63 return std::move(E);
64 if (!Handle)
66 "missing object '" + getID(Ref).toString() + "'");
67 return *Handle;
68}
69
70std::unique_ptr<MemoryBuffer>
72 bool RequiresNullTerminator) {
74 toStringRef(getData(Node, RequiresNullTerminator)), Name,
75 RequiresNullTerminator);
76}
77
78std::unique_ptr<MemoryBuffer>
80 bool RequiresNullTerminator) {
81 return getStandaloneMemoryBufferImpl(Node, Name, RequiresNullTerminator);
82}
83
84std::unique_ptr<MemoryBuffer>
86 bool RequiresNullTerminator) {
88 toStringRef(getData(Node, RequiresNullTerminator)), Name);
89}
90
92 SmallVectorImpl<ObjectRef> &Refs) const {
94 Refs.push_back(Ref);
95 return Error::success();
96 }));
97}
98
100 std::optional<ObjectRef> Ref = getReference(ID);
101 if (!Ref)
102 return createUnknownObjectError(ID);
103
104 return getProxy(*Ref);
105}
106
108 std::optional<ObjectHandle> H;
109 if (Error E = load(Ref).moveInto(H))
110 return std::move(E);
111
112 return ObjectProxy::load(*this, Ref, *H);
113}
114
117 std::optional<ObjectHandle> H;
118 if (Error E = loadIfExists(Ref).moveInto(H))
119 return std::move(E);
120 if (!H)
121 return std::nullopt;
122 return ObjectProxy::load(*this, Ref, *H);
123}
124
126 return createStringError(std::make_error_code(std::errc::invalid_argument),
127 "unknown object '" + ID.toString() + "'");
128}
129
137
140 std::optional<sys::fs::file_status> Status) {
141 // TODO: For the on-disk CAS implementation use cloning to store it as a
142 // standalone file if the file-system supports it and the file is large.
143 uint64_t Size = Status ? Status->getSize() : -1;
144 auto Buffer = MemoryBuffer::getOpenFile(FD, /*Filename=*/"", Size);
145 if (!Buffer)
146 return errorCodeToError(Buffer.getError());
147
148 return store({}, arrayRefFromStringRef<char>((*Buffer)->getBuffer()));
149}
150
152 auto BypassSandbox = sys::sandbox::scopedDisable();
153
155 if (Error E = sys::fs::openNativeFileForRead(Path).moveInto(FD))
156 return E;
157 auto CloseFile = scope_exit([&FD] { sys::fs::closeFile(FD); });
158 return storeFromOpenFile(FD);
159}
160
162 auto BypassSandbox = sys::sandbox::scopedDisable();
163
164 SmallString<256> TmpPath;
165 SmallString<256> Model;
166 Model += sys::path::parent_path(Path);
167 sys::path::append(Model, "%%%%%%%.tmp");
168 if (std::error_code EC = sys::fs::createUniqueFile(Model, TmpPath))
169 return createFileError(Model, EC);
170 auto RemoveTmpFile = scope_exit([&] {
171 if (!TmpPath.empty())
172 sys::fs::remove(TmpPath);
173 });
174
176 std::error_code EC;
177 raw_fd_ostream FS(TmpPath, EC);
178 if (EC)
179 return createFileError(TmpPath, EC);
180 FS.write(Data.begin(), Data.size());
181 FS.close();
182 if (FS.has_error())
183 return createFileError(TmpPath, FS.error());
184
185 if (std::error_code EC = sys::fs::rename(TmpPath, Path))
186 return createFileError(Path, EC);
187 TmpPath.clear();
188
189 return Error::success();
190}
191
193 SmallDenseSet<ObjectRef> ValidatedRefs;
194 SmallVector<ObjectRef, 16> RefsToValidate;
195 RefsToValidate.push_back(Root);
196
197 while (!RefsToValidate.empty()) {
198 ObjectRef Ref = RefsToValidate.pop_back_val();
199 auto [I, Inserted] = ValidatedRefs.insert(Ref);
200 if (!Inserted)
201 continue; // already validated.
202 if (Error E = validateObject(getID(Ref)))
203 return E;
205 if (!Obj)
206 return Obj.takeError();
207 if (Error E = forEachRef(*Obj, [&RefsToValidate](ObjectRef R) -> Error {
208 RefsToValidate.push_back(R);
209 return Error::success();
210 }))
211 return E;
212 }
213 return Error::success();
214}
215
218 // Copy the full CAS tree from upstream with depth-first ordering to ensure
219 // all the child nodes are available in downstream CAS before inserting
220 // current object. This uses a similar algorithm as
221 // `OnDiskGraphDB::importFullTree` but doesn't assume the upstream CAS schema
222 // so it can be used to import from any other ObjectStore reguardless of the
223 // CAS schema.
224
225 // There is no work to do if importing from self.
226 if (this == &Upstream)
227 return Other;
228
229 /// Keeps track of the state of visitation for current node and all of its
230 /// parents. Upstream Cursor holds information only from upstream CAS.
231 struct UpstreamCursor {
234 size_t RefsCount;
235 std::deque<ObjectRef> Refs;
236 };
238 /// PrimaryNodeStack holds the ObjectRef of the current CAS, with nodes either
239 /// just stored in the CAS or nodes already exists in the current CAS.
240 SmallVector<ObjectRef, 128> PrimaryRefStack;
241 /// A map from upstream ObjectRef to current ObjectRef.
243
244 auto enqueueNode = [&](ObjectRef Ref, ObjectHandle Node) {
245 unsigned NumRefs = Upstream.getNumRefs(Node);
246 std::deque<ObjectRef> Refs;
247 for (unsigned I = 0; I < NumRefs; ++I)
248 Refs.push_back(Upstream.readRef(Node, I));
249
250 CursorStack.push_back({Ref, Node, NumRefs, std::move(Refs)});
251 };
252
253 auto UpstreamHandle = Upstream.load(Other);
254 if (!UpstreamHandle)
255 return UpstreamHandle.takeError();
256 enqueueNode(Other, *UpstreamHandle);
257
258 while (!CursorStack.empty()) {
259 UpstreamCursor &Cur = CursorStack.back();
260 if (Cur.Refs.empty()) {
261 // Copy the node data into the primary store.
262 // The bottom of \p PrimaryRefStack contains the ObjectRef for the
263 // current node.
264 assert(PrimaryRefStack.size() >= Cur.RefsCount);
265 auto Refs = ArrayRef(PrimaryRefStack)
266 .slice(PrimaryRefStack.size() - Cur.RefsCount);
267 auto NewNode = store(Refs, Upstream.getData(Cur.Node));
268 if (!NewNode)
269 return NewNode.takeError();
270
271 // Remove the current node and its IDs from the stack.
272 PrimaryRefStack.truncate(PrimaryRefStack.size() - Cur.RefsCount);
273
274 // Push new node into created objects.
275 PrimaryRefStack.push_back(*NewNode);
276 CreatedObjects.try_emplace(Cur.Ref, *NewNode);
277
278 // Pop the cursor in the end after all uses.
279 CursorStack.pop_back();
280 continue;
281 }
282
283 // Check if the node exists already.
284 auto CurrentID = Cur.Refs.front();
285 Cur.Refs.pop_front();
286 auto Ref = CreatedObjects.find(CurrentID);
287 if (Ref != CreatedObjects.end()) {
288 // If exists already, just need to enqueue the primary node.
289 PrimaryRefStack.push_back(Ref->second);
290 continue;
291 }
292
293 // Load child.
294 auto PrimaryID = Upstream.load(CurrentID);
295 if (LLVM_UNLIKELY(!PrimaryID))
296 return PrimaryID.takeError();
297
298 enqueueNode(CurrentID, *PrimaryID);
299 }
300
301 assert(PrimaryRefStack.size() == 1);
302 return PrimaryRefStack.front();
303}
304
305std::unique_ptr<MemoryBuffer>
307 bool RequiresNullTerminator) const {
308 return CAS->getMemoryBuffer(H, Name, RequiresNullTerminator);
309}
310
311std::unique_ptr<MemoryBuffer>
313 bool RequiresNullTerminator) const {
314 return CAS->getStandaloneMemoryBuffer(H, Name, RequiresNullTerminator);
315}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
AMDGPU Mark last scratch load
#define LLVM_UNLIKELY(EXPR)
Definition Compiler.h:344
#define LLVM_DUMP_METHOD
Mark debug helper function definitions like dump() that should not be stripped from debug builds.
Definition Compiler.h:678
This file defines the DenseSet and SmallDenseSet classes.
#define I(x, y, z)
Definition MD5.cpp:57
#define H(x, y, z)
Definition MD5.cpp:56
static void printReferenceBase(raw_ostream &OS, StringRef Kind, uint64_t InternalRef, std::optional< CASID > ID)
This file contains the declaration of the ObjectStore class.
This file defines the scope_exit class, which executes user-defined cleanup logic at scope exit.
Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition ArrayRef.h:40
iterator find(const_arg_type_t< KeyT > Val)
Definition DenseMap.h:223
std::pair< iterator, bool > try_emplace(KeyT &&Key, Ts &&...Args)
Definition DenseMap.h:299
iterator end()
Definition DenseMap.h:141
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
static ErrorOr< std::unique_ptr< MemoryBuffer > > getOpenFile(sys::fs::file_t FD, const Twine &Filename, uint64_t FileSize, bool RequiresNullTerminator=true, bool IsVolatile=false, std::optional< Align > Alignment=std::nullopt)
Given an already-open file descriptor, read the file and return a MemoryBuffer.
static std::unique_ptr< MemoryBuffer > getMemBuffer(StringRef InputData, StringRef BufferName="", bool RequiresNullTerminator=true)
Open the specified memory range as a MemoryBuffer.
static std::unique_ptr< MemoryBuffer > getMemBufferCopy(StringRef InputData, const Twine &BufferName="")
Open the specified memory range as a MemoryBuffer, copying the contents and taking ownership of it.
Implements a dense probed hash-table based set with some number of buckets stored inline.
Definition DenseSet.h:293
SmallString - A SmallString is just a SmallVector with methods and accessors that make it work better...
Definition SmallString.h:26
This class consists of common code factored out of the SmallVector class to reduce code duplication b...
void truncate(size_type N)
Like resize, but requires that N is less than size().
void push_back(const T &Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Represent a constant reference to a string, i.e.
Definition StringRef.h:56
Unique identifier for a CAS object.
Definition CASID.h:58
LLVM_ABI void dump() const
void print(raw_ostream &OS) const
Print CASID.
Definition CASID.h:68
LLVM_ABI std::string toString() const
Return a printable string for CASID.
Handle to a loaded object in a ObjectStore instance.
void print(raw_ostream &OS) const
Print internal ref and/or CASID. Only suitable for debugging.
LLVM_DUMP_METHOD void dump() const
static ObjectProxy load(ObjectStore &CAS, ObjectRef Ref, ObjectHandle Node)
LLVM_ABI std::unique_ptr< MemoryBuffer > getMemoryBuffer(StringRef Name="", bool RequiresNullTerminator=true) const
LLVM_ABI std::unique_ptr< MemoryBuffer > getStandaloneMemoryBuffer(StringRef Name="", bool RequiresNullTerminator=true) const
Get a MemoryBuffer that stays valid after the CAS is destroyed.
Reference to an object in an ObjectStore instance.
void print(raw_ostream &OS) const
Print internal ref and/or CASID. Only suitable for debugging.
LLVM_DUMP_METHOD void dump() const
Expected< ObjectHandle > load(ObjectRef Ref)
Like loadIfExists but returns an error if the object is missing.
Expected< ObjectProxy > createProxy(ArrayRef< ObjectRef > Refs, StringRef Data)
Helper functions to store object and returns a ObjectProxy.
virtual void print(raw_ostream &) const
Print the ObjectStore internals for debugging purpose.
virtual Error validateObject(const CASID &ID)=0
Validate the underlying object referred by CASID.
Expected< ObjectRef > importObject(ObjectStore &Upstream, ObjectRef Other)
Import object from another CAS.
Expected< ObjectRef > storeFromOpenFile(sys::fs::file_t FD, std::optional< sys::fs::file_status > Status=std::nullopt)
Default implementation reads FD and calls storeNode().
Expected< std::optional< ObjectProxy > > getProxyIfExists(ObjectRef Ref)
virtual Expected< ObjectRef > store(ArrayRef< ObjectRef > Refs, ArrayRef< char > Data)=0
Store object into ObjectStore.
virtual std::unique_ptr< MemoryBuffer > getStandaloneMemoryBufferImpl(ObjectHandle Node, StringRef Name, bool RequiresNullTerminator)
Customization point for getStandaloneMemoryBuffer().
virtual ArrayRef< char > getData(ObjectHandle Node, bool RequiresNullTerminator=false) const =0
virtual Error exportDataToFile(ObjectHandle Node, StringRef Path) const
Exports the data of an object to a file path.
virtual CASID getID(ObjectRef Ref) const =0
Get an ID for Ref.
static Error createUnknownObjectError(const CASID &ID)
virtual Expected< std::optional< ObjectHandle > > loadIfExists(ObjectRef Ref)=0
Load the object referenced by Ref.
virtual Expected< ObjectRef > storeFromFile(StringRef Path)
Stores the data of a file into ObjectStore.
Error validateTree(ObjectRef Ref)
Validate the whole node tree.
virtual ObjectRef readRef(ObjectHandle Node, size_t I) const =0
ObjectStore(const CASContext &Context)
virtual Expected< ObjectRef > storeFromOpenFileImpl(sys::fs::file_t FD, std::optional< sys::fs::file_status > Status)
Get ObjectRef from open file.
virtual void readRefs(ObjectHandle Node, SmallVectorImpl< ObjectRef > &Refs) const
Read all the refs from object in a SmallVector.
virtual size_t getNumRefs(ObjectHandle Node) const =0
std::unique_ptr< MemoryBuffer > getStandaloneMemoryBuffer(ObjectHandle Node, StringRef Name="", bool RequiresNullTerminator=true)
Get a MemoryBuffer for Node that stays valid after this ObjectStore is destroyed.
std::unique_ptr< MemoryBuffer > getMemoryBuffer(ObjectHandle Node, StringRef Name="", bool RequiresNullTerminator=true)
Get a MemoryBuffer pointing at Data.
virtual std::optional< ObjectRef > getReference(const CASID &ID) const =0
Get an existing reference to the object called ID.
Expected< ObjectProxy > getProxy(const CASID &ID)
Create ObjectProxy from CASID. If the object doesn't exist, get an error.
virtual Error forEachRef(ObjectHandle Node, function_ref< Error(ObjectRef)> Callback) const =0
Methods for handling objects.
void print(raw_ostream &OS, const ObjectHandle &This) const
std::pair< iterator, bool > insert(const ValueT &V)
Definition DenseSet.h:209
A raw_ostream that writes to a file descriptor.
This class implements an extremely fast bulk output stream that can only output to a stream.
Definition raw_ostream.h:53
A raw_ostream that writes to an std::string.
LLVM_ABI std::error_code closeFile(file_t &F)
Close the file object.
LLVM_ABI std::error_code rename(const Twine &from, const Twine &to)
Rename from to to.
LLVM_ABI std::error_code createUniqueFile(const Twine &Model, int &ResultFD, SmallVectorImpl< char > &ResultPath, OpenFlags Flags=OF_None, unsigned Mode=all_read|all_write)
Create a uniquely named file.
Definition Path.cpp:891
LLVM_ABI std::error_code remove(const Twine &path, bool IgnoreNonExisting=true)
Remove path.
LLVM_ABI Expected< file_t > openNativeFileForRead(const Twine &Name, OpenFlags Flags=OF_None, SmallVectorImpl< char > *RealPath=nullptr)
Opens the file with the given name in a read-only mode, returning its open file descriptor.
LLVM_ABI StringRef parent_path(StringRef path LLVM_LIFETIME_BOUND, Style style=Style::native)
Get parent path.
Definition Path.cpp:478
LLVM_ABI void append(SmallVectorImpl< char > &path, const Twine &a, const Twine &b="", const Twine &c="", const Twine &d="")
Append to path.
Definition Path.cpp:467
ScopedSetting scopedDisable()
Definition IOSandbox.h:36
This is an optimization pass for GlobalISel generic memory operations.
Error createFileError(const Twine &F, Error E)
Concatenate a source file path and/or name with an Error.
Definition Error.h:1415
ArrayRef< CharT > arrayRefFromStringRef(StringRef Input)
Construct an array ref of bytes from a string ref.
scope_exit(Callable) -> scope_exit< Callable >
Error createStringError(std::error_code EC, char const *Fmt, const Ts &... Vals)
Create formatted StringError object.
Definition Error.h:1321
@ invalid_argument
Definition Errc.h:56
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition Debug.cpp:209
@ Ref
The access may reference the value stored in memory.
Definition ModRef.h:32
@ Other
Any other memory.
Definition ModRef.h:68
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)
LLVM_ABI Error errorCodeToError(std::error_code EC)
Helper for converting an std::error_code to a Error.
Definition Error.cpp:107
void consumeError(Error Err)
Consume a Error without doing anything.
Definition Error.h:1106
StringRef toStringRef(bool B)
Construct a string ref from a boolean.