blob: 1c603ef8863f58c53cd1ff0bfe08044d579915f5 [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
Cooper Partin4d61f7e2015-08-12 10:56:50 -070079 (*idToIndex)[data.node->getFunctionId()] = static_cast<int>(data.index);
Corentin Wallez71d147f2015-02-11 11:15:24 -080080 }
81 }
82
83 private:
84
85 struct CreatorFunctionData
86 {
87 CreatorFunctionData()
88 : node(nullptr),
89 index(0),
90 indexAssigned(false),
91 visiting(false)
92 {
93 }
94
95 std::set<CreatorFunctionData*> callees;
96 TIntermAggregate *node;
97 TString name;
98 size_t index;
99 bool indexAssigned;
100 bool visiting;
101 };
102
103 // Aggregates the AST node for each function as well as the name of the functions called by it
104 bool visitAggregate(Visit visit, TIntermAggregate *node) override
105 {
106 switch (node->getOp())
107 {
108 case EOpPrototype:
109 if (visit == PreVisit)
110 {
111 // Function declaration, create an empty record.
Corentin Wallez91dec842015-12-08 15:18:08 -0500112 auto& record = mFunctions[node->getName()];
113 record.name = node->getName();
Corentin Wallez71d147f2015-02-11 11:15:24 -0800114 }
115 break;
116 case EOpFunction:
117 {
118 // Function definition, create the record if need be and remember the node.
119 if (visit == PreVisit)
120 {
121 auto it = mFunctions.find(node->getName());
122
123 if (it == mFunctions.end())
124 {
125 mCurrentFunction = &mFunctions[node->getName()];
126 }
127 else
128 {
129 mCurrentFunction = &it->second;
130 }
131
132 mCurrentFunction->node = node;
133 mCurrentFunction->name = node->getName();
134
135 }
136 else if (visit == PostVisit)
137 {
138 mCurrentFunction = nullptr;
139 }
140 break;
141 }
142 case EOpFunctionCall:
143 {
144 // Function call, add the callees
145 if (visit == PreVisit)
146 {
147 // Do not handle calls to builtin functions
148 if (node->isUserDefined())
149 {
150 auto it = mFunctions.find(node->getName());
151 ASSERT(it != mFunctions.end());
152
153 // We might be in a top-level function call to set a global variable
154 if (mCurrentFunction)
155 {
156 mCurrentFunction->callees.insert(&it->second);
157 }
158 }
159 }
160 break;
161 }
162 default:
163 break;
164 }
165 return true;
166 }
167
168 // Recursively assigns indices to a sub DAG
Corentin Walleza59fcdf2016-09-14 10:52:14 -0400169 InitResult assignIndicesInternal(CreatorFunctionData *root)
Corentin Wallez71d147f2015-02-11 11:15:24 -0800170 {
Corentin Walleza59fcdf2016-09-14 10:52:14 -0400171 // Iterative implementation of the index assignment algorithm. A recursive version
172 // would be prettier but since the CallDAG creation runs before the limiting of the
173 // call depth, we might get stack overflows (computation of the call depth uses the
174 // CallDAG).
Corentin Wallez71d147f2015-02-11 11:15:24 -0800175
Corentin Walleza59fcdf2016-09-14 10:52:14 -0400176 ASSERT(root);
Corentin Wallez71d147f2015-02-11 11:15:24 -0800177
Corentin Walleza59fcdf2016-09-14 10:52:14 -0400178 if (root->indexAssigned)
Corentin Wallez71d147f2015-02-11 11:15:24 -0800179 {
180 return INITDAG_SUCCESS;
181 }
182
Corentin Walleza59fcdf2016-09-14 10:52:14 -0400183 // If we didn't have to detect recursion, functionsToProcess could be a simple queue
184 // in which we add the function being processed's callees. However in order to detect
185 // recursion we need to know which functions we are currently visiting. For that reason
186 // functionsToProcess will look like a concatenation of segments of the form
187 // [F visiting = true, subset of F callees with visiting = false] and the following
188 // segment (if any) will be start with a callee of F.
189 // This way we can remember when we started visiting a function, to put visiting back
190 // to false.
191 TVector<CreatorFunctionData *> functionsToProcess;
192 functionsToProcess.push_back(root);
Corentin Wallez71d147f2015-02-11 11:15:24 -0800193
Corentin Walleza59fcdf2016-09-14 10:52:14 -0400194 InitResult result = INITDAG_SUCCESS;
195
196 while (!functionsToProcess.empty())
Corentin Wallez71d147f2015-02-11 11:15:24 -0800197 {
Corentin Walleza59fcdf2016-09-14 10:52:14 -0400198 CreatorFunctionData *function = functionsToProcess.back();
199
200 if (function->visiting)
201 {
202 function->visiting = false;
203 function->index = mCurrentIndex++;
204 function->indexAssigned = true;
205
206 functionsToProcess.pop_back();
207 continue;
208 }
209
210 if (!function->node)
211 {
212 *mCreationInfo << "Undefined function '" << function->name
213 << ")' used in the following call chain:";
214 result = INITDAG_UNDEFINED;
215 break;
216 }
217
218 if (function->indexAssigned)
219 {
220 functionsToProcess.pop_back();
221 continue;
222 }
223
224 function->visiting = true;
225
226 for (auto callee : function->callees)
227 {
228 functionsToProcess.push_back(callee);
229
230 // Check if the callee is already being visited after pushing it so that it appears
231 // in the chain printed in the info log.
232 if (callee->visiting)
233 {
234 *mCreationInfo << "Recursive function call in the following call chain:";
235 result = INITDAG_RECURSION;
236 break;
237 }
238 }
239
Corentin Wallez91dec842015-12-08 15:18:08 -0500240 if (result != INITDAG_SUCCESS)
Corentin Wallez71d147f2015-02-11 11:15:24 -0800241 {
Corentin Walleza59fcdf2016-09-14 10:52:14 -0400242 break;
Corentin Wallez71d147f2015-02-11 11:15:24 -0800243 }
244 }
245
Corentin Walleza59fcdf2016-09-14 10:52:14 -0400246 // The call chain is made of the function we were visiting when the error was detected.
247 if (result != INITDAG_SUCCESS)
248 {
249 bool first = true;
250 for (auto function : functionsToProcess)
251 {
252 if (function->visiting)
253 {
254 if (!first)
255 {
256 *mCreationInfo << " -> ";
257 }
258 *mCreationInfo << function->name << ")";
259 first = false;
260 }
261 }
262 }
Corentin Wallez71d147f2015-02-11 11:15:24 -0800263
Corentin Walleza59fcdf2016-09-14 10:52:14 -0400264 return result;
Corentin Wallez71d147f2015-02-11 11:15:24 -0800265 }
266
267 TInfoSinkBase *mCreationInfo;
268
269 std::map<TString, CreatorFunctionData> mFunctions;
270 CreatorFunctionData *mCurrentFunction;
271 size_t mCurrentIndex;
272};
273
274// CallDAG
275
276CallDAG::CallDAG()
277{
278}
279
280CallDAG::~CallDAG()
281{
282}
283
284const size_t CallDAG::InvalidIndex = std::numeric_limits<size_t>::max();
285
286size_t CallDAG::findIndex(const TIntermAggregate *function) const
287{
288 TOperator op = function->getOp();
289 ASSERT(op == EOpPrototype || op == EOpFunction || op == EOpFunctionCall);
Olli Etuahod57e0db2015-04-24 15:05:08 +0300290 UNUSED_ASSERTION_VARIABLE(op);
Corentin Wallez71d147f2015-02-11 11:15:24 -0800291
292 auto it = mFunctionIdToIndex.find(function->getFunctionId());
293
294 if (it == mFunctionIdToIndex.end())
295 {
296 return InvalidIndex;
297 }
298 else
299 {
300 return it->second;
301 }
302}
303
304const CallDAG::Record &CallDAG::getRecordFromIndex(size_t index) const
305{
306 ASSERT(index != InvalidIndex && index < mRecords.size());
307 return mRecords[index];
308}
309
310const CallDAG::Record &CallDAG::getRecord(const TIntermAggregate *function) const
311{
312 size_t index = findIndex(function);
313 ASSERT(index != InvalidIndex && index < mRecords.size());
314 return mRecords[index];
315}
316
317size_t CallDAG::size() const
318{
319 return mRecords.size();
320}
321
322void CallDAG::clear()
323{
324 mRecords.clear();
325 mFunctionIdToIndex.clear();
326}
327
328CallDAG::InitResult CallDAG::init(TIntermNode *root, TInfoSinkBase *info)
329{
Corentin Walleza59fcdf2016-09-14 10:52:14 -0400330 ASSERT(info);
331
Corentin Wallez71d147f2015-02-11 11:15:24 -0800332 CallDAGCreator creator(info);
333
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}