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/Attributes.h" |
18 | #include "mlir/IR/BlockSupport.h" |
19 | #include "mlir/IR/BuiltinAttributes.h" |
20 | #include "mlir/IR/Diagnostics.h" |
21 | #include "mlir/IR/DialectRegistry.h" |
22 | #include "mlir/IR/Location.h" |
23 | #include "mlir/IR/TypeRange.h" |
24 | #include "mlir/IR/Types.h" |
25 | #include "mlir/IR/Value.h" |
26 | #include "mlir/Support/InterfaceSupport.h" |
27 | #include "llvm/ADT/BitmaskEnum.h" |
28 | #include "llvm/ADT/PointerUnion.h" |
29 | #include "llvm/ADT/STLFunctionalExtras.h" |
30 | #include "llvm/Support/ErrorHandling.h" |
31 | #include "llvm/Support/PointerLikeTypeTraits.h" |
32 | #include "llvm/Support/TrailingObjects.h" |
33 | #include <memory> |
34 | #include <optional> |
35 | |
36 | namespace llvm { |
37 | class BitVector; |
38 | } // namespace llvm |
39 | |
40 | namespace mlir { |
41 | class Dialect; |
42 | class DictionaryAttr; |
43 | class ElementsAttr; |
44 | struct EmptyProperties; |
45 | class MutableOperandRangeRange; |
46 | class NamedAttrList; |
47 | class Operation; |
48 | struct OperationState; |
49 | class OpAsmParser; |
50 | class OpAsmPrinter; |
51 | class OperandRange; |
52 | class OperandRangeRange; |
53 | class OpFoldResult; |
54 | class Pattern; |
55 | class Region; |
56 | class ResultRange; |
57 | class RewritePattern; |
58 | class RewritePatternSet; |
59 | class Type; |
60 | class Value; |
61 | class ValueRange; |
62 | template <typename ValueRangeT> |
63 | class ValueTypeRange; |
64 | |
65 | //===----------------------------------------------------------------------===// |
66 | // OpaqueProperties |
67 | //===----------------------------------------------------------------------===// |
68 | |
69 | /// Simple wrapper around a void* in order to express generically how to pass |
70 | /// in op properties through APIs. |
71 | class OpaqueProperties { |
72 | public: |
73 | OpaqueProperties(void *prop) : properties(prop) {} |
74 | operator bool() const { return properties != nullptr; } |
75 | template <typename Dest> |
76 | Dest as() const { |
77 | return static_cast<Dest>(const_cast<void *>(properties)); |
78 | } |
79 | |
80 | private: |
81 | void *properties; |
82 | }; |
83 | |
84 | //===----------------------------------------------------------------------===// |
85 | // OperationName |
86 | //===----------------------------------------------------------------------===// |
87 | |
88 | class OperationName { |
89 | public: |
90 | using FoldHookFn = llvm::unique_function<LogicalResult( |
91 | Operation *, ArrayRef<Attribute>, SmallVectorImpl<OpFoldResult> &) const>; |
92 | using HasTraitFn = llvm::unique_function<bool(TypeID) const>; |
93 | using ParseAssemblyFn = |
94 | llvm::unique_function<ParseResult(OpAsmParser &, OperationState &)>; |
95 | // Note: RegisteredOperationName is passed as reference here as the derived |
96 | // class is defined below. |
97 | using PopulateDefaultAttrsFn = |
98 | llvm::unique_function<void(const OperationName &, NamedAttrList &) const>; |
99 | using PrintAssemblyFn = |
100 | llvm::unique_function<void(Operation *, OpAsmPrinter &, StringRef) const>; |
101 | using VerifyInvariantsFn = |
102 | llvm::unique_function<LogicalResult(Operation *) const>; |
103 | using VerifyRegionInvariantsFn = |
104 | llvm::unique_function<LogicalResult(Operation *) const>; |
105 | |
106 | /// This class represents a type erased version of an operation. It contains |
107 | /// all of the components necessary for opaquely interacting with an |
108 | /// operation. If the operation is not registered, some of these components |
109 | /// may not be populated. |
110 | struct InterfaceConcept { |
111 | virtual ~InterfaceConcept() = default; |
112 | virtual LogicalResult foldHook(Operation *, ArrayRef<Attribute>, |
113 | SmallVectorImpl<OpFoldResult> &) = 0; |
114 | virtual void getCanonicalizationPatterns(RewritePatternSet &, |
115 | MLIRContext *) = 0; |
116 | virtual bool hasTrait(TypeID) = 0; |
117 | virtual OperationName::ParseAssemblyFn getParseAssemblyFn() = 0; |
118 | virtual void populateDefaultAttrs(const OperationName &, |
119 | NamedAttrList &) = 0; |
120 | virtual void printAssembly(Operation *, OpAsmPrinter &, StringRef) = 0; |
121 | virtual LogicalResult verifyInvariants(Operation *) = 0; |
122 | virtual LogicalResult verifyRegionInvariants(Operation *) = 0; |
123 | /// Implementation for properties |
124 | virtual std::optional<Attribute> getInherentAttr(Operation *, |
125 | StringRef name) = 0; |
126 | virtual void setInherentAttr(Operation *op, StringAttr name, |
127 | Attribute value) = 0; |
128 | virtual void populateInherentAttrs(Operation *op, NamedAttrList &attrs) = 0; |
129 | virtual LogicalResult |
130 | verifyInherentAttrs(OperationName opName, NamedAttrList &attributes, |
131 | function_ref<InFlightDiagnostic()> emitError) = 0; |
132 | virtual int getOpPropertyByteSize() = 0; |
133 | virtual void initProperties(OperationName opName, OpaqueProperties storage, |
134 | OpaqueProperties init) = 0; |
135 | virtual void deleteProperties(OpaqueProperties) = 0; |
136 | virtual void populateDefaultProperties(OperationName opName, |
137 | OpaqueProperties properties) = 0; |
138 | virtual LogicalResult |
139 | setPropertiesFromAttr(OperationName, OpaqueProperties, Attribute, |
140 | function_ref<InFlightDiagnostic()> emitError) = 0; |
141 | virtual Attribute getPropertiesAsAttr(Operation *) = 0; |
142 | virtual void copyProperties(OpaqueProperties, OpaqueProperties) = 0; |
143 | virtual bool compareProperties(OpaqueProperties, OpaqueProperties) = 0; |
144 | virtual llvm::hash_code hashProperties(OpaqueProperties) = 0; |
145 | }; |
146 | |
147 | public: |
148 | class Impl : public InterfaceConcept { |
149 | public: |
150 | Impl(StringRef, Dialect *dialect, TypeID typeID, |
151 | detail::InterfaceMap interfaceMap); |
152 | Impl(StringAttr name, Dialect *dialect, TypeID typeID, |
153 | detail::InterfaceMap interfaceMap) |
154 | : name(name), typeID(typeID), dialect(dialect), |
155 | interfaceMap(std::move(interfaceMap)) {} |
156 | |
157 | /// Returns true if this is a registered operation. |
158 | bool isRegistered() const { return typeID != TypeID::get<void>(); } |
159 | detail::InterfaceMap &getInterfaceMap() { return interfaceMap; } |
160 | Dialect *getDialect() const { return dialect; } |
161 | StringAttr getName() const { return name; } |
162 | TypeID getTypeID() const { return typeID; } |
163 | ArrayRef<StringAttr> getAttributeNames() const { return attributeNames; } |
164 | |
165 | protected: |
166 | //===------------------------------------------------------------------===// |
167 | // Registered Operation Info |
168 | |
169 | /// The name of the operation. |
170 | StringAttr name; |
171 | |
172 | /// The unique identifier of the derived Op class. |
173 | TypeID typeID; |
174 | |
175 | /// The following fields are only populated when the operation is |
176 | /// registered. |
177 | |
178 | /// This is the dialect that this operation belongs to. |
179 | Dialect *dialect; |
180 | |
181 | /// A map of interfaces that were registered to this operation. |
182 | detail::InterfaceMap interfaceMap; |
183 | |
184 | /// A list of attribute names registered to this operation in StringAttr |
185 | /// form. This allows for operation classes to use StringAttr for attribute |
186 | /// lookup/creation/etc., as opposed to raw strings. |
187 | ArrayRef<StringAttr> attributeNames; |
188 | |
189 | friend class RegisteredOperationName; |
190 | }; |
191 | |
192 | protected: |
193 | /// Default implementation for unregistered operations. |
194 | struct UnregisteredOpModel : public Impl { |
195 | using Impl::Impl; |
196 | LogicalResult foldHook(Operation *, ArrayRef<Attribute>, |
197 | SmallVectorImpl<OpFoldResult> &) final; |
198 | void getCanonicalizationPatterns(RewritePatternSet &, MLIRContext *) final; |
199 | bool hasTrait(TypeID) final; |
200 | OperationName::ParseAssemblyFn getParseAssemblyFn() final; |
201 | void populateDefaultAttrs(const OperationName &, NamedAttrList &) final; |
202 | void printAssembly(Operation *, OpAsmPrinter &, StringRef) final; |
203 | LogicalResult verifyInvariants(Operation *) final; |
204 | LogicalResult verifyRegionInvariants(Operation *) final; |
205 | /// Implementation for properties |
206 | std::optional<Attribute> getInherentAttr(Operation *op, |
207 | StringRef name) final; |
208 | void setInherentAttr(Operation *op, StringAttr name, Attribute value) final; |
209 | void populateInherentAttrs(Operation *op, NamedAttrList &attrs) final; |
210 | LogicalResult |
211 | verifyInherentAttrs(OperationName opName, NamedAttrList &attributes, |
212 | function_ref<InFlightDiagnostic()> emitError) final; |
213 | int getOpPropertyByteSize() final; |
214 | void initProperties(OperationName opName, OpaqueProperties storage, |
215 | OpaqueProperties init) final; |
216 | void deleteProperties(OpaqueProperties) final; |
217 | void populateDefaultProperties(OperationName opName, |
218 | OpaqueProperties properties) final; |
219 | LogicalResult |
220 | setPropertiesFromAttr(OperationName, OpaqueProperties, Attribute, |
221 | function_ref<InFlightDiagnostic()> emitError) final; |
222 | Attribute getPropertiesAsAttr(Operation *) final; |
223 | void copyProperties(OpaqueProperties, OpaqueProperties) final; |
224 | bool compareProperties(OpaqueProperties, OpaqueProperties) final; |
225 | llvm::hash_code hashProperties(OpaqueProperties) final; |
226 | }; |
227 | |
228 | public: |
229 | OperationName(StringRef name, MLIRContext *context); |
230 | |
231 | /// Return if this operation is registered. |
232 | bool isRegistered() const { return getImpl()->isRegistered(); } |
233 | |
234 | /// Return the unique identifier of the derived Op class, or null if not |
235 | /// registered. |
236 | TypeID getTypeID() const { return getImpl()->getTypeID(); } |
237 | |
238 | /// If this operation is registered, returns the registered information, |
239 | /// std::nullopt otherwise. |
240 | std::optional<RegisteredOperationName> getRegisteredInfo() const; |
241 | |
242 | /// This hook implements a generalized folder for this operation. Operations |
243 | /// can implement this to provide simplifications rules that are applied by |
244 | /// the Builder::createOrFold API and the canonicalization pass. |
245 | /// |
246 | /// This is an intentionally limited interface - implementations of this |
247 | /// hook can only perform the following changes to the operation: |
248 | /// |
249 | /// 1. They can leave the operation alone and without changing the IR, and |
250 | /// return failure. |
251 | /// 2. They can mutate the operation in place, without changing anything |
252 | /// else in the IR. In this case, return success. |
253 | /// 3. They can return a list of existing values that can be used instead |
254 | /// of the operation. In this case, fill in the results list and return |
255 | /// success. The caller will remove the operation and use those results |
256 | /// instead. |
257 | /// |
258 | /// This allows expression of some simple in-place canonicalizations (e.g. |
259 | /// "x+0 -> x", "min(x,y,x,z) -> min(x,y,z)", "x+y-x -> y", etc), as well as |
260 | /// generalized constant folding. |
261 | LogicalResult foldHook(Operation *op, ArrayRef<Attribute> operands, |
262 | SmallVectorImpl<OpFoldResult> &results) const { |
263 | return getImpl()->foldHook(op, operands, results); |
264 | } |
265 | |
266 | /// This hook returns any canonicalization pattern rewrites that the |
267 | /// operation supports, for use by the canonicalization pass. |
268 | void getCanonicalizationPatterns(RewritePatternSet &results, |
269 | MLIRContext *context) const { |
270 | return getImpl()->getCanonicalizationPatterns(results, context); |
271 | } |
272 | |
273 | /// Returns true if the operation was registered with a particular trait, e.g. |
274 | /// hasTrait<OperandsAreSignlessIntegerLike>(). Returns false if the operation |
275 | /// is unregistered. |
276 | template <template <typename T> class Trait> |
277 | bool hasTrait() const { |
278 | return hasTrait(TypeID::get<Trait>()); |
279 | } |
280 | bool hasTrait(TypeID traitID) const { return getImpl()->hasTrait(traitID); } |
281 | |
282 | /// Returns true if the operation *might* have the provided trait. This |
283 | /// means that either the operation is unregistered, or it was registered with |
284 | /// the provide trait. |
285 | template <template <typename T> class Trait> |
286 | bool mightHaveTrait() const { |
287 | return mightHaveTrait(TypeID::get<Trait>()); |
288 | } |
289 | bool mightHaveTrait(TypeID traitID) const { |
290 | return !isRegistered() || getImpl()->hasTrait(traitID); |
291 | } |
292 | |
293 | /// Return the static hook for parsing this operation assembly. |
294 | ParseAssemblyFn getParseAssemblyFn() const { |
295 | return getImpl()->getParseAssemblyFn(); |
296 | } |
297 | |
298 | /// This hook implements the method to populate defaults attributes that are |
299 | /// unset. |
300 | void populateDefaultAttrs(NamedAttrList &attrs) const { |
301 | getImpl()->populateDefaultAttrs(*this, attrs); |
302 | } |
303 | |
304 | /// This hook implements the AsmPrinter for this operation. |
305 | void printAssembly(Operation *op, OpAsmPrinter &p, |
306 | StringRef defaultDialect) const { |
307 | return getImpl()->printAssembly(op, p, defaultDialect); |
308 | } |
309 | |
310 | /// These hooks implement the verifiers for this operation. It should emits |
311 | /// an error message and returns failure if a problem is detected, or |
312 | /// returns success if everything is ok. |
313 | LogicalResult verifyInvariants(Operation *op) const { |
314 | return getImpl()->verifyInvariants(op); |
315 | } |
316 | LogicalResult verifyRegionInvariants(Operation *op) const { |
317 | return getImpl()->verifyRegionInvariants(op); |
318 | } |
319 | |
320 | /// Return the list of cached attribute names registered to this operation. |
321 | /// The order of attributes cached here is unique to each type of operation, |
322 | /// and the interpretation of this attribute list should generally be driven |
323 | /// by the respective operation. In many cases, this caching removes the |
324 | /// need to use the raw string name of a known attribute. |
325 | /// |
326 | /// For example the ODS generator, with an op defining the following |
327 | /// attributes: |
328 | /// |
329 | /// let arguments = (ins I32Attr:$attr1, I32Attr:$attr2); |
330 | /// |
331 | /// ... may produce an order here of ["attr1", "attr2"]. This allows for the |
332 | /// ODS generator to directly access the cached name for a known attribute, |
333 | /// greatly simplifying the cost and complexity of attribute usage produced |
334 | /// by the generator. |
335 | /// |
336 | ArrayRef<StringAttr> getAttributeNames() const { |
337 | return getImpl()->getAttributeNames(); |
338 | } |
339 | |
340 | /// Returns an instance of the concept object for the given interface if it |
341 | /// was registered to this operation, null otherwise. This should not be used |
342 | /// directly. |
343 | template <typename T> |
344 | typename T::Concept *getInterface() const { |
345 | return getImpl()->getInterfaceMap().lookup<T>(); |
346 | } |
347 | |
348 | /// Attach the given models as implementations of the corresponding |
349 | /// interfaces for the concrete operation. |
350 | template <typename... Models> |
351 | void attachInterface() { |
352 | // Handle the case where the models resolve a promised interface. |
353 | (dialect_extension_detail::handleAdditionOfUndefinedPromisedInterface( |
354 | dialect&: *getDialect(), interfaceRequestorID: getTypeID(), interfaceID: Models::Interface::getInterfaceID()), |
355 | ...); |
356 | |
357 | getImpl()->getInterfaceMap().insertModels<Models...>(); |
358 | } |
359 | |
360 | /// Returns true if `InterfaceT` has been promised by the dialect or |
361 | /// implemented. |
362 | template <typename InterfaceT> |
363 | bool hasPromiseOrImplementsInterface() const { |
364 | return dialect_extension_detail::hasPromisedInterface( |
365 | getDialect(), getTypeID(), InterfaceT::getInterfaceID()) || |
366 | hasInterface<InterfaceT>(); |
367 | } |
368 | |
369 | /// Returns true if this operation has the given interface registered to it. |
370 | template <typename T> |
371 | bool hasInterface() const { |
372 | return hasInterface(TypeID::get<T>()); |
373 | } |
374 | bool hasInterface(TypeID interfaceID) const { |
375 | return getImpl()->getInterfaceMap().contains(interfaceID); |
376 | } |
377 | |
378 | /// Returns true if the operation *might* have the provided interface. This |
379 | /// means that either the operation is unregistered, or it was registered with |
380 | /// the provide interface. |
381 | template <typename T> |
382 | bool mightHaveInterface() const { |
383 | return mightHaveInterface(TypeID::get<T>()); |
384 | } |
385 | bool mightHaveInterface(TypeID interfaceID) const { |
386 | return !isRegistered() || hasInterface(interfaceID); |
387 | } |
388 | |
389 | /// Lookup an inherent attribute by name, this method isn't recommended |
390 | /// and may be removed in the future. |
391 | std::optional<Attribute> getInherentAttr(Operation *op, |
392 | StringRef name) const { |
393 | return getImpl()->getInherentAttr(op, name); |
394 | } |
395 | |
396 | void setInherentAttr(Operation *op, StringAttr name, Attribute value) const { |
397 | return getImpl()->setInherentAttr(op, name: name, value); |
398 | } |
399 | |
400 | void populateInherentAttrs(Operation *op, NamedAttrList &attrs) const { |
401 | return getImpl()->populateInherentAttrs(op, attrs); |
402 | } |
403 | /// This method exists for backward compatibility purpose when using |
404 | /// properties to store inherent attributes, it enables validating the |
405 | /// attributes when parsed from the older generic syntax pre-Properties. |
406 | LogicalResult |
407 | verifyInherentAttrs(NamedAttrList &attributes, |
408 | function_ref<InFlightDiagnostic()> emitError) const { |
409 | return getImpl()->verifyInherentAttrs(*this, attributes, emitError); |
410 | } |
411 | /// This hooks return the number of bytes to allocate for the op properties. |
412 | int getOpPropertyByteSize() const { |
413 | return getImpl()->getOpPropertyByteSize(); |
414 | } |
415 | |
416 | /// This hooks destroy the op properties. |
417 | void destroyOpProperties(OpaqueProperties properties) const { |
418 | getImpl()->deleteProperties(properties); |
419 | } |
420 | |
421 | /// Initialize the op properties. |
422 | void initOpProperties(OpaqueProperties storage, OpaqueProperties init) const { |
423 | getImpl()->initProperties(*this, storage, init); |
424 | } |
425 | |
426 | /// Set the default values on the ODS attribute in the properties. |
427 | void populateDefaultProperties(OpaqueProperties properties) const { |
428 | getImpl()->populateDefaultProperties(*this, properties); |
429 | } |
430 | |
431 | /// Return the op properties converted to an Attribute. |
432 | Attribute getOpPropertiesAsAttribute(Operation *op) const { |
433 | return getImpl()->getPropertiesAsAttr(op); |
434 | } |
435 | |
436 | /// Define the op properties from the provided Attribute. |
437 | LogicalResult setOpPropertiesFromAttribute( |
438 | OperationName opName, OpaqueProperties properties, Attribute attr, |
439 | function_ref<InFlightDiagnostic()> emitError) const { |
440 | return getImpl()->setPropertiesFromAttr(opName, properties, attr, |
441 | emitError); |
442 | } |
443 | |
444 | void copyOpProperties(OpaqueProperties lhs, OpaqueProperties rhs) const { |
445 | return getImpl()->copyProperties(lhs, rhs); |
446 | } |
447 | |
448 | bool compareOpProperties(OpaqueProperties lhs, OpaqueProperties rhs) const { |
449 | return getImpl()->compareProperties(lhs, rhs); |
450 | } |
451 | |
452 | llvm::hash_code hashOpProperties(OpaqueProperties properties) const { |
453 | return getImpl()->hashProperties(properties); |
454 | } |
455 | |
456 | /// Return the dialect this operation is registered to if the dialect is |
457 | /// loaded in the context, or nullptr if the dialect isn't loaded. |
458 | Dialect *getDialect() const { |
459 | return isRegistered() ? getImpl()->getDialect() |
460 | : getImpl()->getName().getReferencedDialect(); |
461 | } |
462 | |
463 | /// Return the name of the dialect this operation is registered to. |
464 | StringRef getDialectNamespace() const; |
465 | |
466 | /// Return the operation name with dialect name stripped, if it has one. |
467 | StringRef stripDialect() const { return getStringRef().split(Separator: '.').second; } |
468 | |
469 | /// Return the context this operation is associated with. |
470 | MLIRContext *getContext() { return getIdentifier().getContext(); } |
471 | |
472 | /// Return the name of this operation. This always succeeds. |
473 | StringRef getStringRef() const { return getIdentifier(); } |
474 | |
475 | /// Return the name of this operation as a StringAttr. |
476 | StringAttr getIdentifier() const { return getImpl()->getName(); } |
477 | |
478 | void print(raw_ostream &os) const; |
479 | void dump() const; |
480 | |
481 | /// Represent the operation name as an opaque pointer. (Used to support |
482 | /// PointerLikeTypeTraits). |
483 | void *getAsOpaquePointer() const { return const_cast<Impl *>(impl); } |
484 | static OperationName getFromOpaquePointer(const void *pointer) { |
485 | return OperationName( |
486 | const_cast<Impl *>(reinterpret_cast<const Impl *>(pointer))); |
487 | } |
488 | |
489 | bool operator==(const OperationName &rhs) const { return impl == rhs.impl; } |
490 | bool operator!=(const OperationName &rhs) const { return !(*this == rhs); } |
491 | |
492 | protected: |
493 | OperationName(Impl *impl) : impl(impl) {} |
494 | Impl *getImpl() const { return impl; } |
495 | void setImpl(Impl *rhs) { impl = rhs; } |
496 | |
497 | private: |
498 | /// The internal implementation of the operation name. |
499 | Impl *impl = nullptr; |
500 | |
501 | /// Allow access to the Impl struct. |
502 | friend MLIRContextImpl; |
503 | friend DenseMapInfo<mlir::OperationName>; |
504 | friend DenseMapInfo<mlir::RegisteredOperationName>; |
505 | }; |
506 | |
507 | inline raw_ostream &operator<<(raw_ostream &os, OperationName info) { |
508 | info.print(os); |
509 | return os; |
510 | } |
511 | |
512 | // Make operation names hashable. |
513 | inline llvm::hash_code hash_value(OperationName arg) { |
514 | return llvm::hash_value(ptr: arg.getAsOpaquePointer()); |
515 | } |
516 | |
517 | //===----------------------------------------------------------------------===// |
518 | // RegisteredOperationName |
519 | //===----------------------------------------------------------------------===// |
520 | |
521 | /// This is a "type erased" representation of a registered operation. This |
522 | /// should only be used by things like the AsmPrinter and other things that need |
523 | /// to be parameterized by generic operation hooks. Most user code should use |
524 | /// the concrete operation types. |
525 | class RegisteredOperationName : public OperationName { |
526 | public: |
527 | /// Implementation of the InterfaceConcept for operation APIs that forwarded |
528 | /// to a concrete op implementation. |
529 | template <typename ConcreteOp> |
530 | struct Model : public Impl { |
531 | Model(Dialect *dialect) |
532 | : Impl(ConcreteOp::getOperationName(), dialect, |
533 | TypeID::get<ConcreteOp>(), ConcreteOp::getInterfaceMap()) {} |
534 | LogicalResult foldHook(Operation *op, ArrayRef<Attribute> attrs, |
535 | SmallVectorImpl<OpFoldResult> &results) final { |
536 | return ConcreteOp::getFoldHookFn()(op, attrs, results); |
537 | } |
538 | void getCanonicalizationPatterns(RewritePatternSet &set, |
539 | MLIRContext *context) final { |
540 | ConcreteOp::getCanonicalizationPatterns(set, context); |
541 | } |
542 | bool hasTrait(TypeID id) final { return ConcreteOp::getHasTraitFn()(id); } |
543 | OperationName::ParseAssemblyFn getParseAssemblyFn() final { |
544 | return ConcreteOp::parse; |
545 | } |
546 | void populateDefaultAttrs(const OperationName &name, |
547 | NamedAttrList &attrs) final { |
548 | ConcreteOp::populateDefaultAttrs(name, attrs); |
549 | } |
550 | void printAssembly(Operation *op, OpAsmPrinter &printer, |
551 | StringRef name) final { |
552 | ConcreteOp::getPrintAssemblyFn()(op, printer, name); |
553 | } |
554 | LogicalResult verifyInvariants(Operation *op) final { |
555 | return ConcreteOp::getVerifyInvariantsFn()(op); |
556 | } |
557 | LogicalResult verifyRegionInvariants(Operation *op) final { |
558 | return ConcreteOp::getVerifyRegionInvariantsFn()(op); |
559 | } |
560 | |
561 | /// Implementation for "Properties" |
562 | |
563 | using Properties = std::remove_reference_t< |
564 | decltype(std::declval<ConcreteOp>().getProperties())>; |
565 | |
566 | std::optional<Attribute> getInherentAttr(Operation *op, |
567 | StringRef name) final { |
568 | if constexpr (hasProperties) { |
569 | auto concreteOp = cast<ConcreteOp>(op); |
570 | return ConcreteOp::getInherentAttr(concreteOp->getContext(), |
571 | concreteOp.getProperties(), name); |
572 | } |
573 | // If the op does not have support for properties, we dispatch back to the |
574 | // dictionnary of discardable attributes for now. |
575 | return cast<ConcreteOp>(op)->getDiscardableAttr(name); |
576 | } |
577 | void setInherentAttr(Operation *op, StringAttr name, |
578 | Attribute value) final { |
579 | if constexpr (hasProperties) { |
580 | auto concreteOp = cast<ConcreteOp>(op); |
581 | return ConcreteOp::setInherentAttr(concreteOp.getProperties(), name, |
582 | value); |
583 | } |
584 | // If the op does not have support for properties, we dispatch back to the |
585 | // dictionnary of discardable attributes for now. |
586 | return cast<ConcreteOp>(op)->setDiscardableAttr(name, value); |
587 | } |
588 | void populateInherentAttrs(Operation *op, NamedAttrList &attrs) final { |
589 | if constexpr (hasProperties) { |
590 | auto concreteOp = cast<ConcreteOp>(op); |
591 | ConcreteOp::populateInherentAttrs(concreteOp->getContext(), |
592 | concreteOp.getProperties(), attrs); |
593 | } |
594 | } |
595 | LogicalResult |
596 | verifyInherentAttrs(OperationName opName, NamedAttrList &attributes, |
597 | function_ref<InFlightDiagnostic()> emitError) final { |
598 | if constexpr (hasProperties) |
599 | return ConcreteOp::verifyInherentAttrs(opName, attributes, emitError); |
600 | return success(); |
601 | } |
602 | // Detect if the concrete operation defined properties. |
603 | static constexpr bool hasProperties = !std::is_same_v< |
604 | typename ConcreteOp::template InferredProperties<ConcreteOp>, |
605 | EmptyProperties>; |
606 | |
607 | int getOpPropertyByteSize() final { |
608 | if constexpr (hasProperties) |
609 | return sizeof(Properties); |
610 | return 0; |
611 | } |
612 | void initProperties(OperationName opName, OpaqueProperties storage, |
613 | OpaqueProperties init) final { |
614 | using Properties = |
615 | typename ConcreteOp::template InferredProperties<ConcreteOp>; |
616 | if (init) |
617 | new (storage.as<Properties *>()) Properties(*init.as<Properties *>()); |
618 | else |
619 | new (storage.as<Properties *>()) Properties(); |
620 | if constexpr (hasProperties) |
621 | ConcreteOp::populateDefaultProperties(opName, |
622 | *storage.as<Properties *>()); |
623 | } |
624 | void deleteProperties(OpaqueProperties prop) final { |
625 | prop.as<Properties *>()->~Properties(); |
626 | } |
627 | void populateDefaultProperties(OperationName opName, |
628 | OpaqueProperties properties) final { |
629 | if constexpr (hasProperties) |
630 | ConcreteOp::populateDefaultProperties(opName, |
631 | *properties.as<Properties *>()); |
632 | } |
633 | |
634 | LogicalResult |
635 | setPropertiesFromAttr(OperationName opName, OpaqueProperties properties, |
636 | Attribute attr, |
637 | function_ref<InFlightDiagnostic()> emitError) final { |
638 | if constexpr (hasProperties) { |
639 | auto p = properties.as<Properties *>(); |
640 | return ConcreteOp::setPropertiesFromAttr(*p, attr, emitError); |
641 | } |
642 | emitError() << "this operation does not support properties"; |
643 | return failure(); |
644 | } |
645 | Attribute getPropertiesAsAttr(Operation *op) final { |
646 | if constexpr (hasProperties) { |
647 | auto concreteOp = cast<ConcreteOp>(op); |
648 | return ConcreteOp::getPropertiesAsAttr(concreteOp->getContext(), |
649 | concreteOp.getProperties()); |
650 | } |
651 | return {}; |
652 | } |
653 | bool compareProperties(OpaqueProperties lhs, OpaqueProperties rhs) final { |
654 | if constexpr (hasProperties) { |
655 | return *lhs.as<Properties *>() == *rhs.as<Properties *>(); |
656 | } else { |
657 | return true; |
658 | } |
659 | } |
660 | void copyProperties(OpaqueProperties lhs, OpaqueProperties rhs) final { |
661 | *lhs.as<Properties *>() = *rhs.as<Properties *>(); |
662 | } |
663 | llvm::hash_code hashProperties(OpaqueProperties prop) final { |
664 | if constexpr (hasProperties) |
665 | return ConcreteOp::computePropertiesHash(*prop.as<Properties *>()); |
666 | |
667 | return {}; |
668 | } |
669 | }; |
670 | |
671 | /// Lookup the registered operation information for the given operation. |
672 | /// Returns std::nullopt if the operation isn't registered. |
673 | static std::optional<RegisteredOperationName> lookup(StringRef name, |
674 | MLIRContext *ctx); |
675 | |
676 | /// Lookup the registered operation information for the given operation. |
677 | /// Returns std::nullopt if the operation isn't registered. |
678 | static std::optional<RegisteredOperationName> lookup(TypeID typeID, |
679 | MLIRContext *ctx); |
680 | |
681 | /// Register a new operation in a Dialect object. |
682 | /// This constructor is used by Dialect objects when they register the list |
683 | /// of operations they contain. |
684 | template <typename T> |
685 | static void insert(Dialect &dialect) { |
686 | insert(std::make_unique<Model<T>>(&dialect), T::getAttributeNames()); |
687 | } |
688 | /// The use of this method is in general discouraged in favor of |
689 | /// 'insert<CustomOp>(dialect)'. |
690 | static void insert(std::unique_ptr<OperationName::Impl> ownedImpl, |
691 | ArrayRef<StringRef> attrNames); |
692 | |
693 | /// Return the dialect this operation is registered to. |
694 | Dialect &getDialect() const { return *getImpl()->getDialect(); } |
695 | |
696 | /// Represent the operation name as an opaque pointer. (Used to support |
697 | /// PointerLikeTypeTraits). |
698 | static RegisteredOperationName getFromOpaquePointer(const void *pointer) { |
699 | return RegisteredOperationName( |
700 | const_cast<Impl *>(reinterpret_cast<const Impl *>(pointer))); |
701 | } |
702 | |
703 | private: |
704 | RegisteredOperationName(Impl *impl) : OperationName(impl) {} |
705 | |
706 | /// Allow access to the constructor. |
707 | friend OperationName; |
708 | }; |
709 | |
710 | inline std::optional<RegisteredOperationName> |
711 | OperationName::getRegisteredInfo() const { |
712 | return isRegistered() ? RegisteredOperationName(impl) |
713 | : std::optional<RegisteredOperationName>(); |
714 | } |
715 | |
716 | //===----------------------------------------------------------------------===// |
717 | // Attribute Dictionary-Like Interface |
718 | //===----------------------------------------------------------------------===// |
719 | |
720 | /// Attribute collections provide a dictionary-like interface. Define common |
721 | /// lookup functions. |
722 | namespace impl { |
723 | |
724 | /// Unsorted string search or identifier lookups are linear scans. |
725 | template <typename IteratorT, typename NameT> |
726 | std::pair<IteratorT, bool> findAttrUnsorted(IteratorT first, IteratorT last, |
727 | NameT name) { |
728 | for (auto it = first; it != last; ++it) |
729 | if (it->getName() == name) |
730 | return {it, true}; |
731 | return {last, false}; |
732 | } |
733 | |
734 | /// Using llvm::lower_bound requires an extra string comparison to check whether |
735 | /// the returned iterator points to the found element or whether it indicates |
736 | /// the lower bound. Skip this redundant comparison by checking if `compare == |
737 | /// 0` during the binary search. |
738 | template <typename IteratorT> |
739 | std::pair<IteratorT, bool> findAttrSorted(IteratorT first, IteratorT last, |
740 | StringRef name) { |
741 | ptrdiff_t length = std::distance(first, last); |
742 | |
743 | while (length > 0) { |
744 | ptrdiff_t half = length / 2; |
745 | IteratorT mid = first + half; |
746 | int compare = mid->getName().strref().compare(name); |
747 | if (compare < 0) { |
748 | first = mid + 1; |
749 | length = length - half - 1; |
750 | } else if (compare > 0) { |
751 | length = half; |
752 | } else { |
753 | return {mid, true}; |
754 | } |
755 | } |
756 | return {first, false}; |
757 | } |
758 | |
759 | /// StringAttr lookups on large attribute lists will switch to string binary |
760 | /// search. String binary searches become significantly faster than linear scans |
761 | /// with the identifier when the attribute list becomes very large. |
762 | template <typename IteratorT> |
763 | std::pair<IteratorT, bool> findAttrSorted(IteratorT first, IteratorT last, |
764 | StringAttr name) { |
765 | constexpr unsigned kSmallAttributeList = 16; |
766 | if (std::distance(first, last) > kSmallAttributeList) |
767 | return findAttrSorted(first, last, name.strref()); |
768 | return findAttrUnsorted(first, last, name); |
769 | } |
770 | |
771 | /// Get an attribute from a sorted range of named attributes. Returns null if |
772 | /// the attribute was not found. |
773 | template <typename IteratorT, typename NameT> |
774 | Attribute getAttrFromSortedRange(IteratorT first, IteratorT last, NameT name) { |
775 | std::pair<IteratorT, bool> result = findAttrSorted(first, last, name); |
776 | return result.second ? result.first->getValue() : Attribute(); |
777 | } |
778 | |
779 | /// Get an attribute from a sorted range of named attributes. Returns |
780 | /// std::nullopt if the attribute was not found. |
781 | template <typename IteratorT, typename NameT> |
782 | std::optional<NamedAttribute> |
783 | getNamedAttrFromSortedRange(IteratorT first, IteratorT last, NameT name) { |
784 | std::pair<IteratorT, bool> result = findAttrSorted(first, last, name); |
785 | return result.second ? *result.first : std::optional<NamedAttribute>(); |
786 | } |
787 | |
788 | } // namespace impl |
789 | |
790 | //===----------------------------------------------------------------------===// |
791 | // NamedAttrList |
792 | //===----------------------------------------------------------------------===// |
793 | |
794 | /// NamedAttrList is array of NamedAttributes that tracks whether it is sorted |
795 | /// and does some basic work to remain sorted. |
796 | class NamedAttrList { |
797 | public: |
798 | using iterator = SmallVectorImpl<NamedAttribute>::iterator; |
799 | using const_iterator = SmallVectorImpl<NamedAttribute>::const_iterator; |
800 | using reference = NamedAttribute &; |
801 | using const_reference = const NamedAttribute &; |
802 | using size_type = size_t; |
803 | |
804 | NamedAttrList() : dictionarySorted({}, true) {} |
805 | NamedAttrList(std::nullopt_t none) : NamedAttrList() {} |
806 | NamedAttrList(ArrayRef<NamedAttribute> attributes); |
807 | NamedAttrList(DictionaryAttr attributes); |
808 | NamedAttrList(const_iterator inStart, const_iterator inEnd); |
809 | |
810 | template <typename Container> |
811 | NamedAttrList(const Container &vec) |
812 | : NamedAttrList(ArrayRef<NamedAttribute>(vec)) {} |
813 | |
814 | bool operator!=(const NamedAttrList &other) const { |
815 | return !(*this == other); |
816 | } |
817 | bool operator==(const NamedAttrList &other) const { |
818 | return attrs == other.attrs; |
819 | } |
820 | |
821 | /// Add an attribute with the specified name. |
822 | void append(StringRef name, Attribute attr) { |
823 | append(attr: NamedAttribute(name, attr)); |
824 | } |
825 | |
826 | /// Add an attribute with the specified name. |
827 | void append(StringAttr name, Attribute attr) { |
828 | append(attr: NamedAttribute(name, attr)); |
829 | } |
830 | |
831 | /// Append the given named attribute. |
832 | void append(NamedAttribute attr) { push_back(newAttribute: attr); } |
833 | |
834 | /// Add an array of named attributes. |
835 | template <typename RangeT> |
836 | void append(RangeT &&newAttributes) { |
837 | append(std::begin(newAttributes), std::end(newAttributes)); |
838 | } |
839 | |
840 | /// Add a range of named attributes. |
841 | template <typename IteratorT, |
842 | typename = std::enable_if_t<std::is_convertible< |
843 | typename std::iterator_traits<IteratorT>::iterator_category, |
844 | std::input_iterator_tag>::value>> |
845 | void append(IteratorT inStart, IteratorT inEnd) { |
846 | // TODO: expand to handle case where values appended are in order & after |
847 | // end of current list. |
848 | dictionarySorted.setPointerAndInt(PtrVal: nullptr, IntVal: false); |
849 | attrs.append(inStart, inEnd); |
850 | } |
851 | |
852 | /// Replaces the attributes with new list of attributes. |
853 | void assign(const_iterator inStart, const_iterator inEnd); |
854 | |
855 | /// Replaces the attributes with new list of attributes. |
856 | void assign(ArrayRef<NamedAttribute> range) { |
857 | assign(inStart: range.begin(), inEnd: range.end()); |
858 | } |
859 | |
860 | void clear() { |
861 | attrs.clear(); |
862 | dictionarySorted.setPointerAndInt(PtrVal: nullptr, IntVal: false); |
863 | } |
864 | |
865 | bool empty() const { return attrs.empty(); } |
866 | |
867 | void reserve(size_type N) { attrs.reserve(N); } |
868 | |
869 | /// Add an attribute with the specified name. |
870 | void push_back(NamedAttribute newAttribute); |
871 | |
872 | /// Pop last element from list. |
873 | void pop_back() { attrs.pop_back(); } |
874 | |
875 | /// Returns an entry with a duplicate name the list, if it exists, else |
876 | /// returns std::nullopt. |
877 | std::optional<NamedAttribute> findDuplicate() const; |
878 | |
879 | /// Return a dictionary attribute for the underlying dictionary. This will |
880 | /// return an empty dictionary attribute if empty rather than null. |
881 | DictionaryAttr getDictionary(MLIRContext *context) const; |
882 | |
883 | /// Return all of the attributes on this operation. |
884 | ArrayRef<NamedAttribute> getAttrs() const; |
885 | |
886 | /// Return the specified attribute if present, null otherwise. |
887 | Attribute get(StringAttr name) const; |
888 | Attribute get(StringRef name) const; |
889 | |
890 | /// Return the specified named attribute if present, std::nullopt otherwise. |
891 | std::optional<NamedAttribute> getNamed(StringRef name) const; |
892 | std::optional<NamedAttribute> getNamed(StringAttr name) const; |
893 | |
894 | /// If the an attribute exists with the specified name, change it to the new |
895 | /// value. Otherwise, add a new attribute with the specified name/value. |
896 | /// Returns the previous attribute value of `name`, or null if no |
897 | /// attribute previously existed with `name`. |
898 | Attribute set(StringAttr name, Attribute value); |
899 | Attribute set(StringRef name, Attribute value); |
900 | |
901 | /// Erase the attribute with the given name from the list. Return the |
902 | /// attribute that was erased, or nullptr if there was no attribute with such |
903 | /// name. |
904 | Attribute erase(StringAttr name); |
905 | Attribute erase(StringRef name); |
906 | |
907 | iterator begin() { return attrs.begin(); } |
908 | iterator end() { return attrs.end(); } |
909 | const_iterator begin() const { return attrs.begin(); } |
910 | const_iterator end() const { return attrs.end(); } |
911 | |
912 | NamedAttrList &operator=(const SmallVectorImpl<NamedAttribute> &rhs); |
913 | operator ArrayRef<NamedAttribute>() const; |
914 | |
915 | private: |
916 | /// Return whether the attributes are sorted. |
917 | bool isSorted() const { return dictionarySorted.getInt(); } |
918 | |
919 | /// Erase the attribute at the given iterator position. |
920 | Attribute eraseImpl(SmallVectorImpl<NamedAttribute>::iterator it); |
921 | |
922 | /// Lookup an attribute in the list. |
923 | template <typename AttrListT, typename NameT> |
924 | static auto findAttr(AttrListT &attrs, NameT name) { |
925 | return attrs.isSorted() |
926 | ? impl::findAttrSorted(attrs.begin(), attrs.end(), name) |
927 | : impl::findAttrUnsorted(attrs.begin(), attrs.end(), name); |
928 | } |
929 | |
930 | // These are marked mutable as they may be modified (e.g., sorted) |
931 | mutable SmallVector<NamedAttribute, 4> attrs; |
932 | // Pair with cached DictionaryAttr and status of whether attrs is sorted. |
933 | // Note: just because sorted does not mean a DictionaryAttr has been created |
934 | // but the case where there is a DictionaryAttr but attrs isn't sorted should |
935 | // not occur. |
936 | mutable llvm::PointerIntPair<Attribute, 1, bool> dictionarySorted; |
937 | }; |
938 | |
939 | //===----------------------------------------------------------------------===// |
940 | // OperationState |
941 | //===----------------------------------------------------------------------===// |
942 | |
943 | /// This represents an operation in an abstracted form, suitable for use with |
944 | /// the builder APIs. This object is a large and heavy weight object meant to |
945 | /// be used as a temporary object on the stack. It is generally unwise to put |
946 | /// this in a collection. |
947 | struct OperationState { |
948 | Location location; |
949 | OperationName name; |
950 | SmallVector<Value, 4> operands; |
951 | /// Types of the results of this operation. |
952 | SmallVector<Type, 4> types; |
953 | NamedAttrList attributes; |
954 | /// Successors of this operation and their respective operands. |
955 | SmallVector<Block *, 1> successors; |
956 | /// Regions that the op will hold. |
957 | SmallVector<std::unique_ptr<Region>, 1> regions; |
958 | |
959 | /// This Attribute is used to opaquely construct the properties of the |
960 | /// operation. If we're creating an unregistered operation, the Attribute is |
961 | /// used as-is as the Properties storage of the operation. Otherwise, the |
962 | /// operation properties are constructed opaquely using its |
963 | /// `setPropertiesFromAttr` hook. Note that `getOrAddProperties` is the |
964 | /// preferred method to construct properties from C++. |
965 | Attribute propertiesAttr; |
966 | |
967 | private: |
968 | OpaqueProperties properties = nullptr; |
969 | TypeID propertiesId; |
970 | llvm::function_ref<void(OpaqueProperties)> propertiesDeleter; |
971 | llvm::function_ref<void(OpaqueProperties, const OpaqueProperties)> |
972 | propertiesSetter; |
973 | friend class Operation; |
974 | |
975 | public: |
976 | OperationState(Location location, StringRef name); |
977 | OperationState(Location location, OperationName name); |
978 | |
979 | OperationState(Location location, OperationName name, ValueRange operands, |
980 | TypeRange types, ArrayRef<NamedAttribute> attributes = {}, |
981 | BlockRange successors = {}, |
982 | MutableArrayRef<std::unique_ptr<Region>> regions = {}); |
983 | OperationState(Location location, StringRef name, ValueRange operands, |
984 | TypeRange types, ArrayRef<NamedAttribute> attributes = {}, |
985 | BlockRange successors = {}, |
986 | MutableArrayRef<std::unique_ptr<Region>> regions = {}); |
987 | OperationState(OperationState &&other) = default; |
988 | OperationState &operator=(OperationState &&other) = default; |
989 | OperationState(const OperationState &other) = delete; |
990 | OperationState &operator=(const OperationState &other) = delete; |
991 | ~OperationState(); |
992 | |
993 | /// Get (or create) a properties of the provided type to be set on the |
994 | /// operation on creation. |
995 | template <typename T> |
996 | T &getOrAddProperties() { |
997 | if (!properties) { |
998 | T *p = new T{}; |
999 | properties = p; |
1000 | #if defined(__clang__) |
1001 | #if __has_warning("-Wdangling-assignment-gsl") |
1002 | #pragma clang diagnostic push |
1003 | // https://github.com/llvm/llvm-project/issues/126600 |
1004 | #pragma clang diagnostic ignored "-Wdangling-assignment-gsl" |
1005 | #endif |
1006 | #endif |
1007 | propertiesDeleter = [](OpaqueProperties prop) { |
1008 | delete prop.as<const T *>(); |
1009 | }; |
1010 | propertiesSetter = [](OpaqueProperties newProp, |
1011 | const OpaqueProperties prop) { |
1012 | *newProp.as<T *>() = *prop.as<const T *>(); |
1013 | }; |
1014 | #if defined(__clang__) |
1015 | #if __has_warning("-Wdangling-assignment-gsl") |
1016 | #pragma clang diagnostic pop |
1017 | #endif |
1018 | #endif |
1019 | propertiesId = TypeID::get<T>(); |
1020 | } |
1021 | assert(propertiesId == TypeID::get<T>() && "Inconsistent properties"); |
1022 | return *properties.as<T *>(); |
1023 | } |
1024 | OpaqueProperties getRawProperties() { return properties; } |
1025 | |
1026 | // Set the properties defined on this OpState on the given operation, |
1027 | // optionally emit diagnostics on error through the provided diagnostic. |
1028 | LogicalResult |
1029 | setProperties(Operation *op, |
1030 | function_ref<InFlightDiagnostic()> emitError) const; |
1031 | |
1032 | // Make `newProperties` the source of the properties that will be copied into |
1033 | // the operation. The memory referenced by `newProperties` must remain live |
1034 | // until after the `Operation` is created, at which time it may be |
1035 | // deallocated. Calls to `getOrAddProperties<>() will return references to |
1036 | // this memory. |
1037 | template <typename T> |
1038 | void useProperties(T &newProperties) { |
1039 | assert(!properties && |
1040 | "Can't provide a properties struct when one has been allocated"); |
1041 | properties = &newProperties; |
1042 | #if defined(__clang__) |
1043 | #if __has_warning("-Wdangling-assignment-gsl") |
1044 | #pragma clang diagnostic push |
1045 | // https://github.com/llvm/llvm-project/issues/126600 |
1046 | #pragma clang diagnostic ignored "-Wdangling-assignment-gsl" |
1047 | #endif |
1048 | #endif |
1049 | propertiesDeleter = [](OpaqueProperties) {}; |
1050 | propertiesSetter = [](OpaqueProperties newProp, |
1051 | const OpaqueProperties prop) { |
1052 | *newProp.as<T *>() = *prop.as<const T *>(); |
1053 | }; |
1054 | #if defined(__clang__) |
1055 | #if __has_warning("-Wdangling-assignment-gsl") |
1056 | #pragma clang diagnostic pop |
1057 | #endif |
1058 | #endif |
1059 | propertiesId = TypeID::get<T>(); |
1060 | } |
1061 | |
1062 | void addOperands(ValueRange newOperands); |
1063 | |
1064 | void addTypes(ArrayRef<Type> newTypes) { |
1065 | types.append(in_start: newTypes.begin(), in_end: newTypes.end()); |
1066 | } |
1067 | template <typename RangeT> |
1068 | std::enable_if_t<!std::is_convertible<RangeT, ArrayRef<Type>>::value> |
1069 | addTypes(RangeT &&newTypes) { |
1070 | types.append(newTypes.begin(), newTypes.end()); |
1071 | } |
1072 | |
1073 | /// Add an attribute with the specified name. |
1074 | void addAttribute(StringRef name, Attribute attr) { |
1075 | addAttribute(StringAttr::get(getContext(), name), attr); |
1076 | } |
1077 | |
1078 | /// Add an attribute with the specified name. `name` and `attr` must not be |
1079 | /// null. |
1080 | void addAttribute(StringAttr name, Attribute attr) { |
1081 | assert(name && "attribute name cannot be null"); |
1082 | assert(attr && "attribute cannot be null"); |
1083 | attributes.append(name, attr); |
1084 | } |
1085 | |
1086 | /// Add an array of named attributes. |
1087 | void addAttributes(ArrayRef<NamedAttribute> newAttributes) { |
1088 | attributes.append(newAttributes); |
1089 | } |
1090 | |
1091 | /// Adds a successor to the operation sate. `successor` must not be null. |
1092 | void addSuccessors(Block *successor) { |
1093 | assert(successor && "successor cannot be null"); |
1094 | successors.push_back(Elt: successor); |
1095 | } |
1096 | void addSuccessors(BlockRange newSuccessors); |
1097 | |
1098 | /// Create a region that should be attached to the operation. These regions |
1099 | /// can be filled in immediately without waiting for Operation to be |
1100 | /// created. When it is, the region bodies will be transferred. |
1101 | Region *addRegion(); |
1102 | |
1103 | /// Take a region that should be attached to the Operation. The body of the |
1104 | /// region will be transferred when the Operation is constructed. If the |
1105 | /// region is null, a new empty region will be attached to the Operation. |
1106 | void addRegion(std::unique_ptr<Region> &®ion); |
1107 | |
1108 | /// Take ownership of a set of regions that should be attached to the |
1109 | /// Operation. |
1110 | void addRegions(MutableArrayRef<std::unique_ptr<Region>> regions); |
1111 | |
1112 | /// Get the context held by this operation state. |
1113 | MLIRContext *getContext() const { return location->getContext(); } |
1114 | }; |
1115 | |
1116 | //===----------------------------------------------------------------------===// |
1117 | // OperandStorage |
1118 | //===----------------------------------------------------------------------===// |
1119 | |
1120 | namespace detail { |
1121 | /// This class handles the management of operation operands. Operands are |
1122 | /// stored either in a trailing array, or a dynamically resizable vector. |
1123 | class alignas(8) OperandStorage { |
1124 | public: |
1125 | OperandStorage(Operation *owner, OpOperand *trailingOperands, |
1126 | ValueRange values); |
1127 | ~OperandStorage(); |
1128 | |
1129 | /// Replace the operands contained in the storage with the ones provided in |
1130 | /// 'values'. |
1131 | void setOperands(Operation *owner, ValueRange values); |
1132 | |
1133 | /// Replace the operands beginning at 'start' and ending at 'start' + 'length' |
1134 | /// with the ones provided in 'operands'. 'operands' may be smaller or larger |
1135 | /// than the range pointed to by 'start'+'length'. |
1136 | void setOperands(Operation *owner, unsigned start, unsigned length, |
1137 | ValueRange operands); |
1138 | |
1139 | /// Erase the operands held by the storage within the given range. |
1140 | void eraseOperands(unsigned start, unsigned length); |
1141 | |
1142 | /// Erase the operands held by the storage that have their corresponding bit |
1143 | /// set in `eraseIndices`. |
1144 | void eraseOperands(const BitVector &eraseIndices); |
1145 | |
1146 | /// Get the operation operands held by the storage. |
1147 | MutableArrayRef<OpOperand> getOperands() { return {operandStorage, size()}; } |
1148 | |
1149 | /// Return the number of operands held in the storage. |
1150 | unsigned size() { return numOperands; } |
1151 | |
1152 | private: |
1153 | /// Resize the storage to the given size. Returns the array containing the new |
1154 | /// operands. |
1155 | MutableArrayRef<OpOperand> resize(Operation *owner, unsigned newSize); |
1156 | |
1157 | /// The total capacity number of operands that the storage can hold. |
1158 | unsigned capacity : 31; |
1159 | /// A flag indicating if the operand storage was dynamically allocated, as |
1160 | /// opposed to inlined into the owning operation. |
1161 | unsigned isStorageDynamic : 1; |
1162 | /// The number of operands within the storage. |
1163 | unsigned numOperands; |
1164 | /// A pointer to the operand storage. |
1165 | OpOperand *operandStorage; |
1166 | }; |
1167 | } // namespace detail |
1168 | |
1169 | //===----------------------------------------------------------------------===// |
1170 | // OpPrintingFlags |
1171 | //===----------------------------------------------------------------------===// |
1172 | |
1173 | /// Set of flags used to control the behavior of the various IR print methods |
1174 | /// (e.g. Operation::Print). |
1175 | class OpPrintingFlags { |
1176 | public: |
1177 | OpPrintingFlags(); |
1178 | OpPrintingFlags(std::nullopt_t) : OpPrintingFlags() {} |
1179 | |
1180 | /// Enables the elision of large elements attributes by printing a lexically |
1181 | /// valid but otherwise meaningless form instead of the element data. The |
1182 | /// `largeElementLimit` is used to configure what is considered to be a |
1183 | /// "large" ElementsAttr by providing an upper limit to the number of |
1184 | /// elements. |
1185 | OpPrintingFlags &elideLargeElementsAttrs(int64_t largeElementLimit = 16); |
1186 | |
1187 | /// Enables the printing of large element attributes with a hex string. The |
1188 | /// `largeElementLimit` is used to configure what is considered to be a |
1189 | /// "large" ElementsAttr by providing an upper limit to the number of |
1190 | /// elements. Use -1 to disable the hex printing. |
1191 | OpPrintingFlags & |
1192 | printLargeElementsAttrWithHex(int64_t largeElementLimit = 100); |
1193 | |
1194 | /// Enables the elision of large resources strings by omitting them from the |
1195 | /// `dialect_resources` section. The `largeResourceLimit` is used to configure |
1196 | /// what is considered to be a "large" resource by providing an upper limit to |
1197 | /// the string size. |
1198 | OpPrintingFlags &elideLargeResourceString(int64_t largeResourceLimit = 64); |
1199 | |
1200 | /// Enable or disable printing of debug information (based on `enable`). If |
1201 | /// 'prettyForm' is set to true, debug information is printed in a more |
1202 | /// readable 'pretty' form. Note: The IR generated with 'prettyForm' is not |
1203 | /// parsable. |
1204 | OpPrintingFlags &enableDebugInfo(bool enable = true, bool prettyForm = false); |
1205 | |
1206 | /// Always print operations in the generic form. |
1207 | OpPrintingFlags &printGenericOpForm(bool enable = true); |
1208 | |
1209 | /// Skip printing regions. |
1210 | OpPrintingFlags &skipRegions(bool skip = true); |
1211 | |
1212 | /// Do not verify the operation when using custom operation printers. |
1213 | OpPrintingFlags &assumeVerified(bool enable = true); |
1214 | |
1215 | /// Use local scope when printing the operation. This allows for using the |
1216 | /// printer in a more localized and thread-safe setting, but may not |
1217 | /// necessarily be identical to what the IR will look like when dumping |
1218 | /// the full module. |
1219 | OpPrintingFlags &useLocalScope(bool enable = true); |
1220 | |
1221 | /// Print users of values as comments. |
1222 | OpPrintingFlags &printValueUsers(bool enable = true); |
1223 | |
1224 | /// Print unique SSA ID numbers for values, block arguments and naming |
1225 | /// conflicts across all regions |
1226 | OpPrintingFlags &printUniqueSSAIDs(bool enable = true); |
1227 | |
1228 | /// Print SSA IDs using their NameLoc, if provided, as prefix. |
1229 | OpPrintingFlags &printNameLocAsPrefix(bool enable = true); |
1230 | |
1231 | /// Return if the given ElementsAttr should be elided. |
1232 | bool shouldElideElementsAttr(ElementsAttr attr) const; |
1233 | |
1234 | /// Return if the given ElementsAttr should be printed as hex string. |
1235 | bool shouldPrintElementsAttrWithHex(ElementsAttr attr) const; |
1236 | |
1237 | /// Return the size limit for printing large ElementsAttr. |
1238 | std::optional<int64_t> getLargeElementsAttrLimit() const; |
1239 | |
1240 | /// Return the size limit for printing large ElementsAttr as hex string. |
1241 | int64_t getLargeElementsAttrHexLimit() const; |
1242 | |
1243 | /// Return the size limit in chars for printing large resources. |
1244 | std::optional<uint64_t> getLargeResourceStringLimit() const; |
1245 | |
1246 | /// Return if debug information should be printed. |
1247 | bool shouldPrintDebugInfo() const; |
1248 | |
1249 | /// Return if debug information should be printed in the pretty form. |
1250 | bool shouldPrintDebugInfoPrettyForm() const; |
1251 | |
1252 | /// Return if operations should be printed in the generic form. |
1253 | bool shouldPrintGenericOpForm() const; |
1254 | |
1255 | /// Return if regions should be skipped. |
1256 | bool shouldSkipRegions() const; |
1257 | |
1258 | /// Return if operation verification should be skipped. |
1259 | bool shouldAssumeVerified() const; |
1260 | |
1261 | /// Return if the printer should use local scope when dumping the IR. |
1262 | bool shouldUseLocalScope() const; |
1263 | |
1264 | /// Return if the printer should print users of values. |
1265 | bool shouldPrintValueUsers() const; |
1266 | |
1267 | /// Return if printer should use unique SSA IDs. |
1268 | bool shouldPrintUniqueSSAIDs() const; |
1269 | |
1270 | /// Return if the printer should use NameLocs as prefixes when printing SSA |
1271 | /// IDs |
1272 | bool shouldUseNameLocAsPrefix() const; |
1273 | |
1274 | private: |
1275 | /// Elide large elements attributes if the number of elements is larger than |
1276 | /// the upper limit. |
1277 | std::optional<int64_t> elementsAttrElementLimit; |
1278 | |
1279 | /// Elide printing large resources based on size of string. |
1280 | std::optional<uint64_t> resourceStringCharLimit; |
1281 | |
1282 | /// Print large element attributes with hex strings if the number of elements |
1283 | /// is larger than the upper limit. |
1284 | int64_t elementsAttrHexElementLimit = 100; |
1285 | |
1286 | /// Print debug information. |
1287 | bool printDebugInfoFlag : 1; |
1288 | bool printDebugInfoPrettyFormFlag : 1; |
1289 | |
1290 | /// Print operations in the generic form. |
1291 | bool printGenericOpFormFlag : 1; |
1292 | |
1293 | /// Always skip Regions. |
1294 | bool skipRegionsFlag : 1; |
1295 | |
1296 | /// Skip operation verification. |
1297 | bool assumeVerifiedFlag : 1; |
1298 | |
1299 | /// Print operations with numberings local to the current operation. |
1300 | bool printLocalScope : 1; |
1301 | |
1302 | /// Print users of values. |
1303 | bool printValueUsersFlag : 1; |
1304 | |
1305 | /// Print unique SSA IDs for values, block arguments and naming conflicts |
1306 | bool printUniqueSSAIDsFlag : 1; |
1307 | |
1308 | /// Print SSA IDs using NameLocs as prefixes |
1309 | bool useNameLocAsPrefix : 1; |
1310 | }; |
1311 | |
1312 | //===----------------------------------------------------------------------===// |
1313 | // Operation Equivalency |
1314 | //===----------------------------------------------------------------------===// |
1315 | |
1316 | /// This class provides utilities for computing if two operations are |
1317 | /// equivalent. |
1318 | struct OperationEquivalence { |
1319 | enum Flags { |
1320 | None = 0, |
1321 | |
1322 | // When provided, the location attached to the operation are ignored. |
1323 | IgnoreLocations = 1, |
1324 | |
1325 | // When provided, the discardable attributes attached to the operation are |
1326 | // ignored. |
1327 | IgnoreDiscardableAttrs = 2, |
1328 | |
1329 | // When provided, the properties attached to the operation are ignored. |
1330 | IgnoreProperties = 4, |
1331 | |
1332 | LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ IgnoreProperties) |
1333 | }; |
1334 | |
1335 | /// Compute a hash for the given operation. |
1336 | /// The `hashOperands` and `hashResults` callbacks are expected to return a |
1337 | /// unique hash_code for a given Value. |
1338 | static llvm::hash_code computeHash( |
1339 | Operation *op, |
1340 | function_ref<llvm::hash_code(Value)> hashOperands = |
1341 | [](Value v) { return hash_value(arg: v); }, |
1342 | function_ref<llvm::hash_code(Value)> hashResults = |
1343 | [](Value v) { return hash_value(arg: v); }, |
1344 | Flags flags = Flags::None); |
1345 | |
1346 | /// Helper that can be used with `computeHash` above to ignore operation |
1347 | /// operands/result mapping. |
1348 | static llvm::hash_code ignoreHashValue(Value) { return llvm::hash_code{}; } |
1349 | /// Helper that can be used with `computeHash` above to ignore operation |
1350 | /// operands/result mapping. |
1351 | static llvm::hash_code directHashValue(Value v) { return hash_value(arg: v); } |
1352 | |
1353 | /// Compare two operations (including their regions) and return if they are |
1354 | /// equivalent. |
1355 | /// |
1356 | /// * `checkEquivalent` is a callback to check if two values are equivalent. |
1357 | /// For two operations to be equivalent, their operands must be the same SSA |
1358 | /// value or this callback must return `success`. |
1359 | /// * `markEquivalent` is a callback to inform the caller that the analysis |
1360 | /// determined that two values are equivalent. |
1361 | /// * `checkCommutativeEquivalent` is an optional callback to check for |
1362 | /// equivalence across two ranges for a commutative operation. If not passed |
1363 | /// in, then equivalence is checked pairwise. This callback is needed to be |
1364 | /// able to query the optional equivalence classes. |
1365 | /// |
1366 | /// Note: Additional information regarding value equivalence can be injected |
1367 | /// into the analysis via `checkEquivalent`. Typically, callers may want |
1368 | /// values that were determined to be equivalent as per `markEquivalent` to be |
1369 | /// reflected in `checkEquivalent`, unless `exactValueMatch` or a different |
1370 | /// equivalence relationship is desired. |
1371 | static bool |
1372 | isEquivalentTo(Operation *lhs, Operation *rhs, |
1373 | function_ref<LogicalResult(Value, Value)> checkEquivalent, |
1374 | function_ref<void(Value, Value)> markEquivalent = nullptr, |
1375 | Flags flags = Flags::None, |
1376 | function_ref<LogicalResult(ValueRange, ValueRange)> |
1377 | checkCommutativeEquivalent = nullptr); |
1378 | |
1379 | /// Compare two operations and return if they are equivalent. |
1380 | static bool isEquivalentTo(Operation *lhs, Operation *rhs, Flags flags); |
1381 | |
1382 | /// Compare two regions (including their subregions) and return if they are |
1383 | /// equivalent. See also `isEquivalentTo` for details. |
1384 | static bool isRegionEquivalentTo( |
1385 | Region *lhs, Region *rhs, |
1386 | function_ref<LogicalResult(Value, Value)> checkEquivalent, |
1387 | function_ref<void(Value, Value)> markEquivalent, |
1388 | OperationEquivalence::Flags flags, |
1389 | function_ref<LogicalResult(ValueRange, ValueRange)> |
1390 | checkCommutativeEquivalent = nullptr); |
1391 | |
1392 | /// Compare two regions and return if they are equivalent. |
1393 | static bool isRegionEquivalentTo(Region *lhs, Region *rhs, |
1394 | OperationEquivalence::Flags flags); |
1395 | |
1396 | /// Helper that can be used with `isEquivalentTo` above to consider ops |
1397 | /// equivalent even if their operands are not equivalent. |
1398 | static LogicalResult ignoreValueEquivalence(Value lhs, Value rhs) { |
1399 | return success(); |
1400 | } |
1401 | /// Helper that can be used with `isEquivalentTo` above to consider ops |
1402 | /// equivalent only if their operands are the exact same SSA values. |
1403 | static LogicalResult exactValueMatch(Value lhs, Value rhs) { |
1404 | return success(IsSuccess: lhs == rhs); |
1405 | } |
1406 | }; |
1407 | |
1408 | /// Enable Bitmask enums for OperationEquivalence::Flags. |
1409 | LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE(); |
1410 | |
1411 | //===----------------------------------------------------------------------===// |
1412 | // OperationFingerPrint |
1413 | //===----------------------------------------------------------------------===// |
1414 | |
1415 | /// A unique fingerprint for a specific operation, and all of it's internal |
1416 | /// operations (if `includeNested` is set). |
1417 | class OperationFingerPrint { |
1418 | public: |
1419 | OperationFingerPrint(Operation *topOp, bool includeNested = true); |
1420 | OperationFingerPrint(const OperationFingerPrint &) = default; |
1421 | OperationFingerPrint &operator=(const OperationFingerPrint &) = default; |
1422 | |
1423 | bool operator==(const OperationFingerPrint &other) const { |
1424 | return hash == other.hash; |
1425 | } |
1426 | bool operator!=(const OperationFingerPrint &other) const { |
1427 | return !(*this == other); |
1428 | } |
1429 | |
1430 | private: |
1431 | std::array<uint8_t, 20> hash; |
1432 | }; |
1433 | |
1434 | } // namespace mlir |
1435 | |
1436 | namespace llvm { |
1437 | template <> |
1438 | struct DenseMapInfo<mlir::OperationName> { |
1439 | static mlir::OperationName getEmptyKey() { |
1440 | void *pointer = llvm::DenseMapInfo<void *>::getEmptyKey(); |
1441 | return mlir::OperationName::getFromOpaquePointer(pointer); |
1442 | } |
1443 | static mlir::OperationName getTombstoneKey() { |
1444 | void *pointer = llvm::DenseMapInfo<void *>::getTombstoneKey(); |
1445 | return mlir::OperationName::getFromOpaquePointer(pointer); |
1446 | } |
1447 | static unsigned getHashValue(mlir::OperationName val) { |
1448 | return DenseMapInfo<void *>::getHashValue(PtrVal: val.getAsOpaquePointer()); |
1449 | } |
1450 | static bool isEqual(mlir::OperationName lhs, mlir::OperationName rhs) { |
1451 | return lhs == rhs; |
1452 | } |
1453 | }; |
1454 | template <> |
1455 | struct DenseMapInfo<mlir::RegisteredOperationName> |
1456 | : public DenseMapInfo<mlir::OperationName> { |
1457 | static mlir::RegisteredOperationName getEmptyKey() { |
1458 | void *pointer = llvm::DenseMapInfo<void *>::getEmptyKey(); |
1459 | return mlir::RegisteredOperationName::getFromOpaquePointer(pointer); |
1460 | } |
1461 | static mlir::RegisteredOperationName getTombstoneKey() { |
1462 | void *pointer = llvm::DenseMapInfo<void *>::getTombstoneKey(); |
1463 | return mlir::RegisteredOperationName::getFromOpaquePointer(pointer); |
1464 | } |
1465 | }; |
1466 | |
1467 | template <> |
1468 | struct PointerLikeTypeTraits<mlir::OperationName> { |
1469 | static inline void *getAsVoidPointer(mlir::OperationName I) { |
1470 | return const_cast<void *>(I.getAsOpaquePointer()); |
1471 | } |
1472 | static inline mlir::OperationName getFromVoidPointer(void *P) { |
1473 | return mlir::OperationName::getFromOpaquePointer(pointer: P); |
1474 | } |
1475 | static constexpr int NumLowBitsAvailable = |
1476 | PointerLikeTypeTraits<void *>::NumLowBitsAvailable; |
1477 | }; |
1478 | template <> |
1479 | struct PointerLikeTypeTraits<mlir::RegisteredOperationName> |
1480 | : public PointerLikeTypeTraits<mlir::OperationName> { |
1481 | static inline mlir::RegisteredOperationName getFromVoidPointer(void *P) { |
1482 | return mlir::RegisteredOperationName::getFromOpaquePointer(pointer: P); |
1483 | } |
1484 | }; |
1485 | |
1486 | } // namespace llvm |
1487 | |
1488 | #endif |
1489 |
Definitions
- OpaqueProperties
- OpaqueProperties
- operator bool
- as
- OperationName
- InterfaceConcept
- ~InterfaceConcept
- Impl
- Impl
- isRegistered
- getInterfaceMap
- getDialect
- getName
- getTypeID
- getAttributeNames
- UnregisteredOpModel
- isRegistered
- getTypeID
- foldHook
- getCanonicalizationPatterns
- hasTrait
- hasTrait
- mightHaveTrait
- mightHaveTrait
- getParseAssemblyFn
- populateDefaultAttrs
- printAssembly
- verifyInvariants
- verifyRegionInvariants
- getAttributeNames
- getInterface
- attachInterface
- hasPromiseOrImplementsInterface
- hasInterface
- hasInterface
- mightHaveInterface
- mightHaveInterface
- getInherentAttr
- setInherentAttr
- populateInherentAttrs
- verifyInherentAttrs
- getOpPropertyByteSize
- destroyOpProperties
- initOpProperties
- populateDefaultProperties
- getOpPropertiesAsAttribute
- setOpPropertiesFromAttribute
- copyOpProperties
- compareOpProperties
- hashOpProperties
- getDialect
- stripDialect
- getContext
- getStringRef
- getIdentifier
- getAsOpaquePointer
- getFromOpaquePointer
- operator==
- operator!=
- OperationName
- getImpl
- setImpl
- operator<<
- hash_value
- RegisteredOperationName
- Model
- Model
- foldHook
- getCanonicalizationPatterns
- hasTrait
- getParseAssemblyFn
- populateDefaultAttrs
- printAssembly
- verifyInvariants
- verifyRegionInvariants
- getInherentAttr
- setInherentAttr
- populateInherentAttrs
- verifyInherentAttrs
- hasProperties
- getOpPropertyByteSize
- initProperties
- deleteProperties
- populateDefaultProperties
- setPropertiesFromAttr
- getPropertiesAsAttr
- compareProperties
- copyProperties
- hashProperties
- insert
- getDialect
- getFromOpaquePointer
- RegisteredOperationName
- getRegisteredInfo
- findAttrUnsorted
- findAttrSorted
- findAttrSorted
- getAttrFromSortedRange
- getNamedAttrFromSortedRange
- NamedAttrList
- NamedAttrList
- NamedAttrList
- NamedAttrList
- operator!=
- operator==
- append
- append
- append
- append
- append
- assign
- clear
- empty
- reserve
- pop_back
- begin
- end
- begin
- end
- isSorted
- findAttr
- OperationState
- OperationState
- operator=
- OperationState
- operator=
- getOrAddProperties
- getRawProperties
- useProperties
- addTypes
- addTypes
- addAttribute
- addAttribute
- addAttributes
- addSuccessors
- getContext
- OperandStorage
- getOperands
- size
- OpPrintingFlags
- OpPrintingFlags
- OperationEquivalence
- Flags
- ignoreHashValue
- directHashValue
- ignoreValueEquivalence
- exactValueMatch
- OperationFingerPrint
- OperationFingerPrint
- operator=
- operator==
- operator!=
- DenseMapInfo
- getEmptyKey
- getTombstoneKey
- getHashValue
- isEqual
- DenseMapInfo
- getEmptyKey
- getTombstoneKey
- PointerLikeTypeTraits
- getAsVoidPointer
- getFromVoidPointer
- NumLowBitsAvailable
- PointerLikeTypeTraits
Improve your Profiling and Debugging skills
Find out more