17 #include "llvm/ADT/ArrayRef.h"
18 #include "llvm/ADT/CachedHashString.h"
19 #include "llvm/ADT/MapVector.h"
20 #include "llvm/ADT/SmallVector.h"
21 #include "llvm/Support/Debug.h"
22 #include "llvm/Support/Endian.h"
23 #include "llvm/Support/raw_ostream.h"
26 #define DEBUG_TYPE "mlir-bytecode-writer"
36 Impl(StringRef producer) : producer(producer) {}
44 bool shouldElideResourceData =
false;
63 :
impl(std::make_unique<
Impl>(producer)) {}
76 return impl->attributeWriterCallbacks;
81 return impl->typeWriterCallbacks;
86 impl->attributeWriterCallbacks.emplace_back(std::move(callback));
91 impl->typeWriterCallbacks.emplace_back(std::move(callback));
95 std::unique_ptr<AsmResourcePrinter> printer) {
96 impl->externalResourcePrinters.emplace_back(std::move(printer));
100 bool shouldElideResourceData) {
101 impl->shouldElideResourceData = shouldElideResourceData;
105 impl->bytecodeVersion = bytecodeVersion;
109 return impl->bytecodeVersion;
112 llvm::StringMap<std::unique_ptr<DialectVersion>> &
114 return impl->dialectVersionMap;
118 llvm::StringRef dialectName,
119 std::unique_ptr<DialectVersion> dialectVersion)
const {
120 assert(!
impl->dialectVersionMap.contains(dialectName) &&
121 "cannot override a previously set dialect version");
122 impl->dialectVersionMap.insert({dialectName, std::move(dialectVersion)});
135 class EncodingEmitter {
137 EncodingEmitter() =
default;
138 EncodingEmitter(
const EncodingEmitter &) =
delete;
139 EncodingEmitter &operator=(
const EncodingEmitter &) =
delete;
142 void writeTo(raw_ostream &os)
const;
145 size_t size()
const {
return prevResultSize + currentResult.size(); }
152 void patchByte(uint64_t offset, uint8_t value, StringLiteral desc) {
153 LLVM_DEBUG(llvm::dbgs() <<
"patchByte(" << offset <<
',' << uint64_t(value)
154 <<
")\t" << desc <<
'\n');
155 assert(offset < size() && offset >= prevResultSize &&
156 "cannot patch previously emitted data");
157 currentResult[offset - prevResultSize] = value;
163 LLVM_DEBUG(llvm::dbgs()
164 <<
"emitOwnedBlob(" << data.size() <<
"b)\t" << desc <<
'\n');
166 appendResult(std::move(currentResult));
167 appendOwnedResult(data);
175 StringLiteral desc) {
176 emitVarInt(alignment, desc);
177 emitVarInt(data.size(), desc);
180 emitOwnedBlob(data, desc);
182 void emitOwnedBlobAndAlignment(
ArrayRef<char> data, uint32_t alignment,
183 StringLiteral desc) {
186 emitOwnedBlobAndAlignment(castedData, alignment, desc);
190 void alignTo(
unsigned alignment) {
193 assert(llvm::isPowerOf2_32(alignment) &&
"expected valid alignment");
197 size_t curOffset = size();
198 size_t paddingSize = llvm::alignTo(curOffset, alignment) - curOffset;
199 while (paddingSize--)
203 requiredAlignment =
std::max(requiredAlignment, alignment);
210 template <
typename T>
211 void emitByte(T
byte, StringLiteral desc) {
212 LLVM_DEBUG(llvm::dbgs()
213 <<
"emitByte(" << uint64_t(
byte) <<
")\t" << desc <<
'\n');
214 currentResult.push_back(
static_cast<uint8_t
>(
byte));
219 LLVM_DEBUG(llvm::dbgs()
220 <<
"emitBytes(" << bytes.size() <<
"b)\t" << desc <<
'\n');
221 llvm::append_range(currentResult, bytes);
231 void emitVarInt(uint64_t value, StringLiteral desc) {
232 LLVM_DEBUG(llvm::dbgs() <<
"emitVarInt(" << value <<
")\t" << desc <<
'\n');
236 if ((value >> 7) == 0)
237 return emitByte((value << 1) | 0x1, desc);
238 emitMultiByteVarInt(value, desc);
245 void emitSignedVarInt(uint64_t value, StringLiteral desc) {
246 emitVarInt((value << 1) ^ (uint64_t)((int64_t)value >> 63), desc);
251 void emitVarIntWithFlag(uint64_t value,
bool flag, StringLiteral desc) {
252 emitVarInt((value << 1) | (flag ? 1 : 0), desc);
259 void emitNulTerminatedString(StringRef str, StringLiteral desc) {
260 emitString(str, desc);
261 emitByte(0,
"null terminator");
265 void emitString(StringRef str, StringLiteral desc) {
266 emitBytes({
reinterpret_cast<const uint8_t *
>(str.data()), str.size()},
279 uint64_t codeOffset = currentResult.size();
280 emitByte(code,
"section code");
281 emitVarInt(emitter.size(),
"section size");
284 unsigned emitterAlign = emitter.requiredAlignment;
285 if (emitterAlign > 1) {
286 if (size() & (emitterAlign - 1)) {
287 emitVarInt(emitterAlign,
"section alignment");
288 alignTo(emitterAlign);
292 currentResult[codeOffset] |= 0b10000000;
296 requiredAlignment =
std::max(requiredAlignment, emitterAlign);
302 appendResult(std::move(currentResult));
303 for (std::vector<uint8_t> &result : emitter.prevResultStorage)
304 prevResultStorage.push_back(std::move(result));
305 llvm::append_range(prevResultList, emitter.prevResultList);
306 prevResultSize += emitter.prevResultSize;
307 appendResult(std::move(emitter.currentResult));
315 LLVM_ATTRIBUTE_NOINLINE
void emitMultiByteVarInt(uint64_t value,
319 void appendResult(std::vector<uint8_t> &&result) {
322 prevResultStorage.emplace_back(std::move(result));
323 appendOwnedResult(prevResultStorage.back());
328 prevResultSize += result.size();
329 prevResultList.emplace_back(result);
337 std::vector<uint8_t> currentResult;
338 std::vector<ArrayRef<uint8_t>> prevResultList;
339 std::vector<std::vector<uint8_t>> prevResultStorage;
343 size_t prevResultSize = 0;
346 unsigned requiredAlignment = 1;
355 class StringSectionBuilder {
359 size_t insert(StringRef str) {
360 auto it = strings.insert({llvm::CachedHashStringRef(str), strings.size()});
361 return it.first->second;
365 void write(EncodingEmitter &emitter) {
366 emitter.emitVarInt(strings.size(),
"string section size");
370 for (
const auto &it : llvm::reverse(strings))
371 emitter.emitVarInt(it.first.size() + 1,
"string size");
373 for (
const auto &it : strings)
374 emitter.emitNulTerminatedString(it.first.val(),
"string");
380 llvm::MapVector<llvm::CachedHashStringRef, size_t> strings;
385 using DialectVersionMapT = llvm::StringMap<std::unique_ptr<DialectVersion>>;
388 DialectWriter(int64_t bytecodeVersion, EncodingEmitter &emitter,
390 StringSectionBuilder &stringSection,
391 const DialectVersionMapT &dialectVersionMap)
392 : bytecodeVersion(bytecodeVersion), emitter(emitter),
393 numberingState(numberingState), stringSection(stringSection),
394 dialectVersionMap(dialectVersionMap) {}
400 void writeAttribute(
Attribute attr)
override {
401 emitter.emitVarInt(numberingState.getNumber(attr),
"dialect attr");
403 void writeOptionalAttribute(
Attribute attr)
override {
405 emitter.emitVarInt(0,
"dialect optional attr none");
408 emitter.emitVarIntWithFlag(numberingState.getNumber(attr),
true,
409 "dialect optional attr");
412 void writeType(
Type type)
override {
413 emitter.emitVarInt(numberingState.getNumber(type),
"dialect type");
417 emitter.emitVarInt(numberingState.getNumber(resource),
"dialect resource");
424 void writeVarInt(uint64_t value)
override {
425 emitter.emitVarInt(value,
"dialect writer");
428 void writeSignedVarInt(int64_t value)
override {
429 emitter.emitSignedVarInt(value,
"dialect writer");
432 void writeAPIntWithKnownWidth(
const APInt &value)
override {
433 size_t bitWidth = value.getBitWidth();
438 return emitter.emitByte(value.getLimitedValue(),
"dialect APInt");
442 return emitter.emitSignedVarInt(value.getLimitedValue(),
"dialect APInt");
447 unsigned numActiveWords = value.getActiveWords();
448 emitter.emitVarInt(numActiveWords,
"dialect APInt word count");
450 const uint64_t *rawValueData = value.getRawData();
451 for (
unsigned i = 0; i < numActiveWords; ++i)
452 emitter.emitSignedVarInt(rawValueData[i],
"dialect APInt word");
455 void writeAPFloatWithKnownSemantics(
const APFloat &value)
override {
456 writeAPIntWithKnownWidth(value.bitcastToAPInt());
459 void writeOwnedString(StringRef str)
override {
460 emitter.emitVarInt(stringSection.insert(str),
"dialect string");
464 emitter.emitVarInt(blob.size(),
"dialect blob");
465 emitter.emitOwnedBlob(
471 void writeOwnedBool(
bool value)
override {
472 emitter.emitByte(value,
"dialect bool");
475 int64_t getBytecodeVersion()
const override {
return bytecodeVersion; }
477 FailureOr<const DialectVersion *>
478 getDialectVersion(StringRef dialectName)
const override {
479 auto dialectEntry = dialectVersionMap.find(dialectName);
480 if (dialectEntry == dialectVersionMap.end())
482 return dialectEntry->getValue().get();
486 int64_t bytecodeVersion;
487 EncodingEmitter &emitter;
489 StringSectionBuilder &stringSection;
490 const DialectVersionMapT &dialectVersionMap;
494 class PropertiesSectionBuilder {
497 StringSectionBuilder &stringSection,
499 : numberingState(numberingState), stringSection(stringSection),
504 std::optional<ssize_t> emit(
Operation *op) {
505 EncodingEmitter propertiesEmitter;
513 EncodingEmitter sizeEmitter;
514 sizeEmitter.emitVarInt(numberingState.getNumber(prop),
"properties size");
516 llvm::raw_svector_ostream os(scratch);
517 sizeEmitter.writeTo(os);
518 return emit(scratch);
521 EncodingEmitter emitter;
522 DialectWriter propertiesWriter(
config.bytecodeVersion, emitter,
523 numberingState, stringSection,
524 config.dialectVersionMap);
525 auto iface = cast<BytecodeOpInterface>(op);
526 iface.writeProperties(propertiesWriter);
528 llvm::raw_svector_ostream os(scratch);
530 return emit(scratch);
534 void write(EncodingEmitter &emitter) {
535 emitter.emitVarInt(propertiesStorage.size(),
"properties size");
536 if (propertiesStorage.empty())
538 for (
const auto &storage : propertiesStorage) {
539 if (storage.empty()) {
543 emitter.emitBytes(
ArrayRef(
reinterpret_cast<const uint8_t *
>(&storage[0]),
550 bool empty() {
return propertiesStorage.empty(); }
560 EncodingEmitter sizeEmitter;
561 sizeEmitter.emitVarInt(rawProperties.size(),
"properties");
562 llvm::raw_svector_ostream os(sizeScratch);
563 sizeEmitter.writeTo(os);
566 size_t index = propertiesStorage.size();
567 propertiesStorage.emplace_back();
568 std::vector<char> &newStorage = propertiesStorage.back();
569 size_t propertiesSize = sizeScratch.size() + rawProperties.size();
570 newStorage.reserve(propertiesSize);
571 newStorage.insert(newStorage.end(), sizeScratch.begin(), sizeScratch.end());
572 newStorage.insert(newStorage.end(), rawProperties.begin(),
573 rawProperties.end());
577 auto inserted = propertiesUniquing.insert(
579 if (!inserted.second)
580 propertiesStorage.pop_back();
581 return inserted.first->getSecond();
585 std::vector<std::vector<char>> propertiesStorage;
589 StringSectionBuilder &stringSection;
597 class RawEmitterOstream :
public raw_ostream {
599 explicit RawEmitterOstream(EncodingEmitter &emitter) : emitter(emitter) {
604 void write_impl(
const char *ptr,
size_t size)
override {
605 emitter.emitBytes({
reinterpret_cast<const uint8_t *
>(ptr), size},
608 uint64_t current_pos()
const override {
return emitter.size(); }
611 EncodingEmitter &emitter;
615 void EncodingEmitter::writeTo(raw_ostream &os)
const {
617 os.reserveExtraSpace(size());
619 for (
auto &prevResult : prevResultList)
620 os.write((
const char *)prevResult.data(), prevResult.size());
621 os.write((
const char *)currentResult.data(), currentResult.size());
624 void EncodingEmitter::emitMultiByteVarInt(uint64_t value, StringLiteral desc) {
628 uint64_t it = value >> 7;
629 for (
size_t numBytes = 2; numBytes < 9; ++numBytes) {
630 if (LLVM_LIKELY(it >>= 7) == 0) {
631 uint64_t encodedValue = (value << 1) | 0x1;
632 encodedValue <<= (numBytes - 1);
633 llvm::support::ulittle64_t encodedValueLE(encodedValue);
634 emitBytes({
reinterpret_cast<uint8_t *
>(&encodedValueLE), numBytes}, desc);
642 llvm::support::ulittle64_t valueLE(value);
643 emitBytes({
reinterpret_cast<uint8_t *
>(&valueLE),
sizeof(valueLE)}, desc);
651 class BytecodeWriter {
655 propertiesSection(numberingState, stringSection,
config.getImpl()) {}
658 LogicalResult write(
Operation *rootOp, raw_ostream &os);
664 void writeDialectSection(EncodingEmitter &emitter);
669 void writeAttrTypeSection(EncodingEmitter &emitter);
674 LogicalResult writeBlock(EncodingEmitter &emitter,
Block *block);
675 LogicalResult writeOp(EncodingEmitter &emitter,
Operation *op);
676 LogicalResult writeRegion(EncodingEmitter &emitter,
Region *region);
677 LogicalResult writeIRSection(EncodingEmitter &emitter,
Operation *op);
679 LogicalResult writeRegions(EncodingEmitter &emitter,
681 return success(llvm::all_of(regions, [&](
Region ®ion) {
682 return succeeded(writeRegion(emitter, ®ion));
689 void writeResourceSection(
Operation *op, EncodingEmitter &emitter);
694 void writeStringSection(EncodingEmitter &emitter);
699 void writePropertiesSection(EncodingEmitter &emitter);
704 void writeUseListOrders(EncodingEmitter &emitter, uint8_t &opEncodingMask,
711 StringSectionBuilder stringSection;
720 PropertiesSectionBuilder propertiesSection;
724 LogicalResult BytecodeWriter::write(
Operation *rootOp, raw_ostream &os) {
725 EncodingEmitter emitter;
729 emitter.emitString(
"ML\xefR",
"bytecode header");
735 <<
"unsupported version requested " <<
config.bytecodeVersion
736 <<
", must be in range ["
739 emitter.emitVarInt(
config.bytecodeVersion,
"bytecode version");
742 emitter.emitNulTerminatedString(
config.producer,
"bytecode producer");
745 writeDialectSection(emitter);
748 writeAttrTypeSection(emitter);
751 if (failed(writeIRSection(emitter, rootOp)))
755 writeResourceSection(rootOp, emitter);
758 writeStringSection(emitter);
762 writePropertiesSection(emitter);
763 else if (!propertiesSection.empty())
765 "unexpected properties emitted incompatible with bytecode <5");
780 template <
typename EntriesT,
typename EntryCallbackT>
782 EntryCallbackT &&callback) {
783 for (
auto it = entries.begin(), e = entries.end(); it != e;) {
784 auto groupStart = it++;
788 it = std::find_if(it, e, [&](
const auto &entry) {
789 return entry.dialect != currentDialect;
793 emitter.emitVarInt(currentDialect->
number,
"dialect number");
794 emitter.emitVarInt(std::distance(groupStart, it),
"dialect offset");
797 for (
auto &entry : llvm::make_range(groupStart, it))
802 void BytecodeWriter::writeDialectSection(EncodingEmitter &emitter) {
803 EncodingEmitter dialectEmitter;
806 auto dialects = numberingState.getDialects();
807 dialectEmitter.emitVarInt(llvm::size(dialects),
"dialects count");
810 size_t nameID = stringSection.insert(dialect.name);
813 dialectEmitter.emitVarInt(nameID,
"dialect name ID");
818 EncodingEmitter versionEmitter;
819 if (dialect.interface) {
821 DialectWriter versionWriter(
config.bytecodeVersion, versionEmitter,
822 numberingState, stringSection,
823 config.dialectVersionMap);
824 dialect.interface->writeVersion(versionWriter);
830 size_t versionAvailable = versionEmitter.size() > 0;
831 dialectEmitter.emitVarIntWithFlag(nameID, versionAvailable,
833 if (versionAvailable)
835 std::move(versionEmitter));
839 dialectEmitter.emitVarInt(size(numberingState.getOpNames()),
844 size_t stringId = stringSection.insert(name.name.stripDialect());
846 dialectEmitter.emitVarInt(stringId,
"dialect op name");
848 dialectEmitter.emitVarIntWithFlag(stringId, name.name.isRegistered(),
859 void BytecodeWriter::writeAttrTypeSection(EncodingEmitter &emitter) {
860 EncodingEmitter attrTypeEmitter;
861 EncodingEmitter offsetEmitter;
862 offsetEmitter.emitVarInt(llvm::size(numberingState.getAttributes()),
864 offsetEmitter.emitVarInt(llvm::size(numberingState.getTypes()),
868 uint64_t prevOffset = 0;
869 auto emitAttrOrType = [&](
auto &entry) {
870 auto entryValue = entry.getValue();
872 auto emitAttrOrTypeRawImpl = [&]() ->
void {
873 RawEmitterOstream(attrTypeEmitter) << entryValue;
874 attrTypeEmitter.emitByte(0,
"attr/type separator");
876 auto emitAttrOrTypeImpl = [&]() ->
bool {
879 if (entryValue.template hasTrait<TypeTrait::IsMutable>() ||
880 entryValue.template hasTrait<AttributeTrait::IsMutable>()) {
881 emitAttrOrTypeRawImpl();
885 DialectWriter dialectWriter(
config.bytecodeVersion, attrTypeEmitter,
886 numberingState, stringSection,
887 config.dialectVersionMap);
888 if constexpr (std::is_same_v<std::decay_t<decltype(entryValue)>,
Type>) {
889 for (
const auto &callback :
config.typeWriterCallbacks) {
890 if (succeeded(callback->write(entryValue, dialectWriter)))
894 entry.dialect->interface) {
895 if (succeeded(interface->writeType(entryValue, dialectWriter)))
899 for (
const auto &callback :
config.attributeWriterCallbacks) {
900 if (succeeded(callback->write(entryValue, dialectWriter)))
904 entry.dialect->interface) {
905 if (succeeded(interface->writeAttribute(entryValue, dialectWriter)))
912 emitAttrOrTypeRawImpl();
916 bool hasCustomEncoding = emitAttrOrTypeImpl();
919 uint64_t curOffset = attrTypeEmitter.size();
920 offsetEmitter.emitVarIntWithFlag(curOffset - prevOffset, hasCustomEncoding,
922 prevOffset = curOffset;
933 std::move(offsetEmitter));
940 LogicalResult BytecodeWriter::writeBlock(EncodingEmitter &emitter,
943 bool hasArgs = !args.empty();
948 unsigned numOps = numberingState.getOperationCount(block);
949 emitter.emitVarIntWithFlag(numOps, hasArgs,
"block num ops");
953 emitter.emitVarInt(args.size(),
"block args count");
957 emitter.emitVarIntWithFlag(numberingState.getNumber(arg.getType()),
958 !isa<UnknownLoc>(argLoc),
"block arg type");
959 if (!isa<UnknownLoc>(argLoc))
960 emitter.emitVarInt(numberingState.getNumber(argLoc),
961 "block arg location");
963 emitter.emitVarInt(numberingState.getNumber(arg.getType()),
965 emitter.emitVarInt(numberingState.getNumber(argLoc),
966 "block arg location");
970 uint64_t maskOffset = emitter.size();
971 uint8_t encodingMask = 0;
972 emitter.emitByte(0,
"use-list separator");
973 writeUseListOrders(emitter, encodingMask, args);
975 emitter.patchByte(maskOffset, encodingMask,
"block patch encoding");
981 if (failed(writeOp(emitter, &op)))
986 LogicalResult BytecodeWriter::writeOp(EncodingEmitter &emitter,
Operation *op) {
987 emitter.emitVarInt(numberingState.getNumber(op->
getName()),
"op name ID");
992 uint64_t maskOffset = emitter.size();
993 uint8_t opEncodingMask = 0;
994 emitter.emitByte(0,
"op separator");
997 emitter.emitVarInt(numberingState.getNumber(op->
getLoc()),
"op location");
1009 if (!attrs.empty()) {
1011 emitter.emitVarInt(numberingState.getNumber(attrs),
"op attrs count");
1017 std::optional<ssize_t> propertiesId = propertiesSection.emit(op);
1018 if (propertiesId.has_value()) {
1020 emitter.emitVarInt(*propertiesId,
"op properties ID");
1027 emitter.emitVarInt(numResults,
"op results count");
1029 emitter.emitVarInt(numberingState.getNumber(type),
"op result type");
1035 emitter.emitVarInt(numOperands,
"op operands count");
1037 emitter.emitVarInt(numberingState.getNumber(operand),
"op operand types");
1043 emitter.emitVarInt(numSuccessors,
"op successors count");
1045 emitter.emitVarInt(numberingState.getNumber(successor),
"op successor");
1059 emitter.patchByte(maskOffset, opEncodingMask,
"op encoding mask");
1066 bool isIsolatedFromAbove = numberingState.isIsolatedFromAbove(op);
1067 emitter.emitVarIntWithFlag(numRegions, isIsolatedFromAbove,
1068 "op regions count");
1072 if (isIsolatedFromAbove &&
1074 EncodingEmitter regionEmitter;
1075 if (failed(writeRegions(regionEmitter, op->
getRegions())))
1079 }
else if (failed(writeRegions(emitter, op->
getRegions()))) {
1086 void BytecodeWriter::writeUseListOrders(EncodingEmitter &emitter,
1087 uint8_t &opEncodingMask,
1092 auto value = item.value();
1095 if (value.use_empty() || value.hasOneUse())
1101 bool alreadyOrdered =
true;
1102 auto &firstUse = *value.use_begin();
1104 firstUse, numberingState.getNumber(firstUse.getOwner()));
1110 use.value(), numberingState.getNumber(use.value().getOwner()));
1114 alreadyOrdered &= (prevID > currentID);
1115 useListPairs.push_back({use.index(), currentID});
1125 useListPairs.begin(), useListPairs.end(),
1126 [](
auto elem1,
auto elem2) { return elem1.second > elem2.second; });
1128 map.try_emplace(item.index(), llvm::map_range(useListPairs, [](
auto elem) {
1139 if (range.size() != 1) {
1140 emitter.emitVarInt(map.size(),
"custom use-list size");
1143 for (
const auto &item : map) {
1144 auto resultIdx = item.getFirst();
1145 auto useListOrder = item.getSecond();
1150 size_t shuffledElements =
1152 [](
auto item) {
return item.index() != item.value(); });
1153 bool indexPairEncoding = shuffledElements < (useListOrder.size() / 2);
1156 if (range.size() != 1)
1157 emitter.emitVarInt(resultIdx,
"use-list result index");
1159 if (indexPairEncoding) {
1160 emitter.emitVarIntWithFlag(shuffledElements * 2, indexPairEncoding,
1161 "use-list index pair size");
1163 if (pair.index() != pair.value()) {
1164 emitter.emitVarInt(pair.value(),
"use-list index pair first");
1165 emitter.emitVarInt(pair.index(),
"use-list index pair second");
1169 emitter.emitVarIntWithFlag(useListOrder.size(), indexPairEncoding,
1171 for (
const auto &index : useListOrder)
1172 emitter.emitVarInt(index,
"use-list order");
1177 LogicalResult BytecodeWriter::writeRegion(EncodingEmitter &emitter,
1181 if (region->
empty()) {
1182 emitter.emitVarInt( 0,
"region block count empty");
1187 unsigned numBlocks, numValues;
1188 std::tie(numBlocks, numValues) = numberingState.getBlockValueCount(region);
1189 emitter.emitVarInt(numBlocks,
"region block count");
1190 emitter.emitVarInt(numValues,
"region value count");
1193 for (
Block &block : *region)
1194 if (failed(writeBlock(emitter, &block)))
1199 LogicalResult BytecodeWriter::writeIRSection(EncodingEmitter &emitter,
1201 EncodingEmitter irEmitter;
1206 irEmitter.emitVarIntWithFlag( 1,
false,
"ir section");
1209 if (failed(writeOp(irEmitter, op)))
1226 ResourceBuilder(EncodingEmitter &emitter, StringSectionBuilder &stringSection,
1227 PostProcessFn postProcessFn,
bool shouldElideData)
1228 : emitter(emitter), stringSection(stringSection),
1229 postProcessFn(postProcessFn), shouldElideData(shouldElideData) {}
1230 ~ResourceBuilder()
override =
default;
1233 uint32_t dataAlignment)
final {
1234 if (!shouldElideData)
1235 emitter.emitOwnedBlobAndAlignment(data, dataAlignment,
"resource blob");
1238 void buildBool(StringRef key,
bool data)
final {
1239 if (!shouldElideData)
1240 emitter.emitByte(data,
"resource bool");
1243 void buildString(StringRef key, StringRef data)
final {
1244 if (!shouldElideData)
1245 emitter.emitVarInt(stringSection.insert(data),
"resource string");
1250 EncodingEmitter &emitter;
1251 StringSectionBuilder &stringSection;
1252 PostProcessFn postProcessFn;
1253 bool shouldElideData =
false;
1257 void BytecodeWriter::writeResourceSection(
Operation *op,
1258 EncodingEmitter &emitter) {
1259 EncodingEmitter resourceEmitter;
1260 EncodingEmitter resourceOffsetEmitter;
1261 uint64_t prevOffset = 0;
1268 uint64_t curOffset = resourceEmitter.size();
1269 curResourceEntries.emplace_back(key,
kind, curOffset - prevOffset);
1270 prevOffset = curOffset;
1274 auto emitResourceGroup = [&](uint64_t key) {
1275 resourceOffsetEmitter.emitVarInt(key,
"resource group key");
1276 resourceOffsetEmitter.emitVarInt(curResourceEntries.size(),
1277 "resource group size");
1278 for (
auto [key,
kind, size] : curResourceEntries) {
1279 resourceOffsetEmitter.emitVarInt(stringSection.insert(key),
1281 resourceOffsetEmitter.emitVarInt(size,
"resource size");
1282 resourceOffsetEmitter.emitByte(
kind,
"resource kind");
1287 ResourceBuilder entryBuilder(resourceEmitter, stringSection,
1288 appendResourceOffset,
1289 config.shouldElideResourceData);
1292 resourceOffsetEmitter.emitVarInt(
config.externalResourcePrinters.size(),
1293 "external resource printer count");
1294 for (
const auto &printer :
config.externalResourcePrinters) {
1295 curResourceEntries.clear();
1296 printer->buildResources(op, entryBuilder);
1297 emitResourceGroup(stringSection.insert(printer->getName()));
1302 if (!dialect.asmInterface)
1304 curResourceEntries.clear();
1305 dialect.asmInterface->buildResources(op, dialect.resources, entryBuilder);
1310 for (
const auto &resource : dialect.resourceMap)
1311 if (resource.second->isDeclaration)
1315 if (!curResourceEntries.empty())
1316 emitResourceGroup(dialect.number);
1320 if (resourceOffsetEmitter.size() == 0)
1324 std::move(resourceOffsetEmitter));
1331 void BytecodeWriter::writeStringSection(EncodingEmitter &emitter) {
1332 EncodingEmitter stringEmitter;
1333 stringSection.write(stringEmitter);
1340 void BytecodeWriter::writePropertiesSection(EncodingEmitter &emitter) {
1341 EncodingEmitter propertiesEmitter;
1342 propertiesSection.write(propertiesEmitter);
1344 std::move(propertiesEmitter));
1353 BytecodeWriter writer(op,
config);
1354 return writer.write(op, os);
static void writeDialectGrouping(EncodingEmitter &emitter, EntriesT &&entries, EntryCallbackT &&callback)
Write the given entries in contiguous groups with the same parent dialect.
union mlir::linalg::@1179::ArityGroupAndKind::Kind kind
static Value max(ImplicitLocOpBuilder &builder, Value value, Value bound)
This class represents an opaque handle to a dialect resource entry.
This class is used to build resource entries for use by the printer.
A class to interact with the attributes and types printer when emitting MLIR bytecode.
Attributes are known-constant values of operations.
This class represents an argument of a Block.
Block represents an ordered list of Operations.
BlockArgListType getArguments()
This class contains the configuration used for the bytecode writer.
void attachTypeCallback(std::unique_ptr< AttrTypeBytecodeWriter< Type >> callback)
llvm::StringMap< std::unique_ptr< DialectVersion > > & getDialectVersionMap() const
A map containing the dialect versions to emit.
void setElideResourceDataFlag(bool shouldElideResourceData=true)
Set a boolean flag to skip emission of resources into the bytecode file.
BytecodeWriterConfig(StringRef producer="MLIR" LLVM_VERSION_STRING)
producer is an optional string that can be used to identify the producer of the bytecode when reading...
void attachFallbackResourcePrinter(FallbackAsmResourceMap &map)
Attach resource printers to the AsmState for the fallback resources in the given map.
int64_t getDesiredBytecodeVersion() const
Get the set desired bytecode version to emit.
void setDialectVersion(std::unique_ptr< DialectVersion > dialectVersion) const
Set a given dialect version to emit on the map.
ArrayRef< std::unique_ptr< AttrTypeBytecodeWriter< Type > > > getTypeWriterCallbacks() const
ArrayRef< std::unique_ptr< AttrTypeBytecodeWriter< Attribute > > > getAttributeWriterCallbacks() const
Retrieve the callbacks.
void setDesiredBytecodeVersion(int64_t bytecodeVersion)
Set the desired bytecode version to emit.
void attachResourcePrinter(std::unique_ptr< AsmResourcePrinter > printer)
Attach the given resource printer to the writer configuration.
void attachAttributeCallback(std::unique_ptr< AttrTypeBytecodeWriter< Attribute >> callback)
Attach a custom bytecode printer callback to the configuration for the emission of custom type/attrib...
This class defines a virtual interface for writing to a bytecode stream, providing hooks into the byt...
A fallback map containing external resources not explicitly handled by another parser/printer.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Operation is the basic unit of execution within MLIR.
DictionaryAttr getAttrDictionary()
Return all of the attributes on this operation as a DictionaryAttr.
unsigned getNumSuccessors()
bool isRegistered()
Returns true if this operation has a registered operation description, otherwise false.
unsigned getNumRegions()
Returns the number of regions held by this operation.
Location getLoc()
The source location the operation was defined or derived from.
unsigned getNumOperands()
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
MutableArrayRef< Region > getRegions()
Returns the regions held by this operation.
OperationName getName()
The name of an operation is the key identifier for it.
DictionaryAttr getDiscardableAttrDictionary()
Return all of the discardable attributes on this operation as a DictionaryAttr.
result_type_range getResultTypes()
operand_range getOperands()
Returns an iterator on the underlying Value's.
SuccessorRange getSuccessors()
result_range getResults()
int getPropertiesStorageSize() const
Returns the properties storage size.
OpaqueProperties getPropertiesStorage()
Returns the properties storage.
unsigned getNumResults()
Return the number of results held by this operation.
This class contains a list of basic blocks and a link to the parent operation it is attached to.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
This class provides an abstraction over the different types of ranges over Values.
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
This class manages numbering IR entities in preparation of bytecode emission.
@ 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.
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.
constexpr void enumerate(std::tuple< Tys... > &tuple, CallbackT &&callback)
Include the generated interface declarations.
const FrozenRewritePatternSet GreedyRewriteConfig config
AsmResourceEntryKind
This enum represents the different kinds of resource values.
@ Blob
A blob of data with an accompanying alignment.
LogicalResult writeBytecodeToFile(Operation *op, raw_ostream &os, const BytecodeWriterConfig &config={})
Write the bytecode for the given operation to the provided output stream.
StringRef producer
The producer of the bytecode.
llvm::StringMap< std::unique_ptr< DialectVersion > > dialectVersionMap
A map containing dialect version information for each dialect to emit.
llvm::SmallVector< std::unique_ptr< AttrTypeBytecodeWriter< Attribute > > > attributeWriterCallbacks
Printer callbacks used to emit custom type and attribute encodings.
SmallVector< std::unique_ptr< AsmResourcePrinter > > externalResourcePrinters
A collection of non-dialect resource printers.
llvm::SmallVector< std::unique_ptr< AttrTypeBytecodeWriter< Type > > > typeWriterCallbacks
This class represents a numbering entry for an Dialect.
unsigned number
The number assigned to the dialect.