| // 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>())); |
| } |