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