57 return state.getDesiredBytecodeVersion();
60 FailureOr<const DialectVersion *>
65 return dialectEntry->getValue().get();
89 auto sortByDialect = [](
unsigned dialectToOrderFirst,
const auto &
lhs,
91 if (
lhs->dialect->number == dialectToOrderFirst)
92 return rhs->dialect->number != dialectToOrderFirst;
93 if (
rhs->dialect->number == dialectToOrderFirst)
95 return lhs->dialect->number <
rhs->dialect->number;
98 unsigned dialectToOrderFirst = 0;
99 size_t elementsInByteGroup = 0;
100 auto iterRange = range;
101 for (
unsigned i = 1; i < 9; ++i) {
105 elementsInByteGroup = (1ULL << (7ULL * i)) - elementsInByteGroup;
109 auto byteSubRange = iterRange.take_front(elementsInByteGroup);
110 iterRange = iterRange.drop_front(byteSubRange.size());
113 llvm::stable_sort(byteSubRange, [&](
const auto &
lhs,
const auto &
rhs) {
114 return sortByDialect(dialectToOrderFirst,
lhs,
rhs);
120 dialectToOrderFirst = byteSubRange.back()->dialect->number;
123 if (iterRange.empty())
128 for (
auto [idx, value] : llvm::enumerate(range))
135 computeGlobalNumberingState(op);
146 auto addOpRegionsToNumber = [&](
Operation *op) {
154 for (
Region ®ion : regions)
155 numberContext.emplace_back(®ion, opFirstValueID);
157 addOpRegionsToNumber(op);
160 while (!numberContext.empty()) {
162 std::tie(region, nextValueID) = numberContext.pop_back_val();
167 addOpRegionsToNumber(&op);
174 for (
auto [idx, dialect] : llvm::enumerate(dialects))
175 dialect.second->number = idx;
182 auto sortByRefCountFn = [](
const auto &
lhs,
const auto &
rhs) {
183 return lhs->refCount >
rhs->refCount;
185 llvm::stable_sort(orderedAttrs, sortByRefCountFn);
186 llvm::stable_sort(orderedOpNames, sortByRefCountFn);
187 llvm::stable_sort(orderedTypes, sortByRefCountFn);
199 finalizeDialectResourceNumberings(op);
203 if (config.shouldElideLocations()) {
209void IRNumberingState::computeGlobalNumberingState(
Operation *rootOp) {
237 bool hasUnresolvedIsolation;
242 unsigned operationID = 0;
280 if (!opStack.empty() && opStack.back().hasUnresolvedIsolation) {
284 if (operandRegion == parentRegion)
290 auto it = std::find_if(
291 opStack.rbegin(), opStack.rend(), [=](
const StackState &it) {
294 return !it.hasUnresolvedIsolation || it.op == operandContainerOp;
296 assert(it != opStack.rend() &&
"expected to find the container");
297 for (
auto &state : llvm::make_range(opStack.rbegin(), it)) {
300 state.hasUnresolvedIsolation = it->hasUnresolvedIsolation;
301 state.numbering->isIsolatedFromAbove =
false;
308 new (opAllocator.Allocate()) OperationNumbering(operationID++);
309 if (op->
hasTrait<OpTrait::IsIsolatedFromAbove>())
311 operations.try_emplace(op, numbering);
313 opStack.emplace_back(StackState{
319void IRNumberingState::number(Location loc) {
320 if (config.shouldElideLocations()) {
321 number(Attribute(UnknownLoc::get(loc.
getContext())));
323 number(Attribute(loc));
327void IRNumberingState::number(Attribute attr) {
328 auto it = attrs.try_emplace(attr);
330 ++it.first->second->refCount;
333 auto *numbering =
new (attrAllocator.Allocate()) AttributeNumbering(attr);
334 it.first->second = numbering;
335 orderedAttrs.push_back(numbering);
341 if (OpaqueAttr opaqueAttr = dyn_cast<OpaqueAttr>(attr)) {
342 numbering->dialect = &numberDialect(opaqueAttr.getDialectNamespace());
345 numbering->dialect = &numberDialect(&attr.
getDialect());
352 for (
const auto &callback : config.getAttributeWriterCallbacks()) {
356 std::optional<StringRef> groupNameOverride;
357 if (succeeded(callback->write(attr, groupNameOverride, writer))) {
358 if (groupNameOverride.has_value())
359 numbering->dialect = &numberDialect(*groupNameOverride);
364 if (
const auto *interface = numbering->dialect->interface) {
366 if (succeeded(interface->writeAttribute(attr, writer)))
375 llvm::raw_null_ostream dummyOS;
376 attr.
print(dummyOS, tempState);
379 for (
const auto &it : tempState.getDialectResources())
380 number(it.getFirst(), it.getSecond().getArrayRef());
383void IRNumberingState::number(
Block &block) {
386 valueIDs.try_emplace(arg, nextValueID++);
387 number(arg.getLoc());
388 number(arg.getType());
392 unsigned &numOps = blockOperationCounts[&block];
393 for (Operation &op : block) {
399auto IRNumberingState::numberDialect(Dialect *dialect) ->
DialectNumbering & {
402 numbering = &numberDialect(dialect->getNamespace());
403 numbering->
interface = dyn_cast<BytecodeDialectInterface>(dialect);
404 numbering->
asmInterface = dyn_cast<OpAsmDialectInterface>(dialect);
409auto IRNumberingState::numberDialect(StringRef dialect) ->
DialectNumbering & {
412 numbering =
new (dialectAllocator.Allocate())
418void IRNumberingState::number(Region ®ion) {
421 size_t firstValueID = nextValueID;
424 size_t blockCount = 0;
425 for (
auto it : llvm::enumerate(region)) {
426 blockIDs.try_emplace(&it.value(), it.index());
432 regionBlockValueCounts.try_emplace(®ion, blockCount,
433 nextValueID - firstValueID);
436void IRNumberingState::number(Operation &op) {
441 valueIDs.try_emplace(
result, nextValueID++);
448 DictionaryAttr dictAttr;
454 if (!dictAttr.empty())
459 if (config.getDesiredBytecodeVersion() >=
464 auto iface = cast<BytecodeOpInterface>(op);
466 iface.writeProperties(writer);
477void IRNumberingState::number(OperationName opName) {
478 OpNameNumbering *&numbering = opNames[opName];
483 DialectNumbering *dialectNumber =
nullptr;
485 dialectNumber = &numberDialect(dialect);
490 new (opNameAllocator.Allocate()) OpNameNumbering(dialectNumber, opName);
491 orderedOpNames.push_back(numbering);
494void IRNumberingState::number(Type type) {
495 auto it = types.try_emplace(type);
497 ++it.first->second->refCount;
500 auto *numbering =
new (typeAllocator.Allocate()) TypeNumbering(type);
501 it.first->second = numbering;
502 orderedTypes.push_back(numbering);
508 if (OpaqueType opaqueType = dyn_cast<OpaqueType>(type)) {
509 numbering->
dialect = &numberDialect(opaqueType.getDialectNamespace());
519 for (
const auto &callback : config.getTypeWriterCallbacks()) {
523 std::optional<StringRef> groupNameOverride;
524 if (succeeded(callback->write(type, groupNameOverride, writer))) {
525 if (groupNameOverride.has_value())
526 numbering->
dialect = &numberDialect(*groupNameOverride);
535 if (succeeded(interface->writeType(type, writer)))
544 llvm::raw_null_ostream dummyOS;
545 type.
print(dummyOS, tempState);
548 for (
const auto &it : tempState.getDialectResources())
549 number(it.getFirst(), it.getSecond().getArrayRef());
552void IRNumberingState::number(Dialect *dialect,
553 ArrayRef<AsmDialectResourceHandle> resources) {
554 DialectNumbering &dialectNumber = numberDialect(dialect);
557 "expected dialect owning a resource to implement OpAsmDialectInterface");
559 for (
const auto &resource : resources) {
561 if (!dialectNumber.
resources.insert(resource))
565 new (resourceAllocator.Allocate()) DialectResourceNumbering(
567 dialectNumber.
resourceMap.insert({numbering->key, numbering});
568 dialectResources.try_emplace(resource, numbering);
573 return config.getDesiredBytecodeVersion();
579 NumberingResourceBuilder(
DialectNumbering *dialect,
unsigned &nextResourceID)
580 : dialect(dialect), nextResourceID(nextResourceID) {}
581 ~NumberingResourceBuilder()
override =
default;
586 void buildBool(StringRef key,
bool)
final { numberEntry(key); }
587 void buildString(StringRef key, StringRef)
final {
593 void numberEntry(StringRef key) {
596 auto *it = dialect->resourceMap.find(key);
597 if (it != dialect->resourceMap.end()) {
598 it->second->number = nextResourceID++;
599 it->second->isDeclaration =
false;
603 DialectNumbering *dialect;
604 unsigned &nextResourceID;
608void IRNumberingState::finalizeDialectResourceNumberings(Operation *rootOp) {
609 unsigned nextResourceID = 0;
611 if (!dialect.asmInterface)
613 NumberingResourceBuilder entryBuilder(&dialect, nextResourceID);
614 dialect.asmInterface->buildResources(rootOp, dialect.resources,
621 for (
const auto &it : dialect.resourceMap)
622 if (it.second->isDeclaration)
623 it.second->number = nextResourceID++;
static void groupByDialectPerByte(T range)
Group and sort the elements of the given range by their parent dialect.
This class represents an opaque handle to a dialect resource entry.
Dialect * getDialect() const
Return the dialect that owns the resource.
This class is used to build resource entries for use by the printer.
Attributes are known-constant values of operations.
Dialect & getDialect() const
Get the dialect this attribute is registered to.
void print(raw_ostream &os, bool elideType=false) const
Print the attribute.
MLIRContext * getContext() const
Return the context this attribute belongs to.
bool hasTrait()
Returns true if the type was registered with a particular trait.
BlockArgListType getArguments()
This class contains the configuration used for the bytecode writer.
This class defines a virtual interface for writing to a bytecode stream, providing hooks into the byt...
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
MLIRContext * getContext() const
Return the context this location is uniqued in.
Dialect * getDialect() const
Return the dialect this operation is registered to if the dialect is loaded in the context,...
StringRef getDialectNamespace() const
Return the name of the dialect this operation is registered to.
Operation is the basic unit of execution within MLIR.
PropertyRef getPropertiesStorage()
Return a generic (but typed) reference to the property type storage.
DictionaryAttr getAttrDictionary()
Return all of the attributes on this operation as a DictionaryAttr.
bool hasTrait()
Returns true if the operation was registered with a particular trait, e.g.
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.
DictionaryAttr getRawDictionaryAttrs()
Return all attributes that are not stored as properties.
OperationName getName()
The name of an operation is the key identifier for it.
MutableArrayRef< Region > getRegions()
Returns the regions held by this operation.
operand_range getOperands()
Returns an iterator on the underlying Value's.
std::enable_if_t< llvm::function_traits< std::decay_t< FnT > >::num_args==1, RetT > walk(FnT &&callback)
Walk the operation by calling the callback for each nested operation (including this one),...
result_range getResults()
int getPropertiesStorageSize() const
Returns the properties storage size.
Region * getParentRegion()
Returns the region to which the instruction belongs.
This class contains a list of basic blocks and a link to the parent operation it is attached to.
Region * getParentRegion()
Return the region containing this region or nullptr if the region is attached to a top-level operatio...
iterator_range< OpIterator > getOps()
Operation * getParentOp()
Return the parent operation this region is attached to.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
void print(raw_ostream &os) const
Print the current type.
Dialect & getDialect() const
Get the dialect this type is registered to.
MLIRContext * getContext() const
Return the MLIRContext in which this type was uniqued.
bool hasTrait()
Returns true if the type was registered with a particular trait.
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
A utility class to encode the current walk stage for "generic" walkers.
bool isAfterAllRegions() const
Return true if parent operation is being visited after all regions.
bool isBeforeAllRegions() const
Return true if parent operation is being visited before all regions.
IRNumberingState(Operation *op, const BytecodeWriterConfig &config)
int64_t getDesiredBytecodeVersion() const
Get the set desired bytecode version to emit.
bool isIsolatedFromAbove(Operation *op)
Return if the given operation is isolated from above.
auto getDialects()
Return the numbered dialects.
unsigned getNumber(Attribute attr)
Return the number for the given IR unit.
detail::StorageUserTrait::IsMutable< ConcreteType > IsMutable
This trait is used to determine if an attribute is mutable or not.
detail::StorageUserTrait::IsMutable< ConcreteType > IsMutable
@ kNativePropertiesEncoding
Support for encoding properties natively in bytecode instead of merged with the discardable attribute...
Include the generated interface declarations.
NumberingDialectWriter(IRNumberingState &state, llvm::StringMap< std::unique_ptr< DialectVersion > > &dialectVersionMap)
llvm::StringMap< std::unique_ptr< DialectVersion > > & dialectVersionMap
A map containing dialect version information for each dialect to emit.
void writeType(Type type) override
Write a reference to the given type.
void writeVarInt(uint64_t) override
Stubbed out methods that are not used for numbering.
FailureOr< const DialectVersion * > getDialectVersion(StringRef dialectName) const override
Retrieve the dialect version by name if available.
void writeOptionalAttribute(Attribute attr) override
void writeOwnedString(StringRef) override
Write a string to the bytecode, which is owned by the caller and is guaranteed to not die before the ...
IRNumberingState & state
The parent numbering state that is populated by this writer.
void writeUnownedBlob(ArrayRef< char > blob) override
Write a blob to the bytecode, which is not owned by the caller.
void writeAttribute(Attribute attr) override
Write a reference to the given attribute.
void writeResourceHandle(const AsmDialectResourceHandle &resource) override
Write the given handle to a dialect resource.
void writeOwnedBool(bool value) override
Write a bool to the output stream.
void writeAPIntWithKnownWidth(const APInt &value) override
Write an APInt to the bytecode stream whose bitwidth will be known externally at read time.
void writeSignedVarInt(int64_t value) override
Write a signed variable width integer to the output stream.
int64_t getBytecodeVersion() const override
Return the bytecode version being emitted for.
void writeOwnedBlob(ArrayRef< char > blob) override
Write a blob to the bytecode, which is owned by the caller and is guaranteed to not die before the en...
void writeAPFloatWithKnownSemantics(const APFloat &value) override
Write an APFloat to the bytecode stream whose semantics will be known externally at read time.
This class represents a numbering entry for an Dialect.
const BytecodeDialectInterface * interface
The bytecode dialect interface of the dialect if defined.
llvm::MapVector< StringRef, DialectResourceNumbering * > resourceMap
A mapping from resource key to the corresponding resource numbering entry.
SetVector< AsmDialectResourceHandle > resources
The referenced resources of this dialect.
const OpAsmDialectInterface * asmInterface
The asm dialect interface of the dialect if defined.
This class represents the numbering entry of an operation.
std::optional< bool > isIsolatedFromAbove
A flag indicating if this operation's regions are isolated.