Dev/annagrin/opt neg branch (#721)

* Added c++17 test configurations for clang5.0 and clang6.0

* Added optimization that removes a branch from span::operator[]

* minor beauty fix

* added a better message for the optimization, fixed signed/unsigned warning

* better check fir wrap-around possibility
This commit is contained in:
Anna Gringauze 2018-08-17 11:36:06 -07:00 committed by GitHub
parent 6241b3faa6
commit 5016ce4a4d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -46,8 +46,8 @@
#define constexpr /*constexpr*/ #define constexpr /*constexpr*/
#define GSL_USE_STATIC_CONSTEXPR_WORKAROUND #define GSL_USE_STATIC_CONSTEXPR_WORKAROUND
#endif // _MSC_VER < 1910 #endif // _MSC_VER < 1910
#endif // _MSC_VER #endif // _MSC_VER
// See if we have enough C++17 power to use a static constexpr data member // See if we have enough C++17 power to use a static constexpr data member
// without needing an out-of-line definition // without needing an out-of-line definition
@ -142,15 +142,13 @@ namespace details
constexpr span_iterator(const Span* span, typename Span::index_type idx) noexcept constexpr span_iterator(const Span* span, typename Span::index_type idx) noexcept
: span_(span), index_(idx) : span_(span), index_(idx)
{ {}
}
friend span_iterator<Span, true>; friend span_iterator<Span, true>;
template <bool B, std::enable_if_t<!B && IsConst>* = nullptr> template <bool B, std::enable_if_t<!B && IsConst>* = nullptr>
constexpr span_iterator(const span_iterator<Span, B>& other) noexcept constexpr span_iterator(const span_iterator<Span, B>& other) noexcept
: span_iterator(other.span_, other.index_) : span_iterator(other.span_, other.index_)
{ {}
}
GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute
constexpr reference operator*() const constexpr reference operator*() const
@ -332,8 +330,7 @@ namespace details
template <index_type Other> template <index_type Other>
explicit constexpr extent_type(extent_type<Other> ext) : size_(ext.size()) explicit constexpr extent_type(extent_type<Other> ext) : size_(ext.size())
{ {}
}
explicit constexpr extent_type(index_type size) : size_(size) { Expects(size >= 0); } explicit constexpr extent_type(index_type size) : size_(size) { Expects(size >= 0); }
@ -383,35 +380,30 @@ public:
// since "std::enable_if_t<Extent <= 0>" is ill-formed when Extent is greater than 0. // since "std::enable_if_t<Extent <= 0>" is ill-formed when Extent is greater than 0.
class = std::enable_if_t<(Dependent || Extent <= 0)>> class = std::enable_if_t<(Dependent || Extent <= 0)>>
constexpr span() noexcept : storage_(nullptr, details::extent_type<0>()) constexpr span() noexcept : storage_(nullptr, details::extent_type<0>())
{ {}
}
constexpr span(pointer ptr, index_type count) : storage_(ptr, count) {} constexpr span(pointer ptr, index_type count) : storage_(ptr, count) {}
constexpr span(pointer firstElem, pointer lastElem) constexpr span(pointer firstElem, pointer lastElem)
: storage_(firstElem, std::distance(firstElem, lastElem)) : storage_(firstElem, std::distance(firstElem, lastElem))
{ {}
}
template <std::size_t N> template <std::size_t N>
constexpr span(element_type (&arr)[N]) noexcept constexpr span(element_type (&arr)[N]) noexcept
: storage_(KnownNotNull{&arr[0]}, details::extent_type<N>()) : storage_(KnownNotNull{&arr[0]}, details::extent_type<N>())
{ {}
}
template <std::size_t N, class ArrayElementType = std::remove_const_t<element_type>> template <std::size_t N, class ArrayElementType = std::remove_const_t<element_type>>
// GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute // TODO: parser bug // GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute // TODO: parser bug
constexpr span(std::array<ArrayElementType, N>& arr) noexcept constexpr span(std::array<ArrayElementType, N>& arr) noexcept
: storage_(&arr[0], details::extent_type<N>()) : storage_(&arr[0], details::extent_type<N>())
{ {}
}
template <std::size_t N> template <std::size_t N>
// GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute // TODO: parser bug // GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute // TODO: parser bug
constexpr span(const std::array<std::remove_const_t<element_type>, N>& arr) noexcept constexpr span(const std::array<std::remove_const_t<element_type>, N>& arr) noexcept
: storage_(&arr[0], details::extent_type<N>()) : storage_(&arr[0], details::extent_type<N>())
{ {}
}
// NB: the SFINAE here uses .data() as a incomplete/imperfect proxy for the requirement // NB: the SFINAE here uses .data() as a incomplete/imperfect proxy for the requirement
// on Container to be a contiguous sequence container. // on Container to be a contiguous sequence container.
@ -422,8 +414,7 @@ public:
std::is_convertible<typename Container::pointer, std::is_convertible<typename Container::pointer,
decltype(std::declval<Container>().data())>::value>> decltype(std::declval<Container>().data())>::value>>
constexpr span(Container& cont) : span(cont.data(), narrow<index_type>(cont.size())) constexpr span(Container& cont) : span(cont.data(), narrow<index_type>(cont.size()))
{ {}
}
template <class Container, template <class Container,
class = std::enable_if_t< class = std::enable_if_t<
@ -432,8 +423,7 @@ public:
std::is_convertible<typename Container::pointer, std::is_convertible<typename Container::pointer,
decltype(std::declval<Container>().data())>::value>> decltype(std::declval<Container>().data())>::value>>
constexpr span(const Container& cont) : span(cont.data(), narrow<index_type>(cont.size())) constexpr span(const Container& cont) : span(cont.data(), narrow<index_type>(cont.size()))
{ {}
}
constexpr span(const span& other) noexcept = default; constexpr span(const span& other) noexcept = default;
@ -444,8 +434,7 @@ public:
details::is_allowed_element_type_conversion<OtherElementType, element_type>::value>> details::is_allowed_element_type_conversion<OtherElementType, element_type>::value>>
constexpr span(const span<OtherElementType, OtherExtent>& other) constexpr span(const span<OtherElementType, OtherExtent>& other)
: storage_(other.data(), details::extent_type<OtherExtent>(other.size())) : storage_(other.data(), details::extent_type<OtherExtent>(other.size()))
{ {}
}
~span() noexcept = default; ~span() noexcept = default;
constexpr span& operator=(const span& other) noexcept = default; constexpr span& operator=(const span& other) noexcept = default;
@ -506,7 +495,7 @@ public:
GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute
constexpr reference operator[](index_type idx) const constexpr reference operator[](index_type idx) const
{ {
Expects(idx >= 0 && idx < storage_.size()); Expects(CheckRange(idx, storage_.size()));
return data()[idx]; return data()[idx];
} }
@ -544,6 +533,29 @@ public:
#endif // _MSC_VER #endif // _MSC_VER
private: private:
static bool CheckRange(index_type idx, index_type size)
{
// Optimization:
//
// idx >= 0 && idx < size
// =>
// static_cast<size_t>(idx) < static_cast<size_t>(size)
//
// because size >=0 by span construction, and negative idx will
// wrap around to a value always greater than size when casted.
// check if we have enough space to wrap around
if (narrow_cast<unsigned long long>(std::numeric_limits<index_type>::max()) <
narrow_cast<unsigned long long>(std::numeric_limits<size_t>::max()))
{
return narrow_cast<size_t>(idx) < narrow_cast<size_t>(size);
}
else
{
return idx >= 0 && idx < size;
}
}
// Needed to remove unnecessary null check in subspans // Needed to remove unnecessary null check in subspans
struct KnownNotNull struct KnownNotNull
{ {