1/* Copyright (C) 2013 Povilas Kanapickas <povilas@radix.lt>
2
3 Distributed under the Boost Software License, Version 1.0.
4 (See accompanying file LICENSE_1_0.txt or copy at
5 http://www.boost.org/LICENSE_1_0.txt)
6*/
7
8#ifndef LIBSIMDPP_DISPATCHER_H
9#define LIBSIMDPP_DISPATCHER_H
10
11#ifndef LIBSIMDPP_SIMD_H
12 #error "This file must be included through simd.h"
13#endif
14
15#include <algorithm>
16#include <cstdlib>
17#include <simdpp/dispatch/arch.h>
18
19namespace simdpp {
20
21/** @def SIMDPP_USER_ARCH_INFO
22 The user must define this macro if he wants to use the dispatcher
23 infrastructure. The macro must evaluate to a constant expression that could
24 implicitly initialize an object of type @c std::function<Arch()>.
25
26 The macro is used by @c SIMDPP_MAKE_DISPATCHER_* to specify function which
27 is invoked to determine which version of the dispatched function to
28 dispatch. The macro is just a more convenient method to pass a parameter
29 which has high chance to be the same in the majority of use cases. The user
30 may redefine the macro and use different definitions for each site of
31 @c SIMDPP_MAKE_DISPATCHER_* expansion.
32
33 The function identified by the @c SIMDPP_USER_ARCH_INFO is called at the
34 first time the specific dispatcher is invoked. The user must ensure that
35 proper synchronization is used if the dispatcher is called concurrently.
36
37 The user must ensure that the returned information is sensible: e.g. SSE2
38 must be supported if SSE3 support is indicated.
39
40 The @c simdpp/dispatch/get_arch_*.h files provide several ready
41 implementations of CPU features detection.
42*/
43
44namespace detail {
45
46using VoidFunPtr = void (*)();
47
48struct FnVersion {
49 /* Identifies the instruction support that is needed for this version to
50 run.
51 */
52 Arch needed_arch;
53
54 /* The type-erased function pointer. Only non-member functions are
55 supported.
56
57 We type-erase this pointer to reduce the code bloat by allowing @c
58 select_version to be shared across all dispatchers.
59
60 Note, that we can't reinterpret_cast to void*, as that is undefined
61 behavior in C++. reinterpret_cast to another function pointer
62 type is allowed fortunately.
63 */
64 VoidFunPtr fun_ptr;
65
66 /* Optional string identifier identifying the architecture. */
67 const char* arch_name;
68};
69
70inline FnVersion select_version_any(FnVersion* versions, unsigned size,
71 Arch arch)
72{
73 // No need to try to be very efficient here.
74 std::sort(versions, versions + size,
75 [](const FnVersion& lhs, const FnVersion& rhs) {
76 return lhs.needed_arch > rhs.needed_arch;
77 });
78
79 unsigned i;
80 for (i = 0; i < size; ++i) {
81 if (versions[i].fun_ptr == nullptr)
82 continue;
83 if (test_arch_subset(arch, versions[i].needed_arch)) {
84 break;
85 }
86 }
87 if (i == size) {
88 // The user didn't provide the NONE_NULL version and no SIMD
89 // architecture is supported. We can't do anything except to abort
90 std::abort();
91 }
92 return versions[i];
93}
94
95} // namespace detail
96} // namespace simdpp
97
98#endif
99