1mod convert;
31mod error;
32mod function;
33mod image;
34mod null;
35
36pub use error::Error;
37
38use alloc::{borrow::ToOwned, format, string::String, vec, vec::Vec};
39use core::{convert::TryInto, mem, num::NonZeroU32};
40
41use half::f16;
42use petgraph::graphmap::GraphMap;
43
44use super::atomic_upgrade::Upgrades;
45use crate::{
46 arena::{Arena, Handle, UniqueArena},
47 proc::{Alignment, Layouter},
48 FastHashMap, FastHashSet, FastIndexMap,
49};
50use convert::*;
51use function::*;
52
53pub const SUPPORTED_CAPABILITIES: &[spirv::Capability] = &[
54 spirv::Capability::Shader,
55 spirv::Capability::VulkanMemoryModel,
56 spirv::Capability::ClipDistance,
57 spirv::Capability::CullDistance,
58 spirv::Capability::SampleRateShading,
59 spirv::Capability::DerivativeControl,
60 spirv::Capability::Matrix,
61 spirv::Capability::ImageQuery,
62 spirv::Capability::Sampled1D,
63 spirv::Capability::Image1D,
64 spirv::Capability::SampledCubeArray,
65 spirv::Capability::ImageCubeArray,
66 spirv::Capability::StorageImageExtendedFormats,
67 spirv::Capability::Int8,
68 spirv::Capability::Int16,
69 spirv::Capability::Int64,
70 spirv::Capability::Int64Atomics,
71 spirv::Capability::Float16,
72 spirv::Capability::AtomicFloat32AddEXT,
73 spirv::Capability::Float64,
74 spirv::Capability::Geometry,
75 spirv::Capability::MultiView,
76 spirv::Capability::StorageBuffer16BitAccess,
77 spirv::Capability::UniformAndStorageBuffer16BitAccess,
78 spirv::Capability::GroupNonUniform,
79 spirv::Capability::GroupNonUniformVote,
80 spirv::Capability::GroupNonUniformArithmetic,
81 spirv::Capability::GroupNonUniformBallot,
82 spirv::Capability::GroupNonUniformShuffle,
83 spirv::Capability::GroupNonUniformShuffleRelative,
84 spirv::Capability::RuntimeDescriptorArray,
85 spirv::Capability::StorageImageMultisample,
86 spirv::Capability::FragmentBarycentricKHR,
87 spirv::Capability::UniformBufferArrayDynamicIndexing,
89 spirv::Capability::StorageBufferArrayDynamicIndexing,
90];
91pub const SUPPORTED_EXTENSIONS: &[&str] = &[
92 "SPV_KHR_storage_buffer_storage_class",
93 "SPV_KHR_vulkan_memory_model",
94 "SPV_KHR_multiview",
95 "SPV_EXT_descriptor_indexing",
96 "SPV_EXT_shader_atomic_float_add",
97 "SPV_KHR_16bit_storage",
98];
99pub const SUPPORTED_EXT_SETS: &[&str] = &["GLSL.std.450"];
100
101#[derive(Copy, Clone)]
102pub struct Instruction {
103 op: spirv::Op,
104 wc: u16,
105}
106
107impl Instruction {
108 const fn expect(self, count: u16) -> Result<(), Error> {
109 if self.wc == count {
110 Ok(())
111 } else {
112 Err(Error::InvalidOperandCount(self.op, self.wc))
113 }
114 }
115
116 fn expect_at_least(self, count: u16) -> Result<u16, Error> {
117 self.wc
118 .checked_sub(count)
119 .ok_or(Error::InvalidOperandCount(self.op, self.wc))
120 }
121}
122
123impl crate::TypeInner {
124 fn can_comparison_sample(&self, module: &crate::Module) -> bool {
125 match *self {
126 crate::TypeInner::Image {
127 class:
128 crate::ImageClass::Sampled {
129 kind: crate::ScalarKind::Float,
130 multi: false,
131 },
132 ..
133 } => true,
134 crate::TypeInner::Sampler { .. } => true,
135 crate::TypeInner::BindingArray { base, .. } => {
136 module.types[base].inner.can_comparison_sample(module)
137 }
138 _ => false,
139 }
140 }
141}
142
143#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
144pub enum ModuleState {
145 Empty,
146 Capability,
147 Extension,
148 ExtInstImport,
149 MemoryModel,
150 EntryPoint,
151 ExecutionMode,
152 Source,
153 Name,
154 ModuleProcessed,
155 Annotation,
156 Type,
157 Function,
158}
159
160trait LookupHelper {
161 type Target;
162 fn lookup(&self, key: spirv::Word) -> Result<&Self::Target, Error>;
163}
164
165impl<T> LookupHelper for FastHashMap<spirv::Word, T> {
166 type Target = T;
167 fn lookup(&self, key: spirv::Word) -> Result<&T, Error> {
168 self.get(&key).ok_or(Error::InvalidId(key))
169 }
170}
171
172impl crate::ImageDimension {
173 const fn required_coordinate_size(&self) -> Option<crate::VectorSize> {
174 match *self {
175 crate::ImageDimension::D1 => None,
176 crate::ImageDimension::D2 => Some(crate::VectorSize::Bi),
177 crate::ImageDimension::D3 => Some(crate::VectorSize::Tri),
178 crate::ImageDimension::Cube => Some(crate::VectorSize::Tri),
179 }
180 }
181}
182
183type MemberIndex = u32;
184
185bitflags::bitflags! {
186 #[derive(Clone, Copy, Debug, Default)]
187 struct DecorationFlags: u32 {
188 const NON_READABLE = 0x1;
189 const NON_WRITABLE = 0x2;
190 }
191}
192
193impl DecorationFlags {
194 fn to_storage_access(self) -> crate::StorageAccess {
195 let mut access = crate::StorageAccess::LOAD | crate::StorageAccess::STORE;
196 if self.contains(DecorationFlags::NON_READABLE) {
197 access &= !crate::StorageAccess::LOAD;
198 }
199 if self.contains(DecorationFlags::NON_WRITABLE) {
200 access &= !crate::StorageAccess::STORE;
201 }
202 access
203 }
204}
205
206#[derive(Debug, PartialEq)]
207enum Majority {
208 Column,
209 Row,
210}
211
212#[derive(Debug, Default)]
213struct Decoration {
214 name: Option<String>,
215 built_in: Option<spirv::Word>,
216 location: Option<spirv::Word>,
217 desc_set: Option<spirv::Word>,
218 desc_index: Option<spirv::Word>,
219 specialization_constant_id: Option<spirv::Word>,
220 storage_buffer: bool,
221 offset: Option<spirv::Word>,
222 array_stride: Option<NonZeroU32>,
223 matrix_stride: Option<NonZeroU32>,
224 matrix_major: Option<Majority>,
225 invariant: bool,
226 interpolation: Option<crate::Interpolation>,
227 sampling: Option<crate::Sampling>,
228 flags: DecorationFlags,
229}
230
231impl Decoration {
232 fn debug_name(&self) -> &str {
233 match self.name {
234 Some(ref name) => name.as_str(),
235 None => "?",
236 }
237 }
238
239 const fn resource_binding(&self) -> Option<crate::ResourceBinding> {
240 match *self {
241 Decoration {
242 desc_set: Some(group),
243 desc_index: Some(binding),
244 ..
245 } => Some(crate::ResourceBinding { group, binding }),
246 _ => None,
247 }
248 }
249
250 fn io_binding(&self) -> Result<crate::Binding, Error> {
251 match *self {
252 Decoration {
253 built_in: Some(built_in),
254 location: None,
255 invariant,
256 ..
257 } => Ok(crate::Binding::BuiltIn(map_builtin(built_in, invariant)?)),
258 Decoration {
259 built_in: None,
260 location: Some(location),
261 interpolation,
262 sampling,
263 ..
264 } => Ok(crate::Binding::Location {
265 location,
266 interpolation,
267 sampling,
268 blend_src: None,
269 per_primitive: false,
270 }),
271 _ => Err(Error::MissingDecoration(spirv::Decoration::Location)),
272 }
273 }
274}
275
276#[derive(Debug)]
277struct LookupFunctionType {
278 parameter_type_ids: Vec<spirv::Word>,
279 return_type_id: spirv::Word,
280}
281
282struct LookupFunction {
283 handle: Handle<crate::Function>,
284 parameters_sampling: Vec<image::SamplingFlags>,
285}
286
287#[derive(Debug)]
288struct EntryPoint {
289 stage: crate::ShaderStage,
290 name: String,
291 early_depth_test: Option<crate::EarlyDepthTest>,
292 workgroup_size: [u32; 3],
293 variable_ids: Vec<spirv::Word>,
294}
295
296#[derive(Clone, Debug)]
297struct LookupType {
298 handle: Handle<crate::Type>,
299 base_id: Option<spirv::Word>,
300}
301
302#[derive(Debug)]
303enum Constant {
304 Constant(Handle<crate::Constant>),
305 Override(Handle<crate::Override>),
306}
307
308impl Constant {
309 const fn to_expr(&self) -> crate::Expression {
310 match *self {
311 Self::Constant(c) => crate::Expression::Constant(c),
312 Self::Override(o) => crate::Expression::Override(o),
313 }
314 }
315}
316
317#[derive(Debug)]
318struct LookupConstant {
319 inner: Constant,
320 type_id: spirv::Word,
321}
322
323#[derive(Debug)]
324enum Variable {
325 Global,
326 Input(crate::FunctionArgument),
327 Output(crate::FunctionResult),
328}
329
330#[derive(Debug)]
331struct LookupVariable {
332 inner: Variable,
333 handle: Handle<crate::GlobalVariable>,
334 type_id: spirv::Word,
335}
336
337#[derive(Clone, Debug)]
339struct LookupExpression {
340 handle: Handle<crate::Expression>,
347
348 type_id: spirv::Word,
350
351 block_id: spirv::Word,
356}
357
358#[derive(Debug)]
359struct LookupMember {
360 type_id: spirv::Word,
361 row_major: bool,
363}
364
365#[derive(Clone, Debug)]
366enum LookupLoadOverride {
367 Pending,
369 Loaded(Handle<crate::Expression>),
371}
372
373#[derive(PartialEq)]
374enum ExtendedClass {
375 Global(crate::AddressSpace),
376 Input,
377 Output,
378}
379
380#[derive(Clone, Debug)]
381pub struct Options {
382 pub adjust_coordinate_space: bool,
386 pub strict_capabilities: bool,
388 pub block_ctx_dump_prefix: Option<String>,
389}
390
391impl Default for Options {
392 fn default() -> Self {
393 Options {
394 adjust_coordinate_space: true,
395 strict_capabilities: true,
396 block_ctx_dump_prefix: None,
397 }
398 }
399}
400
401type BodyIndex = usize;
403
404#[derive(Debug)]
413enum BodyFragment {
414 BlockId(spirv::Word),
415 If {
416 condition: Handle<crate::Expression>,
417 accept: BodyIndex,
418 reject: BodyIndex,
419 },
420 Loop {
421 body: BodyIndex,
424
425 continuing: BodyIndex,
428
429 break_if: Option<Handle<crate::Expression>>,
433 },
434 Switch {
435 selector: Handle<crate::Expression>,
436 cases: Vec<(i32, BodyIndex)>,
437 default: BodyIndex,
438 },
439 Break,
440 Continue,
441}
442
443#[derive(Debug)]
450struct Body {
451 parent: usize,
453 data: Vec<BodyFragment>,
454}
455
456impl Body {
457 pub const fn with_parent(parent: usize) -> Self {
459 Body {
460 parent,
461 data: Vec::new(),
462 }
463 }
464}
465
466#[derive(Debug)]
467struct PhiExpression {
468 local: Handle<crate::LocalVariable>,
470 expressions: Vec<(spirv::Word, spirv::Word)>,
472}
473
474#[derive(Copy, Clone, Debug, PartialEq, Eq)]
475enum MergeBlockInformation {
476 LoopMerge,
477 LoopContinue,
478 SelectionMerge,
479 SwitchMerge,
480}
481
482#[derive(Debug)]
523struct BlockContext<'function> {
524 phis: Vec<PhiExpression>,
527
528 blocks: FastHashMap<spirv::Word, crate::Block>,
535
536 body_for_label: FastHashMap<spirv::Word, BodyIndex>,
554
555 mergers: FastHashMap<spirv::Word, MergeBlockInformation>,
557
558 bodies: Vec<Body>,
562
563 module: &'function mut crate::Module,
565
566 function_id: spirv::Word,
568 expressions: &'function mut Arena<crate::Expression>,
570 local_arena: &'function mut Arena<crate::LocalVariable>,
572 arguments: &'function [crate::FunctionArgument],
574 parameter_sampling: &'function mut [image::SamplingFlags],
576}
577
578enum SignAnchor {
579 Result,
580 Operand,
581}
582
583pub struct Frontend<I> {
584 data: I,
585 data_offset: usize,
586 state: ModuleState,
587 layouter: Layouter,
588 temp_bytes: Vec<u8>,
589 ext_glsl_id: Option<spirv::Word>,
590 future_decor: FastHashMap<spirv::Word, Decoration>,
591 future_member_decor: FastHashMap<(spirv::Word, MemberIndex), Decoration>,
592 lookup_member: FastHashMap<(Handle<crate::Type>, MemberIndex), LookupMember>,
593 handle_sampling: FastHashMap<Handle<crate::GlobalVariable>, image::SamplingFlags>,
594
595 upgrade_atomics: Upgrades,
600
601 lookup_type: FastHashMap<spirv::Word, LookupType>,
602 lookup_void_type: Option<spirv::Word>,
603 lookup_storage_buffer_types: FastHashMap<Handle<crate::Type>, crate::StorageAccess>,
604 lookup_constant: FastHashMap<spirv::Word, LookupConstant>,
605 lookup_variable: FastHashMap<spirv::Word, LookupVariable>,
606 lookup_expression: FastHashMap<spirv::Word, LookupExpression>,
607 lookup_load_override: FastHashMap<spirv::Word, LookupLoadOverride>,
609 lookup_sampled_image: FastHashMap<spirv::Word, image::LookupSampledImage>,
610 lookup_function_type: FastHashMap<spirv::Word, LookupFunctionType>,
611 lookup_function: FastHashMap<spirv::Word, LookupFunction>,
612 lookup_entry_point: FastHashMap<spirv::Word, EntryPoint>,
613 deferred_entry_points: Vec<(EntryPoint, spirv::Word)>,
616 deferred_function_calls: Vec<spirv::Word>,
619 dummy_functions: Arena<crate::Function>,
620 function_call_graph: GraphMap<
624 spirv::Word,
625 (),
626 petgraph::Directed,
627 core::hash::BuildHasherDefault<rustc_hash::FxHasher>,
628 >,
629 options: Options,
630
631 switch_cases: FastIndexMap<spirv::Word, (BodyIndex, Vec<i32>)>,
636
637 gl_per_vertex_builtin_access: FastHashSet<crate::BuiltIn>,
646}
647
648impl<I: Iterator<Item = u32>> Frontend<I> {
649 pub fn new(data: I, options: &Options) -> Self {
650 Frontend {
651 data,
652 data_offset: 0,
653 state: ModuleState::Empty,
654 layouter: Layouter::default(),
655 temp_bytes: Vec::new(),
656 ext_glsl_id: None,
657 future_decor: FastHashMap::default(),
658 future_member_decor: FastHashMap::default(),
659 handle_sampling: FastHashMap::default(),
660 lookup_member: FastHashMap::default(),
661 upgrade_atomics: Default::default(),
662 lookup_type: FastHashMap::default(),
663 lookup_void_type: None,
664 lookup_storage_buffer_types: FastHashMap::default(),
665 lookup_constant: FastHashMap::default(),
666 lookup_variable: FastHashMap::default(),
667 lookup_expression: FastHashMap::default(),
668 lookup_load_override: FastHashMap::default(),
669 lookup_sampled_image: FastHashMap::default(),
670 lookup_function_type: FastHashMap::default(),
671 lookup_function: FastHashMap::default(),
672 lookup_entry_point: FastHashMap::default(),
673 deferred_entry_points: Vec::default(),
674 deferred_function_calls: Vec::default(),
675 dummy_functions: Arena::new(),
676 function_call_graph: GraphMap::new(),
677 options: options.clone(),
678 switch_cases: FastIndexMap::default(),
679 gl_per_vertex_builtin_access: FastHashSet::default(),
680 }
681 }
682
683 fn span_from(&self, from: usize) -> crate::Span {
684 crate::Span::from(from..self.data_offset)
685 }
686
687 fn span_from_with_op(&self, from: usize) -> crate::Span {
688 crate::Span::from((from - 4)..self.data_offset)
689 }
690
691 fn next(&mut self) -> Result<u32, Error> {
692 if let Some(res) = self.data.next() {
693 self.data_offset += 4;
694 Ok(res)
695 } else {
696 Err(Error::IncompleteData)
697 }
698 }
699
700 fn next_inst(&mut self) -> Result<Instruction, Error> {
701 let word = self.next()?;
702 let (wc, opcode) = ((word >> 16) as u16, (word & 0xffff) as u16);
703 if wc == 0 {
704 return Err(Error::InvalidWordCount);
705 }
706 let op = spirv::Op::from_u32(opcode as u32).ok_or(Error::UnknownInstruction(opcode))?;
707
708 Ok(Instruction { op, wc })
709 }
710
711 fn next_string(&mut self, mut count: u16) -> Result<(String, u16), Error> {
712 self.temp_bytes.clear();
713 loop {
714 if count == 0 {
715 return Err(Error::BadString);
716 }
717 count -= 1;
718 let chars = self.next()?.to_le_bytes();
719 let pos = chars.iter().position(|&c| c == 0).unwrap_or(4);
720 self.temp_bytes.extend_from_slice(&chars[..pos]);
721 if pos < 4 {
722 break;
723 }
724 }
725 core::str::from_utf8(&self.temp_bytes)
726 .map(|s| (s.to_owned(), count))
727 .map_err(|_| Error::BadString)
728 }
729
730 fn next_decoration(
731 &mut self,
732 inst: Instruction,
733 base_words: u16,
734 dec: &mut Decoration,
735 ) -> Result<(), Error> {
736 let raw = self.next()?;
737 let dec_typed = spirv::Decoration::from_u32(raw).ok_or(Error::InvalidDecoration(raw))?;
738 log::trace!("\t\t{}: {:?}", dec.debug_name(), dec_typed);
739 match dec_typed {
740 spirv::Decoration::BuiltIn => {
741 inst.expect(base_words + 2)?;
742 dec.built_in = Some(self.next()?);
743 }
744 spirv::Decoration::Location => {
745 inst.expect(base_words + 2)?;
746 dec.location = Some(self.next()?);
747 }
748 spirv::Decoration::DescriptorSet => {
749 inst.expect(base_words + 2)?;
750 dec.desc_set = Some(self.next()?);
751 }
752 spirv::Decoration::Binding => {
753 inst.expect(base_words + 2)?;
754 dec.desc_index = Some(self.next()?);
755 }
756 spirv::Decoration::BufferBlock => {
757 dec.storage_buffer = true;
758 }
759 spirv::Decoration::Offset => {
760 inst.expect(base_words + 2)?;
761 dec.offset = Some(self.next()?);
762 }
763 spirv::Decoration::ArrayStride => {
764 inst.expect(base_words + 2)?;
765 dec.array_stride = NonZeroU32::new(self.next()?);
766 }
767 spirv::Decoration::MatrixStride => {
768 inst.expect(base_words + 2)?;
769 dec.matrix_stride = NonZeroU32::new(self.next()?);
770 }
771 spirv::Decoration::Invariant => {
772 dec.invariant = true;
773 }
774 spirv::Decoration::NoPerspective => {
775 dec.interpolation = Some(crate::Interpolation::Linear);
776 }
777 spirv::Decoration::Flat => {
778 dec.interpolation = Some(crate::Interpolation::Flat);
779 }
780 spirv::Decoration::Centroid => {
781 dec.sampling = Some(crate::Sampling::Centroid);
782 }
783 spirv::Decoration::Sample => {
784 dec.sampling = Some(crate::Sampling::Sample);
785 }
786 spirv::Decoration::NonReadable => {
787 dec.flags |= DecorationFlags::NON_READABLE;
788 }
789 spirv::Decoration::NonWritable => {
790 dec.flags |= DecorationFlags::NON_WRITABLE;
791 }
792 spirv::Decoration::ColMajor => {
793 dec.matrix_major = Some(Majority::Column);
794 }
795 spirv::Decoration::RowMajor => {
796 dec.matrix_major = Some(Majority::Row);
797 }
798 spirv::Decoration::SpecId => {
799 dec.specialization_constant_id = Some(self.next()?);
800 }
801 other => {
802 log::warn!("Unknown decoration {other:?}");
803 for _ in base_words + 1..inst.wc {
804 let _var = self.next()?;
805 }
806 }
807 }
808 Ok(())
809 }
810
811 fn get_expr_handle(
882 &self,
883 id: spirv::Word,
884 lookup: &LookupExpression,
885 ctx: &mut BlockContext,
886 emitter: &mut crate::proc::Emitter,
887 block: &mut crate::Block,
888 body_idx: BodyIndex,
889 ) -> Handle<crate::Expression> {
890 let expr_body_idx = ctx
892 .body_for_label
893 .get(&lookup.block_id)
894 .copied()
895 .unwrap_or(0);
896
897 if is_parent(body_idx, expr_body_idx, ctx) {
904 lookup.handle
905 } else {
906 let ty = self.lookup_type[&lookup.type_id].handle;
909 let local = ctx.local_arena.append(
910 crate::LocalVariable {
911 name: None,
912 ty,
913 init: None,
914 },
915 crate::Span::default(),
916 );
917
918 block.extend(emitter.finish(ctx.expressions));
919 let pointer = ctx.expressions.append(
920 crate::Expression::LocalVariable(local),
921 crate::Span::default(),
922 );
923 emitter.start(ctx.expressions);
924 let expr = ctx
925 .expressions
926 .append(crate::Expression::Load { pointer }, crate::Span::default());
927
928 ctx.phis.push(PhiExpression {
937 local,
938 expressions: vec![(id, lookup.block_id)],
939 });
940
941 expr
942 }
943 }
944
945 fn parse_expr_unary_op(
946 &mut self,
947 ctx: &mut BlockContext,
948 emitter: &mut crate::proc::Emitter,
949 block: &mut crate::Block,
950 block_id: spirv::Word,
951 body_idx: usize,
952 op: crate::UnaryOperator,
953 ) -> Result<(), Error> {
954 let start = self.data_offset;
955 let result_type_id = self.next()?;
956 let result_id = self.next()?;
957 let p_id = self.next()?;
958
959 let p_lexp = self.lookup_expression.lookup(p_id)?;
960 let handle = self.get_expr_handle(p_id, p_lexp, ctx, emitter, block, body_idx);
961
962 let expr = crate::Expression::Unary { op, expr: handle };
963 self.lookup_expression.insert(
964 result_id,
965 LookupExpression {
966 handle: ctx.expressions.append(expr, self.span_from_with_op(start)),
967 type_id: result_type_id,
968 block_id,
969 },
970 );
971 Ok(())
972 }
973
974 fn parse_expr_binary_op(
975 &mut self,
976 ctx: &mut BlockContext,
977 emitter: &mut crate::proc::Emitter,
978 block: &mut crate::Block,
979 block_id: spirv::Word,
980 body_idx: usize,
981 op: crate::BinaryOperator,
982 ) -> Result<(), Error> {
983 let start = self.data_offset;
984 let result_type_id = self.next()?;
985 let result_id = self.next()?;
986 let p1_id = self.next()?;
987 let p2_id = self.next()?;
988
989 let p1_lexp = self.lookup_expression.lookup(p1_id)?;
990 let left = self.get_expr_handle(p1_id, p1_lexp, ctx, emitter, block, body_idx);
991 let p2_lexp = self.lookup_expression.lookup(p2_id)?;
992 let right = self.get_expr_handle(p2_id, p2_lexp, ctx, emitter, block, body_idx);
993
994 let expr = crate::Expression::Binary { op, left, right };
995 self.lookup_expression.insert(
996 result_id,
997 LookupExpression {
998 handle: ctx.expressions.append(expr, self.span_from_with_op(start)),
999 type_id: result_type_id,
1000 block_id,
1001 },
1002 );
1003 Ok(())
1004 }
1005
1006 fn parse_expr_unary_op_sign_adjusted(
1009 &mut self,
1010 ctx: &mut BlockContext,
1011 emitter: &mut crate::proc::Emitter,
1012 block: &mut crate::Block,
1013 block_id: spirv::Word,
1014 body_idx: usize,
1015 op: crate::UnaryOperator,
1016 ) -> Result<(), Error> {
1017 let start = self.data_offset;
1018 let result_type_id = self.next()?;
1019 let result_id = self.next()?;
1020 let p1_id = self.next()?;
1021 let span = self.span_from_with_op(start);
1022
1023 let p1_lexp = self.lookup_expression.lookup(p1_id)?;
1024 let left = self.get_expr_handle(p1_id, p1_lexp, ctx, emitter, block, body_idx);
1025
1026 let result_lookup_ty = self.lookup_type.lookup(result_type_id)?;
1027 let kind = ctx.module.types[result_lookup_ty.handle]
1028 .inner
1029 .scalar_kind()
1030 .unwrap();
1031
1032 let expr = crate::Expression::Unary {
1033 op,
1034 expr: if p1_lexp.type_id == result_type_id {
1035 left
1036 } else {
1037 ctx.expressions.append(
1038 crate::Expression::As {
1039 expr: left,
1040 kind,
1041 convert: None,
1042 },
1043 span,
1044 )
1045 },
1046 };
1047
1048 self.lookup_expression.insert(
1049 result_id,
1050 LookupExpression {
1051 handle: ctx.expressions.append(expr, span),
1052 type_id: result_type_id,
1053 block_id,
1054 },
1055 );
1056 Ok(())
1057 }
1058
1059 #[allow(clippy::too_many_arguments)]
1063 fn parse_expr_binary_op_sign_adjusted(
1064 &mut self,
1065 ctx: &mut BlockContext,
1066 emitter: &mut crate::proc::Emitter,
1067 block: &mut crate::Block,
1068 block_id: spirv::Word,
1069 body_idx: usize,
1070 op: crate::BinaryOperator,
1071 anchor: SignAnchor,
1075 ) -> Result<(), Error> {
1076 let start = self.data_offset;
1077 let result_type_id = self.next()?;
1078 let result_id = self.next()?;
1079 let p1_id = self.next()?;
1080 let p2_id = self.next()?;
1081 let span = self.span_from_with_op(start);
1082
1083 let p1_lexp = self.lookup_expression.lookup(p1_id)?;
1084 let left = self.get_expr_handle(p1_id, p1_lexp, ctx, emitter, block, body_idx);
1085 let p2_lexp = self.lookup_expression.lookup(p2_id)?;
1086 let right = self.get_expr_handle(p2_id, p2_lexp, ctx, emitter, block, body_idx);
1087
1088 let expected_type_id = match anchor {
1089 SignAnchor::Result => result_type_id,
1090 SignAnchor::Operand => p1_lexp.type_id,
1091 };
1092 let expected_lookup_ty = self.lookup_type.lookup(expected_type_id)?;
1093 let kind = ctx.module.types[expected_lookup_ty.handle]
1094 .inner
1095 .scalar_kind()
1096 .unwrap();
1097
1098 let expr = crate::Expression::Binary {
1099 op,
1100 left: if p1_lexp.type_id == expected_type_id {
1101 left
1102 } else {
1103 ctx.expressions.append(
1104 crate::Expression::As {
1105 expr: left,
1106 kind,
1107 convert: None,
1108 },
1109 span,
1110 )
1111 },
1112 right: if p2_lexp.type_id == expected_type_id {
1113 right
1114 } else {
1115 ctx.expressions.append(
1116 crate::Expression::As {
1117 expr: right,
1118 kind,
1119 convert: None,
1120 },
1121 span,
1122 )
1123 },
1124 };
1125
1126 self.lookup_expression.insert(
1127 result_id,
1128 LookupExpression {
1129 handle: ctx.expressions.append(expr, span),
1130 type_id: result_type_id,
1131 block_id,
1132 },
1133 );
1134 Ok(())
1135 }
1136
1137 #[allow(clippy::too_many_arguments)]
1141 fn parse_expr_int_comparison(
1142 &mut self,
1143 ctx: &mut BlockContext,
1144 emitter: &mut crate::proc::Emitter,
1145 block: &mut crate::Block,
1146 block_id: spirv::Word,
1147 body_idx: usize,
1148 op: crate::BinaryOperator,
1149 kind: crate::ScalarKind,
1150 ) -> Result<(), Error> {
1151 let start = self.data_offset;
1152 let result_type_id = self.next()?;
1153 let result_id = self.next()?;
1154 let p1_id = self.next()?;
1155 let p2_id = self.next()?;
1156 let span = self.span_from_with_op(start);
1157
1158 let p1_lexp = self.lookup_expression.lookup(p1_id)?;
1159 let left = self.get_expr_handle(p1_id, p1_lexp, ctx, emitter, block, body_idx);
1160 let p1_lookup_ty = self.lookup_type.lookup(p1_lexp.type_id)?;
1161 let p1_kind = ctx.module.types[p1_lookup_ty.handle]
1162 .inner
1163 .scalar_kind()
1164 .unwrap();
1165 let p2_lexp = self.lookup_expression.lookup(p2_id)?;
1166 let right = self.get_expr_handle(p2_id, p2_lexp, ctx, emitter, block, body_idx);
1167 let p2_lookup_ty = self.lookup_type.lookup(p2_lexp.type_id)?;
1168 let p2_kind = ctx.module.types[p2_lookup_ty.handle]
1169 .inner
1170 .scalar_kind()
1171 .unwrap();
1172
1173 let expr = crate::Expression::Binary {
1174 op,
1175 left: if p1_kind == kind {
1176 left
1177 } else {
1178 ctx.expressions.append(
1179 crate::Expression::As {
1180 expr: left,
1181 kind,
1182 convert: None,
1183 },
1184 span,
1185 )
1186 },
1187 right: if p2_kind == kind {
1188 right
1189 } else {
1190 ctx.expressions.append(
1191 crate::Expression::As {
1192 expr: right,
1193 kind,
1194 convert: None,
1195 },
1196 span,
1197 )
1198 },
1199 };
1200
1201 self.lookup_expression.insert(
1202 result_id,
1203 LookupExpression {
1204 handle: ctx.expressions.append(expr, span),
1205 type_id: result_type_id,
1206 block_id,
1207 },
1208 );
1209 Ok(())
1210 }
1211
1212 fn parse_expr_shift_op(
1213 &mut self,
1214 ctx: &mut BlockContext,
1215 emitter: &mut crate::proc::Emitter,
1216 block: &mut crate::Block,
1217 block_id: spirv::Word,
1218 body_idx: usize,
1219 op: crate::BinaryOperator,
1220 ) -> Result<(), Error> {
1221 let start = self.data_offset;
1222 let result_type_id = self.next()?;
1223 let result_id = self.next()?;
1224 let p1_id = self.next()?;
1225 let p2_id = self.next()?;
1226
1227 let span = self.span_from_with_op(start);
1228
1229 let p1_lexp = self.lookup_expression.lookup(p1_id)?;
1230 let left = self.get_expr_handle(p1_id, p1_lexp, ctx, emitter, block, body_idx);
1231 let p2_lexp = self.lookup_expression.lookup(p2_id)?;
1232 let p2_handle = self.get_expr_handle(p2_id, p2_lexp, ctx, emitter, block, body_idx);
1233 let right = ctx.expressions.append(
1235 crate::Expression::As {
1236 expr: p2_handle,
1237 kind: crate::ScalarKind::Uint,
1238 convert: None,
1239 },
1240 span,
1241 );
1242
1243 let expr = crate::Expression::Binary { op, left, right };
1244 self.lookup_expression.insert(
1245 result_id,
1246 LookupExpression {
1247 handle: ctx.expressions.append(expr, span),
1248 type_id: result_type_id,
1249 block_id,
1250 },
1251 );
1252 Ok(())
1253 }
1254
1255 fn parse_expr_derivative(
1256 &mut self,
1257 ctx: &mut BlockContext,
1258 emitter: &mut crate::proc::Emitter,
1259 block: &mut crate::Block,
1260 block_id: spirv::Word,
1261 body_idx: usize,
1262 (axis, ctrl): (crate::DerivativeAxis, crate::DerivativeControl),
1263 ) -> Result<(), Error> {
1264 let start = self.data_offset;
1265 let result_type_id = self.next()?;
1266 let result_id = self.next()?;
1267 let arg_id = self.next()?;
1268
1269 let arg_lexp = self.lookup_expression.lookup(arg_id)?;
1270 let arg_handle = self.get_expr_handle(arg_id, arg_lexp, ctx, emitter, block, body_idx);
1271
1272 let expr = crate::Expression::Derivative {
1273 axis,
1274 ctrl,
1275 expr: arg_handle,
1276 };
1277 self.lookup_expression.insert(
1278 result_id,
1279 LookupExpression {
1280 handle: ctx.expressions.append(expr, self.span_from_with_op(start)),
1281 type_id: result_type_id,
1282 block_id,
1283 },
1284 );
1285 Ok(())
1286 }
1287
1288 #[allow(clippy::too_many_arguments)]
1289 fn insert_composite(
1290 &self,
1291 root_expr: Handle<crate::Expression>,
1292 root_type_id: spirv::Word,
1293 object_expr: Handle<crate::Expression>,
1294 selections: &[spirv::Word],
1295 type_arena: &UniqueArena<crate::Type>,
1296 expressions: &mut Arena<crate::Expression>,
1297 span: crate::Span,
1298 ) -> Result<Handle<crate::Expression>, Error> {
1299 let selection = match selections.first() {
1300 Some(&index) => index,
1301 None => return Ok(object_expr),
1302 };
1303 let root_span = expressions.get_span(root_expr);
1304 let root_lookup = self.lookup_type.lookup(root_type_id)?;
1305
1306 let (count, child_type_id) = match type_arena[root_lookup.handle].inner {
1307 crate::TypeInner::Struct { ref members, .. } => {
1308 let child_member = self
1309 .lookup_member
1310 .get(&(root_lookup.handle, selection))
1311 .ok_or(Error::InvalidAccessType(root_type_id))?;
1312 (members.len(), child_member.type_id)
1313 }
1314 crate::TypeInner::Array { size, .. } => {
1315 let size = match size {
1316 crate::ArraySize::Constant(size) => size.get(),
1317 crate::ArraySize::Pending(_) => {
1318 unreachable!();
1319 }
1320 crate::ArraySize::Dynamic => {
1322 return Err(Error::InvalidAccessType(root_type_id))
1323 }
1324 };
1325
1326 let child_type_id = root_lookup
1327 .base_id
1328 .ok_or(Error::InvalidAccessType(root_type_id))?;
1329
1330 (size as usize, child_type_id)
1331 }
1332 crate::TypeInner::Vector { size, .. }
1333 | crate::TypeInner::Matrix { columns: size, .. } => {
1334 let child_type_id = root_lookup
1335 .base_id
1336 .ok_or(Error::InvalidAccessType(root_type_id))?;
1337 (size as usize, child_type_id)
1338 }
1339 _ => return Err(Error::InvalidAccessType(root_type_id)),
1340 };
1341
1342 let mut components = Vec::with_capacity(count);
1343 for index in 0..count as u32 {
1344 let expr = expressions.append(
1345 crate::Expression::AccessIndex {
1346 base: root_expr,
1347 index,
1348 },
1349 if index == selection { span } else { root_span },
1350 );
1351 components.push(expr);
1352 }
1353 components[selection as usize] = self.insert_composite(
1354 components[selection as usize],
1355 child_type_id,
1356 object_expr,
1357 &selections[1..],
1358 type_arena,
1359 expressions,
1360 span,
1361 )?;
1362
1363 Ok(expressions.append(
1364 crate::Expression::Compose {
1365 ty: root_lookup.handle,
1366 components,
1367 },
1368 span,
1369 ))
1370 }
1371
1372 fn get_exp_and_base_ty_handles(
1386 &self,
1387 pointer_id: spirv::Word,
1388 ctx: &mut BlockContext,
1389 emitter: &mut crate::proc::Emitter,
1390 block: &mut crate::Block,
1391 body_idx: usize,
1392 ) -> Result<(Handle<crate::Expression>, Handle<crate::Type>), Error> {
1393 log::trace!("\t\t\tlooking up pointer expr {pointer_id:?}");
1394 let p_lexp_handle;
1395 let p_lexp_ty_id;
1396 {
1397 let lexp = self.lookup_expression.lookup(pointer_id)?;
1398 p_lexp_handle = self.get_expr_handle(pointer_id, lexp, ctx, emitter, block, body_idx);
1399 p_lexp_ty_id = lexp.type_id;
1400 };
1401
1402 log::trace!("\t\t\tlooking up pointer type {pointer_id:?}");
1403 let p_ty = self.lookup_type.lookup(p_lexp_ty_id)?;
1404 let p_ty_base_id = p_ty.base_id.ok_or(Error::InvalidAccessType(p_lexp_ty_id))?;
1405
1406 log::trace!("\t\t\tlooking up pointer base type {p_ty_base_id:?} of {p_ty:?}");
1407 let p_base_ty = self.lookup_type.lookup(p_ty_base_id)?;
1408
1409 Ok((p_lexp_handle, p_base_ty.handle))
1410 }
1411
1412 #[allow(clippy::too_many_arguments)]
1413 fn parse_atomic_expr_with_value(
1414 &mut self,
1415 inst: Instruction,
1416 emitter: &mut crate::proc::Emitter,
1417 ctx: &mut BlockContext,
1418 block: &mut crate::Block,
1419 block_id: spirv::Word,
1420 body_idx: usize,
1421 atomic_function: crate::AtomicFunction,
1422 ) -> Result<(), Error> {
1423 inst.expect(7)?;
1424 let start = self.data_offset;
1425 let result_type_id = self.next()?;
1426 let result_id = self.next()?;
1427 let pointer_id = self.next()?;
1428 let _scope_id = self.next()?;
1429 let _memory_semantics_id = self.next()?;
1430 let value_id = self.next()?;
1431 let span = self.span_from_with_op(start);
1432
1433 let (p_lexp_handle, p_base_ty_handle) =
1434 self.get_exp_and_base_ty_handles(pointer_id, ctx, emitter, block, body_idx)?;
1435
1436 log::trace!("\t\t\tlooking up value expr {value_id:?}");
1437 let v_lexp_handle = self.lookup_expression.lookup(value_id)?.handle;
1438
1439 block.extend(emitter.finish(ctx.expressions));
1440 let r_lexp_handle = {
1442 let expr = crate::Expression::AtomicResult {
1443 ty: p_base_ty_handle,
1444 comparison: false,
1445 };
1446 let handle = ctx.expressions.append(expr, span);
1447 self.lookup_expression.insert(
1448 result_id,
1449 LookupExpression {
1450 handle,
1451 type_id: result_type_id,
1452 block_id,
1453 },
1454 );
1455 handle
1456 };
1457 emitter.start(ctx.expressions);
1458
1459 let stmt = crate::Statement::Atomic {
1461 pointer: p_lexp_handle,
1462 fun: atomic_function,
1463 value: v_lexp_handle,
1464 result: Some(r_lexp_handle),
1465 };
1466 block.push(stmt, span);
1467
1468 self.record_atomic_access(ctx, p_lexp_handle)?;
1470
1471 Ok(())
1472 }
1473
1474 fn next_block(&mut self, block_id: spirv::Word, ctx: &mut BlockContext) -> Result<(), Error> {
1480 fn merger(body: &mut Body, target: &MergeBlockInformation) {
1482 body.data.push(match *target {
1483 MergeBlockInformation::LoopContinue => BodyFragment::Continue,
1484 MergeBlockInformation::LoopMerge | MergeBlockInformation::SwitchMerge => {
1485 BodyFragment::Break
1486 }
1487
1488 MergeBlockInformation::SelectionMerge => return,
1491 })
1492 }
1493
1494 let mut emitter = crate::proc::Emitter::default();
1495 emitter.start(ctx.expressions);
1496
1497 let mut body_idx = *ctx.body_for_label.entry(block_id).or_default();
1511
1512 let mut block = crate::Block::new();
1516
1517 let mut selection_merge_block = None;
1523
1524 macro_rules! get_expr_handle {
1525 ($id:expr, $lexp:expr) => {
1526 self.get_expr_handle($id, $lexp, ctx, &mut emitter, &mut block, body_idx)
1527 };
1528 }
1529 macro_rules! parse_expr_op {
1530 ($op:expr, BINARY) => {
1531 self.parse_expr_binary_op(ctx, &mut emitter, &mut block, block_id, body_idx, $op)
1532 };
1533
1534 ($op:expr, SHIFT) => {
1535 self.parse_expr_shift_op(ctx, &mut emitter, &mut block, block_id, body_idx, $op)
1536 };
1537 ($op:expr, UNARY) => {
1538 self.parse_expr_unary_op(ctx, &mut emitter, &mut block, block_id, body_idx, $op)
1539 };
1540 ($axis:expr, $ctrl:expr, DERIVATIVE) => {
1541 self.parse_expr_derivative(
1542 ctx,
1543 &mut emitter,
1544 &mut block,
1545 block_id,
1546 body_idx,
1547 ($axis, $ctrl),
1548 )
1549 };
1550 }
1551
1552 let terminator = loop {
1553 use spirv::Op;
1554 let start = self.data_offset;
1555 let inst = self.next_inst()?;
1556 let span = crate::Span::from(start..(start + 4 * (inst.wc as usize)));
1557 log::debug!("\t\t{:?} [{}]", inst.op, inst.wc);
1558
1559 match inst.op {
1560 Op::Line => {
1561 inst.expect(4)?;
1562 let _file_id = self.next()?;
1563 let _row_id = self.next()?;
1564 let _col_id = self.next()?;
1565 }
1566 Op::NoLine => inst.expect(1)?,
1567 Op::Undef => {
1568 inst.expect(3)?;
1569 let type_id = self.next()?;
1570 let id = self.next()?;
1571 let type_lookup = self.lookup_type.lookup(type_id)?;
1572 let ty = type_lookup.handle;
1573
1574 self.lookup_expression.insert(
1575 id,
1576 LookupExpression {
1577 handle: ctx
1578 .expressions
1579 .append(crate::Expression::ZeroValue(ty), span),
1580 type_id,
1581 block_id,
1582 },
1583 );
1584 }
1585 Op::Variable => {
1586 inst.expect_at_least(4)?;
1587 block.extend(emitter.finish(ctx.expressions));
1588
1589 let result_type_id = self.next()?;
1590 let result_id = self.next()?;
1591 let _storage_class = self.next()?;
1592 let init = if inst.wc > 4 {
1593 inst.expect(5)?;
1594 let init_id = self.next()?;
1595 let lconst = self.lookup_constant.lookup(init_id)?;
1596 Some(ctx.expressions.append(lconst.inner.to_expr(), span))
1597 } else {
1598 None
1599 };
1600
1601 let name = self
1602 .future_decor
1603 .remove(&result_id)
1604 .and_then(|decor| decor.name);
1605 if let Some(ref name) = name {
1606 log::debug!("\t\t\tid={result_id} name={name}");
1607 }
1608 let lookup_ty = self.lookup_type.lookup(result_type_id)?;
1609 let var_handle = ctx.local_arena.append(
1610 crate::LocalVariable {
1611 name,
1612 ty: match ctx.module.types[lookup_ty.handle].inner {
1613 crate::TypeInner::Pointer { base, .. } => base,
1614 _ => lookup_ty.handle,
1615 },
1616 init,
1617 },
1618 span,
1619 );
1620
1621 self.lookup_expression.insert(
1622 result_id,
1623 LookupExpression {
1624 handle: ctx
1625 .expressions
1626 .append(crate::Expression::LocalVariable(var_handle), span),
1627 type_id: result_type_id,
1628 block_id,
1629 },
1630 );
1631 emitter.start(ctx.expressions);
1632 }
1633 Op::Phi => {
1634 inst.expect_at_least(3)?;
1635 block.extend(emitter.finish(ctx.expressions));
1636
1637 let result_type_id = self.next()?;
1638 let result_id = self.next()?;
1639
1640 let name = format!("phi_{result_id}");
1641 let local = ctx.local_arena.append(
1642 crate::LocalVariable {
1643 name: Some(name),
1644 ty: self.lookup_type.lookup(result_type_id)?.handle,
1645 init: None,
1646 },
1647 self.span_from(start),
1648 );
1649 let pointer = ctx
1650 .expressions
1651 .append(crate::Expression::LocalVariable(local), span);
1652
1653 let in_count = (inst.wc - 3) / 2;
1654 let mut phi = PhiExpression {
1655 local,
1656 expressions: Vec::with_capacity(in_count as usize),
1657 };
1658 for _ in 0..in_count {
1659 let expr = self.next()?;
1660 let block = self.next()?;
1661 phi.expressions.push((expr, block));
1662 }
1663
1664 ctx.phis.push(phi);
1665 emitter.start(ctx.expressions);
1666
1667 self.lookup_expression.insert(
1670 result_id,
1671 LookupExpression {
1672 handle: ctx
1673 .expressions
1674 .append(crate::Expression::Load { pointer }, span),
1675 type_id: result_type_id,
1676 block_id,
1677 },
1678 );
1679 }
1680 Op::AccessChain | Op::InBoundsAccessChain => {
1681 struct AccessExpression {
1682 base_handle: Handle<crate::Expression>,
1683 type_id: spirv::Word,
1684 load_override: Option<LookupLoadOverride>,
1685 }
1686
1687 inst.expect_at_least(4)?;
1688
1689 let result_type_id = self.next()?;
1690 let result_id = self.next()?;
1691 let base_id = self.next()?;
1692 log::trace!("\t\t\tlooking up expr {base_id:?}");
1693
1694 let mut acex = {
1695 let lexp = self.lookup_expression.lookup(base_id)?;
1696 let lty = self.lookup_type.lookup(lexp.type_id)?;
1697
1698 let dereference = match ctx.module.types[lty.handle].inner {
1708 crate::TypeInner::BindingArray { .. } => false,
1709 _ => true,
1710 };
1711
1712 let type_id = if dereference {
1713 lty.base_id.ok_or(Error::InvalidAccessType(lexp.type_id))?
1714 } else {
1715 lexp.type_id
1716 };
1717
1718 AccessExpression {
1719 base_handle: get_expr_handle!(base_id, lexp),
1720 type_id,
1721 load_override: self.lookup_load_override.get(&base_id).cloned(),
1722 }
1723 };
1724
1725 for _ in 4..inst.wc {
1726 let access_id = self.next()?;
1727 log::trace!("\t\t\tlooking up index expr {access_id:?}");
1728 let index_expr = self.lookup_expression.lookup(access_id)?.clone();
1729 let index_expr_handle = get_expr_handle!(access_id, &index_expr);
1730 let index_expr_data = &ctx.expressions[index_expr.handle];
1731 let index_maybe = match *index_expr_data {
1732 crate::Expression::Constant(const_handle) => Some(
1733 ctx.gctx()
1734 .eval_expr_to_u32(ctx.module.constants[const_handle].init)
1735 .map_err(|_| {
1736 Error::InvalidAccess(crate::Expression::Constant(
1737 const_handle,
1738 ))
1739 })?,
1740 ),
1741 _ => None,
1742 };
1743
1744 log::trace!("\t\t\tlooking up type {:?}", acex.type_id);
1745 let type_lookup = self.lookup_type.lookup(acex.type_id)?;
1746 let ty = &ctx.module.types[type_lookup.handle];
1747 acex = match ty.inner {
1748 crate::TypeInner::Struct { ref members, .. } => {
1750 let index = index_maybe
1751 .ok_or_else(|| Error::InvalidAccess(index_expr_data.clone()))?;
1752
1753 let lookup_member = self
1754 .lookup_member
1755 .get(&(type_lookup.handle, index))
1756 .ok_or(Error::InvalidAccessType(acex.type_id))?;
1757 let base_handle = ctx.expressions.append(
1758 crate::Expression::AccessIndex {
1759 base: acex.base_handle,
1760 index,
1761 },
1762 span,
1763 );
1764
1765 if let Some(crate::Binding::BuiltIn(built_in)) =
1766 members[index as usize].binding
1767 {
1768 self.gl_per_vertex_builtin_access.insert(built_in);
1769 }
1770
1771 AccessExpression {
1772 base_handle,
1773 type_id: lookup_member.type_id,
1774 load_override: if lookup_member.row_major {
1775 debug_assert!(acex.load_override.is_none());
1776 let sub_type_lookup =
1777 self.lookup_type.lookup(lookup_member.type_id)?;
1778 Some(match ctx.module.types[sub_type_lookup.handle].inner {
1779 crate::TypeInner::Matrix { .. } => {
1781 let loaded = ctx.expressions.append(
1782 crate::Expression::Load {
1783 pointer: base_handle,
1784 },
1785 span,
1786 );
1787 let transposed = ctx.expressions.append(
1788 crate::Expression::Math {
1789 fun: crate::MathFunction::Transpose,
1790 arg: loaded,
1791 arg1: None,
1792 arg2: None,
1793 arg3: None,
1794 },
1795 span,
1796 );
1797 LookupLoadOverride::Loaded(transposed)
1798 }
1799 _ => LookupLoadOverride::Pending,
1800 })
1801 } else {
1802 None
1803 },
1804 }
1805 }
1806 crate::TypeInner::Matrix { .. } => {
1807 let load_override = match acex.load_override {
1808 Some(LookupLoadOverride::Loaded(load_expr)) => {
1810 let index = index_maybe.ok_or_else(|| {
1811 Error::InvalidAccess(index_expr_data.clone())
1812 })?;
1813 let sub_handle = ctx.expressions.append(
1814 crate::Expression::AccessIndex {
1815 base: load_expr,
1816 index,
1817 },
1818 span,
1819 );
1820 Some(LookupLoadOverride::Loaded(sub_handle))
1821 }
1822 _ => None,
1823 };
1824 let sub_expr = match index_maybe {
1825 Some(index) => crate::Expression::AccessIndex {
1826 base: acex.base_handle,
1827 index,
1828 },
1829 None => crate::Expression::Access {
1830 base: acex.base_handle,
1831 index: index_expr_handle,
1832 },
1833 };
1834 AccessExpression {
1835 base_handle: ctx.expressions.append(sub_expr, span),
1836 type_id: type_lookup
1837 .base_id
1838 .ok_or(Error::InvalidAccessType(acex.type_id))?,
1839 load_override,
1840 }
1841 }
1842 _ => {
1844 let base_handle = ctx.expressions.append(
1845 crate::Expression::Access {
1846 base: acex.base_handle,
1847 index: index_expr_handle,
1848 },
1849 span,
1850 );
1851 let load_override = match acex.load_override {
1852 Some(lookup_load_override) => {
1855 let sub_expr = match lookup_load_override {
1856 LookupLoadOverride::Pending => {
1859 let loaded = ctx.expressions.append(
1860 crate::Expression::Load {
1861 pointer: base_handle,
1862 },
1863 span,
1864 );
1865 ctx.expressions.append(
1866 crate::Expression::Math {
1867 fun: crate::MathFunction::Transpose,
1868 arg: loaded,
1869 arg1: None,
1870 arg2: None,
1871 arg3: None,
1872 },
1873 span,
1874 )
1875 }
1876 LookupLoadOverride::Loaded(load_expr) => {
1878 ctx.expressions.append(
1879 crate::Expression::Access {
1880 base: load_expr,
1881 index: index_expr_handle,
1882 },
1883 span,
1884 )
1885 }
1886 };
1887 Some(LookupLoadOverride::Loaded(sub_expr))
1888 }
1889 None => None,
1890 };
1891 AccessExpression {
1892 base_handle,
1893 type_id: type_lookup
1894 .base_id
1895 .ok_or(Error::InvalidAccessType(acex.type_id))?,
1896 load_override,
1897 }
1898 }
1899 };
1900 }
1901
1902 if let Some(load_expr) = acex.load_override {
1903 self.lookup_load_override.insert(result_id, load_expr);
1904 }
1905 let lookup_expression = LookupExpression {
1906 handle: acex.base_handle,
1907 type_id: result_type_id,
1908 block_id,
1909 };
1910 self.lookup_expression.insert(result_id, lookup_expression);
1911 }
1912 Op::VectorExtractDynamic => {
1913 inst.expect(5)?;
1914
1915 let result_type_id = self.next()?;
1916 let id = self.next()?;
1917 let composite_id = self.next()?;
1918 let index_id = self.next()?;
1919
1920 let root_lexp = self.lookup_expression.lookup(composite_id)?;
1921 let root_handle = get_expr_handle!(composite_id, root_lexp);
1922 let root_type_lookup = self.lookup_type.lookup(root_lexp.type_id)?;
1923 let index_lexp = self.lookup_expression.lookup(index_id)?;
1924 let index_handle = get_expr_handle!(index_id, index_lexp);
1925 let index_type = self.lookup_type.lookup(index_lexp.type_id)?.handle;
1926
1927 let num_components = match ctx.module.types[root_type_lookup.handle].inner {
1928 crate::TypeInner::Vector { size, .. } => size as u32,
1929 _ => return Err(Error::InvalidVectorType(root_type_lookup.handle)),
1930 };
1931
1932 let mut make_index = |ctx: &mut BlockContext, index: u32| {
1933 make_index_literal(
1934 ctx,
1935 index,
1936 &mut block,
1937 &mut emitter,
1938 index_type,
1939 index_lexp.type_id,
1940 span,
1941 )
1942 };
1943
1944 let index_expr = make_index(ctx, 0)?;
1945 let mut handle = ctx.expressions.append(
1946 crate::Expression::Access {
1947 base: root_handle,
1948 index: index_expr,
1949 },
1950 span,
1951 );
1952 for index in 1..num_components {
1953 let index_expr = make_index(ctx, index)?;
1954 let access_expr = ctx.expressions.append(
1955 crate::Expression::Access {
1956 base: root_handle,
1957 index: index_expr,
1958 },
1959 span,
1960 );
1961 let cond = ctx.expressions.append(
1962 crate::Expression::Binary {
1963 op: crate::BinaryOperator::Equal,
1964 left: index_expr,
1965 right: index_handle,
1966 },
1967 span,
1968 );
1969 handle = ctx.expressions.append(
1970 crate::Expression::Select {
1971 condition: cond,
1972 accept: access_expr,
1973 reject: handle,
1974 },
1975 span,
1976 );
1977 }
1978
1979 self.lookup_expression.insert(
1980 id,
1981 LookupExpression {
1982 handle,
1983 type_id: result_type_id,
1984 block_id,
1985 },
1986 );
1987 }
1988 Op::VectorInsertDynamic => {
1989 inst.expect(6)?;
1990
1991 let result_type_id = self.next()?;
1992 let id = self.next()?;
1993 let composite_id = self.next()?;
1994 let object_id = self.next()?;
1995 let index_id = self.next()?;
1996
1997 let object_lexp = self.lookup_expression.lookup(object_id)?;
1998 let object_handle = get_expr_handle!(object_id, object_lexp);
1999 let root_lexp = self.lookup_expression.lookup(composite_id)?;
2000 let root_handle = get_expr_handle!(composite_id, root_lexp);
2001 let root_type_lookup = self.lookup_type.lookup(root_lexp.type_id)?;
2002 let index_lexp = self.lookup_expression.lookup(index_id)?;
2003 let index_handle = get_expr_handle!(index_id, index_lexp);
2004 let index_type = self.lookup_type.lookup(index_lexp.type_id)?.handle;
2005
2006 let num_components = match ctx.module.types[root_type_lookup.handle].inner {
2007 crate::TypeInner::Vector { size, .. } => size as u32,
2008 _ => return Err(Error::InvalidVectorType(root_type_lookup.handle)),
2009 };
2010
2011 let mut components = Vec::with_capacity(num_components as usize);
2012 for index in 0..num_components {
2013 let index_expr = make_index_literal(
2014 ctx,
2015 index,
2016 &mut block,
2017 &mut emitter,
2018 index_type,
2019 index_lexp.type_id,
2020 span,
2021 )?;
2022 let access_expr = ctx.expressions.append(
2023 crate::Expression::Access {
2024 base: root_handle,
2025 index: index_expr,
2026 },
2027 span,
2028 );
2029 let cond = ctx.expressions.append(
2030 crate::Expression::Binary {
2031 op: crate::BinaryOperator::Equal,
2032 left: index_expr,
2033 right: index_handle,
2034 },
2035 span,
2036 );
2037 let handle = ctx.expressions.append(
2038 crate::Expression::Select {
2039 condition: cond,
2040 accept: object_handle,
2041 reject: access_expr,
2042 },
2043 span,
2044 );
2045 components.push(handle);
2046 }
2047 let handle = ctx.expressions.append(
2048 crate::Expression::Compose {
2049 ty: root_type_lookup.handle,
2050 components,
2051 },
2052 span,
2053 );
2054
2055 self.lookup_expression.insert(
2056 id,
2057 LookupExpression {
2058 handle,
2059 type_id: result_type_id,
2060 block_id,
2061 },
2062 );
2063 }
2064 Op::CompositeExtract => {
2065 inst.expect_at_least(4)?;
2066
2067 let result_type_id = self.next()?;
2068 let result_id = self.next()?;
2069 let base_id = self.next()?;
2070 log::trace!("\t\t\tlooking up expr {base_id:?}");
2071 let mut lexp = self.lookup_expression.lookup(base_id)?.clone();
2072 lexp.handle = get_expr_handle!(base_id, &lexp);
2073 for _ in 4..inst.wc {
2074 let index = self.next()?;
2075 log::trace!("\t\t\tlooking up type {:?}", lexp.type_id);
2076 let type_lookup = self.lookup_type.lookup(lexp.type_id)?;
2077 let type_id = match ctx.module.types[type_lookup.handle].inner {
2078 crate::TypeInner::Struct { .. } => {
2079 self.lookup_member
2080 .get(&(type_lookup.handle, index))
2081 .ok_or(Error::InvalidAccessType(lexp.type_id))?
2082 .type_id
2083 }
2084 crate::TypeInner::Array { .. }
2085 | crate::TypeInner::Vector { .. }
2086 | crate::TypeInner::Matrix { .. } => type_lookup
2087 .base_id
2088 .ok_or(Error::InvalidAccessType(lexp.type_id))?,
2089 ref other => {
2090 log::warn!("composite type {other:?}");
2091 return Err(Error::UnsupportedType(type_lookup.handle));
2092 }
2093 };
2094 lexp = LookupExpression {
2095 handle: ctx.expressions.append(
2096 crate::Expression::AccessIndex {
2097 base: lexp.handle,
2098 index,
2099 },
2100 span,
2101 ),
2102 type_id,
2103 block_id,
2104 };
2105 }
2106
2107 self.lookup_expression.insert(
2108 result_id,
2109 LookupExpression {
2110 handle: lexp.handle,
2111 type_id: result_type_id,
2112 block_id,
2113 },
2114 );
2115 }
2116 Op::CompositeInsert => {
2117 inst.expect_at_least(5)?;
2118
2119 let result_type_id = self.next()?;
2120 let id = self.next()?;
2121 let object_id = self.next()?;
2122 let composite_id = self.next()?;
2123 let mut selections = Vec::with_capacity(inst.wc as usize - 5);
2124 for _ in 5..inst.wc {
2125 selections.push(self.next()?);
2126 }
2127
2128 let object_lexp = self.lookup_expression.lookup(object_id)?.clone();
2129 let object_handle = get_expr_handle!(object_id, &object_lexp);
2130 let root_lexp = self.lookup_expression.lookup(composite_id)?.clone();
2131 let root_handle = get_expr_handle!(composite_id, &root_lexp);
2132 let handle = self.insert_composite(
2133 root_handle,
2134 result_type_id,
2135 object_handle,
2136 &selections,
2137 &ctx.module.types,
2138 ctx.expressions,
2139 span,
2140 )?;
2141
2142 self.lookup_expression.insert(
2143 id,
2144 LookupExpression {
2145 handle,
2146 type_id: result_type_id,
2147 block_id,
2148 },
2149 );
2150 }
2151 Op::CompositeConstruct => {
2152 inst.expect_at_least(3)?;
2153
2154 let result_type_id = self.next()?;
2155 let id = self.next()?;
2156 let mut components = Vec::with_capacity(inst.wc as usize - 2);
2157 for _ in 3..inst.wc {
2158 let comp_id = self.next()?;
2159 log::trace!("\t\t\tlooking up expr {comp_id:?}");
2160 let lexp = self.lookup_expression.lookup(comp_id)?;
2161 let handle = get_expr_handle!(comp_id, lexp);
2162 components.push(handle);
2163 }
2164 let ty = self.lookup_type.lookup(result_type_id)?.handle;
2165 let first = components[0];
2166 let expr = match ctx.module.types[ty].inner {
2167 crate::TypeInner::Vector { size, .. }
2169 if components.len() == size as usize
2170 && components[1..].iter().all(|&c| c == first) =>
2171 {
2172 crate::Expression::Splat { size, value: first }
2173 }
2174 _ => crate::Expression::Compose { ty, components },
2175 };
2176 self.lookup_expression.insert(
2177 id,
2178 LookupExpression {
2179 handle: ctx.expressions.append(expr, span),
2180 type_id: result_type_id,
2181 block_id,
2182 },
2183 );
2184 }
2185 Op::Load => {
2186 inst.expect_at_least(4)?;
2187
2188 let result_type_id = self.next()?;
2189 let result_id = self.next()?;
2190 let pointer_id = self.next()?;
2191 if inst.wc != 4 {
2192 inst.expect(5)?;
2193 let _memory_access = self.next()?;
2194 }
2195
2196 let base_lexp = self.lookup_expression.lookup(pointer_id)?;
2197 let base_handle = get_expr_handle!(pointer_id, base_lexp);
2198 let type_lookup = self.lookup_type.lookup(base_lexp.type_id)?;
2199 let handle = match ctx.module.types[type_lookup.handle].inner {
2200 crate::TypeInner::Image { .. } | crate::TypeInner::Sampler { .. } => {
2201 base_handle
2202 }
2203 _ => match self.lookup_load_override.get(&pointer_id) {
2204 Some(&LookupLoadOverride::Loaded(handle)) => handle,
2205 _ => ctx.expressions.append(
2207 crate::Expression::Load {
2208 pointer: base_handle,
2209 },
2210 span,
2211 ),
2212 },
2213 };
2214
2215 self.lookup_expression.insert(
2216 result_id,
2217 LookupExpression {
2218 handle,
2219 type_id: result_type_id,
2220 block_id,
2221 },
2222 );
2223 }
2224 Op::Store => {
2225 inst.expect_at_least(3)?;
2226
2227 let pointer_id = self.next()?;
2228 let value_id = self.next()?;
2229 if inst.wc != 3 {
2230 inst.expect(4)?;
2231 let _memory_access = self.next()?;
2232 }
2233 let base_expr = self.lookup_expression.lookup(pointer_id)?;
2234 let base_handle = get_expr_handle!(pointer_id, base_expr);
2235 let value_expr = self.lookup_expression.lookup(value_id)?;
2236 let value_handle = get_expr_handle!(value_id, value_expr);
2237
2238 block.extend(emitter.finish(ctx.expressions));
2239 block.push(
2240 crate::Statement::Store {
2241 pointer: base_handle,
2242 value: value_handle,
2243 },
2244 span,
2245 );
2246 emitter.start(ctx.expressions);
2247 }
2248 Op::SNegate | Op::FNegate => {
2250 inst.expect(4)?;
2251 self.parse_expr_unary_op_sign_adjusted(
2252 ctx,
2253 &mut emitter,
2254 &mut block,
2255 block_id,
2256 body_idx,
2257 crate::UnaryOperator::Negate,
2258 )?;
2259 }
2260 Op::IAdd
2261 | Op::ISub
2262 | Op::IMul
2263 | Op::BitwiseOr
2264 | Op::BitwiseXor
2265 | Op::BitwiseAnd
2266 | Op::SDiv
2267 | Op::SRem => {
2268 inst.expect(5)?;
2269 let operator = map_binary_operator(inst.op)?;
2270 self.parse_expr_binary_op_sign_adjusted(
2271 ctx,
2272 &mut emitter,
2273 &mut block,
2274 block_id,
2275 body_idx,
2276 operator,
2277 SignAnchor::Result,
2278 )?;
2279 }
2280 Op::IEqual | Op::INotEqual => {
2281 inst.expect(5)?;
2282 let operator = map_binary_operator(inst.op)?;
2283 self.parse_expr_binary_op_sign_adjusted(
2284 ctx,
2285 &mut emitter,
2286 &mut block,
2287 block_id,
2288 body_idx,
2289 operator,
2290 SignAnchor::Operand,
2291 )?;
2292 }
2293 Op::FAdd => {
2294 inst.expect(5)?;
2295 parse_expr_op!(crate::BinaryOperator::Add, BINARY)?;
2296 }
2297 Op::FSub => {
2298 inst.expect(5)?;
2299 parse_expr_op!(crate::BinaryOperator::Subtract, BINARY)?;
2300 }
2301 Op::FMul => {
2302 inst.expect(5)?;
2303 parse_expr_op!(crate::BinaryOperator::Multiply, BINARY)?;
2304 }
2305 Op::UDiv | Op::FDiv => {
2306 inst.expect(5)?;
2307 parse_expr_op!(crate::BinaryOperator::Divide, BINARY)?;
2308 }
2309 Op::UMod | Op::FRem => {
2310 inst.expect(5)?;
2311 parse_expr_op!(crate::BinaryOperator::Modulo, BINARY)?;
2312 }
2313 Op::SMod => {
2314 inst.expect(5)?;
2315
2316 let start = self.data_offset;
2319 let result_type_id = self.next()?;
2320 let result_id = self.next()?;
2321 let p1_id = self.next()?;
2322 let p2_id = self.next()?;
2323 let span = self.span_from_with_op(start);
2324
2325 let p1_lexp = self.lookup_expression.lookup(p1_id)?;
2326 let left = self.get_expr_handle(
2327 p1_id,
2328 p1_lexp,
2329 ctx,
2330 &mut emitter,
2331 &mut block,
2332 body_idx,
2333 );
2334 let p2_lexp = self.lookup_expression.lookup(p2_id)?;
2335 let right = self.get_expr_handle(
2336 p2_id,
2337 p2_lexp,
2338 ctx,
2339 &mut emitter,
2340 &mut block,
2341 body_idx,
2342 );
2343
2344 let result_ty = self.lookup_type.lookup(result_type_id)?;
2345 let inner = &ctx.module.types[result_ty.handle].inner;
2346 let kind = inner.scalar_kind().unwrap();
2347 let size = inner.size(ctx.gctx()) as u8;
2348
2349 let left_cast = ctx.expressions.append(
2350 crate::Expression::As {
2351 expr: left,
2352 kind: crate::ScalarKind::Float,
2353 convert: Some(size),
2354 },
2355 span,
2356 );
2357 let right_cast = ctx.expressions.append(
2358 crate::Expression::As {
2359 expr: right,
2360 kind: crate::ScalarKind::Float,
2361 convert: Some(size),
2362 },
2363 span,
2364 );
2365 let div = ctx.expressions.append(
2366 crate::Expression::Binary {
2367 op: crate::BinaryOperator::Divide,
2368 left: left_cast,
2369 right: right_cast,
2370 },
2371 span,
2372 );
2373 let floor = ctx.expressions.append(
2374 crate::Expression::Math {
2375 fun: crate::MathFunction::Floor,
2376 arg: div,
2377 arg1: None,
2378 arg2: None,
2379 arg3: None,
2380 },
2381 span,
2382 );
2383 let cast = ctx.expressions.append(
2384 crate::Expression::As {
2385 expr: floor,
2386 kind,
2387 convert: Some(size),
2388 },
2389 span,
2390 );
2391 let mult = ctx.expressions.append(
2392 crate::Expression::Binary {
2393 op: crate::BinaryOperator::Multiply,
2394 left: cast,
2395 right,
2396 },
2397 span,
2398 );
2399 let sub = ctx.expressions.append(
2400 crate::Expression::Binary {
2401 op: crate::BinaryOperator::Subtract,
2402 left,
2403 right: mult,
2404 },
2405 span,
2406 );
2407 self.lookup_expression.insert(
2408 result_id,
2409 LookupExpression {
2410 handle: sub,
2411 type_id: result_type_id,
2412 block_id,
2413 },
2414 );
2415 }
2416 Op::FMod => {
2417 inst.expect(5)?;
2418
2419 let start = self.data_offset;
2422 let span = self.span_from_with_op(start);
2423
2424 let result_type_id = self.next()?;
2425 let result_id = self.next()?;
2426 let p1_id = self.next()?;
2427 let p2_id = self.next()?;
2428
2429 let p1_lexp = self.lookup_expression.lookup(p1_id)?;
2430 let left = self.get_expr_handle(
2431 p1_id,
2432 p1_lexp,
2433 ctx,
2434 &mut emitter,
2435 &mut block,
2436 body_idx,
2437 );
2438 let p2_lexp = self.lookup_expression.lookup(p2_id)?;
2439 let right = self.get_expr_handle(
2440 p2_id,
2441 p2_lexp,
2442 ctx,
2443 &mut emitter,
2444 &mut block,
2445 body_idx,
2446 );
2447
2448 let div = ctx.expressions.append(
2449 crate::Expression::Binary {
2450 op: crate::BinaryOperator::Divide,
2451 left,
2452 right,
2453 },
2454 span,
2455 );
2456 let floor = ctx.expressions.append(
2457 crate::Expression::Math {
2458 fun: crate::MathFunction::Floor,
2459 arg: div,
2460 arg1: None,
2461 arg2: None,
2462 arg3: None,
2463 },
2464 span,
2465 );
2466 let mult = ctx.expressions.append(
2467 crate::Expression::Binary {
2468 op: crate::BinaryOperator::Multiply,
2469 left: floor,
2470 right,
2471 },
2472 span,
2473 );
2474 let sub = ctx.expressions.append(
2475 crate::Expression::Binary {
2476 op: crate::BinaryOperator::Subtract,
2477 left,
2478 right: mult,
2479 },
2480 span,
2481 );
2482 self.lookup_expression.insert(
2483 result_id,
2484 LookupExpression {
2485 handle: sub,
2486 type_id: result_type_id,
2487 block_id,
2488 },
2489 );
2490 }
2491 Op::VectorTimesScalar
2492 | Op::VectorTimesMatrix
2493 | Op::MatrixTimesScalar
2494 | Op::MatrixTimesVector
2495 | Op::MatrixTimesMatrix => {
2496 inst.expect(5)?;
2497 parse_expr_op!(crate::BinaryOperator::Multiply, BINARY)?;
2498 }
2499 Op::Transpose => {
2500 inst.expect(4)?;
2501
2502 let result_type_id = self.next()?;
2503 let result_id = self.next()?;
2504 let matrix_id = self.next()?;
2505 let matrix_lexp = self.lookup_expression.lookup(matrix_id)?;
2506 let matrix_handle = get_expr_handle!(matrix_id, matrix_lexp);
2507 let expr = crate::Expression::Math {
2508 fun: crate::MathFunction::Transpose,
2509 arg: matrix_handle,
2510 arg1: None,
2511 arg2: None,
2512 arg3: None,
2513 };
2514 self.lookup_expression.insert(
2515 result_id,
2516 LookupExpression {
2517 handle: ctx.expressions.append(expr, span),
2518 type_id: result_type_id,
2519 block_id,
2520 },
2521 );
2522 }
2523 Op::Dot => {
2524 inst.expect(5)?;
2525
2526 let result_type_id = self.next()?;
2527 let result_id = self.next()?;
2528 let left_id = self.next()?;
2529 let right_id = self.next()?;
2530 let left_lexp = self.lookup_expression.lookup(left_id)?;
2531 let left_handle = get_expr_handle!(left_id, left_lexp);
2532 let right_lexp = self.lookup_expression.lookup(right_id)?;
2533 let right_handle = get_expr_handle!(right_id, right_lexp);
2534 let expr = crate::Expression::Math {
2535 fun: crate::MathFunction::Dot,
2536 arg: left_handle,
2537 arg1: Some(right_handle),
2538 arg2: None,
2539 arg3: None,
2540 };
2541 self.lookup_expression.insert(
2542 result_id,
2543 LookupExpression {
2544 handle: ctx.expressions.append(expr, span),
2545 type_id: result_type_id,
2546 block_id,
2547 },
2548 );
2549 }
2550 Op::BitFieldInsert => {
2551 inst.expect(7)?;
2552
2553 let start = self.data_offset;
2554 let span = self.span_from_with_op(start);
2555
2556 let result_type_id = self.next()?;
2557 let result_id = self.next()?;
2558 let base_id = self.next()?;
2559 let insert_id = self.next()?;
2560 let offset_id = self.next()?;
2561 let count_id = self.next()?;
2562 let base_lexp = self.lookup_expression.lookup(base_id)?;
2563 let base_handle = get_expr_handle!(base_id, base_lexp);
2564 let insert_lexp = self.lookup_expression.lookup(insert_id)?;
2565 let insert_handle = get_expr_handle!(insert_id, insert_lexp);
2566 let offset_lexp = self.lookup_expression.lookup(offset_id)?;
2567 let offset_handle = get_expr_handle!(offset_id, offset_lexp);
2568 let offset_lookup_ty = self.lookup_type.lookup(offset_lexp.type_id)?;
2569 let count_lexp = self.lookup_expression.lookup(count_id)?;
2570 let count_handle = get_expr_handle!(count_id, count_lexp);
2571 let count_lookup_ty = self.lookup_type.lookup(count_lexp.type_id)?;
2572
2573 let offset_kind = ctx.module.types[offset_lookup_ty.handle]
2574 .inner
2575 .scalar_kind()
2576 .unwrap();
2577 let count_kind = ctx.module.types[count_lookup_ty.handle]
2578 .inner
2579 .scalar_kind()
2580 .unwrap();
2581
2582 let offset_cast_handle = if offset_kind != crate::ScalarKind::Uint {
2583 ctx.expressions.append(
2584 crate::Expression::As {
2585 expr: offset_handle,
2586 kind: crate::ScalarKind::Uint,
2587 convert: None,
2588 },
2589 span,
2590 )
2591 } else {
2592 offset_handle
2593 };
2594
2595 let count_cast_handle = if count_kind != crate::ScalarKind::Uint {
2596 ctx.expressions.append(
2597 crate::Expression::As {
2598 expr: count_handle,
2599 kind: crate::ScalarKind::Uint,
2600 convert: None,
2601 },
2602 span,
2603 )
2604 } else {
2605 count_handle
2606 };
2607
2608 let expr = crate::Expression::Math {
2609 fun: crate::MathFunction::InsertBits,
2610 arg: base_handle,
2611 arg1: Some(insert_handle),
2612 arg2: Some(offset_cast_handle),
2613 arg3: Some(count_cast_handle),
2614 };
2615 self.lookup_expression.insert(
2616 result_id,
2617 LookupExpression {
2618 handle: ctx.expressions.append(expr, span),
2619 type_id: result_type_id,
2620 block_id,
2621 },
2622 );
2623 }
2624 Op::BitFieldSExtract | Op::BitFieldUExtract => {
2625 inst.expect(6)?;
2626
2627 let result_type_id = self.next()?;
2628 let result_id = self.next()?;
2629 let base_id = self.next()?;
2630 let offset_id = self.next()?;
2631 let count_id = self.next()?;
2632 let base_lexp = self.lookup_expression.lookup(base_id)?;
2633 let base_handle = get_expr_handle!(base_id, base_lexp);
2634 let offset_lexp = self.lookup_expression.lookup(offset_id)?;
2635 let offset_handle = get_expr_handle!(offset_id, offset_lexp);
2636 let offset_lookup_ty = self.lookup_type.lookup(offset_lexp.type_id)?;
2637 let count_lexp = self.lookup_expression.lookup(count_id)?;
2638 let count_handle = get_expr_handle!(count_id, count_lexp);
2639 let count_lookup_ty = self.lookup_type.lookup(count_lexp.type_id)?;
2640
2641 let offset_kind = ctx.module.types[offset_lookup_ty.handle]
2642 .inner
2643 .scalar_kind()
2644 .unwrap();
2645 let count_kind = ctx.module.types[count_lookup_ty.handle]
2646 .inner
2647 .scalar_kind()
2648 .unwrap();
2649
2650 let offset_cast_handle = if offset_kind != crate::ScalarKind::Uint {
2651 ctx.expressions.append(
2652 crate::Expression::As {
2653 expr: offset_handle,
2654 kind: crate::ScalarKind::Uint,
2655 convert: None,
2656 },
2657 span,
2658 )
2659 } else {
2660 offset_handle
2661 };
2662
2663 let count_cast_handle = if count_kind != crate::ScalarKind::Uint {
2664 ctx.expressions.append(
2665 crate::Expression::As {
2666 expr: count_handle,
2667 kind: crate::ScalarKind::Uint,
2668 convert: None,
2669 },
2670 span,
2671 )
2672 } else {
2673 count_handle
2674 };
2675
2676 let expr = crate::Expression::Math {
2677 fun: crate::MathFunction::ExtractBits,
2678 arg: base_handle,
2679 arg1: Some(offset_cast_handle),
2680 arg2: Some(count_cast_handle),
2681 arg3: None,
2682 };
2683 self.lookup_expression.insert(
2684 result_id,
2685 LookupExpression {
2686 handle: ctx.expressions.append(expr, span),
2687 type_id: result_type_id,
2688 block_id,
2689 },
2690 );
2691 }
2692 Op::BitReverse | Op::BitCount => {
2693 inst.expect(4)?;
2694
2695 let result_type_id = self.next()?;
2696 let result_id = self.next()?;
2697 let base_id = self.next()?;
2698 let base_lexp = self.lookup_expression.lookup(base_id)?;
2699 let base_handle = get_expr_handle!(base_id, base_lexp);
2700 let expr = crate::Expression::Math {
2701 fun: match inst.op {
2702 Op::BitReverse => crate::MathFunction::ReverseBits,
2703 Op::BitCount => crate::MathFunction::CountOneBits,
2704 _ => unreachable!(),
2705 },
2706 arg: base_handle,
2707 arg1: None,
2708 arg2: None,
2709 arg3: None,
2710 };
2711 self.lookup_expression.insert(
2712 result_id,
2713 LookupExpression {
2714 handle: ctx.expressions.append(expr, span),
2715 type_id: result_type_id,
2716 block_id,
2717 },
2718 );
2719 }
2720 Op::OuterProduct => {
2721 inst.expect(5)?;
2722
2723 let result_type_id = self.next()?;
2724 let result_id = self.next()?;
2725 let left_id = self.next()?;
2726 let right_id = self.next()?;
2727 let left_lexp = self.lookup_expression.lookup(left_id)?;
2728 let left_handle = get_expr_handle!(left_id, left_lexp);
2729 let right_lexp = self.lookup_expression.lookup(right_id)?;
2730 let right_handle = get_expr_handle!(right_id, right_lexp);
2731 let expr = crate::Expression::Math {
2732 fun: crate::MathFunction::Outer,
2733 arg: left_handle,
2734 arg1: Some(right_handle),
2735 arg2: None,
2736 arg3: None,
2737 };
2738 self.lookup_expression.insert(
2739 result_id,
2740 LookupExpression {
2741 handle: ctx.expressions.append(expr, span),
2742 type_id: result_type_id,
2743 block_id,
2744 },
2745 );
2746 }
2747 Op::Not => {
2749 inst.expect(4)?;
2750 self.parse_expr_unary_op_sign_adjusted(
2751 ctx,
2752 &mut emitter,
2753 &mut block,
2754 block_id,
2755 body_idx,
2756 crate::UnaryOperator::BitwiseNot,
2757 )?;
2758 }
2759 Op::ShiftRightLogical => {
2760 inst.expect(5)?;
2761 parse_expr_op!(crate::BinaryOperator::ShiftRight, SHIFT)?;
2763 }
2764 Op::ShiftRightArithmetic => {
2765 inst.expect(5)?;
2766 parse_expr_op!(crate::BinaryOperator::ShiftRight, SHIFT)?;
2768 }
2769 Op::ShiftLeftLogical => {
2770 inst.expect(5)?;
2771 parse_expr_op!(crate::BinaryOperator::ShiftLeft, SHIFT)?;
2772 }
2773 Op::Image => {
2775 inst.expect(4)?;
2776 self.parse_image_uncouple(block_id)?;
2777 }
2778 Op::SampledImage => {
2779 inst.expect(5)?;
2780 self.parse_image_couple()?;
2781 }
2782 Op::ImageWrite => {
2783 let extra = inst.expect_at_least(4)?;
2784 let stmt =
2785 self.parse_image_write(extra, ctx, &mut emitter, &mut block, body_idx)?;
2786 block.extend(emitter.finish(ctx.expressions));
2787 block.push(stmt, span);
2788 emitter.start(ctx.expressions);
2789 }
2790 Op::ImageFetch | Op::ImageRead => {
2791 let extra = inst.expect_at_least(5)?;
2792 self.parse_image_load(
2793 extra,
2794 ctx,
2795 &mut emitter,
2796 &mut block,
2797 block_id,
2798 body_idx,
2799 )?;
2800 }
2801 Op::ImageSampleImplicitLod | Op::ImageSampleExplicitLod => {
2802 let extra = inst.expect_at_least(5)?;
2803 let options = image::SamplingOptions {
2804 compare: false,
2805 project: false,
2806 gather: false,
2807 };
2808 self.parse_image_sample(
2809 extra,
2810 options,
2811 ctx,
2812 &mut emitter,
2813 &mut block,
2814 block_id,
2815 body_idx,
2816 )?;
2817 }
2818 Op::ImageSampleProjImplicitLod | Op::ImageSampleProjExplicitLod => {
2819 let extra = inst.expect_at_least(5)?;
2820 let options = image::SamplingOptions {
2821 compare: false,
2822 project: true,
2823 gather: false,
2824 };
2825 self.parse_image_sample(
2826 extra,
2827 options,
2828 ctx,
2829 &mut emitter,
2830 &mut block,
2831 block_id,
2832 body_idx,
2833 )?;
2834 }
2835 Op::ImageSampleDrefImplicitLod | Op::ImageSampleDrefExplicitLod => {
2836 let extra = inst.expect_at_least(6)?;
2837 let options = image::SamplingOptions {
2838 compare: true,
2839 project: false,
2840 gather: false,
2841 };
2842 self.parse_image_sample(
2843 extra,
2844 options,
2845 ctx,
2846 &mut emitter,
2847 &mut block,
2848 block_id,
2849 body_idx,
2850 )?;
2851 }
2852 Op::ImageSampleProjDrefImplicitLod | Op::ImageSampleProjDrefExplicitLod => {
2853 let extra = inst.expect_at_least(6)?;
2854 let options = image::SamplingOptions {
2855 compare: true,
2856 project: true,
2857 gather: false,
2858 };
2859 self.parse_image_sample(
2860 extra,
2861 options,
2862 ctx,
2863 &mut emitter,
2864 &mut block,
2865 block_id,
2866 body_idx,
2867 )?;
2868 }
2869 Op::ImageGather => {
2870 let extra = inst.expect_at_least(6)?;
2871 let options = image::SamplingOptions {
2872 compare: false,
2873 project: false,
2874 gather: true,
2875 };
2876 self.parse_image_sample(
2877 extra,
2878 options,
2879 ctx,
2880 &mut emitter,
2881 &mut block,
2882 block_id,
2883 body_idx,
2884 )?;
2885 }
2886 Op::ImageDrefGather => {
2887 let extra = inst.expect_at_least(6)?;
2888 let options = image::SamplingOptions {
2889 compare: true,
2890 project: false,
2891 gather: true,
2892 };
2893 self.parse_image_sample(
2894 extra,
2895 options,
2896 ctx,
2897 &mut emitter,
2898 &mut block,
2899 block_id,
2900 body_idx,
2901 )?;
2902 }
2903 Op::ImageQuerySize => {
2904 inst.expect(4)?;
2905 self.parse_image_query_size(
2906 false,
2907 ctx,
2908 &mut emitter,
2909 &mut block,
2910 block_id,
2911 body_idx,
2912 )?;
2913 }
2914 Op::ImageQuerySizeLod => {
2915 inst.expect(5)?;
2916 self.parse_image_query_size(
2917 true,
2918 ctx,
2919 &mut emitter,
2920 &mut block,
2921 block_id,
2922 body_idx,
2923 )?;
2924 }
2925 Op::ImageQueryLevels => {
2926 inst.expect(4)?;
2927 self.parse_image_query_other(crate::ImageQuery::NumLevels, ctx, block_id)?;
2928 }
2929 Op::ImageQuerySamples => {
2930 inst.expect(4)?;
2931 self.parse_image_query_other(crate::ImageQuery::NumSamples, ctx, block_id)?;
2932 }
2933 Op::Select => {
2935 inst.expect(6)?;
2936 let result_type_id = self.next()?;
2937 let result_id = self.next()?;
2938 let condition = self.next()?;
2939 let o1_id = self.next()?;
2940 let o2_id = self.next()?;
2941
2942 let cond_lexp = self.lookup_expression.lookup(condition)?;
2943 let cond_handle = get_expr_handle!(condition, cond_lexp);
2944 let o1_lexp = self.lookup_expression.lookup(o1_id)?;
2945 let o1_handle = get_expr_handle!(o1_id, o1_lexp);
2946 let o2_lexp = self.lookup_expression.lookup(o2_id)?;
2947 let o2_handle = get_expr_handle!(o2_id, o2_lexp);
2948
2949 let expr = crate::Expression::Select {
2950 condition: cond_handle,
2951 accept: o1_handle,
2952 reject: o2_handle,
2953 };
2954 self.lookup_expression.insert(
2955 result_id,
2956 LookupExpression {
2957 handle: ctx.expressions.append(expr, span),
2958 type_id: result_type_id,
2959 block_id,
2960 },
2961 );
2962 }
2963 Op::VectorShuffle => {
2964 inst.expect_at_least(5)?;
2965 let result_type_id = self.next()?;
2966 let result_id = self.next()?;
2967 let v1_id = self.next()?;
2968 let v2_id = self.next()?;
2969
2970 let v1_lexp = self.lookup_expression.lookup(v1_id)?;
2971 let v1_lty = self.lookup_type.lookup(v1_lexp.type_id)?;
2972 let v1_handle = get_expr_handle!(v1_id, v1_lexp);
2973 let n1 = match ctx.module.types[v1_lty.handle].inner {
2974 crate::TypeInner::Vector { size, .. } => size as u32,
2975 _ => return Err(Error::InvalidInnerType(v1_lexp.type_id)),
2976 };
2977 let v2_lexp = self.lookup_expression.lookup(v2_id)?;
2978 let v2_lty = self.lookup_type.lookup(v2_lexp.type_id)?;
2979 let v2_handle = get_expr_handle!(v2_id, v2_lexp);
2980 let n2 = match ctx.module.types[v2_lty.handle].inner {
2981 crate::TypeInner::Vector { size, .. } => size as u32,
2982 _ => return Err(Error::InvalidInnerType(v2_lexp.type_id)),
2983 };
2984
2985 self.temp_bytes.clear();
2986 let mut max_component = 0;
2987 for _ in 5..inst.wc as usize {
2988 let mut index = self.next()?;
2989 if index == u32::MAX {
2990 index = 0;
2992 }
2993 max_component = max_component.max(index);
2994 self.temp_bytes.push(index as u8);
2995 }
2996
2997 let expr = if max_component < n1 {
2999 use crate::SwizzleComponent as Sc;
3000 let size = match self.temp_bytes.len() {
3001 2 => crate::VectorSize::Bi,
3002 3 => crate::VectorSize::Tri,
3003 _ => crate::VectorSize::Quad,
3004 };
3005 let mut pattern = [Sc::X; 4];
3006 for (pat, index) in pattern.iter_mut().zip(self.temp_bytes.drain(..)) {
3007 *pat = match index {
3008 0 => Sc::X,
3009 1 => Sc::Y,
3010 2 => Sc::Z,
3011 _ => Sc::W,
3012 };
3013 }
3014 crate::Expression::Swizzle {
3015 size,
3016 vector: v1_handle,
3017 pattern,
3018 }
3019 } else {
3020 let mut components = Vec::with_capacity(self.temp_bytes.len());
3022 for index in self.temp_bytes.drain(..).map(|i| i as u32) {
3023 let expr = if index < n1 {
3024 crate::Expression::AccessIndex {
3025 base: v1_handle,
3026 index,
3027 }
3028 } else if index < n1 + n2 {
3029 crate::Expression::AccessIndex {
3030 base: v2_handle,
3031 index: index - n1,
3032 }
3033 } else {
3034 return Err(Error::InvalidAccessIndex(index));
3035 };
3036 components.push(ctx.expressions.append(expr, span));
3037 }
3038 crate::Expression::Compose {
3039 ty: self.lookup_type.lookup(result_type_id)?.handle,
3040 components,
3041 }
3042 };
3043
3044 self.lookup_expression.insert(
3045 result_id,
3046 LookupExpression {
3047 handle: ctx.expressions.append(expr, span),
3048 type_id: result_type_id,
3049 block_id,
3050 },
3051 );
3052 }
3053 Op::Bitcast
3054 | Op::ConvertSToF
3055 | Op::ConvertUToF
3056 | Op::ConvertFToU
3057 | Op::ConvertFToS
3058 | Op::FConvert
3059 | Op::UConvert
3060 | Op::SConvert => {
3061 inst.expect(4)?;
3062 let result_type_id = self.next()?;
3063 let result_id = self.next()?;
3064 let value_id = self.next()?;
3065
3066 let value_lexp = self.lookup_expression.lookup(value_id)?;
3067 let ty_lookup = self.lookup_type.lookup(result_type_id)?;
3068 let scalar = match ctx.module.types[ty_lookup.handle].inner {
3069 crate::TypeInner::Scalar(scalar)
3070 | crate::TypeInner::Vector { scalar, .. }
3071 | crate::TypeInner::Matrix { scalar, .. } => scalar,
3072 _ => return Err(Error::InvalidAsType(ty_lookup.handle)),
3073 };
3074
3075 let expr = crate::Expression::As {
3076 expr: get_expr_handle!(value_id, value_lexp),
3077 kind: scalar.kind,
3078 convert: if scalar.kind == crate::ScalarKind::Bool {
3079 Some(crate::BOOL_WIDTH)
3080 } else if inst.op == Op::Bitcast {
3081 None
3082 } else {
3083 Some(scalar.width)
3084 },
3085 };
3086 self.lookup_expression.insert(
3087 result_id,
3088 LookupExpression {
3089 handle: ctx.expressions.append(expr, span),
3090 type_id: result_type_id,
3091 block_id,
3092 },
3093 );
3094 }
3095 Op::FunctionCall => {
3096 inst.expect_at_least(4)?;
3097
3098 let result_type_id = self.next()?;
3099 let result_id = self.next()?;
3100 let func_id = self.next()?;
3101
3102 let mut arguments = Vec::with_capacity(inst.wc as usize - 4);
3103 for _ in 0..arguments.capacity() {
3104 let arg_id = self.next()?;
3105 let lexp = self.lookup_expression.lookup(arg_id)?;
3106 arguments.push(get_expr_handle!(arg_id, lexp));
3107 }
3108
3109 block.extend(emitter.finish(ctx.expressions));
3110
3111 let function = self.add_call(ctx.function_id, func_id);
3113
3114 let result = if self.lookup_void_type == Some(result_type_id) {
3115 None
3116 } else {
3117 let expr_handle = ctx
3118 .expressions
3119 .append(crate::Expression::CallResult(function), span);
3120 self.lookup_expression.insert(
3121 result_id,
3122 LookupExpression {
3123 handle: expr_handle,
3124 type_id: result_type_id,
3125 block_id,
3126 },
3127 );
3128 Some(expr_handle)
3129 };
3130 block.push(
3131 crate::Statement::Call {
3132 function,
3133 arguments,
3134 result,
3135 },
3136 span,
3137 );
3138 emitter.start(ctx.expressions);
3139 }
3140 Op::ExtInst => {
3141 use crate::MathFunction as Mf;
3142 use spirv::GLOp as Glo;
3143
3144 let base_wc = 5;
3145 inst.expect_at_least(base_wc)?;
3146
3147 let result_type_id = self.next()?;
3148 let result_id = self.next()?;
3149 let set_id = self.next()?;
3150 if Some(set_id) != self.ext_glsl_id {
3151 return Err(Error::UnsupportedExtInstSet(set_id));
3152 }
3153 let inst_id = self.next()?;
3154 let gl_op = Glo::from_u32(inst_id).ok_or(Error::UnsupportedExtInst(inst_id))?;
3155
3156 let fun = match gl_op {
3157 Glo::Round => Mf::Round,
3158 Glo::RoundEven => Mf::Round,
3159 Glo::Trunc => Mf::Trunc,
3160 Glo::FAbs | Glo::SAbs => Mf::Abs,
3161 Glo::FSign | Glo::SSign => Mf::Sign,
3162 Glo::Floor => Mf::Floor,
3163 Glo::Ceil => Mf::Ceil,
3164 Glo::Fract => Mf::Fract,
3165 Glo::Sin => Mf::Sin,
3166 Glo::Cos => Mf::Cos,
3167 Glo::Tan => Mf::Tan,
3168 Glo::Asin => Mf::Asin,
3169 Glo::Acos => Mf::Acos,
3170 Glo::Atan => Mf::Atan,
3171 Glo::Sinh => Mf::Sinh,
3172 Glo::Cosh => Mf::Cosh,
3173 Glo::Tanh => Mf::Tanh,
3174 Glo::Atan2 => Mf::Atan2,
3175 Glo::Asinh => Mf::Asinh,
3176 Glo::Acosh => Mf::Acosh,
3177 Glo::Atanh => Mf::Atanh,
3178 Glo::Radians => Mf::Radians,
3179 Glo::Degrees => Mf::Degrees,
3180 Glo::Pow => Mf::Pow,
3181 Glo::Exp => Mf::Exp,
3182 Glo::Log => Mf::Log,
3183 Glo::Exp2 => Mf::Exp2,
3184 Glo::Log2 => Mf::Log2,
3185 Glo::Sqrt => Mf::Sqrt,
3186 Glo::InverseSqrt => Mf::InverseSqrt,
3187 Glo::MatrixInverse => Mf::Inverse,
3188 Glo::Determinant => Mf::Determinant,
3189 Glo::ModfStruct => Mf::Modf,
3190 Glo::FMin | Glo::UMin | Glo::SMin | Glo::NMin => Mf::Min,
3191 Glo::FMax | Glo::UMax | Glo::SMax | Glo::NMax => Mf::Max,
3192 Glo::FClamp | Glo::UClamp | Glo::SClamp | Glo::NClamp => Mf::Clamp,
3193 Glo::FMix => Mf::Mix,
3194 Glo::Step => Mf::Step,
3195 Glo::SmoothStep => Mf::SmoothStep,
3196 Glo::Fma => Mf::Fma,
3197 Glo::FrexpStruct => Mf::Frexp,
3198 Glo::Ldexp => Mf::Ldexp,
3199 Glo::Length => Mf::Length,
3200 Glo::Distance => Mf::Distance,
3201 Glo::Cross => Mf::Cross,
3202 Glo::Normalize => Mf::Normalize,
3203 Glo::FaceForward => Mf::FaceForward,
3204 Glo::Reflect => Mf::Reflect,
3205 Glo::Refract => Mf::Refract,
3206 Glo::PackUnorm4x8 => Mf::Pack4x8unorm,
3207 Glo::PackSnorm4x8 => Mf::Pack4x8snorm,
3208 Glo::PackHalf2x16 => Mf::Pack2x16float,
3209 Glo::PackUnorm2x16 => Mf::Pack2x16unorm,
3210 Glo::PackSnorm2x16 => Mf::Pack2x16snorm,
3211 Glo::UnpackUnorm4x8 => Mf::Unpack4x8unorm,
3212 Glo::UnpackSnorm4x8 => Mf::Unpack4x8snorm,
3213 Glo::UnpackHalf2x16 => Mf::Unpack2x16float,
3214 Glo::UnpackUnorm2x16 => Mf::Unpack2x16unorm,
3215 Glo::UnpackSnorm2x16 => Mf::Unpack2x16snorm,
3216 Glo::FindILsb => Mf::FirstTrailingBit,
3217 Glo::FindUMsb | Glo::FindSMsb => Mf::FirstLeadingBit,
3218 Glo::Modf | Glo::Frexp => return Err(Error::UnsupportedExtInst(inst_id)),
3220 Glo::IMix
3221 | Glo::PackDouble2x32
3222 | Glo::UnpackDouble2x32
3223 | Glo::InterpolateAtCentroid
3224 | Glo::InterpolateAtSample
3225 | Glo::InterpolateAtOffset => {
3226 return Err(Error::UnsupportedExtInst(inst_id))
3227 }
3228 };
3229
3230 let arg_count = fun.argument_count();
3231 inst.expect(base_wc + arg_count as u16)?;
3232 let arg = {
3233 let arg_id = self.next()?;
3234 let lexp = self.lookup_expression.lookup(arg_id)?;
3235 get_expr_handle!(arg_id, lexp)
3236 };
3237 let arg1 = if arg_count > 1 {
3238 let arg_id = self.next()?;
3239 let lexp = self.lookup_expression.lookup(arg_id)?;
3240 Some(get_expr_handle!(arg_id, lexp))
3241 } else {
3242 None
3243 };
3244 let arg2 = if arg_count > 2 {
3245 let arg_id = self.next()?;
3246 let lexp = self.lookup_expression.lookup(arg_id)?;
3247 Some(get_expr_handle!(arg_id, lexp))
3248 } else {
3249 None
3250 };
3251 let arg3 = if arg_count > 3 {
3252 let arg_id = self.next()?;
3253 let lexp = self.lookup_expression.lookup(arg_id)?;
3254 Some(get_expr_handle!(arg_id, lexp))
3255 } else {
3256 None
3257 };
3258
3259 let expr = crate::Expression::Math {
3260 fun,
3261 arg,
3262 arg1,
3263 arg2,
3264 arg3,
3265 };
3266 self.lookup_expression.insert(
3267 result_id,
3268 LookupExpression {
3269 handle: ctx.expressions.append(expr, span),
3270 type_id: result_type_id,
3271 block_id,
3272 },
3273 );
3274 }
3275 Op::LogicalNot => {
3277 inst.expect(4)?;
3278 parse_expr_op!(crate::UnaryOperator::LogicalNot, UNARY)?;
3279 }
3280 Op::LogicalOr => {
3281 inst.expect(5)?;
3282 parse_expr_op!(crate::BinaryOperator::LogicalOr, BINARY)?;
3283 }
3284 Op::LogicalAnd => {
3285 inst.expect(5)?;
3286 parse_expr_op!(crate::BinaryOperator::LogicalAnd, BINARY)?;
3287 }
3288 Op::SGreaterThan | Op::SGreaterThanEqual | Op::SLessThan | Op::SLessThanEqual => {
3289 inst.expect(5)?;
3290 self.parse_expr_int_comparison(
3291 ctx,
3292 &mut emitter,
3293 &mut block,
3294 block_id,
3295 body_idx,
3296 map_binary_operator(inst.op)?,
3297 crate::ScalarKind::Sint,
3298 )?;
3299 }
3300 Op::UGreaterThan | Op::UGreaterThanEqual | Op::ULessThan | Op::ULessThanEqual => {
3301 inst.expect(5)?;
3302 self.parse_expr_int_comparison(
3303 ctx,
3304 &mut emitter,
3305 &mut block,
3306 block_id,
3307 body_idx,
3308 map_binary_operator(inst.op)?,
3309 crate::ScalarKind::Uint,
3310 )?;
3311 }
3312 Op::FOrdEqual
3313 | Op::FUnordEqual
3314 | Op::FOrdNotEqual
3315 | Op::FUnordNotEqual
3316 | Op::FOrdLessThan
3317 | Op::FUnordLessThan
3318 | Op::FOrdGreaterThan
3319 | Op::FUnordGreaterThan
3320 | Op::FOrdLessThanEqual
3321 | Op::FUnordLessThanEqual
3322 | Op::FOrdGreaterThanEqual
3323 | Op::FUnordGreaterThanEqual
3324 | Op::LogicalEqual
3325 | Op::LogicalNotEqual => {
3326 inst.expect(5)?;
3327 let operator = map_binary_operator(inst.op)?;
3328 parse_expr_op!(operator, BINARY)?;
3329 }
3330 Op::Any | Op::All | Op::IsNan | Op::IsInf | Op::IsFinite | Op::IsNormal => {
3331 inst.expect(4)?;
3332 let result_type_id = self.next()?;
3333 let result_id = self.next()?;
3334 let arg_id = self.next()?;
3335
3336 let arg_lexp = self.lookup_expression.lookup(arg_id)?;
3337 let arg_handle = get_expr_handle!(arg_id, arg_lexp);
3338
3339 let expr = crate::Expression::Relational {
3340 fun: map_relational_fun(inst.op)?,
3341 argument: arg_handle,
3342 };
3343 self.lookup_expression.insert(
3344 result_id,
3345 LookupExpression {
3346 handle: ctx.expressions.append(expr, span),
3347 type_id: result_type_id,
3348 block_id,
3349 },
3350 );
3351 }
3352 Op::Kill => {
3353 inst.expect(1)?;
3354 break Some(crate::Statement::Kill);
3355 }
3356 Op::Unreachable => {
3357 inst.expect(1)?;
3358 break None;
3359 }
3360 Op::Return => {
3361 inst.expect(1)?;
3362 break Some(crate::Statement::Return { value: None });
3363 }
3364 Op::ReturnValue => {
3365 inst.expect(2)?;
3366 let value_id = self.next()?;
3367 let value_lexp = self.lookup_expression.lookup(value_id)?;
3368 let value_handle = get_expr_handle!(value_id, value_lexp);
3369 break Some(crate::Statement::Return {
3370 value: Some(value_handle),
3371 });
3372 }
3373 Op::Branch => {
3374 inst.expect(2)?;
3375 let target_id = self.next()?;
3376
3377 if let Some(info) = ctx.mergers.get(&target_id) {
3386 block.extend(emitter.finish(ctx.expressions));
3387 ctx.blocks.insert(block_id, block);
3388 let body = &mut ctx.bodies[body_idx];
3389 body.data.push(BodyFragment::BlockId(block_id));
3390
3391 merger(body, info);
3392
3393 return Ok(());
3394 }
3395
3396 ctx.body_for_label.entry(target_id).or_insert(body_idx);
3413
3414 break None;
3415 }
3416 Op::BranchConditional => {
3417 inst.expect_at_least(4)?;
3418
3419 let condition = {
3420 let condition_id = self.next()?;
3421 let lexp = self.lookup_expression.lookup(condition_id)?;
3422 get_expr_handle!(condition_id, lexp)
3423 };
3424
3425 #[derive(Copy, Clone)]
3428 struct BranchTarget {
3429 label_id: spirv::Word,
3430 merge_info: Option<MergeBlockInformation>,
3431 }
3432 let branch_target = |label_id| BranchTarget {
3433 label_id,
3434 merge_info: ctx.mergers.get(&label_id).copied(),
3435 };
3436
3437 let true_target = branch_target(self.next()?);
3438 let false_target = branch_target(self.next()?);
3439
3440 for _ in 4..inst.wc {
3442 let _ = self.next()?;
3443 }
3444
3445 let parent_body_idx = ctx.bodies[body_idx].parent;
3453 let parent_parent_body_idx = ctx.bodies[parent_body_idx].parent;
3454 match ctx.bodies[parent_parent_body_idx].data[..] {
3455 [.., BodyFragment::Loop {
3459 body: loop_body_idx,
3460 continuing: loop_continuing_idx,
3461 break_if: ref mut break_if_slot @ None,
3462 }] if body_idx == loop_continuing_idx => {
3463 let break_if_cond = [true, false].into_iter().find_map(|true_breaks| {
3466 let (break_candidate, backedge_candidate) = if true_breaks {
3467 (true_target, false_target)
3468 } else {
3469 (false_target, true_target)
3470 };
3471
3472 if break_candidate.merge_info
3473 != Some(MergeBlockInformation::LoopMerge)
3474 {
3475 return None;
3476 }
3477
3478 let backedge_candidate_is_backedge =
3482 backedge_candidate.merge_info.is_none()
3483 && ctx.body_for_label.get(&backedge_candidate.label_id)
3484 == Some(&loop_body_idx);
3485 if !backedge_candidate_is_backedge {
3486 return None;
3487 }
3488
3489 Some(if true_breaks {
3490 condition
3491 } else {
3492 ctx.expressions.append(
3493 crate::Expression::Unary {
3494 op: crate::UnaryOperator::LogicalNot,
3495 expr: condition,
3496 },
3497 span,
3498 )
3499 })
3500 });
3501
3502 if let Some(break_if_cond) = break_if_cond {
3503 *break_if_slot = Some(break_if_cond);
3504
3505 break None;
3509 }
3510 }
3511 _ => {}
3512 }
3513
3514 block.extend(emitter.finish(ctx.expressions));
3515 ctx.blocks.insert(block_id, block);
3516 let body = &mut ctx.bodies[body_idx];
3517 body.data.push(BodyFragment::BlockId(block_id));
3518
3519 let same_target = true_target.label_id == false_target.label_id;
3520
3521 let accept = ctx.bodies.len();
3523 let mut accept_block = Body::with_parent(body_idx);
3524
3525 if let Some(info) = true_target.merge_info {
3529 merger(
3530 match same_target {
3531 true => &mut ctx.bodies[body_idx],
3532 false => &mut accept_block,
3533 },
3534 &info,
3535 )
3536 } else {
3537 let prev = ctx.body_for_label.insert(
3539 true_target.label_id,
3540 match same_target {
3541 true => body_idx,
3542 false => accept,
3543 },
3544 );
3545 debug_assert!(prev.is_none());
3546 }
3547
3548 if same_target {
3549 return Ok(());
3550 }
3551
3552 ctx.bodies.push(accept_block);
3553
3554 let reject = ctx.bodies.len();
3556 let mut reject_block = Body::with_parent(body_idx);
3557
3558 if let Some(info) = false_target.merge_info {
3559 merger(&mut reject_block, &info)
3560 } else {
3561 let prev = ctx.body_for_label.insert(false_target.label_id, reject);
3562 debug_assert!(prev.is_none());
3563 }
3564
3565 ctx.bodies.push(reject_block);
3566
3567 let body = &mut ctx.bodies[body_idx];
3568 body.data.push(BodyFragment::If {
3569 condition,
3570 accept,
3571 reject,
3572 });
3573
3574 return Ok(());
3575 }
3576 Op::Switch => {
3577 inst.expect_at_least(3)?;
3578 let selector = self.next()?;
3579 let default_id = self.next()?;
3580
3581 if let Some(merge) = selection_merge_block {
3584 ctx.mergers
3585 .insert(merge, MergeBlockInformation::SwitchMerge);
3586 }
3587
3588 let default = ctx.bodies.len();
3589 ctx.bodies.push(Body::with_parent(body_idx));
3590 ctx.body_for_label.entry(default_id).or_insert(default);
3591
3592 let selector_lexp = &self.lookup_expression[&selector];
3593 let selector_lty = self.lookup_type.lookup(selector_lexp.type_id)?;
3594 let selector_handle = get_expr_handle!(selector, selector_lexp);
3595 let selector = match ctx.module.types[selector_lty.handle].inner {
3596 crate::TypeInner::Scalar(crate::Scalar {
3597 kind: crate::ScalarKind::Uint,
3598 width: _,
3599 }) => {
3600 ctx.expressions.append(
3602 crate::Expression::As {
3603 kind: crate::ScalarKind::Sint,
3604 expr: selector_handle,
3605 convert: None,
3606 },
3607 span,
3608 )
3609 }
3610 crate::TypeInner::Scalar(crate::Scalar {
3611 kind: crate::ScalarKind::Sint,
3612 width: _,
3613 }) => selector_handle,
3614 ref other => unimplemented!("Unexpected selector {:?}", other),
3615 };
3616
3617 self.switch_cases.clear();
3619
3620 for _ in 0..(inst.wc - 3) / 2 {
3621 let literal = self.next()?;
3622 let target = self.next()?;
3623
3624 let case_body_idx = ctx.bodies.len();
3625
3626 if let Some(&mut (_, ref mut literals)) = self.switch_cases.get_mut(&target)
3630 {
3631 literals.push(literal as i32);
3632 continue;
3633 }
3634
3635 let mut body = Body::with_parent(body_idx);
3636
3637 if let Some(info) = ctx.mergers.get(&target) {
3638 merger(&mut body, info);
3639 }
3640
3641 ctx.bodies.push(body);
3642 ctx.body_for_label.entry(target).or_insert(case_body_idx);
3643
3644 self.switch_cases
3647 .insert(target, (case_body_idx, vec![literal as i32]));
3648 }
3649
3650 let mut cases = Vec::with_capacity((inst.wc as usize - 3) / 2);
3659 for &(case_body_idx, ref literals) in self.switch_cases.values() {
3660 let value = literals[0];
3661
3662 for &literal in literals.iter().skip(1) {
3663 let empty_body_idx = ctx.bodies.len();
3664 let body = Body::with_parent(body_idx);
3665
3666 ctx.bodies.push(body);
3667
3668 cases.push((literal, empty_body_idx));
3669 }
3670
3671 cases.push((value, case_body_idx));
3672 }
3673
3674 block.extend(emitter.finish(ctx.expressions));
3675
3676 let body = &mut ctx.bodies[body_idx];
3677 ctx.blocks.insert(block_id, block);
3678 body.data.reserve(2);
3680 body.data.push(BodyFragment::BlockId(block_id));
3681 body.data.push(BodyFragment::Switch {
3682 selector,
3683 cases,
3684 default,
3685 });
3686
3687 return Ok(());
3688 }
3689 Op::SelectionMerge => {
3690 inst.expect(3)?;
3691 let merge_block_id = self.next()?;
3692 let _selection_control = self.next()?;
3694
3695 ctx.body_for_label.entry(merge_block_id).or_insert(body_idx);
3698
3699 ctx.mergers
3702 .insert(merge_block_id, MergeBlockInformation::SelectionMerge);
3703
3704 selection_merge_block = Some(merge_block_id);
3705 }
3706 Op::LoopMerge => {
3707 inst.expect_at_least(4)?;
3708 let merge_block_id = self.next()?;
3709 let continuing = self.next()?;
3710
3711 for _ in 0..inst.wc - 3 {
3713 self.next()?;
3714 }
3715
3716 ctx.body_for_label.entry(merge_block_id).or_insert(body_idx);
3719 ctx.mergers
3722 .insert(merge_block_id, MergeBlockInformation::LoopMerge);
3723
3724 let loop_body_idx = ctx.bodies.len();
3725 ctx.bodies.push(Body::with_parent(body_idx));
3726
3727 let continue_idx = ctx.bodies.len();
3728 ctx.bodies.push(Body::with_parent(loop_body_idx));
3730 ctx.body_for_label.entry(continuing).or_insert(continue_idx);
3731 ctx.mergers
3734 .insert(continuing, MergeBlockInformation::LoopContinue);
3735
3736 ctx.body_for_label.insert(block_id, loop_body_idx);
3738
3739 let parent_body = &mut ctx.bodies[body_idx];
3740 parent_body.data.push(BodyFragment::Loop {
3741 body: loop_body_idx,
3742 continuing: continue_idx,
3743 break_if: None,
3744 });
3745 body_idx = loop_body_idx;
3746 }
3747 Op::DPdxCoarse => {
3748 parse_expr_op!(
3749 crate::DerivativeAxis::X,
3750 crate::DerivativeControl::Coarse,
3751 DERIVATIVE
3752 )?;
3753 }
3754 Op::DPdyCoarse => {
3755 parse_expr_op!(
3756 crate::DerivativeAxis::Y,
3757 crate::DerivativeControl::Coarse,
3758 DERIVATIVE
3759 )?;
3760 }
3761 Op::FwidthCoarse => {
3762 parse_expr_op!(
3763 crate::DerivativeAxis::Width,
3764 crate::DerivativeControl::Coarse,
3765 DERIVATIVE
3766 )?;
3767 }
3768 Op::DPdxFine => {
3769 parse_expr_op!(
3770 crate::DerivativeAxis::X,
3771 crate::DerivativeControl::Fine,
3772 DERIVATIVE
3773 )?;
3774 }
3775 Op::DPdyFine => {
3776 parse_expr_op!(
3777 crate::DerivativeAxis::Y,
3778 crate::DerivativeControl::Fine,
3779 DERIVATIVE
3780 )?;
3781 }
3782 Op::FwidthFine => {
3783 parse_expr_op!(
3784 crate::DerivativeAxis::Width,
3785 crate::DerivativeControl::Fine,
3786 DERIVATIVE
3787 )?;
3788 }
3789 Op::DPdx => {
3790 parse_expr_op!(
3791 crate::DerivativeAxis::X,
3792 crate::DerivativeControl::None,
3793 DERIVATIVE
3794 )?;
3795 }
3796 Op::DPdy => {
3797 parse_expr_op!(
3798 crate::DerivativeAxis::Y,
3799 crate::DerivativeControl::None,
3800 DERIVATIVE
3801 )?;
3802 }
3803 Op::Fwidth => {
3804 parse_expr_op!(
3805 crate::DerivativeAxis::Width,
3806 crate::DerivativeControl::None,
3807 DERIVATIVE
3808 )?;
3809 }
3810 Op::ArrayLength => {
3811 inst.expect(5)?;
3812 let result_type_id = self.next()?;
3813 let result_id = self.next()?;
3814 let structure_id = self.next()?;
3815 let member_index = self.next()?;
3816
3817 let structure_ptr = self.lookup_expression.lookup(structure_id)?;
3821 let structure_handle = get_expr_handle!(structure_id, structure_ptr);
3822
3823 let member_ptr = ctx.expressions.append(
3824 crate::Expression::AccessIndex {
3825 base: structure_handle,
3826 index: member_index,
3827 },
3828 span,
3829 );
3830
3831 let length = ctx
3832 .expressions
3833 .append(crate::Expression::ArrayLength(member_ptr), span);
3834
3835 self.lookup_expression.insert(
3836 result_id,
3837 LookupExpression {
3838 handle: length,
3839 type_id: result_type_id,
3840 block_id,
3841 },
3842 );
3843 }
3844 Op::CopyMemory => {
3845 inst.expect_at_least(3)?;
3846 let target_id = self.next()?;
3847 let source_id = self.next()?;
3848 let _memory_access = if inst.wc != 3 {
3849 inst.expect(4)?;
3850 spirv::MemoryAccess::from_bits(self.next()?)
3851 .ok_or(Error::InvalidParameter(Op::CopyMemory))?
3852 } else {
3853 spirv::MemoryAccess::NONE
3854 };
3855
3856 let target = self.lookup_expression.lookup(target_id)?;
3858 let target_handle = get_expr_handle!(target_id, target);
3859 let source = self.lookup_expression.lookup(source_id)?;
3860 let source_handle = get_expr_handle!(source_id, source);
3861
3862 let value_expr = ctx.expressions.append(
3864 crate::Expression::Load {
3865 pointer: source_handle,
3866 },
3867 span,
3868 );
3869
3870 block.extend(emitter.finish(ctx.expressions));
3871 block.push(
3872 crate::Statement::Store {
3873 pointer: target_handle,
3874 value: value_expr,
3875 },
3876 span,
3877 );
3878
3879 emitter.start(ctx.expressions);
3880 }
3881 Op::ControlBarrier => {
3882 inst.expect(4)?;
3883 let exec_scope_id = self.next()?;
3884 let _mem_scope_raw = self.next()?;
3885 let semantics_id = self.next()?;
3886 let exec_scope_const = self.lookup_constant.lookup(exec_scope_id)?;
3887 let semantics_const = self.lookup_constant.lookup(semantics_id)?;
3888
3889 let exec_scope = resolve_constant(ctx.gctx(), &exec_scope_const.inner)
3890 .ok_or(Error::InvalidBarrierScope(exec_scope_id))?;
3891 let semantics = resolve_constant(ctx.gctx(), &semantics_const.inner)
3892 .ok_or(Error::InvalidBarrierMemorySemantics(semantics_id))?;
3893
3894 if exec_scope == spirv::Scope::Workgroup as u32
3895 || exec_scope == spirv::Scope::Subgroup as u32
3896 {
3897 let mut flags = crate::Barrier::empty();
3898 flags.set(
3899 crate::Barrier::STORAGE,
3900 semantics & spirv::MemorySemantics::UNIFORM_MEMORY.bits() != 0,
3901 );
3902 flags.set(
3903 crate::Barrier::WORK_GROUP,
3904 semantics & (spirv::MemorySemantics::WORKGROUP_MEMORY).bits() != 0,
3905 );
3906 flags.set(
3907 crate::Barrier::SUB_GROUP,
3908 semantics & spirv::MemorySemantics::SUBGROUP_MEMORY.bits() != 0,
3909 );
3910 flags.set(
3911 crate::Barrier::TEXTURE,
3912 semantics & spirv::MemorySemantics::IMAGE_MEMORY.bits() != 0,
3913 );
3914
3915 block.extend(emitter.finish(ctx.expressions));
3916 block.push(crate::Statement::ControlBarrier(flags), span);
3917 emitter.start(ctx.expressions);
3918 } else {
3919 log::warn!("Unsupported barrier execution scope: {exec_scope}");
3920 }
3921 }
3922 Op::MemoryBarrier => {
3923 inst.expect(3)?;
3924 let mem_scope_id = self.next()?;
3925 let semantics_id = self.next()?;
3926 let mem_scope_const = self.lookup_constant.lookup(mem_scope_id)?;
3927 let semantics_const = self.lookup_constant.lookup(semantics_id)?;
3928
3929 let mem_scope = resolve_constant(ctx.gctx(), &mem_scope_const.inner)
3930 .ok_or(Error::InvalidBarrierScope(mem_scope_id))?;
3931 let semantics = resolve_constant(ctx.gctx(), &semantics_const.inner)
3932 .ok_or(Error::InvalidBarrierMemorySemantics(semantics_id))?;
3933
3934 let mut flags = if mem_scope == spirv::Scope::Device as u32 {
3935 crate::Barrier::STORAGE
3936 } else if mem_scope == spirv::Scope::Workgroup as u32 {
3937 crate::Barrier::WORK_GROUP
3938 } else if mem_scope == spirv::Scope::Subgroup as u32 {
3939 crate::Barrier::SUB_GROUP
3940 } else {
3941 crate::Barrier::empty()
3942 };
3943 flags.set(
3944 crate::Barrier::STORAGE,
3945 semantics & spirv::MemorySemantics::UNIFORM_MEMORY.bits() != 0,
3946 );
3947 flags.set(
3948 crate::Barrier::WORK_GROUP,
3949 semantics & (spirv::MemorySemantics::WORKGROUP_MEMORY).bits() != 0,
3950 );
3951 flags.set(
3952 crate::Barrier::SUB_GROUP,
3953 semantics & spirv::MemorySemantics::SUBGROUP_MEMORY.bits() != 0,
3954 );
3955 flags.set(
3956 crate::Barrier::TEXTURE,
3957 semantics & spirv::MemorySemantics::IMAGE_MEMORY.bits() != 0,
3958 );
3959
3960 block.extend(emitter.finish(ctx.expressions));
3961 block.push(crate::Statement::MemoryBarrier(flags), span);
3962 emitter.start(ctx.expressions);
3963 }
3964 Op::CopyObject => {
3965 inst.expect(4)?;
3966 let result_type_id = self.next()?;
3967 let result_id = self.next()?;
3968 let operand_id = self.next()?;
3969
3970 let lookup = self.lookup_expression.lookup(operand_id)?;
3971 let handle = get_expr_handle!(operand_id, lookup);
3972
3973 self.lookup_expression.insert(
3974 result_id,
3975 LookupExpression {
3976 handle,
3977 type_id: result_type_id,
3978 block_id,
3979 },
3980 );
3981 }
3982 Op::GroupNonUniformBallot => {
3983 inst.expect(5)?;
3984 block.extend(emitter.finish(ctx.expressions));
3985 let result_type_id = self.next()?;
3986 let result_id = self.next()?;
3987 let exec_scope_id = self.next()?;
3988 let predicate_id = self.next()?;
3989
3990 let exec_scope_const = self.lookup_constant.lookup(exec_scope_id)?;
3991 let _exec_scope = resolve_constant(ctx.gctx(), &exec_scope_const.inner)
3992 .filter(|exec_scope| *exec_scope == spirv::Scope::Subgroup as u32)
3993 .ok_or(Error::InvalidBarrierScope(exec_scope_id))?;
3994
3995 let predicate = if self
3996 .lookup_constant
3997 .lookup(predicate_id)
3998 .ok()
3999 .filter(|predicate_const| match predicate_const.inner {
4000 Constant::Constant(constant) => matches!(
4001 ctx.gctx().global_expressions[ctx.gctx().constants[constant].init],
4002 crate::Expression::Literal(crate::Literal::Bool(true)),
4003 ),
4004 Constant::Override(_) => false,
4005 })
4006 .is_some()
4007 {
4008 None
4009 } else {
4010 let predicate_lookup = self.lookup_expression.lookup(predicate_id)?;
4011 let predicate_handle = get_expr_handle!(predicate_id, predicate_lookup);
4012 Some(predicate_handle)
4013 };
4014
4015 let result_handle = ctx
4016 .expressions
4017 .append(crate::Expression::SubgroupBallotResult, span);
4018 self.lookup_expression.insert(
4019 result_id,
4020 LookupExpression {
4021 handle: result_handle,
4022 type_id: result_type_id,
4023 block_id,
4024 },
4025 );
4026
4027 block.push(
4028 crate::Statement::SubgroupBallot {
4029 result: result_handle,
4030 predicate,
4031 },
4032 span,
4033 );
4034 emitter.start(ctx.expressions);
4035 }
4036 Op::GroupNonUniformAll
4037 | Op::GroupNonUniformAny
4038 | Op::GroupNonUniformIAdd
4039 | Op::GroupNonUniformFAdd
4040 | Op::GroupNonUniformIMul
4041 | Op::GroupNonUniformFMul
4042 | Op::GroupNonUniformSMax
4043 | Op::GroupNonUniformUMax
4044 | Op::GroupNonUniformFMax
4045 | Op::GroupNonUniformSMin
4046 | Op::GroupNonUniformUMin
4047 | Op::GroupNonUniformFMin
4048 | Op::GroupNonUniformBitwiseAnd
4049 | Op::GroupNonUniformBitwiseOr
4050 | Op::GroupNonUniformBitwiseXor
4051 | Op::GroupNonUniformLogicalAnd
4052 | Op::GroupNonUniformLogicalOr
4053 | Op::GroupNonUniformLogicalXor => {
4054 block.extend(emitter.finish(ctx.expressions));
4055 inst.expect(
4056 if matches!(inst.op, Op::GroupNonUniformAll | Op::GroupNonUniformAny) {
4057 5
4058 } else {
4059 6
4060 },
4061 )?;
4062 let result_type_id = self.next()?;
4063 let result_id = self.next()?;
4064 let exec_scope_id = self.next()?;
4065 let collective_op_id = match inst.op {
4066 Op::GroupNonUniformAll | Op::GroupNonUniformAny => {
4067 crate::CollectiveOperation::Reduce
4068 }
4069 _ => {
4070 let group_op_id = self.next()?;
4071 match spirv::GroupOperation::from_u32(group_op_id) {
4072 Some(spirv::GroupOperation::Reduce) => {
4073 crate::CollectiveOperation::Reduce
4074 }
4075 Some(spirv::GroupOperation::InclusiveScan) => {
4076 crate::CollectiveOperation::InclusiveScan
4077 }
4078 Some(spirv::GroupOperation::ExclusiveScan) => {
4079 crate::CollectiveOperation::ExclusiveScan
4080 }
4081 _ => return Err(Error::UnsupportedGroupOperation(group_op_id)),
4082 }
4083 }
4084 };
4085 let argument_id = self.next()?;
4086
4087 let argument_lookup = self.lookup_expression.lookup(argument_id)?;
4088 let argument_handle = get_expr_handle!(argument_id, argument_lookup);
4089
4090 let exec_scope_const = self.lookup_constant.lookup(exec_scope_id)?;
4091 let _exec_scope = resolve_constant(ctx.gctx(), &exec_scope_const.inner)
4092 .filter(|exec_scope| *exec_scope == spirv::Scope::Subgroup as u32)
4093 .ok_or(Error::InvalidBarrierScope(exec_scope_id))?;
4094
4095 let op_id = match inst.op {
4096 Op::GroupNonUniformAll => crate::SubgroupOperation::All,
4097 Op::GroupNonUniformAny => crate::SubgroupOperation::Any,
4098 Op::GroupNonUniformIAdd | Op::GroupNonUniformFAdd => {
4099 crate::SubgroupOperation::Add
4100 }
4101 Op::GroupNonUniformIMul | Op::GroupNonUniformFMul => {
4102 crate::SubgroupOperation::Mul
4103 }
4104 Op::GroupNonUniformSMax
4105 | Op::GroupNonUniformUMax
4106 | Op::GroupNonUniformFMax => crate::SubgroupOperation::Max,
4107 Op::GroupNonUniformSMin
4108 | Op::GroupNonUniformUMin
4109 | Op::GroupNonUniformFMin => crate::SubgroupOperation::Min,
4110 Op::GroupNonUniformBitwiseAnd | Op::GroupNonUniformLogicalAnd => {
4111 crate::SubgroupOperation::And
4112 }
4113 Op::GroupNonUniformBitwiseOr | Op::GroupNonUniformLogicalOr => {
4114 crate::SubgroupOperation::Or
4115 }
4116 Op::GroupNonUniformBitwiseXor | Op::GroupNonUniformLogicalXor => {
4117 crate::SubgroupOperation::Xor
4118 }
4119 _ => unreachable!(),
4120 };
4121
4122 let result_type = self.lookup_type.lookup(result_type_id)?;
4123
4124 let result_handle = ctx.expressions.append(
4125 crate::Expression::SubgroupOperationResult {
4126 ty: result_type.handle,
4127 },
4128 span,
4129 );
4130 self.lookup_expression.insert(
4131 result_id,
4132 LookupExpression {
4133 handle: result_handle,
4134 type_id: result_type_id,
4135 block_id,
4136 },
4137 );
4138
4139 block.push(
4140 crate::Statement::SubgroupCollectiveOperation {
4141 result: result_handle,
4142 op: op_id,
4143 collective_op: collective_op_id,
4144 argument: argument_handle,
4145 },
4146 span,
4147 );
4148 emitter.start(ctx.expressions);
4149 }
4150 Op::GroupNonUniformBroadcastFirst
4151 | Op::GroupNonUniformBroadcast
4152 | Op::GroupNonUniformShuffle
4153 | Op::GroupNonUniformShuffleDown
4154 | Op::GroupNonUniformShuffleUp
4155 | Op::GroupNonUniformShuffleXor
4156 | Op::GroupNonUniformQuadBroadcast => {
4157 inst.expect(if matches!(inst.op, Op::GroupNonUniformBroadcastFirst) {
4158 5
4159 } else {
4160 6
4161 })?;
4162 block.extend(emitter.finish(ctx.expressions));
4163 let result_type_id = self.next()?;
4164 let result_id = self.next()?;
4165 let exec_scope_id = self.next()?;
4166 let argument_id = self.next()?;
4167
4168 let argument_lookup = self.lookup_expression.lookup(argument_id)?;
4169 let argument_handle = get_expr_handle!(argument_id, argument_lookup);
4170
4171 let exec_scope_const = self.lookup_constant.lookup(exec_scope_id)?;
4172 let _exec_scope = resolve_constant(ctx.gctx(), &exec_scope_const.inner)
4173 .filter(|exec_scope| *exec_scope == spirv::Scope::Subgroup as u32)
4174 .ok_or(Error::InvalidBarrierScope(exec_scope_id))?;
4175
4176 let mode = if matches!(inst.op, Op::GroupNonUniformBroadcastFirst) {
4177 crate::GatherMode::BroadcastFirst
4178 } else {
4179 let index_id = self.next()?;
4180 let index_lookup = self.lookup_expression.lookup(index_id)?;
4181 let index_handle = get_expr_handle!(index_id, index_lookup);
4182 match inst.op {
4183 Op::GroupNonUniformBroadcast => {
4184 crate::GatherMode::Broadcast(index_handle)
4185 }
4186 Op::GroupNonUniformShuffle => crate::GatherMode::Shuffle(index_handle),
4187 Op::GroupNonUniformShuffleDown => {
4188 crate::GatherMode::ShuffleDown(index_handle)
4189 }
4190 Op::GroupNonUniformShuffleUp => {
4191 crate::GatherMode::ShuffleUp(index_handle)
4192 }
4193 Op::GroupNonUniformShuffleXor => {
4194 crate::GatherMode::ShuffleXor(index_handle)
4195 }
4196 Op::GroupNonUniformQuadBroadcast => {
4197 crate::GatherMode::QuadBroadcast(index_handle)
4198 }
4199 _ => unreachable!(),
4200 }
4201 };
4202
4203 let result_type = self.lookup_type.lookup(result_type_id)?;
4204
4205 let result_handle = ctx.expressions.append(
4206 crate::Expression::SubgroupOperationResult {
4207 ty: result_type.handle,
4208 },
4209 span,
4210 );
4211 self.lookup_expression.insert(
4212 result_id,
4213 LookupExpression {
4214 handle: result_handle,
4215 type_id: result_type_id,
4216 block_id,
4217 },
4218 );
4219
4220 block.push(
4221 crate::Statement::SubgroupGather {
4222 result: result_handle,
4223 mode,
4224 argument: argument_handle,
4225 },
4226 span,
4227 );
4228 emitter.start(ctx.expressions);
4229 }
4230 Op::GroupNonUniformQuadSwap => {
4231 inst.expect(6)?;
4232 block.extend(emitter.finish(ctx.expressions));
4233 let result_type_id = self.next()?;
4234 let result_id = self.next()?;
4235 let exec_scope_id = self.next()?;
4236 let argument_id = self.next()?;
4237 let direction_id = self.next()?;
4238
4239 let argument_lookup = self.lookup_expression.lookup(argument_id)?;
4240 let argument_handle = get_expr_handle!(argument_id, argument_lookup);
4241
4242 let exec_scope_const = self.lookup_constant.lookup(exec_scope_id)?;
4243 let _exec_scope = resolve_constant(ctx.gctx(), &exec_scope_const.inner)
4244 .filter(|exec_scope| *exec_scope == spirv::Scope::Subgroup as u32)
4245 .ok_or(Error::InvalidBarrierScope(exec_scope_id))?;
4246
4247 let direction_const = self.lookup_constant.lookup(direction_id)?;
4248 let direction_const = resolve_constant(ctx.gctx(), &direction_const.inner)
4249 .ok_or(Error::InvalidOperand)?;
4250 let direction = match direction_const {
4251 0 => crate::Direction::X,
4252 1 => crate::Direction::Y,
4253 2 => crate::Direction::Diagonal,
4254 _ => unreachable!(),
4255 };
4256
4257 let result_type = self.lookup_type.lookup(result_type_id)?;
4258
4259 let result_handle = ctx.expressions.append(
4260 crate::Expression::SubgroupOperationResult {
4261 ty: result_type.handle,
4262 },
4263 span,
4264 );
4265 self.lookup_expression.insert(
4266 result_id,
4267 LookupExpression {
4268 handle: result_handle,
4269 type_id: result_type_id,
4270 block_id,
4271 },
4272 );
4273
4274 block.push(
4275 crate::Statement::SubgroupGather {
4276 mode: crate::GatherMode::QuadSwap(direction),
4277 result: result_handle,
4278 argument: argument_handle,
4279 },
4280 span,
4281 );
4282 emitter.start(ctx.expressions);
4283 }
4284 Op::AtomicLoad => {
4285 inst.expect(6)?;
4286 let start = self.data_offset;
4287 let result_type_id = self.next()?;
4288 let result_id = self.next()?;
4289 let pointer_id = self.next()?;
4290 let _scope_id = self.next()?;
4291 let _memory_semantics_id = self.next()?;
4292 let span = self.span_from_with_op(start);
4293
4294 log::trace!("\t\t\tlooking up expr {pointer_id:?}");
4295 let p_lexp_handle =
4296 get_expr_handle!(pointer_id, self.lookup_expression.lookup(pointer_id)?);
4297
4298 let expr = crate::Expression::Load {
4300 pointer: p_lexp_handle,
4301 };
4302 let handle = ctx.expressions.append(expr, span);
4303 self.lookup_expression.insert(
4304 result_id,
4305 LookupExpression {
4306 handle,
4307 type_id: result_type_id,
4308 block_id,
4309 },
4310 );
4311
4312 self.record_atomic_access(ctx, p_lexp_handle)?;
4314 }
4315 Op::AtomicStore => {
4316 inst.expect(5)?;
4317 let start = self.data_offset;
4318 let pointer_id = self.next()?;
4319 let _scope_id = self.next()?;
4320 let _memory_semantics_id = self.next()?;
4321 let value_id = self.next()?;
4322 let span = self.span_from_with_op(start);
4323
4324 log::trace!("\t\t\tlooking up pointer expr {pointer_id:?}");
4325 let p_lexp_handle =
4326 get_expr_handle!(pointer_id, self.lookup_expression.lookup(pointer_id)?);
4327
4328 log::trace!("\t\t\tlooking up value expr {pointer_id:?}");
4329 let v_lexp_handle =
4330 get_expr_handle!(value_id, self.lookup_expression.lookup(value_id)?);
4331
4332 block.extend(emitter.finish(ctx.expressions));
4333 let stmt = crate::Statement::Store {
4335 pointer: p_lexp_handle,
4336 value: v_lexp_handle,
4337 };
4338 block.push(stmt, span);
4339 emitter.start(ctx.expressions);
4340
4341 self.record_atomic_access(ctx, p_lexp_handle)?;
4343 }
4344 Op::AtomicIIncrement | Op::AtomicIDecrement => {
4345 inst.expect(6)?;
4346 let start = self.data_offset;
4347 let result_type_id = self.next()?;
4348 let result_id = self.next()?;
4349 let pointer_id = self.next()?;
4350 let _scope_id = self.next()?;
4351 let _memory_semantics_id = self.next()?;
4352 let span = self.span_from_with_op(start);
4353
4354 let (p_exp_h, p_base_ty_h) = self.get_exp_and_base_ty_handles(
4355 pointer_id,
4356 ctx,
4357 &mut emitter,
4358 &mut block,
4359 body_idx,
4360 )?;
4361
4362 block.extend(emitter.finish(ctx.expressions));
4363 let r_lexp_handle = {
4365 let expr = crate::Expression::AtomicResult {
4366 ty: p_base_ty_h,
4367 comparison: false,
4368 };
4369 let handle = ctx.expressions.append(expr, span);
4370 self.lookup_expression.insert(
4371 result_id,
4372 LookupExpression {
4373 handle,
4374 type_id: result_type_id,
4375 block_id,
4376 },
4377 );
4378 handle
4379 };
4380 emitter.start(ctx.expressions);
4381
4382 let one_lexp_handle = make_index_literal(
4384 ctx,
4385 1,
4386 &mut block,
4387 &mut emitter,
4388 p_base_ty_h,
4389 result_type_id,
4390 span,
4391 )?;
4392
4393 let stmt = crate::Statement::Atomic {
4395 pointer: p_exp_h,
4396 fun: match inst.op {
4397 Op::AtomicIIncrement => crate::AtomicFunction::Add,
4398 _ => crate::AtomicFunction::Subtract,
4399 },
4400 value: one_lexp_handle,
4401 result: Some(r_lexp_handle),
4402 };
4403 block.push(stmt, span);
4404
4405 self.record_atomic_access(ctx, p_exp_h)?;
4407 }
4408 Op::AtomicCompareExchange => {
4409 inst.expect(9)?;
4410
4411 let start = self.data_offset;
4412 let span = self.span_from_with_op(start);
4413 let result_type_id = self.next()?;
4414 let result_id = self.next()?;
4415 let pointer_id = self.next()?;
4416 let _memory_scope_id = self.next()?;
4417 let _equal_memory_semantics_id = self.next()?;
4418 let _unequal_memory_semantics_id = self.next()?;
4419 let value_id = self.next()?;
4420 let comparator_id = self.next()?;
4421
4422 let (p_exp_h, p_base_ty_h) = self.get_exp_and_base_ty_handles(
4423 pointer_id,
4424 ctx,
4425 &mut emitter,
4426 &mut block,
4427 body_idx,
4428 )?;
4429
4430 log::trace!("\t\t\tlooking up value expr {value_id:?}");
4431 let v_lexp_handle =
4432 get_expr_handle!(value_id, self.lookup_expression.lookup(value_id)?);
4433
4434 log::trace!("\t\t\tlooking up comparator expr {value_id:?}");
4435 let c_lexp_handle = get_expr_handle!(
4436 comparator_id,
4437 self.lookup_expression.lookup(comparator_id)?
4438 );
4439
4440 let crate::TypeInner::Scalar(scalar) = ctx.module.types[p_base_ty_h].inner
4444 else {
4445 return Err(
4446 crate::front::atomic_upgrade::Error::CompareExchangeNonScalarBaseType
4447 .into(),
4448 );
4449 };
4450
4451 let atomic_result_struct_ty_h = ctx.module.generate_predeclared_type(
4453 crate::PredeclaredType::AtomicCompareExchangeWeakResult(scalar),
4454 );
4455
4456 block.extend(emitter.finish(ctx.expressions));
4457
4458 let atomic_lexp_handle = {
4460 let expr = crate::Expression::AtomicResult {
4461 ty: atomic_result_struct_ty_h,
4462 comparison: true,
4463 };
4464 ctx.expressions.append(expr, span)
4465 };
4466
4467 {
4471 let expr = crate::Expression::AccessIndex {
4472 base: atomic_lexp_handle,
4473 index: 0,
4474 };
4475 let handle = ctx.expressions.append(expr, span);
4476 let _ = self.lookup_expression.insert(
4478 result_id,
4479 LookupExpression {
4480 handle,
4481 type_id: result_type_id,
4482 block_id,
4483 },
4484 );
4485 }
4486
4487 emitter.start(ctx.expressions);
4488
4489 let stmt = crate::Statement::Atomic {
4491 pointer: p_exp_h,
4492 fun: crate::AtomicFunction::Exchange {
4493 compare: Some(c_lexp_handle),
4494 },
4495 value: v_lexp_handle,
4496 result: Some(atomic_lexp_handle),
4497 };
4498 block.push(stmt, span);
4499
4500 self.record_atomic_access(ctx, p_exp_h)?;
4502 }
4503 Op::AtomicExchange
4504 | Op::AtomicIAdd
4505 | Op::AtomicISub
4506 | Op::AtomicSMin
4507 | Op::AtomicUMin
4508 | Op::AtomicSMax
4509 | Op::AtomicUMax
4510 | Op::AtomicAnd
4511 | Op::AtomicOr
4512 | Op::AtomicXor
4513 | Op::AtomicFAddEXT => self.parse_atomic_expr_with_value(
4514 inst,
4515 &mut emitter,
4516 ctx,
4517 &mut block,
4518 block_id,
4519 body_idx,
4520 match inst.op {
4521 Op::AtomicExchange => crate::AtomicFunction::Exchange { compare: None },
4522 Op::AtomicIAdd | Op::AtomicFAddEXT => crate::AtomicFunction::Add,
4523 Op::AtomicISub => crate::AtomicFunction::Subtract,
4524 Op::AtomicSMin => crate::AtomicFunction::Min,
4525 Op::AtomicUMin => crate::AtomicFunction::Min,
4526 Op::AtomicSMax => crate::AtomicFunction::Max,
4527 Op::AtomicUMax => crate::AtomicFunction::Max,
4528 Op::AtomicAnd => crate::AtomicFunction::And,
4529 Op::AtomicOr => crate::AtomicFunction::InclusiveOr,
4530 Op::AtomicXor => crate::AtomicFunction::ExclusiveOr,
4531 _ => unreachable!(),
4532 },
4533 )?,
4534
4535 _ => {
4536 return Err(Error::UnsupportedInstruction(self.state, inst.op));
4537 }
4538 }
4539 };
4540
4541 block.extend(emitter.finish(ctx.expressions));
4542 if let Some(stmt) = terminator {
4543 block.push(stmt, crate::Span::default());
4544 }
4545
4546 ctx.blocks.insert(block_id, block);
4549 let body = &mut ctx.bodies[body_idx];
4550 body.data.push(BodyFragment::BlockId(block_id));
4551 Ok(())
4552 }
4553
4554 fn make_expression_storage(
4555 &mut self,
4556 globals: &Arena<crate::GlobalVariable>,
4557 constants: &Arena<crate::Constant>,
4558 overrides: &Arena<crate::Override>,
4559 ) -> Arena<crate::Expression> {
4560 let mut expressions = Arena::new();
4561 #[allow(clippy::panic)]
4562 {
4563 assert!(self.lookup_expression.is_empty());
4564 }
4565 for (&id, var) in self.lookup_variable.iter() {
4567 let span = globals.get_span(var.handle);
4568 let handle = expressions.append(crate::Expression::GlobalVariable(var.handle), span);
4569 self.lookup_expression.insert(
4570 id,
4571 LookupExpression {
4572 type_id: var.type_id,
4573 handle,
4574 block_id: 0,
4578 },
4579 );
4580 }
4581 for (&id, con) in self.lookup_constant.iter() {
4583 let (expr, span) = match con.inner {
4584 Constant::Constant(c) => (crate::Expression::Constant(c), constants.get_span(c)),
4585 Constant::Override(o) => (crate::Expression::Override(o), overrides.get_span(o)),
4586 };
4587 let handle = expressions.append(expr, span);
4588 self.lookup_expression.insert(
4589 id,
4590 LookupExpression {
4591 type_id: con.type_id,
4592 handle,
4593 block_id: 0,
4597 },
4598 );
4599 }
4600 expressions
4602 }
4603
4604 fn switch(&mut self, state: ModuleState, op: spirv::Op) -> Result<(), Error> {
4605 if state < self.state {
4606 Err(Error::UnsupportedInstruction(self.state, op))
4607 } else {
4608 self.state = state;
4609 Ok(())
4610 }
4611 }
4612
4613 fn patch_statements(
4616 &mut self,
4617 statements: &mut crate::Block,
4618 expressions: &mut Arena<crate::Expression>,
4619 fun_parameter_sampling: &mut [image::SamplingFlags],
4620 ) -> Result<(), Error> {
4621 use crate::Statement as S;
4622 let mut i = 0usize;
4623 while i < statements.len() {
4624 match statements[i] {
4625 S::Emit(_) => {}
4626 S::Block(ref mut block) => {
4627 self.patch_statements(block, expressions, fun_parameter_sampling)?;
4628 }
4629 S::If {
4630 condition: _,
4631 ref mut accept,
4632 ref mut reject,
4633 } => {
4634 self.patch_statements(reject, expressions, fun_parameter_sampling)?;
4635 self.patch_statements(accept, expressions, fun_parameter_sampling)?;
4636 }
4637 S::Switch {
4638 selector: _,
4639 ref mut cases,
4640 } => {
4641 for case in cases.iter_mut() {
4642 self.patch_statements(&mut case.body, expressions, fun_parameter_sampling)?;
4643 }
4644 }
4645 S::Loop {
4646 ref mut body,
4647 ref mut continuing,
4648 break_if: _,
4649 } => {
4650 self.patch_statements(body, expressions, fun_parameter_sampling)?;
4651 self.patch_statements(continuing, expressions, fun_parameter_sampling)?;
4652 }
4653 S::Break
4654 | S::Continue
4655 | S::Return { .. }
4656 | S::Kill
4657 | S::ControlBarrier(_)
4658 | S::MemoryBarrier(_)
4659 | S::Store { .. }
4660 | S::ImageStore { .. }
4661 | S::Atomic { .. }
4662 | S::ImageAtomic { .. }
4663 | S::RayQuery { .. }
4664 | S::SubgroupBallot { .. }
4665 | S::SubgroupCollectiveOperation { .. }
4666 | S::SubgroupGather { .. } => {}
4667 S::Call {
4668 function: ref mut callee,
4669 ref arguments,
4670 ..
4671 } => {
4672 let fun_id = self.deferred_function_calls[callee.index()];
4673 let fun_lookup = self.lookup_function.lookup(fun_id)?;
4674 *callee = fun_lookup.handle;
4675
4676 for (arg_index, arg) in arguments.iter().enumerate() {
4678 let flags = match fun_lookup.parameters_sampling.get(arg_index) {
4679 Some(&flags) if !flags.is_empty() => flags,
4680 _ => continue,
4681 };
4682
4683 match expressions[*arg] {
4684 crate::Expression::GlobalVariable(handle) => {
4685 if let Some(sampling) = self.handle_sampling.get_mut(&handle) {
4686 *sampling |= flags
4687 }
4688 }
4689 crate::Expression::FunctionArgument(i) => {
4690 fun_parameter_sampling[i as usize] |= flags;
4691 }
4692 ref other => return Err(Error::InvalidGlobalVar(other.clone())),
4693 }
4694 }
4695 }
4696 S::WorkGroupUniformLoad { .. } => unreachable!(),
4697 }
4698 i += 1;
4699 }
4700 Ok(())
4701 }
4702
4703 fn patch_function(
4704 &mut self,
4705 handle: Option<Handle<crate::Function>>,
4706 fun: &mut crate::Function,
4707 ) -> Result<(), Error> {
4708 let (fun_id, mut parameters_sampling) = match handle {
4710 Some(h) => {
4711 let (&fun_id, lookup) = self
4712 .lookup_function
4713 .iter_mut()
4714 .find(|&(_, ref lookup)| lookup.handle == h)
4715 .unwrap();
4716 (fun_id, mem::take(&mut lookup.parameters_sampling))
4717 }
4718 None => (0, Vec::new()),
4719 };
4720
4721 for (_, expr) in fun.expressions.iter_mut() {
4722 if let crate::Expression::CallResult(ref mut function) = *expr {
4723 let fun_id = self.deferred_function_calls[function.index()];
4724 *function = self.lookup_function.lookup(fun_id)?.handle;
4725 }
4726 }
4727
4728 self.patch_statements(
4729 &mut fun.body,
4730 &mut fun.expressions,
4731 &mut parameters_sampling,
4732 )?;
4733
4734 if let Some(lookup) = self.lookup_function.get_mut(&fun_id) {
4735 lookup.parameters_sampling = parameters_sampling;
4736 }
4737 Ok(())
4738 }
4739
4740 pub fn parse(mut self) -> Result<crate::Module, Error> {
4741 let mut module = {
4742 if self.next()? != spirv::MAGIC_NUMBER {
4743 return Err(Error::InvalidHeader);
4744 }
4745 let version_raw = self.next()?;
4746 let generator = self.next()?;
4747 let _bound = self.next()?;
4748 let _schema = self.next()?;
4749 log::info!("Generated by {generator} version {version_raw:x}");
4750 crate::Module::default()
4751 };
4752
4753 self.layouter.clear();
4754 self.dummy_functions = Arena::new();
4755 self.lookup_function.clear();
4756 self.function_call_graph.clear();
4757
4758 loop {
4759 use spirv::Op;
4760
4761 let inst = match self.next_inst() {
4762 Ok(inst) => inst,
4763 Err(Error::IncompleteData) => break,
4764 Err(other) => return Err(other),
4765 };
4766 log::debug!("\t{:?} [{}]", inst.op, inst.wc);
4767
4768 match inst.op {
4769 Op::Capability => self.parse_capability(inst),
4770 Op::Extension => self.parse_extension(inst),
4771 Op::ExtInstImport => self.parse_ext_inst_import(inst),
4772 Op::MemoryModel => self.parse_memory_model(inst),
4773 Op::EntryPoint => self.parse_entry_point(inst),
4774 Op::ExecutionMode => self.parse_execution_mode(inst),
4775 Op::String => self.parse_string(inst),
4776 Op::Source => self.parse_source(inst),
4777 Op::SourceExtension => self.parse_source_extension(inst),
4778 Op::Name => self.parse_name(inst),
4779 Op::MemberName => self.parse_member_name(inst),
4780 Op::ModuleProcessed => self.parse_module_processed(inst),
4781 Op::Decorate => self.parse_decorate(inst),
4782 Op::MemberDecorate => self.parse_member_decorate(inst),
4783 Op::TypeVoid => self.parse_type_void(inst),
4784 Op::TypeBool => self.parse_type_bool(inst, &mut module),
4785 Op::TypeInt => self.parse_type_int(inst, &mut module),
4786 Op::TypeFloat => self.parse_type_float(inst, &mut module),
4787 Op::TypeVector => self.parse_type_vector(inst, &mut module),
4788 Op::TypeMatrix => self.parse_type_matrix(inst, &mut module),
4789 Op::TypeFunction => self.parse_type_function(inst),
4790 Op::TypePointer => self.parse_type_pointer(inst, &mut module),
4791 Op::TypeArray => self.parse_type_array(inst, &mut module),
4792 Op::TypeRuntimeArray => self.parse_type_runtime_array(inst, &mut module),
4793 Op::TypeStruct => self.parse_type_struct(inst, &mut module),
4794 Op::TypeImage => self.parse_type_image(inst, &mut module),
4795 Op::TypeSampledImage => self.parse_type_sampled_image(inst),
4796 Op::TypeSampler => self.parse_type_sampler(inst, &mut module),
4797 Op::Constant | Op::SpecConstant => self.parse_constant(inst, &mut module),
4798 Op::ConstantComposite | Op::SpecConstantComposite => {
4799 self.parse_composite_constant(inst, &mut module)
4800 }
4801 Op::ConstantNull | Op::Undef => self.parse_null_constant(inst, &mut module),
4802 Op::ConstantTrue | Op::SpecConstantTrue => {
4803 self.parse_bool_constant(inst, true, &mut module)
4804 }
4805 Op::ConstantFalse | Op::SpecConstantFalse => {
4806 self.parse_bool_constant(inst, false, &mut module)
4807 }
4808 Op::Variable => self.parse_global_variable(inst, &mut module),
4809 Op::Function => {
4810 self.switch(ModuleState::Function, inst.op)?;
4811 inst.expect(5)?;
4812 self.parse_function(&mut module)
4813 }
4814 _ => Err(Error::UnsupportedInstruction(self.state, inst.op)), }?;
4816 }
4817
4818 if !self.upgrade_atomics.is_empty() {
4819 log::info!("Upgrading atomic pointers...");
4820 module.upgrade_atomics(&self.upgrade_atomics)?;
4821 }
4822
4823 for (ep, fun_id) in mem::take(&mut self.deferred_entry_points) {
4826 self.process_entry_point(&mut module, ep, fun_id)?;
4827 }
4828
4829 log::info!("Patching...");
4830 {
4831 let mut nodes = petgraph::algo::toposort(&self.function_call_graph, None)
4832 .map_err(|cycle| Error::FunctionCallCycle(cycle.node_id()))?;
4833 nodes.reverse(); let mut functions = mem::take(&mut module.functions);
4835 for fun_id in nodes {
4836 if fun_id > !(functions.len() as u32) {
4837 continue;
4839 }
4840 let lookup = self.lookup_function.get_mut(&fun_id).unwrap();
4841 let fun = mem::take(&mut functions[lookup.handle]);
4843 lookup.handle = module
4845 .functions
4846 .append(fun, functions.get_span(lookup.handle));
4847 }
4848 }
4849 for (handle, fun) in module.functions.iter_mut() {
4851 self.patch_function(Some(handle), fun)?;
4852 }
4853 for ep in module.entry_points.iter_mut() {
4854 self.patch_function(None, &mut ep.function)?;
4855 }
4856
4857 for (handle, flags) in self.handle_sampling.drain() {
4859 if !image::patch_comparison_type(
4860 flags,
4861 module.global_variables.get_mut(handle),
4862 &mut module.types,
4863 ) {
4864 return Err(Error::InconsistentComparisonSampling(handle));
4865 }
4866 }
4867
4868 if !self.future_decor.is_empty() {
4869 log::warn!("Unused item decorations: {:?}", self.future_decor);
4870 self.future_decor.clear();
4871 }
4872 if !self.future_member_decor.is_empty() {
4873 log::warn!("Unused member decorations: {:?}", self.future_member_decor);
4874 self.future_member_decor.clear();
4875 }
4876
4877 Ok(module)
4878 }
4879
4880 fn parse_capability(&mut self, inst: Instruction) -> Result<(), Error> {
4881 self.switch(ModuleState::Capability, inst.op)?;
4882 inst.expect(2)?;
4883 let capability = self.next()?;
4884 let cap =
4885 spirv::Capability::from_u32(capability).ok_or(Error::UnknownCapability(capability))?;
4886 if !SUPPORTED_CAPABILITIES.contains(&cap) {
4887 if self.options.strict_capabilities {
4888 return Err(Error::UnsupportedCapability(cap));
4889 } else {
4890 log::warn!("Unknown capability {cap:?}");
4891 }
4892 }
4893 Ok(())
4894 }
4895
4896 fn parse_extension(&mut self, inst: Instruction) -> Result<(), Error> {
4897 self.switch(ModuleState::Extension, inst.op)?;
4898 inst.expect_at_least(2)?;
4899 let (name, left) = self.next_string(inst.wc - 1)?;
4900 if left != 0 {
4901 return Err(Error::InvalidOperand);
4902 }
4903 if !SUPPORTED_EXTENSIONS.contains(&name.as_str()) {
4904 return Err(Error::UnsupportedExtension(name));
4905 }
4906 Ok(())
4907 }
4908
4909 fn parse_ext_inst_import(&mut self, inst: Instruction) -> Result<(), Error> {
4910 self.switch(ModuleState::Extension, inst.op)?;
4911 inst.expect_at_least(3)?;
4912 let result_id = self.next()?;
4913 let (name, left) = self.next_string(inst.wc - 2)?;
4914 if left != 0 {
4915 return Err(Error::InvalidOperand);
4916 }
4917 if !SUPPORTED_EXT_SETS.contains(&name.as_str()) {
4918 return Err(Error::UnsupportedExtSet(name));
4919 }
4920 self.ext_glsl_id = Some(result_id);
4921 Ok(())
4922 }
4923
4924 fn parse_memory_model(&mut self, inst: Instruction) -> Result<(), Error> {
4925 self.switch(ModuleState::MemoryModel, inst.op)?;
4926 inst.expect(3)?;
4927 let _addressing_model = self.next()?;
4928 let _memory_model = self.next()?;
4929 Ok(())
4930 }
4931
4932 fn parse_entry_point(&mut self, inst: Instruction) -> Result<(), Error> {
4933 self.switch(ModuleState::EntryPoint, inst.op)?;
4934 inst.expect_at_least(4)?;
4935 let exec_model = self.next()?;
4936 let exec_model = spirv::ExecutionModel::from_u32(exec_model)
4937 .ok_or(Error::UnsupportedExecutionModel(exec_model))?;
4938 let function_id = self.next()?;
4939 let (name, left) = self.next_string(inst.wc - 3)?;
4940 let ep = EntryPoint {
4941 stage: match exec_model {
4942 spirv::ExecutionModel::Vertex => crate::ShaderStage::Vertex,
4943 spirv::ExecutionModel::Fragment => crate::ShaderStage::Fragment,
4944 spirv::ExecutionModel::GLCompute => crate::ShaderStage::Compute,
4945 spirv::ExecutionModel::TaskEXT => crate::ShaderStage::Task,
4946 spirv::ExecutionModel::MeshEXT => crate::ShaderStage::Mesh,
4947 _ => return Err(Error::UnsupportedExecutionModel(exec_model as u32)),
4948 },
4949 name,
4950 early_depth_test: None,
4951 workgroup_size: [0; 3],
4952 variable_ids: self.data.by_ref().take(left as usize).collect(),
4953 };
4954 self.lookup_entry_point.insert(function_id, ep);
4955 Ok(())
4956 }
4957
4958 fn parse_execution_mode(&mut self, inst: Instruction) -> Result<(), Error> {
4959 use spirv::ExecutionMode;
4960
4961 self.switch(ModuleState::ExecutionMode, inst.op)?;
4962 inst.expect_at_least(3)?;
4963
4964 let ep_id = self.next()?;
4965 let mode_id = self.next()?;
4966 let args: Vec<spirv::Word> = self.data.by_ref().take(inst.wc as usize - 3).collect();
4967
4968 let ep = self
4969 .lookup_entry_point
4970 .get_mut(&ep_id)
4971 .ok_or(Error::InvalidId(ep_id))?;
4972 let mode =
4973 ExecutionMode::from_u32(mode_id).ok_or(Error::UnsupportedExecutionMode(mode_id))?;
4974
4975 match mode {
4976 ExecutionMode::EarlyFragmentTests => {
4977 ep.early_depth_test = Some(crate::EarlyDepthTest::Force);
4978 }
4979 ExecutionMode::DepthUnchanged => {
4980 if let &mut Some(ref mut early_depth_test) = &mut ep.early_depth_test {
4981 if let &mut crate::EarlyDepthTest::Allow {
4982 ref mut conservative,
4983 } = early_depth_test
4984 {
4985 *conservative = crate::ConservativeDepth::Unchanged;
4986 }
4987 } else {
4988 ep.early_depth_test = Some(crate::EarlyDepthTest::Allow {
4989 conservative: crate::ConservativeDepth::Unchanged,
4990 });
4991 }
4992 }
4993 ExecutionMode::DepthGreater => {
4994 if let &mut Some(ref mut early_depth_test) = &mut ep.early_depth_test {
4995 if let &mut crate::EarlyDepthTest::Allow {
4996 ref mut conservative,
4997 } = early_depth_test
4998 {
4999 *conservative = crate::ConservativeDepth::GreaterEqual;
5000 }
5001 } else {
5002 ep.early_depth_test = Some(crate::EarlyDepthTest::Allow {
5003 conservative: crate::ConservativeDepth::GreaterEqual,
5004 });
5005 }
5006 }
5007 ExecutionMode::DepthLess => {
5008 if let &mut Some(ref mut early_depth_test) = &mut ep.early_depth_test {
5009 if let &mut crate::EarlyDepthTest::Allow {
5010 ref mut conservative,
5011 } = early_depth_test
5012 {
5013 *conservative = crate::ConservativeDepth::LessEqual;
5014 }
5015 } else {
5016 ep.early_depth_test = Some(crate::EarlyDepthTest::Allow {
5017 conservative: crate::ConservativeDepth::LessEqual,
5018 });
5019 }
5020 }
5021 ExecutionMode::DepthReplacing => {
5022 }
5024 ExecutionMode::OriginUpperLeft => {
5025 }
5027 ExecutionMode::LocalSize => {
5028 ep.workgroup_size = [args[0], args[1], args[2]];
5029 }
5030 _ => {
5031 return Err(Error::UnsupportedExecutionMode(mode_id));
5032 }
5033 }
5034
5035 Ok(())
5036 }
5037
5038 fn parse_string(&mut self, inst: Instruction) -> Result<(), Error> {
5039 self.switch(ModuleState::Source, inst.op)?;
5040 inst.expect_at_least(3)?;
5041 let _id = self.next()?;
5042 let (_name, _) = self.next_string(inst.wc - 2)?;
5043 Ok(())
5044 }
5045
5046 fn parse_source(&mut self, inst: Instruction) -> Result<(), Error> {
5047 self.switch(ModuleState::Source, inst.op)?;
5048 for _ in 1..inst.wc {
5049 let _ = self.next()?;
5050 }
5051 Ok(())
5052 }
5053
5054 fn parse_source_extension(&mut self, inst: Instruction) -> Result<(), Error> {
5055 self.switch(ModuleState::Source, inst.op)?;
5056 inst.expect_at_least(2)?;
5057 let (_name, _) = self.next_string(inst.wc - 1)?;
5058 Ok(())
5059 }
5060
5061 fn parse_name(&mut self, inst: Instruction) -> Result<(), Error> {
5062 self.switch(ModuleState::Name, inst.op)?;
5063 inst.expect_at_least(3)?;
5064 let id = self.next()?;
5065 let (name, left) = self.next_string(inst.wc - 2)?;
5066 if left != 0 {
5067 return Err(Error::InvalidOperand);
5068 }
5069 self.future_decor.entry(id).or_default().name = Some(name);
5070 Ok(())
5071 }
5072
5073 fn parse_member_name(&mut self, inst: Instruction) -> Result<(), Error> {
5074 self.switch(ModuleState::Name, inst.op)?;
5075 inst.expect_at_least(4)?;
5076 let id = self.next()?;
5077 let member = self.next()?;
5078 let (name, left) = self.next_string(inst.wc - 3)?;
5079 if left != 0 {
5080 return Err(Error::InvalidOperand);
5081 }
5082
5083 self.future_member_decor
5084 .entry((id, member))
5085 .or_default()
5086 .name = Some(name);
5087 Ok(())
5088 }
5089
5090 fn parse_module_processed(&mut self, inst: Instruction) -> Result<(), Error> {
5091 self.switch(ModuleState::Name, inst.op)?;
5092 inst.expect_at_least(2)?;
5093 let (_info, left) = self.next_string(inst.wc - 1)?;
5094 if left != 0 {
5096 return Err(Error::InvalidOperand);
5097 }
5098 Ok(())
5099 }
5100
5101 fn parse_decorate(&mut self, inst: Instruction) -> Result<(), Error> {
5102 self.switch(ModuleState::Annotation, inst.op)?;
5103 inst.expect_at_least(3)?;
5104 let id = self.next()?;
5105 let mut dec = self.future_decor.remove(&id).unwrap_or_default();
5106 self.next_decoration(inst, 2, &mut dec)?;
5107 self.future_decor.insert(id, dec);
5108 Ok(())
5109 }
5110
5111 fn parse_member_decorate(&mut self, inst: Instruction) -> Result<(), Error> {
5112 self.switch(ModuleState::Annotation, inst.op)?;
5113 inst.expect_at_least(4)?;
5114 let id = self.next()?;
5115 let member = self.next()?;
5116
5117 let mut dec = self
5118 .future_member_decor
5119 .remove(&(id, member))
5120 .unwrap_or_default();
5121 self.next_decoration(inst, 3, &mut dec)?;
5122 self.future_member_decor.insert((id, member), dec);
5123 Ok(())
5124 }
5125
5126 fn parse_type_void(&mut self, inst: Instruction) -> Result<(), Error> {
5127 self.switch(ModuleState::Type, inst.op)?;
5128 inst.expect(2)?;
5129 let id = self.next()?;
5130 self.lookup_void_type = Some(id);
5131 Ok(())
5132 }
5133
5134 fn parse_type_bool(
5135 &mut self,
5136 inst: Instruction,
5137 module: &mut crate::Module,
5138 ) -> Result<(), Error> {
5139 let start = self.data_offset;
5140 self.switch(ModuleState::Type, inst.op)?;
5141 inst.expect(2)?;
5142 let id = self.next()?;
5143 let inner = crate::TypeInner::Scalar(crate::Scalar::BOOL);
5144 self.lookup_type.insert(
5145 id,
5146 LookupType {
5147 handle: module.types.insert(
5148 crate::Type {
5149 name: self.future_decor.remove(&id).and_then(|dec| dec.name),
5150 inner,
5151 },
5152 self.span_from_with_op(start),
5153 ),
5154 base_id: None,
5155 },
5156 );
5157 Ok(())
5158 }
5159
5160 fn parse_type_int(
5161 &mut self,
5162 inst: Instruction,
5163 module: &mut crate::Module,
5164 ) -> Result<(), Error> {
5165 let start = self.data_offset;
5166 self.switch(ModuleState::Type, inst.op)?;
5167 inst.expect(4)?;
5168 let id = self.next()?;
5169 let width = self.next()?;
5170 let sign = self.next()?;
5171 let inner = crate::TypeInner::Scalar(crate::Scalar {
5172 kind: match sign {
5173 0 => crate::ScalarKind::Uint,
5174 1 => crate::ScalarKind::Sint,
5175 _ => return Err(Error::InvalidSign(sign)),
5176 },
5177 width: map_width(width)?,
5178 });
5179 self.lookup_type.insert(
5180 id,
5181 LookupType {
5182 handle: module.types.insert(
5183 crate::Type {
5184 name: self.future_decor.remove(&id).and_then(|dec| dec.name),
5185 inner,
5186 },
5187 self.span_from_with_op(start),
5188 ),
5189 base_id: None,
5190 },
5191 );
5192 Ok(())
5193 }
5194
5195 fn parse_type_float(
5196 &mut self,
5197 inst: Instruction,
5198 module: &mut crate::Module,
5199 ) -> Result<(), Error> {
5200 let start = self.data_offset;
5201 self.switch(ModuleState::Type, inst.op)?;
5202 inst.expect(3)?;
5203 let id = self.next()?;
5204 let width = self.next()?;
5205 let inner = crate::TypeInner::Scalar(crate::Scalar::float(map_width(width)?));
5206 self.lookup_type.insert(
5207 id,
5208 LookupType {
5209 handle: module.types.insert(
5210 crate::Type {
5211 name: self.future_decor.remove(&id).and_then(|dec| dec.name),
5212 inner,
5213 },
5214 self.span_from_with_op(start),
5215 ),
5216 base_id: None,
5217 },
5218 );
5219 Ok(())
5220 }
5221
5222 fn parse_type_vector(
5223 &mut self,
5224 inst: Instruction,
5225 module: &mut crate::Module,
5226 ) -> Result<(), Error> {
5227 let start = self.data_offset;
5228 self.switch(ModuleState::Type, inst.op)?;
5229 inst.expect(4)?;
5230 let id = self.next()?;
5231 let type_id = self.next()?;
5232 let type_lookup = self.lookup_type.lookup(type_id)?;
5233 let scalar = match module.types[type_lookup.handle].inner {
5234 crate::TypeInner::Scalar(scalar) => scalar,
5235 _ => return Err(Error::InvalidInnerType(type_id)),
5236 };
5237 let component_count = self.next()?;
5238 let inner = crate::TypeInner::Vector {
5239 size: map_vector_size(component_count)?,
5240 scalar,
5241 };
5242 self.lookup_type.insert(
5243 id,
5244 LookupType {
5245 handle: module.types.insert(
5246 crate::Type {
5247 name: self.future_decor.remove(&id).and_then(|dec| dec.name),
5248 inner,
5249 },
5250 self.span_from_with_op(start),
5251 ),
5252 base_id: Some(type_id),
5253 },
5254 );
5255 Ok(())
5256 }
5257
5258 fn parse_type_matrix(
5259 &mut self,
5260 inst: Instruction,
5261 module: &mut crate::Module,
5262 ) -> Result<(), Error> {
5263 let start = self.data_offset;
5264 self.switch(ModuleState::Type, inst.op)?;
5265 inst.expect(4)?;
5266 let id = self.next()?;
5267 let vector_type_id = self.next()?;
5268 let num_columns = self.next()?;
5269 let decor = self.future_decor.remove(&id);
5270
5271 let vector_type_lookup = self.lookup_type.lookup(vector_type_id)?;
5272 let inner = match module.types[vector_type_lookup.handle].inner {
5273 crate::TypeInner::Vector { size, scalar } => crate::TypeInner::Matrix {
5274 columns: map_vector_size(num_columns)?,
5275 rows: size,
5276 scalar,
5277 },
5278 _ => return Err(Error::InvalidInnerType(vector_type_id)),
5279 };
5280
5281 self.lookup_type.insert(
5282 id,
5283 LookupType {
5284 handle: module.types.insert(
5285 crate::Type {
5286 name: decor.and_then(|dec| dec.name),
5287 inner,
5288 },
5289 self.span_from_with_op(start),
5290 ),
5291 base_id: Some(vector_type_id),
5292 },
5293 );
5294 Ok(())
5295 }
5296
5297 fn parse_type_function(&mut self, inst: Instruction) -> Result<(), Error> {
5298 self.switch(ModuleState::Type, inst.op)?;
5299 inst.expect_at_least(3)?;
5300 let id = self.next()?;
5301 let return_type_id = self.next()?;
5302 let parameter_type_ids = self.data.by_ref().take(inst.wc as usize - 3).collect();
5303 self.lookup_function_type.insert(
5304 id,
5305 LookupFunctionType {
5306 parameter_type_ids,
5307 return_type_id,
5308 },
5309 );
5310 Ok(())
5311 }
5312
5313 fn parse_type_pointer(
5314 &mut self,
5315 inst: Instruction,
5316 module: &mut crate::Module,
5317 ) -> Result<(), Error> {
5318 let start = self.data_offset;
5319 self.switch(ModuleState::Type, inst.op)?;
5320 inst.expect(4)?;
5321 let id = self.next()?;
5322 let storage_class = self.next()?;
5323 let type_id = self.next()?;
5324
5325 let decor = self.future_decor.remove(&id);
5326 let base_lookup_ty = self.lookup_type.lookup(type_id)?;
5327 let base_inner = &module.types[base_lookup_ty.handle].inner;
5328
5329 let space = if let Some(space) = base_inner.pointer_space() {
5330 space
5331 } else if self
5332 .lookup_storage_buffer_types
5333 .contains_key(&base_lookup_ty.handle)
5334 {
5335 crate::AddressSpace::Storage {
5336 access: crate::StorageAccess::default(),
5337 }
5338 } else {
5339 match map_storage_class(storage_class)? {
5340 ExtendedClass::Global(space) => space,
5341 ExtendedClass::Input | ExtendedClass::Output => crate::AddressSpace::Private,
5342 }
5343 };
5344
5345 if let crate::TypeInner::Array {
5349 size: crate::ArraySize::Dynamic,
5350 ..
5351 } = *base_inner
5352 {
5353 match space {
5354 crate::AddressSpace::Storage { .. } => {}
5355 _ => {
5356 return Err(Error::UnsupportedRuntimeArrayStorageClass);
5357 }
5358 }
5359 }
5360
5361 let lookup_ty = if space == crate::AddressSpace::Handle {
5363 base_lookup_ty.clone()
5364 } else {
5365 LookupType {
5366 handle: module.types.insert(
5367 crate::Type {
5368 name: decor.and_then(|dec| dec.name),
5369 inner: crate::TypeInner::Pointer {
5370 base: base_lookup_ty.handle,
5371 space,
5372 },
5373 },
5374 self.span_from_with_op(start),
5375 ),
5376 base_id: Some(type_id),
5377 }
5378 };
5379 self.lookup_type.insert(id, lookup_ty);
5380 Ok(())
5381 }
5382
5383 fn parse_type_array(
5384 &mut self,
5385 inst: Instruction,
5386 module: &mut crate::Module,
5387 ) -> Result<(), Error> {
5388 let start = self.data_offset;
5389 self.switch(ModuleState::Type, inst.op)?;
5390 inst.expect(4)?;
5391 let id = self.next()?;
5392 let type_id = self.next()?;
5393 let length_id = self.next()?;
5394 let length_const = self.lookup_constant.lookup(length_id)?;
5395
5396 let size = resolve_constant(module.to_ctx(), &length_const.inner)
5397 .and_then(NonZeroU32::new)
5398 .ok_or(Error::InvalidArraySize(length_id))?;
5399
5400 let decor = self.future_decor.remove(&id).unwrap_or_default();
5401 let base = self.lookup_type.lookup(type_id)?.handle;
5402
5403 self.layouter.update(module.to_ctx()).unwrap();
5404
5405 let inner = if let crate::TypeInner::Image { .. } | crate::TypeInner::Sampler { .. } =
5437 module.types[base].inner
5438 {
5439 crate::TypeInner::BindingArray {
5440 base,
5441 size: crate::ArraySize::Constant(size),
5442 }
5443 } else {
5444 crate::TypeInner::Array {
5445 base,
5446 size: crate::ArraySize::Constant(size),
5447 stride: match decor.array_stride {
5448 Some(stride) => stride.get(),
5449 None => self.layouter[base].to_stride(),
5450 },
5451 }
5452 };
5453
5454 self.lookup_type.insert(
5455 id,
5456 LookupType {
5457 handle: module.types.insert(
5458 crate::Type {
5459 name: decor.name,
5460 inner,
5461 },
5462 self.span_from_with_op(start),
5463 ),
5464 base_id: Some(type_id),
5465 },
5466 );
5467 Ok(())
5468 }
5469
5470 fn parse_type_runtime_array(
5471 &mut self,
5472 inst: Instruction,
5473 module: &mut crate::Module,
5474 ) -> Result<(), Error> {
5475 let start = self.data_offset;
5476 self.switch(ModuleState::Type, inst.op)?;
5477 inst.expect(3)?;
5478 let id = self.next()?;
5479 let type_id = self.next()?;
5480
5481 let decor = self.future_decor.remove(&id).unwrap_or_default();
5482 let base = self.lookup_type.lookup(type_id)?.handle;
5483
5484 self.layouter.update(module.to_ctx()).unwrap();
5485
5486 let inner = if let crate::TypeInner::Image { .. } | crate::TypeInner::Sampler { .. } =
5488 module.types[base].inner
5489 {
5490 crate::TypeInner::BindingArray {
5491 base: self.lookup_type.lookup(type_id)?.handle,
5492 size: crate::ArraySize::Dynamic,
5493 }
5494 } else {
5495 crate::TypeInner::Array {
5496 base: self.lookup_type.lookup(type_id)?.handle,
5497 size: crate::ArraySize::Dynamic,
5498 stride: match decor.array_stride {
5499 Some(stride) => stride.get(),
5500 None => self.layouter[base].to_stride(),
5501 },
5502 }
5503 };
5504
5505 self.lookup_type.insert(
5506 id,
5507 LookupType {
5508 handle: module.types.insert(
5509 crate::Type {
5510 name: decor.name,
5511 inner,
5512 },
5513 self.span_from_with_op(start),
5514 ),
5515 base_id: Some(type_id),
5516 },
5517 );
5518 Ok(())
5519 }
5520
5521 fn parse_type_struct(
5522 &mut self,
5523 inst: Instruction,
5524 module: &mut crate::Module,
5525 ) -> Result<(), Error> {
5526 let start = self.data_offset;
5527 self.switch(ModuleState::Type, inst.op)?;
5528 inst.expect_at_least(2)?;
5529 let id = self.next()?;
5530 let parent_decor = self.future_decor.remove(&id);
5531 let is_storage_buffer = parent_decor
5532 .as_ref()
5533 .is_some_and(|decor| decor.storage_buffer);
5534
5535 self.layouter.update(module.to_ctx()).unwrap();
5536
5537 let mut members = Vec::<crate::StructMember>::with_capacity(inst.wc as usize - 2);
5538 let mut member_lookups = Vec::with_capacity(members.capacity());
5539 let mut storage_access = crate::StorageAccess::empty();
5540 let mut span = 0;
5541 let mut alignment = Alignment::ONE;
5542 for i in 0..u32::from(inst.wc) - 2 {
5543 let type_id = self.next()?;
5544 let ty = self.lookup_type.lookup(type_id)?.handle;
5545 let decor = self
5546 .future_member_decor
5547 .remove(&(id, i))
5548 .unwrap_or_default();
5549
5550 storage_access |= decor.flags.to_storage_access();
5551
5552 member_lookups.push(LookupMember {
5553 type_id,
5554 row_major: decor.matrix_major == Some(Majority::Row),
5555 });
5556
5557 let member_alignment = self.layouter[ty].alignment;
5558 span = member_alignment.round_up(span);
5559 alignment = member_alignment.max(alignment);
5560
5561 let binding = decor.io_binding().ok();
5562 if let Some(offset) = decor.offset {
5563 span = offset;
5564 }
5565 let offset = span;
5566
5567 span += self.layouter[ty].size;
5568
5569 let inner = &module.types[ty].inner;
5570 if let crate::TypeInner::Matrix {
5571 columns,
5572 rows,
5573 scalar,
5574 } = *inner
5575 {
5576 if let Some(stride) = decor.matrix_stride {
5577 let expected_stride = Alignment::from(rows) * scalar.width as u32;
5578 if stride.get() != expected_stride {
5579 return Err(Error::UnsupportedMatrixStride {
5580 stride: stride.get(),
5581 columns: columns as u8,
5582 rows: rows as u8,
5583 width: scalar.width,
5584 });
5585 }
5586 }
5587 }
5588
5589 members.push(crate::StructMember {
5590 name: decor.name,
5591 ty,
5592 binding,
5593 offset,
5594 });
5595 }
5596
5597 span = alignment.round_up(span);
5598
5599 let inner = crate::TypeInner::Struct { span, members };
5600
5601 let ty_handle = module.types.insert(
5602 crate::Type {
5603 name: parent_decor.and_then(|dec| dec.name),
5604 inner,
5605 },
5606 self.span_from_with_op(start),
5607 );
5608
5609 if is_storage_buffer {
5610 self.lookup_storage_buffer_types
5611 .insert(ty_handle, storage_access);
5612 }
5613 for (i, member_lookup) in member_lookups.into_iter().enumerate() {
5614 self.lookup_member
5615 .insert((ty_handle, i as u32), member_lookup);
5616 }
5617 self.lookup_type.insert(
5618 id,
5619 LookupType {
5620 handle: ty_handle,
5621 base_id: None,
5622 },
5623 );
5624 Ok(())
5625 }
5626
5627 fn parse_type_image(
5628 &mut self,
5629 inst: Instruction,
5630 module: &mut crate::Module,
5631 ) -> Result<(), Error> {
5632 let start = self.data_offset;
5633 self.switch(ModuleState::Type, inst.op)?;
5634 inst.expect(9)?;
5635
5636 let id = self.next()?;
5637 let sample_type_id = self.next()?;
5638 let dim = self.next()?;
5639 let is_depth = self.next()?;
5640 let is_array = self.next()? != 0;
5641 let is_msaa = self.next()? != 0;
5642 let is_sampled = self.next()?;
5643 let format = self.next()?;
5644
5645 let dim = map_image_dim(dim)?;
5646 let decor = self.future_decor.remove(&id).unwrap_or_default();
5647
5648 module.types.insert(
5650 crate::Type {
5651 name: None,
5652 inner: {
5653 let scalar = crate::Scalar::F32;
5654 match dim.required_coordinate_size() {
5655 None => crate::TypeInner::Scalar(scalar),
5656 Some(size) => crate::TypeInner::Vector { size, scalar },
5657 }
5658 },
5659 },
5660 Default::default(),
5661 );
5662
5663 let base_handle = self.lookup_type.lookup(sample_type_id)?.handle;
5664 let kind = module.types[base_handle]
5665 .inner
5666 .scalar_kind()
5667 .ok_or(Error::InvalidImageBaseType(base_handle))?;
5668
5669 let inner = crate::TypeInner::Image {
5670 class: if is_depth == 1 {
5671 crate::ImageClass::Depth { multi: is_msaa }
5672 } else if format != 0 {
5673 crate::ImageClass::Storage {
5674 format: map_image_format(format)?,
5675 access: crate::StorageAccess::default(),
5676 }
5677 } else if is_sampled == 2 {
5678 return Err(Error::InvalidImageWriteType);
5679 } else {
5680 crate::ImageClass::Sampled {
5681 kind,
5682 multi: is_msaa,
5683 }
5684 },
5685 dim,
5686 arrayed: is_array,
5687 };
5688
5689 let handle = module.types.insert(
5690 crate::Type {
5691 name: decor.name,
5692 inner,
5693 },
5694 self.span_from_with_op(start),
5695 );
5696
5697 self.lookup_type.insert(
5698 id,
5699 LookupType {
5700 handle,
5701 base_id: Some(sample_type_id),
5702 },
5703 );
5704 Ok(())
5705 }
5706
5707 fn parse_type_sampled_image(&mut self, inst: Instruction) -> Result<(), Error> {
5708 self.switch(ModuleState::Type, inst.op)?;
5709 inst.expect(3)?;
5710 let id = self.next()?;
5711 let image_id = self.next()?;
5712 self.lookup_type.insert(
5713 id,
5714 LookupType {
5715 handle: self.lookup_type.lookup(image_id)?.handle,
5716 base_id: Some(image_id),
5717 },
5718 );
5719 Ok(())
5720 }
5721
5722 fn parse_type_sampler(
5723 &mut self,
5724 inst: Instruction,
5725 module: &mut crate::Module,
5726 ) -> Result<(), Error> {
5727 let start = self.data_offset;
5728 self.switch(ModuleState::Type, inst.op)?;
5729 inst.expect(2)?;
5730 let id = self.next()?;
5731 let decor = self.future_decor.remove(&id).unwrap_or_default();
5732 let handle = module.types.insert(
5733 crate::Type {
5734 name: decor.name,
5735 inner: crate::TypeInner::Sampler { comparison: false },
5736 },
5737 self.span_from_with_op(start),
5738 );
5739 self.lookup_type.insert(
5740 id,
5741 LookupType {
5742 handle,
5743 base_id: None,
5744 },
5745 );
5746 Ok(())
5747 }
5748
5749 fn parse_constant(
5750 &mut self,
5751 inst: Instruction,
5752 module: &mut crate::Module,
5753 ) -> Result<(), Error> {
5754 let start = self.data_offset;
5755 self.switch(ModuleState::Type, inst.op)?;
5756 inst.expect_at_least(4)?;
5757 let type_id = self.next()?;
5758 let id = self.next()?;
5759 let type_lookup = self.lookup_type.lookup(type_id)?;
5760 let ty = type_lookup.handle;
5761
5762 let literal = match module.types[ty].inner {
5763 crate::TypeInner::Scalar(crate::Scalar {
5764 kind: crate::ScalarKind::Uint,
5765 width,
5766 }) => {
5767 let low = self.next()?;
5768 match width {
5769 4 => crate::Literal::U32(low),
5770 8 => {
5771 inst.expect(5)?;
5772 let high = self.next()?;
5773 crate::Literal::U64((u64::from(high) << 32) | u64::from(low))
5774 }
5775 _ => return Err(Error::InvalidTypeWidth(width as u32)),
5776 }
5777 }
5778 crate::TypeInner::Scalar(crate::Scalar {
5779 kind: crate::ScalarKind::Sint,
5780 width,
5781 }) => {
5782 let low = self.next()?;
5783 match width {
5784 4 => crate::Literal::I32(low as i32),
5785 8 => {
5786 inst.expect(5)?;
5787 let high = self.next()?;
5788 crate::Literal::I64(((u64::from(high) << 32) | u64::from(low)) as i64)
5789 }
5790 _ => return Err(Error::InvalidTypeWidth(width as u32)),
5791 }
5792 }
5793 crate::TypeInner::Scalar(crate::Scalar {
5794 kind: crate::ScalarKind::Float,
5795 width,
5796 }) => {
5797 let low = self.next()?;
5798 match width {
5799 2 => crate::Literal::F16(f16::from_bits(low as u16)),
5802 4 => crate::Literal::F32(f32::from_bits(low)),
5803 8 => {
5804 inst.expect(5)?;
5805 let high = self.next()?;
5806 crate::Literal::F64(f64::from_bits(
5807 (u64::from(high) << 32) | u64::from(low),
5808 ))
5809 }
5810 _ => return Err(Error::InvalidTypeWidth(width as u32)),
5811 }
5812 }
5813 _ => return Err(Error::UnsupportedType(type_lookup.handle)),
5814 };
5815
5816 let span = self.span_from_with_op(start);
5817
5818 let init = module
5819 .global_expressions
5820 .append(crate::Expression::Literal(literal), span);
5821
5822 self.insert_parsed_constant(module, id, type_id, ty, init, span)
5823 }
5824
5825 fn parse_composite_constant(
5826 &mut self,
5827 inst: Instruction,
5828 module: &mut crate::Module,
5829 ) -> Result<(), Error> {
5830 let start = self.data_offset;
5831 self.switch(ModuleState::Type, inst.op)?;
5832 inst.expect_at_least(3)?;
5833 let type_id = self.next()?;
5834 let id = self.next()?;
5835
5836 let type_lookup = self.lookup_type.lookup(type_id)?;
5837 let ty = type_lookup.handle;
5838
5839 let mut components = Vec::with_capacity(inst.wc as usize - 3);
5840 for _ in 0..components.capacity() {
5841 let start = self.data_offset;
5842 let component_id = self.next()?;
5843 let span = self.span_from_with_op(start);
5844 let constant = self.lookup_constant.lookup(component_id)?;
5845 let expr = module
5846 .global_expressions
5847 .append(constant.inner.to_expr(), span);
5848 components.push(expr);
5849 }
5850
5851 let span = self.span_from_with_op(start);
5852
5853 let init = module
5854 .global_expressions
5855 .append(crate::Expression::Compose { ty, components }, span);
5856
5857 self.insert_parsed_constant(module, id, type_id, ty, init, span)
5858 }
5859
5860 fn parse_null_constant(
5861 &mut self,
5862 inst: Instruction,
5863 module: &mut crate::Module,
5864 ) -> Result<(), Error> {
5865 let start = self.data_offset;
5866 self.switch(ModuleState::Type, inst.op)?;
5867 inst.expect(3)?;
5868 let type_id = self.next()?;
5869 let id = self.next()?;
5870 let span = self.span_from_with_op(start);
5871
5872 let type_lookup = self.lookup_type.lookup(type_id)?;
5873 let ty = type_lookup.handle;
5874
5875 let init = module
5876 .global_expressions
5877 .append(crate::Expression::ZeroValue(ty), span);
5878
5879 self.insert_parsed_constant(module, id, type_id, ty, init, span)
5880 }
5881
5882 fn parse_bool_constant(
5883 &mut self,
5884 inst: Instruction,
5885 value: bool,
5886 module: &mut crate::Module,
5887 ) -> Result<(), Error> {
5888 let start = self.data_offset;
5889 self.switch(ModuleState::Type, inst.op)?;
5890 inst.expect(3)?;
5891 let type_id = self.next()?;
5892 let id = self.next()?;
5893 let span = self.span_from_with_op(start);
5894
5895 let type_lookup = self.lookup_type.lookup(type_id)?;
5896 let ty = type_lookup.handle;
5897
5898 let init = module.global_expressions.append(
5899 crate::Expression::Literal(crate::Literal::Bool(value)),
5900 span,
5901 );
5902
5903 self.insert_parsed_constant(module, id, type_id, ty, init, span)
5904 }
5905
5906 fn insert_parsed_constant(
5907 &mut self,
5908 module: &mut crate::Module,
5909 id: u32,
5910 type_id: u32,
5911 ty: Handle<crate::Type>,
5912 init: Handle<crate::Expression>,
5913 span: crate::Span,
5914 ) -> Result<(), Error> {
5915 let decor = self.future_decor.remove(&id).unwrap_or_default();
5916
5917 let inner = if let Some(id) = decor.specialization_constant_id {
5918 let o = crate::Override {
5919 name: decor.name,
5920 id: Some(id.try_into().map_err(|_| Error::SpecIdTooHigh(id))?),
5921 ty,
5922 init: Some(init),
5923 };
5924 Constant::Override(module.overrides.append(o, span))
5925 } else {
5926 let c = crate::Constant {
5927 name: decor.name,
5928 ty,
5929 init,
5930 };
5931 Constant::Constant(module.constants.append(c, span))
5932 };
5933
5934 self.lookup_constant
5935 .insert(id, LookupConstant { inner, type_id });
5936 Ok(())
5937 }
5938
5939 fn parse_global_variable(
5940 &mut self,
5941 inst: Instruction,
5942 module: &mut crate::Module,
5943 ) -> Result<(), Error> {
5944 let start = self.data_offset;
5945 self.switch(ModuleState::Type, inst.op)?;
5946 inst.expect_at_least(4)?;
5947 let type_id = self.next()?;
5948 let id = self.next()?;
5949 let storage_class = self.next()?;
5950 let init = if inst.wc > 4 {
5951 inst.expect(5)?;
5952 let start = self.data_offset;
5953 let init_id = self.next()?;
5954 let span = self.span_from_with_op(start);
5955 let lconst = self.lookup_constant.lookup(init_id)?;
5956 let expr = module
5957 .global_expressions
5958 .append(lconst.inner.to_expr(), span);
5959 Some(expr)
5960 } else {
5961 None
5962 };
5963 let span = self.span_from_with_op(start);
5964 let dec = self.future_decor.remove(&id).unwrap_or_default();
5965
5966 let original_ty = self.lookup_type.lookup(type_id)?.handle;
5967 let mut ty = original_ty;
5968
5969 if let crate::TypeInner::Pointer { base, space: _ } = module.types[original_ty].inner {
5970 ty = base;
5971 }
5972
5973 if let crate::TypeInner::BindingArray { .. } = module.types[original_ty].inner {
5974 if dec.desc_set.is_none() || dec.desc_index.is_none() {
5977 return Err(Error::NonBindingArrayOfImageOrSamplers);
5978 }
5979 }
5980
5981 if let crate::TypeInner::Image {
5982 dim,
5983 arrayed,
5984 class: crate::ImageClass::Storage { format, access: _ },
5985 } = module.types[ty].inner
5986 {
5987 let access = dec.flags.to_storage_access();
5991
5992 ty = module.types.insert(
5993 crate::Type {
5994 name: None,
5995 inner: crate::TypeInner::Image {
5996 dim,
5997 arrayed,
5998 class: crate::ImageClass::Storage { format, access },
5999 },
6000 },
6001 Default::default(),
6002 );
6003 }
6004
6005 let ext_class = match self.lookup_storage_buffer_types.get(&ty) {
6006 Some(&access) => ExtendedClass::Global(crate::AddressSpace::Storage { access }),
6007 None => map_storage_class(storage_class)?,
6008 };
6009
6010 let (inner, var) = match ext_class {
6011 ExtendedClass::Global(mut space) => {
6012 if let crate::AddressSpace::Storage { ref mut access } = space {
6013 *access &= dec.flags.to_storage_access();
6014 }
6015 let var = crate::GlobalVariable {
6016 binding: dec.resource_binding(),
6017 name: dec.name,
6018 space,
6019 ty,
6020 init,
6021 };
6022 (Variable::Global, var)
6023 }
6024 ExtendedClass::Input => {
6025 let binding = dec.io_binding()?;
6026 let mut unsigned_ty = ty;
6027 if let crate::Binding::BuiltIn(built_in) = binding {
6028 let needs_inner_uint = match built_in {
6029 crate::BuiltIn::BaseInstance
6030 | crate::BuiltIn::BaseVertex
6031 | crate::BuiltIn::InstanceIndex
6032 | crate::BuiltIn::SampleIndex
6033 | crate::BuiltIn::VertexIndex
6034 | crate::BuiltIn::PrimitiveIndex
6035 | crate::BuiltIn::LocalInvocationIndex => {
6036 Some(crate::TypeInner::Scalar(crate::Scalar::U32))
6037 }
6038 crate::BuiltIn::GlobalInvocationId
6039 | crate::BuiltIn::LocalInvocationId
6040 | crate::BuiltIn::WorkGroupId
6041 | crate::BuiltIn::WorkGroupSize => Some(crate::TypeInner::Vector {
6042 size: crate::VectorSize::Tri,
6043 scalar: crate::Scalar::U32,
6044 }),
6045 crate::BuiltIn::Barycentric => Some(crate::TypeInner::Vector {
6046 size: crate::VectorSize::Tri,
6047 scalar: crate::Scalar::F32,
6048 }),
6049 _ => None,
6050 };
6051 if let (Some(inner), Some(crate::ScalarKind::Sint)) =
6052 (needs_inner_uint, module.types[ty].inner.scalar_kind())
6053 {
6054 unsigned_ty = module
6055 .types
6056 .insert(crate::Type { name: None, inner }, Default::default());
6057 }
6058 }
6059
6060 let var = crate::GlobalVariable {
6061 name: dec.name.clone(),
6062 space: crate::AddressSpace::Private,
6063 binding: None,
6064 ty,
6065 init: None,
6066 };
6067
6068 let inner = Variable::Input(crate::FunctionArgument {
6069 name: dec.name,
6070 ty: unsigned_ty,
6071 binding: Some(binding),
6072 });
6073 (inner, var)
6074 }
6075 ExtendedClass::Output => {
6076 let binding = dec.io_binding().ok();
6078 let init = match binding {
6079 Some(crate::Binding::BuiltIn(built_in)) => {
6080 match null::generate_default_built_in(
6081 Some(built_in),
6082 ty,
6083 &mut module.global_expressions,
6084 span,
6085 ) {
6086 Ok(handle) => Some(handle),
6087 Err(e) => {
6088 log::warn!("Failed to initialize output built-in: {e}");
6089 None
6090 }
6091 }
6092 }
6093 Some(crate::Binding::Location { .. }) => None,
6094 None => match module.types[ty].inner {
6095 crate::TypeInner::Struct { ref members, .. } => {
6096 let mut components = Vec::with_capacity(members.len());
6097 for member in members.iter() {
6098 let built_in = match member.binding {
6099 Some(crate::Binding::BuiltIn(built_in)) => Some(built_in),
6100 _ => None,
6101 };
6102 let handle = null::generate_default_built_in(
6103 built_in,
6104 member.ty,
6105 &mut module.global_expressions,
6106 span,
6107 )?;
6108 components.push(handle);
6109 }
6110 Some(
6111 module
6112 .global_expressions
6113 .append(crate::Expression::Compose { ty, components }, span),
6114 )
6115 }
6116 _ => None,
6117 },
6118 };
6119
6120 let var = crate::GlobalVariable {
6121 name: dec.name,
6122 space: crate::AddressSpace::Private,
6123 binding: None,
6124 ty,
6125 init,
6126 };
6127 let inner = Variable::Output(crate::FunctionResult { ty, binding });
6128 (inner, var)
6129 }
6130 };
6131
6132 let handle = module.global_variables.append(var, span);
6133
6134 if module.types[ty].inner.can_comparison_sample(module) {
6135 log::debug!("\t\ttracking {handle:?} for sampling properties");
6136
6137 self.handle_sampling
6138 .insert(handle, image::SamplingFlags::empty());
6139 }
6140
6141 self.lookup_variable.insert(
6142 id,
6143 LookupVariable {
6144 inner,
6145 handle,
6146 type_id,
6147 },
6148 );
6149 Ok(())
6150 }
6151
6152 fn record_atomic_access(
6165 &mut self,
6166 ctx: &BlockContext,
6167 handle: Handle<crate::Expression>,
6168 ) -> Result<Handle<crate::Type>, Error> {
6169 log::debug!("\t\tlocating global variable in {handle:?}");
6170 match ctx.expressions[handle] {
6171 crate::Expression::Access { base, index } => {
6172 log::debug!("\t\t access {handle:?} {index:?}");
6173 let ty = self.record_atomic_access(ctx, base)?;
6174 let crate::TypeInner::Array { base, .. } = ctx.module.types[ty].inner else {
6175 unreachable!("Atomic operations on Access expressions only work for arrays");
6176 };
6177 Ok(base)
6178 }
6179 crate::Expression::AccessIndex { base, index } => {
6180 log::debug!("\t\t access index {handle:?} {index:?}");
6181 let ty = self.record_atomic_access(ctx, base)?;
6182 match ctx.module.types[ty].inner {
6183 crate::TypeInner::Struct { ref members, .. } => {
6184 let index = index as usize;
6185 self.upgrade_atomics.insert_field(ty, index);
6186 Ok(members[index].ty)
6187 }
6188 crate::TypeInner::Array { base, .. } => {
6189 Ok(base)
6190 }
6191 _ => unreachable!("Atomic operations on AccessIndex expressions only work for structs and arrays"),
6192 }
6193 }
6194 crate::Expression::GlobalVariable(h) => {
6195 log::debug!("\t\t found {h:?}");
6196 self.upgrade_atomics.insert_global(h);
6197 Ok(ctx.module.global_variables[h].ty)
6198 }
6199 _ => Err(Error::AtomicUpgradeError(
6200 crate::front::atomic_upgrade::Error::GlobalVariableMissing,
6201 )),
6202 }
6203 }
6204}
6205
6206fn make_index_literal(
6207 ctx: &mut BlockContext,
6208 index: u32,
6209 block: &mut crate::Block,
6210 emitter: &mut crate::proc::Emitter,
6211 index_type: Handle<crate::Type>,
6212 index_type_id: spirv::Word,
6213 span: crate::Span,
6214) -> Result<Handle<crate::Expression>, Error> {
6215 block.extend(emitter.finish(ctx.expressions));
6216
6217 let literal = match ctx.module.types[index_type].inner.scalar_kind() {
6218 Some(crate::ScalarKind::Uint) => crate::Literal::U32(index),
6219 Some(crate::ScalarKind::Sint) => crate::Literal::I32(index as i32),
6220 _ => return Err(Error::InvalidIndexType(index_type_id)),
6221 };
6222 let expr = ctx
6223 .expressions
6224 .append(crate::Expression::Literal(literal), span);
6225
6226 emitter.start(ctx.expressions);
6227 Ok(expr)
6228}
6229
6230fn resolve_constant(gctx: crate::proc::GlobalCtx, constant: &Constant) -> Option<u32> {
6231 let constant = match *constant {
6232 Constant::Constant(constant) => constant,
6233 Constant::Override(_) => return None,
6234 };
6235 match gctx.global_expressions[gctx.constants[constant].init] {
6236 crate::Expression::Literal(crate::Literal::U32(id)) => Some(id),
6237 crate::Expression::Literal(crate::Literal::I32(id)) => Some(id as u32),
6238 _ => None,
6239 }
6240}
6241
6242pub fn parse_u8_slice(data: &[u8], options: &Options) -> Result<crate::Module, Error> {
6243 if data.len() % 4 != 0 {
6244 return Err(Error::IncompleteData);
6245 }
6246
6247 let words = data
6248 .chunks(4)
6249 .map(|c| u32::from_le_bytes(c.try_into().unwrap()));
6250 Frontend::new(words, options).parse()
6251}
6252
6253fn is_parent(mut child: usize, parent: usize, block_ctx: &BlockContext) -> bool {
6255 loop {
6256 if child == parent {
6257 break true;
6259 } else if child == 0 {
6260 break false;
6262 }
6263
6264 child = block_ctx.bodies[child].parent;
6265 }
6266}
6267
6268#[cfg(test)]
6269mod test {
6270 use alloc::vec;
6271
6272 #[test]
6273 fn parse() {
6274 let bin = vec![
6275 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00,
6277 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
6282 ];
6283 let _ = super::parse_u8_slice(&bin, &Default::default()).unwrap();
6284 }
6285}