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    },
381    /// `dcl_resource_<dimension> t<slot>`: the D3D resource dimension code, the sample count of a multisampled one,
382    /// and the four return types.
383    Resource {
384        /// The resource slot in Shader Model 5.0, its id in 5.1 (its registers in `range`).
385        slot: u32,
386        /// The D3D resource dimension code.
387        dimension: u32,
388        /// The sample count of a multisampled resource.
389        samples: u32,
390        /// The return type of each lane.
391        returns: ReturnCodes,
392        /// The return type token as stored.
393        token: u32,
394        /// The registers and space of a Shader Model 5.1 declaration; `None` before 5.1.
395        range: Option<RegisterRange>,
396    },
397    /// `dcl_resource_raw t<slot>`.
398    ResourceRaw {
399        /// The resource slot in Shader Model 5.0, its id in 5.1 (its registers in `range`).
400        slot: u32,
401        /// The registers and space of a Shader Model 5.1 declaration; `None` before 5.1.
402        range: Option<RegisterRange>,
403    },
404    /// `dcl_resource_structured t<slot>, stride`.
405    ResourceStructured {
406        /// The resource slot in Shader Model 5.0, its id in 5.1 (its registers in `range`).
407        slot: u32,
408        /// The element stride in bytes.
409        stride: u32,
410        /// The registers and space of a Shader Model 5.1 declaration; `None` before 5.1.
411        range: Option<RegisterRange>,
412    },
413    /// `dcl_uav_typed` and its control bits (dimension in bits 0 to 4, globally coherent bit 5).
414    UavTyped {
415        /// The UAV slot in Shader Model 5.0, its id in 5.1 (its registers in `range`).
416        slot: u32,
417        /// The D3D resource dimension code.
418        dimension: u32,
419        /// The return type of each lane.
420        returns: ReturnCodes,
421        /// The return type token as stored.
422        token: u32,
423        /// The opcode token's control bits.
424        control: u32,
425        /// The registers and space of a Shader Model 5.1 declaration; `None` before 5.1.
426        range: Option<RegisterRange>,
427    },
428    /// `dcl_uav_raw u<slot>`.
429    UavRaw {
430        /// The UAV slot in Shader Model 5.0, its id in 5.1 (its registers in `range`).
431        slot: u32,
432        /// The opcode token's control bits.
433        control: u32,
434        /// The registers and space of a Shader Model 5.1 declaration; `None` before 5.1.
435        range: Option<RegisterRange>,
436    },
437    /// `dcl_uav_structured u<slot>, stride`.
438    UavStructured {
439        /// The UAV slot in Shader Model 5.0, its id in 5.1 (its registers in `range`).
440        slot: u32,
441        /// The element stride in bytes.
442        stride: u32,
443        /// The opcode token's control bits.
444        control: u32,
445        /// The registers and space of a Shader Model 5.1 declaration; `None` before 5.1.
446        range: Option<RegisterRange>,
447    },
448    /// `dcl_thread_group x, y, z`.
449    ThreadGroup([u32; 3]),
450    /// `dcl_tgsm_raw g<register>, bytes`.
451    GroupSharedRaw {
452        /// The shared memory register index.
453        register: u32,
454        /// Its size in bytes.
455        bytes: u32,
456    },
457    /// `dcl_tgsm_structured g<register>, stride, count`.
458    GroupSharedStructured {
459        /// The shared memory register index.
460        register: u32,
461        /// The element stride in bytes.
462        stride: u32,
463        /// The number of elements.
464        count: u32,
465    },
466    /// An input register or special input: `dcl_input`, `_sgv`, `_siv`, `_ps`, `_ps_sgv`, `_ps_siv`. `interpolation` is
467    /// a pixel input's `D3D_INTERPOLATION_MODE`; `name` a system value's `D3D_NAME`.
468    Input {
469        /// The input register or special input the declaration names.
470        operand: Operand,
471        /// The interpolation mode of a pixel input.
472        interpolation: Option<u32>,
473        /// The system value (`D3D_NAME`) of a `_sgv` or `_siv` declaration.
474        name: Option<u32>,
475    },
476    /// An output register or special output: `dcl_output`, `_sgv`, `_siv`.
477    Output {
478        /// The output register or special output the declaration names.
479        operand: Operand,
480        /// The system value (`D3D_NAME`) of a `_siv` or `_sgv` declaration.
481        name: Option<u32>,
482    },
483    /// `dcl_indexrange`: registers from the operand's that a program indexes as one array, `count` of them.
484    IndexRange {
485        /// The first register of the range.
486        operand: Operand,
487        /// The number of registers.
488        count: u32,
489    },
490    /// `dcl_stream m<n>`: a geometry program's output stream that the declarations after it describe.
491    Stream(Operand),
492    /// `dcl_immediateConstantBuffer`.
493    ImmediateBuffer(Vec<[u32; 4]>),
494    /// A message the debug layer reports when the program runs (custom data of class 4,
495    /// `D3D11_SB_CUSTOMDATA_SHADER_MESSAGE`), which fxc writes for `printf` and `errorf`: the message and the operands
496    /// its format string formats.
497    ShaderMessage {
498        /// `D3D11_SB_SHADER_MESSAGE_ID`, the debug layer's message id: 0x200102 for `printf`, 0x200103 for `errorf`.
499        id: u32,
500        /// `D3D11_SB_SHADER_MESSAGE_FORMAT`: 0 text, 1 a `printf` format string.
501        format: u32,
502        /// The message's characters as stored, without the terminating zero.
503        text: Vec<u8>,
504        /// The operands the format string formats, in order.
505        operands: Vec<Operand>,
506    },
507    /// Custom data that is not an immediate constant buffer or a shader message (a comment, debug information, ...) by
508    /// its class.
509    CustomData {
510        /// The custom data class.
511        class: u32,
512    },
513    /// `dcl_function_body fb<id>`: a function body of a program with class linkage.
514    FunctionBody(u32),
515    /// `dcl_function_table ft<id> = {fb..}`: the function bodies a class's table holds, one per call site.
516    FunctionTable {
517        /// The table's id.
518        id: u32,
519        /// The function bodies, by call site.
520        bodies: Vec<u32>,
521    },
522    /// `dcl_interface fp<id>[array][sites] = {ft..}`: an interface pointer, or an array of them.
523    Interface {
524        /// The interface pointer's id, its first where it is an array.
525        id: u32,
526        /// The pointer is indexed by a register (`dcl_interface_dynamicindexed`).
527        dynamic: bool,
528        /// The number of call sites, the length of each of its tables.
529        sites: u32,
530        /// The array length, 1 for one pointer.
531        length: u32,
532        /// The function tables of the classes that can be bound to it.
533        tables: Vec<u32>,
534    },
535    /// A declaration this decoder does not type: stage-specific (geometry, tessellation, compute).
536    Other {
537        /// The opcode.
538        opcode: u16,
539        /// The opcode token's control bits.
540        control: u32,
541        /// The declaration's words after its operands, as stored.
542        words: Vec<u32>,
543    },
544}
545
546impl Declared {
547    /// The registers and space a resource, sampler, view or constant buffer declaration binds: its `range` in Shader
548    /// Model 5.1, its one register in space 0 before; `None` for the other declarations.
549    pub fn registers(&self) -> Option<RegisterRange> {
550        let (slot, range) = match self {
551            Declared::ConstantBuffer { slot, range, .. }
552            | Declared::Sampler { slot, range, .. }
553            | Declared::Resource { slot, range, .. }
554            | Declared::ResourceRaw { slot, range }
555            | Declared::ResourceStructured { slot, range, .. }
556            | Declared::UavTyped { slot, range, .. }
557            | Declared::UavRaw { slot, range, .. }
558            | Declared::UavStructured { slot, range, .. } => (*slot, *range),
559            _ => return None,
560        };
561        Some(range.unwrap_or(RegisterRange {
562            lower: slot,
563            upper: slot,
564            space: 0,
565        }))
566    }
567}
568
569/// The kind of program a version token names.
570#[derive(Clone, Copy, Debug, PartialEq, Eq)]
571pub enum ProgramType {
572    /// A pixel program (`ps`).
573    Pixel,
574    /// A vertex program (`vs`).
575    Vertex,
576    /// A geometry program (`gs`).
577    Geometry,
578    /// A hull program (`hs`).
579    Hull,
580    /// A domain program (`ds`).
581    Domain,
582    /// A compute program (`cs`).
583    Compute,
584}
585impl ProgramType {
586    /// The target's stage prefix (`ps`, `vs`, ...).
587    pub fn name(self) -> &'static str {
588        match self {
589            ProgramType::Pixel => "ps",
590            ProgramType::Vertex => "vs",
591            ProgramType::Geometry => "gs",
592            ProgramType::Hull => "hs",
593            ProgramType::Domain => "ds",
594            ProgramType::Compute => "cs",
595        }
596    }
597}
598
599/// A decoded token stream: its target, declarations and instructions, each in program order.
600#[derive(Clone, Debug)]
601pub struct Decoded {
602    /// The program's stage.
603    pub kind: ProgramType,
604    /// The shader model's major version.
605    pub major: u32,
606    /// The shader model's minor version.
607    pub minor: u32,
608    /// The declarations, in program order.
609    pub declarations: Vec<Declaration>,
610    /// The instructions, in program order.
611    pub instructions: Vec<Instruction>,
612}
613impl Decoded {
614    /// The target's name (`ps_5_0`).
615    pub fn target(&self) -> String {
616        format!("{}_{}_{}", self.kind.name(), self.major, self.minor)
617    }
618}
619
620#[derive(Clone)]
621struct Cursor<'a> {
622    words: &'a [u32],
623    at: usize,
624    origin: usize,
625    /// The program's type: the input registers of a geometry, hull or domain program are indexed by vertex.
626    kind: ProgramType,
627    /// The program is Shader Model 5.1, whose resource, sampler, view and constant buffer operands carry the
628    /// resource's id as their first index.
629    sm51: bool,
630    /// The words are a declaration's, whose 5.1 binding operands give the id and the first and last register.
631    declaration: bool,
632}
633impl Cursor<'_> {
634    /// The word the cursor is at, in the token stream.
635    fn position(&self) -> usize {
636        self.origin.saturating_add(self.at)
637    }
638    fn take(&mut self) -> Result<u32> {
639        let value = *self
640            .words
641            .get(self.at)
642            .ok_or_else(|| error(self.position(), "truncated instruction"))?;
643        self.at = self.at.saturating_add(1);
644        Ok(value)
645    }
646    fn rest(&mut self) -> Vec<u32> {
647        let rest = self.words.get(self.at..).unwrap_or_default().to_vec();
648        self.at = self.words.len();
649        rest
650    }
651    fn operand(&mut self, depth: usize) -> Result<Operand> {
652        let pos = self.position();
653        if depth > 2 {
654            return Err(error(pos, "nested relative operand exceeds bound"));
655        }
656        let t = self.take()?;
657        let components = t & 3;
658        let selection = match components {
659            0 => Selection::None,
660            1 => Selection::Scalar(0),
661            2 => match (t >> 2) & 3 {
662                0 => Selection::Mask(low8((t >> 4) & 15)),
663                1 => Selection::Swizzle([4, 6, 8, 10].map(|shift| low8((t >> shift) & 3))),
664                2 => Selection::Scalar(low8((t >> 4) & 3)),
665                _ => return Err(error(pos, "reserved component selection")),
666            },
667            _ => return Err(error(pos, "unsupported N-component operand")),
668        };
669        let kind = (t >> 12) & 255;
670        let register = Register::from_type(kind)
671            .ok_or_else(|| error(pos, format!("unsupported operand register type {kind}")))?;
672        let (mut modifier, mut precision, mut non_uniform) = (0, 0, false);
673        if t >> 31 != 0 {
674            let mut ext = self.take()?;
675            loop {
676                // only the modifier extension (kind 1) exists in Shader Model 4 and 5
677                if ext & 63 != 1 {
678                    return Err(error(pos, "unsupported extended operand (precision/modifier)"));
679                }
680                modifier = low8((ext >> 6) & 255);
681                precision = low8((ext >> 14) & 7);
682                non_uniform = ext & (1 << 17) != 0;
683                if ext >> 31 == 0 {
684                    break;
685                }
686                ext = self.take()?;
687            }
688            if modifier > 3 {
689                return Err(error(pos, "unsupported extended operand (precision/modifier)"));
690            }
691        }
692        let dimensions = (t >> 20) & 3;
693        let expected = match (register, self.kind) {
694            (Register::Input, ProgramType::Geometry | ProgramType::Hull | ProgramType::Domain) => 2,
695            // Shader Model 5.1: `CB1[id][register][row]`, `T0[id][register]`; declared `CB1[id][lower:upper]`
696            (Register::Constant, _) if self.sm51 => 3,
697            (Register::Texture | Register::Sampler | Register::Uav, _) if self.sm51 => {
698                if self.declaration && depth == 0 { 3 } else { 2 }
699            }
700            _ => register.dimensions(),
701        };
702        if dimensions != expected {
703            return Err(error(pos, "unexpected operand index dimensions"));
704        }
705        let mut indices = Vec::new();
706        // the representation of each index dimension, three bits each from bit 22
707        for shift in [22, 25, 28].into_iter().take(dimensions as usize) {
708            let representation = (t >> shift) & 7;
709            if !matches!(representation, 0 | 2 | 3) {
710                return Err(error(pos, "unsupported 64-bit/reserved index"));
711            }
712            let base = if representation != 2 { self.take()? } else { 0 };
713            let relative = if representation != 0 {
714                Some(Box::new(self.operand(depth.saturating_add(1))?))
715            } else {
716                None
717            };
718            indices.push(Index { base, relative });
719        }
720        let mut immediate = [0; 4];
721        match (register, components) {
722            // one word even for a 64-bit immediate, as Microsoft's disassembler reads it
723            (Register::Immediate | Register::Immediate64, 1) => immediate = [self.take()?; 4],
724            // four 32-bit values, or two 64-bit values of two words each
725            (Register::Immediate | Register::Immediate64, 2) => {
726                for x in &mut immediate {
727                    *x = self.take()?;
728                }
729            }
730            (Register::Immediate | Register::Immediate64, _) => {
731                return Err(error(pos, "immediate requires one or four components"));
732            }
733            _ => {}
734        }
735        Ok(Operand {
736            register,
737            selection,
738            components: match components {
739                0 => 0,
740                1 => 1,
741                _ => 4,
742            },
743            indices,
744            immediate,
745            modifier,
746            precision,
747            non_uniform,
748        })
749    }
750}
751
752/// `interface_call`, whose first word after its token is a call site, not an operand.
753const INTERFACE_CALL: u16 = 120;
754
755fn return_codes(token: u32) -> ReturnCodes {
756    [0, 4, 8, 12].map(|shift| low8((token >> shift) & 15))
757}
758
759/// The opcodes that are declarations (and the phase markers of hull programs, which carry none): 88-106 and
760/// 143-162 of Direct3D 10 and 11, the hull phases 113-116 and `dcl_gs_instance_count` (206).
761fn is_declaration(opcode: u16) -> bool {
762    matches!(opcode, 88..=106 | 113..=116 | 143..=162 | 206)
763}
764
765/// A shader message's custom data after its two header words, `origin` the word of the stream it starts at: the
766/// message id, the format, the number of characters, the number of operands and of their words, the operands, then the
767/// characters (a terminating zero and padding after them).
768fn shader_message(body: &[u32], origin: usize, kind: ProgramType, sm51: bool) -> Result<Declared> {
769    let [id, format, characters, count, operand_words, ..] = *body else {
770        return Err(error(
771            origin,
772            format!(
773                "a shader message of {} words, shorter than its 5-word header",
774                body.len()
775            ),
776        ));
777    };
778    let start = 5usize;
779    let end = usize::try_from(operand_words)
780        .ok()
781        .and_then(|n| start.checked_add(n));
782    let Some(words) = end.and_then(|end| body.get(start..end)) else {
783        return Err(error(
784            origin.saturating_add(4),
785            format!(
786                "a shader message's {operand_words} words of operands do not fit its {} words",
787                body.len()
788            ),
789        ));
790    };
791    let mut c = Cursor {
792        words,
793        at: 0,
794        origin: origin.saturating_add(start),
795        kind,
796        sm51,
797        declaration: false,
798    };
799    let mut operands = Vec::new();
800    while c.at < c.words.len() {
801        operands.push(c.operand(0)?);
802    }
803    if operands.len() != usize::try_from(count).unwrap_or(usize::MAX) {
804        return Err(error(
805            origin.saturating_add(3),
806            format!(
807                "a shader message of {count} operands whose {operand_words} words hold {}",
808                operands.len()
809            ),
810        ));
811    }
812    let bytes: Vec<u8> = body
813        .get(start.saturating_add(words.len())..)
814        .unwrap_or_default()
815        .iter()
816        .flat_map(|w| w.to_le_bytes())
817        .collect();
818    let Some(text) = usize::try_from(characters).ok().and_then(|n| bytes.get(..n)) else {
819        return Err(error(
820            origin.saturating_add(2),
821            format!(
822                "a shader message of {characters} characters, more than the {} bytes after its operands",
823                bytes.len()
824            ),
825        ));
826    };
827    Ok(Declared::ShaderMessage {
828        id,
829        format,
830        text: text.to_vec(),
831        operands,
832    })
833}
834
835/// Decode a complete token stream: version token, length, then declarations and instructions to its end.
836pub fn decode(words: &[u32]) -> Result<Decoded> {
837    let (Some(&version), Some(&length)) = (words.first(), words.get(1)) else {
838        return Err(error(0, "token stream shorter than its header"));
839    };
840    if usize::try_from(length).ok() != Some(words.len()) {
841        return Err(error(
842            1,
843            format!(
844                "the declared length is {length} words, the stream has {}",
845                words.len()
846            ),
847        ));
848    }
849    let kind = match version >> 16 {
850        0 => ProgramType::Pixel,
851        1 => ProgramType::Vertex,
852        2 => ProgramType::Geometry,
853        3 => ProgramType::Hull,
854        4 => ProgramType::Domain,
855        5 => ProgramType::Compute,
856        other => {
857            return Err(error(
858                0,
859                format!("program type {other} is not a Shader Model 4 or 5 stage"),
860            ));
861        }
862    };
863    let mut result = Decoded {
864        kind,
865        major: (version >> 4) & 15,
866        minor: version & 15,
867        declarations: Vec::new(),
868        instructions: Vec::new(),
869    };
870    let sm51 = (result.major, result.minor) >= (5, 1);
871    let mut at = 2;
872    while let Some(&t) = words.get(at) {
873        let opcode = low16(t & 0x7ff);
874        // customdata: its class in the opcode token's upper bits, its length (these two words included) the next word
875        if opcode == 53 {
876            let len = words
877                .get(at.saturating_add(1))
878                .and_then(|&n| usize::try_from(n).ok())
879                .unwrap_or(0);
880            let end = at.checked_add(len).filter(|&end| len >= 2 && end <= words.len());
881            let (Some(end), Some(body)) = (end, words.get(at.saturating_add(2)..at.saturating_add(len)))
882            else {
883                return Err(error(
884                    at,
885                    format!(
886                        "customdata of {len} words (at least 2) does not fit the stream's {} words",
887                        words.len()
888                    ),
889                ));
890            };
891            let class = t >> 11;
892            let kind = if class == 3 {
893                let (rows, rest) = body.as_chunks::<4>();
894                if !rest.is_empty() {
895                    return Err(error(
896                        at,
897                        format!(
898                            "an immediate constant buffer of {} words, expected whole rows of 4",
899                            body.len()
900                        ),
901                    ));
902                }
903                Declared::ImmediateBuffer(rows.to_vec())
904            } else if class == 4 {
905                shader_message(body, at.saturating_add(2), kind, sm51)?
906            } else {
907                Declared::CustomData { class }
908            };
909            result.declarations.push(Declaration {
910                word: at,
911                opcode,
912                control: class,
913                kind,
914            });
915            at = end;
916            continue;
917        }
918        let len = usize::try_from((t >> 24) & 127).unwrap_or(0);
919        if len == 0 {
920            return Err(error(at, "instruction length 0 (at least 1)"));
921        }
922        let end = at.checked_add(len).filter(|&end| end <= words.len());
923        let (Some(end), Some(body)) = (end, words.get(at.saturating_add(1)..at.saturating_add(len))) else {
924            return Err(error(
925                at,
926                format!(
927                    "instruction length {len} (at least 1) does not fit the stream's {} words",
928                    words.len()
929                ),
930            ));
931        };
932        let mut c = Cursor {
933            words: body,
934            at: 0,
935            origin: at.saturating_add(1),
936            kind,
937            sm51,
938            declaration: is_declaration(opcode),
939        };
940        let mut extensions = Vec::new();
941        let mut extended = t >> 31 != 0;
942        while extended {
943            let ext = c.take()?;
944            extensions.push(ext);
945            extended = ext >> 31 != 0;
946        }
947        let control = (t >> 11) & 0x1fff;
948        if is_declaration(opcode) {
949            let kind = declaration(opcode, control, &mut c)?;
950            if c.at != c.words.len() {
951                return Err(error(
952                    at,
953                    format!(
954                        "{} words after the operands of {}",
955                        c.words.len().saturating_sub(c.at),
956                        opcode_name(opcode)
957                    ),
958                ));
959            }
960            result.declarations.push(Declaration {
961                word: at,
962                opcode,
963                control,
964                kind,
965            });
966        } else {
967            let function = (opcode == INTERFACE_CALL).then(|| c.take()).transpose()?;
968            let format = operand_format(opcode);
969            let mut operands = Vec::new();
970            while c.at < c.words.len() && format.is_none_or(|f| operands.len() < f.operands()) {
971                operands.push(c.operand(0)?);
972            }
973            // fxc leaves words after the operands of some instructions, which Microsoft's disassembler skips: zeros after
974            // `samplepos` and `sample_cl_s`, 0x2000 after `sample_d_cl_s`. Words that read as whole further operands are
975            // instead an instruction with another operand count than its format's.
976            if let Some(format) = format
977                && c.words.get(c.at..).unwrap_or_default().iter().any(|&w| w != 0)
978            {
979                let mut extra = c.clone();
980                let mut count = operands.len();
981                while extra.at < extra.words.len() && extra.operand(0).is_ok() {
982                    count = count.saturating_add(1);
983                }
984                if count > operands.len() && extra.at == extra.words.len() {
985                    return Err(error(
986                        at,
987                        format!(
988                            "{} has {count} operands, its format is {} destinations and {} sources",
989                            opcode_name(opcode),
990                            format.destinations.len(),
991                            format.sources.len()
992                        ),
993                    ));
994                }
995            }
996            if let Some(format) = format
997                && operands.len() != format.operands()
998            {
999                return Err(error(
1000                    at,
1001                    format!(
1002                        "{} has {} operands, its format is {} destinations and {} sources",
1003                        opcode_name(opcode),
1004                        operands.len(),
1005                        format.destinations.len(),
1006                        format.sources.len()
1007                    ),
1008                ));
1009            }
1010            result.instructions.push(Instruction {
1011                word: at,
1012                opcode,
1013                control,
1014                extensions,
1015                saturate: t & 0x2000 != 0,
1016                nonzero: t & 0x4_0000 != 0,
1017                operands,
1018                function,
1019            });
1020        }
1021        at = end;
1022    }
1023    Ok(result)
1024}
1025
1026fn declaration(opcode: u16, control: u32, c: &mut Cursor<'_>) -> Result<Declared> {
1027    let pos = c.origin.saturating_sub(1);
1028    let slot = |o: &Operand| -> Result<u32> {
1029        o.indices
1030            .first()
1031            .map(|i| i.base)
1032            .ok_or_else(|| error(pos, "missing declaration index"))
1033    };
1034    // A Shader Model 5.1 binding declaration's operand is `[id][lower][upper]`, and its last word the register space.
1035    let range = |o: &Operand, c: &mut Cursor<'_>| -> Result<Option<RegisterRange>> {
1036        if !c.sm51 {
1037            return Ok(None);
1038        }
1039        let [_, lower, upper] = o.indices.as_slice() else {
1040            return Err(error(
1041                pos,
1042                "a Shader Model 5.1 binding declaration without its three indices",
1043            ));
1044        };
1045        Ok(Some(RegisterRange {
1046            lower: lower.base,
1047            upper: upper.base,
1048            space: c.take()?,
1049        }))
1050    };
1051    Ok(match opcode {
1052        104 => Declared::Temps(c.take()?),
1053        105 => Declared::IndexableTemp {
1054            register: c.take()?,
1055            size: c.take()?,
1056            components: c.take()?,
1057        },
1058        106 => Declared::GlobalFlags(control),
1059        88 => {
1060            let o = c.operand(0)?;
1061            let token = c.take()?;
1062            Declared::Resource {
1063                slot: slot(&o)?,
1064                dimension: control & 31,
1065                samples: (control >> 5) & 127,
1066                returns: return_codes(token),
1067                token,
1068                range: range(&o, c)?,
1069            }
1070        }
1071        89 => {
1072            let o = c.operand(0)?;
1073            // 5.1 states the rows in a word of their own; before, they are the operand's second index
1074            let rows = if c.sm51 {
1075                c.take()?
1076            } else {
1077                o.indices.get(1).map_or(0, |i| i.base)
1078            };
1079            Declared::ConstantBuffer {
1080                slot: slot(&o)?,
1081                rows,
1082                dynamic: control & 1 != 0,
1083                dimensions: o.indices.len(),
1084                range: range(&o, c)?,
1085            }
1086        }
1087        90 => {
1088            let o = c.operand(0)?;
1089            Declared::Sampler {
1090                slot: slot(&o)?,
1091                mode: control & 15,
1092                range: range(&o, c)?,
1093            }
1094        }
1095        95..=100 => {
1096            let operand = c.operand(0)?;
1097            let pixel = matches!(opcode, 98..=100);
1098            let interpolation = pixel.then_some(control & 15);
1099            let name = matches!(opcode, 96 | 97 | 99 | 100)
1100                .then(|| c.take())
1101                .transpose()?;
1102            Declared::Input {
1103                operand,
1104                interpolation,
1105                name,
1106            }
1107        }
1108        101..=103 => {
1109            let operand = c.operand(0)?;
1110            let name = (opcode != 101).then(|| c.take()).transpose()?;
1111            Declared::Output { operand, name }
1112        }
1113        156 => {
1114            let o = c.operand(0)?;
1115            let token = c.take()?;
1116            Declared::UavTyped {
1117                slot: slot(&o)?,
1118                dimension: control & 31,
1119                returns: return_codes(token),
1120                token,
1121                control,
1122                range: range(&o, c)?,
1123            }
1124        }
1125        157 => {
1126            let o = c.operand(0)?;
1127            Declared::UavRaw {
1128                slot: slot(&o)?,
1129                control,
1130                range: range(&o, c)?,
1131            }
1132        }
1133        158 => {
1134            let o = c.operand(0)?;
1135            Declared::UavStructured {
1136                slot: slot(&o)?,
1137                stride: c.take()?,
1138                control,
1139                range: range(&o, c)?,
1140            }
1141        }
1142        155 => Declared::ThreadGroup([c.take()?, c.take()?, c.take()?]),
1143        91 => Declared::IndexRange {
1144            operand: c.operand(0)?,
1145            count: c.take()?,
1146        },
1147        143 => Declared::Stream(c.operand(0)?),
1148        159 => {
1149            let o = c.operand(0)?;
1150            Declared::GroupSharedRaw {
1151                register: slot(&o)?,
1152                bytes: c.take()?,
1153            }
1154        }
1155        160 => {
1156            let o = c.operand(0)?;
1157            Declared::GroupSharedStructured {
1158                register: slot(&o)?,
1159                stride: c.take()?,
1160                count: c.take()?,
1161            }
1162        }
1163        161 => {
1164            let o = c.operand(0)?;
1165            Declared::ResourceRaw {
1166                slot: slot(&o)?,
1167                range: range(&o, c)?,
1168            }
1169        }
1170        162 => {
1171            let o = c.operand(0)?;
1172            Declared::ResourceStructured {
1173                slot: slot(&o)?,
1174                stride: c.take()?,
1175                range: range(&o, c)?,
1176            }
1177        }
1178        144 => Declared::FunctionBody(c.take()?),
1179        145 => {
1180            let id = c.take()?;
1181            let count = c.take()?;
1182            let bodies = c.rest();
1183            if bodies.len() != count as usize {
1184                return Err(error(
1185                    pos.saturating_add(2),
1186                    format!(
1187                        "dcl_function_table word 2 counts {count} function bodies, the declaration holds {}",
1188                        bodies.len()
1189                    ),
1190                ));
1191            }
1192            Declared::FunctionTable { id, bodies }
1193        }
1194        146 => {
1195            let id = c.take()?;
1196            let sites = c.take()?;
1197            let lengths = c.take()?;
1198            let tables = c.rest();
1199            // the array length in the high half, the table count in the low
1200            let count = lengths & 0xffff;
1201            if tables.len() != count as usize {
1202                return Err(error(
1203                    pos.saturating_add(3),
1204                    format!(
1205                        "dcl_interface word 3 counts {count} function tables in its low 16 bits, the declaration \
1206                         holds {}",
1207                        tables.len()
1208                    ),
1209                ));
1210            }
1211            Declared::Interface {
1212                id,
1213                dynamic: control & 1 != 0,
1214                sites,
1215                length: lengths >> 16,
1216                tables,
1217            }
1218        }
1219        _ => Declared::Other {
1220            opcode,
1221            control,
1222            words: c.rest(),
1223        },
1224    })
1225}