NEURON
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;
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);
251  void visit_with_new_chain(const ast::Node& node, DUState state);
252  void start_new_chain(DUState state);
253 
254  public:
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 
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 
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
Concrete visitor for all AST classes.
Represents an argument to functions and procedures.
Definition: argument.hpp:48
Represents binary expression in the NMODL.
Represents CONDUCTANCE statement in NMODL.
Represent CONSERVE statement in NMODL.
Definition: conserve.hpp:38
Represents GLOBAL statement in NMODL.
Definition: global.hpp:39
Represents specific element of an array variable.
One equation in a system of equations tha collectively form a LINEAR block.
Represents a name.
Definition: name.hpp:44
Base class for all AST node.
Definition: node.hpp:40
One equation in a system of equations that collectively make a NONLINEAR block.
Represents block encapsulating list of statements.
Represents a variable.
Definition: var_name.hpp:43
Represents a C code block.
Definition: verbatim.hpp:38
Helper class for printing AST in JSON form.
Represent symbol table for a NMODL block.
Concrete constant visitor for all AST classes.
Def-Use chain for an AST node.
std::string to_string(bool compact=true) const
return json representation
std::vector< DUInstance > chain
def-use chain for a variable
DUChain(std::string name, DUVariableType type)
DUVariableType variable_type
type of variable
DUState eval() const
return "effective" usage of a variable
std::string name
name of the node
Represent use of a variable in the statement.
DUInstance(DUState state, const std::shared_ptr< const ast::BinaryExpression > binary_expression)
std::shared_ptr< const ast::BinaryExpression > binary_expression
binary expression in which the variable is used/defined
DUState eval(DUVariableType variable_type) const
analyze all children and return "effective" usage
DUState state
state of the usage
DUState conditional_block_eval(DUVariableType variable_type) const
evaluate global usage i.e. with [D,U] states of children
void print(printer::JSONPrinter &printer) const
DUInstance to JSON string.
std::vector< DUInstance > children
usage of variable in case of if like statements
DUState sub_block_eval(DUVariableType variable_type) const
if, elseif and else evaluation
Visitor to return Def-Use chain for a given variable in the block/node
DUVariableType variable_type
variable type (Local or Global)
bool unsupported_node
indicate that there is unsupported construct encountered
void visit_name(const ast::Name &node) override
visit node of type ast::Name
symtab::SymbolTable * current_symtab
symbol table for current statement block (or of parent block if doesn't have) should be initialized b...
symtab::SymbolTable * global_symtab
symbol table containing global variables
void visit_indexed_name(const ast::IndexedName &node) override
visit node of type ast::IndexedName
void visit_binary_expression(const ast::BinaryExpression &node) override
Nmodl grammar doesn't allow assignment operator on rhs (e.g.
DUChain analyze(const ast::Ast &node, const std::string &name)
compute def-use chain for a variable within the node
void visit_non_lin_equation(const ast::NonLinEquation &node) override
visit node of type ast::NonLinEquation
void visit_conductance_hint(const ast::ConductanceHint &node) override
statements / nodes that should not be used for def-use chain analysis
void visit_from_statement(const ast::FromStatement &node) override
visit node of type ast::FromStatement
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 ...
bool visiting_lhs
starting visiting lhs of assignment statement
void visit_conserve(const ast::Conserve &node) override
visit node of type ast::Conserve
void visit_with_new_chain(const ast::Node &node, DUState state)
DefUseAnalyzeVisitor(symtab::SymbolTable &symtab)
void visit_local_list_statement(const ast::LocalListStatement &node) override
visit node of type ast::LocalListStatement
std::vector< DUInstance > * current_chain
def-use chain currently being constructed
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...
void process_variable(const std::string &name)
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...
void visit_if_statement(const ast::IfStatement &node) override
visit node of type ast::IfStatement
void visit_statement_block(const ast::StatementBlock &node) override
visit node of type ast::StatementBlock
std::stack< symtab::SymbolTable * > symtab_stack
symbol tables in call hierarchy
void visit_var_name(const ast::VarName &node) override
visit node of type ast::VarName
void update_defuse_chain(const std::string &name)
Update the Def-Use chain for given variable.
void visit_unsupported_node(const ast::Node &node)
DefUseAnalyzeVisitor(symtab::SymbolTable &symtab, bool ignore_verbatim)
std::shared_ptr< const ast::BinaryExpression > current_binary_expression
void visit_argument(const ast::Argument &node) override
visit node of type ast::Argument
std::string variable_name
variable for which to construct def-use chain
void visit_lin_equation(const ast::LinEquation &node) override
visit node of type ast::LinEquation
Helper class for printing AST in JSON form.
const char * name
Definition: init.cpp:16
void move(Item *q1, Item *q2, Item *q3)
Definition: list.cpp:200
DUState
Represent a state in Def-Use chain.
@ ELSEIF
elseif sub-block
@ CONDITIONAL_BLOCK
conditional block
@ CD
global or local variable is conditionally defined
@ U
global variable is used
@ UNKNOWN
state not known
@ ELSE
else sub-block
@ LU
local variable is used
@ LD
local variable is defined
@ NONE
variable is not used
@ D
global variable is defined
DUVariableType
Variable type processed by DefUseAnalyzeVisitor.
std::ostream & operator<<(std::ostream &os, DUState state)
encapsulates code generation backend implementations
Definition: ast_common.hpp:26
static Node * node(Object *)
Definition: netcvode.cpp:291
short type
Definition: cabvars.h:10
Base class for all Abstract Syntax Tree node types.
Definition: ast.hpp:52
Forward declarations of symbols in namespace nmodl::symtab.
Utility functions for visitors implementation.