From ce900632d35aa193ee01ae4f957efe7123bbcea3 Mon Sep 17 00:00:00 2001 From: admin Date: Sun, 14 Dec 2025 16:53:06 +0100 Subject: [PATCH] feat: fasm stack code generator --- hello.asm | 35 -------- include/codegen.hpp | 136 ------------------------------- include/codegens/fasm_stack.hpp | 140 ++++++++++++++++++++++++++++++++ src/main.cpp | 3 +- test.asm | 39 --------- 5 files changed, 142 insertions(+), 211 deletions(-) delete mode 100644 hello.asm create mode 100644 include/codegens/fasm_stack.hpp delete mode 100644 test.asm diff --git a/hello.asm b/hello.asm deleted file mode 100644 index b3088ae..0000000 --- a/hello.asm +++ /dev/null @@ -1,35 +0,0 @@ -format ELF64 - -section '.text' executable - -extrn 'putchar' as __putchar -putchar = PLT __putchar - -public main - -main: - ; allocate space for locals (a and b: 4 bytes each → 8 bytes total) - push rbp - mov rbp, rsp - sub rsp, 8 - - ; local a = 34 → stored at [rbp - 4] - mov dword [rbp - 4], 34 - - ; local b = 35 → stored at [rbp - 8] - mov dword [rbp - 8], 35 - - ; compute a + b - mov eax, [rbp - 4] - add eax, [rbp - 8] - - ; call putchar(a + b) - ; SysV: first integer arg → EDI - mov edi, eax - call putchar - - ; return 0 from main - mov eax, 0 - - leave - ret \ No newline at end of file diff --git a/include/codegen.hpp b/include/codegen.hpp index d9cef8d..f660fa4 100644 --- a/include/codegen.hpp +++ b/include/codegen.hpp @@ -153,139 +153,3 @@ public: virtual void Generate(const char* filename, View ops) = 0; }; - -class StackFasmX86_64Generator : public CodeGenerator -{ -public: - ~StackFasmX86_64Generator() override = default; -private: - int GetStackSize(const IR::OpView ops) - { - int stackSize = 0; - for (auto &op : ops) - { - switch (op->GetType()) - { - case IR::OpType::STORE: - case IR::OpType::LOAD: - case IR::OpType::LOAD_CONST: - case IR::OpType::ADD: - case IR::OpType::CALL: - stackSize += 4; - } - } - return stackSize; - } - - StringView GetTempAddr(IR::Reg reg) - { - return std::move((StringBuilder() << 't' << reg).view()); - } -private: - void GenerateOp(const IR::Op *op) - { - switch (op->GetType()) - { - case IR::OpType::EXTERN: - { - auto extrn = reinterpret_cast(op); - auto symbol = extrn->symbol(); - printf("extrn '%s' as __%s\n", symbol.c_str(), symbol.c_str()); - printf("%s = PLT __%s\n", symbol.c_str(), symbol.c_str()); - } - break; - case IR::OpType::FN: - { - auto fn = reinterpret_cast(op); - auto name = fn->name(); - printf("public %s\n", name.c_str()); - printf("%s:\n", name.c_str()); - printf("push rbp\n"); - printf("mov rbp, rsp\n"); - m_stack = new StackAllocator(); - int stackSize = GetStackSize(fn->ops()); - printf("sub rsp, %d\n", stackSize); - if (fn->params().size > 0) - { - auto param_slot = m_stack->Allocate(fn->params().data[0]); - printf("mov [rbp-%d], edi\n", param_slot.offset); - } - for(auto &fOp : fn->ops()) - { - GenerateOp(fOp); - } - m_stack = nullptr; - printf("leave\nret\n"); - } - break; - case IR::OpType::CALL: - { - auto call = reinterpret_cast(op); - // TODO: support several arguments - if (call->args().size == 1) - { - auto slot = m_stack->Resolve(GetTempAddr(call->args().data[0])); - printf("mov edi, [rbp-%d]\n", slot.offset); - } - printf("call %s\n", call->callee().c_str()); - auto result_slot = m_stack->Allocate(GetTempAddr(call->result())); - printf("mov dword [rbp-%d], eax\n", result_slot.offset); - } - break; - case IR::OpType::LOAD_CONST: - { - auto lc = reinterpret_cast(op); - auto addr = GetTempAddr(lc->result()); - auto slot = m_stack->Allocate(addr); - printf("mov dword [rbp-%d], %ld\n", slot.offset, lc->value()); - } - break; - case IR::OpType::STORE: - { - auto s = reinterpret_cast(op); - printf("; DEBUG: resolving stack slot at %s\n", s->addr().c_str()); - auto slot = m_stack->Allocate(s->addr()); - auto value_slot = m_stack->Resolve(GetTempAddr(s->src())); - printf("mov eax, [rbp-%d]\n", value_slot.offset); - printf("mov dword [rbp-%d], eax\n", slot.offset); - } - break; - case IR::OpType::LOAD: - { - auto l = reinterpret_cast(op); - auto value_slot = m_stack->Allocate(GetTempAddr(l->result())); - auto variable_slot = m_stack->Resolve(l->addr()); - printf("mov eax, [rbp-%d]\n", variable_slot.offset); - printf("mov dword [rbp-%d], eax\n", value_slot.offset); - } - break; - case IR::OpType::ADD: - { - auto expr = reinterpret_cast(op); - auto lhs_slot = m_stack->Resolve(GetTempAddr(expr->lhs())); - printf("mov eax, [rbp-%d]\n", lhs_slot.offset); - auto rhs_slot = m_stack->Resolve(GetTempAddr(expr->rhs())); - printf("add eax, [rbp-%d]\n", rhs_slot.offset); - auto result_slot = m_stack->Allocate(GetTempAddr(expr->result())); - printf("mov dword [rbp-%d], eax\n", result_slot.offset); - } - break; - default: printf("; NOT HANDLED\n; %s\n", op->Format(0).c_str()); break; - } - } -public: - void Generate(const char* filename, View ops) override - { - printf("; fasm x86_64 linux generated assembly using pl\n"); - printf("format ELF64\n"); - printf("section '.text' executable\n"); - - for (auto& op : ops) - { - GenerateOp(op); - } - } -public: - // TODO: handle sub-blocks - StackAllocator* m_stack = nullptr; -}; diff --git a/include/codegens/fasm_stack.hpp b/include/codegens/fasm_stack.hpp new file mode 100644 index 0000000..2a82bb3 --- /dev/null +++ b/include/codegens/fasm_stack.hpp @@ -0,0 +1,140 @@ +#pragma once + +#include "codegen.hpp" + +class StackFasmX86_64Generator : public CodeGenerator +{ +public: + ~StackFasmX86_64Generator() override = default; +private: + int GetStackSize(const IR::OpView ops) + { + int stackSize = 0; + for (auto &op : ops) + { + switch (op->GetType()) + { + case IR::OpType::STORE: + case IR::OpType::LOAD: + case IR::OpType::LOAD_CONST: + case IR::OpType::ADD: + case IR::OpType::CALL: + stackSize += 4; + } + } + return stackSize; + } + + StringView GetTempAddr(IR::Reg reg) + { + return std::move((StringBuilder() << 't' << reg).view()); + } +private: + void GenerateOp(const IR::Op *op) + { + switch (op->GetType()) + { + case IR::OpType::EXTERN: + { + auto extrn = reinterpret_cast(op); + auto symbol = extrn->symbol(); + printf("extrn '%s' as __%s\n", symbol.c_str(), symbol.c_str()); + printf("%s = PLT __%s\n", symbol.c_str(), symbol.c_str()); + } + break; + case IR::OpType::FN: + { + auto fn = reinterpret_cast(op); + auto name = fn->name(); + printf("public %s\n", name.c_str()); + printf("%s:\n", name.c_str()); + printf("push rbp\n"); + printf("mov rbp, rsp\n"); + m_stack = new StackAllocator(); + int stackSize = GetStackSize(fn->ops()); + printf("sub rsp, %d\n", stackSize); + if (fn->params().size > 0) + { + auto param_slot = m_stack->Allocate(fn->params().data[0]); + printf("mov [rbp-%d], edi\n", param_slot.offset); + } + for(auto &fOp : fn->ops()) + { + GenerateOp(fOp); + } + operator delete(m_stack); + m_stack = nullptr; + printf("leave\nret\n"); + } + break; + case IR::OpType::CALL: + { + auto call = reinterpret_cast(op); + // TODO: support several arguments + if (call->args().size == 1) + { + auto slot = m_stack->Resolve(GetTempAddr(call->args().data[0])); + printf("mov edi, [rbp-%d]\n", slot.offset); + } + printf("call %s\n", call->callee().c_str()); + auto result_slot = m_stack->Allocate(GetTempAddr(call->result())); + printf("mov dword [rbp-%d], eax\n", result_slot.offset); + } + break; + case IR::OpType::LOAD_CONST: + { + auto lc = reinterpret_cast(op); + auto addr = GetTempAddr(lc->result()); + auto slot = m_stack->Allocate(addr); + printf("mov dword [rbp-%d], %ld\n", slot.offset, lc->value()); + } + break; + case IR::OpType::STORE: + { + auto s = reinterpret_cast(op); + printf("; DEBUG: resolving stack slot at %s\n", s->addr().c_str()); + auto slot = m_stack->Allocate(s->addr()); + auto value_slot = m_stack->Resolve(GetTempAddr(s->src())); + printf("mov eax, [rbp-%d]\n", value_slot.offset); + printf("mov dword [rbp-%d], eax\n", slot.offset); + } + break; + case IR::OpType::LOAD: + { + auto l = reinterpret_cast(op); + auto value_slot = m_stack->Allocate(GetTempAddr(l->result())); + auto variable_slot = m_stack->Resolve(l->addr()); + printf("mov eax, [rbp-%d]\n", variable_slot.offset); + printf("mov dword [rbp-%d], eax\n", value_slot.offset); + } + break; + case IR::OpType::ADD: + { + auto expr = reinterpret_cast(op); + auto lhs_slot = m_stack->Resolve(GetTempAddr(expr->lhs())); + printf("mov eax, [rbp-%d]\n", lhs_slot.offset); + auto rhs_slot = m_stack->Resolve(GetTempAddr(expr->rhs())); + printf("add eax, [rbp-%d]\n", rhs_slot.offset); + auto result_slot = m_stack->Allocate(GetTempAddr(expr->result())); + printf("mov dword [rbp-%d], eax\n", result_slot.offset); + } + break; + default: printf("; NOT HANDLED\n; %s\n", op->Format(0).c_str()); break; + } + } +public: + void Generate(const char* filename, View ops) override + { + printf("; fasm x86_64 linux generated assembly using pl\n"); + printf("format ELF64\n"); + printf("section '.text' executable\n"); + + for (auto& op : ops) + { + GenerateOp(op); + } + } +public: + // TODO: handle sub-blocks + StackAllocator* m_stack = nullptr; +}; diff --git a/src/main.cpp b/src/main.cpp index e9e7d82..22df138 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4,7 +4,8 @@ #include "lexer.hpp" #include "ast.hpp" #include "ir.hpp" -#include "codegen.hpp" + +#include "codegens/fasm_stack.hpp" void dump_tokens(const char* filename, Lexer* lexer) { diff --git a/test.asm b/test.asm deleted file mode 100644 index ae4f281..0000000 --- a/test.asm +++ /dev/null @@ -1,39 +0,0 @@ -format ELF64 - -section '.text' executable - -extrn 'putchar' as __putchar -putchar = PLT __putchar - -public main - -main: - ; create stack frame - push rbp - mov rbp, rsp - sub rsp, 8 ; space for locals: a (4 bytes), b (4 bytes) - - ; t0 = LOAD_CONST 34 - mov dword [rbp-4], 34 ; STORE "a", t0 - - ; t1 = LOAD_CONST 35 - mov dword [rbp-8], 35 ; STORE "b", t1 - - ; t2 = LOAD "a" - mov eax, [rbp-4] - - ; t3 = LOAD "b" - mov ecx, [rbp-8] - - ; t4 = ADD t2, t3 - add eax, ecx ; eax = t4 - - ; PARAM t4 → first arg in EDI - mov edi, eax - - ; t5 = CALL putchar - call putchar - - ; function return (SysV: rax contains return value from putchar) - leave - ret \ No newline at end of file