blob: 10f0eb937c4182adde652fdc45de7164b74cccf7 [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 }
47 ASSERT(mFunctions.size() == mCurrentIndex + skipped);
48 return INITDAG_SUCCESS;
49 }
50
51 void fillDataStructures(std::vector<Record> *records, std::map<int, int> *idToIndex)
52 {
53 ASSERT(records->empty());
54 ASSERT(idToIndex->empty());
55
56 records->resize(mCurrentIndex);
57
58 for (auto &it : mFunctions)
59 {
60 CreatorFunctionData &data = it.second;
61 // Skip unimplemented functions
62 if (!data.node)
63 {
64 continue;
65 }
66 ASSERT(data.index < records->size());
67 Record &record = (*records)[data.index];
68
69 record.name = data.name.data();
70 record.node = data.node;
71
72 record.callees.reserve(data.callees.size());
73 for (auto &callee : data.callees)
74 {
Cooper Partin4d61f7e2015-08-12 10:56:50 -070075 record.callees.push_back(static_cast<int>(callee->index));
Corentin Wallez71d147f2015-02-11 11:15:24 -080076 }
77
Cooper Partin4d61f7e2015-08-12 10:56:50 -070078 (*idToIndex)[data.node->getFunctionId()] = static_cast<int>(data.index);
Corentin Wallez71d147f2015-02-11 11:15:24 -080079 }
80 }
81
82 private:
83
84 struct CreatorFunctionData
85 {
86 CreatorFunctionData()
87 : node(nullptr),
88 index(0),
89 indexAssigned(false),
90 visiting(false)
91 {
92 }
93
94 std::set<CreatorFunctionData*> callees;
95 TIntermAggregate *node;
96 TString name;
97 size_t index;
98 bool indexAssigned;
99 bool visiting;
100 };
101
102 // Aggregates the AST node for each function as well as the name of the functions called by it
103 bool visitAggregate(Visit visit, TIntermAggregate *node) override
104 {
105 switch (node->getOp())
106 {
107 case EOpPrototype:
108 if (visit == PreVisit)
109 {
110 // Function declaration, create an empty record.
Corentin Wallez91dec842015-12-08 15:18:08 -0500111 auto& record = mFunctions[node->getName()];
112 record.name = node->getName();
Corentin Wallez71d147f2015-02-11 11:15:24 -0800113 }
114 break;
115 case EOpFunction:
116 {
117 // Function definition, create the record if need be and remember the node.
118 if (visit == PreVisit)
119 {
120 auto it = mFunctions.find(node->getName());
121
122 if (it == mFunctions.end())
123 {
124 mCurrentFunction = &mFunctions[node->getName()];
125 }
126 else
127 {
128 mCurrentFunction = &it->second;
129 }
130
131 mCurrentFunction->node = node;
132 mCurrentFunction->name = node->getName();
133
134 }
135 else if (visit == PostVisit)
136 {
137 mCurrentFunction = nullptr;
138 }
139 break;
140 }
141 case EOpFunctionCall:
142 {
143 // Function call, add the callees
144 if (visit == PreVisit)
145 {
146 // Do not handle calls to builtin functions
147 if (node->isUserDefined())
148 {
149 auto it = mFunctions.find(node->getName());
150 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 }
161 default:
162 break;
163 }
164 return true;
165 }
166
167 // Recursively assigns indices to a sub DAG
168 InitResult assignIndicesInternal(CreatorFunctionData *function)
169 {
170 ASSERT(function);
171
172 if (!function->node)
173 {
Corentin Wallez91dec842015-12-08 15:18:08 -0500174 *mCreationInfo << "Undefined function '" << function->name
175 << ")' used in the following call chain:";
Corentin Wallez71d147f2015-02-11 11:15:24 -0800176 return INITDAG_UNDEFINED;
177 }
178
179 if (function->indexAssigned)
180 {
181 return INITDAG_SUCCESS;
182 }
183
184 if (function->visiting)
185 {
186 if (mCreationInfo)
187 {
Corentin Wallez91dec842015-12-08 15:18:08 -0500188 *mCreationInfo << "Recursive function call in the following call chain:" << function->name;
Corentin Wallez71d147f2015-02-11 11:15:24 -0800189 }
190 return INITDAG_RECURSION;
191 }
192 function->visiting = true;
193
194 for (auto &callee : function->callees)
195 {
196 InitResult result = assignIndicesInternal(callee);
Corentin Wallez91dec842015-12-08 15:18:08 -0500197 if (result != INITDAG_SUCCESS)
Corentin Wallez71d147f2015-02-11 11:15:24 -0800198 {
Corentin Wallez91dec842015-12-08 15:18:08 -0500199 // We know that there is an issue with the call chain in the AST,
Corentin Wallez71d147f2015-02-11 11:15:24 -0800200 // print the link of the chain we were processing.
201 if (mCreationInfo)
202 {
Corentin Wallez91dec842015-12-08 15:18:08 -0500203 *mCreationInfo << " <- " << function->name << ")";
Corentin Wallez71d147f2015-02-11 11:15:24 -0800204 }
Corentin Wallez91dec842015-12-08 15:18:08 -0500205 return result;
Corentin Wallez71d147f2015-02-11 11:15:24 -0800206 }
207 }
208
209 function->index = mCurrentIndex++;
210 function->indexAssigned = true;
211
212 function->visiting = false;
213 return INITDAG_SUCCESS;
214 }
215
216 TInfoSinkBase *mCreationInfo;
217
218 std::map<TString, CreatorFunctionData> mFunctions;
219 CreatorFunctionData *mCurrentFunction;
220 size_t mCurrentIndex;
221};
222
223// CallDAG
224
225CallDAG::CallDAG()
226{
227}
228
229CallDAG::~CallDAG()
230{
231}
232
233const size_t CallDAG::InvalidIndex = std::numeric_limits<size_t>::max();
234
235size_t CallDAG::findIndex(const TIntermAggregate *function) const
236{
237 TOperator op = function->getOp();
238 ASSERT(op == EOpPrototype || op == EOpFunction || op == EOpFunctionCall);
Olli Etuahod57e0db2015-04-24 15:05:08 +0300239 UNUSED_ASSERTION_VARIABLE(op);
Corentin Wallez71d147f2015-02-11 11:15:24 -0800240
241 auto it = mFunctionIdToIndex.find(function->getFunctionId());
242
243 if (it == mFunctionIdToIndex.end())
244 {
245 return InvalidIndex;
246 }
247 else
248 {
249 return it->second;
250 }
251}
252
253const CallDAG::Record &CallDAG::getRecordFromIndex(size_t index) const
254{
255 ASSERT(index != InvalidIndex && index < mRecords.size());
256 return mRecords[index];
257}
258
259const CallDAG::Record &CallDAG::getRecord(const TIntermAggregate *function) const
260{
261 size_t index = findIndex(function);
262 ASSERT(index != InvalidIndex && index < mRecords.size());
263 return mRecords[index];
264}
265
266size_t CallDAG::size() const
267{
268 return mRecords.size();
269}
270
271void CallDAG::clear()
272{
273 mRecords.clear();
274 mFunctionIdToIndex.clear();
275}
276
277CallDAG::InitResult CallDAG::init(TIntermNode *root, TInfoSinkBase *info)
278{
279 CallDAGCreator creator(info);
280
281 // Creates the mapping of functions to callees
282 root->traverse(&creator);
283
284 // Does the topological sort and detects recursions
285 InitResult result = creator.assignIndices();
286 if (result != INITDAG_SUCCESS)
287 {
288 return result;
289 }
290
291 creator.fillDataStructures(&mRecords, &mFunctionIdToIndex);
292 return INITDAG_SUCCESS;
293}