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