Files
honey/source/llvm/llvm.hon
T

627 lines
29 KiB
Plaintext

import "@std/mem"
import "@std/io"
import "@std/enummap"
import "@std/arraylist"
import "@source/ir"
import "@source/diagnostics"
@hide
Type :: struct {
kind ir.Type = .void
shape ir.ArrayId = ir.no_array
}
@hide
State :: struct {
output io.Writer
module @ir.Module
line_starts []arraylist.ArrayList(usize)
values []mut Type
next_register usize = 0
}
#! writes a standalone native LLVM module; all temporary emitter storage is released before returning.
emit proc(
allocator mem.Allocator,
output io.Writer,
module @ir.Module,
diagnostic_store @diagnostics.Store,
sources []diagnostics.Source,
primary diagnostics.SourceId,
) void ! (mem.AllocError | io.WriteError) {
line_starts := arraylist.init(arraylist.ArrayList(usize), allocator)
defer {
for (line_starts.items) |@starts| arraylist.deinit(starts)
arraylist.deinit(&line_starts)
}
for (sources) |source| {
starts := arraylist.init(usize, allocator)
errdefer arraylist.deinit(&starts)
try arraylist.append(&starts, usize(0))
for (source.text) |byte, index| {
if (byte == '\n') try arraylist.append(&starts, index + 1)
}
try arraylist.append(&line_starts, starts)
}
try io.write_all(output, runtime)
for (sources) |source, index| {
try io.print("\n@p{} = private constant ", output, {index})
try bytes_constant(output, source.path, false)
}
try io.print("\n@paths = private constant [{} x %string] [", output, {sources.len})
for (sources) |source, index| {
if (index != 0) try io.write_all(output, ", ")
try io.print("%string {{ ptr @p{}, i64 {} }}", output, {index, source.path.len})
}
try io.write_all(output, "]\n")
for (module.strings.items) |string, index| {
try io.print("@s{} = private constant ", output, {index})
try bytes_constant(output, string.allocation[..string.len], true)
try io.write_all(output, "\n")
}
for (diagnostic_store.list.items) |diagnostic, index| {
try io.print("@d{} = private constant ", output, {index})
try bytes_constant(output, enummap.get(diagnostics.Code, []u8, &diagnostics.messages, diagnostic.code)?, false)
try io.write_all(output, "\n")
}
memory_codes [6]diagnostics.Code :: [
.null_unwrap, .invalid_slice_bounds, .sentinel_mismatch,
.overlapping_memory_copy, .unsupported_foreign_call, .invalid_pointer_access,
]
for (memory_codes) |code| {
try io.print("@memory_{s} = private constant ", output, {memory_symbol(code)})
try bytes_constant(output, enummap.get(diagnostics.Code, []u8, &diagnostics.messages, code)?, false)
try io.write_all(output, "\n")
}
try emit_reporting(output, diagnostic_store, line_starts.items, sources, primary)
for (module.globals.items) |global, index| {
try io.print("@g{} = internal global ", output, {index})
storage_kind :: if (global.type == .void) ir.Type.invalid else global.type
try write_type(output, module, Type{ kind = storage_kind, shape = global.array_type })
try io.print(" zeroinitializer\n@gs{} = internal global i8 0\n", output, {index})
try emit_global(output, module, global, index)
}
for (module.arrays.items) |shape, index| {
if (shape.kind == .record) try emit_record_printer(output, module, ir.ArrayId(index))
else try emit_array_printer(output, module, ir.ArrayId(index))
}
for (module.functions.items) |function, index| {
if (function.external_name.len != 0) {
duplicate := false
for (module.functions.items[..index]) |previous| {
if (mem.eql(previous.external_name, function.external_name)) duplicate = true
}
if (duplicate or mem.eql(function.external_name, "write") or mem.eql(function.external_name, "__error")
or mem.eql(function.external_name, "snprintf") or mem.eql(function.external_name, "exit")) continue
}
values :: try mem.alloc(Type, allocator, function.value_count)
defer mem.free(allocator, values)
mem.set(values, Type{})
for (function.blocks.items) |block| {
for (block.instructions.items) |instruction| {
if (instruction.destination != ir.no_value) {
values[usize(instruction.destination)] = Type{ kind = instruction.type, shape = instruction.array_type }
}
}
}
state := State{ output = output, module = module, line_starts = line_starts.items, values = values }
try emit_function(&state, &function, index)
}
try io.write_all(output, "define i32 @main() {\nentry:\n")
if (module.entry) |entry| {
function :: module.functions.items[usize(entry)]
result :: Type{ kind = function.result, shape = function.result_array_type }
if (result.kind != .void) try io.write_all(output, " %r0 = ")
else try io.write_all(output, " ")
try io.write_all(output, "call ")
try write_abi_result(output, module, result)
try io.write_all(output, " ")
try function_symbol(output, module, entry)
try io.write_all(output, "()\n")
if (result.kind != .void and result.kind != .invalid) {
try print_call(output, module, result, 0)
try io.write_all(output, " call void @out(i32 1, ptr @newline, i64 1)\n")
}
try io.write_all(output, " call void @report(i64 -1)\n")
status i32 :: if (diagnostics.count(diagnostic_store) == 0) 0 else 1
try io.print(" ret i32 {}\n", output, {status})
} else {
try io.print(" call void @trap(i64 {}, i64 0, i64 0, i64 0, ptr null, i64 0)\n unreachable\n", output, {u32(module.entry_diagnostic)})
}
try io.write_all(output, "}\n")
}
@hide:file
bytes_constant proc(output io.Writer, bytes []u8, terminated bool) void ! io.WriteError {
size usize :: if (terminated) bytes.len + 1 else bytes.len
try io.print("[{} x i8] c\"", output, {size})
digits :: "0123456789ABCDEF"
for (bytes) |byte| {
escaped [3]u8 :: ['\\', digits[usize(divtrunc!(byte, 16))], digits[usize(rem!(byte, 16))]]
try io.write_all(output, (&escaped)[..])
}
if (terminated) try io.write_all(output, "\\00")
try io.write_all(output, "\"")
}
@hide
write_type proc(output io.Writer, module @ir.Module, type Type) void ! io.WriteError {
match type.kind {
.i8, .u8: try io.write_all(output, "i8")
.i16, .u16: try io.write_all(output, "i16")
.i32, .u32: try io.write_all(output, "i32")
.int, .isize, .u64, .usize: try io.write_all(output, "i64")
.float: try io.write_all(output, "double")
.bool: try io.write_all(output, "i1")
.string: try io.write_all(output, "%string")
.function, .pointer: try io.write_all(output, "ptr")
.address: try io.write_all(output, "%address")
.array: {
shape :: module.arrays.items[usize(type.shape)]
try io.print("[{} x ", output, {shape.len})
try write_type(output, module, Type{ kind = shape.element, shape = shape.element_array })
try io.write_all(output, "]")
}
.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, ", ")
try write_type(output, module, Type{ kind = field.type, shape = field.array_type })
}
try io.write_all(output, " }")
}
.void: try io.write_all(output, "void")
# invalid storage only belongs to recovery paths that trap before reading it.
.invalid: try io.write_all(output, "i8")
}
}
@hide:file
abi_extension proc(kind ir.Type) []u8 {
return match kind {
.i8, .i16: "signext"
.u8, .u16, .bool: "zeroext"
else: ""
}
}
@hide
write_abi_result proc(output io.Writer, module @ir.Module, type Type) void ! io.WriteError {
extension :: abi_extension(type.kind)
if (extension.len != 0) try io.print("{s} ", output, {extension})
try write_type(output, module, type)
}
@hide
write_abi_parameter proc(output io.Writer, module @ir.Module, type Type) void ! io.WriteError {
try write_type(output, module, type)
extension :: abi_extension(type.kind)
if (extension.len != 0) try io.print(" {s}", output, {extension})
}
@hide
function_symbol proc(output io.Writer, module @ir.Module, id ir.FunctionId) void ! io.WriteError {
name :: module.functions.items[usize(id)].external_name
if (name.len == 0) {
try io.print("@f{}", output, {u32(id)})
return
}
try io.write_all(output, "@\"")
digits :: "0123456789ABCDEF"
for (name) |byte| {
escaped [3]u8 :: ['\\', digits[usize(divtrunc!(byte, 16))], digits[usize(rem!(byte, 16))]]
try io.write_all(output, (&escaped)[..])
}
try io.write_all(output, "\"")
}
@hide:file
local_type proc(function @ir.Function, index usize) Type {
type := Type{ kind = function.locals.items[index] }
if (type.kind == .void) type.kind = .invalid
if (ir.aggregate(type.kind)) {
for (function.array_storage.items) |storage| {
if (storage.local and usize(storage.index) == index) {
type.shape = storage.array_type
break
}
}
}
return type
}
@hide
Location :: struct {
line usize = 1
column usize = 1
}
@hide
location proc(sources []arraylist.ArrayList(usize), span diagnostics.Span) Location {
line_starts :: sources[usize(span.source)].items
offset :: usize(span.start)
line := usize(0)
end := line_starts.len
while (line + 1 < end) {
middle :: line + divtrunc!(end - line, 2)
if (line_starts[middle] <= offset) line = middle
else end = middle
}
return Location{ line = line + 1, column = offset - line_starts[line] + 1 }
}
@hide:file
emit_reporting proc(
output io.Writer,
diagnostic_store @diagnostics.Store,
line_starts []arraylist.ArrayList(usize),
sources []diagnostics.Source,
primary diagnostics.SourceId,
) void ! io.WriteError {
try io.write_all(output, "define internal void @io_failure() noreturn {\nentry:\n")
try io.print(" %path_result = call i64 @write(i32 2, ptr @p{}, i64 {})\n", output, {usize(primary), sources[usize(primary)].path.len})
try io.write_all(output, " %message_result = call i64 @write(i32 2, ptr @write_error, i64 44)\n call void @exit(i32 2)\n unreachable\n}\n")
try io.write_all(output, "define internal void @diagnose(i64 %source, i64 %line, i64 %column, ptr %message, i64 %length, i1 %trap) {\nentry:\n")
try io.print(" %descriptor = getelementptr [{} x %string], ptr @paths, i64 0, i64 %source\n", output, {sources.len})
try io.write_all(output, " %path = load %string, ptr %descriptor\n %bytes = extractvalue %string %path, 0\n %path_length = extractvalue %string %path, 1\n call void @out(i32 2, ptr %bytes, i64 %path_length)\n")
try io.write_all(output,
` call void @out(i32 2, ptr @colon, i64 1)
` call void @print_integer(i32 2, i64 %line, i1 true)
` call void @out(i32 2, ptr @colon, i64 1)
` call void @print_integer(i32 2, i64 %column, i1 true)
` %label = select i1 %trap, ptr @runtime_trap, ptr @error
` %label_length = select i1 %trap, i64 16, i64 9
` call void @out(i32 2, ptr %label, i64 %label_length)
` call void @out(i32 2, ptr %message, i64 %length)
` call void @out(i32 2, ptr @newline, i64 1)
` ret void
`}
)
try io.write_all(output, "define internal void @report(i64 %trap) {\nentry:\n")
for (diagnostic_store.list.items) |diagnostic, index| {
position :: location(line_starts, diagnostic.span)
message :: enummap.get(diagnostics.Code, []u8, &diagnostics.messages, diagnostic.code)?
try io.print(" %t{} = icmp eq i64 %trap, {}\n", output, {index, index})
try io.print(" call void @diagnose(i64 {}, i64 {}, i64 {}, ptr @d{}, i64 {}, i1 %t{})\n", output, {
usize(diagnostic.span.source), position.line, position.column, index, message.len, index,
})
}
try io.write_all(output, " ret void\n}\n")
}
@hide:file
emit_global proc(output io.Writer, module @ir.Module, global ir.Global, index usize) void ! io.WriteError {
type :: Type{ kind = global.type, shape = global.array_type }
try io.print("define internal ptr @global{}(i64 %source, i64 %line, i64 %column) {{\nentry:\n", output, {index})
try io.print(" %state = load i8, ptr @gs{}\n", output, {index})
try io.write_all(output,
` switch i8 %state, label %initialize [ i8 1, label %cycle i8 2, label %ready ]
`cycle:
` call void @trap(i64 -1, i64 %source, i64 %line, i64 %column, ptr @cycle, i64 27)
` unreachable
`initialize:
)
try io.print(" store i8 1, ptr @gs{}\n", output, {index})
if (type.kind == .void) {
try io.print(" call void @f{}()\n", output, {u32(global.initializer)})
} else {
try io.write_all(output, " %value = call ")
try write_abi_result(output, module, type)
try io.print(" @f{}()\n store ", output, {u32(global.initializer)})
try write_type(output, module, type)
try io.print(" %value, ptr @g{}\n", output, {index})
}
try io.print(" store i8 2, ptr @gs{}\n br label %ready\nready:\n ret ptr @g{}\n}}\n", output, {index, index})
}
@hide:file
print_call proc(output io.Writer, module @ir.Module, type Type, operand usize) void ! io.WriteError {
match type.kind {
.i8, .i16, .i32, .int, .isize, .u8, .u16, .u32, .u64, .usize: {
bits :: ir.integer_bits(type.kind)
unsigned :: ir.unsigned_integer(type.kind)
if (bits < 64) {
extension []u8 :: if (unsigned) "zext" else "sext"
try io.print(" %print_value{} = {s} i{} %r{} to i64\n", output, {operand, extension, bits, operand})
try io.print(" call void @print_integer(i32 1, i64 %print_value{}, i1 {})\n", output, {operand, unsigned})
} else try io.print(" call void @print_integer(i32 1, i64 %r{}, i1 {})\n", output, {operand, unsigned})
}
.float: try io.print(" call void @print_float(double %r{})\n", output, {operand})
.bool: try io.print(" call void @print_bool(i1 %r{})\n", output, {operand})
.string: try io.print(" call void @print_string(%string %r{})\n", output, {operand})
.pointer: try io.write_all(output, " call void @out(i32 1, ptr @pointer_text, i64 9)\n")
.array, .record: {
try io.print(" call void @print_array{}(", output, {u32(type.shape)})
try write_type(output, module, type)
try io.print(" %r{})\n", output, {operand})
}
else: unreachable
}
}
@hide:file
emit_array_printer proc(output io.Writer, module @ir.Module, id ir.ArrayId) void ! io.WriteError {
shape :: module.arrays.items[usize(id)]
type :: Type{ kind = .array, shape = id }
element :: Type{ kind = shape.element, shape = shape.element_array }
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 @open, i64 1)\n br label %loop\nloop:\n %index = phi i64 [ 0, %entry ], [ %next, %element ]\n")
try io.print(" %done = icmp eq i64 %index, {}\n", output, {shape.len})
try io.write_all(output, " br i1 %done, label %end, label %separator\nseparator:\n %first = icmp eq i64 %index, 0\n br i1 %first, label %element, label %comma\ncomma:\n call void @out(i32 1, ptr @separator, i64 2)\n br label %element\nelement:\n %pointer = getelementptr ")
try write_type(output, module, type)
try io.write_all(output, ", ptr %storage, i64 0, i64 %index\n %r0 = load ")
try write_type(output, module, element)
try io.write_all(output, ", ptr %pointer\n")
# functions may occur inside arrays without being printable source results.
if (element.kind != .function) try print_call(output, module, element, 0)
else try io.write_all(output, " call void @exit(i32 2)\n")
try io.write_all(output, " %next = add i64 %index, 1\n br label %loop\nend:\n call void @out(i32 1, ptr @close, i64 1)\n ret void\n}\n")
}
@hide:file
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")
for (shape.fields) |field, index| {
if (index != 0) try io.write_all(output, " call void @out(i32 1, ptr @separator, i64 2)\n")
try io.print(" %r{} = extractvalue ", output, {index})
try write_type(output, module, type)
try io.print(" %value, {}\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, " 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
state.next_register += 1
return value
}
@hide
load proc(state @mut State, value ir.ValueId) usize ! io.WriteError {
result :: fresh(state)
try io.print(" %r{} = load ", state.output, {result})
try write_type(state.output, state.module, state.values[usize(value)])
try io.print(", ptr %v{}\n", state.output, {u32(value)})
return result
}
@hide
store proc(state @State, destination ir.ValueId, register usize) void ! io.WriteError {
if (destination == ir.no_value or state.values[usize(destination)].kind == .void) return
try io.write_all(state.output, " store ")
try write_type(state.output, state.module, state.values[usize(destination)])
try io.print(" %r{}, ptr %v{}\n", state.output, {register, u32(destination)})
}
@hide
check_local proc(state @State, slot u32, read bool, span diagnostics.Span) void ! io.WriteError {
position :: location(state.line_starts, span)
try io.print(" call void @check_init(ptr %i{}, i1 {}, i64 {}, i64 {}, i64 {})\n", state.output, {slot, read, usize(span.source), position.line, position.column})
}
@hide:file
memory_symbol proc(code diagnostics.Code) []u8 {
return match code {
.null_unwrap: "null_unwrap"
.invalid_slice_bounds: "invalid_slice_bounds"
.sentinel_mismatch: "sentinel_mismatch"
.overlapping_memory_copy: "overlapping_memory_copy"
.unsupported_foreign_call: "unsupported_foreign_call"
.invalid_pointer_access: "invalid_pointer_access"
else: unreachable
}
}
@hide:file
trap proc(state @State, code diagnostics.Code, span diagnostics.Span) void ! io.WriteError {
position :: location(state.line_starts, span)
symbol []u8 :: match code {
.integer_overflow: "overflow"
.index_out_of_bounds: "bounds"
.inactive_union_field: "inactive_union"
else: ""
}
message :: enummap.get(diagnostics.Code, []u8, &diagnostics.messages, code)?
length :: message.len
try io.print(" call void @trap(i64 -1, i64 {}, i64 {}, i64 {}, ptr @", state.output, {usize(span.source), position.line, position.column})
if (symbol.len != 0) try io.write_all(state.output, symbol)
else try io.print("memory_{s}", state.output, {memory_symbol(code)})
try io.print(", i64 {})\n unreachable\n", state.output, {length})
}
@hide
check_failure proc(state @mut State, condition usize, code diagnostics.Code, span diagnostics.Span) void ! io.WriteError {
label :: fresh(state)
try io.print(" br i1 %r{}, label %fail{}, label %ok{}\nfail{}:\n", state.output, {condition, label, label, label})
try trap(state, code, span)
try io.print("ok{}:\n", state.output, {label})
}
@hide
write_constant proc(output io.Writer, module @ir.Module, value ir.Value) void ! io.WriteError {
match value.type {
.i8, .i16, .i32, .int, .isize: try io.print("{}", output, {value.data.integer})
.u8, .u16, .u32, .u64, .usize: try io.print("{}", output, {value.data.unsigned})
.float: {
bits :: ptrcast!(@u64, &value.data.real)^
try io.print("bitcast (i64 {} to double)", output, {bits})
}
.bool: try io.print("{}", output, {value.data.boolean})
.string: try io.print("{{ ptr @s{}, i64 {} }}", output, {u32(value.data.string), ir.string_bytes(module, value.data.string).len})
.pointer: {
if (value.data.unsigned == maxval!(u64)) try io.write_all(output, "null")
else unreachable
}
.function: {
if (value.data.function == maxval!(ir.FunctionId)) try io.write_all(output, "null")
else try function_symbol(output, module, value.data.function)
}
.array: {
shape :: module.arrays.items[usize(value.array_type)]
try io.write_all(output, "[")
for (0..shape.len) |index| {
if (index != 0) try io.write_all(output, ", ")
element :: ir.aggregate_item(module, value, index)
try write_type(output, module, Type{ kind = element.type, shape = element.array_type })
try io.write_all(output, " ")
try write_constant(output, module, element)
}
try io.write_all(output, "]")
}
.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, ", ")
field :: ir.aggregate_item(module, value, index)
try write_type(output, module, Type{ kind = field.type, shape = field.array_type })
try io.write_all(output, " ")
try write_constant(output, module, field)
}
try io.write_all(output, " }")
}
else: unreachable
}
}
@hide:file
emit_function proc(state @mut State, function @ir.Function, id usize) void ! io.WriteError {
output :: state.output
if (function.external_name.len != 0) try io.write_all(output, "declare ")
else try io.write_all(output, "define internal ")
try write_abi_result(output, state.module, Type{ kind = function.result, shape = function.result_array_type })
try io.write_all(output, " ")
try function_symbol(output, state.module, ir.FunctionId(id))
try io.write_all(output, "(")
for (function.parameters.items) |_, index| {
if (index != 0) try io.write_all(output, ", ")
try write_abi_parameter(output, state.module, local_type(function, index))
try io.print(" %p{}", output, {index})
}
if (function.external_name.len != 0) {
try io.write_all(output, ")\n")
return
}
try io.write_all(output, ") {\nentry:\n")
for (function.locals.items) |_, index| {
type :: local_type(function, index)
try io.print(" %l{} = alloca ", output, {index})
try write_type(output, state.module, type)
try io.print("\n %i{} = alloca i1\n store i1 {}, ptr %i{}\n", output, {index, index < function.parameters.items.len, index})
if (index < function.parameters.items.len) {
try io.write_all(output, " store ")
try write_type(output, state.module, type)
try io.print(" %p{}, ptr %l{}\n", output, {index, index})
}
}
for (state.values) |type, index| {
if (type.kind == .void) continue
try io.print(" %v{} = alloca ", output, {index})
try write_type(output, state.module, type)
try io.write_all(output, "\n")
}
try io.print(" br label %b{}\n", output, {u32(function.entry)})
for (function.blocks.items) |block, index| {
try io.print("b{}:\n", output, {index})
for (block.instructions.items) |instruction| try emit_instruction(state, &block, instruction)
terminator :: block.terminator
match terminator.kind {
.return_value: {
if (terminator.value == ir.no_value or state.values[usize(terminator.value)].kind == .void) try io.write_all(output, " ret void\n")
else {
result :: try load(state, terminator.value)
try io.write_all(output, " ret ")
try write_type(output, state.module, state.values[usize(terminator.value)])
try io.print(" %r{}\n", output, {result})
}
}
.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)
try io.print(" br i1 %r{}, label %b{}, label %b{}\n", output, {condition, u32(terminator.target), u32(terminator.alternative)})
}
}
}
try io.write_all(output, "}\n")
}