//===- 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 "llvm/ADT/Twine.h" #include <optional> 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 documentation 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 contain optionally 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 contain optionally 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<Operation, Block>, private llvm::TrailingObjects<Operation, detail::OperandStorage, detail::OpProperties, BlockOperand, Region, OpOperand> { … }; inline raw_ostream &operator<<(raw_ostream &os, const Operation &op) { … } } // namespace mlir namespace llvm { /// Cast from an (const) Operation * to a derived operation type. CastInfo<T, ::mlir::Operation *>; CastInfo<T, const ::mlir::Operation *>; /// Cast from an (const) Operation & to a derived operation type. CastInfo<T, ::mlir::Operation>; CastInfo<T, const ::mlir::Operation>; /// Cast (const) Operation * to itself. This is helpful to avoid SFINAE in /// templated implementations that should work on both base and derived /// operation types. template <> struct CastInfo<::mlir::Operation *, ::mlir::Operation *> : public NullableValueCastFailed<::mlir::Operation *>, public DefaultDoCastIfPossible< ::mlir::Operation *, ::mlir::Operation *, CastInfo<::mlir::Operation *, ::mlir::Operation *>> { … }; template <> struct CastInfo<const ::mlir::Operation *, const ::mlir::Operation *> : public ConstStrippingForwardingCast< const ::mlir::Operation *, const ::mlir::Operation *, CastInfo<::mlir::Operation *, ::mlir::Operation *>> { … }; } // namespace llvm #endif // MLIR_IR_OPERATION_H