20#include "llvm/ADT/ArrayRef.h"
21#include "llvm/ADT/ScopeExit.h"
22#include "llvm/ADT/StringExtras.h"
23#include "llvm/ADT/StringRef.h"
24#include "llvm/Support/Endian.h"
25#include "llvm/Support/MemoryBufferRef.h"
26#include "llvm/Support/SourceMgr.h"
36#define DEBUG_TYPE "mlir-bytecode-reader"
48 return "AttrType (2)";
50 return "AttrTypeOffset (3)";
54 return "Resource (5)";
56 return "ResourceOffset (6)";
58 return "DialectVersions (7)";
60 return "Properties (8)";
62 return (
"Unknown (" + Twine(
static_cast<unsigned>(sectionID)) +
")").str();
82 llvm_unreachable(
"unknown section ID");
93 explicit EncodingReader(ArrayRef<uint8_t> contents, Location fileLoc)
94 : buffer(contents), dataIt(buffer.begin()), fileLoc(fileLoc) {}
95 explicit EncodingReader(StringRef contents, Location fileLoc)
96 : EncodingReader({
reinterpret_cast<const uint8_t *
>(contents.data()),
101 bool empty()
const {
return dataIt == buffer.end(); }
104 size_t size()
const {
return buffer.end() - dataIt; }
107 LogicalResult alignTo(
unsigned alignment) {
108 if (!llvm::isPowerOf2_32(alignment))
109 return emitError(
"expected alignment to be a power-of-two");
111 auto isUnaligned = [&](
const uint8_t *ptr) {
112 return ((uintptr_t)ptr & (alignment - 1)) != 0;
119 while (isUnaligned(dataIt)) {
121 if (
failed(parseByte(padding)))
124 return emitError(
"expected alignment byte (0xCB), but got: '0x" +
125 llvm::utohexstr(padding) +
"'");
131 if (LLVM_UNLIKELY(isUnaligned(dataIt))) {
132 return emitError(
"expected data iterator aligned to ", alignment,
133 ", but got pointer: '0x" +
134 llvm::utohexstr((uintptr_t)dataIt) +
"'");
141 template <
typename... Args>
142 InFlightDiagnostic
emitError(Args &&...args)
const {
143 return ::emitError(fileLoc).
append(std::forward<Args>(args)...);
145 InFlightDiagnostic
emitError()
const { return ::emitError(fileLoc); }
148 template <
typename... Args>
149 InFlightDiagnostic
emitWarning(Args &&...args)
const {
150 return ::emitWarning(fileLoc).
append(std::forward<Args>(args)...);
152 InFlightDiagnostic
emitWarning()
const { return ::emitWarning(fileLoc); }
155 template <
typename T>
156 LogicalResult parseByte(T &value) {
158 return emitError(
"attempting to parse a byte at the end of the bytecode");
159 value =
static_cast<T
>(*dataIt++);
163 LogicalResult parseBytes(
size_t length, ArrayRef<uint8_t> &
result) {
164 if (length > size()) {
165 return emitError(
"attempting to parse ", length,
" bytes when only ",
168 result = {dataIt, length};
174 LogicalResult parseBytes(
size_t length, uint8_t *
result) {
175 if (length > size()) {
176 return emitError(
"attempting to parse ", length,
" bytes when only ",
179 memcpy(
result, dataIt, length);
186 LogicalResult parseBlobAndAlignment(ArrayRef<uint8_t> &data,
187 uint64_t &alignment) {
189 if (
failed(parseVarInt(alignment)) ||
failed(parseVarInt(dataSize)) ||
190 failed(alignTo(alignment)))
192 return parseBytes(dataSize, data);
202 LogicalResult parseVarInt(uint64_t &
result) {
209 if (LLVM_LIKELY(
result & 1)) {
217 if (LLVM_UNLIKELY(
result == 0)) {
218 llvm::support::ulittle64_t resultLE;
219 if (
failed(parseBytes(
sizeof(resultLE),
220 reinterpret_cast<uint8_t *
>(&resultLE))))
225 return parseMultiByteVarInt(
result);
231 LogicalResult parseSignedVarInt(uint64_t &
result) {
241 LogicalResult parseVarIntWithFlag(uint64_t &
result,
bool &flag) {
250 LogicalResult skipBytes(
size_t length) {
251 if (length > size()) {
252 return emitError(
"attempting to skip ", length,
" bytes when only ",
261 LogicalResult parseNullTerminatedString(StringRef &
result) {
262 const char *startIt = (
const char *)dataIt;
263 const char *nulIt = (
const char *)memchr(startIt, 0, size());
266 "malformed null-terminated string, no null character found");
268 result = StringRef(startIt, nulIt - startIt);
269 dataIt = (
const uint8_t *)nulIt + 1;
274 using ValidateAlignmentFn =
function_ref<LogicalResult(
unsigned alignment)>;
279 ValidateAlignmentFn alignmentValidator,
280 ArrayRef<uint8_t> §ionData) {
281 uint8_t sectionIDAndHasAlignment;
283 if (
failed(parseByte(sectionIDAndHasAlignment)) ||
284 failed(parseVarInt(length)))
291 bool hasAlignment = sectionIDAndHasAlignment & 0b10000000;
296 return emitError(
"invalid section ID: ",
unsigned(sectionID));
302 if (
failed(parseVarInt(alignment)))
337 if (
failed(alignmentValidator(alignment)))
338 return emitError(
"failed to align section ID: ",
unsigned(sectionID));
341 if (
failed(alignTo(alignment)))
346 return parseBytes(
static_cast<size_t>(length), sectionData);
349 Location getLoc()
const {
return fileLoc; }
358 LLVM_ATTRIBUTE_NOINLINE LogicalResult parseMultiByteVarInt(uint64_t &
result) {
364 uint32_t numBytes = llvm::countr_zero<uint32_t>(
result);
365 assert(numBytes > 0 && numBytes <= 7 &&
366 "unexpected number of trailing zeros in varint encoding");
369 llvm::support::ulittle64_t resultLE(
result);
371 parseBytes(numBytes,
reinterpret_cast<uint8_t *
>(&resultLE) + 1)))
376 result = resultLE >> (numBytes + 1);
381 ArrayRef<uint8_t> buffer;
384 const uint8_t *dataIt;
395template <
typename RangeT,
typename T>
396static LogicalResult
resolveEntry(EncodingReader &reader, RangeT &entries,
397 uint64_t
index, T &entry,
398 StringRef entryStr) {
399 if (
index >= entries.size())
400 return reader.emitError(
"invalid ", entryStr,
" index: ",
index);
403 if constexpr (std::is_convertible_v<llvm::detail::ValueOfRange<RangeT>, T>)
404 entry = entries[
index];
406 entry = &entries[
index];
411template <
typename RangeT,
typename T>
412static LogicalResult
parseEntry(EncodingReader &reader, RangeT &entries,
413 T &entry, StringRef entryStr) {
415 if (failed(reader.parseVarInt(entryIdx)))
417 return resolveEntry(reader, entries, entryIdx, entry, entryStr);
427class StringSectionReader {
430 LogicalResult
initialize(Location fileLoc, ArrayRef<uint8_t> sectionData);
434 LogicalResult parseString(EncodingReader &reader, StringRef &
result)
const {
441 LogicalResult parseStringWithFlag(EncodingReader &reader, StringRef &
result,
444 if (
failed(reader.parseVarIntWithFlag(entryIdx, flag)))
446 return parseStringAtIndex(reader, entryIdx,
result);
451 LogicalResult parseStringAtIndex(EncodingReader &reader, uint64_t index,
452 StringRef &
result)
const {
458 SmallVector<StringRef> strings;
462LogicalResult StringSectionReader::initialize(
Location fileLoc,
464 EncodingReader stringReader(sectionData, fileLoc);
468 if (
failed(stringReader.parseVarInt(numStrings)))
470 strings.resize(numStrings);
474 size_t stringDataEndOffset = sectionData.size();
475 for (StringRef &
string : llvm::reverse(strings)) {
477 if (
failed(stringReader.parseVarInt(stringSize)))
479 if (stringDataEndOffset < stringSize) {
480 return stringReader.emitError(
481 "string size exceeds the available data size");
485 size_t stringOffset = stringDataEndOffset - stringSize;
487 reinterpret_cast<const char *
>(sectionData.data() + stringOffset),
489 stringDataEndOffset = stringOffset;
494 if ((sectionData.size() - stringReader.size()) != stringDataEndOffset) {
495 return stringReader.emitError(
"unexpected trailing data between the "
496 "offsets for strings and their data");
509struct BytecodeDialect {
514 LogicalResult
load(
const DialectReader &reader, MLIRContext *ctx);
518 Dialect *getLoadedDialect()
const {
520 "expected `load` to be invoked before `getLoadedDialect`");
527 std::optional<Dialect *> dialect;
532 const BytecodeDialectInterface *
interface =
nullptr;
538 ArrayRef<uint8_t> versionBuffer;
541 std::unique_ptr<DialectVersion> loadedVersion;
545struct BytecodeOperationName {
546 BytecodeOperationName(BytecodeDialect *dialect, StringRef name,
547 std::optional<bool> wasRegistered)
548 : dialect(dialect), name(name), wasRegistered(wasRegistered) {}
552 std::optional<OperationName> opName;
555 BytecodeDialect *dialect;
562 std::optional<bool> wasRegistered;
568 EncodingReader &reader,
570 function_ref<LogicalResult(BytecodeDialect *)> entryCallback) {
572 std::unique_ptr<BytecodeDialect> *dialect;
573 if (failed(
parseEntry(reader, dialects, dialect,
"dialect")))
576 if (failed(reader.parseVarInt(numEntries)))
579 for (uint64_t i = 0; i < numEntries; ++i)
580 if (failed(entryCallback(dialect->get())))
591class ResourceSectionReader {
595 initialize(Location fileLoc,
const ParserConfig &config,
596 MutableArrayRef<std::unique_ptr<BytecodeDialect>> dialects,
597 StringSectionReader &stringReader, ArrayRef<uint8_t> sectionData,
598 ArrayRef<uint8_t> offsetSectionData, DialectReader &dialectReader,
599 const std::shared_ptr<llvm::SourceMgr> &bufferOwnerRef);
602 LogicalResult parseResourceHandle(EncodingReader &reader,
603 AsmDialectResourceHandle &
result)
const {
609 SmallVector<AsmDialectResourceHandle> dialectResources;
610 llvm::StringMap<std::string> dialectResourceHandleRenamingMap;
613class ParsedResourceEntry :
public AsmParsedResourceEntry {
616 EncodingReader &reader, StringSectionReader &stringReader,
617 const std::shared_ptr<llvm::SourceMgr> &bufferOwnerRef)
618 : key(key), kind(kind), reader(reader), stringReader(stringReader),
619 bufferOwnerRef(bufferOwnerRef) {}
620 ~ParsedResourceEntry()
override =
default;
622 StringRef getKey() const final {
return key; }
624 InFlightDiagnostic
emitError() const final {
return reader.emitError(); }
628 FailureOr<bool> parseAsBool() const final {
629 if (kind != AsmResourceEntryKind::Bool)
630 return emitError() <<
"expected a bool resource entry, but found a "
631 <<
toString(kind) <<
" entry instead";
634 if (
failed(reader.parseByte(value)))
638 FailureOr<std::string> parseAsString() const final {
639 if (kind != AsmResourceEntryKind::String)
640 return emitError() <<
"expected a string resource entry, but found a "
641 <<
toString(kind) <<
" entry instead";
644 if (
failed(stringReader.parseString(reader,
string)))
649 FailureOr<AsmResourceBlob>
650 parseAsBlob(BlobAllocatorFn allocator)
const final {
651 if (kind != AsmResourceEntryKind::Blob)
652 return emitError() <<
"expected a blob resource entry, but found a "
653 <<
toString(kind) <<
" entry instead";
655 ArrayRef<uint8_t> data;
657 if (
failed(reader.parseBlobAndAlignment(data, alignment)))
662 if (bufferOwnerRef) {
663 ArrayRef<char> charData(
reinterpret_cast<const char *
>(data.data()),
671 [bufferOwnerRef = bufferOwnerRef](
void *,
size_t,
size_t) {});
676 AsmResourceBlob blob = allocator(data.size(), alignment);
677 assert(llvm::isAddrAligned(llvm::Align(alignment), blob.
getData().data()) &&
679 "blob allocator did not return a properly aligned address");
687 EncodingReader &reader;
688 StringSectionReader &stringReader;
689 const std::shared_ptr<llvm::SourceMgr> &bufferOwnerRef;
696 EncodingReader &offsetReader, EncodingReader &resourceReader,
697 StringSectionReader &stringReader, T *handler,
698 const std::shared_ptr<llvm::SourceMgr> &bufferOwnerRef,
700 function_ref<LogicalResult(StringRef)> processKeyFn = {}) {
701 uint64_t numResources;
702 if (
failed(offsetReader.parseVarInt(numResources)))
705 for (uint64_t i = 0; i < numResources; ++i) {
708 uint64_t resourceOffset;
709 ArrayRef<uint8_t> data;
710 if (
failed(stringReader.parseString(offsetReader, key)) ||
711 failed(offsetReader.parseVarInt(resourceOffset)) ||
712 failed(offsetReader.parseByte(kind)) ||
713 failed(resourceReader.parseBytes(resourceOffset, data)))
717 if ((processKeyFn &&
failed(processKeyFn(key))))
722 if (allowEmpty && data.empty())
730 EncodingReader entryReader(data, fileLoc);
732 ParsedResourceEntry entry(key, kind, entryReader, stringReader,
734 if (
failed(handler->parseResource(entry)))
736 if (!entryReader.empty()) {
737 return entryReader.emitError(
738 "unexpected trailing bytes in resource entry '", key,
"'");
744LogicalResult ResourceSectionReader::initialize(
745 Location fileLoc,
const ParserConfig &config,
746 MutableArrayRef<std::unique_ptr<BytecodeDialect>> dialects,
747 StringSectionReader &stringReader, ArrayRef<uint8_t> sectionData,
748 ArrayRef<uint8_t> offsetSectionData, DialectReader &dialectReader,
749 const std::shared_ptr<llvm::SourceMgr> &bufferOwnerRef) {
750 EncodingReader resourceReader(sectionData, fileLoc);
751 EncodingReader offsetReader(offsetSectionData, fileLoc);
754 uint64_t numExternalResourceGroups;
755 if (
failed(offsetReader.parseVarInt(numExternalResourceGroups)))
760 auto parseGroup = [&](
auto *handler,
bool allowEmpty =
false,
762 auto resolveKey = [&](StringRef key) -> StringRef {
763 auto it = dialectResourceHandleRenamingMap.find(key);
764 if (it == dialectResourceHandleRenamingMap.end())
770 stringReader, handler, bufferOwnerRef, resolveKey,
775 for (uint64_t i = 0; i < numExternalResourceGroups; ++i) {
777 if (
failed(stringReader.parseString(offsetReader, key)))
784 emitWarning(fileLoc) <<
"ignoring unknown external resources for '" << key
788 if (
failed(parseGroup(handler)))
794 while (!offsetReader.empty()) {
795 std::unique_ptr<BytecodeDialect> *dialect;
797 failed((*dialect)->load(dialectReader, ctx)))
799 Dialect *loadedDialect = (*dialect)->getLoadedDialect();
800 if (!loadedDialect) {
801 return resourceReader.emitError()
802 <<
"dialect '" << (*dialect)->name <<
"' is unknown";
804 const auto *handler = dyn_cast<OpAsmDialectInterface>(loadedDialect);
806 return resourceReader.emitError()
807 <<
"unexpected resources for dialect '" << (*dialect)->name <<
"'";
811 auto processResourceKeyFn = [&](StringRef key) -> LogicalResult {
812 FailureOr<AsmDialectResourceHandle> handle =
813 handler->declareResource(key);
815 return resourceReader.emitError()
816 <<
"unknown 'resource' key '" << key <<
"' for dialect '"
817 << (*dialect)->name <<
"'";
819 dialectResourceHandleRenamingMap[key] = handler->getResourceKey(*handle);
820 dialectResources.push_back(*handle);
826 if (
failed(parseGroup(handler,
true, processResourceKeyFn)))
858class AttrTypeReader {
860 template <
typename T>
865 BytecodeDialect *dialect =
nullptr;
868 bool hasCustomEncoding =
false;
870 ArrayRef<uint8_t> data;
872 using AttrEntry = Entry<Attribute>;
873 using TypeEntry = Entry<Type>;
876 AttrTypeReader(
const StringSectionReader &stringReader,
877 const ResourceSectionReader &resourceReader,
878 const llvm::StringMap<BytecodeDialect *> &dialectsMap,
879 uint64_t &bytecodeVersion, Location fileLoc,
880 const ParserConfig &config)
881 : stringReader(stringReader), resourceReader(resourceReader),
882 dialectsMap(dialectsMap), fileLoc(fileLoc),
883 bytecodeVersion(bytecodeVersion), parserConfig(config) {}
887 initialize(MutableArrayRef<std::unique_ptr<BytecodeDialect>> dialects,
888 ArrayRef<uint8_t> sectionData,
889 ArrayRef<uint8_t> offsetSectionData);
891 LogicalResult readAttribute(uint64_t index, Attribute &
result,
892 uint64_t depth = 0) {
893 return readEntry(attributes, index,
result,
"attribute", depth);
896 LogicalResult readType(uint64_t index, Type &
result, uint64_t depth = 0) {
897 return readEntry(types, index,
result,
"type", depth);
902 Attribute resolveAttribute(
size_t index, uint64_t depth = 0) {
903 return resolveEntry(attributes, index,
"Attribute", depth);
905 Type resolveType(
size_t index, uint64_t depth = 0) {
909 Attribute getAttributeOrSentinel(
size_t index) {
910 if (index >= attributes.size())
912 return attributes[index].entry;
914 Type getTypeOrSentinel(
size_t index) {
915 if (index >= types.size())
917 return types[index].entry;
923 if (
failed(reader.parseVarInt(attrIdx)))
925 result = resolveAttribute(attrIdx);
928 LogicalResult parseOptionalAttribute(EncodingReader &reader,
932 if (
failed(reader.parseVarIntWithFlag(attrIdx, flag)))
936 result = resolveAttribute(attrIdx);
942 if (
failed(reader.parseVarInt(typeIdx)))
944 result = resolveType(typeIdx);
948 template <
typename T>
950 Attribute baseResult;
953 if ((
result = dyn_cast<T>(baseResult)))
955 return reader.emitError(
"expected attribute of type: ",
956 llvm::getTypeName<T>(),
", but got: ", baseResult);
960 enum class EntryKind { Attribute, Type };
963 void addDeferredParsing(uint64_t index, EntryKind kind) {
964 deferredWorklist.emplace_back(index, kind);
968 bool isResolving()
const {
return resolving; }
972 template <
typename T>
973 T
resolveEntry(SmallVectorImpl<Entry<T>> &entries, uint64_t index,
974 StringRef entryType, uint64_t depth = 0);
978 template <
typename T>
979 LogicalResult readEntry(SmallVectorImpl<Entry<T>> &entries, uint64_t index,
980 T &
result, StringRef entryType, uint64_t depth);
984 template <
typename T>
985 LogicalResult parseCustomEntry(Entry<T> &entry, EncodingReader &reader,
986 StringRef entryType, uint64_t index,
991 template <
typename T>
992 LogicalResult parseAsmEntry(T &
result, EncodingReader &reader,
993 StringRef entryType);
997 const StringSectionReader &stringReader;
1001 const ResourceSectionReader &resourceReader;
1005 const llvm::StringMap<BytecodeDialect *> &dialectsMap;
1008 SmallVector<AttrEntry> attributes;
1009 SmallVector<TypeEntry> types;
1015 uint64_t &bytecodeVersion;
1018 const ParserConfig &parserConfig;
1024 std::vector<std::pair<uint64_t, EntryKind>> deferredWorklist;
1027 bool resolving =
false;
1030class DialectReader :
public DialectBytecodeReader {
1032 DialectReader(AttrTypeReader &attrTypeReader,
1033 const StringSectionReader &stringReader,
1034 const ResourceSectionReader &resourceReader,
1035 const llvm::StringMap<BytecodeDialect *> &dialectsMap,
1036 EncodingReader &reader, uint64_t &bytecodeVersion,
1038 : attrTypeReader(attrTypeReader), stringReader(stringReader),
1039 resourceReader(resourceReader), dialectsMap(dialectsMap),
1040 reader(reader), bytecodeVersion(bytecodeVersion), depth(depth) {}
1042 InFlightDiagnostic
emitError(
const Twine &msg)
const override {
1043 return reader.emitError(msg);
1046 InFlightDiagnostic
emitWarning(
const Twine &msg)
const override {
1047 return reader.emitWarning(msg);
1050 FailureOr<const DialectVersion *>
1051 getDialectVersion(StringRef dialectName)
const override {
1053 auto dialectEntry = dialectsMap.find(dialectName);
1054 if (dialectEntry == dialectsMap.end())
1059 if (
failed(dialectEntry->getValue()->load(*
this, getLoc().
getContext())) ||
1060 dialectEntry->getValue()->loadedVersion ==
nullptr)
1062 return dialectEntry->getValue()->loadedVersion.get();
1065 MLIRContext *
getContext()
const override {
return getLoc().getContext(); }
1067 uint64_t getBytecodeVersion()
const override {
return bytecodeVersion; }
1069 DialectReader withEncodingReader(EncodingReader &encReader)
const {
1070 return DialectReader(attrTypeReader, stringReader, resourceReader,
1071 dialectsMap, encReader, bytecodeVersion);
1074 Location getLoc()
const {
return reader.getLoc(); }
1082 static constexpr uint64_t maxAttrTypeDepth = 5;
1084 LogicalResult readAttribute(Attribute &
result)
override {
1086 if (
failed(reader.parseVarInt(index)))
1092 if (!attrTypeReader.isResolving()) {
1093 if (Attribute attr = attrTypeReader.resolveAttribute(index)) {
1100 if (depth > maxAttrTypeDepth) {
1101 if (Attribute attr = attrTypeReader.getAttributeOrSentinel(index)) {
1105 attrTypeReader.addDeferredParsing(index,
1106 AttrTypeReader::EntryKind::Attribute);
1109 return attrTypeReader.readAttribute(index,
result, depth + 1);
1111 LogicalResult readOptionalAttribute(Attribute &
result)
override {
1112 return attrTypeReader.parseOptionalAttribute(reader,
result);
1114 LogicalResult readType(Type &
result)
override {
1116 if (
failed(reader.parseVarInt(index)))
1122 if (!attrTypeReader.isResolving()) {
1123 if (Type type = attrTypeReader.resolveType(index)) {
1130 if (depth > maxAttrTypeDepth) {
1131 if (Type type = attrTypeReader.getTypeOrSentinel(index)) {
1135 attrTypeReader.addDeferredParsing(index, AttrTypeReader::EntryKind::Type);
1138 return attrTypeReader.readType(index,
result, depth + 1);
1142 AsmDialectResourceHandle handle;
1143 if (
failed(resourceReader.parseResourceHandle(reader, handle)))
1152 LogicalResult readVarInt(uint64_t &
result)
override {
1153 return reader.parseVarInt(
result);
1156 LogicalResult readSignedVarInt(int64_t &
result)
override {
1157 uint64_t unsignedResult;
1158 if (
failed(reader.parseSignedVarInt(unsignedResult)))
1160 result =
static_cast<int64_t
>(unsignedResult);
1164 FailureOr<APInt> readAPIntWithKnownWidth(
unsigned bitWidth)
override {
1166 if (bitWidth <= 8) {
1168 if (
failed(reader.parseByte(value)))
1170 return APInt(bitWidth, value);
1174 if (bitWidth <= 64) {
1176 if (
failed(reader.parseSignedVarInt(value)))
1178 return APInt(bitWidth, value);
1183 uint64_t numActiveWords;
1184 if (
failed(reader.parseVarInt(numActiveWords)))
1186 SmallVector<uint64_t, 4> words(numActiveWords);
1187 for (uint64_t i = 0; i < numActiveWords; ++i)
1188 if (
failed(reader.parseSignedVarInt(words[i])))
1190 return APInt(bitWidth, words);
1194 readAPFloatWithKnownSemantics(
const llvm::fltSemantics &semantics)
override {
1195 FailureOr<APInt> intVal =
1196 readAPIntWithKnownWidth(APFloat::getSizeInBits(semantics));
1199 return APFloat(semantics, *intVal);
1202 LogicalResult readString(StringRef &
result)
override {
1203 return stringReader.parseString(reader,
result);
1206 LogicalResult readBlob(ArrayRef<char> &
result)
override {
1208 ArrayRef<uint8_t> data;
1209 if (
failed(reader.parseVarInt(dataSize)) ||
1210 failed(reader.parseBytes(dataSize, data)))
1212 result = llvm::ArrayRef(
reinterpret_cast<const char *
>(data.data()),
1217 LogicalResult readBool(
bool &
result)
override {
1218 return reader.parseByte(
result);
1222 AttrTypeReader &attrTypeReader;
1223 const StringSectionReader &stringReader;
1224 const ResourceSectionReader &resourceReader;
1225 const llvm::StringMap<BytecodeDialect *> &dialectsMap;
1226 EncodingReader &reader;
1227 uint64_t &bytecodeVersion;
1232class PropertiesSectionReader {
1235 LogicalResult
initialize(Location fileLoc, ArrayRef<uint8_t> sectionData) {
1236 if (sectionData.empty())
1238 EncodingReader propReader(sectionData, fileLoc);
1240 if (
failed(propReader.parseVarInt(count)))
1243 if (
failed(propReader.parseBytes(propReader.size(), propertiesBuffers)))
1246 EncodingReader offsetsReader(propertiesBuffers, fileLoc);
1247 offsetTable.reserve(count);
1248 for (
auto idx : llvm::seq<int64_t>(0, count)) {
1250 offsetTable.push_back(propertiesBuffers.size() - offsetsReader.size());
1251 ArrayRef<uint8_t> rawProperties;
1253 if (
failed(offsetsReader.parseVarInt(dataSize)) ||
1254 failed(offsetsReader.parseBytes(dataSize, rawProperties)))
1257 if (!offsetsReader.empty())
1258 return offsetsReader.emitError()
1259 <<
"Broken properties section: didn't exhaust the offsets table";
1263 LogicalResult read(Location fileLoc, DialectReader &dialectReader,
1264 OperationName *opName, OperationState &opState)
const {
1265 uint64_t propertiesIdx;
1266 if (
failed(dialectReader.readVarInt(propertiesIdx)))
1268 if (propertiesIdx >= offsetTable.size())
1269 return dialectReader.emitError(
"Properties idx out-of-bound for ")
1271 size_t propertiesOffset = offsetTable[propertiesIdx];
1272 if (propertiesIdx >= propertiesBuffers.size())
1273 return dialectReader.emitError(
"Properties offset out-of-bound for ")
1277 ArrayRef<char> rawProperties;
1281 EncodingReader reader(propertiesBuffers.drop_front(propertiesOffset),
1285 dialectReader.withEncodingReader(reader).readBlob(rawProperties)))
1289 EncodingReader reader(
1290 StringRef(rawProperties.begin(), rawProperties.size()), fileLoc);
1291 DialectReader propReader = dialectReader.withEncodingReader(reader);
1293 auto *iface = opName->
getInterface<BytecodeOpInterface>();
1295 return iface->readProperties(propReader, opState);
1297 return propReader.emitError(
1298 "has properties but missing BytecodeOpInterface for ")
1306 ArrayRef<uint8_t> propertiesBuffers;
1309 SmallVector<int64_t> offsetTable;
1313LogicalResult AttrTypeReader::initialize(
1314 MutableArrayRef<std::unique_ptr<BytecodeDialect>> dialects,
1315 ArrayRef<uint8_t> sectionData, ArrayRef<uint8_t> offsetSectionData) {
1316 EncodingReader offsetReader(offsetSectionData, fileLoc);
1319 uint64_t numAttributes, numTypes;
1320 if (
failed(offsetReader.parseVarInt(numAttributes)) ||
1321 failed(offsetReader.parseVarInt(numTypes)))
1323 attributes.resize(numAttributes);
1324 types.resize(numTypes);
1328 uint64_t currentOffset = 0;
1329 auto parseEntries = [&](
auto &&range) {
1330 size_t currentIndex = 0, endIndex = range.size();
1333 auto parseEntryFn = [&](BytecodeDialect *dialect) -> LogicalResult {
1336 if (currentIndex == endIndex) {
1337 return offsetReader.emitError(
1338 "Attribute or Type dialect group entries exceed the declared "
1339 "number of entries (",
1342 auto &entry = range[currentIndex++];
1345 if (
failed(offsetReader.parseVarIntWithFlag(entrySize,
1346 entry.hasCustomEncoding)))
1350 if (currentOffset + entrySize > sectionData.size()) {
1351 return offsetReader.emitError(
1352 "Attribute or Type entry offset points past the end of section");
1355 entry.data = sectionData.slice(currentOffset, entrySize);
1356 entry.dialect = dialect;
1357 currentOffset += entrySize;
1360 while (currentIndex != endIndex)
1367 if (
failed(parseEntries(attributes)) ||
failed(parseEntries(types)))
1371 if (!offsetReader.empty()) {
1372 return offsetReader.emitError(
1373 "unexpected trailing data in the Attribute/Type offset section");
1379template <
typename T>
1380T AttrTypeReader::resolveEntry(SmallVectorImpl<Entry<T>> &entries,
1381 uint64_t index, StringRef entryType,
1383 bool oldResolving = resolving;
1385 llvm::scope_exit restoreResolving([&]() { resolving = oldResolving; });
1387 if (index >= entries.size()) {
1388 emitError(fileLoc) <<
"invalid " << entryType <<
" index: " << index;
1394 assert(deferredWorklist.empty());
1396 if (succeeded(readEntry(entries, index,
result, entryType, depth))) {
1397 assert(deferredWorklist.empty());
1400 if (deferredWorklist.empty()) {
1410 std::deque<std::pair<uint64_t, EntryKind>> worklist;
1411 llvm::DenseSet<std::pair<uint64_t, EntryKind>> inWorklist;
1413 EntryKind entryKind =
1414 std::is_same_v<T, Type> ? EntryKind::Type : EntryKind::Attribute;
1416 static_assert((std::is_same_v<T, Type> || std::is_same_v<T, Attribute>) &&
1417 "Only support resolving Attributes and Types");
1419 auto addToWorklistFront = [&](std::pair<uint64_t, EntryKind> entry) {
1420 if (inWorklist.insert(entry).second)
1421 worklist.push_front(entry);
1425 worklist.emplace_back(index, entryKind);
1426 inWorklist.insert({index, entryKind});
1427 for (
auto entry : llvm::reverse(deferredWorklist))
1428 addToWorklistFront(entry);
1430 while (!worklist.empty()) {
1431 auto [currentIndex, entryKind] = worklist.front();
1432 worklist.pop_front();
1435 deferredWorklist.clear();
1437 if (entryKind == EntryKind::Type) {
1439 if (succeeded(readType(currentIndex,
result, depth))) {
1440 inWorklist.erase({currentIndex, entryKind});
1444 assert(entryKind == EntryKind::Attribute &&
"Unexpected entry kind");
1446 if (succeeded(readAttribute(currentIndex,
result, depth))) {
1447 inWorklist.erase({currentIndex, entryKind});
1452 if (deferredWorklist.empty()) {
1458 worklist.emplace_back(currentIndex, entryKind);
1461 for (
auto entry : llvm::reverse(deferredWorklist))
1462 addToWorklistFront(entry);
1464 deferredWorklist.clear();
1466 return entries[index].entry;
1469template <
typename T>
1470LogicalResult AttrTypeReader::readEntry(SmallVectorImpl<Entry<T>> &entries,
1471 uint64_t index, T &
result,
1472 StringRef entryType, uint64_t depth) {
1473 if (index >= entries.size())
1474 return emitError(fileLoc) <<
"invalid " << entryType <<
" index: " << index;
1477 Entry<T> &entry = entries[index];
1484 EncodingReader reader(entry.data, fileLoc);
1485 LogicalResult parseResult =
1486 entry.hasCustomEncoding
1487 ? parseCustomEntry(entry, reader, entryType, index, depth)
1488 : parseAsmEntry(entry.entry, reader, entryType);
1492 if (!reader.empty())
1493 return reader.emitError(
"unexpected trailing bytes after " + entryType +
1500template <
typename T>
1501LogicalResult AttrTypeReader::parseCustomEntry(Entry<T> &entry,
1502 EncodingReader &reader,
1503 StringRef entryType,
1504 uint64_t index, uint64_t depth) {
1505 DialectReader dialectReader(*
this, stringReader, resourceReader, dialectsMap,
1506 reader, bytecodeVersion, depth);
1510 if constexpr (std::is_same_v<T, Type>) {
1512 for (
const auto &callback :
1514 size_t savedWorklistSize = deferredWorklist.size();
1516 callback->read(dialectReader, entry.dialect->name, entry.entry)))
1525 deferredWorklist.resize(savedWorklistSize);
1526 reader = EncodingReader(entry.data, reader.getLoc());
1530 for (
const auto &callback :
1532 size_t savedWorklistSize = deferredWorklist.size();
1534 callback->read(dialectReader, entry.dialect->name, entry.entry)))
1543 deferredWorklist.resize(savedWorklistSize);
1544 reader = EncodingReader(entry.data, reader.getLoc());
1549 if (!entry.dialect->interface) {
1550 return reader.emitError(
"dialect '", entry.dialect->name,
1551 "' does not implement the bytecode interface");
1554 if constexpr (std::is_same_v<T, Type>)
1555 entry.entry = entry.dialect->interface->readType(dialectReader);
1557 entry.entry = entry.dialect->interface->readAttribute(dialectReader);
1559 return success(!!entry.entry);
1562template <
typename T>
1563LogicalResult AttrTypeReader::parseAsmEntry(T &
result, EncodingReader &reader,
1564 StringRef entryType) {
1566 if (
failed(reader.parseNullTerminatedString(asmStr)))
1571 MLIRContext *context = fileLoc->
getContext();
1572 if constexpr (std::is_same_v<T, Type>)
1582 if (numRead != asmStr.size()) {
1583 return reader.emitError(
"trailing characters found after ", entryType,
1584 " assembly format: ", asmStr.drop_front(numRead));
1595 struct RegionReadState;
1596 using LazyLoadableOpsInfo =
1597 std::list<std::pair<Operation *, RegionReadState>>;
1598 using LazyLoadableOpsMap =
1603 llvm::MemoryBufferRef buffer,
1604 const std::shared_ptr<llvm::SourceMgr> &bufferOwnerRef)
1605 : config(config), fileLoc(fileLoc), lazyLoading(lazyLoading),
1606 attrTypeReader(stringReader, resourceReader, dialectsMap, version,
1610 forwardRefOpState(UnknownLoc::
get(config.getContext()),
1611 "builtin.unrealized_conversion_cast",
ValueRange(),
1612 NoneType::
get(config.getContext())),
1613 buffer(buffer), bufferOwnerRef(bufferOwnerRef) {}
1616 LogicalResult read(
Block *block,
1629 this->lazyOpsCallback = lazyOpsCallback;
1630 llvm::scope_exit resetlazyOpsCallback(
1631 [&] { this->lazyOpsCallback =
nullptr; });
1632 auto it = lazyLoadableOpsMap.find(op);
1633 assert(it != lazyLoadableOpsMap.end() &&
1634 "materialize called on non-materializable op");
1640 while (!lazyLoadableOpsMap.empty()) {
1641 if (failed(
materialize(lazyLoadableOpsMap.begin())))
1652 while (!lazyLoadableOps.empty()) {
1653 Operation *op = lazyLoadableOps.begin()->first;
1654 if (shouldMaterialize(op)) {
1655 if (failed(
materialize(lazyLoadableOpsMap.find(op))))
1661 lazyLoadableOps.pop_front();
1662 lazyLoadableOpsMap.erase(op);
1668 LogicalResult
materialize(LazyLoadableOpsMap::iterator it) {
1669 assert(it != lazyLoadableOpsMap.end() &&
1670 "materialize called on non-materializable op");
1671 valueScopes.emplace_back();
1672 std::vector<RegionReadState> regionStack;
1673 regionStack.push_back(std::move(it->getSecond()->second));
1674 lazyLoadableOps.erase(it->getSecond());
1675 lazyLoadableOpsMap.erase(it);
1677 while (!regionStack.empty())
1678 if (failed(parseRegions(regionStack, regionStack.back())))
1683 LogicalResult checkSectionAlignment(
1694 const bool isGloballyAligned =
1695 ((uintptr_t)buffer.getBufferStart() & (alignment - 1)) == 0;
1697 if (!isGloballyAligned)
1698 return emitError(
"expected section alignment ")
1699 << alignment <<
" but bytecode buffer 0x"
1700 << Twine::utohexstr((uint64_t)buffer.getBufferStart())
1701 <<
" is not aligned";
1710 LogicalResult parseVersion(EncodingReader &reader);
1715 LogicalResult parseDialectSection(ArrayRef<uint8_t> sectionData);
1720 FailureOr<OperationName> parseOpName(EncodingReader &reader,
1721 std::optional<bool> &wasRegistered);
1727 template <
typename T>
1729 return attrTypeReader.parseAttribute(reader,
result);
1732 return attrTypeReader.parseType(reader,
result);
1739 parseResourceSection(EncodingReader &reader,
1740 std::optional<ArrayRef<uint8_t>> resourceData,
1741 std::optional<ArrayRef<uint8_t>> resourceOffsetData);
1748 struct RegionReadState {
1749 RegionReadState(Operation *op, EncodingReader *reader,
1750 bool isIsolatedFromAbove)
1751 : RegionReadState(op->getRegions(), reader, isIsolatedFromAbove) {}
1752 RegionReadState(MutableArrayRef<Region> regions, EncodingReader *reader,
1753 bool isIsolatedFromAbove)
1754 : curRegion(regions.begin()), endRegion(regions.end()), reader(reader),
1755 isIsolatedFromAbove(isIsolatedFromAbove) {}
1758 MutableArrayRef<Region>::iterator curRegion, endRegion;
1763 EncodingReader *reader;
1764 std::unique_ptr<EncodingReader> owningReader;
1767 unsigned numValues = 0;
1770 SmallVector<Block *> curBlocks;
1775 uint64_t numOpsRemaining = 0;
1778 bool isIsolatedFromAbove =
false;
1781 LogicalResult parseIRSection(ArrayRef<uint8_t> sectionData,
Block *block);
1782 LogicalResult parseRegions(std::vector<RegionReadState> ®ionStack,
1783 RegionReadState &readState);
1784 FailureOr<Operation *> parseOpWithoutRegions(EncodingReader &reader,
1785 RegionReadState &readState,
1786 bool &isIsolatedFromAbove);
1788 LogicalResult parseRegion(RegionReadState &readState);
1789 LogicalResult parseBlockHeader(EncodingReader &reader,
1790 RegionReadState &readState);
1791 LogicalResult parseBlockArguments(EncodingReader &reader,
Block *block);
1798 Value parseOperand(EncodingReader &reader);
1801 LogicalResult defineValues(EncodingReader &reader,
ValueRange values);
1804 Value createForwardRef();
1812 struct UseListOrderStorage {
1813 UseListOrderStorage(
bool isIndexPairEncoding,
1814 SmallVector<unsigned, 4> &&
indices)
1816 isIndexPairEncoding(isIndexPairEncoding) {};
1819 SmallVector<unsigned, 4>
indices;
1823 bool isIndexPairEncoding;
1831 using UseListMapT = DenseMap<unsigned, UseListOrderStorage>;
1832 FailureOr<UseListMapT> parseUseListOrderForRange(EncodingReader &reader,
1833 uint64_t rangeSize);
1836 LogicalResult sortUseListOrder(Value value);
1840 LogicalResult processUseLists(Operation *topLevelOp);
1850 void push(RegionReadState &readState) {
1851 nextValueIDs.push_back(values.size());
1852 values.resize(values.size() + readState.numValues);
1857 void pop(RegionReadState &readState) {
1858 values.resize(values.size() - readState.numValues);
1859 nextValueIDs.pop_back();
1863 std::vector<Value> values;
1867 SmallVector<unsigned, 4> nextValueIDs;
1871 const ParserConfig &config;
1882 LazyLoadableOpsInfo lazyLoadableOps;
1883 LazyLoadableOpsMap lazyLoadableOpsMap;
1884 llvm::function_ref<bool(Operation *)> lazyOpsCallback;
1887 AttrTypeReader attrTypeReader;
1890 uint64_t version = 0;
1896 SmallVector<std::unique_ptr<BytecodeDialect>> dialects;
1897 llvm::StringMap<BytecodeDialect *> dialectsMap;
1898 SmallVector<BytecodeOperationName> opNames;
1901 ResourceSectionReader resourceReader;
1905 DenseMap<void *, UseListOrderStorage> valueToUseListMap;
1908 StringSectionReader stringReader;
1911 PropertiesSectionReader propertiesReader;
1914 std::vector<ValueScope> valueScopes;
1921 Block forwardRefOps;
1925 Block openForwardRefOps;
1928 OperationState forwardRefOpState;
1931 llvm::MemoryBufferRef buffer;
1935 const std::shared_ptr<llvm::SourceMgr> bufferOwnerRef;
1940 EncodingReader reader(buffer.getBuffer(), fileLoc);
1941 this->lazyOpsCallback = lazyOpsCallback;
1942 llvm::scope_exit resetlazyOpsCallback(
1943 [&] { this->lazyOpsCallback =
nullptr; });
1946 if (failed(reader.skipBytes(StringRef(
"ML\xefR").size())))
1949 if (failed(parseVersion(reader)) ||
1950 failed(reader.parseNullTerminatedString(producer)))
1956 diag.attachNote() <<
"in bytecode version " << version
1957 <<
" produced by: " << producer;
1961 const auto checkSectionAlignment = [&](
unsigned alignment) {
1962 return this->checkSectionAlignment(
1963 alignment, [&](
const auto &msg) {
return reader.emitError(msg); });
1967 std::optional<ArrayRef<uint8_t>>
1969 while (!reader.empty()) {
1974 reader.parseSection(sectionID, checkSectionAlignment, sectionData)))
1978 if (sectionDatas[sectionID]) {
1979 return reader.emitError(
"duplicate top-level section: ",
1982 sectionDatas[sectionID] = sectionData;
1988 return reader.emitError(
"missing data for top-level section: ",
1994 if (failed(stringReader.initialize(
2000 failed(propertiesReader.initialize(
2009 if (failed(parseResourceSection(
2015 if (failed(attrTypeReader.initialize(
2024LogicalResult BytecodeReader::Impl::parseVersion(EncodingReader &reader) {
2025 if (failed(reader.parseVarInt(version)))
2031 if (version < minSupportedVersion) {
2032 return reader.emitError(
"bytecode version ", version,
2033 " is older than the current version of ",
2034 currentVersion,
", and upgrade is not supported");
2036 if (version > currentVersion) {
2037 return reader.emitError(
"bytecode version ", version,
2038 " is newer than the current version ",
2043 lazyLoading =
false;
2051LogicalResult BytecodeDialect::load(
const DialectReader &reader,
2057 return reader.emitError(
"dialect '")
2059 <<
"' is unknown. If this is intended, please call "
2060 "allowUnregisteredDialects() on the MLIRContext, or use "
2061 "-allow-unregistered-dialect with the MLIR tool used.";
2063 dialect = loadedDialect;
2068 interface = dyn_cast<BytecodeDialectInterface>(loadedDialect);
2069 if (!versionBuffer.empty()) {
2071 return reader.emitError(
"dialect '")
2073 <<
"' does not implement the bytecode interface, "
2074 "but found a version entry";
2075 EncodingReader encReader(versionBuffer, reader.getLoc());
2076 DialectReader versionReader = reader.withEncodingReader(encReader);
2077 loadedVersion = interface->readVersion(versionReader);
2085BytecodeReader::Impl::parseDialectSection(ArrayRef<uint8_t> sectionData) {
2086 EncodingReader sectionReader(sectionData, fileLoc);
2089 uint64_t numDialects;
2090 if (
failed(sectionReader.parseVarInt(numDialects)))
2092 dialects.resize(numDialects);
2094 const auto checkSectionAlignment = [&](
unsigned alignment) {
2095 return this->checkSectionAlignment(alignment, [&](
const auto &msg) {
2096 return sectionReader.emitError(msg);
2101 for (uint64_t i = 0; i < numDialects; ++i) {
2102 dialects[i] = std::make_unique<BytecodeDialect>();
2106 if (
failed(stringReader.parseString(sectionReader, dialects[i]->name)))
2112 uint64_t dialectNameIdx;
2113 bool versionAvailable;
2114 if (
failed(sectionReader.parseVarIntWithFlag(dialectNameIdx,
2117 if (
failed(stringReader.parseStringAtIndex(sectionReader, dialectNameIdx,
2118 dialects[i]->name)))
2120 if (versionAvailable) {
2122 if (
failed(sectionReader.parseSection(sectionID, checkSectionAlignment,
2123 dialects[i]->versionBuffer)))
2126 emitError(fileLoc,
"expected dialect version section");
2130 dialectsMap[dialects[i]->name] = dialects[i].get();
2134 auto parseOpName = [&](BytecodeDialect *dialect) {
2136 std::optional<bool> wasRegistered;
2140 if (
failed(stringReader.parseString(sectionReader, opName)))
2143 bool wasRegisteredFlag;
2144 if (
failed(stringReader.parseStringWithFlag(sectionReader, opName,
2145 wasRegisteredFlag)))
2147 wasRegistered = wasRegisteredFlag;
2149 opNames.emplace_back(dialect, opName, wasRegistered);
2156 if (
failed(sectionReader.parseVarInt(numOps)))
2158 opNames.reserve(numOps);
2160 while (!sectionReader.empty())
2166FailureOr<OperationName>
2167BytecodeReader::Impl::parseOpName(EncodingReader &reader,
2168 std::optional<bool> &wasRegistered) {
2169 BytecodeOperationName *opName =
nullptr;
2172 wasRegistered = opName->wasRegistered;
2175 if (!opName->opName) {
2180 if (opName->name.empty()) {
2181 opName->opName.emplace(opName->dialect->name,
getContext());
2184 DialectReader dialectReader(attrTypeReader, stringReader, resourceReader,
2185 dialectsMap, reader, version);
2188 opName->opName.emplace((opName->dialect->name +
"." + opName->name).str(),
2192 return *opName->opName;
2199LogicalResult BytecodeReader::Impl::parseResourceSection(
2200 EncodingReader &reader, std::optional<ArrayRef<uint8_t>> resourceData,
2201 std::optional<ArrayRef<uint8_t>> resourceOffsetData) {
2203 if (resourceData.has_value() != resourceOffsetData.has_value()) {
2204 if (resourceOffsetData)
2205 return emitError(fileLoc,
"unexpected resource offset section when "
2206 "resource section is not present");
2209 "expected resource offset section when resource section is present");
2217 DialectReader dialectReader(attrTypeReader, stringReader, resourceReader,
2218 dialectsMap, reader, version);
2219 return resourceReader.initialize(fileLoc, config, dialects, stringReader,
2220 *resourceData, *resourceOffsetData,
2221 dialectReader, bufferOwnerRef);
2228FailureOr<BytecodeReader::Impl::UseListMapT>
2229BytecodeReader::Impl::parseUseListOrderForRange(EncodingReader &reader,
2230 uint64_t numResults) {
2231 BytecodeReader::Impl::UseListMapT map;
2232 uint64_t numValuesToRead = 1;
2233 if (numResults > 1 &&
failed(reader.parseVarInt(numValuesToRead)))
2236 for (
size_t valueIdx = 0; valueIdx < numValuesToRead; valueIdx++) {
2237 uint64_t resultIdx = 0;
2238 if (numResults > 1 &&
failed(reader.parseVarInt(resultIdx)))
2242 bool indexPairEncoding;
2243 if (
failed(reader.parseVarIntWithFlag(numValues, indexPairEncoding)))
2246 SmallVector<unsigned, 4> useListOrders;
2247 for (
size_t idx = 0; idx < numValues; idx++) {
2249 if (
failed(reader.parseVarInt(index)))
2251 useListOrders.push_back(index);
2255 map.try_emplace(resultIdx, UseListOrderStorage(indexPairEncoding,
2256 std::move(useListOrders)));
2267LogicalResult BytecodeReader::Impl::sortUseListOrder(Value value) {
2272 bool hasIncomingOrder =
2277 bool alreadySorted =
true;
2281 llvm::SmallVector<std::pair<unsigned, uint64_t>> currentOrder = {{0, prevID}};
2282 for (
auto item : llvm::drop_begin(llvm::enumerate(value.
getUses()))) {
2284 item.value(), operationIDs.at(item.value().getOwner()));
2285 alreadySorted &= prevID > currentID;
2286 currentOrder.push_back({item.index(), currentID});
2292 if (alreadySorted && !hasIncomingOrder)
2299 currentOrder.begin(), currentOrder.end(),
2300 [](
auto elem1,
auto elem2) { return elem1.second > elem2.second; });
2302 if (!hasIncomingOrder) {
2306 SmallVector<unsigned> shuffle(llvm::make_first_range(currentOrder));
2312 UseListOrderStorage customOrder =
2314 SmallVector<unsigned, 4> shuffle = std::move(customOrder.indices);
2321 if (customOrder.isIndexPairEncoding) {
2323 if (shuffle.size() & 1)
2326 SmallVector<unsigned, 4> newShuffle(numUses);
2328 std::iota(newShuffle.begin(), newShuffle.end(), idx);
2329 for (idx = 0; idx < shuffle.size(); idx += 2)
2330 newShuffle[shuffle[idx]] = shuffle[idx + 1];
2332 shuffle = std::move(newShuffle);
2339 uint64_t accumulator = 0;
2340 for (
const auto &elem : shuffle) {
2341 if (!set.insert(elem).second)
2343 accumulator += elem;
2345 if (numUses != shuffle.size() ||
2346 accumulator != (((numUses - 1) * numUses) >> 1))
2365 SmallVector<unsigned, 4> finalShuffle(numUses);
2366 if (customOrder.isIndexPairEncoding) {
2367 for (
size_t writerMemIdx = 0; writerMemIdx < numUses; ++writerMemIdx)
2368 finalShuffle[currentOrder[shuffle[writerMemIdx]].first] = writerMemIdx;
2370 for (
size_t sortedPos = 0; sortedPos < numUses; ++sortedPos)
2371 finalShuffle[currentOrder[sortedPos].first] = shuffle[sortedPos];
2373 shuffle = std::move(finalShuffle);
2379LogicalResult BytecodeReader::Impl::processUseLists(Operation *topLevelOp) {
2383 unsigned operationID = 0;
2385 [&](Operation *op) { operationIDs.try_emplace(op, operationID++); });
2387 auto blockWalk = topLevelOp->
walk([
this](
Block *block) {
2389 if (
failed(sortUseListOrder(arg)))
2394 auto resultWalk = topLevelOp->
walk([
this](Operation *op) {
2401 return failure(blockWalk.wasInterrupted() || resultWalk.wasInterrupted());
2409BytecodeReader::Impl::parseIRSection(ArrayRef<uint8_t> sectionData,
2411 EncodingReader reader(sectionData, fileLoc);
2414 std::vector<RegionReadState> regionStack;
2417 OwningOpRef<ModuleOp> moduleOp = ModuleOp::create(fileLoc);
2418 regionStack.emplace_back(*moduleOp, &reader,
true);
2419 regionStack.back().curBlocks.push_back(moduleOp->getBody());
2420 regionStack.back().curBlock = regionStack.back().curRegion->begin();
2421 if (
failed(parseBlockHeader(reader, regionStack.back())))
2423 valueScopes.emplace_back();
2424 valueScopes.back().push(regionStack.back());
2427 while (!regionStack.empty())
2428 if (
failed(parseRegions(regionStack, regionStack.back())))
2430 if (!forwardRefOps.empty()) {
2431 return reader.emitError(
2432 "not all forward unresolved forward operand references");
2436 if (
failed(processUseLists(*moduleOp)))
2437 return reader.emitError(
2438 "parsed use-list orders were invalid and could not be applied");
2441 for (
const std::unique_ptr<BytecodeDialect> &byteCodeDialect : dialects) {
2444 if (!byteCodeDialect->loadedVersion)
2446 if (byteCodeDialect->interface &&
2447 failed(byteCodeDialect->interface->upgradeFromVersion(
2448 *moduleOp, *byteCodeDialect->loadedVersion)))
2457 auto &parsedOps = moduleOp->getBody()->getOperations();
2459 destOps.splice(destOps.end(), parsedOps, parsedOps.begin(), parsedOps.end());
2464BytecodeReader::Impl::parseRegions(std::vector<RegionReadState> ®ionStack,
2465 RegionReadState &readState) {
2466 const auto checkSectionAlignment = [&](
unsigned alignment) {
2467 return this->checkSectionAlignment(
2468 alignment, [&](
const auto &msg) {
return emitError(fileLoc, msg); });
2474 for (; readState.curRegion != readState.endRegion; ++readState.curRegion) {
2480 if (
failed(parseRegion(readState)))
2484 if (readState.curRegion->empty())
2489 EncodingReader &reader = *readState.reader;
2491 while (readState.numOpsRemaining--) {
2494 bool isIsolatedFromAbove =
false;
2495 FailureOr<Operation *> op =
2496 parseOpWithoutRegions(reader, readState, isIsolatedFromAbove);
2504 if ((*op)->getNumRegions()) {
2505 RegionReadState childState(*op, &reader, isIsolatedFromAbove);
2510 ArrayRef<uint8_t> sectionData;
2511 if (
failed(reader.parseSection(sectionID, checkSectionAlignment,
2515 return emitError(fileLoc,
"expected IR section for region");
2516 childState.owningReader =
2517 std::make_unique<EncodingReader>(sectionData, fileLoc);
2518 childState.reader = childState.owningReader.get();
2522 if (lazyLoading && (!lazyOpsCallback || !lazyOpsCallback(*op))) {
2523 lazyLoadableOps.emplace_back(*op, std::move(childState));
2524 lazyLoadableOpsMap.try_emplace(*op,
2525 std::prev(lazyLoadableOps.end()));
2529 regionStack.push_back(std::move(childState));
2532 if (isIsolatedFromAbove)
2533 valueScopes.emplace_back();
2539 if (++readState.curBlock == readState.curRegion->end())
2541 if (
failed(parseBlockHeader(reader, readState)))
2546 readState.curBlock = {};
2547 valueScopes.back().pop(readState);
2552 if (readState.isIsolatedFromAbove) {
2553 assert(!valueScopes.empty() &&
"Expect a valueScope after reading region");
2554 valueScopes.pop_back();
2556 assert(!regionStack.empty() &&
"Expect a regionStack after reading region");
2557 regionStack.pop_back();
2561FailureOr<Operation *>
2562BytecodeReader::Impl::parseOpWithoutRegions(EncodingReader &reader,
2563 RegionReadState &readState,
2564 bool &isIsolatedFromAbove) {
2566 std::optional<bool> wasRegistered;
2567 FailureOr<OperationName> opName = parseOpName(reader, wasRegistered);
2574 if (
failed(reader.parseByte(opMask)))
2584 OperationState opState(opLoc, *opName);
2588 DictionaryAttr dictAttr;
2599 "Unexpected missing `wasRegistered` opname flag at "
2600 "bytecode version ")
2601 << version <<
" with properties.";
2605 if (wasRegistered) {
2606 DialectReader dialectReader(attrTypeReader, stringReader, resourceReader,
2607 dialectsMap, reader, version);
2609 propertiesReader.read(fileLoc, dialectReader, &*opName, opState)))
2621 uint64_t numResults;
2622 if (
failed(reader.parseVarInt(numResults)))
2624 opState.
types.resize(numResults);
2625 for (
int i = 0, e = numResults; i < e; ++i)
2632 uint64_t numOperands;
2633 if (
failed(reader.parseVarInt(numOperands)))
2635 opState.
operands.resize(numOperands);
2636 for (
int i = 0, e = numOperands; i < e; ++i)
2637 if (!(opState.
operands[i] = parseOperand(reader)))
2644 if (
failed(reader.parseVarInt(numSuccs)))
2647 for (
int i = 0, e = numSuccs; i < e; ++i) {
2656 std::optional<UseListMapT> resultIdxToUseListMap = std::nullopt;
2659 size_t numResults = opState.
types.size();
2660 auto parseResult = parseUseListOrderForRange(reader, numResults);
2663 resultIdxToUseListMap = std::move(*parseResult);
2668 uint64_t numRegions;
2669 if (
failed(reader.parseVarIntWithFlag(numRegions, isIsolatedFromAbove)))
2672 opState.
regions.reserve(numRegions);
2673 for (
int i = 0, e = numRegions; i < e; ++i)
2674 opState.
regions.push_back(std::make_unique<Region>());
2679 readState.curBlock->push_back(op);
2690 if (resultIdxToUseListMap.has_value()) {
2692 if (resultIdxToUseListMap->contains(idx)) {
2694 resultIdxToUseListMap->at(idx));
2701LogicalResult BytecodeReader::Impl::parseRegion(RegionReadState &readState) {
2702 EncodingReader &reader = *readState.reader;
2706 if (
failed(reader.parseVarInt(numBlocks)))
2715 if (
failed(reader.parseVarInt(numValues)))
2717 readState.numValues = numValues;
2721 readState.curBlocks.clear();
2722 readState.curBlocks.reserve(numBlocks);
2723 for (uint64_t i = 0; i < numBlocks; ++i) {
2724 readState.curBlocks.push_back(
new Block());
2725 readState.curRegion->push_back(readState.curBlocks.back());
2729 valueScopes.back().push(readState);
2732 readState.curBlock = readState.curRegion->begin();
2733 return parseBlockHeader(reader, readState);
2737BytecodeReader::Impl::parseBlockHeader(EncodingReader &reader,
2738 RegionReadState &readState) {
2740 if (
failed(reader.parseVarIntWithFlag(readState.numOpsRemaining, hasArgs)))
2744 if (hasArgs &&
failed(parseBlockArguments(reader, &*readState.curBlock)))
2751 uint8_t hasUseListOrders = 0;
2752 if (hasArgs &&
failed(reader.parseByte(hasUseListOrders)))
2755 if (!hasUseListOrders)
2758 Block &blk = *readState.curBlock;
2759 auto argIdxToUseListMap =
2761 if (
failed(argIdxToUseListMap) || argIdxToUseListMap->empty())
2765 if (argIdxToUseListMap->contains(idx))
2767 argIdxToUseListMap->at(idx));
2773LogicalResult BytecodeReader::Impl::parseBlockArguments(EncodingReader &reader,
2777 if (
failed(reader.parseVarInt(numArgs)))
2780 SmallVector<Type> argTypes;
2781 SmallVector<Location> argLocs;
2782 argTypes.reserve(numArgs);
2783 argLocs.reserve(numArgs);
2785 Location unknownLoc = UnknownLoc::get(config.
getContext());
2788 LocationAttr argLoc = unknownLoc;
2793 if (
failed(reader.parseVarIntWithFlag(typeIdx, hasLoc)) ||
2794 !(argType = attrTypeReader.resolveType(typeIdx)))
2804 argTypes.push_back(argType);
2805 argLocs.push_back(argLoc);
2815Value BytecodeReader::Impl::parseOperand(EncodingReader &reader) {
2816 std::vector<Value> &values = valueScopes.back().values;
2817 Value *value =
nullptr;
2823 *value = createForwardRef();
2827LogicalResult BytecodeReader::Impl::defineValues(EncodingReader &reader,
2829 ValueScope &valueScope = valueScopes.back();
2830 std::vector<Value> &values = valueScope.values;
2832 unsigned &valueID = valueScope.nextValueIDs.back();
2833 unsigned valueIDEnd = valueID + newValues.size();
2834 if (valueIDEnd > values.size()) {
2835 return reader.emitError(
2836 "value index range was outside of the expected range for "
2837 "the parent region, got [",
2838 valueID,
", ", valueIDEnd,
"), but the maximum index was ",
2843 for (
unsigned i = 0, e = newValues.size(); i != e; ++i, ++valueID) {
2844 Value newValue = newValues[i];
2847 if (Value oldValue = std::exchange(values[valueID], newValue)) {
2848 Operation *forwardRefOp = oldValue.getDefiningOp();
2853 assert(forwardRefOp && forwardRefOp->
getBlock() == &forwardRefOps &&
2854 "value index was already defined?");
2856 oldValue.replaceAllUsesWith(newValue);
2857 forwardRefOp->
moveBefore(&openForwardRefOps, openForwardRefOps.end());
2863Value BytecodeReader::Impl::createForwardRef() {
2866 if (!openForwardRefOps.empty()) {
2867 Operation *op = &openForwardRefOps.back();
2868 op->
moveBefore(&forwardRefOps, forwardRefOps.end());
2872 return forwardRefOps.back().getResult(0);
2882 llvm::MemoryBufferRef buffer,
const ParserConfig &config,
bool lazyLoading,
2883 const std::shared_ptr<llvm::SourceMgr> &bufferOwnerRef) {
2887 impl = std::make_unique<Impl>(sourceFileLoc, config, lazyLoading, buffer,
2893 return impl->read(block, lazyOpsCallback);
2897 return impl->getNumOpsToMaterialize();
2901 return impl->isMaterializable(op);
2906 return impl->materialize(op, lazyOpsCallback);
2911 return impl->finalize(shouldMaterialize);
2915 return buffer.getBuffer().starts_with(
"ML\xefR");
2924 const std::shared_ptr<llvm::SourceMgr> &bufferOwnerRef) {
2930 "input buffer is not an MLIR bytecode file");
2934 buffer, bufferOwnerRef);
2935 return reader.
read(block,
nullptr);
2946 *sourceMgr->getMemoryBuffer(sourceMgr->getMainFileID()), block, config,
static LogicalResult parseDialectGrouping(EncodingReader &reader, MutableArrayRef< std::unique_ptr< BytecodeDialect > > dialects, function_ref< LogicalResult(BytecodeDialect *)> entryCallback)
Parse a single dialect group encoded in the byte stream.
static LogicalResult readBytecodeFileImpl(llvm::MemoryBufferRef buffer, Block *block, const ParserConfig &config, const std::shared_ptr< llvm::SourceMgr > &bufferOwnerRef)
Read the bytecode from the provided memory buffer reference.
static bool isSectionOptional(bytecode::Section::ID sectionID, int version)
Returns true if the given top-level section ID is optional.
static LogicalResult parseResourceGroup(Location fileLoc, bool allowEmpty, EncodingReader &offsetReader, EncodingReader &resourceReader, StringSectionReader &stringReader, T *handler, const std::shared_ptr< llvm::SourceMgr > &bufferOwnerRef, function_ref< StringRef(StringRef)> remapKey={}, function_ref< LogicalResult(StringRef)> processKeyFn={})
static LogicalResult resolveEntry(EncodingReader &reader, RangeT &entries, uint64_t index, T &entry, StringRef entryStr)
Resolve an index into the given entry list.
static LogicalResult parseEntry(EncodingReader &reader, RangeT &entries, T &entry, StringRef entryStr)
Parse and resolve an index into the given entry list.
LogicalResult initialize(unsigned origNumLoops, ArrayRef< ReassociationIndices > foldedIterationDims)
MutableArrayRef< char > getMutableData()
Return a mutable reference to the raw underlying data of this blob.
ArrayRef< char > getData() const
Return the raw underlying data of this blob.
bool isMutable() const
Return if the data of this blob is mutable.
MLIRContext * getContext() const
Return the context this attribute belongs to.
Block represents an ordered list of Operations.
BlockArgument getArgument(unsigned i)
unsigned getNumArguments()
iterator_range< args_iterator > addArguments(TypeRange types, ArrayRef< Location > locs)
Add one argument to the argument list for each type specified in the list.
OpListType & getOperations()
BlockArgListType getArguments()
ArrayRef< std::unique_ptr< AttrTypeBytecodeReader< Type > > > getTypeCallbacks() const
ArrayRef< std::unique_ptr< AttrTypeBytecodeReader< Attribute > > > getAttributeCallbacks() const
Returns the callbacks available to the parser.
This class is used to read a bytecode buffer and translate it into MLIR.
LogicalResult materializeAll()
Materialize all operations.
LogicalResult read(Block *block, llvm::function_ref< bool(Operation *)> lazyOps)
Read the bytecode defined within buffer into the given block.
bool isMaterializable(Operation *op)
Impl(Location fileLoc, const ParserConfig &config, bool lazyLoading, llvm::MemoryBufferRef buffer, const std::shared_ptr< llvm::SourceMgr > &bufferOwnerRef)
LogicalResult finalize(function_ref< bool(Operation *)> shouldMaterialize)
Finalize the lazy-loading by calling back with every op that hasn't been materialized to let the clie...
LogicalResult materialize(Operation *op, llvm::function_ref< bool(Operation *)> lazyOpsCallback)
Materialize the provided operation, invoke the lazyOpsCallback on every newly found lazy operation.
int64_t getNumOpsToMaterialize() const
Return the number of ops that haven't been materialized yet.
LogicalResult materialize(Operation *op, llvm::function_ref< bool(Operation *)> lazyOpsCallback=[](Operation *) { return false;})
Materialize the provide operation.
LogicalResult finalize(function_ref< bool(Operation *)> shouldMaterialize=[](Operation *) { return true;})
Finalize the lazy-loading by calling back with every op that hasn't been materialized to let the clie...
BytecodeReader(llvm::MemoryBufferRef buffer, const ParserConfig &config, bool lazyLoad, const std::shared_ptr< llvm::SourceMgr > &bufferOwnerRef={})
Create a bytecode reader for the given buffer.
int64_t getNumOpsToMaterialize() const
Return the number of ops that haven't been materialized yet.
bool isMaterializable(Operation *op)
Return true if the provided op is materializable.
LogicalResult readTopLevel(Block *block, llvm::function_ref< bool(Operation *)> lazyOps=[](Operation *) { return false;})
Read the operations defined within the given memory buffer, containing MLIR bytecode,...
This class contains all of the information necessary to report a diagnostic to the DiagnosticEngine.
static FileLineColLoc get(StringAttr filename, unsigned line, unsigned column)
This class represents a diagnostic that is inflight and set to be reported.
InFlightDiagnostic & append(Args &&...args) &
Append arguments to the diagnostic.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
MLIRContext * getContext() const
Return the context this location is uniqued in.
MLIRContext is the top-level object for a collection of MLIR operations.
T * getOrLoadDialect()
Get (or create) a dialect for the given derived dialect type.
bool allowsUnregisteredDialects()
Return true if we allow to create operation for unregistered dialects.
StringRef getStringRef() const
Return the name of this operation. This always succeeds.
bool isRegistered() const
Return if this operation is registered.
T::Concept * getInterface() const
Returns an instance of the concept object for the given interface if it was registered to this operat...
Operation is the basic unit of execution within MLIR.
void dropAllReferences()
This drops all operand uses from this operation, which is an essential step in breaking cyclic depend...
Block * getBlock()
Returns the operation block that contains this operation.
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
static Operation * create(Location location, OperationName name, TypeRange resultTypes, ValueRange operands, NamedAttrList &&attributes, PropertyRef properties, BlockRange successors, unsigned numRegions)
Create a new Operation with the specific fields.
void moveBefore(Operation *existingOp)
Unlink this operation from its current block and insert it right before existingOp which may be in th...
std::enable_if_t< llvm::function_traits< std::decay_t< FnT > >::num_args==1, RetT > walk(FnT &&callback)
Walk the operation by calling the callback for each nested operation (including this one),...
result_range getResults()
void erase()
Remove this operation from its parent block and delete it.
unsigned getNumResults()
Return the number of results held by this operation.
This class represents a configuration for the MLIR assembly parser.
MLIRContext * getContext() const
Return the MLIRContext to be used when parsing.
bool shouldVerifyAfterParse() const
Returns if the parser should verify the IR after parsing.
BytecodeReaderConfig & getBytecodeReaderConfig() const
Returns the parsing configurations associated to the bytecode read.
AsmResourceParser * getResourceParser(StringRef name) const
Return the resource parser registered to the given name, or nullptr if no parser with name is registe...
BlockListType::iterator iterator
This diagnostic handler is a simple RAII class that registers and erases a diagnostic handler on a gi...
static AsmResourceBlob allocateWithAlign(ArrayRef< char > data, size_t align, AsmResourceBlob::DeleterFn deleter={}, bool dataIsMutable=false)
Create a new unmanaged resource directly referencing the provided data.
This class provides an abstraction over the different types of ranges over Values.
bool use_empty() const
Returns true if this value has no uses.
void shuffleUseList(ArrayRef< unsigned > indices)
Shuffle the use list order according to the provided indices.
use_range getUses() const
Returns a range of all uses, which is useful for iterating over all uses.
void * getAsOpaquePointer() const
Methods for supporting PointerLikeTypeTraits.
unsigned getNumUses() const
This method computes the number of uses of this Value.
bool hasOneUse() const
Returns true if this value has exactly one use.
use_iterator use_begin() const
static WalkResult advance()
static WalkResult interrupt()
@ kAttrType
This section contains the attributes and types referenced within an IR module.
@ kAttrTypeOffset
This section contains the offsets for the attribute and types within the AttrType section.
@ kIR
This section contains the list of operations serialized into the bytecode, and their nested regions/o...
@ kResource
This section contains the resources of the bytecode.
@ kResourceOffset
This section contains the offsets of resources within the Resource section.
@ kDialect
This section contains the dialects referenced within an IR module.
@ kString
This section contains strings referenced within the bytecode.
@ kDialectVersions
This section contains the versions of each dialect.
@ kProperties
This section contains the properties for the operations.
@ kNumSections
The total number of section types.
static uint64_t getUseID(OperandT &val, unsigned ownerID)
Get the unique ID of a value use.
@ kUseListOrdering
Use-list ordering started to be encoded in version 3.
@ kAlignmentByte
An arbitrary value used to fill alignment padding.
@ kVersion
The current bytecode version.
@ kLazyLoading
Support for lazy-loading of isolated region was added in version 2.
@ kDialectVersioning
Dialects versioning was added in version 1.
@ kElideUnknownBlockArgLocation
Avoid recording unknown locations on block arguments (compression) started in version 4.
@ kNativePropertiesEncoding
Support for encoding properties natively in bytecode instead of merged with the discardable attribute...
@ kMinSupportedVersion
The minimum supported version of the bytecode.
Include the generated interface declarations.
InFlightDiagnostic emitWarning(Location loc)
Utility method to emit a warning message using this location.
StringRef toString(AsmResourceEntryKind kind)
bool isBytecode(llvm::MemoryBufferRef buffer)
Returns true if the given buffer starts with the magic bytes that signal MLIR bytecode.
llvm::DenseSet< ValueT, ValueInfoT > DenseSet
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
Attribute parseAttribute(llvm::StringRef attrStr, MLIRContext *context, Type type={}, size_t *numRead=nullptr, bool isKnownNullTerminated=false)
This parses a single MLIR attribute to an MLIR context if it was valid.
auto get(MLIRContext *context, Ts &&...params)
Helper method that injects context only if needed, this helps unify some of the attribute constructio...
Type parseType(llvm::StringRef typeStr, MLIRContext *context, size_t *numRead=nullptr, bool isKnownNullTerminated=false)
This parses a single MLIR type to an MLIR context if it was valid.
llvm::DenseMap< KeyT, ValueT, KeyInfoT, BucketT > DenseMap
AsmResourceEntryKind
This enum represents the different kinds of resource values.
LogicalResult readBytecodeFile(llvm::MemoryBufferRef buffer, Block *block, const ParserConfig &config)
Read the operations defined within the given memory buffer, containing MLIR bytecode,...
LogicalResult verify(Operation *op, bool verifyRecursively=true)
Perform (potentially expensive) checks of invariants, used to detect compiler bugs,...
llvm::function_ref< Fn > function_ref
LogicalResult readResourceHandle(DialectBytecodeReader &reader, FailureOr< T > &value, Ts &&...params)
Helper for resource handle reading that returns LogicalResult.
SmallVector< Block *, 1 > successors
Successors of this operation and their respective operands.
SmallVector< Value, 4 > operands
SmallVector< std::unique_ptr< Region >, 1 > regions
Regions that the op will hold.
Attribute propertiesAttr
This Attribute is used to opaquely construct the properties of the operation.
SmallVector< Type, 4 > types
Types of the results of this operation.