Line | Branch | Exec | Source |
---|---|---|---|
1 | // Copyright Contributors to the OpenVDB Project | ||
2 | // SPDX-License-Identifier: MPL-2.0 | ||
3 | |||
4 | /// @file ast/Scanners.cc | ||
5 | |||
6 | #include "Scanners.h" | ||
7 | #include "Visitor.h" | ||
8 | |||
9 | #include <string> | ||
10 | #include <map> | ||
11 | |||
12 | namespace openvdb { | ||
13 | OPENVDB_USE_VERSION_NAMESPACE | ||
14 | namespace OPENVDB_VERSION_NAME { | ||
15 | |||
16 | namespace ax { | ||
17 | namespace ast { | ||
18 | |||
19 | namespace { | ||
20 | |||
21 | template <typename NodeT, typename OpT> | ||
22 | struct VariableDependencyVisitor : | ||
23 | public ast::VisitNodeType<NodeT, OpT, | ||
24 | VariableDependencyVisitor<NodeT, OpT>> | ||
25 | { | ||
26 | using BaseT = ast::VisitNodeType<NodeT, OpT, | ||
27 | VariableDependencyVisitor<NodeT, OpT>>; | ||
28 | using BaseT::traverse; | ||
29 | using BaseT::visit; | ||
30 | |||
31 | VariableDependencyVisitor(const OpT& op) : BaseT(op) {} | ||
32 | ~VariableDependencyVisitor() = default; | ||
33 | |||
34 | 5373 | bool traverse(const ast::Loop* loop) | |
35 | { | ||
36 |
1/2✓ Branch 0 taken 5373 times.
✗ Branch 1 not taken.
|
5373 | if (!loop) return true; |
37 |
2/2✓ Branch 1 taken 5367 times.
✓ Branch 2 taken 6 times.
|
5373 | if (!this->traverse(loop->initial())) return false; |
38 |
2/2✓ Branch 1 taken 4847 times.
✓ Branch 2 taken 520 times.
|
5367 | if (!this->traverse(loop->condition())) return false; |
39 |
2/2✓ Branch 1 taken 4837 times.
✓ Branch 2 taken 10 times.
|
4847 | if (!this->traverse(loop->iteration())) return false; |
40 |
2/2✓ Branch 0 taken 753 times.
✓ Branch 1 taken 4084 times.
|
4837 | if (!this->traverse(loop->body())) return false; |
41 | if (!this->visit(loop)) return false; | ||
42 | return true; | ||
43 | } | ||
44 | }; | ||
45 | |||
46 | /// @brief For a given variable at a particular position in an AST, find all | ||
47 | /// attributes, locals and external variables which it depends on (i.e. any | ||
48 | /// Attribute, Local or ExternalVariable AST nodes which impacts the given | ||
49 | /// variables value) by recursively traversing through all connected paths. | ||
50 | /// This includes both direct and indirect influences; for example, a direct | ||
51 | /// assignment "@b = @a;" and an indirect code branch "if (@a) @b = 1"; | ||
52 | /// @note This is position dependent in regards to the given variables location. | ||
53 | /// Any code which writes to this variable after the given usage will not be | ||
54 | /// cataloged in the output dependency vector. | ||
55 | /// @warning This does not currently handle scoped local variable re-declarations | ||
56 | /// and instead will end up adding matching names as extra dependencies | ||
57 | /// @todo: fix this for scoped variables, capturing of all instances, and not adding | ||
58 | /// dependencies between different branches of conditionals | ||
59 | 19309 | void variableDependencies(const ast::Variable& var, | |
60 | std::vector<const ast::Variable*>& dependencies) | ||
61 | { | ||
62 | // external variables are read-only i.e. have no dependencies | ||
63 |
2/2✓ Branch 1 taken 77 times.
✓ Branch 2 taken 19232 times.
|
19309 | if (var.nodetype() == ast::Node::ExternalVariableNode) return; |
64 | |||
65 | // Get the root node | ||
66 | const ast::Node* root = &var; | ||
67 |
2/2✓ Branch 0 taken 69553 times.
✓ Branch 1 taken 19232 times.
|
88785 | while (const ast::Node* parent = root->parent()) { |
68 | root = parent; | ||
69 | } | ||
70 | |||
71 | // collect all occurrences of this var up to and including | ||
72 | // it's current usage, terminating traversal afterwards | ||
73 | const bool attributeVisit = | ||
74 |
1/2✓ Branch 2 taken 19232 times.
✗ Branch 3 not taken.
|
19232 | (var.nodetype() == ast::Node::AttributeNode); |
75 | |||
76 | std::vector<const ast::Variable*> usage; | ||
77 | |||
78 | auto collect = | ||
79 | 338127 | [&var, &usage, attributeVisit] | |
80 |
2/2✓ Branch 0 taken 30583 times.
✓ Branch 1 taken 82719 times.
|
113302 | (const ast::Variable& use) -> bool |
81 | { | ||
82 |
2/2✓ Branch 0 taken 190052 times.
✓ Branch 1 taken 148075 times.
|
338127 | if (attributeVisit) { |
83 |
2/2✓ Branch 1 taken 132633 times.
✓ Branch 2 taken 57419 times.
|
190052 | if (use.nodetype() != ast::Node::AttributeNode) return true; |
84 | 132633 | const auto& attrib = static_cast<const ast::Attribute&>(var); | |
85 | const auto& useAttrib = static_cast<const ast::Attribute&>(use); | ||
86 |
2/2✓ Branch 1 taken 20272 times.
✓ Branch 2 taken 112361 times.
|
132633 | if (attrib.tokenname() != useAttrib.tokenname()) return true; |
87 | } | ||
88 | else { | ||
89 |
2/2✓ Branch 1 taken 113302 times.
✓ Branch 2 taken 34773 times.
|
148075 | if (use.nodetype() != ast::Node::LocalNode) return true; |
90 |
2/2✓ Branch 0 taken 30583 times.
✓ Branch 1 taken 82719 times.
|
113302 | if (use.name() != var.name()) return true; |
91 | } | ||
92 | 50855 | usage.emplace_back(&use); | |
93 | 50855 | return &use != &var; | |
94 |
1/2✓ Branch 1 taken 19232 times.
✗ Branch 2 not taken.
|
19232 | }; |
95 | |||
96 | VariableDependencyVisitor<ast::Variable, decltype(collect)> | ||
97 | depVisitor(collect); | ||
98 |
1/2✓ Branch 1 taken 19232 times.
✗ Branch 2 not taken.
|
19232 | depVisitor.traverse(root); |
99 | |||
100 | // The list of nodes which can be considered dependencies to collect | ||
101 | using ListT = openvdb::TypeList< | ||
102 | ast::Attribute, | ||
103 | ast::Local, | ||
104 | ast::ExternalVariable>; | ||
105 | |||
106 | // small lambda to check to see if a dep is already being tracked | ||
107 | 30927 | auto hasDep = [&](const ast::Variable* dep) -> bool { | |
108 | 30927 | return (std::find(dependencies.cbegin(), dependencies.cend(), dep) != | |
109 | 30927 | dependencies.cend()); | |
110 | 19232 | }; | |
111 | // recursively traverse all usages and resolve dependencies | ||
112 |
2/2✓ Branch 0 taken 50855 times.
✓ Branch 1 taken 19232 times.
|
70087 | for (const auto& use : usage) |
113 | { | ||
114 | 50855 | const ast::Node* child = use; | |
115 | // track writable for conditionals | ||
116 | bool written = false; | ||
117 |
2/2✓ Branch 0 taken 215360 times.
✓ Branch 1 taken 50855 times.
|
266215 | while (const ast::Node* parent = child->parent()) { |
118 |
1/2✓ Branch 1 taken 215360 times.
✗ Branch 2 not taken.
|
215360 | const ast::Node::NodeType type = parent->nodetype(); |
119 |
2/2✓ Branch 0 taken 3885 times.
✓ Branch 1 taken 211475 times.
|
215360 | if (type == ast::Node::CrementNode) { |
120 | written = true; | ||
121 |
2/2✓ Branch 1 taken 2617 times.
✓ Branch 2 taken 1268 times.
|
3885 | if (!hasDep(use)) { |
122 |
1/2✓ Branch 1 taken 1268 times.
✗ Branch 2 not taken.
|
1268 | dependencies.emplace_back(use); |
123 | } | ||
124 | } | ||
125 |
2/2✓ Branch 0 taken 1454 times.
✓ Branch 1 taken 210021 times.
|
211475 | else if (type == ast::Node::ConditionalStatementNode) { |
126 | const ast::ConditionalStatement* conditional = | ||
127 | static_cast<const ast::ConditionalStatement*>(parent); | ||
128 | const ast::Expression* condition = conditional->condition(); | ||
129 | // traverse down and collect variables | ||
130 |
2/2✓ Branch 0 taken 1229 times.
✓ Branch 1 taken 225 times.
|
1454 | if (child != condition){ |
131 | std::vector<const ast::Variable*> vars; | ||
132 |
1/2✓ Branch 1 taken 225 times.
✗ Branch 2 not taken.
|
225 | collectNodeTypes<ListT>(*condition, vars); |
133 | // find next deps | ||
134 |
2/2✓ Branch 0 taken 146 times.
✓ Branch 1 taken 225 times.
|
371 | for (const ast::Variable* dep : vars) { |
135 | // don't add this dep if it's not being written to. Unlike | ||
136 | // all other visits, the conditionals dictate program flow. | ||
137 | // Values in the conditional expression only link to the | ||
138 | // current usage if the current usage is being modified | ||
139 |
4/4✓ Branch 0 taken 94 times.
✓ Branch 1 taken 52 times.
✓ Branch 3 taken 37 times.
✓ Branch 4 taken 57 times.
|
146 | if (!written || hasDep(dep)) continue; |
140 |
1/2✓ Branch 1 taken 57 times.
✗ Branch 2 not taken.
|
57 | dependencies.emplace_back(dep); |
141 |
1/2✓ Branch 1 taken 57 times.
✗ Branch 2 not taken.
|
57 | variableDependencies(*dep, dependencies); |
142 | } | ||
143 | } | ||
144 | } | ||
145 |
2/2✓ Branch 0 taken 235 times.
✓ Branch 1 taken 209786 times.
|
210021 | else if (type == ast::Node::TernaryOperatorNode) { |
146 | const ast::TernaryOperator* ternary = | ||
147 | static_cast<const ast::TernaryOperator*>(parent); | ||
148 | const ast::Expression* condition = ternary->condition(); | ||
149 | // traverse down and collect variables | ||
150 |
2/2✓ Branch 0 taken 127 times.
✓ Branch 1 taken 108 times.
|
235 | if (child != condition) { |
151 | std::vector<const ast::Variable*> vars; | ||
152 |
1/2✓ Branch 1 taken 108 times.
✗ Branch 2 not taken.
|
108 | collectNodeTypes<ListT>(*condition, vars); |
153 | // find next deps | ||
154 |
2/2✓ Branch 0 taken 76 times.
✓ Branch 1 taken 108 times.
|
184 | for (const ast::Variable* dep : vars) { |
155 | // don't add this dep if it's not being written to. Unlike | ||
156 | // all other visits, the conditionals dictate program flow. | ||
157 | // Values in the conditional expression only link to the | ||
158 | // current usage if the current usage is being modified | ||
159 |
4/4✓ Branch 0 taken 7 times.
✓ Branch 1 taken 69 times.
✓ Branch 3 taken 5 times.
✓ Branch 4 taken 2 times.
|
76 | if (!written || hasDep(dep)) continue; |
160 |
1/2✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
|
5 | dependencies.emplace_back(dep); |
161 |
1/2✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
|
5 | variableDependencies(*dep, dependencies); |
162 | } | ||
163 | } | ||
164 | } | ||
165 |
2/2✓ Branch 0 taken 14404 times.
✓ Branch 1 taken 195382 times.
|
209786 | else if (type == ast::Node::LoopNode) { |
166 | const ast::Loop* loop = | ||
167 | static_cast<const ast::Loop*>(parent); | ||
168 | const ast::Statement* condition = loop->condition(); | ||
169 | // traverse down and collect variables | ||
170 |
2/2✓ Branch 0 taken 2495 times.
✓ Branch 1 taken 11909 times.
|
14404 | if (child != condition) { |
171 | std::vector<const ast::Variable*> vars; | ||
172 | // if the condition is a comma operator the last element determines flow | ||
173 |
3/4✓ Branch 1 taken 11909 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 116 times.
✓ Branch 4 taken 11793 times.
|
11909 | if (condition->nodetype() == ast::Node::NodeType::CommaOperatorNode) { |
174 | const ast::CommaOperator* | ||
175 | comma = static_cast<const ast::CommaOperator*>(condition); | ||
176 |
1/2✓ Branch 0 taken 116 times.
✗ Branch 1 not taken.
|
116 | if (!comma->empty()) { |
177 |
1/2✓ Branch 0 taken 116 times.
✗ Branch 1 not taken.
|
116 | const ast::Expression* lastExpression = comma->child(comma->size()-1); |
178 |
1/2✓ Branch 1 taken 116 times.
✗ Branch 2 not taken.
|
116 | collectNodeTypes<ListT>(*lastExpression, vars); |
179 | } | ||
180 | } | ||
181 | else { | ||
182 | collectNodeTypes<ListT>(*condition, vars); | ||
183 | } | ||
184 | // find next deps | ||
185 |
2/2✓ Branch 0 taken 10152 times.
✓ Branch 1 taken 11909 times.
|
22061 | for (const ast::Variable* dep : vars) { |
186 | // don't add this dep if it's not being written to. Unlike | ||
187 | // all other visits, the conditionals dictate program flow. | ||
188 | // Values in the conditional expression only link to the | ||
189 | // current usage if the current usage is being modified | ||
190 |
4/4✓ Branch 0 taken 6801 times.
✓ Branch 1 taken 3351 times.
✓ Branch 3 taken 493 times.
✓ Branch 4 taken 6308 times.
|
10152 | if (!written || hasDep(dep)) continue; |
191 |
1/2✓ Branch 1 taken 493 times.
✗ Branch 2 not taken.
|
493 | dependencies.emplace_back(dep); |
192 |
1/2✓ Branch 1 taken 493 times.
✗ Branch 2 not taken.
|
493 | variableDependencies(*dep, dependencies); |
193 | } | ||
194 | } | ||
195 | |||
196 | } | ||
197 |
2/2✓ Branch 0 taken 35475 times.
✓ Branch 1 taken 159907 times.
|
195382 | else if (type == ast::Node::AssignExpressionNode) { |
198 | const ast::AssignExpression* assignment = | ||
199 | static_cast<const ast::AssignExpression*>(parent); | ||
200 |
2/2✓ Branch 0 taken 18131 times.
✓ Branch 1 taken 17344 times.
|
35475 | if (assignment->lhs() == child) { |
201 | written = true; | ||
202 | // add self dependency if compound | ||
203 |
2/2✓ Branch 0 taken 1616 times.
✓ Branch 1 taken 15728 times.
|
17344 | if (assignment->isCompound()) { |
204 |
2/2✓ Branch 1 taken 1592 times.
✓ Branch 2 taken 24 times.
|
1616 | if (!hasDep(use)) { |
205 |
1/2✓ Branch 1 taken 1592 times.
✗ Branch 2 not taken.
|
1592 | dependencies.emplace_back(use); |
206 | } | ||
207 | } | ||
208 | // traverse down and collect variables | ||
209 | std::vector<const ast::Variable*> vars; | ||
210 |
1/2✓ Branch 1 taken 17344 times.
✗ Branch 2 not taken.
|
17344 | collectNodeTypes<ListT>(*assignment->rhs(), vars); |
211 | // find next deps | ||
212 |
2/2✓ Branch 0 taken 12475 times.
✓ Branch 1 taken 17344 times.
|
29819 | for (const ast::Variable* dep : vars) { |
213 |
2/2✓ Branch 1 taken 5351 times.
✓ Branch 2 taken 7124 times.
|
12475 | if (hasDep(dep)) continue; |
214 |
1/2✓ Branch 1 taken 7124 times.
✗ Branch 2 not taken.
|
7124 | dependencies.emplace_back(dep); |
215 |
1/2✓ Branch 1 taken 7124 times.
✗ Branch 2 not taken.
|
7124 | variableDependencies(*dep, dependencies); |
216 | } | ||
217 | } | ||
218 | } | ||
219 |
2/2✓ Branch 0 taken 9258 times.
✓ Branch 1 taken 150649 times.
|
159907 | else if (type == ast::Node::DeclareLocalNode) { |
220 | const ast::DeclareLocal* declareLocal = | ||
221 | static_cast<const ast::DeclareLocal*>(parent); | ||
222 |
4/4✓ Branch 0 taken 144 times.
✓ Branch 1 taken 9114 times.
✓ Branch 2 taken 331 times.
✓ Branch 3 taken 8783 times.
|
9258 | if (declareLocal->local() == child && declareLocal->hasInit()) { |
223 | std::vector<const ast::Variable*> vars; | ||
224 | written = true; | ||
225 | // traverse down and collect variables | ||
226 |
1/2✓ Branch 1 taken 8783 times.
✗ Branch 2 not taken.
|
8783 | collectNodeTypes<ListT>(*declareLocal->init(), vars); |
227 |
2/2✓ Branch 0 taken 78 times.
✓ Branch 1 taken 8783 times.
|
8861 | for (const ast::Variable* dep : vars) { |
228 |
2/2✓ Branch 1 taken 3 times.
✓ Branch 2 taken 75 times.
|
78 | if (hasDep(dep)) continue; |
229 |
1/2✓ Branch 1 taken 75 times.
✗ Branch 2 not taken.
|
75 | dependencies.emplace_back(dep); |
230 |
1/2✓ Branch 1 taken 75 times.
✗ Branch 2 not taken.
|
75 | variableDependencies(*dep, dependencies); |
231 | } | ||
232 | } | ||
233 | } | ||
234 |
2/2✓ Branch 0 taken 145782 times.
✓ Branch 1 taken 4867 times.
|
150649 | else if (type == ast::Node::FunctionCallNode) { |
235 | written = true; | ||
236 | // @todo We currently can't detect if attributes are being passed by | ||
237 | // pointer and being modified automatically. We have to link this | ||
238 | // attribute to any other attribute passes into the function | ||
239 | const ast::FunctionCall* call = | ||
240 | static_cast<const ast::FunctionCall*>(parent); | ||
241 | // traverse down and collect variables | ||
242 | std::vector<const ast::Variable*> vars; | ||
243 |
2/2✓ Branch 0 taken 6614 times.
✓ Branch 1 taken 4867 times.
|
11481 | for (size_t i = 0; i < call->children(); ++i) { |
244 |
1/2✓ Branch 1 taken 6614 times.
✗ Branch 2 not taken.
|
6614 | collectNodeTypes<ListT>(*call->child(i), vars); |
245 | } | ||
246 | // only append dependencies here if they haven't already been visited | ||
247 | // due to recursion issues | ||
248 |
2/2✓ Branch 0 taken 5971 times.
✓ Branch 1 taken 4867 times.
|
10838 | for (const ast::Variable* dep : vars) { |
249 | // make sure the dep doesn't already exist in the container, otherwise | ||
250 | // we can get into issues where functions with multiple arguments | ||
251 | // constantly try to check themselves | ||
252 | // @note should be removed with function refactoring | ||
253 |
2/2✓ Branch 1 taken 4628 times.
✓ Branch 2 taken 1343 times.
|
5971 | if (hasDep(dep)) continue; |
254 |
1/2✓ Branch 1 taken 1343 times.
✗ Branch 2 not taken.
|
1343 | dependencies.emplace_back(dep); |
255 |
1/2✓ Branch 1 taken 1343 times.
✗ Branch 2 not taken.
|
1343 | variableDependencies(*dep, dependencies); |
256 | } | ||
257 | } | ||
258 | child = parent; | ||
259 | } | ||
260 | } | ||
261 | } | ||
262 | |||
263 | |||
264 | } // anonymous namespace | ||
265 | |||
266 | ✗ | bool usesAttribute(const ast::Node& node, | |
267 | const std::string& name, | ||
268 | const tokens::CoreType type) | ||
269 | { | ||
270 | ✗ | bool found = false; | |
271 | ✗ | visitNodeType<ast::Attribute>(node, | |
272 | ✗ | [&](const ast::Attribute& attrib) -> bool { | |
273 | ✗ | assert(!found); | |
274 | ✗ | if (type != tokens::UNKNOWN) { | |
275 | ✗ | if (attrib.type() != type) return true; | |
276 | } | ||
277 | ✗ | if (attrib.name() != name) return true; | |
278 | ✗ | found = true; | |
279 | ✗ | return false; | |
280 | }); | ||
281 | |||
282 | ✗ | return found; | |
283 | } | ||
284 | |||
285 | ✗ | bool writesToAttribute(const ast::Node& node, | |
286 | const std::string& name, | ||
287 | const tokens::CoreType type) | ||
288 | { | ||
289 | std::vector<const ast::Variable*> vars; | ||
290 | ✗ | catalogueVariables(node, nullptr, &vars, &vars, false, true); | |
291 | |||
292 | // See if any attributes in the result vec match the given name/type | ||
293 | ✗ | for (const ast::Variable* var : vars) { | |
294 | ✗ | assert(var->isType<ast::Attribute>()); | |
295 | const ast::Attribute* attrib = static_cast<const ast::Attribute*>(var); | ||
296 | ✗ | if (type != tokens::UNKNOWN) { | |
297 | ✗ | if (attrib->type() != type) continue; | |
298 | } | ||
299 | ✗ | if (attrib->name() != name) continue; | |
300 | ✗ | return true; | |
301 | } | ||
302 | |||
303 | ✗ | return false; | |
304 | } | ||
305 | |||
306 |
1/2✓ Branch 0 taken 1537 times.
✗ Branch 1 not taken.
|
1537 | void catalogueVariables(const ast::Node& node, |
307 | std::vector<const ast::Variable*>* readOnly, | ||
308 | std::vector<const ast::Variable*>* writeOnly, | ||
309 | std::vector<const ast::Variable*>* readWrite, | ||
310 | const bool locals, | ||
311 | const bool attributes) | ||
312 | { | ||
313 | std::vector<const ast::Variable*> vars; | ||
314 | |||
315 | if (locals) { | ||
316 | collectNodeTypes<ast::Local>(node, vars); | ||
317 | } | ||
318 |
1/2✓ Branch 0 taken 1537 times.
✗ Branch 1 not taken.
|
1537 | if (attributes) { |
319 | collectNodeType<ast::Attribute>(node, vars); | ||
320 | } | ||
321 | |||
322 |
2/2✓ Branch 0 taken 12091 times.
✓ Branch 1 taken 1537 times.
|
13628 | for (const ast::Variable* var : vars) { |
323 | // traverse upwards, see if we're embedded in an assign or crement expression | ||
324 | const ast::Node* child = var; | ||
325 | const ast::Node* parent = child->parent(); | ||
326 | bool read = false, write = false; | ||
327 |
4/4✓ Branch 0 taken 35673 times.
✓ Branch 1 taken 1176 times.
✓ Branch 2 taken 24758 times.
✓ Branch 3 taken 10915 times.
|
36849 | while (parent && !(write && read)) { |
328 |
1/2✓ Branch 1 taken 24758 times.
✗ Branch 2 not taken.
|
24758 | const ast::Node::NodeType type = parent->nodetype(); |
329 | // crement operations read and write | ||
330 |
2/2✓ Branch 0 taken 24158 times.
✓ Branch 1 taken 600 times.
|
24758 | if (type == ast::Node::CrementNode) { |
331 | read = write = true; | ||
332 | } | ||
333 |
2/2✓ Branch 0 taken 11545 times.
✓ Branch 1 taken 12613 times.
|
24158 | else if (type == ast::Node::AssignExpressionNode) { |
334 | const ast::AssignExpression* assignment = | ||
335 | static_cast<const ast::AssignExpression*>(parent); | ||
336 |
2/2✓ Branch 0 taken 10291 times.
✓ Branch 1 taken 1254 times.
|
11545 | if (assignment->lhs() == child) { |
337 |
2/2✓ Branch 0 taken 9196 times.
✓ Branch 1 taken 1095 times.
|
10291 | if (assignment->isCompound()) { |
338 | // +=, *=, /= etc | ||
339 | read = write = true; | ||
340 | } | ||
341 | else { | ||
342 | // op = op | ||
343 | write = true; | ||
344 | } | ||
345 | } | ||
346 | else { | ||
347 | read = true; | ||
348 | } | ||
349 | } | ||
350 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12613 times.
|
12613 | else if (type == ast::Node::DeclareLocalNode) { |
351 | const ast::DeclareLocal* declareLocal = | ||
352 | static_cast<const ast::DeclareLocal*>(parent); | ||
353 | ✗ | if (declareLocal->local() == child) { | |
354 | ✗ | if (declareLocal->hasInit()) { | |
355 | write = true; | ||
356 | } | ||
357 | } | ||
358 | } | ||
359 |
2/2✓ Branch 0 taken 12589 times.
✓ Branch 1 taken 24 times.
|
12613 | else if (type == ast::Node::FunctionCallNode) { |
360 | // @todo We currently can't detect if attributes are being passed by | ||
361 | // pointer and being modified automatically. This is a major limitation | ||
362 | // as it means any attribute passed into any function directly must | ||
363 | // be marked as writeable | ||
364 | read = write = true; | ||
365 | } | ||
366 | else { | ||
367 | read = true; | ||
368 | } | ||
369 | child = parent; | ||
370 | parent = child->parent(); | ||
371 | } | ||
372 | |||
373 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12091 times.
|
12091 | assert(read || write); |
374 |
4/6✓ Branch 0 taken 12091 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 10915 times.
✓ Branch 3 taken 1176 times.
✓ Branch 5 taken 10915 times.
✗ Branch 6 not taken.
|
12091 | if (readWrite && read && write) readWrite->emplace_back(var); |
375 |
4/6✓ Branch 0 taken 12091 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1176 times.
✓ Branch 3 taken 10915 times.
✓ Branch 5 taken 1176 times.
✗ Branch 6 not taken.
|
12091 | if (readOnly && read && !write) readOnly->emplace_back(var); |
376 |
1/6✗ Branch 0 not taken.
✓ Branch 1 taken 12091 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
|
12091 | if (writeOnly && !read && write) writeOnly->emplace_back(var); |
377 | } | ||
378 | 1537 | } | |
379 | |||
380 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1537 times.
|
1537 | void catalogueAttributeTokens(const ast::Node& node, |
381 | std::vector<std::string>* readOnly, | ||
382 | std::vector<std::string>* writeOnly, | ||
383 | std::vector<std::string>* readWrite) | ||
384 | { | ||
385 | std::vector<const ast::Variable*> readOnlyVars; | ||
386 | std::vector<const ast::Variable*> writeOnlyVars; | ||
387 | std::vector<const ast::Variable*> readWriteVars; | ||
388 |
4/8✗ Branch 0 not taken.
✓ Branch 1 taken 1537 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1537 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1537 times.
✓ Branch 7 taken 1537 times.
✗ Branch 8 not taken.
|
1537 | catalogueVariables(node, |
389 | (readOnly ? &readOnlyVars : nullptr), | ||
390 | (writeOnly ? &writeOnlyVars : nullptr), | ||
391 | (readWrite ? &readWriteVars : nullptr), | ||
392 | false, // locals | ||
393 | true); // attributes | ||
394 | |||
395 | // fill a single map with the access patterns for all attributes | ||
396 | // .first = read, .second = write | ||
397 | // @note use a map rather than an unordered_map to preserve order | ||
398 | // of the output vectors on different platforms (the AX compiler | ||
399 | // doesn't care about the order but it's reasonable to expect | ||
400 | // an attribute has the same index from one platform to the next). | ||
401 | std::map<std::string, std::pair<bool,bool>> accessmap; | ||
402 | |||
403 | 4611 | auto addAccesses = [&](const std::vector<const ast::Variable*>& vars, | |
404 | const bool read, | ||
405 | const bool write) | ||
406 | { | ||
407 |
2/2✓ Branch 0 taken 12091 times.
✓ Branch 1 taken 4611 times.
|
16702 | for (const ast::Variable* var : vars) { |
408 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12091 times.
|
12091 | assert(var->isType<ast::Attribute>()); |
409 | const ast::Attribute* attrib = static_cast<const ast::Attribute*>(var); | ||
410 |
1/2✓ Branch 1 taken 12091 times.
✗ Branch 2 not taken.
|
12091 | auto& access = accessmap[attrib->tokenname()]; |
411 | 12091 | access.first |= read; | |
412 | 12091 | access.second |= write; | |
413 | } | ||
414 | 4611 | }; | |
415 | |||
416 |
1/2✓ Branch 1 taken 1537 times.
✗ Branch 2 not taken.
|
1537 | addAccesses(readWriteVars, true, true); |
417 |
1/2✓ Branch 1 taken 1537 times.
✗ Branch 2 not taken.
|
1537 | addAccesses(writeOnlyVars, false, true); |
418 |
1/2✓ Branch 1 taken 1537 times.
✗ Branch 2 not taken.
|
1537 | addAccesses(readOnlyVars, true, false); |
419 | |||
420 | // set the results from the access map | ||
421 |
2/2✓ Branch 0 taken 10149 times.
✓ Branch 1 taken 1537 times.
|
11686 | for (const auto& result : accessmap) { |
422 | const std::pair<bool,bool>& pair = result.second; | ||
423 |
4/6✓ Branch 0 taken 10149 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 10149 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 9860 times.
✓ Branch 5 taken 289 times.
|
10149 | if (readWrite && pair.first && pair.second) { |
424 |
1/2✓ Branch 1 taken 9860 times.
✗ Branch 2 not taken.
|
9860 | readWrite->emplace_back(result.first); |
425 | } | ||
426 |
2/6✓ Branch 0 taken 289 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 289 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
289 | else if (writeOnly && !pair.first && pair.second) { |
427 | ✗ | writeOnly->emplace_back(result.first); | |
428 | } | ||
429 |
3/6✓ Branch 0 taken 289 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 289 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 289 times.
✗ Branch 5 not taken.
|
289 | else if (readOnly && pair.first && !pair.second) { |
430 |
1/2✓ Branch 1 taken 289 times.
✗ Branch 2 not taken.
|
289 | readOnly->emplace_back(result.first); |
431 | } | ||
432 | } | ||
433 | 1537 | } | |
434 | |||
435 | template <bool First> | ||
436 | struct UseVisitor : | ||
437 | public ast::Visitor<UseVisitor<First>> | ||
438 | { | ||
439 | using ast::Visitor<UseVisitor<First>>::traverse; | ||
440 | using ast::Visitor<UseVisitor<First>>::visit; | ||
441 | |||
442 | // reverse the ast traversal if !First | ||
443 | inline bool reverseChildVisits() const { return !First; } | ||
444 | |||
445 | 20604 | UseVisitor(const std::string& tokenOrName) | |
446 | : mToken(tokenOrName) | ||
447 | , mAttribute(false) | ||
448 |
1/2✓ Branch 2 taken 10302 times.
✗ Branch 3 not taken.
|
20604 | , mVar(nullptr) { |
449 | // rebuild the expected token if necessary | ||
450 | std::string name, type; | ||
451 |
1/2✓ Branch 1 taken 10302 times.
✗ Branch 2 not taken.
|
20604 | mAttribute = ast::Attribute::nametypeFromToken(mToken, &name, &type); |
452 |
1/2✓ Branch 0 taken 10302 times.
✗ Branch 1 not taken.
|
20604 | if (mAttribute) { |
453 |
1/2✓ Branch 1 taken 10302 times.
✗ Branch 2 not taken.
|
41208 | mToken = type + ast::Attribute::symbolseparator() + name; |
454 | } | ||
455 | 20604 | } | |
456 | ~UseVisitor() = default; | ||
457 | |||
458 | 1100 | bool traverse(const ast::Loop* loop) | |
459 | { | ||
460 |
1/2✓ Branch 0 taken 550 times.
✗ Branch 1 not taken.
|
1100 | if (!loop) return true; |
461 | const ast::tokens::LoopToken type = loop->loopType(); | ||
462 |
2/2✓ Branch 0 taken 38 times.
✓ Branch 1 taken 512 times.
|
1100 | if (type == ast::tokens::DO) { |
463 | if (!this->reverseChildVisits()) { | ||
464 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
2 | if (!this->traverse(loop->body())) return false; |
465 | ✗ | if (!this->traverse(loop->condition())) return false; | |
466 | } | ||
467 | else { | ||
468 |
2/2✓ Branch 1 taken 36 times.
✓ Branch 2 taken 1 times.
|
74 | if (!this->traverse(loop->condition())) return false; |
469 |
1/2✓ Branch 0 taken 36 times.
✗ Branch 1 not taken.
|
72 | if (!this->traverse(loop->body())) return false; |
470 | } | ||
471 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 36 times.
|
72 | assert(!loop->initial()); |
472 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 36 times.
|
72 | assert(!loop->iteration()); |
473 | } | ||
474 | else { | ||
475 | if (!this->reverseChildVisits()) { | ||
476 |
2/2✓ Branch 1 taken 11 times.
✓ Branch 2 taken 2 times.
|
26 | if (!this->traverse(loop->initial())) return false; |
477 |
2/2✓ Branch 1 taken 10 times.
✓ Branch 2 taken 1 times.
|
22 | if (!this->traverse(loop->condition())) return false; |
478 |
2/2✓ Branch 1 taken 9 times.
✓ Branch 2 taken 1 times.
|
20 | if (!this->traverse(loop->iteration())) return false; |
479 |
1/2✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
|
18 | if (!this->traverse(loop->body())) return false; |
480 | } | ||
481 | else { | ||
482 |
2/2✓ Branch 0 taken 462 times.
✓ Branch 1 taken 37 times.
|
998 | if (!this->traverse(loop->body())) return false; |
483 |
2/2✓ Branch 1 taken 460 times.
✓ Branch 2 taken 2 times.
|
924 | if (!this->traverse(loop->iteration())) return false; |
484 |
2/2✓ Branch 1 taken 458 times.
✓ Branch 2 taken 2 times.
|
920 | if (!this->traverse(loop->condition())) return false; |
485 |
2/2✓ Branch 1 taken 2 times.
✓ Branch 2 taken 456 times.
|
916 | if (!this->traverse(loop->initial())) return false; |
486 | } | ||
487 | } | ||
488 | |||
489 | if (!this->visit(loop)) return false; | ||
490 | return true; | ||
491 | } | ||
492 | |||
493 | 154678 | inline bool visit(const ast::Attribute* node) { | |
494 |
1/2✓ Branch 0 taken 77339 times.
✗ Branch 1 not taken.
|
154678 | if (!mAttribute) return true; |
495 |
2/2✓ Branch 1 taken 10302 times.
✓ Branch 2 taken 67037 times.
|
309356 | if (node->tokenname() != mToken) return true; |
496 | 20604 | mVar = node; | |
497 | 20604 | return false; | |
498 | } | ||
499 | inline bool visit(const ast::Local* node) { | ||
500 |
2/4✗ Branch 0 not taken.
✓ Branch 1 taken 41217 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 25 times.
|
41242 | if (mAttribute) return true; |
501 | ✗ | if (node->name() != mToken) return true; | |
502 | ✗ | mVar = node; | |
503 | ✗ | return false; | |
504 | } | ||
505 | |||
506 | 10302 | const ast::Variable* var() const { return mVar; } | |
507 | private: | ||
508 | std::string mToken; | ||
509 | bool mAttribute; | ||
510 | const ast::Variable* mVar; | ||
511 | }; | ||
512 | |||
513 | 10212 | void attributeDependencyTokens(const ast::Tree& tree, | |
514 | const std::string& name, | ||
515 | const tokens::CoreType type, | ||
516 | std::vector<std::string>& dependencies) | ||
517 | { | ||
518 | 10212 | const std::string token = ast::Attribute::tokenFromNameType(name, type); | |
519 |
1/2✓ Branch 1 taken 10212 times.
✗ Branch 2 not taken.
|
10212 | const ast::Variable* var = lastUse(tree, token); |
520 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 10212 times.
|
10212 | if (!var) return; |
521 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 10212 times.
|
10212 | assert(var->isType<ast::Attribute>()); |
522 | |||
523 | std::vector<const ast::Variable*> deps; | ||
524 |
1/2✓ Branch 1 taken 10212 times.
✗ Branch 2 not taken.
|
10212 | variableDependencies(*var, deps); |
525 | |||
526 |
2/2✓ Branch 0 taken 11957 times.
✓ Branch 1 taken 10212 times.
|
22169 | for (const auto& dep : deps) { |
527 |
3/4✓ Branch 1 taken 11957 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 7768 times.
✓ Branch 4 taken 4189 times.
|
11957 | if (dep->nodetype() != ast::Node::AttributeNode) continue; |
528 |
2/6✓ Branch 1 taken 4189 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4189 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
|
8378 | dependencies.emplace_back(static_cast<const ast::Attribute*>(dep)->tokenname()); |
529 | } | ||
530 | |||
531 | 10212 | std::sort(dependencies.begin(), dependencies.end()); | |
532 | 10212 | auto iter = std::unique(dependencies.begin(), dependencies.end()); | |
533 |
2/2✓ Branch 1 taken 5638 times.
✓ Branch 2 taken 4574 times.
|
10212 | dependencies.erase(iter, dependencies.end()); |
534 | } | ||
535 | |||
536 | 45 | const ast::Variable* firstUse(const ast::Node& node, const std::string& tokenOrName) | |
537 | { | ||
538 | 45 | UseVisitor<true> visitor(tokenOrName); | |
539 |
1/2✓ Branch 1 taken 45 times.
✗ Branch 2 not taken.
|
45 | visitor.traverse(&node); |
540 | 45 | return visitor.var(); | |
541 | } | ||
542 | |||
543 | 10257 | const ast::Variable* lastUse(const ast::Node& node, const std::string& tokenOrName) | |
544 | { | ||
545 | 10257 | UseVisitor<false> visitor(tokenOrName); | |
546 |
1/2✓ Branch 1 taken 10257 times.
✗ Branch 2 not taken.
|
10257 | visitor.traverse(&node); |
547 | 10257 | return visitor.var(); | |
548 | } | ||
549 | |||
550 | 5232 | bool callsFunction(const ast::Node& node, const std::string& name) | |
551 | { | ||
552 | 5232 | bool found = false; | |
553 | 5232 | visitNodeType<ast::FunctionCall>(node, | |
554 | [&](const ast::FunctionCall& call) -> bool { | ||
555 |
2/2✓ Branch 0 taken 23 times.
✓ Branch 1 taken 17098 times.
|
17121 | if (call.name() != name) return true; |
556 | 23 | found = true; | |
557 | 23 | return false; | |
558 | }); | ||
559 | |||
560 | 5232 | return found; | |
561 | } | ||
562 | |||
563 | 930 | void linearize(const ast::Node& node, std::vector<const ast::Node*>& list) | |
564 | { | ||
565 | collectNodeType<ast::Node>(node, list); | ||
566 | 930 | } | |
567 | |||
568 | } // namespace ast | ||
569 | } // namespace ax | ||
570 | } // namespace OPENVDB_VERSION_NAME | ||
571 | } // namespace openvdb | ||
572 | |||
573 | |||
574 |