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 &param)
186 {
187     return setFunctionCalled(FunctionId(op, &param));
188 }
189 
setFunctionCalled(TOperator op,const TType & param1,const TType & param2)190 bool BuiltInFunctionEmulator::setFunctionCalled(TOperator op,
191                                                 const TType &param1,
192                                                 const TType &param2)
193 {
194     return setFunctionCalled(FunctionId(op, &param1, &param2));
195 }
196 
setFunctionCalled(TOperator op,const TType & param1,const TType & param2,const TType & param3)197 bool BuiltInFunctionEmulator::setFunctionCalled(TOperator op,
198                                                 const TType &param1,
199                                                 const TType &param2,
200                                                 const TType &param3)
201 {
202     return setFunctionCalled(FunctionId(op, &param1, &param2, &param3));
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 &param1,
207                                                 const TType &param2,
208                                                 const TType &param3,
209                                                 const TType &param4)
210 {
211     return setFunctionCalled(FunctionId(op, &param1, &param2, &param3, &param4));
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