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