forked from OSchip/llvm-project
[mlir] Refactor operation results to use a single use list for all results of the operation.
Summary: A new class is added, IRMultiObjectWithUseList, that allows for representing an IR use list that holds multiple sub values(used in this case for OpResults). This class provides all of the same functionality as the base IRObjectWithUseList, but for specific sub-values. This saves a word per operation result and is a necessary step in optimizing the layout of operation results. For now the use list is placed on the operation itself, so zero-result operations grow by a word. When the work for optimizing layout is finished, this can be moved back to being a trailing object based on memory/runtime benchmarking. Reviewed By: jpienaar Differential Revision: https://reviews.llvm.org/D71955
This commit is contained in:
parent
787e078f3e
commit
0d6ebb4f0d
|
@ -18,7 +18,7 @@
|
|||
|
||||
namespace mlir {
|
||||
/// `Block` represents an ordered list of `Operation`s.
|
||||
class Block : public IRObjectWithUseList,
|
||||
class Block : public IRObjectWithUseList<BlockOperand>,
|
||||
public llvm::ilist_node_with_parent<Block, Region> {
|
||||
public:
|
||||
explicit Block() {}
|
||||
|
|
|
@ -25,7 +25,8 @@ namespace mlir {
|
|||
/// operations are organized into operation blocks represented by a 'Block'
|
||||
/// class.
|
||||
class Operation final
|
||||
: public llvm::ilist_node_with_parent<Operation, Block>,
|
||||
: public IRMultiObjectWithUseList<OpOperand>,
|
||||
public llvm::ilist_node_with_parent<Operation, Block>,
|
||||
private llvm::TrailingObjects<Operation, OpResult, BlockOperand, Region,
|
||||
detail::OperandStorage> {
|
||||
public:
|
||||
|
@ -237,9 +238,6 @@ public:
|
|||
// Results
|
||||
//===--------------------------------------------------------------------===//
|
||||
|
||||
/// Return true if there are no users of any results of this operation.
|
||||
bool use_empty();
|
||||
|
||||
unsigned getNumResults() { return numResults; }
|
||||
|
||||
Value getResult(unsigned idx) { return getOpResult(idx); }
|
||||
|
@ -639,32 +637,6 @@ inline raw_ostream &operator<<(raw_ostream &os, Operation &op) {
|
|||
return os;
|
||||
}
|
||||
|
||||
/// This class implements use iterator for the Operation. This iterates over all
|
||||
/// uses of all results of an Operation.
|
||||
class UseIterator final
|
||||
: public llvm::iterator_facade_base<UseIterator, std::forward_iterator_tag,
|
||||
Operation *> {
|
||||
public:
|
||||
/// Initialize UseIterator for op, specify end to return iterator to last use.
|
||||
explicit UseIterator(Operation *op, bool end = false);
|
||||
|
||||
UseIterator &operator++();
|
||||
Operation *operator->() { return use->getOwner(); }
|
||||
Operation *operator*() { return use->getOwner(); }
|
||||
|
||||
bool operator==(const UseIterator &other) const;
|
||||
bool operator!=(const UseIterator &other) const;
|
||||
|
||||
private:
|
||||
void skipOverResultsWithNoUsers();
|
||||
|
||||
/// The operation whose uses are being iterated over.
|
||||
Operation *op;
|
||||
/// The result of op who's uses are being iterated over.
|
||||
Operation::result_iterator res;
|
||||
/// The use of the result.
|
||||
Value::use_iterator use;
|
||||
};
|
||||
} // end namespace mlir
|
||||
|
||||
namespace llvm {
|
||||
|
|
|
@ -20,65 +20,162 @@
|
|||
namespace mlir {
|
||||
|
||||
class Block;
|
||||
class IROperand;
|
||||
class Operation;
|
||||
class Value;
|
||||
template <typename OperandType> class ValueUseIterator;
|
||||
template <typename OperandType> class ValueUserIterator;
|
||||
template <typename OperandType> class FilteredValueUseIterator;
|
||||
template <typename UseIteratorT, typename OperandType> class ValueUserIterator;
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// IRObjectWithUseList
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
class IRObjectWithUseList {
|
||||
/// This class represents a single IR object that contains a use list.
|
||||
template <typename OperandType> class IRObjectWithUseList {
|
||||
public:
|
||||
~IRObjectWithUseList() {
|
||||
assert(use_empty() && "Cannot destroy a value that still has uses!");
|
||||
}
|
||||
|
||||
/// Returns true if this value has no uses.
|
||||
bool use_empty() const { return firstUse == nullptr; }
|
||||
|
||||
/// Returns true if this value has exactly one use.
|
||||
inline bool hasOneUse() const;
|
||||
|
||||
using use_iterator = ValueUseIterator<IROperand>;
|
||||
using use_range = iterator_range<use_iterator>;
|
||||
|
||||
inline use_iterator use_begin() const;
|
||||
inline use_iterator use_end() const;
|
||||
|
||||
/// Returns a range of all uses, which is useful for iterating over all uses.
|
||||
inline use_range getUses() const;
|
||||
|
||||
using user_iterator = ValueUserIterator<IROperand>;
|
||||
using user_range = iterator_range<user_iterator>;
|
||||
|
||||
inline user_iterator user_begin() const;
|
||||
inline user_iterator user_end() const;
|
||||
|
||||
/// Returns a range of all users.
|
||||
inline user_range getUsers() const;
|
||||
/// Drop all uses of this object from their respective owners.
|
||||
void dropAllUses() {
|
||||
while (!use_empty())
|
||||
use_begin()->drop();
|
||||
}
|
||||
|
||||
/// 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(IRObjectWithUseList *newValue);
|
||||
void replaceAllUsesWith(typename OperandType::ValueType newValue) {
|
||||
assert(this != OperandType::getUseList(newValue) &&
|
||||
"cannot RAUW a value with itself");
|
||||
while (!use_empty())
|
||||
use_begin()->set(newValue);
|
||||
}
|
||||
|
||||
/// Drop all uses of this object from their respective owners.
|
||||
void dropAllUses();
|
||||
//===--------------------------------------------------------------------===//
|
||||
// Uses
|
||||
//===--------------------------------------------------------------------===//
|
||||
|
||||
using use_iterator = ValueUseIterator<OperandType>;
|
||||
using use_range = iterator_range<use_iterator>;
|
||||
|
||||
use_iterator use_begin() const { return use_iterator(firstUse); }
|
||||
use_iterator use_end() const { return use_iterator(nullptr); }
|
||||
|
||||
/// 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 {
|
||||
return firstUse && firstUse->getNextOperandUsingThisValue() == nullptr;
|
||||
}
|
||||
|
||||
/// Returns true if this value has no uses.
|
||||
bool use_empty() const { return firstUse == nullptr; }
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// Users
|
||||
//===--------------------------------------------------------------------===//
|
||||
|
||||
using user_iterator = ValueUserIterator<use_iterator, OperandType>;
|
||||
using user_range = iterator_range<user_iterator>;
|
||||
|
||||
user_iterator user_begin() const { return user_iterator(use_begin()); }
|
||||
user_iterator user_end() const { return user_iterator(use_end()); }
|
||||
|
||||
/// Returns a range of all users.
|
||||
user_range getUsers() const { return {user_begin(), user_end()}; }
|
||||
|
||||
protected:
|
||||
IRObjectWithUseList() {}
|
||||
|
||||
/// Return the first IROperand that is using this value, for use by custom
|
||||
/// Return the first operand that is using this value, for use by custom
|
||||
/// use/def iterators.
|
||||
IROperand *getFirstUse() { return firstUse; }
|
||||
const IROperand *getFirstUse() const { return firstUse; }
|
||||
OperandType *getFirstUse() const { return firstUse; }
|
||||
|
||||
private:
|
||||
friend class IROperand;
|
||||
IROperand *firstUse = nullptr;
|
||||
template <typename DerivedT, typename IRValueTy> friend class IROperand;
|
||||
OperandType *firstUse = nullptr;
|
||||
};
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// IRMultiObjectWithUseList
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// This class represents multiple IR objects with a single use list. This class
|
||||
/// provides wrapper functionality for manipulating the uses of a single object.
|
||||
template <typename OperandType>
|
||||
class IRMultiObjectWithUseList : public IRObjectWithUseList<OperandType> {
|
||||
public:
|
||||
using BaseType = IRObjectWithUseList<OperandType>;
|
||||
using ValueType = typename OperandType::ValueType;
|
||||
|
||||
/// Drop all uses of `value` from their respective owners.
|
||||
void dropAllUses(ValueType value) {
|
||||
assert(this == OperandType::getUseList(value) &&
|
||||
"value not attached to this use list");
|
||||
for (OperandType &use : llvm::make_early_inc_range(getUses(value)))
|
||||
use.drop();
|
||||
}
|
||||
using BaseType::dropAllUses;
|
||||
|
||||
/// Replace all uses of `oldValue` 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(ValueType oldValue, ValueType newValue) {
|
||||
assert(this == OperandType::getUseList(oldValue) &&
|
||||
"value not attached to this use list");
|
||||
assert(this != OperandType::getUseList(newValue) &&
|
||||
"cannot RAUW a value with itself");
|
||||
for (OperandType &use : llvm::make_early_inc_range(getUses(oldValue)))
|
||||
use.set(newValue);
|
||||
}
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// Uses
|
||||
//===--------------------------------------------------------------------===//
|
||||
|
||||
using filtered_use_iterator = FilteredValueUseIterator<OperandType>;
|
||||
using filtered_use_range = iterator_range<filtered_use_iterator>;
|
||||
|
||||
filtered_use_iterator use_begin(ValueType value) const {
|
||||
return filtered_use_iterator(this->getFirstUse(), value);
|
||||
}
|
||||
filtered_use_iterator use_end(ValueType) const { return use_end(); }
|
||||
filtered_use_range getUses(ValueType value) const {
|
||||
return {use_begin(value), use_end(value)};
|
||||
}
|
||||
bool hasOneUse(ValueType value) const {
|
||||
return mlir::has_single_element(getUses(value));
|
||||
}
|
||||
bool use_empty(ValueType value) const {
|
||||
return use_begin(value) == use_end(value);
|
||||
}
|
||||
using BaseType::getUses;
|
||||
using BaseType::hasOneUse;
|
||||
using BaseType::use_begin;
|
||||
using BaseType::use_empty;
|
||||
using BaseType::use_end;
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// Users
|
||||
//===--------------------------------------------------------------------===//
|
||||
|
||||
using filtered_user_iterator =
|
||||
ValueUserIterator<filtered_use_iterator, OperandType>;
|
||||
using filtered_user_range = iterator_range<filtered_user_iterator>;
|
||||
|
||||
filtered_user_iterator user_begin(ValueType value) const {
|
||||
return {use_begin(value)};
|
||||
}
|
||||
filtered_user_iterator user_end(ValueType value) const { return {use_end()}; }
|
||||
filtered_user_range getUsers(ValueType value) const {
|
||||
return {user_begin(value), user_end(value)};
|
||||
}
|
||||
using BaseType::getUsers;
|
||||
using BaseType::user_begin;
|
||||
using BaseType::user_end;
|
||||
};
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
@ -86,19 +183,24 @@ private:
|
|||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// A reference to a value, suitable for use as an operand of an operation.
|
||||
class IROperand {
|
||||
/// IRValueTy is the root type to use for values this tracks. Derived operand
|
||||
/// types are expected to provide the following:
|
||||
/// * static IRObjectWithUseList *getUseList(IRValueTy value);
|
||||
/// - Provide the use list that is attached to the given value.
|
||||
template <typename DerivedT, typename IRValueTy> class IROperand {
|
||||
public:
|
||||
using ValueType = IRValueTy;
|
||||
|
||||
IROperand(Operation *owner) : owner(owner) {}
|
||||
IROperand(Operation *owner, IRObjectWithUseList *value)
|
||||
: value(value), owner(owner) {
|
||||
IROperand(Operation *owner, ValueType value) : value(value), owner(owner) {
|
||||
insertIntoCurrent();
|
||||
}
|
||||
|
||||
/// Return the current value being used by this operand.
|
||||
IRObjectWithUseList *get() const { return value; }
|
||||
ValueType get() const { return value; }
|
||||
|
||||
/// Set the current value being used by this operand.
|
||||
void set(IRObjectWithUseList *newValue) {
|
||||
void set(ValueType newValue) {
|
||||
// It isn't worth optimizing for the case of switching operands on a single
|
||||
// value.
|
||||
removeFromCurrent();
|
||||
|
@ -123,7 +225,7 @@ public:
|
|||
/// Return the next operand on the use-list of the value we are referring to.
|
||||
/// This should generally only be used by the internal implementation details
|
||||
/// of the SSA machinery.
|
||||
IROperand *getNextOperandUsingThisValue() { return nextUse; }
|
||||
DerivedT *getNextOperandUsingThisValue() { return nextUse; }
|
||||
|
||||
/// We support a move constructor so IROperand's can be in vectors, but this
|
||||
/// shouldn't be used by general clients.
|
||||
|
@ -143,15 +245,15 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
/// The value used as this operand. This can be null when in a
|
||||
/// "dropAllUses" state.
|
||||
IRObjectWithUseList *value = nullptr;
|
||||
/// The value used as this operand. This can be null when in a 'dropAllUses'
|
||||
/// state.
|
||||
ValueType value = {};
|
||||
|
||||
/// The next operand in the use-chain.
|
||||
IROperand *nextUse = nullptr;
|
||||
DerivedT *nextUse = nullptr;
|
||||
|
||||
/// This points to the previous link in the use-chain.
|
||||
IROperand **back = nullptr;
|
||||
DerivedT **back = nullptr;
|
||||
|
||||
/// The operation owner of this operand.
|
||||
Operation *const owner;
|
||||
|
@ -169,11 +271,12 @@ private:
|
|||
}
|
||||
|
||||
void insertIntoCurrent() {
|
||||
back = &value->firstUse;
|
||||
nextUse = value->firstUse;
|
||||
auto *useList = DerivedT::getUseList(value);
|
||||
back = &useList->firstUse;
|
||||
nextUse = useList->firstUse;
|
||||
if (nextUse)
|
||||
nextUse->back = &nextUse;
|
||||
value->firstUse = this;
|
||||
useList->firstUse = static_cast<DerivedT *>(this);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -182,15 +285,12 @@ private:
|
|||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// Terminator operations can have Block operands to represent successors.
|
||||
class BlockOperand : public IROperand {
|
||||
class BlockOperand : public IROperand<BlockOperand, Block *> {
|
||||
public:
|
||||
using IROperand::IROperand;
|
||||
using IROperand<BlockOperand, Block *>::IROperand;
|
||||
|
||||
/// Return the current block being used by this operand.
|
||||
Block *get();
|
||||
|
||||
/// Set the current value being used by this operand.
|
||||
void set(Block *block);
|
||||
/// Provide the use list that is attached to the given block.
|
||||
static IRObjectWithUseList<BlockOperand> *getUseList(Block *value);
|
||||
|
||||
/// Return which operand this is in the operand list of the User.
|
||||
unsigned getOperandNumber();
|
||||
|
@ -207,17 +307,35 @@ private:
|
|||
// OpOperand
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// A reference to a value, suitable for use as an operand of an operation.
|
||||
class OpOperand : public IROperand {
|
||||
namespace detail {
|
||||
/// This class provides an opaque type erased wrapper around a `Value`.
|
||||
class OpaqueValue {
|
||||
public:
|
||||
OpOperand(Operation *owner) : IROperand(owner) {}
|
||||
OpOperand(Operation *owner, Value value);
|
||||
/// Implicit conversion from 'Value'.
|
||||
OpaqueValue(Value value);
|
||||
OpaqueValue(std::nullptr_t = nullptr) : impl(nullptr) {}
|
||||
OpaqueValue(const OpaqueValue &) = default;
|
||||
OpaqueValue &operator=(const OpaqueValue &) = default;
|
||||
operator bool() const { return impl; }
|
||||
|
||||
/// Implicit conversion back to 'Value'.
|
||||
operator Value() const;
|
||||
|
||||
private:
|
||||
void *impl;
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
/// A reference to a value, suitable for use as an operand of an operation.
|
||||
class OpOperand : public IROperand<OpOperand, detail::OpaqueValue> {
|
||||
public:
|
||||
using IROperand<OpOperand, detail::OpaqueValue>::IROperand;
|
||||
|
||||
/// Provide the use list that is attached to the given value.
|
||||
static IRObjectWithUseList<OpOperand> *getUseList(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);
|
||||
Value get() const;
|
||||
|
||||
/// Return which operand this is in the operand list of the User.
|
||||
unsigned getOperandNumber();
|
||||
|
@ -227,67 +345,101 @@ public:
|
|||
// ValueUseIterator
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// An iterator over all uses of a ValueBase.
|
||||
template <typename OperandType>
|
||||
class ValueUseIterator
|
||||
: public std::iterator<std::forward_iterator_tag, OperandType> {
|
||||
namespace detail {
|
||||
/// A base iterator class that allows for iterating over the uses of a value.
|
||||
/// This is templated to allow for derived iterators to override specific
|
||||
/// iterator methods.
|
||||
template <typename DerivedT, typename OperandType>
|
||||
class ValueUseIteratorImpl
|
||||
: public llvm::iterator_facade_base<DerivedT, std::forward_iterator_tag,
|
||||
OperandType> {
|
||||
public:
|
||||
ValueUseIterator() = default;
|
||||
explicit ValueUseIterator(OperandType *current) : current(current) {}
|
||||
OperandType *operator->() const { return current; }
|
||||
OperandType &operator*() const { return *current; }
|
||||
template <typename T>
|
||||
ValueUseIteratorImpl(const ValueUseIteratorImpl<T, OperandType> &other)
|
||||
: current(other.getOperand()) {}
|
||||
ValueUseIteratorImpl(OperandType *current = nullptr) : current(current) {}
|
||||
|
||||
Operation *getUser() const { return current->getOwner(); }
|
||||
OperandType *getOperand() const { return current; }
|
||||
|
||||
ValueUseIterator &operator++() {
|
||||
OperandType &operator*() const { return *current; }
|
||||
|
||||
using llvm::iterator_facade_base<DerivedT, std::forward_iterator_tag,
|
||||
OperandType>::operator++;
|
||||
ValueUseIteratorImpl &operator++() {
|
||||
assert(current && "incrementing past end()!");
|
||||
current = (OperandType *)current->getNextOperandUsingThisValue();
|
||||
return *this;
|
||||
}
|
||||
|
||||
ValueUseIterator operator++(int unused) {
|
||||
ValueUseIterator copy = *this;
|
||||
++*this;
|
||||
return copy;
|
||||
bool operator==(const ValueUseIteratorImpl &rhs) const {
|
||||
return current == rhs.current;
|
||||
}
|
||||
|
||||
friend bool operator==(ValueUseIterator lhs, ValueUseIterator rhs) {
|
||||
return lhs.current == rhs.current;
|
||||
}
|
||||
|
||||
friend bool operator!=(ValueUseIterator lhs, ValueUseIterator rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
private:
|
||||
protected:
|
||||
OperandType *current;
|
||||
};
|
||||
|
||||
inline auto IRObjectWithUseList::use_begin() const -> use_iterator {
|
||||
return use_iterator(firstUse);
|
||||
}
|
||||
} // end namespace detail
|
||||
|
||||
inline auto IRObjectWithUseList::use_end() const -> use_iterator {
|
||||
return use_iterator(nullptr);
|
||||
}
|
||||
/// An iterator over all of the uses of an IR object.
|
||||
template <typename OperandType>
|
||||
class ValueUseIterator
|
||||
: public detail::ValueUseIteratorImpl<ValueUseIterator<OperandType>,
|
||||
OperandType> {
|
||||
public:
|
||||
using detail::ValueUseIteratorImpl<ValueUseIterator<OperandType>,
|
||||
OperandType>::ValueUseIteratorImpl;
|
||||
};
|
||||
|
||||
inline auto IRObjectWithUseList::getUses() const -> use_range {
|
||||
return {use_begin(), use_end()};
|
||||
}
|
||||
/// This class represents an iterator of the uses of a IR object that optionally
|
||||
/// filters on a specific sub-value. This allows for filtering the uses of an
|
||||
/// IRMultiObjectWithUseList.
|
||||
template <typename OperandType>
|
||||
class FilteredValueUseIterator
|
||||
: public detail::ValueUseIteratorImpl<FilteredValueUseIterator<OperandType>,
|
||||
OperandType> {
|
||||
public:
|
||||
using BaseT =
|
||||
detail::ValueUseIteratorImpl<FilteredValueUseIterator<OperandType>,
|
||||
OperandType>;
|
||||
|
||||
/// Returns true if this value has exactly one use.
|
||||
inline bool IRObjectWithUseList::hasOneUse() const {
|
||||
return firstUse && firstUse->getNextOperandUsingThisValue() == nullptr;
|
||||
}
|
||||
FilteredValueUseIterator() = default;
|
||||
FilteredValueUseIterator(const ValueUseIterator<OperandType> &it)
|
||||
: BaseT(it), filterVal(nullptr) {}
|
||||
FilteredValueUseIterator(OperandType *current,
|
||||
typename OperandType::ValueType filterVal)
|
||||
: BaseT(current), filterVal(filterVal) {
|
||||
findNextValid();
|
||||
}
|
||||
|
||||
using BaseT::operator++;
|
||||
FilteredValueUseIterator<OperandType> &operator++() {
|
||||
BaseT::operator++();
|
||||
findNextValid();
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
void findNextValid() {
|
||||
if (!filterVal)
|
||||
return;
|
||||
while (this->current && ((OperandType *)this->current)->get() != filterVal)
|
||||
BaseT::operator++();
|
||||
}
|
||||
|
||||
/// An optional value to use to filter specific uses.
|
||||
typename OperandType::ValueType filterVal;
|
||||
};
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// ValueUserIterator
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// An iterator over all users of a ValueBase.
|
||||
template <typename OperandType>
|
||||
template <typename UseIteratorT, typename OperandType>
|
||||
class ValueUserIterator final
|
||||
: public llvm::mapped_iterator<ValueUseIterator<OperandType>,
|
||||
: public llvm::mapped_iterator<UseIteratorT,
|
||||
Operation *(*)(OperandType &)> {
|
||||
static Operation *unwrap(OperandType &value) { return value.getOwner(); }
|
||||
|
||||
|
@ -296,24 +448,12 @@ public:
|
|||
using reference = Operation *;
|
||||
|
||||
/// Initializes the result type iterator to the specified result iterator.
|
||||
ValueUserIterator(ValueUseIterator<OperandType> it)
|
||||
: llvm::mapped_iterator<ValueUseIterator<OperandType>,
|
||||
Operation *(*)(OperandType &)>(it, &unwrap) {}
|
||||
ValueUserIterator(UseIteratorT it)
|
||||
: llvm::mapped_iterator<UseIteratorT, Operation *(*)(OperandType &)>(
|
||||
it, &unwrap) {}
|
||||
Operation *operator->() { return **this; }
|
||||
};
|
||||
|
||||
inline auto IRObjectWithUseList::user_begin() const -> user_iterator {
|
||||
return user_iterator(use_begin());
|
||||
}
|
||||
|
||||
inline auto IRObjectWithUseList::user_end() const -> user_iterator {
|
||||
return user_iterator(use_end());
|
||||
}
|
||||
|
||||
inline auto IRObjectWithUseList::getUsers() const -> user_range {
|
||||
return {user_begin(), user_end()};
|
||||
}
|
||||
|
||||
} // namespace mlir
|
||||
|
||||
#endif
|
||||
|
|
|
@ -27,7 +27,7 @@ class Value;
|
|||
|
||||
namespace detail {
|
||||
/// The internal implementation of a Value.
|
||||
class ValueImpl : public IRObjectWithUseList {
|
||||
class ValueImpl {
|
||||
protected:
|
||||
/// This enumerates all of the SSA value kinds.
|
||||
enum class Kind {
|
||||
|
@ -46,7 +46,8 @@ private:
|
|||
};
|
||||
|
||||
/// The internal implementation of a BlockArgument.
|
||||
class BlockArgumentImpl : public ValueImpl {
|
||||
class BlockArgumentImpl : public ValueImpl,
|
||||
public IRObjectWithUseList<OpOperand> {
|
||||
BlockArgumentImpl(Type type, Block *owner)
|
||||
: ValueImpl(Kind::BlockArgument, type), owner(owner) {}
|
||||
|
||||
|
@ -132,13 +133,6 @@ public:
|
|||
/// place.
|
||||
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(Value newValue) const {
|
||||
impl->replaceAllUsesWith(newValue.impl);
|
||||
}
|
||||
|
||||
/// If this value is the result of an operation, return the operation that
|
||||
/// defines it.
|
||||
Operation *getDefiningOp() const;
|
||||
|
@ -150,32 +144,52 @@ public:
|
|||
/// Return the Region in which this Value is defined.
|
||||
Region *getParentRegion();
|
||||
|
||||
using use_iterator = ValueUseIterator<OpOperand>;
|
||||
using use_range = iterator_range<use_iterator>;
|
||||
//===--------------------------------------------------------------------===//
|
||||
// UseLists
|
||||
//===--------------------------------------------------------------------===//
|
||||
|
||||
inline use_iterator use_begin();
|
||||
inline use_iterator use_end();
|
||||
|
||||
/// Returns a range of all uses, which is useful for iterating over all uses.
|
||||
inline use_range getUses();
|
||||
|
||||
using user_iterator = ValueUserIterator<IROperand>;
|
||||
using user_range = iterator_range<user_iterator>;
|
||||
|
||||
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(); }
|
||||
/// Provide the use list that is attached to this value.
|
||||
IRObjectWithUseList<OpOperand> *getUseList() const;
|
||||
|
||||
/// Drop all uses of this object from their respective owners.
|
||||
void dropAllUses() const { impl->dropAllUses(); }
|
||||
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;
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// Uses
|
||||
|
||||
/// This class implements an iterator over the uses of a value.
|
||||
using use_iterator = FilteredValueUseIterator<OpOperand>;
|
||||
using use_range = iterator_range<use_iterator>;
|
||||
|
||||
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<use_iterator, OpOperand>;
|
||||
using user_range = iterator_range<user_iterator>;
|
||||
|
||||
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);
|
||||
void dump();
|
||||
|
@ -201,17 +215,6 @@ inline raw_ostream &operator<<(raw_ostream &os, Value value) {
|
|||
return os;
|
||||
}
|
||||
|
||||
// Utility functions for iterating through Value uses.
|
||||
inline auto Value::use_begin() -> use_iterator {
|
||||
return use_iterator((OpOperand *)impl->getFirstUse());
|
||||
}
|
||||
|
||||
inline auto Value::use_end() -> use_iterator { return use_iterator(nullptr); }
|
||||
|
||||
inline auto Value::getUses() -> iterator_range<use_iterator> {
|
||||
return {use_begin(), use_end()};
|
||||
}
|
||||
|
||||
/// Block arguments are values.
|
||||
class BlockArgument : public Value {
|
||||
public:
|
||||
|
@ -248,6 +251,9 @@ private:
|
|||
|
||||
/// Allow access to `create` and `destroy`.
|
||||
friend Block;
|
||||
|
||||
/// Allow access to 'getImpl'.
|
||||
friend Value;
|
||||
};
|
||||
|
||||
/// This is a value defined by a result of an operation.
|
||||
|
|
|
@ -533,22 +533,13 @@ void Operation::dropAllReferences() {
|
|||
/// This drops all uses of any values defined by this operation or its nested
|
||||
/// regions, wherever they are located.
|
||||
void Operation::dropAllDefinedValueUses() {
|
||||
for (auto &val : getOpResults())
|
||||
val.dropAllUses();
|
||||
dropAllUses();
|
||||
|
||||
for (auto ®ion : getRegions())
|
||||
for (auto &block : region)
|
||||
block.dropAllDefinedValueUses();
|
||||
}
|
||||
|
||||
/// Return true if there are no users of any results of this operation.
|
||||
bool Operation::use_empty() {
|
||||
for (auto result : getResults())
|
||||
if (!result->use_empty())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Operation::setSuccessor(Block *block, unsigned index) {
|
||||
assert(index < getNumSuccessors());
|
||||
getBlockOperands()[index].set(block);
|
||||
|
@ -1144,50 +1135,3 @@ void impl::ensureRegionTerminator(
|
|||
|
||||
block.push_back(buildTerminatorOp());
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// UseIterator
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
UseIterator::UseIterator(Operation *op, bool end)
|
||||
: op(op), res(end ? op->result_end() : op->result_begin()) {
|
||||
// Only initialize current use if there are results/can be uses.
|
||||
if (op->getNumResults())
|
||||
skipOverResultsWithNoUsers();
|
||||
}
|
||||
|
||||
UseIterator &UseIterator::operator++() {
|
||||
// We increment over uses, if we reach the last use then move to next
|
||||
// result.
|
||||
if (use != (*res)->use_end())
|
||||
++use;
|
||||
if (use == (*res)->use_end()) {
|
||||
++res;
|
||||
skipOverResultsWithNoUsers();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool UseIterator::operator==(const UseIterator &other) const {
|
||||
if (op != other.op)
|
||||
return false;
|
||||
if (op->getNumResults() == 0)
|
||||
return true;
|
||||
return res == other.res && use == other.use;
|
||||
}
|
||||
|
||||
bool UseIterator::operator!=(const UseIterator &other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
void UseIterator::skipOverResultsWithNoUsers() {
|
||||
while (res != op->result_end() && (*res)->use_empty())
|
||||
++res;
|
||||
|
||||
// If we are at the last result, then set use to first use of
|
||||
// first result (sentinel value used for end).
|
||||
if (res == op->result_end())
|
||||
use = {};
|
||||
else
|
||||
use = (*res)->use_begin();
|
||||
}
|
||||
|
|
|
@ -32,15 +32,65 @@ Region *Value::getParentRegion() {
|
|||
return cast<BlockArgument>()->getOwner()->getParent();
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Value::UseLists
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// Provide the use list that is attached to this value.
|
||||
IRObjectWithUseList<OpOperand> *Value::getUseList() const {
|
||||
if (BlockArgument arg = dyn_cast<BlockArgument>())
|
||||
return arg.getImpl();
|
||||
return cast<OpResult>().getOwner();
|
||||
}
|
||||
|
||||
/// Drop all uses of this object from their respective owners.
|
||||
void Value::dropAllUses() const {
|
||||
if (BlockArgument arg = dyn_cast<BlockArgument>())
|
||||
return arg.getImpl()->dropAllUses();
|
||||
return cast<OpResult>().getOwner()->dropAllUses(*this);
|
||||
}
|
||||
|
||||
/// 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 Value::replaceAllUsesWith(Value newValue) const {
|
||||
if (BlockArgument arg = dyn_cast<BlockArgument>())
|
||||
return arg.getImpl()->replaceAllUsesWith(newValue);
|
||||
IRMultiObjectWithUseList<OpOperand> *useList = cast<OpResult>().getOwner();
|
||||
useList->replaceAllUsesWith(*this, newValue);
|
||||
}
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// Uses
|
||||
|
||||
auto Value::use_begin() const -> use_iterator {
|
||||
if (BlockArgument arg = dyn_cast<BlockArgument>())
|
||||
return arg.getImpl()->use_begin();
|
||||
return cast<OpResult>().getOwner()->use_begin(*this);
|
||||
}
|
||||
|
||||
/// Returns true if this value has exactly one use.
|
||||
bool Value::hasOneUse() const {
|
||||
if (BlockArgument arg = dyn_cast<BlockArgument>())
|
||||
return arg.getImpl()->hasOneUse();
|
||||
return cast<OpResult>().getOwner()->hasOneUse(*this);
|
||||
}
|
||||
|
||||
/// Returns true if this value has no uses.
|
||||
bool Value::use_empty() const {
|
||||
if (BlockArgument arg = dyn_cast<BlockArgument>())
|
||||
return arg.getImpl()->use_empty();
|
||||
return cast<OpResult>().getOwner()->use_empty(*this);
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// BlockOperand
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// Return the current block being used by this operand.
|
||||
Block *BlockOperand::get() { return static_cast<Block *>(IROperand::get()); }
|
||||
|
||||
/// Set the current value being used by this operand.
|
||||
void BlockOperand::set(Block *block) { IROperand::set(block); }
|
||||
/// Provide the use list that is attached to the given block.
|
||||
IRObjectWithUseList<BlockOperand> *BlockOperand::getUseList(Block *value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
/// Return which operand this is in the operand list.
|
||||
unsigned BlockOperand::getOperandNumber() {
|
||||
|
@ -51,14 +101,15 @@ unsigned BlockOperand::getOperandNumber() {
|
|||
// OpOperand
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
OpOperand::OpOperand(Operation *owner, Value value)
|
||||
: IROperand(owner, value.impl) {}
|
||||
/// Provide the use list that is attached to the given value.
|
||||
IRObjectWithUseList<OpOperand> *OpOperand::getUseList(Value value) {
|
||||
return value.getUseList();
|
||||
}
|
||||
|
||||
/// 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); }
|
||||
Value OpOperand::get() const {
|
||||
return IROperand<OpOperand, detail::OpaqueValue>::get();
|
||||
}
|
||||
|
||||
/// Return which operand this is in the operand list.
|
||||
unsigned OpOperand::getOperandNumber() {
|
||||
|
@ -66,22 +117,14 @@ unsigned OpOperand::getOperandNumber() {
|
|||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// IRObjectWithUseList implementation.
|
||||
// detail::OpaqueValue
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// 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 IRObjectWithUseList::replaceAllUsesWith(IRObjectWithUseList *newValue) {
|
||||
assert(this != newValue && "cannot RAUW a value with itself");
|
||||
while (!use_empty()) {
|
||||
use_begin()->set(newValue);
|
||||
}
|
||||
}
|
||||
/// Implicit conversion from 'Value'.
|
||||
detail::OpaqueValue::OpaqueValue(Value value)
|
||||
: impl(value.getAsOpaquePointer()) {}
|
||||
|
||||
/// Drop all uses of this object from their respective owners.
|
||||
void IRObjectWithUseList::dropAllUses() {
|
||||
while (!use_empty()) {
|
||||
use_begin()->drop();
|
||||
}
|
||||
/// Implicit conversion back to 'Value'.
|
||||
detail::OpaqueValue::operator Value() const {
|
||||
return Value::getFromOpaquePointer(impl);
|
||||
}
|
||||
|
|
|
@ -29,13 +29,9 @@ template <> struct GraphTraits<Block *> {
|
|||
using GraphType = Block *;
|
||||
using NodeRef = Operation *;
|
||||
|
||||
using ChildIteratorType = UseIterator;
|
||||
static ChildIteratorType child_begin(NodeRef n) {
|
||||
return ChildIteratorType(n);
|
||||
}
|
||||
static ChildIteratorType child_end(NodeRef n) {
|
||||
return ChildIteratorType(n, /*end=*/true);
|
||||
}
|
||||
using ChildIteratorType = Operation::user_iterator;
|
||||
static ChildIteratorType child_begin(NodeRef n) { return n->user_begin(); }
|
||||
static ChildIteratorType child_end(NodeRef n) { return n->user_end(); }
|
||||
|
||||
// Operation's destructor is private so use Operation* instead and use
|
||||
// mapped iterator.
|
||||
|
|
Loading…
Reference in New Issue