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