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