1mod block;
8mod helpers;
9mod image;
10mod index;
11mod instructions;
12mod layout;
13mod ray;
14mod recyclable;
15mod selection;
16mod subgroup;
17mod writer;
18
19pub use spirv::Capability;
20
21use crate::arena::Handle;
22use crate::proc::{BoundsCheckPolicies, TypeResolution};
23
24use spirv::Word;
25use std::ops;
26use thiserror::Error;
27
28#[derive(Clone)]
29struct PhysicalLayout {
30 magic_number: Word,
31 version: Word,
32 generator: Word,
33 bound: Word,
34 instruction_schema: Word,
35}
36
37#[derive(Default)]
38struct LogicalLayout {
39 capabilities: Vec<Word>,
40 extensions: Vec<Word>,
41 ext_inst_imports: Vec<Word>,
42 memory_model: Vec<Word>,
43 entry_points: Vec<Word>,
44 execution_modes: Vec<Word>,
45 debugs: Vec<Word>,
46 annotations: Vec<Word>,
47 declarations: Vec<Word>,
48 function_declarations: Vec<Word>,
49 function_definitions: Vec<Word>,
50}
51
52struct Instruction {
53 op: spirv::Op,
54 wc: u32,
55 type_id: Option<Word>,
56 result_id: Option<Word>,
57 operands: Vec<Word>,
58}
59
60const BITS_PER_BYTE: crate::Bytes = 8;
61
62#[derive(Clone, Debug, Error)]
63pub enum Error {
64 #[error("The requested entry point couldn't be found")]
65 EntryPointNotFound,
66 #[error("target SPIRV-{0}.{1} is not supported")]
67 UnsupportedVersion(u8, u8),
68 #[error("using {0} requires at least one of the capabilities {1:?}, but none are available")]
69 MissingCapabilities(&'static str, Vec<Capability>),
70 #[error("unimplemented {0}")]
71 FeatureNotImplemented(&'static str),
72 #[error("module is not validated properly: {0}")]
73 Validation(&'static str),
74 #[error("overrides should not be present at this stage")]
75 Override,
76}
77
78#[derive(Default)]
79struct IdGenerator(Word);
80
81impl IdGenerator {
82 fn next(&mut self) -> Word {
83 self.0 += 1;
84 self.0
85 }
86}
87
88#[derive(Debug, Clone)]
89pub struct DebugInfo<'a> {
90 pub source_code: &'a str,
91 pub file_name: &'a std::path::Path,
92}
93
94struct Block {
106 label_id: Word,
107 body: Vec<Instruction>,
108}
109
110struct TerminatedBlock {
112 label_id: Word,
113 body: Vec<Instruction>,
114}
115
116impl Block {
117 const fn new(label_id: Word) -> Self {
118 Block {
119 label_id,
120 body: Vec::new(),
121 }
122 }
123}
124
125struct LocalVariable {
126 id: Word,
127 instruction: Instruction,
128}
129
130struct ResultMember {
131 id: Word,
132 type_id: Word,
133 built_in: Option<crate::BuiltIn>,
134}
135
136struct EntryPointContext {
137 argument_ids: Vec<Word>,
138 results: Vec<ResultMember>,
139}
140
141#[derive(Default)]
142struct Function {
143 signature: Option<Instruction>,
144 parameters: Vec<FunctionArgument>,
145 variables: crate::FastHashMap<Handle<crate::LocalVariable>, LocalVariable>,
146 blocks: Vec<TerminatedBlock>,
147 entry_point_context: Option<EntryPointContext>,
148}
149
150impl Function {
151 fn consume(&mut self, mut block: Block, termination: Instruction) {
152 block.body.push(termination);
153 self.blocks.push(TerminatedBlock {
154 label_id: block.label_id,
155 body: block.body,
156 })
157 }
158
159 fn parameter_id(&self, index: u32) -> Word {
160 match self.entry_point_context {
161 Some(ref context) => context.argument_ids[index as usize],
162 None => self.parameters[index as usize]
163 .instruction
164 .result_id
165 .unwrap(),
166 }
167 }
168}
169
170#[derive(Debug, PartialEq, Hash, Eq, Copy, Clone)]
179struct LocalImageType {
180 sampled_type: crate::ScalarKind,
181 dim: spirv::Dim,
182 flags: ImageTypeFlags,
183 image_format: spirv::ImageFormat,
184}
185
186bitflags::bitflags! {
187 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
189 pub struct ImageTypeFlags: u8 {
190 const DEPTH = 0x1;
191 const ARRAYED = 0x2;
192 const MULTISAMPLED = 0x4;
193 const SAMPLED = 0x8;
194 }
195}
196
197impl LocalImageType {
198 fn from_inner(dim: crate::ImageDimension, arrayed: bool, class: crate::ImageClass) -> Self {
200 let make_flags = |multi: bool, other: ImageTypeFlags| -> ImageTypeFlags {
201 let mut flags = other;
202 flags.set(ImageTypeFlags::ARRAYED, arrayed);
203 flags.set(ImageTypeFlags::MULTISAMPLED, multi);
204 flags
205 };
206
207 let dim = spirv::Dim::from(dim);
208
209 match class {
210 crate::ImageClass::Sampled { kind, multi } => LocalImageType {
211 sampled_type: kind,
212 dim,
213 flags: make_flags(multi, ImageTypeFlags::SAMPLED),
214 image_format: spirv::ImageFormat::Unknown,
215 },
216 crate::ImageClass::Depth { multi } => LocalImageType {
217 sampled_type: crate::ScalarKind::Float,
218 dim,
219 flags: make_flags(multi, ImageTypeFlags::DEPTH | ImageTypeFlags::SAMPLED),
220 image_format: spirv::ImageFormat::Unknown,
221 },
222 crate::ImageClass::Storage { format, access: _ } => LocalImageType {
223 sampled_type: crate::ScalarKind::from(format),
224 dim,
225 flags: make_flags(false, ImageTypeFlags::empty()),
226 image_format: format.into(),
227 },
228 }
229 }
230}
231
232#[derive(Debug, PartialEq, Hash, Eq, Copy, Clone)]
276enum LocalType {
277 Value {
279 vector_size: Option<crate::VectorSize>,
282 scalar: crate::Scalar,
283 pointer_space: Option<spirv::StorageClass>,
284 },
285 Matrix {
287 columns: crate::VectorSize,
288 rows: crate::VectorSize,
289 width: crate::Bytes,
290 },
291 Pointer {
292 base: Handle<crate::Type>,
293 class: spirv::StorageClass,
294 },
295 Image(LocalImageType),
296 SampledImage {
297 image_type_id: Word,
298 },
299 Sampler,
300 PointerToBindingArray {
306 base: Handle<crate::Type>,
307 size: u32,
308 space: crate::AddressSpace,
309 },
310 BindingArray {
311 base: Handle<crate::Type>,
312 size: u32,
313 },
314 AccelerationStructure,
315 RayQuery,
316}
317
318#[derive(Debug, PartialEq, Hash, Eq, Copy, Clone)]
341enum LookupType {
342 Handle(Handle<crate::Type>),
343 Local(LocalType),
344}
345
346impl From<LocalType> for LookupType {
347 fn from(local: LocalType) -> Self {
348 Self::Local(local)
349 }
350}
351
352#[derive(Debug, PartialEq, Clone, Hash, Eq)]
353struct LookupFunctionType {
354 parameter_type_ids: Vec<Word>,
355 return_type_id: Word,
356}
357
358fn make_local(inner: &crate::TypeInner) -> Option<LocalType> {
359 Some(match *inner {
360 crate::TypeInner::Scalar(scalar) | crate::TypeInner::Atomic(scalar) => LocalType::Value {
361 vector_size: None,
362 scalar,
363 pointer_space: None,
364 },
365 crate::TypeInner::Vector { size, scalar } => LocalType::Value {
366 vector_size: Some(size),
367 scalar,
368 pointer_space: None,
369 },
370 crate::TypeInner::Matrix {
371 columns,
372 rows,
373 scalar,
374 } => LocalType::Matrix {
375 columns,
376 rows,
377 width: scalar.width,
378 },
379 crate::TypeInner::Pointer { base, space } => LocalType::Pointer {
380 base,
381 class: helpers::map_storage_class(space),
382 },
383 crate::TypeInner::ValuePointer {
384 size,
385 scalar,
386 space,
387 } => LocalType::Value {
388 vector_size: size,
389 scalar,
390 pointer_space: Some(helpers::map_storage_class(space)),
391 },
392 crate::TypeInner::Image {
393 dim,
394 arrayed,
395 class,
396 } => LocalType::Image(LocalImageType::from_inner(dim, arrayed, class)),
397 crate::TypeInner::Sampler { comparison: _ } => LocalType::Sampler,
398 crate::TypeInner::AccelerationStructure => LocalType::AccelerationStructure,
399 crate::TypeInner::RayQuery => LocalType::RayQuery,
400 crate::TypeInner::Array { .. }
401 | crate::TypeInner::Struct { .. }
402 | crate::TypeInner::BindingArray { .. } => return None,
403 })
404}
405
406#[derive(Debug)]
407enum Dimension {
408 Scalar,
409 Vector,
410 Matrix,
411}
412
413#[derive(Default)]
422struct CachedExpressions {
423 ids: Vec<Word>,
424}
425impl CachedExpressions {
426 fn reset(&mut self, length: usize) {
427 self.ids.clear();
428 self.ids.resize(length, 0);
429 }
430}
431impl ops::Index<Handle<crate::Expression>> for CachedExpressions {
432 type Output = Word;
433 fn index(&self, h: Handle<crate::Expression>) -> &Word {
434 let id = &self.ids[h.index()];
435 if *id == 0 {
436 unreachable!("Expression {:?} is not cached!", h);
437 }
438 id
439 }
440}
441impl ops::IndexMut<Handle<crate::Expression>> for CachedExpressions {
442 fn index_mut(&mut self, h: Handle<crate::Expression>) -> &mut Word {
443 let id = &mut self.ids[h.index()];
444 if *id != 0 {
445 unreachable!("Expression {:?} is already cached!", h);
446 }
447 id
448 }
449}
450impl recyclable::Recyclable for CachedExpressions {
451 fn recycle(self) -> Self {
452 CachedExpressions {
453 ids: self.ids.recycle(),
454 }
455 }
456}
457
458#[derive(Eq, Hash, PartialEq)]
459enum CachedConstant {
460 Literal(crate::proc::HashableLiteral),
461 Composite {
462 ty: LookupType,
463 constituent_ids: Vec<Word>,
464 },
465 ZeroValue(Word),
466}
467
468#[derive(Clone)]
469struct GlobalVariable {
470 var_id: Word,
483
484 handle_id: Word,
488
489 access_id: Word,
501}
502
503impl GlobalVariable {
504 const fn dummy() -> Self {
505 Self {
506 var_id: 0,
507 handle_id: 0,
508 access_id: 0,
509 }
510 }
511
512 const fn new(id: Word) -> Self {
513 Self {
514 var_id: id,
515 handle_id: 0,
516 access_id: 0,
517 }
518 }
519
520 fn reset_for_function(&mut self) {
522 self.handle_id = 0;
523 self.access_id = 0;
524 }
525}
526
527struct FunctionArgument {
528 instruction: Instruction,
530 handle_id: Word,
531}
532
533struct ExpressionConstnessTracker {
540 inner: bit_set::BitSet,
541}
542
543impl ExpressionConstnessTracker {
544 fn from_arena(arena: &crate::Arena<crate::Expression>) -> Self {
545 let mut inner = bit_set::BitSet::new();
546 for (handle, expr) in arena.iter() {
547 let insert = match *expr {
548 crate::Expression::Literal(_)
549 | crate::Expression::ZeroValue(_)
550 | crate::Expression::Constant(_) => true,
551 crate::Expression::Compose { ref components, .. } => {
552 components.iter().all(|h| inner.contains(h.index()))
553 }
554 crate::Expression::Splat { value, .. } => inner.contains(value.index()),
555 _ => false,
556 };
557 if insert {
558 inner.insert(handle.index());
559 }
560 }
561 Self { inner }
562 }
563
564 fn is_const(&self, value: Handle<crate::Expression>) -> bool {
565 self.inner.contains(value.index())
566 }
567}
568
569struct BlockContext<'w> {
571 writer: &'w mut Writer,
573
574 ir_module: &'w crate::Module,
576
577 ir_function: &'w crate::Function,
579
580 fun_info: &'w crate::valid::FunctionInfo,
583
584 function: &'w mut Function,
586
587 cached: CachedExpressions,
589
590 temp_list: Vec<Word>,
592
593 expression_constness: ExpressionConstnessTracker,
595}
596
597impl BlockContext<'_> {
598 fn gen_id(&mut self) -> Word {
599 self.writer.id_gen.next()
600 }
601
602 fn get_type_id(&mut self, lookup_type: LookupType) -> Word {
603 self.writer.get_type_id(lookup_type)
604 }
605
606 fn get_expression_type_id(&mut self, tr: &TypeResolution) -> Word {
607 self.writer.get_expression_type_id(tr)
608 }
609
610 fn get_index_constant(&mut self, index: Word) -> Word {
611 self.writer.get_constant_scalar(crate::Literal::U32(index))
612 }
613
614 fn get_scope_constant(&mut self, scope: Word) -> Word {
615 self.writer
616 .get_constant_scalar(crate::Literal::I32(scope as _))
617 }
618
619 fn get_pointer_id(
620 &mut self,
621 handle: Handle<crate::Type>,
622 class: spirv::StorageClass,
623 ) -> Result<Word, Error> {
624 self.writer
625 .get_pointer_id(&self.ir_module.types, handle, class)
626 }
627}
628
629#[derive(Clone, Copy, Default)]
630struct LoopContext {
631 continuing_id: Option<Word>,
632 break_id: Option<Word>,
633}
634
635pub struct Writer {
636 physical_layout: PhysicalLayout,
637 logical_layout: LogicalLayout,
638 id_gen: IdGenerator,
639
640 capabilities_available: Option<crate::FastHashSet<Capability>>,
644
645 capabilities_used: crate::FastIndexSet<Capability>,
650
651 extensions_used: crate::FastIndexSet<&'static str>,
653
654 debugs: Vec<Instruction>,
655 annotations: Vec<Instruction>,
656 flags: WriterFlags,
657 bounds_check_policies: BoundsCheckPolicies,
658 zero_initialize_workgroup_memory: ZeroInitializeWorkgroupMemoryMode,
659 void_type: Word,
660 lookup_type: crate::FastHashMap<LookupType, Word>,
662 lookup_function: crate::FastHashMap<Handle<crate::Function>, Word>,
663 lookup_function_type: crate::FastHashMap<LookupFunctionType, Word>,
664 constant_ids: Vec<Word>,
666 cached_constants: crate::FastHashMap<CachedConstant, Word>,
667 global_variables: Vec<GlobalVariable>,
668 binding_map: BindingMap,
669
670 saved_cached: CachedExpressions,
673
674 gl450_ext_inst_id: Word,
675
676 temp_list: Vec<Word>,
678}
679
680bitflags::bitflags! {
681 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
682 pub struct WriterFlags: u32 {
683 const DEBUG = 0x1;
685 const ADJUST_COORDINATE_SPACE = 0x2;
687 const LABEL_VARYINGS = 0x4;
691 const FORCE_POINT_SIZE = 0x8;
694 const CLAMP_FRAG_DEPTH = 0x10;
696 }
697}
698
699#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
700#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
701#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
702pub struct BindingInfo {
703 pub binding_array_size: Option<u32>,
705}
706
707pub type BindingMap = std::collections::BTreeMap<crate::ResourceBinding, BindingInfo>;
709
710#[derive(Clone, Copy, Debug, PartialEq, Eq)]
711pub enum ZeroInitializeWorkgroupMemoryMode {
712 Native,
714 Polyfill,
716 None,
717}
718
719#[derive(Debug, Clone)]
720pub struct Options<'a> {
721 pub lang_version: (u8, u8),
723
724 pub flags: WriterFlags,
726
727 pub binding_map: BindingMap,
729
730 pub capabilities: Option<crate::FastHashSet<Capability>>,
735
736 pub bounds_check_policies: BoundsCheckPolicies,
739
740 pub zero_initialize_workgroup_memory: ZeroInitializeWorkgroupMemoryMode,
742
743 pub debug_info: Option<DebugInfo<'a>>,
744}
745
746impl<'a> Default for Options<'a> {
747 fn default() -> Self {
748 let mut flags = WriterFlags::ADJUST_COORDINATE_SPACE
749 | WriterFlags::LABEL_VARYINGS
750 | WriterFlags::CLAMP_FRAG_DEPTH;
751 if cfg!(debug_assertions) {
752 flags |= WriterFlags::DEBUG;
753 }
754 Options {
755 lang_version: (1, 0),
756 flags,
757 binding_map: BindingMap::default(),
758 capabilities: None,
759 bounds_check_policies: crate::proc::BoundsCheckPolicies::default(),
760 zero_initialize_workgroup_memory: ZeroInitializeWorkgroupMemoryMode::Polyfill,
761 debug_info: None,
762 }
763 }
764}
765
766#[derive(Debug, Clone)]
768#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
769#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
770pub struct PipelineOptions {
771 pub shader_stage: crate::ShaderStage,
773 pub entry_point: String,
777}
778
779pub fn write_vec(
780 module: &crate::Module,
781 info: &crate::valid::ModuleInfo,
782 options: &Options,
783 pipeline_options: Option<&PipelineOptions>,
784) -> Result<Vec<u32>, Error> {
785 let mut words: Vec<u32> = Vec::new();
786 let mut w = Writer::new(options)?;
787
788 w.write(
789 module,
790 info,
791 pipeline_options,
792 &options.debug_info,
793 &mut words,
794 )?;
795 Ok(words)
796}