/* 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 peephole.cc /// @brief Backend sliding-window peephole pass. #include "rcc/all.h" namespace rcc { namespace { // Store op to byte width. int store_size(int op) { switch (op) { case Ostoreb: return 1; case Ostoreh: return 2; case Ostorew: case Ostores: return 4; case Ostorel: case Ostored: return 8; default: return 0; } } // Check if store and load are compatible for forwarding. bool store_load_compatible(int sop, int lcls) { switch (sop) { case Ostorew: return lcls == Kw; case Ostorel: return lcls == Kl; case Ostores: return lcls == Ks; case Ostored: return lcls == Kd; default: return false; } } void peep_blk(Blk* b) { uint n = b->nins; if (n == 0) return; // Self Ocopy elimination. for (uint k = 0; k < n; k++) { Ins* i = &b->ins[k]; if (i->op == Ocopy && req(i->to, i->arg[0]) && rtype(i->to) != -1) i->op = Onop; } // Window 2 mode. auto next_live = [&](uint from) -> int { for (uint k = from; k < n; k++) if (b->ins[k].op != Onop) return static_cast(k); return -1; }; int p = next_live(0); while (p >= 0) { int q = next_live(static_cast(p) + 1); if (q < 0) break; Ins* pi = &b->ins[p]; Ins* qi = &b->ins[q]; // Store + Load to same slot: keep store, change load to copy. if (isstore(pi->op) && qi->op == Oload && rtype(pi->arg[1]) == RSlot && rtype(qi->arg[0]) == RSlot && req(pi->arg[1], qi->arg[0]) && store_load_compatible(pi->op, qi->cls) && rtype(pi->arg[0]) != RCon) { // Skip forwarding if value is a constant. qi->op = Ocopy; qi->arg[0] = pi->arg[0]; qi->arg[1] = Ref::none(); // Keep cls and to. p = q; continue; } // Consecutive stores to same slot: drop first. if (isstore(pi->op) && pi->op == qi->op && rtype(pi->arg[1]) == RSlot && rtype(qi->arg[1]) == RSlot && req(pi->arg[1], qi->arg[1]) && store_size(pi->op) > 0) { pi->op = Onop; p = q; continue; } p = q; } // Redundant Oextsw elimination. for (uint pidx = 0; pidx < n; pidx++) { Ins* pi = &b->ins[pidx]; if (pi->op != Oextsw) continue; if (!isreg(pi->to) || !isreg(pi->arg[0])) continue; for (uint qidx = pidx + 1; qidx < n; qidx++) { Ins* qi = &b->ins[qidx]; if (qi->op == Onop) continue; // Identical Oextsw. if (qi->op == pi->op && req(qi->to, pi->to) && req(qi->arg[0], pi->arg[0]) && req(qi->arg[1], pi->arg[1])) { qi->op = Onop; continue; } // Anchor invalidated. if (isreg(qi->to) && (req(qi->to, pi->to) || req(qi->to, pi->arg[0]))) break; } } } } // namespace void peephole(Ctx* /*ctx*/, Fn* fn) { for (Blk* b = fn->start; b; b = b->link) peep_blk(b); } } // namespace rcc