From 7fbf784354c0ae3e0aee31c29d33618a9354e7c7 Mon Sep 17 00:00:00 2001 From: ell1e Date: Thu, 23 Nov 2023 02:16:52 +0100 Subject: [PATCH] compiler: Add more work toward properly cataloging all funcs DCO-1.1-Signed-off-by: Ellie --- src/compiler/ast/analyze/analyze.h64 | 123 +++++++++++++++++++- src/compiler/ast/transform/transform.h64 | 13 ++- src/compiler/project.h64 | 1 + src/compiler/storage/scope/global_scope.h64 | 15 ++- src/compiler/storage/scope/scope.h64 | 10 ++ src/compiler/storage/storage.h64 | 64 ++++++++++ 6 files changed, 217 insertions(+), 9 deletions(-) diff --git a/src/compiler/ast/analyze/analyze.h64 b/src/compiler/ast/analyze/analyze.h64 index 8dc5ebbc..262b2c1d 100644 --- a/src/compiler/ast/analyze/analyze.h64 +++ b/src/compiler/ast/analyze/analyze.h64 @@ -75,7 +75,93 @@ func stmt_list_contains_later_call( return no } -func _basic_check_and_analyze_do( +func advanced_phase2_later_and_computevals( + stmts, msgs=none, project_file=none + ) { + func check_and_analyze_recurse( + stmts, msgs + ) { + for stmt in stmts { + if stmt.kind == cast.N_STMT_FUNC { + var is_later_func = no + for block in stmt.subblocks { + if stmt_list_contains_later_call( + block, check_recursive=yes, + include_later_ignore=no, + recurse_into_funcs=no) { + is_later_func = yes + break + } + } + stmt.func_scope.is_later_func = is_later_func + } + + # FIXME: go through all sorts of things we could already + # know the value of at compile-time, and try to iteratively + # find out as many values already as we can. + + # Blocks: + if stmt.kind == cast.N_STMT_IF or + stmt.kind == cast.N_STMT_DO { + for clause in stmt.subexprs { + for block in clause.subblocks { + if not check_and_analyze_recurse(block, msgs) { + return no + } + } + } + } elseif stmt.subblocks.len > 0 { + for block in stmt.subblocks { + if not check_and_analyze_recurse(block, msgs) { + return no + } + } + } + } + return yes + } + return check_and_analyze_recurse( + stmts, msgs + ) +} + +func advanced_phase1_globalrefs( + stmts, msgs=none, project_file=none + ) { + func check_and_analyze_recurse( + stmts, msgs) { + for stmt in stmts { + # FIXME + # IMPORTANT: we also want to auto-const all vrs here already. + # (Then the next state, the iteration stage, can do better + # guesses, especially inside functions.) + + # Blocks: + if stmt.kind == cast.N_STMT_IF or + stmt.kind == cast.N_STMT_DO { + for clause in stmt.subexprs { + for block in clause.subblocks { + if not check_and_analyze_recurse(block, msgs) { + return no + } + } + } + } elseif stmt.subblocks.len > 0 { + for block in stmt.subblocks { + if not check_and_analyze_recurse(block, msgs) { + return no + } + } + } + } + return yes + } + return check_and_analyze_recurse( + stmts, msgs + ) +} + +func basic_phase_nestingdepth_and_misc( stmts, msgs=none, project_file=none ) { func check_and_analyze_recurse( @@ -93,9 +179,6 @@ func _basic_check_and_analyze_do( return no } for stmt in stmts { - # Expressions (FIXME): - - # Blocks: if stmt.kind == cast.N_STMT_IF or stmt.kind == cast.N_STMT_DO { for clause in stmt.subexprs { @@ -122,11 +205,41 @@ func _basic_check_and_analyze_do( ) } +func do_advanced_check_and_analysis(project, msgs) { + func process_file(pfile) { + pfile.ensure_ast() later: + + if not advanced_phase1_globalrefs( + pfile.ast.stmts, msgs, + project_file=pfile) { + return later no + } + if not advanced_phase2_later_and_computevals( + pfile.ast.stmts, msgs, + project_file=pfile) { + return later no + } + return later yes + } + var check_went_ok = project.do_for_all_files(process_file) + later: + + await check_went_ok + for m in msgs { + if m.kind == msg.M_ERROR { + check_went_ok = no + } + } + + return later check_went_ok +} + func do_basic_check_and_analysis(project, msgs) { func process_file(pfile) { pfile.ensure_ast() later: - if not _basic_check_and_analyze_do(pfile.ast.stmts, msgs, + if not basic_phase_nestingdepth_and_misc( + pfile.ast.stmts, msgs, project_file=pfile) { return later no } diff --git a/src/compiler/ast/transform/transform.h64 b/src/compiler/ast/transform/transform.h64 index 7214a541..54c81763 100644 --- a/src/compiler/ast/transform/transform.h64 +++ b/src/compiler/ast/transform/transform.h64 @@ -26,6 +26,7 @@ # license, see accompanied LICENSE.md. import compiler.ast as ast +import compiler.ast.analyze as ast_analyze import compiler.ast.transform.later_transform as later_transform type VisitQueueEntry { @@ -154,9 +155,17 @@ func visit_ast_tree(node, visit_cb, pass_func_boundaries=yes) { } func do_all_transformations(project, msgs, optimize=yes) { - later_transform.first_pass_later_transform(project, msgs) + var success = ast_analyze.do_advanced_check_and_analysis( + project, msgs) later: + + await success + if not success { + return later none + } + success = later_transform.first_pass_later_transform(project, msgs) later: - return later none + await success + return later success } diff --git a/src/compiler/project.h64 b/src/compiler/project.h64 index 08186986..8894d447 100644 --- a/src/compiler/project.h64 +++ b/src/compiler/project.h64 @@ -33,6 +33,7 @@ import text from core.horse64.org import uri from core.horse64.org import compiler.ast as ast +import compiler.ast.func_stmt as func_stmt import compiler.ast.import_stmt as import_stmt import compiler.ast.transform as transform import compiler.cext as cext diff --git a/src/compiler/storage/scope/global_scope.h64 b/src/compiler/storage/scope/global_scope.h64 index 8a0d14dd..5bcd0ba2 100644 --- a/src/compiler/storage/scope/global_scope.h64 +++ b/src/compiler/storage/scope/global_scope.h64 @@ -27,6 +27,7 @@ import compiler.ast as ast import compiler.ast.enum_stmt as enum_stmt +import compiler.ast.func_stmt as func_stmt import compiler.ast.type_stmt as type_stmt import compiler.msg as msg import compiler.storage.scope as storage_scope @@ -76,6 +77,10 @@ func GlobalScope.ensure_assigned_storage(msgs=none, return } var storage_num = 1 + if project_file != none and project_file.project != none { + storage_num = project_file.project. + last_global_storage_id + 1 + } for name in self.name_to_stmt_map { var value = self.name_to_stmt_map[name] if typename(value) == "list" { @@ -98,6 +103,10 @@ func GlobalScope.ensure_assigned_storage(msgs=none, storage_num += 1 } } + if project_file != none and project_file.project != none { + project_file.project.last_global_storage_id = + storage_num - 1 + } # Now register enum values in the global scope too: var names_unchanged = self.name_to_stmt_map.keys() for name in names_unchanged { @@ -216,7 +225,7 @@ func has_global_scope_duplicate( return no } -func get_statement_global_name(statement) { +func get_statement_global_names(statement) { if statement.kind == ast.N_STMT_IMPORT { if statement.renamed_as != none { return [statement.renamed_as] @@ -242,7 +251,7 @@ func process_toplevel_stmt( scope, statement, msgs, project_file=none, debug=no) { assert(statement != none) - var names = get_statement_global_name(statement) + var names = get_statement_global_names(statement) if names.len == 0 { return } @@ -270,6 +279,8 @@ func process_toplevel_stmt( stand_in_node = new type_stmt.TypeStmt() } elseif statement.kind == ast.N_STMT_TYPEEXTEND { stand_in_node = new type_stmt.TypeExtendStmt() + } elseif statement.kind == ast.N_STMT_FUNC { + stand_in_node = new func_stmt.FuncStmt() } else { stand_in_node = new ast.StmtNode() } diff --git a/src/compiler/storage/scope/scope.h64 b/src/compiler/storage/scope/scope.h64 index 7e8d439e..b3234f28 100644 --- a/src/compiler/storage/scope/scope.h64 +++ b/src/compiler/storage/scope/scope.h64 @@ -27,6 +27,7 @@ import compiler.ast as ast import compiler.ast.enum_stmt as enum_stmt +import compiler.ast.func_stmt as func_stmt import compiler.ast.transform as transform import compiler.ast.type_stmt as type_stmt import compiler.msg as msg @@ -46,9 +47,16 @@ func SymbolInfo.init(name) { type FuncScope { var last_storage_id = 0 var is_type_attr = no + var is_later_func = no + var global_storage_id = 0 + var owning_type_storage_id var parent } +extend func_stmt.FuncStmt { + var func_scope +} + type TypeScope { const is_enum = no var node_map = {->} @@ -105,5 +113,7 @@ extend enum_stmt.EnumExtendStmt { extend project.Project { var type_or_enum_scope_map = {->} + + var storage_id_to_global_scope_map = {->} } diff --git a/src/compiler/storage/storage.h64 b/src/compiler/storage/storage.h64 index f2e9913b..cd3b5479 100644 --- a/src/compiler/storage/storage.h64 +++ b/src/compiler/storage/storage.h64 @@ -27,20 +27,30 @@ import compiler.ast as ast import compiler.ast.expr as ast_expr +import compiler.ast.func_stmt as func_stmt import compiler.ast.transform as transform import compiler.cext as cext import compiler.project as project import compiler.storage.scope as scope +import compiler.storage.scope.global_scope as global_scope import compiler.storage.ref as storage_ref extend project.Project { var did_compute_scopes = no + + var last_global_storage_id = 0 } extend ast_expr.IdRefExpr { var ref } +func project.Project.get_free_global_storage_slot { + var v = self.last_global_storage_id + 1 + self.last_global_storage_id = v + return v +} + func project.Project.compute_all_scopes(msgs, debug=no) { if self.did_compute_scopes { if debug { @@ -309,6 +319,7 @@ func project.Project.compute_all_scopes(msgs, debug=no) { } if node.kind == ast.N_STMT_FUNC { inner_scope.func_scope = new scope.FuncScope() + node.func_scope = inner_scope.func_scope if inner_scope.parent != none { inner_scope.func_scope.parent = inner_scope.parent.func_scope @@ -398,6 +409,52 @@ func project.Project.compute_all_scopes(msgs, debug=no) { pfile.global_scope.ensure_assigned_storage( msgs=msgs, project_file=pfile) func expr_idref_resolve_visitor(node, parent) { + if node.kind == ast.N_STMT_FUNC and + node.func_scope != none { + var is_global = no + var storage_id = 0 + if (parent == none or not has_attr(parent, "kind")) and + node.type_path == none { + # Global function. Get it from global scope: + is_global = yes + if pfile.global_scope. + name_to_stmt_map.has(node.label) { + var el = pfile.global_scope. + name_to_stmt_map[node.label] + if typename(el) != "list" and + el.kind == ast.N_STMT_FUNC and + el.symbol_info.storage_id != none { + storage_id = el.symbol_info.storage_id + } + } + if storage_id > 0 { + # Will only not get here as a run-on error + # in damaged code. + node.func_scope.global_storage_id = storage_id + } + } else { + assert(node.func_scope.global_storage_id == 0 or + node.func_scope.global_storage_id == none) + storage_id = self.get_free_global_storage_slot() + assert(storage_id != 0 and storage_id != none) + node.func_scope.global_storage_id = storage_id + } + if storage_id > 0 and not is_global { + # Register this without a name, we need all funcs in + # their global scope. + var stand_in_node = new func_stmt.FuncStmt() + pfile.global_scope.storage_id_to_entry[ + storage_id] = stand_in_node + stand_in_node.line = node.line + stand_in_node.col = node.col + } + if storage_id > 0 and + pfile.global_scope.storage_id_to_entry. + has(storage_id) { + pfile.global_scope.storage_id_to_entry[storage_id]. + func_scope = node.func_scope + } + } if node.kind == ast.N_EXPR_IDREF { if parent.kind == ast.N_EXPR_BINOP and parent.optoken.str == "." { @@ -474,6 +531,13 @@ func project.Project.compute_all_scopes(msgs, debug=no) { pfile.ast, expr_idref_resolve_visitor) later: await success + for storage_id in pfile.global_scope. + storage_id_to_entry.keys() { + assert(not self.storage_id_to_global_scope_map. + has(storage_id)) + self.storage_id_to_global_scope_map[storage_id] = + pfile.global_scope + } assert(success == yes) return later success }