1mod convert;
31mod error;
32mod function;
33mod image;
34mod next_block;
35mod null;
36
37pub use error::Error;
38
39use alloc::{borrow::ToOwned, string::String, vec, vec::Vec};
40use core::{convert::TryInto, mem, num::NonZeroU32};
41
42use half::f16;
43use petgraph::graphmap::GraphMap;
44
45use super::atomic_upgrade::Upgrades;
46use crate::{
47 arena::{Arena, Handle, UniqueArena},
48 proc::{Alignment, Layouter},
49 FastHashMap, FastHashSet, FastIndexMap,
50};
51use convert::*;
52use function::*;
53
54pub const SUPPORTED_CAPABILITIES: &[spirv::Capability] = &[
55 spirv::Capability::Shader,
56 spirv::Capability::VulkanMemoryModel,
57 spirv::Capability::ClipDistance,
58 spirv::Capability::CullDistance,
59 spirv::Capability::SampleRateShading,
60 spirv::Capability::DerivativeControl,
61 spirv::Capability::Matrix,
62 spirv::Capability::ImageQuery,
63 spirv::Capability::Sampled1D,
64 spirv::Capability::Image1D,
65 spirv::Capability::SampledCubeArray,
66 spirv::Capability::ImageCubeArray,
67 spirv::Capability::StorageImageExtendedFormats,
68 spirv::Capability::Int8,
69 spirv::Capability::Int16,
70 spirv::Capability::Int64,
71 spirv::Capability::Int64Atomics,
72 spirv::Capability::Float16,
73 spirv::Capability::AtomicFloat32AddEXT,
74 spirv::Capability::Float64,
75 spirv::Capability::Geometry,
76 spirv::Capability::MultiView,
77 spirv::Capability::StorageBuffer16BitAccess,
78 spirv::Capability::UniformAndStorageBuffer16BitAccess,
79 spirv::Capability::GroupNonUniform,
80 spirv::Capability::GroupNonUniformVote,
81 spirv::Capability::GroupNonUniformArithmetic,
82 spirv::Capability::GroupNonUniformBallot,
83 spirv::Capability::GroupNonUniformShuffle,
84 spirv::Capability::GroupNonUniformShuffleRelative,
85 spirv::Capability::RuntimeDescriptorArray,
86 spirv::Capability::StorageImageMultisample,
87 spirv::Capability::FragmentBarycentricKHR,
88 spirv::Capability::UniformBufferArrayDynamicIndexing,
90 spirv::Capability::StorageBufferArrayDynamicIndexing,
91];
92pub const SUPPORTED_EXTENSIONS: &[&str] = &[
93 "SPV_KHR_storage_buffer_storage_class",
94 "SPV_KHR_vulkan_memory_model",
95 "SPV_KHR_multiview",
96 "SPV_EXT_descriptor_indexing",
97 "SPV_EXT_shader_atomic_float_add",
98 "SPV_KHR_16bit_storage",
99 "SPV_KHR_non_semantic_info",
100 "SPV_KHR_fragment_shader_barycentric",
101];
102
103#[derive(Copy, Clone)]
104pub struct Instruction {
105 op: spirv::Op,
106 wc: u16,
107}
108
109impl Instruction {
110 const fn expect(self, count: u16) -> Result<(), Error> {
111 if self.wc == count {
112 Ok(())
113 } else {
114 Err(Error::InvalidOperandCount(self.op, self.wc))
115 }
116 }
117
118 fn expect_at_least(self, count: u16) -> Result<u16, Error> {
119 self.wc
120 .checked_sub(count)
121 .ok_or(Error::InvalidOperandCount(self.op, self.wc))
122 }
123}
124
125impl crate::TypeInner {
126 fn can_comparison_sample(&self, module: &crate::Module) -> bool {
127 match *self {
128 crate::TypeInner::Image {
129 class:
130 crate::ImageClass::Sampled {
131 kind: crate::ScalarKind::Float,
132 multi: false,
133 },
134 ..
135 } => true,
136 crate::TypeInner::Sampler { .. } => true,
137 crate::TypeInner::BindingArray { base, .. } => {
138 module.types[base].inner.can_comparison_sample(module)
139 }
140 _ => false,
141 }
142 }
143}
144
145#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
146pub enum ModuleState {
147 Empty,
148 Capability,
149 Extension,
150 ExtInstImport,
151 MemoryModel,
152 EntryPoint,
153 ExecutionMode,
154 Source,
155 Name,
156 ModuleProcessed,
157 Annotation,
158 Type,
159 Function,
160}
161
162trait LookupHelper {
163 type Target;
164 fn lookup(&self, key: spirv::Word) -> Result<&Self::Target, Error>;
165}
166
167impl<T> LookupHelper for FastHashMap<spirv::Word, T> {
168 type Target = T;
169 fn lookup(&self, key: spirv::Word) -> Result<&T, Error> {
170 self.get(&key).ok_or(Error::InvalidId(key))
171 }
172}
173
174impl crate::ImageDimension {
175 const fn required_coordinate_size(&self) -> Option<crate::VectorSize> {
176 match *self {
177 crate::ImageDimension::D1 => None,
178 crate::ImageDimension::D2 => Some(crate::VectorSize::Bi),
179 crate::ImageDimension::D3 => Some(crate::VectorSize::Tri),
180 crate::ImageDimension::Cube => Some(crate::VectorSize::Tri),
181 }
182 }
183}
184
185type MemberIndex = u32;
186
187bitflags::bitflags! {
188 #[derive(Clone, Copy, Debug, Default)]
189 struct DecorationFlags: u32 {
190 const NON_READABLE = 0x1;
191 const NON_WRITABLE = 0x2;
192 const COHERENT = 0x4;
193 const VOLATILE = 0x8;
194 }
195}
196
197impl DecorationFlags {
198 fn to_storage_access(self) -> crate::StorageAccess {
199 let mut access = crate::StorageAccess::LOAD | crate::StorageAccess::STORE;
200 if self.contains(DecorationFlags::NON_READABLE) {
201 access &= !crate::StorageAccess::LOAD;
202 }
203 if self.contains(DecorationFlags::NON_WRITABLE) {
204 access &= !crate::StorageAccess::STORE;
205 }
206 access
207 }
208
209 fn to_memory_decorations(self) -> crate::MemoryDecorations {
210 let mut decorations = crate::MemoryDecorations::empty();
211 if self.contains(DecorationFlags::COHERENT) {
212 decorations |= crate::MemoryDecorations::COHERENT;
213 }
214 if self.contains(DecorationFlags::VOLATILE) {
215 decorations |= crate::MemoryDecorations::VOLATILE;
216 }
217 decorations
218 }
219}
220
221#[derive(Debug, PartialEq)]
222enum Majority {
223 Column,
224 Row,
225}
226
227#[derive(Debug, Default)]
228struct Decoration {
229 name: Option<String>,
230 built_in: Option<spirv::Word>,
231 location: Option<spirv::Word>,
232 index: Option<spirv::Word>,
233 desc_set: Option<spirv::Word>,
234 desc_index: Option<spirv::Word>,
235 specialization_constant_id: Option<spirv::Word>,
236 storage_buffer: bool,
237 offset: Option<spirv::Word>,
238 array_stride: Option<NonZeroU32>,
239 matrix_stride: Option<NonZeroU32>,
240 matrix_major: Option<Majority>,
241 invariant: bool,
242 interpolation: Option<crate::Interpolation>,
243 sampling: Option<crate::Sampling>,
244 flags: DecorationFlags,
245}
246
247impl Decoration {
248 const fn debug_name(&self) -> &str {
249 match self.name {
250 Some(ref name) => name.as_str(),
251 None => "?",
252 }
253 }
254
255 const fn resource_binding(&self) -> Option<crate::ResourceBinding> {
256 match *self {
257 Decoration {
258 desc_set: Some(group),
259 desc_index: Some(binding),
260 ..
261 } => Some(crate::ResourceBinding { group, binding }),
262 _ => None,
263 }
264 }
265
266 fn io_binding(&self) -> Result<crate::Binding, Error> {
267 match *self {
268 Decoration {
269 built_in: Some(built_in),
270 location: None,
271 invariant,
272 ..
273 } => Ok(crate::Binding::BuiltIn(map_builtin(built_in, invariant)?)),
274 Decoration {
275 built_in: None,
276 location: Some(location),
277 index: Some(index),
278 ..
279 } => Ok(crate::Binding::Location {
280 location,
281 interpolation: None,
282 sampling: None,
283 blend_src: Some(index),
284 per_primitive: false,
285 }),
286 Decoration {
287 built_in: None,
288 location: Some(location),
289 interpolation,
290 sampling,
291 ..
292 } => Ok(crate::Binding::Location {
293 location,
294 interpolation,
295 sampling,
296 blend_src: None,
297 per_primitive: false,
298 }),
299 _ => Err(Error::MissingDecoration(spirv::Decoration::Location)),
300 }
301 }
302}
303
304#[derive(Debug)]
305struct LookupFunctionType {
306 parameter_type_ids: Vec<spirv::Word>,
307 return_type_id: spirv::Word,
308}
309
310struct LookupFunction {
311 handle: Handle<crate::Function>,
312 parameters_sampling: Vec<image::SamplingFlags>,
313}
314
315#[derive(Debug)]
316struct EntryPoint {
317 stage: crate::ShaderStage,
318 name: String,
319 early_depth_test: Option<crate::EarlyDepthTest>,
320 workgroup_size: [u32; 3],
321 variable_ids: Vec<spirv::Word>,
322}
323
324#[derive(Clone, Debug)]
325struct LookupType {
326 handle: Handle<crate::Type>,
327 base_id: Option<spirv::Word>,
328}
329
330#[derive(Debug)]
331enum Constant {
332 Constant(Handle<crate::Constant>),
333 Override(Handle<crate::Override>),
334}
335
336impl Constant {
337 const fn to_expr(&self) -> crate::Expression {
338 match *self {
339 Self::Constant(c) => crate::Expression::Constant(c),
340 Self::Override(o) => crate::Expression::Override(o),
341 }
342 }
343}
344
345#[derive(Debug)]
346struct LookupConstant {
347 inner: Constant,
348 type_id: spirv::Word,
349}
350
351#[derive(Debug)]
352enum Variable {
353 Global,
354 Input(crate::FunctionArgument),
355 Output(crate::FunctionResult),
356}
357
358#[derive(Debug)]
359struct LookupVariable {
360 inner: Variable,
361 handle: Handle<crate::GlobalVariable>,
362 type_id: spirv::Word,
363}
364
365#[derive(Clone, Debug)]
367struct LookupExpression {
368 handle: Handle<crate::Expression>,
375
376 type_id: spirv::Word,
378
379 block_id: spirv::Word,
384}
385
386#[derive(Debug)]
387struct LookupMember {
388 type_id: spirv::Word,
389 row_major: bool,
391}
392
393#[derive(Clone, Debug)]
394enum LookupLoadOverride {
395 Pending,
397 Loaded(Handle<crate::Expression>),
399}
400
401#[derive(PartialEq)]
402enum ExtendedClass {
403 Global(crate::AddressSpace),
404 Input,
405 Output,
406}
407
408#[derive(Clone, Debug)]
409pub struct Options {
410 pub adjust_coordinate_space: bool,
414 pub strict_capabilities: bool,
416 pub block_ctx_dump_prefix: Option<String>,
417}
418
419impl Default for Options {
420 fn default() -> Self {
421 Options {
422 adjust_coordinate_space: true,
423 strict_capabilities: true,
424 block_ctx_dump_prefix: None,
425 }
426 }
427}
428
429type BodyIndex = usize;
431
432#[derive(Debug)]
441enum BodyFragment {
442 BlockId(spirv::Word),
443 If {
444 condition: Handle<crate::Expression>,
445 accept: BodyIndex,
446 reject: BodyIndex,
447 },
448 Loop {
449 body: BodyIndex,
452
453 continuing: BodyIndex,
456
457 break_if: Option<Handle<crate::Expression>>,
461 },
462 Switch {
463 selector: Handle<crate::Expression>,
464 cases: Vec<(i32, BodyIndex)>,
465 default: BodyIndex,
466 },
467 Break,
468 Continue,
469}
470
471#[derive(Debug)]
478struct Body {
479 parent: usize,
481 data: Vec<BodyFragment>,
482}
483
484impl Body {
485 pub const fn with_parent(parent: usize) -> Self {
487 Body {
488 parent,
489 data: Vec::new(),
490 }
491 }
492}
493
494#[derive(Debug)]
495struct PhiExpression {
496 local: Handle<crate::LocalVariable>,
498 expressions: Vec<(spirv::Word, spirv::Word)>,
500}
501
502#[derive(Copy, Clone, Debug, PartialEq, Eq)]
503enum MergeBlockInformation {
504 LoopMerge,
505 LoopContinue,
506 SelectionMerge,
507 SwitchMerge,
508}
509
510#[derive(Debug)]
551struct BlockContext<'function> {
552 phis: Vec<PhiExpression>,
555
556 blocks: FastHashMap<spirv::Word, crate::Block>,
563
564 body_for_label: FastHashMap<spirv::Word, BodyIndex>,
582
583 mergers: FastHashMap<spirv::Word, MergeBlockInformation>,
585
586 bodies: Vec<Body>,
590
591 module: &'function mut crate::Module,
593
594 function_id: spirv::Word,
596 expressions: &'function mut Arena<crate::Expression>,
598 local_arena: &'function mut Arena<crate::LocalVariable>,
600 arguments: &'function [crate::FunctionArgument],
602 parameter_sampling: &'function mut [image::SamplingFlags],
604}
605
606enum SignAnchor {
607 Result,
608 Operand,
609}
610
611pub struct Frontend<I> {
612 data: I,
613 data_offset: usize,
614 state: ModuleState,
615 layouter: Layouter,
616 temp_bytes: Vec<u8>,
617 ext_glsl_id: Option<spirv::Word>,
618 ext_non_semantic_id: Option<spirv::Word>,
619 future_decor: FastHashMap<spirv::Word, Decoration>,
620 future_member_decor: FastHashMap<(spirv::Word, MemberIndex), Decoration>,
621 lookup_member: FastHashMap<(Handle<crate::Type>, MemberIndex), LookupMember>,
622 handle_sampling: FastHashMap<Handle<crate::GlobalVariable>, image::SamplingFlags>,
623
624 upgrade_atomics: Upgrades,
629
630 lookup_type: FastHashMap<spirv::Word, LookupType>,
631 lookup_void_type: Option<spirv::Word>,
632 lookup_storage_buffer_types: FastHashMap<Handle<crate::Type>, crate::StorageAccess>,
633 lookup_constant: FastHashMap<spirv::Word, LookupConstant>,
634 lookup_variable: FastHashMap<spirv::Word, LookupVariable>,
635 lookup_expression: FastHashMap<spirv::Word, LookupExpression>,
636 lookup_load_override: FastHashMap<spirv::Word, LookupLoadOverride>,
638 lookup_sampled_image: FastHashMap<spirv::Word, image::LookupSampledImage>,
639 lookup_function_type: FastHashMap<spirv::Word, LookupFunctionType>,
640 lookup_function: FastHashMap<spirv::Word, LookupFunction>,
641 lookup_entry_point: FastHashMap<spirv::Word, EntryPoint>,
642 deferred_entry_points: Vec<(EntryPoint, spirv::Word)>,
645 deferred_function_calls: Vec<spirv::Word>,
648 dummy_functions: Arena<crate::Function>,
649 function_call_graph: GraphMap<
653 spirv::Word,
654 (),
655 petgraph::Directed,
656 core::hash::BuildHasherDefault<rustc_hash::FxHasher>,
657 >,
658 options: Options,
659
660 switch_cases: FastIndexMap<spirv::Word, (BodyIndex, Vec<i32>)>,
665
666 gl_per_vertex_builtin_access: FastHashSet<crate::BuiltIn>,
675}
676
677impl<I: Iterator<Item = u32>> Frontend<I> {
678 pub fn new(data: I, options: &Options) -> Self {
679 Frontend {
680 data,
681 data_offset: 0,
682 state: ModuleState::Empty,
683 layouter: Layouter::default(),
684 temp_bytes: Vec::new(),
685 ext_glsl_id: None,
686 ext_non_semantic_id: None,
687 future_decor: FastHashMap::default(),
688 future_member_decor: FastHashMap::default(),
689 handle_sampling: FastHashMap::default(),
690 lookup_member: FastHashMap::default(),
691 upgrade_atomics: Default::default(),
692 lookup_type: FastHashMap::default(),
693 lookup_void_type: None,
694 lookup_storage_buffer_types: FastHashMap::default(),
695 lookup_constant: FastHashMap::default(),
696 lookup_variable: FastHashMap::default(),
697 lookup_expression: FastHashMap::default(),
698 lookup_load_override: FastHashMap::default(),
699 lookup_sampled_image: FastHashMap::default(),
700 lookup_function_type: FastHashMap::default(),
701 lookup_function: FastHashMap::default(),
702 lookup_entry_point: FastHashMap::default(),
703 deferred_entry_points: Vec::default(),
704 deferred_function_calls: Vec::default(),
705 dummy_functions: Arena::new(),
706 function_call_graph: GraphMap::new(),
707 options: options.clone(),
708 switch_cases: FastIndexMap::default(),
709 gl_per_vertex_builtin_access: FastHashSet::default(),
710 }
711 }
712
713 fn span_from(&self, from: usize) -> crate::Span {
714 crate::Span::from(from..self.data_offset)
715 }
716
717 fn span_from_with_op(&self, from: usize) -> crate::Span {
718 crate::Span::from((from - 4)..self.data_offset)
719 }
720
721 fn next(&mut self) -> Result<u32, Error> {
722 if let Some(res) = self.data.next() {
723 self.data_offset += 4;
724 Ok(res)
725 } else {
726 Err(Error::IncompleteData)
727 }
728 }
729
730 fn next_inst(&mut self) -> Result<Instruction, Error> {
731 let word = self.next()?;
732 let (wc, opcode) = ((word >> 16) as u16, (word & 0xffff) as u16);
733 if wc == 0 {
734 return Err(Error::InvalidWordCount);
735 }
736 let op = spirv::Op::from_u32(opcode as u32).ok_or(Error::UnknownInstruction(opcode))?;
737
738 Ok(Instruction { op, wc })
739 }
740
741 fn next_string(&mut self, mut count: u16) -> Result<(String, u16), Error> {
742 self.temp_bytes.clear();
743 loop {
744 if count == 0 {
745 return Err(Error::BadString);
746 }
747 count -= 1;
748 let chars = self.next()?.to_le_bytes();
749 let pos = chars.iter().position(|&c| c == 0).unwrap_or(4);
750 self.temp_bytes.extend_from_slice(&chars[..pos]);
751 if pos < 4 {
752 break;
753 }
754 }
755 core::str::from_utf8(&self.temp_bytes)
756 .map(|s| (s.to_owned(), count))
757 .map_err(|_| Error::BadString)
758 }
759
760 fn next_decoration(
761 &mut self,
762 inst: Instruction,
763 base_words: u16,
764 dec: &mut Decoration,
765 ) -> Result<(), Error> {
766 let raw = self.next()?;
767 let dec_typed = spirv::Decoration::from_u32(raw).ok_or(Error::InvalidDecoration(raw))?;
768 log::trace!("\t\t{}: {:?}", dec.debug_name(), dec_typed);
769 match dec_typed {
770 spirv::Decoration::BuiltIn => {
771 inst.expect(base_words + 2)?;
772 dec.built_in = Some(self.next()?);
773 }
774 spirv::Decoration::Location => {
775 inst.expect(base_words + 2)?;
776 dec.location = Some(self.next()?);
777 }
778 spirv::Decoration::Index => {
779 inst.expect(base_words + 2)?;
780 dec.index = Some(self.next()?);
781 }
782 spirv::Decoration::DescriptorSet => {
783 inst.expect(base_words + 2)?;
784 dec.desc_set = Some(self.next()?);
785 }
786 spirv::Decoration::Binding => {
787 inst.expect(base_words + 2)?;
788 dec.desc_index = Some(self.next()?);
789 }
790 spirv::Decoration::BufferBlock => {
791 dec.storage_buffer = true;
792 }
793 spirv::Decoration::Offset => {
794 inst.expect(base_words + 2)?;
795 dec.offset = Some(self.next()?);
796 }
797 spirv::Decoration::ArrayStride => {
798 inst.expect(base_words + 2)?;
799 dec.array_stride = NonZeroU32::new(self.next()?);
800 }
801 spirv::Decoration::MatrixStride => {
802 inst.expect(base_words + 2)?;
803 dec.matrix_stride = NonZeroU32::new(self.next()?);
804 }
805 spirv::Decoration::Invariant => {
806 dec.invariant = true;
807 }
808 spirv::Decoration::NoPerspective => {
809 dec.interpolation = Some(crate::Interpolation::Linear);
810 }
811 spirv::Decoration::Flat => {
812 dec.interpolation = Some(crate::Interpolation::Flat);
813 }
814 spirv::Decoration::PerVertexKHR => {
815 dec.interpolation = Some(crate::Interpolation::PerVertex);
816 }
817 spirv::Decoration::Centroid => {
818 dec.sampling = Some(crate::Sampling::Centroid);
819 }
820 spirv::Decoration::Sample => {
821 dec.sampling = Some(crate::Sampling::Sample);
822 }
823 spirv::Decoration::NonReadable => {
824 dec.flags |= DecorationFlags::NON_READABLE;
825 }
826 spirv::Decoration::NonWritable => {
827 dec.flags |= DecorationFlags::NON_WRITABLE;
828 }
829 spirv::Decoration::Coherent => {
830 dec.flags |= DecorationFlags::COHERENT;
831 }
832 spirv::Decoration::Volatile => {
833 dec.flags |= DecorationFlags::VOLATILE;
834 }
835 spirv::Decoration::ColMajor => {
836 dec.matrix_major = Some(Majority::Column);
837 }
838 spirv::Decoration::RowMajor => {
839 dec.matrix_major = Some(Majority::Row);
840 }
841 spirv::Decoration::SpecId => {
842 dec.specialization_constant_id = Some(self.next()?);
843 }
844 other => {
845 let level = match other {
846 spirv::Decoration::Block => log::Level::Debug,
850 _ => log::Level::Warn,
851 };
852
853 log::log!(level, "Unknown decoration {other:?}");
854 for _ in base_words + 1..inst.wc {
855 let _var = self.next()?;
856 }
857 }
858 }
859 Ok(())
860 }
861
862 fn get_expr_handle(
933 &self,
934 id: spirv::Word,
935 lookup: &LookupExpression,
936 ctx: &mut BlockContext,
937 emitter: &mut crate::proc::Emitter,
938 block: &mut crate::Block,
939 body_idx: BodyIndex,
940 ) -> Handle<crate::Expression> {
941 let expr_body_idx = ctx
943 .body_for_label
944 .get(&lookup.block_id)
945 .copied()
946 .unwrap_or(0);
947
948 if is_parent(body_idx, expr_body_idx, ctx) {
955 lookup.handle
956 } else {
957 let ty = self.lookup_type[&lookup.type_id].handle;
960 let local = ctx.local_arena.append(
961 crate::LocalVariable {
962 name: None,
963 ty,
964 init: None,
965 },
966 crate::Span::default(),
967 );
968
969 block.extend(emitter.finish(ctx.expressions));
970 let pointer = ctx.expressions.append(
971 crate::Expression::LocalVariable(local),
972 crate::Span::default(),
973 );
974 emitter.start(ctx.expressions);
975 let expr = ctx
976 .expressions
977 .append(crate::Expression::Load { pointer }, crate::Span::default());
978
979 ctx.phis.push(PhiExpression {
988 local,
989 expressions: vec![(id, lookup.block_id)],
990 });
991
992 expr
993 }
994 }
995
996 fn parse_expr_unary_op(
997 &mut self,
998 ctx: &mut BlockContext,
999 emitter: &mut crate::proc::Emitter,
1000 block: &mut crate::Block,
1001 block_id: spirv::Word,
1002 body_idx: usize,
1003 op: crate::UnaryOperator,
1004 ) -> Result<(), Error> {
1005 let start = self.data_offset;
1006 let result_type_id = self.next()?;
1007 let result_id = self.next()?;
1008 let p_id = self.next()?;
1009
1010 let p_lexp = self.lookup_expression.lookup(p_id)?;
1011 let handle = self.get_expr_handle(p_id, p_lexp, ctx, emitter, block, body_idx);
1012
1013 let expr = crate::Expression::Unary { op, expr: handle };
1014 self.lookup_expression.insert(
1015 result_id,
1016 LookupExpression {
1017 handle: ctx.expressions.append(expr, self.span_from_with_op(start)),
1018 type_id: result_type_id,
1019 block_id,
1020 },
1021 );
1022 Ok(())
1023 }
1024
1025 fn parse_expr_binary_op(
1026 &mut self,
1027 ctx: &mut BlockContext,
1028 emitter: &mut crate::proc::Emitter,
1029 block: &mut crate::Block,
1030 block_id: spirv::Word,
1031 body_idx: usize,
1032 op: crate::BinaryOperator,
1033 ) -> Result<(), Error> {
1034 let start = self.data_offset;
1035 let result_type_id = self.next()?;
1036 let result_id = self.next()?;
1037 let p1_id = self.next()?;
1038 let p2_id = self.next()?;
1039
1040 let p1_lexp = self.lookup_expression.lookup(p1_id)?;
1041 let left = self.get_expr_handle(p1_id, p1_lexp, ctx, emitter, block, body_idx);
1042 let p2_lexp = self.lookup_expression.lookup(p2_id)?;
1043 let right = self.get_expr_handle(p2_id, p2_lexp, ctx, emitter, block, body_idx);
1044
1045 let expr = crate::Expression::Binary { op, left, right };
1046 self.lookup_expression.insert(
1047 result_id,
1048 LookupExpression {
1049 handle: ctx.expressions.append(expr, self.span_from_with_op(start)),
1050 type_id: result_type_id,
1051 block_id,
1052 },
1053 );
1054 Ok(())
1055 }
1056
1057 fn parse_expr_unary_op_sign_adjusted(
1060 &mut self,
1061 ctx: &mut BlockContext,
1062 emitter: &mut crate::proc::Emitter,
1063 block: &mut crate::Block,
1064 block_id: spirv::Word,
1065 body_idx: usize,
1066 op: crate::UnaryOperator,
1067 ) -> Result<(), Error> {
1068 let start = self.data_offset;
1069 let result_type_id = self.next()?;
1070 let result_id = self.next()?;
1071 let p1_id = self.next()?;
1072 let span = self.span_from_with_op(start);
1073
1074 let p1_lexp = self.lookup_expression.lookup(p1_id)?;
1075 let left = self.get_expr_handle(p1_id, p1_lexp, ctx, emitter, block, body_idx);
1076
1077 let result_lookup_ty = self.lookup_type.lookup(result_type_id)?;
1078 let kind = ctx.module.types[result_lookup_ty.handle]
1079 .inner
1080 .scalar_kind()
1081 .unwrap();
1082
1083 let expr = crate::Expression::Unary {
1084 op,
1085 expr: if p1_lexp.type_id == result_type_id {
1086 left
1087 } else {
1088 ctx.expressions.append(
1089 crate::Expression::As {
1090 expr: left,
1091 kind,
1092 convert: None,
1093 },
1094 span,
1095 )
1096 },
1097 };
1098
1099 self.lookup_expression.insert(
1100 result_id,
1101 LookupExpression {
1102 handle: ctx.expressions.append(expr, span),
1103 type_id: result_type_id,
1104 block_id,
1105 },
1106 );
1107 Ok(())
1108 }
1109
1110 #[allow(clippy::too_many_arguments)]
1114 fn parse_expr_binary_op_sign_adjusted(
1115 &mut self,
1116 ctx: &mut BlockContext,
1117 emitter: &mut crate::proc::Emitter,
1118 block: &mut crate::Block,
1119 block_id: spirv::Word,
1120 body_idx: usize,
1121 op: crate::BinaryOperator,
1122 anchor: SignAnchor,
1126 ) -> Result<(), Error> {
1127 let start = self.data_offset;
1128 let result_type_id = self.next()?;
1129 let result_id = self.next()?;
1130 let p1_id = self.next()?;
1131 let p2_id = self.next()?;
1132 let span = self.span_from_with_op(start);
1133
1134 let p1_lexp = self.lookup_expression.lookup(p1_id)?;
1135 let left = self.get_expr_handle(p1_id, p1_lexp, ctx, emitter, block, body_idx);
1136 let p2_lexp = self.lookup_expression.lookup(p2_id)?;
1137 let right = self.get_expr_handle(p2_id, p2_lexp, ctx, emitter, block, body_idx);
1138
1139 let expected_type_id = match anchor {
1140 SignAnchor::Result => result_type_id,
1141 SignAnchor::Operand => p1_lexp.type_id,
1142 };
1143 let expected_lookup_ty = self.lookup_type.lookup(expected_type_id)?;
1144 let kind = ctx.module.types[expected_lookup_ty.handle]
1145 .inner
1146 .scalar_kind()
1147 .unwrap();
1148
1149 let expr = crate::Expression::Binary {
1150 op,
1151 left: if p1_lexp.type_id == expected_type_id {
1152 left
1153 } else {
1154 ctx.expressions.append(
1155 crate::Expression::As {
1156 expr: left,
1157 kind,
1158 convert: None,
1159 },
1160 span,
1161 )
1162 },
1163 right: if p2_lexp.type_id == expected_type_id {
1164 right
1165 } else {
1166 ctx.expressions.append(
1167 crate::Expression::As {
1168 expr: right,
1169 kind,
1170 convert: None,
1171 },
1172 span,
1173 )
1174 },
1175 };
1176
1177 self.lookup_expression.insert(
1178 result_id,
1179 LookupExpression {
1180 handle: ctx.expressions.append(expr, span),
1181 type_id: result_type_id,
1182 block_id,
1183 },
1184 );
1185 Ok(())
1186 }
1187
1188 #[allow(clippy::too_many_arguments)]
1192 fn parse_expr_int_comparison(
1193 &mut self,
1194 ctx: &mut BlockContext,
1195 emitter: &mut crate::proc::Emitter,
1196 block: &mut crate::Block,
1197 block_id: spirv::Word,
1198 body_idx: usize,
1199 op: crate::BinaryOperator,
1200 kind: crate::ScalarKind,
1201 ) -> Result<(), Error> {
1202 let start = self.data_offset;
1203 let result_type_id = self.next()?;
1204 let result_id = self.next()?;
1205 let p1_id = self.next()?;
1206 let p2_id = self.next()?;
1207 let span = self.span_from_with_op(start);
1208
1209 let p1_lexp = self.lookup_expression.lookup(p1_id)?;
1210 let left = self.get_expr_handle(p1_id, p1_lexp, ctx, emitter, block, body_idx);
1211 let p1_lookup_ty = self.lookup_type.lookup(p1_lexp.type_id)?;
1212 let p1_kind = ctx.module.types[p1_lookup_ty.handle]
1213 .inner
1214 .scalar_kind()
1215 .unwrap();
1216 let p2_lexp = self.lookup_expression.lookup(p2_id)?;
1217 let right = self.get_expr_handle(p2_id, p2_lexp, ctx, emitter, block, body_idx);
1218 let p2_lookup_ty = self.lookup_type.lookup(p2_lexp.type_id)?;
1219 let p2_kind = ctx.module.types[p2_lookup_ty.handle]
1220 .inner
1221 .scalar_kind()
1222 .unwrap();
1223
1224 let expr = crate::Expression::Binary {
1225 op,
1226 left: if p1_kind == kind {
1227 left
1228 } else {
1229 ctx.expressions.append(
1230 crate::Expression::As {
1231 expr: left,
1232 kind,
1233 convert: None,
1234 },
1235 span,
1236 )
1237 },
1238 right: if p2_kind == kind {
1239 right
1240 } else {
1241 ctx.expressions.append(
1242 crate::Expression::As {
1243 expr: right,
1244 kind,
1245 convert: None,
1246 },
1247 span,
1248 )
1249 },
1250 };
1251
1252 self.lookup_expression.insert(
1253 result_id,
1254 LookupExpression {
1255 handle: ctx.expressions.append(expr, span),
1256 type_id: result_type_id,
1257 block_id,
1258 },
1259 );
1260 Ok(())
1261 }
1262
1263 fn parse_expr_shift_op(
1264 &mut self,
1265 ctx: &mut BlockContext,
1266 emitter: &mut crate::proc::Emitter,
1267 block: &mut crate::Block,
1268 block_id: spirv::Word,
1269 body_idx: usize,
1270 op: crate::BinaryOperator,
1271 ) -> Result<(), Error> {
1272 let start = self.data_offset;
1273 let result_type_id = self.next()?;
1274 let result_id = self.next()?;
1275 let p1_id = self.next()?;
1276 let p2_id = self.next()?;
1277
1278 let span = self.span_from_with_op(start);
1279
1280 let p1_lexp = self.lookup_expression.lookup(p1_id)?;
1281 let left = self.get_expr_handle(p1_id, p1_lexp, ctx, emitter, block, body_idx);
1282 let p2_lexp = self.lookup_expression.lookup(p2_id)?;
1283 let p2_handle = self.get_expr_handle(p2_id, p2_lexp, ctx, emitter, block, body_idx);
1284 let right = ctx.expressions.append(
1286 crate::Expression::As {
1287 expr: p2_handle,
1288 kind: crate::ScalarKind::Uint,
1289 convert: None,
1290 },
1291 span,
1292 );
1293
1294 let expr = crate::Expression::Binary { op, left, right };
1295 self.lookup_expression.insert(
1296 result_id,
1297 LookupExpression {
1298 handle: ctx.expressions.append(expr, span),
1299 type_id: result_type_id,
1300 block_id,
1301 },
1302 );
1303 Ok(())
1304 }
1305
1306 fn parse_expr_derivative(
1307 &mut self,
1308 ctx: &mut BlockContext,
1309 emitter: &mut crate::proc::Emitter,
1310 block: &mut crate::Block,
1311 block_id: spirv::Word,
1312 body_idx: usize,
1313 (axis, ctrl): (crate::DerivativeAxis, crate::DerivativeControl),
1314 ) -> Result<(), Error> {
1315 let start = self.data_offset;
1316 let result_type_id = self.next()?;
1317 let result_id = self.next()?;
1318 let arg_id = self.next()?;
1319
1320 let arg_lexp = self.lookup_expression.lookup(arg_id)?;
1321 let arg_handle = self.get_expr_handle(arg_id, arg_lexp, ctx, emitter, block, body_idx);
1322
1323 let expr = crate::Expression::Derivative {
1324 axis,
1325 ctrl,
1326 expr: arg_handle,
1327 };
1328 self.lookup_expression.insert(
1329 result_id,
1330 LookupExpression {
1331 handle: ctx.expressions.append(expr, self.span_from_with_op(start)),
1332 type_id: result_type_id,
1333 block_id,
1334 },
1335 );
1336 Ok(())
1337 }
1338
1339 #[allow(clippy::too_many_arguments)]
1340 fn insert_composite(
1341 &self,
1342 root_expr: Handle<crate::Expression>,
1343 root_type_id: spirv::Word,
1344 object_expr: Handle<crate::Expression>,
1345 selections: &[spirv::Word],
1346 type_arena: &UniqueArena<crate::Type>,
1347 expressions: &mut Arena<crate::Expression>,
1348 span: crate::Span,
1349 ) -> Result<Handle<crate::Expression>, Error> {
1350 let selection = match selections.first() {
1351 Some(&index) => index,
1352 None => return Ok(object_expr),
1353 };
1354 let root_span = expressions.get_span(root_expr);
1355 let root_lookup = self.lookup_type.lookup(root_type_id)?;
1356
1357 let (count, child_type_id) = match type_arena[root_lookup.handle].inner {
1358 crate::TypeInner::Struct { ref members, .. } => {
1359 let child_member = self
1360 .lookup_member
1361 .get(&(root_lookup.handle, selection))
1362 .ok_or(Error::InvalidAccessType(root_type_id))?;
1363 (members.len(), child_member.type_id)
1364 }
1365 crate::TypeInner::Array { size, .. } => {
1366 let size = match size {
1367 crate::ArraySize::Constant(size) => size.get(),
1368 crate::ArraySize::Pending(_) => {
1369 unreachable!();
1370 }
1371 crate::ArraySize::Dynamic => {
1373 return Err(Error::InvalidAccessType(root_type_id))
1374 }
1375 };
1376
1377 let child_type_id = root_lookup
1378 .base_id
1379 .ok_or(Error::InvalidAccessType(root_type_id))?;
1380
1381 (size as usize, child_type_id)
1382 }
1383 crate::TypeInner::Vector { size, .. }
1384 | crate::TypeInner::Matrix { columns: size, .. } => {
1385 let child_type_id = root_lookup
1386 .base_id
1387 .ok_or(Error::InvalidAccessType(root_type_id))?;
1388 (size as usize, child_type_id)
1389 }
1390 _ => return Err(Error::InvalidAccessType(root_type_id)),
1391 };
1392
1393 let mut components = Vec::with_capacity(count);
1394 for index in 0..count as u32 {
1395 let expr = expressions.append(
1396 crate::Expression::AccessIndex {
1397 base: root_expr,
1398 index,
1399 },
1400 if index == selection { span } else { root_span },
1401 );
1402 components.push(expr);
1403 }
1404 components[selection as usize] = self.insert_composite(
1405 components[selection as usize],
1406 child_type_id,
1407 object_expr,
1408 &selections[1..],
1409 type_arena,
1410 expressions,
1411 span,
1412 )?;
1413
1414 Ok(expressions.append(
1415 crate::Expression::Compose {
1416 ty: root_lookup.handle,
1417 components,
1418 },
1419 span,
1420 ))
1421 }
1422
1423 fn get_exp_and_base_ty_handles(
1437 &self,
1438 pointer_id: spirv::Word,
1439 ctx: &mut BlockContext,
1440 emitter: &mut crate::proc::Emitter,
1441 block: &mut crate::Block,
1442 body_idx: usize,
1443 ) -> Result<(Handle<crate::Expression>, Handle<crate::Type>), Error> {
1444 log::trace!("\t\t\tlooking up pointer expr {pointer_id:?}");
1445 let p_lexp_handle;
1446 let p_lexp_ty_id;
1447 {
1448 let lexp = self.lookup_expression.lookup(pointer_id)?;
1449 p_lexp_handle = self.get_expr_handle(pointer_id, lexp, ctx, emitter, block, body_idx);
1450 p_lexp_ty_id = lexp.type_id;
1451 };
1452
1453 log::trace!("\t\t\tlooking up pointer type {pointer_id:?}");
1454 let p_ty = self.lookup_type.lookup(p_lexp_ty_id)?;
1455 let p_ty_base_id = p_ty.base_id.ok_or(Error::InvalidAccessType(p_lexp_ty_id))?;
1456
1457 log::trace!("\t\t\tlooking up pointer base type {p_ty_base_id:?} of {p_ty:?}");
1458 let p_base_ty = self.lookup_type.lookup(p_ty_base_id)?;
1459
1460 Ok((p_lexp_handle, p_base_ty.handle))
1461 }
1462
1463 #[allow(clippy::too_many_arguments)]
1464 fn parse_atomic_expr_with_value(
1465 &mut self,
1466 inst: Instruction,
1467 emitter: &mut crate::proc::Emitter,
1468 ctx: &mut BlockContext,
1469 block: &mut crate::Block,
1470 block_id: spirv::Word,
1471 body_idx: usize,
1472 atomic_function: crate::AtomicFunction,
1473 ) -> Result<(), Error> {
1474 inst.expect(7)?;
1475 let start = self.data_offset;
1476 let result_type_id = self.next()?;
1477 let result_id = self.next()?;
1478 let pointer_id = self.next()?;
1479 let _scope_id = self.next()?;
1480 let _memory_semantics_id = self.next()?;
1481 let value_id = self.next()?;
1482 let span = self.span_from_with_op(start);
1483
1484 let (p_lexp_handle, p_base_ty_handle) =
1485 self.get_exp_and_base_ty_handles(pointer_id, ctx, emitter, block, body_idx)?;
1486
1487 log::trace!("\t\t\tlooking up value expr {value_id:?}");
1488 let v_lexp_handle = self.lookup_expression.lookup(value_id)?.handle;
1489
1490 block.extend(emitter.finish(ctx.expressions));
1491 let r_lexp_handle = {
1493 let expr = crate::Expression::AtomicResult {
1494 ty: p_base_ty_handle,
1495 comparison: false,
1496 };
1497 let handle = ctx.expressions.append(expr, span);
1498 self.lookup_expression.insert(
1499 result_id,
1500 LookupExpression {
1501 handle,
1502 type_id: result_type_id,
1503 block_id,
1504 },
1505 );
1506 handle
1507 };
1508 emitter.start(ctx.expressions);
1509
1510 let stmt = crate::Statement::Atomic {
1512 pointer: p_lexp_handle,
1513 fun: atomic_function,
1514 value: v_lexp_handle,
1515 result: Some(r_lexp_handle),
1516 };
1517 block.push(stmt, span);
1518
1519 self.record_atomic_access(ctx, p_lexp_handle)?;
1521
1522 Ok(())
1523 }
1524
1525 fn make_expression_storage(
1526 &mut self,
1527 globals: &Arena<crate::GlobalVariable>,
1528 constants: &Arena<crate::Constant>,
1529 overrides: &Arena<crate::Override>,
1530 ) -> Arena<crate::Expression> {
1531 let mut expressions = Arena::new();
1532 assert!(self.lookup_expression.is_empty());
1533 for (&id, var) in self.lookup_variable.iter() {
1535 let span = globals.get_span(var.handle);
1536 let handle = expressions.append(crate::Expression::GlobalVariable(var.handle), span);
1537 self.lookup_expression.insert(
1538 id,
1539 LookupExpression {
1540 type_id: var.type_id,
1541 handle,
1542 block_id: 0,
1546 },
1547 );
1548 }
1549 for (&id, con) in self.lookup_constant.iter() {
1551 let (expr, span) = match con.inner {
1552 Constant::Constant(c) => (crate::Expression::Constant(c), constants.get_span(c)),
1553 Constant::Override(o) => (crate::Expression::Override(o), overrides.get_span(o)),
1554 };
1555 let handle = expressions.append(expr, span);
1556 self.lookup_expression.insert(
1557 id,
1558 LookupExpression {
1559 type_id: con.type_id,
1560 handle,
1561 block_id: 0,
1565 },
1566 );
1567 }
1568 expressions
1570 }
1571
1572 fn switch(&mut self, state: ModuleState, op: spirv::Op) -> Result<(), Error> {
1573 if state < self.state {
1574 Err(Error::UnsupportedInstruction(self.state, op))
1575 } else {
1576 self.state = state;
1577 Ok(())
1578 }
1579 }
1580
1581 fn patch_statements(
1584 &mut self,
1585 statements: &mut crate::Block,
1586 expressions: &mut Arena<crate::Expression>,
1587 fun_parameter_sampling: &mut [image::SamplingFlags],
1588 ) -> Result<(), Error> {
1589 use crate::Statement as S;
1590 let mut i = 0usize;
1591 while i < statements.len() {
1592 match statements[i] {
1593 S::Emit(_) => {}
1594 S::Block(ref mut block) => {
1595 self.patch_statements(block, expressions, fun_parameter_sampling)?;
1596 }
1597 S::If {
1598 condition: _,
1599 ref mut accept,
1600 ref mut reject,
1601 } => {
1602 self.patch_statements(reject, expressions, fun_parameter_sampling)?;
1603 self.patch_statements(accept, expressions, fun_parameter_sampling)?;
1604 }
1605 S::Switch {
1606 selector: _,
1607 ref mut cases,
1608 } => {
1609 for case in cases.iter_mut() {
1610 self.patch_statements(&mut case.body, expressions, fun_parameter_sampling)?;
1611 }
1612 }
1613 S::Loop {
1614 ref mut body,
1615 ref mut continuing,
1616 break_if: _,
1617 } => {
1618 self.patch_statements(body, expressions, fun_parameter_sampling)?;
1619 self.patch_statements(continuing, expressions, fun_parameter_sampling)?;
1620 }
1621 S::Break
1622 | S::Continue
1623 | S::Return { .. }
1624 | S::Kill
1625 | S::ControlBarrier(_)
1626 | S::MemoryBarrier(_)
1627 | S::Store { .. }
1628 | S::ImageStore { .. }
1629 | S::Atomic { .. }
1630 | S::ImageAtomic { .. }
1631 | S::RayQuery { .. }
1632 | S::SubgroupBallot { .. }
1633 | S::SubgroupCollectiveOperation { .. }
1634 | S::SubgroupGather { .. }
1635 | S::RayPipelineFunction(..) => {}
1636 S::Call {
1637 function: ref mut callee,
1638 ref arguments,
1639 ..
1640 } => {
1641 let fun_id = self.deferred_function_calls[callee.index()];
1642 let fun_lookup = self.lookup_function.lookup(fun_id)?;
1643 *callee = fun_lookup.handle;
1644
1645 for (arg_index, arg) in arguments.iter().enumerate() {
1647 let flags = match fun_lookup.parameters_sampling.get(arg_index) {
1648 Some(&flags) if !flags.is_empty() => flags,
1649 _ => continue,
1650 };
1651
1652 match expressions[*arg] {
1653 crate::Expression::GlobalVariable(handle) => {
1654 if let Some(sampling) = self.handle_sampling.get_mut(&handle) {
1655 *sampling |= flags
1656 }
1657 }
1658 crate::Expression::FunctionArgument(i) => {
1659 fun_parameter_sampling[i as usize] |= flags;
1660 }
1661 ref other => return Err(Error::InvalidGlobalVar(other.clone())),
1662 }
1663 }
1664 }
1665 S::WorkGroupUniformLoad { .. } => unreachable!(),
1666 S::CooperativeStore { .. } => unreachable!(),
1667 }
1668 i += 1;
1669 }
1670 Ok(())
1671 }
1672
1673 fn patch_function(
1674 &mut self,
1675 handle: Option<Handle<crate::Function>>,
1676 fun: &mut crate::Function,
1677 ) -> Result<(), Error> {
1678 let (fun_id, mut parameters_sampling) = match handle {
1680 Some(h) => {
1681 let (&fun_id, lookup) = self
1682 .lookup_function
1683 .iter_mut()
1684 .find(|&(_, ref lookup)| lookup.handle == h)
1685 .unwrap();
1686 (fun_id, mem::take(&mut lookup.parameters_sampling))
1687 }
1688 None => (0, Vec::new()),
1689 };
1690
1691 for (_, expr) in fun.expressions.iter_mut() {
1692 if let crate::Expression::CallResult(ref mut function) = *expr {
1693 let fun_id = self.deferred_function_calls[function.index()];
1694 *function = self.lookup_function.lookup(fun_id)?.handle;
1695 }
1696 }
1697
1698 self.patch_statements(
1699 &mut fun.body,
1700 &mut fun.expressions,
1701 &mut parameters_sampling,
1702 )?;
1703
1704 if let Some(lookup) = self.lookup_function.get_mut(&fun_id) {
1705 lookup.parameters_sampling = parameters_sampling;
1706 }
1707 Ok(())
1708 }
1709
1710 pub fn parse(mut self) -> Result<crate::Module, Error> {
1711 let mut module = {
1712 if self.next()? != spirv::MAGIC_NUMBER {
1713 return Err(Error::InvalidHeader);
1714 }
1715 let version_raw = self.next()?;
1716 let generator = self.next()?;
1717 let _bound = self.next()?;
1718 let _schema = self.next()?;
1719 log::debug!("Generated by {generator} version {version_raw:x}");
1720 crate::Module::default()
1721 };
1722
1723 self.layouter.clear();
1724 self.dummy_functions = Arena::new();
1725 self.lookup_function.clear();
1726 self.function_call_graph.clear();
1727
1728 loop {
1729 use spirv::Op;
1730
1731 let inst = match self.next_inst() {
1732 Ok(inst) => inst,
1733 Err(Error::IncompleteData) => break,
1734 Err(other) => return Err(other),
1735 };
1736 log::debug!("\t{:?} [{}]", inst.op, inst.wc);
1737
1738 match inst.op {
1739 Op::Capability => self.parse_capability(inst),
1740 Op::Extension => self.parse_extension(inst),
1741 Op::ExtInstImport => self.parse_ext_inst_import(inst),
1742 Op::MemoryModel => self.parse_memory_model(inst),
1743 Op::EntryPoint => self.parse_entry_point(inst),
1744 Op::ExecutionMode => self.parse_execution_mode(inst),
1745 Op::String => self.parse_string(inst),
1746 Op::Source => self.parse_source(inst),
1747 Op::SourceExtension => self.parse_source_extension(inst),
1748 Op::Name => self.parse_name(inst),
1749 Op::MemberName => self.parse_member_name(inst),
1750 Op::ModuleProcessed => self.parse_module_processed(inst),
1751 Op::Decorate => self.parse_decorate(inst),
1752 Op::MemberDecorate => self.parse_member_decorate(inst),
1753 Op::TypeVoid => self.parse_type_void(inst),
1754 Op::TypeBool => self.parse_type_bool(inst, &mut module),
1755 Op::TypeInt => self.parse_type_int(inst, &mut module),
1756 Op::TypeFloat => self.parse_type_float(inst, &mut module),
1757 Op::TypeVector => self.parse_type_vector(inst, &mut module),
1758 Op::TypeMatrix => self.parse_type_matrix(inst, &mut module),
1759 Op::TypeFunction => self.parse_type_function(inst),
1760 Op::TypePointer => self.parse_type_pointer(inst, &mut module),
1761 Op::TypeArray => self.parse_type_array(inst, &mut module),
1762 Op::TypeRuntimeArray => self.parse_type_runtime_array(inst, &mut module),
1763 Op::TypeStruct => self.parse_type_struct(inst, &mut module),
1764 Op::TypeImage => self.parse_type_image(inst, &mut module),
1765 Op::TypeSampledImage => self.parse_type_sampled_image(inst),
1766 Op::TypeSampler => self.parse_type_sampler(inst, &mut module),
1767 Op::Constant | Op::SpecConstant => self.parse_constant(inst, &mut module),
1768 Op::ConstantComposite | Op::SpecConstantComposite => {
1769 self.parse_composite_constant(inst, &mut module)
1770 }
1771 Op::ConstantNull | Op::Undef => self.parse_null_constant(inst, &mut module),
1772 Op::ConstantTrue | Op::SpecConstantTrue => {
1773 self.parse_bool_constant(inst, true, &mut module)
1774 }
1775 Op::ConstantFalse | Op::SpecConstantFalse => {
1776 self.parse_bool_constant(inst, false, &mut module)
1777 }
1778 Op::Variable => self.parse_global_variable(inst, &mut module),
1779 Op::Function => {
1780 self.switch(ModuleState::Function, inst.op)?;
1781 inst.expect(5)?;
1782 self.parse_function(&mut module)
1783 }
1784 Op::ExtInst => {
1785 let _ = self.next()?;
1787 let _ = self.next()?;
1788 let set_id = self.next()?;
1789 if Some(set_id) == self.ext_non_semantic_id {
1790 for _ in 0..inst.wc - 4 {
1792 self.next()?;
1793 }
1794 Ok(())
1795 } else {
1796 return Err(Error::UnsupportedInstruction(self.state, inst.op));
1797 }
1798 }
1799 _ => Err(Error::UnsupportedInstruction(self.state, inst.op)), }?;
1801 }
1802
1803 if !self.upgrade_atomics.is_empty() {
1804 log::debug!("Upgrading atomic pointers...");
1805 module.upgrade_atomics(&self.upgrade_atomics)?;
1806 }
1807
1808 for (ep, fun_id) in mem::take(&mut self.deferred_entry_points) {
1811 self.process_entry_point(&mut module, ep, fun_id)?;
1812 }
1813
1814 log::debug!("Patching...");
1815 {
1816 let mut nodes = petgraph::algo::toposort(&self.function_call_graph, None)
1817 .map_err(|cycle| Error::FunctionCallCycle(cycle.node_id()))?;
1818 nodes.reverse(); let mut functions = mem::take(&mut module.functions);
1820 for fun_id in nodes {
1821 if fun_id > !(functions.len() as u32) {
1822 continue;
1824 }
1825 let lookup = self.lookup_function.get_mut(&fun_id).unwrap();
1826 let fun = mem::take(&mut functions[lookup.handle]);
1828 lookup.handle = module
1830 .functions
1831 .append(fun, functions.get_span(lookup.handle));
1832 }
1833 }
1834 for (handle, fun) in module.functions.iter_mut() {
1836 self.patch_function(Some(handle), fun)?;
1837 }
1838 for ep in module.entry_points.iter_mut() {
1839 self.patch_function(None, &mut ep.function)?;
1840 }
1841
1842 for (handle, flags) in self.handle_sampling.drain() {
1844 if !image::patch_comparison_type(
1845 flags,
1846 module.global_variables.get_mut(handle),
1847 &mut module.types,
1848 ) {
1849 return Err(Error::InconsistentComparisonSampling(handle));
1850 }
1851 }
1852
1853 if !self.future_decor.is_empty() {
1854 log::debug!("Unused item decorations: {:?}", self.future_decor);
1855 self.future_decor.clear();
1856 }
1857 if !self.future_member_decor.is_empty() {
1858 log::debug!("Unused member decorations: {:?}", self.future_member_decor);
1859 self.future_member_decor.clear();
1860 }
1861
1862 Ok(module)
1863 }
1864
1865 fn parse_capability(&mut self, inst: Instruction) -> Result<(), Error> {
1866 self.switch(ModuleState::Capability, inst.op)?;
1867 inst.expect(2)?;
1868 let capability = self.next()?;
1869 let cap =
1870 spirv::Capability::from_u32(capability).ok_or(Error::UnknownCapability(capability))?;
1871 if !SUPPORTED_CAPABILITIES.contains(&cap) {
1872 if self.options.strict_capabilities {
1873 return Err(Error::UnsupportedCapability(cap));
1874 } else {
1875 log::warn!("Unknown capability {cap:?}");
1876 }
1877 }
1878 Ok(())
1879 }
1880
1881 fn parse_extension(&mut self, inst: Instruction) -> Result<(), Error> {
1882 self.switch(ModuleState::Extension, inst.op)?;
1883 inst.expect_at_least(2)?;
1884 let (name, left) = self.next_string(inst.wc - 1)?;
1885 if left != 0 {
1886 return Err(Error::InvalidOperand);
1887 }
1888 if !SUPPORTED_EXTENSIONS.contains(&name.as_str()) {
1889 return Err(Error::UnsupportedExtension(name));
1890 }
1891 Ok(())
1892 }
1893
1894 fn parse_ext_inst_import(&mut self, inst: Instruction) -> Result<(), Error> {
1895 self.switch(ModuleState::Extension, inst.op)?;
1896 inst.expect_at_least(3)?;
1897 let result_id = self.next()?;
1898 let (name, left) = self.next_string(inst.wc - 2)?;
1899 if left != 0 {
1900 return Err(Error::InvalidOperand);
1901 }
1902 if &name == "GLSL.std.450" {
1903 self.ext_glsl_id = Some(result_id);
1904 } else if &name == "NonSemantic.Shader.DebugInfo.100" {
1905 self.ext_non_semantic_id = Some(result_id);
1910 } else {
1911 return Err(Error::UnsupportedExtSet(name));
1912 }
1913 Ok(())
1914 }
1915
1916 fn parse_memory_model(&mut self, inst: Instruction) -> Result<(), Error> {
1917 self.switch(ModuleState::MemoryModel, inst.op)?;
1918 inst.expect(3)?;
1919 let _addressing_model = self.next()?;
1920 let _memory_model = self.next()?;
1921 Ok(())
1922 }
1923
1924 fn parse_entry_point(&mut self, inst: Instruction) -> Result<(), Error> {
1925 self.switch(ModuleState::EntryPoint, inst.op)?;
1926 inst.expect_at_least(4)?;
1927 let exec_model = self.next()?;
1928 let exec_model = spirv::ExecutionModel::from_u32(exec_model)
1929 .ok_or(Error::UnsupportedExecutionModel(exec_model))?;
1930 let function_id = self.next()?;
1931 let (name, left) = self.next_string(inst.wc - 3)?;
1932 let ep = EntryPoint {
1933 stage: match exec_model {
1934 spirv::ExecutionModel::Vertex => crate::ShaderStage::Vertex,
1935 spirv::ExecutionModel::Fragment => crate::ShaderStage::Fragment,
1936 spirv::ExecutionModel::GLCompute => crate::ShaderStage::Compute,
1937 spirv::ExecutionModel::TaskEXT => crate::ShaderStage::Task,
1938 spirv::ExecutionModel::MeshEXT => crate::ShaderStage::Mesh,
1939 _ => return Err(Error::UnsupportedExecutionModel(exec_model as u32)),
1940 },
1941 name,
1942 early_depth_test: None,
1943 workgroup_size: [0; 3],
1944 variable_ids: self.data.by_ref().take(left as usize).collect(),
1945 };
1946 self.lookup_entry_point.insert(function_id, ep);
1947 Ok(())
1948 }
1949
1950 fn parse_execution_mode(&mut self, inst: Instruction) -> Result<(), Error> {
1951 use spirv::ExecutionMode;
1952
1953 self.switch(ModuleState::ExecutionMode, inst.op)?;
1954 inst.expect_at_least(3)?;
1955
1956 let ep_id = self.next()?;
1957 let mode_id = self.next()?;
1958 let args: Vec<spirv::Word> = self.data.by_ref().take(inst.wc as usize - 3).collect();
1959
1960 let ep = self
1961 .lookup_entry_point
1962 .get_mut(&ep_id)
1963 .ok_or(Error::InvalidId(ep_id))?;
1964 let mode =
1965 ExecutionMode::from_u32(mode_id).ok_or(Error::UnsupportedExecutionMode(mode_id))?;
1966
1967 match mode {
1968 ExecutionMode::EarlyFragmentTests => {
1969 ep.early_depth_test = Some(crate::EarlyDepthTest::Force);
1970 }
1971 ExecutionMode::DepthUnchanged => {
1972 if let &mut Some(ref mut early_depth_test) = &mut ep.early_depth_test {
1973 if let &mut crate::EarlyDepthTest::Allow {
1974 ref mut conservative,
1975 } = early_depth_test
1976 {
1977 *conservative = crate::ConservativeDepth::Unchanged;
1978 }
1979 } else {
1980 ep.early_depth_test = Some(crate::EarlyDepthTest::Allow {
1981 conservative: crate::ConservativeDepth::Unchanged,
1982 });
1983 }
1984 }
1985 ExecutionMode::DepthGreater => {
1986 if let &mut Some(ref mut early_depth_test) = &mut ep.early_depth_test {
1987 if let &mut crate::EarlyDepthTest::Allow {
1988 ref mut conservative,
1989 } = early_depth_test
1990 {
1991 *conservative = crate::ConservativeDepth::GreaterEqual;
1992 }
1993 } else {
1994 ep.early_depth_test = Some(crate::EarlyDepthTest::Allow {
1995 conservative: crate::ConservativeDepth::GreaterEqual,
1996 });
1997 }
1998 }
1999 ExecutionMode::DepthLess => {
2000 if let &mut Some(ref mut early_depth_test) = &mut ep.early_depth_test {
2001 if let &mut crate::EarlyDepthTest::Allow {
2002 ref mut conservative,
2003 } = early_depth_test
2004 {
2005 *conservative = crate::ConservativeDepth::LessEqual;
2006 }
2007 } else {
2008 ep.early_depth_test = Some(crate::EarlyDepthTest::Allow {
2009 conservative: crate::ConservativeDepth::LessEqual,
2010 });
2011 }
2012 }
2013 ExecutionMode::DepthReplacing => {
2014 }
2016 ExecutionMode::OriginUpperLeft => {
2017 }
2019 ExecutionMode::LocalSize => {
2020 ep.workgroup_size = [args[0], args[1], args[2]];
2021 }
2022 _ => {
2023 return Err(Error::UnsupportedExecutionMode(mode_id));
2024 }
2025 }
2026
2027 Ok(())
2028 }
2029
2030 fn parse_string(&mut self, inst: Instruction) -> Result<(), Error> {
2031 self.switch(ModuleState::Source, inst.op)?;
2032 inst.expect_at_least(3)?;
2033 let _id = self.next()?;
2034 let (_name, _) = self.next_string(inst.wc - 2)?;
2035 Ok(())
2036 }
2037
2038 fn parse_source(&mut self, inst: Instruction) -> Result<(), Error> {
2039 self.switch(ModuleState::Source, inst.op)?;
2040 for _ in 1..inst.wc {
2041 let _ = self.next()?;
2042 }
2043 Ok(())
2044 }
2045
2046 fn parse_source_extension(&mut self, inst: Instruction) -> Result<(), Error> {
2047 self.switch(ModuleState::Source, inst.op)?;
2048 inst.expect_at_least(2)?;
2049 let (_name, _) = self.next_string(inst.wc - 1)?;
2050 Ok(())
2051 }
2052
2053 fn parse_name(&mut self, inst: Instruction) -> Result<(), Error> {
2054 self.switch(ModuleState::Name, inst.op)?;
2055 inst.expect_at_least(3)?;
2056 let id = self.next()?;
2057 let (name, left) = self.next_string(inst.wc - 2)?;
2058 if left != 0 {
2059 return Err(Error::InvalidOperand);
2060 }
2061 self.future_decor.entry(id).or_default().name = Some(name);
2062 Ok(())
2063 }
2064
2065 fn parse_member_name(&mut self, inst: Instruction) -> Result<(), Error> {
2066 self.switch(ModuleState::Name, inst.op)?;
2067 inst.expect_at_least(4)?;
2068 let id = self.next()?;
2069 let member = self.next()?;
2070 let (name, left) = self.next_string(inst.wc - 3)?;
2071 if left != 0 {
2072 return Err(Error::InvalidOperand);
2073 }
2074
2075 self.future_member_decor
2076 .entry((id, member))
2077 .or_default()
2078 .name = Some(name);
2079 Ok(())
2080 }
2081
2082 fn parse_module_processed(&mut self, inst: Instruction) -> Result<(), Error> {
2083 self.switch(ModuleState::Name, inst.op)?;
2084 inst.expect_at_least(2)?;
2085 let (_info, left) = self.next_string(inst.wc - 1)?;
2086 if left != 0 {
2088 return Err(Error::InvalidOperand);
2089 }
2090 Ok(())
2091 }
2092
2093 fn parse_decorate(&mut self, inst: Instruction) -> Result<(), Error> {
2094 self.switch(ModuleState::Annotation, inst.op)?;
2095 inst.expect_at_least(3)?;
2096 let id = self.next()?;
2097 let mut dec = self.future_decor.remove(&id).unwrap_or_default();
2098 self.next_decoration(inst, 2, &mut dec)?;
2099 self.future_decor.insert(id, dec);
2100 Ok(())
2101 }
2102
2103 fn parse_member_decorate(&mut self, inst: Instruction) -> Result<(), Error> {
2104 self.switch(ModuleState::Annotation, inst.op)?;
2105 inst.expect_at_least(4)?;
2106 let id = self.next()?;
2107 let member = self.next()?;
2108
2109 let mut dec = self
2110 .future_member_decor
2111 .remove(&(id, member))
2112 .unwrap_or_default();
2113 self.next_decoration(inst, 3, &mut dec)?;
2114 self.future_member_decor.insert((id, member), dec);
2115 Ok(())
2116 }
2117
2118 fn parse_type_void(&mut self, inst: Instruction) -> Result<(), Error> {
2119 self.switch(ModuleState::Type, inst.op)?;
2120 inst.expect(2)?;
2121 let id = self.next()?;
2122 self.lookup_void_type = Some(id);
2123 Ok(())
2124 }
2125
2126 fn parse_type_bool(
2127 &mut self,
2128 inst: Instruction,
2129 module: &mut crate::Module,
2130 ) -> Result<(), Error> {
2131 let start = self.data_offset;
2132 self.switch(ModuleState::Type, inst.op)?;
2133 inst.expect(2)?;
2134 let id = self.next()?;
2135 let inner = crate::TypeInner::Scalar(crate::Scalar::BOOL);
2136 self.lookup_type.insert(
2137 id,
2138 LookupType {
2139 handle: module.types.insert(
2140 crate::Type {
2141 name: self.future_decor.remove(&id).and_then(|dec| dec.name),
2142 inner,
2143 },
2144 self.span_from_with_op(start),
2145 ),
2146 base_id: None,
2147 },
2148 );
2149 Ok(())
2150 }
2151
2152 fn parse_type_int(
2153 &mut self,
2154 inst: Instruction,
2155 module: &mut crate::Module,
2156 ) -> Result<(), Error> {
2157 let start = self.data_offset;
2158 self.switch(ModuleState::Type, inst.op)?;
2159 inst.expect(4)?;
2160 let id = self.next()?;
2161 let width = self.next()?;
2162 let sign = self.next()?;
2163 let inner = crate::TypeInner::Scalar(crate::Scalar {
2164 kind: match sign {
2165 0 => crate::ScalarKind::Uint,
2166 1 => crate::ScalarKind::Sint,
2167 _ => return Err(Error::InvalidSign(sign)),
2168 },
2169 width: map_width(width)?,
2170 });
2171 self.lookup_type.insert(
2172 id,
2173 LookupType {
2174 handle: module.types.insert(
2175 crate::Type {
2176 name: self.future_decor.remove(&id).and_then(|dec| dec.name),
2177 inner,
2178 },
2179 self.span_from_with_op(start),
2180 ),
2181 base_id: None,
2182 },
2183 );
2184 Ok(())
2185 }
2186
2187 fn parse_type_float(
2188 &mut self,
2189 inst: Instruction,
2190 module: &mut crate::Module,
2191 ) -> Result<(), Error> {
2192 let start = self.data_offset;
2193 self.switch(ModuleState::Type, inst.op)?;
2194 inst.expect(3)?;
2195 let id = self.next()?;
2196 let width = self.next()?;
2197 let inner = crate::TypeInner::Scalar(crate::Scalar::float(map_width(width)?));
2198 self.lookup_type.insert(
2199 id,
2200 LookupType {
2201 handle: module.types.insert(
2202 crate::Type {
2203 name: self.future_decor.remove(&id).and_then(|dec| dec.name),
2204 inner,
2205 },
2206 self.span_from_with_op(start),
2207 ),
2208 base_id: None,
2209 },
2210 );
2211 Ok(())
2212 }
2213
2214 fn parse_type_vector(
2215 &mut self,
2216 inst: Instruction,
2217 module: &mut crate::Module,
2218 ) -> Result<(), Error> {
2219 let start = self.data_offset;
2220 self.switch(ModuleState::Type, inst.op)?;
2221 inst.expect(4)?;
2222 let id = self.next()?;
2223 let type_id = self.next()?;
2224 let type_lookup = self.lookup_type.lookup(type_id)?;
2225 let scalar = match module.types[type_lookup.handle].inner {
2226 crate::TypeInner::Scalar(scalar) => scalar,
2227 _ => return Err(Error::InvalidInnerType(type_id)),
2228 };
2229 let component_count = self.next()?;
2230 let inner = crate::TypeInner::Vector {
2231 size: map_vector_size(component_count)?,
2232 scalar,
2233 };
2234 self.lookup_type.insert(
2235 id,
2236 LookupType {
2237 handle: module.types.insert(
2238 crate::Type {
2239 name: self.future_decor.remove(&id).and_then(|dec| dec.name),
2240 inner,
2241 },
2242 self.span_from_with_op(start),
2243 ),
2244 base_id: Some(type_id),
2245 },
2246 );
2247 Ok(())
2248 }
2249
2250 fn parse_type_matrix(
2251 &mut self,
2252 inst: Instruction,
2253 module: &mut crate::Module,
2254 ) -> Result<(), Error> {
2255 let start = self.data_offset;
2256 self.switch(ModuleState::Type, inst.op)?;
2257 inst.expect(4)?;
2258 let id = self.next()?;
2259 let vector_type_id = self.next()?;
2260 let num_columns = self.next()?;
2261 let decor = self.future_decor.remove(&id);
2262
2263 let vector_type_lookup = self.lookup_type.lookup(vector_type_id)?;
2264 let inner = match module.types[vector_type_lookup.handle].inner {
2265 crate::TypeInner::Vector { size, scalar } => crate::TypeInner::Matrix {
2266 columns: map_vector_size(num_columns)?,
2267 rows: size,
2268 scalar,
2269 },
2270 _ => return Err(Error::InvalidInnerType(vector_type_id)),
2271 };
2272
2273 self.lookup_type.insert(
2274 id,
2275 LookupType {
2276 handle: module.types.insert(
2277 crate::Type {
2278 name: decor.and_then(|dec| dec.name),
2279 inner,
2280 },
2281 self.span_from_with_op(start),
2282 ),
2283 base_id: Some(vector_type_id),
2284 },
2285 );
2286 Ok(())
2287 }
2288
2289 fn parse_type_function(&mut self, inst: Instruction) -> Result<(), Error> {
2290 self.switch(ModuleState::Type, inst.op)?;
2291 inst.expect_at_least(3)?;
2292 let id = self.next()?;
2293 let return_type_id = self.next()?;
2294 let parameter_type_ids = self.data.by_ref().take(inst.wc as usize - 3).collect();
2295 self.lookup_function_type.insert(
2296 id,
2297 LookupFunctionType {
2298 parameter_type_ids,
2299 return_type_id,
2300 },
2301 );
2302 Ok(())
2303 }
2304
2305 fn parse_type_pointer(
2306 &mut self,
2307 inst: Instruction,
2308 module: &mut crate::Module,
2309 ) -> Result<(), Error> {
2310 let start = self.data_offset;
2311 self.switch(ModuleState::Type, inst.op)?;
2312 inst.expect(4)?;
2313 let id = self.next()?;
2314 let storage_class = self.next()?;
2315 let type_id = self.next()?;
2316
2317 let decor = self.future_decor.remove(&id);
2318 let base_lookup_ty = self.lookup_type.lookup(type_id)?;
2319 let base_inner = &module.types[base_lookup_ty.handle].inner;
2320
2321 let space = if let Some(space) = base_inner.pointer_space() {
2322 space
2323 } else if self
2324 .lookup_storage_buffer_types
2325 .contains_key(&base_lookup_ty.handle)
2326 {
2327 crate::AddressSpace::Storage {
2328 access: crate::StorageAccess::default(),
2329 }
2330 } else {
2331 match map_storage_class(storage_class)? {
2332 ExtendedClass::Global(space) => space,
2333 ExtendedClass::Input | ExtendedClass::Output => crate::AddressSpace::Private,
2334 }
2335 };
2336
2337 if let crate::TypeInner::Array {
2341 size: crate::ArraySize::Dynamic,
2342 ..
2343 } = *base_inner
2344 {
2345 match space {
2346 crate::AddressSpace::Storage { .. } => {}
2347 _ => {
2348 return Err(Error::UnsupportedRuntimeArrayStorageClass);
2349 }
2350 }
2351 }
2352
2353 let lookup_ty = if space == crate::AddressSpace::Handle {
2355 base_lookup_ty.clone()
2356 } else {
2357 LookupType {
2358 handle: module.types.insert(
2359 crate::Type {
2360 name: decor.and_then(|dec| dec.name),
2361 inner: crate::TypeInner::Pointer {
2362 base: base_lookup_ty.handle,
2363 space,
2364 },
2365 },
2366 self.span_from_with_op(start),
2367 ),
2368 base_id: Some(type_id),
2369 }
2370 };
2371 self.lookup_type.insert(id, lookup_ty);
2372 Ok(())
2373 }
2374
2375 fn parse_type_array(
2376 &mut self,
2377 inst: Instruction,
2378 module: &mut crate::Module,
2379 ) -> Result<(), Error> {
2380 let start = self.data_offset;
2381 self.switch(ModuleState::Type, inst.op)?;
2382 inst.expect(4)?;
2383 let id = self.next()?;
2384 let type_id = self.next()?;
2385 let length_id = self.next()?;
2386 let length_const = self.lookup_constant.lookup(length_id)?;
2387
2388 let size = resolve_constant(module.to_ctx(), &length_const.inner)
2389 .and_then(NonZeroU32::new)
2390 .ok_or(Error::InvalidArraySize(length_id))?;
2391
2392 let decor = self.future_decor.remove(&id).unwrap_or_default();
2393 let base = self.lookup_type.lookup(type_id)?.handle;
2394
2395 self.layouter.update(module.to_ctx()).unwrap();
2396
2397 let inner = if let crate::TypeInner::Image { .. } | crate::TypeInner::Sampler { .. } =
2429 module.types[base].inner
2430 {
2431 crate::TypeInner::BindingArray {
2432 base,
2433 size: crate::ArraySize::Constant(size),
2434 }
2435 } else {
2436 crate::TypeInner::Array {
2437 base,
2438 size: crate::ArraySize::Constant(size),
2439 stride: match decor.array_stride {
2440 Some(stride) => stride.get(),
2441 None => self.layouter[base].to_stride(),
2442 },
2443 }
2444 };
2445
2446 self.lookup_type.insert(
2447 id,
2448 LookupType {
2449 handle: module.types.insert(
2450 crate::Type {
2451 name: decor.name,
2452 inner,
2453 },
2454 self.span_from_with_op(start),
2455 ),
2456 base_id: Some(type_id),
2457 },
2458 );
2459 Ok(())
2460 }
2461
2462 fn parse_type_runtime_array(
2463 &mut self,
2464 inst: Instruction,
2465 module: &mut crate::Module,
2466 ) -> Result<(), Error> {
2467 let start = self.data_offset;
2468 self.switch(ModuleState::Type, inst.op)?;
2469 inst.expect(3)?;
2470 let id = self.next()?;
2471 let type_id = self.next()?;
2472
2473 let decor = self.future_decor.remove(&id).unwrap_or_default();
2474 let base = self.lookup_type.lookup(type_id)?.handle;
2475
2476 self.layouter.update(module.to_ctx()).unwrap();
2477
2478 let inner = if let crate::TypeInner::Image { .. } | crate::TypeInner::Sampler { .. } =
2480 module.types[base].inner
2481 {
2482 crate::TypeInner::BindingArray {
2483 base: self.lookup_type.lookup(type_id)?.handle,
2484 size: crate::ArraySize::Dynamic,
2485 }
2486 } else {
2487 crate::TypeInner::Array {
2488 base: self.lookup_type.lookup(type_id)?.handle,
2489 size: crate::ArraySize::Dynamic,
2490 stride: match decor.array_stride {
2491 Some(stride) => stride.get(),
2492 None => self.layouter[base].to_stride(),
2493 },
2494 }
2495 };
2496
2497 self.lookup_type.insert(
2498 id,
2499 LookupType {
2500 handle: module.types.insert(
2501 crate::Type {
2502 name: decor.name,
2503 inner,
2504 },
2505 self.span_from_with_op(start),
2506 ),
2507 base_id: Some(type_id),
2508 },
2509 );
2510 Ok(())
2511 }
2512
2513 fn parse_type_struct(
2514 &mut self,
2515 inst: Instruction,
2516 module: &mut crate::Module,
2517 ) -> Result<(), Error> {
2518 let start = self.data_offset;
2519 self.switch(ModuleState::Type, inst.op)?;
2520 inst.expect_at_least(2)?;
2521 let id = self.next()?;
2522 let parent_decor = self.future_decor.remove(&id);
2523 let is_storage_buffer = parent_decor
2524 .as_ref()
2525 .is_some_and(|decor| decor.storage_buffer);
2526
2527 self.layouter.update(module.to_ctx()).unwrap();
2528
2529 let mut members = Vec::<crate::StructMember>::with_capacity(inst.wc as usize - 2);
2530 let mut member_lookups = Vec::with_capacity(members.capacity());
2531 let mut storage_access = crate::StorageAccess::empty();
2532 let mut span = 0;
2533 let mut alignment = Alignment::ONE;
2534 for i in 0..u32::from(inst.wc) - 2 {
2535 let type_id = self.next()?;
2536 let ty = self.lookup_type.lookup(type_id)?.handle;
2537 let decor = self
2538 .future_member_decor
2539 .remove(&(id, i))
2540 .unwrap_or_default();
2541
2542 storage_access |= decor.flags.to_storage_access();
2543
2544 member_lookups.push(LookupMember {
2545 type_id,
2546 row_major: decor.matrix_major == Some(Majority::Row),
2547 });
2548
2549 let member_alignment = self.layouter[ty].alignment;
2550 span = member_alignment.round_up(span);
2551 alignment = member_alignment.max(alignment);
2552
2553 let binding = decor.io_binding().ok();
2554 if let Some(offset) = decor.offset {
2555 span = offset;
2556 }
2557 let offset = span;
2558
2559 span += self.layouter[ty].size;
2560
2561 let inner = &module.types[ty].inner;
2562 if let crate::TypeInner::Matrix {
2563 columns,
2564 rows,
2565 scalar,
2566 } = *inner
2567 {
2568 if let Some(stride) = decor.matrix_stride {
2569 let expected_stride = Alignment::from(rows) * scalar.width as u32;
2570 if stride.get() != expected_stride {
2571 return Err(Error::UnsupportedMatrixStride {
2572 stride: stride.get(),
2573 columns: columns as u8,
2574 rows: rows as u8,
2575 width: scalar.width,
2576 });
2577 }
2578 }
2579 }
2580
2581 members.push(crate::StructMember {
2582 name: decor.name,
2583 ty,
2584 binding,
2585 offset,
2586 });
2587 }
2588
2589 span = alignment.round_up(span);
2590
2591 let inner = crate::TypeInner::Struct { span, members };
2592
2593 let ty_handle = module.types.insert(
2594 crate::Type {
2595 name: parent_decor.and_then(|dec| dec.name),
2596 inner,
2597 },
2598 self.span_from_with_op(start),
2599 );
2600
2601 if is_storage_buffer {
2602 self.lookup_storage_buffer_types
2603 .insert(ty_handle, storage_access);
2604 }
2605 for (i, member_lookup) in member_lookups.into_iter().enumerate() {
2606 self.lookup_member
2607 .insert((ty_handle, i as u32), member_lookup);
2608 }
2609 self.lookup_type.insert(
2610 id,
2611 LookupType {
2612 handle: ty_handle,
2613 base_id: None,
2614 },
2615 );
2616 Ok(())
2617 }
2618
2619 fn parse_type_image(
2620 &mut self,
2621 inst: Instruction,
2622 module: &mut crate::Module,
2623 ) -> Result<(), Error> {
2624 let start = self.data_offset;
2625 self.switch(ModuleState::Type, inst.op)?;
2626 inst.expect(9)?;
2627
2628 let id = self.next()?;
2629 let sample_type_id = self.next()?;
2630 let dim = self.next()?;
2631 let is_depth = self.next()?;
2632 let is_array = self.next()? != 0;
2633 let is_msaa = self.next()? != 0;
2634 let is_sampled = self.next()?;
2635 let format = self.next()?;
2636
2637 let dim = map_image_dim(dim)?;
2638 let decor = self.future_decor.remove(&id).unwrap_or_default();
2639
2640 module.types.insert(
2642 crate::Type {
2643 name: None,
2644 inner: {
2645 let scalar = crate::Scalar::F32;
2646 match dim.required_coordinate_size() {
2647 None => crate::TypeInner::Scalar(scalar),
2648 Some(size) => crate::TypeInner::Vector { size, scalar },
2649 }
2650 },
2651 },
2652 Default::default(),
2653 );
2654
2655 let base_handle = self.lookup_type.lookup(sample_type_id)?.handle;
2656 let kind = module.types[base_handle]
2657 .inner
2658 .scalar_kind()
2659 .ok_or(Error::InvalidImageBaseType(base_handle))?;
2660
2661 let inner = crate::TypeInner::Image {
2662 class: if is_depth == 1 {
2663 if is_sampled == 2 {
2664 return Err(Error::InvalidImageDepthStorage);
2665 }
2666
2667 crate::ImageClass::Depth { multi: is_msaa }
2668 }
2669 else if is_sampled == 2 && format == 0 {
2673 return Err(Error::InvalidStorageImageWithoutFormat);
2674 }
2675 else if format != 0 && (is_sampled == 0 || is_sampled == 2) {
2680 crate::ImageClass::Storage {
2681 format: map_image_format(format)?,
2682 access: crate::StorageAccess::default(),
2683 }
2684 }
2685 else {
2688 crate::ImageClass::Sampled {
2689 kind,
2690 multi: is_msaa,
2691 }
2692 },
2693 dim,
2694 arrayed: is_array,
2695 };
2696
2697 let handle = module.types.insert(
2698 crate::Type {
2699 name: decor.name,
2700 inner,
2701 },
2702 self.span_from_with_op(start),
2703 );
2704
2705 self.lookup_type.insert(
2706 id,
2707 LookupType {
2708 handle,
2709 base_id: Some(sample_type_id),
2710 },
2711 );
2712 Ok(())
2713 }
2714
2715 fn parse_type_sampled_image(&mut self, inst: Instruction) -> Result<(), Error> {
2716 self.switch(ModuleState::Type, inst.op)?;
2717 inst.expect(3)?;
2718 let id = self.next()?;
2719 let image_id = self.next()?;
2720 self.lookup_type.insert(
2721 id,
2722 LookupType {
2723 handle: self.lookup_type.lookup(image_id)?.handle,
2724 base_id: Some(image_id),
2725 },
2726 );
2727 Ok(())
2728 }
2729
2730 fn parse_type_sampler(
2731 &mut self,
2732 inst: Instruction,
2733 module: &mut crate::Module,
2734 ) -> Result<(), Error> {
2735 let start = self.data_offset;
2736 self.switch(ModuleState::Type, inst.op)?;
2737 inst.expect(2)?;
2738 let id = self.next()?;
2739 let decor = self.future_decor.remove(&id).unwrap_or_default();
2740 let handle = module.types.insert(
2741 crate::Type {
2742 name: decor.name,
2743 inner: crate::TypeInner::Sampler { comparison: false },
2744 },
2745 self.span_from_with_op(start),
2746 );
2747 self.lookup_type.insert(
2748 id,
2749 LookupType {
2750 handle,
2751 base_id: None,
2752 },
2753 );
2754 Ok(())
2755 }
2756
2757 fn parse_constant(
2758 &mut self,
2759 inst: Instruction,
2760 module: &mut crate::Module,
2761 ) -> Result<(), Error> {
2762 let start = self.data_offset;
2763 self.switch(ModuleState::Type, inst.op)?;
2764 inst.expect_at_least(4)?;
2765 let type_id = self.next()?;
2766 let id = self.next()?;
2767 let type_lookup = self.lookup_type.lookup(type_id)?;
2768 let ty = type_lookup.handle;
2769
2770 let literal = match module.types[ty].inner {
2771 crate::TypeInner::Scalar(crate::Scalar {
2772 kind: crate::ScalarKind::Uint,
2773 width,
2774 }) => {
2775 let low = self.next()?;
2776 match width {
2777 4 => crate::Literal::U32(low),
2778 8 => {
2779 inst.expect(5)?;
2780 let high = self.next()?;
2781 crate::Literal::U64((u64::from(high) << 32) | u64::from(low))
2782 }
2783 _ => return Err(Error::InvalidTypeWidth(width as u32)),
2784 }
2785 }
2786 crate::TypeInner::Scalar(crate::Scalar {
2787 kind: crate::ScalarKind::Sint,
2788 width,
2789 }) => {
2790 let low = self.next()?;
2791 match width {
2792 4 => crate::Literal::I32(low as i32),
2793 8 => {
2794 inst.expect(5)?;
2795 let high = self.next()?;
2796 crate::Literal::I64(((u64::from(high) << 32) | u64::from(low)) as i64)
2797 }
2798 _ => return Err(Error::InvalidTypeWidth(width as u32)),
2799 }
2800 }
2801 crate::TypeInner::Scalar(crate::Scalar {
2802 kind: crate::ScalarKind::Float,
2803 width,
2804 }) => {
2805 let low = self.next()?;
2806 match width {
2807 2 => crate::Literal::F16(f16::from_bits(low as u16)),
2810 4 => crate::Literal::F32(f32::from_bits(low)),
2811 8 => {
2812 inst.expect(5)?;
2813 let high = self.next()?;
2814 crate::Literal::F64(f64::from_bits(
2815 (u64::from(high) << 32) | u64::from(low),
2816 ))
2817 }
2818 _ => return Err(Error::InvalidTypeWidth(width as u32)),
2819 }
2820 }
2821 _ => return Err(Error::UnsupportedType(type_lookup.handle)),
2822 };
2823
2824 let span = self.span_from_with_op(start);
2825
2826 let init = module
2827 .global_expressions
2828 .append(crate::Expression::Literal(literal), span);
2829
2830 self.insert_parsed_constant(module, id, type_id, ty, init, span)
2831 }
2832
2833 fn parse_composite_constant(
2834 &mut self,
2835 inst: Instruction,
2836 module: &mut crate::Module,
2837 ) -> Result<(), Error> {
2838 let start = self.data_offset;
2839 self.switch(ModuleState::Type, inst.op)?;
2840 inst.expect_at_least(3)?;
2841 let type_id = self.next()?;
2842 let id = self.next()?;
2843
2844 let type_lookup = self.lookup_type.lookup(type_id)?;
2845 let ty = type_lookup.handle;
2846
2847 let mut components = Vec::with_capacity(inst.wc as usize - 3);
2848 for _ in 0..components.capacity() {
2849 let start = self.data_offset;
2850 let component_id = self.next()?;
2851 let span = self.span_from_with_op(start);
2852 let constant = self.lookup_constant.lookup(component_id)?;
2853 let expr = module
2854 .global_expressions
2855 .append(constant.inner.to_expr(), span);
2856 components.push(expr);
2857 }
2858
2859 let span = self.span_from_with_op(start);
2860
2861 let init = module
2862 .global_expressions
2863 .append(crate::Expression::Compose { ty, components }, span);
2864
2865 self.insert_parsed_constant(module, id, type_id, ty, init, span)
2866 }
2867
2868 fn parse_null_constant(
2869 &mut self,
2870 inst: Instruction,
2871 module: &mut crate::Module,
2872 ) -> Result<(), Error> {
2873 let start = self.data_offset;
2874 self.switch(ModuleState::Type, inst.op)?;
2875 inst.expect(3)?;
2876 let type_id = self.next()?;
2877 let id = self.next()?;
2878 let span = self.span_from_with_op(start);
2879
2880 let type_lookup = self.lookup_type.lookup(type_id)?;
2881 let ty = type_lookup.handle;
2882
2883 let init = module
2884 .global_expressions
2885 .append(crate::Expression::ZeroValue(ty), span);
2886
2887 self.insert_parsed_constant(module, id, type_id, ty, init, span)
2888 }
2889
2890 fn parse_bool_constant(
2891 &mut self,
2892 inst: Instruction,
2893 value: bool,
2894 module: &mut crate::Module,
2895 ) -> Result<(), Error> {
2896 let start = self.data_offset;
2897 self.switch(ModuleState::Type, inst.op)?;
2898 inst.expect(3)?;
2899 let type_id = self.next()?;
2900 let id = self.next()?;
2901 let span = self.span_from_with_op(start);
2902
2903 let type_lookup = self.lookup_type.lookup(type_id)?;
2904 let ty = type_lookup.handle;
2905
2906 let init = module.global_expressions.append(
2907 crate::Expression::Literal(crate::Literal::Bool(value)),
2908 span,
2909 );
2910
2911 self.insert_parsed_constant(module, id, type_id, ty, init, span)
2912 }
2913
2914 fn insert_parsed_constant(
2915 &mut self,
2916 module: &mut crate::Module,
2917 id: u32,
2918 type_id: u32,
2919 ty: Handle<crate::Type>,
2920 init: Handle<crate::Expression>,
2921 span: crate::Span,
2922 ) -> Result<(), Error> {
2923 let decor = self.future_decor.remove(&id).unwrap_or_default();
2924
2925 let inner = if let Some(id) = decor.specialization_constant_id {
2926 let o = crate::Override {
2927 name: decor.name,
2928 id: Some(id.try_into().map_err(|_| Error::SpecIdTooHigh(id))?),
2929 ty,
2930 init: Some(init),
2931 };
2932 Constant::Override(module.overrides.append(o, span))
2933 } else {
2934 let c = crate::Constant {
2935 name: decor.name,
2936 ty,
2937 init,
2938 };
2939 Constant::Constant(module.constants.append(c, span))
2940 };
2941
2942 self.lookup_constant
2943 .insert(id, LookupConstant { inner, type_id });
2944 Ok(())
2945 }
2946
2947 fn parse_global_variable(
2948 &mut self,
2949 inst: Instruction,
2950 module: &mut crate::Module,
2951 ) -> Result<(), Error> {
2952 let start = self.data_offset;
2953 self.switch(ModuleState::Type, inst.op)?;
2954 inst.expect_at_least(4)?;
2955 let type_id = self.next()?;
2956 let id = self.next()?;
2957 let storage_class = self.next()?;
2958 let init = if inst.wc > 4 {
2959 inst.expect(5)?;
2960 let start = self.data_offset;
2961 let init_id = self.next()?;
2962 let span = self.span_from_with_op(start);
2963 let lconst = self.lookup_constant.lookup(init_id)?;
2964 let expr = module
2965 .global_expressions
2966 .append(lconst.inner.to_expr(), span);
2967 Some(expr)
2968 } else {
2969 None
2970 };
2971 let span = self.span_from_with_op(start);
2972 let dec = self.future_decor.remove(&id).unwrap_or_default();
2973
2974 let original_ty = self.lookup_type.lookup(type_id)?.handle;
2975 let mut ty = original_ty;
2976
2977 if let crate::TypeInner::Pointer { base, space: _ } = module.types[original_ty].inner {
2978 ty = base;
2979 }
2980
2981 if let crate::TypeInner::BindingArray { .. } = module.types[original_ty].inner {
2982 if dec.desc_set.is_none() || dec.desc_index.is_none() {
2985 return Err(Error::NonBindingArrayOfImageOrSamplers);
2986 }
2987 }
2988
2989 if let crate::TypeInner::Image {
2990 dim,
2991 arrayed,
2992 class: crate::ImageClass::Storage { format, access: _ },
2993 } = module.types[ty].inner
2994 {
2995 let access = dec.flags.to_storage_access();
2999
3000 ty = module.types.insert(
3001 crate::Type {
3002 name: None,
3003 inner: crate::TypeInner::Image {
3004 dim,
3005 arrayed,
3006 class: crate::ImageClass::Storage { format, access },
3007 },
3008 },
3009 Default::default(),
3010 );
3011 }
3012
3013 let ext_class = match self.lookup_storage_buffer_types.get(&ty) {
3014 Some(&access) => ExtendedClass::Global(crate::AddressSpace::Storage { access }),
3015 None => map_storage_class(storage_class)?,
3016 };
3017
3018 let (inner, var) = match ext_class {
3019 ExtendedClass::Global(mut space) => {
3020 if let crate::AddressSpace::Storage { ref mut access } = space {
3021 *access &= dec.flags.to_storage_access();
3022 }
3023 let var = crate::GlobalVariable {
3024 binding: dec.resource_binding(),
3025 name: dec.name,
3026 space,
3027 ty,
3028 init,
3029 memory_decorations: dec.flags.to_memory_decorations(),
3030 };
3031 (Variable::Global, var)
3032 }
3033 ExtendedClass::Input => {
3034 let binding = dec.io_binding()?;
3035 let mut unsigned_ty = ty;
3036 if let crate::Binding::BuiltIn(built_in) = binding {
3037 let needs_inner_uint = match built_in {
3038 crate::BuiltIn::BaseInstance
3039 | crate::BuiltIn::BaseVertex
3040 | crate::BuiltIn::InstanceIndex
3041 | crate::BuiltIn::SampleIndex
3042 | crate::BuiltIn::VertexIndex
3043 | crate::BuiltIn::PrimitiveIndex
3044 | crate::BuiltIn::LocalInvocationIndex => {
3045 Some(crate::TypeInner::Scalar(crate::Scalar::U32))
3046 }
3047 crate::BuiltIn::GlobalInvocationId
3048 | crate::BuiltIn::LocalInvocationId
3049 | crate::BuiltIn::WorkGroupId
3050 | crate::BuiltIn::WorkGroupSize => Some(crate::TypeInner::Vector {
3051 size: crate::VectorSize::Tri,
3052 scalar: crate::Scalar::U32,
3053 }),
3054 crate::BuiltIn::Barycentric { perspective: false } => {
3055 Some(crate::TypeInner::Vector {
3056 size: crate::VectorSize::Tri,
3057 scalar: crate::Scalar::F32,
3058 })
3059 }
3060 _ => None,
3061 };
3062 if let (Some(inner), Some(crate::ScalarKind::Sint)) =
3063 (needs_inner_uint, module.types[ty].inner.scalar_kind())
3064 {
3065 unsigned_ty = module
3066 .types
3067 .insert(crate::Type { name: None, inner }, Default::default());
3068 }
3069 }
3070
3071 let var = crate::GlobalVariable {
3072 name: dec.name.clone(),
3073 space: crate::AddressSpace::Private,
3074 binding: None,
3075 ty,
3076 init: None,
3077 memory_decorations: crate::MemoryDecorations::empty(),
3078 };
3079
3080 let inner = Variable::Input(crate::FunctionArgument {
3081 name: dec.name,
3082 ty: unsigned_ty,
3083 binding: Some(binding),
3084 });
3085 (inner, var)
3086 }
3087 ExtendedClass::Output => {
3088 let binding = dec.io_binding().ok();
3090 let init = match binding {
3091 Some(crate::Binding::BuiltIn(built_in)) => {
3092 match null::generate_default_built_in(
3093 Some(built_in),
3094 ty,
3095 &mut module.global_expressions,
3096 span,
3097 ) {
3098 Ok(handle) => Some(handle),
3099 Err(e) => {
3100 log::warn!("Failed to initialize output built-in: {e}");
3101 None
3102 }
3103 }
3104 }
3105 Some(crate::Binding::Location { .. }) => None,
3106 None => match module.types[ty].inner {
3107 crate::TypeInner::Struct { ref members, .. } => {
3108 let mut components = Vec::with_capacity(members.len());
3109 for member in members.iter() {
3110 let built_in = match member.binding {
3111 Some(crate::Binding::BuiltIn(built_in)) => Some(built_in),
3112 _ => None,
3113 };
3114 let handle = null::generate_default_built_in(
3115 built_in,
3116 member.ty,
3117 &mut module.global_expressions,
3118 span,
3119 )?;
3120 components.push(handle);
3121 }
3122 Some(
3123 module
3124 .global_expressions
3125 .append(crate::Expression::Compose { ty, components }, span),
3126 )
3127 }
3128 _ => None,
3129 },
3130 };
3131
3132 let var = crate::GlobalVariable {
3133 name: dec.name,
3134 space: crate::AddressSpace::Private,
3135 binding: None,
3136 ty,
3137 init,
3138 memory_decorations: crate::MemoryDecorations::empty(),
3139 };
3140 let inner = Variable::Output(crate::FunctionResult { ty, binding });
3141 (inner, var)
3142 }
3143 };
3144
3145 let handle = module.global_variables.append(var, span);
3146
3147 if module.types[ty].inner.can_comparison_sample(module) {
3148 log::debug!("\t\ttracking {handle:?} for sampling properties");
3149
3150 self.handle_sampling
3151 .insert(handle, image::SamplingFlags::empty());
3152 }
3153
3154 self.lookup_variable.insert(
3155 id,
3156 LookupVariable {
3157 inner,
3158 handle,
3159 type_id,
3160 },
3161 );
3162 Ok(())
3163 }
3164
3165 fn record_atomic_access(
3178 &mut self,
3179 ctx: &BlockContext,
3180 handle: Handle<crate::Expression>,
3181 ) -> Result<Handle<crate::Type>, Error> {
3182 log::debug!("\t\tlocating global variable in {handle:?}");
3183 match ctx.expressions[handle] {
3184 crate::Expression::Access { base, index } => {
3185 log::debug!("\t\t access {handle:?} {index:?}");
3186 let ty = self.record_atomic_access(ctx, base)?;
3187 let crate::TypeInner::Array { base, .. } = ctx.module.types[ty].inner else {
3188 unreachable!("Atomic operations on Access expressions only work for arrays");
3189 };
3190 Ok(base)
3191 }
3192 crate::Expression::AccessIndex { base, index } => {
3193 log::debug!("\t\t access index {handle:?} {index:?}");
3194 let ty = self.record_atomic_access(ctx, base)?;
3195 match ctx.module.types[ty].inner {
3196 crate::TypeInner::Struct { ref members, .. } => {
3197 let index = index as usize;
3198 self.upgrade_atomics.insert_field(ty, index);
3199 Ok(members[index].ty)
3200 }
3201 crate::TypeInner::Array { base, .. } => {
3202 Ok(base)
3203 }
3204 _ => unreachable!("Atomic operations on AccessIndex expressions only work for structs and arrays"),
3205 }
3206 }
3207 crate::Expression::GlobalVariable(h) => {
3208 log::debug!("\t\t found {h:?}");
3209 self.upgrade_atomics.insert_global(h);
3210 Ok(ctx.module.global_variables[h].ty)
3211 }
3212 _ => Err(Error::AtomicUpgradeError(
3213 crate::front::atomic_upgrade::Error::GlobalVariableMissing,
3214 )),
3215 }
3216 }
3217}
3218
3219fn resolve_constant(gctx: crate::proc::GlobalCtx, constant: &Constant) -> Option<u32> {
3220 let constant = match *constant {
3221 Constant::Constant(constant) => constant,
3222 Constant::Override(_) => return None,
3223 };
3224 match gctx.global_expressions[gctx.constants[constant].init] {
3225 crate::Expression::Literal(crate::Literal::U32(id)) => Some(id),
3226 crate::Expression::Literal(crate::Literal::I32(id)) => Some(id as u32),
3227 _ => None,
3228 }
3229}
3230
3231pub fn parse_u8_slice(data: &[u8], options: &Options) -> Result<crate::Module, Error> {
3232 if !data.len().is_multiple_of(4) {
3233 return Err(Error::IncompleteData);
3234 }
3235
3236 let words = data
3237 .chunks(4)
3238 .map(|c| u32::from_le_bytes(c.try_into().unwrap()));
3239 Frontend::new(words, options).parse()
3240}
3241
3242fn is_parent(mut child: usize, parent: usize, block_ctx: &BlockContext) -> bool {
3244 loop {
3245 if child == parent {
3246 break true;
3248 } else if child == 0 {
3249 break false;
3251 }
3252
3253 child = block_ctx.bodies[child].parent;
3254 }
3255}
3256
3257#[cfg(test)]
3258mod test {
3259 use alloc::vec;
3260
3261 #[test]
3262 fn parse() {
3263 let bin = vec![
3264 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00,
3266 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
3271 ];
3272 let _ = super::parse_u8_slice(&bin, &Default::default()).unwrap();
3273 }
3274}