• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 &paramType         = 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