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, 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, Add, Sub, Mul, Div, Div_Trunc, Div_Floor, Div_Exact, Div_Ceil, Rem, Mod, Pointer_Add, 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, } 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. Arithmetic 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) }