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