Avoid warnings with 2.0 OpenCL headers when using 1.2 deprecated functions
[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   $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 += "#  include <CL/opencl.h>\n"
227     run_dummy_icd += self.include_headers
228     run_dummy_icd += "#pragma GCC diagnostic pop\n"
229     run_dummy_icd += "\n\n"
230     run_dummy_icd += "typedef CL_API_ENTRY cl_int (CL_API_CALL* oclFuncPtr_fn)(cl_platform_id platform);\n\n"
231     run_dummy_icd += "void call_all_OpenCL_functions(cl_platform_id chosen_platform) {\n"
232     run_dummy_icd += "  oclFuncPtr_fn oclFuncPtr;\n"
233     run_dummy_icd += "  cl_context_properties properties[] = { CL_CONTEXT_PLATFORM, (cl_context_properties)chosen_platform, 0 };\n"
234     $api_entries.each_key { |func_name|
235        next if $forbidden_funcs.include?(func_name)
236        if func_name == "clCreateContext" then
237          run_dummy_icd += "  #{func_name}(properties,1,(cl_device_id*)&chosen_platform,NULL,NULL,NULL);\n"
238        elsif func_name == "clGetGLContextInfoKHR" then
239          run_dummy_icd += "  #{func_name}(properties,CL_CURRENT_DEVICE_FOR_GL_CONTEXT_KHR, 0, NULL, NULL);\n"
240        elsif func_name == "clCreateContextFromType" then
241          run_dummy_icd += "  #{func_name}(properties,CL_DEVICE_TYPE_CPU,NULL,NULL,NULL);\n"
242        elsif func_name == "clWaitForEvents" then
243          run_dummy_icd += "  #{func_name}(1,(cl_event*)&chosen_platform);\n"
244        elsif func_name == "clGetExtensionFunctionAddressForPlatform" then
245          run_dummy_icd += "  #{func_name}((cl_platform_id)chosen_platform, \"clIcdGetPlatformIDsKHR\");\n"
246        else
247          run_dummy_icd += "  oclFuncPtr = (oclFuncPtr_fn)" + func_name + ";\n"
248          run_dummy_icd += "  oclFuncPtr(chosen_platform);\n"
249        end
250        run_dummy_icd += "  printf(\"%s\\n\", \"#{func_name}\");"
251        run_dummy_icd += "  fflush(NULL);\n"
252     }
253     run_dummy_icd += "  return;\n}\n"
254     return run_dummy_icd
255   end
256
257   def self.generate_run_dummy_icd_weak_source
258     run_dummy_icd_weak = "/**\n#{$license}\n*/\n"
259     run_dummy_icd_weak += <<EOF
260 #define _GNU_SOURCE 1
261 #include <stdio.h>
262 #include <dlfcn.h>
263
264 #define F(f) \\
265 __attribute__((weak)) int f (void* arg, void* arg2) { \\
266   void (* p)(void*, void*)=NULL; \\
267   p=dlsym(RTLD_NEXT, #f); \\
268   if (p) { \\
269     (*p)(arg, arg2); \\
270   } else { \\
271     printf("-1 : "); \\
272   } \\
273   return 0; \\
274
275
276 EOF
277     $api_entries.each_key { |func_name|
278        next if $noweak_funcs.include?(func_name)
279        run_dummy_icd_weak += "F(#{func_name})\n"
280     }
281     return run_dummy_icd_weak
282   end
283
284   def self.generate_sources(from_headers=true, from_database=false, database=nil)
285     if from_headers then        
286       parse_headers
287     end
288     if from_database then
289       load_database(database)
290     end
291     File.open('libdummy_icd_gen.h','w') { |f|
292       f.puts generate_libdummy_icd_header
293     }
294     File.open('libdummy_icd_gen.c','w') { |f|
295       f.puts generate_libdummy_icd_source
296     }
297     File.open('run_dummy_icd_gen.c','w') { |f|
298       f.puts generate_run_dummy_icd_source
299     }
300     File.open('run_dummy_icd_weak_gen.c','w') { |f|
301       f.puts generate_run_dummy_icd_weak_source
302     }
303   end
304
305   ##########################################################
306   ##########################################################
307   # database mode
308   def self.generate_ocl_icd_header
309     ocl_icd_header = "/**\n#{$license}\n*/\n\n"
310     ocl_icd_header += "#ifndef OCL_ICD_H\n"
311     ocl_icd_header += "#define OCL_ICD_H\n"
312     ocl_icd_header += "#pragma GCC diagnostic push\n"
313     ocl_icd_header += "#  pragma GCC diagnostic ignored \"-Wcpp\"\n"
314     ocl_icd_header += "#  define CL_USE_DEPRECATED_OPENCL_1_0_APIS\n"
315     ocl_icd_header += "#  define CL_USE_DEPRECATED_OPENCL_1_1_APIS\n"
316     ocl_icd_header += "#  define CL_USE_DEPRECATED_OPENCL_1_2_APIS\n"
317     ocl_icd_header += "#  include <CL/opencl.h>\n"
318     ocl_icd_header += self.include_headers
319     ocl_icd_header += "#pragma GCC diagnostic pop\n"
320     ocl_icd_header += <<EOF
321
322 #define OCL_ICD_API_VERSION     1
323 #define OCL_ICD_IDENTIFIED_FUNCTIONS    #{$known_entries.count}
324
325 struct _cl_icd_dispatch {
326 EOF
327     $api_entries_array.each { |entry|
328       ocl_icd_header += entry.gsub("\r","").
329         sub(/CL_API_CALL\n?(.*?)\(/m,'(CL_API_CALL*\1)('+"\n  ").
330         gsub(/\) (CL_API_SUFFIX__VERSION)/m,"\n) \\1").gsub(/\s*$/,'').
331         gsub(/^[\t ]+/,"    ").gsub(/^([^\t ])/, '  \1') + "\n\n"
332     }
333     ocl_icd_header += "};\n"
334     ocl_icd_header += "#endif\n\n"
335     return ocl_icd_header
336   end
337
338   def self.generate_ocl_icd_loader_header
339     ocl_icd_header = "/**\n#{$license}\n*/\n\n"
340     ocl_icd_header += "#include \"ocl_icd.h\"\n\n"
341     ocl_icd_header += <<EOF
342
343 struct func_desc {
344   const char* name;
345   void(*const addr)(void);
346 };
347 typedef __typeof__(clGetExtensionFunctionAddress) *clGetExtensionFunctionAddress_fn;
348 extern const struct func_desc function_description[];
349 struct vendor_icd {
350   cl_uint       num_platforms;
351   cl_uint       first_platform;
352   void *        dl_handle;
353   clGetExtensionFunctionAddress_fn ext_fn_ptr;
354 };
355 struct platform_icd {
356   char *         extension_suffix;
357   char *         version;
358   struct vendor_icd *vicd;
359   cl_platform_id pid;
360 };
361
362 EOF
363     ocl_icd_header += "extern struct _cl_icd_dispatch master_dispatch;\n"
364     $cl_objects.each { |o|
365       ocl_icd_header += "struct _cl_#{o} { struct _cl_icd_dispatch *dispatch; };\n"
366     }
367     return ocl_icd_header
368   end
369
370   def self.generate_ocl_icd_loader_map
371     ocl_icd_loader_map = "/**\n#{$license}\n*/\n\n"
372     prev_version=""
373     $versions_entries.keys.sort.each { |version|
374       ocl_icd_loader_map += "OPENCL_#{version.sub('_','.')} {\n";
375       ocl_icd_loader_map += "  global:\n";
376       $versions_entries[version].each { |symb|
377         ocl_icd_loader_map += "    #{symb};\n"
378       }
379       if (prev_version == "") then
380         ocl_icd_loader_map += "  local:\n";
381         ocl_icd_loader_map += "    *;\n";
382       end
383       ocl_icd_loader_map += "} #{prev_version};\n\n";
384       prev_version="OPENCL_#{version.sub('_','.')}";
385     }
386     return ocl_icd_loader_map
387   end
388  
389   def self.generate_ocl_icd_bindings_source
390     ocl_icd_bindings_source = "/**\n#{$license}\n*/\n"
391     ocl_icd_bindings_source += "#include \"ocl_icd.h\"\n"
392     ocl_icd_bindings_source += "struct _cl_icd_dispatch master_dispatch = {\n"
393     ($api_entries.length+$buff-1).times { |i|
394       if( $known_entries[i] ) then 
395         ocl_icd_bindings_source += "  #{$known_entries[i]},\n"
396       else
397         ocl_icd_bindings_source += "  (void *) NULL,\n"
398       end
399     }
400     if( $known_entries[$api_entries.length+$buff-1] ) then
401       ocl_icd_bindings_source += "  #{$known_entries[$api_entries.length+$buff-1]}\n"
402     else
403       ocl_icd_bindings_source += "  (void *) NULL\n"
404     end
405     ocl_icd_bindings_source += "};\n"
406     ocl_icd_bindings_source += <<EOF
407
408 CL_API_ENTRY cl_int CL_API_CALL clIcdGetPlatformIDsKHR(  
409              cl_uint num_entries, 
410              cl_platform_id *platforms,
411              cl_uint *num_platforms) {
412   if( platforms == NULL && num_platforms == NULL )
413     return CL_INVALID_VALUE;
414   if( num_entries == 0 && platforms != NULL )
415     return CL_INVALID_VALUE;
416 #error You have to fill the commented lines with corresponding variables from your library
417 //  if( your_number_of_platforms == 0)
418 //    return CL_PLATFORM_NOT_FOUND_KHR;
419 //  if( num_platforms != NULL )
420 //    *num_platforms = your_number_of_platforms;
421   if( platforms != NULL ) {
422     cl_uint i;
423 //    for( i=0; i<(your_number_of_platforms<num_entries?your_number_of_platforms:num_entries); i++)
424 //      platforms[i] = &your_platforms[i];
425   }
426   return CL_SUCCESS;
427 }
428
429 CL_API_ENTRY void * CL_API_CALL clGetExtensionFunctionAddress(
430              const char *   func_name) CL_API_SUFFIX__VERSION_1_0 {
431 #error You have to fill this function with your extensions of incorporate these lines in your version
432   if( func_name != NULL &&  strcmp("clIcdGetPlatformIDsKHR", func_name) == 0 )
433     return (void *)clIcdGetPlatformIDsKHR;
434   return NULL;
435 }
436 CL_API_ENTRY cl_int CL_API_CALL clGetPlatformInfo(
437              cl_platform_id   platform, 
438              cl_platform_info param_name,
439              size_t           param_value_size, 
440              void *           param_value,
441              size_t *         param_value_size_ret) CL_API_SUFFIX__VERSION_1_0 {
442 #error You ahve to fill this function with your information or assert that your version responds to CL_PLATFORM_ICD_SUFFIX_KHR
443 //  char cl_platform_profile[] = "FULL_PROFILE";
444 //  char cl_platform_version[] = "OpenCL 1.1";
445 //  char cl_platform_name[] = "DummyCL";
446 //  char cl_platform_vendor[] = "LIG";
447 //  char cl_platform_extensions[] = "cl_khr_icd";
448 //  char cl_platform_icd_suffix_khr[] = "DUMMY";
449   size_t size_string;
450   char * string_p;
451   if( platform != NULL ) {
452     int found = 0;
453     int i;
454     for(i=0; i<num_master_platforms; i++) {
455       if( platform == &master_platforms[i] )
456         found = 1;
457     }
458     if(!found)
459       return CL_INVALID_PLATFORM;
460   }
461   switch ( param_name ) {
462     case CL_PLATFORM_PROFILE:
463       string_p = cl_platform_profile;
464       size_string = sizeof(cl_platform_profile);
465       break;
466     case CL_PLATFORM_VERSION:
467       string_p = cl_platform_version;
468       size_string = sizeof(cl_platform_version);
469       break;
470     case CL_PLATFORM_NAME:
471       string_p = cl_platform_name;
472       size_string = sizeof(cl_platform_name);
473       break;
474     case CL_PLATFORM_VENDOR:
475       string_p = cl_platform_vendor;
476       size_string = sizeof(cl_platform_vendor);
477       break;
478     case CL_PLATFORM_EXTENSIONS:
479       string_p = cl_platform_extensions;
480       size_string = sizeof(cl_platform_extensions);
481       break;
482     case CL_PLATFORM_ICD_SUFFIX_KHR:
483       string_p = cl_platform_icd_suffix_khr;
484       size_string = sizeof(cl_platform_icd_suffix_khr);
485       break;
486     default:
487       return CL_INVALID_VALUE;
488       break;
489   }
490   if( param_value != NULL ) {
491     if( size_string > param_value_size )
492       return CL_INVALID_VALUE;
493     memcpy(param_value, string_p, size_string);
494   }
495   if( param_value_size_ret != NULL )
496     *param_value_size_ret = size_string;
497   return CL_SUCCESS;
498 }
499 EOF
500     return ocl_icd_bindings_source
501   end
502
503   def self.generate_ocl_icd_loader_gen_source
504     skip_funcs = $specific_loader_funcs
505     ocl_icd_loader_gen_source = "/**\n#{$license}\n*/\n"
506     ocl_icd_loader_gen_source += "#include \"ocl_icd_loader.h\"\n"
507     ocl_icd_loader_gen_source += "#define DEBUG_OCL_ICD_PROVIDE_DUMP_FIELD\n"
508     ocl_icd_loader_gen_source += "#include \"ocl_icd_debug.h\"\n"
509     $api_entries.each { |func_name, entry|
510       next if skip_funcs.include?(func_name)
511       clean_entry = entry.sub(/(.*\)).*/m,'\1').gsub("/*","").gsub("*/","").gsub("\r","") + "{\n"
512       return_type = entry.match(/CL_API_ENTRY (.*) CL_API_CALL/)[1]
513       parameters = clean_entry.match(/\(.*\)/m)[0][1..-2]
514       parameters.gsub!(/\[.*?\]/,"")
515       parameters.sub!(/\(.*?\*\s*(.*?)\)\s*\(.*?\)/m,'\1')
516       ocl_icd_loader_gen_source += clean_entry.gsub(/\*\[.*?\]/,"*  ").gsub(/\[.+?\]/,"")
517       first_parameter = parameters.match(/.*?\,/m)
518       if not first_parameter then
519         first_parameter =  parameters.match(/.*/m)[0]
520       else
521         first_parameter = first_parameter[0][0..-2]
522       end
523       fps = first_parameter.split
524       ocl_icd_loader_gen_source += "  debug_trace();\n"
525       raise "Unsupported data_type #{fps[0]}" if not $cl_data_type_error[fps[0]]
526       ps = parameters.split(",")
527       ps = ps.collect { |p|
528         p = p.split
529         p = p[-1].gsub("*","")
530       }
531       error_handler = lambda {
532          if(ps.include?("errcode_ret")) then
533           ocl_icd_loader_gen_source += "    if( errcode_ret != NULL ) {\n";
534           ocl_icd_loader_gen_source += "      *errcode_ret = #{$cl_data_type_error[fps[0]]};\n"
535           ocl_icd_loader_gen_source += "    }\n"
536           if return_type != "void" then
537             ocl_icd_loader_gen_source += "    RETURN(NULL);\n"
538           else
539             ocl_icd_loader_gen_source += "    return;\n"
540           end
541         elsif ($non_standard_error.include?(func_name)) then
542           if return_type != "void" then
543             ocl_icd_loader_gen_source += "    RETURN(NULL);\n"
544           else
545             ocl_icd_loader_gen_source += "    return;\n"
546           end
547         else
548           if return_type != "void" then
549             ocl_icd_loader_gen_source += "    RETURN(#{$cl_data_type_error[fps[0]]});\n" if return_type != "void"
550           else
551             ocl_icd_loader_gen_source += "    return;\n"
552           end
553         end
554       }
555        
556       if(fps[0] == "cl_platform_id") then
557         ocl_icd_loader_gen_source += "  #{fps[1]}=selectPlatformID(#{fps[1]});\n"
558       end
559       ocl_icd_loader_gen_source += "  if( (struct _#{fps[0]} *)#{fps[1]} == NULL) {\n"
560       error_handler.call
561       ocl_icd_loader_gen_source += "  }\n"
562       if return_type != "void" then
563         ocl_icd_loader_gen_source += "  RETURN(((struct _#{fps[0]} *)#{fps[1]})->dispatch->#{func_name}("
564         ocl_icd_loader_gen_source += ps.join(", ")
565         ocl_icd_loader_gen_source += "));\n"
566       else
567         ocl_icd_loader_gen_source += "  return;"
568       end
569       ocl_icd_loader_gen_source += "}\n\n"
570     }
571     ocl_icd_loader_gen_source += "#pragma GCC visibility push(hidden)\n\n"
572     skip_funcs = $specific_loader_funcs
573     $api_entries.each { |func_name, entry|
574       #next if func_name.match(/EXT$/)
575       #next if func_name.match(/KHR$/)
576       if (skip_funcs.include?(func_name)) then
577         ocl_icd_loader_gen_source += "extern typeof(#{func_name}) #{func_name}_hid;\n"
578       else
579         ocl_icd_loader_gen_source += "typeof(#{func_name}) #{func_name}_hid __attribute__ ((alias (\"#{func_name}\"), visibility(\"hidden\")));\n"
580       end
581     }
582     ocl_icd_loader_gen_source += "\n\nstruct func_desc const function_description[]= {\n"
583     $api_entries.each { |func_name, entry|
584       #next if func_name.match(/EXT$/)
585       #next if func_name.match(/KHR$/)
586       ocl_icd_loader_gen_source += "  {\"#{func_name}\", (void(* const)(void))&#{func_name}_hid },\n"
587     }
588     ocl_icd_loader_gen_source += <<EOF
589   {NULL, NULL}
590 };
591
592 #ifdef DEBUG_OCL_ICD
593 void dump_platform(clGEFA_t f, cl_platform_id pid) {
594   debug(D_ALWAYS, "platform @%p:  name=field_in_struct [clGetExtensionFunctionAddress(name)/clGetExtensionFunctionAddressForPlatform(name)]", pid);
595 EOF
596     $api_entries_array.each { |entry|
597       e = entry.gsub("\r"," ").gsub("\n"," ").gsub("\t"," ").
598         sub(/.*CL_API_CALL *([^ ()]*)[ ()].*$/m, '\1')
599       ocl_icd_loader_gen_source += "  dump_field(pid, f, #{e});\n"
600     }
601
602     ocl_icd_loader_gen_source += <<EOF
603 }
604 #endif
605
606 #pragma GCC visibility pop
607
608 EOF
609     return ocl_icd_loader_gen_source;
610   end
611   
612   def self.generate_from_database(yamlfile)
613     load_database(yamlfile)
614     File.open('ocl_icd.h','w') { |f|
615       f.puts generate_ocl_icd_header
616     }
617     File.open('ocl_icd_loader_gen.h','w') { |f|
618       f.puts generate_ocl_icd_loader_header
619     }
620     File.open('ocl_icd_loader_gen.map','w') { |f|
621       f.puts generate_ocl_icd_loader_map
622     }
623     File.open('ocl_icd_bindings.c','w') { |f|
624       f.puts generate_ocl_icd_bindings_source
625     }
626     File.open('ocl_icd_loader_gen.c','w') { |f|
627       f.puts generate_ocl_icd_loader_gen_source
628     }
629   end
630
631   ##########################################################
632   ##########################################################
633   # update-database mode
634   def self.savedb(yamlfile)
635     File::open(yamlfile,"w") { |f|
636       f.write($license.gsub(/^/,"# "))
637       f.write( <<EOF
638
639 # In Intel (OpenCL 1.1):
640 # * clSetCommandQueueProperty(13): nil (deprecated in 1.1)
641 # * clGetGLContextInfoKHR(74): function present with its symbol
642 # * 75-80: nil
643 # * 92: correspond to symbol clGetKernelArgInfo (first abandonned version?)
644 # * 93-: garbage
645 # In nvidia (OpenCL 1.1):
646 # * clGetGLContextInfoKHR(74): function present but no symbol
647 # * 75-80: nil
648 # * 89-: nil
649 # * only two OpenCL symbols: clGetPlatformInfo(1) and clGetExtensionFunctionAddress(65)
650 # In AMD (OpenCL 1.2):
651 # * clGetPlatformIDs(0): nil (symbol present)
652 # * clGetGLContextInfoKHR(74): function present but no symbol
653 # * 75-80: nil
654 # * 92: nil
655 # * 109-118: nil
656 # * 119-: garbage
657
658 EOF
659 )
660       # Not using YAML::dump as:
661       # * keys are not ordered
662       # * strings are badly formatted in new YAML ruby implementation (psych)
663       # * it is very easy to do it ourself
664       #f.write(YAML::dump(api_db))
665       f.write("--- ")
666       $known_entries.keys.sort.each { |k|
667         f.write("\n#{k}: |-\n  ")
668         f.write($api_entries[$known_entries[k]].gsub("\n","\n  "))
669       }
670       f.write("\n")
671     }
672   end
673
674   def self.updatedb_from_input(dbfile, inputfile)
675     parse_headers
676     load_database(dbfile, with_windows=true)
677     doc = YAML::load_file(inputfile)
678     doc.delete(-1)
679     doc.each_key {|i|
680       next if $known_entries[i]
681       $known_entries[i]=doc[i]
682     }
683     self.savedb(dbfile)
684   end
685
686 end
687
688 ############################################################
689 ############################################################
690 ############################################################
691
692 ### Main program
693
694 require 'optparse'
695
696 options = {}
697 OptionParser.new do |opts|
698   opts.banner = "Usage: cd_generator.rb [options] mode"
699
700   opts.on("-d", "--database FILE", String, "YAML file (default ocl_interface.yaml)") do |v|
701     options[:database] = v
702   end
703   opts.on("-i", "--input FILE", String,
704           "binding between OpenCL functions and entry number (required for update-database)") \
705   do |v|
706     options[:input] = v
707   end
708   opts.on("-s", "--[no-]system-headers", 
709           "Look for OpenCL functions in system header files") \
710   do |v|
711     options[:"system-headers"] = v
712   end
713   opts.on("-m", "--mode [MODE]", [:database, :generate, :"update-database"],
714           "Select mode (database, generate, update-database)") do |m|
715     options[:mode] = m
716   end
717 end.parse!
718
719 if !options[:database] then
720   options[:database] = "ocl_interface.yaml"
721 end
722
723 if !options[:mode] then
724   raise "--mode option required"
725 end
726 if options[:mode] == :generate then
727   if !options[:"system-headers"] then
728     IcdGenerator.generate_sources(from_headers=false, from_database=true, database=options[:database])
729   else
730     IcdGenerator.generate_sources(from_headers=true, from_database=false)
731   end
732 elsif options[:mode] == :"update-database" then
733   if !options[:input] then
734     raise "--input option required"
735   end
736   IcdGenerator.updatedb_from_input(options[:database], options[:input])
737 elsif options[:mode] == :database then
738   IcdGenerator.generate_from_database(options[:database])
739 else
740   raise "Mode must be one of generate, database or update-database not #{options[:mode]}" 
741 end
742