diff options
| author | gingerBill <gingerBill@users.noreply.github.com> | 2022-12-07 11:42:23 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-12-07 11:42:23 +0000 |
| commit | a5bdb4a8e85f955ea660bd66b2c1c5990ab46c03 (patch) | |
| tree | 9cc0fe9c6a7b7cd4728bafc21b5252372d131d52 /src/llvm_backend_proc.cpp | |
| parent | 521ed286321a30e5742a432effefb2c98b9484a7 (diff) | |
| parent | d88b052d2d9aa8fa012be314bd29d7ae311fc941 (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.cpp | 207 |
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); } |