Revert "[MLIR] Add native Bytecode support for properties"

This reverts commit ca5a12fd69
and follow-up fixes:

df34c288c4
07dc906883
ab80ad0095
837d1ce0dc

The first commit was incomplete and broken, I'll prepare a new version
later, in the meantime pull this work out of tree.
This commit is contained in:
Mehdi Amini
2023-05-25 20:58:53 -07:00
parent facf22b8b0
commit bb9a0c736b
62 changed files with 47 additions and 775 deletions

View File

@@ -1,17 +0,0 @@
//===- BytecodeOpInterface.cpp - Bytecode Op Interfaces -------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "mlir/Bytecode/BytecodeOpInterface.h"
using namespace mlir;
//===----------------------------------------------------------------------===//
// BytecodeOpInterface
//===----------------------------------------------------------------------===//
#include "mlir/Bytecode/BytecodeOpInterface.cpp.inc"

View File

@@ -1,13 +1,2 @@
add_subdirectory(Reader)
add_subdirectory(Writer)
add_mlir_library(MLIRBytecodeOpInterface
BytecodeOpInterface.cpp
ADDITIONAL_HEADER_DIRS
${MLIR_MAIN_INCLUDE_DIR}/mlir/Bytecode
LINK_LIBS PUBLIC
MLIRIR
MLIRSupport
)

View File

@@ -11,7 +11,6 @@
#include "mlir/Bytecode/BytecodeReader.h"
#include "mlir/AsmParser/AsmParser.h"
#include "mlir/Bytecode/BytecodeImplementation.h"
#include "mlir/Bytecode/BytecodeOpInterface.h"
#include "mlir/Bytecode/Encoding.h"
#include "mlir/IR/BuiltinDialect.h"
#include "mlir/IR/BuiltinOps.h"
@@ -21,7 +20,6 @@
#include "mlir/IR/Visitors.h"
#include "mlir/Support/LLVM.h"
#include "mlir/Support/LogicalResult.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/ScopeExit.h"
#include "llvm/ADT/SmallString.h"
@@ -30,7 +28,6 @@
#include "llvm/Support/MemoryBufferRef.h"
#include "llvm/Support/SaveAndRestore.h"
#include "llvm/Support/SourceMgr.h"
#include <cstddef>
#include <list>
#include <memory>
#include <numeric>
@@ -59,15 +56,13 @@ static std::string toString(bytecode::Section::ID sectionID) {
return "ResourceOffset (6)";
case bytecode::Section::kDialectVersions:
return "DialectVersions (7)";
case bytecode::Section::kProperties:
return "Properties (8)";
default:
return ("Unknown (" + Twine(static_cast<unsigned>(sectionID)) + ")").str();
}
}
/// Returns true if the given top-level section ID is optional.
static bool isSectionOptional(bytecode::Section::ID sectionID, int version) {
static bool isSectionOptional(bytecode::Section::ID sectionID) {
switch (sectionID) {
case bytecode::Section::kString:
case bytecode::Section::kDialect:
@@ -79,8 +74,6 @@ static bool isSectionOptional(bytecode::Section::ID sectionID, int version) {
case bytecode::Section::kResourceOffset:
case bytecode::Section::kDialectVersions:
return true;
case bytecode::Section::kProperties:
return version < 4;
default:
llvm_unreachable("unknown section ID");
}
@@ -369,17 +362,6 @@ public:
return parseEntry(reader, strings, result, "string");
}
/// Parse a shared string from the string section. The shared string is
/// encoded using an index to a corresponding string in the string section.
/// This variant parses a flag compressed with the index.
LogicalResult parseStringWithFlag(EncodingReader &reader, StringRef &result,
bool &flag) {
uint64_t entryIdx;
if (failed(reader.parseVarIntWithFlag(entryIdx, flag)))
return failure();
return parseStringAtIndex(reader, entryIdx, result);
}
/// Parse a shared string from the string section. The shared string is
/// encoded using an index to a corresponding string in the string section.
LogicalResult parseStringAtIndex(EncodingReader &reader, uint64_t index,
@@ -477,9 +459,8 @@ struct BytecodeDialect {
/// This struct represents an operation name entry within the bytecode.
struct BytecodeOperationName {
BytecodeOperationName(BytecodeDialect *dialect, StringRef name,
bool wasRegistered)
: dialect(dialect), name(name), wasRegistered(wasRegistered) {}
BytecodeOperationName(BytecodeDialect *dialect, StringRef name)
: dialect(dialect), name(name) {}
/// The loaded operation name, or std::nullopt if it hasn't been processed
/// yet.
@@ -490,10 +471,6 @@ struct BytecodeOperationName {
/// The name of the operation, without the dialect prefix.
StringRef name;
/// Whether this operation was registered when the bytecode was produced.
/// This flag is populated when bytecode version >=4.
bool wasRegistered;
};
} // namespace
@@ -814,18 +791,6 @@ public:
result = resolveAttribute(attrIdx);
return success(!!result);
}
LogicalResult parseOptionalAttribute(EncodingReader &reader,
Attribute &result) {
uint64_t attrIdx;
bool flag;
if (failed(reader.parseVarIntWithFlag(attrIdx, flag)))
return failure();
if (!flag)
return success();
result = resolveAttribute(attrIdx);
return success(!!result);
}
LogicalResult parseType(EncodingReader &reader, Type &result) {
uint64_t typeIdx;
if (failed(reader.parseVarInt(typeIdx)))
@@ -905,9 +870,7 @@ public:
LogicalResult readAttribute(Attribute &result) override {
return attrTypeReader.parseAttribute(reader, result);
}
LogicalResult readOptionalAttribute(Attribute &result) override {
return attrTypeReader.parseOptionalAttribute(reader, result);
}
LogicalResult readType(Type &result) override {
return attrTypeReader.parseType(reader, result);
}
@@ -994,87 +957,6 @@ private:
ResourceSectionReader &resourceReader;
EncodingReader &reader;
};
/// Wraps the properties section and handles reading properties out of it.
class PropertiesSectionReader {
public:
/// Initialize the properties section reader with the given section data.
LogicalResult initialize(Location fileLoc, ArrayRef<uint8_t> sectionData) {
if (sectionData.empty())
return success();
EncodingReader propReader(sectionData, fileLoc);
uint64_t count;
if (failed(propReader.parseVarInt(count)))
return failure();
// Parse the raw properties buffer.
if (failed(propReader.parseBytes(propReader.size(), propertiesBuffers)))
return failure();
EncodingReader offsetsReader(propertiesBuffers, fileLoc);
offsetTable.reserve(count);
for (auto idx : llvm::seq<int64_t>(0, count)) {
(void)idx;
offsetTable.push_back(propertiesBuffers.size() - offsetsReader.size());
ArrayRef<uint8_t> rawProperties;
uint64_t dataSize;
if (failed(offsetsReader.parseVarInt(dataSize)) ||
failed(offsetsReader.parseBytes(dataSize, rawProperties)))
return failure();
}
if (!offsetsReader.empty())
return offsetsReader.emitError()
<< "Broken properties section: didn't exhaust the offsets table";
return success();
}
LogicalResult read(Location fileLoc, DialectReader &dialectReader,
OperationName *opName, OperationState &opState) {
uint64_t propertiesIdx;
if (failed(dialectReader.readVarInt(propertiesIdx)))
return failure();
if (propertiesIdx >= offsetTable.size())
return dialectReader.emitError("Properties idx out-of-bound for ")
<< opName->getStringRef();
size_t propertiesOffset = offsetTable[propertiesIdx];
if (propertiesIdx >= propertiesBuffers.size())
return dialectReader.emitError("Properties offset out-of-bound for ")
<< opName->getStringRef();
// Acquire the sub-buffer that represent the requested properties.
ArrayRef<char> rawProperties;
{
// "Seek" to the requested offset by getting a new reader with the right
// sub-buffer.
EncodingReader reader(propertiesBuffers.drop_front(propertiesOffset),
fileLoc);
// Properties are stored as a sequence of {size + raw_data}.
if (failed(
dialectReader.withEncodingReader(reader).readBlob(rawProperties)))
return failure();
}
// Setup a new reader to read from the `rawProperties` sub-buffer.
EncodingReader reader(
StringRef(rawProperties.begin(), rawProperties.size()), fileLoc);
DialectReader propReader = dialectReader.withEncodingReader(reader);
auto *iface = opName->getInterface<BytecodeOpInterface>();
if (iface)
return iface->readProperties(propReader, opState);
if (opName->isRegistered())
return propReader.emitError(
"has properties but missing BytecodeOpInterface for ")
<< opName->getStringRef();
// Unregistered op are storing properties as an attribute.
return propReader.readAttribute(opState.propertiesAttr);
}
private:
/// The properties buffer referenced within the bytecode file.
ArrayRef<uint8_t> propertiesBuffers;
/// Table of offset in the buffer above.
SmallVector<int64_t> offsetTable;
};
} // namespace
LogicalResult
@@ -1312,9 +1194,7 @@ private:
lazyLoadableOps.erase(it->getSecond());
lazyLoadableOpsMap.erase(it);
auto result = parseRegions(regionStack, regionStack.back());
assert((regionStack.empty() || failed(result)) &&
"broken invariant: regionStack should be empty when parseRegions "
"succeeds");
assert(regionStack.empty());
return result;
}
@@ -1330,8 +1210,7 @@ private:
LogicalResult parseDialectSection(ArrayRef<uint8_t> sectionData);
/// Parse an operation name reference using the given reader.
FailureOr<OperationName> parseOpName(EncodingReader &reader,
bool &wasRegistered);
FailureOr<OperationName> parseOpName(EncodingReader &reader);
//===--------------------------------------------------------------------===//
// Attribute/Type Section
@@ -1519,9 +1398,6 @@ private:
/// The table of strings referenced within the bytecode file.
StringSectionReader stringReader;
/// The table of properties referenced by the operation in the bytecode file.
PropertiesSectionReader propertiesReader;
/// The current set of available IR value scopes.
std::vector<ValueScope> valueScopes;
@@ -1590,7 +1466,7 @@ LogicalResult BytecodeReader::Impl::read(
// Check that all of the required sections were found.
for (int i = 0; i < bytecode::Section::kNumSections; ++i) {
bytecode::Section::ID sectionID = static_cast<bytecode::Section::ID>(i);
if (!sectionDatas[i] && !isSectionOptional(sectionID, version)) {
if (!sectionDatas[i] && !isSectionOptional(sectionID)) {
return reader.emitError("missing data for top-level section: ",
::toString(sectionID));
}
@@ -1601,12 +1477,6 @@ LogicalResult BytecodeReader::Impl::read(
fileLoc, *sectionDatas[bytecode::Section::kString])))
return failure();
// Process the properties section.
if (sectionDatas[bytecode::Section::kProperties] &&
failed(propertiesReader.initialize(
fileLoc, *sectionDatas[bytecode::Section::kProperties])))
return failure();
// Process the dialect section.
if (failed(parseDialectSection(*sectionDatas[bytecode::Section::kDialect])))
return failure();
@@ -1728,18 +1598,9 @@ BytecodeReader::Impl::parseDialectSection(ArrayRef<uint8_t> sectionData) {
// Parse the operation names, which are grouped by dialect.
auto parseOpName = [&](BytecodeDialect *dialect) {
StringRef opName;
bool wasRegistered;
// Prior to version 4, the information about wheter an op was registered or
// not wasn't encoded.
if (version < 4) {
if (failed(stringReader.parseString(sectionReader, opName)))
return failure();
} else {
if (failed(stringReader.parseStringWithFlag(sectionReader, opName,
wasRegistered)))
return failure();
}
opNames.emplace_back(dialect, opName, wasRegistered);
if (failed(stringReader.parseString(sectionReader, opName)))
return failure();
opNames.emplace_back(dialect, opName);
return success();
};
// Avoid re-allocation in bytecode version > 3 where the number of ops are
@@ -1757,11 +1618,11 @@ BytecodeReader::Impl::parseDialectSection(ArrayRef<uint8_t> sectionData) {
}
FailureOr<OperationName>
BytecodeReader::Impl::parseOpName(EncodingReader &reader, bool &wasRegistered) {
BytecodeReader::Impl::parseOpName(EncodingReader &reader) {
BytecodeOperationName *opName = nullptr;
if (failed(parseEntry(reader, opNames, opName, "operation name")))
return failure();
wasRegistered = opName->wasRegistered;
// Check to see if this operation name has already been resolved. If we
// haven't, load the dialect and build the operation name.
if (!opName->opName) {
@@ -2133,8 +1994,7 @@ BytecodeReader::Impl::parseOpWithoutRegions(EncodingReader &reader,
RegionReadState &readState,
bool &isIsolatedFromAbove) {
// Parse the name of the operation.
bool wasRegistered;
FailureOr<OperationName> opName = parseOpName(reader, wasRegistered);
FailureOr<OperationName> opName = parseOpName(reader);
if (failed(opName))
return failure();
@@ -2161,21 +2021,6 @@ BytecodeReader::Impl::parseOpWithoutRegions(EncodingReader &reader,
opState.attributes = dictAttr;
}
if (opMask & bytecode::OpEncodingMask::kHasProperties) {
if (wasRegistered) {
DialectReader dialectReader(attrTypeReader, stringReader, resourceReader,
reader);
if (failed(
propertiesReader.read(fileLoc, dialectReader, &*opName, opState)))
return failure();
} else {
// If the operation wasn't registered when it was emitted, the properties
// was serialized as an attribute.
if (failed(parseAttribute(reader, opState.propertiesAttr)))
return failure();
}
}
/// Parse the results of the operation.
if (opMask & bytecode::OpEncodingMask::kHasResults) {
uint64_t numResults;

View File

@@ -9,23 +9,11 @@
#include "mlir/Bytecode/BytecodeWriter.h"
#include "IRNumbering.h"
#include "mlir/Bytecode/BytecodeImplementation.h"
#include "mlir/Bytecode/BytecodeOpInterface.h"
#include "mlir/Bytecode/Encoding.h"
#include "mlir/IR/Attributes.h"
#include "mlir/IR/Diagnostics.h"
#include "mlir/IR/OpImplementation.h"
#include "mlir/Support/LogicalResult.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/CachedHashString.h"
#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/raw_ostream.h"
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <optional>
#include <sys/types.h>
#define DEBUG_TYPE "mlir-bytecode-writer"
@@ -70,10 +58,6 @@ void BytecodeWriterConfig::setDesiredBytecodeVersion(int64_t bytecodeVersion) {
std::min<int64_t>(bytecodeVersion, bytecode::kVersion);
}
int64_t BytecodeWriterConfig::getDesiredBytecodeVersion() const {
return impl->bytecodeVersion;
}
//===----------------------------------------------------------------------===//
// EncodingEmitter
//===----------------------------------------------------------------------===//
@@ -334,14 +318,6 @@ public:
void writeAttribute(Attribute attr) override {
emitter.emitVarInt(numberingState.getNumber(attr));
}
void writeOptionalAttribute(Attribute attr) override {
if (!attr) {
emitter.emitVarInt(0);
return;
}
emitter.emitVarIntWithFlag(numberingState.getNumber(attr), true);
}
void writeType(Type type) override {
emitter.emitVarInt(numberingState.getNumber(type));
}
@@ -406,105 +382,6 @@ private:
StringSectionBuilder &stringSection;
};
namespace {
class PropertiesSectionBuilder {
public:
PropertiesSectionBuilder(IRNumberingState &numberingState,
StringSectionBuilder &stringSection,
const BytecodeWriterConfig::Impl &config)
: numberingState(numberingState), stringSection(stringSection),
config(config) {}
/// Emit the op properties in the properties section and return the index of
/// the properties within the section. Return -1 if no properties was emitted.
std::optional<ssize_t> emit(Operation *op) {
EncodingEmitter propertiesEmitter;
if (!op->getPropertiesStorageSize())
return std::nullopt;
if (!op->isRegistered()) {
// Unregistered op are storing properties as an optional attribute.
Attribute prop = *op->getPropertiesStorage().as<Attribute *>();
if (!prop)
return std::nullopt;
EncodingEmitter sizeEmitter;
sizeEmitter.emitVarInt(numberingState.getNumber(prop));
scratch.clear();
llvm::raw_svector_ostream os(scratch);
sizeEmitter.writeTo(os);
return emit(scratch);
}
EncodingEmitter emitter;
DialectWriter propertiesWriter(config.bytecodeVersion, emitter,
numberingState, stringSection);
auto iface = cast<BytecodeOpInterface>(op);
iface.writeProperties(propertiesWriter);
scratch.clear();
llvm::raw_svector_ostream os(scratch);
emitter.writeTo(os);
return emit(scratch);
}
/// Write the current set of properties to the given emitter.
void write(EncodingEmitter &emitter) {
emitter.emitVarInt(propertiesStorage.size());
if (propertiesStorage.empty())
return;
for (const auto &storage : propertiesStorage) {
if (storage.empty()) {
emitter.emitBytes(ArrayRef<uint8_t>());
continue;
}
emitter.emitBytes(ArrayRef(reinterpret_cast<const uint8_t *>(&storage[0]),
storage.size()));
}
}
/// Returns true if the section is empty.
bool empty() { return propertiesStorage.empty(); }
private:
/// Emit raw data and returns the offset in the internal buffer.
/// Data are deduplicated and will be copied in the internal buffer only if
/// they don't exist there already.
ssize_t emit(ArrayRef<char> rawProperties) {
// Populate a scratch buffer with the properties size.
SmallVector<char> sizeScratch;
{
EncodingEmitter sizeEmitter;
sizeEmitter.emitVarInt(rawProperties.size());
llvm::raw_svector_ostream os(sizeScratch);
sizeEmitter.writeTo(os);
}
// Append a new storage to the table now.
size_t index = propertiesStorage.size();
propertiesStorage.emplace_back();
std::vector<char> &newStorage = propertiesStorage.back();
size_t propertiesSize = sizeScratch.size() + rawProperties.size();
newStorage.reserve(propertiesSize);
newStorage.insert(newStorage.end(), sizeScratch.begin(), sizeScratch.end());
newStorage.insert(newStorage.end(), rawProperties.begin(),
rawProperties.end());
// Try to de-duplicate the new serialized properties.
// If the properties is a duplicate, pop it back from the storage.
auto inserted = propertiesUniquing.insert(
std::make_pair(ArrayRef<char>(newStorage), index));
if (!inserted.second)
propertiesStorage.pop_back();
return inserted.first->getSecond();
}
/// Storage for properties.
std::vector<std::vector<char>> propertiesStorage;
SmallVector<char> scratch;
DenseMap<ArrayRef<char>, int64_t> propertiesUniquing;
IRNumberingState &numberingState;
StringSectionBuilder &stringSection;
const BytecodeWriterConfig::Impl &config;
};
} // namespace
/// A simple raw_ostream wrapper around a EncodingEmitter. This removes the need
/// to go through an intermediate buffer when interacting with code that wants a
/// raw_ostream.
@@ -558,12 +435,11 @@ void EncodingEmitter::emitMultiByteVarInt(uint64_t value) {
namespace {
class BytecodeWriter {
public:
BytecodeWriter(Operation *op, const BytecodeWriterConfig &config)
: numberingState(op, config), config(config.getImpl()),
propertiesSection(numberingState, stringSection, config.getImpl()) {}
BytecodeWriter(Operation *op, const BytecodeWriterConfig::Impl &config)
: numberingState(op), config(config) {}
/// Write the bytecode for the given root operation.
LogicalResult write(Operation *rootOp, raw_ostream &os);
void write(Operation *rootOp, raw_ostream &os);
private:
//===--------------------------------------------------------------------===//
@@ -579,10 +455,10 @@ private:
//===--------------------------------------------------------------------===//
// Operations
LogicalResult writeBlock(EncodingEmitter &emitter, Block *block);
LogicalResult writeOp(EncodingEmitter &emitter, Operation *op);
LogicalResult writeRegion(EncodingEmitter &emitter, Region *region);
LogicalResult writeIRSection(EncodingEmitter &emitter, Operation *op);
void writeBlock(EncodingEmitter &emitter, Block *block);
void writeOp(EncodingEmitter &emitter, Operation *op);
void writeRegion(EncodingEmitter &emitter, Region *region);
void writeIRSection(EncodingEmitter &emitter, Operation *op);
//===--------------------------------------------------------------------===//
// Resources
@@ -594,11 +470,6 @@ private:
void writeStringSection(EncodingEmitter &emitter);
//===--------------------------------------------------------------------===//
// Properties
void writePropertiesSection(EncodingEmitter &emitter);
//===--------------------------------------------------------------------===//
// Helpers
@@ -616,13 +487,10 @@ private:
/// Configuration dictating bytecode emission.
const BytecodeWriterConfig::Impl &config;
/// Storage for the properties section
PropertiesSectionBuilder propertiesSection;
};
} // namespace
LogicalResult BytecodeWriter::write(Operation *rootOp, raw_ostream &os) {
void BytecodeWriter::write(Operation *rootOp, raw_ostream &os) {
EncodingEmitter emitter;
// Emit the bytecode file header. This is how we identify the output as a
@@ -642,8 +510,7 @@ LogicalResult BytecodeWriter::write(Operation *rootOp, raw_ostream &os) {
writeAttrTypeSection(emitter);
// Emit the IR section.
if (failed(writeIRSection(emitter, rootOp)))
return failure();
writeIRSection(emitter, rootOp);
// Emit the resources section.
writeResourceSection(rootOp, emitter);
@@ -651,17 +518,8 @@ LogicalResult BytecodeWriter::write(Operation *rootOp, raw_ostream &os) {
// Emit the string section.
writeStringSection(emitter);
// Emit the properties section.
if (config.bytecodeVersion >= 5)
writePropertiesSection(emitter);
else if (!propertiesSection.empty())
return rootOp->emitError(
"unexpected properties emitted incompatible with bytecode <5");
// Write the generated bytecode to the provided output stream.
emitter.writeTo(os);
return success();
}
//===----------------------------------------------------------------------===//
@@ -732,11 +590,7 @@ void BytecodeWriter::writeDialectSection(EncodingEmitter &emitter) {
// Emit the referenced operation names grouped by dialect.
auto emitOpName = [&](OpNameNumbering &name) {
size_t stringId = stringSection.insert(name.name.stripDialect());
if (config.bytecodeVersion < 4)
dialectEmitter.emitVarInt(stringId);
else
dialectEmitter.emitVarIntWithFlag(stringId, name.name.isRegistered());
dialectEmitter.emitVarInt(stringSection.insert(name.name.stripDialect()));
};
writeDialectGrouping(dialectEmitter, numberingState.getOpNames(), emitOpName);
@@ -805,8 +659,7 @@ void BytecodeWriter::writeAttrTypeSection(EncodingEmitter &emitter) {
//===----------------------------------------------------------------------===//
// Operations
LogicalResult BytecodeWriter::writeBlock(EncodingEmitter &emitter,
Block *block) {
void BytecodeWriter::writeBlock(EncodingEmitter &emitter, Block *block) {
ArrayRef<BlockArgument> args = block->getArguments();
bool hasArgs = !args.empty();
@@ -843,12 +696,10 @@ LogicalResult BytecodeWriter::writeBlock(EncodingEmitter &emitter,
// Emit the operations within the block.
for (Operation &op : *block)
if (failed(writeOp(emitter, &op)))
return failure();
return success();
writeOp(emitter, &op);
}
LogicalResult BytecodeWriter::writeOp(EncodingEmitter &emitter, Operation *op) {
void BytecodeWriter::writeOp(EncodingEmitter &emitter, Operation *op) {
emitter.emitVarInt(numberingState.getNumber(op->getName()));
// Emit a mask for the operation components. We need to fill this in later
@@ -862,24 +713,10 @@ LogicalResult BytecodeWriter::writeOp(EncodingEmitter &emitter, Operation *op) {
emitter.emitVarInt(numberingState.getNumber(op->getLoc()));
// Emit the attributes of this operation.
DictionaryAttr attrs = op->getDiscardableAttrDictionary();
// Allow deployment to version <4 by merging inherent attribute with the
// discardable ones. We should fail if there are any conflicts.
if (config.bytecodeVersion < 4)
attrs = op->getAttrDictionary();
DictionaryAttr attrs = op->getAttrDictionary();
if (!attrs.empty()) {
opEncodingMask |= bytecode::OpEncodingMask::kHasAttrs;
emitter.emitVarInt(numberingState.getNumber(attrs));
}
// Emit the properties of this operation, for now we still support deployment
// to version <4.
if (config.bytecodeVersion >= 4) {
std::optional<ssize_t> propertiesId = propertiesSection.emit(op);
if (propertiesId.has_value()) {
opEncodingMask |= bytecode::OpEncodingMask::kHasProperties;
emitter.emitVarInt(*propertiesId);
}
emitter.emitVarInt(numberingState.getNumber(op->getAttrDictionary()));
}
// Emit the result types of the operation.
@@ -931,18 +768,15 @@ LogicalResult BytecodeWriter::writeOp(EncodingEmitter &emitter, Operation *op) {
// If the region is not isolated from above, or we are emitting bytecode
// targeting version <2, we don't use a section.
if (!isIsolatedFromAbove || config.bytecodeVersion < 2) {
if (failed(writeRegion(emitter, &region)))
return failure();
writeRegion(emitter, &region);
continue;
}
EncodingEmitter regionEmitter;
if (failed(writeRegion(regionEmitter, &region)))
return failure();
writeRegion(regionEmitter, &region);
emitter.emitSection(bytecode::Section::kIR, std::move(regionEmitter));
}
}
return success();
}
void BytecodeWriter::writeUseListOrders(EncodingEmitter &emitter,
@@ -1033,14 +867,11 @@ void BytecodeWriter::writeUseListOrders(EncodingEmitter &emitter,
}
}
LogicalResult BytecodeWriter::writeRegion(EncodingEmitter &emitter,
Region *region) {
void BytecodeWriter::writeRegion(EncodingEmitter &emitter, Region *region) {
// If the region is empty, we only need to emit the number of blocks (which is
// zero).
if (region->empty()) {
emitter.emitVarInt(/*numBlocks*/ 0);
return success();
}
if (region->empty())
return emitter.emitVarInt(/*numBlocks*/ 0);
// Emit the number of blocks and values within the region.
unsigned numBlocks, numValues;
@@ -1050,13 +881,10 @@ LogicalResult BytecodeWriter::writeRegion(EncodingEmitter &emitter,
// Emit the blocks within the region.
for (Block &block : *region)
if (failed(writeBlock(emitter, &block)))
return failure();
return success();
writeBlock(emitter, &block);
}
LogicalResult BytecodeWriter::writeIRSection(EncodingEmitter &emitter,
Operation *op) {
void BytecodeWriter::writeIRSection(EncodingEmitter &emitter, Operation *op) {
EncodingEmitter irEmitter;
// Write the IR section the same way as a block with no arguments. Note that
@@ -1065,11 +893,9 @@ LogicalResult BytecodeWriter::writeIRSection(EncodingEmitter &emitter,
irEmitter.emitVarIntWithFlag(/*numOps*/ 1, /*hasArgs*/ false);
// Emit the operations.
if (failed(writeOp(irEmitter, op)))
return failure();
writeOp(irEmitter, op);
emitter.emitSection(bytecode::Section::kIR, std::move(irEmitter));
return success();
}
//===----------------------------------------------------------------------===//
@@ -1185,22 +1011,14 @@ void BytecodeWriter::writeStringSection(EncodingEmitter &emitter) {
emitter.emitSection(bytecode::Section::kString, std::move(stringEmitter));
}
//===----------------------------------------------------------------------===//
// Properties
void BytecodeWriter::writePropertiesSection(EncodingEmitter &emitter) {
EncodingEmitter propertiesEmitter;
propertiesSection.write(propertiesEmitter);
emitter.emitSection(bytecode::Section::kProperties,
std::move(propertiesEmitter));
}
//===----------------------------------------------------------------------===//
// Entry Points
//===----------------------------------------------------------------------===//
LogicalResult mlir::writeBytecodeToFile(Operation *op, raw_ostream &os,
const BytecodeWriterConfig &config) {
BytecodeWriter writer(op, config);
return writer.write(op, os);
BytecodeWriter writer(op, config.getImpl());
writer.write(op, os);
// Currently there is no failure case.
return success();
}

View File

@@ -8,5 +8,4 @@ add_mlir_library(MLIRBytecodeWriter
LINK_LIBS PUBLIC
MLIRIR
MLIRSupport
MLIRBytecodeOpInterface
)

View File

@@ -8,7 +8,6 @@
#include "IRNumbering.h"
#include "mlir/Bytecode/BytecodeImplementation.h"
#include "mlir/Bytecode/BytecodeOpInterface.h"
#include "mlir/IR/AsmState.h"
#include "mlir/IR/BuiltinTypes.h"
#include "mlir/IR/OpDefinition.h"
@@ -25,10 +24,6 @@ struct IRNumberingState::NumberingDialectWriter : public DialectBytecodeWriter {
NumberingDialectWriter(IRNumberingState &state) : state(state) {}
void writeAttribute(Attribute attr) override { state.number(attr); }
void writeOptionalAttribute(Attribute attr) override {
if (attr)
state.number(attr);
}
void writeType(Type type) override { state.number(type); }
void writeResourceHandle(const AsmDialectResourceHandle &resource) override {
state.number(resource.getDialect(), resource);
@@ -111,9 +106,7 @@ static void groupByDialectPerByte(T range) {
value->number = idx;
}
IRNumberingState::IRNumberingState(Operation *op,
const BytecodeWriterConfig &config)
: config(config) {
IRNumberingState::IRNumberingState(Operation *op) {
// Compute a global operation ID numbering according to the pre-order walk of
// the IR. This is used as reference to construct use-list orders.
unsigned operationID = 0;
@@ -283,29 +276,10 @@ void IRNumberingState::number(Operation &op) {
}
// Only number the operation's dictionary if it isn't empty.
DictionaryAttr dictAttr = op.getDiscardableAttrDictionary();
if (config.getDesiredBytecodeVersion() < 4)
dictAttr = op.getAttrDictionary();
DictionaryAttr dictAttr = op.getAttrDictionary();
if (!dictAttr.empty())
number(dictAttr);
// Visit the operation properties (if any) to make sure referenced attributes
// are numbered.
if (config.getDesiredBytecodeVersion() >= 4 &&
op.getPropertiesStorageSize()) {
if (op.isRegistered()) {
// Operation that have properties *must* implement this interface.
auto iface = cast<BytecodeOpInterface>(op);
NumberingDialectWriter writer(*this);
iface.writeProperties(writer);
} else {
// Unregistered op are storing properties as an optional attribute.
Attribute prop = *op.getPropertiesStorage().as<Attribute *>();
if (prop)
number(prop);
}
}
number(op.getLoc());
}

View File

@@ -18,8 +18,6 @@
#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/CodeGen/NonRelocatableStringpool.h"
#include <cstdint>
namespace mlir {
class BytecodeDialectInterface;
@@ -135,7 +133,7 @@ struct DialectNumbering {
/// emission.
class IRNumberingState {
public:
IRNumberingState(Operation *op, const BytecodeWriterConfig &config);
IRNumberingState(Operation *op);
/// Return the numbered dialects.
auto getDialects() {
@@ -243,9 +241,6 @@ private:
/// The next value ID to assign when numbering.
unsigned nextValueID = 0;
// Configuration: useful to query the required version to emit.
const BytecodeWriterConfig &config;
};
} // namespace detail
} // namespace bytecode