User Guide
inline_visitor.hpp
Go to the documentation of this file.
1 /*
2  * Copyright 2023 Blue Brain Project, EPFL.
3  * See the top-level LICENSE file for details.
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #pragma once
9 
10 /**
11  * \file
12  * \brief \copybrief nmodl::visitor::InlineVisitor
13  */
14 
15 #include <map>
16 #include <stack>
17 
18 #include "symtab/decl.hpp"
19 #include "visitors/ast_visitor.hpp"
20 
21 namespace nmodl {
22 namespace visitor {
23 
24 /**
25  * \addtogroup visitor_classes
26  * \{
27  */
28 
29 /**
30  * \class InlineVisitor
31  * \brief %Visitor to inline local procedure and function calls
32  *
33  * Motivation: Mod files often have function and procedure calls. Procedure
34  * typically has use of range variables like:
35  *
36  * \code{.mod}
37  * NEURON {
38  * RANGE tau, alpha, beta
39  * }
40  *
41  * DERIVATIVE states() {
42  * ...
43  * rates()
44  * alpha = tau + beta
45  * }
46  *
47  * PROCEDURE rates() {
48  * tau = xx * 0.12 * some_var
49  * beta = yy * 0.11
50  * }
51  * \endcode
52  *
53  * One can reduce the memory bandwidth pressure by inlining rates() and then
54  * replacing tau and beta with local variables. Many mod files from BlueBrain
55  * and other open source projects could be hugely benefited by inlining pass.
56  * The goal of this pass is to implement procedure and function inlining in
57  * the nmodl programs. After inlining we should be able to translate AST back
58  * to "transformed" nmodl program which can be compiled and run by NEURON or
59  * CoreNEURON simulator.
60  *
61  * Implementation Notes:
62  * - We start with iterating statements in the statement block
63  * - Function or procedure calls are treated in same way
64  * - We visit the childrens of a statement and "trap" function calls
65  * - New ast node WrappedExpression has been added to YAML specification
66  * to facilitate easily "capturing" function calls. For example, function
67  * call can appear in argument, condition, binary expression, unary expression
68  * etc. When inlining happens, we have to replace FunctionCall node with new
69  * variable name. As visitor receives the pointer to function call, we can't
70  * replace the node. Hence WrappedExpression node has added. With this we
71  * can just override visit function of a single node and then can easily
72  * replace the expression to whatever the result will be
73  * - In case of lag and table statement inlining is disabled (haven't carefully
74  * looked into side effects)
75  * - Inlining pass needs to be run after symbol table pass
76  * - Inlining is done in recursive way i.e. if A() calls B() then first B()
77  * is checked for function calls/inlining and so on. This is dony by looking
78  * ast node of B() in symbol table and visiting it recursively
79  * - Procedure calls are typically standalone statements i.e. expression statements.
80  * But note that the nmodl grammar allows procedures to appear as part of expression.
81  * Procedures always return 0
82  * - Standalone procedure or function calls are replaced with the procedure/function body
83  *
84  * Examples:
85  *
86  * \code{.mod}
87  * PROCEDURE rates_1() {
88  * LOCAL x
89  * rates_2(23.1)
90  * }
91  *
92  * PROCEDURE rates_2(y) {
93  * LOCAL x
94  * x = 21.1*v+y
95  * }
96  * \endcode
97  *
98  * The result of inlining :
99  *
100  * \code{.mod}
101  * PROCEDURE rates_1() {
102  * LOCAL x, rates_2_in_0
103  * {
104  * LOCAL x, y_in_0
105  * y_in_0 = 23.1
106  * x = 21.1*v+y_in_0
107  * rates_2_in_0 = 0
108  * }
109  * }
110  *
111  * PROCEDURE rates_2(y) {
112  * LOCAL x
113  * x = 21.1*v+y
114  * }
115  * \endcode
116  *
117  * - Arguments for function call are copied into local variables
118  * - Local statement gets added to callee block (if doesn't exist)
119  * - Procedure body gets appended with extra assignment statement with variable
120  * used for returning value.
121  *
122  * \todo
123  * - Recursive function calls are not supported and need to add checks to avoid stack explosion
124  * - Currently we rename variables more than necessary, this could be improved [low priority]
125  * - Function calls as part of an argument of function call itself are not completely inlined [low
126  * priority]
127  * - Symbol table pass needs to be re-run in order to update the definitions/usage
128  * - Location of symbol/nodes after inlining still points to old nodes
129  */
130 class InlineVisitor: public AstVisitor {
131  private:
132  /// statement block containing current function call
134 
135  /// statement containing current function call
136  std::shared_ptr<ast::Statement> caller_statement;
137 
138  /// symbol table for program node
140 
141  /// statement blocks in call hierarchy
142  std::stack<ast::StatementBlock*> statementblock_stack;
143 
144  /// statements being executed in call hierarchy
145  std::stack<std::shared_ptr<ast::Statement>> statement_stack;
146 
147  /// map to track the statements being replaces (typically for procedure calls)
148  std::map<std::shared_ptr<ast::Statement>, ast::ExpressionStatement*> replaced_statements;
149 
150  /// map to track statements being prepended before function call (typically for function calls)
151  std::map<std::shared_ptr<ast::Statement>,
152  std::vector<std::shared_ptr<ast::ExpressionStatement>>>
154 
155  /// map to track replaced function calls (typically for function calls)
156  std::map<ast::FunctionCall*, std::string> replaced_fun_calls;
157 
158  /// variables currently being renamed and their count (for renaming)
159  std::map<std::string, int> inlined_variables;
160 
161  /// true if given statement block can be inlined
162  static bool can_inline_block(const ast::StatementBlock& block);
163 
164  /// true if statement can be replaced with inlined body
165  /// this is possible for standalone function/procedure call as statement
166  bool can_replace_statement(const std::shared_ptr<ast::Statement>& statement);
167 
168  /**
169  * inline function/procedure into caller block
170  * @param callee : ast node representing function/procedure definition being called
171  * @param node : function/procedure call node
172  * @param caller : statement block containing function call
173  */
174  bool inline_function_call(ast::Block& callee,
175  ast::FunctionCall& node,
176  ast::StatementBlock& caller);
177 
178  /// add assignment statements into given statement block to inline arguments
179  void inline_arguments(ast::StatementBlock& inlined_block,
180  const ast::ArgumentVector& callee_parameters,
181  const ast::ExpressionVector& caller_expressions);
182 
183  /// add assignment statement at end of block (to use as a return statement
184  /// in case of procedure blocks)
185  static void add_return_variable(ast::StatementBlock& block, std::string& varname);
186 
187  public:
188  InlineVisitor() = default;
189 
190  void visit_function_call(ast::FunctionCall& node) override;
191 
192  void visit_statement_block(ast::StatementBlock& node) override;
193 
195 
196  void visit_program(ast::Program& node) override;
197 };
198 
199 /** \} */ // end of visitor_classes
200 
201 } // namespace visitor
202 } // namespace nmodl
nmodl::visitor::InlineVisitor::inline_arguments
void inline_arguments(ast::StatementBlock &inlined_block, const ast::ArgumentVector &callee_parameters, const ast::ExpressionVector &caller_expressions)
add assignment statements into given statement block to inline arguments
Definition: inline_visitor.cpp:90
nmodl::visitor::InlineVisitor::inlined_statements
std::map< std::shared_ptr< ast::Statement >, std::vector< std::shared_ptr< ast::ExpressionStatement > > > inlined_statements
map to track statements being prepended before function call (typically for function calls)
Definition: inline_visitor.hpp:153
nmodl::visitor::InlineVisitor::inlined_variables
std::map< std::string, int > inlined_variables
variables currently being renamed and their count (for renaming)
Definition: inline_visitor.hpp:159
nmodl
encapsulates code generation backend implementations
Definition: ast_common.hpp:26
nmodl::visitor::InlineVisitor::caller_block
ast::StatementBlock * caller_block
statement block containing current function call
Definition: inline_visitor.hpp:133
nmodl::visitor::InlineVisitor::can_replace_statement
bool can_replace_statement(const std::shared_ptr< ast::Statement > &statement)
true if statement can be replaced with inlined body this is possible for standalone function/procedur...
Definition: inline_visitor.cpp:66
nmodl::visitor::InlineVisitor::statementblock_stack
std::stack< ast::StatementBlock * > statementblock_stack
statement blocks in call hierarchy
Definition: inline_visitor.hpp:142
nmodl::visitor::InlineVisitor::inline_function_call
bool inline_function_call(ast::Block &callee, ast::FunctionCall &node, ast::StatementBlock &caller)
inline function/procedure into caller block
Definition: inline_visitor.cpp:128
nmodl::visitor::InlineVisitor::can_inline_block
static bool can_inline_block(const ast::StatementBlock &block)
true if given statement block can be inlined
Definition: inline_visitor.cpp:24
nmodl::ast::ExpressionStatement
TODO.
Definition: expression_statement.hpp:38
nmodl::ast::Block
Base class for all block scoped nodes.
Definition: block.hpp:41
nmodl::visitor::InlineVisitor::replaced_statements
std::map< std::shared_ptr< ast::Statement >, ast::ExpressionStatement * > replaced_statements
map to track the statements being replaces (typically for procedure calls)
Definition: inline_visitor.hpp:148
nmodl::visitor::InlineVisitor::visit_function_call
void visit_function_call(ast::FunctionCall &node) override
visit node of type ast::FunctionCall
Definition: inline_visitor.cpp:201
nmodl::ast::ArgumentVector
std::vector< std::shared_ptr< Argument > > ArgumentVector
Definition: ast_decl.hpp:312
nmodl::ast::FunctionCall
TODO.
Definition: function_call.hpp:38
nmodl::visitor::InlineVisitor
Visitor to inline local procedure and function calls
Definition: inline_visitor.hpp:130
nmodl::visitor::InlineVisitor::visit_program
void visit_program(ast::Program &node) override
visit node of type ast::Program
Definition: inline_visitor.cpp:320
nmodl::visitor::AstVisitor
Concrete visitor for all AST classes.
Definition: ast_visitor.hpp:37
nmodl::visitor::InlineVisitor::statement_stack
std::stack< std::shared_ptr< ast::Statement > > statement_stack
statements being executed in call hierarchy
Definition: inline_visitor.hpp:145
nmodl::symtab::SymbolTable
Represent symbol table for a NMODL block.
Definition: symbol_table.hpp:57
nmodl::visitor::InlineVisitor::InlineVisitor
InlineVisitor()=default
nmodl::ast::StatementBlock
Represents block encapsulating list of statements.
Definition: statement_block.hpp:53
nmodl::ast::ExpressionVector
std::vector< std::shared_ptr< Expression > > ExpressionVector
Definition: ast_decl.hpp:299
nmodl::visitor::InlineVisitor::add_return_variable
static void add_return_variable(ast::StatementBlock &block, std::string &varname)
add assignment statement at end of block (to use as a return statement in case of procedure blocks)
Definition: inline_visitor.cpp:50
nmodl::visitor::InlineVisitor::visit_wrapped_expression
void visit_wrapped_expression(ast::WrappedExpression &node) override
Visit all wrapped expressions which can contain function calls.
Definition: inline_visitor.cpp:305
nmodl::visitor::InlineVisitor::program_symtab
symtab::SymbolTable const * program_symtab
symbol table for program node
Definition: inline_visitor.hpp:139
nmodl::visitor::InlineVisitor::caller_statement
std::shared_ptr< ast::Statement > caller_statement
statement containing current function call
Definition: inline_visitor.hpp:136
nmodl::visitor::InlineVisitor::replaced_fun_calls
std::map< ast::FunctionCall *, std::string > replaced_fun_calls
map to track replaced function calls (typically for function calls)
Definition: inline_visitor.hpp:156
nmodl::ast::Program
Represents top level AST node for whole NMODL input.
Definition: program.hpp:39
decl.hpp
Forward declarations of symbols in namespace nmodl::symtab.
nmodl::ast::WrappedExpression
Wrap any other expression type.
Definition: wrapped_expression.hpp:38
nmodl::visitor::InlineVisitor::visit_statement_block
void visit_statement_block(ast::StatementBlock &node) override
visit node of type ast::StatementBlock
Definition: inline_visitor.cpp:234
ast_visitor.hpp
Concrete visitor for all AST classes.