|
| 1 | +# Implementation design for `sycl::any_device_has` and `sycl::all_devices_have` |
| 2 | + |
| 3 | +This design document describes the implementation of the SYCL 2020 device aspect |
| 4 | +traits `any_device_has` and `all_devices_have` as described in the |
| 5 | +[SYCL 2020 Specification Rev. 6 Section 4.6.4.3][1]. |
| 6 | + |
| 7 | +In summary, `any_device_has<aspect>` and `all_devices_have<aspect>` must inherit |
| 8 | +from either `std::true_t` or `std::false_t` depending on whether the |
| 9 | +corresponding compilation environment can guarantee that any and all the |
| 10 | +supported devices support the `aspect`. Since DPC++ allows for compiling for |
| 11 | +multiple targets, these traits can be different when compiling for the |
| 12 | +individual targets and on host. |
| 13 | + |
| 14 | +The design of these traits is inspired by the implementation of the |
| 15 | +[sycl\_ext\_oneapi\_device\_if][2] and |
| 16 | +[sycl\_ext\_oneapi\_device\_architecture][3] extensions as described in |
| 17 | +[DeviceIf.md][4]. Additionally, it leverages part of the design for optional |
| 18 | +kernel features, as described in [OptionalDeviceFeatures.md][5]. |
| 19 | + |
| 20 | +## Changes to the compiler driver |
| 21 | + |
| 22 | +Using the `-fsycl-targets` options introduced in [DeviceIf.md][4] and the |
| 23 | +configuration file introduced in [OptionalDeviceFeatures.md][5], the compiler |
| 24 | +driver finds the set of all aspects supported by each specified target. Note |
| 25 | +that in this section we refer to aspects as their integral representation as |
| 26 | +specified in the device headers rather than by the names specified in the |
| 27 | +[SYCL 2020 specification][1]. |
| 28 | + |
| 29 | +For each target $t$ in `-fsycl-targets`, let $A^{any}_t$ be the set of aspects |
| 30 | +supported by any device supporting $t$ and let $A^{all}_t$ be the set of aspects |
| 31 | +supported by all devices supporting $t$. If $t$ has an entry in the |
| 32 | +configuration file, these sets are defined by the `aspects` list in that entry |
| 33 | +and $A^{any}_t = A^{all}_t$. If there is no entry for $t$ in the configuration |
| 34 | +file, then $A^{any}_t$ is the set of all aspects and $A^{all}_t = \emptyset$. |
| 35 | + |
| 36 | +In the device-side compilation of a SYCL program for $t$ the driver defines the |
| 37 | +following macros: |
| 38 | +* `__SYCL_ALL_DEVICES_HAVE_`$i$`__` as `1` for all $i$ in $A^{all}_t$. |
| 39 | +* `__SYCL_ANY_DEVICE_HAS_ANY_ASPECT__` as `1` if $A^{any}_t$ is the set of all |
| 40 | +aspects. |
| 41 | +* `__SYCL_ANY_DEVICE_HAS_`$j$`__` as `1` for all $j$ in $A^{any}_t$ if |
| 42 | +`__SYCL_ANY_DEVICE_HAS_ANY_ASPECT__` was not defined. |
| 43 | + |
| 44 | +In the host-side compilation of a SYCL program, where $[t1, t2, \ldots, tn]$ are |
| 45 | +the $n$ targets specified in `-fsycl-targets`, the driver defines the following |
| 46 | +macros: |
| 47 | +* `__SYCL_ALL_DEVICES_HAVE_`$i$`__` as `1` for all $i$ in |
| 48 | +${\bigcap}^n_{k=1} A^{all}_{tk}$. |
| 49 | +* `__SYCL_ANY_DEVICE_HAS_ANY_ASPECT__` as `1` if |
| 50 | +${\bigcup}^n_{k=1} A^{any}_{tk}$ is the set of all aspects. |
| 51 | +* `__SYCL_ANY_DEVICE_HAS_`$j$`__` as `1` for all $j$ in |
| 52 | +${\bigcup}^n_{k=1} A^{any}_{tk}$ if `__SYCL_ANY_DEVICE_HAS_ANY_ASPECT__` was not |
| 53 | +defined. |
| 54 | + |
| 55 | +Note that the need for the `__SYCL_ANY_DEVICE_HAS_ANY_ASPECT__` macro is |
| 56 | +due to the special case where the driver finds no configuration for a target and |
| 57 | +must assume that there exists some device that supports any given aspect. Since |
| 58 | +the driver has no way of knowing all possible aspects, we use a catch-all macro |
| 59 | +to denote this case instead. This is not needed for $A^{all}_t$ for any target |
| 60 | +$t$, as the driver will always know all relevant aspects. |
| 61 | + |
| 62 | +## Changes to the device headers |
| 63 | + |
| 64 | +Using the macros defined by the driver, the device headers define the traits |
| 65 | +together with specializations for each aspect: |
| 66 | + |
| 67 | +```c++ |
| 68 | +namespace sycl { |
| 69 | +template <aspect Aspect> all_devices_have; |
| 70 | +template<> all_devices_have<aspect::host> : std::bool_constant<__SYCL_ALL_DEVICES_HAVE_0__ + 0> {}; |
| 71 | +template<> all_devices_have<aspect::cpu> : std::bool_constant<__SYCL_ALL_DEVICES_HAVE_1__ + 0> {}; |
| 72 | +template<> all_devices_have<aspect::gpu> : std::bool_constant<__SYCL_ALL_DEVICES_HAVE_2__ + 0> {}; |
| 73 | +... |
| 74 | + |
| 75 | +#ifdef __SYCL_ANY_DEVICE_HAS_ANY_ASPECT__ |
| 76 | +// Special case where any_device_has is trivially true. |
| 77 | +template <aspect Aspect> any_device_has : std::true_t {}; |
| 78 | +#else |
| 79 | +template <aspect Aspect> any_device_has; |
| 80 | +template<> any_device_has<aspect::host> : std::bool_constant<__SYCL_ANY_DEVICE_HAS_0__ + 0> {}; |
| 81 | +template<> any_device_has<aspect::cpu> : std::bool_constant<__SYCL_ANY_DEVICE_HAS_1__ + 0> {}; |
| 82 | +template<> any_device_has<aspect::gpu> : std::bool_constant<__SYCL_ANY_DEVICE_HAS_2__ + 0> {}; |
| 83 | +... |
| 84 | +#endif // __SYCL_ANY_DEVICE_HAS_ANY_ASPECT__ |
| 85 | + |
| 86 | +template <aspect Aspect> constexpr bool all_devices_have_v = all_devices_have<Aspect>::value; |
| 87 | +template <aspect Aspect> constexpr bool any_device_has_v = any_device_has<Aspect>::value; |
| 88 | +} // namespace sycl |
| 89 | +``` |
| 90 | +
|
| 91 | +Note that the driver may not define macros for all aspects, so the `+ 0` is |
| 92 | +used to ensure the boolean constant value of the specializations become `false` |
| 93 | +when the corresponding macro is undefined. |
| 94 | +
|
| 95 | +Since the specializations need to be explicitly specified, there is a high |
| 96 | +probability of mistakes when new aspects are added. To avoid such mistakes, a |
| 97 | +SYCL unit-test uses the [aspects.def](../../include/sycl/info/aspects.def) file |
| 98 | +to generate test cases, ensuring that specializations exist for all aspects: |
| 99 | +
|
| 100 | +```c++ |
| 101 | +#define __SYCL_ASPECT(ASPECT, ASPECT_VAL) \ |
| 102 | + constexpr bool CheckAnyDeviceHas##ASPECT = any_devices_has_v<aspect::ASPECT>; \ |
| 103 | + constexpr bool CheckAllDevicesHave##ASPECT = all_devices_have_v<aspect::ASPECT>; |
| 104 | +
|
| 105 | +#include <sycl/info/aspects.def> |
| 106 | +
|
| 107 | +#undef __SYCL_ASPECT |
| 108 | +``` |
| 109 | + |
| 110 | +This relies on the fact that unspecialized variants of `any_device_has` and |
| 111 | +`all_devices_have` are undefined. |
| 112 | + |
| 113 | +[1]: <https://registry.khronos.org/SYCL/specs/sycl-2020/html/sycl-2020.html#sec:device-aspects> |
| 114 | +[2]: <../extensions/proposed/sycl_ext_oneapi_device_if.asciidoc> |
| 115 | +[3]: <../extensions/proposed/sycl_ext_oneapi_device_architecture.asciidoc> |
| 116 | +[4]: <DeviceIf.md> |
| 117 | +[5]: <OptionalDeviceFeatures.md> |
0 commit comments