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 S::DebugPrintf { .. } => unreachable!(),
1671 }
1672 i += 1;
1673 }
1674 Ok(())
1675 }
1676
1677 fn patch_function(
1678 &mut self,
1679 handle: Option<Handle<crate::Function>>,
1680 fun: &mut crate::Function,
1681 ) -> Result<(), Error> {
1682 let (fun_id, mut parameters_sampling) = match handle {
1684 Some(h) => {
1685 let (&fun_id, lookup) = self
1686 .lookup_function
1687 .iter_mut()
1688 .find(|&(_, ref lookup)| lookup.handle == h)
1689 .unwrap();
1690 (fun_id, mem::take(&mut lookup.parameters_sampling))
1691 }
1692 None => (0, Vec::new()),
1693 };
1694
1695 for (_, expr) in fun.expressions.iter_mut() {
1696 if let crate::Expression::CallResult(ref mut function) = *expr {
1697 let fun_id = self.deferred_function_calls[function.index()];
1698 *function = self.lookup_function.lookup(fun_id)?.handle;
1699 }
1700 }
1701
1702 self.patch_statements(
1703 &mut fun.body,
1704 &mut fun.expressions,
1705 &mut parameters_sampling,
1706 )?;
1707
1708 if let Some(lookup) = self.lookup_function.get_mut(&fun_id) {
1709 lookup.parameters_sampling = parameters_sampling;
1710 }
1711 Ok(())
1712 }
1713
1714 pub fn parse(mut self) -> Result<crate::Module, Error> {
1715 let mut module = {
1716 if self.next()? != spirv::MAGIC_NUMBER {
1717 return Err(Error::InvalidHeader);
1718 }
1719 let version_raw = self.next()?;
1720 let generator = self.next()?;
1721 let _bound = self.next()?;
1722 let _schema = self.next()?;
1723 log::debug!("Generated by {generator} version {version_raw:x}");
1724 crate::Module::default()
1725 };
1726
1727 self.layouter.clear();
1728 self.dummy_functions = Arena::new();
1729 self.lookup_function.clear();
1730 self.function_call_graph.clear();
1731
1732 loop {
1733 use spirv::Op;
1734
1735 let inst = match self.next_inst() {
1736 Ok(inst) => inst,
1737 Err(Error::IncompleteData) => break,
1738 Err(other) => return Err(other),
1739 };
1740 log::debug!("\t{:?} [{}]", inst.op, inst.wc);
1741
1742 match inst.op {
1743 Op::Capability => self.parse_capability(inst),
1744 Op::Extension => self.parse_extension(inst),
1745 Op::ExtInstImport => self.parse_ext_inst_import(inst),
1746 Op::MemoryModel => self.parse_memory_model(inst),
1747 Op::EntryPoint => self.parse_entry_point(inst),
1748 Op::ExecutionMode => self.parse_execution_mode(inst),
1749 Op::String => self.parse_string(inst),
1750 Op::Source => self.parse_source(inst),
1751 Op::SourceExtension => self.parse_source_extension(inst),
1752 Op::Name => self.parse_name(inst),
1753 Op::MemberName => self.parse_member_name(inst),
1754 Op::ModuleProcessed => self.parse_module_processed(inst),
1755 Op::Decorate => self.parse_decorate(inst),
1756 Op::MemberDecorate => self.parse_member_decorate(inst),
1757 Op::TypeVoid => self.parse_type_void(inst),
1758 Op::TypeBool => self.parse_type_bool(inst, &mut module),
1759 Op::TypeInt => self.parse_type_int(inst, &mut module),
1760 Op::TypeFloat => self.parse_type_float(inst, &mut module),
1761 Op::TypeVector => self.parse_type_vector(inst, &mut module),
1762 Op::TypeMatrix => self.parse_type_matrix(inst, &mut module),
1763 Op::TypeFunction => self.parse_type_function(inst),
1764 Op::TypePointer => self.parse_type_pointer(inst, &mut module),
1765 Op::TypeArray => self.parse_type_array(inst, &mut module),
1766 Op::TypeRuntimeArray => self.parse_type_runtime_array(inst, &mut module),
1767 Op::TypeStruct => self.parse_type_struct(inst, &mut module),
1768 Op::TypeImage => self.parse_type_image(inst, &mut module),
1769 Op::TypeSampledImage => self.parse_type_sampled_image(inst),
1770 Op::TypeSampler => self.parse_type_sampler(inst, &mut module),
1771 Op::Constant | Op::SpecConstant => self.parse_constant(inst, &mut module),
1772 Op::ConstantComposite | Op::SpecConstantComposite => {
1773 self.parse_composite_constant(inst, &mut module)
1774 }
1775 Op::ConstantNull | Op::Undef => self.parse_null_constant(inst, &mut module),
1776 Op::ConstantTrue | Op::SpecConstantTrue => {
1777 self.parse_bool_constant(inst, true, &mut module)
1778 }
1779 Op::ConstantFalse | Op::SpecConstantFalse => {
1780 self.parse_bool_constant(inst, false, &mut module)
1781 }
1782 Op::Variable => self.parse_global_variable(inst, &mut module),
1783 Op::Function => {
1784 self.switch(ModuleState::Function, inst.op)?;
1785 inst.expect(5)?;
1786 self.parse_function(&mut module)
1787 }
1788 Op::ExtInst => {
1789 let _ = self.next()?;
1791 let _ = self.next()?;
1792 let set_id = self.next()?;
1793 if Some(set_id) == self.ext_non_semantic_id {
1794 for _ in 0..inst.wc - 4 {
1796 self.next()?;
1797 }
1798 Ok(())
1799 } else {
1800 return Err(Error::UnsupportedInstruction(self.state, inst.op));
1801 }
1802 }
1803 _ => Err(Error::UnsupportedInstruction(self.state, inst.op)), }?;
1805 }
1806
1807 if !self.upgrade_atomics.is_empty() {
1808 log::debug!("Upgrading atomic pointers...");
1809 module.upgrade_atomics(&self.upgrade_atomics)?;
1810 }
1811
1812 for (ep, fun_id) in mem::take(&mut self.deferred_entry_points) {
1815 self.process_entry_point(&mut module, ep, fun_id)?;
1816 }
1817
1818 log::debug!("Patching...");
1819 {
1820 let mut nodes = petgraph::algo::toposort(&self.function_call_graph, None)
1821 .map_err(|cycle| Error::FunctionCallCycle(cycle.node_id()))?;
1822 nodes.reverse(); let mut functions = module.functions.take();
1824 for fun_id in nodes {
1825 if fun_id > !(functions.len() as u32) {
1826 continue;
1828 }
1829 let lookup = self.lookup_function.get_mut(&fun_id).unwrap();
1830 let fun = mem::take(&mut functions[lookup.handle]);
1832 lookup.handle = module
1834 .functions
1835 .append(fun, functions.get_span(lookup.handle));
1836 }
1837 }
1838 for (handle, fun) in module.functions.iter_mut() {
1840 self.patch_function(Some(handle), fun)?;
1841 }
1842 for ep in module.entry_points.iter_mut() {
1843 self.patch_function(None, &mut ep.function)?;
1844 }
1845
1846 for (handle, flags) in self.handle_sampling.drain() {
1848 if !image::patch_comparison_type(
1849 flags,
1850 module.global_variables.get_mut(handle),
1851 &mut module.types,
1852 ) {
1853 return Err(Error::InconsistentComparisonSampling(handle));
1854 }
1855 }
1856
1857 if !self.future_decor.is_empty() {
1858 log::debug!("Unused item decorations: {:?}", self.future_decor);
1859 self.future_decor.clear();
1860 }
1861 if !self.future_member_decor.is_empty() {
1862 log::debug!("Unused member decorations: {:?}", self.future_member_decor);
1863 self.future_member_decor.clear();
1864 }
1865
1866 Ok(module)
1867 }
1868
1869 fn parse_capability(&mut self, inst: Instruction) -> Result<(), Error> {
1870 self.switch(ModuleState::Capability, inst.op)?;
1871 inst.expect(2)?;
1872 let capability = self.next()?;
1873 let cap =
1874 spirv::Capability::from_u32(capability).ok_or(Error::UnknownCapability(capability))?;
1875 if !SUPPORTED_CAPABILITIES.contains(&cap) {
1876 if self.options.strict_capabilities {
1877 return Err(Error::UnsupportedCapability(cap));
1878 } else {
1879 log::warn!("Unknown capability {cap:?}");
1880 }
1881 }
1882 Ok(())
1883 }
1884
1885 fn parse_extension(&mut self, inst: Instruction) -> Result<(), Error> {
1886 self.switch(ModuleState::Extension, inst.op)?;
1887 inst.expect_at_least(2)?;
1888 let (name, left) = self.next_string(inst.wc - 1)?;
1889 if left != 0 {
1890 return Err(Error::InvalidOperand);
1891 }
1892 if !SUPPORTED_EXTENSIONS.contains(&name.as_str()) {
1893 return Err(Error::UnsupportedExtension(name));
1894 }
1895 Ok(())
1896 }
1897
1898 fn parse_ext_inst_import(&mut self, inst: Instruction) -> Result<(), Error> {
1899 self.switch(ModuleState::Extension, inst.op)?;
1900 inst.expect_at_least(3)?;
1901 let result_id = self.next()?;
1902 let (name, left) = self.next_string(inst.wc - 2)?;
1903 if left != 0 {
1904 return Err(Error::InvalidOperand);
1905 }
1906 if &name == "GLSL.std.450" {
1907 self.ext_glsl_id = Some(result_id);
1908 } else if &name == "NonSemantic.Shader.DebugInfo.100" {
1909 self.ext_non_semantic_id = Some(result_id);
1914 } else {
1915 return Err(Error::UnsupportedExtSet(name));
1916 }
1917 Ok(())
1918 }
1919
1920 fn parse_memory_model(&mut self, inst: Instruction) -> Result<(), Error> {
1921 self.switch(ModuleState::MemoryModel, inst.op)?;
1922 inst.expect(3)?;
1923 let _addressing_model = self.next()?;
1924 let _memory_model = self.next()?;
1925 Ok(())
1926 }
1927
1928 fn parse_entry_point(&mut self, inst: Instruction) -> Result<(), Error> {
1929 self.switch(ModuleState::EntryPoint, inst.op)?;
1930 inst.expect_at_least(4)?;
1931 let exec_model = self.next()?;
1932 let exec_model = spirv::ExecutionModel::from_u32(exec_model)
1933 .ok_or(Error::UnsupportedExecutionModel(exec_model))?;
1934 let function_id = self.next()?;
1935 let (name, left) = self.next_string(inst.wc - 3)?;
1936 let ep = EntryPoint {
1937 stage: match exec_model {
1938 spirv::ExecutionModel::Vertex => crate::ShaderStage::Vertex,
1939 spirv::ExecutionModel::Fragment => crate::ShaderStage::Fragment,
1940 spirv::ExecutionModel::GLCompute => crate::ShaderStage::Compute,
1941 spirv::ExecutionModel::TaskEXT => crate::ShaderStage::Task,
1942 spirv::ExecutionModel::MeshEXT => crate::ShaderStage::Mesh,
1943 _ => return Err(Error::UnsupportedExecutionModel(exec_model as u32)),
1944 },
1945 name,
1946 early_depth_test: None,
1947 workgroup_size: [0; 3],
1948 variable_ids: self.data.by_ref().take(left as usize).collect(),
1949 };
1950 self.lookup_entry_point.insert(function_id, ep);
1951 Ok(())
1952 }
1953
1954 fn parse_execution_mode(&mut self, inst: Instruction) -> Result<(), Error> {
1955 use spirv::ExecutionMode;
1956
1957 self.switch(ModuleState::ExecutionMode, inst.op)?;
1958 inst.expect_at_least(3)?;
1959
1960 let ep_id = self.next()?;
1961 let mode_id = self.next()?;
1962 let args: Vec<spirv::Word> = self.data.by_ref().take(inst.wc as usize - 3).collect();
1963
1964 let ep = self
1965 .lookup_entry_point
1966 .get_mut(&ep_id)
1967 .ok_or(Error::InvalidId(ep_id))?;
1968 let mode =
1969 ExecutionMode::from_u32(mode_id).ok_or(Error::UnsupportedExecutionMode(mode_id))?;
1970
1971 match mode {
1972 ExecutionMode::EarlyFragmentTests => {
1973 ep.early_depth_test = Some(crate::EarlyDepthTest::Force);
1974 }
1975 ExecutionMode::DepthUnchanged => {
1976 if let &mut Some(ref mut early_depth_test) = &mut ep.early_depth_test {
1977 if let &mut crate::EarlyDepthTest::Allow {
1978 ref mut conservative,
1979 } = early_depth_test
1980 {
1981 *conservative = crate::ConservativeDepth::Unchanged;
1982 }
1983 } else {
1984 ep.early_depth_test = Some(crate::EarlyDepthTest::Allow {
1985 conservative: crate::ConservativeDepth::Unchanged,
1986 });
1987 }
1988 }
1989 ExecutionMode::DepthGreater => {
1990 if let &mut Some(ref mut early_depth_test) = &mut ep.early_depth_test {
1991 if let &mut crate::EarlyDepthTest::Allow {
1992 ref mut conservative,
1993 } = early_depth_test
1994 {
1995 *conservative = crate::ConservativeDepth::GreaterEqual;
1996 }
1997 } else {
1998 ep.early_depth_test = Some(crate::EarlyDepthTest::Allow {
1999 conservative: crate::ConservativeDepth::GreaterEqual,
2000 });
2001 }
2002 }
2003 ExecutionMode::DepthLess => {
2004 if let &mut Some(ref mut early_depth_test) = &mut ep.early_depth_test {
2005 if let &mut crate::EarlyDepthTest::Allow {
2006 ref mut conservative,
2007 } = early_depth_test
2008 {
2009 *conservative = crate::ConservativeDepth::LessEqual;
2010 }
2011 } else {
2012 ep.early_depth_test = Some(crate::EarlyDepthTest::Allow {
2013 conservative: crate::ConservativeDepth::LessEqual,
2014 });
2015 }
2016 }
2017 ExecutionMode::DepthReplacing => {
2018 }
2020 ExecutionMode::OriginUpperLeft => {
2021 }
2023 ExecutionMode::LocalSize => {
2024 ep.workgroup_size = [args[0], args[1], args[2]];
2025 }
2026 _ => {
2027 return Err(Error::UnsupportedExecutionMode(mode_id));
2028 }
2029 }
2030
2031 Ok(())
2032 }
2033
2034 fn parse_string(&mut self, inst: Instruction) -> Result<(), Error> {
2035 self.switch(ModuleState::Source, inst.op)?;
2036 inst.expect_at_least(3)?;
2037 let _id = self.next()?;
2038 let (_name, _) = self.next_string(inst.wc - 2)?;
2039 Ok(())
2040 }
2041
2042 fn parse_source(&mut self, inst: Instruction) -> Result<(), Error> {
2043 self.switch(ModuleState::Source, inst.op)?;
2044 for _ in 1..inst.wc {
2045 let _ = self.next()?;
2046 }
2047 Ok(())
2048 }
2049
2050 fn parse_source_extension(&mut self, inst: Instruction) -> Result<(), Error> {
2051 self.switch(ModuleState::Source, inst.op)?;
2052 inst.expect_at_least(2)?;
2053 let (_name, _) = self.next_string(inst.wc - 1)?;
2054 Ok(())
2055 }
2056
2057 fn parse_name(&mut self, inst: Instruction) -> Result<(), Error> {
2058 self.switch(ModuleState::Name, inst.op)?;
2059 inst.expect_at_least(3)?;
2060 let id = self.next()?;
2061 let (name, left) = self.next_string(inst.wc - 2)?;
2062 if left != 0 {
2063 return Err(Error::InvalidOperand);
2064 }
2065 self.future_decor.entry(id).or_default().name = Some(name);
2066 Ok(())
2067 }
2068
2069 fn parse_member_name(&mut self, inst: Instruction) -> Result<(), Error> {
2070 self.switch(ModuleState::Name, inst.op)?;
2071 inst.expect_at_least(4)?;
2072 let id = self.next()?;
2073 let member = self.next()?;
2074 let (name, left) = self.next_string(inst.wc - 3)?;
2075 if left != 0 {
2076 return Err(Error::InvalidOperand);
2077 }
2078
2079 self.future_member_decor
2080 .entry((id, member))
2081 .or_default()
2082 .name = Some(name);
2083 Ok(())
2084 }
2085
2086 fn parse_module_processed(&mut self, inst: Instruction) -> Result<(), Error> {
2087 self.switch(ModuleState::Name, inst.op)?;
2088 inst.expect_at_least(2)?;
2089 let (_info, left) = self.next_string(inst.wc - 1)?;
2090 if left != 0 {
2092 return Err(Error::InvalidOperand);
2093 }
2094 Ok(())
2095 }
2096
2097 fn parse_decorate(&mut self, inst: Instruction) -> Result<(), Error> {
2098 self.switch(ModuleState::Annotation, inst.op)?;
2099 inst.expect_at_least(3)?;
2100 let id = self.next()?;
2101 let mut dec = self.future_decor.remove(&id).unwrap_or_default();
2102 self.next_decoration(inst, 2, &mut dec)?;
2103 self.future_decor.insert(id, dec);
2104 Ok(())
2105 }
2106
2107 fn parse_member_decorate(&mut self, inst: Instruction) -> Result<(), Error> {
2108 self.switch(ModuleState::Annotation, inst.op)?;
2109 inst.expect_at_least(4)?;
2110 let id = self.next()?;
2111 let member = self.next()?;
2112
2113 let mut dec = self
2114 .future_member_decor
2115 .remove(&(id, member))
2116 .unwrap_or_default();
2117 self.next_decoration(inst, 3, &mut dec)?;
2118 self.future_member_decor.insert((id, member), dec);
2119 Ok(())
2120 }
2121
2122 fn parse_type_void(&mut self, inst: Instruction) -> Result<(), Error> {
2123 self.switch(ModuleState::Type, inst.op)?;
2124 inst.expect(2)?;
2125 let id = self.next()?;
2126 self.lookup_void_type = Some(id);
2127 Ok(())
2128 }
2129
2130 fn parse_type_bool(
2131 &mut self,
2132 inst: Instruction,
2133 module: &mut crate::Module,
2134 ) -> Result<(), Error> {
2135 let start = self.data_offset;
2136 self.switch(ModuleState::Type, inst.op)?;
2137 inst.expect(2)?;
2138 let id = self.next()?;
2139 let inner = crate::TypeInner::Scalar(crate::Scalar::BOOL);
2140 self.lookup_type.insert(
2141 id,
2142 LookupType {
2143 handle: module.types.insert(
2144 crate::Type {
2145 name: self.future_decor.remove(&id).and_then(|dec| dec.name),
2146 inner,
2147 },
2148 self.span_from_with_op(start),
2149 ),
2150 base_id: None,
2151 },
2152 );
2153 Ok(())
2154 }
2155
2156 fn parse_type_int(
2157 &mut self,
2158 inst: Instruction,
2159 module: &mut crate::Module,
2160 ) -> Result<(), Error> {
2161 let start = self.data_offset;
2162 self.switch(ModuleState::Type, inst.op)?;
2163 inst.expect(4)?;
2164 let id = self.next()?;
2165 let width = self.next()?;
2166 let sign = self.next()?;
2167 let inner = crate::TypeInner::Scalar(crate::Scalar {
2168 kind: match sign {
2169 0 => crate::ScalarKind::Uint,
2170 1 => crate::ScalarKind::Sint,
2171 _ => return Err(Error::InvalidSign(sign)),
2172 },
2173 width: map_width(width)?,
2174 });
2175 self.lookup_type.insert(
2176 id,
2177 LookupType {
2178 handle: module.types.insert(
2179 crate::Type {
2180 name: self.future_decor.remove(&id).and_then(|dec| dec.name),
2181 inner,
2182 },
2183 self.span_from_with_op(start),
2184 ),
2185 base_id: None,
2186 },
2187 );
2188 Ok(())
2189 }
2190
2191 fn parse_type_float(
2192 &mut self,
2193 inst: Instruction,
2194 module: &mut crate::Module,
2195 ) -> Result<(), Error> {
2196 let start = self.data_offset;
2197 self.switch(ModuleState::Type, inst.op)?;
2198 inst.expect(3)?;
2199 let id = self.next()?;
2200 let width = self.next()?;
2201 let inner = crate::TypeInner::Scalar(crate::Scalar::float(map_width(width)?));
2202 self.lookup_type.insert(
2203 id,
2204 LookupType {
2205 handle: module.types.insert(
2206 crate::Type {
2207 name: self.future_decor.remove(&id).and_then(|dec| dec.name),
2208 inner,
2209 },
2210 self.span_from_with_op(start),
2211 ),
2212 base_id: None,
2213 },
2214 );
2215 Ok(())
2216 }
2217
2218 fn parse_type_vector(
2219 &mut self,
2220 inst: Instruction,
2221 module: &mut crate::Module,
2222 ) -> Result<(), Error> {
2223 let start = self.data_offset;
2224 self.switch(ModuleState::Type, inst.op)?;
2225 inst.expect(4)?;
2226 let id = self.next()?;
2227 let type_id = self.next()?;
2228 let type_lookup = self.lookup_type.lookup(type_id)?;
2229 let scalar = match module.types[type_lookup.handle].inner {
2230 crate::TypeInner::Scalar(scalar) => scalar,
2231 _ => return Err(Error::InvalidInnerType(type_id)),
2232 };
2233 let component_count = self.next()?;
2234 let inner = crate::TypeInner::Vector {
2235 size: map_vector_size(component_count)?,
2236 scalar,
2237 };
2238 self.lookup_type.insert(
2239 id,
2240 LookupType {
2241 handle: module.types.insert(
2242 crate::Type {
2243 name: self.future_decor.remove(&id).and_then(|dec| dec.name),
2244 inner,
2245 },
2246 self.span_from_with_op(start),
2247 ),
2248 base_id: Some(type_id),
2249 },
2250 );
2251 Ok(())
2252 }
2253
2254 fn parse_type_matrix(
2255 &mut self,
2256 inst: Instruction,
2257 module: &mut crate::Module,
2258 ) -> Result<(), Error> {
2259 let start = self.data_offset;
2260 self.switch(ModuleState::Type, inst.op)?;
2261 inst.expect(4)?;
2262 let id = self.next()?;
2263 let vector_type_id = self.next()?;
2264 let num_columns = self.next()?;
2265 let decor = self.future_decor.remove(&id);
2266
2267 let vector_type_lookup = self.lookup_type.lookup(vector_type_id)?;
2268 let inner = match module.types[vector_type_lookup.handle].inner {
2269 crate::TypeInner::Vector { size, scalar } => crate::TypeInner::Matrix {
2270 columns: map_vector_size(num_columns)?,
2271 rows: size,
2272 scalar,
2273 },
2274 _ => return Err(Error::InvalidInnerType(vector_type_id)),
2275 };
2276
2277 self.lookup_type.insert(
2278 id,
2279 LookupType {
2280 handle: module.types.insert(
2281 crate::Type {
2282 name: decor.and_then(|dec| dec.name),
2283 inner,
2284 },
2285 self.span_from_with_op(start),
2286 ),
2287 base_id: Some(vector_type_id),
2288 },
2289 );
2290 Ok(())
2291 }
2292
2293 fn parse_type_function(&mut self, inst: Instruction) -> Result<(), Error> {
2294 self.switch(ModuleState::Type, inst.op)?;
2295 inst.expect_at_least(3)?;
2296 let id = self.next()?;
2297 let return_type_id = self.next()?;
2298 let parameter_type_ids = self.data.by_ref().take(inst.wc as usize - 3).collect();
2299 self.lookup_function_type.insert(
2300 id,
2301 LookupFunctionType {
2302 parameter_type_ids,
2303 return_type_id,
2304 },
2305 );
2306 Ok(())
2307 }
2308
2309 fn parse_type_pointer(
2310 &mut self,
2311 inst: Instruction,
2312 module: &mut crate::Module,
2313 ) -> Result<(), Error> {
2314 let start = self.data_offset;
2315 self.switch(ModuleState::Type, inst.op)?;
2316 inst.expect(4)?;
2317 let id = self.next()?;
2318 let storage_class = self.next()?;
2319 let type_id = self.next()?;
2320
2321 let decor = self.future_decor.remove(&id);
2322 let base_lookup_ty = self.lookup_type.lookup(type_id)?;
2323 let base_inner = &module.types[base_lookup_ty.handle].inner;
2324
2325 let space = if let Some(space) = base_inner.pointer_space() {
2326 space
2327 } else if self
2328 .lookup_storage_buffer_types
2329 .contains_key(&base_lookup_ty.handle)
2330 {
2331 crate::AddressSpace::Storage {
2332 access: crate::StorageAccess::default(),
2333 }
2334 } else {
2335 match map_storage_class(storage_class)? {
2336 ExtendedClass::Global(space) => space,
2337 ExtendedClass::Input | ExtendedClass::Output => crate::AddressSpace::Private,
2338 }
2339 };
2340
2341 if let crate::TypeInner::Array {
2345 size: crate::ArraySize::Dynamic,
2346 ..
2347 } = *base_inner
2348 {
2349 match space {
2350 crate::AddressSpace::Storage { .. } => {}
2351 _ => {
2352 return Err(Error::UnsupportedRuntimeArrayStorageClass);
2353 }
2354 }
2355 }
2356
2357 let lookup_ty = if space == crate::AddressSpace::Handle {
2359 base_lookup_ty.clone()
2360 } else {
2361 LookupType {
2362 handle: module.types.insert(
2363 crate::Type {
2364 name: decor.and_then(|dec| dec.name),
2365 inner: crate::TypeInner::Pointer {
2366 base: base_lookup_ty.handle,
2367 space,
2368 },
2369 },
2370 self.span_from_with_op(start),
2371 ),
2372 base_id: Some(type_id),
2373 }
2374 };
2375 self.lookup_type.insert(id, lookup_ty);
2376 Ok(())
2377 }
2378
2379 fn parse_type_array(
2380 &mut self,
2381 inst: Instruction,
2382 module: &mut crate::Module,
2383 ) -> Result<(), Error> {
2384 let start = self.data_offset;
2385 self.switch(ModuleState::Type, inst.op)?;
2386 inst.expect(4)?;
2387 let id = self.next()?;
2388 let type_id = self.next()?;
2389 let length_id = self.next()?;
2390 let length_const = self.lookup_constant.lookup(length_id)?;
2391
2392 let size = resolve_constant(module.to_ctx(), &length_const.inner)
2393 .and_then(NonZeroU32::new)
2394 .ok_or(Error::InvalidArraySize(length_id))?;
2395
2396 let decor = self.future_decor.remove(&id).unwrap_or_default();
2397 let base = self.lookup_type.lookup(type_id)?.handle;
2398
2399 self.layouter.update(module.to_ctx()).unwrap();
2400
2401 let inner = if let crate::TypeInner::Image { .. } | crate::TypeInner::Sampler { .. } =
2433 module.types[base].inner
2434 {
2435 crate::TypeInner::BindingArray {
2436 base,
2437 size: crate::ArraySize::Constant(size),
2438 }
2439 } else {
2440 crate::TypeInner::Array {
2441 base,
2442 size: crate::ArraySize::Constant(size),
2443 stride: match decor.array_stride {
2444 Some(stride) => stride.get(),
2445 None => self.layouter[base].to_stride(),
2446 },
2447 }
2448 };
2449
2450 self.lookup_type.insert(
2451 id,
2452 LookupType {
2453 handle: module.types.insert(
2454 crate::Type {
2455 name: decor.name,
2456 inner,
2457 },
2458 self.span_from_with_op(start),
2459 ),
2460 base_id: Some(type_id),
2461 },
2462 );
2463 Ok(())
2464 }
2465
2466 fn parse_type_runtime_array(
2467 &mut self,
2468 inst: Instruction,
2469 module: &mut crate::Module,
2470 ) -> Result<(), Error> {
2471 let start = self.data_offset;
2472 self.switch(ModuleState::Type, inst.op)?;
2473 inst.expect(3)?;
2474 let id = self.next()?;
2475 let type_id = self.next()?;
2476
2477 let decor = self.future_decor.remove(&id).unwrap_or_default();
2478 let base = self.lookup_type.lookup(type_id)?.handle;
2479
2480 self.layouter.update(module.to_ctx()).unwrap();
2481
2482 let inner = if let crate::TypeInner::Image { .. } | crate::TypeInner::Sampler { .. } =
2484 module.types[base].inner
2485 {
2486 crate::TypeInner::BindingArray {
2487 base: self.lookup_type.lookup(type_id)?.handle,
2488 size: crate::ArraySize::Dynamic,
2489 }
2490 } else {
2491 crate::TypeInner::Array {
2492 base: self.lookup_type.lookup(type_id)?.handle,
2493 size: crate::ArraySize::Dynamic,
2494 stride: match decor.array_stride {
2495 Some(stride) => stride.get(),
2496 None => self.layouter[base].to_stride(),
2497 },
2498 }
2499 };
2500
2501 self.lookup_type.insert(
2502 id,
2503 LookupType {
2504 handle: module.types.insert(
2505 crate::Type {
2506 name: decor.name,
2507 inner,
2508 },
2509 self.span_from_with_op(start),
2510 ),
2511 base_id: Some(type_id),
2512 },
2513 );
2514 Ok(())
2515 }
2516
2517 fn parse_type_struct(
2518 &mut self,
2519 inst: Instruction,
2520 module: &mut crate::Module,
2521 ) -> Result<(), Error> {
2522 let start = self.data_offset;
2523 self.switch(ModuleState::Type, inst.op)?;
2524 inst.expect_at_least(2)?;
2525 let id = self.next()?;
2526 let parent_decor = self.future_decor.remove(&id);
2527 let is_storage_buffer = parent_decor
2528 .as_ref()
2529 .is_some_and(|decor| decor.storage_buffer);
2530
2531 self.layouter.update(module.to_ctx()).unwrap();
2532
2533 let mut members = Vec::<crate::StructMember>::with_capacity(inst.wc as usize - 2);
2534 let mut member_lookups = Vec::with_capacity(members.capacity());
2535 let mut storage_access = crate::StorageAccess::empty();
2536 let mut span = 0;
2537 let mut alignment = Alignment::ONE;
2538 for i in 0..u32::from(inst.wc) - 2 {
2539 let type_id = self.next()?;
2540 let ty = self.lookup_type.lookup(type_id)?.handle;
2541 let decor = self
2542 .future_member_decor
2543 .remove(&(id, i))
2544 .unwrap_or_default();
2545
2546 storage_access |= decor.flags.to_storage_access();
2547
2548 member_lookups.push(LookupMember {
2549 type_id,
2550 row_major: decor.matrix_major == Some(Majority::Row),
2551 });
2552
2553 let member_alignment = self.layouter[ty].alignment;
2554 span = member_alignment.round_up(span);
2555 alignment = member_alignment.max(alignment);
2556
2557 let binding = decor.io_binding().ok();
2558 if let Some(offset) = decor.offset {
2559 span = offset;
2560 }
2561 let offset = span;
2562
2563 span += self.layouter[ty].size;
2564
2565 let inner = &module.types[ty].inner;
2566 if let crate::TypeInner::Matrix {
2567 columns,
2568 rows,
2569 scalar,
2570 } = *inner
2571 {
2572 if let Some(stride) = decor.matrix_stride {
2573 let expected_stride = Alignment::from(rows) * scalar.width as u32;
2574 if stride.get() != expected_stride {
2575 return Err(Error::UnsupportedMatrixStride {
2576 stride: stride.get(),
2577 columns: columns as u8,
2578 rows: rows as u8,
2579 width: scalar.width,
2580 });
2581 }
2582 }
2583 }
2584
2585 members.push(crate::StructMember {
2586 name: decor.name,
2587 ty,
2588 binding,
2589 offset,
2590 });
2591 }
2592
2593 span = alignment.round_up(span);
2594
2595 let inner = crate::TypeInner::Struct { span, members };
2596
2597 let ty_handle = module.types.insert(
2598 crate::Type {
2599 name: parent_decor.and_then(|dec| dec.name),
2600 inner,
2601 },
2602 self.span_from_with_op(start),
2603 );
2604
2605 if is_storage_buffer {
2606 self.lookup_storage_buffer_types
2607 .insert(ty_handle, storage_access);
2608 }
2609 for (i, member_lookup) in member_lookups.into_iter().enumerate() {
2610 self.lookup_member
2611 .insert((ty_handle, i as u32), member_lookup);
2612 }
2613 self.lookup_type.insert(
2614 id,
2615 LookupType {
2616 handle: ty_handle,
2617 base_id: None,
2618 },
2619 );
2620 Ok(())
2621 }
2622
2623 fn parse_type_image(
2624 &mut self,
2625 inst: Instruction,
2626 module: &mut crate::Module,
2627 ) -> Result<(), Error> {
2628 let start = self.data_offset;
2629 self.switch(ModuleState::Type, inst.op)?;
2630 inst.expect(9)?;
2631
2632 let id = self.next()?;
2633 let sample_type_id = self.next()?;
2634 let dim = self.next()?;
2635 let is_depth = self.next()?;
2636 let is_array = self.next()? != 0;
2637 let is_msaa = self.next()? != 0;
2638 let is_sampled = self.next()?;
2639 let format = self.next()?;
2640
2641 let dim = map_image_dim(dim)?;
2642 let decor = self.future_decor.remove(&id).unwrap_or_default();
2643
2644 module.types.insert(
2646 crate::Type {
2647 name: None,
2648 inner: {
2649 let scalar = crate::Scalar::F32;
2650 match dim.required_coordinate_size() {
2651 None => crate::TypeInner::Scalar(scalar),
2652 Some(size) => crate::TypeInner::Vector { size, scalar },
2653 }
2654 },
2655 },
2656 Default::default(),
2657 );
2658
2659 let base_handle = self.lookup_type.lookup(sample_type_id)?.handle;
2660 let kind = module.types[base_handle]
2661 .inner
2662 .scalar_kind()
2663 .ok_or(Error::InvalidImageBaseType(base_handle))?;
2664
2665 let inner = crate::TypeInner::Image {
2666 class: if is_depth == 1 {
2667 if is_sampled == 2 {
2668 return Err(Error::InvalidImageDepthStorage);
2669 }
2670
2671 crate::ImageClass::Depth { multi: is_msaa }
2672 }
2673 else if is_sampled == 2 && format == 0 {
2677 return Err(Error::InvalidStorageImageWithoutFormat);
2678 }
2679 else if format != 0 && (is_sampled == 0 || is_sampled == 2) {
2684 crate::ImageClass::Storage {
2685 format: map_image_format(format)?,
2686 access: crate::StorageAccess::default(),
2687 }
2688 }
2689 else {
2692 crate::ImageClass::Sampled {
2693 kind,
2694 multi: is_msaa,
2695 }
2696 },
2697 dim,
2698 arrayed: is_array,
2699 };
2700
2701 let handle = module.types.insert(
2702 crate::Type {
2703 name: decor.name,
2704 inner,
2705 },
2706 self.span_from_with_op(start),
2707 );
2708
2709 self.lookup_type.insert(
2710 id,
2711 LookupType {
2712 handle,
2713 base_id: Some(sample_type_id),
2714 },
2715 );
2716 Ok(())
2717 }
2718
2719 fn parse_type_sampled_image(&mut self, inst: Instruction) -> Result<(), Error> {
2720 self.switch(ModuleState::Type, inst.op)?;
2721 inst.expect(3)?;
2722 let id = self.next()?;
2723 let image_id = self.next()?;
2724 self.lookup_type.insert(
2725 id,
2726 LookupType {
2727 handle: self.lookup_type.lookup(image_id)?.handle,
2728 base_id: Some(image_id),
2729 },
2730 );
2731 Ok(())
2732 }
2733
2734 fn parse_type_sampler(
2735 &mut self,
2736 inst: Instruction,
2737 module: &mut crate::Module,
2738 ) -> Result<(), Error> {
2739 let start = self.data_offset;
2740 self.switch(ModuleState::Type, inst.op)?;
2741 inst.expect(2)?;
2742 let id = self.next()?;
2743 let decor = self.future_decor.remove(&id).unwrap_or_default();
2744 let handle = module.types.insert(
2745 crate::Type {
2746 name: decor.name,
2747 inner: crate::TypeInner::Sampler { comparison: false },
2748 },
2749 self.span_from_with_op(start),
2750 );
2751 self.lookup_type.insert(
2752 id,
2753 LookupType {
2754 handle,
2755 base_id: None,
2756 },
2757 );
2758 Ok(())
2759 }
2760
2761 fn parse_constant(
2762 &mut self,
2763 inst: Instruction,
2764 module: &mut crate::Module,
2765 ) -> Result<(), Error> {
2766 let start = self.data_offset;
2767 self.switch(ModuleState::Type, inst.op)?;
2768 inst.expect_at_least(4)?;
2769 let type_id = self.next()?;
2770 let id = self.next()?;
2771 let type_lookup = self.lookup_type.lookup(type_id)?;
2772 let ty = type_lookup.handle;
2773
2774 let literal = match module.types[ty].inner {
2775 crate::TypeInner::Scalar(crate::Scalar {
2776 kind: crate::ScalarKind::Uint,
2777 width,
2778 }) => {
2779 let low = self.next()?;
2780 match width {
2781 4 => crate::Literal::U32(low),
2782 8 => {
2783 inst.expect(5)?;
2784 let high = self.next()?;
2785 crate::Literal::U64((u64::from(high) << 32) | u64::from(low))
2786 }
2787 _ => return Err(Error::InvalidTypeWidth(width as u32)),
2788 }
2789 }
2790 crate::TypeInner::Scalar(crate::Scalar {
2791 kind: crate::ScalarKind::Sint,
2792 width,
2793 }) => {
2794 let low = self.next()?;
2795 match width {
2796 4 => crate::Literal::I32(low as i32),
2797 8 => {
2798 inst.expect(5)?;
2799 let high = self.next()?;
2800 crate::Literal::I64(((u64::from(high) << 32) | u64::from(low)) as i64)
2801 }
2802 _ => return Err(Error::InvalidTypeWidth(width as u32)),
2803 }
2804 }
2805 crate::TypeInner::Scalar(crate::Scalar {
2806 kind: crate::ScalarKind::Float,
2807 width,
2808 }) => {
2809 let low = self.next()?;
2810 match width {
2811 2 => crate::Literal::F16(f16::from_bits(low as u16)),
2814 4 => crate::Literal::F32(f32::from_bits(low)),
2815 8 => {
2816 inst.expect(5)?;
2817 let high = self.next()?;
2818 crate::Literal::F64(f64::from_bits(
2819 (u64::from(high) << 32) | u64::from(low),
2820 ))
2821 }
2822 _ => return Err(Error::InvalidTypeWidth(width as u32)),
2823 }
2824 }
2825 _ => return Err(Error::UnsupportedType(type_lookup.handle)),
2826 };
2827
2828 let span = self.span_from_with_op(start);
2829
2830 let init = module
2831 .global_expressions
2832 .append(crate::Expression::Literal(literal), span);
2833
2834 self.insert_parsed_constant(module, id, type_id, ty, init, span)
2835 }
2836
2837 fn parse_composite_constant(
2838 &mut self,
2839 inst: Instruction,
2840 module: &mut crate::Module,
2841 ) -> Result<(), Error> {
2842 let start = self.data_offset;
2843 self.switch(ModuleState::Type, inst.op)?;
2844 inst.expect_at_least(3)?;
2845 let type_id = self.next()?;
2846 let id = self.next()?;
2847
2848 let type_lookup = self.lookup_type.lookup(type_id)?;
2849 let ty = type_lookup.handle;
2850
2851 let mut components = Vec::with_capacity(inst.wc as usize - 3);
2852 for _ in 0..components.capacity() {
2853 let start = self.data_offset;
2854 let component_id = self.next()?;
2855 let span = self.span_from_with_op(start);
2856 let constant = self.lookup_constant.lookup(component_id)?;
2857 let expr = module
2858 .global_expressions
2859 .append(constant.inner.to_expr(), span);
2860 components.push(expr);
2861 }
2862
2863 let span = self.span_from_with_op(start);
2864
2865 let init = module
2866 .global_expressions
2867 .append(crate::Expression::Compose { ty, components }, span);
2868
2869 self.insert_parsed_constant(module, id, type_id, ty, init, span)
2870 }
2871
2872 fn parse_null_constant(
2873 &mut self,
2874 inst: Instruction,
2875 module: &mut crate::Module,
2876 ) -> Result<(), Error> {
2877 let start = self.data_offset;
2878 self.switch(ModuleState::Type, inst.op)?;
2879 inst.expect(3)?;
2880 let type_id = self.next()?;
2881 let id = self.next()?;
2882 let span = self.span_from_with_op(start);
2883
2884 let type_lookup = self.lookup_type.lookup(type_id)?;
2885 let ty = type_lookup.handle;
2886
2887 let init = module
2888 .global_expressions
2889 .append(crate::Expression::ZeroValue(ty), span);
2890
2891 self.insert_parsed_constant(module, id, type_id, ty, init, span)
2892 }
2893
2894 fn parse_bool_constant(
2895 &mut self,
2896 inst: Instruction,
2897 value: bool,
2898 module: &mut crate::Module,
2899 ) -> Result<(), Error> {
2900 let start = self.data_offset;
2901 self.switch(ModuleState::Type, inst.op)?;
2902 inst.expect(3)?;
2903 let type_id = self.next()?;
2904 let id = self.next()?;
2905 let span = self.span_from_with_op(start);
2906
2907 let type_lookup = self.lookup_type.lookup(type_id)?;
2908 let ty = type_lookup.handle;
2909
2910 let init = module.global_expressions.append(
2911 crate::Expression::Literal(crate::Literal::Bool(value)),
2912 span,
2913 );
2914
2915 self.insert_parsed_constant(module, id, type_id, ty, init, span)
2916 }
2917
2918 fn insert_parsed_constant(
2919 &mut self,
2920 module: &mut crate::Module,
2921 id: u32,
2922 type_id: u32,
2923 ty: Handle<crate::Type>,
2924 init: Handle<crate::Expression>,
2925 span: crate::Span,
2926 ) -> Result<(), Error> {
2927 let decor = self.future_decor.remove(&id).unwrap_or_default();
2928
2929 let inner = if let Some(id) = decor.specialization_constant_id {
2930 let o = crate::Override {
2931 name: decor.name,
2932 id: Some(id.try_into().map_err(|_| Error::SpecIdTooHigh(id))?),
2933 ty,
2934 init: Some(init),
2935 };
2936 Constant::Override(module.overrides.append(o, span))
2937 } else {
2938 let c = crate::Constant {
2939 name: decor.name,
2940 ty,
2941 init,
2942 };
2943 Constant::Constant(module.constants.append(c, span))
2944 };
2945
2946 self.lookup_constant
2947 .insert(id, LookupConstant { inner, type_id });
2948 Ok(())
2949 }
2950
2951 fn parse_global_variable(
2952 &mut self,
2953 inst: Instruction,
2954 module: &mut crate::Module,
2955 ) -> Result<(), Error> {
2956 let start = self.data_offset;
2957 self.switch(ModuleState::Type, inst.op)?;
2958 inst.expect_at_least(4)?;
2959 let type_id = self.next()?;
2960 let id = self.next()?;
2961 let storage_class = self.next()?;
2962 let init = if inst.wc > 4 {
2963 inst.expect(5)?;
2964 let start = self.data_offset;
2965 let init_id = self.next()?;
2966 let span = self.span_from_with_op(start);
2967 let lconst = self.lookup_constant.lookup(init_id)?;
2968 let expr = module
2969 .global_expressions
2970 .append(lconst.inner.to_expr(), span);
2971 Some(expr)
2972 } else {
2973 None
2974 };
2975 let span = self.span_from_with_op(start);
2976 let dec = self.future_decor.remove(&id).unwrap_or_default();
2977
2978 let original_ty = self.lookup_type.lookup(type_id)?.handle;
2979 let mut ty = original_ty;
2980
2981 if let crate::TypeInner::Pointer { base, space: _ } = module.types[original_ty].inner {
2982 ty = base;
2983 }
2984
2985 if let crate::TypeInner::BindingArray { .. } = module.types[original_ty].inner {
2986 if dec.desc_set.is_none() || dec.desc_index.is_none() {
2989 return Err(Error::NonBindingArrayOfImageOrSamplers);
2990 }
2991 }
2992
2993 if let crate::TypeInner::Image {
2994 dim,
2995 arrayed,
2996 class: crate::ImageClass::Storage { format, access: _ },
2997 } = module.types[ty].inner
2998 {
2999 let access = dec.flags.to_storage_access();
3003
3004 ty = module.types.insert(
3005 crate::Type {
3006 name: None,
3007 inner: crate::TypeInner::Image {
3008 dim,
3009 arrayed,
3010 class: crate::ImageClass::Storage { format, access },
3011 },
3012 },
3013 Default::default(),
3014 );
3015 }
3016
3017 let ext_class = match self.lookup_storage_buffer_types.get(&ty) {
3018 Some(&access) => ExtendedClass::Global(crate::AddressSpace::Storage { access }),
3019 None => map_storage_class(storage_class)?,
3020 };
3021
3022 let (inner, var) = match ext_class {
3023 ExtendedClass::Global(mut space) => {
3024 if let crate::AddressSpace::Storage { ref mut access } = space {
3025 *access &= dec.flags.to_storage_access();
3026 }
3027 let var = crate::GlobalVariable {
3028 binding: dec.resource_binding(),
3029 name: dec.name,
3030 space,
3031 ty,
3032 init,
3033 memory_decorations: dec.flags.to_memory_decorations(),
3034 };
3035 (Variable::Global, var)
3036 }
3037 ExtendedClass::Input => {
3038 let binding = dec.io_binding()?;
3039 let mut unsigned_ty = ty;
3040 if let crate::Binding::BuiltIn(built_in) = binding {
3041 let needs_inner_uint = match built_in {
3042 crate::BuiltIn::BaseInstance
3043 | crate::BuiltIn::BaseVertex
3044 | crate::BuiltIn::InstanceIndex
3045 | crate::BuiltIn::SampleIndex
3046 | crate::BuiltIn::VertexIndex
3047 | crate::BuiltIn::PrimitiveIndex
3048 | crate::BuiltIn::LocalInvocationIndex => {
3049 Some(crate::TypeInner::Scalar(crate::Scalar::U32))
3050 }
3051 crate::BuiltIn::GlobalInvocationId
3052 | crate::BuiltIn::LocalInvocationId
3053 | crate::BuiltIn::WorkGroupId
3054 | crate::BuiltIn::WorkGroupSize => Some(crate::TypeInner::Vector {
3055 size: crate::VectorSize::Tri,
3056 scalar: crate::Scalar::U32,
3057 }),
3058 crate::BuiltIn::Barycentric { perspective: false } => {
3059 Some(crate::TypeInner::Vector {
3060 size: crate::VectorSize::Tri,
3061 scalar: crate::Scalar::F32,
3062 })
3063 }
3064 _ => None,
3065 };
3066 if let (Some(inner), Some(crate::ScalarKind::Sint)) =
3067 (needs_inner_uint, module.types[ty].inner.scalar_kind())
3068 {
3069 unsigned_ty = module
3070 .types
3071 .insert(crate::Type { name: None, inner }, Default::default());
3072 }
3073 }
3074
3075 let var = crate::GlobalVariable {
3076 name: dec.name.clone(),
3077 space: crate::AddressSpace::Private,
3078 binding: None,
3079 ty,
3080 init: None,
3081 memory_decorations: crate::MemoryDecorations::empty(),
3082 };
3083
3084 let inner = Variable::Input(crate::FunctionArgument {
3085 name: dec.name,
3086 ty: unsigned_ty,
3087 binding: Some(binding),
3088 });
3089 (inner, var)
3090 }
3091 ExtendedClass::Output => {
3092 let binding = dec.io_binding().ok();
3094 let init = match binding {
3095 Some(crate::Binding::BuiltIn(built_in)) => {
3096 match null::generate_default_built_in(
3097 Some(built_in),
3098 ty,
3099 &mut module.global_expressions,
3100 span,
3101 ) {
3102 Ok(handle) => Some(handle),
3103 Err(e) => {
3104 log::warn!("Failed to initialize output built-in: {e}");
3105 None
3106 }
3107 }
3108 }
3109 Some(crate::Binding::Location { .. }) => None,
3110 None => match module.types[ty].inner {
3111 crate::TypeInner::Struct { ref members, .. } => {
3112 let mut components = Vec::with_capacity(members.len());
3113 for member in members.iter() {
3114 let built_in = match member.binding {
3115 Some(crate::Binding::BuiltIn(built_in)) => Some(built_in),
3116 _ => None,
3117 };
3118 let handle = null::generate_default_built_in(
3119 built_in,
3120 member.ty,
3121 &mut module.global_expressions,
3122 span,
3123 )?;
3124 components.push(handle);
3125 }
3126 Some(
3127 module
3128 .global_expressions
3129 .append(crate::Expression::Compose { ty, components }, span),
3130 )
3131 }
3132 _ => None,
3133 },
3134 };
3135
3136 let var = crate::GlobalVariable {
3137 name: dec.name,
3138 space: crate::AddressSpace::Private,
3139 binding: None,
3140 ty,
3141 init,
3142 memory_decorations: crate::MemoryDecorations::empty(),
3143 };
3144 let inner = Variable::Output(crate::FunctionResult { ty, binding });
3145 (inner, var)
3146 }
3147 };
3148
3149 let handle = module.global_variables.append(var, span);
3150
3151 if module.types[ty].inner.can_comparison_sample(module) {
3152 log::debug!("\t\ttracking {handle:?} for sampling properties");
3153
3154 self.handle_sampling
3155 .insert(handle, image::SamplingFlags::empty());
3156 }
3157
3158 self.lookup_variable.insert(
3159 id,
3160 LookupVariable {
3161 inner,
3162 handle,
3163 type_id,
3164 },
3165 );
3166 Ok(())
3167 }
3168
3169 fn record_atomic_access(
3182 &mut self,
3183 ctx: &BlockContext,
3184 handle: Handle<crate::Expression>,
3185 ) -> Result<Handle<crate::Type>, Error> {
3186 log::debug!("\t\tlocating global variable in {handle:?}");
3187 match ctx.expressions[handle] {
3188 crate::Expression::Access { base, index } => {
3189 log::debug!("\t\t access {handle:?} {index:?}");
3190 let ty = self.record_atomic_access(ctx, base)?;
3191 let crate::TypeInner::Array { base, .. } = ctx.module.types[ty].inner else {
3192 unreachable!("Atomic operations on Access expressions only work for arrays");
3193 };
3194 Ok(base)
3195 }
3196 crate::Expression::AccessIndex { base, index } => {
3197 log::debug!("\t\t access index {handle:?} {index:?}");
3198 let ty = self.record_atomic_access(ctx, base)?;
3199 match ctx.module.types[ty].inner {
3200 crate::TypeInner::Struct { ref members, .. } => {
3201 let index = index as usize;
3202 self.upgrade_atomics.insert_field(ty, index);
3203 Ok(members[index].ty)
3204 }
3205 crate::TypeInner::Array { base, .. } => {
3206 Ok(base)
3207 }
3208 _ => unreachable!("Atomic operations on AccessIndex expressions only work for structs and arrays"),
3209 }
3210 }
3211 crate::Expression::GlobalVariable(h) => {
3212 log::debug!("\t\t found {h:?}");
3213 self.upgrade_atomics.insert_global(h);
3214 Ok(ctx.module.global_variables[h].ty)
3215 }
3216 _ => Err(Error::AtomicUpgradeError(
3217 crate::front::atomic_upgrade::Error::GlobalVariableMissing,
3218 )),
3219 }
3220 }
3221}
3222
3223fn resolve_constant(gctx: crate::proc::GlobalCtx, constant: &Constant) -> Option<u32> {
3224 let constant = match *constant {
3225 Constant::Constant(constant) => constant,
3226 Constant::Override(_) => return None,
3227 };
3228 match gctx.global_expressions[gctx.constants[constant].init] {
3229 crate::Expression::Literal(crate::Literal::U32(id)) => Some(id),
3230 crate::Expression::Literal(crate::Literal::I32(id)) => Some(id as u32),
3231 _ => None,
3232 }
3233}
3234
3235pub fn parse_u8_slice(data: &[u8], options: &Options) -> Result<crate::Module, Error> {
3236 if !data.len().is_multiple_of(4) {
3237 return Err(Error::IncompleteData);
3238 }
3239
3240 let words = data
3241 .chunks(4)
3242 .map(|c| u32::from_le_bytes(c.try_into().unwrap()));
3243 Frontend::new(words, options).parse()
3244}
3245
3246fn is_parent(mut child: usize, parent: usize, block_ctx: &BlockContext) -> bool {
3248 loop {
3249 if child == parent {
3250 break true;
3252 } else if child == 0 {
3253 break false;
3255 }
3256
3257 child = block_ctx.bodies[child].parent;
3258 }
3259}
3260
3261#[cfg(test)]
3262mod test {
3263 use alloc::vec;
3264
3265 #[test]
3266 fn parse() {
3267 let bin = vec![
3268 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00,
3270 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
3275 ];
3276 let _ = super::parse_u8_slice(&bin, &Default::default()).unwrap();
3277 }
3278}