The second half of encode(): the <id> side tables (Module.type_ids /
global_ids / function_ids -- SPIR-V's flat id space, which ir.Type/Global/
Function don't carry) plus the lowering:
emit_types ir.Type -> OpTypeXxx (void/int/float/vector/pointer/struct/
function; INT signedness + POINTER storage class ride in aux)
emit_constants OpConstant / OpConstantComposite / true/false/null
emit_globals OpVariable (storage class from the pointer type)
emit_operation generic table-driven op emit: INSTRUCTION_INDEX gives the
result-type/result-id prefix, the rest stream from op.operands
emit_functions OpFunction / OpLabel / body / OpFunctionEnd
Validated: a complete void compute main module encodes to byte-exact-correct
SPIR-V (29 words, all checked). Known gaps: OpFunctionParameter, ARRAY/bool
types, explicit enum-parameter operands, computed bound. Decoder next.
The Module -> word-stream encoder's first half: a fast single-pass word writer
(one host-endian store per word; instruction headers written as a placeholder
and backpatched, so variable-length instructions need no measure pass) plus the
header and the preamble / debug / annotation sections in spec layout order
(capabilities, extensions, ext-inst imports, memory model, entry points,
execution modes, OpString/OpSource/OpName, decorations).
Validated: a minimal module encodes to byte-exact-correct SPIR-V (magic, v1.5,
OpCapability Shader, OpMemoryModel Logical GLSL450, OpEntryPoint GLCompute %main,
NUL-terminated 'main' string, correct backpatched word counts).
Types/constants/globals/function bodies (the <id> + type lowering) are next.
Extend the tablegen to emit encoding_table.odin from the grammar -- the
per-opcode operand layout that drives the codec:
INSTRUCTION_SPECS flat []Operand_Spec {Spec_Kind, Quantifier}
INSTRUCTION_INDEX [max_opcode+1]Spec_Run -- O(1) opcode -> its operand run
ENUM_PARAMS enumerant -> trailing-parameter operands, so the codec
PARAM_SPECS reads MemoryAccess.Aligned / OpExecutionMode LocalSize /
Decoration.SpecId etc. correctly
873 opcodes, 3693 operand specs. Package compiles.
Start the SPIR-V intermediate representation under core/rexcode/ir/spirv,
following the ir API (docs/ir_design.md) and the ISA package conventions. SPIR-V
is table-driven on encoding and SSA on dataflow, so it re-exports the shared ir
vocabulary and is laid out like an arch package.
spirv.odin package + ir re-exports + physical format (MAGIC, version,
Header, wordCount<<16|opcode framing)
module.odin Module :: struct { using base: ir.Module, ...sections... } --
capabilities, ext-imports, entry points, exec modes, constant
pool, decorations, debug; SPIR-V's module metadata has no
ir.Module slot, so it is carried alongside the core
reloc.odin Relocation (linkage import/export)
opcodes.odin GENERATED: the Opcode enum (873 opcodes)
operand_kinds.odin GENERATED: ValueEnum/BitEnum operand kinds + Spec_Kind
tablegen/gen.odin the generator (core:encoding/json)
tablegen/spirv.core.grammar.json vendored authoritative grammar (Khronos
SPIRV-Headers unified1) -- single source of truth
Generating from the authoritative grammar avoids hand-transcription errors
(hand-authoring had Quads/Aliased/... wrong). Package compiles. Codec
(encode/decode + flat<->structured lowering), printer, and tests are next.
A sibling to core:rexcode/isa for the intermediate representations (WASM,
SPIR-V, LLVM bitcode + the LLVM dialects AIR/DXIL). Holds the shared
vocabulary every IR package builds on, implements no specific IR.
Design stance (see docs/ir_design.md): keep the ISA layer's spirit, but
where IRs are structurally MORE uniform than ISAs (SSA + a type system
regularize the operand/module shape), the shared core is richer. ir/ owns:
status.odin Error/Error_Code (shape-identical to isa.Error)
refs.odin Id/Ref/Ref_Space/Symbol_Table (the label analog: structural
id references, not PC-relative byte offsets)
types.odin Type/Type_Ref/Type_Kind (the type table -- no ISA analog)
module.odin Module/Function/Block/Operation/Operand/Result/Dataflow
(the structured model; Operation = isa.Instruction + an
optional typed Result, opcode a u16 like Mnemonic)
print.odin token kinds + options + num-fmt (parallels isa.print)
Three honest concessions vs the ISA API, made explicit not inert: a
structured Module replaces the flat []Instruction; a first-class type
system; id-based entity refs replace labels. The encode/decode verbs take
a Module and drop label_defs/resolve/base_address. Dataflow hosts both the
WASM value stack and SSA; the codec is pluggable (table for WASM/SPIR-V,
bitstream for the LLVM family -- AIR/DXIL are LLVM dialects, not peers).
Package compiles; a hand-built SSA module round-trips through the types.