mirror of
https://github.com/odin-lang/Odin.git
synced 2026-07-23 16:02:38 +00:00
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.
125 lines
4.8 KiB
Odin
125 lines
4.8 KiB
Odin
// rexcode · Brendan Punsky (dotbmp@github), original author
|
|
|
|
// rexcode/ir/spirv -- the SPIR-V intermediate representation.
|
|
//
|
|
// SPIR-V is the Khronos binary IR for shaders and compute kernels: a stream of
|
|
// 32-bit words, each instruction headed by a `wordCount<<16 | opcode` word, over
|
|
// an SSA value model with a first-class type system (OpTypeInt, OpConstant,
|
|
// OpFunction, ...). On the two axes that sort the IR family (docs/ir_design.md
|
|
// §2) it is **table-driven** on encoding -- a static `opcode -> operand-layout`
|
|
// grammar, exactly the ISA `ENCODING_TABLE` shape -- and **SSA** on dataflow,
|
|
// like LLVM and unlike WASM.
|
|
//
|
|
// So this package is built as a sibling of the ISA arch packages: it re-exports
|
|
// the shared `ir` vocabulary (Module / Function / Block / Operation / Operand /
|
|
// Type / Id / ...), owns a `u16` `Opcode` (the SPIR-V opcode values), a static
|
|
// operand-layout table that drives a word-level codec, the three Module-based
|
|
// verbs (`encode` / `decode` / `print`), a `Relocation` for linkage fixups, and
|
|
// the SPIR-V <-> `ir.Type` lowering. No PC-relative label pass exists; operands
|
|
// reference entities (results, types, functions) by `Id`, resolved structurally.
|
|
package rexcode_spirv
|
|
|
|
import "core:rexcode/ir"
|
|
|
|
// =============================================================================
|
|
// SECTION: Re-exported shared vocabulary (the IR naming contract, ir_design §4)
|
|
// =============================================================================
|
|
//
|
|
// A consumer sees one namespace -- `spirv.Operation`, `spirv.Type`, ... -- the
|
|
// way an arch package re-exports `isa`. These are the IR's leaf model; the
|
|
// concrete additions (Opcode, Relocation, the codec) live in the sibling files.
|
|
|
|
// `Module` is NOT re-exported -- SPIR-V's module carries dialect sections the
|
|
// shared core has no slot for (capabilities, entry points, decorations, ...), so
|
|
// it is a superset struct embedding `ir.Module`; see module.odin.
|
|
Function :: ir.Function
|
|
Block :: ir.Block
|
|
Global :: ir.Global
|
|
Operation :: ir.Operation
|
|
Operation_Flags :: ir.Operation_Flags
|
|
Operand :: ir.Operand
|
|
Operand_Kind :: ir.Operand_Kind
|
|
Result :: ir.Result
|
|
Type :: ir.Type
|
|
Type_Ref :: ir.Type_Ref
|
|
Type_Kind :: ir.Type_Kind
|
|
Id :: ir.Id
|
|
Ref :: ir.Ref
|
|
Ref_Space :: ir.Ref_Space
|
|
Symbol_Table :: ir.Symbol_Table
|
|
Dataflow :: ir.Dataflow
|
|
Error :: ir.Error
|
|
Error_Code :: ir.Error_Code
|
|
Token :: ir.Token
|
|
Token_Kind :: ir.Token_Kind
|
|
Print_Options :: ir.Print_Options
|
|
Print_Result :: ir.Print_Result
|
|
|
|
ID_NONE :: ir.ID_NONE
|
|
TYPE_NONE :: ir.TYPE_NONE
|
|
DEFAULT_PRINT_OPTIONS :: ir.DEFAULT_PRINT_OPTIONS
|
|
|
|
// Operand / type / ref constructors (shared; SSA makes them uniform).
|
|
op_int :: ir.op_int
|
|
op_float :: ir.op_float
|
|
op_type :: ir.op_type
|
|
op_ref :: ir.op_ref
|
|
op_value :: ir.op_value
|
|
op_block :: ir.op_block
|
|
operand_id :: ir.operand_id
|
|
operand_type :: ir.operand_type
|
|
ref :: ir.ref
|
|
|
|
type_void :: ir.type_void
|
|
type_int :: ir.type_int
|
|
type_float :: ir.type_float
|
|
type_vector :: ir.type_vector
|
|
type_pointer :: ir.type_pointer
|
|
|
|
// =============================================================================
|
|
// SECTION: Physical format (SPIR-V spec §2.3 -- the 32-bit word stream)
|
|
// =============================================================================
|
|
|
|
// SPIR-V is a stream of 32-bit words in the module's declared endianness.
|
|
Word :: u32
|
|
|
|
// The first word of every module.
|
|
MAGIC :: u32(0x0723_0203)
|
|
|
|
// Version word layout: 0x00 <major> <minor> 0x00.
|
|
@(require_results)
|
|
version :: #force_inline proc "contextless" (major, minor: u8) -> u32 {
|
|
return (u32(major) << 16) | (u32(minor) << 8)
|
|
}
|
|
|
|
VERSION_1_0 :: u32(0x0001_0000)
|
|
VERSION_1_1 :: u32(0x0001_0100)
|
|
VERSION_1_2 :: u32(0x0001_0200)
|
|
VERSION_1_3 :: u32(0x0001_0300)
|
|
VERSION_1_4 :: u32(0x0001_0400)
|
|
VERSION_1_5 :: u32(0x0001_0500)
|
|
VERSION_1_6 :: u32(0x0001_0600)
|
|
|
|
// Generator's magic number, registered in the SPIR-V Registry. 0 = unregistered.
|
|
GENERATOR :: u32(0)
|
|
|
|
// The 5-word module header that precedes the instruction stream.
|
|
Header :: struct {
|
|
magic: u32, // == MAGIC
|
|
version: u32, // see version()
|
|
generator: u32,
|
|
bound: u32, // exclusive upper bound on every <id> (i.e. max id + 1)
|
|
schema: u32, // reserved; 0
|
|
}
|
|
#assert(size_of(Header) == 20)
|
|
|
|
HEADER_WORDS :: 5
|
|
|
|
// An instruction's first word packs its total word count (including this word)
|
|
// in the high 16 bits and the opcode in the low 16.
|
|
@(require_results) inst_word_count :: #force_inline proc "contextless" (w: u32) -> u32 { return w >> 16 }
|
|
@(require_results) inst_opcode :: #force_inline proc "contextless" (w: u32) -> u16 { return u16(w & 0xFFFF) }
|
|
@(require_results) inst_head :: #force_inline proc "contextless" (word_count: u32, opcode: Opcode) -> u32 {
|
|
return (word_count << 16) | u32(u16(opcode))
|
|
}
|