Add experimental support for a threaded semantic checker to -threaded-checker

This commit is contained in:
gingerBill
2021-07-10 23:51:37 +01:00
parent 690374d4de
commit d9e6ade030
12 changed files with 252 additions and 101 deletions

View File

@@ -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;

View File

@@ -208,6 +208,7 @@ struct BuildContext {
bool linker_map_file;
bool use_separate_modules;
bool threaded_checker;
u32 cmd_doc_flags;
Array<String> extra_packages;

View File

@@ -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<Entity *>(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) {

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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);
}
}

View File

@@ -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<Entity *>(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) {

View File

@@ -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);
}

View File

@@ -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<ExprInfo *> 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);

View File

@@ -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,
};

View File

@@ -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<String> 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<String> 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;

View File

@@ -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<Type *> *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<i64> type_set_offsets_of(Array<Entity *> 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: {