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:
1098                                crate::ShaderStage::Compute
1099                                | crate::ShaderStage::Mesh
1100                                | crate::ShaderStage::Task,
1101                            ..
1102                        },
1103                    ),
1104                ) => context.writer.generate_workgroup_vars_init_block(
1105                    next_id,
1106                    ir_module,
1107                    info,
1108                    local_invocation_id,
1109                    interface,
1110                    context.function,
1111                ),
1112                _ => None,
1113            };
1114
1115        let main_id = if let Some(exit_id) = workgroup_vars_init_exit_block_id {
1116            exit_id
1117        } else {
1118            next_id
1119        };
1120
1121        context.write_function_body(main_id, debug_info.as_ref())?;
1122
1123        // Consume the `BlockContext`, ending its borrows and letting the
1124        // `Writer` steal back its cached expression table and temp_list.
1125        let BlockContext {
1126            cached, temp_list, ..
1127        } = context;
1128        self.saved_cached = cached;
1129        self.temp_list = temp_list;
1130
1131        function.to_words(&mut self.logical_layout.function_definitions);
1132
1133        Ok(function_id)
1134    }
1135
1136    fn write_execution_mode(
1137        &mut self,
1138        function_id: Word,
1139        mode: spirv::ExecutionMode,
1140    ) -> Result<(), Error> {
1141        //self.check(mode.required_capabilities())?;
1142        Instruction::execution_mode(function_id, mode, &[])
1143            .to_words(&mut self.logical_layout.execution_modes);
1144        Ok(())
1145    }
1146
1147    // TODO Move to instructions module
1148    fn write_entry_point(
1149        &mut self,
1150        entry_point: &crate::EntryPoint,
1151        info: &FunctionInfo,
1152        ir_module: &crate::Module,
1153        debug_info: &Option<DebugInfoInner>,
1154    ) -> Result<Instruction, Error> {
1155        let mut interface_ids = Vec::new();
1156        let function_id = self.write_function(
1157            &entry_point.function,
1158            info,
1159            ir_module,
1160            Some(FunctionInterface {
1161                varying_ids: &mut interface_ids,
1162                stage: entry_point.stage,
1163            }),
1164            debug_info,
1165        )?;
1166
1167        let exec_model = match entry_point.stage {
1168            crate::ShaderStage::Vertex => spirv::ExecutionModel::Vertex,
1169            crate::ShaderStage::Fragment => {
1170                self.write_execution_mode(function_id, spirv::ExecutionMode::OriginUpperLeft)?;
1171                match entry_point.early_depth_test {
1172                    Some(crate::EarlyDepthTest::Force) => {
1173                        self.write_execution_mode(
1174                            function_id,
1175                            spirv::ExecutionMode::EarlyFragmentTests,
1176                        )?;
1177                    }
1178                    Some(crate::EarlyDepthTest::Allow { conservative }) => {
1179                        // TODO: Consider emitting EarlyAndLateFragmentTestsAMD here, if available.
1180                        // https://github.khronos.org/SPIRV-Registry/extensions/AMD/SPV_AMD_shader_early_and_late_fragment_tests.html
1181                        // This permits early depth tests even if the shader writes to a storage
1182                        // binding
1183                        match conservative {
1184                            crate::ConservativeDepth::GreaterEqual => self.write_execution_mode(
1185                                function_id,
1186                                spirv::ExecutionMode::DepthGreater,
1187                            )?,
1188                            crate::ConservativeDepth::LessEqual => self.write_execution_mode(
1189                                function_id,
1190                                spirv::ExecutionMode::DepthLess,
1191                            )?,
1192                            crate::ConservativeDepth::Unchanged => self.write_execution_mode(
1193                                function_id,
1194                                spirv::ExecutionMode::DepthUnchanged,
1195                            )?,
1196                        }
1197                    }
1198                    None => {}
1199                }
1200                if let Some(ref result) = entry_point.function.result {
1201                    if contains_builtin(
1202                        result.binding.as_ref(),
1203                        result.ty,
1204                        &ir_module.types,
1205                        crate::BuiltIn::FragDepth,
1206                    ) {
1207                        self.write_execution_mode(
1208                            function_id,
1209                            spirv::ExecutionMode::DepthReplacing,
1210                        )?;
1211                    }
1212                }
1213                spirv::ExecutionModel::Fragment
1214            }
1215            crate::ShaderStage::Compute => {
1216                let execution_mode = spirv::ExecutionMode::LocalSize;
1217                //self.check(execution_mode.required_capabilities())?;
1218                Instruction::execution_mode(
1219                    function_id,
1220                    execution_mode,
1221                    &entry_point.workgroup_size,
1222                )
1223                .to_words(&mut self.logical_layout.execution_modes);
1224                spirv::ExecutionModel::GLCompute
1225            }
1226            crate::ShaderStage::Task | crate::ShaderStage::Mesh => unreachable!(),
1227        };
1228        //self.check(exec_model.required_capabilities())?;
1229
1230        Ok(Instruction::entry_point(
1231            exec_model,
1232            function_id,
1233            &entry_point.name,
1234            interface_ids.as_slice(),
1235        ))
1236    }
1237
1238    fn make_scalar(&mut self, id: Word, scalar: crate::Scalar) -> Instruction {
1239        use crate::ScalarKind as Sk;
1240
1241        let bits = (scalar.width * BITS_PER_BYTE) as u32;
1242        match scalar.kind {
1243            Sk::Sint | Sk::Uint => {
1244                let signedness = if scalar.kind == Sk::Sint {
1245                    super::instructions::Signedness::Signed
1246                } else {
1247                    super::instructions::Signedness::Unsigned
1248                };
1249                let cap = match bits {
1250                    8 => Some(spirv::Capability::Int8),
1251                    16 => Some(spirv::Capability::Int16),
1252                    64 => Some(spirv::Capability::Int64),
1253                    _ => None,
1254                };
1255                if let Some(cap) = cap {
1256                    self.capabilities_used.insert(cap);
1257                }
1258                Instruction::type_int(id, bits, signedness)
1259            }
1260            Sk::Float => {
1261                if bits == 64 {
1262                    self.capabilities_used.insert(spirv::Capability::Float64);
1263                }
1264                if bits == 16 {
1265                    self.capabilities_used.insert(spirv::Capability::Float16);
1266                    self.capabilities_used
1267                        .insert(spirv::Capability::StorageBuffer16BitAccess);
1268                    self.capabilities_used
1269                        .insert(spirv::Capability::UniformAndStorageBuffer16BitAccess);
1270                    if self.use_storage_input_output_16 {
1271                        self.capabilities_used
1272                            .insert(spirv::Capability::StorageInputOutput16);
1273                    }
1274                }
1275                Instruction::type_float(id, bits)
1276            }
1277            Sk::Bool => Instruction::type_bool(id),
1278            Sk::AbstractInt | Sk::AbstractFloat => {
1279                unreachable!("abstract types should never reach the backend");
1280            }
1281        }
1282    }
1283
1284    fn request_type_capabilities(&mut self, inner: &crate::TypeInner) -> Result<(), Error> {
1285        match *inner {
1286            crate::TypeInner::Image {
1287                dim,
1288                arrayed,
1289                class,
1290            } => {
1291                let sampled = match class {
1292                    crate::ImageClass::Sampled { .. } => true,
1293                    crate::ImageClass::Depth { .. } => true,
1294                    crate::ImageClass::Storage { format, .. } => {
1295                        self.request_image_format_capabilities(format.into())?;
1296                        false
1297                    }
1298                    crate::ImageClass::External => unimplemented!(),
1299                };
1300
1301                match dim {
1302                    crate::ImageDimension::D1 => {
1303                        if sampled {
1304                            self.require_any("sampled 1D images", &[spirv::Capability::Sampled1D])?;
1305                        } else {
1306                            self.require_any("1D storage images", &[spirv::Capability::Image1D])?;
1307                        }
1308                    }
1309                    crate::ImageDimension::Cube if arrayed => {
1310                        if sampled {
1311                            self.require_any(
1312                                "sampled cube array images",
1313                                &[spirv::Capability::SampledCubeArray],
1314                            )?;
1315                        } else {
1316                            self.require_any(
1317                                "cube array storage images",
1318                                &[spirv::Capability::ImageCubeArray],
1319                            )?;
1320                        }
1321                    }
1322                    _ => {}
1323                }
1324            }
1325            crate::TypeInner::AccelerationStructure { .. } => {
1326                self.require_any("Acceleration Structure", &[spirv::Capability::RayQueryKHR])?;
1327            }
1328            crate::TypeInner::RayQuery { .. } => {
1329                self.require_any("Ray Query", &[spirv::Capability::RayQueryKHR])?;
1330            }
1331            crate::TypeInner::Atomic(crate::Scalar { width: 8, kind: _ }) => {
1332                self.require_any("64 bit integer atomics", &[spirv::Capability::Int64Atomics])?;
1333            }
1334            crate::TypeInner::Atomic(crate::Scalar {
1335                width: 4,
1336                kind: crate::ScalarKind::Float,
1337            }) => {
1338                self.require_any(
1339                    "32 bit floating-point atomics",
1340                    &[spirv::Capability::AtomicFloat32AddEXT],
1341                )?;
1342                self.use_extension("SPV_EXT_shader_atomic_float_add");
1343            }
1344            // 16 bit floating-point support requires Float16 capability
1345            crate::TypeInner::Matrix {
1346                scalar: crate::Scalar::F16,
1347                ..
1348            }
1349            | crate::TypeInner::Vector {
1350                scalar: crate::Scalar::F16,
1351                ..
1352            }
1353            | crate::TypeInner::Scalar(crate::Scalar::F16) => {
1354                self.require_any("16 bit floating-point", &[spirv::Capability::Float16])?;
1355                self.use_extension("SPV_KHR_16bit_storage");
1356            }
1357            _ => {}
1358        }
1359        Ok(())
1360    }
1361
1362    fn write_numeric_type_declaration_local(&mut self, id: Word, numeric: NumericType) {
1363        let instruction = match numeric {
1364            NumericType::Scalar(scalar) => self.make_scalar(id, scalar),
1365            NumericType::Vector { size, scalar } => {
1366                let scalar_id = self.get_numeric_type_id(NumericType::Scalar(scalar));
1367                Instruction::type_vector(id, scalar_id, size)
1368            }
1369            NumericType::Matrix {
1370                columns,
1371                rows,
1372                scalar,
1373            } => {
1374                let column_id =
1375                    self.get_numeric_type_id(NumericType::Vector { size: rows, scalar });
1376                Instruction::type_matrix(id, column_id, columns)
1377            }
1378        };
1379
1380        instruction.to_words(&mut self.logical_layout.declarations);
1381    }
1382
1383    fn write_type_declaration_local(&mut self, id: Word, local_ty: LocalType) {
1384        let instruction = match local_ty {
1385            LocalType::Numeric(numeric) => {
1386                self.write_numeric_type_declaration_local(id, numeric);
1387                return;
1388            }
1389            LocalType::Pointer { base, class } => Instruction::type_pointer(id, class, base),
1390            LocalType::Image(image) => {
1391                let local_type = LocalType::Numeric(NumericType::Scalar(image.sampled_type));
1392                let type_id = self.get_localtype_id(local_type);
1393                Instruction::type_image(id, type_id, image.dim, image.flags, image.image_format)
1394            }
1395            LocalType::Sampler => Instruction::type_sampler(id),
1396            LocalType::SampledImage { image_type_id } => {
1397                Instruction::type_sampled_image(id, image_type_id)
1398            }
1399            LocalType::BindingArray { base, size } => {
1400                let inner_ty = self.get_handle_type_id(base);
1401                let scalar_id = self.get_constant_scalar(crate::Literal::U32(size));
1402                Instruction::type_array(id, inner_ty, scalar_id)
1403            }
1404            LocalType::AccelerationStructure => Instruction::type_acceleration_structure(id),
1405            LocalType::RayQuery => Instruction::type_ray_query(id),
1406        };
1407
1408        instruction.to_words(&mut self.logical_layout.declarations);
1409    }
1410
1411    fn write_type_declaration_arena(
1412        &mut self,
1413        module: &crate::Module,
1414        handle: Handle<crate::Type>,
1415    ) -> Result<Word, Error> {
1416        let ty = &module.types[handle];
1417        // If it's a type that needs SPIR-V capabilities, request them now.
1418        // This needs to happen regardless of the LocalType lookup succeeding,
1419        // because some types which map to the same LocalType have different
1420        // capability requirements. See https://github.com/gfx-rs/wgpu/issues/5569
1421        self.request_type_capabilities(&ty.inner)?;
1422        let id = if let Some(local) = self.localtype_from_inner(&ty.inner) {
1423            // This type can be represented as a `LocalType`, so check if we've
1424            // already written an instruction for it. If not, do so now, with
1425            // `write_type_declaration_local`.
1426            match self.lookup_type.entry(LookupType::Local(local)) {
1427                // We already have an id for this `LocalType`.
1428                Entry::Occupied(e) => *e.get(),
1429
1430                // It's a type we haven't seen before.
1431                Entry::Vacant(e) => {
1432                    let id = self.id_gen.next();
1433                    e.insert(id);
1434
1435                    self.write_type_declaration_local(id, local);
1436
1437                    id
1438                }
1439            }
1440        } else {
1441            use spirv::Decoration;
1442
1443            let id = self.id_gen.next();
1444            let instruction = match ty.inner {
1445                crate::TypeInner::Array { base, size, stride } => {
1446                    self.decorate(id, Decoration::ArrayStride, &[stride]);
1447
1448                    let type_id = self.get_handle_type_id(base);
1449                    match size.resolve(module.to_ctx())? {
1450                        crate::proc::IndexableLength::Known(length) => {
1451                            let length_id = self.get_index_constant(length);
1452                            Instruction::type_array(id, type_id, length_id)
1453                        }
1454                        crate::proc::IndexableLength::Dynamic => {
1455                            Instruction::type_runtime_array(id, type_id)
1456                        }
1457                    }
1458                }
1459                crate::TypeInner::BindingArray { base, size } => {
1460                    let type_id = self.get_handle_type_id(base);
1461                    match size.resolve(module.to_ctx())? {
1462                        crate::proc::IndexableLength::Known(length) => {
1463                            let length_id = self.get_index_constant(length);
1464                            Instruction::type_array(id, type_id, length_id)
1465                        }
1466                        crate::proc::IndexableLength::Dynamic => {
1467                            Instruction::type_runtime_array(id, type_id)
1468                        }
1469                    }
1470                }
1471                crate::TypeInner::Struct {
1472                    ref members,
1473                    span: _,
1474                } => {
1475                    let mut has_runtime_array = false;
1476                    let mut member_ids = Vec::with_capacity(members.len());
1477                    for (index, member) in members.iter().enumerate() {
1478                        let member_ty = &module.types[member.ty];
1479                        match member_ty.inner {
1480                            crate::TypeInner::Array {
1481                                base: _,
1482                                size: crate::ArraySize::Dynamic,
1483                                stride: _,
1484                            } => {
1485                                has_runtime_array = true;
1486                            }
1487                            _ => (),
1488                        }
1489                        self.decorate_struct_member(id, index, member, &module.types)?;
1490                        let member_id = self.get_handle_type_id(member.ty);
1491                        member_ids.push(member_id);
1492                    }
1493                    if has_runtime_array {
1494                        self.decorate(id, Decoration::Block, &[]);
1495                    }
1496                    Instruction::type_struct(id, member_ids.as_slice())
1497                }
1498
1499                // These all have TypeLocal representations, so they should have been
1500                // handled by `write_type_declaration_local` above.
1501                crate::TypeInner::Scalar(_)
1502                | crate::TypeInner::Atomic(_)
1503                | crate::TypeInner::Vector { .. }
1504                | crate::TypeInner::Matrix { .. }
1505                | crate::TypeInner::Pointer { .. }
1506                | crate::TypeInner::ValuePointer { .. }
1507                | crate::TypeInner::Image { .. }
1508                | crate::TypeInner::Sampler { .. }
1509                | crate::TypeInner::AccelerationStructure { .. }
1510                | crate::TypeInner::RayQuery { .. } => unreachable!(),
1511            };
1512
1513            instruction.to_words(&mut self.logical_layout.declarations);
1514            id
1515        };
1516
1517        // Add this handle as a new alias for that type.
1518        self.lookup_type.insert(LookupType::Handle(handle), id);
1519
1520        if self.flags.contains(WriterFlags::DEBUG) {
1521            if let Some(ref name) = ty.name {
1522                self.debugs.push(Instruction::name(id, name));
1523            }
1524        }
1525
1526        Ok(id)
1527    }
1528
1529    fn request_image_format_capabilities(
1530        &mut self,
1531        format: spirv::ImageFormat,
1532    ) -> Result<(), Error> {
1533        use spirv::ImageFormat as If;
1534        match format {
1535            If::Rg32f
1536            | If::Rg16f
1537            | If::R11fG11fB10f
1538            | If::R16f
1539            | If::Rgba16
1540            | If::Rgb10A2
1541            | If::Rg16
1542            | If::Rg8
1543            | If::R16
1544            | If::R8
1545            | If::Rgba16Snorm
1546            | If::Rg16Snorm
1547            | If::Rg8Snorm
1548            | If::R16Snorm
1549            | If::R8Snorm
1550            | If::Rg32i
1551            | If::Rg16i
1552            | If::Rg8i
1553            | If::R16i
1554            | If::R8i
1555            | If::Rgb10a2ui
1556            | If::Rg32ui
1557            | If::Rg16ui
1558            | If::Rg8ui
1559            | If::R16ui
1560            | If::R8ui => self.require_any(
1561                "storage image format",
1562                &[spirv::Capability::StorageImageExtendedFormats],
1563            ),
1564            If::R64ui | If::R64i => {
1565                self.use_extension("SPV_EXT_shader_image_int64");
1566                self.require_any(
1567                    "64-bit integer storage image format",
1568                    &[spirv::Capability::Int64ImageEXT],
1569                )
1570            }
1571            If::Unknown
1572            | If::Rgba32f
1573            | If::Rgba16f
1574            | If::R32f
1575            | If::Rgba8
1576            | If::Rgba8Snorm
1577            | If::Rgba32i
1578            | If::Rgba16i
1579            | If::Rgba8i
1580            | If::R32i
1581            | If::Rgba32ui
1582            | If::Rgba16ui
1583            | If::Rgba8ui
1584            | If::R32ui => Ok(()),
1585        }
1586    }
1587
1588    pub(super) fn get_index_constant(&mut self, index: Word) -> Word {
1589        self.get_constant_scalar(crate::Literal::U32(index))
1590    }
1591
1592    pub(super) fn get_constant_scalar_with(
1593        &mut self,
1594        value: u8,
1595        scalar: crate::Scalar,
1596    ) -> Result<Word, Error> {
1597        Ok(
1598            self.get_constant_scalar(crate::Literal::new(value, scalar).ok_or(
1599                Error::Validation("Unexpected kind and/or width for Literal"),
1600            )?),
1601        )
1602    }
1603
1604    pub(super) fn get_constant_scalar(&mut self, value: crate::Literal) -> Word {
1605        let scalar = CachedConstant::Literal(value.into());
1606        if let Some(&id) = self.cached_constants.get(&scalar) {
1607            return id;
1608        }
1609        let id = self.id_gen.next();
1610        self.write_constant_scalar(id, &value, None);
1611        self.cached_constants.insert(scalar, id);
1612        id
1613    }
1614
1615    fn write_constant_scalar(
1616        &mut self,
1617        id: Word,
1618        value: &crate::Literal,
1619        debug_name: Option<&String>,
1620    ) {
1621        if self.flags.contains(WriterFlags::DEBUG) {
1622            if let Some(name) = debug_name {
1623                self.debugs.push(Instruction::name(id, name));
1624            }
1625        }
1626        let type_id = self.get_numeric_type_id(NumericType::Scalar(value.scalar()));
1627        let instruction = match *value {
1628            crate::Literal::F64(value) => {
1629                let bits = value.to_bits();
1630                Instruction::constant_64bit(type_id, id, bits as u32, (bits >> 32) as u32)
1631            }
1632            crate::Literal::F32(value) => Instruction::constant_32bit(type_id, id, value.to_bits()),
1633            crate::Literal::F16(value) => {
1634                let low = value.to_bits();
1635                Instruction::constant_16bit(type_id, id, low as u32)
1636            }
1637            crate::Literal::U32(value) => Instruction::constant_32bit(type_id, id, value),
1638            crate::Literal::I32(value) => Instruction::constant_32bit(type_id, id, value as u32),
1639            crate::Literal::U64(value) => {
1640                Instruction::constant_64bit(type_id, id, value as u32, (value >> 32) as u32)
1641            }
1642            crate::Literal::I64(value) => {
1643                Instruction::constant_64bit(type_id, id, value as u32, (value >> 32) as u32)
1644            }
1645            crate::Literal::Bool(true) => Instruction::constant_true(type_id, id),
1646            crate::Literal::Bool(false) => Instruction::constant_false(type_id, id),
1647            crate::Literal::AbstractInt(_) | crate::Literal::AbstractFloat(_) => {
1648                unreachable!("Abstract types should not appear in IR presented to backends");
1649            }
1650        };
1651
1652        instruction.to_words(&mut self.logical_layout.declarations);
1653    }
1654
1655    pub(super) fn get_constant_composite(
1656        &mut self,
1657        ty: LookupType,
1658        constituent_ids: &[Word],
1659    ) -> Word {
1660        let composite = CachedConstant::Composite {
1661            ty,
1662            constituent_ids: constituent_ids.to_vec(),
1663        };
1664        if let Some(&id) = self.cached_constants.get(&composite) {
1665            return id;
1666        }
1667        let id = self.id_gen.next();
1668        self.write_constant_composite(id, ty, constituent_ids, None);
1669        self.cached_constants.insert(composite, id);
1670        id
1671    }
1672
1673    fn write_constant_composite(
1674        &mut self,
1675        id: Word,
1676        ty: LookupType,
1677        constituent_ids: &[Word],
1678        debug_name: Option<&String>,
1679    ) {
1680        if self.flags.contains(WriterFlags::DEBUG) {
1681            if let Some(name) = debug_name {
1682                self.debugs.push(Instruction::name(id, name));
1683            }
1684        }
1685        let type_id = self.get_type_id(ty);
1686        Instruction::constant_composite(type_id, id, constituent_ids)
1687            .to_words(&mut self.logical_layout.declarations);
1688    }
1689
1690    pub(super) fn get_constant_null(&mut self, type_id: Word) -> Word {
1691        let null = CachedConstant::ZeroValue(type_id);
1692        if let Some(&id) = self.cached_constants.get(&null) {
1693            return id;
1694        }
1695        let id = self.write_constant_null(type_id);
1696        self.cached_constants.insert(null, id);
1697        id
1698    }
1699
1700    pub(super) fn write_constant_null(&mut self, type_id: Word) -> Word {
1701        let null_id = self.id_gen.next();
1702        Instruction::constant_null(type_id, null_id)
1703            .to_words(&mut self.logical_layout.declarations);
1704        null_id
1705    }
1706
1707    fn write_constant_expr(
1708        &mut self,
1709        handle: Handle<crate::Expression>,
1710        ir_module: &crate::Module,
1711        mod_info: &ModuleInfo,
1712    ) -> Result<Word, Error> {
1713        let id = match ir_module.global_expressions[handle] {
1714            crate::Expression::Literal(literal) => self.get_constant_scalar(literal),
1715            crate::Expression::Constant(constant) => {
1716                let constant = &ir_module.constants[constant];
1717                self.constant_ids[constant.init]
1718            }
1719            crate::Expression::ZeroValue(ty) => {
1720                let type_id = self.get_handle_type_id(ty);
1721                self.get_constant_null(type_id)
1722            }
1723            crate::Expression::Compose { ty, ref components } => {
1724                let component_ids: Vec<_> = crate::proc::flatten_compose(
1725                    ty,
1726                    components,
1727                    &ir_module.global_expressions,
1728                    &ir_module.types,
1729                )
1730                .map(|component| self.constant_ids[component])
1731                .collect();
1732                self.get_constant_composite(LookupType::Handle(ty), component_ids.as_slice())
1733            }
1734            crate::Expression::Splat { size, value } => {
1735                let value_id = self.constant_ids[value];
1736                let component_ids = &[value_id; 4][..size as usize];
1737
1738                let ty = self.get_expression_lookup_type(&mod_info[handle]);
1739
1740                self.get_constant_composite(ty, component_ids)
1741            }
1742            _ => {
1743                return Err(Error::Override);
1744            }
1745        };
1746
1747        self.constant_ids[handle] = id;
1748
1749        Ok(id)
1750    }
1751
1752    pub(super) fn write_control_barrier(&mut self, flags: crate::Barrier, block: &mut Block) {
1753        let memory_scope = if flags.contains(crate::Barrier::STORAGE) {
1754            spirv::Scope::Device
1755        } else if flags.contains(crate::Barrier::SUB_GROUP) {
1756            spirv::Scope::Subgroup
1757        } else {
1758            spirv::Scope::Workgroup
1759        };
1760        let mut semantics = spirv::MemorySemantics::ACQUIRE_RELEASE;
1761        semantics.set(
1762            spirv::MemorySemantics::UNIFORM_MEMORY,
1763            flags.contains(crate::Barrier::STORAGE),
1764        );
1765        semantics.set(
1766            spirv::MemorySemantics::WORKGROUP_MEMORY,
1767            flags.contains(crate::Barrier::WORK_GROUP),
1768        );
1769        semantics.set(
1770            spirv::MemorySemantics::SUBGROUP_MEMORY,
1771            flags.contains(crate::Barrier::SUB_GROUP),
1772        );
1773        semantics.set(
1774            spirv::MemorySemantics::IMAGE_MEMORY,
1775            flags.contains(crate::Barrier::TEXTURE),
1776        );
1777        let exec_scope_id = if flags.contains(crate::Barrier::SUB_GROUP) {
1778            self.get_index_constant(spirv::Scope::Subgroup as u32)
1779        } else {
1780            self.get_index_constant(spirv::Scope::Workgroup as u32)
1781        };
1782        let mem_scope_id = self.get_index_constant(memory_scope as u32);
1783        let semantics_id = self.get_index_constant(semantics.bits());
1784        block.body.push(Instruction::control_barrier(
1785            exec_scope_id,
1786            mem_scope_id,
1787            semantics_id,
1788        ));
1789    }
1790
1791    pub(super) fn write_memory_barrier(&mut self, flags: crate::Barrier, block: &mut Block) {
1792        let mut semantics = spirv::MemorySemantics::ACQUIRE_RELEASE;
1793        semantics.set(
1794            spirv::MemorySemantics::UNIFORM_MEMORY,
1795            flags.contains(crate::Barrier::STORAGE),
1796        );
1797        semantics.set(
1798            spirv::MemorySemantics::WORKGROUP_MEMORY,
1799            flags.contains(crate::Barrier::WORK_GROUP),
1800        );
1801        semantics.set(
1802            spirv::MemorySemantics::SUBGROUP_MEMORY,
1803            flags.contains(crate::Barrier::SUB_GROUP),
1804        );
1805        semantics.set(
1806            spirv::MemorySemantics::IMAGE_MEMORY,
1807            flags.contains(crate::Barrier::TEXTURE),
1808        );
1809        let mem_scope_id = if flags.contains(crate::Barrier::STORAGE) {
1810            self.get_index_constant(spirv::Scope::Device as u32)
1811        } else if flags.contains(crate::Barrier::SUB_GROUP) {
1812            self.get_index_constant(spirv::Scope::Subgroup as u32)
1813        } else {
1814            self.get_index_constant(spirv::Scope::Workgroup as u32)
1815        };
1816        let semantics_id = self.get_index_constant(semantics.bits());
1817        block
1818            .body
1819            .push(Instruction::memory_barrier(mem_scope_id, semantics_id));
1820    }
1821
1822    fn generate_workgroup_vars_init_block(
1823        &mut self,
1824        entry_id: Word,
1825        ir_module: &crate::Module,
1826        info: &FunctionInfo,
1827        local_invocation_id: Option<Word>,
1828        interface: &mut FunctionInterface,
1829        function: &mut Function,
1830    ) -> Option<Word> {
1831        let body = ir_module
1832            .global_variables
1833            .iter()
1834            .filter(|&(handle, var)| {
1835                !info[handle].is_empty() && var.space == crate::AddressSpace::WorkGroup
1836            })
1837            .map(|(handle, var)| {
1838                // It's safe to use `var_id` here, not `access_id`, because only
1839                // variables in the `Uniform` and `StorageBuffer` address spaces
1840                // get wrapped, and we're initializing `WorkGroup` variables.
1841                let var_id = self.global_variables[handle].var_id;
1842                let var_type_id = self.get_handle_type_id(var.ty);
1843                let init_word = self.get_constant_null(var_type_id);
1844                Instruction::store(var_id, init_word, None)
1845            })
1846            .collect::<Vec<_>>();
1847
1848        if body.is_empty() {
1849            return None;
1850        }
1851
1852        let uint3_type_id = self.get_vec3u_type_id();
1853
1854        let mut pre_if_block = Block::new(entry_id);
1855
1856        let local_invocation_id = if let Some(local_invocation_id) = local_invocation_id {
1857            local_invocation_id
1858        } else {
1859            let varying_id = self.id_gen.next();
1860            let class = spirv::StorageClass::Input;
1861            let pointer_type_id = self.get_vec3u_pointer_type_id(class);
1862
1863            Instruction::variable(pointer_type_id, varying_id, class, None)
1864                .to_words(&mut self.logical_layout.declarations);
1865
1866            self.decorate(
1867                varying_id,
1868                spirv::Decoration::BuiltIn,
1869                &[spirv::BuiltIn::LocalInvocationId as u32],
1870            );
1871
1872            interface.varying_ids.push(varying_id);
1873            let id = self.id_gen.next();
1874            pre_if_block
1875                .body
1876                .push(Instruction::load(uint3_type_id, id, varying_id, None));
1877
1878            id
1879        };
1880
1881        let zero_id = self.get_constant_null(uint3_type_id);
1882        let bool3_type_id = self.get_vec3_bool_type_id();
1883
1884        let eq_id = self.id_gen.next();
1885        pre_if_block.body.push(Instruction::binary(
1886            spirv::Op::IEqual,
1887            bool3_type_id,
1888            eq_id,
1889            local_invocation_id,
1890            zero_id,
1891        ));
1892
1893        let condition_id = self.id_gen.next();
1894        let bool_type_id = self.get_bool_type_id();
1895        pre_if_block.body.push(Instruction::relational(
1896            spirv::Op::All,
1897            bool_type_id,
1898            condition_id,
1899            eq_id,
1900        ));
1901
1902        let merge_id = self.id_gen.next();
1903        pre_if_block.body.push(Instruction::selection_merge(
1904            merge_id,
1905            spirv::SelectionControl::NONE,
1906        ));
1907
1908        let accept_id = self.id_gen.next();
1909        function.consume(
1910            pre_if_block,
1911            Instruction::branch_conditional(condition_id, accept_id, merge_id),
1912        );
1913
1914        let accept_block = Block {
1915            label_id: accept_id,
1916            body,
1917        };
1918        function.consume(accept_block, Instruction::branch(merge_id));
1919
1920        let mut post_if_block = Block::new(merge_id);
1921
1922        self.write_control_barrier(crate::Barrier::WORK_GROUP, &mut post_if_block);
1923
1924        let next_id = self.id_gen.next();
1925        function.consume(post_if_block, Instruction::branch(next_id));
1926        Some(next_id)
1927    }
1928
1929    /// Generate an `OpVariable` for one value in an [`EntryPoint`]'s IO interface.
1930    ///
1931    /// The [`Binding`]s of the arguments and result of an [`EntryPoint`]'s
1932    /// [`Function`] describe a SPIR-V shader interface. In SPIR-V, the
1933    /// interface is represented by global variables in the `Input` and `Output`
1934    /// storage classes, with decorations indicating which builtin or location
1935    /// each variable corresponds to.
1936    ///
1937    /// This function emits a single global `OpVariable` for a single value from
1938    /// the interface, and adds appropriate decorations to indicate which
1939    /// builtin or location it represents, how it should be interpolated, and so
1940    /// on. The `class` argument gives the variable's SPIR-V storage class,
1941    /// which should be either [`Input`] or [`Output`].
1942    ///
1943    /// [`Binding`]: crate::Binding
1944    /// [`Function`]: crate::Function
1945    /// [`EntryPoint`]: crate::EntryPoint
1946    /// [`Input`]: spirv::StorageClass::Input
1947    /// [`Output`]: spirv::StorageClass::Output
1948    fn write_varying(
1949        &mut self,
1950        ir_module: &crate::Module,
1951        stage: crate::ShaderStage,
1952        class: spirv::StorageClass,
1953        debug_name: Option<&str>,
1954        ty: Handle<crate::Type>,
1955        binding: &crate::Binding,
1956    ) -> Result<Word, Error> {
1957        use crate::TypeInner;
1958
1959        let id = self.id_gen.next();
1960        let ty_inner = &ir_module.types[ty].inner;
1961        let needs_polyfill = self.needs_f16_polyfill(ty_inner);
1962
1963        let pointer_type_id = if needs_polyfill {
1964            let f32_value_local =
1965                super::f16_polyfill::F16IoPolyfill::create_polyfill_type(ty_inner)
1966                    .expect("needs_polyfill returned true but create_polyfill_type returned None");
1967
1968            let f32_type_id = self.get_localtype_id(f32_value_local);
1969            let ptr_id = self.get_pointer_type_id(f32_type_id, class);
1970            self.io_f16_polyfills.register_io_var(id, f32_type_id);
1971
1972            ptr_id
1973        } else {
1974            self.get_handle_pointer_type_id(ty, class)
1975        };
1976
1977        Instruction::variable(pointer_type_id, id, class, None)
1978            .to_words(&mut self.logical_layout.declarations);
1979
1980        if self
1981            .flags
1982            .contains(WriterFlags::DEBUG | WriterFlags::LABEL_VARYINGS)
1983        {
1984            if let Some(name) = debug_name {
1985                self.debugs.push(Instruction::name(id, name));
1986            }
1987        }
1988
1989        use spirv::{BuiltIn, Decoration};
1990
1991        match *binding {
1992            crate::Binding::Location {
1993                location,
1994                interpolation,
1995                sampling,
1996                blend_src,
1997                per_primitive: _,
1998            } => {
1999                self.decorate(id, Decoration::Location, &[location]);
2000
2001                let no_decorations =
2002                    // VUID-StandaloneSpirv-Flat-06202
2003                    // > The Flat, NoPerspective, Sample, and Centroid decorations
2004                    // > must not be used on variables with the Input storage class in a vertex shader
2005                    (class == spirv::StorageClass::Input && stage == crate::ShaderStage::Vertex) ||
2006                    // VUID-StandaloneSpirv-Flat-06201
2007                    // > The Flat, NoPerspective, Sample, and Centroid decorations
2008                    // > must not be used on variables with the Output storage class in a fragment shader
2009                    (class == spirv::StorageClass::Output && stage == crate::ShaderStage::Fragment);
2010
2011                if !no_decorations {
2012                    match interpolation {
2013                        // Perspective-correct interpolation is the default in SPIR-V.
2014                        None | Some(crate::Interpolation::Perspective) => (),
2015                        Some(crate::Interpolation::Flat) => {
2016                            self.decorate(id, Decoration::Flat, &[]);
2017                        }
2018                        Some(crate::Interpolation::Linear) => {
2019                            self.decorate(id, Decoration::NoPerspective, &[]);
2020                        }
2021                    }
2022                    match sampling {
2023                        // Center sampling is the default in SPIR-V.
2024                        None
2025                        | Some(
2026                            crate::Sampling::Center
2027                            | crate::Sampling::First
2028                            | crate::Sampling::Either,
2029                        ) => (),
2030                        Some(crate::Sampling::Centroid) => {
2031                            self.decorate(id, Decoration::Centroid, &[]);
2032                        }
2033                        Some(crate::Sampling::Sample) => {
2034                            self.require_any(
2035                                "per-sample interpolation",
2036                                &[spirv::Capability::SampleRateShading],
2037                            )?;
2038                            self.decorate(id, Decoration::Sample, &[]);
2039                        }
2040                    }
2041                }
2042                if let Some(blend_src) = blend_src {
2043                    self.decorate(id, Decoration::Index, &[blend_src]);
2044                }
2045            }
2046            crate::Binding::BuiltIn(built_in) => {
2047                use crate::BuiltIn as Bi;
2048                let built_in = match built_in {
2049                    Bi::Position { invariant } => {
2050                        if invariant {
2051                            self.decorate(id, Decoration::Invariant, &[]);
2052                        }
2053
2054                        if class == spirv::StorageClass::Output {
2055                            BuiltIn::Position
2056                        } else {
2057                            BuiltIn::FragCoord
2058                        }
2059                    }
2060                    Bi::ViewIndex => {
2061                        self.require_any("`view_index` built-in", &[spirv::Capability::MultiView])?;
2062                        BuiltIn::ViewIndex
2063                    }
2064                    // vertex
2065                    Bi::BaseInstance => BuiltIn::BaseInstance,
2066                    Bi::BaseVertex => BuiltIn::BaseVertex,
2067                    Bi::ClipDistance => {
2068                        self.require_any(
2069                            "`clip_distance` built-in",
2070                            &[spirv::Capability::ClipDistance],
2071                        )?;
2072                        BuiltIn::ClipDistance
2073                    }
2074                    Bi::CullDistance => {
2075                        self.require_any(
2076                            "`cull_distance` built-in",
2077                            &[spirv::Capability::CullDistance],
2078                        )?;
2079                        BuiltIn::CullDistance
2080                    }
2081                    Bi::InstanceIndex => BuiltIn::InstanceIndex,
2082                    Bi::PointSize => BuiltIn::PointSize,
2083                    Bi::VertexIndex => BuiltIn::VertexIndex,
2084                    Bi::DrawID => BuiltIn::DrawIndex,
2085                    // fragment
2086                    Bi::FragDepth => BuiltIn::FragDepth,
2087                    Bi::PointCoord => BuiltIn::PointCoord,
2088                    Bi::FrontFacing => BuiltIn::FrontFacing,
2089                    Bi::PrimitiveIndex => {
2090                        self.require_any(
2091                            "`primitive_index` built-in",
2092                            &[spirv::Capability::Geometry],
2093                        )?;
2094                        BuiltIn::PrimitiveId
2095                    }
2096                    Bi::Barycentric => {
2097                        self.require_any(
2098                            "`barycentric` built-in",
2099                            &[spirv::Capability::FragmentBarycentricKHR],
2100                        )?;
2101                        self.use_extension("SPV_KHR_fragment_shader_barycentric");
2102                        BuiltIn::BaryCoordKHR
2103                    }
2104                    Bi::SampleIndex => {
2105                        self.require_any(
2106                            "`sample_index` built-in",
2107                            &[spirv::Capability::SampleRateShading],
2108                        )?;
2109
2110                        BuiltIn::SampleId
2111                    }
2112                    Bi::SampleMask => BuiltIn::SampleMask,
2113                    // compute
2114                    Bi::GlobalInvocationId => BuiltIn::GlobalInvocationId,
2115                    Bi::LocalInvocationId => BuiltIn::LocalInvocationId,
2116                    Bi::LocalInvocationIndex => BuiltIn::LocalInvocationIndex,
2117                    Bi::WorkGroupId => BuiltIn::WorkgroupId,
2118                    Bi::WorkGroupSize => BuiltIn::WorkgroupSize,
2119                    Bi::NumWorkGroups => BuiltIn::NumWorkgroups,
2120                    // Subgroup
2121                    Bi::NumSubgroups => {
2122                        self.require_any(
2123                            "`num_subgroups` built-in",
2124                            &[spirv::Capability::GroupNonUniform],
2125                        )?;
2126                        BuiltIn::NumSubgroups
2127                    }
2128                    Bi::SubgroupId => {
2129                        self.require_any(
2130                            "`subgroup_id` built-in",
2131                            &[spirv::Capability::GroupNonUniform],
2132                        )?;
2133                        BuiltIn::SubgroupId
2134                    }
2135                    Bi::SubgroupSize => {
2136                        self.require_any(
2137                            "`subgroup_size` built-in",
2138                            &[
2139                                spirv::Capability::GroupNonUniform,
2140                                spirv::Capability::SubgroupBallotKHR,
2141                            ],
2142                        )?;
2143                        BuiltIn::SubgroupSize
2144                    }
2145                    Bi::SubgroupInvocationId => {
2146                        self.require_any(
2147                            "`subgroup_invocation_id` built-in",
2148                            &[
2149                                spirv::Capability::GroupNonUniform,
2150                                spirv::Capability::SubgroupBallotKHR,
2151                            ],
2152                        )?;
2153                        BuiltIn::SubgroupLocalInvocationId
2154                    }
2155                    Bi::MeshTaskSize
2156                    | Bi::CullPrimitive
2157                    | Bi::PointIndex
2158                    | Bi::LineIndices
2159                    | Bi::TriangleIndices => unreachable!(),
2160                };
2161
2162                self.decorate(id, Decoration::BuiltIn, &[built_in as u32]);
2163
2164                use crate::ScalarKind as Sk;
2165
2166                // Per the Vulkan spec, `VUID-StandaloneSpirv-Flat-04744`:
2167                //
2168                // > Any variable with integer or double-precision floating-
2169                // > point type and with Input storage class in a fragment
2170                // > shader, must be decorated Flat
2171                if class == spirv::StorageClass::Input && stage == crate::ShaderStage::Fragment {
2172                    let is_flat = match ir_module.types[ty].inner {
2173                        TypeInner::Scalar(scalar) | TypeInner::Vector { scalar, .. } => match scalar
2174                            .kind
2175                        {
2176                            Sk::Uint | Sk::Sint | Sk::Bool => true,
2177                            Sk::Float => false,
2178                            Sk::AbstractInt | Sk::AbstractFloat => {
2179                                return Err(Error::Validation(
2180                                    "Abstract types should not appear in IR presented to backends",
2181                                ))
2182                            }
2183                        },
2184                        _ => false,
2185                    };
2186
2187                    if is_flat {
2188                        self.decorate(id, Decoration::Flat, &[]);
2189                    }
2190                }
2191            }
2192        }
2193
2194        Ok(id)
2195    }
2196
2197    /// Load an IO variable, converting from `f32` to `f16` if polyfill is active.
2198    /// Returns the id of the loaded value matching `target_type_id`.
2199    pub(super) fn load_io_with_f16_polyfill(
2200        &mut self,
2201        body: &mut Vec<Instruction>,
2202        varying_id: Word,
2203        target_type_id: Word,
2204    ) -> Word {
2205        let tmp = self.id_gen.next();
2206        if let Some(f32_ty) = self.io_f16_polyfills.get_f32_io_type(varying_id) {
2207            body.push(Instruction::load(f32_ty, tmp, varying_id, None));
2208            let converted = self.id_gen.next();
2209            super::f16_polyfill::F16IoPolyfill::emit_f32_to_f16_conversion(
2210                tmp,
2211                target_type_id,
2212                converted,
2213                body,
2214            );
2215            converted
2216        } else {
2217            body.push(Instruction::load(target_type_id, tmp, varying_id, None));
2218            tmp
2219        }
2220    }
2221
2222    /// Store an IO variable, converting from `f16` to `f32` if polyfill is active.
2223    pub(super) fn store_io_with_f16_polyfill(
2224        &mut self,
2225        body: &mut Vec<Instruction>,
2226        varying_id: Word,
2227        value_id: Word,
2228    ) {
2229        if let Some(f32_ty) = self.io_f16_polyfills.get_f32_io_type(varying_id) {
2230            let converted = self.id_gen.next();
2231            super::f16_polyfill::F16IoPolyfill::emit_f16_to_f32_conversion(
2232                value_id, f32_ty, converted, body,
2233            );
2234            body.push(Instruction::store(varying_id, converted, None));
2235        } else {
2236            body.push(Instruction::store(varying_id, value_id, None));
2237        }
2238    }
2239
2240    fn write_global_variable(
2241        &mut self,
2242        ir_module: &crate::Module,
2243        global_variable: &crate::GlobalVariable,
2244    ) -> Result<Word, Error> {
2245        use spirv::Decoration;
2246
2247        let id = self.id_gen.next();
2248        let class = map_storage_class(global_variable.space);
2249
2250        //self.check(class.required_capabilities())?;
2251
2252        if self.flags.contains(WriterFlags::DEBUG) {
2253            if let Some(ref name) = global_variable.name {
2254                self.debugs.push(Instruction::name(id, name));
2255            }
2256        }
2257
2258        let storage_access = match global_variable.space {
2259            crate::AddressSpace::Storage { access } => Some(access),
2260            _ => match ir_module.types[global_variable.ty].inner {
2261                crate::TypeInner::Image {
2262                    class: crate::ImageClass::Storage { access, .. },
2263                    ..
2264                } => Some(access),
2265                _ => None,
2266            },
2267        };
2268        if let Some(storage_access) = storage_access {
2269            if !storage_access.contains(crate::StorageAccess::LOAD) {
2270                self.decorate(id, Decoration::NonReadable, &[]);
2271            }
2272            if !storage_access.contains(crate::StorageAccess::STORE) {
2273                self.decorate(id, Decoration::NonWritable, &[]);
2274            }
2275        }
2276
2277        // Note: we should be able to substitute `binding_array<Foo, 0>`,
2278        // but there is still code that tries to register the pre-substituted type,
2279        // and it is failing on 0.
2280        let mut substitute_inner_type_lookup = None;
2281        if let Some(ref res_binding) = global_variable.binding {
2282            let bind_target = self.resolve_resource_binding(res_binding)?;
2283            self.decorate(id, Decoration::DescriptorSet, &[bind_target.descriptor_set]);
2284            self.decorate(id, Decoration::Binding, &[bind_target.binding]);
2285
2286            if let Some(remapped_binding_array_size) = bind_target.binding_array_size {
2287                if let crate::TypeInner::BindingArray { base, .. } =
2288                    ir_module.types[global_variable.ty].inner
2289                {
2290                    let binding_array_type_id =
2291                        self.get_type_id(LookupType::Local(LocalType::BindingArray {
2292                            base,
2293                            size: remapped_binding_array_size,
2294                        }));
2295                    substitute_inner_type_lookup = Some(LookupType::Local(LocalType::Pointer {
2296                        base: binding_array_type_id,
2297                        class,
2298                    }));
2299                }
2300            }
2301        };
2302
2303        let init_word = global_variable
2304            .init
2305            .map(|constant| self.constant_ids[constant]);
2306        let inner_type_id = self.get_type_id(
2307            substitute_inner_type_lookup.unwrap_or(LookupType::Handle(global_variable.ty)),
2308        );
2309
2310        // generate the wrapping structure if needed
2311        let pointer_type_id = if global_needs_wrapper(ir_module, global_variable) {
2312            let wrapper_type_id = self.id_gen.next();
2313
2314            self.decorate(wrapper_type_id, Decoration::Block, &[]);
2315            let member = crate::StructMember {
2316                name: None,
2317                ty: global_variable.ty,
2318                binding: None,
2319                offset: 0,
2320            };
2321            self.decorate_struct_member(wrapper_type_id, 0, &member, &ir_module.types)?;
2322
2323            Instruction::type_struct(wrapper_type_id, &[inner_type_id])
2324                .to_words(&mut self.logical_layout.declarations);
2325
2326            let pointer_type_id = self.id_gen.next();
2327            Instruction::type_pointer(pointer_type_id, class, wrapper_type_id)
2328                .to_words(&mut self.logical_layout.declarations);
2329
2330            pointer_type_id
2331        } else {
2332            // This is a global variable in the Storage address space. The only
2333            // way it could have `global_needs_wrapper() == false` is if it has
2334            // a runtime-sized or binding array.
2335            // Runtime-sized arrays were decorated when iterating through struct content.
2336            // Now binding arrays require Block decorating.
2337            if let crate::AddressSpace::Storage { .. } = global_variable.space {
2338                match ir_module.types[global_variable.ty].inner {
2339                    crate::TypeInner::BindingArray { base, .. } => {
2340                        let ty = &ir_module.types[base];
2341                        let mut should_decorate = true;
2342                        // Check if the type has a runtime array.
2343                        // A normal runtime array gets validated out,
2344                        // so only structs can be with runtime arrays
2345                        if let crate::TypeInner::Struct { ref members, .. } = ty.inner {
2346                            // only the last member in a struct can be dynamically sized
2347                            if let Some(last_member) = members.last() {
2348                                if let &crate::TypeInner::Array {
2349                                    size: crate::ArraySize::Dynamic,
2350                                    ..
2351                                } = &ir_module.types[last_member.ty].inner
2352                                {
2353                                    should_decorate = false;
2354                                }
2355                            }
2356                        }
2357                        if should_decorate {
2358                            let decorated_id = self.get_handle_type_id(base);
2359                            self.decorate(decorated_id, Decoration::Block, &[]);
2360                        }
2361                    }
2362                    _ => (),
2363                };
2364            }
2365            if substitute_inner_type_lookup.is_some() {
2366                inner_type_id
2367            } else {
2368                self.get_handle_pointer_type_id(global_variable.ty, class)
2369            }
2370        };
2371
2372        let init_word = match (global_variable.space, self.zero_initialize_workgroup_memory) {
2373            (crate::AddressSpace::Private, _)
2374            | (crate::AddressSpace::WorkGroup, super::ZeroInitializeWorkgroupMemoryMode::Native) => {
2375                init_word.or_else(|| Some(self.get_constant_null(inner_type_id)))
2376            }
2377            _ => init_word,
2378        };
2379
2380        Instruction::variable(pointer_type_id, id, class, init_word)
2381            .to_words(&mut self.logical_layout.declarations);
2382        Ok(id)
2383    }
2384
2385    /// Write the necessary decorations for a struct member.
2386    ///
2387    /// Emit decorations for the `index`'th member of the struct type
2388    /// designated by `struct_id`, described by `member`.
2389    fn decorate_struct_member(
2390        &mut self,
2391        struct_id: Word,
2392        index: usize,
2393        member: &crate::StructMember,
2394        arena: &UniqueArena<crate::Type>,
2395    ) -> Result<(), Error> {
2396        use spirv::Decoration;
2397
2398        self.annotations.push(Instruction::member_decorate(
2399            struct_id,
2400            index as u32,
2401            Decoration::Offset,
2402            &[member.offset],
2403        ));
2404
2405        if self.flags.contains(WriterFlags::DEBUG) {
2406            if let Some(ref name) = member.name {
2407                self.debugs
2408                    .push(Instruction::member_name(struct_id, index as u32, name));
2409            }
2410        }
2411
2412        // Matrices and (potentially nested) arrays of matrices both require decorations,
2413        // so "see through" any arrays to determine if they're needed.
2414        let mut member_array_subty_inner = &arena[member.ty].inner;
2415        while let crate::TypeInner::Array { base, .. } = *member_array_subty_inner {
2416            member_array_subty_inner = &arena[base].inner;
2417        }
2418
2419        if let crate::TypeInner::Matrix {
2420            columns: _,
2421            rows,
2422            scalar,
2423        } = *member_array_subty_inner
2424        {
2425            let byte_stride = Alignment::from(rows) * scalar.width as u32;
2426            self.annotations.push(Instruction::member_decorate(
2427                struct_id,
2428                index as u32,
2429                Decoration::ColMajor,
2430                &[],
2431            ));
2432            self.annotations.push(Instruction::member_decorate(
2433                struct_id,
2434                index as u32,
2435                Decoration::MatrixStride,
2436                &[byte_stride],
2437            ));
2438        }
2439
2440        Ok(())
2441    }
2442
2443    pub(super) fn get_function_type(&mut self, lookup_function_type: LookupFunctionType) -> Word {
2444        match self
2445            .lookup_function_type
2446            .entry(lookup_function_type.clone())
2447        {
2448            Entry::Occupied(e) => *e.get(),
2449            Entry::Vacant(_) => {
2450                let id = self.id_gen.next();
2451                let instruction = Instruction::type_function(
2452                    id,
2453                    lookup_function_type.return_type_id,
2454                    &lookup_function_type.parameter_type_ids,
2455                );
2456                instruction.to_words(&mut self.logical_layout.declarations);
2457                self.lookup_function_type.insert(lookup_function_type, id);
2458                id
2459            }
2460        }
2461    }
2462
2463    fn write_physical_layout(&mut self) {
2464        self.physical_layout.bound = self.id_gen.0 + 1;
2465    }
2466
2467    fn write_logical_layout(
2468        &mut self,
2469        ir_module: &crate::Module,
2470        mod_info: &ModuleInfo,
2471        ep_index: Option<usize>,
2472        debug_info: &Option<DebugInfo>,
2473    ) -> Result<(), Error> {
2474        fn has_view_index_check(
2475            ir_module: &crate::Module,
2476            binding: Option<&crate::Binding>,
2477            ty: Handle<crate::Type>,
2478        ) -> bool {
2479            match ir_module.types[ty].inner {
2480                crate::TypeInner::Struct { ref members, .. } => members.iter().any(|member| {
2481                    has_view_index_check(ir_module, member.binding.as_ref(), member.ty)
2482                }),
2483                _ => binding == Some(&crate::Binding::BuiltIn(crate::BuiltIn::ViewIndex)),
2484            }
2485        }
2486
2487        let has_storage_buffers =
2488            ir_module
2489                .global_variables
2490                .iter()
2491                .any(|(_, var)| match var.space {
2492                    crate::AddressSpace::Storage { .. } => true,
2493                    _ => false,
2494                });
2495        let has_view_index = ir_module
2496            .entry_points
2497            .iter()
2498            .flat_map(|entry| entry.function.arguments.iter())
2499            .any(|arg| has_view_index_check(ir_module, arg.binding.as_ref(), arg.ty));
2500        let mut has_ray_query = ir_module.special_types.ray_desc.is_some()
2501            | ir_module.special_types.ray_intersection.is_some();
2502        let has_vertex_return = ir_module.special_types.ray_vertex_return.is_some();
2503
2504        for (_, &crate::Type { ref inner, .. }) in ir_module.types.iter() {
2505            // spirv does not know whether these have vertex return - that is done by us
2506            if let &crate::TypeInner::AccelerationStructure { .. }
2507            | &crate::TypeInner::RayQuery { .. } = inner
2508            {
2509                has_ray_query = true
2510            }
2511        }
2512
2513        if self.physical_layout.version < 0x10300 && has_storage_buffers {
2514            // enable the storage buffer class on < SPV-1.3
2515            Instruction::extension("SPV_KHR_storage_buffer_storage_class")
2516                .to_words(&mut self.logical_layout.extensions);
2517        }
2518        if has_view_index {
2519            Instruction::extension("SPV_KHR_multiview")
2520                .to_words(&mut self.logical_layout.extensions)
2521        }
2522        if has_ray_query {
2523            Instruction::extension("SPV_KHR_ray_query")
2524                .to_words(&mut self.logical_layout.extensions)
2525        }
2526        if has_vertex_return {
2527            Instruction::extension("SPV_KHR_ray_tracing_position_fetch")
2528                .to_words(&mut self.logical_layout.extensions);
2529        }
2530        Instruction::type_void(self.void_type).to_words(&mut self.logical_layout.declarations);
2531        Instruction::ext_inst_import(self.gl450_ext_inst_id, "GLSL.std.450")
2532            .to_words(&mut self.logical_layout.ext_inst_imports);
2533
2534        let mut debug_info_inner = None;
2535        if self.flags.contains(WriterFlags::DEBUG) {
2536            if let Some(debug_info) = debug_info.as_ref() {
2537                let source_file_id = self.id_gen.next();
2538                self.debugs
2539                    .push(Instruction::string(debug_info.file_name, source_file_id));
2540
2541                debug_info_inner = Some(DebugInfoInner {
2542                    source_code: debug_info.source_code,
2543                    source_file_id,
2544                });
2545                self.debugs.append(&mut Instruction::source_auto_continued(
2546                    debug_info.language,
2547                    0,
2548                    &debug_info_inner,
2549                ));
2550            }
2551        }
2552
2553        // write all types
2554        for (handle, _) in ir_module.types.iter() {
2555            self.write_type_declaration_arena(ir_module, handle)?;
2556        }
2557
2558        // write all const-expressions as constants
2559        self.constant_ids
2560            .resize(ir_module.global_expressions.len(), 0);
2561        for (handle, _) in ir_module.global_expressions.iter() {
2562            self.write_constant_expr(handle, ir_module, mod_info)?;
2563        }
2564        debug_assert!(self.constant_ids.iter().all(|&id| id != 0));
2565
2566        // write the name of constants on their respective const-expression initializer
2567        if self.flags.contains(WriterFlags::DEBUG) {
2568            for (_, constant) in ir_module.constants.iter() {
2569                if let Some(ref name) = constant.name {
2570                    let id = self.constant_ids[constant.init];
2571                    self.debugs.push(Instruction::name(id, name));
2572                }
2573            }
2574        }
2575
2576        // write all global variables
2577        for (handle, var) in ir_module.global_variables.iter() {
2578            // If a single entry point was specified, only write `OpVariable` instructions
2579            // for the globals it actually uses. Emit dummies for the others,
2580            // to preserve the indices in `global_variables`.
2581            let gvar = match ep_index {
2582                Some(index) if mod_info.get_entry_point(index)[handle].is_empty() => {
2583                    GlobalVariable::dummy()
2584                }
2585                _ => {
2586                    let id = self.write_global_variable(ir_module, var)?;
2587                    GlobalVariable::new(id)
2588                }
2589            };
2590            self.global_variables.insert(handle, gvar);
2591        }
2592
2593        // write all functions
2594        for (handle, ir_function) in ir_module.functions.iter() {
2595            let info = &mod_info[handle];
2596            if let Some(index) = ep_index {
2597                let ep_info = mod_info.get_entry_point(index);
2598                // If this function uses globals that we omitted from the SPIR-V
2599                // because the entry point and its callees didn't use them,
2600                // then we must skip it.
2601                if !ep_info.dominates_global_use(info) {
2602                    log::info!("Skip function {:?}", ir_function.name);
2603                    continue;
2604                }
2605
2606                // Skip functions that that are not compatible with this entry point's stage.
2607                //
2608                // When validation is enabled, it rejects modules whose entry points try to call
2609                // incompatible functions, so if we got this far, then any functions incompatible
2610                // with our selected entry point must not be used.
2611                //
2612                // When validation is disabled, `fun_info.available_stages` is always just
2613                // `ShaderStages::all()`, so this will write all functions in the module, and
2614                // the downstream GLSL compiler will catch any problems.
2615                if !info.available_stages.contains(ep_info.available_stages) {
2616                    continue;
2617                }
2618            }
2619            let id = self.write_function(ir_function, info, ir_module, None, &debug_info_inner)?;
2620            self.lookup_function.insert(handle, id);
2621        }
2622
2623        // write all or one entry points
2624        for (index, ir_ep) in ir_module.entry_points.iter().enumerate() {
2625            if ep_index.is_some() && ep_index != Some(index) {
2626                continue;
2627            }
2628            let info = mod_info.get_entry_point(index);
2629            let ep_instruction =
2630                self.write_entry_point(ir_ep, info, ir_module, &debug_info_inner)?;
2631            ep_instruction.to_words(&mut self.logical_layout.entry_points);
2632        }
2633
2634        for capability in self.capabilities_used.iter() {
2635            Instruction::capability(*capability).to_words(&mut self.logical_layout.capabilities);
2636        }
2637        for extension in self.extensions_used.iter() {
2638            Instruction::extension(extension).to_words(&mut self.logical_layout.extensions);
2639        }
2640        if ir_module.entry_points.is_empty() {
2641            // SPIR-V doesn't like modules without entry points
2642            Instruction::capability(spirv::Capability::Linkage)
2643                .to_words(&mut self.logical_layout.capabilities);
2644        }
2645
2646        let addressing_model = spirv::AddressingModel::Logical;
2647        let memory_model = spirv::MemoryModel::GLSL450;
2648        //self.check(addressing_model.required_capabilities())?;
2649        //self.check(memory_model.required_capabilities())?;
2650
2651        Instruction::memory_model(addressing_model, memory_model)
2652            .to_words(&mut self.logical_layout.memory_model);
2653
2654        if self.flags.contains(WriterFlags::DEBUG) {
2655            for debug in self.debugs.iter() {
2656                debug.to_words(&mut self.logical_layout.debugs);
2657            }
2658        }
2659
2660        for annotation in self.annotations.iter() {
2661            annotation.to_words(&mut self.logical_layout.annotations);
2662        }
2663
2664        Ok(())
2665    }
2666
2667    pub fn write(
2668        &mut self,
2669        ir_module: &crate::Module,
2670        info: &ModuleInfo,
2671        pipeline_options: Option<&PipelineOptions>,
2672        debug_info: &Option<DebugInfo>,
2673        words: &mut Vec<Word>,
2674    ) -> Result<(), Error> {
2675        self.reset();
2676
2677        // Try to find the entry point and corresponding index
2678        let ep_index = match pipeline_options {
2679            Some(po) => {
2680                let index = ir_module
2681                    .entry_points
2682                    .iter()
2683                    .position(|ep| po.shader_stage == ep.stage && po.entry_point == ep.name)
2684                    .ok_or(Error::EntryPointNotFound)?;
2685                Some(index)
2686            }
2687            None => None,
2688        };
2689
2690        self.write_logical_layout(ir_module, info, ep_index, debug_info)?;
2691        self.write_physical_layout();
2692
2693        self.physical_layout.in_words(words);
2694        self.logical_layout.in_words(words);
2695        Ok(())
2696    }
2697
2698    /// Return the set of capabilities the last module written used.
2699    pub const fn get_capabilities_used(&self) -> &crate::FastIndexSet<spirv::Capability> {
2700        &self.capabilities_used
2701    }
2702
2703    pub fn decorate_non_uniform_binding_array_access(&mut self, id: Word) -> Result<(), Error> {
2704        self.require_any("NonUniformEXT", &[spirv::Capability::ShaderNonUniform])?;
2705        self.use_extension("SPV_EXT_descriptor_indexing");
2706        self.decorate(id, spirv::Decoration::NonUniform, &[]);
2707        Ok(())
2708    }
2709
2710    pub(super) fn needs_f16_polyfill(&self, ty_inner: &crate::TypeInner) -> bool {
2711        self.io_f16_polyfills.needs_polyfill(ty_inner)
2712    }
2713}
2714
2715#[test]
2716fn test_write_physical_layout() {
2717    let mut writer = Writer::new(&Options::default()).unwrap();
2718    assert_eq!(writer.physical_layout.bound, 0);
2719    writer.write_physical_layout();
2720    assert_eq!(writer.physical_layout.bound, 3);
2721}