diff --git a/.gitignore b/.gitignore index d163863..a4fb4fb 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -build/ \ No newline at end of file +build/ +.cache/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 9d0e055..a6b48b2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,8 @@ cmake_minimum_required(VERSION 3.16.0 FATAL_ERROR) project(pl VERSION 0.0.1 LANGUAGES C CXX) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + set(CMAKE_CXX_STANDARD 23) set(SOURCES src/main.cpp diff --git a/include/ir/ir.hpp b/include/ir/ir.hpp index e46e25c..2bd5379 100644 --- a/include/ir/ir.hpp +++ b/include/ir/ir.hpp @@ -1,7 +1,7 @@ #pragma once #include +#include "parser/nodes.hpp" #include "prelude/string.hpp" -#include "parser/ast.hpp" #include "ir/value.hpp" #include "ir/ops.hpp" @@ -26,7 +26,7 @@ namespace IR { auto value = ParseExpression(varDecl->value()); // TODO: gather type information from var decl signature, aka local v = 0; - auto dst = AllocateNamed(); + auto dst = AllocateNamed(value->GetType()); m_ops->Push(new AllocateOp(dst, value->GetType())); m_ops->Push(new StoreOp(value, reinterpret_cast(dst))); m_locals.insert(std::make_pair(varDecl->name(), reinterpret_cast(dst))); @@ -34,14 +34,14 @@ namespace IR 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()) { // TODO: throw proper error assert(0 && "ERROR: variable does not exist"); } - return reinterpret_cast(m_locals[var->name()]); + auto dst = AllocateNamed(m_locals[var->name()]->GetValueType()); + m_ops->Push(new LoadOp(dst, m_locals[var->name()])); + return reinterpret_cast(dst); } ValueHandle *ParseFnCall(const FnCallNode *fn) @@ -54,7 +54,7 @@ namespace IR argRegs.Push(arg); } // TODO: gather return type of the function - auto dst = AllocateUnnamed(); + auto dst = VoidValue(); m_ops->Push(new CallOp(dst, fn->name(), argRegs.view())); return dst; } @@ -75,7 +75,7 @@ namespace IR } assert(0 && "unreachable"); - return reinterpret_cast(new Void(0)); + return reinterpret_cast(new Void()); } ValueHandle *ParseExpression(const Node *expression) @@ -187,12 +187,18 @@ namespace IR return new V(ValueHandle::kNoId, std::forward(args)...); } + ValueHandle *VoidValue() + { + return new Void(); + } + private: const Node *m_root = nullptr; OpBuilder *m_ops = nullptr; unsigned int m_value_counter = 0; unsigned int m_block_counter = 0; + std::unordered_map m_locals; Builder m_containers; diff --git a/include/ir/ops.hpp b/include/ir/ops.hpp index 48ea214..f069e77 100644 --- a/include/ir/ops.hpp +++ b/include/ir/ops.hpp @@ -1,6 +1,5 @@ #pragma once #include "prelude/string.hpp" -#include "parser/nodes.hpp" #include "ir/value.hpp" #include "ir/op.hpp" #include "ir/block.hpp" @@ -21,7 +20,7 @@ namespace IR { StringBuilder sb; sb.AppendIndent(indent); - sb << "EXTRN " << m_symbol.c_str(); + sb << "extern " << m_symbol.c_str(); return sb.view(); } @@ -46,7 +45,7 @@ namespace IR { StringBuilder sb; sb.AppendIndent(indent); - sb << "LABEL " << m_name.c_str() << ':' << '\n'; + sb << "label " << m_name.c_str() << ':' << '\n'; sb << m_body.Format(indent); return sb.view(); } @@ -79,7 +78,7 @@ namespace IR { StringBuilder sb; sb.AppendIndent(indent); - sb << result()->Format() << " = ALLOCATE " << m_typ->Format(); + sb << result()->Format() << " = allocate " << m_typ->Format(); return sb.view(); } @@ -90,8 +89,8 @@ namespace IR class LoadOp : public OpValued { public: - LoadOp(ValueHandle *dest, StringView addr) - : OpValued(dest), m_addr(addr) {} + LoadOp(ValueHandle *dest, Pointer *ptr) + : OpValued(dest), m_ptr(ptr) {} ~LoadOp() {} OP_TYPE(LOAD) @@ -100,15 +99,12 @@ namespace IR { StringBuilder sb; sb.AppendIndent(indent); - sb << result()->Format() << " = LOAD \"" << m_addr.c_str() << "\""; + sb << result()->Format() << " = load " << m_ptr->Format(); return sb.view(); } - public: - const StringView &addr() const { return m_addr; } - private: - StringView m_addr; + Pointer* m_ptr; }; class StoreOp : public Op @@ -124,7 +120,7 @@ namespace IR { StringBuilder sb; sb.AppendIndent(indent); - sb << "STORE " << src()->Format() << ", " << dst()->Format(); + sb << "store " << src()->Format() << ", " << dst()->Format(); return sb.view(); } @@ -152,13 +148,14 @@ namespace IR switch (m_typ) { case OpType::ADD: - return StringView("ADD"); + return StringView("add"); case OpType::MUL: - return StringView("MUL"); + return StringView("mul"); case OpType::SUB: - return StringView("SUB"); + return StringView("sub"); case OpType::DIV: - return StringView("DIV"); + return StringView("div"); + default: break; } assert(false && "unreachable"); @@ -198,10 +195,10 @@ namespace IR for (size_t i = 0; i < m_args.size; ++i) { sb.AppendIndent(indent); - sb << "PARAM " << m_args.data[i]->Format() << '\n'; + sb << "param " << m_args.data[i]->Format() << '\n'; } sb.AppendIndent(indent); - sb << result()->Format() << " = CALL " << m_callee.c_str(); + sb << result()->Format() << " = call " << m_callee.c_str(); return sb.view(); } diff --git a/include/ir/optimize.hpp b/include/ir/optimize.hpp new file mode 100644 index 0000000..211f1f4 --- /dev/null +++ b/include/ir/optimize.hpp @@ -0,0 +1,34 @@ +#pragma once +#include "ir/op.hpp" +#include "prelude/linkedlist.hpp" + +namespace IR { + +class Optimizer +{ +public: + virtual bool Apply(DoubleLinkedList& ops) = 0; +}; + +class AllocOptimizer : public Optimizer +{ +public: + bool Apply(DoubleLinkedList& ops) override + { + for (ListNode* 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; + } +}; + +} diff --git a/include/ir/value.hpp b/include/ir/value.hpp index 59dc248..57c1bbf 100644 --- a/include/ir/value.hpp +++ b/include/ir/value.hpp @@ -1,4 +1,5 @@ #pragma once +#include #include "prelude/string.hpp" namespace IR @@ -104,8 +105,8 @@ namespace IR class Pointer : public ValueHandle { public: - Pointer(unsigned int id) - : ValueHandle(id) + Pointer(unsigned int id, const Type* value_typ) + : ValueHandle(id), m_value_typ(new Type(value_typ)) { m_type = new Type{Type::Kind::Ptr}; } @@ -120,16 +121,19 @@ namespace IR return sb.view(); } + public: + const Type *GetValueType() const { return m_value_typ; } + private: Type *m_type; + Type *m_value_typ; }; - // TODO: Remove void value and use void type only class Void : public ValueHandle { public: - Void(unsigned int id) - : ValueHandle(id) + Void() + : ValueHandle() { m_type = new Type{Type::Kind::Void}; } diff --git a/include/parser/ast.hpp b/include/parser/ast.hpp index d08db77..4aed12d 100644 --- a/include/parser/ast.hpp +++ b/include/parser/ast.hpp @@ -1,7 +1,5 @@ #pragma once -#include #include -#include #include "parser/lexer.hpp" #include "parser/nodes.hpp" diff --git a/include/parser/lexer.hpp b/include/parser/lexer.hpp index 669552f..99163b2 100644 --- a/include/parser/lexer.hpp +++ b/include/parser/lexer.hpp @@ -1,5 +1,4 @@ #pragma once -#include #include #include @@ -159,7 +158,7 @@ public: m_token.string = s.data; return true; } - + m_token = Token((TokenType)c, m_line, m_pos - m_last_newline, m_pos - m_last_newline + 1); return true; } @@ -196,4 +195,4 @@ private: size_t m_pos = 0; size_t m_line = 1; size_t m_last_newline = 0; -}; \ No newline at end of file +}; diff --git a/include/prelude/file.hpp b/include/prelude/file.hpp new file mode 100644 index 0000000..b765200 --- /dev/null +++ b/include/prelude/file.hpp @@ -0,0 +1,79 @@ +#pragma once +#include "prelude/string.hpp" +#include + +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.size - 1, m_handle) == 0) + { + return false; + } + return true; + } + +public: + StringView ReadAll(bool* status = nullptr) + { + auto fail = [&]() -> StringView { + if (status) *status = false; + return {}; + }; + + if (status) *status = true; + + assert(Readable() && "attempt to read from a file in write only mode"); + if (fseek(m_handle, 0, SEEK_END) != 0) return fail(); + + auto size = ftell(m_handle); + if (size == -1 && status) return fail(); + + StringBuilder sb; + sb.ensure_extra(size); + + size_t wrote = fread(sb.data, sizeof(char), size, m_handle); + if (wrote == 0) return fail(); + + sb.size = wrote; + return sb.view(); + } + +private: + FILE* m_handle; + Mode m_mode; +}; diff --git a/include/prelude/linkedlist.hpp b/include/prelude/linkedlist.hpp new file mode 100644 index 0000000..6dc8a56 --- /dev/null +++ b/include/prelude/linkedlist.hpp @@ -0,0 +1,85 @@ +#pragma once +#include +#include "prelude/string.hpp" + +template +struct ListNode +{ + T value; + ListNode* prev = nullptr; + ListNode* next = nullptr; +}; + +template +class DoubleLinkedList { +public: + DoubleLinkedList() = default; + +public: + static DoubleLinkedList FromView(const View &view) + { + auto list = DoubleLinkedList(); + for (size_t i = 0; i < view.size; ++i) + { + list.Append(new ListNode(view.data[i])); + } + return list; + } + +public: + void Append(ListNode* 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* node) + { + if (IsEmpty()) + { + Append(node); + } else { + tail->prev = node; + node->next = tail; + node->prev = nullptr; + tail = node; + m_size++; + } + } + + void SpliceFront(ListNode* 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* Begin() const { return tail; } + ListNode* End() const { return head; } +private: + ListNode* head = nullptr; + ListNode* tail = nullptr; + size_t m_size = 0; +}; diff --git a/include/prelude/string.hpp b/include/prelude/string.hpp index a546556..74b26a8 100644 --- a/include/prelude/string.hpp +++ b/include/prelude/string.hpp @@ -68,8 +68,6 @@ public: }; -// using StringView = View; - class StringView final : public View { public: diff --git a/src/main.cpp b/src/main.cpp index af32bfa..2b01ed1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,11 +1,14 @@ -#include #include #include #include #include +#include "ir/op.hpp" +#include "ir/optimize.hpp" #include "parser/lexer.hpp" #include "parser/ast.hpp" #include "ir/ir.hpp" +#include "prelude/string.hpp" +#include "prelude/linkedlist.hpp" // #include "codegen/fasm_stack.hpp" @@ -59,9 +62,37 @@ int main(int argc, char **argv) for (size_t i = 0; i < ops.size; ++i) { - printf("%s\n", ops.data[i]->Format(0).c_str()); + auto op = ops.data[i]; + if (op->GetType() == IR::OpType::FN) + { + auto fn = reinterpret_cast(op); + auto blockList = DoubleLinkedList::FromView(fn->body().ops()); + + auto allocaoptimz = IR::AllocOptimizer(); + printf("fn <%s>: status = %d\n", fn->name().c_str(), allocaoptimz.Apply(blockList)); + printf("fn %s:\n", fn->name().c_str()); + for (ListNode* cur = blockList.Begin(); cur != nullptr; cur = cur->next) + { + printf("%s\n", cur->value->Format(2).c_str()); + } + } } + // StringBuilder sb; + // for (size_t i = 0; i < ops.size; ++i) + // { + // sb.AppendFormat("%s\n", ops.data[i]->Format(0).c_str()); + // } + + // printf("%s\n", sb.c_str()); + + // auto output = File::Open("example.ll", File::Mode::WRITE); + // if (!output.Write(sb.view())) + // { + // fprintf(stderr, "ERROR: Failed to write IR to a file"); + // } + // std::println("OK"); + // StackFasmX86_64Generator gen; // gen.Generate(filename, ops);