// Copyright (C) 2020-2023 Free Software Foundation, Inc. // // This file is part of the GNU ISO C++ Library. This library is free // software; you can redistribute it and/or modify it under the // terms of the GNU General Public License as published by the // Free Software Foundation; either version 3, or (at your option) // any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with this library; see the file COPYING3. If not see // . // expensive: * [1-9] * * // timeout-factor: 2 #include "bits/main.h" template void load_store() { // types, tags, and constants using T = typename V::value_type; auto&& gen = make_vec; using std::experimental::element_aligned; using std::experimental::vector_aligned; // stride_alignment: consider V::size() == 6. The only reliable alignment is // 2 * sizeof(U). I.e. if the first address is aligned to 8 * sizeof(U), // then the next address is 6 * sizeof(U) larger, thus only aligned to 2 * // sizeof(U). // => the LSB determines the stride alignment constexpr size_t stride_alignment = size_t(1) << __builtin_ctz(V::size()); using stride_aligned_t = std::conditional_t< V::size() == stride_alignment, decltype(vector_aligned), std::experimental::overaligned_tag>; constexpr stride_aligned_t stride_aligned = {}; constexpr size_t alignment = 2 * std::experimental::memory_alignment_v; constexpr auto overaligned = std::experimental::overaligned; const V indexes_from_0([](auto i) { return i; }); for (std::size_t i = 0; i < V::size(); ++i) { COMPARE(indexes_from_0[i], T(i)); } // loads cvt_inputs test_values; constexpr auto mem_size = test_values.size() > 3 * V::size() ? test_values.size() : 3 * V::size(); alignas(std::experimental::memory_alignment_v * 2) U mem[mem_size] = {}; alignas(std::experimental::memory_alignment_v * 2) T reference[mem_size] = {}; for (std::size_t i = 0; i < test_values.size(); ++i) { const U value = test_values[i]; mem[i] = value; reference[i] = static_cast(value); } for (std::size_t i = test_values.size(); i < mem_size; ++i) { mem[i] = U(i); reference[i] = mem[i]; } V x(&mem[V::size()], stride_aligned); auto&& compare = [&](const std::size_t offset) { static int n = 0; const V ref(&reference[offset], element_aligned); for (auto i = 0ul; i < V::size(); ++i) { if (is_conversion_undefined(mem[i + offset])) { continue; } COMPARE(x[i], reference[i + offset]) << "\nbefore conversion: " << mem[i + offset] << "\n offset = " << offset << "\n x = " << x << "\nreference = " << ref << "\nx == ref = " << (x == ref) << "\ncall no. " << n; } ++n; }; compare(V::size()); x = V{mem, overaligned}; compare(0); x = {&mem[1], element_aligned}; compare(1); x.copy_from(&mem[V::size()], stride_aligned); compare(V::size()); x.copy_from(&mem[1], element_aligned); compare(1); x.copy_from(mem, vector_aligned); compare(0); for (std::size_t i = 0; i < mem_size - V::size(); ++i) { x.copy_from(&mem[i], element_aligned); compare(i); } for (std::size_t i = 0; i < test_values.size(); ++i) { mem[i] = U(i); } x = indexes_from_0; using M = typename V::mask_type; const M alternating_mask = make_mask({0, 1}); where(alternating_mask, x).copy_from(&mem[V::size()], stride_aligned); const V indexes_from_size = gen({T(V::size())}, 1); COMPARE(x == indexes_from_size, alternating_mask) << "x: " << x << "\nindexes_from_size: " << indexes_from_size; COMPARE(x == indexes_from_0, !alternating_mask); where(alternating_mask, x).copy_from(&mem[1], element_aligned); const V indexes_from_1 = gen({1, 2, 3, 4}, 4); COMPARE(x == indexes_from_1, alternating_mask); COMPARE(x == indexes_from_0, !alternating_mask); where(!alternating_mask, x).copy_from(mem, overaligned); COMPARE(x == indexes_from_0, !alternating_mask); COMPARE(x == indexes_from_1, alternating_mask); x = where(alternating_mask, V()).copy_from(&mem[V::size()], stride_aligned); COMPARE(x == indexes_from_size, alternating_mask); COMPARE(x == 0, !alternating_mask); x = where(!alternating_mask, V()).copy_from(&mem[1], element_aligned); COMPARE(x == indexes_from_1, !alternating_mask); COMPARE(x == 0, alternating_mask); // stores auto&& init_mem = [&mem](U init) { for (auto i = mem_size; i; --i) { mem[i - 1] = init; } }; init_mem(-1); x = indexes_from_1; x.copy_to(&mem[V::size()], stride_aligned); std::size_t i = 0; for (; i < V::size(); ++i) { COMPARE(mem[i], U(-1)) << "i: " << i; } for (; i < 2 * V::size(); ++i) { COMPARE(mem[i], U(i - V::size() + 1)) << "i: " << i; } for (; i < 3 * V::size(); ++i) { COMPARE(mem[i], U(-1)) << "i: " << i; } init_mem(-1); x.copy_to(&mem[1], element_aligned); COMPARE(mem[0], U(-1)); for (i = 1; i <= V::size(); ++i) { COMPARE(mem[i], U(i)); } for (; i < 3 * V::size(); ++i) { COMPARE(mem[i], U(-1)); } init_mem(-1); x.copy_to(mem, vector_aligned); for (i = 0; i < V::size(); ++i) { COMPARE(mem[i], U(i + 1)); } for (; i < 3 * V::size(); ++i) { COMPARE(mem[i], U(-1)); } init_mem(-1); where(alternating_mask, indexes_from_0) .copy_to(&mem[V::size()], stride_aligned); for (i = 0; i < V::size() + 1; ++i) { COMPARE(mem[i], U(-1)); } for (; i < 2 * V::size(); i += 2) { COMPARE(mem[i], U(i - V::size())); } for (i = V::size() + 2; i < 2 * V::size(); i += 2) { COMPARE(mem[i], U(-1)); } for (; i < 3 * V::size(); ++i) { COMPARE(mem[i], U(-1)); } } template void test() { load_store(); load_store(); load_store(); load_store(); load_store(); load_store(); load_store(); load_store(); load_store(); load_store(); load_store(); load_store(); load_store(); load_store(); load_store(); load_store(); load_store(); }