diff --git a/core/rexcode/ir/spirv/decoder.odin b/core/rexcode/ir/spirv/decoder.odin index 7dcfd27ba..0fc7f4df2 100644 --- a/core/rexcode/ir/spirv/decoder.odin +++ b/core/rexcode/ir/spirv/decoder.odin @@ -213,7 +213,8 @@ lower :: proc(d: ^Decoder, opcode: Opcode, w: []u32) { append(&d.decorations, Decoration_Inst{Id(w[0]), Decoration(w[2]), w[1], ops}) case .OpTypeVoid: add_type(d, Id(w[0]), Type{kind = .VOID}) - case .OpTypeBool: add_type(d, Id(w[0]), Type{kind = .INT, bits = 1}) // ir has no BOOL kind + case .OpTypeBool: add_type(d, Id(w[0]), Type{kind = .BOOL}) + case .OpTypeArray: add_type(d, Id(w[0]), Type{kind = .ARRAY, elem = tref(d, w[1]), len_ref = Id(w[2])}) case .OpTypeInt: add_type(d, Id(w[0]), Type{kind = .INT, bits = u16(w[1]), aux = u16(w[2] & 1)}) case .OpTypeFloat: add_type(d, Id(w[0]), Type{kind = .FLOAT, bits = u16(w[1])}) case .OpTypeVector: add_type(d, Id(w[0]), Type{kind = .VECTOR, elem = tref(d, w[1]), count = w[2]}) diff --git a/core/rexcode/ir/spirv/encoder.odin b/core/rexcode/ir/spirv/encoder.odin index 10ec5c3b6..996d75918 100644 --- a/core/rexcode/ir/spirv/encoder.odin +++ b/core/rexcode/ir/spirv/encoder.odin @@ -177,9 +177,11 @@ emit_types :: proc "contextless" (w: ^Writer, m: ^Module) { op: Opcode switch t.kind { case .VOID: op = .OpTypeVoid + case .BOOL: op = .OpTypeBool case .INT: w_word(w, u32(t.bits)); w_word(w, u32(t.aux & 1)); op = .OpTypeInt case .FLOAT: w_word(w, u32(t.bits)); op = .OpTypeFloat case .VECTOR: w_id(w, tid(m, t.elem)); w_word(w, t.count); op = .OpTypeVector + case .ARRAY: w_id(w, tid(m, t.elem)); w_id(w, t.len_ref); op = .OpTypeArray // length: a constant case .POINTER: w_word(w, u32(t.aux)); w_id(w, tid(m, t.elem)); op = .OpTypePointer case .STRUCT: for f in t.fields { w_id(w, tid(m, f)) } @@ -188,7 +190,7 @@ emit_types :: proc "contextless" (w: ^Writer, m: ^Module) { 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 .ARRAY, .OPAQUE, .REF: + case .OPAQUE, .REF: w.pos = s // rewind the placeholder; not yet lowered continue } diff --git a/core/rexcode/ir/spirv/tests/roundtrip_test.odin b/core/rexcode/ir/spirv/tests/roundtrip_test.odin index 7f27b9258..923d83d50 100644 --- a/core/rexcode/ir/spirv/tests/roundtrip_test.odin +++ b/core/rexcode/ir/spirv/tests/roundtrip_test.odin @@ -203,6 +203,24 @@ main :: proc() { roundtrip("load_aligned", m) } + // (6) the extended ir types: Type_Kind.BOOL (OpTypeBool) and ARRAY with an + // length (OpTypeArray, len_ref -> a constant). NOTE: a spec-valid + // module orders the length constant before the array type; the codec + // round-trips this shape byte-exact regardless (dependency-ordered + // types/constants emit is a separate follow-up). + { + m := spirv.make_module() + m.capabilities = {.Shader} + m.types = { + {kind = .BOOL}, + {kind = .INT, bits = 32, aux = 1}, + {kind = .ARRAY, elem = spirv.Type_Ref(1), len_ref = spirv.Id(4)}, // int32[4] + } + m.type_ids = {spirv.Id(1), spirv.Id(2), spirv.Id(3)} + m.constants = {{result = {spirv.Id(4), spirv.Type_Ref(1)}, opcode = .OpConstant, value = 4}} + roundtrip("bool_and_array", m) + } + fmt.printf("\n%d passed, %d failed\n", ok_count, fail_count) if fail_count > 0 { os.exit(1) } } diff --git a/core/rexcode/ir/types.odin b/core/rexcode/ir/types.odin index 332b77340..323fa9714 100644 --- a/core/rexcode/ir/types.odin +++ b/core/rexcode/ir/types.odin @@ -27,10 +27,11 @@ TYPE_NONE :: Type_Ref(0xFFFFFFFF) Type_Kind :: enum u8 { VOID, + BOOL, // a distinct boolean (SPIR-V OpTypeBool, LLVM i1) INT, // `bits` = width (1/8/16/32/64/...); signedness is op-level in most IRs FLOAT, // `bits` = width (16/32/64/128) VECTOR, // `elem` x `count` (fixed-width SIMD) - ARRAY, // `elem` x `count` + ARRAY, // `elem` x `count` (literal length) or `elem` x `len_ref` ( length) POINTER, // `elem`, address space in `aux` STRUCT, // members in `fields` FUNCTION, // `fields` = params ++ [result]; `count` = param count @@ -41,20 +42,27 @@ Type_Kind :: enum u8 { // One node in a module's type table. `fields` (struct members / function // signature) is caller-owned, like the rest of the decoded module. Type :: struct { - fields: []Type_Ref, // STRUCT members, or FUNCTION params ++ result - name: string, // OPAQUE / named struct - elem: Type_Ref, // VECTOR / ARRAY / POINTER / typed REF element - count: u32, // VECTOR / ARRAY length, or FUNCTION param count - bits: u16, // INT / FLOAT width - aux: u16, // POINTER address space, packed kind flags, ... - kind: Type_Kind, - _: [3]u8, + fields: []Type_Ref, // STRUCT members, or FUNCTION params ++ result + name: string, // OPAQUE / named struct + elem: Type_Ref, // VECTOR / ARRAY / POINTER / typed REF element + count: u32, // VECTOR length, literal ARRAY length, or FUNCTION param count + len_ref: Id, // ARRAY length as a constant (id-typed lengths, e.g. SPIR-V) + bits: u16, // INT / FLOAT width + aux: u16, // POINTER address space, packed kind flags, ... + kind: Type_Kind, + _: [3]u8, } @(require_results) type_void :: #force_inline proc "contextless" () -> Type { return Type{kind = .VOID} } +@(require_results) type_bool :: #force_inline proc "contextless" () -> Type { return Type{kind = .BOOL} } @(require_results) type_int :: #force_inline proc "contextless" (bits: u16) -> Type { return Type{kind = .INT, bits = bits} } @(require_results) type_float :: #force_inline proc "contextless" (bits: u16) -> Type { return Type{kind = .FLOAT, bits = bits} } +@(require_results) +type_array :: #force_inline proc "contextless" (elem: Type_Ref, len_ref: Id) -> Type { + return Type{kind = .ARRAY, elem = elem, len_ref = len_ref} +} + @(require_results) type_vector :: #force_inline proc "contextless" (elem: Type_Ref, count: u32) -> Type { return Type{kind = .VECTOR, elem = elem, count = count}