blob: 8557fdf6c8e965fcd4770a9740466219a7076736 [file] [log] [blame]
Corentin Wallez71d147f2015-02-11 11:15:24 -08001//
2// Copyright (c) 2002-2015 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"
Olli Etuahofe486322017-03-21 09:30:54 +000012
Olli Etuaho77ba4082016-12-16 12:01:18 +000013#include "compiler/translator/Diagnostics.h"
Olli Etuahofe486322017-03-21 09:30:54 +000014#include "compiler/translator/SymbolTable.h"
Corentin Wallez71d147f2015-02-11 11:15:24 -080015
Jamie Madill45bcc782016-11-07 13:58:48 -050016namespace sh
17{
18
Corentin Wallez71d147f2015-02-11 11:15:24 -080019// The CallDAGCreator does all the processing required to create the CallDAG
20// structure so that the latter contains only the necessary variables.
21class CallDAG::CallDAGCreator : public TIntermTraverser
22{
23 public:
Olli Etuaho77ba4082016-12-16 12:01:18 +000024 CallDAGCreator(TDiagnostics *diagnostics)
Corentin Wallez71d147f2015-02-11 11:15:24 -080025 : TIntermTraverser(true, false, true),
Olli Etuaho77ba4082016-12-16 12:01:18 +000026 mDiagnostics(diagnostics),
Corentin Wallez71d147f2015-02-11 11:15:24 -080027 mCurrentFunction(nullptr),
Corentin Wallezbc99bb62015-05-14 17:42:20 -040028 mCurrentIndex(0)
Corentin Wallez71d147f2015-02-11 11:15:24 -080029 {
30 }
31
32 InitResult assignIndices()
33 {
34 int skipped = 0;
35 for (auto &it : mFunctions)
36 {
37 // Skip unimplemented functions
38 if (it.second.node)
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 }
Corentin Walleza59fcdf2016-09-14 10:52:14 -040051
Corentin Wallez71d147f2015-02-11 11:15:24 -080052 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.node)
68 {
69 continue;
70 }
71 ASSERT(data.index < records->size());
72 Record &record = (*records)[data.index];
73
74 record.name = data.name.data();
75 record.node = data.node;
76
77 record.callees.reserve(data.callees.size());
78 for (auto &callee : data.callees)
79 {
Cooper Partin4d61f7e2015-08-12 10:56:50 -070080 record.callees.push_back(static_cast<int>(callee->index));
Corentin Wallez71d147f2015-02-11 11:15:24 -080081 }
82
Olli Etuahofe486322017-03-21 09:30:54 +000083 (*idToIndex)[data.node->getFunctionSymbolInfo()->getId().get()] =
Olli Etuahobd674552016-10-06 13:28:42 +010084 static_cast<int>(data.index);
Corentin Wallez71d147f2015-02-11 11:15:24 -080085 }
86 }
87
88 private:
Corentin Wallez71d147f2015-02-11 11:15:24 -080089 struct CreatorFunctionData
90 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -050091 CreatorFunctionData() : node(nullptr), index(0), indexAssigned(false), visiting(false) {}
Corentin Wallez71d147f2015-02-11 11:15:24 -080092
Jamie Madilld7b1ab52016-12-12 14:42:19 -050093 std::set<CreatorFunctionData *> callees;
Olli Etuaho336b1472016-10-05 16:37:55 +010094 TIntermFunctionDefinition *node;
Corentin Wallez71d147f2015-02-11 11:15:24 -080095 TString name;
96 size_t index;
97 bool indexAssigned;
98 bool visiting;
99 };
100
Olli Etuaho336b1472016-10-05 16:37:55 +0100101 bool visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) override
102 {
103 // Create the record if need be and remember the node.
104 if (visit == PreVisit)
105 {
Olli Etuahofe486322017-03-21 09:30:54 +0000106 auto it = mFunctions.find(node->getFunctionSymbolInfo()->getId().get());
Olli Etuaho336b1472016-10-05 16:37:55 +0100107
108 if (it == mFunctions.end())
109 {
Olli Etuahofe486322017-03-21 09:30:54 +0000110 mCurrentFunction = &mFunctions[node->getFunctionSymbolInfo()->getId().get()];
111 mCurrentFunction->name = node->getFunctionSymbolInfo()->getName();
Olli Etuaho336b1472016-10-05 16:37:55 +0100112 }
113 else
114 {
115 mCurrentFunction = &it->second;
Olli Etuahofe486322017-03-21 09:30:54 +0000116 ASSERT(mCurrentFunction->name == node->getFunctionSymbolInfo()->getName());
Olli Etuaho336b1472016-10-05 16:37:55 +0100117 }
118
119 mCurrentFunction->node = node;
Olli Etuaho336b1472016-10-05 16:37:55 +0100120 }
121 else if (visit == PostVisit)
122 {
123 mCurrentFunction = nullptr;
124 }
125 return true;
126 }
127
Olli Etuaho16c745a2017-01-16 17:02:27 +0000128 bool visitFunctionPrototype(Visit visit, TIntermFunctionPrototype *node) override
129 {
130 ASSERT(visit == PreVisit);
Olli Etuahofe486322017-03-21 09:30:54 +0000131 if (mCurrentFunction != nullptr)
132 {
133 return false;
134 }
135
Olli Etuaho16c745a2017-01-16 17:02:27 +0000136 // Function declaration, create an empty record.
Olli Etuahofe486322017-03-21 09:30:54 +0000137 auto &record = mFunctions[node->getFunctionSymbolInfo()->getId().get()];
Olli Etuaho16c745a2017-01-16 17:02:27 +0000138 record.name = node->getFunctionSymbolInfo()->getName();
139
140 // No need to traverse the parameters.
141 return false;
142 }
143
Corentin Wallez71d147f2015-02-11 11:15:24 -0800144 // Aggregates the AST node for each function as well as the name of the functions called by it
145 bool visitAggregate(Visit visit, TIntermAggregate *node) override
146 {
Olli Etuaho1ecd14b2017-01-26 13:54:15 -0800147 if (visit == PreVisit && node->getOp() == EOpCallFunctionInAST)
Corentin Wallez71d147f2015-02-11 11:15:24 -0800148 {
Olli Etuaho1ecd14b2017-01-26 13:54:15 -0800149 // Function call, add the callees
Olli Etuahofe486322017-03-21 09:30:54 +0000150 auto it = mFunctions.find(node->getFunctionSymbolInfo()->getId().get());
Olli Etuaho1ecd14b2017-01-26 13:54:15 -0800151 ASSERT(it != mFunctions.end());
Corentin Wallez71d147f2015-02-11 11:15:24 -0800152
Olli Etuahofe486322017-03-21 09:30:54 +0000153 // We might be traversing the initializer of a global variable. Even though function
154 // calls in global scope are forbidden by the parser, some subsequent AST
155 // transformations can add them to emulate particular features.
Olli Etuaho1ecd14b2017-01-26 13:54:15 -0800156 if (mCurrentFunction)
157 {
158 mCurrentFunction->callees.insert(&it->second);
Corentin Wallez71d147f2015-02-11 11:15:24 -0800159 }
Corentin Wallez71d147f2015-02-11 11:15:24 -0800160 }
161 return true;
162 }
163
164 // Recursively assigns indices to a sub DAG
Corentin Walleza59fcdf2016-09-14 10:52:14 -0400165 InitResult assignIndicesInternal(CreatorFunctionData *root)
Corentin Wallez71d147f2015-02-11 11:15:24 -0800166 {
Corentin Walleza59fcdf2016-09-14 10:52:14 -0400167 // Iterative implementation of the index assignment algorithm. A recursive version
168 // would be prettier but since the CallDAG creation runs before the limiting of the
169 // call depth, we might get stack overflows (computation of the call depth uses the
170 // CallDAG).
Corentin Wallez71d147f2015-02-11 11:15:24 -0800171
Corentin Walleza59fcdf2016-09-14 10:52:14 -0400172 ASSERT(root);
Corentin Wallez71d147f2015-02-11 11:15:24 -0800173
Corentin Walleza59fcdf2016-09-14 10:52:14 -0400174 if (root->indexAssigned)
Corentin Wallez71d147f2015-02-11 11:15:24 -0800175 {
176 return INITDAG_SUCCESS;
177 }
178
Corentin Walleza59fcdf2016-09-14 10:52:14 -0400179 // If we didn't have to detect recursion, functionsToProcess could be a simple queue
180 // in which we add the function being processed's callees. However in order to detect
181 // recursion we need to know which functions we are currently visiting. For that reason
182 // functionsToProcess will look like a concatenation of segments of the form
183 // [F visiting = true, subset of F callees with visiting = false] and the following
184 // segment (if any) will be start with a callee of F.
185 // This way we can remember when we started visiting a function, to put visiting back
186 // to false.
187 TVector<CreatorFunctionData *> functionsToProcess;
188 functionsToProcess.push_back(root);
Corentin Wallez71d147f2015-02-11 11:15:24 -0800189
Corentin Walleza59fcdf2016-09-14 10:52:14 -0400190 InitResult result = INITDAG_SUCCESS;
191
Olli Etuaho77ba4082016-12-16 12:01:18 +0000192 std::stringstream errorStream;
193
Corentin Walleza59fcdf2016-09-14 10:52:14 -0400194 while (!functionsToProcess.empty())
Corentin Wallez71d147f2015-02-11 11:15:24 -0800195 {
Corentin Walleza59fcdf2016-09-14 10:52:14 -0400196 CreatorFunctionData *function = functionsToProcess.back();
197
198 if (function->visiting)
199 {
200 function->visiting = false;
201 function->index = mCurrentIndex++;
202 function->indexAssigned = true;
203
204 functionsToProcess.pop_back();
205 continue;
206 }
207
208 if (!function->node)
209 {
Olli Etuaho77ba4082016-12-16 12:01:18 +0000210 errorStream << "Undefined function '" << function->name
211 << ")' used in the following call chain:";
Corentin Walleza59fcdf2016-09-14 10:52:14 -0400212 result = INITDAG_UNDEFINED;
213 break;
214 }
215
216 if (function->indexAssigned)
217 {
218 functionsToProcess.pop_back();
219 continue;
220 }
221
222 function->visiting = true;
223
224 for (auto callee : function->callees)
225 {
226 functionsToProcess.push_back(callee);
227
228 // Check if the callee is already being visited after pushing it so that it appears
229 // in the chain printed in the info log.
230 if (callee->visiting)
231 {
Olli Etuaho77ba4082016-12-16 12:01:18 +0000232 errorStream << "Recursive function call in the following call chain:";
Corentin Walleza59fcdf2016-09-14 10:52:14 -0400233 result = INITDAG_RECURSION;
234 break;
235 }
236 }
237
Corentin Wallez91dec842015-12-08 15:18:08 -0500238 if (result != INITDAG_SUCCESS)
Corentin Wallez71d147f2015-02-11 11:15:24 -0800239 {
Corentin Walleza59fcdf2016-09-14 10:52:14 -0400240 break;
Corentin Wallez71d147f2015-02-11 11:15:24 -0800241 }
242 }
243
Corentin Walleza59fcdf2016-09-14 10:52:14 -0400244 // The call chain is made of the function we were visiting when the error was detected.
245 if (result != INITDAG_SUCCESS)
246 {
247 bool first = true;
248 for (auto function : functionsToProcess)
249 {
250 if (function->visiting)
251 {
252 if (!first)
253 {
Olli Etuaho77ba4082016-12-16 12:01:18 +0000254 errorStream << " -> ";
Corentin Walleza59fcdf2016-09-14 10:52:14 -0400255 }
Olli Etuaho77ba4082016-12-16 12:01:18 +0000256 errorStream << function->name << ")";
Corentin Walleza59fcdf2016-09-14 10:52:14 -0400257 first = false;
258 }
259 }
Olli Etuaho77ba4082016-12-16 12:01:18 +0000260 if (mDiagnostics)
261 {
262 std::string errorStr = errorStream.str();
263 mDiagnostics->globalError(errorStr.c_str());
264 }
Corentin Walleza59fcdf2016-09-14 10:52:14 -0400265 }
Corentin Wallez71d147f2015-02-11 11:15:24 -0800266
Corentin Walleza59fcdf2016-09-14 10:52:14 -0400267 return result;
Corentin Wallez71d147f2015-02-11 11:15:24 -0800268 }
269
Olli Etuaho77ba4082016-12-16 12:01:18 +0000270 TDiagnostics *mDiagnostics;
Corentin Wallez71d147f2015-02-11 11:15:24 -0800271
Olli Etuahofe486322017-03-21 09:30:54 +0000272 std::map<int, CreatorFunctionData> mFunctions;
Corentin Wallez71d147f2015-02-11 11:15:24 -0800273 CreatorFunctionData *mCurrentFunction;
274 size_t mCurrentIndex;
275};
276
277// CallDAG
278
279CallDAG::CallDAG()
280{
281}
282
283CallDAG::~CallDAG()
284{
285}
286
287const size_t CallDAG::InvalidIndex = std::numeric_limits<size_t>::max();
288
Olli Etuahobd674552016-10-06 13:28:42 +0100289size_t CallDAG::findIndex(const TFunctionSymbolInfo *functionInfo) const
Corentin Wallez71d147f2015-02-11 11:15:24 -0800290{
Olli Etuahofe486322017-03-21 09:30:54 +0000291 auto it = mFunctionIdToIndex.find(functionInfo->getId().get());
Corentin Wallez71d147f2015-02-11 11:15:24 -0800292
293 if (it == mFunctionIdToIndex.end())
294 {
295 return InvalidIndex;
296 }
297 else
298 {
299 return it->second;
300 }
301}
302
303const CallDAG::Record &CallDAG::getRecordFromIndex(size_t index) const
304{
305 ASSERT(index != InvalidIndex && index < mRecords.size());
306 return mRecords[index];
307}
308
309const CallDAG::Record &CallDAG::getRecord(const TIntermAggregate *function) const
310{
Olli Etuahobd674552016-10-06 13:28:42 +0100311 size_t index = findIndex(function->getFunctionSymbolInfo());
Corentin Wallez71d147f2015-02-11 11:15:24 -0800312 ASSERT(index != InvalidIndex && index < mRecords.size());
313 return mRecords[index];
314}
315
316size_t CallDAG::size() const
317{
318 return mRecords.size();
319}
320
321void CallDAG::clear()
322{
323 mRecords.clear();
324 mFunctionIdToIndex.clear();
325}
326
Olli Etuaho77ba4082016-12-16 12:01:18 +0000327CallDAG::InitResult CallDAG::init(TIntermNode *root, TDiagnostics *diagnostics)
Corentin Wallez71d147f2015-02-11 11:15:24 -0800328{
Olli Etuaho77ba4082016-12-16 12:01:18 +0000329 CallDAGCreator creator(diagnostics);
Corentin Wallez71d147f2015-02-11 11:15:24 -0800330
331 // Creates the mapping of functions to callees
332 root->traverse(&creator);
333
334 // Does the topological sort and detects recursions
335 InitResult result = creator.assignIndices();
336 if (result != INITDAG_SUCCESS)
337 {
338 return result;
339 }
340
341 creator.fillDataStructures(&mRecords, &mFunctionIdToIndex);
342 return INITDAG_SUCCESS;
343}
Jamie Madill45bcc782016-11-07 13:58:48 -0500344
345} // namespace sh