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