=begin Copyright (c) 2012-2015, Brice Videau Copyright (c) 2012-2015, Vincent Danjean All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. =end require 'yaml' module IcdGenerator $api_entries = {} $key_entries = {} $api_entries_array = [] $cl_objects = ["platform_id", "device_id", "context", "command_queue", "mem", "program", "kernel", "event", "sampler"] $know_entries = { 1 => "clGetPlatformInfo", 0 => "clGetPlatformIDs", 2 => "clGetDeviceIDs" } $use_name_in_test = { 1 => "clGetPlatformInfo", 0 => "clGetPlatformIDs", 2 => "clGetDeviceIDs" } # do not call these functions when trying to discover the mapping $forbidden_funcs = [ "clUnloadCompiler", # No parameters so no way to forward to an ICD, OpenCL 1.0 "clGetPlatformIDs", # Implemented directly within the ICD Loader, not forwarded to ICD ] # windows function to ignore when loading the database $windows_funcs = ["clGetDeviceIDsFromD3D10KHR", "clCreateFromD3D10BufferKHR", "clCreateFromD3D10Texture2DKHR", "clCreateFromD3D10Texture3DKHR", "clEnqueueAcquireD3D10ObjectsKHR", "clEnqueueReleaseD3D10ObjectsKHR", "clGetDeviceIDsFromD3D11KHR", "clCreateFromD3D11BufferKHR", "clCreateFromD3D11Texture2DKHR", "clCreateFromD3D11Texture3DKHR", "clEnqueueAcquireD3D11ObjectsKHR", "clEnqueueReleaseD3D11ObjectsKHR", "clGetDeviceIDsFromDX9MediaAdapterKHR", "clCreateFromDX9MediaSurfaceKHR", "clEnqueueAcquireDX9MediaSurfacesKHR", "clEnqueueReleaseDX9MediaSurfacesKHR"] # do not create weak functions for these ones in the discovering program $noweak_funcs = ["clGetExtensionFunctionAddress", "clGetPlatformIDs", "clGetPlatformInfo", "clGetGLContextInfoKHR", "clUnloadCompiler", "clCreateContext", "clCreateContextFromType", "clWaitForEvents"] # functions written specifically in the loader $specific_loader_funcs = ["clGetExtensionFunctionAddress","clGetPlatformIDs", "clGetGLContextInfoKHR", "clUnloadCompiler", "clCreateContext", "clCreateContextFromType", "clWaitForEvents"] $header_files = ["/usr/include/CL/cl.h", "/usr/include/CL/cl_gl.h", "/usr/include/CL/cl_egl.h", "/usr/include/CL/cl_ext.h", "/usr/include/CL/cl_gl_ext.h"] $windows_header_files = ["/usr/include/CL/cl_dx9_media_sharing.h", "/usr/include/CL/cl_d3d11.h", "/usr/include/CL/cl_d3d10.h"] $cl_data_type_error = { "cl_platform_id" => "CL_INVALID_PLATFORM", "cl_device_id" => "CL_INVALID_DEVICE", "cl_context" => "CL_INVALID_CONTEXT", "cl_command_queue" => "CL_INVALID_COMMAND_QUEUE", "cl_mem" => "CL_INVALID_MEM_OBJECT", "cl_program" => "CL_INVALID_PROGRAM", "cl_kernel" => "CL_INVALID_KERNEL", "cl_event" => "CL_INVALID_EVENT", "cl_sampler" => "CL_INVALID_SAMPLER"} $non_standard_error = [ "clGetExtensionFunctionAddressForPlatform", "clSVMAlloc" ] $versions_entries = [] $buff=50 $license = < Copyright (c) 2012-2015, Vincent Danjean All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Do not edit this file. It is automatically generated. EOF ########################################################## ########################################################## # helper functions def self.parse_headers api_entries = [] $header_files.each{ |fname| f = File::open(fname) doc = f.read api_entries += doc.scan(/CL_API_ENTRY.*?;/m) f.close } api_entries.each{ |entry| # puts entry begin entry_name = entry.match(/CL_API_CALL(.*?)\(/m)[1].strip rescue entry_name = entry.match(/(\S*?)\(/m)[1].strip end next if entry_name.match('\*') next if entry_name.match("INTEL") next if entry_name.match("APPLE") $api_entries[entry_name] = entry.gsub("\r","") } # $api_entries.each{ |key, value| # puts "#{key}: #{value}" # } end def self.load_database(yamlfile, with_windows=false) doc = YAML::load_file(yamlfile) $known_entries = {} $api_entries ||= {} $versions_entries = Hash::new { |hash,key| hash[key]=[] } entry_name = "" version = "" doc.each { |key, value| #puts (key.to_s+":: "+value) begin entry_name = value.match(/CL_API_CALL(.*?)\(/m)[1].strip rescue entry_name = value.match(/(\S*?)\(/m)[1].strip end next if (!with_windows) && $windows_funcs.include?(entry_name) version = value.match(/SUFFIX__VERSION_(\d_\d)/m)[1] $versions_entries[version].push(entry_name) $known_entries[key] = entry_name $key_entries[entry_name] = key $api_entries[entry_name] = value } $api_entries_array = [] ($known_entries.length+$buff).times { |i| #puts (i.to_s+": "+$known_entries[i]) if $known_entries[i] then $api_entries_array.push( $api_entries[$known_entries[i]] ) else $api_entries_array.push( "CL_API_ENTRY cl_int CL_API_CALL clUnknown#{i}(void);" ) end } end def self.include_headers headers ="" $header_files.each { |h| if h.match('^/usr/include/') then headers += "#include <#{h[13..-1]}>\n" else headers += "#include \"#{h}\"\n" end } return headers end ########################################################## ########################################################## # generate mode def self.generate_libdummy_icd_header libdummy_icd_structures = "/**\n#{$license}\n*/\n" libdummy_icd_structures += "#include \n" libdummy_icd_structures += self.include_headers libdummy_icd_structures += "\n\nstruct _cl_icd_dispatch;\n" libdummy_icd_structures += "struct _cl_platform_id { struct _cl_icd_dispatch *dispatch; };\n\n" libdummy_icd_structures += "struct _cl_icd_dispatch {\n" ($api_entries.length+$buff).times { |i| if( $known_entries[i] ) then libdummy_icd_structures += " void(*known#{i})(void);\n" else libdummy_icd_structures += " void(*unknown#{i})(void);\n" end } libdummy_icd_structures += "};\n\n" libdummy_icd_structures += "#pragma GCC visibility push(hidden)\n\n" libdummy_icd_structures += "struct _cl_icd_dispatch master_dispatch; \n\n" $use_name_in_test.each { |k, f| libdummy_icd_structures += "typeof(#{f}) INT#{f};\n" } libdummy_icd_structures += "#pragma GCC visibility pop\n\n" return libdummy_icd_structures end def self.generate_libdummy_icd_source libdummy_icd_source = "/**\n#{$license}\n*/\n\n" libdummy_icd_source += "#include \n\n" libdummy_icd_source += "#include \"libdummy_icd_gen.h\"\n\n" libdummy_icd_source += "#include \"libdummy_icd.h\"\n\n" (0...$api_entries.length+$buff).each { |i| libdummy_icd_source += "void dummyFunc#{i}(void){ printf(\"#{i} : \"); fflush(NULL); }\n" } libdummy_icd_source += "\nstruct _cl_icd_dispatch master_dispatch = {\n" comma="," ($api_entries.length+$buff).times { |i| comma="" if (i == $api_entries.length+$buff-1) if( $use_name_in_test[i] ) then libdummy_icd_source += " (void(*)(void))& INT#{$known_entries[i]}#{comma}\n" else libdummy_icd_source += " (void(*)(void))& dummyFunc#{i}#{comma}\n" end } libdummy_icd_source += "};\n" return libdummy_icd_source end def self.generate_run_dummy_icd_source run_dummy_icd = "/**\n#{$license}\n*/\n" run_dummy_icd += "#include \n" run_dummy_icd += "#include \n" run_dummy_icd += "#pragma GCC diagnostic push\n" run_dummy_icd += "# pragma GCC diagnostic ignored \"-Wcpp\"\n" run_dummy_icd += "# define CL_USE_DEPRECATED_OPENCL_1_0_APIS\n" run_dummy_icd += "# define CL_USE_DEPRECATED_OPENCL_1_1_APIS\n" run_dummy_icd += "# define CL_USE_DEPRECATED_OPENCL_1_2_APIS\n" run_dummy_icd += "# include \n" run_dummy_icd += self.include_headers run_dummy_icd += "#pragma GCC diagnostic pop\n" run_dummy_icd += "\n\n" $api_entries.each_key { |func_name| next if $forbidden_funcs.include?(func_name) run_dummy_icd += $api_entries[func_name]+";\n" } run_dummy_icd += "\n\n" run_dummy_icd += "void call_all_OpenCL_functions(cl_platform_id chosen_platform) {\n" run_dummy_icd += " cl_context_properties properties[] = { CL_CONTEXT_PLATFORM, (cl_context_properties)chosen_platform, 0 };\n" $api_entries.each_key { |func_name| next if $forbidden_funcs.include?(func_name) if func_name == "clCreateContext" then run_dummy_icd += " #{func_name}(properties,1,(cl_device_id*)&chosen_platform,NULL,NULL,NULL);\n" elsif func_name == "clGetGLContextInfoKHR" then run_dummy_icd += " #{func_name}(properties,CL_CURRENT_DEVICE_FOR_GL_CONTEXT_KHR, 0, NULL, NULL);\n" elsif func_name == "clCreateContextFromType" then run_dummy_icd += " #{func_name}(properties,CL_DEVICE_TYPE_CPU,NULL,NULL,NULL);\n" elsif func_name == "clWaitForEvents" then run_dummy_icd += " #{func_name}(1,(cl_event*)&chosen_platform);\n" elsif func_name == "clGetExtensionFunctionAddress" then run_dummy_icd += " #{func_name}(\"extLIG\");\n" elsif func_name == "clGetExtensionFunctionAddressForPlatform" then run_dummy_icd += " #{func_name}((cl_platform_id )chosen_platform,\"extLIG\");\n" elsif func_name == "clUnloadCompiler" then run_dummy_icd += " #{func_name}();\n" else params = $api_entries[func_name].gsub(/[[:space:]]+/, ' ') params.gsub!(/[\/][*](([^*]*)[*][^\/])*[^*]*[*][\/]/,'') params.gsub!(/^[^(]*[(] *(.*[^ ]) *[)][^)]*$/, '\1') params = params.gsub(/[^,(]+([(][^)]*[)])*[^,]*/) { |p| p.gsub!("[]", "*") p.gsub!("user_func", "") if false && p.match(/[*]/) then "NULL" else "(#{p})0" end } if func_name != "clGetPlatformIDs" then params.gsub!(/^([^0]*)0/, '\1chosen_platform') end run_dummy_icd += " #{func_name}(#{params});\n" end run_dummy_icd += " printf(\"%s\\n\", \"#{func_name}\");\n" run_dummy_icd += "#ifdef OCL_ICD_PRINT_EXPECTED\n" run_dummy_icd += " printf(\"#{$key_entries[func_name]} : %s (expected)\\n\", \"#{func_name}\");\n" run_dummy_icd += "#endif\n" run_dummy_icd += " fflush(NULL);\n" } run_dummy_icd += " return;\n}\n" return run_dummy_icd end def self.generate_run_dummy_icd_weak_source run_dummy_icd_weak = "/**\n#{$license}\n*/\n" run_dummy_icd_weak += < #include EOF run_dummy_icd_weak += "#pragma GCC diagnostic push\n" run_dummy_icd_weak += "# pragma GCC diagnostic ignored \"-Wcpp\"\n" run_dummy_icd_weak += "# define CL_USE_DEPRECATED_OPENCL_1_0_APIS\n" run_dummy_icd_weak += "# define CL_USE_DEPRECATED_OPENCL_1_1_APIS\n" run_dummy_icd_weak += "# define CL_USE_DEPRECATED_OPENCL_1_2_APIS\n" run_dummy_icd_weak += "# include \n" run_dummy_icd_weak += self.include_headers run_dummy_icd_weak += "#pragma GCC diagnostic pop\n" $api_entries.each { |func_name, entry| next if $noweak_funcs.include?(func_name) clean_entry = entry.sub(/(.*\)).*/m,'\1').gsub("/*","").gsub("*/","").gsub("\r","") + "{\n" return_type = entry.match(/CL_API_ENTRY (.*) CL_API_CALL/)[1] parameters = clean_entry.match(/\(.*\)/m)[0][1..-2] parameters.gsub!(/\[.*?\]/,"") parameters.sub!(/\(.*?\*\s*(.*?)\)\s*\(.*?\)/m,'\1') run_dummy_icd_weak += clean_entry.gsub(/\*\[.*?\]/,"* ").gsub(/\[.+?\]/,"") first_parameter = parameters.match(/.*?\,/m) if not first_parameter then first_parameter = parameters.match(/.*/m)[0] else first_parameter = first_parameter[0][0..-2] end fps = first_parameter.split run_dummy_icd_weak += " void (*p)()=dlsym(RTLD_NEXT, \"#{func_name}\");\n" ps = parameters.split(",") ps = ps.collect { |p| p = p.split p = p[-1].gsub("*","") } run_dummy_icd_weak += " if(p) {\n" run_dummy_icd_weak += " return((*(typeof(#{func_name})*)p)(" run_dummy_icd_weak += ps.join(", ") run_dummy_icd_weak += "));\n" run_dummy_icd_weak += " } else {\n" run_dummy_icd_weak += " printf(\"-1 : \");\n" run_dummy_icd_weak += " return (#{return_type})0;\n" run_dummy_icd_weak += " }\n" run_dummy_icd_weak += "}\n\n" } return run_dummy_icd_weak end def self.generate_sources(from_headers=true, from_database=false, database=nil) if from_headers then parse_headers end if from_database then load_database(database) end File.open('libdummy_icd_gen.h','w') { |f| f.puts generate_libdummy_icd_header } File.open('libdummy_icd_gen.c','w') { |f| f.puts generate_libdummy_icd_source } File.open('run_dummy_icd_gen.c','w') { |f| f.puts generate_run_dummy_icd_source } File.open('run_dummy_icd_weak_gen.c','w') { |f| f.puts generate_run_dummy_icd_weak_source } end ########################################################## ########################################################## # database mode def self.generate_ocl_icd_header ocl_icd_header = "/**\n#{$license}\n*/\n\n" ocl_icd_header += "#ifndef OCL_ICD_H\n" ocl_icd_header += "#define OCL_ICD_H\n" ocl_icd_header += "#pragma GCC diagnostic push\n" ocl_icd_header += "# pragma GCC diagnostic ignored \"-Wcpp\"\n" ocl_icd_header += "# define CL_USE_DEPRECATED_OPENCL_1_0_APIS\n" ocl_icd_header += "# define CL_USE_DEPRECATED_OPENCL_1_1_APIS\n" ocl_icd_header += "# define CL_USE_DEPRECATED_OPENCL_1_2_APIS\n" ocl_icd_header += "# include \n" ocl_icd_header += self.include_headers ocl_icd_header += "#pragma GCC diagnostic pop\n" ocl_icd_header += < param_value_size ) return CL_INVALID_VALUE; memcpy(param_value, string_p, size_string); } if( param_value_size_ret != NULL ) *param_value_size_ret = size_string; return CL_SUCCESS; } EOF return ocl_icd_bindings_source end def self.generate_get_extension_address_for_platform src = < 3 && (strcmp(func_name+lenfn-3, "KHR")==0 || strcmp(func_name+lenfn-3, "EXT")==0)) { while (fn->name != NULL) { if (strcmp(func_name, fn->name)==0) RETURN(fn->addr); fn++; } } EOF return src end def self.generate_ocl_icd_loader_gen_source skip_funcs = $specific_loader_funcs ocl_icd_loader_gen_source = "/**\n#{$license}\n*/\n" ocl_icd_loader_gen_source += "#include \n" ocl_icd_loader_gen_source += "#include \"ocl_icd_loader.h\"\n" ocl_icd_loader_gen_source += "#define DEBUG_OCL_ICD_PROVIDE_DUMP_FIELD\n" ocl_icd_loader_gen_source += "#include \"ocl_icd_debug.h\"\n" $api_entries.each { |func_name, entry| next if skip_funcs.include?(func_name) clean_entry = entry.sub(/(.*\)).*/m,'\1').gsub("/*","").gsub("*/","").gsub("\r","") + "{\n" return_type = entry.match(/CL_API_ENTRY (.*) CL_API_CALL/)[1] parameters = clean_entry.match(/\(.*\)/m)[0][1..-2] parameters.gsub!(/\[.*?\]/,"") parameters.sub!(/\(.*?\*\s*(.*?)\)\s*\(.*?\)/m,'\1') ocl_icd_loader_gen_source += clean_entry.gsub(/\*\[.*?\]/,"* ").gsub(/\[.+?\]/,"") first_parameter = parameters.match(/.*?\,/m) if not first_parameter then first_parameter = parameters.match(/.*/m)[0] else first_parameter = first_parameter[0][0..-2] end fps = first_parameter.split ocl_icd_loader_gen_source += " debug_trace();\n" ocl_icd_loader_gen_source += generate_get_extension_address_for_platform if func_name == "clGetExtensionFunctionAddressForPlatform" raise "Unsupported data_type #{fps[0]}" if not $cl_data_type_error[fps[0]] ps = parameters.split(",") ps = ps.collect { |p| p = p.split p = p[-1].gsub("*","") } error_handler = lambda { if(ps.include?("errcode_ret")) then ocl_icd_loader_gen_source += " if( errcode_ret != NULL ) {\n"; ocl_icd_loader_gen_source += " *errcode_ret = #{$cl_data_type_error[fps[0]]};\n" ocl_icd_loader_gen_source += " }\n" if return_type != "void" then ocl_icd_loader_gen_source += " RETURN(NULL);\n" else ocl_icd_loader_gen_source += " return;\n" end elsif ($non_standard_error.include?(func_name)) then if return_type != "void" then ocl_icd_loader_gen_source += " RETURN(NULL);\n" else ocl_icd_loader_gen_source += " return;\n" end else if return_type != "void" then ocl_icd_loader_gen_source += " RETURN(#{$cl_data_type_error[fps[0]]});\n" if return_type != "void" else ocl_icd_loader_gen_source += " return;\n" end end } if(fps[0] == "cl_platform_id") then ocl_icd_loader_gen_source += " #{fps[1]}=selectPlatformID(#{fps[1]});\n" end ocl_icd_loader_gen_source += " if( (struct _#{fps[0]} *)#{fps[1]} == NULL) {\n" error_handler.call ocl_icd_loader_gen_source += " }\n" if return_type != "void" then return_debug="RETURN" else return_debug="return" end ocl_icd_loader_gen_source += " #{return_debug}(((struct _#{fps[0]} *)#{fps[1]})->dispatch->#{func_name}(" ocl_icd_loader_gen_source += ps.join(", ") ocl_icd_loader_gen_source += "));\n" ocl_icd_loader_gen_source += "}\n\n" } ocl_icd_loader_gen_source += "#pragma GCC visibility push(hidden)\n\n" skip_funcs = $specific_loader_funcs $api_entries.each { |func_name, entry| #next if func_name.match(/EXT$/) #next if func_name.match(/KHR$/) if (skip_funcs.include?(func_name)) then ocl_icd_loader_gen_source += "extern typeof(#{func_name}) #{func_name}_hid;\n" else ocl_icd_loader_gen_source += "typeof(#{func_name}) #{func_name}_hid __attribute__ ((alias (\"#{func_name}\"), visibility(\"hidden\")));\n" end } ocl_icd_loader_gen_source += "\n\nstruct func_desc const function_description[]= {\n" $api_entries.each { |func_name, entry| #next if func_name.match(/EXT$/) #next if func_name.match(/KHR$/) ocl_icd_loader_gen_source += " {\"#{func_name}\", (void(* const)(void))&#{func_name}_hid },\n" } ocl_icd_loader_gen_source += <