//===- Value.h - Base of the SSA Value hierarchy ----------------*- 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 generic Value type and manipulation utilities. // //===----------------------------------------------------------------------===// #ifndef MLIR_IR_VALUE_H #define MLIR_IR_VALUE_H #include "mlir/IR/Types.h" #include "mlir/IR/UseDefLists.h" #include "mlir/Support/LLVM.h" #include "llvm/Support/PointerLikeTypeTraits.h" namespace mlir { class AsmState; class Block; class BlockArgument; class Operation; class OpOperand; class OpPrintingFlags; class OpResult; class Region; class Value; //===----------------------------------------------------------------------===// // Value //===----------------------------------------------------------------------===// namespace detail { /// The base class for all derived Value classes. It contains all of the /// components that are shared across Value classes. class alignas(8) ValueImpl : public IRObjectWithUseList { public: /// The enumeration represents the various different kinds of values the /// internal representation may take. We use all of the bits from Type that we /// can to store indices inline. enum class Kind { /// The first N kinds are all inline operation results. An inline operation /// result means that the kind represents the result number. This removes /// the need to store an additional index value. The derived class here is /// an `OpResultImpl`. InlineOpResult = 0, /// The next kind represents a 'out-of-line' operation result. This is for /// results with numbers larger than we can represent inline. The derived /// class here is an `OpResultImpl`. OutOfLineOpResult = 6, /// The last kind represents a block argument. The derived class here is an /// `BlockArgumentImpl`. BlockArgument = 7 }; /// Return the type of this value. Type getType() const { return typeAndKind.getPointer(); } /// Set the type of this value. void setType(Type type) { return typeAndKind.setPointer(type); } /// Return the kind of this value. Kind getKind() const { return typeAndKind.getInt(); } protected: ValueImpl(Type type, Kind kind) : typeAndKind(type, kind) {} /// Expose a few methods explicitly for the debugger to call for /// visualization. #ifndef NDEBUG LLVM_DUMP_METHOD Type debug_getType() const { return getType(); } LLVM_DUMP_METHOD Kind debug_getKind() const { return getKind(); } #endif /// The type of this result and the kind. llvm::PointerIntPair typeAndKind; }; } // namespace detail /// This class represents an instance of an SSA value in the MLIR system, /// representing a computable value that has a type and a set of users. An SSA /// value is either a BlockArgument or the result of an operation. /// Note: This class has value-type semantics and is just a simple wrapper /// around a ValueImpl that is either owned by a block (in the case of a /// BlockArgument) or an Operation (in the case of an OpResult). /// As most IR constructs, this isn't const-correct, but we keep the method /// consistent, and therefore methods that immediately modify this Value aren't /// marked `const` (including modifying the Value's use-list). class Value { public: constexpr Value(detail::ValueImpl *impl = nullptr) : impl(impl) {} explicit operator bool() const { return impl; } bool operator==(const Value &other) const { return impl == other.impl; } bool operator!=(const Value &other) const { return !(*this == other); } /// Return the type of this value. Type getType() const { return impl->getType(); } /// Utility to get the associated MLIRContext that this value is defined in. MLIRContext *getContext() const { return getType().getContext(); } /// Mutate the type of this Value to be of the specified type. /// /// Note that this is an extremely dangerous operation which can create /// completely invalid IR very easily. It is strongly recommended that you /// recreate IR objects with the right types instead of mutating them in /// place. void setType(Type newType) { impl->setType(newType); } /// If this value is the result of an operation, return the operation that /// defines it. Operation *getDefiningOp() const; /// If this value is the result of an operation of type OpTy, return the /// operation that defines it. template OpTy getDefiningOp() const { return llvm::dyn_cast_or_null(getDefiningOp()); } /// Return the location of this value. Location getLoc() const; void setLoc(Location loc); /// Return the Region in which this Value is defined. Region *getParentRegion(); /// Return the Block in which this Value is defined. Block *getParentBlock(); //===--------------------------------------------------------------------===// // UseLists //===--------------------------------------------------------------------===// /// Drop all uses of this object from their respective owners. void dropAllUses() { return impl->dropAllUses(); } /// Replace all uses of 'this' value with the new value, updating anything in /// the IR that uses 'this' to use the other value instead. When this returns /// there are zero uses of 'this'. void replaceAllUsesWith(Value newValue) { impl->replaceAllUsesWith(newValue); } /// Replace all uses of 'this' value with 'newValue', updating anything in the /// IR that uses 'this' to use the other value instead except if the user is /// listed in 'exceptions' . void replaceAllUsesExcept(Value newValue, const SmallPtrSetImpl &exceptions); /// Replace all uses of 'this' value with 'newValue', updating anything in the /// IR that uses 'this' to use the other value instead except if the user is /// 'exceptedUser'. void replaceAllUsesExcept(Value newValue, Operation *exceptedUser); /// Replace all uses of 'this' value with 'newValue' if the given callback /// returns true. void replaceUsesWithIf(Value newValue, function_ref shouldReplace); /// Returns true if the value is used outside of the given block. bool isUsedOutsideOfBlock(Block *block) const; /// Shuffle the use list order according to the provided indices. It is /// responsibility of the caller to make sure that the indices map the current /// use-list chain to another valid use-list chain. void shuffleUseList(ArrayRef indices); //===--------------------------------------------------------------------===// // Uses /// This class implements an iterator over the uses of a value. using use_iterator = ValueUseIterator; using use_range = iterator_range; use_iterator use_begin() const { return impl->use_begin(); } use_iterator use_end() const { return use_iterator(); } /// Returns a range of all uses, which is useful for iterating over all uses. use_range getUses() const { return {use_begin(), use_end()}; } /// This method computes the number of uses of this Value. /// /// This is a linear time operation. Use hasOneUse, hasNUses, or /// hasNUsesOrMore to check for specific values. unsigned getNumUses() const; /// Returns true if this value has exactly one use. bool hasOneUse() const { return impl->hasOneUse(); } /// Return true if this Value has exactly n uses. bool hasNUses(unsigned n) const; /// Return true if this value has n uses or more. /// /// This is logically equivalent to getNumUses() >= N. bool hasNUsesOrMore(unsigned n) const; /// Returns true if this value has no uses. bool use_empty() const { return impl->use_empty(); } //===--------------------------------------------------------------------===// // Users using user_iterator = ValueUserIterator; using user_range = iterator_range; user_iterator user_begin() const { return use_begin(); } user_iterator user_end() const { return use_end(); } user_range getUsers() const { return {user_begin(), user_end()}; } //===--------------------------------------------------------------------===// // Utilities void print(raw_ostream &os) const; void print(raw_ostream &os, const OpPrintingFlags &flags) const; void print(raw_ostream &os, AsmState &state) const; void dump() const; /// Print this value as if it were an operand. void printAsOperand(raw_ostream &os, AsmState &state) const; void printAsOperand(raw_ostream &os, const OpPrintingFlags &flags) const; /// Methods for supporting PointerLikeTypeTraits. void *getAsOpaquePointer() const { return impl; } static Value getFromOpaquePointer(const void *pointer) { return reinterpret_cast(const_cast(pointer)); } detail::ValueImpl *getImpl() const { return impl; } friend ::llvm::hash_code hash_value(Value arg); protected: /// A pointer to the internal implementation of the value. detail::ValueImpl *impl; }; raw_ostream &operator<<(raw_ostream &os, Value value); //===----------------------------------------------------------------------===// // OpOperand //===----------------------------------------------------------------------===// /// This class represents an operand of an operation. Instances of this class /// contain a reference to a specific `Value`. class OpOperand : public IROperand { public: /// Provide the use list that is attached to the given value. static IRObjectWithUseList *getUseList(Value value) { return value.getImpl(); } /// Return which operand this is in the OpOperand list of the Operation. unsigned getOperandNumber() const; /// Set the current value being used by this operand. void assign(Value value) { set(value); } private: /// Keep the constructor private and accessible to the OperandStorage class /// only to avoid hard-to-debug typo/programming mistakes. friend class OperandStorage; using IROperand::IROperand; }; //===----------------------------------------------------------------------===// // BlockArgument //===----------------------------------------------------------------------===// namespace detail { /// The internal implementation of a BlockArgument. class BlockArgumentImpl : public ValueImpl { public: static bool classof(const ValueImpl *value) { return value->getKind() == ValueImpl::Kind::BlockArgument; } private: BlockArgumentImpl(Type type, Block *owner, int64_t index, Location loc) : ValueImpl(type, Kind::BlockArgument), owner(owner), index(index), loc(loc) {} /// The owner of this argument. Block *owner; /// The position in the argument list. int64_t index; /// The source location of this argument. Location loc; /// Allow access to owner and constructor. friend BlockArgument; }; } // namespace detail /// This class represents an argument of a Block. class BlockArgument : public Value { public: using Value::Value; static bool classof(Value value) { return llvm::isa(value.getImpl()); } /// Returns the block that owns this argument. Block *getOwner() const { return getImpl()->owner; } /// Returns the number of this argument. unsigned getArgNumber() const { return getImpl()->index; } /// Return the location for this argument. Location getLoc() const { return getImpl()->loc; } void setLoc(Location loc) { getImpl()->loc = loc; } private: /// Allocate a new argument with the given type and owner. static BlockArgument create(Type type, Block *owner, int64_t index, Location loc) { return new detail::BlockArgumentImpl(type, owner, index, loc); } /// Destroy and deallocate this argument. void destroy() { delete getImpl(); } /// Get a raw pointer to the internal implementation. detail::BlockArgumentImpl *getImpl() const { return reinterpret_cast(impl); } /// Cache the position in the block argument list. void setArgNumber(int64_t index) { getImpl()->index = index; } /// Allow access to `create`, `destroy` and `setArgNumber`. friend Block; /// Allow access to 'getImpl'. friend Value; }; //===----------------------------------------------------------------------===// // OpResult //===----------------------------------------------------------------------===// namespace detail { /// This class provides the implementation for an operation result. class alignas(8) OpResultImpl : public ValueImpl { public: using ValueImpl::ValueImpl; static bool classof(const ValueImpl *value) { return value->getKind() != ValueImpl::Kind::BlockArgument; } /// Returns the parent operation of this result. Operation *getOwner() const; /// Returns the result number of this op result. unsigned getResultNumber() const; /// Returns the next operation result at `offset` after this result. This /// method is useful when indexing the result storage of an operation, given /// that there is more than one kind of operation result (with the different /// kinds having different sizes) and that operations are stored in reverse /// order. OpResultImpl *getNextResultAtOffset(intptr_t offset); /// Returns the maximum number of results that can be stored inline. static unsigned getMaxInlineResults() { return static_cast(Kind::OutOfLineOpResult); } }; /// This class provides the implementation for an operation result whose index /// can be represented "inline" in the underlying ValueImpl. struct InlineOpResult : public OpResultImpl { public: InlineOpResult(Type type, unsigned resultNo) : OpResultImpl(type, static_cast(resultNo)) { assert(resultNo < getMaxInlineResults()); } /// Return the result number of this op result. unsigned getResultNumber() const { return static_cast(getKind()); } static bool classof(const OpResultImpl *value) { return value->getKind() != ValueImpl::Kind::OutOfLineOpResult; } }; /// This class provides the implementation for an operation result whose index /// cannot be represented "inline", and thus requires an additional index field. class OutOfLineOpResult : public OpResultImpl { public: OutOfLineOpResult(Type type, uint64_t outOfLineIndex) : OpResultImpl(type, Kind::OutOfLineOpResult), outOfLineIndex(outOfLineIndex) {} static bool classof(const OpResultImpl *value) { return value->getKind() == ValueImpl::Kind::OutOfLineOpResult; } /// Return the result number of this op result. unsigned getResultNumber() const { return outOfLineIndex + getMaxInlineResults(); } /// The trailing result number, or the offset from the beginning of the /// `OutOfLineOpResult` array. uint64_t outOfLineIndex; }; /// Return the result number of this op result. inline unsigned OpResultImpl::getResultNumber() const { if (const auto *outOfLineResult = dyn_cast(this)) return outOfLineResult->getResultNumber(); return cast(this)->getResultNumber(); } /// TypedValue is a Value with a statically know type. /// TypedValue can be null/empty template struct TypedValue : Value { using Value::Value; using ValueType = Ty; static bool classof(Value value) { return llvm::isa(value.getType()); } /// TypedValue can implicitly convert to TypedValue if B is assignable /// to A. template ::value>::type> operator ToTy() const { return llvm::cast(*this); } /// Return the known Type Ty getType() const { return llvm::cast(Value::getType()); } void setType(Ty ty) { Value::setType(ty); } }; } // namespace detail /// This is a value defined by a result of an operation. class OpResult : public Value { public: using Value::Value; static bool classof(Value value) { return llvm::isa(value.getImpl()); } /// Returns the operation that owns this result. Operation *getOwner() const { return getImpl()->getOwner(); } /// Returns the number of this result. unsigned getResultNumber() const { return getImpl()->getResultNumber(); } private: /// Get a raw pointer to the internal implementation. detail::OpResultImpl *getImpl() const { return reinterpret_cast(impl); } /// Given a number of operation results, returns the number that need to be /// stored inline. static unsigned getNumInline(unsigned numResults); /// Given a number of operation results, returns the number that need to be /// stored as trailing. static unsigned getNumTrailing(unsigned numResults); /// Allow access to constructor. friend Operation; }; /// Make Value hashable. inline ::llvm::hash_code hash_value(Value arg) { return ::llvm::hash_value(arg.getImpl()); } template /// If Ty is mlir::Type this will select `Value` instead of having a wrapper /// around it. This helps resolve ambiguous conversion issues. using TypedValue = std::conditional_t, mlir::Value, detail::TypedValue>; } // namespace mlir namespace llvm { template <> struct DenseMapInfo { static unsigned getHashValue(mlir::Value val) { return mlir::hash_value(val); } static bool isEqual(mlir::Value lhs, mlir::Value rhs) { return lhs == rhs; } }; template <> struct DenseMapInfo : public DenseMapInfo { }; template <> struct DenseMapInfo : public DenseMapInfo { }; template struct DenseMapInfo> : public DenseMapInfo { }; /// Allow stealing the low bits of a value. template <> struct PointerLikeTypeTraits { public: static inline void *getAsVoidPointer(mlir::Value value) { return const_cast(value.getAsOpaquePointer()); } static inline mlir::Value getFromVoidPointer(void *pointer) { return mlir::Value::getFromOpaquePointer(pointer); } enum { NumLowBitsAvailable = PointerLikeTypeTraits::NumLowBitsAvailable }; }; template <> struct PointerLikeTypeTraits : public PointerLikeTypeTraits { public: static inline mlir::BlockArgument getFromVoidPointer(void *pointer) { return reinterpret_cast(pointer); } }; template <> struct PointerLikeTypeTraits : public PointerLikeTypeTraits { public: static inline mlir::OpResult getFromVoidPointer(void *pointer) { return reinterpret_cast(pointer); } }; template struct PointerLikeTypeTraits> : public PointerLikeTypeTraits { public: static inline mlir::detail::TypedValue getFromVoidPointer(void *pointer) { return reinterpret_cast(pointer); } }; /// Add support for llvm style casts. We provide a cast between To and From if /// From is mlir::Value or derives from it. template struct CastInfo< To, From, std::enable_if_t> || std::is_base_of_v>> : NullableValueCastFailed, DefaultDoCastIfPossible> { /// Arguments are taken as mlir::Value here and not as `From`, because /// when casting from an intermediate type of the hierarchy to one of its /// children, the val.getKind() inside T::classof will use the static /// getKind() of the parent instead of the non-static ValueImpl::getKind() /// that returns the dynamic type. This means that T::classof would end up /// comparing the static Kind of the children to the static Kind of its /// parent, making it impossible to downcast from the parent to the child. static inline bool isPossible(mlir::Value ty) { /// Return a constant true instead of a dynamic true when casting to self or /// up the hierarchy. if constexpr (std::is_base_of_v) { return true; } else { return To::classof(ty); } } static inline To doCast(mlir::Value value) { return To(value.getImpl()); } }; } // namespace llvm #endif