blob: 9abc13d7fd12c404cb46a43185e087b60409e3da [file]
// 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