Basic support for new ABI experiment on Win64

This commit is contained in:
gingerBill
2022-11-23 16:25:09 +00:00
parent 0a0db23b17
commit 7ab591667a
5 changed files with 202 additions and 63 deletions
+90 -21
View File
@@ -1,3 +1,5 @@
#define ALLOW_SPLIT_MULTI_RETURNS true
enum lbArgKind { enum lbArgKind {
lbArg_Direct, lbArg_Direct,
lbArg_Indirect, lbArg_Indirect,
@@ -48,8 +50,16 @@ struct lbFunctionType {
ProcCallingConvention calling_convention; ProcCallingConvention calling_convention;
Array<lbArgType> args; Array<lbArgType> args;
lbArgType ret; lbArgType ret;
LLVMTypeRef multiple_return_original_type; // nullptr if not used
isize original_arg_count;
}; };
gbAllocator lb_function_type_args_allocator(void) {
return heap_allocator();
}
i64 llvm_align_formula(i64 off, i64 a) { i64 llvm_align_formula(i64 off, i64 a) {
return (off + a - 1) / a * a; return (off + a - 1) / a * a;
} }
@@ -100,7 +110,9 @@ LLVMTypeRef lb_function_type_to_llvm_raw(lbFunctionType *ft, bool is_var_arg) {
} }
args[arg_index++] = arg_type; args[arg_index++] = arg_type;
} else if (arg->kind == lbArg_Indirect) { } else if (arg->kind == lbArg_Indirect) {
GB_ASSERT(!lb_is_type_kind(arg->type, LLVMPointerTypeKind)); if (ft->multiple_return_original_type == nullptr || i < ft->original_arg_count) {
GB_ASSERT(!lb_is_type_kind(arg->type, LLVMPointerTypeKind));
}
args[arg_index++] = LLVMPointerType(arg->type, 0); args[arg_index++] = LLVMPointerType(arg->type, 0);
} else if (arg->kind == lbArg_Ignore) { } else if (arg->kind == lbArg_Ignore) {
// ignore // ignore
@@ -147,6 +159,13 @@ void lb_add_function_type_attributes(LLVMValueRef fn, lbFunctionType *ft, ProcCa
LLVMAddAttributeAtIndex(fn, arg_index+1, arg->align_attribute); LLVMAddAttributeAtIndex(fn, arg_index+1, arg->align_attribute);
} }
if (ft->multiple_return_original_type) {
if (ft->original_arg_count <= i) {
LLVMAddAttributeAtIndex(fn, arg_index+1, noalias_attr);
LLVMAddAttributeAtIndex(fn, arg_index+1, nonnull_attr);
}
}
arg_index++; arg_index++;
} }
@@ -307,7 +326,7 @@ i64 lb_alignof(LLVMTypeRef type) {
} }
#define LB_ABI_INFO(name) lbFunctionType *name(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count, LLVMTypeRef return_type, bool return_is_defined, ProcCallingConvention calling_convention) #define LB_ABI_INFO(name) lbFunctionType *name(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count, LLVMTypeRef return_type, bool return_is_defined, bool return_is_tuple, ProcCallingConvention calling_convention)
typedef LB_ABI_INFO(lbAbiInfoType); typedef LB_ABI_INFO(lbAbiInfoType);
@@ -353,7 +372,7 @@ namespace lbAbi386 {
} }
Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count) { Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count) {
auto args = array_make<lbArgType>(heap_allocator(), arg_count); auto args = array_make<lbArgType>(lb_function_type_args_allocator(), arg_count);
for (unsigned i = 0; i < arg_count; i++) { for (unsigned i = 0; i < arg_count; i++) {
LLVMTypeRef t = arg_types[i]; LLVMTypeRef t = arg_types[i];
@@ -392,19 +411,19 @@ namespace lbAbi386 {
namespace lbAbiAmd64Win64 { namespace lbAbiAmd64Win64 {
Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count); Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count);
lbArgType compute_return_type(lbFunctionType *ft, LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined, bool return_is_tuple);
LB_ABI_INFO(abi_info) { LB_ABI_INFO(abi_info) {
lbFunctionType *ft = gb_alloc_item(permanent_allocator(), lbFunctionType); lbFunctionType *ft = gb_alloc_item(permanent_allocator(), lbFunctionType);
ft->ctx = c; ft->ctx = c;
ft->args = compute_arg_types(c, arg_types, arg_count); ft->args = compute_arg_types(c, arg_types, arg_count);
ft->ret = lbAbi386::compute_return_type(c, return_type, return_is_defined); ft->ret = compute_return_type(ft, c, return_type, return_is_defined, return_is_tuple);
ft->calling_convention = calling_convention; ft->calling_convention = calling_convention;
return ft; return ft;
} }
Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count) { Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count) {
auto args = array_make<lbArgType>(heap_allocator(), arg_count); auto args = array_make<lbArgType>(lb_function_type_args_allocator(), arg_count);
for (unsigned i = 0; i < arg_count; i++) { for (unsigned i = 0; i < arg_count; i++) {
LLVMTypeRef t = arg_types[i]; LLVMTypeRef t = arg_types[i];
@@ -428,6 +447,45 @@ namespace lbAbiAmd64Win64 {
} }
return args; return args;
} }
lbArgType compute_return_type(lbFunctionType *ft, LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined, bool return_is_tuple) {
if (!return_is_defined) {
return lb_arg_type_direct(LLVMVoidTypeInContext(c));
} else if (lb_is_type_kind(return_type, LLVMStructTypeKind) || lb_is_type_kind(return_type, LLVMArrayTypeKind)) {
i64 sz = lb_sizeof(return_type);
switch (sz) {
case 1: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 8), nullptr, nullptr);
case 2: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 16), nullptr, nullptr);
case 4: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 32), nullptr, nullptr);
case 8: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 64), nullptr, nullptr);
}
if (return_is_tuple) {
GB_ASSERT(lb_is_type_kind(return_type, LLVMStructTypeKind));
unsigned field_count = LLVMCountStructElementTypes(return_type);
if (field_count > 1) {
ft->original_arg_count = ft->args.count;
ft->multiple_return_original_type = return_type;
for (unsigned i = 0; i < field_count-1; i++) {
LLVMTypeRef field_type = LLVMStructGetTypeAtIndex(return_type, i);
LLVMTypeRef field_pointer_type = LLVMPointerType(field_type, 0);
lbArgType ret_partial = lb_arg_type_direct(field_pointer_type);
array_add(&ft->args, ret_partial);
}
// override the return type for the last field
LLVMTypeRef new_return_type = LLVMStructGetTypeAtIndex(return_type, field_count-1);
return compute_return_type(ft, c, new_return_type, true, false);
}
}
LLVMAttributeRef attr = lb_create_enum_attribute_with_type(c, "sret", return_type);
return lb_arg_type_indirect(return_type, attr);
}
return lbAbi386::non_struct(c, return_type, true);
}
}; };
// NOTE(bill): I hate `namespace` in C++ but this is just because I don't want to prefix everything // NOTE(bill): I hate `namespace` in C++ but this is just because I don't want to prefix everything
@@ -490,7 +548,7 @@ namespace lbAbiAmd64SysV {
ft->ctx = c; ft->ctx = c;
ft->calling_convention = calling_convention; ft->calling_convention = calling_convention;
ft->args = array_make<lbArgType>(heap_allocator(), arg_count); ft->args = array_make<lbArgType>(lb_function_type_args_allocator(), arg_count);
for (unsigned i = 0; i < arg_count; i++) { for (unsigned i = 0; i < arg_count; i++) {
ft->args[i] = amd64_type(c, arg_types[i], Amd64TypeAttribute_ByVal, calling_convention); ft->args[i] = amd64_type(c, arg_types[i], Amd64TypeAttribute_ByVal, calling_convention);
} }
@@ -1056,7 +1114,7 @@ namespace lbAbiArm64 {
} }
Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count) { Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count) {
auto args = array_make<lbArgType>(heap_allocator(), arg_count); auto args = array_make<lbArgType>(lb_function_type_args_allocator(), arg_count);
for (unsigned i = 0; i < arg_count; i++) { for (unsigned i = 0; i < arg_count; i++) {
LLVMTypeRef type = arg_types[i]; LLVMTypeRef type = arg_types[i];
@@ -1188,7 +1246,7 @@ namespace lbAbiWasm {
Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count) { Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count) {
auto args = array_make<lbArgType>(heap_allocator(), arg_count); auto args = array_make<lbArgType>(lb_function_type_args_allocator(), arg_count);
for (unsigned i = 0; i < arg_count; i++) { for (unsigned i = 0; i < arg_count; i++) {
LLVMTypeRef t = arg_types[i]; LLVMTypeRef t = arg_types[i];
@@ -1266,7 +1324,7 @@ namespace lbAbiArm32 {
} }
Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count, ProcCallingConvention calling_convention) { Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count, ProcCallingConvention calling_convention) {
auto args = array_make<lbArgType>(heap_allocator(), arg_count); auto args = array_make<lbArgType>(lb_function_type_args_allocator(), arg_count);
for (unsigned i = 0; i < arg_count; i++) { for (unsigned i = 0; i < arg_count; i++) {
LLVMTypeRef t = arg_types[i]; LLVMTypeRef t = arg_types[i];
@@ -1307,14 +1365,14 @@ namespace lbAbiArm32 {
}; };
LB_ABI_INFO(lb_get_abi_info) { LB_ABI_INFO(lb_get_abi_info_internal) {
switch (calling_convention) { switch (calling_convention) {
case ProcCC_None: case ProcCC_None:
case ProcCC_InlineAsm: case ProcCC_InlineAsm:
{ {
lbFunctionType *ft = gb_alloc_item(permanent_allocator(), lbFunctionType); lbFunctionType *ft = gb_alloc_item(permanent_allocator(), lbFunctionType);
ft->ctx = c; ft->ctx = c;
ft->args = array_make<lbArgType>(heap_allocator(), arg_count); ft->args = array_make<lbArgType>(lb_function_type_args_allocator(), arg_count);
for (unsigned i = 0; i < arg_count; i++) { for (unsigned i = 0; i < arg_count; i++) {
ft->args[i] = lb_arg_type_direct(arg_types[i]); ft->args[i] = lb_arg_type_direct(arg_types[i]);
} }
@@ -1328,32 +1386,43 @@ LB_ABI_INFO(lb_get_abi_info) {
} }
case ProcCC_Win64: case ProcCC_Win64:
GB_ASSERT(build_context.metrics.arch == TargetArch_amd64); GB_ASSERT(build_context.metrics.arch == TargetArch_amd64);
return lbAbiAmd64Win64::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention); return lbAbiAmd64Win64::abi_info(c, arg_types, arg_count, return_type, return_is_defined, return_is_tuple, calling_convention);
case ProcCC_SysV: case ProcCC_SysV:
GB_ASSERT(build_context.metrics.arch == TargetArch_amd64); GB_ASSERT(build_context.metrics.arch == TargetArch_amd64);
return lbAbiAmd64SysV::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention); return lbAbiAmd64SysV::abi_info(c, arg_types, arg_count, return_type, return_is_defined, return_is_tuple, calling_convention);
} }
switch (build_context.metrics.arch) { switch (build_context.metrics.arch) {
case TargetArch_amd64: case TargetArch_amd64:
if (build_context.metrics.os == TargetOs_windows || build_context.metrics.abi == TargetABI_Win64) { if (build_context.metrics.os == TargetOs_windows || build_context.metrics.abi == TargetABI_Win64) {
return lbAbiAmd64Win64::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention); return lbAbiAmd64Win64::abi_info(c, arg_types, arg_count, return_type, return_is_defined, return_is_tuple, calling_convention);
} else if (build_context.metrics.abi == TargetABI_SysV) { } else if (build_context.metrics.abi == TargetABI_SysV) {
return lbAbiAmd64SysV::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention); return lbAbiAmd64SysV::abi_info(c, arg_types, arg_count, return_type, return_is_defined, return_is_tuple, calling_convention);
} else { } else {
return lbAbiAmd64SysV::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention); return lbAbiAmd64SysV::abi_info(c, arg_types, arg_count, return_type, return_is_defined, return_is_tuple, calling_convention);
} }
case TargetArch_i386: case TargetArch_i386:
return lbAbi386::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention); return lbAbi386::abi_info(c, arg_types, arg_count, return_type, return_is_defined, return_is_tuple, calling_convention);
case TargetArch_arm32: case TargetArch_arm32:
return lbAbiArm32::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention); return lbAbiArm32::abi_info(c, arg_types, arg_count, return_type, return_is_defined, return_is_tuple, calling_convention);
case TargetArch_arm64: case TargetArch_arm64:
return lbAbiArm64::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention); return lbAbiArm64::abi_info(c, arg_types, arg_count, return_type, return_is_defined, return_is_tuple, calling_convention);
case TargetArch_wasm32: case TargetArch_wasm32:
case TargetArch_wasm64: case TargetArch_wasm64:
return lbAbiWasm::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention); return lbAbiWasm::abi_info(c, arg_types, arg_count, return_type, return_is_defined, return_is_tuple, calling_convention);
} }
GB_PANIC("Unsupported ABI"); GB_PANIC("Unsupported ABI");
return {}; return {};
} }
LB_ABI_INFO(lb_get_abi_info) {
lbFunctionType *ft = lb_get_abi_info_internal(c, arg_types, arg_count, return_type, return_is_defined, ALLOW_SPLIT_MULTI_RETURNS && return_is_tuple, calling_convention);
if (calling_convention == ProcCC_Odin) {
// append the `context` pointer
lbArgType context_param = lb_arg_type_direct(LLVMPointerType(LLVMInt8TypeInContext(c), 0));
array_add(&ft->args, context_param);
}
return ft;
}
+15 -20
View File
@@ -1499,9 +1499,6 @@ LLVMTypeRef lb_type_internal_for_procedures_raw(lbModule *m, Type *type) {
} }
unsigned param_count = 0; unsigned param_count = 0;
if (type->Proc.calling_convention == ProcCC_Odin) {
param_count += 1;
}
if (type->Proc.param_count != 0) { if (type->Proc.param_count != 0) {
GB_ASSERT(type->Proc.params->kind == Type_Tuple); GB_ASSERT(type->Proc.params->kind == Type_Tuple);
@@ -1519,21 +1516,23 @@ LLVMTypeRef lb_type_internal_for_procedures_raw(lbModule *m, Type *type) {
m->internal_type_level += 1; m->internal_type_level += 1;
defer (m->internal_type_level -= 1); defer (m->internal_type_level -= 1);
bool return_is_tuple = false;
LLVMTypeRef ret = nullptr; LLVMTypeRef ret = nullptr;
LLVMTypeRef *params = gb_alloc_array(permanent_allocator(), LLVMTypeRef, param_count); LLVMTypeRef *params = gb_alloc_array(permanent_allocator(), LLVMTypeRef, param_count);
bool *params_by_ptr = gb_alloc_array(permanent_allocator(), bool, param_count); bool *params_by_ptr = gb_alloc_array(permanent_allocator(), bool, param_count);
if (type->Proc.result_count != 0) { if (type->Proc.result_count != 0) {
Type *single_ret = reduce_tuple_to_single_type(type->Proc.results); Type *single_ret = reduce_tuple_to_single_type(type->Proc.results);
if (is_type_proc(single_ret)) { if (is_type_proc(single_ret)) {
single_ret = t_rawptr; single_ret = t_rawptr;
} }
ret = lb_type(m, single_ret); ret = lb_type(m, single_ret);
if (ret != nullptr) { if (is_type_tuple(single_ret)) {
if (is_type_boolean(single_ret) && return_is_tuple = true;
is_calling_convention_none(type->Proc.calling_convention) && }
type_size_of(single_ret) <= 1) { if (is_type_boolean(single_ret) &&
ret = LLVMInt1TypeInContext(m->ctx); is_calling_convention_none(type->Proc.calling_convention) &&
} type_size_of(single_ret) <= 1) {
ret = LLVMInt1TypeInContext(m->ctx);
} }
} }
@@ -1571,12 +1570,8 @@ LLVMTypeRef lb_type_internal_for_procedures_raw(lbModule *m, Type *type) {
params[param_index++] = param_type; params[param_index++] = param_type;
} }
} }
if (param_index < param_count) {
params[param_index++] = lb_type(m, t_rawptr);
}
GB_ASSERT(param_index == param_count); GB_ASSERT(param_index == param_count);
lbFunctionType *ft = lb_get_abi_info(m->ctx, params, param_count, ret, ret != nullptr, return_is_tuple, type->Proc.calling_convention);
lbFunctionType *ft = lb_get_abi_info(m->ctx, params, param_count, ret, ret != nullptr, type->Proc.calling_convention);
{ {
for_array(j, ft->args) { for_array(j, ft->args) {
auto arg = ft->args[j]; auto arg = ft->args[j];
@@ -1593,10 +1588,10 @@ LLVMTypeRef lb_type_internal_for_procedures_raw(lbModule *m, Type *type) {
LLVMPrintTypeToString(ft->ret.type), LLVMPrintTypeToString(ft->ret.type),
LLVMGetTypeContext(ft->ret.type), ft->ctx, LLVMGetGlobalContext()); LLVMGetTypeContext(ft->ret.type), ft->ctx, LLVMGetGlobalContext());
} }
for_array(j, ft->args) { for (unsigned i = 0; i < param_count; i++) {
if (params_by_ptr[j]) { if (params_by_ptr[i]) {
// NOTE(bill): The parameter needs to be passed "indirectly", override it // NOTE(bill): The parameter needs to be passed "indirectly", override it
ft->args[j].kind = lbArg_Indirect; ft->args[i].kind = lbArg_Indirect;
} }
} }
@@ -2161,11 +2156,11 @@ LLVMTypeRef lb_type(lbModule *m, Type *type) {
return llvm_type; return llvm_type;
} }
lbFunctionType *lb_get_function_type(lbModule *m, lbProcedure *p, Type *pt) { lbFunctionType *lb_get_function_type(lbModule *m, Type *pt) {
lbFunctionType **ft_found = nullptr; lbFunctionType **ft_found = nullptr;
ft_found = map_get(&m->function_type_map, pt); ft_found = map_get(&m->function_type_map, pt);
if (!ft_found) { if (!ft_found) {
LLVMTypeRef llvm_proc_type = lb_type(p->module, pt); LLVMTypeRef llvm_proc_type = lb_type(m, pt);
gb_unused(llvm_proc_type); gb_unused(llvm_proc_type);
ft_found = map_get(&m->function_type_map, pt); ft_found = map_get(&m->function_type_map, pt);
} }
+75 -18
View File
@@ -501,6 +501,14 @@ void lb_begin_procedure_body(lbProcedure *p) {
// NOTE(bill): this must be parameter 0 // NOTE(bill): this must be parameter 0
String name = str_lit("agg.result"); String name = str_lit("agg.result");
if (ft->multiple_return_original_type &&
p->type->Proc.has_named_results) {
auto const &variables = p->type->Proc.results->Tuple.variables;
Entity *e = variables[variables.count-1];
if (!is_blank_ident(e->token)) {
name = e->token.string;
}
}
Type *ptr_type = alloc_type_pointer(reduce_tuple_to_single_type(p->type->Proc.results)); Type *ptr_type = alloc_type_pointer(reduce_tuple_to_single_type(p->type->Proc.results));
Entity *e = alloc_entity_param(nullptr, make_token_ident(name), ptr_type, false, false); Entity *e = alloc_entity_param(nullptr, make_token_ident(name), ptr_type, false, false);
@@ -580,14 +588,31 @@ void lb_begin_procedure_body(lbProcedure *p) {
if (e->token.string != "") { if (e->token.string != "") {
GB_ASSERT(!is_blank_ident(e->token)); GB_ASSERT(!is_blank_ident(e->token));
// NOTE(bill): Don't even bother trying to optimize this with the return ptr value lbAddr res = {};
// This will violate the defer rules if you do:
// foo :: proc() -> (x, y: T) { lbFunctionType *ft = p->abi_function_type;
// defer x = ... // defer is executed after the `defer` if (ft->multiple_return_original_type &&
// return // the values returned should be zeroed i < results->variables.count-1) {
// } isize ret_offset = param_offset + ft->original_arg_count + i;
// NOTE(bill): REALLY, don't even bother. lbValue ptr = {};
lbAddr res = lb_add_local(p, e->type, e); ptr.value = LLVMGetParam(p->value, cast(unsigned)ret_offset);
ptr.type = alloc_type_pointer(e->type);
res = lb_addr(ptr);
lb_add_entity(p->module, e, ptr);
lb_add_debug_local_variable(p, ptr.value, e->type, e->token);
} else {
// NOTE(bill): Don't even bother trying to optimize this with the return ptr value
// This will violate the defer rules if you do:
// foo :: proc() -> (x, y: T) {
// defer x = ... // defer is executed after the `defer`
// return // the values returned should be zeroed
// }
// NOTE(bill): REALLY, don't even bother.
res = lb_add_local(p, e->type, e);
}
if (e->Variable.param_value.kind != ParameterValue_Invalid) { if (e->Variable.param_value.kind != ParameterValue_Invalid) {
lbValue c = lb_handle_param_value(p, e->type, e->Variable.param_value, e->token.pos); lbValue c = lb_handle_param_value(p, e->type, e->Variable.param_value, e->token.pos);
lb_addr_store(p, res, c); lb_addr_store(p, res, c);
@@ -734,6 +759,7 @@ lbValue lb_emit_call_internal(lbProcedure *p, lbValue value, lbValue return_ptr,
if (return_ptr.value != nullptr) { if (return_ptr.value != nullptr) {
args[arg_index++] = return_ptr.value; args[arg_index++] = return_ptr.value;
} }
for_array(i, processed_args) { for_array(i, processed_args) {
lbValue arg = processed_args[i]; lbValue arg = processed_args[i];
if (is_type_proc(arg.type)) { if (is_type_proc(arg.type)) {
@@ -741,16 +767,23 @@ lbValue lb_emit_call_internal(lbProcedure *p, lbValue value, lbValue return_ptr,
} }
args[arg_index++] = arg.value; args[arg_index++] = arg.value;
} }
if (context_ptr.addr.value != nullptr) { if (context_ptr.addr.value != nullptr) {
LLVMValueRef cp = context_ptr.addr.value; LLVMValueRef cp = context_ptr.addr.value;
cp = LLVMBuildPointerCast(p->builder, cp, lb_type(p->module, t_rawptr), ""); cp = LLVMBuildPointerCast(p->builder, cp, lb_type(p->module, t_rawptr), "");
args[arg_index++] = cp; args[arg_index++] = cp;
} }
GB_ASSERT(arg_index == arg_count);
LLVMBasicBlockRef curr_block = LLVMGetInsertBlock(p->builder); LLVMBasicBlockRef curr_block = LLVMGetInsertBlock(p->builder);
GB_ASSERT(curr_block != p->decl_block->block); GB_ASSERT(curr_block != p->decl_block->block);
{ {
LLVMTypeRef fnp = lb_type_internal_for_procedures_raw(p->module, value.type); Type *proc_type = base_type(value.type);
GB_ASSERT(proc_type->kind == Type_Proc);
LLVMTypeRef fnp = lb_type_internal_for_procedures_raw(p->module, proc_type);
LLVMTypeRef ftp = LLVMPointerType(fnp, 0); LLVMTypeRef ftp = LLVMPointerType(fnp, 0);
LLVMValueRef fn = value.value; LLVMValueRef fn = value.value;
if (!lb_is_type_kind(LLVMTypeOf(value.value), LLVMFunctionTypeKind)) { if (!lb_is_type_kind(LLVMTypeOf(value.value), LLVMFunctionTypeKind)) {
@@ -775,10 +808,11 @@ lbValue lb_emit_call_internal(lbProcedure *p, lbValue value, lbValue return_ptr,
// LLVMTypeKind arg_kind = LLVMGetTypeKind(arg_type); // LLVMTypeKind arg_kind = LLVMGetTypeKind(arg_type);
GB_ASSERT_MSG( GB_ASSERT_MSG(
arg_type == param_type, arg_type == param_type,
"Parameter types do not match: %s != %s, argument: %s", "Parameter types do not match: %s != %s, argument: %s\n\t%s",
LLVMPrintTypeToString(arg_type), LLVMPrintTypeToString(arg_type),
LLVMPrintTypeToString(param_type), LLVMPrintTypeToString(param_type),
LLVMPrintValueToString(args[i]) LLVMPrintValueToString(args[i]),
LLVMPrintTypeToString(fnp)
); );
} }
} }
@@ -915,8 +949,9 @@ lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> const &args,
bool is_odin_cc = is_calling_convention_odin(pt->Proc.calling_convention); bool is_odin_cc = is_calling_convention_odin(pt->Proc.calling_convention);
lbFunctionType *ft = lb_get_function_type(m, p, pt); lbFunctionType *ft = lb_get_function_type(m, pt);
bool return_by_pointer = ft->ret.kind == lbArg_Indirect; bool return_by_pointer = ft->ret.kind == lbArg_Indirect;
bool split_returns = ft->multiple_return_original_type != nullptr;
unsigned param_index = 0; unsigned param_index = 0;
for (isize i = 0; i < param_count; i++) { for (isize i = 0; i < param_count; i++) {
@@ -979,13 +1014,19 @@ lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> const &args,
} }
Type *rt = reduce_tuple_to_single_type(results); Type *rt = reduce_tuple_to_single_type(results);
if (return_by_pointer) { Type *original_rt = rt;
lbValue return_ptr = {}; if (split_returns) {
if (return_ptr.value == nullptr) { GB_ASSERT(rt->kind == Type_Tuple);
lbAddr r = lb_add_local_generated(p, rt, true); for (isize j = 0; j < rt->Tuple.variables.count-1; j++) {
return_ptr = r.addr; Type *partial_return_type = rt->Tuple.variables[j]->type;
lbValue partial_return_ptr = lb_add_local_generated(p, partial_return_type, true).addr;
array_add(&processed_args, partial_return_ptr);
} }
GB_ASSERT(is_type_pointer(return_ptr.type)); rt = reduce_tuple_to_single_type(rt->Tuple.variables[rt->Tuple.variables.count-1]->type);
}
if (return_by_pointer) {
lbValue return_ptr = lb_add_local_generated(p, rt, true).addr;
lb_emit_call_internal(p, value, return_ptr, processed_args, nullptr, context_ptr, inlining); lb_emit_call_internal(p, value, return_ptr, processed_args, nullptr, context_ptr, inlining);
result = lb_emit_load(p, return_ptr); result = lb_emit_load(p, return_ptr);
} else if (rt != nullptr) { } else if (rt != nullptr) {
@@ -1005,6 +1046,22 @@ lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> const &args,
lb_emit_call_internal(p, value, {}, processed_args, nullptr, context_ptr, inlining); lb_emit_call_internal(p, value, {}, processed_args, nullptr, context_ptr, inlining);
} }
if (original_rt != rt) {
GB_ASSERT(split_returns);
GB_ASSERT(original_rt->kind == Type_Tuple);
lbValue result_ptr = lb_add_local_generated(p, original_rt, false).addr;
isize ret_count = original_rt->Tuple.variables.count;
isize offset = ft->original_arg_count;
for (isize j = 0; j < ret_count-1; j++) {
lbValue ret_arg_ptr = processed_args[offset + j];
lbValue ret_arg = lb_emit_load(p, ret_arg_ptr);
lb_emit_store(p, lb_emit_struct_ep(p, result_ptr, cast(i32)j), ret_arg);
}
lb_emit_store(p, lb_emit_struct_ep(p, result_ptr, cast(i32)(ret_count-1)), result);
result = lb_emit_load(p, result_ptr);
}
} }
Entity **found = map_get(&p->module->procedure_values, value.value); Entity **found = map_get(&p->module->procedure_values, value.value);
+21 -3
View File
@@ -1576,9 +1576,27 @@ void lb_build_assignment(lbProcedure *p, Array<lbAddr> &lvals, Slice<Ast *> cons
} }
} }
void lb_build_return_stmt_internal(lbProcedure *p, lbValue const &res) { void lb_build_return_stmt_internal(lbProcedure *p, lbValue res) {
lbFunctionType *ft = lb_get_function_type(p->module, p, p->type); lbFunctionType *ft = lb_get_function_type(p->module, p->type);
bool return_by_pointer = ft->ret.kind == lbArg_Indirect; bool return_by_pointer = ft->ret.kind == lbArg_Indirect;
bool split_returns = ft->multiple_return_original_type != nullptr;
if (split_returns) {
GB_ASSERT(res.value != nullptr);
Type *res_type = res.type;
GB_ASSERT(is_type_tuple(res_type));
isize res_count = res_type->Tuple.variables.count;
isize param_offset = return_by_pointer ? 1 : 0;
param_offset += ft->original_arg_count;
for (isize i = 0; i < res_count-1; i++) {
lbValue ret_ptr = {};
ret_ptr.value = LLVMGetParam(p->value, cast(unsigned)(param_offset + i));
ret_ptr.type = alloc_type_pointer(res_type->Tuple.variables[i]->type);
lb_emit_store(p, ret_ptr, lb_emit_struct_ev(p, res, cast(i32)i));
}
res = lb_emit_struct_ev(p, res, cast(i32)(res_count-1));
}
if (return_by_pointer) { if (return_by_pointer) {
if (res.value != nullptr) { if (res.value != nullptr) {
@@ -1617,7 +1635,7 @@ void lb_build_return_stmt(lbProcedure *p, Slice<Ast *> const &return_results) {
isize return_count = p->type->Proc.result_count; isize return_count = p->type->Proc.result_count;
isize res_count = return_results.count; isize res_count = return_results.count;
lbFunctionType *ft = lb_get_function_type(p->module, p, p->type); lbFunctionType *ft = lb_get_function_type(p->module, p->type);
bool return_by_pointer = ft->ret.kind == lbArg_Indirect; bool return_by_pointer = ft->ret.kind == lbArg_Indirect;
if (return_count == 0) { if (return_count == 0) {
+1 -1
View File
@@ -436,7 +436,7 @@ lbValue lb_emit_or_else(lbProcedure *p, Ast *arg, Ast *else_expr, TypeAndValue c
} }
void lb_build_return_stmt(lbProcedure *p, Slice<Ast *> const &return_results); void lb_build_return_stmt(lbProcedure *p, Slice<Ast *> const &return_results);
void lb_build_return_stmt_internal(lbProcedure *p, lbValue const &res); void lb_build_return_stmt_internal(lbProcedure *p, lbValue res);
lbValue lb_emit_or_return(lbProcedure *p, Ast *arg, TypeAndValue const &tv) { lbValue lb_emit_or_return(lbProcedure *p, Ast *arg, TypeAndValue const &tv) {
lbValue lhs = {}; lbValue lhs = {};