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