| // 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 | |
| #include "main.h" | |
| #include "../blas.h" | |
| static_assert(sizeof(EIGEN_BLAS_INT) == 8, "EIGEN_BLAS_INT must be 64-bit in blas_ilp64"); | |
| static_assert(sizeof(Eigen::BlasIndex) == 8, "Eigen::BlasIndex must be 64-bit in blas_ilp64"); | |
| namespace { | |
| EIGEN_BLAS_INT g_last_xerbla_info = 0; | |
| } // namespace | |
| extern "C" void BLASFUNC(xerbla)(const char* /*msg*/, EIGEN_BLAS_INT* info, size_t /*len*/) { | |
| g_last_xerbla_info = *info; | |
| } | |
| namespace { | |
| // Low 32 bits equal 1, full 64-bit value is negative (-4294967295). | |
| // A routine reading 32-bit int* sees 1 and executes; a 64-bit routine rejects it via xerbla_. | |
| constexpr EIGEN_BLAS_INT kNegativeWithPositiveLowWord = | |
| static_cast<EIGEN_BLAS_INT>(std::uint64_t(1) | (std::uint64_t(0xFFFFFFFF) << 32)); | |
| // A Windows DLL binds its own xerbla_ at link time, so the definition above never sees the | |
| // library's calls there; c == -999 still shows that the argument was rejected. | |
| #if defined(_WIN32) || defined(__CYGWIN__) | |
| constexpr bool kXerblaOverridable = false; | |
| #else | |
| constexpr bool kXerblaOverridable = true; | |
| #endif | |
| void test_ilp64_upper_word_sensitivity() { | |
| EIGEN_BLAS_INT one = 1; | |
| EIGEN_BLAS_INT bad = kNegativeWithPositiveLowWord; | |
| double alpha = 1.0; | |
| double beta = 0.0; | |
| double a = 3.0; | |
| double b = 5.0; | |
| double c = -999.0; | |
| g_last_xerbla_info = 0; | |
| BLASFUNC(dgemm)("N", "N", &bad, &one, &one, &alpha, &a, &one, &b, &one, &beta, &c, &one); | |
| if (kXerblaOverridable) VERIFY_IS_EQUAL(g_last_xerbla_info, EIGEN_BLAS_INT(3)); | |
| VERIFY_IS_EQUAL(c, -999.0); | |
| g_last_xerbla_info = 0; | |
| BLASFUNC(dgemv)("N", &bad, &one, &alpha, &a, &one, &b, &one, &beta, &c, &one); | |
| if (kXerblaOverridable) VERIFY_IS_EQUAL(g_last_xerbla_info, EIGEN_BLAS_INT(2)); | |
| VERIFY_IS_EQUAL(c, -999.0); | |
| g_last_xerbla_info = 0; | |
| char uplo = 'U'; | |
| BLASFUNC(dsymv)(&uplo, &bad, &alpha, &a, &one, &b, &one, &beta, &c, &one); | |
| if (kXerblaOverridable) VERIFY_IS_EQUAL(g_last_xerbla_info, EIGEN_BLAS_INT(2)); | |
| VERIFY_IS_EQUAL(c, -999.0); | |
| g_last_xerbla_info = 0; | |
| BLASFUNC(dsyrk)(&uplo, "N", &bad, &one, &alpha, &a, &one, &beta, &c, &one); | |
| if (kXerblaOverridable) VERIFY_IS_EQUAL(g_last_xerbla_info, EIGEN_BLAS_INT(3)); | |
| VERIFY_IS_EQUAL(c, -999.0); | |
| } | |
| void test_ilp64_level1() { | |
| EIGEN_BLAS_INT n = 5; | |
| EIGEN_BLAS_INT inc_pos = 1; | |
| EIGEN_BLAS_INT inc_neg = -1; | |
| EIGEN_BLAS_INT inc_zero = 0; | |
| double x[5] = {1.0, -4.0, 2.0, 9.0, -3.0}; | |
| double y[5] = {10.0, 20.0, 30.0, 40.0, 50.0}; | |
| VERIFY_IS_EQUAL(BLASFUNC(idamax)(&n, x, &inc_pos), EIGEN_BLAS_INT(4)); | |
| VERIFY_IS_EQUAL(BLASFUNC(idamin)(&n, x, &inc_pos), EIGEN_BLAS_INT(1)); | |
| // Reverse stride (-1 has 0xFFFFFFFF in the upper 32 bits). | |
| double d = BLASFUNC(ddot)(&n, x, &inc_neg, y, &inc_pos); | |
| double expected_d = x[4] * y[0] + x[3] * y[1] + x[2] * y[2] + x[1] * y[3] + x[0] * y[4]; | |
| VERIFY_IS_APPROX(d, expected_d); | |
| // Broadcast copy (incx == 0). | |
| double src = 7.5; | |
| double dst[5] = {0.0, 0.0, 0.0, 0.0, 0.0}; | |
| BLASFUNC(dcopy)(&n, &src, &inc_zero, dst, &inc_pos); | |
| for (EIGEN_BLAS_INT i = 0; i < n; ++i) { | |
| VERIFY_IS_EQUAL(dst[i], src); | |
| } | |
| } | |
| template <typename Scalar> | |
| void test_ilp64_eigen_blas_dispatch() { | |
| using Mat = Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic>; | |
| using Vec = Eigen::Matrix<Scalar, Eigen::Dynamic, 1>; | |
| const Eigen::Index m = 19; | |
| const Eigen::Index n = 23; | |
| const Eigen::Index k = 17; | |
| Mat A = Mat::Random(m, k); | |
| Mat B = Mat::Random(k, n); | |
| Mat C = Mat::Zero(m, n); | |
| Mat C_ref = A.lazyProduct(B); | |
| C.noalias() = A * B; | |
| VERIFY_IS_APPROX(C, C_ref); | |
| Vec v = Vec::Random(k); | |
| Vec y = A * v; | |
| Vec y_ref = A.lazyProduct(v); | |
| VERIFY_IS_APPROX(y, y_ref); | |
| Mat S = Mat::Random(m, m); | |
| S = S + S.adjoint().eval(); | |
| Mat B_sym = Mat::Random(m, n); | |
| Mat C_sym = S.template selfadjointView<Eigen::Upper>() * B_sym; | |
| Mat S_full = S.template selfadjointView<Eigen::Upper>(); | |
| VERIFY_IS_APPROX(C_sym, S_full.lazyProduct(B_sym)); | |
| Mat T = Mat::Random(m, m); | |
| T.diagonal().array() += Scalar(m); | |
| Mat X = T.template triangularView<Eigen::Upper>().solve(B_sym); | |
| Mat T_upper = T.template triangularView<Eigen::Upper>(); | |
| VERIFY_IS_APPROX(T_upper * X, B_sym); | |
| } | |
| } // namespace | |
| EIGEN_DECLARE_TEST(blas_ilp64) { | |
| CALL_SUBTEST(test_ilp64_upper_word_sensitivity()); | |
| CALL_SUBTEST(test_ilp64_level1()); | |
| CALL_SUBTEST(test_ilp64_eigen_blas_dispatch<float>()); | |
| CALL_SUBTEST(test_ilp64_eigen_blas_dispatch<double>()); | |
| CALL_SUBTEST(test_ilp64_eigen_blas_dispatch<std::complex<float>>()); | |
| CALL_SUBTEST(test_ilp64_eigen_blas_dispatch<std::complex<double>>()); | |
| } |