Skip to content

Commit 73d3473

Browse files
authored
[SYCL] Choose image with inlined default values if default value is set explicitly (#12626)
If -fsycl-add-default-spec-consts-image option is used then DPCPP generates a device image where default values of specialization constants are inlined (in addition to regular device image). Currently we always choose regular device image if somebody calls `set_specialization_constant ` API. This PR improves this behavior: if `set_specialization_constant` sets the value equal to default value then we can choose the device image where default values of specialization constants are inlined.
1 parent f642575 commit 73d3473

File tree

3 files changed

+133
-17
lines changed

3 files changed

+133
-17
lines changed

sycl/include/sycl/kernel_bundle.hpp

+2
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,8 @@ class __SYCL_EXPORT kernel_bundle_plain {
205205
void get_specialization_constant_impl(const char *SpecName,
206206
void *Value) const noexcept;
207207

208+
// \returns a bool value which indicates if specialization constant was set to
209+
// a value different from default value.
208210
bool is_specialization_constant_set(const char *SpecName) const noexcept;
209211

210212
detail::KernelBundleImplPtr impl;

sycl/source/detail/device_image_impl.hpp

+51-17
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ class device_image_impl {
5050
unsigned int CompositeOffset = 0;
5151
unsigned int Size = 0;
5252
unsigned int BlobOffset = 0;
53+
// Indicates if the specialization constant was set to a value which is
54+
// different from the default value.
5355
bool IsSet = false;
5456
};
5557

@@ -61,7 +63,8 @@ class device_image_impl {
6163
sycl::detail::pi::PiProgram Program)
6264
: MBinImage(BinImage), MContext(std::move(Context)),
6365
MDevices(std::move(Devices)), MState(State), MProgram(Program),
64-
MKernelIDs(std::move(KernelIDs)) {
66+
MKernelIDs(std::move(KernelIDs)),
67+
MSpecConstsDefValBlob(getSpecConstsDefValBlob()) {
6568
updateSpecConstSymMap();
6669
}
6770

@@ -74,6 +77,7 @@ class device_image_impl {
7477
: MBinImage(BinImage), MContext(std::move(Context)),
7578
MDevices(std::move(Devices)), MState(State), MProgram(Program),
7679
MKernelIDs(std::move(KernelIDs)), MSpecConstsBlob(SpecConstsBlob),
80+
MSpecConstsDefValBlob(getSpecConstsDefValBlob()),
7781
MSpecConstSymMap(SpecConstMap) {}
7882

7983
bool has_kernel(const kernel_id &KernelIDCand) const noexcept {
@@ -152,6 +156,21 @@ class device_image_impl {
152156
std::vector<SpecConstDescT> &Descs =
153157
MSpecConstSymMap[std::string{SpecName}];
154158
for (SpecConstDescT &Desc : Descs) {
159+
// If there is a default value of the specialization constant and it is
160+
// the same as the value which is being set then do nothing, runtime is
161+
// going to handle this case just like if only the default value of the
162+
// specialization constant was provided.
163+
if (MSpecConstsDefValBlob.size() &&
164+
(std::memcmp(MSpecConstsDefValBlob.begin() + Desc.BlobOffset,
165+
static_cast<const char *>(Value) + Desc.CompositeOffset,
166+
Desc.Size) == 0)) {
167+
// Now we have default value, so reset to false.
168+
Desc.IsSet = false;
169+
continue;
170+
}
171+
172+
// Value of the specialization constant is set to a value which is
173+
// different from the default value.
155174
Desc.IsSet = true;
156175
std::memcpy(MSpecConstsBlob.data() + Desc.BlobOffset,
157176
static_cast<const char *>(Value) + Desc.CompositeOffset,
@@ -161,19 +180,20 @@ class device_image_impl {
161180

162181
void get_specialization_constant_raw_value(const char *SpecName,
163182
void *ValueRet) const noexcept {
164-
assert(is_specialization_constant_set(SpecName));
183+
bool IsSet = is_specialization_constant_set(SpecName);
165184
// Lock the mutex to prevent when one thread in the middle of writing a
166185
// new value while another thread is reading the value to pass it to
167186
// JIT compiler.
168187
const std::lock_guard<std::mutex> SpecConstLock(MSpecConstAccessMtx);
169-
188+
assert(IsSet || MSpecConstsDefValBlob.size());
170189
// operator[] can't be used here, since it's not marked as const
171190
const std::vector<SpecConstDescT> &Descs =
172191
MSpecConstSymMap.at(std::string{SpecName});
173192
for (const SpecConstDescT &Desc : Descs) {
174-
193+
auto Blob =
194+
IsSet ? MSpecConstsBlob.data() : MSpecConstsDefValBlob.begin();
175195
std::memcpy(static_cast<char *>(ValueRet) + Desc.CompositeOffset,
176-
MSpecConstsBlob.data() + Desc.BlobOffset, Desc.Size);
196+
Blob + Desc.BlobOffset, Desc.Size);
177197
}
178198
}
179199

@@ -293,16 +313,30 @@ class device_image_impl {
293313
}
294314

295315
private:
316+
// Get the specialization constant default value blob.
317+
ByteArray getSpecConstsDefValBlob() const {
318+
if (!MBinImage)
319+
return ByteArray(nullptr, 0);
320+
321+
// Get default values for specialization constants.
322+
const RTDeviceBinaryImage::PropertyRange &SCDefValRange =
323+
MBinImage->getSpecConstantsDefaultValues();
324+
if (!SCDefValRange.size())
325+
return ByteArray(nullptr, 0);
326+
327+
ByteArray DefValDescriptors =
328+
DeviceBinaryProperty(*SCDefValRange.begin()).asByteArray();
329+
// First 8 bytes are consumed by the size of the property.
330+
DefValDescriptors.dropBytes(8);
331+
return DefValDescriptors;
332+
}
333+
296334
void updateSpecConstSymMap() {
297335
if (MBinImage) {
298336
const RTDeviceBinaryImage::PropertyRange &SCRange =
299337
MBinImage->getSpecConstants();
300338
using SCItTy = RTDeviceBinaryImage::PropertyRange::ConstIterator;
301339

302-
// get default values for specialization constants
303-
const RTDeviceBinaryImage::PropertyRange &SCDefValRange =
304-
MBinImage->getSpecConstantsDefaultValues();
305-
306340
// This variable is used to calculate spec constant value offset in a
307341
// flat byte array.
308342
unsigned BlobOffset = 0;
@@ -341,16 +375,13 @@ class device_image_impl {
341375
}
342376
MSpecConstsBlob.resize(BlobOffset);
343377

344-
bool HasDefaultValues = SCDefValRange.begin() != SCDefValRange.end();
345-
346-
if (HasDefaultValues) {
347-
ByteArray DefValDescriptors =
348-
DeviceBinaryProperty(*SCDefValRange.begin()).asByteArray();
349-
assert(DefValDescriptors.size() - 8 == MSpecConstsBlob.size() &&
378+
if (MSpecConstsDefValBlob.size()) {
379+
assert(MSpecConstsDefValBlob.size() == MSpecConstsBlob.size() &&
350380
"Specialization constant default value blob do not have the "
351381
"expected size.");
352-
std::uninitialized_copy(&DefValDescriptors[8],
353-
&DefValDescriptors[8] + MSpecConstsBlob.size(),
382+
std::uninitialized_copy(MSpecConstsDefValBlob.begin(),
383+
MSpecConstsDefValBlob.begin() +
384+
MSpecConstsBlob.size(),
354385
MSpecConstsBlob.data());
355386
}
356387
}
@@ -372,6 +403,9 @@ class device_image_impl {
372403
// Binary blob which can have values of all specialization constants in the
373404
// image
374405
std::vector<unsigned char> MSpecConstsBlob;
406+
// Binary blob which can have default values of all specialization constants
407+
// in the image.
408+
const ByteArray MSpecConstsDefValBlob;
375409
// Buffer containing binary blob which can have values of all specialization
376410
// constants in the image, it is using for storing non-native specialization
377411
// constants

sycl/test-e2e/SpecConstants/2020/image_selection.cpp

+80
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,33 @@
2323
// RUN: env SYCL_PI_TRACE=-1 %{run} %t3.out | FileCheck --match-full-lines --check-prefix=CHECK-MIX %s
2424
// clang-format on
2525

26+
// Check the behaviour when -fsycl-add-default-spec-consts-image option is used
27+
// and default value is explicitly set with the same value - we are supposed to
28+
// choose images with inlined values in this case.
29+
30+
// clang-format off
31+
// RUN: %clangxx -fsycl-add-default-spec-consts-image -fsycl -fsycl-targets=spir64_gen -Xsycl-target-backend=spir64_gen %gpu_aot_target_opts %s -o %t3.out
32+
// RUN: env SYCL_PI_TRACE=-1 %{run} %t3.out | FileCheck --match-full-lines --check-prefix=CHECK-DEFAULT-EXPLICIT-SET %s
33+
// clang-format on
34+
35+
// Check the behaviour when -fsycl-add-default-spec-consts-image option is used
36+
// and value of specialization constant is changed to new value and then back to
37+
// the default value - we are supposed to choose images with inlined values in
38+
// this case.
39+
40+
// clang-format off
41+
// RUN: %clangxx -fsycl-add-default-spec-consts-image -fsycl -fsycl-targets=spir64_gen -Xsycl-target-backend=spir64_gen %gpu_aot_target_opts %s -o %t3.out
42+
// RUN: env SYCL_PI_TRACE=-1 %{run} %t3.out | FileCheck --match-full-lines --check-prefix=CHECK-DEFAULT-BACK-TO-DEFAULT %s
43+
// clang-format on
44+
2645
#include <sycl/sycl.hpp>
2746

2847
constexpr sycl::specialization_id<int> int_id(3);
2948

3049
class Kernel1;
3150
class Kernel2;
51+
class Kernel3;
52+
class Kernel4;
3253

3354
int main() {
3455
sycl::queue Q;
@@ -189,5 +210,64 @@ int main() {
189210
else
190211
std::cout << "Default value of specialization constant was used."
191212
<< std::endl;
213+
214+
// Test that if user calls set_specialization_constant with the value equal to
215+
// default then we choose image with inlined default values of specialization
216+
// constants. We are verifying that by checking the 4th parameter is set to
217+
// zero.
218+
// CHECK-DEFAULT-EXPLICIT-SET: Default value was explicitly set
219+
// CHECK-DEFAULT-EXPLICIT-SET: ---> piextKernelSetArgMemObj(
220+
// CHECK-DEFAULT-EXPLICIT-SET-NEXT: <unknown> : {{.*}}
221+
// CHECK-DEFAULT-EXPLICIT-SET-NEXT: <unknown> : {{.*}}
222+
// CHECK-DEFAULT-EXPLICIT-SET-NEXT: <unknown> : {{.*}}
223+
// CHECK-DEFAULT-EXPLICIT-SET-NEXT: <unknown> : 0
224+
// CHECK-DEFAULT-EXPLICIT-SET-NEXT: ) ---> pi_result : PI_SUCCESS
225+
// CHECK-DEFAULT-EXPLICIT-SET: Default value of specialization constant was used.
226+
std::cout << "Default value was explicitly set" << std::endl;
227+
Q.submit([&](sycl::handler &cgh) {
228+
cgh.set_specialization_constant<int_id>(3);
229+
230+
cgh.single_task<Kernel3>([=](sycl::kernel_handler h) {
231+
auto SpecConst = h.get_specialization_constant<int_id>();
232+
*Res = SpecConst == 3 ? 0 : 1;
233+
});
234+
}).wait();
235+
236+
if (*Res)
237+
std::cout << "New specialization constant value was set." << std::endl;
238+
else
239+
std::cout << "Default value of specialization constant was used."
240+
<< std::endl;
241+
242+
// Test that if user sets new value of specialization constant and then
243+
// changes it back to default value then we choose image with inlined default
244+
// values of specialization constants. We are verifying that by checking the
245+
// 4th parameter is set to zero.
246+
// CHECK-DEFAULT-BACK-TO-DEFAULT: Changed to new value and then default value was explicitly set
247+
// CHECK-DEFAULT-BACK-TO-DEFAULT: ---> piextKernelSetArgMemObj(
248+
// CHECK-DEFAULT-BACK-TO-DEFAULT-NEXT: <unknown> : {{.*}}
249+
// CHECK-DEFAULT-BACK-TO-DEFAULT-NEXT: <unknown> : {{.*}}
250+
// CHECK-DEFAULT-BACK-TO-DEFAULT-NEXT: <unknown> : {{.*}}
251+
// CHECK-DEFAULT-BACK-TO-DEFAULT-NEXT: <unknown> : 0
252+
// CHECK-DEFAULT-BACK-TO-DEFAULT-NEXT: ) ---> pi_result : PI_SUCCESS
253+
// CHECK-DEFAULT-BACK-TO-DEFAULT: Default value of specialization constant was used.
254+
std::cout << "Changed to new value and then default value was explicitly set"
255+
<< std::endl;
256+
Q.submit([&](sycl::handler &cgh) {
257+
cgh.set_specialization_constant<int_id>(4);
258+
cgh.set_specialization_constant<int_id>(3);
259+
260+
cgh.single_task<Kernel4>([=](sycl::kernel_handler h) {
261+
auto SpecConst = h.get_specialization_constant<int_id>();
262+
*Res = SpecConst == 3 ? 0 : 1;
263+
});
264+
}).wait();
265+
266+
if (*Res)
267+
std::cout << "New specialization constant value was set." << std::endl;
268+
else
269+
std::cout << "Default value of specialization constant was used."
270+
<< std::endl;
271+
192272
return 0;
193273
}

0 commit comments

Comments
 (0)