Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 107 additions & 0 deletions test/unit/mem/constCorrectness.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -914,6 +914,113 @@ TEST_CASE(
}
}

TEST_CASE(
"alpaka::makeMdSpan(pointer, extents, pitches, alignment) preserves explicit host metadata and inner constness",
"[mem][mdspan][correctness][makeMdSpan]")
{
auto const extents = alpaka::Vec{2u, 3u, 4u};
auto const explicitPitches = alpaka::Vec{80u, 20u, 4u};
alignas(64) std::array<int, 2 * 3 * 5 * 2> mutableStorage{};
alignas(64) std::array<int const, 2 * 3 * 5 * 2> innerConstStorage{};

auto mutableExplicitPitchMdSpan
= alpaka::makeMdSpan(mutableStorage.data(), extents, explicitPitches, alpaka::Alignment<64>{});
auto const outerConstExplicitPitchMdSpan
= alpaka::makeMdSpan(mutableStorage.data(), extents, explicitPitches, alpaka::Alignment<64>{});
auto innerConstExplicitPitchMdSpan
= alpaka::makeMdSpan(innerConstStorage.data(), extents, explicitPitches, alpaka::Alignment<64>{});

SECTION("compile-time explicit pitches stay part of the rebuilt mdspan contract")
{
// The raw explicit-pitch overload should keep the caller-supplied host layout, alignment, and pointed-to
// constness instead of normalizing the span back to a packed representation.
static_assert(std::same_as<
std::remove_cvref_t<decltype(mutableExplicitPitchMdSpan.getExtents())>,
std::remove_cvref_t<decltype(extents)>>);
static_assert(std::same_as<
std::remove_cvref_t<decltype(mutableExplicitPitchMdSpan.getPitches())>,
std::remove_cvref_t<decltype(explicitPitches)>>);
static_assert(std::same_as<decltype(mutableExplicitPitchMdSpan.getAlignment()), alpaka::Alignment<64>>);
static_assert(!std::is_const_v<std::remove_pointer_t<decltype(mutableExplicitPitchMdSpan.data())>>);
static_assert(
!std::is_const_v<std::remove_reference_t<decltype(mutableExplicitPitchMdSpan[alpaka::Vec{0u, 0u, 0u}])>>);

static_assert(std::same_as<
std::remove_cvref_t<decltype(outerConstExplicitPitchMdSpan.getExtents())>,
std::remove_cvref_t<decltype(extents)>>);
static_assert(std::same_as<
std::remove_cvref_t<decltype(outerConstExplicitPitchMdSpan.getPitches())>,
std::remove_cvref_t<decltype(explicitPitches)>>);
static_assert(std::same_as<decltype(outerConstExplicitPitchMdSpan.getAlignment()), alpaka::Alignment<64>>);
static_assert(std::is_const_v<std::remove_pointer_t<decltype(outerConstExplicitPitchMdSpan.data())>>);
static_assert(std::is_const_v<
std::remove_reference_t<decltype(outerConstExplicitPitchMdSpan[alpaka::Vec{0u, 0u, 0u}])>>);

static_assert(std::same_as<
std::remove_cvref_t<decltype(innerConstExplicitPitchMdSpan.getExtents())>,
std::remove_cvref_t<decltype(extents)>>);
static_assert(std::same_as<
std::remove_cvref_t<decltype(innerConstExplicitPitchMdSpan.getPitches())>,
std::remove_cvref_t<decltype(explicitPitches)>>);
static_assert(std::same_as<decltype(innerConstExplicitPitchMdSpan.getAlignment()), alpaka::Alignment<64>>);
static_assert(std::is_const_v<std::remove_pointer_t<decltype(innerConstExplicitPitchMdSpan.data())>>);
static_assert(std::is_const_v<
std::remove_reference_t<decltype(innerConstExplicitPitchMdSpan[alpaka::Vec{0u, 0u, 0u}])>>);
}
}

TEST_CASE(
"alpaka::makeMdSpan(pointer, extents, alignment) preserves packed host metadata and inner constness",
"[mem][mdspan][correctness][makeMdSpan]")
{
auto const extents = alpaka::Vec{2u, 3u, 4u};
auto const packedPitches = alpaka::calculatePitchesFromExtents<int>(extents);
alignas(64) std::array<int, 2 * 3 * 4> mutableStorage{};
alignas(64) std::array<int const, 2 * 3 * 4> innerConstStorage{};

auto mutablePackedMdSpan = alpaka::makeMdSpan(mutableStorage.data(), extents, alpaka::Alignment<64>{});
auto const outerConstPackedMdSpan = alpaka::makeMdSpan(mutableStorage.data(), extents, alpaka::Alignment<64>{});
auto innerConstPackedMdSpan = alpaka::makeMdSpan(innerConstStorage.data(), extents, alpaka::Alignment<64>{});

SECTION("compile-time packed pitches are derived from extents and keep inner constness")
{
// The packed raw overload should keep the host extents and alignment while deriving the packed pitch type from
// the extents instead of accepting caller-managed layout metadata.
static_assert(std::same_as<
std::remove_cvref_t<decltype(mutablePackedMdSpan.getExtents())>,
std::remove_cvref_t<decltype(extents)>>);
static_assert(std::same_as<
std::remove_cvref_t<decltype(mutablePackedMdSpan.getPitches())>,
std::remove_cvref_t<decltype(packedPitches)>>);
static_assert(std::same_as<decltype(mutablePackedMdSpan.getAlignment()), alpaka::Alignment<64>>);
static_assert(!std::is_const_v<std::remove_pointer_t<decltype(mutablePackedMdSpan.data())>>);
static_assert(
!std::is_const_v<std::remove_reference_t<decltype(mutablePackedMdSpan[alpaka::Vec{0u, 0u, 0u}])>>);

static_assert(std::same_as<
std::remove_cvref_t<decltype(outerConstPackedMdSpan.getExtents())>,
std::remove_cvref_t<decltype(extents)>>);
static_assert(std::same_as<
std::remove_cvref_t<decltype(outerConstPackedMdSpan.getPitches())>,
std::remove_cvref_t<decltype(packedPitches)>>);
static_assert(std::same_as<decltype(outerConstPackedMdSpan.getAlignment()), alpaka::Alignment<64>>);
static_assert(std::is_const_v<std::remove_pointer_t<decltype(outerConstPackedMdSpan.data())>>);
static_assert(
std::is_const_v<std::remove_reference_t<decltype(outerConstPackedMdSpan[alpaka::Vec{0u, 0u, 0u}])>>);

static_assert(std::same_as<
std::remove_cvref_t<decltype(innerConstPackedMdSpan.getExtents())>,
std::remove_cvref_t<decltype(extents)>>);
static_assert(std::same_as<
std::remove_cvref_t<decltype(innerConstPackedMdSpan.getPitches())>,
std::remove_cvref_t<decltype(packedPitches)>>);
static_assert(std::same_as<decltype(innerConstPackedMdSpan.getAlignment()), alpaka::Alignment<64>>);
static_assert(std::is_const_v<std::remove_pointer_t<decltype(innerConstPackedMdSpan.data())>>);
static_assert(
std::is_const_v<std::remove_reference_t<decltype(innerConstPackedMdSpan[alpaka::Vec{0u, 0u, 0u}])>>);
}
}

TEST_CASE("View::getMdSpan keeps host mutability boundaries", "[mem][view][mdspan][correctness]")
{
alignas(32) std::array<int, 2 * 3> storage{};
Expand Down
95 changes: 95 additions & 0 deletions test/unit/mem/mdIterator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,101 @@ TEST_CASE("mdIterator host coverage", "[mem][mdIterator][iterator]")
REQUIRE(collectValues(constMdSpan) == visited);
}

SECTION("explicit-pitch makeMdSpan keeps padded host layout and aliases the same storage")
{
// The raw explicit-pitch overload must keep caller-provided padded pitches for indexing instead of deriving a
// packed layout from the extents.
auto const extents = alpaka::Vec{2u, 3u, 4u};
auto const explicitPitches = alpaka::Vec{80u, 20u, 4u};
auto const packedPitches = alpaka::calculatePitchesFromExtents<int>(extents);
alignas(64) std::array<int, 40> mutableStorage{};
mutableStorage.fill(-1);
alignas(64) std::array<int const, 40> innerConstStorage{};

auto mdSpan = alpaka::makeMdSpan(mutableStorage.data(), extents, explicitPitches, alpaka::Alignment<64>{});
auto const outerConstMdSpan
= alpaka::makeMdSpan(mutableStorage.data(), extents, explicitPitches, alpaka::Alignment<64>{});
auto innerConstMdSpan
= alpaka::makeMdSpan(innerConstStorage.data(), extents, explicitPitches, alpaka::Alignment<64>{});

REQUIRE(mdSpan.getExtents() == extents);
REQUIRE(mdSpan.getPitches() == explicitPitches);
REQUIRE(mdSpan.getPitches() != packedPitches);
REQUIRE(mdSpan.data() == mutableStorage.data());
STATIC_REQUIRE(std::is_same_v<decltype(mdSpan.getAlignment()), alpaka::Alignment<64>>);
STATIC_REQUIRE(std::is_same_v<decltype(outerConstMdSpan.getAlignment()), alpaka::Alignment<64>>);
STATIC_REQUIRE(std::is_same_v<decltype(innerConstMdSpan.getAlignment()), alpaka::Alignment<64>>);
static_assert(!std::is_const_v<std::remove_reference_t<decltype(mdSpan[alpaka::Vec{0u, 0u, 0u}])>>);
static_assert(std::is_const_v<std::remove_reference_t<decltype(outerConstMdSpan[alpaka::Vec{0u, 0u, 0u}])>>);
static_assert(std::is_const_v<std::remove_reference_t<decltype(innerConstMdSpan[alpaka::Vec{0u, 0u, 0u}])>>);

mutableStorage[5] = -50;
mutableStorage[4] = -40;
REQUIRE(mdSpan[alpaka::Vec{0u, 1u, 0u}] == -50);

mutableStorage[20] = 220;
mutableStorage[12] = 120;
REQUIRE(mdSpan[alpaka::Vec{1u, 0u, 0u}] == 220);

auto const sampleIdx = alpaka::Vec{1u, 2u, 3u};
mdSpan[sampleIdx] = 1323;
REQUIRE(mutableStorage[33] == 1323);
REQUIRE(mutableStorage[23] == -1);
REQUIRE(&mdSpan[sampleIdx] == &mutableStorage[33]);
REQUIRE(&outerConstMdSpan[sampleIdx] == &mutableStorage[33]);

auto iter = mdSpan.begin();
++iter;
REQUIRE(&*iter == &mutableStorage[1]);
}

SECTION("packed raw makeMdSpan derives packed layout and aliases the same host storage")
{
// The packed raw overload must rebuild pitches from extents, keep the original pointer and alignment, and let
// immediate element access plus iteration observe the same packed storage contract.
auto const extents = alpaka::Vec{2u, 3u, 4u};
auto const packedPitches = alpaka::calculatePitchesFromExtents<int>(extents);
auto const explicitPitches = alpaka::Vec{80u, 20u, 4u};
alignas(64) std::array<int, 2 * 3 * 4> mutableStorage{};
alignas(64) std::array<int const, 2 * 3 * 4> innerConstStorage{200, 201, 202, 203, 204, 205, 206, 207,
208, 209, 210, 211, 212, 213, 214, 215,
216, 217, 218, 219, 220, 221, 222, 223};
for(std::size_t i = 0; i < mutableStorage.size(); ++i)
mutableStorage[i] = static_cast<int>(10 + i);

auto mdSpan = alpaka::makeMdSpan(mutableStorage.data(), extents, alpaka::Alignment<64>{});
auto const outerConstMdSpan = alpaka::makeMdSpan(mutableStorage.data(), extents, alpaka::Alignment<64>{});
auto innerConstMdSpan = alpaka::makeMdSpan(innerConstStorage.data(), extents, alpaka::Alignment<64>{});

REQUIRE(mdSpan.getExtents() == extents);
REQUIRE(mdSpan.getPitches() == packedPitches);
REQUIRE(mdSpan.getPitches() != explicitPitches);
REQUIRE(mdSpan.data() == mutableStorage.data());
STATIC_REQUIRE(std::is_same_v<decltype(mdSpan.getAlignment()), alpaka::Alignment<64>>);
STATIC_REQUIRE(std::is_same_v<decltype(outerConstMdSpan.getAlignment()), alpaka::Alignment<64>>);
STATIC_REQUIRE(std::is_same_v<decltype(innerConstMdSpan.getAlignment()), alpaka::Alignment<64>>);
static_assert(!std::is_const_v<std::remove_reference_t<decltype(mdSpan[alpaka::Vec{0u, 0u, 0u}])>>);
static_assert(std::is_const_v<std::remove_reference_t<decltype(outerConstMdSpan[alpaka::Vec{0u, 0u, 0u}])>>);
static_assert(std::is_const_v<std::remove_reference_t<decltype(innerConstMdSpan[alpaka::Vec{0u, 0u, 0u}])>>);

mutableStorage[15] = 1500;
mutableStorage[23] = 2300;
REQUIRE(mdSpan[alpaka::Vec{1u, 0u, 3u}] == 1500);

auto const sampleIdx = alpaka::Vec{1u, 2u, 3u};
REQUIRE(mdSpan[sampleIdx] == 2300);
mdSpan[sampleIdx] = 777;
REQUIRE(mutableStorage[23] == 777);
REQUIRE(&mdSpan[sampleIdx] == &mutableStorage[23]);
REQUIRE(&outerConstMdSpan[sampleIdx] == &mutableStorage[23]);
REQUIRE(innerConstMdSpan[sampleIdx] == 223);
REQUIRE(&innerConstMdSpan[sampleIdx] == &innerConstStorage[23]);

auto iter = mdSpan.begin();
++iter;
REQUIRE(&*iter == &mutableStorage[1]);
}

SECTION("pre-increment and post-increment advance one element at a time")
{
// Forward-iterator increments need to preserve the old value for post-increment and return self for
Expand Down