aboutsummaryrefslogtreecommitdiff
path: root/src/mid/licm.cc
diff options
context:
space:
mode:
authorRichard Qin <richard.qin.001@gmail.com>2026-09-15 08:16:43 +0800
committerRichard Qin <richard.qin.001@gmail.com>2026-09-15 08:16:43 +0800
commit4022e5c95210631e4aceb08eaf9d9071aa5aefc4 (patch)
treea5922559836e45fb3459a3d070367912e1035b1a /src/mid/licm.cc
downloadrcc-4022e5c95210631e4aceb08eaf9d9071aa5aefc4.tar.gz
rcc-4022e5c95210631e4aceb08eaf9d9071aa5aefc4.tar.xz
Set up a git repoHEADmaster
Diffstat (limited to 'src/mid/licm.cc')
-rw-r--r--src/mid/licm.cc213
1 files changed, 213 insertions, 0 deletions
diff --git a/src/mid/licm.cc b/src/mid/licm.cc
new file mode 100644
index 0000000..87d61a3
--- /dev/null
+++ b/src/mid/licm.cc
@@ -0,0 +1,213 @@
+/* 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
+<http://www.gnu.org/licenses/>. */
+
+/// @file licm.cc
+/// @brief Phase A Loop Invariant Code Motion (LICM).
+///
+/// See docs/licm.md for architectural notes.
+
+#include "rcc/all.h"
+
+#include <vector>
+
+namespace rcc {
+
+namespace {
+
+struct LoopInfo {
+ Blk* header;
+ std::vector<Blk*> body; // includes header
+ Blk* preheader; // nullptr if no unique non-back-edge predecessor
+};
+
+Ins make_nop() {
+ Ins i = {};
+ i.op = Onop;
+ return i;
+}
+
+// Loop Identification.
+void build_loops(Fn* fn, std::vector<LoopInfo>& loops, std::vector<int>& blk_loop) {
+ loops.clear();
+ blk_loop.assign(static_cast<size_t>(fn->nblk), -1);
+
+ for (Blk* b = fn->start; b; b = b->link)
+ b->visit = ~0u;
+
+ auto refine_inner = [&](uint bid, int lidx) {
+ int cur = blk_loop[bid];
+ if (cur < 0 || loops[static_cast<size_t>(cur)].header->id <
+ loops[static_cast<size_t>(lidx)].header->id)
+ blk_loop[bid] = lidx;
+ };
+
+ for (uint i = 0; i < fn->nblk; i++) {
+ Blk* hd = fn->rpo[i];
+ bool has_back = false;
+ for (uint k = 0; k < hd->npred; k++) {
+ if (hd->pred[k]->id >= i) {
+ has_back = true;
+ break;
+ }
+ }
+ if (!has_back)
+ continue;
+
+ int lidx = static_cast<int>(loops.size());
+ loops.push_back({hd, {hd}, nullptr});
+ refine_inner(hd->id, lidx);
+
+ // DFS body collect.
+ std::vector<Blk*> stack;
+ for (uint k = 0; k < hd->npred; k++)
+ if (hd->pred[k]->id >= i)
+ stack.push_back(hd->pred[k]);
+
+ while (!stack.empty()) {
+ Blk* n = stack.back();
+ stack.pop_back();
+ if (n->id < hd->id || n->visit == hd->id)
+ continue;
+ n->visit = hd->id;
+ loops[static_cast<size_t>(lidx)].body.push_back(n);
+ refine_inner(n->id, lidx);
+ for (uint k = 0; k < n->npred; k++)
+ stack.push_back(n->pred[k]);
+ }
+ }
+
+ // Find preheader.
+ for (auto& L : loops) {
+ Blk* phdr = nullptr;
+ bool unique = true;
+ for (uint k = 0; k < L.header->npred; k++) {
+ Blk* p = L.header->pred[k];
+ bool in_body = false;
+ for (Blk* b : L.body)
+ if (b == p) {
+ in_body = true;
+ break;
+ }
+ if (in_body)
+ continue; // back-edge
+ if (phdr) {
+ unique = false;
+ break;
+ }
+ phdr = p;
+ }
+ L.preheader = unique ? phdr : nullptr;
+ }
+}
+
+// Invariance
+bool blk_in(Blk* x, const std::vector<Blk*>& body) {
+ for (Blk* b : body)
+ if (b == x)
+ return true;
+ return false;
+}
+
+bool is_invariant(Fn* fn, Ref r, const LoopInfo& L) {
+ if (rtype(r) == RCon)
+ return true;
+ if (rtype(r) != RTmp)
+ return false;
+ Tmp* t = &fn->tmp[r.val];
+ if (t->bid == ~0u)
+ return true; // Undefined (treat as param/global-like) - equivalent to "outside loop"
+ return !blk_in(fn->rpo[t->bid], L.body);
+}
+
+// Check safety.
+bool clobbered_in_loop(Fn* fn, Ins* load_ins, Ref addr, int sz, const LoopInfo& L) {
+ for (Blk* b : L.body) {
+ for (Ins* i = b->ins; i < &b->ins[b->nins]; i++) {
+ if (i == load_ins)
+ continue;
+ if (i->op == Ocall)
+ return true; // Conservative: call is treated as writing all escapable memory
+ if (isstore(i->op)) {
+ int ssz = storesz(i);
+ int delta;
+ if (alias(addr, 0, sz, i->arg[1], ssz, &delta, fn) != NoAlias)
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+} // namespace
+
+void licm(Ctx* ctx, Fn* fn) {
+ fillloop(fn);
+
+ // Phase A hoisted load.
+ int total_hoisted = 0;
+ int iter = 0;
+ for (; iter < 16; iter++) {
+ std::vector<LoopInfo> loops;
+ std::vector<int> blk_loop;
+ build_loops(fn, loops, blk_loop);
+ if (loops.empty())
+ break;
+
+ int round_hoisted = 0;
+ for (Blk* b = fn->start; b; b = b->link) {
+ if (b->id >= blk_loop.size())
+ continue;
+ int lidx = blk_loop[b->id];
+ if (lidx < 0)
+ continue;
+ LoopInfo& L = loops[static_cast<size_t>(lidx)];
+ if (!L.preheader)
+ continue;
+
+ for (Ins* i = b->ins; i < &b->ins[b->nins]; i++) {
+ if (!isload(i->op))
+ continue;
+ if (!is_invariant(fn, i->arg[0], L))
+ continue;
+ int sz = loadsz(i);
+ if (clobbered_in_loop(fn, i, i->arg[0], sz, L))
+ continue;
+
+ // Hit: move to preheader.
+ addins(ctx, &L.preheader->ins, &L.preheader->nins, i);
+ *i = make_nop();
+ round_hoisted++;
+ }
+ }
+
+ total_hoisted += round_hoisted;
+ if (round_hoisted == 0)
+ break;
+ // Reset loop state.
+ filluse(ctx, fn);
+ }
+
+ if (debug['L']) {
+ FILE* out = ctx->dbg_buf ? ctx->dbg_buf : stderr;
+ fprintf(out, "\n> LICM hoisted %d load(s) in %s (in %d round%s)\n", total_hoisted, fn->name,
+ iter, iter == 1 ? "" : "s");
+ }
+}
+
+} // namespace rcc