User Guide
defuse_analyze_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::DefUseAnalyzeVisitor
13  */
14 
15 #include <map>
16 #include <stack>
17 
18 #include "printer/json_printer.hpp"
19 #include "symtab/decl.hpp"
20 #include "visitors/ast_visitor.hpp"
22 
23 
24 namespace nmodl {
25 namespace visitor {
26 
27 /// Represent a state in Def-Use chain
28 enum class DUState {
29  /// global variable is used
30  U,
31  /// global variable is defined
32  D,
33  /// global or local variable is conditionally defined
34  CD,
35  /// local variable is used
36  LU,
37  /// local variable is defined
38  LD,
39  /// state not known
40  UNKNOWN,
41  /// conditional block
43  /// if sub-block
44  IF,
45  /// elseif sub-block
46  ELSEIF,
47  /// else sub-block
48  ELSE,
49  /// variable is not used
50  NONE
51 };
52 
53 /**
54  * Variable type processed by DefUseAnalyzeVisitor
55  *
56  * DUVariableType::Local means that we are looking for LD, LU and CD DUStates, while Global means we
57  * are looking for U, D and CD DUStates.
58  */
59 enum class DUVariableType { Local, Global };
60 
61 std::ostream& operator<<(std::ostream& os, DUState state);
62 
63 /**
64  * \class DUInstance
65  * \brief Represent use of a variable in the statement
66  *
67  * For a given variable, say tau, when we have statement like `a = tau + c + tau`
68  * then def-use is simply `[DUState::U, DUState::U]`. But if we have if-else block
69  * like:
70  *
71  * \code{.mod}
72  * IF (...) {
73  * a = tau
74  * tau = c + d
75  * } ELSE {
76  * tau = b
77  * }
78  * \endcode
79  *
80  * Then to know the effective result, we have to analyze def-use in `IF` and `ELSE`
81  * blocks i.e. if variable is used in any of the if-elseif-else block then it needs
82  * to mark as `DUState::U`. Hence we keep the track of all children in case of
83  * statements like if-else.
84  */
85 class DUInstance {
86  public:
87  /// state of the usage
89 
90  /// usage of variable in case of if like statements
91  std::vector<DUInstance> children;
92 
94  const std::shared_ptr<const ast::BinaryExpression> binary_expression)
95  : state(state)
97 
98  /// analyze all children and return "effective" usage
99  DUState eval(DUVariableType variable_type) const;
100 
101  /// if, elseif and else evaluation
102  DUState sub_block_eval(DUVariableType variable_type) const;
103 
104  /// evaluate global usage i.e. with [D,U] states of children
105  DUState conditional_block_eval(DUVariableType variable_type) const;
106 
107  void print(printer::JSONPrinter& printer) const;
108 
109  /** \brief binary expression in which the variable is used/defined
110  *
111  * We use the binary expression because it is used in both:
112  *
113  * - x = ... // expression statement, binary expression
114  * - IF (x == 0) // not an expression statement, binary expression
115  *
116  * We also want the outermost binary expression. Thus, we do not keep track
117  * of the interior ones. For example:
118  *
119  * \f$ tau = tau + 1 \f$
120  *
121  * we want to return the full statement, not only \f$ tau + 1 \f$
122  */
123  std::shared_ptr<const ast::BinaryExpression> binary_expression;
124 };
125 
126 
127 /**
128  * \class DUChain
129  * \brief Def-Use chain for an AST node
130  */
131 class DUChain {
132  public:
133  /// name of the node
134  std::string name;
135 
136  /// type of variable
138 
139  /// def-use chain for a variable
140  std::vector<DUInstance> chain;
141 
142  DUChain() = default;
143  DUChain(std::string name, DUVariableType type)
144  : name(std::move(name))
145  , variable_type(type) {}
146 
147  /// return "effective" usage of a variable
148  DUState eval() const;
149 
150  /// return json representation
151  std::string to_string(bool compact = true) const;
152 };
153 
154 
155 /**
156  * @addtogroup visitor_classes
157  * @{
158  */
159 
160 /**
161  * \class DefUseAnalyzeVisitor
162  * \brief %Visitor to return Def-Use chain for a given variable in the block/node
163  *
164  * Motivation: For global to local variable transformation aka localizer
165  * pass, we need to compute Def-Use chains for all global variables. For
166  * example, if we have variable usage like:
167  *
168  * \code{.mod}
169  * NEURON {
170  * RANGE tau, beta
171  * }
172  *
173  * DERIVATIVE states() {
174  * ...
175  * x = alpha
176  * beta = x + y
177  * ....
178  * z = beta
179  * }
180  *
181  * INITIAL {
182  * ...
183  * beta = x + y
184  * z = beta * 0.1
185  * alpha = x * y
186  * }
187  * \endcode
188  *
189  * In the above example if we look at variable beta then it's defined before it's
190  * usage and hence it can be safely made local. But variable alpha is used in first
191  * block and defined in second block. Hence it can't be made local. A variable can
192  * be made local if it is defined before it's usage (in all global blocks). We exclude
193  * procedures/functions because we expect inlining to be done prior to this pass.
194  *
195  * The analysis of if-elseif-else needs special attention because the def-use chain
196  * needs re-cursive evaluation in order to find end-result. For example:
197  *
198  * \code{.mod}
199  * IF(...) {
200  * beta = y
201  * } ELSE {
202  * IF(...) {
203  * beta = x
204  * } ELSE {
205  * x = beta
206  * }
207  * }
208  * \endcode
209  *
210  * For if-else statements, in the above example, if the variable is used
211  * in any of the if-elseif-else part then it is considered as "used". And
212  * this is done recursively from innermost level to the top.
213  */
215  private:
216  /// symbol table containing global variables
218 
219  /// def-use chain currently being constructed
220  std::vector<DUInstance>* current_chain = nullptr;
221 
222  /// symbol table for current statement block (or of parent block if doesn't have)
223  /// should be initialized by caller somehow
225 
226  /// symbol tables in call hierarchy
227  std::stack<symtab::SymbolTable*> symtab_stack;
228 
229  /// variable for which to construct def-use chain
230  std::string variable_name;
231 
232  /// variable type (Local or Global)
234 
235  /// indicate that there is unsupported construct encountered
236  bool unsupported_node = false;
237 
238  /// ignore verbatim blocks
239  bool ignore_verbatim = false;
240 
241  /// starting visiting lhs of assignment statement
242  bool visiting_lhs = false;
243 
244  std::shared_ptr<const ast::BinaryExpression> current_binary_expression = nullptr;
245 
246  void process_variable(const std::string& name);
247  void process_variable(const std::string& name, int index);
248 
249  void update_defuse_chain(const std::string& name);
250  void visit_unsupported_node(const ast::Node& node);
251  void visit_with_new_chain(const ast::Node& node, DUState state);
252  void start_new_chain(DUState state);
253 
254  public:
255  DefUseAnalyzeVisitor() = delete;
256 
258  : global_symtab(&symtab) {}
259 
261  : global_symtab(&symtab)
263 
264  void visit_binary_expression(const ast::BinaryExpression& node) override;
265  void visit_if_statement(const ast::IfStatement& node) override;
266  void visit_function_call(const ast::FunctionCall& node) override;
267  void visit_statement_block(const ast::StatementBlock& node) override;
268  void visit_verbatim(const ast::Verbatim& node) override;
269 
270  /**
271  * /\name unsupported statements
272  * we aren't sure how to handle this "yet" and hence variables
273  * used in any of the below statements are handled separately
274  * \{
275  */
276 
277  void visit_reaction_statement(const ast::ReactionStatement& node) override;
278 
279  void visit_non_lin_equation(const ast::NonLinEquation& node) override;
280 
281  void visit_lin_equation(const ast::LinEquation& node) override;
282 
283  void visit_from_statement(const ast::FromStatement& node) override;
284 
285  void visit_conserve(const ast::Conserve& node) override;
286 
287  void visit_var_name(const ast::VarName& node) override;
288 
289  void visit_name(const ast::Name& node) override;
290 
291  void visit_indexed_name(const ast::IndexedName& node) override;
292 
293  /** \} */
294 
295  /**
296  * /\name statements
297  * nodes that should not be used for def-use chain analysis
298  * \{
299  */
300 
301  void visit_conductance_hint(const ast::ConductanceHint& node) override;
302 
303  void visit_local_list_statement(const ast::LocalListStatement& node) override;
304 
305  void visit_argument(const ast::Argument& node) override;
306 
307  /** \} */
308 
309  /// compute def-use chain for a variable within the node
310  DUChain analyze(const ast::Ast& node, const std::string& name);
311 };
312 
313 /** @} */ // end of visitor_classes
314 
315 } // namespace visitor
316 } // namespace nmodl
nmodl::visitor::DefUseAnalyzeVisitor::unsupported_node
bool unsupported_node
indicate that there is unsupported construct encountered
Definition: defuse_analyze_visitor.hpp:236
nmodl::visitor::DefUseAnalyzeVisitor::visit_function_call
void visit_function_call(const ast::FunctionCall &node) override
Nothing to do if called function is not defined or it's external but if there is a function call for ...
Definition: defuse_analyze_visitor.cpp:204
nmodl::visitor::DefUseAnalyzeVisitor::DefUseAnalyzeVisitor
DefUseAnalyzeVisitor(symtab::SymbolTable &symtab)
Definition: defuse_analyze_visitor.hpp:257
nmodl::ast::Node
Base class for all AST node.
Definition: node.hpp:40
nmodl::ast::Verbatim
Represents a C code block.
Definition: verbatim.hpp:38
nmodl::visitor::DefUseAnalyzeVisitor::variable_name
std::string variable_name
variable for which to construct def-use chain
Definition: defuse_analyze_visitor.hpp:230
nmodl::visitor::DUVariableType
DUVariableType
Variable type processed by DefUseAnalyzeVisitor.
Definition: defuse_analyze_visitor.hpp:59
nmodl::visitor::DUInstance::eval
DUState eval(DUVariableType variable_type) const
analyze all children and return "effective" usage
Definition: defuse_analyze_visitor.cpp:165
nmodl::visitor::DUInstance::print
void print(printer::JSONPrinter &printer) const
DUInstance to JSON string.
Definition: defuse_analyze_visitor.cpp:57
nmodl::visitor::DUState
DUState
Represent a state in Def-Use chain.
Definition: defuse_analyze_visitor.hpp:28
nmodl::visitor::ConstAstVisitor
Concrete constant visitor for all AST classes.
Definition: ast_visitor.hpp:166
nmodl::visitor::DefUseAnalyzeVisitor::visit_indexed_name
void visit_indexed_name(const ast::IndexedName &node) override
visit node of type ast::IndexedName
Definition: defuse_analyze_visitor.cpp:327
nmodl::ast::Ast
Base class for all Abstract Syntax Tree node types.
Definition: ast.hpp:69
nmodl::visitor::DUInstance::DUInstance
DUInstance(DUState state, const std::shared_ptr< const ast::BinaryExpression > binary_expression)
Definition: defuse_analyze_visitor.hpp:93
nmodl::ast::NonLinEquation
TODO.
Definition: non_lin_equation.hpp:38
nmodl::visitor::DefUseAnalyzeVisitor::variable_type
DUVariableType variable_type
variable type (Local or Global)
Definition: defuse_analyze_visitor.hpp:233
nmodl::visitor::DefUseAnalyzeVisitor::update_defuse_chain
void update_defuse_chain(const std::string &name)
Update the Def-Use chain for given variable.
Definition: defuse_analyze_visitor.cpp:369
nmodl::visitor::DefUseAnalyzeVisitor::visit_lin_equation
void visit_lin_equation(const ast::LinEquation &node) override
visit node of type ast::LinEquation
Definition: defuse_analyze_visitor.cpp:305
nmodl::visitor::DefUseAnalyzeVisitor::visit_conductance_hint
void visit_conductance_hint(const ast::ConductanceHint &node) override
statements / nodes that should not be used for def-use chain analysis
Definition: defuse_analyze_visitor.cpp:350
nmodl::ast::Conserve
Represent CONSERVE statement in NMODL.
Definition: conserve.hpp:38
nmodl::ast::ConductanceHint
Represents CONDUCTANCE statement in NMODL.
Definition: conductance_hint.hpp:46
nmodl
encapsulates code generation backend implementations
Definition: ast_common.hpp:26
nmodl::visitor::DefUseAnalyzeVisitor::start_new_chain
void start_new_chain(DUState state)
Definition: defuse_analyze_visitor.cpp:414
nmodl::visitor::DefUseAnalyzeVisitor::visit_unsupported_node
void visit_unsupported_node(const ast::Node &node)
Definition: defuse_analyze_visitor.cpp:193
nmodl::visitor::DUState::LD
@ LD
local variable is defined
nmodl::visitor::DefUseAnalyzeVisitor::DefUseAnalyzeVisitor
DefUseAnalyzeVisitor()=delete
nmodl::visitor::DUVariableType::Local
@ Local
nmodl::visitor::DUChain::eval
DUState eval() const
return "effective" usage of a variable
Definition: defuse_analyze_visitor.cpp:177
nmodl::visitor::DefUseAnalyzeVisitor::visit_from_statement
void visit_from_statement(const ast::FromStatement &node) override
visit node of type ast::FromStatement
Definition: defuse_analyze_visitor.cpp:309
nmodl::ast::VarName
Represents a variable.
Definition: var_name.hpp:43
nmodl::visitor::DUChain::variable_type
DUVariableType variable_type
type of variable
Definition: defuse_analyze_visitor.hpp:137
nmodl::visitor::DefUseAnalyzeVisitor
Visitor to return Def-Use chain for a given variable in the block/node
Definition: defuse_analyze_visitor.hpp:214
nmodl::visitor::DefUseAnalyzeVisitor::ignore_verbatim
bool ignore_verbatim
ignore verbatim blocks
Definition: defuse_analyze_visitor.hpp:239
json_printer.hpp
Helper class for printing AST in JSON form.
nmodl::visitor::DUState::D
@ D
global variable is defined
nmodl::visitor::DefUseAnalyzeVisitor::global_symtab
symtab::SymbolTable * global_symtab
symbol table containing global variables
Definition: defuse_analyze_visitor.hpp:217
nmodl::visitor::DefUseAnalyzeVisitor::analyze
DUChain analyze(const ast::Ast &node, const std::string &name)
compute def-use chain for a variable within the node
Definition: defuse_analyze_visitor.cpp:419
nmodl::visitor::DUState::U
@ U
global variable is used
nmodl::visitor::DUState::ELSEIF
@ ELSEIF
elseif sub-block
nmodl::visitor::DUInstance::conditional_block_eval
DUState conditional_block_eval(DUVariableType variable_type) const
evaluate global usage i.e. with [D,U] states of children
Definition: defuse_analyze_visitor.cpp:133
nmodl::ast::LocalListStatement
TODO.
Definition: local_list_statement.hpp:39
visitor_utils.hpp
Utility functions for visitors implementation.
nmodl::printer::JSONPrinter
Helper class for printing AST in JSON form.
Definition: json_printer.hpp:46
nmodl::visitor::DefUseAnalyzeVisitor::visit_with_new_chain
void visit_with_new_chain(const ast::Node &node, DUState state)
Definition: defuse_analyze_visitor.cpp:407
nmodl::visitor::DUInstance::sub_block_eval
DUState sub_block_eval(DUVariableType variable_type) const
if, elseif and else evaluation
Definition: defuse_analyze_visitor.cpp:89
nmodl::visitor::DefUseAnalyzeVisitor::visit_conserve
void visit_conserve(const ast::Conserve &node) override
visit node of type ast::Conserve
Definition: defuse_analyze_visitor.cpp:313
nmodl::ast::IndexedName
Represents specific element of an array variable.
Definition: indexed_name.hpp:48
nmodl::visitor::DefUseAnalyzeVisitor::visit_statement_block
void visit_statement_block(const ast::StatementBlock &node) override
visit node of type ast::StatementBlock
Definition: defuse_analyze_visitor.cpp:214
nmodl::visitor::operator<<
std::ostream & operator<<(std::ostream &os, DUState state)
Definition: defuse_analyze_visitor.cpp:52
nmodl::ast::FunctionCall
TODO.
Definition: function_call.hpp:38
nmodl::visitor::DefUseAnalyzeVisitor::process_variable
void process_variable(const std::string &name)
Definition: defuse_analyze_visitor.cpp:393
nmodl::visitor::DefUseAnalyzeVisitor::DefUseAnalyzeVisitor
DefUseAnalyzeVisitor(symtab::SymbolTable &symtab, bool ignore_verbatim)
Definition: defuse_analyze_visitor.hpp:260
nmodl::visitor::DefUseAnalyzeVisitor::current_chain
std::vector< DUInstance > * current_chain
def-use chain currently being constructed
Definition: defuse_analyze_visitor.hpp:220
nmodl::visitor::DUState::NONE
@ NONE
variable is not used
nmodl::visitor::DUInstance::binary_expression
std::shared_ptr< const ast::BinaryExpression > binary_expression
binary expression in which the variable is used/defined
Definition: defuse_analyze_visitor.hpp:123
nmodl::visitor::DefUseAnalyzeVisitor::visit_verbatim
void visit_verbatim(const ast::Verbatim &node) override
We are not analyzing verbatim blocks yet and hence if there is a verbatim block we assume there is va...
Definition: defuse_analyze_visitor.cpp:287
nmodl::ast::Global
Represents GLOBAL statement in NMODL.
Definition: global.hpp:39
nmodl::visitor::DefUseAnalyzeVisitor::current_binary_expression
std::shared_ptr< const ast::BinaryExpression > current_binary_expression
Definition: defuse_analyze_visitor.hpp:244
nmodl::visitor::DUChain::to_string
std::string to_string(bool compact=true) const
return json representation
Definition: defuse_analyze_visitor.cpp:70
nmodl::ast::IfStatement
TODO.
Definition: if_statement.hpp:39
nmodl::visitor::DefUseAnalyzeVisitor::symtab_stack
std::stack< symtab::SymbolTable * > symtab_stack
symbol tables in call hierarchy
Definition: defuse_analyze_visitor.hpp:227
nmodl::symtab::SymbolTable
Represent symbol table for a NMODL block.
Definition: symbol_table.hpp:57
nmodl::visitor::DUState::CONDITIONAL_BLOCK
@ CONDITIONAL_BLOCK
conditional block
nmodl::visitor::DefUseAnalyzeVisitor::visit_argument
void visit_argument(const ast::Argument &node) override
visit node of type ast::Argument
Definition: defuse_analyze_visitor.cpp:354
nmodl::visitor::DefUseAnalyzeVisitor::visit_non_lin_equation
void visit_non_lin_equation(const ast::NonLinEquation &node) override
visit node of type ast::NonLinEquation
Definition: defuse_analyze_visitor.cpp:301
nmodl::visitor::DUInstance::children
std::vector< DUInstance > children
usage of variable in case of if like statements
Definition: defuse_analyze_visitor.hpp:91
nmodl::visitor::DUState::ELSE
@ ELSE
else sub-block
nmodl::visitor::DefUseAnalyzeVisitor::visiting_lhs
bool visiting_lhs
starting visiting lhs of assignment statement
Definition: defuse_analyze_visitor.hpp:242
nmodl::ast::StatementBlock
Represents block encapsulating list of statements.
Definition: statement_block.hpp:53
nmodl::visitor::DUInstance::state
DUState state
state of the usage
Definition: defuse_analyze_visitor.hpp:88
nmodl::ast::Argument
Represents an argument to functions and procedures.
Definition: argument.hpp:48
nmodl::visitor::DefUseAnalyzeVisitor::visit_if_statement
void visit_if_statement(const ast::IfStatement &node) override
visit node of type ast::IfStatement
Definition: defuse_analyze_visitor.cpp:249
nmodl::visitor::DUState::IF
@ IF
if sub-block
nmodl::visitor::DefUseAnalyzeVisitor::visit_local_list_statement
void visit_local_list_statement(const ast::LocalListStatement &node) override
visit node of type ast::LocalListStatement
Definition: defuse_analyze_visitor.cpp:352
nmodl::visitor::DUState::LU
@ LU
local variable is used
nmodl::ast::ReactionStatement
TODO.
Definition: reaction_statement.hpp:39
nmodl::ast::LinEquation
TODO.
Definition: lin_equation.hpp:38
nmodl::visitor::DUChain::chain
std::vector< DUInstance > chain
def-use chain for a variable
Definition: defuse_analyze_visitor.hpp:140
nmodl::visitor::DefUseAnalyzeVisitor::visit_name
void visit_name(const ast::Name &node) override
visit node of type ast::Name
Definition: defuse_analyze_visitor.cpp:322
nmodl::visitor::DefUseAnalyzeVisitor::current_symtab
symtab::SymbolTable * current_symtab
symbol table for current statement block (or of parent block if doesn't have) should be initialized b...
Definition: defuse_analyze_visitor.hpp:224
nmodl::visitor::DefUseAnalyzeVisitor::visit_var_name
void visit_var_name(const ast::VarName &node) override
visit node of type ast::VarName
Definition: defuse_analyze_visitor.cpp:317
nmodl::visitor::DUChain::name
std::string name
name of the node
Definition: defuse_analyze_visitor.hpp:134
nmodl::visitor::DUState::CD
@ CD
global or local variable is conditionally defined
nmodl::visitor::DefUseAnalyzeVisitor::visit_reaction_statement
void visit_reaction_statement(const ast::ReactionStatement &node) override
unsupported statements : we aren't sure how to handle this "yet" and hence variables used in any of t...
Definition: defuse_analyze_visitor.cpp:297
nmodl::ast::Name
Represents a name.
Definition: name.hpp:44
nmodl::visitor::DUChain::DUChain
DUChain(std::string name, DUVariableType type)
Definition: defuse_analyze_visitor.hpp:143
nmodl::ast::BinaryExpression
Represents binary expression in the NMODL.
Definition: binary_expression.hpp:52
nmodl::visitor::DUState::UNKNOWN
@ UNKNOWN
state not known
nmodl::visitor::DUChain
Def-Use chain for an AST node.
Definition: defuse_analyze_visitor.hpp:131
nmodl::visitor::DefUseAnalyzeVisitor::visit_binary_expression
void visit_binary_expression(const ast::BinaryExpression &node) override
Nmodl grammar doesn't allow assignment operator on rhs (e.g.
Definition: defuse_analyze_visitor.cpp:229
decl.hpp
Forward declarations of symbols in namespace nmodl::symtab.
nmodl::visitor::DUChain::DUChain
DUChain()=default
nmodl::visitor::DUInstance
Represent use of a variable in the statement.
Definition: defuse_analyze_visitor.hpp:85
nmodl::ast::FromStatement
TODO.
Definition: from_statement.hpp:38
ast_visitor.hpp
Concrete visitor for all AST classes.