xref: /aosp_15_r20/external/OpenCL-CTS/test_conformance/spir/run_build_test.cpp (revision 6467f958c7de8070b317fc65bcb0f6472e388d82)
1 //
2 // Copyright (c) 2017 The Khronos Group Inc.
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //    http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16 #include "harness/compat.h"
17 
18 #ifdef __APPLE__
19 #include <OpenCL/opencl.h>
20 #else
21 #include <CL/cl.h>
22 #endif
23 
24 #include <sstream>
25 #include <fstream>
26 #include <assert.h>
27 #include <functional>
28 #include <memory>
29 
30 #include "harness/errorHelpers.h"
31 #include "harness/kernelHelpers.h"
32 #include "harness/typeWrappers.h"
33 #include "harness/clImageHelper.h"
34 #include "harness/os_helpers.h"
35 
36 #include "../math_brute_force/function_list.h"
37 #include "datagen.h"
38 #include "exceptions.h"
39 #include "kernelargs.h"
40 #include "run_build_test.h"
41 #include "run_services.h"
42 #include <CL/cl.h>
43 //
44 // Task
45 //
Task(cl_device_id device,const char * options)46 Task::Task(cl_device_id device, const char* options):
47 m_devid(device) {
48   if (options)
49     m_options = options;
50 }
51 
~Task()52 Task::~Task() {}
53 
getErrorLog() const54 const char* Task::getErrorLog() const {
55   return m_log.c_str();
56 }
57 
setErrorLog(cl_program prog)58 void Task::setErrorLog(cl_program prog) {
59     size_t len = 0;
60     std::vector<char> log;
61 
62     cl_int err_code = clGetProgramBuildInfo(prog, m_devid, CL_PROGRAM_BUILD_LOG, 0, NULL, &len);
63     if(err_code != CL_SUCCESS)
64     {
65         m_log = "Error: clGetProgramBuildInfo(CL_PROGRAM_BUILD_LOG, &len) failed.\n";
66         return;
67     }
68 
69     log.resize(len, 0);
70 
71     err_code = clGetProgramBuildInfo(prog, m_devid, CL_PROGRAM_BUILD_LOG, len, &log[0], NULL);
72     if(err_code != CL_SUCCESS)
73     {
74         m_log = "Error: clGetProgramBuildInfo(CL_PROGRAM_BUILD_LOG, &log) failed.\n";
75         return;
76     }
77     m_log.append(&log[0]);
78 }
79 
80 //
81 // BuildTask
82 //
BuildTask(cl_program prog,cl_device_id dev,const char * options)83 BuildTask::BuildTask(cl_program prog, cl_device_id dev, const char* options)
84     : Task(dev, options), m_program(prog)
85 {}
86 
execute()87 bool BuildTask::execute() {
88     cl_int err_code = clBuildProgram(m_program, 0, NULL, m_options.c_str(), NULL, NULL);
89     if(CL_SUCCESS == err_code)
90         return true;
91 
92     setErrorLog(m_program);
93     return false;
94 }
95 
96 //
97 // SpirBuildTask
98 //
SpirBuildTask(cl_program prog,cl_device_id dev,const char * options)99 SpirBuildTask::SpirBuildTask(cl_program prog, cl_device_id dev, const char* options) :
100     BuildTask(prog, dev, options) {}
101 
102 //
103 // CompileTask
104 //
105 
CompileTask(cl_program prog,cl_device_id dev,const char * options)106 CompileTask::CompileTask(cl_program prog, cl_device_id dev, const char* options)
107     : Task(dev, options), m_program(prog)
108 {}
109 
addHeader(const char * hname,cl_program hprog)110 void CompileTask::addHeader(const char* hname, cl_program hprog) {
111     m_headers.push_back(std::make_pair(hname, hprog));
112 }
113 
first(std::pair<const char *,cl_program> & p)114 const char* first(std::pair<const char*,cl_program>& p) {
115     return p.first;
116 }
117 
second(const std::pair<const char *,cl_program> & p)118 cl_program second(const std::pair<const char*, cl_program>& p) {
119     return p.second;
120 }
121 
execute()122 bool CompileTask::execute() {
123     // Generating the header names vector.
124     std::vector<const char*> names;
125     std::transform(m_headers.begin(), m_headers.end(), names.begin(), first);
126 
127     // Generating the header programs vector.
128     std::vector<cl_program> programs;
129     std::transform(m_headers.begin(), m_headers.end(), programs.begin(), second);
130 
131     const char** h_names = NULL;
132     const cl_program* h_programs = NULL;
133     if (!m_headers.empty())
134     {
135         h_programs = &programs[0];
136         h_names    = &names[0];
137     }
138 
139     // Compiling with the headers.
140     cl_int err_code = clCompileProgram(
141         m_program,
142         1U,
143         &m_devid,
144         m_options.c_str(),
145         m_headers.size(), // # of headers
146         h_programs,
147         h_names,
148         NULL, NULL);
149     if (CL_SUCCESS == err_code)
150         return true;
151 
152     setErrorLog(m_program);
153     return false;
154 }
155 
156 //
157 // SpirCompileTask
158 //
SpirCompileTask(cl_program prog,cl_device_id dev,const char * options)159 SpirCompileTask::SpirCompileTask(cl_program prog, cl_device_id dev, const char* options) :
160     CompileTask(prog, dev, options) {}
161 
162 
163 //
164 // LinkTask
165 //
LinkTask(cl_program * programs,int num_programs,cl_context ctxt,cl_device_id dev,const char * options)166 LinkTask::LinkTask(cl_program* programs, int num_programs, cl_context ctxt,
167                    cl_device_id dev, const char* options)
168     : Task(dev, options), m_executable(NULL), m_programs(programs),
169       m_numPrograms(num_programs), m_context(ctxt)
170 {}
171 
execute()172 bool LinkTask::execute() {
173     cl_int err_code;
174     int i;
175 
176     for(i = 0; i < m_numPrograms; ++i)
177     {
178         err_code = clCompileProgram(m_programs[i], 1, &m_devid, "-x spir -spir-std=1.2 -cl-kernel-arg-info", 0, NULL, NULL, NULL, NULL);
179         if (CL_SUCCESS != err_code)
180         {
181             setErrorLog(m_programs[i]);
182             return false;
183         }
184     }
185 
186     m_executable = clLinkProgram(m_context, 1, &m_devid, m_options.c_str(), m_numPrograms, m_programs, NULL, NULL, &err_code);
187     if (CL_SUCCESS == err_code)
188       return true;
189 
190     if(m_executable) setErrorLog(m_executable);
191     return false;
192 }
193 
getExecutable() const194 cl_program LinkTask::getExecutable() const {
195     return m_executable;
196 }
197 
~LinkTask()198 LinkTask::~LinkTask() {
199     if(m_executable) clReleaseProgram(m_executable);
200 }
201 
202 //
203 // KernelEnumerator
204 //
process(cl_program prog)205 void KernelEnumerator::process(cl_program prog) {
206     const size_t MAX_KERNEL_NAME = 64;
207     size_t num_kernels;
208 
209     cl_int err_code = clGetProgramInfo(
210         prog,
211         CL_PROGRAM_NUM_KERNELS,
212         sizeof(size_t),
213         &num_kernels,
214         NULL
215     );
216     if (CL_SUCCESS != err_code)
217         return;
218 
219     // Querying for the number of kernels.
220     size_t buffer_len = sizeof(char)*num_kernels*MAX_KERNEL_NAME;
221     char* kernel_names = new char[buffer_len];
222     memset(kernel_names, '\0', buffer_len);
223     size_t str_len = 0;
224     err_code = clGetProgramInfo(
225         prog,
226         CL_PROGRAM_KERNEL_NAMES,
227         buffer_len,
228         (void *)kernel_names,
229         &str_len
230     );
231     if (CL_SUCCESS != err_code)
232         return;
233 
234     //parsing the names and inserting them to the list
235     std::string names(kernel_names);
236     assert (str_len == 1+names.size() && "incompatible string lengths");
237     size_t offset = 0;
238     for(size_t i=0 ; i<names.size() ; ++i){
239         //kernel names are separated by semi colons
240         if (names[i] == ';'){
241             m_kernels.push_back(names.substr(offset, i-offset));
242             offset = i+1;
243         }
244     }
245     m_kernels.push_back(names.substr(offset, names.size()-offset));
246     delete[] kernel_names;
247 }
248 
KernelEnumerator(cl_program prog)249 KernelEnumerator::KernelEnumerator(cl_program prog) {
250     process(prog);
251 }
252 
begin()253 KernelEnumerator::iterator KernelEnumerator::begin(){
254     return m_kernels.begin();
255 }
256 
end()257 KernelEnumerator::iterator KernelEnumerator::end(){
258     return m_kernels.end();
259 }
260 
size() const261 size_t KernelEnumerator::size() const {
262     return m_kernels.size();
263 }
264 
265 /**
266  Run the single test - run the test for both CL and SPIR versions of the kernel
267  */
run_test(cl_context context,cl_command_queue queue,cl_program clprog,cl_program bcprog,const std::string & kernel_name,std::string & err,const cl_device_id device,float ulps)268 static bool run_test(cl_context context, cl_command_queue queue, cl_program clprog,
269     cl_program bcprog, const std::string& kernel_name, std::string& err, const cl_device_id device,
270     float ulps)
271 {
272     WorkSizeInfo ws;
273     TestResult cl_result;
274     std::unique_ptr<TestResult> bc_result;
275     // first, run the single CL test
276     {
277         // make sure that the kernel will be released before the program
278         clKernelWrapper kernel = create_kernel_helper(clprog, kernel_name);
279         // based on the kernel characteristics, we are generating and initializing the arguments for both phases (cl and bc executions)
280         generate_kernel_data(context, kernel, ws, cl_result);
281         bc_result.reset(cl_result.clone(context, ws, kernel, device));
282         assert (compare_results(cl_result, *bc_result, ulps) && "not equal?");
283         run_kernel( kernel, queue, ws, cl_result );
284     }
285     // now, run the single BC test
286     {
287         // make sure that the kernel will be released before the program
288         clKernelWrapper kernel = create_kernel_helper(bcprog, kernel_name);
289         run_kernel( kernel, queue, ws, *bc_result );
290     }
291 
292     int error = clFinish(queue);
293     if( CL_SUCCESS != error)
294     {
295         err = "clFinish failed\n";
296         return false;
297     }
298 
299     // compare the results
300     if( !compare_results(cl_result, *bc_result, ulps) )
301     {
302         err = " (result diff in kernel '" + kernel_name + "').";
303         return false;
304     }
305     return true;
306 }
307 
308 /**
309  Get the maximum relative error defined as ULP of floating-point math functions
310  */
get_max_ulps(const char * test_name)311 static float get_max_ulps(const char *test_name)
312 {
313     float ulps = 0.f;
314     // Get ULP values from math_brute_force functionList
315     if (strstr(test_name, "math_kernel"))
316     {
317         for( size_t i = 0; i < functionListCount; i++ )
318         {
319             char name[64];
320             const Func *func = &functionList[ i ];
321             sprintf(name, ".%s_float", func->name);
322             if (strstr(test_name, name))
323             {
324                 ulps = func->float_ulps;
325             }
326             else
327             {
328                 sprintf(name, ".%s_double", func->name);
329                 if (strstr(test_name, name))
330                 {
331                     ulps = func->double_ulps;
332                 }
333             }
334         }
335     }
336     return ulps;
337 }
338 
TestRunner(EventHandler * success,EventHandler * failure,const OclExtensions & devExt)339 TestRunner::TestRunner(EventHandler *success, EventHandler *failure,
340                        const OclExtensions& devExt):
341     m_successHandler(success), m_failureHandler(failure), m_devExt(&devExt) {}
342 
343 /**
344  Based on the test name build the cl file name, the bc file name and execute
345  the kernel for both modes (cl and bc).
346  */
runBuildTest(cl_device_id device,const char * folder,const char * test_name,cl_uint size_t_width)347 bool TestRunner::runBuildTest(cl_device_id device, const char *folder,
348                               const char *test_name, cl_uint size_t_width)
349 {
350     int failures = 0;
351     // Composing the name of the CSV file.
352     char* dir = get_exe_dir();
353     std::string csvName(dir);
354     csvName.append(dir_sep());
355     csvName.append("khr.csv");
356     free(dir);
357 
358     log_info("%s...\n", test_name);
359 
360     float ulps = get_max_ulps(test_name);
361 
362     // Figure out whether the test can run on the device. If not, we skip it.
363     const KhrSupport& khrDb = *KhrSupport::get(csvName);
364     cl_bool images = khrDb.isImagesRequired(folder, test_name);
365     cl_bool images3D = khrDb.isImages3DRequired(folder, test_name);
366 
367     char deviceProfile[64];
368     clGetDeviceInfo(device, CL_DEVICE_PROFILE, sizeof(deviceProfile), &deviceProfile, NULL);
369     std::string device_profile(deviceProfile, 64);
370 
371     if(images == CL_TRUE && checkForImageSupport(device) != 0)
372     {
373         (*m_successHandler)(test_name, "");
374         std::cout << "Skipped. (Cannot run on device due to Images is not supported)." << std::endl;
375         return true;
376     }
377 
378     if(images3D == CL_TRUE && checkFor3DImageSupport(device) != 0)
379     {
380         (*m_successHandler)(test_name, "");
381         std::cout << "Skipped. (Cannot run on device as 3D images are not supported)." << std::endl;
382         return true;
383     }
384 
385     OclExtensions requiredExt = khrDb.getRequiredExtensions(folder, test_name);
386     if(!m_devExt->supports(requiredExt))
387     {
388         (*m_successHandler)(test_name, "");
389         std::cout << "Skipped. (Cannot run on device due to missing extensions: " << m_devExt->get_missing(requiredExt) << " )." << std::endl;
390         return true;
391     }
392 
393     std::string cl_file_path, bc_file;
394     // Build cl file name based on the test name
395     get_cl_file_path(folder, test_name, cl_file_path);
396     // Build bc file name based on the test name
397     get_bc_file_path(folder, test_name, bc_file, size_t_width);
398     gRG.init(1);
399     //
400     // Processing each kernel in the program separately
401     //
402     clContextWrapper context;
403     clCommandQueueWrapper queue;
404     create_context_and_queue(device, &context, &queue);
405     clProgramWrapper clprog = create_program_from_cl(context, cl_file_path);
406     clProgramWrapper bcprog = create_program_from_bc(context, bc_file);
407     std::string bcoptions = "-x spir -spir-std=1.2 -cl-kernel-arg-info";
408     std::string cloptions = "-cl-kernel-arg-info";
409 
410     cl_device_fp_config gFloatCapabilities = 0;
411     cl_int err;
412     if ((err = clGetDeviceInfo(device, CL_DEVICE_SINGLE_FP_CONFIG, sizeof(gFloatCapabilities), &gFloatCapabilities, NULL)))
413     {
414         log_info("Unable to get device CL_DEVICE_SINGLE_FP_CONFIG. (%d)\n", err);
415     }
416 
417     if (strstr(test_name, "div_cr") || strstr(test_name, "sqrt_cr")) {
418         if ((gFloatCapabilities & CL_FP_CORRECTLY_ROUNDED_DIVIDE_SQRT) == 0) {
419             (*m_successHandler)(test_name, "");
420             std::cout << "Skipped. (Cannot run on device due to missing CL_FP_CORRECTLY_ROUNDED_DIVIDE_SQRT property.)" << std::endl;
421             return true;
422         } else {
423             bcoptions += " -cl-fp32-correctly-rounded-divide-sqrt";
424             cloptions += " -cl-fp32-correctly-rounded-divide-sqrt";
425         }
426     }
427 
428     // Building the programs.
429     BuildTask clBuild(clprog, device, cloptions.c_str());
430     if (!clBuild.execute()) {
431         std::cerr << clBuild.getErrorLog() << std::endl;
432         return false;
433     }
434 
435     SpirBuildTask bcBuild(bcprog, device, bcoptions.c_str());
436     if (!bcBuild.execute()) {
437         std::cerr << bcBuild.getErrorLog() << std::endl;
438         return false;
439     }
440 
441     KernelEnumerator clkernel_enumerator(clprog),
442                      bckernel_enumerator(bcprog);
443     if (clkernel_enumerator.size() != bckernel_enumerator.size()) {
444         std::cerr << "number of kernels in test" << test_name
445                   << " doesn't match in bc and cl files" << std::endl;
446         return false;
447     }
448     KernelEnumerator::iterator it = clkernel_enumerator.begin(),
449         e = clkernel_enumerator.end();
450     while (it != e)
451     {
452         std::string kernel_name = *it++;
453         std::string err;
454         try
455         {
456             bool success = run_test(context, queue, clprog, bcprog, kernel_name, err, device, ulps);
457             if (success)
458             {
459                 log_info("kernel '%s' passed.\n", kernel_name.c_str());
460                 (*m_successHandler)(test_name, kernel_name);
461             }
462             else
463             {
464                 ++failures;
465                 log_info("kernel '%s' failed.\n", kernel_name.c_str());
466                 (*m_failureHandler)(test_name, kernel_name);
467             }
468         } catch (const std::runtime_error& err)
469         {
470             ++failures;
471             log_info("kernel '%s' failed: %s\n", kernel_name.c_str(), err.what());
472             (*m_failureHandler)(test_name, kernel_name);
473         }
474     }
475 
476     log_info("%s %s\n", test_name, failures ? "FAILED" : "passed.");
477     return failures == 0;
478 }
479 
480