/* Source code for RCC-Legacy. Copyright (C) 2026 Richard Qin This file is part of RCC. RCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. RCC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with RCC; see the file LICENSE. If not see . */ /// @file main.cc /// @brief RCC compiler entry point. #include "rcc/all.h" #include "rcc/ast.h" #include "rcc/irgen.h" #include "rcc/rv64.h" #include #include #include #include // External Flex/Bison declarations extern FILE* yyin; extern int yyparse(); extern rcc::CompUnitNode* g_ast_root; namespace { struct Args { std::string input; std::string output; int opt_level = 0; bool dump_ast = false; bool dump_ir = false; bool emit_il = false; // Output QBE-compatible SSA text to -o (or stdout) bool dbg = false; // Set if any -d flag is enabled; triggers skipping emit }; Args parse_args(int argc, char* argv[]) { Args args; for (int i = 1; i < argc; i++) { if (strcmp(argv[i], "-S") == 0) { continue; } else if (strcmp(argv[i], "-o") == 0 && i + 1 < argc) { args.output = argv[++i]; } else if (strcmp(argv[i], "-O1") == 0) { args.opt_level = 1; } else if (strcmp(argv[i], "-O2") == 0) { args.opt_level = 2; } else if (strcmp(argv[i], "--dump-ast") == 0) { args.dump_ast = true; } else if (strcmp(argv[i], "--dump-ir") == 0) { args.dump_ir = true; } else if (strcmp(argv[i], "--emit-il") == 0) { args.emit_il = true; } else if (strcmp(argv[i], "-d") == 0 && i + 1 < argc) { for (char* p = argv[++i]; *p; ++p) { unsigned char c = static_cast(*p); if (std::isalpha(c)) { rcc::debug[std::toupper(c)] = 1; args.dbg = true; } } } else if (argv[i][0] != '-') { args.input = argv[i]; } } if (args.input.empty()) { fprintf(stderr, "rcc: error: no input file\n"); fprintf(stderr, "usage: compiler input.sy -S -o output.s [-O1] [--emit-il] [-d ]\n"); exit(1); } if (args.output.empty()) { args.output = args.emit_il ? "-" : "a.s"; } return args; } // globals → Dat* → emitdat constexpr size_t kElemBytes = 4; void emit_one_global(const rcc::GlobalData& g, FILE* f) { rcc::Lnk lnk{}; lnk.align = 4; // Do not export: SysY globals are not visible externally. std::string name = g.name; rcc::Dat d{}; d.lnk = &lnk; d.name = name.data(); d.type = rcc::Dat::DStart; rcc::emitdat(&d, f); if (g.is_zeroinit) { d.type = rcc::Dat::DZ; d.u.num = static_cast(g.total_elems * kElemBytes); rcc::emitdat(&d, f); } else { for (const auto& v : g.init_vals) { int32_t bits32 = 0; if (std::holds_alternative(v)) { int32_t iv = std::get(v); if (g.elem_cls == rcc::Ks) { float fv = static_cast(iv); std::memcpy(&bits32, &fv, sizeof(bits32)); } else { bits32 = iv; } } else { float fv = std::get(v); if (g.elem_cls == rcc::Kw) { bits32 = static_cast(fv); } else { std::memcpy(&bits32, &fv, sizeof(bits32)); } } d.type = rcc::Dat::DW; d.isref = 0; d.isstr = 0; d.u.num = static_cast(static_cast(bits32)); rcc::emitdat(&d, f); } } d.type = rcc::Dat::DEnd; rcc::emitdat(&d, f); } void emit_globals_asm(const std::vector& globals, FILE* f) { for (const auto& g : globals) emit_one_global(g, f); } // Mid-end pass chain // ssacheck after gcm; ifconvert only if '-d K' is set. void run_midend(rcc::Ctx* ctx, rcc::Fn* fn) { ctx->fn = fn; ctx->spill_fp_live_peak = 0; rcc::T.abi0(fn); rcc::fillcfg(ctx, fn); rcc::filluse(ctx, fn); rcc::promote(ctx, fn); rcc::filluse(ctx, fn); rcc::ssa(ctx, fn); rcc::filluse(ctx, fn); rcc::ssacheck(fn); rcc::filldom(fn); rcc::loopinterchange(ctx, fn); rcc::fillcfg(ctx, fn); rcc::filluse(ctx, fn); rcc::filldom(fn); rcc::ssacheck(fn); rcc::lsr(ctx, fn); rcc::filluse(ctx, fn); rcc::ssacheck(fn); rcc::loop_idiom(ctx, fn); rcc::fillcfg(ctx, fn); rcc::filluse(ctx, fn); rcc::filldom(fn); rcc::ssacheck(fn); rcc::dce(ctx, fn); rcc::filluse(ctx, fn); rcc::ssacheck(fn); rcc::reduction_unroll(ctx, fn); rcc::fillcfg(ctx, fn); rcc::filluse(ctx, fn); rcc::filldom(fn); rcc::ssacheck(fn); rcc::dce(ctx, fn); rcc::filluse(ctx, fn); rcc::ssacheck(fn); rcc::dead_loop(ctx, fn); rcc::fillcfg(ctx, fn); rcc::filluse(ctx, fn); rcc::ssacheck(fn); rcc::unroll(ctx, fn); rcc::filluse(ctx, fn); rcc::ssacheck(fn); rcc::unswitch(ctx, fn); rcc::filluse(ctx, fn); rcc::ssacheck(fn); rcc::tre(ctx, fn); rcc::fillcfg(ctx, fn); rcc::filluse(ctx, fn); rcc::ssacheck(fn); rcc::fillalias(fn); rcc::loadopt(ctx, fn); rcc::filluse(ctx, fn); rcc::sccp(ctx, fn); rcc::fillcfg(ctx, fn); rcc::filluse(ctx, fn); rcc::filldom(fn); rcc::ssacheck(fn); rcc::fillalias(fn); rcc::licm(ctx, fn); rcc::filluse(ctx, fn); rcc::filldom(fn); rcc::fillalias(fn); rcc::scalar_repl(ctx, fn); rcc::filluse(ctx, fn); rcc::coalesce(ctx, fn); rcc::filluse(ctx, fn); rcc::filldom(fn); rcc::ssacheck(fn); rcc::sccp(ctx, fn); rcc::fillcfg(ctx, fn); rcc::filluse(ctx, fn); rcc::filldom(fn); rcc::ssacheck(fn); rcc::reassoc(ctx, fn); rcc::filluse(ctx, fn); rcc::ssacheck(fn); rcc::gvn(ctx, fn); rcc::fillcfg(ctx, fn); rcc::simplcfg(ctx, fn); rcc::filluse(ctx, fn); rcc::filldom(fn); rcc::gcm(ctx, fn); rcc::filluse(ctx, fn); rcc::divopt(ctx, fn); rcc::filluse(ctx, fn); rcc::mulopt(ctx, fn); rcc::filluse(ctx, fn); rcc::storemrg(ctx, fn); rcc::filluse(ctx, fn); rcc::dce(ctx, fn); rcc::filluse(ctx, fn); rcc::ssacheck(fn); if (rcc::T.cansel) { rcc::ifconvert(ctx, fn); rcc::fillcfg(ctx, fn); rcc::filluse(ctx, fn); rcc::filldom(fn); rcc::ssacheck(fn); } } // Backend pass chain void run_backend(rcc::Ctx* ctx, rcc::Fn* fn) { #define STAGE(call) \ do { \ call; \ if (rcc::debug['B']) { \ fprintf(stderr, "\n>>>> after %s (%s):\n", #call, fn->name); \ rcc::printfn(fn, stderr); \ } \ } while (0) STAGE(rcc::T.abi1(ctx, fn)); STAGE(rcc::simpl(ctx, fn)); STAGE(rcc::fillcfg(ctx, fn)); STAGE(rcc::filluse(ctx, fn)); STAGE(rcc::T.isel(ctx, fn)); STAGE(rcc::fillcfg(ctx, fn)); STAGE(rcc::filllive(ctx, fn)); STAGE(rcc::fillloop(fn)); STAGE(rcc::fillcost(ctx, fn)); STAGE(rcc::spill(ctx, fn)); STAGE(rcc::rega(ctx, fn)); STAGE(rcc::peephole(ctx, fn)); STAGE(rcc::fillcfg(ctx, fn)); STAGE(rcc::simpljmp(ctx, fn)); STAGE(rcc::fillcfg(ctx, fn)); #undef STAGE // Convert rpo[] to linked list for rv64_emitfn assert(fn->rpo[0] == fn->start); for (uint n = 0;; ++n) { if (n == fn->nblk - 1) { fn->rpo[n]->link = nullptr; break; } fn->rpo[n]->link = fn->rpo[n + 1]; } } } // namespace int main(int argc, char* argv[]) { auto args = parse_args(argc, argv); // Front-end: Lexical + Syntax Analysis → AST yyin = fopen(args.input.c_str(), "r"); if (!yyin) { fprintf(stderr, "rcc: error: cannot open '%s'\n", args.input.c_str()); return 1; } int parse_ret = yyparse(); fclose(yyin); if (parse_ret != 0 || !g_ast_root) { fprintf(stderr, "rcc: error: parsing failed\n"); return 1; } if (args.dump_ast) { rcc::ast_dump(g_ast_root); } // Target descriptor: rv64 lp64d. Assigned before worker creation, read-only afterwards. rcc::T = rcc::T_rv64; rcc::Ctx ctx_main; ctx_main.init(); ctx_main.subpool = new rcc::SubPool(); ctx_main.dbg_buf = stderr; // Dump destination for if (debug['X']) // IR Generation: AST → SSA IR (Braun algorithm) rcc::IRGen irgen(&ctx_main); auto ir = irgen.generate(g_ast_root); if (args.dump_ir) { fprintf(stderr, "[%zu functions, %zu globals]\n", ir.functions.size(), ir.globals.size()); } rcc::inline_module(&ctx_main, ir.functions); if (args.emit_il) { for (rcc::Fn* fn : ir.functions) run_midend(&ctx_main, fn); FILE* out = (args.output == "-") ? stdout : fopen(args.output.c_str(), "w"); if (!out) { fprintf(stderr, "rcc: error: cannot open '%s' for write\n", args.output.c_str()); return 1; } rcc::print_globals(ir.globals, out); for (rcc::Fn* fn : ir.functions) rcc::printfn(fn, out); if (out != stdout) fclose(out); } else { FILE* outf = nullptr; if (!args.dbg) { outf = (args.output == "-") ? stdout : fopen(args.output.c_str(), "w"); if (!outf) { fprintf(stderr, "rcc: error: cannot open '%s' for write\n", args.output.c_str()); return 1; } emit_globals_asm(ir.globals, outf); } for (rcc::Fn* fn : ir.functions) { if (args.dbg) fprintf(stderr, "**** Function %s ****", fn->name); run_midend(&ctx_main, fn); run_backend(&ctx_main, fn); if (!args.dbg) { rcc::set_die_ctx(&ctx_main); rcc::T.emitfn(fn, outf); fprintf(outf, "/* end function %s */\n\n", fn->name); } else { fprintf(stderr, "\n"); } } if (!args.dbg) { rcc::T.emitfin(&ctx_main, outf); if (outf != stdout) fclose(outf); } } // Clean AST delete g_ast_root; g_ast_root = nullptr; // Release SubPool; ~Ctx automatically frees insb ctx_main.release(); return 0; }