18#include "llvm/ADT/ArrayRef.h"
19#include "llvm/ADT/CachedHashString.h"
20#include "llvm/ADT/MapVector.h"
21#include "llvm/ADT/SmallVector.h"
22#include "llvm/Support/Debug.h"
23#include "llvm/Support/DebugLog.h"
24#include "llvm/Support/Endian.h"
25#include "llvm/Support/raw_ostream.h"
28#define DEBUG_TYPE "mlir-bytecode-writer"
68 :
impl(std::make_unique<
Impl>(producer)) {}
81 return impl->attributeWriterCallbacks;
86 return impl->typeWriterCallbacks;
91 impl->attributeWriterCallbacks.emplace_back(std::move(callback));
96 impl->typeWriterCallbacks.emplace_back(std::move(callback));
100 std::unique_ptr<AsmResourcePrinter> printer) {
101 impl->externalResourcePrinters.emplace_back(std::move(printer));
105 bool shouldElideResourceData) {
106 impl->shouldElideResourceData = shouldElideResourceData;
114 return impl->shouldElideLocations;
118 impl->bytecodeVersion = bytecodeVersion;
122 return impl->bytecodeVersion;
125llvm::StringMap<std::unique_ptr<DialectVersion>> &
127 return impl->dialectVersionMap;
131 llvm::StringRef dialectName,
132 std::unique_ptr<DialectVersion> dialectVersion)
const {
133 assert(!
impl->dialectVersionMap.contains(dialectName) &&
134 "cannot override a previously set dialect version");
135 impl->dialectVersionMap.insert({dialectName, std::move(dialectVersion)});
148class EncodingEmitter {
150 EncodingEmitter() =
default;
151 EncodingEmitter(
const EncodingEmitter &) =
delete;
152 EncodingEmitter &operator=(
const EncodingEmitter &) =
delete;
155 void writeTo(raw_ostream &os)
const;
158 size_t size()
const {
return prevResultSize + currentResult.size(); }
165 void patchByte(uint64_t offset, uint8_t value, StringLiteral desc) {
166 LDBG() <<
"patchByte(" << offset <<
',' << uint64_t(value) <<
")\t" << desc;
167 assert(offset < size() && offset >= prevResultSize &&
168 "cannot patch previously emitted data");
169 currentResult[offset - prevResultSize] = value;
174 void emitOwnedBlob(ArrayRef<uint8_t> data, StringLiteral desc) {
175 LDBG() <<
"emitOwnedBlob(" << data.size() <<
"b)\t" << desc;
177 appendResult(std::move(currentResult));
178 appendOwnedResult(data);
185 void emitOwnedBlobAndAlignment(ArrayRef<uint8_t> data, uint32_t alignment,
186 StringLiteral desc) {
187 emitVarInt(alignment, desc);
188 emitVarInt(data.size(), desc);
191 emitOwnedBlob(data, desc);
193 void emitOwnedBlobAndAlignment(ArrayRef<char> data, uint32_t alignment,
194 StringLiteral desc) {
195 ArrayRef<uint8_t> castedData(
reinterpret_cast<const uint8_t *
>(data.data()),
197 emitOwnedBlobAndAlignment(castedData, alignment, desc);
201 void alignTo(
unsigned alignment) {
204 assert(llvm::isPowerOf2_32(alignment) &&
"expected valid alignment");
208 size_t curOffset = size();
209 size_t paddingSize = llvm::alignTo(curOffset, alignment) - curOffset;
210 while (paddingSize--)
214 requiredAlignment = std::max(requiredAlignment, alignment);
221 template <
typename T>
222 void emitByte(T
byte, StringLiteral desc) {
223 LDBG() <<
"emitByte(" << uint64_t(
byte) <<
")\t" << desc;
224 currentResult.push_back(
static_cast<uint8_t
>(
byte));
228 void emitBytes(ArrayRef<uint8_t> bytes, StringLiteral desc) {
229 LDBG() <<
"emitBytes(" << bytes.size() <<
"b)\t" << desc;
230 llvm::append_range(currentResult, bytes);
240 void emitVarInt(uint64_t value, StringLiteral desc) {
241 LDBG() <<
"emitVarInt(" << value <<
")\t" << desc;
245 if ((value >> 7) == 0)
246 return emitByte((value << 1) | 0x1, desc);
247 emitMultiByteVarInt(value, desc);
254 void emitSignedVarInt(uint64_t value, StringLiteral desc) {
255 emitVarInt((value << 1) ^ (uint64_t)((int64_t)value >> 63), desc);
260 void emitVarIntWithFlag(uint64_t value,
bool flag, StringLiteral desc) {
261 emitVarInt((value << 1) | (flag ? 1 : 0), desc);
268 void emitNulTerminatedString(StringRef str, StringLiteral desc) {
269 emitString(str, desc);
270 emitByte(0,
"null terminator");
274 void emitString(StringRef str, StringLiteral desc) {
275 emitBytes({
reinterpret_cast<const uint8_t *
>(str.data()), str.size()},
288 uint64_t codeOffset = currentResult.size();
289 emitByte(code,
"section code");
290 emitVarInt(emitter.size(),
"section size");
293 unsigned emitterAlign = emitter.requiredAlignment;
294 if (emitterAlign > 1) {
295 if (size() & (emitterAlign - 1)) {
296 emitVarInt(emitterAlign,
"section alignment");
297 alignTo(emitterAlign);
301 currentResult[codeOffset] |= 0b10000000;
305 requiredAlignment = std::max(requiredAlignment, emitterAlign);
311 appendResult(std::move(currentResult));
312 for (std::vector<uint8_t> &
result : emitter.prevResultStorage)
313 prevResultStorage.push_back(std::move(
result));
314 llvm::append_range(prevResultList, emitter.prevResultList);
315 prevResultSize += emitter.prevResultSize;
316 appendResult(std::move(emitter.currentResult));
324 LLVM_ATTRIBUTE_NOINLINE
void emitMultiByteVarInt(uint64_t value,
328 void appendResult(std::vector<uint8_t> &&
result) {
331 prevResultStorage.emplace_back(std::move(
result));
332 appendOwnedResult(prevResultStorage.back());
334 void appendOwnedResult(ArrayRef<uint8_t>
result) {
337 prevResultSize +=
result.size();
338 prevResultList.emplace_back(
result);
346 std::vector<uint8_t> currentResult;
347 std::vector<ArrayRef<uint8_t>> prevResultList;
348 std::vector<std::vector<uint8_t>> prevResultStorage;
352 size_t prevResultSize = 0;
355 unsigned requiredAlignment = 1;
364class StringSectionBuilder {
368 size_t insert(StringRef str) {
369 auto it = strings.insert({llvm::CachedHashStringRef(str), strings.size()});
370 return it.first->second;
374 void write(EncodingEmitter &emitter) {
375 emitter.emitVarInt(strings.size(),
"string section size");
379 for (
const auto &it : llvm::reverse(strings))
380 emitter.emitVarInt(it.first.size() + 1,
"string size");
382 for (
const auto &it : strings)
383 emitter.emitNulTerminatedString(it.first.val(),
"string");
389 llvm::MapVector<llvm::CachedHashStringRef, size_t> strings;
394 using DialectVersionMapT = llvm::StringMap<std::unique_ptr<DialectVersion>>;
397 DialectWriter(int64_t bytecodeVersion, EncodingEmitter &emitter,
398 IRNumberingState &numberingState,
399 StringSectionBuilder &stringSection,
400 const DialectVersionMapT &dialectVersionMap)
401 : bytecodeVersion(bytecodeVersion), emitter(emitter),
402 numberingState(numberingState), stringSection(stringSection),
403 dialectVersionMap(dialectVersionMap) {}
409 void writeAttribute(Attribute attr)
override {
410 emitter.emitVarInt(numberingState.getNumber(attr),
"dialect attr");
412 void writeOptionalAttribute(Attribute attr)
override {
414 emitter.emitVarInt(0,
"dialect optional attr none");
417 emitter.emitVarIntWithFlag(numberingState.getNumber(attr),
true,
418 "dialect optional attr");
421 void writeType(Type type)
override {
422 emitter.emitVarInt(numberingState.getNumber(type),
"dialect type");
425 void writeResourceHandle(
const AsmDialectResourceHandle &resource)
override {
426 emitter.emitVarInt(numberingState.getNumber(resource),
"dialect resource");
433 void writeVarInt(uint64_t value)
override {
434 emitter.emitVarInt(value,
"dialect writer");
437 void writeSignedVarInt(int64_t value)
override {
438 emitter.emitSignedVarInt(value,
"dialect writer");
441 void writeAPIntWithKnownWidth(
const APInt &value)
override {
442 size_t bitWidth = value.getBitWidth();
447 return emitter.emitByte(value.getLimitedValue(),
"dialect APInt");
451 return emitter.emitSignedVarInt(value.getLimitedValue(),
"dialect APInt");
456 unsigned numActiveWords = value.getActiveWords();
457 emitter.emitVarInt(numActiveWords,
"dialect APInt word count");
459 const uint64_t *rawValueData = value.getRawData();
460 for (
unsigned i = 0; i < numActiveWords; ++i)
461 emitter.emitSignedVarInt(rawValueData[i],
"dialect APInt word");
464 void writeAPFloatWithKnownSemantics(
const APFloat &value)
override {
465 writeAPIntWithKnownWidth(value.bitcastToAPInt());
468 void writeOwnedString(StringRef str)
override {
469 emitter.emitVarInt(stringSection.insert(str),
"dialect string");
472 void writeOwnedBlob(ArrayRef<char> blob)
override {
473 emitter.emitVarInt(blob.size(),
"dialect blob");
474 emitter.emitOwnedBlob(
475 ArrayRef<uint8_t>(
reinterpret_cast<const uint8_t *
>(blob.data()),
480 void writeUnownedBlob(ArrayRef<char> blob)
override {
481 emitter.emitVarInt(blob.size(),
"dialect blob");
483 ArrayRef<uint8_t>(
reinterpret_cast<const uint8_t *
>(blob.data()),
488 void writeOwnedBool(
bool value)
override {
489 emitter.emitByte(value,
"dialect bool");
492 int64_t getBytecodeVersion()
const override {
return bytecodeVersion; }
494 FailureOr<const DialectVersion *>
495 getDialectVersion(StringRef dialectName)
const override {
496 auto dialectEntry = dialectVersionMap.find(dialectName);
497 if (dialectEntry == dialectVersionMap.end())
499 return dialectEntry->getValue().get();
503 int64_t bytecodeVersion;
504 EncodingEmitter &emitter;
505 IRNumberingState &numberingState;
506 StringSectionBuilder &stringSection;
507 const DialectVersionMapT &dialectVersionMap;
511class PropertiesSectionBuilder {
513 PropertiesSectionBuilder(IRNumberingState &numberingState,
514 StringSectionBuilder &stringSection,
515 const BytecodeWriterConfig::Impl &config)
516 : numberingState(numberingState), stringSection(stringSection),
521 std::optional<ssize_t>
emit(Operation *op) {
522 EncodingEmitter propertiesEmitter;
530 EncodingEmitter sizeEmitter;
531 sizeEmitter.emitVarInt(numberingState.getNumber(prop),
"properties size");
533 llvm::raw_svector_ostream os(scratch);
534 sizeEmitter.writeTo(os);
535 return emit(scratch);
538 EncodingEmitter emitter;
539 DialectWriter propertiesWriter(config.bytecodeVersion, emitter,
540 numberingState, stringSection,
541 config.dialectVersionMap);
542 auto iface = cast<BytecodeOpInterface>(op);
543 iface.writeProperties(propertiesWriter);
545 llvm::raw_svector_ostream os(scratch);
547 return emit(scratch);
551 void write(EncodingEmitter &emitter) {
552 emitter.emitVarInt(propertiesStorage.size(),
"properties size");
553 if (propertiesStorage.empty())
555 for (
const auto &storage : propertiesStorage) {
556 if (storage.empty()) {
557 emitter.emitBytes(ArrayRef<uint8_t>(),
"empty properties");
560 emitter.emitBytes(ArrayRef(
reinterpret_cast<const uint8_t *
>(&storage[0]),
567 bool empty() {
return propertiesStorage.empty(); }
573 ssize_t
emit(ArrayRef<char> rawProperties) {
575 SmallVector<char> sizeScratch;
577 EncodingEmitter sizeEmitter;
578 sizeEmitter.emitVarInt(rawProperties.size(),
"properties");
579 llvm::raw_svector_ostream os(sizeScratch);
580 sizeEmitter.writeTo(os);
583 size_t index = propertiesStorage.size();
584 propertiesStorage.emplace_back();
585 std::vector<char> &newStorage = propertiesStorage.back();
586 size_t propertiesSize = sizeScratch.size() + rawProperties.size();
587 newStorage.reserve(propertiesSize);
588 llvm::append_range(newStorage, sizeScratch);
589 llvm::append_range(newStorage, rawProperties);
593 auto inserted = propertiesUniquing.insert(
594 std::make_pair(ArrayRef<char>(newStorage), index));
596 propertiesStorage.pop_back();
601 std::vector<std::vector<char>> propertiesStorage;
602 SmallVector<char> scratch;
604 IRNumberingState &numberingState;
605 StringSectionBuilder &stringSection;
606 const BytecodeWriterConfig::Impl &config;
615 explicit RawEmitterOstream(EncodingEmitter &emitter) : emitter(emitter) {
620 void write_impl(
const char *ptr,
size_t size)
override {
621 emitter.emitBytes({
reinterpret_cast<const uint8_t *
>(ptr), size},
624 uint64_t current_pos()
const override {
return emitter.size(); }
627 EncodingEmitter &emitter;
631void EncodingEmitter::writeTo(
raw_ostream &os)
const {
633 os.reserveExtraSpace(size());
635 for (
auto &prevResult : prevResultList)
636 os.write((
const char *)prevResult.data(), prevResult.size());
637 os.write((
const char *)currentResult.data(), currentResult.size());
640void EncodingEmitter::emitMultiByteVarInt(uint64_t value, StringLiteral desc) {
644 uint64_t it = value >> 7;
645 for (
size_t numBytes = 2; numBytes < 9; ++numBytes) {
646 if (LLVM_LIKELY(it >>= 7) == 0) {
647 uint64_t encodedValue = (value << 1) | 0x1;
648 encodedValue <<= (numBytes - 1);
649 llvm::support::ulittle64_t encodedValueLE(encodedValue);
650 emitBytes({
reinterpret_cast<uint8_t *
>(&encodedValueLE), numBytes}, desc);
658 llvm::support::ulittle64_t valueLE(value);
659 emitBytes({
reinterpret_cast<uint8_t *
>(&valueLE),
sizeof(valueLE)}, desc);
667class BytecodeWriter {
669 BytecodeWriter(Operation *op,
const BytecodeWriterConfig &config)
670 : numberingState(op, config), config(config.getImpl()),
671 propertiesSection(numberingState, stringSection, config.getImpl()) {}
674 LogicalResult write(Operation *rootOp, raw_ostream &os);
680 void writeDialectSection(EncodingEmitter &emitter);
685 void writeAttrTypeSection(EncodingEmitter &emitter);
690 LogicalResult writeBlock(EncodingEmitter &emitter,
Block *block);
691 LogicalResult writeOp(EncodingEmitter &emitter, Operation *op);
692 LogicalResult writeRegion(EncodingEmitter &emitter, Region *region);
693 LogicalResult writeIRSection(EncodingEmitter &emitter, Operation *op);
695 LogicalResult writeRegions(EncodingEmitter &emitter,
696 MutableArrayRef<Region> regions) {
697 return success(llvm::all_of(regions, [&](Region ®ion) {
698 return succeeded(writeRegion(emitter, ®ion));
705 void writeResourceSection(Operation *op, EncodingEmitter &emitter);
710 void writeStringSection(EncodingEmitter &emitter);
715 void writePropertiesSection(EncodingEmitter &emitter);
720 void writeUseListOrders(EncodingEmitter &emitter, uint8_t &opEncodingMask,
727 StringSectionBuilder stringSection;
730 IRNumberingState numberingState;
733 const BytecodeWriterConfig::Impl &config;
736 PropertiesSectionBuilder propertiesSection;
741 EncodingEmitter emitter;
745 emitter.emitString(
"ML\xefR",
"bytecode header");
751 <<
"unsupported version requested " << config.bytecodeVersion
752 <<
", must be in range ["
755 emitter.emitVarInt(config.bytecodeVersion,
"bytecode version");
758 emitter.emitNulTerminatedString(config.producer,
"bytecode producer");
761 writeDialectSection(emitter);
764 writeAttrTypeSection(emitter);
767 if (
failed(writeIRSection(emitter, rootOp)))
771 writeResourceSection(rootOp, emitter);
774 writeStringSection(emitter);
778 writePropertiesSection(emitter);
779 else if (!propertiesSection.empty())
781 "unexpected properties emitted incompatible with bytecode <5");
797template <
typename EntriesT,
typename EntryCallbackT>
799 EntryCallbackT &&callback) {
800 for (
auto it = entries.begin(), e = entries.end(); it != e;) {
801 auto groupStart = it++;
805 it = std::find_if(it, e, [&](
const auto &entry) {
806 return entry.dialect != currentDialect;
810 emitter.emitVarInt(currentDialect->
number,
"dialect number");
811 emitter.emitVarInt(std::distance(groupStart, it),
"dialect offset");
814 for (
auto &entry : llvm::make_range(groupStart, it))
819void BytecodeWriter::writeDialectSection(EncodingEmitter &emitter) {
820 EncodingEmitter dialectEmitter;
823 auto dialects = numberingState.getDialects();
824 dialectEmitter.emitVarInt(llvm::size(dialects),
"dialects count");
825 for (DialectNumbering &dialect : dialects) {
827 size_t nameID = stringSection.insert(dialect.name);
830 dialectEmitter.emitVarInt(nameID,
"dialect name ID");
835 EncodingEmitter versionEmitter;
836 if (dialect.interface) {
838 DialectWriter versionWriter(config.bytecodeVersion, versionEmitter,
839 numberingState, stringSection,
840 config.dialectVersionMap);
841 dialect.interface->writeVersion(versionWriter);
847 size_t versionAvailable = versionEmitter.size() > 0;
848 dialectEmitter.emitVarIntWithFlag(nameID, versionAvailable,
850 if (versionAvailable)
852 std::move(versionEmitter));
856 dialectEmitter.emitVarInt(size(numberingState.getOpNames()),
860 auto emitOpName = [&](OpNameNumbering &name) {
861 size_t stringId = stringSection.insert(name.name.stripDialect());
863 dialectEmitter.emitVarInt(stringId,
"dialect op name");
865 dialectEmitter.emitVarIntWithFlag(stringId, name.name.isRegistered(),
877void BytecodeWriter::writeAttrTypeSection(EncodingEmitter &emitter) {
878 EncodingEmitter attrTypeEmitter;
879 EncodingEmitter offsetEmitter;
880 offsetEmitter.emitVarInt(llvm::size(numberingState.getAttributes()),
882 offsetEmitter.emitVarInt(llvm::size(numberingState.getTypes()),
886 uint64_t prevOffset = 0;
887 auto emitAttrOrType = [&](
auto &entry) {
888 auto entryValue = entry.getValue();
890 auto emitAttrOrTypeRawImpl = [&]() ->
void {
891 RawEmitterOstream(attrTypeEmitter) << entryValue;
892 attrTypeEmitter.emitByte(0,
"attr/type separator");
894 auto emitAttrOrTypeImpl = [&]() ->
bool {
897 if (entryValue.template hasTrait<TypeTrait::IsMutable>() ||
898 entryValue.template hasTrait<AttributeTrait::IsMutable>()) {
899 emitAttrOrTypeRawImpl();
903 DialectWriter dialectWriter(config.bytecodeVersion, attrTypeEmitter,
904 numberingState, stringSection,
905 config.dialectVersionMap);
906 if constexpr (std::is_same_v<std::decay_t<
decltype(entryValue)>,
Type>) {
907 for (
const auto &callback : config.typeWriterCallbacks) {
908 if (succeeded(callback->write(entryValue, dialectWriter)))
911 if (
const BytecodeDialectInterface *interface =
912 entry.dialect->interface) {
913 if (succeeded(interface->writeType(entryValue, dialectWriter)))
917 for (
const auto &callback : config.attributeWriterCallbacks) {
918 if (succeeded(callback->write(entryValue, dialectWriter)))
921 if (
const BytecodeDialectInterface *interface =
922 entry.dialect->interface) {
923 if (succeeded(interface->writeAttribute(entryValue, dialectWriter)))
930 emitAttrOrTypeRawImpl();
934 bool hasCustomEncoding = emitAttrOrTypeImpl();
937 uint64_t curOffset = attrTypeEmitter.size();
938 offsetEmitter.emitVarIntWithFlag(curOffset - prevOffset, hasCustomEncoding,
940 prevOffset = curOffset;
951 std::move(offsetEmitter));
959LogicalResult BytecodeWriter::writeBlock(EncodingEmitter &emitter,
962 bool hasArgs = !args.empty();
967 unsigned numOps = numberingState.getOperationCount(block);
968 emitter.emitVarIntWithFlag(numOps, hasArgs,
"block num ops");
972 emitter.emitVarInt(args.size(),
"block args count");
976 emitter.emitVarIntWithFlag(numberingState.getNumber(arg.getType()),
977 !isa<UnknownLoc>(argLoc),
"block arg type");
978 if (!isa<UnknownLoc>(argLoc))
979 emitter.emitVarInt(numberingState.getNumber(argLoc),
980 "block arg location");
982 emitter.emitVarInt(numberingState.getNumber(arg.getType()),
984 emitter.emitVarInt(numberingState.getNumber(argLoc),
985 "block arg location");
989 uint64_t maskOffset = emitter.size();
990 uint8_t encodingMask = 0;
991 emitter.emitByte(0,
"use-list separator");
992 writeUseListOrders(emitter, encodingMask, args);
994 emitter.patchByte(maskOffset, encodingMask,
"block patch encoding");
1000 if (
failed(writeOp(emitter, &op)))
1005LogicalResult BytecodeWriter::writeOp(EncodingEmitter &emitter,
Operation *op) {
1006 emitter.emitVarInt(numberingState.getNumber(op->
getName()),
"op name ID");
1011 uint64_t maskOffset = emitter.size();
1012 uint8_t opEncodingMask = 0;
1013 emitter.emitByte(0,
"op separator");
1016 emitter.emitVarInt(numberingState.getNumber(op->
getLoc()),
"op location");
1028 allAttrs.
append(name, attr);
1033 if (!attrs.empty()) {
1035 emitter.emitVarInt(numberingState.getNumber(attrs),
"op attrs count");
1041 std::optional<ssize_t> propertiesId = propertiesSection.emit(op);
1042 if (propertiesId.has_value()) {
1044 emitter.emitVarInt(*propertiesId,
"op properties ID");
1051 emitter.emitVarInt(numResults,
"op results count");
1053 emitter.emitVarInt(numberingState.getNumber(type),
"op result type");
1059 emitter.emitVarInt(numOperands,
"op operands count");
1061 emitter.emitVarInt(numberingState.getNumber(operand),
"op operand types");
1067 emitter.emitVarInt(numSuccessors,
"op successors count");
1069 emitter.emitVarInt(numberingState.getNumber(successor),
"op successor");
1083 emitter.patchByte(maskOffset, opEncodingMask,
"op encoding mask");
1090 bool isIsolatedFromAbove = numberingState.isIsolatedFromAbove(op);
1091 emitter.emitVarIntWithFlag(numRegions, isIsolatedFromAbove,
1092 "op regions count");
1096 if (isIsolatedFromAbove &&
1098 EncodingEmitter regionEmitter;
1110void BytecodeWriter::writeUseListOrders(EncodingEmitter &emitter,
1111 uint8_t &opEncodingMask,
1114 llvm::MapVector<unsigned, llvm::SmallVector<unsigned>> map;
1115 for (
auto item : llvm::enumerate(range)) {
1116 auto value = item.value();
1119 if (value.use_empty() || value.hasOneUse())
1125 bool alreadyOrdered =
true;
1126 auto &firstUse = *value.use_begin();
1128 firstUse, numberingState.getNumber(firstUse.getOwner()));
1129 llvm::SmallVector<std::pair<unsigned, uint64_t>> useListPairs(
1132 for (
auto use : llvm::drop_begin(llvm::enumerate(value.getUses()))) {
1134 use.value(), numberingState.getNumber(use.value().getOwner()));
1138 alreadyOrdered &= (prevID > currentID);
1139 useListPairs.push_back({use.index(), currentID});
1149 useListPairs.begin(), useListPairs.end(),
1150 [](
auto elem1,
auto elem2) { return elem1.second > elem2.second; });
1152 map.try_emplace(item.index(), llvm::map_range(useListPairs, [](
auto elem) {
1163 if (range.size() != 1) {
1164 emitter.emitVarInt(map.size(),
"custom use-list size");
1167 for (
const auto &[resultIdx, useListOrder] : map) {
1171 size_t shuffledElements =
1172 llvm::count_if(llvm::enumerate(useListOrder),
1173 [](
auto item) {
return item.index() != item.value(); });
1174 bool indexPairEncoding = shuffledElements < (useListOrder.size() / 2);
1177 if (range.size() != 1)
1178 emitter.emitVarInt(resultIdx,
"use-list result index");
1180 if (indexPairEncoding) {
1181 emitter.emitVarIntWithFlag(shuffledElements * 2, indexPairEncoding,
1182 "use-list index pair size");
1183 for (
auto pair : llvm::enumerate(useListOrder)) {
1184 if (pair.index() != pair.value()) {
1185 emitter.emitVarInt(pair.value(),
"use-list index pair first");
1186 emitter.emitVarInt(pair.index(),
"use-list index pair second");
1190 emitter.emitVarIntWithFlag(useListOrder.size(), indexPairEncoding,
1192 for (
const auto &
index : useListOrder)
1193 emitter.emitVarInt(
index,
"use-list order");
1198LogicalResult BytecodeWriter::writeRegion(EncodingEmitter &emitter,
1202 if (region->
empty()) {
1203 emitter.emitVarInt( 0,
"region block count empty");
1208 unsigned numBlocks, numValues;
1209 std::tie(numBlocks, numValues) = numberingState.getBlockValueCount(region);
1210 emitter.emitVarInt(numBlocks,
"region block count");
1211 emitter.emitVarInt(numValues,
"region value count");
1214 for (
Block &block : *region)
1215 if (
failed(writeBlock(emitter, &block)))
1220LogicalResult BytecodeWriter::writeIRSection(EncodingEmitter &emitter,
1222 EncodingEmitter irEmitter;
1227 irEmitter.emitVarIntWithFlag( 1,
false,
"ir section");
1230 if (
failed(writeOp(irEmitter, op)))
1248 ResourceBuilder(EncodingEmitter &emitter, StringSectionBuilder &stringSection,
1249 PostProcessFn postProcessFn,
bool shouldElideData)
1250 : emitter(emitter), stringSection(stringSection),
1251 postProcessFn(postProcessFn), shouldElideData(shouldElideData) {}
1252 ~ResourceBuilder()
override =
default;
1254 void buildBlob(StringRef key, ArrayRef<char> data,
1255 uint32_t dataAlignment)
final {
1256 if (!shouldElideData)
1257 emitter.emitOwnedBlobAndAlignment(data, dataAlignment,
"resource blob");
1258 postProcessFn(key, AsmResourceEntryKind::Blob);
1260 void buildBool(StringRef key,
bool data)
final {
1261 if (!shouldElideData)
1262 emitter.emitByte(data,
"resource bool");
1263 postProcessFn(key, AsmResourceEntryKind::Bool);
1265 void buildString(StringRef key, StringRef data)
final {
1266 if (!shouldElideData)
1267 emitter.emitVarInt(stringSection.insert(data),
"resource string");
1268 postProcessFn(key, AsmResourceEntryKind::String);
1272 EncodingEmitter &emitter;
1273 StringSectionBuilder &stringSection;
1274 PostProcessFn postProcessFn;
1275 bool shouldElideData =
false;
1279void BytecodeWriter::writeResourceSection(
Operation *op,
1280 EncodingEmitter &emitter) {
1281 EncodingEmitter resourceEmitter;
1282 EncodingEmitter resourceOffsetEmitter;
1283 uint64_t prevOffset = 0;
1290 uint64_t curOffset = resourceEmitter.size();
1291 curResourceEntries.emplace_back(key, kind, curOffset - prevOffset);
1292 prevOffset = curOffset;
1296 auto emitResourceGroup = [&](uint64_t key) {
1297 resourceOffsetEmitter.emitVarInt(key,
"resource group key");
1298 resourceOffsetEmitter.emitVarInt(curResourceEntries.size(),
1299 "resource group size");
1300 for (
auto [key, kind, size] : curResourceEntries) {
1301 resourceOffsetEmitter.emitVarInt(stringSection.insert(key),
1303 resourceOffsetEmitter.emitVarInt(size,
"resource size");
1304 resourceOffsetEmitter.emitByte(kind,
"resource kind");
1309 ResourceBuilder entryBuilder(resourceEmitter, stringSection,
1310 appendResourceOffset,
1311 config.shouldElideResourceData);
1314 resourceOffsetEmitter.emitVarInt(config.externalResourcePrinters.size(),
1315 "external resource printer count");
1316 for (
const auto &printer : config.externalResourcePrinters) {
1317 curResourceEntries.clear();
1318 printer->buildResources(op, entryBuilder);
1319 emitResourceGroup(stringSection.insert(printer->getName()));
1323 for (DialectNumbering &dialect : numberingState.getDialects()) {
1324 if (!dialect.asmInterface)
1326 curResourceEntries.clear();
1327 dialect.asmInterface->buildResources(op, dialect.resources, entryBuilder);
1332 for (
const auto &resource : dialect.resourceMap)
1333 if (resource.second->isDeclaration)
1337 if (!curResourceEntries.empty())
1338 emitResourceGroup(dialect.number);
1342 if (resourceOffsetEmitter.size() == 0)
1346 std::move(resourceOffsetEmitter));
1354void BytecodeWriter::writeStringSection(EncodingEmitter &emitter) {
1355 EncodingEmitter stringEmitter;
1356 stringSection.write(stringEmitter);
1364void BytecodeWriter::writePropertiesSection(EncodingEmitter &emitter) {
1365 EncodingEmitter propertiesEmitter;
1366 propertiesSection.write(propertiesEmitter);
1368 std::move(propertiesEmitter));
1383 dialect->getRegisteredInterface<BytecodeDialectInterface>();
1384 if (interface && failed(interface->verifyVersion(op, *entry.getValue())))
1387 BytecodeWriter writer(op, config);
1388 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.
*if copies could not be generated due to yet unimplemented cases *copyInPlacementStart and copyOutPlacementStart in copyPlacementBlock *specify the insertion points where the incoming copies and outgoing should be inserted(the insertion happens right before the *insertion point). Since `begin` can itself be invalidated due to the memref *rewriting done from this method
static LogicalResult emit(SolverOp solver, const SMTEmissionOptions &options, mlir::raw_indented_ostream &stream)
Emit the SMT operations in the given 'solver' to the 'stream'.
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.
bool shouldElideLocations() const
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.
void attachTypeCallback(std::unique_ptr< AttrTypeBytecodeWriter< Type > > callback)
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.
void attachAttributeCallback(std::unique_ptr< AttrTypeBytecodeWriter< Attribute > > callback)
Attach a custom bytecode printer callback to the configuration for the emission of custom type/attrib...
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 setElideLocations(bool shouldElideLocations=true)
Set a boolean flag to skip emission of unique locations into the bytecode file.
void attachResourcePrinter(std::unique_ptr< AsmResourcePrinter > printer)
Attach the given resource printer to the writer configuration.
This class defines a virtual interface for writing to a bytecode stream, providing hooks into the byt...
Dialects are groups of MLIR operations, types and attributes, as well as behavior associated with the...
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...
Dialect * getLoadedDialect(StringRef name)
Get a registered IR dialect with the given namespace.
NamedAttrList is array of NamedAttributes that tracks whether it is sorted and does some basic work t...
DictionaryAttr getDictionary(MLIRContext *context) const
Return a dictionary attribute for the underlying dictionary.
void append(StringRef name, Attribute attr)
Add an attribute with the specified name.
void walkInherentAttrs(Operation *op, InherentAttrVisitor visitor) const
Visit the inherent attributes stored in the properties of op.
Operation is the basic unit of execution within MLIR.
PropertyRef getPropertiesStorage()
Return a generic (but typed) reference to the property type storage.
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.
DictionaryAttr getRawDictionaryAttrs()
Return all attributes that are not stored as properties.
unsigned getNumOperands()
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
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.
MutableArrayRef< Region > getRegions()
Returns the regions held by this operation.
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.
MLIRContext * getContext()
Return the context this operation is associated with.
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...
@ 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.
Include the generated interface declarations.
llvm::DenseMap< KeyT, ValueT, KeyInfoT, BucketT > DenseMap
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.
llvm::function_ref< Fn > function_ref
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.
bool shouldElideLocations
A flag specifying whether to elide emission of locations.
llvm::SmallVector< std::unique_ptr< AttrTypeBytecodeWriter< Type > > > typeWriterCallbacks
int64_t bytecodeVersion
Version to use when writing.
bool shouldElideResourceData
A flag specifying whether to elide emission of resources into the bytecode file.
This class represents a numbering entry for an Dialect.
unsigned number
The number assigned to the dialect.