//===- 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 BlockArgument; class Operation; class OpResult; class Region; class Value; namespace detail { /// The internal implementation of a BlockArgument. class BlockArgumentImpl; } // end 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 owner by a block(in the case of a BlockArgument) or /// an Operation(in the case of an OpResult). class Value { public: /// The enumeration represents the various different kinds of values the /// internal representation may take. We steal 2 bits to support a total of 4 /// possible values. enum class Kind { /// The first N kinds are all inline operation results. An inline operation /// result means that the kind represents the result number, and the owner /// pointer is the owning `Operation*`. Note: These are packed first to make /// result number lookups more efficient. OpResult0 = 0, OpResult1 = 1, /// The next kind represents a 'trailing' operation result. This is for /// results with numbers larger than we can represent inline. The owner here /// is an `TrailingOpResult*` that points to a trailing storage on the /// parent operation. TrailingOpResult = 2, /// The last kind represents a block argument. The owner here is a /// `BlockArgumentImpl*`. BlockArgument = 3 }; /// This value represents the 'owner' of the value and its kind. See the /// 'Kind' enumeration above for a more detailed description of each kind of /// owner. struct ImplTypeTraits : public llvm::PointerLikeTypeTraits { // We know that all pointers within the ImplType are aligned by 8-bytes, // meaning that we can steal up to 3 bits for the different values. static constexpr int NumLowBitsAvailable = 3; }; using ImplType = llvm::PointerIntPair; public: constexpr Value(std::nullptr_t) : ownerAndKind() {} Value(ImplType ownerAndKind = {}) : ownerAndKind(ownerAndKind) {} Value(const Value &) = default; Value &operator=(const Value &) = default; template bool isa() const { assert(*this && "isa<> used on a null type."); return U::classof(*this); } template bool isa() const { return isa() || isa(); } template U dyn_cast() const { return isa() ? U(ownerAndKind) : U(nullptr); } template U dyn_cast_or_null() const { return (*this && isa()) ? U(ownerAndKind) : U(nullptr); } template U cast() const { assert(isa()); return U(ownerAndKind); } explicit operator bool() const { return ownerAndKind.getPointer(); } bool operator==(const Value &other) const { return ownerAndKind == other.ownerAndKind; } bool operator!=(const Value &other) const { return !(*this == other); } /// Return the type of this value. Type getType() const; /// 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); /// 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()); } /// If this value is the result of an operation, use it as a location, /// otherwise return an unknown location. Location getLoc() const; /// Return the Region in which this Value is defined. Region *getParentRegion(); /// Return the Block in which this Value is defined. Block *getParentBlock(); //===--------------------------------------------------------------------===// // UseLists //===--------------------------------------------------------------------===// /// Provide the use list that is attached to this value. IRObjectWithUseList *getUseList() const; /// Drop all uses of this object from their respective owners. void dropAllUses() const; /// 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) const; /// 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) const; /// 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); //===--------------------------------------------------------------------===// // 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; 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()}; } /// Returns true if this value has exactly one use. bool hasOneUse() const; /// Returns true if this value has no uses. bool use_empty() const; //===--------------------------------------------------------------------===// // 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 /// Returns the kind of this value. Kind getKind() const { return ownerAndKind.getInt(); } void print(raw_ostream &os); void print(raw_ostream &os, AsmState &state); void dump(); /// Print this value as if it were an operand. void printAsOperand(raw_ostream &os, AsmState &state); /// Methods for supporting PointerLikeTypeTraits. void *getAsOpaquePointer() const { return ownerAndKind.getOpaqueValue(); } static Value getFromOpaquePointer(const void *pointer) { Value value; value.ownerAndKind.setFromOpaqueValue(const_cast(pointer)); return value; } friend ::llvm::hash_code hash_value(Value arg); protected: /// Returns true if the given operation result can be packed inline. static bool canPackResultInline(unsigned resultNo) { return resultNo < static_cast(Kind::TrailingOpResult); } /// Construct a value. Value(detail::BlockArgumentImpl *impl); Value(Operation *op, unsigned resultNo); /// This value represents the 'owner' of the value and its kind. See the /// 'Kind' enumeration above for a more detailed description of each kind of /// owner. ImplType ownerAndKind; }; inline raw_ostream &operator<<(raw_ostream &os, Value value) { value.print(os); return os; } //===----------------------------------------------------------------------===// // BlockArgument //===----------------------------------------------------------------------===// namespace detail { /// The internal implementation of a BlockArgument. class BlockArgumentImpl : public IRObjectWithUseList { BlockArgumentImpl(Type type, Block *owner) : type(type), owner(owner) {} /// The type of this argument. Type type; /// The owner of this argument. Block *owner; /// Allow access to owner and constructor. friend BlockArgument; }; } // end namespace detail /// Block arguments are values. class BlockArgument : public Value { public: using Value::Value; static bool classof(Value value) { return value.getKind() == Kind::BlockArgument; } /// Returns the block that owns this argument. Block *getOwner() const { return getImpl()->owner; } /// Return the type of this value. Type getType() const { return getImpl()->type; } /// Set the type of this value. void setType(Type newType) { getImpl()->type = newType; } /// Returns the number of this argument. unsigned getArgNumber() const; private: /// Allocate a new argument with the given type and owner. static BlockArgument create(Type type, Block *owner) { return new detail::BlockArgumentImpl(type, owner); } /// Destroy and deallocate this argument. void destroy() { delete getImpl(); } /// Get a raw pointer to the internal implementation. detail::BlockArgumentImpl *getImpl() const { return reinterpret_cast( ownerAndKind.getPointer()); } /// Allow access to `create` and `destroy`. friend Block; /// Allow access to 'getImpl'. friend Value; }; //===----------------------------------------------------------------------===// // OpResult //===----------------------------------------------------------------------===// /// 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 value.getKind() != Kind::BlockArgument; } /// Returns the operation that owns this result. Operation *getOwner() const; /// Returns the number of this result. unsigned getResultNumber() const; /// Returns the maximum number of results that can be stored inline. static unsigned getMaxInlineResults() { return static_cast(Kind::TrailingOpResult); } private: /// 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.ownerAndKind.getOpaqueValue()); } } // namespace mlir namespace llvm { template <> struct DenseMapInfo { static mlir::Value getEmptyKey() { auto pointer = llvm::DenseMapInfo::getEmptyKey(); return mlir::Value::getFromOpaquePointer(pointer); } static mlir::Value getTombstoneKey() { auto pointer = llvm::DenseMapInfo::getTombstoneKey(); return mlir::Value::getFromOpaquePointer(pointer); } static unsigned getHashValue(mlir::Value val) { return mlir::hash_value(val); } static bool isEqual(mlir::Value lhs, mlir::Value rhs) { return lhs == rhs; } }; /// Allow stealing the low bits of a value. template <> struct PointerLikeTypeTraits { public: static inline void *getAsVoidPointer(mlir::Value I) { return const_cast(I.getAsOpaquePointer()); } static inline mlir::Value getFromVoidPointer(void *P) { return mlir::Value::getFromOpaquePointer(P); } enum { NumLowBitsAvailable = PointerLikeTypeTraits::NumLowBitsAvailable }; }; template <> struct DenseMapInfo { static mlir::BlockArgument getEmptyKey() { auto pointer = llvm::DenseMapInfo::getEmptyKey(); return mlir::BlockArgument( mlir::Value::ImplType::getFromOpaqueValue(pointer)); } static mlir::BlockArgument getTombstoneKey() { auto pointer = llvm::DenseMapInfo::getTombstoneKey(); return mlir::BlockArgument( mlir::Value::ImplType::getFromOpaqueValue(pointer)); } static unsigned getHashValue(mlir::BlockArgument val) { return mlir::hash_value(val); } static bool isEqual(mlir::BlockArgument LHS, mlir::BlockArgument RHS) { return LHS == RHS; } }; /// Allow stealing the low bits of a value. template <> struct PointerLikeTypeTraits { public: static inline void *getAsVoidPointer(mlir::Value I) { return const_cast(I.getAsOpaquePointer()); } static inline mlir::BlockArgument getFromVoidPointer(void *P) { return mlir::Value::getFromOpaquePointer(P).cast(); } enum { NumLowBitsAvailable = PointerLikeTypeTraits::NumLowBitsAvailable }; }; } // end namespace llvm #endif