mirror of
https://github.com/odin-lang/Odin.git
synced 2026-06-05 02:04:06 +00:00
Temporarily disable -threaded-checker; Restructure the untyped-expr-info system to be much more thread-friendly
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
198
src/checker.cpp
198
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
|
||||
|
||||
@@ -259,6 +259,13 @@ struct AtomOpMapEntry {
|
||||
|
||||
struct CheckerContext;
|
||||
|
||||
struct UntypedExprInfo {
|
||||
Ast *expr;
|
||||
ExprInfo *info;
|
||||
};
|
||||
|
||||
typedef Map<ExprInfo *> 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<Entity *> minimum_dependency_set;
|
||||
PtrSet<isize> 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<Entity *> 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<ExprInfo *> 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<Array<Entity *> > gen_procs; // Key: Ast * | Identifier -> Entity
|
||||
Map<Array<Entity *> > gen_types; // Key: Type *
|
||||
|
||||
@@ -311,6 +315,8 @@ struct CheckerInfo {
|
||||
|
||||
Array<Entity *> required_global_variables;
|
||||
Array<Entity *> required_foreign_imports_through_force;
|
||||
|
||||
MPMCQueue<UntypedExprInfo> 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<ProcInfo *> 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);
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include <atomic> // 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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#include <atomic> // Because I wanted the C++11 memory order semantics, of which gb.h does not offer (because it was a C89 library)
|
||||
|
||||
template <typename T>
|
||||
struct MPMCQueueNode {
|
||||
T data;
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user