blob: aa1a042facd8fc3102bf578a521c01f4f3f25cce [file] [log] [blame]
Zhenyao Mo4a667fe2014-02-11 12:35:01 -08001//
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"
Olli Etuahod57e0db2015-04-24 15:05:08 +03008
Zhenyao Mo72111912016-07-20 17:45:56 -07009#include "angle_gl.h"
Olli Etuahod57e0db2015-04-24 15:05:08 +030010#include "common/debug.h"
Olli Etuaho9cbc07c2017-05-10 18:22:01 +030011#include "compiler/translator/FindMain.h"
Olli Etuaho3ec75682017-07-05 17:02:55 +030012#include "compiler/translator/IntermNode_util.h"
Olli Etuahocccf2b02017-07-05 14:50:54 +030013#include "compiler/translator/IntermTraverse.h"
Zhenyao Mod7490962016-11-09 15:49:51 -080014#include "compiler/translator/SymbolTable.h"
Zhenyao Mo72111912016-07-20 17:45:56 -070015#include "compiler/translator/util.h"
Zhenyao Mo4a667fe2014-02-11 12:35:01 -080016
Jamie Madill45bcc782016-11-07 13:58:48 -050017namespace sh
18{
19
Zhenyao Mo4a667fe2014-02-11 12:35:01 -080020namespace
21{
22
Olli Etuaho9733cee2017-05-11 19:14:35 +030023void AddArrayZeroInitSequence(const TIntermTyped *initializedNode,
Olli Etuaho2c7f34c2017-10-09 17:18:02 +030024 bool canUseLoopsToInitialize,
25 TIntermSequence *initSequenceOut,
26 TSymbolTable *symbolTable);
27
28void AddStructZeroInitSequence(const TIntermTyped *initializedNode,
29 bool canUseLoopsToInitialize,
30 TIntermSequence *initSequenceOut,
31 TSymbolTable *symbolTable);
Olli Etuaho9733cee2017-05-11 19:14:35 +030032
33TIntermBinary *CreateZeroInitAssignment(const TIntermTyped *initializedNode)
34{
Olli Etuaho3ec75682017-07-05 17:02:55 +030035 TIntermTyped *zero = CreateZeroNode(initializedNode->getType());
Olli Etuaho9733cee2017-05-11 19:14:35 +030036 return new TIntermBinary(EOpAssign, initializedNode->deepCopy(), zero);
37}
38
Olli Etuaho2c7f34c2017-10-09 17:18:02 +030039void 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
Olli Etuaho9733cee2017-05-11 19:14:35 +030061void AddStructZeroInitSequence(const TIntermTyped *initializedNode,
Olli Etuaho2c7f34c2017-10-09 17:18:02 +030062 bool canUseLoopsToInitialize,
63 TIntermSequence *initSequenceOut,
64 TSymbolTable *symbolTable)
Olli Etuaho9733cee2017-05-11 19:14:35 +030065{
66 ASSERT(initializedNode->getBasicType() == EbtStruct);
Olli Etuahobd3cd502017-11-03 15:48:52 +020067 const TStructure *structType = initializedNode->getType().getStruct();
Olli Etuaho9733cee2017-05-11 19:14:35 +030068 for (int i = 0; i < static_cast<int>(structType->fields().size()); ++i)
69 {
Olli Etuaho3ec75682017-07-05 17:02:55 +030070 TIntermBinary *element = new TIntermBinary(EOpIndexDirectStruct,
71 initializedNode->deepCopy(), CreateIndexNode(i));
Olli Etuaho2c7f34c2017-10-09 17:18:02 +030072 // 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);
Olli Etuaho9733cee2017-05-11 19:14:35 +030076 }
77}
78
Olli Etuaho2c7f34c2017-10-09 17:18:02 +030079void AddArrayZeroInitStatementList(const TIntermTyped *initializedNode,
80 bool canUseLoopsToInitialize,
81 TIntermSequence *initSequenceOut,
82 TSymbolTable *symbolTable)
Olli Etuaho9733cee2017-05-11 19:14:35 +030083{
Olli Etuaho96f6adf2017-08-16 11:18:54 +030084 for (unsigned int i = 0; i < initializedNode->getOutermostArraySize(); ++i)
Olli Etuaho9733cee2017-05-11 19:14:35 +030085 {
Olli Etuaho3ec75682017-07-05 17:02:55 +030086 TIntermBinary *element =
87 new TIntermBinary(EOpIndexDirect, initializedNode->deepCopy(), CreateIndexNode(i));
Olli Etuaho2c7f34c2017-10-09 17:18:02 +030088 AddZeroInitSequence(element, canUseLoopsToInitialize, initSequenceOut, symbolTable);
89 }
90}
91
92void 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
119void 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);
Olli Etuaho9733cee2017-05-11 19:14:35 +0300144 }
145}
146
147void InsertInitCode(TIntermSequence *mainBody,
Olli Etuaho9cbc07c2017-05-10 18:22:01 +0300148 const InitVariableList &variables,
Olli Etuaho2c7f34c2017-10-09 17:18:02 +0300149 TSymbolTable *symbolTable,
Martin Radeve145def2017-06-22 12:49:12 +0300150 int shaderVersion,
Olli Etuaho2c7f34c2017-10-09 17:18:02 +0300151 const TExtensionBehavior &extensionBehavior,
152 bool canUseLoopsToInitialize)
Zhenyao Mo72111912016-07-20 17:45:56 -0700153{
Olli Etuaho9cbc07c2017-05-10 18:22:01 +0300154 for (const auto &var : variables)
Zhenyao Mo4a667fe2014-02-11 12:35:01 -0800155 {
Zhenyao Mo72111912016-07-20 17:45:56 -0700156 TString name = TString(var.name.c_str());
Martin Radeve145def2017-06-22 12:49:12 +0300157 size_t pos = name.find_last_of('[');
158 if (pos != TString::npos)
Zhenyao Mo4a667fe2014-02-11 12:35:01 -0800159 {
Martin Radeve145def2017-06-22 12:49:12 +0300160 name = name.substr(0, pos);
Zhenyao Mof9312682016-07-22 12:51:31 -0700161 }
Zhenyao Mod7490962016-11-09 15:49:51 -0800162
Olli Etuahodaaff1c2017-07-05 18:03:26 +0300163 TIntermTyped *initializedSymbol = nullptr;
Martin Radeve145def2017-06-22 12:49:12 +0300164 if (var.isBuiltIn())
165 {
Olli Etuaho2c7f34c2017-10-09 17:18:02 +0300166 initializedSymbol = ReferenceBuiltInVariable(name, *symbolTable, shaderVersion);
Olli Etuahodaaff1c2017-07-05 18:03:26 +0300167 if (initializedSymbol->getQualifier() == EvqFragData &&
Olli Etuaho2a1e8f92017-07-14 11:49:36 +0300168 !IsExtensionEnabled(extensionBehavior, TExtension::EXT_draw_buffers))
Olli Etuahodaaff1c2017-07-05 18:03:26 +0300169 {
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 }
Zhenyao Mod7490962016-11-09 15:49:51 -0800179 }
Zhenyao Mo4a667fe2014-02-11 12:35:01 -0800180 else
181 {
Olli Etuaho2c7f34c2017-10-09 17:18:02 +0300182 initializedSymbol = ReferenceGlobalVariable(name, *symbolTable);
Corentin Wallez509e4562016-08-25 14:55:44 -0400183 }
Olli Etuahodaaff1c2017-07-05 18:03:26 +0300184 ASSERT(initializedSymbol != nullptr);
Martin Radeve145def2017-06-22 12:49:12 +0300185
Olli Etuaho2c7f34c2017-10-09 17:18:02 +0300186 TIntermSequence *initCode =
187 CreateInitCode(initializedSymbol, canUseLoopsToInitialize, symbolTable);
Olli Etuaho9733cee2017-05-11 19:14:35 +0300188 mainBody->insert(mainBody->begin(), initCode->begin(), initCode->end());
Zhenyao Mo4a667fe2014-02-11 12:35:01 -0800189 }
190}
191
Olli Etuaho9733cee2017-05-11 19:14:35 +0300192class InitializeLocalsTraverser : public TIntermTraverser
193{
194 public:
Olli Etuaho2c7f34c2017-10-09 17:18:02 +0300195 InitializeLocalsTraverser(int shaderVersion,
196 TSymbolTable *symbolTable,
197 bool canUseLoopsToInitialize)
198 : TIntermTraverser(true, false, false, symbolTable),
199 mShaderVersion(shaderVersion),
200 mCanUseLoopsToInitialize(canUseLoopsToInitialize)
Olli Etuaho9733cee2017-05-11 19:14:35 +0300201 {
202 }
203
204 protected:
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.
Olli Etuaho2c7f34c2017-10-09 17:18:02 +0300225 // 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.
Olli Etuaho858ff482017-11-15 13:41:46 +0200228 if (arrayConstructorUnavailable || symbol->getType().isNamelessStruct())
Olli Etuaho9733cee2017-05-11 19:14:35 +0300229 {
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);
Olli Etuaho2c7f34c2017-10-09 17:18:02 +0300237 insertStatementsInParentBlock(
238 TIntermSequence(),
239 *CreateInitCode(symbol, mCanUseLoopsToInitialize, mSymbolTable));
Olli Etuaho9733cee2017-05-11 19:14:35 +0300240 }
241 else
242 {
Olli Etuaho3ec75682017-07-05 17:02:55 +0300243 TIntermBinary *init =
244 new TIntermBinary(EOpInitialize, symbol, CreateZeroNode(symbol->getType()));
Olli Etuaho9733cee2017-05-11 19:14:35 +0300245 queueReplacementWithParent(node, symbol, init, OriginalNode::BECOMES_CHILD);
246 }
247 }
248 }
249 return false;
250 }
251
252 private:
253 int mShaderVersion;
Olli Etuaho2c7f34c2017-10-09 17:18:02 +0300254 bool mCanUseLoopsToInitialize;
Olli Etuaho9733cee2017-05-11 19:14:35 +0300255};
256
Zhenyao Mo72111912016-07-20 17:45:56 -0700257} // namespace anonymous
258
Olli Etuaho2c7f34c2017-10-09 17:18:02 +0300259TIntermSequence *CreateInitCode(const TIntermTyped *initializedSymbol,
260 bool canUseLoopsToInitialize,
261 TSymbolTable *symbolTable)
Olli Etuaho9733cee2017-05-11 19:14:35 +0300262{
263 TIntermSequence *initCode = new TIntermSequence();
Olli Etuaho2c7f34c2017-10-09 17:18:02 +0300264 AddZeroInitSequence(initializedSymbol, canUseLoopsToInitialize, initCode, symbolTable);
Olli Etuaho9733cee2017-05-11 19:14:35 +0300265 return initCode;
266}
267
Olli Etuaho2c7f34c2017-10-09 17:18:02 +0300268void InitializeUninitializedLocals(TIntermBlock *root,
269 int shaderVersion,
270 bool canUseLoopsToInitialize,
271 TSymbolTable *symbolTable)
Olli Etuaho9733cee2017-05-11 19:14:35 +0300272{
Olli Etuaho2c7f34c2017-10-09 17:18:02 +0300273 InitializeLocalsTraverser traverser(shaderVersion, symbolTable, canUseLoopsToInitialize);
Olli Etuaho9733cee2017-05-11 19:14:35 +0300274 root->traverse(&traverser);
275 traverser.updateTree();
276}
277
Olli Etuaho9cbc07c2017-05-10 18:22:01 +0300278void InitializeVariables(TIntermBlock *root,
Zhenyao Mod7490962016-11-09 15:49:51 -0800279 const InitVariableList &vars,
Olli Etuaho2c7f34c2017-10-09 17:18:02 +0300280 TSymbolTable *symbolTable,
Martin Radeve145def2017-06-22 12:49:12 +0300281 int shaderVersion,
Olli Etuaho2c7f34c2017-10-09 17:18:02 +0300282 const TExtensionBehavior &extensionBehavior,
283 bool canUseLoopsToInitialize)
Zhenyao Mo72111912016-07-20 17:45:56 -0700284{
Martin Radevb50ccd32017-07-06 17:09:58 +0300285 TIntermBlock *body = FindMainBody(root);
Olli Etuaho2c7f34c2017-10-09 17:18:02 +0300286 InsertInitCode(body->getSequence(), vars, symbolTable, shaderVersion, extensionBehavior,
287 canUseLoopsToInitialize);
Zhenyao Mo72111912016-07-20 17:45:56 -0700288}
Jamie Madill45bcc782016-11-07 13:58:48 -0500289
290} // namespace sh