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

Initial attempt at implementing VERBATIM. #1364

Merged
merged 23 commits into from
Nov 4, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 0 additions & 1 deletion src/codegen/codegen_coreneuron_cpp_visitor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,6 @@ class CodegenCoreneuronCppVisitor: public CodegenCppVisitor {
*/
std::string process_verbatim_token(const std::string& token);


/**
* Check if variable is qualified as constant
* \param name The name of variable
Expand Down
1 change: 1 addition & 0 deletions src/codegen/codegen_cpp_visitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "ast/all.hpp"
#include "codegen/codegen_helper_visitor.hpp"
#include "codegen/codegen_utils.hpp"
#include "utils/string_utils.hpp"
#include "visitors/defuse_analyze_visitor.hpp"
#include "visitors/rename_visitor.hpp"
#include "visitors/symtab_visitor.hpp"
Expand Down
4 changes: 4 additions & 0 deletions src/codegen/codegen_cpp_visitor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,10 @@ struct ShadowUseStatement {
std::string rhs;
};

inline std::string get_name(ast::Ast const* sym) {
JCGoran marked this conversation as resolved.
Show resolved Hide resolved
return sym->get_node_name();
}

inline std::string get_name(const std::shared_ptr<symtab::Symbol>& sym) {
return sym->get_name();
}
Expand Down
18 changes: 18 additions & 0 deletions src/codegen/codegen_naming.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -176,9 +176,27 @@ static constexpr char NRN_WATCH_CHECK_METHOD[] = "nrn_watch_check";
/// verbatim name of the variable for nrn thread arguments
static constexpr char THREAD_ARGS[] = "_threadargs_";

/// verbatim name of the variable for nrn thread arguments, sometimes with trailing comma
static constexpr char THREAD_ARGS_COMMA[] = "_threadargscomma_";

/// verbatim name of the variable for nrn thread arguments in prototype
static constexpr char THREAD_ARGS_PROTO[] = "_threadargsproto_";

/// verbatim name of the variable for nrn thread arguments in prototype and a comma
static constexpr char THREAD_ARGS_PROTO_COMMA[] = "_threadargsprotocomma_";

/// variation of `_threadargs_` for "internal" functions.
static constexpr char INTERNAL_THREAD_ARGS[] = "_internalthreadargs_";

/// variation of `_threadargs_` for "internal" functions, with comma (maybe).
static constexpr char INTERNAL_THREAD_ARGS_COMMA[] = "_internalthreadargscomma_";

/// variation of `_threadargsproto_` for "internal" functions.
static constexpr char INTERNAL_THREAD_ARGS_PROTO[] = "_internalthreadargsproto_";

/// variation of `_threadargsproto_` for "internal" functions, possibly with comma.
static constexpr char INTERNAL_THREAD_ARGS_PROTO_COMMA[] = "_internalthreadargsprotocomma_";

/// prefix for ion variable
static constexpr char ION_VARNAME_PREFIX[] = "ion_";

Expand Down
203 changes: 202 additions & 1 deletion src/codegen/codegen_neuron_cpp_visitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@
#include <stdexcept>

#include "ast/all.hpp"
#include "ast/procedure_block.hpp"
#include "codegen/codegen_cpp_visitor.hpp"
#include "codegen/codegen_utils.hpp"
#include "codegen_naming.hpp"
#include "config/config.h"
#include "parser/c11_driver.hpp"
#include "solver/solver.hpp"
#include "utils/string_utils.hpp"
#include "visitors/rename_visitor.hpp"
Expand Down Expand Up @@ -610,6 +612,21 @@
}


CodegenCppVisitor::ParamVector CodegenNeuronCppVisitor::internalthreadargs_parameters() {
return internal_method_parameters();
}


CodegenCppVisitor::ParamVector CodegenNeuronCppVisitor::threadargs_parameters() {
return {{"", "Memb_list*", "", "_ml"},
{"", "size_t", "", "_iml"},
{"", "Datum*", "", "_ppvar"},
{"", "Datum*", "", "_thread"},
{"", "double*", "", "_globals"},
{"", "NrnThread*", "", "_nt"}};
}


/// TODO: Edit for NEURON
std::string CodegenNeuronCppVisitor::nrn_thread_arguments() const {
return {};
Expand All @@ -632,8 +649,176 @@
}


/** Map of the non-(global/top-local) LOCAL variables.
*
* The map associates the name in the MOD file, e.g. `a` with
* the current name of that LOCAL variable, e.g. `a_r_4`.
*
* auto map = get_nonglobal_local_variable_names();
* assert map["a"] == "a_r_4";
*
* The two names can differ, because an early pass makes all
* names unique by renaming local variables.
*/
std::unordered_map<std::string, std::string> get_nonglobal_local_variable_names(
JCGoran marked this conversation as resolved.
Show resolved Hide resolved
const symtab::SymbolTable& symtab) {
if (symtab.global_scope()) {
return {};
}

auto local_variables = symtab.get_variables(NmodlType::local_var);
auto parent_symtab = symtab.get_parent_table();
if (parent_symtab == nullptr) {
throw std::runtime_error(
"Internal NMODL error: non top-level symbol table doesn't have a parent.");
}

auto variable_names = get_nonglobal_local_variable_names(*parent_symtab);

for (const auto& symbol: local_variables) {
auto status = symbol->get_status();
bool is_renamed = (status & symtab::syminfo::Status::renamed) !=
symtab::syminfo::Status::empty;
auto current_name = symbol->get_name();
auto mod_name = is_renamed ? symbol->get_original_name() : current_name;

variable_names[mod_name] = current_name;
}

return variable_names;
}


std::vector<std::string> CodegenNeuronCppVisitor::print_verbatim_setup(
JCGoran marked this conversation as resolved.
Show resolved Hide resolved
const ast::Verbatim& node,
const std::string& verbatim) {
// Note, the logic for reducing the number of macros printed, aims to
// improve legibility of the generated code by reducing number of lines of
// code. It would be correct to print all macros, because that's
// essentially what NOCMODL does. Therefore, the logic isn't sharp (and
// doesn't have to be).

std::vector<std::string> macros_defined;
auto print_macro = [this, &verbatim, &macros_defined](const std::string& macro_name,
const std::string& macro_value) {
if (verbatim.find(macro_name) != std::string::npos) {
printer->fmt_line("#define {} {}", macro_name, macro_value);
macros_defined.push_back(macro_name);
}
};

printer->add_line("// Setup for VERBATIM");
for (const auto& var: codegen_float_variables) {
auto name = get_name(var);
print_macro(name, get_variable_name(name));
}

for (const auto& var: codegen_int_variables) {
auto name = get_name(var);
std::string macro_value = get_variable_name(name);
print_macro(name, macro_value);
if (verbatim.find("_p_" + name) != std::string::npos) {
print_macro("_p_" + name, get_pointer_name(name));
}
}

for (const auto& var: codegen_global_variables) {
auto name = get_name(var);
print_macro(name, get_variable_name(name));
}

for (const auto& var: codegen_thread_variables) {
auto name = get_name(var);
print_macro(name, get_variable_name(name));
}

for (const auto& func: info.functions) {
auto name = get_name(func);
print_macro(name, method_name(name));
print_macro(fmt::format("_l{}", name), fmt::format("ret_{}", name));
}

for (const auto& proc: info.procedures) {
auto name = get_name(proc);
print_macro(name, method_name(name));
}


auto symtab = node.get_parent()->get_symbol_table();
auto locals = get_nonglobal_local_variable_names(*symtab);
for (const auto& [mod_name, current_name]: locals) {
print_macro(fmt::format("_l{}", mod_name), get_variable_name(current_name));
}

print_macro(naming::NTHREAD_T_VARIABLE, "nt->_t");
print_macro("_nt", "nt");
print_macro("_tqitem", "tqitem");

auto print_args_macro = [this, print_macro](const std::string& macro_basename,

Check warning on line 757 in src/codegen/codegen_neuron_cpp_visitor.cpp

View workflow job for this annotation

GitHub Actions / { "flag_warnings": "ON", "os": "ubuntu-22.04", "sanitizer": "undefined" }

lambda capture 'this' is not used [-Wunused-lambda-capture]
JCGoran marked this conversation as resolved.
Show resolved Hide resolved
const ParamVector& params) {
print_macro("_" + macro_basename + "_", get_arg_str(params));
print_macro("_" + macro_basename + "comma_", get_arg_str(params) + ",");
print_macro("_" + macro_basename + "proto_", get_parameter_str(params));
print_macro("_" + macro_basename + "protocomma_", get_parameter_str(params) + ",");
};

print_args_macro("internalthreadargs", internalthreadargs_parameters());
print_args_macro("threadargs", threadargs_parameters());

return macros_defined;
}


void CodegenNeuronCppVisitor::print_verbatim_cleanup(
const std::vector<std::string>& macros_defined) {
for (const auto& macro: macros_defined) {
printer->fmt_line("#undef {}", macro);
}
printer->add_line("// End of cleanup for VERBATIM");
}


std::string CodegenNeuronCppVisitor::process_verbatim_text(const std::string& verbatim) {
JCGoran marked this conversation as resolved.
Show resolved Hide resolved
parser::CDriver driver;
driver.scan_string(verbatim);
auto tokens = driver.all_tokens();
std::string result;
for (size_t i = 0; i < tokens.size(); i++) {
auto token = tokens[i];

// check if we have function call in the verbatim block where
// function is defined in the same mod file
if (program_symtab->is_method_defined(token) && tokens[i + 1] == "(") {
result += token + "(";
if (tokens[i + 2] == naming::THREAD_ARGS) {
result += naming::INTERNAL_THREAD_ARGS;
} else if (tokens[i + 2] == naming::THREAD_ARGS_COMMA) {
result += naming::INTERNAL_THREAD_ARGS_COMMA;
} else {
result += tokens[i + 2];
}

i += 2;
} else {
result += token;
}
}
return result;
}


void CodegenNeuronCppVisitor::visit_verbatim(const Verbatim& node) {
// Not implemented yet.
const auto& verbatim_code = node.get_statement()->eval();
auto massaged_verbatim = process_verbatim_text(verbatim_code);

auto macros_defined = print_verbatim_setup(node, massaged_verbatim);
printer->add_line("// Begin VERBATIM");
const auto& lines = stringutils::split_string(massaged_verbatim, '\n');
for (const auto& line: lines) {
printer->add_line(line);
}
printer->add_line("// End VERBATIM");
print_verbatim_cleanup(macros_defined);
}


Expand Down Expand Up @@ -793,6 +978,20 @@
}


std::string CodegenNeuronCppVisitor::get_pointer_name(const std::string& name) const {
auto name_comparator = [&name](const auto& sym) { return name == get_name(sym); };

auto var =
std::find_if(codegen_int_variables.begin(), codegen_int_variables.end(), name_comparator);

if (var == codegen_int_variables.end()) {
throw std::runtime_error("Only integer variables have a 'pointer name'.");
}
auto position = position_of_int_var(name);
return fmt::format("_ppvar[{}].literal_value<void*>()", position);
}


std::string CodegenNeuronCppVisitor::get_variable_name(const std::string& name,
bool use_instance) const {
std::string varname = update_if_ion_variable_name(name);
Expand Down Expand Up @@ -2460,6 +2659,7 @@
print_point_process_function_definitions();
print_setdata_functions();
print_check_table_entrypoint();
print_top_verbatim_blocks();
print_functors_definitions();
print_global_variables_for_hoc();
print_thread_memory_callbacks();
Expand All @@ -2477,6 +2677,7 @@
print_headers_include();
print_namespace_start();
print_function_prototypes();
print_top_verbatim_blocks();
print_global_variables_for_hoc();
print_function_definitions();
print_mechanism_register();
Expand Down
41 changes: 40 additions & 1 deletion src/codegen/codegen_neuron_cpp_visitor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
#include <string_view>
#include <utility>

#include "ast/function_block.hpp"
#include "ast/procedure_block.hpp"
#include "codegen/codegen_cpp_visitor.hpp"
#include "codegen/codegen_info.hpp"
#include "codegen/codegen_naming.hpp"
Expand Down Expand Up @@ -343,6 +345,14 @@ class CodegenNeuronCppVisitor: public CodegenCppVisitor {
const ParamVector external_method_parameters(bool table = false) noexcept override;


/** The parameters for the four macros `_internalthreadargs*_`. */
ParamVector internalthreadargs_parameters();


/** The parameters for the four macros `_threadargs*_`. */
ParamVector threadargs_parameters();


/**
* Arguments for "_threadargs_" macro in neuron implementation
*/
Expand All @@ -358,6 +368,19 @@ class CodegenNeuronCppVisitor: public CodegenCppVisitor {
const ast::FunctionTableBlock& /* node */) override;


/** Print compatibility macros required for VERBATIM blocks.
*
* Returns the names of all macros introduced.
*/
std::vector<std::string> print_verbatim_setup(const ast::Verbatim& node,
const std::string& verbatim);


/** Print `#undef`s to erase all compatibility macros.
*/
void print_verbatim_cleanup(const std::vector<std::string>& macros_defined);


/**
* Arguments for register_mech or point_register_mech function
*/
Expand Down Expand Up @@ -462,7 +485,10 @@ class CodegenNeuronCppVisitor: public CodegenCppVisitor {


/**
* Determine variable name in the structure of mechanism properties
* Determine the C++ string to replace variable names with.
*
* Given a variable name such as `ion_cai` or `v`, return the C++ code
* required to get the value.
*
* \param name Variable name that is being printed
* \param use_instance Should the variable be accessed via instance or data array
Expand All @@ -471,6 +497,18 @@ class CodegenNeuronCppVisitor: public CodegenCppVisitor {
*/
std::string get_variable_name(const std::string& name, bool use_instance = true) const override;

/**
* Determine the C++ string to replace pointer names with.
*
* Given a variable name such as `_p_ptr` or `_p_rng`, return the C++ code
* required to get a pointer to `ptr` (or `rng`).
*
* \param name Variable name that is being printed
* \return The C++ string representing the variable.
* thread structure
*/
std::string get_pointer_name(const std::string& name) const;


/****************************************************************************************/
/* Main printing routines for code generation */
Expand Down Expand Up @@ -760,6 +798,7 @@ class CodegenNeuronCppVisitor: public CodegenCppVisitor {
/* Overloaded visitor routines */
/****************************************************************************************/

std::string process_verbatim_text(const std::string& verbatim);
void visit_verbatim(const ast::Verbatim& node) override;
void visit_watch_statement(const ast::WatchStatement& node) override;
void visit_for_netcon(const ast::ForNetcon& node) override;
Expand Down
1 change: 1 addition & 0 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,7 @@ int run_nmodl(int argc, const char* argv[]) {
CLI11_PARSE(app, argc, argv);

std::string simulator_name = neuron_code ? "neuron" : "coreneuron";
verbatim_rename = neuron_code ? false : verbatim_rename;

fs::create_directories(output_dir);
fs::create_directories(scratch_dir);
Expand Down
Loading
Loading