| // |
| // Copyright (c) 2002-2011 The ANGLE Project Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| // |
| |
| #include "compiler/translator/BuiltInFunctionEmulator.h" |
| #include "angle_gl.h" |
| #include "compiler/translator/Cache.h" |
| #include "compiler/translator/IntermTraverse.h" |
| #include "compiler/translator/SymbolTable.h" |
| |
| namespace sh |
| { |
| |
| class BuiltInFunctionEmulator::BuiltInFunctionEmulationMarker : public TIntermTraverser |
| { |
| public: |
| BuiltInFunctionEmulationMarker(BuiltInFunctionEmulator &emulator) |
| : TIntermTraverser(true, false, false), mEmulator(emulator) |
| { |
| } |
| |
| bool visitUnary(Visit visit, TIntermUnary *node) override |
| { |
| if (visit == PreVisit) |
| { |
| bool needToEmulate = |
| mEmulator.setFunctionCalled(node->getOp(), node->getOperand()->getType()); |
| if (needToEmulate) |
| node->setUseEmulatedFunction(); |
| } |
| return true; |
| } |
| |
| bool visitAggregate(Visit visit, TIntermAggregate *node) override |
| { |
| if (visit == PreVisit) |
| { |
| // Here we handle all the built-in functions mapped to ops, not just the ones that are |
| // currently identified as problematic. |
| if (node->isConstructor() || node->isFunctionCall()) |
| { |
| return true; |
| } |
| const TIntermSequence &sequence = *(node->getSequence()); |
| bool needToEmulate = false; |
| // Right now we only handle built-in functions with two to four parameters. |
| if (sequence.size() == 2) |
| { |
| TIntermTyped *param1 = sequence[0]->getAsTyped(); |
| TIntermTyped *param2 = sequence[1]->getAsTyped(); |
| if (!param1 || !param2) |
| return true; |
| needToEmulate = mEmulator.setFunctionCalled(node->getOp(), param1->getType(), |
| param2->getType()); |
| } |
| else if (sequence.size() == 3) |
| { |
| TIntermTyped *param1 = sequence[0]->getAsTyped(); |
| TIntermTyped *param2 = sequence[1]->getAsTyped(); |
| TIntermTyped *param3 = sequence[2]->getAsTyped(); |
| if (!param1 || !param2 || !param3) |
| return true; |
| needToEmulate = mEmulator.setFunctionCalled(node->getOp(), param1->getType(), |
| param2->getType(), param3->getType()); |
| } |
| else if (sequence.size() == 4) |
| { |
| TIntermTyped *param1 = sequence[0]->getAsTyped(); |
| TIntermTyped *param2 = sequence[1]->getAsTyped(); |
| TIntermTyped *param3 = sequence[2]->getAsTyped(); |
| TIntermTyped *param4 = sequence[3]->getAsTyped(); |
| if (!param1 || !param2 || !param3 || !param4) |
| return true; |
| needToEmulate = |
| mEmulator.setFunctionCalled(node->getOp(), param1->getType(), param2->getType(), |
| param3->getType(), param4->getType()); |
| } |
| else |
| { |
| return true; |
| } |
| |
| if (needToEmulate) |
| node->setUseEmulatedFunction(); |
| } |
| return true; |
| } |
| |
| private: |
| BuiltInFunctionEmulator &mEmulator; |
| }; |
| |
| BuiltInFunctionEmulator::BuiltInFunctionEmulator() |
| { |
| } |
| |
| FunctionId BuiltInFunctionEmulator::addEmulatedFunction(TOperator op, |
| const TType *param, |
| const char *emulatedFunctionDefinition) |
| { |
| FunctionId id(op, param); |
| mEmulatedFunctions[id] = std::string(emulatedFunctionDefinition); |
| return id; |
| } |
| |
| FunctionId BuiltInFunctionEmulator::addEmulatedFunction(TOperator op, |
| const TType *param1, |
| const TType *param2, |
| const char *emulatedFunctionDefinition) |
| { |
| FunctionId id(op, param1, param2); |
| mEmulatedFunctions[id] = std::string(emulatedFunctionDefinition); |
| return id; |
| } |
| |
| FunctionId BuiltInFunctionEmulator::addEmulatedFunctionWithDependency( |
| const FunctionId &dependency, |
| TOperator op, |
| const TType *param1, |
| const TType *param2, |
| const char *emulatedFunctionDefinition) |
| { |
| FunctionId id(op, param1, param2); |
| mEmulatedFunctions[id] = std::string(emulatedFunctionDefinition); |
| mFunctionDependencies[id] = dependency; |
| return id; |
| } |
| |
| FunctionId BuiltInFunctionEmulator::addEmulatedFunction(TOperator op, |
| const TType *param1, |
| const TType *param2, |
| const TType *param3, |
| const char *emulatedFunctionDefinition) |
| { |
| FunctionId id(op, param1, param2, param3); |
| mEmulatedFunctions[id] = std::string(emulatedFunctionDefinition); |
| return id; |
| } |
| |
| FunctionId BuiltInFunctionEmulator::addEmulatedFunction(TOperator op, |
| const TType *param1, |
| const TType *param2, |
| const TType *param3, |
| const TType *param4, |
| const char *emulatedFunctionDefinition) |
| { |
| FunctionId id(op, param1, param2, param3, param4); |
| mEmulatedFunctions[id] = std::string(emulatedFunctionDefinition); |
| return id; |
| } |
| |
| FunctionId BuiltInFunctionEmulator::addEmulatedFunctionWithDependency( |
| const FunctionId &dependency, |
| TOperator op, |
| const TType *param1, |
| const TType *param2, |
| const TType *param3, |
| const TType *param4, |
| const char *emulatedFunctionDefinition) |
| { |
| FunctionId id(op, param1, param2, param3, param4); |
| mEmulatedFunctions[id] = std::string(emulatedFunctionDefinition); |
| mFunctionDependencies[id] = dependency; |
| return id; |
| } |
| |
| bool BuiltInFunctionEmulator::isOutputEmpty() const |
| { |
| return (mFunctions.size() == 0); |
| } |
| |
| void BuiltInFunctionEmulator::outputEmulatedFunctions(TInfoSinkBase &out) const |
| { |
| for (const auto &function : mFunctions) |
| { |
| const char *body = findEmulatedFunction(function); |
| ASSERT(body); |
| out << body; |
| out << "\n\n"; |
| } |
| } |
| |
| bool BuiltInFunctionEmulator::setFunctionCalled(TOperator op, const TType ¶m) |
| { |
| return setFunctionCalled(FunctionId(op, ¶m)); |
| } |
| |
| bool BuiltInFunctionEmulator::setFunctionCalled(TOperator op, |
| const TType ¶m1, |
| const TType ¶m2) |
| { |
| return setFunctionCalled(FunctionId(op, ¶m1, ¶m2)); |
| } |
| |
| bool BuiltInFunctionEmulator::setFunctionCalled(TOperator op, |
| const TType ¶m1, |
| const TType ¶m2, |
| const TType ¶m3) |
| { |
| return setFunctionCalled(FunctionId(op, ¶m1, ¶m2, ¶m3)); |
| } |
| |
| bool BuiltInFunctionEmulator::setFunctionCalled(TOperator op, |
| const TType ¶m1, |
| const TType ¶m2, |
| const TType ¶m3, |
| const TType ¶m4) |
| { |
| return setFunctionCalled(FunctionId(op, ¶m1, ¶m2, ¶m3, ¶m4)); |
| } |
| |
| const char *BuiltInFunctionEmulator::findEmulatedFunction(const FunctionId &functionId) const |
| { |
| for (const auto &queryFunction : mQueryFunctions) |
| { |
| const char *result = queryFunction(functionId); |
| if (result) |
| { |
| return result; |
| } |
| } |
| |
| const auto &result = mEmulatedFunctions.find(functionId); |
| if (result != mEmulatedFunctions.end()) |
| { |
| return result->second.c_str(); |
| } |
| |
| return nullptr; |
| } |
| |
| bool BuiltInFunctionEmulator::setFunctionCalled(const FunctionId &functionId) |
| { |
| if (!findEmulatedFunction(functionId)) |
| { |
| return false; |
| } |
| |
| for (size_t i = 0; i < mFunctions.size(); ++i) |
| { |
| if (mFunctions[i] == functionId) |
| return true; |
| } |
| // If the function depends on another, mark the dependency as called. |
| auto dependency = mFunctionDependencies.find(functionId); |
| if (dependency != mFunctionDependencies.end()) |
| { |
| setFunctionCalled((*dependency).second); |
| } |
| // Copy the functionId if it needs to be stored, to make sure that the TType pointers inside |
| // remain valid and constant. |
| mFunctions.push_back(functionId.getCopy()); |
| return true; |
| } |
| |
| void BuiltInFunctionEmulator::markBuiltInFunctionsForEmulation(TIntermNode *root) |
| { |
| ASSERT(root); |
| |
| if (mEmulatedFunctions.empty() && mQueryFunctions.empty()) |
| return; |
| |
| BuiltInFunctionEmulationMarker marker(*this); |
| root->traverse(&marker); |
| } |
| |
| void BuiltInFunctionEmulator::cleanup() |
| { |
| mFunctions.clear(); |
| mFunctionDependencies.clear(); |
| } |
| |
| void BuiltInFunctionEmulator::addFunctionMap(BuiltinQueryFunc queryFunc) |
| { |
| mQueryFunctions.push_back(queryFunc); |
| } |
| |
| // static |
| void BuiltInFunctionEmulator::WriteEmulatedFunctionName(TInfoSinkBase &out, const char *name) |
| { |
| ASSERT(name[strlen(name) - 1] != '('); |
| out << name << "_emu"; |
| } |
| |
| FunctionId::FunctionId() |
| : mOp(EOpNull), |
| mParam1(TCache::getType(EbtVoid)), |
| mParam2(TCache::getType(EbtVoid)), |
| mParam3(TCache::getType(EbtVoid)), |
| mParam4(TCache::getType(EbtVoid)) |
| { |
| } |
| |
| FunctionId::FunctionId(TOperator op, const TType *param) |
| : mOp(op), |
| mParam1(param), |
| mParam2(TCache::getType(EbtVoid)), |
| mParam3(TCache::getType(EbtVoid)), |
| mParam4(TCache::getType(EbtVoid)) |
| { |
| } |
| |
| FunctionId::FunctionId(TOperator op, const TType *param1, const TType *param2) |
| : mOp(op), |
| mParam1(param1), |
| mParam2(param2), |
| mParam3(TCache::getType(EbtVoid)), |
| mParam4(TCache::getType(EbtVoid)) |
| { |
| } |
| |
| FunctionId::FunctionId(TOperator op, const TType *param1, const TType *param2, const TType *param3) |
| : mOp(op), mParam1(param1), mParam2(param2), mParam3(param3), mParam4(TCache::getType(EbtVoid)) |
| { |
| } |
| |
| FunctionId::FunctionId(TOperator op, |
| const TType *param1, |
| const TType *param2, |
| const TType *param3, |
| const TType *param4) |
| : mOp(op), mParam1(param1), mParam2(param2), mParam3(param3), mParam4(param4) |
| { |
| } |
| |
| bool FunctionId::operator==(const FunctionId &other) const |
| { |
| return (mOp == other.mOp && *mParam1 == *other.mParam1 && *mParam2 == *other.mParam2 && |
| *mParam3 == *other.mParam3 && *mParam4 == *other.mParam4); |
| } |
| |
| bool FunctionId::operator<(const FunctionId &other) const |
| { |
| if (mOp != other.mOp) |
| return mOp < other.mOp; |
| if (*mParam1 != *other.mParam1) |
| return *mParam1 < *other.mParam1; |
| if (*mParam2 != *other.mParam2) |
| return *mParam2 < *other.mParam2; |
| if (*mParam3 != *other.mParam3) |
| return *mParam3 < *other.mParam3; |
| if (*mParam4 != *other.mParam4) |
| return *mParam4 < *other.mParam4; |
| return false; // all fields are equal |
| } |
| |
| FunctionId FunctionId::getCopy() const |
| { |
| return FunctionId(mOp, new TType(*mParam1), new TType(*mParam2), new TType(*mParam3), |
| new TType(*mParam4)); |
| } |
| |
| } // namespace sh |