Skip to content

Commit

Permalink
CTAD (deduction guides) for RangePolicy (kokkos#6850)
Browse files Browse the repository at this point in the history
* Removed the check for NVCC compiler version when testing CTAD,
as decltype(RangePolicy(...)) isn't compiling under any version
of Cuda.

* Added in the explicit deduction guides for RangePolicy:
• Correctness when passing in an execution space
• Workaround for nvcc as RangePolicy<...> doesn't have any
template parameters that can be deduced, so gcc/clang assume that a
matching ctor in the primary template deduces to RangePolicy<> while
nvcc assumes it is a bug.

Rewrote the tests to be of the form:

[[maybe_unused]] static inline auto rpxy = RangePolicy(x, y);
static_assert(is_same_v<RangePolicy<WhatShouldBeHere>, decltype(rpxy)>);

This form avoids the most vexing parse.o, and seems to pass on all
compilers.

Added tests for SomeExecutionSpace which is guaranteed not to be
DefaultExecutionSpace.

Added calls to ImplicitlyConvertibleToDefaultExecutionSpace::operator
DefaultExecutionSpace() and SomeExecutionSpace::concurrency() to
avoid maybe_unused errors under some compilers.

Notes:

The default constructed CTAD RangePolicy uses list initialization syntax
(curlies) instead of parentheses to get around a gcc 8.2 compiler bug.

nestodes uses "Kokkos::DefaultExecutionSpace" instead of "auto" in order
to trigger the implicit conversion.
  • Loading branch information
nliber authored Mar 7, 2024
1 parent c7ad79c commit 6db04b3
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 43 deletions.
15 changes: 15 additions & 0 deletions core/src/Kokkos_ExecPolicy.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,21 @@ class RangePolicy : public Impl::PolicyTraits<Properties...> {
};
};

RangePolicy()->RangePolicy<>;

RangePolicy(int64_t, int64_t)->RangePolicy<>;
RangePolicy(int64_t, int64_t, ChunkSize const&)->RangePolicy<>;

RangePolicy(DefaultExecutionSpace const&, int64_t, int64_t)->RangePolicy<>;
RangePolicy(DefaultExecutionSpace const&, int64_t, int64_t, ChunkSize const&)
->RangePolicy<>;

template <typename ES, typename = std::enable_if_t<is_execution_space_v<ES>>>
RangePolicy(ES const&, int64_t, int64_t)->RangePolicy<ES>;

template <typename ES, typename = std::enable_if_t<is_execution_space_v<ES>>>
RangePolicy(ES const&, int64_t, int64_t, ChunkSize const&)->RangePolicy<ES>;

} // namespace Kokkos

//----------------------------------------------------------------------------
Expand Down
144 changes: 101 additions & 43 deletions core/unit_test/TestRangePolicyCTAD.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,76 +17,134 @@
#include <Kokkos_Core.hpp>
#include "Kokkos_Core_fwd.hpp"

#if !defined(KOKKOS_COMPILER_NVCC) || KOKKOS_COMPILER_NVCC >= 1120

namespace {

template <class... Args>
using PolicyMaker = decltype(::Kokkos::RangePolicy(std::declval<Args>()...));

template <class Policy, class... Args>
inline constexpr bool IsSamePolicy =
std::is_same_v<Policy, PolicyMaker<Args...>>;
struct TestRangePolicyCTAD {
struct SomeExecutionSpace {
using execution_space = SomeExecutionSpace;
using size_type = size_t;

#define KOKKOS_TEST_RANGE_POLICY(...) static_assert(IsSamePolicy<__VA_ARGS__>)
[[maybe_unused]] static int concurrency() { return 0; }
};
static_assert(Kokkos::is_execution_space_v<SomeExecutionSpace>);

struct TestRangePolicyCTAD {
struct ImplicitlyConvertibleToDefaultExecutionSpace {
operator Kokkos::DefaultExecutionSpace() const {
[[maybe_unused]] operator Kokkos::DefaultExecutionSpace() const {
return Kokkos::DefaultExecutionSpace();
}
};
static_assert(!Kokkos::is_execution_space_v<
ImplicitlyConvertibleToDefaultExecutionSpace>);

using des = Kokkos::DefaultExecutionSpace;
using nes = ImplicitlyConvertibleToDefaultExecutionSpace;
using i64 = int64_t;
using i32 = int32_t;
using cs = Kokkos::ChunkSize;
[[maybe_unused]] static inline auto i64 = int64_t();
[[maybe_unused]] static inline auto i32 = int32_t();
[[maybe_unused]] static inline auto cs = Kokkos::ChunkSize(0);
[[maybe_unused]] static inline auto des = Kokkos::DefaultExecutionSpace();
[[maybe_unused]] static inline auto nes =
ImplicitlyConvertibleToDefaultExecutionSpace();
[[maybe_unused]] static inline auto ses = SomeExecutionSpace();

// RangePolicy()

// Guard against GGC 8.4 bug
// error: cannot deduce template arguments for ‘RangePolicy’ from ()
// error: template argument 2 is invalid
#if !defined(KOKKOS_COMPILER_GNU) || (KOKKOS_COMPILER_GNU > 900)
KOKKOS_TEST_RANGE_POLICY(Kokkos::RangePolicy<> /*, no argument */);
#endif
[[maybe_unused]] static inline auto rp = Kokkos::RangePolicy{};
static_assert(std::is_same_v<Kokkos::RangePolicy<>, decltype(rp)>);

// RangePolicy(index_type, index_type)

KOKKOS_TEST_RANGE_POLICY(Kokkos::RangePolicy<>, i64, i64);
KOKKOS_TEST_RANGE_POLICY(Kokkos::RangePolicy<>, i64, i32);
KOKKOS_TEST_RANGE_POLICY(Kokkos::RangePolicy<>, i32, i64);
KOKKOS_TEST_RANGE_POLICY(Kokkos::RangePolicy<>, i32, i32);
[[maybe_unused]] static inline auto rpi64i64 = Kokkos::RangePolicy(i64, i64);
static_assert(std::is_same_v<Kokkos::RangePolicy<>, decltype(rpi64i64)>);

[[maybe_unused]] static inline auto rpi64i32 = Kokkos::RangePolicy(i64, i32);
static_assert(std::is_same_v<Kokkos::RangePolicy<>, decltype(rpi64i32)>);

[[maybe_unused]] static inline auto rpi32i64 = Kokkos::RangePolicy(i32, i64);
static_assert(std::is_same_v<Kokkos::RangePolicy<>, decltype(rpi32i64)>);

// RangePolicy(index_type, index_type, Args...)
[[maybe_unused]] static inline auto rpi32i32 = Kokkos::RangePolicy(i32, i32);
static_assert(std::is_same_v<Kokkos::RangePolicy<>, decltype(rpi32i32)>);

KOKKOS_TEST_RANGE_POLICY(Kokkos::RangePolicy<>, i64, i64, cs);
KOKKOS_TEST_RANGE_POLICY(Kokkos::RangePolicy<>, i64, i32, cs);
KOKKOS_TEST_RANGE_POLICY(Kokkos::RangePolicy<>, i32, i64, cs);
KOKKOS_TEST_RANGE_POLICY(Kokkos::RangePolicy<>, i32, i32, cs);
// RangePolicy(index_type, index_type, ChunkSize)

[[maybe_unused]] static inline auto rpi64i64cs =
Kokkos::RangePolicy(i64, i64, cs);
static_assert(std::is_same_v<Kokkos::RangePolicy<>, decltype(rpi64i64cs)>);

[[maybe_unused]] static inline auto rpi64i32cs =
Kokkos::RangePolicy(i64, i32, cs);
static_assert(std::is_same_v<Kokkos::RangePolicy<>, decltype(rpi64i32cs)>);

[[maybe_unused]] static inline auto rpi32i64cs =
Kokkos::RangePolicy(i32, i64, cs);
static_assert(std::is_same_v<Kokkos::RangePolicy<>, decltype(rpi32i64cs)>);

[[maybe_unused]] static inline auto rpi32i32cs =
Kokkos::RangePolicy(i32, i32, cs);
static_assert(std::is_same_v<Kokkos::RangePolicy<>, decltype(rpi32i32cs)>);

// RangePolicy(execution_space, index_type, index_type)

KOKKOS_TEST_RANGE_POLICY(Kokkos::RangePolicy<>, des, i64, i64);
KOKKOS_TEST_RANGE_POLICY(Kokkos::RangePolicy<>, des, i32, i32);
KOKKOS_TEST_RANGE_POLICY(Kokkos::RangePolicy<>, nes, i64, i64);
KOKKOS_TEST_RANGE_POLICY(Kokkos::RangePolicy<>, nes, i32, i32);
[[maybe_unused]] static inline auto rpdesi64i64 =
Kokkos::RangePolicy(des, i64, i64);
static_assert(std::is_same_v<Kokkos::RangePolicy<>, decltype(rpdesi64i64)>);

[[maybe_unused]] static inline auto rpdesi32i32 =
Kokkos::RangePolicy(des, i32, i32);
static_assert(std::is_same_v<Kokkos::RangePolicy<>, decltype(rpdesi32i32)>);

[[maybe_unused]] static inline auto rpnesi64i64 =
Kokkos::RangePolicy(nes, i64, i64);
static_assert(std::is_same_v<Kokkos::RangePolicy<>, decltype(rpnesi64i64)>);

// RangePolicy(execution_space, index_type, index_type, Args...)
[[maybe_unused]] static inline auto rpnesi32i32 =
Kokkos::RangePolicy(nes, i32, i32);
static_assert(std::is_same_v<Kokkos::RangePolicy<>, decltype(rpnesi32i32)>);

[[maybe_unused]] static inline auto rpsesi64i64 =
Kokkos::RangePolicy(ses, i64, i64);
static_assert(std::is_same_v<Kokkos::RangePolicy<SomeExecutionSpace>,
decltype(rpsesi64i64)>);

[[maybe_unused]] static inline auto rpsesi32i32 =
Kokkos::RangePolicy(ses, i32, i32);
static_assert(std::is_same_v<Kokkos::RangePolicy<SomeExecutionSpace>,
decltype(rpsesi32i32)>);

// RangePolicy(execution_space, index_type, index_type, ChunkSize)

[[maybe_unused]] static inline auto rpdesi64i64cs =
Kokkos::RangePolicy(des, i64, i64, cs);
static_assert(std::is_same_v<Kokkos::RangePolicy<>, decltype(rpdesi64i64cs)>);

[[maybe_unused]] static inline auto rpdesi32i32cs =
Kokkos::RangePolicy(des, i32, i32, cs);
static_assert(std::is_same_v<Kokkos::RangePolicy<>, decltype(rpdesi32i32cs)>);

[[maybe_unused]] static inline auto rpnesi64i64cs =
Kokkos::RangePolicy(nes, i64, i64, cs);
static_assert(std::is_same_v<Kokkos::RangePolicy<>, decltype(rpnesi64i64cs)>);

[[maybe_unused]] static inline auto rpnesi32i32cs =
Kokkos::RangePolicy(nes, i32, i32, cs);
static_assert(std::is_same_v<Kokkos::RangePolicy<>, decltype(rpnesi32i32cs)>);

[[maybe_unused]] static inline auto rpsesi64i64cs =
Kokkos::RangePolicy(ses, i64, i64, cs);
static_assert(std::is_same_v<Kokkos::RangePolicy<SomeExecutionSpace>,
decltype(rpsesi64i64cs)>);

[[maybe_unused]] static inline auto rpsesi32i32cs =
Kokkos::RangePolicy(ses, i32, i32, cs);
static_assert(std::is_same_v<Kokkos::RangePolicy<SomeExecutionSpace>,
decltype(rpsesi32i32cs)>);

KOKKOS_TEST_RANGE_POLICY(Kokkos::RangePolicy<>, des, i64, i64, cs);
KOKKOS_TEST_RANGE_POLICY(Kokkos::RangePolicy<>, des, i32, i32, cs);
KOKKOS_TEST_RANGE_POLICY(Kokkos::RangePolicy<>, nes, i64, i64, cs);
KOKKOS_TEST_RANGE_POLICY(Kokkos::RangePolicy<>, nes, i32, i32, cs);
}; // TestRangePolicyCTAD struct

// To eliminate maybe_unused warning on some compilers
[[maybe_unused]] const Kokkos::DefaultExecutionSpace des =

[[maybe_unused]] const Kokkos::DefaultExecutionSpace nestodes =
TestRangePolicyCTAD::ImplicitlyConvertibleToDefaultExecutionSpace();

} // namespace
[[maybe_unused]] const auto sesconcurrency =
TestRangePolicyCTAD::ses.concurrency();

#endif
} // namespace

0 comments on commit 6db04b3

Please sign in to comment.