blob: 5d136a327b8a0d8f28ca20224524f8c14f6e8580 [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"
13#include "compiler/translator/IntermNode.h"
Jamie Madill666f65a2016-08-26 01:34:37 +000014#include "compiler/translator/IntermNodePatternMatcher.h"
Olli Etuaho5d91dda2015-06-18 15:47:46 +030015#include "compiler/translator/SymbolTable.h"
16
Jamie Madill45bcc782016-11-07 13:58:48 -050017namespace sh
18{
19
Olli Etuaho5d91dda2015-06-18 15:47:46 +030020namespace
21{
22
Olli Etuahofe486322017-03-21 09:30:54 +000023std::string GetIndexFunctionName(const TType &type, bool write)
Olli Etuaho5d91dda2015-06-18 15:47:46 +030024{
25 TInfoSinkBase nameSink;
26 nameSink << "dyn_index_";
27 if (write)
28 {
29 nameSink << "write_";
30 }
31 if (type.isMatrix())
32 {
33 nameSink << "mat" << type.getCols() << "x" << type.getRows();
34 }
35 else
36 {
37 switch (type.getBasicType())
38 {
39 case EbtInt:
40 nameSink << "ivec";
41 break;
42 case EbtBool:
43 nameSink << "bvec";
44 break;
45 case EbtUInt:
46 nameSink << "uvec";
47 break;
48 case EbtFloat:
49 nameSink << "vec";
50 break;
51 default:
52 UNREACHABLE();
53 }
54 nameSink << type.getNominalSize();
55 }
Olli Etuahofe486322017-03-21 09:30:54 +000056 return nameSink.str();
Olli Etuaho5d91dda2015-06-18 15:47:46 +030057}
58
59TIntermSymbol *CreateBaseSymbol(const TType &type, TQualifier qualifier)
60{
61 TIntermSymbol *symbol = new TIntermSymbol(0, "base", type);
62 symbol->setInternal(true);
63 symbol->getTypePointer()->setQualifier(qualifier);
64 return symbol;
65}
66
67TIntermSymbol *CreateIndexSymbol()
68{
69 TIntermSymbol *symbol = new TIntermSymbol(0, "index", TType(EbtInt, EbpHigh));
70 symbol->setInternal(true);
71 symbol->getTypePointer()->setQualifier(EvqIn);
72 return symbol;
73}
74
75TIntermSymbol *CreateValueSymbol(const TType &type)
76{
77 TIntermSymbol *symbol = new TIntermSymbol(0, "value", type);
78 symbol->setInternal(true);
79 symbol->getTypePointer()->setQualifier(EvqIn);
80 return symbol;
81}
82
83TIntermConstantUnion *CreateIntConstantNode(int i)
84{
85 TConstantUnion *constant = new TConstantUnion();
86 constant->setIConst(i);
87 return new TIntermConstantUnion(constant, TType(EbtInt, EbpHigh));
88}
89
90TIntermBinary *CreateIndexDirectBaseSymbolNode(const TType &indexedType,
91 const TType &fieldType,
92 const int index,
93 TQualifier baseQualifier)
94{
Olli Etuaho5d91dda2015-06-18 15:47:46 +030095 TIntermSymbol *baseSymbol = CreateBaseSymbol(indexedType, baseQualifier);
Olli Etuaho3272a6d2016-08-29 17:54:50 +030096 TIntermBinary *indexNode =
97 new TIntermBinary(EOpIndexDirect, baseSymbol, TIntermTyped::CreateIndexNode(index));
Olli Etuaho5d91dda2015-06-18 15:47:46 +030098 return indexNode;
99}
100
101TIntermBinary *CreateAssignValueSymbolNode(TIntermTyped *targetNode, const TType &assignedValueType)
102{
Olli Etuaho3272a6d2016-08-29 17:54:50 +0300103 return new TIntermBinary(EOpAssign, targetNode, CreateValueSymbol(assignedValueType));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300104}
105
106TIntermTyped *EnsureSignedInt(TIntermTyped *node)
107{
108 if (node->getBasicType() == EbtInt)
109 return node;
110
Olli Etuahoaf6fc1b2017-01-26 17:45:35 -0800111 TIntermSequence *arguments = new TIntermSequence();
112 arguments->push_back(node);
Olli Etuahoa7ecec32017-05-08 17:43:55 +0300113 return TIntermAggregate::CreateConstructor(TType(EbtInt), arguments);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300114}
115
116TType GetFieldType(const TType &indexedType)
117{
118 if (indexedType.isMatrix())
119 {
120 TType fieldType = TType(indexedType.getBasicType(), indexedType.getPrecision());
121 fieldType.setPrimarySize(static_cast<unsigned char>(indexedType.getRows()));
122 return fieldType;
123 }
124 else
125 {
126 return TType(indexedType.getBasicType(), indexedType.getPrecision());
127 }
128}
129
130// Generate a read or write function for one field in a vector/matrix.
131// Out-of-range indices are clamped. This is consistent with how ANGLE handles out-of-range
132// indices in other places.
133// Note that indices can be either int or uint. We create only int versions of the functions,
134// and convert uint indices to int at the call site.
135// read function example:
136// float dyn_index_vec2(in vec2 base, in int index)
137// {
138// switch(index)
139// {
140// case (0):
141// return base[0];
142// case (1):
143// return base[1];
144// default:
145// break;
146// }
147// if (index < 0)
148// return base[0];
149// return base[1];
150// }
151// write function example:
152// void dyn_index_write_vec2(inout vec2 base, in int index, in float value)
153// {
154// switch(index)
155// {
156// case (0):
157// base[0] = value;
158// return;
159// case (1):
160// base[1] = value;
161// return;
162// default:
163// break;
164// }
165// if (index < 0)
166// {
167// base[0] = value;
168// return;
169// }
170// base[1] = value;
171// }
172// Note that else is not used in above functions to avoid the RewriteElseBlocks transformation.
Olli Etuahofe486322017-03-21 09:30:54 +0000173TIntermFunctionDefinition *GetIndexFunctionDefinition(TType type,
174 bool write,
175 const TSymbolUniqueId &functionId)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300176{
177 ASSERT(!type.isArray());
178 // Conservatively use highp here, even if the indexed type is not highp. That way the code can't
179 // end up using mediump version of an indexing function for a highp value, if both mediump and
180 // highp values are being indexed in the shader. For HLSL precision doesn't matter, but in
181 // principle this code could be used with multiple backends.
182 type.setPrecision(EbpHigh);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300183
184 TType fieldType = GetFieldType(type);
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500185 int numCases = 0;
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300186 if (type.isMatrix())
187 {
188 numCases = type.getCols();
189 }
190 else
191 {
192 numCases = type.getNominalSize();
193 }
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300194
Olli Etuahofe486322017-03-21 09:30:54 +0000195 TType returnType(EbtVoid);
196 if (!write)
Olli Etuaho8ad9e752017-01-16 19:55:20 +0000197 {
Olli Etuahofe486322017-03-21 09:30:54 +0000198 returnType = fieldType;
Olli Etuaho8ad9e752017-01-16 19:55:20 +0000199 }
Olli Etuahofe486322017-03-21 09:30:54 +0000200
201 std::string functionName = GetIndexFunctionName(type, write);
202 TIntermFunctionPrototype *prototypeNode = TIntermTraverser::CreateInternalFunctionPrototypeNode(
203 returnType, functionName.c_str(), functionId);
Olli Etuaho8ad9e752017-01-16 19:55:20 +0000204
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500205 TQualifier baseQualifier = EvqInOut;
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300206 if (!write)
207 baseQualifier = EvqIn;
208 TIntermSymbol *baseParam = CreateBaseSymbol(type, baseQualifier);
Olli Etuaho8ad9e752017-01-16 19:55:20 +0000209 prototypeNode->getSequence()->push_back(baseParam);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300210 TIntermSymbol *indexParam = CreateIndexSymbol();
Olli Etuaho8ad9e752017-01-16 19:55:20 +0000211 prototypeNode->getSequence()->push_back(indexParam);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300212 if (write)
213 {
214 TIntermSymbol *valueParam = CreateValueSymbol(fieldType);
Olli Etuaho8ad9e752017-01-16 19:55:20 +0000215 prototypeNode->getSequence()->push_back(valueParam);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300216 }
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300217
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100218 TIntermBlock *statementList = new TIntermBlock();
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300219 for (int i = 0; i < numCases; ++i)
220 {
221 TIntermCase *caseNode = new TIntermCase(CreateIntConstantNode(i));
222 statementList->getSequence()->push_back(caseNode);
223
224 TIntermBinary *indexNode =
225 CreateIndexDirectBaseSymbolNode(type, fieldType, i, baseQualifier);
226 if (write)
227 {
228 TIntermBinary *assignNode = CreateAssignValueSymbolNode(indexNode, fieldType);
229 statementList->getSequence()->push_back(assignNode);
230 TIntermBranch *returnNode = new TIntermBranch(EOpReturn, nullptr);
231 statementList->getSequence()->push_back(returnNode);
232 }
233 else
234 {
235 TIntermBranch *returnNode = new TIntermBranch(EOpReturn, indexNode);
236 statementList->getSequence()->push_back(returnNode);
237 }
238 }
239
240 // Default case
241 TIntermCase *defaultNode = new TIntermCase(nullptr);
242 statementList->getSequence()->push_back(defaultNode);
243 TIntermBranch *breakNode = new TIntermBranch(EOpBreak, nullptr);
244 statementList->getSequence()->push_back(breakNode);
245
246 TIntermSwitch *switchNode = new TIntermSwitch(CreateIndexSymbol(), statementList);
247
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100248 TIntermBlock *bodyNode = new TIntermBlock();
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300249 bodyNode->getSequence()->push_back(switchNode);
250
Olli Etuaho3272a6d2016-08-29 17:54:50 +0300251 TIntermBinary *cond =
252 new TIntermBinary(EOpLessThan, CreateIndexSymbol(), CreateIntConstantNode(0));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300253 cond->setType(TType(EbtBool, EbpUndefined));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300254
255 // Two blocks: one accesses (either reads or writes) the first element and returns,
256 // the other accesses the last element.
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100257 TIntermBlock *useFirstBlock = new TIntermBlock();
258 TIntermBlock *useLastBlock = new TIntermBlock();
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300259 TIntermBinary *indexFirstNode =
260 CreateIndexDirectBaseSymbolNode(type, fieldType, 0, baseQualifier);
261 TIntermBinary *indexLastNode =
262 CreateIndexDirectBaseSymbolNode(type, fieldType, numCases - 1, baseQualifier);
263 if (write)
264 {
265 TIntermBinary *assignFirstNode = CreateAssignValueSymbolNode(indexFirstNode, fieldType);
266 useFirstBlock->getSequence()->push_back(assignFirstNode);
267 TIntermBranch *returnNode = new TIntermBranch(EOpReturn, nullptr);
268 useFirstBlock->getSequence()->push_back(returnNode);
269
270 TIntermBinary *assignLastNode = CreateAssignValueSymbolNode(indexLastNode, fieldType);
271 useLastBlock->getSequence()->push_back(assignLastNode);
272 }
273 else
274 {
275 TIntermBranch *returnFirstNode = new TIntermBranch(EOpReturn, indexFirstNode);
276 useFirstBlock->getSequence()->push_back(returnFirstNode);
277
278 TIntermBranch *returnLastNode = new TIntermBranch(EOpReturn, indexLastNode);
279 useLastBlock->getSequence()->push_back(returnLastNode);
280 }
Olli Etuaho57961272016-09-14 13:57:46 +0300281 TIntermIfElse *ifNode = new TIntermIfElse(cond, useFirstBlock, nullptr);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300282 bodyNode->getSequence()->push_back(ifNode);
283 bodyNode->getSequence()->push_back(useLastBlock);
284
Olli Etuaho8ad9e752017-01-16 19:55:20 +0000285 TIntermFunctionDefinition *indexingFunction =
286 new TIntermFunctionDefinition(prototypeNode, bodyNode);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300287 return indexingFunction;
288}
289
290class RemoveDynamicIndexingTraverser : public TLValueTrackingTraverser
291{
292 public:
293 RemoveDynamicIndexingTraverser(const TSymbolTable &symbolTable, int shaderVersion);
294
295 bool visitBinary(Visit visit, TIntermBinary *node) override;
296
297 void insertHelperDefinitions(TIntermNode *root);
298
299 void nextIteration();
300
301 bool usedTreeInsertion() const { return mUsedTreeInsertion; }
302
303 protected:
Olli Etuahofe486322017-03-21 09:30:54 +0000304 // Maps of types that are indexed to the indexing function ids used for them. Note that these
305 // can not store multiple variants of the same type with different precisions - only one
306 // precision gets stored.
307 std::map<TType, TSymbolUniqueId> mIndexedVecAndMatrixTypes;
308 std::map<TType, TSymbolUniqueId> mWrittenVecAndMatrixTypes;
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300309
310 bool mUsedTreeInsertion;
311
312 // When true, the traverser will remove side effects from any indexing expression.
313 // This is done so that in code like
314 // V[j++][i]++.
315 // where V is an array of vectors, j++ will only be evaluated once.
316 bool mRemoveIndexSideEffectsInSubtree;
317};
318
319RemoveDynamicIndexingTraverser::RemoveDynamicIndexingTraverser(const TSymbolTable &symbolTable,
320 int shaderVersion)
321 : TLValueTrackingTraverser(true, false, false, symbolTable, shaderVersion),
322 mUsedTreeInsertion(false),
323 mRemoveIndexSideEffectsInSubtree(false)
324{
325}
326
327void RemoveDynamicIndexingTraverser::insertHelperDefinitions(TIntermNode *root)
328{
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100329 TIntermBlock *rootBlock = root->getAsBlock();
330 ASSERT(rootBlock != nullptr);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300331 TIntermSequence insertions;
Olli Etuahofe486322017-03-21 09:30:54 +0000332 for (auto &type : mIndexedVecAndMatrixTypes)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300333 {
Olli Etuahofe486322017-03-21 09:30:54 +0000334 insertions.push_back(GetIndexFunctionDefinition(type.first, false, type.second));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300335 }
Olli Etuahofe486322017-03-21 09:30:54 +0000336 for (auto &type : mWrittenVecAndMatrixTypes)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300337 {
Olli Etuahofe486322017-03-21 09:30:54 +0000338 insertions.push_back(GetIndexFunctionDefinition(type.first, true, type.second));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300339 }
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100340 mInsertions.push_back(NodeInsertMultipleEntry(rootBlock, 0, insertions, TIntermSequence()));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300341}
342
343// Create a call to dyn_index_*() based on an indirect indexing op node
344TIntermAggregate *CreateIndexFunctionCall(TIntermBinary *node,
Olli Etuahofe486322017-03-21 09:30:54 +0000345 TIntermTyped *index,
346 const TSymbolUniqueId &functionId)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300347{
348 ASSERT(node->getOp() == EOpIndexIndirect);
Olli Etuahoaf6fc1b2017-01-26 17:45:35 -0800349 TIntermSequence *arguments = new TIntermSequence();
Olli Etuahofe486322017-03-21 09:30:54 +0000350 arguments->push_back(node->getLeft());
Olli Etuahoaf6fc1b2017-01-26 17:45:35 -0800351 arguments->push_back(index);
352
Olli Etuahofe486322017-03-21 09:30:54 +0000353 TType fieldType = GetFieldType(node->getLeft()->getType());
354 std::string functionName = GetIndexFunctionName(node->getLeft()->getType(), false);
355 TIntermAggregate *indexingCall = TIntermTraverser::CreateInternalFunctionCallNode(
356 fieldType, functionName.c_str(), functionId, arguments);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300357 indexingCall->setLine(node->getLine());
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300358 return indexingCall;
359}
360
361TIntermAggregate *CreateIndexedWriteFunctionCall(TIntermBinary *node,
362 TIntermTyped *index,
Olli Etuahofe486322017-03-21 09:30:54 +0000363 TIntermTyped *writtenValue,
364 const TSymbolUniqueId &functionId)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300365{
Olli Etuahofe486322017-03-21 09:30:54 +0000366 ASSERT(node->getOp() == EOpIndexIndirect);
367 TIntermSequence *arguments = new TIntermSequence();
368 // Deep copy the child nodes so that two pointers to the same node don't end up in the tree.
369 arguments->push_back(node->getLeft()->deepCopy());
370 arguments->push_back(index->deepCopy());
371 arguments->push_back(writtenValue);
372
373 std::string functionName = GetIndexFunctionName(node->getLeft()->getType(), true);
374 TIntermAggregate *indexedWriteCall = TIntermTraverser::CreateInternalFunctionCallNode(
375 TType(EbtVoid), functionName.c_str(), functionId, arguments);
376 indexedWriteCall->setLine(node->getLine());
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300377 return indexedWriteCall;
378}
379
380bool RemoveDynamicIndexingTraverser::visitBinary(Visit visit, TIntermBinary *node)
381{
382 if (mUsedTreeInsertion)
383 return false;
384
385 if (node->getOp() == EOpIndexIndirect)
386 {
387 if (mRemoveIndexSideEffectsInSubtree)
388 {
389 ASSERT(node->getRight()->hasSideEffects());
390 // In case we're just removing index side effects, convert
391 // v_expr[index_expr]
392 // to this:
393 // int s0 = index_expr; v_expr[s0];
394 // Now v_expr[s0] can be safely executed several times without unintended side effects.
395
396 // Init the temp variable holding the index
Olli Etuaho13389b62016-10-16 11:48:18 +0100397 TIntermDeclaration *initIndex = createTempInitDeclaration(node->getRight());
Jamie Madill1048e432016-07-23 18:51:28 -0400398 insertStatementInParentBlock(initIndex);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300399 mUsedTreeInsertion = true;
400
401 // Replace the index with the temp variable
402 TIntermSymbol *tempIndex = createTempSymbol(node->getRight()->getType());
Jamie Madill03d863c2016-07-27 18:15:53 -0400403 queueReplacementWithParent(node, node->getRight(), tempIndex, OriginalNode::IS_DROPPED);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300404 }
Jamie Madill666f65a2016-08-26 01:34:37 +0000405 else if (IntermNodePatternMatcher::IsDynamicIndexingOfVectorOrMatrix(node))
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300406 {
407 bool write = isLValueRequiredHere();
408
Jamie Madill666f65a2016-08-26 01:34:37 +0000409#if defined(ANGLE_ENABLE_ASSERTS)
410 // Make sure that IntermNodePatternMatcher is consistent with the slightly differently
411 // implemented checks in this traverser.
412 IntermNodePatternMatcher matcher(
413 IntermNodePatternMatcher::kDynamicIndexingOfVectorOrMatrixInLValue);
414 ASSERT(matcher.match(node, getParentNode(), isLValueRequiredHere()) == write);
415#endif
416
Olli Etuahofe486322017-03-21 09:30:54 +0000417 const TType &type = node->getLeft()->getType();
418 TSymbolUniqueId indexingFunctionId;
419 if (mIndexedVecAndMatrixTypes.find(type) == mIndexedVecAndMatrixTypes.end())
420 {
421 mIndexedVecAndMatrixTypes[type] = indexingFunctionId;
422 }
423 else
424 {
425 indexingFunctionId = mIndexedVecAndMatrixTypes[type];
426 }
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300427
428 if (write)
429 {
430 // Convert:
431 // v_expr[index_expr]++;
432 // to this:
433 // int s0 = index_expr; float s1 = dyn_index(v_expr, s0); s1++;
434 // dyn_index_write(v_expr, s0, s1);
435 // This works even if index_expr has some side effects.
436 if (node->getLeft()->hasSideEffects())
437 {
438 // If v_expr has side effects, those need to be removed before proceeding.
439 // Otherwise the side effects of v_expr would be evaluated twice.
440 // The only case where an l-value can have side effects is when it is
441 // indexing. For example, it can be V[j++] where V is an array of vectors.
442 mRemoveIndexSideEffectsInSubtree = true;
443 return true;
444 }
Olli Etuaho8f6eb2a2017-01-12 17:04:58 +0000445
446 TIntermBinary *leftBinary = node->getLeft()->getAsBinaryNode();
447 if (leftBinary != nullptr &&
448 IntermNodePatternMatcher::IsDynamicIndexingOfVectorOrMatrix(leftBinary))
449 {
450 // This is a case like:
451 // mat2 m;
452 // m[a][b]++;
453 // Process the child node m[a] first.
454 return true;
455 }
456
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300457 // TODO(oetuaho@nvidia.com): This is not optimal if the expression using the value
458 // only writes it and doesn't need the previous value. http://anglebug.com/1116
459
Olli Etuahofe486322017-03-21 09:30:54 +0000460 TSymbolUniqueId indexedWriteFunctionId;
461 if (mWrittenVecAndMatrixTypes.find(type) == mWrittenVecAndMatrixTypes.end())
462 {
463 mWrittenVecAndMatrixTypes[type] = indexedWriteFunctionId;
464 }
465 else
466 {
467 indexedWriteFunctionId = mWrittenVecAndMatrixTypes[type];
468 }
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300469 TType fieldType = GetFieldType(type);
470
471 TIntermSequence insertionsBefore;
472 TIntermSequence insertionsAfter;
473
474 // Store the index in a temporary signed int variable.
475 TIntermTyped *indexInitializer = EnsureSignedInt(node->getRight());
Olli Etuaho13389b62016-10-16 11:48:18 +0100476 TIntermDeclaration *initIndex = createTempInitDeclaration(indexInitializer);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300477 initIndex->setLine(node->getLine());
478 insertionsBefore.push_back(initIndex);
479
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300480 // Create a node for referring to the index after the nextTemporaryIndex() call
481 // below.
482 TIntermSymbol *tempIndex = createTempSymbol(indexInitializer->getType());
483
Olli Etuahofe486322017-03-21 09:30:54 +0000484 TIntermAggregate *indexingCall =
485 CreateIndexFunctionCall(node, tempIndex, indexingFunctionId);
486
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300487 nextTemporaryIndex(); // From now on, creating temporary symbols that refer to the
488 // field value.
489 insertionsBefore.push_back(createTempInitDeclaration(indexingCall));
490
Olli Etuahofe486322017-03-21 09:30:54 +0000491 TIntermAggregate *indexedWriteCall = CreateIndexedWriteFunctionCall(
492 node, tempIndex, createTempSymbol(fieldType), indexedWriteFunctionId);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300493 insertionsAfter.push_back(indexedWriteCall);
494 insertStatementsInParentBlock(insertionsBefore, insertionsAfter);
Jamie Madill03d863c2016-07-27 18:15:53 -0400495 queueReplacement(node, createTempSymbol(fieldType), OriginalNode::IS_DROPPED);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300496 mUsedTreeInsertion = true;
497 }
498 else
499 {
500 // The indexed value is not being written, so we can simply convert
501 // v_expr[index_expr]
502 // into
503 // dyn_index(v_expr, index_expr)
504 // If the index_expr is unsigned, we'll convert it to signed.
505 ASSERT(!mRemoveIndexSideEffectsInSubtree);
506 TIntermAggregate *indexingCall = CreateIndexFunctionCall(
Olli Etuahofe486322017-03-21 09:30:54 +0000507 node, EnsureSignedInt(node->getRight()), indexingFunctionId);
Jamie Madill03d863c2016-07-27 18:15:53 -0400508 queueReplacement(node, indexingCall, OriginalNode::IS_DROPPED);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300509 }
510 }
511 }
512 return !mUsedTreeInsertion;
513}
514
515void RemoveDynamicIndexingTraverser::nextIteration()
516{
517 mUsedTreeInsertion = false;
518 mRemoveIndexSideEffectsInSubtree = false;
519 nextTemporaryIndex();
520}
521
522} // namespace
523
524void RemoveDynamicIndexing(TIntermNode *root,
525 unsigned int *temporaryIndex,
526 const TSymbolTable &symbolTable,
527 int shaderVersion)
528{
529 RemoveDynamicIndexingTraverser traverser(symbolTable, shaderVersion);
530 ASSERT(temporaryIndex != nullptr);
531 traverser.useTemporaryIndex(temporaryIndex);
532 do
533 {
534 traverser.nextIteration();
535 root->traverse(&traverser);
536 traverser.updateTree();
537 } while (traverser.usedTreeInsertion());
Olli Etuaho1ecd14b2017-01-26 13:54:15 -0800538 // TOOD(oetuaho@nvidia.com): It might be nicer to add the helper definitions also in the middle
539 // of traversal. Now the tree ends up in an inconsistent state in the middle, since there are
540 // function call nodes with no corresponding definition nodes. This needs special handling in
541 // TIntermLValueTrackingTraverser, and creates intricacies that are not easily apparent from a
542 // superficial reading of the code.
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300543 traverser.insertHelperDefinitions(root);
544 traverser.updateTree();
545}
Jamie Madill45bcc782016-11-07 13:58:48 -0500546
547} // namespace sh