compact compiler ids and spans to reduce memory usage
This commit is contained in:
@@ -4,7 +4,6 @@
|
||||
|
||||
# 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
|
||||
- support unary minus, including the signed i64 minimum literal boundary
|
||||
- move ignored example binaries into a dedicated build directory and remove `.review_tmp`
|
||||
|
||||
@@ -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
|
||||
reported 13,222,443 peak bytes and 40,117 allocations. The reusable traversal
|
||||
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.
|
||||
|
||||
@@ -3,6 +3,7 @@ package main
|
||||
import "../../compiler/ast"
|
||||
import "../../compiler/checker"
|
||||
import "../../compiler/hir"
|
||||
import "../../compiler/ir"
|
||||
import "../../compiler/lexer"
|
||||
import "../../compiler/parser"
|
||||
import "../../compiler/source"
|
||||
@@ -72,6 +73,10 @@ main :: proc() {
|
||||
fmt.printf("allocation_count: %d\n", tracker.total_allocation_count)
|
||||
fmt.printf("token_count: %d\n", metrics.token_count)
|
||||
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("stored_symbol_bytes: %d\n", metrics.stored_symbol_bytes)
|
||||
fmt.printf("diagnostic_count: %d\n", metrics.diagnostic_count)
|
||||
|
||||
+80
-26
@@ -4,9 +4,63 @@ import "../source"
|
||||
import "../symbol"
|
||||
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,
|
||||
Int,
|
||||
I8,
|
||||
@@ -16,7 +70,7 @@ Type_Syntax :: enum {
|
||||
Void,
|
||||
}
|
||||
|
||||
Expr_Kind :: enum {
|
||||
Expr_Kind :: enum u8 {
|
||||
Invalid,
|
||||
Integer,
|
||||
Name,
|
||||
@@ -25,15 +79,15 @@ Expr_Kind :: enum {
|
||||
}
|
||||
|
||||
Expr :: struct {
|
||||
kind: Expr_Kind,
|
||||
span: source.Span,
|
||||
integer: i64,
|
||||
args: []Expr_Id,
|
||||
qualifier: symbol.Id,
|
||||
name: symbol.Id,
|
||||
integer: i64,
|
||||
left: int,
|
||||
right: int,
|
||||
args: []int,
|
||||
diagnostic: int,
|
||||
left: Expr_Id,
|
||||
right: Expr_Id,
|
||||
diagnostic: source.Diagnostic_Id,
|
||||
kind: Expr_Kind,
|
||||
}
|
||||
|
||||
Param :: struct {
|
||||
@@ -42,7 +96,7 @@ Param :: struct {
|
||||
type: Type_Syntax,
|
||||
}
|
||||
|
||||
Stmt_Kind :: enum {
|
||||
Stmt_Kind :: enum u8 {
|
||||
Invalid,
|
||||
Declaration,
|
||||
Assignment,
|
||||
@@ -56,49 +110,49 @@ Stmt :: struct {
|
||||
name: symbol.Id,
|
||||
type: Type_Syntax,
|
||||
immutable: bool,
|
||||
expr: int,
|
||||
diagnostic: int,
|
||||
expr: Expr_Id,
|
||||
diagnostic: source.Diagnostic_Id,
|
||||
}
|
||||
|
||||
Function :: struct {
|
||||
span: source.Span,
|
||||
name: symbol.Id,
|
||||
pkg: int,
|
||||
file: int,
|
||||
pkg: Package_Id,
|
||||
file: File_Id,
|
||||
c_abi: bool,
|
||||
has_body: bool,
|
||||
params: []Param,
|
||||
result: Type_Syntax,
|
||||
body: []int,
|
||||
diagnostic: int,
|
||||
body: []Stmt_Id,
|
||||
diagnostic: source.Diagnostic_Id,
|
||||
}
|
||||
|
||||
Global :: struct {
|
||||
span: source.Span,
|
||||
name: symbol.Id,
|
||||
pkg: int,
|
||||
file: int,
|
||||
pkg: Package_Id,
|
||||
file: File_Id,
|
||||
type: Type_Syntax,
|
||||
immutable: bool,
|
||||
expr: int,
|
||||
diagnostic: int,
|
||||
expr: Expr_Id,
|
||||
diagnostic: source.Diagnostic_Id,
|
||||
}
|
||||
|
||||
Import :: struct {
|
||||
span: source.Span,
|
||||
alias: symbol.Id,
|
||||
path: string,
|
||||
pkg: int,
|
||||
file: int,
|
||||
target: int,
|
||||
pkg: Package_Id,
|
||||
file: File_Id,
|
||||
target: Package_Id,
|
||||
valid: bool,
|
||||
used: bool,
|
||||
diagnostic: int,
|
||||
diagnostic: source.Diagnostic_Id,
|
||||
}
|
||||
|
||||
File :: struct {
|
||||
source: int,
|
||||
pkg: int,
|
||||
source: source.Source_Id,
|
||||
pkg: Package_Id,
|
||||
}
|
||||
|
||||
Package :: struct {
|
||||
|
||||
+354
-268
File diff suppressed because it is too large
Load Diff
@@ -56,6 +56,7 @@ compile_package :: proc(input_path, output_path: string, link_arguments: []linke
|
||||
vmem.arena_allocator(&parser_arena),
|
||||
)
|
||||
if !loaded {
|
||||
source.print_all(&diagnostics)
|
||||
fmt.eprintln("failed to load root package directory:", input_path)
|
||||
return 2
|
||||
}
|
||||
|
||||
+73
-24
@@ -5,24 +5,73 @@ import "../symbol"
|
||||
import "../types"
|
||||
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,
|
||||
C,
|
||||
}
|
||||
|
||||
Implementation :: enum {
|
||||
Implementation :: enum u8 {
|
||||
Definition,
|
||||
Declaration,
|
||||
}
|
||||
|
||||
Linkage :: enum {
|
||||
Linkage :: enum u8 {
|
||||
Internal,
|
||||
External,
|
||||
}
|
||||
|
||||
Expr_Kind :: enum {
|
||||
Expr_Kind :: enum u8 {
|
||||
Invalid,
|
||||
Integer,
|
||||
Local,
|
||||
@@ -33,15 +82,15 @@ Expr_Kind :: enum {
|
||||
}
|
||||
|
||||
Expr :: struct {
|
||||
kind: Expr_Kind,
|
||||
span: source.Span,
|
||||
type: types.Type,
|
||||
integer: i64,
|
||||
target: int,
|
||||
left: int,
|
||||
right: int,
|
||||
args: []int,
|
||||
diagnostic: int,
|
||||
args: []Expr_Id,
|
||||
target: Ref,
|
||||
left: Expr_Id,
|
||||
right: Expr_Id,
|
||||
diagnostic: source.Diagnostic_Id,
|
||||
kind: Expr_Kind,
|
||||
}
|
||||
|
||||
Local :: struct {
|
||||
@@ -51,7 +100,7 @@ Local :: struct {
|
||||
parameter: bool,
|
||||
}
|
||||
|
||||
Stmt_Kind :: enum {
|
||||
Stmt_Kind :: enum u8 {
|
||||
Declaration,
|
||||
Assignment,
|
||||
Return,
|
||||
@@ -63,9 +112,9 @@ Stmt_Kind :: enum {
|
||||
Stmt :: struct {
|
||||
kind: Stmt_Kind,
|
||||
span: source.Span,
|
||||
local: int,
|
||||
expr: int,
|
||||
diagnostic: int,
|
||||
local: Local_Id,
|
||||
expr: Expr_Id,
|
||||
diagnostic: source.Diagnostic_Id,
|
||||
}
|
||||
|
||||
Function :: struct {
|
||||
@@ -75,27 +124,27 @@ Function :: struct {
|
||||
implementation: Implementation,
|
||||
linkage: Linkage,
|
||||
is_main: bool,
|
||||
params: []int,
|
||||
params: []Local_Id,
|
||||
result: types.Type,
|
||||
locals: []Local,
|
||||
body: []int,
|
||||
direct_global_reads: [dynamic]int,
|
||||
calls: []int,
|
||||
body: []Stmt_Id,
|
||||
direct_global_reads: [dynamic]Global_Id,
|
||||
calls: []Function_Id,
|
||||
problematic: bool,
|
||||
diagnostic: int,
|
||||
diagnostic: source.Diagnostic_Id,
|
||||
}
|
||||
|
||||
Global :: struct {
|
||||
name: symbol.Id,
|
||||
type: types.Type,
|
||||
expr: int,
|
||||
expr: Expr_Id,
|
||||
static_value: i64,
|
||||
is_static: bool,
|
||||
dependencies: [dynamic]int,
|
||||
calls: []int,
|
||||
dependencies: [dynamic]Global_Id,
|
||||
calls: []Function_Id,
|
||||
direct_problem: bool,
|
||||
problematic: bool,
|
||||
diagnostic: int,
|
||||
diagnostic: source.Diagnostic_Id,
|
||||
}
|
||||
|
||||
Module :: struct {
|
||||
|
||||
+54
-12
@@ -5,24 +5,66 @@ import "../symbol"
|
||||
import "../types"
|
||||
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,
|
||||
C,
|
||||
}
|
||||
|
||||
Implementation :: enum {
|
||||
Implementation :: enum u8 {
|
||||
Definition,
|
||||
Declaration,
|
||||
}
|
||||
|
||||
Linkage :: enum {
|
||||
Linkage :: enum u8 {
|
||||
Internal,
|
||||
External,
|
||||
}
|
||||
|
||||
Opcode :: enum {
|
||||
Opcode :: enum u8 {
|
||||
Param,
|
||||
Const,
|
||||
Load_Global,
|
||||
@@ -38,15 +80,15 @@ Opcode :: enum {
|
||||
}
|
||||
|
||||
Instruction :: struct {
|
||||
op: Opcode,
|
||||
span: source.Span,
|
||||
type: types.Type,
|
||||
integer: i64,
|
||||
target: int,
|
||||
a: int,
|
||||
b: int,
|
||||
args: []int,
|
||||
diagnostic: int,
|
||||
args: []Instruction_Id,
|
||||
target: Ref,
|
||||
a: Instruction_Id,
|
||||
b: Instruction_Id,
|
||||
diagnostic: source.Diagnostic_Id,
|
||||
op: Opcode,
|
||||
}
|
||||
|
||||
Function :: struct {
|
||||
@@ -68,7 +110,7 @@ Global :: struct {
|
||||
static_value: i64,
|
||||
initializer: []Instruction,
|
||||
problematic: bool,
|
||||
diagnostic: int,
|
||||
diagnostic: source.Diagnostic_Id,
|
||||
}
|
||||
|
||||
Module :: struct {
|
||||
|
||||
@@ -34,11 +34,11 @@ append_token :: proc(
|
||||
kind: token.Kind,
|
||||
start, end: int,
|
||||
id := symbol.INVALID,
|
||||
diagnostic := -1,
|
||||
diagnostic := source.INVALID_DIAGNOSTIC,
|
||||
) {
|
||||
append(&stream.items, token.Token{
|
||||
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,
|
||||
diagnostic=diagnostic,
|
||||
})
|
||||
@@ -74,7 +74,7 @@ lex :: proc(
|
||||
cursor += 1
|
||||
append_token(&stream, source_file, .Colon_Colon, start, cursor)
|
||||
} 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)
|
||||
}
|
||||
case '=':
|
||||
@@ -111,7 +111,7 @@ lex :: proc(
|
||||
if cursor >= len(bytes) || (bytes[cursor] != '\\' && bytes[cursor] != '"') {
|
||||
source.add(
|
||||
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",
|
||||
)
|
||||
valid = false
|
||||
@@ -127,7 +127,7 @@ lex :: proc(
|
||||
} else {
|
||||
id := source.add(
|
||||
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",
|
||||
)
|
||||
append_token(&stream, source_file, .Invalid, start, cursor, diagnostic=id)
|
||||
@@ -135,7 +135,7 @@ lex :: proc(
|
||||
case ';':
|
||||
id := source.add(
|
||||
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",
|
||||
)
|
||||
append_token(&stream, source_file, .Invalid, cursor, cursor+1, diagnostic=id)
|
||||
@@ -162,7 +162,7 @@ lex :: proc(
|
||||
} else {
|
||||
id := source.addf(
|
||||
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",
|
||||
value,
|
||||
)
|
||||
|
||||
+46
-43
@@ -49,11 +49,11 @@ sentinel :: proc(value_type: types.Type) -> i64 {
|
||||
}
|
||||
}
|
||||
|
||||
valid_instruction :: proc(instructions: []ir.Instruction, instruction_id: int) -> bool {
|
||||
return instruction_id >= 0 && instruction_id < len(instructions)
|
||||
valid_instruction :: proc(instructions: []ir.Instruction, instruction_id: ir.Instruction_Id) -> bool {
|
||||
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) ||
|
||||
!types.is_concrete_integer(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
|
||||
}
|
||||
|
||||
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) {
|
||||
fmt.sbprintf(builder, "%d", sentinel(expected))
|
||||
return
|
||||
@@ -88,8 +88,8 @@ register_message :: proc(emitter: ^Emitter, text: string) -> int {
|
||||
return id
|
||||
}
|
||||
|
||||
diagnostic_message :: proc(emitter: ^Emitter, diagnostic: int, span: source.Span, fallback: string) -> int {
|
||||
if diagnostic >= 0 && diagnostic < len(emitter.diagnostics.items) {
|
||||
diagnostic_message :: proc(emitter: ^Emitter, diagnostic: source.Diagnostic_Id, span: source.Span, fallback: string) -> int {
|
||||
if _, ok := source.diagnostic_index(diagnostic, len(emitter.diagnostics.items)); ok {
|
||||
message := source.format(emitter.diagnostics, diagnostic, emitter.allocator)
|
||||
id := register_message(emitter, message)
|
||||
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 {
|
||||
if index > 0 {
|
||||
strings.write_string(builder, ", ")
|
||||
@@ -152,60 +152,62 @@ emit_instruction_stream :: proc(
|
||||
instructions: []ir.Instruction,
|
||||
function: ir.Function,
|
||||
global_initializer := false,
|
||||
) -> int {
|
||||
return_value := -1
|
||||
) -> ir.Instruction_Id {
|
||||
return_value := ir.INVALID_INSTRUCTION
|
||||
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 {
|
||||
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
|
||||
}
|
||||
switch instruction.op {
|
||||
case .Param, .Const:
|
||||
case .Load_Global:
|
||||
if instruction.target < 0 || instruction.target >= len(emitter.module.globals) {
|
||||
emit_recovery_value(emitter, instruction_id, instruction, "invalid global reference")
|
||||
global_id := ir.as_global(instruction.target)
|
||||
if global_id == ir.INVALID_GLOBAL || int(global_id) >= len(emitter.module.globals) {
|
||||
emit_recovery_value(emitter, instruction_index, instruction, "invalid global reference")
|
||||
continue
|
||||
}
|
||||
global := emitter.module.globals[instruction.target]
|
||||
global := emitter.module.globals[global_id]
|
||||
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
|
||||
}
|
||||
if global.is_static {
|
||||
fmt.sbprintf(
|
||||
&emitter.builder,
|
||||
" %%v%d = load %s, ptr @bro.g.%d\n",
|
||||
instruction_id,
|
||||
instruction_index,
|
||||
llvm_type(global.type),
|
||||
instruction.target,
|
||||
global_id,
|
||||
)
|
||||
} else {
|
||||
fmt.sbprintf(
|
||||
&emitter.builder,
|
||||
" %%v%d = call %s @bro.get.%d()\n",
|
||||
instruction_id,
|
||||
instruction_index,
|
||||
llvm_type(global.type),
|
||||
instruction.target,
|
||||
global_id,
|
||||
)
|
||||
}
|
||||
case .Alloca:
|
||||
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
|
||||
}
|
||||
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:
|
||||
if !valid_instruction(instructions, instruction.a) ||
|
||||
instructions[instruction.a].op != .Alloca ||
|
||||
!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
|
||||
}
|
||||
fmt.sbprintf(
|
||||
&emitter.builder,
|
||||
" %%v%d = load %s, ptr %%v%d\n",
|
||||
instruction_id,
|
||||
instruction_index,
|
||||
llvm_type(instruction.type),
|
||||
instruction.a,
|
||||
)
|
||||
@@ -214,7 +216,7 @@ emit_instruction_stream :: proc(
|
||||
instructions[instruction.a].op != .Alloca ||
|
||||
!types.equal(instructions[instruction.a].type, 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
|
||||
}
|
||||
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(instruction.type) ||
|
||||
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
|
||||
}
|
||||
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)
|
||||
fmt.sbprintf(&emitter.builder, " to %s\n", llvm_type(instruction.type))
|
||||
case .Add_Checked:
|
||||
if !valid_value(instructions, instruction.a, 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
|
||||
}
|
||||
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, "{ ")
|
||||
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)
|
||||
fmt.sbprintf(&emitter.builder, ", %s ", type_name)
|
||||
write_operand(&emitter.builder, instructions, instruction.b, instruction.type)
|
||||
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, "{ ")
|
||||
fmt.sbprintf(&emitter.builder, "%s, i1 } %%pair%d, 0\n", type_name, instruction_id)
|
||||
fmt.sbprintf(&emitter.builder, " %%overflow%d = extractvalue ", instruction_id)
|
||||
fmt.sbprintf(&emitter.builder, "%s, i1 } %%pair%d, 0\n", type_name, instruction_index)
|
||||
fmt.sbprintf(&emitter.builder, " %%overflow%d = extractvalue ", instruction_index)
|
||||
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(
|
||||
&emitter.builder,
|
||||
" br i1 %%overflow%d, label %%overflow_trap%d, label %%overflow_continue%d\n",
|
||||
instruction_id,
|
||||
instruction_id,
|
||||
instruction_id,
|
||||
instruction_index,
|
||||
instruction_index,
|
||||
instruction_index,
|
||||
)
|
||||
fmt.sbprintf(&emitter.builder, "overflow_trap%d:\n", instruction_id)
|
||||
message := diagnostic_message(emitter, -1, instruction.span, "signed integer addition overflow")
|
||||
fmt.sbprintf(&emitter.builder, "overflow_trap%d:\n", instruction_index)
|
||||
message := diagnostic_message(emitter, source.INVALID_DIAGNOSTIC, instruction.span, "signed integer addition overflow")
|
||||
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:
|
||||
if instruction.target < 0 || instruction.target >= len(emitter.module.functions) {
|
||||
emit_recovery_value(emitter, instruction_id, instruction, "invalid function specialization")
|
||||
function_id := ir.as_function(instruction.target)
|
||||
if function_id == ir.INVALID_FUNCTION || int(function_id) >= len(emitter.module.functions) {
|
||||
emit_recovery_value(emitter, instruction_index, instruction, "invalid function specialization")
|
||||
continue
|
||||
}
|
||||
target := emitter.module.functions[instruction.target]
|
||||
target := emitter.module.functions[function_id]
|
||||
valid_args := len(instruction.args) == len(target.param_types)
|
||||
if valid_args {
|
||||
for arg, index in instruction.args {
|
||||
@@ -283,11 +286,11 @@ emit_instruction_stream :: proc(
|
||||
target_result = types.I32
|
||||
}
|
||||
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
|
||||
}
|
||||
if instruction.type.kind != .Void {
|
||||
fmt.sbprintf(&emitter.builder, " %%v%d = ", instruction_id)
|
||||
fmt.sbprintf(&emitter.builder, " %%v%d = ", instruction_index)
|
||||
} else {
|
||||
strings.write_string(&emitter.builder, " ")
|
||||
}
|
||||
|
||||
+27
-16
@@ -39,20 +39,20 @@ is_identifier :: proc(value: string) -> bool {
|
||||
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 {
|
||||
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 {
|
||||
if existing := find_package(state, path); existing >= 0 {
|
||||
add_placeholder :: proc(state: ^State, path: string) -> ast.Package_Id {
|
||||
if existing := find_package(state, path); existing != ast.INVALID_PACKAGE {
|
||||
return existing
|
||||
}
|
||||
id := len(state.module.packages)
|
||||
id := ast.package_id(len(state.module.packages))
|
||||
append(&state.module.packages, ast.Package{
|
||||
path=strings.clone(path, state.allocator),
|
||||
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
|
||||
}
|
||||
|
||||
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)
|
||||
if !ok || !os.is_dir(path) {
|
||||
if is_root {
|
||||
@@ -111,7 +111,7 @@ load_package :: proc(state: ^State, path: string, import_span: source.Span, is_r
|
||||
if len(canonical) > 0 {
|
||||
delete(canonical, state.allocator)
|
||||
}
|
||||
return -1
|
||||
return ast.INVALID_PACKAGE
|
||||
}
|
||||
placeholder := path
|
||||
if len(canonical) > 0 {
|
||||
@@ -124,12 +124,12 @@ load_package :: proc(state: ^State, path: string, import_span: source.Span, is_r
|
||||
}
|
||||
return id
|
||||
}
|
||||
if existing := find_package(state, canonical); existing >= 0 {
|
||||
if existing := find_package(state, canonical); existing != ast.INVALID_PACKAGE {
|
||||
delete(canonical, state.allocator)
|
||||
return existing
|
||||
}
|
||||
|
||||
pkg_id := len(state.module.packages)
|
||||
pkg_id := ast.package_id(len(state.module.packages))
|
||||
append(&state.module.packages, ast.Package{
|
||||
path=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 {
|
||||
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)
|
||||
if !read_ok {
|
||||
state.root_failed = true
|
||||
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)
|
||||
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})
|
||||
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)
|
||||
@@ -169,7 +180,7 @@ load_package :: proc(state: ^State, path: string, import_span: source.Span, is_r
|
||||
import_count := len(state.module.imports)
|
||||
for import_id in 0..<import_count {
|
||||
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
|
||||
}
|
||||
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 := load_package(state, target_path, import_item.span)
|
||||
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
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
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 {
|
||||
if function.pkg == pkg && function.name == name {
|
||||
return true
|
||||
@@ -205,7 +216,7 @@ declaration_conflicts :: proc(module: ^ast.Module, pkg: int, name: symbol.Id) ->
|
||||
|
||||
validate_imports :: proc(state: ^State) {
|
||||
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
|
||||
}
|
||||
alias := state.module.imports[import_id].alias
|
||||
@@ -260,7 +271,7 @@ load :: proc(
|
||||
allocator=allocator,
|
||||
}
|
||||
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
|
||||
}
|
||||
validate_imports(&state)
|
||||
|
||||
+92
-82
@@ -10,20 +10,20 @@ import "core:mem"
|
||||
State :: struct {
|
||||
hir_module: ^hir.Module,
|
||||
instructions: [dynamic]ir.Instruction,
|
||||
local_values: []int,
|
||||
local_slots: []int,
|
||||
local_values: []ir.Instruction_Id,
|
||||
local_slots: []ir.Instruction_Id,
|
||||
expr_stack: [dynamic]Lower_Expr_Frame,
|
||||
allocator: mem.Allocator,
|
||||
}
|
||||
|
||||
append_instruction :: proc(state: ^State, instruction: ir.Instruction) -> int {
|
||||
id := len(state.instructions)
|
||||
append_instruction :: proc(state: ^State, instruction: ir.Instruction) -> ir.Instruction_Id {
|
||||
id := ir.instruction_id(len(state.instructions))
|
||||
append(&state.instructions, instruction)
|
||||
return id
|
||||
}
|
||||
|
||||
clone_args :: proc(values: []int, allocator: mem.Allocator) -> []int {
|
||||
result := make([]int, len(values), allocator)
|
||||
clone_args :: proc(values: []ir.Instruction_Id, allocator: mem.Allocator) -> []ir.Instruction_Id {
|
||||
result := make([]ir.Instruction_Id, len(values), allocator)
|
||||
copy(result, values)
|
||||
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{
|
||||
op=.Trap,
|
||||
span=span,
|
||||
type=types.VOID,
|
||||
target=-1,
|
||||
a=-1,
|
||||
b=-1,
|
||||
target=ir.INVALID_REF,
|
||||
a=ir.INVALID_INSTRUCTION,
|
||||
b=ir.INVALID_INSTRUCTION,
|
||||
diagnostic=diagnostic,
|
||||
})
|
||||
fallback := value_type
|
||||
@@ -56,22 +61,22 @@ append_recovery_value :: proc(state: ^State, span: source.Span, value_type: type
|
||||
span=span,
|
||||
type=fallback,
|
||||
integer=sentinel(fallback),
|
||||
target=-1,
|
||||
a=-1,
|
||||
b=-1,
|
||||
diagnostic=-1,
|
||||
target=ir.INVALID_REF,
|
||||
a=ir.INVALID_INSTRUCTION,
|
||||
b=ir.INVALID_INSTRUCTION,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
}
|
||||
|
||||
Lower_Expr_Frame :: struct {
|
||||
expr: int,
|
||||
expr: hir.Expr_Id,
|
||||
stage: u8,
|
||||
left: int,
|
||||
left: ir.Instruction_Id,
|
||||
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
|
||||
clear_dynamic_array(&stack)
|
||||
defer {
|
||||
@@ -82,11 +87,11 @@ lower_expr :: proc(state: ^State, expr_id: int) -> int {
|
||||
state.expr_stack = stack
|
||||
}
|
||||
append(&stack, Lower_Expr_Frame{expr=expr_id})
|
||||
last := -1
|
||||
last := ir.INVALID_INSTRUCTION
|
||||
for len(stack) > 0 {
|
||||
frame_index := len(stack)-1
|
||||
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)
|
||||
_ = pop(&stack)
|
||||
continue
|
||||
@@ -100,31 +105,33 @@ lower_expr :: proc(state: ^State, expr_id: int) -> int {
|
||||
case .Integer:
|
||||
last = append_instruction(state, ir.Instruction{
|
||||
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)
|
||||
case .Local:
|
||||
last = -1
|
||||
if expr.target >= 0 && expr.target < len(state.local_slots) && state.local_slots[expr.target] >= 0 {
|
||||
last = ir.INVALID_INSTRUCTION
|
||||
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{
|
||||
op=.Load, span=expr.span, type=expr.type, target=-1,
|
||||
a=state.local_slots[expr.target], b=-1, diagnostic=-1,
|
||||
op=.Load, span=expr.span, type=expr.type, target=ir.INVALID_REF,
|
||||
a=state.local_slots[local], b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
} else if expr.target >= 0 && expr.target < len(state.local_values) &&
|
||||
state.local_values[expr.target] >= 0 {
|
||||
last = state.local_values[expr.target]
|
||||
} else if local != hir.INVALID_LOCAL && int(local) < len(state.local_values) &&
|
||||
state.local_values[local] != ir.INVALID_INSTRUCTION {
|
||||
last = state.local_values[local]
|
||||
}
|
||||
if last < 0 {
|
||||
if last == ir.INVALID_INSTRUCTION {
|
||||
last = append_recovery_value(state, expr.span, expr.type, expr.diagnostic)
|
||||
}
|
||||
_ = pop(&stack)
|
||||
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)
|
||||
} else {
|
||||
last = append_instruction(state, ir.Instruction{
|
||||
op=.Load_Global, span=expr.span, type=expr.type, target=expr.target,
|
||||
a=-1, b=-1, diagnostic=-1,
|
||||
op=.Load_Global, span=expr.span, type=expr.type, target=ir.global_ref(ir.Global_Id(global)),
|
||||
a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
}
|
||||
_ = pop(&stack)
|
||||
@@ -135,12 +142,13 @@ lower_expr :: proc(state: ^State, expr_id: int) -> int {
|
||||
stack[frame_index].stage = 2
|
||||
append(&stack, Lower_Expr_Frame{expr=expr.left})
|
||||
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)
|
||||
_ = pop(&stack)
|
||||
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
|
||||
if len(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 {
|
||||
last = append_instruction(state, ir.Instruction{
|
||||
op=.Widen, span=expr.span, type=expr.type, target=-1,
|
||||
a=last, b=-1, diagnostic=-1,
|
||||
op=.Widen, span=expr.span, type=expr.type, target=ir.INVALID_REF,
|
||||
a=last, b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
_ = pop(&stack)
|
||||
continue
|
||||
@@ -164,8 +172,8 @@ lower_expr :: proc(state: ^State, expr_id: int) -> int {
|
||||
}
|
||||
if frame.stage == 3 {
|
||||
last = append_instruction(state, ir.Instruction{
|
||||
op=.Add_Checked, span=expr.span, type=expr.type, target=-1,
|
||||
a=frame.left, b=last, diagnostic=-1,
|
||||
op=.Add_Checked, span=expr.span, type=expr.type, target=ir.INVALID_REF,
|
||||
a=frame.left, b=last, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
_ = pop(&stack)
|
||||
continue
|
||||
@@ -180,8 +188,8 @@ lower_expr :: proc(state: ^State, expr_id: int) -> int {
|
||||
}
|
||||
}
|
||||
last = append_instruction(state, ir.Instruction{
|
||||
op=.Call, span=expr.span, type=expr.type, target=expr.target,
|
||||
a=-1, b=-1, args=stack[frame_index].args, diagnostic=-1,
|
||||
op=.Call, span=expr.span, type=expr.type, target=ir.function_ref(ir.Function_Id(hir.as_function(expr.target))),
|
||||
a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION, args=stack[frame_index].args, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
stack[frame_index].args = nil
|
||||
_ = pop(&stack)
|
||||
@@ -194,8 +202,8 @@ lower_body :: proc(hir_module: ^hir.Module, function: hir.Function, allocator: m
|
||||
state := State{
|
||||
hir_module=hir_module,
|
||||
allocator=allocator,
|
||||
local_values=make([]int, len(function.locals), allocator),
|
||||
local_slots=make([]int, len(function.locals), allocator),
|
||||
local_values=make([]ir.Instruction_Id, len(function.locals), allocator),
|
||||
local_slots=make([]ir.Instruction_Id, len(function.locals), allocator),
|
||||
}
|
||||
state.instructions.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)
|
||||
}
|
||||
for _, index in state.local_values {
|
||||
state.local_values[index] = -1
|
||||
state.local_slots[index] = -1
|
||||
state.local_values[index] = ir.INVALID_INSTRUCTION
|
||||
state.local_slots[index] = ir.INVALID_INSTRUCTION
|
||||
}
|
||||
for local_id in function.params {
|
||||
param := append_instruction(&state, ir.Instruction{
|
||||
op=.Param,
|
||||
type=function.locals[local_id].type,
|
||||
target=local_id,
|
||||
a=-1,
|
||||
b=-1,
|
||||
diagnostic=-1,
|
||||
target=ir.local_ref(ir.Local_Id(local_id)),
|
||||
a=ir.INVALID_INSTRUCTION,
|
||||
b=ir.INVALID_INSTRUCTION,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
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 {
|
||||
case .Declaration:
|
||||
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{
|
||||
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
|
||||
}
|
||||
@@ -238,34 +246,34 @@ lower_body :: proc(hir_module: ^hir.Module, function: hir.Function, allocator: m
|
||||
op=.Alloca,
|
||||
span=statement.span,
|
||||
type=local.type,
|
||||
target=statement.local,
|
||||
a=-1,
|
||||
b=-1,
|
||||
diagnostic=-1,
|
||||
target=ir.local_ref(ir.Local_Id(statement.local)),
|
||||
a=ir.INVALID_INSTRUCTION,
|
||||
b=ir.INVALID_INSTRUCTION,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
state.local_slots[statement.local] = slot
|
||||
append_instruction(&state, ir.Instruction{
|
||||
op=.Store,
|
||||
span=statement.span,
|
||||
type=local.type,
|
||||
target=-1,
|
||||
target=ir.INVALID_REF,
|
||||
a=slot,
|
||||
b=value,
|
||||
diagnostic=-1,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
} else {
|
||||
state.local_values[statement.local] = value
|
||||
}
|
||||
case .Assignment:
|
||||
value := lower_expr(&state, statement.expr)
|
||||
slot := -1
|
||||
if statement.local >= 0 && statement.local < len(state.local_slots) {
|
||||
slot := ir.INVALID_INSTRUCTION
|
||||
if statement.local != hir.INVALID_LOCAL && int(statement.local) < len(state.local_slots) {
|
||||
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{
|
||||
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
|
||||
}
|
||||
@@ -273,21 +281,21 @@ lower_body :: proc(hir_module: ^hir.Module, function: hir.Function, allocator: m
|
||||
op=.Store,
|
||||
span=statement.span,
|
||||
type=function.locals[statement.local].type,
|
||||
target=-1,
|
||||
target=ir.INVALID_REF,
|
||||
a=slot,
|
||||
b=value,
|
||||
diagnostic=-1,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
case .Return:
|
||||
if statement.expr < 0 {
|
||||
if statement.expr == hir.INVALID_EXPR {
|
||||
append_instruction(&state, ir.Instruction{
|
||||
op=.Return_Void,
|
||||
span=statement.span,
|
||||
type=types.VOID,
|
||||
target=-1,
|
||||
a=-1,
|
||||
b=-1,
|
||||
diagnostic=-1,
|
||||
target=ir.INVALID_REF,
|
||||
a=ir.INVALID_INSTRUCTION,
|
||||
b=ir.INVALID_INSTRUCTION,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
} else {
|
||||
value := lower_expr(&state, statement.expr)
|
||||
@@ -295,10 +303,10 @@ lower_body :: proc(hir_module: ^hir.Module, function: hir.Function, allocator: m
|
||||
op=.Return,
|
||||
span=statement.span,
|
||||
type=function.result,
|
||||
target=-1,
|
||||
target=ir.INVALID_REF,
|
||||
a=value,
|
||||
b=-1,
|
||||
diagnostic=-1,
|
||||
b=ir.INVALID_INSTRUCTION,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
}
|
||||
case .Expression, .Sink:
|
||||
@@ -308,9 +316,9 @@ lower_body :: proc(hir_module: ^hir.Module, function: hir.Function, allocator: m
|
||||
op=.Trap,
|
||||
span=statement.span,
|
||||
type=types.VOID,
|
||||
target=-1,
|
||||
a=-1,
|
||||
b=-1,
|
||||
target=ir.INVALID_REF,
|
||||
a=ir.INVALID_INSTRUCTION,
|
||||
b=ir.INVALID_INSTRUCTION,
|
||||
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_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 {
|
||||
value := append_instruction(&state, ir.Instruction{
|
||||
op=.Const,
|
||||
type=function.result,
|
||||
integer=sentinel(function.result),
|
||||
target=-1,
|
||||
a=-1,
|
||||
b=-1,
|
||||
diagnostic=-1,
|
||||
target=ir.INVALID_REF,
|
||||
a=ir.INVALID_INSTRUCTION,
|
||||
b=ir.INVALID_INSTRUCTION,
|
||||
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[:]
|
||||
@@ -345,10 +353,10 @@ lower_global_initializer :: proc(hir_module: ^hir.Module, global: hir.Global, al
|
||||
append_instruction(&state, ir.Instruction{
|
||||
op=.Return,
|
||||
type=global.type,
|
||||
target=-1,
|
||||
target=ir.INVALID_REF,
|
||||
a=value,
|
||||
b=-1,
|
||||
diagnostic=-1,
|
||||
b=ir.INVALID_INSTRUCTION,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
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 {
|
||||
module := ir.init_module(allocator)
|
||||
for global in hir_module.globals {
|
||||
_ = ir.global_id(len(module.globals))
|
||||
append(&module.globals, ir.Global{
|
||||
name=global.name,
|
||||
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 {
|
||||
param_types[index] = function.locals[local_id].type
|
||||
}
|
||||
_ = ir.function_id(len(module.functions))
|
||||
append(&module.functions, ir.Function{
|
||||
link_name=fmt.aprintf("%s", function.link_name, allocator=allocator),
|
||||
calling_convention=.C if function.calling_convention == .C else .Brolang,
|
||||
|
||||
+58
-52
@@ -13,8 +13,8 @@ Parser :: struct {
|
||||
source_file: ^source.Source,
|
||||
diagnostics: ^source.Diagnostics,
|
||||
module: ast.Module,
|
||||
pkg: int,
|
||||
file: int,
|
||||
pkg: ast.Package_Id,
|
||||
file: ast.File_Id,
|
||||
cursor: int,
|
||||
delimiter_depth: int,
|
||||
}
|
||||
@@ -22,10 +22,10 @@ Parser :: struct {
|
||||
MAX_EXPRESSION_NESTING :: 256
|
||||
|
||||
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 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 {
|
||||
@@ -61,19 +61,19 @@ skip_newlines :: proc(parser: ^Parser) {
|
||||
}
|
||||
}
|
||||
|
||||
add_expr :: proc(parser: ^Parser, expr: ast.Expr) -> int {
|
||||
id := len(parser.module.exprs)
|
||||
add_expr :: proc(parser: ^Parser, expr: ast.Expr) -> ast.Expr_Id {
|
||||
id := ast.expr_id(len(parser.module.exprs))
|
||||
append(&parser.module.exprs, expr)
|
||||
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)
|
||||
return add_expr(parser, ast.Expr{
|
||||
kind=.Invalid,
|
||||
span=span,
|
||||
left=ast.INVALID_ID,
|
||||
right=ast.INVALID_ID,
|
||||
left=ast.INVALID_EXPR,
|
||||
right=ast.INVALID_EXPR,
|
||||
diagnostic=id,
|
||||
})
|
||||
}
|
||||
@@ -131,7 +131,7 @@ skip_parenthesized :: proc(parser: ^Parser) -> source.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 {
|
||||
span := skip_parenthesized(parser)
|
||||
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)
|
||||
parser.delimiter_depth += 1
|
||||
defer parser.delimiter_depth -= 1
|
||||
args: [dynamic]int
|
||||
args: [dynamic]ast.Expr_Id
|
||||
args.allocator = parser.module.allocator
|
||||
skip_newlines(parser)
|
||||
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,
|
||||
name=name.symbol,
|
||||
args=args[:],
|
||||
left=ast.INVALID_ID,
|
||||
right=ast.INVALID_ID,
|
||||
diagnostic=-1,
|
||||
left=ast.INVALID_EXPR,
|
||||
right=ast.INVALID_EXPR,
|
||||
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)
|
||||
#partial switch tok.kind {
|
||||
case .Integer:
|
||||
@@ -181,9 +181,9 @@ parse_primary :: proc(parser: ^Parser, nesting: int) -> int {
|
||||
kind=.Integer,
|
||||
span=tok.span,
|
||||
integer=value,
|
||||
left=ast.INVALID_ID,
|
||||
right=ast.INVALID_ID,
|
||||
diagnostic=-1,
|
||||
left=ast.INVALID_EXPR,
|
||||
right=ast.INVALID_EXPR,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
case .Identifier:
|
||||
first := advance(parser)
|
||||
@@ -204,9 +204,9 @@ parse_primary :: proc(parser: ^Parser, nesting: int) -> int {
|
||||
span=span_from(first.span, name.span),
|
||||
qualifier=qualifier,
|
||||
name=name.symbol,
|
||||
left=ast.INVALID_ID,
|
||||
right=ast.INVALID_ID,
|
||||
diagnostic=-1,
|
||||
left=ast.INVALID_EXPR,
|
||||
right=ast.INVALID_EXPR,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
case .Underscore:
|
||||
advance(parser)
|
||||
@@ -231,8 +231,8 @@ parse_primary :: proc(parser: ^Parser, nesting: int) -> int {
|
||||
return add_expr(parser, ast.Expr{
|
||||
kind=.Invalid,
|
||||
span=tok.span,
|
||||
left=ast.INVALID_ID,
|
||||
right=ast.INVALID_ID,
|
||||
left=ast.INVALID_EXPR,
|
||||
right=ast.INVALID_EXPR,
|
||||
diagnostic=tok.diagnostic,
|
||||
})
|
||||
}
|
||||
@@ -250,7 +250,7 @@ infix_binding_power :: proc(kind: token.Kind) -> (left, right: int, ok: bool) {
|
||||
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 {
|
||||
tok := current(parser)
|
||||
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),
|
||||
left=left,
|
||||
right=right,
|
||||
diagnostic=-1,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
if parser.delimiter_depth > 0 {
|
||||
skip_newlines(parser)
|
||||
@@ -286,17 +286,17 @@ parse_expression_bp :: proc(parser: ^Parser, minimum_binding_power, nesting: int
|
||||
return left
|
||||
}
|
||||
|
||||
parse_expression :: proc(parser: ^Parser) -> int {
|
||||
parse_expression :: proc(parser: ^Parser) -> ast.Expr_Id {
|
||||
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 {
|
||||
skip_newlines(parser)
|
||||
return -1
|
||||
return source.INVALID_DIAGNOSTIC
|
||||
}
|
||||
if current(parser).kind == .Eof || allow_closing_brace && current(parser).kind == .Right_Brace {
|
||||
return -1
|
||||
return source.INVALID_DIAGNOSTIC
|
||||
}
|
||||
diagnostic := source.add(
|
||||
parser.diagnostics,
|
||||
@@ -312,33 +312,33 @@ finish_statement :: proc(parser: ^Parser, allow_closing_brace := false) -> int {
|
||||
return diagnostic
|
||||
}
|
||||
|
||||
parse_return :: proc(parser: ^Parser) -> int {
|
||||
parse_return :: proc(parser: ^Parser) -> ast.Stmt_Id {
|
||||
start := advance(parser)
|
||||
skip_newlines(parser)
|
||||
if current(parser).kind == .Underscore {
|
||||
end := advance(parser)
|
||||
id := len(parser.module.statements)
|
||||
id := ast.stmt_id(len(parser.module.statements))
|
||||
append(&parser.module.statements, ast.Stmt{
|
||||
kind=.Return,
|
||||
span=span_from(start.span, end.span),
|
||||
name=end.symbol,
|
||||
expr=ast.INVALID_ID,
|
||||
diagnostic=-1,
|
||||
expr=ast.INVALID_EXPR,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
return id
|
||||
}
|
||||
expr := parse_expression(parser)
|
||||
id := len(parser.module.statements)
|
||||
id := ast.stmt_id(len(parser.module.statements))
|
||||
append(&parser.module.statements, ast.Stmt{
|
||||
kind=.Return,
|
||||
span=span_from(start.span, parser.module.exprs[expr].span),
|
||||
expr=expr,
|
||||
diagnostic=-1,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
return id
|
||||
}
|
||||
|
||||
parse_statement :: proc(parser: ^Parser) -> int {
|
||||
parse_statement :: proc(parser: ^Parser) -> ast.Stmt_Id {
|
||||
if current(parser).kind == .Keyword_Return {
|
||||
return parse_return(parser)
|
||||
}
|
||||
@@ -363,7 +363,7 @@ parse_statement :: proc(parser: ^Parser) -> int {
|
||||
kind = .Declaration
|
||||
immutable = operator.kind == .Colon_Colon
|
||||
}
|
||||
id := len(parser.module.statements)
|
||||
id := ast.stmt_id(len(parser.module.statements))
|
||||
append(&parser.module.statements, ast.Stmt{
|
||||
kind=kind,
|
||||
span=span_from(name.span, parser.module.exprs[expr].span),
|
||||
@@ -371,7 +371,7 @@ parse_statement :: proc(parser: ^Parser) -> int {
|
||||
type=type_syntax,
|
||||
immutable=immutable,
|
||||
expr=expr,
|
||||
diagnostic=-1,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
return id
|
||||
}
|
||||
@@ -379,12 +379,12 @@ parse_statement :: proc(parser: ^Parser) -> int {
|
||||
}
|
||||
|
||||
expr := parse_expression(parser)
|
||||
id := len(parser.module.statements)
|
||||
id := ast.stmt_id(len(parser.module.statements))
|
||||
append(&parser.module.statements, ast.Stmt{
|
||||
kind=.Expression,
|
||||
span=parser.module.exprs[expr].span,
|
||||
expr=expr,
|
||||
diagnostic=-1,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
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 {
|
||||
_ = finish_statement(parser)
|
||||
}
|
||||
_ = ast.function_id(len(parser.module.functions))
|
||||
append(&parser.module.functions, ast.Function{
|
||||
span=span_from(name.span, end.span),
|
||||
name=name.symbol,
|
||||
@@ -455,23 +456,23 @@ parse_function :: proc(parser: ^Parser, name: token.Token, c_abi: bool) {
|
||||
has_body=false,
|
||||
params=params,
|
||||
result=result,
|
||||
diagnostic=-1,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
return
|
||||
}
|
||||
advance(parser)
|
||||
|
||||
body: [dynamic]int
|
||||
body: [dynamic]ast.Stmt_Id
|
||||
body.allocator = parser.module.allocator
|
||||
skip_newlines(parser)
|
||||
for current(parser).kind != .Right_Brace && current(parser).kind != .Eof {
|
||||
append(&body, parse_statement(parser))
|
||||
if diagnostic := finish_statement(parser, true); diagnostic >= 0 {
|
||||
statement_id := len(parser.module.statements)
|
||||
if diagnostic := finish_statement(parser, true); diagnostic != source.INVALID_DIAGNOSTIC {
|
||||
statement_id := ast.stmt_id(len(parser.module.statements))
|
||||
append(&parser.module.statements, ast.Stmt{
|
||||
kind=.Invalid,
|
||||
span=current(parser).span,
|
||||
expr=ast.INVALID_ID,
|
||||
expr=ast.INVALID_EXPR,
|
||||
diagnostic=diagnostic,
|
||||
})
|
||||
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")
|
||||
end = func_token
|
||||
}
|
||||
_ = ast.function_id(len(parser.module.functions))
|
||||
append(&parser.module.functions, ast.Function{
|
||||
span=span_from(name.span, end.span),
|
||||
name=name.symbol,
|
||||
@@ -492,7 +494,7 @@ parse_function :: proc(parser: ^Parser, name: token.Token, c_abi: bool) {
|
||||
params=params,
|
||||
result=result,
|
||||
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 {
|
||||
advance(parser)
|
||||
}
|
||||
_ = ast.import_id(len(parser.module.imports))
|
||||
append(&parser.module.imports, ast.Import{
|
||||
span=start.span,
|
||||
alias=alias.symbol,
|
||||
pkg=parser.pkg,
|
||||
file=parser.file,
|
||||
target=-1,
|
||||
target=ast.INVALID_PACKAGE,
|
||||
valid=false,
|
||||
diagnostic=-1,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
_ = finish_statement(parser)
|
||||
return
|
||||
}
|
||||
advance(parser)
|
||||
_ = ast.import_id(len(parser.module.imports))
|
||||
append(&parser.module.imports, ast.Import{
|
||||
span=span_from(start.span, path_token.span),
|
||||
alias=alias.symbol,
|
||||
path=decode_import_path(parser, path_token),
|
||||
pkg=parser.pkg,
|
||||
file=parser.file,
|
||||
target=-1,
|
||||
target=ast.INVALID_PACKAGE,
|
||||
valid=true,
|
||||
diagnostic=-1,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
_ = finish_statement(parser)
|
||||
}
|
||||
@@ -606,6 +610,7 @@ parse_top_level :: proc(parser: ^Parser) {
|
||||
}
|
||||
|
||||
expr := parse_expression(parser)
|
||||
_ = ast.global_id(len(parser.module.globals))
|
||||
append(&parser.module.globals, ast.Global{
|
||||
span=span_from(name.span, parser.module.exprs[expr].span),
|
||||
name=name.symbol,
|
||||
@@ -614,7 +619,7 @@ parse_top_level :: proc(parser: ^Parser) {
|
||||
type=type_syntax,
|
||||
immutable=operator.kind == .Colon_Colon,
|
||||
expr=expr,
|
||||
diagnostic=-1,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
_ = finish_statement(parser)
|
||||
}
|
||||
@@ -644,7 +649,8 @@ parse_into :: proc(
|
||||
source_file: ^source.Source,
|
||||
diagnostics: ^source.Diagnostics,
|
||||
module: ^ast.Module,
|
||||
pkg, file: int,
|
||||
pkg: ast.Package_Id,
|
||||
file: ast.File_Id,
|
||||
) {
|
||||
parser := Parser{
|
||||
tokens=stream,
|
||||
|
||||
+63
-27
@@ -3,17 +3,48 @@ package source
|
||||
import "core:fmt"
|
||||
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 {
|
||||
file: int,
|
||||
start: int,
|
||||
end: int,
|
||||
file: Source_Id,
|
||||
start: Offset,
|
||||
end: Offset,
|
||||
}
|
||||
|
||||
Source :: struct {
|
||||
id: int,
|
||||
id: Source_Id,
|
||||
path: string,
|
||||
text: string,
|
||||
line_starts: []int,
|
||||
line_starts: []Offset,
|
||||
}
|
||||
|
||||
Store :: struct {
|
||||
@@ -30,7 +61,7 @@ Diagnostics :: struct {
|
||||
source: ^Source,
|
||||
store: ^Store,
|
||||
items: [dynamic]Diagnostic,
|
||||
index: map[Diagnostic_Key]int,
|
||||
index: map[Diagnostic_Key]Diagnostic_Id,
|
||||
allocator: mem.Allocator,
|
||||
}
|
||||
|
||||
@@ -55,20 +86,22 @@ destroy_store :: proc(store: ^Store) {
|
||||
delete(store.items)
|
||||
}
|
||||
|
||||
make_line_starts :: proc(text: string, allocator: mem.Allocator) -> []int {
|
||||
result: [dynamic]int
|
||||
make_line_starts :: proc(text: string, allocator: mem.Allocator) -> []Offset {
|
||||
assert(fits_source_length(u64(len(text))))
|
||||
result: [dynamic]Offset
|
||||
result.allocator = allocator
|
||||
append(&result, 0)
|
||||
for value, offset in transmute([]byte)text {
|
||||
if value == '\n' {
|
||||
append(&result, offset+1)
|
||||
append(&result, Offset(offset+1))
|
||||
}
|
||||
}
|
||||
return result[:]
|
||||
}
|
||||
|
||||
add_source_owned :: proc(store: ^Store, path: string, text: []byte) -> int {
|
||||
id := len(store.items)
|
||||
add_source_owned :: proc(store: ^Store, path: string, text: []byte) -> Source_Id {
|
||||
assert(fits_source_length(u64(len(text))))
|
||||
id := source_id(len(store.items))
|
||||
owned_text := string(text)
|
||||
append(&store.items, Source{
|
||||
id=id,
|
||||
@@ -79,7 +112,7 @@ add_source_owned :: proc(store: ^Store, path: string, text: []byte) -> int {
|
||||
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)
|
||||
copy(owned, transmute([]byte)text)
|
||||
return add_source_owned(store, path, owned)
|
||||
@@ -111,51 +144,51 @@ destroy_diagnostics :: proc(diagnostics: ^Diagnostics) {
|
||||
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}
|
||||
if id, ok := diagnostics.index[key]; ok {
|
||||
return id
|
||||
}
|
||||
id := len(diagnostics.items)
|
||||
id := diagnostic_id(len(diagnostics.items))
|
||||
cloned := fmt.aprintf("%s", message, allocator=diagnostics.allocator)
|
||||
append(&diagnostics.items, Diagnostic{span=span, message=cloned})
|
||||
diagnostics.index[Diagnostic_Key{span=span, message=cloned}] = 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)
|
||||
key := Diagnostic_Key{span=span, message=message}
|
||||
if id, ok := diagnostics.index[key]; ok {
|
||||
delete(message, diagnostics.allocator)
|
||||
return id
|
||||
}
|
||||
id := len(diagnostics.items)
|
||||
id := diagnostic_id(len(diagnostics.items))
|
||||
append(&diagnostics.items, Diagnostic{span=span, message=message})
|
||||
diagnostics.index[Diagnostic_Key{span=span, message=message}] = 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 {
|
||||
limit := min(max(offset, 0), len(source_file.text))
|
||||
limit := min(int(offset), len(source_file.text))
|
||||
low := 0
|
||||
high := len(source_file.line_starts)
|
||||
for low < high {
|
||||
middle := low + (high-low)/2
|
||||
if source_file.line_starts[middle] <= limit {
|
||||
if int(source_file.line_starts[middle]) <= limit {
|
||||
low = middle+1
|
||||
} else {
|
||||
high = middle
|
||||
}
|
||||
}
|
||||
line = max(low, 1)
|
||||
column = limit-source_file.line_starts[line-1]+1
|
||||
column = limit-int(source_file.line_starts[line-1])+1
|
||||
return
|
||||
}
|
||||
line = 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] {
|
||||
if byte_value == '\n' {
|
||||
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 {
|
||||
if diagnostics.store != nil && span.file >= 0 && span.file < len(diagnostics.store.items) {
|
||||
return &diagnostics.store.items[span.file]
|
||||
if diagnostics.store != nil {
|
||||
if index, ok := source_index(span.file, len(diagnostics.store.items)); ok {
|
||||
return &diagnostics.store.items[index]
|
||||
}
|
||||
}
|
||||
return diagnostics.source
|
||||
}
|
||||
|
||||
format :: proc(diagnostics: ^Diagnostics, id: int, allocator := context.allocator) -> string {
|
||||
if id < 0 || id >= len(diagnostics.items) {
|
||||
format :: proc(diagnostics: ^Diagnostics, id: Diagnostic_Id, allocator := context.allocator) -> string {
|
||||
index, ok := diagnostic_index(id, len(diagnostics.items))
|
||||
if !ok {
|
||||
path := "<unknown>"
|
||||
if diagnostics.source != nil {
|
||||
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)
|
||||
}
|
||||
diagnostic := diagnostics.items[id]
|
||||
diagnostic := diagnostics.items[index]
|
||||
source_file := source_for_span(diagnostics, diagnostic.span)
|
||||
if source_file == nil {
|
||||
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) {
|
||||
for _, id in diagnostics.items {
|
||||
message := format(diagnostics, id)
|
||||
message := format(diagnostics, diagnostic_id(id))
|
||||
fmt.eprintln(message)
|
||||
delete(message)
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package token
|
||||
import "../source"
|
||||
import "../symbol"
|
||||
|
||||
Kind :: enum {
|
||||
Kind :: enum u8 {
|
||||
Invalid,
|
||||
Eof,
|
||||
Newline,
|
||||
@@ -32,10 +32,10 @@ Kind :: enum {
|
||||
}
|
||||
|
||||
Token :: struct {
|
||||
kind: Kind,
|
||||
span: source.Span,
|
||||
symbol: symbol.Id,
|
||||
diagnostic: int,
|
||||
span: source.Span,
|
||||
symbol: symbol.Id,
|
||||
diagnostic: source.Diagnostic_Id,
|
||||
kind: Kind,
|
||||
}
|
||||
|
||||
Stream :: struct {
|
||||
|
||||
+101
-10
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user