From ab46543cebbd8845262a50291f296e453d110a14 Mon Sep 17 00:00:00 2001 From: River Riddle Date: Mon, 23 Dec 2019 12:36:20 -0800 Subject: [PATCH] Resubmit: ReImplement the Value classes as value-typed objects wrapping an internal pointer storage. This will enable future commits to reimplement the internal implementation of OpResult without needing to change all of the existing users. This is part of a chain of commits optimizing the size of operation results. PiperOrigin-RevId: 286930047 --- mlir/bindings/python/pybind.cpp | 3 +- mlir/include/mlir/Analysis/AffineAnalysis.h | 7 +- mlir/include/mlir/Analysis/Liveness.h | 2 +- mlir/include/mlir/Analysis/LoopAnalysis.h | 2 +- .../Conversion/AffineToStandard/AffineToStandard.h | 2 +- .../mlir/Conversion/LoopsToGPU/LoopsToGPU.h | 2 +- mlir/include/mlir/Dialect/VectorOps/Utils.h | 2 +- mlir/include/mlir/EDSC/Builders.h | 1 + mlir/include/mlir/IR/Block.h | 2 +- mlir/include/mlir/IR/BlockAndValueMapping.h | 38 +-- mlir/include/mlir/IR/OpImplementation.h | 16 +- mlir/include/mlir/IR/Operation.h | 2 +- mlir/include/mlir/IR/OperationSupport.h | 44 ++- mlir/include/mlir/IR/TypeUtilities.h | 1 - mlir/include/mlir/IR/UseDefLists.h | 17 ++ mlir/include/mlir/IR/Value.h | 297 +++++++++++++++++---- mlir/lib/Analysis/AffineStructures.cpp | 4 +- mlir/lib/Analysis/Dominance.cpp | 2 +- mlir/lib/Analysis/Liveness.cpp | 4 +- mlir/lib/Analysis/SliceAnalysis.cpp | 2 +- mlir/lib/Dialect/AffineOps/AffineOps.cpp | 6 +- .../Dialect/Linalg/Analysis/DependenceAnalysis.cpp | 4 +- mlir/lib/Dialect/LoopOps/LoopOps.cpp | 2 +- .../lib/Dialect/SPIRV/Serialization/Serializer.cpp | 2 +- mlir/lib/IR/AsmPrinter.cpp | 6 +- mlir/lib/IR/Block.cpp | 11 +- mlir/lib/IR/Operation.cpp | 26 +- mlir/lib/IR/OperationSupport.cpp | 15 +- mlir/lib/IR/TypeUtilities.cpp | 4 - mlir/lib/IR/Value.cpp | 4 +- 30 files changed, 370 insertions(+), 160 deletions(-) diff --git a/mlir/bindings/python/pybind.cpp b/mlir/bindings/python/pybind.cpp index 10445ed..caff9af 100644 --- a/mlir/bindings/python/pybind.cpp +++ b/mlir/bindings/python/pybind.cpp @@ -87,7 +87,8 @@ struct PythonValueHandle { operator ValueHandle &() { return value; } std::string str() const { - return std::to_string(reinterpret_cast(value.getValue())); + return std::to_string( + reinterpret_cast(value.getValue().getAsOpaquePointer())); } PythonValueHandle call(const std::vector &args) { diff --git a/mlir/include/mlir/Analysis/AffineAnalysis.h b/mlir/include/mlir/Analysis/AffineAnalysis.h index 5d94228..6029a9c 100644 --- a/mlir/include/mlir/Analysis/AffineAnalysis.h +++ b/mlir/include/mlir/Analysis/AffineAnalysis.h @@ -15,9 +15,7 @@ #ifndef MLIR_ANALYSIS_AFFINE_ANALYSIS_H #define MLIR_ANALYSIS_AFFINE_ANALYSIS_H -#include "mlir/Support/LLVM.h" -#include "mlir/Support/LogicalResult.h" -#include "llvm/ADT/ArrayRef.h" +#include "mlir/IR/Value.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallVector.h" @@ -28,10 +26,9 @@ class AffineForOp; class AffineValueMap; class FlatAffineConstraints; class Operation; -class Value; // TODO(riverriddle) Remove this after Value is value-typed. -using ValuePtr = Value *; +using ValuePtr = Value; /// Returns in `affineApplyOps`, the sequence of those AffineApplyOp /// Operations that are reachable via a search starting from `operands` and diff --git a/mlir/include/mlir/Analysis/Liveness.h b/mlir/include/mlir/Analysis/Liveness.h index 791c164..cbd2e63 100644 --- a/mlir/include/mlir/Analysis/Liveness.h +++ b/mlir/include/mlir/Analysis/Liveness.h @@ -33,7 +33,7 @@ class Region; class Value; // TODO(riverriddle) Remove this after Value is value-typed. -using ValuePtr = Value *; +using ValuePtr = Value; /// Represents an analysis for computing liveness information from a /// given top-level operation. The analysis iterates over all associated diff --git a/mlir/include/mlir/Analysis/LoopAnalysis.h b/mlir/include/mlir/Analysis/LoopAnalysis.h index 66f0033..75d7b98 100644 --- a/mlir/include/mlir/Analysis/LoopAnalysis.h +++ b/mlir/include/mlir/Analysis/LoopAnalysis.h @@ -28,7 +28,7 @@ class Operation; class Value; // TODO(riverriddle) Remove this after Value is value-typed. -using ValuePtr = Value *; +using ValuePtr = Value; /// Returns the trip count of the loop as an affine map with its corresponding /// operands if the latter is expressible as an affine expression, and nullptr diff --git a/mlir/include/mlir/Conversion/AffineToStandard/AffineToStandard.h b/mlir/include/mlir/Conversion/AffineToStandard/AffineToStandard.h index 8e873bf..c829876 100644 --- a/mlir/include/mlir/Conversion/AffineToStandard/AffineToStandard.h +++ b/mlir/include/mlir/Conversion/AffineToStandard/AffineToStandard.h @@ -22,7 +22,7 @@ class RewritePattern; class Value; // TODO(riverriddle) Remove this after Value is value-typed. -using ValuePtr = Value *; +using ValuePtr = Value; // Owning list of rewriting patterns. class OwningRewritePatternList; diff --git a/mlir/include/mlir/Conversion/LoopsToGPU/LoopsToGPU.h b/mlir/include/mlir/Conversion/LoopsToGPU/LoopsToGPU.h index 5f3ea87..b7423a5 100644 --- a/mlir/include/mlir/Conversion/LoopsToGPU/LoopsToGPU.h +++ b/mlir/include/mlir/Conversion/LoopsToGPU/LoopsToGPU.h @@ -16,7 +16,7 @@ struct LogicalResult; class Value; // TODO(riverriddle) Remove this after Value is value-typed. -using ValuePtr = Value *; +using ValuePtr = Value; namespace loop { class ForOp; diff --git a/mlir/include/mlir/Dialect/VectorOps/Utils.h b/mlir/include/mlir/Dialect/VectorOps/Utils.h index b4d8ad6..04bd8b5 100644 --- a/mlir/include/mlir/Dialect/VectorOps/Utils.h +++ b/mlir/include/mlir/Dialect/VectorOps/Utils.h @@ -26,7 +26,7 @@ class Value; class VectorType; // TODO(riverriddle) Remove this after Value is value-typed. -using ValuePtr = Value *; +using ValuePtr = Value; /// Computes and returns the multi-dimensional ratio of `superShape` to /// `subShape`. This is calculated by performing a traversal from minor to major diff --git a/mlir/include/mlir/EDSC/Builders.h b/mlir/include/mlir/EDSC/Builders.h index 6607f26..f9629a8d 100644 --- a/mlir/include/mlir/EDSC/Builders.h +++ b/mlir/include/mlir/EDSC/Builders.h @@ -329,6 +329,7 @@ public: /// Implicit conversion useful for automatic conversion to Container. operator ValuePtr() const { return getValue(); } + operator bool() const { return hasValue(); } /// Generic mlir::Op create. This is the key to being extensible to the whole /// of MLIR without duplicating the type system or the op definitions. diff --git a/mlir/include/mlir/IR/Block.h b/mlir/include/mlir/IR/Block.h index b5189b4..33feea7 100644 --- a/mlir/include/mlir/IR/Block.h +++ b/mlir/include/mlir/IR/Block.h @@ -63,7 +63,7 @@ public: //===--------------------------------------------------------------------===// // This is the list of arguments to the block. - using BlockArgListType = ArrayRef; + using BlockArgListType = MutableArrayRef; BlockArgListType getArguments() { return arguments; } diff --git a/mlir/include/mlir/IR/BlockAndValueMapping.h b/mlir/include/mlir/IR/BlockAndValueMapping.h index 82173c3..b7ad360 100644 --- a/mlir/include/mlir/IR/BlockAndValueMapping.h +++ b/mlir/include/mlir/IR/BlockAndValueMapping.h @@ -28,14 +28,18 @@ public: /// Inserts a new mapping for 'from' to 'to'. If there is an existing mapping, /// it is overwritten. void map(Block *from, Block *to) { valueMap[from] = to; } - void map(ValuePtr from, ValuePtr to) { valueMap[from] = to; } + void map(Value from, Value to) { + valueMap[from.getAsOpaquePointer()] = to.getAsOpaquePointer(); + } /// Erases a mapping for 'from'. - void erase(IRObjectWithUseList *from) { valueMap.erase(from); } + void erase(Block *from) { valueMap.erase(from); } + void erase(Value from) { valueMap.erase(from.getAsOpaquePointer()); } /// Checks to see if a mapping for 'from' exists. - bool contains(IRObjectWithUseList *from) const { - return valueMap.count(from); + bool contains(Block *from) const { return valueMap.count(from); } + bool contains(Value from) const { + return valueMap.count(from.getAsOpaquePointer()); } /// Lookup a mapped value within the map. If a mapping for the provided value @@ -43,23 +47,19 @@ public: Block *lookupOrNull(Block *from) const { return lookupOrValue(from, (Block *)nullptr); } - ValuePtr lookupOrNull(ValuePtr from) const { - return lookupOrValue(from, (ValuePtr) nullptr); - } + Value lookupOrNull(Value from) const { return lookupOrValue(from, Value()); } /// Lookup a mapped value within the map. If a mapping for the provided value /// does not exist then return the provided value. Block *lookupOrDefault(Block *from) const { return lookupOrValue(from, from); } - ValuePtr lookupOrDefault(ValuePtr from) const { - return lookupOrValue(from, from); - } + Value lookupOrDefault(Value from) const { return lookupOrValue(from, from); } /// Lookup a mapped value within the map. This asserts the provided value /// exists within the map. - template T *lookup(T *from) const { - auto *result = lookupOrNull(from); + template T lookup(T from) const { + auto result = lookupOrNull(from); assert(result && "expected 'from' to be contained within the map"); return result; } @@ -69,14 +69,18 @@ public: private: /// Utility lookupOrValue that looks up an existing key or returns the - /// provided value. This function assumes that if a mapping does exist, then - /// it is of 'T' type. - template T *lookupOrValue(T *from, T *value) const { + /// provided value. + Block *lookupOrValue(Block *from, Block *value) const { auto it = valueMap.find(from); - return it != valueMap.end() ? static_cast(it->second) : value; + return it != valueMap.end() ? reinterpret_cast(it->second) : value; + } + Value lookupOrValue(Value from, Value value) const { + auto it = valueMap.find(from.getAsOpaquePointer()); + return it != valueMap.end() ? Value::getFromOpaquePointer(it->second) + : value; } - DenseMap valueMap; + DenseMap valueMap; }; } // end namespace mlir diff --git a/mlir/include/mlir/IR/OpImplementation.h b/mlir/include/mlir/IR/OpImplementation.h index e58a5b0..8e2aed2 100644 --- a/mlir/include/mlir/IR/OpImplementation.h +++ b/mlir/include/mlir/IR/OpImplementation.h @@ -142,17 +142,14 @@ private: // Make the implementations convenient to use. inline OpAsmPrinter &operator<<(OpAsmPrinter &p, ValueRef value) { - p.printOperand(&value); + p.printOperand(value); return p; } -inline OpAsmPrinter &operator<<(OpAsmPrinter &p, ValuePtr value) { - return p << *value; -} -template ::value && - !std::is_convertible::value, - T>::type * = nullptr> +template ::value && + !std::is_convertible::value, + T>::type * = nullptr> inline OpAsmPrinter &operator<<(OpAsmPrinter &p, const T &values) { p.printOperands(values); return p; @@ -172,8 +169,7 @@ inline OpAsmPrinter &operator<<(OpAsmPrinter &p, Attribute attr) { // even if it isn't exactly one of them. For example, we want to print // FunctionType with the Type version above, not have it match this. template ::value && - !std::is_convertible::value && + !std::is_convertible::value && !std::is_convertible::value && !std::is_convertible::value && !std::is_convertible::value && diff --git a/mlir/include/mlir/IR/Operation.h b/mlir/include/mlir/IR/Operation.h index 9ab900c..2922761 100644 --- a/mlir/include/mlir/IR/Operation.h +++ b/mlir/include/mlir/IR/Operation.h @@ -246,7 +246,7 @@ public: unsigned getNumResults() { return numResults; } - ValuePtr getResult(unsigned idx) { return &getOpResult(idx); } + ValuePtr getResult(unsigned idx) { return getOpResult(idx); } /// Support result iteration. using result_range = ResultRange; diff --git a/mlir/include/mlir/IR/OperationSupport.h b/mlir/include/mlir/IR/OperationSupport.h index 1468166..ef2ff44e 100644 --- a/mlir/include/mlir/IR/OperationSupport.h +++ b/mlir/include/mlir/IR/OperationSupport.h @@ -525,8 +525,8 @@ private: /// This class implements iteration on the types of a given range of values. template class ValueTypeIterator final - : public llvm::mapped_iterator { - static Type unwrap(ValuePtr value) { return value->getType(); } + : public llvm::mapped_iterator { + static Type unwrap(Value value) { return value.getType(); } public: using reference = Type; @@ -536,8 +536,7 @@ public: /// Initializes the type iterator to the specified value iterator. ValueTypeIterator(ValueIteratorT it) - : llvm::mapped_iterator(it, &unwrap) { - } + : llvm::mapped_iterator(it, &unwrap) {} }; //===----------------------------------------------------------------------===// @@ -546,7 +545,7 @@ public: /// This class implements the operand iterators for the Operation class. class OperandRange final : public detail::indexed_accessor_range_base { + Value, Value, Value> { public: using RangeBaseT::RangeBaseT; OperandRange(Operation *op); @@ -561,7 +560,7 @@ private: return object + index; } /// See `detail::indexed_accessor_range_base` for details. - static ValuePtr dereference_iterator(OpOperand *object, ptrdiff_t index) { + static Value dereference_iterator(OpOperand *object, ptrdiff_t index) { return object[index].get(); } @@ -574,8 +573,8 @@ private: /// This class implements the result iterators for the Operation class. class ResultRange final - : public detail::indexed_accessor_range_base { + : public detail::indexed_accessor_range_base { public: using RangeBaseT::RangeBaseT; ResultRange(Operation *op); @@ -586,12 +585,12 @@ public: private: /// See `detail::indexed_accessor_range_base` for details. - static OpResultPtr offset_base(OpResultPtr object, ptrdiff_t index) { + static OpResult *offset_base(OpResult *object, ptrdiff_t index) { return object + index; } /// See `detail::indexed_accessor_range_base` for details. - static ValuePtr dereference_iterator(OpResultPtr object, ptrdiff_t index) { - return &object[index]; + static Value dereference_iterator(OpResult *object, ptrdiff_t index) { + return object[index]; } /// Allow access to `offset_base` and `dereference_iterator`. @@ -608,25 +607,24 @@ private: /// parameter. class ValueRange final : public detail::indexed_accessor_range_base< - ValueRange, PointerUnion, - ValuePtr, ValuePtr, ValuePtr> { + ValueRange, PointerUnion, + Value, Value, Value> { public: using RangeBaseT::RangeBaseT; template , Arg>::value && - !std::is_convertible::value>> - ValueRange(Arg &&arg) - : ValueRange(ArrayRef(std::forward(arg))) {} - ValueRange(ValuePtr const &value) : ValueRange(&value, /*count=*/1) {} - ValueRange(const std::initializer_list &values) - : ValueRange(ArrayRef(values)) {} + std::is_constructible, Arg>::value && + !std::is_convertible::value>> + ValueRange(Arg &&arg) : ValueRange(ArrayRef(std::forward(arg))) {} + ValueRange(const Value &value) : ValueRange(&value, /*count=*/1) {} + ValueRange(const std::initializer_list &values) + : ValueRange(ArrayRef(values)) {} ValueRange(iterator_range values) : ValueRange(OperandRange(values)) {} ValueRange(iterator_range values) : ValueRange(ResultRange(values)) {} - ValueRange(ArrayRef values = llvm::None); + ValueRange(ArrayRef values = llvm::None); ValueRange(OperandRange values); ValueRange(ResultRange values); @@ -637,12 +635,12 @@ public: private: /// The type representing the owner of this range. This is either a list of /// values, operands, or results. - using OwnerT = PointerUnion; + using OwnerT = PointerUnion; /// See `detail::indexed_accessor_range_base` for details. static OwnerT offset_base(const OwnerT &owner, ptrdiff_t index); /// See `detail::indexed_accessor_range_base` for details. - static ValuePtr dereference_iterator(const OwnerT &owner, ptrdiff_t index); + static Value dereference_iterator(const OwnerT &owner, ptrdiff_t index); /// Allow access to `offset_base` and `dereference_iterator`. friend RangeBaseT; diff --git a/mlir/include/mlir/IR/TypeUtilities.h b/mlir/include/mlir/IR/TypeUtilities.h index b471322..fd9d317 100644 --- a/mlir/include/mlir/IR/TypeUtilities.h +++ b/mlir/include/mlir/IR/TypeUtilities.h @@ -33,7 +33,6 @@ Type getElementTypeOrSelf(Type type); /// Return the element type or return the type itself. Type getElementTypeOrSelf(Attribute attr); Type getElementTypeOrSelf(ValuePtr val); -Type getElementTypeOrSelf(ValueRef val); /// Get the types within a nested Tuple. A helper for the class method that /// handles storage concerns, which is tricky to do in tablegen. diff --git a/mlir/include/mlir/IR/UseDefLists.h b/mlir/include/mlir/IR/UseDefLists.h index 898d0da..05720ed 100644 --- a/mlir/include/mlir/IR/UseDefLists.h +++ b/mlir/include/mlir/IR/UseDefLists.h @@ -21,6 +21,7 @@ namespace mlir { class IROperand; class Operation; +class Value; template class ValueUseIterator; template class ValueUserIterator; @@ -167,6 +168,22 @@ private: } }; +/// A reference to a value, suitable for use as an operand of an operation. +class OpOperand : public IROperand { +public: + OpOperand(Operation *owner) : IROperand(owner) {} + OpOperand(Operation *owner, Value value); + + /// Return the current value being used by this operand. + Value get(); + + /// Set the current value being used by this operand. + void set(Value newValue); + + /// Return which operand this is in the operand list of the User. + unsigned getOperandNumber(); +}; + /// A reference to a value, suitable for use as an operand of an operation, /// operation, etc. IRValueTy is the root type to use for values this tracks, /// and SSAUserTy is the type that will contain operands. diff --git a/mlir/include/mlir/IR/Value.h b/mlir/include/mlir/IR/Value.h index 030e6fa..26703a2 100644 --- a/mlir/include/mlir/IR/Value.h +++ b/mlir/include/mlir/IR/Value.h @@ -25,40 +25,101 @@ class OpResult; class Region; class Value; -/// Using directives that simplify the transition of Value to being value typed. -using BlockArgumentPtr = BlockArgument *; -using OpResultPtr = OpResult *; -using ValueRef = Value &; -using ValuePtr = Value *; +namespace detail { +/// The internal implementation of a Value. +class ValueImpl : public IRObjectWithUseList { +protected: + /// This enumerates all of the SSA value kinds. + enum class Kind { + BlockArgument, + OpResult, + }; + + ValueImpl(Kind kind, Type type) : typeAndKind(type, kind) {} + +private: + /// The type of the value and its kind. + llvm::PointerIntPair typeAndKind; + + /// Allow access to 'typeAndKind'. + friend Value; +}; -/// Operands contain a Value. -using OpOperand = IROperandImpl; +/// The internal implementation of a BlockArgument. +class BlockArgumentImpl : public ValueImpl { + BlockArgumentImpl(Type type, Block *owner) + : ValueImpl(Kind::BlockArgument, type), owner(owner) {} -/// This is the common base class for all SSA values in the MLIR system, -/// representing a computable value that has a type and a set of users. + /// The owner of this argument. + Block *owner; + + /// Allow access to owner and constructor. + friend BlockArgument; +}; + +class OpResultImpl : public ValueImpl { + OpResultImpl(Type type, Operation *owner) + : ValueImpl(Kind::OpResult, type), owner(owner) {} + + /// The owner of this result. + Operation *owner; + + /// Allow access to owner and the constructor. + friend OpResult; +}; +} // 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 IRObjectWithUseList { +class Value { public: /// This enumerates all of the SSA value kinds in the MLIR system. enum class Kind { - BlockArgument, // block argument - OpResult, // operation result + BlockArgument, + OpResult, }; + Value(std::nullptr_t) : impl(nullptr) {} + Value(detail::ValueImpl *impl = nullptr) : impl(impl) {} + Value(const Value &) = default; + Value &operator=(const Value &) = default; ~Value() {} - template bool isa() const { return U::classof(this); } - template U *dyn_cast() const { - return isa() ? (U *)this : nullptr; + template bool isa() const { + assert(impl && "isa<> used on a null type."); + return U::classof(*this); + } + template U dyn_cast() const { + return isa() ? U(impl) : U(nullptr); } - template U *cast() const { + template U dyn_cast_or_null() const { + return (impl && isa()) ? U(impl) : U(nullptr); + } + template U cast() const { assert(isa()); - return (U *)this; + return U(impl); } - Kind getKind() const { return typeAndKind.getInt(); } + /// Temporary methods to enable transition of Value to being used as a + /// value-type. + /// TODO(riverriddle) Remove these when all usages have been removed. + Value operator*() const { return *this; } + Value *operator->() const { return (Value *)this; } + + operator bool() const { return impl; } + bool operator==(const Value &other) const { return impl == other.impl; } + bool operator!=(const Value &other) const { return !(*this == other); } - Type getType() const { return typeAndKind.getPointer(); } + /// Return the kind of this value. + Kind getKind() const { return (Kind)impl->typeAndKind.getInt(); } + + /// Return the type of this value. + Type getType() const { return impl->typeAndKind.getPointer(); } /// Utility to get the associated MLIRContext that this value is defined in. MLIRContext *getContext() const { return getType().getContext(); } @@ -69,18 +130,18 @@ public: /// 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) { typeAndKind.setPointer(newType); } + void setType(Type newType) { impl->typeAndKind.setPointer(newType); } /// 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(ValuePtr newValue) { - IRObjectWithUseList::replaceAllUsesWith(newValue); + void replaceAllUsesWith(Value newValue) const { + impl->replaceAllUsesWith(newValue.impl); } /// If this value is the result of an operation, return the operation that /// defines it. - Operation *getDefiningOp(); + Operation *getDefiningOp() const; /// If this value is the result of an operation, use it as a location, /// otherwise return an unknown location. @@ -98,24 +159,51 @@ public: /// Returns a range of all uses, which is useful for iterating over all uses. inline use_range getUses(); + using user_iterator = ValueUserIterator; + using user_range = iterator_range; + + user_iterator user_begin() const { return impl->user_begin(); } + user_iterator user_end() const { return impl->user_end(); } + + /// Returns a range of all users. + user_range getUsers() const { return impl->getUsers(); } + + /// Returns true if this value has no uses. + bool use_empty() const { return impl->use_empty(); } + + /// Returns true if this value has exactly one use. + bool hasOneUse() const { return impl->hasOneUse(); } + + /// Drop all uses of this object from their respective owners. + void dropAllUses() const { impl->dropAllUses(); } + void print(raw_ostream &os); void dump(); + /// Methods for supporting PointerLikeTypeTraits. + void *getAsOpaquePointer() const { return static_cast(impl); } + static Value getFromOpaquePointer(const void *pointer) { + return reinterpret_cast(const_cast(pointer)); + } + + friend ::llvm::hash_code hash_value(Value arg); + protected: - Value(Kind kind, Type type) : typeAndKind(type, kind) {} + /// The internal implementation of this value. + mutable detail::ValueImpl *impl; -private: - llvm::PointerIntPair typeAndKind; + /// Allow access to 'impl'. + friend OpOperand; }; -inline raw_ostream &operator<<(raw_ostream &os, ValueRef value) { +inline raw_ostream &operator<<(raw_ostream &os, Value value) { value.print(os); return os; } // Utility functions for iterating through Value uses. inline auto Value::use_begin() -> use_iterator { - return use_iterator((OpOperand *)getFirstUse()); + return use_iterator((OpOperand *)impl->getFirstUse()); } inline auto Value::use_end() -> use_iterator { return use_iterator(nullptr); } @@ -127,47 +215,154 @@ inline auto Value::getUses() -> iterator_range { /// Block arguments are values. class BlockArgument : public Value { public: - static bool classof(const Value *value) { - return const_cast(value)->getKind() == Kind::BlockArgument; + using Value::Value; + + /// Temporary methods to enable transition of Value to being used as a + /// value-type. + /// TODO(riverriddle) Remove this when all usages have been removed. + BlockArgument *operator->() { return this; } + + static bool classof(Value value) { + return value.getKind() == Kind::BlockArgument; } - Block *getOwner() { return owner; } + /// Returns the block that owns this argument. + Block *getOwner() const { return getImpl()->owner; } /// Returns the number of this argument. - unsigned getArgNumber(); + unsigned getArgNumber() const; private: - friend class Block; // For access to private constructor. - BlockArgument(Type type, Block *owner) - : Value(Value::Kind::BlockArgument, type), owner(owner) {} - - /// The owner of this operand. - /// TODO: can encode this more efficiently to avoid the space hit of this - /// through bitpacking shenanigans. - Block *const owner; + /// 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(impl); + } + + /// Allow access to `create` and `destroy`. + friend Block; }; /// This is a value defined by a result of an operation. class OpResult : public Value { public: - OpResult(Type type, Operation *owner) - : Value(Value::Kind::OpResult, type), owner(owner) {} + using Value::Value; - static bool classof(const Value *value) { - return const_cast(value)->getKind() == Kind::OpResult; - } + /// Temporary methods to enable transition of Value to being used as a + /// value-type. + /// TODO(riverriddle) Remove these when all usages have been removed. + OpResult *operator*() { return this; } + OpResult *operator->() { return this; } + + static bool classof(Value value) { return value.getKind() == Kind::OpResult; } - Operation *getOwner() { return owner; } + /// Returns the operation that owns this result. + Operation *getOwner() const { return getImpl()->owner; } /// Returns the number of this result. - unsigned getResultNumber(); + unsigned getResultNumber() const; private: - /// The owner of this operand. - /// TODO: can encode this more efficiently to avoid the space hit of this - /// through bitpacking shenanigans. - Operation *const owner; + /// Allocate a new result with the given type and owner. + static OpResult create(Type type, Operation *owner) { + return new detail::OpResultImpl(type, owner); + } + + /// Destroy and deallocate this result. + void destroy() { delete getImpl(); } + + /// Get a raw pointer to the internal implementation. + detail::OpResultImpl *getImpl() const { + return reinterpret_cast(impl); + } + + /// Allow access to `create` and `destroy`. + friend Operation; }; + +/// Make Value hashable. +inline ::llvm::hash_code hash_value(Value arg) { + return ::llvm::hash_value(arg.impl); +} + +/// Using directives that simplify the transition of Value to being value typed. +using BlockArgumentPtr = BlockArgument; +using OpResultPtr = OpResult; +using ValueRef = Value; +using ValuePtr = Value; + } // namespace mlir +namespace llvm { + +template <> struct DenseMapInfo { + static mlir::Value getEmptyKey() { + auto pointer = llvm::DenseMapInfo::getEmptyKey(); + return mlir::Value(static_cast(pointer)); + } + static mlir::Value getTombstoneKey() { + auto pointer = llvm::DenseMapInfo::getTombstoneKey(); + return mlir::Value(static_cast(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(static_cast(pointer)); + } + static mlir::BlockArgument getTombstoneKey() { + auto pointer = llvm::DenseMapInfo::getTombstoneKey(); + return mlir::BlockArgument(static_cast(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 diff --git a/mlir/lib/Analysis/AffineStructures.cpp b/mlir/lib/Analysis/AffineStructures.cpp index 7ab5474..ce96a19 100644 --- a/mlir/lib/Analysis/AffineStructures.cpp +++ b/mlir/lib/Analysis/AffineStructures.cpp @@ -1965,7 +1965,7 @@ void FlatAffineConstraints::addLocalFloorDiv(ArrayRef dividend, bool FlatAffineConstraints::findId(ValueRef id, unsigned *pos) const { unsigned i = 0; for (const auto &mayBeId : ids) { - if (mayBeId.hasValue() && mayBeId.getValue() == &id) { + if (mayBeId.hasValue() && mayBeId.getValue() == id) { *pos = i; return true; } @@ -1976,7 +1976,7 @@ bool FlatAffineConstraints::findId(ValueRef id, unsigned *pos) const { bool FlatAffineConstraints::containsId(ValueRef id) const { return llvm::any_of(ids, [&](const Optional &mayBeId) { - return mayBeId.hasValue() && mayBeId.getValue() == &id; + return mayBeId.hasValue() && mayBeId.getValue() == id; }); } diff --git a/mlir/lib/Analysis/Dominance.cpp b/mlir/lib/Analysis/Dominance.cpp index 060a505..ea1501e 100644 --- a/mlir/lib/Analysis/Dominance.cpp +++ b/mlir/lib/Analysis/Dominance.cpp @@ -129,7 +129,7 @@ bool DominanceInfo::properlyDominates(ValuePtr a, Operation *b) { // block arguments properly dominate all operations in their own block, so // we use a dominates check here, not a properlyDominates check. - return dominates(cast(a)->getOwner(), b->getBlock()); + return dominates(a.cast()->getOwner(), b->getBlock()); } DominanceInfoNode *DominanceInfo::getNode(Block *a) { diff --git a/mlir/lib/Analysis/Liveness.cpp b/mlir/lib/Analysis/Liveness.cpp index bef0b9f..9b7b806 100644 --- a/mlir/lib/Analysis/Liveness.cpp +++ b/mlir/lib/Analysis/Liveness.cpp @@ -174,7 +174,7 @@ Liveness::OperationListT Liveness::resolveLiveness(ValuePtr value) const { if (Operation *defOp = value->getDefiningOp()) currentBlock = defOp->getBlock(); else - currentBlock = cast(value)->getOwner(); + currentBlock = value.cast()->getOwner(); toProcess.push_back(currentBlock); visited.insert(currentBlock); @@ -272,7 +272,7 @@ void Liveness::print(raw_ostream &os) const { if (Operation *defOp = value->getDefiningOp()) os << "val_" << defOp->getName(); else { - auto blockArg = cast(value); + auto blockArg = value.cast(); os << "arg" << blockArg->getArgNumber() << "@" << blockIds[blockArg->getOwner()]; } diff --git a/mlir/lib/Analysis/SliceAnalysis.cpp b/mlir/lib/Analysis/SliceAnalysis.cpp index befe3d3..89ee613 100644 --- a/mlir/lib/Analysis/SliceAnalysis.cpp +++ b/mlir/lib/Analysis/SliceAnalysis.cpp @@ -96,7 +96,7 @@ static void getBackwardSliceImpl(Operation *op, for (auto en : llvm::enumerate(op->getOperands())) { auto operand = en.value(); - if (auto blockArg = dyn_cast(operand)) { + if (auto blockArg = operand.dyn_cast()) { if (auto affIv = getForInductionVarOwner(operand)) { auto *affOp = affIv.getOperation(); if (backwardSlice->count(affOp) == 0) diff --git a/mlir/lib/Dialect/AffineOps/AffineOps.cpp b/mlir/lib/Dialect/AffineOps/AffineOps.cpp index bfe7210..d80f986 100644 --- a/mlir/lib/Dialect/AffineOps/AffineOps.cpp +++ b/mlir/lib/Dialect/AffineOps/AffineOps.cpp @@ -107,7 +107,7 @@ static bool isFunctionRegion(Region *region) { /// function. A value of index type defined at the top level is always a valid /// symbol. bool mlir::isTopLevelValue(ValuePtr value) { - if (auto arg = dyn_cast(value)) + if (auto arg = value.dyn_cast()) return isFunctionRegion(arg->getOwner()->getParent()); return isFunctionRegion(value->getDefiningOp()->getParentRegion()); } @@ -134,7 +134,7 @@ bool mlir::isValidDim(ValuePtr value) { return false; } // This value has to be a block argument for a FuncOp or an affine.for. - auto *parentOp = cast(value)->getOwner()->getParentOp(); + auto *parentOp = value.cast()->getOwner()->getParentOp(); return isa(parentOp) || isa(parentOp); } @@ -1571,7 +1571,7 @@ bool mlir::isForInductionVar(ValuePtr val) { /// Returns the loop parent of an induction variable. If the provided value is /// not an induction variable, then return nullptr. AffineForOp mlir::getForInductionVarOwner(ValuePtr val) { - auto ivArg = dyn_cast(val); + auto ivArg = val.dyn_cast(); if (!ivArg || !ivArg->getOwner()) return AffineForOp(); auto *containingInst = ivArg->getOwner()->getParent()->getParentOp(); diff --git a/mlir/lib/Dialect/Linalg/Analysis/DependenceAnalysis.cpp b/mlir/lib/Dialect/Linalg/Analysis/DependenceAnalysis.cpp index 5fbbdea..be90b1c 100644 --- a/mlir/lib/Dialect/Linalg/Analysis/DependenceAnalysis.cpp +++ b/mlir/lib/Dialect/Linalg/Analysis/DependenceAnalysis.cpp @@ -41,7 +41,7 @@ static StringRef toStringRef(LinalgDependenceGraph::DependenceType dt) { } ValuePtr Aliases::find(ValuePtr v) { - if (isa(v)) + if (v.isa()) return v; auto it = aliases.find(v); @@ -51,7 +51,7 @@ ValuePtr Aliases::find(ValuePtr v) { } while (true) { - if (isa(v)) + if (v.isa()) return v; if (auto alloc = dyn_cast_or_null(v->getDefiningOp())) { if (isStrided(alloc.getType())) diff --git a/mlir/lib/Dialect/LoopOps/LoopOps.cpp b/mlir/lib/Dialect/LoopOps/LoopOps.cpp index d3040c1..8e19eba 100644 --- a/mlir/lib/Dialect/LoopOps/LoopOps.cpp +++ b/mlir/lib/Dialect/LoopOps/LoopOps.cpp @@ -136,7 +136,7 @@ LogicalResult ForOp::moveOutOfLoop(ArrayRef ops) { } ForOp mlir::loop::getForInductionVarOwner(ValuePtr val) { - auto ivArg = dyn_cast(val); + auto ivArg = val.dyn_cast(); if (!ivArg) return ForOp(); assert(ivArg->getOwner() && "unlinked block argument"); diff --git a/mlir/lib/Dialect/SPIRV/Serialization/Serializer.cpp b/mlir/lib/Dialect/SPIRV/Serialization/Serializer.cpp index 7ff471d..424c2e0 100644 --- a/mlir/lib/Dialect/SPIRV/Serialization/Serializer.cpp +++ b/mlir/lib/Dialect/SPIRV/Serialization/Serializer.cpp @@ -509,7 +509,7 @@ void Serializer::printValueIDMap(raw_ostream &os) { << "id = " << valueIDPair.second << ' '; if (auto *op = val->getDefiningOp()) { os << "from op '" << op->getName() << "'"; - } else if (auto arg = dyn_cast(val)) { + } else if (auto arg = val.dyn_cast()) { Block *block = arg->getOwner(); os << "from argument of block " << block << ' '; os << " in op '" << block->getParentOp()->getName() << "'"; diff --git a/mlir/lib/IR/AsmPrinter.cpp b/mlir/lib/IR/AsmPrinter.cpp index a574f87..4eeb5e4 100644 --- a/mlir/lib/IR/AsmPrinter.cpp +++ b/mlir/lib/IR/AsmPrinter.cpp @@ -1612,7 +1612,7 @@ void OperationPrinter::numberValuesInRegion(Region ®ion) { void OperationPrinter::numberValuesInBlock(Block &block) { auto setArgNameFn = [&](ValuePtr arg, StringRef name) { assert(!valueIDs.count(arg) && "arg numbered multiple times"); - assert(cast(arg)->getOwner() == &block && + assert(arg.cast()->getOwner() == &block && "arg not defined in 'block'"); setValueName(arg, name); }; @@ -1658,7 +1658,7 @@ void OperationPrinter::numberValuesInOp(Operation &op) { setValueName(result, name); // Record the result number for groups not anchored at 0. - if (int resultNo = cast(result)->getResultNumber()) + if (int resultNo = result.cast()->getResultNumber()) resultGroups.push_back(resultNo); }; @@ -1831,7 +1831,7 @@ void OperationPrinter::printValueIDImpl(ValuePtr value, bool printResultNo, // If this is a reference to the result of a multi-result operation or // operation, print out the # identifier and make sure to map our lookup // to the first result of the operation. - if (OpResultPtr result = dyn_cast(value)) + if (OpResultPtr result = value.dyn_cast()) getResultIDAndNumber(result, lookupValue, resultNo); auto it = valueIDs.find(lookupValue); diff --git a/mlir/lib/IR/Block.cpp b/mlir/lib/IR/Block.cpp index b168a8f..3abbe10 100644 --- a/mlir/lib/IR/Block.cpp +++ b/mlir/lib/IR/Block.cpp @@ -16,10 +16,10 @@ using namespace mlir; //===----------------------------------------------------------------------===// /// Returns the number of this argument. -unsigned BlockArgument::getArgNumber() { +unsigned BlockArgument::getArgNumber() const { // Arguments are not stored in place, so we have to find it within the list. auto argList = getOwner()->getArguments(); - return std::distance(argList.begin(), llvm::find(argList, this)); + return std::distance(argList.begin(), llvm::find(argList, *this)); } //===----------------------------------------------------------------------===// @@ -29,7 +29,8 @@ unsigned BlockArgument::getArgNumber() { Block::~Block() { assert(!verifyOpOrder() && "Expected valid operation ordering."); clear(); - llvm::DeleteContainerPointers(arguments); + for (BlockArgument arg : arguments) + arg.destroy(); } Region *Block::getParent() const { return parentValidOpOrderPair.getPointer(); } @@ -143,7 +144,7 @@ void Block::recomputeOpOrder() { //===----------------------------------------------------------------------===// BlockArgumentPtr Block::addArgument(Type type) { - auto *arg = new BlockArgument(type, this); + BlockArgument arg = BlockArgument::create(type, this); arguments.push_back(arg); return arg; } @@ -163,7 +164,7 @@ void Block::eraseArgument(unsigned index, bool updatePredTerms) { assert(index < arguments.size()); // Delete the argument. - delete arguments[index]; + arguments[index].destroy(); arguments.erase(arguments.begin() + index); // If we aren't updating predecessors, there is nothing left to do. diff --git a/mlir/lib/IR/Operation.cpp b/mlir/lib/IR/Operation.cpp index 1dc7cb4..77288b2 100644 --- a/mlir/lib/IR/Operation.cpp +++ b/mlir/lib/IR/Operation.cpp @@ -68,23 +68,29 @@ OperationName OperationName::getFromOpaquePointer(void *pointer) { //===----------------------------------------------------------------------===// /// Return the result number of this result. -unsigned OpResult::getResultNumber() { - // Results are always stored consecutively, so use pointer subtraction to - // figure out what number this is. - return this - &getOwner()->getOpResults()[0]; +unsigned OpResult::getResultNumber() const { + // Results are not stored in place, so we have to find it within the list. + auto resList = getOwner()->getOpResults(); + return std::distance(resList.begin(), llvm::find(resList, *this)); } //===----------------------------------------------------------------------===// // OpOperand //===----------------------------------------------------------------------===// -// TODO: This namespace is only required because of a bug in GCC<7.0. -namespace mlir { +OpOperand::OpOperand(Operation *owner, Value value) + : IROperand(owner, value.impl) {} + +/// Return the current value being used by this operand. +Value OpOperand::get() { return (detail::ValueImpl *)IROperand::get(); } + +/// Set the current value being used by this operand. +void OpOperand::set(Value newValue) { IROperand::set(newValue.impl); } + /// Return which operand this is in the operand list. -template <> unsigned OpOperand::getOperandNumber() { +unsigned OpOperand::getOperandNumber() { return this - &getOwner()->getOpOperands()[0]; } -} // end namespace mlir //===----------------------------------------------------------------------===// // BlockOperand @@ -179,7 +185,7 @@ Operation *Operation::create(Location location, OperationName name, auto instResults = op->getOpResults(); for (unsigned i = 0, e = resultTypes.size(); i != e; ++i) - new (&instResults[i]) OpResult(resultTypes[i], op); + new (&instResults[i]) OpResult(OpResult::create(resultTypes[i], op)); auto opOperands = op->getOpOperands(); @@ -256,7 +262,7 @@ Operation::~Operation() { getOperandStorage().~OperandStorage(); for (auto &result : getOpResults()) - result.~OpResult(); + result.destroy(); // Explicitly run the destructors for the successors. for (auto &successor : getBlockOperands()) diff --git a/mlir/lib/IR/OperationSupport.cpp b/mlir/lib/IR/OperationSupport.cpp index 1c68686..5dfd3b0 100644 --- a/mlir/lib/IR/OperationSupport.cpp +++ b/mlir/lib/IR/OperationSupport.cpp @@ -155,7 +155,7 @@ ResultRange::ResultRange(Operation *op) //===----------------------------------------------------------------------===// // ValueRange -ValueRange::ValueRange(ArrayRef values) +ValueRange::ValueRange(ArrayRef values) : ValueRange(values.data(), values.size()) {} ValueRange::ValueRange(OperandRange values) : ValueRange(values.begin().getBase(), values.size()) {} @@ -167,19 +167,18 @@ ValueRange::OwnerT ValueRange::offset_base(const OwnerT &owner, ptrdiff_t index) { if (OpOperand *operand = owner.dyn_cast()) return operand + index; - if (OpResultPtr result = owner.dyn_cast()) + if (OpResult *result = owner.dyn_cast()) return result + index; - return owner.get() + index; + return owner.get() + index; } /// See `detail::indexed_accessor_range_base` for details. -ValuePtr ValueRange::dereference_iterator(const OwnerT &owner, - ptrdiff_t index) { +Value ValueRange::dereference_iterator(const OwnerT &owner, ptrdiff_t index) { // Operands access the held value via 'get'. if (OpOperand *operand = owner.dyn_cast()) return operand[index].get(); // An OpResult is a value, so we can return it directly. - if (OpResultPtr result = owner.dyn_cast()) - return &result[index]; + if (OpResult *result = owner.dyn_cast()) + return result[index]; // Otherwise, this is a raw value array so just index directly. - return owner.get()[index]; + return owner.get()[index]; } diff --git a/mlir/lib/IR/TypeUtilities.cpp b/mlir/lib/IR/TypeUtilities.cpp index 8bc67e4..1fa13a8 100644 --- a/mlir/lib/IR/TypeUtilities.cpp +++ b/mlir/lib/IR/TypeUtilities.cpp @@ -28,10 +28,6 @@ Type mlir::getElementTypeOrSelf(ValuePtr val) { return getElementTypeOrSelf(val->getType()); } -Type mlir::getElementTypeOrSelf(ValueRef val) { - return getElementTypeOrSelf(val.getType()); -} - Type mlir::getElementTypeOrSelf(Attribute attr) { return getElementTypeOrSelf(attr.getType()); } diff --git a/mlir/lib/IR/Value.cpp b/mlir/lib/IR/Value.cpp index d723eec..ffb9601 100644 --- a/mlir/lib/IR/Value.cpp +++ b/mlir/lib/IR/Value.cpp @@ -13,8 +13,8 @@ using namespace mlir; /// If this value is the result of an Operation, return the operation that /// defines it. -Operation *Value::getDefiningOp() { - if (auto *result = dyn_cast()) +Operation *Value::getDefiningOp() const { + if (auto result = dyn_cast()) return result->getOwner(); return nullptr; } -- 2.7.4