naga/back/spv/
writer.rs

1use alloc::{string::String, vec, vec::Vec};
2
3use hashbrown::hash_map::Entry;
4use spirv::Word;
5
6use super::{
7    block::DebugInfoInner,
8    helpers::{contains_builtin, global_needs_wrapper, map_storage_class},
9    Block, BlockContext, CachedConstant, CachedExpressions, DebugInfo, EntryPointContext, Error,
10    Function, FunctionArgument, GlobalVariable, IdGenerator, Instruction, LocalImageType,
11    LocalType, LocalVariable, LogicalLayout, LookupFunctionType, LookupType, NumericType, Options,
12    PhysicalLayout, PipelineOptions, ResultMember, Writer, WriterFlags, BITS_PER_BYTE,
13};
14use crate::{
15    arena::{Handle, HandleVec, UniqueArena},
16    back::spv::{BindingInfo, WrappedFunction},
17    proc::{Alignment, TypeResolution},
18    valid::{FunctionInfo, ModuleInfo},
19};
20
21struct FunctionInterface<'a> {
22    varying_ids: &'a mut Vec<Word>,
23    stage: crate::ShaderStage,
24}
25
26impl Function {
27    pub(super) fn to_words(&self, sink: &mut impl Extend<Word>) {
28        self.signature.as_ref().unwrap().to_words(sink);
29        for argument in self.parameters.iter() {
30            argument.instruction.to_words(sink);
31        }
32        for (index, block) in self.blocks.iter().enumerate() {
33            Instruction::label(block.label_id).to_words(sink);
34            if index == 0 {
35                for local_var in self.variables.values() {
36                    local_var.instruction.to_words(sink);
37                }
38                for local_var in self.force_loop_bounding_vars.iter() {
39                    local_var.instruction.to_words(sink);
40                }
41                for internal_var in self.spilled_composites.values() {
42                    internal_var.instruction.to_words(sink);
43                }
44            }
45            for instruction in block.body.iter() {
46                instruction.to_words(sink);
47            }
48        }
49        Instruction::function_end().to_words(sink);
50    }
51}
52
53impl Writer {
54    pub fn new(options: &Options) -> Result<Self, Error> {
55        let (major, minor) = options.lang_version;
56        if major != 1 {
57            return Err(Error::UnsupportedVersion(major, minor));
58        }
59
60        let mut capabilities_used = crate::FastIndexSet::default();
61        capabilities_used.insert(spirv::Capability::Shader);
62
63        let mut id_gen = IdGenerator::default();
64        let gl450_ext_inst_id = id_gen.next();
65        let void_type = id_gen.next();
66
67        Ok(Writer {
68            physical_layout: PhysicalLayout::new(major, minor),
69            logical_layout: LogicalLayout::default(),
70            id_gen,
71            capabilities_available: options.capabilities.clone(),
72            capabilities_used,
73            extensions_used: crate::FastIndexSet::default(),
74            debugs: vec![],
75            annotations: vec![],
76            flags: options.flags,
77            bounds_check_policies: options.bounds_check_policies,
78            zero_initialize_workgroup_memory: options.zero_initialize_workgroup_memory,
79            force_loop_bounding: options.force_loop_bounding,
80            use_storage_input_output_16: options.use_storage_input_output_16,
81            void_type,
82            lookup_type: crate::FastHashMap::default(),
83            lookup_function: crate::FastHashMap::default(),
84            lookup_function_type: crate::FastHashMap::default(),
85            wrapped_functions: crate::FastHashMap::default(),
86            constant_ids: HandleVec::new(),
87            cached_constants: crate::FastHashMap::default(),
88            global_variables: HandleVec::new(),
89            fake_missing_bindings: options.fake_missing_bindings,
90            binding_map: options.binding_map.clone(),
91            saved_cached: CachedExpressions::default(),
92            gl450_ext_inst_id,
93            temp_list: Vec::new(),
94            ray_get_committed_intersection_function: None,
95            ray_get_candidate_intersection_function: None,
96            io_f16_polyfills: super::f16_polyfill::F16IoPolyfill::new(
97                options.use_storage_input_output_16,
98            ),
99        })
100    }
101
102    pub fn set_options(&mut self, options: &Options) -> Result<(), Error> {
103        let (major, minor) = options.lang_version;
104        if major != 1 {
105            return Err(Error::UnsupportedVersion(major, minor));
106        }
107        self.physical_layout = PhysicalLayout::new(major, minor);
108        self.capabilities_available = options.capabilities.clone();
109        self.flags = options.flags;
110        self.bounds_check_policies = options.bounds_check_policies;
111        self.zero_initialize_workgroup_memory = options.zero_initialize_workgroup_memory;
112        self.force_loop_bounding = options.force_loop_bounding;
113        self.use_storage_input_output_16 = options.use_storage_input_output_16;
114        self.binding_map = options.binding_map.clone();
115        self.io_f16_polyfills =
116            super::f16_polyfill::F16IoPolyfill::new(options.use_storage_input_output_16);
117        Ok(())
118    }
119
120    /// Returns `(major, minor)` of the SPIR-V language version.
121    pub const fn lang_version(&self) -> (u8, u8) {
122        self.physical_layout.lang_version()
123    }
124
125    /// Reset `Writer` to its initial state, retaining any allocations.
126    ///
127    /// Why not just implement `Recyclable` for `Writer`? By design,
128    /// `Recyclable::recycle` requires ownership of the value, not just
129    /// `&mut`; see the trait documentation. But we need to use this method
130    /// from functions like `Writer::write`, which only have `&mut Writer`.
131    /// Workarounds include unsafe code (`core::ptr::read`, then `write`, ugh)
132    /// or something like a `Default` impl that returns an oddly-initialized
133    /// `Writer`, which is worse.
134    fn reset(&mut self) {
135        use super::recyclable::Recyclable;
136        use core::mem::take;
137
138        let mut id_gen = IdGenerator::default();
139        let gl450_ext_inst_id = id_gen.next();
140        let void_type = id_gen.next();
141
142        // Every field of the old writer that is not determined by the `Options`
143        // passed to `Writer::new` should be reset somehow.
144        let fresh = Writer {
145            // Copied from the old Writer:
146            flags: self.flags,
147            bounds_check_policies: self.bounds_check_policies,
148            zero_initialize_workgroup_memory: self.zero_initialize_workgroup_memory,
149            force_loop_bounding: self.force_loop_bounding,
150            use_storage_input_output_16: self.use_storage_input_output_16,
151            capabilities_available: take(&mut self.capabilities_available),
152            fake_missing_bindings: self.fake_missing_bindings,
153            binding_map: take(&mut self.binding_map),
154
155            // Initialized afresh:
156            id_gen,
157            void_type,
158            gl450_ext_inst_id,
159
160            // Recycled:
161            capabilities_used: take(&mut self.capabilities_used).recycle(),
162            extensions_used: take(&mut self.extensions_used).recycle(),
163            physical_layout: self.physical_layout.clone().recycle(),
164            logical_layout: take(&mut self.logical_layout).recycle(),
165            debugs: take(&mut self.debugs).recycle(),
166            annotations: take(&mut self.annotations).recycle(),
167            lookup_type: take(&mut self.lookup_type).recycle(),
168            lookup_function: take(&mut self.lookup_function).recycle(),
169            lookup_function_type: take(&mut self.lookup_function_type).recycle(),
170            wrapped_functions: take(&mut self.wrapped_functions).recycle(),
171            constant_ids: take(&mut self.constant_ids).recycle(),
172            cached_constants: take(&mut self.cached_constants).recycle(),
173            global_variables: take(&mut self.global_variables).recycle(),
174            saved_cached: take(&mut self.saved_cached).recycle(),
175            temp_list: take(&mut self.temp_list).recycle(),
176            ray_get_candidate_intersection_function: None,
177            ray_get_committed_intersection_function: None,
178            io_f16_polyfills: take(&mut self.io_f16_polyfills).recycle(),
179        };
180
181        *self = fresh;
182
183        self.capabilities_used.insert(spirv::Capability::Shader);
184    }
185
186    /// Indicate that the code requires any one of the listed capabilities.
187    ///
188    /// If nothing in `capabilities` appears in the available capabilities
189    /// specified in the [`Options`] from which this `Writer` was created,
190    /// return an error. The `what` string is used in the error message to
191    /// explain what provoked the requirement. (If no available capabilities were
192    /// given, assume everything is available.)
193    ///
194    /// The first acceptable capability will be added to this `Writer`'s
195    /// [`capabilities_used`] table, and an `OpCapability` emitted for it in the
196    /// result. For this reason, more specific capabilities should be listed
197    /// before more general.
198    ///
199    /// [`capabilities_used`]: Writer::capabilities_used
200    pub(super) fn require_any(
201        &mut self,
202        what: &'static str,
203        capabilities: &[spirv::Capability],
204    ) -> Result<(), Error> {
205        match *capabilities {
206            [] => Ok(()),
207            [first, ..] => {
208                // Find the first acceptable capability, or return an error if
209                // there is none.
210                let selected = match self.capabilities_available {
211                    None => first,
212                    Some(ref available) => {
213                        match capabilities
214                            .iter()
215                            // need explicit type for hashbrown::HashSet::contains fn call to keep rustc happy
216                            .find(|cap| available.contains::<spirv::Capability>(cap))
217                        {
218                            Some(&cap) => cap,
219                            None => {
220                                return Err(Error::MissingCapabilities(what, capabilities.to_vec()))
221                            }
222                        }
223                    }
224                };
225                self.capabilities_used.insert(selected);
226                Ok(())
227            }
228        }
229    }
230
231    /// Indicate that the code requires all of the listed capabilities.
232    ///
233    /// If all entries of `capabilities` appear in the available capabilities
234    /// specified in the [`Options`] from which this `Writer` was created
235    /// (including the case where [`Options::capabilities`] is `None`), add
236    /// them all to this `Writer`'s [`capabilities_used`] table, and return
237    /// `Ok(())`. If at least one of the listed capabilities is not available,
238    /// do not add anything to the `capabilities_used` table, and return the
239    /// first unavailable requested capability, wrapped in `Err()`.
240    ///
241    /// This method is does not return an [`enum@Error`] in case of failure
242    /// because it may be used in cases where the caller can recover (e.g.,
243    /// with a polyfill) if the requested capabilities are not available. In
244    /// this case, it would be unnecessary work to find *all* the unavailable
245    /// requested capabilities, and to allocate a `Vec` for them, just so we
246    /// could return an [`Error::MissingCapabilities`]).
247    ///
248    /// [`capabilities_used`]: Writer::capabilities_used
249    pub(super) fn require_all(
250        &mut self,
251        capabilities: &[spirv::Capability],
252    ) -> Result<(), spirv::Capability> {
253        if let Some(ref available) = self.capabilities_available {
254            for requested in capabilities {
255                if !available.contains(requested) {
256                    return Err(*requested);
257                }
258            }
259        }
260
261        for requested in capabilities {
262            self.capabilities_used.insert(*requested);
263        }
264
265        Ok(())
266    }
267
268    /// Indicate that the code uses the given extension.
269    pub(super) fn use_extension(&mut self, extension: &'static str) {
270        self.extensions_used.insert(extension);
271    }
272
273    pub(super) fn get_type_id(&mut self, lookup_ty: LookupType) -> Word {
274        match self.lookup_type.entry(lookup_ty) {
275            Entry::Occupied(e) => *e.get(),
276            Entry::Vacant(e) => {
277                let local = match lookup_ty {
278                    LookupType::Handle(_handle) => unreachable!("Handles are populated at start"),
279                    LookupType::Local(local) => local,
280                };
281
282                let id = self.id_gen.next();
283                e.insert(id);
284                self.write_type_declaration_local(id, local);
285                id
286            }
287        }
288    }
289
290    pub(super) fn get_handle_type_id(&mut self, handle: Handle<crate::Type>) -> Word {
291        self.get_type_id(LookupType::Handle(handle))
292    }
293
294    pub(super) fn get_expression_lookup_type(&mut self, tr: &TypeResolution) -> LookupType {
295        match *tr {
296            TypeResolution::Handle(ty_handle) => LookupType::Handle(ty_handle),
297            TypeResolution::Value(ref inner) => {
298                let inner_local_type = self.localtype_from_inner(inner).unwrap();
299                LookupType::Local(inner_local_type)
300            }
301        }
302    }
303
304    pub(super) fn get_expression_type_id(&mut self, tr: &TypeResolution) -> Word {
305        let lookup_ty = self.get_expression_lookup_type(tr);
306        self.get_type_id(lookup_ty)
307    }
308
309    pub(super) fn get_localtype_id(&mut self, local: LocalType) -> Word {
310        self.get_type_id(LookupType::Local(local))
311    }
312
313    pub(super) fn get_pointer_type_id(&mut self, base: Word, class: spirv::StorageClass) -> Word {
314        self.get_type_id(LookupType::Local(LocalType::Pointer { base, class }))
315    }
316
317    pub(super) fn get_handle_pointer_type_id(
318        &mut self,
319        base: Handle<crate::Type>,
320        class: spirv::StorageClass,
321    ) -> Word {
322        let base_id = self.get_handle_type_id(base);
323        self.get_pointer_type_id(base_id, class)
324    }
325
326    pub(super) fn get_ray_query_pointer_id(&mut self) -> Word {
327        let rq_id = self.get_type_id(LookupType::Local(LocalType::RayQuery));
328        self.get_pointer_type_id(rq_id, spirv::StorageClass::Function)
329    }
330
331    /// Return a SPIR-V type for a pointer to `resolution`.
332    ///
333    /// The given `resolution` must be one that we can represent
334    /// either as a `LocalType::Pointer` or `LocalType::LocalPointer`.
335    pub(super) fn get_resolution_pointer_id(
336        &mut self,
337        resolution: &TypeResolution,
338        class: spirv::StorageClass,
339    ) -> Word {
340        let resolution_type_id = self.get_expression_type_id(resolution);
341        self.get_pointer_type_id(resolution_type_id, class)
342    }
343
344    pub(super) fn get_numeric_type_id(&mut self, numeric: NumericType) -> Word {
345        self.get_type_id(LocalType::Numeric(numeric).into())
346    }
347
348    pub(super) fn get_u32_type_id(&mut self) -> Word {
349        self.get_numeric_type_id(NumericType::Scalar(crate::Scalar::U32))
350    }
351
352    pub(super) fn get_f32_type_id(&mut self) -> Word {
353        self.get_numeric_type_id(NumericType::Scalar(crate::Scalar::F32))
354    }
355
356    pub(super) fn get_vec2u_type_id(&mut self) -> Word {
357        self.get_numeric_type_id(NumericType::Vector {
358            size: crate::VectorSize::Bi,
359            scalar: crate::Scalar::U32,
360        })
361    }
362
363    pub(super) fn get_vec2f_type_id(&mut self) -> Word {
364        self.get_numeric_type_id(NumericType::Vector {
365            size: crate::VectorSize::Bi,
366            scalar: crate::Scalar::F32,
367        })
368    }
369
370    pub(super) fn get_vec3u_type_id(&mut self) -> Word {
371        self.get_numeric_type_id(NumericType::Vector {
372            size: crate::VectorSize::Tri,
373            scalar: crate::Scalar::U32,
374        })
375    }
376
377    pub(super) fn get_f32_pointer_type_id(&mut self, class: spirv::StorageClass) -> Word {
378        let f32_id = self.get_f32_type_id();
379        self.get_pointer_type_id(f32_id, class)
380    }
381
382    pub(super) fn get_vec2u_pointer_type_id(&mut self, class: spirv::StorageClass) -> Word {
383        let vec2u_id = self.get_numeric_type_id(NumericType::Vector {
384            size: crate::VectorSize::Bi,
385            scalar: crate::Scalar::U32,
386        });
387        self.get_pointer_type_id(vec2u_id, class)
388    }
389
390    pub(super) fn get_vec3u_pointer_type_id(&mut self, class: spirv::StorageClass) -> Word {
391        let vec3u_id = self.get_numeric_type_id(NumericType::Vector {
392            size: crate::VectorSize::Tri,
393            scalar: crate::Scalar::U32,
394        });
395        self.get_pointer_type_id(vec3u_id, class)
396    }
397
398    pub(super) fn get_bool_type_id(&mut self) -> Word {
399        self.get_numeric_type_id(NumericType::Scalar(crate::Scalar::BOOL))
400    }
401
402    pub(super) fn get_vec2_bool_type_id(&mut self) -> Word {
403        self.get_numeric_type_id(NumericType::Vector {
404            size: crate::VectorSize::Bi,
405            scalar: crate::Scalar::BOOL,
406        })
407    }
408
409    pub(super) fn get_vec3_bool_type_id(&mut self) -> Word {
410        self.get_numeric_type_id(NumericType::Vector {
411            size: crate::VectorSize::Tri,
412            scalar: crate::Scalar::BOOL,
413        })
414    }
415
416    pub(super) fn decorate(&mut self, id: Word, decoration: spirv::Decoration, operands: &[Word]) {
417        self.annotations
418            .push(Instruction::decorate(id, decoration, operands));
419    }
420
421    /// Return `inner` as a `LocalType`, if that's possible.
422    ///
423    /// If `inner` can be represented as a `LocalType`, return
424    /// `Some(local_type)`.
425    ///
426    /// Otherwise, return `None`. In this case, the type must always be looked
427    /// up using a `LookupType::Handle`.
428    fn localtype_from_inner(&mut self, inner: &crate::TypeInner) -> Option<LocalType> {
429        Some(match *inner {
430            crate::TypeInner::Scalar(_)
431            | crate::TypeInner::Atomic(_)
432            | crate::TypeInner::Vector { .. }
433            | crate::TypeInner::Matrix { .. } => {
434                // We expect `NumericType::from_inner` to handle all
435                // these cases, so unwrap.
436                LocalType::Numeric(NumericType::from_inner(inner).unwrap())
437            }
438            crate::TypeInner::Pointer { base, space } => {
439                let base_type_id = self.get_handle_type_id(base);
440                LocalType::Pointer {
441                    base: base_type_id,
442                    class: map_storage_class(space),
443                }
444            }
445            crate::TypeInner::ValuePointer {
446                size,
447                scalar,
448                space,
449            } => {
450                let base_numeric_type = match size {
451                    Some(size) => NumericType::Vector { size, scalar },
452                    None => NumericType::Scalar(scalar),
453                };
454                LocalType::Pointer {
455                    base: self.get_numeric_type_id(base_numeric_type),
456                    class: map_storage_class(space),
457                }
458            }
459            crate::TypeInner::Image {
460                dim,
461                arrayed,
462                class,
463            } => LocalType::Image(LocalImageType::from_inner(dim, arrayed, class)),
464            crate::TypeInner::Sampler { comparison: _ } => LocalType::Sampler,
465            crate::TypeInner::AccelerationStructure { .. } => LocalType::AccelerationStructure,
466            crate::TypeInner::RayQuery { .. } => LocalType::RayQuery,
467            crate::TypeInner::Array { .. }
468            | crate::TypeInner::Struct { .. }
469            | crate::TypeInner::BindingArray { .. } => return None,
470        })
471    }
472
473    /// Resolve the [`BindingInfo`] for a [`crate::ResourceBinding`] from the
474    /// provided [`Writer::binding_map`].
475    ///
476    /// If the specified resource is not present in the binding map this will
477    /// return an error, unless [`Writer::fake_missing_bindings`] is set.
478    fn resolve_resource_binding(
479        &self,
480        res_binding: &crate::ResourceBinding,
481    ) -> Result<BindingInfo, Error> {
482        match self.binding_map.get(res_binding) {
483            Some(target) => Ok(*target),
484            None if self.fake_missing_bindings => Ok(BindingInfo {
485                descriptor_set: res_binding.group,
486                binding: res_binding.binding,
487                binding_array_size: None,
488            }),
489            None => Err(Error::MissingBinding(*res_binding)),
490        }
491    }
492
493    /// Emits code for any wrapper functions required by the expressions in ir_function.
494    /// The IDs of any emitted functions will be stored in [`Self::wrapped_functions`].
495    fn write_wrapped_functions(
496        &mut self,
497        ir_function: &crate::Function,
498        info: &FunctionInfo,
499        ir_module: &crate::Module,
500    ) -> Result<(), Error> {
501        log::trace!("Generating wrapped functions for {:?}", ir_function.name);
502
503        for (expr_handle, expr) in ir_function.expressions.iter() {
504            match *expr {
505                crate::Expression::Binary { op, left, right } => {
506                    let expr_ty_inner = info[expr_handle].ty.inner_with(&ir_module.types);
507                    if let Some(expr_ty) = NumericType::from_inner(expr_ty_inner) {
508                        match (op, expr_ty.scalar().kind) {
509                            // Division and modulo are undefined behaviour when the
510                            // dividend is the minimum representable value and the divisor
511                            // is negative one, or when the divisor is zero. These wrapped
512                            // functions override the divisor to one in these cases,
513                            // matching the WGSL spec.
514                            (
515                                crate::BinaryOperator::Divide | crate::BinaryOperator::Modulo,
516                                crate::ScalarKind::Sint | crate::ScalarKind::Uint,
517                            ) => {
518                                self.write_wrapped_binary_op(
519                                    op,
520                                    expr_ty,
521                                    &info[left].ty,
522                                    &info[right].ty,
523                                )?;
524                            }
525                            _ => {}
526                        }
527                    }
528                }
529                _ => {}
530            }
531        }
532
533        Ok(())
534    }
535
536    /// Write a SPIR-V function that performs the operator `op` with Naga IR semantics.
537    ///
538    /// Define a function that performs an integer division or modulo operation,
539    /// except that using a divisor of zero or causing signed overflow with a
540    /// divisor of -1 returns the numerator unchanged, rather than exhibiting
541    /// undefined behavior.
542    ///
543    /// Store the generated function's id in the [`wrapped_functions`] table.
544    ///
545    /// The operator `op` must be either [`Divide`] or [`Modulo`].
546    ///
547    /// # Panics
548    ///
549    /// The `return_type`, `left_type` or `right_type` arguments must all be
550    /// integer scalars or vectors. If not, this function panics.
551    ///
552    /// [`wrapped_functions`]: Writer::wrapped_functions
553    /// [`Divide`]: crate::BinaryOperator::Divide
554    /// [`Modulo`]: crate::BinaryOperator::Modulo
555    fn write_wrapped_binary_op(
556        &mut self,
557        op: crate::BinaryOperator,
558        return_type: NumericType,
559        left_type: &TypeResolution,
560        right_type: &TypeResolution,
561    ) -> Result<(), Error> {
562        let return_type_id = self.get_localtype_id(LocalType::Numeric(return_type));
563        let left_type_id = self.get_expression_type_id(left_type);
564        let right_type_id = self.get_expression_type_id(right_type);
565
566        // Check if we've already emitted this function.
567        let wrapped = WrappedFunction::BinaryOp {
568            op,
569            left_type_id,
570            right_type_id,
571        };
572        let function_id = match self.wrapped_functions.entry(wrapped) {
573            Entry::Occupied(_) => return Ok(()),
574            Entry::Vacant(e) => *e.insert(self.id_gen.next()),
575        };
576
577        let scalar = return_type.scalar();
578
579        if self.flags.contains(WriterFlags::DEBUG) {
580            let function_name = match op {
581                crate::BinaryOperator::Divide => "naga_div",
582                crate::BinaryOperator::Modulo => "naga_mod",
583                _ => unreachable!(),
584            };
585            self.debugs
586                .push(Instruction::name(function_id, function_name));
587        }
588        let mut function = Function::default();
589
590        let function_type_id = self.get_function_type(LookupFunctionType {
591            parameter_type_ids: vec![left_type_id, right_type_id],
592            return_type_id,
593        });
594        function.signature = Some(Instruction::function(
595            return_type_id,
596            function_id,
597            spirv::FunctionControl::empty(),
598            function_type_id,
599        ));
600
601        let lhs_id = self.id_gen.next();
602        let rhs_id = self.id_gen.next();
603        if self.flags.contains(WriterFlags::DEBUG) {
604            self.debugs.push(Instruction::name(lhs_id, "lhs"));
605            self.debugs.push(Instruction::name(rhs_id, "rhs"));
606        }
607        let left_par = Instruction::function_parameter(left_type_id, lhs_id);
608        let right_par = Instruction::function_parameter(right_type_id, rhs_id);
609        for instruction in [left_par, right_par] {
610            function.parameters.push(FunctionArgument {
611                instruction,
612                handle_id: 0,
613            });
614        }
615
616        let label_id = self.id_gen.next();
617        let mut block = Block::new(label_id);
618
619        let bool_type = return_type.with_scalar(crate::Scalar::BOOL);
620        let bool_type_id = self.get_numeric_type_id(bool_type);
621
622        let maybe_splat_const = |writer: &mut Self, const_id| match return_type {
623            NumericType::Scalar(_) => const_id,
624            NumericType::Vector { size, .. } => {
625                let constituent_ids = [const_id; crate::VectorSize::MAX];
626                writer.get_constant_composite(
627                    LookupType::Local(LocalType::Numeric(return_type)),
628                    &constituent_ids[..size as usize],
629                )
630            }
631            NumericType::Matrix { .. } => unreachable!(),
632        };
633
634        let const_zero_id = self.get_constant_scalar_with(0, scalar)?;
635        let composite_zero_id = maybe_splat_const(self, const_zero_id);
636        let rhs_eq_zero_id = self.id_gen.next();
637        block.body.push(Instruction::binary(
638            spirv::Op::IEqual,
639            bool_type_id,
640            rhs_eq_zero_id,
641            rhs_id,
642            composite_zero_id,
643        ));
644        let divisor_selector_id = match scalar.kind {
645            crate::ScalarKind::Sint => {
646                let (const_min_id, const_neg_one_id) = match scalar.width {
647                    4 => Ok((
648                        self.get_constant_scalar(crate::Literal::I32(i32::MIN)),
649                        self.get_constant_scalar(crate::Literal::I32(-1i32)),
650                    )),
651                    8 => Ok((
652                        self.get_constant_scalar(crate::Literal::I64(i64::MIN)),
653                        self.get_constant_scalar(crate::Literal::I64(-1i64)),
654                    )),
655                    _ => Err(Error::Validation("Unexpected scalar width")),
656                }?;
657                let composite_min_id = maybe_splat_const(self, const_min_id);
658                let composite_neg_one_id = maybe_splat_const(self, const_neg_one_id);
659
660                let lhs_eq_int_min_id = self.id_gen.next();
661                block.body.push(Instruction::binary(
662                    spirv::Op::IEqual,
663                    bool_type_id,
664                    lhs_eq_int_min_id,
665                    lhs_id,
666                    composite_min_id,
667                ));
668                let rhs_eq_neg_one_id = self.id_gen.next();
669                block.body.push(Instruction::binary(
670                    spirv::Op::IEqual,
671                    bool_type_id,
672                    rhs_eq_neg_one_id,
673                    rhs_id,
674                    composite_neg_one_id,
675                ));
676                let lhs_eq_int_min_and_rhs_eq_neg_one_id = self.id_gen.next();
677                block.body.push(Instruction::binary(
678                    spirv::Op::LogicalAnd,
679                    bool_type_id,
680                    lhs_eq_int_min_and_rhs_eq_neg_one_id,
681                    lhs_eq_int_min_id,
682                    rhs_eq_neg_one_id,
683                ));
684                let rhs_eq_zero_or_lhs_eq_int_min_and_rhs_eq_neg_one_id = self.id_gen.next();
685                block.body.push(Instruction::binary(
686                    spirv::Op::LogicalOr,
687                    bool_type_id,
688                    rhs_eq_zero_or_lhs_eq_int_min_and_rhs_eq_neg_one_id,
689                    rhs_eq_zero_id,
690                    lhs_eq_int_min_and_rhs_eq_neg_one_id,
691                ));
692                rhs_eq_zero_or_lhs_eq_int_min_and_rhs_eq_neg_one_id
693            }
694            crate::ScalarKind::Uint => rhs_eq_zero_id,
695            _ => unreachable!(),
696        };
697
698        let const_one_id = self.get_constant_scalar_with(1, scalar)?;
699        let composite_one_id = maybe_splat_const(self, const_one_id);
700        let divisor_id = self.id_gen.next();
701        block.body.push(Instruction::select(
702            right_type_id,
703            divisor_id,
704            divisor_selector_id,
705            composite_one_id,
706            rhs_id,
707        ));
708        let op = match (op, scalar.kind) {
709            (crate::BinaryOperator::Divide, crate::ScalarKind::Sint) => spirv::Op::SDiv,
710            (crate::BinaryOperator::Divide, crate::ScalarKind::Uint) => spirv::Op::UDiv,
711            (crate::BinaryOperator::Modulo, crate::ScalarKind::Sint) => spirv::Op::SRem,
712            (crate::BinaryOperator::Modulo, crate::ScalarKind::Uint) => spirv::Op::UMod,
713            _ => unreachable!(),
714        };
715        let return_id = self.id_gen.next();
716        block.body.push(Instruction::binary(
717            op,
718            return_type_id,
719            return_id,
720            lhs_id,
721            divisor_id,
722        ));
723
724        function.consume(block, Instruction::return_value(return_id));
725        function.to_words(&mut self.logical_layout.function_definitions);
726        Ok(())
727    }
728
729    fn write_function(
730        &mut self,
731        ir_function: &crate::Function,
732        info: &FunctionInfo,
733        ir_module: &crate::Module,
734        mut interface: Option<FunctionInterface>,
735        debug_info: &Option<DebugInfoInner>,
736    ) -> Result<Word, Error> {
737        self.write_wrapped_functions(ir_function, info, ir_module)?;
738
739        log::trace!("Generating code for {:?}", ir_function.name);
740        let mut function = Function::default();
741
742        let prelude_id = self.id_gen.next();
743        let mut prelude = Block::new(prelude_id);
744        let mut ep_context = EntryPointContext {
745            argument_ids: Vec::new(),
746            results: Vec::new(),
747        };
748
749        let mut local_invocation_id = None;
750
751        let mut parameter_type_ids = Vec::with_capacity(ir_function.arguments.len());
752        for argument in ir_function.arguments.iter() {
753            let class = spirv::StorageClass::Input;
754            let handle_ty = ir_module.types[argument.ty].inner.is_handle();
755            let argument_type_id = if handle_ty {
756                self.get_handle_pointer_type_id(argument.ty, spirv::StorageClass::UniformConstant)
757            } else {
758                self.get_handle_type_id(argument.ty)
759            };
760
761            if let Some(ref mut iface) = interface {
762                let id = if let Some(ref binding) = argument.binding {
763                    let name = argument.name.as_deref();
764
765                    let varying_id = self.write_varying(
766                        ir_module,
767                        iface.stage,
768                        class,
769                        name,
770                        argument.ty,
771                        binding,
772                    )?;
773                    iface.varying_ids.push(varying_id);
774                    let id = self.load_io_with_f16_polyfill(
775                        &mut prelude.body,
776                        varying_id,
777                        argument_type_id,
778                    );
779
780                    if binding == &crate::Binding::BuiltIn(crate::BuiltIn::LocalInvocationId) {
781                        local_invocation_id = Some(id);
782                    }
783
784                    id
785                } else if let crate::TypeInner::Struct { ref members, .. } =
786                    ir_module.types[argument.ty].inner
787                {
788                    let struct_id = self.id_gen.next();
789                    let mut constituent_ids = Vec::with_capacity(members.len());
790                    for member in members {
791                        let type_id = self.get_handle_type_id(member.ty);
792                        let name = member.name.as_deref();
793                        let binding = member.binding.as_ref().unwrap();
794                        let varying_id = self.write_varying(
795                            ir_module,
796                            iface.stage,
797                            class,
798                            name,
799                            member.ty,
800                            binding,
801                        )?;
802                        iface.varying_ids.push(varying_id);
803                        let id =
804                            self.load_io_with_f16_polyfill(&mut prelude.body, varying_id, type_id);
805                        constituent_ids.push(id);
806
807                        if binding == &crate::Binding::BuiltIn(crate::BuiltIn::LocalInvocationId) {
808                            local_invocation_id = Some(id);
809                        }
810                    }
811                    prelude.body.push(Instruction::composite_construct(
812                        argument_type_id,
813                        struct_id,
814                        &constituent_ids,
815                    ));
816                    struct_id
817                } else {
818                    unreachable!("Missing argument binding on an entry point");
819                };
820                ep_context.argument_ids.push(id);
821            } else {
822                let argument_id = self.id_gen.next();
823                let instruction = Instruction::function_parameter(argument_type_id, argument_id);
824                if self.flags.contains(WriterFlags::DEBUG) {
825                    if let Some(ref name) = argument.name {
826                        self.debugs.push(Instruction::name(argument_id, name));
827                    }
828                }
829                function.parameters.push(FunctionArgument {
830                    instruction,
831                    handle_id: if handle_ty {
832                        let id = self.id_gen.next();
833                        prelude.body.push(Instruction::load(
834                            self.get_handle_type_id(argument.ty),
835                            id,
836                            argument_id,
837                            None,
838                        ));
839                        id
840                    } else {
841                        0
842                    },
843                });
844                parameter_type_ids.push(argument_type_id);
845            };
846        }
847
848        let return_type_id = match ir_function.result {
849            Some(ref result) => {
850                if let Some(ref mut iface) = interface {
851                    let mut has_point_size = false;
852                    let class = spirv::StorageClass::Output;
853                    if let Some(ref binding) = result.binding {
854                        has_point_size |=
855                            *binding == crate::Binding::BuiltIn(crate::BuiltIn::PointSize);
856                        let type_id = self.get_handle_type_id(result.ty);
857                        let varying_id = self.write_varying(
858                            ir_module,
859                            iface.stage,
860                            class,
861                            None,
862                            result.ty,
863                            binding,
864                        )?;
865                        iface.varying_ids.push(varying_id);
866                        ep_context.results.push(ResultMember {
867                            id: varying_id,
868                            type_id,
869                            built_in: binding.to_built_in(),
870                        });
871                    } else if let crate::TypeInner::Struct { ref members, .. } =
872                        ir_module.types[result.ty].inner
873                    {
874                        for member in members {
875                            let type_id = self.get_handle_type_id(member.ty);
876                            let name = member.name.as_deref();
877                            let binding = member.binding.as_ref().unwrap();
878                            has_point_size |=
879                                *binding == crate::Binding::BuiltIn(crate::BuiltIn::PointSize);
880                            let varying_id = self.write_varying(
881                                ir_module,
882                                iface.stage,
883                                class,
884                                name,
885                                member.ty,
886                                binding,
887                            )?;
888                            iface.varying_ids.push(varying_id);
889                            ep_context.results.push(ResultMember {
890                                id: varying_id,
891                                type_id,
892                                built_in: binding.to_built_in(),
893                            });
894                        }
895                    } else {
896                        unreachable!("Missing result binding on an entry point");
897                    }
898
899                    if self.flags.contains(WriterFlags::FORCE_POINT_SIZE)
900                        && iface.stage == crate::ShaderStage::Vertex
901                        && !has_point_size
902                    {
903                        // add point size artificially
904                        let varying_id = self.id_gen.next();
905                        let pointer_type_id = self.get_f32_pointer_type_id(class);
906                        Instruction::variable(pointer_type_id, varying_id, class, None)
907                            .to_words(&mut self.logical_layout.declarations);
908                        self.decorate(
909                            varying_id,
910                            spirv::Decoration::BuiltIn,
911                            &[spirv::BuiltIn::PointSize as u32],
912                        );
913                        iface.varying_ids.push(varying_id);
914
915                        let default_value_id = self.get_constant_scalar(crate::Literal::F32(1.0));
916                        prelude
917                            .body
918                            .push(Instruction::store(varying_id, default_value_id, None));
919                    }
920                    self.void_type
921                } else {
922                    self.get_handle_type_id(result.ty)
923                }
924            }
925            None => self.void_type,
926        };
927
928        let lookup_function_type = LookupFunctionType {
929            parameter_type_ids,
930            return_type_id,
931        };
932
933        let function_id = self.id_gen.next();
934        if self.flags.contains(WriterFlags::DEBUG) {
935            if let Some(ref name) = ir_function.name {
936                self.debugs.push(Instruction::name(function_id, name));
937            }
938        }
939
940        let function_type = self.get_function_type(lookup_function_type);
941        function.signature = Some(Instruction::function(
942            return_type_id,
943            function_id,
944            spirv::FunctionControl::empty(),
945            function_type,
946        ));
947
948        if interface.is_some() {
949            function.entry_point_context = Some(ep_context);
950        }
951
952        // fill up the `GlobalVariable::access_id`
953        for gv in self.global_variables.iter_mut() {
954            gv.reset_for_function();
955        }
956        for (handle, var) in ir_module.global_variables.iter() {
957            if info[handle].is_empty() {
958                continue;
959            }
960
961            let mut gv = self.global_variables[handle].clone();
962            if let Some(ref mut iface) = interface {
963                // Have to include global variables in the interface
964                if self.physical_layout.version >= 0x10400 {
965                    iface.varying_ids.push(gv.var_id);
966                }
967            }
968
969            // Handle globals are pre-emitted and should be loaded automatically.
970            //
971            // Any that are binding arrays we skip as we cannot load the array, we must load the result after indexing.
972            match ir_module.types[var.ty].inner {
973                crate::TypeInner::BindingArray { .. } => {
974                    gv.access_id = gv.var_id;
975                }
976                _ => {
977                    if var.space == crate::AddressSpace::Handle {
978                        let var_type_id = self.get_handle_type_id(var.ty);
979                        let id = self.id_gen.next();
980                        prelude
981                            .body
982                            .push(Instruction::load(var_type_id, id, gv.var_id, None));
983                        gv.access_id = gv.var_id;
984                        gv.handle_id = id;
985                    } else if global_needs_wrapper(ir_module, var) {
986                        let class = map_storage_class(var.space);
987                        let pointer_type_id = self.get_handle_pointer_type_id(var.ty, class);
988                        let index_id = self.get_index_constant(0);
989                        let id = self.id_gen.next();
990                        prelude.body.push(Instruction::access_chain(
991                            pointer_type_id,
992                            id,
993                            gv.var_id,
994                            &[index_id],
995                        ));
996                        gv.access_id = id;
997                    } else {
998                        // by default, the variable ID is accessed as is
999                        gv.access_id = gv.var_id;
1000                    };
1001                }
1002            }
1003
1004            // work around borrow checking in the presence of `self.xxx()` calls
1005            self.global_variables[handle] = gv;
1006        }
1007
1008        // Create a `BlockContext` for generating SPIR-V for the function's
1009        // body.
1010        let mut context = BlockContext {
1011            ir_module,
1012            ir_function,
1013            fun_info: info,
1014            function: &mut function,
1015            // Re-use the cached expression table from prior functions.
1016            cached: core::mem::take(&mut self.saved_cached),
1017
1018            // Steal the Writer's temp list for a bit.
1019            temp_list: core::mem::take(&mut self.temp_list),
1020            force_loop_bounding: self.force_loop_bounding,
1021            writer: self,
1022            expression_constness: super::ExpressionConstnessTracker::from_arena(
1023                &ir_function.expressions,
1024            ),
1025        };
1026
1027        // fill up the pre-emitted and const expressions
1028        context.cached.reset(ir_function.expressions.len());
1029        for (handle, expr) in ir_function.expressions.iter() {
1030            if (expr.needs_pre_emit() && !matches!(*expr, crate::Expression::LocalVariable(_)))
1031                || context.expression_constness.is_const(handle)
1032            {
1033                context.cache_expression_value(handle, &mut prelude)?;
1034            }
1035        }
1036
1037        for (handle, variable) in ir_function.local_variables.iter() {
1038            let id = context.gen_id();
1039
1040            if context.writer.flags.contains(WriterFlags::DEBUG) {
1041                if let Some(ref name) = variable.name {
1042                    context.writer.debugs.push(Instruction::name(id, name));
1043                }
1044            }
1045
1046            let init_word = variable.init.map(|constant| context.cached[constant]);
1047            let pointer_type_id = context
1048                .writer
1049                .get_handle_pointer_type_id(variable.ty, spirv::StorageClass::Function);
1050            let instruction = Instruction::variable(
1051                pointer_type_id,
1052                id,
1053                spirv::StorageClass::Function,
1054                init_word.or_else(|| match ir_module.types[variable.ty].inner {
1055                    crate::TypeInner::RayQuery { .. } => None,
1056                    _ => {
1057                        let type_id = context.get_handle_type_id(variable.ty);
1058                        Some(context.writer.write_constant_null(type_id))
1059                    }
1060                }),
1061            );
1062            context
1063                .function
1064                .variables
1065                .insert(handle, LocalVariable { id, instruction });
1066        }
1067
1068        for (handle, expr) in ir_function.expressions.iter() {
1069            match *expr {
1070                crate::Expression::LocalVariable(_) => {
1071                    // Cache the `OpVariable` instruction we generated above as
1072                    // the value of this expression.
1073                    context.cache_expression_value(handle, &mut prelude)?;
1074                }
1075                crate::Expression::Access { base, .. }
1076                | crate::Expression::AccessIndex { base, .. } => {
1077                    // Count references to `base` by `Access` and `AccessIndex`
1078                    // instructions. See `access_uses` for details.
1079                    *context.function.access_uses.entry(base).or_insert(0) += 1;
1080                }
1081                _ => {}
1082            }
1083        }
1084
1085        let next_id = context.gen_id();
1086
1087        context
1088            .function
1089            .consume(prelude, Instruction::branch(next_id));
1090
1091        let workgroup_vars_init_exit_block_id =
1092            match (context.writer.zero_initialize_workgroup_memory, interface) {
1093                (
1094                    super::ZeroInitializeWorkgroupMemoryMode::Polyfill,
1095                    Some(
1096                        ref mut interface @ FunctionInterface {
1097                            stage: crate::ShaderStage::Compute,
1098                            ..
1099                        },
1100                    ),
1101                ) => context.writer.generate_workgroup_vars_init_block(
1102                    next_id,
1103                    ir_module,
1104                    info,
1105                    local_invocation_id,
1106                    interface,
1107                    context.function,
1108                ),
1109                _ => None,
1110            };
1111
1112        let main_id = if let Some(exit_id) = workgroup_vars_init_exit_block_id {
1113            exit_id
1114        } else {
1115            next_id
1116        };
1117
1118        context.write_function_body(main_id, debug_info.as_ref())?;
1119
1120        // Consume the `BlockContext`, ending its borrows and letting the
1121        // `Writer` steal back its cached expression table and temp_list.
1122        let BlockContext {
1123            cached, temp_list, ..
1124        } = context;
1125        self.saved_cached = cached;
1126        self.temp_list = temp_list;
1127
1128        function.to_words(&mut self.logical_layout.function_definitions);
1129
1130        Ok(function_id)
1131    }
1132
1133    fn write_execution_mode(
1134        &mut self,
1135        function_id: Word,
1136        mode: spirv::ExecutionMode,
1137    ) -> Result<(), Error> {
1138        //self.check(mode.required_capabilities())?;
1139        Instruction::execution_mode(function_id, mode, &[])
1140            .to_words(&mut self.logical_layout.execution_modes);
1141        Ok(())
1142    }
1143
1144    // TODO Move to instructions module
1145    fn write_entry_point(
1146        &mut self,
1147        entry_point: &crate::EntryPoint,
1148        info: &FunctionInfo,
1149        ir_module: &crate::Module,
1150        debug_info: &Option<DebugInfoInner>,
1151    ) -> Result<Instruction, Error> {
1152        let mut interface_ids = Vec::new();
1153        let function_id = self.write_function(
1154            &entry_point.function,
1155            info,
1156            ir_module,
1157            Some(FunctionInterface {
1158                varying_ids: &mut interface_ids,
1159                stage: entry_point.stage,
1160            }),
1161            debug_info,
1162        )?;
1163
1164        let exec_model = match entry_point.stage {
1165            crate::ShaderStage::Vertex => spirv::ExecutionModel::Vertex,
1166            crate::ShaderStage::Fragment => {
1167                self.write_execution_mode(function_id, spirv::ExecutionMode::OriginUpperLeft)?;
1168                match entry_point.early_depth_test {
1169                    Some(crate::EarlyDepthTest::Force) => {
1170                        self.write_execution_mode(
1171                            function_id,
1172                            spirv::ExecutionMode::EarlyFragmentTests,
1173                        )?;
1174                    }
1175                    Some(crate::EarlyDepthTest::Allow { conservative }) => {
1176                        // TODO: Consider emitting EarlyAndLateFragmentTestsAMD here, if available.
1177                        // https://github.khronos.org/SPIRV-Registry/extensions/AMD/SPV_AMD_shader_early_and_late_fragment_tests.html
1178                        // This permits early depth tests even if the shader writes to a storage
1179                        // binding
1180                        match conservative {
1181                            crate::ConservativeDepth::GreaterEqual => self.write_execution_mode(
1182                                function_id,
1183                                spirv::ExecutionMode::DepthGreater,
1184                            )?,
1185                            crate::ConservativeDepth::LessEqual => self.write_execution_mode(
1186                                function_id,
1187                                spirv::ExecutionMode::DepthLess,
1188                            )?,
1189                            crate::ConservativeDepth::Unchanged => self.write_execution_mode(
1190                                function_id,
1191                                spirv::ExecutionMode::DepthUnchanged,
1192                            )?,
1193                        }
1194                    }
1195                    None => {}
1196                }
1197                if let Some(ref result) = entry_point.function.result {
1198                    if contains_builtin(
1199                        result.binding.as_ref(),
1200                        result.ty,
1201                        &ir_module.types,
1202                        crate::BuiltIn::FragDepth,
1203                    ) {
1204                        self.write_execution_mode(
1205                            function_id,
1206                            spirv::ExecutionMode::DepthReplacing,
1207                        )?;
1208                    }
1209                }
1210                spirv::ExecutionModel::Fragment
1211            }
1212            crate::ShaderStage::Compute => {
1213                let execution_mode = spirv::ExecutionMode::LocalSize;
1214                //self.check(execution_mode.required_capabilities())?;
1215                Instruction::execution_mode(
1216                    function_id,
1217                    execution_mode,
1218                    &entry_point.workgroup_size,
1219                )
1220                .to_words(&mut self.logical_layout.execution_modes);
1221                spirv::ExecutionModel::GLCompute
1222            }
1223            crate::ShaderStage::Task | crate::ShaderStage::Mesh => unreachable!(),
1224        };
1225        //self.check(exec_model.required_capabilities())?;
1226
1227        Ok(Instruction::entry_point(
1228            exec_model,
1229            function_id,
1230            &entry_point.name,
1231            interface_ids.as_slice(),
1232        ))
1233    }
1234
1235    fn make_scalar(&mut self, id: Word, scalar: crate::Scalar) -> Instruction {
1236        use crate::ScalarKind as Sk;
1237
1238        let bits = (scalar.width * BITS_PER_BYTE) as u32;
1239        match scalar.kind {
1240            Sk::Sint | Sk::Uint => {
1241                let signedness = if scalar.kind == Sk::Sint {
1242                    super::instructions::Signedness::Signed
1243                } else {
1244                    super::instructions::Signedness::Unsigned
1245                };
1246                let cap = match bits {
1247                    8 => Some(spirv::Capability::Int8),
1248                    16 => Some(spirv::Capability::Int16),
1249                    64 => Some(spirv::Capability::Int64),
1250                    _ => None,
1251                };
1252                if let Some(cap) = cap {
1253                    self.capabilities_used.insert(cap);
1254                }
1255                Instruction::type_int(id, bits, signedness)
1256            }
1257            Sk::Float => {
1258                if bits == 64 {
1259                    self.capabilities_used.insert(spirv::Capability::Float64);
1260                }
1261                if bits == 16 {
1262                    self.capabilities_used.insert(spirv::Capability::Float16);
1263                    self.capabilities_used
1264                        .insert(spirv::Capability::StorageBuffer16BitAccess);
1265                    self.capabilities_used
1266                        .insert(spirv::Capability::UniformAndStorageBuffer16BitAccess);
1267                    if self.use_storage_input_output_16 {
1268                        self.capabilities_used
1269                            .insert(spirv::Capability::StorageInputOutput16);
1270                    }
1271                }
1272                Instruction::type_float(id, bits)
1273            }
1274            Sk::Bool => Instruction::type_bool(id),
1275            Sk::AbstractInt | Sk::AbstractFloat => {
1276                unreachable!("abstract types should never reach the backend");
1277            }
1278        }
1279    }
1280
1281    fn request_type_capabilities(&mut self, inner: &crate::TypeInner) -> Result<(), Error> {
1282        match *inner {
1283            crate::TypeInner::Image {
1284                dim,
1285                arrayed,
1286                class,
1287            } => {
1288                let sampled = match class {
1289                    crate::ImageClass::Sampled { .. } => true,
1290                    crate::ImageClass::Depth { .. } => true,
1291                    crate::ImageClass::Storage { format, .. } => {
1292                        self.request_image_format_capabilities(format.into())?;
1293                        false
1294                    }
1295                    crate::ImageClass::External => unimplemented!(),
1296                };
1297
1298                match dim {
1299                    crate::ImageDimension::D1 => {
1300                        if sampled {
1301                            self.require_any("sampled 1D images", &[spirv::Capability::Sampled1D])?;
1302                        } else {
1303                            self.require_any("1D storage images", &[spirv::Capability::Image1D])?;
1304                        }
1305                    }
1306                    crate::ImageDimension::Cube if arrayed => {
1307                        if sampled {
1308                            self.require_any(
1309                                "sampled cube array images",
1310                                &[spirv::Capability::SampledCubeArray],
1311                            )?;
1312                        } else {
1313                            self.require_any(
1314                                "cube array storage images",
1315                                &[spirv::Capability::ImageCubeArray],
1316                            )?;
1317                        }
1318                    }
1319                    _ => {}
1320                }
1321            }
1322            crate::TypeInner::AccelerationStructure { .. } => {
1323                self.require_any("Acceleration Structure", &[spirv::Capability::RayQueryKHR])?;
1324            }
1325            crate::TypeInner::RayQuery { .. } => {
1326                self.require_any("Ray Query", &[spirv::Capability::RayQueryKHR])?;
1327            }
1328            crate::TypeInner::Atomic(crate::Scalar { width: 8, kind: _ }) => {
1329                self.require_any("64 bit integer atomics", &[spirv::Capability::Int64Atomics])?;
1330            }
1331            crate::TypeInner::Atomic(crate::Scalar {
1332                width: 4,
1333                kind: crate::ScalarKind::Float,
1334            }) => {
1335                self.require_any(
1336                    "32 bit floating-point atomics",
1337                    &[spirv::Capability::AtomicFloat32AddEXT],
1338                )?;
1339                self.use_extension("SPV_EXT_shader_atomic_float_add");
1340            }
1341            // 16 bit floating-point support requires Float16 capability
1342            crate::TypeInner::Matrix {
1343                scalar: crate::Scalar::F16,
1344                ..
1345            }
1346            | crate::TypeInner::Vector {
1347                scalar: crate::Scalar::F16,
1348                ..
1349            }
1350            | crate::TypeInner::Scalar(crate::Scalar::F16) => {
1351                self.require_any("16 bit floating-point", &[spirv::Capability::Float16])?;
1352                self.use_extension("SPV_KHR_16bit_storage");
1353            }
1354            _ => {}
1355        }
1356        Ok(())
1357    }
1358
1359    fn write_numeric_type_declaration_local(&mut self, id: Word, numeric: NumericType) {
1360        let instruction = match numeric {
1361            NumericType::Scalar(scalar) => self.make_scalar(id, scalar),
1362            NumericType::Vector { size, scalar } => {
1363                let scalar_id = self.get_numeric_type_id(NumericType::Scalar(scalar));
1364                Instruction::type_vector(id, scalar_id, size)
1365            }
1366            NumericType::Matrix {
1367                columns,
1368                rows,
1369                scalar,
1370            } => {
1371                let column_id =
1372                    self.get_numeric_type_id(NumericType::Vector { size: rows, scalar });
1373                Instruction::type_matrix(id, column_id, columns)
1374            }
1375        };
1376
1377        instruction.to_words(&mut self.logical_layout.declarations);
1378    }
1379
1380    fn write_type_declaration_local(&mut self, id: Word, local_ty: LocalType) {
1381        let instruction = match local_ty {
1382            LocalType::Numeric(numeric) => {
1383                self.write_numeric_type_declaration_local(id, numeric);
1384                return;
1385            }
1386            LocalType::Pointer { base, class } => Instruction::type_pointer(id, class, base),
1387            LocalType::Image(image) => {
1388                let local_type = LocalType::Numeric(NumericType::Scalar(image.sampled_type));
1389                let type_id = self.get_localtype_id(local_type);
1390                Instruction::type_image(id, type_id, image.dim, image.flags, image.image_format)
1391            }
1392            LocalType::Sampler => Instruction::type_sampler(id),
1393            LocalType::SampledImage { image_type_id } => {
1394                Instruction::type_sampled_image(id, image_type_id)
1395            }
1396            LocalType::BindingArray { base, size } => {
1397                let inner_ty = self.get_handle_type_id(base);
1398                let scalar_id = self.get_constant_scalar(crate::Literal::U32(size));
1399                Instruction::type_array(id, inner_ty, scalar_id)
1400            }
1401            LocalType::AccelerationStructure => Instruction::type_acceleration_structure(id),
1402            LocalType::RayQuery => Instruction::type_ray_query(id),
1403        };
1404
1405        instruction.to_words(&mut self.logical_layout.declarations);
1406    }
1407
1408    fn write_type_declaration_arena(
1409        &mut self,
1410        module: &crate::Module,
1411        handle: Handle<crate::Type>,
1412    ) -> Result<Word, Error> {
1413        let ty = &module.types[handle];
1414        // If it's a type that needs SPIR-V capabilities, request them now.
1415        // This needs to happen regardless of the LocalType lookup succeeding,
1416        // because some types which map to the same LocalType have different
1417        // capability requirements. See https://github.com/gfx-rs/wgpu/issues/5569
1418        self.request_type_capabilities(&ty.inner)?;
1419        let id = if let Some(local) = self.localtype_from_inner(&ty.inner) {
1420            // This type can be represented as a `LocalType`, so check if we've
1421            // already written an instruction for it. If not, do so now, with
1422            // `write_type_declaration_local`.
1423            match self.lookup_type.entry(LookupType::Local(local)) {
1424                // We already have an id for this `LocalType`.
1425                Entry::Occupied(e) => *e.get(),
1426
1427                // It's a type we haven't seen before.
1428                Entry::Vacant(e) => {
1429                    let id = self.id_gen.next();
1430                    e.insert(id);
1431
1432                    self.write_type_declaration_local(id, local);
1433
1434                    id
1435                }
1436            }
1437        } else {
1438            use spirv::Decoration;
1439
1440            let id = self.id_gen.next();
1441            let instruction = match ty.inner {
1442                crate::TypeInner::Array { base, size, stride } => {
1443                    self.decorate(id, Decoration::ArrayStride, &[stride]);
1444
1445                    let type_id = self.get_handle_type_id(base);
1446                    match size.resolve(module.to_ctx())? {
1447                        crate::proc::IndexableLength::Known(length) => {
1448                            let length_id = self.get_index_constant(length);
1449                            Instruction::type_array(id, type_id, length_id)
1450                        }
1451                        crate::proc::IndexableLength::Dynamic => {
1452                            Instruction::type_runtime_array(id, type_id)
1453                        }
1454                    }
1455                }
1456                crate::TypeInner::BindingArray { base, size } => {
1457                    let type_id = self.get_handle_type_id(base);
1458                    match size.resolve(module.to_ctx())? {
1459                        crate::proc::IndexableLength::Known(length) => {
1460                            let length_id = self.get_index_constant(length);
1461                            Instruction::type_array(id, type_id, length_id)
1462                        }
1463                        crate::proc::IndexableLength::Dynamic => {
1464                            Instruction::type_runtime_array(id, type_id)
1465                        }
1466                    }
1467                }
1468                crate::TypeInner::Struct {
1469                    ref members,
1470                    span: _,
1471                } => {
1472                    let mut has_runtime_array = false;
1473                    let mut member_ids = Vec::with_capacity(members.len());
1474                    for (index, member) in members.iter().enumerate() {
1475                        let member_ty = &module.types[member.ty];
1476                        match member_ty.inner {
1477                            crate::TypeInner::Array {
1478                                base: _,
1479                                size: crate::ArraySize::Dynamic,
1480                                stride: _,
1481                            } => {
1482                                has_runtime_array = true;
1483                            }
1484                            _ => (),
1485                        }
1486                        self.decorate_struct_member(id, index, member, &module.types)?;
1487                        let member_id = self.get_handle_type_id(member.ty);
1488                        member_ids.push(member_id);
1489                    }
1490                    if has_runtime_array {
1491                        self.decorate(id, Decoration::Block, &[]);
1492                    }
1493                    Instruction::type_struct(id, member_ids.as_slice())
1494                }
1495
1496                // These all have TypeLocal representations, so they should have been
1497                // handled by `write_type_declaration_local` above.
1498                crate::TypeInner::Scalar(_)
1499                | crate::TypeInner::Atomic(_)
1500                | crate::TypeInner::Vector { .. }
1501                | crate::TypeInner::Matrix { .. }
1502                | crate::TypeInner::Pointer { .. }
1503                | crate::TypeInner::ValuePointer { .. }
1504                | crate::TypeInner::Image { .. }
1505                | crate::TypeInner::Sampler { .. }
1506                | crate::TypeInner::AccelerationStructure { .. }
1507                | crate::TypeInner::RayQuery { .. } => unreachable!(),
1508            };
1509
1510            instruction.to_words(&mut self.logical_layout.declarations);
1511            id
1512        };
1513
1514        // Add this handle as a new alias for that type.
1515        self.lookup_type.insert(LookupType::Handle(handle), id);
1516
1517        if self.flags.contains(WriterFlags::DEBUG) {
1518            if let Some(ref name) = ty.name {
1519                self.debugs.push(Instruction::name(id, name));
1520            }
1521        }
1522
1523        Ok(id)
1524    }
1525
1526    fn request_image_format_capabilities(
1527        &mut self,
1528        format: spirv::ImageFormat,
1529    ) -> Result<(), Error> {
1530        use spirv::ImageFormat as If;
1531        match format {
1532            If::Rg32f
1533            | If::Rg16f
1534            | If::R11fG11fB10f
1535            | If::R16f
1536            | If::Rgba16
1537            | If::Rgb10A2
1538            | If::Rg16
1539            | If::Rg8
1540            | If::R16
1541            | If::R8
1542            | If::Rgba16Snorm
1543            | If::Rg16Snorm
1544            | If::Rg8Snorm
1545            | If::R16Snorm
1546            | If::R8Snorm
1547            | If::Rg32i
1548            | If::Rg16i
1549            | If::Rg8i
1550            | If::R16i
1551            | If::R8i
1552            | If::Rgb10a2ui
1553            | If::Rg32ui
1554            | If::Rg16ui
1555            | If::Rg8ui
1556            | If::R16ui
1557            | If::R8ui => self.require_any(
1558                "storage image format",
1559                &[spirv::Capability::StorageImageExtendedFormats],
1560            ),
1561            If::R64ui | If::R64i => {
1562                self.use_extension("SPV_EXT_shader_image_int64");
1563                self.require_any(
1564                    "64-bit integer storage image format",
1565                    &[spirv::Capability::Int64ImageEXT],
1566                )
1567            }
1568            If::Unknown
1569            | If::Rgba32f
1570            | If::Rgba16f
1571            | If::R32f
1572            | If::Rgba8
1573            | If::Rgba8Snorm
1574            | If::Rgba32i
1575            | If::Rgba16i
1576            | If::Rgba8i
1577            | If::R32i
1578            | If::Rgba32ui
1579            | If::Rgba16ui
1580            | If::Rgba8ui
1581            | If::R32ui => Ok(()),
1582        }
1583    }
1584
1585    pub(super) fn get_index_constant(&mut self, index: Word) -> Word {
1586        self.get_constant_scalar(crate::Literal::U32(index))
1587    }
1588
1589    pub(super) fn get_constant_scalar_with(
1590        &mut self,
1591        value: u8,
1592        scalar: crate::Scalar,
1593    ) -> Result<Word, Error> {
1594        Ok(
1595            self.get_constant_scalar(crate::Literal::new(value, scalar).ok_or(
1596                Error::Validation("Unexpected kind and/or width for Literal"),
1597            )?),
1598        )
1599    }
1600
1601    pub(super) fn get_constant_scalar(&mut self, value: crate::Literal) -> Word {
1602        let scalar = CachedConstant::Literal(value.into());
1603        if let Some(&id) = self.cached_constants.get(&scalar) {
1604            return id;
1605        }
1606        let id = self.id_gen.next();
1607        self.write_constant_scalar(id, &value, None);
1608        self.cached_constants.insert(scalar, id);
1609        id
1610    }
1611
1612    fn write_constant_scalar(
1613        &mut self,
1614        id: Word,
1615        value: &crate::Literal,
1616        debug_name: Option<&String>,
1617    ) {
1618        if self.flags.contains(WriterFlags::DEBUG) {
1619            if let Some(name) = debug_name {
1620                self.debugs.push(Instruction::name(id, name));
1621            }
1622        }
1623        let type_id = self.get_numeric_type_id(NumericType::Scalar(value.scalar()));
1624        let instruction = match *value {
1625            crate::Literal::F64(value) => {
1626                let bits = value.to_bits();
1627                Instruction::constant_64bit(type_id, id, bits as u32, (bits >> 32) as u32)
1628            }
1629            crate::Literal::F32(value) => Instruction::constant_32bit(type_id, id, value.to_bits()),
1630            crate::Literal::F16(value) => {
1631                let low = value.to_bits();
1632                Instruction::constant_16bit(type_id, id, low as u32)
1633            }
1634            crate::Literal::U32(value) => Instruction::constant_32bit(type_id, id, value),
1635            crate::Literal::I32(value) => Instruction::constant_32bit(type_id, id, value as u32),
1636            crate::Literal::U64(value) => {
1637                Instruction::constant_64bit(type_id, id, value as u32, (value >> 32) as u32)
1638            }
1639            crate::Literal::I64(value) => {
1640                Instruction::constant_64bit(type_id, id, value as u32, (value >> 32) as u32)
1641            }
1642            crate::Literal::Bool(true) => Instruction::constant_true(type_id, id),
1643            crate::Literal::Bool(false) => Instruction::constant_false(type_id, id),
1644            crate::Literal::AbstractInt(_) | crate::Literal::AbstractFloat(_) => {
1645                unreachable!("Abstract types should not appear in IR presented to backends");
1646            }
1647        };
1648
1649        instruction.to_words(&mut self.logical_layout.declarations);
1650    }
1651
1652    pub(super) fn get_constant_composite(
1653        &mut self,
1654        ty: LookupType,
1655        constituent_ids: &[Word],
1656    ) -> Word {
1657        let composite = CachedConstant::Composite {
1658            ty,
1659            constituent_ids: constituent_ids.to_vec(),
1660        };
1661        if let Some(&id) = self.cached_constants.get(&composite) {
1662            return id;
1663        }
1664        let id = self.id_gen.next();
1665        self.write_constant_composite(id, ty, constituent_ids, None);
1666        self.cached_constants.insert(composite, id);
1667        id
1668    }
1669
1670    fn write_constant_composite(
1671        &mut self,
1672        id: Word,
1673        ty: LookupType,
1674        constituent_ids: &[Word],
1675        debug_name: Option<&String>,
1676    ) {
1677        if self.flags.contains(WriterFlags::DEBUG) {
1678            if let Some(name) = debug_name {
1679                self.debugs.push(Instruction::name(id, name));
1680            }
1681        }
1682        let type_id = self.get_type_id(ty);
1683        Instruction::constant_composite(type_id, id, constituent_ids)
1684            .to_words(&mut self.logical_layout.declarations);
1685    }
1686
1687    pub(super) fn get_constant_null(&mut self, type_id: Word) -> Word {
1688        let null = CachedConstant::ZeroValue(type_id);
1689        if let Some(&id) = self.cached_constants.get(&null) {
1690            return id;
1691        }
1692        let id = self.write_constant_null(type_id);
1693        self.cached_constants.insert(null, id);
1694        id
1695    }
1696
1697    pub(super) fn write_constant_null(&mut self, type_id: Word) -> Word {
1698        let null_id = self.id_gen.next();
1699        Instruction::constant_null(type_id, null_id)
1700            .to_words(&mut self.logical_layout.declarations);
1701        null_id
1702    }
1703
1704    fn write_constant_expr(
1705        &mut self,
1706        handle: Handle<crate::Expression>,
1707        ir_module: &crate::Module,
1708        mod_info: &ModuleInfo,
1709    ) -> Result<Word, Error> {
1710        let id = match ir_module.global_expressions[handle] {
1711            crate::Expression::Literal(literal) => self.get_constant_scalar(literal),
1712            crate::Expression::Constant(constant) => {
1713                let constant = &ir_module.constants[constant];
1714                self.constant_ids[constant.init]
1715            }
1716            crate::Expression::ZeroValue(ty) => {
1717                let type_id = self.get_handle_type_id(ty);
1718                self.get_constant_null(type_id)
1719            }
1720            crate::Expression::Compose { ty, ref components } => {
1721                let component_ids: Vec<_> = crate::proc::flatten_compose(
1722                    ty,
1723                    components,
1724                    &ir_module.global_expressions,
1725                    &ir_module.types,
1726                )
1727                .map(|component| self.constant_ids[component])
1728                .collect();
1729                self.get_constant_composite(LookupType::Handle(ty), component_ids.as_slice())
1730            }
1731            crate::Expression::Splat { size, value } => {
1732                let value_id = self.constant_ids[value];
1733                let component_ids = &[value_id; 4][..size as usize];
1734
1735                let ty = self.get_expression_lookup_type(&mod_info[handle]);
1736
1737                self.get_constant_composite(ty, component_ids)
1738            }
1739            _ => {
1740                return Err(Error::Override);
1741            }
1742        };
1743
1744        self.constant_ids[handle] = id;
1745
1746        Ok(id)
1747    }
1748
1749    pub(super) fn write_control_barrier(&mut self, flags: crate::Barrier, block: &mut Block) {
1750        let memory_scope = if flags.contains(crate::Barrier::STORAGE) {
1751            spirv::Scope::Device
1752        } else if flags.contains(crate::Barrier::SUB_GROUP) {
1753            spirv::Scope::Subgroup
1754        } else {
1755            spirv::Scope::Workgroup
1756        };
1757        let mut semantics = spirv::MemorySemantics::ACQUIRE_RELEASE;
1758        semantics.set(
1759            spirv::MemorySemantics::UNIFORM_MEMORY,
1760            flags.contains(crate::Barrier::STORAGE),
1761        );
1762        semantics.set(
1763            spirv::MemorySemantics::WORKGROUP_MEMORY,
1764            flags.contains(crate::Barrier::WORK_GROUP),
1765        );
1766        semantics.set(
1767            spirv::MemorySemantics::SUBGROUP_MEMORY,
1768            flags.contains(crate::Barrier::SUB_GROUP),
1769        );
1770        semantics.set(
1771            spirv::MemorySemantics::IMAGE_MEMORY,
1772            flags.contains(crate::Barrier::TEXTURE),
1773        );
1774        let exec_scope_id = if flags.contains(crate::Barrier::SUB_GROUP) {
1775            self.get_index_constant(spirv::Scope::Subgroup as u32)
1776        } else {
1777            self.get_index_constant(spirv::Scope::Workgroup as u32)
1778        };
1779        let mem_scope_id = self.get_index_constant(memory_scope as u32);
1780        let semantics_id = self.get_index_constant(semantics.bits());
1781        block.body.push(Instruction::control_barrier(
1782            exec_scope_id,
1783            mem_scope_id,
1784            semantics_id,
1785        ));
1786    }
1787
1788    pub(super) fn write_memory_barrier(&mut self, flags: crate::Barrier, block: &mut Block) {
1789        let mut semantics = spirv::MemorySemantics::ACQUIRE_RELEASE;
1790        semantics.set(
1791            spirv::MemorySemantics::UNIFORM_MEMORY,
1792            flags.contains(crate::Barrier::STORAGE),
1793        );
1794        semantics.set(
1795            spirv::MemorySemantics::WORKGROUP_MEMORY,
1796            flags.contains(crate::Barrier::WORK_GROUP),
1797        );
1798        semantics.set(
1799            spirv::MemorySemantics::SUBGROUP_MEMORY,
1800            flags.contains(crate::Barrier::SUB_GROUP),
1801        );
1802        semantics.set(
1803            spirv::MemorySemantics::IMAGE_MEMORY,
1804            flags.contains(crate::Barrier::TEXTURE),
1805        );
1806        let mem_scope_id = if flags.contains(crate::Barrier::STORAGE) {
1807            self.get_index_constant(spirv::Scope::Device as u32)
1808        } else if flags.contains(crate::Barrier::SUB_GROUP) {
1809            self.get_index_constant(spirv::Scope::Subgroup as u32)
1810        } else {
1811            self.get_index_constant(spirv::Scope::Workgroup as u32)
1812        };
1813        let semantics_id = self.get_index_constant(semantics.bits());
1814        block
1815            .body
1816            .push(Instruction::memory_barrier(mem_scope_id, semantics_id));
1817    }
1818
1819    fn generate_workgroup_vars_init_block(
1820        &mut self,
1821        entry_id: Word,
1822        ir_module: &crate::Module,
1823        info: &FunctionInfo,
1824        local_invocation_id: Option<Word>,
1825        interface: &mut FunctionInterface,
1826        function: &mut Function,
1827    ) -> Option<Word> {
1828        let body = ir_module
1829            .global_variables
1830            .iter()
1831            .filter(|&(handle, var)| {
1832                !info[handle].is_empty() && var.space == crate::AddressSpace::WorkGroup
1833            })
1834            .map(|(handle, var)| {
1835                // It's safe to use `var_id` here, not `access_id`, because only
1836                // variables in the `Uniform` and `StorageBuffer` address spaces
1837                // get wrapped, and we're initializing `WorkGroup` variables.
1838                let var_id = self.global_variables[handle].var_id;
1839                let var_type_id = self.get_handle_type_id(var.ty);
1840                let init_word = self.get_constant_null(var_type_id);
1841                Instruction::store(var_id, init_word, None)
1842            })
1843            .collect::<Vec<_>>();
1844
1845        if body.is_empty() {
1846            return None;
1847        }
1848
1849        let uint3_type_id = self.get_vec3u_type_id();
1850
1851        let mut pre_if_block = Block::new(entry_id);
1852
1853        let local_invocation_id = if let Some(local_invocation_id) = local_invocation_id {
1854            local_invocation_id
1855        } else {
1856            let varying_id = self.id_gen.next();
1857            let class = spirv::StorageClass::Input;
1858            let pointer_type_id = self.get_vec3u_pointer_type_id(class);
1859
1860            Instruction::variable(pointer_type_id, varying_id, class, None)
1861                .to_words(&mut self.logical_layout.declarations);
1862
1863            self.decorate(
1864                varying_id,
1865                spirv::Decoration::BuiltIn,
1866                &[spirv::BuiltIn::LocalInvocationId as u32],
1867            );
1868
1869            interface.varying_ids.push(varying_id);
1870            let id = self.id_gen.next();
1871            pre_if_block
1872                .body
1873                .push(Instruction::load(uint3_type_id, id, varying_id, None));
1874
1875            id
1876        };
1877
1878        let zero_id = self.get_constant_null(uint3_type_id);
1879        let bool3_type_id = self.get_vec3_bool_type_id();
1880
1881        let eq_id = self.id_gen.next();
1882        pre_if_block.body.push(Instruction::binary(
1883            spirv::Op::IEqual,
1884            bool3_type_id,
1885            eq_id,
1886            local_invocation_id,
1887            zero_id,
1888        ));
1889
1890        let condition_id = self.id_gen.next();
1891        let bool_type_id = self.get_bool_type_id();
1892        pre_if_block.body.push(Instruction::relational(
1893            spirv::Op::All,
1894            bool_type_id,
1895            condition_id,
1896            eq_id,
1897        ));
1898
1899        let merge_id = self.id_gen.next();
1900        pre_if_block.body.push(Instruction::selection_merge(
1901            merge_id,
1902            spirv::SelectionControl::NONE,
1903        ));
1904
1905        let accept_id = self.id_gen.next();
1906        function.consume(
1907            pre_if_block,
1908            Instruction::branch_conditional(condition_id, accept_id, merge_id),
1909        );
1910
1911        let accept_block = Block {
1912            label_id: accept_id,
1913            body,
1914        };
1915        function.consume(accept_block, Instruction::branch(merge_id));
1916
1917        let mut post_if_block = Block::new(merge_id);
1918
1919        self.write_control_barrier(crate::Barrier::WORK_GROUP, &mut post_if_block);
1920
1921        let next_id = self.id_gen.next();
1922        function.consume(post_if_block, Instruction::branch(next_id));
1923        Some(next_id)
1924    }
1925
1926    /// Generate an `OpVariable` for one value in an [`EntryPoint`]'s IO interface.
1927    ///
1928    /// The [`Binding`]s of the arguments and result of an [`EntryPoint`]'s
1929    /// [`Function`] describe a SPIR-V shader interface. In SPIR-V, the
1930    /// interface is represented by global variables in the `Input` and `Output`
1931    /// storage classes, with decorations indicating which builtin or location
1932    /// each variable corresponds to.
1933    ///
1934    /// This function emits a single global `OpVariable` for a single value from
1935    /// the interface, and adds appropriate decorations to indicate which
1936    /// builtin or location it represents, how it should be interpolated, and so
1937    /// on. The `class` argument gives the variable's SPIR-V storage class,
1938    /// which should be either [`Input`] or [`Output`].
1939    ///
1940    /// [`Binding`]: crate::Binding
1941    /// [`Function`]: crate::Function
1942    /// [`EntryPoint`]: crate::EntryPoint
1943    /// [`Input`]: spirv::StorageClass::Input
1944    /// [`Output`]: spirv::StorageClass::Output
1945    fn write_varying(
1946        &mut self,
1947        ir_module: &crate::Module,
1948        stage: crate::ShaderStage,
1949        class: spirv::StorageClass,
1950        debug_name: Option<&str>,
1951        ty: Handle<crate::Type>,
1952        binding: &crate::Binding,
1953    ) -> Result<Word, Error> {
1954        use crate::TypeInner;
1955
1956        let id = self.id_gen.next();
1957        let ty_inner = &ir_module.types[ty].inner;
1958        let needs_polyfill = self.needs_f16_polyfill(ty_inner);
1959
1960        let pointer_type_id = if needs_polyfill {
1961            let f32_value_local =
1962                super::f16_polyfill::F16IoPolyfill::create_polyfill_type(ty_inner)
1963                    .expect("needs_polyfill returned true but create_polyfill_type returned None");
1964
1965            let f32_type_id = self.get_localtype_id(f32_value_local);
1966            let ptr_id = self.get_pointer_type_id(f32_type_id, class);
1967            self.io_f16_polyfills.register_io_var(id, f32_type_id);
1968
1969            ptr_id
1970        } else {
1971            self.get_handle_pointer_type_id(ty, class)
1972        };
1973
1974        Instruction::variable(pointer_type_id, id, class, None)
1975            .to_words(&mut self.logical_layout.declarations);
1976
1977        if self
1978            .flags
1979            .contains(WriterFlags::DEBUG | WriterFlags::LABEL_VARYINGS)
1980        {
1981            if let Some(name) = debug_name {
1982                self.debugs.push(Instruction::name(id, name));
1983            }
1984        }
1985
1986        use spirv::{BuiltIn, Decoration};
1987
1988        match *binding {
1989            crate::Binding::Location {
1990                location,
1991                interpolation,
1992                sampling,
1993                blend_src,
1994            } => {
1995                self.decorate(id, Decoration::Location, &[location]);
1996
1997                let no_decorations =
1998                    // VUID-StandaloneSpirv-Flat-06202
1999                    // > The Flat, NoPerspective, Sample, and Centroid decorations
2000                    // > must not be used on variables with the Input storage class in a vertex shader
2001                    (class == spirv::StorageClass::Input && stage == crate::ShaderStage::Vertex) ||
2002                    // VUID-StandaloneSpirv-Flat-06201
2003                    // > The Flat, NoPerspective, Sample, and Centroid decorations
2004                    // > must not be used on variables with the Output storage class in a fragment shader
2005                    (class == spirv::StorageClass::Output && stage == crate::ShaderStage::Fragment);
2006
2007                if !no_decorations {
2008                    match interpolation {
2009                        // Perspective-correct interpolation is the default in SPIR-V.
2010                        None | Some(crate::Interpolation::Perspective) => (),
2011                        Some(crate::Interpolation::Flat) => {
2012                            self.decorate(id, Decoration::Flat, &[]);
2013                        }
2014                        Some(crate::Interpolation::Linear) => {
2015                            self.decorate(id, Decoration::NoPerspective, &[]);
2016                        }
2017                    }
2018                    match sampling {
2019                        // Center sampling is the default in SPIR-V.
2020                        None
2021                        | Some(
2022                            crate::Sampling::Center
2023                            | crate::Sampling::First
2024                            | crate::Sampling::Either,
2025                        ) => (),
2026                        Some(crate::Sampling::Centroid) => {
2027                            self.decorate(id, Decoration::Centroid, &[]);
2028                        }
2029                        Some(crate::Sampling::Sample) => {
2030                            self.require_any(
2031                                "per-sample interpolation",
2032                                &[spirv::Capability::SampleRateShading],
2033                            )?;
2034                            self.decorate(id, Decoration::Sample, &[]);
2035                        }
2036                    }
2037                }
2038                if let Some(blend_src) = blend_src {
2039                    self.decorate(id, Decoration::Index, &[blend_src]);
2040                }
2041            }
2042            crate::Binding::BuiltIn(built_in) => {
2043                use crate::BuiltIn as Bi;
2044                let built_in = match built_in {
2045                    Bi::Position { invariant } => {
2046                        if invariant {
2047                            self.decorate(id, Decoration::Invariant, &[]);
2048                        }
2049
2050                        if class == spirv::StorageClass::Output {
2051                            BuiltIn::Position
2052                        } else {
2053                            BuiltIn::FragCoord
2054                        }
2055                    }
2056                    Bi::ViewIndex => {
2057                        self.require_any("`view_index` built-in", &[spirv::Capability::MultiView])?;
2058                        BuiltIn::ViewIndex
2059                    }
2060                    // vertex
2061                    Bi::BaseInstance => BuiltIn::BaseInstance,
2062                    Bi::BaseVertex => BuiltIn::BaseVertex,
2063                    Bi::ClipDistance => {
2064                        self.require_any(
2065                            "`clip_distance` built-in",
2066                            &[spirv::Capability::ClipDistance],
2067                        )?;
2068                        BuiltIn::ClipDistance
2069                    }
2070                    Bi::CullDistance => {
2071                        self.require_any(
2072                            "`cull_distance` built-in",
2073                            &[spirv::Capability::CullDistance],
2074                        )?;
2075                        BuiltIn::CullDistance
2076                    }
2077                    Bi::InstanceIndex => BuiltIn::InstanceIndex,
2078                    Bi::PointSize => BuiltIn::PointSize,
2079                    Bi::VertexIndex => BuiltIn::VertexIndex,
2080                    Bi::DrawID => BuiltIn::DrawIndex,
2081                    // fragment
2082                    Bi::FragDepth => BuiltIn::FragDepth,
2083                    Bi::PointCoord => BuiltIn::PointCoord,
2084                    Bi::FrontFacing => BuiltIn::FrontFacing,
2085                    Bi::PrimitiveIndex => {
2086                        self.require_any(
2087                            "`primitive_index` built-in",
2088                            &[spirv::Capability::Geometry],
2089                        )?;
2090                        BuiltIn::PrimitiveId
2091                    }
2092                    Bi::SampleIndex => {
2093                        self.require_any(
2094                            "`sample_index` built-in",
2095                            &[spirv::Capability::SampleRateShading],
2096                        )?;
2097
2098                        BuiltIn::SampleId
2099                    }
2100                    Bi::SampleMask => BuiltIn::SampleMask,
2101                    // compute
2102                    Bi::GlobalInvocationId => BuiltIn::GlobalInvocationId,
2103                    Bi::LocalInvocationId => BuiltIn::LocalInvocationId,
2104                    Bi::LocalInvocationIndex => BuiltIn::LocalInvocationIndex,
2105                    Bi::WorkGroupId => BuiltIn::WorkgroupId,
2106                    Bi::WorkGroupSize => BuiltIn::WorkgroupSize,
2107                    Bi::NumWorkGroups => BuiltIn::NumWorkgroups,
2108                    // Subgroup
2109                    Bi::NumSubgroups => {
2110                        self.require_any(
2111                            "`num_subgroups` built-in",
2112                            &[spirv::Capability::GroupNonUniform],
2113                        )?;
2114                        BuiltIn::NumSubgroups
2115                    }
2116                    Bi::SubgroupId => {
2117                        self.require_any(
2118                            "`subgroup_id` built-in",
2119                            &[spirv::Capability::GroupNonUniform],
2120                        )?;
2121                        BuiltIn::SubgroupId
2122                    }
2123                    Bi::SubgroupSize => {
2124                        self.require_any(
2125                            "`subgroup_size` built-in",
2126                            &[
2127                                spirv::Capability::GroupNonUniform,
2128                                spirv::Capability::SubgroupBallotKHR,
2129                            ],
2130                        )?;
2131                        BuiltIn::SubgroupSize
2132                    }
2133                    Bi::SubgroupInvocationId => {
2134                        self.require_any(
2135                            "`subgroup_invocation_id` built-in",
2136                            &[
2137                                spirv::Capability::GroupNonUniform,
2138                                spirv::Capability::SubgroupBallotKHR,
2139                            ],
2140                        )?;
2141                        BuiltIn::SubgroupLocalInvocationId
2142                    }
2143                };
2144
2145                self.decorate(id, Decoration::BuiltIn, &[built_in as u32]);
2146
2147                use crate::ScalarKind as Sk;
2148
2149                // Per the Vulkan spec, `VUID-StandaloneSpirv-Flat-04744`:
2150                //
2151                // > Any variable with integer or double-precision floating-
2152                // > point type and with Input storage class in a fragment
2153                // > shader, must be decorated Flat
2154                if class == spirv::StorageClass::Input && stage == crate::ShaderStage::Fragment {
2155                    let is_flat = match ir_module.types[ty].inner {
2156                        TypeInner::Scalar(scalar) | TypeInner::Vector { scalar, .. } => match scalar
2157                            .kind
2158                        {
2159                            Sk::Uint | Sk::Sint | Sk::Bool => true,
2160                            Sk::Float => false,
2161                            Sk::AbstractInt | Sk::AbstractFloat => {
2162                                return Err(Error::Validation(
2163                                    "Abstract types should not appear in IR presented to backends",
2164                                ))
2165                            }
2166                        },
2167                        _ => false,
2168                    };
2169
2170                    if is_flat {
2171                        self.decorate(id, Decoration::Flat, &[]);
2172                    }
2173                }
2174            }
2175        }
2176
2177        Ok(id)
2178    }
2179
2180    /// Load an IO variable, converting from `f32` to `f16` if polyfill is active.
2181    /// Returns the id of the loaded value matching `target_type_id`.
2182    pub(super) fn load_io_with_f16_polyfill(
2183        &mut self,
2184        body: &mut Vec<Instruction>,
2185        varying_id: Word,
2186        target_type_id: Word,
2187    ) -> Word {
2188        let tmp = self.id_gen.next();
2189        if let Some(f32_ty) = self.io_f16_polyfills.get_f32_io_type(varying_id) {
2190            body.push(Instruction::load(f32_ty, tmp, varying_id, None));
2191            let converted = self.id_gen.next();
2192            super::f16_polyfill::F16IoPolyfill::emit_f32_to_f16_conversion(
2193                tmp,
2194                target_type_id,
2195                converted,
2196                body,
2197            );
2198            converted
2199        } else {
2200            body.push(Instruction::load(target_type_id, tmp, varying_id, None));
2201            tmp
2202        }
2203    }
2204
2205    /// Store an IO variable, converting from `f16` to `f32` if polyfill is active.
2206    pub(super) fn store_io_with_f16_polyfill(
2207        &mut self,
2208        body: &mut Vec<Instruction>,
2209        varying_id: Word,
2210        value_id: Word,
2211    ) {
2212        if let Some(f32_ty) = self.io_f16_polyfills.get_f32_io_type(varying_id) {
2213            let converted = self.id_gen.next();
2214            super::f16_polyfill::F16IoPolyfill::emit_f16_to_f32_conversion(
2215                value_id, f32_ty, converted, body,
2216            );
2217            body.push(Instruction::store(varying_id, converted, None));
2218        } else {
2219            body.push(Instruction::store(varying_id, value_id, None));
2220        }
2221    }
2222
2223    fn write_global_variable(
2224        &mut self,
2225        ir_module: &crate::Module,
2226        global_variable: &crate::GlobalVariable,
2227    ) -> Result<Word, Error> {
2228        use spirv::Decoration;
2229
2230        let id = self.id_gen.next();
2231        let class = map_storage_class(global_variable.space);
2232
2233        //self.check(class.required_capabilities())?;
2234
2235        if self.flags.contains(WriterFlags::DEBUG) {
2236            if let Some(ref name) = global_variable.name {
2237                self.debugs.push(Instruction::name(id, name));
2238            }
2239        }
2240
2241        let storage_access = match global_variable.space {
2242            crate::AddressSpace::Storage { access } => Some(access),
2243            _ => match ir_module.types[global_variable.ty].inner {
2244                crate::TypeInner::Image {
2245                    class: crate::ImageClass::Storage { access, .. },
2246                    ..
2247                } => Some(access),
2248                _ => None,
2249            },
2250        };
2251        if let Some(storage_access) = storage_access {
2252            if !storage_access.contains(crate::StorageAccess::LOAD) {
2253                self.decorate(id, Decoration::NonReadable, &[]);
2254            }
2255            if !storage_access.contains(crate::StorageAccess::STORE) {
2256                self.decorate(id, Decoration::NonWritable, &[]);
2257            }
2258        }
2259
2260        // Note: we should be able to substitute `binding_array<Foo, 0>`,
2261        // but there is still code that tries to register the pre-substituted type,
2262        // and it is failing on 0.
2263        let mut substitute_inner_type_lookup = None;
2264        if let Some(ref res_binding) = global_variable.binding {
2265            let bind_target = self.resolve_resource_binding(res_binding)?;
2266            self.decorate(id, Decoration::DescriptorSet, &[bind_target.descriptor_set]);
2267            self.decorate(id, Decoration::Binding, &[bind_target.binding]);
2268
2269            if let Some(remapped_binding_array_size) = bind_target.binding_array_size {
2270                if let crate::TypeInner::BindingArray { base, .. } =
2271                    ir_module.types[global_variable.ty].inner
2272                {
2273                    let binding_array_type_id =
2274                        self.get_type_id(LookupType::Local(LocalType::BindingArray {
2275                            base,
2276                            size: remapped_binding_array_size,
2277                        }));
2278                    substitute_inner_type_lookup = Some(LookupType::Local(LocalType::Pointer {
2279                        base: binding_array_type_id,
2280                        class,
2281                    }));
2282                }
2283            }
2284        };
2285
2286        let init_word = global_variable
2287            .init
2288            .map(|constant| self.constant_ids[constant]);
2289        let inner_type_id = self.get_type_id(
2290            substitute_inner_type_lookup.unwrap_or(LookupType::Handle(global_variable.ty)),
2291        );
2292
2293        // generate the wrapping structure if needed
2294        let pointer_type_id = if global_needs_wrapper(ir_module, global_variable) {
2295            let wrapper_type_id = self.id_gen.next();
2296
2297            self.decorate(wrapper_type_id, Decoration::Block, &[]);
2298            let member = crate::StructMember {
2299                name: None,
2300                ty: global_variable.ty,
2301                binding: None,
2302                offset: 0,
2303            };
2304            self.decorate_struct_member(wrapper_type_id, 0, &member, &ir_module.types)?;
2305
2306            Instruction::type_struct(wrapper_type_id, &[inner_type_id])
2307                .to_words(&mut self.logical_layout.declarations);
2308
2309            let pointer_type_id = self.id_gen.next();
2310            Instruction::type_pointer(pointer_type_id, class, wrapper_type_id)
2311                .to_words(&mut self.logical_layout.declarations);
2312
2313            pointer_type_id
2314        } else {
2315            // This is a global variable in the Storage address space. The only
2316            // way it could have `global_needs_wrapper() == false` is if it has
2317            // a runtime-sized or binding array.
2318            // Runtime-sized arrays were decorated when iterating through struct content.
2319            // Now binding arrays require Block decorating.
2320            if let crate::AddressSpace::Storage { .. } = global_variable.space {
2321                match ir_module.types[global_variable.ty].inner {
2322                    crate::TypeInner::BindingArray { base, .. } => {
2323                        let ty = &ir_module.types[base];
2324                        let mut should_decorate = true;
2325                        // Check if the type has a runtime array.
2326                        // A normal runtime array gets validated out,
2327                        // so only structs can be with runtime arrays
2328                        if let crate::TypeInner::Struct { ref members, .. } = ty.inner {
2329                            // only the last member in a struct can be dynamically sized
2330                            if let Some(last_member) = members.last() {
2331                                if let &crate::TypeInner::Array {
2332                                    size: crate::ArraySize::Dynamic,
2333                                    ..
2334                                } = &ir_module.types[last_member.ty].inner
2335                                {
2336                                    should_decorate = false;
2337                                }
2338                            }
2339                        }
2340                        if should_decorate {
2341                            let decorated_id = self.get_handle_type_id(base);
2342                            self.decorate(decorated_id, Decoration::Block, &[]);
2343                        }
2344                    }
2345                    _ => (),
2346                };
2347            }
2348            if substitute_inner_type_lookup.is_some() {
2349                inner_type_id
2350            } else {
2351                self.get_handle_pointer_type_id(global_variable.ty, class)
2352            }
2353        };
2354
2355        let init_word = match (global_variable.space, self.zero_initialize_workgroup_memory) {
2356            (crate::AddressSpace::Private, _)
2357            | (crate::AddressSpace::WorkGroup, super::ZeroInitializeWorkgroupMemoryMode::Native) => {
2358                init_word.or_else(|| Some(self.get_constant_null(inner_type_id)))
2359            }
2360            _ => init_word,
2361        };
2362
2363        Instruction::variable(pointer_type_id, id, class, init_word)
2364            .to_words(&mut self.logical_layout.declarations);
2365        Ok(id)
2366    }
2367
2368    /// Write the necessary decorations for a struct member.
2369    ///
2370    /// Emit decorations for the `index`'th member of the struct type
2371    /// designated by `struct_id`, described by `member`.
2372    fn decorate_struct_member(
2373        &mut self,
2374        struct_id: Word,
2375        index: usize,
2376        member: &crate::StructMember,
2377        arena: &UniqueArena<crate::Type>,
2378    ) -> Result<(), Error> {
2379        use spirv::Decoration;
2380
2381        self.annotations.push(Instruction::member_decorate(
2382            struct_id,
2383            index as u32,
2384            Decoration::Offset,
2385            &[member.offset],
2386        ));
2387
2388        if self.flags.contains(WriterFlags::DEBUG) {
2389            if let Some(ref name) = member.name {
2390                self.debugs
2391                    .push(Instruction::member_name(struct_id, index as u32, name));
2392            }
2393        }
2394
2395        // Matrices and (potentially nested) arrays of matrices both require decorations,
2396        // so "see through" any arrays to determine if they're needed.
2397        let mut member_array_subty_inner = &arena[member.ty].inner;
2398        while let crate::TypeInner::Array { base, .. } = *member_array_subty_inner {
2399            member_array_subty_inner = &arena[base].inner;
2400        }
2401
2402        if let crate::TypeInner::Matrix {
2403            columns: _,
2404            rows,
2405            scalar,
2406        } = *member_array_subty_inner
2407        {
2408            let byte_stride = Alignment::from(rows) * scalar.width as u32;
2409            self.annotations.push(Instruction::member_decorate(
2410                struct_id,
2411                index as u32,
2412                Decoration::ColMajor,
2413                &[],
2414            ));
2415            self.annotations.push(Instruction::member_decorate(
2416                struct_id,
2417                index as u32,
2418                Decoration::MatrixStride,
2419                &[byte_stride],
2420            ));
2421        }
2422
2423        Ok(())
2424    }
2425
2426    pub(super) fn get_function_type(&mut self, lookup_function_type: LookupFunctionType) -> Word {
2427        match self
2428            .lookup_function_type
2429            .entry(lookup_function_type.clone())
2430        {
2431            Entry::Occupied(e) => *e.get(),
2432            Entry::Vacant(_) => {
2433                let id = self.id_gen.next();
2434                let instruction = Instruction::type_function(
2435                    id,
2436                    lookup_function_type.return_type_id,
2437                    &lookup_function_type.parameter_type_ids,
2438                );
2439                instruction.to_words(&mut self.logical_layout.declarations);
2440                self.lookup_function_type.insert(lookup_function_type, id);
2441                id
2442            }
2443        }
2444    }
2445
2446    fn write_physical_layout(&mut self) {
2447        self.physical_layout.bound = self.id_gen.0 + 1;
2448    }
2449
2450    fn write_logical_layout(
2451        &mut self,
2452        ir_module: &crate::Module,
2453        mod_info: &ModuleInfo,
2454        ep_index: Option<usize>,
2455        debug_info: &Option<DebugInfo>,
2456    ) -> Result<(), Error> {
2457        fn has_view_index_check(
2458            ir_module: &crate::Module,
2459            binding: Option<&crate::Binding>,
2460            ty: Handle<crate::Type>,
2461        ) -> bool {
2462            match ir_module.types[ty].inner {
2463                crate::TypeInner::Struct { ref members, .. } => members.iter().any(|member| {
2464                    has_view_index_check(ir_module, member.binding.as_ref(), member.ty)
2465                }),
2466                _ => binding == Some(&crate::Binding::BuiltIn(crate::BuiltIn::ViewIndex)),
2467            }
2468        }
2469
2470        let has_storage_buffers =
2471            ir_module
2472                .global_variables
2473                .iter()
2474                .any(|(_, var)| match var.space {
2475                    crate::AddressSpace::Storage { .. } => true,
2476                    _ => false,
2477                });
2478        let has_view_index = ir_module
2479            .entry_points
2480            .iter()
2481            .flat_map(|entry| entry.function.arguments.iter())
2482            .any(|arg| has_view_index_check(ir_module, arg.binding.as_ref(), arg.ty));
2483        let mut has_ray_query = ir_module.special_types.ray_desc.is_some()
2484            | ir_module.special_types.ray_intersection.is_some();
2485        let has_vertex_return = ir_module.special_types.ray_vertex_return.is_some();
2486
2487        for (_, &crate::Type { ref inner, .. }) in ir_module.types.iter() {
2488            // spirv does not know whether these have vertex return - that is done by us
2489            if let &crate::TypeInner::AccelerationStructure { .. }
2490            | &crate::TypeInner::RayQuery { .. } = inner
2491            {
2492                has_ray_query = true
2493            }
2494        }
2495
2496        if self.physical_layout.version < 0x10300 && has_storage_buffers {
2497            // enable the storage buffer class on < SPV-1.3
2498            Instruction::extension("SPV_KHR_storage_buffer_storage_class")
2499                .to_words(&mut self.logical_layout.extensions);
2500        }
2501        if has_view_index {
2502            Instruction::extension("SPV_KHR_multiview")
2503                .to_words(&mut self.logical_layout.extensions)
2504        }
2505        if has_ray_query {
2506            Instruction::extension("SPV_KHR_ray_query")
2507                .to_words(&mut self.logical_layout.extensions)
2508        }
2509        if has_vertex_return {
2510            Instruction::extension("SPV_KHR_ray_tracing_position_fetch")
2511                .to_words(&mut self.logical_layout.extensions);
2512        }
2513        Instruction::type_void(self.void_type).to_words(&mut self.logical_layout.declarations);
2514        Instruction::ext_inst_import(self.gl450_ext_inst_id, "GLSL.std.450")
2515            .to_words(&mut self.logical_layout.ext_inst_imports);
2516
2517        let mut debug_info_inner = None;
2518        if self.flags.contains(WriterFlags::DEBUG) {
2519            if let Some(debug_info) = debug_info.as_ref() {
2520                let source_file_id = self.id_gen.next();
2521                self.debugs
2522                    .push(Instruction::string(debug_info.file_name, source_file_id));
2523
2524                debug_info_inner = Some(DebugInfoInner {
2525                    source_code: debug_info.source_code,
2526                    source_file_id,
2527                });
2528                self.debugs.append(&mut Instruction::source_auto_continued(
2529                    debug_info.language,
2530                    0,
2531                    &debug_info_inner,
2532                ));
2533            }
2534        }
2535
2536        // write all types
2537        for (handle, _) in ir_module.types.iter() {
2538            self.write_type_declaration_arena(ir_module, handle)?;
2539        }
2540
2541        // write all const-expressions as constants
2542        self.constant_ids
2543            .resize(ir_module.global_expressions.len(), 0);
2544        for (handle, _) in ir_module.global_expressions.iter() {
2545            self.write_constant_expr(handle, ir_module, mod_info)?;
2546        }
2547        debug_assert!(self.constant_ids.iter().all(|&id| id != 0));
2548
2549        // write the name of constants on their respective const-expression initializer
2550        if self.flags.contains(WriterFlags::DEBUG) {
2551            for (_, constant) in ir_module.constants.iter() {
2552                if let Some(ref name) = constant.name {
2553                    let id = self.constant_ids[constant.init];
2554                    self.debugs.push(Instruction::name(id, name));
2555                }
2556            }
2557        }
2558
2559        // write all global variables
2560        for (handle, var) in ir_module.global_variables.iter() {
2561            // If a single entry point was specified, only write `OpVariable` instructions
2562            // for the globals it actually uses. Emit dummies for the others,
2563            // to preserve the indices in `global_variables`.
2564            let gvar = match ep_index {
2565                Some(index) if mod_info.get_entry_point(index)[handle].is_empty() => {
2566                    GlobalVariable::dummy()
2567                }
2568                _ => {
2569                    let id = self.write_global_variable(ir_module, var)?;
2570                    GlobalVariable::new(id)
2571                }
2572            };
2573            self.global_variables.insert(handle, gvar);
2574        }
2575
2576        // write all functions
2577        for (handle, ir_function) in ir_module.functions.iter() {
2578            let info = &mod_info[handle];
2579            if let Some(index) = ep_index {
2580                let ep_info = mod_info.get_entry_point(index);
2581                // If this function uses globals that we omitted from the SPIR-V
2582                // because the entry point and its callees didn't use them,
2583                // then we must skip it.
2584                if !ep_info.dominates_global_use(info) {
2585                    log::info!("Skip function {:?}", ir_function.name);
2586                    continue;
2587                }
2588
2589                // Skip functions that that are not compatible with this entry point's stage.
2590                //
2591                // When validation is enabled, it rejects modules whose entry points try to call
2592                // incompatible functions, so if we got this far, then any functions incompatible
2593                // with our selected entry point must not be used.
2594                //
2595                // When validation is disabled, `fun_info.available_stages` is always just
2596                // `ShaderStages::all()`, so this will write all functions in the module, and
2597                // the downstream GLSL compiler will catch any problems.
2598                if !info.available_stages.contains(ep_info.available_stages) {
2599                    continue;
2600                }
2601            }
2602            let id = self.write_function(ir_function, info, ir_module, None, &debug_info_inner)?;
2603            self.lookup_function.insert(handle, id);
2604        }
2605
2606        // write all or one entry points
2607        for (index, ir_ep) in ir_module.entry_points.iter().enumerate() {
2608            if ep_index.is_some() && ep_index != Some(index) {
2609                continue;
2610            }
2611            let info = mod_info.get_entry_point(index);
2612            let ep_instruction =
2613                self.write_entry_point(ir_ep, info, ir_module, &debug_info_inner)?;
2614            ep_instruction.to_words(&mut self.logical_layout.entry_points);
2615        }
2616
2617        for capability in self.capabilities_used.iter() {
2618            Instruction::capability(*capability).to_words(&mut self.logical_layout.capabilities);
2619        }
2620        for extension in self.extensions_used.iter() {
2621            Instruction::extension(extension).to_words(&mut self.logical_layout.extensions);
2622        }
2623        if ir_module.entry_points.is_empty() {
2624            // SPIR-V doesn't like modules without entry points
2625            Instruction::capability(spirv::Capability::Linkage)
2626                .to_words(&mut self.logical_layout.capabilities);
2627        }
2628
2629        let addressing_model = spirv::AddressingModel::Logical;
2630        let memory_model = spirv::MemoryModel::GLSL450;
2631        //self.check(addressing_model.required_capabilities())?;
2632        //self.check(memory_model.required_capabilities())?;
2633
2634        Instruction::memory_model(addressing_model, memory_model)
2635            .to_words(&mut self.logical_layout.memory_model);
2636
2637        if self.flags.contains(WriterFlags::DEBUG) {
2638            for debug in self.debugs.iter() {
2639                debug.to_words(&mut self.logical_layout.debugs);
2640            }
2641        }
2642
2643        for annotation in self.annotations.iter() {
2644            annotation.to_words(&mut self.logical_layout.annotations);
2645        }
2646
2647        Ok(())
2648    }
2649
2650    pub fn write(
2651        &mut self,
2652        ir_module: &crate::Module,
2653        info: &ModuleInfo,
2654        pipeline_options: Option<&PipelineOptions>,
2655        debug_info: &Option<DebugInfo>,
2656        words: &mut Vec<Word>,
2657    ) -> Result<(), Error> {
2658        self.reset();
2659
2660        // Try to find the entry point and corresponding index
2661        let ep_index = match pipeline_options {
2662            Some(po) => {
2663                let index = ir_module
2664                    .entry_points
2665                    .iter()
2666                    .position(|ep| po.shader_stage == ep.stage && po.entry_point == ep.name)
2667                    .ok_or(Error::EntryPointNotFound)?;
2668                Some(index)
2669            }
2670            None => None,
2671        };
2672
2673        self.write_logical_layout(ir_module, info, ep_index, debug_info)?;
2674        self.write_physical_layout();
2675
2676        self.physical_layout.in_words(words);
2677        self.logical_layout.in_words(words);
2678        Ok(())
2679    }
2680
2681    /// Return the set of capabilities the last module written used.
2682    pub const fn get_capabilities_used(&self) -> &crate::FastIndexSet<spirv::Capability> {
2683        &self.capabilities_used
2684    }
2685
2686    pub fn decorate_non_uniform_binding_array_access(&mut self, id: Word) -> Result<(), Error> {
2687        self.require_any("NonUniformEXT", &[spirv::Capability::ShaderNonUniform])?;
2688        self.use_extension("SPV_EXT_descriptor_indexing");
2689        self.decorate(id, spirv::Decoration::NonUniform, &[]);
2690        Ok(())
2691    }
2692
2693    pub(super) fn needs_f16_polyfill(&self, ty_inner: &crate::TypeInner) -> bool {
2694        self.io_f16_polyfills.needs_polyfill(ty_inner)
2695    }
2696}
2697
2698#[test]
2699fn test_write_physical_layout() {
2700    let mut writer = Writer::new(&Options::default()).unwrap();
2701    assert_eq!(writer.physical_layout.bound, 0);
2702    writer.write_physical_layout();
2703    assert_eq!(writer.physical_layout.bound, 3);
2704}