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.
937 lines
37 KiB
937 lines
37 KiB
//
|
|
// 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 "harness/conversions.h"
|
|
#include "harness/typeWrappers.h"
|
|
#include "harness/testHarness.h"
|
|
|
|
// #define USE_NEW_SYNTAX 1
|
|
// The number of shuffles to test per test
|
|
#define NUM_TESTS 32
|
|
// The number of times to run each combination of shuffles
|
|
#define NUM_ITERATIONS_PER_TEST 2
|
|
#define MAX_PROGRAM_SIZE NUM_TESTS*1024
|
|
#define PRINT_SHUFFLE_KERNEL_SOURCE 0
|
|
#define SPEW_ORDER_DETAILS 0
|
|
|
|
enum ShuffleMode
|
|
{
|
|
kNormalMode = 0,
|
|
kFunctionCallMode,
|
|
kArrayAccessMode,
|
|
kBuiltInFnMode,
|
|
kBuiltInDualInputFnMode
|
|
};
|
|
|
|
static const char *shuffleKernelPattern[3] = {
|
|
"__kernel void sample_test( __global %s%s *source, __global %s%s *dest )\n"
|
|
"{\n"
|
|
" if (get_global_id(0) != 0) return;\n"
|
|
" //%s%s src1 %s, src2%s;\n",// Here's a comma...
|
|
// Above code is commented out for now, but keeping around for testing local storage options
|
|
"}\n" };
|
|
|
|
static const char *shuffleTempPattern = " %s%s tmp;\n";
|
|
|
|
static const char *clearTempPattern = " tmp = (%s%s)((%s)0);\n";
|
|
|
|
static const char *shuffleSinglePattern =
|
|
" tmp%s%s = source[%d]%s%s;\n"
|
|
" dest[%d] = tmp;\n"
|
|
;
|
|
|
|
static const char * shuffleSinglePatternV3src =
|
|
" tmp%s%s = vload3(%d, source)%s%s;\n"
|
|
" dest[%d] = tmp;\n";
|
|
|
|
static const char * shuffleSinglePatternV3dst =
|
|
" tmp%s%s = source[%d]%s%s;\n"
|
|
" vstore3(tmp, %d, dest);\n";
|
|
|
|
|
|
static const char * shuffleSinglePatternV3srcV3dst =
|
|
"tmp%s%s = vload3(%d, source)%s%s;\n"
|
|
"vstore3(tmp, %d, dest);\n";
|
|
|
|
static const char *shuffleFnLinePattern = "%s%s shuffle_fn( %s%s source );\n%s%s shuffle_fn( %s%s source ) { return source; }\n\n";
|
|
|
|
static const char *shuffleFnPattern =
|
|
" tmp%s%s = shuffle_fn( source[%d] )%s%s;\n"
|
|
" dest[%d] = tmp;\n"
|
|
;
|
|
|
|
|
|
static const char *shuffleFnPatternV3src =
|
|
" tmp%s%s = shuffle_fn( vload3(%d, source) )%s%s;\n"
|
|
" dest[%d] = tmp;\n"
|
|
;
|
|
|
|
|
|
static const char *shuffleFnPatternV3dst =
|
|
" tmp%s%s = shuffle_fn( source[%d] )%s%s;\n"
|
|
" vstore3(tmp, %d, dest);\n"
|
|
;
|
|
|
|
|
|
static const char *shuffleFnPatternV3srcV3dst =
|
|
" tmp%s%s = shuffle_fn(vload3(%d, source) )%s%s;\n"
|
|
" vstore3(tmp, %d, dest);\n"
|
|
;
|
|
|
|
// shuffle() built-in function patterns
|
|
static const char *shuffleBuiltInPattern =
|
|
" {\n"
|
|
" %s%s src1 = %s;\n"
|
|
" %s%s%s mask = (%s%s%s)( %s );\n"
|
|
" tmp = shuffle( src1, mask );\n"
|
|
" %s;\n"
|
|
" }\n"
|
|
;
|
|
|
|
// shuffle() built-in dual-input function patterns
|
|
static const char *shuffleBuiltInDualPattern =
|
|
" {\n"
|
|
" %s%s src1 = %s;\n"
|
|
" %s%s src2 = %s;\n"
|
|
" %s%s%s mask = (%s%s%s)( %s );\n"
|
|
" tmp = shuffle2( src1, src2, mask );\n"
|
|
" %s;\n"
|
|
" }\n"
|
|
;
|
|
|
|
|
|
typedef unsigned char ShuffleOrder[ 16 ];
|
|
|
|
void incrementShuffleOrder( ShuffleOrder &order, size_t orderSize, size_t orderRange )
|
|
{
|
|
for( size_t i = 0; i < orderSize; i++ )
|
|
{
|
|
order[ i ]++;
|
|
if( order[ i ] < orderRange )
|
|
return;
|
|
order[ i ] = 0;
|
|
}
|
|
}
|
|
|
|
bool shuffleOrderContainsDuplicates( ShuffleOrder &order, size_t orderSize )
|
|
{
|
|
bool flags[ 16 ] = { false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false };
|
|
for( size_t i = 0; i < orderSize; i++ )
|
|
{
|
|
if( flags[ order[ i ] ] )
|
|
return true;
|
|
flags[ order[ i ] ] = true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static void shuffleVector( unsigned char *inVector, unsigned char *outVector, ShuffleOrder order, size_t vecSize, size_t typeSize, cl_uint lengthToUse )
|
|
{
|
|
for(size_t i = 0; i < lengthToUse; i++ )
|
|
{
|
|
unsigned char *inPtr = inVector + typeSize *order[ i ];
|
|
memcpy( outVector, inPtr, typeSize );
|
|
outVector += typeSize;
|
|
}
|
|
}
|
|
|
|
static void shuffleVector2( unsigned char *inVector, unsigned char *outVector, ShuffleOrder order, size_t vecSize, size_t typeSize, cl_uint lengthToUse )
|
|
{
|
|
for(size_t i = 0; i < lengthToUse; i++ )
|
|
{
|
|
unsigned char *outPtr = outVector + typeSize *order[ i ];
|
|
memcpy( outPtr, inVector, typeSize );
|
|
inVector += typeSize;
|
|
}
|
|
}
|
|
|
|
static void shuffleVectorDual( unsigned char *inVector, unsigned char *inSecondVector, unsigned char *outVector, ShuffleOrder order, size_t vecSize, size_t typeSize, cl_uint lengthToUse )
|
|
{
|
|
// This is tricky: the indices of each shuffle are in a range (0-srcVecSize * 2-1),
|
|
// where (srcVecSize-srcVecSize*2-1) refers to the second input.
|
|
size_t uphalfMask = (size_t)vecSize;
|
|
size_t lowerBits = (size_t)( vecSize - 1 );
|
|
|
|
for(size_t i = 0; i < lengthToUse; i++ )
|
|
{
|
|
unsigned char *inPtr;
|
|
#if SPEW_ORDER_DETAILS
|
|
log_info("order[%d] is %d, or %d of %s\n", (int)i,
|
|
(int)(order[i]),
|
|
(int)(order[i] & lowerBits),
|
|
((order[i]&uphalfMask) == 0)?"lower num":"upper num");
|
|
#endif
|
|
if( order[ i ] & uphalfMask )
|
|
inPtr = inSecondVector + typeSize * ( order[ i ] & lowerBits );
|
|
else
|
|
inPtr = inVector + typeSize * ( order[ i ] & lowerBits );
|
|
memcpy( outVector, inPtr, typeSize );
|
|
outVector += typeSize;
|
|
}
|
|
}
|
|
|
|
|
|
static ShuffleOrder sNaturalOrder = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
|
|
|
|
static int useNumbersFlip = 0;
|
|
const char *get_order_string( ShuffleOrder &order, size_t vecSize, cl_uint lengthToUse, bool byNumber, MTdata d )
|
|
{
|
|
// NOTE: names are only valid for hex characters (up to F) but for debugging, we use
|
|
// this to print out orders for dual inputs, which actually can be valid up to position 31 (two 16-element vectors)
|
|
// so we go ahead and fake the rest of the alphabet for those other 16 positions, so we have
|
|
// some (indirectly) meaningful output
|
|
char names[] = "0123456789abcdefghijklmnopqrstuv";
|
|
char namesUpperCase[] = "0123456789ABCDEFGHIJKLMNOPQRSTUV";
|
|
char names2[] = "xyzw!!!!!!!!!!!!";
|
|
|
|
static char orderString[ 18 ];
|
|
|
|
size_t j, idx;
|
|
|
|
// Assume we don't have to use numbers
|
|
byNumber = 0;
|
|
// Check to see
|
|
for( j = 0; j < lengthToUse; j++ )
|
|
{
|
|
if (order[j] > 3) {
|
|
// An index is > xyzw so we need to use numbers
|
|
byNumber = 1;
|
|
break;
|
|
}
|
|
}
|
|
// If we can use numbers, do so half the time.
|
|
if (!byNumber) {
|
|
byNumber = (useNumbersFlip++)%2;
|
|
}
|
|
// Do not use xyzw for vectors whose length is not 2 or 4 per the spec.
|
|
if (vecSize != 2 || vecSize != 4 || vecSize != 3)
|
|
byNumber = 1;
|
|
|
|
if( byNumber || vecSize > 4 )
|
|
{
|
|
idx = 0;
|
|
// Randomly chose upper and lower case S
|
|
orderString[ idx++ ] = random_in_range(0, 1, d) ? 's' : 'S';
|
|
for( j = 0; j < vecSize && j < lengthToUse; j++ ) {
|
|
// Randomly choose upper and lower case.
|
|
orderString[ idx++ ] = random_in_range(0, 1, d) ? names[ (int)order[ j ] ] : namesUpperCase[ (int)order[ j ] ];
|
|
}
|
|
orderString[ idx++ ] = 0;
|
|
}
|
|
else
|
|
{
|
|
for( j = 0; j < vecSize && j < lengthToUse; j++ ) {
|
|
// Randomly choose upper and lower case.
|
|
orderString[ j ] = names2[ (int)order[ j ] ];
|
|
}
|
|
orderString[ j ] = 0;
|
|
}
|
|
|
|
return orderString;
|
|
}
|
|
|
|
char * get_order_name( ExplicitType vecType, size_t inVecSize, size_t outVecSize, ShuffleOrder &inOrder, ShuffleOrder &outOrder, cl_uint lengthToUse, MTdata d, bool inUseNumerics, bool outUseNumerics )
|
|
{
|
|
static char orderName[ 512 ] = "";
|
|
char inOrderStr[ 512 ], outOrderStr[ 512 ];
|
|
|
|
if( inVecSize == 1 )
|
|
inOrderStr[ 0 ] = 0;
|
|
else
|
|
sprintf( inOrderStr, "%d.%s", (int)inVecSize, get_order_string( inOrder, outVecSize, lengthToUse, inUseNumerics, d ) );
|
|
if( outVecSize == 1 )
|
|
outOrderStr[ 0 ] = 0;
|
|
else
|
|
sprintf( outOrderStr, "%d.%s", (int)outVecSize, get_order_string( outOrder, outVecSize, lengthToUse, outUseNumerics, d ) );
|
|
|
|
sprintf( orderName, "order %s%s -> %s%s",
|
|
get_explicit_type_name( vecType ), inOrderStr, get_explicit_type_name( vecType ), outOrderStr );
|
|
return orderName;
|
|
}
|
|
|
|
void print_hex_mem_dump( const unsigned char *inDataPtr, const unsigned char * inDataPtr2, const unsigned char *expected, const unsigned char *outDataPtr, size_t inVecSize, size_t outVecSize, size_t typeSize )
|
|
{
|
|
char error [4096] = "";
|
|
strcat(error, " Source: ");
|
|
for( unsigned int j = 0; j < inVecSize * typeSize; j++ )
|
|
{
|
|
sprintf(error, "%s%s%02x ",error, ( j % typeSize ) ? "" : " ", (cl_uchar)inDataPtr[ j ] );
|
|
}
|
|
if( inDataPtr2 != NULL )
|
|
{
|
|
strcat(error, "\n Source 2: ");
|
|
for( unsigned int j = 0; j < inVecSize * typeSize; j++ )
|
|
{
|
|
sprintf(error, "%s%s%02x ",error, ( j % typeSize ) ? "" : " ", (cl_uchar)inDataPtr2[ j ] );
|
|
}
|
|
}
|
|
strcat(error, "\n Expected: " );
|
|
for( unsigned int j = 0; j < outVecSize * typeSize; j++ )
|
|
{
|
|
sprintf(error, "%s%s%02x ",error, ( j % typeSize ) ? "" : " ", (cl_uchar)expected[ j ] );
|
|
}
|
|
strcat(error, "\n Actual: " );
|
|
for( unsigned int j = 0; j < outVecSize * typeSize; j++ )
|
|
{
|
|
sprintf(error, "%s%s%02x ",error, ( j % typeSize ) ? "" : " ", (cl_uchar)outDataPtr[ j ] );
|
|
}
|
|
log_info("%s\n", error);
|
|
}
|
|
|
|
void generate_shuffle_mask( char *outMaskString, size_t maskSize, const ShuffleOrder *order )
|
|
{
|
|
outMaskString[ 0 ] = 0;
|
|
if( order != NULL )
|
|
{
|
|
for( size_t jj = 0; jj < maskSize; jj++ )
|
|
{
|
|
char thisMask[ 16 ];
|
|
sprintf( thisMask, "%s%d", ( jj == 0 ) ? "" : ", ", (*order)[ jj ] );
|
|
strcat( outMaskString, thisMask );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for( size_t jj = 0; jj < maskSize; jj++ )
|
|
{
|
|
char thisMask[ 16 ];
|
|
sprintf( thisMask, "%s%ld", ( jj == 0 ) ? "" : ", ", jj );
|
|
strcat( outMaskString, thisMask );
|
|
}
|
|
}
|
|
}
|
|
|
|
static int create_shuffle_kernel( cl_context context, cl_program *outProgram, cl_kernel *outKernel,
|
|
size_t *outRealVecSize,
|
|
ExplicitType vecType, size_t inVecSize, size_t outVecSize, cl_uint *lengthToUse, bool inUseNumerics, bool outUseNumerics,
|
|
size_t numOrders, ShuffleOrder *inOrders, ShuffleOrder *outOrders,
|
|
MTdata d, ShuffleMode shuffleMode = kNormalMode )
|
|
{
|
|
char inOrder[18], shuffledOrder[18];
|
|
size_t typeSize;
|
|
char kernelSource[MAX_PROGRAM_SIZE], progLine[ 10240 ];
|
|
char *programPtr;
|
|
char inSizeName[4], outSizeName[4], outRealSizeName[4], inSizeArgName[4];
|
|
char outSizeNameTmpVar[4];
|
|
|
|
|
|
/* Create the source; note vec size is the vector length we are testing */
|
|
if( inVecSize == 1 ) //|| (inVecSize == 3)) // just have arrays if we go with size 3
|
|
inSizeName[ 0 ] = 0;
|
|
else
|
|
sprintf( inSizeName, "%ld", inVecSize );
|
|
if( inVecSize == 3 )
|
|
inSizeArgName[ 0 ] = 0;
|
|
else
|
|
strcpy( inSizeArgName, inSizeName );
|
|
|
|
|
|
typeSize = get_explicit_type_size( vecType );
|
|
|
|
*outRealVecSize = outVecSize;
|
|
|
|
if( outVecSize == 1 || (outVecSize == 3))
|
|
outSizeName[ 0 ] = 0;
|
|
else
|
|
sprintf( outSizeName, "%d", (int)outVecSize );
|
|
|
|
if(outVecSize == 1) {
|
|
outSizeNameTmpVar[0] = 0;
|
|
} else {
|
|
sprintf(outSizeNameTmpVar, "%d", (int)outVecSize);
|
|
}
|
|
|
|
if( *outRealVecSize == 1 || ( *outRealVecSize == 3))
|
|
outRealSizeName[ 0 ] = 0;
|
|
else
|
|
sprintf( outRealSizeName, "%d", (int)*outRealVecSize );
|
|
|
|
|
|
// Loop through and create the source for all order strings
|
|
kernelSource[ 0 ] = 0;
|
|
if (vecType == kDouble) {
|
|
strcat(kernelSource, "#pragma OPENCL EXTENSION cl_khr_fp64 : enable\n");
|
|
}
|
|
|
|
if( shuffleMode == kFunctionCallMode )
|
|
{
|
|
sprintf( progLine, shuffleFnLinePattern, get_explicit_type_name( vecType ), inSizeName, get_explicit_type_name( vecType ), inSizeName,
|
|
get_explicit_type_name( vecType ), inSizeName, get_explicit_type_name( vecType ), inSizeName );
|
|
strcat(kernelSource, progLine);
|
|
}
|
|
|
|
// We're going to play a REALLY NASTY trick here. We're going to use the inSize insert point
|
|
// to put in an entire third parameter if we need it
|
|
char inParamSizeString[ 1024 ];
|
|
if( shuffleMode == kBuiltInDualInputFnMode )
|
|
sprintf( inParamSizeString, "%s *secondSource, __global %s%s", inSizeArgName, get_explicit_type_name( vecType ), inSizeArgName );
|
|
else
|
|
strcpy( inParamSizeString, inSizeArgName );
|
|
|
|
// These two take care of unused variable warnings
|
|
const char * src2EnableA = ( shuffleMode == kBuiltInDualInputFnMode ) ? "" : "/*";
|
|
const char * src2EnableB = ( shuffleMode == kBuiltInDualInputFnMode ) ? "" : "*/";
|
|
|
|
sprintf( progLine, shuffleKernelPattern[ 0 ], get_explicit_type_name( vecType ), inParamSizeString,
|
|
get_explicit_type_name( vecType ), outRealSizeName, get_explicit_type_name( vecType ), inSizeName,
|
|
src2EnableA, src2EnableB );
|
|
strcat(kernelSource, progLine);
|
|
if( inOrders == NULL )
|
|
strcpy( inOrder, get_order_string( sNaturalOrder, outVecSize, (cl_uint)outVecSize, inUseNumerics, d ) );
|
|
|
|
sprintf( progLine, shuffleTempPattern, get_explicit_type_name( vecType ), outSizeNameTmpVar);
|
|
strcat(kernelSource, progLine);
|
|
|
|
for( unsigned int i = 0; i < numOrders; i++ )
|
|
{
|
|
if( inOrders != NULL )
|
|
strcpy( inOrder, get_order_string( inOrders[ i ], outVecSize, lengthToUse[i], inUseNumerics, d ) );
|
|
strcpy( shuffledOrder, get_order_string( outOrders[ i ], outVecSize, lengthToUse[i], outUseNumerics, d ) );
|
|
|
|
|
|
sprintf( progLine, clearTempPattern, get_explicit_type_name( vecType ), outSizeName,get_explicit_type_name( vecType ));
|
|
strcat(kernelSource, progLine);
|
|
|
|
|
|
if( shuffleMode == kNormalMode )
|
|
{
|
|
if(outVecSize == 3 && inVecSize == 3) {
|
|
// shuffleSinglePatternV3srcV3dst
|
|
sprintf( progLine, shuffleSinglePatternV3srcV3dst,
|
|
outVecSize > 1 ? "." : "", outVecSize > 1 ? shuffledOrder : "", (int)i,
|
|
inVecSize > 1 ? "." : "", inVecSize > 1 ? inOrder : "", (int)i );
|
|
} else if(inVecSize == 3) {
|
|
// shuffleSinglePatternV3src
|
|
sprintf( progLine, shuffleSinglePatternV3src,
|
|
outVecSize > 1 ? "." : "", outVecSize > 1 ? shuffledOrder : "", (int)i,
|
|
inVecSize > 1 ? "." : "", inVecSize > 1 ? inOrder : "", (int)i );
|
|
} else if(outVecSize == 3) {
|
|
// shuffleSinglePatternV3dst
|
|
sprintf( progLine, shuffleSinglePatternV3dst,
|
|
outVecSize > 1 ? "." : "", outVecSize > 1 ? shuffledOrder : "", (int)i,
|
|
inVecSize > 1 ? "." : "", inVecSize > 1 ? inOrder : "",
|
|
(int)i );
|
|
} else {
|
|
sprintf( progLine, shuffleSinglePattern,
|
|
outVecSize > 1 ? "." : "", outVecSize > 1 ? shuffledOrder : "", (int)i,
|
|
inVecSize > 1 ? "." : "", inVecSize > 1 ? inOrder : "", (int)i );
|
|
}
|
|
}
|
|
else if( shuffleMode == kFunctionCallMode )
|
|
{
|
|
// log_info("About to make a shuffle line\n");
|
|
// fflush(stdout);
|
|
if(inVecSize == 3 && outVecSize == 3) { // swap last two
|
|
sprintf( progLine, shuffleFnPatternV3srcV3dst,
|
|
outVecSize > 1 ? "." : "", outVecSize > 1 ? shuffledOrder : "", (int)i,
|
|
inVecSize > 1 ? "." : "", inVecSize > 1 ? inOrder : "",
|
|
(int)i );
|
|
} else if(outVecSize == 3) { // swap last two
|
|
// log_info("Here\n\n");
|
|
// fflush(stdout);
|
|
sprintf( progLine, shuffleFnPatternV3dst,
|
|
outVecSize > 1 ? "." : "",
|
|
outVecSize > 1 ? shuffledOrder : "",
|
|
(int)i,
|
|
inVecSize > 1 ? "." : "",
|
|
inVecSize > 1 ? inOrder : "",
|
|
(int)i );
|
|
// log_info("\n%s\n", progLine);
|
|
// fflush(stdout);
|
|
} else if(inVecSize == 3) {
|
|
sprintf( progLine, shuffleFnPatternV3src,
|
|
outVecSize > 1 ? "." : "", outVecSize > 1 ? shuffledOrder : "", (int)i,
|
|
inVecSize > 1 ? "." : "", inVecSize > 1 ? inOrder : "", (int)i );
|
|
} else {
|
|
sprintf( progLine, shuffleFnPattern,
|
|
outVecSize > 1 ? "." : "", outVecSize > 1 ? shuffledOrder : "", (int)i,
|
|
inVecSize > 1 ? "." : "", inVecSize > 1 ? inOrder : "", (int)i );
|
|
}
|
|
}
|
|
else if( shuffleMode == kArrayAccessMode )
|
|
{ // now we want to replace inSizeName with inSizeNameShuffleFn
|
|
int vectorSizeToCastTo = 16;
|
|
cl_uint item;
|
|
for (item =0; item<lengthToUse[i]; item++) {
|
|
int absoluteIndex = i*(int)inVecSize+(int)inOrders[i][item];
|
|
int castVectorIndex = absoluteIndex/vectorSizeToCastTo;
|
|
size_t castElementIndex = absoluteIndex % vectorSizeToCastTo;
|
|
ShuffleOrder myOutOrders, myInOrders;
|
|
myOutOrders[0] = outOrders[i][item];
|
|
myInOrders[0] = castElementIndex;
|
|
|
|
strcpy( inOrder, get_order_string( myInOrders, 1, 1, 0, d ) );
|
|
strcpy( shuffledOrder, get_order_string( myOutOrders, 1, 1, 0, d ) );
|
|
|
|
sprintf(progLine, " tmp%s%s = ((__global %s%d *)source)[%d]%s%s;\n",
|
|
outVecSize > 1 ? "." : "", outVecSize > 1 ? shuffledOrder : "",
|
|
get_explicit_type_name( vecType ), vectorSizeToCastTo,
|
|
castVectorIndex,
|
|
vectorSizeToCastTo > 1 ? "." : "", vectorSizeToCastTo > 1 ? inOrder : "");
|
|
strcat(kernelSource, progLine);
|
|
}
|
|
if(outVecSize == 3) {
|
|
sprintf(progLine," vstore3(tmp, %d, (__global %s *)dest);\n",
|
|
i, get_explicit_type_name( vecType ));
|
|
// probably don't need that last
|
|
// cast to (__global %s *) where %s is get_explicit_type_name( vecType)
|
|
} else {
|
|
sprintf(progLine," dest[%d] = tmp;\n", i );
|
|
}
|
|
}
|
|
else // shuffleMode == kBuiltInFnMode or kBuiltInDualInputFnMode
|
|
{
|
|
if(inVecSize == 3 || outVecSize == 3 ||
|
|
inVecSize == 1 || outVecSize == 1) {
|
|
// log_info("Skipping test for size 3\n");
|
|
continue;
|
|
}
|
|
ExplicitType maskType = vecType;
|
|
if( maskType == kFloat )
|
|
maskType = kUInt;
|
|
if( maskType == kDouble) {
|
|
maskType = kULong;
|
|
}
|
|
|
|
char maskString[ 1024 ] = "";
|
|
size_t maskSize = outVecSize;// ( shuffleMode == kBuiltInDualInputFnMode ) ? ( outVecSize << 1 ) : outVecSize;
|
|
generate_shuffle_mask( maskString, maskSize, ( outOrders != NULL ) ? &outOrders[ i ] : NULL );
|
|
|
|
// Set up a quick prefix, so mask gets unsigned type regardless of the input/output type
|
|
char maskPrefix[ 2 ] = "u";
|
|
if( get_explicit_type_name( maskType )[ 0 ] == 'u' )
|
|
maskPrefix[ 0 ] = 0;
|
|
|
|
char progLine2[ 10240 ];
|
|
if( shuffleMode == kBuiltInDualInputFnMode )
|
|
{
|
|
sprintf( progLine2, shuffleBuiltInDualPattern, get_explicit_type_name( vecType ), inSizeName,
|
|
( inVecSize == 3 ) ? "vload3( %ld, (__global %s *)source )" : "source[ %ld ]",
|
|
get_explicit_type_name( vecType ), inSizeName,
|
|
( inVecSize == 3 ) ? "vload3( %ld, (__global %s *)secondSource )" : "secondSource[ %ld ]",
|
|
maskPrefix, get_explicit_type_name( maskType ), outSizeName, maskPrefix, get_explicit_type_name( maskType ), outSizeName,
|
|
maskString,
|
|
( outVecSize == 3 ) ? "vstore3( tmp, %ld, (__global %s *)dest )" : "dest[ %ld ] = tmp" );
|
|
|
|
if( outVecSize == 3 )
|
|
{
|
|
if( inVecSize == 3 )
|
|
sprintf( progLine, progLine2, i, get_explicit_type_name( vecType ), i, get_explicit_type_name( vecType ), i, get_explicit_type_name( vecType ) );
|
|
else
|
|
sprintf( progLine, progLine2, i, i, i, get_explicit_type_name( vecType ) );
|
|
}
|
|
else
|
|
{
|
|
if( inVecSize == 3 )
|
|
sprintf( progLine, progLine2, i, get_explicit_type_name( vecType ), i, get_explicit_type_name( vecType ), i );
|
|
else
|
|
sprintf( progLine, progLine2, i, i, i );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sprintf( progLine2, shuffleBuiltInPattern, get_explicit_type_name( vecType ), inSizeName,
|
|
( inVecSize == 3 ) ? "vload3( %ld, (__global %s *)source )" : "source[ %ld ]",
|
|
maskPrefix, get_explicit_type_name( maskType ), outSizeName, maskPrefix, get_explicit_type_name( maskType ), outSizeName,
|
|
maskString,
|
|
( outVecSize == 3 ) ? "vstore3( tmp, %ld, (__global %s *)dest )" : "dest[ %ld ] = tmp" );
|
|
|
|
if( outVecSize == 3 )
|
|
{
|
|
if( inVecSize == 3 )
|
|
sprintf( progLine, progLine2, i, get_explicit_type_name( vecType ), i, get_explicit_type_name( vecType ) );
|
|
else
|
|
sprintf( progLine, progLine2, i, i, get_explicit_type_name( vecType ) );
|
|
}
|
|
else
|
|
{
|
|
if( inVecSize == 3 )
|
|
sprintf( progLine, progLine2, i, get_explicit_type_name( vecType ), i );
|
|
else
|
|
sprintf( progLine, progLine2, i, i );
|
|
}
|
|
}
|
|
}
|
|
|
|
strcat( kernelSource, progLine );
|
|
if (strlen(kernelSource) > 0.9*MAX_PROGRAM_SIZE)
|
|
log_info("WARNING: Program has grown to 90%% (%d) of the defined max program size of %d\n", (int)strlen(kernelSource), (int)MAX_PROGRAM_SIZE);
|
|
}
|
|
strcat( kernelSource, shuffleKernelPattern[ 1 ] );
|
|
|
|
// Print the kernel source
|
|
if (PRINT_SHUFFLE_KERNEL_SOURCE)
|
|
log_info( "Kernel:%s\n", kernelSource );
|
|
|
|
/* Create kernel */
|
|
programPtr = kernelSource;
|
|
if( create_single_kernel_helper( context, outProgram, outKernel, 1, (const char **)&programPtr, "sample_test" ) )
|
|
{
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int test_shuffle_dual_kernel(cl_context context, cl_command_queue queue,
|
|
ExplicitType vecType, size_t inVecSize, size_t outVecSize, cl_uint *lengthToUse, size_t numOrders,
|
|
ShuffleOrder *inOrderIdx, ShuffleOrder *outOrderIdx, bool inUseNumerics, bool outUseNumerics, MTdata d,
|
|
ShuffleMode shuffleMode = kNormalMode )
|
|
{
|
|
clProgramWrapper program;
|
|
clKernelWrapper kernel;
|
|
int error;
|
|
size_t threads[1], localThreads[1];
|
|
size_t typeSize, outRealVecSize;
|
|
clMemWrapper streams[ 3 ];
|
|
|
|
/* Create the source */
|
|
error = create_shuffle_kernel( context, &program, &kernel, &outRealVecSize, vecType,
|
|
inVecSize, outVecSize, lengthToUse, inUseNumerics, outUseNumerics, numOrders, inOrderIdx, outOrderIdx,
|
|
d, shuffleMode );
|
|
if( error != 0 )
|
|
return error;
|
|
|
|
typeSize = get_explicit_type_size( vecType );
|
|
|
|
#if !(defined(_WIN32) && defined (_MSC_VER))
|
|
cl_long inData[ inVecSize * numOrders ];
|
|
cl_long inSecondData[ inVecSize * numOrders ];
|
|
cl_long outData[ outRealVecSize * numOrders ];
|
|
#else
|
|
cl_long* inData = (cl_long*)_malloca(inVecSize * numOrders * sizeof(cl_long));
|
|
cl_long* inSecondData = (cl_long*)_malloca(inVecSize * numOrders * sizeof(cl_long));
|
|
cl_long* outData = (cl_long*)_malloca(outRealVecSize * numOrders * sizeof(cl_long));
|
|
#endif
|
|
memset(outData, 0, outRealVecSize * numOrders * sizeof(cl_long) );
|
|
|
|
generate_random_data( vecType, (unsigned int)( numOrders * inVecSize ), d, inData );
|
|
if( shuffleMode == kBuiltInDualInputFnMode )
|
|
generate_random_data( vecType, (unsigned int)( numOrders * inVecSize ), d, inSecondData );
|
|
|
|
streams[0] =
|
|
clCreateBuffer(context, CL_MEM_COPY_HOST_PTR,
|
|
typeSize * inVecSize * numOrders, inData, &error);
|
|
test_error( error, "Unable to create input stream" );
|
|
|
|
streams[1] =
|
|
clCreateBuffer(context, CL_MEM_COPY_HOST_PTR,
|
|
typeSize * outRealVecSize * numOrders, outData, &error);
|
|
test_error( error, "Unable to create output stream" );
|
|
|
|
int argIndex = 0;
|
|
if( shuffleMode == kBuiltInDualInputFnMode )
|
|
{
|
|
streams[2] = clCreateBuffer(context, CL_MEM_COPY_HOST_PTR,
|
|
typeSize * inVecSize * numOrders,
|
|
inSecondData, &error);
|
|
test_error( error, "Unable to create second input stream" );
|
|
|
|
error = clSetKernelArg( kernel, argIndex++, sizeof( streams[ 2 ] ), &streams[ 2 ] );
|
|
test_error( error, "Unable to set kernel argument" );
|
|
}
|
|
|
|
// Set kernel arguments
|
|
error = clSetKernelArg( kernel, argIndex++, sizeof( streams[ 0 ] ), &streams[ 0 ] );
|
|
test_error( error, "Unable to set kernel argument" );
|
|
error = clSetKernelArg( kernel, argIndex++, sizeof( streams[ 1 ] ), &streams[ 1 ] );
|
|
test_error( error, "Unable to set kernel argument" );
|
|
|
|
|
|
/* Run the kernel */
|
|
threads[0] = numOrders;
|
|
|
|
error = get_max_common_work_group_size( context, kernel, threads[0], &localThreads[0] );
|
|
test_error( error, "Unable to get work group size to use" );
|
|
|
|
error = clEnqueueNDRangeKernel( queue, kernel, 1, NULL, threads, localThreads, 0, NULL, NULL );
|
|
test_error( error, "Unable to execute test kernel" );
|
|
|
|
|
|
// Read the results back
|
|
error = clEnqueueReadBuffer( queue, streams[ 1 ], CL_TRUE, 0, typeSize * numOrders * outRealVecSize, outData, 0, NULL, NULL );
|
|
test_error( error, "Unable to read results" );
|
|
|
|
unsigned char *inDataPtr = (unsigned char *)inData;
|
|
unsigned char *inSecondDataPtr = (unsigned char *)inSecondData;
|
|
unsigned char *outDataPtr = (unsigned char *)outData;
|
|
int ret = 0;
|
|
int errors_printed = 0;
|
|
for( size_t i = 0; i < numOrders; i++ )
|
|
{
|
|
unsigned char expected[ 1024 ];
|
|
unsigned char temp[ 1024 ];
|
|
memset(expected, 0, sizeof(expected));
|
|
memset(temp, 0, sizeof(temp));
|
|
if( shuffleMode == kBuiltInFnMode )
|
|
shuffleVector( inDataPtr, expected, outOrderIdx[ i ], outVecSize, typeSize, lengthToUse[i] );
|
|
else if( shuffleMode == kBuiltInDualInputFnMode )
|
|
shuffleVectorDual( inDataPtr, inSecondDataPtr, expected, outOrderIdx[ i ], inVecSize, typeSize, lengthToUse[i] );
|
|
else
|
|
{
|
|
shuffleVector( inDataPtr, temp, inOrderIdx[ i ], inVecSize, typeSize, lengthToUse[i] );
|
|
shuffleVector2( temp, expected, outOrderIdx[ i ], outVecSize, typeSize, lengthToUse[i] );
|
|
}
|
|
|
|
if( memcmp( expected, outDataPtr, outVecSize * typeSize ) != 0 )
|
|
{
|
|
log_error( " ERROR: Shuffle test %d FAILED for %s (memory hex dump follows)\n", (int)i,
|
|
get_order_name( vecType, inVecSize, outVecSize, inOrderIdx[ i ], outOrderIdx[ i ], lengthToUse[i], d, inUseNumerics, outUseNumerics ) );
|
|
|
|
print_hex_mem_dump( inDataPtr, ( shuffleMode == kBuiltInDualInputFnMode ) ? inSecondDataPtr : NULL, expected, outDataPtr, inVecSize, outVecSize, typeSize );
|
|
|
|
if( ( shuffleMode == kBuiltInFnMode ) || ( shuffleMode == kBuiltInDualInputFnMode ) )
|
|
{
|
|
// Mask would've been different for every shuffle done, so we have to regen it to print it
|
|
char maskString[ 1024 ];
|
|
generate_shuffle_mask( maskString, outVecSize, ( outOrderIdx != NULL ) ? &outOrderIdx[ i ] : NULL );
|
|
log_error( " Mask: %s\n", maskString );
|
|
}
|
|
|
|
ret++;
|
|
errors_printed++;
|
|
if (errors_printed > MAX_ERRORS_TO_PRINT)
|
|
{
|
|
log_info("Further errors suppressed.\n");
|
|
return ret;
|
|
}
|
|
}
|
|
inDataPtr += inVecSize * typeSize;
|
|
inSecondDataPtr += inVecSize * typeSize;
|
|
outDataPtr += outRealVecSize * typeSize;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void build_random_shuffle_order( ShuffleOrder &outIndices, unsigned int length, unsigned int selectLength, bool allowRepeats, MTdata d )
|
|
{
|
|
char flags[ 16 ];
|
|
|
|
memset( flags, 0, sizeof( flags ) );
|
|
|
|
for( unsigned int i = 0; i < length; i++ )
|
|
{
|
|
char selector = (char)random_in_range( 0, selectLength - 1, d );
|
|
if( !allowRepeats )
|
|
{
|
|
while( flags[ (int)selector ] )
|
|
selector = (char)random_in_range( 0, selectLength - 1, d );
|
|
flags[ (int)selector ] = true;
|
|
}
|
|
outIndices[ i ] = selector;
|
|
}
|
|
}
|
|
|
|
class shuffleBuffer
|
|
{
|
|
public:
|
|
|
|
shuffleBuffer( cl_context ctx, cl_command_queue queue, ExplicitType type, size_t inSize, size_t outSize, ShuffleMode mode )
|
|
{
|
|
mContext = ctx;
|
|
mQueue = queue;
|
|
mVecType = type;
|
|
mInVecSize = inSize;
|
|
mOutVecSize = outSize;
|
|
mShuffleMode = mode;
|
|
|
|
mCount = 0;
|
|
|
|
// Here's the deal with mLengthToUse[i].
|
|
// if you have, for instance
|
|
// uchar4 dst;
|
|
// uchar8 src;
|
|
// you can do
|
|
// src.s0213 = dst.s1045;
|
|
// but you can also do
|
|
// src.s02 = dst.s10;
|
|
// which has a different effect
|
|
// The intent with these "sub lengths" is to test all such
|
|
// possibilities
|
|
// Calculate a range of sub-lengths within the vector to copy.
|
|
int i;
|
|
size_t maxSize = (mInVecSize < mOutVecSize) ? mInVecSize : mOutVecSize;
|
|
for(i=0; i<NUM_TESTS; i++)
|
|
{
|
|
// Built-in fns can't select sub-lengths (the mask must be the length of the dest vector).
|
|
// Well, at least for these tests...
|
|
if( ( mode == kBuiltInFnMode ) || ( mode == kBuiltInDualInputFnMode ) )
|
|
mLengthToUse[i] = (cl_int)mOutVecSize;
|
|
else
|
|
{
|
|
mLengthToUse[i] = (cl_uint)(((double)i/NUM_TESTS)*maxSize) + 1;
|
|
// Force the length to be a valid vector length.
|
|
if( ( mLengthToUse[i] == 1 ) && ( mode != kBuiltInFnMode ) )
|
|
mLengthToUse[i] = 1;
|
|
else if (mLengthToUse[i] < 4)
|
|
mLengthToUse[i] = 2;
|
|
else if (mLengthToUse[i] < 8)
|
|
mLengthToUse[i] = 4;
|
|
else if (mLengthToUse[i] < 16)
|
|
mLengthToUse[i] = 8;
|
|
else
|
|
mLengthToUse[i] = 16;
|
|
}
|
|
}
|
|
}
|
|
|
|
int AddRun( ShuffleOrder &inOrder, ShuffleOrder &outOrder, MTdata d )
|
|
{
|
|
memcpy( &mInOrders[ mCount ], &inOrder, sizeof( inOrder ) );
|
|
memcpy( &mOutOrders[ mCount ], &outOrder, sizeof( outOrder ) );
|
|
mCount++;
|
|
|
|
if( mCount == NUM_TESTS )
|
|
return Flush(d);
|
|
|
|
return CL_SUCCESS;
|
|
}
|
|
|
|
int Flush( MTdata d )
|
|
{
|
|
int err = CL_SUCCESS;
|
|
if( mCount > 0 )
|
|
{
|
|
err = test_shuffle_dual_kernel( mContext, mQueue, mVecType, mInVecSize, mOutVecSize, mLengthToUse,
|
|
mCount, mInOrders, mOutOrders, true, true, d, mShuffleMode );
|
|
mCount = 0;
|
|
}
|
|
return err;
|
|
}
|
|
|
|
protected:
|
|
cl_context mContext;
|
|
cl_command_queue mQueue;
|
|
ExplicitType mVecType;
|
|
size_t mInVecSize, mOutVecSize, mCount;
|
|
ShuffleMode mShuffleMode;
|
|
cl_uint mLengthToUse[ NUM_TESTS ];
|
|
|
|
ShuffleOrder mInOrders[ NUM_TESTS ], mOutOrders[ NUM_TESTS ];
|
|
};
|
|
|
|
|
|
int test_shuffle_random(cl_device_id device, cl_context context, cl_command_queue queue, ShuffleMode shuffleMode, MTdata d )
|
|
{
|
|
ExplicitType vecType[] = { kChar, kUChar, kShort, kUShort, kInt, kUInt, kLong, kULong, kFloat, kDouble };
|
|
unsigned int vecSizes[] = { 1, 2, 3, 4, 8, 16, 0 };
|
|
unsigned int srcIdx, dstIdx, typeIndex;
|
|
int error = 0, totalError = 0, prevTotalError = 0;
|
|
RandomSeed seed(gRandomSeed);
|
|
|
|
for( typeIndex = 0; typeIndex < 10; typeIndex++ )
|
|
{
|
|
//log_info( "\n\t%s... ", get_explicit_type_name( vecType[ typeIndex ] ) );
|
|
//fflush( stdout );
|
|
if (vecType[typeIndex] == kDouble) {
|
|
if (!is_extension_available(device, "cl_khr_fp64")) {
|
|
log_info("Extension cl_khr_fp64 not supported; skipping double tests.\n");
|
|
continue;
|
|
}
|
|
log_info("Testing doubles.\n");
|
|
}
|
|
|
|
if ((vecType[typeIndex] == kLong || vecType[typeIndex] == kULong) && !gHasLong )
|
|
{
|
|
log_info("Long types are unsupported, skipping.");
|
|
continue;
|
|
}
|
|
|
|
error = 0;
|
|
for( srcIdx = 0; vecSizes[ srcIdx ] != 0 /*&& error == 0*/; srcIdx++ )
|
|
{
|
|
for( dstIdx = 0; vecSizes[ dstIdx ] != 0 /*&& error == 0*/; dstIdx++ )
|
|
{
|
|
if( ( ( shuffleMode == kBuiltInDualInputFnMode ) || ( shuffleMode == kBuiltInFnMode ) ) &&
|
|
( ( vecSizes[ dstIdx ] & 1 ) || ( vecSizes[ srcIdx ] & 1 ) ) )
|
|
{
|
|
// Built-in shuffle functions don't work on size 1 (scalars) or size 3 (vec3s)
|
|
continue;
|
|
}
|
|
|
|
log_info("Testing [%s%d to %s%d]... ", get_explicit_type_name( vecType[ typeIndex ] ) , vecSizes[srcIdx], get_explicit_type_name( vecType[ typeIndex ] ) , vecSizes[dstIdx]);
|
|
shuffleBuffer buffer( context, queue, vecType[ typeIndex ], vecSizes[ srcIdx ], vecSizes[ dstIdx ], shuffleMode );
|
|
|
|
int numTests = NUM_TESTS*NUM_ITERATIONS_PER_TEST;
|
|
for( int i = 0; i < numTests /*&& error == 0*/; i++ )
|
|
{
|
|
ShuffleOrder src, dst;
|
|
if( shuffleMode == kBuiltInFnMode )
|
|
{
|
|
build_random_shuffle_order( dst, vecSizes[ dstIdx ], vecSizes[ srcIdx ], true, d );
|
|
}
|
|
else if(shuffleMode == kBuiltInDualInputFnMode)
|
|
{
|
|
build_random_shuffle_order(dst, vecSizes[dstIdx], 2*vecSizes[srcIdx], true, d);
|
|
}
|
|
else
|
|
{
|
|
build_random_shuffle_order( src, vecSizes[ dstIdx ], vecSizes[ srcIdx ], true, d );
|
|
build_random_shuffle_order( dst, vecSizes[ dstIdx ], vecSizes[ dstIdx ], false, d );
|
|
}
|
|
|
|
error = buffer.AddRun( src, dst, seed );
|
|
if (error)
|
|
totalError++;
|
|
}
|
|
int test_error = buffer.Flush(seed);
|
|
if (test_error)
|
|
totalError++;
|
|
|
|
if (totalError == prevTotalError)
|
|
log_info("\tPassed.\n");
|
|
else
|
|
{
|
|
log_error("\tFAILED.\n");
|
|
prevTotalError = totalError;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return totalError;
|
|
}
|
|
|
|
int test_shuffle_copy(cl_device_id device, cl_context context, cl_command_queue queue, int n_elems)
|
|
{
|
|
RandomSeed seed(gRandomSeed);
|
|
return test_shuffle_random( device, context, queue, kNormalMode, seed );
|
|
}
|
|
|
|
int test_shuffle_function_call(cl_device_id device, cl_context context, cl_command_queue queue, int n_elems)
|
|
{
|
|
RandomSeed seed(gRandomSeed);
|
|
return test_shuffle_random( device, context, queue, kFunctionCallMode, seed );
|
|
}
|
|
|
|
int test_shuffle_array_cast(cl_device_id device, cl_context context, cl_command_queue queue, int n_elems)
|
|
{
|
|
RandomSeed seed(gRandomSeed);
|
|
return test_shuffle_random( device, context, queue, kArrayAccessMode, seed );
|
|
}
|
|
|
|
int test_shuffle_built_in(cl_device_id device, cl_context context, cl_command_queue queue, int n_elems)
|
|
{
|
|
RandomSeed seed(gRandomSeed);
|
|
return test_shuffle_random( device, context, queue, kBuiltInFnMode, seed );
|
|
}
|
|
|
|
int test_shuffle_built_in_dual_input(cl_device_id device, cl_context context, cl_command_queue queue, int n_elems)
|
|
{
|
|
RandomSeed seed(gRandomSeed);
|
|
return test_shuffle_random( device, context, queue, kBuiltInDualInputFnMode, seed );
|
|
}
|
|
|