69#define DEBUG_TYPE "on-disk-cas"
87 return ID.takeError();
90 "corrupt object '" +
toHex(*
ID) +
"'");
100 enum class StorageKind : uint8_t {
117 StandaloneLeaf0 = 12,
120 static StringRef getStandaloneFilePrefix(StorageKind SK) {
124 case TrieRecord::StorageKind::Standalone:
126 case TrieRecord::StorageKind::StandaloneLeaf:
128 case TrieRecord::StorageKind::StandaloneLeaf0:
133 enum Limits : int64_t {
135 MaxEmbeddedSize = 64LL * 1024LL - 1,
139 StorageKind SK = StorageKind::Unknown;
144 static uint64_t pack(Data
D) {
145 assert(
D.Offset.get() < (int64_t)(1ULL << 56));
146 uint64_t
Packed = uint64_t(
D.SK) << 56 |
D.Offset.get();
147 assert(
D.SK != StorageKind::Unknown || Packed == 0);
149 Data RoundTrip = unpack(Packed);
151 assert(
D.Offset.get() == RoundTrip.Offset.get());
157 static Data unpack(uint64_t Packed) {
161 D.SK = (StorageKind)(Packed >> 56);
166 TrieRecord() : Storage(0) {}
168 Data
load()
const {
return unpack(Storage); }
169 bool compare_exchange_strong(Data &Existing, Data New);
172 std::atomic<uint64_t> Storage;
182struct DataRecordHandle {
185 enum class NumRefsFlags : uint8_t {
195 enum class DataSizeFlags {
204 enum class RefKindFlags {
213 DataSizeShift = NumRefsShift + NumRefsBits,
215 RefKindShift = DataSizeShift + DataSizeBits,
218 static_assert(((UINT32_MAX << NumRefsBits) & (uint32_t)NumRefsFlags::Max) ==
221 static_assert(((UINT32_MAX << DataSizeBits) & (uint32_t)DataSizeFlags::Max) ==
224 static_assert(((UINT32_MAX << RefKindBits) & (uint32_t)RefKindFlags::Max) ==
230 NumRefsFlags NumRefs;
231 DataSizeFlags DataSize;
232 RefKindFlags RefKind;
234 static uint64_t pack(LayoutFlags LF) {
235 unsigned Packed = ((unsigned)LF.NumRefs << NumRefsShift) |
236 ((
unsigned)LF.DataSize << DataSizeShift) |
237 ((unsigned)LF.RefKind << RefKindShift);
239 LayoutFlags RoundTrip = unpack(Packed);
240 assert(LF.NumRefs == RoundTrip.NumRefs);
241 assert(LF.DataSize == RoundTrip.DataSize);
242 assert(LF.RefKind == RoundTrip.RefKind);
246 static LayoutFlags unpack(uint64_t Storage) {
247 assert(Storage <= UINT8_MAX &&
"Expect storage to fit in a byte");
250 (NumRefsFlags)((Storage >> NumRefsShift) & ((1U << NumRefsBits) - 1));
251 LF.DataSize = (DataSizeFlags)((Storage >> DataSizeShift) &
252 ((1U << DataSizeBits) - 1));
254 (RefKindFlags)((Storage >> RefKindShift) & ((1U << RefKindBits) - 1));
264 using PackTy = uint32_t;
267 static constexpr unsigned LayoutFlagsShift =
268 (
sizeof(PackTy) - 1) * CHAR_BIT;
272 InternalRefArrayRef Refs;
276 LayoutFlags getLayoutFlags()
const {
277 return LayoutFlags::unpack(H->Packed >> Header::LayoutFlagsShift);
281 void skipDataSize(LayoutFlags LF, int64_t &RelOffset)
const;
282 uint32_t getNumRefs()
const;
283 void skipNumRefs(LayoutFlags LF, int64_t &RelOffset)
const;
284 int64_t getRefsRelOffset()
const;
285 int64_t getDataRelOffset()
const;
287 static uint64_t getTotalSize(uint64_t DataRelOffset, uint64_t
DataSize) {
288 return DataRelOffset +
DataSize + 1;
290 uint64_t getTotalSize()
const {
297 explicit Layout(
const Input &
I);
300 uint64_t DataSize = 0;
301 uint32_t NumRefs = 0;
302 int64_t RefsRelOffset = 0;
303 int64_t DataRelOffset = 0;
304 uint64_t getTotalSize()
const {
305 return DataRecordHandle::getTotalSize(DataRelOffset, DataSize);
309 InternalRefArrayRef getRefs()
const {
310 assert(H &&
"Expected valid handle");
311 auto *BeginByte =
reinterpret_cast<const char *
>(H) + getRefsRelOffset();
312 size_t Size = getNumRefs();
314 return InternalRefArrayRef();
315 if (getLayoutFlags().RefKind == RefKindFlags::InternalRef4B)
316 return ArrayRef(
reinterpret_cast<const InternalRef4B *
>(BeginByte),
Size);
317 return ArrayRef(
reinterpret_cast<const InternalRef *
>(BeginByte),
Size);
320 ArrayRef<char> getData()
const {
321 assert(H &&
"Expected valid handle");
322 return ArrayRef(
reinterpret_cast<const char *
>(H) + getDataRelOffset(),
326 static DataRecordHandle create(function_ref<
char *(
size_t Size)>
Alloc,
328 static Expected<DataRecordHandle>
329 createWithError(function_ref<Expected<char *>(
size_t Size)>
Alloc,
331 static DataRecordHandle construct(
char *Mem,
const Input &
I);
333 static DataRecordHandle
get(
const char *Mem) {
334 return DataRecordHandle(
335 *
reinterpret_cast<const DataRecordHandle::Header *
>(Mem));
337 static Expected<DataRecordHandle>
338 getFromDataPool(
const OnDiskDataAllocator &Pool, FileOffset
Offset);
340 explicit operator bool()
const {
return H; }
341 const Header &getHeader()
const {
return *H; }
343 DataRecordHandle() =
default;
344 explicit DataRecordHandle(
const Header &H) : H(&H) {}
347 static DataRecordHandle constructImpl(
char *Mem,
const Input &
I,
349 const Header *H =
nullptr;
353struct OnDiskContent {
354 std::optional<DataRecordHandle> Record;
355 std::optional<ArrayRef<char>> Bytes;
359class StandaloneDataInMemory {
361 OnDiskContent getContent()
const;
363 StandaloneDataInMemory(std::unique_ptr<sys::fs::mapped_file_region> Region,
364 TrieRecord::StorageKind SK)
365 : Region(std::
move(Region)), SK(SK) {
367 bool IsStandalone =
false;
369 case TrieRecord::StorageKind::Standalone:
370 case TrieRecord::StorageKind::StandaloneLeaf:
371 case TrieRecord::StorageKind::StandaloneLeaf0:
382 std::unique_ptr<sys::fs::mapped_file_region> Region;
383 TrieRecord::StorageKind SK;
387template <
size_t NumShards>
class StandaloneDataMap {
388 static_assert(
isPowerOf2_64(NumShards),
"Expected power of 2");
391 uintptr_t insert(ArrayRef<uint8_t> Hash, TrieRecord::StorageKind SK,
392 std::unique_ptr<sys::fs::mapped_file_region> Region);
394 const StandaloneDataInMemory *
lookup(ArrayRef<uint8_t> Hash)
const;
395 bool count(ArrayRef<uint8_t> Hash)
const {
return bool(
lookup(Hash)); }
400 DenseMap<const uint8_t *, std::unique_ptr<StandaloneDataInMemory>> Map;
401 mutable std::mutex Mutex;
403 Shard &getShard(ArrayRef<uint8_t> Hash) {
404 return const_cast<Shard &
>(
405 const_cast<const StandaloneDataMap *
>(
this)->getShard(Hash));
407 const Shard &getShard(ArrayRef<uint8_t> Hash)
const {
408 static_assert(NumShards <= 256,
"Expected only 8 bits of shard");
409 return Shards[Hash[0] % NumShards];
412 Shard Shards[NumShards];
415using StandaloneDataMapTy = StandaloneDataMap<16>;
418class InternalRefVector {
420 void push_back(InternalRef
Ref) {
422 return FullRefs.push_back(
Ref);
424 return SmallRefs.push_back(*Small);
427 FullRefs.reserve(SmallRefs.size() + 1);
428 for (InternalRef4B Small : SmallRefs)
429 FullRefs.push_back(Small);
430 FullRefs.push_back(
Ref);
434 operator InternalRefArrayRef()
const {
435 assert(SmallRefs.empty() || FullRefs.empty());
436 return NeedsFull ? InternalRefArrayRef(FullRefs)
437 : InternalRefArrayRef(SmallRefs);
441 bool NeedsFull =
false;
451 if (Expected<char *> Mem =
Alloc(
L.getTotalSize()))
452 return constructImpl(*Mem,
I, L);
454 return Mem.takeError();
477uintptr_t StandaloneDataMap<N>::insert(
479 std::unique_ptr<sys::fs::mapped_file_region>
Region) {
480 auto &S = getShard(Hash);
481 std::lock_guard<std::mutex> Lock(S.Mutex);
482 auto &V = S.Map[Hash.
data()];
484 V = std::make_unique<StandaloneDataInMemory>(std::move(
Region), SK);
485 return reinterpret_cast<uintptr_t
>(V.get());
489const StandaloneDataInMemory *
491 auto &S = getShard(Hash);
492 std::lock_guard<std::mutex> Lock(S.Mutex);
493 auto I = S.Map.find(Hash.
data());
494 if (
I == S.Map.end())
514 TempFile(TempFile &&
Other) { *
this = std::move(
Other); }
515 TempFile &operator=(TempFile &&
Other) {
516 TmpName = std::move(
Other.TmpName);
530 OnDiskCASLogger *Logger =
nullptr;
533 Error keep(
const Twine &Name);
540class MappedTempFile {
542 char *
data()
const {
return Map.
data(); }
543 size_t size()
const {
return Map.
size(); }
546 assert(Map &&
"Map already destroyed");
548 return Temp.discard();
551 Error keep(
const Twine &Name) {
552 assert(Map &&
"Map already destroyed");
554 return Temp.keep(Name);
557 MappedTempFile(TempFile Temp, sys::fs::mapped_file_region Map)
562 sys::fs::mapped_file_region Map;
576 std::error_code RemoveEC;
617 Logger->logTempFileCreate(ResultPath);
619 TempFile Ret(ResultPath,
FD,
Logger);
620 return std::move(Ret);
623bool TrieRecord::compare_exchange_strong(
Data &Existing,
Data New) {
624 uint64_t ExistingPacked = pack(Existing);
626 if (Storage.compare_exchange_strong(ExistingPacked, NewPacked))
628 Existing = unpack(ExistingPacked);
635 auto HeaderData = Pool.
get(
Offset,
sizeof(DataRecordHandle::Header));
637 return HeaderData.takeError();
639 auto Record = DataRecordHandle::get(HeaderData->data());
643 "data record span passed the end of the data pool");
648DataRecordHandle DataRecordHandle::constructImpl(
char *Mem,
const Input &
I,
650 char *
Next = Mem +
sizeof(Header);
653 Header::PackTy Packed = 0;
654 Packed |= LayoutFlags::pack(L.Flags) << Header::LayoutFlagsShift;
657 switch (L.Flags.DataSize) {
658 case DataSizeFlags::Uses1B:
659 assert(
I.Data.size() <= UINT8_MAX);
660 Packed |= (Header::PackTy)
I.Data.size()
661 << ((
sizeof(Packed) - 2) * CHAR_BIT);
663 case DataSizeFlags::Uses2B:
664 assert(
I.Data.size() <= UINT16_MAX);
665 Packed |= (Header::PackTy)
I.Data.size()
666 << ((
sizeof(Packed) - 4) * CHAR_BIT);
668 case DataSizeFlags::Uses4B:
672 case DataSizeFlags::Uses8B:
682 switch (L.Flags.NumRefs) {
683 case NumRefsFlags::Uses0B:
685 case NumRefsFlags::Uses1B:
686 assert(
I.Refs.size() <= UINT8_MAX);
687 Packed |= (Header::PackTy)
I.Refs.size()
688 << ((
sizeof(Packed) - 2) * CHAR_BIT);
690 case NumRefsFlags::Uses2B:
691 assert(
I.Refs.size() <= UINT16_MAX);
692 Packed |= (Header::PackTy)
I.Refs.size()
693 << ((
sizeof(Packed) - 4) * CHAR_BIT);
695 case NumRefsFlags::Uses4B:
699 case NumRefsFlags::Uses8B:
706 if (!
I.Refs.empty()) {
707 assert((
L.Flags.RefKind == RefKindFlags::InternalRef4B) ==
I.Refs.is4B());
708 ArrayRef<uint8_t> RefsBuffer =
I.Refs.getBuffer();
716 Next[
I.Data.size()] = 0;
719 Header *
H =
new (Mem) Header{
Packed};
724 assert(
Record.getLayoutFlags().DataSize ==
L.Flags.DataSize);
730DataRecordHandle::Layout::Layout(
const Input &
I) {
732 uint64_t RelOffset =
sizeof(Header);
736 NumRefs =
I.Refs.size();
740 I.Refs.is4B() ? RefKindFlags::InternalRef4B : RefKindFlags::InternalRef;
745 if (
DataSize <= UINT8_MAX && Has1B) {
746 Flags.DataSize = DataSizeFlags::Uses1B;
748 }
else if (
DataSize <= UINT16_MAX && Has2B) {
749 Flags.DataSize = DataSizeFlags::Uses2B;
751 }
else if (
DataSize <= UINT32_MAX) {
752 Flags.DataSize = DataSizeFlags::Uses4B;
755 Flags.DataSize = DataSizeFlags::Uses8B;
761 Flags.NumRefs = NumRefsFlags::Uses0B;
762 }
else if (NumRefs <= UINT8_MAX && Has1B) {
763 Flags.NumRefs = NumRefsFlags::Uses1B;
765 }
else if (NumRefs <= UINT16_MAX && Has2B) {
766 Flags.NumRefs = NumRefsFlags::Uses2B;
769 Flags.NumRefs = NumRefsFlags::Uses4B;
780 auto GrowSizeFieldsBy4B = [&]() {
784 assert(Flags.NumRefs != NumRefsFlags::Uses8B &&
785 "Expected to be able to grow NumRefs8B");
791 if (Flags.DataSize < DataSizeFlags::Uses4B)
792 Flags.DataSize = DataSizeFlags::Uses4B;
793 else if (Flags.DataSize < DataSizeFlags::Uses8B)
794 Flags.DataSize = DataSizeFlags::Uses8B;
795 else if (Flags.NumRefs < NumRefsFlags::Uses4B)
796 Flags.NumRefs = NumRefsFlags::Uses4B;
798 Flags.NumRefs = NumRefsFlags::Uses8B;
802 if (Flags.RefKind == RefKindFlags::InternalRef) {
806 GrowSizeFieldsBy4B();
809 RefsRelOffset = RelOffset;
810 RelOffset += 8 * NumRefs;
818 uint64_t RefListSize = 4 * NumRefs;
820 GrowSizeFieldsBy4B();
821 RefsRelOffset = RelOffset;
822 RelOffset += RefListSize;
826 DataRelOffset = RelOffset;
829uint64_t DataRecordHandle::getDataSize()
const {
830 int64_t RelOffset =
sizeof(Header);
831 auto *DataSizePtr =
reinterpret_cast<const char *
>(
H) + RelOffset;
832 switch (getLayoutFlags().
DataSize) {
833 case DataSizeFlags::Uses1B:
834 return (
H->Packed >> ((
sizeof(Header::PackTy) - 2) * CHAR_BIT)) & UINT8_MAX;
835 case DataSizeFlags::Uses2B:
836 return (
H->Packed >> ((
sizeof(Header::PackTy) - 4) * CHAR_BIT)) &
838 case DataSizeFlags::Uses4B:
840 case DataSizeFlags::Uses8B:
846void DataRecordHandle::skipDataSize(LayoutFlags LF, int64_t &RelOffset)
const {
847 if (LF.DataSize >= DataSizeFlags::Uses4B)
849 if (LF.DataSize >= DataSizeFlags::Uses8B)
853uint32_t DataRecordHandle::getNumRefs()
const {
854 LayoutFlags LF = getLayoutFlags();
855 int64_t RelOffset =
sizeof(Header);
856 skipDataSize(LF, RelOffset);
857 auto *NumRefsPtr =
reinterpret_cast<const char *
>(
H) + RelOffset;
858 switch (LF.NumRefs) {
859 case NumRefsFlags::Uses0B:
861 case NumRefsFlags::Uses1B:
862 return (
H->Packed >> ((
sizeof(Header::PackTy) - 2) * CHAR_BIT)) & UINT8_MAX;
863 case NumRefsFlags::Uses2B:
864 return (
H->Packed >> ((
sizeof(Header::PackTy) - 4) * CHAR_BIT)) &
866 case NumRefsFlags::Uses4B:
868 case NumRefsFlags::Uses8B:
874void DataRecordHandle::skipNumRefs(LayoutFlags LF, int64_t &RelOffset)
const {
875 if (LF.NumRefs >= NumRefsFlags::Uses4B)
877 if (LF.NumRefs >= NumRefsFlags::Uses8B)
881int64_t DataRecordHandle::getRefsRelOffset()
const {
882 LayoutFlags LF = getLayoutFlags();
883 int64_t RelOffset =
sizeof(Header);
884 skipDataSize(LF, RelOffset);
885 skipNumRefs(LF, RelOffset);
889int64_t DataRecordHandle::getDataRelOffset()
const {
890 LayoutFlags LF = getLayoutFlags();
891 int64_t RelOffset =
sizeof(Header);
892 skipDataSize(LF, RelOffset);
893 skipNumRefs(LF, RelOffset);
894 uint32_t RefSize = LF.RefKind == RefKindFlags::InternalRef4B ? 4 : 8;
895 RelOffset += RefSize * getNumRefs();
901 if (
auto E = UpstreamDB->validate(Deep, Hasher))
907 auto formatError = [&](
Twine Msg) {
915 if (
Record.Data.size() !=
sizeof(TrieRecord))
916 return formatError(
"wrong data record size");
918 return formatError(
"wrong data record alignment");
920 auto *R =
reinterpret_cast<const TrieRecord *
>(
Record.Data.data());
921 TrieRecord::Data
D = R->load();
922 std::unique_ptr<MemoryBuffer> FileBuffer;
928 return formatError(
"invalid record kind value");
931 auto I = getIndexProxyFromRef(
Ref);
933 return I.takeError();
936 case TrieRecord::StorageKind::Unknown:
941 case TrieRecord::StorageKind::DataPool:
944 if (
D.Offset.get() <= 0 ||
945 D.Offset.get() +
sizeof(DataRecordHandle::Header) >= DataPool.size())
946 return formatError(
"datapool record out of bound");
948 case TrieRecord::StorageKind::Standalone:
949 case TrieRecord::StorageKind::StandaloneLeaf:
950 case TrieRecord::StorageKind::StandaloneLeaf0:
952 getStandalonePath(TrieRecord::getStandaloneFilePrefix(
D.SK), *
I, Path);
959 return formatError(
"record file \'" + Path +
"\' does not exist");
961 FileBuffer = std::move(*File);
963 return formatError(
"record file \'" + Path +
"\' does not exist");
969 auto dataError = [&](
Twine Msg) {
971 "bad data for digest \'" +
toHex(
I->Hash) +
978 case TrieRecord::StorageKind::Unknown:
980 case TrieRecord::StorageKind::DataPool: {
981 auto DataRecord = DataRecordHandle::getFromDataPool(DataPool,
D.Offset);
983 return dataError(
toString(DataRecord.takeError()));
985 for (
auto InternRef : DataRecord->getRefs()) {
986 auto Index = getIndexProxyFromRef(InternRef);
988 return Index.takeError();
991 StoredData = DataRecord->getData();
994 case TrieRecord::StorageKind::Standalone: {
995 if (FileBuffer->getBufferSize() <
sizeof(DataRecordHandle::Header))
996 return dataError(
"data record is not big enough to read the header");
997 auto DataRecord = DataRecordHandle::get(FileBuffer->getBufferStart());
998 if (DataRecord.getTotalSize() < FileBuffer->getBufferSize())
1000 "data record span passed the end of the standalone file");
1001 for (
auto InternRef : DataRecord.getRefs()) {
1002 auto Index = getIndexProxyFromRef(InternRef);
1004 return Index.takeError();
1007 StoredData = DataRecord.getData();
1010 case TrieRecord::StorageKind::StandaloneLeaf:
1011 case TrieRecord::StorageKind::StandaloneLeaf0: {
1013 if (
D.SK == TrieRecord::StorageKind::StandaloneLeaf0) {
1014 if (!FileBuffer->getBuffer().ends_with(
'\0'))
1015 return dataError(
"standalone file is not zero terminated");
1023 Hasher(Refs, StoredData, ComputedHash);
1025 return dataError(
"hash mismatch, got \'" +
toHex(ComputedHash) +
1033 auto formatError = [&](
Twine Msg) {
1042 return formatError(
"zero is not a valid ref");
1052 return formatError(
"not found using hash " +
toHex(Hash));
1054 ObjectID OtherRef = getExternalReference(makeInternalRef(OtherI.
Offset));
1055 if (OtherRef != ExternalRef)
1056 return formatError(
"ref does not match indexed offset " +
1058 " for hash " +
toHex(Hash));
1063 OS <<
"on-disk-root-path: " << RootPath <<
"\n";
1075 auto *R =
reinterpret_cast<const TrieRecord *
>(
Data.data());
1076 TrieRecord::Data
D = R->load();
1079 case TrieRecord::StorageKind::Unknown:
1082 case TrieRecord::StorageKind::DataPool:
1086 case TrieRecord::StorageKind::Standalone:
1087 OS <<
"standalone-data ";
1089 case TrieRecord::StorageKind::StandaloneLeaf:
1090 OS <<
"standalone-leaf ";
1092 case TrieRecord::StorageKind::StandaloneLeaf0:
1093 OS <<
"standalone-leaf+0";
1096 OS <<
" Offset=" << (
void *)
D.Offset.get();
1104 Pool, [](PoolInfo LHS, PoolInfo RHS) {
return LHS.Offset < RHS.Offset; });
1105 for (PoolInfo PI : Pool) {
1106 OS <<
"- addr=" << (
void *)PI.Offset <<
" ";
1107 auto D = DataRecordHandle::getFromDataPool(DataPool,
FileOffset(PI.Offset));
1109 OS <<
"error: " <<
toString(
D.takeError());
1113 OS <<
"record refs=" <<
D->getNumRefs() <<
" data=" <<
D->getDataSize()
1114 <<
" size=" <<
D->getTotalSize()
1115 <<
" end=" << (
void *)(PI.Offset +
D->getTotalSize()) <<
"\n";
1121 auto P = Index.insertLazy(
1127 new (TentativeValue.
Data.
data()) TrieRecord();
1130 return P.takeError();
1132 assert(*
P &&
"Expected insertion");
1133 return getIndexProxyFromPointer(*
P);
1140 return IndexProxy{
P.getOffset(),
P->Hash,
1141 *
const_cast<TrieRecord *
>(
1142 reinterpret_cast<const TrieRecord *
>(
P->Data.data()))};
1146 auto I = indexHash(Hash);
1148 return I.takeError();
1149 return getExternalReference(*
I);
1152ObjectID OnDiskGraphDB::getExternalReference(
const IndexProxy &
I) {
1153 return getExternalReference(makeInternalRef(
I.Offset));
1156std::optional<ObjectID>
1158 bool CheckUpstream) {
1160 [&](std::optional<IndexProxy>
I) -> std::optional<ObjectID> {
1161 if (!CheckUpstream || !UpstreamDB)
1162 return std::nullopt;
1163 std::optional<ObjectID> UpstreamID =
1164 UpstreamDB->getExistingReference(Digest);
1166 return std::nullopt;
1169 return std::nullopt;
1172 return getExternalReference(*
I);
1177 return tryUpstream(std::nullopt);
1179 TrieRecord::Data Obj =
I.Ref.load();
1180 if (Obj.SK == TrieRecord::StorageKind::Unknown)
1181 return tryUpstream(
I);
1182 return getExternalReference(makeInternalRef(
I.Offset));
1187 auto P = Index.recoverFromFileOffset(
Ref.getFileOffset());
1189 return P.takeError();
1190 return getIndexProxyFromPointer(*
P);
1194 auto I = getIndexProxyFromRef(
Ref);
1196 return I.takeError();
1210 reinterpret_cast<const StandaloneDataInMemory *
>(
Data & (-1ULL << 1));
1211 return SDIM->getContent();
1216 assert(DataHandle.getData().end()[0] == 0 &&
"Null termination");
1217 return OnDiskContent{DataHandle, std::nullopt};
1223 return *Content.Bytes;
1224 assert(Content.Record &&
"Expected record or bytes");
1225 return Content.Record->getData();
1229 if (std::optional<DataRecordHandle>
Record =
1231 return Record->getRefs();
1232 return std::nullopt;
1238 auto I = getIndexProxyFromRef(
Ref);
1240 return I.takeError();
1241 TrieRecord::Data Object =
I->Ref.load();
1243 if (Object.SK == TrieRecord::StorageKind::Unknown)
1244 return faultInFromUpstream(ExternalRef);
1246 if (Object.SK == TrieRecord::StorageKind::DataPool)
1256 switch (Object.SK) {
1257 case TrieRecord::StorageKind::Unknown:
1258 case TrieRecord::StorageKind::DataPool:
1260 case TrieRecord::StorageKind::Standalone:
1261 case TrieRecord::StorageKind::StandaloneLeaf0:
1262 case TrieRecord::StorageKind::StandaloneLeaf:
1272 getStandalonePath(TrieRecord::getStandaloneFilePrefix(Object.SK), *
I, Path);
1287 auto Region = std::make_unique<sys::fs::mapped_file_region>(
1293 static_cast<StandaloneDataMapTy *
>(StandaloneData)
1294 ->insert(
I->Hash, Object.SK, std::move(
Region)));
1298 auto Presence = getObjectPresence(
Ref,
true);
1300 return Presence.takeError();
1302 switch (*Presence) {
1303 case ObjectPresence::Missing:
1305 case ObjectPresence::InPrimaryDB:
1307 case ObjectPresence::OnlyInUpstreamDB:
1308 if (
auto FaultInResult = faultInFromUpstream(
Ref); !FaultInResult)
1309 return FaultInResult.takeError();
1316OnDiskGraphDB::getObjectPresence(
ObjectID ExternalRef,
1317 bool CheckUpstream)
const {
1319 auto I = getIndexProxyFromRef(
Ref);
1321 return I.takeError();
1323 TrieRecord::Data Object =
I->Ref.load();
1324 if (Object.SK != TrieRecord::StorageKind::Unknown)
1325 return ObjectPresence::InPrimaryDB;
1327 if (!CheckUpstream || !UpstreamDB)
1328 return ObjectPresence::Missing;
1330 std::optional<ObjectID> UpstreamID =
1331 UpstreamDB->getExistingReference(getDigest(*
I));
1332 return UpstreamID.has_value() ? ObjectPresence::OnlyInUpstreamDB
1333 : ObjectPresence::Missing;
1340void OnDiskGraphDB::getStandalonePath(
StringRef Prefix,
const IndexProxy &
I,
1342 Path.assign(RootPath.begin(), RootPath.end());
1347OnDiskContent StandaloneDataInMemory::getContent()
const {
1353 case TrieRecord::StorageKind::Standalone:
1355 case TrieRecord::StorageKind::StandaloneLeaf0:
1356 Leaf = Leaf0 =
true;
1358 case TrieRecord::StorageKind::StandaloneLeaf:
1365 assert(
Data.drop_back(Leaf0).end()[0] == 0 &&
1366 "Standalone node data missing null termination");
1367 return OnDiskContent{std::nullopt,
1371 DataRecordHandle
Record = DataRecordHandle::get(
Region->data());
1373 "Standalone object record missing null termination for data");
1374 return OnDiskContent{
Record, std::nullopt};
1377static Expected<MappedTempFile>
1381 assert(
Size &&
"Unexpected request for an empty temp file");
1384 return File.takeError();
1398 return MappedTempFile(std::move(*File), std::move(Map));
1406Error OnDiskGraphDB::createStandaloneLeaf(IndexProxy &
I, ArrayRef<char>
Data) {
1407 assert(
Data.size() > TrieRecord::MaxEmbeddedSize &&
1408 "Expected a bigger file for external content...");
1411 TrieRecord::StorageKind SK = Leaf0 ? TrieRecord::StorageKind::StandaloneLeaf0
1412 : TrieRecord::StorageKind::StandaloneLeaf;
1414 SmallString<256>
Path;
1415 int64_t FileSize =
Data.size() + Leaf0;
1416 getStandalonePath(TrieRecord::getStandaloneFilePrefix(SK),
I, Path);
1424 return File.takeError();
1434 TrieRecord::Data Existing;
1436 TrieRecord::Data Leaf{SK, FileOffset()};
1437 if (
I.Ref.compare_exchange_strong(Existing, Leaf)) {
1438 recordStandaloneSizeIncrease(FileSize);
1444 if (Existing.SK == TrieRecord::StorageKind::Unknown)
1452 auto I = getIndexProxyFromRef(getInternalRef(
ID));
1454 return I.takeError();
1458 TrieRecord::Data Existing =
I->Ref.load();
1459 if (Existing.SK != TrieRecord::StorageKind::Unknown)
1464 if (Refs.
empty() &&
Data.size() > TrieRecord::MaxEmbeddedSize)
1465 return createStandaloneLeaf(*
I,
Data);
1470 InternalRefVector InternalRefs;
1472 InternalRefs.push_back(getInternalRef(
Ref));
1476 DataRecordHandle::Input
Input{InternalRefs,
Data};
1479 TrieRecord::StorageKind SK = TrieRecord::StorageKind::Unknown;
1482 std::optional<MappedTempFile> File;
1483 std::optional<uint64_t> FileSize;
1485 getStandalonePath(TrieRecord::getStandaloneFilePrefix(
1486 TrieRecord::StorageKind::Standalone),
1489 return std::move(E);
1492 SK = TrieRecord::StorageKind::Standalone;
1493 return File->data();
1496 if (
Size <= TrieRecord::MaxEmbeddedSize) {
1497 SK = TrieRecord::StorageKind::DataPool;
1498 auto P = DataPool.allocate(
Size);
1500 char *NewAlloc =
nullptr;
1502 P.takeError(), [&](std::unique_ptr<StringError> E) ->
Error {
1503 if (E->convertToErrorCode() == std::errc::not_enough_memory)
1504 return AllocStandaloneFile(Size).moveInto(NewAlloc);
1505 return Error(std::move(E));
1509 return std::move(NewE);
1511 PoolOffset =
P->getOffset();
1513 dbgs() <<
"pool-alloc addr=" << (
void *)PoolOffset.
get()
1515 <<
" end=" << (
void *)(PoolOffset.
get() +
Size) <<
"\n";
1517 return (*P)->data();
1519 return AllocStandaloneFile(
Size);
1526 assert(
Record.getData().end()[0] == 0 &&
"Expected null-termination");
1528 assert(SK != TrieRecord::StorageKind::Unknown);
1529 assert(
bool(File) !=
bool(PoolOffset) &&
1530 "Expected either a mapped file or a pooled offset");
1536 TrieRecord::Data Existing =
I->Ref.load();
1538 TrieRecord::Data NewObject{SK, PoolOffset};
1540 if (Existing.SK == TrieRecord::StorageKind::Unknown) {
1542 if (
Error E = File->keep(Path))
1554 if (Existing.SK == TrieRecord::StorageKind::Unknown) {
1555 if (
I->Ref.compare_exchange_strong(Existing, NewObject)) {
1557 recordStandaloneSizeIncrease(*FileSize);
1563 if (Existing.SK == TrieRecord::StorageKind::Unknown)
1570void OnDiskGraphDB::recordStandaloneSizeIncrease(
size_t SizeIncrease) {
1571 standaloneStorageSize().fetch_add(SizeIncrease, std::memory_order_relaxed);
1574std::atomic<uint64_t> &OnDiskGraphDB::standaloneStorageSize()
const {
1576 assert(UserHeader.
size() ==
sizeof(std::atomic<uint64_t>));
1578 return *
reinterpret_cast<std::atomic<uint64_t> *
>(UserHeader.
data());
1581uint64_t OnDiskGraphDB::getStandaloneStorageSize()
const {
1582 return standaloneStorageSize().load(std::memory_order_relaxed);
1586 return Index.size() + DataPool.size() + getStandaloneStorageSize();
1590 unsigned IndexPercent = Index.size() * 100ULL / Index.capacity();
1591 unsigned DataPercent = DataPool.size() * 100ULL / DataPool.capacity();
1592 return std::max(IndexPercent, DataPercent);
1597 unsigned HashByteSize, OnDiskGraphDB *UpstreamDB,
1598 std::shared_ptr<OnDiskCASLogger> Logger,
1603 constexpr uint64_t MB = 1024ull * 1024ull;
1604 constexpr uint64_t GB = 1024ull * 1024ull * 1024ull;
1607 uint64_t MaxDataPoolSize = 24 * GB;
1610 MaxIndexSize = 1 * GB;
1611 MaxDataPoolSize = 2 * GB;
1616 return CustomSize.takeError();
1618 MaxIndexSize = MaxDataPoolSize = **CustomSize;
1622 std::optional<OnDiskTrieRawHashMap> Index;
1625 HashByteSize * CHAR_BIT,
1626 sizeof(TrieRecord), MaxIndexSize,
1629 return std::move(E);
1631 uint32_t UserHeaderSize =
sizeof(std::atomic<uint64_t>);
1635 std::optional<OnDiskDataAllocator> DataPool;
1641 MaxDataPoolSize, MB, UserHeaderSize, Logger,
1642 [](
void *UserHeaderPtr) {
1643 new (UserHeaderPtr) std::atomic<uint64_t>(0);
1645 .moveInto(DataPool))
1646 return std::move(E);
1647 if (DataPool->getUserHeader().size() != UserHeaderSize)
1649 "unexpected user header in '" + DataPoolPath +
1652 return std::unique_ptr<OnDiskGraphDB>(
1653 new OnDiskGraphDB(AbsPath, std::move(*Index), std::move(*DataPool),
1654 UpstreamDB, Policy, std::move(Logger)));
1660 std::shared_ptr<OnDiskCASLogger>
Logger)
1662 RootPath(RootPath.str()), UpstreamDB(UpstreamDB), FIPolicy(Policy),
1673 StandaloneData =
new StandaloneDataMapTy();
1677 delete static_cast<StandaloneDataMapTy *
>(StandaloneData);
1686 struct UpstreamCursor {
1703 auto enqueueNode = [&](
ObjectID PrimaryID, std::optional<ObjectHandle>
Node) {
1712 enqueueNode(PrimaryID, UpstreamNode);
1714 while (!CursorStack.
empty()) {
1715 UpstreamCursor &Cur = CursorStack.
back();
1716 if (Cur.RefI == Cur.RefE) {
1723 assert(PrimaryNodesStack.
size() >= Cur.RefsCount + 1);
1724 ObjectID PrimaryID = *(PrimaryNodesStack.
end() - Cur.RefsCount - 1);
1725 auto PrimaryRefs =
ArrayRef(PrimaryNodesStack)
1726 .slice(PrimaryNodesStack.
size() - Cur.RefsCount);
1731 PrimaryNodesStack.
truncate(PrimaryNodesStack.
size() - Cur.RefsCount);
1736 ObjectID UpstreamID = *(Cur.RefI++);
1737 auto PrimaryID =
getReference(UpstreamDB->getDigest(UpstreamID));
1739 return PrimaryID.takeError();
1744 enqueueNode(*PrimaryID, std::nullopt);
1747 Expected<std::optional<ObjectHandle>> UpstreamNode =
1748 UpstreamDB->load(UpstreamID);
1751 enqueueNode(*PrimaryID, *UpstreamNode);
1766 auto Data = UpstreamDB->getObjectData(UpstreamNode);
1767 auto UpstreamRefs = UpstreamDB->getObjectRefs(UpstreamNode);
1770 for (ObjectID UpstreamRef : UpstreamRefs) {
1773 return Ref.takeError();
1780Expected<std::optional<ObjectHandle>>
1781OnDiskGraphDB::faultInFromUpstream(
ObjectID PrimaryID) {
1783 return std::nullopt;
1785 auto UpstreamID = UpstreamDB->getReference(
getDigest(PrimaryID));
1787 return UpstreamID.takeError();
1789 Expected<std::optional<ObjectHandle>> UpstreamNode =
1790 UpstreamDB->load(*UpstreamID);
1794 return std::nullopt;
1797 ? importSingleNode(PrimaryID, **UpstreamNode)
1798 : importFullTree(PrimaryID, **UpstreamNode))
1799 return std::move(
E);
1800 return load(PrimaryID);
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
AMDGPU Mark last scratch load
AMDGPU Prepare AGPR Alloc
static GCRegistry::Add< StatepointGC > D("statepoint-example", "an example strategy for statepoint")
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
#define LLVM_UNLIKELY(EXPR)
This file defines the DenseMap class.
static cl::opt< int > PageSize("imp-null-check-page-size", cl::desc("The page size of the target in bytes"), cl::init(4096), cl::Hidden)
static bool lookup(const GsymReader &GR, DataExtractor &Data, uint64_t &Offset, uint64_t BaseAddr, uint64_t Addr, SourceLocations &SrcLocs, llvm::Error &Err)
A Lookup helper functions.
This file declares interface for OnDiskCASLogger, an interface that can be used to log CAS events to ...
This file declares interface for OnDiskDataAllocator, a file backed data pool can be used to allocate...
static constexpr StringLiteral FilePrefixLeaf0
static constexpr StringLiteral DataPoolTableName
static constexpr StringLiteral FilePrefixObject
static constexpr StringLiteral FilePrefixLeaf
static constexpr StringLiteral IndexFilePrefix
static OnDiskContent getContentFromHandle(const OnDiskDataAllocator &DataPool, ObjectHandle OH)
static constexpr StringLiteral DataPoolFilePrefix
static Error createCorruptObjectError(Expected< ArrayRef< uint8_t > > ID)
static size_t getPageSize()
static Expected< MappedTempFile > createTempFile(StringRef FinalPath, uint64_t Size, OnDiskCASLogger *Logger)
static constexpr StringLiteral IndexTableName
This declares OnDiskGraphDB, an ondisk CAS database with a fixed length hash.
This file declares interface for OnDiskTrieRawHashMap, a thread-safe and (mostly) lock-free hash map ...
Provides a library for accessing information about this process and other processes on the operating ...
This file defines the make_scope_exit function, which executes user-defined cleanup logic at scope ex...
ArrayRef - Represent a constant reference to an array (0 or more elements consecutively in memory),...
size_t size() const
size - Get the array size.
ArrayRef< T > drop_back(size_t N=1) const
Drop the last N elements of the array.
bool empty() const
empty - Check if the array is empty.
Lightweight error class with error context and mandatory checking.
static ErrorSuccess success()
Create a success value.
Tagged union holding either a T or a Error.
Error takeError()
Take ownership of the stored error.
Logging utility - given an ordered specification of features, and assuming a scalar reward,...
static ErrorOr< std::unique_ptr< MemoryBuffer > > getFile(const Twine &Filename, bool IsText=false, bool RequiresNullTerminator=true, bool IsVolatile=false, std::optional< Align > Alignment=std::nullopt)
Open the specified file as a MemoryBuffer, returning a new MemoryBuffer if successful,...
MutableArrayRef - Represent a mutable reference to an array (0 or more elements consecutively in memo...
SmallString - A SmallString is just a SmallVector with methods and accessors that make it work better...
This class consists of common code factored out of the SmallVector class to reduce code duplication b...
void reserve(size_type N)
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.
A wrapper around a string literal that serves as a proxy for constructing global tables of StringRefs...
StringRef - Represent a constant reference to a string, i.e.
Twine - A lightweight data structure for efficiently representing the concatenation of temporary valu...
FileOffset is a wrapper around uint64_t to represent the offset of data from the beginning of the fil...
Handle to a loaded object in a ObjectStore instance.
LLVM_ABI_FOR_TEST Expected< ArrayRef< char > > get(FileOffset Offset, size_t Size) const
Get the data of Size stored at the given Offset.
static LLVM_ABI_FOR_TEST Expected< OnDiskDataAllocator > create(const Twine &Path, const Twine &TableName, uint64_t MaxFileSize, std::optional< uint64_t > NewFileInitialSize, uint32_t UserHeaderSize=0, std::shared_ptr< ondisk::OnDiskCASLogger > Logger=nullptr, function_ref< void(void *)> UserHeaderInit=nullptr)
LLVM_ABI_FOR_TEST size_t size() const
OnDiskTrieRawHashMap is a persistent trie data structure used as hash maps.
static LLVM_ABI_FOR_TEST Expected< OnDiskTrieRawHashMap > create(const Twine &Path, const Twine &TrieName, size_t NumHashBits, uint64_t DataSize, uint64_t MaxFileSize, std::optional< uint64_t > NewFileInitialSize, std::shared_ptr< ondisk::OnDiskCASLogger > Logger=nullptr, std::optional< size_t > NewTableNumRootBits=std::nullopt, std::optional< size_t > NewTableNumSubtrieBits=std::nullopt)
Gets or creates a file at Path with a hash-mapped trie named TrieName.
static std::optional< InternalRef4B > tryToShrink(InternalRef Ref)
Shrink to 4B reference.
Array of internal node references.
Standard 8 byte reference inside OnDiskGraphDB.
static InternalRef getFromOffset(FileOffset Offset)
Handle for a loaded node object.
static ObjectHandle fromFileOffset(FileOffset Offset)
static ObjectHandle fromMemory(uintptr_t Ptr)
ObjectHandle(uint64_t Opaque)
uint64_t getOpaqueData() const
Interface for logging low-level on-disk cas operations.
On-disk CAS nodes database, independent of a particular hashing algorithm.
FaultInPolicy
How to fault-in nodes if an upstream database is used.
@ SingleNode
Copy only the requested node.
void print(raw_ostream &OS) const
LLVM_ABI_FOR_TEST Error validateObjectID(ObjectID ID) const
Checks that ID exists in the index.
LLVM_ABI_FOR_TEST Expected< std::optional< ObjectHandle > > load(ObjectID Ref)
Expected< bool > isMaterialized(ObjectID Ref)
Check whether the object associated with Ref is stored in the CAS.
Error validate(bool Deep, HashingFuncT Hasher) const
Validate the OnDiskGraphDB.
object_refs_range getObjectRefs(ObjectHandle Node) const
unsigned getHardStorageLimitUtilization() const
LLVM_ABI_FOR_TEST Error store(ObjectID ID, ArrayRef< ObjectID > Refs, ArrayRef< char > Data)
Associate data & references with a particular object ID.
ArrayRef< uint8_t > getDigest(ObjectID Ref) const
LLVM_ABI_FOR_TEST size_t getStorageSize() const
static LLVM_ABI_FOR_TEST Expected< std::unique_ptr< OnDiskGraphDB > > open(StringRef Path, StringRef HashName, unsigned HashByteSize, OnDiskGraphDB *UpstreamDB=nullptr, std::shared_ptr< OnDiskCASLogger > Logger=nullptr, FaultInPolicy Policy=FaultInPolicy::FullTree)
Open the on-disk store from a directory.
bool containsObject(ObjectID Ref, bool CheckUpstream=true) const
Check whether the object associated with Ref is stored in the CAS.
LLVM_ABI_FOR_TEST ~OnDiskGraphDB()
LLVM_ABI_FOR_TEST Expected< ObjectID > getReference(ArrayRef< uint8_t > Hash)
Form a reference for the provided hash.
function_ref< void( ArrayRef< ArrayRef< uint8_t > >, ArrayRef< char >, SmallVectorImpl< uint8_t > &)> HashingFuncT
Hashing function type for validation.
LLVM_ABI_FOR_TEST ArrayRef< char > getObjectData(ObjectHandle Node) const
LLVM_ABI_FOR_TEST std::optional< ObjectID > getExistingReference(ArrayRef< uint8_t > Digest, bool CheckUpstream=true)
Get an existing reference to the object Digest.
An efficient, type-erasing, non-owning reference to a callable.
This class implements an extremely fast bulk output stream that can only output to a stream.
static unsigned getPageSizeEstimate()
Get the process's estimated page size.
LLVM_ABI Error keep(const Twine &Name)
static LLVM_ABI Expected< TempFile > create(const Twine &Model, unsigned Mode=all_read|all_write, OpenFlags ExtraFlags=OF_None)
This creates a temporary file with createUniqueFile and schedules it for deletion with sys::RemoveFil...
Represents the result of a call to sys::fs::status().
This class represents a memory mapped file.
LLVM_ABI size_t size() const
@ readonly
May only access map via const_data as read only.
@ readwrite
May access map via data and modify it. Written to path.
LLVM_ABI char * data() const
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
constexpr char Align[]
Key for Kernel::Arg::Metadata::mAlign.
unsigned ID
LLVM IR allows to use arbitrary numbers as calling convention identifiers.
constexpr StringLiteral CASFormatVersion
The version for all the ondisk database files.
Expected< std::optional< uint64_t > > getOverriddenMaxMappingSize()
Retrieves an overridden maximum mapping size for CAS files, if any, speicified by LLVM_CAS_MAX_MAPPIN...
Expected< size_t > preallocateFileTail(int FD, size_t CurrentSize, size_t NewSize)
Allocate space for the file FD on disk, if the filesystem supports it.
bool useSmallMappingSize(const Twine &Path)
Whether to use a small file mapping for ondisk databases created in Path.
uint64_t getDataSize(const FuncRecordTy *Record)
Return the coverage map data size for the function.
uint64_t read64le(const void *P)
void write64le(void *P, uint64_t V)
void write32le(void *P, uint32_t V)
uint32_t read32le(const void *P)
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.
std::error_code resize_file_before_mapping_readwrite(int FD, uint64_t Size)
Resize FD to Size before mapping mapped_file_region::readwrite.
LLVM_ABI bool exists(const basic_file_status &status)
Does file exist?
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.
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 std::error_code create_directories(const Twine &path, bool IgnoreExisting=true, perms Perms=owner_all|group_all)
Create all the non-existent directories in path.
LLVM_ABI file_t convertFDToNativeFile(int FD)
Converts from a Posix file descriptor number to a native file handle.
LLVM_ABI std::error_code status(const Twine &path, file_status &result, bool follow=true)
Get file status as if by POSIX stat().
LLVM_ABI void append(SmallVectorImpl< char > &path, const Twine &a, const Twine &b="", const Twine &c="", const Twine &d="")
Append to path.
ScopedSetting scopedDisable()
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.
auto size(R &&Range, std::enable_if_t< std::is_base_of< std::random_access_iterator_tag, typename std::iterator_traits< decltype(Range.begin())>::iterator_category >::value, void > *=nullptr)
Get the size of a range.
ArrayRef< CharT > arrayRefFromStringRef(StringRef Input)
Construct a string ref from an array ref of unsigned chars.
std::error_code make_error_code(BitcodeError E)
bool isAligned(Align Lhs, uint64_t SizeInBytes)
Checks that SizeInBytes is a multiple of the alignment.
Error handleErrors(Error E, HandlerTs &&... Hs)
Pass the ErrorInfo(s) contained in E to their respective handlers.
FunctionAddr VTableAddr uintptr_t uintptr_t DataSize
std::string utohexstr(uint64_t X, bool LowerCase=false, unsigned Width=0)
constexpr bool isPowerOf2_64(uint64_t Value)
Return true if the argument is a power of two > 0 (64 bit edition.)
Error createStringError(std::error_code EC, char const *Fmt, const Ts &... Vals)
Create formatted StringError object.
std::optional< T > expectedToOptional(Expected< T > &&E)
Convert an Expected to an Optional without doing anything.
decltype(auto) get(const PointerIntPair< PointerTy, IntBits, IntType, PtrTraits, Info > &Pair)
void sort(IteratorTy Start, IteratorTy End)
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
@ Ref
The access may reference the value stored in memory.
void cantFail(Error Err, const char *Msg=nullptr)
Report a fatal error if Err is a failure value.
FunctionAddr VTableAddr uintptr_t uintptr_t Data
FunctionAddr VTableAddr Next
auto count(R &&Range, const E &Element)
Wrapper function around std::count to count the number of times an element Element occurs in the give...
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)
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.
LLVM_ABI Error errorCodeToError(std::error_code EC)
Helper for converting an std::error_code to a Error.
void consumeError(Error Err)
Consume a Error without doing anything.
bool isAddrAligned(Align Lhs, const void *Addr)
Checks that Addr is a multiple of the alignment.
Implement std::hash so that hash_code can be used in STL containers.
Proxy for an on-disk index record.
This struct is a compact representation of a valid (non-zero power of two) alignment.
static constexpr Align Of()
Allow constructions of constexpr Align from types.
Const value proxy to access the records stored in TrieRawHashMap.
Value proxy to access the records stored in TrieRawHashMap.
MutableArrayRef< char > Data