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/DebugLog.h"
23#include "llvm/Support/Endian.h"
24#include "llvm/Support/raw_ostream.h"
27#define DEBUG_TYPE "mlir-bytecode-writer"
64 :
impl(std::make_unique<
Impl>(producer)) {}
77 return impl->attributeWriterCallbacks;
82 return impl->typeWriterCallbacks;
87 impl->attributeWriterCallbacks.emplace_back(std::move(callback));
92 impl->typeWriterCallbacks.emplace_back(std::move(callback));
96 std::unique_ptr<AsmResourcePrinter> printer) {
97 impl->externalResourcePrinters.emplace_back(std::move(printer));
101 bool shouldElideResourceData) {
102 impl->shouldElideResourceData = shouldElideResourceData;
106 impl->bytecodeVersion = bytecodeVersion;
110 return impl->bytecodeVersion;
113llvm::StringMap<std::unique_ptr<DialectVersion>> &
115 return impl->dialectVersionMap;
119 llvm::StringRef dialectName,
120 std::unique_ptr<DialectVersion> dialectVersion)
const {
121 assert(!
impl->dialectVersionMap.contains(dialectName) &&
122 "cannot override a previously set dialect version");
123 impl->dialectVersionMap.insert({dialectName, std::move(dialectVersion)});
136class EncodingEmitter {
138 EncodingEmitter() =
default;
139 EncodingEmitter(
const EncodingEmitter &) =
delete;
140 EncodingEmitter &operator=(
const EncodingEmitter &) =
delete;
143 void writeTo(raw_ostream &os)
const;
146 size_t size()
const {
return prevResultSize + currentResult.size(); }
153 void patchByte(uint64_t offset, uint8_t value, StringLiteral desc) {
154 LDBG() <<
"patchByte(" << offset <<
',' << uint64_t(value) <<
")\t" << desc;
155 assert(offset < size() && offset >= prevResultSize &&
156 "cannot patch previously emitted data");
157 currentResult[offset - prevResultSize] = value;
162 void emitOwnedBlob(ArrayRef<uint8_t> data, StringLiteral desc) {
163 LDBG() <<
"emitOwnedBlob(" << data.size() <<
"b)\t" << desc;
165 appendResult(std::move(currentResult));
166 appendOwnedResult(data);
173 void emitOwnedBlobAndAlignment(ArrayRef<uint8_t> data, uint32_t alignment,
174 StringLiteral desc) {
175 emitVarInt(alignment, desc);
176 emitVarInt(data.size(), desc);
179 emitOwnedBlob(data, desc);
181 void emitOwnedBlobAndAlignment(ArrayRef<char> data, uint32_t alignment,
182 StringLiteral desc) {
183 ArrayRef<uint8_t> castedData(
reinterpret_cast<const uint8_t *
>(data.data()),
185 emitOwnedBlobAndAlignment(castedData, alignment, desc);
189 void alignTo(
unsigned alignment) {
192 assert(llvm::isPowerOf2_32(alignment) &&
"expected valid alignment");
196 size_t curOffset = size();
197 size_t paddingSize = llvm::alignTo(curOffset, alignment) - curOffset;
198 while (paddingSize--)
202 requiredAlignment = std::max(requiredAlignment, alignment);
209 template <
typename T>
210 void emitByte(T
byte, StringLiteral desc) {
211 LDBG() <<
"emitByte(" << uint64_t(
byte) <<
")\t" << desc;
212 currentResult.push_back(
static_cast<uint8_t
>(
byte));
216 void emitBytes(ArrayRef<uint8_t> bytes, StringLiteral desc) {
217 LDBG() <<
"emitBytes(" << bytes.size() <<
"b)\t" << desc;
218 llvm::append_range(currentResult, bytes);
228 void emitVarInt(uint64_t value, StringLiteral desc) {
229 LDBG() <<
"emitVarInt(" << value <<
")\t" << desc;
233 if ((value >> 7) == 0)
234 return emitByte((value << 1) | 0x1, desc);
235 emitMultiByteVarInt(value, desc);
242 void emitSignedVarInt(uint64_t value, StringLiteral desc) {
243 emitVarInt((value << 1) ^ (uint64_t)((int64_t)value >> 63), desc);
248 void emitVarIntWithFlag(uint64_t value,
bool flag, StringLiteral desc) {
249 emitVarInt((value << 1) | (flag ? 1 : 0), desc);
256 void emitNulTerminatedString(StringRef str, StringLiteral desc) {
257 emitString(str, desc);
258 emitByte(0,
"null terminator");
262 void emitString(StringRef str, StringLiteral desc) {
263 emitBytes({
reinterpret_cast<const uint8_t *
>(str.data()), str.size()},
276 uint64_t codeOffset = currentResult.size();
277 emitByte(code,
"section code");
278 emitVarInt(emitter.size(),
"section size");
281 unsigned emitterAlign = emitter.requiredAlignment;
282 if (emitterAlign > 1) {
283 if (size() & (emitterAlign - 1)) {
284 emitVarInt(emitterAlign,
"section alignment");
285 alignTo(emitterAlign);
289 currentResult[codeOffset] |= 0b10000000;
293 requiredAlignment = std::max(requiredAlignment, emitterAlign);
299 appendResult(std::move(currentResult));
300 for (std::vector<uint8_t> &
result : emitter.prevResultStorage)
301 prevResultStorage.push_back(std::move(
result));
302 llvm::append_range(prevResultList, emitter.prevResultList);
303 prevResultSize += emitter.prevResultSize;
304 appendResult(std::move(emitter.currentResult));
312 LLVM_ATTRIBUTE_NOINLINE
void emitMultiByteVarInt(uint64_t value,
316 void appendResult(std::vector<uint8_t> &&
result) {
319 prevResultStorage.emplace_back(std::move(
result));
320 appendOwnedResult(prevResultStorage.back());
322 void appendOwnedResult(ArrayRef<uint8_t>
result) {
325 prevResultSize +=
result.size();
326 prevResultList.emplace_back(
result);
334 std::vector<uint8_t> currentResult;
335 std::vector<ArrayRef<uint8_t>> prevResultList;
336 std::vector<std::vector<uint8_t>> prevResultStorage;
340 size_t prevResultSize = 0;
343 unsigned requiredAlignment = 1;
352class StringSectionBuilder {
356 size_t insert(StringRef str) {
357 auto it = strings.insert({llvm::CachedHashStringRef(str), strings.size()});
358 return it.first->second;
362 void write(EncodingEmitter &emitter) {
363 emitter.emitVarInt(strings.size(),
"string section size");
367 for (
const auto &it : llvm::reverse(strings))
368 emitter.emitVarInt(it.first.size() + 1,
"string size");
370 for (
const auto &it : strings)
371 emitter.emitNulTerminatedString(it.first.val(),
"string");
377 llvm::MapVector<llvm::CachedHashStringRef, size_t> strings;
382 using DialectVersionMapT = llvm::StringMap<std::unique_ptr<DialectVersion>>;
385 DialectWriter(int64_t bytecodeVersion, EncodingEmitter &emitter,
386 IRNumberingState &numberingState,
387 StringSectionBuilder &stringSection,
388 const DialectVersionMapT &dialectVersionMap)
389 : bytecodeVersion(bytecodeVersion), emitter(emitter),
390 numberingState(numberingState), stringSection(stringSection),
391 dialectVersionMap(dialectVersionMap) {}
397 void writeAttribute(Attribute attr)
override {
398 emitter.emitVarInt(numberingState.getNumber(attr),
"dialect attr");
400 void writeOptionalAttribute(Attribute attr)
override {
402 emitter.emitVarInt(0,
"dialect optional attr none");
405 emitter.emitVarIntWithFlag(numberingState.getNumber(attr),
true,
406 "dialect optional attr");
409 void writeType(Type type)
override {
410 emitter.emitVarInt(numberingState.getNumber(type),
"dialect type");
413 void writeResourceHandle(
const AsmDialectResourceHandle &resource)
override {
414 emitter.emitVarInt(numberingState.getNumber(resource),
"dialect resource");
421 void writeVarInt(uint64_t value)
override {
422 emitter.emitVarInt(value,
"dialect writer");
425 void writeSignedVarInt(int64_t value)
override {
426 emitter.emitSignedVarInt(value,
"dialect writer");
429 void writeAPIntWithKnownWidth(
const APInt &value)
override {
430 size_t bitWidth = value.getBitWidth();
435 return emitter.emitByte(value.getLimitedValue(),
"dialect APInt");
439 return emitter.emitSignedVarInt(value.getLimitedValue(),
"dialect APInt");
444 unsigned numActiveWords = value.getActiveWords();
445 emitter.emitVarInt(numActiveWords,
"dialect APInt word count");
447 const uint64_t *rawValueData = value.getRawData();
448 for (
unsigned i = 0; i < numActiveWords; ++i)
449 emitter.emitSignedVarInt(rawValueData[i],
"dialect APInt word");
452 void writeAPFloatWithKnownSemantics(
const APFloat &value)
override {
453 writeAPIntWithKnownWidth(value.bitcastToAPInt());
456 void writeOwnedString(StringRef str)
override {
457 emitter.emitVarInt(stringSection.insert(str),
"dialect string");
460 void writeOwnedBlob(ArrayRef<char> blob)
override {
461 emitter.emitVarInt(blob.size(),
"dialect blob");
462 emitter.emitOwnedBlob(
463 ArrayRef<uint8_t>(
reinterpret_cast<const uint8_t *
>(blob.data()),
468 void writeUnownedBlob(ArrayRef<char> blob)
override {
469 emitter.emitVarInt(blob.size(),
"dialect blob");
471 ArrayRef<uint8_t>(
reinterpret_cast<const uint8_t *
>(blob.data()),
476 void writeOwnedBool(
bool value)
override {
477 emitter.emitByte(value,
"dialect bool");
480 int64_t getBytecodeVersion()
const override {
return bytecodeVersion; }
482 FailureOr<const DialectVersion *>
483 getDialectVersion(StringRef dialectName)
const override {
484 auto dialectEntry = dialectVersionMap.find(dialectName);
485 if (dialectEntry == dialectVersionMap.end())
487 return dialectEntry->getValue().get();
491 int64_t bytecodeVersion;
492 EncodingEmitter &emitter;
493 IRNumberingState &numberingState;
494 StringSectionBuilder &stringSection;
495 const DialectVersionMapT &dialectVersionMap;
499class PropertiesSectionBuilder {
501 PropertiesSectionBuilder(IRNumberingState &numberingState,
502 StringSectionBuilder &stringSection,
503 const BytecodeWriterConfig::Impl &
config)
504 : numberingState(numberingState), stringSection(stringSection),
509 std::optional<ssize_t>
emit(Operation *op) {
510 EncodingEmitter propertiesEmitter;
518 EncodingEmitter sizeEmitter;
519 sizeEmitter.emitVarInt(numberingState.getNumber(prop),
"properties size");
521 llvm::raw_svector_ostream os(scratch);
522 sizeEmitter.writeTo(os);
523 return emit(scratch);
526 EncodingEmitter emitter;
527 DialectWriter propertiesWriter(
config.bytecodeVersion, emitter,
528 numberingState, stringSection,
529 config.dialectVersionMap);
530 auto iface = cast<BytecodeOpInterface>(op);
531 iface.writeProperties(propertiesWriter);
533 llvm::raw_svector_ostream os(scratch);
535 return emit(scratch);
539 void write(EncodingEmitter &emitter) {
540 emitter.emitVarInt(propertiesStorage.size(),
"properties size");
541 if (propertiesStorage.empty())
543 for (
const auto &storage : propertiesStorage) {
544 if (storage.empty()) {
545 emitter.emitBytes(ArrayRef<uint8_t>(),
"empty properties");
548 emitter.emitBytes(ArrayRef(
reinterpret_cast<const uint8_t *
>(&storage[0]),
555 bool empty() {
return propertiesStorage.empty(); }
561 ssize_t
emit(ArrayRef<char> rawProperties) {
563 SmallVector<char> sizeScratch;
565 EncodingEmitter sizeEmitter;
566 sizeEmitter.emitVarInt(rawProperties.size(),
"properties");
567 llvm::raw_svector_ostream os(sizeScratch);
568 sizeEmitter.writeTo(os);
571 size_t index = propertiesStorage.size();
572 propertiesStorage.emplace_back();
573 std::vector<char> &newStorage = propertiesStorage.back();
574 size_t propertiesSize = sizeScratch.size() + rawProperties.size();
575 newStorage.reserve(propertiesSize);
576 llvm::append_range(newStorage, sizeScratch);
577 llvm::append_range(newStorage, rawProperties);
581 auto inserted = propertiesUniquing.insert(
582 std::make_pair(ArrayRef<char>(newStorage), index));
584 propertiesStorage.pop_back();
589 std::vector<std::vector<char>> propertiesStorage;
590 SmallVector<char> scratch;
592 IRNumberingState &numberingState;
593 StringSectionBuilder &stringSection;
594 const BytecodeWriterConfig::Impl &
config;
603 explicit RawEmitterOstream(EncodingEmitter &emitter) : emitter(emitter) {
608 void write_impl(
const char *ptr,
size_t size)
override {
609 emitter.emitBytes({
reinterpret_cast<const uint8_t *
>(ptr), size},
612 uint64_t current_pos()
const override {
return emitter.size(); }
615 EncodingEmitter &emitter;
619void EncodingEmitter::writeTo(
raw_ostream &os)
const {
621 os.reserveExtraSpace(size());
623 for (
auto &prevResult : prevResultList)
624 os.write((
const char *)prevResult.data(), prevResult.size());
625 os.write((
const char *)currentResult.data(), currentResult.size());
628void EncodingEmitter::emitMultiByteVarInt(uint64_t value, StringLiteral desc) {
632 uint64_t it = value >> 7;
633 for (
size_t numBytes = 2; numBytes < 9; ++numBytes) {
634 if (LLVM_LIKELY(it >>= 7) == 0) {
635 uint64_t encodedValue = (value << 1) | 0x1;
636 encodedValue <<= (numBytes - 1);
637 llvm::support::ulittle64_t encodedValueLE(encodedValue);
638 emitBytes({
reinterpret_cast<uint8_t *
>(&encodedValueLE), numBytes}, desc);
646 llvm::support::ulittle64_t valueLE(value);
647 emitBytes({
reinterpret_cast<uint8_t *
>(&valueLE),
sizeof(valueLE)}, desc);
655class BytecodeWriter {
657 BytecodeWriter(Operation *op,
const BytecodeWriterConfig &
config)
659 propertiesSection(numberingState, stringSection,
config.getImpl()) {}
662 LogicalResult write(Operation *rootOp, raw_ostream &os);
668 void writeDialectSection(EncodingEmitter &emitter);
673 void writeAttrTypeSection(EncodingEmitter &emitter);
678 LogicalResult writeBlock(EncodingEmitter &emitter,
Block *block);
679 LogicalResult writeOp(EncodingEmitter &emitter, Operation *op);
680 LogicalResult writeRegion(EncodingEmitter &emitter, Region *region);
681 LogicalResult writeIRSection(EncodingEmitter &emitter, Operation *op);
683 LogicalResult writeRegions(EncodingEmitter &emitter,
684 MutableArrayRef<Region> regions) {
685 return success(llvm::all_of(regions, [&](Region ®ion) {
686 return succeeded(writeRegion(emitter, ®ion));
693 void writeResourceSection(Operation *op, EncodingEmitter &emitter);
698 void writeStringSection(EncodingEmitter &emitter);
703 void writePropertiesSection(EncodingEmitter &emitter);
708 void writeUseListOrders(EncodingEmitter &emitter, uint8_t &opEncodingMask,
715 StringSectionBuilder stringSection;
718 IRNumberingState numberingState;
721 const BytecodeWriterConfig::Impl &
config;
724 PropertiesSectionBuilder propertiesSection;
729 EncodingEmitter emitter;
733 emitter.emitString(
"ML\xefR",
"bytecode header");
739 <<
"unsupported version requested " <<
config.bytecodeVersion
740 <<
", must be in range ["
743 emitter.emitVarInt(
config.bytecodeVersion,
"bytecode version");
746 emitter.emitNulTerminatedString(
config.producer,
"bytecode producer");
749 writeDialectSection(emitter);
752 writeAttrTypeSection(emitter);
755 if (
failed(writeIRSection(emitter, rootOp)))
759 writeResourceSection(rootOp, emitter);
762 writeStringSection(emitter);
766 writePropertiesSection(emitter);
767 else if (!propertiesSection.empty())
769 "unexpected properties emitted incompatible with bytecode <5");
785template <
typename EntriesT,
typename EntryCallbackT>
787 EntryCallbackT &&callback) {
788 for (
auto it = entries.begin(), e = entries.end(); it != e;) {
789 auto groupStart = it++;
793 it = std::find_if(it, e, [&](
const auto &entry) {
794 return entry.dialect != currentDialect;
798 emitter.emitVarInt(currentDialect->
number,
"dialect number");
799 emitter.emitVarInt(std::distance(groupStart, it),
"dialect offset");
802 for (
auto &entry : llvm::make_range(groupStart, it))
807void BytecodeWriter::writeDialectSection(EncodingEmitter &emitter) {
808 EncodingEmitter dialectEmitter;
811 auto dialects = numberingState.getDialects();
812 dialectEmitter.emitVarInt(llvm::size(dialects),
"dialects count");
813 for (DialectNumbering &dialect : dialects) {
815 size_t nameID = stringSection.insert(dialect.name);
818 dialectEmitter.emitVarInt(nameID,
"dialect name ID");
823 EncodingEmitter versionEmitter;
824 if (dialect.interface) {
826 DialectWriter versionWriter(
config.bytecodeVersion, versionEmitter,
827 numberingState, stringSection,
828 config.dialectVersionMap);
829 dialect.interface->writeVersion(versionWriter);
835 size_t versionAvailable = versionEmitter.size() > 0;
836 dialectEmitter.emitVarIntWithFlag(nameID, versionAvailable,
838 if (versionAvailable)
840 std::move(versionEmitter));
844 dialectEmitter.emitVarInt(size(numberingState.getOpNames()),
848 auto emitOpName = [&](OpNameNumbering &name) {
849 size_t stringId = stringSection.insert(name.name.stripDialect());
851 dialectEmitter.emitVarInt(stringId,
"dialect op name");
853 dialectEmitter.emitVarIntWithFlag(stringId, name.name.isRegistered(),
865void BytecodeWriter::writeAttrTypeSection(EncodingEmitter &emitter) {
866 EncodingEmitter attrTypeEmitter;
867 EncodingEmitter offsetEmitter;
868 offsetEmitter.emitVarInt(llvm::size(numberingState.getAttributes()),
870 offsetEmitter.emitVarInt(llvm::size(numberingState.getTypes()),
874 uint64_t prevOffset = 0;
875 auto emitAttrOrType = [&](
auto &entry) {
876 auto entryValue = entry.getValue();
878 auto emitAttrOrTypeRawImpl = [&]() ->
void {
879 RawEmitterOstream(attrTypeEmitter) << entryValue;
880 attrTypeEmitter.emitByte(0,
"attr/type separator");
882 auto emitAttrOrTypeImpl = [&]() ->
bool {
885 if (entryValue.template hasTrait<TypeTrait::IsMutable>() ||
886 entryValue.template hasTrait<AttributeTrait::IsMutable>()) {
887 emitAttrOrTypeRawImpl();
891 DialectWriter dialectWriter(
config.bytecodeVersion, attrTypeEmitter,
892 numberingState, stringSection,
893 config.dialectVersionMap);
894 if constexpr (std::is_same_v<std::decay_t<
decltype(entryValue)>,
Type>) {
895 for (
const auto &callback :
config.typeWriterCallbacks) {
896 if (succeeded(callback->write(entryValue, dialectWriter)))
900 entry.dialect->interface) {
901 if (succeeded(interface->writeType(entryValue, dialectWriter)))
905 for (
const auto &callback :
config.attributeWriterCallbacks) {
906 if (succeeded(callback->write(entryValue, dialectWriter)))
910 entry.dialect->interface) {
911 if (succeeded(interface->writeAttribute(entryValue, dialectWriter)))
918 emitAttrOrTypeRawImpl();
922 bool hasCustomEncoding = emitAttrOrTypeImpl();
925 uint64_t curOffset = attrTypeEmitter.size();
926 offsetEmitter.emitVarIntWithFlag(curOffset - prevOffset, hasCustomEncoding,
928 prevOffset = curOffset;
939 std::move(offsetEmitter));
947LogicalResult BytecodeWriter::writeBlock(EncodingEmitter &emitter,
950 bool hasArgs = !args.empty();
955 unsigned numOps = numberingState.getOperationCount(block);
956 emitter.emitVarIntWithFlag(numOps, hasArgs,
"block num ops");
960 emitter.emitVarInt(args.size(),
"block args count");
964 emitter.emitVarIntWithFlag(numberingState.getNumber(arg.getType()),
965 !isa<UnknownLoc>(argLoc),
"block arg type");
966 if (!isa<UnknownLoc>(argLoc))
967 emitter.emitVarInt(numberingState.getNumber(argLoc),
968 "block arg location");
970 emitter.emitVarInt(numberingState.getNumber(arg.getType()),
972 emitter.emitVarInt(numberingState.getNumber(argLoc),
973 "block arg location");
977 uint64_t maskOffset = emitter.size();
978 uint8_t encodingMask = 0;
979 emitter.emitByte(0,
"use-list separator");
980 writeUseListOrders(emitter, encodingMask, args);
982 emitter.patchByte(maskOffset, encodingMask,
"block patch encoding");
988 if (
failed(writeOp(emitter, &op)))
993LogicalResult BytecodeWriter::writeOp(EncodingEmitter &emitter,
Operation *op) {
994 emitter.emitVarInt(numberingState.getNumber(op->
getName()),
"op name ID");
999 uint64_t maskOffset = emitter.size();
1000 uint8_t opEncodingMask = 0;
1001 emitter.emitByte(0,
"op separator");
1004 emitter.emitVarInt(numberingState.getNumber(op->
getLoc()),
"op location");
1016 if (!attrs.empty()) {
1018 emitter.emitVarInt(numberingState.getNumber(attrs),
"op attrs count");
1024 std::optional<ssize_t> propertiesId = propertiesSection.emit(op);
1025 if (propertiesId.has_value()) {
1027 emitter.emitVarInt(*propertiesId,
"op properties ID");
1034 emitter.emitVarInt(numResults,
"op results count");
1036 emitter.emitVarInt(numberingState.getNumber(type),
"op result type");
1042 emitter.emitVarInt(numOperands,
"op operands count");
1044 emitter.emitVarInt(numberingState.getNumber(operand),
"op operand types");
1050 emitter.emitVarInt(numSuccessors,
"op successors count");
1052 emitter.emitVarInt(numberingState.getNumber(successor),
"op successor");
1066 emitter.patchByte(maskOffset, opEncodingMask,
"op encoding mask");
1073 bool isIsolatedFromAbove = numberingState.isIsolatedFromAbove(op);
1074 emitter.emitVarIntWithFlag(numRegions, isIsolatedFromAbove,
1075 "op regions count");
1079 if (isIsolatedFromAbove &&
1081 EncodingEmitter regionEmitter;
1093void BytecodeWriter::writeUseListOrders(EncodingEmitter &emitter,
1094 uint8_t &opEncodingMask,
1098 for (
auto item : llvm::enumerate(range)) {
1099 auto value = item.value();
1102 if (value.use_empty() || value.hasOneUse())
1108 bool alreadyOrdered =
true;
1109 auto &firstUse = *value.use_begin();
1111 firstUse, numberingState.getNumber(firstUse.getOwner()));
1112 llvm::SmallVector<std::pair<unsigned, uint64_t>> useListPairs(
1115 for (
auto use : llvm::drop_begin(llvm::enumerate(value.getUses()))) {
1117 use.value(), numberingState.getNumber(use.value().getOwner()));
1121 alreadyOrdered &= (prevID > currentID);
1122 useListPairs.push_back({use.index(), currentID});
1132 useListPairs.begin(), useListPairs.end(),
1133 [](
auto elem1,
auto elem2) { return elem1.second > elem2.second; });
1135 map.try_emplace(item.index(), llvm::map_range(useListPairs, [](
auto elem) {
1146 if (range.size() != 1) {
1147 emitter.emitVarInt(map.size(),
"custom use-list size");
1150 for (
const auto &item : map) {
1151 auto resultIdx = item.getFirst();
1152 auto useListOrder = item.getSecond();
1157 size_t shuffledElements =
1158 llvm::count_if(llvm::enumerate(useListOrder),
1159 [](
auto item) {
return item.index() != item.value(); });
1160 bool indexPairEncoding = shuffledElements < (useListOrder.size() / 2);
1163 if (range.size() != 1)
1164 emitter.emitVarInt(resultIdx,
"use-list result index");
1166 if (indexPairEncoding) {
1167 emitter.emitVarIntWithFlag(shuffledElements * 2, indexPairEncoding,
1168 "use-list index pair size");
1169 for (
auto pair : llvm::enumerate(useListOrder)) {
1170 if (pair.index() != pair.value()) {
1171 emitter.emitVarInt(pair.value(),
"use-list index pair first");
1172 emitter.emitVarInt(pair.index(),
"use-list index pair second");
1176 emitter.emitVarIntWithFlag(useListOrder.size(), indexPairEncoding,
1178 for (
const auto &
index : useListOrder)
1179 emitter.emitVarInt(
index,
"use-list order");
1184LogicalResult BytecodeWriter::writeRegion(EncodingEmitter &emitter,
1188 if (region->
empty()) {
1189 emitter.emitVarInt( 0,
"region block count empty");
1194 unsigned numBlocks, numValues;
1195 std::tie(numBlocks, numValues) = numberingState.getBlockValueCount(region);
1196 emitter.emitVarInt(numBlocks,
"region block count");
1197 emitter.emitVarInt(numValues,
"region value count");
1200 for (
Block &block : *region)
1201 if (
failed(writeBlock(emitter, &block)))
1206LogicalResult BytecodeWriter::writeIRSection(EncodingEmitter &emitter,
1208 EncodingEmitter irEmitter;
1213 irEmitter.emitVarIntWithFlag( 1,
false,
"ir section");
1216 if (
failed(writeOp(irEmitter, op)))
1234 ResourceBuilder(EncodingEmitter &emitter, StringSectionBuilder &stringSection,
1235 PostProcessFn postProcessFn,
bool shouldElideData)
1236 : emitter(emitter), stringSection(stringSection),
1237 postProcessFn(postProcessFn), shouldElideData(shouldElideData) {}
1238 ~ResourceBuilder()
override =
default;
1240 void buildBlob(StringRef key, ArrayRef<char> data,
1241 uint32_t dataAlignment)
final {
1242 if (!shouldElideData)
1243 emitter.emitOwnedBlobAndAlignment(data, dataAlignment,
"resource blob");
1244 postProcessFn(key, AsmResourceEntryKind::Blob);
1246 void buildBool(StringRef key,
bool data)
final {
1247 if (!shouldElideData)
1248 emitter.emitByte(data,
"resource bool");
1249 postProcessFn(key, AsmResourceEntryKind::Bool);
1251 void buildString(StringRef key, StringRef data)
final {
1252 if (!shouldElideData)
1253 emitter.emitVarInt(stringSection.insert(data),
"resource string");
1254 postProcessFn(key, AsmResourceEntryKind::String);
1258 EncodingEmitter &emitter;
1259 StringSectionBuilder &stringSection;
1260 PostProcessFn postProcessFn;
1261 bool shouldElideData =
false;
1265void BytecodeWriter::writeResourceSection(
Operation *op,
1266 EncodingEmitter &emitter) {
1267 EncodingEmitter resourceEmitter;
1268 EncodingEmitter resourceOffsetEmitter;
1269 uint64_t prevOffset = 0;
1276 uint64_t curOffset = resourceEmitter.size();
1277 curResourceEntries.emplace_back(key, kind, curOffset - prevOffset);
1278 prevOffset = curOffset;
1282 auto emitResourceGroup = [&](uint64_t key) {
1283 resourceOffsetEmitter.emitVarInt(key,
"resource group key");
1284 resourceOffsetEmitter.emitVarInt(curResourceEntries.size(),
1285 "resource group size");
1286 for (
auto [key, kind, size] : curResourceEntries) {
1287 resourceOffsetEmitter.emitVarInt(stringSection.insert(key),
1289 resourceOffsetEmitter.emitVarInt(size,
"resource size");
1290 resourceOffsetEmitter.emitByte(kind,
"resource kind");
1295 ResourceBuilder entryBuilder(resourceEmitter, stringSection,
1296 appendResourceOffset,
1297 config.shouldElideResourceData);
1300 resourceOffsetEmitter.emitVarInt(
config.externalResourcePrinters.size(),
1301 "external resource printer count");
1302 for (
const auto &printer :
config.externalResourcePrinters) {
1303 curResourceEntries.clear();
1304 printer->buildResources(op, entryBuilder);
1305 emitResourceGroup(stringSection.insert(printer->getName()));
1309 for (DialectNumbering &dialect : numberingState.getDialects()) {
1310 if (!dialect.asmInterface)
1312 curResourceEntries.clear();
1313 dialect.asmInterface->buildResources(op, dialect.resources, entryBuilder);
1318 for (
const auto &resource : dialect.resourceMap)
1319 if (resource.second->isDeclaration)
1323 if (!curResourceEntries.empty())
1324 emitResourceGroup(dialect.number);
1328 if (resourceOffsetEmitter.size() == 0)
1332 std::move(resourceOffsetEmitter));
1340void BytecodeWriter::writeStringSection(EncodingEmitter &emitter) {
1341 EncodingEmitter stringEmitter;
1342 stringSection.write(stringEmitter);
1350void BytecodeWriter::writePropertiesSection(EncodingEmitter &emitter) {
1351 EncodingEmitter propertiesEmitter;
1352 propertiesSection.write(propertiesEmitter);
1354 std::move(propertiesEmitter));
1363 BytecodeWriter writer(op,
config);
1364 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.
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.
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 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...
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...
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.
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...
@ 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.
const FrozenRewritePatternSet GreedyRewriteConfig config
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.
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.