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