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 | //===--------------------------------------------------------------------===// |
326 | // Operands |
327 | //===--------------------------------------------------------------------===// |
328 | |
329 | /// Replace the current operands of this operation with the ones provided in |
330 | /// 'operands'. |
331 | void setOperands(ValueRange operands); |
332 | |
333 | /// Replace the operands beginning at 'start' and ending at 'start' + 'length' |
334 | /// with the ones provided in 'operands'. 'operands' may be smaller or larger |
335 | /// than the range pointed to by 'start'+'length'. |
336 | void setOperands(unsigned start, unsigned length, ValueRange operands); |
337 | |
338 | /// Insert the given operands into the operand list at the given 'index'. |
339 | void insertOperands(unsigned index, ValueRange operands); |
340 | |
341 | unsigned getNumOperands() { |
342 | return LLVM_LIKELY(hasOperandStorage) ? getOperandStorage().size() : 0; |
343 | } |
344 | |
345 | Value getOperand(unsigned idx) { return getOpOperand(idx).get(); } |
346 | void setOperand(unsigned idx, Value value) { |
347 | return getOpOperand(idx).set(value); |
348 | } |
349 | |
350 | /// Erase the operand at position `idx`. |
351 | void eraseOperand(unsigned idx) { eraseOperands(idx); } |
352 | |
353 | /// Erase the operands starting at position `idx` and ending at position |
354 | /// 'idx'+'length'. |
355 | void eraseOperands(unsigned idx, unsigned length = 1) { |
356 | getOperandStorage().eraseOperands(start: idx, length); |
357 | } |
358 | |
359 | /// Erases the operands that have their corresponding bit set in |
360 | /// `eraseIndices` and removes them from the operand list. |
361 | void eraseOperands(const BitVector &eraseIndices) { |
362 | getOperandStorage().eraseOperands(eraseIndices); |
363 | } |
364 | |
365 | // Support operand iteration. |
366 | using operand_range = OperandRange; |
367 | using operand_iterator = operand_range::iterator; |
368 | |
369 | operand_iterator operand_begin() { return getOperands().begin(); } |
370 | operand_iterator operand_end() { return getOperands().end(); } |
371 | |
372 | /// Returns an iterator on the underlying Value's. |
373 | operand_range getOperands() { |
374 | MutableArrayRef<OpOperand> operands = getOpOperands(); |
375 | return OperandRange(operands.data(), operands.size()); |
376 | } |
377 | |
378 | MutableArrayRef<OpOperand> getOpOperands() { |
379 | return LLVM_LIKELY(hasOperandStorage) ? getOperandStorage().getOperands() |
380 | : MutableArrayRef<OpOperand>(); |
381 | } |
382 | |
383 | OpOperand &getOpOperand(unsigned idx) { |
384 | return getOperandStorage().getOperands()[idx]; |
385 | } |
386 | |
387 | // Support operand type iteration. |
388 | using operand_type_iterator = operand_range::type_iterator; |
389 | using operand_type_range = operand_range::type_range; |
390 | operand_type_iterator operand_type_begin() { return operand_begin(); } |
391 | operand_type_iterator operand_type_end() { return operand_end(); } |
392 | operand_type_range getOperandTypes() { return getOperands().getTypes(); } |
393 | |
394 | //===--------------------------------------------------------------------===// |
395 | // Results |
396 | //===--------------------------------------------------------------------===// |
397 | |
398 | /// Return the number of results held by this operation. |
399 | unsigned getNumResults() { return numResults; } |
400 | |
401 | /// Get the 'idx'th result of this operation. |
402 | OpResult getResult(unsigned idx) { return OpResult(getOpResultImpl(resultNumber: idx)); } |
403 | |
404 | /// Support result iteration. |
405 | using result_range = ResultRange; |
406 | using result_iterator = result_range::iterator; |
407 | |
408 | result_iterator result_begin() { return getResults().begin(); } |
409 | result_iterator result_end() { return getResults().end(); } |
410 | result_range getResults() { |
411 | return numResults == 0 ? result_range(nullptr, 0) |
412 | : result_range(getInlineOpResult(resultNumber: 0), numResults); |
413 | } |
414 | |
415 | result_range getOpResults() { return getResults(); } |
416 | OpResult getOpResult(unsigned idx) { return getResult(idx); } |
417 | |
418 | /// Support result type iteration. |
419 | using result_type_iterator = result_range::type_iterator; |
420 | using result_type_range = result_range::type_range; |
421 | result_type_iterator result_type_begin() { return getResultTypes().begin(); } |
422 | result_type_iterator result_type_end() { return getResultTypes().end(); } |
423 | result_type_range getResultTypes() { return getResults().getTypes(); } |
424 | |
425 | //===--------------------------------------------------------------------===// |
426 | // Attributes |
427 | //===--------------------------------------------------------------------===// |
428 | |
429 | // Operations may optionally carry a list of attributes that associate |
430 | // constants to names. Attributes may be dynamically added and removed over |
431 | // the lifetime of an operation. |
432 | |
433 | /// Access an inherent attribute by name: returns an empty optional if there |
434 | /// is no inherent attribute with this name. |
435 | /// |
436 | /// This method is available as a transient facility in the migration process |
437 | /// to use Properties instead. |
438 | std::optional<Attribute> getInherentAttr(StringRef name); |
439 | |
440 | /// Set an inherent attribute by name. |
441 | /// |
442 | /// This method is available as a transient facility in the migration process |
443 | /// to use Properties instead. |
444 | void setInherentAttr(StringAttr name, Attribute value); |
445 | |
446 | /// Access a discardable attribute by name, returns an null Attribute if the |
447 | /// discardable attribute does not exist. |
448 | Attribute getDiscardableAttr(StringRef name) { return attrs.get(name); } |
449 | |
450 | /// Access a discardable attribute by name, returns an null Attribute if the |
451 | /// discardable attribute does not exist. |
452 | Attribute getDiscardableAttr(StringAttr name) { return attrs.get(name); } |
453 | |
454 | /// Set a discardable attribute by name. |
455 | void setDiscardableAttr(StringAttr name, Attribute value) { |
456 | NamedAttrList attributes(attrs); |
457 | if (attributes.set(name, value) != value) |
458 | attrs = attributes.getDictionary(getContext()); |
459 | } |
460 | void setDiscardableAttr(StringRef name, Attribute value) { |
461 | setDiscardableAttr(StringAttr::get(getContext(), name), value); |
462 | } |
463 | |
464 | /// Remove the discardable attribute with the specified name if it exists. |
465 | /// Return the attribute that was erased, or nullptr if there was no attribute |
466 | /// with such name. |
467 | Attribute removeDiscardableAttr(StringAttr name) { |
468 | NamedAttrList attributes(attrs); |
469 | Attribute removedAttr = attributes.erase(name); |
470 | if (removedAttr) |
471 | attrs = attributes.getDictionary(getContext()); |
472 | return removedAttr; |
473 | } |
474 | Attribute removeDiscardableAttr(StringRef name) { |
475 | return removeDiscardableAttr(StringAttr::get(getContext(), name)); |
476 | } |
477 | |
478 | /// Return a range of all of discardable attributes on this operation. Note |
479 | /// that for unregistered operations that are not storing inherent attributes |
480 | /// as properties, all attributes are considered discardable. |
481 | auto getDiscardableAttrs() { |
482 | std::optional<RegisteredOperationName> opName = getRegisteredInfo(); |
483 | ArrayRef<StringAttr> attributeNames = |
484 | opName ? getRegisteredInfo()->getAttributeNames() |
485 | : ArrayRef<StringAttr>(); |
486 | return llvm::make_filter_range( |
487 | attrs.getValue(), |
488 | [this, attributeNames](const NamedAttribute attribute) { |
489 | return getPropertiesStorage() || |
490 | !llvm::is_contained(attributeNames, attribute.getName()); |
491 | }); |
492 | } |
493 | |
494 | /// Return all of the discardable attributes on this operation as a |
495 | /// DictionaryAttr. |
496 | DictionaryAttr getDiscardableAttrDictionary() { |
497 | if (getPropertiesStorage()) |
498 | return attrs; |
499 | return DictionaryAttr::get(getContext(), |
500 | llvm::to_vector(getDiscardableAttrs())); |
501 | } |
502 | |
503 | /// Return all attributes that are not stored as properties. |
504 | DictionaryAttr getRawDictionaryAttrs() { return attrs; } |
505 | |
506 | /// Return all of the attributes on this operation. |
507 | ArrayRef<NamedAttribute> getAttrs() { return getAttrDictionary().getValue(); } |
508 | |
509 | /// Return all of the attributes on this operation as a DictionaryAttr. |
510 | DictionaryAttr getAttrDictionary(); |
511 | |
512 | /// Set the attributes from a dictionary on this operation. |
513 | /// These methods are expensive: if the dictionnary only contains discardable |
514 | /// attributes, `setDiscardableAttrs` is more efficient. |
515 | void setAttrs(DictionaryAttr newAttrs); |
516 | void setAttrs(ArrayRef<NamedAttribute> newAttrs); |
517 | /// Set the discardable attribute dictionary on this operation. |
518 | void setDiscardableAttrs(DictionaryAttr newAttrs) { |
519 | assert(newAttrs && "expected valid attribute dictionary" ); |
520 | attrs = newAttrs; |
521 | } |
522 | void setDiscardableAttrs(ArrayRef<NamedAttribute> newAttrs) { |
523 | setDiscardableAttrs(DictionaryAttr::get(getContext(), newAttrs)); |
524 | } |
525 | |
526 | /// Return the specified attribute if present, null otherwise. |
527 | /// These methods are expensive: if the dictionnary only contains discardable |
528 | /// attributes, `getDiscardableAttr` is more efficient. |
529 | Attribute getAttr(StringAttr name) { |
530 | if (getPropertiesStorageSize()) { |
531 | if (std::optional<Attribute> inherentAttr = getInherentAttr(name: name)) |
532 | return *inherentAttr; |
533 | } |
534 | return attrs.get(name); |
535 | } |
536 | Attribute getAttr(StringRef name) { |
537 | if (getPropertiesStorageSize()) { |
538 | if (std::optional<Attribute> inherentAttr = getInherentAttr(name)) |
539 | return *inherentAttr; |
540 | } |
541 | return attrs.get(name); |
542 | } |
543 | |
544 | template <typename AttrClass> |
545 | AttrClass getAttrOfType(StringAttr name) { |
546 | return llvm::dyn_cast_or_null<AttrClass>(getAttr(name)); |
547 | } |
548 | template <typename AttrClass> |
549 | AttrClass getAttrOfType(StringRef name) { |
550 | return llvm::dyn_cast_or_null<AttrClass>(getAttr(name)); |
551 | } |
552 | |
553 | /// Return true if the operation has an attribute with the provided name, |
554 | /// false otherwise. |
555 | bool hasAttr(StringAttr name) { |
556 | if (getPropertiesStorageSize()) { |
557 | if (std::optional<Attribute> inherentAttr = getInherentAttr(name: name)) |
558 | return (bool)*inherentAttr; |
559 | } |
560 | return attrs.contains(name); |
561 | } |
562 | bool hasAttr(StringRef name) { |
563 | if (getPropertiesStorageSize()) { |
564 | if (std::optional<Attribute> inherentAttr = getInherentAttr(name)) |
565 | return (bool)*inherentAttr; |
566 | } |
567 | return attrs.contains(name); |
568 | } |
569 | template <typename AttrClass, typename NameT> |
570 | bool hasAttrOfType(NameT &&name) { |
571 | return static_cast<bool>( |
572 | getAttrOfType<AttrClass>(std::forward<NameT>(name))); |
573 | } |
574 | |
575 | /// If the an attribute exists with the specified name, change it to the new |
576 | /// value. Otherwise, add a new attribute with the specified name/value. |
577 | void setAttr(StringAttr name, Attribute value) { |
578 | if (getPropertiesStorageSize()) { |
579 | if (getInherentAttr(name: name)) { |
580 | setInherentAttr(name: name, value); |
581 | return; |
582 | } |
583 | } |
584 | NamedAttrList attributes(attrs); |
585 | if (attributes.set(name, value) != value) |
586 | attrs = attributes.getDictionary(getContext()); |
587 | } |
588 | void setAttr(StringRef name, Attribute value) { |
589 | setAttr(StringAttr::get(getContext(), name), value); |
590 | } |
591 | |
592 | /// Remove the attribute with the specified name if it exists. Return the |
593 | /// attribute that was erased, or nullptr if there was no attribute with such |
594 | /// name. |
595 | Attribute removeAttr(StringAttr name) { |
596 | if (getPropertiesStorageSize()) { |
597 | if (std::optional<Attribute> inherentAttr = getInherentAttr(name: name)) { |
598 | setInherentAttr(name: name, value: {}); |
599 | return *inherentAttr; |
600 | } |
601 | } |
602 | NamedAttrList attributes(attrs); |
603 | Attribute removedAttr = attributes.erase(name); |
604 | if (removedAttr) |
605 | attrs = attributes.getDictionary(getContext()); |
606 | return removedAttr; |
607 | } |
608 | Attribute removeAttr(StringRef name) { |
609 | return removeAttr(StringAttr::get(getContext(), name)); |
610 | } |
611 | |
612 | /// A utility iterator that filters out non-dialect attributes. |
613 | class dialect_attr_iterator |
614 | : public llvm::filter_iterator<ArrayRef<NamedAttribute>::iterator, |
615 | bool (*)(NamedAttribute)> { |
616 | static bool filter(NamedAttribute attr) { |
617 | // Dialect attributes are prefixed by the dialect name, like operations. |
618 | return attr.getName().strref().count('.'); |
619 | } |
620 | |
621 | explicit dialect_attr_iterator(ArrayRef<NamedAttribute>::iterator it, |
622 | ArrayRef<NamedAttribute>::iterator end) |
623 | : llvm::filter_iterator<ArrayRef<NamedAttribute>::iterator, |
624 | bool (*)(NamedAttribute)>(it, end, &filter) {} |
625 | |
626 | // Allow access to the constructor. |
627 | friend Operation; |
628 | }; |
629 | using dialect_attr_range = iterator_range<dialect_attr_iterator>; |
630 | |
631 | /// Return a range corresponding to the dialect attributes for this operation. |
632 | dialect_attr_range getDialectAttrs() { |
633 | auto attrs = getAttrs(); |
634 | return {dialect_attr_iterator(attrs.begin(), attrs.end()), |
635 | dialect_attr_iterator(attrs.end(), attrs.end())}; |
636 | } |
637 | dialect_attr_iterator dialect_attr_begin() { |
638 | auto attrs = getAttrs(); |
639 | return dialect_attr_iterator(attrs.begin(), attrs.end()); |
640 | } |
641 | dialect_attr_iterator dialect_attr_end() { |
642 | auto attrs = getAttrs(); |
643 | return dialect_attr_iterator(attrs.end(), attrs.end()); |
644 | } |
645 | |
646 | /// Set the dialect attributes for this operation, and preserve all inherent. |
647 | template <typename DialectAttrT> |
648 | void setDialectAttrs(DialectAttrT &&dialectAttrs) { |
649 | NamedAttrList attrs; |
650 | attrs.append(std::begin(dialectAttrs), std::end(dialectAttrs)); |
651 | for (auto attr : getAttrs()) |
652 | if (!attr.getName().strref().contains('.')) |
653 | attrs.push_back(newAttribute: attr); |
654 | setAttrs(attrs.getDictionary(getContext())); |
655 | } |
656 | |
657 | /// Sets default attributes on unset attributes. |
658 | void populateDefaultAttrs() { |
659 | NamedAttrList attrs(getAttrDictionary()); |
660 | name.populateDefaultAttrs(attrs); |
661 | setAttrs(attrs.getDictionary(getContext())); |
662 | } |
663 | |
664 | //===--------------------------------------------------------------------===// |
665 | // Blocks |
666 | //===--------------------------------------------------------------------===// |
667 | |
668 | /// Returns the number of regions held by this operation. |
669 | unsigned getNumRegions() { return numRegions; } |
670 | |
671 | /// Returns the regions held by this operation. |
672 | MutableArrayRef<Region> getRegions() { |
673 | // Check the count first, as computing the trailing objects can be slow. |
674 | if (numRegions == 0) |
675 | return MutableArrayRef<Region>(); |
676 | |
677 | auto *regions = getTrailingObjects<Region>(); |
678 | return {regions, numRegions}; |
679 | } |
680 | |
681 | /// Returns the region held by this operation at position 'index'. |
682 | Region &getRegion(unsigned index) { |
683 | assert(index < numRegions && "invalid region index" ); |
684 | return getRegions()[index]; |
685 | } |
686 | |
687 | //===--------------------------------------------------------------------===// |
688 | // Successors |
689 | //===--------------------------------------------------------------------===// |
690 | |
691 | MutableArrayRef<BlockOperand> getBlockOperands() { |
692 | return {getTrailingObjects<BlockOperand>(), numSuccs}; |
693 | } |
694 | |
695 | // Successor iteration. |
696 | using succ_iterator = SuccessorRange::iterator; |
697 | succ_iterator successor_begin() { return getSuccessors().begin(); } |
698 | succ_iterator successor_end() { return getSuccessors().end(); } |
699 | SuccessorRange getSuccessors() { return SuccessorRange(this); } |
700 | |
701 | bool hasSuccessors() { return numSuccs != 0; } |
702 | unsigned getNumSuccessors() { return numSuccs; } |
703 | |
704 | Block *getSuccessor(unsigned index) { |
705 | assert(index < getNumSuccessors()); |
706 | return getBlockOperands()[index].get(); |
707 | } |
708 | void setSuccessor(Block *block, unsigned index); |
709 | |
710 | //===--------------------------------------------------------------------===// |
711 | // Accessors for various properties of operations |
712 | //===--------------------------------------------------------------------===// |
713 | |
714 | /// Attempt to fold this operation with the specified constant operand values |
715 | /// - the elements in "operands" will correspond directly to the operands of |
716 | /// the operation, but may be null if non-constant. |
717 | /// |
718 | /// If folding was successful, this function returns "success". |
719 | /// * If this operation was modified in-place (but not folded away), |
720 | /// `results` is empty. |
721 | /// * Otherwise, `results` is filled with the folded results. |
722 | /// If folding was unsuccessful, this function returns "failure". |
723 | LogicalResult fold(ArrayRef<Attribute> operands, |
724 | SmallVectorImpl<OpFoldResult> &results); |
725 | |
726 | /// Attempt to fold this operation. |
727 | /// |
728 | /// If folding was successful, this function returns "success". |
729 | /// * If this operation was modified in-place (but not folded away), |
730 | /// `results` is empty. |
731 | /// * Otherwise, `results` is filled with the folded results. |
732 | /// If folding was unsuccessful, this function returns "failure". |
733 | LogicalResult fold(SmallVectorImpl<OpFoldResult> &results); |
734 | |
735 | /// Returns true if `InterfaceT` has been promised by the dialect or |
736 | /// implemented. |
737 | template <typename InterfaceT> |
738 | bool hasPromiseOrImplementsInterface() const { |
739 | return name.hasPromiseOrImplementsInterface<InterfaceT>(); |
740 | } |
741 | |
742 | /// Returns true if the operation was registered with a particular trait, e.g. |
743 | /// hasTrait<OperandsAreSignlessIntegerLike>(). |
744 | template <template <typename T> class Trait> |
745 | bool hasTrait() { |
746 | return name.hasTrait<Trait>(); |
747 | } |
748 | |
749 | /// Returns true if the operation *might* have the provided trait. This |
750 | /// means that either the operation is unregistered, or it was registered with |
751 | /// the provide trait. |
752 | template <template <typename T> class Trait> |
753 | bool mightHaveTrait() { |
754 | return name.mightHaveTrait<Trait>(); |
755 | } |
756 | |
757 | //===--------------------------------------------------------------------===// |
758 | // Operation Walkers |
759 | //===--------------------------------------------------------------------===// |
760 | |
761 | /// Walk the operation by calling the callback for each nested operation |
762 | /// (including this one), block or region, depending on the callback provided. |
763 | /// The order in which regions, blocks and operations at the same nesting |
764 | /// level are visited (e.g., lexicographical or reverse lexicographical order) |
765 | /// is determined by 'Iterator'. The walk order for enclosing regions, blocks |
766 | /// and operations with respect to their nested ones is specified by 'Order' |
767 | /// (post-order by default). A callback on a block or operation is allowed to |
768 | /// erase that block or operation if either: |
769 | /// * the walk is in post-order, or |
770 | /// * the walk is in pre-order and the walk is skipped after the erasure. |
771 | /// |
772 | /// The callback method can take any of the following forms: |
773 | /// void(Operation*) : Walk all operations opaquely. |
774 | /// * op->walk([](Operation *nestedOp) { ...}); |
775 | /// void(OpT) : Walk all operations of the given derived type. |
776 | /// * op->walk([](ReturnOp returnOp) { ...}); |
777 | /// WalkResult(Operation*|OpT) : Walk operations, but allow for |
778 | /// interruption/skipping. |
779 | /// * op->walk([](... op) { |
780 | /// // Skip the walk of this op based on some invariant. |
781 | /// if (some_invariant) |
782 | /// return WalkResult::skip(); |
783 | /// // Interrupt, i.e cancel, the walk based on some invariant. |
784 | /// if (another_invariant) |
785 | /// return WalkResult::interrupt(); |
786 | /// return WalkResult::advance(); |
787 | /// }); |
788 | template <WalkOrder Order = WalkOrder::PostOrder, |
789 | typename Iterator = ForwardIterator, typename FnT, |
790 | typename RetT = detail::walkResultType<FnT>> |
791 | std::enable_if_t<llvm::function_traits<std::decay_t<FnT>>::num_args == 1, |
792 | RetT> |
793 | walk(FnT &&callback) { |
794 | return detail::walk<Order, Iterator>(this, std::forward<FnT>(callback)); |
795 | } |
796 | |
797 | /// Generic walker with a stage aware callback. Walk the operation by calling |
798 | /// the callback for each nested operation (including this one) N+1 times, |
799 | /// where N is the number of regions attached to that operation. |
800 | /// |
801 | /// The callback method can take any of the following forms: |
802 | /// void(Operation *, const WalkStage &) : Walk all operation opaquely |
803 | /// * op->walk([](Operation *nestedOp, const WalkStage &stage) { ...}); |
804 | /// void(OpT, const WalkStage &) : Walk all operations of the given derived |
805 | /// type. |
806 | /// * op->walk([](ReturnOp returnOp, const WalkStage &stage) { ...}); |
807 | /// WalkResult(Operation*|OpT, const WalkStage &stage) : Walk operations, |
808 | /// but allow for interruption/skipping. |
809 | /// * op->walk([](... op, const WalkStage &stage) { |
810 | /// // Skip the walk of this op based on some invariant. |
811 | /// if (some_invariant) |
812 | /// return WalkResult::skip(); |
813 | /// // Interrupt, i.e cancel, the walk based on some invariant. |
814 | /// if (another_invariant) |
815 | /// return WalkResult::interrupt(); |
816 | /// return WalkResult::advance(); |
817 | /// }); |
818 | template <typename FnT, typename RetT = detail::walkResultType<FnT>> |
819 | std::enable_if_t<llvm::function_traits<std::decay_t<FnT>>::num_args == 2, |
820 | RetT> |
821 | walk(FnT &&callback) { |
822 | return detail::walk(this, std::forward<FnT>(callback)); |
823 | } |
824 | |
825 | //===--------------------------------------------------------------------===// |
826 | // Uses |
827 | //===--------------------------------------------------------------------===// |
828 | |
829 | /// Drop all uses of results of this operation. |
830 | void dropAllUses() { |
831 | for (OpResult result : getOpResults()) |
832 | result.dropAllUses(); |
833 | } |
834 | |
835 | using use_iterator = result_range::use_iterator; |
836 | using use_range = result_range::use_range; |
837 | |
838 | use_iterator use_begin() { return getResults().use_begin(); } |
839 | use_iterator use_end() { return getResults().use_end(); } |
840 | |
841 | /// Returns a range of all uses, which is useful for iterating over all uses. |
842 | use_range getUses() { return getResults().getUses(); } |
843 | |
844 | /// Returns true if this operation has exactly one use. |
845 | bool hasOneUse() { return llvm::hasSingleElement(C: getUses()); } |
846 | |
847 | /// Returns true if this operation has no uses. |
848 | bool use_empty() { return getResults().use_empty(); } |
849 | |
850 | /// Returns true if the results of this operation are used outside of the |
851 | /// given block. |
852 | bool isUsedOutsideOfBlock(Block *block) { |
853 | return llvm::any_of(Range: getOpResults(), P: [block](OpResult result) { |
854 | return result.isUsedOutsideOfBlock(block); |
855 | }); |
856 | } |
857 | |
858 | //===--------------------------------------------------------------------===// |
859 | // Users |
860 | //===--------------------------------------------------------------------===// |
861 | |
862 | using user_iterator = ValueUserIterator<use_iterator, OpOperand>; |
863 | using user_range = iterator_range<user_iterator>; |
864 | |
865 | user_iterator user_begin() { return user_iterator(use_begin()); } |
866 | user_iterator user_end() { return user_iterator(use_end()); } |
867 | |
868 | /// Returns a range of all users. |
869 | user_range getUsers() { return {user_begin(), user_end()}; } |
870 | |
871 | //===--------------------------------------------------------------------===// |
872 | // Other |
873 | //===--------------------------------------------------------------------===// |
874 | |
875 | /// Emit an error with the op name prefixed, like "'dim' op " which is |
876 | /// convenient for verifiers. |
877 | InFlightDiagnostic emitOpError(const Twine &message = {}); |
878 | |
879 | /// Emit an error about fatal conditions with this operation, reporting up to |
880 | /// any diagnostic handlers that may be listening. |
881 | InFlightDiagnostic emitError(const Twine &message = {}); |
882 | |
883 | /// Emit a warning about this operation, reporting up to any diagnostic |
884 | /// handlers that may be listening. |
885 | InFlightDiagnostic emitWarning(const Twine &message = {}); |
886 | |
887 | /// Emit a remark about this operation, reporting up to any diagnostic |
888 | /// handlers that may be listening. |
889 | InFlightDiagnostic (const Twine &message = {}); |
890 | |
891 | /// Returns the properties storage size. |
892 | int getPropertiesStorageSize() const { |
893 | return ((int)propertiesStorageSize) * 8; |
894 | } |
895 | /// Returns the properties storage. |
896 | OpaqueProperties getPropertiesStorage() { |
897 | if (propertiesStorageSize) |
898 | return getPropertiesStorageUnsafe(); |
899 | return {nullptr}; |
900 | } |
901 | OpaqueProperties getPropertiesStorage() const { |
902 | if (propertiesStorageSize) |
903 | return {reinterpret_cast<void *>(const_cast<detail::OpProperties *>( |
904 | getTrailingObjects<detail::OpProperties>()))}; |
905 | return {nullptr}; |
906 | } |
907 | /// Returns the properties storage without checking whether properties are |
908 | /// present. |
909 | OpaqueProperties getPropertiesStorageUnsafe() { |
910 | return { |
911 | reinterpret_cast<void *>(getTrailingObjects<detail::OpProperties>())}; |
912 | } |
913 | |
914 | /// Return the properties converted to an attribute. |
915 | /// This is expensive, and mostly useful when dealing with unregistered |
916 | /// operation. Returns an empty attribute if no properties are present. |
917 | Attribute getPropertiesAsAttribute(); |
918 | |
919 | /// Set the properties from the provided attribute. |
920 | /// This is an expensive operation that can fail if the attribute is not |
921 | /// matching the expectations of the properties for this operation. This is |
922 | /// mostly useful for unregistered operations or used when parsing the |
923 | /// generic format. An optional diagnostic can be passed in for richer errors. |
924 | LogicalResult |
925 | setPropertiesFromAttribute(Attribute attr, |
926 | function_ref<InFlightDiagnostic()> emitError); |
927 | |
928 | /// Copy properties from an existing other properties object. The two objects |
929 | /// must be the same type. |
930 | void copyProperties(OpaqueProperties rhs); |
931 | |
932 | /// Compute a hash for the op properties (if any). |
933 | llvm::hash_code hashProperties(); |
934 | |
935 | private: |
936 | //===--------------------------------------------------------------------===// |
937 | // Ordering |
938 | //===--------------------------------------------------------------------===// |
939 | |
940 | /// This value represents an invalid index ordering for an operation within a |
941 | /// block. |
942 | static constexpr unsigned kInvalidOrderIdx = -1; |
943 | |
944 | /// This value represents the stride to use when computing a new order for an |
945 | /// operation. |
946 | static constexpr unsigned kOrderStride = 5; |
947 | |
948 | /// Update the order index of this operation of this operation if necessary, |
949 | /// potentially recomputing the order of the parent block. |
950 | void updateOrderIfNecessary(); |
951 | |
952 | /// Returns true if this operation has a valid order. |
953 | bool hasValidOrder() { return orderIndex != kInvalidOrderIdx; } |
954 | |
955 | private: |
956 | Operation(Location location, OperationName name, unsigned numResults, |
957 | unsigned numSuccessors, unsigned numRegions, |
958 | int propertiesStorageSize, DictionaryAttr attributes, |
959 | OpaqueProperties properties, bool hasOperandStorage); |
960 | |
961 | // Operations are deleted through the destroy() member because they are |
962 | // allocated with malloc. |
963 | ~Operation(); |
964 | |
965 | /// Returns the additional size necessary for allocating the given objects |
966 | /// before an Operation in-memory. |
967 | static size_t prefixAllocSize(unsigned numOutOfLineResults, |
968 | unsigned numInlineResults) { |
969 | return sizeof(detail::OutOfLineOpResult) * numOutOfLineResults + |
970 | sizeof(detail::InlineOpResult) * numInlineResults; |
971 | } |
972 | /// Returns the additional size allocated before this Operation in-memory. |
973 | size_t prefixAllocSize() { |
974 | unsigned numResults = getNumResults(); |
975 | unsigned numOutOfLineResults = OpResult::getNumTrailing(numResults); |
976 | unsigned numInlineResults = OpResult::getNumInline(numResults); |
977 | return prefixAllocSize(numOutOfLineResults, numInlineResults); |
978 | } |
979 | |
980 | /// Returns the operand storage object. |
981 | detail::OperandStorage &getOperandStorage() { |
982 | assert(hasOperandStorage && "expected operation to have operand storage" ); |
983 | return *getTrailingObjects<detail::OperandStorage>(); |
984 | } |
985 | |
986 | /// Returns a pointer to the use list for the given out-of-line result. |
987 | detail::OutOfLineOpResult *getOutOfLineOpResult(unsigned resultNumber) { |
988 | // Out-of-line results are stored in reverse order after (before in memory) |
989 | // the inline results. |
990 | return reinterpret_cast<detail::OutOfLineOpResult *>(getInlineOpResult( |
991 | resultNumber: detail::OpResultImpl::getMaxInlineResults() - 1)) - |
992 | ++resultNumber; |
993 | } |
994 | |
995 | /// Returns a pointer to the use list for the given inline result. |
996 | detail::InlineOpResult *getInlineOpResult(unsigned resultNumber) { |
997 | // Inline results are stored in reverse order before the operation in |
998 | // memory. |
999 | return reinterpret_cast<detail::InlineOpResult *>(this) - ++resultNumber; |
1000 | } |
1001 | |
1002 | /// Returns a pointer to the use list for the given result, which may be |
1003 | /// either inline or out-of-line. |
1004 | detail::OpResultImpl *getOpResultImpl(unsigned resultNumber) { |
1005 | assert(resultNumber < getNumResults() && |
1006 | "Result number is out of range for operation" ); |
1007 | unsigned maxInlineResults = detail::OpResultImpl::getMaxInlineResults(); |
1008 | if (resultNumber < maxInlineResults) |
1009 | return getInlineOpResult(resultNumber); |
1010 | return getOutOfLineOpResult(resultNumber: resultNumber - maxInlineResults); |
1011 | } |
1012 | |
1013 | /// Provide a 'getParent' method for ilist_node_with_parent methods. |
1014 | /// We mark it as a const function because ilist_node_with_parent specifically |
1015 | /// requires a 'getParent() const' method. Once ilist_node removes this |
1016 | /// constraint, we should drop the const to fit the rest of the MLIR const |
1017 | /// model. |
1018 | Block *getParent() const { return block; } |
1019 | |
1020 | /// Expose a few methods explicitly for the debugger to call for |
1021 | /// visualization. |
1022 | #ifndef NDEBUG |
1023 | LLVM_DUMP_METHOD operand_range debug_getOperands() { return getOperands(); } |
1024 | LLVM_DUMP_METHOD result_range debug_getResults() { return getResults(); } |
1025 | LLVM_DUMP_METHOD SuccessorRange debug_getSuccessors() { |
1026 | return getSuccessors(); |
1027 | } |
1028 | LLVM_DUMP_METHOD MutableArrayRef<Region> debug_getRegions() { |
1029 | return getRegions(); |
1030 | } |
1031 | #endif |
1032 | |
1033 | /// The operation block that contains this operation. |
1034 | Block *block = nullptr; |
1035 | |
1036 | /// This holds information about the source location the operation was defined |
1037 | /// or derived from. |
1038 | Location location; |
1039 | |
1040 | /// Relative order of this operation in its parent block. Used for |
1041 | /// O(1) local dominance checks between operations. |
1042 | mutable unsigned orderIndex = 0; |
1043 | |
1044 | const unsigned numResults; |
1045 | const unsigned numSuccs; |
1046 | const unsigned numRegions : 23; |
1047 | |
1048 | /// This bit signals whether this operation has an operand storage or not. The |
1049 | /// operand storage may be elided for operations that are known to never have |
1050 | /// operands. |
1051 | bool hasOperandStorage : 1; |
1052 | |
1053 | /// The size of the storage for properties (if any), divided by 8: since the |
1054 | /// Properties storage will always be rounded up to the next multiple of 8 we |
1055 | /// save some bits here. |
1056 | unsigned char propertiesStorageSize : 8; |
1057 | /// This is the maximum size we support to allocate properties inline with an |
1058 | /// operation: this must match the bitwidth above. |
1059 | static constexpr int64_t propertiesCapacity = 8 * 256; |
1060 | |
1061 | /// This holds the name of the operation. |
1062 | OperationName name; |
1063 | |
1064 | /// This holds general named attributes for the operation. |
1065 | DictionaryAttr attrs; |
1066 | |
1067 | // allow ilist_traits access to 'block' field. |
1068 | friend struct llvm::ilist_traits<Operation>; |
1069 | |
1070 | // allow block to access the 'orderIndex' field. |
1071 | friend class Block; |
1072 | |
1073 | // allow value to access the 'ResultStorage' methods. |
1074 | friend class Value; |
1075 | |
1076 | // allow ilist_node_with_parent to access the 'getParent' method. |
1077 | friend class llvm::ilist_node_with_parent<Operation, Block>; |
1078 | |
1079 | // This stuff is used by the TrailingObjects template. |
1080 | friend llvm::TrailingObjects<Operation, detail::OperandStorage, |
1081 | detail::OpProperties, BlockOperand, Region, |
1082 | OpOperand>; |
1083 | size_t numTrailingObjects(OverloadToken<detail::OperandStorage>) const { |
1084 | return hasOperandStorage ? 1 : 0; |
1085 | } |
1086 | size_t numTrailingObjects(OverloadToken<BlockOperand>) const { |
1087 | return numSuccs; |
1088 | } |
1089 | size_t numTrailingObjects(OverloadToken<Region>) const { return numRegions; } |
1090 | size_t numTrailingObjects(OverloadToken<detail::OpProperties>) const { |
1091 | return getPropertiesStorageSize(); |
1092 | } |
1093 | }; |
1094 | |
1095 | inline raw_ostream &operator<<(raw_ostream &os, const Operation &op) { |
1096 | const_cast<Operation &>(op).print(os, flags: OpPrintingFlags().useLocalScope()); |
1097 | return os; |
1098 | } |
1099 | |
1100 | } // namespace mlir |
1101 | |
1102 | namespace llvm { |
1103 | /// Cast from an (const) Operation * to a derived operation type. |
1104 | template <typename T> |
1105 | struct CastInfo<T, ::mlir::Operation *> |
1106 | : public ValueFromPointerCast<T, ::mlir::Operation, |
1107 | CastInfo<T, ::mlir::Operation *>> { |
1108 | static bool isPossible(::mlir::Operation *op) { return T::classof(op); } |
1109 | }; |
1110 | template <typename T> |
1111 | struct CastInfo<T, const ::mlir::Operation *> |
1112 | : public ConstStrippingForwardingCast<T, const ::mlir::Operation *, |
1113 | CastInfo<T, ::mlir::Operation *>> {}; |
1114 | |
1115 | /// Cast from an (const) Operation & to a derived operation type. |
1116 | template <typename T> |
1117 | struct CastInfo<T, ::mlir::Operation> |
1118 | : public NullableValueCastFailed<T>, |
1119 | public DefaultDoCastIfPossible<T, ::mlir::Operation &, |
1120 | CastInfo<T, ::mlir::Operation>> { |
1121 | // Provide isPossible here because here we have the const-stripping from |
1122 | // ConstStrippingCast. |
1123 | static bool isPossible(::mlir::Operation &val) { return T::classof(&val); } |
1124 | static T doCast(::mlir::Operation &val) { return T(&val); } |
1125 | }; |
1126 | template <typename T> |
1127 | struct CastInfo<T, const ::mlir::Operation> |
1128 | : public ConstStrippingForwardingCast<T, const ::mlir::Operation, |
1129 | CastInfo<T, ::mlir::Operation>> {}; |
1130 | |
1131 | /// Cast (const) Operation * to itself. This is helpful to avoid SFINAE in |
1132 | /// templated implementations that should work on both base and derived |
1133 | /// operation types. |
1134 | template <> |
1135 | struct CastInfo<::mlir::Operation *, ::mlir::Operation *> |
1136 | : public NullableValueCastFailed<::mlir::Operation *>, |
1137 | public DefaultDoCastIfPossible< |
1138 | ::mlir::Operation *, ::mlir::Operation *, |
1139 | CastInfo<::mlir::Operation *, ::mlir::Operation *>> { |
1140 | static bool isPossible(::mlir::Operation *op) { return true; } |
1141 | static ::mlir::Operation *doCast(::mlir::Operation *op) { return op; } |
1142 | }; |
1143 | template <> |
1144 | struct CastInfo<const ::mlir::Operation *, const ::mlir::Operation *> |
1145 | : public ConstStrippingForwardingCast< |
1146 | const ::mlir::Operation *, const ::mlir::Operation *, |
1147 | CastInfo<::mlir::Operation *, ::mlir::Operation *>> {}; |
1148 | } // namespace llvm |
1149 | |
1150 | #endif // MLIR_IR_OPERATION_H |
1151 | |