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. A 64-bit immediate's
224    /// four words are two doubles, low word first; one of one component is a single word repeated, as Microsoft's
225    /// disassembler reads it (fxc writes none).
226    pub immediate: [u32; 4],
227    /// 0 none, 1 negate, 2 absolute, 3 negated absolute (float sources); 4 two's-complement negate (an integer
228    /// instruction's negate; the encoding has no such value, so a caller that knows the instruction reads integers sets it).
229    pub modifier: u8,
230    /// The minimum-precision annotation of the operand's extended token (0 none; 1 float16, 2 float2_8, 4 sint16,
231    /// 5 uint16).
232    pub precision: u8,
233    /// The operand's extended token marks it as a non-uniform resource index.
234    pub non_uniform: bool,
235}
236
237/// One index dimension of an operand: a constant, a relative register, or both added.
238#[derive(Clone, Debug)]
239pub struct Index {
240    /// The constant part of the index.
241    pub base: u32,
242    /// The register whose component is added to `base`, where the index is relative.
243    pub relative: Option<Box<Operand>>,
244}
245
246/// An instruction as stored.
247#[derive(Clone, Debug)]
248pub struct Instruction {
249    /// The instruction's first word in the token stream.
250    pub word: usize,
251    /// The opcode token's low 11 bits (see [`opcode_name`]).
252    pub opcode: u16,
253    /// The opcode token's bits 11 to 23: saturate and the opcode-specific controls (the test of a conditional, a
254    /// `resinfo` return type, the interpolation of a declaration).
255    pub control: u32,
256    /// The opcode's extended tokens as stored (sample offsets, resource dimension, return type).
257    pub extensions: Vec<u32>,
258    /// The saturate bit of the opcode token.
259    pub saturate: bool,
260    /// A conditional (`if`, `breakc`, `continuec`) taken when its operand is nonzero (`_nz`), else when zero (`_z`).
261    pub nonzero: bool,
262    /// The operands, destination first where the instruction has one.
263    pub operands: Vec<Operand>,
264    /// The call site an `interface_call` stores before its operand (the entry of the interface's function tables it
265    /// calls); `None` for other instructions.
266    pub function: Option<u32>,
267}
268impl Instruction {
269    /// The opcode's name (`mov`, `add`, ...), or `unknown opcode`.
270    pub fn name(&self) -> &'static str {
271        opcode_name(self.opcode)
272    }
273    /// The sample offsets of the extended token of kind 1 (`_aoffimmi`), if the instruction has one.
274    pub fn sample_offset(&self) -> Option<[i32; 3]> {
275        self.extension(1).map(|e| {
276            let field = |shift: u32| (((e >> shift) & 15) as i32) << 28 >> 28;
277            [field(9), field(13), field(17)]
278        })
279    }
280    /// The resource dimension and structured stride of the extended token of kind 2.
281    pub fn resource_dimension(&self) -> Option<(u32, u32)> {
282        self.extension(2).map(|e| ((e >> 6) & 31, (e >> 11) & 0xf_ffff))
283    }
284    /// The return-type token of the extended token of kind 3 (four nibbles).
285    pub fn return_type(&self) -> Option<u32> {
286        self.extension(3).map(|e| (e >> 6) & 0xffff)
287    }
288    fn extension(&self, kind: u32) -> Option<u32> {
289        self.extensions.iter().copied().find(|e| e & 63 == kind)
290    }
291    /// The opcode's operand format (see [`operand_format`]); `decode` has checked the operand count against it.
292    pub fn format(&self) -> Option<OperandFormat> {
293        operand_format(self.opcode)
294    }
295    /// The destination operands and the source operands, split as the opcode's format says; `None` for an opcode
296    /// with no format.
297    pub fn destinations_and_sources(&self) -> Option<(&[Operand], &[Operand])> {
298        self.operands.split_at_checked(self.format()?.destinations.len())
299    }
300}
301
302/// The name of an opcode in Microsoft's tokenized program format, or `unknown opcode`.
303pub fn opcode_name(opcode: u16) -> &'static str {
304    NAMES
305        .get(usize::from(opcode))
306        .copied()
307        .unwrap_or("unknown opcode")
308}
309
310/// The type of a program's four return lanes in a resource declaration, as D3D_RETURN_TYPE codes (1 unorm, 2 snorm,
311/// 3 sint, 4 uint, 5 float, 6 mixed, 7 double, 8 continued, 9 unused), one nibble per lane.
312pub type ReturnCodes = [u8; 4];
313
314/// The registers a Shader Model 5.1 declaration binds and their register space (`T1[4:7], space=1`). Shader Model 5.0
315/// declares each register on its own, in space 0; 5.1 names the resource by an id (the declaration's `slot`) and
316/// instructions by the id and a register of the range.
317#[derive(Clone, Copy, Debug, PartialEq, Eq)]
318pub struct RegisterRange {
319    /// The first register.
320    pub lower: u32,
321    /// The last register; `u32::MAX` for an array of no stated size (`T0[0:*]`).
322    pub upper: u32,
323    /// The register space.
324    pub space: u32,
325}
326
327/// A declaration, typed. `word` is where it starts.
328#[derive(Clone, Debug)]
329pub struct Declaration {
330    /// The word of the token stream the declaration starts at.
331    pub word: usize,
332    /// The opcode token's low 11 bits (see [`opcode_name`]).
333    pub opcode: u16,
334    /// The opcode token's bits 11 to 23 as stored.
335    pub control: u32,
336    /// The declaration, typed.
337    pub kind: Declared,
338}
339
340/// A declaration, typed where this reader knows its layout.
341#[derive(Clone, Debug)]
342#[non_exhaustive]
343pub enum Declared {
344    /// `dcl_temps`.
345    Temps(u32),
346    /// `dcl_indexable_temp x<register>[size], components`.
347    IndexableTemp {
348        /// The register index (the `<n>` of `x<n>`).
349        register: u32,
350        /// The number of elements.
351        size: u32,
352        /// The number of components per element.
353        components: u32,
354    },
355    /// `dcl_globalFlags`: the control bits as stored (bit 0 refactoring allowed, 1 double precision, 2 forced early
356    /// depth-stencil, 3 raw and structured buffers, 4 skip optimization, 5 min precision, 6 double extensions, 7
357    /// shader extensions).
358    GlobalFlags(u32),
359    /// `dcl_constantbuffer cb<slot>[rows]`; `dynamic` when it is dynamically indexed.
360    ConstantBuffer {
361        /// The buffer slot (the `<slot>` of `cb<slot>`) in Shader Model 5.0, its id in 5.1 (its registers in `range`).
362        slot: u32,
363        /// The number of 16-byte rows; 0 where the declaration states none.
364        rows: u32,
365        /// The buffer is dynamically indexed.
366        dynamic: bool,
367        /// The number of index dimensions the operand had (1 or 2; 3 in Shader Model 5.1).
368        dimensions: usize,
369        /// The registers and space of a Shader Model 5.1 declaration; `None` before 5.1.
370        range: Option<RegisterRange>,
371    },
372    /// `dcl_sampler s<slot>`, its mode: 0 default, 1 comparison, 2 mono.
373    Sampler {
374        /// The sampler slot in Shader Model 5.0, its id in 5.1 (its registers in `range`).
375        slot: u32,
376        /// 0 default, 1 comparison, 2 mono.
377        mode: u32,
378        /// The registers and space of a Shader Model 5.1 declaration; `None` before 5.1.
379        range: Option<RegisterRange>,
380        /// The operand as stored: the register it names, and a component selection (fxc writes none), which
381        /// Microsoft's listing writes after the register before Shader Model 5.1.
382        operand: Operand,
383    },
384    /// `dcl_resource_<dimension> t<slot>`: the D3D resource dimension code, the sample count of a multisampled one,
385    /// and the four return types.
386    Resource {
387        /// The resource slot in Shader Model 5.0, its id in 5.1 (its registers in `range`).
388        slot: u32,
389        /// The D3D resource dimension code.
390        dimension: u32,
391        /// The sample count of a multisampled resource.
392        samples: u32,
393        /// The return type of each lane.
394        returns: ReturnCodes,
395        /// The return type token as stored.
396        token: u32,
397        /// The registers and space of a Shader Model 5.1 declaration; `None` before 5.1.
398        range: Option<RegisterRange>,
399        /// The operand as stored: the register it names, and a component selection (fxc writes none), which
400        /// Microsoft's listing writes after the register before Shader Model 5.1.
401        operand: Operand,
402    },
403    /// `dcl_resource_raw t<slot>`.
404    ResourceRaw {
405        /// The resource slot in Shader Model 5.0, its id in 5.1 (its registers in `range`).
406        slot: u32,
407        /// The registers and space of a Shader Model 5.1 declaration; `None` before 5.1.
408        range: Option<RegisterRange>,
409        /// The operand as stored: the register it names, and a component selection (fxc writes none), which
410        /// Microsoft's listing writes after the register before Shader Model 5.1.
411        operand: Operand,
412    },
413    /// `dcl_resource_structured t<slot>, stride`.
414    ResourceStructured {
415        /// The resource slot in Shader Model 5.0, its id in 5.1 (its registers in `range`).
416        slot: u32,
417        /// The element stride in bytes.
418        stride: u32,
419        /// The registers and space of a Shader Model 5.1 declaration; `None` before 5.1.
420        range: Option<RegisterRange>,
421        /// The operand as stored: the register it names, and a component selection (fxc writes none), which
422        /// Microsoft's listing writes after the register before Shader Model 5.1.
423        operand: Operand,
424    },
425    /// `dcl_uav_typed` and its control bits (dimension in bits 0 to 4, globally coherent bit 5).
426    UavTyped {
427        /// The UAV slot in Shader Model 5.0, its id in 5.1 (its registers in `range`).
428        slot: u32,
429        /// The D3D resource dimension code.
430        dimension: u32,
431        /// The return type of each lane.
432        returns: ReturnCodes,
433        /// The return type token as stored.
434        token: u32,
435        /// The opcode token's control bits.
436        control: u32,
437        /// The registers and space of a Shader Model 5.1 declaration; `None` before 5.1.
438        range: Option<RegisterRange>,
439        /// The operand as stored: the register it names, and a component selection (fxc writes none), which
440        /// Microsoft's listing writes after the register before Shader Model 5.1.
441        operand: Operand,
442    },
443    /// `dcl_uav_raw u<slot>`.
444    UavRaw {
445        /// The UAV slot in Shader Model 5.0, its id in 5.1 (its registers in `range`).
446        slot: u32,
447        /// The opcode token's control bits.
448        control: u32,
449        /// The registers and space of a Shader Model 5.1 declaration; `None` before 5.1.
450        range: Option<RegisterRange>,
451        /// The operand as stored: the register it names, and a component selection (fxc writes none), which
452        /// Microsoft's listing writes after the register before Shader Model 5.1.
453        operand: Operand,
454    },
455    /// `dcl_uav_structured u<slot>, stride`.
456    UavStructured {
457        /// The UAV slot in Shader Model 5.0, its id in 5.1 (its registers in `range`).
458        slot: u32,
459        /// The element stride in bytes.
460        stride: u32,
461        /// The opcode token's control bits.
462        control: u32,
463        /// The registers and space of a Shader Model 5.1 declaration; `None` before 5.1.
464        range: Option<RegisterRange>,
465        /// The operand as stored: the register it names, and a component selection (fxc writes none), which
466        /// Microsoft's listing writes after the register before Shader Model 5.1.
467        operand: Operand,
468    },
469    /// `dcl_thread_group x, y, z`.
470    ThreadGroup([u32; 3]),
471    /// `dcl_tgsm_raw g<register>, bytes`.
472    GroupSharedRaw {
473        /// The shared memory register index.
474        register: u32,
475        /// Its size in bytes.
476        bytes: u32,
477        /// The operand as stored: the register it names, and a component selection (fxc writes none), which
478        /// Microsoft's listing writes after the register.
479        operand: Operand,
480    },
481    /// `dcl_tgsm_structured g<register>, stride, count`.
482    GroupSharedStructured {
483        /// The shared memory register index.
484        register: u32,
485        /// The element stride in bytes.
486        stride: u32,
487        /// The number of elements.
488        count: u32,
489        /// The operand as stored: the register it names, and a component selection (fxc writes none), which
490        /// Microsoft's listing writes after the register.
491        operand: Operand,
492    },
493    /// An input register or special input: `dcl_input`, `_sgv`, `_siv`, `_ps`, `_ps_sgv`, `_ps_siv`. `interpolation` is
494    /// a pixel input's `D3D_INTERPOLATION_MODE`; `name` a system value's `D3D_NAME`.
495    Input {
496        /// The input register or special input the declaration names.
497        operand: Operand,
498        /// The interpolation mode of a pixel input.
499        interpolation: Option<u32>,
500        /// The system value (`D3D_NAME`) of a `_sgv` or `_siv` declaration: the low 16 bits of its token, the rest
501        /// being reserved.
502        name: Option<u32>,
503    },
504    /// An output register or special output: `dcl_output`, `_sgv`, `_siv`.
505    Output {
506        /// The output register or special output the declaration names.
507        operand: Operand,
508        /// The system value (`D3D_NAME`) of a `_siv` or `_sgv` declaration: the low 16 bits of its token, the rest
509        /// being reserved.
510        name: Option<u32>,
511    },
512    /// `dcl_indexrange`: registers from the operand's that a program indexes as one array, `count` of them.
513    IndexRange {
514        /// The first register of the range.
515        operand: Operand,
516        /// The number of registers.
517        count: u32,
518    },
519    /// `dcl_stream m<n>`: a geometry program's output stream that the declarations after it describe.
520    Stream(Operand),
521    /// `dcl_immediateConstantBuffer`.
522    ImmediateBuffer(Vec<[u32; 4]>),
523    /// A message the debug layer reports when the program runs (custom data of class 4,
524    /// `D3D11_SB_CUSTOMDATA_SHADER_MESSAGE`), which fxc writes for `printf` and `errorf`: the message and the operands
525    /// its format string formats.
526    ShaderMessage {
527        /// `D3D11_SB_SHADER_MESSAGE_ID`, the debug layer's message id: 0x200102 for `printf`, 0x200103 for `errorf`.
528        id: u32,
529        /// `D3D11_SB_SHADER_MESSAGE_FORMAT`: 0 text, 1 a `printf` format string.
530        format: u32,
531        /// The number of characters the message states, without the terminating zero.
532        characters: u32,
533        /// The message's characters as Microsoft's listing reads them: from the string's start to its first zero
534        /// byte, whatever `characters` says.
535        text: Vec<u8>,
536        /// The operands the format string formats, in order.
537        operands: Vec<Operand>,
538    },
539    /// Custom data that is not an immediate constant buffer or a shader message (a comment, debug information, ...) by
540    /// its class; of class 4, a shader message whose character count leaves no room in it for its string's terminating
541    /// zero, which Microsoft's listing calls undecipherable.
542    CustomData {
543        /// The custom data class.
544        class: u32,
545    },
546    /// `dcl_function_body fb<id>`: a function body of a program with class linkage.
547    FunctionBody(u32),
548    /// `dcl_function_table ft<id> = {fb..}`: the function bodies a class's table holds, one per call site.
549    FunctionTable {
550        /// The table's id.
551        id: u32,
552        /// The function bodies, by call site.
553        bodies: Vec<u32>,
554    },
555    /// `dcl_interface fp<id>[array][sites] = {ft..}`: an interface pointer, or an array of them.
556    Interface {
557        /// The interface pointer's id, its first where it is an array.
558        id: u32,
559        /// The pointer is indexed by a register (`dcl_interface_dynamicindexed`).
560        dynamic: bool,
561        /// The number of call sites, the length of each of its tables.
562        sites: u32,
563        /// The array length, 1 for one pointer.
564        length: u32,
565        /// The function tables of the classes that can be bound to it.
566        tables: Vec<u32>,
567    },
568    /// A declaration this decoder does not type: stage-specific (geometry, tessellation, compute).
569    Other {
570        /// The opcode.
571        opcode: u16,
572        /// The opcode token's control bits.
573        control: u32,
574        /// The declaration's words after its operands, as stored.
575        words: Vec<u32>,
576    },
577}
578
579impl Declared {
580    /// The operand of a declaration that names a register: a resource, sampler, view, shared memory, input, output,
581    /// index range or stream; `None` for the others (a constant buffer's is read into its fields).
582    pub fn operand(&self) -> Option<&Operand> {
583        match self {
584            Declared::Sampler { operand, .. }
585            | Declared::Resource { operand, .. }
586            | Declared::ResourceRaw { operand, .. }
587            | Declared::ResourceStructured { operand, .. }
588            | Declared::UavTyped { operand, .. }
589            | Declared::UavRaw { operand, .. }
590            | Declared::UavStructured { operand, .. }
591            | Declared::GroupSharedRaw { operand, .. }
592            | Declared::GroupSharedStructured { operand, .. }
593            | Declared::Input { operand, .. }
594            | Declared::Output { operand, .. }
595            | Declared::IndexRange { operand, .. }
596            | Declared::Stream(operand) => Some(operand),
597            _ => None,
598        }
599    }
600
601    /// The registers and space a resource, sampler, view or constant buffer declaration binds: its `range` in Shader
602    /// Model 5.1, its one register in space 0 before; `None` for the other declarations.
603    pub fn registers(&self) -> Option<RegisterRange> {
604        let (slot, range) = match self {
605            Declared::ConstantBuffer { slot, range, .. }
606            | Declared::Sampler { slot, range, .. }
607            | Declared::Resource { slot, range, .. }
608            | Declared::ResourceRaw { slot, range, .. }
609            | Declared::ResourceStructured { slot, range, .. }
610            | Declared::UavTyped { slot, range, .. }
611            | Declared::UavRaw { slot, range, .. }
612            | Declared::UavStructured { slot, range, .. } => (*slot, *range),
613            _ => return None,
614        };
615        Some(range.unwrap_or(RegisterRange {
616            lower: slot,
617            upper: slot,
618            space: 0,
619        }))
620    }
621}
622
623/// The kind of program a version token names.
624#[derive(Clone, Copy, Debug, PartialEq, Eq)]
625pub enum ProgramType {
626    /// A pixel program (`ps`).
627    Pixel,
628    /// A vertex program (`vs`).
629    Vertex,
630    /// A geometry program (`gs`).
631    Geometry,
632    /// A hull program (`hs`).
633    Hull,
634    /// A domain program (`ds`).
635    Domain,
636    /// A compute program (`cs`).
637    Compute,
638}
639impl ProgramType {
640    /// The target's stage prefix (`ps`, `vs`, ...).
641    pub fn name(self) -> &'static str {
642        match self {
643            ProgramType::Pixel => "ps",
644            ProgramType::Vertex => "vs",
645            ProgramType::Geometry => "gs",
646            ProgramType::Hull => "hs",
647            ProgramType::Domain => "ds",
648            ProgramType::Compute => "cs",
649        }
650    }
651}
652
653/// A decoded token stream: its target, declarations and instructions, each in program order.
654#[derive(Clone, Debug)]
655pub struct Decoded {
656    /// The program's stage.
657    pub kind: ProgramType,
658    /// The shader model's major version.
659    pub major: u32,
660    /// The shader model's minor version.
661    pub minor: u32,
662    /// The declarations, in program order.
663    pub declarations: Vec<Declaration>,
664    /// The instructions, in program order.
665    pub instructions: Vec<Instruction>,
666}
667impl Decoded {
668    /// The target's name (`ps_5_0`).
669    pub fn target(&self) -> String {
670        format!("{}_{}_{}", self.kind.name(), self.major, self.minor)
671    }
672}
673
674#[derive(Clone)]
675struct Cursor<'a> {
676    words: &'a [u32],
677    at: usize,
678    origin: usize,
679    /// The program's type: the input registers of a geometry, hull or domain program are indexed by vertex.
680    kind: ProgramType,
681    /// The program is Shader Model 5.1, whose resource, sampler, view and constant buffer operands carry the
682    /// resource's id as their first index.
683    sm51: bool,
684    /// The words are a declaration's, whose 5.1 binding operands give the id and the first and last register.
685    declaration: bool,
686}
687impl Cursor<'_> {
688    /// The word the cursor is at, in the token stream.
689    fn position(&self) -> usize {
690        self.origin.saturating_add(self.at)
691    }
692    fn take(&mut self) -> Result<u32> {
693        let value = *self
694            .words
695            .get(self.at)
696            .ok_or_else(|| error(self.position(), "truncated instruction"))?;
697        self.at = self.at.saturating_add(1);
698        Ok(value)
699    }
700    fn rest(&mut self) -> Vec<u32> {
701        let rest = self.words.get(self.at..).unwrap_or_default().to_vec();
702        self.at = self.words.len();
703        rest
704    }
705    fn operand(&mut self, depth: usize) -> Result<Operand> {
706        let pos = self.position();
707        if depth > 2 {
708            return Err(error(pos, "nested relative operand exceeds bound"));
709        }
710        let t = self.take()?;
711        let components = t & 3;
712        let selection = match components {
713            0 => Selection::None,
714            1 => Selection::Scalar(0),
715            2 => match (t >> 2) & 3 {
716                0 => Selection::Mask(low8((t >> 4) & 15)),
717                1 => Selection::Swizzle([4, 6, 8, 10].map(|shift| low8((t >> shift) & 3))),
718                2 => Selection::Scalar(low8((t >> 4) & 3)),
719                _ => return Err(error(pos, "reserved component selection")),
720            },
721            _ => return Err(error(pos, "unsupported N-component operand")),
722        };
723        let kind = (t >> 12) & 255;
724        let register = Register::from_type(kind)
725            .ok_or_else(|| error(pos, format!("unsupported operand register type {kind}")))?;
726        let (mut modifier, mut precision, mut non_uniform) = (0, 0, false);
727        if t >> 31 != 0 {
728            let mut ext = self.take()?;
729            loop {
730                // only the modifier extension (kind 1) exists in Shader Model 4 and 5
731                if ext & 63 != 1 {
732                    return Err(error(pos, "unsupported extended operand (precision/modifier)"));
733                }
734                modifier = low8((ext >> 6) & 255);
735                precision = low8((ext >> 14) & 7);
736                non_uniform = ext & (1 << 17) != 0;
737                if ext >> 31 == 0 {
738                    break;
739                }
740                ext = self.take()?;
741            }
742            if modifier > 3 {
743                return Err(error(pos, "unsupported extended operand (precision/modifier)"));
744            }
745        }
746        let dimensions = (t >> 20) & 3;
747        let expected = match (register, self.kind) {
748            (Register::Input, ProgramType::Geometry | ProgramType::Hull | ProgramType::Domain) => 2,
749            // Shader Model 5.1: `CB1[id][register][row]`, `T0[id][register]`; declared `CB1[id][lower:upper]`
750            (Register::Constant, _) if self.sm51 => 3,
751            (Register::Texture | Register::Sampler | Register::Uav, _) if self.sm51 => {
752                if self.declaration && depth == 0 { 3 } else { 2 }
753            }
754            _ => register.dimensions(),
755        };
756        if dimensions != expected {
757            return Err(error(pos, "unexpected operand index dimensions"));
758        }
759        let mut indices = Vec::new();
760        // the representation of each index dimension, three bits each from bit 22
761        for shift in [22, 25, 28].into_iter().take(dimensions as usize) {
762            let representation = (t >> shift) & 7;
763            if !matches!(representation, 0 | 2 | 3) {
764                return Err(error(pos, "unsupported 64-bit/reserved index"));
765            }
766            let base = if representation != 2 { self.take()? } else { 0 };
767            let relative = if representation != 0 {
768                Some(Box::new(self.operand(depth.saturating_add(1))?))
769            } else {
770                None
771            };
772            indices.push(Index { base, relative });
773        }
774        let mut immediate = [0; 4];
775        match (register, components) {
776            // one word even for a 64-bit immediate, as Microsoft's disassembler reads it
777            (Register::Immediate | Register::Immediate64, 1) => immediate = [self.take()?; 4],
778            // four 32-bit values, or two 64-bit values of two words each
779            (Register::Immediate | Register::Immediate64, 2) => {
780                for x in &mut immediate {
781                    *x = self.take()?;
782                }
783            }
784            (Register::Immediate | Register::Immediate64, _) => {
785                return Err(error(pos, "immediate requires one or four components"));
786            }
787            _ => {}
788        }
789        Ok(Operand {
790            register,
791            selection,
792            components: match components {
793                0 => 0,
794                1 => 1,
795                _ => 4,
796            },
797            indices,
798            immediate,
799            modifier,
800            precision,
801            non_uniform,
802        })
803    }
804}
805
806/// `interface_call`, whose first word after its token is a call site, not an operand.
807const INTERFACE_CALL: u16 = 120;
808
809/// `D3D10_SB_NAME_MASK`: the bits of a declaration's name token that hold its `D3D_NAME`.
810const NAME: u32 = 0xffff;
811
812fn return_codes(token: u32) -> ReturnCodes {
813    [0, 4, 8, 12].map(|shift| low8((token >> shift) & 15))
814}
815
816/// The opcodes that are declarations (and the phase markers of hull programs, which carry none): 88-106 and
817/// 143-162 of Direct3D 10 and 11, the hull phases 113-116 and `dcl_gs_instance_count` (206).
818fn is_declaration(opcode: u16) -> bool {
819    matches!(opcode, 88..=106 | 113..=116 | 143..=162 | 206)
820}
821
822/// A shader message's custom data after its two header words, `origin` the word of the stream it starts at: the
823/// message id, the format, the number of characters, the number of operands and of their words, the operands, then the
824/// characters (a terminating zero and padding after them).
825fn shader_message(body: &[u32], origin: usize, kind: ProgramType, sm51: bool) -> Result<Declared> {
826    let [id, format, characters, count, operand_words, ..] = *body else {
827        return Err(error(
828            origin,
829            format!(
830                "a shader message of {} words, shorter than its 5-word header",
831                body.len()
832            ),
833        ));
834    };
835    let start = 5usize;
836    let end = usize::try_from(operand_words)
837        .ok()
838        .and_then(|n| start.checked_add(n));
839    let Some(words) = end.and_then(|end| body.get(start..end)) else {
840        return Err(error(
841            origin.saturating_add(4),
842            format!(
843                "a shader message's {operand_words} words of operands do not fit its {} words",
844                body.len()
845            ),
846        ));
847    };
848    // Microsoft deciphers a message whose count leaves room for the string's zero, before it reads the operands
849    let string = body.get(start.saturating_add(words.len())..).unwrap_or_default();
850    if usize::try_from(characters).map_or(true, |n| n >= string.len().saturating_mul(4)) {
851        return Ok(Declared::CustomData { class: 4 });
852    }
853    let mut c = Cursor {
854        words,
855        at: 0,
856        origin: origin.saturating_add(start),
857        kind,
858        sm51,
859        declaration: false,
860    };
861    let mut operands = Vec::new();
862    while c.at < c.words.len() {
863        operands.push(c.operand(0)?);
864    }
865    if operands.len() != usize::try_from(count).unwrap_or(usize::MAX) {
866        return Err(error(
867            origin.saturating_add(3),
868            format!(
869                "a shader message of {count} operands whose {operand_words} words hold {}",
870                operands.len()
871            ),
872        ));
873    }
874    // Microsoft reads the string to its zero, not by its count; past the custom data, what it reads is not the program's
875    let mut text: Vec<u8> = Vec::new();
876    for byte in string.iter().flat_map(|w| w.to_le_bytes()) {
877        if byte == 0 {
878            return Ok(Declared::ShaderMessage {
879                id,
880                format,
881                characters,
882                text,
883                operands,
884            });
885        }
886        text.push(byte);
887    }
888    Err(error(
889        origin.saturating_add(start).saturating_add(words.len()),
890        "a shader message's string has no terminating zero in its custom data",
891    ))
892}
893
894/// Decode a complete token stream: version token, length, then declarations and instructions to its end.
895pub fn decode(words: &[u32]) -> Result<Decoded> {
896    let (Some(&version), Some(&length)) = (words.first(), words.get(1)) else {
897        return Err(error(0, "token stream shorter than its header"));
898    };
899    if usize::try_from(length).ok() != Some(words.len()) {
900        return Err(error(
901            1,
902            format!(
903                "the declared length is {length} words, the stream has {}",
904                words.len()
905            ),
906        ));
907    }
908    let kind = match version >> 16 {
909        0 => ProgramType::Pixel,
910        1 => ProgramType::Vertex,
911        2 => ProgramType::Geometry,
912        3 => ProgramType::Hull,
913        4 => ProgramType::Domain,
914        5 => ProgramType::Compute,
915        other => {
916            return Err(error(
917                0,
918                format!("program type {other} is not a Shader Model 4 or 5 stage"),
919            ));
920        }
921    };
922    let mut result = Decoded {
923        kind,
924        major: (version >> 4) & 15,
925        minor: version & 15,
926        declarations: Vec::new(),
927        instructions: Vec::new(),
928    };
929    let sm51 = (result.major, result.minor) >= (5, 1);
930    let mut at = 2;
931    while let Some(&t) = words.get(at) {
932        let opcode = low16(t & 0x7ff);
933        // customdata: its class in the opcode token's upper bits, its length (these two words included) the next word
934        if opcode == 53 {
935            let len = words
936                .get(at.saturating_add(1))
937                .and_then(|&n| usize::try_from(n).ok())
938                .unwrap_or(0);
939            let end = at.checked_add(len).filter(|&end| len >= 2 && end <= words.len());
940            let (Some(end), Some(body)) = (end, words.get(at.saturating_add(2)..at.saturating_add(len)))
941            else {
942                return Err(error(
943                    at,
944                    format!(
945                        "customdata of {len} words (at least 2) does not fit the stream's {} words",
946                        words.len()
947                    ),
948                ));
949            };
950            let class = t >> 11;
951            let kind = if class == 3 {
952                let (rows, rest) = body.as_chunks::<4>();
953                if !rest.is_empty() {
954                    return Err(error(
955                        at,
956                        format!(
957                            "an immediate constant buffer of {} words, expected whole rows of 4",
958                            body.len()
959                        ),
960                    ));
961                }
962                Declared::ImmediateBuffer(rows.to_vec())
963            } else if class == 4 {
964                shader_message(body, at.saturating_add(2), kind, sm51)?
965            } else {
966                Declared::CustomData { class }
967            };
968            result.declarations.push(Declaration {
969                word: at,
970                opcode,
971                control: class,
972                kind,
973            });
974            at = end;
975            continue;
976        }
977        let len = usize::try_from((t >> 24) & 127).unwrap_or(0);
978        if len == 0 {
979            return Err(error(at, "instruction length 0 (at least 1)"));
980        }
981        let end = at.checked_add(len).filter(|&end| end <= words.len());
982        let (Some(end), Some(body)) = (end, words.get(at.saturating_add(1)..at.saturating_add(len))) else {
983            return Err(error(
984                at,
985                format!(
986                    "instruction length {len} (at least 1) does not fit the stream's {} words",
987                    words.len()
988                ),
989            ));
990        };
991        let mut c = Cursor {
992            words: body,
993            at: 0,
994            origin: at.saturating_add(1),
995            kind,
996            sm51,
997            declaration: is_declaration(opcode),
998        };
999        let mut extensions = Vec::new();
1000        let mut extended = t >> 31 != 0;
1001        while extended {
1002            let ext = c.take()?;
1003            extensions.push(ext);
1004            extended = ext >> 31 != 0;
1005        }
1006        let control = (t >> 11) & 0x1fff;
1007        if is_declaration(opcode) {
1008            let kind = declaration(opcode, control, &mut c)?;
1009            if c.at != c.words.len() {
1010                return Err(error(
1011                    at,
1012                    format!(
1013                        "{} words after the operands of {}",
1014                        c.words.len().saturating_sub(c.at),
1015                        opcode_name(opcode)
1016                    ),
1017                ));
1018            }
1019            result.declarations.push(Declaration {
1020                word: at,
1021                opcode,
1022                control,
1023                kind,
1024            });
1025        } else {
1026            let function = (opcode == INTERFACE_CALL).then(|| c.take()).transpose()?;
1027            let format = operand_format(opcode);
1028            let mut operands = Vec::new();
1029            while c.at < c.words.len() && format.is_none_or(|f| operands.len() < f.operands()) {
1030                operands.push(c.operand(0)?);
1031            }
1032            // fxc leaves words after the operands of some instructions, which Microsoft's disassembler skips: zeros after
1033            // `samplepos` and `sample_cl_s`, 0x2000 after `sample_d_cl_s`. Words that read as whole further operands are
1034            // instead an instruction with another operand count than its format's.
1035            if let Some(format) = format
1036                && c.words.get(c.at..).unwrap_or_default().iter().any(|&w| w != 0)
1037            {
1038                let mut extra = c.clone();
1039                let mut count = operands.len();
1040                while extra.at < extra.words.len() && extra.operand(0).is_ok() {
1041                    count = count.saturating_add(1);
1042                }
1043                if count > operands.len() && extra.at == extra.words.len() {
1044                    return Err(error(
1045                        at,
1046                        format!(
1047                            "{} has {count} operands, its format is {} destinations and {} sources",
1048                            opcode_name(opcode),
1049                            format.destinations.len(),
1050                            format.sources.len()
1051                        ),
1052                    ));
1053                }
1054            }
1055            if let Some(format) = format
1056                && operands.len() != format.operands()
1057            {
1058                return Err(error(
1059                    at,
1060                    format!(
1061                        "{} has {} operands, its format is {} destinations and {} sources",
1062                        opcode_name(opcode),
1063                        operands.len(),
1064                        format.destinations.len(),
1065                        format.sources.len()
1066                    ),
1067                ));
1068            }
1069            result.instructions.push(Instruction {
1070                word: at,
1071                opcode,
1072                control,
1073                extensions,
1074                saturate: t & 0x2000 != 0,
1075                nonzero: t & 0x4_0000 != 0,
1076                operands,
1077                function,
1078            });
1079        }
1080        at = end;
1081    }
1082    Ok(result)
1083}
1084
1085fn declaration(opcode: u16, control: u32, c: &mut Cursor<'_>) -> Result<Declared> {
1086    let pos = c.origin.saturating_sub(1);
1087    let slot = |o: &Operand| -> Result<u32> {
1088        o.indices
1089            .first()
1090            .map(|i| i.base)
1091            .ok_or_else(|| error(pos, "missing declaration index"))
1092    };
1093    // A Shader Model 5.1 binding declaration's operand is `[id][lower][upper]`, and its last word the register space.
1094    let range = |o: &Operand, c: &mut Cursor<'_>| -> Result<Option<RegisterRange>> {
1095        if !c.sm51 {
1096            return Ok(None);
1097        }
1098        let [_, lower, upper] = o.indices.as_slice() else {
1099            return Err(error(
1100                pos,
1101                "a Shader Model 5.1 binding declaration without its three indices",
1102            ));
1103        };
1104        Ok(Some(RegisterRange {
1105            lower: lower.base,
1106            upper: upper.base,
1107            space: c.take()?,
1108        }))
1109    };
1110    Ok(match opcode {
1111        104 => Declared::Temps(c.take()?),
1112        105 => Declared::IndexableTemp {
1113            register: c.take()?,
1114            size: c.take()?,
1115            components: c.take()?,
1116        },
1117        106 => Declared::GlobalFlags(control),
1118        88 => {
1119            let o = c.operand(0)?;
1120            let token = c.take()?;
1121            Declared::Resource {
1122                slot: slot(&o)?,
1123                dimension: control & 31,
1124                samples: (control >> 5) & 127,
1125                returns: return_codes(token),
1126                token,
1127                range: range(&o, c)?,
1128                operand: o,
1129            }
1130        }
1131        89 => {
1132            let o = c.operand(0)?;
1133            // 5.1 states the rows in a word of their own; before, they are the operand's second index
1134            let rows = if c.sm51 {
1135                c.take()?
1136            } else {
1137                o.indices.get(1).map_or(0, |i| i.base)
1138            };
1139            Declared::ConstantBuffer {
1140                slot: slot(&o)?,
1141                rows,
1142                dynamic: control & 1 != 0,
1143                dimensions: o.indices.len(),
1144                range: range(&o, c)?,
1145            }
1146        }
1147        90 => {
1148            let o = c.operand(0)?;
1149            Declared::Sampler {
1150                slot: slot(&o)?,
1151                mode: control & 15,
1152                range: range(&o, c)?,
1153                operand: o,
1154            }
1155        }
1156        95..=100 => {
1157            let operand = c.operand(0)?;
1158            let pixel = matches!(opcode, 98..=100);
1159            let interpolation = pixel.then_some(control & 15);
1160            let name = matches!(opcode, 96 | 97 | 99 | 100)
1161                .then(|| c.take().map(|t| t & NAME))
1162                .transpose()?;
1163            Declared::Input {
1164                operand,
1165                interpolation,
1166                name,
1167            }
1168        }
1169        101..=103 => {
1170            let operand = c.operand(0)?;
1171            let name = (opcode != 101).then(|| c.take().map(|t| t & NAME)).transpose()?;
1172            Declared::Output { operand, name }
1173        }
1174        156 => {
1175            let o = c.operand(0)?;
1176            let token = c.take()?;
1177            Declared::UavTyped {
1178                slot: slot(&o)?,
1179                dimension: control & 31,
1180                returns: return_codes(token),
1181                token,
1182                control,
1183                range: range(&o, c)?,
1184                operand: o,
1185            }
1186        }
1187        157 => {
1188            let o = c.operand(0)?;
1189            Declared::UavRaw {
1190                slot: slot(&o)?,
1191                control,
1192                range: range(&o, c)?,
1193                operand: o,
1194            }
1195        }
1196        158 => {
1197            let o = c.operand(0)?;
1198            Declared::UavStructured {
1199                slot: slot(&o)?,
1200                stride: c.take()?,
1201                control,
1202                range: range(&o, c)?,
1203                operand: o,
1204            }
1205        }
1206        155 => Declared::ThreadGroup([c.take()?, c.take()?, c.take()?]),
1207        91 => Declared::IndexRange {
1208            operand: c.operand(0)?,
1209            count: c.take()?,
1210        },
1211        143 => Declared::Stream(c.operand(0)?),
1212        159 => {
1213            let o = c.operand(0)?;
1214            Declared::GroupSharedRaw {
1215                register: slot(&o)?,
1216                bytes: c.take()?,
1217                operand: o,
1218            }
1219        }
1220        160 => {
1221            let o = c.operand(0)?;
1222            Declared::GroupSharedStructured {
1223                register: slot(&o)?,
1224                stride: c.take()?,
1225                count: c.take()?,
1226                operand: o,
1227            }
1228        }
1229        161 => {
1230            let o = c.operand(0)?;
1231            Declared::ResourceRaw {
1232                slot: slot(&o)?,
1233                range: range(&o, c)?,
1234                operand: o,
1235            }
1236        }
1237        162 => {
1238            let o = c.operand(0)?;
1239            Declared::ResourceStructured {
1240                slot: slot(&o)?,
1241                stride: c.take()?,
1242                range: range(&o, c)?,
1243                operand: o,
1244            }
1245        }
1246        144 => Declared::FunctionBody(c.take()?),
1247        145 => {
1248            let id = c.take()?;
1249            let count = c.take()?;
1250            let bodies = c.rest();
1251            if bodies.len() != count as usize {
1252                return Err(error(
1253                    pos.saturating_add(2),
1254                    format!(
1255                        "dcl_function_table word 2 counts {count} function bodies, the declaration holds {}",
1256                        bodies.len()
1257                    ),
1258                ));
1259            }
1260            Declared::FunctionTable { id, bodies }
1261        }
1262        146 => {
1263            let id = c.take()?;
1264            let sites = c.take()?;
1265            let lengths = c.take()?;
1266            let tables = c.rest();
1267            // the array length in the high half, the table count in the low
1268            let count = lengths & 0xffff;
1269            if tables.len() != count as usize {
1270                return Err(error(
1271                    pos.saturating_add(3),
1272                    format!(
1273                        "dcl_interface word 3 counts {count} function tables in its low 16 bits, the declaration \
1274                         holds {}",
1275                        tables.len()
1276                    ),
1277                ));
1278            }
1279            Declared::Interface {
1280                id,
1281                dynamic: control & 1 != 0,
1282                sites,
1283                length: lengths >> 16,
1284                tables,
1285            }
1286        }
1287        _ => Declared::Other {
1288            opcode,
1289            control,
1290            words: c.rest(),
1291        },
1292    })
1293}