From dc55e885888a9d036dafea2036c1907306e69d14 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 15 Apr 2023 16:04:04 +0100 Subject: [PATCH] Add `@(deferred_*_by_ptr=)` --- src/checker.cpp | 321 ++++++++++++++++++++++++-------------- src/checker.hpp | 4 + src/llvm_backend_proc.cpp | 16 ++ 3 files changed, 226 insertions(+), 115 deletions(-) diff --git a/src/checker.cpp b/src/checker.cpp index 696802e99..a6768d495 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -3081,6 +3081,54 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) { } error(elem, "Expected a procedure entity for '%.*s'", LIT(name)); return false; + } else if (name == "deferred_in_by_ptr") { + if (value != nullptr) { + Operand o = {}; + check_expr(c, &o, value); + Entity *e = entity_of_node(o.expr); + if (e != nullptr && e->kind == Entity_Procedure) { + if (ac->deferred_procedure.entity != nullptr) { + error(elem, "Previous usage of a 'deferred_*' attribute"); + } + ac->deferred_procedure.kind = DeferredProcedure_in_by_ptr; + ac->deferred_procedure.entity = e; + return true; + } + } + error(elem, "Expected a procedure entity for '%.*s'", LIT(name)); + return false; + } else if (name == "deferred_out_by_ptr") { + if (value != nullptr) { + Operand o = {}; + check_expr(c, &o, value); + Entity *e = entity_of_node(o.expr); + if (e != nullptr && e->kind == Entity_Procedure) { + if (ac->deferred_procedure.entity != nullptr) { + error(elem, "Previous usage of a 'deferred_*' attribute"); + } + ac->deferred_procedure.kind = DeferredProcedure_out_by_ptr; + ac->deferred_procedure.entity = e; + return true; + } + } + error(elem, "Expected a procedure entity for '%.*s'", LIT(name)); + return false; + } else if (name == "deferred_in_out_by_ptr") { + if (value != nullptr) { + Operand o = {}; + check_expr(c, &o, value); + Entity *e = entity_of_node(o.expr); + if (e != nullptr && e->kind == Entity_Procedure) { + if (ac->deferred_procedure.entity != nullptr) { + error(elem, "Previous usage of a 'deferred_*' attribute"); + } + ac->deferred_procedure.kind = DeferredProcedure_in_out_by_ptr; + ac->deferred_procedure.entity = e; + return true; + } + } + error(elem, "Expected a procedure entity for '%.*s'", LIT(name)); + return false; } else if (name == "link_name") { ExactValue ev = check_decl_attribute_value(c, value); @@ -5438,6 +5486,26 @@ gb_internal void add_untyped_expressions(CheckerInfo *cinfo, UntypedExprInfoMap map_clear(untyped); } +gb_internal Type *tuple_to_pointers(Type *ot) { + if (ot == nullptr) { + return nullptr; + } + GB_ASSERT(ot->kind == Type_Tuple); + + + Type *t = alloc_type_tuple(); + t->Tuple.variables = slice_make(heap_allocator(), ot->Tuple.variables.count); + + Scope *scope = nullptr; + for_array(i, t->Tuple.variables) { + Entity *e = ot->Tuple.variables[i]; + t->Tuple.variables[i] = alloc_entity_variable(scope, e->token, alloc_type_pointer(e->type)); + } + t->Tuple.is_packed = ot->Tuple.is_packed; + + return t; +} + gb_internal void check_deferred_procedures(Checker *c) { for (Entity *src = nullptr; mpsc_dequeue(&c->procs_with_deferred_to_check, &src); /**/) { GB_ASSERT(src->kind == Entity_Procedure); @@ -5449,18 +5517,13 @@ gb_internal void check_deferred_procedures(Checker *c) { char const *attribute = "deferred_none"; switch (dst_kind) { - case DeferredProcedure_none: - attribute = "deferred_none"; - break; - case DeferredProcedure_in: - attribute = "deferred_in"; - break; - case DeferredProcedure_out: - attribute = "deferred_out"; - break; - case DeferredProcedure_in_out: - attribute = "deferred_in_out"; - break; + case DeferredProcedure_none: attribute = "deferred_none"; break; + case DeferredProcedure_in: attribute = "deferred_in"; break; + case DeferredProcedure_out: attribute = "deferred_out"; break; + case DeferredProcedure_in_out: attribute = "deferred_in_out"; break; + case DeferredProcedure_in_by_ptr: attribute = "deferred_in_by_ptr"; break; + case DeferredProcedure_out_by_ptr: attribute = "deferred_out_by_ptr"; break; + case DeferredProcedure_in_out_by_ptr: attribute = "deferred_in_out_by_ptr"; break; } if (is_type_polymorphic(src->type) || is_type_polymorphic(dst->type)) { @@ -5474,118 +5537,146 @@ gb_internal void check_deferred_procedures(Checker *c) { Type *src_results = base_type(src->type)->Proc.results; Type *dst_params = base_type(dst->type)->Proc.params; - if (dst_kind == DeferredProcedure_none) { - if (dst_params == nullptr) { - // Okay - continue; - } + bool by_ptr = false; + switch (dst_kind) { + case DeferredProcedure_in_by_ptr: + by_ptr = true; + src_params = tuple_to_pointers(src_params); + break; + case DeferredProcedure_out_by_ptr: + by_ptr = true; + src_results = tuple_to_pointers(src_results); + break; + case DeferredProcedure_in_out_by_ptr: + by_ptr = true; + src_params = tuple_to_pointers(src_params); + src_results = tuple_to_pointers(src_results); + break; + } - error(src->token, "Deferred procedure '%.*s' must have no input parameters", LIT(dst->token.string)); - } else if (dst_kind == DeferredProcedure_in) { - if (src_params == nullptr && dst_params == nullptr) { - // Okay - continue; - } - if ((src_params == nullptr && dst_params != nullptr) || - (src_params != nullptr && dst_params == nullptr)) { - error(src->token, "Deferred procedure '%.*s' parameters do not match the inputs of initial procedure '%.*s'", LIT(src->token.string), LIT(dst->token.string)); - continue; - } + switch (dst_kind) { + case DeferredProcedure_none: + { + if (dst_params == nullptr) { + // Okay + continue; + } - GB_ASSERT(src_params->kind == Type_Tuple); - GB_ASSERT(dst_params->kind == Type_Tuple); + error(src->token, "Deferred procedure '%.*s' must have no input parameters", LIT(dst->token.string)); + } break; + case DeferredProcedure_in: + case DeferredProcedure_in_by_ptr: + { + if (src_params == nullptr && dst_params == nullptr) { + // Okay + continue; + } + if ((src_params == nullptr && dst_params != nullptr) || + (src_params != nullptr && dst_params == nullptr)) { + error(src->token, "Deferred procedure '%.*s' parameters do not match the inputs of initial procedure '%.*s'", LIT(src->token.string), LIT(dst->token.string)); + continue; + } - if (are_types_identical(src_params, dst_params)) { - // Okay! - } else { - gbString s = type_to_string(src_params); - gbString d = type_to_string(dst_params); - error(src->token, "Deferred procedure '%.*s' parameters do not match the inputs of initial procedure '%.*s':\n\t(%s) =/= (%s)", - LIT(src->token.string), LIT(dst->token.string), - s, d - ); - gb_string_free(d); - gb_string_free(s); - continue; - } - - } else if (dst_kind == DeferredProcedure_out) { - if (src_results == nullptr && dst_params == nullptr) { - // Okay - continue; - } - if ((src_results == nullptr && dst_params != nullptr) || - (src_results != nullptr && dst_params == nullptr)) { - error(src->token, "Deferred procedure '%.*s' parameters do not match the results of initial procedure '%.*s'", LIT(src->token.string), LIT(dst->token.string)); - continue; - } - - GB_ASSERT(src_results->kind == Type_Tuple); - GB_ASSERT(dst_params->kind == Type_Tuple); - - if (are_types_identical(src_results, dst_params)) { - // Okay! - } else { - gbString s = type_to_string(src_results); - gbString d = type_to_string(dst_params); - error(src->token, "Deferred procedure '%.*s' parameters do not match the results of initial procedure '%.*s':\n\t(%s) =/= (%s)", - LIT(src->token.string), LIT(dst->token.string), - s, d - ); - gb_string_free(d); - gb_string_free(s); - continue; - } - } else if (dst_kind == DeferredProcedure_in_out) { - if (src_params == nullptr && src_results == nullptr && dst_params == nullptr) { - // Okay - continue; - } - - GB_ASSERT(dst_params->kind == Type_Tuple); - - Type *tsrc = alloc_type_tuple(); - auto &sv = tsrc->Tuple.variables; - auto const &dv = dst_params->Tuple.variables; - gb_unused(dv); - - isize len = 0; - if (src_params != nullptr) { GB_ASSERT(src_params->kind == Type_Tuple); - len += src_params->Tuple.variables.count; - } - if (src_results != nullptr) { + GB_ASSERT(dst_params->kind == Type_Tuple); + + if (are_types_identical(src_params, dst_params)) { + // Okay! + } else { + gbString s = type_to_string(src_params); + gbString d = type_to_string(dst_params); + error(src->token, "Deferred procedure '%.*s' parameters do not match the inputs of initial procedure '%.*s':\n\t(%s) =/= (%s)", + LIT(src->token.string), LIT(dst->token.string), + s, d + ); + gb_string_free(d); + gb_string_free(s); + continue; + } + } break; + case DeferredProcedure_out: + case DeferredProcedure_out_by_ptr: + { + if (src_results == nullptr && dst_params == nullptr) { + // Okay + continue; + } + if ((src_results == nullptr && dst_params != nullptr) || + (src_results != nullptr && dst_params == nullptr)) { + error(src->token, "Deferred procedure '%.*s' parameters do not match the results of initial procedure '%.*s'", LIT(src->token.string), LIT(dst->token.string)); + continue; + } + GB_ASSERT(src_results->kind == Type_Tuple); - len += src_results->Tuple.variables.count; - } - slice_init(&sv, heap_allocator(), len); - isize offset = 0; - if (src_params != nullptr) { - for_array(i, src_params->Tuple.variables) { - sv[offset++] = src_params->Tuple.variables[i]; + GB_ASSERT(dst_params->kind == Type_Tuple); + + if (are_types_identical(src_results, dst_params)) { + // Okay! + } else { + gbString s = type_to_string(src_results); + gbString d = type_to_string(dst_params); + error(src->token, "Deferred procedure '%.*s' parameters do not match the results of initial procedure '%.*s':\n\t(%s) =/= (%s)", + LIT(src->token.string), LIT(dst->token.string), + s, d + ); + gb_string_free(d); + gb_string_free(s); + continue; } - } - if (src_results != nullptr) { - for_array(i, src_results->Tuple.variables) { - sv[offset++] = src_results->Tuple.variables[i]; + } break; + case DeferredProcedure_in_out: + case DeferredProcedure_in_out_by_ptr: + { + if (src_params == nullptr && src_results == nullptr && dst_params == nullptr) { + // Okay + continue; } - } - GB_ASSERT(offset == len); + + GB_ASSERT(dst_params->kind == Type_Tuple); + + Type *tsrc = alloc_type_tuple(); + auto &sv = tsrc->Tuple.variables; + auto const &dv = dst_params->Tuple.variables; + gb_unused(dv); + + isize len = 0; + if (src_params != nullptr) { + GB_ASSERT(src_params->kind == Type_Tuple); + len += src_params->Tuple.variables.count; + } + if (src_results != nullptr) { + GB_ASSERT(src_results->kind == Type_Tuple); + len += src_results->Tuple.variables.count; + } + slice_init(&sv, heap_allocator(), len); + isize offset = 0; + if (src_params != nullptr) { + for_array(i, src_params->Tuple.variables) { + sv[offset++] = src_params->Tuple.variables[i]; + } + } + if (src_results != nullptr) { + for_array(i, src_results->Tuple.variables) { + sv[offset++] = src_results->Tuple.variables[i]; + } + } + GB_ASSERT(offset == len); - if (are_types_identical(tsrc, dst_params)) { - // Okay! - } else { - gbString s = type_to_string(tsrc); - gbString d = type_to_string(dst_params); - error(src->token, "Deferred procedure '%.*s' parameters do not match the results of initial procedure '%.*s':\n\t(%s) =/= (%s)", - LIT(src->token.string), LIT(dst->token.string), - s, d - ); - gb_string_free(d); - gb_string_free(s); - continue; - } + if (are_types_identical(tsrc, dst_params)) { + // Okay! + } else { + gbString s = type_to_string(tsrc); + gbString d = type_to_string(dst_params); + error(src->token, "Deferred procedure '%.*s' parameters do not match the results of initial procedure '%.*s':\n\t(%s) =/= (%s)", + LIT(src->token.string), LIT(dst->token.string), + s, d + ); + gb_string_free(d); + gb_string_free(s); + continue; + } + } break; } } diff --git a/src/checker.hpp b/src/checker.hpp index 2918b7e83..1a95e2772 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -92,6 +92,10 @@ enum DeferredProcedureKind { DeferredProcedure_in, DeferredProcedure_out, DeferredProcedure_in_out, + + DeferredProcedure_in_by_ptr, + DeferredProcedure_out_by_ptr, + DeferredProcedure_in_out_by_ptr, }; struct DeferredProcedure { DeferredProcedureKind kind; diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 02748663b..93c480e7f 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -1169,17 +1169,27 @@ gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array c lbValue deferred = lb_find_procedure_value_from_entity(p->module, deferred_entity); + bool by_ptr = false; auto in_args = args; Array result_as_args = {}; switch (kind) { case DeferredProcedure_none: break; + case DeferredProcedure_in_by_ptr: + by_ptr = true; + /*fallthrough*/ case DeferredProcedure_in: result_as_args = array_clone(heap_allocator(), in_args); break; + case DeferredProcedure_out_by_ptr: + by_ptr = true; + /*fallthrough*/ case DeferredProcedure_out: result_as_args = lb_value_to_array(p, heap_allocator(), result); break; + case DeferredProcedure_in_out_by_ptr: + by_ptr = true; + /*fallthrough*/ case DeferredProcedure_in_out: { auto out_args = lb_value_to_array(p, heap_allocator(), result); @@ -1189,6 +1199,12 @@ gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array c } break; } + if (by_ptr) { + for_array(i, result_as_args) { + lbValue arg_ptr = lb_address_from_load_or_generate_local(p, result_as_args[i]); + result_as_args[i] = arg_ptr; + } + } lb_add_defer_proc(p, p->scope_index, deferred, result_as_args); }