@@ -18,6 +18,7 @@ class KernelD;
1818class KernelE ;
1919class KernelF ;
2020class KernelG ;
21+ class KernelH ;
2122
2223} // namespace VirtualFunctionsTest
2324
@@ -39,6 +40,7 @@ KERNEL_INFO(KernelD)
3940KERNEL_INFO (KernelE)
4041KERNEL_INFO (KernelF)
4142KERNEL_INFO (KernelG)
43+ KERNEL_INFO (KernelH)
4244
4345#undef KERNEL_INFO
4446
@@ -48,9 +50,13 @@ KERNEL_INFO(KernelG)
4850
4951static sycl::unittest::MockDeviceImage
5052generateImage (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;
99118static constexpr unsigned PROGRAM_F = 41 ;
100119static constexpr unsigned PROGRAM_F0 = 47 ;
101120static 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
136164TEST (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
0 commit comments