Files
brolang/compiler/hir/hir.odin
T
2026-07-19 00:45:51 +02:00

339 lines
7.9 KiB
Odin

package hir
import "../source"
import "../symbol"
import "../target"
import "../types"
import "core:mem"
Expr_Id :: distinct u32
Stmt_Id :: distinct u32
Local_Id :: distinct u32
Function_Id :: distinct u32
Global_Id :: distinct u32
Ref :: distinct u32
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 u8 {
Definition,
Declaration,
}
Linkage :: enum u8 {
Internal,
External,
}
Expr_Kind :: enum u8 {
Invalid,
Void,
Integer,
Float,
String,
Bool,
Array,
Struct,
None,
Optional_Some,
Local,
Global,
Function,
Address,
Deref,
Index,
Slice,
Field,
Union_Tag,
Length,
Slice_Ptr,
Unwrap,
Orelse,
Try,
Catch,
Widen,
Sum_Widen,
C_Coerce,
C_Vararg_Promote,
Retype,
Scalar_Cast,
Pointer_Cast,
Weaken_Pointer,
Weaken_Slice,
Decay_Array_Pointer,
Negate,
Not,
Bit_Not,
Add,
Sub,
Mul,
Div,
Div_Trunc,
Div_Floor,
Div_Exact,
Div_Ceil,
Rem,
Mod,
Pointer_Add,
Bit_And,
Bit_Or,
Bit_Xor,
Shift_Left,
Shift_Right,
Shift_Left_Saturating,
Eq,
Ne,
Lt,
Le,
Gt,
Ge,
And,
Or,
Range,
Call,
}
CATCH_EXPRESSION :: i64(0)
CATCH_BLOCK :: i64(1)
CATCH_VOID_FALLTHROUGH :: i64(2)
Expr :: struct {
span: source.Span,
type: types.Type,
integer: i64,
// Aggregate/call children normally; Try stores errdefer capture local IDs
// encoded as Expr_Id because it otherwise has no args.
args: []Expr_Id,
// `Catch` block handlers use `body` for the handler statements and `target`
// for the optional captured error local. `integer` selects the catch mode;
// a missing `right` means the block exits or completes with void. `Try` uses
// `body` for active error-exit cleanup.
body: []Stmt_Id,
target: Ref,
left: Expr_Id,
right: Expr_Id,
diagnostic: source.Diagnostic_Id,
kind: Expr_Kind,
}
Local :: struct {
name: symbol.Id,
type: types.Type,
mutable: bool,
parameter: bool,
}
Conditional_Unwrap :: struct {
expr: Expr_Id,
local: Local_Id,
}
Stmt_Kind :: enum u8 {
Declaration,
Assignment,
Return,
Expression,
Sink,
Trap,
If,
While,
For,
Break,
Continue,
// A labeled value block (`blk: { … yield :blk v }`): lowers to its `then_body`
// followed by an exit label, so a `yield :blk` (a labeled break) lands after it.
Block,
}
Assignment_Op :: enum u8 {
Set,
Add,
Sub,
Mul,
Div,
Pointer_Add,
Bit_And,
Bit_Or,
Bit_Xor,
Shift_Left,
Shift_Right,
Shift_Left_Saturating,
}
Stmt :: struct {
kind: Stmt_Kind,
span: source.Span,
local: Local_Id,
index_local: Local_Id,
// `While`/`For`/`Block` may carry a label; a labeled `Break`/`Continue` targets the
// matching enclosing loop or value block. `INVALID` when absent.
label: symbol.Id,
target: Expr_Id,
expr: Expr_Id,
iterator_type: types.Type,
pointer_capture: bool,
// Assignments carry their operation explicitly. Compound operations lower
// by computing the target address once, loading its current value, applying
// the operation to `expr`, and storing through the original address.
assignment_op: Assignment_Op,
// Boolean `If` statements use `expr` as the condition. Conditional unwraps
// use `unwraps` for the ordered optional expressions and capture locals, and
// `guard` for the optional boolean checked after every unwrap succeeds.
// Both forms use `then_body`/`else_body` as the branch statement lists.
// `While` statements use `expr` as the condition, `then_body` as the loop
// body, and `update` as the optional post-iteration statement.
// `For` statements use `expr` as the iterable, `local` as the item capture,
// `index_local` as the optional sequence index, and `iterator_type` as the
// normalized many-item pointer type for sequence iteration.
unwraps: []Conditional_Unwrap,
guard: Expr_Id,
then_body: []Stmt_Id,
else_body: []Stmt_Id,
update: Stmt_Id,
diagnostic: source.Diagnostic_Id,
}
Function :: struct {
name: symbol.Id,
link_name: string,
calling_convention: Calling_Convention,
implementation: Implementation,
linkage: Linkage,
is_main: bool,
variadic: bool,
params: []Local_Id,
result: types.Type,
locals: []Local,
body: []Stmt_Id,
direct_global_reads: [dynamic]Global_Id,
calls: []Function_Id,
problematic: bool,
diagnostic: source.Diagnostic_Id,
}
Global :: struct {
name: symbol.Id,
link_name: string,
type: types.Type,
expr: Expr_Id,
static_value: i64,
is_static: bool,
external: bool,
writable: bool,
dependencies: [dynamic]Global_Id,
calls: []Function_Id,
direct_problem: bool,
problematic: bool,
diagnostic: source.Diagnostic_Id,
}
Module :: struct {
exprs: [dynamic]Expr,
statements: [dynamic]Stmt,
functions: [dynamic]Function,
globals: [dynamic]Global,
strings: [dynamic]string,
injected_main: Function_Id,
io_provider: Function_Id,
types: types.Store,
target: target.Target,
allocator: mem.Allocator,
}
init_module :: proc(selected := target.DEFAULT, allocator := context.allocator) -> Module {
module: Module
module.target = selected
module.injected_main = INVALID_FUNCTION
module.io_provider = INVALID_FUNCTION
module.types = types.init_store(allocator)
module.types.selected = selected
module.allocator = allocator
module.exprs.allocator = allocator
module.statements.allocator = allocator
module.functions.allocator = allocator
module.globals.allocator = allocator
module.strings.allocator = allocator
return module
}
destroy_module :: proc(module: ^Module) {
for expr in module.exprs {
delete(expr.args, module.allocator)
delete(expr.body, module.allocator)
}
for statement in module.statements {
delete(statement.unwraps, module.allocator)
delete(statement.then_body, module.allocator)
delete(statement.else_body, module.allocator)
}
for function in module.functions {
delete(function.link_name, module.allocator)
delete(function.params, module.allocator)
delete(function.locals, module.allocator)
delete(function.body, module.allocator)
delete(function.direct_global_reads)
delete(function.calls, module.allocator)
}
for global in module.globals {
delete(global.link_name, module.allocator)
delete(global.dependencies)
delete(global.calls, module.allocator)
}
for value in module.strings {
delete(value, module.allocator)
}
delete(module.exprs)
delete(module.statements)
delete(module.functions)
delete(module.globals)
delete(module.strings)
types.destroy_store(&module.types)
}