From 3b8dfc4dae881759146c36c5e98371ba3ce3a5a4 Mon Sep 17 00:00:00 2001 From: admin Date: Fri, 26 Dec 2025 22:15:54 +0100 Subject: [PATCH] feat: plan for register allocation + temp fix for 'E' not being printed --- TODO.md | 20 +++++++++++++++++ include/codegen/fasm_stack.hpp | 36 +++++++++++++++++++----------- src/main.cpp | 40 ++++++++++++++++++++++++---------- 3 files changed, 71 insertions(+), 25 deletions(-) create mode 100644 TODO.md diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..4881061 --- /dev/null +++ b/TODO.md @@ -0,0 +1,20 @@ + +0. Define how many physical regs are available, which are caller-saved and callee-saved +1. Implement if-else and while statements +2. Find out what use[B] and def[B] are for each block +3. Define successors and predecessors of each block and make them accessible +4. Compute liveIn[B] and liveOut[B] for each block + + Classic equations: + + liveIn[B] = use[B] ∪ (liveOut[B] - def[B]) + + liveOut[B] = ⋃ liveIn[S] for S in succ[B] +5. Linear scan register allocation + + +- Instruction numbering + per-instruction liveness +- Live intervals +- Linear scan allocator with ~6 fake regs +- Spill to stack slots +- Add “CALL clobbers regs” rule \ No newline at end of file diff --git a/include/codegen/fasm_stack.hpp b/include/codegen/fasm_stack.hpp index 7f93e47..a6146e4 100644 --- a/include/codegen/fasm_stack.hpp +++ b/include/codegen/fasm_stack.hpp @@ -8,32 +8,36 @@ class StackFasmX86_64Generator : public CodeGenerator { public: ~StackFasmX86_64Generator() override = default; + private: StringView GetTempAddr(IR::Value reg) { return std::move((StringBuilder() << reg.Format()).view()); } - StringView GetSlotAddr(const IR::IRSlot& slot) + StringView GetSlotAddr(const IR::IRSlot &slot) { switch (slot.GetType()) { case IR::IRSlot::Type::REGISTRY: { StringBuilder sb; - sb.AppendFormat("r%d", slot.GetSlot() + 10); // for r10, r11, r12 etc. + sb.AppendFormat("r%d", slot.GetSlot() + 12); // for r10, r11, r12 etc. return sb.view(); - } break; + } + break; case IR::IRSlot::Type::STACK: { StringBuilder sb; sb.AppendFormat("[rbp-%d]", slot.GetSlot()); // for r10, r11, r12 etc. return sb.view(); - } break; + } + break; default: assert(0 && "TODO: either unreachable or handle properly"); } } + private: void GenerateOp(const IR::Op *op) { @@ -59,16 +63,16 @@ private: appendf("mov rbp, rsp\n"); StringBuilder fnOutput; - StringBuilder* backup = m_output; + StringBuilder *backup = m_output; m_output = &fnOutput; - + if (fn->params().size > 0) { // TODO: support multiple parameters auto param_slot = m_allocator->Allocate(fn->params().data[0]); appendf("mov %s, rdi\n", GetSlotAddr(param_slot).c_str()); } - for(auto &fOp : fn->body().ops()) + for (auto &fOp : fn->body().ops()) { GenerateOp(fOp); } @@ -135,11 +139,13 @@ private: appendf("mov %s, rax\n", GetSlotAddr(result_slot).c_str()); } break; - default: appendf("; NOT HANDLED\n; %s\n", op->Format(0).c_str()); break; + default: + appendf("; NOT HANDLED\n; %s\n", op->Format(0).c_str()); + break; } } - void appendf(const char * fmt, ...) + void appendf(const char *fmt, ...) { assert(m_output != nullptr && "nowhere to write"); va_list args; @@ -147,25 +153,29 @@ private: m_output->VAppendFormat(fmt, args); va_end(args); } + public: StringView GetOutput() { return m_output->view(); } + private: - StringBuilder* m_output = nullptr; + StringBuilder *m_output = nullptr; + public: - void Generate(const char* filename, View ops) override + void Generate(const char *filename, View ops) override { m_output = new StringBuilder(); appendf("; fasm x86_64 linux generated assembly using pl\n"); appendf("format ELF64\n"); appendf("section '.text' executable\n"); - for (auto& op : ops) + for (auto &op : ops) { GenerateOp(op); } } + public: // StackAllocator* m_stack = nullptr; // TODO: handle sub-blocks - SlotAllocator* m_allocator = nullptr; + SlotAllocator *m_allocator = nullptr; }; diff --git a/src/main.cpp b/src/main.cpp index 9de7527..1df4924 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,5 +1,7 @@ #include #include +#include +#include #include #include "parser/lexer.hpp" #include "parser/ast.hpp" @@ -7,31 +9,36 @@ #include "codegen/fasm_stack.hpp" -void dump_tokens(const char* filename, Lexer* lexer) +void dump_tokens(const char *filename, Lexer *lexer) { - while (lexer->NextToken()) { + while (lexer->NextToken()) + { std::print("{}:{}:{}: ", filename, lexer->token().line_number, lexer->token().offset_start); if (lexer->token().token == TokenType::Id) std::println("id = {}", lexer->token().string); else if (lexer->token().token == TokenType::IntLiteral) std::println("int = {}", lexer->token().int_number); - else + else std::println("token = {}", (char)lexer->token().token); } } -int main(int argc, char** argv) +int main(int argc, char **argv) { - char* filename; - if (argc > 1) { + char *filename; + if (argc > 1) + { filename = (++argv)[0]; - } else { + } + else + { fprintf(stderr, "ERROR: Input file is required.\n"); return 1; } std::ifstream f(filename); - if (!f.is_open()) { + if (!f.is_open()) + { fprintf(stderr, "ERROR: Failed to open input file: %s\n", filename); return 1; } @@ -41,7 +48,7 @@ int main(int argc, char** argv) f.close(); Lexer lexer(filename, StringView(content.c_str())); - + AstParser parser(&lexer); auto program = parser.Parse(); @@ -55,11 +62,20 @@ int main(int argc, char** argv) printf("%s\n", ops.data[i]->Format(0).c_str()); } - // StackFasmX86_64Generator gen; + StackFasmX86_64Generator gen; - // gen.Generate(filename, ops); + gen.Generate(filename, ops); - // printf("%s\n", gen.GetOutput().c_str()); + StringView output = gen.GetOutput(); + + FILE *file = fopen("out.asm", "w"); + + fwrite(output.c_str(), output.size - 1, sizeof(char), file); + + fclose(file); + + system("fasm out.asm"); + system("gcc -o out out.o"); return 0; }