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"
67 :
impl(std::make_unique<
Impl>(producer)) {}
80 return impl->attributeWriterCallbacks;
85 return impl->typeWriterCallbacks;
90 impl->attributeWriterCallbacks.emplace_back(std::move(callback));
95 impl->typeWriterCallbacks.emplace_back(std::move(callback));
99 std::unique_ptr<AsmResourcePrinter> printer) {
100 impl->externalResourcePrinters.emplace_back(std::move(printer));
104 bool shouldElideResourceData) {
105 impl->shouldElideResourceData = shouldElideResourceData;
113 return impl->shouldElideLocations;
117 impl->bytecodeVersion = bytecodeVersion;
121 return impl->bytecodeVersion;
124llvm::StringMap<std::unique_ptr<DialectVersion>> &
126 return impl->dialectVersionMap;
130 llvm::StringRef dialectName,
131 std::unique_ptr<DialectVersion> dialectVersion)
const {
132 assert(!
impl->dialectVersionMap.contains(dialectName) &&
133 "cannot override a previously set dialect version");
134 impl->dialectVersionMap.insert({dialectName, std::move(dialectVersion)});
147class EncodingEmitter {
149 EncodingEmitter() =
default;
150 EncodingEmitter(
const EncodingEmitter &) =
delete;
151 EncodingEmitter &operator=(
const EncodingEmitter &) =
delete;
154 void writeTo(raw_ostream &os)
const;
157 size_t size()
const {
return prevResultSize + currentResult.size(); }
164 void patchByte(uint64_t offset, uint8_t value, StringLiteral desc) {
165 LDBG() <<
"patchByte(" << offset <<
',' << uint64_t(value) <<
")\t" << desc;
166 assert(offset < size() && offset >= prevResultSize &&
167 "cannot patch previously emitted data");
168 currentResult[offset - prevResultSize] = value;
173 void emitOwnedBlob(ArrayRef<uint8_t> data, StringLiteral desc) {
174 LDBG() <<
"emitOwnedBlob(" << data.size() <<
"b)\t" << desc;
176 appendResult(std::move(currentResult));
177 appendOwnedResult(data);
184 void emitOwnedBlobAndAlignment(ArrayRef<uint8_t> data, uint32_t alignment,
185 StringLiteral desc) {
186 emitVarInt(alignment, desc);
187 emitVarInt(data.size(), desc);
190 emitOwnedBlob(data, desc);
192 void emitOwnedBlobAndAlignment(ArrayRef<char> data, uint32_t alignment,
193 StringLiteral desc) {
194 ArrayRef<uint8_t> castedData(
reinterpret_cast<const uint8_t *
>(data.data()),
196 emitOwnedBlobAndAlignment(castedData, alignment, desc);
200 void alignTo(
unsigned alignment) {
203 assert(llvm::isPowerOf2_32(alignment) &&
"expected valid alignment");
207 size_t curOffset = size();
208 size_t paddingSize = llvm::alignTo(curOffset, alignment) - curOffset;
209 while (paddingSize--)
213 requiredAlignment = std::max(requiredAlignment, alignment);
220 template <
typename T>
221 void emitByte(T
byte, StringLiteral desc) {
222 LDBG() <<
"emitByte(" << uint64_t(
byte) <<
")\t" << desc;
223 currentResult.push_back(
static_cast<uint8_t
>(
byte));
227 void emitBytes(ArrayRef<uint8_t> bytes, StringLiteral desc) {
228 LDBG() <<
"emitBytes(" << bytes.size() <<
"b)\t" << desc;
229 llvm::append_range(currentResult, bytes);
239 void emitVarInt(uint64_t value, StringLiteral desc) {
240 LDBG() <<
"emitVarInt(" << value <<
")\t" << desc;
244 if ((value >> 7) == 0)
245 return emitByte((value << 1) | 0x1, desc);
246 emitMultiByteVarInt(value, desc);
253 void emitSignedVarInt(uint64_t value, StringLiteral desc) {
254 emitVarInt((value << 1) ^ (uint64_t)((int64_t)value >> 63), desc);
259 void emitVarIntWithFlag(uint64_t value,
bool flag, StringLiteral desc) {
260 emitVarInt((value << 1) | (flag ? 1 : 0), desc);
267 void emitNulTerminatedString(StringRef str, StringLiteral desc) {
268 emitString(str, desc);
269 emitByte(0,
"null terminator");
273 void emitString(StringRef str, StringLiteral desc) {
274 emitBytes({
reinterpret_cast<const uint8_t *
>(str.data()), str.size()},
287 uint64_t codeOffset = currentResult.size();
288 emitByte(code,
"section code");
289 emitVarInt(emitter.size(),
"section size");
292 unsigned emitterAlign = emitter.requiredAlignment;
293 if (emitterAlign > 1) {
294 if (size() & (emitterAlign - 1)) {
295 emitVarInt(emitterAlign,
"section alignment");
296 alignTo(emitterAlign);
300 currentResult[codeOffset] |= 0b10000000;
304 requiredAlignment = std::max(requiredAlignment, emitterAlign);
310 appendResult(std::move(currentResult));
311 for (std::vector<uint8_t> &
result : emitter.prevResultStorage)
312 prevResultStorage.push_back(std::move(
result));
313 llvm::append_range(prevResultList, emitter.prevResultList);
314 prevResultSize += emitter.prevResultSize;
315 appendResult(std::move(emitter.currentResult));
323 LLVM_ATTRIBUTE_NOINLINE
void emitMultiByteVarInt(uint64_t value,
327 void appendResult(std::vector<uint8_t> &&
result) {
330 prevResultStorage.emplace_back(std::move(
result));
331 appendOwnedResult(prevResultStorage.back());
333 void appendOwnedResult(ArrayRef<uint8_t>
result) {
336 prevResultSize +=
result.size();
337 prevResultList.emplace_back(
result);
345 std::vector<uint8_t> currentResult;
346 std::vector<ArrayRef<uint8_t>> prevResultList;
347 std::vector<std::vector<uint8_t>> prevResultStorage;
351 size_t prevResultSize = 0;
354 unsigned requiredAlignment = 1;
363class StringSectionBuilder {
367 size_t insert(StringRef str) {
368 auto it = strings.insert({llvm::CachedHashStringRef(str), strings.size()});
369 return it.first->second;
373 void write(EncodingEmitter &emitter) {
374 emitter.emitVarInt(strings.size(),
"string section size");
378 for (
const auto &it : llvm::reverse(strings))
379 emitter.emitVarInt(it.first.size() + 1,
"string size");
381 for (
const auto &it : strings)
382 emitter.emitNulTerminatedString(it.first.val(),
"string");
388 llvm::MapVector<llvm::CachedHashStringRef, size_t> strings;
393 using DialectVersionMapT = llvm::StringMap<std::unique_ptr<DialectVersion>>;
396 DialectWriter(int64_t bytecodeVersion, EncodingEmitter &emitter,
397 IRNumberingState &numberingState,
398 StringSectionBuilder &stringSection,
399 const DialectVersionMapT &dialectVersionMap)
400 : bytecodeVersion(bytecodeVersion), emitter(emitter),
401 numberingState(numberingState), stringSection(stringSection),
402 dialectVersionMap(dialectVersionMap) {}
408 void writeAttribute(Attribute attr)
override {
409 emitter.emitVarInt(numberingState.getNumber(attr),
"dialect attr");
411 void writeOptionalAttribute(Attribute attr)
override {
413 emitter.emitVarInt(0,
"dialect optional attr none");
416 emitter.emitVarIntWithFlag(numberingState.getNumber(attr),
true,
417 "dialect optional attr");
420 void writeType(Type type)
override {
421 emitter.emitVarInt(numberingState.getNumber(type),
"dialect type");
424 void writeResourceHandle(
const AsmDialectResourceHandle &resource)
override {
425 emitter.emitVarInt(numberingState.getNumber(resource),
"dialect resource");
432 void writeVarInt(uint64_t value)
override {
433 emitter.emitVarInt(value,
"dialect writer");
436 void writeSignedVarInt(int64_t value)
override {
437 emitter.emitSignedVarInt(value,
"dialect writer");
440 void writeAPIntWithKnownWidth(
const APInt &value)
override {
441 size_t bitWidth = value.getBitWidth();
446 return emitter.emitByte(value.getLimitedValue(),
"dialect APInt");
450 return emitter.emitSignedVarInt(value.getLimitedValue(),
"dialect APInt");
455 unsigned numActiveWords = value.getActiveWords();
456 emitter.emitVarInt(numActiveWords,
"dialect APInt word count");
458 const uint64_t *rawValueData = value.getRawData();
459 for (
unsigned i = 0; i < numActiveWords; ++i)
460 emitter.emitSignedVarInt(rawValueData[i],
"dialect APInt word");
463 void writeAPFloatWithKnownSemantics(
const APFloat &value)
override {
464 writeAPIntWithKnownWidth(value.bitcastToAPInt());
467 void writeOwnedString(StringRef str)
override {
468 emitter.emitVarInt(stringSection.insert(str),
"dialect string");
471 void writeOwnedBlob(ArrayRef<char> blob)
override {
472 emitter.emitVarInt(blob.size(),
"dialect blob");
473 emitter.emitOwnedBlob(
474 ArrayRef<uint8_t>(
reinterpret_cast<const uint8_t *
>(blob.data()),
479 void writeUnownedBlob(ArrayRef<char> blob)
override {
480 emitter.emitVarInt(blob.size(),
"dialect blob");
482 ArrayRef<uint8_t>(
reinterpret_cast<const uint8_t *
>(blob.data()),
487 void writeOwnedBool(
bool value)
override {
488 emitter.emitByte(value,
"dialect bool");
491 int64_t getBytecodeVersion()
const override {
return bytecodeVersion; }
493 FailureOr<const DialectVersion *>
494 getDialectVersion(StringRef dialectName)
const override {
495 auto dialectEntry = dialectVersionMap.find(dialectName);
496 if (dialectEntry == dialectVersionMap.end())
498 return dialectEntry->getValue().get();
502 int64_t bytecodeVersion;
503 EncodingEmitter &emitter;
504 IRNumberingState &numberingState;
505 StringSectionBuilder &stringSection;
506 const DialectVersionMapT &dialectVersionMap;
510class PropertiesSectionBuilder {
512 PropertiesSectionBuilder(IRNumberingState &numberingState,
513 StringSectionBuilder &stringSection,
514 const BytecodeWriterConfig::Impl &config)
515 : numberingState(numberingState), stringSection(stringSection),
520 std::optional<ssize_t>
emit(Operation *op) {
521 EncodingEmitter propertiesEmitter;
529 EncodingEmitter sizeEmitter;
530 sizeEmitter.emitVarInt(numberingState.getNumber(prop),
"properties size");
532 llvm::raw_svector_ostream os(scratch);
533 sizeEmitter.writeTo(os);
534 return emit(scratch);
537 EncodingEmitter emitter;
538 DialectWriter propertiesWriter(config.bytecodeVersion, emitter,
539 numberingState, stringSection,
540 config.dialectVersionMap);
541 auto iface = cast<BytecodeOpInterface>(op);
542 iface.writeProperties(propertiesWriter);
544 llvm::raw_svector_ostream os(scratch);
546 return emit(scratch);
550 void write(EncodingEmitter &emitter) {
551 emitter.emitVarInt(propertiesStorage.size(),
"properties size");
552 if (propertiesStorage.empty())
554 for (
const auto &storage : propertiesStorage) {
555 if (storage.empty()) {
556 emitter.emitBytes(ArrayRef<uint8_t>(),
"empty properties");
559 emitter.emitBytes(ArrayRef(
reinterpret_cast<const uint8_t *
>(&storage[0]),
566 bool empty() {
return propertiesStorage.empty(); }
572 ssize_t
emit(ArrayRef<char> rawProperties) {
574 SmallVector<char> sizeScratch;
576 EncodingEmitter sizeEmitter;
577 sizeEmitter.emitVarInt(rawProperties.size(),
"properties");
578 llvm::raw_svector_ostream os(sizeScratch);
579 sizeEmitter.writeTo(os);
582 size_t index = propertiesStorage.size();
583 propertiesStorage.emplace_back();
584 std::vector<char> &newStorage = propertiesStorage.back();
585 size_t propertiesSize = sizeScratch.size() + rawProperties.size();
586 newStorage.reserve(propertiesSize);
587 llvm::append_range(newStorage, sizeScratch);
588 llvm::append_range(newStorage, rawProperties);
592 auto inserted = propertiesUniquing.insert(
593 std::make_pair(ArrayRef<char>(newStorage), index));
595 propertiesStorage.pop_back();
600 std::vector<std::vector<char>> propertiesStorage;
601 SmallVector<char> scratch;
603 IRNumberingState &numberingState;
604 StringSectionBuilder &stringSection;
605 const BytecodeWriterConfig::Impl &config;
614 explicit RawEmitterOstream(EncodingEmitter &emitter) : emitter(emitter) {
619 void write_impl(
const char *ptr,
size_t size)
override {
620 emitter.emitBytes({
reinterpret_cast<const uint8_t *
>(ptr), size},
623 uint64_t current_pos()
const override {
return emitter.size(); }
626 EncodingEmitter &emitter;
630void EncodingEmitter::writeTo(
raw_ostream &os)
const {
632 os.reserveExtraSpace(size());
634 for (
auto &prevResult : prevResultList)
635 os.write((
const char *)prevResult.data(), prevResult.size());
636 os.write((
const char *)currentResult.data(), currentResult.size());
639void EncodingEmitter::emitMultiByteVarInt(uint64_t value, StringLiteral desc) {
643 uint64_t it = value >> 7;
644 for (
size_t numBytes = 2; numBytes < 9; ++numBytes) {
645 if (LLVM_LIKELY(it >>= 7) == 0) {
646 uint64_t encodedValue = (value << 1) | 0x1;
647 encodedValue <<= (numBytes - 1);
648 llvm::support::ulittle64_t encodedValueLE(encodedValue);
649 emitBytes({
reinterpret_cast<uint8_t *
>(&encodedValueLE), numBytes}, desc);
657 llvm::support::ulittle64_t valueLE(value);
658 emitBytes({
reinterpret_cast<uint8_t *
>(&valueLE),
sizeof(valueLE)}, desc);
666class BytecodeWriter {
668 BytecodeWriter(Operation *op,
const BytecodeWriterConfig &config)
669 : numberingState(op, config), config(config.getImpl()),
670 propertiesSection(numberingState, stringSection, config.getImpl()) {}
673 LogicalResult write(Operation *rootOp, raw_ostream &os);
679 void writeDialectSection(EncodingEmitter &emitter);
684 void writeAttrTypeSection(EncodingEmitter &emitter);
689 LogicalResult writeBlock(EncodingEmitter &emitter,
Block *block);
690 LogicalResult writeOp(EncodingEmitter &emitter, Operation *op);
691 LogicalResult writeRegion(EncodingEmitter &emitter, Region *region);
692 LogicalResult writeIRSection(EncodingEmitter &emitter, Operation *op);
694 LogicalResult writeRegions(EncodingEmitter &emitter,
695 MutableArrayRef<Region> regions) {
696 return success(llvm::all_of(regions, [&](Region ®ion) {
697 return succeeded(writeRegion(emitter, ®ion));
704 void writeResourceSection(Operation *op, EncodingEmitter &emitter);
709 void writeStringSection(EncodingEmitter &emitter);
714 void writePropertiesSection(EncodingEmitter &emitter);
719 void writeUseListOrders(EncodingEmitter &emitter, uint8_t &opEncodingMask,
726 StringSectionBuilder stringSection;
729 IRNumberingState numberingState;
732 const BytecodeWriterConfig::Impl &config;
735 PropertiesSectionBuilder propertiesSection;
740 EncodingEmitter emitter;
744 emitter.emitString(
"ML\xefR",
"bytecode header");
750 <<
"unsupported version requested " << config.bytecodeVersion
751 <<
", must be in range ["
754 emitter.emitVarInt(config.bytecodeVersion,
"bytecode version");
757 emitter.emitNulTerminatedString(config.producer,
"bytecode producer");
760 writeDialectSection(emitter);
763 writeAttrTypeSection(emitter);
766 if (
failed(writeIRSection(emitter, rootOp)))
770 writeResourceSection(rootOp, emitter);
773 writeStringSection(emitter);
777 writePropertiesSection(emitter);
778 else if (!propertiesSection.empty())
780 "unexpected properties emitted incompatible with bytecode <5");
796template <
typename EntriesT,
typename EntryCallbackT>
798 EntryCallbackT &&callback) {
799 for (
auto it = entries.begin(), e = entries.end(); it != e;) {
800 auto groupStart = it++;
804 it = std::find_if(it, e, [&](
const auto &entry) {
805 return entry.dialect != currentDialect;
809 emitter.emitVarInt(currentDialect->
number,
"dialect number");
810 emitter.emitVarInt(std::distance(groupStart, it),
"dialect offset");
813 for (
auto &entry : llvm::make_range(groupStart, it))
818void BytecodeWriter::writeDialectSection(EncodingEmitter &emitter) {
819 EncodingEmitter dialectEmitter;
822 auto dialects = numberingState.getDialects();
823 dialectEmitter.emitVarInt(llvm::size(dialects),
"dialects count");
824 for (DialectNumbering &dialect : dialects) {
826 size_t nameID = stringSection.insert(dialect.name);
829 dialectEmitter.emitVarInt(nameID,
"dialect name ID");
834 EncodingEmitter versionEmitter;
835 if (dialect.interface) {
837 DialectWriter versionWriter(config.bytecodeVersion, versionEmitter,
838 numberingState, stringSection,
839 config.dialectVersionMap);
840 dialect.interface->writeVersion(versionWriter);
846 size_t versionAvailable = versionEmitter.size() > 0;
847 dialectEmitter.emitVarIntWithFlag(nameID, versionAvailable,
849 if (versionAvailable)
851 std::move(versionEmitter));
855 dialectEmitter.emitVarInt(size(numberingState.getOpNames()),
859 auto emitOpName = [&](OpNameNumbering &name) {
860 size_t stringId = stringSection.insert(name.name.stripDialect());
862 dialectEmitter.emitVarInt(stringId,
"dialect op name");
864 dialectEmitter.emitVarIntWithFlag(stringId, name.name.isRegistered(),
876void BytecodeWriter::writeAttrTypeSection(EncodingEmitter &emitter) {
877 EncodingEmitter attrTypeEmitter;
878 EncodingEmitter offsetEmitter;
879 offsetEmitter.emitVarInt(llvm::size(numberingState.getAttributes()),
881 offsetEmitter.emitVarInt(llvm::size(numberingState.getTypes()),
885 uint64_t prevOffset = 0;
886 auto emitAttrOrType = [&](
auto &entry) {
887 auto entryValue = entry.getValue();
889 auto emitAttrOrTypeRawImpl = [&]() ->
void {
890 RawEmitterOstream(attrTypeEmitter) << entryValue;
891 attrTypeEmitter.emitByte(0,
"attr/type separator");
893 auto emitAttrOrTypeImpl = [&]() ->
bool {
896 if (entryValue.template hasTrait<TypeTrait::IsMutable>() ||
897 entryValue.template hasTrait<AttributeTrait::IsMutable>()) {
898 emitAttrOrTypeRawImpl();
902 DialectWriter dialectWriter(config.bytecodeVersion, attrTypeEmitter,
903 numberingState, stringSection,
904 config.dialectVersionMap);
905 if constexpr (std::is_same_v<std::decay_t<
decltype(entryValue)>,
Type>) {
906 for (
const auto &callback : config.typeWriterCallbacks) {
907 if (succeeded(callback->write(entryValue, dialectWriter)))
910 if (
const BytecodeDialectInterface *interface =
911 entry.dialect->interface) {
912 if (succeeded(interface->writeType(entryValue, dialectWriter)))
916 for (
const auto &callback : config.attributeWriterCallbacks) {
917 if (succeeded(callback->write(entryValue, dialectWriter)))
920 if (
const BytecodeDialectInterface *interface =
921 entry.dialect->interface) {
922 if (succeeded(interface->writeAttribute(entryValue, dialectWriter)))
929 emitAttrOrTypeRawImpl();
933 bool hasCustomEncoding = emitAttrOrTypeImpl();
936 uint64_t curOffset = attrTypeEmitter.size();
937 offsetEmitter.emitVarIntWithFlag(curOffset - prevOffset, hasCustomEncoding,
939 prevOffset = curOffset;
950 std::move(offsetEmitter));
958LogicalResult BytecodeWriter::writeBlock(EncodingEmitter &emitter,
961 bool hasArgs = !args.empty();
966 unsigned numOps = numberingState.getOperationCount(block);
967 emitter.emitVarIntWithFlag(numOps, hasArgs,
"block num ops");
971 emitter.emitVarInt(args.size(),
"block args count");
975 emitter.emitVarIntWithFlag(numberingState.getNumber(arg.getType()),
976 !isa<UnknownLoc>(argLoc),
"block arg type");
977 if (!isa<UnknownLoc>(argLoc))
978 emitter.emitVarInt(numberingState.getNumber(argLoc),
979 "block arg location");
981 emitter.emitVarInt(numberingState.getNumber(arg.getType()),
983 emitter.emitVarInt(numberingState.getNumber(argLoc),
984 "block arg location");
988 uint64_t maskOffset = emitter.size();
989 uint8_t encodingMask = 0;
990 emitter.emitByte(0,
"use-list separator");
991 writeUseListOrders(emitter, encodingMask, args);
993 emitter.patchByte(maskOffset, encodingMask,
"block patch encoding");
999 if (
failed(writeOp(emitter, &op)))
1004LogicalResult BytecodeWriter::writeOp(EncodingEmitter &emitter,
Operation *op) {
1005 emitter.emitVarInt(numberingState.getNumber(op->
getName()),
"op name ID");
1010 uint64_t maskOffset = emitter.size();
1011 uint8_t opEncodingMask = 0;
1012 emitter.emitByte(0,
"op separator");
1015 emitter.emitVarInt(numberingState.getNumber(op->
getLoc()),
"op location");
1027 if (!attrs.empty()) {
1029 emitter.emitVarInt(numberingState.getNumber(attrs),
"op attrs count");
1035 std::optional<ssize_t> propertiesId = propertiesSection.emit(op);
1036 if (propertiesId.has_value()) {
1038 emitter.emitVarInt(*propertiesId,
"op properties ID");
1045 emitter.emitVarInt(numResults,
"op results count");
1047 emitter.emitVarInt(numberingState.getNumber(type),
"op result type");
1053 emitter.emitVarInt(numOperands,
"op operands count");
1055 emitter.emitVarInt(numberingState.getNumber(operand),
"op operand types");
1061 emitter.emitVarInt(numSuccessors,
"op successors count");
1063 emitter.emitVarInt(numberingState.getNumber(successor),
"op successor");
1077 emitter.patchByte(maskOffset, opEncodingMask,
"op encoding mask");
1084 bool isIsolatedFromAbove = numberingState.isIsolatedFromAbove(op);
1085 emitter.emitVarIntWithFlag(numRegions, isIsolatedFromAbove,
1086 "op regions count");
1090 if (isIsolatedFromAbove &&
1092 EncodingEmitter regionEmitter;
1104void BytecodeWriter::writeUseListOrders(EncodingEmitter &emitter,
1105 uint8_t &opEncodingMask,
1108 llvm::MapVector<unsigned, llvm::SmallVector<unsigned>> map;
1109 for (
auto item : llvm::enumerate(range)) {
1110 auto value = item.value();
1113 if (value.use_empty() || value.hasOneUse())
1119 bool alreadyOrdered =
true;
1120 auto &firstUse = *value.use_begin();
1122 firstUse, numberingState.getNumber(firstUse.getOwner()));
1123 llvm::SmallVector<std::pair<unsigned, uint64_t>> useListPairs(
1126 for (
auto use : llvm::drop_begin(llvm::enumerate(value.getUses()))) {
1128 use.value(), numberingState.getNumber(use.value().getOwner()));
1132 alreadyOrdered &= (prevID > currentID);
1133 useListPairs.push_back({use.index(), currentID});
1143 useListPairs.begin(), useListPairs.end(),
1144 [](
auto elem1,
auto elem2) { return elem1.second > elem2.second; });
1146 map.try_emplace(item.index(), llvm::map_range(useListPairs, [](
auto elem) {
1157 if (range.size() != 1) {
1158 emitter.emitVarInt(map.size(),
"custom use-list size");
1161 for (
const auto &[resultIdx, useListOrder] : map) {
1165 size_t shuffledElements =
1166 llvm::count_if(llvm::enumerate(useListOrder),
1167 [](
auto item) {
return item.index() != item.value(); });
1168 bool indexPairEncoding = shuffledElements < (useListOrder.size() / 2);
1171 if (range.size() != 1)
1172 emitter.emitVarInt(resultIdx,
"use-list result index");
1174 if (indexPairEncoding) {
1175 emitter.emitVarIntWithFlag(shuffledElements * 2, indexPairEncoding,
1176 "use-list index pair size");
1177 for (
auto pair : llvm::enumerate(useListOrder)) {
1178 if (pair.index() != pair.value()) {
1179 emitter.emitVarInt(pair.value(),
"use-list index pair first");
1180 emitter.emitVarInt(pair.index(),
"use-list index pair second");
1184 emitter.emitVarIntWithFlag(useListOrder.size(), indexPairEncoding,
1186 for (
const auto &
index : useListOrder)
1187 emitter.emitVarInt(
index,
"use-list order");
1192LogicalResult BytecodeWriter::writeRegion(EncodingEmitter &emitter,
1196 if (region->
empty()) {
1197 emitter.emitVarInt( 0,
"region block count empty");
1202 unsigned numBlocks, numValues;
1203 std::tie(numBlocks, numValues) = numberingState.getBlockValueCount(region);
1204 emitter.emitVarInt(numBlocks,
"region block count");
1205 emitter.emitVarInt(numValues,
"region value count");
1208 for (
Block &block : *region)
1209 if (
failed(writeBlock(emitter, &block)))
1214LogicalResult BytecodeWriter::writeIRSection(EncodingEmitter &emitter,
1216 EncodingEmitter irEmitter;
1221 irEmitter.emitVarIntWithFlag( 1,
false,
"ir section");
1224 if (
failed(writeOp(irEmitter, op)))
1242 ResourceBuilder(EncodingEmitter &emitter, StringSectionBuilder &stringSection,
1243 PostProcessFn postProcessFn,
bool shouldElideData)
1244 : emitter(emitter), stringSection(stringSection),
1245 postProcessFn(postProcessFn), shouldElideData(shouldElideData) {}
1246 ~ResourceBuilder()
override =
default;
1248 void buildBlob(StringRef key, ArrayRef<char> data,
1249 uint32_t dataAlignment)
final {
1250 if (!shouldElideData)
1251 emitter.emitOwnedBlobAndAlignment(data, dataAlignment,
"resource blob");
1252 postProcessFn(key, AsmResourceEntryKind::Blob);
1254 void buildBool(StringRef key,
bool data)
final {
1255 if (!shouldElideData)
1256 emitter.emitByte(data,
"resource bool");
1257 postProcessFn(key, AsmResourceEntryKind::Bool);
1259 void buildString(StringRef key, StringRef data)
final {
1260 if (!shouldElideData)
1261 emitter.emitVarInt(stringSection.insert(data),
"resource string");
1262 postProcessFn(key, AsmResourceEntryKind::String);
1266 EncodingEmitter &emitter;
1267 StringSectionBuilder &stringSection;
1268 PostProcessFn postProcessFn;
1269 bool shouldElideData =
false;
1273void BytecodeWriter::writeResourceSection(
Operation *op,
1274 EncodingEmitter &emitter) {
1275 EncodingEmitter resourceEmitter;
1276 EncodingEmitter resourceOffsetEmitter;
1277 uint64_t prevOffset = 0;
1284 uint64_t curOffset = resourceEmitter.size();
1285 curResourceEntries.emplace_back(key, kind, curOffset - prevOffset);
1286 prevOffset = curOffset;
1290 auto emitResourceGroup = [&](uint64_t key) {
1291 resourceOffsetEmitter.emitVarInt(key,
"resource group key");
1292 resourceOffsetEmitter.emitVarInt(curResourceEntries.size(),
1293 "resource group size");
1294 for (
auto [key, kind, size] : curResourceEntries) {
1295 resourceOffsetEmitter.emitVarInt(stringSection.insert(key),
1297 resourceOffsetEmitter.emitVarInt(size,
"resource size");
1298 resourceOffsetEmitter.emitByte(kind,
"resource kind");
1303 ResourceBuilder entryBuilder(resourceEmitter, stringSection,
1304 appendResourceOffset,
1305 config.shouldElideResourceData);
1308 resourceOffsetEmitter.emitVarInt(config.externalResourcePrinters.size(),
1309 "external resource printer count");
1310 for (
const auto &printer : config.externalResourcePrinters) {
1311 curResourceEntries.clear();
1312 printer->buildResources(op, entryBuilder);
1313 emitResourceGroup(stringSection.insert(printer->getName()));
1317 for (DialectNumbering &dialect : numberingState.getDialects()) {
1318 if (!dialect.asmInterface)
1320 curResourceEntries.clear();
1321 dialect.asmInterface->buildResources(op, dialect.resources, entryBuilder);
1326 for (
const auto &resource : dialect.resourceMap)
1327 if (resource.second->isDeclaration)
1331 if (!curResourceEntries.empty())
1332 emitResourceGroup(dialect.number);
1336 if (resourceOffsetEmitter.size() == 0)
1340 std::move(resourceOffsetEmitter));
1348void BytecodeWriter::writeStringSection(EncodingEmitter &emitter) {
1349 EncodingEmitter stringEmitter;
1350 stringSection.write(stringEmitter);
1358void BytecodeWriter::writePropertiesSection(EncodingEmitter &emitter) {
1359 EncodingEmitter propertiesEmitter;
1360 propertiesSection.write(propertiesEmitter);
1362 std::move(propertiesEmitter));
1371 BytecodeWriter writer(op, config);
1372 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.
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...
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.
PropertyRef getPropertiesStorage()
Return a generic (but typed) reference to the property type storage.
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.
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.