diff options
| author | Richard Qin <richard.qin.001@gmail.com> | 2026-09-15 08:16:43 +0800 |
|---|---|---|
| committer | Richard Qin <richard.qin.001@gmail.com> | 2026-09-15 08:16:43 +0800 |
| commit | 4022e5c95210631e4aceb08eaf9d9071aa5aefc4 (patch) | |
| tree | a5922559836e45fb3459a3d070367912e1035b1a /src/backend/emit.cc | |
| download | rcc-4022e5c95210631e4aceb08eaf9d9071aa5aefc4.tar.gz rcc-4022e5c95210631e4aceb08eaf9d9071aa5aefc4.tar.xz | |
Diffstat (limited to 'src/backend/emit.cc')
| -rw-r--r-- | src/backend/emit.cc | 304 |
1 files changed, 304 insertions, 0 deletions
diff --git a/src/backend/emit.cc b/src/backend/emit.cc new file mode 100644 index 0000000..6e1f971 --- /dev/null +++ b/src/backend/emit.cc @@ -0,0 +1,304 @@ +/* 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 backend/emit.cc +/// @brief Target-independent assembly/data/debug section output tools. + +#include "rcc/all.h" + +#include <cinttypes> +#include <cstring> +#include <vector> + +namespace rcc { + +// Section types. + +namespace { + +enum Sec : int { kSecText = 0, kSecData = 1, kSecBss = 2 }; + +// Given (sec, thread), return the name/instruction string after .section. +// When thread=true, SecText is unreachable. +const char* section_name(int sec, bool thread) { + if (!thread) { + switch (sec) { + case kSecText: + return ".text"; + case kSecData: + return ".data"; + case kSecBss: + return ".bss"; + } + } else { + switch (sec) { + case kSecText: + return ".abort \"unreachable\""; + case kSecData: + return ".section .tdata,\"awT\""; + case kSecBss: + return ".section .tbss,\"awT\""; + } + } + return nullptr; // unreachable +} + +// Write ".globl / label / section switch" header. Apple branch has TLV trampolines. +void write_lnk(char* n, Lnk* l, int sec_kind, FILE* f) { + const char* pfx = (n[0] == '"') ? "" : T.assym; + const char* sfx = ""; + + if (T.apple && l->thread) { + l->sec = const_cast<char*>("__DATA"); + l->secf = const_cast<char*>("__thread_data,thread_local_regular"); + sfx = "$tlv$init"; + fputs(".section __DATA,__thread_vars,thread_local_variables\n", f); + fprintf(f, "%s%s:\n", pfx, n); + fprintf(f, + "\t.quad __tlv_bootstrap\n" + "\t.quad 0\n" + "\t.quad %s%s%s\n\n", + pfx, n, sfx); + } + + if (l->sec) { + fprintf(f, ".section %s", l->sec); + if (l->secf) + fprintf(f, ",%s", l->secf); + } else { + fputs(section_name(sec_kind, l->thread != 0), f); + } + fputc('\n', f); + + if (l->align) + fprintf(f, ".balign %d\n", l->align); + if (l->export_) + fprintf(f, ".globl %s%s\n", pfx, n); + fprintf(f, "%s%s%s:\n", pfx, n, sfx); +} + +} // namespace + +void emitfnlnk(char* n, Lnk* l, FILE* f) { + write_lnk(n, l, kSecText, f); +} + +// Data sections. + +namespace { +int64_t g_data_zero = 0; + +struct DataDecl { + const char* decl; + int64_t mask; +}; + +constexpr DataDecl kDataDecl[] = { + /* DStart */ {nullptr, 0}, + /* DEnd */ {nullptr, 0}, + /* DB */ {"\t.byte", 0xffLL}, + /* DH */ {"\t.short", 0xffffLL}, + /* DW */ {"\t.int", 0xffffffffLL}, + /* DL */ {"\t.quad", -1LL}, + /* DZ */ {nullptr, 0}, +}; + +} // namespace + +void emitdat(Dat* d, FILE* f) { + switch (d->type) { + case Dat::DStart: + g_data_zero = 0; + return; + + case Dat::DEnd: + if (d->lnk->common) { + if (g_data_zero == -1) + die("invalid common data definition"); + const char* p = (d->name[0] == '"') ? "" : T.assym; + fprintf(f, ".comm %s%s,%" PRId64, p, d->name, g_data_zero); + if (d->lnk->align) + fprintf(f, ",%d", d->lnk->align); + fputc('\n', f); + } else if (g_data_zero != -1) { + write_lnk(d->name, d->lnk, kSecBss, f); + fprintf(f, "\t.fill %" PRId64 ",1,0\n", g_data_zero); + } + return; + + case Dat::DZ: + if (g_data_zero != -1) + g_data_zero += d->u.num; + else + fprintf(f, "\t.fill %" PRId64 ",1,0\n", d->u.num); + return; + + case Dat::DB: + case Dat::DH: + case Dat::DW: + case Dat::DL: + break; + } + + // Switch to .data and flush accumulated zeros on first non-zero content. + if (g_data_zero != -1) { + write_lnk(d->name, d->lnk, kSecData, f); + if (g_data_zero > 0) + fprintf(f, "\t.fill %" PRId64 ",1,0\n", g_data_zero); + g_data_zero = -1; + } + + const DataDecl& dd = kDataDecl[d->type]; + if (d->isstr) { + if (d->type != Dat::DB) + err(const_cast<char*>("strings only supported for 'b' currently")); + fprintf(f, "\t.ascii %s\n", d->u.str); + } else if (d->isref) { + const char* p = (d->u.ref.name[0] == '"') ? "" : T.assym; + fprintf(f, "%s %s%s%+" PRId64 "\n", dd.decl, p, d->u.ref.name, d->u.ref.off); + } else { + fprintf(f, "%s %" PRId64 "\n", dd.decl, d->u.num & dd.mask); + } +} + +// FP constant pool (stashbits + emitfin). + +struct Asmbits { + bits n; + int size; + Asmbits* link; +}; + +int stashbits(Ctx* ctx, bits n, int size) { + assert(size == 4 || size == 8 || size == 16); + Asmbits** pb; + Asmbits* b; + int i = 0; + for (pb = &ctx->emit_stash; (b = *pb); pb = &b->link, ++i) { + if (size <= b->size && b->n == n) + return i; + } + b = static_cast<Asmbits*>(emalloc(sizeof(Asmbits))); + b->n = n; + b->size = size; + b->link = nullptr; + *pb = b; + return i; +} + +namespace { + +// Layout by .quad / .quad 0 (16B) / .quad (8B) / .int (4B). +void emit_fp_pool(Ctx* ctx, FILE* f, const char* sec[3]) { + if (!ctx->emit_stash) + return; + fprintf(f, "/* floating point constants */\n"); + + union { + int32_t i; + float f; + } u; + + // Three passes from largest to smallest (16, 8, 4). + for (int lg = 4; lg >= 2; --lg) { + int width = 1 << lg; + int i = 0; + for (Asmbits* b = ctx->emit_stash; b; b = b->link, ++i) { + if (b->size != width) + continue; + fprintf(f, + ".section %s\n" + ".p2align %d\n" + "%sfp%d:", + sec[lg - 2], lg, T.asloc, i); + if (lg == 4) { + fprintf(f, "\n\t.quad %" PRId64 "\n\t.quad 0\n\n", static_cast<int64_t>(b->n)); + } else if (lg == 3) { + double dv; + memcpy(&dv, &b->n, sizeof(dv)); + fprintf(f, "\n\t.quad %" PRId64 " /* %f */\n\n", static_cast<int64_t>(b->n), dv); + } else /* lg == 2 */ { + u.i = static_cast<int32_t>(b->n); + fprintf(f, "\n\t.int %" PRId32 " /* %f */\n\n", u.i, static_cast<double>(u.f)); + } + } + } + + // Free linked list. + while (Asmbits* b = ctx->emit_stash) { + ctx->emit_stash = b->link; + free(b); + } +} + +} // namespace + +void elf_emitfin(Ctx* ctx, FILE* f) { + static const char* sec[3] = {".rodata", ".rodata", ".rodata"}; + emit_fp_pool(ctx, f, sec); + fprintf(f, ".section .note.GNU-stack,\"\",@progbits\n"); +} + +void elf_emitfnfin(char* fn, FILE* f) { + fprintf(f, ".type %s, @function\n", fn); + fprintf(f, ".size %s, .-%s\n", fn, fn); +} + +void macho_emitfin(Ctx* ctx, FILE* f) { + static const char* sec[3] = { + "__TEXT,__literal4,4byte_literals", + "__TEXT,__literal8,8byte_literals", + "__TEXT,__literal16,16byte_literals", + }; + emit_fp_pool(ctx, f, sec); +} + +void pe_emitfin(Ctx* ctx, FILE* f) { + static const char* sec[3] = {".rodata", ".rodata", ".rodata"}; + emit_fp_pool(ctx, f, sec); +} + +// DWARF .file / .loc. + +namespace { +std::vector<uint32_t> g_dbg_files; // intern id sequence. +uint32_t g_dbg_curfile = 0; // 1-based (gas requires > 0). +} // namespace + +void emitdbgfile(char* fn, FILE* f) { + uint32_t id = intern(fn); + for (size_t i = 0; i < g_dbg_files.size(); ++i) { + if (g_dbg_files[i] == id) { + g_dbg_curfile = static_cast<uint32_t>(i + 1); + return; + } + } + g_dbg_files.push_back(id); + g_dbg_curfile = static_cast<uint32_t>(g_dbg_files.size()); + fprintf(f, ".file %u %s\n", g_dbg_curfile, fn); +} + +void emitdbgloc(uint line, uint col, FILE* f) { + if (col != 0) + fprintf(f, "\t.loc %u %u %u\n", g_dbg_curfile, line, col); + else + fprintf(f, "\t.loc %u %u\n", g_dbg_curfile, line); +} + +} // namespace rcc |
