Files
spl/stage1/spl_ast2ir.c
T

2135 lines
80 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#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);
/* Emit one exit path without consuming the lexical registrations needed by
* sibling paths. A private stack also allows cleanup bodies to register defer. */
static void emit_deferred_until(spl_ast2ir_t *a, usize base) {
if (vec_size(a->defer_stack) <= base)
return;
spl_defer_stack_t saved = a->defer_stack;
vec_init(a->defer_stack);
for (usize i = 0; i < vec_size(saved); ++i)
vec_push(a->defer_stack, vec_at(saved, i));
while (!a->ir->terminated && vec_size(a->defer_stack) > base) {
spl_ast_node_ref_t body = vec_pop(a->defer_stack);
emit_stmt(a, body);
}
vec_free(a->defer_stack);
a->defer_stack = saved;
}
static void emit_decl(spl_ast2ir_t *a, spl_ast_node_ref_t ref);
static spl_ir_node_ref_t emit_var_decl(spl_ast2ir_t *a, spl_ast_node_t *n);
static void emit_pattern_bind(spl_ast2ir_t *a, spl_ast_node_ref_t pat_ref,
spl_ir_node_ref_t src_ptr, spl_type_id_t tid);
static spl_ir_node_ref_t emit_match_check(spl_ast2ir_t *a, spl_ast_node_ref_t pat_ref,
spl_type_id_t scrut_ir, spl_ir_node_ref_t scrut_slot);
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;
}
}
/* 把 pattern 里的 ident 绑到 src_ptr 指向内存的对应位置。
- PATTERN_IDENT: def2ir_map[def_id] = src_ptr
- PATTERN_DOT: src_ptr 指向 enum,payload 在 mem_field(tid, src_ptr, vidx+1)
- PATTERN_TUPLE / PATTERN_AGGREGATE: 递归 mem_field
UB 语义:不做 tag 检查,调用者保证变体正确。
*/
static void emit_pattern_bind(spl_ast2ir_t *a, spl_ast_node_ref_t pat_ref,
spl_ir_node_ref_t src_ptr, spl_type_id_t tid) {
if (!pat_ref || !src_ptr)
return;
spl_ast_node_t *pat = spl_ast_node(a->ast, pat_ref);
if (!pat)
return;
switch (pat->kind) {
case SPL_AST_PATTERN_IDENT: {
if (pat->resolved_def_id)
map_put(a->def2ir_map, pat->resolved_def_id, src_ptr);
return;
}
case SPL_AST_EXPR_INT_LIT:
case SPL_AST_EXPR_FLOAT_LIT:
case SPL_AST_EXPR_CHAR_LIT:
case SPL_AST_EXPR_STRING_LIT:
case SPL_AST_EXPR_BOOL_LIT:
case SPL_AST_EXPR_NULL_LIT:
return; /* 字面量不绑定 */
case SPL_AST_PATTERN_UNDERSCORE:
return;
case SPL_AST_PATTERN_LIST:
for (usize i = 0; i < vec_size(pat->pattern_list.patterns); ++i)
emit_pattern_bind(a, vec_at(pat->pattern_list.patterns, i), src_ptr, tid);
return;
case SPL_AST_PATTERN_TUPLE: {
spl_type_node_t *tn = spl_type_node(&a->sema->type->ir, tid);
if (!tn || tn->kind != SPL_TYPE_STRUCT)
return;
usize n = vec_size(pat->pattern_tuple.patterns);
usize nf = vec_size(tn->agg_type.variants);
if (n > nf)
return;
for (usize i = 0; i < n; ++i) {
spl_type_id_t ft = vec_at(tn->agg_type.variants, i).type_id;
spl_ir_node_ref_t fp = spl_ir_builder_mem_field(a->ir, tid, src_ptr, i);
emit_pattern_bind(a, vec_at(pat->pattern_tuple.patterns, i), fp, ft);
}
return;
}
case SPL_AST_PATTERN_DOT: {
spl_type_node_t *tn = spl_type_node(&a->sema->type->ir, tid);
if (!tn || tn->kind != SPL_TYPE_ENUM)
return;
usize vidx = pat->resolved_def_id;
if (vidx == 0)
return;
vidx -= 1;
if (vidx >= vec_size(tn->agg_type.variants))
return;
spl_type_id_t payload_ir = vec_at(tn->agg_type.variants, vidx).type_id;
spl_ir_node_ref_t pptr = spl_ir_builder_mem_field(a->ir, tid, src_ptr, vidx + 1);
if (pat->pattern_dot.sub_pattern)
emit_pattern_bind(a, pat->pattern_dot.sub_pattern, pptr, payload_ir);
return;
}
case SPL_AST_PATTERN_AGGREGATE: {
spl_type_node_t *tn = spl_type_node(&a->sema->type->ir, tid);
if (!tn || (tn->kind != SPL_TYPE_STRUCT && tn->kind != SPL_TYPE_UNION))
return;
for (usize i = 0; i < vec_size(pat->pattern_aggregate.fields); ++i) {
spl_ast_node_t *f = spl_ast_node(a->ast, vec_at(pat->pattern_aggregate.fields, i));
if (!f || f->kind != SPL_AST_PATTERN_FIELD)
continue;
usize idx = f->resolved_def_id;
if (idx >= vec_size(tn->agg_type.variants))
continue;
spl_type_id_t ft = vec_at(tn->agg_type.variants, idx).type_id;
spl_ir_node_ref_t fp = spl_ir_builder_mem_field(a->ir, tid, src_ptr, idx);
if (f->pattern_field.pattern)
emit_pattern_bind(a, f->pattern_field.pattern, fp, ft);
}
return;
}
default:
return;
}
}
/* 计算 pattern 对 scrutinee 的匹配条件。
返回 0 表示无条件命中(IDENT / UNDERSCORE),否则返回 bool 节点。 */
static spl_ir_node_ref_t emit_match_check(spl_ast2ir_t *a, spl_ast_node_ref_t pat_ref,
spl_type_id_t scrut_ir, spl_ir_node_ref_t scrut_slot) {
spl_ast_node_t *pat = spl_ast_node(a->ast, pat_ref);
if (!pat)
return 0;
switch (pat->kind) {
case SPL_AST_PATTERN_UNDERSCORE:
case SPL_AST_PATTERN_IDENT:
return 0;
case SPL_AST_PATTERN_LIST: {
spl_lang_type_id_t bt = spl_lang_make_bool(a->sema->type);
spl_type_id_t bt_ir = to_ir_type(a, bt);
spl_ir_node_ref_t combined = 0;
for (usize i = 0; i < vec_size(pat->pattern_list.patterns); ++i) {
spl_ir_node_ref_t c =
emit_match_check(a, vec_at(pat->pattern_list.patterns, i), scrut_ir, scrut_slot);
if (!c)
return 0;
combined = combined ? spl_ir_builder_arith_or(a->ir, bt_ir, combined, c) : c;
}
return combined;
}
case SPL_AST_PATTERN_DOT: {
spl_type_node_t *tn = spl_type_node(&a->sema->type->ir, scrut_ir);
if (!tn || tn->kind != SPL_TYPE_ENUM)
return 0;
usize vidx = pat->resolved_def_id;
if (vidx == 0)
return 0;
vidx -= 1;
spl_type_id_t tag_ir = tn->agg_type.tag_type;
spl_ir_node_ref_t tag_ptr =
spl_ir_builder_mem_field(a->ir, scrut_ir, scrut_slot, SPL_ENUM_TAG_FIELD_IDX);
spl_ir_node_ref_t tag_val = spl_ir_builder_mem_load(a->ir, tag_ir, tag_ptr);
spl_ir_node_ref_t vidx_const = spl_ir_builder_type_const_int(a->ir, tag_ir, vidx);
return spl_ir_builder_cmp_eq(a->ir, tag_ir, tag_val, vidx_const);
}
case SPL_AST_EXPR_INT_LIT:
case SPL_AST_EXPR_FLOAT_LIT:
case SPL_AST_EXPR_CHAR_LIT:
case SPL_AST_EXPR_STRING_LIT:
case SPL_AST_EXPR_BOOL_LIT:
case SPL_AST_EXPR_NULL_LIT: {
spl_ir_node_ref_t val = emit_expr(a, pat_ref);
if (!val)
return 0;
spl_ir_node_ref_t loaded = spl_ir_builder_mem_load(a->ir, scrut_ir, scrut_slot);
return spl_ir_builder_cmp_eq(a->ir, scrut_ir, loaded, val);
}
default:
/* tuple / aggregate pattern 暂不作为 match 条件 */
return 0;
}
}
/* 分配 slot → store init 值 → bind pattern。
返回指向变量内存的指针(*tid),调用者可用于读取初值。 */
static spl_ir_node_ref_t emit_var_decl(spl_ast2ir_t *a, spl_ast_node_t *n) {
spl_type_id_t var_ir = node_ir(a, n);
if (!var_ir)
return 0;
spl_ir_node_ref_t slot = spl_ir_builder_mem_alloca(a->ir, var_ir, const_usize(a, 1));
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);
}
emit_pattern_bind(a, n->var_const_decl.pattern, slot, var_ir);
return slot;
}
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_BITSIZEOF: {
spl_ast_node_t *arg = spl_ast_node(a->ast, vec_at(*args, 0));
return spl_ir_builder_type_bitsizeof(a->ir, node_ir(a, arg));
}
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_ENUM2INT: {
/* enum 值 -> 其 tag(判别值);field 0 即 tag。 */
spl_ast_node_t *src = spl_ast_node(a->ast, vec_at(*args, 0));
spl_ir_node_ref_t v = emit_expr(a, vec_at(*args, 0));
if (!v)
return 0;
spl_type_id_t agg_t = node_ir(a, src);
spl_type_id_t tag_t = node_ir(a, n);
if (!agg_t || !tag_t)
return 0;
return spl_ir_builder_agg_extract(a->ir, agg_t, tag_t, 0, v);
}
case SPL_INTRIN_FIELD_COUNT: {
spl_ast_node_t *arg = spl_ast_node(a->ast, vec_at(*args, 0));
return spl_ir_builder_type_field_count(a->ir, node_ir(a, arg));
}
case SPL_INTRIN_OFFSETOF: {
spl_ast_node_t *arg = spl_ast_node(a->ast, vec_at(*args, 0));
spl_type_id_t agg_t = node_ir(a, arg);
if (!agg_t)
return 0;
spl_ast_node_t *fnode = spl_ast_node(a->ast, vec_at(*args, 1));
usize idx = 0;
if (fnode && fnode->kind == SPL_AST_EXPR_INT_LIT) {
idx = (usize)fnode->int_lit.bits;
} else if (fnode && fnode->kind == SPL_AST_EXPR_IDENT) {
if (!spl_type_agg_member_idx(&a->sema->type->ir, agg_t, fnode->ident.name, &idx))
return 0;
} else {
return 0;
}
spl_ir_node_ref_t idx_node = const_usize(a, idx);
return spl_ir_builder_type_offsetof(a->ir, agg_t, idx_node);
}
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_FLOAT2INT:
case SPL_INTRIN_INT2FLOAT:
case SPL_INTRIN_BOOL2INT:
case SPL_INTRIN_PTR2INT:
case SPL_INTRIN_INT2PTR:
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;
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_FLOAT2INT:
return spl_ir_builder_case_float2int(a->ir, ft, tt, v);
case SPL_INTRIN_INT2FLOAT:
return spl_ir_builder_case_int2float(a->ir, ft, tt, v);
case SPL_INTRIN_BOOL2INT:
return spl_ir_builder_cast_bool2int(a->ir, ft, tt, v);
case SPL_INTRIN_PTR2INT:
return spl_ir_builder_cast_ptr2int(a->ir, ft, tt, v);
case SPL_INTRIN_INT2PTR:
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);
}
/* 字段访问的基对象是 rvalue(如函数调用结果)时的回退路径:
* 先求值整个聚合值,再用 agg.extract 取出字段值(无需左值/临时槽)。 */
static spl_ir_node_ref_t ast2ir_field_from_value(spl_ast2ir_t *a, spl_ast_node_t *n) {
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_type_id_t agg_ir = to_ir_type(a, obj_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;
usize field_idx = idx;
if (obj_ln->kind == SPL_LANG_TYPE_ENUM)
field_idx = idx + 1; /* enum: field 0 是 tag */
spl_type_id_t field_ir = 0;
spl_type_node_t *agg_tn = spl_type_node(&a->sema->type->ir, agg_ir);
if (agg_tn && field_idx < vec_size(agg_tn->agg_type.variants))
field_ir = vec_at(agg_tn->agg_type.variants, field_idx).type_id;
spl_ir_node_ref_t val = emit_expr(a, n->field.obj);
if (!val)
return 0;
return spl_ir_builder_agg_extract(a->ir, agg_ir, field_ir, field_idx, val);
}
/* 限定枚举变体 Type.Variant:产出枚举常量(tag=变体下标;无载荷变体不带 payload)。 */
static spl_ir_node_ref_t ast2ir_enum_variant(spl_ast2ir_t *a, spl_ast_node_t *n) {
spl_type_id_t t = node_ir(a, n);
if (!t)
return 0;
spl_type_node_t *tn = spl_type_node(&a->sema->type->ir, t);
if (!tn)
return 0;
usize vidx = 0;
if (!spl_type_agg_member_idx(&a->sema->type->ir, t, n->field.field, &vidx))
return 0;
spl_type_id_t tag_ir = tn->agg_type.tag_type;
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);
if (vidx < vec_size(tn->agg_type.variants)) {
spl_type_id_t payload_ir = vec_at(tn->agg_type.variants, vidx).type_id;
if (payload_ir) {
spl_ir_node_ref_t pptr = spl_ir_builder_mem_field(a->ir, t, eptr, vidx + 1);
spl_ir_node_ref_t zero = emit_zero_value(a, payload_ir);
spl_ir_builder_mem_store(a->ir, payload_ir, pptr, zero);
}
}
return eptr;
}
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;
}
if (op == SPL_OP_BOOL_OR || op == SPL_OP_BOOL_AND) {
spl_lang_type_id_t b_lang = spl_lang_make_bool(a->sema->type);
spl_type_id_t b_ir = to_ir_type(a, b_lang);
spl_ir_node_ref_t slot = spl_ir_builder_mem_alloca(a->ir, b_ir, const_usize(a, 1));
spl_ir_node_ref_t l = emit_expr(a, n->binary.left);
if (!l)
return 0;
spl_ir_builder_mem_store(a->ir, b_ir, slot, l);
spl_ir_node_ref_t br = spl_ir_builder_control_br(a->ir, l);
spl_ir_builder_block_end(a->ir);
usize rhs_blk = spl_ir_builder_block_begin(a->ir);
spl_ir_node_ref_t r = emit_expr(a, n->binary.right);
if (r)
spl_ir_builder_mem_store(a->ir, b_ir, slot, r);
/* 右侧可能以 ret / trap 终止,此时不能再补 jmp */
spl_ir_node_ref_t rhs_end = 0;
if (!a->ir->terminated)
rhs_end = spl_ir_builder_control_jmp(a->ir);
spl_ir_builder_block_end(a->ir);
usize end_blk = spl_ir_builder_block_begin(a->ir);
/* control.br: cond != 0 -> true_label。AND 左侧真才进 rhs,
OR 左侧假才进 rhs,所以 true/false 标签顺序相反。 */
if (op == SPL_OP_BOOL_AND)
spl_ir_builder_control_br_fixup(a->ir, br, rhs_blk, end_blk);
else
spl_ir_builder_control_br_fixup(a->ir, br, end_blk, rhs_blk);
if (rhs_end)
spl_ir_builder_control_jmp_fixup(a->ir, rhs_end, end_blk);
return spl_ir_builder_mem_load(a->ir, b_ir, slot);
}
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;
}
}
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);
}
}
/* 限定枚举变体 Type.Variant:基对象是聚合类型名且为 enum。 */
spl_ast_node_t *objn = spl_ast_node(a->ast, n->field.obj);
if (objn && objn->kind == SPL_AST_EXPR_IDENT) {
spl_def_node_t *od = spl_def_node(a->sema->type, objn->resolved_def_id);
spl_lang_type_id_t et = node_lang(a, objn);
spl_lang_type_node_t *etn = spl_lang_type_node(a->sema->type, et);
if (od && od->kind == SPL_DEF_AGG && etn && etn->kind == SPL_LANG_TYPE_ENUM)
return ast2ir_enum_variant(a, n);
}
spl_ir_node_ref_t ptr = emit_lvalue(a, ref);
if (ptr)
return spl_ir_builder_mem_load(a->ir, node_ir(a, n), ptr);
return ast2ir_field_from_value(a, n);
}
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);
/* [N]T -> []T:物化数组到局部存储,再构造 {ptr, len} 切片。 */
spl_lang_type_node_t *sln = spl_lang_type_node(a->sema->type, node_lang(a, n));
if (sln && sln->kind == SPL_LANG_TYPE_SLICE) {
spl_type_id_t elem_ir = to_ir_type(a, sln->extra.elem);
if (!elem_ir)
Panic("array literal slice element has no ir type");
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_type_id_t ptr_ir = spl_type_builder_ptr(&a->sema->type->ir, elem_ir);
spl_ir_node_ref_t base = 0;
if (nelems == 0) {
base = spl_ir_builder_type_const_ptr(a->ir, ptr_ir, NULL);
} else {
spl_type_id_t arr_ir =
spl_type_builder_array(&a->sema->type->ir, elem_ir, nelems);
spl_ir_node_ref_t slot =
spl_ir_builder_mem_alloca(a->ir, arr_ir, const_usize(a, 1));
for (usize i = 0; i < nelems; ++i) {
spl_ir_node_ref_t fptr =
spl_ir_builder_mem_field(a->ir, arr_ir, slot, i);
spl_ir_node_ref_t v = emit_expr(a, vec_at(n->array_literal.elems, i));
if (!v)
Panic("array element emit failed");
spl_ir_builder_mem_store(a->ir, elem_ir, fptr, v);
}
base = spl_ir_builder_mem_field(a->ir, arr_ir, slot, 0);
}
if (!base)
Panic("array literal slice base failed");
spl_ir_node_ref_t len = spl_ir_builder_type_const_int(a->ir, usize_ir, nelems);
spl_ir_node_ref_t node = spl_ir_builder_agg_construct(a->ir, t);
if (!node)
Panic("agg_construct failed for array-to-slice");
spl_ir_builder_agg_construct_field(a->ir, node, base);
spl_ir_builder_agg_construct_field(a->ir, node, len);
return node;
}
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_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);
if (item->struct_init_item.expr) {
spl_type_id_t payload_ir = vec_at(tn->agg_type.variants, vidx).type_id;
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: {
usize defer_base = vec_size(a->defer_stack);
for (usize i = 0; i < vec_size(n->block.stmts) && !a->ir->terminated; ++i)
emit_stmt(a, vec_at(n->block.stmts, i));
spl_ir_node_ref_t value = 0;
if (!a->ir->terminated && n->block.tail_expr)
value = emit_expr(a, n->block.tail_expr);
if (!a->ir->terminated)
emit_deferred_until(a, defer_base);
a->defer_stack.size = defer_base;
return value;
}
case SPL_AST_EXPR_IF:
case SPL_AST_EXPR_IFVAR:
return emit_if_like(a, ref);
case SPL_AST_EXPR_MATCH: {
spl_ast_node_ref_t matched_ref = n->match_expr.matched_expr;
spl_ast_node_t *matched_node = spl_ast_node(a->ast, matched_ref);
spl_lang_type_id_t matched_lang = node_lang(a, matched_node);
spl_type_id_t matched_ir = to_ir_type(a, matched_lang);
if (!matched_ir)
return 0;
spl_ir_node_ref_t scrut_slot =
spl_ir_builder_mem_alloca(a->ir, matched_ir, const_usize(a, 1));
spl_ir_node_ref_t scrut_val = emit_expr(a, matched_ref);
if (!scrut_val)
return 0;
spl_ir_builder_mem_store(a->ir, matched_ir, scrut_slot, scrut_val);
usize narms = vec_size(n->match_expr.arms);
spl_ir_node_ref_vec_t arm_branches;
spl_ir_block_vec_t cond_blks;
vec_init(arm_branches);
vec_init(cond_blks);
for (usize i = 0; i < narms; ++i) {
vec_push(cond_blks, a->ir->current_block);
spl_ast_node_t *arm = spl_ast_node(a->ast, vec_at(n->match_expr.arms, i));
if (arm && arm->kind == SPL_AST_MATCH_ARM) {
spl_ir_node_ref_t cond =
emit_match_check(a, arm->match_arm.pattern, matched_ir, scrut_slot);
if (cond) {
spl_ir_node_ref_t br = spl_ir_builder_control_br(a->ir, cond);
vec_push(arm_branches, br);
} else {
spl_ir_node_ref_t j = spl_ir_builder_control_jmp(a->ir);
vec_push(arm_branches, j);
}
} else {
spl_ir_node_ref_t j = spl_ir_builder_control_jmp(a->ir);
vec_push(arm_branches, j);
}
spl_ir_builder_block_end(a->ir);
if (i + 1 < narms)
spl_ir_builder_block_begin(a->ir);
}
usize no_match_blk = spl_ir_builder_block_begin(a->ir);
spl_ir_node_ref_t no_match_jmp = spl_ir_builder_control_jmp(a->ir);
spl_ir_builder_block_end(a->ir);
spl_ir_node_ref_vec_t body_jmps;
spl_ir_block_vec_t body_blks;
vec_init(body_jmps);
vec_init(body_blks);
for (usize i = 0; i < narms; ++i) {
usize blk = spl_ir_builder_block_begin(a->ir);
vec_push(body_blks, blk);
spl_ast_node_t *arm = spl_ast_node(a->ast, vec_at(n->match_expr.arms, i));
if (arm && arm->kind == SPL_AST_MATCH_ARM) {
emit_pattern_bind(a, arm->match_arm.pattern, scrut_slot, matched_ir);
if (arm->match_arm.body)
emit_expr(a, arm->match_arm.body);
}
spl_ir_node_ref_t j = 0;
if (!a->ir->terminated)
j = spl_ir_builder_control_jmp(a->ir);
vec_push(body_jmps, j);
spl_ir_builder_block_end(a->ir);
}
usize end_blk = spl_ir_builder_block_begin(a->ir);
for (usize i = 0; i < narms; ++i) {
spl_ir_node_ref_t branch = vec_at(arm_branches, i);
if (!branch)
continue;
spl_ir_node_t *bn = spl_ir_node(&a->ir->ir, a->ir->current_fn, branch);
if (!bn)
continue;
usize body_blk = vec_at(body_blks, i);
if (bn->kind == SPL_IR_CONTROL_BR) {
usize next_blk = (i + 1 < narms) ? vec_at(cond_blks, i + 1) : no_match_blk;
spl_ir_builder_control_br_fixup(a->ir, branch, body_blk, next_blk);
} else if (bn->kind == SPL_IR_CONTROL_JMP) {
spl_ir_builder_control_jmp_fixup(a->ir, branch, body_blk);
}
}
for (usize i = 0; i < narms; ++i) {
spl_ir_node_ref_t j = vec_at(body_jmps, i);
if (j)
spl_ir_builder_control_jmp_fixup(a->ir, j, end_blk);
}
spl_ir_builder_control_jmp_fixup(a->ir, no_match_jmp, end_blk);
vec_free(arm_branches);
vec_free(cond_blks);
vec_free(body_jmps);
vec_free(body_blks);
return 0;
}
case SPL_AST_EXPR_WHILE:
case SPL_AST_EXPR_LOOP:
return emit_loop_like(a, ref);
case SPL_AST_EXPR_FOR:
spl_diag_fatal(a->diag, SPL_DIAG_KIND_UNHANDLED_NODE, &n->dbg,
"for is not implemented yet");
return 0;
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) {
spl_ast_node_t *vd = spl_ast_node(a->ast, n->ifvar_expr.var_decl);
if (!vd)
Panic("ifvar missing var_decl");
spl_ir_node_ref_t slot = emit_var_decl(a, vd);
if (!slot)
Panic("ifvar var_decl emit failed");
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.defer_base = vec_size(a->defer_stack);
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);
(void)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;
}
usize target_index = vec_size(a->loop_stack);
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_index = i - 1;
break;
}
}
} else {
target_index = vec_size(a->loop_stack) - 1;
}
if (target_index == vec_size(a->loop_stack))
return;
/* Emitting a value or a cleanup can grow/reallocate loop_stack. */
spl_loop_ctx_t target = vec_at(a->loop_stack, target_index);
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);
}
emit_deferred_until(a, target.defer_base);
if (a->ir->terminated)
return;
spl_ir_node_ref_t jmp = spl_ir_builder_control_jmp(a->ir);
vec_push(vec_at(a->loop_stack, target_index).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);
emit_deferred_until(a, a->cur_defer_base);
if (!a->ir->terminated)
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;
}
usize target_index = vec_size(a->loop_stack) - 1;
if (lbl) {
target_index = vec_size(a->loop_stack);
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_index = i - 1;
break;
}
}
}
if (target_index == vec_size(a->loop_stack))
return;
spl_loop_ctx_t target = vec_at(a->loop_stack, target_index);
emit_deferred_until(a, target.defer_base);
if (!a->ir->terminated) {
spl_ir_node_ref_t jmp = spl_ir_builder_control_jmp(a->ir);
spl_ir_builder_control_jmp_fixup(a->ir, jmp, target.cont_blk);
}
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;
emit_var_decl(a, n);
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_ir_node_ref_t existing = 0;
if (map_get(a->def2ir_map, n->resolved_def_id, &existing)) {
spl_diag_error(a->diag, SPL_DIAG_KIND_INTERNAL, &n->dbg,
"fn `%s` reserved twice (def#%zu);", n->fn_decl.name, 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)
emit_deferred_until(a, defer_base);
if (!a->ir->terminated) {
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);
}
}
a->defer_stack.size = defer_base;
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);
(void)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 *tpat = spl_ast_node(a->ast, d->type_decl.pattern);
if (!tpat || tpat->kind != SPL_AST_PATTERN_IDENT)
break;
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 *tpat = spl_ast_node(a->ast, d->type_decl.pattern);
if (!tpat || tpat->kind != SPL_AST_PATTERN_IDENT)
break;
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);
bool fs_ptr_like = fs->kind == SPL_TYPE_PTR || fs->kind == SPL_TYPE_FN;
bool ts_ptr_like = ts->kind == SPL_TYPE_PTR || ts->kind == SPL_TYPE_FN;
if (fs_ptr_like && ts_ptr_like)
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, spl_layout_t *layout,
const spl_sema_t *sema) {
ast2ir->sema = sema;
ast2ir->ir = ir;
ast2ir->ast = sema->ast;
ast2ir->layout = layout;
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);
}