feat: support enums, distinct types, unions and matching
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import "@std/io"
|
||||
import "@source/ir"
|
||||
import "@source/diagnostics"
|
||||
|
||||
@hide
|
||||
emit_instruction proc(state @mut State, block @ir.Block, instruction ir.Instruction) void ! io.WriteError {
|
||||
@@ -57,11 +58,49 @@ emit_instruction proc(state @mut State, block @ir.Block, instruction ir.Instruct
|
||||
}
|
||||
return
|
||||
}
|
||||
.union_literal: {
|
||||
shape :: state.module.arrays.items[usize(instruction.array_type)]
|
||||
try io.write_all(output, " store ")
|
||||
try write_type(output, state.module, type)
|
||||
try io.print(" zeroinitializer, ptr %v{}\n", output, {u32(instruction.destination)})
|
||||
if (shape.tag_type != .void) {
|
||||
try io.write_all(output, " store ")
|
||||
try write_type(output, state.module, Type{ kind = shape.tag_type })
|
||||
try io.write_all(output, " ")
|
||||
try write_constant(output, state.module, instruction.constant)
|
||||
try io.print(", ptr %v{}\n", output, {u32(instruction.destination)})
|
||||
}
|
||||
if (instruction.left != ir.no_value) {
|
||||
payload :: try load(state, instruction.left)
|
||||
pointer :: fresh(state)
|
||||
try io.print(" %r{} = getelementptr i8, ptr %v{}, i64 {}\n", output, {pointer, u32(instruction.destination), shape.payload_offset})
|
||||
try store_payload(state, state.values[usize(instruction.left)], payload, pointer)
|
||||
}
|
||||
return
|
||||
}
|
||||
.union_tag: {
|
||||
try io.print(" %r{} = load ", output, {result})
|
||||
try write_type(output, state.module, type)
|
||||
try io.print(", ptr %v{}\n", output, {u32(instruction.left)})
|
||||
}
|
||||
.field: {
|
||||
base :: try load(state, instruction.left)
|
||||
try io.print(" %r{} = extractvalue ", output, {result})
|
||||
try write_type(output, state.module, state.values[usize(instruction.left)])
|
||||
try io.print(" %r{}, {}\n", output, {base, instruction.slot})
|
||||
base_type :: state.values[usize(instruction.left)]
|
||||
shape :: state.module.arrays.items[usize(base_type.shape)]
|
||||
if (shape.is_union) {
|
||||
pointer :: fresh(state)
|
||||
try io.print(" %r{} = getelementptr i8, ptr %v{}, i64 0\n", output, {pointer, u32(instruction.left)})
|
||||
try check_union(state, pointer, shape, usize(instruction.slot), instruction.span)
|
||||
if (instruction.type == .void) return
|
||||
payload :: fresh(state)
|
||||
try io.print(" %r{} = getelementptr i8, ptr %r{}, i64 {}\n %r{} = load ", output, {payload, pointer, shape.payload_offset, result})
|
||||
try write_type(output, state.module, type)
|
||||
try io.print(", ptr %r{}\n", output, {payload})
|
||||
} else {
|
||||
base :: try load(state, instruction.left)
|
||||
try io.print(" %r{} = extractvalue ", output, {result})
|
||||
try write_type(output, state.module, base_type)
|
||||
try io.print(" %r{}, {}\n", output, {base, instruction.slot})
|
||||
}
|
||||
}
|
||||
.field_address: {
|
||||
base_type :: state.values[usize(instruction.left)]
|
||||
@@ -72,9 +111,19 @@ emit_instruction proc(state @mut State, block @ir.Block, instruction ir.Instruct
|
||||
address :: fresh(state)
|
||||
try io.print(" %r{} = extractvalue %address %r{}, 0\n", output, {data, base})
|
||||
try io.print(" %r{} = extractvalue %address %r{}, 1\n", output, {flag, base})
|
||||
try io.print(" %r{} = getelementptr ", output, {pointer})
|
||||
try write_type(output, state.module, Type{ kind = .record, shape = base_type.shape })
|
||||
try io.print(", ptr %r{}, i64 0, i32 {}\n", output, {data, instruction.slot})
|
||||
shape :: state.module.arrays.items[usize(base_type.shape)]
|
||||
if (shape.is_union) {
|
||||
if (shape.tag_type != .void) {
|
||||
position :: location(state.line_starts, instruction.span)
|
||||
try io.print(" call void @check_init(ptr %r{}, i1 true, i64 {}, i64 {}, i64 {})\n", output, {flag, usize(instruction.span.source), position.line, position.column})
|
||||
try check_union(state, data, shape, usize(instruction.slot), instruction.span)
|
||||
}
|
||||
try io.print(" %r{} = getelementptr i8, ptr %r{}, i64 {}\n", output, {pointer, data, shape.payload_offset})
|
||||
} else {
|
||||
try io.print(" %r{} = getelementptr ", output, {pointer})
|
||||
try write_type(output, state.module, Type{ kind = .record, shape = base_type.shape })
|
||||
try io.print(", ptr %r{}, i64 0, i32 {}\n", output, {data, instruction.slot})
|
||||
}
|
||||
try io.print(" %r{} = insertvalue %address poison, ptr %r{}, 0\n", output, {address, pointer})
|
||||
try io.print(" %r{} = insertvalue %address %r{}, ptr %r{}, 1\n", output, {result, address, flag})
|
||||
}
|
||||
@@ -129,9 +178,7 @@ emit_instruction proc(state @mut State, block @ir.Block, instruction ir.Instruct
|
||||
try io.print(" call void @check_init(ptr %r{}, i1 {}, i64 {}, i64 {}, i64 {})\n", output, {flag, instruction.op == .load_address, usize(instruction.span.source), position.line, position.column})
|
||||
if (instruction.op == .store_address) {
|
||||
value :: try load(state, instruction.right)
|
||||
try io.write_all(output, " store ")
|
||||
try write_type(output, state.module, state.values[usize(instruction.right)])
|
||||
try io.print(" %r{}, ptr %r{}\n", output, {value, pointer})
|
||||
try store_payload(state, state.values[usize(instruction.right)], value, pointer)
|
||||
return
|
||||
}
|
||||
try io.print(" %r{} = load ", output, {result})
|
||||
@@ -246,6 +293,46 @@ emit_instruction proc(state @mut State, block @ir.Block, instruction ir.Instruct
|
||||
try store(state, instruction.destination, result)
|
||||
}
|
||||
|
||||
@hide:file
|
||||
check_union proc(state @mut State, pointer usize, shape ir.ArrayType, field usize, span diagnostics.Span) void ! io.WriteError {
|
||||
if (shape.tag_type == .void) return
|
||||
tag :: fresh(state)
|
||||
failed :: fresh(state)
|
||||
try io.print(" %r{} = load ", state.output, {tag})
|
||||
try write_type(state.output, state.module, Type{ kind = shape.tag_type })
|
||||
try io.print(", ptr %r{}\n %r{} = icmp ne ", state.output, {pointer, failed})
|
||||
try write_type(state.output, state.module, Type{ kind = shape.tag_type })
|
||||
try io.print(" %r{}, ", state.output, {tag})
|
||||
try write_constant(state.output, state.module, shape.fields[field].tag)
|
||||
try io.write_all(state.output, "\n")
|
||||
try check_failure(state, failed, .inactive_union_field, span)
|
||||
}
|
||||
|
||||
@hide:file
|
||||
store_payload proc(state @mut State, type Type, value, pointer usize) void ! io.WriteError {
|
||||
if (type.kind == .void) return
|
||||
if (ir.aggregate(type.kind)) {
|
||||
shape :: state.module.arrays.items[usize(type.shape)]
|
||||
if (!shape.is_union) {
|
||||
count usize :: if (type.kind == .record) shape.fields.len else shape.len
|
||||
for (0..count) |i| {
|
||||
child :: if (type.kind == .record) Type{ kind = shape.fields[i].type, shape = shape.fields[i].array_type } else Type{ kind = shape.element, shape = shape.element_array }
|
||||
offset usize :: if (type.kind == .record) shape.fields[i].byte_offset else i * ir.storage_size(state.module, shape.element, shape.element_array)
|
||||
element :: fresh(state)
|
||||
address :: fresh(state)
|
||||
try io.print(" %r{} = extractvalue ", state.output, {element})
|
||||
try write_type(state.output, state.module, type)
|
||||
try io.print(" %r{}, {}\n %r{} = getelementptr i8, ptr %r{}, i64 {}\n", state.output, {value, i, address, pointer, offset})
|
||||
try store_payload(state, child, element, address)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
try io.write_all(state.output, " store ")
|
||||
try write_type(state.output, state.module, type)
|
||||
try io.print(" %r{}, ptr %r{}\n", state.output, {value, pointer})
|
||||
}
|
||||
|
||||
@hide:file
|
||||
emit_conversion proc(state @mut State, instruction ir.Instruction) usize ! io.WriteError {
|
||||
output :: state.output
|
||||
|
||||
@@ -143,6 +143,11 @@ write_type proc(output io.Writer, module @ir.Module, type Type) void ! io.WriteE
|
||||
}
|
||||
.record: {
|
||||
shape :: module.arrays.items[usize(type.shape)]
|
||||
if (shape.is_union) {
|
||||
words usize :: divtrunc!(shape.byte_size, 8)
|
||||
try io.print("{{ [{} x i64] }}", output, {words})
|
||||
return
|
||||
}
|
||||
try io.write_all(output, "{ ")
|
||||
for (shape.fields) |field, index| {
|
||||
if (index != 0) try io.write_all(output, ", ")
|
||||
@@ -307,6 +312,10 @@ emit_array_printer proc(output io.Writer, module @ir.Module, id ir.ArrayId) void
|
||||
emit_record_printer proc(output io.Writer, module @ir.Module, id ir.ArrayId) void ! io.WriteError {
|
||||
shape :: module.arrays.items[usize(id)]
|
||||
type :: Type{ kind = .record, shape = id }
|
||||
if (shape.is_union) {
|
||||
try emit_union_printer(output, module, id)
|
||||
return
|
||||
}
|
||||
try io.print("define internal void @print_array{}(", output, {u32(id)})
|
||||
try write_type(output, module, type)
|
||||
try io.write_all(output, " %value) {\nentry:\n call void @out(i32 1, ptr @record_open, i64 1)\n")
|
||||
@@ -321,6 +330,51 @@ emit_record_printer proc(output io.Writer, module @ir.Module, id ir.ArrayId) voi
|
||||
try io.write_all(output, " call void @out(i32 1, ptr @record_close, i64 1)\n ret void\n}\n")
|
||||
}
|
||||
|
||||
@hide:file
|
||||
emit_union_printer proc(output io.Writer, module @ir.Module, id ir.ArrayId) void ! io.WriteError {
|
||||
shape :: module.arrays.items[usize(id)]
|
||||
type :: Type{ kind = .record, shape = id }
|
||||
try io.print("define internal void @print_array{}(", output, {u32(id)})
|
||||
try write_type(output, module, type)
|
||||
try io.write_all(output, " %value) {\nentry:\n %storage = alloca ")
|
||||
try write_type(output, module, type)
|
||||
try io.write_all(output, "\n store ")
|
||||
try write_type(output, module, type)
|
||||
try io.write_all(output, " %value, ptr %storage\n call void @out(i32 1, ptr @record_open, i64 1)\n")
|
||||
if (shape.tag_type != .void) {
|
||||
try io.write_all(output, " %tag = load ")
|
||||
try write_type(output, module, Type{ kind = shape.tag_type })
|
||||
try io.write_all(output, ", ptr %storage\n")
|
||||
target :: if (shape.fields.len == 0) " br label %end\n" else " br label %test0\n"
|
||||
try io.write_all(output, target)
|
||||
for (shape.fields) |field, index| {
|
||||
try io.print("test{}:\n %tag_match{} = icmp eq ", output, {index, index})
|
||||
try write_type(output, module, Type{ kind = shape.tag_type })
|
||||
try io.write_all(output, " %tag, ")
|
||||
try write_constant(output, module, field.tag)
|
||||
try io.print("\n br i1 %tag_match{}, label %case{}, label %", output, {index, index})
|
||||
if (index + 1 == shape.fields.len) try io.write_all(output, "end\n")
|
||||
else try io.print("test{}\n", output, {index + 1})
|
||||
}
|
||||
} else {
|
||||
target :: if (shape.fields.len == 0) " br label %end\n" else " br label %case0\n"
|
||||
try io.write_all(output, target)
|
||||
}
|
||||
for (shape.fields) |field, index| {
|
||||
if (shape.tag_type == .void and index != 0) break
|
||||
try io.print("case{}:\n", output, {index})
|
||||
if (field.type != .void) {
|
||||
try io.print(" %p{} = getelementptr i8, ptr %storage, i64 {}\n %r{} = load ", output, {index, shape.payload_offset, index})
|
||||
try write_type(output, module, Type{ kind = field.type, shape = field.array_type })
|
||||
try io.print(", ptr %p{}\n", output, {index})
|
||||
if (field.type != .function) try print_call(output, module, Type{ kind = field.type, shape = field.array_type }, index)
|
||||
else try io.write_all(output, " call void @exit(i32 2)\n")
|
||||
}
|
||||
try io.write_all(output, " br label %end\n")
|
||||
}
|
||||
try io.write_all(output, "end:\n call void @out(i32 1, ptr @record_close, i64 1)\n ret void\n}\n")
|
||||
}
|
||||
|
||||
@hide
|
||||
fresh proc(state @mut State) usize {
|
||||
value :: state.next_register
|
||||
@@ -357,6 +411,7 @@ trap proc(state @State, code diagnostics.Code, span diagnostics.Span) void ! io.
|
||||
symbol []u8 :: match code {
|
||||
.integer_overflow: "overflow"
|
||||
.index_out_of_bounds: "bounds"
|
||||
.inactive_union_field: "inactive_union"
|
||||
else: unreachable
|
||||
}
|
||||
message :: enummap.get(diagnostics.Code, []u8, &diagnostics.messages, code)?
|
||||
@@ -398,6 +453,17 @@ write_constant proc(output io.Writer, module @ir.Module, value ir.Value) void !
|
||||
}
|
||||
.record: {
|
||||
shape :: module.arrays.items[usize(value.array_type)]
|
||||
if (shape.is_union) {
|
||||
words usize :: divtrunc!(shape.byte_size, 8)
|
||||
try io.print("{{ [{} x i64] [", output, {words})
|
||||
for (0..words) |index| {
|
||||
if (index != 0) try io.write_all(output, ", ")
|
||||
word :: ir.read_storage(module, .u64, ir.no_array, value.data.elements[index * 8..], true)
|
||||
try io.print("i64 {}", output, {word.data.unsigned})
|
||||
}
|
||||
try io.write_all(output, "] }")
|
||||
return
|
||||
}
|
||||
try io.write_all(output, "{ ")
|
||||
for (shape.fields) |_, index| {
|
||||
if (index != 0) try io.write_all(output, ", ")
|
||||
@@ -457,6 +523,7 @@ emit_function proc(state @mut State, function @ir.Function, id usize) void ! io.
|
||||
}
|
||||
}
|
||||
.trap: try io.print(" call void @trap(i64 {}, i64 0, i64 0, i64 0, ptr null, i64 0)\n unreachable\n", output, {u32(terminator.diagnostic)})
|
||||
.unreachable: try io.write_all(output, " unreachable\n")
|
||||
.jump: try io.print(" br label %b{}\n", output, {u32(terminator.target)})
|
||||
.branch: {
|
||||
condition :: try load(state, terminator.value)
|
||||
|
||||
@@ -52,6 +52,7 @@ runtime ::
|
||||
`@cycle = private constant [27 x i8] c"global initialization cycle"
|
||||
`@overflow = private constant [27 x i8] c"integer arithmetic overflow"
|
||||
`@bounds = private constant [19 x i8] c"index out of bounds"
|
||||
`@inactive_union = private constant [32 x i8] c"tagged union field is not active"
|
||||
`define internal void @out(i32 %fd, ptr %bytes, i64 %len) {
|
||||
`entry:
|
||||
` br label %loop
|
||||
|
||||
Reference in New Issue
Block a user