diff options
Diffstat (limited to 'include')
| -rw-r--r-- | include/rcc/all.h | 826 | ||||
| -rw-r--r-- | include/rcc/ast.h | 277 | ||||
| -rw-r--r-- | include/rcc/error.h | 34 | ||||
| -rw-r--r-- | include/rcc/irgen.h | 192 | ||||
| -rw-r--r-- | include/rcc/loopinfo.h | 74 | ||||
| -rw-r--r-- | include/rcc/ops.h | 393 | ||||
| -rw-r--r-- | include/rcc/rv64.h | 155 | ||||
| -rw-r--r-- | include/rcc/symtab.h | 120 | ||||
| -rw-r--r-- | include/rcc/sysy_type.h | 40 |
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 |
