xref: /aosp_15_r20/external/angle/src/compiler/translator/tree_ops/RescopeGlobalVariables.cpp (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
1 //
2 // Copyright 2023 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 
7 #include <set>
8 
9 #include "compiler/translator/tree_ops/RescopeGlobalVariables.h"
10 #include "compiler/translator/tree_util/IntermTraverse.h"
11 #include "compiler/translator/tree_util/ReplaceVariable.h"
12 #include "compiler/translator/util.h"
13 
14 namespace sh
15 {
16 
17 ////////////////////////////////////////////////////////////////////////////////
18 
19 namespace
20 {
21 
22 class Rescoper : public TIntermTraverser
23 {
24   public:
25     struct VariableInfo
26     {
27         std::set<TIntermFunctionDefinition *> functions;
28         TIntermDeclaration *declaration;
29     };
30 
Rescoper(TSymbolTable * symbolTable)31     Rescoper(TSymbolTable *symbolTable) : TIntermTraverser(true, false, true, symbolTable) {}
32 
rescope(TCompiler * compiler,TIntermBlock & root)33     bool rescope(TCompiler *compiler, TIntermBlock &root)
34     {
35         if (mGlobalVarsNeedRescope.empty())
36         {
37             return true;
38         }
39 
40         // Insert the declarations into the first block in the function body and keep track of which
41         // ones have been moved, as well as the variables to replace.
42         VariableReplacementMap replacementMap;
43         std::set<TIntermDeclaration *> movedDeclarations;
44         for (auto &pair : mGlobalVarsNeedRescope)
45         {
46             if (pair.second.functions.size() == 1)
47             {
48                 TIntermFunctionDefinition *func = *pair.second.functions.begin();
49                 // Function* may be a nullptr if the variable was used in a
50                 // global initializer. Don't rescope, http://anglebug.com/42266827
51                 if (func != nullptr)
52                 {
53                     TIntermSequence *funcSequence = func->getBody()->getSequence();
54                     funcSequence->insert(funcSequence->begin(), pair.second.declaration);
55 
56                     TType *newType = new TType(pair.first->getType());
57                     newType->setQualifier(TQualifier::EvqTemporary);
58                     const TVariable *newVar =
59                         new TVariable(&compiler->getSymbolTable(), pair.first->name(), newType,
60                                       pair.first->symbolType(), pair.first->extensions());
61                     replacementMap[pair.first] = new TIntermSymbol(newVar);
62 
63                     movedDeclarations.insert(pair.second.declaration);
64                 }
65             }
66         }
67 
68         // Remove the global declarations that have been moved from the root block.
69         TIntermSequence *rootOriginal = root.getSequence();
70         TIntermSequence rootReplacement;
71         for (TIntermNode *node : *rootOriginal)
72         {
73             if (movedDeclarations.find(node->getAsDeclarationNode()) == movedDeclarations.end())
74             {
75                 rootReplacement.push_back(node);
76             }
77         }
78         *rootOriginal = std::move(rootReplacement);
79         return ReplaceVariables(compiler, &root, replacementMap);
80     }
81 
82   protected:
visitSymbol(TIntermSymbol * node)83     void visitSymbol(TIntermSymbol *node) override
84     {
85         const TVariable &var = node->variable();
86         // Check that the symbol is in the globals list, but is not LHS of
87         // the current global initialiser
88         if (&var != mCurrentGlobal &&
89             mGlobalVarsNeedRescope.find(&var) != mGlobalVarsNeedRescope.end())
90         {
91             std::set<TIntermFunctionDefinition *> &set = mGlobalVarsNeedRescope.at(&var).functions;
92             if (set.find(mCurrentFunction) == set.end())
93             {
94                 set.emplace(mCurrentFunction);
95             }
96         }
97     }
98 
visitDeclaration(Visit visit,TIntermDeclaration * node)99     bool visitDeclaration(Visit visit, TIntermDeclaration *node) override
100     {
101         if (visit == Visit::PreVisit)
102         {
103             Declaration decl           = ViewDeclaration(*node);
104             const TVariable &var       = decl.symbol.variable();
105             const TType &nodeType      = var.getType();
106             const TQualifier qualifier = nodeType.getQualifier();
107             if (qualifier == TQualifier::EvqGlobal && !nodeType.isStructSpecifier())
108             {
109                 mGlobalVarsNeedRescope.emplace(&var, VariableInfo());
110                 mGlobalVarsNeedRescope.at(&var).declaration = node;
111             }
112 
113             // A declaration outside function definition context would be a
114             // global variable, set the flag to avoid rescoping any variables
115             // used in initializers.
116             if (!mCurrentFunction)
117             {
118                 mCurrentGlobal = &var;
119             }
120         }
121         else if (visit == Visit::PostVisit)
122         {
123             if (!mCurrentFunction)
124             {
125                 mCurrentGlobal = nullptr;
126             }
127         }
128         return true;
129     }
130 
visitFunctionDefinition(Visit visit,TIntermFunctionDefinition * node)131     bool visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) override
132     {
133         if (visit == Visit::PreVisit)
134         {
135             TIntermFunctionDefinition *func = node->getAsFunctionDefinition();
136             if (func)
137             {
138                 mCurrentFunction = func;
139             }
140         }
141         else if (visit == Visit::PostVisit)
142         {
143             if (mCurrentFunction && mCurrentFunction == node->getAsFunctionDefinition())
144             {
145                 mCurrentFunction = nullptr;
146             }
147         }
148         return true;
149     }
150 
151   private:
152     TUnorderedMap<const TVariable *, VariableInfo> mGlobalVarsNeedRescope;
153     TIntermFunctionDefinition *mCurrentFunction = nullptr;
154     const TVariable *mCurrentGlobal             = nullptr;
155 };
156 
157 }  // anonymous namespace
158 
159 ////////////////////////////////////////////////////////////////////////////////
160 
RescopeGlobalVariables(TCompiler & compiler,TIntermBlock & root)161 bool RescopeGlobalVariables(TCompiler &compiler, TIntermBlock &root)
162 {
163     TSymbolTable &symbolTable = compiler.getSymbolTable();
164     Rescoper rescoper(&symbolTable);
165     root.traverse(&rescoper);
166     return rescoper.rescope(&compiler, root);
167 }
168 
169 }  // namespace sh
170