| // |
| // 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 "harness/compat.h" |
| #include "harness/rounding_mode.h" |
| #include "harness/kernelHelpers.h" |
| |
| #include "test_printf.h" |
| #include <assert.h> |
| |
| // Helpers for generating runtime reference results |
| static void intRefBuilder(printDataGenParameters&, char*, const size_t); |
| static void floatRefBuilder(printDataGenParameters&, char* rResult, const size_t); |
| static void octalRefBuilder(printDataGenParameters&, char*, const size_t); |
| static void unsignedRefBuilder(printDataGenParameters&, char*, const size_t); |
| static void hexRefBuilder(printDataGenParameters&, char*, const size_t); |
| |
| //================================== |
| |
| // int |
| |
| //================================== |
| |
| //------------------------------------------------------ |
| |
| // [string] format | [string] int-data representation | |
| |
| //------------------------------------------------------ |
| |
| std::vector<printDataGenParameters> printIntGenParameters = { |
| |
| //(Minimum)Five-wide,default(right)-justified |
| |
| {"%5d","10"}, |
| |
| //(Minimum)Five-wide,left-justified |
| |
| {"%-5d","10"}, |
| |
| //(Minimum)Five-wide,default(right)-justified,zero-filled |
| |
| {"%05d","10"}, |
| |
| //(Minimum)Five-wide,default(right)-justified,with sign |
| |
| {"%+5d","10"}, |
| |
| //(Minimum)Five-wide ,left-justified,with sign |
| |
| {"%-+5d","10"}, |
| |
| //(Minimum)Five-digit(zero-filled in absent digits),default(right)-justified |
| |
| {"%.5i","100"}, |
| |
| //(Minimum)Six-wide,Five-digit(zero-filled in absent digits),default(right)-justified |
| |
| {"%6.5i","100"}, |
| |
| //0 and - flag both apper ==>0 is ignored,left-justified,capital I |
| |
| {"%-06i","100"}, |
| |
| //(Minimum)Six-wide,Five-digit(zero-filled in absent digits),default(right)-justified |
| |
| {"%06.5i","100"} |
| |
| }; |
| |
| //----------------------------------------------- |
| |
| //test case for int | |
| |
| //----------------------------------------------- |
| |
| testCase testCaseInt = { |
| |
| TYPE_INT, |
| |
| correctBufferInt, |
| |
| printIntGenParameters, |
| |
| intRefBuilder, |
| |
| kint |
| |
| }; |
| |
| |
| |
| |
| |
| //============================================== |
| |
| // float |
| |
| //============================================== |
| |
| |
| |
| //-------------------------------------------------------- |
| |
| // [string] format | [string] float-data representation | |
| |
| //-------------------------------------------------------- |
| |
| std::vector<printDataGenParameters> printFloatGenParameters = { |
| |
| //Default(right)-justified |
| |
| {"%f","10.3456"}, |
| |
| //One position after the decimal,default(right)-justified |
| |
| {"%.1f","10.3456"}, |
| |
| //Two positions after the decimal,default(right)-justified |
| |
| {"%.2f","10.3456"}, |
| |
| //(Minimum)Eight-wide,three positions after the decimal,default(right)-justified |
| |
| {"%8.3f","10.3456"}, |
| |
| //(Minimum)Eight-wide,two positions after the decimal,zero-filled,default(right)-justified |
| |
| {"%08.2f","10.3456"}, |
| |
| //(Minimum)Eight-wide,two positions after the decimal,left-justified |
| |
| {"%-8.2f","10.3456"}, |
| |
| //(Minimum)Eight-wide,two positions after the decimal,with sign,default(right)-justified |
| |
| {"%+8.2f","-10.3456"}, |
| |
| //Zero positions after the decimal([floor]rounding),default(right)-justified |
| |
| {"%.0f","0.1"}, |
| |
| //Zero positions after the decimal([ceil]rounding),default(right)-justified |
| |
| {"%.0f","0.6"}, |
| |
| //Zero-filled,default positions number after the decimal,default(right)-justified |
| |
| {"%0f","0.6"}, |
| |
| //Double argument representing floating-point,used by f style,default(right)-justified |
| |
| {"%4g","12345.6789"}, |
| |
| //Double argument representing floating-point,used by e style,default(right)-justified |
| |
| {"%4.2g","12345.6789"}, |
| |
| //Double argument representing floating-point,used by f style,default(right)-justified |
| |
| {"%4G","0.0000023"}, |
| |
| //Double argument representing floating-point,used by e style,default(right)-justified |
| |
| {"%4G","0.023"}, |
| |
| //Double argument representing floating-point,with exponent,left-justified,default(right)-justified |
| |
| {"%-#20.15e","789456123.0"}, |
| |
| //Double argument representing floating-point,with exponent,left-justified,with sign,capital E,default(right)-justified |
| |
| {"%+#21.15E","789456123.0"}, |
| |
| //Double argument representing floating-point,in [-]xh.hhhhpAd style |
| |
| {"%.6a","0.1"}, |
| |
| //(Minimum)Ten-wide,Double argument representing floating-point,in xh.hhhhpAd style,default(right)-justified |
| |
| {"%10.2a","9990.235"}, |
| }; |
| |
| //--------------------------------------------------------- |
| |
| //Test case for float | |
| |
| //--------------------------------------------------------- |
| |
| testCase testCaseFloat = { |
| |
| TYPE_FLOAT, |
| |
| correctBufferFloat, |
| |
| printFloatGenParameters, |
| |
| floatRefBuilder, |
| |
| kfloat |
| |
| }; |
| |
| //============================================== |
| |
| // float limits |
| |
| //============================================== |
| |
| |
| |
| //-------------------------------------------------------- |
| |
| // [string] format | [string] float-data representation | |
| |
| //-------------------------------------------------------- |
| |
| |
| std::vector<printDataGenParameters> printFloatLimitsGenParameters = { |
| |
| //Infinity (1.0/0.0) |
| |
| {"%f","1.0f/0.0f"}, |
| |
| //NaN |
| |
| {"%f","sqrt(-1.0f)"}, |
| |
| //NaN |
| {"%f","acospi(2.0f)"} |
| }; |
| //-------------------------------------------------------- |
| |
| // Lookup table - [string]float-correct buffer | |
| |
| //-------------------------------------------------------- |
| |
| std::vector<std::string> correctBufferFloatLimits = { |
| |
| "inf", |
| |
| "-nan", |
| |
| "nan" |
| }; |
| |
| //--------------------------------------------------------- |
| |
| //Test case for float | |
| |
| //--------------------------------------------------------- |
| |
| testCase testCaseFloatLimits = { |
| |
| TYPE_FLOAT_LIMITS, |
| |
| correctBufferFloatLimits, |
| |
| printFloatLimitsGenParameters, |
| |
| NULL |
| |
| }; |
| |
| //========================================================= |
| |
| // octal |
| |
| //========================================================= |
| |
| |
| |
| //--------------------------------------------------------- |
| |
| // [string] format | [string] octal-data representation | |
| |
| //--------------------------------------------------------- |
| |
| std::vector<printDataGenParameters> printOctalGenParameters = { |
| |
| //Default(right)-justified |
| |
| {"%o","10"}, |
| |
| //Five-digit,default(right)-justified |
| |
| {"%.5o","10"}, |
| |
| //Default(right)-justified,increase precision |
| |
| {"%#o","100000000"}, |
| |
| //(Minimum)Four-wide,Five-digit,0-flag ignored(because of precision),default(right)-justified |
| |
| {"%04.5o","10"} |
| |
| }; |
| |
| //------------------------------------------------------- |
| |
| //Test case for octal | |
| |
| //------------------------------------------------------- |
| |
| testCase testCaseOctal = { |
| |
| TYPE_OCTAL, |
| |
| correctBufferOctal, |
| |
| printOctalGenParameters, |
| |
| octalRefBuilder, |
| |
| kulong |
| |
| }; |
| |
| |
| |
| //========================================================= |
| |
| // unsigned |
| |
| //========================================================= |
| |
| |
| |
| //--------------------------------------------------------- |
| |
| // [string] format | [string] unsined-data representation | |
| |
| //--------------------------------------------------------- |
| |
| std::vector<printDataGenParameters> printUnsignedGenParameters = { |
| |
| //Default(right)-justified |
| |
| {"%u","10"}, |
| |
| //Zero precision for zero,default(right)-justified |
| |
| {"%.0u","0"}, |
| |
| }; |
| |
| //------------------------------------------------------- |
| |
| //Test case for octal | |
| |
| //------------------------------------------------------- |
| |
| testCase testCaseUnsigned = { |
| |
| TYPE_UNSIGNED, |
| |
| correctBufferUnsigned, |
| |
| printUnsignedGenParameters, |
| |
| unsignedRefBuilder, |
| |
| kulong |
| |
| }; |
| |
| |
| |
| //======================================================= |
| |
| // hexadecimal |
| |
| //======================================================= |
| |
| |
| |
| //-------------------------------------------------------------- |
| |
| // [string] format | [string] hexadecimal-data representation | |
| |
| //-------------------------------------------------------------- |
| |
| std::vector<printDataGenParameters> printHexadecimalGenParameters = { |
| |
| //Add 0x,low x,default(right)-justified |
| |
| {"%#x","0xABCDEF"}, |
| |
| //Add 0x,capital X,default(right)-justified |
| |
| {"%#X","0xABCDEF"}, |
| |
| //Not add 0x,if zero,default(right)-justified |
| |
| {"%#X","0"}, |
| |
| //(Minimum)Eight-wide,default(right)-justified |
| |
| {"%8x","399"}, |
| |
| //(Minimum)Four-wide,zero-filled,default(right)-justified |
| |
| {"%04x","399"} |
| |
| }; |
| |
| //-------------------------------------------------------------- |
| |
| //Test case for hexadecimal | |
| |
| //-------------------------------------------------------------- |
| |
| testCase testCaseHexadecimal = { |
| |
| TYPE_HEXADEC, |
| |
| correctBufferHexadecimal, |
| |
| printHexadecimalGenParameters, |
| |
| hexRefBuilder, |
| |
| kulong |
| |
| }; |
| |
| |
| |
| //============================================================= |
| |
| // char |
| |
| //============================================================= |
| |
| |
| |
| //----------------------------------------------------------- |
| |
| // [string] format | [string] string-data representation | |
| |
| //----------------------------------------------------------- |
| |
| std::vector<printDataGenParameters> printCharGenParameters = { |
| |
| //Four-wide,zero-filled,default(right)-justified |
| |
| {"%4c","\'1\'"}, |
| |
| //Four-wide,left-justified |
| |
| {"%-4c","\'1\'"}, |
| |
| //(unsigned) int argument,default(right)-justified |
| |
| {"%c","66"} |
| |
| }; |
| |
| //--------------------------------------------------------- |
| |
| // Lookup table -[string] char-correct buffer | |
| |
| //--------------------------------------------------------- |
| |
| std::vector<std::string> correctBufferChar = { |
| |
| " 1", |
| |
| "1 ", |
| |
| "B", |
| |
| }; |
| |
| |
| |
| |
| //---------------------------------------------------------- |
| |
| //Test case for char | |
| |
| //---------------------------------------------------------- |
| |
| testCase testCaseChar = { |
| |
| TYPE_CHAR, |
| |
| correctBufferChar, |
| |
| printCharGenParameters, |
| |
| NULL, |
| |
| kchar |
| |
| }; |
| |
| |
| |
| //========================================================== |
| |
| // string |
| |
| //========================================================== |
| |
| |
| |
| //-------------------------------------------------------- |
| |
| // [string]format | [string] string-data representation | |
| |
| //-------------------------------------------------------- |
| |
| std::vector<printDataGenParameters> printStringGenParameters = { |
| |
| //(Minimum)Four-wide,zero-filled,default(right)-justified |
| |
| {"%4s","\"foo\""}, |
| |
| //One-digit(precision ignored),left-justified |
| |
| {"%.1s","\"foo\""}, |
| |
| //%% specification |
| |
| {"%s","\"%%\""}, |
| }; |
| |
| //--------------------------------------------------------- |
| |
| // Lookup table -[string] string-correct buffer | |
| |
| //--------------------------------------------------------- |
| |
| std::vector<std::string> correctBufferString = { |
| |
| " foo", |
| |
| "f", |
| |
| "%%", |
| }; |
| |
| |
| //--------------------------------------------------------- |
| |
| //Test case for string | |
| |
| //--------------------------------------------------------- |
| |
| testCase testCaseString = { |
| |
| TYPE_STRING, |
| |
| correctBufferString, |
| |
| printStringGenParameters, |
| |
| NULL, |
| |
| kchar |
| |
| }; |
| |
| |
| |
| //========================================================= |
| |
| // vector |
| |
| //========================================================= |
| |
| |
| |
| //------------------------------------------------------------------------------------------------------------------- |
| |
| //[string] flag | [string] specifier | [string] type | [string] vector-data representation | [string] vector size | |
| |
| //------------------------------------------------------------------------------------------------------------------- |
| |
| std::vector<printDataGenParameters> printVectorGenParameters = { |
| |
| //(Minimum)Two-wide,two positions after decimal |
| |
| {NULL,"(1.0f,2.0f,3.0f,4.0f)","%2.2","hlf","float","4"}, |
| |
| //Alternative form,uchar argument |
| |
| {NULL,"(0xFA,0xFB)","%#","hhx","uchar","2"}, |
| |
| //Alternative form,ushort argument |
| |
| {NULL,"(0x1234,0x8765)","%#","hx","ushort","2"}, |
| |
| //Alternative form,uint argument |
| |
| {NULL,"(0x12345678,0x87654321)","%#","hlx","uint","2"}, |
| |
| //Alternative form,long argument |
| |
| {NULL,"(12345678,98765432)","%","ld","long","2"} |
| |
| }; |
| |
| //------------------------------------------------------------ |
| |
| // Lookup table -[string] vector-correct buffer | |
| |
| //------------------------------------------------------------ |
| |
| std::vector<std::string> correctBufferVector = { |
| |
| "1.00,2.00,3.00,4.00", |
| |
| "0xfa,0xfb", |
| |
| "0x1234,0x8765", |
| |
| "0x12345678,0x87654321", |
| |
| "12345678,98765432" |
| |
| }; |
| |
| //----------------------------------------------------------- |
| |
| //Test case for vector | |
| |
| //----------------------------------------------------------- |
| |
| testCase testCaseVector = { |
| |
| TYPE_VECTOR, |
| |
| correctBufferVector, |
| |
| printVectorGenParameters, |
| |
| NULL |
| |
| }; |
| |
| |
| |
| //================================================================== |
| |
| // address space |
| |
| //================================================================== |
| |
| |
| |
| //------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| |
| // [string] argument type qualifier |[string] variable type qualifier + initialization | [string] format | [string] parameter |[string]%p indicator/additional code | |
| |
| //------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| |
| |
| |
| std::vector<printDataGenParameters> printAddrSpaceGenParameters = { |
| |
| //Global memory region |
| |
| {"\"%d\\n\"",NULL,NULL,NULL,NULL,NULL,"__global int* x","","*x",""}, |
| |
| //Global,constant, memory region |
| |
| {"\"%d\\n\"",NULL,NULL,NULL,NULL,NULL,"constant int* x","","*x",""}, |
| |
| //Local memory region |
| |
| {"\"%+d\\n\"",NULL,NULL,NULL,NULL,NULL,"","local int x;\n x= (int)3;\n","x",""}, |
| |
| //Private memory region |
| |
| {"\"%i\\n\"",NULL,NULL,NULL,NULL,NULL,"","private int x;\n x = (int)-1;\n","x",""}, |
| |
| //Address of void * from global memory region |
| |
| {"\"%p\\n\"",NULL,NULL,NULL,NULL,NULL,"__global void* x,__global intptr_t* xAddr","","x","*xAddr = (intptr_t)x;\n"} |
| |
| }; |
| |
| //------------------------------------------------------------------------------- |
| |
| // Lookup table -[string] address space -correct buffer | |
| |
| //------------------------------------------------------------------------------- |
| |
| std::vector<std::string> correctAddrSpace = { |
| |
| "2","2","+3","-1","" |
| |
| }; |
| |
| //------------------------------------------------------------------------------- |
| |
| //Test case for address space | |
| |
| //------------------------------------------------------------------------------- |
| |
| testCase testCaseAddrSpace = { |
| |
| TYPE_ADDRESS_SPACE, |
| |
| correctAddrSpace, |
| |
| printAddrSpaceGenParameters, |
| |
| NULL |
| |
| }; |
| |
| |
| |
| //------------------------------------------------------------------------------- |
| |
| //All Test cases | |
| |
| //------------------------------------------------------------------------------- |
| |
| std::vector<testCase*> allTestCase = {&testCaseInt,&testCaseFloat,&testCaseFloatLimits,&testCaseOctal,&testCaseUnsigned,&testCaseHexadecimal,&testCaseChar,&testCaseString,&testCaseVector,&testCaseAddrSpace}; |
| |
| |
| //----------------------------------------- |
| |
| // Check functions |
| |
| //----------------------------------------- |
| |
| size_t verifyOutputBuffer(char *analysisBuffer,testCase* pTestCase,size_t testId,cl_ulong pAddr) |
| { |
| int terminatePos = strlen(analysisBuffer); |
| if(terminatePos > 0) |
| { |
| analysisBuffer[terminatePos - 1] = '\0'; |
| } |
| |
| //Convert analysis buffer to long for address space |
| if(pTestCase->_type == TYPE_ADDRESS_SPACE && strcmp(pTestCase->_genParameters[testId].addrSpacePAdd,"")) |
| |
| { |
| char analysisBufferTmp[ANALYSIS_BUFFER_SIZE]; |
| |
| if(strstr(analysisBuffer,"0x") == NULL) |
| // Need to prepend 0x to ASCII number before calling strtol. |
| strcpy(analysisBufferTmp,"0x"); |
| |
| else analysisBufferTmp[0]='\0'; |
| strcat(analysisBufferTmp,analysisBuffer); |
| if (sizeof(long) == 8) { |
| if(strtoul(analysisBufferTmp,NULL,0) == pAddr) return 0; |
| } |
| else { |
| if(strtoull(analysisBufferTmp,NULL,0) == pAddr) return 0; |
| } |
| return 1; |
| |
| } |
| |
| char* exp; |
| //Exponenent representation |
| if((exp = strstr(analysisBuffer,"E+")) != NULL || (exp = strstr(analysisBuffer,"e+")) != NULL || (exp = strstr(analysisBuffer,"E-")) != NULL || (exp = strstr(analysisBuffer,"e-")) != NULL) |
| { |
| char correctExp[3]={0}; |
| strncpy(correctExp,exp,2); |
| |
| char* eCorrectBuffer = strstr((char*)pTestCase->_correctBuffer[testId].c_str(),correctExp); |
| if(eCorrectBuffer == NULL) |
| return 1; |
| |
| eCorrectBuffer+=2; |
| exp += 2; |
| |
| //Exponent always contains at least two digits |
| if(strlen(exp) < 2) |
| return 1; |
| //Skip leading zeros in the exponent |
| while(*exp == '0') |
| ++exp; |
| while(*eCorrectBuffer == '0') |
| ++eCorrectBuffer; |
| return strcmp(eCorrectBuffer,exp); |
| } |
| if(!strcmp(pTestCase->_correctBuffer[testId].c_str(),"inf")) |
| return strcmp(analysisBuffer,"inf")&&strcmp(analysisBuffer,"infinity")&&strcmp(analysisBuffer,"1.#INF00")&&strcmp(analysisBuffer,"Inf"); |
| if(!strcmp(pTestCase->_correctBuffer[testId].c_str(),"nan") || !strcmp(pTestCase->_correctBuffer[testId].c_str(),"-nan")) { |
| return strcmp(analysisBuffer,"nan")&&strcmp(analysisBuffer,"-nan")&&strcmp(analysisBuffer,"1.#IND00")&&strcmp(analysisBuffer,"-1.#IND00")&&strcmp(analysisBuffer,"NaN")&&strcmp(analysisBuffer,"nan(ind)")&&strcmp(analysisBuffer,"nan(snan)")&&strcmp(analysisBuffer,"-nan(ind)"); |
| } |
| return strcmp(analysisBuffer,pTestCase->_correctBuffer[testId].c_str()); |
| } |
| |
| static void intRefBuilder(printDataGenParameters& params, char* refResult, const size_t refSize) |
| { |
| snprintf(refResult, refSize, params.genericFormat, atoi(params.dataRepresentation)); |
| } |
| |
| static void floatRefBuilder(printDataGenParameters& params, char* refResult, const size_t refSize) |
| { |
| snprintf(refResult, refSize, params.genericFormat, strtof(params.dataRepresentation, NULL)); |
| } |
| |
| static void octalRefBuilder(printDataGenParameters& params, char* refResult, const size_t refSize) |
| { |
| const unsigned long int data = strtoul(params.dataRepresentation, NULL, 10); |
| snprintf(refResult, refSize, params.genericFormat, data); |
| } |
| |
| static void unsignedRefBuilder(printDataGenParameters& params, char* refResult, const size_t refSize) |
| { |
| const unsigned long int data = strtoul(params.dataRepresentation, NULL, 10); |
| snprintf(refResult, refSize, params.genericFormat, data); |
| } |
| |
| static void hexRefBuilder(printDataGenParameters& params, char* refResult, const size_t refSize) |
| { |
| const unsigned long int data = strtoul(params.dataRepresentation, NULL, 0); |
| snprintf(refResult, refSize, params.genericFormat, data); |
| } |
| |
| /* |
| Generate reference results. |
| |
| Results are only generated for test cases |
| that can easily be generated by using CPU |
| printf. |
| |
| If that is not the case, results are constants |
| that have been hard-coded. |
| */ |
| void generateRef(const cl_device_id device) |
| { |
| int fd = -1; |
| char _refBuffer[ANALYSIS_BUFFER_SIZE]; |
| const cl_device_fp_config fpConfig = get_default_rounding_mode(device); |
| const RoundingMode hostRound = get_round(); |
| RoundingMode deviceRound; |
| |
| // Map device rounding to CTS rounding type |
| // get_default_rounding_mode supports RNE and RTZ |
| if (fpConfig == CL_FP_ROUND_TO_NEAREST) |
| { |
| deviceRound = kRoundToNearestEven; |
| } |
| else if (fpConfig == CL_FP_ROUND_TO_ZERO) |
| { |
| deviceRound = kRoundTowardZero; |
| } |
| else |
| { |
| assert(false && "Unreachable"); |
| } |
| |
| // Loop through all test cases |
| for (auto &caseToTest: allTestCase) |
| { |
| /* |
| Cases that have a NULL function pointer |
| already have their reference results |
| as they're constant and hard-coded |
| */ |
| if (caseToTest->printFN == NULL) |
| continue; |
| |
| // Make sure the reference result is empty |
| assert(caseToTest->_correctBuffer.size() == 0); |
| |
| // Loop through each input |
| for (auto ¶ms: caseToTest->_genParameters) |
| { |
| char refResult[ANALYSIS_BUFFER_SIZE]; |
| // Set CPU rounding mode to match that of the device |
| set_round(deviceRound, caseToTest->dataType); |
| // Generate the result |
| caseToTest->printFN(params, refResult, ARRAY_SIZE(refResult)); |
| // Restore the original CPU rounding mode |
| set_round(hostRound, kfloat); |
| // Save the reference result |
| caseToTest->_correctBuffer.push_back(refResult); |
| } |
| } |
| } |