Skip to content
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

Add InstanceStruct test data generation helper and unit test #546

Merged
merged 17 commits into from
Mar 13, 2021
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 19 additions & 6 deletions src/codegen/llvm/codegen_llvm_helper_visitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ namespace codegen {

using namespace fmt::literals;

/// initialize static member variables
const ast::AstNodeType CodegenLLVMHelperVisitor::INTEGER_TYPE = ast::AstNodeType::INTEGER;
const ast::AstNodeType CodegenLLVMHelperVisitor::FLOAT_TYPE = ast::AstNodeType::DOUBLE;
const std::string CodegenLLVMHelperVisitor::NODECOUNT_VAR = "node_count";
const std::string CodegenLLVMHelperVisitor::VOLTAGE_VAR = "voltage";
const std::string CodegenLLVMHelperVisitor::NODE_INDEX_VAR = "node_index";

/**
* \brief Create variable definition statement
*
Expand Down Expand Up @@ -157,7 +164,12 @@ void CodegenLLVMHelperVisitor::create_function_for_node(ast::Block& node) {
auto function = std::make_shared<ast::CodegenFunction>(fun_ret_type, name, arguments, block);
codegen_functions.push_back(function);
}

/**
* \note : Order of variables is not important but we assume all pointers
* are added first and then scalar variables like t, dt, second_order etc.
* This order is assumed when we allocate data for integration testing
* and benchmarking purpose. See CodegenDataHelper::create_data().
*/
std::shared_ptr<ast::InstanceStruct> CodegenLLVMHelperVisitor::create_instance_struct() {
ast::CodegenVarWithTypeVector codegen_vars;

Expand Down Expand Up @@ -186,15 +198,15 @@ std::shared_ptr<ast::InstanceStruct> CodegenLLVMHelperVisitor::create_instance_s
}

// add voltage and node index
add_var_with_type("voltage", FLOAT_TYPE, /*is_pointer=*/1);
add_var_with_type("node_index", INTEGER_TYPE, /*is_pointer=*/1);
add_var_with_type(VOLTAGE_VAR, FLOAT_TYPE, /*is_pointer=*/1);
add_var_with_type(NODE_INDEX_VAR, INTEGER_TYPE, /*is_pointer=*/1);

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

return std::make_shared<ast::InstanceStruct>(codegen_vars);
}
Expand Down Expand Up @@ -510,7 +522,7 @@ void CodegenLLVMHelperVisitor::visit_nrn_state_block(ast::NrnStateBlock& node) {

/// loop constructs : initialization, condition and increment
const auto& initialization = loop_initialization_expression(INDUCTION_VAR);
const auto& condition = create_expression("{} < {}"_format(INDUCTION_VAR, MECH_NODECOUNT_VAR));
const auto& condition = create_expression("{} < {}"_format(INDUCTION_VAR, NODECOUNT_VAR));
const auto& increment = loop_increment_expression(INDUCTION_VAR, vector_width);

/// loop body : initialization + solve blocks
Expand All @@ -524,7 +536,8 @@ void CodegenLLVMHelperVisitor::visit_nrn_state_block(ast::NrnStateBlock& node) {
/// access node index and corresponding voltage
loop_index_statements.push_back(
visitor::create_statement("node_id = node_index[{}]"_format(INDUCTION_VAR)));
loop_body_statements.push_back(visitor::create_statement("v = voltage[node_id]"));
loop_body_statements.push_back(
visitor::create_statement("v = {}[node_id]"_format(VOLTAGE_VAR)));

/// read ion variables
ion_read_statements(BlockType::State,
Expand Down
14 changes: 9 additions & 5 deletions src/codegen/llvm/codegen_llvm_helper_visitor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -112,13 +112,8 @@ class CodegenLLVMHelperVisitor: public visitor::AstVisitor {
/// 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";

/// name of induction variable used in the kernel.
const std::string INDUCTION_VAR = "id";
Expand All @@ -130,6 +125,15 @@ class CodegenLLVMHelperVisitor: public visitor::AstVisitor {
std::shared_ptr<ast::InstanceStruct> create_instance_struct();

public:
/// default integer and float node type
static const ast::AstNodeType INTEGER_TYPE;
static const ast::AstNodeType FLOAT_TYPE;

// node count, voltage and node index variables
static const std::string NODECOUNT_VAR;
static const std::string VOLTAGE_VAR;
static const std::string NODE_INDEX_VAR;

CodegenLLVMHelperVisitor(int vector_width)
: vector_width(vector_width){};

Expand Down
4 changes: 4 additions & 0 deletions src/codegen/llvm/codegen_llvm_visitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,10 @@ llvm::Value* CodegenLLVMVisitor::get_variable_ptr(const ast::VarName& node) {
return ptr;
}

std::shared_ptr<ast::InstanceStruct> CodegenLLVMVisitor::get_instance_struct_ptr() {
return instance_var_helper.instance;
}

void CodegenLLVMVisitor::run_llvm_opt_passes() {
/// run some common optimisation passes that are commonly suggested
fpm.add(llvm::createInstructionCombiningPass());
Expand Down
6 changes: 6 additions & 0 deletions src/codegen/llvm/codegen_llvm_visitor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,12 @@ class CodegenLLVMVisitor: public visitor::ConstAstVisitor {
*/
llvm::Value* get_variable_ptr(const ast::VarName& node);

/**
* Returns shared_ptr to generated ast::InstanceStruct
* \return std::shared_ptr<ast::InstanceStruct>
*/
std::shared_ptr<ast::InstanceStruct> get_instance_struct_ptr();

/**
* Create a function call to an external method
* \param name external method name
Expand Down
5 changes: 3 additions & 2 deletions test/unit/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,9 @@ target_link_libraries(
${NMODL_WRAPPER_LIBS})

if(NMODL_ENABLE_LLVM)
include_directories(${LLVM_INCLUDE_DIRS})
add_executable(testllvm visitor/main.cpp codegen/codegen_llvm_ir.cpp)
include_directories(${LLVM_INCLUDE_DIRS} codegen)
add_executable(testllvm visitor/main.cpp codegen/codegen_llvm_ir.cpp
codegen/codegen_data_helper.cpp codegen/codegen_llvm_instance_struct.cpp)
add_executable(test_llvm_runner visitor/main.cpp codegen/codegen_llvm_execution.cpp)
target_link_libraries(
testllvm
Expand Down
190 changes: 190 additions & 0 deletions test/unit/codegen/codegen_data_helper.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
#include <algorithm>

#include "ast/codegen_var_type.hpp"
#include "codegen/llvm/codegen_llvm_helper_visitor.hpp"

#include "codegen_data_helper.hpp"

namespace nmodl {
namespace codegen {

// scalar variables with default values
const double default_nthread_dt_value = 0.025;
const double default_nthread_t_value = 100.0;
const double default_celsius_value = 34.0;
const int default_second_order_value = 0;

std::vector<double> generate_double_data(size_t initial_value, size_t num_elements) {
std::vector<double> data(num_elements);
for (size_t i = 0; i < num_elements; i++) {
data[i] = initial_value + (i + 1) * 1e-15;
iomaganaris marked this conversation as resolved.
Show resolved Hide resolved
}
return data;
}

std::vector<float> generate_float_data(size_t initial_value, size_t num_elements) {
std::vector<float> data(num_elements);
for (size_t i = 0; i < num_elements; i++) {
data[i] = initial_value + i * 1e-6;
}
return data;
}

std::vector<int> generate_int_data(size_t initial_value, size_t num_elements) {
std::vector<int> data(num_elements);
for (size_t i = 0; i < num_elements; i++) {
data[i] = initial_value + i;
}
return data;
}

void initialize_variable(const std::shared_ptr<ast::CodegenVarWithType>& var,
void* ptr,
size_t initial_value,
size_t num_elements) {
ast::AstNodeType type = var->get_type()->get_type();
const std::string& name = var->get_name()->get_node_name();

// todos : various things one need to take care of here
// - if variable is voltage then initialization range could be -65 to +65
// - if variable is double or float then those could be initialize with
// "some" floating point value between range like 1.0 to 100.0. Note
// it would be nice to have unique values to avoid errors like division
// by zero
// - if variable is integer then initialization range must be between
// 0 and num_elements. In practice, num_elements is number of instances
// of a particular mechanism. This would be <= number of compartments
// in the cell. For now, just initialize integer variables from 0 to
// num_elements - 1.

if (type == ast::AstNodeType::DOUBLE) {
std::vector<double> generated_double_data = generate_double_data(initial_value,
num_elements);
double* data = (double*) ptr;
for (size_t i = 0; i < num_elements; i++) {
data[i] = generated_double_data[i];
}
} else if (type == ast::AstNodeType::FLOAT) {
std::vector<float> generated_float_data = generate_float_data(initial_value, num_elements);
float* data = (float*) ptr;
for (size_t i = 0; i < num_elements; i++) {
data[i] = generated_float_data[i];
}
} else if (type == ast::AstNodeType::INTEGER) {
std::vector<int> generated_int_data = generate_int_data(initial_value, num_elements);
int* data = (int*) ptr;
for (size_t i = 0; i < num_elements; i++) {
data[i] = generated_int_data[i];
}
} else {
throw std::runtime_error("Unhandled data type during initialize_variable");
};
}

CodegenInstanceData CodegenDataHelper::create_data(size_t num_elements, size_t seed) {
const unsigned NBYTE_ALIGNMENT = 64;

const auto& variables = instance->get_codegen_vars();

CodegenInstanceData data;
data.num_elements = num_elements;

// base pointer to instance object
void* base = nullptr;
// max size of each member : pointer / double has maximum size
size_t member_size = std::max(sizeof(double), sizeof(double*));
// allocate instance object with memory alignment
posix_memalign(&base, NBYTE_ALIGNMENT, member_size * variables.size());
data.base_ptr = base;

size_t offset = 0;
void* ptr = base;
size_t variable_index = 0;

for (auto& var: variables) {
// only process until first non-pointer variable
if (!var->get_is_pointer()) {
break;
}

size_t member_size = 0;
ast::AstNodeType type = var->get_type()->get_type();
if (type == ast::AstNodeType::DOUBLE) {
member_size = sizeof(double);
} else if (type == ast::AstNodeType::FLOAT) {
member_size = sizeof(float);
} else if (type == ast::AstNodeType::INTEGER) {
member_size = sizeof(int);
}

void* member;
posix_memalign(&member, NBYTE_ALIGNMENT, member_size * num_elements);
initialize_variable(var, member, variable_index, num_elements);

// copy address at specific location in the struct
memcpy(ptr, &member, sizeof(double*));

data.offsets.push_back(offset);
data.members.push_back(member);

// all pointer types are of same size, so just use double*
offset += sizeof(double*);
ptr = (char*) base + offset;

variable_index++;
}

// we are now switching from pointer type to next member type (e.g. double)
// ideally we should use padding but switching from double* to double should
// already meet alignment requirements
for (auto& var: variables) {
// process only scalar elements
if (var->get_is_pointer()) {
continue;
}
ast::AstNodeType type = var->get_type()->get_type();
const std::string& name = var->get_name()->get_node_name();

// some default values for standard parameters
double value = 0;
if (name == naming::NTHREAD_DT_VARIABLE) {
value = default_nthread_dt_value;
} else if (name == naming::NTHREAD_T_VARIABLE) {
value = default_nthread_t_value;
} else if (name == naming::CELSIUS_VARIABLE) {
value = default_celsius_value;
} else if (name == CodegenLLVMHelperVisitor::NODECOUNT_VAR) {
value = num_elements;
} else if (name == naming::SECOND_ORDER_VARIABLE) {
value = default_second_order_value;
}

if (type == ast::AstNodeType::DOUBLE) {
*((double*) ptr) = value;
data.offsets.push_back(offset);
data.members.push_back(ptr);
offset += sizeof(double);
ptr = (char*) base + offset;
} else if (type == ast::AstNodeType::FLOAT) {
*((float*) ptr) = float(value);
data.offsets.push_back(offset);
data.members.push_back(ptr);
offset += sizeof(float);
ptr = (char*) base + offset;
} else if (type == ast::AstNodeType::INTEGER) {
*((int*) ptr) = int(value);
data.offsets.push_back(offset);
data.members.push_back(ptr);
offset += sizeof(int);
ptr = (char*) base + offset;
} else {
throw std::runtime_error(
"Unhandled type while allocating data in CodegenDataHelper::create_data()");
}
}

return data;
}

} // namespace codegen
} // namespace nmodl
Loading