feat: plan for register allocation + temp fix for 'E' not being printed
This commit is contained in:
20
TODO.md
Normal file
20
TODO.md
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
|
||||||
|
0. Define how many physical regs are available, which are caller-saved and callee-saved
|
||||||
|
1. Implement if-else and while statements
|
||||||
|
2. Find out what use[B] and def[B] are for each block
|
||||||
|
3. Define successors and predecessors of each block and make them accessible
|
||||||
|
4. Compute liveIn[B] and liveOut[B] for each block
|
||||||
|
|
||||||
|
Classic equations:
|
||||||
|
|
||||||
|
liveIn[B] = use[B] ∪ (liveOut[B] - def[B])
|
||||||
|
|
||||||
|
liveOut[B] = ⋃ liveIn[S] for S in succ[B]
|
||||||
|
5. Linear scan register allocation
|
||||||
|
|
||||||
|
|
||||||
|
- Instruction numbering + per-instruction liveness
|
||||||
|
- Live intervals
|
||||||
|
- Linear scan allocator with ~6 fake regs
|
||||||
|
- Spill to stack slots
|
||||||
|
- Add “CALL clobbers regs” rule
|
||||||
@@ -8,32 +8,36 @@ class StackFasmX86_64Generator : public CodeGenerator
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
~StackFasmX86_64Generator() override = default;
|
~StackFasmX86_64Generator() override = default;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
StringView GetTempAddr(IR::Value reg)
|
StringView GetTempAddr(IR::Value reg)
|
||||||
{
|
{
|
||||||
return std::move((StringBuilder() << reg.Format()).view());
|
return std::move((StringBuilder() << reg.Format()).view());
|
||||||
}
|
}
|
||||||
|
|
||||||
StringView GetSlotAddr(const IR::IRSlot& slot)
|
StringView GetSlotAddr(const IR::IRSlot &slot)
|
||||||
{
|
{
|
||||||
switch (slot.GetType())
|
switch (slot.GetType())
|
||||||
{
|
{
|
||||||
case IR::IRSlot::Type::REGISTRY:
|
case IR::IRSlot::Type::REGISTRY:
|
||||||
{
|
{
|
||||||
StringBuilder sb;
|
StringBuilder sb;
|
||||||
sb.AppendFormat("r%d", slot.GetSlot() + 10); // for r10, r11, r12 etc.
|
sb.AppendFormat("r%d", slot.GetSlot() + 12); // for r10, r11, r12 etc.
|
||||||
return sb.view();
|
return sb.view();
|
||||||
} break;
|
}
|
||||||
|
break;
|
||||||
case IR::IRSlot::Type::STACK:
|
case IR::IRSlot::Type::STACK:
|
||||||
{
|
{
|
||||||
StringBuilder sb;
|
StringBuilder sb;
|
||||||
sb.AppendFormat("[rbp-%d]", slot.GetSlot()); // for r10, r11, r12 etc.
|
sb.AppendFormat("[rbp-%d]", slot.GetSlot()); // for r10, r11, r12 etc.
|
||||||
return sb.view();
|
return sb.view();
|
||||||
} break;
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
assert(0 && "TODO: either unreachable or handle properly");
|
assert(0 && "TODO: either unreachable or handle properly");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void GenerateOp(const IR::Op *op)
|
void GenerateOp(const IR::Op *op)
|
||||||
{
|
{
|
||||||
@@ -59,7 +63,7 @@ private:
|
|||||||
appendf("mov rbp, rsp\n");
|
appendf("mov rbp, rsp\n");
|
||||||
|
|
||||||
StringBuilder fnOutput;
|
StringBuilder fnOutput;
|
||||||
StringBuilder* backup = m_output;
|
StringBuilder *backup = m_output;
|
||||||
m_output = &fnOutput;
|
m_output = &fnOutput;
|
||||||
|
|
||||||
if (fn->params().size > 0)
|
if (fn->params().size > 0)
|
||||||
@@ -68,7 +72,7 @@ private:
|
|||||||
auto param_slot = m_allocator->Allocate(fn->params().data[0]);
|
auto param_slot = m_allocator->Allocate(fn->params().data[0]);
|
||||||
appendf("mov %s, rdi\n", GetSlotAddr(param_slot).c_str());
|
appendf("mov %s, rdi\n", GetSlotAddr(param_slot).c_str());
|
||||||
}
|
}
|
||||||
for(auto &fOp : fn->body().ops())
|
for (auto &fOp : fn->body().ops())
|
||||||
{
|
{
|
||||||
GenerateOp(fOp);
|
GenerateOp(fOp);
|
||||||
}
|
}
|
||||||
@@ -135,11 +139,13 @@ private:
|
|||||||
appendf("mov %s, rax\n", GetSlotAddr(result_slot).c_str());
|
appendf("mov %s, rax\n", GetSlotAddr(result_slot).c_str());
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default: appendf("; NOT HANDLED\n; %s\n", op->Format(0).c_str()); break;
|
default:
|
||||||
|
appendf("; NOT HANDLED\n; %s\n", op->Format(0).c_str());
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void appendf(const char * fmt, ...)
|
void appendf(const char *fmt, ...)
|
||||||
{
|
{
|
||||||
assert(m_output != nullptr && "nowhere to write");
|
assert(m_output != nullptr && "nowhere to write");
|
||||||
va_list args;
|
va_list args;
|
||||||
@@ -147,25 +153,29 @@ private:
|
|||||||
m_output->VAppendFormat(fmt, args);
|
m_output->VAppendFormat(fmt, args);
|
||||||
va_end(args);
|
va_end(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
StringView GetOutput() { return m_output->view(); }
|
StringView GetOutput() { return m_output->view(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
StringBuilder* m_output = nullptr;
|
StringBuilder *m_output = nullptr;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void Generate(const char* filename, View<IR::Op*> ops) override
|
void Generate(const char *filename, View<IR::Op *> ops) override
|
||||||
{
|
{
|
||||||
m_output = new StringBuilder();
|
m_output = new StringBuilder();
|
||||||
appendf("; fasm x86_64 linux generated assembly using pl\n");
|
appendf("; fasm x86_64 linux generated assembly using pl\n");
|
||||||
appendf("format ELF64\n");
|
appendf("format ELF64\n");
|
||||||
appendf("section '.text' executable\n");
|
appendf("section '.text' executable\n");
|
||||||
|
|
||||||
for (auto& op : ops)
|
for (auto &op : ops)
|
||||||
{
|
{
|
||||||
GenerateOp(op);
|
GenerateOp(op);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// StackAllocator* m_stack = nullptr;
|
// StackAllocator* m_stack = nullptr;
|
||||||
// TODO: handle sub-blocks
|
// TODO: handle sub-blocks
|
||||||
SlotAllocator* m_allocator = nullptr;
|
SlotAllocator *m_allocator = nullptr;
|
||||||
};
|
};
|
||||||
|
|||||||
36
src/main.cpp
36
src/main.cpp
@@ -1,5 +1,7 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
#include <print>
|
#include <print>
|
||||||
#include "parser/lexer.hpp"
|
#include "parser/lexer.hpp"
|
||||||
#include "parser/ast.hpp"
|
#include "parser/ast.hpp"
|
||||||
@@ -7,9 +9,10 @@
|
|||||||
|
|
||||||
#include "codegen/fasm_stack.hpp"
|
#include "codegen/fasm_stack.hpp"
|
||||||
|
|
||||||
void dump_tokens(const char* filename, Lexer* lexer)
|
void dump_tokens(const char *filename, Lexer *lexer)
|
||||||
{
|
{
|
||||||
while (lexer->NextToken()) {
|
while (lexer->NextToken())
|
||||||
|
{
|
||||||
std::print("{}:{}:{}: ", filename, lexer->token().line_number, lexer->token().offset_start);
|
std::print("{}:{}:{}: ", filename, lexer->token().line_number, lexer->token().offset_start);
|
||||||
if (lexer->token().token == TokenType::Id)
|
if (lexer->token().token == TokenType::Id)
|
||||||
std::println("id = {}", lexer->token().string);
|
std::println("id = {}", lexer->token().string);
|
||||||
@@ -20,18 +23,22 @@ void dump_tokens(const char* filename, Lexer* lexer)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
char* filename;
|
char *filename;
|
||||||
if (argc > 1) {
|
if (argc > 1)
|
||||||
|
{
|
||||||
filename = (++argv)[0];
|
filename = (++argv)[0];
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
fprintf(stderr, "ERROR: Input file is required.\n");
|
fprintf(stderr, "ERROR: Input file is required.\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ifstream f(filename);
|
std::ifstream f(filename);
|
||||||
if (!f.is_open()) {
|
if (!f.is_open())
|
||||||
|
{
|
||||||
fprintf(stderr, "ERROR: Failed to open input file: %s\n", filename);
|
fprintf(stderr, "ERROR: Failed to open input file: %s\n", filename);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -55,11 +62,20 @@ int main(int argc, char** argv)
|
|||||||
printf("%s\n", ops.data[i]->Format(0).c_str());
|
printf("%s\n", ops.data[i]->Format(0).c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
// StackFasmX86_64Generator gen;
|
StackFasmX86_64Generator gen;
|
||||||
|
|
||||||
// gen.Generate(filename, ops);
|
gen.Generate(filename, ops);
|
||||||
|
|
||||||
// printf("%s\n", gen.GetOutput().c_str());
|
StringView output = gen.GetOutput();
|
||||||
|
|
||||||
|
FILE *file = fopen("out.asm", "w");
|
||||||
|
|
||||||
|
fwrite(output.c_str(), output.size - 1, sizeof(char), file);
|
||||||
|
|
||||||
|
fclose(file);
|
||||||
|
|
||||||
|
system("fasm out.asm");
|
||||||
|
system("gcc -o out out.o");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user