//===- Operation.h - MLIR Operation Class -----------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file defines the Operation class. // //===----------------------------------------------------------------------===// #ifndef MLIR_IR_OPERATION_H #define MLIR_IR_OPERATION_H #include "mlir/IR/Block.h" #include "mlir/IR/BuiltinAttributes.h" #include "mlir/IR/Diagnostics.h" #include "mlir/IR/OperationSupport.h" #include "mlir/IR/Region.h" #include namespace mlir { namespace detail { /// This is a "tag" used for mapping the properties storage in /// llvm::TrailingObjects. enum class OpProperties : char {}; } // namespace detail /// Operation is the basic unit of execution within MLIR. /// /// The following documentations are recommended to understand this class: /// - https://mlir.llvm.org/docs/LangRef/#operations /// - https://mlir.llvm.org/docs/Tutorials/UnderstandingTheIRStructure/ /// /// An Operation is defined first by its name, which is a unique string. The /// name is interpreted so that if it contains a '.' character, the part before /// is the dialect name this operation belongs to, and everything that follows /// is this operation name within the dialect. /// /// An Operation defines zero or more SSA `Value` that we refer to as the /// Operation results. This array of Value is actually stored in memory before /// the Operation itself in reverse order. That is for an Operation with 3 /// results we allocate the following memory layout: /// /// [Result2, Result1, Result0, Operation] /// ^ this is where `Operation*` pointer points to. /// /// A consequence of this is that this class must be heap allocated, which is /// handled by the various `create` methods. Each result contains: /// - one pointer to the first use (see `OpOperand`) /// - the type of the SSA Value this result defines. /// - the index for this result in the array. /// The results are defined as subclass of `ValueImpl`, and more precisely as /// the only two subclasses of `OpResultImpl`: `InlineOpResult` and /// `OutOfLineOpResult`. The former is used for the first 5 results and the /// latter for the subsequent ones. They differ in how they store their index: /// the first 5 results only need 3 bits and thus are packed with the Type /// pointer, while the subsequent one have an extra `unsigned` value and thus /// need more space. /// /// An Operation also has zero or more operands: these are uses of SSA Value, /// which can be the results of other operations or Block arguments. Each of /// these uses is an instance of `OpOperand`. This optional array is initially /// tail allocated with the operation class itself, but can be dynamically moved /// out-of-line in a dynamic allocation as needed. /// /// An Operation may optionally contain one or multiple Regions, stored in a /// tail allocated array. Each `Region` is a list of Blocks. Each `Block` is /// itself a list of Operations. This structure is effectively forming a tree. /// /// Some operations like branches also refer to other Block, in which case they /// would have an array of `BlockOperand`. /// /// An Operation may optionally contain a "Properties" object: this is a /// pre-defined C++ object with a fixed size. This object is owned by the /// operation and deleted with the operation. It can be converted to an /// Attribute on demand, or loaded from an Attribute. /// /// /// Finally an Operation also contain an optional `DictionaryAttr`, a Location, /// and a pointer to its parent Block (if any). class alignas(8) Operation final : public llvm::ilist_node_with_parent, private llvm::TrailingObjects { public: /// Create a new Operation with the specific fields. This constructor /// populates the provided attribute list with default attributes if /// necessary. static Operation *create(Location location, OperationName name, TypeRange resultTypes, ValueRange operands, NamedAttrList &&attributes, PropertyRef properties, BlockRange successors, unsigned numRegions); /// Create a new Operation with the specific fields. This constructor uses an /// existing attribute dictionary to avoid uniquing a list of attributes. static Operation *create(Location location, OperationName name, TypeRange resultTypes, ValueRange operands, DictionaryAttr attributes, PropertyRef properties, BlockRange successors, unsigned numRegions); /// Create a new Operation from the fields stored in `state`. static Operation *create(const OperationState &state); /// Create a new Operation with the specific fields. static Operation *create(Location location, OperationName name, TypeRange resultTypes, ValueRange operands, NamedAttrList &&attributes, PropertyRef properties, BlockRange successors = {}, RegionRange regions = {}); /// The name of an operation is the key identifier for it. OperationName getName() { return name; } /// If this operation has a registered operation description, return it. /// Otherwise return std::nullopt. std::optional getRegisteredInfo() { return getName().getRegisteredInfo(); } /// Returns true if this operation has a registered operation description, /// otherwise false. bool isRegistered() { return getName().isRegistered(); } /// Remove this operation from its parent block and delete it. void erase(); /// Remove the operation from its parent block, but don't delete it. void remove(); /// Class encompassing various options related to cloning an operation. Users /// of this class should pass it to Operation's 'clone' methods. /// Current options include: /// * Whether cloning should recursively traverse into the regions of the /// operation or not. /// * Whether cloning should also clone the operands of the operation. /// * Whether to use different result types or clone them. class CloneOptions { public: /// Default constructs an option with all flags set to false. That means all /// parts of an operation that may optionally not be cloned, are not cloned. CloneOptions(); /// Constructs an instance with the options set accordingly. CloneOptions(bool cloneRegions, bool cloneOperands, std::optional> resultTypes); /// Returns an instance such that all elements of the operation are cloned. /// This is the default when using the clone method and clones all parts of /// the operation. static CloneOptions all(); /// Configures whether cloning should traverse into any of the regions of /// the operation. If set to true, the operation's regions are recursively /// cloned. If set to false, cloned operations will have the same number of /// regions, but they will be empty. /// Cloning of nested operations in the operation's regions are currently /// unaffected by other flags. CloneOptions &cloneRegions(bool enable = true); /// Returns whether regions of the operation should be cloned as well. bool shouldCloneRegions() const { return cloneRegionsFlag; } /// Configures whether operation' operands should be cloned. Otherwise the /// resulting clones will simply have zero operands. CloneOptions &cloneOperands(bool enable = true); /// Returns whether operands should be cloned as well. bool shouldCloneOperands() const { return cloneOperandsFlag; } /// Configures different result types to use for the cloned operation. /// If an empty optional, the result types are cloned from the original /// operation. CloneOptions &withResultTypes(std::optional> resultTypes); /// Returns true if the results are cloned from the operation. bool shouldCloneResults() const { return !resultTypes.has_value(); } /// Returns the result types that should be used for the created operation /// or `defaultResultTypes` if none were set. TypeRange resultTypesOr(TypeRange defaultResultTypes) const { if (resultTypes) return *resultTypes; return defaultResultTypes; } private: /// Whether regions should be cloned. bool cloneRegionsFlag : 1; /// Whether operands should be cloned. bool cloneOperandsFlag : 1; /// New result types to use in the cloned operation. std::optional> resultTypes; }; /// Create a deep copy of this operation, remapping any operands that use /// values outside of the operation using the map that is provided (leaving /// them alone if no entry is present). Replaces references to cloned /// sub-operations to the corresponding operation that is copied, and adds /// those mappings to the map. /// Optionally, one may configure what parts of the operation to clone using /// the options parameter. If parts of the operation (e.g. results or regions) /// are not cloned, they will not appear in the mapper. /// /// Calling this method from multiple threads is generally safe if through the /// process of cloning no new uses of 'Value's from outside the operation are /// created. Cloning an isolated-from-above operation with no operands, such /// as top level function operations, is therefore always safe. Using the /// mapper, it is possible to avoid adding uses to outside operands by /// remapping them to 'Value's owned by the caller thread. Operation *clone(IRMapping &mapper, const CloneOptions &options = CloneOptions::all()); Operation *clone(const CloneOptions &options = CloneOptions::all()); /// Create a partial copy of this operation without traversing into attached /// regions. The new operation will have the same number of regions as the /// original one, but they will be left empty. /// Operands are remapped using `mapper` (if present), and `mapper` is updated /// to contain the results. Operation *cloneWithoutRegions(IRMapping &mapper); /// Create a partial copy of this operation without traversing into attached /// regions. The new operation will have the same number of regions as the /// original one, but they will be left empty. Operation *cloneWithoutRegions(); /// Returns the operation block that contains this operation. Block *getBlock() { return block; } /// Return the context this operation is associated with. MLIRContext *getContext() { return location->getContext(); } /// Return the dialect this operation is associated with, or nullptr if the /// associated dialect is not loaded. Dialect *getDialect() { return getName().getDialect(); } /// The source location the operation was defined or derived from. Location getLoc() { return location; } /// Set the source location the operation was defined or derived from. void setLoc(Location loc) { location = loc; } /// Returns the region to which the instruction belongs. Returns nullptr if /// the instruction is unlinked. Region *getParentRegion() { return block ? block->getParent() : nullptr; } /// Returns the closest surrounding operation that contains this operation /// or nullptr if this is a top-level operation. Operation *getParentOp() { return block ? block->getParentOp() : nullptr; } /// Return the closest surrounding parent operation that is of type 'OpTy'. template OpTy getParentOfType() { auto *op = this; while ((op = op->getParentOp())) if (auto parentOp = dyn_cast(op)) return parentOp; return OpTy(); } template std::enable_if_t<(sizeof...(OpTy) > 1), Operation *> getParentOfType() { auto *op = this; while ((op = op->getParentOp())) if (isa(op)) return op; return nullptr; } /// Returns the closest surrounding parent operation with trait `Trait`. template