Commit f63ff4e9 authored by zygzagZ's avatar zygzagZ

Refactor Compiler - oddzielony generator kodu czwórkowego

parent 5b9b463e
CC=g++ CC=g++
CCFLAGS=-g -W -Wall -O0 -std=c++2a -Wno-unused-parameter CCFLAGS=-g -W -Wall -O0 -std=c++2a -Wno-unused-parameter
OBJS=Absyn.o Lexer.o Parser.o Printer.o TypeCheck.o Info.o Skeleton.o ParseError.o Compiler.o Quadruple.o BasicBlock.o OBJS=Absyn.o Lexer.o Parser.o Printer.o TypeCheck.o Info.o Skeleton.o ParseError.o Compiler.o Quadruple.o BasicBlock.o QuadrupleGenerator.o
.PHONY : clean distclean .PHONY : clean distclean
...@@ -32,7 +32,7 @@ Skeleton.o : src/Skeleton.cpp src/Skeleton.h src/Absyn.h ...@@ -32,7 +32,7 @@ Skeleton.o : src/Skeleton.cpp src/Skeleton.h src/Absyn.h
TypeCheck.o : src/TypeCheck.cpp src/TypeCheck.h src/Info.h src/InfoList.h src/Absyn.h src/ParseError.h TypeCheck.o : src/TypeCheck.cpp src/TypeCheck.h src/Info.h src/InfoList.h src/Absyn.h src/ParseError.h
${CC} ${CCFLAGS} -c src/TypeCheck.cpp ${CC} ${CCFLAGS} -c src/TypeCheck.cpp
Compiler.o : src/Compiler.cpp src/Compiler.h src/TypeCheck.h src/Info.h src/InfoList.h src/Absyn.h src/ParseError.h src/codeGen/Quadruple.h src/codeGen/Variable.h Compiler.o : src/Compiler.cpp src/Compiler.h src/TypeCheck.h src/Info.h src/InfoList.h src/Absyn.h src/ParseError.h src/codeGen/Quadruple.h src/codeGen/Variable.h src/codeGen/QuadrupleGenerator.h
${CC} ${CCFLAGS} -c src/Compiler.cpp ${CC} ${CCFLAGS} -c src/Compiler.cpp
Info.o : src/Info.cpp src/Info.h src/InfoList.h src/Absyn.h Info.o : src/Info.cpp src/Info.h src/InfoList.h src/Absyn.h
...@@ -50,7 +50,10 @@ Variable.o : src/codeGen/Quadruple.h src/codeGen/Variable.cpp src/Info.h src/Inf ...@@ -50,7 +50,10 @@ Variable.o : src/codeGen/Quadruple.h src/codeGen/Variable.cpp src/Info.h src/Inf
BasicBlock.o : src/codeGen/Quadruple.h src/codeGen/BasicBlock.h src/codeGen/BasicBlock.cpp src/codeGen/Variable.h src/Info.h src/InfoList.h src/Absyn.h BasicBlock.o : src/codeGen/Quadruple.h src/codeGen/BasicBlock.h src/codeGen/BasicBlock.cpp src/codeGen/Variable.h src/Info.h src/InfoList.h src/Absyn.h
${CC} ${CCFLAGS} -c src/codeGen/BasicBlock.cpp ${CC} ${CCFLAGS} -c src/codeGen/BasicBlock.cpp
Latte.o : src/Latte.cpp src/Parser.h src/Printer.h src/Absyn.h src/ParseError.h src/TypeCheck.h src/Compiler.h src/Info.h src/InfoList.h QuadrupleGenerator.o : src/codeGen/QuadrupleGenerator.cpp src/codeGen/Quadruple.h src/codeGen/BasicBlock.h src/Info.h src/InfoList.h src/Absyn.h src/codeGen/Variable.h src/Compiler.h src/codeGen/QuadrupleGenerator.h
${CC} ${CCFLAGS} -c src/codeGen/QuadrupleGenerator.cpp
Latte.o : src/Latte.cpp src/Parser.h src/Printer.h src/Absyn.h src/ParseError.h src/TypeCheck.h src/Compiler.h src/Info.h src/InfoList.h src/codeGen/QuadrupleGenerator.h
${CC} ${CCFLAGS} -c src/Latte.cpp ${CC} ${CCFLAGS} -c src/Latte.cpp
lib/runtime.o : lib/runtime.c Makefile lib/runtime.o : lib/runtime.c Makefile
......
...@@ -21,7 +21,7 @@ void Compiler::externalCommand(std::filesystem::path lat) { ...@@ -21,7 +21,7 @@ void Compiler::externalCommand(std::filesystem::path lat) {
system(cmd.data()); system(cmd.data());
} }
std::string Compiler::compile(Visitable *v) { string Compiler::compile(Visitable *v) {
buf << buf <<
".globl _start\n" ".globl _start\n"
".globl exit\n" ".globl exit\n"
...@@ -32,19 +32,19 @@ std::string Compiler::compile(Visitable *v) { ...@@ -32,19 +32,19 @@ std::string Compiler::compile(Visitable *v) {
" pushl %eax\n" " pushl %eax\n"
" call exit\n"; " call exit\n";
for (auto c : scope->classes) { for (const auto& c : scope->classes) {
c->calculateSize(); c->calculateSize();
} }
for (auto f : scope->functions) { for (const auto& f : scope->functions) {
compileFunction(f); compileFunction(f);
} }
return buf.str(); return buf.str();
} }
void Compiler::compileFunction(FunctionInfoPtr f) { void Compiler::compileFunction(const FunctionInfoPtr& f) {
std::string name = mangleFunctionName(f); string name = mangleFunctionName(f);
buf << ".globl \"" << name << "\"\n"; buf << ".globl \"" << name << "\"\n";
if (!f->block) return; if (!f->block) return;
...@@ -53,533 +53,12 @@ void Compiler::compileFunction(FunctionInfoPtr f) { ...@@ -53,533 +53,12 @@ void Compiler::compileFunction(FunctionInfoPtr f) {
// c++ thiscall ecx, stack right-to-left (push c, push b, push a) // c++ thiscall ecx, stack right-to-left (push c, push b, push a)
buf << "\"" << name << "\":\n"; buf << "\"" << name << "\":\n";
scope->currentFunction = f;
block = make_shared<BasicBlock>();
f->block->accept(this);
int id = 1; auto blocks = quadGen.compileFunction(f);
for (auto v : vars) {
if (v->constExpr && !v->info) {
v->name = std::to_string(v->val);
continue;
}
v->name = "@" + std::to_string(id);
if (v->info) {
v->name += "." + v->info->name + ":" + to_string(v->info->lineLocation);
}
id++;
}
flushBasicBlock();
block = nullptr;
for (auto b : blocks) {
b->finishQuads();
}
int label = 1; for (const auto& b : blocks) {
for (auto b : blocks) { for (const auto& q : b->quads) {
for (auto q : b->quads) {
if (auto l = dynamic_pointer_cast<QLabel>(q)) {
if (!l->label.empty())
l->label = to_string(label++) + "_" + l->label;
else
l->label = to_string(label++);
}
}
}
for (auto b : blocks) {
for (auto q : b->quads) {
buf << q->toString() << endl; buf << q->toString() << endl;
} }
} }
for (auto v : vars) {
if (v->info)
v->info->loc = nullptr;
}
vars.clear();
blocks.clear();
scope->currentFunction = nullptr;
}
/*
- kiedy przechodzę między blokami:
- pierwsze wejście w while/fora: ustawienie używanych wartości w pętli w zależności od ostatnich wartości przed pętlą
- dump przed wejściem w stmt fora, dump po sparsowaniu fora
- różnice ustawić w bloku przed i tak samo za nim
- if: przed ifem dump, za ifem dorobić else i ustawić diff
- ifelse: przed ifem dump, w środku dump, po elsie dump
- wszystkie poza zmienionymi w bloku ustawiamy na koniec bloku
wskazówki do implementacji:
- w momencie przypisywania do zmiennej lokalnej chcemy powiadomić najświeższy detektor zmian lokalnych
- po zakończeniu bloku detektor zmian odpowiednio poprzypisuje w pożądanych miejcach odpowiednie definicje
- po zakończeniu bloku detektor powiadomi kolejny z rzędu detektor o najnowszych instancjach zmienionych zmiennych
- detektor powinien działać ponad blokami prostymi
funkcje detektora:
- dodanie zmiennej VarInfoPtr do zbioru, przyporządkowanie jej najnowszej definicji (i zapamiętanie oryginalnej)
- umiejętność porównania dwóch zbiorów poprzez iterację
- każdy element który występuje tylko w jednym
- para jeśli el. występuje w obu zbiorach
implementacja detektora:
- map<VarInfoPtr, pair<orig, new>>
dalej: << i >> dla BB
porobić graf BB
złapać blocki i podumpować envy dookoła flow
dopisać sumę dumpów
*/
BasicBlockPtr Compiler::flushBasicBlock() {
auto ret = block;
if (block) {
blocks.emplace_back(block);
flushLastQuad();
}
block = make_shared<BasicBlock>();
return ret;
}
VariableLayout Compiler::captureEnv() {
VariableLayout ret;
BindingPtr b = scope->currentBinding;
while (b) {
for (const auto& info : b->variables) {
if (info->isInstanceVariable()) continue;
if (info->loc) {
ret.add(info);
}
}
b = b->getParent();
}
return ret;
}
/// expressions
void Compiler::visitEVar(EVar *p) {
lastVar = alloc(p->pident_->var.lock());
}
void Compiler::visitELitInt(ELitInt *p) {
lastVar = alloc(p->integer_);
}
void Compiler::visitELitTrue(ELitTrue *p) {
lastVar = alloc(1);
}
void Compiler::visitELitFalse(ELitFalse *p) {
lastVar = alloc(0);
}
void Compiler::visitEString(EString *p) {
// TODO: zrobic liste stringow per app
lastVar = alloc();
}
void Compiler::visitNullCast(NullCast *p) {
lastVar = alloc(0);
}
// unary ops
void Compiler::visitNeg(Neg *p) {
auto var = evalExpr(p->expr_);
lastVar = alloc();
addQuad<QAssign>(lastVar, Op::Neg, var);
}
void Compiler::visitNot(Not *p) {
auto var = evalExpr(p->expr_);
lastVar = alloc();
addQuad<QAssign>(lastVar, Op::Not, var);
}
// binary ops
void Compiler::visitEMul(EMul *p) {
auto l = evalExpr(p->expr_1);
auto r = evalExpr(p->expr_2);
lastVar = alloc();
p->mulop_->accept(this);
addQuad<QAssign>(lastVar, l, op, r);
}
void Compiler::visitEAdd(EAdd *p) {
auto l = evalExpr(p->expr_1);
auto r = evalExpr(p->expr_2);
lastVar = alloc();
p->addop_->accept(this);
addQuad<QAssign>(lastVar, l, op, r);
}
void Compiler::visitERel(ERel *p) {
auto l = evalExpr(p->expr_1);
auto r = evalExpr(p->expr_2);
lastVar = alloc();
p->relop_->accept(this);
addQuad<QAssign>(lastVar, l, op, r);
}
// lazy bools
void Compiler::visitEAnd(EAnd *p) {
auto l = evalExpr(p->expr_1);
auto labelLeft = make_shared<QLabel>("use_left");
auto labelAfter = make_shared<QLabel>("end_and");
addQuad<QJumpCond>(labelLeft, Op::Not, l);
auto r = evalExpr(p->expr_2);
lastVar = alloc();
addQuad<QAssign>(lastVar, Op::Copy, r);
addQuad<QJump>(labelAfter);
addQuad(labelLeft);
addQuad<QAssign>(lastVar, Op::Copy, alloc(0));
addQuad(labelAfter);
}
void Compiler::visitEOr(EOr *p) {
auto l = evalExpr(p->expr_1);
auto labelLeft = make_shared<QLabel>("use_left");
auto labelAfter = make_shared<QLabel>("end_or");
addQuad<QJumpCond>(labelLeft, Op::Copy, l);
auto r = evalExpr(p->expr_2);
lastVar = alloc();
addQuad<QAssign>(lastVar, Op::Copy, r);
addQuad<QJump>(labelAfter);
addQuad(labelLeft);
addQuad<QAssign>(lastVar, Op::Copy, alloc(1));
addQuad(labelAfter);
}
// complex extensions
void Compiler::visitEIndexAcc(EIndexAcc *p) {
auto lhs = evalExpr(p->expr_1);
auto index = evalExpr(p->expr_2);
lastVar = alloc();
auto type = dynamic_pointer_cast<Array>(lhs->type);
if (!type) {
throw ParseError("compiler evalExpr Variable has no Array type", p->expr_1);
}
if (!type->type_) {
throw ParseError("compiler evalExpr Variable is Array of no type", p->expr_1);
}
if (type->type_->size() > 4) {
// calculate quantifier, we multiply index because size is larger than 4
auto q = alloc(type->type_->size() / 4);
// calculate var = index * quantifier
addQuad<QAssign>(lastVar, index, Op::Mul, q);
// allocate new lastVar, index is now calculated multiplied index
index = alloc();
swap(index, lastVar);
}
auto quad = make_shared<QAccess>(lastVar, lhs, index, 4, 0);
addQuad(quad);
}
void Compiler::visitEClsMmbr(EClsMmbr *p) {
auto l = evalExpr(p->expr_);
auto var = p->pident_->var.lock();
assert(var);
// it cannot be function, as function might only be used in EApp and are handled directly there
assert(!var->toFunction());
// Array length calculation works because of arrayLengthVar that has offset 0
size_t offset = var->offset;
if (dynamic_cast<EIndexAcc*>(p->expr_)) {
// opt if EIndexAcc is used inside visitEClsMmbr
auto access = dynamic_pointer_cast<QAccess>(block->quads.back());
access->access.offset += offset;
l->info = var;
lastVar = l;
} else {
lastVar = alloc(var);
auto quad = make_shared<QAccess>(lastVar, l, alloc(0), 0, offset);
addQuad(quad);
}
}
void Compiler::visitEApp(EApp *p) {
auto funType = static_pointer_cast<Fun>(p->expr_->type);
FunctionInfoPtr info;
VariablePtr self;
if (auto mem = dynamic_cast<EClsMmbr*>(p->expr_)) {
// call tab[i].fun()
// call obj.fun()
self = evalExpr(mem->expr_);
info = static_pointer_cast<FunctionInfo>(mem->pident_->var.lock());
} else if (auto ident = dynamic_cast<EVar*>(p->expr_)) {
auto klass = scope->currentFunction->klass.lock();
info = static_pointer_cast<FunctionInfo>(ident->pident_->var.lock());
auto otherClass = info->klass.lock();
if (otherClass) {
// call fun() = self.fun()
assert(otherClass == klass);
auto binding = scope->currentFunction->binding.lock();
assert(binding);
auto selfInfo = binding->variables.local("self");
assert(selfInfo);
self = alloc(selfInfo);
} else {
// call main()
}
} else {
throw ParseError("Unimplemented EApp instantiation (neither EClsMmbr nor EVar", p);
}
int i = 1;
for (auto param : *p->listexpr_) {
auto var = evalExpr(param);
addQuad<QParam>(var, i++);
}
if (self) {
addQuad<QParam>(self, 0);
}
lastVar = alloc();
addQuad<QCall>(lastVar, info, i, self);
}
void Compiler::visitENewArray(ENewArray *p) {
// allocate enough memory with calloc, if it is class then init each entry...
auto type = p->type_;
size_t size = type->size();
std::string virtSymbol;
if (auto klass = dynamic_cast<ClassT*>(type)) {
auto info = klass->pident_->var.lock()->toClass();
size = info->size;
virtSymbol = getVirtName(info);
}
auto count = evalExpr(p->expr_);
lastVar = alloc();
addQuad<QAlloc>(lastVar, size, virtSymbol, count);
}
void Compiler::visitENewClass(ENewClass *p) {
// allocate enough memory with calloc, setup virt table pointer.
auto info = p->pident_->var.lock()->toClass();
size_t size = info->size;
std::string virtSymbol = getVirtName(info);
lastVar = alloc();
addQuad<QAlloc>(lastVar, size, virtSymbol);
}
/// statements
void Compiler::visitInit(Init *p) {
auto info = p->pident_->var.lock();
assert(info);
auto var = evalExpr(p->expr_);
addQuad<QAssign>(alloc(info), Op::Copy, var);
}
void Compiler::assign(Expr* lval, VariablePtr val) {
auto dest = evalLVal(lval);
if (dest->info && dest->info->isInstanceVariable()) {
// instance variable, need to write it to memory
auto quad = dynamic_pointer_cast<QAccess>(lastQuad);
assert(quad);
auto loc = quad->access;
lastQuad = nullptr;
addQuad<QWrite>(loc, val);
} else {
// local variable - only assign
addQuad<QAssign>(dest, Op::Copy, val);
}
}
void Compiler::visitAss(Ass *p) {
auto val = evalExpr(p->expr_2);
assign(p->expr_1, val);
}
void Compiler::visitIncr(Incr *p) {
auto lhs = evalExpr(p->expr_);
auto tmp = alloc();
addQuad<QAssign>(tmp, lhs, Op::Plus, alloc(1));
assign(p->expr_, tmp);
}
void Compiler::visitDecr(Decr *p) {
auto lhs = evalExpr(p->expr_);
auto tmp = alloc();
addQuad<QAssign>(tmp, lhs, Op::Minus, alloc(1));
assign(p->expr_, tmp);
}
// flow control
void Compiler::visitRet(Ret *p) {
auto var = evalExpr(p->expr_);
addQuad<QReturn>(var);
flushBasicBlock();
}
void Compiler::visitVRet(VRet *p) {
addQuad<QReturn>(nullptr);
flushBasicBlock();
}
void Compiler::visitCond(Cond *p) {
compileCond(p->expr_, p->stmt_, nullptr);
}
void Compiler::visitCondElse(CondElse *p) {
compileCond(p->expr_, p->stmt_1, p->stmt_2);
}
void Compiler::compileCond(Expr *expr_, Stmt *stmt_1, Stmt *stmt_2) {
auto elseBranch = make_shared<QLabel>("else");
auto after = make_shared<QLabel>("end_else");
auto var = evalExpr(expr_);
addQuad<QJumpCond>(elseBranch, Op::Not, var);
auto beforeBlock = flushBasicBlock(); // possible jump -> else
auto env1 = captureEnv(), env2 = env1;
stmt_1->accept(this);
addQuad<QJump>(after);
auto stmt1Block = flushBasicBlock(); // jump -> after
env1.capture();
env1.revert();
addQuad(elseBranch);
if (stmt_2) {
stmt_2->accept(this);
}
auto stmt2Block = flushBasicBlock(); // jump <- cond
env2.capture();
addQuad(after);
beforeBlock->append(stmt1Block);
beforeBlock->append(stmt2Block);
stmt1Block->append(block);
stmt2Block->append(block);
merge2Envs(&env1, stmt1Block, &env2, stmt2Block);
}
void Compiler::merge2Envs(VariableLayout *env1, BasicBlockPtr b1, VariableLayout *env2, BasicBlockPtr b2) {
for (int i = 0; i < 2; i++) {
for (const auto& p1 : env1->changes) {
if (p1.second.first == p1.second.second) continue;
auto info = p1.first;
auto it = env2->changes.find(info);
if (it != env2->changes.end()) {
auto p2 = *it;
// variables have been already merged
if (p1.second.first == p2.second.second) continue;
info->loc = nullptr;
auto merged = alloc(info);
// assign both versions to merged variable location
b1->addJumpInitQuad(make_shared<QAssign>(merged, Op::Copy, p1.second.second));
b2->addJumpInitQuad(make_shared<QAssign>(merged, Op::Copy, p2.second.second));
} else {
// copy changes to another block that did not touch var
b2->addJumpInitQuad(make_shared<QAssign>(p1.second.second, Op::Copy, p1.second.first));
}
}
swap(env1, env2);
swap(b1, b2);
}
}
void Compiler::visitWhile(While *p) {
auto cond = make_shared<QLabel>("cond");
addQuad<QJump>(cond);
auto beforeBlock = flushBasicBlock(); // jump <- loop -> cond
auto loop = make_shared<QLabel>("loop");
addQuad(loop); // jump <- loop
// hook env
auto env1 = captureEnv();
// produce all variables hooks
for (const auto& change : env1.changes) {
auto info = change.first;
info->loc = nullptr;
alloc(info);
}
// save hooks for later
env1.capture();
// env2 starts with hooked variables
auto env2 = captureEnv();
p->stmt_->accept(this);
auto loopBlock = flushBasicBlock(); // jump <- cond
addQuad(cond);
// env2 contains changed hooks
env2.capture();
// restore env1 pre-hook variables
env1.revert();
for (auto p : env2.changes) {
auto info = p.first;
auto x1 = p.second.first;
auto x2 = p.second.second;
// save hooks if used
info->loc = x1;
// transition from pre-hook to hooked var [before -> cond]
beforeBlock->addJumpInitQuad(make_shared<QAssign>(x1, Op::Copy, env1.changes[info].first));
// transition from loop var to hooked var [loop -> cond]
loopBlock->addJumpInitQuad(make_shared<QAssign>(x1, Op::Copy, x2));
}
// expr uses pre-hook variables iff unused in loop
auto var = evalExpr(p->expr_);
addQuad<QJumpCond>(loop, Op::Copy, var);
auto condBlock = flushBasicBlock(); // jump -> loop
// next block is ready to use updated variables
beforeBlock->append(condBlock);
condBlock->append(loopBlock);
condBlock->append(block);
} }
void Compiler::visitSExp(SExp *p) {
evalExpr(p->expr_);
}
void Compiler::visitForEach(ForEach *p) {
auto tab = evalExpr(p->expr_);
throw ParseError("Unimplemented instruction!", p);
// TODO: implement
}
void Compiler::visitBlk(Blk *blk) {
scope->currentBinding = blk->getBinding();
Skeleton::visitBlk(blk);
scope->currentBinding = scope->currentBinding->getParent();
}
...@@ -11,157 +11,35 @@ ...@@ -11,157 +11,35 @@
#include "Info.h" #include "Info.h"
#include "codeGen/Quadruple.h" #include "codeGen/Quadruple.h"
#include "codeGen/BasicBlock.h" #include "codeGen/BasicBlock.h"
#include "codeGen/QuadrupleGenerator.h"
using Op = Quadruple::Op; using Op = Quadruple::Op;
class Compiler : public Skeleton class Compiler {
{
public: public:
Compiler(const std::string& f, shared_ptr<Scope> s) : file(f), buf(), scope(std::move(s)) {}; Compiler(const std::string& f, shared_ptr<Scope> s) : file(f), buf(), scope(s), quadGen(std::move(s)) {};
std::string compile(Visitable *v);
static const std::string extension; static const std::string extension;
static void externalCommand(std::filesystem::path file); static void externalCommand(std::filesystem::path file);
string compile(Visitable *v);
static std::string getVirtName(const ClassInfoPtr& c) {
return c->name + ":virt_table";
}
private: private:
std::filesystem::path file; std::filesystem::path file;
std::stringstream buf; std::stringstream buf;
shared_ptr<Scope> scope; shared_ptr<Scope> scope;
vector<VariablePtr> vars; QuadrupleGenerator quadGen;
vector<BasicBlockPtr> blocks;
Quadruple::Op op;
BasicBlockPtr block;
VariablePtr lastVar;
int lineno;
void compileFunction(FunctionInfoPtr f);
VariablePtr evalLVal(Visitable *expr) {
auto info = evalExpr(expr)->info;
if (!info) {
throw ParseError("LValue expected", expr);
}
info->loc = nullptr;
return alloc(info);
}
VariablePtr evalExpr(Visitable *expr) { void compileFunction(const FunctionInfoPtr& f);
if (!expr) throw runtime_error("No expr to eval");
lineno = expr->lineno;
lastVar = nullptr;
try {
expr->accept(this);
} catch (const ParseError &err) {
throw ParseError(err, expr);
}
if (!lastVar) throw ParseError("No variable found", expr);
auto ret = lastVar;
lastVar = nullptr;
return ret;
};
static std::string mangleFunctionName(const FunctionInfoPtr& f) { static std::string mangleFunctionName(const FunctionInfoPtr& f) {
if (auto c = f->klass.lock()) { if (auto c = f->klass.lock()) {
return c->name + "::" + f->name; return c->name + "::" + f->name;
} }
return f->name; return f->name;
};
static std::string getVirtName(const ClassInfoPtr& c) {
return c->name + ":virt_table";
};
VariablePtr alloc(const VarInfoPtr& info) {
if (info->loc) {
return info->loc;
}
auto v = make_shared<Variable>(info);
vars.emplace_back(v);
if (!info->isInstanceVariable()) {
info->loc = v;
}
return v;
}
template<typename... Args> VariablePtr alloc(Args... args) {
auto v = make_shared<Variable>(args...);
vars.emplace_back(v);
return v;
}
template<typename T, typename... Args> QuadruplePtr addQuad(Args... args) {
return addQuad(make_shared<T>(args...));
}
QuadruplePtr addQuad(QuadruplePtr quad) {
quad->lineno = lineno;
flushLastQuad();
lastQuad = quad;
return quad;
} }
QuadruplePtr lastQuad;
void flushLastQuad() {
if (lastQuad) {
block->quads.emplace_back(lastQuad);
lastQuad = nullptr;
}
}
BasicBlockPtr flushBasicBlock();
void assign(Expr* lval, VariablePtr val);
VariableLayout captureEnv();
void merge2Envs(VariableLayout *env1, BasicBlockPtr b1, VariableLayout *env2, BasicBlockPtr b2);
void compileCond(Expr *cond, Stmt *stmt1, Stmt *stmt2);
void visitEVar(EVar *p) override;
void visitEIndexAcc(EIndexAcc *p) override;
void visitEClsMmbr(EClsMmbr *p) override;
void visitEApp(EApp *p) override;
void visitELitInt(ELitInt *p) override;
void visitELitTrue(ELitTrue *p) override;
void visitELitFalse(ELitFalse *p) override;
void visitEString(EString *p) override;
void visitENewArray(ENewArray *p) override;
void visitENewClass(ENewClass *p) override;
void visitNullCast(NullCast *p) override;
void visitNeg(Neg *p) override;
void visitNot(Not *p) override;
void visitEMul(EMul *p) override;
void visitEAdd(EAdd *p) override;
void visitERel(ERel *p) override;
void visitEAnd(EAnd *p) override;
void visitEOr(EOr *p) override;
void visitBlk(Blk *p) override;
void visitInit(Init *p) override;
void visitAss(Ass *p) override;
void visitIncr(Incr *p) override;
void visitDecr(Decr *p) override;
void visitRet(Ret *p) override;
void visitVRet(VRet *p) override;
void visitCond(Cond *p) override;
void visitCondElse(CondElse *p) override;
void visitWhile(While *p) override;
void visitSExp(SExp *p) override;
void visitForEach(ForEach *p) override;
void visitPlus(Plus *p) override { op = Op::Plus; };
void visitMinus(Minus *p) override { op = Op::Minus; };
void visitTimes(Times *p) override { op = Op::Mul; };
void visitDiv(Div *p) override { op = Op::Div; };
void visitMod(Mod *p) override { op = Op::Mod; };
void visitLTH(LTH *p) override { op = Op::LT; };
void visitLE(LE *p) override { op = Op::LE; };
void visitGTH(GTH *p) override { op = Op::GT; };
void visitGE(GE *p) override { op = Op::GE; };
void visitEQU(EQU *p) override { op = Op::EQ; };
void visitNE(NE *p) override { op = Op::NEQ; };
}; };
#endif #endif
//
// Created by zygzagz on 05.01.2021.
//
#include <cassert>
#include <iostream>
#include <filesystem>
#include "Quadruple.h"
#include "../Compiler.h"
#include "QuadrupleGenerator.h"
vector<BasicBlockPtr> QuadrupleGenerator::compileFunction(FunctionInfoPtr f) {
if (!f->block) return {};
scope->currentFunction = f;
block = make_shared<BasicBlock>();
f->block->accept(this);
int id = 1;
for (auto v : vars) {
if (v->constExpr && !v->info) {
v->name = to_string(v->val);
continue;
}
v->name = "@" + to_string(id);
if (v->info) {
v->name += "." + v->info->name + ":" + to_string(v->info->lineLocation);
}
id++;
}
flushBasicBlock();
block = nullptr;
for (auto b : blocks) {
b->finishQuads();
}
int label = 1;
for (auto b : blocks) {
for (auto q : b->quads) {
if (auto l = dynamic_pointer_cast<QLabel>(q)) {
if (!l->label.empty())
l->label = to_string(label++) + "_" + l->label;
else
l->label = to_string(label++);
}
}
}
for (auto v : vars) {
if (v->info)
v->info->loc = nullptr;
}
vars.clear();
vector<BasicBlockPtr> ret;
blocks.swap(ret);
scope->currentFunction = nullptr;
return ret;
}
/*
- kiedy przechodzę między blokami:
- pierwsze wejście w while/fora: ustawienie używanych wartości w pętli w zależności od ostatnich wartości przed pętlą
- dump przed wejściem w stmt fora, dump po sparsowaniu fora
- różnice ustawić w bloku przed i tak samo za nim
- if: przed ifem dump, za ifem dorobić else i ustawić diff
- ifelse: przed ifem dump, w środku dump, po elsie dump
- wszystkie poza zmienionymi w bloku ustawiamy na koniec bloku
wskazówki do implementacji:
- w momencie przypisywania do zmiennej lokalnej chcemy powiadomić najświeższy detektor zmian lokalnych
- po zakończeniu bloku detektor zmian odpowiednio poprzypisuje w pożądanych miejcach odpowiednie definicje
- po zakończeniu bloku detektor powiadomi kolejny z rzędu detektor o najnowszych instancjach zmienionych zmiennych
- detektor powinien działać ponad blokami prostymi
funkcje detektora:
- dodanie zmiennej VarInfoPtr do zbioru, przyporządkowanie jej najnowszej definicji (i zapamiętanie oryginalnej)
- umiejętność porównania dwóch zbiorów poprzez iterację
- każdy element który występuje tylko w jednym
- para jeśli el. występuje w obu zbiorach
implementacja detektora:
- map<VarInfoPtr, pair<orig, new>>
dalej: << i >> dla BB
porobić graf BB
złapać blocki i podumpować envy dookoła flow
dopisać sumę dumpów
*/
BasicBlockPtr QuadrupleGenerator::flushBasicBlock() {
auto ret = block;
if (block) {
blocks.emplace_back(block);
flushLastQuad();
}
block = make_shared<BasicBlock>();
return ret;
}
VariableLayout QuadrupleGenerator::captureEnv() {
VariableLayout ret;
BindingPtr b = scope->currentBinding;
while (b) {
for (const auto& info : b->variables) {
if (info->isInstanceVariable()) continue;
if (info->loc) {
ret.add(info);
}
}
b = b->getParent();
}
return ret;
}
/// expressions
void QuadrupleGenerator::visitEVar(EVar *p) {
lastVar = alloc(p->pident_->var.lock());
}
void QuadrupleGenerator::visitELitInt(ELitInt *p) {
lastVar = alloc(p->integer_);
}
void QuadrupleGenerator::visitELitTrue(ELitTrue *p) {
lastVar = alloc(1);
}
void QuadrupleGenerator::visitELitFalse(ELitFalse *p) {
lastVar = alloc(0);
}
void QuadrupleGenerator::visitEString(EString *p) {
// TODO: zrobic liste stringow per app
lastVar = alloc();
}
void QuadrupleGenerator::visitNullCast(NullCast *p) {
lastVar = alloc(0);
}
void QuadrupleGenerator::visitNeg(Neg *p) {
auto var = evalExpr(p->expr_);
lastVar = alloc();
addQuad<QAssign>(lastVar, Op::Neg, var);
}
void QuadrupleGenerator::visitNot(Not *p) {
auto var = evalExpr(p->expr_);
lastVar = alloc();
addQuad<QAssign>(lastVar, Op::Not, var);
}
void QuadrupleGenerator::visitEMul(EMul *p) {
auto l = evalExpr(p->expr_1);
auto r = evalExpr(p->expr_2);
lastVar = alloc();
p->mulop_->accept(this);
addQuad<QAssign>(lastVar, l, op, r);
}
void QuadrupleGenerator::visitEAdd(EAdd *p) {
auto l = evalExpr(p->expr_1);
auto r = evalExpr(p->expr_2);
lastVar = alloc();
p->addop_->accept(this);
addQuad<QAssign>(lastVar, l, op, r);
}
void QuadrupleGenerator::visitERel(ERel *p) {
auto l = evalExpr(p->expr_1);
auto r = evalExpr(p->expr_2);
lastVar = alloc();
p->relop_->accept(this);
addQuad<QAssign>(lastVar, l, op, r);
}
void QuadrupleGenerator::visitEAnd(EAnd *p) {
auto l = evalExpr(p->expr_1);
auto labelLeft = make_shared<QLabel>("use_left");
auto labelAfter = make_shared<QLabel>("end_and");
addQuad<QJumpCond>(labelLeft, Op::Not, l);
auto r = evalExpr(p->expr_2);
lastVar = alloc();
addQuad<QAssign>(lastVar, Op::Copy, r);
addQuad<QJump>(labelAfter);
addQuad(labelLeft);
addQuad<QAssign>(lastVar, Op::Copy, alloc(0));
addQuad(labelAfter);
}
void QuadrupleGenerator::visitEOr(EOr *p) {
auto l = evalExpr(p->expr_1);
auto labelLeft = make_shared<QLabel>("use_left");
auto labelAfter = make_shared<QLabel>("end_or");
addQuad<QJumpCond>(labelLeft, Op::Copy, l);
auto r = evalExpr(p->expr_2);
lastVar = alloc();
addQuad<QAssign>(lastVar, Op::Copy, r);
addQuad<QJump>(labelAfter);
addQuad(labelLeft);
addQuad<QAssign>(lastVar, Op::Copy, alloc(1));
addQuad(labelAfter);
}
/// complex extensions
void QuadrupleGenerator::visitEIndexAcc(EIndexAcc *p) {
auto lhs = evalExpr(p->expr_1);
auto index = evalExpr(p->expr_2);
lastVar = alloc();
auto type = dynamic_pointer_cast<Array>(lhs->type);
if (!type) {
throw ParseError("compiler evalExpr Variable has no Array type", p->expr_1);
}
if (!type->type_) {
throw ParseError("compiler evalExpr Variable is Array of no type", p->expr_1);
}
if (type->type_->size() > 4) {
// calculate quantifier, we multiply index because size is larger than 4
auto q = alloc(type->type_->size() / 4);
// calculate var = index * quantifier
addQuad<QAssign>(lastVar, index, Op::Mul, q);
// allocate new lastVar, index is now calculated multiplied index
index = alloc();
swap(index, lastVar);
}
auto quad = make_shared<QAccess>(lastVar, lhs, index, 4, 0);
addQuad(quad);
}
void QuadrupleGenerator::visitEClsMmbr(EClsMmbr *p) {
auto l = evalExpr(p->expr_);
auto var = p->pident_->var.lock();
assert(var);
// it cannot be function, as function might only be used in EApp and are handled directly there
assert(!var->toFunction());
// Array length calculation works because of arrayLengthVar that has offset 0
size_t offset = var->offset;
if (dynamic_cast<EIndexAcc*>(p->expr_)) {
// opt if EIndexAcc is used inside visitEClsMmbr
auto access = dynamic_pointer_cast<QAccess>(block->quads.back());
access->access.offset += offset;
l->info = var;
lastVar = l;
} else {
lastVar = alloc(var);
auto quad = make_shared<QAccess>(lastVar, l, alloc(0), 0, offset);
addQuad(quad);
}
}
void QuadrupleGenerator::visitEApp(EApp *p) {
auto funType = static_pointer_cast<Fun>(p->expr_->type);
FunctionInfoPtr info;
VariablePtr self;
if (auto mem = dynamic_cast<EClsMmbr*>(p->expr_)) {
// call tab[i].fun()
// call obj.fun()
self = evalExpr(mem->expr_);
info = static_pointer_cast<FunctionInfo>(mem->pident_->var.lock());
} else if (auto ident = dynamic_cast<EVar*>(p->expr_)) {
auto klass = scope->currentFunction->klass.lock();
info = static_pointer_cast<FunctionInfo>(ident->pident_->var.lock());
auto otherClass = info->klass.lock();
if (otherClass) {
// call fun() = self.fun()
assert(otherClass == klass);
auto binding = scope->currentFunction->binding.lock();
assert(binding);
auto selfInfo = binding->variables.local("self");
assert(selfInfo);
self = alloc(selfInfo);
} else {
// call main()
}
} else {
throw ParseError("Unimplemented EApp instantiation (neither EClsMmbr nor EVar", p);
}
int i = 1;
for (auto param : *p->listexpr_) {
auto var = evalExpr(param);
addQuad<QParam>(var, i++);
}
if (self) {
addQuad<QParam>(self, 0);
}
lastVar = alloc();
addQuad<QCall>(lastVar, info, i, self);
}
void QuadrupleGenerator::visitENewArray(ENewArray *p) {
// allocate enough memory with calloc, if it is class then init each entry...
auto type = p->type_;
size_t size = type->size();
string virtSymbol;
if (auto klass = dynamic_cast<ClassT*>(type)) {
auto info = klass->pident_->var.lock()->toClass();
size = info->size;
virtSymbol = Compiler::getVirtName(info);
}
auto count = evalExpr(p->expr_);
lastVar = alloc();
addQuad<QAlloc>(lastVar, size, virtSymbol, count);
}
void QuadrupleGenerator::visitENewClass(ENewClass *p) {
// allocate enough memory with calloc, setup virt table pointer.
auto info = p->pident_->var.lock()->toClass();
size_t size = info->size;
string virtSymbol = Compiler::getVirtName(info);
lastVar = alloc();
addQuad<QAlloc>(lastVar, size, virtSymbol);
}
/// statements
void QuadrupleGenerator::visitInit(Init *p) {
auto info = p->pident_->var.lock();
assert(info);
auto var = evalExpr(p->expr_);
addQuad<QAssign>(alloc(info), Op::Copy, var);
}
void QuadrupleGenerator::assign(Expr* lval, VariablePtr val) {
auto dest = evalLVal(lval);
if (dest->info && dest->info->isInstanceVariable()) {
// instance variable, need to write it to memory
auto quad = dynamic_pointer_cast<QAccess>(lastQuad);
assert(quad);
auto loc = quad->access;
lastQuad = nullptr;
addQuad<QWrite>(loc, val);
} else {
// local variable - only assign
addQuad<QAssign>(dest, Op::Copy, val);
}
}
void QuadrupleGenerator::visitAss(Ass *p) {
auto val = evalExpr(p->expr_2);
assign(p->expr_1, val);
}
void QuadrupleGenerator::visitIncr(Incr *p) {
auto lhs = evalExpr(p->expr_);
auto tmp = alloc();
addQuad<QAssign>(tmp, lhs, Op::Plus, alloc(1));
assign(p->expr_, tmp);
}
void QuadrupleGenerator::visitDecr(Decr *p) {
auto lhs = evalExpr(p->expr_);
auto tmp = alloc();
addQuad<QAssign>(tmp, lhs, Op::Minus, alloc(1));
assign(p->expr_, tmp);
}
void QuadrupleGenerator::visitBlk(Blk *blk) {
scope->currentBinding = blk->getBinding();
Skeleton::visitBlk(blk);
scope->currentBinding = scope->currentBinding->getParent();
}
/// flow control
void QuadrupleGenerator::visitRet(Ret *p) {
auto var = evalExpr(p->expr_);
addQuad<QReturn>(var);
flushBasicBlock();
}
void QuadrupleGenerator::visitVRet(VRet *p) {
addQuad<QReturn>(nullptr);
flushBasicBlock();
}
void QuadrupleGenerator::visitCond(Cond *p) {
compileCond(p->expr_, p->stmt_, nullptr);
}
void QuadrupleGenerator::visitCondElse(CondElse *p) {
compileCond(p->expr_, p->stmt_1, p->stmt_2);
}
void QuadrupleGenerator::compileCond(Expr *expr_, Stmt *stmt_1, Stmt *stmt_2) {
auto elseBranch = make_shared<QLabel>("else");
auto after = make_shared<QLabel>("end_else");
auto var = evalExpr(expr_);
addQuad<QJumpCond>(elseBranch, Op::Not, var);
auto beforeBlock = flushBasicBlock(); // possible jump -> else
auto env1 = captureEnv(), env2 = env1;
stmt_1->accept(this);
addQuad<QJump>(after);
auto stmt1Block = flushBasicBlock(); // jump -> after
env1.capture();
env1.revert();
addQuad(elseBranch);
if (stmt_2) {
stmt_2->accept(this);
}
auto stmt2Block = flushBasicBlock(); // jump <- cond
env2.capture();
addQuad(after);
beforeBlock->append(stmt1Block);
beforeBlock->append(stmt2Block);
stmt1Block->append(block);
stmt2Block->append(block);
merge2Envs(&env1, stmt1Block, &env2, stmt2Block);
}
void QuadrupleGenerator::merge2Envs(VariableLayout *env1, BasicBlockPtr b1, VariableLayout *env2, BasicBlockPtr b2) {
for (int i = 0; i < 2; i++) {
for (const auto& p1 : env1->changes) {
if (p1.second.first == p1.second.second) continue;
auto info = p1.first;
auto it = env2->changes.find(info);
if (it != env2->changes.end()) {
auto p2 = *it;
// variables have been already merged
if (p1.second.first == p2.second.second) continue;
info->loc = nullptr;
auto merged = alloc(info);
// assign both versions to merged variable location
b1->addJumpInitQuad(make_shared<QAssign>(merged, Op::Copy, p1.second.second));
b2->addJumpInitQuad(make_shared<QAssign>(merged, Op::Copy, p2.second.second));
} else {
// copy changes to another block that did not touch var
b2->addJumpInitQuad(make_shared<QAssign>(p1.second.second, Op::Copy, p1.second.first));
}
}
swap(env1, env2);
swap(b1, b2);
}
}
void QuadrupleGenerator::visitWhile(While *expr) {
auto cond = make_shared<QLabel>("cond");
addQuad<QJump>(cond);
auto beforeBlock = flushBasicBlock(); // jump <- loop -> cond
auto loop = make_shared<QLabel>("loop");
addQuad(loop); // jump <- loop
// hook env
auto env1 = captureEnv();
// produce all variables hooks
for (const auto& change : env1.changes) {
auto info = change.first;
info->loc = nullptr;
alloc(info);
}
// save hooks for later
env1.capture();
// env2 starts with hooked variables
auto env2 = captureEnv();
expr->stmt_->accept(this);
auto loopBlock = flushBasicBlock(); // jump <- cond
addQuad(cond);
// env2 contains changed hooks
env2.capture();
// restore env1 pre-hook variables
env1.revert();
for (const auto& p : env2.changes) {
auto info = p.first;
auto x1 = p.second.first;
auto x2 = p.second.second;
// save hooks if used
info->loc = x1;
// transition from pre-hook to hooked var [before -> cond]
beforeBlock->addJumpInitQuad(make_shared<QAssign>(x1, Op::Copy, env1.changes[info].first));
// transition from loop var to hooked var [loop -> cond]
loopBlock->addJumpInitQuad(make_shared<QAssign>(x1, Op::Copy, x2));
}
// expr uses pre-hook variables iff unused in loop
auto var = evalExpr(expr->expr_);
addQuad<QJumpCond>(loop, Op::Copy, var);
auto condBlock = flushBasicBlock(); // jump -> loop
// next block is ready to use updated variables
beforeBlock->append(condBlock);
condBlock->append(loopBlock);
condBlock->append(block);
}
void QuadrupleGenerator::visitSExp(SExp *p) {
evalExpr(p->expr_);
}
void QuadrupleGenerator::visitForEach(ForEach *p) {
auto tab = evalExpr(p->expr_);
throw ParseError("ForEach is yet unimplemented!", p);
// TODO: implement
}
\ No newline at end of file
//
// Created by zygzagz on 05.01.2021.
//
#ifndef ZAD2_QUADRUPLEGENERATOR_H
#define ZAD2_QUADRUPLEGENERATOR_H
#include <filesystem>
#include <utility>
#include "../TypeDefs.h"
#include "../Absyn.h"
#include "../Skeleton.h"
#include "Variable.h"
#include "../ParseError.h"
#include "../Info.h"
#include "Quadruple.h"
#include "BasicBlock.h"
class QuadrupleGenerator : public Skeleton {
public:
QuadrupleGenerator(shared_ptr<Scope> s) : lineno(0), scope(std::move(s)) {}
vector<BasicBlockPtr> compileFunction(FunctionInfoPtr f);
private:
vector<VariablePtr> vars;
vector<BasicBlockPtr> blocks;
Quadruple::Op op;
BasicBlockPtr block;
VariablePtr lastVar;
int lineno;
shared_ptr<Scope> scope;
VariablePtr evalLVal(Visitable *expr) {
auto info = evalExpr(expr)->info;
if (!info) {
throw ParseError("LValue expected", expr);
}
info->loc = nullptr;
return alloc(info);
}
VariablePtr evalExpr(Visitable *expr) {
if (!expr) throw runtime_error("No expr to eval");
lineno = expr->lineno;
lastVar = nullptr;
try {
expr->accept(this);
} catch (const ParseError &err) {
throw ParseError(err, expr);
}
if (!lastVar) throw ParseError("No variable found", expr);
auto ret = lastVar;
lastVar = nullptr;
return ret;
}
VariablePtr alloc(const VarInfoPtr& info) {
if (info->loc) {
return info->loc;
}
auto v = make_shared<Variable>(info);
vars.emplace_back(v);
if (!info->isInstanceVariable()) {
info->loc = v;
}
return v;
}
template<typename... Args> VariablePtr alloc(Args... args) {
auto v = make_shared<Variable>(args...);
vars.emplace_back(v);
return v;
}
template<typename T, typename... Args> QuadruplePtr addQuad(Args... args) {
return addQuad(make_shared<T>(args...));
}
QuadruplePtr addQuad(QuadruplePtr quad) {
quad->lineno = lineno;
flushLastQuad();
lastQuad = quad;
return quad;
}
QuadruplePtr lastQuad;
void flushLastQuad() {
if (lastQuad) {
block->quads.emplace_back(lastQuad);
lastQuad = nullptr;
}
}
BasicBlockPtr flushBasicBlock();
void visitPlus(Plus *p) override { op = Quadruple::Op::Plus; }
void visitMinus(Minus *p) override { op = Quadruple::Op::Minus; }
VariableLayout captureEnv();
void visitTimes(Times *p) override { op = Quadruple::Op::Mul; }
void visitDiv(Div *p) override { op = Quadruple::Op::Div; }
void visitMod(Mod *p) override { op = Quadruple::Op::Mod; }
void visitLTH(LTH *p) override { op = Quadruple::Op::LT; }
void visitLE(LE *p) override { op = Quadruple::Op::LE; }
void visitEVar(EVar *p) override;
void visitGTH(GTH *p) override { op = Quadruple::Op::GT; }
void visitGE(GE *p) override { op = Quadruple::Op::GE; }
void visitELitInt(ELitInt *p) override;
void visitEQU(EQU *p) override { op = Quadruple::Op::EQ; }
void visitNE(NE *p) override { op = Quadruple::Op::NEQ; }
void visitELitTrue(ELitTrue *p) override;
void visitELitFalse(ELitFalse *p) override;
void visitEString(EString *p) override;
void visitNullCast(NullCast *p) override;
void visitNeg(Neg *p) override;
void visitNot(Not *p) override;
void visitEMul(EMul *p) override;
void visitEAdd(EAdd *p) override;
void visitERel(ERel *p) override;
void visitEAnd(EAnd *p) override;
void visitEOr(EOr *p) override;
void visitEIndexAcc(EIndexAcc *p) override;
void visitEClsMmbr(EClsMmbr *p) override;
void visitEApp(EApp *p) override;
void visitENewArray(ENewArray *p) override;
void visitENewClass(ENewClass *p) override;
void visitInit(Init *p) override;
void assign(Expr* lval, VariablePtr val);
void visitAss(Ass *p) override;
void visitIncr(Incr *p) override;
void visitDecr(Decr *p) override;
void visitRet(Ret *p) override;
void visitVRet(VRet *p) override;
void visitCond(Cond *p) override;
void visitCondElse(CondElse *p) override;
void compileCond(Expr *cond, Stmt *stmt1, Stmt *stmt2);
void merge2Envs(VariableLayout *env1, BasicBlockPtr b1, VariableLayout *env2, BasicBlockPtr b2);
void visitWhile(While *expr) override;
void visitSExp(SExp *p) override;
void visitForEach(ForEach *p) override;
void visitBlk(Blk *p) override;
};
#endif //ZAD2_QUADRUPLEGENERATOR_H
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment