From 95873e66ab8cfe6fe7be3842e4a753961fd0bfb3 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 14 Dec 2018 21:05:02 +0000 Subject: [PATCH] `deferred` procedure attribute --- src/check_decl.cpp | 5 ++++ src/checker.cpp | 75 ++++++++++++++++++++++++++++++++++++++++++++-- src/checker.hpp | 1 + src/entity.cpp | 9 ++++++ src/ir.cpp | 67 ++++++++++++++++++++++++++++++++++++----- 5 files changed, 147 insertions(+), 10 deletions(-) diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 3fb53bb19..6723b6754 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -633,6 +633,11 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { e->Procedure.link_name = ac.link_name; } + if (ac.deferred_procedure != nullptr) { + e->Procedure.deferred_procedure = ac.deferred_procedure; + array_add(&ctx->checker->procs_with_deferred_to_check, e); + } + if (is_foreign) { String name = e->token.string; if (e->Procedure.link_name.len > 0) { diff --git a/src/checker.cpp b/src/checker.cpp index a5e58f980..7f56770b9 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -778,6 +778,7 @@ void init_checker(Checker *c, Parser *parser) { init_checker_info(&c->info); array_init(&c->procs_to_check, a); + array_init(&c->procs_with_deferred_to_check, a); // NOTE(bill): Is this big enough or too small? isize item_size = gb_max3(gb_size_of(Entity), gb_size_of(Type), gb_size_of(Scope)); @@ -793,6 +794,7 @@ void destroy_checker(Checker *c) { destroy_checker_info(&c->info); array_free(&c->procs_to_check); + array_free(&c->procs_with_deferred_to_check); destroy_checker_context(&c->init_ctx); } @@ -1030,6 +1032,10 @@ void add_entity_use(CheckerContext *c, Ast *identifier, Entity *entity) { } entity->flags |= EntityFlag_Used; add_declaration_dependency(c, entity); + if (entity_has_deferred_procedure(entity)) { + Entity *deferred = entity->Procedure.deferred_procedure; + add_entity_use(c, nullptr, deferred); + } } @@ -1890,9 +1896,24 @@ DECL_ATTRIBUTE_PROC(foreign_block_decl_attribute) { } DECL_ATTRIBUTE_PROC(proc_decl_attribute) { - ExactValue ev = check_decl_attribute_value(c, value); + ExactValue ev = {}; + if (name != "deferred") { + ev = check_decl_attribute_value(c, value); + } - if (name == "link_name") { + if (name == "deferred") { + if (value != nullptr) { + Operand o = {}; + check_expr(c, &o, value); + Entity *e = entity_of_ident(o.expr); + if (e != nullptr && e->kind == Entity_Procedure) { + ac->deferred_procedure = e; + return true; + } + } + error(elem, "Expected a procedure entity for '%.*s'", LIT(name)); + return false; + } else if (name == "link_name") { if (ev.kind == ExactValue_String) { ac->link_name = ev.value_string; if (!is_foreign_name_valid(ac->link_name)) { @@ -3539,6 +3560,56 @@ void check_parsed_files(Checker *c) { } } + TIME_SECTION("check deferred procedures"); + for_array(i, c->procs_with_deferred_to_check) { + Entity *src = c->procs_with_deferred_to_check[i]; + GB_ASSERT(src->kind == Entity_Procedure); + + Entity *dst = src->Procedure.deferred_procedure; + GB_ASSERT(dst != nullptr); + GB_ASSERT(dst->kind == Entity_Procedure); + + if (is_type_polymorphic(src->type) || is_type_polymorphic(dst->type)) { + error(src->token, "'deferred' cannot be used with a polymorphic procedure"); + continue; + } + + GB_ASSERT(is_type_proc(src->type)); + GB_ASSERT(is_type_proc(dst->type)); + Type *src_results = base_type(src->type)->Proc.results; + Type *dst_params = base_type(dst->type)->Proc.params; + 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); + + auto const &sv = src_results->Tuple.variables; + auto const &dv = dst_params->Tuple.variables; + + 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; + } + } + + TIME_SECTION("check entry point"); if (!build_context.is_dll) { Scope *s = c->info.init_scope; diff --git a/src/checker.hpp b/src/checker.hpp index ef0b27ab0..e7b689b17 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -477,6 +477,7 @@ struct Checker { CheckerInfo info; Array procs_to_check; + Array procs_with_deferred_to_check; gbAllocator allocator; CheckerContext init_ctx; diff --git a/src/entity.cpp b/src/entity.cpp index f3e11492a..f4bc99247 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -127,6 +127,7 @@ struct Entity { Ast * foreign_library_ident; String link_name; String link_prefix; + Entity *deferred_procedure; bool is_foreign; bool is_export; } Procedure; @@ -184,6 +185,14 @@ bool is_entity_exported(Entity *e, bool allow_builtin = false) { return name[0] != '_'; } +bool entity_has_deferred_procedure(Entity *e) { + GB_ASSERT(e != nullptr); + if (e->kind == Entity_Procedure) { + return e->Procedure.deferred_procedure != nullptr; + } + return false; +} + gb_global u64 global_entity_id = 0; diff --git a/src/ir.cpp b/src/ir.cpp index c2739b387..9ddc182c5 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -859,6 +859,10 @@ void ir_pop_debug_location (irModule *m); irDebugInfo *ir_add_debug_info_local(irProcedure *proc, Entity *e, i32 arg_id); irDebugInfo *ir_add_debug_info_file(irModule *module, AstFile *file); irDebugInfo *ir_add_debug_info_proc(irProcedure *proc); +void ir_emit_increment(irProcedure *proc, irValue *addr); +irValue *ir_emit_array_ep(irProcedure *proc, irValue *s, irValue *index); +irValue *ir_emit_array_epi(irProcedure *proc, irValue *s, i32 index); +irValue *ir_emit_struct_ev(irProcedure *proc, irValue *s, i32 index); irValue *ir_emit_byte_swap(irProcedure *proc, irValue *value, Type *t); @@ -1274,6 +1278,17 @@ irValue *ir_emit(irProcedure *proc, irValue *instr) { return instr; } +irValue *ir_de_emit(irProcedure *proc, irValue *instr) { + GB_ASSERT(instr->kind == irValue_Instr); + irModule *m = proc->module; + irBlock *b = proc->curr_block; + GB_ASSERT(b != nullptr); + irInstr *i = ir_get_last_instr(b); + GB_ASSERT(i == &instr->Instr); + array_pop(&b->instrs); + return instr; +} + irValue *ir_const_int(i64 i) { @@ -2800,6 +2815,28 @@ irValue *ir_find_or_generate_context_ptr(irProcedure *proc) { return c; } +Array ir_value_to_array(irProcedure *p, irValue *value) { + Array array = {}; + Type *t = base_type(ir_type(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(ir_allocator(), rt->variables.count); + for_array(i, rt->variables) { + irValue *elem = ir_emit_struct_ev(p, value, cast(i32)i); + array[i] = elem; + } + } + } else { + array = array_make(ir_allocator(), 1); + array[0] = value; + } + return array; +} + irValue *ir_emit_call(irProcedure *p, irValue *value, Array args, ProcInlining inlining = ProcInlining_none) { Type *pt = base_type(ir_type(value)); @@ -2847,18 +2884,36 @@ irValue *ir_emit_call(irProcedure *p, irValue *value, Array args, Pro inlining = p->inlining; } + irValue *result = nullptr; + Type *abi_rt = pt->Proc.abi_compat_result_type; Type *rt = reduce_tuple_to_single_type(results); if (pt->Proc.return_by_pointer) { irValue *return_ptr = ir_add_local_generated(p, rt); GB_ASSERT(is_type_pointer(ir_type(return_ptr))); ir_emit(p, ir_instr_call(p, value, return_ptr, args, nullptr, context_ptr, inlining)); - return ir_emit_load(p, return_ptr); + result = ir_emit_load(p, return_ptr); + } else { + result = ir_emit(p, ir_instr_call(p, value, nullptr, args, abi_rt, context_ptr, inlining)); + if (abi_rt != results) { + result = ir_emit_transmute(p, result, rt); + } } - irValue *result = ir_emit(p, ir_instr_call(p, value, nullptr, args, abi_rt, context_ptr, inlining)); - if (abi_rt != results) { - result = ir_emit_transmute(p, result, rt); + if (value->kind == irValue_Proc) { + irProcedure *the_proc = &value->Proc; + Entity *e = the_proc->entity; + if (entity_has_deferred_procedure(e)) { + Entity *deferred_entity = e->Procedure.deferred_procedure; + irValue **deferred_found = map_get(&p->module->values, hash_entity(deferred_entity)); + GB_ASSERT(deferred_found != nullptr); + irValue *deferred = *deferred_found; + + Array result_as_args = ir_value_to_array(p, result); + + irValue *deferred_call = ir_de_emit(p, ir_emit_call(p, deferred, result_as_args)); + ir_add_defer_instr(p, p->scope_index, deferred_call); + } } return result; @@ -3396,10 +3451,6 @@ irValue *ir_map_cap(irProcedure *proc, irValue *value) { } -void ir_emit_increment(irProcedure *proc, irValue *addr); -irValue *ir_emit_array_ep(irProcedure *proc, irValue *s, irValue *index); -irValue *ir_emit_array_epi(irProcedure *proc, irValue *s, i32 index); -irValue *ir_emit_struct_ev(irProcedure *proc, irValue *s, i32 index); struct irLoopData { irValue *idx_addr;