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/Endian.h"
23 #include "llvm/Support/raw_ostream.h"
26 #define DEBUG_TYPE "mlir-bytecode-writer"
36 Impl(StringRef producer) : producer(producer) {}
44 bool shouldElideResourceData =
false;
63 :
impl(std::make_unique<
Impl>(producer)) {}
73 return impl->attributeWriterCallbacks;
78 return impl->typeWriterCallbacks;
83 impl->attributeWriterCallbacks.emplace_back(std::move(callback));
88 impl->typeWriterCallbacks.emplace_back(std::move(callback));
92 std::unique_ptr<AsmResourcePrinter> printer) {
93 impl->externalResourcePrinters.emplace_back(std::move(printer));
97 bool shouldElideResourceData) {
98 impl->shouldElideResourceData = shouldElideResourceData;
102 impl->bytecodeVersion = bytecodeVersion;
106 return impl->bytecodeVersion;
109 llvm::StringMap<std::unique_ptr<DialectVersion>> &
111 return impl->dialectVersionMap;
115 llvm::StringRef dialectName,
116 std::unique_ptr<DialectVersion> dialectVersion)
const {
117 assert(!
impl->dialectVersionMap.contains(dialectName) &&
118 "cannot override a previously set dialect version");
119 impl->dialectVersionMap.insert({dialectName, std::move(dialectVersion)});
132 class EncodingEmitter {
134 EncodingEmitter() =
default;
135 EncodingEmitter(
const EncodingEmitter &) =
delete;
136 EncodingEmitter &operator=(
const EncodingEmitter &) =
delete;
139 void writeTo(raw_ostream &os)
const;
142 size_t size()
const {
return prevResultSize + currentResult.size(); }
149 void patchByte(uint64_t offset, uint8_t value, StringLiteral desc) {
150 LLVM_DEBUG(llvm::dbgs() <<
"patchByte(" << offset <<
',' << uint64_t(value)
151 <<
")\t" << desc <<
'\n');
152 assert(offset < size() && offset >= prevResultSize &&
153 "cannot patch previously emitted data");
154 currentResult[offset - prevResultSize] = value;
160 LLVM_DEBUG(llvm::dbgs()
161 <<
"emitOwnedBlob(" << data.size() <<
"b)\t" << desc <<
'\n');
163 appendResult(std::move(currentResult));
164 appendOwnedResult(data);
172 StringLiteral desc) {
173 emitVarInt(alignment, desc);
174 emitVarInt(data.size(), desc);
177 emitOwnedBlob(data, desc);
179 void emitOwnedBlobAndAlignment(
ArrayRef<char> data, uint32_t alignment,
180 StringLiteral desc) {
183 emitOwnedBlobAndAlignment(castedData, alignment, desc);
187 void alignTo(
unsigned alignment) {
190 assert(llvm::isPowerOf2_32(alignment) &&
"expected valid alignment");
194 size_t curOffset = size();
195 size_t paddingSize = llvm::alignTo(curOffset, alignment) - curOffset;
196 while (paddingSize--)
200 requiredAlignment =
std::max(requiredAlignment, alignment);
207 template <
typename T>
208 void emitByte(T
byte, StringLiteral desc) {
209 LLVM_DEBUG(llvm::dbgs()
210 <<
"emitByte(" << uint64_t(
byte) <<
")\t" << desc <<
'\n');
211 currentResult.push_back(
static_cast<uint8_t
>(
byte));
216 LLVM_DEBUG(llvm::dbgs()
217 <<
"emitBytes(" << bytes.size() <<
"b)\t" << desc <<
'\n');
218 llvm::append_range(currentResult, bytes);
228 void emitVarInt(uint64_t value, StringLiteral desc) {
229 LLVM_DEBUG(llvm::dbgs() <<
"emitVarInt(" << value <<
")\t" << desc <<
'\n');
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),
501 std::optional<ssize_t> emit(
Operation *op) {
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 newStorage.insert(newStorage.end(), sizeScratch.begin(), sizeScratch.end());
569 newStorage.insert(newStorage.end(), rawProperties.begin(),
570 rawProperties.end());
574 auto inserted = propertiesUniquing.insert(
576 if (!inserted.second)
577 propertiesStorage.pop_back();
578 return inserted.first->getSecond();
582 std::vector<std::vector<char>> propertiesStorage;
586 StringSectionBuilder &stringSection;
594 class RawEmitterOstream :
public raw_ostream {
596 explicit RawEmitterOstream(EncodingEmitter &emitter) : emitter(emitter) {
601 void write_impl(
const char *ptr,
size_t size)
override {
602 emitter.emitBytes({
reinterpret_cast<const uint8_t *
>(ptr), size},
605 uint64_t current_pos()
const override {
return emitter.size(); }
608 EncodingEmitter &emitter;
612 void EncodingEmitter::writeTo(raw_ostream &os)
const {
613 for (
auto &prevResult : prevResultList)
614 os.write((
const char *)prevResult.data(), prevResult.size());
615 os.write((
const char *)currentResult.data(), currentResult.size());
618 void EncodingEmitter::emitMultiByteVarInt(uint64_t value, StringLiteral desc) {
622 uint64_t it = value >> 7;
623 for (
size_t numBytes = 2; numBytes < 9; ++numBytes) {
624 if (LLVM_LIKELY(it >>= 7) == 0) {
625 uint64_t encodedValue = (value << 1) | 0x1;
626 encodedValue <<= (numBytes - 1);
627 llvm::support::ulittle64_t encodedValueLE(encodedValue);
628 emitBytes({
reinterpret_cast<uint8_t *
>(&encodedValueLE), numBytes}, desc);
636 llvm::support::ulittle64_t valueLE(value);
637 emitBytes({
reinterpret_cast<uint8_t *
>(&valueLE),
sizeof(valueLE)}, desc);
645 class BytecodeWriter {
648 : numberingState(op, config), config(config.getImpl()),
649 propertiesSection(numberingState, stringSection, config.getImpl()) {}
652 LogicalResult write(
Operation *rootOp, raw_ostream &os);
658 void writeDialectSection(EncodingEmitter &emitter);
663 void writeAttrTypeSection(EncodingEmitter &emitter);
668 LogicalResult writeBlock(EncodingEmitter &emitter,
Block *block);
669 LogicalResult writeOp(EncodingEmitter &emitter,
Operation *op);
670 LogicalResult writeRegion(EncodingEmitter &emitter,
Region *region);
671 LogicalResult writeIRSection(EncodingEmitter &emitter,
Operation *op);
673 LogicalResult writeRegions(EncodingEmitter &emitter,
675 return success(llvm::all_of(regions, [&](
Region ®ion) {
676 return succeeded(writeRegion(emitter, ®ion));
683 void writeResourceSection(
Operation *op, EncodingEmitter &emitter);
688 void writeStringSection(EncodingEmitter &emitter);
693 void writePropertiesSection(EncodingEmitter &emitter);
698 void writeUseListOrders(EncodingEmitter &emitter, uint8_t &opEncodingMask,
705 StringSectionBuilder stringSection;
714 PropertiesSectionBuilder propertiesSection;
718 LogicalResult BytecodeWriter::write(
Operation *rootOp, raw_ostream &os) {
719 EncodingEmitter emitter;
723 emitter.emitString(
"ML\xefR",
"bytecode header");
729 <<
"unsupported version requested " << config.bytecodeVersion
730 <<
", must be in range ["
733 emitter.emitVarInt(config.bytecodeVersion,
"bytecode version");
736 emitter.emitNulTerminatedString(config.producer,
"bytecode producer");
739 writeDialectSection(emitter);
742 writeAttrTypeSection(emitter);
745 if (failed(writeIRSection(emitter, rootOp)))
749 writeResourceSection(rootOp, emitter);
752 writeStringSection(emitter);
756 writePropertiesSection(emitter);
757 else if (!propertiesSection.empty())
759 "unexpected properties emitted incompatible with bytecode <5");
774 template <
typename EntriesT,
typename EntryCallbackT>
776 EntryCallbackT &&callback) {
777 for (
auto it = entries.begin(), e = entries.end(); it != e;) {
778 auto groupStart = it++;
782 it = std::find_if(it, e, [&](
const auto &entry) {
783 return entry.dialect != currentDialect;
787 emitter.emitVarInt(currentDialect->
number,
"dialect number");
788 emitter.emitVarInt(std::distance(groupStart, it),
"dialect offset");
791 for (
auto &entry : llvm::make_range(groupStart, it))
796 void BytecodeWriter::writeDialectSection(EncodingEmitter &emitter) {
797 EncodingEmitter dialectEmitter;
800 auto dialects = numberingState.getDialects();
801 dialectEmitter.emitVarInt(llvm::size(dialects),
"dialects count");
804 size_t nameID = stringSection.insert(dialect.name);
807 dialectEmitter.emitVarInt(nameID,
"dialect name ID");
812 EncodingEmitter versionEmitter;
813 if (dialect.interface) {
815 DialectWriter versionWriter(config.bytecodeVersion, versionEmitter,
816 numberingState, stringSection,
817 config.dialectVersionMap);
818 dialect.interface->writeVersion(versionWriter);
824 size_t versionAvailable = versionEmitter.size() > 0;
825 dialectEmitter.emitVarIntWithFlag(nameID, versionAvailable,
827 if (versionAvailable)
829 std::move(versionEmitter));
833 dialectEmitter.emitVarInt(size(numberingState.getOpNames()),
838 size_t stringId = stringSection.insert(name.name.stripDialect());
840 dialectEmitter.emitVarInt(stringId,
"dialect op name");
842 dialectEmitter.emitVarIntWithFlag(stringId, name.name.isRegistered(),
853 void BytecodeWriter::writeAttrTypeSection(EncodingEmitter &emitter) {
854 EncodingEmitter attrTypeEmitter;
855 EncodingEmitter offsetEmitter;
856 offsetEmitter.emitVarInt(llvm::size(numberingState.getAttributes()),
858 offsetEmitter.emitVarInt(llvm::size(numberingState.getTypes()),
862 uint64_t prevOffset = 0;
863 auto emitAttrOrType = [&](
auto &entry) {
864 auto entryValue = entry.getValue();
866 auto emitAttrOrTypeRawImpl = [&]() ->
void {
867 RawEmitterOstream(attrTypeEmitter) << entryValue;
868 attrTypeEmitter.emitByte(0,
"attr/type separator");
870 auto emitAttrOrTypeImpl = [&]() ->
bool {
873 if (entryValue.template hasTrait<TypeTrait::IsMutable>() ||
874 entryValue.template hasTrait<AttributeTrait::IsMutable>()) {
875 emitAttrOrTypeRawImpl();
879 DialectWriter dialectWriter(config.bytecodeVersion, attrTypeEmitter,
880 numberingState, stringSection,
881 config.dialectVersionMap);
882 if constexpr (std::is_same_v<std::decay_t<decltype(entryValue)>,
Type>) {
883 for (
const auto &callback : config.typeWriterCallbacks) {
884 if (succeeded(callback->write(entryValue, dialectWriter)))
888 entry.dialect->interface) {
889 if (succeeded(interface->writeType(entryValue, dialectWriter)))
893 for (
const auto &callback : config.attributeWriterCallbacks) {
894 if (succeeded(callback->write(entryValue, dialectWriter)))
898 entry.dialect->interface) {
899 if (succeeded(interface->writeAttribute(entryValue, dialectWriter)))
906 emitAttrOrTypeRawImpl();
910 bool hasCustomEncoding = emitAttrOrTypeImpl();
913 uint64_t curOffset = attrTypeEmitter.size();
914 offsetEmitter.emitVarIntWithFlag(curOffset - prevOffset, hasCustomEncoding,
916 prevOffset = curOffset;
927 std::move(offsetEmitter));
934 LogicalResult BytecodeWriter::writeBlock(EncodingEmitter &emitter,
937 bool hasArgs = !args.empty();
942 unsigned numOps = numberingState.getOperationCount(block);
943 emitter.emitVarIntWithFlag(numOps, hasArgs,
"block num ops");
947 emitter.emitVarInt(args.size(),
"block args count");
951 emitter.emitVarIntWithFlag(numberingState.getNumber(arg.getType()),
952 !isa<UnknownLoc>(argLoc),
"block arg type");
953 if (!isa<UnknownLoc>(argLoc))
954 emitter.emitVarInt(numberingState.getNumber(argLoc),
955 "block arg location");
957 emitter.emitVarInt(numberingState.getNumber(arg.getType()),
959 emitter.emitVarInt(numberingState.getNumber(argLoc),
960 "block arg location");
964 uint64_t maskOffset = emitter.size();
965 uint8_t encodingMask = 0;
966 emitter.emitByte(0,
"use-list separator");
967 writeUseListOrders(emitter, encodingMask, args);
969 emitter.patchByte(maskOffset, encodingMask,
"block patch encoding");
975 if (failed(writeOp(emitter, &op)))
980 LogicalResult BytecodeWriter::writeOp(EncodingEmitter &emitter,
Operation *op) {
981 emitter.emitVarInt(numberingState.getNumber(op->
getName()),
"op name ID");
986 uint64_t maskOffset = emitter.size();
987 uint8_t opEncodingMask = 0;
988 emitter.emitByte(0,
"op separator");
991 emitter.emitVarInt(numberingState.getNumber(op->
getLoc()),
"op location");
1003 if (!attrs.empty()) {
1005 emitter.emitVarInt(numberingState.getNumber(attrs),
"op attrs count");
1011 std::optional<ssize_t> propertiesId = propertiesSection.emit(op);
1012 if (propertiesId.has_value()) {
1014 emitter.emitVarInt(*propertiesId,
"op properties ID");
1021 emitter.emitVarInt(numResults,
"op results count");
1023 emitter.emitVarInt(numberingState.getNumber(type),
"op result type");
1029 emitter.emitVarInt(numOperands,
"op operands count");
1031 emitter.emitVarInt(numberingState.getNumber(operand),
"op operand types");
1037 emitter.emitVarInt(numSuccessors,
"op successors count");
1039 emitter.emitVarInt(numberingState.getNumber(successor),
"op successor");
1053 emitter.patchByte(maskOffset, opEncodingMask,
"op encoding mask");
1060 bool isIsolatedFromAbove = numberingState.isIsolatedFromAbove(op);
1061 emitter.emitVarIntWithFlag(numRegions, isIsolatedFromAbove,
1062 "op regions count");
1066 if (isIsolatedFromAbove &&
1068 EncodingEmitter regionEmitter;
1069 if (failed(writeRegions(regionEmitter, op->
getRegions())))
1073 }
else if (failed(writeRegions(emitter, op->
getRegions()))) {
1080 void BytecodeWriter::writeUseListOrders(EncodingEmitter &emitter,
1081 uint8_t &opEncodingMask,
1086 auto value = item.value();
1089 if (value.use_empty() || value.hasOneUse())
1095 bool alreadyOrdered =
true;
1096 auto &firstUse = *value.use_begin();
1098 firstUse, numberingState.getNumber(firstUse.getOwner()));
1104 use.value(), numberingState.getNumber(use.value().getOwner()));
1108 alreadyOrdered &= (prevID > currentID);
1109 useListPairs.push_back({use.index(), currentID});
1119 useListPairs.begin(), useListPairs.end(),
1120 [](
auto elem1,
auto elem2) { return elem1.second > elem2.second; });
1122 map.try_emplace(item.index(), llvm::map_range(useListPairs, [](
auto elem) {
1133 if (range.size() != 1) {
1134 emitter.emitVarInt(map.size(),
"custom use-list size");
1137 for (
const auto &item : map) {
1138 auto resultIdx = item.getFirst();
1139 auto useListOrder = item.getSecond();
1144 size_t shuffledElements =
1146 [](
auto item) {
return item.index() != item.value(); });
1147 bool indexPairEncoding = shuffledElements < (useListOrder.size() / 2);
1150 if (range.size() != 1)
1151 emitter.emitVarInt(resultIdx,
"use-list result index");
1153 if (indexPairEncoding) {
1154 emitter.emitVarIntWithFlag(shuffledElements * 2, indexPairEncoding,
1155 "use-list index pair size");
1157 if (pair.index() != pair.value()) {
1158 emitter.emitVarInt(pair.value(),
"use-list index pair first");
1159 emitter.emitVarInt(pair.index(),
"use-list index pair second");
1163 emitter.emitVarIntWithFlag(useListOrder.size(), indexPairEncoding,
1165 for (
const auto &index : useListOrder)
1166 emitter.emitVarInt(index,
"use-list order");
1171 LogicalResult BytecodeWriter::writeRegion(EncodingEmitter &emitter,
1175 if (region->
empty()) {
1176 emitter.emitVarInt( 0,
"region block count empty");
1181 unsigned numBlocks, numValues;
1182 std::tie(numBlocks, numValues) = numberingState.getBlockValueCount(region);
1183 emitter.emitVarInt(numBlocks,
"region block count");
1184 emitter.emitVarInt(numValues,
"region value count");
1187 for (
Block &block : *region)
1188 if (failed(writeBlock(emitter, &block)))
1193 LogicalResult BytecodeWriter::writeIRSection(EncodingEmitter &emitter,
1195 EncodingEmitter irEmitter;
1200 irEmitter.emitVarIntWithFlag( 1,
false,
"ir section");
1203 if (failed(writeOp(irEmitter, op)))
1220 ResourceBuilder(EncodingEmitter &emitter, StringSectionBuilder &stringSection,
1221 PostProcessFn postProcessFn,
bool shouldElideData)
1222 : emitter(emitter), stringSection(stringSection),
1223 postProcessFn(postProcessFn), shouldElideData(shouldElideData) {}
1224 ~ResourceBuilder()
override =
default;
1227 uint32_t dataAlignment)
final {
1228 if (!shouldElideData)
1229 emitter.emitOwnedBlobAndAlignment(data, dataAlignment,
"resource blob");
1232 void buildBool(StringRef key,
bool data)
final {
1233 if (!shouldElideData)
1234 emitter.emitByte(data,
"resource bool");
1237 void buildString(StringRef key, StringRef data)
final {
1238 if (!shouldElideData)
1239 emitter.emitVarInt(stringSection.insert(data),
"resource string");
1244 EncodingEmitter &emitter;
1245 StringSectionBuilder &stringSection;
1246 PostProcessFn postProcessFn;
1247 bool shouldElideData =
false;
1251 void BytecodeWriter::writeResourceSection(
Operation *op,
1252 EncodingEmitter &emitter) {
1253 EncodingEmitter resourceEmitter;
1254 EncodingEmitter resourceOffsetEmitter;
1255 uint64_t prevOffset = 0;
1262 uint64_t curOffset = resourceEmitter.size();
1263 curResourceEntries.emplace_back(key, kind, curOffset - prevOffset);
1264 prevOffset = curOffset;
1268 auto emitResourceGroup = [&](uint64_t key) {
1269 resourceOffsetEmitter.emitVarInt(key,
"resource group key");
1270 resourceOffsetEmitter.emitVarInt(curResourceEntries.size(),
1271 "resource group size");
1272 for (
auto [key, kind, size] : curResourceEntries) {
1273 resourceOffsetEmitter.emitVarInt(stringSection.insert(key),
1275 resourceOffsetEmitter.emitVarInt(size,
"resource size");
1276 resourceOffsetEmitter.emitByte(kind,
"resource kind");
1281 ResourceBuilder entryBuilder(resourceEmitter, stringSection,
1282 appendResourceOffset,
1283 config.shouldElideResourceData);
1286 resourceOffsetEmitter.emitVarInt(config.externalResourcePrinters.size(),
1287 "external resource printer count");
1288 for (
const auto &printer : config.externalResourcePrinters) {
1289 curResourceEntries.clear();
1290 printer->buildResources(op, entryBuilder);
1291 emitResourceGroup(stringSection.insert(printer->getName()));
1296 if (!dialect.asmInterface)
1298 curResourceEntries.clear();
1299 dialect.asmInterface->buildResources(op, dialect.resources, entryBuilder);
1304 for (
const auto &resource : dialect.resourceMap)
1305 if (resource.second->isDeclaration)
1309 if (!curResourceEntries.empty())
1310 emitResourceGroup(dialect.number);
1314 if (resourceOffsetEmitter.size() == 0)
1318 std::move(resourceOffsetEmitter));
1325 void BytecodeWriter::writeStringSection(EncodingEmitter &emitter) {
1326 EncodingEmitter stringEmitter;
1327 stringSection.write(stringEmitter);
1334 void BytecodeWriter::writePropertiesSection(EncodingEmitter &emitter) {
1335 EncodingEmitter propertiesEmitter;
1336 propertiesSection.write(propertiesEmitter);
1338 std::move(propertiesEmitter));
1347 BytecodeWriter writer(op, config);
1348 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.
static Value max(ImplicitLocOpBuilder &builder, Value value, Value bound)
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.
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.