28template <
typename ExtentT = std::
size_t,
typename Str
ideT = std::ptrdiff_t>
struct extent_stride
43 if (
extent == ExtentT{1} || inner.extent == ExtentT{1})
return true;
44 return stride == inner.stride *
static_cast<StrideT
>(inner.extent);
57 stride = inner.extent == ExtentT{1} ? StrideT{1} : inner.stride;
60 if (inner.extent == ExtentT{1})
return;
95 [[nodiscard]] std::size_t
rank() const noexcept {
return dimensions.size(); }
104[[nodiscard]]
constexpr bool checked_iteration_multiply(std::ptrdiff_t lhs, std::ptrdiff_t rhs,
105 std::ptrdiff_t& result)
noexcept
107 if (lhs == 0 || rhs == 0)
113 constexpr auto minimum = std::numeric_limits<std::ptrdiff_t>::min();
114 constexpr auto maximum = std::numeric_limits<std::ptrdiff_t>::max();
115 if ((lhs == -1 && rhs == minimum) || (rhs == -1 && lhs == minimum))
return false;
119 if ((rhs > 0 && lhs > maximum / rhs) || (rhs < 0 && rhs < minimum / lhs))
return false;
123 if ((rhs > 0 && lhs < minimum / rhs) || (rhs < 0 && lhs < maximum / rhs))
return false;
130[[nodiscard]]
constexpr bool checked_iteration_add(std::ptrdiff_t lhs, std::ptrdiff_t rhs,
131 std::ptrdiff_t& result)
noexcept
133 constexpr auto minimum = std::numeric_limits<std::ptrdiff_t>::min();
134 constexpr auto maximum = std::numeric_limits<std::ptrdiff_t>::max();
135 if ((rhs > 0 && lhs > maximum - rhs) || (rhs < 0 && lhs < minimum - rhs))
return false;
140template <std::
size_t OperandCount, std::
size_t MaximumRank>
141void finish_iteration_plan(multi_iteration_plan<OperandCount, MaximumRank>& plan)
143 if (plan.dimensions.size() == 1 && plan.dimensions[0].extent == 0)
145 plan.element_count = 0;
146 for (
auto& range : plan.reachable_offsets)
147 range = iteration_offset_range{.minimum = 0, .maximum = -1};
151 plan.element_count = 1;
152 for (
auto const& dimension : plan.dimensions)
154 auto const extent =
static_cast<std::size_t
>(dimension.extent);
155 if (extent != 0 && plan.element_count > std::numeric_limits<std::size_t>::max() / extent)
157 plan.representable =
false;
160 plan.element_count *= extent;
163 for (std::size_t operand = 0; operand < OperandCount; ++operand)
165 auto minimum = plan.base_offsets[operand];
166 auto maximum = plan.base_offsets[operand];
167 for (
auto const& dimension : plan.dimensions)
169 std::ptrdiff_t delta = 0;
170 if (!checked_iteration_multiply(
static_cast<std::ptrdiff_t
>(dimension.extent - 1), dimension.strides[operand],
173 plan.representable =
false;
177 auto& bound = delta < 0 ? minimum : maximum;
178 if (!checked_iteration_add(bound, delta, bound))
180 plan.representable =
false;
184 plan.reachable_offsets[operand] = iteration_offset_range{.minimum = minimum, .maximum = maximum};
197 using size_type =
typename Mapping::size_type;
198 using index_type =
typename Mapping::index_type;
199 static constexpr size_type Rank = Mapping::extents_type::rank();
207 size_type const extent = mapping.extents().extent(i);
225 if (extent == 1)
continue;
230 offset += stride *
static_cast<index_type>(extent - 1);
234 raw_plan.
emplace_back(extent, std::array<index_type, 1>{stride});
240 if (raw_plan.
empty())
return std::pair{plan, offset};
242 std::sort(raw_plan.
begin(), raw_plan.
end(),
243 [](
auto const& lhs,
auto const& rhs) { return std::abs(lhs.strides[0]) > std::abs(rhs.strides[0]); });
247 for (
auto const& dim : raw_plan)
252 return std::pair{plan, offset};
265 static_assert(
sizeof...(Mappings) >= 1,
"At least one mapping is required");
267 using first_mapping = std::tuple_element_t<0, std::tuple<Mappings...>>;
268 using size_type =
typename first_mapping::size_type;
270 static constexpr std::size_t N =
sizeof...(Mappings);
271 static constexpr size_type Rank = first_mapping::extents_type::rank();
273 static_assert(((Mappings::extents_type::rank() == Rank) && ...),
274 "All iteration-plan mappings must have the same rank");
276 auto const& base_mapping = std::get<0>(mappings);
277 auto const& base_extents = base_mapping.extents();
278 std::size_t logical_element_count = 1;
280 auto check_extents = [&](
auto const& mapping) {
281 for (
size_type axis = 0; axis < Rank; ++axis)
283 "elementwise iteration requires equal extents");
285 std::apply([&](
auto const&... mapping) { (check_extents(mapping), ...); }, mappings);
289 if constexpr (Rank > 0)
291 auto make_empty_plan = [&] {
293 plan.dimensions.emplace_back(
size_type{0}, std::array<index_type, N>{});
294 plan.base_offsets = std::array<index_type, N>{};
295 detail::finish_iteration_plan(plan);
300 size_type const extent = base_extents.extent(i);
312 if (std::cmp_greater(extent, std::numeric_limits<std::ptrdiff_t>::max()) ||
313 logical_element_count > std::numeric_limits<std::ptrdiff_t>::max() /
static_cast<std::size_t
>(extent))
317 for (
size_type remaining = i + 1; remaining < Rank; ++remaining)
319 if (base_extents.extent(remaining) == 0)
325 plan.representable =
false;
328 logical_element_count *=
static_cast<std::size_t
>(extent);
332 if (extent == 1)
continue;
335 [i](
auto const&... mapping) {
336 return std::array<index_type, N>{
static_cast<index_type>(mapping.stride(i))...};
342 for (std::size_t k = 0; k < N; ++k)
345 if (!detail::checked_iteration_multiply(
strides[k],
static_cast<index_type>(extent - 1), delta) ||
346 !detail::checked_iteration_add(plan.base_offsets[k], delta, plan.base_offsets[k]))
348 plan.representable =
false;
351 if (
strides[k] == std::numeric_limits<index_type>::min())
353 plan.representable =
false;
360 plan.dimensions.emplace_back(extent,
strides);
365 if (!plan.dimensions.empty())
367 std::sort(plan.dimensions.begin(), plan.dimensions.end(),
368 [](
auto const& lhs,
auto const& rhs) { return std::abs(lhs.strides[0]) > std::abs(rhs.strides[0]); });
372 detail::finish_iteration_plan(plan);
A fixed-capacity vector with stack storage and std::vector-like API.
Definition static_vector.hpp:24
iterator end() noexcept
Definition static_vector.hpp:198
bool empty() const noexcept
Definition static_vector.hpp:210
void emplace_back(Args &&... args)
Construct a new element in-place at the end.
Definition static_vector.hpp:242
iterator begin() noexcept
Definition static_vector.hpp:194
auto make_iteration_plan_with_offset(Mapping const &mapping)
Construct a merged iteration plan and offset for a single mapping.
Definition iteration_plan.hpp:195
auto make_multi_iteration_plan(std::tuple< Mappings... > const &mappings)
Build a merged iteration plan for multiple tensors sharing the same extents.
Definition iteration_plan.hpp:263
constexpr void merge_with_inner(extent_stride inner) noexcept
Merge an inner dimension into this one.
Definition iteration_plan.hpp:52
constexpr bool can_merge_with_inner(extent_stride inner) const noexcept
Returns true when two adjacent dimensions can be merged.
Definition iteration_plan.hpp:41
auto strides(S const &s)
Retrieve the strides associated with a strided mdspec-like type.
Definition mdspan/concepts.hpp:558
static_vector< extent_strides< 2 >, R > merge_strides_right(Extents const &ext, std::array< std::ptrdiff_t, R > const &Stride1, std::array< std::ptrdiff_t, R > const &Stride2)
Build and merge stride metadata from two stride arrays favouring row-major (right) order.
Definition strides.hpp:240
Mdspan concept and accessor extensions for Uni20.
Definition async/async.hpp:26
std::ptrdiff_t size_type
Signed size and index type, used for tensor extents and indexing.
Definition types.hpp:18
std::ptrdiff_t index_type
Definition types.hpp:19
Defines static_vector, a fixed-capacity, stack-allocated vector with a std::vector-like API.
Stride utilities for mdspan-like tensors.
Represents a single dimension's extent and stride for iteration planning.
Definition iteration_plan.hpp:29
StrideT stride
Definition iteration_plan.hpp:31
constexpr extent_stride()=default
ExtentT extent
Definition iteration_plan.hpp:30
constexpr extent_stride(ExtentT extent_, StrideT stride_)
Definition iteration_plan.hpp:34
Inclusive range of handle-relative offsets visited by an iteration plan.
Definition iteration_plan.hpp:71
std::ptrdiff_t maximum
Definition iteration_plan.hpp:73
constexpr bool empty() const noexcept
Report whether the iteration visits no offsets.
Definition iteration_plan.hpp:76
std::ptrdiff_t minimum
Definition iteration_plan.hpp:72
Compact affine iteration plan shared by host and device backends.
Definition iteration_plan.hpp:84
std::array< std::ptrdiff_t, OperandCount > base_offsets
Definition iteration_plan.hpp:89
static_vector< extent_strides< OperandCount >, MaximumRank > dimensions
Definition iteration_plan.hpp:88
std::array< iteration_offset_range, OperandCount > reachable_offsets
Definition iteration_plan.hpp:90
static constexpr std::size_t operand_count
Definition iteration_plan.hpp:85
std::size_t rank() const noexcept
Report the compact rank after size-one elimination and coalescing.
Definition iteration_plan.hpp:95
bool representable
Definition iteration_plan.hpp:92
bool empty() const noexcept
Report whether the logical iteration space is empty.
Definition iteration_plan.hpp:98
std::size_t element_count
Definition iteration_plan.hpp:91
static constexpr std::size_t maximum_rank
Definition iteration_plan.hpp:86
#define PRECONDITION_EQUAL(a, b,...)
Definition trace.hpp:351