blob: 9810fa935b82dacdbc6306fa37407069e9692096 [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"
12#include "compiler/translator/InfoSink.h"
13
14// The CallDAGCreator does all the processing required to create the CallDAG
15// structure so that the latter contains only the necessary variables.
16class CallDAG::CallDAGCreator : public TIntermTraverser
17{
18 public:
19 CallDAGCreator(TInfoSinkBase *info)
20 : TIntermTraverser(true, false, true),
Corentin Wallezbc99bb62015-05-14 17:42:20 -040021 mCreationInfo(info),
Corentin Wallez71d147f2015-02-11 11:15:24 -080022 mCurrentFunction(nullptr),
Corentin Wallezbc99bb62015-05-14 17:42:20 -040023 mCurrentIndex(0)
Corentin Wallez71d147f2015-02-11 11:15:24 -080024 {
25 }
26
27 InitResult assignIndices()
28 {
29 int skipped = 0;
30 for (auto &it : mFunctions)
31 {
32 // Skip unimplemented functions
33 if (it.second.node)
34 {
35 InitResult result = assignIndicesInternal(&it.second);
36 if (result != INITDAG_SUCCESS)
37 {
Corentin Wallez91dec842015-12-08 15:18:08 -050038 *mCreationInfo << "\n";
Corentin Wallez71d147f2015-02-11 11:15:24 -080039 return result;
40 }
41 }
42 else
43 {
44 skipped++;
45 }
46 }
Corentin Walleza59fcdf2016-09-14 10:52:14 -040047
Corentin Wallez71d147f2015-02-11 11:15:24 -080048 ASSERT(mFunctions.size() == mCurrentIndex + skipped);
49 return INITDAG_SUCCESS;
50 }
51
52 void fillDataStructures(std::vector<Record> *records, std::map<int, int> *idToIndex)
53 {
54 ASSERT(records->empty());
55 ASSERT(idToIndex->empty());
56
57 records->resize(mCurrentIndex);
58
59 for (auto &it : mFunctions)
60 {
61 CreatorFunctionData &data = it.second;
62 // Skip unimplemented functions
63 if (!data.node)
64 {
65 continue;
66 }
67 ASSERT(data.index < records->size());
68 Record &record = (*records)[data.index];
69
70 record.name = data.name.data();
71 record.node = data.node;
72
73 record.callees.reserve(data.callees.size());
74 for (auto &callee : data.callees)
75 {
Cooper Partin4d61f7e2015-08-12 10:56:50 -070076 record.callees.push_back(static_cast<int>(callee->index));
Corentin Wallez71d147f2015-02-11 11:15:24 -080077 }
78
Olli Etuahobd674552016-10-06 13:28:42 +010079 (*idToIndex)[data.node->getFunctionSymbolInfo()->getId()] =
80 static_cast<int>(data.index);
Corentin Wallez71d147f2015-02-11 11:15:24 -080081 }
82 }
83
84 private:
85
86 struct CreatorFunctionData
87 {
88 CreatorFunctionData()
89 : node(nullptr),
90 index(0),
91 indexAssigned(false),
92 visiting(false)
93 {
94 }
95
96 std::set<CreatorFunctionData*> callees;
Olli Etuaho336b1472016-10-05 16:37:55 +010097 TIntermFunctionDefinition *node;
Corentin Wallez71d147f2015-02-11 11:15:24 -080098 TString name;
99 size_t index;
100 bool indexAssigned;
101 bool visiting;
102 };
103
Olli Etuaho336b1472016-10-05 16:37:55 +0100104 bool visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) override
105 {
106 // Create the record if need be and remember the node.
107 if (visit == PreVisit)
108 {
109 auto it = mFunctions.find(node->getFunctionSymbolInfo()->getName());
110
111 if (it == mFunctions.end())
112 {
113 mCurrentFunction = &mFunctions[node->getFunctionSymbolInfo()->getName()];
114 }
115 else
116 {
117 mCurrentFunction = &it->second;
118 }
119
120 mCurrentFunction->node = node;
121 mCurrentFunction->name = node->getFunctionSymbolInfo()->getName();
122 }
123 else if (visit == PostVisit)
124 {
125 mCurrentFunction = nullptr;
126 }
127 return true;
128 }
129
Corentin Wallez71d147f2015-02-11 11:15:24 -0800130 // Aggregates the AST node for each function as well as the name of the functions called by it
131 bool visitAggregate(Visit visit, TIntermAggregate *node) override
132 {
133 switch (node->getOp())
134 {
135 case EOpPrototype:
136 if (visit == PreVisit)
137 {
138 // Function declaration, create an empty record.
Olli Etuahobd674552016-10-06 13:28:42 +0100139 auto &record = mFunctions[node->getFunctionSymbolInfo()->getName()];
140 record.name = node->getFunctionSymbolInfo()->getName();
Corentin Wallez71d147f2015-02-11 11:15:24 -0800141 }
142 break;
Corentin Wallez71d147f2015-02-11 11:15:24 -0800143 case EOpFunctionCall:
144 {
145 // Function call, add the callees
146 if (visit == PreVisit)
147 {
148 // Do not handle calls to builtin functions
149 if (node->isUserDefined())
150 {
Olli Etuahobd674552016-10-06 13:28:42 +0100151 auto it = mFunctions.find(node->getFunctionSymbolInfo()->getName());
Corentin Wallez71d147f2015-02-11 11:15:24 -0800152 ASSERT(it != mFunctions.end());
153
154 // We might be in a top-level function call to set a global variable
155 if (mCurrentFunction)
156 {
157 mCurrentFunction->callees.insert(&it->second);
158 }
159 }
160 }
161 break;
162 }
163 default:
164 break;
165 }
166 return true;
167 }
168
169 // Recursively assigns indices to a sub DAG
Corentin Walleza59fcdf2016-09-14 10:52:14 -0400170 InitResult assignIndicesInternal(CreatorFunctionData *root)
Corentin Wallez71d147f2015-02-11 11:15:24 -0800171 {
Corentin Walleza59fcdf2016-09-14 10:52:14 -0400172 // Iterative implementation of the index assignment algorithm. A recursive version
173 // would be prettier but since the CallDAG creation runs before the limiting of the
174 // call depth, we might get stack overflows (computation of the call depth uses the
175 // CallDAG).
Corentin Wallez71d147f2015-02-11 11:15:24 -0800176
Corentin Walleza59fcdf2016-09-14 10:52:14 -0400177 ASSERT(root);
Corentin Wallez71d147f2015-02-11 11:15:24 -0800178
Corentin Walleza59fcdf2016-09-14 10:52:14 -0400179 if (root->indexAssigned)
Corentin Wallez71d147f2015-02-11 11:15:24 -0800180 {
181 return INITDAG_SUCCESS;
182 }
183
Corentin Walleza59fcdf2016-09-14 10:52:14 -0400184 // If we didn't have to detect recursion, functionsToProcess could be a simple queue
185 // in which we add the function being processed's callees. However in order to detect
186 // recursion we need to know which functions we are currently visiting. For that reason
187 // functionsToProcess will look like a concatenation of segments of the form
188 // [F visiting = true, subset of F callees with visiting = false] and the following
189 // segment (if any) will be start with a callee of F.
190 // This way we can remember when we started visiting a function, to put visiting back
191 // to false.
192 TVector<CreatorFunctionData *> functionsToProcess;
193 functionsToProcess.push_back(root);
Corentin Wallez71d147f2015-02-11 11:15:24 -0800194
Corentin Walleza59fcdf2016-09-14 10:52:14 -0400195 InitResult result = INITDAG_SUCCESS;
196
197 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 {
213 *mCreationInfo << "Undefined function '" << function->name
214 << ")' used in the following call chain:";
215 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 {
235 *mCreationInfo << "Recursive function call in the following call chain:";
236 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 {
257 *mCreationInfo << " -> ";
258 }
259 *mCreationInfo << function->name << ")";
260 first = false;
261 }
262 }
263 }
Corentin Wallez71d147f2015-02-11 11:15:24 -0800264
Corentin Walleza59fcdf2016-09-14 10:52:14 -0400265 return result;
Corentin Wallez71d147f2015-02-11 11:15:24 -0800266 }
267
268 TInfoSinkBase *mCreationInfo;
269
270 std::map<TString, CreatorFunctionData> mFunctions;
271 CreatorFunctionData *mCurrentFunction;
272 size_t mCurrentIndex;
273};
274
275// CallDAG
276
277CallDAG::CallDAG()
278{
279}
280
281CallDAG::~CallDAG()
282{
283}
284
285const size_t CallDAG::InvalidIndex = std::numeric_limits<size_t>::max();
286
Olli Etuahobd674552016-10-06 13:28:42 +0100287size_t CallDAG::findIndex(const TFunctionSymbolInfo *functionInfo) const
Corentin Wallez71d147f2015-02-11 11:15:24 -0800288{
Olli Etuahobd674552016-10-06 13:28:42 +0100289 auto it = mFunctionIdToIndex.find(functionInfo->getId());
Corentin Wallez71d147f2015-02-11 11:15:24 -0800290
291 if (it == mFunctionIdToIndex.end())
292 {
293 return InvalidIndex;
294 }
295 else
296 {
297 return it->second;
298 }
299}
300
301const CallDAG::Record &CallDAG::getRecordFromIndex(size_t index) const
302{
303 ASSERT(index != InvalidIndex && index < mRecords.size());
304 return mRecords[index];
305}
306
307const CallDAG::Record &CallDAG::getRecord(const TIntermAggregate *function) const
308{
Olli Etuahobd674552016-10-06 13:28:42 +0100309 size_t index = findIndex(function->getFunctionSymbolInfo());
Corentin Wallez71d147f2015-02-11 11:15:24 -0800310 ASSERT(index != InvalidIndex && index < mRecords.size());
311 return mRecords[index];
312}
313
314size_t CallDAG::size() const
315{
316 return mRecords.size();
317}
318
319void CallDAG::clear()
320{
321 mRecords.clear();
322 mFunctionIdToIndex.clear();
323}
324
325CallDAG::InitResult CallDAG::init(TIntermNode *root, TInfoSinkBase *info)
326{
Corentin Walleza59fcdf2016-09-14 10:52:14 -0400327 ASSERT(info);
328
Corentin Wallez71d147f2015-02-11 11:15:24 -0800329 CallDAGCreator creator(info);
330
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}