Skip to content

Commit

Permalink
* Improvements to codegen helper (Part I)
Browse files Browse the repository at this point in the history
 - instance structure now contains all global variables
 - instance structure now contains index variables for ions
 - nrn_state kernel now has all variables converted to instance
 - InstanceVarHelper added to query variable and it's location
* Support for codegen variable with type
* Add nmodl_to_json helper added in main.cpp
* Added --vector-width CLI option
* Add instance struct argument to nrn_state_hh
* Add comments as TODOs to support LLVM IR generation

Note that this commit and next commit (Part II) are required to
make LLVM IR code generation working. Vector IR generation is
working except indirect indexes. See comment in #531.
  • Loading branch information
pramodk authored and iomaganaris committed May 10, 2022
1 parent 67adaf4 commit 966841b
Show file tree
Hide file tree
Showing 11 changed files with 249 additions and 58 deletions.
6 changes: 6 additions & 0 deletions src/codegen/codegen_naming.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,12 @@ static constexpr char VOLTAGE_UNUSED_VARIABLE[] = "v_unused";
/// variable t indicating last execution time of net receive block
static constexpr char T_SAVE_VARIABLE[] = "tsave";

/// global variable celsius
static constexpr char CELSIUS_VARIABLE[] = "celsius";

/// global variable second_order
static constexpr char SECOND_ORDER_VARIABLE[] = "secondorder";

/// shadow rhs variable in neuron thread structure
static constexpr char NTHREAD_RHS_SHADOW[] = "_shadow_rhs";

Expand Down
78 changes: 60 additions & 18 deletions src/codegen/llvm/codegen_llvm_helper_visitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,12 +141,12 @@ void CodegenLLVMHelperVisitor::create_function_for_node(ast::Block& node) {
block->emplace_back_statement(return_statement);

/// prepare function arguments based original node arguments
ast::CodegenArgumentVector arguments;
ast::CodegenVarWithTypeVector arguments;
for (const auto& param: node.get_parameters()) {
/// create new type and name for creating new ast node
auto type = new ast::CodegenVarType(FLOAT_TYPE);
auto var = param->get_name()->clone();
arguments.emplace_back(new ast::CodegenArgument(type, var));
arguments.emplace_back(new ast::CodegenVarWithType(type, 0, var));
}

/// return type of the function is same as return variable type
Expand All @@ -159,19 +159,43 @@ void CodegenLLVMHelperVisitor::create_function_for_node(ast::Block& node) {
}

std::shared_ptr<ast::InstanceStruct> CodegenLLVMHelperVisitor::create_instance_struct() {
ast::CodegenVarVector codegen_vars;
ast::CodegenVarWithTypeVector codegen_vars;

auto add_var_with_type =
[&](const std::string& name, const ast::AstNodeType type, int is_pointer) {
auto var_name = new ast::Name(new ast::String(name));
auto var_type = new ast::CodegenVarType(type);
auto codegen_var = new ast::CodegenVarWithType(var_type, is_pointer, var_name);
codegen_vars.emplace_back(codegen_var);
};

/// float variables are standard pointers to float vectors
for (auto& float_var: info.codegen_float_variables) {
auto name = new ast::Name(new ast::String(float_var->get_name()));
auto codegen_var = new ast::CodegenVar(1, name);
codegen_vars.emplace_back(codegen_var);
add_var_with_type(float_var->get_name(), FLOAT_TYPE, 1);
}

/// int variables are pointers to indexes for other vectors
for (auto& int_var: info.codegen_int_variables) {
auto name = new ast::Name(new ast::String(int_var.symbol->get_name()));
auto codegen_var = new ast::CodegenVar(1, name);
codegen_vars.emplace_back(codegen_var);
add_var_with_type(int_var.symbol->get_name(), FLOAT_TYPE, 1);
}

// for integer variables, there should be index
for (auto& int_var: info.codegen_int_variables) {
std::string var_name = int_var.symbol->get_name() + "_index";
add_var_with_type(var_name, INTEGER_TYPE, 1);
}

// add voltage and node index
add_var_with_type("voltage", FLOAT_TYPE, 1);
add_var_with_type("node_index", INTEGER_TYPE, 1);

// add dt, t, celsius
add_var_with_type(naming::NTHREAD_T_VARIABLE, FLOAT_TYPE, 0);
add_var_with_type(naming::NTHREAD_DT_VARIABLE, FLOAT_TYPE, 0);
add_var_with_type(naming::CELSIUS_VARIABLE, FLOAT_TYPE, 0);
add_var_with_type(naming::SECOND_ORDER_VARIABLE, INTEGER_TYPE, 0);
add_var_with_type(MECH_NODECOUNT_VAR, INTEGER_TYPE, 0);

return std::make_shared<ast::InstanceStruct>(codegen_vars);
}

Expand Down Expand Up @@ -362,13 +386,24 @@ void CodegenLLVMHelperVisitor::convert_to_instance_variable(ast::Node& node,
auto variables = collect_nodes(node, {ast::AstNodeType::VAR_NAME});
for (auto& v: variables) {
auto variable = std::dynamic_pointer_cast<ast::VarName>(v);
/// if variable is of type instance then convert it to index
if (info.is_an_instance_variable(variable->get_node_name())) {
auto variable_name = variable->get_node_name();

/// all instance variables defined in the mod file should be converted to
/// indexed variables based on the loop iteration variable
if (info.is_an_instance_variable(variable_name)) {
auto name = variable->get_name()->clone();
auto index = new ast::Name(new ast::String(index_var));
auto indexed_name = std::make_shared<ast::IndexedName>(name, index);
variable->set_name(indexed_name);
}

/// instance_var_helper check of instance variables from mod file as well
/// as extra variables like ion index variables added for code generation
if (instance_var_helper.is_an_instance_variable(variable_name)) {
auto name = new ast::Name(new ast::String(MECH_INSTANCE_VAR));
auto var = std::make_shared<ast::CodegenInstanceVar>(name, variable->clone());
variable->set_name(var);
}
}
}

Expand Down Expand Up @@ -438,7 +473,7 @@ void CodegenLLVMHelperVisitor::visit_nrn_state_block(ast::NrnStateBlock& node) {
/// loop constructs : initialization, condition and increment
const auto& initialization = create_statement_as_expression("id = 0");
const auto& condition = create_expression("id < node_count");
const auto& increment = create_statement_as_expression("id = id + 1");
const auto& increment = create_statement_as_expression("id = id + {}"_format(vector_width));

/// loop body : initialization + solve blocks
ast::StatementVector loop_def_statements;
Expand Down Expand Up @@ -496,9 +531,6 @@ void CodegenLLVMHelperVisitor::visit_nrn_state_block(ast::NrnStateBlock& node) {
/// now construct a new code block which will become the body of the loop
auto loop_block = std::make_shared<ast::StatementBlock>(loop_body);

/// convert all variables inside loop body to instance variables
convert_to_instance_variable(*loop_block, loop_index_var);

/// convert local statement to codegenvar statement
convert_local_statement(*loop_block);

Expand All @@ -508,6 +540,9 @@ void CodegenLLVMHelperVisitor::visit_nrn_state_block(ast::NrnStateBlock& node) {
increment,
loop_block);

/// convert all variables inside loop body to instance variables
convert_to_instance_variable(*for_loop_statement, loop_index_var);

/// loop itself becomes one of the statement in the function
function_statements.push_back(for_loop_statement);

Expand All @@ -520,7 +555,12 @@ void CodegenLLVMHelperVisitor::visit_nrn_state_block(ast::NrnStateBlock& node) {
auto return_type = new ast::CodegenVarType(ast::AstNodeType::VOID);

/// \todo : currently there are no arguments
ast::CodegenArgumentVector code_arguments;
ast::CodegenVarWithTypeVector code_arguments;

auto instance_var_type = new ast::CodegenVarType(ast::AstNodeType::INSTANCE_STRUCT);
auto instance_var_name = new ast::Name(new ast::String("mech"));
auto instance_var = new ast::CodegenVarWithType(instance_var_type, 1, instance_var_name);
code_arguments.emplace_back(instance_var);

/// finally, create new function
auto function =
Expand All @@ -535,14 +575,16 @@ void CodegenLLVMHelperVisitor::visit_program(ast::Program& node) {
CodegenHelperVisitor v;
info = v.analyze(node);

instance_var_helper.instance = create_instance_struct();
node.emplace_back_node(instance_var_helper.instance);

logger->info("Running CodegenLLVMHelperVisitor");
node.visit_children(*this);
for (auto& fun: codegen_functions) {
node.emplace_back_node(fun);
}

auto llvm_instance_struct = create_instance_struct();
node.emplace_back_node(llvm_instance_struct);
std::cout << nmodl::to_nmodl(node);
}


Expand Down
71 changes: 69 additions & 2 deletions src/codegen/llvm/codegen_llvm_helper_visitor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,73 @@

#include <string>

#include "ast/instance_struct.hpp"
#include "codegen/codegen_info.hpp"
#include "symtab/symbol_table.hpp"
#include "visitors/ast_visitor.hpp"

namespace nmodl {
namespace codegen {


using namespace fmt::literals;
typedef std::vector<std::shared_ptr<ast::CodegenFunction>> CodegenFunctionVector;

/**
* @addtogroup llvm_codegen_details
* @{
*/

/**
* \class InstanceVarHelper
* \brief Helper to query instance variables information
*
* For LLVM IR generation we need to know the variable, it's type and
* location in the instance structure. This helper provides convenient
* functions to query this information.
*/
struct InstanceVarHelper {
/// pointer to instance node in the AST
std::shared_ptr<ast::InstanceStruct> instance;

/// find variable with given name and return the iterator
ast::CodegenVarWithTypeVector::const_iterator find_variable(
const ast::CodegenVarWithTypeVector& vars,
const std::string& name) {
return find_if(vars.begin(),
vars.end(),
[&](const std::shared_ptr<ast::CodegenVarWithType>& v) {
return v->get_node_name() == name;
});
}

/// check if given variable is instance variable
bool is_an_instance_variable(const std::string& name) {
const auto& vars = instance->get_codegen_vars();
return find_variable(vars, name) != vars.end();
}

/// return codegen variable with a given name
const std::shared_ptr<ast::CodegenVarWithType>& get_variable(const std::string& name) {
const auto& vars = instance->get_codegen_vars();
auto it = find_variable(vars, name);
if (it == vars.end()) {
throw std::runtime_error("Can not find variable with name {}"_format(name));
}
return *it;
}

/// return position of the variable in the instance structure
int get_variable_index(const std::string& name) {
const auto& vars = instance->get_codegen_vars();
auto it = find_variable(vars, name);
if (it == vars.end()) {
throw std::runtime_error("Can not find codegen variable with name {}"_format(name));
}
return (it - vars.begin());
}
};


/**
* \class CodegenLLVMHelperVisitor
* \brief Helper visitor for AST information to help code generation backends
Expand All @@ -48,24 +100,39 @@ typedef std::vector<std::shared_ptr<ast::CodegenFunction>> CodegenFunctionVector
* these will be common across all backends.
*/
class CodegenLLVMHelperVisitor: public visitor::AstVisitor {
// explicit vectorisation width
int vector_width;

/// newly generated code generation specific functions
CodegenFunctionVector codegen_functions;

/// ast information for code generation
codegen::CodegenInfo info;

/// mechanism data helper
InstanceVarHelper instance_var_helper;

/// default integer and float node type
const ast::AstNodeType INTEGER_TYPE = ast::AstNodeType::INTEGER;
const ast::AstNodeType FLOAT_TYPE = ast::AstNodeType::DOUBLE;

/// name of the mechanism instance parameter
const std::string MECH_INSTANCE_VAR = "mech";
const std::string MECH_NODECOUNT_VAR = "node_count";

/// create new function for FUNCTION or PROCEDURE block
void create_function_for_node(ast::Block& node);

/// create new InstanceStruct
std::shared_ptr<ast::InstanceStruct> create_instance_struct();

public:
CodegenLLVMHelperVisitor() = default;
CodegenLLVMHelperVisitor(int vector_width)
: vector_width(vector_width){};

const InstanceVarHelper& get_instance_var_helper() {
return instance_var_helper;
}

/// run visitor and return code generation functions
CodegenFunctionVector get_codegen_functions(const ast::Program& node);
Expand Down
39 changes: 36 additions & 3 deletions src/codegen/llvm/codegen_llvm_visitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
*************************************************************************/

#include "codegen/llvm/codegen_llvm_visitor.hpp"
#include "codegen/llvm/codegen_llvm_helper_visitor.hpp"

#include "ast/all.hpp"
#include "visitors/rename_visitor.hpp"
Expand Down Expand Up @@ -79,6 +78,8 @@ llvm::Type* CodegenLLVMVisitor::get_codegen_var_type(const ast::CodegenVarType&
return llvm::Type::getInt32Ty(*context);
case ast::AstNodeType::VOID:
return llvm::Type::getVoidTy(*context);
// TODO :: George/Ioannis : Here we have to also return INSTANCE_STRUCT type
// as it is used as an argument to nrn_state function
default:
throw std::runtime_error("Error: expecting a type in CodegenVarType node\n");
}
Expand Down Expand Up @@ -556,8 +557,13 @@ void CodegenLLVMVisitor::visit_program(const ast::Program& node) {
// - convert function and procedure blocks into CodegenFunctions
// - gather information about AST. For now, information about functions
// and procedures is used only.
CodegenLLVMHelperVisitor v;
CodegenLLVMHelperVisitor v{vector_width};
const auto& functions = v.get_codegen_functions(node);
instance_var_helper = v.get_instance_var_helper();

// TODO :: George / Ioannis :: before emitting procedures, we have
// to emmit INSTANCE_STRUCT type as it's used as an argument.
// Currently it's done in node.visit_children which is late.

// For every function, generate its declaration. Thus, we can look up
// `llvm::Function` in the symbol table in the module.
Expand Down Expand Up @@ -603,6 +609,16 @@ void CodegenLLVMVisitor::visit_var_name(const ast::VarName& node) {
if (!identifier->is_name() && !identifier->is_indexed_name())
throw std::runtime_error("Error: Unsupported variable type");

// TODO :: George :: here instance_var_helper can be used to query
// variable type and it's index into structure
auto name = node.get_node_name();

auto codegen_var_with_type = instance_var_helper.get_variable(name);
auto codegen_var_index = instance_var_helper.get_variable_index(name);
// this will be INTEGER or DOUBLE
auto var_type = codegen_var_with_type->get_type()->get_type();
auto is_pointer = codegen_var_with_type->get_is_pointer();

llvm::Value* ptr;
if (identifier->is_name())
ptr = lookup(node.get_node_name());
Expand All @@ -620,7 +636,24 @@ void CodegenLLVMVisitor::visit_var_name(const ast::VarName& node) {
void CodegenLLVMVisitor::visit_instance_struct(const ast::InstanceStruct& node) {
std::vector<llvm::Type*> members;
for (const auto& variable: node.get_codegen_vars()) {
members.push_back(get_default_fp_ptr_type());
// TODO :: Ioannis / George :: we have now double*, int*, double and int
// variables in the instance structure. Each variable is of type
// ast::CodegenVarWithType. So we can query variable type and if
// it's pointer.
auto is_pointer = variable->get_is_pointer();
auto type = variable->get_type()->get_type();

// todo : clean up ?
if (type == ast::AstNodeType::DOUBLE) {
auto llvm_type = is_pointer ? get_default_fp_ptr_type() : get_default_fp_type();
members.push_back(llvm_type);
} else {
if (is_pointer) {
members.push_back(llvm::Type::getInt32PtrTy(*context));
} else {
members.push_back(llvm::Type::getInt32Ty(*context));
}
}
}

llvm_struct = llvm::StructType::create(*context, mod_filename + "_Instance");
Expand Down
8 changes: 8 additions & 0 deletions src/codegen/llvm/codegen_llvm_visitor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <ostream>
#include <string>

#include "codegen/llvm/codegen_llvm_helper_visitor.hpp"
#include "symtab/symbol_table.hpp"
#include "utils/logger.hpp"
#include "visitors/ast_visitor.hpp"
Expand Down Expand Up @@ -56,6 +57,8 @@ class CodegenLLVMVisitor: public visitor::ConstAstVisitor {
std::string output_dir;

private:
InstanceVarHelper instance_var_helper;

std::unique_ptr<llvm::LLVMContext> context = std::make_unique<llvm::LLVMContext>();

std::unique_ptr<llvm::Module> module = std::make_unique<llvm::Module>(mod_filename, *context);
Expand All @@ -79,6 +82,9 @@ class CodegenLLVMVisitor: public visitor::ConstAstVisitor {
// Use 32-bit floating-point type if true. Otherwise, use deafult 64-bit.
bool use_single_precision;

// explicit vectorisation width
int vector_width;

// LLVM mechanism struct
llvm::StructType* llvm_struct;

Expand All @@ -100,11 +106,13 @@ class CodegenLLVMVisitor: public visitor::ConstAstVisitor {
CodegenLLVMVisitor(const std::string& mod_filename,
const std::string& output_dir,
bool opt_passes,
int vector_width = 1,
bool use_single_precision = false)
: mod_filename(mod_filename)
, output_dir(output_dir)
, opt_passes(opt_passes)
, use_single_precision(use_single_precision)
, vector_width(vector_width)
, builder(*context)
, fpm(module.get()) {}

Expand Down
Loading

0 comments on commit 966841b

Please sign in to comment.