blob: b3035c3b51f70f29dd54dabcd1d0c0f87cb63d54 [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"
Olli Etuahob60d30f2018-01-16 12:31:06 +020014#include "compiler/translator/StaticType.h"
Zhenyao Mod7490962016-11-09 15:49:51 -080015#include "compiler/translator/SymbolTable.h"
Zhenyao Mo72111912016-07-20 17:45:56 -070016#include "compiler/translator/util.h"
Zhenyao Mo4a667fe2014-02-11 12:35:01 -080017
Jamie Madill45bcc782016-11-07 13:58:48 -050018namespace sh
19{
20
Zhenyao Mo4a667fe2014-02-11 12:35:01 -080021namespace
22{
23
Olli Etuaho9733cee2017-05-11 19:14:35 +030024void AddArrayZeroInitSequence(const TIntermTyped *initializedNode,
Olli Etuaho2c7f34c2017-10-09 17:18:02 +030025 bool canUseLoopsToInitialize,
Olli Etuaho87cc90d2017-12-12 15:28:06 +020026 bool highPrecisionSupported,
Olli Etuaho2c7f34c2017-10-09 17:18:02 +030027 TIntermSequence *initSequenceOut,
28 TSymbolTable *symbolTable);
29
30void AddStructZeroInitSequence(const TIntermTyped *initializedNode,
31 bool canUseLoopsToInitialize,
Olli Etuaho87cc90d2017-12-12 15:28:06 +020032 bool highPrecisionSupported,
Olli Etuaho2c7f34c2017-10-09 17:18:02 +030033 TIntermSequence *initSequenceOut,
34 TSymbolTable *symbolTable);
Olli Etuaho9733cee2017-05-11 19:14:35 +030035
36TIntermBinary *CreateZeroInitAssignment(const TIntermTyped *initializedNode)
37{
Olli Etuaho3ec75682017-07-05 17:02:55 +030038 TIntermTyped *zero = CreateZeroNode(initializedNode->getType());
Olli Etuaho9733cee2017-05-11 19:14:35 +030039 return new TIntermBinary(EOpAssign, initializedNode->deepCopy(), zero);
40}
41
Olli Etuaho2c7f34c2017-10-09 17:18:02 +030042void AddZeroInitSequence(const TIntermTyped *initializedNode,
43 bool canUseLoopsToInitialize,
Olli Etuaho87cc90d2017-12-12 15:28:06 +020044 bool highPrecisionSupported,
Olli Etuaho2c7f34c2017-10-09 17:18:02 +030045 TIntermSequence *initSequenceOut,
46 TSymbolTable *symbolTable)
47{
48 if (initializedNode->isArray())
49 {
Olli Etuaho87cc90d2017-12-12 15:28:06 +020050 AddArrayZeroInitSequence(initializedNode, canUseLoopsToInitialize, highPrecisionSupported,
51 initSequenceOut, symbolTable);
Olli Etuaho2c7f34c2017-10-09 17:18:02 +030052 }
53 else if (initializedNode->getType().isStructureContainingArrays() ||
54 initializedNode->getType().isNamelessStruct())
55 {
Olli Etuaho87cc90d2017-12-12 15:28:06 +020056 AddStructZeroInitSequence(initializedNode, canUseLoopsToInitialize, highPrecisionSupported,
57 initSequenceOut, symbolTable);
Olli Etuaho2c7f34c2017-10-09 17:18:02 +030058 }
59 else
60 {
61 initSequenceOut->push_back(CreateZeroInitAssignment(initializedNode));
62 }
63}
64
Olli Etuaho9733cee2017-05-11 19:14:35 +030065void AddStructZeroInitSequence(const TIntermTyped *initializedNode,
Olli Etuaho2c7f34c2017-10-09 17:18:02 +030066 bool canUseLoopsToInitialize,
Olli Etuaho87cc90d2017-12-12 15:28:06 +020067 bool highPrecisionSupported,
Olli Etuaho2c7f34c2017-10-09 17:18:02 +030068 TIntermSequence *initSequenceOut,
69 TSymbolTable *symbolTable)
Olli Etuaho9733cee2017-05-11 19:14:35 +030070{
71 ASSERT(initializedNode->getBasicType() == EbtStruct);
Olli Etuahobd3cd502017-11-03 15:48:52 +020072 const TStructure *structType = initializedNode->getType().getStruct();
Olli Etuaho9733cee2017-05-11 19:14:35 +030073 for (int i = 0; i < static_cast<int>(structType->fields().size()); ++i)
74 {
Olli Etuaho3ec75682017-07-05 17:02:55 +030075 TIntermBinary *element = new TIntermBinary(EOpIndexDirectStruct,
76 initializedNode->deepCopy(), CreateIndexNode(i));
Olli Etuaho2c7f34c2017-10-09 17:18:02 +030077 // 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());
Olli Etuaho87cc90d2017-12-12 15:28:06 +020080 AddZeroInitSequence(element, canUseLoopsToInitialize, highPrecisionSupported,
81 initSequenceOut, symbolTable);
Olli Etuaho9733cee2017-05-11 19:14:35 +030082 }
83}
84
Olli Etuaho2c7f34c2017-10-09 17:18:02 +030085void AddArrayZeroInitStatementList(const TIntermTyped *initializedNode,
86 bool canUseLoopsToInitialize,
Olli Etuaho87cc90d2017-12-12 15:28:06 +020087 bool highPrecisionSupported,
Olli Etuaho2c7f34c2017-10-09 17:18:02 +030088 TIntermSequence *initSequenceOut,
89 TSymbolTable *symbolTable)
Olli Etuaho9733cee2017-05-11 19:14:35 +030090{
Olli Etuaho96f6adf2017-08-16 11:18:54 +030091 for (unsigned int i = 0; i < initializedNode->getOutermostArraySize(); ++i)
Olli Etuaho9733cee2017-05-11 19:14:35 +030092 {
Olli Etuaho3ec75682017-07-05 17:02:55 +030093 TIntermBinary *element =
94 new TIntermBinary(EOpIndexDirect, initializedNode->deepCopy(), CreateIndexNode(i));
Olli Etuaho87cc90d2017-12-12 15:28:06 +020095 AddZeroInitSequence(element, canUseLoopsToInitialize, highPrecisionSupported,
96 initSequenceOut, symbolTable);
Olli Etuaho2c7f34c2017-10-09 17:18:02 +030097 }
98}
99
100void AddArrayZeroInitForLoop(const TIntermTyped *initializedNode,
Olli Etuaho87cc90d2017-12-12 15:28:06 +0200101 bool highPrecisionSupported,
Olli Etuaho2c7f34c2017-10-09 17:18:02 +0300102 TIntermSequence *initSequenceOut,
103 TSymbolTable *symbolTable)
104{
105 ASSERT(initializedNode->isArray());
Olli Etuahob60d30f2018-01-16 12:31:06 +0200106 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);
Olli Etuaho2c7f34c2017-10-09 17:18:02 +0300110
Olli Etuaho195be942017-12-04 23:40:14 +0200111 TIntermSymbol *indexSymbolNode = CreateTempSymbolNode(indexVariable);
Olli Etuaho2c7f34c2017-10-09 17:18:02 +0300112 TIntermDeclaration *indexInit =
Olli Etuaho195be942017-12-04 23:40:14 +0200113 CreateTempInitDeclarationNode(indexVariable, CreateZeroNode(indexVariable->getType()));
Olli Etuaho2c7f34c2017-10-09 17:18:02 +0300114 TIntermConstantUnion *arraySizeNode = CreateIndexNode(initializedNode->getOutermostArraySize());
115 TIntermBinary *indexSmallerThanSize =
116 new TIntermBinary(EOpLessThan, indexSymbolNode->deepCopy(), arraySizeNode);
117 TIntermUnary *indexIncrement = new TIntermUnary(EOpPreIncrement, indexSymbolNode->deepCopy());
118
119 TIntermBlock *forLoopBody = new TIntermBlock();
120 TIntermSequence *forLoopBodySeq = forLoopBody->getSequence();
121
122 TIntermBinary *element = new TIntermBinary(EOpIndexIndirect, initializedNode->deepCopy(),
123 indexSymbolNode->deepCopy());
Olli Etuaho87cc90d2017-12-12 15:28:06 +0200124 AddZeroInitSequence(element, true, highPrecisionSupported, forLoopBodySeq, symbolTable);
Olli Etuaho2c7f34c2017-10-09 17:18:02 +0300125
126 TIntermLoop *forLoop =
127 new TIntermLoop(ELoopFor, indexInit, indexSmallerThanSize, indexIncrement, forLoopBody);
128 initSequenceOut->push_back(forLoop);
129}
130
131void AddArrayZeroInitSequence(const TIntermTyped *initializedNode,
132 bool canUseLoopsToInitialize,
Olli Etuaho87cc90d2017-12-12 15:28:06 +0200133 bool highPrecisionSupported,
Olli Etuaho2c7f34c2017-10-09 17:18:02 +0300134 TIntermSequence *initSequenceOut,
135 TSymbolTable *symbolTable)
136{
137 // The array elements are assigned one by one to keep the AST compatible with ESSL 1.00 which
138 // doesn't have array assignment. We'll do this either with a for loop or just a list of
139 // statements assigning to each array index. Note that it is important to have the array init in
140 // the right order to workaround http://crbug.com/709317
141 bool isSmallArray = initializedNode->getOutermostArraySize() <= 1u ||
142 (initializedNode->getBasicType() != EbtStruct &&
143 !initializedNode->getType().isArrayOfArrays() &&
144 initializedNode->getOutermostArraySize() <= 3u);
145 if (initializedNode->getQualifier() == EvqFragData ||
146 initializedNode->getQualifier() == EvqFragmentOut || isSmallArray ||
147 !canUseLoopsToInitialize)
148 {
149 // Fragment outputs should not be indexed by non-constant indices.
150 // Also it doesn't make sense to use loops to initialize very small arrays.
Olli Etuaho87cc90d2017-12-12 15:28:06 +0200151 AddArrayZeroInitStatementList(initializedNode, canUseLoopsToInitialize,
152 highPrecisionSupported, initSequenceOut, symbolTable);
Olli Etuaho2c7f34c2017-10-09 17:18:02 +0300153 }
154 else
155 {
Olli Etuaho87cc90d2017-12-12 15:28:06 +0200156 AddArrayZeroInitForLoop(initializedNode, highPrecisionSupported, initSequenceOut,
157 symbolTable);
Olli Etuaho9733cee2017-05-11 19:14:35 +0300158 }
159}
160
161void InsertInitCode(TIntermSequence *mainBody,
Olli Etuaho9cbc07c2017-05-10 18:22:01 +0300162 const InitVariableList &variables,
Olli Etuaho2c7f34c2017-10-09 17:18:02 +0300163 TSymbolTable *symbolTable,
Martin Radeve145def2017-06-22 12:49:12 +0300164 int shaderVersion,
Olli Etuaho2c7f34c2017-10-09 17:18:02 +0300165 const TExtensionBehavior &extensionBehavior,
Olli Etuaho87cc90d2017-12-12 15:28:06 +0200166 bool canUseLoopsToInitialize,
167 bool highPrecisionSupported)
Zhenyao Mo72111912016-07-20 17:45:56 -0700168{
Olli Etuaho9cbc07c2017-05-10 18:22:01 +0300169 for (const auto &var : variables)
Zhenyao Mo4a667fe2014-02-11 12:35:01 -0800170 {
Zhenyao Mo72111912016-07-20 17:45:56 -0700171 TString name = TString(var.name.c_str());
Martin Radeve145def2017-06-22 12:49:12 +0300172 size_t pos = name.find_last_of('[');
173 if (pos != TString::npos)
Zhenyao Mo4a667fe2014-02-11 12:35:01 -0800174 {
Martin Radeve145def2017-06-22 12:49:12 +0300175 name = name.substr(0, pos);
Zhenyao Mof9312682016-07-22 12:51:31 -0700176 }
Zhenyao Mod7490962016-11-09 15:49:51 -0800177
Olli Etuahodaaff1c2017-07-05 18:03:26 +0300178 TIntermTyped *initializedSymbol = nullptr;
Martin Radeve145def2017-06-22 12:49:12 +0300179 if (var.isBuiltIn())
180 {
Olli Etuaho2c7f34c2017-10-09 17:18:02 +0300181 initializedSymbol = ReferenceBuiltInVariable(name, *symbolTable, shaderVersion);
Olli Etuahodaaff1c2017-07-05 18:03:26 +0300182 if (initializedSymbol->getQualifier() == EvqFragData &&
Olli Etuaho2a1e8f92017-07-14 11:49:36 +0300183 !IsExtensionEnabled(extensionBehavior, TExtension::EXT_draw_buffers))
Olli Etuahodaaff1c2017-07-05 18:03:26 +0300184 {
185 // If GL_EXT_draw_buffers is disabled, only the 0th index of gl_FragData can be
186 // written to.
187 // TODO(oetuaho): This is a bit hacky and would be better to remove, if we came up
188 // with a good way to do it. Right now "gl_FragData" in symbol table is initialized
189 // to have the array size of MaxDrawBuffers, and the initialization happens before
190 // the shader sets the extensions it is using.
191 initializedSymbol =
192 new TIntermBinary(EOpIndexDirect, initializedSymbol, CreateIndexNode(0));
193 }
Zhenyao Mod7490962016-11-09 15:49:51 -0800194 }
Zhenyao Mo4a667fe2014-02-11 12:35:01 -0800195 else
196 {
Olli Etuaho2c7f34c2017-10-09 17:18:02 +0300197 initializedSymbol = ReferenceGlobalVariable(name, *symbolTable);
Corentin Wallez509e4562016-08-25 14:55:44 -0400198 }
Olli Etuahodaaff1c2017-07-05 18:03:26 +0300199 ASSERT(initializedSymbol != nullptr);
Martin Radeve145def2017-06-22 12:49:12 +0300200
Olli Etuaho87cc90d2017-12-12 15:28:06 +0200201 TIntermSequence *initCode = CreateInitCode(initializedSymbol, canUseLoopsToInitialize,
202 highPrecisionSupported, symbolTable);
Olli Etuaho9733cee2017-05-11 19:14:35 +0300203 mainBody->insert(mainBody->begin(), initCode->begin(), initCode->end());
Zhenyao Mo4a667fe2014-02-11 12:35:01 -0800204 }
205}
206
Olli Etuaho9733cee2017-05-11 19:14:35 +0300207class InitializeLocalsTraverser : public TIntermTraverser
208{
209 public:
Olli Etuaho2c7f34c2017-10-09 17:18:02 +0300210 InitializeLocalsTraverser(int shaderVersion,
211 TSymbolTable *symbolTable,
Olli Etuaho87cc90d2017-12-12 15:28:06 +0200212 bool canUseLoopsToInitialize,
213 bool highPrecisionSupported)
Olli Etuaho2c7f34c2017-10-09 17:18:02 +0300214 : TIntermTraverser(true, false, false, symbolTable),
215 mShaderVersion(shaderVersion),
Olli Etuaho87cc90d2017-12-12 15:28:06 +0200216 mCanUseLoopsToInitialize(canUseLoopsToInitialize),
217 mHighPrecisionSupported(highPrecisionSupported)
Olli Etuaho9733cee2017-05-11 19:14:35 +0300218 {
219 }
220
221 protected:
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);
Olli Etuaho8b5e8fd2017-12-15 14:59:15 +0200230 if (symbol->variable().symbolType() == SymbolType::Empty)
Olli Etuaho9733cee2017-05-11 19:14:35 +0300231 {
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.
Olli Etuaho2c7f34c2017-10-09 17:18:02 +0300242 // 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.
Olli Etuaho858ff482017-11-15 13:41:46 +0200245 if (arrayConstructorUnavailable || symbol->getType().isNamelessStruct())
Olli Etuaho9733cee2017-05-11 19:14:35 +0300246 {
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);
Olli Etuaho2c7f34c2017-10-09 17:18:02 +0300254 insertStatementsInParentBlock(
Olli Etuaho87cc90d2017-12-12 15:28:06 +0200255 TIntermSequence(), *CreateInitCode(symbol, mCanUseLoopsToInitialize,
256 mHighPrecisionSupported, mSymbolTable));
Olli Etuaho9733cee2017-05-11 19:14:35 +0300257 }
258 else
259 {
Olli Etuaho3ec75682017-07-05 17:02:55 +0300260 TIntermBinary *init =
261 new TIntermBinary(EOpInitialize, symbol, CreateZeroNode(symbol->getType()));
Olli Etuaho9733cee2017-05-11 19:14:35 +0300262 queueReplacementWithParent(node, symbol, init, OriginalNode::BECOMES_CHILD);
263 }
264 }
265 }
266 return false;
267 }
268
269 private:
270 int mShaderVersion;
Olli Etuaho2c7f34c2017-10-09 17:18:02 +0300271 bool mCanUseLoopsToInitialize;
Olli Etuaho87cc90d2017-12-12 15:28:06 +0200272 bool mHighPrecisionSupported;
Olli Etuaho9733cee2017-05-11 19:14:35 +0300273};
274
Zhenyao Mo72111912016-07-20 17:45:56 -0700275} // namespace anonymous
276
Olli Etuaho2c7f34c2017-10-09 17:18:02 +0300277TIntermSequence *CreateInitCode(const TIntermTyped *initializedSymbol,
278 bool canUseLoopsToInitialize,
Olli Etuaho87cc90d2017-12-12 15:28:06 +0200279 bool highPrecisionSupported,
Olli Etuaho2c7f34c2017-10-09 17:18:02 +0300280 TSymbolTable *symbolTable)
Olli Etuaho9733cee2017-05-11 19:14:35 +0300281{
282 TIntermSequence *initCode = new TIntermSequence();
Olli Etuaho87cc90d2017-12-12 15:28:06 +0200283 AddZeroInitSequence(initializedSymbol, canUseLoopsToInitialize, highPrecisionSupported,
284 initCode, symbolTable);
Olli Etuaho9733cee2017-05-11 19:14:35 +0300285 return initCode;
286}
287
Olli Etuaho2c7f34c2017-10-09 17:18:02 +0300288void InitializeUninitializedLocals(TIntermBlock *root,
289 int shaderVersion,
290 bool canUseLoopsToInitialize,
Olli Etuaho87cc90d2017-12-12 15:28:06 +0200291 bool highPrecisionSupported,
Olli Etuaho2c7f34c2017-10-09 17:18:02 +0300292 TSymbolTable *symbolTable)
Olli Etuaho9733cee2017-05-11 19:14:35 +0300293{
Olli Etuaho87cc90d2017-12-12 15:28:06 +0200294 InitializeLocalsTraverser traverser(shaderVersion, symbolTable, canUseLoopsToInitialize,
295 highPrecisionSupported);
Olli Etuaho9733cee2017-05-11 19:14:35 +0300296 root->traverse(&traverser);
297 traverser.updateTree();
298}
299
Olli Etuaho9cbc07c2017-05-10 18:22:01 +0300300void InitializeVariables(TIntermBlock *root,
Zhenyao Mod7490962016-11-09 15:49:51 -0800301 const InitVariableList &vars,
Olli Etuaho2c7f34c2017-10-09 17:18:02 +0300302 TSymbolTable *symbolTable,
Martin Radeve145def2017-06-22 12:49:12 +0300303 int shaderVersion,
Olli Etuaho2c7f34c2017-10-09 17:18:02 +0300304 const TExtensionBehavior &extensionBehavior,
Olli Etuaho87cc90d2017-12-12 15:28:06 +0200305 bool canUseLoopsToInitialize,
306 bool highPrecisionSupported)
Zhenyao Mo72111912016-07-20 17:45:56 -0700307{
Martin Radevb50ccd32017-07-06 17:09:58 +0300308 TIntermBlock *body = FindMainBody(root);
Olli Etuaho2c7f34c2017-10-09 17:18:02 +0300309 InsertInitCode(body->getSequence(), vars, symbolTable, shaderVersion, extensionBehavior,
Olli Etuaho87cc90d2017-12-12 15:28:06 +0200310 canUseLoopsToInitialize, highPrecisionSupported);
Zhenyao Mo72111912016-07-20 17:45:56 -0700311}
Jamie Madill45bcc782016-11-07 13:58:48 -0500312
313} // namespace sh