Skip to content

Commit 1f932eb

Browse files
committed
[SYCL] Add runtime support for dummy images for virtual functions
1 parent 0a3a324 commit 1f932eb

File tree

5 files changed

+149
-16
lines changed

5 files changed

+149
-16
lines changed

sycl/source/detail/device_binary_image.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ std::ostream &operator<<(std::ostream &Out, const DeviceBinaryProperty &P) {
5252
break;
5353
}
5454
case SYCL_PROPERTY_TYPE_STRING:
55-
Out << P.asCString();
55+
Out << P.asStringView();
5656
break;
5757
default:
5858
assert(false && "Unsupported property");
@@ -77,14 +77,14 @@ ByteArray DeviceBinaryProperty::asByteArray() const {
7777
return {Data, Prop->ValSize};
7878
}
7979

80-
const char *DeviceBinaryProperty::asCString() const {
80+
std::string_view DeviceBinaryProperty::asStringView() const {
8181
assert((Prop->Type == SYCL_PROPERTY_TYPE_STRING ||
8282
Prop->Type == SYCL_PROPERTY_TYPE_BYTE_ARRAY) &&
8383
"property type mismatch");
8484
assert(Prop->ValSize > 0 && "property size mismatch");
8585
// Byte array stores its size in first 8 bytes
8686
size_t Shift = Prop->Type == SYCL_PROPERTY_TYPE_BYTE_ARRAY ? 8 : 0;
87-
return ur::cast<const char *>(Prop->ValAddr) + Shift;
87+
return {ur::cast<const char *>(Prop->ValAddr) + Shift, Prop->ValSize};
8888
}
8989

9090
void RTDeviceBinaryImage::PropertyRange::init(sycl_device_binary Bin,

sycl/source/detail/device_binary_image.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ class DeviceBinaryProperty {
7373

7474
uint32_t asUint32() const;
7575
ByteArray asByteArray() const;
76-
const char *asCString() const;
76+
std::string_view asStringView() const;
7777

7878
protected:
7979
friend std::ostream &operator<<(std::ostream &Out,

sycl/source/detail/program_manager/program_manager.cpp

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -671,10 +671,11 @@ ProgramManager::collectDependentDeviceImagesForVirtualFunctions(
671671
std::set<std::string> HandledSets;
672672
std::queue<std::string> WorkList;
673673
for (const sycl_device_binary_property &VFProp : Img.getVirtualFunctions()) {
674-
std::string StrValue = DeviceBinaryProperty(VFProp).asCString();
674+
std::string_view StrValue = DeviceBinaryProperty(VFProp).asStringView();
675675
// Device image passed to this function is expected to contain SYCL kernels
676676
// and therefore it may only use virtual function sets, but cannot provide
677-
// them. We expect to see just a single property here
677+
// them. Additionally, it cannot be a dummy image.
678+
// We expect to see just a single property here
678679
assert(std::string(VFProp->Name) == "uses-virtual-functions-set" &&
679680
"Unexpected virtual function property");
680681
for (const auto &SetName : detail::split_string(StrValue, ',')) {
@@ -695,9 +696,14 @@ ProgramManager::collectDependentDeviceImagesForVirtualFunctions(
695696
// virtual-functions-set properties, but their handling is the same: we
696697
// just grab all sets they reference and add them for consideration if
697698
// we haven't done so already.
699+
bool isDummyImage = false;
698700
for (const sycl_device_binary_property &VFProp :
699701
BinImage->getVirtualFunctions()) {
700-
std::string StrValue = DeviceBinaryProperty(VFProp).asCString();
702+
if (VFProp->Name == std::string_view("dummy-image")) {
703+
isDummyImage = true;
704+
continue;
705+
}
706+
std::string_view StrValue = DeviceBinaryProperty(VFProp).asStringView();
701707
for (const auto &SetName : detail::split_string(StrValue, ',')) {
702708
if (HandledSets.insert(SetName).second)
703709
WorkList.push(SetName);
@@ -710,7 +716,7 @@ ProgramManager::collectDependentDeviceImagesForVirtualFunctions(
710716
// However, if device image provides virtual function set and it is
711717
// incompatible, then we should link its "dummy" version to avoid link
712718
// errors about unresolved external symbols.
713-
if (doesDevSupportDeviceRequirements(Dev, *BinImage))
719+
if (doesDevSupportDeviceRequirements(Dev, *BinImage) + isDummyImage == 1)
714720
DeviceImagesToLink.insert(BinImage);
715721
}
716722
}
@@ -1797,7 +1803,9 @@ void ProgramManager::addImages(sycl_device_binaries DeviceBinary) {
17971803
// Record mapping between virtual function sets and device images
17981804
for (const sycl_device_binary_property &VFProp :
17991805
Img->getVirtualFunctions()) {
1800-
std::string StrValue = DeviceBinaryProperty(VFProp).asCString();
1806+
if (VFProp->Name == std::string_view("dummy-image"))
1807+
continue;
1808+
std::string_view StrValue = DeviceBinaryProperty(VFProp).asStringView();
18011809
for (const auto &SetName : detail::split_string(StrValue, ','))
18021810
m_VFSet2BinImage[SetName].insert(Img.get());
18031811
}

sycl/unittests/Extensions/VirtualFunctions/RuntimeLinking.cpp

Lines changed: 131 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ class KernelD;
1818
class KernelE;
1919
class KernelF;
2020
class KernelG;
21+
class KernelH;
2122

2223
} // namespace VirtualFunctionsTest
2324

@@ -39,6 +40,7 @@ KERNEL_INFO(KernelD)
3940
KERNEL_INFO(KernelE)
4041
KERNEL_INFO(KernelF)
4142
KERNEL_INFO(KernelG)
43+
KERNEL_INFO(KernelH)
4244

4345
#undef KERNEL_INFO
4446

@@ -48,9 +50,13 @@ KERNEL_INFO(KernelG)
4850

4951
static sycl::unittest::MockDeviceImage
5052
generateImage(std::initializer_list<std::string> KernelNames,
51-
const std::string &VFSets, bool UsesVFSets, unsigned char Magic) {
53+
const std::string &VFSets, bool UsesVFSets, unsigned char Magic,
54+
bool IsDummyImage = false,
55+
std::vector<sycl::aspect> Aspects = {}) {
5256
sycl::unittest::MockPropertySet PropSet;
53-
std::vector<sycl::unittest::MockProperty> Props;
57+
58+
// Construct virtual function properties
59+
std::vector<sycl::unittest::MockProperty> VFProps;
5460
uint64_t PropSize = VFSets.size();
5561
std::vector<char> Storage(/* bytes for size */ 8 + PropSize +
5662
/* null terminator */ 1);
@@ -64,9 +70,22 @@ generateImage(std::initializer_list<std::string> KernelNames,
6470
sycl::unittest::MockProperty Prop(PropName, Storage,
6571
SYCL_PROPERTY_TYPE_BYTE_ARRAY);
6672

67-
Props.push_back(Prop);
68-
PropSet.insert(__SYCL_PROPERTY_SET_SYCL_VIRTUAL_FUNCTIONS, std::move(Props));
73+
VFProps.push_back(Prop);
74+
if (IsDummyImage)
75+
VFProps.emplace_back("dummy-image", std::vector<char>(4),
76+
SYCL_PROPERTY_TYPE_UINT32);
77+
78+
PropSet.insert(__SYCL_PROPERTY_SET_SYCL_VIRTUAL_FUNCTIONS,
79+
std::move(VFProps));
80+
81+
// Construct device requirement properties
82+
std::vector<sycl::unittest::MockProperty> DeviceRequirmentsProps;
83+
DeviceRequirmentsProps.emplace_back(sycl::unittest::makeAspectsProp(Aspects));
6984

85+
PropSet.insert(__SYCL_PROPERTY_SET_SYCL_DEVICE_REQUIREMENTS,
86+
std::move(DeviceRequirmentsProps));
87+
88+
// Assemble final device image
7089
std::vector<unsigned char> Bin{Magic};
7190

7291
std::vector<sycl::unittest::MockOffloadEntry> Entries =
@@ -99,6 +118,9 @@ static constexpr unsigned PROGRAM_E0 = 37;
99118
static constexpr unsigned PROGRAM_F = 41;
100119
static constexpr unsigned PROGRAM_F0 = 47;
101120
static constexpr unsigned PROGRAM_F1 = 53;
121+
static constexpr unsigned PROGRAM_H = 59;
122+
static constexpr unsigned PROGRAM_H0 = 61;
123+
static constexpr unsigned PROGRAM_H0d = 67;
102124

103125
// Device images with no entires are ignored by SYCL RT during registration.
104126
// Therefore, we have to provide some kernel names to make the test work, even
@@ -128,10 +150,16 @@ static sycl::unittest::MockDeviceImage Imgs[] = {
128150
generateImage({"KernelF"}, "set-f", /* uses vf set */ true, PROGRAM_F),
129151
generateImage({"DummyKernel7"}, "set-f", /* provides vf set */ false,
130152
PROGRAM_F0),
131-
generateImage({"KernelG"}, "set-f", /* uses vf set */ true, PROGRAM_F1)};
153+
generateImage({"KernelG"}, "set-f", /* uses vf set */ true, PROGRAM_F1),
154+
generateImage({"KernelH"}, "set-h", /* uses vf set */ true, PROGRAM_H,
155+
false, {}),
156+
generateImage({"DummyKernel7"}, "set-h", /* provides vf set */ false,
157+
PROGRAM_H0, false, {sycl::aspect::fp64}),
158+
generateImage({"DummyKernel7d"}, "set-h", /* provides vf set */ false,
159+
PROGRAM_H0d, true, {sycl::aspect::fp64})};
132160

133161
// Registers mock devices images in the SYCL RT
134-
static sycl::unittest::MockDeviceImageArray<15> ImgArray{Imgs};
162+
static sycl::unittest::MockDeviceImageArray<std::size(Imgs)> ImgArray{Imgs};
135163

136164
TEST(VirtualFunctions, SingleKernelUsesSingleVFSet) {
137165
sycl::unittest::UrMock<> Mock;
@@ -262,4 +290,101 @@ TEST(VirtualFunctions, TwoKernelsShareTheSameSet) {
262290
PROGRAM_F * PROGRAM_F0 * PROGRAM_F1);
263291
}
264292

293+
struct MockDeviceData {
294+
std::string Extensions;
295+
ur_device_handle_t getHandle() {
296+
return reinterpret_cast<ur_device_handle_t>(this);
297+
}
298+
static MockDeviceData *fromHandle(ur_device_handle_t handle) {
299+
return reinterpret_cast<MockDeviceData *>(handle);
300+
}
301+
};
302+
303+
MockDeviceData MockDevices[] = {
304+
{"cl_khr_fp64"},
305+
{""},
306+
};
307+
308+
static ur_result_t redefinedDeviceGet(void *pParams) {
309+
auto params = *static_cast<ur_device_get_params_t *>(pParams);
310+
if (*params.ppNumDevices) {
311+
**params.ppNumDevices = static_cast<uint32_t>(std::size(MockDevices));
312+
return UR_RESULT_SUCCESS;
313+
}
314+
315+
if (*params.pphDevices) {
316+
assert(*params.pNumEntries <= std::size(MockDevices));
317+
for (uint32_t i = 0; i < *params.pNumEntries; ++i) {
318+
(*params.pphDevices)[i] = MockDevices[i].getHandle();
319+
}
320+
}
321+
322+
return UR_RESULT_SUCCESS;
323+
}
324+
325+
static ur_result_t redefinedDeviceGetInfo(void *pParams) {
326+
auto *params = reinterpret_cast<ur_device_get_info_params_t *>(pParams);
327+
if (*params->ppropName == UR_DEVICE_INFO_EXTENSIONS) {
328+
const std::string &Extensions =
329+
MockDeviceData::fromHandle(*params->phDevice)->Extensions;
330+
if (*params->ppPropValue) {
331+
assert(*params->ppropSize >= Extensions.size() + 1);
332+
std::memcpy(*params->ppPropValue, Extensions.data(),
333+
Extensions.size() + 1);
334+
}
335+
if (*params->ppPropSizeRet &&
336+
**params->ppPropSizeRet < Extensions.size() + 1)
337+
**params->ppPropSizeRet = Extensions.size() + 1;
338+
return UR_RESULT_SUCCESS;
339+
}
340+
return UR_RESULT_SUCCESS;
341+
}
342+
343+
TEST(VirtualFunctions, DummyImages) {
344+
sycl::unittest::UrMock<> Mock;
345+
setupRuntimeLinkingMock();
346+
mock::getCallbacks().set_after_callback("urDeviceGet", &redefinedDeviceGet);
347+
mock::getCallbacks().set_after_callback("urDeviceGetInfo",
348+
&redefinedDeviceGetInfo);
349+
350+
sycl::platform Plt = sycl::platform();
351+
sycl::queue Q(sycl::aspect_selector({sycl::aspect::fp64}));
352+
EXPECT_TRUE(Q.get_device().has(sycl::aspect::fp64));
353+
354+
CapturedLinkingData.clear();
355+
356+
// KernelF uses set "set-h" that is also used by KernelG
357+
Q.single_task<VirtualFunctionsTest::KernelH>([=]() {});
358+
// When we submit this kernel, we expect that two programs were created (one
359+
// for KernelH, another providing "set-h"
360+
EXPECT_EQ(CapturedLinkingData.NumOfUrProgramCreateCalls, 2u);
361+
// Both programs should be linked together.
362+
EXPECT_EQ(CapturedLinkingData.NumOfUrProgramLinkCalls, 1u);
363+
// The module providing set-h is set up to use fp64,
364+
// and since the device support fp64, we link the
365+
// non-dummy version that provides set-h.
366+
EXPECT_TRUE(
367+
CapturedLinkingData.LinkedProgramsContains({PROGRAM_H, PROGRAM_H0}));
368+
EXPECT_EQ(CapturedLinkingData.ProgramUsedToCreateKernel,
369+
PROGRAM_H * PROGRAM_H0);
370+
371+
CapturedLinkingData.clear();
372+
373+
EXPECT_EQ(Plt.get_devices().size(), 2);
374+
sycl::queue Q2(sycl::aspect_selector({}, {sycl::aspect::fp64}));
375+
376+
// We now repeat what we did launching KernelH but on another
377+
// device that does not support fp64.
378+
Q2.single_task<VirtualFunctionsTest::KernelH>([=]() {});
379+
EXPECT_EQ(CapturedLinkingData.NumOfUrProgramCreateCalls, 2u);
380+
EXPECT_EQ(CapturedLinkingData.NumOfUrProgramLinkCalls, 1u);
381+
382+
// However, this time, we expect the dummy image to be linked
383+
// as the device does not support fp64.
384+
EXPECT_TRUE(
385+
CapturedLinkingData.LinkedProgramsContains({PROGRAM_H, PROGRAM_H0d}));
386+
EXPECT_EQ(CapturedLinkingData.ProgramUsedToCreateKernel,
387+
PROGRAM_H * PROGRAM_H0d);
388+
}
389+
265390
// TODO: Add test cases for kernel_bundle usage

sycl/unittests/helpers/MockDeviceImage.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -558,7 +558,7 @@ inline MockProperty makeAspectsProp(const std::vector<sycl::aspect> &Aspects) {
558558
uint64_t ValDataSize = ValData.size();
559559
std::uninitialized_copy(&ValDataSize, &ValDataSize + sizeof(uint64_t),
560560
ValData.data());
561-
auto *AspectsPtr = reinterpret_cast<const unsigned char *>(&Aspects[0]);
561+
auto *AspectsPtr = reinterpret_cast<const unsigned char *>(Aspects.data());
562562
std::uninitialized_copy(AspectsPtr, AspectsPtr + Aspects.size(),
563563
ValData.data() + BYTES_FOR_SIZE);
564564
return {"aspects", ValData, SYCL_PROPERTY_TYPE_BYTE_ARRAY};

0 commit comments

Comments
 (0)