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}