18#include "llvm/ADT/ArrayRef.h"
19#include "llvm/ADT/CachedHashString.h"
20#include "llvm/ADT/MapVector.h"
21#include "llvm/ADT/ScopeExit.h"
22#include "llvm/ADT/SmallVector.h"
23#include "llvm/Support/Debug.h"
24#include "llvm/Support/DebugLog.h"
25#include "llvm/Support/Endian.h"
26#include "llvm/Support/MathExtras.h"
27#include "llvm/Support/raw_ostream.h"
30#define DEBUG_TYPE "mlir-bytecode-writer"
70 :
impl(std::make_unique<
Impl>(producer)) {}
83 return impl->attributeWriterCallbacks;
88 return impl->typeWriterCallbacks;
93 impl->attributeWriterCallbacks.emplace_back(std::move(callback));
98 impl->typeWriterCallbacks.emplace_back(std::move(callback));
102 std::unique_ptr<AsmResourcePrinter> printer) {
103 impl->externalResourcePrinters.emplace_back(std::move(printer));
107 bool shouldElideResourceData) {
108 impl->shouldElideResourceData = shouldElideResourceData;
116 return impl->shouldElideLocations;
120 impl->bytecodeVersion = bytecodeVersion;
124 return impl->bytecodeVersion;
127llvm::StringMap<std::unique_ptr<DialectVersion>> &
129 return impl->dialectVersionMap;
133 llvm::StringRef dialectName,
134 std::unique_ptr<DialectVersion> dialectVersion)
const {
135 assert(!
impl->dialectVersionMap.contains(dialectName) &&
136 "cannot override a previously set dialect version");
137 impl->dialectVersionMap.insert({dialectName, std::move(dialectVersion)});
150class EncodingEmitter {
152 EncodingEmitter() =
default;
153 EncodingEmitter(
const EncodingEmitter &) =
delete;
154 EncodingEmitter &operator=(
const EncodingEmitter &) =
delete;
157 void writeTo(raw_ostream &os)
const;
160 size_t size()
const {
return prevResultSize + currentResult.size(); }
167 void patchByte(uint64_t offset, uint8_t value, StringLiteral desc) {
168 LDBG() <<
"patchByte(" << offset <<
',' << uint64_t(value) <<
")\t" << desc;
169 assert(offset < size() && offset >= prevResultSize &&
170 "cannot patch previously emitted data");
171 currentResult[offset - prevResultSize] = value;
176 void emitOwnedBlob(ArrayRef<uint8_t> data, StringLiteral desc) {
177 LDBG() <<
"emitOwnedBlob(" << data.size() <<
"b)\t" << desc;
179 appendResult(std::move(currentResult));
180 appendOwnedResult(data);
187 void emitOwnedBlobAndAlignment(ArrayRef<uint8_t> data, uint32_t alignment,
188 StringLiteral desc) {
189 emitVarInt(alignment, desc);
190 emitVarInt(data.size(), desc);
193 emitOwnedBlob(data, desc);
195 void emitOwnedBlobAndAlignment(ArrayRef<char> data, uint32_t alignment,
196 StringLiteral desc) {
197 ArrayRef<uint8_t> castedData(
reinterpret_cast<const uint8_t *
>(data.data()),
199 emitOwnedBlobAndAlignment(castedData, alignment, desc);
203 void alignTo(
unsigned alignment) {
206 assert(llvm::isPowerOf2_32(alignment) &&
"expected valid alignment");
210 size_t curOffset = size();
211 size_t paddingSize = llvm::alignTo(curOffset, alignment) - curOffset;
212 while (paddingSize--)
216 requiredAlignment = std::max(requiredAlignment, alignment);
223 template <
typename T>
224 void emitByte(T
byte, StringLiteral desc) {
225 LDBG() <<
"emitByte(" << uint64_t(
byte) <<
")\t" << desc;
226 currentResult.push_back(
static_cast<uint8_t
>(
byte));
230 void emitBytes(ArrayRef<uint8_t> bytes, StringLiteral desc) {
231 LDBG() <<
"emitBytes(" << bytes.size() <<
"b)\t" << desc;
232 llvm::append_range(currentResult, bytes);
242 void emitVarInt(uint64_t value, StringLiteral desc) {
243 LDBG() <<
"emitVarInt(" << value <<
")\t" << desc;
247 if ((value >> 7) == 0)
248 return emitByte((value << 1) | 0x1, desc);
249 emitMultiByteVarInt(value, desc);
256 void emitSignedVarInt(uint64_t value, StringLiteral desc) {
257 emitVarInt((value << 1) ^ (uint64_t)((int64_t)value >> 63), desc);
262 void emitVarIntWithFlag(uint64_t value,
bool flag, StringLiteral desc) {
263 emitVarInt((value << 1) | (flag ? 1 : 0), desc);
270 void emitNulTerminatedString(StringRef str, StringLiteral desc) {
271 emitString(str, desc);
272 emitByte(0,
"null terminator");
276 void emitString(StringRef str, StringLiteral desc) {
277 emitBytes({
reinterpret_cast<const uint8_t *
>(str.data()), str.size()},
290 uint64_t codeOffset = currentResult.size();
291 emitByte(code,
"section code");
292 emitVarInt(emitter.size(),
"section size");
295 unsigned emitterAlign = emitter.requiredAlignment;
296 if (emitterAlign > 1) {
297 if (size() & (emitterAlign - 1)) {
298 emitVarInt(emitterAlign,
"section alignment");
299 alignTo(emitterAlign);
303 currentResult[codeOffset] |= 0b10000000;
307 requiredAlignment = std::max(requiredAlignment, emitterAlign);
313 appendResult(std::move(currentResult));
314 for (std::vector<uint8_t> &
result : emitter.prevResultStorage)
315 prevResultStorage.push_back(std::move(
result));
316 llvm::append_range(prevResultList, emitter.prevResultList);
317 prevResultSize += emitter.prevResultSize;
318 appendResult(std::move(emitter.currentResult));
326 LLVM_ATTRIBUTE_NOINLINE
void emitMultiByteVarInt(uint64_t value,
330 void appendResult(std::vector<uint8_t> &&
result) {
333 prevResultStorage.emplace_back(std::move(
result));
334 appendOwnedResult(prevResultStorage.back());
336 void appendOwnedResult(ArrayRef<uint8_t>
result) {
339 prevResultSize +=
result.size();
340 prevResultList.emplace_back(
result);
348 std::vector<uint8_t> currentResult;
349 std::vector<ArrayRef<uint8_t>> prevResultList;
350 std::vector<std::vector<uint8_t>> prevResultStorage;
354 size_t prevResultSize = 0;
357 unsigned requiredAlignment = 1;
366class StringSectionBuilder {
370 size_t insert(StringRef str) {
371 auto it = strings.insert({llvm::CachedHashStringRef(str), strings.size()});
372 return it.first->second;
376 void write(EncodingEmitter &emitter) {
377 emitter.emitVarInt(strings.size(),
"string section size");
381 for (
const auto &it : llvm::reverse(strings))
382 emitter.emitVarInt(it.first.size() + 1,
"string size");
384 for (
const auto &it : strings)
385 emitter.emitNulTerminatedString(it.first.val(),
"string");
391 llvm::MapVector<llvm::CachedHashStringRef, size_t> strings;
396 using DialectVersionMapT = llvm::StringMap<std::unique_ptr<DialectVersion>>;
399 DialectWriter(int64_t bytecodeVersion, EncodingEmitter &emitter,
400 IRNumberingState &numberingState,
401 StringSectionBuilder &stringSection,
402 const DialectVersionMapT &dialectVersionMap)
403 : bytecodeVersion(bytecodeVersion), emitter(emitter),
404 numberingState(numberingState), stringSection(stringSection),
405 dialectVersionMap(dialectVersionMap) {}
411 void writeAttribute(Attribute attr)
override {
412 emitter.emitVarInt(numberingState.getNumber(attr),
"dialect attr");
414 void writeOptionalAttribute(Attribute attr)
override {
416 emitter.emitVarInt(0,
"dialect optional attr none");
419 emitter.emitVarIntWithFlag(numberingState.getNumber(attr),
true,
420 "dialect optional attr");
423 void writeType(Type type)
override {
424 emitter.emitVarInt(numberingState.getNumber(type),
"dialect type");
427 void writeResourceHandle(
const AsmDialectResourceHandle &resource)
override {
428 emitter.emitVarInt(numberingState.getNumber(resource),
"dialect resource");
435 void writeVarInt(uint64_t value)
override {
436 emitter.emitVarInt(value,
"dialect writer");
439 void writeSignedVarInt(int64_t value)
override {
440 emitter.emitSignedVarInt(value,
"dialect writer");
443 void writeAPIntWithKnownWidth(
const APInt &value)
override {
444 size_t bitWidth = value.getBitWidth();
449 return emitter.emitByte(value.getLimitedValue(),
"dialect APInt");
453 return emitter.emitSignedVarInt(value.getLimitedValue(),
"dialect APInt");
458 unsigned numActiveWords = value.getActiveWords();
459 emitter.emitVarInt(numActiveWords,
"dialect APInt word count");
461 const uint64_t *rawValueData = value.getRawData();
462 for (
unsigned i = 0; i < numActiveWords; ++i)
463 emitter.emitSignedVarInt(rawValueData[i],
"dialect APInt word");
466 void writeAPFloatWithKnownSemantics(
const APFloat &value)
override {
467 writeAPIntWithKnownWidth(value.bitcastToAPInt());
470 void writeOwnedString(StringRef str)
override {
471 emitter.emitVarInt(stringSection.insert(str),
"dialect string");
474 void writeOwnedBlob(ArrayRef<char> blob)
override {
475 emitter.emitVarInt(blob.size(),
"dialect blob");
476 emitter.emitOwnedBlob(
477 ArrayRef<uint8_t>(
reinterpret_cast<const uint8_t *
>(blob.data()),
482 void writeUnownedBlob(ArrayRef<char> blob)
override {
483 emitter.emitVarInt(blob.size(),
"dialect blob");
485 ArrayRef<uint8_t>(
reinterpret_cast<const uint8_t *
>(blob.data()),
490 void writeOwnedBool(
bool value)
override {
491 emitter.emitByte(value,
"dialect bool");
494 int64_t getBytecodeVersion()
const override {
return bytecodeVersion; }
496 FailureOr<const DialectVersion *>
497 getDialectVersion(StringRef dialectName)
const override {
498 auto dialectEntry = dialectVersionMap.find(dialectName);
499 if (dialectEntry == dialectVersionMap.end())
501 return dialectEntry->getValue().get();
505 int64_t bytecodeVersion;
506 EncodingEmitter &emitter;
507 IRNumberingState &numberingState;
508 StringSectionBuilder &stringSection;
509 const DialectVersionMapT &dialectVersionMap;
513class PropertiesSectionBuilder {
515 PropertiesSectionBuilder(IRNumberingState &numberingState,
516 StringSectionBuilder &stringSection,
517 const BytecodeWriterConfig::Impl &config)
518 : numberingState(numberingState), stringSection(stringSection),
523 std::optional<ssize_t>
emit(Operation *op) {
524 EncodingEmitter propertiesEmitter;
532 EncodingEmitter sizeEmitter;
533 sizeEmitter.emitVarInt(numberingState.getNumber(prop),
"properties size");
535 llvm::raw_svector_ostream os(scratch);
536 sizeEmitter.writeTo(os);
537 return emit(scratch);
540 EncodingEmitter emitter;
541 DialectWriter propertiesWriter(config.bytecodeVersion, emitter,
542 numberingState, stringSection,
543 config.dialectVersionMap);
544 auto iface = cast<BytecodeOpInterface>(op);
545 iface.writeProperties(propertiesWriter);
547 llvm::raw_svector_ostream os(scratch);
549 return emit(scratch);
553 void write(EncodingEmitter &emitter) {
554 emitter.emitVarInt(propertiesStorage.size(),
"properties size");
555 if (propertiesStorage.empty())
557 for (
const auto &storage : propertiesStorage) {
558 if (storage.empty()) {
559 emitter.emitBytes(ArrayRef<uint8_t>(),
"empty properties");
562 emitter.emitBytes(ArrayRef(
reinterpret_cast<const uint8_t *
>(&storage[0]),
569 bool empty() {
return propertiesStorage.empty(); }
575 ssize_t
emit(ArrayRef<char> rawProperties) {
577 SmallVector<char> sizeScratch;
579 EncodingEmitter sizeEmitter;
580 sizeEmitter.emitVarInt(rawProperties.size(),
"properties");
581 llvm::raw_svector_ostream os(sizeScratch);
582 sizeEmitter.writeTo(os);
585 size_t index = propertiesStorage.size();
586 propertiesStorage.emplace_back();
587 std::vector<char> &newStorage = propertiesStorage.back();
588 size_t propertiesSize = sizeScratch.size() + rawProperties.size();
589 newStorage.reserve(propertiesSize);
590 llvm::append_range(newStorage, sizeScratch);
591 llvm::append_range(newStorage, rawProperties);
595 auto inserted = propertiesUniquing.insert(
596 std::make_pair(ArrayRef<char>(newStorage), index));
598 propertiesStorage.pop_back();
603 std::vector<std::vector<char>> propertiesStorage;
604 SmallVector<char> scratch;
606 IRNumberingState &numberingState;
607 StringSectionBuilder &stringSection;
608 const BytecodeWriterConfig::Impl &config;
617 explicit RawEmitterOstream(EncodingEmitter &emitter) : emitter(emitter) {
622 void write_impl(
const char *ptr,
size_t size)
override {
623 emitter.emitBytes({
reinterpret_cast<const uint8_t *
>(ptr), size},
626 uint64_t current_pos()
const override {
return emitter.size(); }
629 EncodingEmitter &emitter;
633void EncodingEmitter::writeTo(
raw_ostream &os)
const {
635 os.reserveExtraSpace(size());
637 for (
auto &prevResult : prevResultList)
638 os.write((
const char *)prevResult.data(), prevResult.size());
639 os.write((
const char *)currentResult.data(), currentResult.size());
642void EncodingEmitter::emitMultiByteVarInt(uint64_t value, StringLiteral desc) {
646 uint64_t it = value >> 7;
647 for (
size_t numBytes = 2; numBytes < 9; ++numBytes) {
648 if (LLVM_LIKELY(it >>= 7) == 0) {
649 uint64_t encodedValue = (value << 1) | 0x1;
650 encodedValue <<= (numBytes - 1);
651 llvm::support::ulittle64_t encodedValueLE(encodedValue);
652 emitBytes({
reinterpret_cast<uint8_t *
>(&encodedValueLE), numBytes}, desc);
660 llvm::support::ulittle64_t valueLE(value);
661 emitBytes({
reinterpret_cast<uint8_t *
>(&valueLE),
sizeof(valueLE)}, desc);
669class BytecodeWriter {
671 BytecodeWriter(Operation *op,
const BytecodeWriterConfig &config)
672 : numberingState(op, config), config(config.getImpl()),
673 propertiesSection(numberingState, stringSection, config.getImpl()) {}
676 LogicalResult write(Operation *rootOp, raw_ostream &os);
682 void writeDialectSection(EncodingEmitter &emitter);
687 void writeAttrTypeSection(EncodingEmitter &emitter);
692 LogicalResult writeBlock(EncodingEmitter &emitter,
Block *block);
693 LogicalResult writeOp(EncodingEmitter &emitter, Operation *op);
694 LogicalResult writeRegion(EncodingEmitter &emitter, Region *region);
695 LogicalResult writeIRSection(EncodingEmitter &emitter, Operation *op);
697 LogicalResult writeRegions(EncodingEmitter &emitter,
698 MutableArrayRef<Region> regions) {
699 return success(llvm::all_of(regions, [&](Region ®ion) {
700 return succeeded(writeRegion(emitter, ®ion));
707 void writeResourceSection(Operation *op, EncodingEmitter &emitter);
712 void writeStringSection(EncodingEmitter &emitter);
717 void writePropertiesSection(EncodingEmitter &emitter);
722 void writeUseListOrders(EncodingEmitter &emitter, uint8_t &opEncodingMask,
729 StringSectionBuilder stringSection;
732 IRNumberingState numberingState;
735 const BytecodeWriterConfig::Impl &config;
738 PropertiesSectionBuilder propertiesSection;
743 EncodingEmitter emitter;
747 emitter.emitString(
"ML\xefR",
"bytecode header");
753 <<
"unsupported version requested " << config.bytecodeVersion
754 <<
", must be in range ["
757 emitter.emitVarInt(config.bytecodeVersion,
"bytecode version");
760 emitter.emitNulTerminatedString(config.producer,
"bytecode producer");
763 writeDialectSection(emitter);
766 writeAttrTypeSection(emitter);
769 if (
failed(writeIRSection(emitter, rootOp)))
773 writeResourceSection(rootOp, emitter);
776 writeStringSection(emitter);
780 writePropertiesSection(emitter);
781 else if (!propertiesSection.empty())
783 "unexpected properties emitted incompatible with bytecode <5");
799template <
typename EntriesT,
typename EntryCallbackT>
801 EntryCallbackT &&callback) {
802 for (
auto it = entries.begin(), e = entries.end(); it != e;) {
803 auto groupStart = it++;
807 it = std::find_if(it, e, [&](
const auto &entry) {
808 return entry.dialect != currentDialect;
812 emitter.emitVarInt(currentDialect->
number,
"dialect number");
813 emitter.emitVarInt(std::distance(groupStart, it),
"dialect offset");
816 for (
auto &entry : llvm::make_range(groupStart, it))
821void BytecodeWriter::writeDialectSection(EncodingEmitter &emitter) {
822 EncodingEmitter dialectEmitter;
825 auto dialects = numberingState.getDialects();
826 dialectEmitter.emitVarInt(llvm::size(dialects),
"dialects count");
827 for (DialectNumbering &dialect : dialects) {
829 size_t nameID = stringSection.insert(dialect.name);
832 dialectEmitter.emitVarInt(nameID,
"dialect name ID");
837 EncodingEmitter versionEmitter;
838 if (dialect.interface) {
840 DialectWriter versionWriter(config.bytecodeVersion, versionEmitter,
841 numberingState, stringSection,
842 config.dialectVersionMap);
843 dialect.interface->writeVersion(versionWriter);
849 size_t versionAvailable = versionEmitter.size() > 0;
850 dialectEmitter.emitVarIntWithFlag(nameID, versionAvailable,
852 if (versionAvailable)
854 std::move(versionEmitter));
858 dialectEmitter.emitVarInt(size(numberingState.getOpNames()),
862 auto emitOpName = [&](OpNameNumbering &name) {
863 size_t stringId = stringSection.insert(name.name.stripDialect());
865 dialectEmitter.emitVarInt(stringId,
"dialect op name");
867 dialectEmitter.emitVarIntWithFlag(stringId, name.name.isRegistered(),
879void BytecodeWriter::writeAttrTypeSection(EncodingEmitter &emitter) {
880 EncodingEmitter attrTypeEmitter;
881 EncodingEmitter offsetEmitter;
882 offsetEmitter.emitVarInt(llvm::size(numberingState.getAttributes()),
884 offsetEmitter.emitVarInt(llvm::size(numberingState.getTypes()),
888 uint64_t prevOffset = 0;
889 auto emitAttrOrType = [&](
auto &entry) {
890 auto entryValue = entry.getValue();
892 auto emitAttrOrTypeRawImpl = [&]() ->
void {
893 RawEmitterOstream(attrTypeEmitter) << entryValue;
894 attrTypeEmitter.emitByte(0,
"attr/type separator");
896 auto emitAttrOrTypeImpl = [&]() ->
bool {
899 if (entryValue.template hasTrait<TypeTrait::IsMutable>() ||
900 entryValue.template hasTrait<AttributeTrait::IsMutable>()) {
901 emitAttrOrTypeRawImpl();
905 DialectWriter dialectWriter(config.bytecodeVersion, attrTypeEmitter,
906 numberingState, stringSection,
907 config.dialectVersionMap);
908 if constexpr (std::is_same_v<std::decay_t<
decltype(entryValue)>,
Type>) {
909 for (
const auto &callback : config.typeWriterCallbacks) {
910 if (succeeded(callback->write(entryValue, dialectWriter)))
913 if (
const BytecodeDialectInterface *interface =
914 entry.dialect->interface) {
915 if (succeeded(interface->writeType(entryValue, dialectWriter)))
919 for (
const auto &callback : config.attributeWriterCallbacks) {
920 if (succeeded(callback->write(entryValue, dialectWriter)))
923 if (
const BytecodeDialectInterface *interface =
924 entry.dialect->interface) {
925 if (succeeded(interface->writeAttribute(entryValue, dialectWriter)))
932 emitAttrOrTypeRawImpl();
936 bool hasCustomEncoding = emitAttrOrTypeImpl();
939 uint64_t curOffset = attrTypeEmitter.size();
940 offsetEmitter.emitVarIntWithFlag(curOffset - prevOffset, hasCustomEncoding,
942 prevOffset = curOffset;
953 std::move(offsetEmitter));
961LogicalResult BytecodeWriter::writeBlock(EncodingEmitter &emitter,
964 bool hasArgs = !args.empty();
969 unsigned numOps = numberingState.getOperationCount(block);
970 emitter.emitVarIntWithFlag(numOps, hasArgs,
"block num ops");
974 emitter.emitVarInt(args.size(),
"block args count");
978 emitter.emitVarIntWithFlag(numberingState.getNumber(arg.getType()),
979 !isa<UnknownLoc>(argLoc),
"block arg type");
980 if (!isa<UnknownLoc>(argLoc))
981 emitter.emitVarInt(numberingState.getNumber(argLoc),
982 "block arg location");
984 emitter.emitVarInt(numberingState.getNumber(arg.getType()),
986 emitter.emitVarInt(numberingState.getNumber(argLoc),
987 "block arg location");
991 uint64_t maskOffset = emitter.size();
992 uint8_t encodingMask = 0;
993 emitter.emitByte(0,
"use-list separator");
994 writeUseListOrders(emitter, encodingMask, args);
996 emitter.patchByte(maskOffset, encodingMask,
"block patch encoding");
1002 if (
failed(writeOp(emitter, &op)))
1028 inherentAttrs.
append(attr);
1029 if (!inherentAttrs.
empty() &&
1033 auto diag = mlir::emitError(op->getLoc());
1042LogicalResult BytecodeWriter::writeOp(EncodingEmitter &emitter,
Operation *op) {
1043 emitter.emitVarInt(numberingState.getNumber(op->
getName()),
"op name ID");
1048 uint64_t maskOffset = emitter.size();
1049 uint8_t opEncodingMask = 0;
1050 emitter.emitByte(0,
"op separator");
1053 emitter.emitVarInt(numberingState.getNumber(op->
getLoc()),
"op location");
1065 allAttrs.
append(name, attr);
1076 <<
"cannot emit bytecode before version "
1078 <<
" without losing operation properties";
1080 if (!attrs.empty()) {
1082 emitter.emitVarInt(numberingState.getNumber(attrs),
"op attrs count");
1088 std::optional<ssize_t> propertiesId = propertiesSection.emit(op);
1089 if (propertiesId.has_value()) {
1091 emitter.emitVarInt(*propertiesId,
"op properties ID");
1098 emitter.emitVarInt(numResults,
"op results count");
1100 emitter.emitVarInt(numberingState.getNumber(type),
"op result type");
1106 emitter.emitVarInt(numOperands,
"op operands count");
1108 emitter.emitVarInt(numberingState.getNumber(operand),
"op operand types");
1114 emitter.emitVarInt(numSuccessors,
"op successors count");
1116 emitter.emitVarInt(numberingState.getNumber(successor),
"op successor");
1130 emitter.patchByte(maskOffset, opEncodingMask,
"op encoding mask");
1137 bool isIsolatedFromAbove = numberingState.isIsolatedFromAbove(op);
1138 emitter.emitVarIntWithFlag(numRegions, isIsolatedFromAbove,
1139 "op regions count");
1143 if (isIsolatedFromAbove &&
1145 EncodingEmitter regionEmitter;
1157void BytecodeWriter::writeUseListOrders(EncodingEmitter &emitter,
1158 uint8_t &opEncodingMask,
1161 llvm::MapVector<unsigned, llvm::SmallVector<unsigned>> map;
1162 for (
auto item : llvm::enumerate(range)) {
1163 auto value = item.value();
1166 if (value.use_empty() || value.hasOneUse())
1172 bool alreadyOrdered =
true;
1173 auto &firstUse = *value.use_begin();
1175 firstUse, numberingState.getNumber(firstUse.getOwner()));
1176 llvm::SmallVector<std::pair<unsigned, uint64_t>> useListPairs(
1179 for (
auto use : llvm::drop_begin(llvm::enumerate(value.getUses()))) {
1181 use.value(), numberingState.getNumber(use.value().getOwner()));
1185 alreadyOrdered &= (prevID > currentID);
1186 useListPairs.push_back({use.index(), currentID});
1196 useListPairs.begin(), useListPairs.end(),
1197 [](
auto elem1,
auto elem2) { return elem1.second > elem2.second; });
1199 map.try_emplace(item.index(), llvm::map_range(useListPairs, [](
auto elem) {
1210 if (range.size() != 1) {
1211 emitter.emitVarInt(map.size(),
"custom use-list size");
1214 for (
const auto &[resultIdx, useListOrder] : map) {
1218 size_t shuffledElements =
1219 llvm::count_if(llvm::enumerate(useListOrder),
1220 [](
auto item) {
return item.index() != item.value(); });
1221 bool indexPairEncoding = shuffledElements < (useListOrder.size() / 2);
1224 if (range.size() != 1)
1225 emitter.emitVarInt(resultIdx,
"use-list result index");
1227 if (indexPairEncoding) {
1228 emitter.emitVarIntWithFlag(shuffledElements * 2, indexPairEncoding,
1229 "use-list index pair size");
1230 for (
auto pair : llvm::enumerate(useListOrder)) {
1231 if (pair.index() != pair.value()) {
1232 emitter.emitVarInt(pair.value(),
"use-list index pair first");
1233 emitter.emitVarInt(pair.index(),
"use-list index pair second");
1237 emitter.emitVarIntWithFlag(useListOrder.size(), indexPairEncoding,
1239 for (
const auto &
index : useListOrder)
1240 emitter.emitVarInt(
index,
"use-list order");
1245LogicalResult BytecodeWriter::writeRegion(EncodingEmitter &emitter,
1249 if (region->
empty()) {
1250 emitter.emitVarInt( 0,
"region block count empty");
1255 unsigned numBlocks, numValues;
1256 std::tie(numBlocks, numValues) = numberingState.getBlockValueCount(region);
1257 emitter.emitVarInt(numBlocks,
"region block count");
1258 emitter.emitVarInt(numValues,
"region value count");
1261 for (
Block &block : *region)
1262 if (
failed(writeBlock(emitter, &block)))
1267LogicalResult BytecodeWriter::writeIRSection(EncodingEmitter &emitter,
1269 EncodingEmitter irEmitter;
1274 irEmitter.emitVarIntWithFlag( 1,
false,
"ir section");
1277 if (
failed(writeOp(irEmitter, op)))
1295 ResourceBuilder(EncodingEmitter &emitter, StringSectionBuilder &stringSection,
1296 PostProcessFn postProcessFn,
bool shouldElideData)
1297 : emitter(emitter), stringSection(stringSection),
1298 postProcessFn(postProcessFn), shouldElideData(shouldElideData) {}
1299 ~ResourceBuilder()
override =
default;
1301 void buildBlob(StringRef key, ArrayRef<char> data,
1302 uint32_t dataAlignment)
final {
1303 if (!shouldElideData)
1304 emitter.emitOwnedBlobAndAlignment(data, dataAlignment,
"resource blob");
1305 postProcessFn(key, AsmResourceEntryKind::Blob);
1307 void buildBool(StringRef key,
bool data)
final {
1308 if (!shouldElideData)
1309 emitter.emitByte(data,
"resource bool");
1310 postProcessFn(key, AsmResourceEntryKind::Bool);
1312 void buildString(StringRef key, StringRef data)
final {
1313 if (!shouldElideData)
1314 emitter.emitVarInt(stringSection.insert(data),
"resource string");
1315 postProcessFn(key, AsmResourceEntryKind::String);
1319 EncodingEmitter &emitter;
1320 StringSectionBuilder &stringSection;
1321 PostProcessFn postProcessFn;
1322 bool shouldElideData =
false;
1326void BytecodeWriter::writeResourceSection(
Operation *op,
1327 EncodingEmitter &emitter) {
1328 EncodingEmitter resourceEmitter;
1329 EncodingEmitter resourceOffsetEmitter;
1330 uint64_t prevOffset = 0;
1337 uint64_t curOffset = resourceEmitter.size();
1338 curResourceEntries.emplace_back(key, kind, curOffset - prevOffset);
1339 prevOffset = curOffset;
1343 auto emitResourceGroup = [&](uint64_t key) {
1344 resourceOffsetEmitter.emitVarInt(key,
"resource group key");
1345 resourceOffsetEmitter.emitVarInt(curResourceEntries.size(),
1346 "resource group size");
1347 for (
auto [key, kind, size] : curResourceEntries) {
1348 resourceOffsetEmitter.emitVarInt(stringSection.insert(key),
1350 resourceOffsetEmitter.emitVarInt(size,
"resource size");
1351 resourceOffsetEmitter.emitByte(kind,
"resource kind");
1356 ResourceBuilder entryBuilder(resourceEmitter, stringSection,
1357 appendResourceOffset,
1358 config.shouldElideResourceData);
1361 resourceOffsetEmitter.emitVarInt(config.externalResourcePrinters.size(),
1362 "external resource printer count");
1363 for (
const auto &printer : config.externalResourcePrinters) {
1364 curResourceEntries.clear();
1365 printer->buildResources(op, entryBuilder);
1366 emitResourceGroup(stringSection.insert(printer->getName()));
1370 for (DialectNumbering &dialect : numberingState.getDialects()) {
1371 if (!dialect.asmInterface)
1373 curResourceEntries.clear();
1374 dialect.asmInterface->buildResources(op, dialect.resources, entryBuilder);
1379 for (
const auto &resource : dialect.resourceMap)
1380 if (resource.second->isDeclaration)
1384 if (!curResourceEntries.empty())
1385 emitResourceGroup(dialect.number);
1389 if (resourceOffsetEmitter.size() == 0)
1393 std::move(resourceOffsetEmitter));
1401void BytecodeWriter::writeStringSection(EncodingEmitter &emitter) {
1402 EncodingEmitter stringEmitter;
1403 stringSection.write(stringEmitter);
1411void BytecodeWriter::writePropertiesSection(EncodingEmitter &emitter) {
1412 EncodingEmitter propertiesEmitter;
1413 propertiesSection.write(propertiesEmitter);
1415 std::move(propertiesEmitter));
1430 dialect->getRegisteredInterface<BytecodeDialectInterface>();
1431 if (interface && failed(interface->verifyVersion(op, *entry.getValue())))
1434 BytecodeWriter writer(op, config);
1435 return writer.write(op, os);
static bool preservesLegacyProperties(Operation *op, DictionaryAttr attrs)
Check legacy property reconstruction using scratch storage.
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.
NamedAttribute represents a combination of a name and an Attribute value.
void destroyOpProperties(PropertyRef properties) const
This hooks destroy the op properties.
std::optional< Attribute > getInherentAttr(Operation *op, StringRef name) const
Lookup an inherent attribute by name, this method isn't recommended and may be removed in the future.
void walkInherentAttrs(Operation *op, InherentAttrVisitor visitor) const
Visit the inherent attributes stored in the properties of op.
LogicalResult setOpPropertiesFromAttribute(OperationName opName, PropertyRef properties, Attribute attr, function_ref< InFlightDiagnostic()> emitError) const
Define the op properties from the provided Attribute.
void initOpProperties(PropertyRef storage, PropertyRef init) const
Initialize the op properties.
bool isRegistered() const
Return if this operation is registered.
bool compareOpProperties(PropertyRef lhs, PropertyRef rhs) const
TypeID getOpPropertiesTypeID() const
Return the TypeID of the op properties.
void populateDefaultAttrs(NamedAttrList &attrs) const
This hook implements the method to populate defaults attributes that are unset.
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.
InFlightDiagnostic emitOpError(const Twine &message={})
Emit an error with the op name prefixed, like "'dim' op " which is convenient for verifiers.
unsigned getNumResults()
Return the number of results held by this operation.
Type-safe wrapper around a void* for passing properties, including the properties structs of operatio...
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.