|
| 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`. |
| 11 | + |
| 12 | +The design of these traits is inspired by the implementation of the |
| 13 | +[sycl\_ext\_oneapi\_device\_if][2] and |
| 14 | +[sycl\_ext\_oneapi\_device\_architecture][3] extensions as described in |
| 15 | +[DeviceIf.md][4]. Additionally, it leverages part of the design for optional |
| 16 | +kernel features, as described in [OptionalDeviceFeatures.md][5]. |
| 17 | + |
| 18 | +## Changes to the compiler driver |
| 19 | + |
| 20 | +Using the `-fsycl-targets` options introduced in [DeviceIf.md][4] and the |
| 21 | +configuration file introduced in [OptionalDeviceFeatures.md][5], the compiler |
| 22 | +driver finds the set of all aspects supported by each specified target. Note |
| 23 | +that in this section we refer to aspects as their integral representation as |
| 24 | +specified in the device headers rather than by the names specified in the |
| 25 | +[SYCL 2020 specification][1]. |
| 26 | + |
| 27 | +For each target $t$ in `-fsycl-targets`, let $A^{any}_t$ be the set of aspects |
| 28 | +supported by any device supporting $t$ and let $A^{all}_t$ be the set of aspects |
| 29 | +supported by all devices supporting $t$. These sets are defined as follows: |
| 30 | +* If $t$ has an entry in the configuration file, $A^{all}_t$ is the same as the |
| 31 | +`aspects` list in that entry. |
| 32 | +* If $t$ does not have an entry in the configuration file, $A^{all}_t$ is empty. |
| 33 | +* If $t$ has an entry in the configuration file and the entry has a value |
| 34 | +`may_support_other_aspects` set to `false`, $A^{any}_t$ is the same as the |
| 35 | +`aspects` list in that entry. |
| 36 | +* If $t$ does not have an entry the configuration file or the entry has a value |
| 37 | +`may_support_other_aspects` set to `true`, $A^{any}_t$ is the set of all |
| 38 | +aspects. |
| 39 | + |
| 40 | +For example, the target `intel_gpu_dg1` is supported by a specific device (DG1) |
| 41 | +and as such would have an entry in the configuration file with `aspects` being |
| 42 | +the set of aspects that device supports. Likewise it would have |
| 43 | +`may_support_other_aspects` set to `false` as there will be no other devices |
| 44 | +supporting this target, meaning there will never be any devices supporting |
| 45 | +the target and supporting anything not in `aspects`. In contrast, the target |
| 46 | +`nvidia_gpu_sm_80` is supported by CUDA devices with `sm_80` architecture or |
| 47 | +newer, so its entry in the configuration file would have |
| 48 | +`may_support_other_aspects` set to `true` to indicate that there could be future |
| 49 | +devices that support aspects not in `aspects`, while it is known that all |
| 50 | +current and future devices must support the aspects in `aspects`. Lastly, the |
| 51 | +default JIT SPIR-V target (`spir64`) should not have an entry in the |
| 52 | +configuration file as it cannot guarantee anything about the devices supporting |
| 53 | +the target. |
| 54 | + |
| 55 | +When compiling a SYCL program, where $[t1, t2, \ldots, tn]$ are the $n$ targets |
| 56 | +specified in `-fsycl-targets` including any targets implicitly added by the |
| 57 | +driver, the driver defines the following macros in both host and device |
| 58 | +compilation invocations: |
| 59 | +* `__SYCL_ALL_DEVICES_HAVE_`$i$`__` as `1` for all $i$ in |
| 60 | +${\bigcap}^n_{k=1} A^{all}_{tk}$. |
| 61 | +* `__SYCL_ANY_DEVICE_HAS_ANY_ASPECT__` as `1` if |
| 62 | +${\bigcup}^n_{k=1} A^{any}_{tk}$ is the set of all aspects. |
| 63 | +* `__SYCL_ANY_DEVICE_HAS_`$j$`__` as `1` for all $j$ in |
| 64 | +${\bigcup}^n_{k=1} A^{any}_{tk}$ if `__SYCL_ANY_DEVICE_HAS_ANY_ASPECT__` was not |
| 65 | +defined. |
| 66 | + |
| 67 | +Note that the need for the `__SYCL_ANY_DEVICE_HAS_ANY_ASPECT__` macro is |
| 68 | +due to the special case where the driver finds no configuration for a target and |
| 69 | +must assume that there exists some device that supports any given aspect. Since |
| 70 | +the driver has no way of knowing all possible aspects, we use a catch-all macro |
| 71 | +to denote this case instead. This is not needed for $A^{all}_t$ for any target |
| 72 | +$t$, as it will always be a finite set of aspects. |
| 73 | + |
| 74 | +## Changes to the device headers |
| 75 | + |
| 76 | +Using the macros defined by the driver, the device headers define the traits |
| 77 | +together with specializations for each aspect: |
| 78 | + |
| 79 | +```c++ |
| 80 | +namespace sycl { |
| 81 | +template <aspect Aspect> all_devices_have; |
| 82 | +template<> all_devices_have<aspect::host> : std::bool_constant<__SYCL_ALL_DEVICES_HAVE_0__> {}; |
| 83 | +template<> all_devices_have<aspect::cpu> : std::bool_constant<__SYCL_ALL_DEVICES_HAVE_1__> {}; |
| 84 | +template<> all_devices_have<aspect::gpu> : std::bool_constant<__SYCL_ALL_DEVICES_HAVE_2__> {}; |
| 85 | +... |
| 86 | + |
| 87 | +#ifdef __SYCL_ANY_DEVICE_HAS_ANY_ASPECT__ |
| 88 | +// Special case where any_device_has is trivially true. |
| 89 | +template <aspect Aspect> any_device_has : std::true_t {}; |
| 90 | +#else |
| 91 | +template <aspect Aspect> any_device_has; |
| 92 | +template<> any_device_has<aspect::host> : std::bool_constant<__SYCL_ANY_DEVICE_HAS_0__> {}; |
| 93 | +template<> any_device_has<aspect::cpu> : std::bool_constant<__SYCL_ANY_DEVICE_HAS_1__> {}; |
| 94 | +template<> any_device_has<aspect::gpu> : std::bool_constant<__SYCL_ANY_DEVICE_HAS_2__> {}; |
| 95 | +... |
| 96 | +#endif // __SYCL_ANY_DEVICE_HAS_ANY_ASPECT__ |
| 97 | + |
| 98 | +template <aspect Aspect> constexpr bool all_devices_have_v = all_devices_have<Aspect>::value; |
| 99 | +template <aspect Aspect> constexpr bool any_device_has_v = any_device_has<Aspect>::value; |
| 100 | +} // namespace sycl |
| 101 | +``` |
| 102 | +
|
| 103 | +Note that the driver may not define macros for all aspects as it only knows the |
| 104 | +specified subset from the configuration file. As such the device headers will |
| 105 | +have to define any undefined `__SYCL_ANY_DEVICE_HAS_`$i$`__` and |
| 106 | +`__SYCL_ALL_DEVICES_HAVE_`$i$`__` as `0` for all aspect values $i$. |
| 107 | +
|
| 108 | +Since the specializations need to be explicitly specified, there is a high |
| 109 | +probability of mistakes when new aspects are added. To avoid such mistakes, a |
| 110 | +SYCL unit-test uses the [aspects.def](../../include/sycl/info/aspects.def) file |
| 111 | +to generate test cases, ensuring that specializations exist for all aspects: |
| 112 | +
|
| 113 | +```c++ |
| 114 | +#define __SYCL_ASPECT(ASPECT, ASPECT_VAL) \ |
| 115 | + constexpr bool CheckAnyDeviceHas##ASPECT = any_devices_has_v<aspect::ASPECT>; \ |
| 116 | + constexpr bool CheckAllDevicesHave##ASPECT = all_devices_have_v<aspect::ASPECT>; |
| 117 | +
|
| 118 | +#include <sycl/info/aspects.def> |
| 119 | +
|
| 120 | +#undef __SYCL_ASPECT |
| 121 | +``` |
| 122 | + |
| 123 | +This relies on the fact that unspecialized variants of `any_device_has` and |
| 124 | +`all_devices_have` are undefined. |
| 125 | + |
| 126 | +[1]: <https://registry.khronos.org/SYCL/specs/sycl-2020/html/sycl-2020.html#sec:device-aspects> |
| 127 | +[2]: <../extensions/proposed/sycl_ext_oneapi_device_if.asciidoc> |
| 128 | +[3]: <../extensions/proposed/sycl_ext_oneapi_device_architecture.asciidoc> |
| 129 | +[4]: <DeviceIf.md> |
| 130 | +[5]: <OptionalDeviceFeatures.md> |
0 commit comments