31template <
class T>
using kernel_type_probe_arg_t = std::remove_reference_t<T>&;
33template <
class T>
struct is_kernel_acceptance : std::false_type
36template <KernelTypeAcceptance Acceptance>
struct is_kernel_acceptance<
KernelAcceptance<Acceptance>> : std::true_type
39template <
class Backend,
class Op,
class... Args>
consteval bool backend_has_try_kernel()
43 try_kernel(std::declval<Backend const&>(), std::declval<Op const&>(),
44 std::declval<kernel_type_probe_arg_t<Args>>()...)
45 } -> std::same_as<KernelAttempt>;
54template <
class Backend,
class Op,
class... Args>
consteval KernelTypeAcceptance backend_type_acceptance()
56 using backend_type = std::remove_cvref_t<Backend>;
57 using op_type = std::remove_cvref_t<Op>;
58 if constexpr (
requires {
60 std::declval<kernel_type_probe_arg_t<Args>>()...);
64 std::declval<backend_type const&>(), std::declval<op_type const&>(),
65 std::declval<kernel_type_probe_arg_t<Args>>()...))>;
66 if constexpr (!is_kernel_acceptance<result_type>::value)
68 static_assert(is_kernel_acceptance<result_type>::value,
69 "kernel_accepts_types must return kernel_types_no, kernel_types_maybe, or kernel_types_yes");
74 constexpr auto acceptance = result_type::value;
81 static_assert(backend_has_try_kernel<backend_type, op_type, Args...>(),
82 "kernel_accepts_types accepted these types, but try_kernel is not available");
93template <
class BackendSelector>
94using normalized_backend_selector_t =
95 std::remove_cvref_t<decltype(normalize_backend_selector(std::declval<BackendSelector>()))>;
97template <
class Backend,
class BackendList>
struct PrependBackend;
99template <
class Backend,
class... Backends>
struct PrependBackend<Backend,
backend_list<Backends...>>
104template <
class BackendList,
class Op,
class... Args>
struct KernelTypeCandidates;
106template <
class Op,
class... Args>
struct KernelTypeCandidates<
backend_list<>, Op, Args...>
108 using type = backend_list<>;
111template <
class Backend,
class... Backends,
class Op,
class... Args>
112struct KernelTypeCandidates<
backend_list<Backend, Backends...>, Op, Args...>
115 using tail =
typename KernelTypeCandidates<
backend_list<Backends...>, Op, Args...>::type;
119 typename PrependBackend<Backend, tail>::type>;
122template <
class Op,
class... Args,
class Backend>
constexpr auto candidate_backend_tuple(Backend&& backend)
124 using backend_type = std::remove_cvref_t<Backend>;
127 return std::tuple<>{};
131 return std::tuple<backend_type>{std::forward<Backend>(backend)};
135template <
class Tuple, std::size_t... Index>
136constexpr auto backend_list_from_tuple(Tuple&& tuple, std::index_sequence<Index...>)
138 using tuple_type = std::remove_cvref_t<Tuple>;
139 return backend_list<std::tuple_element_t<Index, tuple_type>...>{std::get<Index>(std::forward<Tuple>(tuple))...};
142template <
class Op,
class... Args,
class... Backends>
143consteval KernelTypeAcceptance probe_backend_list_types(std::type_identity<backend_list<Backends...>>)
146 if constexpr (any_yes)
152 if constexpr (any_maybe)
165template <
class BackendSelector,
class Op,
class... Args>
167 typename detail::KernelTypeCandidates<detail::normalized_backend_selector_t<BackendSelector>,
168 std::remove_cvref_t<Op>, Args...>::type;
174template <
class BackendSelector,
class Op,
class... Args>
177 using op_type = std::remove_cvref_t<Op>;
179 auto candidates = std::apply(
180 []<
class... Backends>(Backends&&... backend) {
181 return std::tuple_cat(detail::candidate_backend_tuple<op_type, Args...>(std::forward<Backends>(backend))...);
183 std::move(backends.entries));
184 auto result = detail::backend_list_from_tuple(std::move(candidates),
185 std::make_index_sequence<std::tuple_size_v<
decltype(candidates)>>{});
196 using backends_type = detail::normalized_backend_selector_t<BackendSelector>;
197 static_assert(is_backend_list_v<backends_type>,
"backend selectors must normalize to backend_list");
198 return detail::probe_backend_list_types<std::remove_cvref_t<Op>, Args...>(std::type_identity<backends_type>{});
205template <
class BackendSelector,
class Op,
class... Args>
213template <
class BackendList,
class Op,
class... Args>
214concept KernelDispatchTypesAccepted =
215 probe_backend_list_types<std::remove_cvref_t<Op>, Args...>(std::type_identity<BackendList>{}) !=
218template <
class Entity>
219concept NamedKernelDispatchEntity =
requires {
220 { std::remove_cvref_t<Entity>::name } -> std::convertible_to<std::string_view>;
223template <
class... Backends,
class Op,
class... Args>
224std::vector<KernelBackendAttempt>
225make_kernel_backend_attempts(backend_list<Backends...>
const&,
226 std::span<std::optional<KernelAttempt>
const> runtime_results, Op
const&, Args&&...)
228 static_assert(NamedKernelDispatchEntity<Op>,
"kernel operation tags must define a static name");
229 static_assert((NamedKernelDispatchEntity<Backends> && ...),
"kernel backends must define a static name");
230 CHECK(runtime_results.empty() || runtime_results.size() ==
sizeof...(Backends));
232 std::vector<KernelBackendAttempt> attempts;
233 attempts.reserve(
sizeof...(Backends));
234 std::size_t index = 0;
235 auto append_attempt = [&]<
class Backend>() {
236 constexpr auto acceptance = backend_type_acceptance<Backend, Op, Args...>();
237 attempts.push_back(KernelBackendAttempt{
238 .backend = std::string(std::remove_cvref_t<Backend>::name),
239 .type_acceptance = acceptance,
240 .runtime_result = runtime_results.empty() ? std::nullopt : runtime_results[index],
244 (append_attempt.template operator()<Backends>(), ...);
248template <
class... Backends,
class Op,
class... Args>
249dispatch_diagnostics::event
250make_kernel_dispatch_diagnostic(backend_list<Backends...>
const& backends,
251 std::span<std::optional<KernelAttempt>
const> runtime_results, Op
const& op,
254 return dispatch_diagnostics::event{
255 .
operation = std::string(std::remove_cvref_t<Op>::name),
256 .backend_attempts = make_kernel_backend_attempts(backends, runtime_results, op, args...),
261KernelDispatchError make_kernel_dispatch_error(backend_list<Backends...>
const& backends,
262 std::span<std::optional<KernelAttempt>
const> runtime_results,
263 Op
const& op, Args&&... args)
265 auto diagnostic = make_kernel_dispatch_diagnostic(backends, runtime_results, op, args...);
266 return KernelDispatchError(std::move(diagnostic.operation), Failure, std::move(diagnostic.backend_attempts));
269template <std::size_t Index,
class... Backends,
class Op,
class RecordAttempt,
class... Args>
270bool try_dispatch_kernel_at(backend_list<Backends...>
const& backends, Op
const& op, RecordAttempt& record_attempt,
273 if constexpr (Index ==
sizeof...(Backends))
279 using backend_type = std::tuple_element_t<Index, std::tuple<Backends...>>;
280 constexpr auto acceptance = backend_type_acceptance<backend_type, Op, Args...>();
285 record_attempt(Index, attempt);
292 record_attempt(Index, attempt);
299 return try_dispatch_kernel_at<Index + 1>(backends, op, record_attempt, args...);
303template <
class... Backends,
class Op,
class... Args>
304void dispatch_backend_list(backend_list<Backends...>
const& backends, Op
const& op, Args&&... args)
307 std::array<std::optional<KernelAttempt>,
sizeof...(Backends)> runtime_results{};
308 auto record_attempt = [&](std::size_t index,
KernelAttempt attempt) { runtime_results[index] = attempt; };
309 bool const succeeded = try_dispatch_kernel_at<0>(backends, op, record_attempt, args...);
310 auto const results = std::span<std::optional<KernelAttempt>
const>(runtime_results);
314 if (diagnostics_enabled)
316 auto diagnostic = make_kernel_dispatch_diagnostic(backends, results, op, args...);
317 dispatch_diagnostics::detail::emit(diagnostic);
322 if (diagnostics_enabled)
324 auto diagnostic = make_kernel_dispatch_diagnostic(backends, results, op, args...);
325 dispatch_diagnostics::detail::emit(diagnostic);
327 std::move(diagnostic.backend_attempts)));
331 make_kernel_dispatch_error<KernelDispatchFailure::all_candidates_declined>(backends, results, op, args...));
346template <
class BackendSelector,
class Op,
class... Args>
347 requires detail::KernelDispatchTypesAccepted<detail::normalized_backend_selector_t<BackendSelector>, Op, Args...>
353 constexpr auto backend_count = std::tuple_size_v<
decltype(backends.entries)>;
354 std::array<std::optional<KernelAttempt>, backend_count> runtime_results{};
355 auto record_attempt = [&](std::size_t index,
KernelAttempt attempt) { runtime_results[index] = attempt; };
356 bool const succeeded = detail::try_dispatch_kernel_at<0>(backends, op, record_attempt, args...);
357 auto diagnostic = detail::make_kernel_dispatch_diagnostic(
358 backends, std::span<std::optional<KernelAttempt>
const>(runtime_results), op, args...);
359 dispatch_diagnostics::detail::emit(diagnostic);
364 return detail::try_dispatch_kernel_at<0>(backends, op, ignore_attempt, args...);
372template <
class BackendSelector,
class Op,
class... Args>
373 requires detail::KernelDispatchTypesAccepted<detail::normalized_backend_selector_t<BackendSelector>, Op, Args...>
377 detail::dispatch_backend_list(backends, op, std::forward<Args>(args)...);
383template <
class BackendSelector,
class Op,
class... Args>
386 using backends_type = detail::normalized_backend_selector_t<BackendSelector>;
388 if constexpr (!detail::KernelDispatchTypesAccepted<backends_type, Op, Args...>)
392 auto diagnostic = detail::make_kernel_dispatch_diagnostic(
393 backends, std::span<std::optional<KernelAttempt>
const>{}, op, args...);
394 dispatch_diagnostics::detail::emit(diagnostic);
396 std::move(diagnostic.backend_attempts)));
398 trace::raise(detail::make_kernel_dispatch_error<KernelDispatchFailure::no_eligible_backend>(
399 backends, std::span<std::optional<KernelAttempt>
const>{}, op, args...));
403 detail::dispatch_backend_list(backends, op, std::forward<Args>(args)...);
Ordered backend selector values shared by tensor storage and linalg dispatch.
Exception carrying structured information about a failed backend walk.
Definition dispatch_error.hpp:93
Opt-in structured diagnostics for kernel-dispatch walks.
Structured kernel-dispatch failure data.
Presentation adapter for structured kernel-dispatch failures.
void raise(Exception &&exception, std::source_location where=std::source_location::current())
Raise a concrete exception through Uni20's configured error boundary.
Definition trace_impl.hpp:2184
bool enabled() noexcept
Return whether runtime kernel-dispatch diagnostics are enabled.
Definition dispatch_diagnostics.hpp:60
Definition linalg/async.hpp:28
constexpr bool kernel_attempt_succeeded(KernelAttempt attempt) noexcept
Return whether a backend performed the operation.
Definition kernel_attempt.hpp:40
KernelAttempt try_kernel(BlasBackend, gemm_op const &, C &c, S const &alpha, A &a, B &b, S const &beta)
Pack logical host values and execute GEMM through value-preserving provider conversion.
Definition linalg/backends/blas/binary80.hpp:30
constexpr KernelTypeAcceptance probe_dispatch_kernel(BackendSelector &&, Op const &, Args &&...)
Probe whether a backend selector can dispatch an operation for the argument types.
Definition dispatch.hpp:206
backend_list(Backends...) -> backend_list< std::remove_cvref_t< Backends >... >
KernelAttempt
Result of one runtime kernel backend attempt.
Definition kernel_attempt.hpp:27
KernelDispatchFailure
Classify why an ordered kernel-dispatch walk failed.
Definition dispatch_error.hpp:44
@ all_candidates_declined
constexpr auto normalize_backend_selector(backend_list< Backends... > selector) -> backend_list< Backends... >
Return an explicit backend list unchanged.
Definition backend_selector.hpp:259
typename detail::KernelTypeCandidates< detail::normalized_backend_selector_t< BackendSelector >, std::remove_cvref_t< Op >, Args... >::type kernel_type_candidates_t
Ordered backend-list type whose entries accept the operation argument types.
Definition dispatch.hpp:168
std::integral_constant< KernelTypeAcceptance, Value > KernelAcceptance
Type-level result returned by kernel_accepts_types customizations.
Definition dispatch_error.hpp:31
constexpr auto kernel_type_candidates(BackendSelector &&selector, Op const &, Args &&...)
Filter a backend selector to candidates accepting the call's argument types.
Definition dispatch.hpp:175
consteval KernelTypeAcceptance probe_dispatch_kernel_types()
Probe whether a backend selector type can dispatch operation argument types.
Definition dispatch.hpp:194
bool try_dispatch_kernel(BackendSelector &&selector, Op op, Args &&... args)
Normalize a selector and try each eligible backend until one performs the operation.
Definition dispatch.hpp:348
KernelTypeAcceptance
Type-level backend eligibility for an operation and argument set.
Definition dispatch_error.hpp:24
void dispatch_kernel(BackendSelector &&selector, Op op, Args &&... args)
Normalize a selector and dispatch or report that every eligible backend declined.
Definition dispatch.hpp:374
consteval auto kernel_accepts_types(BlasBackend, gemm_op const &, C &, S const &, A &, B &, S const &)
Accept host-readable native binary80 operands, including transforming accessors.
Definition linalg/backends/blas/binary80.hpp:19
void dynamic_dispatch_kernel(BackendSelector &&selector, Op op, Args &&... args)
Dynamically dispatch an operation, raising even type-level rejection at runtime.
Definition dispatch.hpp:384
std::string operation
Definition dispatch_diagnostics.hpp:25
#define CHECK(cond,...)
Definition trace.hpp:299