// // 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. // #ifndef TEST_CONFORMANCE_CLCPP_ATTRIBUTES_TEST_IVDEP_HPP #define TEST_CONFORMANCE_CLCPP_ATTRIBUTES_TEST_IVDEP_HPP #include #include #include #include // Common for all OpenCL C++ tests #include "../common.hpp" namespace test_ivdep { enum class loop_kind { for_loop, while_loop, do_loop }; struct test_options { loop_kind loop; int ivdep_length; int offset1; int offset2; int iter_count; bool offset1_param; bool offset2_param; bool iter_count_param; bool cond_in_header; bool init_in_header; bool incr_in_header; }; // ----------------------------------------------------------------------------------- // ------------- ONLY FOR OPENCL 22 CONFORMANCE TEST 22 DEVELOPMENT ------------------ // ----------------------------------------------------------------------------------- #if defined(DEVELOPMENT) && defined(USE_OPENCLC_KERNELS) std::string generate_source(test_options options) { std::string offset1s = options.offset1_param ? "offset1" : std::to_string(options.offset1); std::string offset2s = options.offset2_param ? "offset2" : std::to_string(options.offset2); std::string init = "i = 0"; std::string cond = std::string("i < ") + (options.iter_count_param ? "iter_count" : std::to_string(options.iter_count)); std::string incr = "i += 2"; std::stringstream s; s << R"( kernel void test(global int *a, global int *b, global int *c, int offset1, int offset2, int iter_count) { int i; )"; // Loop #1 if (!options.init_in_header) s << init << ";" << std::endl; if (options.loop == loop_kind::for_loop) s << "for (" << (options.init_in_header ? init : "") << ";" << (options.cond_in_header ? cond : "") << ";" << (options.incr_in_header ? incr : "") << ")"; else if (options.loop == loop_kind::while_loop) s << "while (" << (options.cond_in_header ? cond : "true") << ")"; else if (options.loop == loop_kind::do_loop) s << "do"; s << "{" << std::endl; if (!options.cond_in_header) s << "if (!(" << cond << ")) break;" << std::endl; s << "a[i + " << offset1s << "] = b[i + " << offset1s << "] * c[i + " << offset1s << "];" << std::endl; if (!options.incr_in_header) s << incr << ";" << std::endl; s << "}" << std::endl; if (options.loop == loop_kind::do_loop) s << "while (" << (options.cond_in_header ? cond : "true") << ");" << std::endl; // Loop #2 if (!options.init_in_header) s << init << ";" << std::endl; if (options.loop == loop_kind::for_loop) s << "for (" << (options.init_in_header ? init : "") << ";" << (options.cond_in_header ? cond : "") << ";" << (options.incr_in_header ? incr : "") << ")"; else if (options.loop == loop_kind::while_loop) s << "while (" << (options.cond_in_header ? cond : "true") << ")"; else if (options.loop == loop_kind::do_loop) s << "do"; s << "{" << std::endl; if (!options.cond_in_header) s << "if (!(" << cond << ")) break;" << std::endl; s << "a[i + " << offset2s << "] = a[i] + b[i];" << std::endl; if (!options.incr_in_header) s << incr << ";" << std::endl; s << "}" << std::endl; if (options.loop == loop_kind::do_loop) s << "while (" << (options.cond_in_header ? cond : "true") << ");" << std::endl; s << "}" << std::endl; return s.str(); } #else std::string generate_source(test_options options) { std::string offset1s = options.offset1_param ? "offset1" : std::to_string(options.offset1); std::string offset2s = options.offset2_param ? "offset2" : std::to_string(options.offset2); std::string init = "i = 0"; std::string cond = std::string("i < ") + (options.iter_count_param ? "iter_count" : std::to_string(options.iter_count)); std::string incr = "i += 2"; std::stringstream s; s << R"( #include #include using namespace cl; )"; s << R"( kernel void test(global_ptr a, global_ptr b, global_ptr c, int offset1, int offset2, int iter_count) { int i; )"; // Loop #1 if (!options.init_in_header) s << init << ";" << std::endl; if (options.ivdep_length > 0) s << "[[cl::ivdep]]" << std::endl; if (options.loop == loop_kind::for_loop) s << "for (" << (options.init_in_header ? init : "") << ";" << (options.cond_in_header ? cond : "") << ";" << (options.incr_in_header ? incr : "") << ")"; else if (options.loop == loop_kind::while_loop) s << "while (" << (options.cond_in_header ? cond : "true") << ")"; else if (options.loop == loop_kind::do_loop) s << "do"; s << "{" << std::endl; if (!options.cond_in_header) s << "if (!(" << cond << ")) break;" << std::endl; s << "a[i + " << offset1s << "] = b[i + " << offset1s << "] * c[i + " << offset1s << "];" << std::endl; if (!options.incr_in_header) s << incr << ";" << std::endl; s << "}" << std::endl; if (options.loop == loop_kind::do_loop) s << "while (" << (options.cond_in_header ? cond : "true") << ");" << std::endl; // Loop #2 if (!options.init_in_header) s << init << ";" << std::endl; if (options.ivdep_length > 0) s << "[[cl::ivdep(" << options.ivdep_length << ")]]" << std::endl; if (options.loop == loop_kind::for_loop) s << "for (" << (options.init_in_header ? init : "") << ";" << (options.cond_in_header ? cond : "") << ";" << (options.incr_in_header ? incr : "") << ")"; else if (options.loop == loop_kind::while_loop) s << "while (" << (options.cond_in_header ? cond : "true") << ")"; else if (options.loop == loop_kind::do_loop) s << "do"; s << "{" << std::endl; if (!options.cond_in_header) s << "if (!(" << cond << ")) break;" << std::endl; s << "a[i + " << offset2s << "] = a[i] + b[i];" << std::endl; if (!options.incr_in_header) s << incr << ";" << std::endl; s << "}" << std::endl; if (options.loop == loop_kind::do_loop) s << "while (" << (options.cond_in_header ? cond : "true") << ");" << std::endl; s << "}" << std::endl; return s.str(); } #endif int test(cl_device_id device, cl_context context, cl_command_queue queue, test_options options) { int error = CL_SUCCESS; cl_program program; cl_kernel kernel; std::string kernel_name = "test"; std::string source = generate_source(options); // ----------------------------------------------------------------------------------- // ------------- ONLY FOR OPENCL 22 CONFORMANCE TEST 22 DEVELOPMENT ------------------ // ----------------------------------------------------------------------------------- // Only OpenCL C++ to SPIR-V compilation #if defined(DEVELOPMENT) && defined(ONLY_SPIRV_COMPILATION) error = create_opencl_kernel( context, &program, &kernel, source, kernel_name ); RETURN_ON_ERROR(error) return error; // Use OpenCL C kernels instead of OpenCL C++ kernels (test C++ host code) #elif defined(DEVELOPMENT) && defined(USE_OPENCLC_KERNELS) error = create_opencl_kernel( context, &program, &kernel, source, kernel_name, "", false ); RETURN_ON_ERROR(error) // Normal run #else error = create_opencl_kernel( context, &program, &kernel, source, kernel_name ); RETURN_ON_ERROR(error) #endif const size_t count = 100; const size_t global_size = 1; std::vector a(count); std::vector b(count); std::vector c(count); for (size_t i = 0; i < count; i++) { a[i] = 0; b[i] = i; c[i] = 1; } cl_mem a_buffer; a_buffer = clCreateBuffer(context, CL_MEM_READ_WRITE | CL_MEM_COPY_HOST_PTR, sizeof(int) * count, static_cast(a.data()), &error ); RETURN_ON_CL_ERROR(error, "clCreateBuffer") cl_mem b_buffer; b_buffer = clCreateBuffer(context, CL_MEM_READ_WRITE | CL_MEM_COPY_HOST_PTR, sizeof(int) * count, static_cast(b.data()), &error ); RETURN_ON_CL_ERROR(error, "clCreateBuffer") cl_mem c_buffer; c_buffer = clCreateBuffer(context, CL_MEM_READ_WRITE | CL_MEM_COPY_HOST_PTR, sizeof(int) * count, static_cast(c.data()),&error ); RETURN_ON_CL_ERROR(error, "clCreateBuffer") error = clSetKernelArg(kernel, 0, sizeof(cl_mem), &a_buffer); RETURN_ON_CL_ERROR(error, "clSetKernelArg") error = clSetKernelArg(kernel, 1, sizeof(cl_mem), &b_buffer); RETURN_ON_CL_ERROR(error, "clSetKernelArg") error = clSetKernelArg(kernel, 2, sizeof(cl_mem), &c_buffer); RETURN_ON_CL_ERROR(error, "clSetKernelArg") error = clSetKernelArg(kernel, 3, sizeof(cl_int), &options.offset1); RETURN_ON_CL_ERROR(error, "clSetKernelArg") error = clSetKernelArg(kernel, 4, sizeof(cl_int), &options.offset2); RETURN_ON_CL_ERROR(error, "clSetKernelArg") error = clSetKernelArg(kernel, 5, sizeof(cl_int), &options.iter_count); RETURN_ON_CL_ERROR(error, "clSetKernelArg") error = clEnqueueNDRangeKernel(queue, kernel, 1, NULL, &global_size, NULL, 0, NULL, NULL); RETURN_ON_CL_ERROR(error, "clEnqueueNDRangeKernel") std::vector a_output(count); error = clEnqueueReadBuffer( queue, a_buffer, CL_TRUE, 0, sizeof(int) * count, static_cast(a_output.data()), 0, NULL, NULL ); RETURN_ON_CL_ERROR(error, "clEnqueueReadBuffer") for (int i = 0; i < options.iter_count; i += 2) { a[i + options.offset1] = b[i + options.offset1] * c[i + options.offset1]; } for (int i = 0; i < options.iter_count; i += 2) { a[i + options.offset2] = a[i] + b[i]; } for (size_t i = 0; i < count; i++) { const int value = a_output[i]; const int expected = a[i]; if (value != expected) { RETURN_ON_ERROR_MSG(-1, "Test failed. Element %lu: %d should be: %d", i, value, expected ); } } clReleaseMemObject(a_buffer); clReleaseMemObject(b_buffer); clReleaseMemObject(c_buffer); clReleaseKernel(kernel); clReleaseProgram(program); return error; } const std::vector> params{ std::make_tuple( -1, 0, 0 ), std::make_tuple( -1, 3, 4 ), std::make_tuple( 1, 1, 1 ), std::make_tuple( 3, 4, 2 ), std::make_tuple( 3, 4, 3 ), std::make_tuple( 8, 10, 7 ), std::make_tuple( 16, 16, 16 ) }; const std::vector iter_counts{ { 1, 4, 12, 40 } }; AUTO_TEST_CASE(test_ivdep_for) (cl_device_id device, cl_context context, cl_command_queue queue, int num_elements) { int error = CL_SUCCESS; for (auto param : params) for (auto iter_count : iter_counts) for (bool offset1_param : { false, true }) for (bool offset2_param : { false, true }) for (bool iter_count_param : { false, true }) for (bool cond_in_header : { false, true }) for (bool init_in_header : { false, true }) for (bool incr_in_header : { false, true }) { test_options options; options.loop = loop_kind::for_loop; options.ivdep_length = std::get<0>(param); options.offset1 = std::get<1>(param); options.offset2 = std::get<2>(param); options.iter_count = iter_count; options.offset1_param = offset1_param; options.offset2_param = offset2_param; options.iter_count_param = iter_count_param; options.cond_in_header = cond_in_header; options.init_in_header = init_in_header; options.incr_in_header = incr_in_header; error = test(device, context, queue, options); RETURN_ON_ERROR(error) } return error; } AUTO_TEST_CASE(test_ivdep_while) (cl_device_id device, cl_context context, cl_command_queue queue, int num_elements) { int error = CL_SUCCESS; for (auto param : params) for (auto iter_count : iter_counts) for (bool offset1_param : { false, true }) for (bool offset2_param : { false, true }) for (bool iter_count_param : { false, true }) for (bool cond_in_header : { false, true }) { test_options options; options.loop = loop_kind::while_loop; options.ivdep_length = std::get<0>(param); options.offset1 = std::get<1>(param); options.offset2 = std::get<2>(param); options.iter_count = iter_count; options.offset1_param = offset1_param; options.offset2_param = offset2_param; options.iter_count_param = iter_count_param; options.cond_in_header = cond_in_header; options.init_in_header = false; options.incr_in_header = false; error = test(device, context, queue, options); RETURN_ON_ERROR(error) } return error; } AUTO_TEST_CASE(test_ivdep_do) (cl_device_id device, cl_context context, cl_command_queue queue, int num_elements) { int error = CL_SUCCESS; for (auto param : params) for (auto iter_count : iter_counts) for (bool offset1_param : { false, true }) for (bool offset2_param : { false, true }) for (bool iter_count_param : { false, true }) for (bool cond_in_header : { false, true }) { test_options options; options.loop = loop_kind::do_loop; options.ivdep_length = std::get<0>(param); options.offset1 = std::get<1>(param); options.offset2 = std::get<2>(param); options.iter_count = iter_count; options.offset1_param = offset1_param; options.offset2_param = offset2_param; options.iter_count_param = iter_count_param; options.cond_in_header = cond_in_header; options.init_in_header = false; options.incr_in_header = false; error = test(device, context, queue, options); RETURN_ON_ERROR(error) } return error; } } // namespace #endif // TEST_CONFORMANCE_CLCPP_ATTRIBUTES_TEST_IVDEP_HPP