blob: e09f8c9c553ae3f5c26221da2b23d12f1e10d4a9 [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 Etuaho77ba4082016-12-16 12:01:18 +000012#include "compiler/translator/Diagnostics.h"
Corentin Wallez71d147f2015-02-11 11:15:24 -080013
Jamie Madill45bcc782016-11-07 13:58:48 -050014namespace sh
15{
16
Corentin Wallez71d147f2015-02-11 11:15:24 -080017// The CallDAGCreator does all the processing required to create the CallDAG
18// structure so that the latter contains only the necessary variables.
19class CallDAG::CallDAGCreator : public TIntermTraverser
20{
21 public:
Olli Etuaho77ba4082016-12-16 12:01:18 +000022 CallDAGCreator(TDiagnostics *diagnostics)
Corentin Wallez71d147f2015-02-11 11:15:24 -080023 : TIntermTraverser(true, false, true),
Olli Etuaho77ba4082016-12-16 12:01:18 +000024 mDiagnostics(diagnostics),
Corentin Wallez71d147f2015-02-11 11:15:24 -080025 mCurrentFunction(nullptr),
Corentin Wallezbc99bb62015-05-14 17:42:20 -040026 mCurrentIndex(0)
Corentin Wallez71d147f2015-02-11 11:15:24 -080027 {
28 }
29
30 InitResult assignIndices()
31 {
32 int skipped = 0;
33 for (auto &it : mFunctions)
34 {
35 // Skip unimplemented functions
36 if (it.second.node)
37 {
38 InitResult result = assignIndicesInternal(&it.second);
39 if (result != INITDAG_SUCCESS)
40 {
41 return result;
42 }
43 }
44 else
45 {
46 skipped++;
47 }
48 }
Corentin Walleza59fcdf2016-09-14 10:52:14 -040049
Corentin Wallez71d147f2015-02-11 11:15:24 -080050 ASSERT(mFunctions.size() == mCurrentIndex + skipped);
51 return INITDAG_SUCCESS;
52 }
53
54 void fillDataStructures(std::vector<Record> *records, std::map<int, int> *idToIndex)
55 {
56 ASSERT(records->empty());
57 ASSERT(idToIndex->empty());
58
59 records->resize(mCurrentIndex);
60
61 for (auto &it : mFunctions)
62 {
63 CreatorFunctionData &data = it.second;
64 // Skip unimplemented functions
65 if (!data.node)
66 {
67 continue;
68 }
69 ASSERT(data.index < records->size());
70 Record &record = (*records)[data.index];
71
72 record.name = data.name.data();
73 record.node = data.node;
74
75 record.callees.reserve(data.callees.size());
76 for (auto &callee : data.callees)
77 {
Cooper Partin4d61f7e2015-08-12 10:56:50 -070078 record.callees.push_back(static_cast<int>(callee->index));
Corentin Wallez71d147f2015-02-11 11:15:24 -080079 }
80
Olli Etuahobd674552016-10-06 13:28:42 +010081 (*idToIndex)[data.node->getFunctionSymbolInfo()->getId()] =
82 static_cast<int>(data.index);
Corentin Wallez71d147f2015-02-11 11:15:24 -080083 }
84 }
85
86 private:
Corentin Wallez71d147f2015-02-11 11:15:24 -080087 struct CreatorFunctionData
88 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -050089 CreatorFunctionData() : node(nullptr), index(0), indexAssigned(false), visiting(false) {}
Corentin Wallez71d147f2015-02-11 11:15:24 -080090
Jamie Madilld7b1ab52016-12-12 14:42:19 -050091 std::set<CreatorFunctionData *> callees;
Olli Etuaho336b1472016-10-05 16:37:55 +010092 TIntermFunctionDefinition *node;
Corentin Wallez71d147f2015-02-11 11:15:24 -080093 TString name;
94 size_t index;
95 bool indexAssigned;
96 bool visiting;
97 };
98
Olli Etuaho336b1472016-10-05 16:37:55 +010099 bool visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) override
100 {
101 // Create the record if need be and remember the node.
102 if (visit == PreVisit)
103 {
104 auto it = mFunctions.find(node->getFunctionSymbolInfo()->getName());
105
106 if (it == mFunctions.end())
107 {
108 mCurrentFunction = &mFunctions[node->getFunctionSymbolInfo()->getName()];
109 }
110 else
111 {
112 mCurrentFunction = &it->second;
113 }
114
115 mCurrentFunction->node = node;
116 mCurrentFunction->name = node->getFunctionSymbolInfo()->getName();
117 }
118 else if (visit == PostVisit)
119 {
120 mCurrentFunction = nullptr;
121 }
122 return true;
123 }
124
Olli Etuaho16c745a2017-01-16 17:02:27 +0000125 bool visitFunctionPrototype(Visit visit, TIntermFunctionPrototype *node) override
126 {
127 ASSERT(visit == PreVisit);
128 // Function declaration, create an empty record.
129 auto &record = mFunctions[node->getFunctionSymbolInfo()->getName()];
130 record.name = node->getFunctionSymbolInfo()->getName();
131
132 // No need to traverse the parameters.
133 return false;
134 }
135
Corentin Wallez71d147f2015-02-11 11:15:24 -0800136 // Aggregates the AST node for each function as well as the name of the functions called by it
137 bool visitAggregate(Visit visit, TIntermAggregate *node) override
138 {
139 switch (node->getOp())
140 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500141 case EOpFunctionCall:
Corentin Wallez71d147f2015-02-11 11:15:24 -0800142 {
143 // Function call, add the callees
144 if (visit == PreVisit)
145 {
146 // Do not handle calls to builtin functions
147 if (node->isUserDefined())
148 {
Olli Etuahobd674552016-10-06 13:28:42 +0100149 auto it = mFunctions.find(node->getFunctionSymbolInfo()->getName());
Corentin Wallez71d147f2015-02-11 11:15:24 -0800150 ASSERT(it != mFunctions.end());
151
152 // We might be in a top-level function call to set a global variable
153 if (mCurrentFunction)
154 {
155 mCurrentFunction->callees.insert(&it->second);
156 }
157 }
158 }
159 break;
160 }
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500161 default:
162 break;
Corentin Wallez71d147f2015-02-11 11:15:24 -0800163 }
164 return true;
165 }
166
167 // Recursively assigns indices to a sub DAG
Corentin Walleza59fcdf2016-09-14 10:52:14 -0400168 InitResult assignIndicesInternal(CreatorFunctionData *root)
Corentin Wallez71d147f2015-02-11 11:15:24 -0800169 {
Corentin Walleza59fcdf2016-09-14 10:52:14 -0400170 // Iterative implementation of the index assignment algorithm. A recursive version
171 // would be prettier but since the CallDAG creation runs before the limiting of the
172 // call depth, we might get stack overflows (computation of the call depth uses the
173 // CallDAG).
Corentin Wallez71d147f2015-02-11 11:15:24 -0800174
Corentin Walleza59fcdf2016-09-14 10:52:14 -0400175 ASSERT(root);
Corentin Wallez71d147f2015-02-11 11:15:24 -0800176
Corentin Walleza59fcdf2016-09-14 10:52:14 -0400177 if (root->indexAssigned)
Corentin Wallez71d147f2015-02-11 11:15:24 -0800178 {
179 return INITDAG_SUCCESS;
180 }
181
Corentin Walleza59fcdf2016-09-14 10:52:14 -0400182 // If we didn't have to detect recursion, functionsToProcess could be a simple queue
183 // in which we add the function being processed's callees. However in order to detect
184 // recursion we need to know which functions we are currently visiting. For that reason
185 // functionsToProcess will look like a concatenation of segments of the form
186 // [F visiting = true, subset of F callees with visiting = false] and the following
187 // segment (if any) will be start with a callee of F.
188 // This way we can remember when we started visiting a function, to put visiting back
189 // to false.
190 TVector<CreatorFunctionData *> functionsToProcess;
191 functionsToProcess.push_back(root);
Corentin Wallez71d147f2015-02-11 11:15:24 -0800192
Corentin Walleza59fcdf2016-09-14 10:52:14 -0400193 InitResult result = INITDAG_SUCCESS;
194
Olli Etuaho77ba4082016-12-16 12:01:18 +0000195 std::stringstream errorStream;
196
Corentin Walleza59fcdf2016-09-14 10:52:14 -0400197 while (!functionsToProcess.empty())
Corentin Wallez71d147f2015-02-11 11:15:24 -0800198 {
Corentin Walleza59fcdf2016-09-14 10:52:14 -0400199 CreatorFunctionData *function = functionsToProcess.back();
200
201 if (function->visiting)
202 {
203 function->visiting = false;
204 function->index = mCurrentIndex++;
205 function->indexAssigned = true;
206
207 functionsToProcess.pop_back();
208 continue;
209 }
210
211 if (!function->node)
212 {
Olli Etuaho77ba4082016-12-16 12:01:18 +0000213 errorStream << "Undefined function '" << function->name
214 << ")' used in the following call chain:";
Corentin Walleza59fcdf2016-09-14 10:52:14 -0400215 result = INITDAG_UNDEFINED;
216 break;
217 }
218
219 if (function->indexAssigned)
220 {
221 functionsToProcess.pop_back();
222 continue;
223 }
224
225 function->visiting = true;
226
227 for (auto callee : function->callees)
228 {
229 functionsToProcess.push_back(callee);
230
231 // Check if the callee is already being visited after pushing it so that it appears
232 // in the chain printed in the info log.
233 if (callee->visiting)
234 {
Olli Etuaho77ba4082016-12-16 12:01:18 +0000235 errorStream << "Recursive function call in the following call chain:";
Corentin Walleza59fcdf2016-09-14 10:52:14 -0400236 result = INITDAG_RECURSION;
237 break;
238 }
239 }
240
Corentin Wallez91dec842015-12-08 15:18:08 -0500241 if (result != INITDAG_SUCCESS)
Corentin Wallez71d147f2015-02-11 11:15:24 -0800242 {
Corentin Walleza59fcdf2016-09-14 10:52:14 -0400243 break;
Corentin Wallez71d147f2015-02-11 11:15:24 -0800244 }
245 }
246
Corentin Walleza59fcdf2016-09-14 10:52:14 -0400247 // The call chain is made of the function we were visiting when the error was detected.
248 if (result != INITDAG_SUCCESS)
249 {
250 bool first = true;
251 for (auto function : functionsToProcess)
252 {
253 if (function->visiting)
254 {
255 if (!first)
256 {
Olli Etuaho77ba4082016-12-16 12:01:18 +0000257 errorStream << " -> ";
Corentin Walleza59fcdf2016-09-14 10:52:14 -0400258 }
Olli Etuaho77ba4082016-12-16 12:01:18 +0000259 errorStream << function->name << ")";
Corentin Walleza59fcdf2016-09-14 10:52:14 -0400260 first = false;
261 }
262 }
Olli Etuaho77ba4082016-12-16 12:01:18 +0000263 if (mDiagnostics)
264 {
265 std::string errorStr = errorStream.str();
266 mDiagnostics->globalError(errorStr.c_str());
267 }
Corentin Walleza59fcdf2016-09-14 10:52:14 -0400268 }
Corentin Wallez71d147f2015-02-11 11:15:24 -0800269
Corentin Walleza59fcdf2016-09-14 10:52:14 -0400270 return result;
Corentin Wallez71d147f2015-02-11 11:15:24 -0800271 }
272
Olli Etuaho77ba4082016-12-16 12:01:18 +0000273 TDiagnostics *mDiagnostics;
Corentin Wallez71d147f2015-02-11 11:15:24 -0800274
275 std::map<TString, CreatorFunctionData> mFunctions;
276 CreatorFunctionData *mCurrentFunction;
277 size_t mCurrentIndex;
278};
279
280// CallDAG
281
282CallDAG::CallDAG()
283{
284}
285
286CallDAG::~CallDAG()
287{
288}
289
290const size_t CallDAG::InvalidIndex = std::numeric_limits<size_t>::max();
291
Olli Etuahobd674552016-10-06 13:28:42 +0100292size_t CallDAG::findIndex(const TFunctionSymbolInfo *functionInfo) const
Corentin Wallez71d147f2015-02-11 11:15:24 -0800293{
Olli Etuahobd674552016-10-06 13:28:42 +0100294 auto it = mFunctionIdToIndex.find(functionInfo->getId());
Corentin Wallez71d147f2015-02-11 11:15:24 -0800295
296 if (it == mFunctionIdToIndex.end())
297 {
298 return InvalidIndex;
299 }
300 else
301 {
302 return it->second;
303 }
304}
305
306const CallDAG::Record &CallDAG::getRecordFromIndex(size_t index) const
307{
308 ASSERT(index != InvalidIndex && index < mRecords.size());
309 return mRecords[index];
310}
311
312const CallDAG::Record &CallDAG::getRecord(const TIntermAggregate *function) const
313{
Olli Etuahobd674552016-10-06 13:28:42 +0100314 size_t index = findIndex(function->getFunctionSymbolInfo());
Corentin Wallez71d147f2015-02-11 11:15:24 -0800315 ASSERT(index != InvalidIndex && index < mRecords.size());
316 return mRecords[index];
317}
318
319size_t CallDAG::size() const
320{
321 return mRecords.size();
322}
323
324void CallDAG::clear()
325{
326 mRecords.clear();
327 mFunctionIdToIndex.clear();
328}
329
Olli Etuaho77ba4082016-12-16 12:01:18 +0000330CallDAG::InitResult CallDAG::init(TIntermNode *root, TDiagnostics *diagnostics)
Corentin Wallez71d147f2015-02-11 11:15:24 -0800331{
Olli Etuaho77ba4082016-12-16 12:01:18 +0000332 CallDAGCreator creator(diagnostics);
Corentin Wallez71d147f2015-02-11 11:15:24 -0800333
334 // Creates the mapping of functions to callees
335 root->traverse(&creator);
336
337 // Does the topological sort and detects recursions
338 InitResult result = creator.assignIndices();
339 if (result != INITDAG_SUCCESS)
340 {
341 return result;
342 }
343
344 creator.fillDataStructures(&mRecords, &mFunctionIdToIndex);
345 return INITDAG_SUCCESS;
346}
Jamie Madill45bcc782016-11-07 13:58:48 -0500347
348} // namespace sh