1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-22 10:42:39 +01:00

ADT: Add sentinel tracking and custom tags to ilists

This adds two declarative configuration options for intrusive lists
(available for simple_ilist, iplist, and ilist).  Both of these options
affect ilist_node interoperability and need to be passed both to the
node and the list.  Instead of adding a new traits class, they're
specified as optional template parameters (in any order).

The two options:

 1. Pass ilist_sentinel_tracking<true> or ilist_sentinel_tracking<false>
    to control whether there's a bit on ilist_node "prev" pointer
    indicating whether it's the sentinel.  The default behaviour is to
    use a bit if and only if LLVM_ENABLE_ABI_BREAKING_CHECKS.

 2. Pass ilist_tag<TagA> and ilist_tag<TagB> to allow insertion of a
    single node into two different lists (simultaneously).

I have an immediate use-case for (1) ilist_sentinel_tracking: fixing the
validation semantics of MachineBasicBlock::reverse_iterator to match
ilist::reverse_iterator (ala r280032: see the comments at the end of the
commit message there).  I'm adding (2) ilist_tag in the same commit to
validate that the options framework supports expansion.  Justin Bogner
mentioned this might enable a possible cleanup in SelectionDAG, but I'll
leave this to others to explore.  In the meantime, the unit tests and
the comments for simple_ilist and ilist_node have usage examples.

Note that there's a layer of indirection to support optional,
out-of-order, template paramaters.  Internal classes are templated on an
instantiation of the non-variadic ilist_detail::node_options.
User-facing classes use ilist_detail::compute_node_options to compute
the correct instantiation of ilist_detail::node_options.

The comments for ilist_detail::is_valid_option describe how to add new
options (e.g., ilist_packed_int<int NumBits>).

llvm-svn: 281167
This commit is contained in:
Duncan P. N. Exon Smith 2016-09-11 16:20:53 +00:00
parent ceb721fec8
commit 2a55cf5af8
14 changed files with 644 additions and 158 deletions

View File

@ -362,13 +362,18 @@ public:
/// An intrusive list with ownership and callbacks specified/controlled by
/// ilist_traits, only with API safe for polymorphic types.
template <class T>
class iplist : public iplist_impl<simple_ilist<T>, ilist_traits<T>> {};
///
/// The \p Options parameters are the same as those for \a simple_ilist. See
/// there for a description of what's available.
template <class T, class... Options>
class iplist
: public iplist_impl<simple_ilist<T, Options...>, ilist_traits<T>> {};
/// An intrusive list with ownership and callbacks specified/controlled by
/// ilist_traits, with API that is unsafe for polymorphic types.
template <class T> class ilist : public iplist<T> {
typedef iplist<T> base_list_type;
template <class T, class... Options>
class ilist : public iplist<T, Options...> {
typedef iplist<T, Options...> base_list_type;
public:
typedef typename base_list_type::size_type size_type;

View File

@ -18,10 +18,10 @@
namespace llvm {
/// Implementations of list algorithms using ilist_node_base.
class ilist_base {
typedef ilist_node_base node_base_type;
template <bool EnableSentinelTracking> class ilist_base {
public:
typedef ilist_node_base<EnableSentinelTracking> node_base_type;
static void insertBeforeImpl(node_base_type &Next, node_base_type &N) {
node_base_type &Prev = *Next.getPrev();
N.setNext(&Next);

View File

@ -20,11 +20,21 @@ namespace llvm {
namespace ilist_detail {
template <class NodeTy> struct ConstCorrectNodeType {
typedef ilist_node<NodeTy> type;
/// Find const-correct node types.
template <class OptionsT, bool IsConst> struct IteratorTraits;
template <class OptionsT> struct IteratorTraits<OptionsT, false> {
typedef typename OptionsT::value_type value_type;
typedef typename OptionsT::pointer pointer;
typedef typename OptionsT::reference reference;
typedef ilist_node_impl<OptionsT> *node_pointer;
typedef ilist_node_impl<OptionsT> &node_reference;
};
template <class NodeTy> struct ConstCorrectNodeType<const NodeTy> {
typedef const ilist_node<NodeTy> type;
template <class OptionsT> struct IteratorTraits<OptionsT, true> {
typedef const typename OptionsT::value_type value_type;
typedef typename OptionsT::const_pointer pointer;
typedef typename OptionsT::const_reference reference;
typedef const ilist_node_impl<OptionsT> *node_pointer;
typedef const ilist_node_impl<OptionsT> &node_reference;
};
template <bool IsReverse> struct IteratorHelper;
@ -42,28 +52,29 @@ template <> struct IteratorHelper<true> : ilist_detail::NodeAccess {
} // end namespace ilist_detail
/// Iterator for intrusive lists based on ilist_node.
template <typename NodeTy, bool IsReverse>
class ilist_iterator : ilist_detail::SpecificNodeAccess<
typename std::remove_const<NodeTy>::type> {
typedef ilist_detail::SpecificNodeAccess<
typename std::remove_const<NodeTy>::type>
Access;
template <class OptionsT, bool IsReverse, bool IsConst>
class ilist_iterator : ilist_detail::SpecificNodeAccess<OptionsT> {
friend ilist_iterator<OptionsT, IsReverse, !IsConst>;
friend ilist_iterator<OptionsT, !IsReverse, IsConst>;
friend ilist_iterator<OptionsT, !IsReverse, !IsConst>;
typedef ilist_detail::IteratorTraits<OptionsT, IsConst> Traits;
typedef ilist_detail::SpecificNodeAccess<OptionsT> Access;
public:
typedef NodeTy value_type;
typedef value_type *pointer;
typedef value_type &reference;
typedef typename Traits::value_type value_type;
typedef typename Traits::pointer pointer;
typedef typename Traits::reference reference;
typedef ptrdiff_t difference_type;
typedef std::bidirectional_iterator_tag iterator_category;
typedef typename std::add_const<value_type>::type *const_pointer;
typedef typename std::add_const<value_type>::type &const_reference;
typedef typename ilist_detail::ConstCorrectNodeType<NodeTy>::type node_type;
typedef node_type *node_pointer;
typedef node_type &node_reference;
typedef typename OptionsT::const_pointer const_pointer;
typedef typename OptionsT::const_reference const_reference;
private:
typedef typename Traits::node_pointer node_pointer;
typedef typename Traits::node_reference node_reference;
node_pointer NodePtr;
public:
@ -76,19 +87,18 @@ public:
// This is templated so that we can allow constructing a const iterator from
// a nonconst iterator...
template <class node_ty>
template <bool RHSIsConst>
ilist_iterator(
const ilist_iterator<node_ty, IsReverse> &RHS,
typename std::enable_if<std::is_convertible<node_ty *, NodeTy *>::value,
void *>::type = nullptr)
: NodePtr(RHS.getNodePtr()) {}
const ilist_iterator<OptionsT, IsReverse, RHSIsConst> &RHS,
typename std::enable_if<IsConst || !RHSIsConst, void *>::type = nullptr)
: NodePtr(RHS.NodePtr) {}
// This is templated so that we can allow assigning to a const iterator from
// a nonconst iterator...
template <class node_ty>
const ilist_iterator &
operator=(const ilist_iterator<node_ty, IsReverse> &RHS) {
NodePtr = RHS.getNodePtr();
template <bool RHSIsConst>
typename std::enable_if<IsConst || !RHSIsConst, ilist_iterator &>::type
operator=(const ilist_iterator<OptionsT, IsReverse, RHSIsConst> &RHS) {
NodePtr = RHS.NodePtr;
return *this;
}
@ -96,10 +106,19 @@ public:
///
/// TODO: Roll this into the implicit constructor once we're sure that no one
/// is relying on the std::reverse_iterator off-by-one semantics.
ilist_iterator<NodeTy, !IsReverse> getReverse() const {
ilist_iterator<OptionsT, !IsReverse, IsConst> getReverse() const {
if (NodePtr)
return ilist_iterator<NodeTy, !IsReverse>(*NodePtr);
return ilist_iterator<NodeTy, !IsReverse>();
return ilist_iterator<OptionsT, !IsReverse, IsConst>(*NodePtr);
return ilist_iterator<OptionsT, !IsReverse, IsConst>();
}
/// Const-cast.
ilist_iterator<OptionsT, IsReverse, false> getNonConst() const {
if (NodePtr)
return ilist_iterator<OptionsT, IsReverse, false>(
const_cast<typename ilist_iterator<OptionsT, IsReverse,
false>::node_reference>(*NodePtr));
return ilist_iterator<OptionsT, IsReverse, false>();
}
void reset(pointer NP) { NodePtr = NP; }
@ -121,11 +140,11 @@ public:
// Increment and decrement operators...
ilist_iterator &operator--() {
ilist_detail::IteratorHelper<IsReverse>::decrement(NodePtr);
NodePtr = IsReverse ? NodePtr->getNext() : NodePtr->getPrev();
return *this;
}
ilist_iterator &operator++() {
ilist_detail::IteratorHelper<IsReverse>::increment(NodePtr);
NodePtr = IsReverse ? NodePtr->getPrev() : NodePtr->getNext();
return *this;
}
ilist_iterator operator--(int) {
@ -149,20 +168,16 @@ template <typename From> struct simplify_type;
/// used by the dyn_cast, cast, isa mechanisms...
///
/// FIXME: remove this, since there is no implicit conversion to NodeTy.
template <typename NodeTy> struct simplify_type<ilist_iterator<NodeTy>> {
typedef NodeTy *SimpleType;
template <class OptionsT, bool IsConst>
struct simplify_type<ilist_iterator<OptionsT, false, IsConst>> {
typedef ilist_iterator<OptionsT, false, IsConst> iterator;
typedef typename iterator::pointer SimpleType;
static SimpleType getSimplifiedValue(ilist_iterator<NodeTy> &Node) {
return &*Node;
}
};
template <typename NodeTy> struct simplify_type<const ilist_iterator<NodeTy>> {
typedef /*const*/ NodeTy *SimpleType;
static SimpleType getSimplifiedValue(const ilist_iterator<NodeTy> &Node) {
return &*Node;
}
static SimpleType getSimplifiedValue(const iterator &Node) { return &*Node; }
};
template <class OptionsT, bool IsConst>
struct simplify_type<const ilist_iterator<OptionsT, false, IsConst>>
: simplify_type<ilist_iterator<OptionsT, false, IsConst>> {};
} // end namespace llvm

View File

@ -16,6 +16,7 @@
#define LLVM_ADT_ILIST_NODE_H
#include "llvm/ADT/ilist_node_base.h"
#include "llvm/ADT/ilist_node_options.h"
namespace llvm {
@ -26,46 +27,121 @@ struct NodeAccess;
template<typename NodeTy>
struct ilist_traits;
template <typename NodeTy, bool IsReverse = false> class ilist_iterator;
template <typename NodeTy> class ilist_sentinel;
template <class OptionsT, bool IsReverse, bool IsConst> class ilist_iterator;
template <class OptionsT> class ilist_sentinel;
/// Templated wrapper class.
template <typename NodeTy> class ilist_node : ilist_node_base {
friend class ilist_base;
/// Implementation for an ilist node.
///
/// Templated on an appropriate \a ilist_detail::node_options, usually computed
/// by \a ilist_detail::compute_node_options.
///
/// This is a wrapper around \a ilist_node_base whose main purpose is to
/// provide type safety: you can't insert nodes of \a ilist_node_impl into the
/// wrong \a simple_ilist or \a iplist.
template <class OptionsT> class ilist_node_impl : OptionsT::node_base_type {
typedef typename OptionsT::value_type value_type;
typedef typename OptionsT::node_base_type node_base_type;
typedef typename OptionsT::list_base_type list_base_type;
friend typename OptionsT::list_base_type;
friend struct ilist_detail::NodeAccess;
friend struct ilist_traits<NodeTy>;
friend class ilist_iterator<NodeTy, false>;
friend class ilist_iterator<NodeTy, true>;
friend class ilist_sentinel<NodeTy>;
friend class ilist_sentinel<OptionsT>;
friend class ilist_iterator<OptionsT, false, false>;
friend class ilist_iterator<OptionsT, false, true>;
friend class ilist_iterator<OptionsT, true, false>;
friend class ilist_iterator<OptionsT, true, true>;
protected:
ilist_node() = default;
ilist_node_impl() = default;
typedef ilist_iterator<OptionsT, false, false> self_iterator;
typedef ilist_iterator<OptionsT, false, true> const_self_iterator;
typedef ilist_iterator<OptionsT, true, false> reverse_self_iterator;
typedef ilist_iterator<OptionsT, true, true> const_reverse_self_iterator;
private:
ilist_node *getPrev() {
return static_cast<ilist_node *>(ilist_node_base::getPrev());
ilist_node_impl *getPrev() {
return static_cast<ilist_node_impl *>(node_base_type::getPrev());
}
ilist_node *getNext() {
return static_cast<ilist_node *>(ilist_node_base::getNext());
ilist_node_impl *getNext() {
return static_cast<ilist_node_impl *>(node_base_type::getNext());
}
const ilist_node *getPrev() const {
return static_cast<ilist_node *>(ilist_node_base::getPrev());
const ilist_node_impl *getPrev() const {
return static_cast<ilist_node_impl *>(node_base_type::getPrev());
}
const ilist_node *getNext() const {
return static_cast<ilist_node *>(ilist_node_base::getNext());
const ilist_node_impl *getNext() const {
return static_cast<ilist_node_impl *>(node_base_type::getNext());
}
void setPrev(ilist_node *N) { ilist_node_base::setPrev(N); }
void setNext(ilist_node *N) { ilist_node_base::setNext(N); }
void setPrev(ilist_node_impl *N) { node_base_type::setPrev(N); }
void setNext(ilist_node_impl *N) { node_base_type::setNext(N); }
public:
ilist_iterator<NodeTy> getIterator() { return ilist_iterator<NodeTy>(*this); }
ilist_iterator<const NodeTy> getIterator() const {
return ilist_iterator<const NodeTy>(*this);
}
self_iterator getIterator() { return self_iterator(*this); }
const_self_iterator getIterator() const { return const_self_iterator(*this); }
using ilist_node_base::isKnownSentinel;
// Under-approximation, but always available for assertions.
using node_base_type::isKnownSentinel;
/// Check whether this is the sentinel node.
///
/// This requires sentinel tracking to be explicitly enabled. Use the
/// ilist_sentinel_tracking<true> option to get this API.
bool isSentinel() const {
static_assert(OptionsT::is_sentinel_tracking_explicit,
"Use ilist_sentinel_tracking<true> to enable isSentinel()");
return node_base_type::isSentinel();
}
};
/// An intrusive list node.
///
/// A base class to enable membership in intrusive lists, including \a
/// simple_ilist, \a iplist, and \a ilist. The first template parameter is the
/// \a value_type for the list.
///
/// An ilist node can be configured with compile-time options to change
/// behaviour and/or add API.
///
/// By default, an \a ilist_node knows whether it is the list sentinel (an
/// instance of \a ilist_sentinel) if and only if
/// LLVM_ENABLE_ABI_BREAKING_CHECKS. The function \a isKnownSentinel() always
/// returns \c false tracking is off. Sentinel tracking steals a bit from the
/// "prev" link, which adds a mask operation when decrementing an iterator, but
/// enables bug-finding assertions in \a ilist_iterator.
///
/// To turn sentinel tracking on all the time, pass in the
/// ilist_sentinel_tracking<true> template parameter. This also enables the \a
/// isSentinel() function. The same option must be passed to the intrusive
/// list. (ilist_sentinel_tracking<false> turns sentinel tracking off all the
/// time.)
///
/// A type can inherit from ilist_node multiple times by passing in different
/// \a ilist_tag options. This allows a single instance to be inserted into
/// multiple lists simultaneously, where each list is given the same tag.
///
/// \example
/// struct A {};
/// struct B {};
/// struct N : ilist_node<N, ilist_tag<A>>, ilist_node<N, ilist_tag<B>> {};
///
/// void foo() {
/// simple_ilist<N, ilist_tag<A>> ListA;
/// simple_ilist<N, ilist_tag<B>> ListB;
/// N N1;
/// ListA.push_back(N1);
/// ListB.push_back(N1);
/// }
/// \endexample
///
/// See \a is_valid_option for steps on adding a new option.
template <class T, class... Options>
class ilist_node
: public ilist_node_impl<
typename ilist_detail::compute_node_options<T, Options...>::type> {
static_assert(ilist_detail::check_options<Options...>::value,
"Unrecognized node option!");
};
namespace ilist_detail {
@ -77,58 +153,71 @@ namespace ilist_detail {
/// Using this class outside of the ilist implementation is unsupported.
struct NodeAccess {
protected:
template <typename T> static ilist_node<T> *getNodePtr(T *N) { return N; }
template <typename T> static const ilist_node<T> *getNodePtr(const T *N) {
template <class OptionsT>
static ilist_node_impl<OptionsT> *getNodePtr(typename OptionsT::pointer N) {
return N;
}
template <typename T> static T *getValuePtr(ilist_node<T> *N) {
return static_cast<T *>(N);
template <class OptionsT>
static const ilist_node_impl<OptionsT> *
getNodePtr(typename OptionsT::const_pointer N) {
return N;
}
template <typename T> static const T *getValuePtr(const ilist_node<T> *N) {
return static_cast<const T *>(N);
template <class OptionsT>
static typename OptionsT::pointer getValuePtr(ilist_node_impl<OptionsT> *N) {
return static_cast<typename OptionsT::pointer>(N);
}
template <class OptionsT>
static typename OptionsT::const_pointer
getValuePtr(const ilist_node_impl<OptionsT> *N) {
return static_cast<typename OptionsT::const_pointer>(N);
}
template <typename T> static ilist_node<T> *getPrev(ilist_node<T> &N) {
template <class OptionsT>
static ilist_node_impl<OptionsT> *getPrev(ilist_node_impl<OptionsT> &N) {
return N.getPrev();
}
template <typename T> static ilist_node<T> *getNext(ilist_node<T> &N) {
template <class OptionsT>
static ilist_node_impl<OptionsT> *getNext(ilist_node_impl<OptionsT> &N) {
return N.getNext();
}
template <typename T>
static const ilist_node<T> *getPrev(const ilist_node<T> &N) {
template <class OptionsT>
static const ilist_node_impl<OptionsT> *
getPrev(const ilist_node_impl<OptionsT> &N) {
return N.getPrev();
}
template <typename T>
static const ilist_node<T> *getNext(const ilist_node<T> &N) {
template <class OptionsT>
static const ilist_node_impl<OptionsT> *
getNext(const ilist_node_impl<OptionsT> &N) {
return N.getNext();
}
};
template <class T> struct SpecificNodeAccess : NodeAccess {
template <class OptionsT> struct SpecificNodeAccess : NodeAccess {
protected:
typedef T *pointer;
typedef const T *const_pointer;
typedef ilist_node<T> node_type;
typedef typename OptionsT::pointer pointer;
typedef typename OptionsT::const_pointer const_pointer;
typedef ilist_node_impl<OptionsT> node_type;
static node_type *getNodePtr(pointer N) {
return NodeAccess::getNodePtr<T>(N);
return NodeAccess::getNodePtr<OptionsT>(N);
}
static const ilist_node<T> *getNodePtr(const_pointer N) {
return NodeAccess::getNodePtr<T>(N);
static const node_type *getNodePtr(const_pointer N) {
return NodeAccess::getNodePtr<OptionsT>(N);
}
static pointer getValuePtr(node_type *N) {
return NodeAccess::getValuePtr<T>(N);
return NodeAccess::getValuePtr<OptionsT>(N);
}
static const_pointer getValuePtr(const node_type *N) {
return NodeAccess::getValuePtr<T>(N);
return NodeAccess::getValuePtr<OptionsT>(N);
}
};
} // end namespace ilist_detail
template <typename NodeTy> class ilist_sentinel : public ilist_node<NodeTy> {
template <class OptionsT>
class ilist_sentinel : public ilist_node_impl<OptionsT> {
public:
ilist_sentinel() {
ilist_node_base::initializeSentinel();
this->initializeSentinel();
reset();
}
@ -144,8 +233,8 @@ public:
///
/// Requires \c NodeTy to have \a getParent() to find the parent node, and the
/// \c ParentTy to have \a getSublistAccess() to get a reference to the list.
template <typename NodeTy, typename ParentTy>
class ilist_node_with_parent : public ilist_node<NodeTy> {
template <typename NodeTy, typename ParentTy, class... Options>
class ilist_node_with_parent : public ilist_node<NodeTy, Options...> {
protected:
ilist_node_with_parent() = default;

View File

@ -15,31 +15,37 @@
namespace llvm {
/// Base class for ilist nodes.
class ilist_node_base {
#ifdef LLVM_ENABLE_ABI_BREAKING_CHECKS
PointerIntPair<ilist_node_base *, 1> PrevAndSentinel;
#else
///
/// Optionally tracks whether this node is the sentinel.
template <bool EnableSentinelTracking> class ilist_node_base;
template <> class ilist_node_base<false> {
ilist_node_base *Prev = nullptr;
#endif
ilist_node_base *Next = nullptr;
public:
#ifdef LLVM_ENABLE_ABI_BREAKING_CHECKS
void setPrev(ilist_node_base *Prev) { PrevAndSentinel.setPointer(Prev); }
ilist_node_base *getPrev() const { return PrevAndSentinel.getPointer(); }
bool isKnownSentinel() const { return PrevAndSentinel.getInt(); }
void initializeSentinel() { PrevAndSentinel.setInt(true); }
#else
void setPrev(ilist_node_base *Prev) { this->Prev = Prev; }
void setNext(ilist_node_base *Next) { this->Next = Next; }
ilist_node_base *getPrev() const { return Prev; }
ilist_node_base *getNext() const { return Next; }
bool isKnownSentinel() const { return false; }
void initializeSentinel() {}
#endif
};
template <> class ilist_node_base<true> {
PointerIntPair<ilist_node_base *, 1> PrevAndSentinel;
ilist_node_base *Next = nullptr;
public:
void setPrev(ilist_node_base *Prev) { PrevAndSentinel.setPointer(Prev); }
void setNext(ilist_node_base *Next) { this->Next = Next; }
ilist_node_base *getPrev() const { return PrevAndSentinel.getPointer(); }
ilist_node_base *getNext() const { return Next; }
bool isSentinel() const { return PrevAndSentinel.getInt(); }
bool isKnownSentinel() const { return isSentinel(); }
void initializeSentinel() { PrevAndSentinel.setInt(true); }
};
} // end namespace llvm

View File

@ -0,0 +1,128 @@
//===- llvm/ADT/ilist_node_options.h - ilist_node Options -------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_ADT_ILIST_NODE_OPTIONS_H
#define LLVM_ADT_ILIST_NODE_OPTIONS_H
namespace llvm {
template <bool EnableSentinelTracking> class ilist_node_base;
template <bool EnableSentinelTracking> class ilist_base;
/// Option to choose whether to track sentinels.
///
/// This option affects the ABI for the nodes. When not specified explicitly,
/// the ABI depends on LLVM_ENABLE_ABI_BREAKING_CHECKS. Specify explicitly to
/// enable \a ilist_node::isSentinel().
template <bool EnableSentinelTracking> struct ilist_sentinel_tracking {};
/// Option to specify a tag for the node type.
///
/// This option allows a single value type to be inserted in multiple lists
/// simultaneously. See \a ilist_node for usage examples.
template <class Tag> struct ilist_tag {};
namespace ilist_detail {
/// Helper trait for recording whether an option is specified explicitly.
template <bool IsExplicit> struct explicitness {
static const bool is_explicit = IsExplicit;
};
typedef explicitness<true> is_explicit;
typedef explicitness<false> is_implicit;
/// Check whether an option is valid.
///
/// The steps for adding and enabling a new ilist option include:
/// \li define the option, ilist_foo<Bar>, above;
/// \li add new parameters for Bar to \a ilist_detail::node_options;
/// \li add an extraction meta-function, ilist_detail::extract_foo;
/// \li call extract_foo from \a ilist_detail::compute_node_options and pass it
/// into \a ilist_detail::node_options; and
/// \li specialize \c is_valid_option<ilist_foo<Bar>> to inherit from \c
/// std::true_type to get static assertions passing in \a simple_ilist and \a
/// ilist_node.
template <class Option> struct is_valid_option : std::false_type {};
/// Extract sentinel tracking option.
///
/// Look through \p Options for the \a ilist_sentinel_tracking option, with the
/// default depending on LLVM_ENABLE_ABI_BREAKING_CHECKS.
template <class... Options> struct extract_sentinel_tracking;
template <bool EnableSentinelTracking, class... Options>
struct extract_sentinel_tracking<
ilist_sentinel_tracking<EnableSentinelTracking>, Options...>
: std::integral_constant<bool, EnableSentinelTracking>, is_explicit {};
template <class Option1, class... Options>
struct extract_sentinel_tracking<Option1, Options...>
: extract_sentinel_tracking<Options...> {};
#ifdef LLVM_ENABLE_ABI_BREAKING_CHECKS
template <> struct extract_sentinel_tracking<> : std::true_type, is_implicit {};
#else
template <>
struct extract_sentinel_tracking<> : std::false_type, is_implicit {};
#endif
template <bool EnableSentinelTracking>
struct is_valid_option<ilist_sentinel_tracking<EnableSentinelTracking>>
: std::true_type {};
/// Extract custom tag option.
///
/// Look through \p Options for the \a ilist_tag option, pulling out the
/// custom tag type, using void as a default.
template <class... Options> struct extract_tag;
template <class Tag, class... Options>
struct extract_tag<ilist_tag<Tag>, Options...> {
typedef Tag type;
};
template <class Option1, class... Options>
struct extract_tag<Option1, Options...> : extract_tag<Options...> {};
template <> struct extract_tag<> { typedef void type; };
template <class Tag> struct is_valid_option<ilist_tag<Tag>> : std::true_type {};
/// Check whether options are valid.
///
/// The conjunction of \a is_valid_option on each individual option.
template <class... Options> struct check_options;
template <> struct check_options<> : std::true_type {};
template <class Option1, class... Options>
struct check_options<Option1, Options...>
: std::integral_constant<bool, is_valid_option<Option1>::value &&
check_options<Options...>::value> {};
/// Traits for options for \a ilist_node.
///
/// This is usually computed via \a compute_node_options.
template <class T, bool EnableSentinelTracking, bool IsSentinelTrackingExplicit,
class TagT>
struct node_options {
typedef T value_type;
typedef T *pointer;
typedef T &reference;
typedef const T *const_pointer;
typedef const T &const_reference;
static const bool enable_sentinel_tracking = EnableSentinelTracking;
static const bool is_sentinel_tracking_explicit = IsSentinelTrackingExplicit;
typedef TagT tag;
typedef ilist_node_base<enable_sentinel_tracking> node_base_type;
typedef ilist_base<enable_sentinel_tracking> list_base_type;
};
template <class T, class... Options> struct compute_node_options {
typedef node_options<T, extract_sentinel_tracking<Options...>::value,
extract_sentinel_tracking<Options...>::is_explicit,
typename extract_tag<Options...>::type>
type;
};
} // end namespace ilist_detail
} // end namespace llvm
#endif // LLVM_ADT_ILIST_NODE_OPTIONS_H

View File

@ -46,23 +46,54 @@ namespace llvm {
/// eraseAndDispose(), and \a clearAndDispose(). These have different names
/// because the extra semantic is otherwise non-obvious. They are equivalent
/// to calling \a std::for_each() on the range to be discarded.
template <typename T>
class simple_ilist : ilist_base, ilist_detail::SpecificNodeAccess<T> {
typedef ilist_base list_base_type;
ilist_sentinel<T> Sentinel;
///
/// The currently available \p Options customize the nodes in the list. The
/// same options must be specified in the \a ilist_node instantation for
/// compatibility (although the order is irrelevant).
/// \li Use \a ilist_tag to designate which ilist_node for a given \p T this
/// list should use. This is useful if a type \p T is part of multiple,
/// independent lists simultaneously.
/// \li Use \a ilist_sentinel_tracking to always (or never) track whether a
/// node is a sentinel. Specifying \c true enables the \a
/// ilist_node::isSentinel() API. Unlike \a ilist_node::isKnownSentinel(),
/// which is only appropriate for assertions, \a ilist_node::isSentinel() is
/// appropriate for real logic.
///
/// Here are examples of \p Options usage:
/// \li \c simple_ilist<T> gives the defaults. \li \c
/// simple_ilist<T,ilist_sentinel_tracking<true>> enables the \a
/// ilist_node::isSentinel() API.
/// \li \c simple_ilist<T,ilist_tag<A>,ilist_sentinel_tracking<false>>
/// specifies a tag of A and that tracking should be off (even when
/// LLVM_ENABLE_ABI_BREAKING_CHECKS are enabled).
/// \li \c simple_ilist<T,ilist_sentinel_tracking<false>,ilist_tag<A>> is
/// equivalent to the last.
///
/// See \a is_valid_option for steps on adding a new option.
template <typename T, class... Options>
class simple_ilist
: ilist_detail::compute_node_options<T, Options...>::type::list_base_type,
ilist_detail::SpecificNodeAccess<
typename ilist_detail::compute_node_options<T, Options...>::type> {
static_assert(ilist_detail::check_options<Options...>::value,
"Unrecognized node option!");
typedef
typename ilist_detail::compute_node_options<T, Options...>::type OptionsT;
typedef typename OptionsT::list_base_type list_base_type;
ilist_sentinel<OptionsT> Sentinel;
public:
typedef T value_type;
typedef T *pointer;
typedef T &reference;
typedef const T *const_pointer;
typedef const T &const_reference;
typedef ilist_iterator<T> iterator;
typedef ilist_iterator<const T> const_iterator;
typedef typename OptionsT::value_type value_type;
typedef typename OptionsT::pointer pointer;
typedef typename OptionsT::reference reference;
typedef typename OptionsT::const_pointer const_pointer;
typedef typename OptionsT::const_reference const_reference;
typedef ilist_iterator<OptionsT, false, false> iterator;
typedef ilist_iterator<OptionsT, false, true> const_iterator;
typedef ilist_iterator<OptionsT, true, false> reverse_iterator;
typedef ilist_iterator<OptionsT, true, true> const_reverse_iterator;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef ilist_iterator<const T, true> const_reverse_iterator;
typedef ilist_iterator<T, true> reverse_iterator;
simple_ilist() = default;
~simple_ilist() = default;
@ -222,9 +253,9 @@ public:
///@}
};
template <class T>
template <class T, class... Options>
template <class Compare>
void simple_ilist<T>::merge(simple_ilist<T> &RHS, Compare comp) {
void simple_ilist<T, Options...>::merge(simple_ilist &RHS, Compare comp) {
if (this == &RHS || RHS.empty())
return;
iterator LI = begin(), LE = end();
@ -244,9 +275,9 @@ void simple_ilist<T>::merge(simple_ilist<T> &RHS, Compare comp) {
splice(LE, RHS, RI, RE);
}
template <class T>
template <class T, class... Options>
template <class Compare>
void simple_ilist<T>::sort(Compare comp) {
void simple_ilist<T, Options...>::sort(Compare comp) {
// Vacuously sorted.
if (empty() || std::next(begin()) == end())
return;
@ -257,7 +288,7 @@ void simple_ilist<T>::sort(Compare comp) {
++Center;
++End;
}
simple_ilist<T> RHS;
simple_ilist RHS;
RHS.splice(RHS.end(), *this, Center, end());
// Sort the sublists and merge back together.

View File

@ -19,10 +19,22 @@
namespace llvm {
template <class T> struct MachineInstrBundleIteratorTraits {
typedef simple_ilist<T> list_type;
typedef typename list_type::iterator instr_iterator;
typedef typename list_type::iterator nonconst_instr_iterator;
};
template <class T> struct MachineInstrBundleIteratorTraits<const T> {
typedef simple_ilist<T> list_type;
typedef typename list_type::const_iterator instr_iterator;
typedef typename list_type::iterator nonconst_instr_iterator;
};
/// MachineBasicBlock iterator that automatically skips over MIs that are
/// inside bundles (i.e. walk top level MIs only).
template <typename Ty> class MachineInstrBundleIterator {
typedef ilist_iterator<Ty> instr_iterator;
typedef typename MachineInstrBundleIteratorTraits<Ty>::instr_iterator
instr_iterator;
instr_iterator MII;
public:
@ -37,8 +49,8 @@ public:
private:
typedef typename std::remove_const<value_type>::type nonconst_value_type;
typedef ilist_node<nonconst_value_type> node_type;
typedef ilist_iterator<nonconst_value_type> nonconst_instr_iterator;
typedef typename MachineInstrBundleIteratorTraits<Ty>::nonconst_instr_iterator
nonconst_instr_iterator;
typedef MachineInstrBundleIterator<nonconst_value_type> nonconst_iterator;
public:
@ -136,11 +148,7 @@ public:
instr_iterator getInstrIterator() const { return MII; }
nonconst_iterator getNonConstIterator() const {
if (auto *N = const_cast<node_type *>(MII.getNodePtr()))
return nonconst_iterator(nonconst_instr_iterator(*N));
return nonconst_iterator();
}
nonconst_iterator getNonConstIterator() const { return MII.getNonConst(); }
};
} // end namespace llvm

View File

@ -20,6 +20,7 @@ set(ADTSources
IListBaseTest.cpp
IListIteratorTest.cpp
IListNodeBaseTest.cpp
IListNodeTest.cpp
IListSentinelTest.cpp
IListTest.cpp
ImmutableMapTest.cpp

View File

@ -14,11 +14,20 @@ using namespace llvm;
namespace {
typedef ilist_base list_base_type;
typedef ilist_node_base node_base_type;
// Test fixture.
template <typename T> class IListBaseTest : public ::testing::Test {};
// Test variants with the same test.
typedef ::testing::Types<ilist_base<false>, ilist_base<true>>
IListBaseTestTypes;
TYPED_TEST_CASE(IListBaseTest, IListBaseTestTypes);
TYPED_TEST(IListBaseTest, insertBeforeImpl) {
typedef TypeParam list_base_type;
typedef typename list_base_type::node_base_type node_base_type;
TEST(IListBaseTest, insertBeforeImpl) {
node_base_type S, A, B;
// [S] <-> [S]
S.setPrev(&S);
S.setNext(&S);
@ -40,7 +49,10 @@ TEST(IListBaseTest, insertBeforeImpl) {
EXPECT_EQ(&S, B.getNext());
}
TEST(IListBaseTest, removeImpl) {
TYPED_TEST(IListBaseTest, removeImpl) {
typedef TypeParam list_base_type;
typedef typename list_base_type::node_base_type node_base_type;
node_base_type S, A, B;
// [S] <-> A <-> B <-> [S]
@ -66,7 +78,10 @@ TEST(IListBaseTest, removeImpl) {
EXPECT_EQ(nullptr, B.getNext());
}
TEST(IListBaseTest, removeRangeImpl) {
TYPED_TEST(IListBaseTest, removeRangeImpl) {
typedef TypeParam list_base_type;
typedef typename list_base_type::node_base_type node_base_type;
node_base_type S, A, B, C, D;
// [S] <-> A <-> B <-> C <-> D <-> [S]
@ -89,7 +104,10 @@ TEST(IListBaseTest, removeRangeImpl) {
EXPECT_EQ(nullptr, C.getNext());
}
TEST(IListBaseTest, removeRangeImplAllButSentinel) {
TYPED_TEST(IListBaseTest, removeRangeImplAllButSentinel) {
typedef TypeParam list_base_type;
typedef typename list_base_type::node_base_type node_base_type;
node_base_type S, A, B;
// [S] <-> A <-> B <-> [S]
@ -106,7 +124,10 @@ TEST(IListBaseTest, removeRangeImplAllButSentinel) {
EXPECT_EQ(nullptr, B.getNext());
}
TEST(IListBaseTest, transferBeforeImpl) {
TYPED_TEST(IListBaseTest, transferBeforeImpl) {
typedef TypeParam list_base_type;
typedef typename list_base_type::node_base_type node_base_type;
node_base_type S1, S2, A, B, C, D, E;
// [S1] <-> A <-> B <-> C <-> [S1]

View File

@ -14,15 +14,24 @@ using namespace llvm;
namespace {
typedef ilist_node_base<false> RawNode;
typedef ilist_node_base<true> TrackingNode;
TEST(IListNodeBaseTest, DefaultConstructor) {
ilist_node_base A;
RawNode A;
EXPECT_EQ(nullptr, A.getPrev());
EXPECT_EQ(nullptr, A.getNext());
EXPECT_FALSE(A.isKnownSentinel());
TrackingNode TA;
EXPECT_EQ(nullptr, TA.getPrev());
EXPECT_EQ(nullptr, TA.getNext());
EXPECT_FALSE(TA.isKnownSentinel());
EXPECT_FALSE(TA.isSentinel());
}
TEST(IListNodeBaseTest, setPrevAndNext) {
ilist_node_base A, B, C;
RawNode A, B, C;
A.setPrev(&B);
EXPECT_EQ(&B, A.getPrev());
EXPECT_EQ(nullptr, A.getNext());
@ -38,23 +47,54 @@ TEST(IListNodeBaseTest, setPrevAndNext) {
EXPECT_EQ(nullptr, B.getNext());
EXPECT_EQ(nullptr, C.getPrev());
EXPECT_EQ(nullptr, C.getNext());
TrackingNode TA, TB, TC;
TA.setPrev(&TB);
EXPECT_EQ(&TB, TA.getPrev());
EXPECT_EQ(nullptr, TA.getNext());
EXPECT_EQ(nullptr, TB.getPrev());
EXPECT_EQ(nullptr, TB.getNext());
EXPECT_EQ(nullptr, TC.getPrev());
EXPECT_EQ(nullptr, TC.getNext());
TA.setNext(&TC);
EXPECT_EQ(&TB, TA.getPrev());
EXPECT_EQ(&TC, TA.getNext());
EXPECT_EQ(nullptr, TB.getPrev());
EXPECT_EQ(nullptr, TB.getNext());
EXPECT_EQ(nullptr, TC.getPrev());
EXPECT_EQ(nullptr, TC.getNext());
}
TEST(IListNodeBaseTest, isKnownSentinel) {
ilist_node_base A, B;
// Without sentinel tracking.
RawNode A, B;
EXPECT_FALSE(A.isKnownSentinel());
A.setPrev(&B);
A.setNext(&B);
EXPECT_EQ(&B, A.getPrev());
EXPECT_EQ(&B, A.getNext());
A.initializeSentinel();
#ifdef LLVM_ENABLE_ABI_BREAKING_CHECKS
EXPECT_TRUE(A.isKnownSentinel());
#else
EXPECT_FALSE(A.isKnownSentinel());
#endif
A.initializeSentinel();
EXPECT_FALSE(A.isKnownSentinel());
EXPECT_EQ(&B, A.getPrev());
EXPECT_EQ(&B, A.getNext());
// With sentinel tracking.
TrackingNode TA, TB;
EXPECT_FALSE(TA.isKnownSentinel());
EXPECT_FALSE(TA.isSentinel());
TA.setPrev(&TB);
TA.setNext(&TB);
EXPECT_EQ(&TB, TA.getPrev());
EXPECT_EQ(&TB, TA.getNext());
EXPECT_FALSE(TA.isKnownSentinel());
EXPECT_FALSE(TA.isSentinel());
TA.initializeSentinel();
EXPECT_TRUE(TA.isKnownSentinel());
EXPECT_TRUE(TA.isSentinel());
EXPECT_EQ(&TB, TA.getPrev());
EXPECT_EQ(&TB, TA.getNext());
}
} // end namespace

View File

@ -0,0 +1,70 @@
//===- unittests/ADT/IListNodeTest.cpp - ilist_node unit tests ------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "llvm/ADT/ilist_node.h"
#include "gtest/gtest.h"
#include <type_traits>
using namespace llvm;
using namespace llvm::ilist_detail;
namespace {
struct Node;
struct TagA {};
struct TagB {};
TEST(IListNodeTest, Options) {
static_assert(
std::is_same<compute_node_options<Node>::type,
compute_node_options<Node, ilist_tag<void>>::type>::value,
"default tag is void");
static_assert(
!std::is_same<compute_node_options<Node, ilist_tag<TagA>>::type,
compute_node_options<Node, ilist_tag<void>>::type>::value,
"default tag is void, different from TagA");
static_assert(
!std::is_same<compute_node_options<Node, ilist_tag<TagA>>::type,
compute_node_options<Node, ilist_tag<TagB>>::type>::value,
"TagA is not TagB");
static_assert(
std::is_same<
compute_node_options<Node, ilist_sentinel_tracking<false>>::type,
compute_node_options<Node, ilist_sentinel_tracking<false>,
ilist_tag<void>>::type>::value,
"default tag is void, even with sentinel tracking off");
static_assert(
std::is_same<
compute_node_options<Node, ilist_sentinel_tracking<false>>::type,
compute_node_options<Node, ilist_tag<void>,
ilist_sentinel_tracking<false>>::type>::value,
"order shouldn't matter");
static_assert(
std::is_same<
compute_node_options<Node, ilist_sentinel_tracking<true>>::type,
compute_node_options<Node, ilist_sentinel_tracking<true>,
ilist_tag<void>>::type>::value,
"default tag is void, even with sentinel tracking on");
static_assert(
std::is_same<
compute_node_options<Node, ilist_sentinel_tracking<true>>::type,
compute_node_options<Node, ilist_tag<void>,
ilist_sentinel_tracking<true>>::type>::value,
"order shouldn't matter");
static_assert(
std::is_same<
compute_node_options<Node, ilist_sentinel_tracking<true>,
ilist_tag<TagA>>::type,
compute_node_options<Node, ilist_tag<TagA>,
ilist_sentinel_tracking<true>>::type>::value,
"order shouldn't matter with real tags");
}
} // end namespace

View File

@ -7,14 +7,26 @@
//
//===----------------------------------------------------------------------===//
#include "llvm/ADT/ilist.h"
#include "llvm/ADT/ilist_node.h"
#include "gtest/gtest.h"
using namespace llvm;
namespace {
template <class T, class... Options> struct PickSentinel {
typedef ilist_sentinel<
typename ilist_detail::compute_node_options<T, Options...>::type>
type;
};
class Node : public ilist_node<Node> {};
class TrackingNode : public ilist_node<Node, ilist_sentinel_tracking<true>> {};
typedef PickSentinel<Node>::type Sentinel;
typedef PickSentinel<Node, ilist_sentinel_tracking<true>>::type
TrackingSentinel;
typedef PickSentinel<Node, ilist_sentinel_tracking<false>>::type
NoTrackingSentinel;
struct LocalAccess : ilist_detail::NodeAccess {
using NodeAccess::getPrev;
@ -22,7 +34,7 @@ struct LocalAccess : ilist_detail::NodeAccess {
};
TEST(IListSentinelTest, DefaultConstructor) {
ilist_sentinel<Node> S;
Sentinel S;
EXPECT_EQ(&S, LocalAccess::getPrev(S));
EXPECT_EQ(&S, LocalAccess::getNext(S));
#ifdef LLVM_ENABLE_ABI_BREAKING_CHECKS
@ -30,6 +42,12 @@ TEST(IListSentinelTest, DefaultConstructor) {
#else
EXPECT_FALSE(S.isKnownSentinel());
#endif
TrackingSentinel TS;
NoTrackingSentinel NTS;
EXPECT_TRUE(TS.isSentinel());
EXPECT_TRUE(TS.isKnownSentinel());
EXPECT_FALSE(NTS.isKnownSentinel());
}
TEST(IListSentinelTest, NormalNodeIsNotKnownSentinel) {
@ -37,6 +55,9 @@ TEST(IListSentinelTest, NormalNodeIsNotKnownSentinel) {
EXPECT_EQ(nullptr, LocalAccess::getPrev(N));
EXPECT_EQ(nullptr, LocalAccess::getNext(N));
EXPECT_FALSE(N.isKnownSentinel());
TrackingNode TN;
EXPECT_FALSE(TN.isSentinel());
}
} // end namespace

View File

@ -583,4 +583,55 @@ TEST(SimpleIListTest, sortEmpty) {
L.sort();
}
struct Tag1 {};
struct Tag2 {};
struct DoubleNode : ilist_node<DoubleNode, ilist_tag<Tag1>>,
ilist_node<DoubleNode, ilist_tag<Tag2>> {
typedef ilist_node<DoubleNode, ilist_tag<Tag1>> Node1Type;
typedef ilist_node<DoubleNode, ilist_tag<Tag2>> Node2Type;
Node1Type::self_iterator getIterator1() { return Node1Type::getIterator(); }
Node2Type::self_iterator getIterator2() { return Node2Type::getIterator(); }
Node1Type::const_self_iterator getIterator1() const {
return Node1Type::getIterator();
}
Node2Type::const_self_iterator getIterator2() const {
return Node2Type::getIterator();
}
};
typedef simple_ilist<DoubleNode, ilist_tag<Tag1>> TaggedList1Type;
typedef simple_ilist<DoubleNode, ilist_tag<Tag2>> TaggedList2Type;
TEST(SimpleIListTest, TaggedLists) {
TaggedList1Type L1;
TaggedList2Type L2;
// Build the two lists, sharing a couple of nodes.
DoubleNode Ns[10];
int Order1[] = {0, 1, 2, 3, 4, 7, 9};
int Order2[] = {2, 5, 6, 7, 8, 4, 9, 1};
for (int I : Order1)
L1.push_back(Ns[I]);
for (int I : Order2)
L2.push_back(Ns[I]);
// Check that each list is correct.
EXPECT_EQ(sizeof(Order1) / sizeof(int), L1.size());
auto I1 = L1.begin();
for (int I : Order1) {
EXPECT_EQ(Ns[I].getIterator1(), I1);
EXPECT_EQ(&Ns[I], &*I1++);
}
EXPECT_EQ(L1.end(), I1);
EXPECT_EQ(sizeof(Order2) / sizeof(int), L2.size());
auto I2 = L2.begin();
for (int I : Order2) {
EXPECT_EQ(Ns[I].getIterator2(), I2);
EXPECT_EQ(&Ns[I], &*I2++);
}
EXPECT_EQ(L2.end(), I2);
}
} // end namespace