From d9e6ade03007f4ede6471a6ada23b2469e2f052d Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 10 Jul 2021 23:51:37 +0100 Subject: [PATCH] Add experimental support for a threaded semantic checker to `-threaded-checker` --- src/big_int.cpp | 4 +- src/build_settings.cpp | 1 + src/check_builtin.cpp | 6 +- src/check_decl.cpp | 3 +- src/check_expr.cpp | 94 ++++++++++++++-------- src/check_stmt.cpp | 6 +- src/check_type.cpp | 15 ++-- src/checker.cpp | 171 ++++++++++++++++++++++++++++++----------- src/checker.hpp | 3 +- src/exact_value.cpp | 22 +++--- src/main.cpp | 12 +++ src/types.cpp | 16 +++- 12 files changed, 252 insertions(+), 101 deletions(-) diff --git a/src/big_int.cpp b/src/big_int.cpp index 9e57b3fc2..168e53fb2 100644 --- a/src/big_int.cpp +++ b/src/big_int.cpp @@ -587,7 +587,9 @@ void big_int_add(BigInt *dst, BigInt const *x, BigInt const *y) { } } - GB_ASSERT(overflow == 0); + if (overflow != 0) { + GB_ASSERT_MSG(overflow == 0, "%p %p %p", dst, x, y); + } dst->len = i; big_int_normalize(dst); return; diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 3ddec8628..ecca85e4f 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -208,6 +208,7 @@ struct BuildContext { bool linker_map_file; bool use_separate_modules; + bool threaded_checker; u32 cmd_doc_flags; Array extra_packages; diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index cbf49a2c7..2d25aaae4 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -1686,7 +1686,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 Ast *dummy_node_struct = alloc_ast_node(nullptr, Ast_Invalid); Ast *dummy_node_soa = alloc_ast_node(nullptr, Ast_Invalid); - Scope *s = create_scope(builtin_pkg->scope); + Scope *s = create_scope(c->info, builtin_pkg->scope); auto fields = array_make(permanent_allocator(), 0, types.count); for_array(i, types) { @@ -1917,7 +1917,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 soa_struct->Struct.soa_elem = elem; soa_struct->Struct.soa_count = count; - scope = create_scope(c->scope); + scope = create_scope(c->info, c->scope); soa_struct->Struct.scope = scope; String params_xyzw[4] = { @@ -1950,7 +1950,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 soa_struct->Struct.soa_elem = elem; soa_struct->Struct.soa_count = count; - scope = create_scope(old_struct->Struct.scope->parent); + scope = create_scope(c->info, old_struct->Struct.scope->parent); soa_struct->Struct.scope = scope; for_array(i, old_struct->Struct.fields) { diff --git a/src/check_decl.cpp b/src/check_decl.cpp index d7f04ca5c..8b66452f3 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -1276,7 +1276,8 @@ void check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *decl, Type *ty Entity *uvar = using_entities[i].uvar; Entity *prev = scope_insert(ctx->scope, uvar); if (prev != nullptr) { - error(e->token, "Namespace collision while 'using' '%.*s' of: %.*s", LIT(e->token.string), LIT(prev->token.string)); + error(e->token, "Namespace collision while 'using' procedure argument '%.*s' of: %.*s", LIT(e->token.string), LIT(prev->token.string)); + error_line("%.*s != %.*s\n", LIT(uvar->token.string), LIT(prev->token.string)); break; } } diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 3ff002eb1..455fed21a 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -283,7 +283,7 @@ bool find_or_generate_polymorphic_procedure(CheckerContext *c, Entity *base_enti CheckerInfo *info = c->info; CheckerContext nctx = *c; - Scope *scope = create_scope(base_entity->scope); + Scope *scope = create_scope(c->info, base_entity->scope); scope->flags |= ScopeFlag_Proc; nctx.scope = scope; nctx.allow_polymorphic_types = true; @@ -307,11 +307,11 @@ bool find_or_generate_polymorphic_procedure(CheckerContext *c, Entity *base_enti } 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)); - gb_mutex_unlock(&info->gen_procs_mutex); if (found_gen_procs) { - gb_mutex_lock(&info->gen_procs_mutex); - defer (gb_mutex_unlock(&info->gen_procs_mutex)); + // gb_mutex_lock(&info->gen_procs_mutex); + // defer (gb_mutex_unlock(&info->gen_procs_mutex)); auto procs = *found_gen_procs; for_array(i, procs) { @@ -349,8 +349,8 @@ bool find_or_generate_polymorphic_procedure(CheckerContext *c, Entity *base_enti } if (found_gen_procs) { - gb_mutex_lock(&info->gen_procs_mutex); - defer (gb_mutex_unlock(&info->gen_procs_mutex)); + // gb_mutex_lock(&info->gen_procs_mutex); + // defer (gb_mutex_unlock(&info->gen_procs_mutex)); auto procs = *found_gen_procs; for_array(i, procs) { @@ -421,7 +421,7 @@ bool find_or_generate_polymorphic_procedure(CheckerContext *c, Entity *base_enti proc_info->generated_from_polymorphic = true; proc_info->poly_def_node = poly_def_node; - gb_mutex_lock(&info->gen_procs_mutex); + // gb_mutex_lock(&info->gen_procs_mutex); if (found_gen_procs) { array_add(found_gen_procs, entity); } else { @@ -429,7 +429,7 @@ bool find_or_generate_polymorphic_procedure(CheckerContext *c, Entity *base_enti array_add(&array, entity); map_set(&info->gen_procs, hash_pointer(base_entity->identifier), array); } - gb_mutex_unlock(&info->gen_procs_mutex); + // gb_mutex_unlock(&info->gen_procs_mutex); GB_ASSERT(entity != nullptr); @@ -1738,7 +1738,12 @@ void check_cast_error_suggestion(CheckerContext *c, Operand *o, Type *type) { void check_is_expressible(CheckerContext *ctx, Operand *o, Type *type) { GB_ASSERT(o->mode == Addressing_Constant); - if (!is_type_constant_type(type) || !check_representable_as_constant(ctx, o->value, type, &o->value)) { + ExactValue out_value = o->value; + if (is_type_constant_type(type) && check_representable_as_constant(ctx, o->value, type, &out_value)) { + o->value = out_value; + } else { + o->value = out_value; + gbString a = expr_to_string(o->expr); gbString b = type_to_string(type); gbString c = type_to_string(o->type); @@ -1753,7 +1758,13 @@ void check_is_expressible(CheckerContext *ctx, Operand *o, Type *type) { if (!is_type_integer(o->type) && is_type_integer(type)) { error(o->expr, "'%s' truncated to '%s'", a, b); } else { - error(o->expr, "Cannot convert '%s' to '%s' from '%s", a, b, c); + #if 0 + gb_printf_err("AddressingMode, %d\n", o->mode); + gb_printf_err("ExactValueKind, %d\n", o->value.kind); + bool ok = check_representable_as_constant(ctx, o->value, type, &out_value); + gb_printf_err("ok, %d\n", ok); + #endif + error(o->expr, "Cannot convert numeric value '%s' to '%s' from '%s", a, b, c); check_assignment_error_suggestion(ctx, o, type); } } else { @@ -1797,10 +1808,6 @@ bool check_is_not_addressable(CheckerContext *c, Operand *o) { void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast *node) { switch (op.kind) { case Token_And: { // Pointer address - if (node->kind == Ast_TypeAssertion) { - gb_printf_err("%s\n", expr_to_string(node)); - } - if (check_is_not_addressable(c, o)) { if (ast_node_expect(node, Ast_UnaryExpr)) { ast_node(ue, UnaryExpr, node); @@ -2225,7 +2232,8 @@ void check_shift(CheckerContext *c, Operand *x, Operand *y, Ast *node, Type *typ TokenPos pos = ast_token(x->expr).pos; if (x_is_untyped) { - ExprInfo *info = check_get_expr_info(&c->checker->info, x->expr); + gb_mutex_lock(&c->info->untyped_mutex); + ExprInfo *info = check_get_expr_info(c->info, x->expr); if (info != nullptr) { info->is_lhs = true; } @@ -2234,6 +2242,8 @@ void check_shift(CheckerContext *c, Operand *x, Operand *y, Ast *node, Type *typ x->type = type_hint; } // x->value = x_val; + + gb_mutex_unlock(&c->info->untyped_mutex); return; } } @@ -2519,6 +2529,25 @@ bool check_transmute(CheckerContext *c, Ast *node, Operand *o, Type *t) { return false; } + Type *dst_bt = base_type(t); + if (dst_bt == nullptr || dst_bt == t_invalid) { + GB_ASSERT(global_error_collector.count != 0); + + o->mode = Addressing_Invalid; + o->expr = node; + return false; + } + + Type *src_bt = base_type(o->type); + if (src_bt == nullptr || src_bt == t_invalid) { + // NOTE(bill): this should be an error + GB_ASSERT(global_error_collector.count != 0); + o->mode = Addressing_Value; + o->expr = node; + o->type = t; + return true; + } + i64 srcz = type_size_of(o->type); i64 dstz = type_size_of(t); if (srcz != dstz) { @@ -2899,11 +2928,13 @@ 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) { GB_ASSERT(e != nullptr); - ExprInfo *old = check_get_expr_info(&c->checker->info, e); + gb_mutex_lock(&c->info->untyped_mutex); + defer (gb_mutex_unlock(&c->info->untyped_mutex)); + ExprInfo *old = check_get_expr_info(c->info, e); if (old == nullptr) { if (type != nullptr && type != t_invalid) { if (e->tav.type == nullptr || e->tav.type == t_invalid) { - add_type_and_value(&c->checker->info, e, e->tav.mode, type ? type : e->tav.type, e->tav.value); + add_type_and_value(c->info, e, e->tav.mode, type ? type : e->tav.type, e->tav.value); } } return; @@ -2966,7 +2997,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 - check_remove_expr_info(&c->checker->info, e); + map_remove(&c->info->untyped, hash_node(e)); if (old->is_lhs && !is_type_integer(type)) { gbString expr_str = expr_to_string(e); @@ -2977,11 +3008,14 @@ void update_expr_type(CheckerContext *c, Ast *e, Type *type, bool final) { return; } - add_type_and_value(&c->checker->info, e, old->mode, type, old->value); + add_type_and_value(c->info, e, old->mode, type, old->value); } void update_expr_value(CheckerContext *c, Ast *e, ExactValue value) { - ExprInfo *found = check_get_expr_info(&c->checker->info, e); + 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); if (found) { found->value = value; } @@ -3001,7 +3035,7 @@ void convert_untyped_error(CheckerContext *c, Operand *operand, Type *target_typ } } } - error(operand->expr, "Cannot convert '%s' to '%s' from '%s'%s", expr_str, type_str, from_type_str, extra_text); + error(operand->expr, "Cannot convert untyped value '%s' to '%s' from '%s'%s", expr_str, type_str, from_type_str, extra_text); gb_string_free(from_type_str); gb_string_free(type_str); @@ -5682,7 +5716,7 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *pr operand->builtin_id = BuiltinProc_DIRECTIVE; operand->expr = proc; operand->type = t_invalid; - add_type_and_value(&c->checker->info, proc, operand->mode, operand->type, operand->value); + add_type_and_value(c->info, proc, operand->mode, operand->type, operand->value); } else { GB_PANIC("Unhandled #%.*s", LIT(name)); } @@ -5757,7 +5791,7 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *pr GB_ASSERT(ot->kind == Type_Named); Entity *e = ot->Named.type_name; add_entity_use(c, ident, e); - add_type_and_value(&c->checker->info, call, Addressing_Type, ot, empty_exact_value); + add_type_and_value(c->info, call, Addressing_Type, ot, empty_exact_value); } else { operand->mode = Addressing_Invalid; operand->type = t_invalid; @@ -6153,8 +6187,8 @@ bool check_range(CheckerContext *c, Ast *node, Operand *x, Operand *y, ExactValu return false; } - add_type_and_value(&c->checker->info, ie->left, x->mode, x->type, x->value); - add_type_and_value(&c->checker->info, ie->right, y->mode, y->type, y->value); + add_type_and_value(c->info, ie->left, x->mode, x->type, x->value); + add_type_and_value(c->info, ie->right, y->mode, y->type, y->value); return true; } @@ -6284,7 +6318,7 @@ void check_promote_optional_ok(CheckerContext *c, Operand *x, Type **val_type_, Type *pt = base_type(type_of_expr(expr->CallExpr.proc)); if (is_type_proc(pt)) { Type *tuple = pt->Proc.results; - add_type_and_value(&c->checker->info, x->expr, x->mode, tuple, x->value); + add_type_and_value(c->info, x->expr, x->mode, tuple, x->value); if (pt->Proc.result_count >= 2) { if (ok_type_) *ok_type_ = tuple->Tuple.variables[1]->type; @@ -6297,7 +6331,7 @@ void check_promote_optional_ok(CheckerContext *c, Operand *x, Type **val_type_, Type *tuple = make_optional_ok_type(x->type); if (ok_type_) *ok_type_ = tuple->Tuple.variables[1]->type; - add_type_and_value(&c->checker->info, x->expr, x->mode, tuple, x->value); + add_type_and_value(c->info, x->expr, x->mode, tuple, x->value); x->type = tuple; GB_ASSERT(is_type_tuple(type_of_expr(x->expr))); } @@ -8163,7 +8197,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type error(x.expr, "Expected a constant string for the inline asm constraints parameter"); } - Scope *scope = create_scope(c->scope); + Scope *scope = create_scope(c->info, c->scope); scope->flags |= ScopeFlag_Proc; Type *params = alloc_type_tuple(); @@ -8221,9 +8255,9 @@ 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->checker->info, node, false, o->mode, o->type, o->value); + add_untyped(c->info, node, false, o->mode, o->type, o->value); } - add_type_and_value(&c->checker->info, 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 501fb7abf..1d7cf3cce 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -587,7 +587,7 @@ bool check_using_stmt_entity(CheckerContext *ctx, AstUsingStmt *us, Ast *expr, b Entity *found = scope_insert(ctx->scope, f); if (found != nullptr) { gbString expr_str = expr_to_string(expr); - error(us->token, "Namespace collision while 'using' '%s' of: %.*s", expr_str, LIT(found->token.string)); + error(us->token, "Namespace collision while 'using' enum '%s' of: %.*s", expr_str, LIT(found->token.string)); gb_string_free(expr_str); return false; } @@ -611,7 +611,7 @@ bool check_using_stmt_entity(CheckerContext *ctx, AstUsingStmt *us, Ast *expr, b if (found != nullptr) { gbString expr_str = expr_to_string(expr); error(us->token, - "Namespace collision while 'using' '%s' of: %.*s\n" + "Namespace collision while 'using' import name '%s' of: %.*s\n" "\tat %s\n" "\tat %s", expr_str, LIT(found->token.string), @@ -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)); add_constant_switch_case(ctx, &seen, y); } } diff --git a/src/check_type.cpp b/src/check_type.cpp index e7832272a..cc4ffebca 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -1260,7 +1260,10 @@ ParameterValue handle_parameter_value(CheckerContext *ctx, Type *in_type, Type * param_value.kind = ParameterValue_Constant; param_value.value = o.value; } else { - error(o.expr, "Invalid constant parameter"); + gbString s = expr_to_string(o.expr); + error(o.expr, "Invalid constant parameter, got '%s'", s); + // error(o.expr, "Invalid constant parameter, got '%s' %d %d", s, o.mode, o.value.kind); + gb_string_free(s); } } } @@ -2044,7 +2047,7 @@ void init_map_entry_type(Type *type) { } */ Ast *dummy_node = alloc_ast_node(nullptr, Ast_Invalid); - Scope *s = create_scope(builtin_pkg->scope); + Scope *s = create_scope(nullptr, builtin_pkg->scope); auto fields = array_make(permanent_allocator(), 0, 4); array_add(&fields, alloc_entity_field(s, make_token_ident(str_lit("hash")), t_uintptr, false, cast(i32)fields.count, EntityState_Resolved)); @@ -2078,7 +2081,7 @@ void init_map_internal_types(Type *type) { } */ Ast *dummy_node = alloc_ast_node(nullptr, Ast_Invalid); - Scope *s = create_scope(builtin_pkg->scope); + Scope *s = create_scope(nullptr, builtin_pkg->scope); Type *hashes_type = alloc_type_slice(t_int); Type *entries_type = alloc_type_dynamic_array(type->Map.entry_type); @@ -2211,7 +2214,7 @@ Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_expr, Ast *el soa_struct->Struct.soa_count = 0; soa_struct->Struct.is_polymorphic = true; - scope = create_scope(ctx->scope); + scope = create_scope(ctx->info, ctx->scope); soa_struct->Struct.scope = scope; } else if (is_type_array(elem)) { Type *old_array = base_type(elem); @@ -2225,7 +2228,7 @@ Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_expr, Ast *el soa_struct->Struct.soa_elem = elem; soa_struct->Struct.soa_count = count; - scope = create_scope(ctx->scope); + scope = create_scope(ctx->info, ctx->scope); soa_struct->Struct.scope = scope; String params_xyzw[4] = { @@ -2267,7 +2270,7 @@ Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_expr, Ast *el soa_struct->Struct.soa_elem = elem; soa_struct->Struct.soa_count = count; - scope = create_scope(old_struct->Struct.scope->parent); + scope = create_scope(ctx->info, old_struct->Struct.scope->parent); soa_struct->Struct.scope = scope; for_array(i, old_struct->Struct.fields) { diff --git a/src/checker.cpp b/src/checker.cpp index e7a53ded9..529311baa 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -224,7 +224,7 @@ bool decl_info_has_init(DeclInfo *d) { -Scope *create_scope(Scope *parent, isize init_elements_capacity=DEFAULT_SCOPE_CAPACITY) { +Scope *create_scope(CheckerInfo *info, Scope *parent, isize init_elements_capacity=DEFAULT_SCOPE_CAPACITY) { Scope *s = gb_alloc_item(permanent_allocator(), Scope); s->parent = parent; string_map_init(&s->elements, heap_allocator(), init_elements_capacity); @@ -234,7 +234,9 @@ Scope *create_scope(Scope *parent, isize init_elements_capacity=DEFAULT_SCOPE_CA s->delayed_directives.allocator = heap_allocator(); if (parent != nullptr && parent != builtin_pkg->scope) { + if (info) gb_mutex_lock(&info->scope_mutex); DLIST_APPEND(parent->first_child, parent->last_child, s); + if (info) gb_mutex_unlock(&info->scope_mutex); } if (parent != nullptr && parent->flags & ScopeFlag_ContextDefined) { @@ -244,12 +246,12 @@ Scope *create_scope(Scope *parent, isize init_elements_capacity=DEFAULT_SCOPE_CA return s; } -Scope *create_scope_from_file(AstFile *f) { +Scope *create_scope_from_file(CheckerInfo *info, AstFile *f) { GB_ASSERT(f != nullptr); GB_ASSERT(f->pkg != nullptr); GB_ASSERT(f->pkg->scope != nullptr); - Scope *s = create_scope(f->pkg->scope); + Scope *s = create_scope(info, f->pkg->scope); array_reserve(&s->delayed_imports, f->imports.count); array_reserve(&s->delayed_directives, f->directive_count); @@ -269,7 +271,7 @@ Scope *create_scope_from_package(CheckerContext *c, AstPackage *pkg) { decl_count += pkg->files[i]->decls.count; } isize init_elements_capacity = 2*decl_count; - Scope *s = create_scope(builtin_pkg->scope, init_elements_capacity); + Scope *s = create_scope(c->info, builtin_pkg->scope, init_elements_capacity); s->flags |= ScopeFlag_Pkg; s->pkg = pkg; @@ -329,7 +331,7 @@ void check_open_scope(CheckerContext *c, Ast *node) { GB_ASSERT(node->kind == Ast_Invalid || is_ast_stmt(node) || is_ast_type(node)); - Scope *scope = create_scope(c->scope); + Scope *scope = create_scope(c->info, c->scope); add_scope(c, node, scope); switch (node->kind) { case Ast_ProcType: @@ -715,7 +717,7 @@ AstPackage *create_builtin_package(char const *name) { pkg->name = make_string_c(name); pkg->kind = Package_Normal; - pkg->scope = create_scope(nullptr); + pkg->scope = create_scope(nullptr, nullptr); pkg->scope->flags |= ScopeFlag_Pkg | ScopeFlag_Global | ScopeFlag_Builtin; pkg->scope->pkg = pkg; return pkg; @@ -860,6 +862,7 @@ void init_checker_info(CheckerInfo *i) { gb_mutex_init(&i->identifier_uses_mutex); gb_mutex_init(&i->entity_mutex); gb_mutex_init(&i->foreign_mutex); + gb_mutex_init(&i->scope_mutex); } @@ -887,6 +890,7 @@ void destroy_checker_info(CheckerInfo *i) { gb_mutex_destroy(&i->identifier_uses_mutex); gb_mutex_destroy(&i->entity_mutex); gb_mutex_destroy(&i->foreign_mutex); + gb_mutex_destroy(&i->scope_mutex); } CheckerContext make_checker_context(Checker *c) { @@ -902,6 +906,10 @@ CheckerContext make_checker_context(Checker *c) { ctx.poly_level = 0; return ctx; } +void destroy_checker_context(CheckerContext *ctx) { + destroy_checker_type_path(ctx->type_path); + destroy_checker_poly_path(ctx->poly_path); +} void add_curr_ast_file(CheckerContext *ctx, AstFile *file) { if (file != nullptr) { @@ -917,30 +925,13 @@ void reset_checker_context(CheckerContext *ctx, AstFile *file) { if (ctx == nullptr) { return; } - auto checker = ctx->checker; - auto info = ctx->info; - auto type_path = ctx->type_path; - auto poly_path = ctx->poly_path; - array_clear(type_path); - array_clear(poly_path); - - gb_zero_item(ctx); - ctx->checker = checker; - ctx->info = info; - ctx->type_path = type_path; - ctx->poly_path = poly_path; - ctx->scope = builtin_pkg->scope; - ctx->pkg = builtin_pkg; - + destroy_checker_context(ctx); + *ctx = make_checker_context(ctx->checker); add_curr_ast_file(ctx, file); } -void destroy_checker_context(CheckerContext *ctx) { - destroy_checker_type_path(ctx->type_path); - destroy_checker_poly_path(ctx->poly_path); -} bool init_checker(Checker *c, Parser *parser) { c->parser = parser; @@ -1051,21 +1042,13 @@ Scope *scope_of_node(Ast *node) { return node->scope; } ExprInfo *check_get_expr_info(CheckerInfo *i, Ast *expr) { - gb_mutex_lock(&i->untyped_mutex); ExprInfo *res = nullptr; ExprInfo **found = map_get(&i->untyped, hash_node(expr)); if (found) { res = *found; } - gb_mutex_unlock(&i->untyped_mutex); return res; } -void check_remove_expr_info(CheckerInfo *i, Ast *expr) { - gb_mutex_lock(&i->untyped_mutex); - map_remove(&i->untyped, hash_node(expr)); - gb_mutex_unlock(&i->untyped_mutex); -} - isize type_info_index(CheckerInfo *info, Type *type, bool error_on_failure) { @@ -1527,6 +1510,7 @@ void check_procedure_later(Checker *c, ProcInfo *info) { GB_ASSERT(info->decl != nullptr); mpmc_enqueue(&c->procs_to_check_queue, info); + gb_semaphore_post(&c->procs_to_check_semaphore, 1); } void check_procedure_later(Checker *c, AstFile *file, Token token, DeclInfo *decl, Type *type, Ast *body, u64 tags) { @@ -4281,6 +4265,9 @@ void check_proc_info(Checker *c, ProcInfo *pi) { if (pi->type == nullptr) { return; } + if (pi->decl->proc_checked) { + return; + } CheckerContext ctx = make_checker_context(c); defer (destroy_checker_context(&ctx)); @@ -4316,11 +4303,16 @@ void check_proc_info(Checker *c, ProcInfo *pi) { ctx.state_flags |= StateFlag_no_bounds_check; ctx.state_flags &= ~StateFlag_bounds_check; } + if (pi->body != nullptr && pi->decl->entity != nullptr) { + GB_ASSERT((pi->decl->entity->flags & EntityFlag_ProcBodyChecked) == 0); + } check_proc_body(&ctx, pi->token, pi->decl, pi->type, pi->body); if (pi->body != nullptr && pi->decl->entity != nullptr) { pi->decl->entity->flags |= EntityFlag_ProcBodyChecked; } + pi->decl->proc_checked = true; + } GB_STATIC_ASSERT(sizeof(isize) == sizeof(void *)); @@ -4400,23 +4392,114 @@ void check_test_names(Checker *c) { } +static bool proc_bodies_is_running; + +GB_THREAD_PROC(thread_proc_body) { + Checker *c = cast(Checker *)thread->user_data; + auto *q = &c->procs_to_check_queue; + ProcInfo *pi = nullptr; + + while (proc_bodies_is_running) { + gb_semaphore_wait(&c->procs_to_check_semaphore); + + 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); + continue; + } + } + check_proc_info(c, pi); + } + } + + gb_semaphore_release(&c->procs_to_check_semaphore); + + return 0; +} void check_procedure_bodies(Checker *c) { auto *q = &c->procs_to_check_queue; ProcInfo *pi = nullptr; - 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; + 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; + } + + 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); } - check_proc_info(c, pi); } } @@ -4457,7 +4540,7 @@ void check_parsed_files(Checker *c) { AstFile *f = pkg->files[j]; string_map_set(&c->info.files, f->fullpath, f); - create_scope_from_file(f); + create_scope_from_file(nullptr, f); reset_checker_context(ctx, f); check_collect_entities(ctx, f->decls); } diff --git a/src/checker.hpp b/src/checker.hpp index 9395d1565..6a00ce0e1 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -144,6 +144,7 @@ struct DeclInfo { Type * gen_proc_type; // Precalculated bool is_using; bool where_clauses_evaluated; + bool proc_checked; CommentGroup *comment; CommentGroup *docs; @@ -289,6 +290,7 @@ struct CheckerInfo { gbMutex identifier_uses_mutex; gbMutex entity_mutex; 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 @@ -398,7 +400,6 @@ Entity *scope_insert (Scope *s, Entity *entity); ExprInfo *check_get_expr_info (CheckerInfo *i, Ast *expr); -void check_remove_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); void add_entity_use (CheckerContext *c, Ast *identifier, Entity *entity); diff --git a/src/exact_value.cpp b/src/exact_value.cpp index 12c14b4fa..9bcaf76de 100644 --- a/src/exact_value.cpp +++ b/src/exact_value.cpp @@ -27,18 +27,18 @@ Quaternion256 quaternion256_inverse(Quaternion256 x) { enum ExactValueKind { - ExactValue_Invalid, + ExactValue_Invalid = 0, - ExactValue_Bool, - ExactValue_String, - ExactValue_Integer, - ExactValue_Float, - ExactValue_Complex, - ExactValue_Quaternion, - ExactValue_Pointer, - ExactValue_Compound, // TODO(bill): Is this good enough? - ExactValue_Procedure, // TODO(bill): Is this good enough? - ExactValue_Typeid, + ExactValue_Bool = 1, + ExactValue_String = 2, + ExactValue_Integer = 3, + ExactValue_Float = 4, + ExactValue_Complex = 5, + ExactValue_Quaternion = 6, + ExactValue_Pointer = 7, + ExactValue_Compound = 8, // TODO(bill): Is this good enough? + ExactValue_Procedure = 9, // TODO(bill): Is this good enough? + ExactValue_Typeid = 10, ExactValue_Count, }; diff --git a/src/main.cpp b/src/main.cpp index 5222a0321..345642cc6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -600,6 +600,7 @@ enum BuildFlagKind { BuildFlag_NoEntryPoint, BuildFlag_UseLLD, BuildFlag_UseSeparateModules, + BuildFlag_ThreadedChecker, BuildFlag_Vet, BuildFlag_VetExtra, BuildFlag_UseLLVMApi, @@ -722,6 +723,7 @@ bool parse_build_flags(Array args) { add_flag(&build_flags, BuildFlag_NoEntryPoint, str_lit("no-entry-point"), BuildFlagParam_None, Command__does_check &~ Command_test); add_flag(&build_flags, BuildFlag_UseLLD, str_lit("lld"), BuildFlagParam_None, Command__does_build); add_flag(&build_flags, BuildFlag_UseSeparateModules,str_lit("use-separate-modules"),BuildFlagParam_None, Command__does_build); + add_flag(&build_flags, BuildFlag_ThreadedChecker, str_lit("threaded-checker"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_Vet, str_lit("vet"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_VetExtra, str_lit("vet-extra"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_UseLLVMApi, str_lit("llvm-api"), BuildFlagParam_None, Command__does_build); @@ -1206,6 +1208,10 @@ bool parse_build_flags(Array args) { build_context.use_separate_modules = true; break; + case BuildFlag_ThreadedChecker: + build_context.threaded_checker = true; + break; + case BuildFlag_Vet: build_context.vet = true; break; @@ -1746,6 +1752,11 @@ void print_show_help(String const arg0, String const &command) { } if (check) { + print_usage_line(1, "-threaded-checker"); + print_usage_line(1, "[EXPERIMENTAL]"); + print_usage_line(2, "Multithread the semantic checker stage"); + print_usage_line(0, ""); + print_usage_line(1, "-vet"); print_usage_line(2, "Do extra checks on the code"); print_usage_line(2, "Extra checks include:"); @@ -1960,6 +1971,7 @@ int main(int arg_count, char const **arg_ptr) { init_global_error_collector(); init_keyword_hash_table(); global_big_int_init(); + init_type_mutex(); if (!check_env()) { return 1; diff --git a/src/types.cpp b/src/types.cpp index 5f6fdbb34..dac2632a9 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -661,6 +661,8 @@ gb_global Type *t_map_header = nullptr; gb_global Type *t_equal_proc = nullptr; gb_global Type *t_hasher_proc = nullptr; +gb_global gbMutex g_type_mutex; + i64 type_size_of (Type *t); i64 type_align_of (Type *t); @@ -674,6 +676,10 @@ bool is_type_pointer(Type *t); bool is_type_slice(Type *t); bool is_type_integer(Type *t); +void init_type_mutex(void) { + gb_mutex_init(&g_type_mutex); +} + bool type_ptr_set_exists(PtrSet *s, Type *t) { if (ptr_set_exists(s, t)) { return true; @@ -2727,7 +2733,7 @@ void type_path_print_illegal_cycle(TypePath *tp, isize start_index) { GB_ASSERT(start_index < tp->path.count); Entity *e = tp->path[start_index]; GB_ASSERT(e != nullptr); - error(e->token, "Illegal declaration cycle of `%.*s`", LIT(e->token.string)); + error(e->token, "Illegal type declaration cycle of `%.*s`", LIT(e->token.string)); // NOTE(bill): Print cycle, if it's deep enough for (isize j = start_index; j < tp->path.count; j++) { Entity *e = tp->path[j]; @@ -2844,6 +2850,8 @@ i64 type_align_of_internal(Type *t, TypePath *path) { if (t->failure) { return FAILURE_ALIGNMENT; } + gb_mutex_lock(&g_type_mutex); + defer (gb_mutex_unlock(&g_type_mutex)); t = base_type(t); @@ -3038,6 +3046,9 @@ Array type_set_offsets_of(Array const &fields, bool is_packed, bo } bool type_set_offsets(Type *t) { + gb_mutex_lock(&g_type_mutex); + defer (gb_mutex_unlock(&g_type_mutex)); + t = base_type(t); if (t->kind == Type_Struct) { if (!t->Struct.are_offsets_set) { @@ -3066,6 +3077,9 @@ i64 type_size_of_internal(Type *t, TypePath *path) { if (t->failure) { return FAILURE_SIZE; } + gb_mutex_lock(&g_type_mutex); + defer (gb_mutex_unlock(&g_type_mutex)); + switch (t->kind) { case Type_Named: {