llvm/mlir/include/mlir/IR/Operation.h

//===- 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