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