blob: 3da4bcce8bed9e401d45e4da91ef88e5dc67e626 [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
17namespace
18{
19
20TName GetIndexFunctionName(const TType &type, bool write)
21{
22 TInfoSinkBase nameSink;
23 nameSink << "dyn_index_";
24 if (write)
25 {
26 nameSink << "write_";
27 }
28 if (type.isMatrix())
29 {
30 nameSink << "mat" << type.getCols() << "x" << type.getRows();
31 }
32 else
33 {
34 switch (type.getBasicType())
35 {
36 case EbtInt:
37 nameSink << "ivec";
38 break;
39 case EbtBool:
40 nameSink << "bvec";
41 break;
42 case EbtUInt:
43 nameSink << "uvec";
44 break;
45 case EbtFloat:
46 nameSink << "vec";
47 break;
48 default:
49 UNREACHABLE();
50 }
51 nameSink << type.getNominalSize();
52 }
53 TString nameString = TFunction::mangleName(nameSink.c_str());
54 TName name(nameString);
55 name.setInternal(true);
56 return name;
57}
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
111 TIntermAggregate *convertedNode = new TIntermAggregate(EOpConstructInt);
112 convertedNode->setType(TType(EbtInt));
113 convertedNode->getSequence()->push_back(node);
114 convertedNode->setPrecisionFromChildren();
115 return convertedNode;
116}
117
118TType GetFieldType(const TType &indexedType)
119{
120 if (indexedType.isMatrix())
121 {
122 TType fieldType = TType(indexedType.getBasicType(), indexedType.getPrecision());
123 fieldType.setPrimarySize(static_cast<unsigned char>(indexedType.getRows()));
124 return fieldType;
125 }
126 else
127 {
128 return TType(indexedType.getBasicType(), indexedType.getPrecision());
129 }
130}
131
132// Generate a read or write function for one field in a vector/matrix.
133// Out-of-range indices are clamped. This is consistent with how ANGLE handles out-of-range
134// indices in other places.
135// Note that indices can be either int or uint. We create only int versions of the functions,
136// and convert uint indices to int at the call site.
137// read function example:
138// float dyn_index_vec2(in vec2 base, in int index)
139// {
140// switch(index)
141// {
142// case (0):
143// return base[0];
144// case (1):
145// return base[1];
146// default:
147// break;
148// }
149// if (index < 0)
150// return base[0];
151// return base[1];
152// }
153// write function example:
154// void dyn_index_write_vec2(inout vec2 base, in int index, in float value)
155// {
156// switch(index)
157// {
158// case (0):
159// base[0] = value;
160// return;
161// case (1):
162// base[1] = value;
163// return;
164// default:
165// break;
166// }
167// if (index < 0)
168// {
169// base[0] = value;
170// return;
171// }
172// base[1] = value;
173// }
174// Note that else is not used in above functions to avoid the RewriteElseBlocks transformation.
175TIntermAggregate *GetIndexFunctionDefinition(TType type, bool write)
176{
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);
183 TIntermAggregate *indexingFunction = new TIntermAggregate(EOpFunction);
184 indexingFunction->setNameObj(GetIndexFunctionName(type, write));
185
186 TType fieldType = GetFieldType(type);
187 int numCases = 0;
188 if (type.isMatrix())
189 {
190 numCases = type.getCols();
191 }
192 else
193 {
194 numCases = type.getNominalSize();
195 }
196 if (write)
197 {
198 indexingFunction->setType(TType(EbtVoid));
199 }
200 else
201 {
202 indexingFunction->setType(fieldType);
203 }
204
205 TIntermAggregate *paramsNode = new TIntermAggregate(EOpParameters);
206 TQualifier baseQualifier = EvqInOut;
207 if (!write)
208 baseQualifier = EvqIn;
209 TIntermSymbol *baseParam = CreateBaseSymbol(type, baseQualifier);
210 paramsNode->getSequence()->push_back(baseParam);
211 TIntermSymbol *indexParam = CreateIndexSymbol();
212 paramsNode->getSequence()->push_back(indexParam);
213 if (write)
214 {
215 TIntermSymbol *valueParam = CreateValueSymbol(fieldType);
216 paramsNode->getSequence()->push_back(valueParam);
217 }
218 indexingFunction->getSequence()->push_back(paramsNode);
219
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100220 TIntermBlock *statementList = new TIntermBlock();
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300221 for (int i = 0; i < numCases; ++i)
222 {
223 TIntermCase *caseNode = new TIntermCase(CreateIntConstantNode(i));
224 statementList->getSequence()->push_back(caseNode);
225
226 TIntermBinary *indexNode =
227 CreateIndexDirectBaseSymbolNode(type, fieldType, i, baseQualifier);
228 if (write)
229 {
230 TIntermBinary *assignNode = CreateAssignValueSymbolNode(indexNode, fieldType);
231 statementList->getSequence()->push_back(assignNode);
232 TIntermBranch *returnNode = new TIntermBranch(EOpReturn, nullptr);
233 statementList->getSequence()->push_back(returnNode);
234 }
235 else
236 {
237 TIntermBranch *returnNode = new TIntermBranch(EOpReturn, indexNode);
238 statementList->getSequence()->push_back(returnNode);
239 }
240 }
241
242 // Default case
243 TIntermCase *defaultNode = new TIntermCase(nullptr);
244 statementList->getSequence()->push_back(defaultNode);
245 TIntermBranch *breakNode = new TIntermBranch(EOpBreak, nullptr);
246 statementList->getSequence()->push_back(breakNode);
247
248 TIntermSwitch *switchNode = new TIntermSwitch(CreateIndexSymbol(), statementList);
249
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100250 TIntermBlock *bodyNode = new TIntermBlock();
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300251 bodyNode->getSequence()->push_back(switchNode);
252
Olli Etuaho3272a6d2016-08-29 17:54:50 +0300253 TIntermBinary *cond =
254 new TIntermBinary(EOpLessThan, CreateIndexSymbol(), CreateIntConstantNode(0));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300255 cond->setType(TType(EbtBool, EbpUndefined));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300256
257 // Two blocks: one accesses (either reads or writes) the first element and returns,
258 // the other accesses the last element.
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100259 TIntermBlock *useFirstBlock = new TIntermBlock();
260 TIntermBlock *useLastBlock = new TIntermBlock();
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300261 TIntermBinary *indexFirstNode =
262 CreateIndexDirectBaseSymbolNode(type, fieldType, 0, baseQualifier);
263 TIntermBinary *indexLastNode =
264 CreateIndexDirectBaseSymbolNode(type, fieldType, numCases - 1, baseQualifier);
265 if (write)
266 {
267 TIntermBinary *assignFirstNode = CreateAssignValueSymbolNode(indexFirstNode, fieldType);
268 useFirstBlock->getSequence()->push_back(assignFirstNode);
269 TIntermBranch *returnNode = new TIntermBranch(EOpReturn, nullptr);
270 useFirstBlock->getSequence()->push_back(returnNode);
271
272 TIntermBinary *assignLastNode = CreateAssignValueSymbolNode(indexLastNode, fieldType);
273 useLastBlock->getSequence()->push_back(assignLastNode);
274 }
275 else
276 {
277 TIntermBranch *returnFirstNode = new TIntermBranch(EOpReturn, indexFirstNode);
278 useFirstBlock->getSequence()->push_back(returnFirstNode);
279
280 TIntermBranch *returnLastNode = new TIntermBranch(EOpReturn, indexLastNode);
281 useLastBlock->getSequence()->push_back(returnLastNode);
282 }
Olli Etuaho57961272016-09-14 13:57:46 +0300283 TIntermIfElse *ifNode = new TIntermIfElse(cond, useFirstBlock, nullptr);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300284 bodyNode->getSequence()->push_back(ifNode);
285 bodyNode->getSequence()->push_back(useLastBlock);
286
287 indexingFunction->getSequence()->push_back(bodyNode);
288
289 return indexingFunction;
290}
291
292class RemoveDynamicIndexingTraverser : public TLValueTrackingTraverser
293{
294 public:
295 RemoveDynamicIndexingTraverser(const TSymbolTable &symbolTable, int shaderVersion);
296
297 bool visitBinary(Visit visit, TIntermBinary *node) override;
298
299 void insertHelperDefinitions(TIntermNode *root);
300
301 void nextIteration();
302
303 bool usedTreeInsertion() const { return mUsedTreeInsertion; }
304
305 protected:
306 // Sets of types that are indexed. Note that these can not store multiple variants
307 // of the same type with different precisions - only one precision gets stored.
308 std::set<TType> mIndexedVecAndMatrixTypes;
309 std::set<TType> mWrittenVecAndMatrixTypes;
310
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;
333 for (TType type : mIndexedVecAndMatrixTypes)
334 {
335 insertions.push_back(GetIndexFunctionDefinition(type, false));
336 }
337 for (TType type : mWrittenVecAndMatrixTypes)
338 {
339 insertions.push_back(GetIndexFunctionDefinition(type, true));
340 }
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,
346 TIntermTyped *indexedNode,
347 TIntermTyped *index)
348{
349 ASSERT(node->getOp() == EOpIndexIndirect);
350 TIntermAggregate *indexingCall = new TIntermAggregate(EOpFunctionCall);
351 indexingCall->setLine(node->getLine());
352 indexingCall->setUserDefined();
353 indexingCall->setNameObj(GetIndexFunctionName(indexedNode->getType(), false));
354 indexingCall->getSequence()->push_back(indexedNode);
355 indexingCall->getSequence()->push_back(index);
356
357 TType fieldType = GetFieldType(indexedNode->getType());
358 indexingCall->setType(fieldType);
359 return indexingCall;
360}
361
362TIntermAggregate *CreateIndexedWriteFunctionCall(TIntermBinary *node,
363 TIntermTyped *index,
364 TIntermTyped *writtenValue)
365{
366 // Deep copy the left node so that two pointers to the same node don't end up in the tree.
367 TIntermNode *leftCopy = node->getLeft()->deepCopy();
368 ASSERT(leftCopy != nullptr && leftCopy->getAsTyped() != nullptr);
369 TIntermAggregate *indexedWriteCall =
370 CreateIndexFunctionCall(node, leftCopy->getAsTyped(), index);
371 indexedWriteCall->setNameObj(GetIndexFunctionName(node->getLeft()->getType(), true));
372 indexedWriteCall->setType(TType(EbtVoid));
373 indexedWriteCall->getSequence()->push_back(writtenValue);
374 return indexedWriteCall;
375}
376
377bool RemoveDynamicIndexingTraverser::visitBinary(Visit visit, TIntermBinary *node)
378{
379 if (mUsedTreeInsertion)
380 return false;
381
382 if (node->getOp() == EOpIndexIndirect)
383 {
384 if (mRemoveIndexSideEffectsInSubtree)
385 {
386 ASSERT(node->getRight()->hasSideEffects());
387 // In case we're just removing index side effects, convert
388 // v_expr[index_expr]
389 // to this:
390 // int s0 = index_expr; v_expr[s0];
391 // Now v_expr[s0] can be safely executed several times without unintended side effects.
392
393 // Init the temp variable holding the index
394 TIntermAggregate *initIndex = createTempInitDeclaration(node->getRight());
Jamie Madill1048e432016-07-23 18:51:28 -0400395 insertStatementInParentBlock(initIndex);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300396 mUsedTreeInsertion = true;
397
398 // Replace the index with the temp variable
399 TIntermSymbol *tempIndex = createTempSymbol(node->getRight()->getType());
Jamie Madill03d863c2016-07-27 18:15:53 -0400400 queueReplacementWithParent(node, node->getRight(), tempIndex, OriginalNode::IS_DROPPED);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300401 }
Jamie Madill666f65a2016-08-26 01:34:37 +0000402 else if (IntermNodePatternMatcher::IsDynamicIndexingOfVectorOrMatrix(node))
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300403 {
404 bool write = isLValueRequiredHere();
405
Jamie Madill666f65a2016-08-26 01:34:37 +0000406#if defined(ANGLE_ENABLE_ASSERTS)
407 // Make sure that IntermNodePatternMatcher is consistent with the slightly differently
408 // implemented checks in this traverser.
409 IntermNodePatternMatcher matcher(
410 IntermNodePatternMatcher::kDynamicIndexingOfVectorOrMatrixInLValue);
411 ASSERT(matcher.match(node, getParentNode(), isLValueRequiredHere()) == write);
412#endif
413
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300414 TType type = node->getLeft()->getType();
415 mIndexedVecAndMatrixTypes.insert(type);
416
417 if (write)
418 {
419 // Convert:
420 // v_expr[index_expr]++;
421 // to this:
422 // int s0 = index_expr; float s1 = dyn_index(v_expr, s0); s1++;
423 // dyn_index_write(v_expr, s0, s1);
424 // This works even if index_expr has some side effects.
425 if (node->getLeft()->hasSideEffects())
426 {
427 // If v_expr has side effects, those need to be removed before proceeding.
428 // Otherwise the side effects of v_expr would be evaluated twice.
429 // The only case where an l-value can have side effects is when it is
430 // indexing. For example, it can be V[j++] where V is an array of vectors.
431 mRemoveIndexSideEffectsInSubtree = true;
432 return true;
433 }
434 // TODO(oetuaho@nvidia.com): This is not optimal if the expression using the value
435 // only writes it and doesn't need the previous value. http://anglebug.com/1116
436
437 mWrittenVecAndMatrixTypes.insert(type);
438 TType fieldType = GetFieldType(type);
439
440 TIntermSequence insertionsBefore;
441 TIntermSequence insertionsAfter;
442
443 // Store the index in a temporary signed int variable.
444 TIntermTyped *indexInitializer = EnsureSignedInt(node->getRight());
445 TIntermAggregate *initIndex = createTempInitDeclaration(indexInitializer);
446 initIndex->setLine(node->getLine());
447 insertionsBefore.push_back(initIndex);
448
449 TIntermAggregate *indexingCall = CreateIndexFunctionCall(
450 node, node->getLeft(), createTempSymbol(indexInitializer->getType()));
451
452 // Create a node for referring to the index after the nextTemporaryIndex() call
453 // below.
454 TIntermSymbol *tempIndex = createTempSymbol(indexInitializer->getType());
455
456 nextTemporaryIndex(); // From now on, creating temporary symbols that refer to the
457 // field value.
458 insertionsBefore.push_back(createTempInitDeclaration(indexingCall));
459
460 TIntermAggregate *indexedWriteCall =
461 CreateIndexedWriteFunctionCall(node, tempIndex, createTempSymbol(fieldType));
462 insertionsAfter.push_back(indexedWriteCall);
463 insertStatementsInParentBlock(insertionsBefore, insertionsAfter);
Jamie Madill03d863c2016-07-27 18:15:53 -0400464 queueReplacement(node, createTempSymbol(fieldType), OriginalNode::IS_DROPPED);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300465 mUsedTreeInsertion = true;
466 }
467 else
468 {
469 // The indexed value is not being written, so we can simply convert
470 // v_expr[index_expr]
471 // into
472 // dyn_index(v_expr, index_expr)
473 // If the index_expr is unsigned, we'll convert it to signed.
474 ASSERT(!mRemoveIndexSideEffectsInSubtree);
475 TIntermAggregate *indexingCall = CreateIndexFunctionCall(
476 node, node->getLeft(), EnsureSignedInt(node->getRight()));
Jamie Madill03d863c2016-07-27 18:15:53 -0400477 queueReplacement(node, indexingCall, OriginalNode::IS_DROPPED);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300478 }
479 }
480 }
481 return !mUsedTreeInsertion;
482}
483
484void RemoveDynamicIndexingTraverser::nextIteration()
485{
486 mUsedTreeInsertion = false;
487 mRemoveIndexSideEffectsInSubtree = false;
488 nextTemporaryIndex();
489}
490
491} // namespace
492
493void RemoveDynamicIndexing(TIntermNode *root,
494 unsigned int *temporaryIndex,
495 const TSymbolTable &symbolTable,
496 int shaderVersion)
497{
498 RemoveDynamicIndexingTraverser traverser(symbolTable, shaderVersion);
499 ASSERT(temporaryIndex != nullptr);
500 traverser.useTemporaryIndex(temporaryIndex);
501 do
502 {
503 traverser.nextIteration();
504 root->traverse(&traverser);
505 traverser.updateTree();
506 } while (traverser.usedTreeInsertion());
507 traverser.insertHelperDefinitions(root);
508 traverser.updateTree();
509}