Skip to main content

rustc_codegen_spirv/linker/
specializer.rs

1//! Specialize globals (types, constants and module-scoped variables) and functions,
2//! to legalize a SPIR-V module representing a "family" of types with a single type,
3//! by treating some globals and functions as "generic", inferring minimal sets
4//! of "generic parameters", and "monomorphizing" them (i.e. expanding them into
5//! one specialized copy per distinctly parameterized instance required).
6//!
7//! For now, this is only used for pointer type storage classes, because
8//! Rust's pointer/reference types don't have an "address space" distinction,
9//! and we also wouldn't want users to annotate every single type anyway.
10//!
11//! # Future plans
12//!
13//! Recursive data types (using `OpTypeForwardPointer`) are not supported, but
14//! here is an outline of how that could work:
15//! * groups of mutually-recursive `OpTypeForwardPointer`s are computed via SCCs
16//! * each mutual-recursive group gets a single "generic" parameter count, that all
17//!   pointer types in the group will use, and which is the sum of the "generic"
18//!   parameters of all the leaves referenced by the pointer types in the group,
19//!   ignoring the pointer types in the group themselves
20//! * once the pointer types have been assigned their "g"eneric parameter count,
21//!   the non-pointer types in each SCC - i.e. (indirectly) referenced by one of
22//!   the pointer types in the group, and which in turn (indirectly) references
23//!   a pointer type in the group - can have their "generic" parameters computed
24//!   as normal, taking care to record where in the combined lists of "generic"
25//!   parameters, any of the pointer types in the group show up
26//! * each pointer type in the group will "fan out" a copy of its full set of
27//!   "generic" parameters to every (indirect) mention of any pointer type in
28//!   the group, using an additional parameter remapping, for which `Generic`:
29//!   * requires this extra documentation:
30//!     ```
31//!     /// The one exception are `OpTypePointer`s involved in recursive data types
32//!     /// (i.e. they were declared by `OpTypeForwardPointer`s, and their pointees are
33//!     /// `OpTypeStruct`s that have the same pointer type as a leaf).
34//!     /// As the pointee `OpTypeStruct` has more parameters than the pointer (each leaf
35//!     /// use of the same pointer type requires its own copy of the pointer parameters),
36//!     /// a mapping (`expand_params`) indicates how to create the flattened list.
37//!     ```
38//!   * and this extra field:
39//!     ```
40//!     /// For every entry in the regular flattened list of parameters expected by
41//!     /// operands, this contains the parameter index (i.e. `0..self.param_count`)
42//!     /// to use for that parameter.
43//!     ///
44//!     /// For example, to duplicate `5` parameters into `10`, `expand_params`
45//!     /// would be `[0, 1, 2, 3, 4, 0, 1, 2, 3, 4]`.
46//!     ///
47//!     /// See also `Generic` documentation above for why this is needed
48//!     /// (i.e. to replicate parameters for recursive data types).
49//!     expand_params: Option<Vec<usize>>,
50//!     ```
51
52use crate::linker::ipo::CallGraph;
53use crate::spirv_type_constraints::{self, InstSig, StorageClassPat, TyListPat, TyPat};
54use indexmap::{IndexMap, IndexSet};
55use rspirv::dr::{Builder, Function, Instruction, Module, Operand};
56use rspirv::spirv::{Op, StorageClass, Word};
57use rustc_data_structures::fx::{FxHashMap, FxHashSet};
58use smallvec::SmallVec;
59use std::collections::{BTreeMap, VecDeque};
60use std::ops::{Range, RangeTo};
61use std::{fmt, io, iter, mem, slice};
62use tracing::{debug, error};
63
64// FIXME(eddyb) move this elsewhere.
65struct FmtBy<F: Fn(&mut fmt::Formatter<'_>) -> fmt::Result>(F);
66
67impl<F: Fn(&mut fmt::Formatter<'_>) -> fmt::Result> fmt::Debug for FmtBy<F> {
68    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
69        self.0(f)
70    }
71}
72
73impl<F: Fn(&mut fmt::Formatter<'_>) -> fmt::Result> fmt::Display for FmtBy<F> {
74    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75        self.0(f)
76    }
77}
78
79pub trait Specialization {
80    /// Return `true` if the specializer should replace every occurrence of
81    /// `operand` with some other inferred `Operand`.
82    fn specialize_operand(&self, operand: &Operand) -> bool;
83
84    /// The operand that should be used to replace unresolved inference variables,
85    /// i.e. the uses of operands for which `specialize_operand` returns `true`,
86    /// but which none of the instructions in the same SPIR-V function require
87    /// any particular concrete value or relate it to the function's signature,
88    /// so an arbitrary choice can be made (as long as it's valid SPIR-V etc.).
89    fn concrete_fallback(&self) -> Operand;
90}
91
92/// Helper to avoid needing an `impl` of `Specialization`, while allowing the rest
93/// of this module to use `Specialization` (instead of `Fn(&Operand) -> bool`).
94pub struct SimpleSpecialization<SO: Fn(&Operand) -> bool> {
95    pub specialize_operand: SO,
96    pub concrete_fallback: Operand,
97}
98
99impl<SO: Fn(&Operand) -> bool> Specialization for SimpleSpecialization<SO> {
100    fn specialize_operand(&self, operand: &Operand) -> bool {
101        (self.specialize_operand)(operand)
102    }
103    fn concrete_fallback(&self) -> Operand {
104        self.concrete_fallback.clone()
105    }
106}
107
108pub fn specialize(
109    opts: &super::Options,
110    module: Module,
111    specialization: impl Specialization,
112) -> Module {
113    let dump_instances = &opts.specializer_dump_instances;
114
115    let mut debug_names = FxHashMap::default();
116    if dump_instances.is_some() {
117        debug_names = module
118            .debug_names
119            .iter()
120            .filter(|inst| inst.class.opcode == Op::Name)
121            .map(|inst| {
122                (
123                    inst.operands[0].unwrap_id_ref(),
124                    inst.operands[1].unwrap_literal_string().to_string(),
125                )
126            })
127            .collect();
128    }
129
130    let mut specializer = Specializer {
131        specialization,
132        debug_names,
133        generics: IndexMap::new(),
134        int_consts: FxHashMap::default(),
135    };
136
137    specializer.collect_generics(&module);
138
139    // "Generic" module-scoped variables can be fully constrained to the point
140    // where we could theoretically always add an instance for them, in order
141    // to preserve them, even if they would appear to otherwise be unused.
142    // We do this here for fully-constrained variables used by `OpEntryPoint`s,
143    // in order to avoid a failure in `Expander::expand_module` (see #723).
144    let mut interface_concrete_instances = IndexSet::new();
145    for inst in &module.entry_points {
146        for interface_operand in &inst.operands[3..] {
147            let interface_id = interface_operand.unwrap_id_ref();
148            if let Some(generic) = specializer.generics.get(&interface_id)
149                && let Some(param_values) = &generic.param_values
150                && param_values.iter().all(|v| matches!(v, Value::Known(_)))
151            {
152                interface_concrete_instances.insert(Instance {
153                    generic_id: interface_id,
154                    generic_args: param_values
155                        .iter()
156                        .copied()
157                        .map(|v| match v {
158                            Value::Known(v) => v,
159                            _ => unreachable!(),
160                        })
161                        .collect(),
162                });
163            }
164        }
165    }
166
167    let call_graph = CallGraph::collect(&module);
168    let mut non_generic_replacements = vec![];
169    for func_idx in call_graph.post_order() {
170        if let Some(replacements) = specializer.infer_function(&module.functions[func_idx]) {
171            non_generic_replacements.push((func_idx, replacements));
172        }
173    }
174
175    let mut expander = Expander::new(&specializer, module);
176
177    // See comment above on the loop collecting `interface_concrete_instances`.
178    for interface_instance in interface_concrete_instances {
179        expander.alloc_instance_id(interface_instance);
180    }
181
182    // For non-"generic" functions, we can apply `replacements` right away,
183    // though not before finishing inference for all functions first
184    // (because `expander` needs to borrow `specializer` immutably).
185    debug!("non-generic replacements:");
186    for (func_idx, replacements) in non_generic_replacements {
187        let mut func = mem::replace(
188            &mut expander.builder.module_mut().functions[func_idx],
189            Function::new(),
190        );
191        let empty =
192            replacements.with_instance.is_empty() && replacements.with_concrete_or_param.is_empty();
193        if !empty {
194            debug!("    in %{}:", func.def_id().unwrap());
195        }
196        for (loc, operand) in
197            replacements.to_concrete(&[], |instance| expander.alloc_instance_id(instance))
198        {
199            debug!("        {operand} -> {loc:?}");
200            func.index_set(loc, operand.into());
201        }
202        expander.builder.module_mut().functions[func_idx] = func;
203    }
204    expander.propagate_instances();
205
206    if let Some(path) = dump_instances {
207        expander
208            .dump_instances(&mut std::fs::File::create(path).unwrap())
209            .unwrap();
210    }
211
212    expander.expand_module()
213}
214
215// HACK(eddyb) `Copy` version of `Operand` that only includes the cases that
216// are relevant to the inference algorithm (and is also smaller).
217#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
218enum CopyOperand {
219    IdRef(Word),
220    StorageClass(StorageClass),
221}
222
223#[derive(Debug)]
224struct NotSupportedAsCopyOperand(
225    // HACK(eddyb) only exists for `fmt::Debug` in case of error.
226    #[allow(dead_code)] Operand,
227);
228
229impl TryFrom<&Operand> for CopyOperand {
230    type Error = NotSupportedAsCopyOperand;
231    fn try_from(operand: &Operand) -> Result<Self, Self::Error> {
232        match *operand {
233            Operand::IdRef(id) => Ok(Self::IdRef(id)),
234            Operand::StorageClass(s) => Ok(Self::StorageClass(s)),
235            _ => Err(NotSupportedAsCopyOperand(operand.clone())),
236        }
237    }
238}
239
240impl From<CopyOperand> for Operand {
241    fn from(op: CopyOperand) -> Self {
242        match op {
243            CopyOperand::IdRef(id) => Self::IdRef(id),
244            CopyOperand::StorageClass(s) => Self::StorageClass(s),
245        }
246    }
247}
248
249impl fmt::Display for CopyOperand {
250    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
251        match self {
252            Self::IdRef(id) => write!(f, "%{id}"),
253            Self::StorageClass(s) => write!(f, "{s:?}"),
254        }
255    }
256}
257
258/// The "value" of a `Param`/`InferVar`, if we know anything about it.
259// FIXME(eddyb) find a more specific name.
260#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
261enum Value<T> {
262    /// The value of this `Param`/`InferVar` is completely known.
263    Unknown,
264
265    /// The value of this `Param`/`InferVar` is known to be a specific `Operand`.
266    Known(CopyOperand),
267
268    /// The value of this `Param`/`InferVar` is the same as another `Param`/`InferVar`.
269    ///
270    /// For consistency, and to allow some `Param` <-> `InferVar` mapping,
271    /// all cases of `values[y] == Value::SameAs(x)` should have `x < y`,
272    /// i.e. "newer" variables must be redirected to "older" ones.
273    SameAs(T),
274}
275
276impl<T> Value<T> {
277    fn map_var<U>(self, f: impl FnOnce(T) -> U) -> Value<U> {
278        match self {
279            Value::Unknown => Value::Unknown,
280            Value::Known(o) => Value::Known(o),
281            Value::SameAs(var) => Value::SameAs(f(var)),
282        }
283    }
284}
285
286/// Newtype'd "generic" parameter index.
287// FIXME(eddyb) use `rustc_index` for this instead.
288#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
289struct Param(u32);
290
291impl fmt::Display for Param {
292    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
293        write!(f, "${}", self.0)
294    }
295}
296
297impl Param {
298    // HACK(eddyb) this works around `Range<Param>` not being iterable
299    // because `Param` doesn't implement the (unstable) `Step` trait.
300    fn range_iter(range: &Range<Self>) -> impl Iterator<Item = Self> + Clone {
301        (range.start.0..range.end.0).map(Self)
302    }
303}
304
305/// A specific instance of a "generic" global/function.
306#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
307struct Instance<GA> {
308    generic_id: Word,
309    generic_args: GA,
310}
311
312impl<GA> Instance<GA> {
313    fn as_ref(&self) -> Instance<&GA> {
314        Instance {
315            generic_id: self.generic_id,
316            generic_args: &self.generic_args,
317        }
318    }
319
320    fn map_generic_args<T, U, GA2>(self, f: impl FnMut(T) -> U) -> Instance<GA2>
321    where
322        GA: IntoIterator<Item = T>,
323        GA2: std::iter::FromIterator<U>,
324    {
325        Instance {
326            generic_id: self.generic_id,
327            generic_args: self.generic_args.into_iter().map(f).collect(),
328        }
329    }
330
331    // FIXME(eddyb) implement `Step` for `Param` and `InferVar` instead.
332    fn display<'a, T: fmt::Display, GAI: Iterator<Item = T> + Clone>(
333        &'a self,
334        f: impl FnOnce(&'a GA) -> GAI,
335    ) -> impl fmt::Display {
336        let &Self {
337            generic_id,
338            ref generic_args,
339        } = self;
340        let generic_args_iter = f(generic_args);
341        FmtBy(move |f| {
342            write!(f, "%{generic_id}<")?;
343            for (i, arg) in generic_args_iter.clone().enumerate() {
344                if i != 0 {
345                    write!(f, ", ")?;
346                }
347                write!(f, "{arg}")?;
348            }
349            write!(f, ">")
350        })
351    }
352}
353
354#[derive(Copy, Clone, Debug, PartialEq, Eq)]
355enum InstructionLocation {
356    Module,
357    FnParam(usize),
358    FnBody {
359        /// Block index within a function.
360        block_idx: usize,
361
362        /// Instruction index within the block with index `block_idx`.
363        inst_idx: usize,
364    },
365}
366
367trait OperandIndexGetSet<I> {
368    // FIXME(eddyb) how come this isn't used? (is iteration preferred?)
369    #[allow(dead_code)]
370    fn index_get(&self, index: I) -> Operand;
371    fn index_set(&mut self, index: I, operand: Operand);
372}
373
374#[derive(Copy, Clone, Debug, PartialEq, Eq)]
375enum OperandIdx {
376    ResultType,
377    Input(usize),
378}
379
380impl OperandIndexGetSet<OperandIdx> for Instruction {
381    fn index_get(&self, idx: OperandIdx) -> Operand {
382        match idx {
383            OperandIdx::ResultType => Operand::IdRef(self.result_type.unwrap()),
384            OperandIdx::Input(i) => self.operands[i].clone(),
385        }
386    }
387    fn index_set(&mut self, idx: OperandIdx, operand: Operand) {
388        match idx {
389            OperandIdx::ResultType => self.result_type = Some(operand.unwrap_id_ref()),
390            OperandIdx::Input(i) => self.operands[i] = operand,
391        }
392    }
393}
394
395#[derive(Copy, Clone, Debug, PartialEq, Eq)]
396struct OperandLocation {
397    inst_loc: InstructionLocation,
398    operand_idx: OperandIdx,
399}
400
401impl OperandIndexGetSet<OperandLocation> for Instruction {
402    fn index_get(&self, loc: OperandLocation) -> Operand {
403        assert_eq!(loc.inst_loc, InstructionLocation::Module);
404        self.index_get(loc.operand_idx)
405    }
406    fn index_set(&mut self, loc: OperandLocation, operand: Operand) {
407        assert_eq!(loc.inst_loc, InstructionLocation::Module);
408        self.index_set(loc.operand_idx, operand);
409    }
410}
411
412impl OperandIndexGetSet<OperandLocation> for Function {
413    fn index_get(&self, loc: OperandLocation) -> Operand {
414        let inst = match loc.inst_loc {
415            InstructionLocation::Module => self.def.as_ref().unwrap(),
416            InstructionLocation::FnParam(i) => &self.parameters[i],
417            InstructionLocation::FnBody {
418                block_idx,
419                inst_idx,
420            } => &self.blocks[block_idx].instructions[inst_idx],
421        };
422        inst.index_get(loc.operand_idx)
423    }
424    fn index_set(&mut self, loc: OperandLocation, operand: Operand) {
425        let inst = match loc.inst_loc {
426            InstructionLocation::Module => self.def.as_mut().unwrap(),
427            InstructionLocation::FnParam(i) => &mut self.parameters[i],
428            InstructionLocation::FnBody {
429                block_idx,
430                inst_idx,
431            } => &mut self.blocks[block_idx].instructions[inst_idx],
432        };
433        inst.index_set(loc.operand_idx, operand);
434    }
435}
436
437// FIXME(eddyb) this is a bit like `Value<Param>` but more explicit,
438// and the name isn't too nice, but at least it's very clear.
439#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
440enum ConcreteOrParam {
441    Concrete(CopyOperand),
442    Param(Param),
443}
444
445impl ConcreteOrParam {
446    /// Replace `Param(i)` with `generic_args[i]` while preserving `Concrete`.
447    fn apply_generic_args(self, generic_args: &[CopyOperand]) -> CopyOperand {
448        match self {
449            Self::Concrete(x) => x,
450            Self::Param(Param(i)) => generic_args[i as usize],
451        }
452    }
453}
454
455#[derive(Debug)]
456struct Replacements {
457    /// Operands that need to be replaced with instances of "generic" globals.
458    /// Keyed by instance to optimize for few instances used many times.
459    // FIXME(eddyb) fine-tune the length of `SmallVec<[_; 4]>` here.
460    with_instance: IndexMap<Instance<SmallVec<[ConcreteOrParam; 4]>>, Vec<OperandLocation>>,
461
462    /// Operands that need to be replaced with a concrete operand or a parameter.
463    with_concrete_or_param: Vec<(OperandLocation, ConcreteOrParam)>,
464}
465
466impl Replacements {
467    /// Apply `generic_args` to all the `ConcreteOrParam`s in this `Replacements`
468    /// (i.e. replacing `Param(i)` with `generic_args[i]`), producing a stream of
469    /// "replace the operand at `OperandLocation` with this concrete `CopyOperand`".
470    /// The `concrete_instance_id` closure should look up and/or allocate an ID
471    /// for a specific concrete `Instance`.
472    fn to_concrete<'a>(
473        &'a self,
474        generic_args: &'a [CopyOperand],
475        mut concrete_instance_id: impl FnMut(Instance<SmallVec<[CopyOperand; 4]>>) -> Word + 'a,
476    ) -> impl Iterator<Item = (OperandLocation, CopyOperand)> + 'a {
477        self.with_instance
478            .iter()
479            .flat_map(move |(instance, locations)| {
480                let concrete = CopyOperand::IdRef(concrete_instance_id(
481                    instance
482                        .as_ref()
483                        .map_generic_args(|x| x.apply_generic_args(generic_args)),
484                ));
485                locations.iter().map(move |&loc| (loc, concrete))
486            })
487            .chain(
488                self.with_concrete_or_param
489                    .iter()
490                    .map(move |&(loc, x)| (loc, x.apply_generic_args(generic_args))),
491            )
492    }
493}
494
495/// Computed "generic" shape for a SPIR-V global/function. In the interest of efficient
496/// representation, the parameters of operands that are themselves "generic",
497/// are concatenated by default, i.e. parameters come from disjoint leaves.
498///
499/// As an example, for `%T = OpTypeStruct %A %B`, if `%A` and `%B` have 2 and 3
500/// parameters, respectively, `%T` will have `A0, A1, B0, B1, B2` as parameters.
501struct Generic {
502    param_count: u32,
503
504    /// Defining instruction for this global (`OpType...`, `OpConstant...`, etc.)
505    /// or function (`OpFunction`).
506    // FIXME(eddyb) consider using `SmallVec` for the operands, or converting
507    // the operands into something more like `InferOperand`, but that would
508    // complicate `InferOperandList`, which has to be able to iterate them.
509    def: Instruction,
510
511    /// `param_values[p]` constrains what "generic" args `Param(p)` could take.
512    /// This is only present if any constraints were inferred from the defining
513    /// instruction of a global, or the body of a function. Inference performed
514    /// after `collect_generics` (e.g. from instructions in function bodies) is
515    /// monotonic, i.e. it may only introduce more constraints, not remove any.
516    // FIXME(eddyb) use `rustc_index`'s `IndexVec` for this.
517    param_values: Option<Vec<Value<Param>>>,
518
519    /// Operand replacements that need to be performed on the defining instruction
520    /// of a global, or an entire function (including all instructions in its body),
521    /// in order to expand an instance of it.
522    replacements: Replacements,
523}
524
525struct Specializer<S: Specialization> {
526    specialization: S,
527
528    // HACK(eddyb) if debugging is requested, this is used to quickly get `OpName`s.
529    debug_names: FxHashMap<Word, String>,
530
531    // FIXME(eddyb) compact SPIR-V IDs to allow flatter maps.
532    generics: IndexMap<Word, Generic>,
533
534    /// Integer `OpConstant`s (i.e. containing a `LiteralBit32`), to be used
535    /// for interpreting `TyPat::IndexComposite` (such as for `OpAccessChain`).
536    int_consts: FxHashMap<Word, u32>,
537}
538
539impl<S: Specialization> Specializer<S> {
540    /// Returns the number of "generic" parameters `operand` "takes", either
541    /// because it's specialized by, or it refers to a "generic" global/function.
542    /// In the latter case, the `&Generic` for that global/function is also returned.
543    fn params_needed_by(&self, operand: &Operand) -> (u32, Option<&Generic>) {
544        if self.specialization.specialize_operand(operand) {
545            // Each operand we specialize by is one leaf "generic" parameter.
546            (1, None)
547        } else if let Operand::IdRef(id) = operand {
548            self.generics
549                .get(id)
550                .map_or((0, None), |generic| (generic.param_count, Some(generic)))
551        } else {
552            (0, None)
553        }
554    }
555
556    fn collect_generics(&mut self, module: &Module) {
557        // Process all defining instructions for globals (types, constants,
558        // and module-scoped variables), and functions' `OpFunction` instructions,
559        // but note that for `OpFunction`s only the signature is considered,
560        // actual inference based on bodies happens later, in `infer_function`.
561        let types_global_values_and_functions = module
562            .types_global_values
563            .iter()
564            .chain(module.functions.iter().filter_map(|f| f.def.as_ref()));
565
566        let mut forward_declared_pointers = FxHashSet::default();
567        for inst in types_global_values_and_functions {
568            let result_id = if inst.class.opcode == Op::TypeForwardPointer {
569                forward_declared_pointers.insert(inst.operands[0].unwrap_id_ref());
570                inst.operands[0].unwrap_id_ref()
571            } else {
572                let result_id = inst.result_id.unwrap_or_else(|| {
573                    unreachable!(
574                        "Op{:?} is in `types_global_values` but not have a result ID",
575                        inst.class.opcode
576                    );
577                });
578                if forward_declared_pointers.remove(&result_id) {
579                    // HACK(eddyb) this is a forward-declared pointer, pretend
580                    // it's not "generic" at all to avoid breaking the rest of
581                    // the logic - see module-level docs for how this should be
582                    // handled in the future to support recursive data types.
583                    assert_eq!(inst.class.opcode, Op::TypePointer);
584                    continue;
585                }
586                result_id
587            };
588
589            // Record all integer `OpConstant`s (used for `IndexComposite`).
590            if inst.class.opcode == Op::Constant
591                && let Operand::LiteralBit32(x) = inst.operands[0]
592            {
593                self.int_consts.insert(result_id, x);
594            }
595
596            // Instantiate `inst` in a fresh inference context, to determine
597            // how many parameters it needs, and how they might be constrained.
598            let (param_count, param_values, replacements) = {
599                let mut infer_cx = InferCx::new(self);
600                infer_cx.instantiate_instruction(inst, InstructionLocation::Module);
601
602                let param_count = infer_cx.infer_var_values.len() as u32;
603
604                // FIXME(eddyb) dedup this with `infer_function`.
605                let param_values = infer_cx
606                    .infer_var_values
607                    .iter()
608                    .map(|v| v.map_var(|InferVar(i)| Param(i)));
609                // Only allocate `param_values` if they constrain parameters.
610                let param_values = if param_values.clone().any(|v| v != Value::Unknown) {
611                    Some(param_values.collect())
612                } else {
613                    None
614                };
615
616                (
617                    param_count,
618                    param_values,
619                    infer_cx.into_replacements(..Param(param_count)),
620                )
621            };
622
623            // Inference variables become "generic" parameters.
624            if param_count > 0 {
625                self.generics.insert(
626                    result_id,
627                    Generic {
628                        param_count,
629                        def: inst.clone(),
630                        param_values,
631                        replacements,
632                    },
633                );
634            }
635        }
636    }
637
638    /// Perform inference across the entire definition of `func`, including all
639    /// the instructions in its body, and either store the resulting `Replacements`
640    /// in its `Generic` (if `func` is "generic"), or return them otherwise.
641    fn infer_function(&mut self, func: &Function) -> Option<Replacements> {
642        let func_id = func.def_id().unwrap();
643
644        let param_count = self
645            .generics
646            .get(&func_id)
647            .map_or(0, |generic| generic.param_count);
648
649        let (param_values, replacements) = {
650            let mut infer_cx = InferCx::new(self);
651            infer_cx.instantiate_function(func);
652
653            // FIXME(eddyb) dedup this with `collect_generics`.
654            let param_values = infer_cx.infer_var_values[..param_count as usize]
655                .iter()
656                .map(|v| v.map_var(|InferVar(i)| Param(i)));
657            // Only allocate `param_values` if they constrain parameters.
658            let param_values = if param_values.clone().any(|v| v != Value::Unknown) {
659                Some(param_values.collect())
660            } else {
661                None
662            };
663
664            (
665                param_values,
666                infer_cx.into_replacements(..Param(param_count)),
667            )
668        };
669
670        if let Some(generic) = self.generics.get_mut(&func_id) {
671            // All constraints `func` could have from `collect_generics`
672            // would have to come from its `OpTypeFunction`, but types don't have
673            // internal constraints like e.g. `OpConstant*` and `OpVariable` do.
674            assert!(generic.param_values.is_none());
675
676            generic.param_values = param_values;
677            generic.replacements = replacements;
678
679            None
680        } else {
681            Some(replacements)
682        }
683    }
684}
685
686/// Newtype'd inference variable index.
687// FIXME(eddyb) use `rustc_index` for this instead.
688#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
689struct InferVar(u32);
690
691impl fmt::Display for InferVar {
692    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
693        write!(f, "?{}", self.0)
694    }
695}
696
697impl InferVar {
698    // HACK(eddyb) this works around `Range<InferVar>` not being iterable
699    // because `InferVar` doesn't implement the (unstable) `Step` trait.
700    fn range_iter(range: &Range<Self>) -> impl Iterator<Item = Self> + Clone {
701        (range.start.0..range.end.0).map(Self)
702    }
703}
704
705struct InferCx<'a, S: Specialization> {
706    specializer: &'a Specializer<S>,
707
708    /// `infer_var_values[i]` holds the current state of `InferVar(i)`.
709    /// Each inference variable starts out as `Unknown`, may become `SameAs`
710    /// pointing to another inference variable, but eventually inference must
711    /// result in `Known` values (i.e. concrete `Operand`s).
712    // FIXME(eddyb) use `rustc_index`'s `IndexVec` for this.
713    infer_var_values: Vec<Value<InferVar>>,
714
715    /// Instantiated *Result Type* of each instruction that has any `InferVar`s,
716    /// used when an instruction's result is an input to a later instruction.
717    ///
718    /// Note that for consistency, for `OpFunction` this contains *Function Type*
719    /// instead of *Result Type*, which is inexplicably specified as:
720    /// > *Result Type* must be the same as the *Return Type* declared in *Function Type*
721    type_of_result: IndexMap<Word, InferOperand>,
722
723    /// Operands that need to be replaced with instances of "generic" globals/functions
724    /// (taking as "generic" arguments the results of inference).
725    instantiated_operands: Vec<(OperandLocation, Instance<Range<InferVar>>)>,
726
727    /// Operands that need to be replaced with results of inference.
728    inferred_operands: Vec<(OperandLocation, InferVar)>,
729}
730
731impl<'a, S: Specialization> InferCx<'a, S> {
732    fn new(specializer: &'a Specializer<S>) -> Self {
733        InferCx {
734            specializer,
735
736            infer_var_values: vec![],
737            type_of_result: IndexMap::new(),
738            instantiated_operands: vec![],
739            inferred_operands: vec![],
740        }
741    }
742}
743
744#[derive(Clone, Debug, PartialEq, Eq)]
745enum InferOperand {
746    Unknown,
747    Var(InferVar),
748    Concrete(CopyOperand),
749    Instance(Instance<Range<InferVar>>),
750}
751
752impl InferOperand {
753    /// Construct an `InferOperand` based on whether `operand` refers to some
754    /// "generic" definition, or we're specializing by it.
755    /// Also returns the remaining inference variables, not used by this operand.
756    fn from_operand_and_generic_args(
757        operand: &Operand,
758        generic_args: Range<InferVar>,
759        cx: &InferCx<'_, impl Specialization>,
760    ) -> (Self, Range<InferVar>) {
761        let (needed, generic) = cx.specializer.params_needed_by(operand);
762        let split = InferVar(generic_args.start.0 + needed);
763        let (generic_args, rest) = (generic_args.start..split, split..generic_args.end);
764        (
765            if generic.is_some() {
766                Self::Instance(Instance {
767                    generic_id: operand.unwrap_id_ref(),
768                    generic_args,
769                })
770            } else if needed == 0 {
771                CopyOperand::try_from(operand).map_or(Self::Unknown, Self::Concrete)
772            } else {
773                assert_eq!(needed, 1);
774                Self::Var(generic_args.start)
775            },
776            rest,
777        )
778    }
779
780    fn display_with_infer_var_values<'a>(
781        &'a self,
782        infer_var_value: impl Fn(InferVar) -> Value<InferVar> + Copy + 'a,
783    ) -> impl fmt::Display + '_ {
784        FmtBy(move |f| {
785            let var_with_value = |v| {
786                FmtBy(move |f| {
787                    write!(f, "{v}")?;
788                    match infer_var_value(v) {
789                        Value::Unknown => Ok(()),
790                        Value::Known(o) => write!(f, " = {o}"),
791                        Value::SameAs(v) => write!(f, " = {v}"),
792                    }
793                })
794            };
795            match self {
796                Self::Unknown => write!(f, "_"),
797                Self::Var(v) => write!(f, "{}", var_with_value(*v)),
798                Self::Concrete(o) => write!(f, "{o}"),
799                Self::Instance(instance) => write!(
800                    f,
801                    "{}",
802                    instance.display(|generic_args| {
803                        InferVar::range_iter(generic_args).map(var_with_value)
804                    })
805                ),
806            }
807        })
808    }
809
810    fn display_with_infer_cx<'a>(
811        &'a self,
812        cx: &'a InferCx<'_, impl Specialization>,
813    ) -> impl fmt::Display + '_ {
814        self.display_with_infer_var_values(move |v| {
815            // HACK(eddyb) can't use `resolve_infer_var` because that mutates
816            // `InferCx` (for the "path compression" union-find optimization).
817            let get = |v: InferVar| cx.infer_var_values[v.0 as usize];
818            let mut value = get(v);
819            while let Value::SameAs(v) = value {
820                let next = get(v);
821                if next == Value::Unknown {
822                    break;
823                }
824                value = next;
825            }
826            value
827        })
828    }
829}
830
831impl fmt::Display for InferOperand {
832    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
833        self.display_with_infer_var_values(|_| Value::Unknown)
834            .fmt(f)
835    }
836}
837
838/// How to filter and/or map the operands in an `InferOperandList`, while iterating.
839///
840/// Having this in `InferOperandList` itself, instead of using iterator combinators,
841/// allows storing `InferOperandList`s directly in `Match`, for `TyPatList` matches.
842#[derive(Copy, Clone, PartialEq, Eq)]
843enum InferOperandListTransform {
844    /// The list is the result of keeping only ID operands, and mapping them to
845    /// their types (or `InferOperand::Unknown` for non-value operands, or
846    /// value operands which don't have a "generic" type).
847    ///
848    /// This is used to match against the `inputs` `TyListPat` of `InstSig`.
849    TypeOfId,
850}
851
852#[derive(Clone, PartialEq)]
853struct InferOperandList<'a> {
854    operands: &'a [Operand],
855
856    /// Joined ranges of all `InferVar`s needed by individual `Operand`s,
857    /// either for `InferOperand::Instance` or `InferOperand::Var`.
858    all_generic_args: Range<InferVar>,
859
860    transform: Option<InferOperandListTransform>,
861}
862
863impl<'a> InferOperandList<'a> {
864    fn split_first(
865        &self,
866        cx: &InferCx<'_, impl Specialization>,
867    ) -> Option<(InferOperand, InferOperandList<'a>)> {
868        let mut list = self.clone();
869        loop {
870            let (first_operand, rest) = list.operands.split_first()?;
871            list.operands = rest;
872
873            let (first, rest_args) = InferOperand::from_operand_and_generic_args(
874                first_operand,
875                list.all_generic_args.clone(),
876                cx,
877            );
878            list.all_generic_args = rest_args;
879
880            // Maybe filter this operand, but only *after* consuming the "generic" args for it.
881            match self.transform {
882                // Skip a non-ID operand.
883                Some(InferOperandListTransform::TypeOfId)
884                    if first_operand.id_ref_any().is_none() =>
885                {
886                    continue;
887                }
888                None | Some(InferOperandListTransform::TypeOfId) => {}
889            }
890
891            // Maybe replace this operand with a different one.
892            let first = match self.transform {
893                None => first,
894
895                // Map `first` to its type.
896                Some(InferOperandListTransform::TypeOfId) => match first {
897                    InferOperand::Concrete(CopyOperand::IdRef(id)) => cx
898                        .type_of_result
899                        .get(&id)
900                        .cloned()
901                        .unwrap_or(InferOperand::Unknown),
902                    InferOperand::Unknown | InferOperand::Var(_) | InferOperand::Concrete(_) => {
903                        InferOperand::Unknown
904                    }
905                    InferOperand::Instance(instance) => {
906                        let generic = &cx.specializer.generics[&instance.generic_id];
907
908                        // HACK(eddyb) work around the inexplicable fact that `OpFunction` is
909                        // specified with a *Result Type* that isn't the type of its *Result*:
910                        // > *Result Type* must be the same as the *Return Type* declared in *Function Type*
911                        // So we use *Function Type* instead as the type of its *Result*, and
912                        // we are helped by `instantiate_instruction`, which ensures that the
913                        // "generic" args we have are specifically meant for *Function Type*.
914                        let type_of_result = match generic.def.class.opcode {
915                            Op::Function => Some(generic.def.operands[1].unwrap_id_ref()),
916                            _ => generic.def.result_type,
917                        };
918
919                        match type_of_result {
920                            Some(type_of_result) => {
921                                InferOperand::from_operand_and_generic_args(
922                                    &Operand::IdRef(type_of_result),
923                                    instance.generic_args,
924                                    cx,
925                                )
926                                .0
927                            }
928                            None => InferOperand::Unknown,
929                        }
930                    }
931                },
932            };
933
934            return Some((first, list));
935        }
936    }
937
938    fn iter<'b>(
939        &self,
940        cx: &'b InferCx<'_, impl Specialization>,
941    ) -> impl Iterator<Item = InferOperand> + 'b
942    where
943        'a: 'b,
944    {
945        let mut list = self.clone();
946        iter::from_fn(move || {
947            let (next, rest) = list.split_first(cx)?;
948            list = rest;
949            Some(next)
950        })
951    }
952
953    fn display_with_infer_cx<'b>(
954        &'b self,
955        cx: &'b InferCx<'a, impl Specialization>,
956    ) -> impl fmt::Display + '_ {
957        FmtBy(move |f| {
958            f.debug_list()
959                .entries(self.iter(cx).map(|operand| {
960                    FmtBy(move |f| write!(f, "{}", operand.display_with_infer_cx(cx)))
961                }))
962                .finish()
963        })
964    }
965}
966
967/// `SmallVec<A>` with a map interface.
968#[derive(Default)]
969struct SmallIntMap<A: smallvec::Array>(SmallVec<A>);
970
971impl<A: smallvec::Array> SmallIntMap<A> {
972    fn get(&self, i: usize) -> Option<&A::Item> {
973        self.0.get(i)
974    }
975
976    fn get_mut_or_default(&mut self, i: usize) -> &mut A::Item
977    where
978        A::Item: Default,
979    {
980        let needed = i + 1;
981        if self.0.len() < needed {
982            self.0.resize_with(needed, Default::default);
983        }
984        &mut self.0[i]
985    }
986}
987
988impl<A: smallvec::Array> IntoIterator for SmallIntMap<A> {
989    type Item = (usize, A::Item);
990    type IntoIter = iter::Enumerate<smallvec::IntoIter<A>>;
991    fn into_iter(self) -> Self::IntoIter {
992        self.0.into_iter().enumerate()
993    }
994}
995
996impl<'a, A: smallvec::Array> IntoIterator for &'a mut SmallIntMap<A> {
997    type Item = (usize, &'a mut A::Item);
998    type IntoIter = iter::Enumerate<slice::IterMut<'a, A::Item>>;
999    fn into_iter(self) -> Self::IntoIter {
1000        self.0.iter_mut().enumerate()
1001    }
1002}
1003
1004#[derive(PartialEq)]
1005struct IndexCompositeMatch<'a> {
1006    /// *Indexes* `Operand`s (see `TyPat::IndexComposite`'s doc comment for details).
1007    indices: &'a [Operand],
1008
1009    /// The result of indexing the composite type with all `indices`.
1010    leaf: InferOperand,
1011}
1012
1013/// Inference success (e.g. type matched type pattern).
1014#[must_use]
1015#[derive(Default)]
1016struct Match<'a> {
1017    /// Whether this success isn't guaranteed, because of missing information
1018    /// (such as the defining instructions of non-"generic" types).
1019    ///
1020    /// If there are other alternatives, they will be attempted as well,
1021    /// and merged using `Match::or` (if they don't result in `Unapplicable`).
1022    ambiguous: bool,
1023
1024    // FIXME(eddyb) create some type for these that allows providing common methods
1025    //
1026    /// `storage_class_var_found[i][..]` holds all the `InferOperand`s matched by
1027    /// `StorageClassPat::Var(i)` (currently `i` is always `0`, aka `StorageClassPat::S`).
1028    storage_class_var_found: SmallIntMap<[SmallVec<[InferOperand; 2]>; 1]>,
1029
1030    /// `ty_var_found[i][..]` holds all the `InferOperand`s matched by
1031    /// `TyPat::Var(i)` (currently `i` is always `0`, aka `TyPat::T`).
1032    ty_var_found: SmallIntMap<[SmallVec<[InferOperand; 4]>; 1]>,
1033
1034    /// `index_composite_found[i][..]` holds all the `InferOperand`s matched by
1035    /// `TyPat::IndexComposite(TyPat::Var(i))` (currently `i` is always `0`, aka `TyPat::T`).
1036    index_composite_ty_var_found: SmallIntMap<[SmallVec<[IndexCompositeMatch<'a>; 1]>; 1]>,
1037
1038    /// `ty_list_var_found[i][..]` holds all the `InferOperandList`s matched by
1039    /// `TyListPat::Var(i)` (currently `i` is always `0`, aka `TyListPat::TS`).
1040    ty_list_var_found: SmallIntMap<[SmallVec<[InferOperandList<'a>; 2]>; 1]>,
1041}
1042
1043impl<'a> Match<'a> {
1044    /// Combine two `Match`es such that the result implies both of them apply,
1045    /// i.e. contains the union of their constraints.
1046    fn and(mut self, other: Self) -> Self {
1047        let Match {
1048            ambiguous,
1049            storage_class_var_found,
1050            ty_var_found,
1051            index_composite_ty_var_found,
1052            ty_list_var_found,
1053        } = &mut self;
1054
1055        *ambiguous |= other.ambiguous;
1056        for (i, other_found) in other.storage_class_var_found {
1057            storage_class_var_found
1058                .get_mut_or_default(i)
1059                .extend(other_found);
1060        }
1061        for (i, other_found) in other.ty_var_found {
1062            ty_var_found.get_mut_or_default(i).extend(other_found);
1063        }
1064        for (i, other_found) in other.index_composite_ty_var_found {
1065            index_composite_ty_var_found
1066                .get_mut_or_default(i)
1067                .extend(other_found);
1068        }
1069        for (i, other_found) in other.ty_list_var_found {
1070            ty_list_var_found.get_mut_or_default(i).extend(other_found);
1071        }
1072        self
1073    }
1074
1075    /// Combine two `Match`es such that the result allows for either applying,
1076    /// i.e. contains the intersection of their constraints.
1077    fn or(mut self, other: Self) -> Self {
1078        let Match {
1079            ambiguous,
1080            storage_class_var_found,
1081            ty_var_found,
1082            index_composite_ty_var_found,
1083            ty_list_var_found,
1084        } = &mut self;
1085
1086        *ambiguous |= other.ambiguous;
1087        for (i, self_found) in storage_class_var_found {
1088            let other_found = other
1089                .storage_class_var_found
1090                .get(i)
1091                .map_or(&[][..], |xs| &xs[..]);
1092            self_found.retain(|x| other_found.contains(x));
1093        }
1094        for (i, self_found) in ty_var_found {
1095            let other_found = other.ty_var_found.get(i).map_or(&[][..], |xs| &xs[..]);
1096            self_found.retain(|x| other_found.contains(x));
1097        }
1098        for (i, self_found) in index_composite_ty_var_found {
1099            let other_found = other
1100                .index_composite_ty_var_found
1101                .get(i)
1102                .map_or(&[][..], |xs| &xs[..]);
1103            self_found.retain(|x| other_found.contains(x));
1104        }
1105        for (i, self_found) in ty_list_var_found {
1106            let other_found = other.ty_list_var_found.get(i).map_or(&[][..], |xs| &xs[..]);
1107            self_found.retain(|x| other_found.contains(x));
1108        }
1109        self
1110    }
1111
1112    fn debug_with_infer_cx<'b, T: Specialization>(
1113        &'b self,
1114        cx: &'b InferCx<'a, T>,
1115    ) -> impl fmt::Debug + use<'a, 'b, T> {
1116        fn debug_var_found<'a, A: smallvec::Array<Item = T> + 'a, T: 'a, TD: fmt::Display>(
1117            var_found: &'a SmallIntMap<impl smallvec::Array<Item = SmallVec<A>>>,
1118            display: &'a impl Fn(&'a T) -> TD,
1119        ) -> impl Iterator<Item = impl fmt::Debug + 'a> + 'a {
1120            var_found
1121                .0
1122                .iter()
1123                .filter(|found| !found.is_empty())
1124                .map(move |found| {
1125                    FmtBy(move |f| {
1126                        let mut found = found.iter().map(display);
1127                        write!(f, "{}", found.next().unwrap())?;
1128                        for x in found {
1129                            write!(f, " = {x}")?;
1130                        }
1131                        Ok(())
1132                    })
1133                })
1134        }
1135        FmtBy(move |f| {
1136            let Self {
1137                ambiguous,
1138                storage_class_var_found,
1139                ty_var_found,
1140                index_composite_ty_var_found,
1141                ty_list_var_found,
1142            } = self;
1143            write!(f, "Match{} ", if *ambiguous { " (ambiguous)" } else { "" })?;
1144            let mut list = f.debug_list();
1145            list.entries(debug_var_found(storage_class_var_found, &move |operand| {
1146                operand.display_with_infer_cx(cx)
1147            }));
1148            list.entries(debug_var_found(ty_var_found, &move |operand| {
1149                operand.display_with_infer_cx(cx)
1150            }));
1151            list.entries(
1152                index_composite_ty_var_found
1153                    .0
1154                    .iter()
1155                    .enumerate()
1156                    .filter(|(_, found)| !found.is_empty())
1157                    .flat_map(|(i, found)| found.iter().map(move |x| (i, x)))
1158                    .map(move |(i, IndexCompositeMatch { indices, leaf })| {
1159                        FmtBy(move |f| {
1160                            match ty_var_found.get(i) {
1161                                Some(found) if found.len() == 1 => {
1162                                    write!(f, "{}", found[0].display_with_infer_cx(cx))?;
1163                                }
1164                                found => {
1165                                    let found = found.map_or(&[][..], |xs| &xs[..]);
1166                                    write!(f, "(")?;
1167                                    for (j, operand) in found.iter().enumerate() {
1168                                        if j != 0 {
1169                                            write!(f, " = ")?;
1170                                        }
1171                                        write!(f, "{}", operand.display_with_infer_cx(cx))?;
1172                                    }
1173                                    write!(f, ")")?;
1174                                }
1175                            }
1176                            for operand in &indices[..] {
1177                                // Show the value for literals and IDs pointing to
1178                                // known `OpConstant`s (e.g. struct field indices).
1179                                let maybe_idx = match operand {
1180                                    Operand::IdRef(id) => cx.specializer.int_consts.get(id),
1181                                    Operand::LiteralBit32(idx) => Some(idx),
1182                                    _ => None,
1183                                };
1184                                match maybe_idx {
1185                                    Some(idx) => write!(f, ".{idx}")?,
1186                                    None => write!(f, "[{operand}]")?,
1187                                }
1188                            }
1189                            write!(f, " = {}", leaf.display_with_infer_cx(cx))
1190                        })
1191                    }),
1192            );
1193            list.entries(debug_var_found(ty_list_var_found, &move |list| {
1194                list.display_with_infer_cx(cx)
1195            }));
1196            list.finish()
1197        })
1198    }
1199}
1200
1201/// Pattern-matching failure, returned by `match_*` when the pattern doesn't apply.
1202struct Unapplicable;
1203
1204impl<'a, S: Specialization> InferCx<'a, S> {
1205    /// Match `storage_class` against `pat`, returning a `Match` with found `Var`s.
1206    #[allow(clippy::unused_self)] // TODO: remove?
1207    fn match_storage_class_pat(
1208        &self,
1209        pat: &StorageClassPat,
1210        storage_class: InferOperand,
1211    ) -> Match<'a> {
1212        match pat {
1213            StorageClassPat::Any => Match::default(),
1214            StorageClassPat::Var(i) => {
1215                let mut m = Match::default();
1216                m.storage_class_var_found
1217                    .get_mut_or_default(*i)
1218                    .push(storage_class);
1219                m
1220            }
1221        }
1222    }
1223
1224    /// Match `ty` against `pat`, returning a `Match` with found `Var`s.
1225    fn match_ty_pat(&self, pat: &TyPat<'_>, ty: InferOperand) -> Result<Match<'a>, Unapplicable> {
1226        match pat {
1227            TyPat::Any => Ok(Match::default()),
1228            TyPat::Var(i) => {
1229                let mut m = Match::default();
1230                m.ty_var_found.get_mut_or_default(*i).push(ty);
1231                Ok(m)
1232            }
1233            TyPat::Either(a, b) => match self.match_ty_pat(a, ty.clone()) {
1234                Ok(m) if !m.ambiguous => Ok(m),
1235                a_result => match (a_result, self.match_ty_pat(b, ty)) {
1236                    (Ok(ma), Ok(mb)) => Ok(ma.or(mb)),
1237                    (Ok(m), _) | (_, Ok(m)) => Ok(m),
1238                    (Err(Unapplicable), Err(Unapplicable)) => Err(Unapplicable),
1239                },
1240            },
1241            TyPat::IndexComposite(composite_pat) => match composite_pat {
1242                TyPat::Var(i) => {
1243                    let mut m = Match::default();
1244                    m.index_composite_ty_var_found.get_mut_or_default(*i).push(
1245                        IndexCompositeMatch {
1246                            // HACK(eddyb) leave empty `indices` in here for
1247                            // `match_inst_sig` to fill in, as it has access
1248                            // to the whole `Instruction` but we don't.
1249                            indices: &[],
1250                            leaf: ty,
1251                        },
1252                    );
1253                    Ok(m)
1254                }
1255                _ => unreachable!(
1256                    "`IndexComposite({:?})` isn't supported, only type variable
1257                     patterns are (for the composite type), e.g. `IndexComposite(T)`",
1258                    composite_pat
1259                ),
1260            },
1261            _ => {
1262                let instance = match ty {
1263                    InferOperand::Unknown | InferOperand::Concrete(_) => {
1264                        return Ok(Match {
1265                            ambiguous: true,
1266                            ..Match::default()
1267                        });
1268                    }
1269                    InferOperand::Var(_) => return Err(Unapplicable),
1270                    InferOperand::Instance(instance) => instance,
1271                };
1272                let generic = &self.specializer.generics[&instance.generic_id];
1273
1274                let ty_operands = InferOperandList {
1275                    operands: &generic.def.operands,
1276                    all_generic_args: instance.generic_args,
1277                    transform: None,
1278                };
1279                let simple = |op, inner_pat| {
1280                    if generic.def.class.opcode == op {
1281                        self.match_ty_pat(inner_pat, ty_operands.split_first(self).unwrap().0)
1282                    } else {
1283                        Err(Unapplicable)
1284                    }
1285                };
1286                match pat {
1287                    TyPat::Any | TyPat::Var(_) | TyPat::Either(..) | TyPat::IndexComposite(_) => {
1288                        unreachable!()
1289                    }
1290
1291                    // HACK(eddyb) `TyPat::Void` can't be observed because it's
1292                    // not "generic", so it would return early as ambiguous.
1293                    TyPat::Void => unreachable!(),
1294
1295                    TyPat::Pointer(storage_class_pat, pointee_pat) => {
1296                        let mut ty_operands = ty_operands.iter(self);
1297                        let (storage_class, pointee_ty) =
1298                            (ty_operands.next().unwrap(), ty_operands.next().unwrap());
1299                        Ok(self
1300                            .match_storage_class_pat(storage_class_pat, storage_class)
1301                            .and(self.match_ty_pat(pointee_pat, pointee_ty)?))
1302                    }
1303                    TyPat::Array(pat) => simple(Op::TypeArray, pat),
1304                    TyPat::Vector(pat) => simple(Op::TypeVector, pat),
1305                    TyPat::Vector4(pat) => match ty_operands.operands {
1306                        [_, Operand::LiteralBit32(4)] => simple(Op::TypeVector, pat),
1307                        _ => Err(Unapplicable),
1308                    },
1309                    TyPat::Matrix(pat) => simple(Op::TypeMatrix, pat),
1310                    TyPat::Image(pat) => simple(Op::TypeImage, pat),
1311                    TyPat::Pipe(_pat) => {
1312                        if generic.def.class.opcode == Op::TypePipe {
1313                            Ok(Match::default())
1314                        } else {
1315                            Err(Unapplicable)
1316                        }
1317                    }
1318                    TyPat::SampledImage(pat) => simple(Op::TypeSampledImage, pat),
1319                    TyPat::Struct(fields_pat) => {
1320                        if generic.def.class.opcode == Op::TypeStruct {
1321                            self.match_ty_list_pat(fields_pat, ty_operands)
1322                        } else {
1323                            Err(Unapplicable)
1324                        }
1325                    }
1326                    TyPat::Function(ret_pat, params_pat) => {
1327                        let (ret_ty, params_ty_list) = ty_operands.split_first(self).unwrap();
1328                        Ok(self
1329                            .match_ty_pat(ret_pat, ret_ty)?
1330                            .and(self.match_ty_list_pat(params_pat, params_ty_list)?))
1331                    }
1332                }
1333            }
1334        }
1335    }
1336
1337    /// Match `ty_list` against `pat`, returning a `Match` with found `Var`s.
1338    fn match_ty_list_pat(
1339        &self,
1340        mut list_pat: &TyListPat<'_>,
1341        mut ty_list: InferOperandList<'a>,
1342    ) -> Result<Match<'a>, Unapplicable> {
1343        let mut m = Match::default();
1344
1345        while let TyListPat::Cons { first: pat, suffix } = list_pat {
1346            list_pat = suffix;
1347
1348            let (ty, rest) = ty_list.split_first(self).ok_or(Unapplicable)?;
1349            ty_list = rest;
1350
1351            m = m.and(self.match_ty_pat(pat, ty)?);
1352        }
1353
1354        match list_pat {
1355            TyListPat::Cons { .. } => unreachable!(),
1356
1357            TyListPat::Any => {}
1358            TyListPat::Var(i) => {
1359                m.ty_list_var_found.get_mut_or_default(*i).push(ty_list);
1360            }
1361            TyListPat::Repeat(repeat_list_pat) => {
1362                let mut tys = ty_list.iter(self).peekable();
1363                loop {
1364                    let mut list_pat = repeat_list_pat;
1365                    while let TyListPat::Cons { first: pat, suffix } = list_pat {
1366                        m = m.and(self.match_ty_pat(pat, tys.next().ok_or(Unapplicable)?)?);
1367                        list_pat = suffix;
1368                    }
1369                    assert!(matches!(list_pat, TyListPat::Nil));
1370                    if tys.peek().is_none() {
1371                        break;
1372                    }
1373                }
1374            }
1375            TyListPat::Nil => {
1376                if ty_list.split_first(self).is_some() {
1377                    return Err(Unapplicable);
1378                }
1379            }
1380        }
1381
1382        Ok(m)
1383    }
1384
1385    /// Match `inst`'s input operands (with `inputs_generic_args` as "generic" args),
1386    /// and `result_type`, against `sig`, returning a `Match` with found `Var`s.
1387    fn match_inst_sig(
1388        &self,
1389        sig: &InstSig<'_>,
1390        inst: &'a Instruction,
1391        inputs_generic_args: Range<InferVar>,
1392        result_type: Option<InferOperand>,
1393    ) -> Result<Match<'a>, Unapplicable> {
1394        let mut m = Match::default();
1395
1396        if let Some(pat) = sig.storage_class {
1397            // FIXME(eddyb) going through all the operands to find the one that
1398            // is a storage class is inefficient, storage classes should be part
1399            // of a single unified list of operand patterns.
1400            let all_operands = InferOperandList {
1401                operands: &inst.operands,
1402                all_generic_args: inputs_generic_args.clone(),
1403                transform: None,
1404            };
1405            let storage_class = all_operands
1406                .iter(self)
1407                .zip(&inst.operands)
1408                .filter(|(_, original)| matches!(original, Operand::StorageClass(_)))
1409                .map(|(operand, _)| operand)
1410                .next()
1411                .ok_or(Unapplicable)?;
1412            m = m.and(self.match_storage_class_pat(pat, storage_class));
1413        }
1414
1415        let input_ty_list = InferOperandList {
1416            operands: &inst.operands,
1417            all_generic_args: inputs_generic_args,
1418            transform: Some(InferOperandListTransform::TypeOfId),
1419        };
1420
1421        m = m.and(self.match_ty_list_pat(sig.input_types, input_ty_list.clone())?);
1422
1423        match (sig.output_type, result_type) {
1424            (Some(pat), Some(result_type)) => {
1425                m = m.and(self.match_ty_pat(pat, result_type)?);
1426            }
1427            (None, None) => {}
1428            _ => return Err(Unapplicable),
1429        }
1430
1431        if !m.index_composite_ty_var_found.0.is_empty() {
1432            let composite_indices = {
1433                // Drain the `input_types` prefix (everything before `..`).
1434                let mut ty_list = input_ty_list;
1435                let mut list_pat = sig.input_types;
1436                while let TyListPat::Cons { first: _, suffix } = list_pat {
1437                    list_pat = suffix;
1438                    ty_list = ty_list.split_first(self).ok_or(Unapplicable)?.1;
1439                }
1440
1441                assert_eq!(
1442                    list_pat,
1443                    &TyListPat::Any,
1444                    "`IndexComposite` must have input types end in `..`"
1445                );
1446
1447                // Extract the underlying remaining `operands` - while iterating on
1448                // the `TypeOfId` list would skip over non-ID operands, and replace
1449                // ID operands with their types, the `operands` slice is still a
1450                // subslice of `inst.operands` (minus the prefix we drained above).
1451                ty_list.operands
1452            };
1453
1454            // Fill in all the `indices` fields left empty by `match_ty_pat`.
1455            for (_, found) in &mut m.index_composite_ty_var_found {
1456                for index_composite_match in found {
1457                    let empty = mem::replace(&mut index_composite_match.indices, composite_indices);
1458                    assert_eq!(empty, &[]);
1459                }
1460            }
1461        }
1462
1463        Ok(m)
1464    }
1465
1466    /// Match `inst`'s input operands (with `inputs_generic_args` as "generic" args),
1467    /// and `result_type`, against `sigs`, returning a `Match` with found `Var`s.
1468    fn match_inst_sigs(
1469        &self,
1470        sigs: &[InstSig<'_>],
1471        inst: &'a Instruction,
1472        inputs_generic_args: Range<InferVar>,
1473        result_type: Option<InferOperand>,
1474    ) -> Result<Match<'a>, Unapplicable> {
1475        let mut result = Err(Unapplicable);
1476        for sig in sigs {
1477            result = match (
1478                result,
1479                self.match_inst_sig(sig, inst, inputs_generic_args.clone(), result_type.clone()),
1480            ) {
1481                (Err(Unapplicable), Ok(m)) if !m.ambiguous => return Ok(m),
1482                (Ok(a), Ok(b)) => Ok(a.or(b)),
1483                (Ok(m), _) | (_, Ok(m)) => Ok(m),
1484                (Err(Unapplicable), Err(Unapplicable)) => Err(Unapplicable),
1485            };
1486        }
1487        result
1488    }
1489}
1490
1491enum InferError {
1492    /// Mismatch between operands, returned by `equate_*(a, b)` when `a != b`.
1493    // FIXME(eddyb) track where the mismatched operands come from.
1494    Conflict(InferOperand, InferOperand),
1495}
1496
1497impl InferError {
1498    fn report(self, inst: &Instruction) {
1499        // FIXME(eddyb) better error reporting than this.
1500        match self {
1501            Self::Conflict(a, b) => {
1502                error!("inference conflict: {a:?} vs {b:?}");
1503            }
1504        }
1505        error!("    in ");
1506        // FIXME(eddyb) deduplicate this with other instruction printing logic.
1507        if let Some(result_id) = inst.result_id {
1508            error!("%{result_id} = ");
1509        }
1510        error!("Op{:?}", inst.class.opcode);
1511        for operand in inst
1512            .result_type
1513            .map(Operand::IdRef)
1514            .iter()
1515            .chain(inst.operands.iter())
1516        {
1517            error!(" {operand}");
1518        }
1519        error!("");
1520
1521        std::process::exit(1);
1522    }
1523}
1524
1525impl<'a, S: Specialization> InferCx<'a, S> {
1526    /// Traverse `SameAs` chains starting at `x` and return the first `InferVar`
1527    /// that isn't `SameAs` (i.e. that is `Unknown` or `Known`).
1528    /// This corresponds to `find(v)` from union-find.
1529    fn resolve_infer_var(&mut self, v: InferVar) -> InferVar {
1530        match self.infer_var_values[v.0 as usize] {
1531            Value::Unknown | Value::Known(_) => v,
1532            Value::SameAs(next) => {
1533                let resolved = self.resolve_infer_var(next);
1534                if resolved != next {
1535                    // Update the `SameAs` entry for faster lookup next time
1536                    // (also known as "path compression" in union-find).
1537                    self.infer_var_values[v.0 as usize] = Value::SameAs(resolved);
1538                }
1539                resolved
1540            }
1541        }
1542    }
1543
1544    /// Enforce that `a = b`, returning a combined `InferVar`, if successful.
1545    /// This corresponds to `union(a, b)` from union-find.
1546    fn equate_infer_vars(&mut self, a: InferVar, b: InferVar) -> Result<InferVar, InferError> {
1547        let (a, b) = (self.resolve_infer_var(a), self.resolve_infer_var(b));
1548
1549        if a == b {
1550            return Ok(a);
1551        }
1552
1553        // Maintain the invariant that "newer" variables are redirected to "older" ones.
1554        let (older, newer) = (a.min(b), a.max(b));
1555        let newer_value = mem::replace(
1556            &mut self.infer_var_values[newer.0 as usize],
1557            Value::SameAs(older),
1558        );
1559        match (self.infer_var_values[older.0 as usize], newer_value) {
1560            // Guaranteed by `resolve_infer_var`.
1561            (Value::SameAs(_), _) | (_, Value::SameAs(_)) => unreachable!(),
1562
1563            // Both `newer` and `older` had a `Known` value, they must match.
1564            (Value::Known(x), Value::Known(y)) => {
1565                if x != y {
1566                    return Err(InferError::Conflict(
1567                        InferOperand::Concrete(x),
1568                        InferOperand::Concrete(y),
1569                    ));
1570                }
1571            }
1572
1573            // Move the `Known` value from `newer` to `older`.
1574            (Value::Unknown, Value::Known(_)) => {
1575                self.infer_var_values[older.0 as usize] = newer_value;
1576            }
1577
1578            (_, Value::Unknown) => {}
1579        }
1580
1581        Ok(older)
1582    }
1583
1584    /// Enforce that `a = b`, returning a combined `Range<InferVar>`, if successful.
1585    fn equate_infer_var_ranges(
1586        &mut self,
1587        a: Range<InferVar>,
1588        b: Range<InferVar>,
1589    ) -> Result<Range<InferVar>, InferError> {
1590        if a == b {
1591            return Ok(a);
1592        }
1593
1594        assert_eq!(a.end.0 - a.start.0, b.end.0 - b.start.0);
1595
1596        for (a, b) in InferVar::range_iter(&a).zip(InferVar::range_iter(&b)) {
1597            self.equate_infer_vars(a, b)?;
1598        }
1599
1600        // Pick the "oldest" range to maintain the invariant that "newer" variables
1601        // are redirected to "older" ones, while keeping a contiguous range
1602        // (instead of splitting it into individual variables), for performance.
1603        Ok(if a.start < b.start { a } else { b })
1604    }
1605
1606    /// Enforce that `a = b`, returning a combined `InferOperand`, if successful.
1607    fn equate_infer_operands(
1608        &mut self,
1609        a: InferOperand,
1610        b: InferOperand,
1611    ) -> Result<InferOperand, InferError> {
1612        if a == b {
1613            return Ok(a);
1614        }
1615
1616        #[allow(clippy::match_same_arms)]
1617        Ok(match (a.clone(), b.clone()) {
1618            // Instances of "generic" globals/functions must be of the same ID,
1619            // and their `generic_args` inference variables must be unified.
1620            (
1621                InferOperand::Instance(Instance {
1622                    generic_id: a_id,
1623                    generic_args: a_args,
1624                }),
1625                InferOperand::Instance(Instance {
1626                    generic_id: b_id,
1627                    generic_args: b_args,
1628                }),
1629            ) => {
1630                if a_id != b_id {
1631                    return Err(InferError::Conflict(a, b));
1632                }
1633                InferOperand::Instance(Instance {
1634                    generic_id: a_id,
1635                    generic_args: self.equate_infer_var_ranges(a_args, b_args)?,
1636                })
1637            }
1638
1639            // Instances of "generic" globals/functions can never equal anything else.
1640            (InferOperand::Instance(_), _) | (_, InferOperand::Instance(_)) => {
1641                return Err(InferError::Conflict(a, b));
1642            }
1643
1644            // Inference variables must be unified.
1645            (InferOperand::Var(a), InferOperand::Var(b)) => {
1646                InferOperand::Var(self.equate_infer_vars(a, b)?)
1647            }
1648
1649            // An inference variable can be assigned a concrete value.
1650            (InferOperand::Var(v), InferOperand::Concrete(new))
1651            | (InferOperand::Concrete(new), InferOperand::Var(v)) => {
1652                let v = self.resolve_infer_var(v);
1653                match &mut self.infer_var_values[v.0 as usize] {
1654                    // Guaranteed by `resolve_infer_var`.
1655                    Value::SameAs(_) => unreachable!(),
1656
1657                    &mut Value::Known(old) => {
1658                        if new != old {
1659                            return Err(InferError::Conflict(
1660                                InferOperand::Concrete(old),
1661                                InferOperand::Concrete(new),
1662                            ));
1663                        }
1664                    }
1665
1666                    value @ Value::Unknown => *value = Value::Known(new),
1667                }
1668                InferOperand::Var(v)
1669            }
1670
1671            // Concrete `Operand`s must simply match.
1672            (InferOperand::Concrete(_), InferOperand::Concrete(_)) => {
1673                // Success case is handled by `if a == b` early return above.
1674                return Err(InferError::Conflict(a, b));
1675            }
1676
1677            // Unknowns can be ignored in favor of non-`Unknown`.
1678            // NOTE(eddyb) `x` cannot be `Instance`, that is handled above.
1679            (InferOperand::Unknown, x) | (x, InferOperand::Unknown) => x,
1680        })
1681    }
1682
1683    /// Compute the result ("leaf") type for a `TyPat::IndexComposite` pattern,
1684    /// by applying each index in `indices` to `composite_ty`, extracting the
1685    /// element type (for `OpType{Array,RuntimeArray,Vector,Matrix}`), or the
1686    /// field type for `OpTypeStruct`, where `indices` contains the field index.
1687    fn index_composite(&self, composite_ty: InferOperand, indices: &[Operand]) -> InferOperand {
1688        let mut ty = composite_ty;
1689        for idx in indices {
1690            let instance = match ty {
1691                InferOperand::Unknown | InferOperand::Concrete(_) | InferOperand::Var(_) => {
1692                    return InferOperand::Unknown;
1693                }
1694                InferOperand::Instance(instance) => instance,
1695            };
1696            let generic = &self.specializer.generics[&instance.generic_id];
1697
1698            let ty_opcode = generic.def.class.opcode;
1699            let ty_operands = InferOperandList {
1700                operands: &generic.def.operands,
1701                all_generic_args: instance.generic_args,
1702                transform: None,
1703            };
1704
1705            let ty_operands_idx = match ty_opcode {
1706                Op::TypeArray | Op::TypeRuntimeArray | Op::TypeVector | Op::TypeMatrix => 0,
1707                Op::TypeStruct => match idx {
1708                    Operand::IdRef(id) => {
1709                        *self.specializer.int_consts.get(id).unwrap_or_else(|| {
1710                            unreachable!("non-constant `OpTypeStruct` field index {}", id);
1711                        })
1712                    }
1713                    &Operand::LiteralBit32(i) => i,
1714                    _ => {
1715                        unreachable!("invalid `OpTypeStruct` field index operand {:?}", idx);
1716                    }
1717                },
1718                _ => unreachable!("indexing non-composite type `Op{:?}`", ty_opcode),
1719            };
1720
1721            ty = ty_operands
1722                .iter(self)
1723                .nth(ty_operands_idx as usize)
1724                .unwrap_or_else(|| {
1725                    unreachable!(
1726                        "out of bounds index {} for `Op{:?}`",
1727                        ty_operands_idx, ty_opcode
1728                    );
1729                });
1730        }
1731        ty
1732    }
1733
1734    /// Enforce that all the `InferOperand`/`InferOperandList`s found for the
1735    /// same pattern variable (i.e. `*Pat::Var(i)` with the same `i`), are equal.
1736    fn equate_match_findings(&mut self, m: Match<'_>) -> Result<(), InferError> {
1737        let Match {
1738            ambiguous: _,
1739
1740            storage_class_var_found,
1741            ty_var_found,
1742            index_composite_ty_var_found,
1743            ty_list_var_found,
1744        } = m;
1745
1746        for (_, found) in storage_class_var_found {
1747            let mut found = found.into_iter();
1748            if let Some(first) = found.next() {
1749                found.try_fold(first, |a, b| self.equate_infer_operands(a, b))?;
1750            }
1751        }
1752
1753        for (i, found) in ty_var_found {
1754            let mut found = found.into_iter();
1755            if let Some(first) = found.next() {
1756                let equated_ty = found.try_fold(first, |a, b| self.equate_infer_operands(a, b))?;
1757
1758                // Apply any `IndexComposite(Var(i))`'s indices to `equated_ty`,
1759                // and equate the resulting "leaf" type with the found "leaf" type.
1760                let index_composite_found = index_composite_ty_var_found
1761                    .get(i)
1762                    .map_or(&[][..], |xs| &xs[..]);
1763                for IndexCompositeMatch { indices, leaf } in index_composite_found {
1764                    let indexing_result_ty = self.index_composite(equated_ty.clone(), indices);
1765                    self.equate_infer_operands(indexing_result_ty, leaf.clone())?;
1766                }
1767            }
1768        }
1769
1770        for (_, mut found) in ty_list_var_found {
1771            if let Some((first_list, other_lists)) = found.split_first_mut() {
1772                // Advance all the lists in lock-step so that we don't have to
1773                // allocate state proportional to list length and/or `found.len()`.
1774                while let Some((first, rest)) = first_list.split_first(self) {
1775                    *first_list = rest;
1776
1777                    other_lists.iter_mut().try_fold(first, |a, b_list| {
1778                        let (b, rest) = b_list
1779                            .split_first(self)
1780                            .expect("list length mismatch (invalid SPIR-V?)");
1781                        *b_list = rest;
1782                        self.equate_infer_operands(a, b)
1783                    })?;
1784                }
1785
1786                for other_list in other_lists {
1787                    assert!(
1788                        other_list.split_first(self).is_none(),
1789                        "list length mismatch (invalid SPIR-V?)"
1790                    );
1791                }
1792            }
1793        }
1794
1795        Ok(())
1796    }
1797
1798    /// Track an instantiated operand, to be included in the `Replacements`
1799    /// (produced by `into_replacements`), if it has any `InferVar`s at all.
1800    fn record_instantiated_operand(&mut self, loc: OperandLocation, operand: InferOperand) {
1801        match operand {
1802            InferOperand::Var(v) => {
1803                self.inferred_operands.push((loc, v));
1804            }
1805            InferOperand::Instance(instance) => {
1806                self.instantiated_operands.push((loc, instance));
1807            }
1808            InferOperand::Unknown | InferOperand::Concrete(_) => {}
1809        }
1810    }
1811
1812    /// Instantiate all of `inst`'s operands (and *Result Type*) that refer to
1813    /// "generic" globals/functions, or we need to specialize by, with fresh
1814    /// inference variables, and enforce any inference constraints applicable.
1815    fn instantiate_instruction(&mut self, inst: &'a Instruction, inst_loc: InstructionLocation) {
1816        let mut all_generic_args = {
1817            let next_infer_var = InferVar(self.infer_var_values.len().try_into().unwrap());
1818            next_infer_var..next_infer_var
1819        };
1820
1821        // HACK(eddyb) work around the inexplicable fact that `OpFunction` is
1822        // specified with a *Result Type* that isn't the type of its *Result*:
1823        // > *Result Type* must be the same as the *Return Type* declared in *Function Type*
1824        // Specifically, we don't instantiate *Result Type* (to avoid ending
1825        // up with redundant `InferVar`s), and instead overlap its "generic" args
1826        // with that of the *Function Type*, for `instantiations.
1827        let (instantiate_result_type, record_fn_ret_ty, type_of_result) = match inst.class.opcode {
1828            Op::Function => (
1829                None,
1830                inst.result_type,
1831                Some(inst.operands[1].unwrap_id_ref()),
1832            ),
1833            _ => (inst.result_type, None, inst.result_type),
1834        };
1835
1836        for (operand_idx, operand) in instantiate_result_type
1837            .map(Operand::IdRef)
1838            .iter()
1839            .map(|o| (OperandIdx::ResultType, o))
1840            .chain(
1841                inst.operands
1842                    .iter()
1843                    .enumerate()
1844                    .map(|(i, o)| (OperandIdx::Input(i), o)),
1845            )
1846        {
1847            // HACK(eddyb) use `v..InferVar(u32::MAX)` as an open-ended range of sorts.
1848            let (operand, rest) = InferOperand::from_operand_and_generic_args(
1849                operand,
1850                all_generic_args.end..InferVar(u32::MAX),
1851                self,
1852            );
1853            let generic_args = all_generic_args.end..rest.start;
1854            all_generic_args.end = generic_args.end;
1855
1856            let generic = match &operand {
1857                InferOperand::Instance(instance) => {
1858                    Some(&self.specializer.generics[&instance.generic_id])
1859                }
1860                _ => None,
1861            };
1862
1863            // Initialize the new inference variables (for `operand`'s "generic" args)
1864            // with either `generic.param_values` (if present) or all `Unknown`s.
1865            match generic {
1866                Some(Generic {
1867                    param_values: Some(values),
1868                    ..
1869                }) => self.infer_var_values.extend(
1870                    values
1871                        .iter()
1872                        .map(|v| v.map_var(|Param(p)| InferVar(generic_args.start.0 + p))),
1873                ),
1874
1875                _ => {
1876                    self.infer_var_values
1877                        .extend(InferVar::range_iter(&generic_args).map(|_| Value::Unknown));
1878                }
1879            }
1880
1881            self.record_instantiated_operand(
1882                OperandLocation {
1883                    inst_loc,
1884                    operand_idx,
1885                },
1886                operand,
1887            );
1888        }
1889
1890        // HACK(eddyb) workaround for `OpFunction`, see earlier HACK comment.
1891        if let Some(ret_ty) = record_fn_ret_ty {
1892            let (ret_ty, _) = InferOperand::from_operand_and_generic_args(
1893                &Operand::IdRef(ret_ty),
1894                all_generic_args.clone(),
1895                self,
1896            );
1897            self.record_instantiated_operand(
1898                OperandLocation {
1899                    inst_loc,
1900                    operand_idx: OperandIdx::ResultType,
1901                },
1902                ret_ty,
1903            );
1904        }
1905
1906        // *Result Type* comes first in `all_generic_args`, extract it back out.
1907        let (type_of_result, inputs_generic_args) = match type_of_result {
1908            Some(type_of_result) => {
1909                let (type_of_result, rest) = InferOperand::from_operand_and_generic_args(
1910                    &Operand::IdRef(type_of_result),
1911                    all_generic_args.clone(),
1912                    self,
1913                );
1914                (
1915                    Some(type_of_result),
1916                    // HACK(eddyb) workaround for `OpFunction`, see earlier HACK comment.
1917                    match inst.class.opcode {
1918                        Op::Function => all_generic_args,
1919                        _ => rest,
1920                    },
1921                )
1922            }
1923            None => (None, all_generic_args),
1924        };
1925
1926        let debug_dump_if_enabled = |cx: &Self, prefix| {
1927            let result_type = match inst.class.opcode {
1928                // HACK(eddyb) workaround for `OpFunction`, see earlier HACK comment.
1929                Op::Function => Some(
1930                    InferOperand::from_operand_and_generic_args(
1931                        &Operand::IdRef(inst.result_type.unwrap()),
1932                        inputs_generic_args.clone(),
1933                        cx,
1934                    )
1935                    .0,
1936                ),
1937                _ => type_of_result.clone(),
1938            };
1939            let inputs = InferOperandList {
1940                operands: &inst.operands,
1941                all_generic_args: inputs_generic_args.clone(),
1942                transform: None,
1943            };
1944
1945            if inst_loc != InstructionLocation::Module {
1946                debug!("    ");
1947            }
1948            debug!("{prefix}");
1949            if let Some(result_id) = inst.result_id {
1950                debug!("%{result_id} = ");
1951            }
1952            debug!("Op{:?}", inst.class.opcode);
1953            for operand in result_type.into_iter().chain(inputs.iter(cx)) {
1954                debug!(" {}", operand.display_with_infer_cx(cx));
1955            }
1956            debug!("");
1957        };
1958
1959        // If we have some instruction signatures for `inst`, enforce them.
1960        if let Some(sigs) = spirv_type_constraints::instruction_signatures(inst.class.opcode) {
1961            // HACK(eddyb) workaround for `OpFunction`, see earlier HACK comment.
1962            // (specifically, `type_of_result` isn't *Result Type* for `OpFunction`)
1963            assert_ne!(inst.class.opcode, Op::Function);
1964
1965            debug_dump_if_enabled(self, " -> ");
1966
1967            let m = match self.match_inst_sigs(
1968                sigs,
1969                inst,
1970                inputs_generic_args.clone(),
1971                type_of_result.clone(),
1972            ) {
1973                Ok(m) => m,
1974
1975                // While this could be an user error *in theory*, we haven't really
1976                // unified any of the `InferOperand`s found by pattern match variables,
1977                // at this point, so the only the possible error case is that `inst`
1978                // doesn't match the *shapes* specified in `sigs`, i.e. this is likely
1979                // a bug in `spirv_type_constraints`, not some kind of inference conflict.
1980                Err(Unapplicable) => unreachable!(
1981                    "spirv_type_constraints(Op{:?}) = `{:?}` doesn't match `{:?}`",
1982                    inst.class.opcode, sigs, inst
1983                ),
1984            };
1985
1986            if inst_loc != InstructionLocation::Module {
1987                debug!("    ");
1988            }
1989            debug!("    found {:?}", m.debug_with_infer_cx(self));
1990
1991            if let Err(e) = self.equate_match_findings(m) {
1992                e.report(inst);
1993            }
1994
1995            debug_dump_if_enabled(self, " <- ");
1996        } else {
1997            debug_dump_if_enabled(self, "");
1998        }
1999
2000        if let Some(type_of_result) = type_of_result {
2001            // Keep the (instantiated) *Result Type*, for future instructions to use
2002            // (but only if it has any `InferVar`s at all).
2003            match type_of_result {
2004                InferOperand::Var(_) | InferOperand::Instance(_) => {
2005                    self.type_of_result
2006                        .insert(inst.result_id.unwrap(), type_of_result);
2007                }
2008                InferOperand::Unknown | InferOperand::Concrete(_) => {}
2009            }
2010        }
2011    }
2012
2013    /// Instantiate `func`'s definition and all instructions in its body,
2014    /// effectively performing inference across the entire function body.
2015    fn instantiate_function(&mut self, func: &'a Function) {
2016        let func_id = func.def_id().unwrap();
2017
2018        debug!("");
2019        debug!("specializer::instantiate_function(%{func_id}");
2020        if let Some(name) = self.specializer.debug_names.get(&func_id) {
2021            debug!(" {name}");
2022        }
2023        debug!("):");
2024
2025        // Instantiate the defining `OpFunction` first, so that the first
2026        // inference variables match the parameters from the `Generic`
2027        // (if the `OpTypeFunction` is "generic", that is).
2028        assert!(self.infer_var_values.is_empty());
2029        self.instantiate_instruction(func.def.as_ref().unwrap(), InstructionLocation::Module);
2030
2031        debug!("infer body {{");
2032
2033        // If the `OpTypeFunction` is indeed "generic", we have to extract the
2034        // return / parameter types for `OpReturnValue` and `OpFunctionParameter`.
2035        let ret_ty = match self.type_of_result.get(&func_id).cloned() {
2036            Some(InferOperand::Instance(instance)) => {
2037                let generic = &self.specializer.generics[&instance.generic_id];
2038                assert_eq!(generic.def.class.opcode, Op::TypeFunction);
2039
2040                let (ret_ty, mut params_ty_list) = InferOperandList {
2041                    operands: &generic.def.operands,
2042                    all_generic_args: instance.generic_args,
2043                    transform: None,
2044                }
2045                .split_first(self)
2046                .unwrap();
2047
2048                // HACK(eddyb) manual iteration to avoid borrowing `self`.
2049                let mut params = func.parameters.iter().enumerate();
2050                while let Some((param_ty, rest)) = params_ty_list.split_first(self) {
2051                    params_ty_list = rest;
2052
2053                    let (i, param) = params.next().unwrap();
2054                    assert_eq!(param.class.opcode, Op::FunctionParameter);
2055
2056                    debug!(
2057                        "    %{} = Op{:?} {}",
2058                        param.result_id.unwrap(),
2059                        param.class.opcode,
2060                        param_ty.display_with_infer_cx(self)
2061                    );
2062
2063                    self.record_instantiated_operand(
2064                        OperandLocation {
2065                            inst_loc: InstructionLocation::FnParam(i),
2066                            operand_idx: OperandIdx::ResultType,
2067                        },
2068                        param_ty.clone(),
2069                    );
2070                    match param_ty {
2071                        InferOperand::Var(_) | InferOperand::Instance(_) => {
2072                            self.type_of_result
2073                                .insert(param.result_id.unwrap(), param_ty);
2074                        }
2075                        InferOperand::Unknown | InferOperand::Concrete(_) => {}
2076                    }
2077                }
2078                assert_eq!(params.next(), None);
2079
2080                Some(ret_ty)
2081            }
2082
2083            _ => None,
2084        };
2085
2086        for (block_idx, block) in func.blocks.iter().enumerate() {
2087            for (inst_idx, inst) in block.instructions.iter().enumerate() {
2088                // Manually handle `OpReturnValue`/`OpReturn` because there's no
2089                // way to inject `ret_ty` into `spirv_type_constraints` rules.
2090                match inst.class.opcode {
2091                    Op::ReturnValue => {
2092                        let ret_val_id = inst.operands[0].unwrap_id_ref();
2093                        if let (Some(expected), Some(found)) = (
2094                            ret_ty.clone(),
2095                            self.type_of_result.get(&ret_val_id).cloned(),
2096                        ) && let Err(e) = self.equate_infer_operands(expected, found)
2097                        {
2098                            e.report(inst);
2099                        }
2100                    }
2101
2102                    Op::Return => {}
2103
2104                    _ => self.instantiate_instruction(
2105                        inst,
2106                        InstructionLocation::FnBody {
2107                            block_idx,
2108                            inst_idx,
2109                        },
2110                    ),
2111                }
2112            }
2113        }
2114
2115        debug!("}}");
2116        if let Some(func_ty) = self.type_of_result.get(&func_id) {
2117            debug!(" -> %{}: {}", func_id, func_ty.display_with_infer_cx(self));
2118        }
2119        debug!("");
2120    }
2121
2122    /// Helper for `into_replacements`, that computes a single `ConcreteOrParam`.
2123    /// For all `Param(p)` in `generic_params`, inference variables that resolve
2124    /// to `InferVar(p)` are replaced with `Param(p)`, whereas other inference
2125    /// variables are considered unconstrained, and are instead replaced with
2126    /// `S::concrete_fallback()` (which is chosen by the specialization).
2127    fn resolve_infer_var_to_concrete_or_param(
2128        &mut self,
2129        v: InferVar,
2130        generic_params: RangeTo<Param>,
2131    ) -> ConcreteOrParam {
2132        let v = self.resolve_infer_var(v);
2133        let InferVar(i) = v;
2134        match self.infer_var_values[i as usize] {
2135            // Guaranteed by `resolve_infer_var`.
2136            Value::SameAs(_) => unreachable!(),
2137
2138            Value::Unknown => {
2139                if i < generic_params.end.0 {
2140                    ConcreteOrParam::Param(Param(i))
2141                } else {
2142                    ConcreteOrParam::Concrete(
2143                        CopyOperand::try_from(&self.specializer.specialization.concrete_fallback())
2144                            .unwrap(),
2145                    )
2146                }
2147            }
2148            Value::Known(x) => ConcreteOrParam::Concrete(x),
2149        }
2150    }
2151
2152    /// Consume the `InferCx` and return a set of replacements that need to be
2153    /// performed to instantiate the global/function inferred with this `InferCx`.
2154    /// See `resolve_infer_var_to_concrete_or_param` for how inference variables
2155    /// are handled (using `generic_params` and `S::concrete_fallback()`).
2156    fn into_replacements(mut self, generic_params: RangeTo<Param>) -> Replacements {
2157        let mut with_instance: IndexMap<_, Vec<_>> = IndexMap::new();
2158        for (loc, instance) in mem::take(&mut self.instantiated_operands) {
2159            with_instance
2160                .entry(Instance {
2161                    generic_id: instance.generic_id,
2162                    generic_args: InferVar::range_iter(&instance.generic_args)
2163                        .map(|v| self.resolve_infer_var_to_concrete_or_param(v, generic_params))
2164                        .collect(),
2165                })
2166                .or_default()
2167                .push(loc);
2168        }
2169
2170        let with_concrete_or_param = mem::take(&mut self.inferred_operands)
2171            .into_iter()
2172            .map(|(loc, v)| {
2173                (
2174                    loc,
2175                    self.resolve_infer_var_to_concrete_or_param(v, generic_params),
2176                )
2177            })
2178            .collect();
2179
2180        Replacements {
2181            with_instance,
2182            with_concrete_or_param,
2183        }
2184    }
2185}
2186
2187// HACK(eddyb) this state could live in `Specializer` except for the fact that
2188// it's commonly mutated at the same time as parts of `Specializer` are read,
2189// and in particular this arrangement allows calling `&mut self` methods on
2190// `Expander` while (immutably) iterating over data inside the `Specializer`.
2191struct Expander<'a, S: Specialization> {
2192    specializer: &'a Specializer<S>,
2193
2194    builder: Builder,
2195
2196    /// All the instances of "generic" globals/functions that need to be expanded,
2197    /// and their cached IDs (which are allocated as-needed, before expansion).
2198    // NOTE(eddyb) this relies on `BTreeMap` so that `all_instances_of` can use
2199    // `BTreeMap::range` to get all `Instances` that share a certain ID.
2200    // FIXME(eddyb) fine-tune the length of `SmallVec<[_; 4]>` here.
2201    instances: BTreeMap<Instance<SmallVec<[CopyOperand; 4]>>, Word>,
2202
2203    /// Instances of "generic" globals/functions that have yet to have had their
2204    /// own `replacements` analyzed in order to fully collect all instances.
2205    // FIXME(eddyb) fine-tune the length of `SmallVec<[_; 4]>` here.
2206    propagate_instances_queue: VecDeque<Instance<SmallVec<[CopyOperand; 4]>>>,
2207}
2208
2209impl<'a, S: Specialization> Expander<'a, S> {
2210    fn new(specializer: &'a Specializer<S>, module: Module) -> Self {
2211        Expander {
2212            specializer,
2213
2214            builder: Builder::new_from_module(module),
2215
2216            instances: BTreeMap::new(),
2217            propagate_instances_queue: VecDeque::new(),
2218        }
2219    }
2220
2221    /// Return the subset of `instances` that have `generic_id`.
2222    /// This is efficiently implemented via `BTreeMap::range`, taking advantage
2223    /// of the derived `Ord` on `Instance`, which orders by `generic_id` first,
2224    /// resulting in `instances` being grouped by `generic_id`.
2225    fn all_instances_of(
2226        &self,
2227        generic_id: Word,
2228    ) -> std::collections::btree_map::Range<'_, Instance<SmallVec<[CopyOperand; 4]>>, Word> {
2229        let first_instance_of = |generic_id| Instance {
2230            generic_id,
2231            generic_args: SmallVec::new(),
2232        };
2233        self.instances
2234            .range(first_instance_of(generic_id)..first_instance_of(generic_id + 1))
2235    }
2236
2237    /// Allocate a new ID for `instance`, or return a cached one if it exists.
2238    /// If a new ID is created, `instance` is added to `propagate_instances_queue`,
2239    /// so that `propagate_instances` can later find all transitive dependencies.
2240    fn alloc_instance_id(&mut self, instance: Instance<SmallVec<[CopyOperand; 4]>>) -> Word {
2241        use std::collections::btree_map::Entry;
2242
2243        match self.instances.entry(instance) {
2244            Entry::Occupied(entry) => *entry.get(),
2245            Entry::Vacant(entry) => {
2246                // Get the `Instance` back from the map key, to avoid having to
2247                // clone it earlier when calling `self.instances.entry(instance)`.
2248                let instance = entry.key().clone();
2249
2250                self.propagate_instances_queue.push_back(instance);
2251                *entry.insert(self.builder.id())
2252            }
2253        }
2254    }
2255
2256    /// Process all instances seen (by `alloc_instance_id`) up until this point,
2257    /// to find the full set of instances (transitively) needed by the module.
2258    ///
2259    /// **Warning**: calling `alloc_instance_id` later, without another call to
2260    /// `propagate_instances`, will potentially result in missed instances, i.e.
2261    /// that are added to `propagate_instances_queue` but never processed.
2262    fn propagate_instances(&mut self) {
2263        while let Some(instance) = self.propagate_instances_queue.pop_back() {
2264            // Drain the iterator to generate all the `alloc_instance_id` calls.
2265            for _ in self.specializer.generics[&instance.generic_id]
2266                .replacements
2267                .to_concrete(&instance.generic_args, |i| self.alloc_instance_id(i))
2268            {}
2269        }
2270    }
2271
2272    /// Expand every "generic" global/function, and `OpName`/decorations applied
2273    /// to them, to their respective full set of instances, treating the original
2274    /// "generic" definition and its inferred `Replacements` as a template.
2275    fn expand_module(mut self) -> Module {
2276        // From here on out we assume all instances are known, so ensure there
2277        // aren't any left unpropagated.
2278        self.propagate_instances();
2279
2280        // HACK(eddyb) steal `Vec`s so that we can still call methods on `self` below.
2281        let module = self.builder.module_mut();
2282        let mut entry_points = mem::take(&mut module.entry_points);
2283        let debug_names = mem::take(&mut module.debug_names);
2284        let annotations = mem::take(&mut module.annotations);
2285        let types_global_values = mem::take(&mut module.types_global_values);
2286        let functions = mem::take(&mut module.functions);
2287
2288        // Adjust `OpEntryPoint ...` in-place to use the new IDs for *Interface*
2289        // module-scoped `OpVariable`s (which should each have one instance).
2290        for inst in &mut entry_points {
2291            let func_id = inst.operands[1].unwrap_id_ref();
2292            assert!(
2293                !self.specializer.generics.contains_key(&func_id),
2294                "entry-point %{func_id} shouldn't be \"generic\""
2295            );
2296
2297            for interface_operand in &mut inst.operands[3..] {
2298                let interface_id = interface_operand.unwrap_id_ref();
2299                let mut instances = self.all_instances_of(interface_id);
2300                match (instances.next(), instances.next()) {
2301                    (None, _) => unreachable!(
2302                        "entry-point %{} has overly-\"generic\" \
2303                         interface variable %{}, with no instances",
2304                        func_id, interface_id
2305                    ),
2306                    (Some(_), Some(_)) => unreachable!(
2307                        "entry-point %{} has overly-\"generic\" \
2308                         interface variable %{}, with too many instances: {:?}",
2309                        func_id,
2310                        interface_id,
2311                        FmtBy(|f| f
2312                            .debug_list()
2313                            .entries(self.all_instances_of(interface_id).map(
2314                                |(instance, _)| FmtBy(move |f| write!(
2315                                    f,
2316                                    "{}",
2317                                    instance.display(|generic_args| generic_args.iter().copied())
2318                                ))
2319                            ))
2320                            .finish())
2321                    ),
2322                    (Some((_, &instance_id)), None) => {
2323                        *interface_operand = Operand::IdRef(instance_id);
2324                    }
2325                }
2326            }
2327        }
2328
2329        // FIXME(eddyb) bucket `instances` into global vs function, and count
2330        // annotations separately, so that we can know exact capacities below.
2331
2332        // Expand `Op* %target ...` when `target` is "generic".
2333        let expand_debug_or_annotation = |insts: Vec<Instruction>| {
2334            let mut expanded_insts = Vec::with_capacity(insts.len().next_power_of_two());
2335            for inst in insts {
2336                if let [Operand::IdRef(target), ..] = inst.operands[..]
2337                    && self.specializer.generics.contains_key(&target)
2338                {
2339                    expanded_insts.extend(self.all_instances_of(target).map(
2340                        |(_, &instance_id)| {
2341                            let mut expanded_inst = inst.clone();
2342                            expanded_inst.operands[0] = Operand::IdRef(instance_id);
2343                            expanded_inst
2344                        },
2345                    ));
2346                    continue;
2347                }
2348                expanded_insts.push(inst);
2349            }
2350            expanded_insts
2351        };
2352
2353        // Expand `Op(Member)Name %target ...` when `target` is "generic".
2354        let expanded_debug_names = expand_debug_or_annotation(debug_names);
2355
2356        // Expand `Op(Member)Decorate* %target ...`, when `target` is "generic".
2357        let mut expanded_annotations = expand_debug_or_annotation(annotations);
2358
2359        // Expand "generic" globals (types, constants and module-scoped variables).
2360        let mut expanded_types_global_values =
2361            Vec::with_capacity(types_global_values.len().next_power_of_two());
2362        for inst in types_global_values {
2363            if let Some(result_id) = inst.result_id
2364                && let Some(generic) = self.specializer.generics.get(&result_id)
2365            {
2366                expanded_types_global_values.extend(self.all_instances_of(result_id).map(
2367                    |(instance, &instance_id)| {
2368                        let mut expanded_inst = inst.clone();
2369                        expanded_inst.result_id = Some(instance_id);
2370                        for (loc, operand) in generic
2371                            .replacements
2372                            .to_concrete(&instance.generic_args, |i| self.instances[&i])
2373                        {
2374                            expanded_inst.index_set(loc, operand.into());
2375                        }
2376                        expanded_inst
2377                    },
2378                ));
2379                continue;
2380            }
2381            expanded_types_global_values.push(inst);
2382        }
2383
2384        // Expand "generic" functions.
2385        let mut expanded_functions = Vec::with_capacity(functions.len().next_power_of_two());
2386        for func in functions {
2387            let func_id = func.def_id().unwrap();
2388            if let Some(generic) = self.specializer.generics.get(&func_id) {
2389                let old_expanded_functions_len = expanded_functions.len();
2390                expanded_functions.extend(self.all_instances_of(func_id).map(
2391                    |(instance, &instance_id)| {
2392                        let mut expanded_func = func.clone();
2393                        expanded_func.def.as_mut().unwrap().result_id = Some(instance_id);
2394                        for (loc, operand) in generic
2395                            .replacements
2396                            .to_concrete(&instance.generic_args, |i| self.instances[&i])
2397                        {
2398                            expanded_func.index_set(loc, operand.into());
2399                        }
2400                        expanded_func
2401                    },
2402                ));
2403
2404                // Renumber all of the IDs defined within the function itself,
2405                // to avoid conflicts between all the expanded copies.
2406                // While some passes (such as inlining) may handle IDs reuse
2407                // between different function bodies (mostly because they do
2408                // their own renumbering), it's better not to tempt fate here.
2409                // FIXME(eddyb) use compact IDs for more efficient renumbering.
2410                let newly_expanded_functions =
2411                    &mut expanded_functions[old_expanded_functions_len..];
2412                if newly_expanded_functions.len() > 1 {
2413                    // NOTE(eddyb) this is defined outside the loop to avoid
2414                    // allocating it for every expanded copy of the function.
2415                    let mut rewrite_rules = FxHashMap::default();
2416
2417                    for func in newly_expanded_functions {
2418                        rewrite_rules.clear();
2419
2420                        rewrite_rules.extend(func.parameters.iter_mut().map(|param| {
2421                            let old_id = param.result_id.unwrap();
2422                            let new_id = self.builder.id();
2423
2424                            // HACK(eddyb) this is only needed because we're using
2425                            // `apply_rewrite_rules` and that only works on `Block`s,
2426                            // it should be generalized to handle `Function`s too.
2427                            param.result_id = Some(new_id);
2428
2429                            (old_id, new_id)
2430                        }));
2431                        rewrite_rules.extend(
2432                            func.blocks
2433                                .iter()
2434                                .flat_map(|b| b.label.iter().chain(b.instructions.iter()))
2435                                .filter_map(|inst| inst.result_id)
2436                                .map(|old_id| (old_id, self.builder.id())),
2437                        );
2438
2439                        super::apply_rewrite_rules(&rewrite_rules, &mut func.blocks);
2440
2441                        // HACK(eddyb) this duplicates similar logic from `inline`.
2442                        for annotation_idx in 0..expanded_annotations.len() {
2443                            let inst = &expanded_annotations[annotation_idx];
2444                            if let [Operand::IdRef(target), ..] = inst.operands[..]
2445                                && let Some(&rewritten_target) = rewrite_rules.get(&target)
2446                            {
2447                                let mut expanded_inst = inst.clone();
2448                                expanded_inst.operands[0] = Operand::IdRef(rewritten_target);
2449                                expanded_annotations.push(expanded_inst);
2450                            }
2451                        }
2452                    }
2453                }
2454
2455                continue;
2456            }
2457            expanded_functions.push(func);
2458        }
2459
2460        // No new instances should've been found during expansion - they would've
2461        // panicked while attempting to get `self.instances[&instance]` anyway.
2462        assert!(self.propagate_instances_queue.is_empty());
2463
2464        let module = self.builder.module_mut();
2465        module.entry_points = entry_points;
2466        module.debug_names = expanded_debug_names;
2467        module.annotations = expanded_annotations;
2468        module.types_global_values = expanded_types_global_values;
2469        module.functions = expanded_functions;
2470
2471        self.builder.module()
2472    }
2473
2474    fn dump_instances(&self, w: &mut impl io::Write) -> io::Result<()> {
2475        writeln!(w, "; All specializer \"generic\"s and their instances:")?;
2476        writeln!(w)?;
2477
2478        // FIXME(eddyb) maybe dump (transitive) dependencies? could use a def-use graph.
2479        for (&generic_id, generic) in &self.specializer.generics {
2480            if let Some(name) = self.specializer.debug_names.get(&generic_id) {
2481                writeln!(w, "; {name}")?;
2482            }
2483
2484            write!(
2485                w,
2486                "{} = Op{:?}",
2487                Instance {
2488                    generic_id,
2489                    generic_args: Param(0)..Param(generic.param_count)
2490                }
2491                .display(Param::range_iter),
2492                generic.def.class.opcode
2493            )?;
2494            let mut next_param = Param(0);
2495            for operand in generic
2496                .def
2497                .result_type
2498                .map(Operand::IdRef)
2499                .iter()
2500                .chain(generic.def.operands.iter())
2501            {
2502                write!(w, " ")?;
2503                let (needed, used_generic) = self.specializer.params_needed_by(operand);
2504                let params = next_param..Param(next_param.0 + needed);
2505
2506                // NOTE(eddyb) see HACK comment in `instantiate_instruction`.
2507                if generic.def.class.opcode != Op::Function {
2508                    next_param = params.end;
2509                }
2510
2511                if used_generic.is_some() {
2512                    write!(
2513                        w,
2514                        "{}",
2515                        Instance {
2516                            generic_id: operand.unwrap_id_ref(),
2517                            generic_args: params
2518                        }
2519                        .display(Param::range_iter)
2520                    )?;
2521                } else if needed == 1 {
2522                    write!(w, "{}", params.start)?;
2523                } else {
2524                    write!(w, "{operand}")?;
2525                }
2526            }
2527            writeln!(w)?;
2528
2529            if let Some(param_values) = &generic.param_values {
2530                write!(w, "        where")?;
2531                for (i, v) in param_values.iter().enumerate() {
2532                    let p = Param(i as u32);
2533                    match v {
2534                        Value::Unknown => {}
2535                        Value::Known(o) => write!(w, " {p} = {o},")?,
2536                        Value::SameAs(q) => write!(w, " {p} = {q},")?,
2537                    }
2538                }
2539                writeln!(w)?;
2540            }
2541
2542            for (instance, instance_id) in self.all_instances_of(generic_id) {
2543                assert_eq!(instance.generic_id, generic_id);
2544                writeln!(
2545                    w,
2546                    "    %{} = {}",
2547                    instance_id,
2548                    instance.display(|generic_args| generic_args.iter().copied())
2549                )?;
2550            }
2551
2552            writeln!(w)?;
2553        }
2554        Ok(())
2555    }
2556}