package llvm import "../ir" import "../source" import "../symbol" import "../target" import "../types" import "core:fmt" import "core:mem" import "core:strings" Trap_Message :: struct { text: string, } Emitter :: struct { module: ^ir.Module, diagnostics: ^source.Diagnostics, symbols: ^symbol.Table, builder: strings.Builder, messages: [dynamic]Trap_Message, allocator: mem.Allocator, } llvm_type :: proc(value: types.Type, store: ^types.Store = nil) -> string { if types.is_void(value) { return "void" } #partial switch types.kind(value, store) { case .Pointer: return "ptr" case .Slice: return "{ ptr, i64 }" case .Array: item, _ := types.node(store, value) return fmt.tprintf("[%d x %s]", types.physical_count(value, store), llvm_type(item.child, store)) case .Optional: item, _ := types.node(store, value) if types.is_pointer(item.child, store) { return "ptr" } return fmt.tprintf("{{ i1, %s }}", llvm_type(item.child, store)) case .Struct: return fmt.tprintf("%%bro.type.%d", value) } selected := store.selected if store != nil else target.DEFAULT repr := types.representation(value, selected) if types.is_float(repr) { return "float" if types.bits(repr) == 32 else "double" } switch types.bits(repr) { case 8: return "i8" case 16: return "i16" case 32: return "i32" case: return "i64" } } function_result_type :: proc(function: ir.Function, store: ^types.Store) -> string { if function.is_main { return "i32" } return llvm_type(function.result, store) } c_abi_extension :: proc(value: types.Type, selected: target.Target) -> string { if !types.is_concrete_integer(value) { return "" } switch target.c_integer_extension(selected, types.bits(value, selected), types.is_signed(value, selected)) { case .Sign: return "signext" case .Zero: return "zeroext" case .None: return "" } return "" } emit_function_result :: proc(builder: ^strings.Builder, function: ir.Function, store: ^types.Store) { if function.calling_convention == .C { extension := c_abi_extension(function.result, store.selected) if len(extension) > 0 { fmt.sbprintf(builder, "%s ", extension) } } strings.write_string(builder, function_result_type(function, store)) } sentinel :: proc(value_type: types.Type, selected := target.DEFAULT) -> i64 { switch types.bits(value_type, selected) { case 8: return -86 case 16: return -21846 case 32: return -1431655766 case: return -6148914691236517206 } } valid_instruction :: proc(instructions: []ir.Instruction, instruction_id: ir.Instruction_Id) -> bool { return instruction_id != ir.INVALID_INSTRUCTION && int(instruction_id) < len(instructions) } valid_value :: proc( instructions: []ir.Instruction, value_id: ir.Instruction_Id, expected: types.Type, store: ^types.Store, ) -> bool { if !valid_instruction(instructions, value_id) || !types.is_runtime_value(expected, store) || !types.equal(instructions[value_id].type, expected) { return false } switch instructions[value_id].op { case .Param, .Const, .String, .Aggregate, .None, .Optional_Some, .Load_Global, .Address_Of, .Load, .Slice, .Length, .Slice_Ptr, .Unwrap, .Orelse, .Widen, .C_Vararg_Promote, .Weaken_Pointer, .Weaken_Slice, .Decay_Array_Pointer, .Neg_Checked, .Add_Checked, .Pointer_Add, .Call: return true case .Address_Global, .Alloca, .Index_Address, .Field_Address, .Orelse_Begin, .Store, .Trap, .Return, .Return_Void: return false } return false } valid_address :: proc( instructions: []ir.Instruction, value_id: ir.Instruction_Id, pointee: types.Type, store: ^types.Store, ) -> bool { if !valid_instruction(instructions, value_id) { return false } value := instructions[value_id] #partial switch value.op { case .Address_Global, .Alloca, .Index_Address, .Field_Address: return types.equal(value.type, pointee) case: return types.is_pointer(value.type, store) && types.equal(types.child_type(value.type, store), pointee) && valid_value(instructions, value_id, value.type, store) } } write_constant :: proc(builder: ^strings.Builder, value: i64, value_type: types.Type, store: ^types.Store = nil) { if !types.is_concrete_scalar(value_type) { strings.write_string(builder, "zeroinitializer") return } selected := store.selected if store != nil else target.DEFAULT if types.is_float(value_type, selected) { text := "" if types.bits(value_type, selected) == 32 { bits := u32(value) number := transmute(f32)bits text = fmt.tprintf("%.9g", number) } else { number := transmute(f64)value text = fmt.tprintf("%.17g", number) } strings.write_string(builder, text) if !strings.contains(text, ".") && !strings.contains(text, "e") && !strings.contains(text, "E") { strings.write_string(builder, ".0") } return } fmt.sbprintf(builder, "%d", value) } write_operand :: proc( builder: ^strings.Builder, instructions: []ir.Instruction, value_id: ir.Instruction_Id, expected: types.Type, store: ^types.Store, ) { if !valid_value(instructions, value_id, expected, store) { write_constant(builder, sentinel(expected, store.selected), expected, store) return } value := instructions[value_id] if value.op == .Const { write_constant(builder, value.integer, expected, store) } else { fmt.sbprintf(builder, "%%v%d", value_id) } } register_message :: proc(emitter: ^Emitter, text: string) -> int { id := len(emitter.messages) cloned := fmt.aprintf("%s\n", text, allocator=emitter.allocator) append(&emitter.messages, Trap_Message{text=cloned}) return id } diagnostic_message :: proc(emitter: ^Emitter, diagnostic: source.Diagnostic_Id, span: source.Span, fallback: string) -> int { if _, ok := source.diagnostic_index(diagnostic, len(emitter.diagnostics.items)); ok { message := source.format(emitter.diagnostics, diagnostic, emitter.allocator) id := register_message(emitter, message) delete(message, emitter.allocator) return id } source_file := source.source_for_span(emitter.diagnostics, span) if source_file == nil { return register_message(emitter, fallback) } line, column := source.line_and_column(source_file, span.start) message := fmt.aprintf( "%s:%d:%d: runtime trap: %s", source_file.path, line, column, fallback, allocator=emitter.allocator, ) id := register_message(emitter, message) delete(message, emitter.allocator) return id } emit_trap_call :: proc(emitter: ^Emitter, message_id: int) { message := emitter.messages[message_id] fmt.sbprintf( &emitter.builder, " call void @bro.trap(ptr @bro.msg.%d, i64 %d)\n", message_id, len(message.text), ) } emit_recovery_value :: proc(emitter: ^Emitter, instruction_id: int, instruction: ir.Instruction, fallback: string) { message := diagnostic_message(emitter, instruction.diagnostic, instruction.span, fallback) emit_trap_call(emitter, message) if types.is_runtime_value(instruction.type, &emitter.module.types) { if !types.is_float(instruction.type, emitter.module.target) { if !types.is_concrete_scalar(instruction.type) { fmt.sbprintf( &emitter.builder, " %%v%d = freeze %s zeroinitializer\n", instruction_id, llvm_type(instruction.type, &emitter.module.types), ) return } fmt.sbprintf( &emitter.builder, " %%v%d = add %s 0, %d\n", instruction_id, llvm_type(instruction.type, &emitter.module.types), sentinel(instruction.type, emitter.module.target), ) return } fmt.sbprintf( &emitter.builder, " %%v%d = select i1 true, %s ", instruction_id, llvm_type(instruction.type, &emitter.module.types), ) write_constant(&emitter.builder, sentinel(instruction.type, emitter.module.target), instruction.type, &emitter.module.types) fmt.sbprintf(&emitter.builder, ", %s ", llvm_type(instruction.type, &emitter.module.types)) write_constant(&emitter.builder, sentinel(instruction.type, emitter.module.target), instruction.type, &emitter.module.types) strings.write_string(&emitter.builder, "\n") } } emit_call_args :: proc( builder: ^strings.Builder, instructions: []ir.Instruction, args: []ir.Instruction_Id, param_types: []types.Type, store: ^types.Store, c_abi := false, ) { for arg, index in args { if index > 0 { strings.write_string(builder, ", ") } arg_type := param_types[index] if index < len(param_types) && valid_instruction(instructions, arg) else (instructions[arg].type if valid_instruction(instructions, arg) else types.INVALID) fmt.sbprintf(builder, "%s ", llvm_type(arg_type, store)) if c_abi && index < len(param_types) { extension := c_abi_extension(arg_type, store.selected) if len(extension) > 0 { fmt.sbprintf(builder, "%s ", extension) } } write_operand(builder, instructions, arg, arg_type, store) } } emit_instruction_stream :: proc( emitter: ^Emitter, instructions: []ir.Instruction, function: ir.Function, global_initializer := false, ) -> ir.Instruction_Id { return_value := ir.INVALID_INSTRUCTION after_return := false for instruction, instruction_index in instructions { instruction_id := ir.instruction_id(instruction_index) if after_return { fmt.sbprintf(&emitter.builder, "recover_after_return_%d:\n", instruction_index) after_return = false } switch instruction.op { case .Param, .Const: case .String: string_id := int(instruction.integer) _, array, pointer_ok := types.array_pointer(instruction.type, &emitter.module.types) if string_id < 0 || string_id >= len(emitter.module.strings) || !pointer_ok || array.child != types.U8 || !array.has_sentinel || array.sentinel != 0 || array.count != u64(len(emitter.module.strings[string_id])) { emit_recovery_value(emitter, instruction_index, instruction, "invalid string literal") continue } fmt.sbprintf( &emitter.builder, " %%v%d = getelementptr %s, ptr @bro.str.%d, i64 0\n", instruction_index, llvm_type(types.child_type(instruction.type, &emitter.module.types), &emitter.module.types), string_id, ) case .Aggregate: item, ok := types.node(&emitter.module.types, instruction.type) expected_count := 0 if ok && item.kind == .Array { expected_count = int(item.count) } else if ok && item.kind == .Struct { expected_count = int(item.field_count) } else { emit_recovery_value(emitter, instruction_index, instruction, "invalid aggregate type") continue } if len(instruction.args) != expected_count { emit_recovery_value(emitter, instruction_index, instruction, "invalid aggregate operands") continue } type_name := llvm_type(instruction.type, &emitter.module.types) total := len(instruction.args) + (1 if item.kind == .Array && item.has_sentinel else 0) if total == 0 { fmt.sbprintf(&emitter.builder, " %%v%d = freeze %s zeroinitializer\n", instruction_index, type_name) continue } for arg_index := 0; arg_index < total; arg_index += 1 { element_type := item.child if item.kind == .Struct { element_type = types.fields_for(&emitter.module.types, instruction.type)[arg_index].type } final := arg_index == total-1 if final { fmt.sbprintf(&emitter.builder, " %%v%d = insertvalue %s ", instruction_index, type_name) } else { fmt.sbprintf(&emitter.builder, " %%aggregate%d_%d = insertvalue %s ", instruction_index, arg_index, type_name) } if arg_index == 0 { strings.write_string(&emitter.builder, "poison") } else { fmt.sbprintf(&emitter.builder, "%%aggregate%d_%d", instruction_index, arg_index-1) } fmt.sbprintf(&emitter.builder, ", %s ", llvm_type(element_type, &emitter.module.types)) if arg_index < len(instruction.args) { write_operand(&emitter.builder, instructions, instruction.args[arg_index], element_type, &emitter.module.types) } else { write_constant(&emitter.builder, i64(item.sentinel), element_type, &emitter.module.types) } fmt.sbprintf(&emitter.builder, ", %d\n", arg_index) } case .None: item, ok := types.node(&emitter.module.types, instruction.type) if !ok || item.kind != .Optional { emit_recovery_value(emitter, instruction_index, instruction, "invalid optional none") continue } if types.is_pointer(item.child, &emitter.module.types) { fmt.sbprintf(&emitter.builder, " %%v%d = select i1 true, ptr null, ptr null\n", instruction_index) } else { fmt.sbprintf( &emitter.builder, " %%v%d = insertvalue %s zeroinitializer, i1 false, 0\n", instruction_index, llvm_type(instruction.type, &emitter.module.types), ) } case .Optional_Some: item, ok := types.node(&emitter.module.types, instruction.type) if !ok || item.kind != .Optional || !valid_value(instructions, instruction.a, item.child, &emitter.module.types) { emit_recovery_value(emitter, instruction_index, instruction, "invalid optional value") continue } if types.is_pointer(item.child, &emitter.module.types) { fmt.sbprintf(&emitter.builder, " %%v%d = select i1 true, ptr ", instruction_index) write_operand(&emitter.builder, instructions, instruction.a, item.child, &emitter.module.types) strings.write_string(&emitter.builder, ", ptr null\n") } else { type_name := llvm_type(instruction.type, &emitter.module.types) fmt.sbprintf(&emitter.builder, " %%optional%d = insertvalue %s poison, i1 true, 0\n", instruction_index, type_name) fmt.sbprintf(&emitter.builder, " %%v%d = insertvalue %s %%optional%d, %s ", instruction_index, type_name, instruction_index, llvm_type(item.child, &emitter.module.types)) write_operand(&emitter.builder, instructions, instruction.a, item.child, &emitter.module.types) strings.write_string(&emitter.builder, ", 1\n") } case .Load_Global: global_id := ir.as_global(instruction.target) if global_id == ir.INVALID_GLOBAL || int(global_id) >= len(emitter.module.globals) { emit_recovery_value(emitter, instruction_index, instruction, "invalid global reference") continue } global := emitter.module.globals[global_id] if !types.equal(instruction.type, global.type) { emit_recovery_value(emitter, instruction_index, instruction, "invalid global reference type") continue } if global.is_static { fmt.sbprintf( &emitter.builder, " %%v%d = load %s, ptr @bro.g.%d\n", instruction_index, llvm_type(global.type, &emitter.module.types), global_id, ) } else { fmt.sbprintf( &emitter.builder, " %%v%d = call %s @bro.get.%d()\n", instruction_index, llvm_type(global.type, &emitter.module.types), global_id, ) } case .Address_Global: global_id := ir.as_global(instruction.target) if global_id == ir.INVALID_GLOBAL || int(global_id) >= len(emitter.module.globals) || !types.equal(instruction.type, emitter.module.globals[global_id].type) { emit_recovery_value(emitter, instruction_index, instruction, "invalid global address") continue } fmt.sbprintf( &emitter.builder, " %%v%d = getelementptr %s, ptr @bro.g.%d, i64 0\n", instruction_index, llvm_type(instruction.type, &emitter.module.types), global_id, ) case .Address_Of: child := types.child_type(instruction.type, &emitter.module.types) if !types.is_pointer(instruction.type, &emitter.module.types) || !valid_address(instructions, instruction.a, child, &emitter.module.types) { emit_recovery_value(emitter, instruction_index, instruction, "invalid address operand") continue } fmt.sbprintf( &emitter.builder, " %%v%d = getelementptr %s, ptr %%v%d, i64 0\n", instruction_index, llvm_type(child, &emitter.module.types), instruction.a, ) case .Alloca: if !types.is_runtime_value(instruction.type, &emitter.module.types) { emit_recovery_value(emitter, instruction_index, instruction, "invalid allocation type") continue } fmt.sbprintf(&emitter.builder, " %%v%d = alloca %s\n", instruction_index, llvm_type(instruction.type, &emitter.module.types)) case .Index_Address: if !valid_instruction(instructions, instruction.a) || !valid_value(instructions, instruction.b, types.USIZE, &emitter.module.types) { emit_recovery_value(emitter, instruction_index, instruction, "invalid index operands") continue } container := instructions[instruction.a] container_node, container_ok := types.container(container.type, &emitter.module.types) if !container_ok { emit_recovery_value(emitter, instruction_index, instruction, "invalid index container") continue } pointer_name := fmt.tprintf("%%v%d", instruction.a) length: u64 bounded := false if container_node.kind == .Array { length = container_node.count if container_node.has_sentinel && instruction.integer != 0 { length += 1 } bounded = true } else if container_node.kind == .Slice { fmt.sbprintf(&emitter.builder, " %%index_ptr%d = extractvalue %s %%v%d, 0\n", instruction_index, llvm_type(container.type, &emitter.module.types), instruction.a) fmt.sbprintf(&emitter.builder, " %%index_len%d = extractvalue %s %%v%d, 1\n", instruction_index, llvm_type(container.type, &emitter.module.types), instruction.a) pointer_name = fmt.tprintf("%%index_ptr%d", instruction_index) comparison := "ule" if container_node.has_sentinel && instruction.integer != 0 else "ult" fmt.sbprintf(&emitter.builder, " %%index_ok%d = icmp %s i64 ", instruction_index, comparison) write_operand(&emitter.builder, instructions, instruction.b, types.USIZE, &emitter.module.types) fmt.sbprintf(&emitter.builder, ", %%index_len%d\n", instruction_index) bounded = true } else if container_node.kind != .Pointer || !container_node.many { emit_recovery_value(emitter, instruction_index, instruction, "invalid index container") continue } if bounded { if container_node.kind == .Array { fmt.sbprintf(&emitter.builder, " %%index_ok%d = icmp ult i64 ", instruction_index) write_operand(&emitter.builder, instructions, instruction.b, types.USIZE, &emitter.module.types) fmt.sbprintf(&emitter.builder, ", %d\n", length) } fmt.sbprintf(&emitter.builder, " br i1 %%index_ok%d, label %%index_continue%d, label %%index_trap%d\nindex_trap%d:\n", instruction_index, instruction_index, instruction_index, instruction_index) message := diagnostic_message(emitter, source.INVALID_DIAGNOSTIC, instruction.span, "index out of bounds") emit_trap_call(emitter, message) fmt.sbprintf(&emitter.builder, " unreachable\nindex_continue%d:\n", instruction_index) } if container_node.kind == .Array { array_type := container.type _, array, array_pointer_ok := types.array_pointer(container.type, &emitter.module.types) if array_pointer_ok { array_type = types.child_type(container.type, &emitter.module.types) container_node = array } fmt.sbprintf(&emitter.builder, " %%v%d = getelementptr %s, ptr %s, i64 0, i64 ", instruction_index, llvm_type(array_type, &emitter.module.types), pointer_name) } else { fmt.sbprintf(&emitter.builder, " %%v%d = getelementptr %s, ptr %s, i64 ", instruction_index, llvm_type(instruction.type, &emitter.module.types), pointer_name) } write_operand(&emitter.builder, instructions, instruction.b, types.USIZE, &emitter.module.types) strings.write_string(&emitter.builder, "\n") case .Field_Address: if !valid_instruction(instructions, instruction.a) { emit_recovery_value(emitter, instruction_index, instruction, "invalid field base") continue } base_type := instructions[instruction.a].type if types.is_pointer(base_type, &emitter.module.types) { base_type = types.child_type(base_type, &emitter.module.types) } fields := types.fields_for(&emitter.module.types, base_type) field_index := int(instruction.integer) if field_index < 0 || field_index >= len(fields) || !types.equal(fields[field_index].type, instruction.type) { emit_recovery_value(emitter, instruction_index, instruction, "invalid field reference") continue } fmt.sbprintf( &emitter.builder, " %%v%d = getelementptr %s, ptr %%v%d, i32 0, i32 %d\n", instruction_index, llvm_type(base_type, &emitter.module.types), instruction.a, field_index, ) case .Load: if !valid_address(instructions, instruction.a, instruction.type, &emitter.module.types) { emit_recovery_value(emitter, instruction_index, instruction, "invalid load slot") continue } fmt.sbprintf( &emitter.builder, " %%v%d = load %s, ptr %%v%d\n", instruction_index, llvm_type(instruction.type, &emitter.module.types), instruction.a, ) case .Store: if !valid_address(instructions, instruction.a, instruction.type, &emitter.module.types) || !valid_value(instructions, instruction.b, instruction.type, &emitter.module.types) { emit_recovery_value(emitter, instruction_index, instruction, "invalid store operand") continue } fmt.sbprintf(&emitter.builder, " store %s ", llvm_type(instruction.type, &emitter.module.types)) write_operand(&emitter.builder, instructions, instruction.b, instruction.type, &emitter.module.types) fmt.sbprintf(&emitter.builder, ", ptr %%v%d\n", instruction.a) case .Slice: if !valid_instruction(instructions, instruction.a) { emit_recovery_value(emitter, instruction_index, instruction, "invalid slice container") continue } container := instructions[instruction.a] item, ok := types.container(container.type, &emitter.module.types) if !ok || (item.kind != .Array && item.kind != .Slice) { emit_recovery_value(emitter, instruction_index, instruction, "invalid slice container") continue } pointer_name := fmt.tprintf("%%v%d", instruction.a) length_name := fmt.tprintf("%d", item.count) if item.kind == .Array { array_type := container.type if types.is_pointer(container.type, &emitter.module.types) { array_type = types.child_type(container.type, &emitter.module.types) } fmt.sbprintf(&emitter.builder, " %%slice_ptr%d = getelementptr %s, ptr %%v%d, i64 0, i64 0\n", instruction_index, llvm_type(array_type, &emitter.module.types), instruction.a) pointer_name = fmt.tprintf("%%slice_ptr%d", instruction_index) } else if item.kind == .Slice { fmt.sbprintf(&emitter.builder, " %%slice_ptr%d = extractvalue %s %%v%d, 0\n", instruction_index, llvm_type(container.type, &emitter.module.types), instruction.a) fmt.sbprintf(&emitter.builder, " %%slice_len%d = extractvalue %s %%v%d, 1\n", instruction_index, llvm_type(container.type, &emitter.module.types), instruction.a) pointer_name = fmt.tprintf("%%slice_ptr%d", instruction_index) length_name = fmt.tprintf("%%slice_len%d", instruction_index) } fmt.sbprintf(&emitter.builder, " %%slice_bound_start%d = add i64 0, ", instruction_index) if len(instruction.args) > 0 && instruction.args[0] != ir.INVALID_INSTRUCTION { write_operand(&emitter.builder, instructions, instruction.args[0], types.USIZE, &emitter.module.types) } else { strings.write_string(&emitter.builder, "0") } strings.write_string(&emitter.builder, "\n") fmt.sbprintf(&emitter.builder, " %%slice_bound_end%d = add i64 0, ", instruction_index) if len(instruction.args) > 1 && instruction.args[1] != ir.INVALID_INSTRUCTION { write_operand(&emitter.builder, instructions, instruction.args[1], types.USIZE, &emitter.module.types) } else { strings.write_string(&emitter.builder, length_name) } strings.write_string(&emitter.builder, "\n") start_name := fmt.tprintf("%%slice_bound_start%d", instruction_index) end_name := fmt.tprintf("%%slice_bound_end%d", instruction_index) fmt.sbprintf(&emitter.builder, " %%slice_order%d = icmp ule i64 %s, %s\n", instruction_index, start_name, end_name) fmt.sbprintf(&emitter.builder, " %%slice_end_ok%d = icmp ule i64 %s, %s\n", instruction_index, end_name, length_name) fmt.sbprintf(&emitter.builder, " %%slice_ok%d = and i1 %%slice_order%d, %%slice_end_ok%d\n", instruction_index, instruction_index, instruction_index) fmt.sbprintf(&emitter.builder, " br i1 %%slice_ok%d, label %%slice_continue%d, label %%slice_trap%d\nslice_trap%d:\n", instruction_index, instruction_index, instruction_index, instruction_index) message := diagnostic_message(emitter, source.INVALID_DIAGNOSTIC, instruction.span, "slice bounds out of range") emit_trap_call(emitter, message) fmt.sbprintf(&emitter.builder, " unreachable\nslice_continue%d:\n", instruction_index) fmt.sbprintf(&emitter.builder, " %%slice_start%d = getelementptr %s, ptr %s, i64 %s\n", instruction_index, llvm_type(item.child, &emitter.module.types), pointer_name, start_name) fmt.sbprintf(&emitter.builder, " %%slice_result%d = insertvalue %s poison, ptr %%slice_start%d, 0\n", instruction_index, llvm_type(instruction.type, &emitter.module.types), instruction_index) fmt.sbprintf(&emitter.builder, " %%slice_result_len%d = sub i64 %s, %s\n", instruction_index, end_name, start_name) fmt.sbprintf(&emitter.builder, " %%v%d = insertvalue %s %%slice_result%d, i64 %%slice_result_len%d, 1\n", instruction_index, llvm_type(instruction.type, &emitter.module.types), instruction_index, instruction_index) case .Length: if !valid_instruction(instructions, instruction.a) || !types.is_slice(instructions[instruction.a].type, &emitter.module.types) { emit_recovery_value(emitter, instruction_index, instruction, "invalid slice length") continue } fmt.sbprintf( &emitter.builder, " %%v%d = extractvalue %s %%v%d, 1\n", instruction_index, llvm_type(instructions[instruction.a].type, &emitter.module.types), instruction.a, ) case .Slice_Ptr: if !valid_instruction(instructions, instruction.a) || !types.is_pointer(instruction.type, &emitter.module.types) { emit_recovery_value(emitter, instruction_index, instruction, "invalid container pointer") continue } container_type := instructions[instruction.a].type _, _, array_pointer_ok := types.array_pointer(container_type, &emitter.module.types) if types.is_array(container_type, &emitter.module.types) { fmt.sbprintf( &emitter.builder, " %%v%d = getelementptr %s, ptr %%v%d, i64 0, i64 0\n", instruction_index, llvm_type(container_type, &emitter.module.types), instruction.a, ) } else if array_pointer_ok { fmt.sbprintf( &emitter.builder, " %%v%d = getelementptr %s, ptr %%v%d, i64 0, i64 0\n", instruction_index, llvm_type(types.child_type(container_type, &emitter.module.types), &emitter.module.types), instruction.a, ) } else if types.is_slice(container_type, &emitter.module.types) { fmt.sbprintf( &emitter.builder, " %%v%d = extractvalue %s %%v%d, 0\n", instruction_index, llvm_type(container_type, &emitter.module.types), instruction.a, ) } else { emit_recovery_value(emitter, instruction_index, instruction, "invalid container pointer") } case .Unwrap: optional_type := instructions[instruction.a].type if valid_instruction(instructions, instruction.a) else types.INVALID item, ok := types.node(&emitter.module.types, optional_type) if !ok || item.kind != .Optional || !types.equal(item.child, instruction.type) { emit_recovery_value(emitter, instruction_index, instruction, "invalid optional unwrap") continue } if types.is_pointer(item.child, &emitter.module.types) { fmt.sbprintf(&emitter.builder, " %%optional_ok%d = icmp ne ptr %%v%d, null\n", instruction_index, instruction.a) } else { fmt.sbprintf(&emitter.builder, " %%optional_ok%d = extractvalue %s %%v%d, 0\n", instruction_index, llvm_type(optional_type, &emitter.module.types), instruction.a) } fmt.sbprintf(&emitter.builder, " br i1 %%optional_ok%d, label %%optional_continue%d, label %%optional_trap%d\noptional_trap%d:\n", instruction_index, instruction_index, instruction_index, instruction_index) message := diagnostic_message(emitter, source.INVALID_DIAGNOSTIC, instruction.span, "attempted to unwrap none") emit_trap_call(emitter, message) fmt.sbprintf(&emitter.builder, " unreachable\noptional_continue%d:\n", instruction_index) if types.is_pointer(item.child, &emitter.module.types) { fmt.sbprintf(&emitter.builder, " %%v%d = select i1 true, ptr %%v%d, ptr null\n", instruction_index, instruction.a) } else { fmt.sbprintf(&emitter.builder, " %%v%d = extractvalue %s %%v%d, 1\n", instruction_index, llvm_type(optional_type, &emitter.module.types), instruction.a) } case .Orelse_Begin: optional_type := instructions[instruction.a].type if valid_instruction(instructions, instruction.a) else types.INVALID item, ok := types.node(&emitter.module.types, optional_type) if !ok || item.kind != .Optional || !types.equal(item.child, instruction.type) || !valid_value(instructions, instruction.a, optional_type, &emitter.module.types) { emit_recovery_value(emitter, instruction_index, instruction, "invalid optional fallback") continue } fmt.sbprintf(&emitter.builder, " %%orelse_slot%d = alloca %s\n", instruction_index, llvm_type(instruction.type, &emitter.module.types)) if types.is_pointer(item.child, &emitter.module.types) { fmt.sbprintf(&emitter.builder, " %%orelse_ok%d = icmp ne ptr %%v%d, null\n", instruction_index, instruction.a) } else { fmt.sbprintf(&emitter.builder, " %%orelse_ok%d = extractvalue %s %%v%d, 0\n", instruction_index, llvm_type(optional_type, &emitter.module.types), instruction.a) } fmt.sbprintf( &emitter.builder, " br i1 %%orelse_ok%d, label %%orelse_some%d, label %%orelse_fallback%d\norelse_fallback%d:\n", instruction_index, instruction_index, instruction_index, instruction_index, ) case .Orelse: begin := instructions[instruction.a] if valid_instruction(instructions, instruction.a) else ir.Instruction{} optional_type := instructions[begin.a].type if valid_instruction(instructions, begin.a) else types.INVALID item, ok := types.node(&emitter.module.types, optional_type) if begin.op != .Orelse_Begin || !ok || item.kind != .Optional || !types.equal(item.child, instruction.type) || !valid_value(instructions, instruction.b, instruction.type, &emitter.module.types) { emit_recovery_value(emitter, instruction_index, instruction, "invalid optional fallback") continue } type_name := llvm_type(instruction.type, &emitter.module.types) fmt.sbprintf(&emitter.builder, " store %s ", type_name) write_operand(&emitter.builder, instructions, instruction.b, instruction.type, &emitter.module.types) fmt.sbprintf(&emitter.builder, ", ptr %%orelse_slot%d\n", instruction.a) fmt.sbprintf(&emitter.builder, " br label %%orelse_merge%d\norelse_some%d:\n", instruction.a, instruction.a) if types.is_pointer(item.child, &emitter.module.types) { fmt.sbprintf(&emitter.builder, " store ptr %%v%d, ptr %%orelse_slot%d\n", begin.a, instruction.a) } else { fmt.sbprintf(&emitter.builder, " %%orelse_value%d = extractvalue %s %%v%d, 1\n", instruction.a, llvm_type(optional_type, &emitter.module.types), begin.a) fmt.sbprintf(&emitter.builder, " store %s %%orelse_value%d, ptr %%orelse_slot%d\n", type_name, instruction.a, instruction.a) } fmt.sbprintf(&emitter.builder, " br label %%orelse_merge%d\norelse_merge%d:\n", instruction.a, instruction.a) fmt.sbprintf(&emitter.builder, " %%v%d = load %s, ptr %%orelse_slot%d\n", instruction_index, type_name, instruction.a) case .Widen: if !valid_instruction(instructions, instruction.a) || !types.can_widen(instructions[instruction.a].type, instruction.type) { emit_recovery_value(emitter, instruction_index, instruction, "invalid widening operand") continue } from_type := instructions[instruction.a].type operation := "fpext" if types.is_float(from_type, emitter.module.target) else ("sext" if types.is_signed(from_type, emitter.module.target) else "zext") fmt.sbprintf(&emitter.builder, " %%v%d = %s %s ", instruction_index, operation, llvm_type(from_type, &emitter.module.types)) write_operand(&emitter.builder, instructions, instruction.a, from_type, &emitter.module.types) fmt.sbprintf(&emitter.builder, " to %s\n", llvm_type(instruction.type, &emitter.module.types)) case .C_Vararg_Promote: if !valid_instruction(instructions, instruction.a) { emit_recovery_value(emitter, instruction_index, instruction, "invalid C variadic promotion operand") continue } from_type := instructions[instruction.a].type if types.equal(from_type, instruction.type) || !types.equal(types.c_vararg_promotion(from_type, emitter.module.target), instruction.type) { emit_recovery_value(emitter, instruction_index, instruction, "invalid C variadic promotion operand") continue } if types.bits(from_type, emitter.module.target) == types.bits(instruction.type, emitter.module.target) { type_name := llvm_type(instruction.type, &emitter.module.types) fmt.sbprintf(&emitter.builder, " %%v%d = select i1 true, %s ", instruction_index, type_name) write_operand(&emitter.builder, instructions, instruction.a, from_type, &emitter.module.types) fmt.sbprintf(&emitter.builder, ", %s ", type_name) write_operand(&emitter.builder, instructions, instruction.a, from_type, &emitter.module.types) strings.write_string(&emitter.builder, "\n") continue } operation := "fpext" if types.is_float(from_type, emitter.module.target) else ("sext" if types.is_signed(from_type, emitter.module.target) else "zext") fmt.sbprintf(&emitter.builder, " %%v%d = %s %s ", instruction_index, operation, llvm_type(from_type, &emitter.module.types)) write_operand(&emitter.builder, instructions, instruction.a, from_type, &emitter.module.types) fmt.sbprintf(&emitter.builder, " to %s\n", llvm_type(instruction.type, &emitter.module.types)) case .Weaken_Pointer: if !valid_instruction(instructions, instruction.a) || !types.can_weaken_pointer(instructions[instruction.a].type, instruction.type, &emitter.module.types) { emit_recovery_value(emitter, instruction_index, instruction, "invalid pointer weakening operand") continue } fmt.sbprintf(&emitter.builder, " %%v%d = select i1 true, ptr %%v%d, ptr null\n", instruction_index, instruction.a) case .Weaken_Slice: if !valid_instruction(instructions, instruction.a) || !types.can_weaken_slice(instructions[instruction.a].type, instruction.type, &emitter.module.types) { emit_recovery_value(emitter, instruction_index, instruction, "invalid slice weakening operand") continue } type_name := llvm_type(instruction.type, &emitter.module.types) fmt.sbprintf(&emitter.builder, " %%v%d = select i1 true, %s %%v%d, %s zeroinitializer\n", instruction_index, type_name, instruction.a, type_name) case .Decay_Array_Pointer: if !valid_instruction(instructions, instruction.a) || !types.can_decay_array_pointer(instructions[instruction.a].type, instruction.type, &emitter.module.types) { emit_recovery_value(emitter, instruction_index, instruction, "invalid array pointer decay operand") continue } array_type := types.child_type(instructions[instruction.a].type, &emitter.module.types) array, _ := types.node(&emitter.module.types, array_type) if types.is_pointer(instruction.type, &emitter.module.types) { fmt.sbprintf(&emitter.builder, " %%v%d = getelementptr %s, ptr %%v%d, i64 0, i64 0\n", instruction_index, llvm_type(array_type, &emitter.module.types), instruction.a) } else { type_name := llvm_type(instruction.type, &emitter.module.types) fmt.sbprintf(&emitter.builder, " %%decay_ptr%d = getelementptr %s, ptr %%v%d, i64 0, i64 0\n", instruction_index, llvm_type(array_type, &emitter.module.types), instruction.a) fmt.sbprintf(&emitter.builder, " %%decay_slice%d = insertvalue %s poison, ptr %%decay_ptr%d, 0\n", instruction_index, type_name, instruction_index) fmt.sbprintf(&emitter.builder, " %%v%d = insertvalue %s %%decay_slice%d, i64 %d, 1\n", instruction_index, type_name, instruction_index, array.count) } case .Neg_Checked: if !valid_value(instructions, instruction.a, instruction.type, &emitter.module.types) { emit_recovery_value(emitter, instruction_index, instruction, "invalid negation operand") continue } type_name := llvm_type(instruction.type, &emitter.module.types) if types.is_float(instruction.type, emitter.module.target) { fmt.sbprintf(&emitter.builder, " %%v%d = fneg %s ", instruction_index, type_name) write_operand(&emitter.builder, instructions, instruction.a, instruction.type, &emitter.module.types) strings.write_string(&emitter.builder, "\n") continue } fmt.sbprintf(&emitter.builder, " %%pair%d = call ", instruction_index) strings.write_string(&emitter.builder, "{ ") fmt.sbprintf(&emitter.builder, "%s, i1 } @llvm.ssub.with.overflow.%s(%s 0, %s ", type_name, type_name, type_name, type_name) write_operand(&emitter.builder, instructions, instruction.a, instruction.type, &emitter.module.types) fmt.sbprintf(&emitter.builder, ")\n") fmt.sbprintf(&emitter.builder, " %%v%d = extractvalue ", instruction_index) strings.write_string(&emitter.builder, "{ ") fmt.sbprintf(&emitter.builder, "%s, i1 } %%pair%d, 0\n", type_name, instruction_index) fmt.sbprintf(&emitter.builder, " %%overflow%d = extractvalue ", instruction_index) strings.write_string(&emitter.builder, "{ ") fmt.sbprintf(&emitter.builder, "%s, i1 } %%pair%d, 1\n", type_name, instruction_index) fmt.sbprintf( &emitter.builder, " br i1 %%overflow%d, label %%overflow_trap%d, label %%overflow_continue%d\n", instruction_index, instruction_index, instruction_index, ) fmt.sbprintf(&emitter.builder, "overflow_trap%d:\n", instruction_index) message := diagnostic_message(emitter, source.INVALID_DIAGNOSTIC, instruction.span, "signed integer negation overflow") emit_trap_call(emitter, message) fmt.sbprintf(&emitter.builder, " unreachable\noverflow_continue%d:\n", instruction_index) case .Add_Checked: if !valid_value(instructions, instruction.a, instruction.type, &emitter.module.types) || !valid_value(instructions, instruction.b, instruction.type, &emitter.module.types) { emit_recovery_value(emitter, instruction_index, instruction, "invalid addition operand") continue } type_name := llvm_type(instruction.type, &emitter.module.types) if types.is_float(instruction.type, emitter.module.target) { fmt.sbprintf(&emitter.builder, " %%v%d = fadd %s ", instruction_index, type_name) write_operand(&emitter.builder, instructions, instruction.a, instruction.type, &emitter.module.types) strings.write_string(&emitter.builder, ", ") write_operand(&emitter.builder, instructions, instruction.b, instruction.type, &emitter.module.types) strings.write_string(&emitter.builder, "\n") continue } intrinsic := "uadd" if types.is_unsigned(instruction.type, emitter.module.target) else "sadd" fmt.sbprintf(&emitter.builder, " %%pair%d = call ", instruction_index) strings.write_string(&emitter.builder, "{ ") fmt.sbprintf(&emitter.builder, "%s, i1 } @llvm.%s.with.overflow.%s(%s ", type_name, intrinsic, type_name, type_name) write_operand(&emitter.builder, instructions, instruction.a, instruction.type, &emitter.module.types) fmt.sbprintf(&emitter.builder, ", %s ", type_name) write_operand(&emitter.builder, instructions, instruction.b, instruction.type, &emitter.module.types) fmt.sbprintf(&emitter.builder, ")\n") fmt.sbprintf(&emitter.builder, " %%v%d = extractvalue ", instruction_index) strings.write_string(&emitter.builder, "{ ") fmt.sbprintf(&emitter.builder, "%s, i1 } %%pair%d, 0\n", type_name, instruction_index) fmt.sbprintf(&emitter.builder, " %%overflow%d = extractvalue ", instruction_index) strings.write_string(&emitter.builder, "{ ") fmt.sbprintf(&emitter.builder, "%s, i1 } %%pair%d, 1\n", type_name, instruction_index) fmt.sbprintf( &emitter.builder, " br i1 %%overflow%d, label %%overflow_trap%d, label %%overflow_continue%d\n", instruction_index, instruction_index, instruction_index, ) fmt.sbprintf(&emitter.builder, "overflow_trap%d:\n", instruction_index) message := diagnostic_message(emitter, source.INVALID_DIAGNOSTIC, instruction.span, "integer addition overflow") emit_trap_call(emitter, message) fmt.sbprintf(&emitter.builder, " unreachable\noverflow_continue%d:\n", instruction_index) case .Pointer_Add: item, ok := types.node(&emitter.module.types, instruction.type) if !ok || item.kind != .Pointer || !item.many || !valid_value(instructions, instruction.a, instruction.type, &emitter.module.types) || !valid_value(instructions, instruction.b, types.USIZE, &emitter.module.types) { emit_recovery_value(emitter, instruction_index, instruction, "invalid pointer offset") continue } fmt.sbprintf(&emitter.builder, " %%v%d = getelementptr %s, ptr %%v%d, i64 ", instruction_index, llvm_type(item.child, &emitter.module.types), instruction.a) write_operand(&emitter.builder, instructions, instruction.b, types.USIZE, &emitter.module.types) strings.write_string(&emitter.builder, "\n") case .Call: function_id := ir.as_function(instruction.target) if function_id == ir.INVALID_FUNCTION || int(function_id) >= len(emitter.module.functions) { emit_recovery_value(emitter, instruction_index, instruction, "invalid function specialization") continue } target := emitter.module.functions[function_id] valid_args := (len(instruction.args) >= len(target.param_types) if target.variadic else len(instruction.args) == len(target.param_types)) && (!target.variadic || target.calling_convention == .C) if valid_args { for arg, index in instruction.args { expected := target.param_types[index] if index < len(target.param_types) && valid_instruction(instructions, arg) else (instructions[arg].type if valid_instruction(instructions, arg) else types.INVALID) if index >= len(target.param_types) && (!types.is_c_vararg_type(expected, &emitter.module.types) || !types.equal(types.c_vararg_promotion(expected, emitter.module.target), expected)) { valid_args = false break } if !valid_value(instructions, arg, expected, &emitter.module.types) { valid_args = false break } } } target_result := target.result if target.is_main { target_result = types.I32 } if !valid_args || !types.equal(instruction.type, target_result) { emit_recovery_value(emitter, instruction_index, instruction, "invalid function call operands") continue } if !types.is_void(instruction.type) { fmt.sbprintf(&emitter.builder, " %%v%d = ", instruction_index) } else { strings.write_string(&emitter.builder, " ") } strings.write_string(&emitter.builder, "call ") if target.calling_convention == .Brolang { strings.write_string(&emitter.builder, "fastcc ") } emit_function_result(&emitter.builder, target, &emitter.module.types) if target.variadic { strings.write_string(&emitter.builder, " (") for param_type, index in target.param_types { if index > 0 { strings.write_string(&emitter.builder, ", ") } strings.write_string(&emitter.builder, llvm_type(param_type, &emitter.module.types)) } if len(target.param_types) > 0 { strings.write_string(&emitter.builder, ", ") } strings.write_string(&emitter.builder, "...)") } fmt.sbprintf(&emitter.builder, " @%s(", target.link_name) emit_call_args( &emitter.builder, instructions, instruction.args, target.param_types, &emitter.module.types, target.calling_convention == .C, ) strings.write_string(&emitter.builder, ")\n") case .Trap: message := diagnostic_message(emitter, instruction.diagnostic, instruction.span, "invalid recovered source") emit_trap_call(emitter, message) case .Return: if global_initializer { return_value = instruction.a continue } fmt.sbprintf(&emitter.builder, " ret %s ", function_result_type(function, &emitter.module.types)) write_operand(&emitter.builder, instructions, instruction.a, function.result, &emitter.module.types) strings.write_string(&emitter.builder, "\n") after_return = true case .Return_Void: if global_initializer { continue } if function.is_main { strings.write_string(&emitter.builder, " ret i32 0\n") } else { strings.write_string(&emitter.builder, " ret void\n") } after_return = true } } return return_value } emit_globals :: proc(emitter: ^Emitter) { for global, global_id in emitter.module.globals { if global.is_static { fmt.sbprintf( &emitter.builder, "@bro.g.%d = internal constant %s ", global_id, llvm_type(global.type, &emitter.module.types), ) write_constant(&emitter.builder, global.static_value, global.type, &emitter.module.types) strings.write_string(&emitter.builder, "\n") } else { fmt.sbprintf( &emitter.builder, "@bro.g.%d = internal global %s zeroinitializer\n@bro.gstate.%d = internal global i8 0\n", global_id, llvm_type(global.type, &emitter.module.types), global_id, ) } } strings.write_string(&emitter.builder, "\n") } emit_types :: proc(emitter: ^Emitter) { for item, index in emitter.module.types.nodes { if item.kind != .Struct { continue } id := types.DYNAMIC_START+types.Type(index) fmt.sbprintf(&emitter.builder, "%%bro.type.%d = type ", id) if item.opaque { strings.write_string(&emitter.builder, "opaque\n") continue } strings.write_string(&emitter.builder, "{ ") for field, field_index in types.fields_for(&emitter.module.types, id) { if field_index > 0 { strings.write_string(&emitter.builder, ", ") } strings.write_string(&emitter.builder, llvm_type(field.type, &emitter.module.types)) } strings.write_string(&emitter.builder, " }\n") } if len(emitter.module.types.nodes) > 0 { strings.write_string(&emitter.builder, "\n") } } emit_strings :: proc(emitter: ^Emitter) { for value, id in emitter.module.strings { fmt.sbprintf( &emitter.builder, "@bro.str.%d = private unnamed_addr constant [%d x i8] c\"", id, len(value)+1, ) emit_escaped_bytes(&emitter.builder, value) strings.write_string(&emitter.builder, "\\00\"\n") } if len(emitter.module.strings) > 0 { strings.write_string(&emitter.builder, "\n") } } emit_global_accessors :: proc(emitter: ^Emitter) { placeholder_function := ir.Function{result=types.I64} for global, global_id in emitter.module.globals { if global.is_static { continue } type_name := llvm_type(global.type, &emitter.module.types) fmt.sbprintf(&emitter.builder, "define internal %s @bro.get.%d() ", type_name, global_id) strings.write_string(&emitter.builder, "{\nentry:\n") fmt.sbprintf( &emitter.builder, " %%state = load i8, ptr @bro.gstate.%d\n %%done = icmp eq i8 %%state, 2\n br i1 %%done, label %%ready, label %%check\n", global_id, ) strings.write_string(&emitter.builder, "check:\n %visiting = icmp eq i8 %state, 1\n br i1 %visiting, label %cycle, label %initialize\ncycle:\n") message_text := fmt.aprintf( "runtime trap: global initialization cycle involving '%s'", symbol.resolve(emitter.symbols, global.name), allocator=emitter.allocator, ) message := register_message(emitter, message_text) delete(message_text, emitter.allocator) emit_trap_call(emitter, message) strings.write_string(&emitter.builder, " unreachable\ninitialize:\n") fmt.sbprintf(&emitter.builder, " store i8 1, ptr @bro.gstate.%d\n", global_id) placeholder_function.result = global.type value := emit_instruction_stream(emitter, global.initializer, placeholder_function, true) fmt.sbprintf(&emitter.builder, " store %s ", type_name) write_operand(&emitter.builder, global.initializer, value, global.type, &emitter.module.types) fmt.sbprintf(&emitter.builder, ", ptr @bro.g.%d\n", global_id) fmt.sbprintf(&emitter.builder, " store i8 2, ptr @bro.gstate.%d\n", global_id) fmt.sbprintf(&emitter.builder, " ret %s ", type_name) write_operand(&emitter.builder, global.initializer, value, global.type, &emitter.module.types) strings.write_string(&emitter.builder, "\nready:\n") fmt.sbprintf(&emitter.builder, " %%value = load %s, ptr @bro.g.%d\n ret %s %%value\n}\n\n", type_name, global_id, type_name) } } emit_constructor :: proc(emitter: ^Emitter) { count := 0 for global in emitter.module.globals { if !global.is_static && !global.problematic { count += 1 } } if count == 0 { return } strings.write_string( &emitter.builder, "@llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr @bro.init, ptr null }]\n\n", ) strings.write_string(&emitter.builder, "define internal void @bro.init() {\nentry:\n") for global, global_id in emitter.module.globals { if !global.is_static && !global.problematic { fmt.sbprintf(&emitter.builder, " %%g%d = call %s @bro.get.%d()\n", global_id, llvm_type(global.type, &emitter.module.types), global_id) } } strings.write_string(&emitter.builder, " ret void\n}\n\n") } emit_functions :: proc(emitter: ^Emitter) { for function, function_index in emitter.module.functions { if function.implementation == .Declaration { duplicate := false for previous in emitter.module.functions[:function_index] { if previous.link_name == function.link_name { duplicate = true break } } if duplicate { continue } } if function.implementation == .Declaration { strings.write_string(&emitter.builder, "declare ") } else { strings.write_string(&emitter.builder, "define ") if function.linkage == .Internal { strings.write_string(&emitter.builder, "internal ") } } if function.calling_convention == .Brolang { strings.write_string(&emitter.builder, "fastcc ") } emit_function_result(&emitter.builder, function, &emitter.module.types) fmt.sbprintf(&emitter.builder, " @%s(", function.link_name) for param_type, index in function.param_types { if index > 0 { strings.write_string(&emitter.builder, ", ") } if function.implementation == .Declaration { fmt.sbprintf(&emitter.builder, "%s", llvm_type(param_type, &emitter.module.types)) } else { fmt.sbprintf(&emitter.builder, "%s", llvm_type(param_type, &emitter.module.types)) } if function.calling_convention == .C { extension := c_abi_extension(param_type, emitter.module.target) if len(extension) > 0 { fmt.sbprintf(&emitter.builder, " %s", extension) } } if function.implementation != .Declaration { fmt.sbprintf(&emitter.builder, " %%v%d", index) } } if function.variadic { if len(function.param_types) > 0 { strings.write_string(&emitter.builder, ", ") } strings.write_string(&emitter.builder, "...") } if function.implementation == .Declaration { strings.write_string(&emitter.builder, ")\n\n") continue } strings.write_string(&emitter.builder, ") {\nentry:\n") _ = emit_instruction_stream(emitter, function.instructions, function) strings.write_string(&emitter.builder, "}\n\n") } } emit_escaped_bytes :: proc(builder: ^strings.Builder, text: string) { for value in transmute([]byte)text { if value >= 32 && value <= 126 && value != '\\' && value != '"' { strings.write_byte(builder, value) } else { fmt.sbprintf(builder, "\\%02X", value) } } } emit_messages :: proc(emitter: ^Emitter) { for message, message_id in emitter.messages { fmt.sbprintf(&emitter.builder, "@bro.msg.%d = private unnamed_addr constant [%d x i8] c\"", message_id, len(message.text)) emit_escaped_bytes(&emitter.builder, message.text) strings.write_string(&emitter.builder, "\"\n") } strings.write_string(&emitter.builder, "\n") } emit_declarations :: proc(emitter: ^Emitter) { strings.write_string(&emitter.builder, "declare i64 @write(i32, ptr, i64)\ndeclare void @llvm.trap()\n") widths := [?]int{8, 16, 32, 64} for bits in widths { strings.write_string(&emitter.builder, "declare { i") fmt.sbprintf(&emitter.builder, "%d", bits) strings.write_string(&emitter.builder, ", i1 } @llvm.sadd.with.overflow.i") fmt.sbprintf(&emitter.builder, "%d(i%d, i%d)\n", bits, bits, bits) strings.write_string(&emitter.builder, "declare { i") fmt.sbprintf(&emitter.builder, "%d", bits) strings.write_string(&emitter.builder, ", i1 } @llvm.uadd.with.overflow.i") fmt.sbprintf(&emitter.builder, "%d(i%d, i%d)\n", bits, bits, bits) strings.write_string(&emitter.builder, "declare { i") fmt.sbprintf(&emitter.builder, "%d", bits) strings.write_string(&emitter.builder, ", i1 } @llvm.ssub.with.overflow.i") fmt.sbprintf(&emitter.builder, "%d(i%d, i%d)\n", bits, bits, bits) } strings.write_string( &emitter.builder, "\ndefine internal void @bro.trap(ptr %message, i64 %length) noreturn {\nentry:\n %written = call i64 @write(i32 2, ptr %message, i64 %length)\n call void @llvm.trap()\n unreachable\n}\n\n", ) } emit :: proc( module: ^ir.Module, diagnostics: ^source.Diagnostics, symbols: ^symbol.Table, allocator := context.allocator, ) -> string { emitter := Emitter{ module=module, diagnostics=diagnostics, symbols=symbols, builder=strings.builder_make(allocator), allocator=allocator, } emitter.messages.allocator = allocator defer { for message in emitter.messages { delete(message.text, allocator) } delete(emitter.messages) strings.builder_destroy(&emitter.builder) } strings.write_string(&emitter.builder, "; generated by brolang\n") fmt.sbprintf(&emitter.builder, "target datalayout = \"%s\"\n", target.llvm_data_layout(module.target)) fmt.sbprintf(&emitter.builder, "target triple = \"%s\"\n\n", target.llvm_triple(module.target)) emit_types(&emitter) emit_strings(&emitter) emit_globals(&emitter) emit_constructor(&emitter) emit_global_accessors(&emitter) emit_functions(&emitter) emit_messages(&emitter) emit_declarations(&emitter) return fmt.aprintf("%s", strings.to_string(emitter.builder), allocator=allocator) }