naga/back/hlsl/
writer.rs

1use alloc::{
2    format,
3    string::{String, ToString},
4    vec::Vec,
5};
6use core::{fmt, mem};
7
8use super::{
9    help,
10    help::{
11        WrappedArrayLength, WrappedConstructor, WrappedImageQuery, WrappedStructMatrixAccess,
12        WrappedZeroValue,
13    },
14    storage::StoreValue,
15    BackendResult, Error, FragmentEntryPoint, Options, PipelineOptions, ShaderModel,
16};
17use crate::{
18    back::{self, get_entry_points, Baked},
19    common,
20    proc::{self, index, ExternalTextureNameKey, NameKey},
21    valid, Handle, Module, RayQueryFunction, Scalar, ScalarKind, ShaderStage, TypeInner,
22};
23
24const LOCATION_SEMANTIC: &str = "LOC";
25const SPECIAL_CBUF_TYPE: &str = "NagaConstants";
26const SPECIAL_CBUF_VAR: &str = "_NagaConstants";
27const SPECIAL_FIRST_VERTEX: &str = "first_vertex";
28const SPECIAL_FIRST_INSTANCE: &str = "first_instance";
29const SPECIAL_OTHER: &str = "other";
30
31pub(crate) const MODF_FUNCTION: &str = "naga_modf";
32pub(crate) const FREXP_FUNCTION: &str = "naga_frexp";
33pub(crate) const EXTRACT_BITS_FUNCTION: &str = "naga_extractBits";
34pub(crate) const INSERT_BITS_FUNCTION: &str = "naga_insertBits";
35pub(crate) const SAMPLER_HEAP_VAR: &str = "nagaSamplerHeap";
36pub(crate) const COMPARISON_SAMPLER_HEAP_VAR: &str = "nagaComparisonSamplerHeap";
37pub(crate) const SAMPLE_EXTERNAL_TEXTURE_FUNCTION: &str = "nagaSampleExternalTexture";
38pub(crate) const ABS_FUNCTION: &str = "naga_abs";
39pub(crate) const DIV_FUNCTION: &str = "naga_div";
40pub(crate) const MOD_FUNCTION: &str = "naga_mod";
41pub(crate) const NEG_FUNCTION: &str = "naga_neg";
42pub(crate) const F2I32_FUNCTION: &str = "naga_f2i32";
43pub(crate) const F2U32_FUNCTION: &str = "naga_f2u32";
44pub(crate) const F2I64_FUNCTION: &str = "naga_f2i64";
45pub(crate) const F2U64_FUNCTION: &str = "naga_f2u64";
46pub(crate) const IMAGE_SAMPLE_BASE_CLAMP_TO_EDGE_FUNCTION: &str =
47    "nagaTextureSampleBaseClampToEdge";
48pub(crate) const IMAGE_LOAD_EXTERNAL_FUNCTION: &str = "nagaTextureLoadExternal";
49
50enum Index {
51    Expression(Handle<crate::Expression>),
52    Static(u32),
53}
54
55struct EpStructMember {
56    name: String,
57    ty: Handle<crate::Type>,
58    // technically, this should always be `Some`
59    // (we `debug_assert!` this in `write_interface_struct`)
60    binding: Option<crate::Binding>,
61    index: u32,
62}
63
64/// Structure contains information required for generating
65/// wrapped structure of all entry points arguments
66struct EntryPointBinding {
67    /// Name of the fake EP argument that contains the struct
68    /// with all the flattened input data.
69    arg_name: String,
70    /// Generated structure name
71    ty_name: String,
72    /// Members of generated structure
73    members: Vec<EpStructMember>,
74}
75
76pub(super) struct EntryPointInterface {
77    /// If `Some`, the input of an entry point is gathered in a special
78    /// struct with members sorted by binding.
79    /// The `EntryPointBinding::members` array is sorted by index,
80    /// so that we can walk it in `write_ep_arguments_initialization`.
81    input: Option<EntryPointBinding>,
82    /// If `Some`, the output of an entry point is flattened.
83    /// The `EntryPointBinding::members` array is sorted by binding,
84    /// So that we can walk it in `Statement::Return` handler.
85    output: Option<EntryPointBinding>,
86}
87
88#[derive(Clone, Eq, PartialEq, PartialOrd, Ord)]
89enum InterfaceKey {
90    Location(u32),
91    BuiltIn(crate::BuiltIn),
92    Other,
93}
94
95impl InterfaceKey {
96    const fn new(binding: Option<&crate::Binding>) -> Self {
97        match binding {
98            Some(&crate::Binding::Location { location, .. }) => Self::Location(location),
99            Some(&crate::Binding::BuiltIn(built_in)) => Self::BuiltIn(built_in),
100            None => Self::Other,
101        }
102    }
103}
104
105#[derive(Copy, Clone, PartialEq)]
106enum Io {
107    Input,
108    Output,
109}
110
111const fn is_subgroup_builtin_binding(binding: &Option<crate::Binding>) -> bool {
112    let &Some(crate::Binding::BuiltIn(builtin)) = binding else {
113        return false;
114    };
115    matches!(
116        builtin,
117        crate::BuiltIn::SubgroupSize
118            | crate::BuiltIn::SubgroupInvocationId
119            | crate::BuiltIn::NumSubgroups
120            | crate::BuiltIn::SubgroupId
121    )
122}
123
124/// Information for how to generate a `binding_array<sampler>` access.
125struct BindingArraySamplerInfo {
126    /// Variable name of the sampler heap
127    sampler_heap_name: &'static str,
128    /// Variable name of the sampler index buffer
129    sampler_index_buffer_name: String,
130    /// Variable name of the base index _into_ the sampler index buffer
131    binding_array_base_index_name: String,
132}
133
134impl<'a, W: fmt::Write> super::Writer<'a, W> {
135    pub fn new(out: W, options: &'a Options, pipeline_options: &'a PipelineOptions) -> Self {
136        Self {
137            out,
138            names: crate::FastHashMap::default(),
139            namer: proc::Namer::default(),
140            options,
141            pipeline_options,
142            entry_point_io: crate::FastHashMap::default(),
143            named_expressions: crate::NamedExpressions::default(),
144            wrapped: super::Wrapped::default(),
145            written_committed_intersection: false,
146            written_candidate_intersection: false,
147            continue_ctx: back::continue_forward::ContinueCtx::default(),
148            temp_access_chain: Vec::new(),
149            need_bake_expressions: Default::default(),
150        }
151    }
152
153    fn reset(&mut self, module: &Module) {
154        self.names.clear();
155        self.namer.reset(
156            module,
157            &super::keywords::RESERVED_SET,
158            &super::keywords::RESERVED_CASE_INSENSITIVE_SET,
159            super::keywords::RESERVED_PREFIXES,
160            &mut self.names,
161        );
162        self.entry_point_io.clear();
163        self.named_expressions.clear();
164        self.wrapped.clear();
165        self.written_committed_intersection = false;
166        self.written_candidate_intersection = false;
167        self.continue_ctx.clear();
168        self.need_bake_expressions.clear();
169    }
170
171    /// Generates statements to be inserted immediately before and at the very
172    /// start of the body of each loop, to defeat infinite loop reasoning.
173    /// The 0th item of the returned tuple should be inserted immediately prior
174    /// to the loop and the 1st item should be inserted at the very start of
175    /// the loop body.
176    ///
177    /// See [`back::msl::Writer::gen_force_bounded_loop_statements`] for details.
178    fn gen_force_bounded_loop_statements(
179        &mut self,
180        level: back::Level,
181    ) -> Option<(String, String)> {
182        if !self.options.force_loop_bounding {
183            return None;
184        }
185
186        let loop_bound_name = self.namer.call("loop_bound");
187        let max = u32::MAX;
188        // Count down from u32::MAX rather than up from 0 to avoid hang on
189        // certain Intel drivers. See <https://github.com/gfx-rs/wgpu/issues/7319>.
190        let decl = format!("{level}uint2 {loop_bound_name} = uint2({max}u, {max}u);");
191        let level = level.next();
192        let break_and_inc = format!(
193            "{level}if (all({loop_bound_name} == uint2(0u, 0u))) {{ break; }}
194{level}{loop_bound_name} -= uint2({loop_bound_name}.y == 0u, 1u);"
195        );
196
197        Some((decl, break_and_inc))
198    }
199
200    /// Helper method used to find which expressions of a given function require baking
201    ///
202    /// # Notes
203    /// Clears `need_bake_expressions` set before adding to it
204    fn update_expressions_to_bake(
205        &mut self,
206        module: &Module,
207        func: &crate::Function,
208        info: &valid::FunctionInfo,
209    ) {
210        use crate::Expression;
211        self.need_bake_expressions.clear();
212        for (exp_handle, expr) in func.expressions.iter() {
213            let expr_info = &info[exp_handle];
214            let min_ref_count = func.expressions[exp_handle].bake_ref_count();
215            if min_ref_count <= expr_info.ref_count {
216                self.need_bake_expressions.insert(exp_handle);
217            }
218
219            if let Expression::Math { fun, arg, arg1, .. } = *expr {
220                match fun {
221                    crate::MathFunction::Asinh
222                    | crate::MathFunction::Acosh
223                    | crate::MathFunction::Atanh
224                    | crate::MathFunction::Unpack2x16float
225                    | crate::MathFunction::Unpack2x16snorm
226                    | crate::MathFunction::Unpack2x16unorm
227                    | crate::MathFunction::Unpack4x8snorm
228                    | crate::MathFunction::Unpack4x8unorm
229                    | crate::MathFunction::Unpack4xI8
230                    | crate::MathFunction::Unpack4xU8
231                    | crate::MathFunction::Pack2x16float
232                    | crate::MathFunction::Pack2x16snorm
233                    | crate::MathFunction::Pack2x16unorm
234                    | crate::MathFunction::Pack4x8snorm
235                    | crate::MathFunction::Pack4x8unorm
236                    | crate::MathFunction::Pack4xI8
237                    | crate::MathFunction::Pack4xU8
238                    | crate::MathFunction::Pack4xI8Clamp
239                    | crate::MathFunction::Pack4xU8Clamp => {
240                        self.need_bake_expressions.insert(arg);
241                    }
242                    crate::MathFunction::CountLeadingZeros => {
243                        let inner = info[exp_handle].ty.inner_with(&module.types);
244                        if let Some(ScalarKind::Sint) = inner.scalar_kind() {
245                            self.need_bake_expressions.insert(arg);
246                        }
247                    }
248                    crate::MathFunction::Dot4U8Packed | crate::MathFunction::Dot4I8Packed => {
249                        self.need_bake_expressions.insert(arg);
250                        self.need_bake_expressions.insert(arg1.unwrap());
251                    }
252                    _ => {}
253                }
254            }
255
256            if let Expression::Derivative { axis, ctrl, expr } = *expr {
257                use crate::{DerivativeAxis as Axis, DerivativeControl as Ctrl};
258                if axis == Axis::Width && (ctrl == Ctrl::Coarse || ctrl == Ctrl::Fine) {
259                    self.need_bake_expressions.insert(expr);
260                }
261            }
262
263            if let Expression::GlobalVariable(_) = *expr {
264                let inner = info[exp_handle].ty.inner_with(&module.types);
265
266                if let TypeInner::Sampler { .. } = *inner {
267                    self.need_bake_expressions.insert(exp_handle);
268                }
269            }
270        }
271        for statement in func.body.iter() {
272            match *statement {
273                crate::Statement::SubgroupCollectiveOperation {
274                    op: _,
275                    collective_op: crate::CollectiveOperation::InclusiveScan,
276                    argument,
277                    result: _,
278                } => {
279                    self.need_bake_expressions.insert(argument);
280                }
281                crate::Statement::Atomic {
282                    fun: crate::AtomicFunction::Exchange { compare: Some(cmp) },
283                    ..
284                } => {
285                    self.need_bake_expressions.insert(cmp);
286                }
287                _ => {}
288            }
289        }
290    }
291
292    pub fn write(
293        &mut self,
294        module: &Module,
295        module_info: &valid::ModuleInfo,
296        fragment_entry_point: Option<&FragmentEntryPoint<'_>>,
297    ) -> Result<super::ReflectionInfo, Error> {
298        self.reset(module);
299
300        // Write special constants, if needed
301        if let Some(ref bt) = self.options.special_constants_binding {
302            writeln!(self.out, "struct {SPECIAL_CBUF_TYPE} {{")?;
303            writeln!(self.out, "{}int {};", back::INDENT, SPECIAL_FIRST_VERTEX)?;
304            writeln!(self.out, "{}int {};", back::INDENT, SPECIAL_FIRST_INSTANCE)?;
305            writeln!(self.out, "{}uint {};", back::INDENT, SPECIAL_OTHER)?;
306            writeln!(self.out, "}};")?;
307            write!(
308                self.out,
309                "ConstantBuffer<{}> {}: register(b{}",
310                SPECIAL_CBUF_TYPE, SPECIAL_CBUF_VAR, bt.register
311            )?;
312            if bt.space != 0 {
313                write!(self.out, ", space{}", bt.space)?;
314            }
315            writeln!(self.out, ");")?;
316
317            // Extra newline for readability
318            writeln!(self.out)?;
319        }
320
321        for (group, bt) in self.options.dynamic_storage_buffer_offsets_targets.iter() {
322            writeln!(self.out, "struct __dynamic_buffer_offsetsTy{group} {{")?;
323            for i in 0..bt.size {
324                writeln!(self.out, "{}uint _{};", back::INDENT, i)?;
325            }
326            writeln!(self.out, "}};")?;
327            writeln!(
328                self.out,
329                "ConstantBuffer<__dynamic_buffer_offsetsTy{}> __dynamic_buffer_offsets{}: register(b{}, space{});",
330                group, group, bt.register, bt.space
331            )?;
332
333            // Extra newline for readability
334            writeln!(self.out)?;
335        }
336
337        // Save all entry point output types
338        let ep_results = module
339            .entry_points
340            .iter()
341            .map(|ep| (ep.stage, ep.function.result.clone()))
342            .collect::<Vec<(ShaderStage, Option<crate::FunctionResult>)>>();
343
344        self.write_all_mat_cx2_typedefs_and_functions(module)?;
345
346        // Write all structs
347        for (handle, ty) in module.types.iter() {
348            if let TypeInner::Struct { ref members, span } = ty.inner {
349                if module.types[members.last().unwrap().ty]
350                    .inner
351                    .is_dynamically_sized(&module.types)
352                {
353                    // unsized arrays can only be in storage buffers,
354                    // for which we use `ByteAddressBuffer` anyway.
355                    continue;
356                }
357
358                let ep_result = ep_results.iter().find(|e| {
359                    if let Some(ref result) = e.1 {
360                        result.ty == handle
361                    } else {
362                        false
363                    }
364                });
365
366                self.write_struct(
367                    module,
368                    handle,
369                    members,
370                    span,
371                    ep_result.map(|r| (r.0, Io::Output)),
372                )?;
373                writeln!(self.out)?;
374            }
375        }
376
377        self.write_special_functions(module)?;
378
379        self.write_wrapped_expression_functions(module, &module.global_expressions, None)?;
380        self.write_wrapped_zero_value_functions(module, &module.global_expressions)?;
381
382        // Write all named constants
383        let mut constants = module
384            .constants
385            .iter()
386            .filter(|&(_, c)| c.name.is_some())
387            .peekable();
388        while let Some((handle, _)) = constants.next() {
389            self.write_global_constant(module, handle)?;
390            // Add extra newline for readability on last iteration
391            if constants.peek().is_none() {
392                writeln!(self.out)?;
393            }
394        }
395
396        // Write all globals
397        for (global, _) in module.global_variables.iter() {
398            self.write_global(module, global)?;
399        }
400
401        if !module.global_variables.is_empty() {
402            // Add extra newline for readability
403            writeln!(self.out)?;
404        }
405
406        let ep_range = get_entry_points(module, self.pipeline_options.entry_point.as_ref())
407            .map_err(|(stage, name)| Error::EntryPointNotFound(stage, name))?;
408
409        // Write all entry points wrapped structs
410        for index in ep_range.clone() {
411            let ep = &module.entry_points[index];
412            let ep_name = self.names[&NameKey::EntryPoint(index as u16)].clone();
413            let ep_io = self.write_ep_interface(
414                module,
415                &ep.function,
416                ep.stage,
417                &ep_name,
418                fragment_entry_point,
419            )?;
420            self.entry_point_io.insert(index, ep_io);
421        }
422
423        // Write all regular functions
424        for (handle, function) in module.functions.iter() {
425            let info = &module_info[handle];
426
427            // Check if all of the globals are accessible
428            if !self.options.fake_missing_bindings {
429                if let Some((var_handle, _)) =
430                    module
431                        .global_variables
432                        .iter()
433                        .find(|&(var_handle, var)| match var.binding {
434                            Some(ref binding) if !info[var_handle].is_empty() => {
435                                self.options.resolve_resource_binding(binding).is_err()
436                                    && self
437                                        .options
438                                        .resolve_external_texture_resource_binding(binding)
439                                        .is_err()
440                            }
441                            _ => false,
442                        })
443                {
444                    log::debug!(
445                        "Skipping function {:?} (name {:?}) because global {:?} is inaccessible",
446                        handle,
447                        function.name,
448                        var_handle
449                    );
450                    continue;
451                }
452            }
453
454            let ctx = back::FunctionCtx {
455                ty: back::FunctionType::Function(handle),
456                info,
457                expressions: &function.expressions,
458                named_expressions: &function.named_expressions,
459            };
460            let name = self.names[&NameKey::Function(handle)].clone();
461
462            self.write_wrapped_functions(module, &ctx)?;
463
464            self.write_function(module, name.as_str(), function, &ctx, info)?;
465
466            writeln!(self.out)?;
467        }
468
469        let mut translated_ep_names = Vec::with_capacity(ep_range.len());
470
471        // Write all entry points
472        for index in ep_range {
473            let ep = &module.entry_points[index];
474            let info = module_info.get_entry_point(index);
475
476            if !self.options.fake_missing_bindings {
477                let mut ep_error = None;
478                for (var_handle, var) in module.global_variables.iter() {
479                    match var.binding {
480                        Some(ref binding) if !info[var_handle].is_empty() => {
481                            if let Err(err) = self.options.resolve_resource_binding(binding) {
482                                if self
483                                    .options
484                                    .resolve_external_texture_resource_binding(binding)
485                                    .is_err()
486                                {
487                                    ep_error = Some(err);
488                                    break;
489                                }
490                            }
491                        }
492                        _ => {}
493                    }
494                }
495                if let Some(err) = ep_error {
496                    translated_ep_names.push(Err(err));
497                    continue;
498                }
499            }
500
501            let ctx = back::FunctionCtx {
502                ty: back::FunctionType::EntryPoint(index as u16),
503                info,
504                expressions: &ep.function.expressions,
505                named_expressions: &ep.function.named_expressions,
506            };
507
508            self.write_wrapped_functions(module, &ctx)?;
509
510            if ep.stage.compute_like() {
511                // HLSL is calling workgroup size "num threads"
512                let num_threads = ep.workgroup_size;
513                writeln!(
514                    self.out,
515                    "[numthreads({}, {}, {})]",
516                    num_threads[0], num_threads[1], num_threads[2]
517                )?;
518            }
519
520            let name = self.names[&NameKey::EntryPoint(index as u16)].clone();
521            self.write_function(module, &name, &ep.function, &ctx, info)?;
522
523            if index < module.entry_points.len() - 1 {
524                writeln!(self.out)?;
525            }
526
527            translated_ep_names.push(Ok(name));
528        }
529
530        Ok(super::ReflectionInfo {
531            entry_point_names: translated_ep_names,
532        })
533    }
534
535    fn write_modifier(&mut self, binding: &crate::Binding) -> BackendResult {
536        match *binding {
537            crate::Binding::BuiltIn(crate::BuiltIn::Position { invariant: true }) => {
538                write!(self.out, "precise ")?;
539            }
540            crate::Binding::Location {
541                interpolation,
542                sampling,
543                ..
544            } => {
545                if let Some(interpolation) = interpolation {
546                    if let Some(string) = interpolation.to_hlsl_str() {
547                        write!(self.out, "{string} ")?
548                    }
549                }
550
551                if let Some(sampling) = sampling {
552                    if let Some(string) = sampling.to_hlsl_str() {
553                        write!(self.out, "{string} ")?
554                    }
555                }
556            }
557            crate::Binding::BuiltIn(_) => {}
558        }
559
560        Ok(())
561    }
562
563    //TODO: we could force fragment outputs to always go through `entry_point_io.output` path
564    // if they are struct, so that the `stage` argument here could be omitted.
565    fn write_semantic(
566        &mut self,
567        binding: &Option<crate::Binding>,
568        stage: Option<(ShaderStage, Io)>,
569    ) -> BackendResult {
570        match *binding {
571            Some(crate::Binding::BuiltIn(builtin)) if !is_subgroup_builtin_binding(binding) => {
572                if builtin == crate::BuiltIn::ViewIndex
573                    && self.options.shader_model < ShaderModel::V6_1
574                {
575                    return Err(Error::ShaderModelTooLow(
576                        "used @builtin(view_index) or SV_ViewID".to_string(),
577                        ShaderModel::V6_1,
578                    ));
579                }
580                let builtin_str = builtin.to_hlsl_str()?;
581                write!(self.out, " : {builtin_str}")?;
582            }
583            Some(crate::Binding::Location {
584                blend_src: Some(1), ..
585            }) => {
586                write!(self.out, " : SV_Target1")?;
587            }
588            Some(crate::Binding::Location { location, .. }) => {
589                if stage == Some((ShaderStage::Fragment, Io::Output)) {
590                    write!(self.out, " : SV_Target{location}")?;
591                } else {
592                    write!(self.out, " : {LOCATION_SEMANTIC}{location}")?;
593                }
594            }
595            _ => {}
596        }
597
598        Ok(())
599    }
600
601    fn write_interface_struct(
602        &mut self,
603        module: &Module,
604        shader_stage: (ShaderStage, Io),
605        struct_name: String,
606        mut members: Vec<EpStructMember>,
607    ) -> Result<EntryPointBinding, Error> {
608        // Sort the members so that first come the user-defined varyings
609        // in ascending locations, and then built-ins. This allows VS and FS
610        // interfaces to match with regards to order.
611        members.sort_by_key(|m| InterfaceKey::new(m.binding.as_ref()));
612
613        write!(self.out, "struct {struct_name}")?;
614        writeln!(self.out, " {{")?;
615        for m in members.iter() {
616            // Sanity check that each IO member is a built-in or is assigned a
617            // location. Also see note about nesting in `write_ep_input_struct`.
618            debug_assert!(m.binding.is_some());
619
620            if is_subgroup_builtin_binding(&m.binding) {
621                continue;
622            }
623            write!(self.out, "{}", back::INDENT)?;
624            if let Some(ref binding) = m.binding {
625                self.write_modifier(binding)?;
626            }
627            self.write_type(module, m.ty)?;
628            write!(self.out, " {}", &m.name)?;
629            self.write_semantic(&m.binding, Some(shader_stage))?;
630            writeln!(self.out, ";")?;
631        }
632        if members.iter().any(|arg| {
633            matches!(
634                arg.binding,
635                Some(crate::Binding::BuiltIn(crate::BuiltIn::SubgroupId))
636            )
637        }) {
638            writeln!(
639                self.out,
640                "{}uint __local_invocation_index : SV_GroupIndex;",
641                back::INDENT
642            )?;
643        }
644        writeln!(self.out, "}};")?;
645        writeln!(self.out)?;
646
647        // See ordering notes on EntryPointInterface fields
648        match shader_stage.1 {
649            Io::Input => {
650                // bring back the original order
651                members.sort_by_key(|m| m.index);
652            }
653            Io::Output => {
654                // keep it sorted by binding
655            }
656        }
657
658        Ok(EntryPointBinding {
659            arg_name: self.namer.call(struct_name.to_lowercase().as_str()),
660            ty_name: struct_name,
661            members,
662        })
663    }
664
665    /// Flatten all entry point arguments into a single struct.
666    /// This is needed since we need to re-order them: first placing user locations,
667    /// then built-ins.
668    fn write_ep_input_struct(
669        &mut self,
670        module: &Module,
671        func: &crate::Function,
672        stage: ShaderStage,
673        entry_point_name: &str,
674    ) -> Result<EntryPointBinding, Error> {
675        let struct_name = format!("{stage:?}Input_{entry_point_name}");
676
677        let mut fake_members = Vec::new();
678        for arg in func.arguments.iter() {
679            // NOTE: We don't need to handle nesting structs. All members must
680            // be either built-ins or assigned a location. I.E. `binding` is
681            // `Some`. This is checked in `VaryingContext::validate`. See:
682            // https://gpuweb.github.io/gpuweb/wgsl/#input-output-locations
683            match module.types[arg.ty].inner {
684                TypeInner::Struct { ref members, .. } => {
685                    for member in members.iter() {
686                        let name = self.namer.call_or(&member.name, "member");
687                        let index = fake_members.len() as u32;
688                        fake_members.push(EpStructMember {
689                            name,
690                            ty: member.ty,
691                            binding: member.binding.clone(),
692                            index,
693                        });
694                    }
695                }
696                _ => {
697                    let member_name = self.namer.call_or(&arg.name, "member");
698                    let index = fake_members.len() as u32;
699                    fake_members.push(EpStructMember {
700                        name: member_name,
701                        ty: arg.ty,
702                        binding: arg.binding.clone(),
703                        index,
704                    });
705                }
706            }
707        }
708
709        self.write_interface_struct(module, (stage, Io::Input), struct_name, fake_members)
710    }
711
712    /// Flatten all entry point results into a single struct.
713    /// This is needed since we need to re-order them: first placing user locations,
714    /// then built-ins.
715    fn write_ep_output_struct(
716        &mut self,
717        module: &Module,
718        result: &crate::FunctionResult,
719        stage: ShaderStage,
720        entry_point_name: &str,
721        frag_ep: Option<&FragmentEntryPoint<'_>>,
722    ) -> Result<EntryPointBinding, Error> {
723        let struct_name = format!("{stage:?}Output_{entry_point_name}");
724
725        let empty = [];
726        let members = match module.types[result.ty].inner {
727            TypeInner::Struct { ref members, .. } => members,
728            ref other => {
729                log::error!("Unexpected {other:?} output type without a binding");
730                &empty[..]
731            }
732        };
733
734        // Gather list of fragment input locations. We use this below to remove user-defined
735        // varyings from VS outputs that aren't in the FS inputs. This makes the VS interface match
736        // as long as the FS inputs are a subset of the VS outputs. This is only applied if the
737        // writer is supplied with information about the fragment entry point.
738        let fs_input_locs = if let (Some(frag_ep), ShaderStage::Vertex) = (frag_ep, stage) {
739            let mut fs_input_locs = Vec::new();
740            for arg in frag_ep.func.arguments.iter() {
741                let mut push_if_location = |binding: &Option<crate::Binding>| match *binding {
742                    Some(crate::Binding::Location { location, .. }) => fs_input_locs.push(location),
743                    Some(crate::Binding::BuiltIn(_)) | None => {}
744                };
745
746                // NOTE: We don't need to handle struct nesting. See note in
747                // `write_ep_input_struct`.
748                match frag_ep.module.types[arg.ty].inner {
749                    TypeInner::Struct { ref members, .. } => {
750                        for member in members.iter() {
751                            push_if_location(&member.binding);
752                        }
753                    }
754                    _ => push_if_location(&arg.binding),
755                }
756            }
757            fs_input_locs.sort();
758            Some(fs_input_locs)
759        } else {
760            None
761        };
762
763        let mut fake_members = Vec::new();
764        for (index, member) in members.iter().enumerate() {
765            if let Some(ref fs_input_locs) = fs_input_locs {
766                match member.binding {
767                    Some(crate::Binding::Location { location, .. }) => {
768                        if fs_input_locs.binary_search(&location).is_err() {
769                            continue;
770                        }
771                    }
772                    Some(crate::Binding::BuiltIn(_)) | None => {}
773                }
774            }
775
776            let member_name = self.namer.call_or(&member.name, "member");
777            fake_members.push(EpStructMember {
778                name: member_name,
779                ty: member.ty,
780                binding: member.binding.clone(),
781                index: index as u32,
782            });
783        }
784
785        self.write_interface_struct(module, (stage, Io::Output), struct_name, fake_members)
786    }
787
788    /// Writes special interface structures for an entry point. The special structures have
789    /// all the fields flattened into them and sorted by binding. They are needed to emulate
790    /// subgroup built-ins and to make the interfaces between VS outputs and FS inputs match.
791    fn write_ep_interface(
792        &mut self,
793        module: &Module,
794        func: &crate::Function,
795        stage: ShaderStage,
796        ep_name: &str,
797        frag_ep: Option<&FragmentEntryPoint<'_>>,
798    ) -> Result<EntryPointInterface, Error> {
799        Ok(EntryPointInterface {
800            input: if !func.arguments.is_empty()
801                && (stage == ShaderStage::Fragment
802                    || func
803                        .arguments
804                        .iter()
805                        .any(|arg| is_subgroup_builtin_binding(&arg.binding)))
806            {
807                Some(self.write_ep_input_struct(module, func, stage, ep_name)?)
808            } else {
809                None
810            },
811            output: match func.result {
812                Some(ref fr) if fr.binding.is_none() && stage == ShaderStage::Vertex => {
813                    Some(self.write_ep_output_struct(module, fr, stage, ep_name, frag_ep)?)
814                }
815                _ => None,
816            },
817        })
818    }
819
820    fn write_ep_argument_initialization(
821        &mut self,
822        ep: &crate::EntryPoint,
823        ep_input: &EntryPointBinding,
824        fake_member: &EpStructMember,
825    ) -> BackendResult {
826        match fake_member.binding {
827            Some(crate::Binding::BuiltIn(crate::BuiltIn::SubgroupSize)) => {
828                write!(self.out, "WaveGetLaneCount()")?
829            }
830            Some(crate::Binding::BuiltIn(crate::BuiltIn::SubgroupInvocationId)) => {
831                write!(self.out, "WaveGetLaneIndex()")?
832            }
833            Some(crate::Binding::BuiltIn(crate::BuiltIn::NumSubgroups)) => write!(
834                self.out,
835                "({}u + WaveGetLaneCount() - 1u) / WaveGetLaneCount()",
836                ep.workgroup_size[0] * ep.workgroup_size[1] * ep.workgroup_size[2]
837            )?,
838            Some(crate::Binding::BuiltIn(crate::BuiltIn::SubgroupId)) => {
839                write!(
840                    self.out,
841                    "{}.__local_invocation_index / WaveGetLaneCount()",
842                    ep_input.arg_name
843                )?;
844            }
845            _ => {
846                write!(self.out, "{}.{}", ep_input.arg_name, fake_member.name)?;
847            }
848        }
849        Ok(())
850    }
851
852    /// Write an entry point preface that initializes the arguments as specified in IR.
853    fn write_ep_arguments_initialization(
854        &mut self,
855        module: &Module,
856        func: &crate::Function,
857        ep_index: u16,
858    ) -> BackendResult {
859        let ep = &module.entry_points[ep_index as usize];
860        let ep_input = match self
861            .entry_point_io
862            .get_mut(&(ep_index as usize))
863            .unwrap()
864            .input
865            .take()
866        {
867            Some(ep_input) => ep_input,
868            None => return Ok(()),
869        };
870        let mut fake_iter = ep_input.members.iter();
871        for (arg_index, arg) in func.arguments.iter().enumerate() {
872            write!(self.out, "{}", back::INDENT)?;
873            self.write_type(module, arg.ty)?;
874            let arg_name = &self.names[&NameKey::EntryPointArgument(ep_index, arg_index as u32)];
875            write!(self.out, " {arg_name}")?;
876            match module.types[arg.ty].inner {
877                TypeInner::Array { base, size, .. } => {
878                    self.write_array_size(module, base, size)?;
879                    write!(self.out, " = ")?;
880                    self.write_ep_argument_initialization(
881                        ep,
882                        &ep_input,
883                        fake_iter.next().unwrap(),
884                    )?;
885                    writeln!(self.out, ";")?;
886                }
887                TypeInner::Struct { ref members, .. } => {
888                    write!(self.out, " = {{ ")?;
889                    for index in 0..members.len() {
890                        if index != 0 {
891                            write!(self.out, ", ")?;
892                        }
893                        self.write_ep_argument_initialization(
894                            ep,
895                            &ep_input,
896                            fake_iter.next().unwrap(),
897                        )?;
898                    }
899                    writeln!(self.out, " }};")?;
900                }
901                _ => {
902                    write!(self.out, " = ")?;
903                    self.write_ep_argument_initialization(
904                        ep,
905                        &ep_input,
906                        fake_iter.next().unwrap(),
907                    )?;
908                    writeln!(self.out, ";")?;
909                }
910            }
911        }
912        assert!(fake_iter.next().is_none());
913        Ok(())
914    }
915
916    /// Helper method used to write global variables
917    /// # Notes
918    /// Always adds a newline
919    fn write_global(
920        &mut self,
921        module: &Module,
922        handle: Handle<crate::GlobalVariable>,
923    ) -> BackendResult {
924        let global = &module.global_variables[handle];
925        let inner = &module.types[global.ty].inner;
926
927        let handle_ty = match *inner {
928            TypeInner::BindingArray { ref base, .. } => &module.types[*base].inner,
929            _ => inner,
930        };
931
932        // External textures are handled entirely differently, so defer entirely to that method.
933        // We do so prior to calling resolve_resource_binding() below, as we even need to resolve
934        // their bindings separately.
935        let is_external_texture = matches!(
936            *handle_ty,
937            TypeInner::Image {
938                class: crate::ImageClass::External,
939                ..
940            }
941        );
942        if is_external_texture {
943            return self.write_global_external_texture(module, handle, global);
944        }
945
946        if let Some(ref binding) = global.binding {
947            if let Err(err) = self.options.resolve_resource_binding(binding) {
948                log::debug!(
949                    "Skipping global {:?} (name {:?}) for being inaccessible: {}",
950                    handle,
951                    global.name,
952                    err,
953                );
954                return Ok(());
955            }
956        }
957
958        // Samplers are handled entirely differently, so defer entirely to that method.
959        let is_sampler = matches!(*handle_ty, TypeInner::Sampler { .. });
960
961        if is_sampler {
962            return self.write_global_sampler(module, handle, global);
963        }
964
965        // https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-variable-register
966        let register_ty = match global.space {
967            crate::AddressSpace::Function => unreachable!("Function address space"),
968            crate::AddressSpace::Private => {
969                write!(self.out, "static ")?;
970                self.write_type(module, global.ty)?;
971                ""
972            }
973            crate::AddressSpace::WorkGroup => {
974                write!(self.out, "groupshared ")?;
975                self.write_type(module, global.ty)?;
976                ""
977            }
978            crate::AddressSpace::TaskPayload => unimplemented!(),
979            crate::AddressSpace::Uniform => {
980                // constant buffer declarations are expected to be inlined, e.g.
981                // `cbuffer foo: register(b0) { field1: type1; }`
982                write!(self.out, "cbuffer")?;
983                "b"
984            }
985            crate::AddressSpace::Storage { access } => {
986                let (prefix, register) = if access.contains(crate::StorageAccess::STORE) {
987                    ("RW", "u")
988                } else {
989                    ("", "t")
990                };
991                write!(self.out, "{prefix}ByteAddressBuffer")?;
992                register
993            }
994            crate::AddressSpace::Handle => {
995                let register = match *handle_ty {
996                    // all storage textures are UAV, unconditionally
997                    TypeInner::Image {
998                        class: crate::ImageClass::Storage { .. },
999                        ..
1000                    } => "u",
1001                    _ => "t",
1002                };
1003                self.write_type(module, global.ty)?;
1004                register
1005            }
1006            crate::AddressSpace::Immediate => {
1007                // The type of the immediates will be wrapped in `ConstantBuffer`
1008                write!(self.out, "ConstantBuffer<")?;
1009                "b"
1010            }
1011        };
1012
1013        // If the global is a immediate data write the type now because it will be a
1014        // generic argument to `ConstantBuffer`
1015        if global.space == crate::AddressSpace::Immediate {
1016            self.write_global_type(module, global.ty)?;
1017
1018            // need to write the array size if the type was emitted with `write_type`
1019            if let TypeInner::Array { base, size, .. } = module.types[global.ty].inner {
1020                self.write_array_size(module, base, size)?;
1021            }
1022
1023            // Close the angled brackets for the generic argument
1024            write!(self.out, ">")?;
1025        }
1026
1027        let name = &self.names[&NameKey::GlobalVariable(handle)];
1028        write!(self.out, " {name}")?;
1029
1030        // Immediates need to be assigned a binding explicitly by the consumer
1031        // since naga has no way to know the binding from the shader alone
1032        if global.space == crate::AddressSpace::Immediate {
1033            match module.types[global.ty].inner {
1034                TypeInner::Struct { .. } => {}
1035                _ => {
1036                    return Err(Error::Unimplemented(format!(
1037                        "push-constant '{name}' has non-struct type; tracked by: https://github.com/gfx-rs/wgpu/issues/5683"
1038                    )));
1039                }
1040            }
1041
1042            let target = self
1043                .options
1044                .immediates_target
1045                .as_ref()
1046                .expect("No bind target was defined for the immediates block");
1047            write!(self.out, ": register(b{}", target.register)?;
1048            if target.space != 0 {
1049                write!(self.out, ", space{}", target.space)?;
1050            }
1051            write!(self.out, ")")?;
1052        }
1053
1054        if let Some(ref binding) = global.binding {
1055            // this was already resolved earlier when we started evaluating an entry point.
1056            let bt = self.options.resolve_resource_binding(binding).unwrap();
1057
1058            // need to write the binding array size if the type was emitted with `write_type`
1059            if let TypeInner::BindingArray { base, size, .. } = module.types[global.ty].inner {
1060                if let Some(overridden_size) = bt.binding_array_size {
1061                    write!(self.out, "[{overridden_size}]")?;
1062                } else {
1063                    self.write_array_size(module, base, size)?;
1064                }
1065            }
1066
1067            write!(self.out, " : register({}{}", register_ty, bt.register)?;
1068            if bt.space != 0 {
1069                write!(self.out, ", space{}", bt.space)?;
1070            }
1071            write!(self.out, ")")?;
1072        } else {
1073            // need to write the array size if the type was emitted with `write_type`
1074            if let TypeInner::Array { base, size, .. } = module.types[global.ty].inner {
1075                self.write_array_size(module, base, size)?;
1076            }
1077            if global.space == crate::AddressSpace::Private {
1078                write!(self.out, " = ")?;
1079                if let Some(init) = global.init {
1080                    self.write_const_expression(module, init, &module.global_expressions)?;
1081                } else {
1082                    self.write_default_init(module, global.ty)?;
1083                }
1084            }
1085        }
1086
1087        if global.space == crate::AddressSpace::Uniform {
1088            write!(self.out, " {{ ")?;
1089
1090            self.write_global_type(module, global.ty)?;
1091
1092            write!(
1093                self.out,
1094                " {}",
1095                &self.names[&NameKey::GlobalVariable(handle)]
1096            )?;
1097
1098            // need to write the array size if the type was emitted with `write_type`
1099            if let TypeInner::Array { base, size, .. } = module.types[global.ty].inner {
1100                self.write_array_size(module, base, size)?;
1101            }
1102
1103            writeln!(self.out, "; }}")?;
1104        } else {
1105            writeln!(self.out, ";")?;
1106        }
1107
1108        Ok(())
1109    }
1110
1111    fn write_global_sampler(
1112        &mut self,
1113        module: &Module,
1114        handle: Handle<crate::GlobalVariable>,
1115        global: &crate::GlobalVariable,
1116    ) -> BackendResult {
1117        let binding = *global.binding.as_ref().unwrap();
1118
1119        let key = super::SamplerIndexBufferKey {
1120            group: binding.group,
1121        };
1122        self.write_wrapped_sampler_buffer(key)?;
1123
1124        // This was already validated, so we can confidently unwrap it.
1125        let bt = self.options.resolve_resource_binding(&binding).unwrap();
1126
1127        match module.types[global.ty].inner {
1128            TypeInner::Sampler { comparison } => {
1129                // If we are generating a static access, we create a variable for the sampler.
1130                //
1131                // This prevents the DXIL from containing multiple lookups for the sampler, which
1132                // the backend compiler will then have to eliminate. AMD does seem to be able to
1133                // eliminate these, but better safe than sorry.
1134
1135                write!(self.out, "static const ")?;
1136                self.write_type(module, global.ty)?;
1137
1138                let heap_var = if comparison {
1139                    COMPARISON_SAMPLER_HEAP_VAR
1140                } else {
1141                    SAMPLER_HEAP_VAR
1142                };
1143
1144                let index_buffer_name = &self.wrapped.sampler_index_buffers[&key];
1145                let name = &self.names[&NameKey::GlobalVariable(handle)];
1146                writeln!(
1147                    self.out,
1148                    " {name} = {heap_var}[{index_buffer_name}[{register}]];",
1149                    register = bt.register
1150                )?;
1151            }
1152            TypeInner::BindingArray { .. } => {
1153                // If we are generating a binding array, we cannot directly access the sampler as the index
1154                // into the sampler index buffer is unknown at compile time. Instead we generate a constant
1155                // that represents the "base" index into the sampler index buffer. This constant is added
1156                // to the user provided index to get the final index into the sampler index buffer.
1157
1158                let name = &self.names[&NameKey::GlobalVariable(handle)];
1159                writeln!(
1160                    self.out,
1161                    "static const uint {name} = {register};",
1162                    register = bt.register
1163                )?;
1164            }
1165            _ => unreachable!(),
1166        };
1167
1168        Ok(())
1169    }
1170
1171    /// Write the declarations for an external texture global variable.
1172    /// These are emitted as multiple global variables: Three `Texture2D`s
1173    /// (one for each plane) and a parameters cbuffer.
1174    fn write_global_external_texture(
1175        &mut self,
1176        module: &Module,
1177        handle: Handle<crate::GlobalVariable>,
1178        global: &crate::GlobalVariable,
1179    ) -> BackendResult {
1180        let res_binding = global
1181            .binding
1182            .as_ref()
1183            .expect("External texture global variables must have a resource binding");
1184        let ext_tex_bindings = match self
1185            .options
1186            .resolve_external_texture_resource_binding(res_binding)
1187        {
1188            Ok(bindings) => bindings,
1189            Err(err) => {
1190                log::debug!(
1191                    "Skipping global {:?} (name {:?}) for being inaccessible: {}",
1192                    handle,
1193                    global.name,
1194                    err,
1195                );
1196                return Ok(());
1197            }
1198        };
1199
1200        let mut write_plane = |bt: &super::BindTarget, name| -> BackendResult {
1201            write!(
1202                self.out,
1203                "Texture2D<float4> {}: register(t{}",
1204                name, bt.register
1205            )?;
1206            if bt.space != 0 {
1207                write!(self.out, ", space{}", bt.space)?;
1208            }
1209            writeln!(self.out, ");")?;
1210            Ok(())
1211        };
1212        for (i, bt) in ext_tex_bindings.planes.iter().enumerate() {
1213            let plane_name = &self.names
1214                [&NameKey::ExternalTextureGlobalVariable(handle, ExternalTextureNameKey::Plane(i))];
1215            write_plane(bt, plane_name)?;
1216        }
1217
1218        let params_name = &self.names
1219            [&NameKey::ExternalTextureGlobalVariable(handle, ExternalTextureNameKey::Params)];
1220        let params_ty_name =
1221            &self.names[&NameKey::Type(module.special_types.external_texture_params.unwrap())];
1222        write!(
1223            self.out,
1224            "cbuffer {}: register(b{}",
1225            params_name, ext_tex_bindings.params.register
1226        )?;
1227        if ext_tex_bindings.params.space != 0 {
1228            write!(self.out, ", space{}", ext_tex_bindings.params.space)?;
1229        }
1230        writeln!(self.out, ") {{ {params_ty_name} {params_name}; }};")?;
1231
1232        Ok(())
1233    }
1234
1235    /// Helper method used to write global constants
1236    ///
1237    /// # Notes
1238    /// Ends in a newline
1239    fn write_global_constant(
1240        &mut self,
1241        module: &Module,
1242        handle: Handle<crate::Constant>,
1243    ) -> BackendResult {
1244        write!(self.out, "static const ")?;
1245        let constant = &module.constants[handle];
1246        self.write_type(module, constant.ty)?;
1247        let name = &self.names[&NameKey::Constant(handle)];
1248        write!(self.out, " {name}")?;
1249        // Write size for array type
1250        if let TypeInner::Array { base, size, .. } = module.types[constant.ty].inner {
1251            self.write_array_size(module, base, size)?;
1252        }
1253        write!(self.out, " = ")?;
1254        self.write_const_expression(module, constant.init, &module.global_expressions)?;
1255        writeln!(self.out, ";")?;
1256        Ok(())
1257    }
1258
1259    pub(super) fn write_array_size(
1260        &mut self,
1261        module: &Module,
1262        base: Handle<crate::Type>,
1263        size: crate::ArraySize,
1264    ) -> BackendResult {
1265        write!(self.out, "[")?;
1266
1267        match size.resolve(module.to_ctx())? {
1268            proc::IndexableLength::Known(size) => {
1269                write!(self.out, "{size}")?;
1270            }
1271            proc::IndexableLength::Dynamic => unreachable!(),
1272        }
1273
1274        write!(self.out, "]")?;
1275
1276        if let TypeInner::Array {
1277            base: next_base,
1278            size: next_size,
1279            ..
1280        } = module.types[base].inner
1281        {
1282            self.write_array_size(module, next_base, next_size)?;
1283        }
1284
1285        Ok(())
1286    }
1287
1288    /// Helper method used to write structs
1289    ///
1290    /// # Notes
1291    /// Ends in a newline
1292    fn write_struct(
1293        &mut self,
1294        module: &Module,
1295        handle: Handle<crate::Type>,
1296        members: &[crate::StructMember],
1297        span: u32,
1298        shader_stage: Option<(ShaderStage, Io)>,
1299    ) -> BackendResult {
1300        // Write struct name
1301        let struct_name = &self.names[&NameKey::Type(handle)];
1302        writeln!(self.out, "struct {struct_name} {{")?;
1303
1304        let mut last_offset = 0;
1305        for (index, member) in members.iter().enumerate() {
1306            if member.binding.is_none() && member.offset > last_offset {
1307                // using int as padding should work as long as the backend
1308                // doesn't support a type that's less than 4 bytes in size
1309                // (Error::UnsupportedScalar catches this)
1310                let padding = (member.offset - last_offset) / 4;
1311                for i in 0..padding {
1312                    writeln!(self.out, "{}int _pad{}_{};", back::INDENT, index, i)?;
1313                }
1314            }
1315            let ty_inner = &module.types[member.ty].inner;
1316            last_offset = member.offset + ty_inner.size_hlsl(module.to_ctx())?;
1317
1318            // The indentation is only for readability
1319            write!(self.out, "{}", back::INDENT)?;
1320
1321            match module.types[member.ty].inner {
1322                TypeInner::Array { base, size, .. } => {
1323                    // HLSL arrays are written as `type name[size]`
1324
1325                    self.write_global_type(module, member.ty)?;
1326
1327                    // Write `name`
1328                    write!(
1329                        self.out,
1330                        " {}",
1331                        &self.names[&NameKey::StructMember(handle, index as u32)]
1332                    )?;
1333                    // Write [size]
1334                    self.write_array_size(module, base, size)?;
1335                }
1336                // We treat matrices of the form `matCx2` as a sequence of C `vec2`s.
1337                // See the module-level block comment in mod.rs for details.
1338                TypeInner::Matrix {
1339                    rows,
1340                    columns,
1341                    scalar,
1342                } if member.binding.is_none() && rows == crate::VectorSize::Bi => {
1343                    let vec_ty = TypeInner::Vector { size: rows, scalar };
1344                    let field_name_key = NameKey::StructMember(handle, index as u32);
1345
1346                    for i in 0..columns as u8 {
1347                        if i != 0 {
1348                            write!(self.out, "; ")?;
1349                        }
1350                        self.write_value_type(module, &vec_ty)?;
1351                        write!(self.out, " {}_{}", &self.names[&field_name_key], i)?;
1352                    }
1353                }
1354                _ => {
1355                    // Write modifier before type
1356                    if let Some(ref binding) = member.binding {
1357                        self.write_modifier(binding)?;
1358                    }
1359
1360                    // Even though Naga IR matrices are column-major, we must describe
1361                    // matrices passed from the CPU as being in row-major order.
1362                    // See the module-level block comment in mod.rs for details.
1363                    if let TypeInner::Matrix { .. } = module.types[member.ty].inner {
1364                        write!(self.out, "row_major ")?;
1365                    }
1366
1367                    // Write the member type and name
1368                    self.write_type(module, member.ty)?;
1369                    write!(
1370                        self.out,
1371                        " {}",
1372                        &self.names[&NameKey::StructMember(handle, index as u32)]
1373                    )?;
1374                }
1375            }
1376
1377            self.write_semantic(&member.binding, shader_stage)?;
1378            writeln!(self.out, ";")?;
1379        }
1380
1381        // add padding at the end since sizes of types don't get rounded up to their alignment in HLSL
1382        if members.last().unwrap().binding.is_none() && span > last_offset {
1383            let padding = (span - last_offset) / 4;
1384            for i in 0..padding {
1385                writeln!(self.out, "{}int _end_pad_{};", back::INDENT, i)?;
1386            }
1387        }
1388
1389        writeln!(self.out, "}};")?;
1390        Ok(())
1391    }
1392
1393    /// Helper method used to write global/structs non image/sampler types
1394    ///
1395    /// # Notes
1396    /// Adds no trailing or leading whitespace
1397    pub(super) fn write_global_type(
1398        &mut self,
1399        module: &Module,
1400        ty: Handle<crate::Type>,
1401    ) -> BackendResult {
1402        let matrix_data = get_inner_matrix_data(module, ty);
1403
1404        // We treat matrices of the form `matCx2` as a sequence of C `vec2`s.
1405        // See the module-level block comment in mod.rs for details.
1406        if let Some(MatrixType {
1407            columns,
1408            rows: crate::VectorSize::Bi,
1409            width: 4,
1410        }) = matrix_data
1411        {
1412            write!(self.out, "__mat{}x2", columns as u8)?;
1413        } else {
1414            // Even though Naga IR matrices are column-major, we must describe
1415            // matrices passed from the CPU as being in row-major order.
1416            // See the module-level block comment in mod.rs for details.
1417            if matrix_data.is_some() {
1418                write!(self.out, "row_major ")?;
1419            }
1420
1421            self.write_type(module, ty)?;
1422        }
1423
1424        Ok(())
1425    }
1426
1427    /// Helper method used to write non image/sampler types
1428    ///
1429    /// # Notes
1430    /// Adds no trailing or leading whitespace
1431    pub(super) fn write_type(&mut self, module: &Module, ty: Handle<crate::Type>) -> BackendResult {
1432        let inner = &module.types[ty].inner;
1433        match *inner {
1434            TypeInner::Struct { .. } => write!(self.out, "{}", self.names[&NameKey::Type(ty)])?,
1435            // hlsl array has the size separated from the base type
1436            TypeInner::Array { base, .. } | TypeInner::BindingArray { base, .. } => {
1437                self.write_type(module, base)?
1438            }
1439            ref other => self.write_value_type(module, other)?,
1440        }
1441
1442        Ok(())
1443    }
1444
1445    /// Helper method used to write value types
1446    ///
1447    /// # Notes
1448    /// Adds no trailing or leading whitespace
1449    pub(super) fn write_value_type(&mut self, module: &Module, inner: &TypeInner) -> BackendResult {
1450        match *inner {
1451            TypeInner::Scalar(scalar) | TypeInner::Atomic(scalar) => {
1452                write!(self.out, "{}", scalar.to_hlsl_str()?)?;
1453            }
1454            TypeInner::Vector { size, scalar } => {
1455                write!(
1456                    self.out,
1457                    "{}{}",
1458                    scalar.to_hlsl_str()?,
1459                    common::vector_size_str(size)
1460                )?;
1461            }
1462            TypeInner::Matrix {
1463                columns,
1464                rows,
1465                scalar,
1466            } => {
1467                // The IR supports only float matrix
1468                // https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-matrix
1469
1470                // Because of the implicit transpose all matrices have in HLSL, we need to transpose the size as well.
1471                write!(
1472                    self.out,
1473                    "{}{}x{}",
1474                    scalar.to_hlsl_str()?,
1475                    common::vector_size_str(columns),
1476                    common::vector_size_str(rows),
1477                )?;
1478            }
1479            TypeInner::Image {
1480                dim,
1481                arrayed,
1482                class,
1483            } => {
1484                self.write_image_type(dim, arrayed, class)?;
1485            }
1486            TypeInner::Sampler { comparison } => {
1487                let sampler = if comparison {
1488                    "SamplerComparisonState"
1489                } else {
1490                    "SamplerState"
1491                };
1492                write!(self.out, "{sampler}")?;
1493            }
1494            // HLSL arrays are written as `type name[size]`
1495            // Current code is written arrays only as `[size]`
1496            // Base `type` and `name` should be written outside
1497            TypeInner::Array { base, size, .. } | TypeInner::BindingArray { base, size } => {
1498                self.write_array_size(module, base, size)?;
1499            }
1500            TypeInner::AccelerationStructure { .. } => {
1501                write!(self.out, "RaytracingAccelerationStructure")?;
1502            }
1503            TypeInner::RayQuery { .. } => {
1504                // these are constant flags, there are dynamic flags also but constant flags are not supported by naga
1505                write!(self.out, "RayQuery<RAY_FLAG_NONE>")?;
1506            }
1507            _ => return Err(Error::Unimplemented(format!("write_value_type {inner:?}"))),
1508        }
1509
1510        Ok(())
1511    }
1512
1513    /// Helper method used to write functions
1514    /// # Notes
1515    /// Ends in a newline
1516    fn write_function(
1517        &mut self,
1518        module: &Module,
1519        name: &str,
1520        func: &crate::Function,
1521        func_ctx: &back::FunctionCtx<'_>,
1522        info: &valid::FunctionInfo,
1523    ) -> BackendResult {
1524        // Function Declaration Syntax - https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-function-syntax
1525
1526        self.update_expressions_to_bake(module, func, info);
1527
1528        if let Some(ref result) = func.result {
1529            // Write typedef if return type is an array
1530            let array_return_type = match module.types[result.ty].inner {
1531                TypeInner::Array { base, size, .. } => {
1532                    let array_return_type = self.namer.call(&format!("ret_{name}"));
1533                    write!(self.out, "typedef ")?;
1534                    self.write_type(module, result.ty)?;
1535                    write!(self.out, " {array_return_type}")?;
1536                    self.write_array_size(module, base, size)?;
1537                    writeln!(self.out, ";")?;
1538                    Some(array_return_type)
1539                }
1540                _ => None,
1541            };
1542
1543            // Write modifier
1544            if let Some(
1545                ref binding @ crate::Binding::BuiltIn(crate::BuiltIn::Position { invariant: true }),
1546            ) = result.binding
1547            {
1548                self.write_modifier(binding)?;
1549            }
1550
1551            // Write return type
1552            match func_ctx.ty {
1553                back::FunctionType::Function(_) => {
1554                    if let Some(array_return_type) = array_return_type {
1555                        write!(self.out, "{array_return_type}")?;
1556                    } else {
1557                        self.write_type(module, result.ty)?;
1558                    }
1559                }
1560                back::FunctionType::EntryPoint(index) => {
1561                    if let Some(ref ep_output) =
1562                        self.entry_point_io.get(&(index as usize)).unwrap().output
1563                    {
1564                        write!(self.out, "{}", ep_output.ty_name)?;
1565                    } else {
1566                        self.write_type(module, result.ty)?;
1567                    }
1568                }
1569            }
1570        } else {
1571            write!(self.out, "void")?;
1572        }
1573
1574        // Write function name
1575        write!(self.out, " {name}(")?;
1576
1577        let need_workgroup_variables_initialization =
1578            self.need_workgroup_variables_initialization(func_ctx, module);
1579
1580        // Write function arguments for non entry point functions
1581        match func_ctx.ty {
1582            back::FunctionType::Function(handle) => {
1583                for (index, arg) in func.arguments.iter().enumerate() {
1584                    if index != 0 {
1585                        write!(self.out, ", ")?;
1586                    }
1587
1588                    self.write_function_argument(module, handle, arg, index)?;
1589                }
1590            }
1591            back::FunctionType::EntryPoint(ep_index) => {
1592                if let Some(ref ep_input) =
1593                    self.entry_point_io.get(&(ep_index as usize)).unwrap().input
1594                {
1595                    write!(self.out, "{} {}", ep_input.ty_name, ep_input.arg_name)?;
1596                } else {
1597                    let stage = module.entry_points[ep_index as usize].stage;
1598                    for (index, arg) in func.arguments.iter().enumerate() {
1599                        if index != 0 {
1600                            write!(self.out, ", ")?;
1601                        }
1602                        self.write_type(module, arg.ty)?;
1603
1604                        let argument_name =
1605                            &self.names[&NameKey::EntryPointArgument(ep_index, index as u32)];
1606
1607                        write!(self.out, " {argument_name}")?;
1608                        if let TypeInner::Array { base, size, .. } = module.types[arg.ty].inner {
1609                            self.write_array_size(module, base, size)?;
1610                        }
1611
1612                        self.write_semantic(&arg.binding, Some((stage, Io::Input)))?;
1613                    }
1614                }
1615                if need_workgroup_variables_initialization {
1616                    if self
1617                        .entry_point_io
1618                        .get(&(ep_index as usize))
1619                        .unwrap()
1620                        .input
1621                        .is_some()
1622                        || !func.arguments.is_empty()
1623                    {
1624                        write!(self.out, ", ")?;
1625                    }
1626                    write!(self.out, "uint3 __local_invocation_id : SV_GroupThreadID")?;
1627                }
1628            }
1629        }
1630        // Ends of arguments
1631        write!(self.out, ")")?;
1632
1633        // Write semantic if it present
1634        if let back::FunctionType::EntryPoint(index) = func_ctx.ty {
1635            let stage = module.entry_points[index as usize].stage;
1636            if let Some(crate::FunctionResult { ref binding, .. }) = func.result {
1637                self.write_semantic(binding, Some((stage, Io::Output)))?;
1638            }
1639        }
1640
1641        // Function body start
1642        writeln!(self.out)?;
1643        writeln!(self.out, "{{")?;
1644
1645        if need_workgroup_variables_initialization {
1646            self.write_workgroup_variables_initialization(func_ctx, module)?;
1647        }
1648
1649        if let back::FunctionType::EntryPoint(index) = func_ctx.ty {
1650            self.write_ep_arguments_initialization(module, func, index)?;
1651        }
1652
1653        // Write function local variables
1654        for (handle, local) in func.local_variables.iter() {
1655            // Write indentation (only for readability)
1656            write!(self.out, "{}", back::INDENT)?;
1657
1658            // Write the local name
1659            // The leading space is important
1660            self.write_type(module, local.ty)?;
1661            write!(self.out, " {}", self.names[&func_ctx.name_key(handle)])?;
1662            // Write size for array type
1663            if let TypeInner::Array { base, size, .. } = module.types[local.ty].inner {
1664                self.write_array_size(module, base, size)?;
1665            }
1666
1667            match module.types[local.ty].inner {
1668                // from https://microsoft.github.io/DirectX-Specs/d3d/Raytracing.html#tracerayinline-example-1 it seems that ray queries shouldn't be zeroed
1669                TypeInner::RayQuery { .. } => {}
1670                _ => {
1671                    write!(self.out, " = ")?;
1672                    // Write the local initializer if needed
1673                    if let Some(init) = local.init {
1674                        self.write_expr(module, init, func_ctx)?;
1675                    } else {
1676                        // Zero initialize local variables
1677                        self.write_default_init(module, local.ty)?;
1678                    }
1679                }
1680            }
1681            // Finish the local with `;` and add a newline (only for readability)
1682            writeln!(self.out, ";")?
1683        }
1684
1685        if !func.local_variables.is_empty() {
1686            writeln!(self.out)?;
1687        }
1688
1689        // Write the function body (statement list)
1690        for sta in func.body.iter() {
1691            // The indentation should always be 1 when writing the function body
1692            self.write_stmt(module, sta, func_ctx, back::Level(1))?;
1693        }
1694
1695        writeln!(self.out, "}}")?;
1696
1697        self.named_expressions.clear();
1698
1699        Ok(())
1700    }
1701
1702    fn write_function_argument(
1703        &mut self,
1704        module: &Module,
1705        handle: Handle<crate::Function>,
1706        arg: &crate::FunctionArgument,
1707        index: usize,
1708    ) -> BackendResult {
1709        // External texture arguments must be expanded into separate
1710        // arguments for each plane and the params buffer.
1711        if let TypeInner::Image {
1712            class: crate::ImageClass::External,
1713            ..
1714        } = module.types[arg.ty].inner
1715        {
1716            return self.write_function_external_texture_argument(module, handle, index);
1717        }
1718
1719        // Write argument type
1720        let arg_ty = match module.types[arg.ty].inner {
1721            // pointers in function arguments are expected and resolve to `inout`
1722            TypeInner::Pointer { base, .. } => {
1723                //TODO: can we narrow this down to just `in` when possible?
1724                write!(self.out, "inout ")?;
1725                base
1726            }
1727            _ => arg.ty,
1728        };
1729        self.write_type(module, arg_ty)?;
1730
1731        let argument_name = &self.names[&NameKey::FunctionArgument(handle, index as u32)];
1732
1733        // Write argument name. Space is important.
1734        write!(self.out, " {argument_name}")?;
1735        if let TypeInner::Array { base, size, .. } = module.types[arg_ty].inner {
1736            self.write_array_size(module, base, size)?;
1737        }
1738
1739        Ok(())
1740    }
1741
1742    fn write_function_external_texture_argument(
1743        &mut self,
1744        module: &Module,
1745        handle: Handle<crate::Function>,
1746        index: usize,
1747    ) -> BackendResult {
1748        let plane_names = [0, 1, 2].map(|i| {
1749            &self.names[&NameKey::ExternalTextureFunctionArgument(
1750                handle,
1751                index as u32,
1752                ExternalTextureNameKey::Plane(i),
1753            )]
1754        });
1755        let params_name = &self.names[&NameKey::ExternalTextureFunctionArgument(
1756            handle,
1757            index as u32,
1758            ExternalTextureNameKey::Params,
1759        )];
1760        let params_ty_name =
1761            &self.names[&NameKey::Type(module.special_types.external_texture_params.unwrap())];
1762        write!(
1763            self.out,
1764            "Texture2D<float4> {}, Texture2D<float4> {}, Texture2D<float4> {}, {params_ty_name} {params_name}",
1765            plane_names[0], plane_names[1], plane_names[2],
1766        )?;
1767        Ok(())
1768    }
1769
1770    fn need_workgroup_variables_initialization(
1771        &mut self,
1772        func_ctx: &back::FunctionCtx,
1773        module: &Module,
1774    ) -> bool {
1775        self.options.zero_initialize_workgroup_memory
1776            && func_ctx.ty.is_compute_like_entry_point(module)
1777            && module.global_variables.iter().any(|(handle, var)| {
1778                !func_ctx.info[handle].is_empty() && var.space == crate::AddressSpace::WorkGroup
1779            })
1780    }
1781
1782    fn write_workgroup_variables_initialization(
1783        &mut self,
1784        func_ctx: &back::FunctionCtx,
1785        module: &Module,
1786    ) -> BackendResult {
1787        let level = back::Level(1);
1788
1789        writeln!(
1790            self.out,
1791            "{level}if (all(__local_invocation_id == uint3(0u, 0u, 0u))) {{"
1792        )?;
1793
1794        let vars = module.global_variables.iter().filter(|&(handle, var)| {
1795            !func_ctx.info[handle].is_empty() && var.space == crate::AddressSpace::WorkGroup
1796        });
1797
1798        for (handle, var) in vars {
1799            let name = &self.names[&NameKey::GlobalVariable(handle)];
1800            write!(self.out, "{}{} = ", level.next(), name)?;
1801            self.write_default_init(module, var.ty)?;
1802            writeln!(self.out, ";")?;
1803        }
1804
1805        writeln!(self.out, "{level}}}")?;
1806        self.write_control_barrier(crate::Barrier::WORK_GROUP, level)
1807    }
1808
1809    /// Helper method used to write switches
1810    fn write_switch(
1811        &mut self,
1812        module: &Module,
1813        func_ctx: &back::FunctionCtx<'_>,
1814        level: back::Level,
1815        selector: Handle<crate::Expression>,
1816        cases: &[crate::SwitchCase],
1817    ) -> BackendResult {
1818        // Write all cases
1819        let indent_level_1 = level.next();
1820        let indent_level_2 = indent_level_1.next();
1821
1822        // See docs of `back::continue_forward` module.
1823        if let Some(variable) = self.continue_ctx.enter_switch(&mut self.namer) {
1824            writeln!(self.out, "{level}bool {variable} = false;",)?;
1825        };
1826
1827        // Check if there is only one body, by seeing if all except the last case are fall through
1828        // with empty bodies. FXC doesn't handle these switches correctly, so
1829        // we generate a `do {} while(false);` loop instead. There must be a default case, so there
1830        // is no need to check if one of the cases would have matched.
1831        let one_body = cases
1832            .iter()
1833            .rev()
1834            .skip(1)
1835            .all(|case| case.fall_through && case.body.is_empty());
1836        if one_body {
1837            // Start the do-while
1838            writeln!(self.out, "{level}do {{")?;
1839            // Note: Expressions have no side-effects so we don't need to emit selector expression.
1840
1841            // Body
1842            if let Some(case) = cases.last() {
1843                for sta in case.body.iter() {
1844                    self.write_stmt(module, sta, func_ctx, indent_level_1)?;
1845                }
1846            }
1847            // End do-while
1848            writeln!(self.out, "{level}}} while(false);")?;
1849        } else {
1850            // Start the switch
1851            write!(self.out, "{level}")?;
1852            write!(self.out, "switch(")?;
1853            self.write_expr(module, selector, func_ctx)?;
1854            writeln!(self.out, ") {{")?;
1855
1856            for (i, case) in cases.iter().enumerate() {
1857                match case.value {
1858                    crate::SwitchValue::I32(value) => {
1859                        write!(self.out, "{indent_level_1}case {value}:")?
1860                    }
1861                    crate::SwitchValue::U32(value) => {
1862                        write!(self.out, "{indent_level_1}case {value}u:")?
1863                    }
1864                    crate::SwitchValue::Default => write!(self.out, "{indent_level_1}default:")?,
1865                }
1866
1867                // The new block is not only stylistic, it plays a role here:
1868                // We might end up having to write the same case body
1869                // multiple times due to FXC not supporting fallthrough.
1870                // Therefore, some `Expression`s written by `Statement::Emit`
1871                // will end up having the same name (`_expr<handle_index>`).
1872                // So we need to put each case in its own scope.
1873                let write_block_braces = !(case.fall_through && case.body.is_empty());
1874                if write_block_braces {
1875                    writeln!(self.out, " {{")?;
1876                } else {
1877                    writeln!(self.out)?;
1878                }
1879
1880                // Although FXC does support a series of case clauses before
1881                // a block[^yes], it does not support fallthrough from a
1882                // non-empty case block to the next[^no]. If this case has a
1883                // non-empty body with a fallthrough, emulate that by
1884                // duplicating the bodies of all the cases it would fall
1885                // into as extensions of this case's own body. This makes
1886                // the HLSL output potentially quadratic in the size of the
1887                // Naga IR.
1888                //
1889                // [^yes]: ```hlsl
1890                // case 1:
1891                // case 2: do_stuff()
1892                // ```
1893                // [^no]: ```hlsl
1894                // case 1: do_this();
1895                // case 2: do_that();
1896                // ```
1897                if case.fall_through && !case.body.is_empty() {
1898                    let curr_len = i + 1;
1899                    let end_case_idx = curr_len
1900                        + cases
1901                            .iter()
1902                            .skip(curr_len)
1903                            .position(|case| !case.fall_through)
1904                            .unwrap();
1905                    let indent_level_3 = indent_level_2.next();
1906                    for case in &cases[i..=end_case_idx] {
1907                        writeln!(self.out, "{indent_level_2}{{")?;
1908                        let prev_len = self.named_expressions.len();
1909                        for sta in case.body.iter() {
1910                            self.write_stmt(module, sta, func_ctx, indent_level_3)?;
1911                        }
1912                        // Clear all named expressions that were previously inserted by the statements in the block
1913                        self.named_expressions.truncate(prev_len);
1914                        writeln!(self.out, "{indent_level_2}}}")?;
1915                    }
1916
1917                    let last_case = &cases[end_case_idx];
1918                    if last_case.body.last().is_none_or(|s| !s.is_terminator()) {
1919                        writeln!(self.out, "{indent_level_2}break;")?;
1920                    }
1921                } else {
1922                    for sta in case.body.iter() {
1923                        self.write_stmt(module, sta, func_ctx, indent_level_2)?;
1924                    }
1925                    if !case.fall_through && case.body.last().is_none_or(|s| !s.is_terminator()) {
1926                        writeln!(self.out, "{indent_level_2}break;")?;
1927                    }
1928                }
1929
1930                if write_block_braces {
1931                    writeln!(self.out, "{indent_level_1}}}")?;
1932                }
1933            }
1934
1935            writeln!(self.out, "{level}}}")?;
1936        }
1937
1938        // Handle any forwarded continue statements.
1939        use back::continue_forward::ExitControlFlow;
1940        let op = match self.continue_ctx.exit_switch() {
1941            ExitControlFlow::None => None,
1942            ExitControlFlow::Continue { variable } => Some(("continue", variable)),
1943            ExitControlFlow::Break { variable } => Some(("break", variable)),
1944        };
1945        if let Some((control_flow, variable)) = op {
1946            writeln!(self.out, "{level}if ({variable}) {{")?;
1947            writeln!(self.out, "{indent_level_1}{control_flow};")?;
1948            writeln!(self.out, "{level}}}")?;
1949        }
1950
1951        Ok(())
1952    }
1953
1954    fn write_index(
1955        &mut self,
1956        module: &Module,
1957        index: Index,
1958        func_ctx: &back::FunctionCtx<'_>,
1959    ) -> BackendResult {
1960        match index {
1961            Index::Static(index) => {
1962                write!(self.out, "{index}")?;
1963            }
1964            Index::Expression(index) => {
1965                self.write_expr(module, index, func_ctx)?;
1966            }
1967        }
1968        Ok(())
1969    }
1970
1971    /// Helper method used to write statements
1972    ///
1973    /// # Notes
1974    /// Always adds a newline
1975    fn write_stmt(
1976        &mut self,
1977        module: &Module,
1978        stmt: &crate::Statement,
1979        func_ctx: &back::FunctionCtx<'_>,
1980        level: back::Level,
1981    ) -> BackendResult {
1982        use crate::Statement;
1983
1984        match *stmt {
1985            Statement::Emit(ref range) => {
1986                for handle in range.clone() {
1987                    let ptr_class = func_ctx.resolve_type(handle, &module.types).pointer_space();
1988                    let expr_name = if ptr_class.is_some() {
1989                        // HLSL can't save a pointer-valued expression in a variable,
1990                        // but we shouldn't ever need to: they should never be named expressions,
1991                        // and none of the expression types flagged by bake_ref_count can be pointer-valued.
1992                        None
1993                    } else if let Some(name) = func_ctx.named_expressions.get(&handle) {
1994                        // Front end provides names for all variables at the start of writing.
1995                        // But we write them to step by step. We need to recache them
1996                        // Otherwise, we could accidentally write variable name instead of full expression.
1997                        // Also, we use sanitized names! It defense backend from generating variable with name from reserved keywords.
1998                        Some(self.namer.call(name))
1999                    } else if self.need_bake_expressions.contains(&handle) {
2000                        Some(Baked(handle).to_string())
2001                    } else {
2002                        None
2003                    };
2004
2005                    if let Some(name) = expr_name {
2006                        write!(self.out, "{level}")?;
2007                        self.write_named_expr(module, handle, name, handle, func_ctx)?;
2008                    }
2009                }
2010            }
2011            // TODO: copy-paste from glsl-out
2012            Statement::Block(ref block) => {
2013                write!(self.out, "{level}")?;
2014                writeln!(self.out, "{{")?;
2015                for sta in block.iter() {
2016                    // Increase the indentation to help with readability
2017                    self.write_stmt(module, sta, func_ctx, level.next())?
2018                }
2019                writeln!(self.out, "{level}}}")?
2020            }
2021            // TODO: copy-paste from glsl-out
2022            Statement::If {
2023                condition,
2024                ref accept,
2025                ref reject,
2026            } => {
2027                write!(self.out, "{level}")?;
2028                write!(self.out, "if (")?;
2029                self.write_expr(module, condition, func_ctx)?;
2030                writeln!(self.out, ") {{")?;
2031
2032                let l2 = level.next();
2033                for sta in accept {
2034                    // Increase indentation to help with readability
2035                    self.write_stmt(module, sta, func_ctx, l2)?;
2036                }
2037
2038                // If there are no statements in the reject block we skip writing it
2039                // This is only for readability
2040                if !reject.is_empty() {
2041                    writeln!(self.out, "{level}}} else {{")?;
2042
2043                    for sta in reject {
2044                        // Increase indentation to help with readability
2045                        self.write_stmt(module, sta, func_ctx, l2)?;
2046                    }
2047                }
2048
2049                writeln!(self.out, "{level}}}")?
2050            }
2051            // TODO: copy-paste from glsl-out
2052            Statement::Kill => writeln!(self.out, "{level}discard;")?,
2053            Statement::Return { value: None } => {
2054                writeln!(self.out, "{level}return;")?;
2055            }
2056            Statement::Return { value: Some(expr) } => {
2057                let base_ty_res = &func_ctx.info[expr].ty;
2058                let mut resolved = base_ty_res.inner_with(&module.types);
2059                if let TypeInner::Pointer { base, space: _ } = *resolved {
2060                    resolved = &module.types[base].inner;
2061                }
2062
2063                if let TypeInner::Struct { .. } = *resolved {
2064                    // We can safely unwrap here, since we now we working with struct
2065                    let ty = base_ty_res.handle().unwrap();
2066                    let struct_name = &self.names[&NameKey::Type(ty)];
2067                    let variable_name = self.namer.call(&struct_name.to_lowercase());
2068                    write!(self.out, "{level}const {struct_name} {variable_name} = ",)?;
2069                    self.write_expr(module, expr, func_ctx)?;
2070                    writeln!(self.out, ";")?;
2071
2072                    // for entry point returns, we may need to reshuffle the outputs into a different struct
2073                    let ep_output = match func_ctx.ty {
2074                        back::FunctionType::Function(_) => None,
2075                        back::FunctionType::EntryPoint(index) => self
2076                            .entry_point_io
2077                            .get(&(index as usize))
2078                            .unwrap()
2079                            .output
2080                            .as_ref(),
2081                    };
2082                    let final_name = match ep_output {
2083                        Some(ep_output) => {
2084                            let final_name = self.namer.call(&variable_name);
2085                            write!(
2086                                self.out,
2087                                "{}const {} {} = {{ ",
2088                                level, ep_output.ty_name, final_name,
2089                            )?;
2090                            for (index, m) in ep_output.members.iter().enumerate() {
2091                                if index != 0 {
2092                                    write!(self.out, ", ")?;
2093                                }
2094                                let member_name = &self.names[&NameKey::StructMember(ty, m.index)];
2095                                write!(self.out, "{variable_name}.{member_name}")?;
2096                            }
2097                            writeln!(self.out, " }};")?;
2098                            final_name
2099                        }
2100                        None => variable_name,
2101                    };
2102                    writeln!(self.out, "{level}return {final_name};")?;
2103                } else {
2104                    write!(self.out, "{level}return ")?;
2105                    self.write_expr(module, expr, func_ctx)?;
2106                    writeln!(self.out, ";")?
2107                }
2108            }
2109            Statement::Store { pointer, value } => {
2110                let ty_inner = func_ctx.resolve_type(pointer, &module.types);
2111                if let Some(crate::AddressSpace::Storage { .. }) = ty_inner.pointer_space() {
2112                    let var_handle = self.fill_access_chain(module, pointer, func_ctx)?;
2113                    self.write_storage_store(
2114                        module,
2115                        var_handle,
2116                        StoreValue::Expression(value),
2117                        func_ctx,
2118                        level,
2119                        None,
2120                    )?;
2121                } else {
2122                    // We treat matrices of the form `matCx2` as a sequence of C `vec2`s.
2123                    // See the module-level block comment in mod.rs for details.
2124                    //
2125                    // We handle matrix Stores here directly (including sub accesses for Vectors and Scalars).
2126                    // Loads are handled by `Expression::AccessIndex` (since sub accesses work fine for Loads).
2127                    enum MatrixAccess {
2128                        Direct {
2129                            base: Handle<crate::Expression>,
2130                            index: u32,
2131                        },
2132                        Struct {
2133                            columns: crate::VectorSize,
2134                            base: Handle<crate::Expression>,
2135                        },
2136                    }
2137
2138                    let get_members = |expr: Handle<crate::Expression>| {
2139                        let resolved = func_ctx.resolve_type(expr, &module.types);
2140                        match *resolved {
2141                            TypeInner::Pointer { base, .. } => match module.types[base].inner {
2142                                TypeInner::Struct { ref members, .. } => Some(members),
2143                                _ => None,
2144                            },
2145                            _ => None,
2146                        }
2147                    };
2148
2149                    write!(self.out, "{level}")?;
2150
2151                    let matrix_access_on_lhs =
2152                        find_matrix_in_access_chain(module, pointer, func_ctx).and_then(
2153                            |(matrix_expr, vector, scalar)| match (
2154                                func_ctx.resolve_type(matrix_expr, &module.types),
2155                                &func_ctx.expressions[matrix_expr],
2156                            ) {
2157                                (
2158                                    &TypeInner::Pointer { base: ty, .. },
2159                                    &crate::Expression::AccessIndex { base, index },
2160                                ) if matches!(
2161                                    module.types[ty].inner,
2162                                    TypeInner::Matrix {
2163                                        rows: crate::VectorSize::Bi,
2164                                        ..
2165                                    }
2166                                ) && get_members(base)
2167                                    .map(|members| members[index as usize].binding.is_none())
2168                                    == Some(true) =>
2169                                {
2170                                    Some((MatrixAccess::Direct { base, index }, vector, scalar))
2171                                }
2172                                _ => {
2173                                    if let Some(MatrixType {
2174                                        columns,
2175                                        rows: crate::VectorSize::Bi,
2176                                        width: 4,
2177                                    }) = get_inner_matrix_of_struct_array_member(
2178                                        module,
2179                                        matrix_expr,
2180                                        func_ctx,
2181                                        true,
2182                                    ) {
2183                                        Some((
2184                                            MatrixAccess::Struct {
2185                                                columns,
2186                                                base: matrix_expr,
2187                                            },
2188                                            vector,
2189                                            scalar,
2190                                        ))
2191                                    } else {
2192                                        None
2193                                    }
2194                                }
2195                            },
2196                        );
2197
2198                    match matrix_access_on_lhs {
2199                        Some((MatrixAccess::Direct { index, base }, vector, scalar)) => {
2200                            let base_ty_res = &func_ctx.info[base].ty;
2201                            let resolved = base_ty_res.inner_with(&module.types);
2202                            let ty = match *resolved {
2203                                TypeInner::Pointer { base, .. } => base,
2204                                _ => base_ty_res.handle().unwrap(),
2205                            };
2206
2207                            if let Some(Index::Static(vec_index)) = vector {
2208                                self.write_expr(module, base, func_ctx)?;
2209                                write!(
2210                                    self.out,
2211                                    ".{}_{}",
2212                                    &self.names[&NameKey::StructMember(ty, index)],
2213                                    vec_index
2214                                )?;
2215
2216                                if let Some(scalar_index) = scalar {
2217                                    write!(self.out, "[")?;
2218                                    self.write_index(module, scalar_index, func_ctx)?;
2219                                    write!(self.out, "]")?;
2220                                }
2221
2222                                write!(self.out, " = ")?;
2223                                self.write_expr(module, value, func_ctx)?;
2224                                writeln!(self.out, ";")?;
2225                            } else {
2226                                let access = WrappedStructMatrixAccess { ty, index };
2227                                match (&vector, &scalar) {
2228                                    (&Some(_), &Some(_)) => {
2229                                        self.write_wrapped_struct_matrix_set_scalar_function_name(
2230                                            access,
2231                                        )?;
2232                                    }
2233                                    (&Some(_), &None) => {
2234                                        self.write_wrapped_struct_matrix_set_vec_function_name(
2235                                            access,
2236                                        )?;
2237                                    }
2238                                    (&None, _) => {
2239                                        self.write_wrapped_struct_matrix_set_function_name(access)?;
2240                                    }
2241                                }
2242
2243                                write!(self.out, "(")?;
2244                                self.write_expr(module, base, func_ctx)?;
2245                                write!(self.out, ", ")?;
2246                                self.write_expr(module, value, func_ctx)?;
2247
2248                                if let Some(Index::Expression(vec_index)) = vector {
2249                                    write!(self.out, ", ")?;
2250                                    self.write_expr(module, vec_index, func_ctx)?;
2251
2252                                    if let Some(scalar_index) = scalar {
2253                                        write!(self.out, ", ")?;
2254                                        self.write_index(module, scalar_index, func_ctx)?;
2255                                    }
2256                                }
2257                                writeln!(self.out, ");")?;
2258                            }
2259                        }
2260                        Some((
2261                            MatrixAccess::Struct { columns, base },
2262                            Some(Index::Expression(vec_index)),
2263                            scalar,
2264                        )) => {
2265                            // We handle `Store`s to __matCx2 column vectors and scalar elements via
2266                            // the previously injected functions __set_col_of_matCx2 / __set_el_of_matCx2.
2267
2268                            if scalar.is_some() {
2269                                write!(self.out, "__set_el_of_mat{}x2", columns as u8)?;
2270                            } else {
2271                                write!(self.out, "__set_col_of_mat{}x2", columns as u8)?;
2272                            }
2273                            write!(self.out, "(")?;
2274                            self.write_expr(module, base, func_ctx)?;
2275                            write!(self.out, ", ")?;
2276                            self.write_expr(module, vec_index, func_ctx)?;
2277
2278                            if let Some(scalar_index) = scalar {
2279                                write!(self.out, ", ")?;
2280                                self.write_index(module, scalar_index, func_ctx)?;
2281                            }
2282
2283                            write!(self.out, ", ")?;
2284                            self.write_expr(module, value, func_ctx)?;
2285
2286                            writeln!(self.out, ");")?;
2287                        }
2288                        Some((MatrixAccess::Struct { .. }, Some(Index::Static(_)), _))
2289                        | Some((MatrixAccess::Struct { .. }, None, _))
2290                        | None => {
2291                            self.write_expr(module, pointer, func_ctx)?;
2292                            write!(self.out, " = ")?;
2293
2294                            // We cast the RHS of this store in cases where the LHS
2295                            // is a struct member with type:
2296                            //  - matCx2 or
2297                            //  - a (possibly nested) array of matCx2's
2298                            if let Some(MatrixType {
2299                                columns,
2300                                rows: crate::VectorSize::Bi,
2301                                width: 4,
2302                            }) = get_inner_matrix_of_struct_array_member(
2303                                module, pointer, func_ctx, false,
2304                            ) {
2305                                let mut resolved = func_ctx.resolve_type(pointer, &module.types);
2306                                if let TypeInner::Pointer { base, .. } = *resolved {
2307                                    resolved = &module.types[base].inner;
2308                                }
2309
2310                                write!(self.out, "(__mat{}x2", columns as u8)?;
2311                                if let TypeInner::Array { base, size, .. } = *resolved {
2312                                    self.write_array_size(module, base, size)?;
2313                                }
2314                                write!(self.out, ")")?;
2315                            }
2316
2317                            self.write_expr(module, value, func_ctx)?;
2318                            writeln!(self.out, ";")?
2319                        }
2320                    }
2321                }
2322            }
2323            Statement::Loop {
2324                ref body,
2325                ref continuing,
2326                break_if,
2327            } => {
2328                let force_loop_bound_statements = self.gen_force_bounded_loop_statements(level);
2329                let gate_name = (!continuing.is_empty() || break_if.is_some())
2330                    .then(|| self.namer.call("loop_init"));
2331
2332                if let Some((ref decl, _)) = force_loop_bound_statements {
2333                    writeln!(self.out, "{decl}")?;
2334                }
2335                if let Some(ref gate_name) = gate_name {
2336                    writeln!(self.out, "{level}bool {gate_name} = true;")?;
2337                }
2338
2339                self.continue_ctx.enter_loop();
2340                writeln!(self.out, "{level}while(true) {{")?;
2341                if let Some((_, ref break_and_inc)) = force_loop_bound_statements {
2342                    writeln!(self.out, "{break_and_inc}")?;
2343                }
2344                let l2 = level.next();
2345                if let Some(gate_name) = gate_name {
2346                    writeln!(self.out, "{l2}if (!{gate_name}) {{")?;
2347                    let l3 = l2.next();
2348                    for sta in continuing.iter() {
2349                        self.write_stmt(module, sta, func_ctx, l3)?;
2350                    }
2351                    if let Some(condition) = break_if {
2352                        write!(self.out, "{l3}if (")?;
2353                        self.write_expr(module, condition, func_ctx)?;
2354                        writeln!(self.out, ") {{")?;
2355                        writeln!(self.out, "{}break;", l3.next())?;
2356                        writeln!(self.out, "{l3}}}")?;
2357                    }
2358                    writeln!(self.out, "{l2}}}")?;
2359                    writeln!(self.out, "{l2}{gate_name} = false;")?;
2360                }
2361
2362                for sta in body.iter() {
2363                    self.write_stmt(module, sta, func_ctx, l2)?;
2364                }
2365
2366                writeln!(self.out, "{level}}}")?;
2367                self.continue_ctx.exit_loop();
2368            }
2369            Statement::Break => writeln!(self.out, "{level}break;")?,
2370            Statement::Continue => {
2371                if let Some(variable) = self.continue_ctx.continue_encountered() {
2372                    writeln!(self.out, "{level}{variable} = true;")?;
2373                    writeln!(self.out, "{level}break;")?
2374                } else {
2375                    writeln!(self.out, "{level}continue;")?
2376                }
2377            }
2378            Statement::ControlBarrier(barrier) => {
2379                self.write_control_barrier(barrier, level)?;
2380            }
2381            Statement::MemoryBarrier(barrier) => {
2382                self.write_memory_barrier(barrier, level)?;
2383            }
2384            Statement::ImageStore {
2385                image,
2386                coordinate,
2387                array_index,
2388                value,
2389            } => {
2390                write!(self.out, "{level}")?;
2391                self.write_expr(module, image, func_ctx)?;
2392
2393                write!(self.out, "[")?;
2394                if let Some(index) = array_index {
2395                    // Array index accepted only for texture_storage_2d_array, so we can safety use int3(coordinate, array_index) here
2396                    write!(self.out, "int3(")?;
2397                    self.write_expr(module, coordinate, func_ctx)?;
2398                    write!(self.out, ", ")?;
2399                    self.write_expr(module, index, func_ctx)?;
2400                    write!(self.out, ")")?;
2401                } else {
2402                    self.write_expr(module, coordinate, func_ctx)?;
2403                }
2404                write!(self.out, "]")?;
2405
2406                write!(self.out, " = ")?;
2407                self.write_expr(module, value, func_ctx)?;
2408                writeln!(self.out, ";")?;
2409            }
2410            Statement::Call {
2411                function,
2412                ref arguments,
2413                result,
2414            } => {
2415                write!(self.out, "{level}")?;
2416                if let Some(expr) = result {
2417                    write!(self.out, "const ")?;
2418                    let name = Baked(expr).to_string();
2419                    let expr_ty = &func_ctx.info[expr].ty;
2420                    let ty_inner = match *expr_ty {
2421                        proc::TypeResolution::Handle(handle) => {
2422                            self.write_type(module, handle)?;
2423                            &module.types[handle].inner
2424                        }
2425                        proc::TypeResolution::Value(ref value) => {
2426                            self.write_value_type(module, value)?;
2427                            value
2428                        }
2429                    };
2430                    write!(self.out, " {name}")?;
2431                    if let TypeInner::Array { base, size, .. } = *ty_inner {
2432                        self.write_array_size(module, base, size)?;
2433                    }
2434                    write!(self.out, " = ")?;
2435                    self.named_expressions.insert(expr, name);
2436                }
2437                let func_name = &self.names[&NameKey::Function(function)];
2438                write!(self.out, "{func_name}(")?;
2439                for (index, argument) in arguments.iter().enumerate() {
2440                    if index != 0 {
2441                        write!(self.out, ", ")?;
2442                    }
2443                    self.write_expr(module, *argument, func_ctx)?;
2444                }
2445                writeln!(self.out, ");")?
2446            }
2447            Statement::Atomic {
2448                pointer,
2449                ref fun,
2450                value,
2451                result,
2452            } => {
2453                write!(self.out, "{level}")?;
2454                let res_var_info = if let Some(res_handle) = result {
2455                    let name = Baked(res_handle).to_string();
2456                    match func_ctx.info[res_handle].ty {
2457                        proc::TypeResolution::Handle(handle) => self.write_type(module, handle)?,
2458                        proc::TypeResolution::Value(ref value) => {
2459                            self.write_value_type(module, value)?
2460                        }
2461                    };
2462                    write!(self.out, " {name}; ")?;
2463                    self.named_expressions.insert(res_handle, name.clone());
2464                    Some((res_handle, name))
2465                } else {
2466                    None
2467                };
2468                let pointer_space = func_ctx
2469                    .resolve_type(pointer, &module.types)
2470                    .pointer_space()
2471                    .unwrap();
2472                let fun_str = fun.to_hlsl_suffix();
2473                let compare_expr = match *fun {
2474                    crate::AtomicFunction::Exchange { compare: Some(cmp) } => Some(cmp),
2475                    _ => None,
2476                };
2477                match pointer_space {
2478                    crate::AddressSpace::WorkGroup => {
2479                        write!(self.out, "Interlocked{fun_str}(")?;
2480                        self.write_expr(module, pointer, func_ctx)?;
2481                        self.emit_hlsl_atomic_tail(
2482                            module,
2483                            func_ctx,
2484                            fun,
2485                            compare_expr,
2486                            value,
2487                            &res_var_info,
2488                        )?;
2489                    }
2490                    crate::AddressSpace::Storage { .. } => {
2491                        let var_handle = self.fill_access_chain(module, pointer, func_ctx)?;
2492                        let var_name = &self.names[&NameKey::GlobalVariable(var_handle)];
2493                        let width = match func_ctx.resolve_type(value, &module.types) {
2494                            &TypeInner::Scalar(Scalar { width: 8, .. }) => "64",
2495                            _ => "",
2496                        };
2497                        write!(self.out, "{var_name}.Interlocked{fun_str}{width}(")?;
2498                        let chain = mem::take(&mut self.temp_access_chain);
2499                        self.write_storage_address(module, &chain, func_ctx)?;
2500                        self.temp_access_chain = chain;
2501                        self.emit_hlsl_atomic_tail(
2502                            module,
2503                            func_ctx,
2504                            fun,
2505                            compare_expr,
2506                            value,
2507                            &res_var_info,
2508                        )?;
2509                    }
2510                    ref other => {
2511                        return Err(Error::Custom(format!(
2512                            "invalid address space {other:?} for atomic statement"
2513                        )))
2514                    }
2515                }
2516                if let Some(cmp) = compare_expr {
2517                    if let Some(&(_res_handle, ref res_name)) = res_var_info.as_ref() {
2518                        write!(
2519                            self.out,
2520                            "{level}{res_name}.exchanged = ({res_name}.old_value == "
2521                        )?;
2522                        self.write_expr(module, cmp, func_ctx)?;
2523                        writeln!(self.out, ");")?;
2524                    }
2525                }
2526            }
2527            Statement::ImageAtomic {
2528                image,
2529                coordinate,
2530                array_index,
2531                fun,
2532                value,
2533            } => {
2534                write!(self.out, "{level}")?;
2535
2536                let fun_str = fun.to_hlsl_suffix();
2537                write!(self.out, "Interlocked{fun_str}(")?;
2538                self.write_expr(module, image, func_ctx)?;
2539                write!(self.out, "[")?;
2540                self.write_texture_coordinates(
2541                    "int",
2542                    coordinate,
2543                    array_index,
2544                    None,
2545                    module,
2546                    func_ctx,
2547                )?;
2548                write!(self.out, "],")?;
2549
2550                self.write_expr(module, value, func_ctx)?;
2551                writeln!(self.out, ");")?;
2552            }
2553            Statement::WorkGroupUniformLoad { pointer, result } => {
2554                self.write_control_barrier(crate::Barrier::WORK_GROUP, level)?;
2555                write!(self.out, "{level}")?;
2556                let name = Baked(result).to_string();
2557                self.write_named_expr(module, pointer, name, result, func_ctx)?;
2558
2559                self.write_control_barrier(crate::Barrier::WORK_GROUP, level)?;
2560            }
2561            Statement::Switch {
2562                selector,
2563                ref cases,
2564            } => {
2565                self.write_switch(module, func_ctx, level, selector, cases)?;
2566            }
2567            Statement::RayQuery { query, ref fun } => match *fun {
2568                RayQueryFunction::Initialize {
2569                    acceleration_structure,
2570                    descriptor,
2571                } => {
2572                    write!(self.out, "{level}")?;
2573                    self.write_expr(module, query, func_ctx)?;
2574                    write!(self.out, ".TraceRayInline(")?;
2575                    self.write_expr(module, acceleration_structure, func_ctx)?;
2576                    write!(self.out, ", ")?;
2577                    self.write_expr(module, descriptor, func_ctx)?;
2578                    write!(self.out, ".flags, ")?;
2579                    self.write_expr(module, descriptor, func_ctx)?;
2580                    write!(self.out, ".cull_mask, ")?;
2581                    write!(self.out, "RayDescFromRayDesc_(")?;
2582                    self.write_expr(module, descriptor, func_ctx)?;
2583                    writeln!(self.out, "));")?;
2584                }
2585                RayQueryFunction::Proceed { result } => {
2586                    write!(self.out, "{level}")?;
2587                    let name = Baked(result).to_string();
2588                    write!(self.out, "const bool {name} = ")?;
2589                    self.named_expressions.insert(result, name);
2590                    self.write_expr(module, query, func_ctx)?;
2591                    writeln!(self.out, ".Proceed();")?;
2592                }
2593                RayQueryFunction::GenerateIntersection { hit_t } => {
2594                    write!(self.out, "{level}")?;
2595                    self.write_expr(module, query, func_ctx)?;
2596                    write!(self.out, ".CommitProceduralPrimitiveHit(")?;
2597                    self.write_expr(module, hit_t, func_ctx)?;
2598                    writeln!(self.out, ");")?;
2599                }
2600                RayQueryFunction::ConfirmIntersection => {
2601                    write!(self.out, "{level}")?;
2602                    self.write_expr(module, query, func_ctx)?;
2603                    writeln!(self.out, ".CommitNonOpaqueTriangleHit();")?;
2604                }
2605                RayQueryFunction::Terminate => {
2606                    write!(self.out, "{level}")?;
2607                    self.write_expr(module, query, func_ctx)?;
2608                    writeln!(self.out, ".Abort();")?;
2609                }
2610            },
2611            Statement::SubgroupBallot { result, predicate } => {
2612                write!(self.out, "{level}")?;
2613                let name = Baked(result).to_string();
2614                write!(self.out, "const uint4 {name} = ")?;
2615                self.named_expressions.insert(result, name);
2616
2617                write!(self.out, "WaveActiveBallot(")?;
2618                match predicate {
2619                    Some(predicate) => self.write_expr(module, predicate, func_ctx)?,
2620                    None => write!(self.out, "true")?,
2621                }
2622                writeln!(self.out, ");")?;
2623            }
2624            Statement::SubgroupCollectiveOperation {
2625                op,
2626                collective_op,
2627                argument,
2628                result,
2629            } => {
2630                write!(self.out, "{level}")?;
2631                write!(self.out, "const ")?;
2632                let name = Baked(result).to_string();
2633                match func_ctx.info[result].ty {
2634                    proc::TypeResolution::Handle(handle) => self.write_type(module, handle)?,
2635                    proc::TypeResolution::Value(ref value) => {
2636                        self.write_value_type(module, value)?
2637                    }
2638                };
2639                write!(self.out, " {name} = ")?;
2640                self.named_expressions.insert(result, name);
2641
2642                match (collective_op, op) {
2643                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::All) => {
2644                        write!(self.out, "WaveActiveAllTrue(")?
2645                    }
2646                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Any) => {
2647                        write!(self.out, "WaveActiveAnyTrue(")?
2648                    }
2649                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Add) => {
2650                        write!(self.out, "WaveActiveSum(")?
2651                    }
2652                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Mul) => {
2653                        write!(self.out, "WaveActiveProduct(")?
2654                    }
2655                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Max) => {
2656                        write!(self.out, "WaveActiveMax(")?
2657                    }
2658                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Min) => {
2659                        write!(self.out, "WaveActiveMin(")?
2660                    }
2661                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::And) => {
2662                        write!(self.out, "WaveActiveBitAnd(")?
2663                    }
2664                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Or) => {
2665                        write!(self.out, "WaveActiveBitOr(")?
2666                    }
2667                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Xor) => {
2668                        write!(self.out, "WaveActiveBitXor(")?
2669                    }
2670                    (crate::CollectiveOperation::ExclusiveScan, crate::SubgroupOperation::Add) => {
2671                        write!(self.out, "WavePrefixSum(")?
2672                    }
2673                    (crate::CollectiveOperation::ExclusiveScan, crate::SubgroupOperation::Mul) => {
2674                        write!(self.out, "WavePrefixProduct(")?
2675                    }
2676                    (crate::CollectiveOperation::InclusiveScan, crate::SubgroupOperation::Add) => {
2677                        self.write_expr(module, argument, func_ctx)?;
2678                        write!(self.out, " + WavePrefixSum(")?;
2679                    }
2680                    (crate::CollectiveOperation::InclusiveScan, crate::SubgroupOperation::Mul) => {
2681                        self.write_expr(module, argument, func_ctx)?;
2682                        write!(self.out, " * WavePrefixProduct(")?;
2683                    }
2684                    _ => unimplemented!(),
2685                }
2686                self.write_expr(module, argument, func_ctx)?;
2687                writeln!(self.out, ");")?;
2688            }
2689            Statement::SubgroupGather {
2690                mode,
2691                argument,
2692                result,
2693            } => {
2694                write!(self.out, "{level}")?;
2695                write!(self.out, "const ")?;
2696                let name = Baked(result).to_string();
2697                match func_ctx.info[result].ty {
2698                    proc::TypeResolution::Handle(handle) => self.write_type(module, handle)?,
2699                    proc::TypeResolution::Value(ref value) => {
2700                        self.write_value_type(module, value)?
2701                    }
2702                };
2703                write!(self.out, " {name} = ")?;
2704                self.named_expressions.insert(result, name);
2705                match mode {
2706                    crate::GatherMode::BroadcastFirst => {
2707                        write!(self.out, "WaveReadLaneFirst(")?;
2708                        self.write_expr(module, argument, func_ctx)?;
2709                    }
2710                    crate::GatherMode::QuadBroadcast(index) => {
2711                        write!(self.out, "QuadReadLaneAt(")?;
2712                        self.write_expr(module, argument, func_ctx)?;
2713                        write!(self.out, ", ")?;
2714                        self.write_expr(module, index, func_ctx)?;
2715                    }
2716                    crate::GatherMode::QuadSwap(direction) => {
2717                        match direction {
2718                            crate::Direction::X => {
2719                                write!(self.out, "QuadReadAcrossX(")?;
2720                            }
2721                            crate::Direction::Y => {
2722                                write!(self.out, "QuadReadAcrossY(")?;
2723                            }
2724                            crate::Direction::Diagonal => {
2725                                write!(self.out, "QuadReadAcrossDiagonal(")?;
2726                            }
2727                        }
2728                        self.write_expr(module, argument, func_ctx)?;
2729                    }
2730                    _ => {
2731                        write!(self.out, "WaveReadLaneAt(")?;
2732                        self.write_expr(module, argument, func_ctx)?;
2733                        write!(self.out, ", ")?;
2734                        match mode {
2735                            crate::GatherMode::BroadcastFirst => unreachable!(),
2736                            crate::GatherMode::Broadcast(index)
2737                            | crate::GatherMode::Shuffle(index) => {
2738                                self.write_expr(module, index, func_ctx)?;
2739                            }
2740                            crate::GatherMode::ShuffleDown(index) => {
2741                                write!(self.out, "WaveGetLaneIndex() + ")?;
2742                                self.write_expr(module, index, func_ctx)?;
2743                            }
2744                            crate::GatherMode::ShuffleUp(index) => {
2745                                write!(self.out, "WaveGetLaneIndex() - ")?;
2746                                self.write_expr(module, index, func_ctx)?;
2747                            }
2748                            crate::GatherMode::ShuffleXor(index) => {
2749                                write!(self.out, "WaveGetLaneIndex() ^ ")?;
2750                                self.write_expr(module, index, func_ctx)?;
2751                            }
2752                            crate::GatherMode::QuadBroadcast(_) => unreachable!(),
2753                            crate::GatherMode::QuadSwap(_) => unreachable!(),
2754                        }
2755                    }
2756                }
2757                writeln!(self.out, ");")?;
2758            }
2759        }
2760
2761        Ok(())
2762    }
2763
2764    fn write_const_expression(
2765        &mut self,
2766        module: &Module,
2767        expr: Handle<crate::Expression>,
2768        arena: &crate::Arena<crate::Expression>,
2769    ) -> BackendResult {
2770        self.write_possibly_const_expression(module, expr, arena, |writer, expr| {
2771            writer.write_const_expression(module, expr, arena)
2772        })
2773    }
2774
2775    pub(super) fn write_literal(&mut self, literal: crate::Literal) -> BackendResult {
2776        match literal {
2777            crate::Literal::F64(value) => write!(self.out, "{value:?}L")?,
2778            crate::Literal::F32(value) => write!(self.out, "{value:?}")?,
2779            crate::Literal::F16(value) => write!(self.out, "{value:?}h")?,
2780            crate::Literal::U32(value) => write!(self.out, "{value}u")?,
2781            // `-2147483648` is parsed by some compilers as unary negation of
2782            // positive 2147483648, which is too large for an int, causing
2783            // issues for some compilers. Neither DXC nor FXC appear to have
2784            // this problem, but this is not specified and could change. We
2785            // therefore use `-2147483647 - 1` as a precaution.
2786            crate::Literal::I32(value) if value == i32::MIN => {
2787                write!(self.out, "int({} - 1)", value + 1)?
2788            }
2789            // HLSL has no suffix for explicit i32 literals, but not using any suffix
2790            // makes the type ambiguous which prevents overload resolution from
2791            // working. So we explicitly use the int() constructor syntax.
2792            crate::Literal::I32(value) => write!(self.out, "int({value})")?,
2793            crate::Literal::U64(value) => write!(self.out, "{value}uL")?,
2794            // I64 version of the minimum I32 value issue described above.
2795            crate::Literal::I64(value) if value == i64::MIN => {
2796                write!(self.out, "({}L - 1L)", value + 1)?;
2797            }
2798            crate::Literal::I64(value) => write!(self.out, "{value}L")?,
2799            crate::Literal::Bool(value) => write!(self.out, "{value}")?,
2800            crate::Literal::AbstractInt(_) | crate::Literal::AbstractFloat(_) => {
2801                return Err(Error::Custom(
2802                    "Abstract types should not appear in IR presented to backends".into(),
2803                ));
2804            }
2805        }
2806        Ok(())
2807    }
2808
2809    fn write_possibly_const_expression<E>(
2810        &mut self,
2811        module: &Module,
2812        expr: Handle<crate::Expression>,
2813        expressions: &crate::Arena<crate::Expression>,
2814        write_expression: E,
2815    ) -> BackendResult
2816    where
2817        E: Fn(&mut Self, Handle<crate::Expression>) -> BackendResult,
2818    {
2819        use crate::Expression;
2820
2821        match expressions[expr] {
2822            Expression::Literal(literal) => {
2823                self.write_literal(literal)?;
2824            }
2825            Expression::Constant(handle) => {
2826                let constant = &module.constants[handle];
2827                if constant.name.is_some() {
2828                    write!(self.out, "{}", self.names[&NameKey::Constant(handle)])?;
2829                } else {
2830                    self.write_const_expression(module, constant.init, &module.global_expressions)?;
2831                }
2832            }
2833            Expression::ZeroValue(ty) => {
2834                self.write_wrapped_zero_value_function_name(module, WrappedZeroValue { ty })?;
2835                write!(self.out, "()")?;
2836            }
2837            Expression::Compose { ty, ref components } => {
2838                match module.types[ty].inner {
2839                    TypeInner::Struct { .. } | TypeInner::Array { .. } => {
2840                        self.write_wrapped_constructor_function_name(
2841                            module,
2842                            WrappedConstructor { ty },
2843                        )?;
2844                    }
2845                    _ => {
2846                        self.write_type(module, ty)?;
2847                    }
2848                };
2849                write!(self.out, "(")?;
2850                for (index, component) in components.iter().enumerate() {
2851                    if index != 0 {
2852                        write!(self.out, ", ")?;
2853                    }
2854                    write_expression(self, *component)?;
2855                }
2856                write!(self.out, ")")?;
2857            }
2858            Expression::Splat { size, value } => {
2859                // hlsl is not supported one value constructor
2860                // if we write, for example, int4(0), dxc returns error:
2861                // error: too few elements in vector initialization (expected 4 elements, have 1)
2862                let number_of_components = match size {
2863                    crate::VectorSize::Bi => "xx",
2864                    crate::VectorSize::Tri => "xxx",
2865                    crate::VectorSize::Quad => "xxxx",
2866                };
2867                write!(self.out, "(")?;
2868                write_expression(self, value)?;
2869                write!(self.out, ").{number_of_components}")?
2870            }
2871            _ => {
2872                return Err(Error::Override);
2873            }
2874        }
2875
2876        Ok(())
2877    }
2878
2879    /// Helper method to write expressions
2880    ///
2881    /// # Notes
2882    /// Doesn't add any newlines or leading/trailing spaces
2883    pub(super) fn write_expr(
2884        &mut self,
2885        module: &Module,
2886        expr: Handle<crate::Expression>,
2887        func_ctx: &back::FunctionCtx<'_>,
2888    ) -> BackendResult {
2889        use crate::Expression;
2890
2891        // Handle the special semantics of vertex_index/instance_index
2892        let ff_input = if self.options.special_constants_binding.is_some() {
2893            func_ctx.is_fixed_function_input(expr, module)
2894        } else {
2895            None
2896        };
2897        let closing_bracket = match ff_input {
2898            Some(crate::BuiltIn::VertexIndex) => {
2899                write!(self.out, "({SPECIAL_CBUF_VAR}.{SPECIAL_FIRST_VERTEX} + ")?;
2900                ")"
2901            }
2902            Some(crate::BuiltIn::InstanceIndex) => {
2903                write!(self.out, "({SPECIAL_CBUF_VAR}.{SPECIAL_FIRST_INSTANCE} + ",)?;
2904                ")"
2905            }
2906            Some(crate::BuiltIn::NumWorkGroups) => {
2907                // Note: despite their names (`FIRST_VERTEX` and `FIRST_INSTANCE`),
2908                // in compute shaders the special constants contain the number
2909                // of workgroups, which we are using here.
2910                write!(
2911                    self.out,
2912                    "uint3({SPECIAL_CBUF_VAR}.{SPECIAL_FIRST_VERTEX}, {SPECIAL_CBUF_VAR}.{SPECIAL_FIRST_INSTANCE}, {SPECIAL_CBUF_VAR}.{SPECIAL_OTHER})",
2913                )?;
2914                return Ok(());
2915            }
2916            _ => "",
2917        };
2918
2919        if let Some(name) = self.named_expressions.get(&expr) {
2920            write!(self.out, "{name}{closing_bracket}")?;
2921            return Ok(());
2922        }
2923
2924        let expression = &func_ctx.expressions[expr];
2925
2926        match *expression {
2927            Expression::Literal(_)
2928            | Expression::Constant(_)
2929            | Expression::ZeroValue(_)
2930            | Expression::Compose { .. }
2931            | Expression::Splat { .. } => {
2932                self.write_possibly_const_expression(
2933                    module,
2934                    expr,
2935                    func_ctx.expressions,
2936                    |writer, expr| writer.write_expr(module, expr, func_ctx),
2937                )?;
2938            }
2939            Expression::Override(_) => return Err(Error::Override),
2940            // Avoid undefined behaviour for addition, subtraction, and
2941            // multiplication of signed integers by casting operands to
2942            // unsigned, performing the operation, then casting the result back
2943            // to signed.
2944            // TODO(#7109): This relies on the asint()/asuint() functions which only work
2945            // for 32-bit types, so we must find another solution for different bit widths.
2946            Expression::Binary {
2947                op:
2948                    op @ crate::BinaryOperator::Add
2949                    | op @ crate::BinaryOperator::Subtract
2950                    | op @ crate::BinaryOperator::Multiply,
2951                left,
2952                right,
2953            } if matches!(
2954                func_ctx.resolve_type(expr, &module.types).scalar(),
2955                Some(Scalar::I32)
2956            ) =>
2957            {
2958                write!(self.out, "asint(asuint(",)?;
2959                self.write_expr(module, left, func_ctx)?;
2960                write!(self.out, ") {} asuint(", back::binary_operation_str(op))?;
2961                self.write_expr(module, right, func_ctx)?;
2962                write!(self.out, "))")?;
2963            }
2964            // All of the multiplication can be expressed as `mul`,
2965            // except vector * vector, which needs to use the "*" operator.
2966            Expression::Binary {
2967                op: crate::BinaryOperator::Multiply,
2968                left,
2969                right,
2970            } if func_ctx.resolve_type(left, &module.types).is_matrix()
2971                || func_ctx.resolve_type(right, &module.types).is_matrix() =>
2972            {
2973                // We intentionally flip the order of multiplication as our matrices are implicitly transposed.
2974                write!(self.out, "mul(")?;
2975                self.write_expr(module, right, func_ctx)?;
2976                write!(self.out, ", ")?;
2977                self.write_expr(module, left, func_ctx)?;
2978                write!(self.out, ")")?;
2979            }
2980
2981            // WGSL says that floating-point division by zero should return
2982            // infinity. Microsoft's Direct3D 11 functional specification
2983            // (https://microsoft.github.io/DirectX-Specs/d3d/archive/D3D11_3_FunctionalSpec.htm)
2984            // says:
2985            //
2986            //     Divide by 0 produces +/- INF, except 0/0 which results in NaN.
2987            //
2988            // which is what we want. The DXIL specification for the FDiv
2989            // instruction corroborates this:
2990            //
2991            // https://github.com/microsoft/DirectXShaderCompiler/blob/main/docs/DXIL.rst#fdiv
2992            Expression::Binary {
2993                op: crate::BinaryOperator::Divide,
2994                left,
2995                right,
2996            } if matches!(
2997                func_ctx.resolve_type(expr, &module.types).scalar_kind(),
2998                Some(ScalarKind::Sint | ScalarKind::Uint)
2999            ) =>
3000            {
3001                write!(self.out, "{DIV_FUNCTION}(")?;
3002                self.write_expr(module, left, func_ctx)?;
3003                write!(self.out, ", ")?;
3004                self.write_expr(module, right, func_ctx)?;
3005                write!(self.out, ")")?;
3006            }
3007
3008            Expression::Binary {
3009                op: crate::BinaryOperator::Modulo,
3010                left,
3011                right,
3012            } if matches!(
3013                func_ctx.resolve_type(expr, &module.types).scalar_kind(),
3014                Some(ScalarKind::Sint | ScalarKind::Uint | ScalarKind::Float)
3015            ) =>
3016            {
3017                write!(self.out, "{MOD_FUNCTION}(")?;
3018                self.write_expr(module, left, func_ctx)?;
3019                write!(self.out, ", ")?;
3020                self.write_expr(module, right, func_ctx)?;
3021                write!(self.out, ")")?;
3022            }
3023
3024            Expression::Binary { op, left, right } => {
3025                write!(self.out, "(")?;
3026                self.write_expr(module, left, func_ctx)?;
3027                write!(self.out, " {} ", back::binary_operation_str(op))?;
3028                self.write_expr(module, right, func_ctx)?;
3029                write!(self.out, ")")?;
3030            }
3031            Expression::Access { base, index } => {
3032                if let Some(crate::AddressSpace::Storage { .. }) =
3033                    func_ctx.resolve_type(expr, &module.types).pointer_space()
3034                {
3035                    // do nothing, the chain is written on `Load`/`Store`
3036                } else {
3037                    // We use the function __get_col_of_matCx2 here in cases
3038                    // where `base`s type resolves to a matCx2 and is part of a
3039                    // struct member with type of (possibly nested) array of matCx2's.
3040                    //
3041                    // Note that this only works for `Load`s and we handle
3042                    // `Store`s differently in `Statement::Store`.
3043                    if let Some(MatrixType {
3044                        columns,
3045                        rows: crate::VectorSize::Bi,
3046                        width: 4,
3047                    }) = get_inner_matrix_of_struct_array_member(module, base, func_ctx, true)
3048                        .or_else(|| get_global_uniform_matrix(module, base, func_ctx))
3049                    {
3050                        write!(self.out, "__get_col_of_mat{}x2(", columns as u8)?;
3051                        self.write_expr(module, base, func_ctx)?;
3052                        write!(self.out, ", ")?;
3053                        self.write_expr(module, index, func_ctx)?;
3054                        write!(self.out, ")")?;
3055                        return Ok(());
3056                    }
3057
3058                    let resolved = func_ctx.resolve_type(base, &module.types);
3059
3060                    let (indexing_binding_array, non_uniform_qualifier) = match *resolved {
3061                        TypeInner::BindingArray { .. } => {
3062                            let uniformity = &func_ctx.info[index].uniformity;
3063
3064                            (true, uniformity.non_uniform_result.is_some())
3065                        }
3066                        _ => (false, false),
3067                    };
3068
3069                    self.write_expr(module, base, func_ctx)?;
3070
3071                    let array_sampler_info = self.sampler_binding_array_info_from_expression(
3072                        module, func_ctx, base, resolved,
3073                    );
3074
3075                    if let Some(ref info) = array_sampler_info {
3076                        write!(self.out, "{}[", info.sampler_heap_name)?;
3077                    } else {
3078                        write!(self.out, "[")?;
3079                    }
3080
3081                    let needs_bound_check = self.options.restrict_indexing
3082                        && !indexing_binding_array
3083                        && match resolved.pointer_space() {
3084                            Some(
3085                                crate::AddressSpace::Function
3086                                | crate::AddressSpace::Private
3087                                | crate::AddressSpace::WorkGroup
3088                                | crate::AddressSpace::Immediate
3089                                | crate::AddressSpace::TaskPayload,
3090                            )
3091                            | None => true,
3092                            Some(crate::AddressSpace::Uniform) => {
3093                                // check if BindTarget.restrict_indexing is set, this is used for dynamic buffers
3094                                let var_handle = self.fill_access_chain(module, base, func_ctx)?;
3095                                let bind_target = self
3096                                    .options
3097                                    .resolve_resource_binding(
3098                                        module.global_variables[var_handle]
3099                                            .binding
3100                                            .as_ref()
3101                                            .unwrap(),
3102                                    )
3103                                    .unwrap();
3104                                bind_target.restrict_indexing
3105                            }
3106                            Some(
3107                                crate::AddressSpace::Handle | crate::AddressSpace::Storage { .. },
3108                            ) => unreachable!(),
3109                        };
3110                    // Decide whether this index needs to be clamped to fall within range.
3111                    let restriction_needed = if needs_bound_check {
3112                        index::access_needs_check(
3113                            base,
3114                            index::GuardedIndex::Expression(index),
3115                            module,
3116                            func_ctx.expressions,
3117                            func_ctx.info,
3118                        )
3119                    } else {
3120                        None
3121                    };
3122                    if let Some(limit) = restriction_needed {
3123                        write!(self.out, "min(uint(")?;
3124                        self.write_expr(module, index, func_ctx)?;
3125                        write!(self.out, "), ")?;
3126                        match limit {
3127                            index::IndexableLength::Known(limit) => {
3128                                write!(self.out, "{}u", limit - 1)?;
3129                            }
3130                            index::IndexableLength::Dynamic => unreachable!(),
3131                        }
3132                        write!(self.out, ")")?;
3133                    } else {
3134                        if non_uniform_qualifier {
3135                            write!(self.out, "NonUniformResourceIndex(")?;
3136                        }
3137                        if let Some(ref info) = array_sampler_info {
3138                            write!(
3139                                self.out,
3140                                "{}[{} + ",
3141                                info.sampler_index_buffer_name, info.binding_array_base_index_name,
3142                            )?;
3143                        }
3144                        self.write_expr(module, index, func_ctx)?;
3145                        if array_sampler_info.is_some() {
3146                            write!(self.out, "]")?;
3147                        }
3148                        if non_uniform_qualifier {
3149                            write!(self.out, ")")?;
3150                        }
3151                    }
3152
3153                    write!(self.out, "]")?;
3154                }
3155            }
3156            Expression::AccessIndex { base, index } => {
3157                if let Some(crate::AddressSpace::Storage { .. }) =
3158                    func_ctx.resolve_type(expr, &module.types).pointer_space()
3159                {
3160                    // do nothing, the chain is written on `Load`/`Store`
3161                } else {
3162                    // See if we need to write the matrix column access in a
3163                    // special way since the type of `base` is our special
3164                    // __matCx2 struct.
3165                    if let Some(MatrixType {
3166                        rows: crate::VectorSize::Bi,
3167                        width: 4,
3168                        ..
3169                    }) = get_inner_matrix_of_struct_array_member(module, base, func_ctx, true)
3170                        .or_else(|| get_global_uniform_matrix(module, base, func_ctx))
3171                    {
3172                        self.write_expr(module, base, func_ctx)?;
3173                        write!(self.out, "._{index}")?;
3174                        return Ok(());
3175                    }
3176
3177                    let base_ty_res = &func_ctx.info[base].ty;
3178                    let mut resolved = base_ty_res.inner_with(&module.types);
3179                    let base_ty_handle = match *resolved {
3180                        TypeInner::Pointer { base, .. } => {
3181                            resolved = &module.types[base].inner;
3182                            Some(base)
3183                        }
3184                        _ => base_ty_res.handle(),
3185                    };
3186
3187                    // We treat matrices of the form `matCx2` as a sequence of C `vec2`s.
3188                    // See the module-level block comment in mod.rs for details.
3189                    //
3190                    // We handle matrix reconstruction here for Loads.
3191                    // Stores are handled directly by `Statement::Store`.
3192                    if let TypeInner::Struct { ref members, .. } = *resolved {
3193                        let member = &members[index as usize];
3194
3195                        match module.types[member.ty].inner {
3196                            TypeInner::Matrix {
3197                                rows: crate::VectorSize::Bi,
3198                                ..
3199                            } if member.binding.is_none() => {
3200                                let ty = base_ty_handle.unwrap();
3201                                self.write_wrapped_struct_matrix_get_function_name(
3202                                    WrappedStructMatrixAccess { ty, index },
3203                                )?;
3204                                write!(self.out, "(")?;
3205                                self.write_expr(module, base, func_ctx)?;
3206                                write!(self.out, ")")?;
3207                                return Ok(());
3208                            }
3209                            _ => {}
3210                        }
3211                    }
3212
3213                    let array_sampler_info = self.sampler_binding_array_info_from_expression(
3214                        module, func_ctx, base, resolved,
3215                    );
3216
3217                    if let Some(ref info) = array_sampler_info {
3218                        write!(
3219                            self.out,
3220                            "{}[{}",
3221                            info.sampler_heap_name, info.sampler_index_buffer_name
3222                        )?;
3223                    }
3224
3225                    self.write_expr(module, base, func_ctx)?;
3226
3227                    match *resolved {
3228                        // We specifically lift the ValuePointer to this case. While `[0]` is valid
3229                        // HLSL for any vector behind a value pointer, FXC completely miscompiles
3230                        // it and generates completely nonsensical DXBC.
3231                        //
3232                        // See https://github.com/gfx-rs/naga/issues/2095 for more details.
3233                        TypeInner::Vector { .. } | TypeInner::ValuePointer { .. } => {
3234                            // Write vector access as a swizzle
3235                            write!(self.out, ".{}", back::COMPONENTS[index as usize])?
3236                        }
3237                        TypeInner::Matrix { .. }
3238                        | TypeInner::Array { .. }
3239                        | TypeInner::BindingArray { .. } => {
3240                            if let Some(ref info) = array_sampler_info {
3241                                write!(
3242                                    self.out,
3243                                    "[{} + {index}]",
3244                                    info.binding_array_base_index_name
3245                                )?;
3246                            } else {
3247                                write!(self.out, "[{index}]")?;
3248                            }
3249                        }
3250                        TypeInner::Struct { .. } => {
3251                            // This will never panic in case the type is a `Struct`, this is not true
3252                            // for other types so we can only check while inside this match arm
3253                            let ty = base_ty_handle.unwrap();
3254
3255                            write!(
3256                                self.out,
3257                                ".{}",
3258                                &self.names[&NameKey::StructMember(ty, index)]
3259                            )?
3260                        }
3261                        ref other => return Err(Error::Custom(format!("Cannot index {other:?}"))),
3262                    }
3263
3264                    if array_sampler_info.is_some() {
3265                        write!(self.out, "]")?;
3266                    }
3267                }
3268            }
3269            Expression::FunctionArgument(pos) => {
3270                let ty = func_ctx.resolve_type(expr, &module.types);
3271
3272                // We know that any external texture function argument has been expanded into
3273                // separate consecutive arguments for each plane and the parameters buffer. And we
3274                // also know that external textures can only ever be used as an argument to another
3275                // function. Therefore we can simply emit each of the expanded arguments in a
3276                // consecutive comma-separated list.
3277                if let TypeInner::Image {
3278                    class: crate::ImageClass::External,
3279                    ..
3280                } = *ty
3281                {
3282                    let plane_names = [0, 1, 2].map(|i| {
3283                        &self.names[&func_ctx
3284                            .external_texture_argument_key(pos, ExternalTextureNameKey::Plane(i))]
3285                    });
3286                    let params_name = &self.names[&func_ctx
3287                        .external_texture_argument_key(pos, ExternalTextureNameKey::Params)];
3288                    write!(
3289                        self.out,
3290                        "{}, {}, {}, {}",
3291                        plane_names[0], plane_names[1], plane_names[2], params_name
3292                    )?;
3293                } else {
3294                    let key = func_ctx.argument_key(pos);
3295                    let name = &self.names[&key];
3296                    write!(self.out, "{name}")?;
3297                }
3298            }
3299            Expression::ImageSample {
3300                coordinate,
3301                image,
3302                sampler,
3303                clamp_to_edge: true,
3304                gather: None,
3305                array_index: None,
3306                offset: None,
3307                level: crate::SampleLevel::Zero,
3308                depth_ref: None,
3309            } => {
3310                write!(self.out, "{IMAGE_SAMPLE_BASE_CLAMP_TO_EDGE_FUNCTION}(")?;
3311                self.write_expr(module, image, func_ctx)?;
3312                write!(self.out, ", ")?;
3313                self.write_expr(module, sampler, func_ctx)?;
3314                write!(self.out, ", ")?;
3315                self.write_expr(module, coordinate, func_ctx)?;
3316                write!(self.out, ")")?;
3317            }
3318            Expression::ImageSample {
3319                image,
3320                sampler,
3321                gather,
3322                coordinate,
3323                array_index,
3324                offset,
3325                level,
3326                depth_ref,
3327                clamp_to_edge,
3328            } => {
3329                if clamp_to_edge {
3330                    return Err(Error::Custom(
3331                        "ImageSample::clamp_to_edge should have been validated out".to_string(),
3332                    ));
3333                }
3334
3335                use crate::SampleLevel as Sl;
3336                const COMPONENTS: [&str; 4] = ["", "Green", "Blue", "Alpha"];
3337
3338                let (base_str, component_str) = match gather {
3339                    Some(component) => ("Gather", COMPONENTS[component as usize]),
3340                    None => ("Sample", ""),
3341                };
3342                let cmp_str = match depth_ref {
3343                    Some(_) => "Cmp",
3344                    None => "",
3345                };
3346                let level_str = match level {
3347                    Sl::Zero if gather.is_none() => "LevelZero",
3348                    Sl::Auto | Sl::Zero => "",
3349                    Sl::Exact(_) => "Level",
3350                    Sl::Bias(_) => "Bias",
3351                    Sl::Gradient { .. } => "Grad",
3352                };
3353
3354                self.write_expr(module, image, func_ctx)?;
3355                write!(self.out, ".{base_str}{cmp_str}{component_str}{level_str}(")?;
3356                self.write_expr(module, sampler, func_ctx)?;
3357                write!(self.out, ", ")?;
3358                self.write_texture_coordinates(
3359                    "float",
3360                    coordinate,
3361                    array_index,
3362                    None,
3363                    module,
3364                    func_ctx,
3365                )?;
3366
3367                if let Some(depth_ref) = depth_ref {
3368                    write!(self.out, ", ")?;
3369                    self.write_expr(module, depth_ref, func_ctx)?;
3370                }
3371
3372                match level {
3373                    Sl::Auto | Sl::Zero => {}
3374                    Sl::Exact(expr) => {
3375                        write!(self.out, ", ")?;
3376                        self.write_expr(module, expr, func_ctx)?;
3377                    }
3378                    Sl::Bias(expr) => {
3379                        write!(self.out, ", ")?;
3380                        self.write_expr(module, expr, func_ctx)?;
3381                    }
3382                    Sl::Gradient { x, y } => {
3383                        write!(self.out, ", ")?;
3384                        self.write_expr(module, x, func_ctx)?;
3385                        write!(self.out, ", ")?;
3386                        self.write_expr(module, y, func_ctx)?;
3387                    }
3388                }
3389
3390                if let Some(offset) = offset {
3391                    write!(self.out, ", ")?;
3392                    write!(self.out, "int2(")?; // work around https://github.com/microsoft/DirectXShaderCompiler/issues/5082#issuecomment-1540147807
3393                    self.write_const_expression(module, offset, func_ctx.expressions)?;
3394                    write!(self.out, ")")?;
3395                }
3396
3397                write!(self.out, ")")?;
3398            }
3399            Expression::ImageQuery { image, query } => {
3400                // use wrapped image query function
3401                if let TypeInner::Image {
3402                    dim,
3403                    arrayed,
3404                    class,
3405                } = *func_ctx.resolve_type(image, &module.types)
3406                {
3407                    let wrapped_image_query = WrappedImageQuery {
3408                        dim,
3409                        arrayed,
3410                        class,
3411                        query: query.into(),
3412                    };
3413
3414                    self.write_wrapped_image_query_function_name(wrapped_image_query)?;
3415                    write!(self.out, "(")?;
3416                    // Image always first param
3417                    self.write_expr(module, image, func_ctx)?;
3418                    if let crate::ImageQuery::Size { level: Some(level) } = query {
3419                        write!(self.out, ", ")?;
3420                        self.write_expr(module, level, func_ctx)?;
3421                    }
3422                    write!(self.out, ")")?;
3423                }
3424            }
3425            Expression::ImageLoad {
3426                image,
3427                coordinate,
3428                array_index,
3429                sample,
3430                level,
3431            } => self.write_image_load(
3432                &module,
3433                expr,
3434                func_ctx,
3435                image,
3436                coordinate,
3437                array_index,
3438                sample,
3439                level,
3440            )?,
3441            Expression::GlobalVariable(handle) => {
3442                let global_variable = &module.global_variables[handle];
3443                let ty = &module.types[global_variable.ty].inner;
3444
3445                // In the case of binding arrays of samplers, we need to not write anything
3446                // as the we are in the wrong position to fully write the expression.
3447                //
3448                // The entire writing is done by AccessIndex.
3449                let is_binding_array_of_samplers = match *ty {
3450                    TypeInner::BindingArray { base, .. } => {
3451                        let base_ty = &module.types[base].inner;
3452                        matches!(*base_ty, TypeInner::Sampler { .. })
3453                    }
3454                    _ => false,
3455                };
3456
3457                let is_storage_space =
3458                    matches!(global_variable.space, crate::AddressSpace::Storage { .. });
3459
3460                // Our external texture global variable has been expanded into multiple
3461                // global variables, one for each plane and the parameters buffer.
3462                // External textures can only ever be used as arguments to a function
3463                // call, and we know that an external texture argument to any function
3464                // will have been expanded to separate consecutive arguments for each
3465                // plane and the parameters buffer. Therefore we can simply emit each of
3466                // the expanded global variables in a consecutive comma-separated list.
3467                if let TypeInner::Image {
3468                    class: crate::ImageClass::External,
3469                    ..
3470                } = *ty
3471                {
3472                    let plane_names = [0, 1, 2].map(|i| {
3473                        &self.names[&NameKey::ExternalTextureGlobalVariable(
3474                            handle,
3475                            ExternalTextureNameKey::Plane(i),
3476                        )]
3477                    });
3478                    let params_name = &self.names[&NameKey::ExternalTextureGlobalVariable(
3479                        handle,
3480                        ExternalTextureNameKey::Params,
3481                    )];
3482                    write!(
3483                        self.out,
3484                        "{}, {}, {}, {}",
3485                        plane_names[0], plane_names[1], plane_names[2], params_name
3486                    )?;
3487                } else if !is_binding_array_of_samplers && !is_storage_space {
3488                    let name = &self.names[&NameKey::GlobalVariable(handle)];
3489                    write!(self.out, "{name}")?;
3490                }
3491            }
3492            Expression::LocalVariable(handle) => {
3493                write!(self.out, "{}", self.names[&func_ctx.name_key(handle)])?
3494            }
3495            Expression::Load { pointer } => {
3496                match func_ctx
3497                    .resolve_type(pointer, &module.types)
3498                    .pointer_space()
3499                {
3500                    Some(crate::AddressSpace::Storage { .. }) => {
3501                        let var_handle = self.fill_access_chain(module, pointer, func_ctx)?;
3502                        let result_ty = func_ctx.info[expr].ty.clone();
3503                        self.write_storage_load(module, var_handle, result_ty, func_ctx)?;
3504                    }
3505                    _ => {
3506                        let mut close_paren = false;
3507
3508                        // We cast the value loaded to a native HLSL floatCx2
3509                        // in cases where it is of type:
3510                        //  - __matCx2 or
3511                        //  - a (possibly nested) array of __matCx2's
3512                        if let Some(MatrixType {
3513                            rows: crate::VectorSize::Bi,
3514                            width: 4,
3515                            ..
3516                        }) = get_inner_matrix_of_struct_array_member(
3517                            module, pointer, func_ctx, false,
3518                        )
3519                        .or_else(|| get_inner_matrix_of_global_uniform(module, pointer, func_ctx))
3520                        {
3521                            let mut resolved = func_ctx.resolve_type(pointer, &module.types);
3522                            let ptr_tr = resolved.pointer_base_type();
3523                            if let Some(ptr_ty) =
3524                                ptr_tr.as_ref().map(|tr| tr.inner_with(&module.types))
3525                            {
3526                                resolved = ptr_ty;
3527                            }
3528
3529                            write!(self.out, "((")?;
3530                            if let TypeInner::Array { base, size, .. } = *resolved {
3531                                self.write_type(module, base)?;
3532                                self.write_array_size(module, base, size)?;
3533                            } else {
3534                                self.write_value_type(module, resolved)?;
3535                            }
3536                            write!(self.out, ")")?;
3537                            close_paren = true;
3538                        }
3539
3540                        self.write_expr(module, pointer, func_ctx)?;
3541
3542                        if close_paren {
3543                            write!(self.out, ")")?;
3544                        }
3545                    }
3546                }
3547            }
3548            Expression::Unary { op, expr } => {
3549                // https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-operators#unary-operators
3550                let op_str = match op {
3551                    crate::UnaryOperator::Negate => {
3552                        match func_ctx.resolve_type(expr, &module.types).scalar() {
3553                            Some(Scalar::I32) => NEG_FUNCTION,
3554                            _ => "-",
3555                        }
3556                    }
3557                    crate::UnaryOperator::LogicalNot => "!",
3558                    crate::UnaryOperator::BitwiseNot => "~",
3559                };
3560                write!(self.out, "{op_str}(")?;
3561                self.write_expr(module, expr, func_ctx)?;
3562                write!(self.out, ")")?;
3563            }
3564            Expression::As {
3565                expr,
3566                kind,
3567                convert,
3568            } => {
3569                let inner = func_ctx.resolve_type(expr, &module.types);
3570                if inner.scalar_kind() == Some(ScalarKind::Float)
3571                    && (kind == ScalarKind::Sint || kind == ScalarKind::Uint)
3572                    && convert.is_some()
3573                {
3574                    // Use helper functions for float to int casts in order to
3575                    // avoid undefined behaviour when value is out of range for
3576                    // the target type.
3577                    let fun_name = match (kind, convert) {
3578                        (ScalarKind::Sint, Some(4)) => F2I32_FUNCTION,
3579                        (ScalarKind::Uint, Some(4)) => F2U32_FUNCTION,
3580                        (ScalarKind::Sint, Some(8)) => F2I64_FUNCTION,
3581                        (ScalarKind::Uint, Some(8)) => F2U64_FUNCTION,
3582                        _ => unreachable!(),
3583                    };
3584                    write!(self.out, "{fun_name}(")?;
3585                    self.write_expr(module, expr, func_ctx)?;
3586                    write!(self.out, ")")?;
3587                } else {
3588                    let close_paren = match convert {
3589                        Some(dst_width) => {
3590                            let scalar = Scalar {
3591                                kind,
3592                                width: dst_width,
3593                            };
3594                            match *inner {
3595                                TypeInner::Vector { size, .. } => {
3596                                    write!(
3597                                        self.out,
3598                                        "{}{}(",
3599                                        scalar.to_hlsl_str()?,
3600                                        common::vector_size_str(size)
3601                                    )?;
3602                                }
3603                                TypeInner::Scalar(_) => {
3604                                    write!(self.out, "{}(", scalar.to_hlsl_str()?,)?;
3605                                }
3606                                TypeInner::Matrix { columns, rows, .. } => {
3607                                    write!(
3608                                        self.out,
3609                                        "{}{}x{}(",
3610                                        scalar.to_hlsl_str()?,
3611                                        common::vector_size_str(columns),
3612                                        common::vector_size_str(rows)
3613                                    )?;
3614                                }
3615                                _ => {
3616                                    return Err(Error::Unimplemented(format!(
3617                                        "write_expr expression::as {inner:?}"
3618                                    )));
3619                                }
3620                            };
3621                            true
3622                        }
3623                        None => {
3624                            if inner.scalar_width() == Some(8) {
3625                                false
3626                            } else {
3627                                write!(self.out, "{}(", kind.to_hlsl_cast(),)?;
3628                                true
3629                            }
3630                        }
3631                    };
3632                    self.write_expr(module, expr, func_ctx)?;
3633                    if close_paren {
3634                        write!(self.out, ")")?;
3635                    }
3636                }
3637            }
3638            Expression::Math {
3639                fun,
3640                arg,
3641                arg1,
3642                arg2,
3643                arg3,
3644            } => {
3645                use crate::MathFunction as Mf;
3646
3647                enum Function {
3648                    Asincosh { is_sin: bool },
3649                    Atanh,
3650                    Pack2x16float,
3651                    Pack2x16snorm,
3652                    Pack2x16unorm,
3653                    Pack4x8snorm,
3654                    Pack4x8unorm,
3655                    Pack4xI8,
3656                    Pack4xU8,
3657                    Pack4xI8Clamp,
3658                    Pack4xU8Clamp,
3659                    Unpack2x16float,
3660                    Unpack2x16snorm,
3661                    Unpack2x16unorm,
3662                    Unpack4x8snorm,
3663                    Unpack4x8unorm,
3664                    Unpack4xI8,
3665                    Unpack4xU8,
3666                    Dot4I8Packed,
3667                    Dot4U8Packed,
3668                    QuantizeToF16,
3669                    Regular(&'static str),
3670                    MissingIntOverload(&'static str),
3671                    MissingIntReturnType(&'static str),
3672                    CountTrailingZeros,
3673                    CountLeadingZeros,
3674                }
3675
3676                let fun = match fun {
3677                    // comparison
3678                    Mf::Abs => match func_ctx.resolve_type(arg, &module.types).scalar() {
3679                        Some(Scalar::I32) => Function::Regular(ABS_FUNCTION),
3680                        _ => Function::Regular("abs"),
3681                    },
3682                    Mf::Min => Function::Regular("min"),
3683                    Mf::Max => Function::Regular("max"),
3684                    Mf::Clamp => Function::Regular("clamp"),
3685                    Mf::Saturate => Function::Regular("saturate"),
3686                    // trigonometry
3687                    Mf::Cos => Function::Regular("cos"),
3688                    Mf::Cosh => Function::Regular("cosh"),
3689                    Mf::Sin => Function::Regular("sin"),
3690                    Mf::Sinh => Function::Regular("sinh"),
3691                    Mf::Tan => Function::Regular("tan"),
3692                    Mf::Tanh => Function::Regular("tanh"),
3693                    Mf::Acos => Function::Regular("acos"),
3694                    Mf::Asin => Function::Regular("asin"),
3695                    Mf::Atan => Function::Regular("atan"),
3696                    Mf::Atan2 => Function::Regular("atan2"),
3697                    Mf::Asinh => Function::Asincosh { is_sin: true },
3698                    Mf::Acosh => Function::Asincosh { is_sin: false },
3699                    Mf::Atanh => Function::Atanh,
3700                    Mf::Radians => Function::Regular("radians"),
3701                    Mf::Degrees => Function::Regular("degrees"),
3702                    // decomposition
3703                    Mf::Ceil => Function::Regular("ceil"),
3704                    Mf::Floor => Function::Regular("floor"),
3705                    Mf::Round => Function::Regular("round"),
3706                    Mf::Fract => Function::Regular("frac"),
3707                    Mf::Trunc => Function::Regular("trunc"),
3708                    Mf::Modf => Function::Regular(MODF_FUNCTION),
3709                    Mf::Frexp => Function::Regular(FREXP_FUNCTION),
3710                    Mf::Ldexp => Function::Regular("ldexp"),
3711                    // exponent
3712                    Mf::Exp => Function::Regular("exp"),
3713                    Mf::Exp2 => Function::Regular("exp2"),
3714                    Mf::Log => Function::Regular("log"),
3715                    Mf::Log2 => Function::Regular("log2"),
3716                    Mf::Pow => Function::Regular("pow"),
3717                    // geometry
3718                    Mf::Dot => Function::Regular("dot"),
3719                    Mf::Dot4I8Packed => Function::Dot4I8Packed,
3720                    Mf::Dot4U8Packed => Function::Dot4U8Packed,
3721                    //Mf::Outer => ,
3722                    Mf::Cross => Function::Regular("cross"),
3723                    Mf::Distance => Function::Regular("distance"),
3724                    Mf::Length => Function::Regular("length"),
3725                    Mf::Normalize => Function::Regular("normalize"),
3726                    Mf::FaceForward => Function::Regular("faceforward"),
3727                    Mf::Reflect => Function::Regular("reflect"),
3728                    Mf::Refract => Function::Regular("refract"),
3729                    // computational
3730                    Mf::Sign => Function::Regular("sign"),
3731                    Mf::Fma => Function::Regular("mad"),
3732                    Mf::Mix => Function::Regular("lerp"),
3733                    Mf::Step => Function::Regular("step"),
3734                    Mf::SmoothStep => Function::Regular("smoothstep"),
3735                    Mf::Sqrt => Function::Regular("sqrt"),
3736                    Mf::InverseSqrt => Function::Regular("rsqrt"),
3737                    //Mf::Inverse =>,
3738                    Mf::Transpose => Function::Regular("transpose"),
3739                    Mf::Determinant => Function::Regular("determinant"),
3740                    Mf::QuantizeToF16 => Function::QuantizeToF16,
3741                    // bits
3742                    Mf::CountTrailingZeros => Function::CountTrailingZeros,
3743                    Mf::CountLeadingZeros => Function::CountLeadingZeros,
3744                    Mf::CountOneBits => Function::MissingIntOverload("countbits"),
3745                    Mf::ReverseBits => Function::MissingIntOverload("reversebits"),
3746                    Mf::FirstTrailingBit => Function::MissingIntReturnType("firstbitlow"),
3747                    Mf::FirstLeadingBit => Function::MissingIntReturnType("firstbithigh"),
3748                    Mf::ExtractBits => Function::Regular(EXTRACT_BITS_FUNCTION),
3749                    Mf::InsertBits => Function::Regular(INSERT_BITS_FUNCTION),
3750                    // Data Packing
3751                    Mf::Pack2x16float => Function::Pack2x16float,
3752                    Mf::Pack2x16snorm => Function::Pack2x16snorm,
3753                    Mf::Pack2x16unorm => Function::Pack2x16unorm,
3754                    Mf::Pack4x8snorm => Function::Pack4x8snorm,
3755                    Mf::Pack4x8unorm => Function::Pack4x8unorm,
3756                    Mf::Pack4xI8 => Function::Pack4xI8,
3757                    Mf::Pack4xU8 => Function::Pack4xU8,
3758                    Mf::Pack4xI8Clamp => Function::Pack4xI8Clamp,
3759                    Mf::Pack4xU8Clamp => Function::Pack4xU8Clamp,
3760                    // Data Unpacking
3761                    Mf::Unpack2x16float => Function::Unpack2x16float,
3762                    Mf::Unpack2x16snorm => Function::Unpack2x16snorm,
3763                    Mf::Unpack2x16unorm => Function::Unpack2x16unorm,
3764                    Mf::Unpack4x8snorm => Function::Unpack4x8snorm,
3765                    Mf::Unpack4x8unorm => Function::Unpack4x8unorm,
3766                    Mf::Unpack4xI8 => Function::Unpack4xI8,
3767                    Mf::Unpack4xU8 => Function::Unpack4xU8,
3768                    _ => return Err(Error::Unimplemented(format!("write_expr_math {fun:?}"))),
3769                };
3770
3771                match fun {
3772                    Function::Asincosh { is_sin } => {
3773                        write!(self.out, "log(")?;
3774                        self.write_expr(module, arg, func_ctx)?;
3775                        write!(self.out, " + sqrt(")?;
3776                        self.write_expr(module, arg, func_ctx)?;
3777                        write!(self.out, " * ")?;
3778                        self.write_expr(module, arg, func_ctx)?;
3779                        match is_sin {
3780                            true => write!(self.out, " + 1.0))")?,
3781                            false => write!(self.out, " - 1.0))")?,
3782                        }
3783                    }
3784                    Function::Atanh => {
3785                        write!(self.out, "0.5 * log((1.0 + ")?;
3786                        self.write_expr(module, arg, func_ctx)?;
3787                        write!(self.out, ") / (1.0 - ")?;
3788                        self.write_expr(module, arg, func_ctx)?;
3789                        write!(self.out, "))")?;
3790                    }
3791                    Function::Pack2x16float => {
3792                        write!(self.out, "(f32tof16(")?;
3793                        self.write_expr(module, arg, func_ctx)?;
3794                        write!(self.out, "[0]) | f32tof16(")?;
3795                        self.write_expr(module, arg, func_ctx)?;
3796                        write!(self.out, "[1]) << 16)")?;
3797                    }
3798                    Function::Pack2x16snorm => {
3799                        let scale = 32767;
3800
3801                        write!(self.out, "uint((int(round(clamp(")?;
3802                        self.write_expr(module, arg, func_ctx)?;
3803                        write!(
3804                            self.out,
3805                            "[0], -1.0, 1.0) * {scale}.0)) & 0xFFFF) | ((int(round(clamp("
3806                        )?;
3807                        self.write_expr(module, arg, func_ctx)?;
3808                        write!(self.out, "[1], -1.0, 1.0) * {scale}.0)) & 0xFFFF) << 16))",)?;
3809                    }
3810                    Function::Pack2x16unorm => {
3811                        let scale = 65535;
3812
3813                        write!(self.out, "(uint(round(clamp(")?;
3814                        self.write_expr(module, arg, func_ctx)?;
3815                        write!(self.out, "[0], 0.0, 1.0) * {scale}.0)) | uint(round(clamp(")?;
3816                        self.write_expr(module, arg, func_ctx)?;
3817                        write!(self.out, "[1], 0.0, 1.0) * {scale}.0)) << 16)")?;
3818                    }
3819                    Function::Pack4x8snorm => {
3820                        let scale = 127;
3821
3822                        write!(self.out, "uint((int(round(clamp(")?;
3823                        self.write_expr(module, arg, func_ctx)?;
3824                        write!(
3825                            self.out,
3826                            "[0], -1.0, 1.0) * {scale}.0)) & 0xFF) | ((int(round(clamp("
3827                        )?;
3828                        self.write_expr(module, arg, func_ctx)?;
3829                        write!(
3830                            self.out,
3831                            "[1], -1.0, 1.0) * {scale}.0)) & 0xFF) << 8) | ((int(round(clamp("
3832                        )?;
3833                        self.write_expr(module, arg, func_ctx)?;
3834                        write!(
3835                            self.out,
3836                            "[2], -1.0, 1.0) * {scale}.0)) & 0xFF) << 16) | ((int(round(clamp("
3837                        )?;
3838                        self.write_expr(module, arg, func_ctx)?;
3839                        write!(self.out, "[3], -1.0, 1.0) * {scale}.0)) & 0xFF) << 24))",)?;
3840                    }
3841                    Function::Pack4x8unorm => {
3842                        let scale = 255;
3843
3844                        write!(self.out, "(uint(round(clamp(")?;
3845                        self.write_expr(module, arg, func_ctx)?;
3846                        write!(self.out, "[0], 0.0, 1.0) * {scale}.0)) | uint(round(clamp(")?;
3847                        self.write_expr(module, arg, func_ctx)?;
3848                        write!(
3849                            self.out,
3850                            "[1], 0.0, 1.0) * {scale}.0)) << 8 | uint(round(clamp("
3851                        )?;
3852                        self.write_expr(module, arg, func_ctx)?;
3853                        write!(
3854                            self.out,
3855                            "[2], 0.0, 1.0) * {scale}.0)) << 16 | uint(round(clamp("
3856                        )?;
3857                        self.write_expr(module, arg, func_ctx)?;
3858                        write!(self.out, "[3], 0.0, 1.0) * {scale}.0)) << 24)")?;
3859                    }
3860                    fun @ (Function::Pack4xI8
3861                    | Function::Pack4xU8
3862                    | Function::Pack4xI8Clamp
3863                    | Function::Pack4xU8Clamp) => {
3864                        let was_signed =
3865                            matches!(fun, Function::Pack4xI8 | Function::Pack4xI8Clamp);
3866                        let clamp_bounds = match fun {
3867                            Function::Pack4xI8Clamp => Some(("-128", "127")),
3868                            Function::Pack4xU8Clamp => Some(("0", "255")),
3869                            _ => None,
3870                        };
3871                        if was_signed {
3872                            write!(self.out, "uint(")?;
3873                        }
3874                        let write_arg = |this: &mut Self| -> BackendResult {
3875                            if let Some((min, max)) = clamp_bounds {
3876                                write!(this.out, "clamp(")?;
3877                                this.write_expr(module, arg, func_ctx)?;
3878                                write!(this.out, ", {min}, {max})")?;
3879                            } else {
3880                                this.write_expr(module, arg, func_ctx)?;
3881                            }
3882                            Ok(())
3883                        };
3884                        write!(self.out, "(")?;
3885                        write_arg(self)?;
3886                        write!(self.out, "[0] & 0xFF) | ((")?;
3887                        write_arg(self)?;
3888                        write!(self.out, "[1] & 0xFF) << 8) | ((")?;
3889                        write_arg(self)?;
3890                        write!(self.out, "[2] & 0xFF) << 16) | ((")?;
3891                        write_arg(self)?;
3892                        write!(self.out, "[3] & 0xFF) << 24)")?;
3893                        if was_signed {
3894                            write!(self.out, ")")?;
3895                        }
3896                    }
3897
3898                    Function::Unpack2x16float => {
3899                        write!(self.out, "float2(f16tof32(")?;
3900                        self.write_expr(module, arg, func_ctx)?;
3901                        write!(self.out, "), f16tof32((")?;
3902                        self.write_expr(module, arg, func_ctx)?;
3903                        write!(self.out, ") >> 16))")?;
3904                    }
3905                    Function::Unpack2x16snorm => {
3906                        let scale = 32767;
3907
3908                        write!(self.out, "(float2(int2(")?;
3909                        self.write_expr(module, arg, func_ctx)?;
3910                        write!(self.out, " << 16, ")?;
3911                        self.write_expr(module, arg, func_ctx)?;
3912                        write!(self.out, ") >> 16) / {scale}.0)")?;
3913                    }
3914                    Function::Unpack2x16unorm => {
3915                        let scale = 65535;
3916
3917                        write!(self.out, "(float2(")?;
3918                        self.write_expr(module, arg, func_ctx)?;
3919                        write!(self.out, " & 0xFFFF, ")?;
3920                        self.write_expr(module, arg, func_ctx)?;
3921                        write!(self.out, " >> 16) / {scale}.0)")?;
3922                    }
3923                    Function::Unpack4x8snorm => {
3924                        let scale = 127;
3925
3926                        write!(self.out, "(float4(int4(")?;
3927                        self.write_expr(module, arg, func_ctx)?;
3928                        write!(self.out, " << 24, ")?;
3929                        self.write_expr(module, arg, func_ctx)?;
3930                        write!(self.out, " << 16, ")?;
3931                        self.write_expr(module, arg, func_ctx)?;
3932                        write!(self.out, " << 8, ")?;
3933                        self.write_expr(module, arg, func_ctx)?;
3934                        write!(self.out, ") >> 24) / {scale}.0)")?;
3935                    }
3936                    Function::Unpack4x8unorm => {
3937                        let scale = 255;
3938
3939                        write!(self.out, "(float4(")?;
3940                        self.write_expr(module, arg, func_ctx)?;
3941                        write!(self.out, " & 0xFF, ")?;
3942                        self.write_expr(module, arg, func_ctx)?;
3943                        write!(self.out, " >> 8 & 0xFF, ")?;
3944                        self.write_expr(module, arg, func_ctx)?;
3945                        write!(self.out, " >> 16 & 0xFF, ")?;
3946                        self.write_expr(module, arg, func_ctx)?;
3947                        write!(self.out, " >> 24) / {scale}.0)")?;
3948                    }
3949                    fun @ (Function::Unpack4xI8 | Function::Unpack4xU8) => {
3950                        write!(self.out, "(")?;
3951                        if matches!(fun, Function::Unpack4xU8) {
3952                            write!(self.out, "u")?;
3953                        }
3954                        write!(self.out, "int4(")?;
3955                        self.write_expr(module, arg, func_ctx)?;
3956                        write!(self.out, ", ")?;
3957                        self.write_expr(module, arg, func_ctx)?;
3958                        write!(self.out, " >> 8, ")?;
3959                        self.write_expr(module, arg, func_ctx)?;
3960                        write!(self.out, " >> 16, ")?;
3961                        self.write_expr(module, arg, func_ctx)?;
3962                        write!(self.out, " >> 24) << 24 >> 24)")?;
3963                    }
3964                    fun @ (Function::Dot4I8Packed | Function::Dot4U8Packed) => {
3965                        let arg1 = arg1.unwrap();
3966
3967                        if self.options.shader_model >= ShaderModel::V6_4 {
3968                            // Intrinsics `dot4add_{i, u}8packed` are available in SM 6.4 and later.
3969                            let function_name = match fun {
3970                                Function::Dot4I8Packed => "dot4add_i8packed",
3971                                Function::Dot4U8Packed => "dot4add_u8packed",
3972                                _ => unreachable!(),
3973                            };
3974                            write!(self.out, "{function_name}(")?;
3975                            self.write_expr(module, arg, func_ctx)?;
3976                            write!(self.out, ", ")?;
3977                            self.write_expr(module, arg1, func_ctx)?;
3978                            write!(self.out, ", 0)")?;
3979                        } else {
3980                            // Fall back to a polyfill as `dot4add_u8packed` is not available.
3981                            write!(self.out, "dot(")?;
3982
3983                            if matches!(fun, Function::Dot4U8Packed) {
3984                                write!(self.out, "u")?;
3985                            }
3986                            write!(self.out, "int4(")?;
3987                            self.write_expr(module, arg, func_ctx)?;
3988                            write!(self.out, ", ")?;
3989                            self.write_expr(module, arg, func_ctx)?;
3990                            write!(self.out, " >> 8, ")?;
3991                            self.write_expr(module, arg, func_ctx)?;
3992                            write!(self.out, " >> 16, ")?;
3993                            self.write_expr(module, arg, func_ctx)?;
3994                            write!(self.out, " >> 24) << 24 >> 24, ")?;
3995
3996                            if matches!(fun, Function::Dot4U8Packed) {
3997                                write!(self.out, "u")?;
3998                            }
3999                            write!(self.out, "int4(")?;
4000                            self.write_expr(module, arg1, func_ctx)?;
4001                            write!(self.out, ", ")?;
4002                            self.write_expr(module, arg1, func_ctx)?;
4003                            write!(self.out, " >> 8, ")?;
4004                            self.write_expr(module, arg1, func_ctx)?;
4005                            write!(self.out, " >> 16, ")?;
4006                            self.write_expr(module, arg1, func_ctx)?;
4007                            write!(self.out, " >> 24) << 24 >> 24)")?;
4008                        }
4009                    }
4010                    Function::QuantizeToF16 => {
4011                        write!(self.out, "f16tof32(f32tof16(")?;
4012                        self.write_expr(module, arg, func_ctx)?;
4013                        write!(self.out, "))")?;
4014                    }
4015                    Function::Regular(fun_name) => {
4016                        write!(self.out, "{fun_name}(")?;
4017                        self.write_expr(module, arg, func_ctx)?;
4018                        if let Some(arg) = arg1 {
4019                            write!(self.out, ", ")?;
4020                            self.write_expr(module, arg, func_ctx)?;
4021                        }
4022                        if let Some(arg) = arg2 {
4023                            write!(self.out, ", ")?;
4024                            self.write_expr(module, arg, func_ctx)?;
4025                        }
4026                        if let Some(arg) = arg3 {
4027                            write!(self.out, ", ")?;
4028                            self.write_expr(module, arg, func_ctx)?;
4029                        }
4030                        write!(self.out, ")")?
4031                    }
4032                    // These overloads are only missing on FXC, so this is only needed for 32bit types,
4033                    // as non-32bit types are DXC only.
4034                    Function::MissingIntOverload(fun_name) => {
4035                        let scalar_kind = func_ctx.resolve_type(arg, &module.types).scalar();
4036                        if let Some(Scalar::I32) = scalar_kind {
4037                            write!(self.out, "asint({fun_name}(asuint(")?;
4038                            self.write_expr(module, arg, func_ctx)?;
4039                            write!(self.out, ")))")?;
4040                        } else {
4041                            write!(self.out, "{fun_name}(")?;
4042                            self.write_expr(module, arg, func_ctx)?;
4043                            write!(self.out, ")")?;
4044                        }
4045                    }
4046                    // These overloads are only missing on FXC, so this is only needed for 32bit types,
4047                    // as non-32bit types are DXC only.
4048                    Function::MissingIntReturnType(fun_name) => {
4049                        let scalar_kind = func_ctx.resolve_type(arg, &module.types).scalar();
4050                        if let Some(Scalar::I32) = scalar_kind {
4051                            write!(self.out, "asint({fun_name}(")?;
4052                            self.write_expr(module, arg, func_ctx)?;
4053                            write!(self.out, "))")?;
4054                        } else {
4055                            write!(self.out, "{fun_name}(")?;
4056                            self.write_expr(module, arg, func_ctx)?;
4057                            write!(self.out, ")")?;
4058                        }
4059                    }
4060                    Function::CountTrailingZeros => {
4061                        match *func_ctx.resolve_type(arg, &module.types) {
4062                            TypeInner::Vector { size, scalar } => {
4063                                let s = match size {
4064                                    crate::VectorSize::Bi => ".xx",
4065                                    crate::VectorSize::Tri => ".xxx",
4066                                    crate::VectorSize::Quad => ".xxxx",
4067                                };
4068
4069                                let scalar_width_bits = scalar.width * 8;
4070
4071                                if scalar.kind == ScalarKind::Uint || scalar.width != 4 {
4072                                    write!(
4073                                        self.out,
4074                                        "min(({scalar_width_bits}u){s}, firstbitlow("
4075                                    )?;
4076                                    self.write_expr(module, arg, func_ctx)?;
4077                                    write!(self.out, "))")?;
4078                                } else {
4079                                    // This is only needed for the FXC path, on 32bit signed integers.
4080                                    write!(
4081                                        self.out,
4082                                        "asint(min(({scalar_width_bits}u){s}, firstbitlow("
4083                                    )?;
4084                                    self.write_expr(module, arg, func_ctx)?;
4085                                    write!(self.out, ")))")?;
4086                                }
4087                            }
4088                            TypeInner::Scalar(scalar) => {
4089                                let scalar_width_bits = scalar.width * 8;
4090
4091                                if scalar.kind == ScalarKind::Uint || scalar.width != 4 {
4092                                    write!(self.out, "min({scalar_width_bits}u, firstbitlow(")?;
4093                                    self.write_expr(module, arg, func_ctx)?;
4094                                    write!(self.out, "))")?;
4095                                } else {
4096                                    // This is only needed for the FXC path, on 32bit signed integers.
4097                                    write!(
4098                                        self.out,
4099                                        "asint(min({scalar_width_bits}u, firstbitlow("
4100                                    )?;
4101                                    self.write_expr(module, arg, func_ctx)?;
4102                                    write!(self.out, ")))")?;
4103                                }
4104                            }
4105                            _ => unreachable!(),
4106                        }
4107
4108                        return Ok(());
4109                    }
4110                    Function::CountLeadingZeros => {
4111                        match *func_ctx.resolve_type(arg, &module.types) {
4112                            TypeInner::Vector { size, scalar } => {
4113                                let s = match size {
4114                                    crate::VectorSize::Bi => ".xx",
4115                                    crate::VectorSize::Tri => ".xxx",
4116                                    crate::VectorSize::Quad => ".xxxx",
4117                                };
4118
4119                                // scalar width - 1
4120                                let constant = scalar.width * 8 - 1;
4121
4122                                if scalar.kind == ScalarKind::Uint {
4123                                    write!(self.out, "(({constant}u){s} - firstbithigh(")?;
4124                                    self.write_expr(module, arg, func_ctx)?;
4125                                    write!(self.out, "))")?;
4126                                } else {
4127                                    let conversion_func = match scalar.width {
4128                                        4 => "asint",
4129                                        _ => "",
4130                                    };
4131                                    write!(self.out, "(")?;
4132                                    self.write_expr(module, arg, func_ctx)?;
4133                                    write!(
4134                                        self.out,
4135                                        " < (0){s} ? (0){s} : ({constant}){s} - {conversion_func}(firstbithigh("
4136                                    )?;
4137                                    self.write_expr(module, arg, func_ctx)?;
4138                                    write!(self.out, ")))")?;
4139                                }
4140                            }
4141                            TypeInner::Scalar(scalar) => {
4142                                // scalar width - 1
4143                                let constant = scalar.width * 8 - 1;
4144
4145                                if let ScalarKind::Uint = scalar.kind {
4146                                    write!(self.out, "({constant}u - firstbithigh(")?;
4147                                    self.write_expr(module, arg, func_ctx)?;
4148                                    write!(self.out, "))")?;
4149                                } else {
4150                                    let conversion_func = match scalar.width {
4151                                        4 => "asint",
4152                                        _ => "",
4153                                    };
4154                                    write!(self.out, "(")?;
4155                                    self.write_expr(module, arg, func_ctx)?;
4156                                    write!(
4157                                        self.out,
4158                                        " < 0 ? 0 : {constant} - {conversion_func}(firstbithigh("
4159                                    )?;
4160                                    self.write_expr(module, arg, func_ctx)?;
4161                                    write!(self.out, ")))")?;
4162                                }
4163                            }
4164                            _ => unreachable!(),
4165                        }
4166
4167                        return Ok(());
4168                    }
4169                }
4170            }
4171            Expression::Swizzle {
4172                size,
4173                vector,
4174                pattern,
4175            } => {
4176                self.write_expr(module, vector, func_ctx)?;
4177                write!(self.out, ".")?;
4178                for &sc in pattern[..size as usize].iter() {
4179                    self.out.write_char(back::COMPONENTS[sc as usize])?;
4180                }
4181            }
4182            Expression::ArrayLength(expr) => {
4183                let var_handle = match func_ctx.expressions[expr] {
4184                    Expression::AccessIndex { base, index: _ } => {
4185                        match func_ctx.expressions[base] {
4186                            Expression::GlobalVariable(handle) => handle,
4187                            _ => unreachable!(),
4188                        }
4189                    }
4190                    Expression::GlobalVariable(handle) => handle,
4191                    _ => unreachable!(),
4192                };
4193
4194                let var = &module.global_variables[var_handle];
4195                let (offset, stride) = match module.types[var.ty].inner {
4196                    TypeInner::Array { stride, .. } => (0, stride),
4197                    TypeInner::Struct { ref members, .. } => {
4198                        let last = members.last().unwrap();
4199                        let stride = match module.types[last.ty].inner {
4200                            TypeInner::Array { stride, .. } => stride,
4201                            _ => unreachable!(),
4202                        };
4203                        (last.offset, stride)
4204                    }
4205                    _ => unreachable!(),
4206                };
4207
4208                let storage_access = match var.space {
4209                    crate::AddressSpace::Storage { access } => access,
4210                    _ => crate::StorageAccess::default(),
4211                };
4212                let wrapped_array_length = WrappedArrayLength {
4213                    writable: storage_access.contains(crate::StorageAccess::STORE),
4214                };
4215
4216                write!(self.out, "((")?;
4217                self.write_wrapped_array_length_function_name(wrapped_array_length)?;
4218                let var_name = &self.names[&NameKey::GlobalVariable(var_handle)];
4219                write!(self.out, "({var_name}) - {offset}) / {stride})")?
4220            }
4221            Expression::Derivative { axis, ctrl, expr } => {
4222                use crate::{DerivativeAxis as Axis, DerivativeControl as Ctrl};
4223                if axis == Axis::Width && (ctrl == Ctrl::Coarse || ctrl == Ctrl::Fine) {
4224                    let tail = match ctrl {
4225                        Ctrl::Coarse => "coarse",
4226                        Ctrl::Fine => "fine",
4227                        Ctrl::None => unreachable!(),
4228                    };
4229                    write!(self.out, "abs(ddx_{tail}(")?;
4230                    self.write_expr(module, expr, func_ctx)?;
4231                    write!(self.out, ")) + abs(ddy_{tail}(")?;
4232                    self.write_expr(module, expr, func_ctx)?;
4233                    write!(self.out, "))")?
4234                } else {
4235                    let fun_str = match (axis, ctrl) {
4236                        (Axis::X, Ctrl::Coarse) => "ddx_coarse",
4237                        (Axis::X, Ctrl::Fine) => "ddx_fine",
4238                        (Axis::X, Ctrl::None) => "ddx",
4239                        (Axis::Y, Ctrl::Coarse) => "ddy_coarse",
4240                        (Axis::Y, Ctrl::Fine) => "ddy_fine",
4241                        (Axis::Y, Ctrl::None) => "ddy",
4242                        (Axis::Width, Ctrl::Coarse | Ctrl::Fine) => unreachable!(),
4243                        (Axis::Width, Ctrl::None) => "fwidth",
4244                    };
4245                    write!(self.out, "{fun_str}(")?;
4246                    self.write_expr(module, expr, func_ctx)?;
4247                    write!(self.out, ")")?
4248                }
4249            }
4250            Expression::Relational { fun, argument } => {
4251                use crate::RelationalFunction as Rf;
4252
4253                let fun_str = match fun {
4254                    Rf::All => "all",
4255                    Rf::Any => "any",
4256                    Rf::IsNan => "isnan",
4257                    Rf::IsInf => "isinf",
4258                };
4259                write!(self.out, "{fun_str}(")?;
4260                self.write_expr(module, argument, func_ctx)?;
4261                write!(self.out, ")")?
4262            }
4263            Expression::Select {
4264                condition,
4265                accept,
4266                reject,
4267            } => {
4268                write!(self.out, "(")?;
4269                self.write_expr(module, condition, func_ctx)?;
4270                write!(self.out, " ? ")?;
4271                self.write_expr(module, accept, func_ctx)?;
4272                write!(self.out, " : ")?;
4273                self.write_expr(module, reject, func_ctx)?;
4274                write!(self.out, ")")?
4275            }
4276            Expression::RayQueryGetIntersection { query, committed } => {
4277                if committed {
4278                    write!(self.out, "GetCommittedIntersection(")?;
4279                    self.write_expr(module, query, func_ctx)?;
4280                    write!(self.out, ")")?;
4281                } else {
4282                    write!(self.out, "GetCandidateIntersection(")?;
4283                    self.write_expr(module, query, func_ctx)?;
4284                    write!(self.out, ")")?;
4285                }
4286            }
4287            // Not supported yet
4288            Expression::RayQueryVertexPositions { .. } => unreachable!(),
4289            // Nothing to do here, since call expression already cached
4290            Expression::CallResult(_)
4291            | Expression::AtomicResult { .. }
4292            | Expression::WorkGroupUniformLoadResult { .. }
4293            | Expression::RayQueryProceedResult
4294            | Expression::SubgroupBallotResult
4295            | Expression::SubgroupOperationResult { .. } => {}
4296        }
4297
4298        if !closing_bracket.is_empty() {
4299            write!(self.out, "{closing_bracket}")?;
4300        }
4301        Ok(())
4302    }
4303
4304    #[allow(clippy::too_many_arguments)]
4305    fn write_image_load(
4306        &mut self,
4307        module: &&Module,
4308        expr: Handle<crate::Expression>,
4309        func_ctx: &back::FunctionCtx,
4310        image: Handle<crate::Expression>,
4311        coordinate: Handle<crate::Expression>,
4312        array_index: Option<Handle<crate::Expression>>,
4313        sample: Option<Handle<crate::Expression>>,
4314        level: Option<Handle<crate::Expression>>,
4315    ) -> Result<(), Error> {
4316        let mut wrapping_type = None;
4317        match *func_ctx.resolve_type(image, &module.types) {
4318            TypeInner::Image {
4319                class: crate::ImageClass::External,
4320                ..
4321            } => {
4322                write!(self.out, "{IMAGE_LOAD_EXTERNAL_FUNCTION}(")?;
4323                self.write_expr(module, image, func_ctx)?;
4324                write!(self.out, ", ")?;
4325                self.write_expr(module, coordinate, func_ctx)?;
4326                write!(self.out, ")")?;
4327                return Ok(());
4328            }
4329            TypeInner::Image {
4330                class: crate::ImageClass::Storage { format, .. },
4331                ..
4332            } => {
4333                if format.single_component() {
4334                    wrapping_type = Some(Scalar::from(format));
4335                }
4336            }
4337            _ => {}
4338        }
4339        if let Some(scalar) = wrapping_type {
4340            write!(
4341                self.out,
4342                "{}{}(",
4343                help::IMAGE_STORAGE_LOAD_SCALAR_WRAPPER,
4344                scalar.to_hlsl_str()?
4345            )?;
4346        }
4347        // https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-to-load
4348        self.write_expr(module, image, func_ctx)?;
4349        write!(self.out, ".Load(")?;
4350
4351        self.write_texture_coordinates("int", coordinate, array_index, level, module, func_ctx)?;
4352
4353        if let Some(sample) = sample {
4354            write!(self.out, ", ")?;
4355            self.write_expr(module, sample, func_ctx)?;
4356        }
4357
4358        // close bracket for Load function
4359        write!(self.out, ")")?;
4360
4361        if wrapping_type.is_some() {
4362            write!(self.out, ")")?;
4363        }
4364
4365        // return x component if return type is scalar
4366        if let TypeInner::Scalar(_) = *func_ctx.resolve_type(expr, &module.types) {
4367            write!(self.out, ".x")?;
4368        }
4369        Ok(())
4370    }
4371
4372    /// Find the [`BindingArraySamplerInfo`] from an expression so that such an access
4373    /// can be generated later.
4374    fn sampler_binding_array_info_from_expression(
4375        &mut self,
4376        module: &Module,
4377        func_ctx: &back::FunctionCtx<'_>,
4378        base: Handle<crate::Expression>,
4379        resolved: &TypeInner,
4380    ) -> Option<BindingArraySamplerInfo> {
4381        if let TypeInner::BindingArray {
4382            base: base_ty_handle,
4383            ..
4384        } = *resolved
4385        {
4386            let base_ty = &module.types[base_ty_handle].inner;
4387            if let TypeInner::Sampler { comparison, .. } = *base_ty {
4388                let base = &func_ctx.expressions[base];
4389
4390                if let crate::Expression::GlobalVariable(handle) = *base {
4391                    let variable = &module.global_variables[handle];
4392
4393                    let sampler_heap_name = match comparison {
4394                        true => COMPARISON_SAMPLER_HEAP_VAR,
4395                        false => SAMPLER_HEAP_VAR,
4396                    };
4397
4398                    return Some(BindingArraySamplerInfo {
4399                        sampler_heap_name,
4400                        sampler_index_buffer_name: self
4401                            .wrapped
4402                            .sampler_index_buffers
4403                            .get(&super::SamplerIndexBufferKey {
4404                                group: variable.binding.unwrap().group,
4405                            })
4406                            .unwrap()
4407                            .clone(),
4408                        binding_array_base_index_name: self.names[&NameKey::GlobalVariable(handle)]
4409                            .clone(),
4410                    });
4411                }
4412            }
4413        }
4414
4415        None
4416    }
4417
4418    fn write_named_expr(
4419        &mut self,
4420        module: &Module,
4421        handle: Handle<crate::Expression>,
4422        name: String,
4423        // The expression which is being named.
4424        // Generally, this is the same as handle, except in WorkGroupUniformLoad
4425        named: Handle<crate::Expression>,
4426        ctx: &back::FunctionCtx,
4427    ) -> BackendResult {
4428        match ctx.info[named].ty {
4429            proc::TypeResolution::Handle(ty_handle) => match module.types[ty_handle].inner {
4430                TypeInner::Struct { .. } => {
4431                    let ty_name = &self.names[&NameKey::Type(ty_handle)];
4432                    write!(self.out, "{ty_name}")?;
4433                }
4434                _ => {
4435                    self.write_type(module, ty_handle)?;
4436                }
4437            },
4438            proc::TypeResolution::Value(ref inner) => {
4439                self.write_value_type(module, inner)?;
4440            }
4441        }
4442
4443        let resolved = ctx.resolve_type(named, &module.types);
4444
4445        write!(self.out, " {name}")?;
4446        // If rhs is a array type, we should write array size
4447        if let TypeInner::Array { base, size, .. } = *resolved {
4448            self.write_array_size(module, base, size)?;
4449        }
4450        write!(self.out, " = ")?;
4451        self.write_expr(module, handle, ctx)?;
4452        writeln!(self.out, ";")?;
4453        self.named_expressions.insert(named, name);
4454
4455        Ok(())
4456    }
4457
4458    /// Helper function that write default zero initialization
4459    pub(super) fn write_default_init(
4460        &mut self,
4461        module: &Module,
4462        ty: Handle<crate::Type>,
4463    ) -> BackendResult {
4464        write!(self.out, "(")?;
4465        self.write_type(module, ty)?;
4466        if let TypeInner::Array { base, size, .. } = module.types[ty].inner {
4467            self.write_array_size(module, base, size)?;
4468        }
4469        write!(self.out, ")0")?;
4470        Ok(())
4471    }
4472
4473    fn write_control_barrier(
4474        &mut self,
4475        barrier: crate::Barrier,
4476        level: back::Level,
4477    ) -> BackendResult {
4478        if barrier.contains(crate::Barrier::STORAGE) {
4479            writeln!(self.out, "{level}DeviceMemoryBarrierWithGroupSync();")?;
4480        }
4481        if barrier.contains(crate::Barrier::WORK_GROUP) {
4482            writeln!(self.out, "{level}GroupMemoryBarrierWithGroupSync();")?;
4483        }
4484        if barrier.contains(crate::Barrier::SUB_GROUP) {
4485            // Does not exist in DirectX
4486        }
4487        if barrier.contains(crate::Barrier::TEXTURE) {
4488            writeln!(self.out, "{level}DeviceMemoryBarrierWithGroupSync();")?;
4489        }
4490        Ok(())
4491    }
4492
4493    fn write_memory_barrier(
4494        &mut self,
4495        barrier: crate::Barrier,
4496        level: back::Level,
4497    ) -> BackendResult {
4498        if barrier.contains(crate::Barrier::STORAGE) {
4499            writeln!(self.out, "{level}DeviceMemoryBarrier();")?;
4500        }
4501        if barrier.contains(crate::Barrier::WORK_GROUP) {
4502            writeln!(self.out, "{level}GroupMemoryBarrier();")?;
4503        }
4504        if barrier.contains(crate::Barrier::SUB_GROUP) {
4505            // Does not exist in DirectX
4506        }
4507        if barrier.contains(crate::Barrier::TEXTURE) {
4508            writeln!(self.out, "{level}DeviceMemoryBarrier();")?;
4509        }
4510        Ok(())
4511    }
4512
4513    /// Helper to emit the shared tail of an HLSL atomic call (arguments, value, result)
4514    fn emit_hlsl_atomic_tail(
4515        &mut self,
4516        module: &Module,
4517        func_ctx: &back::FunctionCtx<'_>,
4518        fun: &crate::AtomicFunction,
4519        compare_expr: Option<Handle<crate::Expression>>,
4520        value: Handle<crate::Expression>,
4521        res_var_info: &Option<(Handle<crate::Expression>, String)>,
4522    ) -> BackendResult {
4523        if let Some(cmp) = compare_expr {
4524            write!(self.out, ", ")?;
4525            self.write_expr(module, cmp, func_ctx)?;
4526        }
4527        write!(self.out, ", ")?;
4528        if let crate::AtomicFunction::Subtract = *fun {
4529            // we just wrote `InterlockedAdd`, so negate the argument
4530            write!(self.out, "-")?;
4531        }
4532        self.write_expr(module, value, func_ctx)?;
4533        if let Some(&(_res_handle, ref res_name)) = res_var_info.as_ref() {
4534            write!(self.out, ", ")?;
4535            if compare_expr.is_some() {
4536                write!(self.out, "{res_name}.old_value")?;
4537            } else {
4538                write!(self.out, "{res_name}")?;
4539            }
4540        }
4541        writeln!(self.out, ");")?;
4542        Ok(())
4543    }
4544}
4545
4546pub(super) struct MatrixType {
4547    pub(super) columns: crate::VectorSize,
4548    pub(super) rows: crate::VectorSize,
4549    pub(super) width: crate::Bytes,
4550}
4551
4552pub(super) fn get_inner_matrix_data(
4553    module: &Module,
4554    handle: Handle<crate::Type>,
4555) -> Option<MatrixType> {
4556    match module.types[handle].inner {
4557        TypeInner::Matrix {
4558            columns,
4559            rows,
4560            scalar,
4561        } => Some(MatrixType {
4562            columns,
4563            rows,
4564            width: scalar.width,
4565        }),
4566        TypeInner::Array { base, .. } => get_inner_matrix_data(module, base),
4567        _ => None,
4568    }
4569}
4570
4571/// If `base` is an access chain of the form `mat`, `mat[col]`, or `mat[col][row]`,
4572/// returns a tuple of the matrix, the column (vector) index (if present), and
4573/// the row (scalar) index (if present).
4574fn find_matrix_in_access_chain(
4575    module: &Module,
4576    base: Handle<crate::Expression>,
4577    func_ctx: &back::FunctionCtx<'_>,
4578) -> Option<(Handle<crate::Expression>, Option<Index>, Option<Index>)> {
4579    let mut current_base = base;
4580    let mut vector = None;
4581    let mut scalar = None;
4582    loop {
4583        let resolved_tr = func_ctx
4584            .resolve_type(current_base, &module.types)
4585            .pointer_base_type();
4586        let resolved = resolved_tr.as_ref()?.inner_with(&module.types);
4587
4588        match *resolved {
4589            TypeInner::Matrix { .. } => return Some((current_base, vector, scalar)),
4590            TypeInner::Scalar(_) | TypeInner::Vector { .. } => {}
4591            _ => return None,
4592        }
4593
4594        let index;
4595        (current_base, index) = match func_ctx.expressions[current_base] {
4596            crate::Expression::Access { base, index } => (base, Index::Expression(index)),
4597            crate::Expression::AccessIndex { base, index } => (base, Index::Static(index)),
4598            _ => return None,
4599        };
4600
4601        match *resolved {
4602            TypeInner::Scalar(_) => scalar = Some(index),
4603            TypeInner::Vector { .. } => vector = Some(index),
4604            _ => unreachable!(),
4605        }
4606    }
4607}
4608
4609/// Returns the matrix data if the access chain starting at `base`:
4610/// - starts with an expression with resolved type of [`TypeInner::Matrix`] if `direct = true`
4611/// - contains one or more expressions with resolved type of [`TypeInner::Array`] of [`TypeInner::Matrix`]
4612/// - ends at an expression with resolved type of [`TypeInner::Struct`]
4613pub(super) fn get_inner_matrix_of_struct_array_member(
4614    module: &Module,
4615    base: Handle<crate::Expression>,
4616    func_ctx: &back::FunctionCtx<'_>,
4617    direct: bool,
4618) -> Option<MatrixType> {
4619    let mut mat_data = None;
4620    let mut array_base = None;
4621
4622    let mut current_base = base;
4623    loop {
4624        let mut resolved = func_ctx.resolve_type(current_base, &module.types);
4625        if let TypeInner::Pointer { base, .. } = *resolved {
4626            resolved = &module.types[base].inner;
4627        };
4628
4629        match *resolved {
4630            TypeInner::Matrix {
4631                columns,
4632                rows,
4633                scalar,
4634            } => {
4635                mat_data = Some(MatrixType {
4636                    columns,
4637                    rows,
4638                    width: scalar.width,
4639                })
4640            }
4641            TypeInner::Array { base, .. } => {
4642                array_base = Some(base);
4643            }
4644            TypeInner::Struct { .. } => {
4645                if let Some(array_base) = array_base {
4646                    if direct {
4647                        return mat_data;
4648                    } else {
4649                        return get_inner_matrix_data(module, array_base);
4650                    }
4651                }
4652
4653                break;
4654            }
4655            _ => break,
4656        }
4657
4658        current_base = match func_ctx.expressions[current_base] {
4659            crate::Expression::Access { base, .. } => base,
4660            crate::Expression::AccessIndex { base, .. } => base,
4661            _ => break,
4662        };
4663    }
4664    None
4665}
4666
4667/// Simpler version of get_inner_matrix_of_global_uniform that only looks at the
4668/// immediate expression, rather than traversing an access chain.
4669fn get_global_uniform_matrix(
4670    module: &Module,
4671    base: Handle<crate::Expression>,
4672    func_ctx: &back::FunctionCtx<'_>,
4673) -> Option<MatrixType> {
4674    let base_tr = func_ctx
4675        .resolve_type(base, &module.types)
4676        .pointer_base_type();
4677    let base_ty = base_tr.as_ref().map(|tr| tr.inner_with(&module.types));
4678    match (&func_ctx.expressions[base], base_ty) {
4679        (
4680            &crate::Expression::GlobalVariable(handle),
4681            Some(&TypeInner::Matrix {
4682                columns,
4683                rows,
4684                scalar,
4685            }),
4686        ) if module.global_variables[handle].space == crate::AddressSpace::Uniform => {
4687            Some(MatrixType {
4688                columns,
4689                rows,
4690                width: scalar.width,
4691            })
4692        }
4693        _ => None,
4694    }
4695}
4696
4697/// Returns the matrix data if the access chain starting at `base`:
4698/// - starts with an expression with resolved type of [`TypeInner::Matrix`]
4699/// - contains zero or more expressions with resolved type of [`TypeInner::Array`] of [`TypeInner::Matrix`]
4700/// - ends with an [`Expression::GlobalVariable`](crate::Expression::GlobalVariable) in [`AddressSpace::Uniform`](crate::AddressSpace::Uniform)
4701fn get_inner_matrix_of_global_uniform(
4702    module: &Module,
4703    base: Handle<crate::Expression>,
4704    func_ctx: &back::FunctionCtx<'_>,
4705) -> Option<MatrixType> {
4706    let mut mat_data = None;
4707    let mut array_base = None;
4708
4709    let mut current_base = base;
4710    loop {
4711        let mut resolved = func_ctx.resolve_type(current_base, &module.types);
4712        if let TypeInner::Pointer { base, .. } = *resolved {
4713            resolved = &module.types[base].inner;
4714        };
4715
4716        match *resolved {
4717            TypeInner::Matrix {
4718                columns,
4719                rows,
4720                scalar,
4721            } => {
4722                mat_data = Some(MatrixType {
4723                    columns,
4724                    rows,
4725                    width: scalar.width,
4726                })
4727            }
4728            TypeInner::Array { base, .. } => {
4729                array_base = Some(base);
4730            }
4731            _ => break,
4732        }
4733
4734        current_base = match func_ctx.expressions[current_base] {
4735            crate::Expression::Access { base, .. } => base,
4736            crate::Expression::AccessIndex { base, .. } => base,
4737            crate::Expression::GlobalVariable(handle)
4738                if module.global_variables[handle].space == crate::AddressSpace::Uniform =>
4739            {
4740                return mat_data.or_else(|| {
4741                    array_base.and_then(|array_base| get_inner_matrix_data(module, array_base))
4742                })
4743            }
4744            _ => break,
4745        };
4746    }
4747    None
4748}