343 lines
7.9 KiB
Odin
343 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,
|
|
Undefined,
|
|
Array,
|
|
Struct,
|
|
Null,
|
|
Unreachable,
|
|
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,
|
|
Mem_Copy,
|
|
Mem_Set,
|
|
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)
|
|
}
|