• 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 = 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 &paramType         = 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