ir
This commit is contained in:
436
src/ir/inst.zig
Normal file
436
src/ir/inst.zig
Normal file
@@ -0,0 +1,436 @@
|
||||
const std = @import("std");
|
||||
const types = @import("types.zig");
|
||||
const TypeId = types.TypeId;
|
||||
const StringId = types.StringId;
|
||||
|
||||
// ── Handles ─────────────────────────────────────────────────────────────
|
||||
|
||||
/// Reference to an SSA value (instruction result).
|
||||
pub const Ref = enum(u32) {
|
||||
/// Sentinel for "no value" / unused operand.
|
||||
none = std.math.maxInt(u32),
|
||||
_,
|
||||
|
||||
pub fn index(self: Ref) u32 {
|
||||
return @intFromEnum(self);
|
||||
}
|
||||
|
||||
pub fn fromIndex(i: u32) Ref {
|
||||
return @enumFromInt(i);
|
||||
}
|
||||
|
||||
pub fn isNone(self: Ref) bool {
|
||||
return self == .none;
|
||||
}
|
||||
};
|
||||
|
||||
pub const BlockId = enum(u32) {
|
||||
_,
|
||||
|
||||
pub fn index(self: BlockId) u32 {
|
||||
return @intFromEnum(self);
|
||||
}
|
||||
|
||||
pub fn fromIndex(i: u32) BlockId {
|
||||
return @enumFromInt(i);
|
||||
}
|
||||
};
|
||||
|
||||
pub const FuncId = enum(u32) {
|
||||
_,
|
||||
|
||||
pub fn index(self: FuncId) u32 {
|
||||
return @intFromEnum(self);
|
||||
}
|
||||
|
||||
pub fn fromIndex(i: u32) FuncId {
|
||||
return @enumFromInt(i);
|
||||
}
|
||||
};
|
||||
|
||||
pub const GlobalId = enum(u32) {
|
||||
_,
|
||||
|
||||
pub fn index(self: GlobalId) u32 {
|
||||
return @intFromEnum(self);
|
||||
}
|
||||
|
||||
pub fn fromIndex(i: u32) GlobalId {
|
||||
return @enumFromInt(i);
|
||||
}
|
||||
};
|
||||
|
||||
// ── Span ────────────────────────────────────────────────────────────────
|
||||
|
||||
pub const Span = struct {
|
||||
start: u32 = 0,
|
||||
end: u32 = 0,
|
||||
};
|
||||
|
||||
// ── Instruction ─────────────────────────────────────────────────────────
|
||||
|
||||
pub const Inst = struct {
|
||||
op: Op,
|
||||
ty: TypeId,
|
||||
span: Span = .{},
|
||||
};
|
||||
|
||||
// ── Op (tagged union) ───────────────────────────────────────────────────
|
||||
|
||||
pub const Op = union(enum) {
|
||||
// ── Constants ───────────────────────────────────────────────────
|
||||
const_int: i64,
|
||||
const_float: f64,
|
||||
const_bool: bool,
|
||||
const_string: StringId,
|
||||
const_null,
|
||||
const_undef, // `---` undefined initializer
|
||||
|
||||
// ── Arithmetic ──────────────────────────────────────────────────
|
||||
add: BinOp,
|
||||
sub: BinOp,
|
||||
mul: BinOp,
|
||||
div: BinOp,
|
||||
mod: BinOp,
|
||||
neg: UnaryOp, // unary -x
|
||||
|
||||
// ── Bitwise ─────────────────────────────────────────────────────
|
||||
bit_and: BinOp,
|
||||
bit_or: BinOp,
|
||||
bit_xor: BinOp,
|
||||
bit_not: UnaryOp,
|
||||
shl: BinOp,
|
||||
shr: BinOp,
|
||||
|
||||
// ── Comparison ──────────────────────────────────────────────────
|
||||
cmp_eq: BinOp,
|
||||
cmp_ne: BinOp,
|
||||
cmp_lt: BinOp,
|
||||
cmp_le: BinOp,
|
||||
cmp_gt: BinOp,
|
||||
cmp_ge: BinOp,
|
||||
|
||||
// ── Logical ─────────────────────────────────────────────────────
|
||||
bool_and: BinOp, // short-circuit &&
|
||||
bool_or: BinOp, // short-circuit ||
|
||||
bool_not: UnaryOp,
|
||||
|
||||
// ── Conversions ─────────────────────────────────────────────────
|
||||
widen: Conversion, // safe widening (s32 → s64)
|
||||
narrow: Conversion, // truncation via `xx` (s64 → s32)
|
||||
bitcast: Conversion, // reinterpret bits
|
||||
int_to_float: Conversion,
|
||||
float_to_int: Conversion,
|
||||
|
||||
// ── Memory ──────────────────────────────────────────────────────
|
||||
alloca: TypeId, // stack allocation, result is *T
|
||||
load: UnaryOp, // load from pointer
|
||||
store: Store, // store value to pointer
|
||||
heap_alloc: UnaryOp, // context.allocator.alloc(size) → *void
|
||||
heap_free: UnaryOp, // context.allocator.free(ptr)
|
||||
|
||||
// ── Struct ops ──────────────────────────────────────────────────
|
||||
struct_init: Aggregate, // construct struct from field values
|
||||
struct_get: FieldAccess, // read struct field by index
|
||||
struct_gep: FieldAccess, // get pointer to struct field (GEP)
|
||||
|
||||
// ── Enum ops ────────────────────────────────────────────────────
|
||||
enum_init: EnumInit, // construct enum value (tag + optional payload)
|
||||
enum_tag: UnaryOp, // extract tag from enum/union
|
||||
enum_payload: FieldAccess, // extract payload from tagged union
|
||||
|
||||
// ── Union ops ───────────────────────────────────────────────────
|
||||
union_get: FieldAccess, // read union field (reinterpret)
|
||||
union_gep: FieldAccess, // pointer to union field
|
||||
|
||||
// ── Array/Slice ops ─────────────────────────────────────────────
|
||||
index_get: BinOp, // arr[idx] → value
|
||||
index_gep: BinOp, // &arr[idx] → pointer
|
||||
length: UnaryOp, // .len on slice/string/array
|
||||
data_ptr: UnaryOp, // .ptr on slice/string
|
||||
subslice: Subslice, // arr[lo..hi]
|
||||
array_to_slice: UnaryOp, // [N]T → []T
|
||||
|
||||
// ── Tuple ops ───────────────────────────────────────────────────
|
||||
tuple_init: Aggregate, // construct tuple from values
|
||||
tuple_get: FieldAccess, // read tuple element by index
|
||||
|
||||
// ── Optional ops ────────────────────────────────────────────────
|
||||
optional_wrap: UnaryOp, // T → ?T
|
||||
optional_unwrap: UnaryOp, // ?T → T (UB if null)
|
||||
optional_has_value: UnaryOp, // ?T → bool
|
||||
optional_coalesce: BinOp, // a ?? b
|
||||
|
||||
// ── Pointer ops ─────────────────────────────────────────────────
|
||||
addr_of: UnaryOp, // @x → *T
|
||||
deref: UnaryOp, // p.* → T
|
||||
|
||||
// ── Vector ops ──────────────────────────────────────────────────
|
||||
vec_splat: UnaryOp, // scalar → vector (broadcast)
|
||||
vec_extract: BinOp, // vec[idx] → scalar
|
||||
vec_insert: TriOp, // vec, idx, val → new_vec
|
||||
|
||||
// ── Calls ───────────────────────────────────────────────────────
|
||||
call: Call,
|
||||
call_indirect: CallIndirect,
|
||||
call_closure: CallIndirect,
|
||||
call_builtin: BuiltinCall,
|
||||
|
||||
// ── Protocol dispatch ───────────────────────────────────────────
|
||||
protocol_call_dynamic: ProtocolCall, // vtable/inline dispatch
|
||||
protocol_erase: ProtocolErase, // concrete → protocol value (xx)
|
||||
|
||||
// ── Closure creation ────────────────────────────────────────────
|
||||
closure_create: ClosureCreate,
|
||||
|
||||
// ── Context ─────────────────────────────────────────────────────
|
||||
context_load: ContextOp, // read context field
|
||||
context_store: ContextOp, // write context field
|
||||
context_save, // save context state (for push)
|
||||
context_restore: UnaryOp, // restore context state (after push)
|
||||
|
||||
// ── Globals ─────────────────────────────────────────────────────
|
||||
global_get: GlobalId,
|
||||
global_set: GlobalSet,
|
||||
|
||||
// ── Block params (SSA phi alternative) ──────────────────────────
|
||||
block_param: BlockParam,
|
||||
|
||||
// ── Any type ────────────────────────────────────────────────────
|
||||
box_any: BoxAny, // T → Any (erase type)
|
||||
unbox_any: UnaryOp, // Any → T (restore type)
|
||||
|
||||
// ── Terminators ─────────────────────────────────────────────────
|
||||
br: Branch,
|
||||
cond_br: CondBranch,
|
||||
switch_br: SwitchBranch,
|
||||
ret: UnaryOp,
|
||||
ret_void,
|
||||
@"unreachable",
|
||||
|
||||
// ── Misc ────────────────────────────────────────────────────────
|
||||
/// No-op placeholder for unlowered AST nodes.
|
||||
placeholder: StringId, // name of the unlowered construct
|
||||
};
|
||||
|
||||
// ── Operand structs ─────────────────────────────────────────────────────
|
||||
|
||||
pub const UnaryOp = struct {
|
||||
operand: Ref,
|
||||
};
|
||||
|
||||
pub const BinOp = struct {
|
||||
lhs: Ref,
|
||||
rhs: Ref,
|
||||
};
|
||||
|
||||
pub const TriOp = struct {
|
||||
a: Ref,
|
||||
b: Ref,
|
||||
c: Ref,
|
||||
};
|
||||
|
||||
pub const Store = struct {
|
||||
ptr: Ref,
|
||||
val: Ref,
|
||||
};
|
||||
|
||||
pub const Conversion = struct {
|
||||
operand: Ref,
|
||||
from: TypeId,
|
||||
to: TypeId,
|
||||
};
|
||||
|
||||
pub const FieldAccess = struct {
|
||||
base: Ref,
|
||||
field_index: u32,
|
||||
};
|
||||
|
||||
pub const Aggregate = struct {
|
||||
fields: []const Ref,
|
||||
};
|
||||
|
||||
pub const EnumInit = struct {
|
||||
tag: u32,
|
||||
payload: Ref, // Ref.none if no payload
|
||||
};
|
||||
|
||||
pub const Subslice = struct {
|
||||
base: Ref,
|
||||
lo: Ref,
|
||||
hi: Ref,
|
||||
};
|
||||
|
||||
pub const Call = struct {
|
||||
callee: FuncId,
|
||||
args: []const Ref,
|
||||
};
|
||||
|
||||
pub const CallIndirect = struct {
|
||||
callee: Ref,
|
||||
args: []const Ref,
|
||||
};
|
||||
|
||||
pub const BuiltinCall = struct {
|
||||
builtin: BuiltinId,
|
||||
args: []const Ref,
|
||||
};
|
||||
|
||||
pub const BuiltinId = enum(u16) {
|
||||
print,
|
||||
out,
|
||||
sqrt,
|
||||
size_of,
|
||||
cast,
|
||||
malloc,
|
||||
free,
|
||||
memcpy,
|
||||
memset,
|
||||
};
|
||||
|
||||
pub const ProtocolCall = struct {
|
||||
receiver: Ref, // protocol value (ctx + vtable/fn_ptrs)
|
||||
method_index: u32,
|
||||
args: []const Ref,
|
||||
};
|
||||
|
||||
pub const ProtocolErase = struct {
|
||||
concrete: Ref,
|
||||
protocol_type: TypeId,
|
||||
};
|
||||
|
||||
pub const ClosureCreate = struct {
|
||||
func: FuncId, // trampoline function
|
||||
env: Ref, // allocated env pointer (or Ref.none for no captures)
|
||||
};
|
||||
|
||||
pub const ContextOp = struct {
|
||||
field: StringId,
|
||||
value: Ref, // Ref.none for loads
|
||||
};
|
||||
|
||||
pub const GlobalSet = struct {
|
||||
global: GlobalId,
|
||||
value: Ref,
|
||||
};
|
||||
|
||||
pub const BlockParam = struct {
|
||||
block: BlockId,
|
||||
param_index: u32,
|
||||
};
|
||||
|
||||
pub const BoxAny = struct {
|
||||
operand: Ref,
|
||||
source_type: TypeId,
|
||||
};
|
||||
|
||||
pub const Branch = struct {
|
||||
target: BlockId,
|
||||
args: []const Ref, // block param values
|
||||
};
|
||||
|
||||
pub const CondBranch = struct {
|
||||
cond: Ref,
|
||||
then_target: BlockId,
|
||||
then_args: []const Ref,
|
||||
else_target: BlockId,
|
||||
else_args: []const Ref,
|
||||
};
|
||||
|
||||
pub const SwitchBranch = struct {
|
||||
operand: Ref,
|
||||
cases: []const Case,
|
||||
default: BlockId,
|
||||
default_args: []const Ref,
|
||||
|
||||
pub const Case = struct {
|
||||
value: i64,
|
||||
target: BlockId,
|
||||
args: []const Ref,
|
||||
};
|
||||
};
|
||||
|
||||
// ── Block ───────────────────────────────────────────────────────────────
|
||||
|
||||
pub const Block = struct {
|
||||
name: StringId,
|
||||
params: []const TypeId, // block parameter types (SSA phi alternative)
|
||||
insts: std.ArrayList(Inst),
|
||||
|
||||
pub fn init(name: StringId, params: []const TypeId) Block {
|
||||
return .{
|
||||
.name = name,
|
||||
.params = params,
|
||||
.insts = std.ArrayList(Inst).empty,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Block, alloc: std.mem.Allocator) void {
|
||||
self.insts.deinit(alloc);
|
||||
}
|
||||
};
|
||||
|
||||
// ── Function ────────────────────────────────────────────────────────────
|
||||
|
||||
pub const Function = struct {
|
||||
name: StringId,
|
||||
params: []const Param,
|
||||
ret: TypeId,
|
||||
blocks: std.ArrayList(Block),
|
||||
is_extern: bool = false,
|
||||
is_comptime: bool = false,
|
||||
linkage: Linkage = .internal,
|
||||
|
||||
pub const Param = struct {
|
||||
name: StringId,
|
||||
ty: TypeId,
|
||||
};
|
||||
|
||||
pub const Linkage = enum {
|
||||
internal,
|
||||
external,
|
||||
private,
|
||||
};
|
||||
|
||||
pub fn init(name: StringId, params: []const Param, ret: TypeId) Function {
|
||||
return .{
|
||||
.name = name,
|
||||
.params = params,
|
||||
.ret = ret,
|
||||
.blocks = std.ArrayList(Block).empty,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Function, alloc: std.mem.Allocator) void {
|
||||
for (self.blocks.items) |*block| {
|
||||
block.deinit(alloc);
|
||||
}
|
||||
self.blocks.deinit(alloc);
|
||||
}
|
||||
};
|
||||
|
||||
// ── Global ──────────────────────────────────────────────────────────────
|
||||
|
||||
pub const Global = struct {
|
||||
name: StringId,
|
||||
ty: TypeId,
|
||||
init_val: ?ConstantValue = null,
|
||||
is_extern: bool = false,
|
||||
is_const: bool = false,
|
||||
/// For comptime globals: the function to interpret to get the init value.
|
||||
comptime_func: ?FuncId = null,
|
||||
};
|
||||
|
||||
// ── ConstantValue ───────────────────────────────────────────────────────
|
||||
|
||||
pub const ConstantValue = union(enum) {
|
||||
int: i64,
|
||||
float: f64,
|
||||
boolean: bool,
|
||||
string: StringId,
|
||||
null_val,
|
||||
undef,
|
||||
zeroinit,
|
||||
aggregate: []const ConstantValue,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user