From 2c376eab77eb677ef94093055f56bd1470de24dc Mon Sep 17 00:00:00 2001 From: Luc Grosheintz Date: Wed, 11 Sep 2024 13:55:03 +0200 Subject: [PATCH] Implement CONSTRUCTOR/DESTRUCTOR. (#1424) --- src/codegen/codegen_neuron_cpp_visitor.cpp | 58 ++++++++++++++++++++-- src/codegen/codegen_neuron_cpp_visitor.hpp | 4 ++ test/usecases/CMakeLists.txt | 1 + test/usecases/constructor/art_ctor.mod | 17 +++++++ test/usecases/constructor/ctor.mod | 17 +++++++ test/usecases/constructor/test_ctor.py | 36 ++++++++++++++ 6 files changed, 129 insertions(+), 4 deletions(-) create mode 100644 test/usecases/constructor/art_ctor.mod create mode 100644 test/usecases/constructor/ctor.mod create mode 100644 test/usecases/constructor/test_ctor.py diff --git a/src/codegen/codegen_neuron_cpp_visitor.cpp b/src/codegen/codegen_neuron_cpp_visitor.cpp index e098e79f0..e4759b446 100644 --- a/src/codegen/codegen_neuron_cpp_visitor.cpp +++ b/src/codegen/codegen_neuron_cpp_visitor.cpp @@ -1120,10 +1120,16 @@ void CodegenNeuronCppVisitor::print_mechanism_register() { "_pointtype = point_register_mech({}, _hoc_create_pnt, _hoc_destroy_pnt, " "_member_func);", register_mech_args); + + if (info.destructor_node) { + printer->fmt_line("register_destructor({});", + method_name(naming::NRN_DESTRUCTOR_METHOD)); + } } else { printer->fmt_line("register_mech({});", register_mech_args); } + if (info.thread_callback_register) { printer->fmt_line("_extcall_thread.resize({});", info.thread_data_index + 1); printer->fmt_line("thread_mem_init(_extcall_thread.data());"); @@ -1560,21 +1566,55 @@ void CodegenNeuronCppVisitor::print_nrn_jacob() { } -/// TODO: Edit for NEURON +void CodegenNeuronCppVisitor::print_callable_preamble_from_prop() { + printer->add_line("Datum* _ppvar = _nrn_mechanism_access_dparam(prop);"); + printer->add_line("_nrn_mechanism_cache_instance _lmc{prop};"); + printer->add_line("const size_t id = 0;"); + + printer->fmt_line("auto inst = make_instance_{}(_lmc);", info.mod_suffix); + if (!info.artificial_cell) { + printer->fmt_line("auto node_data = make_node_data_{}(prop);", info.mod_suffix); + } + + if (!codegen_thread_variables.empty()) { + printer->fmt_line("auto _thread_vars = {}({}_global.thread_data);", + thread_variables_struct(), + info.mod_suffix); + } + + printer->add_newline(); +} + + void CodegenNeuronCppVisitor::print_nrn_constructor() { - return; + if (info.constructor_node) { + printer->fmt_push_block("void {}(Prop* prop)", method_name(naming::NRN_CONSTRUCTOR_METHOD)); + + print_callable_preamble_from_prop(); + + auto block = info.constructor_node->get_statement_block(); + print_statement_block(*block, false, false); + + printer->pop_block(); + } } void CodegenNeuronCppVisitor::print_nrn_destructor() { - printer->fmt_push_block("void {}(Prop* _prop)", method_name(naming::NRN_DESTRUCTOR_METHOD)); - printer->add_line("Datum* _ppvar = _nrn_mechanism_access_dparam(_prop);"); + printer->fmt_push_block("void {}(Prop* prop)", method_name(naming::NRN_DESTRUCTOR_METHOD)); + print_callable_preamble_from_prop(); for (const auto& rv: info.random_variables) { printer->fmt_line("nrnran123_deletestream((nrnran123_State*) {});", get_variable_name(get_name(rv), false)); } + + if (info.destructor_node) { + auto block = info.destructor_node->get_statement_block(); + print_statement_block(*block, false, false); + } + printer->pop_block(); } @@ -1699,6 +1739,15 @@ void CodegenNeuronCppVisitor::print_nrn_alloc() { method_name(naming::NRN_DESTRUCTOR_METHOD)); } + if (info.point_process || info.artificial_cell) { + printer->fmt_push_block("if(!nrn_point_prop_)"); + + if (info.constructor_node) { + printer->fmt_line("{}(_prop);", method_name(naming::NRN_CONSTRUCTOR_METHOD)); + } + printer->pop_block(); + } + printer->pop_block(); } @@ -2089,6 +2138,7 @@ void CodegenNeuronCppVisitor::print_codegen_routines() { print_prcellstate_macros(); print_mechanism_info(); print_data_structures(true); + print_nrn_constructor(); print_nrn_destructor(); print_nrn_alloc(); print_function_prototypes(); diff --git a/src/codegen/codegen_neuron_cpp_visitor.hpp b/src/codegen/codegen_neuron_cpp_visitor.hpp index 2404d68f4..825173c89 100644 --- a/src/codegen/codegen_neuron_cpp_visitor.hpp +++ b/src/codegen/codegen_neuron_cpp_visitor.hpp @@ -491,6 +491,10 @@ class CodegenNeuronCppVisitor: public CodegenCppVisitor { */ void print_nrn_constructor() override; + /** + * Print the set of common variables from a `Prop` only. + */ + void print_callable_preamble_from_prop(); /** * Print nrn_destructor function definition diff --git a/test/usecases/CMakeLists.txt b/test/usecases/CMakeLists.txt index 362982fce..cc08856bf 100644 --- a/test/usecases/CMakeLists.txt +++ b/test/usecases/CMakeLists.txt @@ -2,6 +2,7 @@ set(NMODL_USECASE_DIRS solve constant electrode_current + constructor function procedure global diff --git a/test/usecases/constructor/art_ctor.mod b/test/usecases/constructor/art_ctor.mod new file mode 100644 index 000000000..9b89aed76 --- /dev/null +++ b/test/usecases/constructor/art_ctor.mod @@ -0,0 +1,17 @@ +NEURON { + ARTIFICIAL_CELL art_ctor + GLOBAL ctor_calls, dtor_calls +} + +ASSIGNED { + ctor_calls + dtor_calls +} + +CONSTRUCTOR { + ctor_calls = ctor_calls + 1 +} + +DESTRUCTOR { + dtor_calls = dtor_calls + 1 +} diff --git a/test/usecases/constructor/ctor.mod b/test/usecases/constructor/ctor.mod new file mode 100644 index 000000000..9d4c5dc5d --- /dev/null +++ b/test/usecases/constructor/ctor.mod @@ -0,0 +1,17 @@ +NEURON { + POINT_PROCESS ctor + GLOBAL ctor_calls, dtor_calls +} + +ASSIGNED { + ctor_calls + dtor_calls +} + +CONSTRUCTOR { + ctor_calls = ctor_calls + 1 +} + +DESTRUCTOR { + dtor_calls = dtor_calls + 1 +} diff --git a/test/usecases/constructor/test_ctor.py b/test/usecases/constructor/test_ctor.py new file mode 100644 index 000000000..1a71626dc --- /dev/null +++ b/test/usecases/constructor/test_ctor.py @@ -0,0 +1,36 @@ +import gc + +from neuron import h, gui + + +def allocate_props(n_instances, mech_name): + s = h.Section() + s.nseg = 1 + + for _ in range(n_instances): + getattr(h, mech_name)(s(0.5)) + + +def assert_equal(actual, expected): + assert actual == expected, f"{actual} != {expected}" + + +def check_ctor(n_instances, mech_name): + allocate_props(n_instances, mech_name) + gc.collect() + + assert_equal(getattr(h, f"ctor_calls_{mech_name}"), n_instances) + assert_equal(getattr(h, f"dtor_calls_{mech_name}"), n_instances) + + +def test_ctor(): + check_ctor(12, "ctor") + + +def test_art_ctor(): + check_ctor(32, "art_ctor") + + +if __name__ == "__main__": + test_ctor() + test_art_ctor()