xref: /aosp_15_r20/external/mesa3d/src/gallium/frontends/rusticl/core/program.rs (revision 6104692788411f58d303aa86923a9ff6ecaded22)
1 use crate::api::icd::*;
2 use crate::core::context::*;
3 use crate::core::device::*;
4 use crate::core::kernel::*;
5 use crate::core::platform::Platform;
6 use crate::impl_cl_type_trait;
7 
8 use mesa_rust::compiler::clc::spirv::SPIRVBin;
9 use mesa_rust::compiler::clc::*;
10 use mesa_rust::compiler::nir::*;
11 use mesa_rust::util::disk_cache::*;
12 use mesa_rust_gen::*;
13 use rusticl_llvm_gen::*;
14 use rusticl_opencl_gen::*;
15 
16 use std::collections::HashMap;
17 use std::collections::HashSet;
18 use std::ffi::CString;
19 use std::mem::size_of;
20 use std::ptr::addr_of;
21 use std::slice;
22 use std::sync::Arc;
23 use std::sync::Mutex;
24 use std::sync::MutexGuard;
25 use std::sync::Once;
26 
27 // 8 bytes so we don't have any padding.
28 const BIN_RUSTICL_MAGIC_STRING: &[u8; 8] = b"rusticl\0";
29 
30 const BIN_HEADER_SIZE_BASE: usize =
31     // 1. magic number
32     size_of::<[u8; 8]>() +
33     // 2. format version
34     size_of::<u32>();
35 
36 const BIN_HEADER_SIZE_V1: usize = BIN_HEADER_SIZE_BASE +
37     // 3. device name length
38     size_of::<u32>() +
39     // 4. spirv len
40     size_of::<u32>() +
41     // 5. binary_type
42     size_of::<cl_program_binary_type>();
43 
44 const BIN_HEADER_SIZE: usize = BIN_HEADER_SIZE_V1;
45 
46 // kernel cache
47 static mut DISK_CACHE: Option<DiskCache> = None;
48 static DISK_CACHE_ONCE: Once = Once::new();
49 
get_disk_cache() -> &'static Option<DiskCache>50 fn get_disk_cache() -> &'static Option<DiskCache> {
51     let func_ptrs = [
52         // ourselves
53         get_disk_cache as _,
54         // LLVM
55         llvm_LLVMContext_LLVMContext as _,
56         // clang
57         clang_getClangFullVersion as _,
58         // SPIRV-LLVM-Translator
59         llvm_writeSpirv1 as _,
60     ];
61     unsafe {
62         DISK_CACHE_ONCE.call_once(|| {
63             DISK_CACHE = DiskCache::new("rusticl", &func_ptrs, 0);
64         });
65         &*addr_of!(DISK_CACHE)
66     }
67 }
68 
clc_validator_options(dev: &Device) -> clc_validator_options69 fn clc_validator_options(dev: &Device) -> clc_validator_options {
70     clc_validator_options {
71         // has to match CL_DEVICE_MAX_PARAMETER_SIZE
72         limit_max_function_arg: dev.param_max_size() as u32,
73     }
74 }
75 
76 pub enum ProgramSourceType {
77     Binary,
78     Linked,
79     Src(CString),
80     Il(spirv::SPIRVBin),
81 }
82 
83 pub struct Program {
84     pub base: CLObjectBase<CL_INVALID_PROGRAM>,
85     pub context: Arc<Context>,
86     pub devs: Vec<&'static Device>,
87     pub src: ProgramSourceType,
88     build: Mutex<ProgramBuild>,
89 }
90 
91 impl_cl_type_trait!(cl_program, Program, CL_INVALID_PROGRAM);
92 
93 pub struct ProgramBuild {
94     pub builds: HashMap<&'static Device, ProgramDevBuild>,
95     pub kernel_info: HashMap<String, Arc<KernelInfo>>,
96     spec_constants: HashMap<u32, nir_const_value>,
97     kernels: Vec<String>,
98 }
99 
100 impl ProgramBuild {
spirv_info(&self, kernel: &str, d: &Device) -> Option<&clc_kernel_info>101     pub fn spirv_info(&self, kernel: &str, d: &Device) -> Option<&clc_kernel_info> {
102         self.dev_build(d).spirv.as_ref()?.kernel_info(kernel)
103     }
104 
args(&self, dev: &Device, kernel: &str) -> Vec<spirv::SPIRVKernelArg>105     fn args(&self, dev: &Device, kernel: &str) -> Vec<spirv::SPIRVKernelArg> {
106         self.dev_build(dev).spirv.as_ref().unwrap().args(kernel)
107     }
108 
build_nirs(&mut self, is_src: bool)109     fn build_nirs(&mut self, is_src: bool) {
110         for kernel_name in &self.kernels.clone() {
111             let kernel_args: HashSet<_> = self
112                 .devs_with_build()
113                 .iter()
114                 .map(|d| self.args(d, kernel_name))
115                 .collect();
116 
117             let args = kernel_args.into_iter().next().unwrap();
118             let mut kernel_info_set = HashSet::new();
119 
120             // TODO: we could run this in parallel?
121             for dev in self.devs_with_build() {
122                 let build_result = convert_spirv_to_nir(self, kernel_name, &args, dev);
123                 kernel_info_set.insert(build_result.kernel_info);
124 
125                 self.builds.get_mut(dev).unwrap().kernels.insert(
126                     kernel_name.clone(),
127                     Arc::new(build_result.nir_kernel_builds),
128                 );
129             }
130 
131             // we want the same (internal) args for every compiled kernel, for now
132             assert!(kernel_info_set.len() == 1);
133             let mut kernel_info = kernel_info_set.into_iter().next().unwrap();
134 
135             // spec: For kernels not created from OpenCL C source and the clCreateProgramWithSource
136             // API call the string returned from this query [CL_KERNEL_ATTRIBUTES] will be empty.
137             if !is_src {
138                 kernel_info.attributes_string = String::new();
139             }
140 
141             self.kernel_info
142                 .insert(kernel_name.clone(), Arc::new(kernel_info));
143         }
144     }
145 
dev_build(&self, dev: &Device) -> &ProgramDevBuild146     fn dev_build(&self, dev: &Device) -> &ProgramDevBuild {
147         self.builds.get(dev).unwrap()
148     }
149 
dev_build_mut(&mut self, dev: &Device) -> &mut ProgramDevBuild150     fn dev_build_mut(&mut self, dev: &Device) -> &mut ProgramDevBuild {
151         self.builds.get_mut(dev).unwrap()
152     }
153 
devs_with_build(&self) -> Vec<&'static Device>154     fn devs_with_build(&self) -> Vec<&'static Device> {
155         self.builds
156             .iter()
157             .filter(|(_, build)| build.status == CL_BUILD_SUCCESS as cl_build_status)
158             .map(|(&d, _)| d)
159             .collect()
160     }
161 
hash_key(&self, dev: &Device, name: &str) -> Option<cache_key>162     pub fn hash_key(&self, dev: &Device, name: &str) -> Option<cache_key> {
163         if let Some(cache) = dev.screen().shader_cache() {
164             let info = self.dev_build(dev);
165             assert_eq!(info.status, CL_BUILD_SUCCESS as cl_build_status);
166 
167             let spirv = info.spirv.as_ref().unwrap();
168             let mut bin = spirv.to_bin().to_vec();
169             bin.extend_from_slice(name.as_bytes());
170 
171             for (k, v) in &self.spec_constants {
172                 bin.extend_from_slice(&k.to_ne_bytes());
173                 unsafe {
174                     // SAFETY: we fully initialize this union
175                     bin.extend_from_slice(&v.u64_.to_ne_bytes());
176                 }
177             }
178 
179             Some(cache.gen_key(&bin))
180         } else {
181             None
182         }
183     }
184 
kernels(&self) -> &[String]185     pub fn kernels(&self) -> &[String] {
186         &self.kernels
187     }
188 
to_nir(&self, kernel: &str, d: &Device) -> NirShader189     pub fn to_nir(&self, kernel: &str, d: &Device) -> NirShader {
190         let mut spec_constants: Vec<_> = self
191             .spec_constants
192             .iter()
193             .map(|(&id, &value)| nir_spirv_specialization {
194                 id: id,
195                 value: value,
196                 defined_on_module: true,
197             })
198             .collect();
199 
200         let info = self.dev_build(d);
201         assert_eq!(info.status, CL_BUILD_SUCCESS as cl_build_status);
202 
203         let mut log = Platform::dbg().program.then(Vec::new);
204         let nir = info.spirv.as_ref().unwrap().to_nir(
205             kernel,
206             d.screen
207                 .nir_shader_compiler_options(pipe_shader_type::PIPE_SHADER_COMPUTE),
208             &d.lib_clc,
209             &mut spec_constants,
210             d.address_bits(),
211             log.as_mut(),
212         );
213 
214         if let Some(log) = log {
215             for line in log {
216                 eprintln!("{}", line);
217             }
218         };
219 
220         nir.unwrap()
221     }
222 }
223 
224 #[derive(Default)]
225 pub struct ProgramDevBuild {
226     spirv: Option<spirv::SPIRVBin>,
227     status: cl_build_status,
228     options: String,
229     log: String,
230     bin_type: cl_program_binary_type,
231     pub kernels: HashMap<String, Arc<NirKernelBuilds>>,
232 }
233 
prepare_options(options: &str, dev: &Device) -> Vec<CString>234 fn prepare_options(options: &str, dev: &Device) -> Vec<CString> {
235     let mut options = options.to_owned();
236     if !options.contains("-cl-std=") {
237         options.push_str(" -cl-std=CL");
238         options.push_str(dev.clc_version.api_str());
239     }
240     options.push_str(" -D__OPENCL_VERSION__=");
241     options.push_str(dev.cl_version.clc_str());
242 
243     let mut res = Vec::new();
244 
245     // we seperate on a ' ' unless we hit a "
246     let mut sep = ' ';
247     let mut old = 0;
248     for (i, c) in options.char_indices() {
249         if c == '"' {
250             if sep == ' ' {
251                 sep = '"';
252             } else {
253                 sep = ' ';
254             }
255         }
256 
257         if c == '"' || c == sep {
258             // beware of double seps
259             if old != i {
260                 res.push(&options[old..i]);
261             }
262             old = i + c.len_utf8();
263         }
264     }
265     // add end of the string
266     res.push(&options[old..]);
267 
268     res.iter()
269         .filter_map(|&a| match a {
270             "-cl-denorms-are-zero" => Some("-fdenormal-fp-math=positive-zero"),
271             // We can ignore it as long as we don't support ifp
272             "-cl-no-subgroup-ifp" => None,
273             _ => Some(a),
274         })
275         .map(CString::new)
276         .map(Result::unwrap)
277         .collect()
278 }
279 
280 impl Program {
create_default_builds( devs: &[&'static Device], ) -> HashMap<&'static Device, ProgramDevBuild>281     fn create_default_builds(
282         devs: &[&'static Device],
283     ) -> HashMap<&'static Device, ProgramDevBuild> {
284         devs.iter()
285             .map(|&d| {
286                 (
287                     d,
288                     ProgramDevBuild {
289                         status: CL_BUILD_NONE,
290                         ..Default::default()
291                     },
292                 )
293             })
294             .collect()
295     }
296 
new(context: Arc<Context>, src: CString) -> Arc<Program>297     pub fn new(context: Arc<Context>, src: CString) -> Arc<Program> {
298         Arc::new(Self {
299             base: CLObjectBase::new(RusticlTypes::Program),
300             build: Mutex::new(ProgramBuild {
301                 builds: Self::create_default_builds(&context.devs),
302                 spec_constants: HashMap::new(),
303                 kernels: Vec::new(),
304                 kernel_info: HashMap::new(),
305             }),
306             devs: context.devs.to_vec(),
307             context: context,
308             src: ProgramSourceType::Src(src),
309         })
310     }
311 
spirv_from_bin_for_dev( dev: &Device, bin: &[u8], ) -> CLResult<(SPIRVBin, cl_program_binary_type)>312     fn spirv_from_bin_for_dev(
313         dev: &Device,
314         bin: &[u8],
315     ) -> CLResult<(SPIRVBin, cl_program_binary_type)> {
316         if bin.is_empty() {
317             return Err(CL_INVALID_VALUE);
318         }
319 
320         if bin.len() < BIN_HEADER_SIZE_BASE {
321             return Err(CL_INVALID_BINARY);
322         }
323 
324         unsafe {
325             let mut blob = blob_reader::default();
326             blob_reader_init(&mut blob, bin.as_ptr().cast(), bin.len());
327 
328             let read_magic: &[u8] = slice::from_raw_parts(
329                 blob_read_bytes(&mut blob, BIN_RUSTICL_MAGIC_STRING.len()).cast(),
330                 BIN_RUSTICL_MAGIC_STRING.len(),
331             );
332             if read_magic != *BIN_RUSTICL_MAGIC_STRING {
333                 return Err(CL_INVALID_BINARY);
334             }
335 
336             let version = u32::from_le(blob_read_uint32(&mut blob));
337             match version {
338                 1 => {
339                     let name_length = u32::from_le(blob_read_uint32(&mut blob)) as usize;
340                     let spirv_size = u32::from_le(blob_read_uint32(&mut blob)) as usize;
341                     let bin_type = u32::from_le(blob_read_uint32(&mut blob));
342 
343                     debug_assert!(
344                         // `blob_read_*` doesn't advance the pointer on failure to read
345                         blob.current.byte_offset_from(blob.data) == BIN_HEADER_SIZE_V1 as isize
346                             || blob.overrun,
347                     );
348 
349                     let name = blob_read_bytes(&mut blob, name_length);
350                     let spirv_data = blob_read_bytes(&mut blob, spirv_size);
351 
352                     // check that all the reads are valid before accessing the data, which might
353                     // be uninitialized otherwise.
354                     if blob.overrun {
355                         return Err(CL_INVALID_BINARY);
356                     }
357 
358                     let name: &[u8] = slice::from_raw_parts(name.cast(), name_length);
359                     if dev.screen().name().to_bytes() != name {
360                         return Err(CL_INVALID_BINARY);
361                     }
362 
363                     let spirv = spirv::SPIRVBin::from_bin(slice::from_raw_parts(
364                         spirv_data.cast(),
365                         spirv_size,
366                     ));
367 
368                     Ok((spirv, bin_type))
369                 }
370                 _ => Err(CL_INVALID_BINARY),
371             }
372         }
373     }
374 
from_bins( context: Arc<Context>, devs: Vec<&'static Device>, bins: &[&[u8]], ) -> Result<Arc<Program>, Vec<cl_int>>375     pub fn from_bins(
376         context: Arc<Context>,
377         devs: Vec<&'static Device>,
378         bins: &[&[u8]],
379     ) -> Result<Arc<Program>, Vec<cl_int>> {
380         let mut builds = HashMap::new();
381         let mut kernels = HashSet::new();
382 
383         let mut errors = vec![CL_SUCCESS as cl_int; devs.len()];
384         for (idx, (&d, b)) in devs.iter().zip(bins).enumerate() {
385             let build = match Self::spirv_from_bin_for_dev(d, b) {
386                 Ok((spirv, bin_type)) => {
387                     for k in spirv.kernels() {
388                         kernels.insert(k);
389                     }
390 
391                     ProgramDevBuild {
392                         spirv: Some(spirv),
393                         bin_type: bin_type,
394                         ..Default::default()
395                     }
396                 }
397                 Err(err) => {
398                     errors[idx] = err;
399                     ProgramDevBuild {
400                         status: CL_BUILD_ERROR,
401                         ..Default::default()
402                     }
403                 }
404             };
405 
406             builds.insert(d, build);
407         }
408 
409         if errors.iter().any(|&e| e != CL_SUCCESS as cl_int) {
410             return Err(errors);
411         }
412 
413         let mut build = ProgramBuild {
414             builds: builds,
415             spec_constants: HashMap::new(),
416             kernels: kernels.into_iter().collect(),
417             kernel_info: HashMap::new(),
418         };
419         build.build_nirs(false);
420 
421         Ok(Arc::new(Self {
422             base: CLObjectBase::new(RusticlTypes::Program),
423             context: context,
424             devs: devs,
425             src: ProgramSourceType::Binary,
426             build: Mutex::new(build),
427         }))
428     }
429 
from_spirv(context: Arc<Context>, spirv: &[u8]) -> Arc<Program>430     pub fn from_spirv(context: Arc<Context>, spirv: &[u8]) -> Arc<Program> {
431         let builds = Self::create_default_builds(&context.devs);
432         Arc::new(Self {
433             base: CLObjectBase::new(RusticlTypes::Program),
434             devs: context.devs.clone(),
435             context: context,
436             src: ProgramSourceType::Il(SPIRVBin::from_bin(spirv)),
437             build: Mutex::new(ProgramBuild {
438                 builds: builds,
439                 spec_constants: HashMap::new(),
440                 kernels: Vec::new(),
441                 kernel_info: HashMap::new(),
442             }),
443         })
444     }
445 
build_info(&self) -> MutexGuard<ProgramBuild>446     pub fn build_info(&self) -> MutexGuard<ProgramBuild> {
447         self.build.lock().unwrap()
448     }
449 
status(&self, dev: &Device) -> cl_build_status450     pub fn status(&self, dev: &Device) -> cl_build_status {
451         self.build_info().dev_build(dev).status
452     }
453 
log(&self, dev: &Device) -> String454     pub fn log(&self, dev: &Device) -> String {
455         self.build_info().dev_build(dev).log.clone()
456     }
457 
bin_type(&self, dev: &Device) -> cl_program_binary_type458     pub fn bin_type(&self, dev: &Device) -> cl_program_binary_type {
459         self.build_info().dev_build(dev).bin_type
460     }
461 
options(&self, dev: &Device) -> String462     pub fn options(&self, dev: &Device) -> String {
463         self.build_info().dev_build(dev).options.clone()
464     }
465 
466     // we need to precalculate the size
bin_sizes(&self) -> Vec<usize>467     pub fn bin_sizes(&self) -> Vec<usize> {
468         let lock = self.build_info();
469         let mut res = Vec::new();
470         for d in &self.devs {
471             let info = lock.dev_build(d);
472 
473             res.push(info.spirv.as_ref().map_or(0, |s| {
474                 s.to_bin().len() + d.screen().name().to_bytes().len() + BIN_HEADER_SIZE
475             }));
476         }
477         res
478     }
479 
binaries(&self, vals: &[u8]) -> CLResult<Vec<*mut u8>>480     pub fn binaries(&self, vals: &[u8]) -> CLResult<Vec<*mut u8>> {
481         // if the application didn't provide any pointers, just return the length of devices
482         if vals.is_empty() {
483             return Ok(vec![std::ptr::null_mut(); self.devs.len()]);
484         }
485 
486         // vals is an array of pointers where we should write the device binaries into
487         if vals.len() != self.devs.len() * size_of::<*const u8>() {
488             return Err(CL_INVALID_VALUE);
489         }
490 
491         let ptrs: &[*mut u8] = unsafe {
492             slice::from_raw_parts(vals.as_ptr().cast(), vals.len() / size_of::<*mut u8>())
493         };
494 
495         let lock = self.build_info();
496         for (i, d) in self.devs.iter().enumerate() {
497             let info = lock.dev_build(d);
498 
499             // no spirv means nothing to write
500             let Some(spirv) = info.spirv.as_ref() else {
501                 continue;
502             };
503             let spirv = spirv.to_bin();
504 
505             unsafe {
506                 let mut blob = blob::default();
507 
508                 // sadly we have to trust the buffer to be correctly sized...
509                 blob_init_fixed(&mut blob, ptrs[i].cast(), usize::MAX);
510 
511                 blob_write_bytes(
512                     &mut blob,
513                     BIN_RUSTICL_MAGIC_STRING.as_ptr().cast(),
514                     BIN_RUSTICL_MAGIC_STRING.len(),
515                 );
516 
517                 // binary format version
518                 blob_write_uint32(&mut blob, 1_u32.to_le());
519 
520                 let device_name = d.screen().name();
521                 let device_name = device_name.to_bytes();
522 
523                 blob_write_uint32(&mut blob, (device_name.len() as u32).to_le());
524                 blob_write_uint32(&mut blob, (spirv.len() as u32).to_le());
525                 blob_write_uint32(&mut blob, info.bin_type.to_le());
526                 debug_assert!(blob.size == BIN_HEADER_SIZE);
527 
528                 blob_write_bytes(&mut blob, device_name.as_ptr().cast(), device_name.len());
529                 blob_write_bytes(&mut blob, spirv.as_ptr().cast(), spirv.len());
530                 blob_finish(&mut blob);
531             }
532         }
533 
534         Ok(ptrs.to_vec())
535     }
536 
537     // TODO: at the moment we do not support compiling programs with different signatures across
538     // devices. If we do in the future, this needs to be properly implemented.
has_unique_kernel_signatures(&self, _kernel_name: &str) -> bool539     pub fn has_unique_kernel_signatures(&self, _kernel_name: &str) -> bool {
540         true
541     }
542 
active_kernels(&self) -> bool543     pub fn active_kernels(&self) -> bool {
544         self.build_info()
545             .builds
546             .values()
547             .any(|b| b.kernels.values().any(|b| Arc::strong_count(b) > 1))
548     }
549 
build(&self, dev: &Device, options: String) -> bool550     pub fn build(&self, dev: &Device, options: String) -> bool {
551         let lib = options.contains("-create-library");
552         let mut info = self.build_info();
553         if !self.do_compile(dev, options, &Vec::new(), &mut info) {
554             return false;
555         }
556 
557         let d = info.dev_build_mut(dev);
558 
559         // skip compilation if we already have the right thing.
560         if self.is_bin() {
561             if d.bin_type == CL_PROGRAM_BINARY_TYPE_EXECUTABLE && !lib
562                 || d.bin_type == CL_PROGRAM_BINARY_TYPE_LIBRARY && lib
563             {
564                 return true;
565             }
566         }
567 
568         let spirvs = [d.spirv.as_ref().unwrap()];
569         let (spirv, log) = spirv::SPIRVBin::link(&spirvs, lib);
570 
571         d.log.push_str(&log);
572         d.spirv = spirv;
573         if let Some(spirv) = &d.spirv {
574             d.bin_type = if lib {
575                 CL_PROGRAM_BINARY_TYPE_LIBRARY
576             } else {
577                 CL_PROGRAM_BINARY_TYPE_EXECUTABLE
578             };
579             d.status = CL_BUILD_SUCCESS as cl_build_status;
580             let mut kernels = spirv.kernels();
581             info.kernels.append(&mut kernels);
582             info.build_nirs(self.is_src());
583             true
584         } else {
585             d.status = CL_BUILD_ERROR;
586             d.bin_type = CL_PROGRAM_BINARY_TYPE_NONE;
587             false
588         }
589     }
590 
do_compile( &self, dev: &Device, options: String, headers: &[spirv::CLCHeader], info: &mut MutexGuard<ProgramBuild>, ) -> bool591     fn do_compile(
592         &self,
593         dev: &Device,
594         options: String,
595         headers: &[spirv::CLCHeader],
596         info: &mut MutexGuard<ProgramBuild>,
597     ) -> bool {
598         let d = info.dev_build_mut(dev);
599 
600         let val_options = clc_validator_options(dev);
601         let (spirv, log) = match &self.src {
602             ProgramSourceType::Il(spirv) => {
603                 if Platform::dbg().allow_invalid_spirv {
604                     (Some(spirv.clone()), String::new())
605                 } else {
606                     spirv.clone_on_validate(&val_options)
607                 }
608             }
609             ProgramSourceType::Src(src) => {
610                 let args = prepare_options(&options, dev);
611 
612                 if Platform::dbg().clc {
613                     let src = src.to_string_lossy();
614                     eprintln!("dumping compilation inputs:");
615                     eprintln!("compilation arguments: {args:?}");
616                     if !headers.is_empty() {
617                         eprintln!("headers: {headers:#?}");
618                     }
619                     eprintln!("source code:\n{src}");
620                 }
621 
622                 let (spirv, msgs) = spirv::SPIRVBin::from_clc(
623                     src,
624                     &args,
625                     headers,
626                     get_disk_cache(),
627                     dev.cl_features(),
628                     &dev.spirv_extensions,
629                     dev.address_bits(),
630                 );
631 
632                 if Platform::dbg().validate_spirv {
633                     if let Some(spirv) = spirv {
634                         let (res, spirv_msgs) = spirv.validate(&val_options);
635                         (res.then_some(spirv), format!("{}\n{}", msgs, spirv_msgs))
636                     } else {
637                         (None, msgs)
638                     }
639                 } else {
640                     (spirv, msgs)
641                 }
642             }
643             // do nothing if we got a library or binary
644             _ => {
645                 return true;
646             }
647         };
648 
649         d.spirv = spirv;
650         d.log = log;
651         d.options = options;
652 
653         if d.spirv.is_some() {
654             d.status = CL_BUILD_SUCCESS as cl_build_status;
655             d.bin_type = CL_PROGRAM_BINARY_TYPE_COMPILED_OBJECT;
656             true
657         } else {
658             d.status = CL_BUILD_ERROR;
659             false
660         }
661     }
662 
compile(&self, dev: &Device, options: String, headers: &[spirv::CLCHeader]) -> bool663     pub fn compile(&self, dev: &Device, options: String, headers: &[spirv::CLCHeader]) -> bool {
664         self.do_compile(dev, options, headers, &mut self.build_info())
665     }
666 
link( context: Arc<Context>, devs: &[&'static Device], progs: &[Arc<Program>], options: String, ) -> Arc<Program>667     pub fn link(
668         context: Arc<Context>,
669         devs: &[&'static Device],
670         progs: &[Arc<Program>],
671         options: String,
672     ) -> Arc<Program> {
673         let mut builds = HashMap::new();
674         let mut kernels = HashSet::new();
675         let mut locks: Vec<_> = progs.iter().map(|p| p.build_info()).collect();
676         let lib = options.contains("-create-library");
677 
678         for &d in devs {
679             let bins: Vec<_> = locks
680                 .iter_mut()
681                 .map(|l| l.dev_build(d).spirv.as_ref().unwrap())
682                 .collect();
683 
684             let (spirv, log) = spirv::SPIRVBin::link(&bins, lib);
685             let (spirv, log) = if Platform::dbg().validate_spirv {
686                 if let Some(spirv) = spirv {
687                     let val_options = clc_validator_options(d);
688                     let (res, spirv_msgs) = spirv.validate(&val_options);
689                     (res.then_some(spirv), format!("{}\n{}", log, spirv_msgs))
690                 } else {
691                     (None, log)
692                 }
693             } else {
694                 (spirv, log)
695             };
696 
697             let status;
698             let bin_type;
699             if let Some(spirv) = &spirv {
700                 for k in spirv.kernels() {
701                     kernels.insert(k);
702                 }
703                 status = CL_BUILD_SUCCESS as cl_build_status;
704                 bin_type = if lib {
705                     CL_PROGRAM_BINARY_TYPE_LIBRARY
706                 } else {
707                     CL_PROGRAM_BINARY_TYPE_EXECUTABLE
708                 };
709             } else {
710                 status = CL_BUILD_ERROR;
711                 bin_type = CL_PROGRAM_BINARY_TYPE_NONE;
712             };
713 
714             builds.insert(
715                 d,
716                 ProgramDevBuild {
717                     spirv: spirv,
718                     status: status,
719                     log: log,
720                     bin_type: bin_type,
721                     ..Default::default()
722                 },
723             );
724         }
725 
726         let mut build = ProgramBuild {
727             builds: builds,
728             spec_constants: HashMap::new(),
729             kernels: kernels.into_iter().collect(),
730             kernel_info: HashMap::new(),
731         };
732 
733         // Pre build nir kernels
734         build.build_nirs(false);
735 
736         Arc::new(Self {
737             base: CLObjectBase::new(RusticlTypes::Program),
738             context: context,
739             devs: devs.to_owned(),
740             src: ProgramSourceType::Linked,
741             build: Mutex::new(build),
742         })
743     }
744 
is_bin(&self) -> bool745     pub fn is_bin(&self) -> bool {
746         matches!(self.src, ProgramSourceType::Binary)
747     }
748 
is_il(&self) -> bool749     pub fn is_il(&self) -> bool {
750         matches!(self.src, ProgramSourceType::Il(_))
751     }
752 
is_src(&self) -> bool753     pub fn is_src(&self) -> bool {
754         matches!(self.src, ProgramSourceType::Src(_))
755     }
756 
get_spec_constant_size(&self, spec_id: u32) -> u8757     pub fn get_spec_constant_size(&self, spec_id: u32) -> u8 {
758         match &self.src {
759             ProgramSourceType::Il(il) => il
760                 .spec_constant(spec_id)
761                 .map_or(0, spirv::CLCSpecConstantType::size),
762             _ => unreachable!(),
763         }
764     }
765 
set_spec_constant(&self, spec_id: u32, data: &[u8])766     pub fn set_spec_constant(&self, spec_id: u32, data: &[u8]) {
767         let mut lock = self.build_info();
768         let mut val = nir_const_value::default();
769 
770         match data.len() {
771             1 => val.u8_ = u8::from_ne_bytes(data.try_into().unwrap()),
772             2 => val.u16_ = u16::from_ne_bytes(data.try_into().unwrap()),
773             4 => val.u32_ = u32::from_ne_bytes(data.try_into().unwrap()),
774             8 => val.u64_ = u64::from_ne_bytes(data.try_into().unwrap()),
775             _ => unreachable!("Spec constant with invalid size!"),
776         };
777 
778         lock.spec_constants.insert(spec_id, val);
779     }
780 }
781