• 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/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