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