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