| // SPDX-FileCopyrightText: The Eigen Authors | |
| // SPDX-License-Identifier: MPL-2.0 | |
| #ifndef EIGEN_TEST_PACKETMATH_DATA_MOVEMENT_H | |
| #define EIGEN_TEST_PACKETMATH_DATA_MOVEMENT_H | |
| #include "packetmath_test_shared.h" | |
| namespace Eigen { | |
| namespace test { | |
| template <typename Scalar> | |
| using Buffer = Array<Scalar, Dynamic, 1>; | |
| // Loads and stores with their own addressing. `offset` misaligns the unaligned forms. | |
| template <typename Packet> | |
| struct ploadu_kernel { | |
| using Scalar = typename packet_layout<Packet>::Scalar; | |
| static constexpr int kSize = packet_layout<Packet>::kSize; | |
| int offset; | |
| EIGEN_PACKET_TEST_FUNC void operator()(int i, const Scalar* in, Scalar* out) const { | |
| #if EIGEN_PACKET_TEST_KERNEL_BODY | |
| internal::pstore(out + i * kSize, internal::ploadu<Packet>(in + i * kSize + offset)); | |
| #endif | |
| } | |
| }; | |
| template <typename Packet> | |
| struct pstoreu_kernel { | |
| using Scalar = typename packet_layout<Packet>::Scalar; | |
| static constexpr int kSize = packet_layout<Packet>::kSize; | |
| int offset; | |
| EIGEN_PACKET_TEST_FUNC void operator()(int i, const Scalar* in, Scalar* out) const { | |
| #if EIGEN_PACKET_TEST_KERNEL_BODY | |
| internal::pstoreu(out + i * kSize + offset, internal::pload<Packet>(in + i * kSize)); | |
| #endif | |
| } | |
| }; | |
| template <typename Packet, int Alignment> | |
| struct ploadt_ro_kernel { | |
| using Scalar = typename packet_layout<Packet>::Scalar; | |
| static constexpr int kSize = packet_layout<Packet>::kSize; | |
| int offset; | |
| EIGEN_PACKET_TEST_FUNC void operator()(int i, const Scalar* in, Scalar* out) const { | |
| #if EIGEN_PACKET_TEST_KERNEL_BODY | |
| internal::pstore(out + i * kSize, internal::ploadt_ro<Packet, Alignment>(in + i * kSize + offset)); | |
| #endif | |
| } | |
| }; | |
| template <typename Packet> | |
| struct ploaddup_kernel { | |
| using Scalar = typename packet_layout<Packet>::Scalar; | |
| static constexpr int kSize = packet_layout<Packet>::kSize; | |
| int offset; | |
| EIGEN_PACKET_TEST_FUNC void operator()(int i, const Scalar* in, Scalar* out) const { | |
| #if EIGEN_PACKET_TEST_KERNEL_BODY | |
| internal::pstore(out + i * kSize, internal::ploaddup<Packet>(in + i * (kSize / 2) + offset)); | |
| #endif | |
| } | |
| }; | |
| template <typename Packet> | |
| struct pset1_kernel { | |
| using Scalar = typename packet_layout<Packet>::Scalar; | |
| static constexpr int kSize = packet_layout<Packet>::kSize; | |
| EIGEN_PACKET_TEST_FUNC void operator()(int i, const Scalar* in, Scalar* out) const { | |
| #if EIGEN_PACKET_TEST_KERNEL_BODY | |
| internal::pstore(out + i * kSize, internal::pset1<Packet>(in[i])); | |
| #endif | |
| } | |
| }; | |
| template <typename Packet> | |
| struct pgather_kernel { | |
| using Scalar = typename packet_layout<Packet>::Scalar; | |
| static constexpr int kSize = packet_layout<Packet>::kSize; | |
| int stride; | |
| EIGEN_PACKET_TEST_FUNC void operator()(int i, const Scalar* in, Scalar* out) const { | |
| #if EIGEN_PACKET_TEST_KERNEL_BODY | |
| internal::pstore(out + i * kSize, internal::pgather<Scalar, Packet>(in + i * kSize * stride, stride)); | |
| #endif | |
| } | |
| }; | |
| template <typename Packet> | |
| struct pscatter_kernel { | |
| using Scalar = typename packet_layout<Packet>::Scalar; | |
| static constexpr int kSize = packet_layout<Packet>::kSize; | |
| int stride; | |
| EIGEN_PACKET_TEST_FUNC void operator()(int i, const Scalar* in, Scalar* out) const { | |
| #if EIGEN_PACKET_TEST_KERNEL_BODY | |
| internal::pscatter<Scalar, Packet>(out + i * kSize * stride, internal::pload<Packet>(in + i * kSize), stride); | |
| #endif | |
| } | |
| }; | |
| template <typename Packet> | |
| struct ptranspose_kernel { | |
| using Scalar = typename packet_layout<Packet>::Scalar; | |
| static constexpr int kSize = packet_layout<Packet>::kSize; | |
| EIGEN_PACKET_TEST_FUNC void operator()(int i, const Scalar* in, Scalar* out) const { | |
| #if EIGEN_PACKET_TEST_KERNEL_BODY | |
| internal::PacketBlock<Packet, kSize> block; | |
| for (int r = 0; r < kSize; ++r) block.packet[r] = internal::pload<Packet>(in + (i * kSize + r) * kSize); | |
| internal::ptranspose(block); | |
| for (int r = 0; r < kSize; ++r) internal::pstore(out + (i * kSize + r) * kSize, block.packet[r]); | |
| #endif | |
| } | |
| }; | |
| // Thread i copies its first (i mod (kSize + 1)) lanes; the rest of the output keeps its sentinel. | |
| template <typename Packet> | |
| struct partial_kernel { | |
| using Scalar = typename packet_layout<Packet>::Scalar; | |
| static constexpr int kSize = packet_layout<Packet>::kSize; | |
| EIGEN_PACKET_TEST_FUNC void operator()(int i, const Scalar* in, Scalar* out) const { | |
| #if EIGEN_PACKET_TEST_KERNEL_BODY | |
| const Index n = i % (kSize + 1); | |
| internal::pstore_partial(out + i * kSize, internal::pload_partial<Packet>(in + i * kSize, n), n); | |
| #endif | |
| } | |
| }; | |
| template <typename Packet> | |
| struct preverse_kernel { | |
| using Scalar = typename packet_layout<Packet>::Scalar; | |
| static constexpr int kSize = packet_layout<Packet>::kSize; | |
| EIGEN_PACKET_TEST_FUNC void operator()(int i, const Scalar* in, Scalar* out) const { | |
| #if EIGEN_PACKET_TEST_KERNEL_BODY | |
| internal::pstore(out + i * kSize, internal::preverse(internal::pload<Packet>(in + i * kSize))); | |
| #endif | |
| } | |
| }; | |
| // The input owns aligned storage and contains at least max(4, packet size) complete packets. | |
| // run(kernel, count, input, output) executes one kernel invocation per packet (or transpose block). | |
| // With `nan_payloads` false, any NaN matches any NaN: the GPU half packets quiet a signalling NaN in transit. | |
| template <typename Packet, typename Run> | |
| void packetmath_data_movement(const Buffer<typename packet_layout<Packet>::Scalar>& in, const Run& run, | |
| bool nan_payloads = true) { | |
| using Scalar = typename packet_layout<Packet>::Scalar; | |
| constexpr int kSize = packet_layout<Packet>::kSize; | |
| const bool nan_is_nan = !nan_payloads; | |
| VERIFY(in.size() >= kSize * (kSize > 4 ? kSize : 4) && in.size() % kSize == 0); | |
| const int n = int(in.size()) / kSize; | |
| { | |
| Buffer<Scalar> out(in.size()), padded(in.size() + kSize), out_padded(in.size() + kSize), | |
| expected_padded(in.size() + kSize); | |
| padded << in, Buffer<Scalar>::Constant(kSize, Scalar(1)); | |
| for (int offset = 0; offset < kSize; ++offset) { | |
| out.setConstant(Scalar(-7)); | |
| run(ploadu_kernel<Packet>{offset}, n, padded, out); | |
| VERIFY(areEqualBits(padded.data() + offset, out.data(), int(in.size()), nan_is_nan) && "ploadu"); | |
| run(ploadt_ro_kernel<Packet, Unaligned>{offset}, n, padded, out); | |
| VERIFY(areEqualBits(padded.data() + offset, out.data(), int(in.size()), nan_is_nan) && "ploadt_ro<Unaligned>"); | |
| out_padded.setConstant(Scalar(-7)); | |
| expected_padded.setConstant(Scalar(-7)); | |
| expected_padded.segment(offset, in.size()) = in; | |
| run(pstoreu_kernel<Packet>{offset}, n, in, out_padded); | |
| VERIFY(areEqualBits(expected_padded.data(), out_padded.data(), int(out_padded.size()), nan_is_nan) && "pstoreu"); | |
| } | |
| } | |
| { | |
| Buffer<Scalar> out(in.size()); | |
| out.setConstant(Scalar(-7)); | |
| run(ploadt_ro_kernel<Packet, Aligned>{0}, n, in, out); | |
| VERIFY(areEqualBits(in.data(), out.data(), int(in.size()), nan_is_nan) && "ploadt_ro<Aligned>"); | |
| Buffer<Scalar> expected(in.size()); | |
| if (kSize > 1) { | |
| for (int offset = 0; offset < 4; ++offset) { | |
| run(ploaddup_kernel<Packet>{offset}, n, in, out); | |
| for (Index k = 0; k < in.size(); ++k) expected[k] = in[(k / kSize) * (kSize / 2) + (k % kSize) / 2 + offset]; | |
| VERIFY(areEqualBits(expected.data(), out.data(), int(in.size()), nan_is_nan) && "ploaddup"); | |
| } | |
| } | |
| run(pset1_kernel<Packet>(), n, in, out); | |
| for (Index k = 0; k < in.size(); ++k) expected[k] = in[k / kSize]; | |
| VERIFY(areEqualBits(expected.data(), out.data(), int(in.size()), nan_is_nan) && "pset1"); | |
| out.setConstant(Scalar(-7)); | |
| run(partial_kernel<Packet>(), n, in, out); | |
| for (Index k = 0; k < in.size(); ++k) { | |
| const Index lanes = (k / kSize) % (kSize + 1); | |
| expected[k] = (k % kSize) < lanes ? in[k] : Scalar(-7); | |
| } | |
| VERIFY(areEqualBits(expected.data(), out.data(), int(in.size()), nan_is_nan) && "pload_partial/pstore_partial"); | |
| } | |
| { | |
| const int stride = 3; | |
| Buffer<Scalar> strided(in.size() * stride); | |
| strided.setConstant(Scalar(-7)); | |
| for (Index k = 0; k < in.size(); ++k) strided[k * stride] = in[k]; | |
| Buffer<Scalar> out(in.size()); | |
| out.setConstant(Scalar(-7)); | |
| run(pgather_kernel<Packet>{stride}, n, strided, out); | |
| VERIFY(areEqualBits(in.data(), out.data(), int(in.size()), nan_is_nan) && "pgather"); | |
| Buffer<Scalar> scattered(in.size() * stride); | |
| scattered.setConstant(Scalar(-7)); | |
| run(pscatter_kernel<Packet>{stride}, n, in, scattered); | |
| VERIFY(areEqualBits(strided.data(), scattered.data(), int(strided.size()), nan_is_nan) && "pscatter"); | |
| } | |
| { | |
| // A kSize x kSize block per thread. | |
| const int blocks = n / kSize; | |
| const int block_elements = kSize * kSize; | |
| Buffer<Scalar> out(blocks * block_elements); | |
| out.setConstant(Scalar(-7)); | |
| run(ptranspose_kernel<Packet>(), blocks, in, out); | |
| Buffer<Scalar> expected(blocks * block_elements); | |
| for (int block = 0; block < blocks; ++block) { | |
| for (int r = 0; r < kSize; ++r) { | |
| for (int c = 0; c < kSize; ++c) { | |
| expected[block * block_elements + r * kSize + c] = in[block * block_elements + c * kSize + r]; | |
| } | |
| } | |
| } | |
| VERIFY(areEqualBits(expected.data(), out.data(), int(out.size()), nan_is_nan) && "ptranspose"); | |
| } | |
| { | |
| Buffer<Scalar> out(in.size()); | |
| out.setConstant(Scalar(-7)); | |
| run(preverse_kernel<Packet>(), n, in, out); | |
| Buffer<Scalar> expected(in.size()); | |
| for (Index k = 0; k < in.size(); ++k) expected[k] = in[(k / kSize) * kSize + (kSize - 1 - k % kSize)]; | |
| VERIFY(areEqualBits(expected.data(), out.data(), int(in.size()), nan_is_nan) && "preverse"); | |
| } | |
| } | |
| } // namespace test | |
| } // namespace Eigen | |
| #endif // EIGEN_TEST_PACKETMATH_DATA_MOVEMENT_H |