1 //
2 // Copyright (c) 2017 The Khronos Group Inc.
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16 #include "harness/ThreadPool.h"
17 #include "harness/testHarness.h"
18 #include "harness/parseParameters.h"
19 #include "harness/mt19937.h"
20
21 #if defined(__APPLE__)
22 #include <sys/sysctl.h>
23 #endif
24
25 #if defined(__linux__)
26 #include <unistd.h>
27 #include <sys/syscall.h>
28 #include <linux/sysctl.h>
29 #endif
30 #if defined(__linux__)
31 #include <sys/param.h>
32 #include <libgen.h>
33 #endif
34
35 #if defined(__MINGW32__)
36 #include <sys/param.h>
37 #endif
38
39 #include <sstream>
40 #include <stdarg.h>
41 #include <stdio.h>
42 #include <string.h>
43 #if !defined(_WIN32)
44 #include <libgen.h>
45 #include <sys/mman.h>
46 #endif
47 #include <time.h>
48
49 #include <algorithm>
50 #include <type_traits>
51 #include <vector>
52
53 #include "Sleep.h"
54
55 #include "basic_test_conversions.h"
56 #include <climits>
57 #include <cstring>
58
59 #if (defined(__arm__) || defined(__aarch64__)) && defined(__GNUC__)
60 #include "fplib.h"
61 #endif
62
63 #if (defined(__arm__) || defined(__aarch64__)) && defined(__GNUC__)
64 /* Rounding modes and saturation for use with qcom 64 bit to float conversion
65 * library */
66 bool qcom_sat;
67 roundingMode qcom_rm;
68 #endif
69
70
71 static int ParseArgs(int argc, const char **argv);
72 static void PrintUsage(void);
73 test_status InitCL(cl_device_id device);
74
75
76 const char *gTypeNames[kTypeCount] = { "uchar", "char", "ushort", "short",
77 "uint", "int", "float", "double",
78 "ulong", "long" };
79
80 const char *gRoundingModeNames[kRoundingModeCount] = { "", "_rte", "_rtp",
81 "_rtn", "_rtz" };
82
83 const char *gSaturationNames[2] = { "", "_sat" };
84
85 size_t gTypeSizes[kTypeCount] = {
86 sizeof(cl_uchar), sizeof(cl_char), sizeof(cl_ushort), sizeof(cl_short),
87 sizeof(cl_uint), sizeof(cl_int), sizeof(cl_float), sizeof(cl_double),
88 sizeof(cl_ulong), sizeof(cl_long),
89 };
90
91 char appName[64] = "ctest";
92 int gMultithread = 1;
93
94
test_conversions(cl_device_id device,cl_context context,cl_command_queue queue,int num_elements)95 int test_conversions(cl_device_id device, cl_context context,
96 cl_command_queue queue, int num_elements)
97 {
98 if (argCount)
99 {
100 return MakeAndRunTest<CustomConversionsTest>(device, context, queue,
101 num_elements);
102 }
103 else
104 {
105 return MakeAndRunTest<ConversionsTest>(device, context, queue,
106 num_elements);
107 }
108 }
109
110
111 test_definition test_list[] = {
112 ADD_TEST(conversions),
113 };
114
115 const int test_num = ARRAY_SIZE(test_list);
116
117
main(int argc,const char ** argv)118 int main(int argc, const char **argv)
119 {
120 int error;
121
122 argc = parseCustomParam(argc, argv);
123 if (argc == -1)
124 {
125 return 1;
126 }
127
128 if ((error = ParseArgs(argc, argv))) return error;
129
130 // Turn off sleep so our tests run to completion
131 PreventSleep();
132 atexit(ResumeSleep);
133
134 if (!gMultithread) SetThreadCount(1);
135
136 #if defined(_MSC_VER) && defined(_M_IX86)
137 // VS2005 (and probably others, since long double got deprecated) sets
138 // the x87 to 53-bit precision. This causes problems with the tests
139 // that convert long and ulong to float and double, since they deal
140 // with values that need more precision than that. So, set the x87
141 // to 64-bit precision.
142 unsigned int ignored;
143 _controlfp_s(&ignored, _PC_64, _MCW_PC);
144 #endif
145
146 vlog("===========================================================\n");
147 vlog("Random seed: %u\n", gRandomSeed);
148 gMTdata = init_genrand(gRandomSeed);
149
150 const char *arg[] = { argv[0] };
151 int ret =
152 runTestHarnessWithCheck(1, arg, test_num, test_list, true, 0, InitCL);
153
154 free_mtdata(gMTdata);
155 if (gQueue)
156 {
157 error = clFinish(gQueue);
158 if (error) vlog_error("clFinish failed: %d\n", error);
159 }
160
161 clReleaseMemObject(gInBuffer);
162
163 for (int i = 0; i < kCallStyleCount; i++)
164 {
165 clReleaseMemObject(gOutBuffers[i]);
166 }
167 clReleaseCommandQueue(gQueue);
168 clReleaseContext(gContext);
169
170 return ret;
171 }
172
173
ParseArgs(int argc,const char ** argv)174 static int ParseArgs(int argc, const char **argv)
175 {
176 int i;
177 argList = (const char **)calloc(argc, sizeof(char *));
178 argCount = 0;
179
180 if (NULL == argList && argc > 1) return -1;
181
182 #if (defined(__APPLE__) || defined(__linux__) || defined(__MINGW32__))
183 { // Extract the app name
184 char baseName[MAXPATHLEN];
185 strncpy(baseName, argv[0], MAXPATHLEN);
186 char *base = basename(baseName);
187 if (NULL != base)
188 {
189 strncpy(appName, base, sizeof(appName));
190 appName[sizeof(appName) - 1] = '\0';
191 }
192 }
193 #elif defined(_WIN32)
194 {
195 char fname[_MAX_FNAME + _MAX_EXT + 1];
196 char ext[_MAX_EXT];
197
198 errno_t err = _splitpath_s(argv[0], NULL, 0, NULL, 0, fname, _MAX_FNAME,
199 ext, _MAX_EXT);
200 if (err == 0)
201 { // no error
202 strcat(fname, ext); // just cat them, size of frame can keep both
203 strncpy(appName, fname, sizeof(appName));
204 appName[sizeof(appName) - 1] = '\0';
205 }
206 }
207 #endif
208
209 vlog("\n%s", appName);
210 for (i = 1; i < argc; i++)
211 {
212 const char *arg = argv[i];
213 if (NULL == arg) break;
214
215 vlog("\t%s", arg);
216 if (arg[0] == '-')
217 {
218 arg++;
219 while (*arg != '\0')
220 {
221 switch (*arg)
222 {
223 case 'd': gTestDouble ^= 1; break;
224 case 'l': gSkipTesting ^= 1; break;
225 case 'm': gMultithread ^= 1; break;
226 case 'w': gWimpyMode ^= 1; break;
227 case '[':
228 parseWimpyReductionFactor(arg, gWimpyReductionFactor);
229 break;
230 case 'z': gForceFTZ ^= 1; break;
231 case 't': gTimeResults ^= 1; break;
232 case 'a': gReportAverageTimes ^= 1; break;
233 case '1':
234 if (arg[1] == '6')
235 {
236 gMinVectorSize = 6;
237 gMaxVectorSize = 7;
238 arg++;
239 }
240 else
241 {
242 gMinVectorSize = 0;
243 gMaxVectorSize = 2;
244 }
245 break;
246
247 case '2':
248 gMinVectorSize = 2;
249 gMaxVectorSize = 3;
250 break;
251
252 case '3':
253 gMinVectorSize = 3;
254 gMaxVectorSize = 4;
255 break;
256
257 case '4':
258 gMinVectorSize = 4;
259 gMaxVectorSize = 5;
260 break;
261
262 case '8':
263 gMinVectorSize = 5;
264 gMaxVectorSize = 6;
265 break;
266
267 default:
268 vlog(" <-- unknown flag: %c (0x%2.2x)\n)", *arg, *arg);
269 PrintUsage();
270 return -1;
271 }
272 arg++;
273 }
274 }
275 else
276 {
277 char *t = NULL;
278 long number = strtol(arg, &t, 0);
279 if (t != arg)
280 {
281 if (gStartTestNumber != -1)
282 gEndTestNumber = gStartTestNumber + (int)number;
283 else
284 gStartTestNumber = (int)number;
285 }
286 else
287 {
288 argList[argCount] = arg;
289 argCount++;
290 }
291 }
292 }
293
294 // Check for the wimpy mode environment variable
295 if (getenv("CL_WIMPY_MODE"))
296 {
297 vlog("\n");
298 vlog("*** Detected CL_WIMPY_MODE env ***\n");
299 gWimpyMode = 1;
300 }
301
302 vlog("\n");
303
304 PrintArch();
305
306 if (gWimpyMode)
307 {
308 vlog("\n");
309 vlog("*** WARNING: Testing in Wimpy mode! ***\n");
310 vlog("*** Wimpy mode is not sufficient to verify correctness. ***\n");
311 vlog("*** It gives warm fuzzy feelings and then nevers calls. ***\n\n");
312 vlog("*** Wimpy Reduction Factor: %-27u ***\n\n",
313 gWimpyReductionFactor);
314 }
315
316 return 0;
317 }
318
319
PrintUsage(void)320 static void PrintUsage(void)
321 {
322 int i;
323 vlog("%s [-wz#]: <optional: test names>\n", appName);
324 vlog("\ttest names:\n");
325 vlog("\t\tdestFormat<_sat><_round>_sourceFormat\n");
326 vlog("\t\t\tPossible format types are:\n\t\t\t\t");
327 for (i = 0; i < kTypeCount; i++) vlog("%s, ", gTypeNames[i]);
328 vlog("\n\n\t\t\tPossible saturation values are: (empty) and _sat\n");
329 vlog("\t\t\tPossible rounding values are:\n\t\t\t\t(empty), ");
330 for (i = 1; i < kRoundingModeCount; i++)
331 vlog("%s, ", gRoundingModeNames[i]);
332 vlog("\n\t\t\tExamples:\n");
333 vlog("\t\t\t\tulong_short converts short to ulong\n");
334 vlog("\t\t\t\tchar_sat_rte_float converts float to char with saturated "
335 "clipping in round to nearest rounding mode\n\n");
336 vlog("\toptions:\n");
337 vlog("\t\t-d\tToggle testing of double precision. On by default if "
338 "cl_khr_fp64 is enabled, ignored otherwise.\n");
339 vlog("\t\t-l\tToggle link check mode. When on, testing is skipped, and we "
340 "just check to see that the kernels build. (Off by default.)\n");
341 vlog("\t\t-m\tToggle Multithreading. (On by default.)\n");
342 vlog("\t\t-w\tToggle wimpy mode. When wimpy mode is on, we run a very "
343 "small subset of the tests for each fn. NOT A VALID TEST! (Off by "
344 "default.)\n");
345 vlog(" \t\t-[2^n]\tSet wimpy reduction factor, recommended range of n is "
346 "1-12, default factor(%u)\n",
347 gWimpyReductionFactor);
348 vlog("\t\t-z\tToggle flush to zero mode (Default: per device)\n");
349 vlog("\t\t-#\tTest just vector size given by #, where # is an element of "
350 "the set {1,2,3,4,8,16}\n");
351 vlog("\n");
352 vlog(
353 "You may also pass the number of the test on which to start.\nA second "
354 "number can be then passed to indicate how many tests to run\n\n");
355 }
356
357
358
InitCL(cl_device_id device)359 test_status InitCL(cl_device_id device)
360 {
361 int error, i;
362 size_t configSize = sizeof(gComputeDevices);
363
364 if ((error = clGetDeviceInfo(device, CL_DEVICE_MAX_COMPUTE_UNITS,
365 configSize, &gComputeDevices, NULL)))
366 gComputeDevices = 1;
367
368 configSize = sizeof(gDeviceFrequency);
369 if ((error = clGetDeviceInfo(device, CL_DEVICE_MAX_CLOCK_FREQUENCY,
370 configSize, &gDeviceFrequency, NULL)))
371 gDeviceFrequency = 0;
372
373 cl_device_fp_config floatCapabilities = 0;
374 if ((error = clGetDeviceInfo(device, CL_DEVICE_SINGLE_FP_CONFIG,
375 sizeof(floatCapabilities), &floatCapabilities,
376 NULL)))
377 floatCapabilities = 0;
378 if (0 == (CL_FP_DENORM & floatCapabilities)) gForceFTZ ^= 1;
379
380 if (0 == (floatCapabilities & CL_FP_ROUND_TO_NEAREST))
381 {
382 char profileStr[128] = "";
383 // Verify that we are an embedded profile device
384 if ((error = clGetDeviceInfo(device, CL_DEVICE_PROFILE,
385 sizeof(profileStr), profileStr, NULL)))
386 {
387 vlog_error("FAILURE: Could not get device profile: error %d\n",
388 error);
389 return TEST_FAIL;
390 }
391
392 if (strcmp(profileStr, "EMBEDDED_PROFILE"))
393 {
394 vlog_error("FAILURE: non-embedded profile device does not support "
395 "CL_FP_ROUND_TO_NEAREST\n");
396 return TEST_FAIL;
397 }
398
399 if (0 == (floatCapabilities & CL_FP_ROUND_TO_ZERO))
400 {
401 vlog_error("FAILURE: embedded profile device supports neither "
402 "CL_FP_ROUND_TO_NEAREST or CL_FP_ROUND_TO_ZERO\n");
403 return TEST_FAIL;
404 }
405
406 gIsRTZ = 1;
407 }
408
409 else if (is_extension_available(device, "cl_khr_fp64"))
410 {
411 gHasDouble = 1;
412 }
413 gTestDouble &= gHasDouble;
414
415 // detect whether profile of the device is embedded
416 char profile[1024] = "";
417 if ((error = clGetDeviceInfo(device, CL_DEVICE_PROFILE, sizeof(profile),
418 profile, NULL)))
419 {
420 vlog_error("clGetDeviceInfo failed. (%d)\n", error);
421 return TEST_FAIL;
422 }
423 else if (strstr(profile, "EMBEDDED_PROFILE"))
424 {
425 gIsEmbedded = 1;
426 if (!is_extension_available(device, "cles_khr_int64")) gHasLong = 0;
427 }
428
429 gContext = clCreateContext(NULL, 1, &device, notify_callback, NULL, &error);
430 if (NULL == gContext || error)
431 {
432 vlog_error("clCreateContext failed. (%d)\n", error);
433 return TEST_FAIL;
434 }
435
436 gQueue = clCreateCommandQueue(gContext, device, 0, &error);
437 if (NULL == gQueue || error)
438 {
439 vlog_error("clCreateCommandQueue failed. (%d)\n", error);
440 return TEST_FAIL;
441 }
442
443 // Allocate buffers
444 // FIXME: use clProtectedArray for guarded allocations?
445 gIn = malloc(BUFFER_SIZE + 2 * kPageSize);
446 gAllowZ = malloc(BUFFER_SIZE + 2 * kPageSize);
447 gRef = malloc(BUFFER_SIZE + 2 * kPageSize);
448 for (i = 0; i < kCallStyleCount; i++)
449 {
450 gOut[i] = malloc(BUFFER_SIZE + 2 * kPageSize);
451 if (NULL == gOut[i]) return TEST_FAIL;
452 }
453
454 // setup input buffers
455 gInBuffer =
456 clCreateBuffer(gContext, CL_MEM_READ_ONLY | CL_MEM_ALLOC_HOST_PTR,
457 BUFFER_SIZE, NULL, &error);
458 if (gInBuffer == NULL || error)
459 {
460 vlog_error("clCreateBuffer failed for input (%d)\n", error);
461 return TEST_FAIL;
462 }
463
464 // setup output buffers
465 for (i = 0; i < kCallStyleCount; i++)
466 {
467 gOutBuffers[i] =
468 clCreateBuffer(gContext, CL_MEM_READ_WRITE | CL_MEM_ALLOC_HOST_PTR,
469 BUFFER_SIZE, NULL, &error);
470 if (gOutBuffers[i] == NULL || error)
471 {
472 vlog_error("clCreateArray failed for output (%d)\n", error);
473 return TEST_FAIL;
474 }
475 }
476
477 char c[1024];
478 static const char *no_yes[] = { "NO", "YES" };
479 vlog("\nCompute Device info:\n");
480 clGetDeviceInfo(device, CL_DEVICE_NAME, sizeof(c), c, NULL);
481 vlog("\tDevice Name: %s\n", c);
482 clGetDeviceInfo(device, CL_DEVICE_VENDOR, sizeof(c), c, NULL);
483 vlog("\tVendor: %s\n", c);
484 clGetDeviceInfo(device, CL_DEVICE_VERSION, sizeof(c), c, NULL);
485 vlog("\tDevice Version: %s\n", c);
486 clGetDeviceInfo(device, CL_DEVICE_OPENCL_C_VERSION, sizeof(c), &c, NULL);
487 vlog("\tCL C Version: %s\n", c);
488 clGetDeviceInfo(device, CL_DRIVER_VERSION, sizeof(c), c, NULL);
489 vlog("\tDriver Version: %s\n", c);
490 vlog("\tProcessing with %ld devices\n", gComputeDevices);
491 vlog("\tDevice Frequency: %d MHz\n", gDeviceFrequency);
492 vlog("\tSubnormal values supported for floats? %s\n",
493 no_yes[0 != (CL_FP_DENORM & floatCapabilities)]);
494 vlog("\tTesting with FTZ mode ON for floats? %s\n", no_yes[0 != gForceFTZ]);
495 vlog("\tTesting with default RTZ mode for floats? %s\n",
496 no_yes[0 != gIsRTZ]);
497 vlog("\tHas Double? %s\n", no_yes[0 != gHasDouble]);
498 if (gHasDouble) vlog("\tTest Double? %s\n", no_yes[0 != gTestDouble]);
499 vlog("\tHas Long? %s\n", no_yes[0 != gHasLong]);
500 vlog("\tTesting vector sizes: ");
501 for (i = gMinVectorSize; i < gMaxVectorSize; i++)
502 vlog("\t%d", vectorSizes[i]);
503 vlog("\n");
504 return TEST_PASS;
505 }
506
507
508