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