aboutsummaryrefslogtreecommitdiff
path: root/include
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 /include
downloadrcc-master.tar.gz
rcc-master.tar.xz
Set up a git repoHEADmaster
Diffstat (limited to 'include')
-rw-r--r--include/rcc/all.h826
-rw-r--r--include/rcc/ast.h277
-rw-r--r--include/rcc/error.h34
-rw-r--r--include/rcc/irgen.h192
-rw-r--r--include/rcc/loopinfo.h74
-rw-r--r--include/rcc/ops.h393
-rw-r--r--include/rcc/rv64.h155
-rw-r--r--include/rcc/symtab.h120
-rw-r--r--include/rcc/sysy_type.h40
9 files changed, 2111 insertions, 0 deletions
diff --git a/include/rcc/all.h b/include/rcc/all.h
new file mode 100644
index 0000000..f81acd1
--- /dev/null
+++ b/include/rcc/all.h
@@ -0,0 +1,826 @@
+/* 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/>. */
+
+#pragma once
+
+#include <array>
+#include <cassert>
+#include <climits>
+#include <cstdint>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <vector>
+
+namespace rcc {
+
+// Basic type aliases
+
+typedef unsigned char uchar;
+typedef unsigned int uint;
+typedef unsigned long ulong;
+typedef unsigned long long bits;
+
+// Forward declaration
+struct BSet;
+struct Ref;
+struct Op;
+struct Ins;
+struct Phi;
+struct Blk;
+struct Use;
+struct Sym;
+struct Num;
+struct Alias;
+struct Tmp;
+struct Con;
+struct Fn;
+struct Typ;
+struct Field;
+struct Dat;
+struct Lnk;
+struct Target;
+struct SubPool;
+struct Ctx;
+struct Insert; // load.cc
+struct Name; // ssa.cc
+struct Asmbits; // emit.cc
+typedef struct Addr Mem;
+
+enum {
+ NIns = 1 << 20,
+ NAlign = 3,
+ NField = 32,
+ NBit = CHAR_BIT * sizeof(bits),
+ NPm = 64, // Max copies for parallel move (rega)
+};
+
+struct Target {
+ char name[16];
+ char apple;
+ char windows;
+ int gpr0;
+ int ngpr;
+ int fpr0;
+ int nfpr;
+ bits rglob;
+ int nrglob;
+ int* rsave;
+ int nrsave[2];
+ bits (*retregs)(Ref, int[2]);
+ bits (*argregs)(Ref, int[2]);
+ int (*memargs)(int);
+ void (*abi0)(Fn*);
+ void (*abi1)(Ctx*, Fn*);
+ void (*isel)(Ctx*, Fn*);
+ void (*emitfn)(Fn*, FILE*);
+ void (*emitfin)(Ctx*, FILE*);
+ char asloc[4];
+ char assym[4];
+ uint cansel : 1;
+};
+
+#define BIT(n) (static_cast<bits>(1) << (n))
+
+enum {
+ RXX = 0,
+ Tmp0 = NBit,
+};
+
+struct BSet {
+ uint nt;
+ bits* t;
+};
+
+enum {
+ RTmp,
+ RCon,
+ RInt,
+ RType, /* last kind to come out of the parser */
+ RSlot,
+ RCall,
+ RMem,
+};
+
+struct Ref {
+ uint type : 3;
+ uint val : 29;
+
+ // Factory methods
+ static Ref none() { return {RTmp, 0}; }
+ static Ref undef() { return {RCon, 0}; }
+ static Ref con_z() { return {RCon, 1}; }
+ static Ref tmp(uint v) { return {RTmp, v}; }
+ static Ref con(uint v) { return {RCon, v}; }
+ static Ref slot(int v) { return {RSlot, static_cast<uint>(v) & 0x1fffffffu}; }
+ static Ref typ(uint v) { return {RType, v}; }
+ static Ref call(uint v) { return {RCall, v}; }
+ static Ref mem(uint v) { return {RMem, v}; }
+ static Ref integer(int v) { return {RInt, static_cast<uint>(v) & 0x1fffffffu}; }
+
+ bool operator==(Ref o) const { return type == o.type && val == o.val; }
+ bool operator!=(Ref o) const { return !(*this == o); }
+};
+
+static inline int req(Ref a, Ref b) {
+ return a.type == b.type && a.val == b.val;
+}
+
+static inline int rtype(Ref r) {
+ if (req(r, Ref::none()))
+ return -1;
+ return r.type;
+}
+
+static inline int rsval(Ref r) {
+ return (static_cast<int>(r.val) ^ 0x10000000) - 0x10000000;
+}
+
+static inline int isreg(Ref r) {
+ return rtype(r) == RTmp && r.val < Tmp0;
+}
+
+enum CmpI {
+ Cieq,
+ Cine,
+ Cisge,
+ Cisgt,
+ Cisle,
+ Cislt,
+ Ciuge,
+ Ciugt,
+ Ciule,
+ Ciult,
+ NCmpI,
+};
+
+enum CmpF {
+ Cfeq,
+ Cfge,
+ Cfgt,
+ Cfle,
+ Cflt,
+ Cfne,
+ Cfo,
+ Cfuo,
+ NCmpF,
+ NCmp = NCmpI + NCmpF,
+};
+
+enum { Kx = -1, Kw, Kl, Ks, Kd };
+
+#define KWIDE(k) ((k) & 1)
+#define KBASE(k) ((k) >> 1)
+
+#include "ops.h" // enum O { Oxxx, ..., NOp } + OpInfo / kops[] single source attribute table
+
+enum J {
+ Jxxx,
+#define JMPS(X) \
+ X(retw) \
+ X(retl) \
+ X(rets) \
+ X(retd) \
+ X(retsb) \
+ X(retub) \
+ X(retsh) \
+ X(retuh) \
+ X(retc) \
+ X(ret0) \
+ X(jmp) \
+ X(jnz) \
+ X(jfieq) \
+ X(jfine) \
+ X(jfisge) \
+ X(jfisgt) X(jfisle) X(jfislt) X(jfiuge) X(jfiugt) X(jfiule) X(jfiult) X(jffeq) X(jffge) \
+ X(jffgt) X(jffle) X(jfflt) X(jffne) X(jffo) X(jffuo) X(hlt)
+#define X(j) J##j,
+ JMPS(X)
+#undef X
+ NJmp
+};
+
+enum {
+ Ocmpw = Oceqw,
+ Ocmpw1 = Ocultw,
+ Ocmpl = Oceql,
+ Ocmpl1 = Ocultl,
+ Ocmps = Oceqs,
+ Ocmps1 = Ocuos,
+ Ocmpd = Oceqd,
+ Ocmpd1 = Ocuod,
+ Oalloc = Oalloc4,
+ Oalloc1 = Oalloc16,
+ Oflag = Oflagieq,
+ Oflag1 = Oflagfuo,
+ Oxsel = Oxselieq,
+ Oxsel1 = Oxselfuo,
+ NPubOp = Onop,
+ Jjf = Jjfieq,
+ Jjf1 = Jjffuo,
+};
+
+#define INRANGE(x, l, u) \
+ (static_cast<unsigned>(x) - static_cast<unsigned>(l) <= \
+ static_cast<unsigned>(u) - static_cast<unsigned>(l))
+#define isstore(o) INRANGE(o, Ostoreb, Ostored)
+#define isload(o) INRANGE(o, Oloadsb, Oload)
+#define isalloc(o) INRANGE(o, Oalloc4, Oalloc16)
+#define isext(o) INRANGE(o, Oextsb, Oextuw)
+#define ispar(o) INRANGE(o, Opar, Opare)
+#define isarg(o) INRANGE(o, Oarg, Oargv)
+#define isret(j) INRANGE(j, Jretw, Jret0)
+#define isparbh(o) INRANGE(o, Oparsb, Oparuh)
+#define isargbh(o) INRANGE(o, Oargsb, Oarguh)
+#define isretbh(j) INRANGE(j, Jretsb, Jretuh)
+#define isxsel(o) INRANGE(o, Oxsel, Oxsel1)
+
+struct Op {
+ char* name;
+ short argcls[2][4];
+ uint canfold : 1;
+ uint hasid : 1;
+ uint idval : 1;
+ uint commutes : 1;
+ uint assoc : 1;
+ uint idemp : 1;
+ uint cmpeqwl : 1;
+ uint cmplgtewl : 1;
+ uint eqval : 1;
+ uint pinned : 1;
+};
+
+struct Ins {
+ uint op : 30;
+ uint cls : 2;
+ Ref to;
+ Ref arg[2];
+};
+
+struct Phi {
+ Ref to;
+ short cls;
+ int visit;
+ uint narg;
+ Ref* arg;
+ Blk** blk;
+ Phi* link;
+};
+
+struct Blk {
+ Phi* phi;
+ Ins* ins;
+ uint nins;
+ struct {
+ short type;
+ short cls; // Jjfi* / Jjff* carried cmp operand cls; other jmp types are 0
+ Ref arg;
+ Ref arg2; // Used only by Jjfi* / Jjff* (carries 2nd cmp operand when fusing cmp+jnz)
+ } jmp;
+ Blk* s1;
+ Blk* s2;
+ Blk* link;
+
+ uint id;
+ uint visit;
+
+ Blk* idom;
+ Blk *dom, *dlink;
+ Blk** fron;
+ uint nfron;
+ int depth;
+
+ Blk** pred;
+ uint npred;
+ BSet in[1], out[1], gen[1];
+ int nlive[2];
+ int loop;
+ char* name;
+};
+
+struct Use {
+ enum {
+ UXXX,
+ UPhi,
+ UIns,
+ UJmp,
+ } type;
+ uint bid;
+ union {
+ Ins* ins;
+ Phi* phi;
+ } u;
+};
+
+struct Sym {
+ enum {
+ SGlo = 0,
+ SThr = 1,
+ SExt = 2,
+ SExtThr = SExt | SThr,
+ } type;
+ uint32_t id;
+};
+
+struct Num {
+ uchar n;
+ uchar nl, nr;
+ Ref l, r;
+};
+
+enum { NoAlias, MayAlias, MustAlias };
+
+struct Alias {
+ enum {
+ ABot = 0,
+ ALoc = 1,
+ ACon = 2,
+ AEsc = 3,
+ ASym = 4,
+ AUnk = 6,
+#define astack(t) ((t) & 1)
+ } type;
+ int base;
+ int64_t offset;
+ // True only for ASym path: Oadd(ASym, AUnk) preserves symbol identity, but offset is
+ // unreliable. Makes alias() degrade to MayAlias on "same symbol / same base" branch, while
+ // "different ASym" can still NoAlias. Not propagated to ALoc/AEsc: stack slot offset is
+ // controlled by IRGen and always known.
+ bool offset_unknown;
+ union {
+ Sym sym;
+ struct {
+ int sz;
+ bits m;
+ } loc;
+ } u;
+ Alias* slot;
+};
+
+struct Tmp {
+ char* name;
+ Ins* def;
+ Use* use;
+ uint ndef, nuse;
+ uint bid;
+ uint cost;
+ int slot;
+ short cls;
+ struct {
+ int r;
+ int w;
+ bits m;
+ } hint;
+ int phi;
+ Alias alias;
+ enum { WFull, Wsb, Wub, Wsh, Wuh, Wsw, Wuw } width;
+ int visit;
+ uint gcmbid;
+};
+
+struct Con {
+ enum {
+ CUndef,
+ CBits,
+ CAddr,
+ } type;
+ Sym sym;
+ union {
+ int64_t i;
+ double d;
+ float s;
+ } bits;
+ char flt;
+};
+
+struct Addr {
+ Con offset;
+ Ref base;
+ Ref index;
+ int scale;
+};
+
+struct Lnk {
+ char export_; // 'export' is C++ reserved word (C++20 module)
+ char thread;
+ char common;
+ char align;
+ char* sec;
+ char* secf;
+};
+
+struct Fn {
+ Blk* start;
+ Tmp* tmp;
+ Con* con;
+ Mem* mem;
+ int ntmp;
+ int ncon;
+ int nmem;
+ uint nblk;
+ int retty;
+ Ref retr;
+ Blk** rpo;
+ bits reg;
+ int slot;
+ int salign;
+ char vararg;
+ char dynalloc;
+ char leaf;
+ char* name;
+ Lnk lnk;
+ SubPool* _subpool; // Memory pool ownership for this fn; parse attaches it when generating fn,
+ // worker calls subpool_free after processing
+};
+
+struct Field {
+ enum {
+ FEnd,
+ Fb,
+ Fh,
+ Fw,
+ Fl,
+ Fs,
+ Fd,
+ FPad,
+ FTyp,
+ } type;
+ uint len;
+};
+
+struct Typ {
+ char* name;
+ char isdark;
+ char isunion;
+ int align;
+ uint64_t size;
+ uint nunion;
+ Field (*fields)[NField + 1];
+};
+
+struct Dat {
+ enum { DStart, DEnd, DB, DH, DW, DL, DZ } type;
+ char* name;
+ Lnk* lnk;
+ union {
+ int64_t num;
+ double fltd;
+ float flts;
+ char* str;
+ struct {
+ char* name;
+ int64_t off;
+ } ref;
+ } u;
+ char isref;
+ char isstr;
+};
+
+extern Target T;
+extern char debug['Z' + 1];
+
+typedef enum {
+ PHeap,
+ PFn,
+} Pool;
+
+// SubPool / Ctx
+
+struct SubPool {
+ std::vector<void*> chunks; // Each chunk is a pointer from emalloc
+};
+
+void* subpool_alloc(SubPool*, size_t);
+void subpool_free(SubPool*);
+
+struct Ctx {
+ SubPool* subpool = nullptr;
+
+ Ins* insb = nullptr; // 8MB, emalloc during init(); freed during ~Ctx
+ Ins* curi = nullptr; // Reverse fill pointer, reset to insb + NIns during bind()
+
+ Fn* load_curf = nullptr;
+ uint load_inum = 0;
+ Insert* load_ilog = nullptr;
+ uint load_nlog = 0;
+
+ Name* ssa_namel = nullptr;
+
+ Ref gvn_con01[2]{};
+ Ins** gvn_tbl = nullptr;
+ uint gvn_tbln = 0;
+
+ bits rega_regu = 0;
+ Tmp* rega_tmp = nullptr;
+ Mem* rega_mem = nullptr;
+ struct {
+ Ref src, dst;
+ int cls;
+ } rega_pm[NPm]{};
+ int rega_npm = 0;
+ int rega_loop = 0;
+ uint rega_stmov = 0, rega_stblk = 0;
+
+ Asmbits* emit_stash = nullptr;
+
+ // die context: during spill, record "peak FPR live set when limit_one"; dumped during emit die
+ int spill_fp_live_peak = 0;
+
+ FILE* out_buf = nullptr;
+ FILE* dbg_buf = nullptr;
+
+ Fn* fn = nullptr;
+
+ void init(); // Allocate insb (8MB) and initial SubPool; other fields zero-initialized
+ void bind(Fn*); // Take over fn's SubPool, reset curi and per-Fn pass states
+ void release(); // Release current SubPool, clear states; do not release insb
+ ~Ctx(); // Release insb and remaining SubPool
+};
+
+extern Typ* typ;
+extern Op optab[NOp];
+
+// util.cc
+
+uint32_t hash(const char*);
+[[noreturn]] void die_(const char*, const char*, ...) __attribute__((format(printf, 2, 3)));
+
+// die-context utilities
+struct Ins;
+void set_die_ctx(Ctx*);
+const char* die_ctx_str(Ctx*, Ins* cur); // cur can be nullptr
+void* emalloc(size_t);
+void* alloc(Ctx*, size_t);
+void freeall(Ctx*);
+void* vnew(Ctx*, ulong, size_t, Pool);
+void vfree(void*);
+void vgrow(Ctx*, void*, ulong);
+
+/// Lazy-allocating vgrow
+inline void vensure(Ctx* ctx, void* vp, ulong len, size_t esz, Pool pl) {
+ void** pp = static_cast<void**>(vp);
+ if (*pp == nullptr)
+ *pp = vnew(ctx, len, esz, pl);
+ else
+ vgrow(ctx, vp, len);
+}
+void addins(Ctx*, Ins**, uint*, Ins*);
+void addbins(Ctx*, Ins**, uint*, Blk*);
+char* strf(Ctx*, Pool, const char*, ...) __attribute__((format(printf, 3, 4)));
+uint32_t intern(const char*);
+char* str(uint32_t);
+int argcls(Ins*, int);
+int iscmp(int, int*, int*);
+void igroup(Blk*, Ins*, Ins**, Ins**);
+void emit(Ctx*, int, int, Ref, Ref, Ref);
+void emiti(Ctx*, Ins);
+void idup(Ctx*, Blk*, Ins*, ulong);
+Ins* icpy(Ins*, Ins*, ulong);
+int cmpop(int);
+int cmpwlneg(int);
+int clsmerge(short*, short);
+int phicls(int, Tmp*);
+uint phiargn(Phi*, Blk*);
+Ref phiarg(Phi*, Blk*);
+Ref newtmp(Ctx*, const char*, int, Fn*);
+void chuse(Ctx*, Ref, int, Fn*);
+int symeq(Sym, Sym);
+Ref newcon(Ctx*, Con*, Fn*);
+Ref getcon(Ctx*, int64_t, Fn*);
+int addcon(Con*, Con*, int);
+int isconbits(Fn* fn, Ref r, int64_t* v);
+void salloc(Ctx*, Ref, Ref, Fn*);
+int lowest_bit(bits);
+void dumpts(BSet*, Tmp*, FILE*);
+void runmatch(uchar*, Num*, Ref, Ref*);
+
+void bsinit(Ctx*, BSet*, uint);
+void bszero(BSet*);
+uint bscount(BSet*);
+void bsset(BSet*, uint);
+void bsclr(BSet*, uint);
+void bscopy(BSet*, BSet*);
+void bsunion(BSet*, BSet*);
+void bsinter(BSet*, BSet*);
+void bsdiff(BSet*, BSet*);
+int bsequal(BSet*, BSet*);
+int bsiter(BSet*, int*);
+
+static inline int bshas(BSet* bs, uint elt) {
+ assert(elt < bs->nt * NBit);
+ return (bs->t[elt / NBit] & BIT(elt % NBit)) != 0;
+}
+
+// parse.cc
+
+void parse(FILE*, char*, void (*)(char*), void (*)(Dat*), void (*)(Fn*));
+void printfn(Fn*, FILE*);
+void printref(Ref, Fn*, FILE*);
+[[noreturn]] void err(char*, ...) __attribute__((format(printf, 1, 2)));
+
+// abi.cc
+
+void elimsb(Fn*);
+
+// cfg.cc - CFG analysis and simplification (Ctx*, Fn*) conventions
+
+Blk* newblk(Ctx*);
+void fillpreds(Ctx*, Fn*);
+void fillcfg(Ctx*, Fn*);
+void filldom(Fn*);
+int sdom(Blk*, Blk*);
+int dom(Blk*, Blk*);
+void fillfron(Ctx*, Fn*);
+void loopiter(Fn*, void (*)(Blk*, Blk*));
+void filldepth(Fn*);
+Blk* lca(Blk*, Blk*);
+void fillloop(Fn*);
+void simpljmp(Ctx*, Fn*);
+int reaches(Fn*, Blk*, Blk*);
+int reachesnotvia(Fn*, Blk*, Blk*, Blk*);
+int ifgraph(Blk*, Blk**, Blk**, Blk**);
+void simplcfg(Ctx*, Fn*);
+
+// mem.cc
+
+void promote(Ctx*, Fn*);
+void coalesce(Ctx*, Fn*);
+
+// alias.cc
+
+void fillalias(Fn*);
+void getalias(Alias*, Ref, Fn*);
+int alias(Ref, int, int, Ref, int, int*, Fn*);
+int escapes(Ref, Fn*);
+
+// load.cc
+
+int loadsz(Ins*);
+int storesz(Ins*);
+void loadopt(Ctx*, Fn*);
+
+// ssa.cc - Use chain population / SSA construction / SSA invariant verification
+
+void adduse_ins(Ctx*, Tmp*, Blk*, Ins*);
+void adduse_phi(Ctx*, Tmp*, Blk*, Phi*);
+void adduse_jmp(Ctx*, Tmp*, Blk*);
+void filluse(Ctx*, Fn*);
+void ssa(Ctx*, Fn*);
+void ssacheck(Fn*);
+void ssa_drain_namel(Ctx*); // Called by ~Ctx to drain emalloc nodes from ssa_namel free list
+
+// copy.cc
+
+void narrowpars(Ctx*, Fn*);
+Ref copyref(Ctx*, Fn*, Blk*, Ins*);
+Ref phicopyref(Ctx*, Fn*, Blk*, Phi*);
+
+// fold.cc
+
+int foldint(Con*, int, int, Con*, Con*);
+Ref foldref(Ctx*, Fn*, Ins*);
+
+// gvn.cc
+//
+
+int zeroval(Fn*, Blk*, Ref, int, int*);
+void gvn(Ctx*, Fn*);
+
+// gcm.cc
+
+int pinned(Ins*);
+void gcm(Ctx*, Fn*);
+
+// ifopt.cc
+
+void ifconvert(Ctx*, Fn*);
+
+// divopt.cc
+
+void divopt(Ctx*, Fn*);
+
+// mulopt.cc - Constant multiplication strength reduction (x * c -> <=3 shift/add/sub/neg).
+
+void mulopt(Ctx*, Fn*);
+
+// storemrg.cc - Merge adjacent same-base 4B-zero stores into 8B storel.
+
+void storemrg(Ctx*, Fn*);
+
+// licm.cc
+
+void licm(Ctx*, Fn*);
+
+// scalar_repl.cc - Hoist intra-loop same-address load+store accumulators to reg + phi.
+
+void scalar_repl(Ctx*, Fn*);
+
+// unroll.cc - Loop unrolling (Phase 1: Full unroll for constant trips).
+
+void unroll(Ctx*, Fn*);
+
+// tre.cc - Tail recursion elimination (Phase 1: Self-recursion + normalized tail call form).
+
+void tre(Ctx*, Fn*);
+
+// lsr.cc - Loop strength reduction (Phase 1: Constant basic induction variable * constant).
+
+void lsr(Ctx*, Fn*);
+
+// loop_idiom.cc - memset / memcpy / memmove loop idiom recognition -> Rewrite as Ocall to builtin
+// runtime.
+
+void loop_idiom(Ctx*, Fn*);
+
+// reduction_unroll.cc - Integer reduction loop K-way parallel accumulator unrolling, breaking
+// serial dependency chains.
+
+void reduction_unroll(Ctx*, Fn*);
+
+// loopinterchange.cc - Integer perfect 2-level nested loop interchange (row-major column access ->
+// row access).
+
+void loopinterchange(Ctx*, Fn*);
+
+// unswitch.cc - Loop unswitching (hoisting if (loop_invariant_cond) out of loop).
+
+void unswitch(Ctx*, Fn*);
+
+// sccp.cc — Sparse Conditional Constant Propagation(Wegman-Zadeck)。
+
+void sccp(Ctx*, Fn*);
+
+// reassoc.cc - Expression reassociation (rank-based, constant gathering + normalization + common
+// term elimination).
+
+void reassoc(Ctx*, Fn*);
+
+// dce.cc - Global dead code elimination (mark-and-sweep, roots = side-effect Ins + jmp.arg).
+
+void dce(Ctx*, Fn*);
+
+// dead_loop.cc - Dead loop elimination (no side-effects + provably terminating + no live-out loop).
+
+void dead_loop(Ctx*, Fn*);
+
+// inline.cc - Function inlining (leaf-only + fixpoint), whole-module perspective, called once after
+// IRGen and before run_midend.
+
+void inline_module(Ctx*, std::vector<Fn*>&);
+
+// simpl.cc
+
+void simpl(Ctx*, Fn*);
+
+// live.cc - Backward dataflow liveness analysis
+
+void liveon(BSet*, Blk*, Blk*);
+void filllive(Ctx*, Fn*);
+
+// spill.cc
+
+void fillcost(Ctx*, Fn*);
+void spill(Ctx*, Fn*);
+
+// rega.cc
+
+void rega(Ctx*, Fn*);
+
+// peephole.cc
+
+void peephole(Ctx*, Fn*);
+
+// emit.cc
+
+void emitfnlnk(char*, Lnk*, FILE*);
+void emitdat(Dat*, FILE*);
+void emitdbgfile(char*, FILE*);
+void emitdbgloc(uint, uint, FILE*);
+int stashbits(Ctx*, bits, int);
+void elf_emitfnfin(char*, FILE*);
+void elf_emitfin(Ctx*, FILE*);
+void macho_emitfin(Ctx*, FILE*);
+void pe_emitfin(Ctx*, FILE*);
+void rv64_emitfin(Ctx*, FILE*);
+
+// macro
+#define die(...) die_(__FILE__, __VA_ARGS__)
+#define MAKESURE(what, x) static_assert((x), #what)
+
+} // namespace rcc
diff --git a/include/rcc/ast.h b/include/rcc/ast.h
new file mode 100644
index 0000000..8f64962
--- /dev/null
+++ b/include/rcc/ast.h
@@ -0,0 +1,277 @@
+/* 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/>. */
+
+#pragma once
+
+/// @file ast.h
+/// @brief SysY AST Node Definitions
+
+#include "rcc/sysy_type.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace rcc {
+
+// Forward declarations of all AST nodes
+struct ASTNode;
+struct CompUnitNode;
+struct FuncDefNode;
+struct FuncFParamNode;
+struct VarDeclNode;
+struct VarDefNode;
+struct BlockNode;
+struct AssignStmtNode;
+struct ExprStmtNode;
+struct IfStmtNode;
+struct WhileStmtNode;
+struct BreakStmtNode;
+struct ContinueStmtNode;
+struct ReturnStmtNode;
+struct IntLiteralNode;
+struct FloatLiteralNode;
+struct LValNode;
+struct FuncCallNode;
+struct BinaryExprNode;
+struct UnaryExprNode;
+struct ImplicitCastNode;
+struct InitListNode;
+
+using ASTPtr = std::unique_ptr<ASTNode>;
+using ASTList = std::vector<ASTPtr>;
+
+// operators
+
+enum class BinOp {
+ Add,
+ Sub,
+ Mul,
+ Div,
+ Mod,
+ Lt,
+ Gt,
+ Le,
+ Ge,
+ Eq,
+ Ne,
+ And,
+ Or,
+};
+
+enum class UnaryOp { Pos, Neg, Not };
+
+// AST Node Kind
+
+enum class NodeKind {
+ CompUnit,
+ FuncDef,
+ FuncFParam,
+ VarDecl,
+ VarDef,
+ Block,
+ AssignStmt,
+ ExprStmt,
+ IfStmt,
+ WhileStmt,
+ BreakStmt,
+ ContinueStmt,
+ ReturnStmt,
+ IntLiteral,
+ FloatLiteral,
+ LVal,
+ FuncCall,
+ UnaryExpr,
+ BinaryExpr,
+ ImplicitCast,
+ InitList,
+};
+
+// AST Node Base Class
+
+struct ASTNode {
+ NodeKind kind;
+ BType eval_type = BType::Int;
+ int lineno = 0;
+
+ explicit ASTNode(NodeKind k) : kind(k) {}
+ virtual ~ASTNode() = default;
+
+ // no copy
+ ASTNode(const ASTNode&) = delete;
+ ASTNode& operator=(const ASTNode&) = delete;
+ ASTNode(ASTNode&&) = default;
+ ASTNode& operator=(ASTNode&&) = default;
+};
+
+// Top-level Nodes
+
+struct CompUnitNode : ASTNode {
+ ASTList decls_and_funcs;
+
+ CompUnitNode() : ASTNode(NodeKind::CompUnit) {}
+};
+
+struct FuncDefNode : ASTNode {
+ BType ret_type = BType::Void;
+ std::string name;
+ ASTList params; // FuncFParamNode
+ ASTPtr body; // BlockNode
+
+ FuncDefNode() : ASTNode(NodeKind::FuncDef) {}
+};
+
+struct FuncFParamNode : ASTNode {
+ BType btype = BType::Int;
+ std::string name;
+ bool is_array = false;
+ ASTList dims; // first dim is empty, others are ConstExp
+
+ FuncFParamNode() : ASTNode(NodeKind::FuncFParam) {}
+};
+
+// declarations
+
+struct VarDeclNode : ASTNode {
+ bool is_const = false;
+ BType btype = BType::Int;
+ ASTList defs; // VarDefNode
+
+ VarDeclNode() : ASTNode(NodeKind::VarDecl) {}
+};
+
+struct VarDefNode : ASTNode {
+ std::string name;
+ ASTList dims; // each dim size (ConstExp), scalar has empty dims list
+ ASTPtr init_val; // InitListNode | Expr | nullptr
+
+ VarDefNode() : ASTNode(NodeKind::VarDef) {}
+};
+
+struct InitListNode : ASTNode {
+ ASTList items; // Expr or InitListNode
+
+ InitListNode() : ASTNode(NodeKind::InitList) {}
+};
+
+// statements
+
+struct BlockNode : ASTNode {
+ ASTList items; // Decl | Stmt
+
+ BlockNode() : ASTNode(NodeKind::Block) {}
+};
+
+struct AssignStmtNode : ASTNode {
+ ASTPtr lval; // LValNode
+ ASTPtr expr;
+
+ AssignStmtNode() : ASTNode(NodeKind::AssignStmt) {}
+};
+
+struct ExprStmtNode : ASTNode {
+ ASTPtr expr; // nullable (empty statement)
+
+ ExprStmtNode() : ASTNode(NodeKind::ExprStmt) {}
+};
+
+struct IfStmtNode : ASTNode {
+ ASTPtr cond;
+ ASTPtr then_stmt;
+ ASTPtr else_stmt; // nullable
+
+ IfStmtNode() : ASTNode(NodeKind::IfStmt) {}
+};
+
+struct WhileStmtNode : ASTNode {
+ ASTPtr cond;
+ ASTPtr body;
+
+ WhileStmtNode() : ASTNode(NodeKind::WhileStmt) {}
+};
+
+struct BreakStmtNode : ASTNode {
+ BreakStmtNode() : ASTNode(NodeKind::BreakStmt) {}
+};
+
+struct ContinueStmtNode : ASTNode {
+ ContinueStmtNode() : ASTNode(NodeKind::ContinueStmt) {}
+};
+
+struct ReturnStmtNode : ASTNode {
+ ASTPtr expr; // nullable for void return
+
+ ReturnStmtNode() : ASTNode(NodeKind::ReturnStmt) {}
+};
+
+// expressions
+
+struct IntLiteralNode : ASTNode {
+ int32_t value = 0;
+
+ IntLiteralNode() : ASTNode(NodeKind::IntLiteral) { eval_type = BType::Int; }
+};
+
+struct FloatLiteralNode : ASTNode {
+ float value = 0.0f;
+
+ FloatLiteralNode() : ASTNode(NodeKind::FloatLiteral) { eval_type = BType::Float; }
+};
+
+struct LValNode : ASTNode {
+ std::string name;
+ ASTList indices; // index expressions
+
+ LValNode() : ASTNode(NodeKind::LVal) {}
+};
+
+struct FuncCallNode : ASTNode {
+ std::string name;
+ ASTList args;
+
+ FuncCallNode() : ASTNode(NodeKind::FuncCall) {}
+};
+
+struct BinaryExprNode : ASTNode {
+ BinOp op;
+ ASTPtr lhs, rhs;
+
+ BinaryExprNode() : ASTNode(NodeKind::BinaryExpr) {}
+};
+
+struct UnaryExprNode : ASTNode {
+ UnaryOp op;
+ ASTPtr operand;
+
+ UnaryExprNode() : ASTNode(NodeKind::UnaryExpr) {}
+};
+
+struct ImplicitCastNode : ASTNode {
+ BType target_type;
+ ASTPtr operand;
+
+ ImplicitCastNode() : ASTNode(NodeKind::ImplicitCast) {}
+};
+
+// AST utility functions
+
+/// Print AST (for debugging)
+void ast_dump(const ASTNode* node, int indent = 0);
+
+} // namespace rcc
diff --git a/include/rcc/error.h b/include/rcc/error.h
new file mode 100644
index 0000000..f831da0
--- /dev/null
+++ b/include/rcc/error.h
@@ -0,0 +1,34 @@
+/* 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/>. */
+
+#pragma once
+
+#include <string>
+
+namespace rcc {
+[[noreturn]] inline void fatal(const std::string& msg) {
+ fprintf(stderr, "rcc: error: %s\n", msg.c_str());
+ exit(1);
+}
+
+[[noreturn]] inline void fatal(int line, const std::string& msg) {
+ fprintf(stderr, "rcc: error (line %d): %s\n", line, msg.c_str());
+ exit(1);
+}
+} // namespace rcc \ No newline at end of file
diff --git a/include/rcc/irgen.h b/include/rcc/irgen.h
new file mode 100644
index 0000000..30d695c
--- /dev/null
+++ b/include/rcc/irgen.h
@@ -0,0 +1,192 @@
+/* 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/>. */
+
+#pragma once
+
+/// @file irgen.h
+/// @brief AST -> SSA IR (Braun Algorithm)
+///
+/// Produces Fn/Blk/Ins/Phi structures defined in all.h.
+/// Memory is managed via the injected Ctx (SubPool-backed).
+
+#include "rcc/all.h"
+#include "rcc/ast.h"
+#include "rcc/symtab.h"
+
+#include <string>
+#include <unordered_map>
+#include <variant>
+#include <vector>
+
+namespace rcc {
+
+/// Global variable data (for data segment emission)
+struct GlobalData {
+ std::string name;
+ int elem_cls = Kw; // Kw / Ks
+ size_t total_elems = 0;
+ bool is_const = false;
+ bool is_zeroinit = false;
+ std::vector<std::variant<int32_t, float>> init_vals;
+};
+
+/// IRGen output
+struct IROutput {
+ std::vector<Fn*> functions;
+ std::vector<GlobalData> globals;
+};
+
+/// Export global variables list as data segment syntax.
+void print_globals(const std::vector<GlobalData>& globals, FILE* f);
+
+class IRGen {
+ public:
+ explicit IRGen(Ctx* ctx) : ctx_(ctx) {}
+
+ /// Translate AST to IR
+ IROutput generate(CompUnitNode* root);
+
+ private:
+ Ctx* ctx_;
+ IROutput output_;
+ Fn* cur_fn_ = nullptr;
+ Blk* cur_blk_ = nullptr;
+ SymbolTable sym_;
+
+ // Current function's return K class: Kw / Ks (Kx=-1 for void).
+ // Do not reuse Fn::retty.
+ int cur_fn_ret_cls_ = Kx;
+
+ // Fn/Blk constructors
+
+ /// Create new function
+ Fn* new_fn(const char* name);
+
+ /// Create new basic block
+ Blk* new_blk(const char* name);
+
+ /// Get basic block (by id)
+ Blk* get_blk(uint id);
+
+ /// All blocks in current function (indexed by id)
+ std::vector<Blk*> blk_vec_;
+
+ // constant/Ref constructors
+
+ /// Create int constant Ref
+ Ref make_int_ref(int32_t val);
+ /// Create float constant Ref
+ Ref make_float_ref(float val);
+ /// Create global symbol RCon (CAddr).
+ /// For call target use only; data access should uniformly go through make_global_addr_ref.
+ Ref make_global_ref(const char* name);
+ /// Materialize global symbol into a Tmp (Oaddr Kl).
+ Ref make_global_addr_ref(const char* name);
+ /// Create new temporary
+ Ref new_tmp_ref(int cls);
+
+ // Braun SSA algorithm
+ //
+ // Index using Symbol::uid rather than variable name to prevent scope shadowing bugs.
+
+ std::unordered_map<int, std::unordered_map<uint, Ref>> current_def_;
+
+ struct IncompletePhi {
+ int var_uid;
+ Phi* phi;
+ uint block_id;
+ int cls;
+ };
+ std::vector<IncompletePhi> incomplete_phis_;
+
+ void write_variable(int uid, Blk* blk, Ref val);
+ Ref read_variable(int uid, Blk* blk, int cls);
+ Ref read_variable_recursive(int uid, Blk* blk, int cls);
+ void seal_block(Blk* blk);
+ void add_phi_operands(int uid, Blk* blk, Phi* phi, int cls);
+ Ref try_remove_trivial_phi(Phi* phi);
+
+ // Block sealing
+
+ std::unordered_map<uint, bool> sealed_;
+ bool is_sealed(Blk* b) const;
+
+ // Loop context
+
+ struct LoopCtx {
+ Blk* cond_block;
+ Blk* exit_block;
+ };
+ std::vector<LoopCtx> loop_stack_;
+
+ // AST traversal
+
+ void visit_comp_unit(CompUnitNode* node);
+ void visit_func_def(FuncDefNode* node);
+ void visit_global_decl(VarDeclNode* node);
+ void visit_local_decl(VarDeclNode* node);
+ void visit_stmt(ASTNode* node);
+ void visit_block(BlockNode* node);
+ void visit_if(IfStmtNode* node);
+ void visit_while(WhileStmtNode* node);
+ void visit_assign(AssignStmtNode* node);
+ void visit_return(ReturnStmtNode* node);
+
+ Ref visit_expr(ASTNode* node);
+
+ void visit_cond(ASTNode* cond, Blk* true_bb, Blk* false_bb);
+ void visit_cond_and(BinaryExprNode* node, Blk* true_bb, Blk* false_bb);
+ void visit_cond_or(BinaryExprNode* node, Blk* true_bb, Blk* false_bb);
+
+ // Instruction emission
+
+ Ref emit_ins(int op, int cls, Ref a0, Ref a1);
+ void emit_store(Ref val, Ref addr, int cls);
+ Ref emit_load(Ref addr, int cls);
+ /// IR-level zero-fill loop. Clears [addr, addr+size_bytes) entirely.
+ void emit_bulk_zero(Ref addr, int size_bytes);
+ void emit_jmp(Blk* target);
+ void emit_jnz(Ref cond, Blk* true_bb, Blk* false_bb);
+ void emit_ret(Ref val, int cls);
+ void emit_ret_void();
+ void push_ins(Ins i);
+ bool cur_blk_terminated() const;
+ void add_edge(Blk* from, Blk* to);
+
+ // Helpers
+
+ Ref compute_array_addr(LValNode* node);
+ Ref get_var_addr_ref(Symbol* sym);
+
+ int32_t eval_const_int(ASTNode* node);
+ float eval_const_float(ASTNode* node);
+
+ void flatten_init(const std::vector<int>& dims, ASTNode* init, std::vector<ASTNode*>& out);
+
+ /// BType -> K class (Kw / Ks)
+ static int btype_to_cls(BType t);
+
+ /// Infer actual K class of a Ref.
+ static int ref_cls_of(Ref r, Fn* fn);
+
+ /// Type coercion: Emit Oswtof/Ostosi when Ref's current cls differs from target.
+ Ref coerce(Ref v, int target_cls);
+};
+
+} // namespace rcc
diff --git a/include/rcc/loopinfo.h b/include/rcc/loopinfo.h
new file mode 100644
index 0000000..581f8c5
--- /dev/null
+++ b/include/rcc/loopinfo.h
@@ -0,0 +1,74 @@
+/* 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 loopinfo.h
+/// @brief Natural loop and constant BIV identification.
+
+#pragma once
+
+#include "rcc/all.h"
+
+#include <unordered_set>
+#include <vector>
+
+namespace rcc {
+
+struct Loop {
+ Blk* header;
+ Blk* preheader;
+ Blk* latch;
+ std::vector<Blk*> body; // Includes header / latch
+ std::unordered_set<uint> body_bid; // Set of b->id in body, used for tmp.bid lookup
+};
+
+/// Constant Basic Induction Variable.
+struct Biv {
+ Ref to; // header phi.to
+ int cls; // phi cls
+ int64_t init; // Preheader constant
+ int64_t step; // Constant step of Oadd at latch, != 0
+};
+
+void find_loops(Fn*, std::vector<Loop>&);
+void detect_bivs(Fn*, const Loop&, std::vector<Biv>&);
+
+/// Decode CBits constant Ref (integer). Returns false for CAddr / float / non-RCon.
+bool ref_is_const(Fn*, Ref, int64_t& out);
+
+/// Get defining Ins via Tmp::def. Returns nullptr for non-RTmp or out of bounds.
+Ins* def_ins(Fn*, Ref);
+
+/// Canonical pointer IV after LSR.
+struct PointerIV {
+ Ref phi_to;
+ Ref base;
+ Ref next;
+ int64_t step;
+};
+
+/// Identify pointer IV on header phi. Returns false on failure.
+bool pick_pointer_iv(Fn*, const Loop&, Phi*, PointerIV*);
+
+/// Match pointer termination condition against pointer IV.
+bool check_exit_cmp(Fn*, const Loop&, Ref p, Ref* end_out, Blk** after_out);
+
+/// Check if any def inside the loop is used by outside users.
+bool has_outside_use(Fn*, const Loop&);
+
+} // namespace rcc
diff --git a/include/rcc/ops.h b/include/rcc/ops.h
new file mode 100644
index 0000000..4e44350
--- /dev/null
+++ b/include/rcc/ops.h
@@ -0,0 +1,393 @@
+/* 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/>. */
+
+// See docs/ops.md for architectural notes.
+
+enum O {
+ Oxxx,
+
+ Oadd,
+ Osub,
+ Oneg,
+ Odiv,
+ Orem,
+ Oudiv,
+ Ourem,
+ Omul,
+ Oand,
+ Oor,
+ Oxor,
+ Osar,
+ Oshr,
+ Oshl,
+
+ Oceqw,
+ Ocnew,
+ Ocsgew,
+ Ocsgtw,
+ Ocslew,
+ Ocsltw,
+ Ocugew,
+ Ocugtw,
+ Oculew,
+ Ocultw,
+ Oceql,
+ Ocnel,
+ Ocsgel,
+ Ocsgtl,
+ Ocslel,
+ Ocsltl,
+ Ocugel,
+ Ocugtl,
+ Oculel,
+ Ocultl,
+ Oceqs,
+ Ocges,
+ Ocgts,
+ Ocles,
+ Oclts,
+ Ocnes,
+ Ocos,
+ Ocuos,
+ Oceqd,
+ Ocged,
+ Ocgtd,
+ Ocled,
+ Ocltd,
+ Ocned,
+ Ocod,
+ Ocuod,
+
+ Ostoreb,
+ Ostoreh,
+ Ostorew,
+ Ostorel,
+ Ostores,
+ Ostored,
+ Oloadsb,
+ Oloadub,
+ Oloadsh,
+ Oloaduh,
+ Oloadsw,
+ Oloaduw,
+ Oload,
+
+ Oextsb,
+ Oextub,
+ Oextsh,
+ Oextuh,
+ Oextsw,
+ Oextuw,
+ Oexts,
+ Otruncd,
+ Ostosi,
+ Ostoui,
+ Odtosi,
+ Odtoui,
+ Oswtof,
+ Ouwtof,
+ Osltof,
+ Oultof,
+ Ocast,
+
+ Oalloc4,
+ Oalloc8,
+ Oalloc16,
+ Ovaarg,
+ Ovastart,
+ Ocopy,
+ Odbgloc,
+
+ // The following are internal ops (Onop must be first)
+
+ Onop,
+ Oaddr,
+ Oblit0,
+ Oblit1,
+ Osel0,
+ Osel1,
+ Oswap,
+ Osign,
+ Osalloc,
+ Oxidiv,
+ Oxdiv,
+ Oxcmp,
+ Oxtest,
+ Oacmp,
+ Oacmn,
+ Oafcmp,
+ Oreqz,
+ Ornez,
+
+ Opar,
+ Oparsb,
+ Oparub,
+ Oparsh,
+ Oparuh,
+ Oparc,
+ Opare,
+ Oarg,
+ Oargsb,
+ Oargub,
+ Oargsh,
+ Oarguh,
+ Oargc,
+ Oarge,
+ Oargv,
+ Ocall,
+
+ Oflagieq,
+ Oflagine,
+ Oflagisge,
+ Oflagisgt,
+ Oflagisle,
+ Oflagislt,
+ Oflagiuge,
+ Oflagiugt,
+ Oflagiule,
+ Oflagiult,
+ Oflagfeq,
+ Oflagfge,
+ Oflagfgt,
+ Oflagfle,
+ Oflagflt,
+ Oflagfne,
+ Oflagfo,
+ Oflagfuo,
+
+ Oxselieq,
+ Oxseline,
+ Oxselisge,
+ Oxselisgt,
+ Oxselisle,
+ Oxselislt,
+ Oxseliuge,
+ Oxseliugt,
+ Oxseliule,
+ Oxseliult,
+ Oxselfeq,
+ Oxselfge,
+ Oxselfgt,
+ Oxselfle,
+ Oxselflt,
+ Oxselfne,
+ Oxselfo,
+ Oxselfuo,
+
+ NOp,
+};
+
+// Placeholders in argcls: Ke means (op,cls) is illegal; Km means memory pointer (same as Kl
+// structurally, used for semantic tagging).
+enum { Ke = -2, Km = Kl };
+
+enum OpFlags : unsigned {
+ Fold = 1u << 0,
+ Id = 1u << 1,
+ IdV = 1u << 2,
+ Comm = 1u << 3,
+ Assoc = 1u << 4,
+ Idemp = 1u << 5,
+ CmpEW = 1u << 6,
+ CmpLW = 1u << 7,
+ EqV = 1u << 8,
+ Pin = 1u << 9,
+};
+
+struct OpInfo {
+ const char* name;
+ short argcls[2][4];
+ unsigned flags;
+ unsigned char imm; // rv64 I-form right operand can use 12-bit immediate
+};
+
+inline constexpr auto kops = [] {
+ std::array<OpInfo, NOp> a{};
+ a[Oxxx] = {"", {{Kx, Kx, Kx, Kx}, {Kx, Kx, Kx, Kx}}, 0, 0};
+
+ a[Oadd] = {"add", {{Kw, Kl, Ks, Kd}, {Kw, Kl, Ks, Kd}}, Fold | Id | Comm | Assoc, 1};
+ a[Osub] = {"sub", {{Kw, Kl, Ks, Kd}, {Kw, Kl, Ks, Kd}}, Fold | Id, 0};
+ a[Oneg] = {"neg", {{Kw, Kl, Ks, Kd}, {Kx, Kx, Kx, Kx}}, Fold, 0};
+ a[Odiv] = {"div", {{Kw, Kl, Ks, Kd}, {Kw, Kl, Ks, Kd}}, Fold | Id | IdV, 0};
+ a[Orem] = {"rem", {{Kw, Kl, Ke, Ke}, {Kw, Kl, Ke, Ke}}, Fold, 0};
+ a[Oudiv] = {"udiv", {{Kw, Kl, Ke, Ke}, {Kw, Kl, Ke, Ke}}, Fold | Id | IdV, 0};
+ a[Ourem] = {"urem", {{Kw, Kl, Ke, Ke}, {Kw, Kl, Ke, Ke}}, Fold, 0};
+ a[Omul] = {"mul", {{Kw, Kl, Ks, Kd}, {Kw, Kl, Ks, Kd}}, Fold | Id | IdV | Comm, 0};
+ a[Oand] = {"and", {{Kw, Kl, Ke, Ke}, {Kw, Kl, Ke, Ke}}, Fold | Comm | Assoc | Idemp, 1};
+ a[Oor] = {"or", {{Kw, Kl, Ke, Ke}, {Kw, Kl, Ke, Ke}}, Fold | Id | Comm | Assoc | Idemp, 1};
+ a[Oxor] = {"xor", {{Kw, Kl, Ke, Ke}, {Kw, Kl, Ke, Ke}}, Fold | Id | Comm | Assoc, 1};
+ a[Osar] = {"sar", {{Kw, Kl, Ke, Ke}, {Kw, Kw, Ke, Ke}}, Fold | Id, 1};
+ a[Oshr] = {"shr", {{Kw, Kl, Ke, Ke}, {Kw, Kw, Ke, Ke}}, Fold | Id, 1};
+ a[Oshl] = {"shl", {{Kw, Kl, Ke, Ke}, {Kw, Kw, Ke, Ke}}, Fold | Id, 1};
+
+ a[Oceqw] = {
+ "ceqw", {{Kw, Kw, Ke, Ke}, {Kw, Kw, Ke, Ke}}, Fold | Id | IdV | Comm | CmpEW | EqV, 0};
+ a[Ocnew] = {"cnew", {{Kw, Kw, Ke, Ke}, {Kw, Kw, Ke, Ke}}, Fold | Id | Comm | CmpEW, 0};
+ a[Ocsgew] = {"csgew", {{Kw, Kw, Ke, Ke}, {Kw, Kw, Ke, Ke}}, Fold | CmpLW | EqV, 0};
+ a[Ocsgtw] = {"csgtw", {{Kw, Kw, Ke, Ke}, {Kw, Kw, Ke, Ke}}, Fold | CmpLW, 0};
+ a[Ocslew] = {"cslew", {{Kw, Kw, Ke, Ke}, {Kw, Kw, Ke, Ke}}, Fold | CmpLW | EqV, 0};
+ a[Ocsltw] = {"csltw", {{Kw, Kw, Ke, Ke}, {Kw, Kw, Ke, Ke}}, Fold | CmpLW, 1};
+ a[Ocugew] = {"cugew", {{Kw, Kw, Ke, Ke}, {Kw, Kw, Ke, Ke}}, Fold | CmpLW | EqV, 0};
+ a[Ocugtw] = {"cugtw", {{Kw, Kw, Ke, Ke}, {Kw, Kw, Ke, Ke}}, Fold | CmpLW, 0};
+ a[Oculew] = {"culew", {{Kw, Kw, Ke, Ke}, {Kw, Kw, Ke, Ke}}, Fold | CmpLW | EqV, 0};
+ a[Ocultw] = {"cultw", {{Kw, Kw, Ke, Ke}, {Kw, Kw, Ke, Ke}}, Fold | CmpLW, 1};
+ a[Oceql] = {"ceql", {{Kl, Kl, Ke, Ke}, {Kl, Kl, Ke, Ke}}, Fold | Comm | CmpEW | EqV, 0};
+ a[Ocnel] = {"cnel", {{Kl, Kl, Ke, Ke}, {Kl, Kl, Ke, Ke}}, Fold | Comm | CmpEW, 0};
+ a[Ocsgel] = {"csgel", {{Kl, Kl, Ke, Ke}, {Kl, Kl, Ke, Ke}}, Fold | CmpLW | EqV, 0};
+ a[Ocsgtl] = {"csgtl", {{Kl, Kl, Ke, Ke}, {Kl, Kl, Ke, Ke}}, Fold | CmpLW, 0};
+ a[Ocslel] = {"cslel", {{Kl, Kl, Ke, Ke}, {Kl, Kl, Ke, Ke}}, Fold | CmpLW | EqV, 0};
+ a[Ocsltl] = {"csltl", {{Kl, Kl, Ke, Ke}, {Kl, Kl, Ke, Ke}}, Fold | CmpLW, 1};
+ a[Ocugel] = {"cugel", {{Kl, Kl, Ke, Ke}, {Kl, Kl, Ke, Ke}}, Fold | CmpLW | EqV, 0};
+ a[Ocugtl] = {"cugtl", {{Kl, Kl, Ke, Ke}, {Kl, Kl, Ke, Ke}}, Fold | CmpLW, 0};
+ a[Oculel] = {"culel", {{Kl, Kl, Ke, Ke}, {Kl, Kl, Ke, Ke}}, Fold | CmpLW | EqV, 0};
+ a[Ocultl] = {"cultl", {{Kl, Kl, Ke, Ke}, {Kl, Kl, Ke, Ke}}, Fold | CmpLW, 1};
+ a[Oceqs] = {"ceqs", {{Ks, Ks, Ke, Ke}, {Ks, Ks, Ke, Ke}}, Fold | Comm, 0};
+ a[Ocges] = {"cges", {{Ks, Ks, Ke, Ke}, {Ks, Ks, Ke, Ke}}, Fold, 0};
+ a[Ocgts] = {"cgts", {{Ks, Ks, Ke, Ke}, {Ks, Ks, Ke, Ke}}, Fold, 0};
+ a[Ocles] = {"cles", {{Ks, Ks, Ke, Ke}, {Ks, Ks, Ke, Ke}}, Fold, 0};
+ a[Oclts] = {"clts", {{Ks, Ks, Ke, Ke}, {Ks, Ks, Ke, Ke}}, Fold, 0};
+ a[Ocnes] = {"cnes", {{Ks, Ks, Ke, Ke}, {Ks, Ks, Ke, Ke}}, Fold | Comm, 0};
+ a[Ocos] = {"cos", {{Ks, Ks, Ke, Ke}, {Ks, Ks, Ke, Ke}}, Fold | Comm, 0};
+ a[Ocuos] = {"cuos", {{Ks, Ks, Ke, Ke}, {Ks, Ks, Ke, Ke}}, Fold | Comm, 0};
+ a[Oceqd] = {"ceqd", {{Kd, Kd, Ke, Ke}, {Kd, Kd, Ke, Ke}}, Fold | Comm, 0};
+ a[Ocged] = {"cged", {{Kd, Kd, Ke, Ke}, {Kd, Kd, Ke, Ke}}, Fold, 0};
+ a[Ocgtd] = {"cgtd", {{Kd, Kd, Ke, Ke}, {Kd, Kd, Ke, Ke}}, Fold, 0};
+ a[Ocled] = {"cled", {{Kd, Kd, Ke, Ke}, {Kd, Kd, Ke, Ke}}, Fold, 0};
+ a[Ocltd] = {"cltd", {{Kd, Kd, Ke, Ke}, {Kd, Kd, Ke, Ke}}, Fold, 0};
+ a[Ocned] = {"cned", {{Kd, Kd, Ke, Ke}, {Kd, Kd, Ke, Ke}}, Fold | Comm, 0};
+ a[Ocod] = {"cod", {{Kd, Kd, Ke, Ke}, {Kd, Kd, Ke, Ke}}, Fold | Comm, 0};
+ a[Ocuod] = {"cuod", {{Kd, Kd, Ke, Ke}, {Kd, Kd, Ke, Ke}}, Fold | Comm, 0};
+
+ a[Ostoreb] = {"storeb", {{Kw, Ke, Ke, Ke}, {Km, Ke, Ke, Ke}}, Pin, 0};
+ a[Ostoreh] = {"storeh", {{Kw, Ke, Ke, Ke}, {Km, Ke, Ke, Ke}}, Pin, 0};
+ a[Ostorew] = {"storew", {{Kw, Ke, Ke, Ke}, {Km, Ke, Ke, Ke}}, Pin, 0};
+ a[Ostorel] = {"storel", {{Kl, Ke, Ke, Ke}, {Km, Ke, Ke, Ke}}, Pin, 0};
+ a[Ostores] = {"stores", {{Ks, Ke, Ke, Ke}, {Km, Ke, Ke, Ke}}, Pin, 0};
+ a[Ostored] = {"stored", {{Kd, Ke, Ke, Ke}, {Km, Ke, Ke, Ke}}, Pin, 0};
+ a[Oloadsb] = {"loadsb", {{Km, Km, Ke, Ke}, {Kx, Kx, Ke, Ke}}, Pin, 0};
+ a[Oloadub] = {"loadub", {{Km, Km, Ke, Ke}, {Kx, Kx, Ke, Ke}}, Pin, 0};
+ a[Oloadsh] = {"loadsh", {{Km, Km, Ke, Ke}, {Kx, Kx, Ke, Ke}}, Pin, 0};
+ a[Oloaduh] = {"loaduh", {{Km, Km, Ke, Ke}, {Kx, Kx, Ke, Ke}}, Pin, 0};
+ a[Oloadsw] = {"loadsw", {{Km, Km, Ke, Ke}, {Kx, Kx, Ke, Ke}}, Pin, 0};
+ a[Oloaduw] = {"loaduw", {{Km, Km, Ke, Ke}, {Kx, Kx, Ke, Ke}}, Pin, 0};
+ a[Oload] = {"load", {{Km, Km, Km, Km}, {Kx, Kx, Kx, Kx}}, Pin, 0};
+
+ a[Oextsb] = {"extsb", {{Kw, Kw, Ke, Ke}, {Kx, Kx, Ke, Ke}}, Fold, 0};
+ a[Oextub] = {"extub", {{Kw, Kw, Ke, Ke}, {Kx, Kx, Ke, Ke}}, Fold, 0};
+ a[Oextsh] = {"extsh", {{Kw, Kw, Ke, Ke}, {Kx, Kx, Ke, Ke}}, Fold, 0};
+ a[Oextuh] = {"extuh", {{Kw, Kw, Ke, Ke}, {Kx, Kx, Ke, Ke}}, Fold, 0};
+ a[Oextsw] = {"extsw", {{Ke, Kw, Ke, Ke}, {Ke, Kx, Ke, Ke}}, Fold, 0};
+ a[Oextuw] = {"extuw", {{Ke, Kw, Ke, Ke}, {Ke, Kx, Ke, Ke}}, Fold, 0};
+ a[Oexts] = {"exts", {{Ke, Ke, Ke, Ks}, {Ke, Ke, Ke, Kx}}, Fold, 0};
+ a[Otruncd] = {"truncd", {{Ke, Ke, Kd, Ke}, {Ke, Ke, Kx, Ke}}, Fold, 0};
+ a[Ostosi] = {"stosi", {{Ks, Ks, Ke, Ke}, {Kx, Kx, Ke, Ke}}, Fold, 0};
+ a[Ostoui] = {"stoui", {{Ks, Ks, Ke, Ke}, {Kx, Kx, Ke, Ke}}, Fold, 0};
+ a[Odtosi] = {"dtosi", {{Kd, Kd, Ke, Ke}, {Kx, Kx, Ke, Ke}}, Fold, 0};
+ a[Odtoui] = {"dtoui", {{Kd, Kd, Ke, Ke}, {Kx, Kx, Ke, Ke}}, Fold, 0};
+ a[Oswtof] = {"swtof", {{Ke, Ke, Kw, Kw}, {Ke, Ke, Kx, Kx}}, Fold, 0};
+ a[Ouwtof] = {"uwtof", {{Ke, Ke, Kw, Kw}, {Ke, Ke, Kx, Kx}}, Fold, 0};
+ a[Osltof] = {"sltof", {{Ke, Ke, Kl, Kl}, {Ke, Ke, Kx, Kx}}, Fold, 0};
+ a[Oultof] = {"ultof", {{Ke, Ke, Kl, Kl}, {Ke, Ke, Kx, Kx}}, Fold, 0};
+ a[Ocast] = {"cast", {{Ks, Kd, Kw, Kl}, {Kx, Kx, Kx, Kx}}, Fold, 0};
+
+ a[Oalloc4] = {"alloc4", {{Ke, Kl, Ke, Ke}, {Ke, Kx, Ke, Ke}}, Pin, 0};
+ a[Oalloc8] = {"alloc8", {{Ke, Kl, Ke, Ke}, {Ke, Kx, Ke, Ke}}, Pin, 0};
+ a[Oalloc16] = {"alloc16", {{Ke, Kl, Ke, Ke}, {Ke, Kx, Ke, Ke}}, Pin, 0};
+
+ a[Ovaarg] = {"vaarg", {{Km, Km, Km, Km}, {Kx, Kx, Kx, Kx}}, Pin, 0};
+ a[Ovastart] = {"vastart", {{Km, Ke, Ke, Ke}, {Kx, Ke, Ke, Ke}}, Pin, 0};
+
+ a[Ocopy] = {"copy", {{Kw, Kl, Ks, Kd}, {Kx, Kx, Kx, Kx}}, 0, 0};
+ a[Odbgloc] = {"dbgloc", {{Kw, Ke, Ke, Ke}, {Kw, Ke, Ke, Ke}}, Pin, 0};
+
+ a[Onop] = {"nop", {{Kx, Kx, Kx, Kx}, {Kx, Kx, Kx, Kx}}, 0, 0};
+ a[Oaddr] = {"addr", {{Km, Km, Ke, Ke}, {Kx, Kx, Ke, Ke}}, 0, 0};
+ a[Oblit0] = {"blit0", {{Km, Ke, Ke, Ke}, {Km, Ke, Ke, Ke}}, Pin, 0};
+ a[Oblit1] = {"blit1", {{Kw, Ke, Ke, Ke}, {Kx, Ke, Ke, Ke}}, Pin, 0};
+ a[Osel0] = {"sel0", {{Kw, Ke, Ke, Ke}, {Kx, Ke, Ke, Ke}}, Pin, 0};
+ a[Osel1] = {"sel1", {{Kw, Kl, Ke, Ke}, {Kw, Kl, Ke, Ke}}, Pin, 0};
+ a[Oswap] = {"swap", {{Kw, Kl, Ks, Kd}, {Kw, Kl, Ks, Kd}}, 0, 0};
+ a[Osign] = {"sign", {{Kw, Kl, Ke, Ke}, {Kx, Kx, Ke, Ke}}, 0, 0};
+ a[Osalloc] = {"salloc", {{Ke, Kl, Ke, Ke}, {Ke, Kx, Ke, Ke}}, 0, 0};
+ a[Oxidiv] = {"xidiv", {{Kw, Kl, Ke, Ke}, {Kx, Kx, Ke, Ke}}, 0, 0};
+ a[Oxdiv] = {"xdiv", {{Kw, Kl, Ke, Ke}, {Kx, Kx, Ke, Ke}}, 0, 0};
+ a[Oxcmp] = {"xcmp", {{Kw, Kl, Ks, Kd}, {Kw, Kl, Ks, Kd}}, 0, 0};
+ a[Oxtest] = {"xtest", {{Kw, Kl, Ke, Ke}, {Kw, Kl, Ke, Ke}}, 0, 0};
+ a[Oacmp] = {"acmp", {{Kw, Kl, Ke, Ke}, {Kw, Kl, Ke, Ke}}, 0, 0};
+ a[Oacmn] = {"acmn", {{Kw, Kl, Ke, Ke}, {Kw, Kl, Ke, Ke}}, 0, 0};
+ a[Oafcmp] = {"afcmp", {{Ke, Ke, Ks, Kd}, {Ke, Ke, Ks, Kd}}, 0, 0};
+ a[Oreqz] = {"reqz", {{Kw, Kl, Ke, Ke}, {Kx, Kx, Ke, Ke}}, 0, 0};
+ a[Ornez] = {"rnez", {{Kw, Kl, Ke, Ke}, {Kx, Kx, Ke, Ke}}, 0, 0};
+
+ a[Opar] = {"par", {{Kx, Kx, Kx, Kx}, {Kx, Kx, Kx, Kx}}, Pin, 0};
+ a[Oparsb] = {"parsb", {{Kx, Kx, Kx, Kx}, {Kx, Kx, Kx, Kx}}, Pin, 0};
+ a[Oparub] = {"parub", {{Kx, Kx, Kx, Kx}, {Kx, Kx, Kx, Kx}}, Pin, 0};
+ a[Oparsh] = {"parsh", {{Kx, Kx, Kx, Kx}, {Kx, Kx, Kx, Kx}}, Pin, 0};
+ a[Oparuh] = {"paruh", {{Kx, Kx, Kx, Kx}, {Kx, Kx, Kx, Kx}}, Pin, 0};
+ a[Oparc] = {"parc", {{Ke, Kx, Ke, Ke}, {Ke, Kx, Ke, Ke}}, Pin, 0};
+ a[Opare] = {"pare", {{Ke, Kx, Ke, Ke}, {Ke, Kx, Ke, Ke}}, Pin, 0};
+ a[Oarg] = {"arg", {{Kw, Kl, Ks, Kd}, {Kx, Kx, Kx, Kx}}, Pin, 0};
+ a[Oargsb] = {"argsb", {{Kw, Ke, Ke, Ke}, {Kx, Kx, Kx, Kx}}, Pin, 0};
+ a[Oargub] = {"argub", {{Kw, Ke, Ke, Ke}, {Kx, Kx, Kx, Kx}}, Pin, 0};
+ a[Oargsh] = {"argsh", {{Kw, Ke, Ke, Ke}, {Kx, Kx, Kx, Kx}}, Pin, 0};
+ a[Oarguh] = {"arguh", {{Kw, Ke, Ke, Ke}, {Kx, Kx, Kx, Kx}}, Pin, 0};
+ a[Oargc] = {"argc", {{Ke, Kx, Ke, Ke}, {Ke, Kl, Ke, Ke}}, Pin, 0};
+ a[Oarge] = {"arge", {{Ke, Kl, Ke, Ke}, {Ke, Kx, Ke, Ke}}, Pin, 0};
+ a[Oargv] = {"argv", {{Kx, Kx, Kx, Kx}, {Kx, Kx, Kx, Kx}}, Pin, 0};
+ a[Ocall] = {"call", {{Km, Km, Km, Km}, {Kx, Kx, Kx, Kx}}, Pin, 0};
+
+ a[Oflagieq] = {"flagieq", {{Kx, Kx, Ke, Ke}, {Kx, Kx, Ke, Ke}}, 0, 0};
+ a[Oflagine] = {"flagine", {{Kx, Kx, Ke, Ke}, {Kx, Kx, Ke, Ke}}, 0, 0};
+ a[Oflagisge] = {"flagisge", {{Kx, Kx, Ke, Ke}, {Kx, Kx, Ke, Ke}}, 0, 0};
+ a[Oflagisgt] = {"flagisgt", {{Kx, Kx, Ke, Ke}, {Kx, Kx, Ke, Ke}}, 0, 0};
+ a[Oflagisle] = {"flagisle", {{Kx, Kx, Ke, Ke}, {Kx, Kx, Ke, Ke}}, 0, 0};
+ a[Oflagislt] = {"flagislt", {{Kx, Kx, Ke, Ke}, {Kx, Kx, Ke, Ke}}, 0, 0};
+ a[Oflagiuge] = {"flagiuge", {{Kx, Kx, Ke, Ke}, {Kx, Kx, Ke, Ke}}, 0, 0};
+ a[Oflagiugt] = {"flagiugt", {{Kx, Kx, Ke, Ke}, {Kx, Kx, Ke, Ke}}, 0, 0};
+ a[Oflagiule] = {"flagiule", {{Kx, Kx, Ke, Ke}, {Kx, Kx, Ke, Ke}}, 0, 0};
+ a[Oflagiult] = {"flagiult", {{Kx, Kx, Ke, Ke}, {Kx, Kx, Ke, Ke}}, 0, 0};
+ a[Oflagfeq] = {"flagfeq", {{Kx, Kx, Ke, Ke}, {Kx, Kx, Ke, Ke}}, 0, 0};
+ a[Oflagfge] = {"flagfge", {{Kx, Kx, Ke, Ke}, {Kx, Kx, Ke, Ke}}, 0, 0};
+ a[Oflagfgt] = {"flagfgt", {{Kx, Kx, Ke, Ke}, {Kx, Kx, Ke, Ke}}, 0, 0};
+ a[Oflagfle] = {"flagfle", {{Kx, Kx, Ke, Ke}, {Kx, Kx, Ke, Ke}}, 0, 0};
+ a[Oflagflt] = {"flagflt", {{Kx, Kx, Ke, Ke}, {Kx, Kx, Ke, Ke}}, 0, 0};
+ a[Oflagfne] = {"flagfne", {{Kx, Kx, Ke, Ke}, {Kx, Kx, Ke, Ke}}, 0, 0};
+ a[Oflagfo] = {"flagfo", {{Kx, Kx, Ke, Ke}, {Kx, Kx, Ke, Ke}}, 0, 0};
+ a[Oflagfuo] = {"flagfuo", {{Kx, Kx, Ke, Ke}, {Kx, Kx, Ke, Ke}}, 0, 0};
+
+ a[Oxselieq] = {"xselieq", {{Kw, Kl, Ke, Ke}, {Kw, Kl, Ke, Ke}}, 0, 0};
+ a[Oxseline] = {"xseline", {{Kw, Kl, Ke, Ke}, {Kw, Kl, Ke, Ke}}, 0, 0};
+ a[Oxselisge] = {"xselisge", {{Kw, Kl, Ke, Ke}, {Kw, Kl, Ke, Ke}}, 0, 0};
+ a[Oxselisgt] = {"xselisgt", {{Kw, Kl, Ke, Ke}, {Kw, Kl, Ke, Ke}}, 0, 0};
+ a[Oxselisle] = {"xselisle", {{Kw, Kl, Ke, Ke}, {Kw, Kl, Ke, Ke}}, 0, 0};
+ a[Oxselislt] = {"xselislt", {{Kw, Kl, Ke, Ke}, {Kw, Kl, Ke, Ke}}, 0, 0};
+ a[Oxseliuge] = {"xseliuge", {{Kw, Kl, Ke, Ke}, {Kw, Kl, Ke, Ke}}, 0, 0};
+ a[Oxseliugt] = {"xseliugt", {{Kw, Kl, Ke, Ke}, {Kw, Kl, Ke, Ke}}, 0, 0};
+ a[Oxseliule] = {"xseliule", {{Kw, Kl, Ke, Ke}, {Kw, Kl, Ke, Ke}}, 0, 0};
+ a[Oxseliult] = {"xseliult", {{Kw, Kl, Ke, Ke}, {Kw, Kl, Ke, Ke}}, 0, 0};
+ a[Oxselfeq] = {"xselfeq", {{Ke, Ke, Ks, Kd}, {Ke, Ke, Ks, Kd}}, 0, 0};
+ a[Oxselfge] = {"xselfge", {{Ke, Ke, Ks, Kd}, {Ke, Ke, Ks, Kd}}, 0, 0};
+ a[Oxselfgt] = {"xselfgt", {{Ke, Ke, Ks, Kd}, {Ke, Ke, Ks, Kd}}, 0, 0};
+ a[Oxselfle] = {"xselfle", {{Ke, Ke, Ks, Kd}, {Ke, Ke, Ks, Kd}}, 0, 0};
+ a[Oxselflt] = {"xselflt", {{Ke, Ke, Ks, Kd}, {Ke, Ke, Ks, Kd}}, 0, 0};
+ a[Oxselfne] = {"xselfne", {{Ke, Ke, Ks, Kd}, {Ke, Ke, Ks, Kd}}, 0, 0};
+ a[Oxselfo] = {"xselfo", {{Ke, Ke, Ks, Kd}, {Ke, Ke, Ks, Kd}}, 0, 0};
+ a[Oxselfuo] = {"xselfuo", {{Ke, Ke, Ks, Kd}, {Ke, Ke, Ks, Kd}}, 0, 0};
+ return a;
+}();
diff --git a/include/rcc/rv64.h b/include/rcc/rv64.h
new file mode 100644
index 0000000..582dbb8
--- /dev/null
+++ b/include/rcc/rv64.h
@@ -0,0 +1,155 @@
+/* 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 rv64.h
+/// @brief RISC-V 64 (lp64d ABI) backend per-target header.
+
+#pragma once
+
+#include "rcc/all.h"
+
+#include <cstdint>
+
+namespace rcc {
+
+// Register Numbers
+
+enum Rv64Reg : int {
+ // GPR: caller-save temps
+ T0 = RXX + 1,
+ T1,
+ T2,
+ T3,
+ T4,
+ T5,
+
+ // GPR: arguments / return values (caller-save)
+ A0,
+ A1,
+ A2,
+ A3,
+ A4,
+ A5,
+ A6,
+ A7,
+
+ // GPR: callee-save (S0 = FP, see below; therefore jumps to S1)
+ S1,
+ S2,
+ S3,
+ S4,
+ S5,
+ S6,
+ S7,
+ S8,
+ S9,
+ S10,
+ S11,
+
+ // Globally live
+ FP, // = S0 alias, frame pointer
+ SP, // stack pointer
+ GP, // global pointer
+ TP, // thread pointer
+ RA, // return address
+
+ // FPR: caller-save temps
+ FT0,
+ FT1,
+ FT2,
+ FT3,
+ FT4,
+ FT5,
+ FT6,
+ FT7,
+ FT8,
+ FT9,
+ FT10,
+
+ // FPR: arguments / return values (caller-save)
+ FA0,
+ FA1,
+ FA2,
+ FA3,
+ FA4,
+ FA5,
+ FA6,
+ FA7,
+
+ // FPR: callee-save
+ FS0,
+ FS1,
+ FS2,
+ FS3,
+ FS4,
+ FS5,
+ FS6,
+ FS7,
+ FS8,
+ FS9,
+ FS10,
+ FS11,
+
+ // Reserved (emit.cc internal scratch, does not participate in rega)
+ T6,
+ FT11,
+
+ // Derived counters (layout-dependent)
+ NFPR = FS11 - FT0 + 1, // Total FPR count (including reserved)
+ NGPR = RA - T0 + 1, // Total GPR count (including reserved)
+ NGPS = A7 - T0 + 1, // T0..A7: scratch + arg segment
+ NFPS = FA7 - FT0 + 1, // FT0..FA7: scratch + arg segment
+ NCLR = (S11 - S1 + 1) // Total callee-save count (GP)
+ + (FS11 - FS0 + 1), // + (FP)
+};
+
+MAKESURE(rv64_reg_not_tmp, FT11 < static_cast<int>(Tmp0));
+
+// Per-op attribute table (defined in targ.cc)
+
+struct Rv64Op {
+ uint8_t imm; // 1 = op accepts immediate operand
+};
+
+// Global tables provided by targ.cc
+
+extern int rv64_rsave[]; // caller-save list (for rega)
+extern int rv64_rclob[]; // call boundary clobber list
+extern Rv64Op rv64_op[]; // Indexed by O* op enum value
+
+// rv64 target descriptor. Assigned `T = T_rv64` early in main startup (before worker thread
+// creation), read-only afterwards.
+extern Target T_rv64;
+
+// Backend pass entries
+
+// abi.cc - RCall encode/decode (pure query, no allocations)
+bits rv64_retregs(Ref, int p[2]);
+bits rv64_argregs(Ref, int p[2]);
+
+// abi.cc - ABI lowering (internal newtmp/emit/idup/vgrow/alloc, requires Ctx*)
+void rv64_abi(Ctx*, Fn*);
+
+// isel.cc - Instruction selection (internal newtmp/emit, requires Ctx*)
+void rv64_isel(Ctx*, Fn*);
+
+// emit.cc - Assembly emission
+void rv64_emitfn(Fn*, FILE*);
+
+} // namespace rcc
diff --git a/include/rcc/symtab.h b/include/rcc/symtab.h
new file mode 100644
index 0000000..78507b9
--- /dev/null
+++ b/include/rcc/symtab.h
@@ -0,0 +1,120 @@
+/* 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/>. */
+
+#pragma once
+
+/// @file symtab.h
+/// @brief Scope chain symbol table.
+
+#include "rcc/sysy_type.h"
+
+#include <cassert>
+#include <string>
+#include <unordered_map>
+#include <variant>
+#include <vector>
+
+namespace rcc {
+
+/// Symbol information
+struct Symbol {
+ std::string name;
+ BType type = BType::Int;
+ bool is_const = false;
+ bool is_array = false;
+ bool is_param = false;
+ bool is_global = false;
+ std::vector<int> dims; // Array dimensions
+
+ // Compile-time constant value (for constant folding and const array initialization)
+ std::variant<std::monostate, int32_t, float, std::vector<int32_t>, std::vector<float>>
+ const_val;
+
+ // IR phase: If variable needs an address (array, global), store its alloca/global address here
+ int alloca_temp_id = -1; // Stack-allocated temp ID, -1 means unallocated
+
+ // Unique ID for Braun SSA: unique within a function, across scopes.
+ // Allocated by SymbolTable::allocate_uid() during Symbol construction.
+ int uid = -1;
+};
+
+/// Function signature
+struct FuncSig {
+ std::string name;
+ BType ret_type = BType::Void;
+ std::vector<std::pair<BType, bool>> params; // (type, is_array)
+};
+
+/// Chained scope symbol table
+class SymbolTable {
+ public:
+ void enter_scope() { scopes_.emplace_back(); }
+
+ void leave_scope() {
+ assert(!scopes_.empty());
+ scopes_.pop_back();
+ }
+
+ /// Insert symbol in current scope. Returns false on duplicate definition.
+ bool insert(const std::string& name, Symbol sym) {
+ auto& cur = scopes_.back().symbols;
+ if (cur.count(name)) {
+ return false;
+ }
+ cur[name] = std::move(sym);
+ return true;
+ }
+
+ /// Lookup variable symbol from inner to outer scopes.
+ Symbol* lookup(const std::string& name) {
+ for (auto it = scopes_.rbegin(); it != scopes_.rend(); ++it) {
+ auto found = it->symbols.find(name);
+ if (found != it->symbols.end()) {
+ return &found->second;
+ }
+ }
+ return nullptr;
+ }
+
+ /// Register function signature.
+ void register_func(FuncSig sig) { func_table_[sig.name] = std::move(sig); }
+
+ /// Lookup function signature.
+ FuncSig* lookup_func(const std::string& name) {
+ auto it = func_table_.find(name);
+ return it != func_table_.end() ? &it->second : nullptr;
+ }
+
+ /// Register SysY runtime library functions.
+ void register_builtins();
+
+ /// Allocate a new Symbol uid.
+ /// Counter increments monotonically across enter/leave_scope without resetting.
+ int allocate_uid() { return next_uid_++; }
+
+ private:
+ struct Scope {
+ std::unordered_map<std::string, Symbol> symbols;
+ };
+ std::vector<Scope> scopes_;
+ std::unordered_map<std::string, FuncSig> func_table_;
+ int next_uid_ = 0;
+};
+
+} // namespace rcc
diff --git a/include/rcc/sysy_type.h b/include/rcc/sysy_type.h
new file mode 100644
index 0000000..a459b22
--- /dev/null
+++ b/include/rcc/sysy_type.h
@@ -0,0 +1,40 @@
+/* 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/>. */
+#pragma once
+
+/// @file sysy_type.h
+/// @brief SysY source language basic types
+
+namespace rcc {
+
+enum class BType { Int, Float, Void };
+
+inline const char* btype_str(BType t) {
+ switch (t) {
+ case BType::Int:
+ return "int";
+ case BType::Float:
+ return "float";
+ case BType::Void:
+ return "void";
+ }
+ return "?";
+}
+
+} // namespace rcc