function pointers and callbacks

This commit is contained in:
2026-06-15 21:09:46 +02:00
parent 3b7c3fcbd0
commit f5605fd3ec
21 changed files with 1927 additions and 122 deletions
+494 -30
View File
@@ -22,6 +22,118 @@ Emitter :: struct {
allocator: mem.Allocator,
}
C_Record_ABI_Kind :: enum u8 {
None,
Small_Integer,
Integer_Pair,
Homogeneous_Float,
Indirect,
}
C_Record_ABI :: struct {
kind: C_Record_ABI_Kind,
size: u64,
alignment: int,
float_type: types.Type,
float_count: int,
}
hfa_walk :: proc(value: types.Type, store: ^types.Store, scalar: ^types.Type, count: ^int, depth := 0) -> bool {
if depth > 64 || count^ > 4 {
return false
}
if types.is_float(value, store.selected) {
repr := types.representation(value, store.selected)
if scalar^ == types.INVALID {
scalar^ = repr
}
if scalar^ != repr {
return false
}
count^ += 1
return count^ <= 4
}
item, ok := types.node(store, value)
if !ok || item.kind == .Union {
return false
}
if item.kind == .Array {
for _ in 0..<int(item.count) {
if !hfa_walk(item.child, store, scalar, count, depth+1) {
return false
}
}
return true
}
if item.kind != .Struct {
return false
}
for field in types.fields_for(store, value) {
if !hfa_walk(field.type, store, scalar, count, depth+1) {
return false
}
}
return true
}
c_record_abi :: proc(value: types.Type, store: ^types.Store) -> C_Record_ABI {
if !types.is_record(value, store) {
return {}
}
result := C_Record_ABI{
size=types.size(value, store, store.selected),
alignment=types.alignment_of(value, store, store.selected),
}
scalar := types.INVALID
count := 0
if !types.is_union(value, store) && hfa_walk(value, store, &scalar, &count) && count > 0 {
result.kind = .Homogeneous_Float
result.float_type = scalar
result.float_count = count
return result
}
if result.size <= 8 {
result.kind = .Small_Integer
} else if result.size <= 16 {
result.kind = .Integer_Pair
} else {
result.kind = .Indirect
}
return result
}
c_abi_param_type :: proc(value: types.Type, store: ^types.Store) -> string {
abi := c_record_abi(value, store)
switch abi.kind {
case .None:
return llvm_type(value, store)
case .Small_Integer:
return "i64"
case .Integer_Pair:
return "[2 x i64]"
case .Homogeneous_Float:
return fmt.tprintf("[%d x %s]", abi.float_count, llvm_type(abi.float_type, store))
case .Indirect:
return "ptr"
}
return llvm_type(value, store)
}
c_abi_result_type :: proc(value: types.Type, store: ^types.Store) -> string {
abi := c_record_abi(value, store)
switch abi.kind {
case .None, .Homogeneous_Float:
return llvm_type(value, store)
case .Small_Integer:
return fmt.tprintf("i%d", abi.size*8)
case .Integer_Pair:
return "[2 x i64]"
case .Indirect:
return "void"
}
return llvm_type(value, store)
}
llvm_type :: proc(value: types.Type, store: ^types.Store = nil) -> string {
if types.is_void(value) {
return "void"
@@ -40,7 +152,7 @@ llvm_type :: proc(value: types.Type, store: ^types.Store = nil) -> string {
return "ptr"
}
return fmt.tprintf("{{ i1, %s }}", llvm_type(item.child, store))
case .Struct:
case .Struct, .Union:
return fmt.tprintf("%%bro.type.%d", value)
}
selected := store.selected if store != nil else target.DEFAULT
@@ -60,6 +172,9 @@ function_result_type :: proc(function: ir.Function, store: ^types.Store) -> stri
if function.is_main {
return "i32"
}
if function.calling_convention == .C {
return c_abi_result_type(function.result, store)
}
return llvm_type(function.result, store)
}
@@ -111,7 +226,7 @@ valid_value :: proc(
}
switch instructions[value_id].op {
case .Param, .Const, .String, .Aggregate, .None, .Optional_Some,
.Load_Global, .Address_Of, .Load, .Slice, .Length, .Slice_Ptr, .Unwrap, .Orelse,
.Load_Global, .Function_Address, .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
@@ -289,11 +404,70 @@ emit_call_args :: proc(
}
}
emit_pack_c_record_arg :: proc(
emitter: ^Emitter,
instructions: []ir.Instruction,
value: ir.Instruction_Id,
value_type: types.Type,
call_index, arg_index: int,
) -> string {
abi := c_record_abi(value_type, &emitter.module.types)
if abi.kind == .None {
return fmt.tprintf("%%v%d", value)
}
if abi.kind == .Indirect {
fmt.sbprintf(&emitter.builder, " %%abi_arg_slot%d_%d = alloca %s, align %d\n", call_index, arg_index, llvm_type(value_type, &emitter.module.types), abi.alignment)
fmt.sbprintf(&emitter.builder, " store %s ", llvm_type(value_type, &emitter.module.types))
write_operand(&emitter.builder, instructions, value, value_type, &emitter.module.types)
fmt.sbprintf(&emitter.builder, ", ptr %%abi_arg_slot%d_%d\n", call_index, arg_index)
return fmt.tprintf("%%abi_arg_slot%d_%d", call_index, arg_index)
}
abi_type := c_abi_param_type(value_type, &emitter.module.types)
temp_alignment := max(abi.alignment, 8)
fmt.sbprintf(&emitter.builder, " %%abi_arg_value_slot%d_%d = alloca %s, align %d\n", call_index, arg_index, llvm_type(value_type, &emitter.module.types), abi.alignment)
fmt.sbprintf(&emitter.builder, " store %s ", llvm_type(value_type, &emitter.module.types))
write_operand(&emitter.builder, instructions, value, value_type, &emitter.module.types)
fmt.sbprintf(&emitter.builder, ", ptr %%abi_arg_value_slot%d_%d\n", call_index, arg_index)
fmt.sbprintf(&emitter.builder, " %%abi_arg_slot%d_%d = alloca %s, align %d\n", call_index, arg_index, abi_type, temp_alignment)
fmt.sbprintf(&emitter.builder, " store %s zeroinitializer, ptr %%abi_arg_slot%d_%d\n", abi_type, call_index, arg_index)
fmt.sbprintf(
&emitter.builder,
" call void @llvm.memcpy.p0.p0.i64(ptr align %d %%abi_arg_slot%d_%d, ptr align %d %%abi_arg_value_slot%d_%d, i64 %d, i1 false)\n",
temp_alignment, call_index, arg_index, abi.alignment, call_index, arg_index, abi.size,
)
fmt.sbprintf(&emitter.builder, " %%abi_arg%d_%d = load %s, ptr %%abi_arg_slot%d_%d\n", call_index, arg_index, abi_type, call_index, arg_index)
return fmt.tprintf("%%abi_arg%d_%d", call_index, arg_index)
}
emit_unpack_c_record :: proc(
emitter: ^Emitter,
value_type: types.Type,
abi_type, abi_name, result_name: string,
tag: int,
) {
abi := c_record_abi(value_type, &emitter.module.types)
if abi.kind == .Indirect {
fmt.sbprintf(&emitter.builder, " %s = load %s, ptr %s\n", result_name, llvm_type(value_type, &emitter.module.types), abi_name)
return
}
temp_alignment := max(abi.alignment, 8)
fmt.sbprintf(&emitter.builder, " %%abi_unpack_source_slot%d = alloca %s, align %d\n", tag, abi_type, temp_alignment)
fmt.sbprintf(&emitter.builder, " store %s %s, ptr %%abi_unpack_source_slot%d\n", abi_type, abi_name, tag)
fmt.sbprintf(&emitter.builder, " %%abi_unpack_slot%d = alloca %s, align %d\n", tag, llvm_type(value_type, &emitter.module.types), abi.alignment)
fmt.sbprintf(
&emitter.builder,
" call void @llvm.memcpy.p0.p0.i64(ptr align %d %%abi_unpack_slot%d, ptr align %d %%abi_unpack_source_slot%d, i64 %d, i1 false)\n",
abi.alignment, tag, temp_alignment, tag, abi.size,
)
fmt.sbprintf(&emitter.builder, " %s = load %s, ptr %%abi_unpack_slot%d\n", result_name, llvm_type(value_type, &emitter.module.types), tag)
}
emit_instruction_stream :: proc(
emitter: ^Emitter,
instructions: []ir.Instruction,
function: ir.Function,
global_initializer := false,
sret_name := "",
) -> ir.Instruction_Id {
return_value := ir.INVALID_INSTRUCTION
after_return := false
@@ -326,6 +500,8 @@ emit_instruction_stream :: proc(
expected_count = int(item.count)
} else if ok && item.kind == .Struct {
expected_count = int(item.field_count)
} else if ok && item.kind == .Union {
expected_count = 1
} else {
emit_recovery_value(emitter, instruction_index, instruction, "invalid aggregate type")
continue
@@ -335,6 +511,22 @@ emit_instruction_stream :: proc(
continue
}
type_name := llvm_type(instruction.type, &emitter.module.types)
if item.kind == .Union {
fields := types.fields_for(&emitter.module.types, instruction.type)
field_index := int(instruction.integer)
if field_index < 0 || field_index >= len(fields) ||
!valid_value(instructions, instruction.args[0], fields[field_index].type, &emitter.module.types) {
emit_recovery_value(emitter, instruction_index, instruction, "invalid union aggregate operands")
continue
}
fmt.sbprintf(&emitter.builder, " %%union_slot%d = alloca %s, align %d\n", instruction_index, type_name, types.alignment_of(instruction.type, &emitter.module.types, emitter.module.target))
fmt.sbprintf(&emitter.builder, " store %s zeroinitializer, ptr %%union_slot%d\n", type_name, instruction_index)
fmt.sbprintf(&emitter.builder, " store %s ", llvm_type(fields[field_index].type, &emitter.module.types))
write_operand(&emitter.builder, instructions, instruction.args[0], fields[field_index].type, &emitter.module.types)
fmt.sbprintf(&emitter.builder, ", ptr %%union_slot%d\n", instruction_index)
fmt.sbprintf(&emitter.builder, " %%v%d = load %s, ptr %%union_slot%d\n", instruction_index, type_name, instruction_index)
continue
}
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)
@@ -425,6 +617,14 @@ emit_instruction_stream :: proc(
global_id,
)
}
case .Function_Address:
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 reference")
continue
}
target := emitter.module.functions[function_id]
fmt.sbprintf(&emitter.builder, " %%v%d = select i1 true, ptr @%s, ptr null\n", instruction_index, target.link_name)
case .Address_Global:
global_id := ir.as_global(instruction.target)
if global_id == ir.INVALID_GLOBAL || int(global_id) >= len(emitter.module.globals) ||
@@ -529,11 +729,15 @@ emit_instruction_stream :: proc(
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,
)
if types.is_union(base_type, &emitter.module.types) {
fmt.sbprintf(&emitter.builder, " %%v%d = getelementptr i8, ptr %%v%d, i64 0\n", instruction_index, instruction.a)
} else {
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")
@@ -868,7 +1072,138 @@ emit_instruction_stream :: proc(
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) {
if function_id == ir.INVALID_FUNCTION {
if !valid_instruction(instructions, instruction.a) ||
!valid_value(instructions, instruction.a, instructions[instruction.a].type, &emitter.module.types) {
emit_recovery_value(emitter, instruction_index, instruction, "invalid function pointer call target")
continue
}
callee := instructions[instruction.a]
_, function_item, function_type, ok := types.function_pointer(callee.type, &emitter.module.types)
if !ok {
emit_recovery_value(emitter, instruction_index, instruction, "invalid function pointer call target")
continue
}
param_fields := types.params_for(&emitter.module.types, function_type)
valid_args := (len(instruction.args) >= len(param_fields) if function_item.variadic else
len(instruction.args) == len(param_fields)) &&
(!function_item.variadic || function_item.c_abi)
if valid_args {
for arg, index in instruction.args {
expected := param_fields[index].type if index < len(param_fields) && valid_instruction(instructions, arg) else
(instructions[arg].type if valid_instruction(instructions, arg) else types.INVALID)
if index >= len(param_fields) &&
(!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
}
}
}
if !valid_args || !types.equal(instruction.type, function_item.child) {
emit_recovery_value(emitter, instruction_index, instruction, "invalid function pointer call operands")
continue
}
arg_names := make([]string, len(instruction.args), context.temp_allocator)
for arg, index in instruction.args {
if function_item.c_abi && index < len(param_fields) &&
types.is_record(param_fields[index].type, &emitter.module.types) {
arg_names[index] = emit_pack_c_record_arg(
emitter, instructions, arg, param_fields[index].type, instruction_index, index,
)
}
}
result_abi := C_Record_ABI{}
if function_item.c_abi {
result_abi = c_record_abi(function_item.child, &emitter.module.types)
}
if result_abi.kind == .Indirect {
fmt.sbprintf(&emitter.builder, " %%abi_result_slot%d = alloca %s, align %d\n", instruction_index, llvm_type(function_item.child, &emitter.module.types), result_abi.alignment)
strings.write_string(&emitter.builder, " ")
} else if result_abi.kind != .None && result_abi.kind != .Homogeneous_Float {
fmt.sbprintf(&emitter.builder, " %%abi_result%d = ", instruction_index)
} else 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 !function_item.c_abi {
strings.write_string(&emitter.builder, "fastcc ")
}
if function_item.c_abi {
extension := c_abi_extension(function_item.child, emitter.module.target)
if len(extension) > 0 {
fmt.sbprintf(&emitter.builder, "%s ", extension)
}
strings.write_string(&emitter.builder, c_abi_result_type(function_item.child, &emitter.module.types))
} else {
strings.write_string(&emitter.builder, llvm_type(function_item.child, &emitter.module.types))
}
if function_item.variadic {
strings.write_string(&emitter.builder, " (")
wrote_type := false
if result_abi.kind == .Indirect {
fmt.sbprintf(&emitter.builder, "ptr")
wrote_type = true
}
for param in param_fields {
if wrote_type {
strings.write_string(&emitter.builder, ", ")
}
strings.write_string(&emitter.builder, c_abi_param_type(param.type, &emitter.module.types))
wrote_type = true
}
if wrote_type {
strings.write_string(&emitter.builder, ", ")
}
strings.write_string(&emitter.builder, "...)")
}
strings.write_string(&emitter.builder, " ")
write_operand(&emitter.builder, instructions, instruction.a, callee.type, &emitter.module.types)
strings.write_string(&emitter.builder, "(")
wrote_arg := false
if result_abi.kind == .Indirect {
fmt.sbprintf(&emitter.builder, "ptr sret(%s) align %d %%abi_result_slot%d", llvm_type(function_item.child, &emitter.module.types), result_abi.alignment, instruction_index)
wrote_arg = true
}
for arg, index in instruction.args {
if wrote_arg {
strings.write_string(&emitter.builder, ", ")
}
arg_type := param_fields[index].type if index < len(param_fields) else instructions[arg].type
fixed := index < len(param_fields)
if function_item.c_abi && fixed && types.is_record(arg_type, &emitter.module.types) {
fmt.sbprintf(&emitter.builder, "%s %s", c_abi_param_type(arg_type, &emitter.module.types), arg_names[index])
} else {
fmt.sbprintf(&emitter.builder, "%s ", llvm_type(arg_type, &emitter.module.types))
if function_item.c_abi && fixed {
extension := c_abi_extension(arg_type, emitter.module.target)
if len(extension) > 0 {
fmt.sbprintf(&emitter.builder, "%s ", extension)
}
}
write_operand(&emitter.builder, instructions, arg, arg_type, &emitter.module.types)
}
wrote_arg = true
}
strings.write_string(&emitter.builder, ")\n")
if result_abi.kind == .Indirect {
fmt.sbprintf(&emitter.builder, " %%v%d = load %s, ptr %%abi_result_slot%d\n", instruction_index, llvm_type(function_item.child, &emitter.module.types), instruction_index)
} else if result_abi.kind != .None && result_abi.kind != .Homogeneous_Float {
emit_unpack_c_record(
emitter, function_item.child, c_abi_result_type(function_item.child, &emitter.module.types),
fmt.tprintf("%%abi_result%d", instruction_index), fmt.tprintf("%%v%d", instruction_index),
100000+instruction_index,
)
}
continue
}
if int(function_id) >= len(emitter.module.functions) {
emit_recovery_value(emitter, instruction_index, instruction, "invalid function specialization")
continue
}
@@ -900,7 +1235,25 @@ emit_instruction_stream :: proc(
emit_recovery_value(emitter, instruction_index, instruction, "invalid function call operands")
continue
}
if !types.is_void(instruction.type) {
arg_names := make([]string, len(instruction.args), context.temp_allocator)
for arg, index in instruction.args {
if target.calling_convention == .C && index < len(target.param_types) &&
types.is_record(target.param_types[index], &emitter.module.types) {
arg_names[index] = emit_pack_c_record_arg(
emitter, instructions, arg, target.param_types[index], instruction_index, index,
)
}
}
result_abi := C_Record_ABI{}
if target.calling_convention == .C {
result_abi = c_record_abi(target.result, &emitter.module.types)
}
if result_abi.kind == .Indirect {
fmt.sbprintf(&emitter.builder, " %%abi_result_slot%d = alloca %s, align %d\n", instruction_index, llvm_type(target.result, &emitter.module.types), result_abi.alignment)
strings.write_string(&emitter.builder, " ")
} else if result_abi.kind != .None && result_abi.kind != .Homogeneous_Float {
fmt.sbprintf(&emitter.builder, " %%abi_result%d = ", instruction_index)
} else if !types.is_void(instruction.type) {
fmt.sbprintf(&emitter.builder, " %%v%d = ", instruction_index)
} else {
strings.write_string(&emitter.builder, " ")
@@ -912,23 +1265,59 @@ emit_instruction_stream :: proc(
emit_function_result(&emitter.builder, target, &emitter.module.types)
if target.variadic {
strings.write_string(&emitter.builder, " (")
wrote_type := false
if result_abi.kind == .Indirect {
fmt.sbprintf(&emitter.builder, "ptr")
wrote_type = true
}
for param_type, index in target.param_types {
if index > 0 {
if wrote_type || index > 0 {
strings.write_string(&emitter.builder, ", ")
}
strings.write_string(&emitter.builder, llvm_type(param_type, &emitter.module.types))
strings.write_string(&emitter.builder, c_abi_param_type(param_type, &emitter.module.types))
wrote_type = true
}
if len(target.param_types) > 0 {
if wrote_type {
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,
)
wrote_arg := false
if result_abi.kind == .Indirect {
fmt.sbprintf(&emitter.builder, "ptr sret(%s) align %d %%abi_result_slot%d", llvm_type(target.result, &emitter.module.types), result_abi.alignment, instruction_index)
wrote_arg = true
}
for arg, index in instruction.args {
if wrote_arg {
strings.write_string(&emitter.builder, ", ")
}
arg_type := target.param_types[index] if index < len(target.param_types) else instructions[arg].type
fixed := index < len(target.param_types)
if target.calling_convention == .C && fixed && types.is_record(arg_type, &emitter.module.types) {
fmt.sbprintf(&emitter.builder, "%s %s", c_abi_param_type(arg_type, &emitter.module.types), arg_names[index])
} else {
fmt.sbprintf(&emitter.builder, "%s ", llvm_type(arg_type, &emitter.module.types))
if target.calling_convention == .C && fixed {
extension := c_abi_extension(arg_type, emitter.module.target)
if len(extension) > 0 {
fmt.sbprintf(&emitter.builder, "%s ", extension)
}
}
write_operand(&emitter.builder, instructions, arg, arg_type, &emitter.module.types)
}
wrote_arg = true
}
strings.write_string(&emitter.builder, ")\n")
if result_abi.kind == .Indirect {
fmt.sbprintf(&emitter.builder, " %%v%d = load %s, ptr %%abi_result_slot%d\n", instruction_index, llvm_type(target.result, &emitter.module.types), instruction_index)
} else if result_abi.kind != .None && result_abi.kind != .Homogeneous_Float {
emit_unpack_c_record(
emitter, target.result, c_abi_result_type(target.result, &emitter.module.types),
fmt.tprintf("%%abi_result%d", instruction_index), fmt.tprintf("%%v%d", instruction_index),
100000+instruction_index,
)
}
case .Trap:
message := diagnostic_message(emitter, instruction.diagnostic, instruction.span, "invalid recovered source")
emit_trap_call(emitter, message)
@@ -937,9 +1326,31 @@ emit_instruction_stream :: proc(
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")
result_abi := c_record_abi(function.result, &emitter.module.types) if function.calling_convention == .C else C_Record_ABI{}
if result_abi.kind == .Indirect {
fmt.sbprintf(&emitter.builder, " store %s ", llvm_type(function.result, &emitter.module.types))
write_operand(&emitter.builder, instructions, instruction.a, function.result, &emitter.module.types)
fmt.sbprintf(&emitter.builder, ", ptr %s\n ret void\n", sret_name)
} else if result_abi.kind != .None && result_abi.kind != .Homogeneous_Float {
abi_type := c_abi_result_type(function.result, &emitter.module.types)
temp_alignment := max(result_abi.alignment, 8)
fmt.sbprintf(&emitter.builder, " %%abi_return_value_slot%d = alloca %s, align %d\n", instruction_index, llvm_type(function.result, &emitter.module.types), result_abi.alignment)
fmt.sbprintf(&emitter.builder, " store %s ", llvm_type(function.result, &emitter.module.types))
write_operand(&emitter.builder, instructions, instruction.a, function.result, &emitter.module.types)
fmt.sbprintf(&emitter.builder, ", ptr %%abi_return_value_slot%d\n", instruction_index)
fmt.sbprintf(&emitter.builder, " %%abi_return_slot%d = alloca %s, align %d\n", instruction_index, abi_type, temp_alignment)
fmt.sbprintf(&emitter.builder, " store %s zeroinitializer, ptr %%abi_return_slot%d\n", abi_type, instruction_index)
fmt.sbprintf(
&emitter.builder,
" call void @llvm.memcpy.p0.p0.i64(ptr align %d %%abi_return_slot%d, ptr align %d %%abi_return_value_slot%d, i64 %d, i1 false)\n",
temp_alignment, instruction_index, result_abi.alignment, instruction_index, result_abi.size,
)
fmt.sbprintf(&emitter.builder, " %%abi_return%d = load %s, ptr %%abi_return_slot%d\n ret %s %%abi_return%d\n", instruction_index, abi_type, instruction_index, abi_type, instruction_index)
} else {
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 {
@@ -982,7 +1393,7 @@ emit_globals :: proc(emitter: ^Emitter) {
emit_types :: proc(emitter: ^Emitter) {
for item, index in emitter.module.types.nodes {
if item.kind != .Struct {
if item.kind != .Struct && item.kind != .Union {
continue
}
id := types.DYNAMIC_START+types.Type(index)
@@ -991,6 +1402,34 @@ emit_types :: proc(emitter: ^Emitter) {
strings.write_string(&emitter.builder, "opaque\n")
continue
}
if item.kind == .Union {
fields := types.fields_for(&emitter.module.types, id)
carrier := types.INVALID
carrier_size: u64
carrier_alignment := 0
for field in fields {
field_alignment := types.alignment_of(field.type, &emitter.module.types, emitter.module.target)
field_size := types.size(field.type, &emitter.module.types, emitter.module.target)
if field_alignment > carrier_alignment ||
(field_alignment == carrier_alignment && field_size > carrier_size) {
carrier = field.type
carrier_size = field_size
carrier_alignment = field_alignment
}
}
total_size := types.size(id, &emitter.module.types, emitter.module.target)
if !types.is_valid(carrier) {
fmt.sbprintf(&emitter.builder, "[%d x i8]\n", total_size)
continue
}
strings.write_string(&emitter.builder, "{ ")
strings.write_string(&emitter.builder, llvm_type(carrier, &emitter.module.types))
if carrier_size < total_size {
fmt.sbprintf(&emitter.builder, ", [%d x i8]", total_size-carrier_size)
}
strings.write_string(&emitter.builder, " }\n")
continue
}
strings.write_string(&emitter.builder, "{ ")
for field, field_index in types.fields_for(&emitter.module.types, id) {
if field_index > 0 {
@@ -1108,24 +1547,38 @@ emit_functions :: proc(emitter: ^Emitter) {
}
emit_function_result(&emitter.builder, function, &emitter.module.types)
fmt.sbprintf(&emitter.builder, " @%s(", function.link_name)
result_abi := c_record_abi(function.result, &emitter.module.types) if function.calling_convention == .C else C_Record_ABI{}
wrote_param := false
if result_abi.kind == .Indirect {
fmt.sbprintf(
&emitter.builder, "ptr sret(%s) align %d",
llvm_type(function.result, &emitter.module.types), result_abi.alignment,
)
if function.implementation != .Declaration {
strings.write_string(&emitter.builder, " %abi_sret")
}
wrote_param = true
}
for param_type, index in function.param_types {
if index > 0 {
if wrote_param || 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 {
type_name := c_abi_param_type(param_type, &emitter.module.types) if function.calling_convention == .C else llvm_type(param_type, &emitter.module.types)
fmt.sbprintf(&emitter.builder, "%s", type_name)
if function.calling_convention == .C && !types.is_record(param_type, &emitter.module.types) {
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.calling_convention == .C && types.is_record(param_type, &emitter.module.types) {
fmt.sbprintf(&emitter.builder, " %%abi_p%d", index)
} else {
fmt.sbprintf(&emitter.builder, " %%v%d", index)
}
}
wrote_param = true
}
if function.variadic {
if len(function.param_types) > 0 {
@@ -1138,7 +1591,18 @@ emit_functions :: proc(emitter: ^Emitter) {
continue
}
strings.write_string(&emitter.builder, ") {\nentry:\n")
_ = emit_instruction_stream(emitter, function.instructions, function)
if function.calling_convention == .C {
for param_type, index in function.param_types {
if !types.is_record(param_type, &emitter.module.types) {
continue
}
emit_unpack_c_record(
emitter, param_type, c_abi_param_type(param_type, &emitter.module.types),
fmt.tprintf("%%abi_p%d", index), fmt.tprintf("%%v%d", index), 200000+index,
)
}
}
_ = emit_instruction_stream(emitter, function.instructions, function, sret_name="%abi_sret")
strings.write_string(&emitter.builder, "}\n\n")
}
}
@@ -1163,7 +1627,7 @@ emit_messages :: proc(emitter: ^Emitter) {
}
emit_declarations :: proc(emitter: ^Emitter) {
strings.write_string(&emitter.builder, "declare i64 @write(i32, ptr, i64)\ndeclare void @llvm.trap()\n")
strings.write_string(&emitter.builder, "declare i64 @write(i32, ptr, i64)\ndeclare void @llvm.trap()\ndeclare void @llvm.memcpy.p0.p0.i64(ptr, ptr, i64, i1 immarg)\n")
widths := [?]int{8, 16, 32, 64}
for bits in widths {
strings.write_string(&emitter.builder, "declare { i")