blob: aef9936877fa025067317a96b1d72c732939767f [file]
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// SPDX-FileCopyrightText: The Eigen Authors
// SPDX-License-Identifier: MPL-2.0
// SVE packet types are fixed-length vectors of exactly EIGEN_ARM64_SVE_VL bits. Running such a
// binary where the hardware vector length differs makes every packet type a lie and lets the
// rest of the suite pass vacuously. qemu is the common way in: above 512 bits it keeps the
// default 512-bit task vector length, so `-cpu max,sve2048=on` alone runs at 512 unless
// `sve-default-vector-length=<bytes>` is also given.
// The backend raises Eigen's own default to fit its blocked kernel panels, but an explicit limit
// is the caller's policy and must survive. 1 is below the requirement at every vector length, so
// the assertion is never vacuous, and nothing in this file allocates a fixed-size Eigen object.
#define EIGEN_STACK_ALLOCATION_LIMIT 1
#include "main.h"
static_assert(EIGEN_STACK_ALLOCATION_LIMIT == 1, "SVE must preserve an explicit stack allocation limit");
// Registered only under EIGEN_TEST_SVE (test/CMakeLists.txt). Without an SVE -march this would
// compile against NEON and trivially pass.
#if !defined(EIGEN_VECTORIZE_SVE)
#error \
"sve_vector_length requires the SVE backend. Build with an SVE -march (e.g. " \
"-march=armv8.2-a+sve) and -DEIGEN_TEST_SVE=ON, which supplies -DEIGEN_ARM64_USE_SVE and " \
"-msve-vector-bits."
#endif
// Must stay inline asm: under -msve-vector-bits the compiler folds svcntb() to the compile-time
// constant, making the comparison below a tautology that holds on a mismatched machine.
static EIGEN_DONT_INLINE int runtime_vector_length_bits() {
numext::uint64_t vl_bytes;
asm("rdvl %0, #1" : "=r"(vl_bytes));
return int(vl_bytes) * CHAR_BIT;
}
void check_runtime_vector_length() { VERIFY_IS_EQUAL(runtime_vector_length_bits(), int(EIGEN_ARM64_SVE_VL)); }
// Cross-checked against RDVL rather than svcntw()/svcntd(), which fold as above.
void check_packet_sizes() {
const int vl = runtime_vector_length_bits();
VERIFY_IS_EQUAL(int(internal::packet_traits<numext::int32_t>::size), vl / (8 * int(sizeof(numext::int32_t))));
VERIFY_IS_EQUAL(int(internal::packet_traits<float>::size), vl / (8 * int(sizeof(float))));
VERIFY_IS_EQUAL(int(internal::packet_traits<double>::size), vl / (8 * int(sizeof(double))));
}
// The opaque barriers keep the store/reload from being constant-folded away; on a narrower
// machine the tail of the buffer then keeps its sentinel.
template <typename Scalar, typename Packet>
void check_full_width_store() {
constexpr int PacketSize = internal::unpacket_traits<Packet>::size;
EIGEN_ALIGN_MAX Scalar buffer[PacketSize];
Scalar value = Scalar(1);
Scalar* p = buffer;
asm volatile("" : "+r"(p) : : "memory");
for (int i = 0; i < PacketSize; ++i) p[i] = Scalar(-12345);
asm volatile("" : "+r"(p), "+r"(value) : : "memory");
internal::pstore(p, internal::pset1<Packet>(value));
asm volatile("" : "+r"(p) : : "memory");
for (int i = 0; i < PacketSize; ++i) {
VERIFY_IS_EQUAL(p[i], Scalar(1));
}
}
EIGEN_DECLARE_TEST(sve_vector_length) {
CALL_SUBTEST(check_runtime_vector_length());
CALL_SUBTEST(check_packet_sizes());
CALL_SUBTEST((check_full_width_store<numext::int32_t, internal::PacketXi>()));
CALL_SUBTEST((check_full_width_store<float, internal::PacketXf>()));
CALL_SUBTEST((check_full_width_store<double, internal::PacketXd>()));
}