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