blob: fbe0264abc66eb870d5eab8dc3174567ae4fa7bf [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
23TName GetIndexFunctionName(const TType &type, bool write)
24{
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 }
56 TString nameString = TFunction::mangleName(nameSink.c_str());
57 TName name(nameString);
Olli Etuaho1ecd14b2017-01-26 13:54:15 -080058 // TODO(oetuaho@nvidia.com): would be better to have the parameter types in the mangled name as
59 // well.
Olli Etuaho5d91dda2015-06-18 15:47:46 +030060 name.setInternal(true);
61 return name;
62}
63
64TIntermSymbol *CreateBaseSymbol(const TType &type, TQualifier qualifier)
65{
66 TIntermSymbol *symbol = new TIntermSymbol(0, "base", type);
67 symbol->setInternal(true);
68 symbol->getTypePointer()->setQualifier(qualifier);
69 return symbol;
70}
71
72TIntermSymbol *CreateIndexSymbol()
73{
74 TIntermSymbol *symbol = new TIntermSymbol(0, "index", TType(EbtInt, EbpHigh));
75 symbol->setInternal(true);
76 symbol->getTypePointer()->setQualifier(EvqIn);
77 return symbol;
78}
79
80TIntermSymbol *CreateValueSymbol(const TType &type)
81{
82 TIntermSymbol *symbol = new TIntermSymbol(0, "value", type);
83 symbol->setInternal(true);
84 symbol->getTypePointer()->setQualifier(EvqIn);
85 return symbol;
86}
87
88TIntermConstantUnion *CreateIntConstantNode(int i)
89{
90 TConstantUnion *constant = new TConstantUnion();
91 constant->setIConst(i);
92 return new TIntermConstantUnion(constant, TType(EbtInt, EbpHigh));
93}
94
95TIntermBinary *CreateIndexDirectBaseSymbolNode(const TType &indexedType,
96 const TType &fieldType,
97 const int index,
98 TQualifier baseQualifier)
99{
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300100 TIntermSymbol *baseSymbol = CreateBaseSymbol(indexedType, baseQualifier);
Olli Etuaho3272a6d2016-08-29 17:54:50 +0300101 TIntermBinary *indexNode =
102 new TIntermBinary(EOpIndexDirect, baseSymbol, TIntermTyped::CreateIndexNode(index));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300103 return indexNode;
104}
105
106TIntermBinary *CreateAssignValueSymbolNode(TIntermTyped *targetNode, const TType &assignedValueType)
107{
Olli Etuaho3272a6d2016-08-29 17:54:50 +0300108 return new TIntermBinary(EOpAssign, targetNode, CreateValueSymbol(assignedValueType));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300109}
110
111TIntermTyped *EnsureSignedInt(TIntermTyped *node)
112{
113 if (node->getBasicType() == EbtInt)
114 return node;
115
116 TIntermAggregate *convertedNode = new TIntermAggregate(EOpConstructInt);
117 convertedNode->setType(TType(EbtInt));
118 convertedNode->getSequence()->push_back(node);
119 convertedNode->setPrecisionFromChildren();
120 return convertedNode;
121}
122
123TType GetFieldType(const TType &indexedType)
124{
125 if (indexedType.isMatrix())
126 {
127 TType fieldType = TType(indexedType.getBasicType(), indexedType.getPrecision());
128 fieldType.setPrimarySize(static_cast<unsigned char>(indexedType.getRows()));
129 return fieldType;
130 }
131 else
132 {
133 return TType(indexedType.getBasicType(), indexedType.getPrecision());
134 }
135}
136
137// Generate a read or write function for one field in a vector/matrix.
138// Out-of-range indices are clamped. This is consistent with how ANGLE handles out-of-range
139// indices in other places.
140// Note that indices can be either int or uint. We create only int versions of the functions,
141// and convert uint indices to int at the call site.
142// read function example:
143// float dyn_index_vec2(in vec2 base, in int index)
144// {
145// switch(index)
146// {
147// case (0):
148// return base[0];
149// case (1):
150// return base[1];
151// default:
152// break;
153// }
154// if (index < 0)
155// return base[0];
156// return base[1];
157// }
158// write function example:
159// void dyn_index_write_vec2(inout vec2 base, in int index, in float value)
160// {
161// switch(index)
162// {
163// case (0):
164// base[0] = value;
165// return;
166// case (1):
167// base[1] = value;
168// return;
169// default:
170// break;
171// }
172// if (index < 0)
173// {
174// base[0] = value;
175// return;
176// }
177// base[1] = value;
178// }
179// Note that else is not used in above functions to avoid the RewriteElseBlocks transformation.
Olli Etuaho336b1472016-10-05 16:37:55 +0100180TIntermFunctionDefinition *GetIndexFunctionDefinition(TType type, bool write)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300181{
182 ASSERT(!type.isArray());
183 // Conservatively use highp here, even if the indexed type is not highp. That way the code can't
184 // end up using mediump version of an indexing function for a highp value, if both mediump and
185 // highp values are being indexed in the shader. For HLSL precision doesn't matter, but in
186 // principle this code could be used with multiple backends.
187 type.setPrecision(EbpHigh);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300188
189 TType fieldType = GetFieldType(type);
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500190 int numCases = 0;
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300191 if (type.isMatrix())
192 {
193 numCases = type.getCols();
194 }
195 else
196 {
197 numCases = type.getNominalSize();
198 }
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300199
Olli Etuaho8ad9e752017-01-16 19:55:20 +0000200 TIntermFunctionPrototype *prototypeNode = nullptr;
201 if (write)
202 {
203 prototypeNode = new TIntermFunctionPrototype(TType(EbtVoid));
204 }
205 else
206 {
207 prototypeNode = new TIntermFunctionPrototype(fieldType);
208 }
209 prototypeNode->getFunctionSymbolInfo()->setNameObj(GetIndexFunctionName(type, write));
210
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500211 TQualifier baseQualifier = EvqInOut;
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300212 if (!write)
213 baseQualifier = EvqIn;
214 TIntermSymbol *baseParam = CreateBaseSymbol(type, baseQualifier);
Olli Etuaho8ad9e752017-01-16 19:55:20 +0000215 prototypeNode->getSequence()->push_back(baseParam);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300216 TIntermSymbol *indexParam = CreateIndexSymbol();
Olli Etuaho8ad9e752017-01-16 19:55:20 +0000217 prototypeNode->getSequence()->push_back(indexParam);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300218 if (write)
219 {
220 TIntermSymbol *valueParam = CreateValueSymbol(fieldType);
Olli Etuaho8ad9e752017-01-16 19:55:20 +0000221 prototypeNode->getSequence()->push_back(valueParam);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300222 }
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300223
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100224 TIntermBlock *statementList = new TIntermBlock();
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300225 for (int i = 0; i < numCases; ++i)
226 {
227 TIntermCase *caseNode = new TIntermCase(CreateIntConstantNode(i));
228 statementList->getSequence()->push_back(caseNode);
229
230 TIntermBinary *indexNode =
231 CreateIndexDirectBaseSymbolNode(type, fieldType, i, baseQualifier);
232 if (write)
233 {
234 TIntermBinary *assignNode = CreateAssignValueSymbolNode(indexNode, fieldType);
235 statementList->getSequence()->push_back(assignNode);
236 TIntermBranch *returnNode = new TIntermBranch(EOpReturn, nullptr);
237 statementList->getSequence()->push_back(returnNode);
238 }
239 else
240 {
241 TIntermBranch *returnNode = new TIntermBranch(EOpReturn, indexNode);
242 statementList->getSequence()->push_back(returnNode);
243 }
244 }
245
246 // Default case
247 TIntermCase *defaultNode = new TIntermCase(nullptr);
248 statementList->getSequence()->push_back(defaultNode);
249 TIntermBranch *breakNode = new TIntermBranch(EOpBreak, nullptr);
250 statementList->getSequence()->push_back(breakNode);
251
252 TIntermSwitch *switchNode = new TIntermSwitch(CreateIndexSymbol(), statementList);
253
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100254 TIntermBlock *bodyNode = new TIntermBlock();
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300255 bodyNode->getSequence()->push_back(switchNode);
256
Olli Etuaho3272a6d2016-08-29 17:54:50 +0300257 TIntermBinary *cond =
258 new TIntermBinary(EOpLessThan, CreateIndexSymbol(), CreateIntConstantNode(0));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300259 cond->setType(TType(EbtBool, EbpUndefined));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300260
261 // Two blocks: one accesses (either reads or writes) the first element and returns,
262 // the other accesses the last element.
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100263 TIntermBlock *useFirstBlock = new TIntermBlock();
264 TIntermBlock *useLastBlock = new TIntermBlock();
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300265 TIntermBinary *indexFirstNode =
266 CreateIndexDirectBaseSymbolNode(type, fieldType, 0, baseQualifier);
267 TIntermBinary *indexLastNode =
268 CreateIndexDirectBaseSymbolNode(type, fieldType, numCases - 1, baseQualifier);
269 if (write)
270 {
271 TIntermBinary *assignFirstNode = CreateAssignValueSymbolNode(indexFirstNode, fieldType);
272 useFirstBlock->getSequence()->push_back(assignFirstNode);
273 TIntermBranch *returnNode = new TIntermBranch(EOpReturn, nullptr);
274 useFirstBlock->getSequence()->push_back(returnNode);
275
276 TIntermBinary *assignLastNode = CreateAssignValueSymbolNode(indexLastNode, fieldType);
277 useLastBlock->getSequence()->push_back(assignLastNode);
278 }
279 else
280 {
281 TIntermBranch *returnFirstNode = new TIntermBranch(EOpReturn, indexFirstNode);
282 useFirstBlock->getSequence()->push_back(returnFirstNode);
283
284 TIntermBranch *returnLastNode = new TIntermBranch(EOpReturn, indexLastNode);
285 useLastBlock->getSequence()->push_back(returnLastNode);
286 }
Olli Etuaho57961272016-09-14 13:57:46 +0300287 TIntermIfElse *ifNode = new TIntermIfElse(cond, useFirstBlock, nullptr);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300288 bodyNode->getSequence()->push_back(ifNode);
289 bodyNode->getSequence()->push_back(useLastBlock);
290
Olli Etuaho8ad9e752017-01-16 19:55:20 +0000291 TIntermFunctionDefinition *indexingFunction =
292 new TIntermFunctionDefinition(prototypeNode, bodyNode);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300293 return indexingFunction;
294}
295
296class RemoveDynamicIndexingTraverser : public TLValueTrackingTraverser
297{
298 public:
299 RemoveDynamicIndexingTraverser(const TSymbolTable &symbolTable, int shaderVersion);
300
301 bool visitBinary(Visit visit, TIntermBinary *node) override;
302
303 void insertHelperDefinitions(TIntermNode *root);
304
305 void nextIteration();
306
307 bool usedTreeInsertion() const { return mUsedTreeInsertion; }
308
309 protected:
310 // Sets of types that are indexed. Note that these can not store multiple variants
311 // of the same type with different precisions - only one precision gets stored.
312 std::set<TType> mIndexedVecAndMatrixTypes;
313 std::set<TType> mWrittenVecAndMatrixTypes;
314
315 bool mUsedTreeInsertion;
316
317 // When true, the traverser will remove side effects from any indexing expression.
318 // This is done so that in code like
319 // V[j++][i]++.
320 // where V is an array of vectors, j++ will only be evaluated once.
321 bool mRemoveIndexSideEffectsInSubtree;
322};
323
324RemoveDynamicIndexingTraverser::RemoveDynamicIndexingTraverser(const TSymbolTable &symbolTable,
325 int shaderVersion)
326 : TLValueTrackingTraverser(true, false, false, symbolTable, shaderVersion),
327 mUsedTreeInsertion(false),
328 mRemoveIndexSideEffectsInSubtree(false)
329{
330}
331
332void RemoveDynamicIndexingTraverser::insertHelperDefinitions(TIntermNode *root)
333{
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100334 TIntermBlock *rootBlock = root->getAsBlock();
335 ASSERT(rootBlock != nullptr);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300336 TIntermSequence insertions;
337 for (TType type : mIndexedVecAndMatrixTypes)
338 {
339 insertions.push_back(GetIndexFunctionDefinition(type, false));
340 }
341 for (TType type : mWrittenVecAndMatrixTypes)
342 {
343 insertions.push_back(GetIndexFunctionDefinition(type, true));
344 }
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100345 mInsertions.push_back(NodeInsertMultipleEntry(rootBlock, 0, insertions, TIntermSequence()));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300346}
347
348// Create a call to dyn_index_*() based on an indirect indexing op node
349TIntermAggregate *CreateIndexFunctionCall(TIntermBinary *node,
350 TIntermTyped *indexedNode,
351 TIntermTyped *index)
352{
353 ASSERT(node->getOp() == EOpIndexIndirect);
Olli Etuaho1ecd14b2017-01-26 13:54:15 -0800354 TIntermAggregate *indexingCall = new TIntermAggregate(EOpCallFunctionInAST);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300355 indexingCall->setLine(node->getLine());
Olli Etuahobd674552016-10-06 13:28:42 +0100356 indexingCall->getFunctionSymbolInfo()->setNameObj(
357 GetIndexFunctionName(indexedNode->getType(), false));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300358 indexingCall->getSequence()->push_back(indexedNode);
359 indexingCall->getSequence()->push_back(index);
360
361 TType fieldType = GetFieldType(indexedNode->getType());
362 indexingCall->setType(fieldType);
363 return indexingCall;
364}
365
366TIntermAggregate *CreateIndexedWriteFunctionCall(TIntermBinary *node,
367 TIntermTyped *index,
368 TIntermTyped *writtenValue)
369{
370 // Deep copy the left node so that two pointers to the same node don't end up in the tree.
371 TIntermNode *leftCopy = node->getLeft()->deepCopy();
372 ASSERT(leftCopy != nullptr && leftCopy->getAsTyped() != nullptr);
373 TIntermAggregate *indexedWriteCall =
374 CreateIndexFunctionCall(node, leftCopy->getAsTyped(), index);
Olli Etuahobd674552016-10-06 13:28:42 +0100375 indexedWriteCall->getFunctionSymbolInfo()->setNameObj(
376 GetIndexFunctionName(node->getLeft()->getType(), true));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300377 indexedWriteCall->setType(TType(EbtVoid));
378 indexedWriteCall->getSequence()->push_back(writtenValue);
379 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 Etuaho5d91dda2015-06-18 15:47:46 +0300419 TType type = node->getLeft()->getType();
420 mIndexedVecAndMatrixTypes.insert(type);
421
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
454 mWrittenVecAndMatrixTypes.insert(type);
455 TType fieldType = GetFieldType(type);
456
457 TIntermSequence insertionsBefore;
458 TIntermSequence insertionsAfter;
459
460 // Store the index in a temporary signed int variable.
461 TIntermTyped *indexInitializer = EnsureSignedInt(node->getRight());
Olli Etuaho13389b62016-10-16 11:48:18 +0100462 TIntermDeclaration *initIndex = createTempInitDeclaration(indexInitializer);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300463 initIndex->setLine(node->getLine());
464 insertionsBefore.push_back(initIndex);
465
466 TIntermAggregate *indexingCall = CreateIndexFunctionCall(
467 node, node->getLeft(), createTempSymbol(indexInitializer->getType()));
468
469 // Create a node for referring to the index after the nextTemporaryIndex() call
470 // below.
471 TIntermSymbol *tempIndex = createTempSymbol(indexInitializer->getType());
472
473 nextTemporaryIndex(); // From now on, creating temporary symbols that refer to the
474 // field value.
475 insertionsBefore.push_back(createTempInitDeclaration(indexingCall));
476
477 TIntermAggregate *indexedWriteCall =
478 CreateIndexedWriteFunctionCall(node, tempIndex, createTempSymbol(fieldType));
479 insertionsAfter.push_back(indexedWriteCall);
480 insertStatementsInParentBlock(insertionsBefore, insertionsAfter);
Jamie Madill03d863c2016-07-27 18:15:53 -0400481 queueReplacement(node, createTempSymbol(fieldType), OriginalNode::IS_DROPPED);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300482 mUsedTreeInsertion = true;
483 }
484 else
485 {
486 // The indexed value is not being written, so we can simply convert
487 // v_expr[index_expr]
488 // into
489 // dyn_index(v_expr, index_expr)
490 // If the index_expr is unsigned, we'll convert it to signed.
491 ASSERT(!mRemoveIndexSideEffectsInSubtree);
492 TIntermAggregate *indexingCall = CreateIndexFunctionCall(
493 node, node->getLeft(), EnsureSignedInt(node->getRight()));
Jamie Madill03d863c2016-07-27 18:15:53 -0400494 queueReplacement(node, indexingCall, OriginalNode::IS_DROPPED);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300495 }
496 }
497 }
498 return !mUsedTreeInsertion;
499}
500
501void RemoveDynamicIndexingTraverser::nextIteration()
502{
503 mUsedTreeInsertion = false;
504 mRemoveIndexSideEffectsInSubtree = false;
505 nextTemporaryIndex();
506}
507
508} // namespace
509
510void RemoveDynamicIndexing(TIntermNode *root,
511 unsigned int *temporaryIndex,
512 const TSymbolTable &symbolTable,
513 int shaderVersion)
514{
515 RemoveDynamicIndexingTraverser traverser(symbolTable, shaderVersion);
516 ASSERT(temporaryIndex != nullptr);
517 traverser.useTemporaryIndex(temporaryIndex);
518 do
519 {
520 traverser.nextIteration();
521 root->traverse(&traverser);
522 traverser.updateTree();
523 } while (traverser.usedTreeInsertion());
Olli Etuaho1ecd14b2017-01-26 13:54:15 -0800524 // TOOD(oetuaho@nvidia.com): It might be nicer to add the helper definitions also in the middle
525 // of traversal. Now the tree ends up in an inconsistent state in the middle, since there are
526 // function call nodes with no corresponding definition nodes. This needs special handling in
527 // TIntermLValueTrackingTraverser, and creates intricacies that are not easily apparent from a
528 // superficial reading of the code.
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300529 traverser.insertHelperDefinitions(root);
530 traverser.updateTree();
531}
Jamie Madill45bcc782016-11-07 13:58:48 -0500532
533} // namespace sh