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;
203 CreateInitCode(initializedSymbol, canUseLoopsToInitialize, highPrecisionSupported,
204 &initCode, symbolTable);
205 mainBody->insert(mainBody->begin(), initCode.begin(), initCode.end());
206 }
207 }
208
209 class InitializeLocalsTraverser : public TIntermTraverser
210 {
211 public:
InitializeLocalsTraverser(int shaderVersion,TSymbolTable * symbolTable,bool canUseLoopsToInitialize,bool highPrecisionSupported)212 InitializeLocalsTraverser(int shaderVersion,
213 TSymbolTable *symbolTable,
214 bool canUseLoopsToInitialize,
215 bool highPrecisionSupported)
216 : TIntermTraverser(true, false, false, symbolTable),
217 mShaderVersion(shaderVersion),
218 mCanUseLoopsToInitialize(canUseLoopsToInitialize),
219 mHighPrecisionSupported(highPrecisionSupported)
220 {}
221
222 protected:
visitDeclaration(Visit visit,TIntermDeclaration * node)223 bool visitDeclaration(Visit visit, TIntermDeclaration *node) override
224 {
225 for (TIntermNode *declarator : *node->getSequence())
226 {
227 if (!mInGlobalScope && !declarator->getAsBinaryNode())
228 {
229 TIntermSymbol *symbol = declarator->getAsSymbolNode();
230 ASSERT(symbol);
231 if (symbol->variable().symbolType() == SymbolType::Empty)
232 {
233 continue;
234 }
235
236 // Arrays may need to be initialized one element at a time, since ESSL 1.00 does not
237 // support array constructors or assigning arrays.
238 bool arrayConstructorUnavailable =
239 (symbol->isArray() || symbol->getType().isStructureContainingArrays()) &&
240 mShaderVersion == 100;
241 // Nameless struct constructors can't be referred to, so they also need to be
242 // initialized one element at a time.
243 // TODO(oetuaho): Check if it makes sense to initialize using a loop, even if we
244 // could use an initializer. It could at least reduce code size for very large
245 // arrays, but could hurt runtime performance.
246 if (arrayConstructorUnavailable || symbol->getType().isNamelessStruct())
247 {
248 // SimplifyLoopConditions should have been run so the parent node of this node
249 // should not be a loop.
250 ASSERT(getParentNode()->getAsLoopNode() == nullptr);
251 // SeparateDeclarations should have already been run, so we don't need to worry
252 // about further declarators in this declaration depending on the effects of
253 // this declarator.
254 ASSERT(node->getSequence()->size() == 1);
255 TIntermSequence initCode;
256 CreateInitCode(symbol, mCanUseLoopsToInitialize, mHighPrecisionSupported,
257 &initCode, mSymbolTable);
258 insertStatementsInParentBlock(TIntermSequence(), initCode);
259 }
260 else
261 {
262 TIntermBinary *init =
263 new TIntermBinary(EOpInitialize, symbol, CreateZeroNode(symbol->getType()));
264 queueReplacementWithParent(node, symbol, init, OriginalNode::BECOMES_CHILD);
265 }
266 }
267 }
268 return false;
269 }
270
271 private:
272 int mShaderVersion;
273 bool mCanUseLoopsToInitialize;
274 bool mHighPrecisionSupported;
275 };
276
277 } // namespace
278
CreateInitCode(const TIntermTyped * initializedSymbol,bool canUseLoopsToInitialize,bool highPrecisionSupported,TIntermSequence * initCode,TSymbolTable * symbolTable)279 void CreateInitCode(const TIntermTyped *initializedSymbol,
280 bool canUseLoopsToInitialize,
281 bool highPrecisionSupported,
282 TIntermSequence *initCode,
283 TSymbolTable *symbolTable)
284 {
285 AddZeroInitSequence(initializedSymbol, canUseLoopsToInitialize, highPrecisionSupported,
286 initCode, symbolTable);
287 }
288
InitializeUninitializedLocals(TCompiler * compiler,TIntermBlock * root,int shaderVersion,bool canUseLoopsToInitialize,bool highPrecisionSupported,TSymbolTable * symbolTable)289 bool InitializeUninitializedLocals(TCompiler *compiler,
290 TIntermBlock *root,
291 int shaderVersion,
292 bool canUseLoopsToInitialize,
293 bool highPrecisionSupported,
294 TSymbolTable *symbolTable)
295 {
296 InitializeLocalsTraverser traverser(shaderVersion, symbolTable, canUseLoopsToInitialize,
297 highPrecisionSupported);
298 root->traverse(&traverser);
299 return traverser.updateTree(compiler, root);
300 }
301
InitializeVariables(TCompiler * compiler,TIntermBlock * root,const InitVariableList & vars,TSymbolTable * symbolTable,int shaderVersion,const TExtensionBehavior & extensionBehavior,bool canUseLoopsToInitialize,bool highPrecisionSupported)302 bool InitializeVariables(TCompiler *compiler,
303 TIntermBlock *root,
304 const InitVariableList &vars,
305 TSymbolTable *symbolTable,
306 int shaderVersion,
307 const TExtensionBehavior &extensionBehavior,
308 bool canUseLoopsToInitialize,
309 bool highPrecisionSupported)
310 {
311 TIntermBlock *body = FindMainBody(root);
312 InsertInitCode(compiler, body->getSequence(), vars, symbolTable, shaderVersion,
313 extensionBehavior, canUseLoopsToInitialize, highPrecisionSupported);
314
315 return compiler->validateAST(root);
316 }
317
318 } // namespace sh
319