/* ir_opt.c - IR 层优化 pass * * 通用函数内联(任意大小/形状的 callee,不再限叶子): * - callee 体内允许 control.call(嵌套调用)、分支/循环、多个返回、unit 返回; * - 参数:克隆体内 Param 的“使用处”替换为调用实参 ref; * - 控制流:克隆体追加到调用者末尾,内部 br/jmp/label 整体 +shift; * - 返回: * * 有返回值:在调用者追加 ret 槽(MemAlloca)与常量 1;每个 Ret 改为 * jmp 到“MemStore(槽, val) + jmp(join)”,join 处 MemLoad(槽) 供使用者; * * 无返回值(unit):每个 Ret 直接 jmp 到调用点之后。 * - 调用点改为 jmp 到克隆体入口;其使用者改指到 join 的 MemLoad(或 0)。 * - 防环:若 callee(传递)可达 caller,则拒绝内联,保证调用图保持无环、 * 内联过程终止(直接/间接递归都不会被内联)。 * - 迭代到不动点(无环调用图上轮数有上界)。 * 默认关闭(splc0 --opt 驱动),且只内联显式 `#inline` 标记的 callee。 */ #include "ir_opt.h" #include #include typedef struct { spl_ir_t *ir; spl_ir_func_ref_t caller; spl_ir_func_ref_t callee; spl_ir_node_ref_t shift; /* new = old + shift */ usize nparams; spl_ir_node_ref_t *param_ref; spl_ir_node_ref_t *param_arg; } inline_ctx_t; static spl_ir_node_t *nd(spl_ir_t *ir, spl_ir_func_ref_t fid, spl_ir_node_ref_t ref) { return spl_ir_node(ir, fid, ref); } /* ---- 引用重写(克隆体旧 ref -> 新 ref) ---- */ static spl_ir_node_ref_t rw(inline_ctx_t *c, spl_ir_node_ref_t r) { if (r == 0) return 0; for (usize i = 0; i < c->nparams; i++) if (c->param_ref[i] == r) return c->param_arg[i]; return r + c->shift; } static void rw_vec(inline_ctx_t *c, spl_ir_node_ref_vec_t *v) { for (usize i = 0; i < vec_size(*v); i++) vec_at(*v, i) = rw(c, vec_at(*v, i)); } static void rw_node(inline_ctx_t *c, spl_ir_node_t *n) { switch (n->kind) { case SPL_IR_ARITH_ADD: case SPL_IR_ARITH_SUB: case SPL_IR_ARITH_MUL: case SPL_IR_ARITH_DIV: case SPL_IR_ARITH_REM: case SPL_IR_ARITH_NEG: case SPL_IR_ARITH_ABS: case SPL_IR_ARITH_AND: case SPL_IR_ARITH_OR: case SPL_IR_ARITH_XOR: case SPL_IR_ARITH_SHL: case SPL_IR_ARITH_SHR: case SPL_IR_ARITH_NOT: n->arith.left = rw(c, n->arith.left); n->arith.right = rw(c, n->arith.right); break; case SPL_IR_CMP_EQ: case SPL_IR_CMP_NE: case SPL_IR_CMP_LT: case SPL_IR_CMP_LE: case SPL_IR_CMP_GT: case SPL_IR_CMP_GE: n->cmp.a = rw(c, n->cmp.a); n->cmp.b = rw(c, n->cmp.b); break; case SPL_IR_CAST_TRUNC: case SPL_IR_CAST_ZEXT: case SPL_IR_CAST_SEXT: case SPL_IR_CAST_FEXT: case SPL_IR_CAST_FTRUNC: case SPL_IR_CAST_BITCAST: case SPL_IR_CAST_PTR2INT: case SPL_IR_CAST_INT2PTR: case SPL_IR_CAST_BOOL2INT: case SPL_IR_CASE_INT2FLOAT: case SPL_IR_CASE_FLOAT2INT: n->cast.val = rw(c, n->cast.val); break; case SPL_IR_MEM_ALLOCA: n->mem_alloc.count = rw(c, n->mem_alloc.count); break; case SPL_IR_MEM_GLOBAL_ALLOC: n->mem_global_alloc.const_node = rw(c, n->mem_global_alloc.const_node); break; case SPL_IR_MEM_LOAD: n->mem_load.ptr = rw(c, n->mem_load.ptr); break; case SPL_IR_MEM_STORE: n->mem_store.ptr = rw(c, n->mem_store.ptr); n->mem_store.val = rw(c, n->mem_store.val); break; case SPL_IR_MEM_OFFSET: n->mem_offset.ptr = rw(c, n->mem_offset.ptr); n->mem_offset.offset = rw(c, n->mem_offset.offset); break; case SPL_IR_MEM_FIELD_PTR: n->mem_field_ptr.agg = rw(c, n->mem_field_ptr.agg); break; case SPL_IR_MEM_COPY: n->mem_copy.dst = rw(c, n->mem_copy.dst); n->mem_copy.src = rw(c, n->mem_copy.src); n->mem_copy.size = rw(c, n->mem_copy.size); break; case SPL_IR_MEM_SET: n->mem_set.dst = rw(c, n->mem_set.dst); n->mem_set.val = rw(c, n->mem_set.val); n->mem_set.size = rw(c, n->mem_set.size); break; case SPL_IR_TYPE_OFFSETOF: n->ir_offsetof.field_idx = rw(c, n->ir_offsetof.field_idx); break; case SPL_IR_AGG_CONSTRUCT: rw_vec(c, &n->agg_construct.fields); break; case SPL_IR_AGG_EXTRACT: n->agg_extract.val = rw(c, n->agg_extract.val); break; case SPL_IR_AGG_INSERT: n->agg_insert.agg = rw(c, n->agg_insert.agg); n->agg_insert.field = rw(c, n->agg_insert.field); break; case SPL_IR_CONTROL_SELECT: n->control_select.cond = rw(c, n->control_select.cond); n->control_select.true_val = rw(c, n->control_select.true_val); n->control_select.false_val = rw(c, n->control_select.false_val); break; case SPL_IR_CONTROL_BR: n->control_br.cond = rw(c, n->control_br.cond); n->control_br.true_label = rw(c, n->control_br.true_label); n->control_br.false_label = rw(c, n->control_br.false_label); break; case SPL_IR_CONTROL_JMP: n->control_jmp.label = rw(c, n->control_jmp.label); break; case SPL_IR_CONTROL_PARAM: n->control_param.idx = rw(c, n->control_param.idx); break; case SPL_IR_CONTROL_RET: n->control_ret.val = rw(c, n->control_ret.val); break; case SPL_IR_CONTROL_CALL: n->control_call.func = rw(c, n->control_call.func); rw_vec(c, &n->control_call.params); break; default: break; } } /* ---- 克隆 ---- */ static void clone_ref_vec(spl_ir_node_ref_vec_t *dst, const spl_ir_node_ref_vec_t *src) { dst->size = 0; dst->cap = 0; dst->data = NULL; usize n = vec_size(*src); if (n == 0) return; spl_ir_node_ref_t *p = (spl_ir_node_ref_t *)malloc(sizeof(spl_ir_node_ref_t) * n); if (!p) return; memcpy(p, src->data, sizeof(spl_ir_node_ref_t) * n); dst->data = p; dst->size = n; dst->cap = n; } static void clone_node(spl_ir_t *ir, spl_ir_func_ref_t caller, spl_ir_node_ref_t dst_ref, const spl_ir_node_t *src) { spl_ir_node_t *dst = nd(ir, caller, dst_ref); memcpy(dst, src, sizeof(*dst)); dst->attrs.data = NULL; dst->attrs.size = 0; dst->attrs.cap = 0; if (vec_size(src->attrs) != 0) clone_ref_vec((spl_ir_node_ref_vec_t *)&dst->attrs, (const spl_ir_node_ref_vec_t *)&src->attrs); if (src->kind == SPL_IR_AGG_CONSTRUCT) clone_ref_vec(&dst->agg_construct.fields, &src->agg_construct.fields); else if (src->kind == SPL_IR_CONTROL_CALL) clone_ref_vec(&dst->control_call.params, &src->control_call.params); } /* ---- 调用图可达性(防环) ---- */ static spl_ir_func_ref_t call_callee(spl_ir_t *ir, spl_ir_func_ref_t fid, spl_ir_node_t *call) { spl_ir_node_t *fn = nd(ir, fid, call->control_call.func); if (!fn || fn->kind != SPL_IR_TYPE_CONST) return 0; return fn->type_const.fn; } static bool reaches(spl_ir_t *ir, spl_ir_func_ref_t from, spl_ir_func_ref_t target, unsigned char *mark) { if (from == 0) return false; if (from == target) return true; if (mark[from]) return false; mark[from] = 1; spl_ir_func_t *f = spl_ir_func(ir, from); if (!f) return false; usize n = vec_size(f->nodes); for (usize i = 1; i < n; i++) { spl_ir_node_t *x = nd(ir, from, i); if (x && x->kind == SPL_IR_CONTROL_CALL) { spl_ir_func_ref_t c = call_callee(ir, from, x); if (c != 0 && reaches(ir, c, target, mark)) return true; } } return false; } /* 从 from 的直接被调者出发是否能到达 target(用于判断 from 自身是否递归/自可达)。 */ static bool reaches_succ(spl_ir_t *ir, spl_ir_func_ref_t from, spl_ir_func_ref_t target, unsigned char *mark) { spl_ir_func_t *f = spl_ir_func(ir, from); if (!f) return false; usize n = vec_size(f->nodes); for (usize i = 1; i < n; i++) { spl_ir_node_t *x = nd(ir, from, i); if (x && x->kind == SPL_IR_CONTROL_CALL) { spl_ir_func_ref_t c = call_callee(ir, from, x); if (c != 0 && reaches(ir, c, target, mark)) return true; } } return false; } /* ---- 内联一个调用点 ---- */ typedef struct { spl_ir_node_ref_t clone_ref; spl_ir_node_ref_t val; } ret_rec_t; static bool param_index(spl_ir_t *ir, spl_ir_func_ref_t fid, spl_ir_node_t *p, usize *out) { spl_ir_node_t *idx = nd(ir, fid, p->control_param.idx); if (!idx || idx->kind != SPL_IR_TYPE_CONST) return false; *out = idx->type_const.int_lit; return true; } static spl_ir_node_ref_t find_const_src(spl_ir_t *ir, spl_ir_func_ref_t fid) { spl_ir_func_t *f = spl_ir_func(ir, fid); if (!f) return 0; for (usize i = 1; i < vec_size(f->nodes); i++) { spl_ir_node_t *x = nd(ir, fid, i); if (x && x->kind == SPL_IR_TYPE_CONST) return i; } return 0; } static bool try_inline(spl_ir_t *ir, spl_ir_func_ref_t caller, spl_ir_node_ref_t ci, spl_ir_func_ref_t callee) { spl_ir_func_t *cf = spl_ir_func(ir, caller); spl_ir_func_t *calf = spl_ir_func(ir, callee); if (!cf || !calf || callee == caller) return false; spl_ir_node_t *call = nd(ir, caller, ci); if (!call || call->kind != SPL_IR_CONTROL_CALL) return false; usize orig_len = vec_size(cf->nodes); if (ci + 1 >= orig_len) return false; usize nargs = vec_size(call->control_call.params); /* 统计参数与返回,分配动态映射。 */ usize cnodes = vec_size(calf->nodes); usize in_params = 0; usize in_rets = 0; for (usize i = 1; i < cnodes; i++) { spl_ir_node_t *x = nd(ir, callee, i); if (!x) return false; if (x->kind == SPL_IR_CONTROL_PARAM) in_params++; else if (x->kind == SPL_IR_CONTROL_RET) in_rets++; } if (in_rets == 0 || in_params != nargs) return false; inline_ctx_t c; memset(&c, 0, sizeof(c)); c.ir = ir; c.caller = caller; c.callee = callee; c.nparams = in_params; c.param_ref = (spl_ir_node_ref_t *)malloc(sizeof(spl_ir_node_ref_t) * (in_params ? in_params : 1)); c.param_arg = (spl_ir_node_ref_t *)malloc(sizeof(spl_ir_node_ref_t) * (in_params ? in_params : 1)); ret_rec_t *recs = (ret_rec_t *)malloc(sizeof(ret_rec_t) * (in_rets ? in_rets : 1)); if (!c.param_ref || !c.param_arg || !recs) { free(c.param_ref); free(c.param_arg); free(recs); return false; } spl_type_id_t ret_tid = call->control_call.tid; /* 参数映射(旧 ref -> 实参 ref)。 */ usize pi = 0; for (usize i = 1; i < cnodes; i++) { spl_ir_node_t *x = nd(ir, callee, i); if (x && x->kind == SPL_IR_CONTROL_PARAM) { usize idx = 0; if (!param_index(ir, callee, x, &idx) || idx >= nargs) { free(c.param_ref); free(c.param_arg); free(recs); return false; } c.param_ref[pi] = i; c.param_arg[pi] = vec_at(call->control_call.params, idx); pi++; } } bool has_val = false; for (usize i = 1; i < cnodes; i++) { spl_ir_node_t *x = nd(ir, callee, i); if (x && x->kind == SPL_IR_CONTROL_RET && x->control_ret.val != 0) has_val = true; } spl_ir_node_ref_t const_src = find_const_src(ir, callee); if (has_val && const_src == 0) { free(c.param_ref); free(c.param_arg); free(recs); return false; } /* 释放调用点原 params(随后 ci 改为 jmp) */ vec_free(call->control_call.params); /* 克隆体入口前置:常量 1 + ret 槽 */ spl_ir_node_ref_t body_entry = 0; spl_ir_node_ref_t slot_ref = 0; if (has_val) { spl_ir_node_ref_t c_ref = spl_ir_alloc_node(ir, caller); clone_node(ir, caller, c_ref, nd(ir, callee, const_src)); spl_ir_node_t *cn = nd(ir, caller, c_ref); cn->kind = SPL_IR_TYPE_CONST; cn->type_const.int_lit = 1; slot_ref = spl_ir_alloc_node(ir, caller); spl_ir_node_t *sn = nd(ir, caller, slot_ref); memset(sn, 0, sizeof(*sn)); sn->kind = SPL_IR_MEM_ALLOCA; sn->mem_alloc.tid = ret_tid; sn->mem_alloc.count = c_ref; body_entry = c_ref; } c.shift = vec_size(cf->nodes) - 1; if (!has_val) body_entry = vec_size(cf->nodes); /* 克隆体 */ usize nrec = 0; for (usize i = 1; i < cnodes; i++) { spl_ir_node_ref_t dst_ref = spl_ir_alloc_node(ir, caller); if (dst_ref == 0) { free(c.param_ref); free(c.param_arg); free(recs); return false; } spl_ir_node_t *src = nd(ir, callee, i); clone_node(ir, caller, dst_ref, src); rw_node(&c, nd(ir, caller, dst_ref)); spl_ir_node_t *x = nd(ir, caller, dst_ref); /* 克隆进来的 Param 使用处已替换为实参,节点本身不能留在调用者里 * 改变其参数个数;退化为无害常量。 */ if (src->kind == SPL_IR_CONTROL_PARAM) { x->kind = SPL_IR_TYPE_CONST; x->type_const.tid = 0; x->type_const.int_lit = 0; continue; } if (x->kind == SPL_IR_CONTROL_RET) { recs[nrec].clone_ref = dst_ref; recs[nrec].val = x->control_ret.val; nrec++; x->kind = SPL_IR_CONTROL_JMP; x->control_jmp.label = 0; /* 稍后回填 */ } } spl_ir_node_ref_t ret_new = 0; if (has_val) { spl_ir_node_ref_t join = spl_ir_alloc_node(ir, caller); spl_ir_node_t *jn = nd(ir, caller, join); memset(jn, 0, sizeof(*jn)); jn->kind = SPL_IR_MEM_LOAD; jn->mem_load.tid = ret_tid; jn->mem_load.ptr = slot_ref; /* join 之后跳回调用点的后继(原代码此处靠 fallthrough)。 */ spl_ir_node_ref_t jc = spl_ir_alloc_node(ir, caller); spl_ir_node_t *jcn = nd(ir, caller, jc); memset(jcn, 0, sizeof(*jcn)); jcn->kind = SPL_IR_CONTROL_JMP; jcn->control_jmp.label = ci + 1; for (usize k = 0; k < nrec; k++) { spl_ir_node_ref_t st = spl_ir_alloc_node(ir, caller); spl_ir_node_t *stn = nd(ir, caller, st); memset(stn, 0, sizeof(*stn)); stn->kind = SPL_IR_MEM_STORE; stn->mem_store.tid = ret_tid; stn->mem_store.ptr = slot_ref; stn->mem_store.val = recs[k].val; spl_ir_node_ref_t jr = spl_ir_alloc_node(ir, caller); spl_ir_node_t *jrn = nd(ir, caller, jr); memset(jrn, 0, sizeof(*jrn)); jrn->kind = SPL_IR_CONTROL_JMP; jrn->control_jmp.label = join; spl_ir_node_t *rn = nd(ir, caller, recs[k].clone_ref); rn->control_jmp.label = st; } ret_new = join; } else { for (usize k = 0; k < nrec; k++) { spl_ir_node_t *rn = nd(ir, caller, recs[k].clone_ref); rn->control_jmp.label = ci + 1; } } /* 调用点 -> jmp 到克隆体入口 */ call = nd(ir, caller, ci); call->kind = SPL_IR_CONTROL_JMP; call->control_jmp.label = body_entry; /* 使用者替换:只把等于 ci 的引用改为 ret_new(不动其它 ref) */ for (usize j = 1; j < orig_len; j++) { spl_ir_node_t *x = nd(ir, caller, j); if (!x) continue; switch (x->kind) { case SPL_IR_ARITH_ADD: case SPL_IR_ARITH_SUB: case SPL_IR_ARITH_MUL: case SPL_IR_ARITH_DIV: case SPL_IR_ARITH_REM: case SPL_IR_ARITH_NEG: case SPL_IR_ARITH_ABS: case SPL_IR_ARITH_AND: case SPL_IR_ARITH_OR: case SPL_IR_ARITH_XOR: case SPL_IR_ARITH_SHL: case SPL_IR_ARITH_SHR: case SPL_IR_ARITH_NOT: if (x->arith.left == ci) x->arith.left = ret_new; if (x->arith.right == ci) x->arith.right = ret_new; break; case SPL_IR_CMP_EQ: case SPL_IR_CMP_NE: case SPL_IR_CMP_LT: case SPL_IR_CMP_LE: case SPL_IR_CMP_GT: case SPL_IR_CMP_GE: if (x->cmp.a == ci) x->cmp.a = ret_new; if (x->cmp.b == ci) x->cmp.b = ret_new; break; case SPL_IR_CAST_TRUNC: case SPL_IR_CAST_ZEXT: case SPL_IR_CAST_SEXT: case SPL_IR_CAST_FEXT: case SPL_IR_CAST_FTRUNC: case SPL_IR_CAST_BITCAST: case SPL_IR_CAST_PTR2INT: case SPL_IR_CAST_INT2PTR: case SPL_IR_CAST_BOOL2INT: case SPL_IR_CASE_INT2FLOAT: case SPL_IR_CASE_FLOAT2INT: if (x->cast.val == ci) x->cast.val = ret_new; break; case SPL_IR_MEM_ALLOCA: if (x->mem_alloc.count == ci) x->mem_alloc.count = ret_new; break; case SPL_IR_MEM_GLOBAL_ALLOC: if (x->mem_global_alloc.const_node == ci) x->mem_global_alloc.const_node = ret_new; break; case SPL_IR_MEM_LOAD: if (x->mem_load.ptr == ci) x->mem_load.ptr = ret_new; break; case SPL_IR_MEM_STORE: if (x->mem_store.ptr == ci) x->mem_store.ptr = ret_new; if (x->mem_store.val == ci) x->mem_store.val = ret_new; break; case SPL_IR_MEM_OFFSET: if (x->mem_offset.ptr == ci) x->mem_offset.ptr = ret_new; if (x->mem_offset.offset == ci) x->mem_offset.offset = ret_new; break; case SPL_IR_MEM_FIELD_PTR: if (x->mem_field_ptr.agg == ci) x->mem_field_ptr.agg = ret_new; break; case SPL_IR_MEM_COPY: if (x->mem_copy.dst == ci) x->mem_copy.dst = ret_new; if (x->mem_copy.src == ci) x->mem_copy.src = ret_new; if (x->mem_copy.size == ci) x->mem_copy.size = ret_new; break; case SPL_IR_MEM_SET: if (x->mem_set.dst == ci) x->mem_set.dst = ret_new; if (x->mem_set.val == ci) x->mem_set.val = ret_new; if (x->mem_set.size == ci) x->mem_set.size = ret_new; break; case SPL_IR_TYPE_OFFSETOF: if (x->ir_offsetof.field_idx == ci) x->ir_offsetof.field_idx = ret_new; break; case SPL_IR_AGG_CONSTRUCT: for (usize k = 0; k < vec_size(x->agg_construct.fields); k++) if (vec_at(x->agg_construct.fields, k) == ci) vec_at(x->agg_construct.fields, k) = ret_new; break; case SPL_IR_AGG_EXTRACT: if (x->agg_extract.val == ci) x->agg_extract.val = ret_new; break; case SPL_IR_AGG_INSERT: if (x->agg_insert.agg == ci) x->agg_insert.agg = ret_new; if (x->agg_insert.field == ci) x->agg_insert.field = ret_new; break; case SPL_IR_CONTROL_SELECT: if (x->control_select.cond == ci) x->control_select.cond = ret_new; if (x->control_select.true_val == ci) x->control_select.true_val = ret_new; if (x->control_select.false_val == ci) x->control_select.false_val = ret_new; break; case SPL_IR_CONTROL_BR: if (x->control_br.cond == ci) x->control_br.cond = ret_new; break; case SPL_IR_CONTROL_CALL: if (x->control_call.func == ci) x->control_call.func = ret_new; for (usize k = 0; k < vec_size(x->control_call.params); k++) if (vec_at(x->control_call.params, k) == ci) vec_at(x->control_call.params, k) = ret_new; break; case SPL_IR_CONTROL_PARAM: if (x->control_param.idx == ci) x->control_param.idx = ret_new; break; case SPL_IR_CONTROL_RET: if (x->control_ret.val == ci) x->control_ret.val = ret_new; break; default: break; } } free(c.param_ref); free(c.param_arg); free(recs); return true; } static bool func_has_attr(const spl_attr_t *attr, const spl_ir_func_t *f, const char *name); static int opt_function(spl_ir_t *ir, spl_ir_func_ref_t fid, const spl_attr_t *attr, unsigned char *mark, usize nfuncs) { spl_ir_func_t *f = spl_ir_func(ir, fid); if (!f) return 0; int done = 0; usize orig = vec_size(f->nodes); for (usize ci = 1; ci < orig; ci++) { spl_ir_node_t *x = nd(ir, fid, ci); if (!x || x->kind != SPL_IR_CONTROL_CALL) continue; spl_ir_func_ref_t callee = call_callee(ir, fid, x); if (callee == 0 || callee == fid || callee >= nfuncs) continue; spl_ir_func_t *calf = spl_ir_func(ir, callee); if (!func_has_attr(attr, calf, "inline") || func_has_attr(attr, calf, "noinline")) continue; /* extern 是占位体,绝不能内联。 */ if (func_has_attr(attr, calf, "extern")) continue; /* 防环:callee 传递可达 caller 时拒绝(覆盖直接/间接互递归)。 */ memset(mark, 0, nfuncs); if (reaches(ir, callee, fid, mark)) continue; /* 防爆炸:callee 自身递归时拒绝(否则不动点会反复内联其内部调用)。 */ memset(mark, 0, nfuncs); if (reaches_succ(ir, callee, callee, mark)) continue; if (try_inline(ir, fid, ci, callee)) done++; } return done; } /* 是否带有指定名字的属性/tag。 */ static bool func_has_attr(const spl_attr_t *attr, const spl_ir_func_t *f, const char *name) { if (!attr || !f) return false; for (usize i = 0; i < vec_size(f->attrs); ++i) { spl_attr_node_t *a = spl_attr_node((spl_attr_t *)attr, vec_at(f->attrs, i)); if (!a || !a->info) continue; if (strcmp(a->info->name, name) == 0) return true; } return false; } void spl_ir_opt(spl_ir_t *ir, const spl_attr_t *attr) { if (!ir) return; usize nfuncs = vec_size(ir->funcs); unsigned char *mark = (unsigned char *)malloc(nfuncs ? nfuncs : 1); if (!mark) return; /* 无环调用图上,最多 nfuncs 轮即可到不动点。 */ for (usize pass = 0; pass <= nfuncs; pass++) { int total = 0; usize n = vec_size(ir->funcs); for (spl_ir_func_ref_t fid = 1; fid < n && fid < nfuncs; fid++) total += opt_function(ir, fid, attr, mark, nfuncs); if (total == 0) break; } free(mark); }