1 | //===- Operation.h - MLIR Operation Class -----------------------*- C++ -*-===// |
---|---|
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 | // This file defines the Operation class. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | |
13 | #ifndef MLIR_IR_OPERATION_H |
14 | #define MLIR_IR_OPERATION_H |
15 | |
16 | #include "mlir/IR/Block.h" |
17 | #include "mlir/IR/BuiltinAttributes.h" |
18 | #include "mlir/IR/Diagnostics.h" |
19 | #include "mlir/IR/OperationSupport.h" |
20 | #include "mlir/IR/Region.h" |
21 | #include "llvm/ADT/Twine.h" |
22 | #include <optional> |
23 | |
24 | namespace mlir { |
25 | namespace detail { |
26 | /// This is a "tag" used for mapping the properties storage in |
27 | /// llvm::TrailingObjects. |
28 | enum class OpProperties : char {}; |
29 | } // namespace detail |
30 | |
31 | /// Operation is the basic unit of execution within MLIR. |
32 | /// |
33 | /// The following documentation are recommended to understand this class: |
34 | /// - https://mlir.llvm.org/docs/LangRef/#operations |
35 | /// - https://mlir.llvm.org/docs/Tutorials/UnderstandingTheIRStructure/ |
36 | /// |
37 | /// An Operation is defined first by its name, which is a unique string. The |
38 | /// name is interpreted so that if it contains a '.' character, the part before |
39 | /// is the dialect name this operation belongs to, and everything that follows |
40 | /// is this operation name within the dialect. |
41 | /// |
42 | /// An Operation defines zero or more SSA `Value` that we refer to as the |
43 | /// Operation results. This array of Value is actually stored in memory before |
44 | /// the Operation itself in reverse order. That is for an Operation with 3 |
45 | /// results we allocate the following memory layout: |
46 | /// |
47 | /// [Result2, Result1, Result0, Operation] |
48 | /// ^ this is where `Operation*` pointer points to. |
49 | /// |
50 | /// A consequence of this is that this class must be heap allocated, which is |
51 | /// handled by the various `create` methods. Each result contains: |
52 | /// - one pointer to the first use (see `OpOperand`) |
53 | /// - the type of the SSA Value this result defines. |
54 | /// - the index for this result in the array. |
55 | /// The results are defined as subclass of `ValueImpl`, and more precisely as |
56 | /// the only two subclasses of `OpResultImpl`: `InlineOpResult` and |
57 | /// `OutOfLineOpResult`. The former is used for the first 5 results and the |
58 | /// latter for the subsequent ones. They differ in how they store their index: |
59 | /// the first 5 results only need 3 bits and thus are packed with the Type |
60 | /// pointer, while the subsequent one have an extra `unsigned` value and thus |
61 | /// need more space. |
62 | /// |
63 | /// An Operation also has zero or more operands: these are uses of SSA Value, |
64 | /// which can be the results of other operations or Block arguments. Each of |
65 | /// these uses is an instance of `OpOperand`. This optional array is initially |
66 | /// tail allocated with the operation class itself, but can be dynamically moved |
67 | /// out-of-line in a dynamic allocation as needed. |
68 | /// |
69 | /// An Operation may contain optionally one or multiple Regions, stored in a |
70 | /// tail allocated array. Each `Region` is a list of Blocks. Each `Block` is |
71 | /// itself a list of Operations. This structure is effectively forming a tree. |
72 | /// |
73 | /// Some operations like branches also refer to other Block, in which case they |
74 | /// would have an array of `BlockOperand`. |
75 | /// |
76 | /// An Operation may contain optionally a "Properties" object: this is a |
77 | /// pre-defined C++ object with a fixed size. This object is owned by the |
78 | /// operation and deleted with the operation. It can be converted to an |
79 | /// Attribute on demand, or loaded from an Attribute. |
80 | /// |
81 | /// |
82 | /// Finally an Operation also contain an optional `DictionaryAttr`, a Location, |
83 | /// and a pointer to its parent Block (if any). |
84 | class alignas(8) Operation final |
85 | : public llvm::ilist_node_with_parent<Operation, Block>, |
86 | private llvm::TrailingObjects<Operation, detail::OperandStorage, |
87 | detail::OpProperties, BlockOperand, Region, |
88 | OpOperand> { |
89 | public: |
90 | /// Create a new Operation with the specific fields. This constructor |
91 | /// populates the provided attribute list with default attributes if |
92 | /// necessary. |
93 | static Operation *create(Location location, OperationName name, |
94 | TypeRange resultTypes, ValueRange operands, |
95 | NamedAttrList &&attributes, |
96 | OpaqueProperties properties, BlockRange successors, |
97 | unsigned numRegions); |
98 | |
99 | /// Create a new Operation with the specific fields. This constructor uses an |
100 | /// existing attribute dictionary to avoid uniquing a list of attributes. |
101 | static Operation *create(Location location, OperationName name, |
102 | TypeRange resultTypes, ValueRange operands, |
103 | DictionaryAttr attributes, |
104 | OpaqueProperties properties, BlockRange successors, |
105 | unsigned numRegions); |
106 | |
107 | /// Create a new Operation from the fields stored in `state`. |
108 | static Operation *create(const OperationState &state); |
109 | |
110 | /// Create a new Operation with the specific fields. |
111 | static Operation *create(Location location, OperationName name, |
112 | TypeRange resultTypes, ValueRange operands, |
113 | NamedAttrList &&attributes, |
114 | OpaqueProperties properties, |
115 | BlockRange successors = {}, |
116 | RegionRange regions = {}); |
117 | |
118 | /// The name of an operation is the key identifier for it. |
119 | OperationName getName() { return name; } |
120 | |
121 | /// If this operation has a registered operation description, return it. |
122 | /// Otherwise return std::nullopt. |
123 | std::optional<RegisteredOperationName> getRegisteredInfo() { |
124 | return getName().getRegisteredInfo(); |
125 | } |
126 | |
127 | /// Returns true if this operation has a registered operation description, |
128 | /// otherwise false. |
129 | bool isRegistered() { return getName().isRegistered(); } |
130 | |
131 | /// Remove this operation from its parent block and delete it. |
132 | void erase(); |
133 | |
134 | /// Remove the operation from its parent block, but don't delete it. |
135 | void remove(); |
136 | |
137 | /// Class encompassing various options related to cloning an operation. Users |
138 | /// of this class should pass it to Operation's 'clone' methods. |
139 | /// Current options include: |
140 | /// * Whether cloning should recursively traverse into the regions of the |
141 | /// operation or not. |
142 | /// * Whether cloning should also clone the operands of the operation. |
143 | class CloneOptions { |
144 | public: |
145 | /// Default constructs an option with all flags set to false. That means all |
146 | /// parts of an operation that may optionally not be cloned, are not cloned. |
147 | CloneOptions(); |
148 | |
149 | /// Constructs an instance with the clone regions and clone operands flags |
150 | /// set accordingly. |
151 | CloneOptions(bool cloneRegions, bool cloneOperands); |
152 | |
153 | /// Returns an instance with all flags set to true. This is the default |
154 | /// when using the clone method and clones all parts of the operation. |
155 | static CloneOptions all(); |
156 | |
157 | /// Configures whether cloning should traverse into any of the regions of |
158 | /// the operation. If set to true, the operation's regions are recursively |
159 | /// cloned. If set to false, cloned operations will have the same number of |
160 | /// regions, but they will be empty. |
161 | /// Cloning of nested operations in the operation's regions are currently |
162 | /// unaffected by other flags. |
163 | CloneOptions &cloneRegions(bool enable = true); |
164 | |
165 | /// Returns whether regions of the operation should be cloned as well. |
166 | bool shouldCloneRegions() const { return cloneRegionsFlag; } |
167 | |
168 | /// Configures whether operation' operands should be cloned. Otherwise the |
169 | /// resulting clones will simply have zero operands. |
170 | CloneOptions &cloneOperands(bool enable = true); |
171 | |
172 | /// Returns whether operands should be cloned as well. |
173 | bool shouldCloneOperands() const { return cloneOperandsFlag; } |
174 | |
175 | private: |
176 | /// Whether regions should be cloned. |
177 | bool cloneRegionsFlag : 1; |
178 | /// Whether operands should be cloned. |
179 | bool cloneOperandsFlag : 1; |
180 | }; |
181 | |
182 | /// Create a deep copy of this operation, remapping any operands that use |
183 | /// values outside of the operation using the map that is provided (leaving |
184 | /// them alone if no entry is present). Replaces references to cloned |
185 | /// sub-operations to the corresponding operation that is copied, and adds |
186 | /// those mappings to the map. |
187 | /// Optionally, one may configure what parts of the operation to clone using |
188 | /// the options parameter. |
189 | /// |
190 | /// Calling this method from multiple threads is generally safe if through the |
191 | /// process of cloning no new uses of 'Value's from outside the operation are |
192 | /// created. Cloning an isolated-from-above operation with no operands, such |
193 | /// as top level function operations, is therefore always safe. Using the |
194 | /// mapper, it is possible to avoid adding uses to outside operands by |
195 | /// remapping them to 'Value's owned by the caller thread. |
196 | Operation *clone(IRMapping &mapper, |
197 | CloneOptions options = CloneOptions::all()); |
198 | Operation *clone(CloneOptions options = CloneOptions::all()); |
199 | |
200 | /// Create a partial copy of this operation without traversing into attached |
201 | /// regions. The new operation will have the same number of regions as the |
202 | /// original one, but they will be left empty. |
203 | /// Operands are remapped using `mapper` (if present), and `mapper` is updated |
204 | /// to contain the results. |
205 | Operation *cloneWithoutRegions(IRMapping &mapper); |
206 | |
207 | /// Create a partial copy of this operation without traversing into attached |
208 | /// regions. The new operation will have the same number of regions as the |
209 | /// original one, but they will be left empty. |
210 | Operation *cloneWithoutRegions(); |
211 | |
212 | /// Returns the operation block that contains this operation. |
213 | Block *getBlock() { return block; } |
214 | |
215 | /// Return the context this operation is associated with. |
216 | MLIRContext *getContext() { return location->getContext(); } |
217 | |
218 | /// Return the dialect this operation is associated with, or nullptr if the |
219 | /// associated dialect is not loaded. |
220 | Dialect *getDialect() { return getName().getDialect(); } |
221 | |
222 | /// The source location the operation was defined or derived from. |
223 | Location getLoc() { return location; } |
224 | |
225 | /// Set the source location the operation was defined or derived from. |
226 | void setLoc(Location loc) { location = loc; } |
227 | |
228 | /// Returns the region to which the instruction belongs. Returns nullptr if |
229 | /// the instruction is unlinked. |
230 | Region *getParentRegion() { return block ? block->getParent() : nullptr; } |
231 | |
232 | /// Returns the closest surrounding operation that contains this operation |
233 | /// or nullptr if this is a top-level operation. |
234 | Operation *getParentOp() { return block ? block->getParentOp() : nullptr; } |
235 | |
236 | /// Return the closest surrounding parent operation that is of type 'OpTy'. |
237 | template <typename OpTy> |
238 | OpTy getParentOfType() { |
239 | auto *op = this; |
240 | while ((op = op->getParentOp())) |
241 | if (auto parentOp = dyn_cast<OpTy>(op)) |
242 | return parentOp; |
243 | return OpTy(); |
244 | } |
245 | |
246 | /// Returns the closest surrounding parent operation with trait `Trait`. |
247 | template <template <typename T> class Trait> |
248 | Operation *getParentWithTrait() { |
249 | Operation *op = this; |
250 | while ((op = op->getParentOp())) |
251 | if (op->hasTrait<Trait>()) |
252 | return op; |
253 | return nullptr; |
254 | } |
255 | |
256 | /// Return true if this operation is a proper ancestor of the `other` |
257 | /// operation. |
258 | bool isProperAncestor(Operation *other); |
259 | |
260 | /// Return true if this operation is an ancestor of the `other` operation. An |
261 | /// operation is considered as its own ancestor, use `isProperAncestor` to |
262 | /// avoid this. |
263 | bool isAncestor(Operation *other) { |
264 | return this == other || isProperAncestor(other); |
265 | } |
266 | |
267 | /// Replace any uses of 'from' with 'to' within this operation. |
268 | void replaceUsesOfWith(Value from, Value to); |
269 | |
270 | /// Replace all uses of results of this operation with the provided 'values'. |
271 | template <typename ValuesT> |
272 | void replaceAllUsesWith(ValuesT &&values) { |
273 | getResults().replaceAllUsesWith(std::forward<ValuesT>(values)); |
274 | } |
275 | |
276 | /// Replace uses of results of this operation with the provided `values` if |
277 | /// the given callback returns true. |
278 | template <typename ValuesT> |
279 | void replaceUsesWithIf(ValuesT &&values, |
280 | function_ref<bool(OpOperand &)> shouldReplace) { |
281 | getResults().replaceUsesWithIf(std::forward<ValuesT>(values), |
282 | shouldReplace); |
283 | } |
284 | |
285 | /// Destroys this operation and its subclass data. |
286 | void destroy(); |
287 | |
288 | /// This drops all operand uses from this operation, which is an essential |
289 | /// step in breaking cyclic dependences between references when they are to |
290 | /// be deleted. |
291 | void dropAllReferences(); |
292 | |
293 | /// Drop uses of all values defined by this operation or its nested regions. |
294 | void dropAllDefinedValueUses(); |
295 | |
296 | /// Unlink this operation from its current block and insert it right before |
297 | /// `existingOp` which may be in the same or another block in the same |
298 | /// function. |
299 | void moveBefore(Operation *existingOp); |
300 | |
301 | /// Unlink this operation from its current block and insert it right before |
302 | /// `iterator` in the specified block. |
303 | void moveBefore(Block *block, llvm::iplist<Operation>::iterator iterator); |
304 | |
305 | /// Unlink this operation from its current block and insert it right after |
306 | /// `existingOp` which may be in the same or another block in the same |
307 | /// function. |
308 | void moveAfter(Operation *existingOp); |
309 | |
310 | /// Unlink this operation from its current block and insert it right after |
311 | /// `iterator` in the specified block. |
312 | void moveAfter(Block *block, llvm::iplist<Operation>::iterator iterator); |
313 | |
314 | /// Given an operation 'other' that is within the same parent block, return |
315 | /// whether the current operation is before 'other' in the operation list |
316 | /// of the parent block. |
317 | /// Note: This function has an average complexity of O(1), but worst case may |
318 | /// take O(N) where N is the number of operations within the parent block. |
319 | bool isBeforeInBlock(Operation *other); |
320 | |
321 | void print(raw_ostream &os, const OpPrintingFlags &flags = std::nullopt); |
322 | void print(raw_ostream &os, AsmState &state); |
323 | void dump(); |
324 | |
325 | // Dump pretty printed IR. This method is helpful for better readability if |
326 | // the Operation is not verified because it won't disable custom printers to |
327 | // fall back to the generic one. |
328 | LLVM_DUMP_METHOD void dumpPretty(); |
329 | |
330 | //===--------------------------------------------------------------------===// |
331 | // Operands |
332 | //===--------------------------------------------------------------------===// |
333 | |
334 | /// Replace the current operands of this operation with the ones provided in |
335 | /// 'operands'. |
336 | void setOperands(ValueRange operands); |
337 | |
338 | /// Replace the operands beginning at 'start' and ending at 'start' + 'length' |
339 | /// with the ones provided in 'operands'. 'operands' may be smaller or larger |
340 | /// than the range pointed to by 'start'+'length'. |
341 | void setOperands(unsigned start, unsigned length, ValueRange operands); |
342 | |
343 | /// Insert the given operands into the operand list at the given 'index'. |
344 | void insertOperands(unsigned index, ValueRange operands); |
345 | |
346 | unsigned getNumOperands() { |
347 | return LLVM_LIKELY(hasOperandStorage) ? getOperandStorage().size() : 0; |
348 | } |
349 | |
350 | Value getOperand(unsigned idx) { return getOpOperand(idx).get(); } |
351 | void setOperand(unsigned idx, Value value) { |
352 | return getOpOperand(idx).set(value); |
353 | } |
354 | |
355 | /// Erase the operand at position `idx`. |
356 | void eraseOperand(unsigned idx) { eraseOperands(idx); } |
357 | |
358 | /// Erase the operands starting at position `idx` and ending at position |
359 | /// 'idx'+'length'. |
360 | void eraseOperands(unsigned idx, unsigned length = 1) { |
361 | getOperandStorage().eraseOperands(start: idx, length); |
362 | } |
363 | |
364 | /// Erases the operands that have their corresponding bit set in |
365 | /// `eraseIndices` and removes them from the operand list. |
366 | void eraseOperands(const BitVector &eraseIndices) { |
367 | getOperandStorage().eraseOperands(eraseIndices); |
368 | } |
369 | |
370 | // Support operand iteration. |
371 | using operand_range = OperandRange; |
372 | using operand_iterator = operand_range::iterator; |
373 | |
374 | operand_iterator operand_begin() { return getOperands().begin(); } |
375 | operand_iterator operand_end() { return getOperands().end(); } |
376 | |
377 | /// Returns an iterator on the underlying Value's. |
378 | operand_range getOperands() { |
379 | MutableArrayRef<OpOperand> operands = getOpOperands(); |
380 | return OperandRange(operands.data(), operands.size()); |
381 | } |
382 | |
383 | MutableArrayRef<OpOperand> getOpOperands() { |
384 | return LLVM_LIKELY(hasOperandStorage) ? getOperandStorage().getOperands() |
385 | : MutableArrayRef<OpOperand>(); |
386 | } |
387 | |
388 | OpOperand &getOpOperand(unsigned idx) { |
389 | return getOperandStorage().getOperands()[idx]; |
390 | } |
391 | |
392 | // Support operand type iteration. |
393 | using operand_type_iterator = operand_range::type_iterator; |
394 | using operand_type_range = operand_range::type_range; |
395 | operand_type_iterator operand_type_begin() { return operand_begin(); } |
396 | operand_type_iterator operand_type_end() { return operand_end(); } |
397 | operand_type_range getOperandTypes() { return getOperands().getTypes(); } |
398 | |
399 | //===--------------------------------------------------------------------===// |
400 | // Results |
401 | //===--------------------------------------------------------------------===// |
402 | |
403 | /// Return the number of results held by this operation. |
404 | unsigned getNumResults() { return numResults; } |
405 | |
406 | /// Get the 'idx'th result of this operation. |
407 | OpResult getResult(unsigned idx) { return OpResult(getOpResultImpl(resultNumber: idx)); } |
408 | |
409 | /// Support result iteration. |
410 | using result_range = ResultRange; |
411 | using result_iterator = result_range::iterator; |
412 | |
413 | result_iterator result_begin() { return getResults().begin(); } |
414 | result_iterator result_end() { return getResults().end(); } |
415 | result_range getResults() { |
416 | return numResults == 0 ? result_range(nullptr, 0) |
417 | : result_range(getInlineOpResult(resultNumber: 0), numResults); |
418 | } |
419 | |
420 | result_range getOpResults() { return getResults(); } |
421 | OpResult getOpResult(unsigned idx) { return getResult(idx); } |
422 | |
423 | /// Support result type iteration. |
424 | using result_type_iterator = result_range::type_iterator; |
425 | using result_type_range = result_range::type_range; |
426 | result_type_iterator result_type_begin() { return getResultTypes().begin(); } |
427 | result_type_iterator result_type_end() { return getResultTypes().end(); } |
428 | result_type_range getResultTypes() { return getResults().getTypes(); } |
429 | |
430 | //===--------------------------------------------------------------------===// |
431 | // Attributes |
432 | //===--------------------------------------------------------------------===// |
433 | |
434 | // Operations may optionally carry a list of attributes that associate |
435 | // constants to names. Attributes may be dynamically added and removed over |
436 | // the lifetime of an operation. |
437 | |
438 | /// Access an inherent attribute by name: returns an empty optional if there |
439 | /// is no inherent attribute with this name. |
440 | /// |
441 | /// This method is available as a transient facility in the migration process |
442 | /// to use Properties instead. |
443 | std::optional<Attribute> getInherentAttr(StringRef name); |
444 | |
445 | /// Set an inherent attribute by name. |
446 | /// |
447 | /// This method is available as a transient facility in the migration process |
448 | /// to use Properties instead. |
449 | void setInherentAttr(StringAttr name, Attribute value); |
450 | |
451 | /// Access a discardable attribute by name, returns an null Attribute if the |
452 | /// discardable attribute does not exist. |
453 | Attribute getDiscardableAttr(StringRef name) { return attrs.get(name); } |
454 | |
455 | /// Access a discardable attribute by name, returns an null Attribute if the |
456 | /// discardable attribute does not exist. |
457 | Attribute getDiscardableAttr(StringAttr name) { return attrs.get(name); } |
458 | |
459 | /// Set a discardable attribute by name. |
460 | void setDiscardableAttr(StringAttr name, Attribute value) { |
461 | NamedAttrList attributes(attrs); |
462 | if (attributes.set(name, value) != value) |
463 | attrs = attributes.getDictionary(getContext()); |
464 | } |
465 | void setDiscardableAttr(StringRef name, Attribute value) { |
466 | setDiscardableAttr(StringAttr::get(getContext(), name), value); |
467 | } |
468 | |
469 | /// Remove the discardable attribute with the specified name if it exists. |
470 | /// Return the attribute that was erased, or nullptr if there was no attribute |
471 | /// with such name. |
472 | Attribute removeDiscardableAttr(StringAttr name) { |
473 | NamedAttrList attributes(attrs); |
474 | Attribute removedAttr = attributes.erase(name); |
475 | if (removedAttr) |
476 | attrs = attributes.getDictionary(getContext()); |
477 | return removedAttr; |
478 | } |
479 | Attribute removeDiscardableAttr(StringRef name) { |
480 | return removeDiscardableAttr(StringAttr::get(getContext(), name)); |
481 | } |
482 | |
483 | /// Return a range of all of discardable attributes on this operation. Note |
484 | /// that for unregistered operations that are not storing inherent attributes |
485 | /// as properties, all attributes are considered discardable. |
486 | auto getDiscardableAttrs() { |
487 | std::optional<RegisteredOperationName> opName = getRegisteredInfo(); |
488 | ArrayRef<StringAttr> attributeNames = |
489 | opName ? getRegisteredInfo()->getAttributeNames() |
490 | : ArrayRef<StringAttr>(); |
491 | return llvm::make_filter_range( |
492 | attrs.getValue(), |
493 | [this, attributeNames](const NamedAttribute attribute) { |
494 | return getPropertiesStorage() || |
495 | !llvm::is_contained(attributeNames, attribute.getName()); |
496 | }); |
497 | } |
498 | |
499 | /// Return all of the discardable attributes on this operation as a |
500 | /// DictionaryAttr. |
501 | DictionaryAttr getDiscardableAttrDictionary() { |
502 | if (getPropertiesStorage()) |
503 | return attrs; |
504 | return DictionaryAttr::get(getContext(), |
505 | llvm::to_vector(getDiscardableAttrs())); |
506 | } |
507 | |
508 | /// Return all attributes that are not stored as properties. |
509 | DictionaryAttr getRawDictionaryAttrs() { return attrs; } |
510 | |
511 | /// Return all of the attributes on this operation. |
512 | ArrayRef<NamedAttribute> getAttrs() { return getAttrDictionary().getValue(); } |
513 | |
514 | /// Return all of the attributes on this operation as a DictionaryAttr. |
515 | DictionaryAttr getAttrDictionary(); |
516 | |
517 | /// Set the attributes from a dictionary on this operation. |
518 | /// These methods are expensive: if the dictionnary only contains discardable |
519 | /// attributes, `setDiscardableAttrs` is more efficient. |
520 | void setAttrs(DictionaryAttr newAttrs); |
521 | void setAttrs(ArrayRef<NamedAttribute> newAttrs); |
522 | /// Set the discardable attribute dictionary on this operation. |
523 | void setDiscardableAttrs(DictionaryAttr newAttrs) { |
524 | assert(newAttrs && "expected valid attribute dictionary"); |
525 | attrs = newAttrs; |
526 | } |
527 | void setDiscardableAttrs(ArrayRef<NamedAttribute> newAttrs) { |
528 | setDiscardableAttrs(DictionaryAttr::get(getContext(), newAttrs)); |
529 | } |
530 | |
531 | /// Return the specified attribute if present, null otherwise. |
532 | /// These methods are expensive: if the dictionnary only contains discardable |
533 | /// attributes, `getDiscardableAttr` is more efficient. |
534 | Attribute getAttr(StringAttr name) { |
535 | if (getPropertiesStorageSize()) { |
536 | if (std::optional<Attribute> inherentAttr = getInherentAttr(name: name)) |
537 | return *inherentAttr; |
538 | } |
539 | return attrs.get(name); |
540 | } |
541 | Attribute getAttr(StringRef name) { |
542 | if (getPropertiesStorageSize()) { |
543 | if (std::optional<Attribute> inherentAttr = getInherentAttr(name)) |
544 | return *inherentAttr; |
545 | } |
546 | return attrs.get(name); |
547 | } |
548 | |
549 | template <typename AttrClass> |
550 | AttrClass getAttrOfType(StringAttr name) { |
551 | return llvm::dyn_cast_or_null<AttrClass>(getAttr(name)); |
552 | } |
553 | template <typename AttrClass> |
554 | AttrClass getAttrOfType(StringRef name) { |
555 | return llvm::dyn_cast_or_null<AttrClass>(getAttr(name)); |
556 | } |
557 | |
558 | /// Return true if the operation has an attribute with the provided name, |
559 | /// false otherwise. |
560 | bool hasAttr(StringAttr name) { |
561 | if (getPropertiesStorageSize()) { |
562 | if (std::optional<Attribute> inherentAttr = getInherentAttr(name: name)) |
563 | return (bool)*inherentAttr; |
564 | } |
565 | return attrs.contains(name); |
566 | } |
567 | bool hasAttr(StringRef name) { |
568 | if (getPropertiesStorageSize()) { |
569 | if (std::optional<Attribute> inherentAttr = getInherentAttr(name)) |
570 | return (bool)*inherentAttr; |
571 | } |
572 | return attrs.contains(name); |
573 | } |
574 | template <typename AttrClass, typename NameT> |
575 | bool hasAttrOfType(NameT &&name) { |
576 | return static_cast<bool>( |
577 | getAttrOfType<AttrClass>(std::forward<NameT>(name))); |
578 | } |
579 | |
580 | /// If the an attribute exists with the specified name, change it to the new |
581 | /// value. Otherwise, add a new attribute with the specified name/value. |
582 | void setAttr(StringAttr name, Attribute value) { |
583 | if (getPropertiesStorageSize()) { |
584 | if (getInherentAttr(name: name)) { |
585 | setInherentAttr(name: name, value); |
586 | return; |
587 | } |
588 | } |
589 | NamedAttrList attributes(attrs); |
590 | if (attributes.set(name, value) != value) |
591 | attrs = attributes.getDictionary(getContext()); |
592 | } |
593 | void setAttr(StringRef name, Attribute value) { |
594 | setAttr(StringAttr::get(getContext(), name), value); |
595 | } |
596 | |
597 | /// Remove the attribute with the specified name if it exists. Return the |
598 | /// attribute that was erased, or nullptr if there was no attribute with such |
599 | /// name. |
600 | Attribute removeAttr(StringAttr name) { |
601 | if (getPropertiesStorageSize()) { |
602 | if (std::optional<Attribute> inherentAttr = getInherentAttr(name: name)) { |
603 | setInherentAttr(name: name, value: {}); |
604 | return *inherentAttr; |
605 | } |
606 | } |
607 | NamedAttrList attributes(attrs); |
608 | Attribute removedAttr = attributes.erase(name); |
609 | if (removedAttr) |
610 | attrs = attributes.getDictionary(getContext()); |
611 | return removedAttr; |
612 | } |
613 | Attribute removeAttr(StringRef name) { |
614 | return removeAttr(StringAttr::get(getContext(), name)); |
615 | } |
616 | |
617 | /// A utility iterator that filters out non-dialect attributes. |
618 | class dialect_attr_iterator |
619 | : public llvm::filter_iterator<ArrayRef<NamedAttribute>::iterator, |
620 | bool (*)(NamedAttribute)> { |
621 | static bool filter(NamedAttribute attr) { |
622 | // Dialect attributes are prefixed by the dialect name, like operations. |
623 | return attr.getName().strref().count('.'); |
624 | } |
625 | |
626 | explicit dialect_attr_iterator(ArrayRef<NamedAttribute>::iterator it, |
627 | ArrayRef<NamedAttribute>::iterator end) |
628 | : llvm::filter_iterator<ArrayRef<NamedAttribute>::iterator, |
629 | bool (*)(NamedAttribute)>(it, end, &filter) {} |
630 | |
631 | // Allow access to the constructor. |
632 | friend Operation; |
633 | }; |
634 | using dialect_attr_range = iterator_range<dialect_attr_iterator>; |
635 | |
636 | /// Return a range corresponding to the dialect attributes for this operation. |
637 | dialect_attr_range getDialectAttrs() { |
638 | auto attrs = getAttrs(); |
639 | return {dialect_attr_iterator(attrs.begin(), attrs.end()), |
640 | dialect_attr_iterator(attrs.end(), attrs.end())}; |
641 | } |
642 | dialect_attr_iterator dialect_attr_begin() { |
643 | auto attrs = getAttrs(); |
644 | return dialect_attr_iterator(attrs.begin(), attrs.end()); |
645 | } |
646 | dialect_attr_iterator dialect_attr_end() { |
647 | auto attrs = getAttrs(); |
648 | return dialect_attr_iterator(attrs.end(), attrs.end()); |
649 | } |
650 | |
651 | /// Set the dialect attributes for this operation, and preserve all inherent. |
652 | template <typename DialectAttrT> |
653 | void setDialectAttrs(DialectAttrT &&dialectAttrs) { |
654 | NamedAttrList attrs; |
655 | attrs.append(std::begin(dialectAttrs), std::end(dialectAttrs)); |
656 | for (auto attr : getAttrs()) |
657 | if (!attr.getName().strref().contains('.')) |
658 | attrs.push_back(newAttribute: attr); |
659 | setAttrs(attrs.getDictionary(getContext())); |
660 | } |
661 | |
662 | /// Sets default attributes on unset attributes. |
663 | void populateDefaultAttrs() { |
664 | NamedAttrList attrs(getAttrDictionary()); |
665 | name.populateDefaultAttrs(attrs); |
666 | setAttrs(attrs.getDictionary(getContext())); |
667 | } |
668 | |
669 | //===--------------------------------------------------------------------===// |
670 | // Blocks |
671 | //===--------------------------------------------------------------------===// |
672 | |
673 | /// Returns the number of regions held by this operation. |
674 | unsigned getNumRegions() { return numRegions; } |
675 | |
676 | /// Returns the regions held by this operation. |
677 | MutableArrayRef<Region> getRegions() { |
678 | // Check the count first, as computing the trailing objects can be slow. |
679 | if (numRegions == 0) |
680 | return MutableArrayRef<Region>(); |
681 | |
682 | return getTrailingObjects<Region>(numRegions); |
683 | } |
684 | |
685 | /// Returns the region held by this operation at position 'index'. |
686 | Region &getRegion(unsigned index) { |
687 | assert(index < numRegions && "invalid region index"); |
688 | return getRegions()[index]; |
689 | } |
690 | |
691 | //===--------------------------------------------------------------------===// |
692 | // Successors |
693 | //===--------------------------------------------------------------------===// |
694 | |
695 | MutableArrayRef<BlockOperand> getBlockOperands() { |
696 | return getTrailingObjects<BlockOperand>(numSuccs); |
697 | } |
698 | |
699 | // Successor iteration. |
700 | using succ_iterator = SuccessorRange::iterator; |
701 | succ_iterator successor_begin() { return getSuccessors().begin(); } |
702 | succ_iterator successor_end() { return getSuccessors().end(); } |
703 | SuccessorRange getSuccessors() { return SuccessorRange(this); } |
704 | |
705 | bool hasSuccessors() { return numSuccs != 0; } |
706 | unsigned getNumSuccessors() { return numSuccs; } |
707 | |
708 | Block *getSuccessor(unsigned index) { |
709 | assert(index < getNumSuccessors()); |
710 | return getBlockOperands()[index].get(); |
711 | } |
712 | void setSuccessor(Block *block, unsigned index); |
713 | |
714 | //===--------------------------------------------------------------------===// |
715 | // Accessors for various properties of operations |
716 | //===--------------------------------------------------------------------===// |
717 | |
718 | /// Attempt to fold this operation with the specified constant operand values |
719 | /// - the elements in "operands" will correspond directly to the operands of |
720 | /// the operation, but may be null if non-constant. |
721 | /// |
722 | /// If folding was successful, this function returns "success". |
723 | /// * If this operation was modified in-place (but not folded away), |
724 | /// `results` is empty. |
725 | /// * Otherwise, `results` is filled with the folded results. |
726 | /// If folding was unsuccessful, this function returns "failure". |
727 | LogicalResult fold(ArrayRef<Attribute> operands, |
728 | SmallVectorImpl<OpFoldResult> &results); |
729 | |
730 | /// Attempt to fold this operation. |
731 | /// |
732 | /// If folding was successful, this function returns "success". |
733 | /// * If this operation was modified in-place (but not folded away), |
734 | /// `results` is empty. |
735 | /// * Otherwise, `results` is filled with the folded results. |
736 | /// If folding was unsuccessful, this function returns "failure". |
737 | LogicalResult fold(SmallVectorImpl<OpFoldResult> &results); |
738 | |
739 | /// Returns true if `InterfaceT` has been promised by the dialect or |
740 | /// implemented. |
741 | template <typename InterfaceT> |
742 | bool hasPromiseOrImplementsInterface() const { |
743 | return name.hasPromiseOrImplementsInterface<InterfaceT>(); |
744 | } |
745 | |
746 | /// Returns true if the operation was registered with a particular trait, e.g. |
747 | /// hasTrait<OperandsAreSignlessIntegerLike>(). |
748 | template <template <typename T> class Trait> |
749 | bool hasTrait() { |
750 | return name.hasTrait<Trait>(); |
751 | } |
752 | |
753 | /// Returns true if the operation *might* have the provided trait. This |
754 | /// means that either the operation is unregistered, or it was registered with |
755 | /// the provide trait. |
756 | template <template <typename T> class Trait> |
757 | bool mightHaveTrait() { |
758 | return name.mightHaveTrait<Trait>(); |
759 | } |
760 | |
761 | //===--------------------------------------------------------------------===// |
762 | // Operation Walkers |
763 | //===--------------------------------------------------------------------===// |
764 | |
765 | /// Walk the operation by calling the callback for each nested operation |
766 | /// (including this one), block or region, depending on the callback provided. |
767 | /// The order in which regions, blocks and operations at the same nesting |
768 | /// level are visited (e.g., lexicographical or reverse lexicographical order) |
769 | /// is determined by 'Iterator'. The walk order for enclosing regions, blocks |
770 | /// and operations with respect to their nested ones is specified by 'Order' |
771 | /// (post-order by default). A callback on a block or operation is allowed to |
772 | /// erase that block or operation if either: |
773 | /// * the walk is in post-order, or |
774 | /// * the walk is in pre-order and the walk is skipped after the erasure. |
775 | /// |
776 | /// The callback method can take any of the following forms: |
777 | /// void(Operation*) : Walk all operations opaquely. |
778 | /// * op->walk([](Operation *nestedOp) { ...}); |
779 | /// void(OpT) : Walk all operations of the given derived type. |
780 | /// * op->walk([](ReturnOp returnOp) { ...}); |
781 | /// WalkResult(Operation*|OpT) : Walk operations, but allow for |
782 | /// interruption/skipping. |
783 | /// * op->walk([](... op) { |
784 | /// // Skip the walk of this op based on some invariant. |
785 | /// if (some_invariant) |
786 | /// return WalkResult::skip(); |
787 | /// // Interrupt, i.e cancel, the walk based on some invariant. |
788 | /// if (another_invariant) |
789 | /// return WalkResult::interrupt(); |
790 | /// return WalkResult::advance(); |
791 | /// }); |
792 | template <WalkOrder Order = WalkOrder::PostOrder, |
793 | typename Iterator = ForwardIterator, typename FnT, |
794 | typename RetT = detail::walkResultType<FnT>> |
795 | std::enable_if_t<llvm::function_traits<std::decay_t<FnT>>::num_args == 1, |
796 | RetT> |
797 | walk(FnT &&callback) { |
798 | return detail::walk<Order, Iterator>(this, std::forward<FnT>(callback)); |
799 | } |
800 | |
801 | /// Generic walker with a stage aware callback. Walk the operation by calling |
802 | /// the callback for each nested operation (including this one) N+1 times, |
803 | /// where N is the number of regions attached to that operation. |
804 | /// |
805 | /// The callback method can take any of the following forms: |
806 | /// void(Operation *, const WalkStage &) : Walk all operation opaquely |
807 | /// * op->walk([](Operation *nestedOp, const WalkStage &stage) { ...}); |
808 | /// void(OpT, const WalkStage &) : Walk all operations of the given derived |
809 | /// type. |
810 | /// * op->walk([](ReturnOp returnOp, const WalkStage &stage) { ...}); |
811 | /// WalkResult(Operation*|OpT, const WalkStage &stage) : Walk operations, |
812 | /// but allow for interruption/skipping. |
813 | /// * op->walk([](... op, const WalkStage &stage) { |
814 | /// // Skip the walk of this op based on some invariant. |
815 | /// if (some_invariant) |
816 | /// return WalkResult::skip(); |
817 | /// // Interrupt, i.e cancel, the walk based on some invariant. |
818 | /// if (another_invariant) |
819 | /// return WalkResult::interrupt(); |
820 | /// return WalkResult::advance(); |
821 | /// }); |
822 | template <typename FnT, typename RetT = detail::walkResultType<FnT>> |
823 | std::enable_if_t<llvm::function_traits<std::decay_t<FnT>>::num_args == 2, |
824 | RetT> |
825 | walk(FnT &&callback) { |
826 | return detail::walk(this, std::forward<FnT>(callback)); |
827 | } |
828 | |
829 | //===--------------------------------------------------------------------===// |
830 | // Uses |
831 | //===--------------------------------------------------------------------===// |
832 | |
833 | /// Drop all uses of results of this operation. |
834 | void dropAllUses() { |
835 | for (OpResult result : getOpResults()) |
836 | result.dropAllUses(); |
837 | } |
838 | |
839 | using use_iterator = result_range::use_iterator; |
840 | using use_range = result_range::use_range; |
841 | |
842 | use_iterator use_begin() { return getResults().use_begin(); } |
843 | use_iterator use_end() { return getResults().use_end(); } |
844 | |
845 | /// Returns a range of all uses, which is useful for iterating over all uses. |
846 | use_range getUses() { return getResults().getUses(); } |
847 | |
848 | /// Returns true if this operation has exactly one use. |
849 | bool hasOneUse() { return llvm::hasSingleElement(C: getUses()); } |
850 | |
851 | /// Returns true if this operation has no uses. |
852 | bool use_empty() { return getResults().use_empty(); } |
853 | |
854 | /// Returns true if the results of this operation are used outside of the |
855 | /// given block. |
856 | bool isUsedOutsideOfBlock(Block *block) { |
857 | return llvm::any_of(Range: getOpResults(), P: [block](OpResult result) { |
858 | return result.isUsedOutsideOfBlock(block); |
859 | }); |
860 | } |
861 | |
862 | //===--------------------------------------------------------------------===// |
863 | // Users |
864 | //===--------------------------------------------------------------------===// |
865 | |
866 | using user_iterator = ValueUserIterator<use_iterator, OpOperand>; |
867 | using user_range = iterator_range<user_iterator>; |
868 | |
869 | user_iterator user_begin() { return user_iterator(use_begin()); } |
870 | user_iterator user_end() { return user_iterator(use_end()); } |
871 | |
872 | /// Returns a range of all users. |
873 | user_range getUsers() { return {user_begin(), user_end()}; } |
874 | |
875 | //===--------------------------------------------------------------------===// |
876 | // Other |
877 | //===--------------------------------------------------------------------===// |
878 | |
879 | /// Emit an error with the op name prefixed, like "'dim' op " which is |
880 | /// convenient for verifiers. |
881 | InFlightDiagnostic emitOpError(const Twine &message = {}); |
882 | |
883 | /// Emit an error about fatal conditions with this operation, reporting up to |
884 | /// any diagnostic handlers that may be listening. |
885 | InFlightDiagnostic emitError(const Twine &message = {}); |
886 | |
887 | /// Emit a warning about this operation, reporting up to any diagnostic |
888 | /// handlers that may be listening. |
889 | InFlightDiagnostic emitWarning(const Twine &message = {}); |
890 | |
891 | /// Emit a remark about this operation, reporting up to any diagnostic |
892 | /// handlers that may be listening. |
893 | InFlightDiagnostic emitRemark(const Twine &message = {}); |
894 | |
895 | /// Returns the properties storage size. |
896 | int getPropertiesStorageSize() const { |
897 | return ((int)propertiesStorageSize) * 8; |
898 | } |
899 | /// Returns the properties storage. |
900 | OpaqueProperties getPropertiesStorage() { |
901 | if (propertiesStorageSize) |
902 | return getPropertiesStorageUnsafe(); |
903 | return {nullptr}; |
904 | } |
905 | OpaqueProperties getPropertiesStorage() const { |
906 | if (propertiesStorageSize) |
907 | return {reinterpret_cast<void *>(const_cast<detail::OpProperties *>( |
908 | getTrailingObjects<detail::OpProperties>()))}; |
909 | return {nullptr}; |
910 | } |
911 | /// Returns the properties storage without checking whether properties are |
912 | /// present. |
913 | OpaqueProperties getPropertiesStorageUnsafe() { |
914 | return { |
915 | reinterpret_cast<void *>(getTrailingObjects<detail::OpProperties>())}; |
916 | } |
917 | |
918 | /// Return the properties converted to an attribute. |
919 | /// This is expensive, and mostly useful when dealing with unregistered |
920 | /// operation. Returns an empty attribute if no properties are present. |
921 | Attribute getPropertiesAsAttribute(); |
922 | |
923 | /// Set the properties from the provided attribute. |
924 | /// This is an expensive operation that can fail if the attribute is not |
925 | /// matching the expectations of the properties for this operation. This is |
926 | /// mostly useful for unregistered operations or used when parsing the |
927 | /// generic format. An optional diagnostic emitter can be passed in for richer |
928 | /// errors, if none is passed then behavior is undefined in error case. |
929 | LogicalResult |
930 | setPropertiesFromAttribute(Attribute attr, |
931 | function_ref<InFlightDiagnostic()> emitError); |
932 | |
933 | /// Copy properties from an existing other properties object. The two objects |
934 | /// must be the same type. |
935 | void copyProperties(OpaqueProperties rhs); |
936 | |
937 | /// Compute a hash for the op properties (if any). |
938 | llvm::hash_code hashProperties(); |
939 | |
940 | private: |
941 | //===--------------------------------------------------------------------===// |
942 | // Ordering |
943 | //===--------------------------------------------------------------------===// |
944 | |
945 | /// This value represents an invalid index ordering for an operation within a |
946 | /// block. |
947 | static constexpr unsigned kInvalidOrderIdx = -1; |
948 | |
949 | /// This value represents the stride to use when computing a new order for an |
950 | /// operation. |
951 | static constexpr unsigned kOrderStride = 5; |
952 | |
953 | /// Update the order index of this operation of this operation if necessary, |
954 | /// potentially recomputing the order of the parent block. |
955 | void updateOrderIfNecessary(); |
956 | |
957 | /// Returns true if this operation has a valid order. |
958 | bool hasValidOrder() { return orderIndex != kInvalidOrderIdx; } |
959 | |
960 | private: |
961 | Operation(Location location, OperationName name, unsigned numResults, |
962 | unsigned numSuccessors, unsigned numRegions, |
963 | int propertiesStorageSize, DictionaryAttr attributes, |
964 | OpaqueProperties properties, bool hasOperandStorage); |
965 | |
966 | // Operations are deleted through the destroy() member because they are |
967 | // allocated with malloc. |
968 | ~Operation(); |
969 | |
970 | /// Returns the additional size necessary for allocating the given objects |
971 | /// before an Operation in-memory. |
972 | static size_t prefixAllocSize(unsigned numOutOfLineResults, |
973 | unsigned numInlineResults) { |
974 | return sizeof(detail::OutOfLineOpResult) * numOutOfLineResults + |
975 | sizeof(detail::InlineOpResult) * numInlineResults; |
976 | } |
977 | /// Returns the additional size allocated before this Operation in-memory. |
978 | size_t prefixAllocSize() { |
979 | unsigned numResults = getNumResults(); |
980 | unsigned numOutOfLineResults = OpResult::getNumTrailing(numResults); |
981 | unsigned numInlineResults = OpResult::getNumInline(numResults); |
982 | return prefixAllocSize(numOutOfLineResults, numInlineResults); |
983 | } |
984 | |
985 | /// Returns the operand storage object. |
986 | detail::OperandStorage &getOperandStorage() { |
987 | assert(hasOperandStorage && "expected operation to have operand storage"); |
988 | return *getTrailingObjects<detail::OperandStorage>(); |
989 | } |
990 | |
991 | /// Returns a pointer to the use list for the given out-of-line result. |
992 | detail::OutOfLineOpResult *getOutOfLineOpResult(unsigned resultNumber) { |
993 | // Out-of-line results are stored in reverse order after (before in memory) |
994 | // the inline results. |
995 | return reinterpret_cast<detail::OutOfLineOpResult *>(getInlineOpResult( |
996 | resultNumber: detail::OpResultImpl::getMaxInlineResults() - 1)) - |
997 | ++resultNumber; |
998 | } |
999 | |
1000 | /// Returns a pointer to the use list for the given inline result. |
1001 | detail::InlineOpResult *getInlineOpResult(unsigned resultNumber) { |
1002 | // Inline results are stored in reverse order before the operation in |
1003 | // memory. |
1004 | return reinterpret_cast<detail::InlineOpResult *>(this) - ++resultNumber; |
1005 | } |
1006 | |
1007 | /// Returns a pointer to the use list for the given result, which may be |
1008 | /// either inline or out-of-line. |
1009 | detail::OpResultImpl *getOpResultImpl(unsigned resultNumber) { |
1010 | assert(resultNumber < getNumResults() && |
1011 | "Result number is out of range for operation"); |
1012 | unsigned maxInlineResults = detail::OpResultImpl::getMaxInlineResults(); |
1013 | if (resultNumber < maxInlineResults) |
1014 | return getInlineOpResult(resultNumber); |
1015 | return getOutOfLineOpResult(resultNumber: resultNumber - maxInlineResults); |
1016 | } |
1017 | |
1018 | /// Provide a 'getParent' method for ilist_node_with_parent methods. |
1019 | /// We mark it as a const function because ilist_node_with_parent specifically |
1020 | /// requires a 'getParent() const' method. Once ilist_node removes this |
1021 | /// constraint, we should drop the const to fit the rest of the MLIR const |
1022 | /// model. |
1023 | Block *getParent() const { return block; } |
1024 | |
1025 | /// Expose a few methods explicitly for the debugger to call for |
1026 | /// visualization. |
1027 | #ifndef NDEBUG |
1028 | LLVM_DUMP_METHOD operand_range debug_getOperands() { return getOperands(); } |
1029 | LLVM_DUMP_METHOD result_range debug_getResults() { return getResults(); } |
1030 | LLVM_DUMP_METHOD SuccessorRange debug_getSuccessors() { |
1031 | return getSuccessors(); |
1032 | } |
1033 | LLVM_DUMP_METHOD MutableArrayRef<Region> debug_getRegions() { |
1034 | return getRegions(); |
1035 | } |
1036 | #endif |
1037 | |
1038 | /// The operation block that contains this operation. |
1039 | Block *block = nullptr; |
1040 | |
1041 | /// This holds information about the source location the operation was defined |
1042 | /// or derived from. |
1043 | Location location; |
1044 | |
1045 | /// Relative order of this operation in its parent block. Used for |
1046 | /// O(1) local dominance checks between operations. |
1047 | mutable unsigned orderIndex = 0; |
1048 | |
1049 | const unsigned numResults; |
1050 | const unsigned numSuccs; |
1051 | const unsigned numRegions : 23; |
1052 | |
1053 | /// This bit signals whether this operation has an operand storage or not. The |
1054 | /// operand storage may be elided for operations that are known to never have |
1055 | /// operands. |
1056 | bool hasOperandStorage : 1; |
1057 | |
1058 | /// The size of the storage for properties (if any), divided by 8: since the |
1059 | /// Properties storage will always be rounded up to the next multiple of 8 we |
1060 | /// save some bits here. |
1061 | unsigned char propertiesStorageSize : 8; |
1062 | /// This is the maximum size we support to allocate properties inline with an |
1063 | /// operation: this must match the bitwidth above. |
1064 | static constexpr int64_t propertiesCapacity = 8 * 256; |
1065 | |
1066 | /// This holds the name of the operation. |
1067 | OperationName name; |
1068 | |
1069 | /// This holds general named attributes for the operation. |
1070 | DictionaryAttr attrs; |
1071 | |
1072 | // allow ilist_traits access to 'block' field. |
1073 | friend struct llvm::ilist_traits<Operation>; |
1074 | |
1075 | // allow block to access the 'orderIndex' field. |
1076 | friend class Block; |
1077 | |
1078 | // allow value to access the 'ResultStorage' methods. |
1079 | friend class Value; |
1080 | |
1081 | // allow ilist_node_with_parent to access the 'getParent' method. |
1082 | friend class llvm::ilist_node_with_parent<Operation, Block>; |
1083 | |
1084 | // This stuff is used by the TrailingObjects template. |
1085 | friend llvm::TrailingObjects<Operation, detail::OperandStorage, |
1086 | detail::OpProperties, BlockOperand, Region, |
1087 | OpOperand>; |
1088 | size_t numTrailingObjects(OverloadToken<detail::OperandStorage>) const { |
1089 | return hasOperandStorage ? 1 : 0; |
1090 | } |
1091 | size_t numTrailingObjects(OverloadToken<BlockOperand>) const { |
1092 | return numSuccs; |
1093 | } |
1094 | size_t numTrailingObjects(OverloadToken<Region>) const { return numRegions; } |
1095 | size_t numTrailingObjects(OverloadToken<detail::OpProperties>) const { |
1096 | return getPropertiesStorageSize(); |
1097 | } |
1098 | }; |
1099 | |
1100 | inline raw_ostream &operator<<(raw_ostream &os, const Operation &op) { |
1101 | const_cast<Operation &>(op).print(os, flags: OpPrintingFlags().useLocalScope()); |
1102 | return os; |
1103 | } |
1104 | |
1105 | } // namespace mlir |
1106 | |
1107 | namespace llvm { |
1108 | /// Cast from an (const) Operation * to a derived operation type. |
1109 | template <typename T> |
1110 | struct CastInfo<T, ::mlir::Operation *> |
1111 | : public ValueFromPointerCast<T, ::mlir::Operation, |
1112 | CastInfo<T, ::mlir::Operation *>> { |
1113 | static bool isPossible(::mlir::Operation *op) { return T::classof(op); } |
1114 | }; |
1115 | template <typename T> |
1116 | struct CastInfo<T, const ::mlir::Operation *> |
1117 | : public ConstStrippingForwardingCast<T, const ::mlir::Operation *, |
1118 | CastInfo<T, ::mlir::Operation *>> {}; |
1119 | |
1120 | /// Cast from an (const) Operation & to a derived operation type. |
1121 | template <typename T> |
1122 | struct CastInfo<T, ::mlir::Operation> |
1123 | : public NullableValueCastFailed<T>, |
1124 | public DefaultDoCastIfPossible<T, ::mlir::Operation &, |
1125 | CastInfo<T, ::mlir::Operation>> { |
1126 | // Provide isPossible here because here we have the const-stripping from |
1127 | // ConstStrippingCast. |
1128 | static bool isPossible(::mlir::Operation &val) { return T::classof(&val); } |
1129 | static T doCast(::mlir::Operation &val) { return T(&val); } |
1130 | }; |
1131 | template <typename T> |
1132 | struct CastInfo<T, const ::mlir::Operation> |
1133 | : public ConstStrippingForwardingCast<T, const ::mlir::Operation, |
1134 | CastInfo<T, ::mlir::Operation>> {}; |
1135 | |
1136 | /// Cast (const) Operation * to itself. This is helpful to avoid SFINAE in |
1137 | /// templated implementations that should work on both base and derived |
1138 | /// operation types. |
1139 | template <> |
1140 | struct CastInfo<::mlir::Operation *, ::mlir::Operation *> |
1141 | : public NullableValueCastFailed<::mlir::Operation *>, |
1142 | public DefaultDoCastIfPossible< |
1143 | ::mlir::Operation *, ::mlir::Operation *, |
1144 | CastInfo<::mlir::Operation *, ::mlir::Operation *>> { |
1145 | static bool isPossible(::mlir::Operation *op) { return true; } |
1146 | static ::mlir::Operation *doCast(::mlir::Operation *op) { return op; } |
1147 | }; |
1148 | template <> |
1149 | struct CastInfo<const ::mlir::Operation *, const ::mlir::Operation *> |
1150 | : public ConstStrippingForwardingCast< |
1151 | const ::mlir::Operation *, const ::mlir::Operation *, |
1152 | CastInfo<::mlir::Operation *, ::mlir::Operation *>> {}; |
1153 | } // namespace llvm |
1154 | |
1155 | #endif // MLIR_IR_OPERATION_H |
1156 |
Definitions
- OpProperties
- Operation
- getName
- getRegisteredInfo
- isRegistered
- CloneOptions
- shouldCloneRegions
- shouldCloneOperands
- getBlock
- getContext
- getDialect
- getLoc
- setLoc
- getParentRegion
- getParentOp
- getParentOfType
- getParentWithTrait
- isAncestor
- replaceAllUsesWith
- replaceUsesWithIf
- getNumOperands
- getOperand
- setOperand
- eraseOperand
- eraseOperands
- eraseOperands
- operand_begin
- operand_end
- getOperands
- getOpOperands
- getOpOperand
- operand_type_begin
- operand_type_end
- getOperandTypes
- getNumResults
- getResult
- result_begin
- result_end
- getResults
- getOpResults
- getOpResult
- result_type_begin
- result_type_end
- getResultTypes
- getDiscardableAttr
- getDiscardableAttr
- setDiscardableAttr
- setDiscardableAttr
- removeDiscardableAttr
- removeDiscardableAttr
- getDiscardableAttrs
- getDiscardableAttrDictionary
- getRawDictionaryAttrs
- getAttrs
- setDiscardableAttrs
- setDiscardableAttrs
- getAttr
- getAttr
- getAttrOfType
- getAttrOfType
- hasAttr
- hasAttr
- hasAttrOfType
- setAttr
- setAttr
- removeAttr
- removeAttr
- dialect_attr_iterator
- filter
- dialect_attr_iterator
- getDialectAttrs
- dialect_attr_begin
- dialect_attr_end
- setDialectAttrs
- populateDefaultAttrs
- getNumRegions
- getRegions
- getRegion
- getBlockOperands
- successor_begin
- successor_end
- getSuccessors
- hasSuccessors
- getNumSuccessors
- getSuccessor
- hasPromiseOrImplementsInterface
- hasTrait
- mightHaveTrait
- walk
- walk
- dropAllUses
- use_begin
- use_end
- getUses
- hasOneUse
- use_empty
- isUsedOutsideOfBlock
- user_begin
- user_end
- getUsers
- getPropertiesStorageSize
- getPropertiesStorage
- getPropertiesStorage
- getPropertiesStorageUnsafe
- kInvalidOrderIdx
- kOrderStride
- hasValidOrder
- prefixAllocSize
- prefixAllocSize
- getOperandStorage
- getOutOfLineOpResult
- getInlineOpResult
- getOpResultImpl
- getParent
- debug_getOperands
- debug_getResults
- debug_getSuccessors
- debug_getRegions
- propertiesCapacity
- numTrailingObjects
- numTrailingObjects
- numTrailingObjects
- numTrailingObjects
- operator<<
- CastInfo
- isPossible
- CastInfo
- CastInfo
- isPossible
- doCast
- CastInfo
- CastInfo
- isPossible
- doCast
Improve your Profiling and Debugging skills
Find out more