Compare commits

...

2 Commits

Author SHA1 Message Date
952df07ce3 feat: support function params + stack based compiler! 2025-12-01 13:59:30 +01:00
b3498135aa feat: don't product IR in stdout for now 2025-11-30 22:15:09 +01:00
6 changed files with 162 additions and 124 deletions

View File

@ -1,15 +1,18 @@
extern putchar extern putchar
fn main() { fn hello() {
local h = 72 local h = 72
local e = 69 local e = 69
local l = 76 local l = 76
local o = 79 local o = 79
local nl = 10
putchar(h) putchar(h)
putchar(e) putchar(e)
putchar(l) putchar(l)
putchar(l) putchar(l)
putchar(o) putchar(o)
putchar(nl) }
fn main() {
hello()
putchar(10)
} }

View File

@ -126,8 +126,8 @@ class FnDeclNode : public Node
{ {
public: public:
// TODO: support parameters // TODO: support parameters
FnDeclNode(const StringView& name, CompoundNode* body) FnDeclNode(const StringView& name, CompoundNode* body, View<StringView> params)
: m_name(name), m_body(body) {} : m_name(name), m_body(body), m_params(params) {}
~FnDeclNode() override { ~FnDeclNode() override {
delete m_body; delete m_body;
} }
@ -136,9 +136,11 @@ public:
public: public:
const StringView& name() const { return m_name; } const StringView& name() const { return m_name; }
const CompoundNode* body() const { return m_body; } const CompoundNode* body() const { return m_body; }
const View<StringView>& params() const { return m_params; }
private: private:
StringView m_name; StringView m_name;
CompoundNode* m_body; CompoundNode* m_body;
View<StringView> m_params;
}; };
class FnCallNode : public Node class FnCallNode : public Node
@ -236,7 +238,13 @@ public:
m_lexer->NextExpect(TokenType::Id); m_lexer->NextExpect(TokenType::Id);
StringView name = m_lexer->token().string; StringView name = m_lexer->token().string;
m_lexer->NextExpect('('); m_lexer->NextExpect('(');
// TODO: parse parameters Builder<StringView> 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(')');
m_lexer->NextExpect('{'); m_lexer->NextExpect('{');
auto compound = new CompoundNode(); auto compound = new CompoundNode();
@ -245,7 +253,7 @@ public:
compound->addNode(ParseStatement()); compound->addNode(ParseStatement());
} }
m_lexer->NextExpect('}'); m_lexer->NextExpect('}');
return new FnDeclNode(name, compound); return new FnDeclNode(name, compound, params.view());
} }
FnCallNode* ParseFnCall(const StringView& name) FnCallNode* ParseFnCall(const StringView& name)
@ -253,7 +261,12 @@ public:
// m_lexer->NextExpect(TokenType::Id); // m_lexer->NextExpect(TokenType::Id);
// char* name = strdup(m_lexer->token().string); // char* name = strdup(m_lexer->token().string);
m_lexer->NextExpect('('); m_lexer->NextExpect('(');
Node* arg = ParseExpression(); Node* arg = nullptr;
// TODO: support multiple arguments
if (m_lexer->seek_token()->token != ')')
{
arg = ParseExpression();
}
m_lexer->NextExpect(')'); m_lexer->NextExpect(')');
return new FnCallNode(name, arg); return new FnCallNode(name, arg);
} }

View File

@ -53,98 +53,98 @@ private:
Builder<StackSlot> m_slots; Builder<StackSlot> m_slots;
}; };
struct ConstSlot // struct ConstSlot
{ // {
long value; // long value;
StringView addr; // StringView addr;
}; // };
class ConstAllocator : public Allocator<ConstSlot> // class ConstAllocator : public Allocator<ConstSlot>
{ // {
public: // public:
ConstAllocator() = default; // ConstAllocator() = default;
~ConstAllocator() override = default; // ~ConstAllocator() override = default;
public: // public:
const ConstSlot& Allocate(const StringView& addr) // const ConstSlot& Allocate(const StringView& addr)
{ // {
m_slots.Push(ConstSlot { 0, addr }); // m_slots.Push(ConstSlot { 0, addr });
return m_slots.data[m_slots.size - 1]; // return m_slots.data[m_slots.size - 1];
} // }
const ConstSlot& StoreValue(const StringView& addr, long value) // const ConstSlot& StoreValue(const StringView& addr, long value)
{ // {
for (size_t i = 0; i < m_slots.size; ++i) // for (size_t i = 0; i < m_slots.size; ++i)
{ // {
if (strcmp(m_slots.data[i].addr.c_str(), addr.c_str()) == 0) // if (strcmp(m_slots.data[i].addr.c_str(), addr.c_str()) == 0)
{ // {
m_slots.data[i].value = value; // m_slots.data[i].value = value;
return m_slots.data[i]; // 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) // const ConstSlot& Resolve(const StringView& addr)
{ // {
for (size_t i = 0; i < m_slots.size; ++i) // for (size_t i = 0; i < m_slots.size; ++i)
{ // {
if (strcmp(m_slots.data[i].addr.c_str(), addr.c_str()) == 0) // if (strcmp(m_slots.data[i].addr.c_str(), addr.c_str()) == 0)
{ // {
return m_slots.data[i]; // return m_slots.data[i];
} // }
} // }
assert(0 && "could not resolve const under specified address"); // assert(0 && "could not resolve const under specified address");
} // }
private: // private:
Builder<ConstSlot> m_slots; // Builder<ConstSlot> m_slots;
}; // };
struct RegisterSlot // struct RegisterSlot
{ // {
const StringView& reg; // const StringView& reg;
StringView addr; // StringView addr;
}; // };
class RegisterAllocator : public Allocator<RegisterSlot> // class RegisterAllocator : public Allocator<RegisterSlot>
{ // {
public: // public:
RegisterAllocator() // RegisterAllocator()
{ // {
m_regs.Push(std::move(StringView("eax"))); // m_regs.Push(std::move(StringView("eax")));
m_regs.Push(std::move(StringView("ecx"))); // m_regs.Push(std::move(StringView("ecx")));
} // }
~RegisterAllocator() override = default; // ~RegisterAllocator() override = default;
public: // public:
const RegisterSlot& Allocate(const StringView& addr) // const RegisterSlot& Allocate(const StringView& addr)
{ // {
assert(m_slots.size < m_regs.size && "no space available for allocating to register"); // 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 }); // m_slots.Push(RegisterSlot { m_regs.data[m_slots.size], addr });
return m_slots.data[m_slots.size - 1]; // return m_slots.data[m_slots.size - 1];
} // }
const RegisterSlot& Resolve(const StringView& addr) // const RegisterSlot& Resolve(const StringView& addr)
{ // {
for (size_t i = 0; i < m_slots.size; ++i) // for (size_t i = 0; i < m_slots.size; ++i)
{ // {
if (strcmp(m_slots.data[i].addr.c_str(), addr.c_str()) == 0) // if (strcmp(m_slots.data[i].addr.c_str(), addr.c_str()) == 0)
{ // {
return m_slots.data[i]; // 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() // void Clear()
{ // {
m_slots.size = 0; // m_slots.size = 0;
} // }
private: // private:
Builder<RegisterSlot> m_slots; // Builder<RegisterSlot> m_slots;
Builder<StringView> m_regs; // Builder<StringView> m_regs;
}; // };
class CodeGenerator class CodeGenerator
{ {
@ -164,7 +164,15 @@ private:
int stackSize = 0; int stackSize = 0;
for (auto &op : ops) 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; return stackSize;
} }
@ -194,12 +202,19 @@ private:
printf("%s:\n", name.c_str()); printf("%s:\n", name.c_str());
printf("push rbp\n"); printf("push rbp\n");
printf("mov rbp, rsp\n"); printf("mov rbp, rsp\n");
m_stack = new StackAllocator();
int stackSize = GetStackSize(fn->ops()); int stackSize = GetStackSize(fn->ops());
printf("sub rsp, %d\n", stackSize); 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()) for(auto &fOp : fn->ops())
{ {
GenerateOp(fOp); GenerateOp(fOp);
} }
m_stack = nullptr;
printf("leave\nret\n"); printf("leave\nret\n");
} }
break; break;
@ -209,46 +224,50 @@ private:
// TODO: support several arguments // TODO: support several arguments
if (call->args().size == 1) if (call->args().size == 1)
{ {
auto reg_slot = m_registers.Resolve(GetTempAddr(call->args().data[0])); auto slot = m_stack->Resolve(GetTempAddr(call->args().data[0]));
printf("mov edi, %s\n", reg_slot.reg.c_str()); printf("mov edi, [rbp-%d]\n", slot.offset);
} }
printf("call %s\n", call->callee().c_str()); 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; break;
case IR::OpType::LOAD_CONST: case IR::OpType::LOAD_CONST:
{ {
auto lc = reinterpret_cast<const IR::LoadConstOp *>(op); auto lc = reinterpret_cast<const IR::LoadConstOp *>(op);
auto addr = GetTempAddr(lc->result()); auto addr = GetTempAddr(lc->result());
m_consts.Allocate(addr); auto slot = m_stack->Allocate(addr);
m_consts.StoreValue(addr, lc->value()); printf("mov dword [rbp-%d], %ld\n", slot.offset, lc->value());
} }
break; break;
case IR::OpType::STORE: case IR::OpType::STORE:
{ {
auto s = reinterpret_cast<const IR::StoreOp *>(op); auto s = reinterpret_cast<const IR::StoreOp *>(op);
printf("; DEBUG: resolving stack slot at %s\n", s->addr().c_str()); printf("; DEBUG: resolving stack slot at %s\n", s->addr().c_str());
auto slot = m_stack.Allocate(s->addr()); auto slot = m_stack->Allocate(s->addr());
auto value = m_consts.Resolve(GetTempAddr(s->src())); auto value_slot = m_stack->Resolve(GetTempAddr(s->src()));
printf("mov dword [rbp-%d], %ld\n", slot.offset, value.value); printf("mov eax, [rbp-%d]\n", value_slot.offset);
printf("mov dword [rbp-%d], eax\n", slot.offset);
} }
break; break;
case IR::OpType::LOAD: case IR::OpType::LOAD:
{ {
auto l = reinterpret_cast<const IR::LoadOp *>(op); auto l = reinterpret_cast<const IR::LoadOp *>(op);
auto reg_slot = m_registers.Allocate(GetTempAddr(l->result())); auto value_slot = m_stack->Allocate(GetTempAddr(l->result()));
auto stack_slot = m_stack.Resolve(l->addr()); auto variable_slot = m_stack->Resolve(l->addr());
printf("mov %s, [rbp-%d]\n", reg_slot.reg.c_str(), stack_slot.offset); printf("mov eax, [rbp-%d]\n", variable_slot.offset);
printf("mov dword [rbp-%d], eax\n", value_slot.offset);
} }
break; break;
case IR::OpType::ADD: case IR::OpType::ADD:
{ {
auto expr = reinterpret_cast<const IR::AddOp *>(op); auto expr = reinterpret_cast<const IR::AddOp *>(op);
auto lhs_slot = m_registers.Resolve(GetTempAddr(expr->lhs())); auto lhs_slot = m_stack->Resolve(GetTempAddr(expr->lhs()));
auto rhs_slot = m_registers.Resolve(GetTempAddr(expr->rhs())); printf("mov eax, [rbp-%d]\n", lhs_slot.offset);
printf("add %s, %s\n", lhs_slot.reg.c_str(), rhs_slot.reg.c_str()); auto rhs_slot = m_stack->Resolve(GetTempAddr(expr->rhs()));
m_registers.Clear(); printf("add eax, [rbp-%d]\n", rhs_slot.offset);
m_registers.Allocate(GetTempAddr(expr->result())); auto result_slot = m_stack->Allocate(GetTempAddr(expr->result()));
printf("mov dword [rbp-%d], eax\n", result_slot.offset);
} }
break; break;
default: printf("; NOT HANDLED\n; %s\n", op->Format(0).c_str()); break; default: printf("; NOT HANDLED\n; %s\n", op->Format(0).c_str()); break;
@ -268,7 +287,5 @@ public:
} }
public: public:
// TODO: handle sub-blocks // TODO: handle sub-blocks
StackAllocator m_stack; StackAllocator* m_stack = nullptr;
ConstAllocator m_consts;
RegisterAllocator m_registers;
}; };

View File

@ -74,7 +74,7 @@ private:
class FnOp : public Op class FnOp : public Op
{ {
public: public:
FnOp(StringView name, const CompoundNode* body); FnOp(StringView name, const CompoundNode* body, const View<StringView>& params);
~FnOp() {} ~FnOp() {}
OP_TYPE(FN) OP_TYPE(FN)
@ -93,9 +93,11 @@ public:
public: public:
const StringView& name() const { return m_name; } const StringView& name() const { return m_name; }
const OpView& ops() const { return m_ops; } const OpView& ops() const { return m_ops; }
const View<StringView>& params() const { return m_params; }
private: private:
StringView m_name; StringView m_name;
OpView m_ops; OpView m_ops;
View<StringView> m_params;
}; };
class LoadConstOp : public Op, public Valued class LoadConstOp : public Op, public Valued
@ -243,11 +245,14 @@ public:
Reg ParseFnCall(const FnCallNode* fn) Reg ParseFnCall(const FnCallNode* fn)
{ {
// TODO: support multiple args // TODO: support multiple args
auto arg = ParseExpression(fn->arg());
auto argRegs = RegBuilder(); auto argRegs = RegBuilder();
if (fn->arg() != nullptr)
{
auto arg = ParseExpression(fn->arg());
argRegs.Push(arg); argRegs.Push(arg);
}
auto dst = AllocateRegister(); 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; return dst;
} }
@ -319,7 +324,7 @@ public:
// Functions // Functions
for (auto &fn : program->funcs()) 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); return OpView(m_ops.data, m_ops.size);

View File

@ -1,7 +1,7 @@
#include "ir.hpp" #include "ir.hpp"
IR::FnOp::FnOp(StringView name, const CompoundNode* body) IR::FnOp::FnOp(StringView name, const CompoundNode* body, const View<StringView>& params)
: m_name(name) : m_name(name), m_params(params)
{ {
IRBuilder ir(body); // Now IRBuilder is complete → OK IRBuilder ir(body); // Now IRBuilder is complete → OK
ir.ParseBlock(body); ir.ParseBlock(body);

View File

@ -49,12 +49,12 @@ int main(int argc, char** argv)
auto ops = irBuilder.Build(); auto ops = irBuilder.Build();
printf("; ------ IR ------\n"); // printf("; ------ IR ------\n");
for (size_t i = 0; i < ops.size; ++i) // for (size_t i = 0; i < ops.size; ++i)
{ // {
printf("; %s\n", ops.data[i]->Format(0).c_str()); // printf("; %s\n", ops.data[i]->Format(0).c_str());
} // }
printf("; ------ IR ------\n"); // printf("; ------ IR ------\n");
StackFasmX86_64Generator gen; StackFasmX86_64Generator gen;