Skip to content

Commit c62860f

Browse files
[SYCL] Add support for composite specialization constants to SYCL RT (#2797)
Extended API to accept composite types as spec constants. Added handling of new device image property describing composite spec constants. Aligned layout of spec_constant class between host and device code.
1 parent 0bd6bb1 commit c62860f

File tree

11 files changed

+290
-29
lines changed

11 files changed

+290
-29
lines changed

sycl/include/CL/sycl/ONEAPI/experimental/spec_constant.hpp

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
#pragma once
1919

20+
#include <CL/sycl/detail/stl_type_traits.hpp>
2021
#include <CL/sycl/detail/sycl_fe_intrins.hpp>
2122
#include <CL/sycl/exception.hpp>
2223

@@ -41,11 +42,15 @@ template <typename T, typename ID = T> class spec_constant {
4142
spec_constant(T Cst) : Val(Cst) {}
4243

4344
T Val;
44-
#endif
45+
#else
46+
char padding[sizeof(T)];
47+
#endif // __SYCL_DEVICE_ONLY__
4548
friend class cl::sycl::program;
4649

4750
public:
48-
T get() const { // explicit access.
51+
template <typename V = T>
52+
typename sycl::detail::enable_if_t<std::is_arithmetic<V>::value, V>
53+
get() const { // explicit access.
4954
#ifdef __SYCL_DEVICE_ONLY__
5055
const char *TName = __builtin_unique_stable_name(ID);
5156
return __sycl_getSpecConstantValue<T>(TName);
@@ -54,6 +59,19 @@ template <typename T, typename ID = T> class spec_constant {
5459
#endif // __SYCL_DEVICE_ONLY__
5560
}
5661

62+
template <typename V = T>
63+
typename sycl::detail::enable_if_t<std::is_class<V>::value &&
64+
std::is_pod<V>::value,
65+
V>
66+
get() const { // explicit access.
67+
#ifdef __SYCL_DEVICE_ONLY__
68+
const char *TName = __builtin_unique_stable_name(ID);
69+
return __sycl_getCompositeSpecConstantValue<T>(TName);
70+
#else
71+
return Val;
72+
#endif // __SYCL_DEVICE_ONLY__
73+
}
74+
5775
operator T() const { // implicit conversion.
5876
return get();
5977
}

sycl/include/CL/sycl/detail/pi.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -640,7 +640,12 @@ static const uint8_t PI_DEVICE_BINARY_OFFLOAD_KIND_SYCL = 4;
640640
/// Name must be consistent with
641641
/// PropertySetRegistry::SYCL_SPECIALIZATION_CONSTANTS defined in
642642
/// PropertySetIO.h
643-
#define __SYCL_PI_PROPERTY_SET_SPEC_CONST_MAP "SYCL/specialization constants"
643+
#define __SYCL_PI_PROPERTY_SET_SCALAR_SPEC_CONST_MAP \
644+
"SYCL/specialization constants"
645+
/// PropertySetRegistry::SYCL_COMPOSITE_SPECIALIZATION_CONSTANTS defined in
646+
/// PropertySetIO.h
647+
#define __SYCL_PI_PROPERTY_SET_COMPOSITE_SPEC_CONST_MAP \
648+
"SYCL/composite specialization constants"
644649
/// PropertySetRegistry::SYCL_DEVICELIB_REQ_MASK defined in PropertySetIO.h
645650
#define __SYCL_PI_PROPERTY_SET_DEVICELIB_REQ_MASK "SYCL/devicelib req mask"
646651
/// PropertySetRegistry::SYCL_KERNEL_PARAM_OPT_INFO defined in PropertySetIO.h

sycl/include/CL/sycl/detail/pi.hpp

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -364,11 +364,32 @@ class DeviceBinaryImage {
364364
return Format;
365365
}
366366

367-
/// Gets the iterator range over specialization constants in this this binary
368-
/// image. For each property pointed to by an iterator within the range, the
369-
/// name of the property is the specializaion constant symbolic ID and the
370-
/// value is 32-bit unsigned integer ID.
371-
const PropertyRange &getSpecConstants() const { return SpecConstIDMap; }
367+
/// Gets the iterator range over scalar specialization constants in this
368+
/// binary image. For each property pointed to by an iterator within the
369+
/// range, the name of the property is the specialization constant symbolic ID
370+
/// and the value is 32-bit unsigned integer ID.
371+
const PropertyRange &getScalarSpecConstants() const {
372+
return ScalarSpecConstIDMap;
373+
}
374+
/// Gets the iterator range over composite specialization constants in this
375+
/// binary image. For each property pointed to by an iterator within the
376+
/// range, the name of the property is the specialization constant symbolic ID
377+
/// and the value is a list of tuples of 32-bit unsigned integer values, which
378+
/// encode scalar specialization constants, that form the composite one.
379+
/// Each tuple consists of ID of scalar specialization constant, its location
380+
/// within a composite (offset in bytes from the beginning) and its size.
381+
/// For example, for the following structure:
382+
/// struct A { int a; float b; };
383+
/// struct POD { A a[2]; int b; };
384+
/// List of tuples will look like:
385+
/// { ID0, 0, 4 }, // .a[0].a
386+
/// { ID1, 4, 4 }, // .a[0].b
387+
/// { ID2, 8, 4 }, // .a[1].a
388+
/// { ID3, 12, 4 }, // .a[1].b
389+
/// { ID4, 16, 4 }, // .b
390+
const PropertyRange &getCompositeSpecConstants() const {
391+
return CompositeSpecConstIDMap;
392+
}
372393
const PropertyRange &getDeviceLibReqMask() const { return DeviceLibReqMask; }
373394
const PropertyRange &getKernelParamOptInfo() const {
374395
return KernelParamOptInfo;
@@ -381,7 +402,8 @@ class DeviceBinaryImage {
381402

382403
pi_device_binary Bin;
383404
pi::PiDeviceBinaryType Format = PI_DEVICE_BINARY_TYPE_NONE;
384-
DeviceBinaryImage::PropertyRange SpecConstIDMap;
405+
DeviceBinaryImage::PropertyRange ScalarSpecConstIDMap;
406+
DeviceBinaryImage::PropertyRange CompositeSpecConstIDMap;
385407
DeviceBinaryImage::PropertyRange DeviceLibReqMask;
386408
DeviceBinaryImage::PropertyRange KernelParamOptInfo;
387409
};

sycl/include/CL/sycl/detail/sycl_fe_intrins.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,7 @@
1818
template <typename T>
1919
SYCL_EXTERNAL T __sycl_getSpecConstantValue(const char *ID);
2020

21+
template <typename T>
22+
SYCL_EXTERNAL T __sycl_getCompositeSpecConstantValue(const char *ID);
23+
2124
#endif

sycl/include/CL/sycl/program.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -343,8 +343,8 @@ class __SYCL_EXPORT program {
343343
template <typename ID, typename T>
344344
ONEAPI::experimental::spec_constant<T, ID> set_spec_constant(T Cst) {
345345
constexpr const char *Name = detail::SpecConstantInfo<ID>::getName();
346-
static_assert(std::is_integral<T>::value ||
347-
std::is_floating_point<T>::value,
346+
static_assert(std::is_arithmetic<T>::value ||
347+
(std::is_class<T>::value && std::is_pod<T>::value),
348348
"unsupported specialization constant type");
349349
#ifdef __SYCL_DEVICE_ONLY__
350350
(void)Cst;

sycl/source/detail/pi.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -592,7 +592,9 @@ void DeviceBinaryImage::init(pi_device_binary Bin) {
592592
// try to determine the format; may remain "NONE"
593593
Format = getBinaryImageFormat(Bin->BinaryStart, getSize());
594594

595-
SpecConstIDMap.init(Bin, __SYCL_PI_PROPERTY_SET_SPEC_CONST_MAP);
595+
ScalarSpecConstIDMap.init(Bin, __SYCL_PI_PROPERTY_SET_SCALAR_SPEC_CONST_MAP);
596+
CompositeSpecConstIDMap.init(Bin,
597+
__SYCL_PI_PROPERTY_SET_COMPOSITE_SPEC_CONST_MAP);
596598
DeviceLibReqMask.init(Bin, __SYCL_PI_PROPERTY_SET_DEVICELIB_REQ_MASK);
597599
KernelParamOptInfo.init(Bin, __SYCL_PI_PROPERTY_SET_KERNEL_PARAM_OPT_INFO);
598600
}

sycl/source/detail/program_impl.cpp

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -523,26 +523,55 @@ void program_impl::flush_spec_constants(const RTDeviceBinaryImage &Img,
523523
RT::PiProgram NativePrg) const {
524524
// iterate via all specialization constants the program's image depends on,
525525
// and set each to current runtime value (if any)
526-
const pi::DeviceBinaryImage::PropertyRange &SCRange = Img.getSpecConstants();
526+
const pi::DeviceBinaryImage::PropertyRange &ScalarSCRange =
527+
Img.getScalarSpecConstants();
528+
const pi::DeviceBinaryImage::PropertyRange &CompositeSCRange =
529+
Img.getCompositeSpecConstants();
527530
ContextImplPtr Ctx = getSyclObjImpl(get_context());
528531
using SCItTy = pi::DeviceBinaryImage::PropertyRange::ConstIterator;
529532

530533
auto LockGuard = Ctx->getKernelProgramCache().acquireCachedPrograms();
534+
NativePrg = NativePrg ? NativePrg : getHandleRef();
531535

532-
for (SCItTy SCIt : SCRange) {
533-
const char *SCName = (*SCIt)->Name;
534-
auto SCEntry = SpecConstRegistry.find(SCName);
536+
for (SCItTy SCIt : ScalarSCRange) {
537+
auto SCEntry = SpecConstRegistry.find((*SCIt)->Name);
535538
if (SCEntry == SpecConstRegistry.end())
536539
// spec constant has not been set in user code - SPIR-V will use default
537540
continue;
538541
const spec_constant_impl &SC = SCEntry->second;
539542
assert(SC.isSet() && "uninitialized spec constant");
540-
pi_device_binary_property SCProp = *SCIt;
541-
pi_uint32 ID = pi::DeviceBinaryProperty(SCProp).asUint32();
542-
NativePrg = NativePrg ? NativePrg : getHandleRef();
543+
pi_uint32 ID = pi::DeviceBinaryProperty(*SCIt).asUint32();
543544
Ctx->getPlugin().call<PiApiKind::piextProgramSetSpecializationConstant>(
544545
NativePrg, ID, SC.getSize(), SC.getValuePtr());
545546
}
547+
548+
for (SCItTy SCIt : CompositeSCRange) {
549+
auto SCEntry = SpecConstRegistry.find((*SCIt)->Name);
550+
if (SCEntry == SpecConstRegistry.end())
551+
// spec constant has not been set in user code - SPIR-V will use default
552+
continue;
553+
const spec_constant_impl &SC = SCEntry->second;
554+
assert(SC.isSet() && "uninitialized spec constant");
555+
pi::ByteArray Descriptors = pi::DeviceBinaryProperty(*SCIt).asByteArray();
556+
// First 8 bytes are consumed by size of the property
557+
assert(Descriptors.size() > 8 && "Unexpected property size");
558+
// Expected layout is vector of 3-component tuples (flattened into a vector
559+
// of scalars), where each tuple consists of: ID of a scalar spec constant,
560+
// which is a member of the composite; offset, which is used to calculate
561+
// location of scalar member within the composite; size of a scalar member
562+
// of the composite.
563+
assert(((Descriptors.size() - 8) / sizeof(std::uint32_t)) % 3 == 0 &&
564+
"unexpected layout of composite spec const descriptors");
565+
auto *It = reinterpret_cast<const std::uint32_t *>(&Descriptors[8]);
566+
auto *End = reinterpret_cast<const std::uint32_t *>(&Descriptors[0] +
567+
Descriptors.size());
568+
while (It != End) {
569+
Ctx->getPlugin().call<PiApiKind::piextProgramSetSpecializationConstant>(
570+
NativePrg, /* ID */ It[0], /* Size */ It[2],
571+
SC.getValuePtr() + /* Offset */ It[1]);
572+
It += 3;
573+
}
574+
}
546575
}
547576

548577
pi_native_handle program_impl::getNative() const {

sycl/source/detail/spec_constant_impl.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@ namespace sycl {
2121
namespace detail {
2222

2323
void spec_constant_impl::set(size_t Size, const void *Val) {
24-
if ((Size > sizeof(Bytes)) || (Size == 0))
24+
if (0 == Size)
2525
throw sycl::runtime_error("invalid spec constant size", PI_INVALID_VALUE);
26-
this->Size = Size;
27-
std::memcpy(Bytes, Val, Size);
26+
auto *BytePtr = reinterpret_cast<const char *>(Val);
27+
this->Bytes.assign(BytePtr, BytePtr + Size);
2828
}
2929

3030
void stableSerializeSpecConstRegistry(const SpecConstRegistryT &Reg,

sycl/source/detail/spec_constant_impl.hpp

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
#include <iostream>
1616
#include <map>
17+
#include <vector>
1718

1819
__SYCL_INLINE_NAMESPACE(cl) {
1920
namespace sycl {
@@ -22,20 +23,18 @@ namespace detail {
2223
// Represents a specialization constant value in SYCL runtime.
2324
class spec_constant_impl {
2425
public:
25-
spec_constant_impl() : Size(0), Bytes{0} {};
26+
spec_constant_impl() = default;
2627

2728
spec_constant_impl(size_t Size, const void *Val) { set(Size, Val); }
2829

2930
void set(size_t Size, const void *Val);
3031

31-
size_t getSize() const { return Size; }
32-
const unsigned char *getValuePtr() const { return Bytes; }
33-
bool isSet() const { return Size != 0; }
32+
size_t getSize() const { return Bytes.size(); }
33+
const char *getValuePtr() const { return Bytes.data(); }
34+
bool isSet() const { return !Bytes.empty(); }
3435

3536
private:
36-
size_t Size; // the size of the spec constant value
37-
// TODO invent more flexible approach to support values of arbitrary type:
38-
unsigned char Bytes[8]; // memory to hold the value bytes
37+
std::vector<char> Bytes;
3938
};
4039

4140
std::ostream &operator<<(std::ostream &Out, const spec_constant_impl &V);
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// UNSUPPORTED: cuda
2+
//
3+
// RUN: %clangxx -fsycl %s -o %t.out
4+
// RUN: %RUN_ON_HOST %t.out | FileCheck %s
5+
// RUN: %CPU_RUN_PLACEHOLDER %t.out %CPU_CHECK_PLACEHOLDER
6+
// RUN: %GPU_RUN_PLACEHOLDER %t.out %GPU_CHECK_PLACEHOLDER
7+
//
8+
// The test checks that the specialization constant feature works correctly with
9+
// composite types: toolchain processes them correctly and runtime can correctly
10+
// execute the program.
11+
//
12+
// CHECK: 1 : 2
13+
// CHECK-NEXT: 3
14+
// CHECK-NEXT: 4 : 5
15+
16+
#include <CL/sycl.hpp>
17+
18+
using namespace cl::sycl;
19+
20+
struct A {
21+
float x;
22+
float y[2];
23+
};
24+
25+
struct pod_t {
26+
int f1[2];
27+
A f2;
28+
};
29+
30+
class my_kernel_t {
31+
public:
32+
using sc_t =
33+
sycl::ONEAPI::experimental::spec_constant<pod_t, class my_kernel_t>;
34+
35+
my_kernel_t(const sc_t &sc, const cl::sycl::stream &strm)
36+
: sc_(sc), strm_(strm) {}
37+
38+
void operator()(cl::sycl::id<1> i) const {
39+
auto p = sc_.get();
40+
strm_ << p.f1[0] << " : " << p.f1[1] << "\n";
41+
strm_ << p.f2.x << "\n";
42+
strm_ << p.f2.y[0] << " : " << p.f2.y[1] << "\n";
43+
strm_ << sycl::endl;
44+
}
45+
46+
sc_t sc_;
47+
cl::sycl::stream strm_;
48+
};
49+
50+
int main() {
51+
cl::sycl::queue q(default_selector{}, [](exception_list l) {
52+
for (auto ep : l) {
53+
try {
54+
std::rethrow_exception(ep);
55+
} catch (cl::sycl::exception &e0) {
56+
std::cout << e0.what();
57+
} catch (std::exception &e1) {
58+
std::cout << e1.what();
59+
} catch (...) {
60+
std::cout << "*** catch (...)\n";
61+
}
62+
}
63+
});
64+
65+
pod_t pod;
66+
pod.f1[0] = 1;
67+
pod.f1[1] = 2;
68+
pod.f2.x = 3;
69+
pod.f2.y[0] = 4;
70+
pod.f2.y[1] = 5;
71+
72+
cl::sycl::program p(q.get_context());
73+
auto sc = p.set_spec_constant<my_kernel_t>(pod);
74+
p.build_with_kernel_type<my_kernel_t>();
75+
76+
q.submit([&](cl::sycl::handler &cgh) {
77+
cl::sycl::stream strm(1024, 256, cgh);
78+
my_kernel_t func(sc, strm);
79+
80+
auto sycl_kernel = p.get_kernel<my_kernel_t>();
81+
cgh.parallel_for(sycl_kernel, cl::sycl::range<1>(1), func);
82+
});
83+
q.wait();
84+
85+
return 0;
86+
}

0 commit comments

Comments
 (0)