1 //
2 // Copyright (c) 2002-2013 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/InitializeVariables.h"
8 
9 #include "angle_gl.h"
10 #include "common/debug.h"
11 #include "compiler/translator/FindMain.h"
12 #include "compiler/translator/IntermNode_util.h"
13 #include "compiler/translator/IntermTraverse.h"
14 #include "compiler/translator/SymbolTable.h"
15 #include "compiler/translator/util.h"
16 
17 namespace sh
18 {
19 
20 namespace
21 {
22 
23 void AddArrayZeroInitSequence(const TIntermTyped *initializedNode,
24                               bool canUseLoopsToInitialize,
25                               TIntermSequence *initSequenceOut,
26                               TSymbolTable *symbolTable);
27 
28 void AddStructZeroInitSequence(const TIntermTyped *initializedNode,
29                                bool canUseLoopsToInitialize,
30                                TIntermSequence *initSequenceOut,
31                                TSymbolTable *symbolTable);
32 
CreateZeroInitAssignment(const TIntermTyped * initializedNode)33 TIntermBinary *CreateZeroInitAssignment(const TIntermTyped *initializedNode)
34 {
35     TIntermTyped *zero = CreateZeroNode(initializedNode->getType());
36     return new TIntermBinary(EOpAssign, initializedNode->deepCopy(), zero);
37 }
38 
AddZeroInitSequence(const TIntermTyped * initializedNode,bool canUseLoopsToInitialize,TIntermSequence * initSequenceOut,TSymbolTable * symbolTable)39 void AddZeroInitSequence(const TIntermTyped *initializedNode,
40                          bool canUseLoopsToInitialize,
41                          TIntermSequence *initSequenceOut,
42                          TSymbolTable *symbolTable)
43 {
44     if (initializedNode->isArray())
45     {
46         AddArrayZeroInitSequence(initializedNode, canUseLoopsToInitialize, initSequenceOut,
47                                  symbolTable);
48     }
49     else if (initializedNode->getType().isStructureContainingArrays() ||
50              initializedNode->getType().isNamelessStruct())
51     {
52         AddStructZeroInitSequence(initializedNode, canUseLoopsToInitialize, initSequenceOut,
53                                   symbolTable);
54     }
55     else
56     {
57         initSequenceOut->push_back(CreateZeroInitAssignment(initializedNode));
58     }
59 }
60 
AddStructZeroInitSequence(const TIntermTyped * initializedNode,bool canUseLoopsToInitialize,TIntermSequence * initSequenceOut,TSymbolTable * symbolTable)61 void AddStructZeroInitSequence(const TIntermTyped *initializedNode,
62                                bool canUseLoopsToInitialize,
63                                TIntermSequence *initSequenceOut,
64                                TSymbolTable *symbolTable)
65 {
66     ASSERT(initializedNode->getBasicType() == EbtStruct);
67     const TStructure *structType = initializedNode->getType().getStruct();
68     for (int i = 0; i < static_cast<int>(structType->fields().size()); ++i)
69     {
70         TIntermBinary *element = new TIntermBinary(EOpIndexDirectStruct,
71                                                    initializedNode->deepCopy(), CreateIndexNode(i));
72         // Structs can't be defined inside structs, so the type of a struct field can't be a
73         // nameless struct.
74         ASSERT(!element->getType().isNamelessStruct());
75         AddZeroInitSequence(element, canUseLoopsToInitialize, initSequenceOut, symbolTable);
76     }
77 }
78 
AddArrayZeroInitStatementList(const TIntermTyped * initializedNode,bool canUseLoopsToInitialize,TIntermSequence * initSequenceOut,TSymbolTable * symbolTable)79 void AddArrayZeroInitStatementList(const TIntermTyped *initializedNode,
80                                    bool canUseLoopsToInitialize,
81                                    TIntermSequence *initSequenceOut,
82                                    TSymbolTable *symbolTable)
83 {
84     for (unsigned int i = 0; i < initializedNode->getOutermostArraySize(); ++i)
85     {
86         TIntermBinary *element =
87             new TIntermBinary(EOpIndexDirect, initializedNode->deepCopy(), CreateIndexNode(i));
88         AddZeroInitSequence(element, canUseLoopsToInitialize, initSequenceOut, symbolTable);
89     }
90 }
91 
AddArrayZeroInitForLoop(const TIntermTyped * initializedNode,TIntermSequence * initSequenceOut,TSymbolTable * symbolTable)92 void AddArrayZeroInitForLoop(const TIntermTyped *initializedNode,
93                              TIntermSequence *initSequenceOut,
94                              TSymbolTable *symbolTable)
95 {
96     ASSERT(initializedNode->isArray());
97     TSymbolUniqueId indexSymbol(symbolTable);
98 
99     TIntermSymbol *indexSymbolNode = CreateTempSymbolNode(indexSymbol, TType(EbtInt), EvqTemporary);
100     TIntermDeclaration *indexInit =
101         CreateTempInitDeclarationNode(indexSymbol, CreateZeroNode(TType(EbtInt)), EvqTemporary);
102     TIntermConstantUnion *arraySizeNode = CreateIndexNode(initializedNode->getOutermostArraySize());
103     TIntermBinary *indexSmallerThanSize =
104         new TIntermBinary(EOpLessThan, indexSymbolNode->deepCopy(), arraySizeNode);
105     TIntermUnary *indexIncrement = new TIntermUnary(EOpPreIncrement, indexSymbolNode->deepCopy());
106 
107     TIntermBlock *forLoopBody       = new TIntermBlock();
108     TIntermSequence *forLoopBodySeq = forLoopBody->getSequence();
109 
110     TIntermBinary *element = new TIntermBinary(EOpIndexIndirect, initializedNode->deepCopy(),
111                                                indexSymbolNode->deepCopy());
112     AddZeroInitSequence(element, true, forLoopBodySeq, symbolTable);
113 
114     TIntermLoop *forLoop =
115         new TIntermLoop(ELoopFor, indexInit, indexSmallerThanSize, indexIncrement, forLoopBody);
116     initSequenceOut->push_back(forLoop);
117 }
118 
AddArrayZeroInitSequence(const TIntermTyped * initializedNode,bool canUseLoopsToInitialize,TIntermSequence * initSequenceOut,TSymbolTable * symbolTable)119 void AddArrayZeroInitSequence(const TIntermTyped *initializedNode,
120                               bool canUseLoopsToInitialize,
121                               TIntermSequence *initSequenceOut,
122                               TSymbolTable *symbolTable)
123 {
124     // The array elements are assigned one by one to keep the AST compatible with ESSL 1.00 which
125     // doesn't have array assignment. We'll do this either with a for loop or just a list of
126     // statements assigning to each array index. Note that it is important to have the array init in
127     // the right order to workaround http://crbug.com/709317
128     bool isSmallArray = initializedNode->getOutermostArraySize() <= 1u ||
129                         (initializedNode->getBasicType() != EbtStruct &&
130                          !initializedNode->getType().isArrayOfArrays() &&
131                          initializedNode->getOutermostArraySize() <= 3u);
132     if (initializedNode->getQualifier() == EvqFragData ||
133         initializedNode->getQualifier() == EvqFragmentOut || isSmallArray ||
134         !canUseLoopsToInitialize)
135     {
136         // Fragment outputs should not be indexed by non-constant indices.
137         // Also it doesn't make sense to use loops to initialize very small arrays.
138         AddArrayZeroInitStatementList(initializedNode, canUseLoopsToInitialize, initSequenceOut,
139                                       symbolTable);
140     }
141     else
142     {
143         AddArrayZeroInitForLoop(initializedNode, initSequenceOut, symbolTable);
144     }
145 }
146 
InsertInitCode(TIntermSequence * mainBody,const InitVariableList & variables,TSymbolTable * symbolTable,int shaderVersion,const TExtensionBehavior & extensionBehavior,bool canUseLoopsToInitialize)147 void InsertInitCode(TIntermSequence *mainBody,
148                     const InitVariableList &variables,
149                     TSymbolTable *symbolTable,
150                     int shaderVersion,
151                     const TExtensionBehavior &extensionBehavior,
152                     bool canUseLoopsToInitialize)
153 {
154     for (const auto &var : variables)
155     {
156         TString name = TString(var.name.c_str());
157         size_t pos   = name.find_last_of('[');
158         if (pos != TString::npos)
159         {
160             name = name.substr(0, pos);
161         }
162 
163         TIntermTyped *initializedSymbol = nullptr;
164         if (var.isBuiltIn())
165         {
166             initializedSymbol = ReferenceBuiltInVariable(name, *symbolTable, shaderVersion);
167             if (initializedSymbol->getQualifier() == EvqFragData &&
168                 !IsExtensionEnabled(extensionBehavior, TExtension::EXT_draw_buffers))
169             {
170                 // If GL_EXT_draw_buffers is disabled, only the 0th index of gl_FragData can be
171                 // written to.
172                 // TODO(oetuaho): This is a bit hacky and would be better to remove, if we came up
173                 // with a good way to do it. Right now "gl_FragData" in symbol table is initialized
174                 // to have the array size of MaxDrawBuffers, and the initialization happens before
175                 // the shader sets the extensions it is using.
176                 initializedSymbol =
177                     new TIntermBinary(EOpIndexDirect, initializedSymbol, CreateIndexNode(0));
178             }
179         }
180         else
181         {
182             initializedSymbol = ReferenceGlobalVariable(name, *symbolTable);
183         }
184         ASSERT(initializedSymbol != nullptr);
185 
186         TIntermSequence *initCode =
187             CreateInitCode(initializedSymbol, canUseLoopsToInitialize, symbolTable);
188         mainBody->insert(mainBody->begin(), initCode->begin(), initCode->end());
189     }
190 }
191 
192 class InitializeLocalsTraverser : public TIntermTraverser
193 {
194   public:
InitializeLocalsTraverser(int shaderVersion,TSymbolTable * symbolTable,bool canUseLoopsToInitialize)195     InitializeLocalsTraverser(int shaderVersion,
196                               TSymbolTable *symbolTable,
197                               bool canUseLoopsToInitialize)
198         : TIntermTraverser(true, false, false, symbolTable),
199           mShaderVersion(shaderVersion),
200           mCanUseLoopsToInitialize(canUseLoopsToInitialize)
201     {
202     }
203 
204   protected:
visitDeclaration(Visit visit,TIntermDeclaration * node)205     bool visitDeclaration(Visit visit, TIntermDeclaration *node) override
206     {
207         for (TIntermNode *declarator : *node->getSequence())
208         {
209             if (!mInGlobalScope && !declarator->getAsBinaryNode())
210             {
211                 TIntermSymbol *symbol = declarator->getAsSymbolNode();
212                 ASSERT(symbol);
213                 if (symbol->getSymbol() == "")
214                 {
215                     continue;
216                 }
217 
218                 // Arrays may need to be initialized one element at a time, since ESSL 1.00 does not
219                 // support array constructors or assigning arrays.
220                 bool arrayConstructorUnavailable =
221                     (symbol->isArray() || symbol->getType().isStructureContainingArrays()) &&
222                     mShaderVersion == 100;
223                 // Nameless struct constructors can't be referred to, so they also need to be
224                 // initialized one element at a time.
225                 // TODO(oetuaho): Check if it makes sense to initialize using a loop, even if we
226                 // could use an initializer. It could at least reduce code size for very large
227                 // arrays, but could hurt runtime performance.
228                 if (arrayConstructorUnavailable || symbol->getType().isNamelessStruct())
229                 {
230                     // SimplifyLoopConditions should have been run so the parent node of this node
231                     // should not be a loop.
232                     ASSERT(getParentNode()->getAsLoopNode() == nullptr);
233                     // SeparateDeclarations should have already been run, so we don't need to worry
234                     // about further declarators in this declaration depending on the effects of
235                     // this declarator.
236                     ASSERT(node->getSequence()->size() == 1);
237                     insertStatementsInParentBlock(
238                         TIntermSequence(),
239                         *CreateInitCode(symbol, mCanUseLoopsToInitialize, mSymbolTable));
240                 }
241                 else
242                 {
243                     TIntermBinary *init =
244                         new TIntermBinary(EOpInitialize, symbol, CreateZeroNode(symbol->getType()));
245                     queueReplacementWithParent(node, symbol, init, OriginalNode::BECOMES_CHILD);
246                 }
247             }
248         }
249         return false;
250     }
251 
252   private:
253     int mShaderVersion;
254     bool mCanUseLoopsToInitialize;
255 };
256 
257 }  // namespace anonymous
258 
CreateInitCode(const TIntermTyped * initializedSymbol,bool canUseLoopsToInitialize,TSymbolTable * symbolTable)259 TIntermSequence *CreateInitCode(const TIntermTyped *initializedSymbol,
260                                 bool canUseLoopsToInitialize,
261                                 TSymbolTable *symbolTable)
262 {
263     TIntermSequence *initCode = new TIntermSequence();
264     AddZeroInitSequence(initializedSymbol, canUseLoopsToInitialize, initCode, symbolTable);
265     return initCode;
266 }
267 
InitializeUninitializedLocals(TIntermBlock * root,int shaderVersion,bool canUseLoopsToInitialize,TSymbolTable * symbolTable)268 void InitializeUninitializedLocals(TIntermBlock *root,
269                                    int shaderVersion,
270                                    bool canUseLoopsToInitialize,
271                                    TSymbolTable *symbolTable)
272 {
273     InitializeLocalsTraverser traverser(shaderVersion, symbolTable, canUseLoopsToInitialize);
274     root->traverse(&traverser);
275     traverser.updateTree();
276 }
277 
InitializeVariables(TIntermBlock * root,const InitVariableList & vars,TSymbolTable * symbolTable,int shaderVersion,const TExtensionBehavior & extensionBehavior,bool canUseLoopsToInitialize)278 void InitializeVariables(TIntermBlock *root,
279                          const InitVariableList &vars,
280                          TSymbolTable *symbolTable,
281                          int shaderVersion,
282                          const TExtensionBehavior &extensionBehavior,
283                          bool canUseLoopsToInitialize)
284 {
285     TIntermBlock *body = FindMainBody(root);
286     InsertInitCode(body->getSequence(), vars, symbolTable, shaderVersion, extensionBehavior,
287                    canUseLoopsToInitialize);
288 }
289 
290 }  // namespace sh
291