CallDAG.cpp (9702B)
1 // 2 // Copyright 2002 The ANGLE Project Authors. All rights reserved. 3 // Use of this source code is governed by a BSD-style license that can be 4 // found in the LICENSE file. 5 // 6 7 // CallDAG.h: Implements a call graph DAG of functions to be re-used accross 8 // analyses, allows to efficiently traverse the functions in topological 9 // order. 10 11 #include "compiler/translator/CallDAG.h" 12 13 #include "compiler/translator/Diagnostics.h" 14 #include "compiler/translator/SymbolTable.h" 15 #include "compiler/translator/tree_util/IntermTraverse.h" 16 17 namespace sh 18 { 19 20 // The CallDAGCreator does all the processing required to create the CallDAG 21 // structure so that the latter contains only the necessary variables. 22 class CallDAG::CallDAGCreator : public TIntermTraverser 23 { 24 public: 25 CallDAGCreator(TDiagnostics *diagnostics) 26 : TIntermTraverser(true, false, false), 27 mDiagnostics(diagnostics), 28 mCurrentFunction(nullptr), 29 mCurrentIndex(0) 30 {} 31 32 InitResult assignIndices() 33 { 34 int skipped = 0; 35 for (auto &it : mFunctions) 36 { 37 // Skip unimplemented functions 38 if (it.second.definitionNode) 39 { 40 InitResult result = assignIndicesInternal(&it.second); 41 if (result != INITDAG_SUCCESS) 42 { 43 return result; 44 } 45 } 46 else 47 { 48 skipped++; 49 } 50 } 51 52 ASSERT(mFunctions.size() == mCurrentIndex + skipped); 53 return INITDAG_SUCCESS; 54 } 55 56 void fillDataStructures(std::vector<Record> *records, std::map<int, int> *idToIndex) 57 { 58 ASSERT(records->empty()); 59 ASSERT(idToIndex->empty()); 60 61 records->resize(mCurrentIndex); 62 63 for (auto &it : mFunctions) 64 { 65 CreatorFunctionData &data = it.second; 66 // Skip unimplemented functions 67 if (!data.definitionNode) 68 { 69 continue; 70 } 71 ASSERT(data.index < records->size()); 72 Record &record = (*records)[data.index]; 73 74 record.node = data.definitionNode; 75 76 record.callees.reserve(data.callees.size()); 77 for (auto &callee : data.callees) 78 { 79 record.callees.push_back(static_cast<int>(callee->index)); 80 } 81 82 (*idToIndex)[it.first] = static_cast<int>(data.index); 83 } 84 } 85 86 private: 87 struct CreatorFunctionData 88 { 89 CreatorFunctionData() 90 : definitionNode(nullptr), name(""), index(0), indexAssigned(false), visiting(false) 91 {} 92 93 std::set<CreatorFunctionData *> callees; 94 TIntermFunctionDefinition *definitionNode; 95 ImmutableString name; 96 size_t index; 97 bool indexAssigned; 98 bool visiting; 99 }; 100 101 bool visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) override 102 { 103 // Create the record if need be and remember the definition node. 104 mCurrentFunction = &mFunctions[node->getFunction()->uniqueId().get()]; 105 // Name will be overwritten here. If we've already traversed the prototype of this function, 106 // it should have had the same name. 107 ASSERT(mCurrentFunction->name == "" || 108 mCurrentFunction->name == node->getFunction()->name()); 109 mCurrentFunction->name = node->getFunction()->name(); 110 mCurrentFunction->definitionNode = node; 111 112 node->getBody()->traverse(this); 113 mCurrentFunction = nullptr; 114 return false; 115 } 116 117 void visitFunctionPrototype(TIntermFunctionPrototype *node) override 118 { 119 ASSERT(mCurrentFunction == nullptr); 120 121 // Function declaration, create an empty record. 122 auto &record = mFunctions[node->getFunction()->uniqueId().get()]; 123 record.name = node->getFunction()->name(); 124 } 125 126 // Track functions called from another function. 127 bool visitAggregate(Visit visit, TIntermAggregate *node) override 128 { 129 if (node->getOp() == EOpCallFunctionInAST) 130 { 131 // Function call, add the callees 132 auto it = mFunctions.find(node->getFunction()->uniqueId().get()); 133 ASSERT(it != mFunctions.end()); 134 135 // We might be traversing the initializer of a global variable. Even though function 136 // calls in global scope are forbidden by the parser, some subsequent AST 137 // transformations can add them to emulate particular features. 138 if (mCurrentFunction) 139 { 140 mCurrentFunction->callees.insert(&it->second); 141 } 142 } 143 return true; 144 } 145 146 // Recursively assigns indices to a sub DAG 147 InitResult assignIndicesInternal(CreatorFunctionData *root) 148 { 149 // Iterative implementation of the index assignment algorithm. A recursive version 150 // would be prettier but since the CallDAG creation runs before the limiting of the 151 // call depth, we might get stack overflows (computation of the call depth uses the 152 // CallDAG). 153 154 ASSERT(root); 155 156 if (root->indexAssigned) 157 { 158 return INITDAG_SUCCESS; 159 } 160 161 // If we didn't have to detect recursion, functionsToProcess could be a simple queue 162 // in which we add the function being processed's callees. However in order to detect 163 // recursion we need to know which functions we are currently visiting. For that reason 164 // functionsToProcess will look like a concatenation of segments of the form 165 // [F visiting = true, subset of F callees with visiting = false] and the following 166 // segment (if any) will be start with a callee of F. 167 // This way we can remember when we started visiting a function, to put visiting back 168 // to false. 169 TVector<CreatorFunctionData *> functionsToProcess; 170 functionsToProcess.push_back(root); 171 172 InitResult result = INITDAG_SUCCESS; 173 174 std::stringstream errorStream = sh::InitializeStream<std::stringstream>(); 175 176 while (!functionsToProcess.empty()) 177 { 178 CreatorFunctionData *function = functionsToProcess.back(); 179 180 if (function->visiting) 181 { 182 function->visiting = false; 183 function->index = mCurrentIndex++; 184 function->indexAssigned = true; 185 186 functionsToProcess.pop_back(); 187 continue; 188 } 189 190 if (!function->definitionNode) 191 { 192 errorStream << "Undefined function '" << function->name 193 << "()' used in the following call chain:"; 194 result = INITDAG_UNDEFINED; 195 break; 196 } 197 198 if (function->indexAssigned) 199 { 200 functionsToProcess.pop_back(); 201 continue; 202 } 203 204 function->visiting = true; 205 206 for (auto callee : function->callees) 207 { 208 functionsToProcess.push_back(callee); 209 210 // Check if the callee is already being visited after pushing it so that it appears 211 // in the chain printed in the info log. 212 if (callee->visiting) 213 { 214 errorStream << "Recursive function call in the following call chain:"; 215 result = INITDAG_RECURSION; 216 break; 217 } 218 } 219 220 if (result != INITDAG_SUCCESS) 221 { 222 break; 223 } 224 } 225 226 // The call chain is made of the function we were visiting when the error was detected. 227 if (result != INITDAG_SUCCESS) 228 { 229 bool first = true; 230 for (auto function : functionsToProcess) 231 { 232 if (function->visiting) 233 { 234 if (!first) 235 { 236 errorStream << " -> "; 237 } 238 errorStream << function->name << ")"; 239 first = false; 240 } 241 } 242 if (mDiagnostics) 243 { 244 std::string errorStr = errorStream.str(); 245 mDiagnostics->globalError(errorStr.c_str()); 246 } 247 } 248 249 return result; 250 } 251 252 TDiagnostics *mDiagnostics; 253 254 std::map<int, CreatorFunctionData> mFunctions; 255 CreatorFunctionData *mCurrentFunction; 256 size_t mCurrentIndex; 257 }; 258 259 // CallDAG 260 261 CallDAG::CallDAG() {} 262 263 CallDAG::~CallDAG() {} 264 265 const size_t CallDAG::InvalidIndex = std::numeric_limits<size_t>::max(); 266 267 size_t CallDAG::findIndex(const TSymbolUniqueId &id) const 268 { 269 auto it = mFunctionIdToIndex.find(id.get()); 270 271 if (it == mFunctionIdToIndex.end()) 272 { 273 return InvalidIndex; 274 } 275 else 276 { 277 return it->second; 278 } 279 } 280 281 const CallDAG::Record &CallDAG::getRecordFromIndex(size_t index) const 282 { 283 ASSERT(index != InvalidIndex && index < mRecords.size()); 284 return mRecords[index]; 285 } 286 287 size_t CallDAG::size() const 288 { 289 return mRecords.size(); 290 } 291 292 void CallDAG::clear() 293 { 294 mRecords.clear(); 295 mFunctionIdToIndex.clear(); 296 } 297 298 CallDAG::InitResult CallDAG::init(TIntermNode *root, TDiagnostics *diagnostics) 299 { 300 CallDAGCreator creator(diagnostics); 301 302 // Creates the mapping of functions to callees 303 root->traverse(&creator); 304 305 // Does the topological sort and detects recursions 306 InitResult result = creator.assignIndices(); 307 if (result != INITDAG_SUCCESS) 308 { 309 return result; 310 } 311 312 creator.fillDataStructures(&mRecords, &mFunctionIdToIndex); 313 return INITDAG_SUCCESS; 314 } 315 316 } // namespace sh