@@ -0,0 +1,70 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2023 - 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
**************************************************************************************************/
|
||||
#pragma once
|
||||
|
||||
#include <cute/config.hpp>
|
||||
|
||||
#include <cute/numeric/int.hpp>
|
||||
#include <cute/numeric/math.hpp>
|
||||
|
||||
namespace cute
|
||||
{
|
||||
|
||||
// Test if a pointer is aligned to N bytes
|
||||
template <int N>
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
bool
|
||||
is_byte_aligned(void const* const ptr)
|
||||
{
|
||||
static_assert(N > 0 && (N & (N - 1)) == 0, "N must be a power of 2 in alignment check");
|
||||
return (reinterpret_cast<uintptr_t>(ptr) & (N-1)) == 0;
|
||||
}
|
||||
|
||||
#if defined(__CUDACC__)
|
||||
# define CUTE_ALIGNAS(n) __align__(n)
|
||||
#else
|
||||
# define CUTE_ALIGNAS(n) alignas(n)
|
||||
#endif
|
||||
|
||||
template <std::size_t Alignment>
|
||||
struct aligned_struct {};
|
||||
|
||||
template <> struct CUTE_ALIGNAS( 1) aligned_struct< 1> {};
|
||||
template <> struct CUTE_ALIGNAS( 2) aligned_struct< 2> {};
|
||||
template <> struct CUTE_ALIGNAS( 4) aligned_struct< 4> {};
|
||||
template <> struct CUTE_ALIGNAS( 8) aligned_struct< 8> {};
|
||||
template <> struct CUTE_ALIGNAS( 16) aligned_struct< 16> {};
|
||||
template <> struct CUTE_ALIGNAS( 32) aligned_struct< 32> {};
|
||||
template <> struct CUTE_ALIGNAS( 64) aligned_struct< 64> {};
|
||||
template <> struct CUTE_ALIGNAS(128) aligned_struct<128> {};
|
||||
template <> struct CUTE_ALIGNAS(256) aligned_struct<256> {};
|
||||
|
||||
} // end namespace cute
|
||||
@@ -0,0 +1,282 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2023 - 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
**************************************************************************************************/
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
|
||||
#include <cute/config.hpp>
|
||||
|
||||
namespace cute
|
||||
{
|
||||
|
||||
template <class T, std::size_t N>
|
||||
struct array
|
||||
{
|
||||
using value_type = T;
|
||||
using size_type = std::size_t;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using reference = value_type&;
|
||||
using const_reference = const value_type&;
|
||||
using pointer = value_type*;
|
||||
using const_pointer = const value_type*;
|
||||
using iterator = pointer;
|
||||
using const_iterator = const_pointer;
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
reference operator[](size_type pos)
|
||||
{
|
||||
return begin()[pos];
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
const_reference operator[](size_type pos) const
|
||||
{
|
||||
return begin()[pos];
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
reference front()
|
||||
{
|
||||
return *begin();
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
const_reference front() const
|
||||
{
|
||||
return *begin();
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
reference back()
|
||||
{
|
||||
// return *rbegin();
|
||||
return operator[](N-1);
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
const_reference back() const
|
||||
{
|
||||
// return *rbegin();
|
||||
return operator[](N-1);
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
T* data()
|
||||
{
|
||||
return __elems_;
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
T const* data() const
|
||||
{
|
||||
return __elems_;
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
iterator begin()
|
||||
{
|
||||
return data();
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
const_iterator begin() const
|
||||
{
|
||||
return data();
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
const_iterator cbegin()
|
||||
{
|
||||
return begin();
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
const_iterator cbegin() const
|
||||
{
|
||||
return begin();
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
iterator end()
|
||||
{
|
||||
return data() + size();
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
const_iterator end() const
|
||||
{
|
||||
return data() + size();
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
const_iterator cend()
|
||||
{
|
||||
return end();
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
const_iterator cend() const
|
||||
{
|
||||
return end();
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
bool empty() const
|
||||
{
|
||||
return size() == 0;
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
size_type size() const
|
||||
{
|
||||
return N;
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
size_type max_size() const
|
||||
{
|
||||
return size();
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
void fill(const T& value)
|
||||
{
|
||||
for (auto& e : *this) {
|
||||
e = value;
|
||||
}
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
void clear()
|
||||
{
|
||||
fill(T(0));
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
void swap(array& other)
|
||||
{
|
||||
using std::swap;
|
||||
for (size_type i = 0; i < size(); ++i) {
|
||||
swap((*this)[i], other[i]);
|
||||
}
|
||||
}
|
||||
|
||||
value_type __elems_[N > 0 ? N : 1];
|
||||
};
|
||||
|
||||
|
||||
template<class T, std::size_t N>
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
bool operator==(array<T,N> const& lhs, array<T,N> const& rhs)
|
||||
{
|
||||
for (std::size_t i = 0; i < N; ++i) {
|
||||
if (lhs[i] != rhs[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
void clear(array<T,N>& a)
|
||||
{
|
||||
a.fill(T(0));
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
void fill(array<T,N>& a, T const& value)
|
||||
{
|
||||
a.fill(value);
|
||||
}
|
||||
|
||||
template<class T, std::size_t N>
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
void swap(array<T,N>& a, array<T,N>& b)
|
||||
{
|
||||
a.swap(b);
|
||||
}
|
||||
|
||||
} // end cute
|
||||
|
||||
|
||||
//
|
||||
// Specialize tuple-related functionality for cute::array
|
||||
//
|
||||
|
||||
#include <tuple>
|
||||
|
||||
namespace cute
|
||||
{
|
||||
|
||||
template<std::size_t I, class T, std::size_t N>
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
T& get(array<T,N>& a)
|
||||
{
|
||||
static_assert(I < N, "Index out of range");
|
||||
return a[I];
|
||||
}
|
||||
|
||||
template<std::size_t I, class T, std::size_t N>
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
T const& get(array<T,N> const& a)
|
||||
{
|
||||
static_assert(I < N, "Index out of range");
|
||||
return a[I];
|
||||
}
|
||||
|
||||
template<std::size_t I, class T, std::size_t N>
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
T&& get(array<T,N>&& a)
|
||||
{
|
||||
static_assert(I < N, "Index out of range");
|
||||
return std::move(a[I]);
|
||||
}
|
||||
|
||||
} // end namespace cute
|
||||
|
||||
namespace std
|
||||
{
|
||||
|
||||
template <class T, std::size_t N>
|
||||
struct tuple_size<cute::array<T,N>>
|
||||
: std::integral_constant<std::size_t, N>
|
||||
{};
|
||||
|
||||
template <std::size_t I, class T, std::size_t N>
|
||||
struct tuple_element<I, cute::array<T,N>>
|
||||
{
|
||||
using type = T;
|
||||
};
|
||||
|
||||
} // end std
|
||||
@@ -0,0 +1,276 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2023 - 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
**************************************************************************************************/
|
||||
#pragma once
|
||||
|
||||
#include <cute/config.hpp>
|
||||
|
||||
#include <cute/container/alignment.hpp>
|
||||
#include <cute/numeric/int.hpp>
|
||||
#include <cute/numeric/math.hpp>
|
||||
|
||||
namespace cute
|
||||
{
|
||||
|
||||
template <typename T, std::size_t N, std::size_t Alignment = 16>
|
||||
struct array_aligned
|
||||
: public aligned_struct<Alignment>
|
||||
{
|
||||
/// Make sure the Alignment makes sense wrt the size of elements.
|
||||
static_assert(Alignment == 16 || Alignment >= sizeof(T), "Alignment is too small");
|
||||
/// Alignment must be a power of two
|
||||
static_assert(has_single_bit(Alignment), "Alignment must be a power of two");
|
||||
|
||||
using value_type = T;
|
||||
using size_type = std::size_t;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using reference = value_type&;
|
||||
using const_reference = const value_type&;
|
||||
using pointer = value_type*;
|
||||
using const_pointer = const value_type*;
|
||||
using iterator = pointer;
|
||||
using const_iterator = const_pointer;
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
reference operator[](size_type pos)
|
||||
{
|
||||
return begin()[pos];
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
const_reference operator[](size_type pos) const
|
||||
{
|
||||
return begin()[pos];
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
reference front()
|
||||
{
|
||||
return *begin();
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
const_reference front() const
|
||||
{
|
||||
return *begin();
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
reference back()
|
||||
{
|
||||
// return *rbegin();
|
||||
return operator[](N-1);
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
const_reference back() const
|
||||
{
|
||||
// return *rbegin();
|
||||
return operator[](N-1);
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
T* data()
|
||||
{
|
||||
return reinterpret_cast<T*>(storage);
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
T const* data() const
|
||||
{
|
||||
return reinterpret_cast<T const*>(storage);
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
iterator begin()
|
||||
{
|
||||
return data();
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
const_iterator begin() const
|
||||
{
|
||||
return data();
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
const_iterator cbegin()
|
||||
{
|
||||
return begin();
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
const_iterator cbegin() const
|
||||
{
|
||||
return begin();
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
iterator end()
|
||||
{
|
||||
return data() + size();
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
const_iterator end() const
|
||||
{
|
||||
return data() + size();
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
const_iterator cend()
|
||||
{
|
||||
return end();
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
const_iterator cend() const
|
||||
{
|
||||
return end();
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
bool empty() const
|
||||
{
|
||||
return size() == 0;
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
size_type size() const
|
||||
{
|
||||
return N;
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
size_type max_size() const
|
||||
{
|
||||
return size();
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
void fill(T const& value)
|
||||
{
|
||||
for (auto& e : *this) {
|
||||
e = value;
|
||||
}
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
void clear()
|
||||
{
|
||||
fill(T(0));
|
||||
}
|
||||
|
||||
// Not private, we want trivial type
|
||||
//private:
|
||||
|
||||
/// Storage type to use for Elements
|
||||
using StorageType = typename uint_byte<static_cast<int>(Alignment)>::type;
|
||||
|
||||
/// Ensure that there's enough storage for all elements
|
||||
static_assert(sizeof(StorageType) <= Alignment, "StorageType is too big for given alignment");
|
||||
|
||||
/// Number of elements in the storage
|
||||
static constexpr std::size_t storageN = (sizeof(T)*N + sizeof(StorageType) - 1) / sizeof(StorageType);
|
||||
|
||||
/// The storage.
|
||||
StorageType storage[storageN > 0 ? storageN : 1];
|
||||
};
|
||||
|
||||
//
|
||||
// Operators
|
||||
//
|
||||
|
||||
template <typename T, std::size_t N, std::size_t Alignment>
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
void clear(array_aligned<T, N, Alignment>& a)
|
||||
{
|
||||
a.clear();
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N, std::size_t Alignment>
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
void fill(array_aligned<T, N, Alignment>& a, T const& value)
|
||||
{
|
||||
a.fill(value);
|
||||
}
|
||||
|
||||
} // end namespace cute
|
||||
|
||||
//
|
||||
// Specialize tuple-related functionality for cute::array
|
||||
//
|
||||
|
||||
#include <tuple>
|
||||
|
||||
namespace cute
|
||||
{
|
||||
|
||||
template <std::size_t I, class T, std::size_t N>
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
T& get(array_aligned<T,N>& a)
|
||||
{
|
||||
static_assert(I < N, "Index out of range");
|
||||
return a[I];
|
||||
}
|
||||
|
||||
template <std::size_t I, class T, std::size_t N>
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
T const& get(array_aligned<T,N> const& a)
|
||||
{
|
||||
static_assert(I < N, "Index out of range");
|
||||
return a[I];
|
||||
}
|
||||
|
||||
template <std::size_t I, class T, std::size_t N>
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
T&& get(array_aligned<T,N>&& a)
|
||||
{
|
||||
static_assert(I < N, "Index out of range");
|
||||
return std::move(a[I]);
|
||||
}
|
||||
|
||||
} // end namespace cute
|
||||
|
||||
namespace std
|
||||
{
|
||||
|
||||
template <class T, std::size_t N>
|
||||
struct tuple_size<cute::array_aligned<T,N>>
|
||||
: std::integral_constant<std::size_t, N>
|
||||
{};
|
||||
|
||||
template <std::size_t I, class T, std::size_t N>
|
||||
struct tuple_element<I, cute::array_aligned<T,N>>
|
||||
{
|
||||
using type = T;
|
||||
};
|
||||
|
||||
} // end std
|
||||
@@ -0,0 +1,613 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2023 - 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
**************************************************************************************************/
|
||||
/*! \file
|
||||
\brief Statically sized array of elements that accommodates subbyte trivial types
|
||||
in a packed storage.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cute/config.hpp>
|
||||
|
||||
#include <cute/numeric/int.hpp> // sizeof_bits
|
||||
|
||||
namespace cute
|
||||
{
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Statically sized array for any data type
|
||||
template <class T, std::size_t N>
|
||||
class array_subbyte
|
||||
{
|
||||
public:
|
||||
|
||||
/// Number of total bits in the array
|
||||
static constexpr int kSizeBits = sizeof_bits<T>::value * N;
|
||||
|
||||
/// Storage type
|
||||
using Storage = typename std::conditional<
|
||||
(kSizeBits % 32) == 0,
|
||||
uint32_t,
|
||||
typename std::conditional<
|
||||
(kSizeBits % 16) == 0,
|
||||
uint16_t,
|
||||
uint8_t
|
||||
>::type
|
||||
>::type;
|
||||
|
||||
|
||||
/// Number of logical elements per stored object
|
||||
static constexpr int kElementsPerStoredItem = sizeof_bits<Storage>::value / sizeof_bits<T>::value;
|
||||
|
||||
/// Number of storage elements
|
||||
static constexpr std::size_t kStorageElements = (N + kElementsPerStoredItem - 1) / kElementsPerStoredItem;
|
||||
|
||||
/// Bitmask for covering one item
|
||||
static constexpr Storage bit_mask_ = ((Storage(1) << sizeof_bits<T>::value) - 1);
|
||||
|
||||
//
|
||||
// C++ standard members with reference and iterator types omitted
|
||||
//
|
||||
|
||||
using value_type = T;
|
||||
using pointer = value_type*;
|
||||
using const_pointer = value_type const*;
|
||||
|
||||
using size_type = std::size_t;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
|
||||
//
|
||||
// References
|
||||
//
|
||||
|
||||
/// Reference object inserts or extracts sub-byte items
|
||||
class reference {
|
||||
/// Pointer to storage element
|
||||
Storage* ptr_;
|
||||
|
||||
/// Index into elements packed into Storage object
|
||||
int idx_;
|
||||
|
||||
public:
|
||||
|
||||
/// Default ctor
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
reference() : ptr_(nullptr), idx_(0) {}
|
||||
|
||||
/// Ctor
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
reference(Storage* ptr, int idx = 0) : ptr_(ptr), idx_(idx) {}
|
||||
|
||||
/// Assignment
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
reference& operator=(T x) {
|
||||
Storage item = (reinterpret_cast<Storage const&>(x) & bit_mask_);
|
||||
Storage kUpdateMask = Storage(~(bit_mask_ << (idx_ * sizeof_bits<T>::value)));
|
||||
*ptr_ = Storage((*ptr_ & kUpdateMask) | (item << (idx_ * sizeof_bits<T>::value)));
|
||||
return *this;
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
T get() const {
|
||||
Storage item = Storage((*ptr_ >> (idx_ * sizeof_bits<T>::value)) & bit_mask_);
|
||||
return reinterpret_cast<T const&>(item);
|
||||
}
|
||||
|
||||
/// Extract to type T -- disable if T == bool
|
||||
template <class U = T, __CUTE_REQUIRES(not std::is_same<U,bool>::value)>
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
operator T() const {
|
||||
return get();
|
||||
}
|
||||
|
||||
// Extract to bool -- potentially faster impl
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
operator bool() const {
|
||||
return bool((*ptr_) & (bit_mask_ << (idx_ * sizeof_bits<T>::value)));
|
||||
}
|
||||
|
||||
/// Explicit cast to int
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
explicit operator int() const {
|
||||
return int(get());
|
||||
}
|
||||
|
||||
/// Explicit cast to float
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
explicit operator float() const {
|
||||
return float(get());
|
||||
}
|
||||
};
|
||||
|
||||
/// Reference object extracts sub-byte items
|
||||
class const_reference {
|
||||
|
||||
/// Pointer to storage element
|
||||
Storage const* ptr_;
|
||||
|
||||
/// Index into elements packed into Storage object
|
||||
int idx_;
|
||||
|
||||
public:
|
||||
|
||||
/// Default ctor
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
const_reference(): ptr_(nullptr), idx_(0) { }
|
||||
|
||||
/// Ctor
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
const_reference(Storage const* ptr, int idx = 0): ptr_(ptr), idx_(idx) { }
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
const T get() const {
|
||||
Storage item = Storage((*ptr_ >> (idx_ * sizeof_bits<T>::value)) & bit_mask_);
|
||||
return reinterpret_cast<T const&>(item);
|
||||
}
|
||||
|
||||
/// Extract to type T -- disable if T == bool
|
||||
template <class U = T, __CUTE_REQUIRES(not std::is_same<U,bool>::value)>
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
operator T() const {
|
||||
return get();
|
||||
}
|
||||
|
||||
// Extract to bool -- potentially faster impl
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
operator bool() const {
|
||||
return bool((*ptr_) & (bit_mask_ << (idx_ * sizeof_bits<T>::value)));
|
||||
}
|
||||
|
||||
/// Explicit cast to int
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
explicit operator int() const {
|
||||
return int(get());
|
||||
}
|
||||
|
||||
/// Explicit cast to float
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
explicit operator float() const {
|
||||
return float(get());
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// Iterators
|
||||
//
|
||||
|
||||
/// Bidirectional iterator over elements
|
||||
class iterator {
|
||||
|
||||
/// Pointer to storage element
|
||||
Storage* ptr_;
|
||||
|
||||
/// Index into elements packed into Storage object
|
||||
int idx_;
|
||||
|
||||
public:
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
iterator(): ptr_(nullptr), idx_(0) { }
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
iterator(Storage* ptr, int idx = 0): ptr_(ptr), idx_(idx) { }
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
iterator& operator++() {
|
||||
++idx_;
|
||||
if (idx_ == kElementsPerStoredItem) {
|
||||
++ptr_;
|
||||
idx_ = 0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
iterator& operator--() {
|
||||
if (idx_) {
|
||||
--idx_;
|
||||
} else {
|
||||
--ptr_;
|
||||
idx_ = kElementsPerStoredItem - 1;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
iterator operator++(int) {
|
||||
iterator ret(*this);
|
||||
++(*this);
|
||||
return ret;
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
iterator operator--(int) {
|
||||
iterator ret(*this);
|
||||
--(*this);
|
||||
return ret;
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
iterator& operator+=(int k) {
|
||||
idx_ += k;
|
||||
ptr_ += idx_ / kElementsPerStoredItem;
|
||||
idx_ = idx_ % kElementsPerStoredItem;
|
||||
return *this;
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
iterator operator+(int k) const {
|
||||
return iterator(ptr_,idx_) += k;
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
reference operator*() const {
|
||||
return reference(ptr_, idx_);
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
reference operator[](int k) const {
|
||||
return *(*this + k);
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
bool operator==(iterator const& other) const {
|
||||
return ptr_ == other.ptr_ && idx_ == other.idx_;
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
bool operator!=(iterator const& other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
};
|
||||
|
||||
/// Bidirectional constant iterator over elements
|
||||
class const_iterator {
|
||||
|
||||
/// Pointer to storage element
|
||||
Storage const* ptr_;
|
||||
|
||||
/// Index into elements packed into Storage object
|
||||
int idx_;
|
||||
|
||||
public:
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
const_iterator(): ptr_(nullptr), idx_(0) { }
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
const_iterator(Storage const* ptr, int idx = 0): ptr_(ptr), idx_(idx) { }
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
const_iterator& operator++() {
|
||||
++idx_;
|
||||
if (idx_ == kElementsPerStoredItem) {
|
||||
++ptr_;
|
||||
idx_ = 0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
const_iterator& operator--() {
|
||||
if (idx_) {
|
||||
--idx_;
|
||||
} else {
|
||||
--ptr_;
|
||||
idx_ = kElementsPerStoredItem - 1;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
const_iterator operator++(int) {
|
||||
iterator ret(*this);
|
||||
++idx_;
|
||||
if (idx_ == kElementsPerStoredItem) {
|
||||
++ptr_;
|
||||
idx_ = 0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
const_iterator operator--(int) {
|
||||
iterator ret(*this);
|
||||
if (idx_) {
|
||||
--idx_;
|
||||
} else {
|
||||
--ptr_;
|
||||
idx_ = kElementsPerStoredItem - 1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
const_iterator& operator+=(int k) {
|
||||
idx_ += k;
|
||||
ptr_ += idx_ / kElementsPerStoredItem;
|
||||
idx_ = idx_ % kElementsPerStoredItem;
|
||||
return *this;
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
const_iterator operator+(int k) const {
|
||||
return const_iterator(ptr_,idx_) += k;
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
const_reference operator*() const {
|
||||
return const_reference(ptr_, idx_);
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
const_reference operator[](int k) const {
|
||||
return *(*this + k);
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
bool operator==(iterator const& other) const {
|
||||
return ptr_ == other.ptr_ && idx_ == other.idx_;
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
bool operator!=(iterator const& other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
/// Internal storage
|
||||
Storage storage[kStorageElements];
|
||||
|
||||
public:
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
array_subbyte() { }
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
array_subbyte(array_subbyte const& x) {
|
||||
CUTE_UNROLL
|
||||
for (unsigned i = 0; i < kStorageElements; ++i) {
|
||||
storage[i] = x.storage[i];
|
||||
}
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
size_type size() const {
|
||||
return N;
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
size_type max_size() const {
|
||||
return N;
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
bool empty() const {
|
||||
return !N;
|
||||
}
|
||||
|
||||
/// Efficient clear method
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
void clear() {
|
||||
CUTE_UNROLL
|
||||
for (unsigned i = 0; i < kStorageElements; ++i) {
|
||||
storage[i] = Storage(0);
|
||||
}
|
||||
}
|
||||
|
||||
// Efficient fill method
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
void fill(T const& value) {
|
||||
Storage item = (reinterpret_cast<Storage const&>(value) & bit_mask_);
|
||||
|
||||
// Reproduce the value over the bits of the storage item
|
||||
CUTE_UNROLL
|
||||
for (unsigned s = sizeof_bits<T>::value; s < sizeof_bits<Storage>::value; s *= 2) {
|
||||
item |= item << s;
|
||||
}
|
||||
|
||||
CUTE_UNROLL
|
||||
for (unsigned i = 0; i < kStorageElements; ++i) {
|
||||
storage[i] = item;
|
||||
}
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
reference at(size_type pos) {
|
||||
return reference(storage + pos / kElementsPerStoredItem, pos % kElementsPerStoredItem);
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
const_reference at(size_type pos) const {
|
||||
return const_reference(storage + pos / kElementsPerStoredItem, pos % kElementsPerStoredItem);
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
reference operator[](size_type pos) {
|
||||
return at(pos);
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
const_reference operator[](size_type pos) const {
|
||||
return at(pos);
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
reference front() {
|
||||
return at(0);
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
const_reference front() const {
|
||||
return at(0);
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
reference back() {
|
||||
return reference(storage + kStorageElements - 1, kElementsPerStoredItem - 1);
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
const_reference back() const {
|
||||
return const_reference(storage + kStorageElements - 1, kElementsPerStoredItem - 1);
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
pointer data() {
|
||||
return reinterpret_cast<pointer>(storage);
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
const_pointer data() const {
|
||||
return reinterpret_cast<const_pointer>(storage);
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
Storage* raw_data() {
|
||||
return storage;
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
Storage const* raw_data() const {
|
||||
return storage;
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
iterator begin() {
|
||||
return iterator(storage);
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
const_iterator begin() const {
|
||||
return const_iterator(storage);
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
const_iterator cbegin() const {
|
||||
return begin();
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
iterator end() {
|
||||
return iterator(storage + N / kElementsPerStoredItem, N % kElementsPerStoredItem);
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
const_iterator end() const {
|
||||
return const_iterator(storage + N / kElementsPerStoredItem, N % kElementsPerStoredItem);
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
const_iterator cend() const {
|
||||
return end();
|
||||
}
|
||||
|
||||
//
|
||||
// Comparison operators
|
||||
//
|
||||
|
||||
};
|
||||
|
||||
//
|
||||
// Operators
|
||||
//
|
||||
|
||||
template <class T, std::size_t N>
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
void clear(array_subbyte<T,N>& a)
|
||||
{
|
||||
a.clear();
|
||||
}
|
||||
|
||||
template <class T, std::size_t N>
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
void fill(array_subbyte<T,N>& a, T const& value)
|
||||
{
|
||||
a.fill(value);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // namespace cute
|
||||
|
||||
//
|
||||
// Specialize tuple-related functionality for cute::array_subbyte
|
||||
//
|
||||
|
||||
#include <tuple>
|
||||
|
||||
namespace cute
|
||||
{
|
||||
|
||||
template <std::size_t I, class T, std::size_t N>
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
T& get(array_subbyte<T,N>& a)
|
||||
{
|
||||
static_assert(I < N, "Index out of range");
|
||||
return a[I];
|
||||
}
|
||||
|
||||
template <std::size_t I, class T, std::size_t N>
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
T const& get(array_subbyte<T,N> const& a)
|
||||
{
|
||||
static_assert(I < N, "Index out of range");
|
||||
return a[I];
|
||||
}
|
||||
|
||||
template <std::size_t I, class T, std::size_t N>
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
T&& get(array_subbyte<T,N>&& a)
|
||||
{
|
||||
static_assert(I < N, "Index out of range");
|
||||
return std::move(a[I]);
|
||||
}
|
||||
|
||||
} // end namespace cute
|
||||
|
||||
namespace std
|
||||
{
|
||||
|
||||
template <class T, std::size_t N>
|
||||
struct tuple_size<cute::array_subbyte<T,N>>
|
||||
: std::integral_constant<std::size_t, N>
|
||||
{};
|
||||
|
||||
template <std::size_t I, class T, std::size_t N>
|
||||
struct tuple_element<I, cute::array_subbyte<T,N>>
|
||||
{
|
||||
using type = T;
|
||||
};
|
||||
|
||||
} // end namespace std
|
||||
@@ -0,0 +1,274 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2023 - 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
**************************************************************************************************/
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
|
||||
#include <cute/config.hpp>
|
||||
|
||||
namespace cute
|
||||
{
|
||||
|
||||
template <class T, std::size_t N>
|
||||
struct array_view
|
||||
{
|
||||
using value_type = T;
|
||||
using size_type = std::size_t;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using reference = value_type&;
|
||||
using const_reference = const value_type&;
|
||||
using pointer = value_type*;
|
||||
using const_pointer = const value_type*;
|
||||
using iterator = pointer;
|
||||
using const_iterator = const_pointer;
|
||||
|
||||
array_view(array<T,N>& a)
|
||||
: __elems_(a.data()) {}
|
||||
|
||||
CUTE_HOST_DEVICE
|
||||
reference operator[](size_type pos)
|
||||
{
|
||||
return begin()[pos];
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE
|
||||
const_reference operator[](size_type pos) const
|
||||
{
|
||||
return begin()[pos];
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE
|
||||
reference front()
|
||||
{
|
||||
return *begin();
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE
|
||||
const_reference front() const
|
||||
{
|
||||
return *begin();
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE
|
||||
reference back()
|
||||
{
|
||||
// return *rbegin();
|
||||
return operator[](N-1);
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE
|
||||
const_reference back() const
|
||||
{
|
||||
// return *rbegin();
|
||||
return operator[](N-1);
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE
|
||||
T* data()
|
||||
{
|
||||
return __elems_;
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE
|
||||
const T* data() const
|
||||
{
|
||||
return __elems_;
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE
|
||||
iterator begin()
|
||||
{
|
||||
return data();
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE
|
||||
const_iterator begin() const
|
||||
{
|
||||
return data();
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE
|
||||
const_iterator cbegin()
|
||||
{
|
||||
return begin();
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE
|
||||
const_iterator cbegin() const
|
||||
{
|
||||
return begin();
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE
|
||||
iterator end()
|
||||
{
|
||||
return data() + size();
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE
|
||||
const_iterator end() const
|
||||
{
|
||||
return data() + size();
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE
|
||||
const_iterator cend()
|
||||
{
|
||||
return end();
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE
|
||||
const_iterator cend() const
|
||||
{
|
||||
return end();
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
bool empty() const
|
||||
{
|
||||
return size() == 0;
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
size_type size() const
|
||||
{
|
||||
return N;
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
size_type max_size() const
|
||||
{
|
||||
return size();
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE
|
||||
void fill(const T& value)
|
||||
{
|
||||
for(auto& e : *this)
|
||||
{
|
||||
e = value;
|
||||
}
|
||||
}
|
||||
|
||||
CUTE_HOST_DEVICE
|
||||
void swap(array_view& other)
|
||||
{
|
||||
using std::swap;
|
||||
swap(__elems_, other.__elems_);
|
||||
}
|
||||
|
||||
value_type* __elems_;
|
||||
};
|
||||
|
||||
|
||||
template<class T, std::size_t N>
|
||||
CUTE_HOST_DEVICE
|
||||
bool operator==(const array_view<T,N>& lhs, const array_view<T,N>& rhs)
|
||||
{
|
||||
for(std::size_t i = 0; i < N; ++i)
|
||||
{
|
||||
if(lhs[i] != rhs[i]) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
CUTE_HOST_DEVICE
|
||||
void clear(array_view<T, N>& a)
|
||||
{
|
||||
a.fill(T(0));
|
||||
}
|
||||
|
||||
template<class T, std::size_t N>
|
||||
CUTE_HOST_DEVICE
|
||||
void swap(array_view<T,N>& a, array_view<T,N>& b)
|
||||
{
|
||||
a.swap(b);
|
||||
}
|
||||
|
||||
} // end cute
|
||||
|
||||
|
||||
//
|
||||
// Specialize tuple-related functionality for cute::array_view
|
||||
//
|
||||
|
||||
#include <tuple>
|
||||
|
||||
namespace cute
|
||||
{
|
||||
|
||||
template<std::size_t I, class T, std::size_t N>
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
T&
|
||||
get(array_view<T,N>& a)
|
||||
{
|
||||
static_assert(I < N, "Index out of range");
|
||||
return a[I];
|
||||
}
|
||||
|
||||
template<std::size_t I, class T, std::size_t N>
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
const T&
|
||||
get(const array_view<T,N>& a)
|
||||
{
|
||||
static_assert(I < N, "Index out of range");
|
||||
return a[I];
|
||||
}
|
||||
|
||||
template<std::size_t I, class T, std::size_t N>
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
T&&
|
||||
get(array_view<T,N>&& a)
|
||||
{
|
||||
static_assert(I < N, "Index out of range");
|
||||
return std::move(a[I]);
|
||||
}
|
||||
|
||||
} // end namespace cute
|
||||
|
||||
namespace std
|
||||
{
|
||||
|
||||
template<class T, std::size_t N>
|
||||
struct tuple_size<cute::array_view<T,N>>
|
||||
: std::integral_constant<std::size_t, N>
|
||||
{};
|
||||
|
||||
template<std::size_t I, class T, std::size_t N>
|
||||
struct tuple_element<I, cute::array_view<T,N>>
|
||||
{
|
||||
using type = T;
|
||||
};
|
||||
|
||||
} // end std
|
||||
@@ -0,0 +1,131 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2023 - 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
**************************************************************************************************/
|
||||
/*! \file
|
||||
\brief Portable bit field that supports byte and word straddling that can
|
||||
be used in unions to bit-wise define parameters.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cute/config.hpp>
|
||||
|
||||
#include <cute/numeric/int.hpp> // uint_bit_t
|
||||
|
||||
namespace cute
|
||||
{
|
||||
|
||||
class dummy_type {};
|
||||
|
||||
template <uint32_t BitStart, uint32_t NumBits, class OtherValueType = dummy_type>
|
||||
struct bit_field
|
||||
{
|
||||
static_assert(0 < NumBits && NumBits <= 64, "bit_fields with more than 64 bits are not supported.");
|
||||
|
||||
// value_type: Use the smallest value type that fits NumBits
|
||||
static constexpr uint32_t value_type_bits = (NumBits <= 8) ? 8 :
|
||||
(NumBits <= 16) ? 16 :
|
||||
(NumBits <= 32) ? 32 : 64;
|
||||
using value_type = cute::uint_bit_t<value_type_bits>;
|
||||
// storage_type: Use the smallest storage_type that avoids boundary crossing
|
||||
static constexpr uint32_t storage_type_bits = (BitStart / 8 == (BitStart + NumBits - 1) / 8) ? 8 :
|
||||
(BitStart / 16 == (BitStart + NumBits - 1) / 16) ? 16 :
|
||||
(BitStart / 32 == (BitStart + NumBits - 1) / 32) ? 32 : 64;
|
||||
using storage_type = cute::uint_bit_t<storage_type_bits>;
|
||||
|
||||
static_assert(sizeof(OtherValueType) == sizeof(value_type) || std::is_same<OtherValueType,dummy_type>::value,
|
||||
"sizeof(OtherValueType) must be same as sizeof(value_type).");
|
||||
|
||||
// Number of storage values needed: ceil_div(BitStart + NumBits, storage_type_bits)
|
||||
static constexpr uint32_t N = (BitStart + NumBits + storage_type_bits - 1) / storage_type_bits;
|
||||
// Index of storage value for BitStart
|
||||
static constexpr uint32_t idx = BitStart / storage_type_bits;
|
||||
// Bit of data_[idx] for BitStart
|
||||
static constexpr uint32_t bit_lo = BitStart % storage_type_bits;
|
||||
// Number of bits in data_[idx] used for NumBits if straddling, else 0
|
||||
static constexpr uint32_t bit_hi = (idx + 1 < N) ? (storage_type_bits - bit_lo) : 0;
|
||||
|
||||
// NumBits mask
|
||||
static constexpr value_type mask = (NumBits < 64) ? ((uint64_t(1) << NumBits) - 1) : uint64_t(-1);
|
||||
// NumBits mask for BitStart
|
||||
static constexpr storage_type mask_lo = storage_type(mask) << bit_lo;
|
||||
// NumBits mask for leftover bits in data_[idx+1] if straddling, else 0
|
||||
static constexpr storage_type mask_hi = (idx + 1 < N) ? (storage_type(mask) >> bit_hi) : 0;
|
||||
|
||||
storage_type data_[N];
|
||||
|
||||
// Get value
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
value_type get() const {
|
||||
storage_type result = (data_[idx] & mask_lo) >> bit_lo;
|
||||
if constexpr (bit_hi) {
|
||||
result |= (data_[idx+1] & mask_hi) << bit_hi;
|
||||
}
|
||||
return static_cast<value_type>(result);
|
||||
}
|
||||
|
||||
// Set value
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
void set(value_type x) {
|
||||
storage_type item = static_cast<storage_type>(x & mask);
|
||||
data_[idx] = static_cast<storage_type>((data_[idx] & ~mask_lo) | (item << bit_lo));
|
||||
if constexpr (bit_hi) {
|
||||
data_[idx+1] = static_cast<storage_type>((data_[idx+1] & ~mask_hi) | (item >> bit_hi));
|
||||
}
|
||||
}
|
||||
|
||||
// Assign value
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
bit_field& operator=(value_type x) {
|
||||
set(x);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Cast to value
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
operator value_type () const {
|
||||
return get();
|
||||
}
|
||||
|
||||
// Assign OtherValueType
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
bit_field& operator=(OtherValueType x) {
|
||||
return *this = *reinterpret_cast<value_type*>(&x);
|
||||
}
|
||||
|
||||
// Cast to OtherValueType
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
operator OtherValueType () const {
|
||||
value_type x = get();
|
||||
return *reinterpret_cast<OtherValueType*>(&x);
|
||||
}
|
||||
};
|
||||
|
||||
} // end namespace cute
|
||||
@@ -0,0 +1,671 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2023 - 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
**************************************************************************************************/
|
||||
#pragma once
|
||||
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
#include <cute/config.hpp>
|
||||
#include <cute/util/type_traits.hpp>
|
||||
|
||||
#include <cute/numeric/integral_constant.hpp> // cute::true_type, cute::false_type
|
||||
//#include <cute/container/array.hpp> // Advanced optimizations
|
||||
|
||||
#if 0
|
||||
//
|
||||
// Use of agency::tuple is functional, but is over-engineered for our purposes...
|
||||
// This tends to result in slow compilation times and unintentionally propagated cvref types
|
||||
//
|
||||
|
||||
#include <agency/tuple.hpp>
|
||||
|
||||
namespace cute
|
||||
{
|
||||
|
||||
using agency::tuple;
|
||||
|
||||
using agency::make_tuple;
|
||||
using agency::tuple_cat;
|
||||
|
||||
} // end namespace cute
|
||||
#endif
|
||||
|
||||
// cute::tuple is like std::tuple, with two differences.
|
||||
//
|
||||
// 1. It works on both host and device.
|
||||
// 2. Its template arguments must be semiregular types.
|
||||
//
|
||||
// Semiregular types are default constructible and copyable.
|
||||
// They include "value types" like int or float,
|
||||
// but do _not_ include references like int& or float&.
|
||||
// (See std::tie for an example of a tuple of references.)
|
||||
//
|
||||
// This is simplified over the implementation in std:: and agency:: by ignoring much of
|
||||
// the conversion SFINAE, special overloading, and avoiding cvref template types.
|
||||
// Furthermore, the empty base optimization (EBO) is MORE aggressive by avoiding
|
||||
// construction calls, and ignoring any need for unique element addresses.
|
||||
//
|
||||
// Over the agency::tuple implementation, this appears to accelerate compilation times by over 3x.
|
||||
|
||||
namespace cute
|
||||
{
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
// EBO stands for "empty base optimization."
|
||||
// We use this technique to ensure that cute::tuple
|
||||
// doesn't need to waste space storing any template arguments
|
||||
// of cute::tuple that have no data (like integral_constant).
|
||||
// Otherwise, cute::tuple would need to spend at least 1 byte
|
||||
// for each of its template arguments.
|
||||
//
|
||||
// EBO always "holds" a single value of type T.
|
||||
// N is like an array index that TupleBase uses
|
||||
// to access the desired tuple element.
|
||||
template <std::size_t N, class T, bool IsEmpty = std::is_empty<T>::value>
|
||||
struct EBO;
|
||||
|
||||
// Specialization for types T that have no data;
|
||||
// the "static tuple leaf." Valid T here include
|
||||
// integral_constant<U, Value>, Int<Value>,
|
||||
// and any other semiregular type
|
||||
// for which std::is_empty_v<T> is true.
|
||||
template <std::size_t N, class T>
|
||||
struct EBO<N, T, true>
|
||||
{
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
EBO() {}
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
EBO(T const&) {}
|
||||
};
|
||||
|
||||
template <std::size_t N, class T>
|
||||
CUTE_HOST_DEVICE constexpr T getv(EBO<N, T, true> const&)
|
||||
{ return {}; }
|
||||
|
||||
// Specialization for types T that are not empty;
|
||||
// the "dynamic tuple leaf." Valid T here include int,
|
||||
// any other integral or floating-point type,
|
||||
// or any semiregular type for which std::is_empty_v<T> is false.
|
||||
template <std::size_t N, class T>
|
||||
struct EBO<N, T, false>
|
||||
{
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
EBO() : t_{} {}
|
||||
|
||||
template <class U>
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
EBO(U const& u) : t_{u} {}
|
||||
|
||||
T t_;
|
||||
};
|
||||
|
||||
template <std::size_t N, class T>
|
||||
CUTE_HOST_DEVICE constexpr T const& getv(EBO<N, T, false> const& x)
|
||||
{ return x.t_; }
|
||||
|
||||
template <std::size_t N, class T>
|
||||
CUTE_HOST_DEVICE constexpr T& getv(EBO<N, T, false>& x)
|
||||
{ return x.t_; }
|
||||
|
||||
template <std::size_t N, class T>
|
||||
CUTE_HOST_DEVICE constexpr T&& getv(EBO<N, T, false>&& x)
|
||||
{ return static_cast<T&&>(x.t_); }
|
||||
|
||||
template <class IdxSeq, class... T>
|
||||
struct TupleBase;
|
||||
|
||||
// Base class of cute::tuple.
|
||||
// It inherits from EBO<i, t> for each (i, t) in (I..., T...).
|
||||
// The actual storage (for nonempty t) lives in the base classes.
|
||||
// index_sequence is a way to wrap up a sequence of zero or more
|
||||
// compile-time integer values in a single type.
|
||||
// We only ever use index_sequence<0, 1, ..., sizeof...(T)> in practice,
|
||||
// as the type alias TupleBase below indicates.
|
||||
template <std::size_t... I, class... T>
|
||||
struct TupleBase<std::index_sequence<I...>, T...>
|
||||
: EBO<I,T>...
|
||||
{
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
TupleBase() {}
|
||||
|
||||
template <class... U>
|
||||
CUTE_HOST_DEVICE constexpr explicit
|
||||
TupleBase(U const&... u)
|
||||
: EBO<I,T>(u)... {}
|
||||
|
||||
template <class... U>
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
TupleBase(TupleBase<std::index_sequence<I...>, U...> const& u)
|
||||
: EBO<I,T>(getv(static_cast<EBO<I,U> const&>(u)))... {}
|
||||
};
|
||||
|
||||
} // end namespace detail
|
||||
|
||||
// make_index_sequence<K> returns index_sequence<0, 1, ..., K-1>.
|
||||
template <class... T>
|
||||
using TupleBase = detail::TupleBase<std::make_index_sequence<sizeof...(T)>, T...>;
|
||||
|
||||
// This is the actual cute::tuple class.
|
||||
// The storage (if any) lives in TupleBase's EBO base classes.
|
||||
template <class... T>
|
||||
struct tuple : TupleBase<T...>
|
||||
{
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
tuple() {}
|
||||
|
||||
template <class... U>
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
tuple(U const&... u) : TupleBase<T...>(u...) {}
|
||||
|
||||
template <class... U>
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
tuple(tuple<U...> const& u)
|
||||
: TupleBase<T...>(static_cast<TupleBase<U...> const&>(u)) {}
|
||||
};
|
||||
|
||||
//
|
||||
// get for cute::tuple (just like std::get for std::tuple)
|
||||
//
|
||||
|
||||
template <std::size_t I, class... T>
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
decltype(auto)
|
||||
get(tuple<T...> const& t) noexcept
|
||||
{
|
||||
static_assert(I < sizeof...(T), "Index out of range");
|
||||
return detail::getv<I>(t);
|
||||
}
|
||||
|
||||
template <std::size_t I, class... T>
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
decltype(auto)
|
||||
get(tuple<T...>& t) noexcept
|
||||
{
|
||||
static_assert(I < sizeof...(T), "Index out of range");
|
||||
return detail::getv<I>(t);
|
||||
}
|
||||
|
||||
template <std::size_t I, class... T>
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
decltype(auto)
|
||||
get(tuple<T...>&& t) noexcept
|
||||
{
|
||||
static_assert(I < sizeof...(T), "Index out of range");
|
||||
return detail::getv<I>(static_cast<tuple<T...>&&>(t));
|
||||
}
|
||||
|
||||
//
|
||||
// Custom is_tuple trait simply checks the existence of std::tuple_size
|
||||
// and assumes std::get<I>(.), std::tuple_element<I,.>
|
||||
//
|
||||
namespace detail {
|
||||
|
||||
template <class T>
|
||||
std::integral_constant<bool, std::tuple_size<T>::value >= 0> has_tuple_size(int);
|
||||
|
||||
template <class T>
|
||||
std::false_type has_tuple_size(...);
|
||||
|
||||
} // end namespace detail
|
||||
|
||||
template <class T>
|
||||
struct is_tuple : decltype(detail::has_tuple_size<T>(0)) {};
|
||||
|
||||
//
|
||||
// make_tuple (value-based implementation)
|
||||
//
|
||||
|
||||
template <class... T>
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
tuple<T...>
|
||||
make_tuple(T const&... t)
|
||||
{
|
||||
return {t...};
|
||||
}
|
||||
|
||||
//
|
||||
// tuple_cat concatenates multiple cute::tuple into a single cute::tuple,
|
||||
// just like std::tuple_cat for std::tuple.
|
||||
//
|
||||
|
||||
#if 0
|
||||
// Original implementation
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <class T0, class T1,
|
||||
std::size_t... I0, std::size_t... I1>
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
auto
|
||||
tuple_cat(T0 const& t0, T1 const& t1,
|
||||
std::index_sequence<I0...>, std::index_sequence<I1...>)
|
||||
{
|
||||
return cute::make_tuple(get<I0>(t0)..., get<I1>(t1)...);
|
||||
}
|
||||
|
||||
} // end namespace detail
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
tuple<>
|
||||
tuple_cat()
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
template <class Tuple,
|
||||
__CUTE_REQUIRES(is_tuple<Tuple>::value)>
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
Tuple const&
|
||||
tuple_cat(Tuple const& t)
|
||||
{
|
||||
return t;
|
||||
}
|
||||
|
||||
template <class T0, class T1>
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
auto
|
||||
tuple_cat(T0 const& t0, T1 const& t1)
|
||||
{
|
||||
return detail::tuple_cat(t0, t1,
|
||||
std::make_index_sequence<std::tuple_size<T0>::value>{},
|
||||
std::make_index_sequence<std::tuple_size<T1>::value>{});
|
||||
}
|
||||
|
||||
template <class T0, class T1, class T2, class... Ts>
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
auto
|
||||
tuple_cat(T0 const& t0, T1 const& t1, T2 const& t2, Ts const&... ts)
|
||||
{
|
||||
return cute::tuple_cat(cute::tuple_cat(t0,t1),t2,ts...);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 1
|
||||
// Extended implementation
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <class T0, class T1,
|
||||
std::size_t... I0, std::size_t... I1>
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
auto
|
||||
tuple_cat(T0 const& t0, T1 const& t1,
|
||||
std::index_sequence<I0...>, std::index_sequence<I1...>)
|
||||
{
|
||||
return cute::make_tuple(get<I0>(t0)..., get<I1>(t1)...);
|
||||
}
|
||||
|
||||
template <class T0, class T1, class T2,
|
||||
std::size_t... I0, std::size_t... I1, std::size_t... I2>
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
auto
|
||||
tuple_cat(T0 const& t0, T1 const& t1, T2 const& t2,
|
||||
std::index_sequence<I0...>, std::index_sequence<I1...>, std::index_sequence<I2...>)
|
||||
{
|
||||
return cute::make_tuple(get<I0>(t0)..., get<I1>(t1)..., get<I2>(t2)...);
|
||||
}
|
||||
|
||||
template <class T0, class T1, class T2, class T3,
|
||||
std::size_t... I0, std::size_t... I1, std::size_t... I2, std::size_t... I3>
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
auto
|
||||
tuple_cat(T0 const& t0, T1 const& t1, T2 const& t2, T3 const& t3,
|
||||
std::index_sequence<I0...>, std::index_sequence<I1...>, std::index_sequence<I2...>, std::index_sequence<I3...>)
|
||||
{
|
||||
return cute::make_tuple(get<I0>(t0)..., get<I1>(t1)..., get<I2>(t2)..., get<I3>(t3)...);
|
||||
}
|
||||
|
||||
template <class T0, class T1, class T2, class T3, class T4,
|
||||
std::size_t... I0, std::size_t... I1, std::size_t... I2, std::size_t... I3, std::size_t... I4>
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
auto
|
||||
tuple_cat(T0 const& t0, T1 const& t1, T2 const& t2, T3 const& t3, T4 const& t4,
|
||||
std::index_sequence<I0...>, std::index_sequence<I1...>, std::index_sequence<I2...>, std::index_sequence<I3...>, std::index_sequence<I4...>)
|
||||
{
|
||||
return cute::make_tuple(get<I0>(t0)..., get<I1>(t1)..., get<I2>(t2)..., get<I3>(t3)..., get<I4>(t4)...);
|
||||
}
|
||||
|
||||
} // end namespace detail
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
tuple<>
|
||||
tuple_cat()
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
template <class Tuple,
|
||||
__CUTE_REQUIRES(is_tuple<Tuple>::value)>
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
Tuple const&
|
||||
tuple_cat(Tuple const& t)
|
||||
{
|
||||
return t;
|
||||
}
|
||||
|
||||
template <class T0, class T1>
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
auto
|
||||
tuple_cat(T0 const& t0, T1 const& t1)
|
||||
{
|
||||
return detail::tuple_cat(t0, t1,
|
||||
std::make_index_sequence<std::tuple_size<T0>::value>{},
|
||||
std::make_index_sequence<std::tuple_size<T1>::value>{});
|
||||
}
|
||||
|
||||
template <class T0, class T1, class T2>
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
auto
|
||||
tuple_cat(T0 const& t0, T1 const& t1, T2 const& t2)
|
||||
{
|
||||
return detail::tuple_cat(t0, t1, t2,
|
||||
std::make_index_sequence<std::tuple_size<T0>::value>{},
|
||||
std::make_index_sequence<std::tuple_size<T1>::value>{},
|
||||
std::make_index_sequence<std::tuple_size<T2>::value>{});
|
||||
}
|
||||
|
||||
template <class T0, class T1, class T2, class T3>
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
auto
|
||||
tuple_cat(T0 const& t0, T1 const& t1, T2 const& t2, T3 const& t3)
|
||||
{
|
||||
return detail::tuple_cat(t0, t1, t2, t3,
|
||||
std::make_index_sequence<std::tuple_size<T0>::value>{},
|
||||
std::make_index_sequence<std::tuple_size<T1>::value>{},
|
||||
std::make_index_sequence<std::tuple_size<T2>::value>{},
|
||||
std::make_index_sequence<std::tuple_size<T3>::value>{});
|
||||
}
|
||||
|
||||
template <class T0, class T1, class T2, class T3, class T4>
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
auto
|
||||
tuple_cat(T0 const& t0, T1 const& t1, T2 const& t2, T3 const& t3, T4 const& t4)
|
||||
{
|
||||
return detail::tuple_cat(t0, t1, t2, t3, t4,
|
||||
std::make_index_sequence<std::tuple_size<T0>::value>{},
|
||||
std::make_index_sequence<std::tuple_size<T1>::value>{},
|
||||
std::make_index_sequence<std::tuple_size<T2>::value>{},
|
||||
std::make_index_sequence<std::tuple_size<T3>::value>{},
|
||||
std::make_index_sequence<std::tuple_size<T4>::value>{});
|
||||
}
|
||||
|
||||
template <class T0, class T1, class T2, class T3, class T4, class T5, class... Ts>
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
auto
|
||||
tuple_cat(T0 const& t0, T1 const& t1, T2 const& t2, T3 const& t3, T4 const& t4, T5 const& t5, Ts const&... ts)
|
||||
{
|
||||
return cute::tuple_cat(cute::tuple_cat(t0,t1,t2,t3,t4), t5, ts...);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
// Outer-Inner indexing trick to concat all tuples at once
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <std::size_t... Ns>
|
||||
struct tuple_cat_helper
|
||||
{
|
||||
static constexpr cute::array<std::size_t,sizeof...(Ns)> ns = {Ns...};
|
||||
|
||||
static constexpr std::size_t total_size() {
|
||||
std::size_t sum = 0;
|
||||
for (std::size_t n : ns) sum += n;
|
||||
return sum;
|
||||
}
|
||||
static constexpr std::size_t total_size_ = total_size();
|
||||
|
||||
static constexpr auto values() {
|
||||
cute::array<std::size_t[2],total_size_> outer_inner = {};
|
||||
|
||||
std::size_t idx = 0;
|
||||
for (std::size_t i = 0; i < ns.size(); ++i) {
|
||||
for (std::size_t j = 0; j < ns[i]; ++j, ++idx) {
|
||||
outer_inner[idx][0] = i;
|
||||
outer_inner[idx][1] = j;
|
||||
}
|
||||
}
|
||||
return outer_inner;
|
||||
}
|
||||
static constexpr auto outer_inner_ = values();
|
||||
|
||||
using total_sequence = std::make_index_sequence<total_size_>;
|
||||
};
|
||||
|
||||
template <class Helper, class Tuple, std::size_t... I>
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
auto
|
||||
tuple_cat(Tuple const& t, std::index_sequence<I...>)
|
||||
{
|
||||
return cute::make_tuple(get<Helper::outer_inner_[I][1]>(get<Helper::outer_inner_[I][0]>(t))...);
|
||||
}
|
||||
|
||||
template <class T0, class T1,
|
||||
std::size_t... I0, std::size_t... I1>
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
auto
|
||||
tuple_cat(T0 const& t0, T1 const& t1,
|
||||
std::index_sequence<I0...>, std::index_sequence<I1...>)
|
||||
{
|
||||
return cute::make_tuple(get<I0>(t0)..., get<I1>(t1)...);
|
||||
}
|
||||
|
||||
} // end namespace detail
|
||||
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
tuple<>
|
||||
tuple_cat()
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
template <class Tuple,
|
||||
__CUTE_REQUIRES(is_tuple<Tuple>::value)>
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
Tuple const&
|
||||
tuple_cat(Tuple const& t)
|
||||
{
|
||||
return t;
|
||||
}
|
||||
|
||||
template <class T0, class T1>
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
auto
|
||||
tuple_cat(T0 const& t0, T1 const& t1)
|
||||
{
|
||||
return detail::tuple_cat(t0, t1,
|
||||
std::make_index_sequence<std::tuple_size<T0>::value>{},
|
||||
std::make_index_sequence<std::tuple_size<T1>::value>{});
|
||||
}
|
||||
|
||||
template <class... Tuples>
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
auto
|
||||
tuple_cat(Tuples const&... ts)
|
||||
{
|
||||
using Helper = detail::tuple_cat_helper<std::tuple_size<Tuples>::value...>;
|
||||
return detail::tuple_cat<Helper>(make_tuple(ts...), typename Helper::total_sequence{});
|
||||
}
|
||||
#endif
|
||||
|
||||
//
|
||||
// Equality operators
|
||||
//
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <std::size_t I, class TupleA, class TupleB>
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
auto
|
||||
equal_impl(TupleA const& a, TupleB const& b)
|
||||
{
|
||||
if constexpr (I == std::tuple_size<TupleA>::value) {
|
||||
return cute::true_type{}; // Terminal: TupleA is exhausted
|
||||
} else if constexpr (I == std::tuple_size<TupleB>::value) {
|
||||
return cute::false_type{}; // Terminal: TupleA is not exhausted, TupleB is exhausted
|
||||
} else {
|
||||
return (get<I>(a) == get<I>(b)) && equal_impl<I+1>(a,b);
|
||||
}
|
||||
|
||||
CUTE_GCC_UNREACHABLE;
|
||||
}
|
||||
|
||||
} // end namespace detail
|
||||
|
||||
template <class TupleT, class TupleU,
|
||||
__CUTE_REQUIRES(is_tuple<TupleT>::value && is_tuple<TupleU>::value)>
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
auto
|
||||
operator==(TupleT const& t, TupleU const& u)
|
||||
{
|
||||
return detail::equal_impl<0>(t, u);
|
||||
}
|
||||
|
||||
template <class TupleT, class TupleU,
|
||||
__CUTE_REQUIRES(is_tuple<TupleT>::value ^ is_tuple<TupleU>::value)>
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
auto
|
||||
operator==(TupleT const& t, TupleU const& u)
|
||||
{
|
||||
return cute::false_type{};
|
||||
}
|
||||
|
||||
template <class TupleT, class TupleU,
|
||||
__CUTE_REQUIRES(is_tuple<TupleT>::value && is_tuple<TupleU>::value)>
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
auto
|
||||
operator!=(TupleT const& t, TupleU const& u)
|
||||
{
|
||||
return !(t == u);
|
||||
}
|
||||
|
||||
template <class TupleT, class TupleU,
|
||||
__CUTE_REQUIRES(is_tuple<TupleT>::value ^ is_tuple<TupleU>::value)>
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
auto
|
||||
operator!=(TupleT const& t, TupleU const& u)
|
||||
{
|
||||
return cute::true_type{};
|
||||
}
|
||||
|
||||
//
|
||||
// Comparison operators
|
||||
//
|
||||
|
||||
//
|
||||
// There are many ways to compare tuple of elements and because CuTe is built
|
||||
// on parameterizing layouts of coordinates, some comparisons are appropriate
|
||||
// only in certain cases.
|
||||
// -- lexicographical comparison [reverse, reflected, revref]
|
||||
// -- colexicographical comparison [reverse, reflected, revref]
|
||||
// -- element-wise comparison [any,all]
|
||||
// This can be very confusing. To avoid errors in selecting the appropriate
|
||||
// comparison, op<|op<=|op>|op>= are *not* implemented for cute::tuple.
|
||||
//
|
||||
// That said, see int_tuple for more explicitly named common comparison ops.
|
||||
//
|
||||
|
||||
//
|
||||
// Shortcuts
|
||||
//
|
||||
|
||||
//using std::get;
|
||||
using std::tuple_size;
|
||||
using std::tuple_element;
|
||||
using std::tuple_element_t;
|
||||
|
||||
//
|
||||
// Display utilities
|
||||
//
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <class Tuple, std::size_t... Is>
|
||||
CUTE_HOST_DEVICE void print_tuple(Tuple const& t,
|
||||
std::index_sequence<Is...>, char s = '(', char e = ')')
|
||||
{
|
||||
using eat = int[];
|
||||
using cute::print;
|
||||
(void) eat {(print(s), 0),
|
||||
(print(Is == 0 ? "" : ","), print(get<Is>(t)), 0)...,
|
||||
(print(e), 0)};
|
||||
}
|
||||
|
||||
template <class Tuple, std::size_t... Is>
|
||||
CUTE_HOST std::ostream& print_tuple_os(std::ostream& os, Tuple const& t,
|
||||
std::index_sequence<Is...>, char s = '(', char e = ')')
|
||||
{
|
||||
using eat = int[];
|
||||
(void) eat {(void(os << s), 0),
|
||||
(void(os << (Is == 0 ? "" : ",") << get<Is>(t)), 0)...,
|
||||
(void(os << e), 0)};
|
||||
return os;
|
||||
}
|
||||
|
||||
} // end namespace detail
|
||||
|
||||
template <class Tuple,
|
||||
__CUTE_REQUIRES(is_tuple<Tuple>::value)>
|
||||
CUTE_HOST_DEVICE void print(Tuple const& t)
|
||||
{
|
||||
return detail::print_tuple(t, std::make_index_sequence<std::tuple_size<Tuple>::value>{});
|
||||
}
|
||||
|
||||
template <class Tuple,
|
||||
__CUTE_REQUIRES(is_tuple<Tuple>::value)>
|
||||
CUTE_HOST std::ostream& operator<<(std::ostream& os, Tuple const& t)
|
||||
{
|
||||
return detail::print_tuple_os(os, t, std::make_index_sequence<std::tuple_size<Tuple>::value>{});
|
||||
}
|
||||
|
||||
} // end namespace cute
|
||||
|
||||
//
|
||||
// std:: compatability
|
||||
//
|
||||
|
||||
namespace std
|
||||
{
|
||||
|
||||
template <class... T>
|
||||
struct tuple_size<cute::tuple<T...>>
|
||||
: std::integral_constant<std::size_t, sizeof...(T)>
|
||||
{};
|
||||
|
||||
template <std::size_t I, class... T>
|
||||
struct tuple_element<I, cute::tuple<T...>>
|
||||
: std::tuple_element<I, std::tuple<T...>>
|
||||
{};
|
||||
|
||||
} // end std
|
||||
@@ -0,0 +1,84 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2023 - 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
**************************************************************************************************/
|
||||
#pragma once
|
||||
|
||||
namespace cute
|
||||
{
|
||||
|
||||
template <class T>
|
||||
struct type_c {
|
||||
using type = T;
|
||||
};
|
||||
|
||||
template <class... T>
|
||||
struct type_list {};
|
||||
|
||||
} // end namespace cute
|
||||
|
||||
//
|
||||
// Specialize tuple-related functionality for cute::type_list
|
||||
//
|
||||
|
||||
#include <tuple>
|
||||
#include <cute/container/tuple.hpp>
|
||||
|
||||
namespace cute
|
||||
{
|
||||
|
||||
template <int I, class... T>
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
std::tuple_element_t<I, type_list<T...>>
|
||||
get(type_list<T...>&) noexcept {
|
||||
return {};
|
||||
}
|
||||
template <int I, class... T>
|
||||
CUTE_HOST_DEVICE constexpr
|
||||
std::tuple_element_t<I, type_list<T...>>
|
||||
get(type_list<T...> const& t) noexcept {
|
||||
return {};
|
||||
}
|
||||
|
||||
} // end namespace cute
|
||||
|
||||
namespace std
|
||||
{
|
||||
|
||||
template <class... T>
|
||||
struct tuple_size<cute::type_list<T...>>
|
||||
: std::integral_constant<std::size_t, sizeof...(T)>
|
||||
{};
|
||||
|
||||
template <std::size_t I, class... T>
|
||||
struct tuple_element<I, cute::type_list<T...>>
|
||||
: cute::type_c<typename std::tuple_element<I, std::tuple<T...>>::type>
|
||||
{};
|
||||
|
||||
} // end namespace std
|
||||
Reference in New Issue
Block a user