blob: 02b1112ee91c08a6a9f332469914194439fbe330 [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 Etuahocccf2b02017-07-05 14:50:54 +030014#include "compiler/translator/IntermTraverse.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 Etuahoa22aa4e2017-05-24 18:17:23 +0300358 indexingCall->getFunctionSymbolInfo()->setKnownToNotHaveSideEffects(true);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300359 return indexingCall;
360}
361
362TIntermAggregate *CreateIndexedWriteFunctionCall(TIntermBinary *node,
363 TIntermTyped *index,
Olli Etuahofe486322017-03-21 09:30:54 +0000364 TIntermTyped *writtenValue,
365 const TSymbolUniqueId &functionId)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300366{
Olli Etuahofe486322017-03-21 09:30:54 +0000367 ASSERT(node->getOp() == EOpIndexIndirect);
368 TIntermSequence *arguments = new TIntermSequence();
369 // Deep copy the child nodes so that two pointers to the same node don't end up in the tree.
370 arguments->push_back(node->getLeft()->deepCopy());
371 arguments->push_back(index->deepCopy());
372 arguments->push_back(writtenValue);
373
374 std::string functionName = GetIndexFunctionName(node->getLeft()->getType(), true);
375 TIntermAggregate *indexedWriteCall = TIntermTraverser::CreateInternalFunctionCallNode(
376 TType(EbtVoid), functionName.c_str(), functionId, arguments);
377 indexedWriteCall->setLine(node->getLine());
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300378 return indexedWriteCall;
379}
380
381bool RemoveDynamicIndexingTraverser::visitBinary(Visit visit, TIntermBinary *node)
382{
383 if (mUsedTreeInsertion)
384 return false;
385
386 if (node->getOp() == EOpIndexIndirect)
387 {
388 if (mRemoveIndexSideEffectsInSubtree)
389 {
390 ASSERT(node->getRight()->hasSideEffects());
391 // In case we're just removing index side effects, convert
392 // v_expr[index_expr]
393 // to this:
394 // int s0 = index_expr; v_expr[s0];
395 // Now v_expr[s0] can be safely executed several times without unintended side effects.
396
397 // Init the temp variable holding the index
Olli Etuaho13389b62016-10-16 11:48:18 +0100398 TIntermDeclaration *initIndex = createTempInitDeclaration(node->getRight());
Jamie Madill1048e432016-07-23 18:51:28 -0400399 insertStatementInParentBlock(initIndex);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300400 mUsedTreeInsertion = true;
401
402 // Replace the index with the temp variable
403 TIntermSymbol *tempIndex = createTempSymbol(node->getRight()->getType());
Jamie Madill03d863c2016-07-27 18:15:53 -0400404 queueReplacementWithParent(node, node->getRight(), tempIndex, OriginalNode::IS_DROPPED);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300405 }
Jamie Madill666f65a2016-08-26 01:34:37 +0000406 else if (IntermNodePatternMatcher::IsDynamicIndexingOfVectorOrMatrix(node))
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300407 {
408 bool write = isLValueRequiredHere();
409
Jamie Madill666f65a2016-08-26 01:34:37 +0000410#if defined(ANGLE_ENABLE_ASSERTS)
411 // Make sure that IntermNodePatternMatcher is consistent with the slightly differently
412 // implemented checks in this traverser.
413 IntermNodePatternMatcher matcher(
414 IntermNodePatternMatcher::kDynamicIndexingOfVectorOrMatrixInLValue);
415 ASSERT(matcher.match(node, getParentNode(), isLValueRequiredHere()) == write);
416#endif
417
Olli Etuahofe486322017-03-21 09:30:54 +0000418 const TType &type = node->getLeft()->getType();
419 TSymbolUniqueId indexingFunctionId;
420 if (mIndexedVecAndMatrixTypes.find(type) == mIndexedVecAndMatrixTypes.end())
421 {
422 mIndexedVecAndMatrixTypes[type] = indexingFunctionId;
423 }
424 else
425 {
426 indexingFunctionId = mIndexedVecAndMatrixTypes[type];
427 }
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300428
429 if (write)
430 {
431 // Convert:
432 // v_expr[index_expr]++;
433 // to this:
434 // int s0 = index_expr; float s1 = dyn_index(v_expr, s0); s1++;
435 // dyn_index_write(v_expr, s0, s1);
436 // This works even if index_expr has some side effects.
437 if (node->getLeft()->hasSideEffects())
438 {
439 // If v_expr has side effects, those need to be removed before proceeding.
440 // Otherwise the side effects of v_expr would be evaluated twice.
441 // The only case where an l-value can have side effects is when it is
442 // indexing. For example, it can be V[j++] where V is an array of vectors.
443 mRemoveIndexSideEffectsInSubtree = true;
444 return true;
445 }
Olli Etuaho8f6eb2a2017-01-12 17:04:58 +0000446
447 TIntermBinary *leftBinary = node->getLeft()->getAsBinaryNode();
448 if (leftBinary != nullptr &&
449 IntermNodePatternMatcher::IsDynamicIndexingOfVectorOrMatrix(leftBinary))
450 {
451 // This is a case like:
452 // mat2 m;
453 // m[a][b]++;
454 // Process the child node m[a] first.
455 return true;
456 }
457
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300458 // TODO(oetuaho@nvidia.com): This is not optimal if the expression using the value
459 // only writes it and doesn't need the previous value. http://anglebug.com/1116
460
Olli Etuahofe486322017-03-21 09:30:54 +0000461 TSymbolUniqueId indexedWriteFunctionId;
462 if (mWrittenVecAndMatrixTypes.find(type) == mWrittenVecAndMatrixTypes.end())
463 {
464 mWrittenVecAndMatrixTypes[type] = indexedWriteFunctionId;
465 }
466 else
467 {
468 indexedWriteFunctionId = mWrittenVecAndMatrixTypes[type];
469 }
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300470 TType fieldType = GetFieldType(type);
471
472 TIntermSequence insertionsBefore;
473 TIntermSequence insertionsAfter;
474
475 // Store the index in a temporary signed int variable.
476 TIntermTyped *indexInitializer = EnsureSignedInt(node->getRight());
Olli Etuaho13389b62016-10-16 11:48:18 +0100477 TIntermDeclaration *initIndex = createTempInitDeclaration(indexInitializer);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300478 initIndex->setLine(node->getLine());
479 insertionsBefore.push_back(initIndex);
480
Olli Etuaho4dd06d52017-07-05 12:41:06 +0300481 // Create a node for referring to the index after the nextTemporaryId() call
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300482 // below.
483 TIntermSymbol *tempIndex = createTempSymbol(indexInitializer->getType());
484
Olli Etuahofe486322017-03-21 09:30:54 +0000485 TIntermAggregate *indexingCall =
486 CreateIndexFunctionCall(node, tempIndex, indexingFunctionId);
487
Olli Etuaho4dd06d52017-07-05 12:41:06 +0300488 nextTemporaryId(); // From now on, creating temporary symbols that refer to the
489 // field value.
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300490 insertionsBefore.push_back(createTempInitDeclaration(indexingCall));
491
Olli Etuahofe486322017-03-21 09:30:54 +0000492 TIntermAggregate *indexedWriteCall = CreateIndexedWriteFunctionCall(
493 node, tempIndex, createTempSymbol(fieldType), indexedWriteFunctionId);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300494 insertionsAfter.push_back(indexedWriteCall);
495 insertStatementsInParentBlock(insertionsBefore, insertionsAfter);
Olli Etuahoea39a222017-07-06 12:47:59 +0300496 queueReplacement(createTempSymbol(fieldType), OriginalNode::IS_DROPPED);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300497 mUsedTreeInsertion = true;
498 }
499 else
500 {
501 // The indexed value is not being written, so we can simply convert
502 // v_expr[index_expr]
503 // into
504 // dyn_index(v_expr, index_expr)
505 // If the index_expr is unsigned, we'll convert it to signed.
506 ASSERT(!mRemoveIndexSideEffectsInSubtree);
507 TIntermAggregate *indexingCall = CreateIndexFunctionCall(
Olli Etuahofe486322017-03-21 09:30:54 +0000508 node, EnsureSignedInt(node->getRight()), indexingFunctionId);
Olli Etuahoea39a222017-07-06 12:47:59 +0300509 queueReplacement(indexingCall, OriginalNode::IS_DROPPED);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300510 }
511 }
512 }
513 return !mUsedTreeInsertion;
514}
515
516void RemoveDynamicIndexingTraverser::nextIteration()
517{
518 mUsedTreeInsertion = false;
519 mRemoveIndexSideEffectsInSubtree = false;
Olli Etuaho4dd06d52017-07-05 12:41:06 +0300520 nextTemporaryId();
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300521}
522
523} // namespace
524
525void RemoveDynamicIndexing(TIntermNode *root,
Olli Etuaho4dd06d52017-07-05 12:41:06 +0300526 TSymbolUniqueId *temporaryId,
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300527 const TSymbolTable &symbolTable,
528 int shaderVersion)
529{
530 RemoveDynamicIndexingTraverser traverser(symbolTable, shaderVersion);
Olli Etuaho4dd06d52017-07-05 12:41:06 +0300531 ASSERT(temporaryId != nullptr);
532 traverser.useTemporaryId(temporaryId);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300533 do
534 {
535 traverser.nextIteration();
536 root->traverse(&traverser);
537 traverser.updateTree();
538 } while (traverser.usedTreeInsertion());
Olli Etuaho1ecd14b2017-01-26 13:54:15 -0800539 // TOOD(oetuaho@nvidia.com): It might be nicer to add the helper definitions also in the middle
540 // of traversal. Now the tree ends up in an inconsistent state in the middle, since there are
541 // function call nodes with no corresponding definition nodes. This needs special handling in
542 // TIntermLValueTrackingTraverser, and creates intricacies that are not easily apparent from a
543 // superficial reading of the code.
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300544 traverser.insertHelperDefinitions(root);
545 traverser.updateTree();
546}
Jamie Madill45bcc782016-11-07 13:58:48 -0500547
548} // namespace sh