feat: implement proper IR with value handles with dynamic and transferable types
This commit is contained in:
@@ -10,158 +10,195 @@
|
||||
namespace IR
|
||||
{
|
||||
|
||||
class IRBuilder
|
||||
{
|
||||
public:
|
||||
IRBuilder(const Node* root)
|
||||
: m_root(root), m_ops(new OpBuilder()) {}
|
||||
public:
|
||||
// TODO: support other literals
|
||||
Value ParseIntLiteral(const IntLiteralNode* literal)
|
||||
class IRBuilder
|
||||
{
|
||||
auto dst = AllocateRegister();
|
||||
m_ops->Push(new LoadConstOp(dst, literal->integer()));
|
||||
return dst;
|
||||
}
|
||||
public:
|
||||
IRBuilder(const Node *root)
|
||||
: m_root(root), m_ops(new OpBuilder()) {}
|
||||
|
||||
Value ParseVariable(const VariableNode* var)
|
||||
{
|
||||
// auto dst = AllocateRegister();
|
||||
// m_ops->Push(new LoadOp(dst, var->name()));
|
||||
if (m_locals.find(var->name()) == m_locals.end())
|
||||
public:
|
||||
// TODO: support other literals
|
||||
ValueHandle *ParseIntLiteral(const IntLiteralNode *literal)
|
||||
{
|
||||
// TODO: throw proper error
|
||||
assert(0 && "ERROR: variable does not exist");
|
||||
}
|
||||
return m_locals[var->name()];
|
||||
}
|
||||
|
||||
Value ParseFnCall(const FnCallNode* fn)
|
||||
{
|
||||
// TODO: support multiple args
|
||||
auto argRegs = ValueBuilder();
|
||||
if (fn->arg() != nullptr)
|
||||
{
|
||||
auto arg = ParseExpression(fn->arg());
|
||||
argRegs.Push(arg);
|
||||
}
|
||||
auto dst = AllocateRegister();
|
||||
m_ops->Push(new CallOp(dst, fn->name(), argRegs.view()));
|
||||
return dst;
|
||||
}
|
||||
|
||||
Value ParseFactor(const Node* factor)
|
||||
{
|
||||
switch(factor->GetType())
|
||||
{
|
||||
case NodeType::IntLiteral: return ParseIntLiteral(reinterpret_cast<const IntLiteralNode*>(factor));
|
||||
case NodeType::Variable: return ParseVariable(reinterpret_cast<const VariableNode*>(factor));
|
||||
case NodeType::FnCall: return ParseFnCall(reinterpret_cast<const FnCallNode*>(factor));
|
||||
default: assert(0 && "some factor may not be handled"); break;
|
||||
}
|
||||
|
||||
assert(0 && "unreachable");
|
||||
return Value();
|
||||
}
|
||||
|
||||
Value ParseExpression(const Node* expression)
|
||||
{
|
||||
if (expression->GetType() == NodeType::Expression)
|
||||
{
|
||||
auto expr = reinterpret_cast<const ExpressionNode*>(expression);
|
||||
auto lhs = ParseExpression(expr->left());
|
||||
auto rhs = ParseExpression(expr->right());
|
||||
auto dst = AllocateRegister();
|
||||
|
||||
assert(4 == static_cast<int>(ExpressionNode::Operator::COUNT_OPERATORS) && "some operators may not be handled");
|
||||
switch (expr->op())
|
||||
{
|
||||
case ExpressionNode::Operator::Plus: m_ops->Push(new AddOp(dst, lhs, rhs)); break;
|
||||
default: assert(0 && "TODO: implement other operations"); break;
|
||||
}
|
||||
|
||||
auto dst = AllocateUnnamed<ConstantInt>(literal->integer());
|
||||
return dst;
|
||||
}
|
||||
}
|
||||
|
||||
return ParseFactor(expression);
|
||||
}
|
||||
|
||||
void ParseVarDecl(const VarDeclNode* varDecl)
|
||||
{
|
||||
auto value = ParseExpression(varDecl->value());
|
||||
// m_ops->Push(new StoreOp(varDecl->name(), value));
|
||||
m_locals.insert(std::make_pair(varDecl->name(), value));
|
||||
}
|
||||
|
||||
Block ParseBlock(const CompoundNode* compound)
|
||||
{
|
||||
StartBlock();
|
||||
for (auto &statement : *compound)
|
||||
void ParseVarDecl(const VarDeclNode *varDecl)
|
||||
{
|
||||
switch(statement->GetType())
|
||||
auto value = ParseExpression(varDecl->value());
|
||||
// TODO: gather type information from var decl signature, aka local <int> v = 0;
|
||||
auto dst = AllocateNamed<Pointer>();
|
||||
m_ops->Push(new AllocateOp(dst, value->GetType()));
|
||||
m_ops->Push(new StoreOp(value, reinterpret_cast<Pointer *>(dst)));
|
||||
m_locals.insert(std::make_pair(varDecl->name(), reinterpret_cast<Pointer *>(dst)));
|
||||
}
|
||||
|
||||
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())
|
||||
{
|
||||
case NodeType::VarDecl: ParseVarDecl(reinterpret_cast<VarDeclNode*>(statement)); continue;
|
||||
default: ParseExpression(statement); continue;
|
||||
// TODO: throw proper error
|
||||
assert(0 && "ERROR: variable does not exist");
|
||||
}
|
||||
return reinterpret_cast<ValueHandle *>(m_locals[var->name()]);
|
||||
}
|
||||
auto ops = EndBlock();
|
||||
auto block = Block(m_block_counter++, std::move(ops->view()));
|
||||
operator delete(ops);
|
||||
return block;
|
||||
}
|
||||
|
||||
OpView Build()
|
||||
{
|
||||
assert(m_root->GetType() == NodeType::Program && "root should be a program");
|
||||
auto program = reinterpret_cast<const ProgramNode*>(m_root);
|
||||
|
||||
// Externs
|
||||
for (auto &extrn : program->externs())
|
||||
ValueHandle *ParseFnCall(const FnCallNode *fn)
|
||||
{
|
||||
m_ops->Push(new ExternOp(extrn->symbol()));
|
||||
// TODO: support multiple args
|
||||
auto argRegs = ValueBuilder();
|
||||
if (fn->arg() != nullptr)
|
||||
{
|
||||
auto arg = ParseExpression(fn->arg());
|
||||
argRegs.Push(arg);
|
||||
}
|
||||
// TODO: gather return type of the function
|
||||
auto dst = AllocateUnnamed<Void>();
|
||||
m_ops->Push(new CallOp(dst, fn->name(), argRegs.view()));
|
||||
return dst;
|
||||
}
|
||||
|
||||
// Functions
|
||||
for (auto &fn : program->funcs())
|
||||
ValueHandle *ParseFactor(const Node *factor)
|
||||
{
|
||||
auto block = ParseBlock(fn->body());
|
||||
m_ops->Push(new FnOp(fn->name(), fn->params(), std::move(block)));
|
||||
switch (factor->GetType())
|
||||
{
|
||||
case NodeType::IntLiteral:
|
||||
return ParseIntLiteral(reinterpret_cast<const IntLiteralNode *>(factor));
|
||||
case NodeType::Variable:
|
||||
return ParseVariable(reinterpret_cast<const VariableNode *>(factor));
|
||||
case NodeType::FnCall:
|
||||
return ParseFnCall(reinterpret_cast<const FnCallNode *>(factor));
|
||||
default:
|
||||
assert(0 && "some factor may not be handled");
|
||||
break;
|
||||
}
|
||||
|
||||
assert(0 && "unreachable");
|
||||
return reinterpret_cast<ValueHandle *>(new Void(0));
|
||||
}
|
||||
|
||||
return OpView(m_ops->data, m_ops->size);
|
||||
}
|
||||
public:
|
||||
// TODO: think about safety (copying m_ops->data before giving)
|
||||
OpView ops() const { return OpView(m_ops->data, m_ops->size); }
|
||||
private:
|
||||
void StartBlock()
|
||||
{
|
||||
m_containers.Push(m_ops);
|
||||
m_ops = new OpBuilder();
|
||||
}
|
||||
ValueHandle *ParseExpression(const Node *expression)
|
||||
{
|
||||
if (expression->GetType() == NodeType::Expression)
|
||||
{
|
||||
auto expr = reinterpret_cast<const ExpressionNode *>(expression);
|
||||
auto lhs = ParseExpression(expr->left());
|
||||
auto rhs = ParseExpression(expr->right());
|
||||
auto dst = AllocateNamed<Instruction>(lhs->GetType());
|
||||
|
||||
OpBuilder* EndBlock()
|
||||
{
|
||||
assert(m_containers.size > 0 && "containers stack is empty");
|
||||
auto current = m_ops;
|
||||
m_ops = m_containers.data[m_containers.size - 1];
|
||||
m_containers.size--;
|
||||
return current;
|
||||
}
|
||||
private:
|
||||
Value AllocateRegister()
|
||||
{
|
||||
return Value(m_value_counter++);
|
||||
}
|
||||
private:
|
||||
const Node* m_root = nullptr;
|
||||
assert(4 == static_cast<int>(ExpressionNode::Operator::COUNT_OPERATORS) && "some operators may not be handled");
|
||||
switch (expr->op())
|
||||
{
|
||||
case ExpressionNode::Operator::Plus:
|
||||
m_ops->Push(new MathOp(dst, lhs, rhs, OpType::ADD));
|
||||
break;
|
||||
case ExpressionNode::Operator::Multiply:
|
||||
m_ops->Push(new MathOp(dst, lhs, rhs, OpType::MUL));
|
||||
break;
|
||||
case ExpressionNode::Operator::Minus:
|
||||
m_ops->Push(new MathOp(dst, lhs, rhs, OpType::SUB));
|
||||
break;
|
||||
case ExpressionNode::Operator::Divide:
|
||||
m_ops->Push(new MathOp(dst, lhs, rhs, OpType::DIV));
|
||||
break;
|
||||
default:
|
||||
assert(0 && "unreachable");
|
||||
break;
|
||||
}
|
||||
|
||||
OpBuilder* m_ops = nullptr;
|
||||
unsigned int m_value_counter = 0;
|
||||
unsigned int m_block_counter = 0;
|
||||
std::unordered_map<StringView, Value> m_locals;
|
||||
return dst;
|
||||
}
|
||||
|
||||
Builder<OpBuilder*> m_containers;
|
||||
};
|
||||
return ParseFactor(expression);
|
||||
}
|
||||
|
||||
Block ParseBlock(const CompoundNode *compound)
|
||||
{
|
||||
StartBlock();
|
||||
for (auto &statement : *compound)
|
||||
{
|
||||
switch (statement->GetType())
|
||||
{
|
||||
case NodeType::VarDecl:
|
||||
ParseVarDecl(reinterpret_cast<VarDeclNode *>(statement));
|
||||
continue;
|
||||
default:
|
||||
ParseExpression(statement);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
auto ops = EndBlock();
|
||||
auto block = Block(m_block_counter++, std::move(ops->view()));
|
||||
operator delete(ops);
|
||||
return block;
|
||||
}
|
||||
|
||||
OpView Build()
|
||||
{
|
||||
assert(m_root->GetType() == NodeType::Program && "root should be a program");
|
||||
auto program = reinterpret_cast<const ProgramNode *>(m_root);
|
||||
|
||||
// Externs
|
||||
for (auto &extrn : program->externs())
|
||||
{
|
||||
m_ops->Push(new ExternOp(extrn->symbol()));
|
||||
}
|
||||
|
||||
// Functions
|
||||
for (auto &fn : program->funcs())
|
||||
{
|
||||
auto block = ParseBlock(fn->body());
|
||||
m_ops->Push(new FnOp(fn->name(), fn->params(), std::move(block)));
|
||||
}
|
||||
|
||||
return OpView(m_ops->data, m_ops->size);
|
||||
}
|
||||
|
||||
public:
|
||||
// TODO: think about safety (copying m_ops->data before giving)
|
||||
OpView ops() const { return OpView(m_ops->data, m_ops->size); }
|
||||
|
||||
private:
|
||||
void StartBlock()
|
||||
{
|
||||
m_containers.Push(m_ops);
|
||||
m_ops = new OpBuilder();
|
||||
}
|
||||
|
||||
OpBuilder *EndBlock()
|
||||
{
|
||||
assert(m_containers.size > 0 && "containers stack is empty");
|
||||
auto current = m_ops;
|
||||
m_ops = m_containers.data[m_containers.size - 1];
|
||||
m_containers.size--;
|
||||
return current;
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename V, typename... Args>
|
||||
ValueHandle *AllocateNamed(Args &&...args)
|
||||
{
|
||||
return new V(++m_value_counter, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename V, typename... Args>
|
||||
ValueHandle *AllocateUnnamed(Args &&...args)
|
||||
{
|
||||
return new V(ValueHandle::kNoId, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
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<StringView, Pointer *> m_locals;
|
||||
|
||||
Builder<OpBuilder *> m_containers;
|
||||
};
|
||||
|
||||
} // namespace IR
|
||||
|
||||
Reference in New Issue
Block a user