From 5edc917bec69e076a6fe0f490d888bc042c1fe79 Mon Sep 17 00:00:00 2001 From: serge-sans-paille Date: Wed, 12 Apr 2023 17:21:36 +0200 Subject: [PATCH] Introduce cstride_slice, constant stride slice It's very rare to have non-constant stride, and using a constant unlocks various optimization, including potential improvement for #2095 --- pythran/backend.py | 17 +- .../pythonic/include/types/numpy_gexpr.hpp | 73 +++-- pythran/pythonic/include/types/slice.hpp | 129 +++++++- pythran/pythonic/include/types/tuple.hpp | 11 + pythran/pythonic/types/numpy_gexpr.hpp | 10 +- pythran/pythonic/types/slice.hpp | 279 +++++++++++++++++- pythran/pythonic/types/tuple.hpp | 79 ++--- pythran/types/types.py | 14 +- 8 files changed, 531 insertions(+), 81 deletions(-) diff --git a/pythran/backend.py b/pythran/backend.py index 65cb3e185..6089a50c2 100644 --- a/pythran/backend.py +++ b/pythran/backend.py @@ -1088,12 +1088,19 @@ def visit_Slice(self, node): else 'pythonic::builtins::None') args.append(arg) - if node.step is None or (isnum(node.step) and node.step.value == 1): - if self.all_positive(node.lower) and self.all_positive(node.upper): - builder = "pythonic::types::fast_contiguous_slice({},{})" + nstep = node.step + if nstep is None or (isnum(nstep) and nstep.value > 0): + if nstep is None or nstep.value == 1: + if self.all_positive(node.lower) and self.all_positive(node.upper): + builder = "pythonic::types::fast_contiguous_slice({0},{1})" + else: + builder = "pythonic::types::contiguous_slice({0},{1})" + step = 1 else: - builder = "pythonic::types::contiguous_slice({},{})" - return builder.format(args[0], args[1]) + builder = "pythonic::types::cstride_slice<{2}>({0},{1})" + step = nstep.value + + return builder.format(args[0], args[1], step) else: return "pythonic::types::slice({},{},{})".format(*args) diff --git a/pythran/pythonic/include/types/numpy_gexpr.hpp b/pythran/pythonic/include/types/numpy_gexpr.hpp index 6dc7c6327..8c5f0b332 100644 --- a/pythran/pythonic/include/types/numpy_gexpr.hpp +++ b/pythran/pythonic/include/types/numpy_gexpr.hpp @@ -161,6 +161,11 @@ namespace types static constexpr size_t value = 0; }; + template + struct count_long> { + static constexpr size_t value = 0; + }; + template struct count_long { static constexpr size_t value = @@ -511,6 +516,13 @@ namespace types : gexpr_shape>, pshape, S...> { }; + template + struct gexpr_shape, + pshape, oTys...>, + cstride_normalized_slice, S...> + : gexpr_shape>, + pshape, S...> { + }; template struct gexpr_shape, pshape, oTys...>, @@ -546,11 +558,9 @@ namespace types static_assert( utils::all_of>::value...>::value, "all slices are normalized"); - static_assert( - utils::all_of<(std::is_same::value || - std::is_same::value || - std::is_same::value)...>::value, - "all slices are valid"); + static_assert(utils::all_of<(std::is_same::value || + is_normalized_slice::value)...>::value, + "all slices are valid"); static_assert(std::decay::type::value >= sizeof...(S), "slicing respects array shape"); @@ -572,6 +582,11 @@ namespace types static constexpr size_t value = std::remove_reference::type::value - count_long::value; + using last_arg_stride_t = + decltype(std::declval().template strides()); + using last_slice_t = + typename std::tuple_element>::type; + // It is not possible to vectorize everything. We only vectorize if the // last dimension is contiguous, which happens if // 1. Arg is an ndarray (this is too strict) @@ -580,18 +595,15 @@ namespace types static const bool is_vectorizable = std::remove_reference::type::is_vectorizable && (sizeof...(S) < std::remove_reference::type::value || - std::is_same>::type>::value); + std::is_same::value); static const bool is_flat = std::remove_reference::type::is_flat && value == 1 && - utils::all_of::value...>::value; + utils::all_of< + std::is_same::value...>::value; static const bool is_strided = std::remove_reference::type::is_strided || (((sizeof...(S) - count_long::value) == value) && - !std::is_same>::type>::value); + !std::is_same::value); using value_type = typename std::decay::get( @@ -614,7 +626,21 @@ namespace types shape_t _shape; dtype *buffer; - array _strides; + + template + static constexpr types::pshape> + last_stride(cstride_normalized_slice); + static constexpr types::pshape> + last_stride(contiguous_normalized_slice); + static constexpr types::array last_stride(...); + + sutils::concat_t, + typename std::conditional< + sizeof...(S) == std::decay::type::value, + decltype(last_stride(std::declval())), + types::array>::type> + _strides; // strides + template auto shape() const -> decltype(std::get(_shape)) { @@ -636,17 +662,11 @@ namespace types numpy_gexpr(numpy_gexpr const &other); template - typename std::enable_if< - std::is_same::value || - std::is_same::value, - void>::type + typename std::enable_if::value, void>::type init_shape(Slice const &s, utils::int_<1>, utils::int_); template - typename std::enable_if< - std::is_same::value || - std::is_same::value, - void>::type + typename std::enable_if::value, void>::type init_shape(Slice const &s, utils::int_, utils::int_); template @@ -877,9 +897,14 @@ namespace types explicit operator bool() const; - - dtype* data() { return buffer;} - const dtype* data() const { return buffer;} + dtype *data() + { + return buffer; + } + const dtype *data() const + { + return buffer; + } long flat_size() const; long size() const; ndarray copy() const diff --git a/pythran/pythonic/include/types/slice.hpp b/pythran/pythonic/include/types/slice.hpp index d9e0d357a..b987a39c2 100644 --- a/pythran/pythonic/include/types/slice.hpp +++ b/pythran/pythonic/include/types/slice.hpp @@ -49,18 +49,27 @@ namespace types }; struct slice; + template + struct cstride_slice; + template + struct cstride_normalized_slice; struct contiguous_slice; struct fast_contiguous_slice; struct contiguous_normalized_slice; struct normalized_slice { long lower, upper, step; - normalized_slice(); + normalized_slice() = default; normalized_slice(long lower, long upper, long step = 1); normalized_slice operator*(normalized_slice const &other) const; normalized_slice operator*(contiguous_normalized_slice const &other) const; + template + normalized_slice + operator*(cstride_normalized_slice const &other) const; normalized_slice operator*(slice const &other) const; + template + normalized_slice operator*(cstride_slice const &other) const; normalized_slice operator*(contiguous_slice const &other) const; normalized_slice operator*(fast_contiguous_slice const &other) const; @@ -77,6 +86,9 @@ namespace types slice operator*(slice const &other) const; + template + slice operator*(cstride_slice const &other) const; + slice operator*(contiguous_slice const &other) const; slice operator*(fast_contiguous_slice const &other) const; @@ -97,6 +109,79 @@ namespace types long get(long i) const; }; + template + struct cstride_normalized_slice { + long lower, upper; + static constexpr long step = stride; + cstride_normalized_slice(); + cstride_normalized_slice(long lower, long upper, long = 0); + + normalized_slice operator*(normalized_slice const &other) const; + cstride_normalized_slice + operator*(contiguous_normalized_slice const &other) const; + template + typename std::conditional<(stride < 256 && other_stride < 256), + cstride_normalized_slice, + normalized_slice>::type + operator*(cstride_normalized_slice const &other) const; + + normalized_slice operator*(slice const &other) const; + template + typename std::conditional<(stride < 256 && other_stride < 256), + cstride_normalized_slice, + normalized_slice>::type + operator*(cstride_slice const &other) const; + cstride_normalized_slice operator*(contiguous_slice const &other) const; + cstride_normalized_slice + operator*(fast_contiguous_slice const &other) const; + + long size() const; + inline long get(long i) const; + }; + + template + constexpr long cstride_normalized_slice::step; + + template + struct cstride_slice { + using normalized_type = cstride_normalized_slice; + + bound lower, upper; + static constexpr long step = stride; + + cstride_slice(none lower, none upper); + cstride_slice(); + + slice operator*(slice const &other) const; + + template + typename std::conditional<(stride < 256 && other_stride < 256), + cstride_slice, slice>::type + operator*(cstride_slice const &other) const; + + cstride_slice operator*(contiguous_slice const &other) const; + + cstride_slice operator*(fast_contiguous_slice const &other) const; + + /* + Normalize change a[:-1] to a[:len(a)-1] to have positif index. + It also check for value bigger than len(a) to fit the size of the + container + */ + cstride_normalized_slice normalize(long max_size) const; + + /* + * An assert is raised when we can't compute the size without more + * informations. + */ + long size() const; + + long get(long i) const; + }; + + template + constexpr long cstride_slice::step; + struct contiguous_normalized_slice { long lower, upper; static constexpr long step = 1; @@ -105,11 +190,17 @@ namespace types contiguous_normalized_slice operator*(contiguous_normalized_slice const &other) const; + template + cstride_normalized_slice + operator*(cstride_normalized_slice const &other) const; contiguous_normalized_slice operator*(contiguous_slice const &other) const; contiguous_normalized_slice operator*(fast_contiguous_slice const &other) const; normalized_slice operator*(normalized_slice const &other) const; normalized_slice operator*(slice const &other) const; + template + cstride_normalized_slice + operator*(cstride_slice const &other) const; long size() const; @@ -167,6 +258,11 @@ namespace types using type = normalized_slice; }; + template + struct normalized> { + using type = cstride_normalized_slice; + }; + template <> struct normalized { using type = contiguous_normalized_slice; @@ -189,6 +285,23 @@ namespace types template <> struct is_slice : std::true_type { }; + template + struct is_slice> : std::true_type { + }; + + template + struct is_normalized_slice : std::false_type { + }; + template <> + struct is_normalized_slice : std::true_type { + }; + template <> + struct is_normalized_slice : std::true_type { + }; + template + struct is_normalized_slice> + : std::true_type { + }; template using normalize_t = typename normalized::type; @@ -253,7 +366,7 @@ namespace types template typename std::enable_if::value, std::ostream &>::type operator<<(std::ostream &os, S const &s); -} +} // namespace types namespace builtins { template @@ -271,7 +384,7 @@ namespace builtins { return s.step; } -} +} // namespace builtins PYTHONIC_NS_END #ifdef ENABLE_PYTHON_MODULE @@ -295,6 +408,16 @@ struct to_python { static PyObject *convert(types::contiguous_normalized_slice const &n); }; +template +struct to_python> { + static PyObject *convert(types::cstride_slice const &n); +}; + +template +struct to_python> { + static PyObject *convert(types::cstride_normalized_slice const &n); +}; + template <> struct to_python { static PyObject *convert(types::slice const &n); diff --git a/pythran/pythonic/include/types/tuple.hpp b/pythran/pythonic/include/types/tuple.hpp index 72a7c42e6..66557c0f4 100644 --- a/pythran/pythonic/include/types/tuple.hpp +++ b/pythran/pythonic/include/types/tuple.hpp @@ -88,6 +88,8 @@ namespace types struct slice; struct contiguous_slice; + template + struct cstride_slice; /* helper to extract the tail of a tuple, && pop the head */ template @@ -276,6 +278,10 @@ namespace types template dynamic_tuple operator()(array const &b, slice const &s); + template + dynamic_tuple operator()(array const &b, + cstride_slice const &s); + template dynamic_tuple operator()(array const &b, contiguous_slice const &s); @@ -1209,6 +1215,11 @@ namespace sutils : concat, types::pshape> { }; + template + struct concat, types::array> { + using type = types::array; + }; + template using concat_t = typename concat::type; diff --git a/pythran/pythonic/types/numpy_gexpr.hpp b/pythran/pythonic/types/numpy_gexpr.hpp index 1f2f0c6f4..fb2fc57c0 100644 --- a/pythran/pythonic/types/numpy_gexpr.hpp +++ b/pythran/pythonic/types/numpy_gexpr.hpp @@ -274,10 +274,7 @@ namespace types template template - typename std::enable_if< - std::is_same::value || - std::is_same::value, - void>::type + typename std::enable_if::value, void>::type numpy_gexpr::init_shape(Slice const &s, utils::int_<1>, utils::int_) { @@ -290,10 +287,7 @@ namespace types template template - typename std::enable_if< - std::is_same::value || - std::is_same::value, - void>::type + typename std::enable_if::value, void>::type numpy_gexpr::init_shape(Slice const &s, utils::int_, utils::int_) { diff --git a/pythran/pythonic/types/slice.hpp b/pythran/pythonic/types/slice.hpp index d4b196fb3..05276f41a 100644 --- a/pythran/pythonic/types/slice.hpp +++ b/pythran/pythonic/types/slice.hpp @@ -30,9 +30,6 @@ namespace types } } // namespace details - inline normalized_slice::normalized_slice() - { - } inline normalized_slice::normalized_slice(long lower, long upper, long step) : lower(lower), upper(upper), step(step) { @@ -50,6 +47,13 @@ namespace types return {lower + step * other.lower, lower + step * other.upper, step * other.step}; } + template + inline normalized_slice normalized_slice::operator*( + cstride_normalized_slice const &other) const + { + return {lower + step * other.lower, lower + step * other.upper, + step * other.step}; + } inline normalized_slice normalized_slice::operator*(slice const &other) const { return (*this) * other.normalize(size()); @@ -59,6 +63,12 @@ namespace types { return (*this) * other.normalize(size()); } + template + inline normalized_slice + normalized_slice::operator*(cstride_slice const &other) const + { + return (*this) * other.normalize(size()); + } inline long normalized_slice::size() const { @@ -215,7 +225,238 @@ namespace types assert(!upper.is_none() && !lower.is_none()); return (long)lower + i * sstep; } + // + + template + inline cstride_normalized_slice::cstride_normalized_slice(long lower, + long upper, + long) + : lower(lower), upper(upper) + { + } + + template + inline normalized_slice cstride_normalized_slice::operator*( + normalized_slice const &other) const + { + return {lower + step * other.lower, lower + step * other.upper, + step * other.step}; + } + + template + template + inline + typename std::conditional<(stride < 256 && other_stride < 256), + cstride_normalized_slice, + normalized_slice>::type + cstride_normalized_slice::operator*( + cstride_normalized_slice const &other) const + { + return {lower + step * other.lower, lower + step * other.upper, + step * other.step}; + } + + template + inline cstride_normalized_slice + cstride_normalized_slice::operator*( + contiguous_normalized_slice const &other) const + { + return {lower + step * other.lower, lower + step * other.upper}; + } + + template + inline normalized_slice + cstride_normalized_slice::operator*(slice const &other) const + { + return (*this) * other.normalize(size()); + } + + template + template + inline + typename std::conditional<(stride < 256 && other_stride < 256), + cstride_normalized_slice, + normalized_slice>::type + cstride_normalized_slice::operator*( + cstride_slice const &other) const + { + return (*this) * other.normalize(size()); + } + + template + inline cstride_normalized_slice + cstride_normalized_slice::operator*( + contiguous_slice const &other) const + { + return (*this) * other.normalize(size()); + } + + template + inline long cstride_normalized_slice::size() const + { + return std::max(0L, details::roundup_divide(upper - lower, step)); + } + + template + inline long cstride_normalized_slice::get(long i) const + { + return lower + i * step; + } + + template + inline cstride_slice::cstride_slice(none lower, + none upper) + : lower(lower), upper(upper) + { + } + + template + inline cstride_slice::cstride_slice() + : lower(builtins::None), upper(builtins::None) + { + } + + template + inline slice cstride_slice::operator*(slice const &other) const + { + // We do not implement these because it requires to know the "end" + // value of the slice which is ! possible if it is ! "step == 1" slice + // TODO: We can skip these constraints if we know begin, end && step. + long ostep = (other.step.is_none()) ? 1 : (long)other.step; + bound new_lower; + if (other.lower.is_none() || (long)other.lower == 0) { + if (ostep > 0) + new_lower = lower; + else { + if (upper.is_none() || (long)upper == -1) + new_lower = none_type{}; + else + new_lower = (long)upper + 1; + } + } else { + none ref = ((long)other.lower > 0) ? lower : upper; + if (ref.is_none) { + new_lower = (long)other.lower * step; + } else + new_lower = (long)ref + (long)other.lower * step; + } + + long new_step = step * ostep; + + bound new_upper; + if (other.upper.is_none()) { + if (ostep > 0) + new_upper = upper; + else { + if (lower.is_none() || (long)lower == 0) + new_upper = none_type{}; + else + new_upper = (long)lower - 1; + } + } else { + none ref = ((long)other.upper > 0) ? lower : upper; + if (ref.is_none) { + new_upper = (long)other.upper * step; + } else + new_upper = (long)ref + (long)other.upper * step; + } + return {new_lower, new_upper, new_step}; + } + + template + template + inline typename std::conditional<(stride < 256 && other_stride < 256), + cstride_slice, + slice>::type + cstride_slice::operator*( + cstride_slice const &other) const + { + bound new_lower; + if (other.lower.is_none() || (long)other.lower == 0) { + new_lower = lower; + } else { + none ref = ((long)other.lower > 0) ? lower : upper; + if (ref.is_none) { + new_lower = (long)other.lower * step; + } else + new_lower = (long)ref + (long)other.lower * step; + } + + long new_step = step * other.step; + + bound new_upper; + if (other.upper.is_none()) { + new_upper = upper; + } else { + none ref = ((long)other.upper > 0) ? lower : upper; + if (ref.is_none) { + new_upper = (long)other.upper * step; + } else + new_upper = (long)ref + (long)other.upper * step; + } + return {new_lower, new_upper, new_step}; + } + + /* + Normalize change a[:-1] to a[:len(a)-1] to have positif index. + It also check for value bigger than len(a) to fit the size of the + container + */ + template + inline cstride_normalized_slice + cstride_slice::normalize(long max_size) const + { + long normalized_upper; + if (upper.is_none()) { + normalized_upper = max_size; + } else { + if (upper < 0L) + normalized_upper = std::max(-1L, max_size + upper); + else if (upper > max_size) + normalized_upper = max_size; + else + normalized_upper = (long)upper; + } + + long normalized_lower; + if (lower.is_none()) + normalized_lower = 0L; + else if (lower < 0L) + normalized_lower = std::max(0L, max_size + lower); + else if (lower > max_size) + normalized_lower = max_size; + else + normalized_lower = (long)lower; + + return {normalized_lower, normalized_upper}; + } + /* + * An assert is raised when we can't compute the size without more + * informations. + */ + template + inline long cstride_slice::size() const + { + assert(!(upper.is_none() && lower.is_none())); + long len; + if (upper.is_none()) { + len = -(long)lower; + } else if (lower.is_none()) { + len = upper; + } else + len = upper - lower; + return std::max(0L, details::roundup_divide(len, step)); + } + + template + inline long cstride_slice::get(long i) const + { + assert(!upper.is_none() && !lower.is_none()); + return (long)lower + i * step; + } + + // inline contiguous_normalized_slice::contiguous_normalized_slice() { } @@ -244,6 +485,14 @@ namespace types return (*this) * other.normalize(size()); } + template + inline cstride_normalized_slice + contiguous_normalized_slice::operator*( + cstride_normalized_slice const &other) const + { + return {lower + step * other.lower, lower + step * other.upper}; + } + inline normalized_slice contiguous_normalized_slice::operator*(normalized_slice const &other) const { @@ -251,6 +500,14 @@ namespace types lower + step * other.upper, step * other.step); } + template + inline cstride_normalized_slice + contiguous_normalized_slice::operator*( + cstride_slice const &other) const + { + return (*this) * other.normalize(size()); + } + inline normalized_slice contiguous_normalized_slice::operator*(slice const &other) const { @@ -577,6 +834,22 @@ to_python::convert(types::contiguous_slice const &v) ::to_python(types::none_type{})); } +template +inline PyObject *to_python>::convert( + types::cstride_normalized_slice const &v) +{ + return PySlice_New(::to_python(v.lower), ::to_python(v.upper), + ::to_python(v.step)); +} + +template +inline PyObject *to_python>::convert( + types::cstride_slice const &v) +{ + return PySlice_New(::to_python(v.lower), ::to_python(v.upper), + ::to_python(v.step)); +} + inline PyObject * to_python::convert(types::normalized_slice const &v) { diff --git a/pythran/pythonic/types/tuple.hpp b/pythran/pythonic/types/tuple.hpp index 5d0ebab5f..d96822224 100644 --- a/pythran/pythonic/types/tuple.hpp +++ b/pythran/pythonic/types/tuple.hpp @@ -4,16 +4,16 @@ #include "pythonic/include/types/tuple.hpp" #include "pythonic/types/assignable.hpp" -#include "pythonic/types/traits.hpp" -#include "pythonic/types/nditerator.hpp" #include "pythonic/types/dynamic_tuple.hpp" +#include "pythonic/types/ndarray.hpp" +#include "pythonic/types/nditerator.hpp" +#include "pythonic/types/traits.hpp" #include "pythonic/utils/int_.hpp" -#include "pythonic/utils/seq.hpp" #include "pythonic/utils/nested_container.hpp" -#include "pythonic/types/ndarray.hpp" +#include "pythonic/utils/seq.hpp" -#include #include +#include namespace std { @@ -27,7 +27,7 @@ namespace std { return self.first == get<0>(other) && self.second == get<1>(other); } -} +} // namespace std template std::tuple operator+(std::tuple const &t0, @@ -133,8 +133,8 @@ namespace types } template - typename array_base::const_iterator array_base::end() const - noexcept + typename array_base::const_iterator + array_base::end() const noexcept { return {data() + N}; } @@ -175,8 +175,8 @@ namespace types } template - typename array_base::const_iterator array_base::cend() const - noexcept + typename array_base::const_iterator + array_base::cend() const noexcept { return {&(buffer[N])}; } @@ -236,7 +236,7 @@ namespace types template template typename array_base::simd_iterator - array_base::vbegin(vectorizer) const + array_base::vbegin(vectorizer) const { return {&buffer[0]}; } @@ -244,7 +244,7 @@ namespace types template template typename array_base::simd_iterator - array_base::vend(vectorizer) const + array_base::vend(vectorizer) const { using vector_type = typename xsimd::batch; static const std::size_t vector_size = vector_type::size; @@ -253,8 +253,8 @@ namespace types #endif template - typename array_base::reference array_base:: - operator[](long __n) + typename array_base::reference + array_base::operator[](long __n) { auto const index = __n < 0 ? (__n + size()) : __n; assert(0 <= index && index < size()); @@ -262,8 +262,8 @@ namespace types } template - typename array_base::const_reference array_base:: - operator[](long __n) const noexcept + typename array_base::const_reference + array_base::operator[](long __n) const noexcept { auto const index = __n < 0 ? (__n + size()) : __n; assert(0 <= index && index < size()); @@ -303,8 +303,8 @@ namespace types } template - typename array_base::const_pointer array_base::data() const - noexcept + typename array_base::const_pointer + array_base::data() const noexcept { return &(buffer[0]); } @@ -333,8 +333,8 @@ namespace types template template - array_base::type, N + M, V> array_base:: - operator+(array_base const &other) const + array_base::type, N + M, V> + array_base::operator+(array_base const &other) const { array_base::type, N + M, V> result; auto next = std::copy(begin(), end(), result.begin()); @@ -412,6 +412,18 @@ namespace types tmp[j] = b[ns.lower + j * ns.step]; return {&tmp[0], &tmp[ns.size()]}; } + + template + dynamic_tuple array_base_slicer::operator()(array const &b, + cstride_slice const &s) + { + cstride_normalized_slice ns = s.normalize(b.size()); + array tmp; + for (long j = 0; j < ns.size(); ++j) + tmp[j] = b[ns.lower + j * ns.step]; + return {&tmp[0], &tmp[ns.size()]}; + } + template dynamic_tuple array_base_slicer::operator()(array const &b, contiguous_slice const &s) @@ -426,7 +438,7 @@ namespace types contiguous_normalized_slice cns = s.normalize(b.size()); return {&b[cns.lower], &b[cns.upper]}; } -} +} // namespace types PYTHONIC_NS_END /* hashable tuples, as proposed in @@ -441,8 +453,9 @@ namespace } template - size_t hash_impl:: - operator()(size_t a, const std::tuple &t) const + size_t + hash_impl::operator()(size_t a, + const std::tuple &t) const { using nexttype = typename std::tuple_element>::type; @@ -459,22 +472,22 @@ namespace size_t b = std::hash()(std::get<0>(t)); return hash_combiner(a, b); } -} +} // namespace /* specialize std::hash */ namespace std { template - size_t hash>:: - operator()(std::tuple const &t) const + size_t + hash>::operator()(std::tuple const &t) const { const size_t begin = std::tuple_size>::value - 1; return hash_impl()(1, t); // 1 should be some largervalue } template - size_t hash>:: - operator()(pythonic::types::array_base const &l) const + size_t hash>::operator()( + pythonic::types::array_base const &l) const { size_t seed = 0; hash h; @@ -482,7 +495,7 @@ namespace std seed ^= h(iter) + 0x9e3779b9 + (seed << 6) + (seed >> 2); return seed; } -} +} // namespace std PYTHONIC_NS_BEGIN @@ -500,7 +513,7 @@ namespace types { os << std::get<0>(t); } -} +} // namespace types PYTHONIC_NS_END namespace std @@ -510,14 +523,14 @@ namespace std { os << '('; pythonic::types::print_tuple(os, t, - pythonic::utils::int_()); + pythonic::utils::int_()); return os << ')'; } -} +} // namespace std #ifdef ENABLE_PYTHON_MODULE -#include "pythonic/include/utils/seq.hpp" #include "pythonic/include/utils/fwd.hpp" +#include "pythonic/include/utils/seq.hpp" #include "pythonic/python/core.hpp" PYTHONIC_NS_BEGIN diff --git a/pythran/types/types.py b/pythran/types/types.py index 22d3c2791..e58664c04 100644 --- a/pythran/types/types.py +++ b/pythran/types/types.py @@ -483,12 +483,16 @@ def visit_Slice(self, node): Also visit subnodes as they may contains relevant typing information. """ self.generic_visit(node) - if node.step is None or (isnum(node.step) and node.step.value == 1): - if all(self.range_values[p].low >= 0 - for p in (node.lower, node.upper)): - ntype = "pythonic::types::fast_contiguous_slice" + nstep = node.step + if nstep is None or (isnum(nstep) and nstep.value > 0): + if nstep is None or nstep.value == 1: + if all(self.range_values[p].low >= 0 + for p in (node.lower, node.upper)): + ntype = "pythonic::types::fast_contiguous_slice" + else: + ntype = "pythonic::types::contiguous_slice" else: - ntype = "pythonic::types::contiguous_slice" + ntype = "pythonic::types::cstride_slice<{}>".format(nstep.value) self.result[node] = self.builder.NamedType(ntype) else: self.result[node] = self.builder.NamedType(