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