mirror of
https://github.com/KhronosGroup/OpenCL-CTS.git
synced 2026-03-18 22:09:01 +00:00
Unlike related PR #2344 that simply warns about unsupported FTZ, this PR attempts to correctly handle FTZ on RISC-V. RISC-V 'f' extension does not support any way to enable/disable flushing subnormals to zero, implementations are required to always support subnormals. Therefore this PR re-uses FTZ handling code from PPC, where flushing also has to be explicitly performed.
404 lines
13 KiB
C++
404 lines
13 KiB
C++
//
|
|
// Copyright (c) 2023-2024 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.
|
|
//
|
|
#ifndef BASIC_TEST_CONVERSIONS_H
|
|
#define BASIC_TEST_CONVERSIONS_H
|
|
|
|
#if !defined(_WIN32)
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#include "harness/errorHelpers.h"
|
|
#include "harness/rounding_mode.h"
|
|
|
|
#include <stdio.h>
|
|
#if defined( __APPLE__ )
|
|
#include <OpenCL/opencl.h>
|
|
#else
|
|
#include <CL/opencl.h>
|
|
#endif
|
|
|
|
#include <CL/cl_half.h>
|
|
|
|
#include "harness/mt19937.h"
|
|
#include "harness/testHarness.h"
|
|
#include "harness/typeWrappers.h"
|
|
|
|
#include <memory>
|
|
#include <tuple>
|
|
#include <vector>
|
|
|
|
#include "conversions_data_info.h"
|
|
|
|
#define kVectorSizeCount 6
|
|
#define kMaxVectorSize 16
|
|
#define kPageSize 4096
|
|
|
|
#define BUFFER_SIZE (1024 * 1024)
|
|
#define EMBEDDED_REDUCTION_FACTOR 16
|
|
#define PERF_LOOP_COUNT 100
|
|
|
|
extern const char *gTypeNames[ kTypeCount ];
|
|
extern const char *gRoundingModeNames[ kRoundingModeCount ]; // { "", "_rte", "_rtp", "_rtn", "_rtz" }
|
|
extern const char *gSaturationNames[ kSaturationModeCount ]; // { "", "_sat" }
|
|
extern const char *gVectorSizeNames[kVectorSizeCount]; // { "", "2", "4", "8", "16" }
|
|
extern size_t gTypeSizes[ kTypeCount ];
|
|
|
|
//Functions for clamping floating point numbers into the representable range for the type
|
|
typedef float (*clampf)( float );
|
|
typedef double (*clampd)( double );
|
|
|
|
extern clampf gClampFloat[ kTypeCount ][kRoundingModeCount];
|
|
extern clampd gClampDouble[ kTypeCount ][kRoundingModeCount];
|
|
|
|
typedef void (*InitDataFunc)( void *dest, SaturationMode, RoundingMode, Type destType, uint64_t start, int count, MTdata d );
|
|
extern InitDataFunc gInitFunctions[ kTypeCount ];
|
|
|
|
typedef int (*CheckResults)( void *out1, void *out2, void *allowZ, uint32_t count, int vectorSize );
|
|
extern CheckResults gCheckResults[ kTypeCount ];
|
|
|
|
#define kCallStyleCount (kVectorSizeCount + 1 /* for implicit scalar */)
|
|
|
|
extern MTdata gMTdata;
|
|
extern cl_command_queue gQueue;
|
|
extern cl_context gContext;
|
|
extern cl_mem gInBuffer;
|
|
extern cl_mem gOutBuffers[];
|
|
extern int gHasDouble;
|
|
extern int gTestDouble;
|
|
extern int gHasHalfs;
|
|
extern int gTestHalfs;
|
|
extern int gWimpyReductionFactor;
|
|
extern int gSkipTesting;
|
|
extern int gMinVectorSize;
|
|
extern int gMaxVectorSize;
|
|
extern int gForceFTZ;
|
|
extern int gStartTestNumber;
|
|
extern int gEndTestNumber;
|
|
extern int gIsRTZ;
|
|
extern int gForceHalfFTZ;
|
|
extern int gIsHalfRTZ;
|
|
extern void *gIn;
|
|
extern void *gRef;
|
|
extern void *gAllowZ;
|
|
extern void *gOut[];
|
|
|
|
extern const char **argList;
|
|
extern int argCount;
|
|
|
|
extern const char *sizeNames[];
|
|
extern int vectorSizes[];
|
|
|
|
extern size_t gComputeDevices;
|
|
extern uint32_t gDeviceFrequency;
|
|
|
|
namespace conv_test {
|
|
|
|
cl_program MakeProgram(Type outType, Type inType, SaturationMode sat,
|
|
RoundingMode round, int vectorSize,
|
|
cl_kernel *outKernel);
|
|
|
|
int RunKernel(cl_kernel kernel, void *inBuf, void *outBuf, size_t blockCount);
|
|
|
|
int GetTestCase(const char *name, Type *outType, Type *inType,
|
|
SaturationMode *sat, RoundingMode *round);
|
|
|
|
cl_int InitData(cl_uint job_id, cl_uint thread_id, void *p);
|
|
cl_int PrepareReference(cl_uint job_id, cl_uint thread_id, void *p);
|
|
uint64_t GetTime(void);
|
|
|
|
void WriteInputBufferComplete(void *);
|
|
}
|
|
|
|
struct CalcRefValsBase
|
|
{
|
|
virtual ~CalcRefValsBase() = default;
|
|
virtual int check_result(void *, uint32_t, int) { return 0; }
|
|
|
|
// pointer back to the parent WriteInputBufferInfo struct
|
|
struct WriteInputBufferInfo *parent;
|
|
clKernelWrapper kernel; // the kernel for this vector size
|
|
clProgramWrapper program; // the program for this vector size
|
|
cl_uint vectorSize; // the vector size for this callback chain
|
|
void *p; // the pointer to mapped result data for this vector size
|
|
cl_int result;
|
|
};
|
|
|
|
template <typename InType, typename OutType, bool InFP, bool OutFP>
|
|
struct CalcRefValsPat : CalcRefValsBase
|
|
{
|
|
int check_result(void *, uint32_t, int) override;
|
|
};
|
|
|
|
struct WriteInputBufferInfo
|
|
{
|
|
WriteInputBufferInfo()
|
|
: calcReferenceValues(nullptr), doneBarrier(nullptr), count(0),
|
|
outType(kuchar), inType(kuchar), barrierCount(0)
|
|
{}
|
|
|
|
volatile cl_event
|
|
calcReferenceValues; // user event which signals when main thread is
|
|
// done calculating reference values
|
|
volatile cl_event
|
|
doneBarrier; // user event which signals when worker threads are done
|
|
cl_uint count; // the number of elements in the array
|
|
Type outType; // the data type of the conversion result
|
|
Type inType; // the data type of the conversion input
|
|
volatile int barrierCount;
|
|
|
|
std::vector<std::unique_ptr<CalcRefValsBase>> calcInfo;
|
|
};
|
|
|
|
// Must be aligned with Type enums!
|
|
using TypeIter =
|
|
std::tuple<cl_uchar, cl_char, cl_ushort, cl_short, cl_uint, cl_int, cl_half,
|
|
cl_float, cl_double, cl_ulong, cl_long>;
|
|
|
|
// hardcoded solution needed due to typeid confusing cl_ushort/cl_half
|
|
constexpr bool isTypeFp[] = { 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0 };
|
|
|
|
// Helper test fixture for constructing OpenCL objects used in testing
|
|
// a variety of simple command-buffer enqueue scenarios.
|
|
struct ConversionsTest
|
|
{
|
|
virtual ~ConversionsTest() = default;
|
|
|
|
ConversionsTest(cl_device_id device, cl_context context,
|
|
cl_command_queue queue);
|
|
|
|
cl_int SetUp(int elements);
|
|
|
|
// Test body returning an OpenCL error code
|
|
cl_int Run();
|
|
|
|
template <typename InType, typename OutType, bool InFP, bool OutFP>
|
|
int DoTest(Type outType, Type inType, SaturationMode sat,
|
|
RoundingMode round);
|
|
|
|
template <typename InType, typename OutType, bool InFP, bool OutFP>
|
|
void TestTypesConversion(const Type &inType, const Type &outType, int &tn,
|
|
int startMinVectorSize);
|
|
|
|
protected:
|
|
cl_context context;
|
|
cl_device_id device;
|
|
cl_command_queue queue;
|
|
|
|
size_t num_elements;
|
|
|
|
TypeIter typeIterator;
|
|
|
|
public:
|
|
static cl_half_rounding_mode defaultHalfRoundingMode;
|
|
};
|
|
|
|
struct CustomConversionsTest : ConversionsTest
|
|
{
|
|
CustomConversionsTest(cl_device_id device, cl_context context,
|
|
cl_command_queue queue)
|
|
: ConversionsTest(device, context, queue)
|
|
{}
|
|
|
|
cl_int Run();
|
|
};
|
|
|
|
template <class T>
|
|
int MakeAndRunTest(cl_device_id device, cl_context context,
|
|
cl_command_queue queue, int num_elements)
|
|
{
|
|
T test_fixture(device, context, queue);
|
|
|
|
cl_int error = test_fixture.SetUp(num_elements);
|
|
test_error_ret(error, "Error in test initialization", TEST_FAIL);
|
|
|
|
return test_fixture.Run();
|
|
}
|
|
|
|
struct TestType
|
|
{
|
|
template <typename T, bool FP> bool testType(Type in)
|
|
{
|
|
switch (in)
|
|
{
|
|
default: return false;
|
|
case kuchar: return std::is_same<cl_uchar, T>::value;
|
|
case kchar: return std::is_same<cl_char, T>::value;
|
|
case kushort: return std::is_same<cl_ushort, T>::value && !FP;
|
|
case kshort: return std::is_same<cl_short, T>::value;
|
|
case kuint: return std::is_same<cl_uint, T>::value;
|
|
case kint: return std::is_same<cl_int, T>::value;
|
|
case khalf: return std::is_same<cl_half, T>::value && FP;
|
|
case kfloat: return std::is_same<cl_float, T>::value;
|
|
case kdouble: return std::is_same<cl_double, T>::value;
|
|
case kulong: return std::is_same<cl_ulong, T>::value;
|
|
case klong: return std::is_same<cl_long, T>::value;
|
|
}
|
|
}
|
|
};
|
|
|
|
// Helper structures to iterate over all tuple attributes of different types
|
|
struct IterOverTypes : public TestType
|
|
{
|
|
IterOverTypes(const TypeIter &typeIter, ConversionsTest &test)
|
|
: inType((Type)0), outType((Type)0), typeIter(typeIter), test(test),
|
|
testNumber(-1), startMinVectorSize(gMinVectorSize)
|
|
{}
|
|
|
|
void Run() { for_each_out_elem(typeIter); }
|
|
|
|
protected:
|
|
template <std::size_t Out = 0, typename OutType>
|
|
void iterate_out_type(const OutType &t)
|
|
{
|
|
for_each_in_elem<0, Out, OutType>(typeIter);
|
|
outType = (Type)(outType + 1);
|
|
inType = (Type)0;
|
|
}
|
|
|
|
template <std::size_t In, std::size_t Out, typename OutType,
|
|
typename InType>
|
|
void iterate_in_type(const InType &t)
|
|
{
|
|
if (!testType<InType, isTypeFp[In]>(inType))
|
|
vlog_error("Unexpected data type!\n");
|
|
|
|
if (!testType<OutType, isTypeFp[Out]>(outType))
|
|
vlog_error("Unexpected data type!\n");
|
|
|
|
// run the conversions
|
|
test.TestTypesConversion<InType, OutType, isTypeFp[In], isTypeFp[Out]>(
|
|
inType, outType, testNumber, startMinVectorSize);
|
|
inType = (Type)(inType + 1);
|
|
}
|
|
|
|
template <std::size_t Out = 0, typename... Tp>
|
|
inline typename std::enable_if<Out == sizeof...(Tp), void>::type
|
|
for_each_out_elem(
|
|
const std::tuple<Tp...> &) // Unused arguments are given no names.
|
|
{}
|
|
|
|
template <std::size_t Out = 0, typename... Tp>
|
|
inline typename std::enable_if < Out<sizeof...(Tp), void>::type
|
|
for_each_out_elem(const std::tuple<Tp...> &t)
|
|
{
|
|
iterate_out_type<Out>(std::get<Out>(t));
|
|
for_each_out_elem<Out + 1, Tp...>(t);
|
|
}
|
|
|
|
template <std::size_t In = 0, std::size_t Out, typename OutType,
|
|
typename... Tp>
|
|
inline typename std::enable_if<In == sizeof...(Tp), void>::type
|
|
for_each_in_elem(
|
|
const std::tuple<Tp...> &) // Unused arguments are given no names.
|
|
{}
|
|
|
|
template <std::size_t In = 0, std::size_t Out, typename OutType,
|
|
typename... Tp>
|
|
inline typename std::enable_if < In<sizeof...(Tp), void>::type
|
|
for_each_in_elem(const std::tuple<Tp...> &t)
|
|
{
|
|
iterate_in_type<In, Out, OutType>(std::get<In>(t));
|
|
for_each_in_elem<In + 1, Out, OutType, Tp...>(t);
|
|
}
|
|
|
|
protected:
|
|
Type inType;
|
|
Type outType;
|
|
const TypeIter &typeIter;
|
|
ConversionsTest &test;
|
|
int testNumber;
|
|
int startMinVectorSize;
|
|
};
|
|
|
|
|
|
// Helper structures to select type 2 type conversion test case
|
|
struct IterOverSelectedTypes : public TestType
|
|
{
|
|
IterOverSelectedTypes(const TypeIter &typeIter, ConversionsTest &test,
|
|
const Type in, const Type out,
|
|
const RoundingMode round, const SaturationMode sat)
|
|
: inType(in), outType(out), rounding(round), saturation(sat),
|
|
typeIter(typeIter), test(test), testNumber(-1),
|
|
startMinVectorSize(gMinVectorSize)
|
|
{}
|
|
|
|
void Run() { for_each_out_elem(typeIter); }
|
|
|
|
protected:
|
|
template <std::size_t Out = 0, typename OutType>
|
|
void iterate_out_type(const OutType &t)
|
|
{
|
|
for_each_in_elem<0, Out, OutType>(typeIter);
|
|
}
|
|
|
|
template <std::size_t In, std::size_t Out, typename OutType,
|
|
typename InType>
|
|
void iterate_in_type(const InType &t)
|
|
{
|
|
if (testType<InType, isTypeFp[In]>(inType)
|
|
&& testType<OutType, isTypeFp[Out]>(outType))
|
|
{
|
|
// run selected conversion
|
|
// testing of the result will happen afterwards
|
|
test.DoTest<InType, OutType, isTypeFp[In], isTypeFp[Out]>(
|
|
outType, inType, saturation, rounding);
|
|
}
|
|
}
|
|
|
|
template <std::size_t Out = 0, typename... Tp>
|
|
inline typename std::enable_if<Out == sizeof...(Tp), void>::type
|
|
for_each_out_elem(const std::tuple<Tp...> &)
|
|
{}
|
|
|
|
template <std::size_t Out = 0, typename... Tp>
|
|
inline typename std::enable_if < Out<sizeof...(Tp), void>::type
|
|
for_each_out_elem(const std::tuple<Tp...> &t)
|
|
{
|
|
iterate_out_type<Out>(std::get<Out>(t));
|
|
for_each_out_elem<Out + 1, Tp...>(t);
|
|
}
|
|
|
|
template <std::size_t In = 0, std::size_t Out, typename OutType,
|
|
typename... Tp>
|
|
inline typename std::enable_if<In == sizeof...(Tp), void>::type
|
|
for_each_in_elem(const std::tuple<Tp...> &)
|
|
{}
|
|
|
|
template <std::size_t In = 0, std::size_t Out, typename OutType,
|
|
typename... Tp>
|
|
inline typename std::enable_if < In<sizeof...(Tp), void>::type
|
|
for_each_in_elem(const std::tuple<Tp...> &t)
|
|
{
|
|
iterate_in_type<In, Out, OutType>(std::get<In>(t));
|
|
for_each_in_elem<In + 1, Out, OutType, Tp...>(t);
|
|
}
|
|
|
|
protected:
|
|
Type inType;
|
|
Type outType;
|
|
RoundingMode rounding;
|
|
SaturationMode saturation;
|
|
|
|
const TypeIter &typeIter;
|
|
ConversionsTest &test;
|
|
int testNumber;
|
|
int startMinVectorSize;
|
|
};
|
|
|
|
|
|
#endif /* BASIC_TEST_CONVERSIONS_H */
|
|
|