Skip to main content

dxbc_reader/
decode.rs

1//! The token-level decoder of Shader Model 4 and 5 programs: any stage's version token, declarations as typed
2//! records, and instructions as an opcode, its control bits, its extended tokens and its operands. It decides nothing
3//! about which instructions a caller can use: they are all returned, and the caller refuses what it cannot handle. A token
4//! it cannot read at all (an operand register type it has no name for, a truncated instruction) is an error naming the
5//! word.
6
7use crate::format::{OperandFormat, operand_format};
8use crate::opcodes::NAMES;
9use crate::{Result, error};
10
11/// The low byte of `v`.
12fn low8(v: u32) -> u8 {
13    let [byte, ..] = v.to_le_bytes();
14    byte
15}
16
17/// The low 16 bits of `v`.
18fn low16(v: u32) -> u16 {
19    let [a, b, ..] = v.to_le_bytes();
20    u16::from_le_bytes([a, b])
21}
22
23/// The kind of register an operand names.
24#[derive(Clone, Copy, Debug, PartialEq, Eq)]
25pub enum Register {
26    /// A temporary register (`r<n>`).
27    Temp,
28    /// An input register (`v<n>`).
29    Input,
30    /// An output register (`o<n>`).
31    Output,
32    /// An indexable temporary (`x<n>[i]`).
33    IndexableTemp,
34    /// A constant buffer register (`cb<slot>[row]`).
35    Constant,
36    /// An immediate operand (`l(...)`), its values in [`Operand::immediate`].
37    Immediate,
38    /// A 64-bit immediate operand (`d(...)`): its values in [`Operand::immediate`] as pairs of words, low word first
39    /// (two words for one value, four for two).
40    Immediate64,
41    /// A subroutine label (`label <n>`), the operand of `label`, `call` and `callc`.
42    Label,
43    /// A function body of a program with class linkage (`fb<n>`).
44    FunctionBody,
45    /// A function table of a program with class linkage (`ft<n>`).
46    FunctionTable,
47    /// An interface pointer of a program with class linkage (`fp<n>[array][call site]`).
48    Interface,
49    /// A function's input register (`fi<n>`).
50    FunctionInput,
51    /// A function's output register (`fo<n>`).
52    FunctionOutput,
53    /// The `this` pointer of a class instance (`this[<n>]`).
54    ThisPointer,
55    /// `vCycleCounter`, the input clock counter.
56    CycleCounter,
57    /// `vInnerCoverage`, the input inner coverage.
58    InnerCoverage,
59    /// A shader resource view (`t<n>`).
60    Texture,
61    /// A sampler (`s<n>`).
62    Sampler,
63    /// The null register (`null`), a destination that discards its result.
64    Null,
65    /// An unordered-access view (`u<n>`).
66    Uav,
67    /// The immediate constant buffer (`icb[n]`), its rows the program's own.
68    ImmediateBuffer,
69    /// `vPrim`, the primitive id input.
70    PrimitiveId,
71    /// `vCoverage`, the input coverage mask.
72    InputCoverage,
73    /// `oDepth`.
74    OutputDepth,
75    /// `oDepthGE`, a depth output that is at least the rasterized depth.
76    OutputDepthGreaterEqual,
77    /// `oDepthLE`, a depth output that is at most the rasterized depth.
78    OutputDepthLessEqual,
79    /// `oMask`, the coverage output.
80    OutputCoverage,
81    /// `rasterizer`: the render target as `sample_info` and `sample_pos` query it (its sample count, a sample's position).
82    Rasterizer,
83    /// `oStencilRef`, the stencil reference output.
84    OutputStencilRef,
85    /// `g<n>`, thread-group shared memory.
86    GroupSharedMemory,
87    /// `vThreadID`, `vThreadGroupID`, `vThreadIDInGroup` and `vThreadIDInGroupFlattened`, a compute program's ids.
88    ThreadId,
89    /// `vThreadGroupID`.
90    ThreadGroupId,
91    /// `vThreadIDInGroup`.
92    ThreadIdInGroup,
93    /// `vThreadIDInGroupFlattened`.
94    ThreadIdInGroupFlattened,
95    /// `vOutputControlPointID`, `vForkInstanceID`, `vJoinInstanceID`: a hull program's phase numbers.
96    OutputControlPointId,
97    /// `vForkInstanceID`, a hull program's fork phase instance.
98    ForkInstanceId,
99    /// `vJoinInstanceID`, a hull program's join phase instance.
100    JoinInstanceId,
101    /// `vicp[point][register]`: an input control point of a hull or domain program.
102    InputControlPoint,
103    /// `vocp[point][register]`: an output control point a hull program's patch-constant phases read.
104    OutputControlPoint,
105    /// `vpc[register]`: a patch constant a domain program reads (or a hull program's join phase).
106    InputPatchConstant,
107    /// `vDomain`: a domain program's location in its patch.
108    InputDomainPoint,
109    /// `m<n>`: a geometry program's output stream.
110    Stream,
111    /// `vGSInstanceID`.
112    GsInstanceId,
113}
114impl Register {
115    /// How many index dimensions an operand of this register has in Shader Model 4 and 5.0.
116    fn dimensions(self) -> u32 {
117        match self {
118            Register::Immediate
119            | Register::Immediate64
120            | Register::CycleCounter
121            | Register::InnerCoverage
122            | Register::Null
123            | Register::PrimitiveId
124            | Register::InputCoverage
125            | Register::OutputDepth
126            | Register::OutputDepthGreaterEqual
127            | Register::OutputDepthLessEqual
128            | Register::OutputCoverage
129            | Register::Rasterizer
130            | Register::OutputStencilRef
131            | Register::ThreadId
132            | Register::ThreadGroupId
133            | Register::ThreadIdInGroup
134            | Register::ThreadIdInGroupFlattened
135            | Register::OutputControlPointId
136            | Register::ForkInstanceId
137            | Register::JoinInstanceId
138            | Register::InputDomainPoint
139            | Register::GsInstanceId => 0,
140            Register::Constant
141            | Register::IndexableTemp
142            | Register::InputControlPoint
143            | Register::OutputControlPoint
144            | Register::Interface => 2,
145            _ => 1,
146        }
147    }
148    fn from_type(n: u32) -> Option<Self> {
149        Some(match n {
150            0 => Register::Temp,
151            1 => Register::Input,
152            2 => Register::Output,
153            3 => Register::IndexableTemp,
154            4 => Register::Immediate,
155            5 => Register::Immediate64,
156            10 => Register::Label,
157            17 => Register::FunctionBody,
158            18 => Register::FunctionTable,
159            19 => Register::Interface,
160            20 => Register::FunctionInput,
161            21 => Register::FunctionOutput,
162            29 => Register::ThisPointer,
163            40 => Register::CycleCounter,
164            42 => Register::InnerCoverage,
165            6 => Register::Sampler,
166            7 => Register::Texture,
167            8 => Register::Constant,
168            9 => Register::ImmediateBuffer,
169            11 => Register::PrimitiveId,
170            12 => Register::OutputDepth,
171            13 => Register::Null,
172            14 => Register::Rasterizer,
173            15 => Register::OutputCoverage,
174            16 => Register::Stream,
175            22 => Register::OutputControlPointId,
176            23 => Register::ForkInstanceId,
177            24 => Register::JoinInstanceId,
178            25 => Register::InputControlPoint,
179            26 => Register::OutputControlPoint,
180            27 => Register::InputPatchConstant,
181            28 => Register::InputDomainPoint,
182            37 => Register::GsInstanceId,
183            30 => Register::Uav,
184            31 => Register::GroupSharedMemory,
185            32 => Register::ThreadId,
186            33 => Register::ThreadGroupId,
187            34 => Register::ThreadIdInGroup,
188            36 => Register::ThreadIdInGroupFlattened,
189            35 => Register::InputCoverage,
190            38 => Register::OutputDepthGreaterEqual,
191            39 => Register::OutputDepthLessEqual,
192            41 => Register::OutputStencilRef,
193            _ => return None,
194        })
195    }
196}
197
198/// The components of a register an operand selects.
199#[derive(Clone, Copy, Debug, PartialEq, Eq)]
200pub enum Selection {
201    /// A write mask: bit 0 is x, bit 3 is w.
202    Mask(u8),
203    /// A four-component swizzle: for each result lane the source lane (0 x to 3 w) it reads.
204    Swizzle([u8; 4]),
205    /// One source lane (0 x to 3 w).
206    Scalar(u8),
207    /// No selection: the operand has no components (a sampler, a resource).
208    None,
209}
210
211/// An instruction's or declaration's operand.
212#[derive(Clone, Debug)]
213pub struct Operand {
214    /// The kind of register it names.
215    pub register: Register,
216    /// Which components it selects.
217    pub selection: Selection,
218    /// The number of components the operand token states (0, 1 or 4): a one-component operand (`oDepth`, `l(1.0)`)
219    /// and a four-component register selecting one lane (`r0.x`) both have a [`Selection::Scalar`].
220    pub components: u8,
221    /// Its index dimensions, outermost first: none for an immediate, one for `r3`, two for `cb2[5]`.
222    pub indices: Vec<Index>,
223    /// The values of an immediate operand (one lane repeated, or four); zero for other registers.
224    pub immediate: [u32; 4],
225    /// 0 none, 1 negate, 2 absolute, 3 negated absolute (float sources); 4 two's-complement negate (an integer
226    /// instruction's negate; the encoding has no such value, so a caller that knows the instruction reads integers sets it).
227    pub modifier: u8,
228    /// The minimum-precision annotation of the operand's extended token (0 none; 1 float16, 2 float2_8, 4 sint16,
229    /// 5 uint16).
230    pub precision: u8,
231    /// The operand's extended token marks it as a non-uniform resource index.
232    pub non_uniform: bool,
233}
234
235/// One index dimension of an operand: a constant, a relative register, or both added.
236#[derive(Clone, Debug)]
237pub struct Index {
238    /// The constant part of the index.
239    pub base: u32,
240    /// The register whose component is added to `base`, where the index is relative.
241    pub relative: Option<Box<Operand>>,
242}
243
244/// An instruction as stored.
245#[derive(Clone, Debug)]
246pub struct Instruction {
247    /// The instruction's first word in the token stream.
248    pub word: usize,
249    /// The opcode token's low 11 bits (see [`opcode_name`]).
250    pub opcode: u16,
251    /// The opcode token's bits 11 to 23: saturate and the opcode-specific controls (the test of a conditional, a
252    /// `resinfo` return type, the interpolation of a declaration).
253    pub control: u32,
254    /// The opcode's extended tokens as stored (sample offsets, resource dimension, return type).
255    pub extensions: Vec<u32>,
256    /// The saturate bit of the opcode token.
257    pub saturate: bool,
258    /// A conditional (`if`, `breakc`, `continuec`) taken when its operand is nonzero (`_nz`), else when zero (`_z`).
259    pub nonzero: bool,
260    /// The operands, destination first where the instruction has one.
261    pub operands: Vec<Operand>,
262    /// The call site an `interface_call` stores before its operand (the entry of the interface's function tables it
263    /// calls); `None` for other instructions.
264    pub function: Option<u32>,
265}
266impl Instruction {
267    /// The opcode's name (`mov`, `add`, ...), or `unknown opcode`.
268    pub fn name(&self) -> &'static str {
269        opcode_name(self.opcode)
270    }
271    /// The sample offsets of the extended token of kind 1 (`_aoffimmi`), if the instruction has one.
272    pub fn sample_offset(&self) -> Option<[i32; 3]> {
273        self.extension(1).map(|e| {
274            let field = |shift: u32| (((e >> shift) & 15) as i32) << 28 >> 28;
275            [field(9), field(13), field(17)]
276        })
277    }
278    /// The resource dimension and structured stride of the extended token of kind 2.
279    pub fn resource_dimension(&self) -> Option<(u32, u32)> {
280        self.extension(2).map(|e| ((e >> 6) & 31, (e >> 11) & 0xf_ffff))
281    }
282    /// The return-type token of the extended token of kind 3 (four nibbles).
283    pub fn return_type(&self) -> Option<u32> {
284        self.extension(3).map(|e| (e >> 6) & 0xffff)
285    }
286    fn extension(&self, kind: u32) -> Option<u32> {
287        self.extensions.iter().copied().find(|e| e & 63 == kind)
288    }
289    /// The opcode's operand format (see [`operand_format`]); `decode` has checked the operand count against it.
290    pub fn format(&self) -> Option<OperandFormat> {
291        operand_format(self.opcode)
292    }
293    /// The destination operands and the source operands, split as the opcode's format says; `None` for an opcode
294    /// with no format.
295    pub fn destinations_and_sources(&self) -> Option<(&[Operand], &[Operand])> {
296        self.operands.split_at_checked(self.format()?.destinations.len())
297    }
298}
299
300/// The name of an opcode in Microsoft's tokenized program format, or `unknown opcode`.
301pub fn opcode_name(opcode: u16) -> &'static str {
302    NAMES
303        .get(usize::from(opcode))
304        .copied()
305        .unwrap_or("unknown opcode")
306}
307
308/// The type of a program's four return lanes in a resource declaration, as D3D_RETURN_TYPE codes (1 unorm, 2 snorm,
309/// 3 sint, 4 uint, 5 float, 6 mixed, 7 double, 8 continued, 9 unused), one nibble per lane.
310pub type ReturnCodes = [u8; 4];
311
312/// The registers a Shader Model 5.1 declaration binds and their register space (`T1[4:7], space=1`). Shader Model 5.0
313/// declares each register on its own, in space 0; 5.1 names the resource by an id (the declaration's `slot`) and
314/// instructions by the id and a register of the range.
315#[derive(Clone, Copy, Debug, PartialEq, Eq)]
316pub struct RegisterRange {
317    /// The first register.
318    pub lower: u32,
319    /// The last register; `u32::MAX` for an array of no stated size (`T0[0:*]`).
320    pub upper: u32,
321    /// The register space.
322    pub space: u32,
323}
324
325/// A declaration, typed. `word` is where it starts.
326#[derive(Clone, Debug)]
327pub struct Declaration {
328    /// The word of the token stream the declaration starts at.
329    pub word: usize,
330    /// The opcode token's low 11 bits (see [`opcode_name`]).
331    pub opcode: u16,
332    /// The opcode token's bits 11 to 23 as stored.
333    pub control: u32,
334    /// The declaration, typed.
335    pub kind: Declared,
336}
337
338/// A declaration, typed where this reader knows its layout.
339#[derive(Clone, Debug)]
340pub enum Declared {
341    /// `dcl_temps`.
342    Temps(u32),
343    /// `dcl_indexable_temp x<register>[size], components`.
344    IndexableTemp {
345        /// The register index (the `<n>` of `x<n>`).
346        register: u32,
347        /// The number of elements.
348        size: u32,
349        /// The number of components per element.
350        components: u32,
351    },
352    /// `dcl_globalFlags`: the control bits as stored (bit 0 refactoring allowed, 1 double precision, 2 forced early
353    /// depth-stencil, 3 raw and structured buffers, 4 skip optimization, 5 min precision, 6 double extensions, 7
354    /// shader extensions).
355    GlobalFlags(u32),
356    /// `dcl_constantbuffer cb<slot>[rows]`; `dynamic` when it is dynamically indexed.
357    ConstantBuffer {
358        /// The buffer slot (the `<slot>` of `cb<slot>`) in Shader Model 5.0, its id in 5.1 (its registers in `range`).
359        slot: u32,
360        /// The number of 16-byte rows; 0 where the declaration states none.
361        rows: u32,
362        /// The buffer is dynamically indexed.
363        dynamic: bool,
364        /// The number of index dimensions the operand had (1 or 2; 3 in Shader Model 5.1).
365        dimensions: usize,
366        /// The registers and space of a Shader Model 5.1 declaration; `None` before 5.1.
367        range: Option<RegisterRange>,
368    },
369    /// `dcl_sampler s<slot>`, its mode: 0 default, 1 comparison, 2 mono.
370    Sampler {
371        /// The sampler slot in Shader Model 5.0, its id in 5.1 (its registers in `range`).
372        slot: u32,
373        /// 0 default, 1 comparison, 2 mono.
374        mode: u32,
375        /// The registers and space of a Shader Model 5.1 declaration; `None` before 5.1.
376        range: Option<RegisterRange>,
377    },
378    /// `dcl_resource_<dimension> t<slot>`: the D3D resource dimension code, the sample count of a multisampled one,
379    /// and the four return types.
380    Resource {
381        /// The resource slot in Shader Model 5.0, its id in 5.1 (its registers in `range`).
382        slot: u32,
383        /// The D3D resource dimension code.
384        dimension: u32,
385        /// The sample count of a multisampled resource.
386        samples: u32,
387        /// The return type of each lane.
388        returns: ReturnCodes,
389        /// The return type token as stored.
390        token: u32,
391        /// The registers and space of a Shader Model 5.1 declaration; `None` before 5.1.
392        range: Option<RegisterRange>,
393    },
394    /// `dcl_resource_raw t<slot>`.
395    ResourceRaw {
396        /// The resource slot in Shader Model 5.0, its id in 5.1 (its registers in `range`).
397        slot: u32,
398        /// The registers and space of a Shader Model 5.1 declaration; `None` before 5.1.
399        range: Option<RegisterRange>,
400    },
401    /// `dcl_resource_structured t<slot>, stride`.
402    ResourceStructured {
403        /// The resource slot in Shader Model 5.0, its id in 5.1 (its registers in `range`).
404        slot: u32,
405        /// The element stride in bytes.
406        stride: u32,
407        /// The registers and space of a Shader Model 5.1 declaration; `None` before 5.1.
408        range: Option<RegisterRange>,
409    },
410    /// `dcl_uav_typed` and its control bits (dimension in bits 0 to 4, globally coherent bit 5).
411    UavTyped {
412        /// The UAV slot in Shader Model 5.0, its id in 5.1 (its registers in `range`).
413        slot: u32,
414        /// The D3D resource dimension code.
415        dimension: u32,
416        /// The return type of each lane.
417        returns: ReturnCodes,
418        /// The return type token as stored.
419        token: u32,
420        /// The opcode token's control bits.
421        control: u32,
422        /// The registers and space of a Shader Model 5.1 declaration; `None` before 5.1.
423        range: Option<RegisterRange>,
424    },
425    /// `dcl_uav_raw u<slot>`.
426    UavRaw {
427        /// The UAV slot in Shader Model 5.0, its id in 5.1 (its registers in `range`).
428        slot: u32,
429        /// The opcode token's control bits.
430        control: u32,
431        /// The registers and space of a Shader Model 5.1 declaration; `None` before 5.1.
432        range: Option<RegisterRange>,
433    },
434    /// `dcl_uav_structured u<slot>, stride`.
435    UavStructured {
436        /// The UAV slot in Shader Model 5.0, its id in 5.1 (its registers in `range`).
437        slot: u32,
438        /// The element stride in bytes.
439        stride: u32,
440        /// The opcode token's control bits.
441        control: u32,
442        /// The registers and space of a Shader Model 5.1 declaration; `None` before 5.1.
443        range: Option<RegisterRange>,
444    },
445    /// `dcl_thread_group x, y, z`.
446    ThreadGroup([u32; 3]),
447    /// `dcl_tgsm_raw g<register>, bytes`.
448    GroupSharedRaw {
449        /// The shared memory register index.
450        register: u32,
451        /// Its size in bytes.
452        bytes: u32,
453    },
454    /// `dcl_tgsm_structured g<register>, stride, count`.
455    GroupSharedStructured {
456        /// The shared memory register index.
457        register: u32,
458        /// The element stride in bytes.
459        stride: u32,
460        /// The number of elements.
461        count: u32,
462    },
463    /// An input register or special input: `dcl_input`, `_sgv`, `_siv`, `_ps`, `_ps_sgv`, `_ps_siv`. `interpolation` is
464    /// a pixel input's `D3D_INTERPOLATION_MODE`; `name` a system value's `D3D_NAME`.
465    Input {
466        /// The input register or special input the declaration names.
467        operand: Operand,
468        /// The interpolation mode of a pixel input.
469        interpolation: Option<u32>,
470        /// The system value (`D3D_NAME`) of a `_sgv` or `_siv` declaration.
471        name: Option<u32>,
472    },
473    /// An output register or special output: `dcl_output`, `_sgv`, `_siv`.
474    Output {
475        /// The output register or special output the declaration names.
476        operand: Operand,
477        /// The system value (`D3D_NAME`) of a `_siv` or `_sgv` declaration.
478        name: Option<u32>,
479    },
480    /// `dcl_indexrange`: registers from the operand's that a program indexes as one array, `count` of them.
481    IndexRange {
482        /// The first register of the range.
483        operand: Operand,
484        /// The number of registers.
485        count: u32,
486    },
487    /// `dcl_stream m<n>`: a geometry program's output stream that the declarations after it describe.
488    Stream(Operand),
489    /// `dcl_immediateConstantBuffer`.
490    ImmediateBuffer(Vec<[u32; 4]>),
491    /// Custom data that is not an immediate constant buffer (a comment, debug information, ...) by its class.
492    CustomData {
493        /// The custom data class.
494        class: u32,
495    },
496    /// `dcl_function_body fb<id>`: a function body of a program with class linkage.
497    FunctionBody(u32),
498    /// `dcl_function_table ft<id> = {fb..}`: the function bodies a class's table holds, one per call site.
499    FunctionTable {
500        /// The table's id.
501        id: u32,
502        /// The function bodies, by call site.
503        bodies: Vec<u32>,
504    },
505    /// `dcl_interface fp<id>[array][sites] = {ft..}`: an interface pointer, or an array of them.
506    Interface {
507        /// The interface pointer's id, its first where it is an array.
508        id: u32,
509        /// The pointer is indexed by a register (`dcl_interface_dynamicindexed`).
510        dynamic: bool,
511        /// The number of call sites, the length of each of its tables.
512        sites: u32,
513        /// The array length, 1 for one pointer.
514        length: u32,
515        /// The function tables of the classes that can be bound to it.
516        tables: Vec<u32>,
517    },
518    /// A declaration this decoder does not type: stage-specific (geometry, tessellation, compute).
519    Other {
520        /// The opcode.
521        opcode: u16,
522        /// The opcode token's control bits.
523        control: u32,
524        /// The declaration's words after its operands, as stored.
525        words: Vec<u32>,
526    },
527}
528
529impl Declared {
530    /// The registers and space a resource, sampler, view or constant buffer declaration binds: its `range` in Shader
531    /// Model 5.1, its one register in space 0 before; `None` for the other declarations.
532    pub fn registers(&self) -> Option<RegisterRange> {
533        let (slot, range) = match self {
534            Declared::ConstantBuffer { slot, range, .. }
535            | Declared::Sampler { slot, range, .. }
536            | Declared::Resource { slot, range, .. }
537            | Declared::ResourceRaw { slot, range }
538            | Declared::ResourceStructured { slot, range, .. }
539            | Declared::UavTyped { slot, range, .. }
540            | Declared::UavRaw { slot, range, .. }
541            | Declared::UavStructured { slot, range, .. } => (*slot, *range),
542            _ => return None,
543        };
544        Some(range.unwrap_or(RegisterRange {
545            lower: slot,
546            upper: slot,
547            space: 0,
548        }))
549    }
550}
551
552/// The kind of program a version token names.
553#[derive(Clone, Copy, Debug, PartialEq, Eq)]
554pub enum ProgramType {
555    /// A pixel program (`ps`).
556    Pixel,
557    /// A vertex program (`vs`).
558    Vertex,
559    /// A geometry program (`gs`).
560    Geometry,
561    /// A hull program (`hs`).
562    Hull,
563    /// A domain program (`ds`).
564    Domain,
565    /// A compute program (`cs`).
566    Compute,
567}
568impl ProgramType {
569    /// The target's stage prefix (`ps`, `vs`, ...).
570    pub fn name(self) -> &'static str {
571        match self {
572            ProgramType::Pixel => "ps",
573            ProgramType::Vertex => "vs",
574            ProgramType::Geometry => "gs",
575            ProgramType::Hull => "hs",
576            ProgramType::Domain => "ds",
577            ProgramType::Compute => "cs",
578        }
579    }
580}
581
582/// A decoded token stream: its target, declarations and instructions, each in program order.
583#[derive(Clone, Debug)]
584pub struct Decoded {
585    /// The program's stage.
586    pub kind: ProgramType,
587    /// The shader model's major version.
588    pub major: u32,
589    /// The shader model's minor version.
590    pub minor: u32,
591    /// The declarations, in program order.
592    pub declarations: Vec<Declaration>,
593    /// The instructions, in program order.
594    pub instructions: Vec<Instruction>,
595}
596impl Decoded {
597    /// The target's name (`ps_5_0`).
598    pub fn target(&self) -> String {
599        format!("{}_{}_{}", self.kind.name(), self.major, self.minor)
600    }
601}
602
603#[derive(Clone)]
604struct Cursor<'a> {
605    words: &'a [u32],
606    at: usize,
607    origin: usize,
608    /// The program's type: the input registers of a geometry, hull or domain program are indexed by vertex.
609    kind: ProgramType,
610    /// The program is Shader Model 5.1, whose resource, sampler, view and constant buffer operands carry the
611    /// resource's id as their first index.
612    sm51: bool,
613    /// The words are a declaration's, whose 5.1 binding operands give the id and the first and last register.
614    declaration: bool,
615}
616impl Cursor<'_> {
617    /// The word the cursor is at, in the token stream.
618    fn position(&self) -> usize {
619        self.origin.saturating_add(self.at)
620    }
621    fn take(&mut self) -> Result<u32> {
622        let value = *self
623            .words
624            .get(self.at)
625            .ok_or_else(|| error(self.position(), "truncated instruction"))?;
626        self.at = self.at.saturating_add(1);
627        Ok(value)
628    }
629    fn rest(&mut self) -> Vec<u32> {
630        let rest = self.words.get(self.at..).unwrap_or_default().to_vec();
631        self.at = self.words.len();
632        rest
633    }
634    fn operand(&mut self, depth: usize) -> Result<Operand> {
635        let pos = self.position();
636        if depth > 2 {
637            return Err(error(pos, "nested relative operand exceeds bound"));
638        }
639        let t = self.take()?;
640        let components = t & 3;
641        let selection = match components {
642            0 => Selection::None,
643            1 => Selection::Scalar(0),
644            2 => match (t >> 2) & 3 {
645                0 => Selection::Mask(low8((t >> 4) & 15)),
646                1 => Selection::Swizzle([4, 6, 8, 10].map(|shift| low8((t >> shift) & 3))),
647                2 => Selection::Scalar(low8((t >> 4) & 3)),
648                _ => return Err(error(pos, "reserved component selection")),
649            },
650            _ => return Err(error(pos, "unsupported N-component operand")),
651        };
652        let kind = (t >> 12) & 255;
653        let register = Register::from_type(kind)
654            .ok_or_else(|| error(pos, format!("unsupported operand register type {kind}")))?;
655        let (mut modifier, mut precision, mut non_uniform) = (0, 0, false);
656        if t >> 31 != 0 {
657            let mut ext = self.take()?;
658            loop {
659                // only the modifier extension (kind 1) exists in Shader Model 4 and 5
660                if ext & 63 != 1 {
661                    return Err(error(pos, "unsupported extended operand (precision/modifier)"));
662                }
663                modifier = low8((ext >> 6) & 255);
664                precision = low8((ext >> 14) & 7);
665                non_uniform = ext & (1 << 17) != 0;
666                if ext >> 31 == 0 {
667                    break;
668                }
669                ext = self.take()?;
670            }
671            if modifier > 3 {
672                return Err(error(pos, "unsupported extended operand (precision/modifier)"));
673            }
674        }
675        let dimensions = (t >> 20) & 3;
676        let expected = match (register, self.kind) {
677            (Register::Input, ProgramType::Geometry | ProgramType::Hull | ProgramType::Domain) => 2,
678            // Shader Model 5.1: `CB1[id][register][row]`, `T0[id][register]`; declared `CB1[id][lower:upper]`
679            (Register::Constant, _) if self.sm51 => 3,
680            (Register::Texture | Register::Sampler | Register::Uav, _) if self.sm51 => {
681                if self.declaration && depth == 0 { 3 } else { 2 }
682            }
683            _ => register.dimensions(),
684        };
685        if dimensions != expected {
686            return Err(error(pos, "unexpected operand index dimensions"));
687        }
688        let mut indices = Vec::new();
689        // the representation of each index dimension, three bits each from bit 22
690        for shift in [22, 25, 28].into_iter().take(dimensions as usize) {
691            let representation = (t >> shift) & 7;
692            if !matches!(representation, 0 | 2 | 3) {
693                return Err(error(pos, "unsupported 64-bit/reserved index"));
694            }
695            let base = if representation != 2 { self.take()? } else { 0 };
696            let relative = if representation != 0 {
697                Some(Box::new(self.operand(depth.saturating_add(1))?))
698            } else {
699                None
700            };
701            indices.push(Index { base, relative });
702        }
703        let mut immediate = [0; 4];
704        match (register, components) {
705            (Register::Immediate, 1) => immediate = [self.take()?; 4],
706            // four 32-bit values, or two 64-bit values of two words each
707            (Register::Immediate | Register::Immediate64, 2) => {
708                for x in &mut immediate {
709                    *x = self.take()?;
710                }
711            }
712            (Register::Immediate64, 1) => {
713                let (low, high) = (self.take()?, self.take()?);
714                immediate = [low, high, low, high];
715            }
716            (Register::Immediate | Register::Immediate64, _) => {
717                return Err(error(pos, "immediate requires one or four components"));
718            }
719            _ => {}
720        }
721        Ok(Operand {
722            register,
723            selection,
724            components: match components {
725                0 => 0,
726                1 => 1,
727                _ => 4,
728            },
729            indices,
730            immediate,
731            modifier,
732            precision,
733            non_uniform,
734        })
735    }
736}
737
738/// `interface_call`, whose first word after its token is a call site, not an operand.
739const INTERFACE_CALL: u16 = 120;
740
741fn return_codes(token: u32) -> ReturnCodes {
742    [0, 4, 8, 12].map(|shift| low8((token >> shift) & 15))
743}
744
745/// The opcodes that are declarations (and the phase markers of hull programs, which carry none): 88-106 and
746/// 143-162 of Direct3D 10 and 11, the hull phases 113-116 and `dcl_gs_instance_count` (206).
747fn is_declaration(opcode: u16) -> bool {
748    matches!(opcode, 88..=106 | 113..=116 | 143..=162 | 206)
749}
750
751/// Decode a complete token stream: version token, length, then declarations and instructions to its end.
752pub fn decode(words: &[u32]) -> Result<Decoded> {
753    let (Some(&version), Some(&length)) = (words.first(), words.get(1)) else {
754        return Err(error(0, "token stream shorter than its header"));
755    };
756    if usize::try_from(length).ok() != Some(words.len()) {
757        return Err(error(
758            1,
759            format!(
760                "the declared length is {length} words, the stream has {}",
761                words.len()
762            ),
763        ));
764    }
765    let kind = match version >> 16 {
766        0 => ProgramType::Pixel,
767        1 => ProgramType::Vertex,
768        2 => ProgramType::Geometry,
769        3 => ProgramType::Hull,
770        4 => ProgramType::Domain,
771        5 => ProgramType::Compute,
772        other => {
773            return Err(error(
774                0,
775                format!("program type {other} is not a Shader Model 4 or 5 stage"),
776            ));
777        }
778    };
779    let mut result = Decoded {
780        kind,
781        major: (version >> 4) & 15,
782        minor: version & 15,
783        declarations: Vec::new(),
784        instructions: Vec::new(),
785    };
786    let sm51 = (result.major, result.minor) >= (5, 1);
787    let mut at = 2;
788    while let Some(&t) = words.get(at) {
789        let opcode = low16(t & 0x7ff);
790        // customdata: its class in the opcode token's upper bits, its length (these two words included) the next word
791        if opcode == 53 {
792            let len = words
793                .get(at.saturating_add(1))
794                .and_then(|&n| usize::try_from(n).ok())
795                .unwrap_or(0);
796            let end = at.checked_add(len).filter(|&end| len >= 2 && end <= words.len());
797            let (Some(end), Some(body)) = (end, words.get(at.saturating_add(2)..at.saturating_add(len)))
798            else {
799                return Err(error(
800                    at,
801                    format!(
802                        "customdata of {len} words (at least 2) does not fit the stream's {} words",
803                        words.len()
804                    ),
805                ));
806            };
807            let class = t >> 11;
808            let kind = if class == 3 {
809                let (rows, rest) = body.as_chunks::<4>();
810                if !rest.is_empty() {
811                    return Err(error(
812                        at,
813                        format!(
814                            "an immediate constant buffer of {} words, expected whole rows of 4",
815                            body.len()
816                        ),
817                    ));
818                }
819                Declared::ImmediateBuffer(rows.to_vec())
820            } else {
821                Declared::CustomData { class }
822            };
823            result.declarations.push(Declaration {
824                word: at,
825                opcode,
826                control: class,
827                kind,
828            });
829            at = end;
830            continue;
831        }
832        let len = usize::try_from((t >> 24) & 127).unwrap_or(0);
833        if len == 0 {
834            return Err(error(at, "instruction length 0 (at least 1)"));
835        }
836        let end = at.checked_add(len).filter(|&end| end <= words.len());
837        let (Some(end), Some(body)) = (end, words.get(at.saturating_add(1)..at.saturating_add(len))) else {
838            return Err(error(
839                at,
840                format!(
841                    "instruction length {len} (at least 1) does not fit the stream's {} words",
842                    words.len()
843                ),
844            ));
845        };
846        let mut c = Cursor {
847            words: body,
848            at: 0,
849            origin: at.saturating_add(1),
850            kind,
851            sm51,
852            declaration: is_declaration(opcode),
853        };
854        let mut extensions = Vec::new();
855        let mut extended = t >> 31 != 0;
856        while extended {
857            let ext = c.take()?;
858            extensions.push(ext);
859            extended = ext >> 31 != 0;
860        }
861        let control = (t >> 11) & 0x1fff;
862        if is_declaration(opcode) {
863            let kind = declaration(opcode, control, &mut c)?;
864            if c.at != c.words.len() {
865                return Err(error(
866                    at,
867                    format!(
868                        "{} words after the operands of {}",
869                        c.words.len().saturating_sub(c.at),
870                        opcode_name(opcode)
871                    ),
872                ));
873            }
874            result.declarations.push(Declaration {
875                word: at,
876                opcode,
877                control,
878                kind,
879            });
880        } else {
881            let function = (opcode == INTERFACE_CALL).then(|| c.take()).transpose()?;
882            let format = operand_format(opcode);
883            let mut operands = Vec::new();
884            while c.at < c.words.len() && format.is_none_or(|f| operands.len() < f.operands()) {
885                operands.push(c.operand(0)?);
886            }
887            // fxc pads some instructions (`samplepos`, `sample_cl_s`) with zero words after their operands; words that
888            // read as further operands are an instruction with another operand count than its format's
889            if let Some(nonzero) = c
890                .words
891                .get(c.at..)
892                .unwrap_or_default()
893                .iter()
894                .position(|&w| w != 0)
895            {
896                let mut extra = c.clone();
897                let mut count = operands.len();
898                while extra.at < extra.words.len() && extra.operand(0).is_ok() {
899                    count = count.saturating_add(1);
900                }
901                if let Some(format) = format
902                    && extra.at == extra.words.len()
903                {
904                    return Err(error(
905                        at,
906                        format!(
907                            "{} has {count} operands, its format is {} destinations and {} sources",
908                            opcode_name(opcode),
909                            format.destinations.len(),
910                            format.sources.len()
911                        ),
912                    ));
913                }
914                return Err(error(
915                    c.position().saturating_add(nonzero),
916                    format!(
917                        "a nonzero word after the {} operands of {}",
918                        operands.len(),
919                        opcode_name(opcode)
920                    ),
921                ));
922            }
923            if let Some(format) = format
924                && operands.len() != format.operands()
925            {
926                return Err(error(
927                    at,
928                    format!(
929                        "{} has {} operands, its format is {} destinations and {} sources",
930                        opcode_name(opcode),
931                        operands.len(),
932                        format.destinations.len(),
933                        format.sources.len()
934                    ),
935                ));
936            }
937            result.instructions.push(Instruction {
938                word: at,
939                opcode,
940                control,
941                extensions,
942                saturate: t & 0x2000 != 0,
943                nonzero: t & 0x4_0000 != 0,
944                operands,
945                function,
946            });
947        }
948        at = end;
949    }
950    Ok(result)
951}
952
953fn declaration(opcode: u16, control: u32, c: &mut Cursor<'_>) -> Result<Declared> {
954    let pos = c.origin.saturating_sub(1);
955    let slot = |o: &Operand| -> Result<u32> {
956        o.indices
957            .first()
958            .map(|i| i.base)
959            .ok_or_else(|| error(pos, "missing declaration index"))
960    };
961    // A Shader Model 5.1 binding declaration's operand is `[id][lower][upper]`, and its last word the register space.
962    let range = |o: &Operand, c: &mut Cursor<'_>| -> Result<Option<RegisterRange>> {
963        if !c.sm51 {
964            return Ok(None);
965        }
966        let [_, lower, upper] = o.indices.as_slice() else {
967            return Err(error(
968                pos,
969                "a Shader Model 5.1 binding declaration without its three indices",
970            ));
971        };
972        Ok(Some(RegisterRange {
973            lower: lower.base,
974            upper: upper.base,
975            space: c.take()?,
976        }))
977    };
978    Ok(match opcode {
979        104 => Declared::Temps(c.take()?),
980        105 => Declared::IndexableTemp {
981            register: c.take()?,
982            size: c.take()?,
983            components: c.take()?,
984        },
985        106 => Declared::GlobalFlags(control),
986        88 => {
987            let o = c.operand(0)?;
988            let token = c.take()?;
989            Declared::Resource {
990                slot: slot(&o)?,
991                dimension: control & 31,
992                samples: (control >> 5) & 127,
993                returns: return_codes(token),
994                token,
995                range: range(&o, c)?,
996            }
997        }
998        89 => {
999            let o = c.operand(0)?;
1000            // 5.1 states the rows in a word of their own; before, they are the operand's second index
1001            let rows = if c.sm51 {
1002                c.take()?
1003            } else {
1004                o.indices.get(1).map_or(0, |i| i.base)
1005            };
1006            Declared::ConstantBuffer {
1007                slot: slot(&o)?,
1008                rows,
1009                dynamic: control & 1 != 0,
1010                dimensions: o.indices.len(),
1011                range: range(&o, c)?,
1012            }
1013        }
1014        90 => {
1015            let o = c.operand(0)?;
1016            Declared::Sampler {
1017                slot: slot(&o)?,
1018                mode: control & 15,
1019                range: range(&o, c)?,
1020            }
1021        }
1022        95..=100 => {
1023            let operand = c.operand(0)?;
1024            let pixel = matches!(opcode, 98..=100);
1025            let interpolation = pixel.then_some(control & 15);
1026            let name = matches!(opcode, 96 | 97 | 99 | 100)
1027                .then(|| c.take())
1028                .transpose()?;
1029            Declared::Input {
1030                operand,
1031                interpolation,
1032                name,
1033            }
1034        }
1035        101..=103 => {
1036            let operand = c.operand(0)?;
1037            let name = (opcode != 101).then(|| c.take()).transpose()?;
1038            Declared::Output { operand, name }
1039        }
1040        156 => {
1041            let o = c.operand(0)?;
1042            let token = c.take()?;
1043            Declared::UavTyped {
1044                slot: slot(&o)?,
1045                dimension: control & 31,
1046                returns: return_codes(token),
1047                token,
1048                control,
1049                range: range(&o, c)?,
1050            }
1051        }
1052        157 => {
1053            let o = c.operand(0)?;
1054            Declared::UavRaw {
1055                slot: slot(&o)?,
1056                control,
1057                range: range(&o, c)?,
1058            }
1059        }
1060        158 => {
1061            let o = c.operand(0)?;
1062            Declared::UavStructured {
1063                slot: slot(&o)?,
1064                stride: c.take()?,
1065                control,
1066                range: range(&o, c)?,
1067            }
1068        }
1069        155 => Declared::ThreadGroup([c.take()?, c.take()?, c.take()?]),
1070        91 => Declared::IndexRange {
1071            operand: c.operand(0)?,
1072            count: c.take()?,
1073        },
1074        143 => Declared::Stream(c.operand(0)?),
1075        159 => {
1076            let o = c.operand(0)?;
1077            Declared::GroupSharedRaw {
1078                register: slot(&o)?,
1079                bytes: c.take()?,
1080            }
1081        }
1082        160 => {
1083            let o = c.operand(0)?;
1084            Declared::GroupSharedStructured {
1085                register: slot(&o)?,
1086                stride: c.take()?,
1087                count: c.take()?,
1088            }
1089        }
1090        161 => {
1091            let o = c.operand(0)?;
1092            Declared::ResourceRaw {
1093                slot: slot(&o)?,
1094                range: range(&o, c)?,
1095            }
1096        }
1097        162 => {
1098            let o = c.operand(0)?;
1099            Declared::ResourceStructured {
1100                slot: slot(&o)?,
1101                stride: c.take()?,
1102                range: range(&o, c)?,
1103            }
1104        }
1105        144 => Declared::FunctionBody(c.take()?),
1106        145 => {
1107            let id = c.take()?;
1108            let count = c.take()?;
1109            let bodies = c.rest();
1110            if bodies.len() != count as usize {
1111                return Err(error(
1112                    pos.saturating_add(2),
1113                    format!(
1114                        "dcl_function_table word 2 counts {count} function bodies, the declaration holds {}",
1115                        bodies.len()
1116                    ),
1117                ));
1118            }
1119            Declared::FunctionTable { id, bodies }
1120        }
1121        146 => {
1122            let id = c.take()?;
1123            let sites = c.take()?;
1124            let lengths = c.take()?;
1125            let tables = c.rest();
1126            // the array length in the high half, the table count in the low
1127            let count = lengths & 0xffff;
1128            if tables.len() != count as usize {
1129                return Err(error(
1130                    pos.saturating_add(3),
1131                    format!(
1132                        "dcl_interface word 3 counts {count} function tables in its low 16 bits, the declaration \
1133                         holds {}",
1134                        tables.len()
1135                    ),
1136                ));
1137            }
1138            Declared::Interface {
1139                id,
1140                dynamic: control & 1 != 0,
1141                sites,
1142                length: lengths >> 16,
1143                tables,
1144            }
1145        }
1146        _ => Declared::Other {
1147            opcode,
1148            control,
1149            words: c.rest(),
1150        },
1151    })
1152}