From 589586db516b9fe33e0ddf2f8843b70817a772f3 Mon Sep 17 00:00:00 2001 From: admin Date: Tue, 6 Jan 2026 16:39:51 +0100 Subject: [PATCH] feat: migrate from `OpView` to `DoubleLinkedList` + multiple funciton arguments --- TODO.md | 2 +- example.rx | 6 +- include/codegen/codegen.hpp | 3 +- include/codegen/targets/fasm_x86_64_linux.hpp | 116 ++++++++++++------ include/ir/block.hpp | 4 +- include/ir/ir.hpp | 48 ++++---- include/parser/ast.hpp | 11 +- include/parser/nodes.hpp | 17 ++- include/prelude/linkedlist.hpp | 8 +- src/main.cpp | 10 +- 10 files changed, 136 insertions(+), 89 deletions(-) diff --git a/TODO.md b/TODO.md index 61c7619..a3c8e1e 100644 --- a/TODO.md +++ b/TODO.md @@ -53,4 +53,4 @@ function's reserved registers: return registers: int => EAX - float => ST0 \ No newline at end of file + float => ST0 diff --git a/example.rx b/example.rx index e5db68f..16ebc55 100644 --- a/example.rx +++ b/example.rx @@ -1,4 +1,5 @@ extern putchar +extern iadd fn hello() { local h = 72 @@ -14,5 +15,6 @@ fn hello() { fn main() { hello() - putchar(3 * 3 + 1) -} \ No newline at end of file + local newline = iadd(3*3, 1) + putchar(newline) +} diff --git a/include/codegen/codegen.hpp b/include/codegen/codegen.hpp index 11fa21a..4d12ccf 100644 --- a/include/codegen/codegen.hpp +++ b/include/codegen/codegen.hpp @@ -1,5 +1,6 @@ #pragma once #include "ir/op.hpp" +#include "prelude/linkedlist.hpp" #include "prelude/string.hpp" class CodeGenerator @@ -9,7 +10,7 @@ public: virtual ~CodeGenerator() {} public: - virtual bool Generate(const IR::OpView* ops) = 0; + virtual bool Generate(const DoubleLinkedList* ops) = 0; StringView GetOutput() { return output().view(); } protected: diff --git a/include/codegen/targets/fasm_x86_64_linux.hpp b/include/codegen/targets/fasm_x86_64_linux.hpp index e957d92..c1bb13f 100644 --- a/include/codegen/targets/fasm_x86_64_linux.hpp +++ b/include/codegen/targets/fasm_x86_64_linux.hpp @@ -13,30 +13,35 @@ public: FasmX86_64Generator() = default; public: - bool Generate(const IR::OpView* ops) override + bool Generate(const DoubleLinkedList* ops) override { + m_ops = ops; + output().Extend("format ELF64\n"); output().Extend("section '.text' executable\n"); - for (size_t i = 0; i < ops->size; ++i) + for (m_current = m_ops->Begin(); m_current != nullptr; node_next()) { - GenerateStatement(ops->data[i]); + GenerateStatement(); } return true; } private: - void GenerateExtern(IR::ExternOp* extrn) + void GenerateExtern() { + auto extrn = current(); // TODO: instead of __symbol().c_str(), extrn->symbol().c_str()); output().AppendFormat("%s = PLT __%s\n", extrn->symbol().c_str(), extrn->symbol().c_str()); } - void GenerateFunction(IR::FnOp* fn) + void GenerateFunction() { + auto fn = current(); + m_slots.clear(); m_stackCounter = 0; @@ -45,17 +50,23 @@ private: output().Extend(" push rbp\n"); output().Extend(" mov rbp, rsp\n"); - for (auto cur = fn->body().ops().Begin(); cur != nullptr; cur = cur->next) + auto fnNode = node(); + auto ops = m_ops; + m_ops = &fn->body().ops(); + for (m_current = m_ops->Begin(); m_current != nullptr; node_next()) { - GenerateStatement(cur->value); + GenerateStatement(); } + m_ops = ops; + m_current = fnNode; output().Extend(" leave\n"); output().Extend(" ret\n"); } - void GenerateCall(IR::CallOp* call) + void GenerateCall() { + auto call = current(); // TODO: support stack spilled arguments assert(call->args().size < 7 && "stack arguments not supported yet"); const char *regs[6] = {"edi", "esi", "edx", "ecx","e8", "e9"}; @@ -77,16 +88,29 @@ private: output().AppendFormat(" mov %s [rbp-%d], eax\n", size, sp); } - void GenerateAllocate(IR::AllocateOp* alloc) + void GenerateAllocate() { - // TODO: support other types - assert(alloc->Type()->kind == IR::ValueHandle::Type::Kind::Int); + auto totalAllocSize = 0; + while (current()->GetType() == IR::OpType::ALLOCATE) + { + auto alloc = current(); + // TODO: support other types + assert(alloc->Type()->kind == IR::ValueHandle::Type::Kind::Int); + // TODO: dynamic size + alignment + auto allocSize = 4; + totalAllocSize += allocSize; + m_stackCounter += allocSize; + m_slots.insert(std::make_pair(alloc->result()->GetId(), m_stackCounter)); - EnsureSlot(alloc->result()); + if (seek() && seek()->get()->GetType() == IR::OpType::ALLOCATE) node_next(); + else break; + }; + output().AppendFormat(" sub rsp, %d\n", totalAllocSize); } - void GenerateStore(IR::StoreOp* store) + void GenerateStore() { + auto store = current(); auto sp = EnsureSlot(store->dst()); // TODO: support other types @@ -103,8 +127,9 @@ private: } } - void GenerateLoad(IR::LoadOp* load) + void GenerateLoad() { + auto load = current(); auto sp = EnsureSlot(load->Ptr()); // TODO: support other types @@ -115,8 +140,9 @@ private: output().AppendFormat(" mov dword [rbp-%d], eax\n", sp); } - void GenerateMath(IR::MathOp* math) + void GenerateMath() { + auto math = current(); StringBuilder sb; switch(math->GetType()) @@ -157,30 +183,31 @@ private: output().AppendFormat(" mov %s [rbp-%d], eax\n", size, sp); } - void GenerateStatement(IR::Op* op) + void GenerateStatement() { - switch(op->GetType()) - { - case IR::OpType::EXTERN: - return GenerateExtern(reinterpret_cast(op)); - case IR::OpType::FN: - return GenerateFunction(reinterpret_cast(op)); - case IR::OpType::CALL: - return GenerateCall(reinterpret_cast(op)); - case IR::OpType::ALLOCATE: - return GenerateAllocate(reinterpret_cast(op)); - case IR::OpType::STORE: - return GenerateStore(reinterpret_cast(op)); - case IR::OpType::LOAD: - return GenerateLoad(reinterpret_cast(op)); - case IR::OpType::ADD: - case IR::OpType::SUB: - case IR::OpType::MUL: - case IR::OpType::DIV: - return GenerateMath(reinterpret_cast(op)); - // TODO: - default: output().AppendFormat(" ; %d not implemented\n", op->GetType()); - } + auto op = current(); + switch(op->GetType()) + { + case IR::OpType::EXTERN: + return GenerateExtern(); + case IR::OpType::FN: + return GenerateFunction(); + case IR::OpType::CALL: + return GenerateCall(); + case IR::OpType::ALLOCATE: + return GenerateAllocate(); + case IR::OpType::STORE: + return GenerateStore(); + case IR::OpType::LOAD: + return GenerateLoad(); + case IR::OpType::ADD: + case IR::OpType::SUB: + case IR::OpType::MUL: + case IR::OpType::DIV: + return GenerateMath(); + // TODO: + default: output().AppendFormat(" ; %d not implemented\n", op->GetType()); + } } private: @@ -202,6 +229,19 @@ private: } private: + template + ListNode* node() { return reinterpret_cast*>(m_current); } + template + ListNode* seek() { return m_current && m_current->next ? reinterpret_cast*>(m_current->next) : nullptr; } + + void node_next() { assert(m_current); m_current = m_current->next; } + + template + T* current() const { return reinterpret_cast(m_current->value); } + +private: + const DoubleLinkedList* m_ops; + ListNode* m_current; std::unordered_map m_slots; uint32_t m_stackCounter = 0; }; diff --git a/include/ir/block.hpp b/include/ir/block.hpp index da8ba10..7684b4f 100644 --- a/include/ir/block.hpp +++ b/include/ir/block.hpp @@ -10,8 +10,8 @@ using BlockID = unsigned int; class Block { public: - Block(BlockID id, const OpView& ops) - : m_id(id), m_ops(DoubleLinkedList::FromView(ops)) {} + Block(BlockID id, DoubleLinkedList ops) + : m_id(id), m_ops(ops) {} public: DoubleLinkedList& ops() { return m_ops; } public: diff --git a/include/ir/ir.hpp b/include/ir/ir.hpp index 249386c..4fad0d3 100644 --- a/include/ir/ir.hpp +++ b/include/ir/ir.hpp @@ -2,6 +2,7 @@ #include #include "parser/nodes.hpp" #include "prelude/error.hpp" +#include "prelude/linkedlist.hpp" #include "prelude/string.hpp" #include "ir/value.hpp" #include "ir/ops.hpp" @@ -13,7 +14,7 @@ namespace IR { public: IRBuilder(const StringView &filename, const Node *root) - : m_root(root), m_ops(new OpBuilder()), m_filename(filename) {} + : m_root(root), m_ops(new DoubleLinkedList), m_filename(filename) {} public: // TODO: support other literals @@ -28,8 +29,8 @@ namespace IR auto value = ParseExpression(varDecl->value()); // TODO: gather type information from var decl signature, aka local v = 0; auto dst = AllocateNamed(value->GetType()); - m_ops->Push(new AllocateOp(dst, value->GetType())); - m_ops->Push(new StoreOp(value, reinterpret_cast(dst))); + m_ops->Append(m_ops->New(new AllocateOp(dst, value->GetType()))); + m_ops->Append(m_ops->New(new StoreOp(value, reinterpret_cast(dst)))); m_locals.insert(std::make_pair(varDecl->name(), reinterpret_cast(dst))); } @@ -42,22 +43,21 @@ namespace IR assert(false); } auto dst = AllocateNamed(m_locals[var->name()]->GetValueType()); - m_ops->Push(new LoadOp(dst, m_locals[var->name()])); + m_ops->Append(m_ops->New(new LoadOp(dst, m_locals[var->name()]))); return reinterpret_cast(dst); } ValueHandle *ParseFnCall(const FnCallNode *fn) { - // TODO: support multiple args - auto argRegs = ValueBuilder(); - if (fn->arg() != nullptr) + auto args = ValueBuilder(); + for (size_t i = 0; i < fn->args().size; ++i) { - auto arg = ParseExpression(fn->arg()); - argRegs.Push(arg); + auto arg = ParseExpression(fn->args().data[i]); + args.Push(arg); } // TODO: gather return type of the function auto dst = AllocateNamed(new ValueHandle::Type {ValueHandle::Type::Kind::Int}); - m_ops->Push(new CallOp(dst, fn->name(), argRegs.view())); + m_ops->Append(m_ops->New(new CallOp(dst, fn->name(), args.view()))); return dst; } @@ -93,16 +93,16 @@ namespace IR switch (expr->op()) { case ExpressionNode::Operator::Plus: - m_ops->Push(new MathOp(dst, lhs, rhs, OpType::ADD)); + m_ops->Append(m_ops->New(new MathOp(dst, lhs, rhs, OpType::ADD))); break; case ExpressionNode::Operator::Multiply: - m_ops->Push(new MathOp(dst, lhs, rhs, OpType::MUL)); + m_ops->Append(m_ops->New(new MathOp(dst, lhs, rhs, OpType::MUL))); break; case ExpressionNode::Operator::Minus: - m_ops->Push(new MathOp(dst, lhs, rhs, OpType::SUB)); + m_ops->Append(m_ops->New(new MathOp(dst, lhs, rhs, OpType::SUB))); break; case ExpressionNode::Operator::Divide: - m_ops->Push(new MathOp(dst, lhs, rhs, OpType::DIV)); + m_ops->Append(m_ops->New(new MathOp(dst, lhs, rhs, OpType::DIV))); break; default: assert(0 && "unreachable"); @@ -131,12 +131,12 @@ namespace IR } } auto ops = EndBlock(); - auto block = Block(m_block_counter++, std::move(ops->view())); + auto block = Block(m_block_counter++, *ops); operator delete(ops); return block; } - OpView Build() + DoubleLinkedList* Build() { assert(m_root->GetType() == NodeType::Program && "root should be a program"); auto program = reinterpret_cast(m_root); @@ -144,30 +144,30 @@ namespace IR // Externs for (auto &extrn : program->externs()) { - m_ops->Push(new ExternOp(extrn->symbol())); + m_ops->Append(m_ops->New(new ExternOp(extrn->symbol()))); } // Functions for (auto &fn : program->funcs()) { auto block = ParseBlock(fn->body()); - m_ops->Push(new FnOp(fn->name(), fn->params(), std::move(block))); + m_ops->Append(m_ops->New(new FnOp(fn->name(), fn->params(), std::move(block)))); } - return OpView(m_ops->data, m_ops->size); + return m_ops; } public: - OpView ops() const { return OpView(m_ops->data, m_ops->size); } + const DoubleLinkedList* ops() const { return m_ops; } private: void StartBlock() { m_containers.Push(m_ops); - m_ops = new OpBuilder(); + m_ops = new DoubleLinkedList; } - OpBuilder *EndBlock() + DoubleLinkedList *EndBlock() { assert(m_containers.size > 0 && "containers stack is empty"); auto current = m_ops; @@ -193,13 +193,13 @@ namespace IR const Node *m_root = nullptr; StringView m_filename; - OpBuilder *m_ops = nullptr; + DoubleLinkedList *m_ops = nullptr; unsigned int m_value_counter = 0; unsigned int m_block_counter = 0; std::unordered_map m_locals; - Builder m_containers; + Builder*> m_containers; }; } // namespace IR diff --git a/include/parser/ast.hpp b/include/parser/ast.hpp index a8f944a..c2e4fec 100644 --- a/include/parser/ast.hpp +++ b/include/parser/ast.hpp @@ -45,14 +45,15 @@ public: // m_lexer->NextExpect(TokenType::Id); // char* name = strdup(m_lexer->token().string); m_lexer->NextExpect('('); - Node* arg = nullptr; - // TODO: support multiple arguments - if (m_lexer->seek_token()->token != ')') + Builder args; + while (m_lexer->seek_token()->token != ')') { - arg = ParseExpression(); + auto arg = ParseExpression(); + args.Push(arg); + if (m_lexer->seek_token()->token == ',') assert(m_lexer->NextToken()); } m_lexer->NextExpect(')'); - return new FnCallNode(name, arg); + return new FnCallNode(name, std::move(args.view())); } Node* ParseFactor() diff --git a/include/parser/nodes.hpp b/include/parser/nodes.hpp index 88839f9..7232a5c 100644 --- a/include/parser/nodes.hpp +++ b/include/parser/nodes.hpp @@ -24,7 +24,7 @@ class Node public: virtual NodeType GetType() const = 0; virtual ~Node() {} -}; +}; class ExpressionNode : public Node { @@ -144,20 +144,17 @@ class FnCallNode : public Node { public: // TODO: support multiple arguments - FnCallNode(const StringView& name, Node* arg) - : m_name(name), m_arg(arg) {} - ~FnCallNode() override { - delete m_arg; - } + FnCallNode(const StringView& name, View&& arg) + : m_name(name), m_args(arg) {} + ~FnCallNode() override = default; NODE_TYPE(FnCall) public: const StringView& name() const { return m_name; } - // TODO: support multiple args - const Node* arg() const { return m_arg; } + const View& args() const { return m_args; } private: StringView m_name; - Node* m_arg; + View m_args; }; class VariableNode : public Node @@ -214,4 +211,4 @@ public: private: std::vector m_funcs; std::vector m_externs; -}; \ No newline at end of file +}; diff --git a/include/prelude/linkedlist.hpp b/include/prelude/linkedlist.hpp index d73b1e0..24d7122 100644 --- a/include/prelude/linkedlist.hpp +++ b/include/prelude/linkedlist.hpp @@ -8,12 +8,16 @@ struct ListNode T value; ListNode* prev = nullptr; ListNode* next = nullptr; +public: + T& get() noexcept { return value; } + const T& get() const noexcept { return value; } }; template class DoubleLinkedList { public: DoubleLinkedList() = default; + ~DoubleLinkedList() = default; public: static DoubleLinkedList FromView(const View &view) @@ -26,7 +30,6 @@ public: return list; } -public: View ToView() { Builder b; @@ -37,6 +40,9 @@ public: return b.view(); } +public: + ListNode* New(T value) const { return new ListNode(value); } + public: void Append(ListNode* node) { diff --git a/src/main.cpp b/src/main.cpp index 2745816..c5ea83b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -61,9 +61,9 @@ int main(int argc, char **argv) auto ops = irBuilder.Build(); - for (size_t i = 0; i < ops.size; ++i) + for (auto cur = ops->Begin(); cur != nullptr; cur = cur->next) { - auto op = ops.data[i]; + auto op = cur->value; if (op->GetType() == IR::OpType::FN) { auto fn = reinterpret_cast(op); @@ -73,15 +73,15 @@ int main(int argc, char **argv) } } - for (size_t i = 0; i < ops.size; ++i) + for (auto cur = ops->Begin(); cur != nullptr; cur = cur->next) { - auto op = ops.data[i]; + auto op = cur->value; printf("%s\n", op->Format(0).c_str()); } FasmX86_64Generator gen; - gen.Generate(&ops); + gen.Generate(ops); auto output = File::Open("example.asm", File::Mode::WRITE); if (!output.Write(gen.GetOutput()))