ReplaceShadowingVariables.cpp (5492B)
1 // 2 // Copyright 2019 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 // ReplaceShadowingVariables.cpp: Replace all references to any variable in the AST that is 7 // a redefinition of a variable in a nested scope. This is a useful for ESSL 1.00 shaders 8 // where the spec section "4.2.3. Redeclaring Variables" states "However, a nested scope can 9 // override an outer scope's declaration of a particular variable name." This is changed in 10 // later spec versions, such as ESSL 3.20 spec which states "If [a variable] is declared as 11 // a parameter in a function definition, it is scoped until the end of that function 12 // definition. A function's parameter declarations and body together form a single scope." 13 // 14 // So this class is useful when translating from ESSL 1.00 shaders, where function body var 15 // redefinition is allowed, to later shader versions where it's not allowed. 16 // 17 18 #include "compiler/translator/tree_util/ReplaceShadowingVariables.h" 19 #include "compiler/translator/tree_util/ReplaceVariable.h" 20 21 #include "compiler/translator/Compiler.h" 22 #include "compiler/translator/IntermNode.h" 23 #include "compiler/translator/Symbol.h" 24 #include "compiler/translator/SymbolTable.h" 25 #include "compiler/translator/tree_util/IntermNode_util.h" 26 #include "compiler/translator/tree_util/IntermTraverse.h" 27 28 #include <unordered_set> 29 30 namespace sh 31 { 32 33 namespace 34 { 35 36 // Custom struct to queue up any replacements until after AST traversal 37 struct DeferredReplacementBlock 38 { 39 const TVariable *originalVariable; // variable to be replaced 40 TVariable *replacementVariable; // variable to replace originalVar with 41 TIntermBlock *functionBody; // function body where replacement occurs 42 }; 43 44 class ReplaceShadowingVariablesTraverser : public TIntermTraverser 45 { 46 public: 47 ReplaceShadowingVariablesTraverser(TSymbolTable *symbolTable) 48 : TIntermTraverser(true, true, true, symbolTable), mParameterNames{}, mFunctionBody(nullptr) 49 {} 50 51 bool visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) override 52 { 53 // In pre-visit of function, record params 54 if (visit == PreVisit) 55 { 56 ASSERT(mParameterNames.size() == 0); 57 const TFunction *func = node->getFunctionPrototype()->getFunction(); 58 // Grab all of the parameter names from the function prototype 59 size_t paramCount = func->getParamCount(); 60 for (size_t i = 0; i < paramCount; ++i) 61 { 62 mParameterNames.emplace(std::string(func->getParam(i)->name().data())); 63 } 64 if (mParameterNames.size() > 0) 65 mFunctionBody = node->getBody(); 66 } 67 else if (visit == PostVisit) 68 { 69 // Clear data saved from function definition 70 mParameterNames.clear(); 71 mFunctionBody = nullptr; 72 } 73 return true; 74 } 75 bool visitDeclaration(Visit visit, TIntermDeclaration *node) override 76 { 77 if (visit == PreVisit && mParameterNames.size() != 0) 78 { 79 TIntermSequence *decls = node->getSequence(); 80 for (auto &declVector : *decls) 81 { 82 // no init case 83 TIntermSymbol *symNode = declVector->getAsSymbolNode(); 84 if (symNode == nullptr) 85 { 86 // init case 87 TIntermBinary *binaryNode = declVector->getAsBinaryNode(); 88 ASSERT(binaryNode->getOp() == EOpInitialize); 89 symNode = binaryNode->getLeft()->getAsSymbolNode(); 90 } 91 ASSERT(symNode != nullptr); 92 std::string varName = std::string(symNode->variable().name().data()); 93 if (mParameterNames.count(varName) > 0) 94 { 95 // We found a redefined var so queue replacement 96 mReplacements.emplace_back(DeferredReplacementBlock{ 97 &symNode->variable(), 98 CreateTempVariable(mSymbolTable, &symNode->variable().getType()), 99 mFunctionBody}); 100 } 101 } 102 } 103 return true; 104 } 105 // Perform replacement of vars for any deferred replacements that were identified 106 [[nodiscard]] bool executeReplacements(TCompiler *compiler) 107 { 108 for (DeferredReplacementBlock &replace : mReplacements) 109 { 110 if (!ReplaceVariable(compiler, replace.functionBody, replace.originalVariable, 111 replace.replacementVariable)) 112 { 113 return false; 114 } 115 } 116 mReplacements.clear(); 117 return true; 118 } 119 120 private: 121 std::unordered_set<std::string> mParameterNames; 122 TIntermBlock *mFunctionBody; 123 std::vector<DeferredReplacementBlock> mReplacements; 124 }; 125 126 } // anonymous namespace 127 128 // Replaces every occurrence of a variable with another variable. 129 [[nodiscard]] bool ReplaceShadowingVariables(TCompiler *compiler, 130 TIntermBlock *root, 131 TSymbolTable *symbolTable) 132 { 133 ReplaceShadowingVariablesTraverser traverser(symbolTable); 134 root->traverse(&traverser); 135 if (!traverser.executeReplacements(compiler)) 136 { 137 return false; 138 } 139 return traverser.updateTree(compiler, root); 140 } 141 142 } // namespace sh