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}