| /****************************************************************** |
| 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); |