Files
spl/stage1/spl_ast2ir.c
T
2026-09-16 20:09:24 +08:00

1643 lines
60 KiB
C

#include "spl_ast2ir.h"
#include "spl_intrin.h"
static spl_type_id_t to_ir_type(spl_ast2ir_t *a, spl_lang_type_id_t lang) {
if (!lang)
return 0;
return spl_lang_type_get_ir_type(a->sema->type, lang);
}
static spl_lang_type_id_t node_lang(spl_ast2ir_t *a, spl_ast_node_t *n) {
(void)a;
return n ? n->resolved_lang_tid : 0;
}
static spl_type_id_t node_ir(spl_ast2ir_t *a, spl_ast_node_t *n) {
return to_ir_type(a, node_lang(a, n));
}
static spl_ir_node_ref_t const_usize(spl_ast2ir_t *a, usize v) {
spl_lang_type_id_t t = spl_lang_make_int(a->sema->type, sizeof(void *) * 8, 0);
return spl_ir_builder_type_const_int(a->ir, to_ir_type(a, t), v);
}
static spl_ir_node_ref_t emit_if_like(spl_ast2ir_t *a, spl_ast_node_ref_t ref);
static spl_ir_node_ref_t emit_loop_like(spl_ast2ir_t *a, spl_ast_node_ref_t ref);
static void emit_break_stmt(spl_ast2ir_t *a, spl_ast_node_t *n);
static spl_ir_node_ref_t emit_expr(spl_ast2ir_t *a, spl_ast_node_ref_t ref);
static spl_ir_node_ref_t emit_lvalue(spl_ast2ir_t *a, spl_ast_node_ref_t ref);
static void emit_stmt(spl_ast2ir_t *a, spl_ast_node_ref_t ref);
static void emit_decl(spl_ast2ir_t *a, spl_ast_node_ref_t ref);
static void emit_fn_reserve(spl_ast2ir_t *a, spl_ast_node_t *n);
static void emit_fn_body(spl_ast2ir_t *a, spl_ast_node_t *n);
static void emit_container_reserve(spl_ast2ir_t *a, spl_ast_node_ref_t ref);
static void emit_container_body(spl_ast2ir_t *a, spl_ast_node_ref_t ref);
static spl_ir_node_ref_t ast2ir_emit_cast_builtin(spl_ast2ir_t *a, spl_ast_node_t *n,
spl_intrin_t in, spl_type_id_t ft,
spl_type_id_t tt, spl_ir_node_ref_t v);
static spl_ir_node_ref_t emit_cast(spl_ast2ir_t *a, spl_ast_node_t *n);
static spl_ir_node_ref_t emit_expr_raw(spl_ast2ir_t *a, spl_ast_node_ref_t ref);
static spl_ir_node_ref_t emit_implicit_cast(spl_ast2ir_t *a, spl_ast_node_t *n, spl_type_id_t ft,
spl_type_id_t tt, spl_ir_node_ref_t v);
static spl_ir_node_ref_t ast2ir_intrin_call(spl_ast2ir_t *a, spl_ast_node_t *n, spl_intrin_t in,
spl_ast_node_ref_vec_t *args);
static spl_ir_node_ref_t ast2ir_intrin_field(spl_ast2ir_t *a, spl_ast_node_t *n, spl_intrin_t in);
static spl_ir_node_ref_t ast2ir_intrin_len(spl_ast2ir_t *a, spl_ast_node_t *n);
static spl_ir_node_ref_t emit_zero_value(spl_ast2ir_t *a, spl_type_id_t tid) {
spl_type_node_t *tn = spl_type_node(&a->sema->type->ir, tid);
if (!tn)
return 0;
spl_type_node_kind_t tk = tn->kind;
switch (tk) {
case SPL_TYPE_BOOL:
return spl_ir_builder_type_const_bool(a->ir, tid, false);
case SPL_TYPE_INT:
return spl_ir_builder_type_const_int(a->ir, tid, 0);
case SPL_TYPE_FLOAT:
return spl_ir_builder_type_const_float(a->ir, tid, 0.0);
case SPL_TYPE_PTR:
case SPL_TYPE_FN:
return spl_ir_builder_type_const_ptr(a->ir, tid, NULL);
case SPL_TYPE_ARRAY: {
usize arr_len = tn->array_type.len;
spl_type_id_t arr_elem = tn->array_type.element;
spl_ir_node_ref_t node = spl_ir_builder_agg_construct(a->ir, tid);
for (usize i = 0; i < arr_len; ++i) {
spl_ir_node_ref_t f = emit_zero_value(a, arr_elem);
if (!f)
return 0;
spl_ir_builder_agg_construct_field(a->ir, node, f);
}
return node;
}
case SPL_TYPE_STRUCT:
case SPL_TYPE_SHAPE: {
usize nfield = vec_size(tn->agg_type.variants);
spl_type_id_vec_t field_tids;
vec_init(field_tids);
for (usize i = 0; i < nfield; ++i)
vec_push(field_tids, vec_at(tn->agg_type.variants, i).type_id);
spl_ir_node_ref_t node = spl_ir_builder_agg_construct(a->ir, tid);
for (usize i = 0; i < nfield; ++i) {
spl_ir_node_ref_t f = emit_zero_value(a, vec_at(field_tids, i));
if (!f) {
vec_free(field_tids);
return 0;
}
spl_ir_builder_agg_construct_field(a->ir, node, f);
}
vec_free(field_tids);
return node;
}
case SPL_TYPE_UNION: {
usize nfield = vec_size(tn->agg_type.variants);
if (nfield == 0)
return spl_ir_builder_agg_construct(a->ir, tid);
spl_type_id_t first_tid = vec_at(tn->agg_type.variants, 0).type_id;
spl_ir_node_ref_t node = spl_ir_builder_agg_construct(a->ir, tid);
spl_ir_node_ref_t f = emit_zero_value(a, first_tid);
if (!f)
return 0;
spl_ir_builder_agg_construct_field(a->ir, node, f);
return node;
}
default:
return 0;
}
}
static void copy_attrs_to_ir_node(spl_ast2ir_t *a, spl_ast_node_t *n, spl_ir_node_ref_t ir_ref) {
if (!ir_ref || !n || vec_size(n->attrs) == 0)
return;
spl_ir_node_t *irn = spl_ir_node(&a->ir->ir, a->ir->current_fn, ir_ref);
if (!irn)
return;
for (usize i = 0; i < vec_size(n->attrs); ++i)
vec_push(irn->attrs, vec_at(n->attrs, i));
}
static void copy_attrs_to_ir_func(spl_ast2ir_t *a, spl_ast_node_t *n, spl_ir_func_ref_t fn_ref) {
if (!fn_ref || !n || vec_size(n->attrs) == 0)
return;
spl_ir_func_t *irf = spl_ir_func(&a->ir->ir, fn_ref);
if (!irf)
return;
for (usize i = 0; i < vec_size(n->attrs); ++i)
vec_push(irf->attrs, vec_at(n->attrs, i));
}
static spl_ir_node_ref_t ast2ir_intrin_cast(spl_ast2ir_t *a, spl_ast_node_t *n, spl_intrin_t in,
spl_ast_node_ref_vec_t *args) {
usize nargs = vec_size(*args);
if (nargs < 1) {
spl_diag_error(a->diag, SPL_DIAG_KIND_INTRIN_ARG_COUNT, &n->dbg,
"cast builtin requires at least 1 argument");
return 0;
}
spl_ir_node_ref_t v = emit_expr(a, vec_at(*args, 0));
if (!v)
return 0;
spl_ast_node_t *src = spl_ast_node(a->ast, vec_at(*args, 0));
spl_type_id_t ft = node_ir(a, src);
spl_type_id_t tt = 0;
if (nargs >= 2) {
spl_ast_node_t *targ = spl_ast_node(a->ast, vec_at(*args, 1));
tt = node_ir(a, targ);
} else {
tt = node_ir(a, n);
}
if (!ft || !tt)
return 0;
return ast2ir_emit_cast_builtin(a, n, in, ft, tt, v);
}
static spl_ir_node_ref_t ast2ir_intrin_call(spl_ast2ir_t *a, spl_ast_node_t *n, spl_intrin_t in,
spl_ast_node_ref_vec_t *args) {
switch (in) {
case SPL_INTRIN_SIZEOF: {
spl_ast_node_t *arg = spl_ast_node(a->ast, vec_at(*args, 0));
return spl_ir_builder_type_sizeof(a->ir, node_ir(a, arg));
}
case SPL_INTRIN_ALIGNOF: {
spl_ast_node_t *arg = spl_ast_node(a->ast, vec_at(*args, 0));
return spl_ir_builder_type_alignof(a->ir, node_ir(a, arg));
}
case SPL_INTRIN_ASSERT: {
spl_ir_node_ref_t cond = emit_expr(a, vec_at(*args, 0));
if (!cond)
return 0;
spl_lang_type_id_t b = spl_lang_make_bool(a->sema->type);
spl_ir_node_ref_t notc = spl_ir_builder_arith_not(a->ir, to_ir_type(a, b), cond);
spl_ir_node_ref_t br = spl_ir_builder_control_br(a->ir, notc);
spl_ir_builder_block_end(a->ir);
usize panic_blk = spl_ir_builder_block_begin(a->ir);
const char *fname = n->dbg.fname ? n->dbg.fname : "?";
const char *user_msg = NULL;
if (vec_size(*args) == 2) {
spl_ast_node_t *mn = spl_ast_node(a->ast, vec_at(*args, 1));
if (mn && mn->kind == SPL_AST_EXPR_STRING_LIT && mn->string_lit.data)
user_msg = mn->string_lit.data;
}
char *msg;
int need;
if (user_msg) {
need = snprintf(NULL, 0, "%s:%d:%d: assert failed: %s", fname, n->dbg.line, n->dbg.col,
user_msg);
msg = malloc((usize)need + 1);
snprintf(msg, (usize)need + 1, "%s:%d:%d: assert failed: %s", fname, n->dbg.line,
n->dbg.col, user_msg);
} else {
need = snprintf(NULL, 0, "%s:%d:%d: assert failed", fname, n->dbg.line, n->dbg.col);
msg = malloc((usize)need + 1);
snprintf(msg, (usize)need + 1, "%s:%d:%d: assert failed", fname, n->dbg.line,
n->dbg.col);
}
spl_ir_builder_dbg_panic(a->ir, msg, (usize)need);
spl_ir_builder_control_trap(a->ir);
spl_ir_builder_block_end(a->ir);
usize cont_blk = spl_ir_builder_block_begin(a->ir);
spl_ir_builder_control_br_fixup(a->ir, br, panic_blk, cont_blk);
return 0;
}
case SPL_INTRIN_INT_CAST:
case SPL_INTRIN_TRUNCATE:
case SPL_INTRIN_BIT_CAST:
case SPL_INTRIN_FLOAT_CAST:
case SPL_INTRIN_INT_FROM_FLOAT:
case SPL_INTRIN_FLOAT_FROM_INT:
case SPL_INTRIN_INT_FROM_BOOL:
case SPL_INTRIN_INT_FROM_PTR:
case SPL_INTRIN_PTR_FROM_INT:
case SPL_INTRIN_PTR_CAST:
return ast2ir_intrin_cast(a, n, in, args);
case SPL_INTRIN_BREAKPOINT:
return spl_ir_builder_dbg_breakpoint(a->ir);
default:
UNREACHABLE();
}
return 0;
}
static spl_ir_node_ref_t ast2ir_intrin_field(spl_ast2ir_t *a, spl_ast_node_t *n, spl_intrin_t in) {
switch (in) {
case SPL_INTRIN_LEN:
return ast2ir_intrin_len(a, n);
default:
UNREACHABLE();
}
return 0;
}
static spl_ir_node_ref_t ast2ir_intrin_len(spl_ast2ir_t *a, spl_ast_node_t *n) {
spl_ast_node_t *obj = spl_ast_node(a->ast, n->field.obj);
spl_lang_type_id_t recv_lang = node_lang(a, obj);
spl_lang_type_node_t *recv = spl_lang_type_node(a->sema->type, recv_lang);
if (!recv)
return 0;
spl_lang_type_id_t usize_lang = spl_lang_make_int(a->sema->type, sizeof(void *) * 8, 0);
spl_type_id_t usize_ir = to_ir_type(a, usize_lang);
switch (recv->kind) {
case SPL_LANG_TYPE_ARRAY: {
spl_type_node_t *arr = spl_type_node(&a->sema->type->ir, recv->ir_type_id);
if (!arr)
return 0;
return spl_ir_builder_type_const_int(a->ir, usize_ir, arr->array_type.len);
}
case SPL_LANG_TYPE_SLICE: {
spl_ir_node_ref_t slice_val = emit_expr(a, n->field.obj);
if (!slice_val)
return 0;
return spl_ir_builder_agg_extract(a->ir, recv->ir_type_id, usize_ir,
SPL_SLICE_LEN_FIELD_IDX, slice_val);
}
default:
return 0;
}
}
static spl_ir_node_ref_t emit_lvalue(spl_ast2ir_t *a, spl_ast_node_ref_t ref) {
spl_ast_node_t *n = spl_ast_node(a->ast, ref);
if (!n)
return 0;
switch (n->kind) {
case SPL_AST_EXPR_IDENT: {
spl_def_node_t *def = spl_def_node(a->sema->type, n->resolved_def_id);
if (def && def->kind == SPL_DEF_FN)
return 0;
spl_ir_node_ref_t slot = 0;
if (!map_get(a->def2ir_map, n->resolved_def_id, &slot))
return 0;
return slot;
}
case SPL_AST_EXPR_UNARY: {
if (n->unary.op != SPL_UNARY_OP_DEREF)
return 0;
return emit_expr(a, n->unary.operand);
}
case SPL_AST_EXPR_FIELD: {
spl_ast_node_t *obj = spl_ast_node(a->ast, n->field.obj);
if (!obj)
return 0;
spl_lang_type_id_t obj_lang = node_lang(a, obj);
spl_lang_type_node_t *obj_ln = spl_lang_type_node(a->sema->type, obj_lang);
if (!obj_ln)
return 0;
spl_lang_type_kind_t obj_kind = obj_ln->kind;
spl_lang_type_id_t obj_elem = (obj_kind == SPL_LANG_TYPE_PTR) ? obj_ln->extra.elem : 0;
if (spl_intrin_lookup_field(obj_kind, n->field.field) != SPL_INTRIN_NONE)
return 0;
spl_ir_node_ref_t base = 0;
spl_lang_type_id_t agg_lang = 0;
if (obj_kind == SPL_LANG_TYPE_PTR) {
base = emit_expr(a, n->field.obj);
agg_lang = obj_elem;
} else {
base = emit_lvalue(a, n->field.obj);
agg_lang = obj_lang;
}
if (!base)
return 0;
spl_type_id_t agg_ir = to_ir_type(a, agg_lang);
if (!agg_ir)
return 0;
usize idx = 0;
if (!spl_type_agg_member_idx(&a->sema->type->ir, agg_ir, n->field.field, &idx))
return 0;
spl_lang_type_node_t *agg_ln = spl_lang_type_node(a->sema->type, agg_lang);
if (agg_ln && agg_ln->kind == SPL_LANG_TYPE_ENUM)
idx += 1;
return spl_ir_builder_mem_field(a->ir, agg_ir, base, idx);
}
case SPL_AST_EXPR_INDEX: {
spl_ast_node_t *obj = spl_ast_node(a->ast, n->index.obj);
if (!obj)
return 0;
spl_lang_type_id_t obj_lang = node_lang(a, obj);
spl_lang_type_node_t *obj_ln = spl_lang_type_node(a->sema->type, obj_lang);
if (!obj_ln)
return 0;
spl_lang_type_kind_t obj_kind = obj_ln->kind;
spl_lang_type_id_t obj_elem = 0;
if (obj_kind == SPL_LANG_TYPE_PTR || obj_kind == SPL_LANG_TYPE_ARRAY ||
obj_kind == SPL_LANG_TYPE_SLICE)
obj_elem = obj_ln->extra.elem;
if (obj_kind == SPL_LANG_TYPE_TUPLE) {
spl_ast_node_t *idx_node = spl_ast_node(a->ast, n->index.index);
if (!idx_node || idx_node->kind != SPL_AST_EXPR_INT_LIT)
return 0;
usize i = (usize)idx_node->int_lit.bits;
spl_ir_node_ref_t base = emit_lvalue(a, n->index.obj);
if (!base)
return 0;
spl_type_id_t tuple_ir = to_ir_type(a, obj_lang);
if (!tuple_ir)
return 0;
return spl_ir_builder_mem_field(a->ir, tuple_ir, base, i);
}
spl_ir_node_ref_t base = 0;
spl_lang_type_id_t elem_lang = 0;
if (obj_kind == SPL_LANG_TYPE_PTR) {
base = emit_expr(a, n->index.obj);
elem_lang = obj_elem;
} else if (obj_kind == SPL_LANG_TYPE_ARRAY) {
base = emit_lvalue(a, n->index.obj);
elem_lang = obj_elem;
} else if (obj_kind == SPL_LANG_TYPE_SLICE) {
spl_ir_node_ref_t slice_val = emit_expr(a, n->index.obj);
if (!slice_val)
return 0;
spl_type_id_t ptr_ir =
spl_type_builder_ptr(&a->sema->type->ir, to_ir_type(a, obj_elem));
spl_ir_node_ref_t ptr = spl_ir_builder_agg_extract(a->ir, ptr_ir, ptr_ir,
SPL_SLICE_PTR_FIELD_IDX, slice_val);
base = ptr;
elem_lang = obj_elem;
} else {
return 0;
}
if (!base)
return 0;
spl_ir_node_ref_t idx = emit_expr(a, n->index.index);
if (!idx)
return 0;
spl_type_id_t elem_ir = to_ir_type(a, elem_lang);
if (!elem_ir)
return 0;
return spl_ir_builder_mem_offset(a->ir, elem_ir, base, idx);
}
default:
return 0;
}
}
static spl_ir_node_ref_t ast2ir_emit_cast_builtin(spl_ast2ir_t *a, spl_ast_node_t *n,
spl_intrin_t in, spl_type_id_t ft,
spl_type_id_t tt, spl_ir_node_ref_t v) {
switch (in) {
case SPL_INTRIN_INT_CAST: {
spl_type_node_t *fs = spl_type_node(&a->sema->type->ir, ft);
if (fs && fs->int_type.is_signed)
return spl_ir_builder_cast_sext(a->ir, ft, tt, v);
return spl_ir_builder_cast_zext(a->ir, ft, tt, v);
}
case SPL_INTRIN_TRUNCATE:
return spl_ir_builder_cast_trunc(a->ir, ft, tt, v);
case SPL_INTRIN_BIT_CAST:
return spl_ir_builder_cast_bitcast(a->ir, ft, tt, v);
case SPL_INTRIN_FLOAT_CAST: {
spl_type_node_t *fs = spl_type_node(&a->sema->type->ir, ft);
spl_type_node_t *ts = spl_type_node(&a->sema->type->ir, tt);
if (fs && ts && fs->float_type.bits > ts->float_type.bits)
return spl_ir_builder_cast_ftrunc(a->ir, ft, tt, v);
return spl_ir_builder_cast_fext(a->ir, ft, tt, v);
}
case SPL_INTRIN_INT_FROM_FLOAT:
return spl_ir_builder_case_float2int(a->ir, ft, tt, v);
case SPL_INTRIN_FLOAT_FROM_INT:
return spl_ir_builder_case_int2float(a->ir, ft, tt, v);
case SPL_INTRIN_INT_FROM_BOOL:
return spl_ir_builder_cast_bool2int(a->ir, ft, tt, v);
case SPL_INTRIN_INT_FROM_PTR:
return spl_ir_builder_cast_ptr2int(a->ir, ft, tt, v);
case SPL_INTRIN_PTR_FROM_INT:
return spl_ir_builder_cast_int2ptr(a->ir, ft, tt, v);
case SPL_INTRIN_PTR_CAST:
return spl_ir_builder_cast_bitcast(a->ir, ft, tt, v);
default:
spl_diag_error(a->diag, SPL_DIAG_KIND_INTERNAL, &n->dbg, "ast2ir: unknown cast builtin");
return 0;
}
}
static spl_ir_node_ref_t emit_cast(spl_ast2ir_t *a, spl_ast_node_t *n) {
spl_ir_node_ref_t v = emit_expr_raw(a, n->cast.expr);
if (!v)
return 0;
spl_ast_node_t *src = spl_ast_node(a->ast, n->cast.expr);
spl_ast_node_t *dst = spl_ast_node(a->ast, n->cast.type);
spl_type_id_t ft = node_ir(a, src);
spl_type_id_t tt = node_ir(a, dst);
if (!ft || !tt)
return 0;
spl_intrin_t in = spl_intrin_lookup_cast(&a->sema->type->ir, ft, tt);
if (in == SPL_INTRIN_NONE) {
spl_diag_error(a->diag, SPL_DIAG_KIND_CAST_INVALID, &n->dbg,
"as: no conversion between the given types");
return 0;
}
return ast2ir_emit_cast_builtin(a, n, in, ft, tt, v);
}
static spl_ir_node_ref_t emit_expr(spl_ast2ir_t *a, spl_ast_node_ref_t ref) {
spl_ir_node_ref_t val = emit_expr_raw(a, ref);
if (!val)
return 0;
spl_ast_node_t *n = spl_ast_node(a->ast, ref);
if (!n || n->expected_lang_tid == 0)
return val;
copy_attrs_to_ir_node(a, n, val);
spl_lang_type_id_t from = n->resolved_lang_tid;
spl_lang_type_id_t to = n->expected_lang_tid;
if (!from || from == to)
return val;
spl_type_id_t from_ir = to_ir_type(a, from);
spl_type_id_t to_ir = to_ir_type(a, to);
if (!from_ir || !to_ir || from_ir == to_ir)
return val;
return emit_implicit_cast(a, n, from_ir, to_ir, val);
}
static spl_ir_node_ref_t emit_expr_raw(spl_ast2ir_t *a, spl_ast_node_ref_t ref) {
spl_ast_node_t *n = spl_ast_node(a->ast, ref);
if (!n)
return 0;
switch (n->kind) {
case SPL_AST_EXPR_INT_LIT: {
spl_type_id_t it = node_ir(a, n);
spl_type_node_t *itn = spl_type_node(&a->sema->type->ir, it);
if (itn && itn->kind == SPL_TYPE_FLOAT) {
double d = (double)(int64_t)n->int_lit.bits;
return spl_ir_builder_type_const_float(a->ir, it, d);
}
return spl_ir_builder_type_const_int(a->ir, it, (usize)n->int_lit.bits);
}
case SPL_AST_EXPR_FLOAT_LIT:
return spl_ir_builder_type_const_float(a->ir, node_ir(a, n), n->float_lit.bits);
case SPL_AST_EXPR_CHAR_LIT:
return spl_ir_builder_type_const_char(a->ir, node_ir(a, n), n->char_lit.val);
case SPL_AST_EXPR_STRING_LIT: {
usize len = n->string_lit.len;
spl_lang_type_id_t u8_lang = spl_lang_make_int(a->sema->type, 8, 0);
spl_type_id_t u8_ir = to_ir_type(a, u8_lang);
spl_type_id_t ptr_u8_ir = spl_type_builder_ptr(&a->sema->type->ir, u8_ir);
spl_lang_type_id_t usize_lang = spl_lang_make_int(a->sema->type, sizeof(void *) * 8, 0);
spl_type_id_t usize_ir = to_ir_type(a, usize_lang);
spl_lang_type_id_t arr_lang = spl_lang_make_array(a->sema->type, u8_lang, len);
spl_type_id_t arr_ir = to_ir_type(a, arr_lang);
spl_ir_node_ref_t raw =
spl_ir_builder_type_const_bytes(a->ir, arr_ir, n->string_lit.data, n->string_lit.len);
spl_ir_node_ref_t ptr = spl_ir_builder_mem_global_alloc(a->ir, ptr_u8_ir, raw);
spl_ir_node_ref_t len_c = spl_ir_builder_type_const_int(a->ir, usize_ir, len);
spl_type_id_t slice_ir = node_ir(a, n);
spl_ir_node_ref_t node = spl_ir_builder_agg_construct(a->ir, slice_ir);
spl_ir_builder_agg_construct_field(a->ir, node, ptr);
spl_ir_builder_agg_construct_field(a->ir, node, len_c);
return node;
}
case SPL_AST_EXPR_BOOL_LIT:
return spl_ir_builder_type_const_bool(a->ir, node_ir(a, n), n->bool_lit.val);
case SPL_AST_EXPR_NULL_LIT:
return spl_ir_builder_type_const_ptr(a->ir, node_ir(a, n), NULL);
case SPL_AST_EXPR_UNDEFINED_LIT:
return 0;
case SPL_AST_EXPR_IDENT: {
spl_def_node_t *def = spl_def_node(a->sema->type, n->resolved_def_id);
if (def && def->kind == SPL_DEF_FN) {
spl_ir_node_ref_t fn_ref = 0;
if (!map_get(a->def2ir_map, n->resolved_def_id, &fn_ref))
return 0;
return spl_ir_builder_type_const_fn(a->ir, node_ir(a, n), (spl_ir_func_ref_t)fn_ref);
}
spl_ir_node_ref_t slot = 0;
if (!map_get(a->def2ir_map, n->resolved_def_id, &slot))
return 0;
return spl_ir_builder_mem_load(a->ir, node_ir(a, n), slot);
}
case SPL_AST_EXPR_UNARY: {
if (n->unary.op == SPL_UNARY_OP_ADDRESS)
return emit_lvalue(a, n->unary.operand);
if (n->unary.op == SPL_UNARY_OP_DEREF) {
spl_ir_node_ref_t ptr = emit_lvalue(a, ref);
if (!ptr)
return 0;
return spl_ir_builder_mem_load(a->ir, node_ir(a, n), ptr);
}
spl_ir_node_ref_t v = emit_expr(a, n->unary.operand);
if (!v)
return 0;
spl_type_id_t t = node_ir(a, n);
switch (n->unary.op) {
case SPL_UNARY_OP_MINUS:
return spl_ir_builder_arith_neg(a->ir, t, v);
case SPL_UNARY_OP_NOT:
case SPL_UNARY_OP_BIT_NOT:
return spl_ir_builder_arith_not(a->ir, t, v);
default:
return 0;
}
}
case SPL_AST_EXPR_BINARY: {
spl_binary_op_t op = n->binary.op;
if (op == SPL_OP_ASSIGN) {
spl_ir_node_ref_t ptr = emit_lvalue(a, n->binary.left);
spl_ir_node_ref_t v = emit_expr(a, n->binary.right);
if (!ptr || !v)
return 0;
spl_ast_node_t *ln = spl_ast_node(a->ast, n->binary.left);
spl_type_id_t t = node_ir(a, ln);
spl_ir_builder_mem_store(a->ir, t, ptr, v);
return v;
}
if (op >= SPL_OP_ASSIGN_ADD && op <= SPL_OP_ASSIGN_RSHIFT) {
spl_ir_node_ref_t ptr = emit_lvalue(a, n->binary.left);
if (!ptr)
return 0;
spl_ast_node_t *ln = spl_ast_node(a->ast, n->binary.left);
spl_type_id_t t = node_ir(a, ln);
spl_ir_node_ref_t lv = spl_ir_builder_mem_load(a->ir, t, ptr);
spl_ir_node_ref_t rv = emit_expr(a, n->binary.right);
if (!lv || !rv)
return 0;
spl_ir_node_ref_t res = 0;
switch (op) {
case SPL_OP_ASSIGN_ADD:
res = spl_ir_builder_arith_add(a->ir, t, lv, rv);
break;
case SPL_OP_ASSIGN_SUB:
res = spl_ir_builder_arith_sub(a->ir, t, lv, rv);
break;
case SPL_OP_ASSIGN_MUL:
res = spl_ir_builder_arith_mul(a->ir, t, lv, rv);
break;
case SPL_OP_ASSIGN_DIV:
res = spl_ir_builder_arith_div(a->ir, t, lv, rv);
break;
case SPL_OP_ASSIGN_MOD:
res = spl_ir_builder_arith_rem(a->ir, t, lv, rv);
break;
case SPL_OP_ASSIGN_AND:
res = spl_ir_builder_arith_and(a->ir, t, lv, rv);
break;
case SPL_OP_ASSIGN_OR:
res = spl_ir_builder_arith_or(a->ir, t, lv, rv);
break;
case SPL_OP_ASSIGN_XOR:
res = spl_ir_builder_arith_xor(a->ir, t, lv, rv);
break;
case SPL_OP_ASSIGN_LSHIFT:
res = spl_ir_builder_arith_shl(a->ir, t, lv, rv);
break;
case SPL_OP_ASSIGN_RSHIFT:
res = spl_ir_builder_arith_shr(a->ir, t, lv, rv);
break;
default:
return 0;
}
if (!res)
return 0;
spl_ir_builder_mem_store(a->ir, t, ptr, res);
return res;
}
spl_ir_node_ref_t l = emit_expr(a, n->binary.left);
spl_ir_node_ref_t r = emit_expr(a, n->binary.right);
if (!l || !r)
return 0;
spl_ast_node_t *ln = spl_ast_node(a->ast, n->binary.left);
spl_lang_type_id_t lt = node_lang(a, ln);
spl_lang_type_node_t *lln = spl_lang_type_node(a->sema->type, lt);
if (lln && lln->kind == SPL_LANG_TYPE_PTR && (op == SPL_OP_ADD || op == SPL_OP_SUB)) {
spl_lang_type_id_t elem_lang = lln->extra.elem;
spl_type_id_t elem_ir = to_ir_type(a, elem_lang);
spl_type_id_t rt_ir = node_ir(a, spl_ast_node(a->ast, n->binary.right));
spl_ir_node_ref_t off = r;
if (op == SPL_OP_SUB)
off = spl_ir_builder_arith_neg(a->ir, rt_ir, r);
return spl_ir_builder_mem_offset(a->ir, elem_ir, l, off);
}
spl_type_id_t t = node_ir(a, n);
switch (op) {
case SPL_OP_ADD:
return spl_ir_builder_arith_add(a->ir, t, l, r);
case SPL_OP_SUB:
return spl_ir_builder_arith_sub(a->ir, t, l, r);
case SPL_OP_MUL:
return spl_ir_builder_arith_mul(a->ir, t, l, r);
case SPL_OP_DIV:
return spl_ir_builder_arith_div(a->ir, t, l, r);
case SPL_OP_MOD:
return spl_ir_builder_arith_rem(a->ir, t, l, r);
case SPL_OP_BIT_AND:
return spl_ir_builder_arith_and(a->ir, t, l, r);
case SPL_OP_BIT_OR:
return spl_ir_builder_arith_or(a->ir, t, l, r);
case SPL_OP_BIT_XOR:
return spl_ir_builder_arith_xor(a->ir, t, l, r);
case SPL_OP_LSHIFT:
return spl_ir_builder_arith_shl(a->ir, t, l, r);
case SPL_OP_RSHIFT:
return spl_ir_builder_arith_shr(a->ir, t, l, r);
case SPL_OP_CMP_EQ:
case SPL_OP_CMP_NE:
case SPL_OP_CMP_LT:
case SPL_OP_CMP_LE:
case SPL_OP_CMP_GT:
case SPL_OP_CMP_GE: {
spl_ast_node_t *cmln = spl_ast_node(a->ast, n->binary.left);
spl_type_id_t op_tid = cmln ? node_ir(a, cmln) : 0;
switch (op) {
case SPL_OP_CMP_EQ:
return spl_ir_builder_cmp_eq(a->ir, op_tid, l, r);
case SPL_OP_CMP_NE:
return spl_ir_builder_cmp_ne(a->ir, op_tid, l, r);
case SPL_OP_CMP_LT:
return spl_ir_builder_cmp_lt(a->ir, op_tid, l, r);
case SPL_OP_CMP_LE:
return spl_ir_builder_cmp_le(a->ir, op_tid, l, r);
case SPL_OP_CMP_GT:
return spl_ir_builder_cmp_gt(a->ir, op_tid, l, r);
case SPL_OP_CMP_GE:
return spl_ir_builder_cmp_ge(a->ir, op_tid, l, r);
default:
return 0;
}
}
case SPL_OP_BOOL_OR: {
spl_ast_node_t *bmln = spl_ast_node(a->ast, n->binary.left);
spl_type_id_t op_tid = bmln ? node_ir(a, bmln) : 0;
return spl_ir_builder_arith_or(a->ir, op_tid, l, r);
}
case SPL_OP_BOOL_AND: {
spl_ast_node_t *bmln = spl_ast_node(a->ast, n->binary.left);
spl_type_id_t op_tid = bmln ? node_ir(a, bmln) : 0;
return spl_ir_builder_arith_and(a->ir, op_tid, l, r);
}
default:
return 0;
}
}
case SPL_AST_EXPR_CALL: {
spl_ast_node_t *callee = spl_ast_node(a->ast, n->call.callee);
if (callee && callee->kind == SPL_AST_EXPR_IDENT) {
spl_intrin_t in = spl_intrin_lookup_call(callee->ident.name);
if (in != SPL_INTRIN_NONE)
return ast2ir_intrin_call(a, n, in, &n->call.args);
}
spl_ir_node_ref_t fn = emit_expr(a, n->call.callee);
if (!fn)
return 0;
usize nargs = vec_size(n->call.args);
spl_ir_node_ref_vec_t arg_refs;
vec_init(arg_refs);
for (usize i = 0; i < nargs; ++i)
vec_push(arg_refs, emit_expr(a, vec_at(n->call.args, i)));
spl_type_id_t ret_t = node_ir(a, n);
spl_ir_node_ref_t call = spl_ir_builder_control_call(a->ir, ret_t, fn);
if (!call) {
vec_free(arg_refs);
return 0;
}
for (usize i = 0; i < vec_size(arg_refs); ++i)
spl_ir_builder_control_call_param(a->ir, call, vec_at(arg_refs, i));
vec_free(arg_refs);
return call;
}
case SPL_AST_EXPR_FIELD: {
spl_ast_node_t *obj = spl_ast_node(a->ast, n->field.obj);
spl_lang_type_id_t obj_lang = node_lang(a, obj);
spl_lang_type_node_t *obj_ln = spl_lang_type_node(a->sema->type, obj_lang);
if (obj_ln) {
spl_intrin_t in = spl_intrin_lookup_field(obj_ln->kind, n->field.field);
if (in != SPL_INTRIN_NONE)
return ast2ir_intrin_field(a, n, in);
}
if (n->resolved_def_id) {
spl_def_node_t *fd = spl_def_node(a->sema->type, n->resolved_def_id);
if (fd && fd->kind == SPL_DEF_FN) {
spl_ir_node_ref_t fn_ref = 0;
if (!map_get(a->def2ir_map, n->resolved_def_id, &fn_ref))
return 0;
return spl_ir_builder_type_const_fn(a->ir, node_ir(a, n),
(spl_ir_func_ref_t)fn_ref);
}
}
spl_ir_node_ref_t ptr = emit_lvalue(a, ref);
if (!ptr)
return 0;
return spl_ir_builder_mem_load(a->ir, node_ir(a, n), ptr);
}
case SPL_AST_EXPR_INDEX: {
spl_ir_node_ref_t ptr = emit_lvalue(a, ref);
if (!ptr)
return 0;
return spl_ir_builder_mem_load(a->ir, node_ir(a, n), ptr);
}
case SPL_AST_EXPR_CAST:
return emit_cast(a, n);
case SPL_AST_EXPR_TUPLE: {
spl_type_id_t t = node_ir(a, n);
usize nelems = vec_size(n->tuple.elems);
spl_ir_node_ref_vec_t field_refs;
vec_init(field_refs);
for (usize i = 0; i < nelems; ++i) {
spl_ir_node_ref_t f = emit_expr(a, vec_at(n->tuple.elems, i));
if (!f)
Panic("tuple element emit failed");
vec_push(field_refs, f);
}
spl_ir_node_ref_t node = spl_ir_builder_agg_construct(a->ir, t);
if (!node)
Panic("agg_construct failed for tuple");
for (usize i = 0; i < nelems; ++i)
spl_ir_builder_agg_construct_field(a->ir, node, vec_at(field_refs, i));
vec_free(field_refs);
return node;
}
case SPL_AST_EXPR_ARRAY_LIT: {
spl_type_id_t t = node_ir(a, n);
if (!t)
Panic("array literal has no type after sema");
usize nelems = vec_size(n->array_literal.elems);
spl_ir_node_ref_vec_t field_refs;
vec_init(field_refs);
for (usize i = 0; i < nelems; ++i) {
spl_ir_node_ref_t f = emit_expr(a, vec_at(n->array_literal.elems, i));
if (!f)
Panic("array element emit failed");
vec_push(field_refs, f);
}
spl_ir_node_ref_t node = spl_ir_builder_agg_construct(a->ir, t);
if (!node)
Panic("agg_construct failed for array literal");
for (usize i = 0; i < nelems; ++i)
spl_ir_builder_agg_construct_field(a->ir, node, vec_at(field_refs, i));
vec_free(field_refs);
return node;
}
case SPL_AST_EXPR_AGGREGATE_LIT: {
spl_type_id_t t = node_ir(a, n);
if (!t)
Panic("aggregate literal has no type after sema");
spl_type_node_t *tn = spl_type_node(&a->sema->type->ir, t);
if (!tn)
Panic("aggregate type node missing");
spl_type_node_kind_t tn_kind = tn->kind;
if (tn_kind == SPL_TYPE_ENUM) {
if (vec_size(n->aggregate_literal.fields) != 1)
Panic("enum literal must have exactly one variant");
spl_ast_node_t *item = spl_ast_node(a->ast, vec_at(n->aggregate_literal.fields, 0));
if (!item || item->kind != SPL_AST_STRUCT_INIT_ITEM)
Panic("invalid enum literal");
usize vidx = 0;
if (!spl_type_agg_member_idx(&a->sema->type->ir, t, item->struct_init_item.name, &vidx))
Panic("enum variant `%s` not found (sema should have caught this)",
item->struct_init_item.name);
spl_type_id_t tag_ir = tn->agg_type.tag_type;
spl_type_id_t payload_ir = vec_at(tn->agg_type.variants, vidx).type_id;
spl_ir_node_ref_t eptr = spl_ir_builder_mem_alloca(a->ir, t, const_usize(a, 1));
spl_ir_node_ref_t tptr =
spl_ir_builder_mem_field(a->ir, t, eptr, SPL_ENUM_TAG_FIELD_IDX);
spl_ir_node_ref_t tag_val = spl_ir_builder_type_const_int(a->ir, tag_ir, vidx);
spl_ir_builder_mem_store(a->ir, tag_ir, tptr, tag_val);
spl_ir_node_ref_t pptr = spl_ir_builder_mem_field(a->ir, t, eptr, vidx + 1);
spl_ir_node_ref_t payload = emit_expr(a, item->struct_init_item.expr);
if (!payload)
Panic("enum payload emit failed");
spl_ir_builder_mem_store(a->ir, payload_ir, pptr, payload);
return eptr;
}
usize nfields = vec_size(tn->agg_type.variants);
spl_type_id_vec_t field_tids;
vec_init(field_tids);
for (usize i = 0; i < nfields; ++i)
vec_push(field_tids, vec_at(tn->agg_type.variants, i).type_id);
spl_ir_node_ref_vec_t fields;
vec_init(fields);
for (usize i = 0; i < nfields; ++i)
vec_push(fields, 0);
for (usize i = 0; i < vec_size(n->aggregate_literal.fields); ++i) {
spl_ast_node_t *item = spl_ast_node(a->ast, vec_at(n->aggregate_literal.fields, i));
if (!item || item->kind != SPL_AST_STRUCT_INIT_ITEM)
continue;
usize idx = 0;
if (!spl_type_agg_member_idx(&a->sema->type->ir, t, item->struct_init_item.name, &idx))
Panic("aggregate field `%s` not found (sema should have caught this)",
item->struct_init_item.name);
spl_ir_node_ref_t f = emit_expr(a, item->struct_init_item.expr);
if (!f)
Panic("aggregate field emit failed");
vec_at(fields, idx) = f;
}
for (usize i = 0; i < nfields; ++i) {
if (!vec_at(fields, i)) {
spl_ir_node_ref_t f = emit_zero_value(a, vec_at(field_tids, i));
if (!f) {
vec_free(field_tids);
vec_free(fields);
Panic("cannot zero-init aggregate field #%zu", i);
}
vec_at(fields, i) = f;
}
}
spl_ir_node_ref_t node = spl_ir_builder_agg_construct(a->ir, t);
if (!node) {
vec_free(field_tids);
vec_free(fields);
Panic("agg_construct failed for aggregate literal");
}
for (usize i = 0; i < nfields; ++i) {
spl_ir_node_ref_t f = vec_at(fields, i);
if (!f) {
vec_free(field_tids);
vec_free(fields);
Panic("aggregate field #%zu missing (sema should have caught this)", i);
}
spl_ir_builder_agg_construct_field(a->ir, node, f);
}
vec_free(field_tids);
vec_free(fields);
return node;
}
case SPL_AST_EXPR_BLOCK: {
for (usize i = 0; i < vec_size(n->block.stmts); ++i)
emit_stmt(a, vec_at(n->block.stmts, i));
if (n->block.tail_expr)
return emit_expr(a, n->block.tail_expr);
return 0;
}
case SPL_AST_EXPR_IF:
case SPL_AST_EXPR_IFVAR:
return emit_if_like(a, ref);
case SPL_AST_EXPR_WHILE:
case SPL_AST_EXPR_LOOP:
return emit_loop_like(a, ref);
case SPL_AST_EXPR_SLICE: {
spl_ast_node_t *obj = spl_ast_node(a->ast, n->slice.obj);
spl_lang_type_id_t obj_lang = node_lang(a, obj);
spl_lang_type_node_t *obj_ln = spl_lang_type_node(a->sema->type, obj_lang);
if (!obj_ln)
return 0;
spl_lang_type_kind_t obj_kind = obj_ln->kind;
spl_lang_type_id_t obj_elem = obj_ln->extra.elem;
spl_type_id_t obj_ir = obj_ln->ir_type_id;
spl_type_id_t slice_ir = node_ir(a, n);
if (!slice_ir)
return 0;
spl_lang_type_id_t elem_lang = obj_elem;
spl_type_id_t elem_ir = to_ir_type(a, elem_lang);
spl_lang_type_id_t usize_lang = spl_lang_make_int(a->sema->type, sizeof(void *) * 8, 0);
spl_type_id_t usize_ir = to_ir_type(a, usize_lang);
spl_ir_node_ref_t base = 0;
if (obj_kind == SPL_LANG_TYPE_PTR) {
base = emit_expr(a, n->slice.obj);
} else if (obj_kind == SPL_LANG_TYPE_SLICE) {
spl_ir_node_ref_t slice_val = emit_expr(a, n->slice.obj);
if (!slice_val)
return 0;
spl_type_id_t ptr_ir = spl_type_builder_ptr(&a->sema->type->ir, elem_ir);
base = spl_ir_builder_agg_extract(a->ir, obj_ir, ptr_ir, SPL_SLICE_PTR_FIELD_IDX,
slice_val);
} else {
base = emit_lvalue(a, n->slice.obj);
}
if (!base)
return 0;
spl_ir_node_ref_t begin_v = 0;
if (n->slice.begin)
begin_v = emit_expr(a, n->slice.begin);
else
begin_v = spl_ir_builder_type_const_int(a->ir, usize_ir, 0);
if (!begin_v)
return 0;
spl_ir_node_ref_t ptr = spl_ir_builder_mem_offset(a->ir, elem_ir, base, begin_v);
if (!ptr)
return 0;
spl_ir_node_ref_t end_v = 0;
if (n->slice.end) {
end_v = emit_expr(a, n->slice.end);
} else if (obj_kind == SPL_LANG_TYPE_ARRAY) {
spl_type_node_t *arr = spl_type_node(&a->sema->type->ir, obj_ir);
if (!arr)
return 0;
usize arr_len = arr->array_type.len;
end_v = spl_ir_builder_type_const_int(a->ir, usize_ir, arr_len);
} else if (obj_kind == SPL_LANG_TYPE_SLICE) {
spl_ir_node_ref_t slice_val = emit_expr(a, n->slice.obj);
end_v = spl_ir_builder_agg_extract(a->ir, obj_ir, usize_ir, SPL_SLICE_LEN_FIELD_IDX,
slice_val);
} else {
return 0;
}
if (!end_v)
return 0;
spl_ir_node_ref_t len = spl_ir_builder_arith_sub(a->ir, usize_ir, end_v, begin_v);
spl_ir_node_ref_t node = spl_ir_builder_agg_construct(a->ir, slice_ir);
if (!node)
return 0;
spl_ir_builder_agg_construct_field(a->ir, node, ptr);
spl_ir_builder_agg_construct_field(a->ir, node, len);
return node;
}
case SPL_AST_EXPR_COMPTIME_CALL: {
spl_intrin_t in = spl_intrin_lookup_call(n->comptime_call.name);
if (in == SPL_INTRIN_NONE)
return 0;
return ast2ir_intrin_call(a, n, in, &n->comptime_call.args);
}
default:
TODO();
return 0;
}
}
static spl_ir_node_ref_t emit_if_like(spl_ast2ir_t *a, spl_ast_node_ref_t ref) {
spl_ast_node_t *n = spl_ast_node(a->ast, ref);
if (!n)
return 0;
bool is_ifvar = (n->kind == SPL_AST_EXPR_IFVAR);
spl_type_id_t result_t = node_ir(a, n);
spl_type_node_t *rtn = result_t ? spl_type_node(&a->sema->type->ir, result_t) : NULL;
bool need_result = rtn && rtn->kind != SPL_TYPE_UNIT;
spl_ir_node_ref_t result_slot = 0;
if (need_result)
result_slot = spl_ir_builder_mem_alloca(a->ir, result_t, const_usize(a, 1));
spl_ir_node_ref_t cond = 0;
spl_ast_node_ref_t then_ref = 0;
spl_ast_node_ref_t else_ref = 0;
if (is_ifvar) {
emit_decl(a, n->ifvar_expr.var_decl);
spl_ast_node_t *vd = spl_ast_node(a->ast, n->ifvar_expr.var_decl);
if (!vd || !vd->var_const_decl.init_expr)
Panic("ifvar requires initializer");
spl_ir_node_ref_t slot = 0;
if (!map_get(a->def2ir_map, vd->resolved_def_id, &slot))
Panic("ifvar var slot not found");
cond = spl_ir_builder_mem_load(a->ir, node_ir(a, vd), slot);
then_ref = n->ifvar_expr.then_block;
else_ref = n->ifvar_expr.else_branch;
} else {
cond = emit_expr(a, n->if_expr.cond);
then_ref = n->if_expr.then_block;
else_ref = n->if_expr.else_branch;
}
if (!cond)
Panic("if cond emit failed");
spl_ir_node_ref_t br = spl_ir_builder_control_br(a->ir, cond);
spl_ir_builder_block_end(a->ir);
usize then_blk = spl_ir_builder_block_begin(a->ir);
spl_ir_node_ref_t then_v = 0;
if (then_ref)
then_v = emit_expr(a, then_ref);
if (result_slot && then_v)
spl_ir_builder_mem_store(a->ir, result_t, result_slot, then_v);
spl_ir_node_ref_t then_end = 0;
if (!a->ir->terminated)
then_end = spl_ir_builder_control_jmp(a->ir);
spl_ir_builder_block_end(a->ir);
usize else_blk = spl_ir_builder_block_begin(a->ir);
spl_ir_node_ref_t else_v = 0;
if (else_ref)
else_v = emit_expr(a, else_ref);
if (result_slot && else_v)
spl_ir_builder_mem_store(a->ir, result_t, result_slot, else_v);
spl_ir_node_ref_t else_end = 0;
if (!a->ir->terminated)
else_end = spl_ir_builder_control_jmp(a->ir);
spl_ir_builder_block_end(a->ir);
usize merge_blk = spl_ir_builder_block_begin(a->ir);
spl_ir_builder_control_br_fixup(a->ir, br, then_blk, else_blk);
if (then_end)
spl_ir_builder_control_jmp_fixup(a->ir, then_end, merge_blk);
if (else_end)
spl_ir_builder_control_jmp_fixup(a->ir, else_end, merge_blk);
if (!result_slot)
return 0;
return spl_ir_builder_mem_load(a->ir, result_t, result_slot);
}
static spl_ir_node_ref_t emit_loop_like(spl_ast2ir_t *a, spl_ast_node_ref_t ref) {
spl_ast_node_t *n = spl_ast_node(a->ast, ref);
if (!n)
return 0;
bool is_while = (n->kind == SPL_AST_EXPR_WHILE);
spl_type_id_t result_t = node_ir(a, n);
spl_type_node_t *rtn = result_t ? spl_type_node(&a->sema->type->ir, result_t) : NULL;
bool need_result = rtn && rtn->kind != SPL_TYPE_UNIT;
spl_ir_node_ref_t result_slot = 0;
if (need_result)
result_slot = spl_ir_builder_mem_alloca(a->ir, result_t, const_usize(a, 1));
spl_ir_node_ref_t pre_jmp = 0;
if (!a->ir->terminated)
pre_jmp = spl_ir_builder_control_jmp(a->ir);
spl_ir_builder_block_end(a->ir);
usize head_blk = 0;
spl_ir_node_ref_t br = 0;
if (is_while) {
head_blk = spl_ir_builder_block_begin(a->ir);
spl_ir_node_ref_t cond = emit_expr(a, n->while_expr.cond);
if (!cond)
Panic("while cond emit failed");
br = spl_ir_builder_control_br(a->ir, cond);
spl_ir_builder_block_end(a->ir);
}
usize body_blk = spl_ir_builder_block_begin(a->ir);
usize cont_blk = is_while ? head_blk : body_blk;
spl_loop_ctx_t ctx = {0};
ctx.cont_blk = cont_blk;
ctx.label = NULL;
ctx.result_slot = result_slot;
ctx.result_tid = result_t;
spl_ast_node_ref_t label_ref = is_while ? n->while_expr.label : n->loop_expr.label;
if (label_ref) {
spl_ast_node_t *ln = spl_ast_node(a->ast, label_ref);
if (ln && ln->kind == SPL_AST_LABEL)
ctx.label = ln->label.name;
}
vec_init(ctx.breaks);
vec_push(a->loop_stack, ctx);
spl_ast_node_ref_t body_ref = is_while ? n->while_expr.body : n->loop_expr.body;
if (body_ref)
emit_expr(a, body_ref);
spl_ir_node_ref_t body_end = 0;
if (!a->ir->terminated)
body_end = spl_ir_builder_control_jmp(a->ir);
spl_ir_builder_block_end(a->ir);
usize after_blk = spl_ir_builder_block_begin(a->ir);
spl_ir_node_ref_t else_v = 0;
if (is_while && n->while_expr.else_branch)
else_v = emit_expr(a, n->while_expr.else_branch);
if (result_slot && else_v)
spl_ir_builder_mem_store(a->ir, result_t, result_slot, else_v);
if (pre_jmp)
spl_ir_builder_control_jmp_fixup(a->ir, pre_jmp, cont_blk);
if (br)
spl_ir_builder_control_br_fixup(a->ir, br, body_blk, after_blk);
if (body_end)
spl_ir_builder_control_jmp_fixup(a->ir, body_end, cont_blk);
spl_loop_ctx_t *top = &vec_at(a->loop_stack, vec_size(a->loop_stack) - 1);
for (usize i = 0; i < vec_size(top->breaks); ++i)
spl_ir_builder_control_jmp_fixup(a->ir, vec_at(top->breaks, i), after_blk);
vec_free(top->breaks);
vec_pop(a->loop_stack);
if (!result_slot)
return 0;
return spl_ir_builder_mem_load(a->ir, result_t, result_slot);
}
static void emit_break_stmt(spl_ast2ir_t *a, spl_ast_node_t *n) {
if (vec_size(a->loop_stack) == 0)
return;
const char *lbl = NULL;
if (n->statement_break.label) {
spl_ast_node_t *ln = spl_ast_node(a->ast, n->statement_break.label);
if (ln && ln->kind == SPL_AST_LABEL)
lbl = ln->label.name;
}
spl_loop_ctx_t *target = NULL;
if (lbl) {
for (usize i = vec_size(a->loop_stack); i > 0; --i) {
spl_loop_ctx_t *c = &vec_at(a->loop_stack, i - 1);
if (c->label && strcmp(c->label, lbl) == 0) {
target = c;
break;
}
}
} else {
target = &vec_at(a->loop_stack, vec_size(a->loop_stack) - 1);
}
if (!target)
return;
if (n->statement_break.expr && target->result_slot) {
spl_ir_node_ref_t bv = emit_expr(a, n->statement_break.expr);
if (bv)
spl_ir_builder_mem_store(a->ir, target->result_tid, target->result_slot, bv);
}
spl_ir_node_ref_t jmp = spl_ir_builder_control_jmp(a->ir);
vec_push(target->breaks, jmp);
}
static void emit_stmt(spl_ast2ir_t *a, spl_ast_node_ref_t ref) {
spl_ast_node_t *n = spl_ast_node(a->ast, ref);
if (!n)
return;
switch (n->kind) {
case SPL_AST_STATEMENT_RET: {
spl_type_id_t ret_ir = to_ir_type(a, a->cur_ret_lang);
spl_ir_node_ref_t v = 0;
if (n->statement_ret.expr)
v = emit_expr(a, n->statement_ret.expr);
while (vec_size(a->defer_stack) > a->cur_defer_base) {
spl_ast_node_ref_t d = vec_pop(a->defer_stack);
emit_stmt(a, d);
}
spl_ir_builder_control_ret(a->ir, ret_ir, v);
return;
}
case SPL_AST_STATEMENT_DEFER:
if (n->statement_defer.body)
vec_push(a->defer_stack, n->statement_defer.body);
return;
case SPL_AST_STATEMENT_COMPTIME:
if (n->statement_comptime.body)
emit_expr(a, n->statement_comptime.body);
return;
case SPL_AST_STATEMENT_DECL:
if (n->statement_decl.decl)
emit_decl(a, n->statement_decl.decl);
return;
case SPL_AST_STATEMENT_EXPR:
if (n->statement_expr.expr)
emit_expr(a, n->statement_expr.expr);
return;
case SPL_AST_EXPR_IF:
case SPL_AST_EXPR_IFVAR: {
(void)emit_if_like(a, ref);
return;
}
case SPL_AST_EXPR_WHILE:
case SPL_AST_EXPR_LOOP: {
(void)emit_loop_like(a, ref);
return;
}
case SPL_AST_STATEMENT_BREAK:
emit_break_stmt(a, n);
return;
case SPL_AST_STATEMENT_CONTINUE: {
if (vec_size(a->loop_stack) == 0)
return;
const char *lbl = NULL;
if (n->statement_continue.label) {
spl_ast_node_t *ln = spl_ast_node(a->ast, n->statement_continue.label);
if (ln && ln->kind == SPL_AST_LABEL)
lbl = ln->label.name;
}
spl_ir_node_ref_t jmp = spl_ir_builder_control_jmp(a->ir);
if (lbl) {
for (usize i = vec_size(a->loop_stack); i > 0; --i) {
spl_loop_ctx_t *c = &vec_at(a->loop_stack, i - 1);
if (c->label && strcmp(c->label, lbl) == 0) {
spl_ir_builder_control_jmp_fixup(a->ir, jmp, c->cont_blk);
return;
}
}
return;
}
usize cont = vec_at(a->loop_stack, vec_size(a->loop_stack) - 1).cont_blk;
spl_ir_builder_control_jmp_fixup(a->ir, jmp, cont);
return;
}
case SPL_AST_EXPR_BLOCK:
emit_expr(a, ref);
return;
default:
emit_expr(a, ref);
return;
}
}
static void emit_decl(spl_ast2ir_t *a, spl_ast_node_ref_t ref) {
spl_ast_node_t *n = spl_ast_node(a->ast, ref);
if (!n)
return;
switch (n->kind) {
case SPL_AST_VAR_DECL:
case SPL_AST_CONST_DECL: {
if (!a->ir->current_fn)
return;
if (!n->resolved_def_id)
return;
spl_ast_node_t *pat = spl_ast_node(a->ast, n->var_const_decl.pattern);
if (!pat || pat->kind != SPL_AST_PATTERN_IDENT)
return;
spl_lang_type_id_t var_lang = spl_def_get_lang_id(a->sema->type, n->resolved_def_id);
spl_type_id_t var_ir = to_ir_type(a, var_lang);
if (!var_ir)
return;
spl_ir_node_ref_t one = const_usize(a, 1);
spl_ir_node_ref_t slot = spl_ir_builder_mem_alloca(a->ir, var_ir, one);
map_put(a->def2ir_map, n->resolved_def_id, slot);
if (n->var_const_decl.init_expr) {
spl_ir_node_ref_t v = emit_expr(a, n->var_const_decl.init_expr);
if (v)
spl_ir_builder_mem_store(a->ir, var_ir, slot, v);
}
return;
}
default:
return;
}
}
static void emit_fn_reserve(spl_ast2ir_t *a, spl_ast_node_t *n) {
if (!n || !n->resolved_def_id)
return;
spl_lang_type_id_t fn_lang = spl_def_get_lang_id(a->sema->type, n->resolved_def_id);
spl_lang_type_node_t *fn_ln = spl_lang_type_node(a->sema->type, fn_lang);
if (!fn_ln || fn_ln->kind != SPL_LANG_TYPE_FN)
return;
spl_type_id_t fn_ir = fn_ln->ir_type_id;
if (!fn_ir)
return;
spl_ir_func_ref_t fn_ref = spl_ir_builder_fn_reserve(a->ir, n->fn_decl.name, fn_ir);
map_put(a->def2ir_map, n->resolved_def_id, (spl_ir_node_ref_t)fn_ref);
copy_attrs_to_ir_func(a, n, fn_ref);
}
static void emit_fn_body(spl_ast2ir_t *a, spl_ast_node_t *n) {
if (!n || !n->resolved_def_id)
return;
spl_ir_func_ref_t fn_ref = 0;
if (!map_get(a->def2ir_map, n->resolved_def_id, &fn_ref))
return;
spl_lang_type_id_t fn_lang = spl_def_get_lang_id(a->sema->type, n->resolved_def_id);
spl_lang_type_node_t *fn_ln = spl_lang_type_node(a->sema->type, fn_lang);
if (!fn_ln || fn_ln->kind != SPL_LANG_TYPE_FN)
return;
spl_lang_type_id_t ret_lang = fn_ln->extra.fn.ret;
spl_type_id_t ret_ir = to_ir_type(a, ret_lang);
spl_lang_type_node_t *ret_ln = spl_lang_type_node(a->sema->type, ret_lang);
int ret_is_unit = (ret_ln && ret_ln->kind == SPL_LANG_TYPE_UNIT) ? 1 : 0;
spl_ir_builder_fn_begin_at(a->ir, fn_ref);
spl_lang_type_id_t saved_ret = a->cur_ret_lang;
a->cur_ret_lang = ret_lang;
usize saved_defer_base = a->cur_defer_base;
a->cur_defer_base = vec_size(a->defer_stack);
usize defer_base = a->cur_defer_base;
usize loop_base = vec_size(a->loop_stack);
spl_ir_builder_block_begin(a->ir);
for (usize i = 0; i < vec_size(n->fn_decl.params); ++i) {
spl_ast_node_ref_t pref = vec_at(n->fn_decl.params, i);
spl_ast_node_t *pd = spl_ast_node(a->ast, pref);
if (!pd || pd->kind != SPL_AST_PARAM_DECL)
continue;
if (!pd->resolved_def_id)
continue;
spl_lang_type_id_t p_lang = spl_def_get_lang_id(a->sema->type, pd->resolved_def_id);
spl_type_id_t p_ir = to_ir_type(a, p_lang);
if (!p_ir)
continue;
spl_ir_node_ref_t idx = const_usize(a, i);
spl_ir_node_ref_t pv = spl_ir_builder_control_param(a->ir, p_ir, idx);
spl_ir_node_ref_t one = const_usize(a, 1);
spl_ir_node_ref_t slot = spl_ir_builder_mem_alloca(a->ir, p_ir, one);
spl_ir_builder_mem_store(a->ir, p_ir, slot, pv);
map_put(a->def2ir_map, pd->resolved_def_id, slot);
}
if (n->fn_decl.body)
emit_expr(a, n->fn_decl.body);
if (!a->ir->terminated) {
while (vec_size(a->defer_stack) > defer_base) {
spl_ast_node_ref_t d = vec_pop(a->defer_stack);
emit_stmt(a, d);
}
if (ret_is_unit) {
spl_ir_builder_control_ret(a->ir, ret_ir, 0);
} else if (ret_ir) {
spl_ir_node_ref_t zero = emit_zero_value(a, ret_ir);
spl_ir_builder_control_ret(a->ir, ret_ir, zero);
} else {
spl_ir_builder_control_unreachable(a->ir);
}
}
while (vec_size(a->defer_stack) > defer_base) {
spl_ast_node_ref_t d = vec_pop(a->defer_stack);
(void)d;
}
while (vec_size(a->loop_stack) > loop_base) {
spl_loop_ctx_t *top = &vec_at(a->loop_stack, vec_size(a->loop_stack) - 1);
vec_free(top->breaks);
vec_pop(a->loop_stack);
}
a->cur_ret_lang = saved_ret;
a->cur_defer_base = saved_defer_base;
spl_ir_builder_block_end(a->ir);
spl_ir_builder_fn_end(a->ir);
}
static void emit_aggregate_fns_reserve(spl_ast2ir_t *a, spl_ast_node_t *te) {
for (usize i = 0; i < vec_size(te->type_aggregate.members); ++i) {
spl_ast_node_t *mn = spl_ast_node(a->ast, vec_at(te->type_aggregate.members, i));
if (!mn)
continue;
if (mn->kind == SPL_AST_FN_DECL) {
emit_fn_reserve(a, mn);
} else if (mn->kind == SPL_AST_MEMBER_DECL && mn->member_decl.type_expr) {
spl_ast_node_t *mt = spl_ast_node(a->ast, mn->member_decl.type_expr);
if (mt && mt->kind == SPL_AST_TYPE_AGGREGATE)
emit_aggregate_fns_reserve(a, mt);
}
}
}
static void emit_aggregate_fns_body(spl_ast2ir_t *a, spl_ast_node_t *te) {
for (usize i = 0; i < vec_size(te->type_aggregate.members); ++i) {
spl_ast_node_t *mn = spl_ast_node(a->ast, vec_at(te->type_aggregate.members, i));
if (!mn)
continue;
if (mn->kind == SPL_AST_FN_DECL) {
emit_fn_body(a, mn);
} else if (mn->kind == SPL_AST_MEMBER_DECL && mn->member_decl.type_expr) {
spl_ast_node_t *mt = spl_ast_node(a->ast, mn->member_decl.type_expr);
if (mt && mt->kind == SPL_AST_TYPE_AGGREGATE)
emit_aggregate_fns_body(a, mt);
}
}
}
static void emit_container_reserve(spl_ast2ir_t *a, spl_ast_node_ref_t ref) {
spl_ast_node_t *n = spl_ast_node(a->ast, ref);
if (!n)
return;
if (n->kind == SPL_AST_TOP_LEVEL_BLOCK) {
if (n->top_level_block.block)
emit_container_reserve(a, n->top_level_block.block);
return;
}
if (n->kind != SPL_AST_CONTAINER_MEMBERS)
return;
for (usize i = 0; i < vec_size(n->container_members.declarations); ++i) {
spl_ast_node_ref_t dref = vec_at(n->container_members.declarations, i);
spl_ast_node_t *d = spl_ast_node(a->ast, dref);
if (!d)
continue;
switch (d->kind) {
case SPL_AST_FN_DECL:
emit_fn_reserve(a, d);
break;
case SPL_AST_TYPE_DECL: {
spl_ast_node_t *te = spl_ast_node(a->ast, d->type_decl.type_expr);
if (te && te->kind == SPL_AST_TYPE_AGGREGATE)
emit_aggregate_fns_reserve(a, te);
break;
}
case SPL_AST_TOP_LEVEL_BLOCK:
emit_container_reserve(a, dref);
break;
default:
break;
}
}
}
static void emit_container_body(spl_ast2ir_t *a, spl_ast_node_ref_t ref) {
spl_ast_node_t *n = spl_ast_node(a->ast, ref);
if (!n)
return;
if (n->kind == SPL_AST_TOP_LEVEL_BLOCK) {
if (n->top_level_block.block)
emit_container_body(a, n->top_level_block.block);
return;
}
if (n->kind != SPL_AST_CONTAINER_MEMBERS)
return;
for (usize i = 0; i < vec_size(n->container_members.declarations); ++i) {
spl_ast_node_ref_t dref = vec_at(n->container_members.declarations, i);
spl_ast_node_t *d = spl_ast_node(a->ast, dref);
if (!d)
continue;
switch (d->kind) {
case SPL_AST_FN_DECL:
emit_fn_body(a, d);
break;
case SPL_AST_TYPE_DECL: {
spl_ast_node_t *te = spl_ast_node(a->ast, d->type_decl.type_expr);
if (te && te->kind == SPL_AST_TYPE_AGGREGATE)
emit_aggregate_fns_body(a, te);
break;
}
case SPL_AST_TOP_LEVEL_BLOCK:
emit_container_body(a, dref);
break;
default:
break;
}
}
}
static spl_ir_node_ref_t emit_implicit_cast(spl_ast2ir_t *a, spl_ast_node_t *n, spl_type_id_t ft,
spl_type_id_t tt, spl_ir_node_ref_t v) {
if (!ft || !tt || ft == tt)
return v;
spl_type_node_t *fs = spl_type_node(&a->sema->type->ir, ft);
spl_type_node_t *ts = spl_type_node(&a->sema->type->ir, tt);
if (!fs || !ts)
return v;
if (fs->kind == SPL_TYPE_INT && ts->kind == SPL_TYPE_INT) {
if (fs->int_type.bits == ts->int_type.bits)
return spl_ir_builder_cast_bitcast(a->ir, ft, tt, v);
if (fs->int_type.bits > ts->int_type.bits)
return spl_ir_builder_cast_trunc(a->ir, ft, tt, v);
if (fs->int_type.is_signed)
return spl_ir_builder_cast_sext(a->ir, ft, tt, v);
return spl_ir_builder_cast_zext(a->ir, ft, tt, v);
}
if (fs->kind == SPL_TYPE_FLOAT && ts->kind == SPL_TYPE_FLOAT) {
if (fs->float_type.bits > ts->float_type.bits)
return spl_ir_builder_cast_ftrunc(a->ir, ft, tt, v);
return spl_ir_builder_cast_fext(a->ir, ft, tt, v);
}
if (fs->kind == SPL_TYPE_BOOL && ts->kind == SPL_TYPE_INT)
return spl_ir_builder_cast_bool2int(a->ir, ft, tt, v);
if (fs->kind == SPL_TYPE_PTR && ts->kind == SPL_TYPE_PTR)
return v;
if (fs->kind == SPL_TYPE_PTR && ts->kind == SPL_TYPE_INT)
return spl_ir_builder_cast_ptr2int(a->ir, ft, tt, v);
if (fs->kind == SPL_TYPE_INT && ts->kind == SPL_TYPE_PTR)
return spl_ir_builder_cast_int2ptr(a->ir, ft, tt, v);
spl_diag_fatal(a->diag, SPL_DIAG_KIND_IMPLICIT_CAST_FAIL, &n->dbg,
"implicit cast: cannot convert type#%zu -> type#%zu", ft, tt);
return 0;
}
static usize spl_int_map_hash(usize k) { return MAP_HASH_INT(k); }
static int spl_int_map_cmp(usize a, usize b) { return MAP_CMP_INT(a, b); }
void spl_ast2ir_init(spl_ast2ir_t *ast2ir, spl_ir_builder_t *ir, const spl_sema_t *sema) {
ast2ir->sema = sema;
ast2ir->ir = ir;
ast2ir->ast = sema->ast;
map_init(ast2ir->def2ir_map, spl_int_map_hash, spl_int_map_cmp);
vec_init(ast2ir->loop_stack);
vec_init(ast2ir->defer_stack);
ast2ir->cur_ret_lang = 0;
ast2ir->cur_defer_base = 0;
ast2ir->diag = NULL;
}
void spl_ast2ir_drop(spl_ast2ir_t *ast2ir) {
for (usize i = 0; i < vec_size(ast2ir->loop_stack); ++i)
vec_free(vec_at(ast2ir->loop_stack, i).breaks);
vec_free(ast2ir->loop_stack);
vec_free(ast2ir->defer_stack);
map_free(ast2ir->def2ir_map);
}
void spl_ast2ir_run(spl_ast2ir_t *ast2ir, spl_ast_ptr_vec_t ast_vec) {
usize n = vec_size(ast_vec);
for (usize i = 0; i < n; ++i) {
spl_ast_t *ast = vec_at(ast_vec, i);
if (!ast || !ast->root)
continue;
ast2ir->ast = ast;
spl_ast_node_t *root = spl_ast_node(ast, ast->root);
if (!root || root->kind != SPL_AST_ROOT)
continue;
emit_container_reserve(ast2ir, root->root.members);
}
for (usize i = 0; i < n; ++i) {
spl_ast_t *ast = vec_at(ast_vec, i);
if (!ast || !ast->root)
continue;
ast2ir->ast = ast;
spl_ast_node_t *root = spl_ast_node(ast, ast->root);
if (!root || root->kind != SPL_AST_ROOT)
continue;
emit_container_body(ast2ir, root->root.members);
}
vec_free(ast_vec);
}