From 9cd5ea59dd091083555d7aea691fcd889e67fb67 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 27 Jul 2021 20:45:50 +0100 Subject: [PATCH] Big simplification and improvement of the entity collection system, reducing unneeded steps for packages --- core/sys/windows/types.odin | 2 + src/checker.cpp | 335 +++++++++++++++++------------------- src/checker.hpp | 4 +- src/llvm_backend.cpp | 279 +++++++++++++++++------------- src/parser.cpp | 7 + src/parser.hpp | 17 +- src/queue.cpp | 9 +- 7 files changed, 355 insertions(+), 298 deletions(-) diff --git a/core/sys/windows/types.odin b/core/sys/windows/types.odin index 146c1a813..bb60ae193 100644 --- a/core/sys/windows/types.odin +++ b/core/sys/windows/types.odin @@ -397,6 +397,8 @@ when size_of(uintptr) == 4 { szDescription: [WSADESCRIPTION_LEN + 1]u8, szSystemStatus: [WSASYS_STATUS_LEN + 1]u8, } +} else { + #panic("unknown word size"); } WSABUF :: struct { diff --git a/src/checker.cpp b/src/checker.cpp index b97cc5ee1..01bb97a97 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -228,9 +228,6 @@ Scope *create_scope(CheckerInfo *info, Scope *parent, isize init_elements_capaci string_map_init(&s->elements, heap_allocator(), init_elements_capacity); ptr_set_init(&s->imported, heap_allocator(), 0); - s->delayed_imports.allocator = heap_allocator(); - s->delayed_directives.allocator = heap_allocator(); - if (parent != nullptr && parent != builtin_pkg->scope) { Scope *prev_head_child = parent->head_child.exchange(s, std::memory_order_acq_rel); if (prev_head_child) { @@ -253,8 +250,6 @@ Scope *create_scope_from_file(CheckerInfo *info, AstFile *f) { isize init_elements_capacity = gb_max(DEFAULT_SCOPE_CAPACITY, 2*f->total_file_decl_count); Scope *s = create_scope(info, f->pkg->scope, init_elements_capacity); - array_reserve(&s->delayed_imports, f->imports.count); - array_reserve(&s->delayed_directives, f->directive_count); s->flags |= ScopeFlag_File; s->file = f; @@ -311,8 +306,6 @@ void destroy_scope(Scope *scope) { } string_map_destroy(&scope->elements); - array_free(&scope->delayed_imports); - array_free(&scope->delayed_directives); ptr_set_destroy(&scope->imported); // NOTE(bill): No need to free scope as it "should" be allocated in an arena (except for the global scope) @@ -872,6 +865,7 @@ void init_checker_info(CheckerInfo *i) { gb_mutex_init(&i->lazy_mutex); mutex_init(&i->type_info_mutex); + mutex_init(&i->scope_mutex); mutex_init(&i->deps_mutex); mutex_init(&i->identifier_uses_mutex); mutex_init(&i->foreign_mutex); @@ -903,6 +897,7 @@ void destroy_checker_info(CheckerInfo *i) { gb_mutex_destroy(&i->gen_types_mutex); gb_mutex_destroy(&i->lazy_mutex); mutex_destroy(&i->type_info_mutex); + mutex_destroy(&i->scope_mutex); mutex_destroy(&i->deps_mutex); mutex_destroy(&i->identifier_uses_mutex); mutex_destroy(&i->foreign_mutex); @@ -1352,15 +1347,22 @@ void add_entity_and_decl_info(CheckerContext *c, Ast *identifier, Entity *e, Dec if (e->scope != nullptr) { Scope *scope = e->scope; - if (scope->flags & ScopeFlag_File) { - if (is_entity_kind_exported(e->kind) && is_exported) { - AstPackage *pkg = scope->file->pkg; - GB_ASSERT(pkg->scope == scope->parent); - GB_ASSERT(c->pkg == pkg); - scope = pkg->scope; - } + if (scope->flags & ScopeFlag_File && is_entity_kind_exported(e->kind) && is_exported) { + AstPackage *pkg = scope->file->pkg; + GB_ASSERT(pkg->scope == scope->parent); + GB_ASSERT(c->pkg == pkg); + + // NOTE(bill): as multiple threads could be accessing this, it needs to be wrapped + // The current hash map for scopes is not thread safe + AstPackageExportedEntity ee = {identifier, e}; + mpmc_enqueue(&pkg->exported_entity_queue, ee); + + // mutex_lock(&c->info->scope_mutex); + // add_entity(c, pkg->scope, identifier, e); + // mutex_unlock(&c->info->scope_mutex); + } else { + add_entity(c, scope, identifier, e); } - add_entity(c, scope, identifier, e); } CheckerInfo *info = c->info; @@ -3330,14 +3332,23 @@ void check_add_foreign_block_decl(CheckerContext *ctx, Ast *decl) { // NOTE(bill): If file_scopes == nullptr, this will act like a local scope void check_collect_entities(CheckerContext *c, Slice const &nodes) { + AstFile *curr_file = nullptr; + if ((c->scope->flags&ScopeFlag_File) != 0) { + curr_file = c->scope->file; + GB_ASSERT(curr_file != nullptr); + } + + for_array(decl_index, nodes) { Ast *decl = nodes[decl_index]; if (!is_ast_decl(decl) && !is_ast_when_stmt(decl)) { - if ((c->scope->flags&ScopeFlag_File) != 0 && decl->kind == Ast_ExprStmt) { + if (curr_file && decl->kind == Ast_ExprStmt) { Ast *expr = decl->ExprStmt.expr; if (expr->kind == Ast_CallExpr && expr->CallExpr.proc->kind == Ast_BasicDirective) { if (c->collect_delayed_decls) { - array_add(&c->scope->delayed_directives, expr); + if (decl->state_flags & StateFlag_BeenHandled) return; + decl->state_flags |= StateFlag_BeenHandled; + mpmc_enqueue(&curr_file->delayed_decls_queues[AstDelayQueue_Expr], expr); } continue; } @@ -3358,15 +3369,14 @@ void check_collect_entities(CheckerContext *c, Slice const &nodes) { case_end; case_ast_node(id, ImportDecl, decl); - if ((c->scope->flags&ScopeFlag_File) == 0) { + if (curr_file == nullptr) { error(decl, "import declarations are only allowed in the file scope"); // NOTE(bill): _Should_ be caught by the parser // TODO(bill): Better error handling if it isn't continue; } - if (c->collect_delayed_decls) { - array_add(&c->scope->delayed_imports, decl); - } + // Will be handled later + mpmc_enqueue(&curr_file->delayed_decls_queues[AstDelayQueue_Import], decl); case_end; case_ast_node(fl, ForeignImportDecl, decl); @@ -3391,15 +3401,14 @@ void check_collect_entities(CheckerContext *c, Slice const &nodes) { } } + // NOTE(bill): 'when' stmts need to be handled after the other as the condition may refer to something // declared after this stmt in source - if ((c->scope->flags&ScopeFlag_File) == 0 || c->collect_delayed_decls) { - for_array(i, nodes) { - Ast *node = nodes[i]; - switch (node->kind) { - case_ast_node(ws, WhenStmt, node); - check_collect_entities_from_when_stmt(c, ws); - case_end; + if (curr_file == nullptr) { + for_array(decl_index, nodes) { + Ast *decl = nodes[decl_index]; + if (decl->kind == Ast_WhenStmt) { + check_collect_entities_from_when_stmt(c, &decl->WhenStmt); } } } @@ -3434,11 +3443,6 @@ void check_single_global_entity(Checker *c, Entity *e, DeclInfo *d) { ctx->decl = d; ctx->scope = d->scope; - if (!e->pkg->used) { - return; - } - - if (pkg->kind == Package_Init) { if (e->kind != Entity_Procedure && e->token.string == "main") { error(e->token, "'main' is reserved as the entry point procedure in the initial scope"); @@ -3733,11 +3737,9 @@ void check_add_import_decl(CheckerContext *ctx, Ast *decl) { if (id->fullpath == "builtin") { scope = builtin_pkg->scope; - builtin_pkg->used = true; force_use = true; } else if (id->fullpath == "intrinsics") { scope = intrinsics_pkg->scope; - intrinsics_pkg->used = true; force_use = true; } else { AstPackage **found = string_map_get(pkgs, id->fullpath); @@ -3750,7 +3752,6 @@ void check_add_import_decl(CheckerContext *ctx, Ast *decl) { GB_PANIC("Unable to find scope for package: %.*s", LIT(id->fullpath)); } else { AstPackage *pkg = *found; - pkg->used = true; scope = pkg->scope; } } @@ -3889,27 +3890,6 @@ void check_add_foreign_import_decl(CheckerContext *ctx, Ast *decl) { } } -bool collect_checked_packages_from_decl_list(Checker *c, Slice const &decls) { - bool new_files = false; - for_array(i, decls) { - Ast *decl = decls[i]; - switch (decl->kind) { - case_ast_node(id, ImportDecl, decl); - AstPackage **found = string_map_get(&c->info.packages, id->fullpath); - if (found == nullptr) { - continue; - } - AstPackage *pkg = *found; - if (!pkg->used) { - new_files = true; - pkg->used = true; - } - case_end; - } - } - return new_files; -} - // Returns true if a new package is present bool collect_file_decls(CheckerContext *ctx, Slice const &decls); bool collect_file_decls_from_when_stmt(CheckerContext *ctx, AstWhenStmt *ws); @@ -3933,11 +3913,11 @@ bool collect_when_stmt_from_file(CheckerContext *ctx, AstWhenStmt *ws) { error(ws->cond, "Invalid body for 'when' statement"); } else { if (ws->determined_cond) { - return collect_checked_packages_from_decl_list(ctx->checker, ws->body->BlockStmt.stmts); + // } else if (ws->else_stmt) { switch (ws->else_stmt->kind) { case Ast_BlockStmt: - return collect_checked_packages_from_decl_list(ctx->checker, ws->else_stmt->BlockStmt.stmts); + return false; case Ast_WhenStmt: return collect_when_stmt_from_file(ctx, &ws->else_stmt->WhenStmt); default: @@ -3986,68 +3966,77 @@ bool collect_file_decls_from_when_stmt(CheckerContext *ctx, AstWhenStmt *ws) { return false; } + +bool collect_file_decl(CheckerContext *ctx, Ast *decl) { + GB_ASSERT(ctx->scope->flags&ScopeFlag_File); + + AstFile *curr_file = ctx->scope->file; + GB_ASSERT(curr_file != nullptr); + + if (decl->state_flags & StateFlag_BeenHandled) { + return false; + } + + switch (decl->kind) { + case_ast_node(vd, ValueDecl, decl); + check_collect_value_decl(ctx, decl); + case_end; + + case_ast_node(id, ImportDecl, decl); + check_add_import_decl(ctx, decl); + case_end; + + case_ast_node(fl, ForeignImportDecl, decl); + check_add_foreign_import_decl(ctx, decl); + case_end; + + case_ast_node(fb, ForeignBlockDecl, decl); + check_add_foreign_block_decl(ctx, decl); + case_end; + + case_ast_node(ws, WhenStmt, decl); + if (!ws->is_cond_determined) { + if (collect_when_stmt_from_file(ctx, ws)) { + return true; + } + + CheckerContext nctx = *ctx; + nctx.collect_delayed_decls = true; + + if (collect_file_decls_from_when_stmt(&nctx, ws)) { + return true; + } + } else { + CheckerContext nctx = *ctx; + nctx.collect_delayed_decls = true; + + if (collect_file_decls_from_when_stmt(&nctx, ws)) { + return true; + } + } + case_end; + + case_ast_node(es, ExprStmt, decl); + GB_ASSERT(ctx->collect_delayed_decls); + decl->state_flags |= StateFlag_BeenHandled; + if (es->expr->kind == Ast_CallExpr) { + ast_node(ce, CallExpr, es->expr); + if (ce->proc->kind == Ast_BasicDirective) { + mpmc_enqueue(&curr_file->delayed_decls_queues[AstDelayQueue_Expr], es->expr); + } + } + case_end; + } + + return false; +} + bool collect_file_decls(CheckerContext *ctx, Slice const &decls) { GB_ASSERT(ctx->scope->flags&ScopeFlag_File); - if (collect_checked_packages_from_decl_list(ctx->checker, decls)) { - return true; - } - for_array(i, decls) { - Ast *decl = decls[i]; - if (decl->state_flags & StateFlag_BeenHandled) { - continue; - } - - switch (decl->kind) { - case_ast_node(vd, ValueDecl, decl); - check_collect_value_decl(ctx, decl); - case_end; - - case_ast_node(id, ImportDecl, decl); - check_add_import_decl(ctx, decl); - case_end; - - case_ast_node(fl, ForeignImportDecl, decl); - check_add_foreign_import_decl(ctx, decl); - case_end; - - case_ast_node(fb, ForeignBlockDecl, decl); - check_add_foreign_block_decl(ctx, decl); - case_end; - - case_ast_node(ws, WhenStmt, decl); - if (!ws->is_cond_determined) { - if (collect_when_stmt_from_file(ctx, ws)) { - return true; - } - - CheckerContext nctx = *ctx; - nctx.collect_delayed_decls = true; - - if (collect_file_decls_from_when_stmt(&nctx, ws)) { - return true; - } - } else { - CheckerContext nctx = *ctx; - nctx.collect_delayed_decls = true; - - if (collect_file_decls_from_when_stmt(&nctx, ws)) { - return true; - } - } - case_end; - - case_ast_node(es, ExprStmt, decl); - if (es->expr->kind == Ast_CallExpr) { - ast_node(ce, CallExpr, es->expr); - if (ce->proc->kind == Ast_BasicDirective) { - if (ctx->collect_delayed_decls) { - array_add(&ctx->scope->delayed_directives, es->expr); - } - } - } - case_end; + if (collect_file_decl(ctx, decls[i])) { + return true; } } @@ -4064,9 +4053,10 @@ void check_create_file_scopes(Checker *c) { string_map_set(&c->info.files, f->fullpath, f); create_scope_from_file(nullptr, f); + total_pkg_decl_count += f->total_file_decl_count; } - pkg->used = true; + mpmc_init(&pkg->exported_entity_queue, heap_allocator(), total_pkg_decl_count); } } @@ -4086,7 +4076,10 @@ GB_THREAD_PROC(thread_proc_collect_entities) { CheckerContext *ctx = &collect_entity_ctx; - for (isize i = data->file_offset; i < data->file_count; i++) { + isize file_offset = data->file_offset; + isize file_end = gb_min(file_offset+data->file_count, c->info.files.entries.count); + + for (isize i = file_offset; i < file_end; i++) { AstFile *f = c->info.files.entries[i].value; reset_checker_context(ctx, f); check_collect_entities(ctx, f->decls); @@ -4126,6 +4119,7 @@ void check_collect_entities_all(Checker *c) { isize file_load_count = (total_file_count+thread_count-1)/thread_count; isize remaining_file_count = c->info.files.entries.count; + ThreadProcCollectEntities *thread_data = gb_alloc_array(permanent_allocator(), ThreadProcCollectEntities, thread_count); for (isize i = 0; i < thread_count; i++) { ThreadProcCollectEntities *data = thread_data + i; @@ -4155,6 +4149,27 @@ void check_collect_entities_all(Checker *c) { } } +void check_export_entites(Checker *c) { + CheckerContext ctx = make_checker_context(c); + + for_array(i, c->info.packages.entries) { + AstPackage *pkg = c->info.packages.entries[i].value; + if (pkg->files.count == 0) { + continue; // Sanity check + } + + + AstPackageExportedEntity item = {}; + while (mpmc_dequeue(&pkg->exported_entity_queue, &item)) { + AstFile *f = item.entity->file; + if (ctx.file != f) { + reset_checker_context(&ctx, f); + } + add_entity(&ctx, pkg->scope, item.identifier, item.entity); + } + } +} + void check_import_entities(Checker *c) { #define TIME_SECTION(str) do { debugf("[Section] %s\n", str); if (build_context.show_more_timings) timings_start_section(&global_timings, str_lit(str)); } while (0) @@ -4167,7 +4182,7 @@ void check_import_entities(Checker *c) { }); - TIME_SECTION("check_import_entities - cycles"); + TIME_SECTION("check_import_entities - sort packages"); // NOTE(bill): Priority queue auto pq = priority_queue_create(dep_graph, import_graph_node_cmp, import_graph_node_swap); @@ -4221,68 +4236,27 @@ void check_import_entities(Checker *c) { array_add(&package_order, n); } - TIME_SECTION("check_import_entities - used"); - for_array(i, c->parser->packages) { - AstPackage *pkg = c->parser->packages[i]; - switch (pkg->kind) { - case Package_Init: - case Package_Runtime: - pkg->used = true; - break; - } - } - - TIME_SECTION("check_import_entities - collect checked packages from decl list"); + TIME_SECTION("check_import_entities - collect file decls"); CheckerContext ctx = make_checker_context(c); - for (isize loop_count = 0; ; loop_count++) { - bool new_files = false; - for_array(i, package_order) { - ImportGraphNode *node = package_order[i]; - GB_ASSERT(node->scope->flags&ScopeFlag_Pkg); - AstPackage *pkg = node->scope->pkg; - if (!pkg->used) { - continue; - } - - for_array(i, pkg->files) { - AstFile *f = pkg->files[i]; - reset_checker_context(&ctx, f); - new_files |= collect_checked_packages_from_decl_list(c, f->decls); - } - } - - if (!new_files) { - break; - } - } - - TIME_SECTION("check_import_entities - collect file decls"); - for (isize pkg_index = 0; pkg_index < package_order.count; pkg_index++) { + for_array(pkg_index, package_order) { ImportGraphNode *node = package_order[pkg_index]; AstPackage *pkg = node->pkg; - if (!pkg->used) { - continue; - } - - bool new_packages = false; - for_array(i, pkg->files) { AstFile *f = pkg->files[i]; reset_checker_context(&ctx, f); ctx.collect_delayed_decls = true; - if (collect_file_decls(&ctx, f->decls)) { - new_packages = true; - break; - } - } + MPMCQueue *q = nullptr; - if (new_packages) { - pkg_index = -1; - continue; + // Check import declarations first to simplify things + for (Ast *id = nullptr; mpmc_dequeue(&f->delayed_decls_queues[AstDelayQueue_Import], &id); /**/) { + check_add_import_decl(&ctx, id); + } + + collect_file_decls(&ctx, f->decls); } } @@ -4296,8 +4270,8 @@ void check_import_entities(Checker *c) { AstFile *f = pkg->files[i]; reset_checker_context(&ctx, f); - for_array(j, f->scope->delayed_imports) { - Ast *decl = f->scope->delayed_imports[j]; + auto *q = &f->delayed_decls_queues[AstDelayQueue_Import]; + for (Ast *decl = nullptr; mpmc_dequeue(q, &decl); /**/) { check_add_import_decl(&ctx, decl); } } @@ -4305,8 +4279,8 @@ void check_import_entities(Checker *c) { AstFile *f = pkg->files[i]; reset_checker_context(&ctx, f); - for_array(j, f->scope->delayed_directives) { - Ast *expr = f->scope->delayed_directives[j]; + auto *q = &f->delayed_decls_queues[AstDelayQueue_Expr]; + for (Ast *expr = nullptr; mpmc_dequeue(q, &expr); /**/) { Operand o = {}; check_expr(&ctx, &o, expr); } @@ -5014,12 +4988,21 @@ void check_parsed_files(Checker *c) { check_create_file_scopes(c); TIME_SECTION("collect entities"); - // Collect Entities check_collect_entities_all(c); - TIME_SECTION("import entities"); + TIME_SECTION("export entities - pre"); + check_export_entites(c); + + // TIME_SECTION("import entities"); check_import_entities(c); + TIME_SECTION("export entities - post"); + check_export_entites(c); + + // if (true) { + // return; + // } + TIME_SECTION("add entities from packages"); check_add_entities_from_queues(c); diff --git a/src/checker.hpp b/src/checker.hpp index 7bd72c9d9..773039f44 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -191,8 +191,6 @@ struct Scope { std::atomic head_child; StringMap elements; - Array delayed_directives; - Array delayed_imports; PtrSet imported; i32 flags; // ScopeFlag @@ -295,6 +293,8 @@ struct CheckerInfo { // NOTE(bill): If the semantic checker (check_proc_body) is to ever to be multithreaded, // these variables will be of contention + BlockingMutex scope_mutex; + // NOT recursive & Only used at the end of `check_proc_body` // This is a possible source of contention but probably not // too much of a problem in practice diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index efc589e05..a59e1baa9 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -7778,6 +7778,18 @@ lbValue lb_build_binary_expr(lbProcedure *p, Ast *expr) { case Token_CmpEq: case Token_NotEq: + if (is_type_untyped_nil(be->right->tav.type)) { + lbValue left = lb_build_expr(p, be->left); + lbValue cmp = lb_emit_comp_against_nil(p, be->op.kind, left); + Type *type = default_type(tv.type); + return lb_emit_conv(p, cmp, type); + } else if (is_type_untyped_nil(be->left->tav.type)) { + lbValue right = lb_build_expr(p, be->right); + lbValue cmp = lb_emit_comp_against_nil(p, be->op.kind, right); + Type *type = default_type(tv.type); + return lb_emit_conv(p, cmp, type); + } + /*fallthrough*/ case Token_Lt: case Token_LtEq: case Token_Gt: @@ -11337,133 +11349,164 @@ lbValue lb_emit_comp_against_nil(lbProcedure *p, TokenKind op_kind, lbValue x) { lbValue res = {}; res.type = t_llvm_bool; Type *t = x.type; - if (is_type_enum(t)) { - if (op_kind == Token_CmpEq) { - res.value = LLVMBuildIsNull(p->builder, x.value, ""); - } else if (op_kind == Token_NotEq) { - res.value = LLVMBuildIsNotNull(p->builder, x.value, ""); - } - return res; - } else if (is_type_pointer(t)) { - if (op_kind == Token_CmpEq) { - res.value = LLVMBuildIsNull(p->builder, x.value, ""); - } else if (op_kind == Token_NotEq) { - res.value = LLVMBuildIsNotNull(p->builder, x.value, ""); - } - return res; - } else if (is_type_cstring(t)) { - lbValue ptr = lb_emit_conv(p, x, t_u8_ptr); - if (op_kind == Token_CmpEq) { - res.value = LLVMBuildIsNull(p->builder, ptr.value, ""); - } else if (op_kind == Token_NotEq) { - res.value = LLVMBuildIsNotNull(p->builder, ptr.value, ""); - } - return res; - } else if (is_type_proc(t)) { - if (op_kind == Token_CmpEq) { - res.value = LLVMBuildIsNull(p->builder, x.value, ""); - } else if (op_kind == Token_NotEq) { - res.value = LLVMBuildIsNotNull(p->builder, x.value, ""); - } - return res; - } else if (is_type_any(t)) { - // TODO(bill): is this correct behaviour for nil comparison for any? - lbValue data = lb_emit_struct_ev(p, x, 0); - lbValue ti = lb_emit_struct_ev(p, x, 1); - if (op_kind == Token_CmpEq) { - LLVMValueRef a = LLVMBuildIsNull(p->builder, data.value, ""); - LLVMValueRef b = LLVMBuildIsNull(p->builder, ti.value, ""); - res.value = LLVMBuildOr(p->builder, a, b, ""); - return res; - } else if (op_kind == Token_NotEq) { - LLVMValueRef a = LLVMBuildIsNotNull(p->builder, data.value, ""); - LLVMValueRef b = LLVMBuildIsNotNull(p->builder, ti.value, ""); - res.value = LLVMBuildAnd(p->builder, a, b, ""); - return res; - } - } else if (is_type_slice(t)) { - lbValue data = lb_emit_struct_ev(p, x, 0); - if (op_kind == Token_CmpEq) { - res.value = LLVMBuildIsNull(p->builder, data.value, ""); - return res; - } else if (op_kind == Token_NotEq) { - res.value = LLVMBuildIsNotNull(p->builder, data.value, ""); - return res; - } - } else if (is_type_dynamic_array(t)) { - lbValue data = lb_emit_struct_ev(p, x, 0); - if (op_kind == Token_CmpEq) { - res.value = LLVMBuildIsNull(p->builder, data.value, ""); - return res; - } else if (op_kind == Token_NotEq) { - res.value = LLVMBuildIsNotNull(p->builder, data.value, ""); - return res; - } - } else if (is_type_map(t)) { - lbValue hashes = lb_emit_struct_ev(p, x, 0); - lbValue data = lb_emit_struct_ev(p, hashes, 0); - return lb_emit_comp(p, op_kind, data, lb_zero(p->module, data.type)); - } else if (is_type_union(t)) { - if (type_size_of(t) == 0) { + Type *bt = base_type(t); + TypeKind type_kind = bt->kind; + + switch (type_kind) { + case Type_Basic: + switch (bt->Basic.kind) { + case Basic_rawptr: + case Basic_cstring: if (op_kind == Token_CmpEq) { - return lb_const_bool(p->module, t_llvm_bool, true); + res.value = LLVMBuildIsNull(p->builder, x.value, ""); } else if (op_kind == Token_NotEq) { - return lb_const_bool(p->module, t_llvm_bool, false); + res.value = LLVMBuildIsNotNull(p->builder, x.value, ""); } - } else if (is_type_union_maybe_pointer(t)) { - lbValue tag = lb_emit_transmute(p, x, t_rawptr); - return lb_emit_comp_against_nil(p, op_kind, tag); - } else { - lbValue tag = lb_emit_union_tag_value(p, x); - return lb_emit_comp(p, op_kind, tag, lb_zero(p->module, tag.type)); + return res; + case Basic_any: + { + // TODO(bill): is this correct behaviour for nil comparison for any? + lbValue data = lb_emit_struct_ev(p, x, 0); + lbValue ti = lb_emit_struct_ev(p, x, 1); + if (op_kind == Token_CmpEq) { + LLVMValueRef a = LLVMBuildIsNull(p->builder, data.value, ""); + LLVMValueRef b = LLVMBuildIsNull(p->builder, ti.value, ""); + res.value = LLVMBuildOr(p->builder, a, b, ""); + return res; + } else if (op_kind == Token_NotEq) { + LLVMValueRef a = LLVMBuildIsNotNull(p->builder, data.value, ""); + LLVMValueRef b = LLVMBuildIsNotNull(p->builder, ti.value, ""); + res.value = LLVMBuildAnd(p->builder, a, b, ""); + return res; + } + } + break; + case Basic_typeid: + lbValue invalid_typeid = lb_const_value(p->module, t_typeid, exact_value_i64(0)); + return lb_emit_comp(p, op_kind, x, invalid_typeid); } - } else if (is_type_typeid(t)) { - lbValue invalid_typeid = lb_const_value(p->module, t_typeid, exact_value_i64(0)); - return lb_emit_comp(p, op_kind, x, invalid_typeid); - } else if (is_type_soa_struct(t)) { - Type *bt = base_type(t); - if (bt->Struct.soa_kind == StructSoa_Slice) { - LLVMValueRef the_value = {}; - if (bt->Struct.fields.count == 0) { - lbValue len = lb_soa_struct_len(p, x); - the_value = len.value; + break; + + case Type_Enum: + case Type_Pointer: + case Type_Proc: + case Type_BitSet: + if (op_kind == Token_CmpEq) { + res.value = LLVMBuildIsNull(p->builder, x.value, ""); + } else if (op_kind == Token_NotEq) { + res.value = LLVMBuildIsNotNull(p->builder, x.value, ""); + } + return res; + + case Type_Slice: + { + lbValue data = lb_emit_struct_ev(p, x, 0); + if (op_kind == Token_CmpEq) { + res.value = LLVMBuildIsNull(p->builder, data.value, ""); + return res; + } else if (op_kind == Token_NotEq) { + res.value = LLVMBuildIsNotNull(p->builder, data.value, ""); + return res; + } + } + break; + + case Type_DynamicArray: + { + lbValue data = lb_emit_struct_ev(p, x, 0); + if (op_kind == Token_CmpEq) { + res.value = LLVMBuildIsNull(p->builder, data.value, ""); + return res; + } else if (op_kind == Token_NotEq) { + res.value = LLVMBuildIsNotNull(p->builder, data.value, ""); + return res; + } + } + break; + + case Type_Map: + { + lbValue map_ptr = lb_address_from_load_or_generate_local(p, x); + + unsigned indices[2] = {0, 0}; + LLVMValueRef hashes_data = LLVMBuildStructGEP(p->builder, map_ptr.value, 0, ""); + LLVMValueRef hashes_data_ptr_ptr = LLVMBuildStructGEP(p->builder, hashes_data, 0, ""); + LLVMValueRef hashes_data_ptr = LLVMBuildLoad(p->builder, hashes_data_ptr_ptr, ""); + + if (op_kind == Token_CmpEq) { + res.value = LLVMBuildIsNull(p->builder, hashes_data_ptr, ""); + return res; } else { - lbValue first_field = lb_emit_struct_ev(p, x, 0); - the_value = first_field.value; - } - if (op_kind == Token_CmpEq) { - res.value = LLVMBuildIsNull(p->builder, the_value, ""); - return res; - } else if (op_kind == Token_NotEq) { - res.value = LLVMBuildIsNotNull(p->builder, the_value, ""); - return res; - } - } else if (bt->Struct.soa_kind == StructSoa_Dynamic) { - LLVMValueRef the_value = {}; - if (bt->Struct.fields.count == 0) { - lbValue cap = lb_soa_struct_cap(p, x); - the_value = cap.value; - } else { - lbValue first_field = lb_emit_struct_ev(p, x, 0); - the_value = first_field.value; - } - if (op_kind == Token_CmpEq) { - res.value = LLVMBuildIsNull(p->builder, the_value, ""); - return res; - } else if (op_kind == Token_NotEq) { - res.value = LLVMBuildIsNotNull(p->builder, the_value, ""); + res.value = LLVMBuildIsNotNull(p->builder, hashes_data_ptr, ""); return res; } } - } else if (is_type_struct(t) && type_has_nil(t)) { - auto args = array_make(permanent_allocator(), 2); - lbValue lhs = lb_address_from_load_or_generate_local(p, x); - args[0] = lb_emit_conv(p, lhs, t_rawptr); - args[1] = lb_const_int(p->module, t_int, type_size_of(t)); - lbValue val = lb_emit_runtime_call(p, "memory_compare_zero", args); - lbValue res = lb_emit_comp(p, op_kind, val, lb_const_int(p->module, t_int, 0)); - return res; + break; + + case Type_Union: + { + if (type_size_of(t) == 0) { + if (op_kind == Token_CmpEq) { + return lb_const_bool(p->module, t_llvm_bool, true); + } else if (op_kind == Token_NotEq) { + return lb_const_bool(p->module, t_llvm_bool, false); + } + } else if (is_type_union_maybe_pointer(t)) { + lbValue tag = lb_emit_transmute(p, x, t_rawptr); + return lb_emit_comp_against_nil(p, op_kind, tag); + } else { + lbValue tag = lb_emit_union_tag_value(p, x); + return lb_emit_comp(p, op_kind, tag, lb_zero(p->module, tag.type)); + } + } + case Type_Struct: + if (is_type_soa_struct(t)) { + Type *bt = base_type(t); + if (bt->Struct.soa_kind == StructSoa_Slice) { + LLVMValueRef the_value = {}; + if (bt->Struct.fields.count == 0) { + lbValue len = lb_soa_struct_len(p, x); + the_value = len.value; + } else { + lbValue first_field = lb_emit_struct_ev(p, x, 0); + the_value = first_field.value; + } + if (op_kind == Token_CmpEq) { + res.value = LLVMBuildIsNull(p->builder, the_value, ""); + return res; + } else if (op_kind == Token_NotEq) { + res.value = LLVMBuildIsNotNull(p->builder, the_value, ""); + return res; + } + } else if (bt->Struct.soa_kind == StructSoa_Dynamic) { + LLVMValueRef the_value = {}; + if (bt->Struct.fields.count == 0) { + lbValue cap = lb_soa_struct_cap(p, x); + the_value = cap.value; + } else { + lbValue first_field = lb_emit_struct_ev(p, x, 0); + the_value = first_field.value; + } + if (op_kind == Token_CmpEq) { + res.value = LLVMBuildIsNull(p->builder, the_value, ""); + return res; + } else if (op_kind == Token_NotEq) { + res.value = LLVMBuildIsNotNull(p->builder, the_value, ""); + return res; + } + } + } else if (is_type_struct(t) && type_has_nil(t)) { + auto args = array_make(permanent_allocator(), 2); + lbValue lhs = lb_address_from_load_or_generate_local(p, x); + args[0] = lb_emit_conv(p, lhs, t_rawptr); + args[1] = lb_const_int(p->module, t_int, type_size_of(t)); + lbValue val = lb_emit_runtime_call(p, "memory_compare_zero", args); + lbValue res = lb_emit_comp(p, op_kind, val, lb_const_int(p->module, t_int, 0)); + return res; + } + break; } + GB_PANIC("Unknown handled type: %s -> %s", type_to_string(t), type_to_string(bt)); return {}; } diff --git a/src/parser.cpp b/src/parser.cpp index f1f348ff4..afead99c6 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -5370,6 +5370,9 @@ bool parse_file(Parser *p, AstFile *f) { } f->total_file_decl_count += calc_decl_count(stmt); + if (stmt->kind == Ast_WhenStmt || stmt->kind == Ast_ExprStmt || stmt->kind == Ast_ImportDecl) { + f->delayed_decl_count += 1; + } } } @@ -5381,6 +5384,10 @@ bool parse_file(Parser *p, AstFile *f) { u64 end = time_stamp_time_now(); f->time_to_parse = cast(f64)(end-start)/cast(f64)time_stamp__freq(); + for (int i = 0; i < AstDelayQueue_COUNT; i++) { + mpmc_init(f->delayed_decls_queues+i, heap_allocator(), f->delayed_decl_count); + } + return f->error_count == 0; } diff --git a/src/parser.hpp b/src/parser.hpp index b3bdfd15d..d7d972f9e 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -83,6 +83,11 @@ enum AstFileFlag : u32 { AstFile_IsLazy = 1<<2, }; +enum AstDelayQueueKind { + AstDelayQueue_Import, + AstDelayQueue_Expr, + AstDelayQueue_COUNT, +}; struct AstFile { i32 id; @@ -113,6 +118,7 @@ struct AstFile { bool allow_type; isize total_file_decl_count; + isize delayed_decl_count; Slice decls; Array imports; // 'import' isize directive_count; @@ -128,6 +134,9 @@ struct AstFile { CommentGroup *docs; // current docs Array comments; // All the comments! + // TODO(bill): make this a basic queue as it does not require + // any multiple thread capabilities + MPMCQueue delayed_decls_queues[AstDelayQueue_COUNT]; #define PARSER_MAX_FIX_COUNT 6 isize fix_count; @@ -151,6 +160,11 @@ struct AstForeignFile { }; +struct AstPackageExportedEntity { + Ast *identifier; + Entity *entity; +}; + struct AstPackage { PackageKind kind; isize id; @@ -160,10 +174,11 @@ struct AstPackage { Array foreign_files; bool is_single_file; + MPMCQueue exported_entity_queue; + // NOTE(bill): Created/set in checker Scope * scope; DeclInfo *decl_info; - bool used; bool is_extra; }; diff --git a/src/queue.cpp b/src/queue.cpp index da3cef687..4d7c0f052 100644 --- a/src/queue.cpp +++ b/src/queue.cpp @@ -64,6 +64,10 @@ void mpmc_destroy(MPMCQueue *q) { template isize mpmc_enqueue(MPMCQueue *q, T const &data) { + if (q->mask == 0) { + return -1; + } + isize head_idx = q->head_idx.load(std::memory_order_relaxed); for (;;) { @@ -101,9 +105,12 @@ isize mpmc_enqueue(MPMCQueue *q, T const &data) { } } - template bool mpmc_dequeue(MPMCQueue *q, T *data_) { + if (q->mask == 0) { + return false; + } + isize tail_idx = q->tail_idx.load(std::memory_order_relaxed); for (;;) {