diff --git a/test/unit/mem/constCorrectness.cpp b/test/unit/mem/constCorrectness.cpp index 0143d6a6..1ee798b2 100644 --- a/test/unit/mem/constCorrectness.cpp +++ b/test/unit/mem/constCorrectness.cpp @@ -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 mutableStorage{}; + alignas(64) std::array 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, + std::remove_cvref_t>); + static_assert(std::same_as< + std::remove_cvref_t, + std::remove_cvref_t>); + static_assert(std::same_as>); + static_assert(!std::is_const_v>); + static_assert( + !std::is_const_v>); + + static_assert(std::same_as< + std::remove_cvref_t, + std::remove_cvref_t>); + static_assert(std::same_as< + std::remove_cvref_t, + std::remove_cvref_t>); + static_assert(std::same_as>); + static_assert(std::is_const_v>); + static_assert(std::is_const_v< + std::remove_reference_t>); + + static_assert(std::same_as< + std::remove_cvref_t, + std::remove_cvref_t>); + static_assert(std::same_as< + std::remove_cvref_t, + std::remove_cvref_t>); + static_assert(std::same_as>); + static_assert(std::is_const_v>); + static_assert(std::is_const_v< + std::remove_reference_t>); + } +} + +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(extents); + alignas(64) std::array mutableStorage{}; + alignas(64) std::array 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, + std::remove_cvref_t>); + static_assert(std::same_as< + std::remove_cvref_t, + std::remove_cvref_t>); + static_assert(std::same_as>); + static_assert(!std::is_const_v>); + static_assert( + !std::is_const_v>); + + static_assert(std::same_as< + std::remove_cvref_t, + std::remove_cvref_t>); + static_assert(std::same_as< + std::remove_cvref_t, + std::remove_cvref_t>); + static_assert(std::same_as>); + static_assert(std::is_const_v>); + static_assert( + std::is_const_v>); + + static_assert(std::same_as< + std::remove_cvref_t, + std::remove_cvref_t>); + static_assert(std::same_as< + std::remove_cvref_t, + std::remove_cvref_t>); + static_assert(std::same_as>); + static_assert(std::is_const_v>); + static_assert( + std::is_const_v>); + } +} + TEST_CASE("View::getMdSpan keeps host mutability boundaries", "[mem][view][mdspan][correctness]") { alignas(32) std::array storage{}; diff --git a/test/unit/mem/mdIterator.cpp b/test/unit/mem/mdIterator.cpp index ad5febce..2cc77423 100644 --- a/test/unit/mem/mdIterator.cpp +++ b/test/unit/mem/mdIterator.cpp @@ -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(extents); + alignas(64) std::array mutableStorage{}; + mutableStorage.fill(-1); + alignas(64) std::array 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>); + STATIC_REQUIRE(std::is_same_v>); + STATIC_REQUIRE(std::is_same_v>); + static_assert(!std::is_const_v>); + static_assert(std::is_const_v>); + static_assert(std::is_const_v>); + + 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(extents); + auto const explicitPitches = alpaka::Vec{80u, 20u, 4u}; + alignas(64) std::array mutableStorage{}; + alignas(64) std::array 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(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>); + STATIC_REQUIRE(std::is_same_v>); + STATIC_REQUIRE(std::is_same_v>); + static_assert(!std::is_const_v>); + static_assert(std::is_const_v>); + static_assert(std::is_const_v>); + + 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