blob: c1085de07ba9773088cc51e28f4a74884d47f12c [file] [log] [blame]
Olli Etuaho5d91dda2015-06-18 15:47:46 +03001//
2// Copyright (c) 2002-2015 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// RemoveDynamicIndexing is an AST traverser to remove dynamic indexing of vectors and matrices,
7// replacing them with calls to functions that choose which component to return or write.
8//
9
10#include "compiler/translator/RemoveDynamicIndexing.h"
11
12#include "compiler/translator/InfoSink.h"
Jamie Madill666f65a2016-08-26 01:34:37 +000013#include "compiler/translator/IntermNodePatternMatcher.h"
Olli Etuaho3ec75682017-07-05 17:02:55 +030014#include "compiler/translator/IntermNode_util.h"
Olli Etuahocccf2b02017-07-05 14:50:54 +030015#include "compiler/translator/IntermTraverse.h"
Olli Etuaho5d91dda2015-06-18 15:47:46 +030016#include "compiler/translator/SymbolTable.h"
17
Jamie Madill45bcc782016-11-07 13:58:48 -050018namespace sh
19{
20
Olli Etuaho5d91dda2015-06-18 15:47:46 +030021namespace
22{
23
Olli Etuahofe486322017-03-21 09:30:54 +000024std::string GetIndexFunctionName(const TType &type, bool write)
Olli Etuaho5d91dda2015-06-18 15:47:46 +030025{
26 TInfoSinkBase nameSink;
27 nameSink << "dyn_index_";
28 if (write)
29 {
30 nameSink << "write_";
31 }
32 if (type.isMatrix())
33 {
34 nameSink << "mat" << type.getCols() << "x" << type.getRows();
35 }
36 else
37 {
38 switch (type.getBasicType())
39 {
40 case EbtInt:
41 nameSink << "ivec";
42 break;
43 case EbtBool:
44 nameSink << "bvec";
45 break;
46 case EbtUInt:
47 nameSink << "uvec";
48 break;
49 case EbtFloat:
50 nameSink << "vec";
51 break;
52 default:
53 UNREACHABLE();
54 }
55 nameSink << type.getNominalSize();
56 }
Olli Etuahofe486322017-03-21 09:30:54 +000057 return nameSink.str();
Olli Etuaho5d91dda2015-06-18 15:47:46 +030058}
59
Olli Etuahob79b3f92017-07-21 14:23:21 +030060TIntermSymbol *CreateBaseSymbol(const TType &type, TQualifier qualifier, TSymbolTable *symbolTable)
Olli Etuaho5d91dda2015-06-18 15:47:46 +030061{
Olli Etuahob79b3f92017-07-21 14:23:21 +030062 TIntermSymbol *symbol = new TIntermSymbol(symbolTable->nextUniqueId(), "base", type);
Olli Etuaho5d91dda2015-06-18 15:47:46 +030063 symbol->setInternal(true);
64 symbol->getTypePointer()->setQualifier(qualifier);
65 return symbol;
66}
67
Olli Etuahob79b3f92017-07-21 14:23:21 +030068TIntermSymbol *CreateIndexSymbol(TSymbolTable *symbolTable)
Olli Etuaho5d91dda2015-06-18 15:47:46 +030069{
Olli Etuahob79b3f92017-07-21 14:23:21 +030070 TIntermSymbol *symbol =
71 new TIntermSymbol(symbolTable->nextUniqueId(), "index", TType(EbtInt, EbpHigh));
Olli Etuaho5d91dda2015-06-18 15:47:46 +030072 symbol->setInternal(true);
73 symbol->getTypePointer()->setQualifier(EvqIn);
74 return symbol;
75}
76
Olli Etuahob79b3f92017-07-21 14:23:21 +030077TIntermSymbol *CreateValueSymbol(const TType &type, TSymbolTable *symbolTable)
Olli Etuaho5d91dda2015-06-18 15:47:46 +030078{
Olli Etuahob79b3f92017-07-21 14:23:21 +030079 TIntermSymbol *symbol = new TIntermSymbol(symbolTable->nextUniqueId(), "value", type);
Olli Etuaho5d91dda2015-06-18 15:47:46 +030080 symbol->setInternal(true);
81 symbol->getTypePointer()->setQualifier(EvqIn);
82 return symbol;
83}
84
85TIntermConstantUnion *CreateIntConstantNode(int i)
86{
87 TConstantUnion *constant = new TConstantUnion();
88 constant->setIConst(i);
89 return new TIntermConstantUnion(constant, TType(EbtInt, EbpHigh));
90}
91
Olli Etuaho5d91dda2015-06-18 15:47:46 +030092TIntermTyped *EnsureSignedInt(TIntermTyped *node)
93{
94 if (node->getBasicType() == EbtInt)
95 return node;
96
Olli Etuahoaf6fc1b2017-01-26 17:45:35 -080097 TIntermSequence *arguments = new TIntermSequence();
98 arguments->push_back(node);
Olli Etuahoa7ecec32017-05-08 17:43:55 +030099 return TIntermAggregate::CreateConstructor(TType(EbtInt), arguments);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300100}
101
102TType GetFieldType(const TType &indexedType)
103{
104 if (indexedType.isMatrix())
105 {
106 TType fieldType = TType(indexedType.getBasicType(), indexedType.getPrecision());
107 fieldType.setPrimarySize(static_cast<unsigned char>(indexedType.getRows()));
108 return fieldType;
109 }
110 else
111 {
112 return TType(indexedType.getBasicType(), indexedType.getPrecision());
113 }
114}
115
116// Generate a read or write function for one field in a vector/matrix.
117// Out-of-range indices are clamped. This is consistent with how ANGLE handles out-of-range
118// indices in other places.
119// Note that indices can be either int or uint. We create only int versions of the functions,
120// and convert uint indices to int at the call site.
121// read function example:
122// float dyn_index_vec2(in vec2 base, in int index)
123// {
124// switch(index)
125// {
126// case (0):
127// return base[0];
128// case (1):
129// return base[1];
130// default:
131// break;
132// }
133// if (index < 0)
134// return base[0];
135// return base[1];
136// }
137// write function example:
138// void dyn_index_write_vec2(inout vec2 base, in int index, in float value)
139// {
140// switch(index)
141// {
142// case (0):
143// base[0] = value;
144// return;
145// case (1):
146// base[1] = value;
147// return;
148// default:
149// break;
150// }
151// if (index < 0)
152// {
153// base[0] = value;
154// return;
155// }
156// base[1] = value;
157// }
158// Note that else is not used in above functions to avoid the RewriteElseBlocks transformation.
Olli Etuahofe486322017-03-21 09:30:54 +0000159TIntermFunctionDefinition *GetIndexFunctionDefinition(TType type,
160 bool write,
Olli Etuahob79b3f92017-07-21 14:23:21 +0300161 const TSymbolUniqueId &functionId,
162 TSymbolTable *symbolTable)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300163{
164 ASSERT(!type.isArray());
165 // Conservatively use highp here, even if the indexed type is not highp. That way the code can't
166 // end up using mediump version of an indexing function for a highp value, if both mediump and
167 // highp values are being indexed in the shader. For HLSL precision doesn't matter, but in
168 // principle this code could be used with multiple backends.
169 type.setPrecision(EbpHigh);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300170
171 TType fieldType = GetFieldType(type);
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500172 int numCases = 0;
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300173 if (type.isMatrix())
174 {
175 numCases = type.getCols();
176 }
177 else
178 {
179 numCases = type.getNominalSize();
180 }
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300181
Olli Etuahofe486322017-03-21 09:30:54 +0000182 TType returnType(EbtVoid);
183 if (!write)
Olli Etuaho8ad9e752017-01-16 19:55:20 +0000184 {
Olli Etuahofe486322017-03-21 09:30:54 +0000185 returnType = fieldType;
Olli Etuaho8ad9e752017-01-16 19:55:20 +0000186 }
Olli Etuahofe486322017-03-21 09:30:54 +0000187
188 std::string functionName = GetIndexFunctionName(type, write);
Olli Etuaho3ec75682017-07-05 17:02:55 +0300189 TIntermFunctionPrototype *prototypeNode =
190 CreateInternalFunctionPrototypeNode(returnType, functionName.c_str(), functionId);
Olli Etuaho8ad9e752017-01-16 19:55:20 +0000191
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500192 TQualifier baseQualifier = EvqInOut;
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300193 if (!write)
194 baseQualifier = EvqIn;
Olli Etuahob79b3f92017-07-21 14:23:21 +0300195 TIntermSymbol *baseParam = CreateBaseSymbol(type, baseQualifier, symbolTable);
Olli Etuaho8ad9e752017-01-16 19:55:20 +0000196 prototypeNode->getSequence()->push_back(baseParam);
Olli Etuahob79b3f92017-07-21 14:23:21 +0300197 TIntermSymbol *indexParam = CreateIndexSymbol(symbolTable);
Olli Etuaho8ad9e752017-01-16 19:55:20 +0000198 prototypeNode->getSequence()->push_back(indexParam);
Olli Etuahob79b3f92017-07-21 14:23:21 +0300199 TIntermSymbol *valueParam = nullptr;
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300200 if (write)
201 {
Olli Etuahob79b3f92017-07-21 14:23:21 +0300202 valueParam = CreateValueSymbol(fieldType, symbolTable);
Olli Etuaho8ad9e752017-01-16 19:55:20 +0000203 prototypeNode->getSequence()->push_back(valueParam);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300204 }
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300205
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100206 TIntermBlock *statementList = new TIntermBlock();
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300207 for (int i = 0; i < numCases; ++i)
208 {
209 TIntermCase *caseNode = new TIntermCase(CreateIntConstantNode(i));
210 statementList->getSequence()->push_back(caseNode);
211
212 TIntermBinary *indexNode =
Olli Etuahob79b3f92017-07-21 14:23:21 +0300213 new TIntermBinary(EOpIndexDirect, baseParam->deepCopy(), CreateIndexNode(i));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300214 if (write)
215 {
Olli Etuahob79b3f92017-07-21 14:23:21 +0300216 TIntermBinary *assignNode =
217 new TIntermBinary(EOpAssign, indexNode, valueParam->deepCopy());
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300218 statementList->getSequence()->push_back(assignNode);
219 TIntermBranch *returnNode = new TIntermBranch(EOpReturn, nullptr);
220 statementList->getSequence()->push_back(returnNode);
221 }
222 else
223 {
224 TIntermBranch *returnNode = new TIntermBranch(EOpReturn, indexNode);
225 statementList->getSequence()->push_back(returnNode);
226 }
227 }
228
229 // Default case
230 TIntermCase *defaultNode = new TIntermCase(nullptr);
231 statementList->getSequence()->push_back(defaultNode);
232 TIntermBranch *breakNode = new TIntermBranch(EOpBreak, nullptr);
233 statementList->getSequence()->push_back(breakNode);
234
Olli Etuahob79b3f92017-07-21 14:23:21 +0300235 TIntermSwitch *switchNode = new TIntermSwitch(indexParam->deepCopy(), statementList);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300236
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100237 TIntermBlock *bodyNode = new TIntermBlock();
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300238 bodyNode->getSequence()->push_back(switchNode);
239
Olli Etuaho3272a6d2016-08-29 17:54:50 +0300240 TIntermBinary *cond =
Olli Etuahob79b3f92017-07-21 14:23:21 +0300241 new TIntermBinary(EOpLessThan, indexParam->deepCopy(), CreateIntConstantNode(0));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300242 cond->setType(TType(EbtBool, EbpUndefined));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300243
244 // Two blocks: one accesses (either reads or writes) the first element and returns,
245 // the other accesses the last element.
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100246 TIntermBlock *useFirstBlock = new TIntermBlock();
247 TIntermBlock *useLastBlock = new TIntermBlock();
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300248 TIntermBinary *indexFirstNode =
Olli Etuahob79b3f92017-07-21 14:23:21 +0300249 new TIntermBinary(EOpIndexDirect, baseParam->deepCopy(), CreateIndexNode(0));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300250 TIntermBinary *indexLastNode =
Olli Etuahob79b3f92017-07-21 14:23:21 +0300251 new TIntermBinary(EOpIndexDirect, baseParam->deepCopy(), CreateIndexNode(numCases - 1));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300252 if (write)
253 {
Olli Etuahob79b3f92017-07-21 14:23:21 +0300254 TIntermBinary *assignFirstNode =
255 new TIntermBinary(EOpAssign, indexFirstNode, valueParam->deepCopy());
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300256 useFirstBlock->getSequence()->push_back(assignFirstNode);
257 TIntermBranch *returnNode = new TIntermBranch(EOpReturn, nullptr);
258 useFirstBlock->getSequence()->push_back(returnNode);
259
Olli Etuahob79b3f92017-07-21 14:23:21 +0300260 TIntermBinary *assignLastNode =
261 new TIntermBinary(EOpAssign, indexLastNode, valueParam->deepCopy());
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300262 useLastBlock->getSequence()->push_back(assignLastNode);
263 }
264 else
265 {
266 TIntermBranch *returnFirstNode = new TIntermBranch(EOpReturn, indexFirstNode);
267 useFirstBlock->getSequence()->push_back(returnFirstNode);
268
269 TIntermBranch *returnLastNode = new TIntermBranch(EOpReturn, indexLastNode);
270 useLastBlock->getSequence()->push_back(returnLastNode);
271 }
Olli Etuaho57961272016-09-14 13:57:46 +0300272 TIntermIfElse *ifNode = new TIntermIfElse(cond, useFirstBlock, nullptr);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300273 bodyNode->getSequence()->push_back(ifNode);
274 bodyNode->getSequence()->push_back(useLastBlock);
275
Olli Etuaho8ad9e752017-01-16 19:55:20 +0000276 TIntermFunctionDefinition *indexingFunction =
277 new TIntermFunctionDefinition(prototypeNode, bodyNode);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300278 return indexingFunction;
279}
280
281class RemoveDynamicIndexingTraverser : public TLValueTrackingTraverser
282{
283 public:
Olli Etuahoa5e693a2017-07-13 16:07:26 +0300284 RemoveDynamicIndexingTraverser(TSymbolTable *symbolTable, int shaderVersion);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300285
286 bool visitBinary(Visit visit, TIntermBinary *node) override;
287
288 void insertHelperDefinitions(TIntermNode *root);
289
290 void nextIteration();
291
292 bool usedTreeInsertion() const { return mUsedTreeInsertion; }
293
294 protected:
Olli Etuahofe486322017-03-21 09:30:54 +0000295 // Maps of types that are indexed to the indexing function ids used for them. Note that these
296 // can not store multiple variants of the same type with different precisions - only one
297 // precision gets stored.
Olli Etuahoa5e693a2017-07-13 16:07:26 +0300298 std::map<TType, TSymbolUniqueId *> mIndexedVecAndMatrixTypes;
299 std::map<TType, TSymbolUniqueId *> mWrittenVecAndMatrixTypes;
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300300
301 bool mUsedTreeInsertion;
302
303 // When true, the traverser will remove side effects from any indexing expression.
304 // This is done so that in code like
305 // V[j++][i]++.
306 // where V is an array of vectors, j++ will only be evaluated once.
307 bool mRemoveIndexSideEffectsInSubtree;
308};
309
Olli Etuahoa5e693a2017-07-13 16:07:26 +0300310RemoveDynamicIndexingTraverser::RemoveDynamicIndexingTraverser(TSymbolTable *symbolTable,
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300311 int shaderVersion)
312 : TLValueTrackingTraverser(true, false, false, symbolTable, shaderVersion),
313 mUsedTreeInsertion(false),
314 mRemoveIndexSideEffectsInSubtree(false)
315{
316}
317
318void RemoveDynamicIndexingTraverser::insertHelperDefinitions(TIntermNode *root)
319{
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100320 TIntermBlock *rootBlock = root->getAsBlock();
321 ASSERT(rootBlock != nullptr);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300322 TIntermSequence insertions;
Olli Etuahofe486322017-03-21 09:30:54 +0000323 for (auto &type : mIndexedVecAndMatrixTypes)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300324 {
Olli Etuahob79b3f92017-07-21 14:23:21 +0300325 insertions.push_back(
326 GetIndexFunctionDefinition(type.first, false, *type.second, mSymbolTable));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300327 }
Olli Etuahofe486322017-03-21 09:30:54 +0000328 for (auto &type : mWrittenVecAndMatrixTypes)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300329 {
Olli Etuahob79b3f92017-07-21 14:23:21 +0300330 insertions.push_back(
331 GetIndexFunctionDefinition(type.first, true, *type.second, mSymbolTable));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300332 }
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100333 mInsertions.push_back(NodeInsertMultipleEntry(rootBlock, 0, insertions, TIntermSequence()));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300334}
335
336// Create a call to dyn_index_*() based on an indirect indexing op node
337TIntermAggregate *CreateIndexFunctionCall(TIntermBinary *node,
Olli Etuahofe486322017-03-21 09:30:54 +0000338 TIntermTyped *index,
339 const TSymbolUniqueId &functionId)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300340{
341 ASSERT(node->getOp() == EOpIndexIndirect);
Olli Etuahoaf6fc1b2017-01-26 17:45:35 -0800342 TIntermSequence *arguments = new TIntermSequence();
Olli Etuahofe486322017-03-21 09:30:54 +0000343 arguments->push_back(node->getLeft());
Olli Etuahoaf6fc1b2017-01-26 17:45:35 -0800344 arguments->push_back(index);
345
Olli Etuahofe486322017-03-21 09:30:54 +0000346 TType fieldType = GetFieldType(node->getLeft()->getType());
347 std::string functionName = GetIndexFunctionName(node->getLeft()->getType(), false);
Olli Etuaho3ec75682017-07-05 17:02:55 +0300348 TIntermAggregate *indexingCall =
349 CreateInternalFunctionCallNode(fieldType, functionName.c_str(), functionId, arguments);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300350 indexingCall->setLine(node->getLine());
Olli Etuahoa22aa4e2017-05-24 18:17:23 +0300351 indexingCall->getFunctionSymbolInfo()->setKnownToNotHaveSideEffects(true);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300352 return indexingCall;
353}
354
355TIntermAggregate *CreateIndexedWriteFunctionCall(TIntermBinary *node,
356 TIntermTyped *index,
Olli Etuahofe486322017-03-21 09:30:54 +0000357 TIntermTyped *writtenValue,
358 const TSymbolUniqueId &functionId)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300359{
Olli Etuahofe486322017-03-21 09:30:54 +0000360 ASSERT(node->getOp() == EOpIndexIndirect);
361 TIntermSequence *arguments = new TIntermSequence();
362 // Deep copy the child nodes so that two pointers to the same node don't end up in the tree.
363 arguments->push_back(node->getLeft()->deepCopy());
364 arguments->push_back(index->deepCopy());
365 arguments->push_back(writtenValue);
366
367 std::string functionName = GetIndexFunctionName(node->getLeft()->getType(), true);
Olli Etuaho3ec75682017-07-05 17:02:55 +0300368 TIntermAggregate *indexedWriteCall =
369 CreateInternalFunctionCallNode(TType(EbtVoid), functionName.c_str(), functionId, arguments);
Olli Etuahofe486322017-03-21 09:30:54 +0000370 indexedWriteCall->setLine(node->getLine());
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300371 return indexedWriteCall;
372}
373
374bool RemoveDynamicIndexingTraverser::visitBinary(Visit visit, TIntermBinary *node)
375{
376 if (mUsedTreeInsertion)
377 return false;
378
379 if (node->getOp() == EOpIndexIndirect)
380 {
381 if (mRemoveIndexSideEffectsInSubtree)
382 {
383 ASSERT(node->getRight()->hasSideEffects());
384 // In case we're just removing index side effects, convert
385 // v_expr[index_expr]
386 // to this:
387 // int s0 = index_expr; v_expr[s0];
388 // Now v_expr[s0] can be safely executed several times without unintended side effects.
389
390 // Init the temp variable holding the index
Olli Etuaho13389b62016-10-16 11:48:18 +0100391 TIntermDeclaration *initIndex = createTempInitDeclaration(node->getRight());
Jamie Madill1048e432016-07-23 18:51:28 -0400392 insertStatementInParentBlock(initIndex);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300393 mUsedTreeInsertion = true;
394
395 // Replace the index with the temp variable
396 TIntermSymbol *tempIndex = createTempSymbol(node->getRight()->getType());
Jamie Madill03d863c2016-07-27 18:15:53 -0400397 queueReplacementWithParent(node, node->getRight(), tempIndex, OriginalNode::IS_DROPPED);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300398 }
Jamie Madill666f65a2016-08-26 01:34:37 +0000399 else if (IntermNodePatternMatcher::IsDynamicIndexingOfVectorOrMatrix(node))
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300400 {
401 bool write = isLValueRequiredHere();
402
Jamie Madill666f65a2016-08-26 01:34:37 +0000403#if defined(ANGLE_ENABLE_ASSERTS)
404 // Make sure that IntermNodePatternMatcher is consistent with the slightly differently
405 // implemented checks in this traverser.
406 IntermNodePatternMatcher matcher(
407 IntermNodePatternMatcher::kDynamicIndexingOfVectorOrMatrixInLValue);
408 ASSERT(matcher.match(node, getParentNode(), isLValueRequiredHere()) == write);
409#endif
410
Olli Etuahofe486322017-03-21 09:30:54 +0000411 const TType &type = node->getLeft()->getType();
Olli Etuahoa5e693a2017-07-13 16:07:26 +0300412 TSymbolUniqueId *indexingFunctionId = new TSymbolUniqueId(mSymbolTable);
Olli Etuahofe486322017-03-21 09:30:54 +0000413 if (mIndexedVecAndMatrixTypes.find(type) == mIndexedVecAndMatrixTypes.end())
414 {
415 mIndexedVecAndMatrixTypes[type] = indexingFunctionId;
416 }
417 else
418 {
419 indexingFunctionId = mIndexedVecAndMatrixTypes[type];
420 }
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300421
422 if (write)
423 {
424 // Convert:
425 // v_expr[index_expr]++;
426 // to this:
427 // int s0 = index_expr; float s1 = dyn_index(v_expr, s0); s1++;
428 // dyn_index_write(v_expr, s0, s1);
429 // This works even if index_expr has some side effects.
430 if (node->getLeft()->hasSideEffects())
431 {
432 // If v_expr has side effects, those need to be removed before proceeding.
433 // Otherwise the side effects of v_expr would be evaluated twice.
434 // The only case where an l-value can have side effects is when it is
435 // indexing. For example, it can be V[j++] where V is an array of vectors.
436 mRemoveIndexSideEffectsInSubtree = true;
437 return true;
438 }
Olli Etuaho8f6eb2a2017-01-12 17:04:58 +0000439
440 TIntermBinary *leftBinary = node->getLeft()->getAsBinaryNode();
441 if (leftBinary != nullptr &&
442 IntermNodePatternMatcher::IsDynamicIndexingOfVectorOrMatrix(leftBinary))
443 {
444 // This is a case like:
445 // mat2 m;
446 // m[a][b]++;
447 // Process the child node m[a] first.
448 return true;
449 }
450
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300451 // TODO(oetuaho@nvidia.com): This is not optimal if the expression using the value
452 // only writes it and doesn't need the previous value. http://anglebug.com/1116
453
Olli Etuahoa5e693a2017-07-13 16:07:26 +0300454 TSymbolUniqueId *indexedWriteFunctionId = new TSymbolUniqueId(mSymbolTable);
Olli Etuahofe486322017-03-21 09:30:54 +0000455 if (mWrittenVecAndMatrixTypes.find(type) == mWrittenVecAndMatrixTypes.end())
456 {
457 mWrittenVecAndMatrixTypes[type] = indexedWriteFunctionId;
458 }
459 else
460 {
461 indexedWriteFunctionId = mWrittenVecAndMatrixTypes[type];
462 }
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300463 TType fieldType = GetFieldType(type);
464
465 TIntermSequence insertionsBefore;
466 TIntermSequence insertionsAfter;
467
468 // Store the index in a temporary signed int variable.
469 TIntermTyped *indexInitializer = EnsureSignedInt(node->getRight());
Olli Etuaho13389b62016-10-16 11:48:18 +0100470 TIntermDeclaration *initIndex = createTempInitDeclaration(indexInitializer);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300471 initIndex->setLine(node->getLine());
472 insertionsBefore.push_back(initIndex);
473
Olli Etuaho4dd06d52017-07-05 12:41:06 +0300474 // Create a node for referring to the index after the nextTemporaryId() call
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300475 // below.
476 TIntermSymbol *tempIndex = createTempSymbol(indexInitializer->getType());
477
Olli Etuahofe486322017-03-21 09:30:54 +0000478 TIntermAggregate *indexingCall =
Olli Etuahoa5e693a2017-07-13 16:07:26 +0300479 CreateIndexFunctionCall(node, tempIndex, *indexingFunctionId);
Olli Etuahofe486322017-03-21 09:30:54 +0000480
Olli Etuaho4dd06d52017-07-05 12:41:06 +0300481 nextTemporaryId(); // From now on, creating temporary symbols that refer to the
482 // field value.
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300483 insertionsBefore.push_back(createTempInitDeclaration(indexingCall));
484
Olli Etuahofe486322017-03-21 09:30:54 +0000485 TIntermAggregate *indexedWriteCall = CreateIndexedWriteFunctionCall(
Olli Etuahoa5e693a2017-07-13 16:07:26 +0300486 node, tempIndex, createTempSymbol(fieldType), *indexedWriteFunctionId);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300487 insertionsAfter.push_back(indexedWriteCall);
488 insertStatementsInParentBlock(insertionsBefore, insertionsAfter);
Olli Etuahoea39a222017-07-06 12:47:59 +0300489 queueReplacement(createTempSymbol(fieldType), OriginalNode::IS_DROPPED);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300490 mUsedTreeInsertion = true;
491 }
492 else
493 {
494 // The indexed value is not being written, so we can simply convert
495 // v_expr[index_expr]
496 // into
497 // dyn_index(v_expr, index_expr)
498 // If the index_expr is unsigned, we'll convert it to signed.
499 ASSERT(!mRemoveIndexSideEffectsInSubtree);
500 TIntermAggregate *indexingCall = CreateIndexFunctionCall(
Olli Etuahoa5e693a2017-07-13 16:07:26 +0300501 node, EnsureSignedInt(node->getRight()), *indexingFunctionId);
Olli Etuahoea39a222017-07-06 12:47:59 +0300502 queueReplacement(indexingCall, OriginalNode::IS_DROPPED);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300503 }
504 }
505 }
506 return !mUsedTreeInsertion;
507}
508
509void RemoveDynamicIndexingTraverser::nextIteration()
510{
511 mUsedTreeInsertion = false;
512 mRemoveIndexSideEffectsInSubtree = false;
Olli Etuaho4dd06d52017-07-05 12:41:06 +0300513 nextTemporaryId();
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300514}
515
516} // namespace
517
Olli Etuahoa5e693a2017-07-13 16:07:26 +0300518void RemoveDynamicIndexing(TIntermNode *root, TSymbolTable *symbolTable, int shaderVersion)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300519{
520 RemoveDynamicIndexingTraverser traverser(symbolTable, shaderVersion);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300521 do
522 {
523 traverser.nextIteration();
524 root->traverse(&traverser);
525 traverser.updateTree();
526 } while (traverser.usedTreeInsertion());
Olli Etuaho1ecd14b2017-01-26 13:54:15 -0800527 // TOOD(oetuaho@nvidia.com): It might be nicer to add the helper definitions also in the middle
528 // of traversal. Now the tree ends up in an inconsistent state in the middle, since there are
529 // function call nodes with no corresponding definition nodes. This needs special handling in
530 // TIntermLValueTrackingTraverser, and creates intricacies that are not easily apparent from a
531 // superficial reading of the code.
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300532 traverser.insertHelperDefinitions(root);
533 traverser.updateTree();
534}
Jamie Madill45bcc782016-11-07 13:58:48 -0500535
536} // namespace sh