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") printable := result operand usize := 0 if (function.result_fallible) { shape :: module.arrays.items[usize(result.shape)] success :: shape.fields[0] try io.write_all(output, " %result = alloca ") try write_type(output, module, result) try io.write_all(output, "\n store ") try write_type(output, module, result) try io.write_all(output, " %r0, ptr %result\n %tag = load ") try write_type(output, module, Type{ kind = shape.tag_type }) try io.write_all(output, ", ptr %result\n %success = icmp eq ") try write_type(output, module, Type{ kind = shape.tag_type }) try io.write_all(output, " %tag, ") try write_constant(output, module, success.tag) try io.write_all(output, "\n br i1 %success, label %ok, label %error\nerror:\n call void @report(i64 -1)\n ret i32 1\nok:\n") printable = Type{ kind = success.type, shape = success.array_type } operand = 1 if (printable.kind != .void) { try io.print(" %payload = getelementptr i8, ptr %result, i64 {}\n %r1 = load ", output, {shape.payload_offset}) try write_type(output, module, printable) try io.write_all(output, ", ptr %payload\n") } } if (printable.kind != .void and printable.kind != .invalid) { try print_call(output, module, printable, operand) 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") }