You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1223 lines
38 KiB
1223 lines
38 KiB
//
|
|
// Copyright (c) 2017-2019 The Khronos Group Inc.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
//
|
|
#include "testHarness.h"
|
|
#include "compat.h"
|
|
#include <algorithm>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <cassert>
|
|
#include <stdexcept>
|
|
#include <vector>
|
|
#include "threadTesting.h"
|
|
#include "errorHelpers.h"
|
|
#include "kernelHelpers.h"
|
|
#include "fpcontrol.h"
|
|
#include "typeWrappers.h"
|
|
#include "imageHelpers.h"
|
|
#include "parseParameters.h"
|
|
|
|
#if !defined(_WIN32)
|
|
#include <sys/utsname.h>
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#if defined(__APPLE__)
|
|
#include <sys/sysctl.h>
|
|
#endif
|
|
|
|
#include <time.h>
|
|
|
|
#if !defined(__APPLE__)
|
|
#include <CL/cl.h>
|
|
#endif
|
|
|
|
int gTestsPassed = 0;
|
|
int gTestsFailed = 0;
|
|
int gFailCount;
|
|
int gTestCount;
|
|
cl_uint gRandomSeed = 0;
|
|
cl_uint gReSeed = 0;
|
|
|
|
int gFlushDenormsToZero = 0;
|
|
int gInfNanSupport = 1;
|
|
int gIsEmbedded = 0;
|
|
int gHasLong = 1;
|
|
bool gCoreILProgram = true;
|
|
|
|
#define DEFAULT_NUM_ELEMENTS 0x4000
|
|
|
|
int runTestHarness(int argc, const char *argv[], int testNum,
|
|
test_definition testList[], int forceNoContextCreation,
|
|
cl_command_queue_properties queueProps)
|
|
{
|
|
return runTestHarnessWithCheck(argc, argv, testNum, testList,
|
|
forceNoContextCreation, queueProps, NULL);
|
|
}
|
|
|
|
int skip_init_info(int count)
|
|
{
|
|
log_info("Test skipped while initialization\n");
|
|
log_info("SKIPPED %d of %d tests.\n", count, count);
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
int fail_init_info(int count)
|
|
{
|
|
log_info("Test failed while initialization\n");
|
|
log_info("FAILED %d of %d tests.\n", count, count);
|
|
return EXIT_FAILURE;
|
|
}
|
|
void version_expected_info(const char *test_name, const char *api_name,
|
|
const char *expected_version,
|
|
const char *device_version)
|
|
{
|
|
log_info("%s skipped (requires at least %s version %s, but the device "
|
|
"reports %s version %s)\n",
|
|
test_name, api_name, expected_version, api_name, device_version);
|
|
}
|
|
int runTestHarnessWithCheck(int argc, const char *argv[], int testNum,
|
|
test_definition testList[],
|
|
int forceNoContextCreation,
|
|
cl_command_queue_properties queueProps,
|
|
DeviceCheckFn deviceCheckFn)
|
|
{
|
|
test_start();
|
|
|
|
cl_device_type device_type = CL_DEVICE_TYPE_DEFAULT;
|
|
cl_uint num_platforms = 0;
|
|
cl_platform_id *platforms;
|
|
cl_device_id device;
|
|
int num_elements = DEFAULT_NUM_ELEMENTS;
|
|
cl_uint num_devices = 0;
|
|
cl_device_id *devices = NULL;
|
|
cl_uint choosen_device_index = 0;
|
|
cl_uint choosen_platform_index = 0;
|
|
|
|
int err, ret;
|
|
char *endPtr;
|
|
int based_on_env_var = 0;
|
|
|
|
|
|
/* Check for environment variable to set device type */
|
|
char *env_mode = getenv("CL_DEVICE_TYPE");
|
|
if (env_mode != NULL)
|
|
{
|
|
based_on_env_var = 1;
|
|
if (strcmp(env_mode, "gpu") == 0
|
|
|| strcmp(env_mode, "CL_DEVICE_TYPE_GPU") == 0)
|
|
device_type = CL_DEVICE_TYPE_GPU;
|
|
else if (strcmp(env_mode, "cpu") == 0
|
|
|| strcmp(env_mode, "CL_DEVICE_TYPE_CPU") == 0)
|
|
device_type = CL_DEVICE_TYPE_CPU;
|
|
else if (strcmp(env_mode, "accelerator") == 0
|
|
|| strcmp(env_mode, "CL_DEVICE_TYPE_ACCELERATOR") == 0)
|
|
device_type = CL_DEVICE_TYPE_ACCELERATOR;
|
|
else if (strcmp(env_mode, "default") == 0
|
|
|| strcmp(env_mode, "CL_DEVICE_TYPE_DEFAULT") == 0)
|
|
device_type = CL_DEVICE_TYPE_DEFAULT;
|
|
else
|
|
{
|
|
log_error("Unknown CL_DEVICE_TYPE env variable setting: "
|
|
"%s.\nAborting...\n",
|
|
env_mode);
|
|
abort();
|
|
}
|
|
}
|
|
|
|
#if defined(__APPLE__)
|
|
{
|
|
// report on any unusual library search path indirection
|
|
char *libSearchPath = getenv("DYLD_LIBRARY_PATH");
|
|
if (libSearchPath)
|
|
log_info("*** DYLD_LIBRARY_PATH = \"%s\"\n", libSearchPath);
|
|
|
|
// report on any unusual framework search path indirection
|
|
char *frameworkSearchPath = getenv("DYLD_FRAMEWORK_PATH");
|
|
if (libSearchPath)
|
|
log_info("*** DYLD_FRAMEWORK_PATH = \"%s\"\n", frameworkSearchPath);
|
|
}
|
|
#endif
|
|
|
|
env_mode = getenv("CL_DEVICE_INDEX");
|
|
if (env_mode != NULL)
|
|
{
|
|
choosen_device_index = atoi(env_mode);
|
|
}
|
|
|
|
env_mode = getenv("CL_PLATFORM_INDEX");
|
|
if (env_mode != NULL)
|
|
{
|
|
choosen_platform_index = atoi(env_mode);
|
|
}
|
|
|
|
/* Process the command line arguments */
|
|
|
|
argc = parseCustomParam(argc, argv);
|
|
if (argc == -1)
|
|
{
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
/* Special case: just list the tests */
|
|
if ((argc > 1)
|
|
&& (!strcmp(argv[1], "-list") || !strcmp(argv[1], "-h")
|
|
|| !strcmp(argv[1], "--help")))
|
|
{
|
|
char *fileName = getenv("CL_CONFORMANCE_RESULTS_FILENAME");
|
|
|
|
log_info(
|
|
"Usage: %s [<test name>*] [pid<num>] [id<num>] [<device type>]\n",
|
|
argv[0]);
|
|
log_info("\t<test name>\tOne or more of: (wildcard character '*') "
|
|
"(default *)\n");
|
|
log_info("\tpid<num>\tIndicates platform at index <num> should be used "
|
|
"(default 0).\n");
|
|
log_info("\tid<num>\t\tIndicates device at index <num> should be used "
|
|
"(default 0).\n");
|
|
log_info("\t<device_type>\tcpu|gpu|accelerator|<CL_DEVICE_TYPE_*> "
|
|
"(default CL_DEVICE_TYPE_DEFAULT)\n");
|
|
log_info("\n");
|
|
log_info("\tNOTE: You may pass environment variable "
|
|
"CL_CONFORMANCE_RESULTS_FILENAME (currently '%s')\n",
|
|
fileName != NULL ? fileName : "<undefined>");
|
|
log_info("\t to save results to JSON file.\n");
|
|
|
|
log_info("\n");
|
|
log_info("Test names:\n");
|
|
for (int i = 0; i < testNum; i++)
|
|
{
|
|
log_info("\t%s\n", testList[i].name);
|
|
}
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
/* How are we supposed to seed the random # generators? */
|
|
if (argc > 1 && strcmp(argv[argc - 1], "randomize") == 0)
|
|
{
|
|
gRandomSeed = (cl_uint)time(NULL);
|
|
log_info("Random seed: %u.\n", gRandomSeed);
|
|
gReSeed = 1;
|
|
argc--;
|
|
}
|
|
else
|
|
{
|
|
log_info(" Initializing random seed to 0.\n");
|
|
}
|
|
|
|
/* Do we have an integer to specify the number of elements to pass to tests?
|
|
*/
|
|
if (argc > 1)
|
|
{
|
|
ret = (int)strtol(argv[argc - 1], &endPtr, 10);
|
|
if (endPtr != argv[argc - 1] && *endPtr == 0)
|
|
{
|
|
/* By spec, this means the entire string was a valid integer, so we
|
|
* treat it as a num_elements spec */
|
|
/* (hence why we stored the result in ret first) */
|
|
num_elements = ret;
|
|
log_info("Testing with num_elements of %d\n", num_elements);
|
|
argc--;
|
|
}
|
|
}
|
|
|
|
/* Do we have a CPU/GPU specification? */
|
|
if (argc > 1)
|
|
{
|
|
if (strcmp(argv[argc - 1], "gpu") == 0
|
|
|| strcmp(argv[argc - 1], "CL_DEVICE_TYPE_GPU") == 0)
|
|
{
|
|
device_type = CL_DEVICE_TYPE_GPU;
|
|
argc--;
|
|
}
|
|
else if (strcmp(argv[argc - 1], "cpu") == 0
|
|
|| strcmp(argv[argc - 1], "CL_DEVICE_TYPE_CPU") == 0)
|
|
{
|
|
device_type = CL_DEVICE_TYPE_CPU;
|
|
argc--;
|
|
}
|
|
else if (strcmp(argv[argc - 1], "accelerator") == 0
|
|
|| strcmp(argv[argc - 1], "CL_DEVICE_TYPE_ACCELERATOR") == 0)
|
|
{
|
|
device_type = CL_DEVICE_TYPE_ACCELERATOR;
|
|
argc--;
|
|
}
|
|
else if (strcmp(argv[argc - 1], "CL_DEVICE_TYPE_DEFAULT") == 0)
|
|
{
|
|
device_type = CL_DEVICE_TYPE_DEFAULT;
|
|
argc--;
|
|
}
|
|
}
|
|
|
|
/* Did we choose a specific device index? */
|
|
if (argc > 1)
|
|
{
|
|
if (strlen(argv[argc - 1]) >= 3 && argv[argc - 1][0] == 'i'
|
|
&& argv[argc - 1][1] == 'd')
|
|
{
|
|
choosen_device_index = atoi(&(argv[argc - 1][2]));
|
|
argc--;
|
|
}
|
|
}
|
|
|
|
/* Did we choose a specific platform index? */
|
|
if (argc > 1)
|
|
{
|
|
if (strlen(argv[argc - 1]) >= 3 && argv[argc - 1][0] == 'p'
|
|
&& argv[argc - 1][1] == 'i' && argv[argc - 1][2] == 'd')
|
|
{
|
|
choosen_platform_index = atoi(&(argv[argc - 1][3]));
|
|
argc--;
|
|
}
|
|
}
|
|
|
|
|
|
switch (device_type)
|
|
{
|
|
case CL_DEVICE_TYPE_GPU: log_info("Requesting GPU device "); break;
|
|
case CL_DEVICE_TYPE_CPU: log_info("Requesting CPU device "); break;
|
|
case CL_DEVICE_TYPE_ACCELERATOR:
|
|
log_info("Requesting Accelerator device ");
|
|
break;
|
|
case CL_DEVICE_TYPE_DEFAULT:
|
|
log_info("Requesting Default device ");
|
|
break;
|
|
default: log_error("Requesting unknown device "); return EXIT_FAILURE;
|
|
}
|
|
log_info(based_on_env_var ? "based on environment variable "
|
|
: "based on command line ");
|
|
log_info("for platform index %d and device index %d\n",
|
|
choosen_platform_index, choosen_device_index);
|
|
|
|
#if defined(__APPLE__)
|
|
#if defined(__i386__) || defined(__x86_64__)
|
|
#define kHasSSE3 0x00000008
|
|
#define kHasSupplementalSSE3 0x00000100
|
|
#define kHasSSE4_1 0x00000400
|
|
#define kHasSSE4_2 0x00000800
|
|
/* check our environment for a hint to disable SSE variants */
|
|
{
|
|
const char *env = getenv("CL_MAX_SSE");
|
|
if (env)
|
|
{
|
|
extern int _cpu_capabilities;
|
|
int mask = 0;
|
|
if (0 == strcasecmp(env, "SSE4.1"))
|
|
mask = kHasSSE4_2;
|
|
else if (0 == strcasecmp(env, "SSSE3"))
|
|
mask = kHasSSE4_2 | kHasSSE4_1;
|
|
else if (0 == strcasecmp(env, "SSE3"))
|
|
mask = kHasSSE4_2 | kHasSSE4_1 | kHasSupplementalSSE3;
|
|
else if (0 == strcasecmp(env, "SSE2"))
|
|
mask =
|
|
kHasSSE4_2 | kHasSSE4_1 | kHasSupplementalSSE3 | kHasSSE3;
|
|
else
|
|
{
|
|
log_error("Error: Unknown CL_MAX_SSE setting: %s\n", env);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
log_info("*** Environment: CL_MAX_SSE = %s ***\n", env);
|
|
_cpu_capabilities &= ~mask;
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
/* Get the platform */
|
|
err = clGetPlatformIDs(0, NULL, &num_platforms);
|
|
if (err)
|
|
{
|
|
print_error(err, "clGetPlatformIDs failed");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
platforms =
|
|
(cl_platform_id *)malloc(num_platforms * sizeof(cl_platform_id));
|
|
if (!platforms || choosen_platform_index >= num_platforms)
|
|
{
|
|
log_error("platform index out of range -- choosen_platform_index (%d) "
|
|
">= num_platforms (%d)\n",
|
|
choosen_platform_index, num_platforms);
|
|
return EXIT_FAILURE;
|
|
}
|
|
BufferOwningPtr<cl_platform_id> platformsBuf(platforms);
|
|
|
|
err = clGetPlatformIDs(num_platforms, platforms, NULL);
|
|
if (err)
|
|
{
|
|
print_error(err, "clGetPlatformIDs failed");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
/* Get the number of requested devices */
|
|
err = clGetDeviceIDs(platforms[choosen_platform_index], device_type, 0,
|
|
NULL, &num_devices);
|
|
if (err)
|
|
{
|
|
print_error(err, "clGetDeviceIDs failed");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
devices = (cl_device_id *)malloc(num_devices * sizeof(cl_device_id));
|
|
if (!devices || choosen_device_index >= num_devices)
|
|
{
|
|
log_error("device index out of range -- choosen_device_index (%d) >= "
|
|
"num_devices (%d)\n",
|
|
choosen_device_index, num_devices);
|
|
return EXIT_FAILURE;
|
|
}
|
|
BufferOwningPtr<cl_device_id> devicesBuf(devices);
|
|
|
|
|
|
/* Get the requested device */
|
|
err = clGetDeviceIDs(platforms[choosen_platform_index], device_type,
|
|
num_devices, devices, NULL);
|
|
if (err)
|
|
{
|
|
print_error(err, "clGetDeviceIDs failed");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
device = devices[choosen_device_index];
|
|
|
|
err = clGetDeviceInfo(device, CL_DEVICE_TYPE, sizeof(gDeviceType),
|
|
&gDeviceType, NULL);
|
|
if (err)
|
|
{
|
|
print_error(err, "Unable to get device type");
|
|
return TEST_FAIL;
|
|
}
|
|
|
|
if (printDeviceHeader(device) != CL_SUCCESS)
|
|
{
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
cl_device_fp_config fpconfig = 0;
|
|
err = clGetDeviceInfo(device, CL_DEVICE_SINGLE_FP_CONFIG, sizeof(fpconfig),
|
|
&fpconfig, NULL);
|
|
if (err)
|
|
{
|
|
print_error(err,
|
|
"clGetDeviceInfo for CL_DEVICE_SINGLE_FP_CONFIG failed");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
gFlushDenormsToZero = (0 == (fpconfig & CL_FP_DENORM));
|
|
log_info("Supports single precision denormals: %s\n",
|
|
gFlushDenormsToZero ? "NO" : "YES");
|
|
log_info("sizeof( void*) = %d (host)\n", (int)sizeof(void *));
|
|
|
|
// detect whether profile of the device is embedded
|
|
char profile[1024] = "";
|
|
err = clGetDeviceInfo(device, CL_DEVICE_PROFILE, sizeof(profile), profile,
|
|
NULL);
|
|
if (err)
|
|
{
|
|
print_error(err, "clGetDeviceInfo for CL_DEVICE_PROFILE failed\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
gIsEmbedded = NULL != strstr(profile, "EMBEDDED_PROFILE");
|
|
|
|
// detect the floating point capabilities
|
|
cl_device_fp_config floatCapabilities = 0;
|
|
err = clGetDeviceInfo(device, CL_DEVICE_SINGLE_FP_CONFIG,
|
|
sizeof(floatCapabilities), &floatCapabilities, NULL);
|
|
if (err)
|
|
{
|
|
print_error(err,
|
|
"clGetDeviceInfo for CL_DEVICE_SINGLE_FP_CONFIG failed\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
// Check for problems that only embedded will have
|
|
if (gIsEmbedded)
|
|
{
|
|
// If the device is embedded, we need to detect if the device supports
|
|
// Infinity and NaN
|
|
if ((floatCapabilities & CL_FP_INF_NAN) == 0) gInfNanSupport = 0;
|
|
|
|
// check the extensions list to see if ulong and long are supported
|
|
if (!is_extension_available(device, "cles_khr_int64")) gHasLong = 0;
|
|
}
|
|
|
|
cl_uint device_address_bits = 0;
|
|
if ((err = clGetDeviceInfo(device, CL_DEVICE_ADDRESS_BITS,
|
|
sizeof(device_address_bits),
|
|
&device_address_bits, NULL)))
|
|
{
|
|
print_error(err, "Unable to obtain device address bits");
|
|
return EXIT_FAILURE;
|
|
}
|
|
if (device_address_bits)
|
|
log_info("sizeof( void*) = %d (device)\n", device_address_bits / 8);
|
|
else
|
|
{
|
|
log_error("Invalid device address bit size returned by device.\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
if (gCompilationMode == kSpir_v)
|
|
{
|
|
test_status spirv_readiness = check_spirv_compilation_readiness(device);
|
|
if (spirv_readiness != TEST_PASS)
|
|
{
|
|
switch (spirv_readiness)
|
|
{
|
|
case TEST_PASS: break;
|
|
case TEST_FAIL: return fail_init_info(testNum);
|
|
case TEST_SKIP: return skip_init_info(testNum);
|
|
case TEST_SKIPPED_ITSELF: return skip_init_info(testNum);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* If we have a device checking function, run it */
|
|
if ((deviceCheckFn != NULL))
|
|
{
|
|
test_status status = deviceCheckFn(device);
|
|
switch (status)
|
|
{
|
|
case TEST_PASS: break;
|
|
case TEST_FAIL: return fail_init_info(testNum);
|
|
case TEST_SKIP: return skip_init_info(testNum);
|
|
case TEST_SKIPPED_ITSELF: return skip_init_info(testNum);
|
|
}
|
|
}
|
|
|
|
if (num_elements <= 0) num_elements = DEFAULT_NUM_ELEMENTS;
|
|
|
|
// On most platforms which support denorm, default is FTZ off. However,
|
|
// on some hardware where the reference is computed, default might be
|
|
// flush denorms to zero e.g. arm. This creates issues in result
|
|
// verification. Since spec allows the implementation to either flush or
|
|
// not flush denorms to zero, an implementation may choose not be flush
|
|
// i.e. return denorm result whereas reference result may be zero
|
|
// (flushed denorm). Hence we need to disable denorm flushing on host
|
|
// side where reference is being computed to make sure we get
|
|
// non-flushed reference result. If implementation returns flushed
|
|
// result, we correctly take care of that in verification code.
|
|
#if defined(__APPLE__) && defined(__arm__)
|
|
FPU_mode_type oldMode;
|
|
DisableFTZ(&oldMode);
|
|
#endif
|
|
|
|
int error = parseAndCallCommandLineTests(argc, argv, device, testNum,
|
|
testList, forceNoContextCreation,
|
|
queueProps, num_elements);
|
|
|
|
#if defined(__APPLE__) && defined(__arm__)
|
|
// Restore the old FP mode before leaving.
|
|
RestoreFPState(&oldMode);
|
|
#endif
|
|
|
|
return (error == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
|
|
}
|
|
|
|
static int find_matching_tests(test_definition testList[],
|
|
unsigned char selectedTestList[], int testNum,
|
|
const char *argument, bool isWildcard)
|
|
{
|
|
int found_tests = 0;
|
|
size_t wildcard_length = strlen(argument) - 1; /* -1 for the asterisk */
|
|
|
|
for (int i = 0; i < testNum; i++)
|
|
{
|
|
if ((!isWildcard && strcmp(testList[i].name, argument) == 0)
|
|
|| (isWildcard
|
|
&& strncmp(testList[i].name, argument, wildcard_length) == 0))
|
|
{
|
|
if (selectedTestList[i])
|
|
{
|
|
log_error("ERROR: Test '%s' has already been selected.\n",
|
|
testList[i].name);
|
|
return EXIT_FAILURE;
|
|
}
|
|
else if (testList[i].func == NULL)
|
|
{
|
|
log_error("ERROR: Test '%s' is missing implementation.\n",
|
|
testList[i].name);
|
|
return EXIT_FAILURE;
|
|
}
|
|
else
|
|
{
|
|
selectedTestList[i] = 1;
|
|
found_tests = 1;
|
|
if (!isWildcard)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!found_tests)
|
|
{
|
|
log_error("ERROR: The argument '%s' did not match any test names.\n",
|
|
argument);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
static int saveResultsToJson(const char *fileName, const char *suiteName,
|
|
test_definition testList[],
|
|
unsigned char selectedTestList[],
|
|
test_status resultTestList[], int testNum)
|
|
{
|
|
FILE *file = fopen(fileName, "w");
|
|
if (NULL == file)
|
|
{
|
|
log_error("ERROR: Failed to open '%s' for writing results.\n",
|
|
fileName);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
const char *save_map[] = { "success", "failure" };
|
|
const char *result_map[] = { "pass", "fail", "skip" };
|
|
const char *linebreak[] = { "", ",\n" };
|
|
int add_linebreak = 0;
|
|
|
|
fprintf(file, "{\n");
|
|
fprintf(file, "\t\"cmd\": \"%s\",\n", suiteName);
|
|
fprintf(file, "\t\"results\": {\n");
|
|
|
|
for (int i = 0; i < testNum; ++i)
|
|
{
|
|
if (selectedTestList[i])
|
|
{
|
|
fprintf(file, "%s\t\t\"%s\": \"%s\"", linebreak[add_linebreak],
|
|
testList[i].name, result_map[(int)resultTestList[i]]);
|
|
add_linebreak = 1;
|
|
}
|
|
}
|
|
fprintf(file, "\n");
|
|
|
|
fprintf(file, "\t}\n");
|
|
fprintf(file, "}\n");
|
|
|
|
int ret = fclose(file) ? 1 : 0;
|
|
|
|
log_info("Saving results to %s: %s!\n", fileName, save_map[ret]);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void print_results(int failed, int count, const char *name)
|
|
{
|
|
if (count < failed)
|
|
{
|
|
count = failed;
|
|
}
|
|
|
|
if (failed == 0)
|
|
{
|
|
if (count > 1)
|
|
{
|
|
log_info("PASSED %d of %d %ss.\n", count, count, name);
|
|
}
|
|
else
|
|
{
|
|
log_info("PASSED %s.\n", name);
|
|
}
|
|
}
|
|
else if (failed > 0)
|
|
{
|
|
if (count > 1)
|
|
{
|
|
log_error("FAILED %d of %d %ss.\n", failed, count, name);
|
|
}
|
|
else
|
|
{
|
|
log_error("FAILED %s.\n", name);
|
|
}
|
|
}
|
|
}
|
|
|
|
int parseAndCallCommandLineTests(int argc, const char *argv[],
|
|
cl_device_id device, int testNum,
|
|
test_definition testList[],
|
|
int forceNoContextCreation,
|
|
cl_command_queue_properties queueProps,
|
|
int num_elements)
|
|
{
|
|
int ret = EXIT_SUCCESS;
|
|
|
|
unsigned char *selectedTestList = (unsigned char *)calloc(testNum, 1);
|
|
test_status *resultTestList = NULL;
|
|
|
|
if (argc == 1)
|
|
{
|
|
/* No actual arguments, all tests will be run. */
|
|
memset(selectedTestList, 1, testNum);
|
|
}
|
|
else
|
|
{
|
|
for (int i = 1; i < argc; i++)
|
|
{
|
|
if (strchr(argv[i], '*') != NULL)
|
|
{
|
|
ret = find_matching_tests(testList, selectedTestList, testNum,
|
|
argv[i], true);
|
|
}
|
|
else
|
|
{
|
|
if (strcmp(argv[i], "all") == 0)
|
|
{
|
|
memset(selectedTestList, 1, testNum);
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
ret = find_matching_tests(testList, selectedTestList,
|
|
testNum, argv[i], false);
|
|
}
|
|
}
|
|
|
|
if (ret == EXIT_FAILURE)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ret == EXIT_SUCCESS)
|
|
{
|
|
resultTestList =
|
|
(test_status *)calloc(testNum, sizeof(*resultTestList));
|
|
|
|
callTestFunctions(testList, selectedTestList, resultTestList, testNum,
|
|
device, forceNoContextCreation, num_elements,
|
|
queueProps);
|
|
|
|
print_results(gFailCount, gTestCount, "sub-test");
|
|
print_results(gTestsFailed, gTestsFailed + gTestsPassed, "test");
|
|
|
|
char *filename = getenv("CL_CONFORMANCE_RESULTS_FILENAME");
|
|
if (filename != NULL)
|
|
{
|
|
ret = saveResultsToJson(filename, argv[0], testList,
|
|
selectedTestList, resultTestList, testNum);
|
|
}
|
|
|
|
if (std::any_of(resultTestList, resultTestList + testNum,
|
|
[](test_status result) {
|
|
switch (result)
|
|
{
|
|
case TEST_PASS:
|
|
case TEST_SKIP: return false;
|
|
case TEST_FAIL:
|
|
default: return true;
|
|
};
|
|
}))
|
|
{
|
|
ret = EXIT_FAILURE;
|
|
}
|
|
}
|
|
|
|
free(selectedTestList);
|
|
free(resultTestList);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void callTestFunctions(test_definition testList[],
|
|
unsigned char selectedTestList[],
|
|
test_status resultTestList[], int testNum,
|
|
cl_device_id deviceToUse, int forceNoContextCreation,
|
|
int numElementsToUse,
|
|
cl_command_queue_properties queueProps)
|
|
{
|
|
for (int i = 0; i < testNum; ++i)
|
|
{
|
|
if (selectedTestList[i])
|
|
{
|
|
resultTestList[i] = callSingleTestFunction(
|
|
testList[i], deviceToUse, forceNoContextCreation,
|
|
numElementsToUse, queueProps);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CL_CALLBACK notify_callback(const char *errinfo, const void *private_info,
|
|
size_t cb, void *user_data)
|
|
{
|
|
log_info("%s\n", errinfo);
|
|
}
|
|
|
|
// Actual function execution
|
|
test_status callSingleTestFunction(test_definition test,
|
|
cl_device_id deviceToUse,
|
|
int forceNoContextCreation,
|
|
int numElementsToUse,
|
|
const cl_queue_properties queueProps)
|
|
{
|
|
test_status status;
|
|
cl_int error;
|
|
cl_context context = NULL;
|
|
cl_command_queue queue = NULL;
|
|
|
|
log_info("%s...\n", test.name);
|
|
fflush(stdout);
|
|
|
|
const Version device_version = get_device_cl_version(deviceToUse);
|
|
if (test.min_version > device_version)
|
|
{
|
|
version_expected_info(test.name, "OpenCL",
|
|
test.min_version.to_string().c_str(),
|
|
device_version.to_string().c_str());
|
|
return TEST_SKIP;
|
|
}
|
|
|
|
/* Create a context to work with, unless we're told not to */
|
|
if (!forceNoContextCreation)
|
|
{
|
|
context = clCreateContext(NULL, 1, &deviceToUse, notify_callback, NULL,
|
|
&error);
|
|
if (!context)
|
|
{
|
|
print_error(error, "Unable to create testing context");
|
|
return TEST_FAIL;
|
|
}
|
|
|
|
if (device_version < Version(2, 0))
|
|
{
|
|
queue =
|
|
clCreateCommandQueue(context, deviceToUse, queueProps, &error);
|
|
}
|
|
else
|
|
{
|
|
const cl_command_queue_properties cmd_queueProps =
|
|
(queueProps) ? CL_QUEUE_PROPERTIES : 0;
|
|
cl_command_queue_properties queueCreateProps[] = { cmd_queueProps,
|
|
queueProps, 0 };
|
|
queue = clCreateCommandQueueWithProperties(
|
|
context, deviceToUse, &queueCreateProps[0], &error);
|
|
}
|
|
|
|
if (queue == NULL)
|
|
{
|
|
print_error(error, "Unable to create testing command queue");
|
|
return TEST_FAIL;
|
|
}
|
|
}
|
|
|
|
/* Run the test and print the result */
|
|
error = check_functions_for_offline_compiler(test.name, deviceToUse);
|
|
test_missing_support_offline_cmpiler(error, test.name);
|
|
|
|
if (test.func == NULL)
|
|
{
|
|
// Skip unimplemented test, can happen when all of the tests are
|
|
// selected
|
|
log_info("%s test currently not implemented\n", test.name);
|
|
status = TEST_SKIP;
|
|
}
|
|
else
|
|
{
|
|
int ret = test.func(deviceToUse, context, queue, numElementsToUse);
|
|
if (ret == TEST_SKIPPED_ITSELF)
|
|
{
|
|
/* Tests can also let us know they're not supported by the
|
|
* implementation */
|
|
log_info("%s test not supported\n", test.name);
|
|
status = TEST_SKIP;
|
|
}
|
|
else
|
|
{
|
|
/* Print result */
|
|
if (ret == 0)
|
|
{
|
|
log_info("%s passed\n", test.name);
|
|
gTestsPassed++;
|
|
status = TEST_PASS;
|
|
}
|
|
else
|
|
{
|
|
log_error("%s FAILED\n", test.name);
|
|
gTestsFailed++;
|
|
status = TEST_FAIL;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Release the context */
|
|
if (!forceNoContextCreation)
|
|
{
|
|
int error = clFinish(queue);
|
|
if (error)
|
|
{
|
|
log_error("clFinish failed: %s\n", IGetErrorString(error));
|
|
status = TEST_FAIL;
|
|
}
|
|
clReleaseCommandQueue(queue);
|
|
clReleaseContext(context);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
#if !defined(__APPLE__)
|
|
void memset_pattern4(void *dest, const void *src_pattern, size_t bytes)
|
|
{
|
|
uint32_t pat = ((uint32_t *)src_pattern)[0];
|
|
size_t count = bytes / 4;
|
|
size_t i;
|
|
uint32_t *d = (uint32_t *)dest;
|
|
|
|
for (i = 0; i < count; i++) d[i] = pat;
|
|
|
|
d += i;
|
|
|
|
bytes &= 3;
|
|
if (bytes) memcpy(d, src_pattern, bytes);
|
|
}
|
|
#endif
|
|
|
|
cl_device_type GetDeviceType(cl_device_id d)
|
|
{
|
|
cl_device_type result = -1;
|
|
cl_int err =
|
|
clGetDeviceInfo(d, CL_DEVICE_TYPE, sizeof(result), &result, NULL);
|
|
if (CL_SUCCESS != err)
|
|
log_error("ERROR: Unable to get device type for device %p\n", d);
|
|
return result;
|
|
}
|
|
|
|
|
|
cl_device_id GetOpposingDevice(cl_device_id device)
|
|
{
|
|
cl_int error;
|
|
cl_device_id *otherDevices;
|
|
cl_uint actualCount;
|
|
cl_platform_id plat;
|
|
|
|
// Get the platform of the device to use for getting a list of devices
|
|
error =
|
|
clGetDeviceInfo(device, CL_DEVICE_PLATFORM, sizeof(plat), &plat, NULL);
|
|
if (error != CL_SUCCESS)
|
|
{
|
|
print_error(error, "Unable to get device's platform");
|
|
return NULL;
|
|
}
|
|
|
|
// Get a list of all devices
|
|
error = clGetDeviceIDs(plat, CL_DEVICE_TYPE_ALL, 0, NULL, &actualCount);
|
|
if (error != CL_SUCCESS)
|
|
{
|
|
print_error(error, "Unable to get list of devices size");
|
|
return NULL;
|
|
}
|
|
otherDevices = (cl_device_id *)malloc(actualCount * sizeof(cl_device_id));
|
|
if (NULL == otherDevices)
|
|
{
|
|
print_error(error, "Unable to allocate list of other devices.");
|
|
return NULL;
|
|
}
|
|
BufferOwningPtr<cl_device_id> otherDevicesBuf(otherDevices);
|
|
|
|
error = clGetDeviceIDs(plat, CL_DEVICE_TYPE_ALL, actualCount, otherDevices,
|
|
NULL);
|
|
if (error != CL_SUCCESS)
|
|
{
|
|
print_error(error, "Unable to get list of devices");
|
|
return NULL;
|
|
}
|
|
|
|
if (actualCount == 1)
|
|
{
|
|
return device; // NULL means error, returning self means we couldn't
|
|
// find another one
|
|
}
|
|
|
|
// Loop and just find one that isn't the one we were given
|
|
cl_uint i;
|
|
for (i = 0; i < actualCount; i++)
|
|
{
|
|
if (otherDevices[i] != device)
|
|
{
|
|
cl_device_type newType;
|
|
error = clGetDeviceInfo(otherDevices[i], CL_DEVICE_TYPE,
|
|
sizeof(newType), &newType, NULL);
|
|
if (error != CL_SUCCESS)
|
|
{
|
|
print_error(error,
|
|
"Unable to get device type for other device");
|
|
return NULL;
|
|
}
|
|
cl_device_id result = otherDevices[i];
|
|
return result;
|
|
}
|
|
}
|
|
|
|
// Should never get here
|
|
return NULL;
|
|
}
|
|
|
|
Version get_device_cl_version(cl_device_id device)
|
|
{
|
|
size_t str_size;
|
|
cl_int err = clGetDeviceInfo(device, CL_DEVICE_VERSION, 0, NULL, &str_size);
|
|
ASSERT_SUCCESS(err, "clGetDeviceInfo");
|
|
|
|
std::vector<char> str(str_size);
|
|
err =
|
|
clGetDeviceInfo(device, CL_DEVICE_VERSION, str_size, str.data(), NULL);
|
|
ASSERT_SUCCESS(err, "clGetDeviceInfo");
|
|
|
|
if (strstr(str.data(), "OpenCL 1.0") != NULL)
|
|
return Version(1, 0);
|
|
else if (strstr(str.data(), "OpenCL 1.1") != NULL)
|
|
return Version(1, 1);
|
|
else if (strstr(str.data(), "OpenCL 1.2") != NULL)
|
|
return Version(1, 2);
|
|
else if (strstr(str.data(), "OpenCL 2.0") != NULL)
|
|
return Version(2, 0);
|
|
else if (strstr(str.data(), "OpenCL 2.1") != NULL)
|
|
return Version(2, 1);
|
|
else if (strstr(str.data(), "OpenCL 2.2") != NULL)
|
|
return Version(2, 2);
|
|
else if (strstr(str.data(), "OpenCL 3.0") != NULL)
|
|
return Version(3, 0);
|
|
|
|
throw std::runtime_error(std::string("Unknown OpenCL version: ")
|
|
+ str.data());
|
|
}
|
|
|
|
bool check_device_spirv_version_reported(cl_device_id device)
|
|
{
|
|
size_t str_size;
|
|
cl_int err;
|
|
std::vector<char> str;
|
|
if (gCoreILProgram)
|
|
{
|
|
err = clGetDeviceInfo(device, CL_DEVICE_IL_VERSION, 0, NULL, &str_size);
|
|
if (err != CL_SUCCESS)
|
|
{
|
|
log_error(
|
|
"clGetDeviceInfo: cannot read CL_DEVICE_IL_VERSION size;");
|
|
return false;
|
|
}
|
|
|
|
str.resize(str_size);
|
|
err = clGetDeviceInfo(device, CL_DEVICE_IL_VERSION, str_size,
|
|
str.data(), NULL);
|
|
if (err != CL_SUCCESS)
|
|
{
|
|
log_error(
|
|
"clGetDeviceInfo: cannot read CL_DEVICE_IL_VERSION value;");
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
cl_int err = clGetDeviceInfo(device, CL_DEVICE_IL_VERSION_KHR, 0, NULL,
|
|
&str_size);
|
|
if (err != CL_SUCCESS)
|
|
{
|
|
log_error(
|
|
"clGetDeviceInfo: cannot read CL_DEVICE_IL_VERSION_KHR size;");
|
|
return false;
|
|
}
|
|
|
|
str.resize(str_size);
|
|
err = clGetDeviceInfo(device, CL_DEVICE_IL_VERSION_KHR, str_size,
|
|
str.data(), NULL);
|
|
if (err != CL_SUCCESS)
|
|
{
|
|
log_error(
|
|
"clGetDeviceInfo: cannot read CL_DEVICE_IL_VERSION_KHR value;");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (strstr(str.data(), "SPIR-V") == NULL)
|
|
{
|
|
log_info("This device does not support SPIR-V offline compilation.\n");
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
Version spirv_version = get_device_spirv_il_version(device);
|
|
log_info("This device supports SPIR-V offline compilation. SPIR-V "
|
|
"version is %s\n",
|
|
spirv_version.to_string().c_str());
|
|
}
|
|
return true;
|
|
}
|
|
|
|
Version get_device_spirv_il_version(cl_device_id device)
|
|
{
|
|
size_t str_size;
|
|
cl_int err;
|
|
std::vector<char> str;
|
|
if (gCoreILProgram)
|
|
{
|
|
err = clGetDeviceInfo(device, CL_DEVICE_IL_VERSION, 0, NULL, &str_size);
|
|
ASSERT_SUCCESS(err, "clGetDeviceInfo");
|
|
|
|
str.resize(str_size);
|
|
err = clGetDeviceInfo(device, CL_DEVICE_IL_VERSION, str_size,
|
|
str.data(), NULL);
|
|
ASSERT_SUCCESS(err, "clGetDeviceInfo");
|
|
}
|
|
else
|
|
{
|
|
err = clGetDeviceInfo(device, CL_DEVICE_IL_VERSION_KHR, 0, NULL,
|
|
&str_size);
|
|
ASSERT_SUCCESS(err, "clGetDeviceInfo");
|
|
|
|
str.resize(str_size);
|
|
err = clGetDeviceInfo(device, CL_DEVICE_IL_VERSION_KHR, str_size,
|
|
str.data(), NULL);
|
|
ASSERT_SUCCESS(err, "clGetDeviceInfo");
|
|
}
|
|
|
|
if (strstr(str.data(), "SPIR-V_1.0") != NULL)
|
|
return Version(1, 0);
|
|
else if (strstr(str.data(), "SPIR-V_1.1") != NULL)
|
|
return Version(1, 1);
|
|
else if (strstr(str.data(), "SPIR-V_1.2") != NULL)
|
|
return Version(1, 2);
|
|
else if (strstr(str.data(), "SPIR-V_1.3") != NULL)
|
|
return Version(1, 3);
|
|
else if (strstr(str.data(), "SPIR-V_1.4") != NULL)
|
|
return Version(1, 4);
|
|
else if (strstr(str.data(), "SPIR-V_1.5") != NULL)
|
|
return Version(1, 5);
|
|
|
|
throw std::runtime_error(std::string("Unknown SPIR-V version: ")
|
|
+ str.data());
|
|
}
|
|
|
|
test_status check_spirv_compilation_readiness(cl_device_id device)
|
|
{
|
|
auto ocl_version = get_device_cl_version(device);
|
|
auto ocl_expected_min_version = Version(2, 1);
|
|
|
|
if (ocl_version < ocl_expected_min_version)
|
|
{
|
|
if (is_extension_available(device, "cl_khr_il_program"))
|
|
{
|
|
gCoreILProgram = false;
|
|
bool spirv_supported = check_device_spirv_version_reported(device);
|
|
if (spirv_supported == false)
|
|
{
|
|
log_error("SPIR-V intermediate language not supported !!! "
|
|
"OpenCL %s requires support.\n",
|
|
ocl_version.to_string().c_str());
|
|
return TEST_FAIL;
|
|
}
|
|
else
|
|
{
|
|
return TEST_PASS;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
log_error("SPIR-V intermediate language support on OpenCL version "
|
|
"%s requires cl_khr_il_program extension.\n",
|
|
ocl_version.to_string().c_str());
|
|
return TEST_SKIP;
|
|
}
|
|
}
|
|
|
|
bool spirv_supported = check_device_spirv_version_reported(device);
|
|
if (ocl_version >= ocl_expected_min_version && ocl_version <= Version(2, 2))
|
|
{
|
|
if (spirv_supported == false)
|
|
{
|
|
log_error("SPIR-V intermediate language not supported !!! OpenCL "
|
|
"%s requires support.\n",
|
|
ocl_version.to_string().c_str());
|
|
return TEST_FAIL;
|
|
}
|
|
}
|
|
|
|
if (ocl_version > Version(2, 2))
|
|
{
|
|
if (spirv_supported == false)
|
|
{
|
|
log_info("SPIR-V intermediate language not supported in OpenCL %s. "
|
|
"Test skipped.\n",
|
|
ocl_version.to_string().c_str());
|
|
return TEST_SKIP;
|
|
}
|
|
}
|
|
return TEST_PASS;
|
|
}
|
|
|
|
cl_platform_id getPlatformFromDevice(cl_device_id deviceID)
|
|
{
|
|
cl_platform_id platform = nullptr;
|
|
cl_int err = clGetDeviceInfo(deviceID, CL_DEVICE_PLATFORM, sizeof(platform),
|
|
&platform, nullptr);
|
|
ASSERT_SUCCESS(err, "clGetDeviceInfo");
|
|
return platform;
|
|
}
|
|
|
|
void PrintArch(void)
|
|
{
|
|
vlog("sizeof( void*) = %ld\n", sizeof(void *));
|
|
#if defined(__ppc__)
|
|
vlog("ARCH:\tppc\n");
|
|
#elif defined(__ppc64__)
|
|
vlog("ARCH:\tppc64\n");
|
|
#elif defined(__PPC__)
|
|
vlog("ARCH:\tppc\n");
|
|
#elif defined(__i386__)
|
|
vlog("ARCH:\ti386\n");
|
|
#elif defined(__x86_64__)
|
|
vlog("ARCH:\tx86_64\n");
|
|
#elif defined(__arm__)
|
|
vlog("ARCH:\tarm\n");
|
|
#elif defined(__aarch64__)
|
|
vlog("ARCH:\taarch64\n");
|
|
#elif defined(_WIN32)
|
|
vlog("ARCH:\tWindows\n");
|
|
#else
|
|
#error unknown arch
|
|
#endif
|
|
|
|
#if defined(__APPLE__)
|
|
|
|
int type = 0;
|
|
size_t typeSize = sizeof(type);
|
|
sysctlbyname("hw.cputype", &type, &typeSize, NULL, 0);
|
|
vlog("cpu type:\t%d\n", type);
|
|
typeSize = sizeof(type);
|
|
sysctlbyname("hw.cpusubtype", &type, &typeSize, NULL, 0);
|
|
vlog("cpu subtype:\t%d\n", type);
|
|
|
|
#elif defined(__linux__)
|
|
struct utsname buffer;
|
|
|
|
if (uname(&buffer) != 0)
|
|
{
|
|
vlog("uname error");
|
|
}
|
|
else
|
|
{
|
|
vlog("system name = %s\n", buffer.sysname);
|
|
vlog("node name = %s\n", buffer.nodename);
|
|
vlog("release = %s\n", buffer.release);
|
|
vlog("version = %s\n", buffer.version);
|
|
vlog("machine = %s\n", buffer.machine);
|
|
}
|
|
#endif
|
|
}
|