blob: ccd7431554a53ab43e989e9b1114debe860961be [file] [log] [blame]
/******************************************************************
Copyright (c) 2016 The Khronos Group Inc. All Rights Reserved.
This code is protected by copyright laws and contains material proprietary to the Khronos Group, Inc.
This is UNPUBLISHED PROPRIETARY SOURCE CODE that may not be disclosed h_in whole or h_in part to
third parties, and may not be reproduced, republished, distributed, transmitted, displayed,
broadcast or otherwise exploited h_in any manner without the express prior written permission
of Khronos Group. The receipt or possession of this code does not convey any rights to reproduce,
disclose, or distribute its contents, or to manufacture, use, or sell anything that it may describe,
h_in whole or h_in part other than under the terms of the Khronos Adopters Agreement
or Khronos Conformance Test Source License Agreement as executed between Khronos and the recipient.
******************************************************************/
#include "testBase.h"
#include "types.hpp"
#include <sstream>
#include <string>
#include <algorithm>
#include <limits>
#include <cmath>
#ifndef isnan
// Ensure isnan is always present as a macro
#define isnan std::isnan
#endif
long double reference_remainderl(long double x, long double y);
int gIsInRTZMode = 0;
int gDeviceILogb0 = 1;
int gDeviceILogbNaN = 1;
int gCheckTininessBeforeRounding = 1;
static int verify_results(cl_device_id deviceID,
cl_context context,
cl_command_queue queue,
const char *kname,
const clProgramWrapper &prog)
{
const int num = 1 << 20;
std::vector<cl_int> h_lhs(num);
std::vector<cl_int> h_rhs(num);
cl_int err = 0;
RandomSeed seed(gRandomSeed);
for (int i = 0; i < num; i++) {
h_lhs[i] = genrand<cl_int>(seed);
h_rhs[i] = genrand<cl_int>(seed);
}
clKernelWrapper kernel = clCreateKernel(prog, kname, &err);
SPIRV_CHECK_ERROR(err, "Failed to create spv kernel");
size_t bytes = sizeof(cl_int) * num;
clMemWrapper lhs = clCreateBuffer(context, CL_MEM_READ_ONLY, bytes, NULL, &err);
SPIRV_CHECK_ERROR(err, "Failed to create in buffer");
err = clEnqueueWriteBuffer(queue, lhs, CL_TRUE, 0, bytes, &h_lhs[0], 0, NULL, NULL);
SPIRV_CHECK_ERROR(err, "Failed to copy to in buffer");
clMemWrapper rhs = clCreateBuffer(context, CL_MEM_READ_ONLY, bytes, NULL, &err);
SPIRV_CHECK_ERROR(err, "Failed to create in buffer");
err = clEnqueueWriteBuffer(queue, rhs, CL_TRUE, 0, bytes, &h_rhs[0], 0, NULL, NULL);
SPIRV_CHECK_ERROR(err, "Failed to copy to in buffer");
clMemWrapper res = clCreateBuffer(context, CL_MEM_READ_WRITE, bytes, NULL, &err);
SPIRV_CHECK_ERROR(err, "Failed to create in buffer");
err = clSetKernelArg(kernel, 0, sizeof(cl_mem), &res);
SPIRV_CHECK_ERROR(err, "Failed to set arg 1");
err = clSetKernelArg(kernel, 1, sizeof(cl_mem), &lhs);
SPIRV_CHECK_ERROR(err, "Failed to set arg 2");
err = clSetKernelArg(kernel, 2, sizeof(cl_mem), &rhs);
SPIRV_CHECK_ERROR(err, "Failed to set arg 3");
size_t global = num;
err = clEnqueueNDRangeKernel(queue, kernel, 1, NULL, &global, NULL, 0, NULL, NULL);
SPIRV_CHECK_ERROR(err, "Failed to enqueue cl kernel");
std::vector<cl_int> h_res(num);
err = clEnqueueReadBuffer(queue, res, CL_TRUE, 0, bytes, &h_res[0], 0, NULL, NULL);
SPIRV_CHECK_ERROR(err, "Failed to read to output");
for (int i = 0; i < num; i++) {
if (h_res[i] != (h_lhs[i] + h_rhs[i])) {
log_error("Values do not match at location %d\n", i);
return -1;
}
}
return 0;
}
int test_decorate_full(cl_device_id deviceID,
cl_context context,
cl_command_queue queue,
const char *name)
{
clProgramWrapper prog;
cl_int err = 0;
err = get_program_with_il(prog, deviceID, context, name);
SPIRV_CHECK_ERROR(err, "Failed to build program");
return verify_results(deviceID, context, queue, name, prog);
}
TEST_SPIRV_FUNC(decorate_restrict)
{
return test_decorate_full(deviceID, context, queue, "decorate_restrict");
}
TEST_SPIRV_FUNC(decorate_aliased)
{
return test_decorate_full(deviceID, context, queue, "decorate_aliased");
}
TEST_SPIRV_FUNC(decorate_alignment)
{
//TODO: Check for results ? How to ensure buffers are aligned
clProgramWrapper prog;
return get_program_with_il(prog, deviceID, context, "decorate_alignment");
}
TEST_SPIRV_FUNC(decorate_constant)
{
return test_decorate_full(deviceID, context, queue, "decorate_constant");
}
TEST_SPIRV_FUNC(decorate_cpacked)
{
PACKED(
struct packed_struct_t {
cl_int ival;
cl_char cval;
}
);
typedef struct packed_struct_t packed_t;
const int num = 1 << 20;
std::vector<packed_t> packed(num);
clProgramWrapper prog;
cl_int err = get_program_with_il(prog, deviceID, context, "decorate_cpacked");
clKernelWrapper kernel = clCreateKernel(prog, "decorate_cpacked", &err);
SPIRV_CHECK_ERROR(err, "Failed to create spv kernel");
size_t bytes = sizeof(packed_t) * num;
clMemWrapper res = clCreateBuffer(context, CL_MEM_READ_WRITE, bytes, NULL, &err);
SPIRV_CHECK_ERROR(err, "Failed to create in buffer");
err = clSetKernelArg(kernel, 0, sizeof(cl_mem), &res);
SPIRV_CHECK_ERROR(err, "Failed to set arg 3");
size_t global = num;
err = clEnqueueNDRangeKernel(queue, kernel, 1, NULL, &global, NULL, 0, NULL, NULL);
SPIRV_CHECK_ERROR(err, "Failed to enqueue cl kernel");
std::vector<packed_t> h_res(num);
err = clEnqueueReadBuffer(queue, res, CL_TRUE, 0, bytes, &h_res[0], 0, NULL, NULL);
SPIRV_CHECK_ERROR(err, "Failed to read to output");
for (int i = 0; i < num; i++) {
if (h_res[i].ival != 2100483600 ||
h_res[i].cval != 127) {
log_error("Values do not match at location %d\n", i);
return -1;
}
}
return 0;
}
template<typename Ti, typename Tl, typename To>
int verify_saturated_results(cl_device_id deviceID,
cl_context context,
cl_command_queue queue,
const char *kname,
const clProgramWrapper &prog)
{
if(std::string(kname).find("double") != std::string::npos) {
if(!is_extension_available(deviceID, "cl_khr_fp64")) {
log_info("Extension cl_khr_fp64 not supported; skipping double tests.\n");
return 0;
}
}
cl_int err = 0;
const int num = 1 << 20;
clKernelWrapper kernel = clCreateKernel(prog, kname, &err);
SPIRV_CHECK_ERROR(err, "Failed to create spv kernel");
size_t in_bytes = sizeof(Ti) * num;
size_t out_bytes = sizeof(To) * num;
std::vector<Ti> h_lhs(num);
std::vector<Ti> h_rhs(num);
To loVal = std::numeric_limits<To>::min();
To hiVal = std::numeric_limits<To>::max();
Tl range = (Tl)(hiVal) - (Tl)(loVal);
RandomSeed seed(gRandomSeed);
for (int i = 0; i < num; i++) {
h_lhs[i] = genrand<Ti>(seed) * range;
Tl val = (genrand<Tl>(seed) % hiVal);
// randomly set some values on rhs to NaN
if (val * 20 < hiVal) {
h_rhs[i] = NAN;
} else {
h_rhs[i] = (Ti)(val);
}
}
clMemWrapper lhs = clCreateBuffer(context, CL_MEM_READ_ONLY, in_bytes, NULL, &err);
SPIRV_CHECK_ERROR(err, "Failed to create in buffer");
err = clEnqueueWriteBuffer(queue, lhs, CL_TRUE, 0, in_bytes, &h_lhs[0], 0, NULL, NULL);
SPIRV_CHECK_ERROR(err, "Failed to copy to in buffer");
clMemWrapper rhs = clCreateBuffer(context, CL_MEM_READ_ONLY, in_bytes, NULL, &err);
SPIRV_CHECK_ERROR(err, "Failed to create in buffer");
err = clEnqueueWriteBuffer(queue, rhs, CL_TRUE, 0, in_bytes, &h_rhs[0], 0, NULL, NULL);
SPIRV_CHECK_ERROR(err, "Failed to copy to in buffer");
clMemWrapper res = clCreateBuffer(context, CL_MEM_READ_WRITE, out_bytes, NULL, &err);
SPIRV_CHECK_ERROR(err, "Failed to create in buffer");
err = clSetKernelArg(kernel, 0, sizeof(cl_mem), &res);
SPIRV_CHECK_ERROR(err, "Failed to set arg 1");
err = clSetKernelArg(kernel, 1, sizeof(cl_mem), &lhs);
SPIRV_CHECK_ERROR(err, "Failed to set arg 2");
err = clSetKernelArg(kernel, 2, sizeof(cl_mem), &rhs);
SPIRV_CHECK_ERROR(err, "Failed to set arg 3");
size_t global = num;
err = clEnqueueNDRangeKernel(queue, kernel, 1, NULL, &global, NULL, 0, NULL, NULL);
SPIRV_CHECK_ERROR(err, "Failed to enqueue cl kernel");
std::vector<To> h_res(num);
err = clEnqueueReadBuffer(queue, res, CL_TRUE, 0, out_bytes, &h_res[0], 0, NULL, NULL);
SPIRV_CHECK_ERROR(err, "Failed to read to output");
for (int i = 0; i < num; i++) {
Tl ival = (Tl)(h_lhs[i] * h_rhs[i]);
To val = (To)std::min<Ti>(std::max<Ti>(ival, loVal), hiVal);
if (isnan(h_rhs[i])) {
val = 0;
}
if (val != h_res[i]) {
log_error("Value error at %d\n", i);
return -1;
}
}
return 0;
}
template<typename Ti, typename Tl, typename To>
int test_saturate_full(cl_device_id deviceID,
cl_context context,
cl_command_queue queue,
const char *name,
const char *types)
{
if(std::string(types).find("double") != std::string::npos) {
if(!is_extension_available(deviceID, "cl_khr_fp64")) {
log_info("Extension cl_khr_fp64 not supported; skipping double tests.\n");
return 0;
}
}
clProgramWrapper prog;
cl_int err = 0;
err = get_program_with_il(prog, deviceID, context, name);
SPIRV_CHECK_ERROR(err, "Failed to build program");
return verify_saturated_results<Ti, Tl, To>(deviceID, context, queue, name, prog);
}
#define TEST_SATURATED_CONVERSION(Ti, Tl, To) \
TEST_SPIRV_FUNC(decorate_saturated_conversion_##To) \
{ \
typedef cl_##Ti cl_Ti; \
typedef cl_##Tl cl_Tl; \
typedef cl_##To cl_To; \
return test_saturate_full<cl_Ti, cl_Tl, cl_To> \
(deviceID, context, queue, \
"decorate_saturated_conversion_" #To, \
#Ti #Tl #To); \
} \
TEST_SATURATED_CONVERSION(float, int, char)
TEST_SATURATED_CONVERSION(float, uint, uchar)
TEST_SATURATED_CONVERSION(float, int, short)
TEST_SATURATED_CONVERSION(float, uint, ushort)
TEST_SATURATED_CONVERSION(double, long, int)
TEST_SATURATED_CONVERSION(double, ulong, uint)
template<typename Ti, typename To>
int test_fp_rounding(cl_device_id deviceID,
cl_context context,
cl_command_queue queue,
const char *name,
std::vector<Ti> &h_in,
std::vector<To> &h_out)
{
if(std::string(name).find("double") != std::string::npos) {
if(!is_extension_available(deviceID, "cl_khr_fp64")) {
log_info("Extension cl_khr_fp64 not supported; skipping double tests.\n");
return 0;
}
}
const int num = h_in.size();
const size_t in_bytes = num * sizeof(Ti);
const size_t out_bytes = num * sizeof(To);
cl_int err = 0;
clMemWrapper in = clCreateBuffer(context, CL_MEM_READ_ONLY, in_bytes, NULL, &err);
SPIRV_CHECK_ERROR(err, "Failed to create input buffer");
clMemWrapper out = clCreateBuffer(context, CL_MEM_READ_ONLY, out_bytes, NULL, &err);
SPIRV_CHECK_ERROR(err, "Failed to create output buffer");
err = clEnqueueWriteBuffer(queue, in, CL_TRUE, 0, in_bytes, &h_in[0], 0, NULL, NULL);
SPIRV_CHECK_ERROR(err, "Failed to write to input");
clProgramWrapper prog;
err = get_program_with_il(prog, deviceID, context, name);
SPIRV_CHECK_ERROR(err, "Failed to build program");
clKernelWrapper kernel = clCreateKernel(prog, name, &err);
SPIRV_CHECK_ERROR(err, "Failed to create spv kernel");
err = clSetKernelArg(kernel, 0, sizeof(cl_mem), &out);
SPIRV_CHECK_ERROR(err, "Failed to set arg 2");
err = clSetKernelArg(kernel, 1, sizeof(cl_mem), &in);
SPIRV_CHECK_ERROR(err, "Failed to set arg 1");
size_t global = num;
err = clEnqueueNDRangeKernel(queue, kernel, 1, NULL, &global, NULL, 0, NULL, NULL);
std::vector<To> h_res(num);
err = clEnqueueReadBuffer(queue, out, CL_TRUE, 0, out_bytes, &h_res[0], 0, NULL, NULL);
SPIRV_CHECK_ERROR(err, "Failed to read from output");
for (int i = 0; i < num; i++) {
if (h_res[i] != h_out[i]) {
log_error("Values do not match at location %d. Original :%lf, Expected: %ld, Found %ld\n",
i, h_in[i], h_out[i], h_res[i]);
return -1;
}
}
return 0;
}
template<typename Ti, typename To>
inline To round_to_zero(Ti in)
{
To out = (To)(in);
return out;
}
template<typename T>
int sign(T val)
{
if (val < 0) return -1;
if (val > 0) return 1;
return 0;
}
template<typename Ti, typename To>
inline To round_to_even(Ti in)
{
// https://en.wikipedia.org/wiki/Rounding#Round_half_to_even
return std::floor(in + 0.5) - 1 + std::abs(sign(reference_remainderl((long double)in, 2) - 0.5));
}
template<typename Ti, typename To>
inline To round_to_posinf(Ti in)
{
To out = std::ceil(in);
return out;
}
template<typename Ti, typename To>
inline To round_to_neginf(Ti in)
{
To out = std::floor(in);
return out;
}
#define TEST_SPIRV_FP_ROUNDING_DECORATE(name, func, Ti, To) \
TEST_SPIRV_FUNC(decorate_fp_rounding_mode_##name##_##Ti##_##To) \
{ \
typedef cl_##Ti clTi; \
typedef cl_##To clTo; \
const int num = 1 << 16; \
std::vector<clTi> in(num); \
std::vector<clTo> out(num); \
RandomSeed seed(gRandomSeed); \
\
for (int i = 0; i < num; i++) { \
in[i] = num * genrand<clTi>(seed) - num/2; \
out[i] = func<clTi, clTo>(in[i]); \
} \
const char *name = "decorate_rounding_" #name "_" #Ti "_" #To; \
return test_fp_rounding(deviceID, context, queue, \
name, in, out); \
} \
TEST_SPIRV_FP_ROUNDING_DECORATE(rte, round_to_even, float, int);
TEST_SPIRV_FP_ROUNDING_DECORATE(rtz, round_to_zero, float, int);
TEST_SPIRV_FP_ROUNDING_DECORATE(rtp, round_to_posinf, float, int);
TEST_SPIRV_FP_ROUNDING_DECORATE(rtn, round_to_neginf, float, int);
TEST_SPIRV_FP_ROUNDING_DECORATE(rte, round_to_even, double, long);
TEST_SPIRV_FP_ROUNDING_DECORATE(rtz, round_to_zero, double, long);
TEST_SPIRV_FP_ROUNDING_DECORATE(rtp, round_to_posinf, double, long);
TEST_SPIRV_FP_ROUNDING_DECORATE(rtn, round_to_neginf, double, long);