blob: 3ea0fa8a2010ed6502a275e2ee86270456d85d9b [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
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:
22 CallDAGCreator(TInfoSinkBase *info)
23 : TIntermTraverser(true, false, true),
Corentin Wallezbc99bb62015-05-14 17:42:20 -040024 mCreationInfo(info),
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 {
Corentin Wallez91dec842015-12-08 15:18:08 -050041 *mCreationInfo << "\n";
Corentin Wallez71d147f2015-02-11 11:15:24 -080042 return result;
43 }
44 }
45 else
46 {
47 skipped++;
48 }
49 }
Corentin Walleza59fcdf2016-09-14 10:52:14 -040050
Corentin Wallez71d147f2015-02-11 11:15:24 -080051 ASSERT(mFunctions.size() == mCurrentIndex + skipped);
52 return INITDAG_SUCCESS;
53 }
54
55 void fillDataStructures(std::vector<Record> *records, std::map<int, int> *idToIndex)
56 {
57 ASSERT(records->empty());
58 ASSERT(idToIndex->empty());
59
60 records->resize(mCurrentIndex);
61
62 for (auto &it : mFunctions)
63 {
64 CreatorFunctionData &data = it.second;
65 // Skip unimplemented functions
66 if (!data.node)
67 {
68 continue;
69 }
70 ASSERT(data.index < records->size());
71 Record &record = (*records)[data.index];
72
73 record.name = data.name.data();
74 record.node = data.node;
75
76 record.callees.reserve(data.callees.size());
77 for (auto &callee : data.callees)
78 {
Cooper Partin4d61f7e2015-08-12 10:56:50 -070079 record.callees.push_back(static_cast<int>(callee->index));
Corentin Wallez71d147f2015-02-11 11:15:24 -080080 }
81
Olli Etuahobd674552016-10-06 13:28:42 +010082 (*idToIndex)[data.node->getFunctionSymbolInfo()->getId()] =
83 static_cast<int>(data.index);
Corentin Wallez71d147f2015-02-11 11:15:24 -080084 }
85 }
86
87 private:
Corentin Wallez71d147f2015-02-11 11:15:24 -080088 struct CreatorFunctionData
89 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -050090 CreatorFunctionData() : node(nullptr), index(0), indexAssigned(false), visiting(false) {}
Corentin Wallez71d147f2015-02-11 11:15:24 -080091
Jamie Madilld7b1ab52016-12-12 14:42:19 -050092 std::set<CreatorFunctionData *> callees;
Olli Etuaho336b1472016-10-05 16:37:55 +010093 TIntermFunctionDefinition *node;
Corentin Wallez71d147f2015-02-11 11:15:24 -080094 TString name;
95 size_t index;
96 bool indexAssigned;
97 bool visiting;
98 };
99
Olli Etuaho336b1472016-10-05 16:37:55 +0100100 bool visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) override
101 {
102 // Create the record if need be and remember the node.
103 if (visit == PreVisit)
104 {
105 auto it = mFunctions.find(node->getFunctionSymbolInfo()->getName());
106
107 if (it == mFunctions.end())
108 {
109 mCurrentFunction = &mFunctions[node->getFunctionSymbolInfo()->getName()];
110 }
111 else
112 {
113 mCurrentFunction = &it->second;
114 }
115
116 mCurrentFunction->node = node;
117 mCurrentFunction->name = node->getFunctionSymbolInfo()->getName();
118 }
119 else if (visit == PostVisit)
120 {
121 mCurrentFunction = nullptr;
122 }
123 return true;
124 }
125
Corentin Wallez71d147f2015-02-11 11:15:24 -0800126 // Aggregates the AST node for each function as well as the name of the functions called by it
127 bool visitAggregate(Visit visit, TIntermAggregate *node) override
128 {
129 switch (node->getOp())
130 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500131 case EOpPrototype:
132 if (visit == PreVisit)
133 {
134 // Function declaration, create an empty record.
135 auto &record = mFunctions[node->getFunctionSymbolInfo()->getName()];
136 record.name = node->getFunctionSymbolInfo()->getName();
137 }
138 break;
139 case EOpFunctionCall:
Corentin Wallez71d147f2015-02-11 11:15:24 -0800140 {
141 // Function call, add the callees
142 if (visit == PreVisit)
143 {
144 // Do not handle calls to builtin functions
145 if (node->isUserDefined())
146 {
Olli Etuahobd674552016-10-06 13:28:42 +0100147 auto it = mFunctions.find(node->getFunctionSymbolInfo()->getName());
Corentin Wallez71d147f2015-02-11 11:15:24 -0800148 ASSERT(it != mFunctions.end());
149
150 // We might be in a top-level function call to set a global variable
151 if (mCurrentFunction)
152 {
153 mCurrentFunction->callees.insert(&it->second);
154 }
155 }
156 }
157 break;
158 }
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500159 default:
160 break;
Corentin Wallez71d147f2015-02-11 11:15:24 -0800161 }
162 return true;
163 }
164
165 // Recursively assigns indices to a sub DAG
Corentin Walleza59fcdf2016-09-14 10:52:14 -0400166 InitResult assignIndicesInternal(CreatorFunctionData *root)
Corentin Wallez71d147f2015-02-11 11:15:24 -0800167 {
Corentin Walleza59fcdf2016-09-14 10:52:14 -0400168 // Iterative implementation of the index assignment algorithm. A recursive version
169 // would be prettier but since the CallDAG creation runs before the limiting of the
170 // call depth, we might get stack overflows (computation of the call depth uses the
171 // CallDAG).
Corentin Wallez71d147f2015-02-11 11:15:24 -0800172
Corentin Walleza59fcdf2016-09-14 10:52:14 -0400173 ASSERT(root);
Corentin Wallez71d147f2015-02-11 11:15:24 -0800174
Corentin Walleza59fcdf2016-09-14 10:52:14 -0400175 if (root->indexAssigned)
Corentin Wallez71d147f2015-02-11 11:15:24 -0800176 {
177 return INITDAG_SUCCESS;
178 }
179
Corentin Walleza59fcdf2016-09-14 10:52:14 -0400180 // If we didn't have to detect recursion, functionsToProcess could be a simple queue
181 // in which we add the function being processed's callees. However in order to detect
182 // recursion we need to know which functions we are currently visiting. For that reason
183 // functionsToProcess will look like a concatenation of segments of the form
184 // [F visiting = true, subset of F callees with visiting = false] and the following
185 // segment (if any) will be start with a callee of F.
186 // This way we can remember when we started visiting a function, to put visiting back
187 // to false.
188 TVector<CreatorFunctionData *> functionsToProcess;
189 functionsToProcess.push_back(root);
Corentin Wallez71d147f2015-02-11 11:15:24 -0800190
Corentin Walleza59fcdf2016-09-14 10:52:14 -0400191 InitResult result = INITDAG_SUCCESS;
192
193 while (!functionsToProcess.empty())
Corentin Wallez71d147f2015-02-11 11:15:24 -0800194 {
Corentin Walleza59fcdf2016-09-14 10:52:14 -0400195 CreatorFunctionData *function = functionsToProcess.back();
196
197 if (function->visiting)
198 {
199 function->visiting = false;
200 function->index = mCurrentIndex++;
201 function->indexAssigned = true;
202
203 functionsToProcess.pop_back();
204 continue;
205 }
206
207 if (!function->node)
208 {
209 *mCreationInfo << "Undefined function '" << function->name
210 << ")' used in the following call chain:";
211 result = INITDAG_UNDEFINED;
212 break;
213 }
214
215 if (function->indexAssigned)
216 {
217 functionsToProcess.pop_back();
218 continue;
219 }
220
221 function->visiting = true;
222
223 for (auto callee : function->callees)
224 {
225 functionsToProcess.push_back(callee);
226
227 // Check if the callee is already being visited after pushing it so that it appears
228 // in the chain printed in the info log.
229 if (callee->visiting)
230 {
231 *mCreationInfo << "Recursive function call in the following call chain:";
232 result = INITDAG_RECURSION;
233 break;
234 }
235 }
236
Corentin Wallez91dec842015-12-08 15:18:08 -0500237 if (result != INITDAG_SUCCESS)
Corentin Wallez71d147f2015-02-11 11:15:24 -0800238 {
Corentin Walleza59fcdf2016-09-14 10:52:14 -0400239 break;
Corentin Wallez71d147f2015-02-11 11:15:24 -0800240 }
241 }
242
Corentin Walleza59fcdf2016-09-14 10:52:14 -0400243 // The call chain is made of the function we were visiting when the error was detected.
244 if (result != INITDAG_SUCCESS)
245 {
246 bool first = true;
247 for (auto function : functionsToProcess)
248 {
249 if (function->visiting)
250 {
251 if (!first)
252 {
253 *mCreationInfo << " -> ";
254 }
255 *mCreationInfo << function->name << ")";
256 first = false;
257 }
258 }
259 }
Corentin Wallez71d147f2015-02-11 11:15:24 -0800260
Corentin Walleza59fcdf2016-09-14 10:52:14 -0400261 return result;
Corentin Wallez71d147f2015-02-11 11:15:24 -0800262 }
263
264 TInfoSinkBase *mCreationInfo;
265
266 std::map<TString, CreatorFunctionData> mFunctions;
267 CreatorFunctionData *mCurrentFunction;
268 size_t mCurrentIndex;
269};
270
271// CallDAG
272
273CallDAG::CallDAG()
274{
275}
276
277CallDAG::~CallDAG()
278{
279}
280
281const size_t CallDAG::InvalidIndex = std::numeric_limits<size_t>::max();
282
Olli Etuahobd674552016-10-06 13:28:42 +0100283size_t CallDAG::findIndex(const TFunctionSymbolInfo *functionInfo) const
Corentin Wallez71d147f2015-02-11 11:15:24 -0800284{
Olli Etuahobd674552016-10-06 13:28:42 +0100285 auto it = mFunctionIdToIndex.find(functionInfo->getId());
Corentin Wallez71d147f2015-02-11 11:15:24 -0800286
287 if (it == mFunctionIdToIndex.end())
288 {
289 return InvalidIndex;
290 }
291 else
292 {
293 return it->second;
294 }
295}
296
297const CallDAG::Record &CallDAG::getRecordFromIndex(size_t index) const
298{
299 ASSERT(index != InvalidIndex && index < mRecords.size());
300 return mRecords[index];
301}
302
303const CallDAG::Record &CallDAG::getRecord(const TIntermAggregate *function) const
304{
Olli Etuahobd674552016-10-06 13:28:42 +0100305 size_t index = findIndex(function->getFunctionSymbolInfo());
Corentin Wallez71d147f2015-02-11 11:15:24 -0800306 ASSERT(index != InvalidIndex && index < mRecords.size());
307 return mRecords[index];
308}
309
310size_t CallDAG::size() const
311{
312 return mRecords.size();
313}
314
315void CallDAG::clear()
316{
317 mRecords.clear();
318 mFunctionIdToIndex.clear();
319}
320
321CallDAG::InitResult CallDAG::init(TIntermNode *root, TInfoSinkBase *info)
322{
Corentin Walleza59fcdf2016-09-14 10:52:14 -0400323 ASSERT(info);
324
Corentin Wallez71d147f2015-02-11 11:15:24 -0800325 CallDAGCreator creator(info);
326
327 // Creates the mapping of functions to callees
328 root->traverse(&creator);
329
330 // Does the topological sort and detects recursions
331 InitResult result = creator.assignIndices();
332 if (result != INITDAG_SUCCESS)
333 {
334 return result;
335 }
336
337 creator.fillDataStructures(&mRecords, &mFunctionIdToIndex);
338 return INITDAG_SUCCESS;
339}
Jamie Madill45bcc782016-11-07 13:58:48 -0500340
341} // namespace sh