Fix and improve testsuite
[ocl-icd] / icd_generator.rb
1 =begin
2 Copyright (c) 2012, Brice Videau <brice.videau@imag.fr>
3 Copyright (c) 2012, Vincent Danjean <Vincent.Danjean@ens-lyon.org>
4 All rights reserved.
5       
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions are met:
8     
9 1. Redistributions of source code must retain the above copyright notice, this
10    list of conditions and the following disclaimer.
11 2. Redistributions in binary form must reproduce the above copyright notice,
12    this list of conditions and the following disclaimer in the documentation
13    and/or other materials provided with the distribution.
14         
15 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
19 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 =end
26
27 require 'yaml'
28
29 module IcdGenerator
30   $api_entries = {}
31   $key_entries = {}
32   $api_entries_array = []
33   $cl_objects = ["platform_id", "device_id", "context", "command_queue", "mem", "program", "kernel", "event", "sampler"]
34   $know_entries = { 1 => "clGetPlatformInfo", 0 => "clGetPlatformIDs", 2 => "clGetDeviceIDs" }
35   $use_name_in_test = { 1 => "clGetPlatformInfo", 0 => "clGetPlatformIDs", 2 => "clGetDeviceIDs" }
36   # do not call these functions when trying to discover the mapping
37   $forbidden_funcs = [
38     "clUnloadCompiler", # No parameters so no way to forward to an ICD, OpenCL 1.0
39     "clGetPlatformIDs", # Implemented directly within the ICD Loader, not forwarded to ICD
40   ]
41   # windows function to ignore when loading the database
42   $windows_funcs = ["clGetDeviceIDsFromD3D10KHR", "clCreateFromD3D10BufferKHR",
43     "clCreateFromD3D10Texture2DKHR", "clCreateFromD3D10Texture3DKHR",
44     "clEnqueueAcquireD3D10ObjectsKHR", "clEnqueueReleaseD3D10ObjectsKHR",
45     "clGetDeviceIDsFromD3D11KHR", "clCreateFromD3D11BufferKHR",
46     "clCreateFromD3D11Texture2DKHR", "clCreateFromD3D11Texture3DKHR",
47     "clEnqueueAcquireD3D11ObjectsKHR", "clEnqueueReleaseD3D11ObjectsKHR",
48     "clGetDeviceIDsFromDX9MediaAdapterKHR", "clCreateFromDX9MediaSurfaceKHR",
49     "clEnqueueAcquireDX9MediaSurfacesKHR", "clEnqueueReleaseDX9MediaSurfacesKHR"]
50   # do not create weak functions for these ones in the discovering program
51   $noweak_funcs = ["clGetExtensionFunctionAddress", "clGetPlatformIDs",
52     "clGetPlatformInfo", "clGetGLContextInfoKHR", "clUnloadCompiler",
53     "clCreateContext", "clCreateContextFromType", "clWaitForEvents"]
54   # functions written specifically in the loader
55   $specific_loader_funcs = ["clGetExtensionFunctionAddress","clGetPlatformIDs",
56                          "clGetGLContextInfoKHR", "clUnloadCompiler",
57     "clCreateContext", "clCreateContextFromType", "clWaitForEvents"]
58   $header_files = ["/usr/include/CL/cl.h", "/usr/include/CL/cl_gl.h",
59     "/usr/include/CL/cl_ext.h", "/usr/include/CL/cl_gl_ext.h"]
60   $windows_header_files = ["/usr/include/CL/cl_dx9_media_sharing.h", "/usr/include/CL/cl_d3d11.h", "/usr/include/CL/cl_d3d10.h"]
61   $cl_data_type_error = { "cl_platform_id"   => "CL_INVALID_PLATFORM",
62                           "cl_device_id"     => "CL_INVALID_DEVICE",
63                           "cl_context"       => "CL_INVALID_CONTEXT",
64                           "cl_command_queue" => "CL_INVALID_COMMAND_QUEUE",
65                           "cl_mem"           => "CL_INVALID_MEM_OBJECT",
66                           "cl_program"       => "CL_INVALID_PROGRAM",
67                           "cl_kernel"        => "CL_INVALID_KERNEL",
68                           "cl_event"         => "CL_INVALID_EVENT",
69                           "cl_sampler"       => "CL_INVALID_SAMPLER"}
70   $non_standard_error = [ "clGetExtensionFunctionAddressForPlatform", "clSVMAlloc" ]
71   $versions_entries = []
72   $buff=50
73   $license = <<EOF
74 Copyright (c) 2012, Brice Videau <brice.videau@imag.fr>
75 Copyright (c) 2012, Vincent Danjean <Vincent.Danjean@ens-lyon.org>
76 All rights reserved.
77       
78 Redistribution and use in source and binary forms, with or without
79 modification, are permitted provided that the following conditions are met:
80     
81 1. Redistributions of source code must retain the above copyright notice, this
82    list of conditions and the following disclaimer.
83 2. Redistributions in binary form must reproduce the above copyright notice,
84    this list of conditions and the following disclaimer in the documentation
85    and/or other materials provided with the distribution.
86         
87 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
88 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
89 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
90 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
91 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
92 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
93 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
94 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
95 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
96 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
97
98 Do not edit this file. It is automatically generated.
99 EOF
100
101   ##########################################################
102   ##########################################################
103   # helper functions
104   def self.parse_headers
105     api_entries = []
106     $header_files.each{ |fname|
107       f = File::open(fname)
108       doc = f.read
109       api_entries += doc.scan(/CL_API_ENTRY.*?;/m)
110       f.close
111     }
112     api_entries.each{ |entry|
113 #      puts entry
114       begin 
115         entry_name = entry.match(/CL_API_CALL(.*?)\(/m)[1].strip
116       rescue
117         entry_name = entry.match(/(\S*?)\(/m)[1].strip
118       end
119       next if entry_name.match('\*')
120       next if entry_name.match("INTEL")
121       next if entry_name.match("APPLE")
122       $api_entries[entry_name] = entry.gsub("\r","")
123     }
124 #    $api_entries.each{ |key, value|
125 #      puts "#{key}: #{value}"
126 #    }
127   end
128
129   def self.load_database(yamlfile, with_windows=false)
130     doc = YAML::load_file(yamlfile)
131     $known_entries = {}
132     $api_entries ||= {}
133     $versions_entries = Hash::new { |hash,key| hash[key]=[] }
134     entry_name = ""
135     version = ""
136     doc.each { |key, value|
137       #puts (key.to_s+":: "+value)
138       begin
139         entry_name = value.match(/CL_API_CALL(.*?)\(/m)[1].strip
140       rescue
141         entry_name = value.match(/(\S*?)\(/m)[1].strip
142       end
143       next if (!with_windows) && $windows_funcs.include?(entry_name)
144       version = value.match(/SUFFIX__VERSION_(\d_\d)/m)[1]
145       $versions_entries[version].push(entry_name)
146       $known_entries[key] = entry_name
147       $key_entries[entry_name] = key
148       $api_entries[entry_name] = value
149     }
150     $api_entries_array = []
151     ($known_entries.length+$buff).times { |i|
152       #puts (i.to_s+": "+$known_entries[i])
153       if $known_entries[i] then
154         $api_entries_array.push( $api_entries[$known_entries[i]] )
155       else
156         $api_entries_array.push( "CL_API_ENTRY cl_int CL_API_CALL clUnknown#{i}(void);" )
157       end
158     }
159   end
160
161   def self.include_headers
162     headers =""
163     $header_files.each { |h|
164       if h.match('^/usr/include/') then
165         headers += "#include <#{h[13..-1]}>\n"
166       else
167         headers += "#include \"#{h}\"\n"
168       end
169     }
170     return headers
171   end
172
173   ##########################################################
174   ##########################################################
175   # generate mode
176   def self.generate_libdummy_icd_header
177     libdummy_icd_structures = "/**\n#{$license}\n*/\n"
178     libdummy_icd_structures +=  "#include <CL/opencl.h>\n"
179     libdummy_icd_structures += self.include_headers
180     libdummy_icd_structures += "\n\nstruct _cl_icd_dispatch;\n"
181     libdummy_icd_structures += "struct _cl_platform_id { struct _cl_icd_dispatch *dispatch; };\n\n"
182     libdummy_icd_structures += "struct _cl_icd_dispatch {\n"
183     ($api_entries.length+$buff).times { |i|
184       if( $known_entries[i] ) then
185         libdummy_icd_structures += "  void(*known#{i})(void);\n"
186       else
187         libdummy_icd_structures += "  void(*unknown#{i})(void);\n"
188       end
189     }
190     libdummy_icd_structures += "};\n\n"
191     libdummy_icd_structures += "#pragma GCC visibility push(hidden)\n\n"
192     libdummy_icd_structures += "struct _cl_icd_dispatch master_dispatch; \n\n"
193     $use_name_in_test.each { |k, f|
194       libdummy_icd_structures += "typeof(#{f}) INT#{f};\n"
195     }
196     libdummy_icd_structures += "#pragma GCC visibility pop\n\n"
197     return libdummy_icd_structures
198   end
199
200   def self.generate_libdummy_icd_source
201     libdummy_icd_source = "/**\n#{$license}\n*/\n\n"
202     libdummy_icd_source += "#include <stdio.h>\n\n"
203     libdummy_icd_source += "#include \"libdummy_icd_gen.h\"\n\n"
204     libdummy_icd_source += "#include \"libdummy_icd.h\"\n\n"
205     (0...$api_entries.length+$buff).each { |i|
206       libdummy_icd_source += "void dummyFunc#{i}(void){ printf(\"#{i}  : \"); fflush(NULL); }\n"
207     }
208     libdummy_icd_source += "\nstruct _cl_icd_dispatch master_dispatch = {\n"
209     comma=","
210     ($api_entries.length+$buff).times { |i|
211       comma="" if (i == $api_entries.length+$buff-1)
212       if( $use_name_in_test[i] ) then 
213         libdummy_icd_source += "  (void(*)(void))& INT#{$known_entries[i]}#{comma}\n"
214       else
215         libdummy_icd_source += "  (void(*)(void))& dummyFunc#{i}#{comma}\n"
216       end
217     }
218     libdummy_icd_source += "};\n"
219     return libdummy_icd_source
220   end
221   
222   def self.generate_run_dummy_icd_source
223     run_dummy_icd = "/**\n#{$license}\n*/\n"
224     run_dummy_icd += "#include <stdlib.h>\n"
225     run_dummy_icd += "#include <stdio.h>\n"
226     run_dummy_icd += "#pragma GCC diagnostic push\n"
227     run_dummy_icd += "#  pragma GCC diagnostic ignored \"-Wcpp\"\n"
228     run_dummy_icd += "#  define CL_USE_DEPRECATED_OPENCL_1_0_APIS\n"
229     run_dummy_icd += "#  define CL_USE_DEPRECATED_OPENCL_1_1_APIS\n"
230     run_dummy_icd += "#  define CL_USE_DEPRECATED_OPENCL_1_2_APIS\n"
231     run_dummy_icd += "#  include <CL/opencl.h>\n"
232     run_dummy_icd += self.include_headers
233     run_dummy_icd += "#pragma GCC diagnostic pop\n"
234     run_dummy_icd += "\n\n"
235     $api_entries.each_key { |func_name|
236        next if $forbidden_funcs.include?(func_name)
237        run_dummy_icd += $api_entries[func_name]+";\n"
238     }
239     run_dummy_icd += "\n\n"
240     run_dummy_icd += "void call_all_OpenCL_functions(cl_platform_id chosen_platform) {\n"
241     run_dummy_icd += "  cl_context_properties properties[] = { CL_CONTEXT_PLATFORM, (cl_context_properties)chosen_platform, 0 };\n"
242     $api_entries.each_key { |func_name|
243        next if $forbidden_funcs.include?(func_name)
244        if func_name == "clCreateContext" then
245          run_dummy_icd += "  #{func_name}(properties,1,(cl_device_id*)&chosen_platform,NULL,NULL,NULL);\n"
246        elsif func_name == "clGetGLContextInfoKHR" then
247          run_dummy_icd += "  #{func_name}(properties,CL_CURRENT_DEVICE_FOR_GL_CONTEXT_KHR, 0, NULL, NULL);\n"
248        elsif func_name == "clCreateContextFromType" then
249          run_dummy_icd += "  #{func_name}(properties,CL_DEVICE_TYPE_CPU,NULL,NULL,NULL);\n"
250        elsif func_name == "clWaitForEvents" then
251          run_dummy_icd += "  #{func_name}(1,(cl_event*)&chosen_platform);\n"
252        elsif func_name == "clGetExtensionFunctionAddress" then
253          run_dummy_icd += "  #{func_name}(\"extLIG\");\n"
254        elsif func_name == "clUnloadCompiler" then
255          run_dummy_icd += "  #{func_name}();\n"
256        else
257          params = $api_entries[func_name].gsub(/[[:space:]]+/, ' ')
258          params.gsub!(/[\/][*](([^*]*)[*][^\/])*[^*]*[*][\/]/,'')
259          params.gsub!(/^[^(]*[(] *(.*[^ ]) *[)][^)]*$/, '\1')
260          params = params.gsub(/[^,(]+([(][^)]*[)])*[^,]*/) { |p|
261            p.gsub!("[]", "*")
262            p.gsub!("user_func", "")
263            if false && p.match(/[*]/) then
264              "NULL"
265            else
266              "(#{p})0"
267            end
268          }
269          if func_name != "clGetPlatformIDs" then
270            params.gsub!(/^([^0]*)0/, '\1chosen_platform')
271          end
272          run_dummy_icd += "  #{func_name}(#{params});\n"
273        end
274        run_dummy_icd += "  printf(\"%s\\n\", \"#{func_name}\");\n"
275        run_dummy_icd += "#ifdef OCL_ICD_PRINT_EXPECTED\n"
276        run_dummy_icd += "  printf(\"#{$key_entries[func_name]}  : %s (expected)\\n\", \"#{func_name}\");\n"
277        run_dummy_icd += "#endif\n"
278        run_dummy_icd += "  fflush(NULL);\n"
279     }
280     run_dummy_icd += "  return;\n}\n"
281     return run_dummy_icd
282   end
283
284   def self.generate_run_dummy_icd_weak_source
285     run_dummy_icd_weak = "/**\n#{$license}\n*/\n"
286     run_dummy_icd_weak += <<EOF
287 #define _GNU_SOURCE 1
288 #include <stdio.h>
289 #include <dlfcn.h>
290
291 #define F(f) \\
292 __attribute__((weak)) int f (void* arg, int arg2, int arg3, void* arg4, void* arg5) { \\
293   void (* p)(void*, int, int, void*, void*)=NULL; \\
294   p=dlsym(RTLD_NEXT, #f); \\
295   if (p) { \\
296     (*p)(arg, arg2, arg3, arg4, arg5); \\
297   } else { \\
298     printf("-1 : "); \\
299   } \\
300   return 0; \\
301 }
302
303 EOF
304     $api_entries.each_key { |func_name|
305        next if $noweak_funcs.include?(func_name)
306        run_dummy_icd_weak += "F(#{func_name})\n"
307     }
308     return run_dummy_icd_weak
309   end
310
311   def self.generate_sources(from_headers=true, from_database=false, database=nil)
312     if from_headers then        
313       parse_headers
314     end
315     if from_database then
316       load_database(database)
317     end
318     File.open('libdummy_icd_gen.h','w') { |f|
319       f.puts generate_libdummy_icd_header
320     }
321     File.open('libdummy_icd_gen.c','w') { |f|
322       f.puts generate_libdummy_icd_source
323     }
324     File.open('run_dummy_icd_gen.c','w') { |f|
325       f.puts generate_run_dummy_icd_source
326     }
327     File.open('run_dummy_icd_weak_gen.c','w') { |f|
328       f.puts generate_run_dummy_icd_weak_source
329     }
330   end
331
332   ##########################################################
333   ##########################################################
334   # database mode
335   def self.generate_ocl_icd_header
336     ocl_icd_header = "/**\n#{$license}\n*/\n\n"
337     ocl_icd_header += "#ifndef OCL_ICD_H\n"
338     ocl_icd_header += "#define OCL_ICD_H\n"
339     ocl_icd_header += "#pragma GCC diagnostic push\n"
340     ocl_icd_header += "#  pragma GCC diagnostic ignored \"-Wcpp\"\n"
341     ocl_icd_header += "#  define CL_USE_DEPRECATED_OPENCL_1_0_APIS\n"
342     ocl_icd_header += "#  define CL_USE_DEPRECATED_OPENCL_1_1_APIS\n"
343     ocl_icd_header += "#  define CL_USE_DEPRECATED_OPENCL_1_2_APIS\n"
344     ocl_icd_header += "#  include <CL/opencl.h>\n"
345     ocl_icd_header += self.include_headers
346     ocl_icd_header += "#pragma GCC diagnostic pop\n"
347     ocl_icd_header += <<EOF
348
349 #define OCL_ICD_API_VERSION     1
350 #define OCL_ICD_IDENTIFIED_FUNCTIONS    #{$known_entries.count}
351
352 struct _cl_icd_dispatch {
353 EOF
354     nb=0
355     $api_entries_array.each { |entry|
356       version = entry.split("\n").
357         grep(/ CL_API_SUFFIX__(VERSION_[0-9_]+)[^0-9_]/).join('').
358         gsub(/.* CL_API_SUFFIX__(VERSION_[0-9_]+)[^0-9_].*$/, '\1')
359       if (version != '') then
360         ocl_icd_header += '#ifdef CL_'+version+"\n"
361       end
362       ocl_icd_header += entry.gsub("\r","").
363         sub(/CL_API_CALL\n?(.*?)\(/m,'(CL_API_CALL*\1)('+"\n  ").
364         gsub(/\) (CL_API_SUFFIX__VERSION)/m,"\n) \\1").gsub(/\s*$/,'').
365         gsub(/^[\t ]+/,"    ").gsub(/^([^\t ])/, '  \1') + "\n"
366       if (version != '') then
367         ocl_icd_header += '#else'+"\n"
368         ocl_icd_header += '  CL_API_ENTRY cl_int (CL_API_CALL* clUnknown'+nb.to_s+")(void);\n"
369         ocl_icd_header += '#endif'+"\n"
370       end
371       ocl_icd_header += "\n"
372       nb=nb+1
373     }
374     ocl_icd_header += "};\n"
375     ocl_icd_header += "#endif\n\n"
376     return ocl_icd_header
377   end
378
379   def self.generate_ocl_icd_loader_header
380     ocl_icd_header = "/**\n#{$license}\n*/\n\n"
381     ocl_icd_header += "#include \"ocl_icd.h\"\n\n"
382     ocl_icd_header += <<EOF
383
384 struct func_desc {
385   const char* name;
386   void(*const addr)(void);
387 };
388 typedef __typeof__(clGetExtensionFunctionAddress) *clGetExtensionFunctionAddress_fn;
389 extern const struct func_desc function_description[];
390 struct vendor_icd {
391   cl_uint       num_platforms;
392   void *        dl_handle;
393   clGetExtensionFunctionAddress_fn ext_fn_ptr;
394 };
395
396 struct platform_icd {
397   char *        extension_suffix;
398   char *        version;
399   struct vendor_icd *vicd;
400   cl_platform_id pid;
401   cl_uint       ngpus; /* number of GPU devices */
402   cl_uint       ncpus; /* number of CPU devices */
403   cl_uint       ndevs; /* total number of devices, of all types */
404 };
405
406 EOF
407     ocl_icd_header += "extern struct _cl_icd_dispatch master_dispatch;\n"
408     $cl_objects.each { |o|
409       ocl_icd_header += "struct _cl_#{o} { struct _cl_icd_dispatch *dispatch; };\n"
410     }
411     return ocl_icd_header
412   end
413
414   def self.generate_ocl_icd_loader_map
415     ocl_icd_loader_map = "/**\n#{$license}\n*/\n\n"
416     prev_version=""
417     $versions_entries.keys.sort.each { |version|
418       ocl_icd_loader_map += "OPENCL_#{version.sub('_','.')} {\n";
419       ocl_icd_loader_map += "  global:\n";
420       $versions_entries[version].each { |symb|
421         ocl_icd_loader_map += "    #{symb};\n"
422       }
423       if (prev_version == "") then
424         ocl_icd_loader_map += "  local:\n";
425         ocl_icd_loader_map += "    *;\n";
426       end
427       ocl_icd_loader_map += "} #{prev_version};\n\n";
428       prev_version="OPENCL_#{version.sub('_','.')}";
429     }
430     return ocl_icd_loader_map
431   end
432  
433   def self.generate_ocl_icd_bindings_source
434     ocl_icd_bindings_source = "/**\n#{$license}\n*/\n"
435     ocl_icd_bindings_source += "#include \"ocl_icd.h\"\n"
436     ocl_icd_bindings_source += "struct _cl_icd_dispatch master_dispatch = {\n"
437     ($api_entries.length+$buff-1).times { |i|
438       if( $known_entries[i] ) then 
439         ocl_icd_bindings_source += "  #{$known_entries[i]},\n"
440       else
441         ocl_icd_bindings_source += "  (void *) NULL,\n"
442       end
443     }
444     if( $known_entries[$api_entries.length+$buff-1] ) then
445       ocl_icd_bindings_source += "  #{$known_entries[$api_entries.length+$buff-1]}\n"
446     else
447       ocl_icd_bindings_source += "  (void *) NULL\n"
448     end
449     ocl_icd_bindings_source += "};\n"
450     ocl_icd_bindings_source += <<EOF
451
452 CL_API_ENTRY cl_int CL_API_CALL clIcdGetPlatformIDsKHR(  
453              cl_uint num_entries, 
454              cl_platform_id *platforms,
455              cl_uint *num_platforms) {
456   if( platforms == NULL && num_platforms == NULL )
457     return CL_INVALID_VALUE;
458   if( num_entries == 0 && platforms != NULL )
459     return CL_INVALID_VALUE;
460 #error You have to fill the commented lines with corresponding variables from your library
461 //  if( your_number_of_platforms == 0)
462 //    return CL_PLATFORM_NOT_FOUND_KHR;
463 //  if( num_platforms != NULL )
464 //    *num_platforms = your_number_of_platforms;
465   if( platforms != NULL ) {
466     cl_uint i;
467 //    for( i=0; i<(your_number_of_platforms<num_entries?your_number_of_platforms:num_entries); i++)
468 //      platforms[i] = &your_platforms[i];
469   }
470   return CL_SUCCESS;
471 }
472
473 CL_API_ENTRY void * CL_API_CALL clGetExtensionFunctionAddress(
474              const char *   func_name) CL_API_SUFFIX__VERSION_1_0 {
475 #error You have to fill this function with your extensions of incorporate these lines in your version
476   if( func_name != NULL &&  strcmp("clIcdGetPlatformIDsKHR", func_name) == 0 )
477     return (void *)clIcdGetPlatformIDsKHR;
478   return NULL;
479 }
480 CL_API_ENTRY cl_int CL_API_CALL clGetPlatformInfo(
481              cl_platform_id   platform, 
482              cl_platform_info param_name,
483              size_t           param_value_size, 
484              void *           param_value,
485              size_t *         param_value_size_ret) CL_API_SUFFIX__VERSION_1_0 {
486 #error You ahve to fill this function with your information or assert that your version responds to CL_PLATFORM_ICD_SUFFIX_KHR
487 //  char cl_platform_profile[] = "FULL_PROFILE";
488 //  char cl_platform_version[] = "OpenCL 1.1";
489 //  char cl_platform_name[] = "DummyCL";
490 //  char cl_platform_vendor[] = "LIG";
491 //  char cl_platform_extensions[] = "cl_khr_icd";
492 //  char cl_platform_icd_suffix_khr[] = "DUMMY";
493   size_t size_string;
494   char * string_p;
495   if( platform != NULL ) {
496     int found = 0;
497     int i;
498     for(i=0; i<num_master_platforms; i++) {
499       if( platform == &master_platforms[i] )
500         found = 1;
501     }
502     if(!found)
503       return CL_INVALID_PLATFORM;
504   }
505   switch ( param_name ) {
506     case CL_PLATFORM_PROFILE:
507       string_p = cl_platform_profile;
508       size_string = sizeof(cl_platform_profile);
509       break;
510     case CL_PLATFORM_VERSION:
511       string_p = cl_platform_version;
512       size_string = sizeof(cl_platform_version);
513       break;
514     case CL_PLATFORM_NAME:
515       string_p = cl_platform_name;
516       size_string = sizeof(cl_platform_name);
517       break;
518     case CL_PLATFORM_VENDOR:
519       string_p = cl_platform_vendor;
520       size_string = sizeof(cl_platform_vendor);
521       break;
522     case CL_PLATFORM_EXTENSIONS:
523       string_p = cl_platform_extensions;
524       size_string = sizeof(cl_platform_extensions);
525       break;
526     case CL_PLATFORM_ICD_SUFFIX_KHR:
527       string_p = cl_platform_icd_suffix_khr;
528       size_string = sizeof(cl_platform_icd_suffix_khr);
529       break;
530     default:
531       return CL_INVALID_VALUE;
532       break;
533   }
534   if( param_value != NULL ) {
535     if( size_string > param_value_size )
536       return CL_INVALID_VALUE;
537     memcpy(param_value, string_p, size_string);
538   }
539   if( param_value_size_ret != NULL )
540     *param_value_size_ret = size_string;
541   return CL_SUCCESS;
542 }
543 EOF
544     return ocl_icd_bindings_source
545   end
546
547   def self.generate_ocl_icd_loader_gen_source
548     skip_funcs = $specific_loader_funcs
549     ocl_icd_loader_gen_source = "/**\n#{$license}\n*/\n"
550     ocl_icd_loader_gen_source += "#include \"ocl_icd_loader.h\"\n"
551     ocl_icd_loader_gen_source += "#define DEBUG_OCL_ICD_PROVIDE_DUMP_FIELD\n"
552     ocl_icd_loader_gen_source += "#include \"ocl_icd_debug.h\"\n"
553     $api_entries.each { |func_name, entry|
554       next if skip_funcs.include?(func_name)
555       clean_entry = entry.sub(/(.*\)).*/m,'\1').gsub("/*","").gsub("*/","").gsub("\r","") + "{\n"
556       return_type = entry.match(/CL_API_ENTRY (.*) CL_API_CALL/)[1]
557       parameters = clean_entry.match(/\(.*\)/m)[0][1..-2]
558       parameters.gsub!(/\[.*?\]/,"")
559       parameters.sub!(/\(.*?\*\s*(.*?)\)\s*\(.*?\)/m,'\1')
560       ocl_icd_loader_gen_source += clean_entry.gsub(/\*\[.*?\]/,"*  ").gsub(/\[.+?\]/,"")
561       first_parameter = parameters.match(/.*?\,/m)
562       if not first_parameter then
563         first_parameter =  parameters.match(/.*/m)[0]
564       else
565         first_parameter = first_parameter[0][0..-2]
566       end
567       fps = first_parameter.split
568       ocl_icd_loader_gen_source += "  debug_trace();\n"
569       raise "Unsupported data_type #{fps[0]}" if not $cl_data_type_error[fps[0]]
570       ps = parameters.split(",")
571       ps = ps.collect { |p|
572         p = p.split
573         p = p[-1].gsub("*","")
574       }
575       error_handler = lambda {
576          if(ps.include?("errcode_ret")) then
577           ocl_icd_loader_gen_source += "    if( errcode_ret != NULL ) {\n";
578           ocl_icd_loader_gen_source += "      *errcode_ret = #{$cl_data_type_error[fps[0]]};\n"
579           ocl_icd_loader_gen_source += "    }\n"
580           if return_type != "void" then
581             ocl_icd_loader_gen_source += "    RETURN(NULL);\n"
582           else
583             ocl_icd_loader_gen_source += "    return;\n"
584           end
585         elsif ($non_standard_error.include?(func_name)) then
586           if return_type != "void" then
587             ocl_icd_loader_gen_source += "    RETURN(NULL);\n"
588           else
589             ocl_icd_loader_gen_source += "    return;\n"
590           end
591         else
592           if return_type != "void" then
593             ocl_icd_loader_gen_source += "    RETURN(#{$cl_data_type_error[fps[0]]});\n" if return_type != "void"
594           else
595             ocl_icd_loader_gen_source += "    return;\n"
596           end
597         end
598       }
599        
600       if(fps[0] == "cl_platform_id") then
601         ocl_icd_loader_gen_source += "  #{fps[1]}=selectPlatformID(#{fps[1]});\n"
602       end
603       ocl_icd_loader_gen_source += "  if( (struct _#{fps[0]} *)#{fps[1]} == NULL) {\n"
604       error_handler.call
605       ocl_icd_loader_gen_source += "  }\n"
606       if return_type != "void" then
607         return_debug="RETURN"
608       else
609         return_debug="return"
610       end
611       ocl_icd_loader_gen_source += "  #{return_debug}(((struct _#{fps[0]} *)#{fps[1]})->dispatch->#{func_name}("
612       ocl_icd_loader_gen_source += ps.join(", ")
613       ocl_icd_loader_gen_source += "));\n"
614       ocl_icd_loader_gen_source += "}\n\n"
615     }
616     ocl_icd_loader_gen_source += "#pragma GCC visibility push(hidden)\n\n"
617     skip_funcs = $specific_loader_funcs
618     $api_entries.each { |func_name, entry|
619       #next if func_name.match(/EXT$/)
620       #next if func_name.match(/KHR$/)
621       if (skip_funcs.include?(func_name)) then
622         ocl_icd_loader_gen_source += "extern typeof(#{func_name}) #{func_name}_hid;\n"
623       else
624         ocl_icd_loader_gen_source += "typeof(#{func_name}) #{func_name}_hid __attribute__ ((alias (\"#{func_name}\"), visibility(\"hidden\")));\n"
625       end
626     }
627     ocl_icd_loader_gen_source += "\n\nstruct func_desc const function_description[]= {\n"
628     $api_entries.each { |func_name, entry|
629       #next if func_name.match(/EXT$/)
630       #next if func_name.match(/KHR$/)
631       ocl_icd_loader_gen_source += "  {\"#{func_name}\", (void(* const)(void))&#{func_name}_hid },\n"
632     }
633     ocl_icd_loader_gen_source += <<EOF
634   {NULL, NULL}
635 };
636
637 #ifdef DEBUG_OCL_ICD
638 void dump_platform(clGEFA_t f, cl_platform_id pid) {
639   debug(D_ALWAYS, "platform @%p:  name=field_in_struct [clGetExtensionFunctionAddress(name)/clGetExtensionFunctionAddressForPlatform(name)]", pid);
640 EOF
641     $api_entries_array.each { |entry|
642       e = entry.gsub("\r"," ").gsub("\n"," ").gsub("\t"," ").
643         sub(/.*CL_API_CALL *([^ ()]*)[ ()].*$/m, '\1')
644       ocl_icd_loader_gen_source += "  dump_field(pid, f, #{e});\n"
645     }
646
647     ocl_icd_loader_gen_source += <<EOF
648 }
649 #endif
650
651 #pragma GCC visibility pop
652
653 EOF
654     return ocl_icd_loader_gen_source;
655   end
656   
657   def self.generate_from_database(yamlfile)
658     load_database(yamlfile)
659     File.open('ocl_icd.h','w') { |f|
660       f.puts generate_ocl_icd_header
661     }
662     File.open('ocl_icd_loader_gen.h','w') { |f|
663       f.puts generate_ocl_icd_loader_header
664     }
665     File.open('ocl_icd_loader_gen.map','w') { |f|
666       f.puts generate_ocl_icd_loader_map
667     }
668     File.open('ocl_icd_bindings.c','w') { |f|
669       f.puts generate_ocl_icd_bindings_source
670     }
671     File.open('ocl_icd_loader_gen.c','w') { |f|
672       f.puts generate_ocl_icd_loader_gen_source
673     }
674   end
675
676   ##########################################################
677   ##########################################################
678   # update-database mode
679   def self.savedb(yamlfile)
680     File::open(yamlfile,"w") { |f|
681       f.write($license.gsub(/^/,"# "))
682       f.write( <<EOF
683
684 # In Intel (OpenCL 1.1):
685 # * clSetCommandQueueProperty(13): nil (deprecated in 1.1)
686 # * clGetGLContextInfoKHR(74): function present with its symbol
687 # * 75-80: nil
688 # * 92: correspond to symbol clGetKernelArgInfo (first abandonned version?)
689 # * 93-: garbage
690 # In nvidia (OpenCL 1.1):
691 # * clGetGLContextInfoKHR(74): function present but no symbol
692 # * 75-80: nil
693 # * 89-: nil
694 # * only two OpenCL symbols: clGetPlatformInfo(1) and clGetExtensionFunctionAddress(65)
695 # In AMD (OpenCL 1.2):
696 # * clGetPlatformIDs(0): nil (symbol present)
697 # * clGetGLContextInfoKHR(74): function present but no symbol
698 # * 75-80: nil
699 # * 92: nil
700 # * 109-118: nil
701 # * 119-: garbage
702
703 EOF
704 )
705       # Not using YAML::dump as:
706       # * keys are not ordered
707       # * strings are badly formatted in new YAML ruby implementation (psych)
708       # * it is very easy to do it ourself
709       #f.write(YAML::dump(api_db))
710       f.write("--- ")
711       $known_entries.keys.sort.each { |k|
712         f.write("\n#{k}: |-\n  ")
713         f.write($api_entries[$known_entries[k]].gsub("\n","\n  "))
714       }
715       f.write("\n")
716     }
717   end
718
719   def self.updatedb_from_input(dbfile, inputfile)
720     parse_headers
721     load_database(dbfile, with_windows=true)
722     doc = YAML::load_file(inputfile)
723     doc.delete(-1)
724     doc.each_key {|i|
725       next if $known_entries[i]
726       $known_entries[i]=doc[i]
727     }
728     self.savedb(dbfile)
729   end
730
731 end
732
733 ############################################################
734 ############################################################
735 ############################################################
736
737 ### Main program
738
739 require 'optparse'
740
741 options = {}
742 OptionParser.new do |opts|
743   opts.banner = "Usage: cd_generator.rb [options] mode"
744
745   opts.on("-d", "--database FILE", String, "YAML file (default ocl_interface.yaml)") do |v|
746     options[:database] = v
747   end
748   opts.on("-i", "--input FILE", String,
749           "binding between OpenCL functions and entry number (required for update-database)") \
750   do |v|
751     options[:input] = v
752   end
753   opts.on("-s", "--[no-]system-headers", 
754           "Look for OpenCL functions in system header files") \
755   do |v|
756     options[:"system-headers"] = v
757   end
758   opts.on("-m", "--mode [MODE]", [:database, :generate, :"update-database"],
759           "Select mode (database, generate, update-database)") do |m|
760     options[:mode] = m
761   end
762 end.parse!
763
764 if !options[:database] then
765   options[:database] = "ocl_interface.yaml"
766 end
767
768 if !options[:mode] then
769   raise "--mode option required"
770 end
771 if options[:mode] == :generate then
772   if !options[:"system-headers"] then
773     IcdGenerator.generate_sources(from_headers=false, from_database=true, database=options[:database])
774   else
775     IcdGenerator.generate_sources(from_headers=true, from_database=false)
776   end
777 elsif options[:mode] == :"update-database" then
778   if !options[:input] then
779     raise "--input option required"
780   end
781   IcdGenerator.updatedb_from_input(options[:database], options[:input])
782 elsif options[:mode] == :database then
783   IcdGenerator.generate_from_database(options[:database])
784 else
785   raise "Mode must be one of generate, database or update-database not #{options[:mode]}" 
786 end
787