Skip to content

Commit fd439fb

Browse files
[SYCL] Update sycl_ext_oneapi_device_architecture device headers to support JIT (#13434)
This patch updates device headers of sycl_ext_oneapi_device_architecture extension to prepare to support API in JIT. New functionality is always disabled now as it breaks the existing functionality, and will be enabled when the feature is fully implemented by updating other components.
1 parent c6f5618 commit fd439fb

File tree

2 files changed

+194
-0
lines changed

2 files changed

+194
-0
lines changed

sycl/include/sycl/ext/oneapi/experimental/device_architecture.hpp

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
#include <cstdint> // for uint64_t
1212
#include <optional>
13+
#include <utility> // for std::integer_sequence
1314

1415
namespace sycl {
1516
inline namespace _V1 {
@@ -1009,6 +1010,163 @@ template <bool MakeCall> class if_architecture_helper {
10091010

10101011
namespace ext::oneapi::experimental {
10111012

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
10121170
/// The condition is `true` only if the device which executes the
10131171
/// `if_architecture_is` function has any one of the architectures listed in the
10141172
/// @tparam Archs pack.
@@ -1026,6 +1184,7 @@ constexpr static auto if_architecture_is(T fn) {
10261184
return sycl::detail::if_architecture_helper<true>{};
10271185
}
10281186
}
1187+
#endif // SYCL_EXT_ONEAPI_DEVICE_ARCHITECTURE_NEW_DESIGN_IMPL
10291188

10301189
/// The condition is `true` only if the device which executes the
10311190
/// `if_architecture_is` function has an architecture that is in any one of the
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// The goal of this test is to check that new design of
2+
// sycl_ext_oneapi_device_architecture extension can be compiled successfullly.
3+
// During binary run there are some errors, this is expected, so there is no run
4+
// line yet for this test.
5+
6+
// RUN: %clangxx -fsycl -DSYCL_EXT_ONEAPI_DEVICE_ARCHITECTURE_NEW_DESIGN_IMPL %s -o %t.out
7+
8+
#include <sycl/ext/oneapi/experimental/device_architecture.hpp>
9+
#include <sycl/sycl.hpp>
10+
11+
using namespace sycl;
12+
using namespace sycl::ext::oneapi::experimental;
13+
14+
int main() {
15+
std::vector<int> vec(4);
16+
{
17+
buffer<int> buf(vec.data(), vec.size());
18+
19+
queue q(gpu_selector_v);
20+
21+
// test if_architecture_is
22+
q.submit([&](handler &cgh) {
23+
auto acc = buf.get_access<access::mode::read_write>(cgh);
24+
cgh.single_task([=]() {
25+
if_architecture_is<architecture::intel_gpu_pvc>([&]() {
26+
acc[0] = 2;
27+
}).otherwise([&]() { acc[0] = 1; });
28+
});
29+
});
30+
}
31+
32+
assert(vec[0] == 1);
33+
34+
return 0;
35+
}

0 commit comments

Comments
 (0)