1 //
2 // Copyright (c) 2002-2011 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 #include "compiler/translator/BuiltInFunctionEmulator.h"
8 #include "angle_gl.h"
9 #include "compiler/translator/Cache.h"
10 #include "compiler/translator/IntermTraverse.h"
11 #include "compiler/translator/SymbolTable.h"
12
13 namespace sh
14 {
15
16 class BuiltInFunctionEmulator::BuiltInFunctionEmulationMarker : public TIntermTraverser
17 {
18 public:
BuiltInFunctionEmulationMarker(BuiltInFunctionEmulator & emulator)19 BuiltInFunctionEmulationMarker(BuiltInFunctionEmulator &emulator)
20 : TIntermTraverser(true, false, false), mEmulator(emulator)
21 {
22 }
23
visitUnary(Visit visit,TIntermUnary * node)24 bool visitUnary(Visit visit, TIntermUnary *node) override
25 {
26 if (visit == PreVisit)
27 {
28 bool needToEmulate =
29 mEmulator.setFunctionCalled(node->getOp(), node->getOperand()->getType());
30 if (needToEmulate)
31 node->setUseEmulatedFunction();
32 }
33 return true;
34 }
35
visitAggregate(Visit visit,TIntermAggregate * node)36 bool visitAggregate(Visit visit, TIntermAggregate *node) override
37 {
38 if (visit == PreVisit)
39 {
40 // Here we handle all the built-in functions mapped to ops, not just the ones that are
41 // currently identified as problematic.
42 if (node->isConstructor() || node->isFunctionCall())
43 {
44 return true;
45 }
46 const TIntermSequence &sequence = *(node->getSequence());
47 bool needToEmulate = false;
48 // Right now we only handle built-in functions with two to four parameters.
49 if (sequence.size() == 2)
50 {
51 TIntermTyped *param1 = sequence[0]->getAsTyped();
52 TIntermTyped *param2 = sequence[1]->getAsTyped();
53 if (!param1 || !param2)
54 return true;
55 needToEmulate = mEmulator.setFunctionCalled(node->getOp(), param1->getType(),
56 param2->getType());
57 }
58 else if (sequence.size() == 3)
59 {
60 TIntermTyped *param1 = sequence[0]->getAsTyped();
61 TIntermTyped *param2 = sequence[1]->getAsTyped();
62 TIntermTyped *param3 = sequence[2]->getAsTyped();
63 if (!param1 || !param2 || !param3)
64 return true;
65 needToEmulate = mEmulator.setFunctionCalled(node->getOp(), param1->getType(),
66 param2->getType(), param3->getType());
67 }
68 else if (sequence.size() == 4)
69 {
70 TIntermTyped *param1 = sequence[0]->getAsTyped();
71 TIntermTyped *param2 = sequence[1]->getAsTyped();
72 TIntermTyped *param3 = sequence[2]->getAsTyped();
73 TIntermTyped *param4 = sequence[3]->getAsTyped();
74 if (!param1 || !param2 || !param3 || !param4)
75 return true;
76 needToEmulate =
77 mEmulator.setFunctionCalled(node->getOp(), param1->getType(), param2->getType(),
78 param3->getType(), param4->getType());
79 }
80 else
81 {
82 return true;
83 }
84
85 if (needToEmulate)
86 node->setUseEmulatedFunction();
87 }
88 return true;
89 }
90
91 private:
92 BuiltInFunctionEmulator &mEmulator;
93 };
94
BuiltInFunctionEmulator()95 BuiltInFunctionEmulator::BuiltInFunctionEmulator()
96 {
97 }
98
addEmulatedFunction(TOperator op,const TType * param,const char * emulatedFunctionDefinition)99 FunctionId BuiltInFunctionEmulator::addEmulatedFunction(TOperator op,
100 const TType *param,
101 const char *emulatedFunctionDefinition)
102 {
103 FunctionId id(op, param);
104 mEmulatedFunctions[id] = std::string(emulatedFunctionDefinition);
105 return id;
106 }
107
addEmulatedFunction(TOperator op,const TType * param1,const TType * param2,const char * emulatedFunctionDefinition)108 FunctionId BuiltInFunctionEmulator::addEmulatedFunction(TOperator op,
109 const TType *param1,
110 const TType *param2,
111 const char *emulatedFunctionDefinition)
112 {
113 FunctionId id(op, param1, param2);
114 mEmulatedFunctions[id] = std::string(emulatedFunctionDefinition);
115 return id;
116 }
117
addEmulatedFunctionWithDependency(const FunctionId & dependency,TOperator op,const TType * param1,const TType * param2,const char * emulatedFunctionDefinition)118 FunctionId BuiltInFunctionEmulator::addEmulatedFunctionWithDependency(
119 const FunctionId &dependency,
120 TOperator op,
121 const TType *param1,
122 const TType *param2,
123 const char *emulatedFunctionDefinition)
124 {
125 FunctionId id(op, param1, param2);
126 mEmulatedFunctions[id] = std::string(emulatedFunctionDefinition);
127 mFunctionDependencies[id] = dependency;
128 return id;
129 }
130
addEmulatedFunction(TOperator op,const TType * param1,const TType * param2,const TType * param3,const char * emulatedFunctionDefinition)131 FunctionId BuiltInFunctionEmulator::addEmulatedFunction(TOperator op,
132 const TType *param1,
133 const TType *param2,
134 const TType *param3,
135 const char *emulatedFunctionDefinition)
136 {
137 FunctionId id(op, param1, param2, param3);
138 mEmulatedFunctions[id] = std::string(emulatedFunctionDefinition);
139 return id;
140 }
141
addEmulatedFunction(TOperator op,const TType * param1,const TType * param2,const TType * param3,const TType * param4,const char * emulatedFunctionDefinition)142 FunctionId BuiltInFunctionEmulator::addEmulatedFunction(TOperator op,
143 const TType *param1,
144 const TType *param2,
145 const TType *param3,
146 const TType *param4,
147 const char *emulatedFunctionDefinition)
148 {
149 FunctionId id(op, param1, param2, param3, param4);
150 mEmulatedFunctions[id] = std::string(emulatedFunctionDefinition);
151 return id;
152 }
153
addEmulatedFunctionWithDependency(const FunctionId & dependency,TOperator op,const TType * param1,const TType * param2,const TType * param3,const TType * param4,const char * emulatedFunctionDefinition)154 FunctionId BuiltInFunctionEmulator::addEmulatedFunctionWithDependency(
155 const FunctionId &dependency,
156 TOperator op,
157 const TType *param1,
158 const TType *param2,
159 const TType *param3,
160 const TType *param4,
161 const char *emulatedFunctionDefinition)
162 {
163 FunctionId id(op, param1, param2, param3, param4);
164 mEmulatedFunctions[id] = std::string(emulatedFunctionDefinition);
165 mFunctionDependencies[id] = dependency;
166 return id;
167 }
168
isOutputEmpty() const169 bool BuiltInFunctionEmulator::isOutputEmpty() const
170 {
171 return (mFunctions.size() == 0);
172 }
173
outputEmulatedFunctions(TInfoSinkBase & out) const174 void BuiltInFunctionEmulator::outputEmulatedFunctions(TInfoSinkBase &out) const
175 {
176 for (const auto &function : mFunctions)
177 {
178 const char *body = findEmulatedFunction(function);
179 ASSERT(body);
180 out << body;
181 out << "\n\n";
182 }
183 }
184
setFunctionCalled(TOperator op,const TType & param)185 bool BuiltInFunctionEmulator::setFunctionCalled(TOperator op, const TType ¶m)
186 {
187 return setFunctionCalled(FunctionId(op, ¶m));
188 }
189
setFunctionCalled(TOperator op,const TType & param1,const TType & param2)190 bool BuiltInFunctionEmulator::setFunctionCalled(TOperator op,
191 const TType ¶m1,
192 const TType ¶m2)
193 {
194 return setFunctionCalled(FunctionId(op, ¶m1, ¶m2));
195 }
196
setFunctionCalled(TOperator op,const TType & param1,const TType & param2,const TType & param3)197 bool BuiltInFunctionEmulator::setFunctionCalled(TOperator op,
198 const TType ¶m1,
199 const TType ¶m2,
200 const TType ¶m3)
201 {
202 return setFunctionCalled(FunctionId(op, ¶m1, ¶m2, ¶m3));
203 }
204
setFunctionCalled(TOperator op,const TType & param1,const TType & param2,const TType & param3,const TType & param4)205 bool BuiltInFunctionEmulator::setFunctionCalled(TOperator op,
206 const TType ¶m1,
207 const TType ¶m2,
208 const TType ¶m3,
209 const TType ¶m4)
210 {
211 return setFunctionCalled(FunctionId(op, ¶m1, ¶m2, ¶m3, ¶m4));
212 }
213
findEmulatedFunction(const FunctionId & functionId) const214 const char *BuiltInFunctionEmulator::findEmulatedFunction(const FunctionId &functionId) const
215 {
216 for (const auto &queryFunction : mQueryFunctions)
217 {
218 const char *result = queryFunction(functionId);
219 if (result)
220 {
221 return result;
222 }
223 }
224
225 const auto &result = mEmulatedFunctions.find(functionId);
226 if (result != mEmulatedFunctions.end())
227 {
228 return result->second.c_str();
229 }
230
231 return nullptr;
232 }
233
setFunctionCalled(const FunctionId & functionId)234 bool BuiltInFunctionEmulator::setFunctionCalled(const FunctionId &functionId)
235 {
236 if (!findEmulatedFunction(functionId))
237 {
238 return false;
239 }
240
241 for (size_t i = 0; i < mFunctions.size(); ++i)
242 {
243 if (mFunctions[i] == functionId)
244 return true;
245 }
246 // If the function depends on another, mark the dependency as called.
247 auto dependency = mFunctionDependencies.find(functionId);
248 if (dependency != mFunctionDependencies.end())
249 {
250 setFunctionCalled((*dependency).second);
251 }
252 // Copy the functionId if it needs to be stored, to make sure that the TType pointers inside
253 // remain valid and constant.
254 mFunctions.push_back(functionId.getCopy());
255 return true;
256 }
257
markBuiltInFunctionsForEmulation(TIntermNode * root)258 void BuiltInFunctionEmulator::markBuiltInFunctionsForEmulation(TIntermNode *root)
259 {
260 ASSERT(root);
261
262 if (mEmulatedFunctions.empty() && mQueryFunctions.empty())
263 return;
264
265 BuiltInFunctionEmulationMarker marker(*this);
266 root->traverse(&marker);
267 }
268
cleanup()269 void BuiltInFunctionEmulator::cleanup()
270 {
271 mFunctions.clear();
272 mFunctionDependencies.clear();
273 }
274
addFunctionMap(BuiltinQueryFunc queryFunc)275 void BuiltInFunctionEmulator::addFunctionMap(BuiltinQueryFunc queryFunc)
276 {
277 mQueryFunctions.push_back(queryFunc);
278 }
279
280 // static
WriteEmulatedFunctionName(TInfoSinkBase & out,const char * name)281 void BuiltInFunctionEmulator::WriteEmulatedFunctionName(TInfoSinkBase &out, const char *name)
282 {
283 ASSERT(name[strlen(name) - 1] != '(');
284 out << name << "_emu";
285 }
286
FunctionId()287 FunctionId::FunctionId()
288 : mOp(EOpNull),
289 mParam1(TCache::getType(EbtVoid)),
290 mParam2(TCache::getType(EbtVoid)),
291 mParam3(TCache::getType(EbtVoid)),
292 mParam4(TCache::getType(EbtVoid))
293 {
294 }
295
FunctionId(TOperator op,const TType * param)296 FunctionId::FunctionId(TOperator op, const TType *param)
297 : mOp(op),
298 mParam1(param),
299 mParam2(TCache::getType(EbtVoid)),
300 mParam3(TCache::getType(EbtVoid)),
301 mParam4(TCache::getType(EbtVoid))
302 {
303 }
304
FunctionId(TOperator op,const TType * param1,const TType * param2)305 FunctionId::FunctionId(TOperator op, const TType *param1, const TType *param2)
306 : mOp(op),
307 mParam1(param1),
308 mParam2(param2),
309 mParam3(TCache::getType(EbtVoid)),
310 mParam4(TCache::getType(EbtVoid))
311 {
312 }
313
FunctionId(TOperator op,const TType * param1,const TType * param2,const TType * param3)314 FunctionId::FunctionId(TOperator op, const TType *param1, const TType *param2, const TType *param3)
315 : mOp(op), mParam1(param1), mParam2(param2), mParam3(param3), mParam4(TCache::getType(EbtVoid))
316 {
317 }
318
FunctionId(TOperator op,const TType * param1,const TType * param2,const TType * param3,const TType * param4)319 FunctionId::FunctionId(TOperator op,
320 const TType *param1,
321 const TType *param2,
322 const TType *param3,
323 const TType *param4)
324 : mOp(op), mParam1(param1), mParam2(param2), mParam3(param3), mParam4(param4)
325 {
326 }
327
operator ==(const FunctionId & other) const328 bool FunctionId::operator==(const FunctionId &other) const
329 {
330 return (mOp == other.mOp && *mParam1 == *other.mParam1 && *mParam2 == *other.mParam2 &&
331 *mParam3 == *other.mParam3 && *mParam4 == *other.mParam4);
332 }
333
operator <(const FunctionId & other) const334 bool FunctionId::operator<(const FunctionId &other) const
335 {
336 if (mOp != other.mOp)
337 return mOp < other.mOp;
338 if (*mParam1 != *other.mParam1)
339 return *mParam1 < *other.mParam1;
340 if (*mParam2 != *other.mParam2)
341 return *mParam2 < *other.mParam2;
342 if (*mParam3 != *other.mParam3)
343 return *mParam3 < *other.mParam3;
344 if (*mParam4 != *other.mParam4)
345 return *mParam4 < *other.mParam4;
346 return false; // all fields are equal
347 }
348
getCopy() const349 FunctionId FunctionId::getCopy() const
350 {
351 return FunctionId(mOp, new TType(*mParam1), new TType(*mParam2), new TType(*mParam3),
352 new TType(*mParam4));
353 }
354
355 } // namespace sh
356