| // | |
| // Copyright 2019 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. | |
| // | |
| // ReplaceShadowingVariables.cpp: Replace all references to any variable in the AST that is | |
| // a redefinition of a variable in a nested scope. This is a useful for ESSL 1.00 shaders | |
| // where the spec section "4.2.3. Redeclaring Variables" states "However, a nested scope can | |
| // override an outer scope's declaration of a particular variable name." This is changed in | |
| // later spec versions, such as ESSL 3.20 spec which states "If [a variable] is declared as | |
| // a parameter in a function definition, it is scoped until the end of that function | |
| // definition. A function's parameter declarations and body together form a single scope." | |
| // | |
| // So this class is useful when translating from ESSL 1.00 shaders, where function body var | |
| // redefinition is allowed, to later shader versions where it's not allowed. | |
| // | |
| #include "compiler/translator/tree_util/ReplaceShadowingVariables.h" | |
| #include "compiler/translator/tree_util/ReplaceVariable.h" | |
| #include "compiler/translator/Compiler.h" | |
| #include "compiler/translator/IntermNode.h" | |
| #include "compiler/translator/Symbol.h" | |
| #include "compiler/translator/SymbolTable.h" | |
| #include "compiler/translator/tree_util/IntermNode_util.h" | |
| #include "compiler/translator/tree_util/IntermTraverse.h" | |
| #include <unordered_set> | |
| namespace sh | |
| { | |
| namespace | |
| { | |
| // Custom struct to queue up any replacements until after AST traversal | |
| struct DeferredReplacementBlock | |
| { | |
| const TVariable *originalVariable; // variable to be replaced | |
| TVariable *replacementVariable; // variable to replace originalVar with | |
| TIntermBlock *functionBody; // function body where replacement occurs | |
| }; | |
| class ReplaceShadowingVariablesTraverser : public TIntermTraverser | |
| { | |
| public: | |
| ReplaceShadowingVariablesTraverser(TSymbolTable *symbolTable) | |
| : TIntermTraverser(true, true, true, symbolTable), mParameterNames{}, mFunctionBody(nullptr) | |
| {} | |
| bool visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) override | |
| { | |
| // In pre-visit of function, record params | |
| if (visit == PreVisit) | |
| { | |
| ASSERT(mParameterNames.size() == 0); | |
| const TFunction *func = node->getFunctionPrototype()->getFunction(); | |
| // Grab all of the parameter names from the function prototype | |
| size_t paramCount = func->getParamCount(); | |
| for (size_t i = 0; i < paramCount; ++i) | |
| { | |
| mParameterNames.emplace(std::string(func->getParam(i)->name().data())); | |
| } | |
| if (mParameterNames.size() > 0) | |
| mFunctionBody = node->getBody(); | |
| } | |
| else if (visit == PostVisit) | |
| { | |
| // Clear data saved from function definition | |
| mParameterNames.clear(); | |
| mFunctionBody = nullptr; | |
| } | |
| return true; | |
| } | |
| bool visitDeclaration(Visit visit, TIntermDeclaration *node) override | |
| { | |
| if (visit == PreVisit && mParameterNames.size() != 0) | |
| { | |
| TIntermSequence *decls = node->getSequence(); | |
| for (auto &declVector : *decls) | |
| { | |
| // no init case | |
| TIntermSymbol *symNode = declVector->getAsSymbolNode(); | |
| if (symNode == nullptr) | |
| { | |
| // init case | |
| TIntermBinary *binaryNode = declVector->getAsBinaryNode(); | |
| ASSERT(binaryNode->getOp() == EOpInitialize); | |
| symNode = binaryNode->getLeft()->getAsSymbolNode(); | |
| } | |
| ASSERT(symNode != nullptr); | |
| std::string varName = std::string(symNode->variable().name().data()); | |
| if (mParameterNames.count(varName) > 0) | |
| { | |
| // We found a redefined var so queue replacement | |
| mReplacements.emplace_back(DeferredReplacementBlock{ | |
| &symNode->variable(), | |
| CreateTempVariable(mSymbolTable, &symNode->variable().getType()), | |
| mFunctionBody}); | |
| } | |
| } | |
| } | |
| return true; | |
| } | |
| // Perform replacement of vars for any deferred replacements that were identified | |
| ANGLE_NO_DISCARD bool executeReplacements(TCompiler *compiler) | |
| { | |
| for (DeferredReplacementBlock &replace : mReplacements) | |
| { | |
| if (!ReplaceVariable(compiler, replace.functionBody, replace.originalVariable, | |
| replace.replacementVariable)) | |
| { | |
| return false; | |
| } | |
| } | |
| mReplacements.clear(); | |
| return true; | |
| } | |
| private: | |
| std::unordered_set<std::string> mParameterNames; | |
| TIntermBlock *mFunctionBody; | |
| std::vector<DeferredReplacementBlock> mReplacements; | |
| }; | |
| } // anonymous namespace | |
| // Replaces every occurrence of a variable with another variable. | |
| ANGLE_NO_DISCARD bool ReplaceShadowingVariables(TCompiler *compiler, | |
| TIntermBlock *root, | |
| TSymbolTable *symbolTable) | |
| { | |
| ReplaceShadowingVariablesTraverser traverser(symbolTable); | |
| root->traverse(&traverser); | |
| if (!traverser.executeReplacements(compiler)) | |
| { | |
| return false; | |
| } | |
| return traverser.updateTree(compiler, root); | |
| } | |
| } // namespace sh |