Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/angle
Path: blob/main_old/src/compiler/translator/CallDAG.cpp
1693 views
1
//
2
// Copyright 2002 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
13
#include "compiler/translator/Diagnostics.h"
14
#include "compiler/translator/SymbolTable.h"
15
#include "compiler/translator/tree_util/IntermTraverse.h"
16
17
namespace sh
18
{
19
20
// The CallDAGCreator does all the processing required to create the CallDAG
21
// structure so that the latter contains only the necessary variables.
22
class CallDAG::CallDAGCreator : public TIntermTraverser
23
{
24
public:
25
CallDAGCreator(TDiagnostics *diagnostics)
26
: TIntermTraverser(true, false, false),
27
mDiagnostics(diagnostics),
28
mCurrentFunction(nullptr),
29
mCurrentIndex(0)
30
{}
31
32
InitResult assignIndices()
33
{
34
int skipped = 0;
35
for (auto &it : mFunctions)
36
{
37
// Skip unimplemented functions
38
if (it.second.definitionNode)
39
{
40
InitResult result = assignIndicesInternal(&it.second);
41
if (result != INITDAG_SUCCESS)
42
{
43
return result;
44
}
45
}
46
else
47
{
48
skipped++;
49
}
50
}
51
52
ASSERT(mFunctions.size() == mCurrentIndex + skipped);
53
return INITDAG_SUCCESS;
54
}
55
56
void fillDataStructures(std::vector<Record> *records, std::map<int, int> *idToIndex)
57
{
58
ASSERT(records->empty());
59
ASSERT(idToIndex->empty());
60
61
records->resize(mCurrentIndex);
62
63
for (auto &it : mFunctions)
64
{
65
CreatorFunctionData &data = it.second;
66
// Skip unimplemented functions
67
if (!data.definitionNode)
68
{
69
continue;
70
}
71
ASSERT(data.index < records->size());
72
Record &record = (*records)[data.index];
73
74
record.node = data.definitionNode;
75
76
record.callees.reserve(data.callees.size());
77
for (auto &callee : data.callees)
78
{
79
record.callees.push_back(static_cast<int>(callee->index));
80
}
81
82
(*idToIndex)[it.first] = static_cast<int>(data.index);
83
}
84
}
85
86
private:
87
struct CreatorFunctionData
88
{
89
CreatorFunctionData()
90
: definitionNode(nullptr), name(""), index(0), indexAssigned(false), visiting(false)
91
{}
92
93
std::set<CreatorFunctionData *> callees;
94
TIntermFunctionDefinition *definitionNode;
95
ImmutableString name;
96
size_t index;
97
bool indexAssigned;
98
bool visiting;
99
};
100
101
bool visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) override
102
{
103
// Create the record if need be and remember the definition node.
104
mCurrentFunction = &mFunctions[node->getFunction()->uniqueId().get()];
105
// Name will be overwritten here. If we've already traversed the prototype of this function,
106
// it should have had the same name.
107
ASSERT(mCurrentFunction->name == "" ||
108
mCurrentFunction->name == node->getFunction()->name());
109
mCurrentFunction->name = node->getFunction()->name();
110
mCurrentFunction->definitionNode = node;
111
112
node->getBody()->traverse(this);
113
mCurrentFunction = nullptr;
114
return false;
115
}
116
117
void visitFunctionPrototype(TIntermFunctionPrototype *node) override
118
{
119
ASSERT(mCurrentFunction == nullptr);
120
121
// Function declaration, create an empty record.
122
auto &record = mFunctions[node->getFunction()->uniqueId().get()];
123
record.name = node->getFunction()->name();
124
}
125
126
// Track functions called from another function.
127
bool visitAggregate(Visit visit, TIntermAggregate *node) override
128
{
129
if (node->getOp() == EOpCallFunctionInAST)
130
{
131
// Function call, add the callees
132
auto it = mFunctions.find(node->getFunction()->uniqueId().get());
133
ASSERT(it != mFunctions.end());
134
135
// We might be traversing the initializer of a global variable. Even though function
136
// calls in global scope are forbidden by the parser, some subsequent AST
137
// transformations can add them to emulate particular features.
138
if (mCurrentFunction)
139
{
140
mCurrentFunction->callees.insert(&it->second);
141
}
142
}
143
return true;
144
}
145
146
// Recursively assigns indices to a sub DAG
147
InitResult assignIndicesInternal(CreatorFunctionData *root)
148
{
149
// Iterative implementation of the index assignment algorithm. A recursive version
150
// would be prettier but since the CallDAG creation runs before the limiting of the
151
// call depth, we might get stack overflows (computation of the call depth uses the
152
// CallDAG).
153
154
ASSERT(root);
155
156
if (root->indexAssigned)
157
{
158
return INITDAG_SUCCESS;
159
}
160
161
// If we didn't have to detect recursion, functionsToProcess could be a simple queue
162
// in which we add the function being processed's callees. However in order to detect
163
// recursion we need to know which functions we are currently visiting. For that reason
164
// functionsToProcess will look like a concatenation of segments of the form
165
// [F visiting = true, subset of F callees with visiting = false] and the following
166
// segment (if any) will be start with a callee of F.
167
// This way we can remember when we started visiting a function, to put visiting back
168
// to false.
169
TVector<CreatorFunctionData *> functionsToProcess;
170
functionsToProcess.push_back(root);
171
172
InitResult result = INITDAG_SUCCESS;
173
174
std::stringstream errorStream = sh::InitializeStream<std::stringstream>();
175
176
while (!functionsToProcess.empty())
177
{
178
CreatorFunctionData *function = functionsToProcess.back();
179
180
if (function->visiting)
181
{
182
function->visiting = false;
183
function->index = mCurrentIndex++;
184
function->indexAssigned = true;
185
186
functionsToProcess.pop_back();
187
continue;
188
}
189
190
if (!function->definitionNode)
191
{
192
errorStream << "Undefined function '" << function->name
193
<< "()' used in the following call chain:";
194
result = INITDAG_UNDEFINED;
195
break;
196
}
197
198
if (function->indexAssigned)
199
{
200
functionsToProcess.pop_back();
201
continue;
202
}
203
204
function->visiting = true;
205
206
for (auto callee : function->callees)
207
{
208
functionsToProcess.push_back(callee);
209
210
// Check if the callee is already being visited after pushing it so that it appears
211
// in the chain printed in the info log.
212
if (callee->visiting)
213
{
214
errorStream << "Recursive function call in the following call chain:";
215
result = INITDAG_RECURSION;
216
break;
217
}
218
}
219
220
if (result != INITDAG_SUCCESS)
221
{
222
break;
223
}
224
}
225
226
// The call chain is made of the function we were visiting when the error was detected.
227
if (result != INITDAG_SUCCESS)
228
{
229
bool first = true;
230
for (auto function : functionsToProcess)
231
{
232
if (function->visiting)
233
{
234
if (!first)
235
{
236
errorStream << " -> ";
237
}
238
errorStream << function->name << ")";
239
first = false;
240
}
241
}
242
if (mDiagnostics)
243
{
244
std::string errorStr = errorStream.str();
245
mDiagnostics->globalError(errorStr.c_str());
246
}
247
}
248
249
return result;
250
}
251
252
TDiagnostics *mDiagnostics;
253
254
std::map<int, CreatorFunctionData> mFunctions;
255
CreatorFunctionData *mCurrentFunction;
256
size_t mCurrentIndex;
257
};
258
259
// CallDAG
260
261
CallDAG::CallDAG() {}
262
263
CallDAG::~CallDAG() {}
264
265
const size_t CallDAG::InvalidIndex = std::numeric_limits<size_t>::max();
266
267
size_t CallDAG::findIndex(const TSymbolUniqueId &id) const
268
{
269
auto it = mFunctionIdToIndex.find(id.get());
270
271
if (it == mFunctionIdToIndex.end())
272
{
273
return InvalidIndex;
274
}
275
else
276
{
277
return it->second;
278
}
279
}
280
281
const CallDAG::Record &CallDAG::getRecordFromIndex(size_t index) const
282
{
283
ASSERT(index != InvalidIndex && index < mRecords.size());
284
return mRecords[index];
285
}
286
287
size_t CallDAG::size() const
288
{
289
return mRecords.size();
290
}
291
292
void CallDAG::clear()
293
{
294
mRecords.clear();
295
mFunctionIdToIndex.clear();
296
}
297
298
CallDAG::InitResult CallDAG::init(TIntermNode *root, TDiagnostics *diagnostics)
299
{
300
CallDAGCreator creator(diagnostics);
301
302
// Creates the mapping of functions to callees
303
root->traverse(&creator);
304
305
// Does the topological sort and detects recursions
306
InitResult result = creator.assignIndices();
307
if (result != INITDAG_SUCCESS)
308
{
309
return result;
310
}
311
312
creator.fillDataStructures(&mRecords, &mFunctionIdToIndex);
313
return INITDAG_SUCCESS;
314
}
315
316
} // namespace sh
317
318