#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; };