• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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