Compare commits
8 Commits
8a2d98e69e
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 589586db51 | |||
| 806e20d9b1 | |||
| 2312129148 | |||
| 1c04a058d7 | |||
| 629b65e151 | |||
| 6176d549c1 | |||
| a453603b9b | |||
| 0b9cb7e7d9 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1 +1,2 @@
|
|||||||
build/
|
build/
|
||||||
|
.cache/
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
cmake_minimum_required(VERSION 3.16.0 FATAL_ERROR)
|
cmake_minimum_required(VERSION 3.16.0 FATAL_ERROR)
|
||||||
project(pl VERSION 0.0.1 LANGUAGES C CXX)
|
project(pl VERSION 0.0.1 LANGUAGES C CXX)
|
||||||
|
|
||||||
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 23)
|
set(CMAKE_CXX_STANDARD 23)
|
||||||
set(SOURCES
|
set(SOURCES
|
||||||
src/main.cpp
|
src/main.cpp
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
extern putchar
|
extern putchar
|
||||||
|
extern iadd
|
||||||
|
|
||||||
fn hello() {
|
fn hello() {
|
||||||
local h = 72
|
local h = 72
|
||||||
@@ -14,5 +15,6 @@ fn hello() {
|
|||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
hello()
|
hello()
|
||||||
putchar(3 * 3 + 1)
|
local newline = iadd(3*3, 1)
|
||||||
|
putchar(newline)
|
||||||
}
|
}
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "prelude/string.hpp"
|
|
||||||
#include "codegen/slot.hpp"
|
|
||||||
|
|
||||||
#define AVAILABLE_REGISTERS 4
|
|
||||||
#define AVAILABLE_STACK_SIZE 4096
|
|
||||||
|
|
||||||
template <typename Slot, typename SlotAddr = const StringView &>
|
|
||||||
class Allocator
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual ~Allocator() {};
|
|
||||||
|
|
||||||
public:
|
|
||||||
virtual const Slot &Allocate(SlotAddr addr) = 0;
|
|
||||||
virtual const Slot &Resolve(SlotAddr addr) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
class SlotAllocator : public Allocator<IR::IRSlot>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
SlotAllocator() = default;
|
|
||||||
~SlotAllocator() = default;
|
|
||||||
|
|
||||||
public:
|
|
||||||
const IR::IRSlot &Allocate(const StringView &addr) override
|
|
||||||
{
|
|
||||||
if (m_regs < AVAILABLE_REGISTERS)
|
|
||||||
{
|
|
||||||
m_slots.Push(IR::IRSlot(addr, m_regs++, IR::IRSlot::Type::REGISTRY));
|
|
||||||
return m_slots.data[m_slots.size - 1];
|
|
||||||
}
|
|
||||||
if (m_offset_counter + 8 <= AVAILABLE_STACK_SIZE)
|
|
||||||
{
|
|
||||||
m_offset_counter += 8;
|
|
||||||
m_slots.Push(IR::IRSlot(addr, m_offset_counter, IR::IRSlot::Type::STACK));
|
|
||||||
return m_slots.data[m_slots.size - 1];
|
|
||||||
}
|
|
||||||
// TODO: proper error handling (stack overflow etc.)
|
|
||||||
assert(0 && "failed to allocate local");
|
|
||||||
}
|
|
||||||
|
|
||||||
const IR::IRSlot &Resolve(const StringView &addr) override
|
|
||||||
{
|
|
||||||
for (size_t i = 0; i < m_slots.size; ++i)
|
|
||||||
{
|
|
||||||
if (m_slots.data[i].GetAddr() == addr)
|
|
||||||
{
|
|
||||||
return m_slots.data[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(0 && "could not resolve stack offset for specified address");
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
View<IR::IRSlot> slots() const { return m_slots.view(); }
|
|
||||||
|
|
||||||
public:
|
|
||||||
unsigned int GetStackSize() const { return m_offset_counter; }
|
|
||||||
|
|
||||||
public:
|
|
||||||
int m_regs = 0;
|
|
||||||
unsigned int m_offset_counter = 0;
|
|
||||||
Builder<IR::IRSlot> m_slots;
|
|
||||||
};
|
|
||||||
@@ -1,17 +1,21 @@
|
|||||||
|
|
||||||
// TODO: store all of values, allocated registers, stack offsets in single allocator
|
|
||||||
// to be able to find out which kind a specific temp register represent
|
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <cstddef>
|
#include "ir/op.hpp"
|
||||||
#include <cstdarg>
|
#include "prelude/linkedlist.hpp"
|
||||||
#include "prelude/string.hpp"
|
#include "prelude/string.hpp"
|
||||||
#include "ir/ir.hpp"
|
|
||||||
|
|
||||||
class CodeGenerator
|
class CodeGenerator
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual ~CodeGenerator() {};
|
CodeGenerator() = default;
|
||||||
|
virtual ~CodeGenerator() {}
|
||||||
|
|
||||||
virtual void Generate(const char* filename, View<IR::Op*> ops) = 0;
|
public:
|
||||||
|
virtual bool Generate(const DoubleLinkedList<IR::Op*>* ops) = 0;
|
||||||
|
StringView GetOutput() { return output().view(); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
StringBuilder& output() { return m_output; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
StringBuilder m_output;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,180 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "codegen/codegen.hpp"
|
|
||||||
#include "ir/slot.hpp"
|
|
||||||
#include "ir/value.hpp"
|
|
||||||
|
|
||||||
class StackFasmX86_64Generator : public CodeGenerator
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
~StackFasmX86_64Generator() override = default;
|
|
||||||
|
|
||||||
private:
|
|
||||||
StringView GetTempAddr(IR::ValueHandle *reg)
|
|
||||||
{
|
|
||||||
return std::move((StringBuilder() << reg->Format()).view());
|
|
||||||
}
|
|
||||||
|
|
||||||
StringView GetSlotAddr(const IR::IRSlot &slot)
|
|
||||||
{
|
|
||||||
switch (slot.GetType())
|
|
||||||
{
|
|
||||||
case IR::IRSlot::Type::REGISTRY:
|
|
||||||
{
|
|
||||||
StringBuilder sb;
|
|
||||||
sb.AppendFormat("r%d", slot.GetSlot() + 12); // for r10, r11, r12 etc.
|
|
||||||
return sb.view();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case IR::IRSlot::Type::STACK:
|
|
||||||
{
|
|
||||||
StringBuilder sb;
|
|
||||||
sb.AppendFormat("[rbp-%d]", slot.GetSlot()); // for r10, r11, r12 etc.
|
|
||||||
return sb.view();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
assert(0 && "TODO: either unreachable or handle properly");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
void GenerateOp(const IR::Op *op)
|
|
||||||
{
|
|
||||||
switch (op->GetType())
|
|
||||||
{
|
|
||||||
case IR::OpType::EXTERN:
|
|
||||||
{
|
|
||||||
auto extrn = reinterpret_cast<const IR::ExternOp *>(op);
|
|
||||||
auto symbol = extrn->symbol();
|
|
||||||
appendf("extrn '%s' as __%s\n", symbol.c_str(), symbol.c_str());
|
|
||||||
appendf("%s = PLT __%s\n", symbol.c_str(), symbol.c_str());
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case IR::OpType::FN:
|
|
||||||
{
|
|
||||||
m_allocator = new SlotAllocator();
|
|
||||||
|
|
||||||
auto fn = reinterpret_cast<const IR::FnOp *>(op);
|
|
||||||
auto name = fn->name();
|
|
||||||
appendf("public %s\n", name.c_str());
|
|
||||||
appendf("%s:\n", name.c_str());
|
|
||||||
appendf("push rbp\n");
|
|
||||||
appendf("mov rbp, rsp\n");
|
|
||||||
|
|
||||||
StringBuilder fnOutput;
|
|
||||||
StringBuilder *backup = m_output;
|
|
||||||
m_output = &fnOutput;
|
|
||||||
|
|
||||||
if (fn->params().size > 0)
|
|
||||||
{
|
|
||||||
// TODO: support multiple parameters
|
|
||||||
auto param_slot = m_allocator->Allocate(fn->params().data[0]);
|
|
||||||
appendf("mov %s, rdi\n", GetSlotAddr(param_slot).c_str());
|
|
||||||
}
|
|
||||||
for (auto &fOp : fn->body().ops())
|
|
||||||
{
|
|
||||||
GenerateOp(fOp);
|
|
||||||
}
|
|
||||||
|
|
||||||
int stackSize = m_allocator->GetStackSize();
|
|
||||||
m_output = backup;
|
|
||||||
appendf("sub rsp, %d\n", stackSize);
|
|
||||||
*m_output << fnOutput.c_str();
|
|
||||||
|
|
||||||
appendf("leave\nret\n");
|
|
||||||
|
|
||||||
m_allocator = nullptr;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case IR::OpType::CALL:
|
|
||||||
{
|
|
||||||
auto call = reinterpret_cast<const IR::CallOp *>(op);
|
|
||||||
// TODO: support several arguments
|
|
||||||
if (call->args().size == 1)
|
|
||||||
{
|
|
||||||
auto slot = m_allocator->Resolve(GetTempAddr(call->args().data[0]));
|
|
||||||
appendf("mov rdi, %s\n", GetSlotAddr(slot).c_str());
|
|
||||||
}
|
|
||||||
appendf("call %s\n", call->callee().c_str());
|
|
||||||
auto result_slot = m_allocator->Allocate(GetTempAddr(call->result()));
|
|
||||||
appendf("mov %s, rax\n", GetSlotAddr(result_slot).c_str());
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
// case IR::OpType::LOAD_CONST:
|
|
||||||
// {
|
|
||||||
// auto lc = reinterpret_cast<const IR::LoadConstOp *>(op);
|
|
||||||
// auto addr = GetTempAddr(lc->result());
|
|
||||||
// auto slot = m_allocator->Allocate(addr);
|
|
||||||
// appendf("mov %s, %ld\n", GetSlotAddr(slot).c_str(), lc->value());
|
|
||||||
// }
|
|
||||||
// break;
|
|
||||||
case IR::OpType::STORE:
|
|
||||||
{
|
|
||||||
auto s = reinterpret_cast<const IR::StoreOp *>(op);
|
|
||||||
auto slot = m_allocator->Allocate(s->addr());
|
|
||||||
auto value_slot = m_allocator->Resolve(GetTempAddr(s->src()));
|
|
||||||
appendf("mov rax, %s\n", GetSlotAddr(value_slot).c_str());
|
|
||||||
appendf("mov %s, rax\n", GetSlotAddr(slot).c_str());
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case IR::OpType::LOAD:
|
|
||||||
{
|
|
||||||
auto l = reinterpret_cast<const IR::LoadOp *>(op);
|
|
||||||
auto value_slot = m_allocator->Allocate(GetTempAddr(l->result()));
|
|
||||||
auto variable_slot = m_allocator->Resolve(l->addr());
|
|
||||||
appendf("mov rax, %s\n", GetSlotAddr(variable_slot).c_str());
|
|
||||||
appendf("mov %s, rax\n", GetSlotAddr(value_slot).c_str());
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case IR::OpType::ADD:
|
|
||||||
{
|
|
||||||
auto expr = reinterpret_cast<const IR::AddOp *>(op);
|
|
||||||
auto lhs_slot = m_allocator->Resolve(GetTempAddr(expr->lhs()));
|
|
||||||
appendf("mov rax, %s\n", GetSlotAddr(lhs_slot).c_str());
|
|
||||||
auto rhs_slot = m_allocator->Resolve(GetTempAddr(expr->rhs()));
|
|
||||||
appendf("add rax, %s\n", GetSlotAddr(rhs_slot).c_str());
|
|
||||||
auto result_slot = m_allocator->Allocate(GetTempAddr(expr->result()));
|
|
||||||
appendf("mov %s, rax\n", GetSlotAddr(result_slot).c_str());
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
appendf("; NOT HANDLED\n; %s\n", op->Format(0).c_str());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void appendf(const char *fmt, ...)
|
|
||||||
{
|
|
||||||
assert(m_output != nullptr && "nowhere to write");
|
|
||||||
va_list args;
|
|
||||||
va_start(args, fmt);
|
|
||||||
m_output->VAppendFormat(fmt, args);
|
|
||||||
va_end(args);
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
StringView GetOutput() { return m_output->view(); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
StringBuilder *m_output = nullptr;
|
|
||||||
|
|
||||||
public:
|
|
||||||
void Generate(const char *filename, View<IR::Op *> ops) override
|
|
||||||
{
|
|
||||||
m_output = new StringBuilder();
|
|
||||||
appendf("; fasm x86_64 linux generated assembly using pl\n");
|
|
||||||
appendf("format ELF64\n");
|
|
||||||
appendf("section '.text' executable\n");
|
|
||||||
|
|
||||||
for (auto &op : ops)
|
|
||||||
{
|
|
||||||
GenerateOp(op);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
// StackAllocator* m_stack = nullptr;
|
|
||||||
// TODO: handle sub-blocks
|
|
||||||
SlotAllocator *m_allocator = nullptr;
|
|
||||||
};
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "prelude/string.hpp"
|
|
||||||
|
|
||||||
namespace IR
|
|
||||||
{
|
|
||||||
|
|
||||||
struct IRSlot
|
|
||||||
{
|
|
||||||
enum class Type
|
|
||||||
{
|
|
||||||
UNKNOWN = 0,
|
|
||||||
STACK,
|
|
||||||
REGISTRY,
|
|
||||||
};
|
|
||||||
public:
|
|
||||||
IRSlot(StringView addr, unsigned int slot, Type slotType) : m_addr(addr), m_slot(slot), m_slotType(slotType) {}
|
|
||||||
IRSlot() = default;
|
|
||||||
public:
|
|
||||||
Type GetType() const { return m_slotType; }
|
|
||||||
const StringView& GetAddr() const { return m_addr; }
|
|
||||||
unsigned int GetSlot() const { return m_slot; }
|
|
||||||
public:
|
|
||||||
StringView Format() const {
|
|
||||||
StringBuilder sb;
|
|
||||||
switch(GetType())
|
|
||||||
{
|
|
||||||
case Type::REGISTRY:
|
|
||||||
sb << "r" << GetSlot();
|
|
||||||
break;
|
|
||||||
case Type::STACK:
|
|
||||||
sb << "s[" << GetSlot() << "]";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
sb << "(UNKNOWN_SLOT_TYPE)";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return sb.view();
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
StringView m_addr;
|
|
||||||
unsigned int m_slot;
|
|
||||||
Type m_slotType;
|
|
||||||
};
|
|
||||||
|
|
||||||
using IRSlotBuilder = Builder<IRSlot>;
|
|
||||||
using IRSlotView = View<IRSlot>;
|
|
||||||
|
|
||||||
} // namespace IR
|
|
||||||
247
include/codegen/targets/fasm_x86_64_linux.hpp
Normal file
247
include/codegen/targets/fasm_x86_64_linux.hpp
Normal file
@@ -0,0 +1,247 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "codegen/codegen.hpp"
|
||||||
|
#include "ir/op.hpp"
|
||||||
|
#include "ir/ops.hpp"
|
||||||
|
#include "ir/value.hpp"
|
||||||
|
#include "prelude/linkedlist.hpp"
|
||||||
|
#include "prelude/string.hpp"
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
class FasmX86_64Generator : public CodeGenerator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FasmX86_64Generator() = default;
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool Generate(const DoubleLinkedList<IR::Op*>* ops) override
|
||||||
|
{
|
||||||
|
m_ops = ops;
|
||||||
|
|
||||||
|
output().Extend("format ELF64\n");
|
||||||
|
output().Extend("section '.text' executable\n");
|
||||||
|
|
||||||
|
for (m_current = m_ops->Begin(); m_current != nullptr; node_next())
|
||||||
|
{
|
||||||
|
GenerateStatement();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void GenerateExtern()
|
||||||
|
{
|
||||||
|
auto extrn = current<IR::ExternOp>();
|
||||||
|
// TODO: instead of __<symbol, use some random UID or hash string
|
||||||
|
// for safety and collision considerations
|
||||||
|
output().AppendFormat("extrn '%s' as __%s\n", extrn->symbol().c_str(), extrn->symbol().c_str());
|
||||||
|
output().AppendFormat("%s = PLT __%s\n", extrn->symbol().c_str(), extrn->symbol().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void GenerateFunction()
|
||||||
|
{
|
||||||
|
auto fn = current<IR::FnOp>();
|
||||||
|
|
||||||
|
m_slots.clear();
|
||||||
|
m_stackCounter = 0;
|
||||||
|
|
||||||
|
output().AppendFormat("public %s\n", fn->name().c_str());
|
||||||
|
output().AppendFormat("%s:\n", fn->name().c_str());
|
||||||
|
output().Extend(" push rbp\n");
|
||||||
|
output().Extend(" mov rbp, rsp\n");
|
||||||
|
|
||||||
|
auto fnNode = node<IR::Op>();
|
||||||
|
auto ops = m_ops;
|
||||||
|
m_ops = &fn->body().ops();
|
||||||
|
for (m_current = m_ops->Begin(); m_current != nullptr; node_next())
|
||||||
|
{
|
||||||
|
GenerateStatement();
|
||||||
|
}
|
||||||
|
m_ops = ops;
|
||||||
|
m_current = fnNode;
|
||||||
|
|
||||||
|
output().Extend(" leave\n");
|
||||||
|
output().Extend(" ret\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void GenerateCall()
|
||||||
|
{
|
||||||
|
auto call = current<IR::CallOp>();
|
||||||
|
// 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"};
|
||||||
|
for (size_t i = 0; i < call->args().size; ++i)
|
||||||
|
{
|
||||||
|
auto arg = call->args().data[i];
|
||||||
|
if (arg->HasId()) {
|
||||||
|
auto sp = EnsureSlot(arg);
|
||||||
|
// TODO:
|
||||||
|
auto size = "dword";
|
||||||
|
output().AppendFormat(" mov %s, %s [rbp-%d]\n", regs[i], size, sp);
|
||||||
|
} else {
|
||||||
|
output().AppendFormat(" mov %s, %d\n", regs[i], reinterpret_cast<IR::ConstantInt*>(arg)->GetValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
output().AppendFormat(" call %s\n", call->callee().c_str());
|
||||||
|
auto sp = EnsureSlot(call->result());
|
||||||
|
auto size = "dword";
|
||||||
|
output().AppendFormat(" mov %s [rbp-%d], eax\n", size, sp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GenerateAllocate()
|
||||||
|
{
|
||||||
|
auto totalAllocSize = 0;
|
||||||
|
while (current<IR::Op>()->GetType() == IR::OpType::ALLOCATE)
|
||||||
|
{
|
||||||
|
auto alloc = current<IR::AllocateOp>();
|
||||||
|
// 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));
|
||||||
|
|
||||||
|
if (seek<IR::Op>() && seek<IR::Op>()->get()->GetType() == IR::OpType::ALLOCATE) node_next();
|
||||||
|
else break;
|
||||||
|
};
|
||||||
|
output().AppendFormat(" sub rsp, %d\n", totalAllocSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GenerateStore()
|
||||||
|
{
|
||||||
|
auto store = current<IR::StoreOp>();
|
||||||
|
auto sp = EnsureSlot(store->dst());
|
||||||
|
|
||||||
|
// TODO: support other types
|
||||||
|
assert(store->src()->GetType()->kind == IR::ValueHandle::Type::Kind::Int);
|
||||||
|
if (!store->src()->HasId()) {
|
||||||
|
auto value = reinterpret_cast<const IR::ConstantInt*>(store->src());
|
||||||
|
auto size = "dword";
|
||||||
|
output().AppendFormat(" mov %s [rbp-%d], %d\n", size, sp, value->GetValue());
|
||||||
|
} else {
|
||||||
|
auto ssp = EnsureSlot(store->src());
|
||||||
|
auto size = "dword";
|
||||||
|
output().AppendFormat(" mov eax, %s [rbp-%d]\n", size, ssp);
|
||||||
|
output().AppendFormat(" mov %s [rbp-%d], eax\n", size, sp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GenerateLoad()
|
||||||
|
{
|
||||||
|
auto load = current<IR::LoadOp>();
|
||||||
|
auto sp = EnsureSlot(load->Ptr());
|
||||||
|
|
||||||
|
// TODO: support other types
|
||||||
|
auto size = "dword";
|
||||||
|
output().AppendFormat(" mov eax, %s [rbp-%d]\n", size, sp);
|
||||||
|
|
||||||
|
sp = EnsureSlot(load->result());
|
||||||
|
output().AppendFormat(" mov dword [rbp-%d], eax\n", sp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GenerateMath()
|
||||||
|
{
|
||||||
|
auto math = current<IR::MathOp>();
|
||||||
|
StringBuilder sb;
|
||||||
|
|
||||||
|
switch(math->GetType())
|
||||||
|
{
|
||||||
|
case IR::OpType::ADD:
|
||||||
|
sb << "add";
|
||||||
|
break;
|
||||||
|
case IR::OpType::SUB:
|
||||||
|
sb << "sub";
|
||||||
|
break;
|
||||||
|
case IR::OpType::MUL:
|
||||||
|
sb << "imul";
|
||||||
|
break;
|
||||||
|
case IR::OpType::DIV:
|
||||||
|
sb << "div";
|
||||||
|
break;
|
||||||
|
default: assert(false && "unreachable or not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto op = sb.view();
|
||||||
|
|
||||||
|
// TODO:
|
||||||
|
auto size = "dword";
|
||||||
|
if (!math->lhs()->HasId()) {
|
||||||
|
output().AppendFormat(" mov eax, %d\n", reinterpret_cast<IR::ConstantInt*>(math->lhs())->GetValue());
|
||||||
|
} else {
|
||||||
|
auto lsp = EnsureSlot(math->lhs());
|
||||||
|
output().AppendFormat(" mov eax, %s [rbp-%d]\n", size, lsp);
|
||||||
|
}
|
||||||
|
if (!math->rhs()->HasId()) {
|
||||||
|
output().AppendFormat(" %s eax, %d\n", op.c_str(), reinterpret_cast<IR::ConstantInt*>(math->rhs())->GetValue());
|
||||||
|
} else {
|
||||||
|
auto lsp = EnsureSlot(math->rhs());
|
||||||
|
output().AppendFormat(" %s eax, %s [rbp-%d]\n", op.c_str(), size, lsp);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto sp = EnsureSlot(math->result());
|
||||||
|
output().AppendFormat(" mov %s [rbp-%d], eax\n", size, sp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GenerateStatement()
|
||||||
|
{
|
||||||
|
auto op = current<IR::Op>();
|
||||||
|
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:
|
||||||
|
uint32_t EnsureSlot(const IR::ValueHandle* value)
|
||||||
|
{
|
||||||
|
assert(value->HasId());
|
||||||
|
auto stackPointer = m_slots.find(value->GetId());
|
||||||
|
if (stackPointer != m_slots.end())
|
||||||
|
{
|
||||||
|
return stackPointer->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: dynamic size based on type
|
||||||
|
auto allocSize = 4;
|
||||||
|
output().AppendFormat(" sub rsp, %d\n", allocSize);
|
||||||
|
m_stackCounter += allocSize;
|
||||||
|
m_slots.insert(std::make_pair(value->GetId(), m_stackCounter));
|
||||||
|
return m_stackCounter;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
template<typename T>
|
||||||
|
ListNode<T*>* node() { return reinterpret_cast<ListNode<T*>*>(m_current); }
|
||||||
|
template<typename T>
|
||||||
|
ListNode<T*>* seek() { return m_current && m_current->next ? reinterpret_cast<ListNode<T*>*>(m_current->next) : nullptr; }
|
||||||
|
|
||||||
|
void node_next() { assert(m_current); m_current = m_current->next; }
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
T* current() const { return reinterpret_cast<T*>(m_current->value); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
const DoubleLinkedList<IR::Op*>* m_ops;
|
||||||
|
ListNode<IR::Op*>* m_current;
|
||||||
|
std::unordered_map<uint32_t, uint32_t> m_slots;
|
||||||
|
uint32_t m_stackCounter = 0;
|
||||||
|
};
|
||||||
@@ -1,35 +1,34 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "ir/op.hpp"
|
#include "ir/op.hpp"
|
||||||
|
#include "prelude/linkedlist.hpp"
|
||||||
|
|
||||||
namespace IR
|
namespace IR
|
||||||
{
|
{
|
||||||
|
|
||||||
using OpView = View<Op*>;
|
|
||||||
|
|
||||||
using BlockID = unsigned int;
|
using BlockID = unsigned int;
|
||||||
|
|
||||||
class Block
|
class Block
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Block(BlockID id, OpView&& ops)
|
Block(BlockID id, DoubleLinkedList<Op*> ops)
|
||||||
: m_id(id), m_ops(ops) {}
|
: m_id(id), m_ops(ops) {}
|
||||||
public:
|
public:
|
||||||
const OpView& ops() const { return m_ops; }
|
DoubleLinkedList<Op*>& ops() { return m_ops; }
|
||||||
public:
|
public:
|
||||||
StringView Format(int indent) const
|
StringView Format(int indent) const
|
||||||
{
|
{
|
||||||
StringBuilder sb;
|
StringBuilder sb;
|
||||||
sb.AppendIndent(indent);
|
sb.AppendIndent(indent);
|
||||||
sb.AppendFormat("b%d:\n", m_id);
|
sb.AppendFormat("b%d:\n", m_id);
|
||||||
for (size_t i = 0; i < m_ops.size; ++i)
|
for (ListNode<Op*>* cur = m_ops.Begin(); cur != nullptr; cur = cur->next)
|
||||||
{
|
{
|
||||||
sb << m_ops.data[i]->Format(indent + 2) << '\n';
|
sb << cur->value->Format(indent + 2) << '\n';
|
||||||
}
|
}
|
||||||
return sb.view();
|
return sb.view();
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
BlockID m_id;
|
BlockID m_id;
|
||||||
OpView m_ops;
|
DoubleLinkedList<Op*> m_ops;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace IR
|
} // namespace IR
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
#include "parser/nodes.hpp"
|
||||||
|
#include "prelude/error.hpp"
|
||||||
|
#include "prelude/linkedlist.hpp"
|
||||||
#include "prelude/string.hpp"
|
#include "prelude/string.hpp"
|
||||||
#include "parser/ast.hpp"
|
|
||||||
#include "ir/value.hpp"
|
#include "ir/value.hpp"
|
||||||
#include "ir/ops.hpp"
|
#include "ir/ops.hpp"
|
||||||
|
|
||||||
@@ -11,8 +13,8 @@ namespace IR
|
|||||||
class IRBuilder
|
class IRBuilder
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
IRBuilder(const Node *root)
|
IRBuilder(const StringView &filename, const Node *root)
|
||||||
: m_root(root), m_ops(new OpBuilder()) {}
|
: m_root(root), m_ops(new DoubleLinkedList<Op*>), m_filename(filename) {}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// TODO: support other literals
|
// TODO: support other literals
|
||||||
@@ -26,36 +28,36 @@ namespace IR
|
|||||||
{
|
{
|
||||||
auto value = ParseExpression(varDecl->value());
|
auto value = ParseExpression(varDecl->value());
|
||||||
// TODO: gather type information from var decl signature, aka local <int> v = 0;
|
// TODO: gather type information from var decl signature, aka local <int> v = 0;
|
||||||
auto dst = AllocateNamed<Pointer>();
|
auto dst = AllocateNamed<Pointer>(value->GetType());
|
||||||
m_ops->Push(new AllocateOp(dst, value->GetType()));
|
m_ops->Append(m_ops->New(new AllocateOp(dst, value->GetType())));
|
||||||
m_ops->Push(new StoreOp(value, reinterpret_cast<Pointer *>(dst)));
|
m_ops->Append(m_ops->New(new StoreOp(value, reinterpret_cast<Pointer *>(dst))));
|
||||||
m_locals.insert(std::make_pair(varDecl->name(), reinterpret_cast<Pointer *>(dst)));
|
m_locals.insert(std::make_pair(varDecl->name(), reinterpret_cast<Pointer *>(dst)));
|
||||||
}
|
}
|
||||||
|
|
||||||
ValueHandle *ParseVariable(const VariableNode *var)
|
ValueHandle *ParseVariable(const VariableNode *var)
|
||||||
{
|
{
|
||||||
// auto dst = AllocateValue();
|
|
||||||
// m_ops->Push(new LoadOp(dst, var->name()));
|
|
||||||
if (m_locals.find(var->name()) == m_locals.end())
|
if (m_locals.find(var->name()) == m_locals.end())
|
||||||
{
|
{
|
||||||
// TODO: throw proper error
|
// TODO: pass line:offset when Node will have them
|
||||||
assert(0 && "ERROR: variable does not exist");
|
ErrorLogger::Raise(Error::CompileError(m_filename, StringView::FromFormat("use of undefined variable '%s'", var->name().c_str())));
|
||||||
|
assert(false);
|
||||||
}
|
}
|
||||||
return reinterpret_cast<ValueHandle *>(m_locals[var->name()]);
|
auto dst = AllocateNamed<Instruction>(m_locals[var->name()]->GetValueType());
|
||||||
|
m_ops->Append(m_ops->New(new LoadOp(dst, m_locals[var->name()])));
|
||||||
|
return reinterpret_cast<ValueHandle *>(dst);
|
||||||
}
|
}
|
||||||
|
|
||||||
ValueHandle *ParseFnCall(const FnCallNode *fn)
|
ValueHandle *ParseFnCall(const FnCallNode *fn)
|
||||||
{
|
{
|
||||||
// TODO: support multiple args
|
auto args = ValueBuilder();
|
||||||
auto argRegs = ValueBuilder();
|
for (size_t i = 0; i < fn->args().size; ++i)
|
||||||
if (fn->arg() != nullptr)
|
|
||||||
{
|
{
|
||||||
auto arg = ParseExpression(fn->arg());
|
auto arg = ParseExpression(fn->args().data[i]);
|
||||||
argRegs.Push(arg);
|
args.Push(arg);
|
||||||
}
|
}
|
||||||
// TODO: gather return type of the function
|
// TODO: gather return type of the function
|
||||||
auto dst = AllocateUnnamed<Void>();
|
auto dst = AllocateNamed<Instruction>(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;
|
return dst;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,7 +77,7 @@ namespace IR
|
|||||||
}
|
}
|
||||||
|
|
||||||
assert(0 && "unreachable");
|
assert(0 && "unreachable");
|
||||||
return reinterpret_cast<ValueHandle *>(new Void(0));
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
ValueHandle *ParseExpression(const Node *expression)
|
ValueHandle *ParseExpression(const Node *expression)
|
||||||
@@ -91,16 +93,16 @@ namespace IR
|
|||||||
switch (expr->op())
|
switch (expr->op())
|
||||||
{
|
{
|
||||||
case ExpressionNode::Operator::Plus:
|
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;
|
break;
|
||||||
case ExpressionNode::Operator::Multiply:
|
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;
|
break;
|
||||||
case ExpressionNode::Operator::Minus:
|
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;
|
break;
|
||||||
case ExpressionNode::Operator::Divide:
|
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;
|
break;
|
||||||
default:
|
default:
|
||||||
assert(0 && "unreachable");
|
assert(0 && "unreachable");
|
||||||
@@ -129,12 +131,12 @@ namespace IR
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
auto ops = EndBlock();
|
auto ops = EndBlock();
|
||||||
auto block = Block(m_block_counter++, std::move(ops->view()));
|
auto block = Block(m_block_counter++, *ops);
|
||||||
operator delete(ops);
|
operator delete(ops);
|
||||||
return block;
|
return block;
|
||||||
}
|
}
|
||||||
|
|
||||||
OpView Build()
|
DoubleLinkedList<Op*>* Build()
|
||||||
{
|
{
|
||||||
assert(m_root->GetType() == NodeType::Program && "root should be a program");
|
assert(m_root->GetType() == NodeType::Program && "root should be a program");
|
||||||
auto program = reinterpret_cast<const ProgramNode *>(m_root);
|
auto program = reinterpret_cast<const ProgramNode *>(m_root);
|
||||||
@@ -142,30 +144,30 @@ namespace IR
|
|||||||
// Externs
|
// Externs
|
||||||
for (auto &extrn : program->externs())
|
for (auto &extrn : program->externs())
|
||||||
{
|
{
|
||||||
m_ops->Push(new ExternOp(extrn->symbol()));
|
m_ops->Append(m_ops->New(new ExternOp(extrn->symbol())));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Functions
|
// Functions
|
||||||
for (auto &fn : program->funcs())
|
for (auto &fn : program->funcs())
|
||||||
{
|
{
|
||||||
auto block = ParseBlock(fn->body());
|
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:
|
public:
|
||||||
OpView ops() const { return OpView(m_ops->data, m_ops->size); }
|
const DoubleLinkedList<Op*>* ops() const { return m_ops; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void StartBlock()
|
void StartBlock()
|
||||||
{
|
{
|
||||||
m_containers.Push(m_ops);
|
m_containers.Push(m_ops);
|
||||||
m_ops = new OpBuilder();
|
m_ops = new DoubleLinkedList<Op*>;
|
||||||
}
|
}
|
||||||
|
|
||||||
OpBuilder *EndBlock()
|
DoubleLinkedList<Op*> *EndBlock()
|
||||||
{
|
{
|
||||||
assert(m_containers.size > 0 && "containers stack is empty");
|
assert(m_containers.size > 0 && "containers stack is empty");
|
||||||
auto current = m_ops;
|
auto current = m_ops;
|
||||||
@@ -189,13 +191,15 @@ namespace IR
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
const Node *m_root = nullptr;
|
const Node *m_root = nullptr;
|
||||||
|
StringView m_filename;
|
||||||
|
|
||||||
OpBuilder *m_ops = nullptr;
|
DoubleLinkedList<Op*> *m_ops = nullptr;
|
||||||
unsigned int m_value_counter = 0;
|
unsigned int m_value_counter = 0;
|
||||||
unsigned int m_block_counter = 0;
|
unsigned int m_block_counter = 0;
|
||||||
|
|
||||||
std::unordered_map<StringView, Pointer *> m_locals;
|
std::unordered_map<StringView, Pointer *> m_locals;
|
||||||
|
|
||||||
Builder<OpBuilder *> m_containers;
|
Builder<DoubleLinkedList<Op*>*> m_containers;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace IR
|
} // namespace IR
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "prelude/string.hpp"
|
#include "prelude/string.hpp"
|
||||||
#include "parser/nodes.hpp"
|
|
||||||
#include "ir/value.hpp"
|
#include "ir/value.hpp"
|
||||||
#include "ir/op.hpp"
|
#include "ir/op.hpp"
|
||||||
#include "ir/block.hpp"
|
#include "ir/block.hpp"
|
||||||
@@ -21,7 +20,7 @@ namespace IR
|
|||||||
{
|
{
|
||||||
StringBuilder sb;
|
StringBuilder sb;
|
||||||
sb.AppendIndent(indent);
|
sb.AppendIndent(indent);
|
||||||
sb << "EXTRN " << m_symbol.c_str();
|
sb << "extern " << m_symbol.c_str();
|
||||||
return sb.view();
|
return sb.view();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,14 +45,14 @@ namespace IR
|
|||||||
{
|
{
|
||||||
StringBuilder sb;
|
StringBuilder sb;
|
||||||
sb.AppendIndent(indent);
|
sb.AppendIndent(indent);
|
||||||
sb << "LABEL " << m_name.c_str() << ':' << '\n';
|
sb << "label " << m_name.c_str() << ':' << '\n';
|
||||||
sb << m_body.Format(indent);
|
sb << m_body.Format(indent);
|
||||||
return sb.view();
|
return sb.view();
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
const StringView &name() const { return m_name; }
|
const StringView &name() const { return m_name; }
|
||||||
const Block &body() const { return m_body; }
|
Block &body() { return m_body; }
|
||||||
const View<StringView> ¶ms() const { return m_params; }
|
const View<StringView> ¶ms() const { return m_params; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -79,10 +78,13 @@ namespace IR
|
|||||||
{
|
{
|
||||||
StringBuilder sb;
|
StringBuilder sb;
|
||||||
sb.AppendIndent(indent);
|
sb.AppendIndent(indent);
|
||||||
sb << result()->Format() << " = ALLOCATE " << m_typ->Format();
|
sb << result()->Format() << " = allocate " << m_typ->Format();
|
||||||
return sb.view();
|
return sb.view();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
const ValueHandle::Type *Type() const { return m_typ; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ValueHandle::Type *m_typ;
|
ValueHandle::Type *m_typ;
|
||||||
};
|
};
|
||||||
@@ -90,8 +92,8 @@ namespace IR
|
|||||||
class LoadOp : public OpValued
|
class LoadOp : public OpValued
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
LoadOp(ValueHandle *dest, StringView addr)
|
LoadOp(ValueHandle *dest, Pointer *ptr)
|
||||||
: OpValued(dest), m_addr(addr) {}
|
: OpValued(dest), m_ptr(ptr) {}
|
||||||
~LoadOp() {}
|
~LoadOp() {}
|
||||||
|
|
||||||
OP_TYPE(LOAD)
|
OP_TYPE(LOAD)
|
||||||
@@ -100,15 +102,15 @@ namespace IR
|
|||||||
{
|
{
|
||||||
StringBuilder sb;
|
StringBuilder sb;
|
||||||
sb.AppendIndent(indent);
|
sb.AppendIndent(indent);
|
||||||
sb << result()->Format() << " = LOAD \"" << m_addr.c_str() << "\"";
|
sb << result()->Format() << " = load " << result()->GetType()->Format() << ", " << m_ptr->Format();
|
||||||
return sb.view();
|
return sb.view();
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
const StringView &addr() const { return m_addr; }
|
const Pointer* Ptr() const { return m_ptr; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
StringView m_addr;
|
Pointer* m_ptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
class StoreOp : public Op
|
class StoreOp : public Op
|
||||||
@@ -124,7 +126,7 @@ namespace IR
|
|||||||
{
|
{
|
||||||
StringBuilder sb;
|
StringBuilder sb;
|
||||||
sb.AppendIndent(indent);
|
sb.AppendIndent(indent);
|
||||||
sb << "STORE " << src()->Format() << ", " << dst()->Format();
|
sb << "store " << src()->Format() << ", " << dst()->Format();
|
||||||
return sb.view();
|
return sb.view();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,13 +154,14 @@ namespace IR
|
|||||||
switch (m_typ)
|
switch (m_typ)
|
||||||
{
|
{
|
||||||
case OpType::ADD:
|
case OpType::ADD:
|
||||||
return StringView("ADD");
|
return StringView("add");
|
||||||
case OpType::MUL:
|
case OpType::MUL:
|
||||||
return StringView("MUL");
|
return StringView("mul");
|
||||||
case OpType::SUB:
|
case OpType::SUB:
|
||||||
return StringView("SUB");
|
return StringView("sub");
|
||||||
case OpType::DIV:
|
case OpType::DIV:
|
||||||
return StringView("DIV");
|
return StringView("div");
|
||||||
|
default: break;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(false && "unreachable");
|
assert(false && "unreachable");
|
||||||
@@ -198,10 +201,10 @@ namespace IR
|
|||||||
for (size_t i = 0; i < m_args.size; ++i)
|
for (size_t i = 0; i < m_args.size; ++i)
|
||||||
{
|
{
|
||||||
sb.AppendIndent(indent);
|
sb.AppendIndent(indent);
|
||||||
sb << "PARAM " << m_args.data[i]->Format() << '\n';
|
sb << "param " << m_args.data[i]->Format() << '\n';
|
||||||
}
|
}
|
||||||
sb.AppendIndent(indent);
|
sb.AppendIndent(indent);
|
||||||
sb << result()->Format() << " = CALL " << m_callee.c_str();
|
sb << result()->Format() << " = call " << m_callee.c_str();
|
||||||
return sb.view();
|
return sb.view();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
34
include/ir/optimize.hpp
Normal file
34
include/ir/optimize.hpp
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "ir/op.hpp"
|
||||||
|
#include "prelude/linkedlist.hpp"
|
||||||
|
|
||||||
|
namespace IR {
|
||||||
|
|
||||||
|
class Optimizer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual bool Apply(DoubleLinkedList<Op*>& ops) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AllocOptimizer : public Optimizer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
bool Apply(DoubleLinkedList<Op*>& ops) override
|
||||||
|
{
|
||||||
|
for (ListNode<Op*>* cur = ops.Begin(); cur != nullptr; )
|
||||||
|
{
|
||||||
|
auto op = cur->value;
|
||||||
|
if (op->GetType() == OpType::ALLOCATE)
|
||||||
|
{
|
||||||
|
auto tmp = cur;
|
||||||
|
cur = cur->next;
|
||||||
|
ops.SpliceFront(tmp);
|
||||||
|
} else {
|
||||||
|
cur = cur->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
#include <cstdint>
|
||||||
#include "prelude/string.hpp"
|
#include "prelude/string.hpp"
|
||||||
|
|
||||||
namespace IR
|
namespace IR
|
||||||
@@ -104,8 +105,8 @@ namespace IR
|
|||||||
class Pointer : public ValueHandle
|
class Pointer : public ValueHandle
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Pointer(unsigned int id)
|
Pointer(unsigned int id, const Type* value_typ)
|
||||||
: ValueHandle(id)
|
: ValueHandle(id), m_value_typ(new Type(value_typ))
|
||||||
{
|
{
|
||||||
m_type = new Type{Type::Kind::Ptr};
|
m_type = new Type{Type::Kind::Ptr};
|
||||||
}
|
}
|
||||||
@@ -120,30 +121,12 @@ namespace IR
|
|||||||
return sb.view();
|
return sb.view();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
|
||||||
Type *m_type;
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: Remove void value and use void type only
|
|
||||||
class Void : public ValueHandle
|
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
Void(unsigned int id)
|
const Type *GetValueType() const { return m_value_typ; }
|
||||||
: ValueHandle(id)
|
|
||||||
{
|
|
||||||
m_type = new Type{Type::Kind::Void};
|
|
||||||
}
|
|
||||||
|
|
||||||
const Type *GetType() const override { return m_type; }
|
|
||||||
|
|
||||||
public:
|
|
||||||
virtual StringView Format() const override
|
|
||||||
{
|
|
||||||
return m_type->Format();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Type *m_type;
|
Type *m_type;
|
||||||
|
Type *m_value_typ;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Instruction : public ValueHandle
|
class Instruction : public ValueHandle
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <cstring>
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <vector>
|
|
||||||
#include "parser/lexer.hpp"
|
#include "parser/lexer.hpp"
|
||||||
#include "parser/nodes.hpp"
|
#include "parser/nodes.hpp"
|
||||||
|
#include "prelude/error.hpp"
|
||||||
|
|
||||||
class AstParser
|
class AstParser
|
||||||
{
|
{
|
||||||
@@ -46,14 +45,15 @@ 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 = nullptr;
|
Builder<Node*> args;
|
||||||
// TODO: support multiple arguments
|
while (m_lexer->seek_token()->token != ')')
|
||||||
if (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(')');
|
m_lexer->NextExpect(')');
|
||||||
return new FnCallNode(name, arg);
|
return new FnCallNode(name, std::move(args.view()));
|
||||||
}
|
}
|
||||||
|
|
||||||
Node* ParseFactor()
|
Node* ParseFactor()
|
||||||
@@ -80,8 +80,8 @@ public:
|
|||||||
return new VariableNode(name);
|
return new VariableNode(name);
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
fprintf(stderr, "%s:%d:%d: ERROR: unexpected token while parsing %ld\n", m_lexer->filename(), token->line_number, token->offset_start, token->token);
|
// fprintf(stderr, "%s:%d:%d: ERROR: unexpected token while parsing %ld\n", m_lexer->filename(), token->line_number, token->offset_start, token->token);
|
||||||
Exit(1);
|
ErrorLogger::Raise(Error::ParseError(m_lexer->filename(), StringView::FromFormat("unexpected token while parsing '%d'", token->token), token->line_number, token->offset_start));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,7 +96,7 @@ public:
|
|||||||
{
|
{
|
||||||
m_lexer->NextToken();
|
m_lexer->NextToken();
|
||||||
ExpressionNode::Operator eop;
|
ExpressionNode::Operator eop;
|
||||||
assert((int)ExpressionNode::Operator::COUNT_OPERATORS == 4 && "some operators may not be handled");
|
assert(static_cast<size_t>(ExpressionNode::Operator::COUNT_OPERATORS) == 4 && "some operators may not be handled");
|
||||||
switch((char)op->token)
|
switch((char)op->token)
|
||||||
{
|
{
|
||||||
case '/':
|
case '/':
|
||||||
@@ -124,7 +124,7 @@ public:
|
|||||||
{
|
{
|
||||||
m_lexer->NextToken();
|
m_lexer->NextToken();
|
||||||
ExpressionNode::Operator eop;
|
ExpressionNode::Operator eop;
|
||||||
assert((int)ExpressionNode::Operator::COUNT_OPERATORS == 4 && "some operators may not be handled");
|
assert(static_cast<size_t>(ExpressionNode::Operator::COUNT_OPERATORS) == 4 && "some operators may not be handled");
|
||||||
switch((char)op->token)
|
switch((char)op->token)
|
||||||
{
|
{
|
||||||
case '+':
|
case '+':
|
||||||
@@ -157,8 +157,7 @@ public:
|
|||||||
Node* ParseStatement()
|
Node* ParseStatement()
|
||||||
{
|
{
|
||||||
auto token = m_lexer->seek_token();
|
auto token = m_lexer->seek_token();
|
||||||
// TODO: proper error handling
|
assert(token != nullptr);
|
||||||
assert(token != nullptr && "next token should be available");
|
|
||||||
switch(token->token)
|
switch(token->token)
|
||||||
{
|
{
|
||||||
case TokenType::Local: return ParseVarDecl();
|
case TokenType::Local: return ParseVarDecl();
|
||||||
@@ -178,13 +177,13 @@ public:
|
|||||||
auto token = m_lexer->token();
|
auto token = m_lexer->token();
|
||||||
switch(token.token)
|
switch(token.token)
|
||||||
{
|
{
|
||||||
case TokenType::Extern: program->PushExtern(ParseExtern()); break;
|
|
||||||
case TokenType::Fn: program->PushFunction(ParseFnDecl()); break;
|
case TokenType::Fn: program->PushFunction(ParseFnDecl()); break;
|
||||||
|
case TokenType::Extern: program->PushExtern(ParseExtern()); break;
|
||||||
default: {
|
default: {
|
||||||
fprintf(stderr, "%s:%d:%d: ERROR: unexpected token while parsing %ld\n", m_lexer->filename(), token.line_number, token.offset_start, token.token);
|
ErrorLogger::Raise(Error::ParseError(m_lexer->filename(), StringView::FromFormat("unexpected token while parsing '%d'", token.token), token.line_number, token.offset_start));
|
||||||
Exit(1);
|
assert(false);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <iostream>
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
|
|
||||||
@@ -48,13 +47,13 @@ inline bool operator!=(char c, TokenType t)
|
|||||||
|
|
||||||
struct Token
|
struct Token
|
||||||
{
|
{
|
||||||
TokenType token;
|
TokenType token = TokenType::Unknown;
|
||||||
long int_number;
|
long int_number = 0;
|
||||||
// null-terminated
|
// null-terminated
|
||||||
StringView string;
|
StringView string;
|
||||||
long line_number;
|
long line_number = 0;
|
||||||
long offset_start;
|
long offset_start = 0;
|
||||||
long offset_end;
|
long offset_end = 0;
|
||||||
public:
|
public:
|
||||||
Token(TokenType t) : token(t) {}
|
Token(TokenType t) : token(t) {}
|
||||||
Token(TokenType t, long lnumber, long soffset, long eoffset)
|
Token(TokenType t, long lnumber, long soffset, long eoffset)
|
||||||
@@ -74,7 +73,7 @@ public:
|
|||||||
auto lnl = m_last_newline;
|
auto lnl = m_last_newline;
|
||||||
if (!NextToken())
|
if (!NextToken())
|
||||||
{
|
{
|
||||||
return nullptr;
|
return new Token(TokenType::Eof, m_line, m_pos - m_last_newline, m_pos - m_last_newline);
|
||||||
}
|
}
|
||||||
auto seeked = m_token;
|
auto seeked = m_token;
|
||||||
m_token = s;
|
m_token = s;
|
||||||
@@ -93,66 +92,98 @@ public:
|
|||||||
public:
|
public:
|
||||||
bool NextToken()
|
bool NextToken()
|
||||||
{
|
{
|
||||||
if (m_pos >= m_code.size || m_code.data[m_pos] == '\0')
|
auto len = m_code.len();
|
||||||
|
|
||||||
|
auto peek = [&]() -> char {
|
||||||
|
return (m_pos < len) ? m_code.data[m_pos] : '\0';
|
||||||
|
};
|
||||||
|
|
||||||
|
auto advance = [&]() -> char {
|
||||||
|
return (m_pos < len) ? m_code.data[m_pos++] : '\0';
|
||||||
|
};
|
||||||
|
|
||||||
|
// IMPORTANT: >= not >
|
||||||
|
if (m_pos >= len)
|
||||||
{
|
{
|
||||||
m_token = Token(TokenType::Eof);
|
m_token = Token(TokenType::Eof);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
char c = m_code.data[m_pos++];
|
char c = advance();
|
||||||
|
|
||||||
while(std::isspace(c)) {
|
// skip whitespace safely
|
||||||
|
while (c != '\0' && std::isspace((unsigned char)c))
|
||||||
|
{
|
||||||
if (c == '\n')
|
if (c == '\n')
|
||||||
{
|
{
|
||||||
m_line++;
|
m_line++;
|
||||||
m_last_newline = m_pos;
|
m_last_newline = m_pos;
|
||||||
}
|
}
|
||||||
c = m_code.data[m_pos++];
|
|
||||||
|
if (m_pos >= len) // reached real EOF while skipping whitespace
|
||||||
|
{
|
||||||
|
m_token = Token(TokenType::Eof);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
c = advance();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (std::isalpha(c) != 0 || c == '_')
|
if (c == '\0' || m_pos > len) // paranoia guard
|
||||||
|
{
|
||||||
|
m_token = Token(TokenType::Eof);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// identifier
|
||||||
|
if (std::isalpha((unsigned char)c) || c == '_')
|
||||||
{
|
{
|
||||||
StringBuilder s;
|
StringBuilder s;
|
||||||
long offset_start = m_pos - m_last_newline;
|
long offset_start = m_pos - m_last_newline - 1; // -1 because we already consumed c
|
||||||
s.Push(c);
|
s.Push(c);
|
||||||
// id
|
|
||||||
while (std::isalpha(m_code.data[m_pos]) != 0 || m_code.data[m_pos] == '_')
|
// NOTE: usually identifiers allow digits after first char; add isdigit if you want
|
||||||
|
while (true)
|
||||||
{
|
{
|
||||||
s.Push(m_code.data[m_pos++]);
|
char p = peek();
|
||||||
|
if (!(std::isalpha((unsigned char)p) || p == '_'))
|
||||||
|
break;
|
||||||
|
s.Push(advance());
|
||||||
}
|
}
|
||||||
|
|
||||||
s.Push('\0');
|
s.Push('\0');
|
||||||
m_token = Token(TokenType::Id, m_line, offset_start, m_pos - m_last_newline);
|
m_token = Token(TokenType::Id, m_line, offset_start, m_pos - m_last_newline);
|
||||||
m_token.string = s.view();
|
m_token.string = s.view();
|
||||||
|
|
||||||
if (strcmp("extern", m_token.string.c_str()) == 0)
|
if (strcmp("extern", m_token.string.c_str()) == 0) m_token.token = TokenType::Extern;
|
||||||
{
|
else if (strcmp("fn", m_token.string.c_str()) == 0) m_token.token = TokenType::Fn;
|
||||||
m_token.token = TokenType::Extern;
|
else if (strcmp("local", m_token.string.c_str()) == 0) m_token.token = TokenType::Local;
|
||||||
}
|
|
||||||
|
|
||||||
if (strcmp("fn", m_token.string.c_str()) == 0)
|
|
||||||
{
|
|
||||||
m_token.token = TokenType::Fn;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strcmp("local", m_token.string.c_str()) == 0)
|
|
||||||
{
|
|
||||||
m_token.token = TokenType::Local;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (std::isdigit(c) != 0)
|
// integer (hex supported)
|
||||||
|
if (std::isdigit((unsigned char)c))
|
||||||
{
|
{
|
||||||
StringBuilder s;
|
StringBuilder s;
|
||||||
long offset_start = m_pos - m_last_newline;
|
long offset_start = m_pos - m_last_newline - 1;
|
||||||
bool hex = c == '0' && m_code.data[m_pos] == 'x';
|
|
||||||
|
bool hex = (c == '0' && peek() == 'x');
|
||||||
s.Push(c);
|
s.Push(c);
|
||||||
// integer (could be hex)
|
|
||||||
while (std::isdigit(m_code.data[m_pos]) != 0 || (hex && std::isalpha(m_code.data[m_pos]) != 0))
|
if (hex) s.Push(advance()); // consume 'x'
|
||||||
|
|
||||||
|
while (true)
|
||||||
{
|
{
|
||||||
s.Push(m_code.data[m_pos++]);
|
char p = peek();
|
||||||
|
if (std::isdigit((unsigned char)p) ||
|
||||||
|
(hex && std::isxdigit((unsigned char)p)))
|
||||||
|
{
|
||||||
|
s.Push(advance());
|
||||||
|
}
|
||||||
|
else break;
|
||||||
}
|
}
|
||||||
|
|
||||||
s.Push('\0');
|
s.Push('\0');
|
||||||
m_token = Token(TokenType::IntLiteral, m_line, offset_start, m_pos - m_last_newline);
|
m_token = Token(TokenType::IntLiteral, m_line, offset_start, m_pos - m_last_newline);
|
||||||
m_token.int_number = std::strtol(s.data, nullptr, hex ? 16 : 10);
|
m_token.int_number = std::strtol(s.data, nullptr, hex ? 16 : 10);
|
||||||
@@ -160,7 +191,8 @@ public:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_token = Token((TokenType)c, m_line, m_pos - m_last_newline, m_pos - m_last_newline + 1);
|
// single-char token fallback
|
||||||
|
m_token = Token((TokenType)c, m_line, m_pos - m_last_newline - 1, m_pos - m_last_newline);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -144,20 +144,17 @@ class FnCallNode : public Node
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
// TODO: support multiple arguments
|
// TODO: support multiple arguments
|
||||||
FnCallNode(const StringView& name, Node* arg)
|
FnCallNode(const StringView& name, View<Node*>&& arg)
|
||||||
: m_name(name), m_arg(arg) {}
|
: m_name(name), m_args(arg) {}
|
||||||
~FnCallNode() override {
|
~FnCallNode() override = default;
|
||||||
delete m_arg;
|
|
||||||
}
|
|
||||||
|
|
||||||
NODE_TYPE(FnCall)
|
NODE_TYPE(FnCall)
|
||||||
public:
|
public:
|
||||||
const StringView& name() const { return m_name; }
|
const StringView& name() const { return m_name; }
|
||||||
// TODO: support multiple args
|
const View<Node*>& args() const { return m_args; }
|
||||||
const Node* arg() const { return m_arg; }
|
|
||||||
private:
|
private:
|
||||||
StringView m_name;
|
StringView m_name;
|
||||||
Node* m_arg;
|
View<Node*> m_args;
|
||||||
};
|
};
|
||||||
|
|
||||||
class VariableNode : public Node
|
class VariableNode : public Node
|
||||||
|
|||||||
53
include/prelude/error.hpp
Normal file
53
include/prelude/error.hpp
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "prelude/string.hpp"
|
||||||
|
#include <print>
|
||||||
|
|
||||||
|
struct Error
|
||||||
|
{
|
||||||
|
enum class Type
|
||||||
|
{
|
||||||
|
PARSE = 0,
|
||||||
|
TYPE,
|
||||||
|
COMPILE,
|
||||||
|
COUNT_ERROR_TYPES,
|
||||||
|
};
|
||||||
|
|
||||||
|
Type type;
|
||||||
|
StringView message;
|
||||||
|
StringView filename;
|
||||||
|
long line = 0;
|
||||||
|
long offset = 0;
|
||||||
|
public:
|
||||||
|
static Error CompileError(StringView filename, StringView message, long line = 0, long offset = 0)
|
||||||
|
{
|
||||||
|
return Error{Type::COMPILE, message, filename, line, offset};
|
||||||
|
}
|
||||||
|
|
||||||
|
static Error ParseError(StringView filename, StringView message, long line = 0, long offset = 0)
|
||||||
|
{
|
||||||
|
return Error{Type::PARSE, message, filename, line, offset};
|
||||||
|
}
|
||||||
|
|
||||||
|
static Error TypeError(StringView filename, StringView message, long line = 0, long offset = 0)
|
||||||
|
{
|
||||||
|
return Error{Type::TYPE, message, filename, line, offset};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr const char* ErrorTypeNames[] = {
|
||||||
|
"ParseError",
|
||||||
|
"TypeError",
|
||||||
|
"CompileError",
|
||||||
|
};
|
||||||
|
|
||||||
|
class ErrorLogger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static void Raise(const Error &error)
|
||||||
|
{
|
||||||
|
StringBuilder sb;
|
||||||
|
sb.AppendFormat("%s:%zu:%zu %s: %s", error.filename.c_str(), error.line, error.offset, ErrorTypeNames[static_cast<size_t>(error.type)], error.message.c_str());
|
||||||
|
std::println("{}", sb.c_str());
|
||||||
|
std::exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
71
include/prelude/file.hpp
Normal file
71
include/prelude/file.hpp
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "prelude/string.hpp"
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
class File
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum class Mode
|
||||||
|
{
|
||||||
|
WRITE,
|
||||||
|
READ,
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
File(FILE* handle, Mode mode)
|
||||||
|
: m_handle(handle), m_mode(mode) {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
static File Open(const StringView& filename, Mode mode)
|
||||||
|
{
|
||||||
|
StringBuilder sb;
|
||||||
|
switch(mode)
|
||||||
|
{
|
||||||
|
case Mode::WRITE:
|
||||||
|
sb.Append('w');
|
||||||
|
break;
|
||||||
|
case Mode::READ:
|
||||||
|
sb.Append('r');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
FILE* handle = fopen(filename.c_str(), sb.c_str());
|
||||||
|
return File(handle, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
inline bool Writable() const { return m_mode == File::Mode::WRITE; }
|
||||||
|
inline bool Readable() const { return m_mode == File::Mode::READ; }
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool Write(const StringView& content)
|
||||||
|
{
|
||||||
|
assert(Writable() && "attempt to write to a file in read only mode");
|
||||||
|
if (fwrite(content.c_str(), sizeof(char), content.len(), m_handle) != content.len())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool ReadAll(StringBuilder& sb)
|
||||||
|
{
|
||||||
|
assert(Readable() && "attempt to read from a file in write only mode");
|
||||||
|
if (fseek(m_handle, 0, SEEK_END) != 0) return false;
|
||||||
|
|
||||||
|
auto size = ftell(m_handle);
|
||||||
|
if (size == -1) return false;
|
||||||
|
|
||||||
|
sb.ensure_extra(size);
|
||||||
|
|
||||||
|
size_t wrote = fread(sb.data, sizeof(char), size, m_handle);
|
||||||
|
if (wrote == 0) return false;
|
||||||
|
|
||||||
|
sb.size = wrote;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
FILE* m_handle;
|
||||||
|
Mode m_mode;
|
||||||
|
};
|
||||||
102
include/prelude/linkedlist.hpp
Normal file
102
include/prelude/linkedlist.hpp
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <cstddef>
|
||||||
|
#include "prelude/string.hpp"
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct ListNode
|
||||||
|
{
|
||||||
|
T value;
|
||||||
|
ListNode* prev = nullptr;
|
||||||
|
ListNode* next = nullptr;
|
||||||
|
public:
|
||||||
|
T& get() noexcept { return value; }
|
||||||
|
const T& get() const noexcept { return value; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
class DoubleLinkedList {
|
||||||
|
public:
|
||||||
|
DoubleLinkedList() = default;
|
||||||
|
~DoubleLinkedList() = default;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static DoubleLinkedList<T> FromView(const View<T> &view)
|
||||||
|
{
|
||||||
|
auto list = DoubleLinkedList<T>();
|
||||||
|
for (size_t i = 0; i < view.size; ++i)
|
||||||
|
{
|
||||||
|
list.Append(new ListNode<T>(view.data[i]));
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
View<T> ToView()
|
||||||
|
{
|
||||||
|
Builder<T> b;
|
||||||
|
for(ListNode<T>* cur = Begin(); cur != nullptr; cur = cur->next)
|
||||||
|
{
|
||||||
|
b.Push(cur->value);
|
||||||
|
}
|
||||||
|
return b.view();
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
ListNode<T>* New(T value) const { return new ListNode<T>(value); }
|
||||||
|
|
||||||
|
public:
|
||||||
|
void Append(ListNode<T>* node)
|
||||||
|
{
|
||||||
|
if (IsEmpty())
|
||||||
|
{
|
||||||
|
node->next = nullptr;
|
||||||
|
node->prev = nullptr;
|
||||||
|
tail = node;
|
||||||
|
head = node;
|
||||||
|
} else {
|
||||||
|
head->next = node;
|
||||||
|
node->prev = head;
|
||||||
|
node->next = nullptr;
|
||||||
|
head = node;
|
||||||
|
}
|
||||||
|
m_size++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Prepend(ListNode<T>* node)
|
||||||
|
{
|
||||||
|
if (IsEmpty())
|
||||||
|
{
|
||||||
|
Append(node);
|
||||||
|
} else {
|
||||||
|
tail->prev = node;
|
||||||
|
node->next = tail;
|
||||||
|
node->prev = nullptr;
|
||||||
|
tail = node;
|
||||||
|
m_size++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpliceFront(ListNode<T>* node)
|
||||||
|
{
|
||||||
|
if (m_size == 1 && tail == node && head == node) return;
|
||||||
|
assert((node->next || node->prev) && "node should be inside of the linked list");
|
||||||
|
if (node == tail) return;
|
||||||
|
if (node == head) {
|
||||||
|
node->prev->next = nullptr;
|
||||||
|
head = node->prev;
|
||||||
|
} else {
|
||||||
|
node->prev->next = node->next;
|
||||||
|
node->next->prev = node->prev;
|
||||||
|
}
|
||||||
|
m_size--;
|
||||||
|
Prepend(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsEmpty() const { return m_size == 0; }
|
||||||
|
|
||||||
|
ListNode<T>* Begin() const { return tail; }
|
||||||
|
ListNode<T>* End() const { return head; }
|
||||||
|
private:
|
||||||
|
ListNode<T>* head = nullptr;
|
||||||
|
ListNode<T>* tail = nullptr;
|
||||||
|
size_t m_size = 0;
|
||||||
|
};
|
||||||
@@ -67,9 +67,6 @@ public:
|
|||||||
const T* end() const { return data + size; }
|
const T* end() const { return data + size; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// using StringView = View<char>;
|
|
||||||
|
|
||||||
class StringView final : public View<char>
|
class StringView final : public View<char>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -104,6 +101,32 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static StringView FromFormat(const char* fmt, ...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
|
||||||
|
va_list args2;
|
||||||
|
va_copy(args2, args);
|
||||||
|
|
||||||
|
int len = std::vsnprintf(nullptr, 0, fmt, args2);
|
||||||
|
va_end(args2);
|
||||||
|
|
||||||
|
if (len < 0) {
|
||||||
|
va_end(args);
|
||||||
|
return StringView();
|
||||||
|
}
|
||||||
|
|
||||||
|
char* str = new char[len + 1];
|
||||||
|
std::vsnprintf(str, len + 1, fmt, args);
|
||||||
|
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
return StringView(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t len() const { return strlen(data); }
|
||||||
|
|
||||||
const char* c_str() const { return data; }
|
const char* c_str() const { return data; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
43
src/main.cpp
43
src/main.cpp
@@ -1,11 +1,15 @@
|
|||||||
#include <iostream>
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <print>
|
#include <print>
|
||||||
|
#include "codegen/targets/fasm_x86_64_linux.hpp"
|
||||||
|
#include "ir/op.hpp"
|
||||||
|
#include "ir/optimize.hpp"
|
||||||
#include "parser/lexer.hpp"
|
#include "parser/lexer.hpp"
|
||||||
#include "parser/ast.hpp"
|
#include "parser/ast.hpp"
|
||||||
#include "ir/ir.hpp"
|
#include "ir/ir.hpp"
|
||||||
|
#include "prelude/file.hpp"
|
||||||
|
#include "prelude/string.hpp"
|
||||||
|
|
||||||
// #include "codegen/fasm_stack.hpp"
|
// #include "codegen/fasm_stack.hpp"
|
||||||
|
|
||||||
@@ -53,29 +57,38 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
auto program = parser.Parse();
|
auto program = parser.Parse();
|
||||||
|
|
||||||
IR::IRBuilder irBuilder(program);
|
IR::IRBuilder irBuilder(filename, program);
|
||||||
|
|
||||||
auto ops = irBuilder.Build();
|
auto ops = irBuilder.Build();
|
||||||
|
|
||||||
for (size_t i = 0; i < ops.size; ++i)
|
for (auto cur = ops->Begin(); cur != nullptr; cur = cur->next)
|
||||||
{
|
{
|
||||||
printf("%s\n", ops.data[i]->Format(0).c_str());
|
auto op = cur->value;
|
||||||
|
if (op->GetType() == IR::OpType::FN)
|
||||||
|
{
|
||||||
|
auto fn = reinterpret_cast<IR::FnOp*>(op);
|
||||||
|
|
||||||
|
auto allocaoptimz = IR::AllocOptimizer();
|
||||||
|
printf("fn <%s>: status = %d\n", fn->name().c_str(), allocaoptimz.Apply(fn->body().ops()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// StackFasmX86_64Generator gen;
|
for (auto cur = ops->Begin(); cur != nullptr; cur = cur->next)
|
||||||
|
{
|
||||||
|
auto op = cur->value;
|
||||||
|
printf("%s\n", op->Format(0).c_str());
|
||||||
|
}
|
||||||
|
|
||||||
// gen.Generate(filename, ops);
|
FasmX86_64Generator gen;
|
||||||
|
|
||||||
// StringView output = gen.GetOutput();
|
gen.Generate(ops);
|
||||||
|
|
||||||
// FILE *file = fopen("out.asm", "w");
|
auto output = File::Open("example.asm", File::Mode::WRITE);
|
||||||
|
if (!output.Write(gen.GetOutput()))
|
||||||
// fwrite(output.c_str(), output.size - 1, sizeof(char), file);
|
{
|
||||||
|
fprintf(stderr, "ERROR: Failed to write IR to a file");
|
||||||
// fclose(file);
|
}
|
||||||
|
std::println("OK");
|
||||||
// system("fasm out.asm");
|
|
||||||
// system("gcc -o out out.o");
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user