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
-1
View File
@@ -4,7 +4,6 @@
# compiler hardening follow-ups # compiler hardening follow-ups
- migrate spans, AST/HIR/IR ids, and diagnostics from `int` to compact integer types
- prune unreachable function specializations before HIR construction and emission - prune unreachable function specializations before HIR construction and emission
- support unary minus, including the signed i64 minimum literal boundary - support unary minus, including the signed i64 minimum literal boundary
- move ignored example binaries into a dedicated build directory and remove `.review_tmp` - move ignored example binaries into a dedicated build directory and remove `.review_tmp`
+6
View File
@@ -29,3 +29,9 @@ The measured run reduced token size by 14.3% and peak tracked memory by 9.8%.
After the iterative compiler-hardening work on 2026-06-12, the same benchmark After the iterative compiler-hardening work on 2026-06-12, the same benchmark
reported 13,222,443 peak bytes and 40,117 allocations. The reusable traversal reported 13,222,443 peak bytes and 40,117 allocations. The reusable traversal
stacks keep allocation count effectively unchanged from the interning baseline. stacks keep allocation count effectively unchanged from the interning baseline.
After migrating persistent compiler references and spans to compact IDs on
2026-06-12, the benchmark reported 8,651,659 peak bytes and 40,117 allocations.
`Span` is 12 bytes and `Token` is 24 bytes; AST expressions, HIR expressions,
and IR instructions are 64, 88, and 88 bytes respectively. These sizes are
also printed by the benchmark to catch layout regressions.
+5
View File
@@ -3,6 +3,7 @@ package main
import "../../compiler/ast" import "../../compiler/ast"
import "../../compiler/checker" import "../../compiler/checker"
import "../../compiler/hir" import "../../compiler/hir"
import "../../compiler/ir"
import "../../compiler/lexer" import "../../compiler/lexer"
import "../../compiler/parser" import "../../compiler/parser"
import "../../compiler/source" import "../../compiler/source"
@@ -72,6 +73,10 @@ main :: proc() {
fmt.printf("allocation_count: %d\n", tracker.total_allocation_count) fmt.printf("allocation_count: %d\n", tracker.total_allocation_count)
fmt.printf("token_count: %d\n", metrics.token_count) fmt.printf("token_count: %d\n", metrics.token_count)
fmt.printf("token_size_bytes: %d\n", size_of(token.Token)) fmt.printf("token_size_bytes: %d\n", size_of(token.Token))
fmt.printf("span_size_bytes: %d\n", size_of(source.Span))
fmt.printf("ast_expr_size_bytes: %d\n", size_of(ast.Expr))
fmt.printf("hir_expr_size_bytes: %d\n", size_of(hir.Expr))
fmt.printf("ir_instruction_size_bytes: %d\n", size_of(ir.Instruction))
fmt.printf("unique_symbol_count: %d\n", metrics.unique_symbol_count) fmt.printf("unique_symbol_count: %d\n", metrics.unique_symbol_count)
fmt.printf("stored_symbol_bytes: %d\n", metrics.stored_symbol_bytes) fmt.printf("stored_symbol_bytes: %d\n", metrics.stored_symbol_bytes)
fmt.printf("diagnostic_count: %d\n", metrics.diagnostic_count) fmt.printf("diagnostic_count: %d\n", metrics.diagnostic_count)
+80 -26
View File
@@ -4,9 +4,63 @@ import "../source"
import "../symbol" import "../symbol"
import "core:mem" import "core:mem"
INVALID_ID :: -1 Expr_Id :: distinct u32
Stmt_Id :: distinct u32
Function_Id :: distinct u32
Global_Id :: distinct u32
Import_Id :: distinct u32
File_Id :: distinct u32
Package_Id :: distinct u32
Type_Syntax :: enum { INVALID_EXPR :: Expr_Id(0xffff_ffff)
INVALID_STMT :: Stmt_Id(0xffff_ffff)
INVALID_FUNCTION :: Function_Id(0xffff_ffff)
INVALID_GLOBAL :: Global_Id(0xffff_ffff)
INVALID_IMPORT :: Import_Id(0xffff_ffff)
INVALID_FILE :: File_Id(0xffff_ffff)
INVALID_PACKAGE :: Package_Id(0xffff_ffff)
expr_id :: proc(index: int) -> Expr_Id {
assert(index >= 0 && u64(index) < u64(INVALID_EXPR))
return Expr_Id(index)
}
stmt_id :: proc(index: int) -> Stmt_Id {
assert(index >= 0 && u64(index) < u64(INVALID_STMT))
return Stmt_Id(index)
}
function_id :: proc(index: int) -> Function_Id {
assert(index >= 0 && u64(index) < u64(INVALID_FUNCTION))
return Function_Id(index)
}
global_id :: proc(index: int) -> Global_Id {
assert(index >= 0 && u64(index) < u64(INVALID_GLOBAL))
return Global_Id(index)
}
import_id :: proc(index: int) -> Import_Id {
assert(index >= 0 && u64(index) < u64(INVALID_IMPORT))
return Import_Id(index)
}
file_id :: proc(index: int) -> File_Id {
assert(index >= 0 && u64(index) < u64(INVALID_FILE))
return File_Id(index)
}
package_id :: proc(index: int) -> Package_Id {
assert(index >= 0 && u64(index) < u64(INVALID_PACKAGE))
return Package_Id(index)
}
index :: proc(id: $T, invalid: T, count: int) -> (int, bool) {
value := int(id)
return value, id != invalid && value < count
}
Type_Syntax :: enum u8 {
Invalid, Invalid,
Int, Int,
I8, I8,
@@ -16,7 +70,7 @@ Type_Syntax :: enum {
Void, Void,
} }
Expr_Kind :: enum { Expr_Kind :: enum u8 {
Invalid, Invalid,
Integer, Integer,
Name, Name,
@@ -25,15 +79,15 @@ Expr_Kind :: enum {
} }
Expr :: struct { Expr :: struct {
kind: Expr_Kind,
span: source.Span, span: source.Span,
integer: i64,
args: []Expr_Id,
qualifier: symbol.Id, qualifier: symbol.Id,
name: symbol.Id, name: symbol.Id,
integer: i64, left: Expr_Id,
left: int, right: Expr_Id,
right: int, diagnostic: source.Diagnostic_Id,
args: []int, kind: Expr_Kind,
diagnostic: int,
} }
Param :: struct { Param :: struct {
@@ -42,7 +96,7 @@ Param :: struct {
type: Type_Syntax, type: Type_Syntax,
} }
Stmt_Kind :: enum { Stmt_Kind :: enum u8 {
Invalid, Invalid,
Declaration, Declaration,
Assignment, Assignment,
@@ -56,49 +110,49 @@ Stmt :: struct {
name: symbol.Id, name: symbol.Id,
type: Type_Syntax, type: Type_Syntax,
immutable: bool, immutable: bool,
expr: int, expr: Expr_Id,
diagnostic: int, diagnostic: source.Diagnostic_Id,
} }
Function :: struct { Function :: struct {
span: source.Span, span: source.Span,
name: symbol.Id, name: symbol.Id,
pkg: int, pkg: Package_Id,
file: int, file: File_Id,
c_abi: bool, c_abi: bool,
has_body: bool, has_body: bool,
params: []Param, params: []Param,
result: Type_Syntax, result: Type_Syntax,
body: []int, body: []Stmt_Id,
diagnostic: int, diagnostic: source.Diagnostic_Id,
} }
Global :: struct { Global :: struct {
span: source.Span, span: source.Span,
name: symbol.Id, name: symbol.Id,
pkg: int, pkg: Package_Id,
file: int, file: File_Id,
type: Type_Syntax, type: Type_Syntax,
immutable: bool, immutable: bool,
expr: int, expr: Expr_Id,
diagnostic: int, diagnostic: source.Diagnostic_Id,
} }
Import :: struct { Import :: struct {
span: source.Span, span: source.Span,
alias: symbol.Id, alias: symbol.Id,
path: string, path: string,
pkg: int, pkg: Package_Id,
file: int, file: File_Id,
target: int, target: Package_Id,
valid: bool, valid: bool,
used: bool, used: bool,
diagnostic: int, diagnostic: source.Diagnostic_Id,
} }
File :: struct { File :: struct {
source: int, source: source.Source_Id,
pkg: int, pkg: Package_Id,
} }
Package :: struct { Package :: struct {
File diff suppressed because it is too large Load Diff
+1
View File
@@ -56,6 +56,7 @@ compile_package :: proc(input_path, output_path: string, link_arguments: []linke
vmem.arena_allocator(&parser_arena), vmem.arena_allocator(&parser_arena),
) )
if !loaded { if !loaded {
source.print_all(&diagnostics)
fmt.eprintln("failed to load root package directory:", input_path) fmt.eprintln("failed to load root package directory:", input_path)
return 2 return 2
} }
+73 -24
View File
@@ -5,24 +5,73 @@ import "../symbol"
import "../types" import "../types"
import "core:mem" import "core:mem"
INVALID_ID :: -1 Expr_Id :: distinct u32
Stmt_Id :: distinct u32
Local_Id :: distinct u32
Function_Id :: distinct u32
Global_Id :: distinct u32
Ref :: distinct u32
Calling_Convention :: enum { INVALID_EXPR :: Expr_Id(0xffff_ffff)
INVALID_STMT :: Stmt_Id(0xffff_ffff)
INVALID_LOCAL :: Local_Id(0xffff_ffff)
INVALID_FUNCTION :: Function_Id(0xffff_ffff)
INVALID_GLOBAL :: Global_Id(0xffff_ffff)
INVALID_REF :: Ref(0xffff_ffff)
expr_id :: proc(index: int) -> Expr_Id {
assert(index >= 0 && u64(index) < u64(INVALID_EXPR))
return Expr_Id(index)
}
stmt_id :: proc(index: int) -> Stmt_Id {
assert(index >= 0 && u64(index) < u64(INVALID_STMT))
return Stmt_Id(index)
}
local_id :: proc(index: int) -> Local_Id {
assert(index >= 0 && u64(index) < u64(INVALID_LOCAL))
return Local_Id(index)
}
function_id :: proc(index: int) -> Function_Id {
assert(index >= 0 && u64(index) < u64(INVALID_FUNCTION))
return Function_Id(index)
}
global_id :: proc(index: int) -> Global_Id {
assert(index >= 0 && u64(index) < u64(INVALID_GLOBAL))
return Global_Id(index)
}
index :: proc(id: $T, invalid: T, count: int) -> (int, bool) {
value := int(id)
return value, id != invalid && value < count
}
local_ref :: proc(id: Local_Id) -> Ref {return Ref(id)}
function_ref :: proc(id: Function_Id) -> Ref {return Ref(id)}
global_ref :: proc(id: Global_Id) -> Ref {return Ref(id)}
as_local :: proc(ref: Ref) -> Local_Id {return Local_Id(ref)}
as_function :: proc(ref: Ref) -> Function_Id {return Function_Id(ref)}
as_global :: proc(ref: Ref) -> Global_Id {return Global_Id(ref)}
Calling_Convention :: enum u8 {
Brolang, Brolang,
C, C,
} }
Implementation :: enum { Implementation :: enum u8 {
Definition, Definition,
Declaration, Declaration,
} }
Linkage :: enum { Linkage :: enum u8 {
Internal, Internal,
External, External,
} }
Expr_Kind :: enum { Expr_Kind :: enum u8 {
Invalid, Invalid,
Integer, Integer,
Local, Local,
@@ -33,15 +82,15 @@ Expr_Kind :: enum {
} }
Expr :: struct { Expr :: struct {
kind: Expr_Kind,
span: source.Span, span: source.Span,
type: types.Type, type: types.Type,
integer: i64, integer: i64,
target: int, args: []Expr_Id,
left: int, target: Ref,
right: int, left: Expr_Id,
args: []int, right: Expr_Id,
diagnostic: int, diagnostic: source.Diagnostic_Id,
kind: Expr_Kind,
} }
Local :: struct { Local :: struct {
@@ -51,7 +100,7 @@ Local :: struct {
parameter: bool, parameter: bool,
} }
Stmt_Kind :: enum { Stmt_Kind :: enum u8 {
Declaration, Declaration,
Assignment, Assignment,
Return, Return,
@@ -63,9 +112,9 @@ Stmt_Kind :: enum {
Stmt :: struct { Stmt :: struct {
kind: Stmt_Kind, kind: Stmt_Kind,
span: source.Span, span: source.Span,
local: int, local: Local_Id,
expr: int, expr: Expr_Id,
diagnostic: int, diagnostic: source.Diagnostic_Id,
} }
Function :: struct { Function :: struct {
@@ -75,27 +124,27 @@ Function :: struct {
implementation: Implementation, implementation: Implementation,
linkage: Linkage, linkage: Linkage,
is_main: bool, is_main: bool,
params: []int, params: []Local_Id,
result: types.Type, result: types.Type,
locals: []Local, locals: []Local,
body: []int, body: []Stmt_Id,
direct_global_reads: [dynamic]int, direct_global_reads: [dynamic]Global_Id,
calls: []int, calls: []Function_Id,
problematic: bool, problematic: bool,
diagnostic: int, diagnostic: source.Diagnostic_Id,
} }
Global :: struct { Global :: struct {
name: symbol.Id, name: symbol.Id,
type: types.Type, type: types.Type,
expr: int, expr: Expr_Id,
static_value: i64, static_value: i64,
is_static: bool, is_static: bool,
dependencies: [dynamic]int, dependencies: [dynamic]Global_Id,
calls: []int, calls: []Function_Id,
direct_problem: bool, direct_problem: bool,
problematic: bool, problematic: bool,
diagnostic: int, diagnostic: source.Diagnostic_Id,
} }
Module :: struct { Module :: struct {
+54 -12
View File
@@ -5,24 +5,66 @@ import "../symbol"
import "../types" import "../types"
import "core:mem" import "core:mem"
INVALID_ID :: -1 Instruction_Id :: distinct u32
Local_Id :: distinct u32
Function_Id :: distinct u32
Global_Id :: distinct u32
Ref :: distinct u32
Calling_Convention :: enum { INVALID_INSTRUCTION :: Instruction_Id(0xffff_ffff)
INVALID_LOCAL :: Local_Id(0xffff_ffff)
INVALID_FUNCTION :: Function_Id(0xffff_ffff)
INVALID_GLOBAL :: Global_Id(0xffff_ffff)
INVALID_REF :: Ref(0xffff_ffff)
instruction_id :: proc(index: int) -> Instruction_Id {
assert(index >= 0 && u64(index) < u64(INVALID_INSTRUCTION))
return Instruction_Id(index)
}
local_id :: proc(index: int) -> Local_Id {
assert(index >= 0 && u64(index) < u64(INVALID_LOCAL))
return Local_Id(index)
}
function_id :: proc(index: int) -> Function_Id {
assert(index >= 0 && u64(index) < u64(INVALID_FUNCTION))
return Function_Id(index)
}
global_id :: proc(index: int) -> Global_Id {
assert(index >= 0 && u64(index) < u64(INVALID_GLOBAL))
return Global_Id(index)
}
index :: proc(id: $T, invalid: T, count: int) -> (int, bool) {
value := int(id)
return value, id != invalid && value < count
}
local_ref :: proc(id: Local_Id) -> Ref {return Ref(id)}
function_ref :: proc(id: Function_Id) -> Ref {return Ref(id)}
global_ref :: proc(id: Global_Id) -> Ref {return Ref(id)}
as_local :: proc(ref: Ref) -> Local_Id {return Local_Id(ref)}
as_function :: proc(ref: Ref) -> Function_Id {return Function_Id(ref)}
as_global :: proc(ref: Ref) -> Global_Id {return Global_Id(ref)}
Calling_Convention :: enum u8 {
Brolang, Brolang,
C, C,
} }
Implementation :: enum { Implementation :: enum u8 {
Definition, Definition,
Declaration, Declaration,
} }
Linkage :: enum { Linkage :: enum u8 {
Internal, Internal,
External, External,
} }
Opcode :: enum { Opcode :: enum u8 {
Param, Param,
Const, Const,
Load_Global, Load_Global,
@@ -38,15 +80,15 @@ Opcode :: enum {
} }
Instruction :: struct { Instruction :: struct {
op: Opcode,
span: source.Span, span: source.Span,
type: types.Type, type: types.Type,
integer: i64, integer: i64,
target: int, args: []Instruction_Id,
a: int, target: Ref,
b: int, a: Instruction_Id,
args: []int, b: Instruction_Id,
diagnostic: int, diagnostic: source.Diagnostic_Id,
op: Opcode,
} }
Function :: struct { Function :: struct {
@@ -68,7 +110,7 @@ Global :: struct {
static_value: i64, static_value: i64,
initializer: []Instruction, initializer: []Instruction,
problematic: bool, problematic: bool,
diagnostic: int, diagnostic: source.Diagnostic_Id,
} }
Module :: struct { Module :: struct {
+7 -7
View File
@@ -34,11 +34,11 @@ append_token :: proc(
kind: token.Kind, kind: token.Kind,
start, end: int, start, end: int,
id := symbol.INVALID, id := symbol.INVALID,
diagnostic := -1, diagnostic := source.INVALID_DIAGNOSTIC,
) { ) {
append(&stream.items, token.Token{ append(&stream.items, token.Token{
kind=kind, kind=kind,
span=source.Span{file=source_file.id, start=start, end=end}, span=source.Span{file=source_file.id, start=source.Offset(start), end=source.Offset(end)},
symbol=id, symbol=id,
diagnostic=diagnostic, diagnostic=diagnostic,
}) })
@@ -74,7 +74,7 @@ lex :: proc(
cursor += 1 cursor += 1
append_token(&stream, source_file, .Colon_Colon, start, cursor) append_token(&stream, source_file, .Colon_Colon, start, cursor)
} else { } else {
id := source.add(diagnostics, source.Span{file=source_file.id, start=start, end=cursor}, "expected a second ':'") id := source.add(diagnostics, source.Span{file=source_file.id, start=source.Offset(start), end=source.Offset(cursor)}, "expected a second ':'")
append_token(&stream, source_file, .Invalid, start, cursor, diagnostic=id) append_token(&stream, source_file, .Invalid, start, cursor, diagnostic=id)
} }
case '=': case '=':
@@ -111,7 +111,7 @@ lex :: proc(
if cursor >= len(bytes) || (bytes[cursor] != '\\' && bytes[cursor] != '"') { if cursor >= len(bytes) || (bytes[cursor] != '\\' && bytes[cursor] != '"') {
source.add( source.add(
diagnostics, diagnostics,
source.Span{file=source_file.id, start=max(cursor-1, start), end=min(cursor+1, len(bytes))}, source.Span{file=source_file.id, start=source.Offset(max(cursor-1, start)), end=source.Offset(min(cursor+1, len(bytes)))},
"import strings only support '\\\\' and '\\\"' escapes", "import strings only support '\\\\' and '\\\"' escapes",
) )
valid = false valid = false
@@ -127,7 +127,7 @@ lex :: proc(
} else { } else {
id := source.add( id := source.add(
diagnostics, diagnostics,
source.Span{file=source_file.id, start=start, end=cursor}, source.Span{file=source_file.id, start=source.Offset(start), end=source.Offset(cursor)},
"unterminated import string", "unterminated import string",
) )
append_token(&stream, source_file, .Invalid, start, cursor, diagnostic=id) append_token(&stream, source_file, .Invalid, start, cursor, diagnostic=id)
@@ -135,7 +135,7 @@ lex :: proc(
case ';': case ';':
id := source.add( id := source.add(
diagnostics, diagnostics,
source.Span{file=source_file.id, start=cursor, end=cursor+1}, source.Span{file=source_file.id, start=source.Offset(cursor), end=source.Offset(cursor+1)},
"semicolons are invalid; terminate statements with a newline", "semicolons are invalid; terminate statements with a newline",
) )
append_token(&stream, source_file, .Invalid, cursor, cursor+1, diagnostic=id) append_token(&stream, source_file, .Invalid, cursor, cursor+1, diagnostic=id)
@@ -162,7 +162,7 @@ lex :: proc(
} else { } else {
id := source.addf( id := source.addf(
diagnostics, diagnostics,
source.Span{file=source_file.id, start=cursor, end=cursor+1}, source.Span{file=source_file.id, start=source.Offset(cursor), end=source.Offset(cursor+1)},
"invalid source byte 0x%02x", "invalid source byte 0x%02x",
value, value,
) )
+46 -43
View File
@@ -49,11 +49,11 @@ sentinel :: proc(value_type: types.Type) -> i64 {
} }
} }
valid_instruction :: proc(instructions: []ir.Instruction, instruction_id: int) -> bool { valid_instruction :: proc(instructions: []ir.Instruction, instruction_id: ir.Instruction_Id) -> bool {
return instruction_id >= 0 && instruction_id < len(instructions) return instruction_id != ir.INVALID_INSTRUCTION && int(instruction_id) < len(instructions)
} }
valid_value :: proc(instructions: []ir.Instruction, value_id: int, expected: types.Type) -> bool { valid_value :: proc(instructions: []ir.Instruction, value_id: ir.Instruction_Id, expected: types.Type) -> bool {
if !valid_instruction(instructions, value_id) || if !valid_instruction(instructions, value_id) ||
!types.is_concrete_integer(expected) || !types.is_concrete_integer(expected) ||
!types.equal(instructions[value_id].type, expected) { !types.equal(instructions[value_id].type, expected) {
@@ -68,7 +68,7 @@ valid_value :: proc(instructions: []ir.Instruction, value_id: int, expected: typ
return false return false
} }
write_operand :: proc(builder: ^strings.Builder, instructions: []ir.Instruction, value_id: int, expected: types.Type) { write_operand :: proc(builder: ^strings.Builder, instructions: []ir.Instruction, value_id: ir.Instruction_Id, expected: types.Type) {
if !valid_value(instructions, value_id, expected) { if !valid_value(instructions, value_id, expected) {
fmt.sbprintf(builder, "%d", sentinel(expected)) fmt.sbprintf(builder, "%d", sentinel(expected))
return return
@@ -88,8 +88,8 @@ register_message :: proc(emitter: ^Emitter, text: string) -> int {
return id return id
} }
diagnostic_message :: proc(emitter: ^Emitter, diagnostic: int, span: source.Span, fallback: string) -> int { diagnostic_message :: proc(emitter: ^Emitter, diagnostic: source.Diagnostic_Id, span: source.Span, fallback: string) -> int {
if diagnostic >= 0 && diagnostic < len(emitter.diagnostics.items) { if _, ok := source.diagnostic_index(diagnostic, len(emitter.diagnostics.items)); ok {
message := source.format(emitter.diagnostics, diagnostic, emitter.allocator) message := source.format(emitter.diagnostics, diagnostic, emitter.allocator)
id := register_message(emitter, message) id := register_message(emitter, message)
delete(message, emitter.allocator) delete(message, emitter.allocator)
@@ -137,7 +137,7 @@ emit_recovery_value :: proc(emitter: ^Emitter, instruction_id: int, instruction:
} }
} }
emit_call_args :: proc(builder: ^strings.Builder, instructions: []ir.Instruction, args: []int, param_types: []types.Type) { emit_call_args :: proc(builder: ^strings.Builder, instructions: []ir.Instruction, args: []ir.Instruction_Id, param_types: []types.Type) {
for arg, index in args { for arg, index in args {
if index > 0 { if index > 0 {
strings.write_string(builder, ", ") strings.write_string(builder, ", ")
@@ -152,60 +152,62 @@ emit_instruction_stream :: proc(
instructions: []ir.Instruction, instructions: []ir.Instruction,
function: ir.Function, function: ir.Function,
global_initializer := false, global_initializer := false,
) -> int { ) -> ir.Instruction_Id {
return_value := -1 return_value := ir.INVALID_INSTRUCTION
after_return := false after_return := false
for instruction, instruction_id in instructions { for instruction, instruction_index in instructions {
instruction_id := ir.instruction_id(instruction_index)
if after_return { if after_return {
fmt.sbprintf(&emitter.builder, "recover_after_return_%d:\n", instruction_id) fmt.sbprintf(&emitter.builder, "recover_after_return_%d:\n", instruction_index)
after_return = false after_return = false
} }
switch instruction.op { switch instruction.op {
case .Param, .Const: case .Param, .Const:
case .Load_Global: case .Load_Global:
if instruction.target < 0 || instruction.target >= len(emitter.module.globals) { global_id := ir.as_global(instruction.target)
emit_recovery_value(emitter, instruction_id, instruction, "invalid global reference") if global_id == ir.INVALID_GLOBAL || int(global_id) >= len(emitter.module.globals) {
emit_recovery_value(emitter, instruction_index, instruction, "invalid global reference")
continue continue
} }
global := emitter.module.globals[instruction.target] global := emitter.module.globals[global_id]
if !types.equal(instruction.type, global.type) { if !types.equal(instruction.type, global.type) {
emit_recovery_value(emitter, instruction_id, instruction, "invalid global reference type") emit_recovery_value(emitter, instruction_index, instruction, "invalid global reference type")
continue continue
} }
if global.is_static { if global.is_static {
fmt.sbprintf( fmt.sbprintf(
&emitter.builder, &emitter.builder,
" %%v%d = load %s, ptr @bro.g.%d\n", " %%v%d = load %s, ptr @bro.g.%d\n",
instruction_id, instruction_index,
llvm_type(global.type), llvm_type(global.type),
instruction.target, global_id,
) )
} else { } else {
fmt.sbprintf( fmt.sbprintf(
&emitter.builder, &emitter.builder,
" %%v%d = call %s @bro.get.%d()\n", " %%v%d = call %s @bro.get.%d()\n",
instruction_id, instruction_index,
llvm_type(global.type), llvm_type(global.type),
instruction.target, global_id,
) )
} }
case .Alloca: case .Alloca:
if !types.is_concrete_integer(instruction.type) { if !types.is_concrete_integer(instruction.type) {
emit_recovery_value(emitter, instruction_id, instruction, "invalid allocation type") emit_recovery_value(emitter, instruction_index, instruction, "invalid allocation type")
continue continue
} }
fmt.sbprintf(&emitter.builder, " %%v%d = alloca %s\n", instruction_id, llvm_type(instruction.type)) fmt.sbprintf(&emitter.builder, " %%v%d = alloca %s\n", instruction_index, llvm_type(instruction.type))
case .Load: case .Load:
if !valid_instruction(instructions, instruction.a) || if !valid_instruction(instructions, instruction.a) ||
instructions[instruction.a].op != .Alloca || instructions[instruction.a].op != .Alloca ||
!types.equal(instructions[instruction.a].type, instruction.type) { !types.equal(instructions[instruction.a].type, instruction.type) {
emit_recovery_value(emitter, instruction_id, instruction, "invalid load slot") emit_recovery_value(emitter, instruction_index, instruction, "invalid load slot")
continue continue
} }
fmt.sbprintf( fmt.sbprintf(
&emitter.builder, &emitter.builder,
" %%v%d = load %s, ptr %%v%d\n", " %%v%d = load %s, ptr %%v%d\n",
instruction_id, instruction_index,
llvm_type(instruction.type), llvm_type(instruction.type),
instruction.a, instruction.a,
) )
@@ -214,7 +216,7 @@ emit_instruction_stream :: proc(
instructions[instruction.a].op != .Alloca || instructions[instruction.a].op != .Alloca ||
!types.equal(instructions[instruction.a].type, instruction.type) || !types.equal(instructions[instruction.a].type, instruction.type) ||
!valid_value(instructions, instruction.b, instruction.type) { !valid_value(instructions, instruction.b, instruction.type) {
emit_recovery_value(emitter, instruction_id, instruction, "invalid store operand") emit_recovery_value(emitter, instruction_index, instruction, "invalid store operand")
continue continue
} }
fmt.sbprintf(&emitter.builder, " store %s ", llvm_type(instruction.type)) fmt.sbprintf(&emitter.builder, " store %s ", llvm_type(instruction.type))
@@ -225,50 +227,51 @@ emit_instruction_stream :: proc(
!types.is_concrete_integer(instructions[instruction.a].type) || !types.is_concrete_integer(instructions[instruction.a].type) ||
!types.is_concrete_integer(instruction.type) || !types.is_concrete_integer(instruction.type) ||
instructions[instruction.a].type.bits >= instruction.type.bits { instructions[instruction.a].type.bits >= instruction.type.bits {
emit_recovery_value(emitter, instruction_id, instruction, "invalid widening operand") emit_recovery_value(emitter, instruction_index, instruction, "invalid widening operand")
continue continue
} }
from_type := instructions[instruction.a].type from_type := instructions[instruction.a].type
fmt.sbprintf(&emitter.builder, " %%v%d = sext %s ", instruction_id, llvm_type(from_type)) fmt.sbprintf(&emitter.builder, " %%v%d = sext %s ", instruction_index, llvm_type(from_type))
write_operand(&emitter.builder, instructions, instruction.a, from_type) write_operand(&emitter.builder, instructions, instruction.a, from_type)
fmt.sbprintf(&emitter.builder, " to %s\n", llvm_type(instruction.type)) fmt.sbprintf(&emitter.builder, " to %s\n", llvm_type(instruction.type))
case .Add_Checked: case .Add_Checked:
if !valid_value(instructions, instruction.a, instruction.type) || if !valid_value(instructions, instruction.a, instruction.type) ||
!valid_value(instructions, instruction.b, instruction.type) { !valid_value(instructions, instruction.b, instruction.type) {
emit_recovery_value(emitter, instruction_id, instruction, "invalid addition operand") emit_recovery_value(emitter, instruction_index, instruction, "invalid addition operand")
continue continue
} }
type_name := llvm_type(instruction.type) type_name := llvm_type(instruction.type)
fmt.sbprintf(&emitter.builder, " %%pair%d = call ", instruction_id) fmt.sbprintf(&emitter.builder, " %%pair%d = call ", instruction_index)
strings.write_string(&emitter.builder, "{ ") strings.write_string(&emitter.builder, "{ ")
fmt.sbprintf(&emitter.builder, "%s, i1 } @llvm.sadd.with.overflow.%s(%s ", type_name, type_name, type_name) fmt.sbprintf(&emitter.builder, "%s, i1 } @llvm.sadd.with.overflow.%s(%s ", type_name, type_name, type_name)
write_operand(&emitter.builder, instructions, instruction.a, instruction.type) write_operand(&emitter.builder, instructions, instruction.a, instruction.type)
fmt.sbprintf(&emitter.builder, ", %s ", type_name) fmt.sbprintf(&emitter.builder, ", %s ", type_name)
write_operand(&emitter.builder, instructions, instruction.b, instruction.type) write_operand(&emitter.builder, instructions, instruction.b, instruction.type)
fmt.sbprintf(&emitter.builder, ")\n") fmt.sbprintf(&emitter.builder, ")\n")
fmt.sbprintf(&emitter.builder, " %%v%d = extractvalue ", instruction_id) fmt.sbprintf(&emitter.builder, " %%v%d = extractvalue ", instruction_index)
strings.write_string(&emitter.builder, "{ ") strings.write_string(&emitter.builder, "{ ")
fmt.sbprintf(&emitter.builder, "%s, i1 } %%pair%d, 0\n", type_name, instruction_id) fmt.sbprintf(&emitter.builder, "%s, i1 } %%pair%d, 0\n", type_name, instruction_index)
fmt.sbprintf(&emitter.builder, " %%overflow%d = extractvalue ", instruction_id) fmt.sbprintf(&emitter.builder, " %%overflow%d = extractvalue ", instruction_index)
strings.write_string(&emitter.builder, "{ ") strings.write_string(&emitter.builder, "{ ")
fmt.sbprintf(&emitter.builder, "%s, i1 } %%pair%d, 1\n", type_name, instruction_id) fmt.sbprintf(&emitter.builder, "%s, i1 } %%pair%d, 1\n", type_name, instruction_index)
fmt.sbprintf( fmt.sbprintf(
&emitter.builder, &emitter.builder,
" br i1 %%overflow%d, label %%overflow_trap%d, label %%overflow_continue%d\n", " br i1 %%overflow%d, label %%overflow_trap%d, label %%overflow_continue%d\n",
instruction_id, instruction_index,
instruction_id, instruction_index,
instruction_id, instruction_index,
) )
fmt.sbprintf(&emitter.builder, "overflow_trap%d:\n", instruction_id) fmt.sbprintf(&emitter.builder, "overflow_trap%d:\n", instruction_index)
message := diagnostic_message(emitter, -1, instruction.span, "signed integer addition overflow") message := diagnostic_message(emitter, source.INVALID_DIAGNOSTIC, instruction.span, "signed integer addition overflow")
emit_trap_call(emitter, message) emit_trap_call(emitter, message)
fmt.sbprintf(&emitter.builder, " unreachable\noverflow_continue%d:\n", instruction_id) fmt.sbprintf(&emitter.builder, " unreachable\noverflow_continue%d:\n", instruction_index)
case .Call: case .Call:
if instruction.target < 0 || instruction.target >= len(emitter.module.functions) { function_id := ir.as_function(instruction.target)
emit_recovery_value(emitter, instruction_id, instruction, "invalid function specialization") if function_id == ir.INVALID_FUNCTION || int(function_id) >= len(emitter.module.functions) {
emit_recovery_value(emitter, instruction_index, instruction, "invalid function specialization")
continue continue
} }
target := emitter.module.functions[instruction.target] target := emitter.module.functions[function_id]
valid_args := len(instruction.args) == len(target.param_types) valid_args := len(instruction.args) == len(target.param_types)
if valid_args { if valid_args {
for arg, index in instruction.args { for arg, index in instruction.args {
@@ -283,11 +286,11 @@ emit_instruction_stream :: proc(
target_result = types.I32 target_result = types.I32
} }
if !valid_args || !types.equal(instruction.type, target_result) { if !valid_args || !types.equal(instruction.type, target_result) {
emit_recovery_value(emitter, instruction_id, instruction, "invalid function call operands") emit_recovery_value(emitter, instruction_index, instruction, "invalid function call operands")
continue continue
} }
if instruction.type.kind != .Void { if instruction.type.kind != .Void {
fmt.sbprintf(&emitter.builder, " %%v%d = ", instruction_id) fmt.sbprintf(&emitter.builder, " %%v%d = ", instruction_index)
} else { } else {
strings.write_string(&emitter.builder, " ") strings.write_string(&emitter.builder, " ")
} }
+27 -16
View File
@@ -39,20 +39,20 @@ is_identifier :: proc(value: string) -> bool {
return true return true
} }
find_package :: proc(state: ^State, path: string) -> int { find_package :: proc(state: ^State, path: string) -> ast.Package_Id {
for pkg, id in state.module.packages { for pkg, id in state.module.packages {
if pkg.path == path { if pkg.path == path {
return id return ast.package_id(id)
} }
} }
return -1 return ast.INVALID_PACKAGE
} }
add_placeholder :: proc(state: ^State, path: string) -> int { add_placeholder :: proc(state: ^State, path: string) -> ast.Package_Id {
if existing := find_package(state, path); existing >= 0 { if existing := find_package(state, path); existing != ast.INVALID_PACKAGE {
return existing return existing
} }
id := len(state.module.packages) id := ast.package_id(len(state.module.packages))
append(&state.module.packages, ast.Package{ append(&state.module.packages, ast.Package{
path=strings.clone(path, state.allocator), path=strings.clone(path, state.allocator),
name=symbol.intern(state.symbols, filepath.base(path)), name=symbol.intern(state.symbols, filepath.base(path)),
@@ -103,7 +103,7 @@ resolve_import_path :: proc(state: ^State, importing_path, import_path: string)
return joined, false return joined, false
} }
load_package :: proc(state: ^State, path: string, import_span: source.Span, is_root := false) -> int { load_package :: proc(state: ^State, path: string, import_span: source.Span, is_root := false) -> ast.Package_Id {
canonical, ok := filepath.abs(path, state.allocator) canonical, ok := filepath.abs(path, state.allocator)
if !ok || !os.is_dir(path) { if !ok || !os.is_dir(path) {
if is_root { if is_root {
@@ -111,7 +111,7 @@ load_package :: proc(state: ^State, path: string, import_span: source.Span, is_r
if len(canonical) > 0 { if len(canonical) > 0 {
delete(canonical, state.allocator) delete(canonical, state.allocator)
} }
return -1 return ast.INVALID_PACKAGE
} }
placeholder := path placeholder := path
if len(canonical) > 0 { if len(canonical) > 0 {
@@ -124,12 +124,12 @@ load_package :: proc(state: ^State, path: string, import_span: source.Span, is_r
} }
return id return id
} }
if existing := find_package(state, canonical); existing >= 0 { if existing := find_package(state, canonical); existing != ast.INVALID_PACKAGE {
delete(canonical, state.allocator) delete(canonical, state.allocator)
return existing return existing
} }
pkg_id := len(state.module.packages) pkg_id := ast.package_id(len(state.module.packages))
append(&state.module.packages, ast.Package{ append(&state.module.packages, ast.Package{
path=canonical, path=canonical,
name=symbol.intern(state.symbols, filepath.base(canonical)), name=symbol.intern(state.symbols, filepath.base(canonical)),
@@ -152,13 +152,24 @@ load_package :: proc(state: ^State, path: string, import_span: source.Span, is_r
} }
for file_info in files { for file_info in files {
if file_info.size < 0 || !source.fits_source_length(u64(file_info.size)) {
source.addf(state.diagnostics, import_span, "source file '%s' exceeds the 4 GiB source limit", file_info.fullpath)
state.root_failed = true
continue
}
bytes, read_ok := os.read_entire_file(file_info.fullpath, state.sources.allocator) bytes, read_ok := os.read_entire_file(file_info.fullpath, state.sources.allocator)
if !read_ok { if !read_ok {
state.root_failed = true state.root_failed = true
continue continue
} }
if !source.fits_source_length(u64(len(bytes))) {
source.addf(state.diagnostics, import_span, "source file '%s' exceeds the 4 GiB source limit", file_info.fullpath)
delete(bytes, state.sources.allocator)
state.root_failed = true
continue
}
source_id := source.add_source_owned(state.sources, file_info.fullpath, bytes) source_id := source.add_source_owned(state.sources, file_info.fullpath, bytes)
file_id := len(state.module.files) file_id := ast.file_id(len(state.module.files))
append(&state.module.files, ast.File{source=source_id, pkg=pkg_id}) append(&state.module.files, ast.File{source=source_id, pkg=pkg_id})
stream := lexer.lex(&state.sources.items[source_id], state.diagnostics, state.symbols, state.token_allocator) stream := lexer.lex(&state.sources.items[source_id], state.diagnostics, state.symbols, state.token_allocator)
parser.parse_into(&stream, &state.sources.items[source_id], state.diagnostics, state.module, pkg_id, file_id) parser.parse_into(&stream, &state.sources.items[source_id], state.diagnostics, state.module, pkg_id, file_id)
@@ -169,7 +180,7 @@ load_package :: proc(state: ^State, path: string, import_span: source.Span, is_r
import_count := len(state.module.imports) import_count := len(state.module.imports)
for import_id in 0..<import_count { for import_id in 0..<import_count {
import_item := state.module.imports[import_id] import_item := state.module.imports[import_id]
if import_item.pkg != pkg_id || import_item.target >= 0 { if import_item.pkg != pkg_id || import_item.target != ast.INVALID_PACKAGE {
continue continue
} }
if filepath.is_abs(import_item.path) { if filepath.is_abs(import_item.path) {
@@ -181,7 +192,7 @@ load_package :: proc(state: ^State, path: string, import_span: source.Span, is_r
target_path, target_ok := resolve_import_path(state, canonical, import_item.path) target_path, target_ok := resolve_import_path(state, canonical, import_item.path)
target := load_package(state, target_path, import_item.span) target := load_package(state, target_path, import_item.span)
state.module.imports[import_id].target = target state.module.imports[import_id].target = target
if !target_ok || target < 0 || !state.module.packages[target].available { if !target_ok || target == ast.INVALID_PACKAGE || !state.module.packages[target].available {
state.module.imports[import_id].valid = false state.module.imports[import_id].valid = false
} }
delete(target_path, state.allocator) delete(target_path, state.allocator)
@@ -189,7 +200,7 @@ load_package :: proc(state: ^State, path: string, import_span: source.Span, is_r
return pkg_id return pkg_id
} }
declaration_conflicts :: proc(module: ^ast.Module, pkg: int, name: symbol.Id) -> bool { declaration_conflicts :: proc(module: ^ast.Module, pkg: ast.Package_Id, name: symbol.Id) -> bool {
for function in module.functions { for function in module.functions {
if function.pkg == pkg && function.name == name { if function.pkg == pkg && function.name == name {
return true return true
@@ -205,7 +216,7 @@ declaration_conflicts :: proc(module: ^ast.Module, pkg: int, name: symbol.Id) ->
validate_imports :: proc(state: ^State) { validate_imports :: proc(state: ^State) {
for import_item, import_id in state.module.imports { for import_item, import_id in state.module.imports {
if !symbol.is_valid(import_item.alias) && import_item.target >= 0 { if !symbol.is_valid(import_item.alias) && import_item.target != ast.INVALID_PACKAGE {
state.module.imports[import_id].alias = state.module.packages[import_item.target].name state.module.imports[import_id].alias = state.module.packages[import_item.target].name
} }
alias := state.module.imports[import_id].alias alias := state.module.imports[import_id].alias
@@ -260,7 +271,7 @@ load :: proc(
allocator=allocator, allocator=allocator,
} }
root := load_package(&state, root_path, source.Span{}, true) root := load_package(&state, root_path, source.Span{}, true)
if root != 0 && root >= 0 { if root != ast.Package_Id(0) && root != ast.INVALID_PACKAGE {
state.root_failed = true state.root_failed = true
} }
validate_imports(&state) validate_imports(&state)
+92 -82
View File
@@ -10,20 +10,20 @@ import "core:mem"
State :: struct { State :: struct {
hir_module: ^hir.Module, hir_module: ^hir.Module,
instructions: [dynamic]ir.Instruction, instructions: [dynamic]ir.Instruction,
local_values: []int, local_values: []ir.Instruction_Id,
local_slots: []int, local_slots: []ir.Instruction_Id,
expr_stack: [dynamic]Lower_Expr_Frame, expr_stack: [dynamic]Lower_Expr_Frame,
allocator: mem.Allocator, allocator: mem.Allocator,
} }
append_instruction :: proc(state: ^State, instruction: ir.Instruction) -> int { append_instruction :: proc(state: ^State, instruction: ir.Instruction) -> ir.Instruction_Id {
id := len(state.instructions) id := ir.instruction_id(len(state.instructions))
append(&state.instructions, instruction) append(&state.instructions, instruction)
return id return id
} }
clone_args :: proc(values: []int, allocator: mem.Allocator) -> []int { clone_args :: proc(values: []ir.Instruction_Id, allocator: mem.Allocator) -> []ir.Instruction_Id {
result := make([]int, len(values), allocator) result := make([]ir.Instruction_Id, len(values), allocator)
copy(result, values) copy(result, values)
return result return result
} }
@@ -37,14 +37,19 @@ sentinel :: proc(value_type: types.Type) -> i64 {
} }
} }
append_recovery_value :: proc(state: ^State, span: source.Span, value_type: types.Type, diagnostic := -1) -> int { append_recovery_value :: proc(
state: ^State,
span: source.Span,
value_type: types.Type,
diagnostic := source.INVALID_DIAGNOSTIC,
) -> ir.Instruction_Id {
append_instruction(state, ir.Instruction{ append_instruction(state, ir.Instruction{
op=.Trap, op=.Trap,
span=span, span=span,
type=types.VOID, type=types.VOID,
target=-1, target=ir.INVALID_REF,
a=-1, a=ir.INVALID_INSTRUCTION,
b=-1, b=ir.INVALID_INSTRUCTION,
diagnostic=diagnostic, diagnostic=diagnostic,
}) })
fallback := value_type fallback := value_type
@@ -56,22 +61,22 @@ append_recovery_value :: proc(state: ^State, span: source.Span, value_type: type
span=span, span=span,
type=fallback, type=fallback,
integer=sentinel(fallback), integer=sentinel(fallback),
target=-1, target=ir.INVALID_REF,
a=-1, a=ir.INVALID_INSTRUCTION,
b=-1, b=ir.INVALID_INSTRUCTION,
diagnostic=-1, diagnostic=source.INVALID_DIAGNOSTIC,
}) })
} }
Lower_Expr_Frame :: struct { Lower_Expr_Frame :: struct {
expr: int, expr: hir.Expr_Id,
stage: u8, stage: u8,
left: int, left: ir.Instruction_Id,
arg_index: int, arg_index: int,
args: []int, args: []ir.Instruction_Id,
} }
lower_expr :: proc(state: ^State, expr_id: int) -> int { lower_expr :: proc(state: ^State, expr_id: hir.Expr_Id) -> ir.Instruction_Id {
stack := state.expr_stack stack := state.expr_stack
clear_dynamic_array(&stack) clear_dynamic_array(&stack)
defer { defer {
@@ -82,11 +87,11 @@ lower_expr :: proc(state: ^State, expr_id: int) -> int {
state.expr_stack = stack state.expr_stack = stack
} }
append(&stack, Lower_Expr_Frame{expr=expr_id}) append(&stack, Lower_Expr_Frame{expr=expr_id})
last := -1 last := ir.INVALID_INSTRUCTION
for len(stack) > 0 { for len(stack) > 0 {
frame_index := len(stack)-1 frame_index := len(stack)-1
frame := stack[frame_index] frame := stack[frame_index]
if frame.expr < 0 || frame.expr >= len(state.hir_module.exprs) { if frame.expr == hir.INVALID_EXPR || int(frame.expr) >= len(state.hir_module.exprs) {
last = append_recovery_value(state, source.Span{}, types.I64) last = append_recovery_value(state, source.Span{}, types.I64)
_ = pop(&stack) _ = pop(&stack)
continue continue
@@ -100,31 +105,33 @@ lower_expr :: proc(state: ^State, expr_id: int) -> int {
case .Integer: case .Integer:
last = append_instruction(state, ir.Instruction{ last = append_instruction(state, ir.Instruction{
op=.Const, span=expr.span, type=expr.type, integer=expr.integer, op=.Const, span=expr.span, type=expr.type, integer=expr.integer,
target=-1, a=-1, b=-1, diagnostic=-1, target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC,
}) })
_ = pop(&stack) _ = pop(&stack)
case .Local: case .Local:
last = -1 last = ir.INVALID_INSTRUCTION
if expr.target >= 0 && expr.target < len(state.local_slots) && state.local_slots[expr.target] >= 0 { local := hir.as_local(expr.target)
if local != hir.INVALID_LOCAL && int(local) < len(state.local_slots) && state.local_slots[local] != ir.INVALID_INSTRUCTION {
last = append_instruction(state, ir.Instruction{ last = append_instruction(state, ir.Instruction{
op=.Load, span=expr.span, type=expr.type, target=-1, op=.Load, span=expr.span, type=expr.type, target=ir.INVALID_REF,
a=state.local_slots[expr.target], b=-1, diagnostic=-1, a=state.local_slots[local], b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC,
}) })
} else if expr.target >= 0 && expr.target < len(state.local_values) && } else if local != hir.INVALID_LOCAL && int(local) < len(state.local_values) &&
state.local_values[expr.target] >= 0 { state.local_values[local] != ir.INVALID_INSTRUCTION {
last = state.local_values[expr.target] last = state.local_values[local]
} }
if last < 0 { if last == ir.INVALID_INSTRUCTION {
last = append_recovery_value(state, expr.span, expr.type, expr.diagnostic) last = append_recovery_value(state, expr.span, expr.type, expr.diagnostic)
} }
_ = pop(&stack) _ = pop(&stack)
case .Global: case .Global:
if expr.target < 0 || expr.target >= len(state.hir_module.globals) { global := hir.as_global(expr.target)
if global == hir.INVALID_GLOBAL || int(global) >= len(state.hir_module.globals) {
last = append_recovery_value(state, expr.span, expr.type, expr.diagnostic) last = append_recovery_value(state, expr.span, expr.type, expr.diagnostic)
} else { } else {
last = append_instruction(state, ir.Instruction{ last = append_instruction(state, ir.Instruction{
op=.Load_Global, span=expr.span, type=expr.type, target=expr.target, op=.Load_Global, span=expr.span, type=expr.type, target=ir.global_ref(ir.Global_Id(global)),
a=-1, b=-1, diagnostic=-1, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC,
}) })
} }
_ = pop(&stack) _ = pop(&stack)
@@ -135,12 +142,13 @@ lower_expr :: proc(state: ^State, expr_id: int) -> int {
stack[frame_index].stage = 2 stack[frame_index].stage = 2
append(&stack, Lower_Expr_Frame{expr=expr.left}) append(&stack, Lower_Expr_Frame{expr=expr.left})
case .Call: case .Call:
if expr.target < 0 || expr.target >= len(state.hir_module.functions) { function := hir.as_function(expr.target)
if function == hir.INVALID_FUNCTION || int(function) >= len(state.hir_module.functions) {
last = append_recovery_value(state, expr.span, expr.type, expr.diagnostic) last = append_recovery_value(state, expr.span, expr.type, expr.diagnostic)
_ = pop(&stack) _ = pop(&stack)
continue continue
} }
stack[frame_index].args = make([]int, len(expr.args), state.allocator) stack[frame_index].args = make([]ir.Instruction_Id, len(expr.args), state.allocator)
stack[frame_index].stage = 4 stack[frame_index].stage = 4
if len(expr.args) > 0 { if len(expr.args) > 0 {
append(&stack, Lower_Expr_Frame{expr=expr.args[0]}) append(&stack, Lower_Expr_Frame{expr=expr.args[0]})
@@ -150,8 +158,8 @@ lower_expr :: proc(state: ^State, expr_id: int) -> int {
} }
if frame.stage == 1 { if frame.stage == 1 {
last = append_instruction(state, ir.Instruction{ last = append_instruction(state, ir.Instruction{
op=.Widen, span=expr.span, type=expr.type, target=-1, op=.Widen, span=expr.span, type=expr.type, target=ir.INVALID_REF,
a=last, b=-1, diagnostic=-1, a=last, b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC,
}) })
_ = pop(&stack) _ = pop(&stack)
continue continue
@@ -164,8 +172,8 @@ lower_expr :: proc(state: ^State, expr_id: int) -> int {
} }
if frame.stage == 3 { if frame.stage == 3 {
last = append_instruction(state, ir.Instruction{ last = append_instruction(state, ir.Instruction{
op=.Add_Checked, span=expr.span, type=expr.type, target=-1, op=.Add_Checked, span=expr.span, type=expr.type, target=ir.INVALID_REF,
a=frame.left, b=last, diagnostic=-1, a=frame.left, b=last, diagnostic=source.INVALID_DIAGNOSTIC,
}) })
_ = pop(&stack) _ = pop(&stack)
continue continue
@@ -180,8 +188,8 @@ lower_expr :: proc(state: ^State, expr_id: int) -> int {
} }
} }
last = append_instruction(state, ir.Instruction{ last = append_instruction(state, ir.Instruction{
op=.Call, span=expr.span, type=expr.type, target=expr.target, op=.Call, span=expr.span, type=expr.type, target=ir.function_ref(ir.Function_Id(hir.as_function(expr.target))),
a=-1, b=-1, args=stack[frame_index].args, diagnostic=-1, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION, args=stack[frame_index].args, diagnostic=source.INVALID_DIAGNOSTIC,
}) })
stack[frame_index].args = nil stack[frame_index].args = nil
_ = pop(&stack) _ = pop(&stack)
@@ -194,8 +202,8 @@ lower_body :: proc(hir_module: ^hir.Module, function: hir.Function, allocator: m
state := State{ state := State{
hir_module=hir_module, hir_module=hir_module,
allocator=allocator, allocator=allocator,
local_values=make([]int, len(function.locals), allocator), local_values=make([]ir.Instruction_Id, len(function.locals), allocator),
local_slots=make([]int, len(function.locals), allocator), local_slots=make([]ir.Instruction_Id, len(function.locals), allocator),
} }
state.instructions.allocator = allocator state.instructions.allocator = allocator
state.expr_stack.allocator = allocator state.expr_stack.allocator = allocator
@@ -205,17 +213,17 @@ lower_body :: proc(hir_module: ^hir.Module, function: hir.Function, allocator: m
delete(state.expr_stack) delete(state.expr_stack)
} }
for _, index in state.local_values { for _, index in state.local_values {
state.local_values[index] = -1 state.local_values[index] = ir.INVALID_INSTRUCTION
state.local_slots[index] = -1 state.local_slots[index] = ir.INVALID_INSTRUCTION
} }
for local_id in function.params { for local_id in function.params {
param := append_instruction(&state, ir.Instruction{ param := append_instruction(&state, ir.Instruction{
op=.Param, op=.Param,
type=function.locals[local_id].type, type=function.locals[local_id].type,
target=local_id, target=ir.local_ref(ir.Local_Id(local_id)),
a=-1, a=ir.INVALID_INSTRUCTION,
b=-1, b=ir.INVALID_INSTRUCTION,
diagnostic=-1, diagnostic=source.INVALID_DIAGNOSTIC,
}) })
state.local_values[local_id] = param state.local_values[local_id] = param
} }
@@ -225,10 +233,10 @@ lower_body :: proc(hir_module: ^hir.Module, function: hir.Function, allocator: m
switch statement.kind { switch statement.kind {
case .Declaration: case .Declaration:
value := lower_expr(&state, statement.expr) value := lower_expr(&state, statement.expr)
if statement.local < 0 || statement.local >= len(function.locals) { if statement.local == hir.INVALID_LOCAL || int(statement.local) >= len(function.locals) {
append_instruction(&state, ir.Instruction{ append_instruction(&state, ir.Instruction{
op=.Trap, span=statement.span, type=types.VOID, op=.Trap, span=statement.span, type=types.VOID,
target=-1, a=-1, b=-1, diagnostic=statement.diagnostic, target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION, diagnostic=statement.diagnostic,
}) })
continue continue
} }
@@ -238,34 +246,34 @@ lower_body :: proc(hir_module: ^hir.Module, function: hir.Function, allocator: m
op=.Alloca, op=.Alloca,
span=statement.span, span=statement.span,
type=local.type, type=local.type,
target=statement.local, target=ir.local_ref(ir.Local_Id(statement.local)),
a=-1, a=ir.INVALID_INSTRUCTION,
b=-1, b=ir.INVALID_INSTRUCTION,
diagnostic=-1, diagnostic=source.INVALID_DIAGNOSTIC,
}) })
state.local_slots[statement.local] = slot state.local_slots[statement.local] = slot
append_instruction(&state, ir.Instruction{ append_instruction(&state, ir.Instruction{
op=.Store, op=.Store,
span=statement.span, span=statement.span,
type=local.type, type=local.type,
target=-1, target=ir.INVALID_REF,
a=slot, a=slot,
b=value, b=value,
diagnostic=-1, diagnostic=source.INVALID_DIAGNOSTIC,
}) })
} else { } else {
state.local_values[statement.local] = value state.local_values[statement.local] = value
} }
case .Assignment: case .Assignment:
value := lower_expr(&state, statement.expr) value := lower_expr(&state, statement.expr)
slot := -1 slot := ir.INVALID_INSTRUCTION
if statement.local >= 0 && statement.local < len(state.local_slots) { if statement.local != hir.INVALID_LOCAL && int(statement.local) < len(state.local_slots) {
slot = state.local_slots[statement.local] slot = state.local_slots[statement.local]
} }
if slot < 0 || statement.local < 0 || statement.local >= len(function.locals) { if slot == ir.INVALID_INSTRUCTION || statement.local == hir.INVALID_LOCAL || int(statement.local) >= len(function.locals) {
append_instruction(&state, ir.Instruction{ append_instruction(&state, ir.Instruction{
op=.Trap, span=statement.span, type=types.VOID, op=.Trap, span=statement.span, type=types.VOID,
target=-1, a=-1, b=-1, diagnostic=statement.diagnostic, target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION, diagnostic=statement.diagnostic,
}) })
continue continue
} }
@@ -273,21 +281,21 @@ lower_body :: proc(hir_module: ^hir.Module, function: hir.Function, allocator: m
op=.Store, op=.Store,
span=statement.span, span=statement.span,
type=function.locals[statement.local].type, type=function.locals[statement.local].type,
target=-1, target=ir.INVALID_REF,
a=slot, a=slot,
b=value, b=value,
diagnostic=-1, diagnostic=source.INVALID_DIAGNOSTIC,
}) })
case .Return: case .Return:
if statement.expr < 0 { if statement.expr == hir.INVALID_EXPR {
append_instruction(&state, ir.Instruction{ append_instruction(&state, ir.Instruction{
op=.Return_Void, op=.Return_Void,
span=statement.span, span=statement.span,
type=types.VOID, type=types.VOID,
target=-1, target=ir.INVALID_REF,
a=-1, a=ir.INVALID_INSTRUCTION,
b=-1, b=ir.INVALID_INSTRUCTION,
diagnostic=-1, diagnostic=source.INVALID_DIAGNOSTIC,
}) })
} else { } else {
value := lower_expr(&state, statement.expr) value := lower_expr(&state, statement.expr)
@@ -295,10 +303,10 @@ lower_body :: proc(hir_module: ^hir.Module, function: hir.Function, allocator: m
op=.Return, op=.Return,
span=statement.span, span=statement.span,
type=function.result, type=function.result,
target=-1, target=ir.INVALID_REF,
a=value, a=value,
b=-1, b=ir.INVALID_INSTRUCTION,
diagnostic=-1, diagnostic=source.INVALID_DIAGNOSTIC,
}) })
} }
case .Expression, .Sink: case .Expression, .Sink:
@@ -308,9 +316,9 @@ lower_body :: proc(hir_module: ^hir.Module, function: hir.Function, allocator: m
op=.Trap, op=.Trap,
span=statement.span, span=statement.span,
type=types.VOID, type=types.VOID,
target=-1, target=ir.INVALID_REF,
a=-1, a=ir.INVALID_INSTRUCTION,
b=-1, b=ir.INVALID_INSTRUCTION,
diagnostic=statement.diagnostic, diagnostic=statement.diagnostic,
}) })
} }
@@ -319,18 +327,18 @@ lower_body :: proc(hir_module: ^hir.Module, function: hir.Function, allocator: m
(state.instructions[len(state.instructions)-1].op != .Return && (state.instructions[len(state.instructions)-1].op != .Return &&
state.instructions[len(state.instructions)-1].op != .Return_Void) { state.instructions[len(state.instructions)-1].op != .Return_Void) {
if function.result.kind == .Void { if function.result.kind == .Void {
append_instruction(&state, ir.Instruction{op=.Return_Void, type=types.VOID, target=-1, a=-1, b=-1, diagnostic=-1}) append_instruction(&state, ir.Instruction{op=.Return_Void, type=types.VOID, target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC})
} else { } else {
value := append_instruction(&state, ir.Instruction{ value := append_instruction(&state, ir.Instruction{
op=.Const, op=.Const,
type=function.result, type=function.result,
integer=sentinel(function.result), integer=sentinel(function.result),
target=-1, target=ir.INVALID_REF,
a=-1, a=ir.INVALID_INSTRUCTION,
b=-1, b=ir.INVALID_INSTRUCTION,
diagnostic=-1, diagnostic=source.INVALID_DIAGNOSTIC,
}) })
append_instruction(&state, ir.Instruction{op=.Return, type=function.result, target=-1, a=value, b=-1, diagnostic=-1}) append_instruction(&state, ir.Instruction{op=.Return, type=function.result, target=ir.INVALID_REF, a=value, b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC})
} }
} }
return state.instructions[:] return state.instructions[:]
@@ -345,10 +353,10 @@ lower_global_initializer :: proc(hir_module: ^hir.Module, global: hir.Global, al
append_instruction(&state, ir.Instruction{ append_instruction(&state, ir.Instruction{
op=.Return, op=.Return,
type=global.type, type=global.type,
target=-1, target=ir.INVALID_REF,
a=value, a=value,
b=-1, b=ir.INVALID_INSTRUCTION,
diagnostic=-1, diagnostic=source.INVALID_DIAGNOSTIC,
}) })
return state.instructions[:] return state.instructions[:]
} }
@@ -356,6 +364,7 @@ lower_global_initializer :: proc(hir_module: ^hir.Module, global: hir.Global, al
lower :: proc(hir_module: ^hir.Module, allocator := context.allocator) -> ir.Module { lower :: proc(hir_module: ^hir.Module, allocator := context.allocator) -> ir.Module {
module := ir.init_module(allocator) module := ir.init_module(allocator)
for global in hir_module.globals { for global in hir_module.globals {
_ = ir.global_id(len(module.globals))
append(&module.globals, ir.Global{ append(&module.globals, ir.Global{
name=global.name, name=global.name,
type=global.type, type=global.type,
@@ -371,6 +380,7 @@ lower :: proc(hir_module: ^hir.Module, allocator := context.allocator) -> ir.Mod
for local_id, index in function.params { for local_id, index in function.params {
param_types[index] = function.locals[local_id].type param_types[index] = function.locals[local_id].type
} }
_ = ir.function_id(len(module.functions))
append(&module.functions, ir.Function{ append(&module.functions, ir.Function{
link_name=fmt.aprintf("%s", function.link_name, allocator=allocator), link_name=fmt.aprintf("%s", function.link_name, allocator=allocator),
calling_convention=.C if function.calling_convention == .C else .Brolang, calling_convention=.C if function.calling_convention == .C else .Brolang,
+58 -52
View File
@@ -13,8 +13,8 @@ Parser :: struct {
source_file: ^source.Source, source_file: ^source.Source,
diagnostics: ^source.Diagnostics, diagnostics: ^source.Diagnostics,
module: ast.Module, module: ast.Module,
pkg: int, pkg: ast.Package_Id,
file: int, file: ast.File_Id,
cursor: int, cursor: int,
delimiter_depth: int, delimiter_depth: int,
} }
@@ -22,10 +22,10 @@ Parser :: struct {
MAX_EXPRESSION_NESTING :: 256 MAX_EXPRESSION_NESTING :: 256
token_text :: proc(parser: ^Parser, tok: token.Token) -> string { token_text :: proc(parser: ^Parser, tok: token.Token) -> string {
if tok.span.start < 0 || tok.span.end < tok.span.start || tok.span.end > len(parser.source_file.text) { if tok.span.end < tok.span.start || int(tok.span.end) > len(parser.source_file.text) {
return "" return ""
} }
return parser.source_file.text[tok.span.start:tok.span.end] return parser.source_file.text[int(tok.span.start):int(tok.span.end)]
} }
span_from :: proc(first, last: source.Span) -> source.Span { span_from :: proc(first, last: source.Span) -> source.Span {
@@ -61,19 +61,19 @@ skip_newlines :: proc(parser: ^Parser) {
} }
} }
add_expr :: proc(parser: ^Parser, expr: ast.Expr) -> int { add_expr :: proc(parser: ^Parser, expr: ast.Expr) -> ast.Expr_Id {
id := len(parser.module.exprs) id := ast.expr_id(len(parser.module.exprs))
append(&parser.module.exprs, expr) append(&parser.module.exprs, expr)
return id return id
} }
invalid_expr :: proc(parser: ^Parser, span: source.Span, message: string) -> int { invalid_expr :: proc(parser: ^Parser, span: source.Span, message: string) -> ast.Expr_Id {
id := source.add(parser.diagnostics, span, message) id := source.add(parser.diagnostics, span, message)
return add_expr(parser, ast.Expr{ return add_expr(parser, ast.Expr{
kind=.Invalid, kind=.Invalid,
span=span, span=span,
left=ast.INVALID_ID, left=ast.INVALID_EXPR,
right=ast.INVALID_ID, right=ast.INVALID_EXPR,
diagnostic=id, diagnostic=id,
}) })
} }
@@ -131,7 +131,7 @@ skip_parenthesized :: proc(parser: ^Parser) -> source.Span {
return span_from(start.span, end.span) return span_from(start.span, end.span)
} }
parse_call :: proc(parser: ^Parser, qualifier: symbol.Id, first, name: token.Token, nesting: int) -> int { parse_call :: proc(parser: ^Parser, qualifier: symbol.Id, first, name: token.Token, nesting: int) -> ast.Expr_Id {
if nesting >= MAX_EXPRESSION_NESTING { if nesting >= MAX_EXPRESSION_NESTING {
span := skip_parenthesized(parser) span := skip_parenthesized(parser)
return invalid_expr(parser, span, "expression nesting exceeds 256 levels") return invalid_expr(parser, span, "expression nesting exceeds 256 levels")
@@ -139,7 +139,7 @@ parse_call :: proc(parser: ^Parser, qualifier: symbol.Id, first, name: token.Tok
left_paren := advance(parser) left_paren := advance(parser)
parser.delimiter_depth += 1 parser.delimiter_depth += 1
defer parser.delimiter_depth -= 1 defer parser.delimiter_depth -= 1
args: [dynamic]int args: [dynamic]ast.Expr_Id
args.allocator = parser.module.allocator args.allocator = parser.module.allocator
skip_newlines(parser) skip_newlines(parser)
for current(parser).kind != .Right_Paren && current(parser).kind != .Eof { for current(parser).kind != .Right_Paren && current(parser).kind != .Eof {
@@ -162,13 +162,13 @@ parse_call :: proc(parser: ^Parser, qualifier: symbol.Id, first, name: token.Tok
qualifier=qualifier, qualifier=qualifier,
name=name.symbol, name=name.symbol,
args=args[:], args=args[:],
left=ast.INVALID_ID, left=ast.INVALID_EXPR,
right=ast.INVALID_ID, right=ast.INVALID_EXPR,
diagnostic=-1, diagnostic=source.INVALID_DIAGNOSTIC,
}) })
} }
parse_primary :: proc(parser: ^Parser, nesting: int) -> int { parse_primary :: proc(parser: ^Parser, nesting: int) -> ast.Expr_Id {
tok := current(parser) tok := current(parser)
#partial switch tok.kind { #partial switch tok.kind {
case .Integer: case .Integer:
@@ -181,9 +181,9 @@ parse_primary :: proc(parser: ^Parser, nesting: int) -> int {
kind=.Integer, kind=.Integer,
span=tok.span, span=tok.span,
integer=value, integer=value,
left=ast.INVALID_ID, left=ast.INVALID_EXPR,
right=ast.INVALID_ID, right=ast.INVALID_EXPR,
diagnostic=-1, diagnostic=source.INVALID_DIAGNOSTIC,
}) })
case .Identifier: case .Identifier:
first := advance(parser) first := advance(parser)
@@ -204,9 +204,9 @@ parse_primary :: proc(parser: ^Parser, nesting: int) -> int {
span=span_from(first.span, name.span), span=span_from(first.span, name.span),
qualifier=qualifier, qualifier=qualifier,
name=name.symbol, name=name.symbol,
left=ast.INVALID_ID, left=ast.INVALID_EXPR,
right=ast.INVALID_ID, right=ast.INVALID_EXPR,
diagnostic=-1, diagnostic=source.INVALID_DIAGNOSTIC,
}) })
case .Underscore: case .Underscore:
advance(parser) advance(parser)
@@ -231,8 +231,8 @@ parse_primary :: proc(parser: ^Parser, nesting: int) -> int {
return add_expr(parser, ast.Expr{ return add_expr(parser, ast.Expr{
kind=.Invalid, kind=.Invalid,
span=tok.span, span=tok.span,
left=ast.INVALID_ID, left=ast.INVALID_EXPR,
right=ast.INVALID_ID, right=ast.INVALID_EXPR,
diagnostic=tok.diagnostic, diagnostic=tok.diagnostic,
}) })
} }
@@ -250,7 +250,7 @@ infix_binding_power :: proc(kind: token.Kind) -> (left, right: int, ok: bool) {
return 0, 0, false return 0, 0, false
} }
parse_expression_bp :: proc(parser: ^Parser, minimum_binding_power, nesting: int) -> int { parse_expression_bp :: proc(parser: ^Parser, minimum_binding_power, nesting: int) -> ast.Expr_Id {
if nesting > MAX_EXPRESSION_NESTING { if nesting > MAX_EXPRESSION_NESTING {
tok := current(parser) tok := current(parser)
if tok.kind != .Newline && tok.kind != .Right_Brace && tok.kind != .Eof { if tok.kind != .Newline && tok.kind != .Right_Brace && tok.kind != .Eof {
@@ -277,7 +277,7 @@ parse_expression_bp :: proc(parser: ^Parser, minimum_binding_power, nesting: int
span=span_from(left_expr.span, right_expr.span), span=span_from(left_expr.span, right_expr.span),
left=left, left=left,
right=right, right=right,
diagnostic=-1, diagnostic=source.INVALID_DIAGNOSTIC,
}) })
if parser.delimiter_depth > 0 { if parser.delimiter_depth > 0 {
skip_newlines(parser) skip_newlines(parser)
@@ -286,17 +286,17 @@ parse_expression_bp :: proc(parser: ^Parser, minimum_binding_power, nesting: int
return left return left
} }
parse_expression :: proc(parser: ^Parser) -> int { parse_expression :: proc(parser: ^Parser) -> ast.Expr_Id {
return parse_expression_bp(parser, 0, 0) return parse_expression_bp(parser, 0, 0)
} }
finish_statement :: proc(parser: ^Parser, allow_closing_brace := false) -> int { finish_statement :: proc(parser: ^Parser, allow_closing_brace := false) -> source.Diagnostic_Id {
if current(parser).kind == .Newline { if current(parser).kind == .Newline {
skip_newlines(parser) skip_newlines(parser)
return -1 return source.INVALID_DIAGNOSTIC
} }
if current(parser).kind == .Eof || allow_closing_brace && current(parser).kind == .Right_Brace { if current(parser).kind == .Eof || allow_closing_brace && current(parser).kind == .Right_Brace {
return -1 return source.INVALID_DIAGNOSTIC
} }
diagnostic := source.add( diagnostic := source.add(
parser.diagnostics, parser.diagnostics,
@@ -312,33 +312,33 @@ finish_statement :: proc(parser: ^Parser, allow_closing_brace := false) -> int {
return diagnostic return diagnostic
} }
parse_return :: proc(parser: ^Parser) -> int { parse_return :: proc(parser: ^Parser) -> ast.Stmt_Id {
start := advance(parser) start := advance(parser)
skip_newlines(parser) skip_newlines(parser)
if current(parser).kind == .Underscore { if current(parser).kind == .Underscore {
end := advance(parser) end := advance(parser)
id := len(parser.module.statements) id := ast.stmt_id(len(parser.module.statements))
append(&parser.module.statements, ast.Stmt{ append(&parser.module.statements, ast.Stmt{
kind=.Return, kind=.Return,
span=span_from(start.span, end.span), span=span_from(start.span, end.span),
name=end.symbol, name=end.symbol,
expr=ast.INVALID_ID, expr=ast.INVALID_EXPR,
diagnostic=-1, diagnostic=source.INVALID_DIAGNOSTIC,
}) })
return id return id
} }
expr := parse_expression(parser) expr := parse_expression(parser)
id := len(parser.module.statements) id := ast.stmt_id(len(parser.module.statements))
append(&parser.module.statements, ast.Stmt{ append(&parser.module.statements, ast.Stmt{
kind=.Return, kind=.Return,
span=span_from(start.span, parser.module.exprs[expr].span), span=span_from(start.span, parser.module.exprs[expr].span),
expr=expr, expr=expr,
diagnostic=-1, diagnostic=source.INVALID_DIAGNOSTIC,
}) })
return id return id
} }
parse_statement :: proc(parser: ^Parser) -> int { parse_statement :: proc(parser: ^Parser) -> ast.Stmt_Id {
if current(parser).kind == .Keyword_Return { if current(parser).kind == .Keyword_Return {
return parse_return(parser) return parse_return(parser)
} }
@@ -363,7 +363,7 @@ parse_statement :: proc(parser: ^Parser) -> int {
kind = .Declaration kind = .Declaration
immutable = operator.kind == .Colon_Colon immutable = operator.kind == .Colon_Colon
} }
id := len(parser.module.statements) id := ast.stmt_id(len(parser.module.statements))
append(&parser.module.statements, ast.Stmt{ append(&parser.module.statements, ast.Stmt{
kind=kind, kind=kind,
span=span_from(name.span, parser.module.exprs[expr].span), span=span_from(name.span, parser.module.exprs[expr].span),
@@ -371,7 +371,7 @@ parse_statement :: proc(parser: ^Parser) -> int {
type=type_syntax, type=type_syntax,
immutable=immutable, immutable=immutable,
expr=expr, expr=expr,
diagnostic=-1, diagnostic=source.INVALID_DIAGNOSTIC,
}) })
return id return id
} }
@@ -379,12 +379,12 @@ parse_statement :: proc(parser: ^Parser) -> int {
} }
expr := parse_expression(parser) expr := parse_expression(parser)
id := len(parser.module.statements) id := ast.stmt_id(len(parser.module.statements))
append(&parser.module.statements, ast.Stmt{ append(&parser.module.statements, ast.Stmt{
kind=.Expression, kind=.Expression,
span=parser.module.exprs[expr].span, span=parser.module.exprs[expr].span,
expr=expr, expr=expr,
diagnostic=-1, diagnostic=source.INVALID_DIAGNOSTIC,
}) })
return id return id
} }
@@ -446,6 +446,7 @@ parse_function :: proc(parser: ^Parser, name: token.Token, c_abi: bool) {
if !ended_by_newline && current(parser).kind != .Eof { if !ended_by_newline && current(parser).kind != .Eof {
_ = finish_statement(parser) _ = finish_statement(parser)
} }
_ = ast.function_id(len(parser.module.functions))
append(&parser.module.functions, ast.Function{ append(&parser.module.functions, ast.Function{
span=span_from(name.span, end.span), span=span_from(name.span, end.span),
name=name.symbol, name=name.symbol,
@@ -455,23 +456,23 @@ parse_function :: proc(parser: ^Parser, name: token.Token, c_abi: bool) {
has_body=false, has_body=false,
params=params, params=params,
result=result, result=result,
diagnostic=-1, diagnostic=source.INVALID_DIAGNOSTIC,
}) })
return return
} }
advance(parser) advance(parser)
body: [dynamic]int body: [dynamic]ast.Stmt_Id
body.allocator = parser.module.allocator body.allocator = parser.module.allocator
skip_newlines(parser) skip_newlines(parser)
for current(parser).kind != .Right_Brace && current(parser).kind != .Eof { for current(parser).kind != .Right_Brace && current(parser).kind != .Eof {
append(&body, parse_statement(parser)) append(&body, parse_statement(parser))
if diagnostic := finish_statement(parser, true); diagnostic >= 0 { if diagnostic := finish_statement(parser, true); diagnostic != source.INVALID_DIAGNOSTIC {
statement_id := len(parser.module.statements) statement_id := ast.stmt_id(len(parser.module.statements))
append(&parser.module.statements, ast.Stmt{ append(&parser.module.statements, ast.Stmt{
kind=.Invalid, kind=.Invalid,
span=current(parser).span, span=current(parser).span,
expr=ast.INVALID_ID, expr=ast.INVALID_EXPR,
diagnostic=diagnostic, diagnostic=diagnostic,
}) })
append(&body, statement_id) append(&body, statement_id)
@@ -482,6 +483,7 @@ parse_function :: proc(parser: ^Parser, name: token.Token, c_abi: bool) {
source.add(parser.diagnostics, current(parser).span, "expected '}' after function body") source.add(parser.diagnostics, current(parser).span, "expected '}' after function body")
end = func_token end = func_token
} }
_ = ast.function_id(len(parser.module.functions))
append(&parser.module.functions, ast.Function{ append(&parser.module.functions, ast.Function{
span=span_from(name.span, end.span), span=span_from(name.span, end.span),
name=name.symbol, name=name.symbol,
@@ -492,7 +494,7 @@ parse_function :: proc(parser: ^Parser, name: token.Token, c_abi: bool) {
params=params, params=params,
result=result, result=result,
body=body[:], body=body[:],
diagnostic=-1, diagnostic=source.INVALID_DIAGNOSTIC,
}) })
} }
@@ -522,28 +524,30 @@ parse_import :: proc(parser: ^Parser, alias: token.Token, start: token.Token) {
if path_token.kind != .Newline && path_token.kind != .Eof { if path_token.kind != .Newline && path_token.kind != .Eof {
advance(parser) advance(parser)
} }
_ = ast.import_id(len(parser.module.imports))
append(&parser.module.imports, ast.Import{ append(&parser.module.imports, ast.Import{
span=start.span, span=start.span,
alias=alias.symbol, alias=alias.symbol,
pkg=parser.pkg, pkg=parser.pkg,
file=parser.file, file=parser.file,
target=-1, target=ast.INVALID_PACKAGE,
valid=false, valid=false,
diagnostic=-1, diagnostic=source.INVALID_DIAGNOSTIC,
}) })
_ = finish_statement(parser) _ = finish_statement(parser)
return return
} }
advance(parser) advance(parser)
_ = ast.import_id(len(parser.module.imports))
append(&parser.module.imports, ast.Import{ append(&parser.module.imports, ast.Import{
span=span_from(start.span, path_token.span), span=span_from(start.span, path_token.span),
alias=alias.symbol, alias=alias.symbol,
path=decode_import_path(parser, path_token), path=decode_import_path(parser, path_token),
pkg=parser.pkg, pkg=parser.pkg,
file=parser.file, file=parser.file,
target=-1, target=ast.INVALID_PACKAGE,
valid=true, valid=true,
diagnostic=-1, diagnostic=source.INVALID_DIAGNOSTIC,
}) })
_ = finish_statement(parser) _ = finish_statement(parser)
} }
@@ -606,6 +610,7 @@ parse_top_level :: proc(parser: ^Parser) {
} }
expr := parse_expression(parser) expr := parse_expression(parser)
_ = ast.global_id(len(parser.module.globals))
append(&parser.module.globals, ast.Global{ append(&parser.module.globals, ast.Global{
span=span_from(name.span, parser.module.exprs[expr].span), span=span_from(name.span, parser.module.exprs[expr].span),
name=name.symbol, name=name.symbol,
@@ -614,7 +619,7 @@ parse_top_level :: proc(parser: ^Parser) {
type=type_syntax, type=type_syntax,
immutable=operator.kind == .Colon_Colon, immutable=operator.kind == .Colon_Colon,
expr=expr, expr=expr,
diagnostic=-1, diagnostic=source.INVALID_DIAGNOSTIC,
}) })
_ = finish_statement(parser) _ = finish_statement(parser)
} }
@@ -644,7 +649,8 @@ parse_into :: proc(
source_file: ^source.Source, source_file: ^source.Source,
diagnostics: ^source.Diagnostics, diagnostics: ^source.Diagnostics,
module: ^ast.Module, module: ^ast.Module,
pkg, file: int, pkg: ast.Package_Id,
file: ast.File_Id,
) { ) {
parser := Parser{ parser := Parser{
tokens=stream, tokens=stream,
+63 -27
View File
@@ -3,17 +3,48 @@ package source
import "core:fmt" import "core:fmt"
import "core:mem" import "core:mem"
Source_Id :: distinct u32
Diagnostic_Id :: distinct u32
Offset :: distinct u32
INVALID_SOURCE :: Source_Id(0xffff_ffff)
INVALID_DIAGNOSTIC :: Diagnostic_Id(0xffff_ffff)
source_id :: proc(index: int) -> Source_Id {
assert(index >= 0 && u64(index) < u64(INVALID_SOURCE))
return Source_Id(index)
}
diagnostic_id :: proc(index: int) -> Diagnostic_Id {
assert(index >= 0 && u64(index) < u64(INVALID_DIAGNOSTIC))
return Diagnostic_Id(index)
}
source_index :: proc(id: Source_Id, count: int) -> (int, bool) {
index := int(id)
return index, id != INVALID_SOURCE && index < count
}
diagnostic_index :: proc(id: Diagnostic_Id, count: int) -> (int, bool) {
index := int(id)
return index, id != INVALID_DIAGNOSTIC && index < count
}
fits_source_length :: proc(length: u64) -> bool {
return length <= u64(max(Offset))
}
Span :: struct { Span :: struct {
file: int, file: Source_Id,
start: int, start: Offset,
end: int, end: Offset,
} }
Source :: struct { Source :: struct {
id: int, id: Source_Id,
path: string, path: string,
text: string, text: string,
line_starts: []int, line_starts: []Offset,
} }
Store :: struct { Store :: struct {
@@ -30,7 +61,7 @@ Diagnostics :: struct {
source: ^Source, source: ^Source,
store: ^Store, store: ^Store,
items: [dynamic]Diagnostic, items: [dynamic]Diagnostic,
index: map[Diagnostic_Key]int, index: map[Diagnostic_Key]Diagnostic_Id,
allocator: mem.Allocator, allocator: mem.Allocator,
} }
@@ -55,20 +86,22 @@ destroy_store :: proc(store: ^Store) {
delete(store.items) delete(store.items)
} }
make_line_starts :: proc(text: string, allocator: mem.Allocator) -> []int { make_line_starts :: proc(text: string, allocator: mem.Allocator) -> []Offset {
result: [dynamic]int assert(fits_source_length(u64(len(text))))
result: [dynamic]Offset
result.allocator = allocator result.allocator = allocator
append(&result, 0) append(&result, 0)
for value, offset in transmute([]byte)text { for value, offset in transmute([]byte)text {
if value == '\n' { if value == '\n' {
append(&result, offset+1) append(&result, Offset(offset+1))
} }
} }
return result[:] return result[:]
} }
add_source_owned :: proc(store: ^Store, path: string, text: []byte) -> int { add_source_owned :: proc(store: ^Store, path: string, text: []byte) -> Source_Id {
id := len(store.items) assert(fits_source_length(u64(len(text))))
id := source_id(len(store.items))
owned_text := string(text) owned_text := string(text)
append(&store.items, Source{ append(&store.items, Source{
id=id, id=id,
@@ -79,7 +112,7 @@ add_source_owned :: proc(store: ^Store, path: string, text: []byte) -> int {
return id return id
} }
add_source :: proc(store: ^Store, path, text: string) -> int { add_source :: proc(store: ^Store, path, text: string) -> Source_Id {
owned := make([]byte, len(text), store.allocator) owned := make([]byte, len(text), store.allocator)
copy(owned, transmute([]byte)text) copy(owned, transmute([]byte)text)
return add_source_owned(store, path, owned) return add_source_owned(store, path, owned)
@@ -111,51 +144,51 @@ destroy_diagnostics :: proc(diagnostics: ^Diagnostics) {
delete(diagnostics.items) delete(diagnostics.items)
} }
add :: proc(diagnostics: ^Diagnostics, span: Span, message: string) -> int { add :: proc(diagnostics: ^Diagnostics, span: Span, message: string) -> Diagnostic_Id {
key := Diagnostic_Key{span=span, message=message} key := Diagnostic_Key{span=span, message=message}
if id, ok := diagnostics.index[key]; ok { if id, ok := diagnostics.index[key]; ok {
return id return id
} }
id := len(diagnostics.items) id := diagnostic_id(len(diagnostics.items))
cloned := fmt.aprintf("%s", message, allocator=diagnostics.allocator) cloned := fmt.aprintf("%s", message, allocator=diagnostics.allocator)
append(&diagnostics.items, Diagnostic{span=span, message=cloned}) append(&diagnostics.items, Diagnostic{span=span, message=cloned})
diagnostics.index[Diagnostic_Key{span=span, message=cloned}] = id diagnostics.index[Diagnostic_Key{span=span, message=cloned}] = id
return id return id
} }
addf :: proc(diagnostics: ^Diagnostics, span: Span, format: string, args: ..any) -> int { addf :: proc(diagnostics: ^Diagnostics, span: Span, format: string, args: ..any) -> Diagnostic_Id {
message := fmt.aprintf(format, ..args, allocator=diagnostics.allocator) message := fmt.aprintf(format, ..args, allocator=diagnostics.allocator)
key := Diagnostic_Key{span=span, message=message} key := Diagnostic_Key{span=span, message=message}
if id, ok := diagnostics.index[key]; ok { if id, ok := diagnostics.index[key]; ok {
delete(message, diagnostics.allocator) delete(message, diagnostics.allocator)
return id return id
} }
id := len(diagnostics.items) id := diagnostic_id(len(diagnostics.items))
append(&diagnostics.items, Diagnostic{span=span, message=message}) append(&diagnostics.items, Diagnostic{span=span, message=message})
diagnostics.index[Diagnostic_Key{span=span, message=message}] = id diagnostics.index[Diagnostic_Key{span=span, message=message}] = id
return id return id
} }
line_and_column :: proc(source_file: ^Source, offset: int) -> (line, column: int) { line_and_column :: proc(source_file: ^Source, offset: Offset) -> (line, column: int) {
if len(source_file.line_starts) > 0 { if len(source_file.line_starts) > 0 {
limit := min(max(offset, 0), len(source_file.text)) limit := min(int(offset), len(source_file.text))
low := 0 low := 0
high := len(source_file.line_starts) high := len(source_file.line_starts)
for low < high { for low < high {
middle := low + (high-low)/2 middle := low + (high-low)/2
if source_file.line_starts[middle] <= limit { if int(source_file.line_starts[middle]) <= limit {
low = middle+1 low = middle+1
} else { } else {
high = middle high = middle
} }
} }
line = max(low, 1) line = max(low, 1)
column = limit-source_file.line_starts[line-1]+1 column = limit-int(source_file.line_starts[line-1])+1
return return
} }
line = 1 line = 1
column = 1 column = 1
limit := min(max(offset, 0), len(source_file.text)) limit := min(int(offset), len(source_file.text))
for byte_value in transmute([]byte)source_file.text[:limit] { for byte_value in transmute([]byte)source_file.text[:limit] {
if byte_value == '\n' { if byte_value == '\n' {
line += 1 line += 1
@@ -168,14 +201,17 @@ line_and_column :: proc(source_file: ^Source, offset: int) -> (line, column: int
} }
source_for_span :: proc(diagnostics: ^Diagnostics, span: Span) -> ^Source { source_for_span :: proc(diagnostics: ^Diagnostics, span: Span) -> ^Source {
if diagnostics.store != nil && span.file >= 0 && span.file < len(diagnostics.store.items) { if diagnostics.store != nil {
return &diagnostics.store.items[span.file] if index, ok := source_index(span.file, len(diagnostics.store.items)); ok {
return &diagnostics.store.items[index]
}
} }
return diagnostics.source return diagnostics.source
} }
format :: proc(diagnostics: ^Diagnostics, id: int, allocator := context.allocator) -> string { format :: proc(diagnostics: ^Diagnostics, id: Diagnostic_Id, allocator := context.allocator) -> string {
if id < 0 || id >= len(diagnostics.items) { index, ok := diagnostic_index(id, len(diagnostics.items))
if !ok {
path := "<unknown>" path := "<unknown>"
if diagnostics.source != nil { if diagnostics.source != nil {
path = diagnostics.source.path path = diagnostics.source.path
@@ -184,7 +220,7 @@ format :: proc(diagnostics: ^Diagnostics, id: int, allocator := context.allocato
} }
return fmt.aprintf("%s: compiler recovery error", path, allocator=allocator) return fmt.aprintf("%s: compiler recovery error", path, allocator=allocator)
} }
diagnostic := diagnostics.items[id] diagnostic := diagnostics.items[index]
source_file := source_for_span(diagnostics, diagnostic.span) source_file := source_for_span(diagnostics, diagnostic.span)
if source_file == nil { if source_file == nil {
return fmt.aprintf("<unknown>: error: %s", diagnostic.message, allocator=allocator) return fmt.aprintf("<unknown>: error: %s", diagnostic.message, allocator=allocator)
@@ -202,7 +238,7 @@ format :: proc(diagnostics: ^Diagnostics, id: int, allocator := context.allocato
print_all :: proc(diagnostics: ^Diagnostics) { print_all :: proc(diagnostics: ^Diagnostics) {
for _, id in diagnostics.items { for _, id in diagnostics.items {
message := format(diagnostics, id) message := format(diagnostics, diagnostic_id(id))
fmt.eprintln(message) fmt.eprintln(message)
delete(message) delete(message)
} }
+5 -5
View File
@@ -3,7 +3,7 @@ package token
import "../source" import "../source"
import "../symbol" import "../symbol"
Kind :: enum { Kind :: enum u8 {
Invalid, Invalid,
Eof, Eof,
Newline, Newline,
@@ -32,10 +32,10 @@ Kind :: enum {
} }
Token :: struct { Token :: struct {
kind: Kind, span: source.Span,
span: source.Span, symbol: symbol.Id,
symbol: symbol.Id, diagnostic: source.Diagnostic_Id,
diagnostic: int, kind: Kind,
} }
Stream :: struct { Stream :: struct {
+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) 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) @(test)
lexer_preserves_newlines_and_skips_comments :: proc(t: ^testing.T) { lexer_preserves_newlines_and_skips_comments :: proc(t: ^testing.T) {
source_file := source.Source{path="test.bro", text="# comment\nmain :: func() void {}\n"} 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") c_symbol := symbol.intern(&symbols, "c")
for tok in stream.items { 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.kind, token.Kind.Identifier)
testing.expect_value(t, tok.symbol, c_symbol) 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) testing.expect(t, loaded)
found := false found := false
for _, diagnostic_id in diagnostics.items { for _, diagnostic_index in diagnostics.items {
message := source.format(&diagnostics, diagnostic_id) message := source.format(&diagnostics, source.diagnostic_id(diagnostic_index))
if strings.contains(message, "/b.bro:2:9:") && if strings.contains(message, "/b.bro:2:9:") &&
strings.contains(message, "unknown package alias 'math'") { strings.contains(message, "unknown package alias 'math'") {
found = true found = true
@@ -788,7 +834,7 @@ main :: func() void {
testing.expect(t, done_id >= 0) testing.expect(t, done_id >= 0)
testing.expect(t, main_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]].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] 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[0]].kind, hir.Stmt_Kind.Expression)
testing.expect_value(t, hir_module.statements[main.body[1]].kind, hir.Stmt_Kind.Sink) 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)) 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) @(test)
malformed_ir_emits_traps_and_typed_sentinels :: proc(t: ^testing.T) { malformed_ir_emits_traps_and_typed_sentinels :: proc(t: ^testing.T) {
module := ir.init_module() module := ir.init_module()
defer ir.destroy_module(&module) defer ir.destroy_module(&module)
instructions := make([]ir.Instruction, 3) instructions := make([]ir.Instruction, 3)
instructions[0] = ir.Instruction{op=.Store, type=types.I8, 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=-1, b=-1, diagnostic=-1} 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=1, b=-1, diagnostic=-1} 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{ append(&module.functions, ir.Function{
link_name=strings.clone("main"), link_name=strings.clone("main"),
calling_convention=.C, calling_convention=.C,
@@ -1335,14 +1426,14 @@ deep_global_cycle_detection_uses_iterative_dfs :: proc(t: ^testing.T) {
for id in 0..<count { for id in 0..<count {
append(&ast_module.globals, ast.Global{name=name}) append(&ast_module.globals, ast.Global{name=name})
dependencies: [dynamic]int dependencies: [dynamic]hir.Global_Id
dependencies.allocator = context.allocator 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}) append(&state.module.globals, hir.Global{name=name, dependencies=dependencies})
} }
states := make([]u8, count) states := make([]u8, count)
defer delete(states) 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) testing.expect_value(t, len(diagnostics.items), 1)
for global in state.module.globals { for global in state.module.globals {