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