-
Notifications
You must be signed in to change notification settings - Fork 16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Initial LLVM codegen vistor routines #457
Changes from 18 commits
397e3e2
9545fa3
4e0c8f9
1409a44
6000561
5827c8b
c174101
1d59d6c
852f418
e3ef5ec
aa64395
a96d8bb
1d6029b
8b6ed9e
8a5642f
a21e170
2adf9b6
ed9f0fe
6787dfa
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,45 +11,154 @@ | |
|
||
#include "llvm/IR/BasicBlock.h" | ||
#include "llvm/IR/Function.h" | ||
#include "llvm/IR/IRBuilder.h" | ||
#include "llvm/IR/LLVMContext.h" | ||
#include "llvm/IR/Module.h" | ||
#include "llvm/IR/Type.h" | ||
#include "llvm/IR/ValueSymbolTable.h" | ||
#include <llvm/IR/Constants.h> | ||
|
||
namespace nmodl { | ||
namespace codegen { | ||
|
||
|
||
// LLVM code generator objects | ||
using namespace llvm; | ||
static std::unique_ptr<LLVMContext> TheContext; | ||
static std::unique_ptr<Module> TheModule; | ||
static std::unique_ptr<IRBuilder<>> Builder; | ||
static std::map<std::string, Value*> NamedValues; | ||
/****************************************************************************************/ | ||
/* Overloaded visitor routines */ | ||
/****************************************************************************************/ | ||
|
||
|
||
void CodegenLLVMVisitor::visit_statement_block(const ast::StatementBlock& node) { | ||
logger->info("CodegenLLVMVisitor : visiting statement block"); | ||
node.visit_children(*this); | ||
// TODO : code generation for new block scope | ||
void CodegenLLVMVisitor::visit_binary_expression(const ast::BinaryExpression& node) { | ||
const auto& op = node.get_op().get_value(); | ||
|
||
// Process rhs first, since lhs is handled differently for assignment and binary | ||
// operators. | ||
node.get_rhs()->accept(*this); | ||
llvm::Value* rhs = values.back(); | ||
pramodk marked this conversation as resolved.
Show resolved
Hide resolved
|
||
values.pop_back(); | ||
if (op == ast::BinaryOp::BOP_ASSIGN) { | ||
auto var = dynamic_cast<ast::VarName*>(node.get_lhs().get()); | ||
if (!var) { | ||
throw std::runtime_error("Error: only VarName assignment is currently supported.\n"); | ||
} | ||
llvm::Value* alloca = named_values[var->get_node_name()]; | ||
builder.CreateStore(rhs, alloca); | ||
return; | ||
pramodk marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
node.get_lhs()->accept(*this); | ||
llvm::Value* lhs = values.back(); | ||
values.pop_back(); | ||
llvm::Value* result; | ||
|
||
// \todo: Support other binary operators | ||
switch (op) { | ||
#define DISPATCH(binary_op, llvm_op) \ | ||
case binary_op: \ | ||
result = llvm_op(lhs, rhs); \ | ||
values.push_back(result); \ | ||
break; | ||
|
||
DISPATCH(ast::BinaryOp::BOP_ADDITION, builder.CreateFAdd); | ||
DISPATCH(ast::BinaryOp::BOP_DIVISION, builder.CreateFDiv); | ||
DISPATCH(ast::BinaryOp::BOP_MULTIPLICATION, builder.CreateFMul); | ||
DISPATCH(ast::BinaryOp::BOP_SUBTRACTION, builder.CreateFSub); | ||
|
||
#undef DISPATCH | ||
} | ||
} | ||
|
||
void CodegenLLVMVisitor::visit_procedure_block(const ast::ProcedureBlock& node) { | ||
logger->info("CodegenLLVMVisitor : visiting {} procedure", node.get_node_name()); | ||
|
||
// print position, nmodl and json form as | ||
/* | ||
logger->info("Location {} \n NMODL {} \n JSON : {} \n", | ||
node.get_token()->position(), | ||
to_nmodl(node), | ||
to_json(node)); | ||
*/ | ||
node.visit_children(*this); | ||
// TODO : code generation for procedure block | ||
void CodegenLLVMVisitor::visit_boolean(const ast::Boolean& node) { | ||
const auto& constant = llvm::ConstantInt::get(llvm::Type::getInt1Ty(*context), | ||
node.get_value()); | ||
values.push_back(constant); | ||
} | ||
|
||
void CodegenLLVMVisitor::visit_double(const ast::Double& node) { | ||
const auto& constant = llvm::ConstantFP::get(llvm::Type::getDoubleTy(*context), | ||
node.get_value()); | ||
values.push_back(constant); | ||
} | ||
|
||
void CodegenLLVMVisitor::visit_integer(const ast::Integer& node) { | ||
const auto& constant = llvm::ConstantInt::get(llvm::Type::getInt32Ty(*context), | ||
node.get_value()); | ||
values.push_back(constant); | ||
} | ||
|
||
void CodegenLLVMVisitor::visit_local_list_statement(const ast::LocalListStatement& node) { | ||
for (const auto& variable: node.get_variables()) { | ||
// LocalVar always stores a Name. | ||
auto name = variable->get_node_name(); | ||
pramodk marked this conversation as resolved.
Show resolved
Hide resolved
|
||
llvm::Type* var_type = llvm::Type::getDoubleTy(*context); | ||
llvm::Value* alloca = builder.CreateAlloca(var_type, /*ArraySize=*/nullptr, name); | ||
named_values[name] = alloca; | ||
} | ||
} | ||
|
||
void CodegenLLVMVisitor::visit_program(const ast::Program& node) { | ||
node.visit_children(*this); | ||
result_code = "Hello World"; | ||
// Keep this for easier development (maybe move to debug mode later). | ||
std::cout << print_module(); | ||
} | ||
|
||
void CodegenLLVMVisitor::visit_procedure_block(const ast::ProcedureBlock& node) { | ||
const auto& name = node.get_node_name(); | ||
const auto& parameters = node.get_parameters(); | ||
|
||
// The procedure parameters are doubles by default. | ||
std::vector<llvm::Type*> arg_types; | ||
for (size_t i = 0, e = parameters.size(); i < e; ++i) | ||
arg_types.push_back(llvm::Type::getDoubleTy(*context)); | ||
llvm::Type* return_type = llvm::Type::getVoidTy(*context); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nitpick : indentation There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually, what is the indentation used for the project? I noticed that headers use 2 spaces, and implementation uses 4 spaces. So I will just use that? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, I was mistaken here. I see that line 103 is indented for loop! For indentation, we use 4 spaces everywhere. In fact, assuming you have clang-format 8/9/10, and cmake option If you see 2 spaces somewhere, let me know. |
||
|
||
llvm::Function* proc = | ||
llvm::Function::Create(llvm::FunctionType::get(return_type, arg_types, /*isVarArg=*/false), | ||
llvm::Function::ExternalLinkage, | ||
name, | ||
*module); | ||
|
||
llvm::BasicBlock* body = llvm::BasicBlock::Create(*context, /*Name=*/"", proc); | ||
; | ||
builder.SetInsertPoint(body); | ||
|
||
// First, allocate parameters on the stack and add them to the symbol table. | ||
unsigned i = 0; | ||
for (auto& arg: proc->args()) { | ||
std::string arg_name = parameters[i++].get()->get_node_name(); | ||
llvm::Value* alloca = builder.CreateAlloca(arg.getType(), /*ArraySize=*/nullptr, arg_name); | ||
arg.setName(arg_name); | ||
builder.CreateStore(&arg, alloca); | ||
named_values[arg_name] = alloca; | ||
} | ||
|
||
const auto& statements = node.get_statement_block()->get_statements(); | ||
for (const auto& statement: statements) { | ||
// \todo: Support other statement types. | ||
if (statement->is_local_list_statement() || statement->is_expression_statement()) | ||
statement->accept(*this); | ||
} | ||
|
||
values.clear(); | ||
// Clear named values for now, since there is no support of global variables yet. | ||
// It will be better to keep a separate global map (or use module?). | ||
pramodk marked this conversation as resolved.
Show resolved
Hide resolved
|
||
named_values.clear(); | ||
} | ||
|
||
void CodegenLLVMVisitor::visit_unary_expression(const ast::UnaryExpression& node) { | ||
ast::UnaryOp op = node.get_op().get_value(); | ||
node.get_expression()->accept(*this); | ||
llvm::Value* value = values.back(); | ||
values.pop_back(); | ||
if (op == ast::UOP_NEGATION) { | ||
llvm::Value* result = builder.CreateFNeg(value); | ||
values.push_back(result); | ||
} else { | ||
// Support only `double` operators for now. | ||
throw std::runtime_error("Error: unsupported unary operator\n"); | ||
} | ||
} | ||
|
||
void CodegenLLVMVisitor::visit_var_name(const ast::VarName& node) { | ||
llvm::Value* var = builder.CreateLoad(named_values[node.get_node_name()]); | ||
values.push_back(var); | ||
} | ||
|
||
} // namespace codegen | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just FYI : I changed this because this was a bug - we were finding LLVM only for Clang compiler.