mirror of
https://github.com/odin-lang/Odin.git
synced 2026-07-17 21:21:04 +00:00
rexcode/ir: add Type_Kind.BOOL + array <id> length; SPIR-V bool/array round-trip
Extend the shared type model so SPIR-V's OpTypeBool and OpTypeArray (whose length
is a constant <id>, not a literal) lower cleanly:
- ir.Type_Kind gains BOOL (a distinct boolean; LLVM i1 will use it too).
- ir.Type gains len_ref: Id -- an ARRAY length carried as a constant <id>
(alongside the existing literal count for dialects with literal lengths).
- type_bool / type_array constructors.
SPIR-V codec: OpTypeBool <-> Type{.BOOL}; OpTypeArray <-> Type{.ARRAY, elem,
len_ref}. Test bool_and_array round-trips byte-exact -> 6 passed.
NOTE: a spec-valid module orders an array's length constant before the array
type; the codec round-trips the shape byte-exact regardless, but emitting the
types/constants section in dependency order is a follow-up.
This commit is contained in:
@@ -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]})
|
||||
|
||||
@@ -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 <id>
|
||||
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
|
||||
}
|
||||
|
||||
@@ -203,6 +203,24 @@ main :: proc() {
|
||||
roundtrip("load_aligned", m)
|
||||
}
|
||||
|
||||
// (6) the extended ir types: Type_Kind.BOOL (OpTypeBool) and ARRAY with an
|
||||
// <id> 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) }
|
||||
}
|
||||
|
||||
@@ -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` (<id> 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> (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}
|
||||
|
||||
Reference in New Issue
Block a user