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 = ReferenceGlobalVariable(tempVariableName, *symbolTable);
228 }
229 else
230 {
231 // Must be a nameless interface block.
232 ASSERT(var.structOrBlockName != "");
233 const TSymbol *symbol = symbolTable->findGlobal(var.structOrBlockName);
234 ASSERT(symbol && symbol->isInterfaceBlock());
235 const TInterfaceBlock *block = static_cast<const TInterfaceBlock *>(symbol);
236
237 for (const TField *field : block->fields())
238 {
239 initializedSymbol = ReferenceGlobalVariable(field->name(), *symbolTable);
240
241 TIntermSequence initCode;
242 CreateInitCode(initializedSymbol, canUseLoopsToInitialize,
243 highPrecisionSupported, &initCode, symbolTable);
244 mainBody->insert(mainBody->begin(), initCode.begin(), initCode.end());
245 }
246 // Already inserted init code in this case
247 continue;
248 }
249 }
250 ASSERT(initializedSymbol != nullptr);
251
252 TIntermSequence initCode;
253 CreateInitCode(initializedSymbol, canUseLoopsToInitialize, highPrecisionSupported,
254 &initCode, symbolTable);
255 mainBody->insert(mainBody->begin(), initCode.begin(), initCode.end());
256 }
257 }
258
259 class InitializeLocalsTraverser : public TIntermTraverser
260 {
261 public:
InitializeLocalsTraverser(int shaderVersion,TSymbolTable * symbolTable,bool canUseLoopsToInitialize,bool highPrecisionSupported)262 InitializeLocalsTraverser(int shaderVersion,
263 TSymbolTable *symbolTable,
264 bool canUseLoopsToInitialize,
265 bool highPrecisionSupported)
266 : TIntermTraverser(true, false, false, symbolTable),
267 mShaderVersion(shaderVersion),
268 mCanUseLoopsToInitialize(canUseLoopsToInitialize),
269 mHighPrecisionSupported(highPrecisionSupported)
270 {}
271
272 protected:
visitDeclaration(Visit visit,TIntermDeclaration * node)273 bool visitDeclaration(Visit visit, TIntermDeclaration *node) override
274 {
275 for (TIntermNode *declarator : *node->getSequence())
276 {
277 if (!mInGlobalScope && !declarator->getAsBinaryNode())
278 {
279 TIntermSymbol *symbol = declarator->getAsSymbolNode();
280 ASSERT(symbol);
281 if (symbol->variable().symbolType() == SymbolType::Empty)
282 {
283 continue;
284 }
285
286 // Arrays may need to be initialized one element at a time, since ESSL 1.00 does not
287 // support array constructors or assigning arrays.
288 bool arrayConstructorUnavailable =
289 (symbol->isArray() || symbol->getType().isStructureContainingArrays()) &&
290 mShaderVersion == 100;
291 // Nameless struct constructors can't be referred to, so they also need to be
292 // initialized one element at a time.
293 // TODO(oetuaho): Check if it makes sense to initialize using a loop, even if we
294 // could use an initializer. It could at least reduce code size for very large
295 // arrays, but could hurt runtime performance.
296 if (arrayConstructorUnavailable || symbol->getType().isNamelessStruct())
297 {
298 // SimplifyLoopConditions should have been run so the parent node of this node
299 // should not be a loop.
300 ASSERT(getParentNode()->getAsLoopNode() == nullptr);
301 // SeparateDeclarations should have already been run, so we don't need to worry
302 // about further declarators in this declaration depending on the effects of
303 // this declarator.
304 ASSERT(node->getSequence()->size() == 1);
305 TIntermSequence initCode;
306 CreateInitCode(symbol, mCanUseLoopsToInitialize, mHighPrecisionSupported,
307 &initCode, mSymbolTable);
308 insertStatementsInParentBlock(TIntermSequence(), initCode);
309 }
310 else
311 {
312 TIntermBinary *init =
313 new TIntermBinary(EOpInitialize, symbol, CreateZeroNode(symbol->getType()));
314 queueReplacementWithParent(node, symbol, init, OriginalNode::BECOMES_CHILD);
315 }
316 }
317 }
318 return false;
319 }
320
visitFunctionDefinition(Visit visit,TIntermFunctionDefinition * node)321 bool visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) override
322 {
323 // Initialize output function arguments as well, the parameter passed in at call time may be
324 // clobbered if the function doesn't fully write to the argument.
325
326 TIntermSequence initCode;
327
328 const TFunction *function = node->getFunction();
329 for (size_t paramIndex = 0; paramIndex < function->getParamCount(); ++paramIndex)
330 {
331 const TVariable *paramVariable = function->getParam(paramIndex);
332 const TType ¶mType = paramVariable->getType();
333
334 if (paramType.getQualifier() != EvqParamOut)
335 {
336 continue;
337 }
338
339 CreateInitCode(new TIntermSymbol(paramVariable), mCanUseLoopsToInitialize,
340 mHighPrecisionSupported, &initCode, mSymbolTable);
341 }
342
343 if (!initCode.empty())
344 {
345 TIntermSequence *body = node->getBody()->getSequence();
346 body->insert(body->begin(), initCode.begin(), initCode.end());
347 }
348
349 return true;
350 }
351
352 private:
353 int mShaderVersion;
354 bool mCanUseLoopsToInitialize;
355 bool mHighPrecisionSupported;
356 };
357
358 } // namespace
359
CreateInitCode(const TIntermTyped * initializedSymbol,bool canUseLoopsToInitialize,bool highPrecisionSupported,TIntermSequence * initCode,TSymbolTable * symbolTable)360 void CreateInitCode(const TIntermTyped *initializedSymbol,
361 bool canUseLoopsToInitialize,
362 bool highPrecisionSupported,
363 TIntermSequence *initCode,
364 TSymbolTable *symbolTable)
365 {
366 AddZeroInitSequence(initializedSymbol, canUseLoopsToInitialize, highPrecisionSupported,
367 initCode, symbolTable);
368 }
369
InitializeUninitializedLocals(TCompiler * compiler,TIntermBlock * root,int shaderVersion,bool canUseLoopsToInitialize,bool highPrecisionSupported,TSymbolTable * symbolTable)370 bool InitializeUninitializedLocals(TCompiler *compiler,
371 TIntermBlock *root,
372 int shaderVersion,
373 bool canUseLoopsToInitialize,
374 bool highPrecisionSupported,
375 TSymbolTable *symbolTable)
376 {
377 InitializeLocalsTraverser traverser(shaderVersion, symbolTable, canUseLoopsToInitialize,
378 highPrecisionSupported);
379 root->traverse(&traverser);
380 return traverser.updateTree(compiler, root);
381 }
382
InitializeVariables(TCompiler * compiler,TIntermBlock * root,const InitVariableList & vars,TSymbolTable * symbolTable,int shaderVersion,const TExtensionBehavior & extensionBehavior,bool canUseLoopsToInitialize,bool highPrecisionSupported)383 bool InitializeVariables(TCompiler *compiler,
384 TIntermBlock *root,
385 const InitVariableList &vars,
386 TSymbolTable *symbolTable,
387 int shaderVersion,
388 const TExtensionBehavior &extensionBehavior,
389 bool canUseLoopsToInitialize,
390 bool highPrecisionSupported)
391 {
392 InsertInitCode(compiler, root, vars, symbolTable, shaderVersion, extensionBehavior,
393 canUseLoopsToInitialize, highPrecisionSupported);
394
395 return compiler->validateAST(root);
396 }
397
398 } // namespace sh
399