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