improve clGetExtensionFunctionAddress{,ForPlatform}
[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_SUFFIX__(VERSION_[0-9_]+)[^0-9_]/).join('').
385         gsub(/.* CL_API_SUFFIX__(VERSION_[0-9_]+)[^0-9_].*$/, '\1')
386       if (version != '') then
387         ocl_icd_header += '#ifdef CL_'+version+"\n"
388       end
389       ocl_icd_header += entry.gsub("\r","").
390         sub(/CL_API_CALL\n?(.*?)\(/m,'(CL_API_CALL*\1)('+"\n  ").
391         gsub(/\) (CL_API_SUFFIX__VERSION)/m,"\n) \\1").gsub(/\s*$/,'').
392         gsub(/^[\t ]+/,"    ").gsub(/^([^\t ])/, '  \1') + "\n"
393       if (version != '') then
394         ocl_icd_header += '#else'+"\n"
395         ocl_icd_header += '  CL_API_ENTRY cl_int (CL_API_CALL* clUnknown'+nb.to_s+")(void);\n"
396         ocl_icd_header += '#endif'+"\n"
397       end
398       ocl_icd_header += "\n"
399       nb=nb+1
400     }
401     ocl_icd_header += "};\n"
402     ocl_icd_header += "#endif\n\n"
403     return ocl_icd_header
404   end
405
406   def self.generate_ocl_icd_loader_header
407     ocl_icd_header = "/**\n#{$license}\n*/\n\n"
408     ocl_icd_header += "#include \"ocl_icd.h\"\n\n"
409     ocl_icd_header += <<EOF
410
411 struct func_desc {
412   const char* name;
413   void(*const addr)(void);
414 };
415 typedef __typeof__(clGetExtensionFunctionAddress) *clGetExtensionFunctionAddress_fn;
416 extern const struct func_desc function_description[];
417 struct vendor_icd {
418   cl_uint       num_platforms;
419   void *        dl_handle;
420   clGetExtensionFunctionAddress_fn ext_fn_ptr;
421 };
422
423 struct platform_icd {
424   char *        extension_suffix;
425   char *        version;
426   struct vendor_icd *vicd;
427   cl_platform_id pid;
428   cl_uint       ngpus; /* number of GPU devices */
429   cl_uint       ncpus; /* number of CPU devices */
430   cl_uint       ndevs; /* total number of devices, of all types */
431 };
432
433 EOF
434     ocl_icd_header += "extern struct _cl_icd_dispatch master_dispatch;\n"
435     $cl_objects.each { |o|
436       ocl_icd_header += "struct _cl_#{o} { struct _cl_icd_dispatch *dispatch; };\n"
437     }
438     return ocl_icd_header
439   end
440
441   def self.generate_ocl_icd_loader_map
442     ocl_icd_loader_map = "/**\n#{$license}\n*/\n\n"
443     prev_version=""
444     $versions_entries.keys.sort.each { |version|
445       ocl_icd_loader_map += "OPENCL_#{version.sub('_','.')} {\n";
446       ocl_icd_loader_map += "  global:\n";
447       $versions_entries[version].each { |symb|
448         ocl_icd_loader_map += "    #{symb};\n"
449       }
450       if (prev_version == "") then
451         ocl_icd_loader_map += "  local:\n";
452         ocl_icd_loader_map += "    *;\n";
453       end
454       ocl_icd_loader_map += "} #{prev_version};\n\n";
455       prev_version="OPENCL_#{version.sub('_','.')}";
456     }
457     return ocl_icd_loader_map
458   end
459  
460   def self.generate_ocl_icd_bindings_source
461     ocl_icd_bindings_source = "/**\n#{$license}\n*/\n"
462     ocl_icd_bindings_source += "#include \"ocl_icd.h\"\n"
463     ocl_icd_bindings_source += "struct _cl_icd_dispatch master_dispatch = {\n"
464     ($api_entries.length+$buff-1).times { |i|
465       if( $known_entries[i] ) then 
466         ocl_icd_bindings_source += "  #{$known_entries[i]},\n"
467       else
468         ocl_icd_bindings_source += "  (void *) NULL,\n"
469       end
470     }
471     if( $known_entries[$api_entries.length+$buff-1] ) then
472       ocl_icd_bindings_source += "  #{$known_entries[$api_entries.length+$buff-1]}\n"
473     else
474       ocl_icd_bindings_source += "  (void *) NULL\n"
475     end
476     ocl_icd_bindings_source += "};\n"
477     ocl_icd_bindings_source += <<EOF
478
479 CL_API_ENTRY cl_int CL_API_CALL clIcdGetPlatformIDsKHR(  
480              cl_uint num_entries, 
481              cl_platform_id *platforms,
482              cl_uint *num_platforms) {
483   if( platforms == NULL && num_platforms == NULL )
484     return CL_INVALID_VALUE;
485   if( num_entries == 0 && platforms != NULL )
486     return CL_INVALID_VALUE;
487 #error You have to fill the commented lines with corresponding variables from your library
488 //  if( your_number_of_platforms == 0)
489 //    return CL_PLATFORM_NOT_FOUND_KHR;
490 //  if( num_platforms != NULL )
491 //    *num_platforms = your_number_of_platforms;
492   if( platforms != NULL ) {
493     cl_uint i;
494 //    for( i=0; i<(your_number_of_platforms<num_entries?your_number_of_platforms:num_entries); i++)
495 //      platforms[i] = &your_platforms[i];
496   }
497   return CL_SUCCESS;
498 }
499
500 CL_API_ENTRY void * CL_API_CALL clGetExtensionFunctionAddress(
501              const char *   func_name) CL_API_SUFFIX__VERSION_1_0 {
502 #error You have to fill this function with your extensions of incorporate these lines in your version
503   if( func_name != NULL &&  strcmp("clIcdGetPlatformIDsKHR", func_name) == 0 )
504     return (void *)clIcdGetPlatformIDsKHR;
505   return NULL;
506 }
507 CL_API_ENTRY cl_int CL_API_CALL clGetPlatformInfo(
508              cl_platform_id   platform, 
509              cl_platform_info param_name,
510              size_t           param_value_size, 
511              void *           param_value,
512              size_t *         param_value_size_ret) CL_API_SUFFIX__VERSION_1_0 {
513 #error You ahve to fill this function with your information or assert that your version responds to CL_PLATFORM_ICD_SUFFIX_KHR
514 //  char cl_platform_profile[] = "FULL_PROFILE";
515 //  char cl_platform_version[] = "OpenCL 1.1";
516 //  char cl_platform_name[] = "DummyCL";
517 //  char cl_platform_vendor[] = "LIG";
518 //  char cl_platform_extensions[] = "cl_khr_icd";
519 //  char cl_platform_icd_suffix_khr[] = "DUMMY";
520   size_t size_string;
521   char * string_p;
522   if( platform != NULL ) {
523     int found = 0;
524     int i;
525     for(i=0; i<num_master_platforms; i++) {
526       if( platform == &master_platforms[i] )
527         found = 1;
528     }
529     if(!found)
530       return CL_INVALID_PLATFORM;
531   }
532   switch ( param_name ) {
533     case CL_PLATFORM_PROFILE:
534       string_p = cl_platform_profile;
535       size_string = sizeof(cl_platform_profile);
536       break;
537     case CL_PLATFORM_VERSION:
538       string_p = cl_platform_version;
539       size_string = sizeof(cl_platform_version);
540       break;
541     case CL_PLATFORM_NAME:
542       string_p = cl_platform_name;
543       size_string = sizeof(cl_platform_name);
544       break;
545     case CL_PLATFORM_VENDOR:
546       string_p = cl_platform_vendor;
547       size_string = sizeof(cl_platform_vendor);
548       break;
549     case CL_PLATFORM_EXTENSIONS:
550       string_p = cl_platform_extensions;
551       size_string = sizeof(cl_platform_extensions);
552       break;
553     case CL_PLATFORM_ICD_SUFFIX_KHR:
554       string_p = cl_platform_icd_suffix_khr;
555       size_string = sizeof(cl_platform_icd_suffix_khr);
556       break;
557     default:
558       return CL_INVALID_VALUE;
559       break;
560   }
561   if( param_value != NULL ) {
562     if( size_string > param_value_size )
563       return CL_INVALID_VALUE;
564     memcpy(param_value, string_p, size_string);
565   }
566   if( param_value_size_ret != NULL )
567     *param_value_size_ret = size_string;
568   return CL_SUCCESS;
569 }
570 EOF
571     return ocl_icd_bindings_source
572   end
573
574   def self.generate_get_extension_address_for_platform
575     src = <<EOF
576   if( func_name == NULL )
577     return NULL;
578   struct func_desc const * fn=&function_description[0];
579   int lenfn=strlen(func_name);
580   if (lenfn > 3 &&
581       (strcmp(func_name+lenfn-3, "KHR")==0 || strcmp(func_name+lenfn-3, "EXT")==0)) {
582     while (fn->name != NULL) {
583       if (strcmp(func_name, fn->name)==0)
584         RETURN(fn->addr);
585       fn++;
586     }
587   }
588 EOF
589     return src
590   end
591
592   def self.generate_ocl_icd_loader_gen_source
593     skip_funcs = $specific_loader_funcs
594     ocl_icd_loader_gen_source = "/**\n#{$license}\n*/\n"
595     ocl_icd_loader_gen_source += "#include <string.h>\n"
596     ocl_icd_loader_gen_source += "#include \"ocl_icd_loader.h\"\n"
597     ocl_icd_loader_gen_source += "#define DEBUG_OCL_ICD_PROVIDE_DUMP_FIELD\n"
598     ocl_icd_loader_gen_source += "#include \"ocl_icd_debug.h\"\n"
599     $api_entries.each { |func_name, entry|
600       next if skip_funcs.include?(func_name)
601       clean_entry = entry.sub(/(.*\)).*/m,'\1').gsub("/*","").gsub("*/","").gsub("\r","") + "{\n"
602       return_type = entry.match(/CL_API_ENTRY (.*) CL_API_CALL/)[1]
603       parameters = clean_entry.match(/\(.*\)/m)[0][1..-2]
604       parameters.gsub!(/\[.*?\]/,"")
605       parameters.sub!(/\(.*?\*\s*(.*?)\)\s*\(.*?\)/m,'\1')
606       ocl_icd_loader_gen_source += clean_entry.gsub(/\*\[.*?\]/,"*  ").gsub(/\[.+?\]/,"")
607       first_parameter = parameters.match(/.*?\,/m)
608       if not first_parameter then
609         first_parameter =  parameters.match(/.*/m)[0]
610       else
611         first_parameter = first_parameter[0][0..-2]
612       end
613       fps = first_parameter.split
614       ocl_icd_loader_gen_source += "  debug_trace();\n"
615       ocl_icd_loader_gen_source += generate_get_extension_address_for_platform if func_name == "clGetExtensionFunctionAddressForPlatform"
616       raise "Unsupported data_type #{fps[0]}" if not $cl_data_type_error[fps[0]]
617       ps = parameters.split(",")
618       ps = ps.collect { |p|
619         p = p.split
620         p = p[-1].gsub("*","")
621       }
622       error_handler = lambda {
623          if(ps.include?("errcode_ret")) then
624           ocl_icd_loader_gen_source += "    if( errcode_ret != NULL ) {\n";
625           ocl_icd_loader_gen_source += "      *errcode_ret = #{$cl_data_type_error[fps[0]]};\n"
626           ocl_icd_loader_gen_source += "    }\n"
627           if return_type != "void" then
628             ocl_icd_loader_gen_source += "    RETURN(NULL);\n"
629           else
630             ocl_icd_loader_gen_source += "    return;\n"
631           end
632         elsif ($non_standard_error.include?(func_name)) then
633           if return_type != "void" then
634             ocl_icd_loader_gen_source += "    RETURN(NULL);\n"
635           else
636             ocl_icd_loader_gen_source += "    return;\n"
637           end
638         else
639           if return_type != "void" then
640             ocl_icd_loader_gen_source += "    RETURN(#{$cl_data_type_error[fps[0]]});\n" if return_type != "void"
641           else
642             ocl_icd_loader_gen_source += "    return;\n"
643           end
644         end
645       }
646        
647       if(fps[0] == "cl_platform_id") then
648         ocl_icd_loader_gen_source += "  #{fps[1]}=selectPlatformID(#{fps[1]});\n"
649       end
650       ocl_icd_loader_gen_source += "  if( (struct _#{fps[0]} *)#{fps[1]} == NULL) {\n"
651       error_handler.call
652       ocl_icd_loader_gen_source += "  }\n"
653       if return_type != "void" then
654         return_debug="RETURN"
655       else
656         return_debug="return"
657       end
658       ocl_icd_loader_gen_source += "  #{return_debug}(((struct _#{fps[0]} *)#{fps[1]})->dispatch->#{func_name}("
659       ocl_icd_loader_gen_source += ps.join(", ")
660       ocl_icd_loader_gen_source += "));\n"
661       ocl_icd_loader_gen_source += "}\n\n"
662     }
663     ocl_icd_loader_gen_source += "#pragma GCC visibility push(hidden)\n\n"
664     skip_funcs = $specific_loader_funcs
665     $api_entries.each { |func_name, entry|
666       #next if func_name.match(/EXT$/)
667       #next if func_name.match(/KHR$/)
668       if (skip_funcs.include?(func_name)) then
669         ocl_icd_loader_gen_source += "extern typeof(#{func_name}) #{func_name}_hid;\n"
670       else
671         ocl_icd_loader_gen_source += "typeof(#{func_name}) #{func_name}_hid __attribute__ ((alias (\"#{func_name}\"), visibility(\"hidden\")));\n"
672       end
673     }
674     ocl_icd_loader_gen_source += "\n\nstruct func_desc const function_description[]= {\n"
675     $api_entries.each { |func_name, entry|
676       #next if func_name.match(/EXT$/)
677       #next if func_name.match(/KHR$/)
678       ocl_icd_loader_gen_source += "  {\"#{func_name}\", (void(* const)(void))&#{func_name}_hid },\n"
679     }
680     ocl_icd_loader_gen_source += <<EOF
681   {NULL, NULL}
682 };
683
684 #ifdef DEBUG_OCL_ICD
685 void dump_platform(clGEFA_t f, cl_platform_id pid) {
686   debug(D_ALWAYS, "platform @%p:  name=field_in_struct [clGetExtensionFunctionAddress(name)/clGetExtensionFunctionAddressForPlatform(name)]", pid);
687 EOF
688     $api_entries_array.each { |entry|
689       e = entry.gsub("\r"," ").gsub("\n"," ").gsub("\t"," ").
690         sub(/.*CL_API_CALL *([^ ()]*)[ ()].*$/m, '\1')
691       ocl_icd_loader_gen_source += "  dump_field(pid, f, #{e});\n"
692     }
693
694     ocl_icd_loader_gen_source += <<EOF
695 }
696 #endif
697
698 #pragma GCC visibility pop
699
700 EOF
701     return ocl_icd_loader_gen_source;
702   end
703   
704   def self.generate_from_database(yamlfile)
705     load_database(yamlfile)
706     File.open('ocl_icd.h','w') { |f|
707       f.puts generate_ocl_icd_header
708     }
709     File.open('ocl_icd_loader_gen.h','w') { |f|
710       f.puts generate_ocl_icd_loader_header
711     }
712     File.open('ocl_icd_loader_gen.map','w') { |f|
713       f.puts generate_ocl_icd_loader_map
714     }
715     File.open('ocl_icd_bindings.c','w') { |f|
716       f.puts generate_ocl_icd_bindings_source
717     }
718     File.open('ocl_icd_loader_gen.c','w') { |f|
719       f.puts generate_ocl_icd_loader_gen_source
720     }
721   end
722
723   ##########################################################
724   ##########################################################
725   # update-database mode
726   def self.savedb(yamlfile)
727     File::open(yamlfile,"w") { |f|
728       f.write($license.gsub(/^/,"# "))
729       f.write( <<EOF
730
731 # In Intel (OpenCL 1.1):
732 # * clSetCommandQueueProperty(13): nil (deprecated in 1.1)
733 # * clGetGLContextInfoKHR(74): function present with its symbol
734 # * 75-80: nil
735 # * 92: correspond to symbol clGetKernelArgInfo (first abandonned version?)
736 # * 93-: garbage
737 # In nvidia (OpenCL 1.1):
738 # * clGetGLContextInfoKHR(74): function present but no symbol
739 # * 75-80: nil
740 # * 89-: nil
741 # * only two OpenCL symbols: clGetPlatformInfo(1) and clGetExtensionFunctionAddress(65)
742 # In AMD (OpenCL 1.2):
743 # * clGetPlatformIDs(0): nil (symbol present)
744 # * clGetGLContextInfoKHR(74): function present but no symbol
745 # * 75-80: nil
746 # * 92: nil
747 # * 109-118: nil
748 # * 119-: garbage
749
750 EOF
751 )
752       # Not using YAML::dump as:
753       # * keys are not ordered
754       # * strings are badly formatted in new YAML ruby implementation (psych)
755       # * it is very easy to do it ourself
756       #f.write(YAML::dump(api_db))
757       f.write("--- ")
758       $known_entries.keys.sort.each { |k|
759         f.write("\n#{k}: |-\n  ")
760         f.write($api_entries[$known_entries[k]].gsub("\n","\n  "))
761       }
762       f.write("\n")
763     }
764   end
765
766   def self.updatedb_from_input(dbfile, inputfile)
767     parse_headers
768     load_database(dbfile, with_windows=true)
769     doc = YAML::load_file(inputfile)
770     doc.delete(-1)
771     doc.each_key {|i|
772       next if $known_entries[i]
773       $known_entries[i]=doc[i]
774     }
775     self.savedb(dbfile)
776   end
777
778 end
779
780 ############################################################
781 ############################################################
782 ############################################################
783
784 ### Main program
785
786 require 'optparse'
787
788 options = {}
789 OptionParser.new do |opts|
790   opts.banner = "Usage: cd_generator.rb [options] mode"
791
792   opts.on("-d", "--database FILE", String, "YAML file (default ocl_interface.yaml)") do |v|
793     options[:database] = v
794   end
795   opts.on("-i", "--input FILE", String,
796           "binding between OpenCL functions and entry number (required for update-database)") \
797   do |v|
798     options[:input] = v
799   end
800   opts.on("-s", "--[no-]system-headers", 
801           "Look for OpenCL functions in system header files") \
802   do |v|
803     options[:"system-headers"] = v
804   end
805   opts.on("-m", "--mode [MODE]", [:database, :generate, :"update-database"],
806           "Select mode (database, generate, update-database)") do |m|
807     options[:mode] = m
808   end
809 end.parse!
810
811 if !options[:database] then
812   options[:database] = "ocl_interface.yaml"
813 end
814
815 if !options[:mode] then
816   raise "--mode option required"
817 end
818 if options[:mode] == :generate then
819   if !options[:"system-headers"] then
820     IcdGenerator.generate_sources(from_headers=false, from_database=true, database=options[:database])
821   else
822     IcdGenerator.generate_sources(from_headers=true, from_database=false)
823   end
824 elsif options[:mode] == :"update-database" then
825   if !options[:input] then
826     raise "--input option required"
827   end
828   IcdGenerator.updatedb_from_input(options[:database], options[:input])
829 elsif options[:mode] == :database then
830   IcdGenerator.generate_from_database(options[:database])
831 else
832   raise "Mode must be one of generate, database or update-database not #{options[:mode]}" 
833 end
834