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/IntermNode.h"
22 #include "compiler/translator/Symbol.h"
23 #include "compiler/translator/SymbolTable.h"
24 #include "compiler/translator/tree_util/IntermNode_util.h"
25 #include "compiler/translator/tree_util/IntermTraverse.h"
26
27 #include <unordered_set>
28
29 namespace sh
30 {
31
32 namespace
33 {
34
35 // Custom struct to queue up any replacements until after AST traversal
36 struct DeferredReplacementBlock
37 {
38 const TVariable *originalVariable; // variable to be replaced
39 TVariable *replacementVariable; // variable to replace originalVar with
40 TIntermBlock *functionBody; // function body where replacement occurs
41 };
42
43 class ReplaceShadowingVariablesTraverser : public TIntermTraverser
44 {
45 public:
ReplaceShadowingVariablesTraverser(TSymbolTable * symbolTable)46 ReplaceShadowingVariablesTraverser(TSymbolTable *symbolTable)
47 : TIntermTraverser(true, true, true),
48 mSymbolTable(symbolTable),
49 mParameterNames{},
50 mFunctionBody(nullptr)
51 {}
52
visitFunctionDefinition(Visit visit,TIntermFunctionDefinition * node)53 bool visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) override
54 {
55 // In pre-visit of function, record params
56 if (visit == PreVisit)
57 {
58 ASSERT(mParameterNames.size() == 0);
59 const TFunction *func = node->getFunctionPrototype()->getFunction();
60 // Grab all of the parameter names from the function prototype
61 size_t paramCount = func->getParamCount();
62 for (size_t i = 0; i < paramCount; ++i)
63 {
64 mParameterNames.emplace(std::string(func->getParam(i)->name().data()));
65 }
66 if (mParameterNames.size() > 0)
67 mFunctionBody = node->getBody();
68 }
69 else if (visit == PostVisit)
70 {
71 // Clear data saved from function definition
72 mParameterNames.clear();
73 mFunctionBody = nullptr;
74 }
75 return true;
76 }
visitDeclaration(Visit visit,TIntermDeclaration * node)77 bool visitDeclaration(Visit visit, TIntermDeclaration *node) override
78 {
79 if (visit == PreVisit && mParameterNames.size() != 0)
80 {
81 TIntermSequence *decls = node->getSequence();
82 for (auto &declVector : *decls)
83 {
84 // no init case
85 TIntermSymbol *symNode = declVector->getAsSymbolNode();
86 if (symNode == nullptr)
87 {
88 // init case
89 TIntermBinary *binaryNode = declVector->getAsBinaryNode();
90 ASSERT(binaryNode->getOp() == EOpInitialize);
91 symNode = binaryNode->getLeft()->getAsSymbolNode();
92 }
93 ASSERT(symNode != nullptr);
94 std::string varName = std::string(symNode->variable().name().data());
95 if (mParameterNames.count(varName) > 0)
96 {
97 // We found a redefined var so queue replacement
98 mReplacements.emplace_back(DeferredReplacementBlock{
99 &symNode->variable(),
100 CreateTempVariable(mSymbolTable, &symNode->variable().getType()),
101 mFunctionBody});
102 }
103 }
104 }
105 return true;
106 }
107 // Perform replacement of vars for any deferred replacements that were identified
executeReplacements()108 void executeReplacements()
109 {
110 for (DeferredReplacementBlock &replace : mReplacements)
111 {
112 ReplaceVariable(replace.functionBody, replace.originalVariable,
113 replace.replacementVariable);
114 }
115 mReplacements.clear();
116 }
117
118 private:
119 TSymbolTable *mSymbolTable;
120 std::unordered_set<std::string> mParameterNames;
121 TIntermBlock *mFunctionBody;
122 std::vector<DeferredReplacementBlock> mReplacements;
123 };
124
125 } // anonymous namespace
126
127 // Replaces every occurrence of a variable with another variable.
ReplaceShadowingVariables(TIntermBlock * root,TSymbolTable * symbolTable)128 void ReplaceShadowingVariables(TIntermBlock *root, TSymbolTable *symbolTable)
129 {
130 ReplaceShadowingVariablesTraverser traverser(symbolTable);
131 root->traverse(&traverser);
132 traverser.executeReplacements();
133 traverser.updateTree();
134 }
135
136 } // namespace sh
137