• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(c"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, ptrs: &[*mut u8]) -> CLResult<()>480     pub fn binaries(&self, ptrs: &[*mut u8]) -> CLResult<()> {
481         // ptrs is an array of pointers where we should write the device binaries into
482         if ptrs.len() < self.devs.len() {
483             return Err(CL_INVALID_VALUE);
484         }
485 
486         let lock = self.build_info();
487         for (d, ptr) in self.devs.iter().zip(ptrs) {
488             if ptr.is_null() {
489                 return Err(CL_INVALID_VALUE);
490             }
491 
492             let info = lock.dev_build(d);
493 
494             // no spirv means nothing to write
495             let Some(spirv) = info.spirv.as_ref() else {
496                 continue;
497             };
498             let spirv = spirv.to_bin();
499 
500             unsafe {
501                 let mut blob = blob::default();
502 
503                 // sadly we have to trust the buffer to be correctly sized...
504                 blob_init_fixed(&mut blob, ptr.cast(), usize::MAX);
505 
506                 blob_write_bytes(
507                     &mut blob,
508                     BIN_RUSTICL_MAGIC_STRING.as_ptr().cast(),
509                     BIN_RUSTICL_MAGIC_STRING.len(),
510                 );
511 
512                 // binary format version
513                 blob_write_uint32(&mut blob, 1_u32.to_le());
514 
515                 let device_name = d.screen().name();
516                 let device_name = device_name.to_bytes();
517 
518                 blob_write_uint32(&mut blob, (device_name.len() as u32).to_le());
519                 blob_write_uint32(&mut blob, (spirv.len() as u32).to_le());
520                 blob_write_uint32(&mut blob, info.bin_type.to_le());
521                 debug_assert!(blob.size == BIN_HEADER_SIZE);
522 
523                 blob_write_bytes(&mut blob, device_name.as_ptr().cast(), device_name.len());
524                 blob_write_bytes(&mut blob, spirv.as_ptr().cast(), spirv.len());
525                 blob_finish(&mut blob);
526             }
527         }
528 
529         Ok(())
530     }
531 
532     // TODO: at the moment we do not support compiling programs with different signatures across
533     // devices. If we do in the future, this needs to be properly implemented.
has_unique_kernel_signatures(&self, _kernel_name: &str) -> bool534     pub fn has_unique_kernel_signatures(&self, _kernel_name: &str) -> bool {
535         true
536     }
537 
active_kernels(&self) -> bool538     pub fn active_kernels(&self) -> bool {
539         self.build_info()
540             .builds
541             .values()
542             .any(|b| b.kernels.values().any(|b| Arc::strong_count(b) > 1))
543     }
544 
build(&self, dev: &Device, options: String) -> bool545     pub fn build(&self, dev: &Device, options: String) -> bool {
546         let lib = options.contains("-create-library");
547         let mut info = self.build_info();
548         if !self.do_compile(dev, options, &Vec::new(), &mut info) {
549             return false;
550         }
551 
552         let d = info.dev_build_mut(dev);
553 
554         // skip compilation if we already have the right thing.
555         if self.is_bin() {
556             if d.bin_type == CL_PROGRAM_BINARY_TYPE_EXECUTABLE && !lib
557                 || d.bin_type == CL_PROGRAM_BINARY_TYPE_LIBRARY && lib
558             {
559                 return true;
560             }
561         }
562 
563         let spirvs = [d.spirv.as_ref().unwrap()];
564         let (spirv, log) = spirv::SPIRVBin::link(&spirvs, lib);
565 
566         d.log.push_str(&log);
567         d.spirv = spirv;
568         if let Some(spirv) = &d.spirv {
569             d.bin_type = if lib {
570                 CL_PROGRAM_BINARY_TYPE_LIBRARY
571             } else {
572                 CL_PROGRAM_BINARY_TYPE_EXECUTABLE
573             };
574             d.status = CL_BUILD_SUCCESS as cl_build_status;
575             let mut kernels = spirv.kernels();
576             info.kernels.append(&mut kernels);
577             info.build_nirs(self.is_src());
578             true
579         } else {
580             d.status = CL_BUILD_ERROR;
581             d.bin_type = CL_PROGRAM_BINARY_TYPE_NONE;
582             false
583         }
584     }
585 
do_compile( &self, dev: &Device, options: String, headers: &[spirv::CLCHeader], info: &mut MutexGuard<ProgramBuild>, ) -> bool586     fn do_compile(
587         &self,
588         dev: &Device,
589         options: String,
590         headers: &[spirv::CLCHeader],
591         info: &mut MutexGuard<ProgramBuild>,
592     ) -> bool {
593         let d = info.dev_build_mut(dev);
594 
595         let val_options = clc_validator_options(dev);
596         let (spirv, log) = match &self.src {
597             ProgramSourceType::Il(spirv) => {
598                 if Platform::dbg().allow_invalid_spirv {
599                     (Some(spirv.clone()), String::new())
600                 } else {
601                     spirv.clone_on_validate(&val_options)
602                 }
603             }
604             ProgramSourceType::Src(src) => {
605                 let args = prepare_options(&options, dev);
606 
607                 if Platform::dbg().clc {
608                     let src = src.to_string_lossy();
609                     eprintln!("dumping compilation inputs:");
610                     eprintln!("compilation arguments: {args:?}");
611                     if !headers.is_empty() {
612                         eprintln!("headers: {headers:#?}");
613                     }
614                     eprintln!("source code:\n{src}");
615                 }
616 
617                 let (spirv, msgs) = spirv::SPIRVBin::from_clc(
618                     src,
619                     &args,
620                     headers,
621                     get_disk_cache(),
622                     dev.cl_features(),
623                     &dev.spirv_extensions,
624                     dev.address_bits(),
625                 );
626 
627                 if Platform::dbg().validate_spirv {
628                     if let Some(spirv) = spirv {
629                         let (res, spirv_msgs) = spirv.validate(&val_options);
630                         (res.then_some(spirv), format!("{}\n{}", msgs, spirv_msgs))
631                     } else {
632                         (None, msgs)
633                     }
634                 } else {
635                     (spirv, msgs)
636                 }
637             }
638             // do nothing if we got a library or binary
639             _ => {
640                 return true;
641             }
642         };
643 
644         d.spirv = spirv;
645         d.log = log;
646         d.options = options;
647 
648         if d.spirv.is_some() {
649             d.status = CL_BUILD_SUCCESS as cl_build_status;
650             d.bin_type = CL_PROGRAM_BINARY_TYPE_COMPILED_OBJECT;
651             true
652         } else {
653             d.status = CL_BUILD_ERROR;
654             false
655         }
656     }
657 
compile(&self, dev: &Device, options: String, headers: &[spirv::CLCHeader]) -> bool658     pub fn compile(&self, dev: &Device, options: String, headers: &[spirv::CLCHeader]) -> bool {
659         self.do_compile(dev, options, headers, &mut self.build_info())
660     }
661 
link( context: Arc<Context>, devs: &[&'static Device], progs: &[Arc<Program>], options: String, ) -> Arc<Program>662     pub fn link(
663         context: Arc<Context>,
664         devs: &[&'static Device],
665         progs: &[Arc<Program>],
666         options: String,
667     ) -> Arc<Program> {
668         let mut builds = HashMap::new();
669         let mut kernels = HashSet::new();
670         let mut locks: Vec<_> = progs.iter().map(|p| p.build_info()).collect();
671         let lib = options.contains("-create-library");
672 
673         for &d in devs {
674             let bins: Vec<_> = locks
675                 .iter_mut()
676                 .map(|l| l.dev_build(d).spirv.as_ref().unwrap())
677                 .collect();
678 
679             let (spirv, log) = spirv::SPIRVBin::link(&bins, lib);
680             let (spirv, log) = if Platform::dbg().validate_spirv {
681                 if let Some(spirv) = spirv {
682                     let val_options = clc_validator_options(d);
683                     let (res, spirv_msgs) = spirv.validate(&val_options);
684                     (res.then_some(spirv), format!("{}\n{}", log, spirv_msgs))
685                 } else {
686                     (None, log)
687                 }
688             } else {
689                 (spirv, log)
690             };
691 
692             let status;
693             let bin_type;
694             if let Some(spirv) = &spirv {
695                 for k in spirv.kernels() {
696                     kernels.insert(k);
697                 }
698                 status = CL_BUILD_SUCCESS as cl_build_status;
699                 bin_type = if lib {
700                     CL_PROGRAM_BINARY_TYPE_LIBRARY
701                 } else {
702                     CL_PROGRAM_BINARY_TYPE_EXECUTABLE
703                 };
704             } else {
705                 status = CL_BUILD_ERROR;
706                 bin_type = CL_PROGRAM_BINARY_TYPE_NONE;
707             };
708 
709             builds.insert(
710                 d,
711                 ProgramDevBuild {
712                     spirv: spirv,
713                     status: status,
714                     log: log,
715                     bin_type: bin_type,
716                     ..Default::default()
717                 },
718             );
719         }
720 
721         let mut build = ProgramBuild {
722             builds: builds,
723             spec_constants: HashMap::new(),
724             kernels: kernels.into_iter().collect(),
725             kernel_info: HashMap::new(),
726         };
727 
728         // Pre build nir kernels
729         build.build_nirs(false);
730 
731         Arc::new(Self {
732             base: CLObjectBase::new(RusticlTypes::Program),
733             context: context,
734             devs: devs.to_owned(),
735             src: ProgramSourceType::Linked,
736             build: Mutex::new(build),
737         })
738     }
739 
is_bin(&self) -> bool740     pub fn is_bin(&self) -> bool {
741         matches!(self.src, ProgramSourceType::Binary)
742     }
743 
is_il(&self) -> bool744     pub fn is_il(&self) -> bool {
745         matches!(self.src, ProgramSourceType::Il(_))
746     }
747 
is_src(&self) -> bool748     pub fn is_src(&self) -> bool {
749         matches!(self.src, ProgramSourceType::Src(_))
750     }
751 
get_spec_constant_size(&self, spec_id: u32) -> u8752     pub fn get_spec_constant_size(&self, spec_id: u32) -> u8 {
753         match &self.src {
754             ProgramSourceType::Il(il) => il
755                 .spec_constant(spec_id)
756                 .map_or(0, spirv::CLCSpecConstantType::size),
757             _ => unreachable!(),
758         }
759     }
760 
set_spec_constant(&self, spec_id: u32, data: &[u8])761     pub fn set_spec_constant(&self, spec_id: u32, data: &[u8]) {
762         let mut lock = self.build_info();
763         let mut val = nir_const_value::default();
764 
765         match data.len() {
766             1 => val.u8_ = u8::from_ne_bytes(data.try_into().unwrap()),
767             2 => val.u16_ = u16::from_ne_bytes(data.try_into().unwrap()),
768             4 => val.u32_ = u32::from_ne_bytes(data.try_into().unwrap()),
769             8 => val.u64_ = u64::from_ne_bytes(data.try_into().unwrap()),
770             _ => unreachable!("Spec constant with invalid size!"),
771         };
772 
773         lock.spec_constants.insert(spec_id, val);
774     }
775 }
776