mirror of
https://github.com/odin-lang/Odin.git
synced 2026-07-17 13:11:07 +00:00
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.
This commit is contained in:
@@ -50,6 +50,7 @@ Decoder :: struct {
|
||||
functions: [dynamic]Function,
|
||||
function_ids: [dynamic]Id,
|
||||
defs: [dynamic]Def, // type/constant/global definition order
|
||||
opaque_info: [dynamic]Opaque_Info, // parallel to types; set for OPAQUE
|
||||
|
||||
// in-flight function / block
|
||||
in_fn: bool,
|
||||
@@ -111,6 +112,17 @@ add_type :: proc(d: ^Decoder, id: Id, t: Type) {
|
||||
append(&d.defs, Def{.TYPE, u32(len(d.types))})
|
||||
append(&d.types, t)
|
||||
append(&d.type_ids, id)
|
||||
append(&d.opaque_info, Opaque_Info{}) // stay parallel; filled for OPAQUE
|
||||
}
|
||||
|
||||
// A type-defining instruction the codec doesn't model: store it as OPAQUE plus
|
||||
// its verbatim operand words (after the result <id>) so it re-emits exactly.
|
||||
@(private="file")
|
||||
add_opaque :: proc(d: ^Decoder, opcode: Opcode, w: []u32) {
|
||||
add_type(d, Id(w[0]), Type{kind = .OPAQUE})
|
||||
words := make([]u32, len(w) - 1)
|
||||
copy(words, w[1:])
|
||||
d.opaque_info[len(d.opaque_info) - 1] = Opaque_Info{opcode = opcode, words = words}
|
||||
}
|
||||
|
||||
@(private="file")
|
||||
@@ -238,6 +250,10 @@ lower :: proc(d: ^Decoder, opcode: Opcode, w: []u32) {
|
||||
for j in 0 ..< nparam { fields[j] = tref(d, w[2 + j]) }
|
||||
fields[nparam] = tref(d, w[1]) // return type last: fields = params ++ [result]
|
||||
add_type(d, Id(w[0]), Type{kind = .FUNCTION, fields = fields, count = u32(nparam)})
|
||||
case .OpTypeMatrix, .OpTypeImage, .OpTypeSampler, .OpTypeSampledImage,
|
||||
.OpTypeOpaque, .OpTypeEvent, .OpTypeDeviceEvent, .OpTypeReserveId,
|
||||
.OpTypeQueue, .OpTypePipe:
|
||||
add_opaque(d, opcode, w) // captured verbatim -> OPAQUE
|
||||
|
||||
case .OpConstant:
|
||||
c := Constant{result = {Id(w[1]), tref(d, w[0])}, opcode = opcode, value = u64(w[2])}
|
||||
@@ -348,6 +364,7 @@ decode :: proc(data: []u8, m: ^Module, errors: ^[dynamic]Error, allocator := con
|
||||
m.functions = d.functions[:]
|
||||
m.function_ids = d.function_ids[:]
|
||||
m.defs = d.defs[:]
|
||||
m.opaque_info = d.opaque_info[:]
|
||||
|
||||
return u32(nwords * 4), true
|
||||
}
|
||||
|
||||
@@ -194,7 +194,16 @@ emit_one_type :: proc "contextless" (w: ^Writer, m: ^Module, i: int) {
|
||||
w_id(w, tid(m, t.fields[t.count])) // return type
|
||||
for pi in 0 ..< int(t.count) { w_id(w, tid(m, t.fields[pi])) }
|
||||
op = .OpTypeFunction
|
||||
case .OPAQUE, .REF:
|
||||
case .OPAQUE:
|
||||
// re-emit the captured instruction verbatim (image/sampler/event/...).
|
||||
info := i < len(m.opaque_info) ? m.opaque_info[i] : Opaque_Info{}
|
||||
if info.opcode == .OpNop {
|
||||
w.pos = s // no recorded detail; cannot lower
|
||||
return
|
||||
}
|
||||
for word in info.words { w_word(w, word) }
|
||||
op = info.opcode
|
||||
case .REF:
|
||||
w.pos = s // rewind the placeholder; not yet lowered
|
||||
return
|
||||
}
|
||||
|
||||
@@ -63,6 +63,14 @@ Module :: struct {
|
||||
// encode replays it for byte-exact, spec-valid output. Empty => encode falls
|
||||
// back to all-types, then all-constants, then all-globals.
|
||||
defs: []Def,
|
||||
|
||||
// --- Opaque type detail ---
|
||||
// Verbatim operand words for type-defining instructions the codec does not
|
||||
// model structurally (OpTypeImage / Sampler / SampledImage / Matrix / Event /
|
||||
// Pipe / ...). Parallel to base.types; the entry is meaningful only where
|
||||
// types[i].kind == OPAQUE. Lets any such type round-trip byte-exact without a
|
||||
// dedicated field per SPIR-V opaque kind.
|
||||
opaque_info: []Opaque_Info,
|
||||
}
|
||||
|
||||
// A node in Module.defs: which of the three definition arrays, and its index.
|
||||
@@ -72,6 +80,13 @@ Def :: struct {
|
||||
index: u32,
|
||||
}
|
||||
|
||||
// An unmodeled type-defining instruction, captured verbatim (the operand words
|
||||
// after its result <id>) so it re-emits exactly.
|
||||
Opaque_Info :: struct {
|
||||
opcode: Opcode,
|
||||
words: []u32,
|
||||
}
|
||||
|
||||
// Member index sentinel: a whole-target decoration / name (OpDecorate / OpName)
|
||||
// rather than a struct member one (OpMemberDecorate / OpMemberName).
|
||||
MEMBER_NONE :: u32(0xFFFF_FFFF)
|
||||
|
||||
@@ -235,6 +235,45 @@ main :: proc() {
|
||||
roundtrip("runtime_array", m)
|
||||
}
|
||||
|
||||
// (8) OpPhi -- SPIR-V's SSA merge (variadic value/parent-block pairs). The
|
||||
// generic codec round-trips it with no special handling; non-entry blocks
|
||||
// use OpPhi, not block params, so this is the whole story.
|
||||
{
|
||||
m := spirv.make_module()
|
||||
m.capabilities = {.Shader}
|
||||
m.types = {
|
||||
{kind = .VOID}, {kind = .INT, bits = 32, aux = 1},
|
||||
{kind = .FUNCTION, fields = {spirv.Type_Ref(0)}, count = 0},
|
||||
}
|
||||
m.type_ids = {spirv.Id(1), spirv.Id(2), spirv.Id(3)}
|
||||
phi := spirv.Operation{ // %6 = OpPhi %int %7 %8 (value %7 from block %8)
|
||||
opcode = u16(spirv.Opcode.OpPhi), result = {spirv.Id(6), spirv.Type_Ref(1)},
|
||||
operands = {spirv.op_value(spirv.Id(7)), spirv.op_value(spirv.Id(8))},
|
||||
}
|
||||
m.functions = {
|
||||
{signature = spirv.Type_Ref(2), blocks = {
|
||||
{id = spirv.Id(5), ops = {phi, {opcode = u16(spirv.Opcode.OpReturn)}}},
|
||||
}},
|
||||
}
|
||||
m.function_ids = {spirv.Id(4)}
|
||||
roundtrip("phi", m)
|
||||
}
|
||||
|
||||
// (9) an opaque type captured verbatim: OpTypeImage (sampled type + 6 enum/
|
||||
// literal operands) round-trips through the Opaque_Info side table, with no
|
||||
// per-image-field modelling.
|
||||
{
|
||||
m := spirv.make_module()
|
||||
m.capabilities = {.Shader}
|
||||
m.types = {{kind = .FLOAT, bits = 32}, {kind = .OPAQUE}}
|
||||
m.type_ids = {spirv.Id(1), spirv.Id(2)}
|
||||
m.opaque_info = {
|
||||
{},
|
||||
{opcode = .OpTypeImage, words = {1, 1, 0, 0, 0, 1, 0}}, // %1, Dim 2D, ..., Unknown
|
||||
}
|
||||
roundtrip("image_type", m)
|
||||
}
|
||||
|
||||
fmt.printf("\n%d passed, %d failed\n", ok_count, fail_count)
|
||||
if fail_count > 0 { os.exit(1) }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user