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