aboutsummaryrefslogtreecommitdiff
path: root/src/llvm_backend_proc.cpp
diff options
context:
space:
mode:
authorgingerBill <gingerBill@users.noreply.github.com>2022-12-07 11:42:23 +0000
committerGitHub <noreply@github.com>2022-12-07 11:42:23 +0000
commita5bdb4a8e85f955ea660bd66b2c1c5990ab46c03 (patch)
tree9cc0fe9c6a7b7cd4728bafc21b5252372d131d52 /src/llvm_backend_proc.cpp
parent521ed286321a30e5742a432effefb2c98b9484a7 (diff)
parentd88b052d2d9aa8fa012be314bd29d7ae311fc941 (diff)
Merge pull request #2208 from odin-lang/multiple-return-abi-experiment
Multiple Return ABI Changes and Improvements
Diffstat (limited to 'src/llvm_backend_proc.cpp')
-rw-r--r--src/llvm_backend_proc.cpp207
1 files changed, 165 insertions, 42 deletions
diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp
index 6e2345436..f8dffa9b6 100644
--- a/src/llvm_backend_proc.cpp
+++ b/src/llvm_backend_proc.cpp
@@ -1,4 +1,3 @@
-
LLVMValueRef lb_call_intrinsic(lbProcedure *p, const char *name, LLVMValueRef* args, unsigned arg_count, LLVMTypeRef* types, unsigned type_count)
{
unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
@@ -123,6 +122,7 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body)
p->scope_stack.allocator = a;
map_init(&p->selector_values, a, 0);
map_init(&p->selector_addr, a, 0);
+ map_init(&p->tuple_fix_map, a, 0);
if (p->is_foreign) {
lb_add_foreign_library_path(p->module, entity->Procedure.foreign_library);
@@ -501,8 +501,23 @@ void lb_begin_procedure_body(lbProcedure *p) {
// NOTE(bill): this must be parameter 0
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 *return_ptr_type = reduce_tuple_to_single_type(p->type->Proc.results);
+ bool split_returns = ft->multiple_return_original_type != nullptr;
+ if (split_returns) {
+ GB_ASSERT(is_type_tuple(return_ptr_type));
+ auto const &variables = return_ptr_type->Tuple.variables;
+ return_ptr_type = variables[variables.count-1]->type;
+ }
+ Type *ptr_type = alloc_type_pointer(return_ptr_type);
Entity *e = alloc_entity_param(nullptr, make_token_ident(name), ptr_type, false, false);
e->flags |= EntityFlag_NoAlias;
@@ -580,14 +595,70 @@ void lb_begin_procedure_body(lbProcedure *p) {
if (e->token.string != "") {
GB_ASSERT(!is_blank_ident(e->token));
- // 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.
- lbAddr res = lb_add_local(p, e->type, e);
+ lbAddr res = {};
+ if (p->entity && p->entity->decl_info &&
+ p->entity->decl_info->defer_use_checked &&
+ p->entity->decl_info->defer_used == 0) {
+
+ // NOTE(bill): this is a bodge to get around the issue of the problem BELOW
+ // We check to see if we ever use a defer statement ever within a procedure and if it
+ // if it never happens, see if you can possibly do take the return value pointer
+ //
+ // NOTE(bill): this could be buggy in that I have missed a case where `defer` was used
+ //
+ // TODO(bill): This could be optimized to check to see where a `defer` only uses
+ // the variable in question
+
+ bool has_return_ptr = p->return_ptr.addr.value != nullptr;
+ lbValue ptr = {};
+
+ if (ft->multiple_return_original_type != nullptr) {
+ isize the_offset = -1;
+ if (i+1 < results->variables.count) {
+ the_offset = cast(isize)param_offset + ft->original_arg_count + i;
+ } else if (has_return_ptr) {
+ GB_ASSERT(i+1 == results->variables.count);
+ the_offset = 0;
+ }
+ if (the_offset >= 0) {
+ lbValue ptr = {};
+ ptr.value = LLVMGetParam(p->value, cast(unsigned)the_offset);
+ ptr.type = alloc_type_pointer(e->type);
+
+
+ }
+ } else if (has_return_ptr) {
+ lbValue ptr = p->return_ptr.addr;
+
+ if (results->variables.count > 1) {
+ ptr = lb_emit_tuple_ep(p, ptr, cast(i32)i);
+ }
+ GB_ASSERT(is_type_pointer(ptr.type));
+ GB_ASSERT(are_types_identical(type_deref(ptr.type), e->type));
+ }
+
+ if (ptr.value != nullptr) {
+ lb_add_entity(p->module, e, ptr);
+ lb_add_debug_local_variable(p, ptr.value, e->type, e->token);
+ // NOTE(bill): no need to zero on the callee side as it is zeroed on the caller side
+
+ res = lb_addr(ptr);
+ }
+ }
+
+ if (res.addr.type == nullptr) {
+ // 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.
+ //
+ // IMPORTANT NOTE(bill): REALLY, don't even bother!!!!!!
+ res = lb_add_local(p, e->type, e);
+ }
+
if (e->Variable.param_value.kind != ParameterValue_Invalid) {
lbValue c = lb_handle_param_value(p, e->type, e->Variable.param_value, e->token.pos);
lb_addr_store(p, res, c);
@@ -700,15 +771,8 @@ Array<lbValue> lb_value_to_array(lbProcedure *p, lbValue value) {
if (t == nullptr) {
// Do nothing
} else if (is_type_tuple(t)) {
- GB_ASSERT(t->kind == Type_Tuple);
- auto *rt = &t->Tuple;
- if (rt->variables.count > 0) {
- array = array_make<lbValue>(permanent_allocator(), rt->variables.count);
- for_array(i, rt->variables) {
- lbValue elem = lb_emit_struct_ev(p, value, cast(i32)i);
- array[i] = elem;
- }
- }
+ array = array_make<lbValue>(permanent_allocator(), 0, t->Tuple.variables.count);
+ lb_append_tuple_values(p, &array, value);
} else {
array = array_make<lbValue>(permanent_allocator(), 1);
array[0] = value;
@@ -734,6 +798,7 @@ lbValue lb_emit_call_internal(lbProcedure *p, lbValue value, lbValue return_ptr,
if (return_ptr.value != nullptr) {
args[arg_index++] = return_ptr.value;
}
+
for_array(i, processed_args) {
lbValue arg = processed_args[i];
if (is_type_proc(arg.type)) {
@@ -741,16 +806,23 @@ lbValue lb_emit_call_internal(lbProcedure *p, lbValue value, lbValue return_ptr,
}
args[arg_index++] = arg.value;
}
+
if (context_ptr.addr.value != nullptr) {
LLVMValueRef cp = context_ptr.addr.value;
cp = LLVMBuildPointerCast(p->builder, cp, lb_type(p->module, t_rawptr), "");
args[arg_index++] = cp;
}
+
+ GB_ASSERT(arg_index == arg_count);
+
LLVMBasicBlockRef curr_block = LLVMGetInsertBlock(p->builder);
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);
LLVMValueRef fn = value.value;
if (!lb_is_type_kind(LLVMTypeOf(value.value), LLVMFunctionTypeKind)) {
@@ -775,10 +847,11 @@ lbValue lb_emit_call_internal(lbProcedure *p, lbValue value, lbValue return_ptr,
// LLVMTypeKind arg_kind = LLVMGetTypeKind(arg_type);
GB_ASSERT_MSG(
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(param_type),
- LLVMPrintValueToString(args[i])
+ LLVMPrintValueToString(args[i]),
+ LLVMPrintTypeToString(fnp)
);
}
}
@@ -882,7 +955,7 @@ lbValue lb_emit_conjugate(lbProcedure *p, lbValue val, Type *type) {
return lb_emit_load(p, res);
}
-lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> const &args, ProcInlining inlining, bool use_copy_elision_hint) {
+lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> const &args, ProcInlining inlining) {
lbModule *m = p->module;
Type *pt = base_type(value.type);
@@ -915,8 +988,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);
- 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 split_returns = ft->multiple_return_original_type != nullptr;
unsigned param_index = 0;
for (isize i = 0; i < param_count; i++) {
@@ -979,18 +1053,19 @@ lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> const &args,
}
Type *rt = reduce_tuple_to_single_type(results);
- if (return_by_pointer) {
- lbValue return_ptr = {};
- if (use_copy_elision_hint && p->copy_elision_hint.ptr.value != nullptr) {
- if (are_types_identical(type_deref(p->copy_elision_hint.ptr.type), rt)) {
- return_ptr = lb_consume_copy_elision_hint(p);
- }
- }
- if (return_ptr.value == nullptr) {
- lbAddr r = lb_add_local_generated(p, rt, true);
- return_ptr = r.addr;
+ Type *original_rt = rt;
+ if (split_returns) {
+ GB_ASSERT(rt->kind == Type_Tuple);
+ for (isize j = 0; j < rt->Tuple.variables.count-1; j++) {
+ Type *partial_return_type = rt->Tuple.variables[j]->type;
+ lbValue partial_return_ptr = lb_add_local(p, partial_return_type, nullptr, true, false).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);
result = lb_emit_load(p, return_ptr);
} else if (rt != nullptr) {
@@ -1010,6 +1085,47 @@ lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> const &args,
lb_emit_call_internal(p, value, {}, processed_args, nullptr, context_ptr, inlining);
}
+ if (original_rt != rt) {
+ GB_ASSERT(split_returns);
+ GB_ASSERT(is_type_tuple(original_rt));
+
+ // IMPORTANT NOTE(bill, 2022-11-24)
+ // result_ptr is a dummy value which is only used to reference a tuple
+ // value for the "tuple-fix"
+ //
+ // The reason for the fake stack allocation is to have a unique pointer
+ // for the value to be used as a key within the procedure itself
+
+ lbValue result_ptr = lb_add_local_generated(p, original_rt, false).addr;
+ isize ret_count = original_rt->Tuple.variables.count;
+
+ auto tuple_fix_values = slice_make<lbValue>(permanent_allocator(), ret_count);
+ auto tuple_geps = slice_make<lbValue>(permanent_allocator(), ret_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);
+ tuple_fix_values[j] = ret_arg;
+ }
+ tuple_fix_values[ret_count-1] = result;
+
+ #if 0
+ for (isize j = 0; j < ret_count; j++) {
+ tuple_geps[j] = lb_emit_struct_ep(p, result_ptr, cast(i32)j);
+ }
+ for (isize j = 0; j < ret_count; j++) {
+ lb_emit_store(p, tuple_geps[j], tuple_fix_values[j]);
+ }
+ #endif
+
+ result = lb_emit_load(p, result_ptr);
+
+ lbTupleFix tf = {tuple_fix_values};
+ map_set(&p->tuple_fix_map, result_ptr.value, tf);
+ map_set(&p->tuple_fix_map, result.value, tf);
+ }
+
}
Entity **found = map_get(&p->module->procedure_values, value.value);
@@ -2300,7 +2416,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
);
LLVMSetWeak(value, weak);
- if (tv.type->kind == Type_Tuple) {
+ if (is_type_tuple(tv.type)) {
Type *fix_typed = alloc_type_tuple();
slice_init(&fix_typed->Tuple.variables, permanent_allocator(), 2);
fix_typed->Tuple.variables[0] = tv.type->Tuple.variables[0];
@@ -3032,7 +3148,7 @@ lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) {
}
}
- return lb_emit_call(p, value, args, ce->inlining, p->copy_elision_hint.ast == expr);
+ return lb_emit_call(p, value, args, ce->inlining);
}
isize arg_index = 0;
@@ -3044,7 +3160,7 @@ lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) {
GB_ASSERT_MSG(tav.mode != Addressing_Invalid, "%s %s %d", expr_to_string(arg), expr_to_string(expr), tav.mode);
GB_ASSERT_MSG(tav.mode != Addressing_ProcGroup, "%s", expr_to_string(arg));
Type *at = tav.type;
- if (at->kind == Type_Tuple) {
+ if (is_type_tuple(at)) {
arg_count += at->Tuple.variables.count;
} else {
arg_count++;
@@ -3084,9 +3200,16 @@ lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) {
lbValue a = lb_build_expr(p, arg);
Type *at = a.type;
if (at->kind == Type_Tuple) {
- for_array(i, at->Tuple.variables) {
- lbValue v = lb_emit_struct_ev(p, a, cast(i32)i);
- args[arg_index++] = v;
+ lbTupleFix *tf = map_get(&p->tuple_fix_map, a.value);
+ if (tf) {
+ for_array(j, tf->values) {
+ args[arg_index++] = tf->values[j];
+ }
+ } else {
+ for_array(j, at->Tuple.variables) {
+ lbValue v = lb_emit_struct_ev(p, a, cast(i32)j);
+ args[arg_index++] = v;
+ }
}
} else {
args[arg_index++] = a;
@@ -3213,6 +3336,6 @@ lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) {
}
auto call_args = array_slice(args, 0, final_count);
- return lb_emit_call(p, value, call_args, ce->inlining, p->copy_elision_hint.ast == expr);
+ return lb_emit_call(p, value, call_args, ce->inlining);
}