diff --git a/src/check_expr.cpp b/src/check_expr.cpp index a0565faaa..0c8bea126 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -87,7 +87,7 @@ void check_not_tuple (CheckerContext *c, Operand *operand); void convert_to_typed (CheckerContext *c, Operand *operand, Type *target_type); gbString expr_to_string (Ast *expression); void check_proc_body (CheckerContext *c, Token token, DeclInfo *decl, Type *type, Ast *body); -void update_expr_type (CheckerContext *c, Ast *e, Type *type, bool final); +void update_untyped_expr_type (CheckerContext *c, Ast *e, Type *type, bool final); bool check_is_terminating (Ast *node, String const &label); bool check_has_break (Ast *stmt, String const &label, bool implicit); void check_stmt (CheckerContext *c, Ast *node, u32 flags); @@ -215,6 +215,7 @@ bool find_or_generate_polymorphic_procedure(CheckerContext *c, Entity *base_enti // // /////////////////////////////////////////////////////////////////////////////// + CheckerInfo *info = c->info; if (base_entity == nullptr) { return false; @@ -224,6 +225,10 @@ bool find_or_generate_polymorphic_procedure(CheckerContext *c, Entity *base_enti return false; } + + gb_mutex_lock(&info->gen_procs_mutex); + defer (gb_mutex_unlock(&info->gen_procs_mutex)); + String name = base_entity->token.string; Type *src = base_type(base_entity->type); @@ -280,7 +285,6 @@ bool find_or_generate_polymorphic_procedure(CheckerContext *c, Entity *base_enti }); - CheckerInfo *info = c->info; CheckerContext nctx = *c; Scope *scope = create_scope(c->info, base_entity->scope); @@ -297,6 +301,8 @@ bool find_or_generate_polymorphic_procedure(CheckerContext *c, Entity *base_enti auto *pt = &src->Proc; + + // NOTE(bill): This is slightly memory leaking if the type already exists // Maybe it's better to check with the previous types first? Type *final_proc_type = alloc_type_proc(scope, nullptr, 0, nullptr, 0, false, pt->calling_convention); @@ -306,8 +312,6 @@ bool find_or_generate_polymorphic_procedure(CheckerContext *c, Entity *base_enti return false; } - gb_mutex_lock(&info->gen_procs_mutex); - defer (gb_mutex_unlock(&info->gen_procs_mutex)); auto *found_gen_procs = map_get(&info->gen_procs, hash_pointer(base_entity->identifier)); if (found_gen_procs) { // gb_mutex_lock(&info->gen_procs_mutex); @@ -632,6 +636,8 @@ i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type if (are_types_identical(src, dst)) { return 3; } + gb_mutex_lock(&c->checker->poly_proc_mutex); + defer (gb_mutex_unlock(&c->checker->poly_proc_mutex)); PolyProcData poly_proc_data = {}; if (check_polymorphic_procedure_assignment(c, operand, type, operand->expr, &poly_proc_data)) { Entity *e = poly_proc_data.gen_entity; @@ -2099,8 +2105,8 @@ void check_comparison(CheckerContext *c, Operand *x, Operand *y, TokenKind op) { } else { x->mode = Addressing_Value; - update_expr_type(c, x->expr, default_type(x->type), true); - update_expr_type(c, y->expr, default_type(y->type), true); + update_untyped_expr_type(c, x->expr, default_type(x->type), true); + update_untyped_expr_type(c, y->expr, default_type(y->type), true); i64 size = 0; if (!is_type_untyped(x->type)) size = gb_max(size, type_size_of(x->type)); @@ -2226,18 +2232,14 @@ void check_shift(CheckerContext *c, Operand *x, Operand *y, Ast *node, Type *typ TokenPos pos = ast_token(x->expr).pos; if (x_is_untyped) { - gb_mutex_lock(&c->info->untyped_mutex); - ExprInfo *info = check_get_expr_info(c->info, x->expr); - if (info != nullptr) { - info->is_lhs = true; + if (x->expr != nullptr) { + x->expr->tav.is_lhs = true; } x->mode = Addressing_Value; if (type_hint && is_type_integer(type_hint)) { x->type = type_hint; } // x->value = x_val; - - gb_mutex_unlock(&c->info->untyped_mutex); return; } } @@ -2484,7 +2486,7 @@ void check_cast(CheckerContext *c, Operand *x, Type *type) { if (is_const_expr && !is_type_constant_type(type)) { final_type = default_type(x->type); } - update_expr_type(c, x->expr, final_type, true); + update_untyped_expr_type(c, x->expr, final_type, true); } if (build_context.vet_extra) { @@ -2920,11 +2922,9 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Type *type_hint } -void update_expr_type(CheckerContext *c, Ast *e, Type *type, bool final) { +void update_untyped_expr_type(CheckerContext *c, Ast *e, Type *type, bool final) { GB_ASSERT(e != nullptr); - gb_mutex_lock(&c->info->untyped_mutex); - defer (gb_mutex_unlock(&c->info->untyped_mutex)); - ExprInfo *old = check_get_expr_info(c->info, e); + ExprInfo *old = check_get_expr_info(c, e); if (old == nullptr) { if (type != nullptr && type != t_invalid) { if (e->tav.type == nullptr || e->tav.type == t_invalid) { @@ -2942,7 +2942,7 @@ void update_expr_type(CheckerContext *c, Ast *e, Type *type, bool final) { // checked at the end of general checking stage. break; } - update_expr_type(c, ue->expr, type, final); + update_untyped_expr_type(c, ue->expr, type, final); case_end; case_ast_node(be, BinaryExpr, e); @@ -2953,10 +2953,10 @@ void update_expr_type(CheckerContext *c, Ast *e, Type *type, bool final) { if (token_is_comparison(be->op.kind)) { // NOTE(bill): Do nothing as the types are fine } else if (token_is_shift(be->op.kind)) { - update_expr_type(c, be->left, type, final); + update_untyped_expr_type(c, be->left, type, final); } else { - update_expr_type(c, be->left, type, final); - update_expr_type(c, be->right, type, final); + update_untyped_expr_type(c, be->left, type, final); + update_untyped_expr_type(c, be->right, type, final); } case_end; @@ -2966,8 +2966,8 @@ void update_expr_type(CheckerContext *c, Ast *e, Type *type, bool final) { break; } - update_expr_type(c, te->x, type, final); - update_expr_type(c, te->y, type, final); + update_untyped_expr_type(c, te->x, type, final); + update_untyped_expr_type(c, te->y, type, final); case_end; case_ast_node(te, TernaryWhenExpr, e); @@ -2976,12 +2976,12 @@ void update_expr_type(CheckerContext *c, Ast *e, Type *type, bool final) { break; } - update_expr_type(c, te->x, type, final); - update_expr_type(c, te->y, type, final); + update_untyped_expr_type(c, te->x, type, final); + update_untyped_expr_type(c, te->y, type, final); case_end; case_ast_node(pe, ParenExpr, e); - update_expr_type(c, pe->expr, type, final); + update_untyped_expr_type(c, pe->expr, type, final); case_end; } @@ -2991,7 +2991,7 @@ void update_expr_type(CheckerContext *c, Ast *e, Type *type, bool final) { } // We need to remove it and then give it a new one - map_remove(&c->info->untyped, hash_node(e)); + check_remove_expr_info(c, e); if (old->is_lhs && !is_type_integer(type)) { gbString expr_str = expr_to_string(e); @@ -3005,11 +3005,9 @@ void update_expr_type(CheckerContext *c, Ast *e, Type *type, bool final) { add_type_and_value(c->info, e, old->mode, type, old->value); } -void update_expr_value(CheckerContext *c, Ast *e, ExactValue value) { - ExprInfo *found = nullptr; - gb_mutex_lock(&c->info->untyped_mutex); - found = check_get_expr_info(c->info, e); - gb_mutex_unlock(&c->info->untyped_mutex); +void update_untyped_expr_value(CheckerContext *c, Ast *e, ExactValue value) { + GB_ASSERT(e != nullptr); + ExprInfo *found = check_get_expr_info(c, e); if (found) { found->value = value; } @@ -3072,7 +3070,7 @@ void convert_to_typed(CheckerContext *c, Operand *operand, Type *target_type) { if (is_type_numeric(operand->type) && is_type_numeric(target_type)) { if (x_kind < y_kind) { operand->type = target_type; - update_expr_type(c, operand->expr, target_type, false); + update_untyped_expr_type(c, operand->expr, target_type, false); } } else if (x_kind != y_kind) { operand->mode = Addressing_Invalid; @@ -3094,7 +3092,7 @@ void convert_to_typed(CheckerContext *c, Operand *operand, Type *target_type) { if (operand->mode == Addressing_Invalid) { return; } - update_expr_value(c, operand->expr, operand->value); + update_untyped_expr_value(c, operand->expr, operand->value); } else { switch (operand->type->Basic.kind) { case Basic_UntypedBool: @@ -3271,7 +3269,7 @@ void convert_to_typed(CheckerContext *c, Operand *operand, Type *target_type) { break; } - update_expr_type(c, operand->expr, target_type, true); + update_untyped_expr_type(c, operand->expr, target_type, true); operand->type = target_type; } @@ -4411,9 +4409,10 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { } } else { // NOTE(bill): Generate the procedure type for this generic instance - PolyProcData poly_proc_data = {}; - if (pt->is_polymorphic && !pt->is_poly_specialized) { + gb_mutex_lock(&c->checker->poly_proc_mutex); + + PolyProcData poly_proc_data = {}; if (find_or_generate_polymorphic_procedure_from_parameters(c, entity, &operands, call, &poly_proc_data)) { gen_entity = poly_proc_data.gen_entity; GB_ASSERT(is_type_proc(gen_entity->type)); @@ -4421,6 +4420,8 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { } else { err = CallArgumentError_WrongTypes; } + + gb_mutex_unlock(&c->checker->poly_proc_mutex); } GB_ASSERT(is_type_proc(final_proc_type)); @@ -4495,7 +4496,7 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { add_type_info_type(c, o.type); add_type_and_value(c->info, o.expr, Addressing_Value, e->type, exact_value_typeid(o.type)); } else if (show_error && is_type_untyped(o.type)) { - update_expr_type(c, o.expr, t, true); + update_untyped_expr_type(c, o.expr, t, true); } } @@ -4546,7 +4547,7 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { add_type_info_type(c, o.type); add_type_and_value(c->info, o.expr, Addressing_Value, t, exact_value_typeid(o.type)); } else if (show_error && is_type_untyped(o.type)) { - update_expr_type(c, o.expr, t, true); + update_untyped_expr_type(c, o.expr, t, true); } } } @@ -4693,6 +4694,7 @@ CALL_ARGUMENT_CHECKER(check_named_call_arguments) { Entity *gen_entity = nullptr; if (pt->is_polymorphic && !pt->is_poly_specialized && err == CallArgumentError_None) { + gb_mutex_lock(&c->checker->poly_proc_mutex); PolyProcData poly_proc_data = {}; if (find_or_generate_polymorphic_procedure_from_parameters(c, entity, &ordered_operands, call, &poly_proc_data)) { gen_entity = poly_proc_data.gen_entity; @@ -4701,6 +4703,7 @@ CALL_ARGUMENT_CHECKER(check_named_call_arguments) { proc_type = gept; pt = &gept->Proc; } + gb_mutex_unlock(&c->checker->poly_proc_mutex); } @@ -5039,7 +5042,7 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type Entity *entity_to_use = data.gen_entity != nullptr ? data.gen_entity : e; add_entity_use(c, ident, entity_to_use); if (entity_to_use != nullptr) { - update_expr_type(c, operand->expr, entity_to_use->type, true); + update_untyped_expr_type(c, operand->expr, entity_to_use->type, true); } return data; } @@ -5313,7 +5316,7 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type Entity *entity_to_use = data.gen_entity != nullptr ? data.gen_entity : e; add_entity_use(c, ident, entity_to_use); if (entity_to_use != nullptr) { - update_expr_type(c, operand->expr, entity_to_use->type, true); + update_untyped_expr_type(c, operand->expr, entity_to_use->type, true); } if (data.gen_entity != nullptr) { @@ -5346,7 +5349,7 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type Entity *entity_to_use = data.gen_entity != nullptr ? data.gen_entity : e; add_entity_use(c, ident, entity_to_use); if (entity_to_use != nullptr) { - update_expr_type(c, operand->expr, entity_to_use->type, true); + update_untyped_expr_type(c, operand->expr, entity_to_use->type, true); } if (data.gen_entity != nullptr) { @@ -5775,6 +5778,7 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *pr operand->type = t_invalid;; return Expr_Expr; } + gb_mutex_lock(&c->checker->poly_type_mutex); auto err = check_polymorphic_record_type(c, operand, call); if (err == 0) { @@ -5792,6 +5796,8 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *pr operand->mode = Addressing_Invalid; operand->type = t_invalid; } + + gb_mutex_unlock(&c->checker->poly_type_mutex); } else { gbString str = type_to_string(t); defer (gb_string_free(str)); @@ -5821,7 +5827,7 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *pr operand->type = t; operand->expr = call; if (operand->mode != Addressing_Invalid) { - update_expr_type(c, arg, t, false); + update_untyped_expr_type(c, arg, t, false); } break; } @@ -6555,7 +6561,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type if (type_hint != nullptr && is_type_untyped(type)) { if (check_cast_internal(c, &x, type_hint) && check_cast_internal(c, &y, type_hint)) { - update_expr_type(c, node, type_hint, !is_type_untyped(type_hint)); + update_untyped_expr_type(c, node, type_hint, !is_type_untyped(type_hint)); o->type = type_hint; } } @@ -8251,7 +8257,7 @@ ExprKind check_expr_base(CheckerContext *c, Operand *o, Ast *node, Type *type_hi gb_string_free(xs); } if (o->type != nullptr && is_type_untyped(o->type)) { - add_untyped(c->info, node, false, o->mode, o->type, o->value); + add_untyped(c, node, o->mode, o->type, o->value); } add_type_and_value(c->info, node, o->mode, o->type, o->value); return kind; diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 1d7cf3cce..8a7555945 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1103,7 +1103,7 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { if (y.mode != Addressing_Constant) { continue; } - update_expr_type(ctx, z.expr, x.type, !is_type_untyped(x.type)); + update_untyped_expr_type(ctx, z.expr, x.type, !is_type_untyped(x.type)); add_constant_switch_case(ctx, &seen, y); } } @@ -1706,7 +1706,7 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { Operand *o = &operands[i]; check_assignment(ctx, o, e->type, str_lit("return statement")); if (is_type_untyped(o->type)) { - update_expr_type(ctx, o->expr, e->type, true); + update_untyped_expr_type(ctx, o->expr, e->type, true); } } } diff --git a/src/checker.cpp b/src/checker.cpp index 529311baa..b23ac9e2d 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -4,7 +4,6 @@ void check_expr(CheckerContext *c, Operand *operand, Ast *expression); void check_expr_or_type(CheckerContext *c, Operand *operand, Ast *expression, Type *type_hint=nullptr); void add_comparison_procedures_for_fields(CheckerContext *c, Type *t); -void check_proc_info(Checker *c, ProcInfo *pi); bool is_operand_value(Operand o) { @@ -835,7 +834,7 @@ void init_checker_info(CheckerInfo *i) { gbAllocator a = heap_allocator(); array_init(&i->definitions, a); array_init(&i->entities, a); - map_init(&i->untyped, a); + map_init(&i->global_untyped, a); string_map_init(&i->foreigns, a); map_init(&i->gen_procs, a); map_init(&i->gen_types, a); @@ -847,6 +846,7 @@ void init_checker_info(CheckerInfo *i) { array_init(&i->required_foreign_imports_through_force, a); array_init(&i->required_global_variables, a); array_init(&i->testing_procedures, a, 0, 0); + mpmc_init(&i->untyped_queue, heap_allocator(), 1<<20); i->allow_identifier_uses = build_context.query_data_set_settings.kind == QueryDataSet_GoToDefinitions; @@ -854,7 +854,6 @@ void init_checker_info(CheckerInfo *i) { array_init(&i->identifier_uses, a); } - gb_mutex_init(&i->untyped_mutex); gb_mutex_init(&i->gen_procs_mutex); gb_mutex_init(&i->gen_types_mutex); gb_mutex_init(&i->type_info_mutex); @@ -869,7 +868,7 @@ void init_checker_info(CheckerInfo *i) { void destroy_checker_info(CheckerInfo *i) { array_free(&i->definitions); array_free(&i->entities); - map_destroy(&i->untyped); + map_destroy(&i->global_untyped); string_map_destroy(&i->foreigns); map_destroy(&i->gen_procs); map_destroy(&i->gen_types); @@ -881,8 +880,8 @@ void destroy_checker_info(CheckerInfo *i) { array_free(&i->identifier_uses); array_free(&i->required_foreign_imports_through_force); array_free(&i->required_global_variables); + mpmc_destroy(&i->untyped_queue); - gb_mutex_destroy(&i->untyped_mutex); gb_mutex_destroy(&i->gen_procs_mutex); gb_mutex_destroy(&i->gen_types_mutex); gb_mutex_destroy(&i->type_info_mutex); @@ -956,6 +955,9 @@ bool init_checker(Checker *c, Parser *parser) { // NOTE(bill): 1 Mi elements should be enough on average mpmc_init(&c->procs_to_check_queue, heap_allocator(), 1<<20); gb_semaphore_init(&c->procs_to_check_semaphore); + + gb_mutex_init(&c->poly_type_mutex); + gb_mutex_init(&c->poly_proc_mutex); return true; } @@ -1041,13 +1043,23 @@ AstFile *ast_file_of_filename(CheckerInfo *i, String filename) { Scope *scope_of_node(Ast *node) { return node->scope; } -ExprInfo *check_get_expr_info(CheckerInfo *i, Ast *expr) { - ExprInfo *res = nullptr; - ExprInfo **found = map_get(&i->untyped, hash_node(expr)); +ExprInfo *check_get_expr_info(CheckerContext *c, Ast *expr) { + UntypedExprInfoMap *untyped = c->untyped ? c->untyped : &c->info->global_untyped; + ExprInfo **found = map_get(untyped, hash_pointer(expr)); if (found) { - res = *found; + return *found; } - return res; + return nullptr; +} + +void check_set_expr_info(CheckerContext *c, Ast *expr, AddressingMode mode, Type *type, ExactValue value) { + UntypedExprInfoMap *untyped = c->untyped ? c->untyped : &c->info->global_untyped; + map_set(untyped, hash_pointer(expr), make_expr_info(mode, type, value, false)); +} + +void check_remove_expr_info(CheckerContext *c, Ast *e) { + UntypedExprInfoMap *untyped = c->untyped ? c->untyped : &c->info->global_untyped; + map_remove(untyped, hash_pointer(e)); } @@ -1089,19 +1101,21 @@ isize type_info_index(CheckerInfo *info, Type *type, bool error_on_failure) { } -void add_untyped(CheckerInfo *i, Ast *expression, bool lhs, AddressingMode mode, Type *type, ExactValue value) { - if (expression == nullptr) { +void add_untyped(CheckerContext *c, Ast *expr, AddressingMode mode, Type *type, ExactValue value) { + if (expr == nullptr) { return; } if (mode == Addressing_Invalid) { return; } + if (mode == Addressing_Constant && type == t_invalid) { compiler_error("add_untyped - invalid type: %s", type_to_string(type)); } - gb_mutex_lock(&i->untyped_mutex); - map_set(&i->untyped, hash_node(expression), make_expr_info(mode, type, value, lhs)); - gb_mutex_unlock(&i->untyped_mutex); + if (!is_type_untyped(type)) { + return; + } + check_set_expr_info(c, expr, mode, type, value); } void add_type_and_value(CheckerInfo *i, Ast *expr, AddressingMode mode, Type *type, ExactValue value) { @@ -3336,12 +3350,14 @@ void check_single_global_entity(Checker *c, Entity *e, DeclInfo *d) { } void check_all_global_entities(Checker *c) { - Scope *prev_file = nullptr; - for_array(i, c->info.entities) { Entity *e = c->info.entities[i]; DeclInfo *d = e->decl_info; check_single_global_entity(c, e, d); + if (e->type != nullptr && is_type_typed(e->type)) { + (void)type_size_of(e->type); + (void)type_align_of(e->type); + } } } @@ -4258,7 +4274,7 @@ void calculate_global_init_order(Checker *c) { } -void check_proc_info(Checker *c, ProcInfo *pi) { +void check_proc_info(Checker *c, ProcInfo *pi, UntypedExprInfoMap *untyped) { if (pi == nullptr) { return; } @@ -4273,6 +4289,7 @@ void check_proc_info(Checker *c, ProcInfo *pi) { defer (destroy_checker_context(&ctx)); reset_checker_context(&ctx, pi->file); ctx.decl = pi->decl; + ctx.untyped = untyped; TypeProc *pt = &pi->type->Proc; String name = pi->token.string; @@ -4312,24 +4329,21 @@ void check_proc_info(Checker *c, ProcInfo *pi) { pi->decl->entity->flags |= EntityFlag_ProcBodyChecked; } pi->decl->proc_checked = true; - + add_untyped_expressions(&c->info, ctx.untyped); } GB_STATIC_ASSERT(sizeof(isize) == sizeof(void *)); -GB_THREAD_PROC(check_proc_info_worker_proc) { - if (thread == nullptr) return 0; - auto *c = cast(Checker *)thread->user_data; - ProcInfo *pi = cast(ProcInfo *)cast(uintptr)thread->user_index; - check_proc_info(c, pi); - return 0; -} - void check_unchecked_bodies(Checker *c) { // NOTE(2021-02-26, bill): Sanity checker // This is a partial hack to make sure all procedure bodies have been checked // even ones which should not exist, due to the multithreaded nature of the parser // HACK TODO(2021-02-26, bill): Actually fix this race condition + + UntypedExprInfoMap untyped = {}; + map_init(&untyped, heap_allocator()); + defer (map_destroy(&untyped)); + for_array(i, c->info.minimum_dependency_set.entries) { Entity *e = c->info.minimum_dependency_set.entries[i].ptr; if (e == nullptr || e->kind != Entity_Procedure) { @@ -4355,7 +4369,8 @@ void check_unchecked_bodies(Checker *c) { continue; } - check_proc_info(c, &pi); + map_clear(&untyped); + check_proc_info(c, &pi, &untyped); } } } @@ -4399,6 +4414,10 @@ GB_THREAD_PROC(thread_proc_body) { auto *q = &c->procs_to_check_queue; ProcInfo *pi = nullptr; + UntypedExprInfoMap untyped = {}; + map_init(&untyped, heap_allocator()); + defer (map_destroy(&untyped)); + while (proc_bodies_is_running) { gb_semaphore_wait(&c->procs_to_check_semaphore); @@ -4413,7 +4432,8 @@ GB_THREAD_PROC(thread_proc_body) { continue; } } - check_proc_info(c, pi); + map_clear(&untyped); + check_proc_info(c, pi, &untyped); } } @@ -4426,81 +4446,34 @@ void check_procedure_bodies(Checker *c) { auto *q = &c->procs_to_check_queue; ProcInfo *pi = nullptr; - isize thread_count = gb_max(build_context.thread_count, 1); - isize worker_count = thread_count-1; // NOTE(bill): The main thread will also be used for work - if (!build_context.threaded_checker) { - worker_count = 0; + while (mpmc_dequeue(q, &pi)) { + GB_ASSERT(pi->decl != nullptr); + if (pi->decl->parent && pi->decl->parent->entity) { + Entity *parent = pi->decl->parent->entity; + // NOTE(bill): Only check a nested procedure if its parent's body has been checked first + // This is prevent any possible race conditions in evaluation when multithreaded + // NOTE(bill): In single threaded mode, this should never happen + if (parent->kind == Entity_Procedure && (parent->flags & EntityFlag_ProcBodyChecked) == 0) { + mpmc_enqueue(q, pi); + continue; + } + } + check_proc_info(c, pi, nullptr); } +} - if (worker_count == 0) { - while (mpmc_dequeue(q, &pi)) { - if (pi->decl->parent && pi->decl->parent->entity) { - Entity *parent = pi->decl->parent->entity; - // NOTE(bill): Only check a nested procedure if its parent's body has been checked first - // This is prevent any possible race conditions in evaluation when multithreaded - // NOTE(bill): In single threaded mode, this should never happen - if (parent->kind == Entity_Procedure && (parent->flags & EntityFlag_ProcBodyChecked) == 0) { - mpmc_enqueue(q, pi); - continue; - } - } - check_proc_info(c, pi); - } - } else { - proc_bodies_is_running = true; - - gbThread threads[64] = {}; - for (isize i = 0; i < worker_count; i++) { - gb_thread_init(threads+i); - } - - for (isize i = 0; i < worker_count; i++) { - gb_thread_start(threads+i, thread_proc_body, c); - } - - while (q->count.load(std::memory_order_relaxed) > 0) { - if (mpmc_dequeue(q, &pi)) { - if (pi->decl->parent && pi->decl->parent->entity) { - Entity *parent = pi->decl->parent->entity; - // NOTE(bill): Only check a nested procedure if its parent's body has been checked first - // This is prevent any possible race conditions in evaluation when multithreaded - // NOTE(bill): In single threaded mode, this should never happen - if (parent->kind == Entity_Procedure && (parent->flags & EntityFlag_ProcBodyChecked) == 0) { - mpmc_enqueue(q, pi); - - gb_yield(); - continue; - } - } - check_proc_info(c, pi); - } - - gb_yield(); - } - - proc_bodies_is_running = false; - gb_semaphore_post(&c->procs_to_check_semaphore, cast(i32)worker_count); - - gb_yield(); - - for (isize i = 0; i < worker_count; i++) { - gb_thread_destroy(threads+i); - } - - while (mpmc_dequeue(q, &pi)) { - if (pi->decl->parent && pi->decl->parent->entity) { - Entity *parent = pi->decl->parent->entity; - // NOTE(bill): Only check a nested procedure if its parent's body has been checked first - // This is prevent any possible race conditions in evaluation when multithreaded - // NOTE(bill): In single threaded mode, this should never happen - if (parent->kind == Entity_Procedure && (parent->flags & EntityFlag_ProcBodyChecked) == 0) { - mpmc_enqueue(q, pi); - continue; - } - } - check_proc_info(c, pi); +void add_untyped_expressions(CheckerInfo *cinfo, UntypedExprInfoMap *untyped) { + if (untyped == nullptr) { + return; + } + for_array(i, untyped->entries) { + Ast *expr = cast(Ast *)cast(uintptr)untyped->entries[i].key.key; + ExprInfo *info = untyped->entries[i].value; + if (expr != nullptr && info != nullptr) { + mpmc_enqueue(&cinfo->untyped_queue, UntypedExprInfo{expr, info}); } } + map_clear(untyped); } @@ -4557,6 +4530,9 @@ void check_parsed_files(Checker *c) { TIME_SECTION("init preload"); init_preload(c); + TIME_SECTION("add global untyped expression to queue"); + add_untyped_expressions(&c->info, &c->info.global_untyped); + CheckerContext prev_context = c->builtin_ctx; defer (c->builtin_ctx = prev_context); c->builtin_ctx.decl = make_decl_info(nullptr, nullptr); @@ -4585,24 +4561,12 @@ void check_parsed_files(Checker *c) { TIME_SECTION("add untyped expression values"); // Add untyped expression values - for_array(i, c->info.untyped.entries) { - auto *entry = &c->info.untyped.entries[i]; - HashKey key = entry->key; - Ast *expr = cast(Ast *)cast(uintptr)key.key; - ExprInfo *info = entry->value; - if (info != nullptr && expr != nullptr) { - if (is_type_typed(info->type)) { - compiler_error("%s (type %s) is typed!", expr_to_string(expr), type_to_string(info->type)); - } - if (info->mode == Addressing_Constant) { - } else if (info->type == t_untyped_nil) { - } else if (info->type == t_untyped_undef) { - } else if (info->type == t_untyped_bool) { - } else { - // gb_printf_err("UNTYPED %s %s\n", expr_to_string(expr), type_to_string(info->type)); - } - add_type_and_value(&c->info, expr, info->mode, info->type, info->value); + for (UntypedExprInfo u = {}; mpmc_dequeue(&c->info.untyped_queue, &u); /**/) { + GB_ASSERT(u.expr != nullptr && u.info != nullptr); + if (is_type_typed(u.info->type)) { + compiler_error("%s (type %s) is typed!", expr_to_string(u.expr), type_to_string(u.info->type)); } + add_type_and_value(&c->info, u.expr, u.info->mode, u.info->type, u.info->value); } // TODO(bill): Check for unused imports (and remove) or even warn/err diff --git a/src/checker.hpp b/src/checker.hpp index b760482ff..98aba8273 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -259,6 +259,13 @@ struct AtomOpMapEntry { struct CheckerContext; +struct UntypedExprInfo { + Ast *expr; + ExprInfo *info; +}; + +typedef Map UntypedExprInfoMap; // Key: Ast * + // CheckerInfo stores all the symbol information for a type-checked program struct CheckerInfo { Checker *checker; @@ -275,6 +282,8 @@ struct CheckerInfo { PtrSet minimum_dependency_set; PtrSet minimum_dependency_type_info_set; + UntypedExprInfoMap global_untyped; // NOTE(bill): This needs to be a map and not on the Ast + // as it needs to be iterated across afterwards Array testing_procedures; @@ -282,7 +291,6 @@ struct CheckerInfo { // NOTE(bill): If the semantic checker (check_proc_body) is to ever to be multithreaded, // these variables will be of contention - gbMutex untyped_mutex; gbMutex gen_procs_mutex; gbMutex gen_types_mutex; gbMutex type_info_mutex; @@ -292,10 +300,6 @@ struct CheckerInfo { gbMutex foreign_mutex; gbMutex scope_mutex; - Map untyped; // Key: Ast * | Expression -> ExprInfo * - // NOTE(bill): This needs to be a map and not on the Ast - // as it needs to be iterated across - Map > gen_procs; // Key: Ast * | Identifier -> Entity Map > gen_types; // Key: Type * @@ -311,6 +315,8 @@ struct CheckerInfo { Array required_global_variables; Array required_foreign_imports_through_force; + + MPMCQueue untyped_queue; }; struct CheckerContext { @@ -337,6 +343,8 @@ struct CheckerContext { CheckerPolyPath *poly_path; isize poly_level; // TODO(bill): Actually handle correctly + UntypedExprInfoMap *untyped; + #define MAX_INLINE_FOR_DEPTH 1024ll i64 inline_for_depth; @@ -362,6 +370,9 @@ struct Checker { MPMCQueue procs_to_check_queue; gbSemaphore procs_to_check_semaphore; + + gbMutex poly_type_mutex; + gbMutex poly_proc_mutex; }; @@ -399,9 +410,9 @@ void scope_lookup_parent (Scope *s, String const &name, Scope **scope_, Entit Entity *scope_insert (Scope *s, Entity *entity); -ExprInfo *check_get_expr_info (CheckerInfo *i, Ast *expr); -void add_untyped (CheckerInfo *i, Ast *expression, bool lhs, AddressingMode mode, Type *basic_type, ExactValue value); void add_type_and_value (CheckerInfo *i, Ast *expression, AddressingMode mode, Type *type, ExactValue value); +ExprInfo *check_get_expr_info (CheckerContext *c, Ast *expr); +void add_untyped (CheckerContext *c, Ast *expression, AddressingMode mode, Type *basic_type, ExactValue value); void add_entity_use (CheckerContext *c, Ast *identifier, Entity *entity); void add_implicit_entity (CheckerContext *c, Ast *node, Entity *e); void add_entity_and_decl_info(CheckerContext *c, Ast *identifier, Entity *e, DeclInfo *d, bool is_exported=true); @@ -434,3 +445,5 @@ Type *check_poly_path_pop (CheckerContext *c); void init_core_context(Checker *c); void init_mem_allocator(Checker *c); + +void add_untyped_expressions(CheckerInfo *cinfo, UntypedExprInfoMap *untyped); diff --git a/src/common.cpp b/src/common.cpp index 211fd4be4..9d528d723 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -27,6 +27,7 @@ #include #include +#include // Because I wanted the C++11 memory order semantics, of which gb.h does not offer (because it was a C89 library) gb_inline void zero_size(void *ptr, isize len) { memset(ptr, 0, len); diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 95ab55134..d04344c1f 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -6580,6 +6580,7 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc } LLVMValueRef str_len = LLVMConstInt(lb_type(m, t_int), value.value_string.len, true); LLVMValueRef values[2] = {ptr, str_len}; + GB_ASSERT(is_type_string(original_type)); res.value = llvm_const_named_struct(lb_type(m, original_type), values, 2); } diff --git a/src/queue.cpp b/src/queue.cpp index 048f8fcb9..92d83a76c 100644 --- a/src/queue.cpp +++ b/src/queue.cpp @@ -1,5 +1,3 @@ -#include // Because I wanted the C++11 memory order semantics, of which gb.h does not offer (because it was a C89 library) - template struct MPMCQueueNode { T data; diff --git a/src/timings.cpp b/src/timings.cpp index 594f6d27e..b1fbdb952 100644 --- a/src/timings.cpp +++ b/src/timings.cpp @@ -104,14 +104,14 @@ u64 time_stamp__freq(void) { #endif } -TimeStamp make_time_stamp(String label) { +TimeStamp make_time_stamp(String const &label) { TimeStamp ts = {0}; ts.start = time_stamp_time_now(); ts.label = label; return ts; } -void timings_init(Timings *t, String label, isize buffer_size) { +void timings_init(Timings *t, String const &label, isize buffer_size) { array_init(&t->sections, heap_allocator(), 0, buffer_size); t->total = make_time_stamp(label); t->freq = time_stamp__freq(); @@ -127,7 +127,8 @@ void timings__stop_current_section(Timings *t) { } } -void timings_start_section(Timings *t, String label) { +void timings_start_section(Timings *t, String const &label) { + // gb_printf_err("[%.*s]\n", LIT(label)); timings__stop_current_section(t); array_add(&t->sections, make_time_stamp(label)); }