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)) {}
76 return impl->attributeWriterCallbacks;
81 return impl->typeWriterCallbacks;
86 impl->attributeWriterCallbacks.emplace_back(std::move(callback));
91 impl->typeWriterCallbacks.emplace_back(std::move(callback));
95 std::unique_ptr<AsmResourcePrinter> printer) {
96 impl->externalResourcePrinters.emplace_back(std::move(printer));
100 bool shouldElideResourceData) {
101 impl->shouldElideResourceData = shouldElideResourceData;
105 impl->bytecodeVersion = bytecodeVersion;
109 return impl->bytecodeVersion;
112 llvm::StringMap<std::unique_ptr<DialectVersion>> &
114 return impl->dialectVersionMap;
118 llvm::StringRef dialectName,
119 std::unique_ptr<DialectVersion> dialectVersion)
const {
120 assert(!
impl->dialectVersionMap.contains(dialectName) &&
121 "cannot override a previously set dialect version");
122 impl->dialectVersionMap.insert({dialectName, std::move(dialectVersion)});
135 class EncodingEmitter {
137 EncodingEmitter() =
default;
138 EncodingEmitter(
const EncodingEmitter &) =
delete;
139 EncodingEmitter &operator=(
const EncodingEmitter &) =
delete;
142 void writeTo(raw_ostream &os)
const;
145 size_t size()
const {
return prevResultSize + currentResult.size(); }
152 void patchByte(uint64_t offset, uint8_t value, StringLiteral desc) {
153 LLVM_DEBUG(llvm::dbgs() <<
"patchByte(" << offset <<
',' << uint64_t(value)
154 <<
")\t" << desc <<
'\n');
155 assert(offset < size() && offset >= prevResultSize &&
156 "cannot patch previously emitted data");
157 currentResult[offset - prevResultSize] = value;
163 LLVM_DEBUG(llvm::dbgs()
164 <<
"emitOwnedBlob(" << data.size() <<
"b)\t" << desc <<
'\n');
166 appendResult(std::move(currentResult));
167 appendOwnedResult(data);
175 StringLiteral desc) {
176 emitVarInt(alignment, desc);
177 emitVarInt(data.size(), desc);
180 emitOwnedBlob(data, desc);
182 void emitOwnedBlobAndAlignment(
ArrayRef<char> data, uint32_t alignment,
183 StringLiteral desc) {
186 emitOwnedBlobAndAlignment(castedData, alignment, desc);
190 void alignTo(
unsigned alignment) {
193 assert(llvm::isPowerOf2_32(alignment) &&
"expected valid alignment");
197 size_t curOffset = size();
198 size_t paddingSize = llvm::alignTo(curOffset, alignment) - curOffset;
199 while (paddingSize--)
203 requiredAlignment =
std::max(requiredAlignment, alignment);
210 template <
typename T>
211 void emitByte(T
byte, StringLiteral desc) {
212 LLVM_DEBUG(llvm::dbgs()
213 <<
"emitByte(" << uint64_t(
byte) <<
")\t" << desc <<
'\n');
214 currentResult.push_back(
static_cast<uint8_t
>(
byte));
219 LLVM_DEBUG(llvm::dbgs()
220 <<
"emitBytes(" << bytes.size() <<
"b)\t" << desc <<
'\n');
221 llvm::append_range(currentResult, bytes);
231 void emitVarInt(uint64_t value, StringLiteral desc) {
232 LLVM_DEBUG(llvm::dbgs() <<
"emitVarInt(" << value <<
")\t" << desc <<
'\n');
236 if ((value >> 7) == 0)
237 return emitByte((value << 1) | 0x1, desc);
238 emitMultiByteVarInt(value, desc);
245 void emitSignedVarInt(uint64_t value, StringLiteral desc) {
246 emitVarInt((value << 1) ^ (uint64_t)((int64_t)value >> 63), desc);
251 void emitVarIntWithFlag(uint64_t value,
bool flag, StringLiteral desc) {
252 emitVarInt((value << 1) | (flag ? 1 : 0), desc);
259 void emitNulTerminatedString(StringRef str, StringLiteral desc) {
260 emitString(str, desc);
261 emitByte(0,
"null terminator");
265 void emitString(StringRef str, StringLiteral desc) {
266 emitBytes({
reinterpret_cast<const uint8_t *
>(str.data()), str.size()},
279 uint64_t codeOffset = currentResult.size();
280 emitByte(code,
"section code");
281 emitVarInt(emitter.size(),
"section size");
284 unsigned emitterAlign = emitter.requiredAlignment;
285 if (emitterAlign > 1) {
286 if (size() & (emitterAlign - 1)) {
287 emitVarInt(emitterAlign,
"section alignment");
288 alignTo(emitterAlign);
292 currentResult[codeOffset] |= 0b10000000;
296 requiredAlignment =
std::max(requiredAlignment, emitterAlign);
302 appendResult(std::move(currentResult));
303 for (std::vector<uint8_t> &result : emitter.prevResultStorage)
304 prevResultStorage.push_back(std::move(result));
305 llvm::append_range(prevResultList, emitter.prevResultList);
306 prevResultSize += emitter.prevResultSize;
307 appendResult(std::move(emitter.currentResult));
315 LLVM_ATTRIBUTE_NOINLINE
void emitMultiByteVarInt(uint64_t value,
319 void appendResult(std::vector<uint8_t> &&result) {
322 prevResultStorage.emplace_back(std::move(result));
323 appendOwnedResult(prevResultStorage.back());
328 prevResultSize += result.size();
329 prevResultList.emplace_back(result);
337 std::vector<uint8_t> currentResult;
338 std::vector<ArrayRef<uint8_t>> prevResultList;
339 std::vector<std::vector<uint8_t>> prevResultStorage;
343 size_t prevResultSize = 0;
346 unsigned requiredAlignment = 1;
355 class StringSectionBuilder {
359 size_t insert(StringRef str) {
360 auto it = strings.insert({llvm::CachedHashStringRef(str), strings.size()});
361 return it.first->second;
365 void write(EncodingEmitter &emitter) {
366 emitter.emitVarInt(strings.size(),
"string section size");
370 for (
const auto &it : llvm::reverse(strings))
371 emitter.emitVarInt(it.first.size() + 1,
"string size");
373 for (
const auto &it : strings)
374 emitter.emitNulTerminatedString(it.first.val(),
"string");
380 llvm::MapVector<llvm::CachedHashStringRef, size_t> strings;
385 using DialectVersionMapT = llvm::StringMap<std::unique_ptr<DialectVersion>>;
388 DialectWriter(int64_t bytecodeVersion, EncodingEmitter &emitter,
390 StringSectionBuilder &stringSection,
391 const DialectVersionMapT &dialectVersionMap)
392 : bytecodeVersion(bytecodeVersion), emitter(emitter),
393 numberingState(numberingState), stringSection(stringSection),
394 dialectVersionMap(dialectVersionMap) {}
400 void writeAttribute(
Attribute attr)
override {
401 emitter.emitVarInt(numberingState.getNumber(attr),
"dialect attr");
403 void writeOptionalAttribute(
Attribute attr)
override {
405 emitter.emitVarInt(0,
"dialect optional attr none");
408 emitter.emitVarIntWithFlag(numberingState.getNumber(attr),
true,
409 "dialect optional attr");
412 void writeType(
Type type)
override {
413 emitter.emitVarInt(numberingState.getNumber(type),
"dialect type");
417 emitter.emitVarInt(numberingState.getNumber(resource),
"dialect resource");
424 void writeVarInt(uint64_t value)
override {
425 emitter.emitVarInt(value,
"dialect writer");
428 void writeSignedVarInt(int64_t value)
override {
429 emitter.emitSignedVarInt(value,
"dialect writer");
432 void writeAPIntWithKnownWidth(
const APInt &value)
override {
433 size_t bitWidth = value.getBitWidth();
438 return emitter.emitByte(value.getLimitedValue(),
"dialect APInt");
442 return emitter.emitSignedVarInt(value.getLimitedValue(),
"dialect APInt");
447 unsigned numActiveWords = value.getActiveWords();
448 emitter.emitVarInt(numActiveWords,
"dialect APInt word count");
450 const uint64_t *rawValueData = value.getRawData();
451 for (
unsigned i = 0; i < numActiveWords; ++i)
452 emitter.emitSignedVarInt(rawValueData[i],
"dialect APInt word");
455 void writeAPFloatWithKnownSemantics(
const APFloat &value)
override {
456 writeAPIntWithKnownWidth(value.bitcastToAPInt());
459 void writeOwnedString(StringRef str)
override {
460 emitter.emitVarInt(stringSection.insert(str),
"dialect string");
464 emitter.emitVarInt(blob.size(),
"dialect blob");
465 emitter.emitOwnedBlob(
471 void writeOwnedBool(
bool value)
override {
472 emitter.emitByte(value,
"dialect bool");
475 int64_t getBytecodeVersion()
const override {
return bytecodeVersion; }
477 FailureOr<const DialectVersion *>
478 getDialectVersion(StringRef dialectName)
const override {
479 auto dialectEntry = dialectVersionMap.find(dialectName);
480 if (dialectEntry == dialectVersionMap.end())
482 return dialectEntry->getValue().get();
486 int64_t bytecodeVersion;
487 EncodingEmitter &emitter;
489 StringSectionBuilder &stringSection;
490 const DialectVersionMapT &dialectVersionMap;
494 class PropertiesSectionBuilder {
497 StringSectionBuilder &stringSection,
499 : numberingState(numberingState), stringSection(stringSection),
505 EncodingEmitter propertiesEmitter;
513 EncodingEmitter sizeEmitter;
514 sizeEmitter.emitVarInt(numberingState.getNumber(prop),
"properties size");
516 llvm::raw_svector_ostream os(scratch);
517 sizeEmitter.writeTo(os);
518 return emit(scratch);
521 EncodingEmitter emitter;
522 DialectWriter propertiesWriter(
config.bytecodeVersion, emitter,
523 numberingState, stringSection,
524 config.dialectVersionMap);
525 auto iface = cast<BytecodeOpInterface>(op);
526 iface.writeProperties(propertiesWriter);
528 llvm::raw_svector_ostream os(scratch);
530 return emit(scratch);
534 void write(EncodingEmitter &emitter) {
535 emitter.emitVarInt(propertiesStorage.size(),
"properties size");
536 if (propertiesStorage.empty())
538 for (
const auto &storage : propertiesStorage) {
539 if (storage.empty()) {
543 emitter.emitBytes(
ArrayRef(
reinterpret_cast<const uint8_t *
>(&storage[0]),
550 bool empty() {
return propertiesStorage.empty(); }
560 EncodingEmitter sizeEmitter;
561 sizeEmitter.emitVarInt(rawProperties.size(),
"properties");
562 llvm::raw_svector_ostream os(sizeScratch);
563 sizeEmitter.writeTo(os);
566 size_t index = propertiesStorage.size();
567 propertiesStorage.emplace_back();
568 std::vector<char> &newStorage = propertiesStorage.back();
569 size_t propertiesSize = sizeScratch.size() + rawProperties.size();
570 newStorage.reserve(propertiesSize);
571 llvm::append_range(newStorage, sizeScratch);
572 llvm::append_range(newStorage, rawProperties);
576 auto inserted = propertiesUniquing.insert(
578 if (!inserted.second)
579 propertiesStorage.pop_back();
580 return inserted.first->getSecond();
584 std::vector<std::vector<char>> propertiesStorage;
588 StringSectionBuilder &stringSection;
596 class RawEmitterOstream :
public raw_ostream {
598 explicit RawEmitterOstream(EncodingEmitter &emitter) : emitter(emitter) {
603 void write_impl(
const char *ptr,
size_t size)
override {
604 emitter.emitBytes({
reinterpret_cast<const uint8_t *
>(ptr), size},
607 uint64_t current_pos()
const override {
return emitter.size(); }
610 EncodingEmitter &emitter;
614 void EncodingEmitter::writeTo(raw_ostream &os)
const {
616 os.reserveExtraSpace(size());
618 for (
auto &prevResult : prevResultList)
619 os.write((
const char *)prevResult.data(), prevResult.size());
620 os.write((
const char *)currentResult.data(), currentResult.size());
623 void EncodingEmitter::emitMultiByteVarInt(uint64_t value, StringLiteral desc) {
627 uint64_t it = value >> 7;
628 for (
size_t numBytes = 2; numBytes < 9; ++numBytes) {
629 if (LLVM_LIKELY(it >>= 7) == 0) {
630 uint64_t encodedValue = (value << 1) | 0x1;
631 encodedValue <<= (numBytes - 1);
632 llvm::support::ulittle64_t encodedValueLE(encodedValue);
633 emitBytes({
reinterpret_cast<uint8_t *
>(&encodedValueLE), numBytes}, desc);
641 llvm::support::ulittle64_t valueLE(value);
642 emitBytes({
reinterpret_cast<uint8_t *
>(&valueLE),
sizeof(valueLE)}, desc);
650 class BytecodeWriter {
654 propertiesSection(numberingState, stringSection,
config.getImpl()) {}
657 LogicalResult write(
Operation *rootOp, raw_ostream &os);
663 void writeDialectSection(EncodingEmitter &emitter);
668 void writeAttrTypeSection(EncodingEmitter &emitter);
673 LogicalResult writeBlock(EncodingEmitter &emitter,
Block *block);
674 LogicalResult writeOp(EncodingEmitter &emitter,
Operation *op);
675 LogicalResult writeRegion(EncodingEmitter &emitter,
Region *region);
676 LogicalResult writeIRSection(EncodingEmitter &emitter,
Operation *op);
678 LogicalResult writeRegions(EncodingEmitter &emitter,
680 return success(llvm::all_of(regions, [&](
Region ®ion) {
681 return succeeded(writeRegion(emitter, ®ion));
688 void writeResourceSection(
Operation *op, EncodingEmitter &emitter);
693 void writeStringSection(EncodingEmitter &emitter);
698 void writePropertiesSection(EncodingEmitter &emitter);
703 void writeUseListOrders(EncodingEmitter &emitter, uint8_t &opEncodingMask,
710 StringSectionBuilder stringSection;
719 PropertiesSectionBuilder propertiesSection;
723 LogicalResult BytecodeWriter::write(
Operation *rootOp, raw_ostream &os) {
724 EncodingEmitter emitter;
728 emitter.emitString(
"ML\xefR",
"bytecode header");
734 <<
"unsupported version requested " <<
config.bytecodeVersion
735 <<
", must be in range ["
738 emitter.emitVarInt(
config.bytecodeVersion,
"bytecode version");
741 emitter.emitNulTerminatedString(
config.producer,
"bytecode producer");
744 writeDialectSection(emitter);
747 writeAttrTypeSection(emitter);
750 if (failed(writeIRSection(emitter, rootOp)))
754 writeResourceSection(rootOp, emitter);
757 writeStringSection(emitter);
761 writePropertiesSection(emitter);
762 else if (!propertiesSection.empty())
764 "unexpected properties emitted incompatible with bytecode <5");
780 template <
typename EntriesT,
typename EntryCallbackT>
782 EntryCallbackT &&callback) {
783 for (
auto it = entries.begin(), e = entries.end(); it != e;) {
784 auto groupStart = it++;
788 it = std::find_if(it, e, [&](
const auto &entry) {
789 return entry.dialect != currentDialect;
793 emitter.emitVarInt(currentDialect->
number,
"dialect number");
794 emitter.emitVarInt(std::distance(groupStart, it),
"dialect offset");
797 for (
auto &entry : llvm::make_range(groupStart, it))
802 void BytecodeWriter::writeDialectSection(EncodingEmitter &emitter) {
803 EncodingEmitter dialectEmitter;
806 auto dialects = numberingState.getDialects();
807 dialectEmitter.emitVarInt(llvm::size(dialects),
"dialects count");
810 size_t nameID = stringSection.insert(dialect.name);
813 dialectEmitter.emitVarInt(nameID,
"dialect name ID");
818 EncodingEmitter versionEmitter;
819 if (dialect.interface) {
821 DialectWriter versionWriter(
config.bytecodeVersion, versionEmitter,
822 numberingState, stringSection,
823 config.dialectVersionMap);
824 dialect.interface->writeVersion(versionWriter);
830 size_t versionAvailable = versionEmitter.size() > 0;
831 dialectEmitter.emitVarIntWithFlag(nameID, versionAvailable,
833 if (versionAvailable)
835 std::move(versionEmitter));
839 dialectEmitter.emitVarInt(size(numberingState.getOpNames()),
844 size_t stringId = stringSection.insert(name.name.stripDialect());
846 dialectEmitter.emitVarInt(stringId,
"dialect op name");
848 dialectEmitter.emitVarIntWithFlag(stringId, name.name.isRegistered(),
860 void BytecodeWriter::writeAttrTypeSection(EncodingEmitter &emitter) {
861 EncodingEmitter attrTypeEmitter;
862 EncodingEmitter offsetEmitter;
863 offsetEmitter.emitVarInt(llvm::size(numberingState.getAttributes()),
865 offsetEmitter.emitVarInt(llvm::size(numberingState.getTypes()),
869 uint64_t prevOffset = 0;
870 auto emitAttrOrType = [&](
auto &entry) {
871 auto entryValue = entry.getValue();
873 auto emitAttrOrTypeRawImpl = [&]() ->
void {
874 RawEmitterOstream(attrTypeEmitter) << entryValue;
875 attrTypeEmitter.emitByte(0,
"attr/type separator");
877 auto emitAttrOrTypeImpl = [&]() ->
bool {
880 if (entryValue.template hasTrait<TypeTrait::IsMutable>() ||
881 entryValue.template hasTrait<AttributeTrait::IsMutable>()) {
882 emitAttrOrTypeRawImpl();
886 DialectWriter dialectWriter(
config.bytecodeVersion, attrTypeEmitter,
887 numberingState, stringSection,
888 config.dialectVersionMap);
889 if constexpr (std::is_same_v<std::decay_t<decltype(entryValue)>,
Type>) {
890 for (
const auto &callback :
config.typeWriterCallbacks) {
891 if (succeeded(callback->write(entryValue, dialectWriter)))
895 entry.dialect->interface) {
896 if (succeeded(interface->writeType(entryValue, dialectWriter)))
900 for (
const auto &callback :
config.attributeWriterCallbacks) {
901 if (succeeded(callback->write(entryValue, dialectWriter)))
905 entry.dialect->interface) {
906 if (succeeded(interface->writeAttribute(entryValue, dialectWriter)))
913 emitAttrOrTypeRawImpl();
917 bool hasCustomEncoding = emitAttrOrTypeImpl();
920 uint64_t curOffset = attrTypeEmitter.size();
921 offsetEmitter.emitVarIntWithFlag(curOffset - prevOffset, hasCustomEncoding,
923 prevOffset = curOffset;
934 std::move(offsetEmitter));
942 LogicalResult BytecodeWriter::writeBlock(EncodingEmitter &emitter,
945 bool hasArgs = !args.empty();
950 unsigned numOps = numberingState.getOperationCount(block);
951 emitter.emitVarIntWithFlag(numOps, hasArgs,
"block num ops");
955 emitter.emitVarInt(args.size(),
"block args count");
959 emitter.emitVarIntWithFlag(numberingState.getNumber(arg.getType()),
960 !isa<UnknownLoc>(argLoc),
"block arg type");
961 if (!isa<UnknownLoc>(argLoc))
962 emitter.emitVarInt(numberingState.getNumber(argLoc),
963 "block arg location");
965 emitter.emitVarInt(numberingState.getNumber(arg.getType()),
967 emitter.emitVarInt(numberingState.getNumber(argLoc),
968 "block arg location");
972 uint64_t maskOffset = emitter.size();
973 uint8_t encodingMask = 0;
974 emitter.emitByte(0,
"use-list separator");
975 writeUseListOrders(emitter, encodingMask, args);
977 emitter.patchByte(maskOffset, encodingMask,
"block patch encoding");
983 if (failed(writeOp(emitter, &op)))
988 LogicalResult BytecodeWriter::writeOp(EncodingEmitter &emitter,
Operation *op) {
989 emitter.emitVarInt(numberingState.getNumber(op->
getName()),
"op name ID");
994 uint64_t maskOffset = emitter.size();
995 uint8_t opEncodingMask = 0;
996 emitter.emitByte(0,
"op separator");
999 emitter.emitVarInt(numberingState.getNumber(op->
getLoc()),
"op location");
1011 if (!attrs.empty()) {
1013 emitter.emitVarInt(numberingState.getNumber(attrs),
"op attrs count");
1019 std::optional<ssize_t> propertiesId = propertiesSection.emit(op);
1020 if (propertiesId.has_value()) {
1022 emitter.emitVarInt(*propertiesId,
"op properties ID");
1029 emitter.emitVarInt(numResults,
"op results count");
1031 emitter.emitVarInt(numberingState.getNumber(type),
"op result type");
1037 emitter.emitVarInt(numOperands,
"op operands count");
1039 emitter.emitVarInt(numberingState.getNumber(operand),
"op operand types");
1045 emitter.emitVarInt(numSuccessors,
"op successors count");
1047 emitter.emitVarInt(numberingState.getNumber(successor),
"op successor");
1061 emitter.patchByte(maskOffset, opEncodingMask,
"op encoding mask");
1068 bool isIsolatedFromAbove = numberingState.isIsolatedFromAbove(op);
1069 emitter.emitVarIntWithFlag(numRegions, isIsolatedFromAbove,
1070 "op regions count");
1074 if (isIsolatedFromAbove &&
1076 EncodingEmitter regionEmitter;
1077 if (failed(writeRegions(regionEmitter, op->
getRegions())))
1081 }
else if (failed(writeRegions(emitter, op->
getRegions()))) {
1088 void BytecodeWriter::writeUseListOrders(EncodingEmitter &emitter,
1089 uint8_t &opEncodingMask,
1094 auto value = item.value();
1097 if (value.use_empty() || value.hasOneUse())
1103 bool alreadyOrdered =
true;
1104 auto &firstUse = *value.use_begin();
1106 firstUse, numberingState.getNumber(firstUse.getOwner()));
1112 use.value(), numberingState.getNumber(use.value().getOwner()));
1116 alreadyOrdered &= (prevID > currentID);
1117 useListPairs.push_back({use.index(), currentID});
1127 useListPairs.begin(), useListPairs.end(),
1128 [](
auto elem1,
auto elem2) { return elem1.second > elem2.second; });
1130 map.try_emplace(item.index(), llvm::map_range(useListPairs, [](
auto elem) {
1141 if (range.size() != 1) {
1142 emitter.emitVarInt(map.size(),
"custom use-list size");
1145 for (
const auto &item : map) {
1146 auto resultIdx = item.getFirst();
1147 auto useListOrder = item.getSecond();
1152 size_t shuffledElements =
1154 [](
auto item) {
return item.index() != item.value(); });
1155 bool indexPairEncoding = shuffledElements < (useListOrder.size() / 2);
1158 if (range.size() != 1)
1159 emitter.emitVarInt(resultIdx,
"use-list result index");
1161 if (indexPairEncoding) {
1162 emitter.emitVarIntWithFlag(shuffledElements * 2, indexPairEncoding,
1163 "use-list index pair size");
1165 if (pair.index() != pair.value()) {
1166 emitter.emitVarInt(pair.value(),
"use-list index pair first");
1167 emitter.emitVarInt(pair.index(),
"use-list index pair second");
1171 emitter.emitVarIntWithFlag(useListOrder.size(), indexPairEncoding,
1173 for (
const auto &index : useListOrder)
1174 emitter.emitVarInt(index,
"use-list order");
1179 LogicalResult BytecodeWriter::writeRegion(EncodingEmitter &emitter,
1183 if (region->
empty()) {
1184 emitter.emitVarInt( 0,
"region block count empty");
1189 unsigned numBlocks, numValues;
1190 std::tie(numBlocks, numValues) = numberingState.getBlockValueCount(region);
1191 emitter.emitVarInt(numBlocks,
"region block count");
1192 emitter.emitVarInt(numValues,
"region value count");
1195 for (
Block &block : *region)
1196 if (failed(writeBlock(emitter, &block)))
1201 LogicalResult BytecodeWriter::writeIRSection(EncodingEmitter &emitter,
1203 EncodingEmitter irEmitter;
1208 irEmitter.emitVarIntWithFlag( 1,
false,
"ir section");
1211 if (failed(writeOp(irEmitter, op)))
1229 ResourceBuilder(EncodingEmitter &emitter, StringSectionBuilder &stringSection,
1230 PostProcessFn postProcessFn,
bool shouldElideData)
1231 : emitter(emitter), stringSection(stringSection),
1232 postProcessFn(postProcessFn), shouldElideData(shouldElideData) {}
1233 ~ResourceBuilder()
override =
default;
1236 uint32_t dataAlignment)
final {
1237 if (!shouldElideData)
1238 emitter.emitOwnedBlobAndAlignment(data, dataAlignment,
"resource blob");
1241 void buildBool(StringRef key,
bool data)
final {
1242 if (!shouldElideData)
1243 emitter.emitByte(data,
"resource bool");
1246 void buildString(StringRef key, StringRef data)
final {
1247 if (!shouldElideData)
1248 emitter.emitVarInt(stringSection.insert(data),
"resource string");
1253 EncodingEmitter &emitter;
1254 StringSectionBuilder &stringSection;
1255 PostProcessFn postProcessFn;
1256 bool shouldElideData =
false;
1260 void BytecodeWriter::writeResourceSection(
Operation *op,
1261 EncodingEmitter &emitter) {
1262 EncodingEmitter resourceEmitter;
1263 EncodingEmitter resourceOffsetEmitter;
1264 uint64_t prevOffset = 0;
1271 uint64_t curOffset = resourceEmitter.size();
1272 curResourceEntries.emplace_back(key,
kind, curOffset - prevOffset);
1273 prevOffset = curOffset;
1277 auto emitResourceGroup = [&](uint64_t key) {
1278 resourceOffsetEmitter.emitVarInt(key,
"resource group key");
1279 resourceOffsetEmitter.emitVarInt(curResourceEntries.size(),
1280 "resource group size");
1281 for (
auto [key,
kind, size] : curResourceEntries) {
1282 resourceOffsetEmitter.emitVarInt(stringSection.insert(key),
1284 resourceOffsetEmitter.emitVarInt(size,
"resource size");
1285 resourceOffsetEmitter.emitByte(
kind,
"resource kind");
1290 ResourceBuilder entryBuilder(resourceEmitter, stringSection,
1291 appendResourceOffset,
1292 config.shouldElideResourceData);
1295 resourceOffsetEmitter.emitVarInt(
config.externalResourcePrinters.size(),
1296 "external resource printer count");
1297 for (
const auto &printer :
config.externalResourcePrinters) {
1298 curResourceEntries.clear();
1299 printer->buildResources(op, entryBuilder);
1300 emitResourceGroup(stringSection.insert(printer->getName()));
1305 if (!dialect.asmInterface)
1307 curResourceEntries.clear();
1308 dialect.asmInterface->buildResources(op, dialect.resources, entryBuilder);
1313 for (
const auto &resource : dialect.resourceMap)
1314 if (resource.second->isDeclaration)
1318 if (!curResourceEntries.empty())
1319 emitResourceGroup(dialect.number);
1323 if (resourceOffsetEmitter.size() == 0)
1327 std::move(resourceOffsetEmitter));
1335 void BytecodeWriter::writeStringSection(EncodingEmitter &emitter) {
1336 EncodingEmitter stringEmitter;
1337 stringSection.write(stringEmitter);
1345 void BytecodeWriter::writePropertiesSection(EncodingEmitter &emitter) {
1346 EncodingEmitter propertiesEmitter;
1347 propertiesSection.write(propertiesEmitter);
1349 std::move(propertiesEmitter));
1358 BytecodeWriter writer(op,
config);
1359 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::@1193::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.