blob: 483adac9a266d09fb71ccb4da2468aadde59b5bb [file] [log] [blame]
//
// Copyright (c) 2017 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 "testBase.h"
#include <limits.h>
#include <ctype.h>
#ifndef _WIN32
#include <unistd.h>
#endif
const char *known_extensions[] = {
"cl_khr_byte_addressable_store",
"cl_khr_3d_image_writes",
"cl_khr_fp16",
"cl_khr_fp64",
"cl_khr_global_int32_base_atomics",
"cl_khr_global_int32_extended_atomics",
"cl_khr_local_int32_base_atomics",
"cl_khr_local_int32_extended_atomics",
"cl_khr_int64_base_atomics",
"cl_khr_int64_extended_atomics",
"cl_khr_select_fprounding_mode",
"cl_khr_depth_images",
"cl_khr_gl_depth_images",
"cl_khr_gl_msaa_sharing",
"cl_khr_device_enqueue_local_arg_types",
"cl_khr_subgroups",
"cl_khr_mipmap_image",
"cl_khr_mipmap_image_writes",
"cl_khr_srgb_image_writes",
"cl_khr_subgroup_named_barrier",
"cl_khr_subgroup_extended_types",
"cl_khr_subgroup_non_uniform_vote",
"cl_khr_subgroup_ballot",
"cl_khr_subgroup_non_uniform_arithmetic",
"cl_khr_subgroup_shuffle",
"cl_khr_subgroup_shuffle_relative",
"cl_khr_subgroup_clustered_reduce",
// API-only extensions after this point. If you add above here, modify
// first_API_extension below.
"cl_khr_icd",
"cl_khr_gl_sharing",
"cl_khr_gl_event",
"cl_khr_d3d10_sharing",
"cl_khr_d3d11_sharing",
"cl_khr_dx9_media_sharing",
"cl_khr_egl_event",
"cl_khr_egl_image",
"cl_khr_image2d_from_buffer",
"cl_khr_spir",
"cl_khr_il_program",
"cl_khr_create_command_queue",
"cl_khr_initialize_memory",
"cl_khr_terminate_context",
"cl_khr_priority_hints",
"cl_khr_throttle_hints",
"cl_khr_spirv_no_integer_wrap_decoration",
"cl_khr_extended_versioning",
"cl_khr_device_uuid",
};
size_t num_known_extensions = sizeof(known_extensions)/sizeof(char*);
size_t first_API_extension = 27;
const char *known_embedded_extensions[] = {
"cles_khr_int64",
NULL
};
typedef enum
{
kUnsupported_extension = -1,
kVendor_extension = 0,
kLanguage_extension = 1,
kAPI_extension = 2
}Extension_Type;
const char *kernel_strings[] = {
"kernel void test(global int *defines)\n{\n",
"#pragma OPENCL EXTENSION %s : enable\n",
"#ifdef %s\n"
" defines[%d] = 1;\n"
"#else\n"
" defines[%d] = 0;\n"
"#endif\n",
"#pragma OPENCL EXTENSION %s : disable\n\n",
"}\n"
};
int test_compiler_defines_for_extensions(cl_device_id device, cl_context context, cl_command_queue queue, int n_elems )
{
int error;
int total_errors = 0;
// Get the extensions string for the device
size_t size;
error = clGetDeviceInfo(device, CL_DEVICE_EXTENSIONS, 0, NULL, &size);
test_error(error, "clGetDeviceInfo for CL_DEVICE_EXTENSIONS size failed");
char *extensions = (char*)malloc(sizeof(char)*(size + 1));
if (extensions == 0) {
log_error("Failed to allocate memory for extensions string.\n");
return -1;
}
memset( extensions, CHAR_MIN, sizeof(char)*(size+1) );
error = clGetDeviceInfo(device, CL_DEVICE_EXTENSIONS, sizeof(char)*size, extensions, NULL);
test_error(error, "clGetDeviceInfo for CL_DEVICE_EXTENSIONS failed");
// Check to make sure the extension string is NUL terminated.
if( extensions[size] != CHAR_MIN )
{
test_error( -1, "clGetDeviceInfo for CL_DEVICE_EXTENSIONS wrote past the end of the array!" );
return -1;
}
extensions[size] = '\0'; // set last char to NUL to avoid problems with string functions later
// test for termination with '\0'
size_t stringSize = strlen( extensions );
if( stringSize == size )
{
test_error( -1, "clGetDeviceInfo for CL_DEVICE_EXTENSIONS is not NUL terminated!" );
return -1;
}
// Break up the extensions
log_info("Device reports the following extensions:\n");
char *extensions_supported[1024];
Extension_Type extension_type[1024];
int num_of_supported_extensions = 0;
char *currentP = extensions;
memset( extension_type, 0, sizeof( extension_type) );
// loop over extension string
while (currentP != extensions + stringSize)
{
// skip leading white space
while( *currentP == ' ' )
currentP++;
// Exit if end of string
if( *currentP == '\0' )
{
if( currentP != extensions + stringSize)
{
test_error( -1, "clGetDeviceInfo for CL_DEVICE_EXTENSIONS contains a NUL in the middle of the string!" );
return -1;
}
break;
}
// Not space, not end of string, so extension
char *start = currentP; // start of extension name
// loop looking for the end
while (*currentP != ' ' && currentP != extensions + stringSize)
{
// check for non-space white space in the extension name
if( isspace(*currentP) )
{
test_error( -1, "clGetDeviceInfo for CL_DEVICE_EXTENSIONS contains a non-space whitespace in an extension name!" );
return -1;
}
currentP++;
}
// record the extension name
uintptr_t extension_length = (uintptr_t) currentP - (uintptr_t) start;
extensions_supported[ num_of_supported_extensions ] = (char*) malloc( (extension_length + 1) * sizeof( char ) );
if( NULL == extensions_supported[ num_of_supported_extensions ] )
{
log_error( "Error: unable to allocate memory to hold extension name: %ld chars\n", extension_length );
return -1;
}
memcpy( extensions_supported[ num_of_supported_extensions ], start, extension_length * sizeof( char ) );
extensions_supported[ num_of_supported_extensions ][extension_length] = '\0';
// If the extension is a cl_khr extension, make sure it is an approved cl_khr extension -- looking for misspellings here
if( extensions_supported[ num_of_supported_extensions ][0] == 'c' &&
extensions_supported[ num_of_supported_extensions ][1] == 'l' &&
extensions_supported[ num_of_supported_extensions ][2] == '_' &&
extensions_supported[ num_of_supported_extensions ][3] == 'k' &&
extensions_supported[ num_of_supported_extensions ][4] == 'h' &&
extensions_supported[ num_of_supported_extensions ][5] == 'r' &&
extensions_supported[ num_of_supported_extensions ][6] == '_' )
{
size_t ii;
for( ii = 0; ii < num_known_extensions; ii++ )
{
if( 0 == strcmp( known_extensions[ii], extensions_supported[ num_of_supported_extensions ] ) )
break;
}
if( ii == num_known_extensions )
{
log_error( "FAIL: Extension %s is not in the list of approved Khronos extensions!", extensions_supported[ num_of_supported_extensions ] );
return -1;
}
}
// Is it an embedded extension?
else if( memcmp( extensions_supported[ num_of_supported_extensions ], "cles_khr_", 9 ) == 0 )
{
// Yes, but is it a known one?
size_t ii;
for( ii = 0; known_embedded_extensions[ ii ] != NULL; ii++ )
{
if( strcmp( known_embedded_extensions[ ii ], extensions_supported[ num_of_supported_extensions ] ) == 0 )
break;
}
if( known_embedded_extensions[ ii ] == NULL )
{
log_error( "FAIL: Extension %s is not in the list of approved Khronos embedded extensions!", extensions_supported[ num_of_supported_extensions ] );
return -1;
}
// It's approved, but are we even an embedded system?
char profileStr[128] = "";
error = clGetDeviceInfo( device, CL_DEVICE_PROFILE, sizeof( profileStr ), &profileStr, NULL );
test_error( error, "Unable to get CL_DEVICE_PROFILE to validate embedded extension name" );
if( strcmp( profileStr, "EMBEDDED_PROFILE" ) != 0 )
{
log_error( "FAIL: Extension %s is an approved embedded extension, but on a non-embedded profile!", extensions_supported[ num_of_supported_extensions ] );
return -1;
}
}
else
{ // All other extensions must be of the form cl_<vendor_name>_<name>
if( extensions_supported[ num_of_supported_extensions ][0] != 'c' ||
extensions_supported[ num_of_supported_extensions ][1] != 'l' ||
extensions_supported[ num_of_supported_extensions ][2] != '_' )
{
log_error( "FAIL: Extension %s doesn't start with \"cl_\"!", extensions_supported[ num_of_supported_extensions ] );
return -1;
}
if( extensions_supported[ num_of_supported_extensions ][3] == '_' || extensions_supported[ num_of_supported_extensions ][3] == '\0' )
{
log_error( "FAIL: Vendor name is missing in extension %s!", extensions_supported[ num_of_supported_extensions ] );
return -1;
}
// look for the second underscore for name
char *p = extensions_supported[ num_of_supported_extensions ] + 4;
while( *p != '\0' && *p != '_' )
p++;
if( *p != '_' || p[1] == '\0')
{
log_error( "FAIL: extension name is missing in extension %s!", extensions_supported[ num_of_supported_extensions ] );
return -1;
}
}
num_of_supported_extensions++;
}
// Build a list of the known extensions that are not supported by the device
char *extensions_not_supported[1024];
int num_not_supported_extensions = 0;
for( int i = 0; i < num_of_supported_extensions; i++ )
{
int is_supported = 0;
for( size_t j = 0; j < num_known_extensions; j++ )
{
if( strcmp( extensions_supported[ i ], known_extensions[ j ] ) == 0 )
{
extension_type[ i ] = ( j < first_API_extension ) ? kLanguage_extension : kAPI_extension;
is_supported = 1;
break;
}
}
if( !is_supported )
{
for( int j = 0; known_embedded_extensions[ j ] != NULL; j++ )
{
if( strcmp( extensions_supported[ i ], known_embedded_extensions[ j ] ) == 0 )
{
extension_type[ i ] = kLanguage_extension;
is_supported = 1;
break;
}
}
}
if (!is_supported) {
extensions_not_supported[num_not_supported_extensions] = (char*)malloc(strlen(extensions_supported[i])+1);
strcpy(extensions_not_supported[num_not_supported_extensions], extensions_supported[i]);
num_not_supported_extensions++;
}
}
for (int i=0; i<num_of_supported_extensions; i++) {
log_info("%40s -- Supported\n", extensions_supported[i]);
}
for (int i=0; i<num_not_supported_extensions; i++) {
log_info("%40s -- Not Supported\n", extensions_not_supported[i]);
}
// Build the kernel
char *kernel_code = (char*)malloc(1025*256*(num_not_supported_extensions+num_of_supported_extensions));
memset(kernel_code, 0, 1025*256*(num_not_supported_extensions+num_of_supported_extensions));
int i, index = 0;
strcat(kernel_code, kernel_strings[0]);
for (i=0; i<num_of_supported_extensions; i++, index++) {
if (extension_type[i] == kLanguage_extension)
sprintf(kernel_code + strlen(kernel_code), kernel_strings[1], extensions_supported[i]);
sprintf(kernel_code + strlen(kernel_code), kernel_strings[2], extensions_supported[i], index, index );
if (extension_type[i] == kLanguage_extension)
sprintf(kernel_code + strlen(kernel_code), kernel_strings[3], extensions_supported[i] );
}
for ( i = 0; i<num_not_supported_extensions; i++, index++) {
sprintf(kernel_code + strlen(kernel_code), kernel_strings[2], extensions_not_supported[i], index, index );
}
strcat(kernel_code, kernel_strings[4]);
// Now we need to execute the kernel
clMemWrapper defines;
cl_int *data;
clProgramWrapper program;
clKernelWrapper kernel;
Version version = get_device_cl_version(device);
error = create_single_kernel_helper(context, &program, &kernel, 1,
(const char **)&kernel_code, "test");
test_error(error, "create_single_kernel_helper failed");
data = (cl_int*)malloc(sizeof(cl_int)*(num_not_supported_extensions+num_of_supported_extensions));
memset(data, 0, sizeof(cl_int)*(num_not_supported_extensions+num_of_supported_extensions));
defines = clCreateBuffer(context, CL_MEM_COPY_HOST_PTR,
sizeof(cl_int)*(num_not_supported_extensions+num_of_supported_extensions), data, &error);
test_error(error, "clCreateBuffer failed");
error = clSetKernelArg(kernel, 0, sizeof(defines), &defines);
test_error(error, "clSetKernelArg failed");
size_t global_size = 1;
error = clEnqueueNDRangeKernel(queue, kernel, 1, NULL, &global_size, NULL, 0, NULL, NULL);
test_error(error, "clEnqueueNDRangeKernel failed");
error = clEnqueueReadBuffer(queue, defines, CL_TRUE, 0, sizeof(cl_int)*(num_not_supported_extensions+num_of_supported_extensions),
data, 0, NULL, NULL);
test_error(error, "clEnqueueReadBuffer failed");
// Report what the compiler reported
log_info("\nCompiler reported the following extensions defined in the OpenCL C kernel environment:\n");
index = 0;
int total_supported = 0;
for (int i=0; i<num_of_supported_extensions; i++, index++) {
if (data[index] == 1) {
log_info("\t%s\n", extensions_supported[i]);
total_supported++;
}
}
for (int i=0; i<num_not_supported_extensions; i++, index++) {
if (data[index] == 1) {
log_info("\t%s\n", extensions_not_supported[i]);
total_supported++;
}
}
if (total_supported == 0)
log_info("\t(none)\n");
// Count the errors
index = 0;
int unknown = 0;
for ( i=0; i<num_of_supported_extensions; i++)
{
if (data[i] != 1)
{
switch( extension_type[i] )
{
case kLanguage_extension:
log_error("ERROR: Supported extension %s not defined in kernel.\n", extensions_supported[i]);
total_errors++;
break;
case kVendor_extension:
unknown++;
break;
case kAPI_extension:
break;
default:
log_error( "ERROR: internal test error in extension detection. This is probably a bug in the test.\n" );
break;
}
}
}
if(unknown)
{
log_info( "\nThe following non-KHR extensions are supported but do not add a preprocessor symbol to OpenCL C.\n" );
for (int z=0; z<num_of_supported_extensions; z++)
{
if (data[z] != 1 && extension_type[z] == kVendor_extension )
log_info( "\t%s\n", extensions_supported[z]);
}
}
for ( ; i<num_not_supported_extensions; i++) {
if (data[i] != 0) {
log_error("ERROR: Unsupported extension %s is defined in kernel.\n", extensions_not_supported[i]);
total_errors++;
}
}
log_info("\n");
// cleanup
free(data);
free(kernel_code);
for(i=0; i<num_of_supported_extensions; i++) {
free(extensions_supported[i]);
}
free(extensions);
if (total_errors)
return -1;
return 0;
}