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"
37 Impl(StringRef producer) : producer(producer) {}
45 bool shouldElideResourceData =
false;
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;
113 llvm::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)});
136 class 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;
163 LDBG() <<
"emitOwnedBlob(" << data.size() <<
"b)\t" << desc;
165 appendResult(std::move(currentResult));
166 appendOwnedResult(data);
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) {
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));
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());
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;
352 class 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,
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");
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");
461 emitter.emitVarInt(blob.size(),
"dialect blob");
462 emitter.emitOwnedBlob(
468 void writeOwnedBool(
bool value)
override {
469 emitter.emitByte(value,
"dialect bool");
472 int64_t getBytecodeVersion()
const override {
return bytecodeVersion; }
474 FailureOr<const DialectVersion *>
475 getDialectVersion(StringRef dialectName)
const override {
476 auto dialectEntry = dialectVersionMap.find(dialectName);
477 if (dialectEntry == dialectVersionMap.end())
479 return dialectEntry->getValue().get();
483 int64_t bytecodeVersion;
484 EncodingEmitter &emitter;
486 StringSectionBuilder &stringSection;
487 const DialectVersionMapT &dialectVersionMap;
491 class PropertiesSectionBuilder {
494 StringSectionBuilder &stringSection,
496 : numberingState(numberingState), stringSection(stringSection),
502 EncodingEmitter propertiesEmitter;
510 EncodingEmitter sizeEmitter;
511 sizeEmitter.emitVarInt(numberingState.getNumber(prop),
"properties size");
513 llvm::raw_svector_ostream os(scratch);
514 sizeEmitter.writeTo(os);
515 return emit(scratch);
518 EncodingEmitter emitter;
519 DialectWriter propertiesWriter(
config.bytecodeVersion, emitter,
520 numberingState, stringSection,
521 config.dialectVersionMap);
522 auto iface = cast<BytecodeOpInterface>(op);
523 iface.writeProperties(propertiesWriter);
525 llvm::raw_svector_ostream os(scratch);
527 return emit(scratch);
531 void write(EncodingEmitter &emitter) {
532 emitter.emitVarInt(propertiesStorage.size(),
"properties size");
533 if (propertiesStorage.empty())
535 for (
const auto &storage : propertiesStorage) {
536 if (storage.empty()) {
540 emitter.emitBytes(
ArrayRef(
reinterpret_cast<const uint8_t *
>(&storage[0]),
547 bool empty() {
return propertiesStorage.empty(); }
557 EncodingEmitter sizeEmitter;
558 sizeEmitter.emitVarInt(rawProperties.size(),
"properties");
559 llvm::raw_svector_ostream os(sizeScratch);
560 sizeEmitter.writeTo(os);
563 size_t index = propertiesStorage.size();
564 propertiesStorage.emplace_back();
565 std::vector<char> &newStorage = propertiesStorage.back();
566 size_t propertiesSize = sizeScratch.size() + rawProperties.size();
567 newStorage.reserve(propertiesSize);
568 llvm::append_range(newStorage, sizeScratch);
569 llvm::append_range(newStorage, rawProperties);
573 auto inserted = propertiesUniquing.insert(
575 if (!inserted.second)
576 propertiesStorage.pop_back();
577 return inserted.first->getSecond();
581 std::vector<std::vector<char>> propertiesStorage;
585 StringSectionBuilder &stringSection;
593 class RawEmitterOstream :
public raw_ostream {
595 explicit RawEmitterOstream(EncodingEmitter &emitter) : emitter(emitter) {
600 void write_impl(
const char *ptr,
size_t size)
override {
601 emitter.emitBytes({
reinterpret_cast<const uint8_t *
>(ptr), size},
604 uint64_t current_pos()
const override {
return emitter.size(); }
607 EncodingEmitter &emitter;
611 void EncodingEmitter::writeTo(raw_ostream &os)
const {
613 os.reserveExtraSpace(size());
615 for (
auto &prevResult : prevResultList)
616 os.write((
const char *)prevResult.data(), prevResult.size());
617 os.write((
const char *)currentResult.data(), currentResult.size());
620 void EncodingEmitter::emitMultiByteVarInt(uint64_t value, StringLiteral desc) {
624 uint64_t it = value >> 7;
625 for (
size_t numBytes = 2; numBytes < 9; ++numBytes) {
626 if (LLVM_LIKELY(it >>= 7) == 0) {
627 uint64_t encodedValue = (value << 1) | 0x1;
628 encodedValue <<= (numBytes - 1);
629 llvm::support::ulittle64_t encodedValueLE(encodedValue);
630 emitBytes({
reinterpret_cast<uint8_t *
>(&encodedValueLE), numBytes}, desc);
638 llvm::support::ulittle64_t valueLE(value);
639 emitBytes({
reinterpret_cast<uint8_t *
>(&valueLE),
sizeof(valueLE)}, desc);
647 class BytecodeWriter {
651 propertiesSection(numberingState, stringSection,
config.getImpl()) {}
654 LogicalResult write(
Operation *rootOp, raw_ostream &os);
660 void writeDialectSection(EncodingEmitter &emitter);
665 void writeAttrTypeSection(EncodingEmitter &emitter);
670 LogicalResult writeBlock(EncodingEmitter &emitter,
Block *block);
671 LogicalResult writeOp(EncodingEmitter &emitter,
Operation *op);
672 LogicalResult writeRegion(EncodingEmitter &emitter,
Region *region);
673 LogicalResult writeIRSection(EncodingEmitter &emitter,
Operation *op);
675 LogicalResult writeRegions(EncodingEmitter &emitter,
677 return success(llvm::all_of(regions, [&](
Region ®ion) {
678 return succeeded(writeRegion(emitter, ®ion));
685 void writeResourceSection(
Operation *op, EncodingEmitter &emitter);
690 void writeStringSection(EncodingEmitter &emitter);
695 void writePropertiesSection(EncodingEmitter &emitter);
700 void writeUseListOrders(EncodingEmitter &emitter, uint8_t &opEncodingMask,
707 StringSectionBuilder stringSection;
716 PropertiesSectionBuilder propertiesSection;
720 LogicalResult BytecodeWriter::write(
Operation *rootOp, raw_ostream &os) {
721 EncodingEmitter emitter;
725 emitter.emitString(
"ML\xefR",
"bytecode header");
731 <<
"unsupported version requested " <<
config.bytecodeVersion
732 <<
", must be in range ["
735 emitter.emitVarInt(
config.bytecodeVersion,
"bytecode version");
738 emitter.emitNulTerminatedString(
config.producer,
"bytecode producer");
741 writeDialectSection(emitter);
744 writeAttrTypeSection(emitter);
747 if (
failed(writeIRSection(emitter, rootOp)))
751 writeResourceSection(rootOp, emitter);
754 writeStringSection(emitter);
758 writePropertiesSection(emitter);
759 else if (!propertiesSection.empty())
761 "unexpected properties emitted incompatible with bytecode <5");
777 template <
typename EntriesT,
typename EntryCallbackT>
779 EntryCallbackT &&callback) {
780 for (
auto it = entries.begin(), e = entries.end(); it != e;) {
781 auto groupStart = it++;
785 it = std::find_if(it, e, [&](
const auto &entry) {
786 return entry.dialect != currentDialect;
790 emitter.emitVarInt(currentDialect->
number,
"dialect number");
791 emitter.emitVarInt(std::distance(groupStart, it),
"dialect offset");
794 for (
auto &entry : llvm::make_range(groupStart, it))
799 void BytecodeWriter::writeDialectSection(EncodingEmitter &emitter) {
800 EncodingEmitter dialectEmitter;
803 auto dialects = numberingState.getDialects();
804 dialectEmitter.emitVarInt(llvm::size(dialects),
"dialects count");
807 size_t nameID = stringSection.insert(dialect.name);
810 dialectEmitter.emitVarInt(nameID,
"dialect name ID");
815 EncodingEmitter versionEmitter;
816 if (dialect.interface) {
818 DialectWriter versionWriter(
config.bytecodeVersion, versionEmitter,
819 numberingState, stringSection,
820 config.dialectVersionMap);
821 dialect.interface->writeVersion(versionWriter);
827 size_t versionAvailable = versionEmitter.size() > 0;
828 dialectEmitter.emitVarIntWithFlag(nameID, versionAvailable,
830 if (versionAvailable)
832 std::move(versionEmitter));
836 dialectEmitter.emitVarInt(size(numberingState.getOpNames()),
841 size_t stringId = stringSection.insert(name.name.stripDialect());
843 dialectEmitter.emitVarInt(stringId,
"dialect op name");
845 dialectEmitter.emitVarIntWithFlag(stringId, name.name.isRegistered(),
857 void BytecodeWriter::writeAttrTypeSection(EncodingEmitter &emitter) {
858 EncodingEmitter attrTypeEmitter;
859 EncodingEmitter offsetEmitter;
860 offsetEmitter.emitVarInt(llvm::size(numberingState.getAttributes()),
862 offsetEmitter.emitVarInt(llvm::size(numberingState.getTypes()),
866 uint64_t prevOffset = 0;
867 auto emitAttrOrType = [&](
auto &entry) {
868 auto entryValue = entry.getValue();
870 auto emitAttrOrTypeRawImpl = [&]() ->
void {
871 RawEmitterOstream(attrTypeEmitter) << entryValue;
872 attrTypeEmitter.emitByte(0,
"attr/type separator");
874 auto emitAttrOrTypeImpl = [&]() ->
bool {
877 if (entryValue.template hasTrait<TypeTrait::IsMutable>() ||
878 entryValue.template hasTrait<AttributeTrait::IsMutable>()) {
879 emitAttrOrTypeRawImpl();
883 DialectWriter dialectWriter(
config.bytecodeVersion, attrTypeEmitter,
884 numberingState, stringSection,
885 config.dialectVersionMap);
886 if constexpr (std::is_same_v<std::decay_t<decltype(entryValue)>,
Type>) {
887 for (
const auto &callback :
config.typeWriterCallbacks) {
888 if (succeeded(callback->write(entryValue, dialectWriter)))
892 entry.dialect->interface) {
893 if (succeeded(interface->writeType(entryValue, dialectWriter)))
897 for (
const auto &callback :
config.attributeWriterCallbacks) {
898 if (succeeded(callback->write(entryValue, dialectWriter)))
902 entry.dialect->interface) {
903 if (succeeded(interface->writeAttribute(entryValue, dialectWriter)))
910 emitAttrOrTypeRawImpl();
914 bool hasCustomEncoding = emitAttrOrTypeImpl();
917 uint64_t curOffset = attrTypeEmitter.size();
918 offsetEmitter.emitVarIntWithFlag(curOffset - prevOffset, hasCustomEncoding,
920 prevOffset = curOffset;
931 std::move(offsetEmitter));
939 LogicalResult BytecodeWriter::writeBlock(EncodingEmitter &emitter,
942 bool hasArgs = !args.empty();
947 unsigned numOps = numberingState.getOperationCount(block);
948 emitter.emitVarIntWithFlag(numOps, hasArgs,
"block num ops");
952 emitter.emitVarInt(args.size(),
"block args count");
956 emitter.emitVarIntWithFlag(numberingState.getNumber(arg.getType()),
957 !isa<UnknownLoc>(argLoc),
"block arg type");
958 if (!isa<UnknownLoc>(argLoc))
959 emitter.emitVarInt(numberingState.getNumber(argLoc),
960 "block arg location");
962 emitter.emitVarInt(numberingState.getNumber(arg.getType()),
964 emitter.emitVarInt(numberingState.getNumber(argLoc),
965 "block arg location");
969 uint64_t maskOffset = emitter.size();
970 uint8_t encodingMask = 0;
971 emitter.emitByte(0,
"use-list separator");
972 writeUseListOrders(emitter, encodingMask, args);
974 emitter.patchByte(maskOffset, encodingMask,
"block patch encoding");
980 if (
failed(writeOp(emitter, &op)))
985 LogicalResult BytecodeWriter::writeOp(EncodingEmitter &emitter,
Operation *op) {
986 emitter.emitVarInt(numberingState.getNumber(op->
getName()),
"op name ID");
991 uint64_t maskOffset = emitter.size();
992 uint8_t opEncodingMask = 0;
993 emitter.emitByte(0,
"op separator");
996 emitter.emitVarInt(numberingState.getNumber(op->
getLoc()),
"op location");
1008 if (!attrs.empty()) {
1010 emitter.emitVarInt(numberingState.getNumber(attrs),
"op attrs count");
1016 std::optional<ssize_t> propertiesId = propertiesSection.emit(op);
1017 if (propertiesId.has_value()) {
1019 emitter.emitVarInt(*propertiesId,
"op properties ID");
1026 emitter.emitVarInt(numResults,
"op results count");
1028 emitter.emitVarInt(numberingState.getNumber(type),
"op result type");
1034 emitter.emitVarInt(numOperands,
"op operands count");
1036 emitter.emitVarInt(numberingState.getNumber(operand),
"op operand types");
1042 emitter.emitVarInt(numSuccessors,
"op successors count");
1044 emitter.emitVarInt(numberingState.getNumber(successor),
"op successor");
1058 emitter.patchByte(maskOffset, opEncodingMask,
"op encoding mask");
1065 bool isIsolatedFromAbove = numberingState.isIsolatedFromAbove(op);
1066 emitter.emitVarIntWithFlag(numRegions, isIsolatedFromAbove,
1067 "op regions count");
1071 if (isIsolatedFromAbove &&
1073 EncodingEmitter regionEmitter;
1085 void BytecodeWriter::writeUseListOrders(EncodingEmitter &emitter,
1086 uint8_t &opEncodingMask,
1091 auto value = item.value();
1094 if (value.use_empty() || value.hasOneUse())
1100 bool alreadyOrdered =
true;
1101 auto &firstUse = *value.use_begin();
1103 firstUse, numberingState.getNumber(firstUse.getOwner()));
1109 use.value(), numberingState.getNumber(use.value().getOwner()));
1113 alreadyOrdered &= (prevID > currentID);
1114 useListPairs.push_back({use.index(), currentID});
1124 useListPairs.begin(), useListPairs.end(),
1125 [](
auto elem1,
auto elem2) { return elem1.second > elem2.second; });
1127 map.try_emplace(item.index(), llvm::map_range(useListPairs, [](
auto elem) {
1138 if (range.size() != 1) {
1139 emitter.emitVarInt(map.size(),
"custom use-list size");
1142 for (
const auto &item : map) {
1143 auto resultIdx = item.getFirst();
1144 auto useListOrder = item.getSecond();
1149 size_t shuffledElements =
1151 [](
auto item) {
return item.index() != item.value(); });
1152 bool indexPairEncoding = shuffledElements < (useListOrder.size() / 2);
1155 if (range.size() != 1)
1156 emitter.emitVarInt(resultIdx,
"use-list result index");
1158 if (indexPairEncoding) {
1159 emitter.emitVarIntWithFlag(shuffledElements * 2, indexPairEncoding,
1160 "use-list index pair size");
1162 if (pair.index() != pair.value()) {
1163 emitter.emitVarInt(pair.value(),
"use-list index pair first");
1164 emitter.emitVarInt(pair.index(),
"use-list index pair second");
1168 emitter.emitVarIntWithFlag(useListOrder.size(), indexPairEncoding,
1170 for (
const auto &index : useListOrder)
1171 emitter.emitVarInt(index,
"use-list order");
1176 LogicalResult BytecodeWriter::writeRegion(EncodingEmitter &emitter,
1180 if (region->
empty()) {
1181 emitter.emitVarInt( 0,
"region block count empty");
1186 unsigned numBlocks, numValues;
1187 std::tie(numBlocks, numValues) = numberingState.getBlockValueCount(region);
1188 emitter.emitVarInt(numBlocks,
"region block count");
1189 emitter.emitVarInt(numValues,
"region value count");
1192 for (
Block &block : *region)
1193 if (
failed(writeBlock(emitter, &block)))
1198 LogicalResult BytecodeWriter::writeIRSection(EncodingEmitter &emitter,
1200 EncodingEmitter irEmitter;
1205 irEmitter.emitVarIntWithFlag( 1,
false,
"ir section");
1208 if (
failed(writeOp(irEmitter, op)))
1226 ResourceBuilder(EncodingEmitter &emitter, StringSectionBuilder &stringSection,
1227 PostProcessFn postProcessFn,
bool shouldElideData)
1228 : emitter(emitter), stringSection(stringSection),
1229 postProcessFn(postProcessFn), shouldElideData(shouldElideData) {}
1230 ~ResourceBuilder()
override =
default;
1233 uint32_t dataAlignment)
final {
1234 if (!shouldElideData)
1235 emitter.emitOwnedBlobAndAlignment(data, dataAlignment,
"resource blob");
1238 void buildBool(StringRef key,
bool data)
final {
1239 if (!shouldElideData)
1240 emitter.emitByte(data,
"resource bool");
1243 void buildString(StringRef key, StringRef data)
final {
1244 if (!shouldElideData)
1245 emitter.emitVarInt(stringSection.insert(data),
"resource string");
1250 EncodingEmitter &emitter;
1251 StringSectionBuilder &stringSection;
1252 PostProcessFn postProcessFn;
1253 bool shouldElideData =
false;
1257 void BytecodeWriter::writeResourceSection(
Operation *op,
1258 EncodingEmitter &emitter) {
1259 EncodingEmitter resourceEmitter;
1260 EncodingEmitter resourceOffsetEmitter;
1261 uint64_t prevOffset = 0;
1268 uint64_t curOffset = resourceEmitter.size();
1269 curResourceEntries.emplace_back(key,
kind, curOffset - prevOffset);
1270 prevOffset = curOffset;
1274 auto emitResourceGroup = [&](uint64_t key) {
1275 resourceOffsetEmitter.emitVarInt(key,
"resource group key");
1276 resourceOffsetEmitter.emitVarInt(curResourceEntries.size(),
1277 "resource group size");
1278 for (
auto [key,
kind, size] : curResourceEntries) {
1279 resourceOffsetEmitter.emitVarInt(stringSection.insert(key),
1281 resourceOffsetEmitter.emitVarInt(size,
"resource size");
1282 resourceOffsetEmitter.emitByte(
kind,
"resource kind");
1287 ResourceBuilder entryBuilder(resourceEmitter, stringSection,
1288 appendResourceOffset,
1289 config.shouldElideResourceData);
1292 resourceOffsetEmitter.emitVarInt(
config.externalResourcePrinters.size(),
1293 "external resource printer count");
1294 for (
const auto &printer :
config.externalResourcePrinters) {
1295 curResourceEntries.clear();
1296 printer->buildResources(op, entryBuilder);
1297 emitResourceGroup(stringSection.insert(printer->getName()));
1302 if (!dialect.asmInterface)
1304 curResourceEntries.clear();
1305 dialect.asmInterface->buildResources(op, dialect.resources, entryBuilder);
1310 for (
const auto &resource : dialect.resourceMap)
1311 if (resource.second->isDeclaration)
1315 if (!curResourceEntries.empty())
1316 emitResourceGroup(dialect.number);
1320 if (resourceOffsetEmitter.size() == 0)
1324 std::move(resourceOffsetEmitter));
1332 void BytecodeWriter::writeStringSection(EncodingEmitter &emitter) {
1333 EncodingEmitter stringEmitter;
1334 stringSection.write(stringEmitter);
1342 void BytecodeWriter::writePropertiesSection(EncodingEmitter &emitter) {
1343 EncodingEmitter propertiesEmitter;
1344 propertiesSection.write(propertiesEmitter);
1346 std::move(propertiesEmitter));
1355 BytecodeWriter writer(op,
config);
1356 return writer.write(op, os);
static void writeDialectGrouping(EncodingEmitter &emitter, EntriesT &&entries, EntryCallbackT &&callback)
Write the given entries in contiguous groups with the same parent dialect.
union mlir::linalg::@1243::ArityGroupAndKind::Kind kind
static Value max(ImplicitLocOpBuilder &builder, Value value, Value bound)
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 represents an opaque handle to a dialect resource entry.
This class is used to build resource entries for use by the printer.
A class to interact with the attributes and types printer when emitting MLIR bytecode.
Attributes are known-constant values of operations.
This class represents an argument of a Block.
Block represents an ordered list of Operations.
BlockArgListType getArguments()
This class contains the configuration used for the bytecode writer.
void attachTypeCallback(std::unique_ptr< AttrTypeBytecodeWriter< Type >> callback)
llvm::StringMap< std::unique_ptr< DialectVersion > > & getDialectVersionMap() const
A map containing the dialect versions to emit.
void setElideResourceDataFlag(bool shouldElideResourceData=true)
Set a boolean flag to skip emission of resources into the bytecode file.
BytecodeWriterConfig(StringRef producer="MLIR" LLVM_VERSION_STRING)
producer is an optional string that can be used to identify the producer of the bytecode when reading...
void attachFallbackResourcePrinter(FallbackAsmResourceMap &map)
Attach resource printers to the AsmState for the fallback resources in the given map.
int64_t getDesiredBytecodeVersion() const
Get the set desired bytecode version to emit.
void setDialectVersion(std::unique_ptr< DialectVersion > dialectVersion) const
Set a given dialect version to emit on the map.
ArrayRef< std::unique_ptr< AttrTypeBytecodeWriter< Type > > > getTypeWriterCallbacks() const
ArrayRef< std::unique_ptr< AttrTypeBytecodeWriter< Attribute > > > getAttributeWriterCallbacks() const
Retrieve the callbacks.
void setDesiredBytecodeVersion(int64_t bytecodeVersion)
Set the desired bytecode version to emit.
void attachResourcePrinter(std::unique_ptr< AsmResourcePrinter > printer)
Attach the given resource printer to the writer configuration.
void attachAttributeCallback(std::unique_ptr< AttrTypeBytecodeWriter< Attribute >> callback)
Attach a custom bytecode printer callback to the configuration for the emission of custom type/attrib...
This class defines a virtual interface for writing to a bytecode stream, providing hooks into the byt...
A fallback map containing external resources not explicitly handled by another parser/printer.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Operation is the basic unit of execution within MLIR.
DictionaryAttr getAttrDictionary()
Return all of the attributes on this operation as a DictionaryAttr.
unsigned getNumSuccessors()
bool isRegistered()
Returns true if this operation has a registered operation description, otherwise false.
unsigned getNumRegions()
Returns the number of regions held by this operation.
Location getLoc()
The source location the operation was defined or derived from.
unsigned getNumOperands()
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
MutableArrayRef< Region > getRegions()
Returns the regions held by this operation.
OperationName getName()
The name of an operation is the key identifier for it.
DictionaryAttr getDiscardableAttrDictionary()
Return all of the discardable attributes on this operation as a DictionaryAttr.
result_type_range getResultTypes()
operand_range getOperands()
Returns an iterator on the underlying Value's.
SuccessorRange getSuccessors()
result_range getResults()
int getPropertiesStorageSize() const
Returns the properties storage size.
OpaqueProperties getPropertiesStorage()
Returns the properties storage.
unsigned getNumResults()
Return the number of results held by this operation.
This class contains a list of basic blocks and a link to the parent operation it is attached to.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
This class provides an abstraction over the different types of ranges over Values.
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
This class manages numbering IR entities in preparation of bytecode emission.
@ kAttrType
This section contains the attributes and types referenced within an IR module.
@ kAttrTypeOffset
This section contains the offsets for the attribute and types within the AttrType section.
@ kIR
This section contains the list of operations serialized into the bytecode, and their nested regions/o...
@ kResource
This section contains the resources of the bytecode.
@ kResourceOffset
This section contains the offsets of resources within the Resource section.
@ kDialect
This section contains the dialects referenced within an IR module.
@ kString
This section contains strings referenced within the bytecode.
@ kDialectVersions
This section contains the versions of each dialect.
@ kProperties
This section contains the properties for the operations.
static uint64_t getUseID(OperandT &val, unsigned ownerID)
Get the unique ID of a value use.
@ kUseListOrdering
Use-list ordering started to be encoded in version 3.
@ kAlignmentByte
An arbitrary value used to fill alignment padding.
@ kVersion
The current bytecode version.
@ kLazyLoading
Support for lazy-loading of isolated region was added in version 2.
@ kDialectVersioning
Dialects versioning was added in version 1.
@ kElideUnknownBlockArgLocation
Avoid recording unknown locations on block arguments (compression) started in version 4.
@ kNativePropertiesEncoding
Support for encoding properties natively in bytecode instead of merged with the discardable attribute...
@ kMinSupportedVersion
The minimum supported version of the bytecode.
constexpr void enumerate(std::tuple< Tys... > &tuple, CallbackT &&callback)
Include the generated interface declarations.
const FrozenRewritePatternSet GreedyRewriteConfig config
AsmResourceEntryKind
This enum represents the different kinds of resource values.
@ Blob
A blob of data with an accompanying alignment.
LogicalResult writeBytecodeToFile(Operation *op, raw_ostream &os, const BytecodeWriterConfig &config={})
Write the bytecode for the given operation to the provided output stream.
StringRef producer
The producer of the bytecode.
llvm::StringMap< std::unique_ptr< DialectVersion > > dialectVersionMap
A map containing dialect version information for each dialect to emit.
llvm::SmallVector< std::unique_ptr< AttrTypeBytecodeWriter< Attribute > > > attributeWriterCallbacks
Printer callbacks used to emit custom type and attribute encodings.
SmallVector< std::unique_ptr< AsmResourcePrinter > > externalResourcePrinters
A collection of non-dialect resource printers.
llvm::SmallVector< std::unique_ptr< AttrTypeBytecodeWriter< Type > > > typeWriterCallbacks
This class represents a numbering entry for an Dialect.
unsigned number
The number assigned to the dialect.