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