blob: 06d77ca5a2d4bb019dfd34ddda906c28bd3b84c4 [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
60TIntermSymbol *CreateBaseSymbol(const TType &type, TQualifier qualifier)
61{
62 TIntermSymbol *symbol = new TIntermSymbol(0, "base", type);
63 symbol->setInternal(true);
64 symbol->getTypePointer()->setQualifier(qualifier);
65 return symbol;
66}
67
68TIntermSymbol *CreateIndexSymbol()
69{
70 TIntermSymbol *symbol = new TIntermSymbol(0, "index", TType(EbtInt, EbpHigh));
71 symbol->setInternal(true);
72 symbol->getTypePointer()->setQualifier(EvqIn);
73 return symbol;
74}
75
76TIntermSymbol *CreateValueSymbol(const TType &type)
77{
78 TIntermSymbol *symbol = new TIntermSymbol(0, "value", type);
79 symbol->setInternal(true);
80 symbol->getTypePointer()->setQualifier(EvqIn);
81 return symbol;
82}
83
84TIntermConstantUnion *CreateIntConstantNode(int i)
85{
86 TConstantUnion *constant = new TConstantUnion();
87 constant->setIConst(i);
88 return new TIntermConstantUnion(constant, TType(EbtInt, EbpHigh));
89}
90
91TIntermBinary *CreateIndexDirectBaseSymbolNode(const TType &indexedType,
92 const TType &fieldType,
93 const int index,
94 TQualifier baseQualifier)
95{
Olli Etuaho5d91dda2015-06-18 15:47:46 +030096 TIntermSymbol *baseSymbol = CreateBaseSymbol(indexedType, baseQualifier);
Olli Etuaho3272a6d2016-08-29 17:54:50 +030097 TIntermBinary *indexNode =
Olli Etuaho3ec75682017-07-05 17:02:55 +030098 new TIntermBinary(EOpIndexDirect, baseSymbol, CreateIndexNode(index));
Olli Etuaho5d91dda2015-06-18 15:47:46 +030099 return indexNode;
100}
101
102TIntermBinary *CreateAssignValueSymbolNode(TIntermTyped *targetNode, const TType &assignedValueType)
103{
Olli Etuaho3272a6d2016-08-29 17:54:50 +0300104 return new TIntermBinary(EOpAssign, targetNode, CreateValueSymbol(assignedValueType));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300105}
106
107TIntermTyped *EnsureSignedInt(TIntermTyped *node)
108{
109 if (node->getBasicType() == EbtInt)
110 return node;
111
Olli Etuahoaf6fc1b2017-01-26 17:45:35 -0800112 TIntermSequence *arguments = new TIntermSequence();
113 arguments->push_back(node);
Olli Etuahoa7ecec32017-05-08 17:43:55 +0300114 return TIntermAggregate::CreateConstructor(TType(EbtInt), arguments);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300115}
116
117TType GetFieldType(const TType &indexedType)
118{
119 if (indexedType.isMatrix())
120 {
121 TType fieldType = TType(indexedType.getBasicType(), indexedType.getPrecision());
122 fieldType.setPrimarySize(static_cast<unsigned char>(indexedType.getRows()));
123 return fieldType;
124 }
125 else
126 {
127 return TType(indexedType.getBasicType(), indexedType.getPrecision());
128 }
129}
130
131// Generate a read or write function for one field in a vector/matrix.
132// Out-of-range indices are clamped. This is consistent with how ANGLE handles out-of-range
133// indices in other places.
134// Note that indices can be either int or uint. We create only int versions of the functions,
135// and convert uint indices to int at the call site.
136// read function example:
137// float dyn_index_vec2(in vec2 base, in int index)
138// {
139// switch(index)
140// {
141// case (0):
142// return base[0];
143// case (1):
144// return base[1];
145// default:
146// break;
147// }
148// if (index < 0)
149// return base[0];
150// return base[1];
151// }
152// write function example:
153// void dyn_index_write_vec2(inout vec2 base, in int index, in float value)
154// {
155// switch(index)
156// {
157// case (0):
158// base[0] = value;
159// return;
160// case (1):
161// base[1] = value;
162// return;
163// default:
164// break;
165// }
166// if (index < 0)
167// {
168// base[0] = value;
169// return;
170// }
171// base[1] = value;
172// }
173// Note that else is not used in above functions to avoid the RewriteElseBlocks transformation.
Olli Etuahofe486322017-03-21 09:30:54 +0000174TIntermFunctionDefinition *GetIndexFunctionDefinition(TType type,
175 bool write,
176 const TSymbolUniqueId &functionId)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300177{
178 ASSERT(!type.isArray());
179 // Conservatively use highp here, even if the indexed type is not highp. That way the code can't
180 // end up using mediump version of an indexing function for a highp value, if both mediump and
181 // highp values are being indexed in the shader. For HLSL precision doesn't matter, but in
182 // principle this code could be used with multiple backends.
183 type.setPrecision(EbpHigh);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300184
185 TType fieldType = GetFieldType(type);
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500186 int numCases = 0;
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300187 if (type.isMatrix())
188 {
189 numCases = type.getCols();
190 }
191 else
192 {
193 numCases = type.getNominalSize();
194 }
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300195
Olli Etuahofe486322017-03-21 09:30:54 +0000196 TType returnType(EbtVoid);
197 if (!write)
Olli Etuaho8ad9e752017-01-16 19:55:20 +0000198 {
Olli Etuahofe486322017-03-21 09:30:54 +0000199 returnType = fieldType;
Olli Etuaho8ad9e752017-01-16 19:55:20 +0000200 }
Olli Etuahofe486322017-03-21 09:30:54 +0000201
202 std::string functionName = GetIndexFunctionName(type, write);
Olli Etuaho3ec75682017-07-05 17:02:55 +0300203 TIntermFunctionPrototype *prototypeNode =
204 CreateInternalFunctionPrototypeNode(returnType, functionName.c_str(), functionId);
Olli Etuaho8ad9e752017-01-16 19:55:20 +0000205
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500206 TQualifier baseQualifier = EvqInOut;
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300207 if (!write)
208 baseQualifier = EvqIn;
209 TIntermSymbol *baseParam = CreateBaseSymbol(type, baseQualifier);
Olli Etuaho8ad9e752017-01-16 19:55:20 +0000210 prototypeNode->getSequence()->push_back(baseParam);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300211 TIntermSymbol *indexParam = CreateIndexSymbol();
Olli Etuaho8ad9e752017-01-16 19:55:20 +0000212 prototypeNode->getSequence()->push_back(indexParam);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300213 if (write)
214 {
215 TIntermSymbol *valueParam = CreateValueSymbol(fieldType);
Olli Etuaho8ad9e752017-01-16 19:55:20 +0000216 prototypeNode->getSequence()->push_back(valueParam);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300217 }
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300218
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100219 TIntermBlock *statementList = new TIntermBlock();
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300220 for (int i = 0; i < numCases; ++i)
221 {
222 TIntermCase *caseNode = new TIntermCase(CreateIntConstantNode(i));
223 statementList->getSequence()->push_back(caseNode);
224
225 TIntermBinary *indexNode =
226 CreateIndexDirectBaseSymbolNode(type, fieldType, i, baseQualifier);
227 if (write)
228 {
229 TIntermBinary *assignNode = CreateAssignValueSymbolNode(indexNode, fieldType);
230 statementList->getSequence()->push_back(assignNode);
231 TIntermBranch *returnNode = new TIntermBranch(EOpReturn, nullptr);
232 statementList->getSequence()->push_back(returnNode);
233 }
234 else
235 {
236 TIntermBranch *returnNode = new TIntermBranch(EOpReturn, indexNode);
237 statementList->getSequence()->push_back(returnNode);
238 }
239 }
240
241 // Default case
242 TIntermCase *defaultNode = new TIntermCase(nullptr);
243 statementList->getSequence()->push_back(defaultNode);
244 TIntermBranch *breakNode = new TIntermBranch(EOpBreak, nullptr);
245 statementList->getSequence()->push_back(breakNode);
246
247 TIntermSwitch *switchNode = new TIntermSwitch(CreateIndexSymbol(), statementList);
248
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100249 TIntermBlock *bodyNode = new TIntermBlock();
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300250 bodyNode->getSequence()->push_back(switchNode);
251
Olli Etuaho3272a6d2016-08-29 17:54:50 +0300252 TIntermBinary *cond =
253 new TIntermBinary(EOpLessThan, CreateIndexSymbol(), CreateIntConstantNode(0));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300254 cond->setType(TType(EbtBool, EbpUndefined));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300255
256 // Two blocks: one accesses (either reads or writes) the first element and returns,
257 // the other accesses the last element.
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100258 TIntermBlock *useFirstBlock = new TIntermBlock();
259 TIntermBlock *useLastBlock = new TIntermBlock();
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300260 TIntermBinary *indexFirstNode =
261 CreateIndexDirectBaseSymbolNode(type, fieldType, 0, baseQualifier);
262 TIntermBinary *indexLastNode =
263 CreateIndexDirectBaseSymbolNode(type, fieldType, numCases - 1, baseQualifier);
264 if (write)
265 {
266 TIntermBinary *assignFirstNode = CreateAssignValueSymbolNode(indexFirstNode, fieldType);
267 useFirstBlock->getSequence()->push_back(assignFirstNode);
268 TIntermBranch *returnNode = new TIntermBranch(EOpReturn, nullptr);
269 useFirstBlock->getSequence()->push_back(returnNode);
270
271 TIntermBinary *assignLastNode = CreateAssignValueSymbolNode(indexLastNode, fieldType);
272 useLastBlock->getSequence()->push_back(assignLastNode);
273 }
274 else
275 {
276 TIntermBranch *returnFirstNode = new TIntermBranch(EOpReturn, indexFirstNode);
277 useFirstBlock->getSequence()->push_back(returnFirstNode);
278
279 TIntermBranch *returnLastNode = new TIntermBranch(EOpReturn, indexLastNode);
280 useLastBlock->getSequence()->push_back(returnLastNode);
281 }
Olli Etuaho57961272016-09-14 13:57:46 +0300282 TIntermIfElse *ifNode = new TIntermIfElse(cond, useFirstBlock, nullptr);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300283 bodyNode->getSequence()->push_back(ifNode);
284 bodyNode->getSequence()->push_back(useLastBlock);
285
Olli Etuaho8ad9e752017-01-16 19:55:20 +0000286 TIntermFunctionDefinition *indexingFunction =
287 new TIntermFunctionDefinition(prototypeNode, bodyNode);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300288 return indexingFunction;
289}
290
291class RemoveDynamicIndexingTraverser : public TLValueTrackingTraverser
292{
293 public:
294 RemoveDynamicIndexingTraverser(const TSymbolTable &symbolTable, int shaderVersion);
295
296 bool visitBinary(Visit visit, TIntermBinary *node) override;
297
298 void insertHelperDefinitions(TIntermNode *root);
299
300 void nextIteration();
301
302 bool usedTreeInsertion() const { return mUsedTreeInsertion; }
303
304 protected:
Olli Etuahofe486322017-03-21 09:30:54 +0000305 // Maps of types that are indexed to the indexing function ids used for them. Note that these
306 // can not store multiple variants of the same type with different precisions - only one
307 // precision gets stored.
308 std::map<TType, TSymbolUniqueId> mIndexedVecAndMatrixTypes;
309 std::map<TType, TSymbolUniqueId> mWrittenVecAndMatrixTypes;
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300310
311 bool mUsedTreeInsertion;
312
313 // When true, the traverser will remove side effects from any indexing expression.
314 // This is done so that in code like
315 // V[j++][i]++.
316 // where V is an array of vectors, j++ will only be evaluated once.
317 bool mRemoveIndexSideEffectsInSubtree;
318};
319
320RemoveDynamicIndexingTraverser::RemoveDynamicIndexingTraverser(const TSymbolTable &symbolTable,
321 int shaderVersion)
322 : TLValueTrackingTraverser(true, false, false, symbolTable, shaderVersion),
323 mUsedTreeInsertion(false),
324 mRemoveIndexSideEffectsInSubtree(false)
325{
326}
327
328void RemoveDynamicIndexingTraverser::insertHelperDefinitions(TIntermNode *root)
329{
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100330 TIntermBlock *rootBlock = root->getAsBlock();
331 ASSERT(rootBlock != nullptr);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300332 TIntermSequence insertions;
Olli Etuahofe486322017-03-21 09:30:54 +0000333 for (auto &type : mIndexedVecAndMatrixTypes)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300334 {
Olli Etuahofe486322017-03-21 09:30:54 +0000335 insertions.push_back(GetIndexFunctionDefinition(type.first, false, type.second));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300336 }
Olli Etuahofe486322017-03-21 09:30:54 +0000337 for (auto &type : mWrittenVecAndMatrixTypes)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300338 {
Olli Etuahofe486322017-03-21 09:30:54 +0000339 insertions.push_back(GetIndexFunctionDefinition(type.first, true, type.second));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300340 }
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100341 mInsertions.push_back(NodeInsertMultipleEntry(rootBlock, 0, insertions, TIntermSequence()));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300342}
343
344// Create a call to dyn_index_*() based on an indirect indexing op node
345TIntermAggregate *CreateIndexFunctionCall(TIntermBinary *node,
Olli Etuahofe486322017-03-21 09:30:54 +0000346 TIntermTyped *index,
347 const TSymbolUniqueId &functionId)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300348{
349 ASSERT(node->getOp() == EOpIndexIndirect);
Olli Etuahoaf6fc1b2017-01-26 17:45:35 -0800350 TIntermSequence *arguments = new TIntermSequence();
Olli Etuahofe486322017-03-21 09:30:54 +0000351 arguments->push_back(node->getLeft());
Olli Etuahoaf6fc1b2017-01-26 17:45:35 -0800352 arguments->push_back(index);
353
Olli Etuahofe486322017-03-21 09:30:54 +0000354 TType fieldType = GetFieldType(node->getLeft()->getType());
355 std::string functionName = GetIndexFunctionName(node->getLeft()->getType(), false);
Olli Etuaho3ec75682017-07-05 17:02:55 +0300356 TIntermAggregate *indexingCall =
357 CreateInternalFunctionCallNode(fieldType, functionName.c_str(), functionId, arguments);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300358 indexingCall->setLine(node->getLine());
Olli Etuahoa22aa4e2017-05-24 18:17:23 +0300359 indexingCall->getFunctionSymbolInfo()->setKnownToNotHaveSideEffects(true);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300360 return indexingCall;
361}
362
363TIntermAggregate *CreateIndexedWriteFunctionCall(TIntermBinary *node,
364 TIntermTyped *index,
Olli Etuahofe486322017-03-21 09:30:54 +0000365 TIntermTyped *writtenValue,
366 const TSymbolUniqueId &functionId)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300367{
Olli Etuahofe486322017-03-21 09:30:54 +0000368 ASSERT(node->getOp() == EOpIndexIndirect);
369 TIntermSequence *arguments = new TIntermSequence();
370 // Deep copy the child nodes so that two pointers to the same node don't end up in the tree.
371 arguments->push_back(node->getLeft()->deepCopy());
372 arguments->push_back(index->deepCopy());
373 arguments->push_back(writtenValue);
374
375 std::string functionName = GetIndexFunctionName(node->getLeft()->getType(), true);
Olli Etuaho3ec75682017-07-05 17:02:55 +0300376 TIntermAggregate *indexedWriteCall =
377 CreateInternalFunctionCallNode(TType(EbtVoid), functionName.c_str(), functionId, arguments);
Olli Etuahofe486322017-03-21 09:30:54 +0000378 indexedWriteCall->setLine(node->getLine());
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300379 return indexedWriteCall;
380}
381
382bool RemoveDynamicIndexingTraverser::visitBinary(Visit visit, TIntermBinary *node)
383{
384 if (mUsedTreeInsertion)
385 return false;
386
387 if (node->getOp() == EOpIndexIndirect)
388 {
389 if (mRemoveIndexSideEffectsInSubtree)
390 {
391 ASSERT(node->getRight()->hasSideEffects());
392 // In case we're just removing index side effects, convert
393 // v_expr[index_expr]
394 // to this:
395 // int s0 = index_expr; v_expr[s0];
396 // Now v_expr[s0] can be safely executed several times without unintended side effects.
397
398 // Init the temp variable holding the index
Olli Etuaho13389b62016-10-16 11:48:18 +0100399 TIntermDeclaration *initIndex = createTempInitDeclaration(node->getRight());
Jamie Madill1048e432016-07-23 18:51:28 -0400400 insertStatementInParentBlock(initIndex);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300401 mUsedTreeInsertion = true;
402
403 // Replace the index with the temp variable
404 TIntermSymbol *tempIndex = createTempSymbol(node->getRight()->getType());
Jamie Madill03d863c2016-07-27 18:15:53 -0400405 queueReplacementWithParent(node, node->getRight(), tempIndex, OriginalNode::IS_DROPPED);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300406 }
Jamie Madill666f65a2016-08-26 01:34:37 +0000407 else if (IntermNodePatternMatcher::IsDynamicIndexingOfVectorOrMatrix(node))
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300408 {
409 bool write = isLValueRequiredHere();
410
Jamie Madill666f65a2016-08-26 01:34:37 +0000411#if defined(ANGLE_ENABLE_ASSERTS)
412 // Make sure that IntermNodePatternMatcher is consistent with the slightly differently
413 // implemented checks in this traverser.
414 IntermNodePatternMatcher matcher(
415 IntermNodePatternMatcher::kDynamicIndexingOfVectorOrMatrixInLValue);
416 ASSERT(matcher.match(node, getParentNode(), isLValueRequiredHere()) == write);
417#endif
418
Olli Etuahofe486322017-03-21 09:30:54 +0000419 const TType &type = node->getLeft()->getType();
420 TSymbolUniqueId indexingFunctionId;
421 if (mIndexedVecAndMatrixTypes.find(type) == mIndexedVecAndMatrixTypes.end())
422 {
423 mIndexedVecAndMatrixTypes[type] = indexingFunctionId;
424 }
425 else
426 {
427 indexingFunctionId = mIndexedVecAndMatrixTypes[type];
428 }
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300429
430 if (write)
431 {
432 // Convert:
433 // v_expr[index_expr]++;
434 // to this:
435 // int s0 = index_expr; float s1 = dyn_index(v_expr, s0); s1++;
436 // dyn_index_write(v_expr, s0, s1);
437 // This works even if index_expr has some side effects.
438 if (node->getLeft()->hasSideEffects())
439 {
440 // If v_expr has side effects, those need to be removed before proceeding.
441 // Otherwise the side effects of v_expr would be evaluated twice.
442 // The only case where an l-value can have side effects is when it is
443 // indexing. For example, it can be V[j++] where V is an array of vectors.
444 mRemoveIndexSideEffectsInSubtree = true;
445 return true;
446 }
Olli Etuaho8f6eb2a2017-01-12 17:04:58 +0000447
448 TIntermBinary *leftBinary = node->getLeft()->getAsBinaryNode();
449 if (leftBinary != nullptr &&
450 IntermNodePatternMatcher::IsDynamicIndexingOfVectorOrMatrix(leftBinary))
451 {
452 // This is a case like:
453 // mat2 m;
454 // m[a][b]++;
455 // Process the child node m[a] first.
456 return true;
457 }
458
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300459 // TODO(oetuaho@nvidia.com): This is not optimal if the expression using the value
460 // only writes it and doesn't need the previous value. http://anglebug.com/1116
461
Olli Etuahofe486322017-03-21 09:30:54 +0000462 TSymbolUniqueId indexedWriteFunctionId;
463 if (mWrittenVecAndMatrixTypes.find(type) == mWrittenVecAndMatrixTypes.end())
464 {
465 mWrittenVecAndMatrixTypes[type] = indexedWriteFunctionId;
466 }
467 else
468 {
469 indexedWriteFunctionId = mWrittenVecAndMatrixTypes[type];
470 }
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300471 TType fieldType = GetFieldType(type);
472
473 TIntermSequence insertionsBefore;
474 TIntermSequence insertionsAfter;
475
476 // Store the index in a temporary signed int variable.
477 TIntermTyped *indexInitializer = EnsureSignedInt(node->getRight());
Olli Etuaho13389b62016-10-16 11:48:18 +0100478 TIntermDeclaration *initIndex = createTempInitDeclaration(indexInitializer);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300479 initIndex->setLine(node->getLine());
480 insertionsBefore.push_back(initIndex);
481
Olli Etuaho4dd06d52017-07-05 12:41:06 +0300482 // Create a node for referring to the index after the nextTemporaryId() call
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300483 // below.
484 TIntermSymbol *tempIndex = createTempSymbol(indexInitializer->getType());
485
Olli Etuahofe486322017-03-21 09:30:54 +0000486 TIntermAggregate *indexingCall =
487 CreateIndexFunctionCall(node, tempIndex, indexingFunctionId);
488
Olli Etuaho4dd06d52017-07-05 12:41:06 +0300489 nextTemporaryId(); // From now on, creating temporary symbols that refer to the
490 // field value.
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300491 insertionsBefore.push_back(createTempInitDeclaration(indexingCall));
492
Olli Etuahofe486322017-03-21 09:30:54 +0000493 TIntermAggregate *indexedWriteCall = CreateIndexedWriteFunctionCall(
494 node, tempIndex, createTempSymbol(fieldType), indexedWriteFunctionId);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300495 insertionsAfter.push_back(indexedWriteCall);
496 insertStatementsInParentBlock(insertionsBefore, insertionsAfter);
Olli Etuahoea39a222017-07-06 12:47:59 +0300497 queueReplacement(createTempSymbol(fieldType), OriginalNode::IS_DROPPED);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300498 mUsedTreeInsertion = true;
499 }
500 else
501 {
502 // The indexed value is not being written, so we can simply convert
503 // v_expr[index_expr]
504 // into
505 // dyn_index(v_expr, index_expr)
506 // If the index_expr is unsigned, we'll convert it to signed.
507 ASSERT(!mRemoveIndexSideEffectsInSubtree);
508 TIntermAggregate *indexingCall = CreateIndexFunctionCall(
Olli Etuahofe486322017-03-21 09:30:54 +0000509 node, EnsureSignedInt(node->getRight()), indexingFunctionId);
Olli Etuahoea39a222017-07-06 12:47:59 +0300510 queueReplacement(indexingCall, OriginalNode::IS_DROPPED);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300511 }
512 }
513 }
514 return !mUsedTreeInsertion;
515}
516
517void RemoveDynamicIndexingTraverser::nextIteration()
518{
519 mUsedTreeInsertion = false;
520 mRemoveIndexSideEffectsInSubtree = false;
Olli Etuaho4dd06d52017-07-05 12:41:06 +0300521 nextTemporaryId();
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300522}
523
524} // namespace
525
526void RemoveDynamicIndexing(TIntermNode *root,
Olli Etuaho4dd06d52017-07-05 12:41:06 +0300527 TSymbolUniqueId *temporaryId,
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300528 const TSymbolTable &symbolTable,
529 int shaderVersion)
530{
531 RemoveDynamicIndexingTraverser traverser(symbolTable, shaderVersion);
Olli Etuaho4dd06d52017-07-05 12:41:06 +0300532 ASSERT(temporaryId != nullptr);
533 traverser.useTemporaryId(temporaryId);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300534 do
535 {
536 traverser.nextIteration();
537 root->traverse(&traverser);
538 traverser.updateTree();
539 } while (traverser.usedTreeInsertion());
Olli Etuaho1ecd14b2017-01-26 13:54:15 -0800540 // TOOD(oetuaho@nvidia.com): It might be nicer to add the helper definitions also in the middle
541 // of traversal. Now the tree ends up in an inconsistent state in the middle, since there are
542 // function call nodes with no corresponding definition nodes. This needs special handling in
543 // TIntermLValueTrackingTraverser, and creates intricacies that are not easily apparent from a
544 // superficial reading of the code.
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300545 traverser.insertHelperDefinitions(root);
546 traverser.updateTree();
547}
Jamie Madill45bcc782016-11-07 13:58:48 -0500548
549} // namespace sh