1 //===- OperationSupport.h ---------------------------------------*- 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 a number of support types that Operation and related
10 // classes build on top of.
11 //
12 //===----------------------------------------------------------------------===//
13
14 #ifndef MLIR_IR_OPERATIONSUPPORT_H
15 #define MLIR_IR_OPERATIONSUPPORT_H
16
17 #include "mlir/IR/BlockSupport.h"
18 #include "mlir/IR/BuiltinAttributes.h"
19 #include "mlir/IR/Location.h"
20 #include "mlir/IR/TypeRange.h"
21 #include "mlir/IR/Types.h"
22 #include "mlir/IR/Value.h"
23 #include "mlir/Support/InterfaceSupport.h"
24 #include "llvm/ADT/BitmaskEnum.h"
25 #include "llvm/ADT/PointerUnion.h"
26 #include "llvm/Support/PointerLikeTypeTraits.h"
27 #include "llvm/Support/TrailingObjects.h"
28 #include <memory>
29
30 namespace llvm {
31 class BitVector;
32 } // namespace llvm
33
34 namespace mlir {
35 class Dialect;
36 class DictionaryAttr;
37 class ElementsAttr;
38 class MutableOperandRangeRange;
39 class NamedAttrList;
40 class Operation;
41 struct OperationState;
42 class OpAsmParser;
43 class OpAsmParserResult;
44 class OpAsmPrinter;
45 class OperandRange;
46 class OperandRangeRange;
47 class OpFoldResult;
48 class ParseResult;
49 class Pattern;
50 class Region;
51 class ResultRange;
52 class RewritePattern;
53 class RewritePatternSet;
54 class Type;
55 class Value;
56 class ValueRange;
57 template <typename ValueRangeT>
58 class ValueTypeRange;
59
60 //===----------------------------------------------------------------------===//
61 // OperationName
62 //===----------------------------------------------------------------------===//
63
64 class OperationName {
65 public:
66 using GetCanonicalizationPatternsFn =
67 llvm::unique_function<void(RewritePatternSet &, MLIRContext *) const>;
68 using FoldHookFn = llvm::unique_function<LogicalResult(
69 Operation *, ArrayRef<Attribute>, SmallVectorImpl<OpFoldResult> &) const>;
70 using HasTraitFn = llvm::unique_function<bool(TypeID) const>;
71 using ParseAssemblyFn =
72 llvm::unique_function<ParseResult(OpAsmParser &, OperationState &) const>;
73 // Note: RegisteredOperationName is passed as reference here as the derived
74 // class is defined below.
75 using PopulateDefaultAttrsFn = llvm::unique_function<void(
76 const RegisteredOperationName &, NamedAttrList &) const>;
77 using PrintAssemblyFn =
78 llvm::unique_function<void(Operation *, OpAsmPrinter &, StringRef) const>;
79 using VerifyInvariantsFn =
80 llvm::unique_function<LogicalResult(Operation *) const>;
81 using VerifyRegionInvariantsFn =
82 llvm::unique_function<LogicalResult(Operation *) const>;
83
84 protected:
85 /// This class represents a type erased version of an operation. It contains
86 /// all of the components necessary for opaquely interacting with an
87 /// operation. If the operation is not registered, some of these components
88 /// may not be populated.
89 struct Impl {
ImplImpl90 Impl(StringAttr name)
91 : name(name), dialect(nullptr), interfaceMap(llvm::None) {}
92
93 /// The name of the operation.
94 StringAttr name;
95
96 //===------------------------------------------------------------------===//
97 // Registered Operation Info
98
99 /// The following fields are only populated when the operation is
100 /// registered.
101
102 /// Returns true if the operation has been registered, i.e. if the
103 /// registration info has been populated.
isRegisteredImpl104 bool isRegistered() const { return dialect; }
105
106 /// This is the dialect that this operation belongs to.
107 Dialect *dialect;
108
109 /// The unique identifier of the derived Op class.
110 TypeID typeID;
111
112 /// A map of interfaces that were registered to this operation.
113 detail::InterfaceMap interfaceMap;
114
115 /// Internal callback hooks provided by the op implementation.
116 FoldHookFn foldHookFn;
117 GetCanonicalizationPatternsFn getCanonicalizationPatternsFn;
118 HasTraitFn hasTraitFn;
119 ParseAssemblyFn parseAssemblyFn;
120 PopulateDefaultAttrsFn populateDefaultAttrsFn;
121 PrintAssemblyFn printAssemblyFn;
122 VerifyInvariantsFn verifyInvariantsFn;
123 VerifyRegionInvariantsFn verifyRegionInvariantsFn;
124
125 /// A list of attribute names registered to this operation in StringAttr
126 /// form. This allows for operation classes to use StringAttr for attribute
127 /// lookup/creation/etc., as opposed to raw strings.
128 ArrayRef<StringAttr> attributeNames;
129 };
130
131 public:
132 OperationName(StringRef name, MLIRContext *context);
133
134 /// Return if this operation is registered.
isRegistered()135 bool isRegistered() const { return impl->isRegistered(); }
136
137 /// If this operation is registered, returns the registered information, None
138 /// otherwise.
139 Optional<RegisteredOperationName> getRegisteredInfo() const;
140
141 /// Returns true if the operation was registered with a particular trait, e.g.
142 /// hasTrait<OperandsAreSignlessIntegerLike>(). Returns false if the operation
143 /// is unregistered.
144 template <template <typename T> class Trait>
hasTrait()145 bool hasTrait() const {
146 return hasTrait(TypeID::get<Trait>());
147 }
hasTrait(TypeID traitID)148 bool hasTrait(TypeID traitID) const {
149 return isRegistered() && impl->hasTraitFn(traitID);
150 }
151
152 /// Returns true if the operation *might* have the provided trait. This
153 /// means that either the operation is unregistered, or it was registered with
154 /// the provide trait.
155 template <template <typename T> class Trait>
mightHaveTrait()156 bool mightHaveTrait() const {
157 return mightHaveTrait(TypeID::get<Trait>());
158 }
mightHaveTrait(TypeID traitID)159 bool mightHaveTrait(TypeID traitID) const {
160 return !isRegistered() || impl->hasTraitFn(traitID);
161 }
162
163 /// Returns an instance of the concept object for the given interface if it
164 /// was registered to this operation, null otherwise. This should not be used
165 /// directly.
166 template <typename T>
getInterface()167 typename T::Concept *getInterface() const {
168 return impl->interfaceMap.lookup<T>();
169 }
170
171 /// Returns true if this operation has the given interface registered to it.
172 template <typename T>
hasInterface()173 bool hasInterface() const {
174 return hasInterface(TypeID::get<T>());
175 }
hasInterface(TypeID interfaceID)176 bool hasInterface(TypeID interfaceID) const {
177 return impl->interfaceMap.contains(interfaceID);
178 }
179
180 /// Returns true if the operation *might* have the provided interface. This
181 /// means that either the operation is unregistered, or it was registered with
182 /// the provide interface.
183 template <typename T>
mightHaveInterface()184 bool mightHaveInterface() const {
185 return mightHaveInterface(TypeID::get<T>());
186 }
mightHaveInterface(TypeID interfaceID)187 bool mightHaveInterface(TypeID interfaceID) const {
188 return !isRegistered() || hasInterface(interfaceID);
189 }
190
191 /// Return the dialect this operation is registered to if the dialect is
192 /// loaded in the context, or nullptr if the dialect isn't loaded.
getDialect()193 Dialect *getDialect() const {
194 return isRegistered() ? impl->dialect : impl->name.getReferencedDialect();
195 }
196
197 /// Return the name of the dialect this operation is registered to.
198 StringRef getDialectNamespace() const;
199
200 /// Return the operation name with dialect name stripped, if it has one.
stripDialect()201 StringRef stripDialect() const { return getStringRef().split('.').second; }
202
203 /// Return the name of this operation. This always succeeds.
getStringRef()204 StringRef getStringRef() const { return getIdentifier(); }
205
206 /// Return the name of this operation as a StringAttr.
getIdentifier()207 StringAttr getIdentifier() const { return impl->name; }
208
209 void print(raw_ostream &os) const;
210 void dump() const;
211
212 /// Represent the operation name as an opaque pointer. (Used to support
213 /// PointerLikeTypeTraits).
getAsOpaquePointer()214 void *getAsOpaquePointer() const { return const_cast<Impl *>(impl); }
getFromOpaquePointer(const void * pointer)215 static OperationName getFromOpaquePointer(const void *pointer) {
216 return OperationName(
217 const_cast<Impl *>(reinterpret_cast<const Impl *>(pointer)));
218 }
219
220 bool operator==(const OperationName &rhs) const { return impl == rhs.impl; }
221 bool operator!=(const OperationName &rhs) const { return !(*this == rhs); }
222
223 protected:
OperationName(Impl * impl)224 OperationName(Impl *impl) : impl(impl) {}
225
226 /// The internal implementation of the operation name.
227 Impl *impl;
228
229 /// Allow access to the Impl struct.
230 friend MLIRContextImpl;
231 };
232
233 inline raw_ostream &operator<<(raw_ostream &os, OperationName info) {
234 info.print(os);
235 return os;
236 }
237
238 // Make operation names hashable.
hash_value(OperationName arg)239 inline llvm::hash_code hash_value(OperationName arg) {
240 return llvm::hash_value(arg.getAsOpaquePointer());
241 }
242
243 //===----------------------------------------------------------------------===//
244 // RegisteredOperationName
245 //===----------------------------------------------------------------------===//
246
247 /// This is a "type erased" representation of a registered operation. This
248 /// should only be used by things like the AsmPrinter and other things that need
249 /// to be parameterized by generic operation hooks. Most user code should use
250 /// the concrete operation types.
251 class RegisteredOperationName : public OperationName {
252 public:
253 /// Lookup the registered operation information for the given operation.
254 /// Returns None if the operation isn't registered.
255 static Optional<RegisteredOperationName> lookup(StringRef name,
256 MLIRContext *ctx);
257
258 /// Register a new operation in a Dialect object.
259 /// This constructor is used by Dialect objects when they register the list of
260 /// operations they contain.
261 template <typename T>
insert(Dialect & dialect)262 static void insert(Dialect &dialect) {
263 insert(T::getOperationName(), dialect, TypeID::get<T>(),
264 T::getParseAssemblyFn(), T::getPrintAssemblyFn(),
265 T::getVerifyInvariantsFn(), T::getVerifyRegionInvariantsFn(),
266 T::getFoldHookFn(), T::getGetCanonicalizationPatternsFn(),
267 T::getInterfaceMap(), T::getHasTraitFn(), T::getAttributeNames(),
268 T::getPopulateDefaultAttrsFn());
269 }
270 /// The use of this method is in general discouraged in favor of
271 /// 'insert<CustomOp>(dialect)'.
272 static void
273 insert(StringRef name, Dialect &dialect, TypeID typeID,
274 ParseAssemblyFn &&parseAssembly, PrintAssemblyFn &&printAssembly,
275 VerifyInvariantsFn &&verifyInvariants,
276 VerifyRegionInvariantsFn &&verifyRegionInvariants,
277 FoldHookFn &&foldHook,
278 GetCanonicalizationPatternsFn &&getCanonicalizationPatterns,
279 detail::InterfaceMap &&interfaceMap, HasTraitFn &&hasTrait,
280 ArrayRef<StringRef> attrNames,
281 PopulateDefaultAttrsFn &&populateDefaultAttrs);
282
283 /// Return the dialect this operation is registered to.
getDialect()284 Dialect &getDialect() const { return *impl->dialect; }
285
286 /// Return the unique identifier of the derived Op class.
getTypeID()287 TypeID getTypeID() const { return impl->typeID; }
288
289 /// Use the specified object to parse this ops custom assembly format.
290 ParseResult parseAssembly(OpAsmParser &parser, OperationState &result) const;
291
292 /// Return the static hook for parsing this operation assembly.
getParseAssemblyFn()293 const ParseAssemblyFn &getParseAssemblyFn() const {
294 return impl->parseAssemblyFn;
295 }
296
297 /// This hook implements the AsmPrinter for this operation.
printAssembly(Operation * op,OpAsmPrinter & p,StringRef defaultDialect)298 void printAssembly(Operation *op, OpAsmPrinter &p,
299 StringRef defaultDialect) const {
300 return impl->printAssemblyFn(op, p, defaultDialect);
301 }
302
303 /// These hooks implement the verifiers for this operation. It should emits
304 /// an error message and returns failure if a problem is detected, or returns
305 /// success if everything is ok.
verifyInvariants(Operation * op)306 LogicalResult verifyInvariants(Operation *op) const {
307 return impl->verifyInvariantsFn(op);
308 }
verifyRegionInvariants(Operation * op)309 LogicalResult verifyRegionInvariants(Operation *op) const {
310 return impl->verifyRegionInvariantsFn(op);
311 }
312
313 /// This hook implements a generalized folder for this operation. Operations
314 /// can implement this to provide simplifications rules that are applied by
315 /// the Builder::createOrFold API and the canonicalization pass.
316 ///
317 /// This is an intentionally limited interface - implementations of this hook
318 /// can only perform the following changes to the operation:
319 ///
320 /// 1. They can leave the operation alone and without changing the IR, and
321 /// return failure.
322 /// 2. They can mutate the operation in place, without changing anything else
323 /// in the IR. In this case, return success.
324 /// 3. They can return a list of existing values that can be used instead of
325 /// the operation. In this case, fill in the results list and return
326 /// success. The caller will remove the operation and use those results
327 /// instead.
328 ///
329 /// This allows expression of some simple in-place canonicalizations (e.g.
330 /// "x+0 -> x", "min(x,y,x,z) -> min(x,y,z)", "x+y-x -> y", etc), as well as
331 /// generalized constant folding.
foldHook(Operation * op,ArrayRef<Attribute> operands,SmallVectorImpl<OpFoldResult> & results)332 LogicalResult foldHook(Operation *op, ArrayRef<Attribute> operands,
333 SmallVectorImpl<OpFoldResult> &results) const {
334 return impl->foldHookFn(op, operands, results);
335 }
336
337 /// This hook returns any canonicalization pattern rewrites that the operation
338 /// supports, for use by the canonicalization pass.
getCanonicalizationPatterns(RewritePatternSet & results,MLIRContext * context)339 void getCanonicalizationPatterns(RewritePatternSet &results,
340 MLIRContext *context) const {
341 return impl->getCanonicalizationPatternsFn(results, context);
342 }
343
344 /// Attach the given models as implementations of the corresponding interfaces
345 /// for the concrete operation.
346 template <typename... Models>
attachInterface()347 void attachInterface() {
348 impl->interfaceMap.insert<Models...>();
349 }
350
351 /// Returns true if the operation has a particular trait.
352 template <template <typename T> class Trait>
hasTrait()353 bool hasTrait() const {
354 return hasTrait(TypeID::get<Trait>());
355 }
356
357 /// Returns true if the operation has a particular trait.
hasTrait(TypeID traitID)358 bool hasTrait(TypeID traitID) const { return impl->hasTraitFn(traitID); }
359
360 /// Return the list of cached attribute names registered to this operation.
361 /// The order of attributes cached here is unique to each type of operation,
362 /// and the interpretation of this attribute list should generally be driven
363 /// by the respective operation. In many cases, this caching removes the need
364 /// to use the raw string name of a known attribute.
365 ///
366 /// For example the ODS generator, with an op defining the following
367 /// attributes:
368 ///
369 /// let arguments = (ins I32Attr:$attr1, I32Attr:$attr2);
370 ///
371 /// ... may produce an order here of ["attr1", "attr2"]. This allows for the
372 /// ODS generator to directly access the cached name for a known attribute,
373 /// greatly simplifying the cost and complexity of attribute usage produced by
374 /// the generator.
375 ///
getAttributeNames()376 ArrayRef<StringAttr> getAttributeNames() const {
377 return impl->attributeNames;
378 }
379
380 /// This hook implements the method to populate defaults attributes that are
381 /// unset.
382 void populateDefaultAttrs(NamedAttrList &attrs) const;
383
384 /// Represent the operation name as an opaque pointer. (Used to support
385 /// PointerLikeTypeTraits).
getFromOpaquePointer(const void * pointer)386 static RegisteredOperationName getFromOpaquePointer(const void *pointer) {
387 return RegisteredOperationName(
388 const_cast<Impl *>(reinterpret_cast<const Impl *>(pointer)));
389 }
390
391 private:
RegisteredOperationName(Impl * impl)392 RegisteredOperationName(Impl *impl) : OperationName(impl) {}
393
394 /// Allow access to the constructor.
395 friend OperationName;
396 };
397
398 inline Optional<RegisteredOperationName>
getRegisteredInfo()399 OperationName::getRegisteredInfo() const {
400 return isRegistered() ? RegisteredOperationName(impl)
401 : Optional<RegisteredOperationName>();
402 }
403
404 //===----------------------------------------------------------------------===//
405 // Attribute Dictionary-Like Interface
406 //===----------------------------------------------------------------------===//
407
408 /// Attribute collections provide a dictionary-like interface. Define common
409 /// lookup functions.
410 namespace impl {
411
412 /// Unsorted string search or identifier lookups are linear scans.
413 template <typename IteratorT, typename NameT>
findAttrUnsorted(IteratorT first,IteratorT last,NameT name)414 std::pair<IteratorT, bool> findAttrUnsorted(IteratorT first, IteratorT last,
415 NameT name) {
416 for (auto it = first; it != last; ++it)
417 if (it->getName() == name)
418 return {it, true};
419 return {last, false};
420 }
421
422 /// Using llvm::lower_bound requires an extra string comparison to check whether
423 /// the returned iterator points to the found element or whether it indicates
424 /// the lower bound. Skip this redundant comparison by checking if `compare ==
425 /// 0` during the binary search.
426 template <typename IteratorT>
findAttrSorted(IteratorT first,IteratorT last,StringRef name)427 std::pair<IteratorT, bool> findAttrSorted(IteratorT first, IteratorT last,
428 StringRef name) {
429 ptrdiff_t length = std::distance(first, last);
430
431 while (length > 0) {
432 ptrdiff_t half = length / 2;
433 IteratorT mid = first + half;
434 int compare = mid->getName().strref().compare(name);
435 if (compare < 0) {
436 first = mid + 1;
437 length = length - half - 1;
438 } else if (compare > 0) {
439 length = half;
440 } else {
441 return {mid, true};
442 }
443 }
444 return {first, false};
445 }
446
447 /// StringAttr lookups on large attribute lists will switch to string binary
448 /// search. String binary searches become significantly faster than linear scans
449 /// with the identifier when the attribute list becomes very large.
450 template <typename IteratorT>
findAttrSorted(IteratorT first,IteratorT last,StringAttr name)451 std::pair<IteratorT, bool> findAttrSorted(IteratorT first, IteratorT last,
452 StringAttr name) {
453 constexpr unsigned kSmallAttributeList = 16;
454 if (std::distance(first, last) > kSmallAttributeList)
455 return findAttrSorted(first, last, name.strref());
456 return findAttrUnsorted(first, last, name);
457 }
458
459 /// Get an attribute from a sorted range of named attributes. Returns null if
460 /// the attribute was not found.
461 template <typename IteratorT, typename NameT>
getAttrFromSortedRange(IteratorT first,IteratorT last,NameT name)462 Attribute getAttrFromSortedRange(IteratorT first, IteratorT last, NameT name) {
463 std::pair<IteratorT, bool> result = findAttrSorted(first, last, name);
464 return result.second ? result.first->getValue() : Attribute();
465 }
466
467 /// Get an attribute from a sorted range of named attributes. Returns None if
468 /// the attribute was not found.
469 template <typename IteratorT, typename NameT>
470 Optional<NamedAttribute>
getNamedAttrFromSortedRange(IteratorT first,IteratorT last,NameT name)471 getNamedAttrFromSortedRange(IteratorT first, IteratorT last, NameT name) {
472 std::pair<IteratorT, bool> result = findAttrSorted(first, last, name);
473 return result.second ? *result.first : Optional<NamedAttribute>();
474 }
475
476 } // namespace impl
477
478 //===----------------------------------------------------------------------===//
479 // NamedAttrList
480 //===----------------------------------------------------------------------===//
481
482 /// NamedAttrList is array of NamedAttributes that tracks whether it is sorted
483 /// and does some basic work to remain sorted.
484 class NamedAttrList {
485 public:
486 using iterator = SmallVectorImpl<NamedAttribute>::iterator;
487 using const_iterator = SmallVectorImpl<NamedAttribute>::const_iterator;
488 using reference = NamedAttribute &;
489 using const_reference = const NamedAttribute &;
490 using size_type = size_t;
491
NamedAttrList()492 NamedAttrList() : dictionarySorted({}, true) {}
NamedAttrList(llvm::NoneType none)493 NamedAttrList(llvm::NoneType none) : NamedAttrList() {}
494 NamedAttrList(ArrayRef<NamedAttribute> attributes);
495 NamedAttrList(DictionaryAttr attributes);
496 NamedAttrList(const_iterator inStart, const_iterator inEnd);
497
498 template <typename Container>
NamedAttrList(const Container & vec)499 NamedAttrList(const Container &vec)
500 : NamedAttrList(ArrayRef<NamedAttribute>(vec)) {}
501
502 bool operator!=(const NamedAttrList &other) const {
503 return !(*this == other);
504 }
505 bool operator==(const NamedAttrList &other) const {
506 return attrs == other.attrs;
507 }
508
509 /// Add an attribute with the specified name.
510 void append(StringRef name, Attribute attr);
511
512 /// Add an attribute with the specified name.
append(StringAttr name,Attribute attr)513 void append(StringAttr name, Attribute attr) {
514 append(NamedAttribute(name, attr));
515 }
516
517 /// Append the given named attribute.
append(NamedAttribute attr)518 void append(NamedAttribute attr) { push_back(attr); }
519
520 /// Add an array of named attributes.
521 template <typename RangeT>
append(RangeT && newAttributes)522 void append(RangeT &&newAttributes) {
523 append(std::begin(newAttributes), std::end(newAttributes));
524 }
525
526 /// Add a range of named attributes.
527 template <typename IteratorT,
528 typename = std::enable_if_t<std::is_convertible<
529 typename std::iterator_traits<IteratorT>::iterator_category,
530 std::input_iterator_tag>::value>>
append(IteratorT inStart,IteratorT inEnd)531 void append(IteratorT inStart, IteratorT inEnd) {
532 // TODO: expand to handle case where values appended are in order & after
533 // end of current list.
534 dictionarySorted.setPointerAndInt(nullptr, false);
535 attrs.append(inStart, inEnd);
536 }
537
538 /// Replaces the attributes with new list of attributes.
539 void assign(const_iterator inStart, const_iterator inEnd);
540
541 /// Replaces the attributes with new list of attributes.
assign(ArrayRef<NamedAttribute> range)542 void assign(ArrayRef<NamedAttribute> range) {
543 assign(range.begin(), range.end());
544 }
545
empty()546 bool empty() const { return attrs.empty(); }
547
reserve(size_type N)548 void reserve(size_type N) { attrs.reserve(N); }
549
550 /// Add an attribute with the specified name.
551 void push_back(NamedAttribute newAttribute);
552
553 /// Pop last element from list.
pop_back()554 void pop_back() { attrs.pop_back(); }
555
556 /// Returns an entry with a duplicate name the list, if it exists, else
557 /// returns llvm::None.
558 Optional<NamedAttribute> findDuplicate() const;
559
560 /// Return a dictionary attribute for the underlying dictionary. This will
561 /// return an empty dictionary attribute if empty rather than null.
562 DictionaryAttr getDictionary(MLIRContext *context) const;
563
564 /// Return all of the attributes on this operation.
565 ArrayRef<NamedAttribute> getAttrs() const;
566
567 /// Return the specified attribute if present, null otherwise.
568 Attribute get(StringAttr name) const;
569 Attribute get(StringRef name) const;
570
571 /// Return the specified named attribute if present, None otherwise.
572 Optional<NamedAttribute> getNamed(StringRef name) const;
573 Optional<NamedAttribute> getNamed(StringAttr name) const;
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 /// Returns the previous attribute value of `name`, or null if no
578 /// attribute previously existed with `name`.
579 Attribute set(StringAttr name, Attribute value);
580 Attribute set(StringRef name, Attribute value);
581
582 /// Erase the attribute with the given name from the list. Return the
583 /// attribute that was erased, or nullptr if there was no attribute with such
584 /// name.
585 Attribute erase(StringAttr name);
586 Attribute erase(StringRef name);
587
begin()588 iterator begin() { return attrs.begin(); }
end()589 iterator end() { return attrs.end(); }
begin()590 const_iterator begin() const { return attrs.begin(); }
end()591 const_iterator end() const { return attrs.end(); }
592
593 NamedAttrList &operator=(const SmallVectorImpl<NamedAttribute> &rhs);
594 operator ArrayRef<NamedAttribute>() const;
595
596 private:
597 /// Return whether the attributes are sorted.
isSorted()598 bool isSorted() const { return dictionarySorted.getInt(); }
599
600 /// Erase the attribute at the given iterator position.
601 Attribute eraseImpl(SmallVectorImpl<NamedAttribute>::iterator it);
602
603 /// Lookup an attribute in the list.
604 template <typename AttrListT, typename NameT>
findAttr(AttrListT & attrs,NameT name)605 static auto findAttr(AttrListT &attrs, NameT name) {
606 return attrs.isSorted()
607 ? impl::findAttrSorted(attrs.begin(), attrs.end(), name)
608 : impl::findAttrUnsorted(attrs.begin(), attrs.end(), name);
609 }
610
611 // These are marked mutable as they may be modified (e.g., sorted)
612 mutable SmallVector<NamedAttribute, 4> attrs;
613 // Pair with cached DictionaryAttr and status of whether attrs is sorted.
614 // Note: just because sorted does not mean a DictionaryAttr has been created
615 // but the case where there is a DictionaryAttr but attrs isn't sorted should
616 // not occur.
617 mutable llvm::PointerIntPair<Attribute, 1, bool> dictionarySorted;
618 };
619
620 //===----------------------------------------------------------------------===//
621 // OperationState
622 //===----------------------------------------------------------------------===//
623
624 /// This represents an operation in an abstracted form, suitable for use with
625 /// the builder APIs. This object is a large and heavy weight object meant to
626 /// be used as a temporary object on the stack. It is generally unwise to put
627 /// this in a collection.
628 struct OperationState {
629 Location location;
630 OperationName name;
631 SmallVector<Value, 4> operands;
632 /// Types of the results of this operation.
633 SmallVector<Type, 4> types;
634 NamedAttrList attributes;
635 /// Successors of this operation and their respective operands.
636 SmallVector<Block *, 1> successors;
637 /// Regions that the op will hold.
638 SmallVector<std::unique_ptr<Region>, 1> regions;
639
640 public:
641 OperationState(Location location, StringRef name);
642 OperationState(Location location, OperationName name);
643
644 OperationState(Location location, OperationName name, ValueRange operands,
645 TypeRange types, ArrayRef<NamedAttribute> attributes,
646 BlockRange successors = {},
647 MutableArrayRef<std::unique_ptr<Region>> regions = {});
648 OperationState(Location location, StringRef name, ValueRange operands,
649 TypeRange types, ArrayRef<NamedAttribute> attributes,
650 BlockRange successors = {},
651 MutableArrayRef<std::unique_ptr<Region>> regions = {});
652
653 void addOperands(ValueRange newOperands);
654
addTypesOperationState655 void addTypes(ArrayRef<Type> newTypes) {
656 types.append(newTypes.begin(), newTypes.end());
657 }
658 template <typename RangeT>
659 std::enable_if_t<!std::is_convertible<RangeT, ArrayRef<Type>>::value>
addTypesOperationState660 addTypes(RangeT &&newTypes) {
661 types.append(newTypes.begin(), newTypes.end());
662 }
663
664 /// Add an attribute with the specified name.
addAttributeOperationState665 void addAttribute(StringRef name, Attribute attr) {
666 addAttribute(StringAttr::get(getContext(), name), attr);
667 }
668
669 /// Add an attribute with the specified name.
addAttributeOperationState670 void addAttribute(StringAttr name, Attribute attr) {
671 attributes.append(name, attr);
672 }
673
674 /// Add an array of named attributes.
addAttributesOperationState675 void addAttributes(ArrayRef<NamedAttribute> newAttributes) {
676 attributes.append(newAttributes);
677 }
678
addSuccessorsOperationState679 void addSuccessors(Block *successor) { successors.push_back(successor); }
680 void addSuccessors(BlockRange newSuccessors);
681
682 /// Create a region that should be attached to the operation. These regions
683 /// can be filled in immediately without waiting for Operation to be
684 /// created. When it is, the region bodies will be transferred.
685 Region *addRegion();
686
687 /// Take a region that should be attached to the Operation. The body of the
688 /// region will be transferred when the Operation is constructed. If the
689 /// region is null, a new empty region will be attached to the Operation.
690 void addRegion(std::unique_ptr<Region> &®ion);
691
692 /// Take ownership of a set of regions that should be attached to the
693 /// Operation.
694 void addRegions(MutableArrayRef<std::unique_ptr<Region>> regions);
695
696 /// Get the context held by this operation state.
getContextOperationState697 MLIRContext *getContext() const { return location->getContext(); }
698 };
699
700 //===----------------------------------------------------------------------===//
701 // OperandStorage
702 //===----------------------------------------------------------------------===//
703
704 namespace detail {
705 /// This class handles the management of operation operands. Operands are
706 /// stored either in a trailing array, or a dynamically resizable vector.
707 class alignas(8) OperandStorage {
708 public:
709 OperandStorage(Operation *owner, OpOperand *trailingOperands,
710 ValueRange values);
711 ~OperandStorage();
712
713 /// Replace the operands contained in the storage with the ones provided in
714 /// 'values'.
715 void setOperands(Operation *owner, ValueRange values);
716
717 /// Replace the operands beginning at 'start' and ending at 'start' + 'length'
718 /// with the ones provided in 'operands'. 'operands' may be smaller or larger
719 /// than the range pointed to by 'start'+'length'.
720 void setOperands(Operation *owner, unsigned start, unsigned length,
721 ValueRange operands);
722
723 /// Erase the operands held by the storage within the given range.
724 void eraseOperands(unsigned start, unsigned length);
725
726 /// Erase the operands held by the storage that have their corresponding bit
727 /// set in `eraseIndices`.
728 void eraseOperands(const BitVector &eraseIndices);
729
730 /// Get the operation operands held by the storage.
getOperands()731 MutableArrayRef<OpOperand> getOperands() { return {operandStorage, size()}; }
732
733 /// Return the number of operands held in the storage.
size()734 unsigned size() { return numOperands; }
735
736 private:
737 /// Resize the storage to the given size. Returns the array containing the new
738 /// operands.
739 MutableArrayRef<OpOperand> resize(Operation *owner, unsigned newSize);
740
741 /// The total capacity number of operands that the storage can hold.
742 unsigned capacity : 31;
743 /// A flag indicating if the operand storage was dynamically allocated, as
744 /// opposed to inlined into the owning operation.
745 unsigned isStorageDynamic : 1;
746 /// The number of operands within the storage.
747 unsigned numOperands;
748 /// A pointer to the operand storage.
749 OpOperand *operandStorage;
750 };
751 } // namespace detail
752
753 //===----------------------------------------------------------------------===//
754 // OpPrintingFlags
755 //===----------------------------------------------------------------------===//
756
757 /// Set of flags used to control the behavior of the various IR print methods
758 /// (e.g. Operation::Print).
759 class OpPrintingFlags {
760 public:
761 OpPrintingFlags();
OpPrintingFlags(llvm::NoneType)762 OpPrintingFlags(llvm::NoneType) : OpPrintingFlags() {}
763
764 /// Enables the elision of large elements attributes by printing a lexically
765 /// valid but otherwise meaningless form instead of the element data. The
766 /// `largeElementLimit` is used to configure what is considered to be a
767 /// "large" ElementsAttr by providing an upper limit to the number of
768 /// elements.
769 OpPrintingFlags &elideLargeElementsAttrs(int64_t largeElementLimit = 16);
770
771 /// Enable printing of debug information. If 'prettyForm' is set to true,
772 /// debug information is printed in a more readable 'pretty' form. Note: The
773 /// IR generated with 'prettyForm' is not parsable.
774 OpPrintingFlags &enableDebugInfo(bool prettyForm = false);
775
776 /// Always print operations in the generic form.
777 OpPrintingFlags &printGenericOpForm();
778
779 /// Do not verify the operation when using custom operation printers.
780 OpPrintingFlags &assumeVerified();
781
782 /// Use local scope when printing the operation. This allows for using the
783 /// printer in a more localized and thread-safe setting, but may not
784 /// necessarily be identical to what the IR will look like when dumping
785 /// the full module.
786 OpPrintingFlags &useLocalScope();
787
788 /// Print users of values as comments.
789 OpPrintingFlags &printValueUsers();
790
791 /// Return if the given ElementsAttr should be elided.
792 bool shouldElideElementsAttr(ElementsAttr attr) const;
793
794 /// Return the size limit for printing large ElementsAttr.
795 Optional<int64_t> getLargeElementsAttrLimit() const;
796
797 /// Return if debug information should be printed.
798 bool shouldPrintDebugInfo() const;
799
800 /// Return if debug information should be printed in the pretty form.
801 bool shouldPrintDebugInfoPrettyForm() const;
802
803 /// Return if operations should be printed in the generic form.
804 bool shouldPrintGenericOpForm() const;
805
806 /// Return if operation verification should be skipped.
807 bool shouldAssumeVerified() const;
808
809 /// Return if the printer should use local scope when dumping the IR.
810 bool shouldUseLocalScope() const;
811
812 /// Return if the printer should print users of values.
813 bool shouldPrintValueUsers() const;
814
815 private:
816 /// Elide large elements attributes if the number of elements is larger than
817 /// the upper limit.
818 Optional<int64_t> elementsAttrElementLimit;
819
820 /// Print debug information.
821 bool printDebugInfoFlag : 1;
822 bool printDebugInfoPrettyFormFlag : 1;
823
824 /// Print operations in the generic form.
825 bool printGenericOpFormFlag : 1;
826
827 /// Skip operation verification.
828 bool assumeVerifiedFlag : 1;
829
830 /// Print operations with numberings local to the current operation.
831 bool printLocalScope : 1;
832
833 /// Print users of values.
834 bool printValueUsersFlag : 1;
835 };
836
837 //===----------------------------------------------------------------------===//
838 // Operation Equivalency
839 //===----------------------------------------------------------------------===//
840
841 /// This class provides utilities for computing if two operations are
842 /// equivalent.
843 struct OperationEquivalence {
844 enum Flags {
845 None = 0,
846
847 // When provided, the location attached to the operation are ignored.
848 IgnoreLocations = 1,
849
850 LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ IgnoreLocations)
851 };
852
853 /// Compute a hash for the given operation.
854 /// The `hashOperands` and `hashResults` callbacks are expected to return a
855 /// unique hash_code for a given Value.
856 static llvm::hash_code computeHash(
857 Operation *op,
858 function_ref<llvm::hash_code(Value)> hashOperands =
859 [](Value v) { return hash_value(v); },
860 function_ref<llvm::hash_code(Value)> hashResults =
861 [](Value v) { return hash_value(v); },
862 Flags flags = Flags::None);
863
864 /// Helper that can be used with `computeHash` above to ignore operation
865 /// operands/result mapping.
ignoreHashValueOperationEquivalence866 static llvm::hash_code ignoreHashValue(Value) { return llvm::hash_code{}; }
867 /// Helper that can be used with `computeHash` above to ignore operation
868 /// operands/result mapping.
directHashValueOperationEquivalence869 static llvm::hash_code directHashValue(Value v) { return hash_value(v); }
870
871 /// Compare two operations and return if they are equivalent.
872 /// `mapOperands` and `mapResults` are optional callbacks that allows the
873 /// caller to check the mapping of SSA value between the lhs and rhs
874 /// operations. It is expected to return success if the mapping is valid and
875 /// failure if it conflicts with a previous mapping.
876 static bool
877 isEquivalentTo(Operation *lhs, Operation *rhs,
878 function_ref<LogicalResult(Value, Value)> mapOperands,
879 function_ref<LogicalResult(Value, Value)> mapResults,
880 Flags flags = Flags::None);
881
882 /// Helper that can be used with `isEquivalentTo` above to ignore operation
883 /// operands/result mapping.
ignoreValueEquivalenceOperationEquivalence884 static LogicalResult ignoreValueEquivalence(Value lhs, Value rhs) {
885 return success();
886 }
887 /// Helper that can be used with `isEquivalentTo` above to ignore operation
888 /// operands/result mapping.
exactValueMatchOperationEquivalence889 static LogicalResult exactValueMatch(Value lhs, Value rhs) {
890 return success(lhs == rhs);
891 }
892 };
893
894 /// Enable Bitmask enums for OperationEquivalence::Flags.
895 LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE();
896
897 } // namespace mlir
898
899 namespace llvm {
900 template <>
901 struct DenseMapInfo<mlir::OperationName> {
902 static mlir::OperationName getEmptyKey() {
903 void *pointer = llvm::DenseMapInfo<void *>::getEmptyKey();
904 return mlir::OperationName::getFromOpaquePointer(pointer);
905 }
906 static mlir::OperationName getTombstoneKey() {
907 void *pointer = llvm::DenseMapInfo<void *>::getTombstoneKey();
908 return mlir::OperationName::getFromOpaquePointer(pointer);
909 }
910 static unsigned getHashValue(mlir::OperationName val) {
911 return DenseMapInfo<void *>::getHashValue(val.getAsOpaquePointer());
912 }
913 static bool isEqual(mlir::OperationName lhs, mlir::OperationName rhs) {
914 return lhs == rhs;
915 }
916 };
917 template <>
918 struct DenseMapInfo<mlir::RegisteredOperationName>
919 : public DenseMapInfo<mlir::OperationName> {
920 static mlir::RegisteredOperationName getEmptyKey() {
921 void *pointer = llvm::DenseMapInfo<void *>::getEmptyKey();
922 return mlir::RegisteredOperationName::getFromOpaquePointer(pointer);
923 }
924 static mlir::RegisteredOperationName getTombstoneKey() {
925 void *pointer = llvm::DenseMapInfo<void *>::getTombstoneKey();
926 return mlir::RegisteredOperationName::getFromOpaquePointer(pointer);
927 }
928 };
929
930 template <>
931 struct PointerLikeTypeTraits<mlir::OperationName> {
932 static inline void *getAsVoidPointer(mlir::OperationName I) {
933 return const_cast<void *>(I.getAsOpaquePointer());
934 }
935 static inline mlir::OperationName getFromVoidPointer(void *P) {
936 return mlir::OperationName::getFromOpaquePointer(P);
937 }
938 static constexpr int NumLowBitsAvailable =
939 PointerLikeTypeTraits<void *>::NumLowBitsAvailable;
940 };
941 template <>
942 struct PointerLikeTypeTraits<mlir::RegisteredOperationName>
943 : public PointerLikeTypeTraits<mlir::OperationName> {
944 static inline mlir::RegisteredOperationName getFromVoidPointer(void *P) {
945 return mlir::RegisteredOperationName::getFromOpaquePointer(P);
946 }
947 };
948
949 } // namespace llvm
950
951 #endif
952