compact compiler ids and spans to reduce memory usage

This commit is contained in:
2026-06-12 16:55:16 +02:00
parent 99ba907f59
commit 4112b79c6b
16 changed files with 972 additions and 573 deletions
+101 -10
View File
@@ -82,6 +82,52 @@ main :: func() void { _ = value }
testing.expect_value(t, module.statements[module.functions[0].body[0]].name, sink_symbol)
}
@(test)
compact_ids_reserve_invalid_values_and_preserve_layout :: proc(t: ^testing.T) {
testing.expect_value(t, size_of(source.Span), 12)
testing.expect_value(t, size_of(token.Token), 24)
source_index, source_ok := source.source_index(source.Source_Id(0), 1)
testing.expect_value(t, source_index, 0)
testing.expect(t, source_ok)
_, source_invalid := source.source_index(source.INVALID_SOURCE, 1)
testing.expect(t, !source_invalid)
_, source_out_of_bounds := source.source_index(source.Source_Id(1), 1)
testing.expect(t, !source_out_of_bounds)
diagnostic_index, diagnostic_ok := source.diagnostic_index(source.Diagnostic_Id(0), 1)
testing.expect_value(t, diagnostic_index, 0)
testing.expect(t, diagnostic_ok)
_, diagnostic_invalid := source.diagnostic_index(source.INVALID_DIAGNOSTIC, 1)
testing.expect(t, !diagnostic_invalid)
expr_index, expr_ok := ast.index(ast.Expr_Id(0), ast.INVALID_EXPR, 1)
testing.expect_value(t, expr_index, 0)
testing.expect(t, expr_ok)
_, expr_invalid := ast.index(ast.INVALID_EXPR, ast.INVALID_EXPR, 1)
testing.expect(t, !expr_invalid)
function_index, function_ok := hir.index(hir.Function_Id(0), hir.INVALID_FUNCTION, 1)
testing.expect_value(t, function_index, 0)
testing.expect(t, function_ok)
_, function_invalid := hir.index(hir.INVALID_FUNCTION, hir.INVALID_FUNCTION, 1)
testing.expect(t, !function_invalid)
instruction_index, instruction_ok := ir.index(ir.Instruction_Id(0), ir.INVALID_INSTRUCTION, 1)
testing.expect_value(t, instruction_index, 0)
testing.expect(t, instruction_ok)
_, instruction_invalid := ir.index(ir.INVALID_INSTRUCTION, ir.INVALID_INSTRUCTION, 1)
testing.expect(t, !instruction_invalid)
spec_index, spec_ok := checker.spec_index(checker.Spec_Id(0), 1)
testing.expect_value(t, spec_index, 0)
testing.expect(t, spec_ok)
_, spec_invalid := checker.spec_index(checker.INVALID_SPEC, 1)
testing.expect(t, !spec_invalid)
testing.expect(t, source.fits_source_length(u64(0xffff_ffff)))
testing.expect(t, !source.fits_source_length(u64(0x1_0000_0000)))
}
@(test)
lexer_preserves_newlines_and_skips_comments :: proc(t: ^testing.T) {
source_file := source.Source{path="test.bro", text="# comment\nmain :: func() void {}\n"}
@@ -196,7 +242,7 @@ main :: func() void {}
c_symbol := symbol.intern(&symbols, "c")
for tok in stream.items {
if tok.span.start < len(text) && text[tok.span.start:tok.span.end] == "c" {
if int(tok.span.start) < len(text) && text[int(tok.span.start):int(tok.span.end)] == "c" {
testing.expect_value(t, tok.kind, token.Kind.Identifier)
testing.expect_value(t, tok.symbol, c_symbol)
}
@@ -415,8 +461,8 @@ multi_source_diagnostics_report_the_originating_file :: proc(t: ^testing.T) {
testing.expect(t, loaded)
found := false
for _, diagnostic_id in diagnostics.items {
message := source.format(&diagnostics, diagnostic_id)
for _, diagnostic_index in diagnostics.items {
message := source.format(&diagnostics, source.diagnostic_id(diagnostic_index))
if strings.contains(message, "/b.bro:2:9:") &&
strings.contains(message, "unknown package alias 'math'") {
found = true
@@ -788,7 +834,7 @@ main :: func() void {
testing.expect(t, done_id >= 0)
testing.expect(t, main_id >= 0)
testing.expect_value(t, hir_module.statements[hir_module.functions[done_id].body[0]].kind, hir.Stmt_Kind.Return)
testing.expect_value(t, hir_module.statements[hir_module.functions[done_id].body[0]].expr, -1)
testing.expect_value(t, hir_module.statements[hir_module.functions[done_id].body[0]].expr, hir.INVALID_EXPR)
main := hir_module.functions[main_id]
testing.expect_value(t, hir_module.statements[main.body[0]].kind, hir.Stmt_Kind.Expression)
testing.expect_value(t, hir_module.statements[main.body[1]].kind, hir.Stmt_Kind.Sink)
@@ -1243,14 +1289,59 @@ maximum_signed_i64_literal_parses_exactly :: proc(t: ^testing.T) {
testing.expect_value(t, module.exprs[module.globals[0].expr].integer, i64(9223372036854775807))
}
@(test)
malformed_hir_references_lower_to_valid_trapped_llvm :: proc(t: ^testing.T) {
hir_module := hir.init_module()
defer hir.destroy_module(&hir_module)
append(&hir_module.exprs, hir.Expr{
kind=.Local,
type=types.I8,
target=hir.local_ref(hir.INVALID_LOCAL),
left=hir.INVALID_EXPR,
right=hir.INVALID_EXPR,
diagnostic=source.INVALID_DIAGNOSTIC,
})
append(&hir_module.statements, hir.Stmt{
kind=.Expression,
expr=hir.Expr_Id(0),
local=hir.INVALID_LOCAL,
diagnostic=source.INVALID_DIAGNOSTIC,
})
body := make([]hir.Stmt_Id, 1)
body[0] = hir.Stmt_Id(0)
append(&hir_module.functions, hir.Function{
name=symbol.INVALID,
link_name=strings.clone("main"),
calling_convention=.C,
implementation=.Definition,
linkage=.External,
is_main=true,
result=types.VOID,
body=body,
diagnostic=source.INVALID_DIAGNOSTIC,
})
ir_module := lower.lower(&hir_module)
defer ir.destroy_module(&ir_module)
source_file := source.Source{path="test.bro", text=""}
diagnostics := source.init_diagnostics(&source_file)
defer source.destroy_diagnostics(&diagnostics)
symbols := symbol.init_table()
defer symbol.destroy_table(&symbols)
text := llvm.emit(&ir_module, &diagnostics, &symbols)
defer delete(text)
testing.expect(t, strings.contains(text, "call void @bro.trap"))
testing.expect(t, !strings.contains(text, "%v-1"))
}
@(test)
malformed_ir_emits_traps_and_typed_sentinels :: proc(t: ^testing.T) {
module := ir.init_module()
defer ir.destroy_module(&module)
instructions := make([]ir.Instruction, 3)
instructions[0] = ir.Instruction{op=.Store, type=types.I8, a=-1, b=-1, diagnostic=-1}
instructions[1] = ir.Instruction{op=.Add_Checked, type=types.I32, a=-1, b=-1, diagnostic=-1}
instructions[2] = ir.Instruction{op=.Return, type=types.I32, a=1, b=-1, diagnostic=-1}
instructions[0] = ir.Instruction{op=.Store, type=types.I8, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC}
instructions[1] = ir.Instruction{op=.Add_Checked, type=types.I32, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC}
instructions[2] = ir.Instruction{op=.Return, type=types.I32, a=ir.Instruction_Id(1), b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC}
append(&module.functions, ir.Function{
link_name=strings.clone("main"),
calling_convention=.C,
@@ -1335,14 +1426,14 @@ deep_global_cycle_detection_uses_iterative_dfs :: proc(t: ^testing.T) {
for id in 0..<count {
append(&ast_module.globals, ast.Global{name=name})
dependencies: [dynamic]int
dependencies: [dynamic]hir.Global_Id
dependencies.allocator = context.allocator
append(&dependencies, (id+1)%count)
append(&dependencies, hir.global_id((id+1)%count))
append(&state.module.globals, hir.Global{name=name, dependencies=dependencies})
}
states := make([]u8, count)
defer delete(states)
checker.detect_global_cycles_visit(&state, 0, states)
checker.detect_global_cycles_visit(&state, hir.Global_Id(0), states)
testing.expect_value(t, len(diagnostics.items), 1)
for global in state.module.globals {