# spvkit ⚡

Grammar → SPIR-V, done at compile time.

`spvkit` ingests the official Khronos `spirv.core.grammar.json` during `cargo build` and emits every opcode, operand kind, and encoding impl for SPIR-V 1.6. No runtime JSON. No proc-macro. Just types.

---

## Generated API

Three layers, all derived from one JSON file:

**Enumerations**

| Grammar | Rust | Examples |
|---------|------|---------|
| `BitEnum` | `bitflags!` struct | `ImageOperands`, `MemoryAccess`, `FunctionControl` |
| `ValueEnum` | `enum` | `StorageClass`, `Decoration`, `BuiltIn`, `Capability` |
| — | `#[repr(u32)] enum Opcode` | ~300 variants, one per instruction |

**Instruction structs** (`ops::*`)

One typed struct per opcode. Fields fall straight out of the grammar:

| Operand | Field type |
|---------|-----------|
| `IdResult` | `result_id: Id` |
| `IdResultType` | `result_type: Id` |
| `?` optional | `Option<T>` |
| `*` variadic | `Vec<T>` |

Every struct carries `const OPCODE: Opcode` and `From<OpXxx> for Opcode`.

**Params structs**

`BitEnum` flags that carry parameters get a `*Params` companion bundling flags with their values — e.g. `MemoryAccessParams { flags: MemoryAccess, aligned: Option<u32> }`.

---

## Core traits

```rust
pub trait Spv {
    fn encode(&self, out: &mut Vec<u32>);
}

pub trait Instruction: Spv {
    fn opcode(&self)      -> Opcode;
    fn result_id(&self)   -> Option<Id>;
    fn result_type(&self) -> Option<Id>;
}
```

`Spv` covers `Id`, `u32`, `String` (word-aligned), `Option<T>`, `Vec<T>`, pair tuples, and everything generated. Calling `encode` on an instruction struct writes the complete SPIR-V word stream for that instruction — header word included (`opcode | word_count << 16`).

`Id` is a `#[repr(transparent)]` newtype over `u32` for SPIR-V SSA ids (`%N`).

---

## Usage

```rust
use spvkit::{Id, Spv};
use spvkit::ops::OpLoad;

let load = OpLoad {
    result_type:   Id(3),
    result_id:     Id(4),
    pointer:       Id(2),
    memory_access: None,
};

let mut words: Vec<u32> = Vec::new();
load.encode(&mut words);
// [0x0004003D, 3, 4, 2]
//   ↑ word_count=4, opcode=61 (OpLoad)
```

Build a full `.spv` module: SPIR-V header words + `encode` output for each instruction in order.

---

## 🔩 Architecture

```
build/
  mod.rs        — entry point; writes $OUT_DIR/generated.rs
  spec.rs       — spirv.core.grammar.json → Grammar / InstructionData / CategoryData
  generator.rs  — walks Grammar, emits TokenStream via proc-macro2 + quote
  spec/
    spirv.core.grammar.json   — Khronos SPIR-V 1.6 rev 4 (MIT)
src/
  lib.rs        — Spv / Instruction / Id + include!(generated.rs)
```

---

## Dependencies

Runtime: `bitflags`

Build: `serde`, `serde_json`, `proc-macro2`, `quote`, `heck`

---

## ✒️ Relationship to ink

`spvkit` is the SPIR-V emission layer of the `inkc` compiler. Ink IR lowers to `spvkit` instruction structs; the final `.spv` binary is assembled by serialising the word stream.

---

## License

MIT — Copyright 2024 Sol Midnight

`spirv.core.grammar.json` — Copyright 2014–2024 Khronos Group, MIT License.
