blob: 90741af69e7af353f4a238a183481a0700ab8e4c [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,
Olli Etuaho87cc90d2017-12-12 15:28:06 +020025 bool highPrecisionSupported,
Olli Etuaho2c7f34c2017-10-09 17:18:02 +030026 TIntermSequence *initSequenceOut,
27 TSymbolTable *symbolTable);
28
29void AddStructZeroInitSequence(const TIntermTyped *initializedNode,
30 bool canUseLoopsToInitialize,
Olli Etuaho87cc90d2017-12-12 15:28:06 +020031 bool highPrecisionSupported,
Olli Etuaho2c7f34c2017-10-09 17:18:02 +030032 TIntermSequence *initSequenceOut,
33 TSymbolTable *symbolTable);
Olli Etuaho9733cee2017-05-11 19:14:35 +030034
35TIntermBinary *CreateZeroInitAssignment(const TIntermTyped *initializedNode)
36{
Olli Etuaho3ec75682017-07-05 17:02:55 +030037 TIntermTyped *zero = CreateZeroNode(initializedNode->getType());
Olli Etuaho9733cee2017-05-11 19:14:35 +030038 return new TIntermBinary(EOpAssign, initializedNode->deepCopy(), zero);
39}
40
Olli Etuaho2c7f34c2017-10-09 17:18:02 +030041void AddZeroInitSequence(const TIntermTyped *initializedNode,
42 bool canUseLoopsToInitialize,
Olli Etuaho87cc90d2017-12-12 15:28:06 +020043 bool highPrecisionSupported,
Olli Etuaho2c7f34c2017-10-09 17:18:02 +030044 TIntermSequence *initSequenceOut,
45 TSymbolTable *symbolTable)
46{
47 if (initializedNode->isArray())
48 {
Olli Etuaho87cc90d2017-12-12 15:28:06 +020049 AddArrayZeroInitSequence(initializedNode, canUseLoopsToInitialize, highPrecisionSupported,
50 initSequenceOut, symbolTable);
Olli Etuaho2c7f34c2017-10-09 17:18:02 +030051 }
52 else if (initializedNode->getType().isStructureContainingArrays() ||
53 initializedNode->getType().isNamelessStruct())
54 {
Olli Etuaho87cc90d2017-12-12 15:28:06 +020055 AddStructZeroInitSequence(initializedNode, canUseLoopsToInitialize, highPrecisionSupported,
56 initSequenceOut, symbolTable);
Olli Etuaho2c7f34c2017-10-09 17:18:02 +030057 }
58 else
59 {
60 initSequenceOut->push_back(CreateZeroInitAssignment(initializedNode));
61 }
62}
63
Olli Etuaho9733cee2017-05-11 19:14:35 +030064void AddStructZeroInitSequence(const TIntermTyped *initializedNode,
Olli Etuaho2c7f34c2017-10-09 17:18:02 +030065 bool canUseLoopsToInitialize,
Olli Etuaho87cc90d2017-12-12 15:28:06 +020066 bool highPrecisionSupported,
Olli Etuaho2c7f34c2017-10-09 17:18:02 +030067 TIntermSequence *initSequenceOut,
68 TSymbolTable *symbolTable)
Olli Etuaho9733cee2017-05-11 19:14:35 +030069{
70 ASSERT(initializedNode->getBasicType() == EbtStruct);
Olli Etuahobd3cd502017-11-03 15:48:52 +020071 const TStructure *structType = initializedNode->getType().getStruct();
Olli Etuaho9733cee2017-05-11 19:14:35 +030072 for (int i = 0; i < static_cast<int>(structType->fields().size()); ++i)
73 {
Olli Etuaho3ec75682017-07-05 17:02:55 +030074 TIntermBinary *element = new TIntermBinary(EOpIndexDirectStruct,
75 initializedNode->deepCopy(), CreateIndexNode(i));
Olli Etuaho2c7f34c2017-10-09 17:18:02 +030076 // Structs can't be defined inside structs, so the type of a struct field can't be a
77 // nameless struct.
78 ASSERT(!element->getType().isNamelessStruct());
Olli Etuaho87cc90d2017-12-12 15:28:06 +020079 AddZeroInitSequence(element, canUseLoopsToInitialize, highPrecisionSupported,
80 initSequenceOut, symbolTable);
Olli Etuaho9733cee2017-05-11 19:14:35 +030081 }
82}
83
Olli Etuaho2c7f34c2017-10-09 17:18:02 +030084void AddArrayZeroInitStatementList(const TIntermTyped *initializedNode,
85 bool canUseLoopsToInitialize,
Olli Etuaho87cc90d2017-12-12 15:28:06 +020086 bool highPrecisionSupported,
Olli Etuaho2c7f34c2017-10-09 17:18:02 +030087 TIntermSequence *initSequenceOut,
88 TSymbolTable *symbolTable)
Olli Etuaho9733cee2017-05-11 19:14:35 +030089{
Olli Etuaho96f6adf2017-08-16 11:18:54 +030090 for (unsigned int i = 0; i < initializedNode->getOutermostArraySize(); ++i)
Olli Etuaho9733cee2017-05-11 19:14:35 +030091 {
Olli Etuaho3ec75682017-07-05 17:02:55 +030092 TIntermBinary *element =
93 new TIntermBinary(EOpIndexDirect, initializedNode->deepCopy(), CreateIndexNode(i));
Olli Etuaho87cc90d2017-12-12 15:28:06 +020094 AddZeroInitSequence(element, canUseLoopsToInitialize, highPrecisionSupported,
95 initSequenceOut, symbolTable);
Olli Etuaho2c7f34c2017-10-09 17:18:02 +030096 }
97}
98
99void AddArrayZeroInitForLoop(const TIntermTyped *initializedNode,
Olli Etuaho87cc90d2017-12-12 15:28:06 +0200100 bool highPrecisionSupported,
Olli Etuaho2c7f34c2017-10-09 17:18:02 +0300101 TIntermSequence *initSequenceOut,
102 TSymbolTable *symbolTable)
103{
104 ASSERT(initializedNode->isArray());
Olli Etuaho195be942017-12-04 23:40:14 +0200105 TVariable *indexVariable = CreateTempVariable(
106 symbolTable, TType(EbtInt, highPrecisionSupported ? EbpHigh : EbpMedium, EvqTemporary));
Olli Etuaho2c7f34c2017-10-09 17:18:02 +0300107
Olli Etuaho195be942017-12-04 23:40:14 +0200108 TIntermSymbol *indexSymbolNode = CreateTempSymbolNode(indexVariable);
Olli Etuaho2c7f34c2017-10-09 17:18:02 +0300109 TIntermDeclaration *indexInit =
Olli Etuaho195be942017-12-04 23:40:14 +0200110 CreateTempInitDeclarationNode(indexVariable, CreateZeroNode(indexVariable->getType()));
Olli Etuaho2c7f34c2017-10-09 17:18:02 +0300111 TIntermConstantUnion *arraySizeNode = CreateIndexNode(initializedNode->getOutermostArraySize());
112 TIntermBinary *indexSmallerThanSize =
113 new TIntermBinary(EOpLessThan, indexSymbolNode->deepCopy(), arraySizeNode);
114 TIntermUnary *indexIncrement = new TIntermUnary(EOpPreIncrement, indexSymbolNode->deepCopy());
115
116 TIntermBlock *forLoopBody = new TIntermBlock();
117 TIntermSequence *forLoopBodySeq = forLoopBody->getSequence();
118
119 TIntermBinary *element = new TIntermBinary(EOpIndexIndirect, initializedNode->deepCopy(),
120 indexSymbolNode->deepCopy());
Olli Etuaho87cc90d2017-12-12 15:28:06 +0200121 AddZeroInitSequence(element, true, highPrecisionSupported, forLoopBodySeq, symbolTable);
Olli Etuaho2c7f34c2017-10-09 17:18:02 +0300122
123 TIntermLoop *forLoop =
124 new TIntermLoop(ELoopFor, indexInit, indexSmallerThanSize, indexIncrement, forLoopBody);
125 initSequenceOut->push_back(forLoop);
126}
127
128void AddArrayZeroInitSequence(const TIntermTyped *initializedNode,
129 bool canUseLoopsToInitialize,
Olli Etuaho87cc90d2017-12-12 15:28:06 +0200130 bool highPrecisionSupported,
Olli Etuaho2c7f34c2017-10-09 17:18:02 +0300131 TIntermSequence *initSequenceOut,
132 TSymbolTable *symbolTable)
133{
134 // The array elements are assigned one by one to keep the AST compatible with ESSL 1.00 which
135 // doesn't have array assignment. We'll do this either with a for loop or just a list of
136 // statements assigning to each array index. Note that it is important to have the array init in
137 // the right order to workaround http://crbug.com/709317
138 bool isSmallArray = initializedNode->getOutermostArraySize() <= 1u ||
139 (initializedNode->getBasicType() != EbtStruct &&
140 !initializedNode->getType().isArrayOfArrays() &&
141 initializedNode->getOutermostArraySize() <= 3u);
142 if (initializedNode->getQualifier() == EvqFragData ||
143 initializedNode->getQualifier() == EvqFragmentOut || isSmallArray ||
144 !canUseLoopsToInitialize)
145 {
146 // Fragment outputs should not be indexed by non-constant indices.
147 // Also it doesn't make sense to use loops to initialize very small arrays.
Olli Etuaho87cc90d2017-12-12 15:28:06 +0200148 AddArrayZeroInitStatementList(initializedNode, canUseLoopsToInitialize,
149 highPrecisionSupported, initSequenceOut, symbolTable);
Olli Etuaho2c7f34c2017-10-09 17:18:02 +0300150 }
151 else
152 {
Olli Etuaho87cc90d2017-12-12 15:28:06 +0200153 AddArrayZeroInitForLoop(initializedNode, highPrecisionSupported, initSequenceOut,
154 symbolTable);
Olli Etuaho9733cee2017-05-11 19:14:35 +0300155 }
156}
157
158void InsertInitCode(TIntermSequence *mainBody,
Olli Etuaho9cbc07c2017-05-10 18:22:01 +0300159 const InitVariableList &variables,
Olli Etuaho2c7f34c2017-10-09 17:18:02 +0300160 TSymbolTable *symbolTable,
Martin Radeve145def2017-06-22 12:49:12 +0300161 int shaderVersion,
Olli Etuaho2c7f34c2017-10-09 17:18:02 +0300162 const TExtensionBehavior &extensionBehavior,
Olli Etuaho87cc90d2017-12-12 15:28:06 +0200163 bool canUseLoopsToInitialize,
164 bool highPrecisionSupported)
Zhenyao Mo72111912016-07-20 17:45:56 -0700165{
Olli Etuaho9cbc07c2017-05-10 18:22:01 +0300166 for (const auto &var : variables)
Zhenyao Mo4a667fe2014-02-11 12:35:01 -0800167 {
Zhenyao Mo72111912016-07-20 17:45:56 -0700168 TString name = TString(var.name.c_str());
Martin Radeve145def2017-06-22 12:49:12 +0300169 size_t pos = name.find_last_of('[');
170 if (pos != TString::npos)
Zhenyao Mo4a667fe2014-02-11 12:35:01 -0800171 {
Martin Radeve145def2017-06-22 12:49:12 +0300172 name = name.substr(0, pos);
Zhenyao Mof9312682016-07-22 12:51:31 -0700173 }
Zhenyao Mod7490962016-11-09 15:49:51 -0800174
Olli Etuahodaaff1c2017-07-05 18:03:26 +0300175 TIntermTyped *initializedSymbol = nullptr;
Martin Radeve145def2017-06-22 12:49:12 +0300176 if (var.isBuiltIn())
177 {
Olli Etuaho2c7f34c2017-10-09 17:18:02 +0300178 initializedSymbol = ReferenceBuiltInVariable(name, *symbolTable, shaderVersion);
Olli Etuahodaaff1c2017-07-05 18:03:26 +0300179 if (initializedSymbol->getQualifier() == EvqFragData &&
Olli Etuaho2a1e8f92017-07-14 11:49:36 +0300180 !IsExtensionEnabled(extensionBehavior, TExtension::EXT_draw_buffers))
Olli Etuahodaaff1c2017-07-05 18:03:26 +0300181 {
182 // If GL_EXT_draw_buffers is disabled, only the 0th index of gl_FragData can be
183 // written to.
184 // TODO(oetuaho): This is a bit hacky and would be better to remove, if we came up
185 // with a good way to do it. Right now "gl_FragData" in symbol table is initialized
186 // to have the array size of MaxDrawBuffers, and the initialization happens before
187 // the shader sets the extensions it is using.
188 initializedSymbol =
189 new TIntermBinary(EOpIndexDirect, initializedSymbol, CreateIndexNode(0));
190 }
Zhenyao Mod7490962016-11-09 15:49:51 -0800191 }
Zhenyao Mo4a667fe2014-02-11 12:35:01 -0800192 else
193 {
Olli Etuaho2c7f34c2017-10-09 17:18:02 +0300194 initializedSymbol = ReferenceGlobalVariable(name, *symbolTable);
Corentin Wallez509e4562016-08-25 14:55:44 -0400195 }
Olli Etuahodaaff1c2017-07-05 18:03:26 +0300196 ASSERT(initializedSymbol != nullptr);
Martin Radeve145def2017-06-22 12:49:12 +0300197
Olli Etuaho87cc90d2017-12-12 15:28:06 +0200198 TIntermSequence *initCode = CreateInitCode(initializedSymbol, canUseLoopsToInitialize,
199 highPrecisionSupported, symbolTable);
Olli Etuaho9733cee2017-05-11 19:14:35 +0300200 mainBody->insert(mainBody->begin(), initCode->begin(), initCode->end());
Zhenyao Mo4a667fe2014-02-11 12:35:01 -0800201 }
202}
203
Olli Etuaho9733cee2017-05-11 19:14:35 +0300204class InitializeLocalsTraverser : public TIntermTraverser
205{
206 public:
Olli Etuaho2c7f34c2017-10-09 17:18:02 +0300207 InitializeLocalsTraverser(int shaderVersion,
208 TSymbolTable *symbolTable,
Olli Etuaho87cc90d2017-12-12 15:28:06 +0200209 bool canUseLoopsToInitialize,
210 bool highPrecisionSupported)
Olli Etuaho2c7f34c2017-10-09 17:18:02 +0300211 : TIntermTraverser(true, false, false, symbolTable),
212 mShaderVersion(shaderVersion),
Olli Etuaho87cc90d2017-12-12 15:28:06 +0200213 mCanUseLoopsToInitialize(canUseLoopsToInitialize),
214 mHighPrecisionSupported(highPrecisionSupported)
Olli Etuaho9733cee2017-05-11 19:14:35 +0300215 {
216 }
217
218 protected:
219 bool visitDeclaration(Visit visit, TIntermDeclaration *node) override
220 {
221 for (TIntermNode *declarator : *node->getSequence())
222 {
223 if (!mInGlobalScope && !declarator->getAsBinaryNode())
224 {
225 TIntermSymbol *symbol = declarator->getAsSymbolNode();
226 ASSERT(symbol);
227 if (symbol->getSymbol() == "")
228 {
229 continue;
230 }
231
232 // Arrays may need to be initialized one element at a time, since ESSL 1.00 does not
233 // support array constructors or assigning arrays.
234 bool arrayConstructorUnavailable =
235 (symbol->isArray() || symbol->getType().isStructureContainingArrays()) &&
236 mShaderVersion == 100;
237 // Nameless struct constructors can't be referred to, so they also need to be
238 // initialized one element at a time.
Olli Etuaho2c7f34c2017-10-09 17:18:02 +0300239 // TODO(oetuaho): Check if it makes sense to initialize using a loop, even if we
240 // could use an initializer. It could at least reduce code size for very large
241 // arrays, but could hurt runtime performance.
Olli Etuaho858ff482017-11-15 13:41:46 +0200242 if (arrayConstructorUnavailable || symbol->getType().isNamelessStruct())
Olli Etuaho9733cee2017-05-11 19:14:35 +0300243 {
244 // SimplifyLoopConditions should have been run so the parent node of this node
245 // should not be a loop.
246 ASSERT(getParentNode()->getAsLoopNode() == nullptr);
247 // SeparateDeclarations should have already been run, so we don't need to worry
248 // about further declarators in this declaration depending on the effects of
249 // this declarator.
250 ASSERT(node->getSequence()->size() == 1);
Olli Etuaho2c7f34c2017-10-09 17:18:02 +0300251 insertStatementsInParentBlock(
Olli Etuaho87cc90d2017-12-12 15:28:06 +0200252 TIntermSequence(), *CreateInitCode(symbol, mCanUseLoopsToInitialize,
253 mHighPrecisionSupported, mSymbolTable));
Olli Etuaho9733cee2017-05-11 19:14:35 +0300254 }
255 else
256 {
Olli Etuaho3ec75682017-07-05 17:02:55 +0300257 TIntermBinary *init =
258 new TIntermBinary(EOpInitialize, symbol, CreateZeroNode(symbol->getType()));
Olli Etuaho9733cee2017-05-11 19:14:35 +0300259 queueReplacementWithParent(node, symbol, init, OriginalNode::BECOMES_CHILD);
260 }
261 }
262 }
263 return false;
264 }
265
266 private:
267 int mShaderVersion;
Olli Etuaho2c7f34c2017-10-09 17:18:02 +0300268 bool mCanUseLoopsToInitialize;
Olli Etuaho87cc90d2017-12-12 15:28:06 +0200269 bool mHighPrecisionSupported;
Olli Etuaho9733cee2017-05-11 19:14:35 +0300270};
271
Zhenyao Mo72111912016-07-20 17:45:56 -0700272} // namespace anonymous
273
Olli Etuaho2c7f34c2017-10-09 17:18:02 +0300274TIntermSequence *CreateInitCode(const TIntermTyped *initializedSymbol,
275 bool canUseLoopsToInitialize,
Olli Etuaho87cc90d2017-12-12 15:28:06 +0200276 bool highPrecisionSupported,
Olli Etuaho2c7f34c2017-10-09 17:18:02 +0300277 TSymbolTable *symbolTable)
Olli Etuaho9733cee2017-05-11 19:14:35 +0300278{
279 TIntermSequence *initCode = new TIntermSequence();
Olli Etuaho87cc90d2017-12-12 15:28:06 +0200280 AddZeroInitSequence(initializedSymbol, canUseLoopsToInitialize, highPrecisionSupported,
281 initCode, symbolTable);
Olli Etuaho9733cee2017-05-11 19:14:35 +0300282 return initCode;
283}
284
Olli Etuaho2c7f34c2017-10-09 17:18:02 +0300285void InitializeUninitializedLocals(TIntermBlock *root,
286 int shaderVersion,
287 bool canUseLoopsToInitialize,
Olli Etuaho87cc90d2017-12-12 15:28:06 +0200288 bool highPrecisionSupported,
Olli Etuaho2c7f34c2017-10-09 17:18:02 +0300289 TSymbolTable *symbolTable)
Olli Etuaho9733cee2017-05-11 19:14:35 +0300290{
Olli Etuaho87cc90d2017-12-12 15:28:06 +0200291 InitializeLocalsTraverser traverser(shaderVersion, symbolTable, canUseLoopsToInitialize,
292 highPrecisionSupported);
Olli Etuaho9733cee2017-05-11 19:14:35 +0300293 root->traverse(&traverser);
294 traverser.updateTree();
295}
296
Olli Etuaho9cbc07c2017-05-10 18:22:01 +0300297void InitializeVariables(TIntermBlock *root,
Zhenyao Mod7490962016-11-09 15:49:51 -0800298 const InitVariableList &vars,
Olli Etuaho2c7f34c2017-10-09 17:18:02 +0300299 TSymbolTable *symbolTable,
Martin Radeve145def2017-06-22 12:49:12 +0300300 int shaderVersion,
Olli Etuaho2c7f34c2017-10-09 17:18:02 +0300301 const TExtensionBehavior &extensionBehavior,
Olli Etuaho87cc90d2017-12-12 15:28:06 +0200302 bool canUseLoopsToInitialize,
303 bool highPrecisionSupported)
Zhenyao Mo72111912016-07-20 17:45:56 -0700304{
Martin Radevb50ccd32017-07-06 17:09:58 +0300305 TIntermBlock *body = FindMainBody(root);
Olli Etuaho2c7f34c2017-10-09 17:18:02 +0300306 InsertInitCode(body->getSequence(), vars, symbolTable, shaderVersion, extensionBehavior,
Olli Etuaho87cc90d2017-12-12 15:28:06 +0200307 canUseLoopsToInitialize, highPrecisionSupported);
Zhenyao Mo72111912016-07-20 17:45:56 -0700308}
Jamie Madill45bcc782016-11-07 13:58:48 -0500309
310} // namespace sh