1 //
2 // Copyright 2002 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 "compiler/translator/tree_ops/InitializeVariables.h"
8
9 #include "angle_gl.h"
10 #include "common/debug.h"
11 #include "compiler/translator/Compiler.h"
12 #include "compiler/translator/StaticType.h"
13 #include "compiler/translator/SymbolTable.h"
14 #include "compiler/translator/tree_util/FindMain.h"
15 #include "compiler/translator/tree_util/IntermNode_util.h"
16 #include "compiler/translator/tree_util/IntermTraverse.h"
17 #include "compiler/translator/util.h"
18
19 namespace sh
20 {
21
22 namespace
23 {
24
25 void AddArrayZeroInitSequence(const TIntermTyped *initializedNode,
26 bool canUseLoopsToInitialize,
27 bool highPrecisionSupported,
28 TIntermSequence *initSequenceOut,
29 TSymbolTable *symbolTable);
30
31 void AddStructZeroInitSequence(const TIntermTyped *initializedNode,
32 bool canUseLoopsToInitialize,
33 bool highPrecisionSupported,
34 TIntermSequence *initSequenceOut,
35 TSymbolTable *symbolTable);
36
CreateZeroInitAssignment(const TIntermTyped * initializedNode)37 TIntermBinary *CreateZeroInitAssignment(const TIntermTyped *initializedNode)
38 {
39 TIntermTyped *zero = CreateZeroNode(initializedNode->getType());
40 return new TIntermBinary(EOpAssign, initializedNode->deepCopy(), zero);
41 }
42
AddZeroInitSequence(const TIntermTyped * initializedNode,bool canUseLoopsToInitialize,bool highPrecisionSupported,TIntermSequence * initSequenceOut,TSymbolTable * symbolTable)43 void AddZeroInitSequence(const TIntermTyped *initializedNode,
44 bool canUseLoopsToInitialize,
45 bool highPrecisionSupported,
46 TIntermSequence *initSequenceOut,
47 TSymbolTable *symbolTable)
48 {
49 if (initializedNode->isArray())
50 {
51 AddArrayZeroInitSequence(initializedNode, canUseLoopsToInitialize, highPrecisionSupported,
52 initSequenceOut, symbolTable);
53 }
54 else if (initializedNode->getType().isStructureContainingArrays() ||
55 initializedNode->getType().isNamelessStruct())
56 {
57 AddStructZeroInitSequence(initializedNode, canUseLoopsToInitialize, highPrecisionSupported,
58 initSequenceOut, symbolTable);
59 }
60 else
61 {
62 initSequenceOut->push_back(CreateZeroInitAssignment(initializedNode));
63 }
64 }
65
AddStructZeroInitSequence(const TIntermTyped * initializedNode,bool canUseLoopsToInitialize,bool highPrecisionSupported,TIntermSequence * initSequenceOut,TSymbolTable * symbolTable)66 void AddStructZeroInitSequence(const TIntermTyped *initializedNode,
67 bool canUseLoopsToInitialize,
68 bool highPrecisionSupported,
69 TIntermSequence *initSequenceOut,
70 TSymbolTable *symbolTable)
71 {
72 ASSERT(initializedNode->getBasicType() == EbtStruct);
73 const TStructure *structType = initializedNode->getType().getStruct();
74 for (int i = 0; i < static_cast<int>(structType->fields().size()); ++i)
75 {
76 TIntermBinary *element = new TIntermBinary(EOpIndexDirectStruct,
77 initializedNode->deepCopy(), CreateIndexNode(i));
78 // Structs can't be defined inside structs, so the type of a struct field can't be a
79 // nameless struct.
80 ASSERT(!element->getType().isNamelessStruct());
81 AddZeroInitSequence(element, canUseLoopsToInitialize, highPrecisionSupported,
82 initSequenceOut, symbolTable);
83 }
84 }
85
AddArrayZeroInitStatementList(const TIntermTyped * initializedNode,bool canUseLoopsToInitialize,bool highPrecisionSupported,TIntermSequence * initSequenceOut,TSymbolTable * symbolTable)86 void AddArrayZeroInitStatementList(const TIntermTyped *initializedNode,
87 bool canUseLoopsToInitialize,
88 bool highPrecisionSupported,
89 TIntermSequence *initSequenceOut,
90 TSymbolTable *symbolTable)
91 {
92 for (unsigned int i = 0; i < initializedNode->getOutermostArraySize(); ++i)
93 {
94 TIntermBinary *element =
95 new TIntermBinary(EOpIndexDirect, initializedNode->deepCopy(), CreateIndexNode(i));
96 AddZeroInitSequence(element, canUseLoopsToInitialize, highPrecisionSupported,
97 initSequenceOut, symbolTable);
98 }
99 }
100
AddArrayZeroInitForLoop(const TIntermTyped * initializedNode,bool highPrecisionSupported,TIntermSequence * initSequenceOut,TSymbolTable * symbolTable)101 void AddArrayZeroInitForLoop(const TIntermTyped *initializedNode,
102 bool highPrecisionSupported,
103 TIntermSequence *initSequenceOut,
104 TSymbolTable *symbolTable)
105 {
106 ASSERT(initializedNode->isArray());
107 const TType *mediumpIndexType = StaticType::Get<EbtInt, EbpMedium, EvqTemporary, 1, 1>();
108 const TType *highpIndexType = StaticType::Get<EbtInt, EbpHigh, EvqTemporary, 1, 1>();
109 TVariable *indexVariable =
110 CreateTempVariable(symbolTable, highPrecisionSupported ? highpIndexType : mediumpIndexType);
111
112 TIntermSymbol *indexSymbolNode = CreateTempSymbolNode(indexVariable);
113 TIntermDeclaration *indexInit =
114 CreateTempInitDeclarationNode(indexVariable, CreateZeroNode(indexVariable->getType()));
115 TIntermConstantUnion *arraySizeNode = CreateIndexNode(initializedNode->getOutermostArraySize());
116 TIntermBinary *indexSmallerThanSize =
117 new TIntermBinary(EOpLessThan, indexSymbolNode->deepCopy(), arraySizeNode);
118 TIntermUnary *indexIncrement =
119 new TIntermUnary(EOpPreIncrement, indexSymbolNode->deepCopy(), nullptr);
120
121 TIntermBlock *forLoopBody = new TIntermBlock();
122 TIntermSequence *forLoopBodySeq = forLoopBody->getSequence();
123
124 TIntermBinary *element = new TIntermBinary(EOpIndexIndirect, initializedNode->deepCopy(),
125 indexSymbolNode->deepCopy());
126 AddZeroInitSequence(element, true, highPrecisionSupported, forLoopBodySeq, symbolTable);
127
128 TIntermLoop *forLoop =
129 new TIntermLoop(ELoopFor, indexInit, indexSmallerThanSize, indexIncrement, forLoopBody);
130 initSequenceOut->push_back(forLoop);
131 }
132
AddArrayZeroInitSequence(const TIntermTyped * initializedNode,bool canUseLoopsToInitialize,bool highPrecisionSupported,TIntermSequence * initSequenceOut,TSymbolTable * symbolTable)133 void AddArrayZeroInitSequence(const TIntermTyped *initializedNode,
134 bool canUseLoopsToInitialize,
135 bool highPrecisionSupported,
136 TIntermSequence *initSequenceOut,
137 TSymbolTable *symbolTable)
138 {
139 // The array elements are assigned one by one to keep the AST compatible with ESSL 1.00 which
140 // doesn't have array assignment. We'll do this either with a for loop or just a list of
141 // statements assigning to each array index. Note that it is important to have the array init in
142 // the right order to workaround http://crbug.com/709317
143 bool isSmallArray = initializedNode->getOutermostArraySize() <= 1u ||
144 (initializedNode->getBasicType() != EbtStruct &&
145 !initializedNode->getType().isArrayOfArrays() &&
146 initializedNode->getOutermostArraySize() <= 3u);
147 if (initializedNode->getQualifier() == EvqFragData ||
148 initializedNode->getQualifier() == EvqFragmentOut || isSmallArray ||
149 !canUseLoopsToInitialize)
150 {
151 // Fragment outputs should not be indexed by non-constant indices.
152 // Also it doesn't make sense to use loops to initialize very small arrays.
153 AddArrayZeroInitStatementList(initializedNode, canUseLoopsToInitialize,
154 highPrecisionSupported, initSequenceOut, symbolTable);
155 }
156 else
157 {
158 AddArrayZeroInitForLoop(initializedNode, highPrecisionSupported, initSequenceOut,
159 symbolTable);
160 }
161 }
162
InsertInitCode(TCompiler * compiler,TIntermSequence * mainBody,const InitVariableList & variables,TSymbolTable * symbolTable,int shaderVersion,const TExtensionBehavior & extensionBehavior,bool canUseLoopsToInitialize,bool highPrecisionSupported)163 void InsertInitCode(TCompiler *compiler,
164 TIntermSequence *mainBody,
165 const InitVariableList &variables,
166 TSymbolTable *symbolTable,
167 int shaderVersion,
168 const TExtensionBehavior &extensionBehavior,
169 bool canUseLoopsToInitialize,
170 bool highPrecisionSupported)
171 {
172 for (const auto &var : variables)
173 {
174 // Note that tempVariableName will reference a short-lived char array here - that's fine
175 // since we're only using it to find symbols.
176 ImmutableString tempVariableName(var.name.c_str(), var.name.length());
177
178 TIntermTyped *initializedSymbol = nullptr;
179 if (var.isBuiltIn())
180 {
181 initializedSymbol =
182 ReferenceBuiltInVariable(tempVariableName, *symbolTable, shaderVersion);
183 if (initializedSymbol->getQualifier() == EvqFragData &&
184 !IsExtensionEnabled(extensionBehavior, TExtension::EXT_draw_buffers))
185 {
186 // If GL_EXT_draw_buffers is disabled, only the 0th index of gl_FragData can be
187 // written to.
188 // TODO(oetuaho): This is a bit hacky and would be better to remove, if we came up
189 // with a good way to do it. Right now "gl_FragData" in symbol table is initialized
190 // to have the array size of MaxDrawBuffers, and the initialization happens before
191 // the shader sets the extensions it is using.
192 initializedSymbol =
193 new TIntermBinary(EOpIndexDirect, initializedSymbol, CreateIndexNode(0));
194 }
195 }
196 else
197 {
198 initializedSymbol = ReferenceGlobalVariable(tempVariableName, *symbolTable);
199 }
200 ASSERT(initializedSymbol != nullptr);
201
202 TIntermSequence *initCode = CreateInitCode(initializedSymbol, canUseLoopsToInitialize,
203 highPrecisionSupported, symbolTable);
204 mainBody->insert(mainBody->begin(), initCode->begin(), initCode->end());
205 }
206 }
207
208 class InitializeLocalsTraverser : public TIntermTraverser
209 {
210 public:
InitializeLocalsTraverser(int shaderVersion,TSymbolTable * symbolTable,bool canUseLoopsToInitialize,bool highPrecisionSupported)211 InitializeLocalsTraverser(int shaderVersion,
212 TSymbolTable *symbolTable,
213 bool canUseLoopsToInitialize,
214 bool highPrecisionSupported)
215 : TIntermTraverser(true, false, false, symbolTable),
216 mShaderVersion(shaderVersion),
217 mCanUseLoopsToInitialize(canUseLoopsToInitialize),
218 mHighPrecisionSupported(highPrecisionSupported)
219 {}
220
221 protected:
visitDeclaration(Visit visit,TIntermDeclaration * node)222 bool visitDeclaration(Visit visit, TIntermDeclaration *node) override
223 {
224 for (TIntermNode *declarator : *node->getSequence())
225 {
226 if (!mInGlobalScope && !declarator->getAsBinaryNode())
227 {
228 TIntermSymbol *symbol = declarator->getAsSymbolNode();
229 ASSERT(symbol);
230 if (symbol->variable().symbolType() == SymbolType::Empty)
231 {
232 continue;
233 }
234
235 // Arrays may need to be initialized one element at a time, since ESSL 1.00 does not
236 // support array constructors or assigning arrays.
237 bool arrayConstructorUnavailable =
238 (symbol->isArray() || symbol->getType().isStructureContainingArrays()) &&
239 mShaderVersion == 100;
240 // Nameless struct constructors can't be referred to, so they also need to be
241 // initialized one element at a time.
242 // TODO(oetuaho): Check if it makes sense to initialize using a loop, even if we
243 // could use an initializer. It could at least reduce code size for very large
244 // arrays, but could hurt runtime performance.
245 if (arrayConstructorUnavailable || symbol->getType().isNamelessStruct())
246 {
247 // SimplifyLoopConditions should have been run so the parent node of this node
248 // should not be a loop.
249 ASSERT(getParentNode()->getAsLoopNode() == nullptr);
250 // SeparateDeclarations should have already been run, so we don't need to worry
251 // about further declarators in this declaration depending on the effects of
252 // this declarator.
253 ASSERT(node->getSequence()->size() == 1);
254 insertStatementsInParentBlock(
255 TIntermSequence(), *CreateInitCode(symbol, mCanUseLoopsToInitialize,
256 mHighPrecisionSupported, mSymbolTable));
257 }
258 else
259 {
260 TIntermBinary *init =
261 new TIntermBinary(EOpInitialize, symbol, CreateZeroNode(symbol->getType()));
262 queueReplacementWithParent(node, symbol, init, OriginalNode::BECOMES_CHILD);
263 }
264 }
265 }
266 return false;
267 }
268
269 private:
270 int mShaderVersion;
271 bool mCanUseLoopsToInitialize;
272 bool mHighPrecisionSupported;
273 };
274
275 } // namespace
276
CreateInitCode(const TIntermTyped * initializedSymbol,bool canUseLoopsToInitialize,bool highPrecisionSupported,TSymbolTable * symbolTable)277 TIntermSequence *CreateInitCode(const TIntermTyped *initializedSymbol,
278 bool canUseLoopsToInitialize,
279 bool highPrecisionSupported,
280 TSymbolTable *symbolTable)
281 {
282 TIntermSequence *initCode = new TIntermSequence();
283 AddZeroInitSequence(initializedSymbol, canUseLoopsToInitialize, highPrecisionSupported,
284 initCode, symbolTable);
285 return initCode;
286 }
287
InitializeUninitializedLocals(TCompiler * compiler,TIntermBlock * root,int shaderVersion,bool canUseLoopsToInitialize,bool highPrecisionSupported,TSymbolTable * symbolTable)288 bool InitializeUninitializedLocals(TCompiler *compiler,
289 TIntermBlock *root,
290 int shaderVersion,
291 bool canUseLoopsToInitialize,
292 bool highPrecisionSupported,
293 TSymbolTable *symbolTable)
294 {
295 InitializeLocalsTraverser traverser(shaderVersion, symbolTable, canUseLoopsToInitialize,
296 highPrecisionSupported);
297 root->traverse(&traverser);
298 return traverser.updateTree(compiler, root);
299 }
300
InitializeVariables(TCompiler * compiler,TIntermBlock * root,const InitVariableList & vars,TSymbolTable * symbolTable,int shaderVersion,const TExtensionBehavior & extensionBehavior,bool canUseLoopsToInitialize,bool highPrecisionSupported)301 bool InitializeVariables(TCompiler *compiler,
302 TIntermBlock *root,
303 const InitVariableList &vars,
304 TSymbolTable *symbolTable,
305 int shaderVersion,
306 const TExtensionBehavior &extensionBehavior,
307 bool canUseLoopsToInitialize,
308 bool highPrecisionSupported)
309 {
310 TIntermBlock *body = FindMainBody(root);
311 InsertInitCode(compiler, body->getSequence(), vars, symbolTable, shaderVersion,
312 extensionBehavior, canUseLoopsToInitialize, highPrecisionSupported);
313
314 return compiler->validateAST(root);
315 }
316
317 } // namespace sh
318