Commit Graph

7 Commits

Author SHA1 Message Date
Brendan Punsky
6665d5b552 rexcode/ir/spirv: generate the typed builders (both layers) from the grammar
Extend tablegen to emit builders_gen.odin -- 723 opcodes get a low-level
inst_<OpName>(buf, ...) constructor and a high-level Builder method, mapping each
grammar operand to a typed param (IdResultType->Type_Ref, IdResult->auto-allocated
Id, IdRef->Id, LiteralInteger->i64, ValueEnum/BitEnum->the typed enum, trailing
IdRef* -> []Id). builder.odin keeps just the hand-written Builder infrastructure.

Skipped (stay hand-writable, like an ISA's can_generate_builder): operands that
aren't simple typed params yet (optional, Pair* composites, LiteralString),
type-declaration opcodes (those are ir.Type), and verbs colliding with Odin
keywords/builtins (return_, switch_, size_of, ...).

Validated: a function body built via i_add / return_ encodes + round-trips
byte-exact (builder_made test) -> 10 passed.
2026-06-26 13:32:07 -04:00
Brendan Punsky
c2a5091336 rexcode/ir/spirv: round-trip opaque types (image/sampler/event/...) verbatim
Type-defining instructions the codec doesn't model structurally -- OpTypeImage,
OpTypeSampler, OpTypeSampledImage, OpTypeMatrix, OpTypeOpaque, OpTypeEvent,
OpTypeDeviceEvent, OpTypeReserveId, OpTypeQueue, OpTypePipe -- decode to
Type{.OPAQUE} plus an Opaque_Info{opcode, words} side entry (the operand words
after the result <id>) and re-emit verbatim. One generic fallback covers every
opaque SPIR-V type byte-exact, with no per-type fields.

Test: image_type (OpTypeImage, 7 operands) round-trips -> 9 passed.
2026-06-26 12:11:25 -04:00
Brendan Punsky
0cd5ed84b2 rexcode/ir/spirv: dependency-ordered definitions + runtime arrays
- Definition order: Module.defs records the exact type/constant/global
  interleaving (SPIR-V's single 'types, constants, global variables' section must
  be dependency-ordered -- a length constant before the array type that uses it,
  etc.). decode records it; encode replays it for byte-exact, spec-valid output
  (empty defs falls back to all-types, then -constants, then -globals).
  emit_one_type/constant/global factored out for the replay; no encoder alloc.
- OpTypeRuntimeArray: ARRAY with len_ref == ID_NONE (vs OpTypeArray's <id> length).

Tests: bool_and_array sets defs so OpConstant precedes OpTypeArray (verified in
disasm); runtime_array added -> 7 passed.
2026-06-26 12:04:27 -04:00
Brendan Punsky
fe27dcdbd4 rexcode/ir: add Type_Kind.BOOL + array <id> length; SPIR-V bool/array round-trip
Extend the shared type model so SPIR-V's OpTypeBool and OpTypeArray (whose length
is a constant <id>, not a literal) lower cleanly:
  - ir.Type_Kind gains BOOL (a distinct boolean; LLVM i1 will use it too).
  - ir.Type gains len_ref: Id -- an ARRAY length carried as a constant <id>
    (alongside the existing literal count for dialects with literal lengths).
  - type_bool / type_array constructors.

SPIR-V codec: OpTypeBool <-> Type{.BOOL}; OpTypeArray <-> Type{.ARRAY, elem,
len_ref}. Test bool_and_array round-trips byte-exact -> 6 passed.

NOTE: a spec-valid module orders an array's length constant before the array
type; the codec round-trips the shape byte-exact regardless, but emitting the
types/constants section in dependency order is a follow-up.
2026-06-26 11:45:35 -04:00
Brendan Punsky
7a8dc34d5f rexcode/ir/spirv: confirm big-endian decode (round-trip test)
The decoder already detects endianness from the magic word and byte-swaps each
word; strings are read from word values, so both endiannesses decode. Lock it in:
the round-trip helper now also byte-swaps every word to big-endian, decodes, and
asserts the re-encoding reproduces the original little-endian bytes -- across all
5 cases.
2026-06-26 11:23:38 -04:00
Brendan Punsky
35fac68c7c rexcode/ir/spirv: codec completeness -- function params, computed bound, enum-param operands
- OpFunctionParameter: the entry block's Block.params round-trip as
  OpFunctionParameter (emitted between OpFunction and the entry OpLabel, decoded
  back onto the entry block).
- encode computes bound (max <id> + 1) when the caller leaves it 0; a non-zero
  bound (e.g. from decode) is preserved, so re-encode is stable.
- Trailing operands an enum value/bit pulls in (MemoryAccess Aligned's alignment,
  ...) are captured on decode as literals, so enum-parameter instructions
  re-encode byte-exact.

Tests: param_function + load_aligned added -> 5 passed.
2026-06-26 11:14:12 -04:00
Brendan Punsky
ded7a2dea0 rexcode/ir/spirv: codec round-trip test suite
A tests/ package (the ISA test convention) that, per module shape, does
encode -> decode -> re-encode and asserts byte-identical output -- exercising the
encoder, decoder, <id> side tables, and the generic operation codec together:

  void_main      header + preamble + a void function (116 B)
  int_constants  i32 type + two scalar OpConstants incl. 0xDEADBEEF (88 B)
  iadd_function  %r = OpIAdd %i32 %a %b ; OpReturn -- generic operand codec
                 with a result + value operands (164 B)

3 passed. Run: odin run core/rexcode/ir/spirv/tests. (build.lua is ISA-centric;
wiring the ir/ packages into the gen/check/test pipeline is a follow-up.)
2026-06-26 09:43:59 -04:00