extern crate mesa_rust; extern crate mesa_rust_util; extern crate rusticl_opencl_gen; use crate::api::icd::*; use crate::api::types::*; use crate::api::util::*; use crate::core::device::*; use crate::core::program::*; use self::mesa_rust::compiler::clc::*; use self::mesa_rust_util::string::*; use self::rusticl_opencl_gen::*; use std::ffi::CStr; use std::ffi::CString; use std::os::raw::c_char; use std::ptr; use std::slice; use std::sync::Arc; impl CLInfo for cl_program { fn query(&self, q: cl_program_info, _: &[u8]) -> CLResult> { let prog = self.get_ref()?; Ok(match q { CL_PROGRAM_CONTEXT => { // Note we use as_ptr here which doesn't increase the reference count. let ptr = Arc::as_ptr(&prog.context); cl_prop::(cl_context::from_ptr(ptr)) } CL_PROGRAM_DEVICES => { cl_prop::<&Vec>( &prog .devs .iter() .map(|d| { // Note we use as_ptr here which doesn't increase the reference count. cl_device_id::from_ptr(Arc::as_ptr(d)) }) .collect(), ) } CL_PROGRAM_KERNEL_NAMES => cl_prop::(prog.kernels().join(";")), CL_PROGRAM_NUM_DEVICES => cl_prop::(prog.devs.len() as cl_uint), CL_PROGRAM_NUM_KERNELS => cl_prop::(prog.kernels().len()), CL_PROGRAM_REFERENCE_COUNT => cl_prop::(self.refcnt()?), CL_PROGRAM_SOURCE => cl_prop::<&CStr>(prog.src.as_c_str()), // CL_INVALID_VALUE if param_name is not one of the supported values _ => return Err(CL_INVALID_VALUE), }) } } impl CLInfoObj for cl_program { fn query(&self, d: cl_device_id, q: cl_program_build_info) -> CLResult> { let prog = self.get_ref()?; let dev = d.get_arc()?; Ok(match q { CL_PROGRAM_BINARY_TYPE => cl_prop::(prog.bin_type(&dev)), CL_PROGRAM_BUILD_GLOBAL_VARIABLE_TOTAL_SIZE => cl_prop::(0), CL_PROGRAM_BUILD_LOG => cl_prop::(prog.log(&dev)), CL_PROGRAM_BUILD_OPTIONS => cl_prop::(prog.options(&dev)), CL_PROGRAM_BUILD_STATUS => cl_prop::(prog.status(&dev)), // CL_INVALID_VALUE if param_name is not one of the supported values _ => return Err(CL_INVALID_VALUE), }) } } fn validate_devices( device_list: *const cl_device_id, num_devices: cl_uint, default: &[Arc], ) -> CLResult>> { let mut devs = cl_device_id::get_arc_vec_from_arr(device_list, num_devices)?; // If device_list is a NULL value, the compile is performed for all devices associated with // program. if devs.is_empty() { devs = default.to_vec(); } Ok(devs) } fn call_cb( pfn_notify: Option, program: cl_program, user_data: *mut ::std::os::raw::c_void, ) { if let Some(cb) = pfn_notify { unsafe { cb(program, user_data) }; } } pub fn create_program_with_source( context: cl_context, count: cl_uint, strings: *mut *const c_char, lengths: *const usize, ) -> CLResult { let c = context.get_arc()?; // CL_INVALID_VALUE if count is zero or if strings ... if count == 0 || strings.is_null() { return Err(CL_INVALID_VALUE); } // ... or any entry in strings is NULL. let srcs = unsafe { slice::from_raw_parts(strings, count as usize) }; if srcs.contains(&ptr::null()) { return Err(CL_INVALID_VALUE); } let mut source = String::new(); // we don't want encoding or any other problems with the source to prevent compilations, so // just use CString::from_vec_unchecked and to_string_lossy for i in 0..count as usize { unsafe { if lengths.is_null() || *lengths.add(i) == 0 { source.push_str(&CStr::from_ptr(*strings.add(i)).to_string_lossy()); } else { let l = *lengths.add(i); let arr = slice::from_raw_parts(*strings.add(i).cast(), l); source.push_str(&CString::from_vec_unchecked(arr.to_vec()).to_string_lossy()); } } } Ok(cl_program::from_arc(Program::new( &c, &c.devs, CString::new(source).map_err(|_| CL_INVALID_VALUE)?, ))) } pub fn build_program( program: cl_program, num_devices: cl_uint, device_list: *const cl_device_id, options: *const c_char, pfn_notify: Option, user_data: *mut ::std::os::raw::c_void, ) -> CLResult<()> { let mut res = true; let p = program.get_ref()?; let devs = validate_devices(device_list, num_devices, &p.devs)?; check_cb(&pfn_notify, user_data)?; // CL_BUILD_PROGRAM_FAILURE if there is a failure to build the program executable. This error // will be returned if clBuildProgram does not return until the build has completed. for dev in devs { res &= p.build(&dev, c_string_to_string(options)); } call_cb(pfn_notify, program, user_data); //• CL_INVALID_BINARY if program is created with clCreateProgramWithBinary and devices listed in device_list do not have a valid program binary loaded. //• CL_INVALID_BUILD_OPTIONS if the build options specified by options are invalid. //• CL_INVALID_OPERATION if the build of a program executable for any of the devices listed in device_list by a previous call to clBuildProgram for program has not completed. //• CL_INVALID_OPERATION if there are kernel objects attached to program. //• CL_INVALID_OPERATION if program was not created with clCreateProgramWithSource, clCreateProgramWithIL or clCreateProgramWithBinary. if res { Ok(()) } else { Err(CL_BUILD_PROGRAM_FAILURE) } } pub fn compile_program( program: cl_program, num_devices: cl_uint, device_list: *const cl_device_id, options: *const c_char, num_input_headers: cl_uint, input_headers: *const cl_program, header_include_names: *mut *const c_char, pfn_notify: Option, user_data: *mut ::std::os::raw::c_void, ) -> CLResult<()> { let mut res = true; let p = program.get_ref()?; let devs = validate_devices(device_list, num_devices, &p.devs)?; check_cb(&pfn_notify, user_data)?; // CL_INVALID_VALUE if num_input_headers is zero and header_include_names or input_headers are // not NULL or if num_input_headers is not zero and header_include_names or input_headers are // NULL. if num_input_headers == 0 && (!header_include_names.is_null() || !input_headers.is_null()) || num_input_headers != 0 && (header_include_names.is_null() || input_headers.is_null()) { return Err(CL_INVALID_VALUE); } let mut headers = Vec::new(); for h in 0..num_input_headers as usize { unsafe { headers.push(spirv::CLCHeader { name: CStr::from_ptr(*header_include_names.add(h)).to_owned(), source: &(*input_headers.add(h)).get_ref()?.src, }); } } // CL_COMPILE_PROGRAM_FAILURE if there is a failure to compile the program source. This error // will be returned if clCompileProgram does not return until the compile has completed. for dev in devs { res &= p.compile(&dev, c_string_to_string(options), &headers); } call_cb(pfn_notify, program, user_data); // CL_INVALID_OPERATION if program has no source or IL available, i.e. it has not been created with clCreateProgramWithSource or clCreateProgramWithIL. // • CL_INVALID_COMPILER_OPTIONS if the compiler options specified by options are invalid. // • CL_INVALID_OPERATION if the compilation or build of a program executable for any of the devices listed in device_list by a previous call to clCompileProgram or clBuildProgram for program has not completed. // • CL_INVALID_OPERATION if there are kernel objects attached to program. if res { Ok(()) } else { Err(CL_COMPILE_PROGRAM_FAILURE) } } pub fn link_program( context: cl_context, num_devices: cl_uint, device_list: *const cl_device_id, options: *const ::std::os::raw::c_char, num_input_programs: cl_uint, input_programs: *const cl_program, pfn_notify: Option, user_data: *mut ::std::os::raw::c_void, ) -> CLResult<(cl_program, cl_int)> { let c = context.get_arc()?; let devs = validate_devices(device_list, num_devices, &c.devs)?; let progs = cl_program::get_arc_vec_from_arr(input_programs, num_input_programs)?; check_cb(&pfn_notify, user_data)?; // CL_INVALID_VALUE if num_input_programs is zero and input_programs is NULL if progs.is_empty() { return Err(CL_INVALID_VALUE); } // CL_INVALID_DEVICE if any device in device_list is not in the list of devices associated with // context. if !devs.iter().all(|d| c.devs.contains(d)) { return Err(CL_INVALID_DEVICE); } // CL_INVALID_OPERATION if the compilation or build of a program executable for any of the // devices listed in device_list by a previous call to clCompileProgram or clBuildProgram for // program has not completed. for d in &devs { if progs .iter() .map(|p| p.status(d)) .any(|s| s != CL_BUILD_SUCCESS as cl_build_status) { return Err(CL_INVALID_OPERATION); } } // CL_LINK_PROGRAM_FAILURE if there is a failure to link the compiled binaries and/or libraries. let res = Program::link(c, &devs, &progs, c_string_to_string(options)); let code = if devs .iter() .map(|d| res.status(d)) .all(|s| s == CL_BUILD_SUCCESS as cl_build_status) { CL_SUCCESS as cl_int } else { CL_LINK_PROGRAM_FAILURE }; let res = cl_program::from_arc(res); call_cb(pfn_notify, res, user_data); Ok((res, code)) //• CL_INVALID_LINKER_OPTIONS if the linker options specified by options are invalid. //• CL_INVALID_OPERATION if the rules for devices containing compiled binaries or libraries as described in input_programs argument above are not followed. }