From 952df07ce31451d31c6017014be94dfb28e1a029 Mon Sep 17 00:00:00 2001 From: admin Date: Mon, 1 Dec 2025 13:59:30 +0100 Subject: [PATCH] feat: support function params + stack based compiler! --- example.rx | 9 +- include/ast.hpp | 23 ++++- include/codegen.hpp | 223 ++++++++++++++++++++++++-------------------- include/ir.hpp | 15 ++- src/ir.cpp | 4 +- 5 files changed, 156 insertions(+), 118 deletions(-) diff --git a/example.rx b/example.rx index b062790..e22fc31 100644 --- a/example.rx +++ b/example.rx @@ -1,15 +1,18 @@ extern putchar -fn main() { +fn hello() { local h = 72 local e = 69 local l = 76 local o = 79 - local nl = 10 putchar(h) putchar(e) putchar(l) putchar(l) putchar(o) - putchar(nl) +} + +fn main() { + hello() + putchar(10) } \ No newline at end of file diff --git a/include/ast.hpp b/include/ast.hpp index b5e6b00..a126268 100644 --- a/include/ast.hpp +++ b/include/ast.hpp @@ -126,8 +126,8 @@ class FnDeclNode : public Node { public: // TODO: support parameters - FnDeclNode(const StringView& name, CompoundNode* body) - : m_name(name), m_body(body) {} + FnDeclNode(const StringView& name, CompoundNode* body, View params) + : m_name(name), m_body(body), m_params(params) {} ~FnDeclNode() override { delete m_body; } @@ -136,9 +136,11 @@ public: public: const StringView& name() const { return m_name; } const CompoundNode* body() const { return m_body; } + const View& params() const { return m_params; } private: StringView m_name; CompoundNode* m_body; + View m_params; }; class FnCallNode : public Node @@ -236,7 +238,13 @@ public: m_lexer->NextExpect(TokenType::Id); StringView name = m_lexer->token().string; m_lexer->NextExpect('('); - // TODO: parse parameters + Builder params; + // TODO: support multiple params + if (m_lexer->seek_token()->token != ')') + { + m_lexer->NextExpect(TokenType::Id); + params.Push(m_lexer->token().string); + } m_lexer->NextExpect(')'); m_lexer->NextExpect('{'); auto compound = new CompoundNode(); @@ -245,7 +253,7 @@ public: compound->addNode(ParseStatement()); } m_lexer->NextExpect('}'); - return new FnDeclNode(name, compound); + return new FnDeclNode(name, compound, params.view()); } FnCallNode* ParseFnCall(const StringView& name) @@ -253,7 +261,12 @@ public: // m_lexer->NextExpect(TokenType::Id); // char* name = strdup(m_lexer->token().string); m_lexer->NextExpect('('); - Node* arg = ParseExpression(); + Node* arg = nullptr; + // TODO: support multiple arguments + if (m_lexer->seek_token()->token != ')') + { + arg = ParseExpression(); + } m_lexer->NextExpect(')'); return new FnCallNode(name, arg); } diff --git a/include/codegen.hpp b/include/codegen.hpp index 73ccd11..d9cef8d 100644 --- a/include/codegen.hpp +++ b/include/codegen.hpp @@ -53,98 +53,98 @@ private: Builder m_slots; }; -struct ConstSlot -{ - long value; - StringView addr; -}; +// struct ConstSlot +// { +// long value; +// StringView addr; +// }; -class ConstAllocator : public Allocator -{ -public: - ConstAllocator() = default; - ~ConstAllocator() override = default; -public: - const ConstSlot& Allocate(const StringView& addr) - { - m_slots.Push(ConstSlot { 0, addr }); - return m_slots.data[m_slots.size - 1]; - } +// class ConstAllocator : public Allocator +// { +// public: +// ConstAllocator() = default; +// ~ConstAllocator() override = default; +// public: +// const ConstSlot& Allocate(const StringView& addr) +// { +// m_slots.Push(ConstSlot { 0, addr }); +// return m_slots.data[m_slots.size - 1]; +// } - const ConstSlot& StoreValue(const StringView& addr, long value) - { - for (size_t i = 0; i < m_slots.size; ++i) - { - if (strcmp(m_slots.data[i].addr.c_str(), addr.c_str()) == 0) - { - m_slots.data[i].value = value; - return m_slots.data[i]; - } - } +// const ConstSlot& StoreValue(const StringView& addr, long value) +// { +// for (size_t i = 0; i < m_slots.size; ++i) +// { +// if (strcmp(m_slots.data[i].addr.c_str(), addr.c_str()) == 0) +// { +// m_slots.data[i].value = value; +// return m_slots.data[i]; +// } +// } - assert(0 && "could not resolve const under specified address"); - } +// assert(0 && "could not resolve const under specified address"); +// } - const ConstSlot& Resolve(const StringView& addr) - { - for (size_t i = 0; i < m_slots.size; ++i) - { - if (strcmp(m_slots.data[i].addr.c_str(), addr.c_str()) == 0) - { - return m_slots.data[i]; - } - } +// const ConstSlot& Resolve(const StringView& addr) +// { +// for (size_t i = 0; i < m_slots.size; ++i) +// { +// if (strcmp(m_slots.data[i].addr.c_str(), addr.c_str()) == 0) +// { +// return m_slots.data[i]; +// } +// } - assert(0 && "could not resolve const under specified address"); - } -private: - Builder m_slots; -}; +// assert(0 && "could not resolve const under specified address"); +// } +// private: +// Builder m_slots; +// }; -struct RegisterSlot -{ - const StringView& reg; - StringView addr; -}; +// struct RegisterSlot +// { +// const StringView& reg; +// StringView addr; +// }; -class RegisterAllocator : public Allocator -{ -public: - RegisterAllocator() - { - m_regs.Push(std::move(StringView("eax"))); - m_regs.Push(std::move(StringView("ecx"))); - } - ~RegisterAllocator() override = default; -public: - const RegisterSlot& Allocate(const StringView& addr) - { - assert(m_slots.size < m_regs.size && "no space available for allocating to register"); - m_slots.Push(RegisterSlot { m_regs.data[m_slots.size], addr }); - return m_slots.data[m_slots.size - 1]; - } +// class RegisterAllocator : public Allocator +// { +// public: +// RegisterAllocator() +// { +// m_regs.Push(std::move(StringView("eax"))); +// m_regs.Push(std::move(StringView("ecx"))); +// } +// ~RegisterAllocator() override = default; +// public: +// const RegisterSlot& Allocate(const StringView& addr) +// { +// assert(m_slots.size < m_regs.size && "no space available for allocating to register"); +// m_slots.Push(RegisterSlot { m_regs.data[m_slots.size], addr }); +// return m_slots.data[m_slots.size - 1]; +// } - const RegisterSlot& Resolve(const StringView& addr) - { - for (size_t i = 0; i < m_slots.size; ++i) - { - if (strcmp(m_slots.data[i].addr.c_str(), addr.c_str()) == 0) - { - return m_slots.data[i]; - } - } +// const RegisterSlot& Resolve(const StringView& addr) +// { +// for (size_t i = 0; i < m_slots.size; ++i) +// { +// if (strcmp(m_slots.data[i].addr.c_str(), addr.c_str()) == 0) +// { +// return m_slots.data[i]; +// } +// } - assert(0 && "could not resolve register for specified address"); - } +// assert(0 && "could not resolve register for specified address"); +// } - void Clear() - { - m_slots.size = 0; - } -private: - Builder m_slots; - Builder m_regs; -}; +// void Clear() +// { +// m_slots.size = 0; +// } +// private: +// Builder m_slots; +// Builder m_regs; +// }; class CodeGenerator { @@ -164,7 +164,15 @@ private: int stackSize = 0; for (auto &op : ops) { - if (op->GetType() == IR::OpType::STORE) stackSize += 4; + 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; } @@ -194,12 +202,19 @@ private: 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; @@ -209,46 +224,50 @@ private: // TODO: support several arguments if (call->args().size == 1) { - auto reg_slot = m_registers.Resolve(GetTempAddr(call->args().data[0])); - printf("mov edi, %s\n", reg_slot.reg.c_str()); + 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()); - m_registers.Clear(); + 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()); - m_consts.Allocate(addr); - m_consts.StoreValue(addr, lc->value()); + 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 = m_consts.Resolve(GetTempAddr(s->src())); - printf("mov dword [rbp-%d], %ld\n", slot.offset, value.value); + 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 reg_slot = m_registers.Allocate(GetTempAddr(l->result())); - auto stack_slot = m_stack.Resolve(l->addr()); - printf("mov %s, [rbp-%d]\n", reg_slot.reg.c_str(), stack_slot.offset); + 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_registers.Resolve(GetTempAddr(expr->lhs())); - auto rhs_slot = m_registers.Resolve(GetTempAddr(expr->rhs())); - printf("add %s, %s\n", lhs_slot.reg.c_str(), rhs_slot.reg.c_str()); - m_registers.Clear(); - m_registers.Allocate(GetTempAddr(expr->result())); + 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; @@ -268,7 +287,5 @@ public: } public: // TODO: handle sub-blocks - StackAllocator m_stack; - ConstAllocator m_consts; - RegisterAllocator m_registers; -}; \ No newline at end of file + StackAllocator* m_stack = nullptr; +}; diff --git a/include/ir.hpp b/include/ir.hpp index 150bad0..74a9e7d 100644 --- a/include/ir.hpp +++ b/include/ir.hpp @@ -74,7 +74,7 @@ private: class FnOp : public Op { public: - FnOp(StringView name, const CompoundNode* body); + FnOp(StringView name, const CompoundNode* body, const View& params); ~FnOp() {} OP_TYPE(FN) @@ -93,9 +93,11 @@ public: public: const StringView& name() const { return m_name; } const OpView& ops() const { return m_ops; } + const View& params() const { return m_params; } private: StringView m_name; OpView m_ops; + View m_params; }; class LoadConstOp : public Op, public Valued @@ -243,11 +245,14 @@ public: Reg ParseFnCall(const FnCallNode* fn) { // TODO: support multiple args - auto arg = ParseExpression(fn->arg()); auto argRegs = RegBuilder(); - argRegs.Push(arg); + if (fn->arg() != nullptr) + { + auto arg = ParseExpression(fn->arg()); + argRegs.Push(arg); + } auto dst = AllocateRegister(); - m_ops.Push(new CallOp(dst, fn->name(), RegView(argRegs.data, argRegs.size))); + m_ops.Push(new CallOp(dst, fn->name(), argRegs.view())); return dst; } @@ -319,7 +324,7 @@ public: // Functions for (auto &fn : program->funcs()) { - m_ops.Push(new FnOp(fn->name(), fn->body())); + m_ops.Push(new FnOp(fn->name(), fn->body(), fn->params())); } return OpView(m_ops.data, m_ops.size); diff --git a/src/ir.cpp b/src/ir.cpp index 6e7ba8a..f4d93b3 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -1,7 +1,7 @@ #include "ir.hpp" -IR::FnOp::FnOp(StringView name, const CompoundNode* body) - : m_name(name) +IR::FnOp::FnOp(StringView name, const CompoundNode* body, const View& params) + : m_name(name), m_params(params) { IRBuilder ir(body); // Now IRBuilder is complete → OK ir.ParseBlock(body);