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"
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() >= bytecode::kNativePropertiesEncoding)
450 dictAttr = op.getRawDictionaryAttrs();
451 else
452 dictAttr = op.getAttrDictionary();
453 // Only number the operation's dictionary if it isn't empty.
454 if (!dictAttr.empty())
455 number(dictAttr);
456
457 // Visit the operation properties (if any) to make sure referenced attributes
458 // are numbered.
459 if (config.getDesiredBytecodeVersion() >=
462 if (op.isRegistered()) {
463 // Operation that have properties *must* implement this interface.
464 auto iface = cast<BytecodeOpInterface>(op);
465 NumberingDialectWriter writer(*this, config.getDialectVersionMap());
466 iface.writeProperties(writer);
467 } else {
468 // Unregistered op are storing properties as an optional attribute.
469 if (Attribute prop = *op.getPropertiesStorage().as<Attribute *>())
470 number(prop);
471 }
472 }
473
474 number(op.getLoc());
475}
476
477void IRNumberingState::number(OperationName opName) {
478 OpNameNumbering *&numbering = opNames[opName];
479 if (numbering) {
480 ++numbering->refCount;
481 return;
482 }
483 DialectNumbering *dialectNumber = nullptr;
484 if (Dialect *dialect = opName.getDialect())
485 dialectNumber = &numberDialect(dialect);
486 else
487 dialectNumber = &numberDialect(opName.getDialectNamespace());
488
489 numbering =
490 new (opNameAllocator.Allocate()) OpNameNumbering(dialectNumber, opName);
491 orderedOpNames.push_back(numbering);
492}
493
494void IRNumberingState::number(Type type) {
495 auto it = types.try_emplace(type);
496 if (!it.second) {
497 ++it.first->second->refCount;
498 return;
499 }
500 auto *numbering = new (typeAllocator.Allocate()) TypeNumbering(type);
501 it.first->second = numbering;
502 orderedTypes.push_back(numbering);
503
504 // Check for OpaqueType, which is a dialect-specific type that didn't have a
505 // registered dialect when it got created. We don't want to encode this as the
506 // builtin OpaqueType, we want to encode it as if the dialect was actually
507 // loaded.
508 if (OpaqueType opaqueType = dyn_cast<OpaqueType>(type)) {
509 numbering->dialect = &numberDialect(opaqueType.getDialectNamespace());
510 return;
511 }
512 numbering->dialect = &numberDialect(&type.getDialect());
513
514 // If this type will be emitted using the bytecode format, perform a dummy
515 // writing to number any nested components.
516 // TODO: We don't allow custom encodings for mutable types right now.
517 if (!type.hasTrait<TypeTrait::IsMutable>()) {
518 // Try overriding emission with callbacks.
519 for (const auto &callback : config.getTypeWriterCallbacks()) {
520 NumberingDialectWriter writer(*this, config.getDialectVersionMap());
521 // The client has the ability to override the group name through the
522 // callback.
523 std::optional<StringRef> groupNameOverride;
524 if (succeeded(callback->write(type, groupNameOverride, writer))) {
525 if (groupNameOverride.has_value())
526 numbering->dialect = &numberDialect(*groupNameOverride);
527 return;
528 }
529 }
530
531 // If this attribute will be emitted using the bytecode format, perform a
532 // dummy writing to number any nested components.
533 if (const auto *interface = numbering->dialect->interface) {
534 NumberingDialectWriter writer(*this, config.getDialectVersionMap());
535 if (succeeded(interface->writeType(type, writer)))
536 return;
537 }
538 }
539 // If this type will be emitted using the fallback, number the nested dialect
540 // resources. We don't number everything (e.g. no nested attributes/types),
541 // because we don't want to encode things we won't decode (the textual format
542 // can't really share much).
543 AsmState tempState(type.getContext());
544 llvm::raw_null_ostream dummyOS;
545 type.print(dummyOS, tempState);
546
547 // Number the used dialect resources.
548 for (const auto &it : tempState.getDialectResources())
549 number(it.getFirst(), it.getSecond().getArrayRef());
550}
551
552void IRNumberingState::number(Dialect *dialect,
553 ArrayRef<AsmDialectResourceHandle> resources) {
554 DialectNumbering &dialectNumber = numberDialect(dialect);
555 assert(
556 dialectNumber.asmInterface &&
557 "expected dialect owning a resource to implement OpAsmDialectInterface");
558
559 for (const auto &resource : resources) {
560 // Check if this is a newly seen resource.
561 if (!dialectNumber.resources.insert(resource))
562 return;
563
564 auto *numbering =
565 new (resourceAllocator.Allocate()) DialectResourceNumbering(
566 dialectNumber.asmInterface->getResourceKey(resource));
567 dialectNumber.resourceMap.insert({numbering->key, numbering});
568 dialectResources.try_emplace(resource, numbering);
569 }
570}
571
573 return config.getDesiredBytecodeVersion();
574}
575
576namespace {
577/// A dummy resource builder used to number dialect resources.
578struct NumberingResourceBuilder : public AsmResourceBuilder {
579 NumberingResourceBuilder(DialectNumbering *dialect, unsigned &nextResourceID)
580 : dialect(dialect), nextResourceID(nextResourceID) {}
581 ~NumberingResourceBuilder() override = default;
582
583 void buildBlob(StringRef key, ArrayRef<char>, uint32_t) final {
584 numberEntry(key);
585 }
586 void buildBool(StringRef key, bool) final { numberEntry(key); }
587 void buildString(StringRef key, StringRef) final {
588 // TODO: We could pre-number the value string here as well.
589 numberEntry(key);
590 }
591
592 /// Number the dialect entry for the given key.
593 void numberEntry(StringRef key) {
594 // TODO: We could pre-number resource key strings here as well.
595
596 auto *it = dialect->resourceMap.find(key);
597 if (it != dialect->resourceMap.end()) {
598 it->second->number = nextResourceID++;
599 it->second->isDeclaration = false;
600 }
601 }
602
603 DialectNumbering *dialect;
604 unsigned &nextResourceID;
605};
606} // namespace
607
608void IRNumberingState::finalizeDialectResourceNumberings(Operation *rootOp) {
609 unsigned nextResourceID = 0;
610 for (DialectNumbering &dialect : getDialects()) {
611 if (!dialect.asmInterface)
612 continue;
613 NumberingResourceBuilder entryBuilder(&dialect, nextResourceID);
614 dialect.asmInterface->buildResources(rootOp, dialect.resources,
615 entryBuilder);
616
617 // Number any resources that weren't added by the dialect. This can happen
618 // if there was no backing data to the resource, but we still want these
619 // resource references to roundtrip, so we number them and indicate that the
620 // data is missing.
621 for (const auto &it : dialect.resourceMap)
622 if (it.second->isDeclaration)
623 it.second->number = nextResourceID++;
624 }
625}
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...
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.
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:926
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:774
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:699
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:534
OperationName getName()
The name of an operation is the key identifier for it.
Definition Operation.h:115
MutableArrayRef< Region > getRegions()
Returns the regions held by this operation.
Definition Operation.h:702
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:822
result_range getResults()
Definition Operation.h:440
int getPropertiesStorageSize() const
Returns the properties storage size.
Definition Operation.h:921
Region * getParentRegion()
Returns the region to which the instruction belongs.
Definition Operation.h:247
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.
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.