D093 - Operands are decoded, and the numbering lives in data

decided ยท 2026-08-19

orbistoun-shader::operand reads what an instruction operates on. data/operands.toml holds the unified operand numbering; data/encodings.toml gains per-family operand layouts.

This is the step between counting instructions and translating them. Knowing a word is VOP1:0x1 ranks it in a worklist; translating it needs to know it moves the constant zero into vector register 0. Register mapping, SPIR-V emission and control-flow reconstruction are all blocked on this and on nothing else.

A wrong boundary here is worse than a wrong instruction length, and that asymmetry drives the whole design. A wrong length desynchronises the decoder and everything after it becomes obvious nonsense - loud, and trivially spotted. A wrong operand boundary produces a plausible register where a constant belongs: code 128 is the integer zero, and read as a register index it is scalar register 128, which exists. A translator built on that emits a shader that compiles, runs, and draws the wrong thing, with nothing anywhere to investigate.

So every operand is checked against the reference disassembler - 99 of them across the fixture corpus - and the table refuses overlapping ranges, since two ranges claiming one code makes file order load-bearing without saying so.

A missing operand layout is distinguished from an instruction with no operands. Instruction::operands_decoded exists so an unfilled family cannot pass a check by producing an empty list.