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 T>
149 LogicalResult parseByte(T &value) {
151 return emitError(
"attempting to parse a byte at the end of the bytecode");
152 value =
static_cast<T
>(*dataIt++);
156 LogicalResult parseBytes(
size_t length, ArrayRef<uint8_t> &
result) {
157 if (length > size()) {
158 return emitError(
"attempting to parse ", length,
" bytes when only ",
161 result = {dataIt, length};
167 LogicalResult parseBytes(
size_t length, uint8_t *
result) {
168 if (length > size()) {
169 return emitError(
"attempting to parse ", length,
" bytes when only ",
172 memcpy(
result, dataIt, length);
179 LogicalResult parseBlobAndAlignment(ArrayRef<uint8_t> &data,
180 uint64_t &alignment) {
182 if (
failed(parseVarInt(alignment)) ||
failed(parseVarInt(dataSize)) ||
183 failed(alignTo(alignment)))
185 return parseBytes(dataSize, data);
195 LogicalResult parseVarInt(uint64_t &
result) {
202 if (LLVM_LIKELY(
result & 1)) {
210 if (LLVM_UNLIKELY(
result == 0)) {
211 llvm::support::ulittle64_t resultLE;
212 if (
failed(parseBytes(
sizeof(resultLE),
213 reinterpret_cast<uint8_t *
>(&resultLE))))
218 return parseMultiByteVarInt(
result);
224 LogicalResult parseSignedVarInt(uint64_t &
result) {
234 LogicalResult parseVarIntWithFlag(uint64_t &
result,
bool &flag) {
243 LogicalResult skipBytes(
size_t length) {
244 if (length > size()) {
245 return emitError(
"attempting to skip ", length,
" bytes when only ",
254 LogicalResult parseNullTerminatedString(StringRef &
result) {
255 const char *startIt = (
const char *)dataIt;
256 const char *nulIt = (
const char *)memchr(startIt, 0, size());
259 "malformed null-terminated string, no null character found");
261 result = StringRef(startIt, nulIt - startIt);
262 dataIt = (
const uint8_t *)nulIt + 1;
267 using ValidateAlignmentFn =
function_ref<LogicalResult(
unsigned alignment)>;
272 ValidateAlignmentFn alignmentValidator,
273 ArrayRef<uint8_t> §ionData) {
274 uint8_t sectionIDAndHasAlignment;
276 if (
failed(parseByte(sectionIDAndHasAlignment)) ||
277 failed(parseVarInt(length)))
284 bool hasAlignment = sectionIDAndHasAlignment & 0b10000000;
289 return emitError(
"invalid section ID: ",
unsigned(sectionID));
295 if (
failed(parseVarInt(alignment)))
330 if (
failed(alignmentValidator(alignment)))
331 return emitError(
"failed to align section ID: ",
unsigned(sectionID));
334 if (
failed(alignTo(alignment)))
339 return parseBytes(
static_cast<size_t>(length), sectionData);
342 Location getLoc()
const {
return fileLoc; }
351 LLVM_ATTRIBUTE_NOINLINE LogicalResult parseMultiByteVarInt(uint64_t &
result) {
357 uint32_t numBytes = llvm::countr_zero<uint32_t>(
result);
358 assert(numBytes > 0 && numBytes <= 7 &&
359 "unexpected number of trailing zeros in varint encoding");
362 llvm::support::ulittle64_t resultLE(
result);
364 parseBytes(numBytes,
reinterpret_cast<uint8_t *
>(&resultLE) + 1)))
369 result = resultLE >> (numBytes + 1);
374 ArrayRef<uint8_t> buffer;
377 const uint8_t *dataIt;
388template <
typename RangeT,
typename T>
389static LogicalResult
resolveEntry(EncodingReader &reader, RangeT &entries,
390 uint64_t
index, T &entry,
391 StringRef entryStr) {
392 if (
index >= entries.size())
393 return reader.emitError(
"invalid ", entryStr,
" index: ",
index);
396 if constexpr (std::is_convertible_v<llvm::detail::ValueOfRange<RangeT>, T>)
397 entry = entries[
index];
399 entry = &entries[
index];
404template <
typename RangeT,
typename T>
405static LogicalResult
parseEntry(EncodingReader &reader, RangeT &entries,
406 T &entry, StringRef entryStr) {
408 if (failed(reader.parseVarInt(entryIdx)))
410 return resolveEntry(reader, entries, entryIdx, entry, entryStr);
420class StringSectionReader {
423 LogicalResult
initialize(Location fileLoc, ArrayRef<uint8_t> sectionData);
427 LogicalResult parseString(EncodingReader &reader, StringRef &
result)
const {
434 LogicalResult parseStringWithFlag(EncodingReader &reader, StringRef &
result,
437 if (
failed(reader.parseVarIntWithFlag(entryIdx, flag)))
439 return parseStringAtIndex(reader, entryIdx,
result);
444 LogicalResult parseStringAtIndex(EncodingReader &reader, uint64_t index,
445 StringRef &
result)
const {
451 SmallVector<StringRef> strings;
455LogicalResult StringSectionReader::initialize(
Location fileLoc,
457 EncodingReader stringReader(sectionData, fileLoc);
461 if (
failed(stringReader.parseVarInt(numStrings)))
463 strings.resize(numStrings);
467 size_t stringDataEndOffset = sectionData.size();
468 for (StringRef &
string : llvm::reverse(strings)) {
470 if (
failed(stringReader.parseVarInt(stringSize)))
472 if (stringDataEndOffset < stringSize) {
473 return stringReader.emitError(
474 "string size exceeds the available data size");
478 size_t stringOffset = stringDataEndOffset - stringSize;
480 reinterpret_cast<const char *
>(sectionData.data() + stringOffset),
482 stringDataEndOffset = stringOffset;
487 if ((sectionData.size() - stringReader.size()) != stringDataEndOffset) {
488 return stringReader.emitError(
"unexpected trailing data between the "
489 "offsets for strings and their data");
502struct BytecodeDialect {
507 LogicalResult
load(
const DialectReader &reader, MLIRContext *ctx);
511 Dialect *getLoadedDialect()
const {
513 "expected `load` to be invoked before `getLoadedDialect`");
520 std::optional<Dialect *> dialect;
525 const BytecodeDialectInterface *
interface =
nullptr;
531 ArrayRef<uint8_t> versionBuffer;
534 std::unique_ptr<DialectVersion> loadedVersion;
538struct BytecodeOperationName {
539 BytecodeOperationName(BytecodeDialect *dialect, StringRef name,
540 std::optional<bool> wasRegistered)
541 : dialect(dialect), name(name), wasRegistered(wasRegistered) {}
545 std::optional<OperationName> opName;
548 BytecodeDialect *dialect;
555 std::optional<bool> wasRegistered;
561 EncodingReader &reader,
563 function_ref<LogicalResult(BytecodeDialect *)> entryCallback) {
565 std::unique_ptr<BytecodeDialect> *dialect;
566 if (failed(
parseEntry(reader, dialects, dialect,
"dialect")))
569 if (failed(reader.parseVarInt(numEntries)))
572 for (uint64_t i = 0; i < numEntries; ++i)
573 if (failed(entryCallback(dialect->get())))
584class ResourceSectionReader {
589 MutableArrayRef<std::unique_ptr<BytecodeDialect>> dialects,
590 StringSectionReader &stringReader, ArrayRef<uint8_t> sectionData,
591 ArrayRef<uint8_t> offsetSectionData, DialectReader &dialectReader,
592 const std::shared_ptr<llvm::SourceMgr> &bufferOwnerRef);
595 LogicalResult parseResourceHandle(EncodingReader &reader,
596 AsmDialectResourceHandle &
result)
const {
602 SmallVector<AsmDialectResourceHandle> dialectResources;
603 llvm::StringMap<std::string> dialectResourceHandleRenamingMap;
606class ParsedResourceEntry :
public AsmParsedResourceEntry {
609 EncodingReader &reader, StringSectionReader &stringReader,
610 const std::shared_ptr<llvm::SourceMgr> &bufferOwnerRef)
611 : key(key), kind(kind), reader(reader), stringReader(stringReader),
612 bufferOwnerRef(bufferOwnerRef) {}
613 ~ParsedResourceEntry()
override =
default;
615 StringRef getKey() const final {
return key; }
617 InFlightDiagnostic
emitError() const final {
return reader.emitError(); }
621 FailureOr<bool> parseAsBool() const final {
622 if (kind != AsmResourceEntryKind::Bool)
623 return emitError() <<
"expected a bool resource entry, but found a "
624 <<
toString(kind) <<
" entry instead";
627 if (
failed(reader.parseByte(value)))
631 FailureOr<std::string> parseAsString() const final {
632 if (kind != AsmResourceEntryKind::String)
633 return emitError() <<
"expected a string resource entry, but found a "
634 <<
toString(kind) <<
" entry instead";
637 if (
failed(stringReader.parseString(reader,
string)))
642 FailureOr<AsmResourceBlob>
643 parseAsBlob(BlobAllocatorFn allocator)
const final {
644 if (kind != AsmResourceEntryKind::Blob)
645 return emitError() <<
"expected a blob resource entry, but found a "
646 <<
toString(kind) <<
" entry instead";
648 ArrayRef<uint8_t> data;
650 if (
failed(reader.parseBlobAndAlignment(data, alignment)))
655 if (bufferOwnerRef) {
656 ArrayRef<char> charData(
reinterpret_cast<const char *
>(data.data()),
664 [bufferOwnerRef = bufferOwnerRef](
void *,
size_t,
size_t) {});
669 AsmResourceBlob blob = allocator(data.size(), alignment);
670 assert(llvm::isAddrAligned(llvm::Align(alignment), blob.
getData().data()) &&
672 "blob allocator did not return a properly aligned address");
680 EncodingReader &reader;
681 StringSectionReader &stringReader;
682 const std::shared_ptr<llvm::SourceMgr> &bufferOwnerRef;
689 EncodingReader &offsetReader, EncodingReader &resourceReader,
690 StringSectionReader &stringReader, T *handler,
691 const std::shared_ptr<llvm::SourceMgr> &bufferOwnerRef,
693 function_ref<LogicalResult(StringRef)> processKeyFn = {}) {
694 uint64_t numResources;
695 if (
failed(offsetReader.parseVarInt(numResources)))
698 for (uint64_t i = 0; i < numResources; ++i) {
701 uint64_t resourceOffset;
702 ArrayRef<uint8_t> data;
703 if (
failed(stringReader.parseString(offsetReader, key)) ||
704 failed(offsetReader.parseVarInt(resourceOffset)) ||
705 failed(offsetReader.parseByte(kind)) ||
706 failed(resourceReader.parseBytes(resourceOffset, data)))
710 if ((processKeyFn &&
failed(processKeyFn(key))))
715 if (allowEmpty && data.empty())
723 EncodingReader entryReader(data, fileLoc);
725 ParsedResourceEntry entry(key, kind, entryReader, stringReader,
727 if (
failed(handler->parseResource(entry)))
729 if (!entryReader.empty()) {
730 return entryReader.emitError(
731 "unexpected trailing bytes in resource entry '", key,
"'");
737LogicalResult ResourceSectionReader::initialize(
738 Location fileLoc,
const ParserConfig &
config,
739 MutableArrayRef<std::unique_ptr<BytecodeDialect>> dialects,
740 StringSectionReader &stringReader, ArrayRef<uint8_t> sectionData,
741 ArrayRef<uint8_t> offsetSectionData, DialectReader &dialectReader,
742 const std::shared_ptr<llvm::SourceMgr> &bufferOwnerRef) {
743 EncodingReader resourceReader(sectionData, fileLoc);
744 EncodingReader offsetReader(offsetSectionData, fileLoc);
747 uint64_t numExternalResourceGroups;
748 if (
failed(offsetReader.parseVarInt(numExternalResourceGroups)))
753 auto parseGroup = [&](
auto *handler,
bool allowEmpty =
false,
755 auto resolveKey = [&](StringRef key) -> StringRef {
756 auto it = dialectResourceHandleRenamingMap.find(key);
757 if (it == dialectResourceHandleRenamingMap.end())
763 stringReader, handler, bufferOwnerRef, resolveKey,
768 for (uint64_t i = 0; i < numExternalResourceGroups; ++i) {
770 if (
failed(stringReader.parseString(offsetReader, key)))
775 AsmResourceParser *handler =
config.getResourceParser(key);
777 emitWarning(fileLoc) <<
"ignoring unknown external resources for '" << key
781 if (
failed(parseGroup(handler)))
787 while (!offsetReader.empty()) {
788 std::unique_ptr<BytecodeDialect> *dialect;
790 failed((*dialect)->load(dialectReader, ctx)))
792 Dialect *loadedDialect = (*dialect)->getLoadedDialect();
793 if (!loadedDialect) {
794 return resourceReader.emitError()
795 <<
"dialect '" << (*dialect)->name <<
"' is unknown";
797 const auto *handler = dyn_cast<OpAsmDialectInterface>(loadedDialect);
799 return resourceReader.emitError()
800 <<
"unexpected resources for dialect '" << (*dialect)->name <<
"'";
804 auto processResourceKeyFn = [&](StringRef key) -> LogicalResult {
805 FailureOr<AsmDialectResourceHandle> handle =
806 handler->declareResource(key);
808 return resourceReader.emitError()
809 <<
"unknown 'resource' key '" << key <<
"' for dialect '"
810 << (*dialect)->name <<
"'";
812 dialectResourceHandleRenamingMap[key] = handler->getResourceKey(*handle);
813 dialectResources.push_back(*handle);
819 if (
failed(parseGroup(handler,
true, processResourceKeyFn)))
851class AttrTypeReader {
853 template <
typename T>
858 BytecodeDialect *dialect =
nullptr;
861 bool hasCustomEncoding =
false;
863 ArrayRef<uint8_t> data;
865 using AttrEntry = Entry<Attribute>;
866 using TypeEntry = Entry<Type>;
869 AttrTypeReader(
const StringSectionReader &stringReader,
870 const ResourceSectionReader &resourceReader,
871 const llvm::StringMap<BytecodeDialect *> &dialectsMap,
872 uint64_t &bytecodeVersion, Location fileLoc,
873 const ParserConfig &
config)
874 : stringReader(stringReader), resourceReader(resourceReader),
875 dialectsMap(dialectsMap), fileLoc(fileLoc),
876 bytecodeVersion(bytecodeVersion), parserConfig(
config) {}
880 initialize(MutableArrayRef<std::unique_ptr<BytecodeDialect>> dialects,
881 ArrayRef<uint8_t> sectionData,
882 ArrayRef<uint8_t> offsetSectionData);
884 LogicalResult readAttribute(uint64_t index, Attribute &
result,
885 uint64_t depth = 0) {
886 return readEntry(attributes, index,
result,
"attribute", depth);
889 LogicalResult readType(uint64_t index, Type &
result, uint64_t depth = 0) {
890 return readEntry(types, index,
result,
"type", depth);
895 Attribute resolveAttribute(
size_t index, uint64_t depth = 0) {
896 return resolveEntry(attributes, index,
"Attribute", depth);
898 Type resolveType(
size_t index, uint64_t depth = 0) {
902 Attribute getAttributeOrSentinel(
size_t index) {
903 if (index >= attributes.size())
905 return attributes[index].entry;
907 Type getTypeOrSentinel(
size_t index) {
908 if (index >= types.size())
910 return types[index].entry;
916 if (
failed(reader.parseVarInt(attrIdx)))
918 result = resolveAttribute(attrIdx);
921 LogicalResult parseOptionalAttribute(EncodingReader &reader,
925 if (
failed(reader.parseVarIntWithFlag(attrIdx, flag)))
929 result = resolveAttribute(attrIdx);
935 if (
failed(reader.parseVarInt(typeIdx)))
937 result = resolveType(typeIdx);
941 template <
typename T>
943 Attribute baseResult;
946 if ((
result = dyn_cast<T>(baseResult)))
948 return reader.emitError(
"expected attribute of type: ",
949 llvm::getTypeName<T>(),
", but got: ", baseResult);
953 void addDeferredParsing(uint64_t index) { deferredWorklist.push_back(index); }
957 template <
typename T>
958 T
resolveEntry(SmallVectorImpl<Entry<T>> &entries, uint64_t index,
959 StringRef entryType, uint64_t depth = 0);
963 template <
typename T>
964 LogicalResult readEntry(SmallVectorImpl<Entry<T>> &entries, uint64_t index,
965 T &
result, StringRef entryType, uint64_t depth);
969 template <
typename T>
970 LogicalResult parseCustomEntry(Entry<T> &entry, EncodingReader &reader,
971 StringRef entryType, uint64_t index,
976 template <
typename T>
977 LogicalResult parseAsmEntry(T &
result, EncodingReader &reader,
978 StringRef entryType);
982 const StringSectionReader &stringReader;
986 const ResourceSectionReader &resourceReader;
990 const llvm::StringMap<BytecodeDialect *> &dialectsMap;
993 SmallVector<AttrEntry> attributes;
994 SmallVector<TypeEntry> types;
1000 uint64_t &bytecodeVersion;
1003 const ParserConfig &parserConfig;
1007 std::vector<uint64_t> deferredWorklist;
1010class DialectReader :
public DialectBytecodeReader {
1012 DialectReader(AttrTypeReader &attrTypeReader,
1013 const StringSectionReader &stringReader,
1014 const ResourceSectionReader &resourceReader,
1015 const llvm::StringMap<BytecodeDialect *> &dialectsMap,
1016 EncodingReader &reader, uint64_t &bytecodeVersion,
1018 : attrTypeReader(attrTypeReader), stringReader(stringReader),
1019 resourceReader(resourceReader), dialectsMap(dialectsMap),
1020 reader(reader), bytecodeVersion(bytecodeVersion), depth(depth) {}
1022 InFlightDiagnostic
emitError(
const Twine &msg)
const override {
1023 return reader.emitError(msg);
1026 FailureOr<const DialectVersion *>
1027 getDialectVersion(StringRef dialectName)
const override {
1029 auto dialectEntry = dialectsMap.find(dialectName);
1030 if (dialectEntry == dialectsMap.end())
1035 if (
failed(dialectEntry->getValue()->load(*
this, getLoc().
getContext())) ||
1036 dialectEntry->getValue()->loadedVersion ==
nullptr)
1038 return dialectEntry->getValue()->loadedVersion.get();
1041 MLIRContext *
getContext()
const override {
return getLoc().getContext(); }
1043 uint64_t getBytecodeVersion()
const override {
return bytecodeVersion; }
1045 DialectReader withEncodingReader(EncodingReader &encReader)
const {
1046 return DialectReader(attrTypeReader, stringReader, resourceReader,
1047 dialectsMap, encReader, bytecodeVersion);
1050 Location getLoc()
const {
return reader.getLoc(); }
1058 static constexpr uint64_t maxAttrTypeDepth = 5;
1060 LogicalResult readAttribute(Attribute &
result)
override {
1062 if (
failed(reader.parseVarInt(index)))
1064 if (depth > maxAttrTypeDepth) {
1065 if (Attribute attr = attrTypeReader.getAttributeOrSentinel(index)) {
1069 attrTypeReader.addDeferredParsing(index);
1072 return attrTypeReader.readAttribute(index,
result, depth + 1);
1074 LogicalResult readOptionalAttribute(Attribute &
result)
override {
1075 return attrTypeReader.parseOptionalAttribute(reader,
result);
1077 LogicalResult readType(Type &
result)
override {
1079 if (
failed(reader.parseVarInt(index)))
1081 if (depth > maxAttrTypeDepth) {
1082 if (Type type = attrTypeReader.getTypeOrSentinel(index)) {
1086 attrTypeReader.addDeferredParsing(index);
1089 return attrTypeReader.readType(index,
result, depth + 1);
1093 AsmDialectResourceHandle handle;
1094 if (
failed(resourceReader.parseResourceHandle(reader, handle)))
1103 LogicalResult readVarInt(uint64_t &
result)
override {
1104 return reader.parseVarInt(
result);
1107 LogicalResult readSignedVarInt(int64_t &
result)
override {
1108 uint64_t unsignedResult;
1109 if (
failed(reader.parseSignedVarInt(unsignedResult)))
1111 result =
static_cast<int64_t
>(unsignedResult);
1115 FailureOr<APInt> readAPIntWithKnownWidth(
unsigned bitWidth)
override {
1117 if (bitWidth <= 8) {
1119 if (
failed(reader.parseByte(value)))
1121 return APInt(bitWidth, value);
1125 if (bitWidth <= 64) {
1127 if (
failed(reader.parseSignedVarInt(value)))
1129 return APInt(bitWidth, value);
1134 uint64_t numActiveWords;
1135 if (
failed(reader.parseVarInt(numActiveWords)))
1137 SmallVector<uint64_t, 4> words(numActiveWords);
1138 for (uint64_t i = 0; i < numActiveWords; ++i)
1139 if (
failed(reader.parseSignedVarInt(words[i])))
1141 return APInt(bitWidth, words);
1145 readAPFloatWithKnownSemantics(
const llvm::fltSemantics &semantics)
override {
1146 FailureOr<APInt> intVal =
1147 readAPIntWithKnownWidth(APFloat::getSizeInBits(semantics));
1150 return APFloat(semantics, *intVal);
1153 LogicalResult readString(StringRef &
result)
override {
1154 return stringReader.parseString(reader,
result);
1157 LogicalResult readBlob(ArrayRef<char> &
result)
override {
1159 ArrayRef<uint8_t> data;
1160 if (
failed(reader.parseVarInt(dataSize)) ||
1161 failed(reader.parseBytes(dataSize, data)))
1163 result = llvm::ArrayRef(
reinterpret_cast<const char *
>(data.data()),
1168 LogicalResult readBool(
bool &
result)
override {
1169 return reader.parseByte(
result);
1173 AttrTypeReader &attrTypeReader;
1174 const StringSectionReader &stringReader;
1175 const ResourceSectionReader &resourceReader;
1176 const llvm::StringMap<BytecodeDialect *> &dialectsMap;
1177 EncodingReader &reader;
1178 uint64_t &bytecodeVersion;
1183class PropertiesSectionReader {
1186 LogicalResult
initialize(Location fileLoc, ArrayRef<uint8_t> sectionData) {
1187 if (sectionData.empty())
1189 EncodingReader propReader(sectionData, fileLoc);
1191 if (
failed(propReader.parseVarInt(count)))
1194 if (
failed(propReader.parseBytes(propReader.size(), propertiesBuffers)))
1197 EncodingReader offsetsReader(propertiesBuffers, fileLoc);
1198 offsetTable.reserve(count);
1199 for (
auto idx : llvm::seq<int64_t>(0, count)) {
1201 offsetTable.push_back(propertiesBuffers.size() - offsetsReader.size());
1202 ArrayRef<uint8_t> rawProperties;
1204 if (
failed(offsetsReader.parseVarInt(dataSize)) ||
1205 failed(offsetsReader.parseBytes(dataSize, rawProperties)))
1208 if (!offsetsReader.empty())
1209 return offsetsReader.emitError()
1210 <<
"Broken properties section: didn't exhaust the offsets table";
1214 LogicalResult read(Location fileLoc, DialectReader &dialectReader,
1215 OperationName *opName, OperationState &opState)
const {
1216 uint64_t propertiesIdx;
1217 if (
failed(dialectReader.readVarInt(propertiesIdx)))
1219 if (propertiesIdx >= offsetTable.size())
1220 return dialectReader.emitError(
"Properties idx out-of-bound for ")
1222 size_t propertiesOffset = offsetTable[propertiesIdx];
1223 if (propertiesIdx >= propertiesBuffers.size())
1224 return dialectReader.emitError(
"Properties offset out-of-bound for ")
1228 ArrayRef<char> rawProperties;
1232 EncodingReader reader(propertiesBuffers.drop_front(propertiesOffset),
1236 dialectReader.withEncodingReader(reader).readBlob(rawProperties)))
1240 EncodingReader reader(
1241 StringRef(rawProperties.begin(), rawProperties.size()), fileLoc);
1242 DialectReader propReader = dialectReader.withEncodingReader(reader);
1244 auto *iface = opName->
getInterface<BytecodeOpInterface>();
1246 return iface->readProperties(propReader, opState);
1248 return propReader.emitError(
1249 "has properties but missing BytecodeOpInterface for ")
1257 ArrayRef<uint8_t> propertiesBuffers;
1260 SmallVector<int64_t> offsetTable;
1264LogicalResult AttrTypeReader::initialize(
1265 MutableArrayRef<std::unique_ptr<BytecodeDialect>> dialects,
1266 ArrayRef<uint8_t> sectionData, ArrayRef<uint8_t> offsetSectionData) {
1267 EncodingReader offsetReader(offsetSectionData, fileLoc);
1270 uint64_t numAttributes, numTypes;
1271 if (
failed(offsetReader.parseVarInt(numAttributes)) ||
1272 failed(offsetReader.parseVarInt(numTypes)))
1274 attributes.resize(numAttributes);
1275 types.resize(numTypes);
1279 uint64_t currentOffset = 0;
1280 auto parseEntries = [&](
auto &&range) {
1281 size_t currentIndex = 0, endIndex = range.size();
1284 auto parseEntryFn = [&](BytecodeDialect *dialect) -> LogicalResult {
1285 auto &entry = range[currentIndex++];
1288 if (
failed(offsetReader.parseVarIntWithFlag(entrySize,
1289 entry.hasCustomEncoding)))
1293 if (currentOffset + entrySize > sectionData.size()) {
1294 return offsetReader.emitError(
1295 "Attribute or Type entry offset points past the end of section");
1298 entry.data = sectionData.slice(currentOffset, entrySize);
1299 entry.dialect = dialect;
1300 currentOffset += entrySize;
1303 while (currentIndex != endIndex)
1310 if (
failed(parseEntries(attributes)) ||
failed(parseEntries(types)))
1314 if (!offsetReader.empty()) {
1315 return offsetReader.emitError(
1316 "unexpected trailing data in the Attribute/Type offset section");
1322template <
typename T>
1323T AttrTypeReader::resolveEntry(SmallVectorImpl<Entry<T>> &entries,
1324 uint64_t index, StringRef entryType,
1326 if (index >= entries.size()) {
1327 emitError(fileLoc) <<
"invalid " << entryType <<
" index: " << index;
1333 assert(deferredWorklist.empty());
1335 if (succeeded(readEntry(entries, index,
result, entryType, depth))) {
1336 assert(deferredWorklist.empty());
1339 if (deferredWorklist.empty()) {
1349 std::deque<size_t> worklist;
1350 llvm::DenseSet<size_t> inWorklist;
1353 worklist.push_back(index);
1354 inWorklist.insert(index);
1355 for (uint64_t idx : llvm::reverse(deferredWorklist)) {
1356 if (inWorklist.insert(idx).second)
1357 worklist.push_front(idx);
1360 while (!worklist.empty()) {
1361 size_t currentIndex = worklist.front();
1362 worklist.pop_front();
1365 deferredWorklist.clear();
1368 if (succeeded(readEntry(entries, currentIndex,
result, entryType, depth))) {
1369 inWorklist.erase(currentIndex);
1373 if (deferredWorklist.empty()) {
1379 worklist.push_back(currentIndex);
1382 for (uint64_t idx : llvm::reverse(deferredWorklist)) {
1383 if (inWorklist.insert(idx).second)
1384 worklist.push_front(idx);
1386 deferredWorklist.clear();
1388 return entries[index].entry;
1391template <
typename T>
1392LogicalResult AttrTypeReader::readEntry(SmallVectorImpl<Entry<T>> &entries,
1393 uint64_t index, T &
result,
1394 StringRef entryType, uint64_t depth) {
1395 if (index >= entries.size())
1396 return emitError(fileLoc) <<
"invalid " << entryType <<
" index: " << index;
1399 Entry<T> &entry = entries[index];
1406 EncodingReader reader(entry.data, fileLoc);
1407 LogicalResult parseResult =
1408 entry.hasCustomEncoding
1409 ? parseCustomEntry(entry, reader, entryType, index, depth)
1410 : parseAsmEntry(entry.entry, reader, entryType);
1414 if (!reader.empty())
1415 return reader.emitError(
"unexpected trailing bytes after " + entryType +
1422template <
typename T>
1423LogicalResult AttrTypeReader::parseCustomEntry(Entry<T> &entry,
1424 EncodingReader &reader,
1425 StringRef entryType,
1426 uint64_t index, uint64_t depth) {
1427 DialectReader dialectReader(*
this, stringReader, resourceReader, dialectsMap,
1428 reader, bytecodeVersion, depth);
1432 if constexpr (std::is_same_v<T, Type>) {
1434 for (
const auto &callback :
1437 callback->read(dialectReader, entry.dialect->name, entry.entry)))
1445 reader = EncodingReader(entry.data, reader.getLoc());
1449 for (
const auto &callback :
1452 callback->read(dialectReader, entry.dialect->name, entry.entry)))
1460 reader = EncodingReader(entry.data, reader.getLoc());
1465 if (!entry.dialect->interface) {
1466 return reader.emitError(
"dialect '", entry.dialect->name,
1467 "' does not implement the bytecode interface");
1470 if constexpr (std::is_same_v<T, Type>)
1471 entry.entry = entry.dialect->interface->readType(dialectReader);
1473 entry.entry = entry.dialect->interface->readAttribute(dialectReader);
1475 return success(!!entry.entry);
1478template <
typename T>
1479LogicalResult AttrTypeReader::parseAsmEntry(T &
result, EncodingReader &reader,
1480 StringRef entryType) {
1482 if (
failed(reader.parseNullTerminatedString(asmStr)))
1487 MLIRContext *context = fileLoc->
getContext();
1488 if constexpr (std::is_same_v<T, Type>)
1498 if (numRead != asmStr.size()) {
1499 return reader.emitError(
"trailing characters found after ", entryType,
1500 " assembly format: ", asmStr.drop_front(numRead));
1511 struct RegionReadState;
1512 using LazyLoadableOpsInfo =
1513 std::list<std::pair<Operation *, RegionReadState>>;
1514 using LazyLoadableOpsMap =
1519 llvm::MemoryBufferRef buffer,
1520 const std::shared_ptr<llvm::SourceMgr> &bufferOwnerRef)
1521 : config(config), fileLoc(fileLoc), lazyLoading(lazyLoading),
1522 attrTypeReader(stringReader, resourceReader, dialectsMap, version,
1526 forwardRefOpState(UnknownLoc::
get(config.getContext()),
1527 "builtin.unrealized_conversion_cast",
ValueRange(),
1528 NoneType::
get(config.getContext())),
1529 buffer(buffer), bufferOwnerRef(bufferOwnerRef) {}
1532 LogicalResult read(
Block *block,
1545 this->lazyOpsCallback = lazyOpsCallback;
1546 auto resetlazyOpsCallback =
1547 llvm::make_scope_exit([&] { this->lazyOpsCallback =
nullptr; });
1548 auto it = lazyLoadableOpsMap.find(op);
1549 assert(it != lazyLoadableOpsMap.end() &&
1550 "materialize called on non-materializable op");
1556 while (!lazyLoadableOpsMap.empty()) {
1557 if (failed(
materialize(lazyLoadableOpsMap.begin())))
1568 while (!lazyLoadableOps.empty()) {
1569 Operation *op = lazyLoadableOps.begin()->first;
1570 if (shouldMaterialize(op)) {
1571 if (failed(
materialize(lazyLoadableOpsMap.find(op))))
1577 lazyLoadableOps.pop_front();
1578 lazyLoadableOpsMap.erase(op);
1584 LogicalResult
materialize(LazyLoadableOpsMap::iterator it) {
1585 assert(it != lazyLoadableOpsMap.end() &&
1586 "materialize called on non-materializable op");
1587 valueScopes.emplace_back();
1588 std::vector<RegionReadState> regionStack;
1589 regionStack.push_back(std::move(it->getSecond()->second));
1590 lazyLoadableOps.erase(it->getSecond());
1591 lazyLoadableOpsMap.erase(it);
1593 while (!regionStack.empty())
1594 if (failed(
parseRegions(regionStack, regionStack.back())))
1599 LogicalResult checkSectionAlignment(
1610 const bool isGloballyAligned =
1611 ((uintptr_t)buffer.getBufferStart() & (alignment - 1)) == 0;
1613 if (!isGloballyAligned)
1614 return emitError(
"expected section alignment ")
1615 << alignment <<
" but bytecode buffer 0x"
1616 << Twine::utohexstr((uint64_t)buffer.getBufferStart())
1617 <<
" is not aligned";
1626 LogicalResult parseVersion(EncodingReader &reader);
1631 LogicalResult parseDialectSection(ArrayRef<uint8_t> sectionData);
1636 FailureOr<OperationName> parseOpName(EncodingReader &reader,
1637 std::optional<bool> &wasRegistered);
1643 template <
typename T>
1645 return attrTypeReader.parseAttribute(reader,
result);
1648 return attrTypeReader.parseType(reader,
result);
1655 parseResourceSection(EncodingReader &reader,
1656 std::optional<ArrayRef<uint8_t>> resourceData,
1657 std::optional<ArrayRef<uint8_t>> resourceOffsetData);
1664 struct RegionReadState {
1665 RegionReadState(Operation *op, EncodingReader *reader,
1666 bool isIsolatedFromAbove)
1667 : RegionReadState(op->getRegions(), reader, isIsolatedFromAbove) {}
1668 RegionReadState(MutableArrayRef<Region> regions, EncodingReader *reader,
1669 bool isIsolatedFromAbove)
1670 : curRegion(regions.begin()), endRegion(regions.end()), reader(reader),
1671 isIsolatedFromAbove(isIsolatedFromAbove) {}
1674 MutableArrayRef<Region>::iterator curRegion, endRegion;
1679 EncodingReader *reader;
1680 std::unique_ptr<EncodingReader> owningReader;
1683 unsigned numValues = 0;
1686 SmallVector<Block *> curBlocks;
1691 uint64_t numOpsRemaining = 0;
1694 bool isIsolatedFromAbove =
false;
1697 LogicalResult parseIRSection(ArrayRef<uint8_t> sectionData,
Block *block);
1698 LogicalResult
parseRegions(std::vector<RegionReadState> ®ionStack,
1699 RegionReadState &readState);
1700 FailureOr<Operation *> parseOpWithoutRegions(EncodingReader &reader,
1701 RegionReadState &readState,
1702 bool &isIsolatedFromAbove);
1704 LogicalResult parseRegion(RegionReadState &readState);
1705 LogicalResult parseBlockHeader(EncodingReader &reader,
1706 RegionReadState &readState);
1707 LogicalResult parseBlockArguments(EncodingReader &reader,
Block *block);
1714 Value parseOperand(EncodingReader &reader);
1717 LogicalResult defineValues(EncodingReader &reader,
ValueRange values);
1720 Value createForwardRef();
1728 struct UseListOrderStorage {
1729 UseListOrderStorage(
bool isIndexPairEncoding,
1730 SmallVector<unsigned, 4> &&
indices)
1732 isIndexPairEncoding(isIndexPairEncoding) {};
1735 SmallVector<unsigned, 4>
indices;
1739 bool isIndexPairEncoding;
1747 using UseListMapT = DenseMap<unsigned, UseListOrderStorage>;
1748 FailureOr<UseListMapT> parseUseListOrderForRange(EncodingReader &reader,
1749 uint64_t rangeSize);
1752 LogicalResult sortUseListOrder(Value value);
1756 LogicalResult processUseLists(Operation *topLevelOp);
1766 void push(RegionReadState &readState) {
1767 nextValueIDs.push_back(values.size());
1768 values.resize(values.size() + readState.numValues);
1773 void pop(RegionReadState &readState) {
1774 values.resize(values.size() - readState.numValues);
1775 nextValueIDs.pop_back();
1779 std::vector<Value> values;
1783 SmallVector<unsigned, 4> nextValueIDs;
1787 const ParserConfig &
config;
1798 LazyLoadableOpsInfo lazyLoadableOps;
1799 LazyLoadableOpsMap lazyLoadableOpsMap;
1800 llvm::function_ref<bool(Operation *)> lazyOpsCallback;
1803 AttrTypeReader attrTypeReader;
1806 uint64_t version = 0;
1812 SmallVector<std::unique_ptr<BytecodeDialect>> dialects;
1813 llvm::StringMap<BytecodeDialect *> dialectsMap;
1814 SmallVector<BytecodeOperationName> opNames;
1817 ResourceSectionReader resourceReader;
1821 DenseMap<void *, UseListOrderStorage> valueToUseListMap;
1824 StringSectionReader stringReader;
1827 PropertiesSectionReader propertiesReader;
1830 std::vector<ValueScope> valueScopes;
1837 Block forwardRefOps;
1841 Block openForwardRefOps;
1844 OperationState forwardRefOpState;
1847 llvm::MemoryBufferRef buffer;
1851 const std::shared_ptr<llvm::SourceMgr> &bufferOwnerRef;
1856 EncodingReader reader(buffer.getBuffer(), fileLoc);
1857 this->lazyOpsCallback = lazyOpsCallback;
1858 auto resetlazyOpsCallback =
1859 llvm::make_scope_exit([&] { this->lazyOpsCallback =
nullptr; });
1862 if (failed(reader.skipBytes(StringRef(
"ML\xefR").size())))
1865 if (failed(parseVersion(reader)) ||
1866 failed(reader.parseNullTerminatedString(producer)))
1872 diag.attachNote() <<
"in bytecode version " << version
1873 <<
" produced by: " << producer;
1877 const auto checkSectionAlignment = [&](
unsigned alignment) {
1878 return this->checkSectionAlignment(
1879 alignment, [&](
const auto &msg) {
return reader.emitError(msg); });
1883 std::optional<ArrayRef<uint8_t>>
1885 while (!reader.empty()) {
1890 reader.parseSection(sectionID, checkSectionAlignment, sectionData)))
1894 if (sectionDatas[sectionID]) {
1895 return reader.emitError(
"duplicate top-level section: ",
1898 sectionDatas[sectionID] = sectionData;
1904 return reader.emitError(
"missing data for top-level section: ",
1910 if (failed(stringReader.initialize(
1916 failed(propertiesReader.initialize(
1925 if (failed(parseResourceSection(
1931 if (failed(attrTypeReader.initialize(
1940LogicalResult BytecodeReader::Impl::parseVersion(EncodingReader &reader) {
1941 if (failed(reader.parseVarInt(version)))
1947 if (version < minSupportedVersion) {
1948 return reader.emitError(
"bytecode version ", version,
1949 " is older than the current version of ",
1950 currentVersion,
", and upgrade is not supported");
1952 if (version > currentVersion) {
1953 return reader.emitError(
"bytecode version ", version,
1954 " is newer than the current version ",
1959 lazyLoading =
false;
1967LogicalResult BytecodeDialect::load(
const DialectReader &reader,
1973 return reader.emitError(
"dialect '")
1975 <<
"' is unknown. If this is intended, please call "
1976 "allowUnregisteredDialects() on the MLIRContext, or use "
1977 "-allow-unregistered-dialect with the MLIR tool used.";
1979 dialect = loadedDialect;
1984 interface = dyn_cast<BytecodeDialectInterface>(loadedDialect);
1985 if (!versionBuffer.empty()) {
1987 return reader.emitError(
"dialect '")
1989 <<
"' does not implement the bytecode interface, "
1990 "but found a version entry";
1991 EncodingReader encReader(versionBuffer, reader.getLoc());
1992 DialectReader versionReader = reader.withEncodingReader(encReader);
1993 loadedVersion = interface->readVersion(versionReader);
2001BytecodeReader::Impl::parseDialectSection(ArrayRef<uint8_t> sectionData) {
2002 EncodingReader sectionReader(sectionData, fileLoc);
2005 uint64_t numDialects;
2006 if (
failed(sectionReader.parseVarInt(numDialects)))
2008 dialects.resize(numDialects);
2010 const auto checkSectionAlignment = [&](
unsigned alignment) {
2011 return this->checkSectionAlignment(alignment, [&](
const auto &msg) {
2012 return sectionReader.emitError(msg);
2017 for (uint64_t i = 0; i < numDialects; ++i) {
2018 dialects[i] = std::make_unique<BytecodeDialect>();
2022 if (
failed(stringReader.parseString(sectionReader, dialects[i]->name)))
2028 uint64_t dialectNameIdx;
2029 bool versionAvailable;
2030 if (
failed(sectionReader.parseVarIntWithFlag(dialectNameIdx,
2033 if (
failed(stringReader.parseStringAtIndex(sectionReader, dialectNameIdx,
2034 dialects[i]->name)))
2036 if (versionAvailable) {
2038 if (
failed(sectionReader.parseSection(sectionID, checkSectionAlignment,
2039 dialects[i]->versionBuffer)))
2042 emitError(fileLoc,
"expected dialect version section");
2046 dialectsMap[dialects[i]->name] = dialects[i].get();
2050 auto parseOpName = [&](BytecodeDialect *dialect) {
2052 std::optional<bool> wasRegistered;
2056 if (
failed(stringReader.parseString(sectionReader, opName)))
2059 bool wasRegisteredFlag;
2060 if (
failed(stringReader.parseStringWithFlag(sectionReader, opName,
2061 wasRegisteredFlag)))
2063 wasRegistered = wasRegisteredFlag;
2065 opNames.emplace_back(dialect, opName, wasRegistered);
2072 if (
failed(sectionReader.parseVarInt(numOps)))
2074 opNames.reserve(numOps);
2076 while (!sectionReader.empty())
2082FailureOr<OperationName>
2083BytecodeReader::Impl::parseOpName(EncodingReader &reader,
2084 std::optional<bool> &wasRegistered) {
2085 BytecodeOperationName *opName =
nullptr;
2088 wasRegistered = opName->wasRegistered;
2091 if (!opName->opName) {
2096 if (opName->name.empty()) {
2097 opName->opName.emplace(opName->dialect->name,
getContext());
2100 DialectReader dialectReader(attrTypeReader, stringReader, resourceReader,
2101 dialectsMap, reader, version);
2104 opName->opName.emplace((opName->dialect->name +
"." + opName->name).str(),
2108 return *opName->opName;
2115LogicalResult BytecodeReader::Impl::parseResourceSection(
2116 EncodingReader &reader, std::optional<ArrayRef<uint8_t>> resourceData,
2117 std::optional<ArrayRef<uint8_t>> resourceOffsetData) {
2119 if (resourceData.has_value() != resourceOffsetData.has_value()) {
2120 if (resourceOffsetData)
2121 return emitError(fileLoc,
"unexpected resource offset section when "
2122 "resource section is not present");
2125 "expected resource offset section when resource section is present");
2133 DialectReader dialectReader(attrTypeReader, stringReader, resourceReader,
2134 dialectsMap, reader, version);
2135 return resourceReader.initialize(fileLoc,
config, dialects, stringReader,
2136 *resourceData, *resourceOffsetData,
2137 dialectReader, bufferOwnerRef);
2144FailureOr<BytecodeReader::Impl::UseListMapT>
2145BytecodeReader::Impl::parseUseListOrderForRange(EncodingReader &reader,
2146 uint64_t numResults) {
2147 BytecodeReader::Impl::UseListMapT map;
2148 uint64_t numValuesToRead = 1;
2149 if (numResults > 1 &&
failed(reader.parseVarInt(numValuesToRead)))
2152 for (
size_t valueIdx = 0; valueIdx < numValuesToRead; valueIdx++) {
2153 uint64_t resultIdx = 0;
2154 if (numResults > 1 &&
failed(reader.parseVarInt(resultIdx)))
2158 bool indexPairEncoding;
2159 if (
failed(reader.parseVarIntWithFlag(numValues, indexPairEncoding)))
2162 SmallVector<unsigned, 4> useListOrders;
2163 for (
size_t idx = 0; idx < numValues; idx++) {
2165 if (
failed(reader.parseVarInt(index)))
2167 useListOrders.push_back(index);
2171 map.try_emplace(resultIdx, UseListOrderStorage(indexPairEncoding,
2172 std::move(useListOrders)));
2183LogicalResult BytecodeReader::Impl::sortUseListOrder(Value value) {
2188 bool hasIncomingOrder =
2193 bool alreadySorted =
true;
2197 llvm::SmallVector<std::pair<unsigned, uint64_t>> currentOrder = {{0, prevID}};
2198 for (
auto item : llvm::drop_begin(llvm::enumerate(value.
getUses()))) {
2200 item.value(), operationIDs.at(item.value().getOwner()));
2201 alreadySorted &= prevID > currentID;
2202 currentOrder.push_back({item.index(), currentID});
2208 if (alreadySorted && !hasIncomingOrder)
2215 currentOrder.begin(), currentOrder.end(),
2216 [](
auto elem1,
auto elem2) { return elem1.second > elem2.second; });
2218 if (!hasIncomingOrder) {
2222 SmallVector<unsigned> shuffle(llvm::make_first_range(currentOrder));
2228 UseListOrderStorage customOrder =
2230 SmallVector<unsigned, 4> shuffle = std::move(customOrder.indices);
2236 if (customOrder.isIndexPairEncoding) {
2238 if (shuffle.size() & 1)
2241 SmallVector<unsigned, 4> newShuffle(numUses);
2243 std::iota(newShuffle.begin(), newShuffle.end(), idx);
2244 for (idx = 0; idx < shuffle.size(); idx += 2)
2245 newShuffle[shuffle[idx]] = shuffle[idx + 1];
2247 shuffle = std::move(newShuffle);
2254 uint64_t accumulator = 0;
2255 for (
const auto &elem : shuffle) {
2256 if (!set.insert(elem).second)
2258 accumulator += elem;
2260 if (numUses != shuffle.size() ||
2261 accumulator != (((numUses - 1) * numUses) >> 1))
2266 shuffle = SmallVector<unsigned, 4>(llvm::map_range(
2267 currentOrder, [&](
auto item) {
return shuffle[item.first]; }));
2272LogicalResult BytecodeReader::Impl::processUseLists(Operation *topLevelOp) {
2276 unsigned operationID = 0;
2278 [&](Operation *op) { operationIDs.try_emplace(op, operationID++); });
2280 auto blockWalk = topLevelOp->
walk([
this](
Block *block) {
2282 if (
failed(sortUseListOrder(arg)))
2287 auto resultWalk = topLevelOp->
walk([
this](Operation *op) {
2294 return failure(blockWalk.wasInterrupted() || resultWalk.wasInterrupted());
2302BytecodeReader::Impl::parseIRSection(ArrayRef<uint8_t> sectionData,
2304 EncodingReader reader(sectionData, fileLoc);
2307 std::vector<RegionReadState> regionStack;
2310 OwningOpRef<ModuleOp> moduleOp = ModuleOp::create(fileLoc);
2311 regionStack.emplace_back(*moduleOp, &reader,
true);
2312 regionStack.back().curBlocks.push_back(moduleOp->getBody());
2313 regionStack.back().curBlock = regionStack.back().curRegion->begin();
2314 if (
failed(parseBlockHeader(reader, regionStack.back())))
2316 valueScopes.emplace_back();
2317 valueScopes.back().push(regionStack.back());
2320 while (!regionStack.empty())
2323 if (!forwardRefOps.empty()) {
2324 return reader.emitError(
2325 "not all forward unresolved forward operand references");
2329 if (
failed(processUseLists(*moduleOp)))
2330 return reader.emitError(
2331 "parsed use-list orders were invalid and could not be applied");
2334 for (
const std::unique_ptr<BytecodeDialect> &byteCodeDialect : dialects) {
2337 if (!byteCodeDialect->loadedVersion)
2339 if (byteCodeDialect->interface &&
2340 failed(byteCodeDialect->interface->upgradeFromVersion(
2341 *moduleOp, *byteCodeDialect->loadedVersion)))
2350 auto &parsedOps = moduleOp->getBody()->getOperations();
2352 destOps.splice(destOps.end(), parsedOps, parsedOps.begin(), parsedOps.end());
2357BytecodeReader::Impl::parseRegions(std::vector<RegionReadState> ®ionStack,
2358 RegionReadState &readState) {
2359 const auto checkSectionAlignment = [&](
unsigned alignment) {
2360 return this->checkSectionAlignment(
2361 alignment, [&](
const auto &msg) {
return emitError(fileLoc, msg); });
2367 for (; readState.curRegion != readState.endRegion; ++readState.curRegion) {
2373 if (
failed(parseRegion(readState)))
2377 if (readState.curRegion->empty())
2382 EncodingReader &reader = *readState.reader;
2384 while (readState.numOpsRemaining--) {
2387 bool isIsolatedFromAbove =
false;
2388 FailureOr<Operation *> op =
2389 parseOpWithoutRegions(reader, readState, isIsolatedFromAbove);
2397 if ((*op)->getNumRegions()) {
2398 RegionReadState childState(*op, &reader, isIsolatedFromAbove);
2403 ArrayRef<uint8_t> sectionData;
2404 if (
failed(reader.parseSection(sectionID, checkSectionAlignment,
2408 return emitError(fileLoc,
"expected IR section for region");
2409 childState.owningReader =
2410 std::make_unique<EncodingReader>(sectionData, fileLoc);
2411 childState.reader = childState.owningReader.get();
2415 if (lazyLoading && (!lazyOpsCallback || !lazyOpsCallback(*op))) {
2416 lazyLoadableOps.emplace_back(*op, std::move(childState));
2417 lazyLoadableOpsMap.try_emplace(*op,
2418 std::prev(lazyLoadableOps.end()));
2422 regionStack.push_back(std::move(childState));
2425 if (isIsolatedFromAbove)
2426 valueScopes.emplace_back();
2432 if (++readState.curBlock == readState.curRegion->end())
2434 if (
failed(parseBlockHeader(reader, readState)))
2439 readState.curBlock = {};
2440 valueScopes.back().pop(readState);
2445 if (readState.isIsolatedFromAbove) {
2446 assert(!valueScopes.empty() &&
"Expect a valueScope after reading region");
2447 valueScopes.pop_back();
2449 assert(!regionStack.empty() &&
"Expect a regionStack after reading region");
2450 regionStack.pop_back();
2454FailureOr<Operation *>
2455BytecodeReader::Impl::parseOpWithoutRegions(EncodingReader &reader,
2456 RegionReadState &readState,
2457 bool &isIsolatedFromAbove) {
2459 std::optional<bool> wasRegistered;
2460 FailureOr<OperationName> opName = parseOpName(reader, wasRegistered);
2467 if (
failed(reader.parseByte(opMask)))
2477 OperationState opState(opLoc, *opName);
2481 DictionaryAttr dictAttr;
2492 "Unexpected missing `wasRegistered` opname flag at "
2493 "bytecode version ")
2494 << version <<
" with properties.";
2498 if (wasRegistered) {
2499 DialectReader dialectReader(attrTypeReader, stringReader, resourceReader,
2500 dialectsMap, reader, version);
2502 propertiesReader.read(fileLoc, dialectReader, &*opName, opState)))
2514 uint64_t numResults;
2515 if (
failed(reader.parseVarInt(numResults)))
2517 opState.
types.resize(numResults);
2518 for (
int i = 0, e = numResults; i < e; ++i)
2525 uint64_t numOperands;
2526 if (
failed(reader.parseVarInt(numOperands)))
2528 opState.
operands.resize(numOperands);
2529 for (
int i = 0, e = numOperands; i < e; ++i)
2530 if (!(opState.
operands[i] = parseOperand(reader)))
2537 if (
failed(reader.parseVarInt(numSuccs)))
2540 for (
int i = 0, e = numSuccs; i < e; ++i) {
2549 std::optional<UseListMapT> resultIdxToUseListMap = std::nullopt;
2552 size_t numResults = opState.
types.size();
2553 auto parseResult = parseUseListOrderForRange(reader, numResults);
2556 resultIdxToUseListMap = std::move(*parseResult);
2561 uint64_t numRegions;
2562 if (
failed(reader.parseVarIntWithFlag(numRegions, isIsolatedFromAbove)))
2565 opState.
regions.reserve(numRegions);
2566 for (
int i = 0, e = numRegions; i < e; ++i)
2567 opState.
regions.push_back(std::make_unique<Region>());
2572 readState.curBlock->push_back(op);
2583 if (resultIdxToUseListMap.has_value()) {
2585 if (resultIdxToUseListMap->contains(idx)) {
2587 resultIdxToUseListMap->at(idx));
2594LogicalResult BytecodeReader::Impl::parseRegion(RegionReadState &readState) {
2595 EncodingReader &reader = *readState.reader;
2599 if (
failed(reader.parseVarInt(numBlocks)))
2608 if (
failed(reader.parseVarInt(numValues)))
2610 readState.numValues = numValues;
2614 readState.curBlocks.clear();
2615 readState.curBlocks.reserve(numBlocks);
2616 for (uint64_t i = 0; i < numBlocks; ++i) {
2617 readState.curBlocks.push_back(
new Block());
2618 readState.curRegion->push_back(readState.curBlocks.back());
2622 valueScopes.back().push(readState);
2625 readState.curBlock = readState.curRegion->begin();
2626 return parseBlockHeader(reader, readState);
2630BytecodeReader::Impl::parseBlockHeader(EncodingReader &reader,
2631 RegionReadState &readState) {
2633 if (
failed(reader.parseVarIntWithFlag(readState.numOpsRemaining, hasArgs)))
2637 if (hasArgs &&
failed(parseBlockArguments(reader, &*readState.curBlock)))
2644 uint8_t hasUseListOrders = 0;
2645 if (hasArgs &&
failed(reader.parseByte(hasUseListOrders)))
2648 if (!hasUseListOrders)
2651 Block &blk = *readState.curBlock;
2652 auto argIdxToUseListMap =
2654 if (
failed(argIdxToUseListMap) || argIdxToUseListMap->empty())
2658 if (argIdxToUseListMap->contains(idx))
2660 argIdxToUseListMap->at(idx));
2666LogicalResult BytecodeReader::Impl::parseBlockArguments(EncodingReader &reader,
2670 if (
failed(reader.parseVarInt(numArgs)))
2673 SmallVector<Type> argTypes;
2674 SmallVector<Location> argLocs;
2675 argTypes.reserve(numArgs);
2676 argLocs.reserve(numArgs);
2678 Location unknownLoc = UnknownLoc::get(
config.getContext());
2681 LocationAttr argLoc = unknownLoc;
2686 if (
failed(reader.parseVarIntWithFlag(typeIdx, hasLoc)) ||
2687 !(argType = attrTypeReader.resolveType(typeIdx)))
2697 argTypes.push_back(argType);
2698 argLocs.push_back(argLoc);
2708Value BytecodeReader::Impl::parseOperand(EncodingReader &reader) {
2709 std::vector<Value> &values = valueScopes.back().values;
2710 Value *value =
nullptr;
2716 *value = createForwardRef();
2720LogicalResult BytecodeReader::Impl::defineValues(EncodingReader &reader,
2722 ValueScope &valueScope = valueScopes.back();
2723 std::vector<Value> &values = valueScope.values;
2725 unsigned &valueID = valueScope.nextValueIDs.back();
2726 unsigned valueIDEnd = valueID + newValues.size();
2727 if (valueIDEnd > values.size()) {
2728 return reader.emitError(
2729 "value index range was outside of the expected range for "
2730 "the parent region, got [",
2731 valueID,
", ", valueIDEnd,
"), but the maximum index was ",
2736 for (
unsigned i = 0, e = newValues.size(); i != e; ++i, ++valueID) {
2737 Value newValue = newValues[i];
2740 if (Value oldValue = std::exchange(values[valueID], newValue)) {
2741 Operation *forwardRefOp = oldValue.getDefiningOp();
2746 assert(forwardRefOp && forwardRefOp->
getBlock() == &forwardRefOps &&
2747 "value index was already defined?");
2749 oldValue.replaceAllUsesWith(newValue);
2750 forwardRefOp->
moveBefore(&openForwardRefOps, openForwardRefOps.end());
2756Value BytecodeReader::Impl::createForwardRef() {
2759 if (!openForwardRefOps.empty()) {
2760 Operation *op = &openForwardRefOps.back();
2761 op->
moveBefore(&forwardRefOps, forwardRefOps.end());
2765 return forwardRefOps.back().getResult(0);
2776 const std::shared_ptr<llvm::SourceMgr> &bufferOwnerRef) {
2780 impl = std::make_unique<Impl>(sourceFileLoc,
config, lazyLoading, buffer,
2786 return impl->read(block, lazyOpsCallback);
2790 return impl->getNumOpsToMaterialize();
2794 return impl->isMaterializable(op);
2799 return impl->materialize(op, lazyOpsCallback);
2804 return impl->finalize(shouldMaterialize);
2808 return buffer.getBuffer().starts_with(
"ML\xefR");
2817 const std::shared_ptr<llvm::SourceMgr> &bufferOwnerRef) {
2823 "input buffer is not an MLIR bytecode file");
2827 buffer, bufferOwnerRef);
2828 return reader.
read(block,
nullptr);
2839 *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)
static std::string diag(const llvm::Value &value)
static ParseResult parseRegions(OpAsmParser &parser, OperationState &state, unsigned nRegions=1)
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, OpaqueProperties 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.
BytecodeReaderConfig & getBytecodeReaderConfig() const
Returns the parsing configurations associated to the bytecode read.
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)
static LogicalResult readResourceHandle(DialectBytecodeReader &reader, FailureOr< T > &value, Ts &&...params)
Helper for resource handle reading that returns LogicalResult.
bool isBytecode(llvm::MemoryBufferRef buffer)
Returns true if the given buffer starts with the magic bytes that signal MLIR bytecode.
const FrozenRewritePatternSet GreedyRewriteConfig config
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
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.