xref: /aosp_15_r20/external/angle/src/compiler/translator/tree_util/ReplaceShadowingVariables.cpp (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
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:
ReplaceShadowingVariablesTraverser(TSymbolTable * symbolTable)47     ReplaceShadowingVariablesTraverser(TSymbolTable *symbolTable)
48         : TIntermTraverser(true, true, true, symbolTable), mParameterNames{}, mFunctionBody(nullptr)
49     {}
50 
visitFunctionDefinition(Visit visit,TIntermFunctionDefinition * node)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     }
visitDeclaration(Visit visit,TIntermDeclaration * node)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                                            EvqTemporary),
100                         mFunctionBody});
101                 }
102             }
103         }
104         return true;
105     }
106     // Perform replacement of vars for any deferred replacements that were identified
executeReplacements(TCompiler * compiler)107     [[nodiscard]] bool executeReplacements(TCompiler *compiler)
108     {
109         for (DeferredReplacementBlock &replace : mReplacements)
110         {
111             if (!ReplaceVariable(compiler, replace.functionBody, replace.originalVariable,
112                                  replace.replacementVariable))
113             {
114                 return false;
115             }
116         }
117         mReplacements.clear();
118         return true;
119     }
120 
121   private:
122     std::unordered_set<std::string> mParameterNames;
123     TIntermBlock *mFunctionBody;
124     std::vector<DeferredReplacementBlock> mReplacements;
125 };
126 
127 }  // anonymous namespace
128 
129 // Replaces every occurrence of a variable with another variable.
ReplaceShadowingVariables(TCompiler * compiler,TIntermBlock * root,TSymbolTable * symbolTable)130 [[nodiscard]] bool ReplaceShadowingVariables(TCompiler *compiler,
131                                              TIntermBlock *root,
132                                              TSymbolTable *symbolTable)
133 {
134     ReplaceShadowingVariablesTraverser traverser(symbolTable);
135     root->traverse(&traverser);
136     if (!traverser.executeReplacements(compiler))
137     {
138         return false;
139     }
140     return traverser.updateTree(compiler, root);
141 }
142 
143 }  // namespace sh
144