/* Source code for RCC-Legacy. Copyright (C) 2026 Richard Qin This file is part of RCC. RCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. RCC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with RCC; see the file LICENSE. If not see . */ /// @file live.cc /// @brief Backward dataflow liveness analysis. /// /// See docs/live.md for architectural notes. #include "rcc/all.h" namespace rcc { void liveon(BSet* v, Blk* b, Blk* s) { bscopy(v, s->in); // s's phi-defs. for (Phi* p = s->phi; p; p = p->link) if (rtype(p->to) == RTmp) bsclr(v, p->to.val); // phi-arg edge check. for (Phi* p = s->phi; p; p = p->link) { for (uint n = 0; n < p->narg; n++) { if (p->blk[n] != b) continue; if (rtype(p->arg[n]) != RTmp) continue; uint t = p->arg[n].val; bsset(v, t); bsset(b->gen, t); } } } namespace { /// Add ref (if tmp) to b->gen and b->in; on first entry to b->in, increment live count for its K /// bucket. inline void use_ref(Ref r, Blk* b, int nlv[2], Tmp* tmp) { if (rtype(r) != RTmp) return; uint t = r.val; bsset(b->gen, t); if (!bshas(b->in, t)) { nlv[KBASE(tmp[t].cls)]++; bsset(b->in, t); } } /// Get maximum of two elements. inline int imax(int a, int b) { return a > b ? a : b; } } // namespace void filllive(Ctx* ctx, Fn* fn) { BSet prev_out[1]; BSet edge_set[1]; bsinit(ctx, prev_out, static_cast(fn->ntmp)); bsinit(ctx, edge_set, static_cast(fn->ntmp)); // Initialize sets. for (Blk* b = fn->start; b; b = b->link) { bsinit(ctx, b->in, static_cast(fn->ntmp)); bsinit(ctx, b->out, static_cast(fn->ntmp)); bsinit(ctx, b->gen, static_cast(fn->ntmp)); } // Fixpoint loop. bool changed = true; while (changed) { changed = false; // Reverse rpo scan. for (int n = static_cast(fn->nblk) - 1; n >= 0; n--) { Blk* b = fn->rpo[n]; // Merge successors liveon. bscopy(prev_out, b->out); if (b->s1) { liveon(edge_set, b, b->s1); bsunion(b->out, edge_set); } if (b->s2) { liveon(edge_set, b, b->s2); bsunion(b->out, edge_set); } if (!bsequal(b->out, prev_out)) changed = true; // Initial in. int nlv[2] = {0, 0}; b->out->t[0] |= T.rglob; bscopy(b->in, b->out); for (int t = 0; bsiter(b->in, &t); t++) nlv[KBASE(fn->tmp[t].cls)]++; // Terminal jump args. if (rtype(b->jmp.arg) == RCall) { assert(static_cast(bscount(b->in)) == T.nrglob && b->in->t[0] == T.rglob); b->in->t[0] |= T.retregs(b->jmp.arg, nlv); } else { use_ref(b->jmp.arg, b, nlv, fn->tmp); } // Fused cmp+jnz check. use_ref(b->jmp.arg2, b, nlv, fn->tmp); // Initial nlive. for (int k = 0; k < 2; k++) b->nlive[k] = nlv[k]; // Reverse scan instructions. for (int idx = static_cast(b->nins) - 1; idx >= 0; idx--) { Ins* i = &b->ins[idx]; // Post-ABI lowering call. if (i->op == Ocall && rtype(i->arg[1]) == RCall) { int call_kill[2]; b->in->t[0] &= ~T.retregs(i->arg[1], call_kill); for (int k = 0; k < 2; k++) { nlv[k] -= call_kill[k]; // Midpoint call. nlv[k] += T.nrsave[k]; b->nlive[k] = imax(b->nlive[k], nlv[k]); } int call_arg[2]; b->in->t[0] |= T.argregs(i->arg[1], call_arg); for (int k = 0; k < 2; k++) { nlv[k] -= T.nrsave[k]; nlv[k] += call_arg[k]; } } // def kill. if (!req(i->to, Ref::none())) { assert(rtype(i->to) == RTmp); uint t = i->to.val; if (bshas(b->in, t)) nlv[KBASE(fn->tmp[t].cls)]--; bsset(b->gen, t); bsclr(b->in, t); } // args gen. for (int k = 0; k < 2; k++) { Ref a = i->arg[k]; if (rtype(a) == RMem) { Mem* m = &fn->mem[a.val]; use_ref(m->base, b, nlv, fn->tmp); use_ref(m->index, b, nlv, fn->tmp); } else { use_ref(a, b, nlv, fn->tmp); } } // Update peak. for (int k = 0; k < 2; k++) b->nlive[k] = imax(b->nlive[k], nlv[k]); } } } // Debug dump. if (debug['L']) { FILE* f = ctx->dbg_buf; fprintf(f, "\n> Liveness analysis:\n"); for (Blk* b = fn->start; b; b = b->link) { fprintf(f, "\t%-10sin: ", b->name); dumpts(b->in, fn->tmp, f); fprintf(f, "\t out: "); dumpts(b->out, fn->tmp, f); fprintf(f, "\t gen: "); dumpts(b->gen, fn->tmp, f); fprintf(f, "\t live: %d %d\n", b->nlive[0], b->nlive[1]); } } } } // namespace rcc