MLIR 24.0.0git
IRNumbering.cpp
Go to the documentation of this file.
1//===- IRNumbering.cpp - MLIR Bytecode IR numbering -----------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "IRNumbering.h"
14#include "mlir/IR/AsmState.h"
16#include "mlir/IR/Location.h"
18
19using namespace mlir;
20using namespace mlir::bytecode::detail;
21
22//===----------------------------------------------------------------------===//
23// NumberingDialectWriter
24//===----------------------------------------------------------------------===//
25
29 llvm::StringMap<std::unique_ptr<DialectVersion>> &dialectVersionMap)
31
32 void writeAttribute(Attribute attr) override { state.number(attr); }
33 void writeOptionalAttribute(Attribute attr) override {
34 if (attr)
35 state.number(attr);
36 }
37 void writeType(Type type) override { state.number(type); }
38 void writeResourceHandle(const AsmDialectResourceHandle &resource) override {
39 state.number(resource.getDialect(), resource);
40 }
41
42 /// Stubbed out methods that are not used for numbering.
43 void writeVarInt(uint64_t) override {}
44 void writeSignedVarInt(int64_t value) override {}
45 void writeAPIntWithKnownWidth(const APInt &value) override {}
46 void writeAPFloatWithKnownSemantics(const APFloat &value) override {}
47 void writeOwnedString(StringRef) override {
48 // TODO: It might be nice to prenumber strings and sort by the number of
49 // references. This could potentially be useful for optimizing things like
50 // file locations.
51 }
52 void writeOwnedBlob(ArrayRef<char> blob) override {}
53 void writeOwnedBool(bool value) override {}
54 void writeUnownedBlob(ArrayRef<char> blob) override {}
55
56 int64_t getBytecodeVersion() const override {
57 return state.getDesiredBytecodeVersion();
58 }
59
60 FailureOr<const DialectVersion *>
61 getDialectVersion(StringRef dialectName) const override {
62 auto dialectEntry = dialectVersionMap.find(dialectName);
63 if (dialectEntry == dialectVersionMap.end())
64 return failure();
65 return dialectEntry->getValue().get();
66 }
67
68 /// The parent numbering state that is populated by this writer.
70
71 /// A map containing dialect version information for each dialect to emit.
72 llvm::StringMap<std::unique_ptr<DialectVersion>> &dialectVersionMap;
73};
74
75//===----------------------------------------------------------------------===//
76// IR Numbering
77//===----------------------------------------------------------------------===//
78
79/// Group and sort the elements of the given range by their parent dialect. This
80/// grouping is applied to sub-sections of the ranged defined by how many bytes
81/// it takes to encode a varint index to that sub-section.
82template <typename T>
83static void groupByDialectPerByte(T range) {
84 if (range.empty())
85 return;
86
87 // A functor used to sort by a given dialect, with a desired dialect to be
88 // ordered first (to better enable sharing of dialects across byte groups).
89 auto sortByDialect = [](unsigned dialectToOrderFirst, const auto &lhs,
90 const auto &rhs) {
91 if (lhs->dialect->number == dialectToOrderFirst)
92 return rhs->dialect->number != dialectToOrderFirst;
93 if (rhs->dialect->number == dialectToOrderFirst)
94 return false;
95 return lhs->dialect->number < rhs->dialect->number;
96 };
97
98 unsigned dialectToOrderFirst = 0;
99 size_t elementsInByteGroup = 0;
100 auto iterRange = range;
101 for (unsigned i = 1; i < 9; ++i) {
102 // Update the number of elements in the current byte grouping. Reminder
103 // that varint encodes 7-bits per byte, so that's how we compute the
104 // number of elements in each byte grouping.
105 elementsInByteGroup = (1ULL << (7ULL * i)) - elementsInByteGroup;
106
107 // Slice out the sub-set of elements that are in the current byte grouping
108 // to be sorted.
109 auto byteSubRange = iterRange.take_front(elementsInByteGroup);
110 iterRange = iterRange.drop_front(byteSubRange.size());
111
112 // Sort the sub range for this byte.
113 llvm::stable_sort(byteSubRange, [&](const auto &lhs, const auto &rhs) {
114 return sortByDialect(dialectToOrderFirst, lhs, rhs);
115 });
116
117 // Update the dialect to order first to be the dialect at the end of the
118 // current grouping. This seeks to allow larger dialect groupings across
119 // byte boundaries.
120 dialectToOrderFirst = byteSubRange.back()->dialect->number;
121
122 // If the data range is now empty, we are done.
123 if (iterRange.empty())
124 break;
125 }
126
127 // Assign the entry numbers based on the sort order.
128 for (auto [idx, value] : llvm::enumerate(range))
129 value->number = idx;
130}
131
133 const BytecodeWriterConfig &config)
134 : config(config) {
135 computeGlobalNumberingState(op);
136
137 // Number the root operation.
138 number(*op);
139
140 // A worklist of region contexts to number and the next value id before that
141 // region.
143
144 // Functor to push the regions of the given operation onto the numbering
145 // context.
146 auto addOpRegionsToNumber = [&](Operation *op) {
147 MutableArrayRef<Region> regions = op->getRegions();
148 if (regions.empty())
149 return;
150
151 // Isolated regions don't share value numbers with their parent, so we can
152 // start numbering these regions at zero.
153 unsigned opFirstValueID = isIsolatedFromAbove(op) ? 0 : nextValueID;
154 for (Region &region : regions)
155 numberContext.emplace_back(&region, opFirstValueID);
156 };
157 addOpRegionsToNumber(op);
158
159 // Iteratively process each of the nested regions.
160 while (!numberContext.empty()) {
161 Region *region;
162 std::tie(region, nextValueID) = numberContext.pop_back_val();
163 number(*region);
164
165 // Traverse into nested regions.
166 for (Operation &op : region->getOps())
167 addOpRegionsToNumber(&op);
168 }
169
170 // Number each of the dialects. For now this is just in the order they were
171 // found, given that the number of dialects on average is small enough to fit
172 // within a singly byte (128). If we ever have real world use cases that have
173 // a huge number of dialects, this could be made more intelligent.
174 for (auto [idx, dialect] : llvm::enumerate(dialects))
175 dialect.second->number = idx;
176
177 // Number each of the recorded components within each dialect.
178
179 // First sort by ref count so that the most referenced elements are first. We
180 // try to bias more heavily used elements to the front. This allows for more
181 // frequently referenced things to be encoded using smaller varints.
182 auto sortByRefCountFn = [](const auto &lhs, const auto &rhs) {
183 return lhs->refCount > rhs->refCount;
184 };
185 llvm::stable_sort(orderedAttrs, sortByRefCountFn);
186 llvm::stable_sort(orderedOpNames, sortByRefCountFn);
187 llvm::stable_sort(orderedTypes, sortByRefCountFn);
188
189 // After that, we apply a secondary ordering based on the parent dialect. This
190 // ordering is applied to sub-sections of the element list defined by how many
191 // bytes it takes to encode a varint index to that sub-section. This allows
192 // for more efficiently encoding components of the same dialect (e.g. we only
193 // have to encode the dialect reference once).
197
198 // Finalize the numbering of the dialect resources.
199 finalizeDialectResourceNumberings(op);
200}
201
203 if (config.shouldElideLocations()) {
204 return getNumber(Attribute(UnknownLoc::get(loc.getContext())));
205 }
206 return getNumber(Attribute(loc));
207}
208
209void IRNumberingState::computeGlobalNumberingState(Operation *rootOp) {
210 // A simple state struct tracking data used when walking operations.
211 struct StackState {
212 /// The operation currently being walked.
213 Operation *op;
214
215 /// The numbering of the operation.
216 OperationNumbering *numbering;
217
218 /// A flag indicating if the current state or one of its parents has
219 /// unresolved isolation status. This is tracked separately from the
220 /// isIsolatedFromAbove bit on `numbering` because we need to be able to
221 /// handle the given case:
222 /// top.op {
223 /// %value = ...
224 /// middle.op {
225 /// %value2 = ...
226 /// inner.op {
227 /// // Here we mark `inner.op` as not isolated. Note `middle.op`
228 /// // isn't known not isolated yet.
229 /// use.op %value2
230 ///
231 /// // Here inner.op is already known to be non-isolated, but
232 /// // `middle.op` is now also discovered to be non-isolated.
233 /// use.op %value
234 /// }
235 /// }
236 /// }
237 bool hasUnresolvedIsolation;
238 };
239
240 // Compute a global operation ID numbering according to the pre-order walk of
241 // the IR. This is used as reference to construct use-list orders.
242 unsigned operationID = 0;
243
244 // Walk each of the operations within the IR, tracking a stack of operations
245 // as we recurse into nested regions. This walk method hooks in at two stages
246 // during the walk:
247 //
248 // BeforeAllRegions:
249 // Here we generate a numbering for the operation and push it onto the
250 // stack if it has regions. We also compute the isolation status of parent
251 // regions at this stage. This is done by checking the parent regions of
252 // operands used by the operation, and marking each region between the
253 // the operand region and the current as not isolated. See
254 // StackState::hasUnresolvedIsolation above for an example.
255 //
256 // AfterAllRegions:
257 // Here we pop the operation from the stack, and if it hasn't been marked
258 // as non-isolated, we mark it as so. A non-isolated use would have been
259 // found while walking the regions, so it is safe to mark the operation at
260 // this point.
261 //
263 rootOp->walk([&](Operation *op, const WalkStage &stage) {
264 // After visiting all nested regions, we pop the operation from the stack.
265 if (op->getNumRegions() && stage.isAfterAllRegions()) {
266 // If no non-isolated uses were found, we can safely mark this operation
267 // as isolated from above.
268 OperationNumbering *numbering = opStack.pop_back_val().numbering;
269 if (!numbering->isIsolatedFromAbove.has_value())
270 numbering->isIsolatedFromAbove = true;
271 return;
272 }
273
274 // When visiting before nested regions, we process "IsolatedFromAbove"
275 // checks and compute the number for this operation.
276 if (!stage.isBeforeAllRegions())
277 return;
278 // Update the isolation status of parent regions if any have yet to be
279 // resolved.
280 if (!opStack.empty() && opStack.back().hasUnresolvedIsolation) {
281 Region *parentRegion = op->getParentRegion();
282 for (Value operand : op->getOperands()) {
283 Region *operandRegion = operand.getParentRegion();
284 if (operandRegion == parentRegion)
285 continue;
286 // We've found a use of an operand outside of the current region,
287 // walk the operation stack searching for the parent operation,
288 // marking every region on the way as not isolated.
289 Operation *operandContainerOp = operandRegion->getParentOp();
290 auto it = std::find_if(
291 opStack.rbegin(), opStack.rend(), [=](const StackState &it) {
292 // We only need to mark up to the container region, or the first
293 // that has an unresolved status.
294 return !it.hasUnresolvedIsolation || it.op == operandContainerOp;
295 });
296 assert(it != opStack.rend() && "expected to find the container");
297 for (auto &state : llvm::make_range(opStack.rbegin(), it)) {
298 // If we stopped at a region that knows its isolation status, we can
299 // stop updating the isolation status for the parent regions.
300 state.hasUnresolvedIsolation = it->hasUnresolvedIsolation;
301 state.numbering->isIsolatedFromAbove = false;
302 }
303 }
304 }
305
306 // Compute the number for this op and push it onto the stack.
307 auto *numbering =
308 new (opAllocator.Allocate()) OperationNumbering(operationID++);
309 if (op->hasTrait<OpTrait::IsIsolatedFromAbove>())
310 numbering->isIsolatedFromAbove = true;
311 operations.try_emplace(op, numbering);
312 if (op->getNumRegions()) {
313 opStack.emplace_back(StackState{
314 op, numbering, !numbering->isIsolatedFromAbove.has_value()});
315 }
316 });
317}
318
319void IRNumberingState::number(Location loc) {
320 if (config.shouldElideLocations()) {
321 number(Attribute(UnknownLoc::get(loc.getContext())));
322 } else {
323 number(Attribute(loc));
324 }
325}
326
327void IRNumberingState::number(Attribute attr) {
328 auto it = attrs.try_emplace(attr);
329 if (!it.second) {
330 ++it.first->second->refCount;
331 return;
332 }
333 auto *numbering = new (attrAllocator.Allocate()) AttributeNumbering(attr);
334 it.first->second = numbering;
335 orderedAttrs.push_back(numbering);
336
337 // Check for OpaqueAttr, which is a dialect-specific attribute that didn't
338 // have a registered dialect when it got created. We don't want to encode this
339 // as the builtin OpaqueAttr, we want to encode it as if the dialect was
340 // actually loaded.
341 if (OpaqueAttr opaqueAttr = dyn_cast<OpaqueAttr>(attr)) {
342 numbering->dialect = &numberDialect(opaqueAttr.getDialectNamespace());
343 return;
344 }
345 numbering->dialect = &numberDialect(&attr.getDialect());
346
347 // If this attribute will be emitted using the bytecode format, perform a
348 // dummy writing to number any nested components.
349 // TODO: We don't allow custom encodings for mutable attributes right now.
350 if (!attr.hasTrait<AttributeTrait::IsMutable>()) {
351 // Try overriding emission with callbacks.
352 for (const auto &callback : config.getAttributeWriterCallbacks()) {
353 NumberingDialectWriter writer(*this, config.getDialectVersionMap());
354 // The client has the ability to override the group name through the
355 // callback.
356 std::optional<StringRef> groupNameOverride;
357 if (succeeded(callback->write(attr, groupNameOverride, writer))) {
358 if (groupNameOverride.has_value())
359 numbering->dialect = &numberDialect(*groupNameOverride);
360 return;
361 }
362 }
363
364 if (const auto *interface = numbering->dialect->interface) {
365 NumberingDialectWriter writer(*this, config.getDialectVersionMap());
366 if (succeeded(interface->writeAttribute(attr, writer)))
367 return;
368 }
369 }
370 // If this attribute will be emitted using the fallback, number the nested
371 // dialect resources. We don't number everything (e.g. no nested
372 // attributes/types), because we don't want to encode things we won't decode
373 // (the textual format can't really share much).
374 AsmState tempState(attr.getContext());
375 llvm::raw_null_ostream dummyOS;
376 attr.print(dummyOS, tempState);
377
378 // Number the used dialect resources.
379 for (const auto &it : tempState.getDialectResources())
380 number(it.getFirst(), it.getSecond().getArrayRef());
381}
382
383void IRNumberingState::number(Block &block) {
384 // Number the arguments of the block.
385 for (BlockArgument arg : block.getArguments()) {
386 valueIDs.try_emplace(arg, nextValueID++);
387 number(arg.getLoc());
388 number(arg.getType());
389 }
390
391 // Number the operations in this block.
392 unsigned &numOps = blockOperationCounts[&block];
393 for (Operation &op : block) {
394 number(op);
395 ++numOps;
396 }
397}
398
399auto IRNumberingState::numberDialect(Dialect *dialect) -> DialectNumbering & {
400 DialectNumbering *&numbering = registeredDialects[dialect];
401 if (!numbering) {
402 numbering = &numberDialect(dialect->getNamespace());
403 numbering->interface = dyn_cast<BytecodeDialectInterface>(dialect);
404 numbering->asmInterface = dyn_cast<OpAsmDialectInterface>(dialect);
405 }
406 return *numbering;
407}
408
409auto IRNumberingState::numberDialect(StringRef dialect) -> DialectNumbering & {
410 DialectNumbering *&numbering = dialects[dialect];
411 if (!numbering) {
412 numbering = new (dialectAllocator.Allocate())
413 DialectNumbering(dialect, dialects.size() - 1);
414 }
415 return *numbering;
416}
417
418void IRNumberingState::number(Region &region) {
419 if (region.empty())
420 return;
421 size_t firstValueID = nextValueID;
422
423 // Number the blocks within this region.
424 size_t blockCount = 0;
425 for (auto it : llvm::enumerate(region)) {
426 blockIDs.try_emplace(&it.value(), it.index());
427 number(it.value());
428 ++blockCount;
429 }
430
431 // Remember the number of blocks and values in this region.
432 regionBlockValueCounts.try_emplace(&region, blockCount,
433 nextValueID - firstValueID);
434}
435
436void IRNumberingState::number(Operation &op) {
437 // Number the components of an operation that won't be numbered elsewhere
438 // (e.g. we don't number operands, regions, or successors here).
439 number(op.getName());
440 for (OpResult result : op.getResults()) {
441 valueIDs.try_emplace(result, nextValueID++);
442 number(result.getType());
443 }
444
445 // Prior to a version with native property encoding, or when properties are
446 // not used, we need to number also the merged dictionary containing both the
447 // inherent and discardable attribute.
448 DictionaryAttr dictAttr;
449 if (config.getDesiredBytecodeVersion() >=
452 dictAttr = op.getRawDictionaryAttrs();
453 else {
454 NamedAttrList attrs;
455 op.getName().walkInherentAttrs(&op, [&](StringRef name, Attribute &attr) {
456 attrs.append(name, attr);
457 });
458 attrs.append(op.getDiscardableAttrDictionary().getValue());
459 dictAttr = attrs.getDictionary(op.getContext());
460 }
461 // Only number the operation's dictionary if it isn't empty.
462 if (!dictAttr.empty())
463 number(dictAttr);
464
465 // Visit the operation properties (if any) to make sure referenced attributes
466 // are numbered.
467 if (config.getDesiredBytecodeVersion() >=
470 if (op.isRegistered()) {
471 // Operation that have properties *must* implement this interface.
472 auto iface = cast<BytecodeOpInterface>(op);
473 NumberingDialectWriter writer(*this, config.getDialectVersionMap());
474 iface.writeProperties(writer);
475 } else {
476 // Unregistered op are storing properties as an optional attribute.
477 if (Attribute prop = *op.getPropertiesStorage().as<Attribute *>())
478 number(prop);
479 }
480 }
481
482 number(op.getLoc());
483}
484
485void IRNumberingState::number(OperationName opName) {
486 OpNameNumbering *&numbering = opNames[opName];
487 if (numbering) {
488 ++numbering->refCount;
489 return;
490 }
491 DialectNumbering *dialectNumber = nullptr;
492 if (Dialect *dialect = opName.getDialect())
493 dialectNumber = &numberDialect(dialect);
494 else
495 dialectNumber = &numberDialect(opName.getDialectNamespace());
496
497 numbering =
498 new (opNameAllocator.Allocate()) OpNameNumbering(dialectNumber, opName);
499 orderedOpNames.push_back(numbering);
500}
501
502void IRNumberingState::number(Type type) {
503 auto it = types.try_emplace(type);
504 if (!it.second) {
505 ++it.first->second->refCount;
506 return;
507 }
508 auto *numbering = new (typeAllocator.Allocate()) TypeNumbering(type);
509 it.first->second = numbering;
510 orderedTypes.push_back(numbering);
511
512 // Check for OpaqueType, which is a dialect-specific type that didn't have a
513 // registered dialect when it got created. We don't want to encode this as the
514 // builtin OpaqueType, we want to encode it as if the dialect was actually
515 // loaded.
516 if (OpaqueType opaqueType = dyn_cast<OpaqueType>(type)) {
517 numbering->dialect = &numberDialect(opaqueType.getDialectNamespace());
518 return;
519 }
520 numbering->dialect = &numberDialect(&type.getDialect());
521
522 // If this type will be emitted using the bytecode format, perform a dummy
523 // writing to number any nested components.
524 // TODO: We don't allow custom encodings for mutable types right now.
525 if (!type.hasTrait<TypeTrait::IsMutable>()) {
526 // Try overriding emission with callbacks.
527 for (const auto &callback : config.getTypeWriterCallbacks()) {
528 NumberingDialectWriter writer(*this, config.getDialectVersionMap());
529 // The client has the ability to override the group name through the
530 // callback.
531 std::optional<StringRef> groupNameOverride;
532 if (succeeded(callback->write(type, groupNameOverride, writer))) {
533 if (groupNameOverride.has_value())
534 numbering->dialect = &numberDialect(*groupNameOverride);
535 return;
536 }
537 }
538
539 // If this attribute will be emitted using the bytecode format, perform a
540 // dummy writing to number any nested components.
541 if (const auto *interface = numbering->dialect->interface) {
542 NumberingDialectWriter writer(*this, config.getDialectVersionMap());
543 if (succeeded(interface->writeType(type, writer)))
544 return;
545 }
546 }
547 // If this type will be emitted using the fallback, number the nested dialect
548 // resources. We don't number everything (e.g. no nested attributes/types),
549 // because we don't want to encode things we won't decode (the textual format
550 // can't really share much).
551 AsmState tempState(type.getContext());
552 llvm::raw_null_ostream dummyOS;
553 type.print(dummyOS, tempState);
554
555 // Number the used dialect resources.
556 for (const auto &it : tempState.getDialectResources())
557 number(it.getFirst(), it.getSecond().getArrayRef());
558}
559
560void IRNumberingState::number(Dialect *dialect,
561 ArrayRef<AsmDialectResourceHandle> resources) {
562 DialectNumbering &dialectNumber = numberDialect(dialect);
563 assert(
564 dialectNumber.asmInterface &&
565 "expected dialect owning a resource to implement OpAsmDialectInterface");
566
567 for (const auto &resource : resources) {
568 // Check if this is a newly seen resource.
569 if (!dialectNumber.resources.insert(resource))
570 return;
571
572 auto *numbering =
573 new (resourceAllocator.Allocate()) DialectResourceNumbering(
574 dialectNumber.asmInterface->getResourceKey(resource));
575 dialectNumber.resourceMap.insert({numbering->key, numbering});
576 dialectResources.try_emplace(resource, numbering);
577 }
578}
579
581 return config.getDesiredBytecodeVersion();
582}
583
584namespace {
585/// A dummy resource builder used to number dialect resources.
586struct NumberingResourceBuilder : public AsmResourceBuilder {
587 NumberingResourceBuilder(DialectNumbering *dialect, unsigned &nextResourceID)
588 : dialect(dialect), nextResourceID(nextResourceID) {}
589 ~NumberingResourceBuilder() override = default;
590
591 void buildBlob(StringRef key, ArrayRef<char>, uint32_t) final {
592 numberEntry(key);
593 }
594 void buildBool(StringRef key, bool) final { numberEntry(key); }
595 void buildString(StringRef key, StringRef) final {
596 // TODO: We could pre-number the value string here as well.
597 numberEntry(key);
598 }
599
600 /// Number the dialect entry for the given key.
601 void numberEntry(StringRef key) {
602 // TODO: We could pre-number resource key strings here as well.
603
604 auto *it = dialect->resourceMap.find(key);
605 if (it != dialect->resourceMap.end()) {
606 it->second->number = nextResourceID++;
607 it->second->isDeclaration = false;
608 }
609 }
610
611 DialectNumbering *dialect;
612 unsigned &nextResourceID;
613};
614} // namespace
615
616void IRNumberingState::finalizeDialectResourceNumberings(Operation *rootOp) {
617 unsigned nextResourceID = 0;
618 for (DialectNumbering &dialect : getDialects()) {
619 if (!dialect.asmInterface)
620 continue;
621 NumberingResourceBuilder entryBuilder(&dialect, nextResourceID);
622 dialect.asmInterface->buildResources(rootOp, dialect.resources,
623 entryBuilder);
624
625 // Number any resources that weren't added by the dialect. This can happen
626 // if there was no backing data to the resource, but we still want these
627 // resource references to roundtrip, so we number them and indicate that the
628 // data is missing.
629 for (const auto &it : dialect.resourceMap)
630 if (it.second->isDeclaration)
631 it.second->number = nextResourceID++;
632 }
633}
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.
Definition AsmState.h:247
Attributes are known-constant values of operations.
Definition Attributes.h:25
Dialect & getDialect() const
Get the dialect this attribute is registered to.
Definition Attributes.h:58
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.
Definition Attributes.h:92
BlockArgListType getArguments()
Definition Block.h:111
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...
Definition Location.h:76
MLIRContext * getContext() const
Return the context this location is uniqued in.
Definition Location.h:86
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.
void walkInherentAttrs(Operation *op, InherentAttrVisitor visitor) const
Visit the inherent attributes stored in the properties of op.
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
PropertyRef getPropertiesStorage()
Return a generic (but typed) reference to the property type storage.
Definition Operation.h:953
bool hasTrait()
Returns true if the operation was registered with a particular trait, e.g.
Definition Operation.h:801
bool isRegistered()
Returns true if this operation has a registered operation description, otherwise false.
Definition Operation.h:125
unsigned getNumRegions()
Returns the number of regions held by this operation.
Definition Operation.h:726
Location getLoc()
The source location the operation was defined or derived from.
Definition Operation.h:240
DictionaryAttr getRawDictionaryAttrs()
Return all attributes that are not stored as properties.
Definition Operation.h:561
OperationName getName()
The name of an operation is the key identifier for it.
Definition Operation.h:115
DictionaryAttr getDiscardableAttrDictionary()
Return all of the discardable attributes on this operation as a DictionaryAttr.
Definition Operation.h:553
MutableArrayRef< Region > getRegions()
Returns the regions held by this operation.
Definition Operation.h:729
operand_range getOperands()
Returns an iterator on the underlying Value's.
Definition Operation.h:403
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),...
Definition Operation.h:849
result_range getResults()
Definition Operation.h:440
int getPropertiesStorageSize() const
Returns the properties storage size.
Definition Operation.h:948
Region * getParentRegion()
Returns the region to which the instruction belongs.
Definition Operation.h:247
MLIRContext * getContext()
Return the context this operation is associated with.
Definition Operation.h:233
This class contains a list of basic blocks and a link to the parent operation it is attached to.
Definition Region.h:26
Region * getParentRegion()
Return the region containing this region or nullptr if the region is attached to a top-level operatio...
Definition Region.cpp:45
iterator_range< OpIterator > getOps()
Definition Region.h:185
bool empty()
Definition Region.h:60
Operation * getParentOp()
Return the parent operation this region is attached to.
Definition Region.h:213
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
void print(raw_ostream &os) const
Print the current type.
Dialect & getDialect() const
Get the dialect this type is registered to.
Definition Types.h:107
MLIRContext * getContext() const
Return the MLIRContext in which this type was uniqued.
Definition Types.cpp:35
bool hasTrait()
Returns true if the type was registered with a particular trait.
Definition Types.h:185
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition Value.h:96
A utility class to encode the current walk stage for "generic" walkers.
Definition Visitors.h:53
bool isAfterAllRegions() const
Return true if parent operation is being visited after all regions.
Definition Visitors.h:66
bool isBeforeAllRegions() const
Return true if parent operation is being visited before all regions.
Definition Visitors.h:58
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.
Definition Attributes.h:288
detail::StorageUserTrait::IsMutable< ConcreteType > IsMutable
Definition Types.h:297
@ kNativePropertiesEncoding
Support for encoding properties natively in bytecode instead of merged with the discardable attribute...
Definition Encoding.h:46
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 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.
unsigned refCount
The number of references to this name.
Definition IRNumbering.h:80
DialectNumbering * dialect
The dialect of this value.
Definition IRNumbering.h:71
This class represents the numbering entry of an operation.
std::optional< bool > isIsolatedFromAbove
A flag indicating if this operation's regions are isolated.