10
10
11
11
#include < cstdint> // for uint64_t
12
12
#include < optional>
13
+ #include < utility> // for std::integer_sequence
13
14
14
15
namespace sycl {
15
16
inline namespace _V1 {
@@ -1009,6 +1010,163 @@ template <bool MakeCall> class if_architecture_helper {
1009
1010
1010
1011
namespace ext ::oneapi::experimental {
1011
1012
1013
+ namespace detail {
1014
+ // Call the callable object "fn" only when this code runs on a device which
1015
+ // has a certain set of aspects or a particular architecture.
1016
+ //
1017
+ // Condition is a parameter pack of int's that define a simple expression
1018
+ // language which tells the set of aspects or architectures that the device
1019
+ // must have in order to enable the call. See the "Condition*" values below.
1020
+ template <typename T, typename ... Condition>
1021
+ #ifdef __SYCL_DEVICE_ONLY__
1022
+ [[__sycl_detail__::add_ir_attributes_function(
1023
+ " sycl-call-if-on-device-conditionally" , true )]]
1024
+ #endif
1025
+ void call_if_on_device_conditionally (T fn, Condition...) {
1026
+ fn ();
1027
+ }
1028
+
1029
+ // The "Condition" parameter pack above is a sequence of int's that define an
1030
+ // expression tree. Each node represents a boolean subexpression:
1031
+ //
1032
+ // ConditionAspect - Next int is a value from "enum aspect". The
1033
+ // subexpression is true if the device has this
1034
+ // aspect.
1035
+ // ConditionArchitecture - Next int is a value from "enum architecture". The
1036
+ // subexpression is true if the device has this
1037
+ // architecture.
1038
+ // ConditionNot - Next int is the root of another subexpression S1.
1039
+ // This subexpression is true if S1 is false.
1040
+ // ConditionAnd - Next int is the root of another subexpression S1.
1041
+ // The int following that subexpression is the root
1042
+ // of another subexpression S2. This subexpression
1043
+ // is true if both S1 and S2 are true.
1044
+ // ConditionOr - Next int is the root of another subexpression S1.
1045
+ // The int following that subexpression is the root
1046
+ // of another subexpression S2. This subexpression
1047
+ // is true if either S1 or S2 are true.
1048
+ //
1049
+ // These values are stored in the application's executable, so they are
1050
+ // effectively part of the ABI. Therefore, any change to an existing value
1051
+ // is an ABI break.
1052
+ //
1053
+ // There is no programmatic reason for the values to be negative. They are
1054
+ // negative only by convention to make it easier for humans to distinguish them
1055
+ // from aspect or architecture values (which are positive).
1056
+ static constexpr int ConditionAspect = -1 ;
1057
+ static constexpr int ConditionArchitecture = -2 ;
1058
+ static constexpr int ConditionNot = -3 ;
1059
+ static constexpr int ConditionAnd = -4 ;
1060
+ static constexpr int ConditionOr = -5 ;
1061
+
1062
+ // Metaprogramming helper to construct a ConditionOr expression for a sequence
1063
+ // of architectures. "ConditionAnyArchitectureBuilder<Archs...>::seq" is an
1064
+ // "std::integer_sequence" representing the expression.
1065
+ template <architecture... Archs> struct ConditionAnyArchitectureBuilder ;
1066
+
1067
+ template <architecture Arch, architecture... Archs>
1068
+ struct ConditionAnyArchitectureBuilder <Arch, Archs...> {
1069
+ template <int I1, int I2, int I3, int ... Is>
1070
+ static auto append (std::integer_sequence<int , Is...>) {
1071
+ return std::integer_sequence<int , I1, I2, I3, Is...>{};
1072
+ }
1073
+ using rest = typename ConditionAnyArchitectureBuilder<Archs...>::seq;
1074
+ static constexpr int arch = static_cast <int >(Arch);
1075
+ using seq =
1076
+ decltype (append<ConditionOr, ConditionArchitecture, arch>(rest{}));
1077
+ };
1078
+
1079
+ template <architecture Arch> struct ConditionAnyArchitectureBuilder <Arch> {
1080
+ static constexpr int arch = static_cast <int >(Arch);
1081
+ using seq = std::integer_sequence<int , ConditionArchitecture, arch>;
1082
+ };
1083
+
1084
+ // Metaprogramming helper to construct a ConditionNot expression.
1085
+ // ConditionNotBuilder<Exp>::seq" is an "std::integer_sequence" representing
1086
+ // the expression.
1087
+ template <typename Exp> struct ConditionNotBuilder {
1088
+ template <int I, int ... Is>
1089
+ static auto append (std::integer_sequence<int , Is...>) {
1090
+ return std::integer_sequence<int , I, Is...>{};
1091
+ }
1092
+ using rest = typename Exp::seq;
1093
+ using seq = decltype (append<ConditionNot>(rest{}));
1094
+ };
1095
+
1096
+ // Metaprogramming helper to construct a ConditionAnd expression.
1097
+ // "ConditionAndBuilder<Exp1, Exp2>::seq" is an "std::integer_sequence"
1098
+ // representing the expression.
1099
+ template <typename Exp1, typename Exp2> struct ConditionAndBuilder {
1100
+ template <int I, int ... I1s, int ... I2s>
1101
+ static auto append (std::integer_sequence<int , I1s...>,
1102
+ std::integer_sequence<int , I2s...>) {
1103
+ return std::integer_sequence<int , I, I1s..., I2s...>{};
1104
+ }
1105
+ using rest1 = typename Exp1::seq;
1106
+ using rest2 = typename Exp2::seq;
1107
+ using seq = decltype (append<ConditionAnd>(rest1{}, rest2{}));
1108
+ };
1109
+
1110
+ // Metaprogramming helper to construct a ConditionOr expression.
1111
+ // "ConditionOrBuilder<Exp1, Exp2>::seq" is an "std::integer_sequence"
1112
+ // representing the expression.
1113
+ template <typename Exp1, typename Exp2> struct ConditionOrBuilder {
1114
+ template <int I, int ... I1s, int ... I2s>
1115
+ static auto append (std::integer_sequence<int , I1s...>,
1116
+ std::integer_sequence<int , I2s...>) {
1117
+ return std::integer_sequence<int , I, I1s..., I2s...>{};
1118
+ }
1119
+ using rest1 = typename Exp1::seq;
1120
+ using rest2 = typename Exp2::seq;
1121
+ using seq = decltype (append<ConditionOr>(rest1{}, rest2{}));
1122
+ };
1123
+
1124
+ // Helper function to call call_if_on_device_conditionally() while converting
1125
+ // the "std::integer_sequence" for a condition expression into individual
1126
+ // arguments of type int.
1127
+ template <typename T, int ... Is>
1128
+ void call_if_on_device_conditionally_helper (T fn,
1129
+ std::integer_sequence<int , Is...>) {
1130
+ call_if_on_device_conditionally (fn, Is...);
1131
+ }
1132
+
1133
+ // Same sort of helper object for "else_if_architecture_is".
1134
+ template <typename MakeCallIf> class if_architecture_is_helper {
1135
+ public:
1136
+ template <architecture... Archs, typename T,
1137
+ typename = std::enable_if<std::is_invocable_v<T>>>
1138
+ auto else_if_architecture_is (T fn) {
1139
+ using make_call_if =
1140
+ ConditionAndBuilder<MakeCallIf,
1141
+ ConditionAnyArchitectureBuilder<Archs...>>;
1142
+ using make_else_call_if = ConditionAndBuilder<
1143
+ MakeCallIf,
1144
+ ConditionNotBuilder<ConditionAnyArchitectureBuilder<Archs...>>>;
1145
+
1146
+ using cond = typename make_call_if::seq;
1147
+ call_if_on_device_conditionally_helper (fn, cond{});
1148
+ return if_architecture_is_helper<make_else_call_if>{};
1149
+ }
1150
+
1151
+ template <typename T> void otherwise (T fn) {
1152
+ using cond = typename MakeCallIf::seq;
1153
+ call_if_on_device_conditionally_helper (fn, cond{});
1154
+ }
1155
+ };
1156
+
1157
+ } // namespace detail
1158
+
1159
+ #ifdef SYCL_EXT_ONEAPI_DEVICE_ARCHITECTURE_NEW_DESIGN_IMPL
1160
+ template <architecture... Archs, typename T>
1161
+ static auto if_architecture_is (T fn) {
1162
+ using make_call_if = detail::ConditionAnyArchitectureBuilder<Archs...>;
1163
+ using make_else_call_if = detail::ConditionNotBuilder<make_call_if>;
1164
+
1165
+ using cond = typename make_call_if::seq;
1166
+ detail::call_if_on_device_conditionally_helper (fn, cond{});
1167
+ return detail::if_architecture_is_helper<make_else_call_if>{};
1168
+ }
1169
+ #else
1012
1170
// / The condition is `true` only if the device which executes the
1013
1171
// / `if_architecture_is` function has any one of the architectures listed in the
1014
1172
// / @tparam Archs pack.
@@ -1026,6 +1184,7 @@ constexpr static auto if_architecture_is(T fn) {
1026
1184
return sycl::detail::if_architecture_helper<true >{};
1027
1185
}
1028
1186
}
1187
+ #endif // SYCL_EXT_ONEAPI_DEVICE_ARCHITECTURE_NEW_DESIGN_IMPL
1029
1188
1030
1189
// / The condition is `true` only if the device which executes the
1031
1190
// / `if_architecture_is` function has an architecture that is in any one of the
0 commit comments