| 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 |