blob: 5fda43f3749f2468eadf4f7be0966fd7001e3328 [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
Olli Etuaho89a69a02017-10-23 12:20:45 +030012#include "compiler/translator/Diagnostics.h"
Olli Etuaho5d91dda2015-06-18 15:47:46 +030013#include "compiler/translator/InfoSink.h"
Jamie Madill666f65a2016-08-26 01:34:37 +000014#include "compiler/translator/IntermNodePatternMatcher.h"
Olli Etuaho3ec75682017-07-05 17:02:55 +030015#include "compiler/translator/IntermNode_util.h"
Olli Etuahocccf2b02017-07-05 14:50:54 +030016#include "compiler/translator/IntermTraverse.h"
Olli Etuaho5d91dda2015-06-18 15:47:46 +030017#include "compiler/translator/SymbolTable.h"
18
Jamie Madill45bcc782016-11-07 13:58:48 -050019namespace sh
20{
21
Olli Etuaho5d91dda2015-06-18 15:47:46 +030022namespace
23{
24
Olli Etuahofe486322017-03-21 09:30:54 +000025std::string GetIndexFunctionName(const TType &type, bool write)
Olli Etuaho5d91dda2015-06-18 15:47:46 +030026{
27 TInfoSinkBase nameSink;
28 nameSink << "dyn_index_";
29 if (write)
30 {
31 nameSink << "write_";
32 }
33 if (type.isMatrix())
34 {
35 nameSink << "mat" << type.getCols() << "x" << type.getRows();
36 }
37 else
38 {
39 switch (type.getBasicType())
40 {
41 case EbtInt:
42 nameSink << "ivec";
43 break;
44 case EbtBool:
45 nameSink << "bvec";
46 break;
47 case EbtUInt:
48 nameSink << "uvec";
49 break;
50 case EbtFloat:
51 nameSink << "vec";
52 break;
53 default:
54 UNREACHABLE();
55 }
56 nameSink << type.getNominalSize();
57 }
Olli Etuahofe486322017-03-21 09:30:54 +000058 return nameSink.str();
Olli Etuaho5d91dda2015-06-18 15:47:46 +030059}
60
Olli Etuahob79b3f92017-07-21 14:23:21 +030061TIntermSymbol *CreateBaseSymbol(const TType &type, TQualifier qualifier, TSymbolTable *symbolTable)
Olli Etuaho5d91dda2015-06-18 15:47:46 +030062{
Olli Etuahob79b3f92017-07-21 14:23:21 +030063 TIntermSymbol *symbol = new TIntermSymbol(symbolTable->nextUniqueId(), "base", type);
Olli Etuaho5d91dda2015-06-18 15:47:46 +030064 symbol->setInternal(true);
65 symbol->getTypePointer()->setQualifier(qualifier);
66 return symbol;
67}
68
Olli Etuahob79b3f92017-07-21 14:23:21 +030069TIntermSymbol *CreateIndexSymbol(TSymbolTable *symbolTable)
Olli Etuaho5d91dda2015-06-18 15:47:46 +030070{
Olli Etuahob79b3f92017-07-21 14:23:21 +030071 TIntermSymbol *symbol =
72 new TIntermSymbol(symbolTable->nextUniqueId(), "index", TType(EbtInt, EbpHigh));
Olli Etuaho5d91dda2015-06-18 15:47:46 +030073 symbol->setInternal(true);
74 symbol->getTypePointer()->setQualifier(EvqIn);
75 return symbol;
76}
77
Olli Etuahob79b3f92017-07-21 14:23:21 +030078TIntermSymbol *CreateValueSymbol(const TType &type, TSymbolTable *symbolTable)
Olli Etuaho5d91dda2015-06-18 15:47:46 +030079{
Olli Etuahob79b3f92017-07-21 14:23:21 +030080 TIntermSymbol *symbol = new TIntermSymbol(symbolTable->nextUniqueId(), "value", type);
Olli Etuaho5d91dda2015-06-18 15:47:46 +030081 symbol->setInternal(true);
82 symbol->getTypePointer()->setQualifier(EvqIn);
83 return symbol;
84}
85
86TIntermConstantUnion *CreateIntConstantNode(int i)
87{
88 TConstantUnion *constant = new TConstantUnion();
89 constant->setIConst(i);
90 return new TIntermConstantUnion(constant, TType(EbtInt, EbpHigh));
91}
92
Olli Etuaho5d91dda2015-06-18 15:47:46 +030093TIntermTyped *EnsureSignedInt(TIntermTyped *node)
94{
95 if (node->getBasicType() == EbtInt)
96 return node;
97
Olli Etuahoaf6fc1b2017-01-26 17:45:35 -080098 TIntermSequence *arguments = new TIntermSequence();
99 arguments->push_back(node);
Olli Etuahoa7ecec32017-05-08 17:43:55 +0300100 return TIntermAggregate::CreateConstructor(TType(EbtInt), arguments);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300101}
102
103TType GetFieldType(const TType &indexedType)
104{
105 if (indexedType.isMatrix())
106 {
107 TType fieldType = TType(indexedType.getBasicType(), indexedType.getPrecision());
108 fieldType.setPrimarySize(static_cast<unsigned char>(indexedType.getRows()));
109 return fieldType;
110 }
111 else
112 {
113 return TType(indexedType.getBasicType(), indexedType.getPrecision());
114 }
115}
116
117// Generate a read or write function for one field in a vector/matrix.
118// Out-of-range indices are clamped. This is consistent with how ANGLE handles out-of-range
119// indices in other places.
120// Note that indices can be either int or uint. We create only int versions of the functions,
121// and convert uint indices to int at the call site.
122// read function example:
123// float dyn_index_vec2(in vec2 base, in int index)
124// {
125// switch(index)
126// {
127// case (0):
128// return base[0];
129// case (1):
130// return base[1];
131// default:
132// break;
133// }
134// if (index < 0)
135// return base[0];
136// return base[1];
137// }
138// write function example:
139// void dyn_index_write_vec2(inout vec2 base, in int index, in float value)
140// {
141// switch(index)
142// {
143// case (0):
144// base[0] = value;
145// return;
146// case (1):
147// base[1] = value;
148// return;
149// default:
150// break;
151// }
152// if (index < 0)
153// {
154// base[0] = value;
155// return;
156// }
157// base[1] = value;
158// }
159// Note that else is not used in above functions to avoid the RewriteElseBlocks transformation.
Olli Etuahofe486322017-03-21 09:30:54 +0000160TIntermFunctionDefinition *GetIndexFunctionDefinition(TType type,
161 bool write,
Olli Etuahob79b3f92017-07-21 14:23:21 +0300162 const TSymbolUniqueId &functionId,
163 TSymbolTable *symbolTable)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300164{
165 ASSERT(!type.isArray());
166 // Conservatively use highp here, even if the indexed type is not highp. That way the code can't
167 // end up using mediump version of an indexing function for a highp value, if both mediump and
168 // highp values are being indexed in the shader. For HLSL precision doesn't matter, but in
169 // principle this code could be used with multiple backends.
170 type.setPrecision(EbpHigh);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300171
172 TType fieldType = GetFieldType(type);
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500173 int numCases = 0;
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300174 if (type.isMatrix())
175 {
176 numCases = type.getCols();
177 }
178 else
179 {
180 numCases = type.getNominalSize();
181 }
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300182
Olli Etuahofe486322017-03-21 09:30:54 +0000183 TType returnType(EbtVoid);
184 if (!write)
Olli Etuaho8ad9e752017-01-16 19:55:20 +0000185 {
Olli Etuahofe486322017-03-21 09:30:54 +0000186 returnType = fieldType;
Olli Etuaho8ad9e752017-01-16 19:55:20 +0000187 }
Olli Etuahofe486322017-03-21 09:30:54 +0000188
189 std::string functionName = GetIndexFunctionName(type, write);
Olli Etuaho3ec75682017-07-05 17:02:55 +0300190 TIntermFunctionPrototype *prototypeNode =
191 CreateInternalFunctionPrototypeNode(returnType, functionName.c_str(), functionId);
Olli Etuaho8ad9e752017-01-16 19:55:20 +0000192
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500193 TQualifier baseQualifier = EvqInOut;
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300194 if (!write)
195 baseQualifier = EvqIn;
Olli Etuahob79b3f92017-07-21 14:23:21 +0300196 TIntermSymbol *baseParam = CreateBaseSymbol(type, baseQualifier, symbolTable);
Olli Etuaho8ad9e752017-01-16 19:55:20 +0000197 prototypeNode->getSequence()->push_back(baseParam);
Olli Etuahob79b3f92017-07-21 14:23:21 +0300198 TIntermSymbol *indexParam = CreateIndexSymbol(symbolTable);
Olli Etuaho8ad9e752017-01-16 19:55:20 +0000199 prototypeNode->getSequence()->push_back(indexParam);
Olli Etuahob79b3f92017-07-21 14:23:21 +0300200 TIntermSymbol *valueParam = nullptr;
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300201 if (write)
202 {
Olli Etuahob79b3f92017-07-21 14:23:21 +0300203 valueParam = CreateValueSymbol(fieldType, symbolTable);
Olli Etuaho8ad9e752017-01-16 19:55:20 +0000204 prototypeNode->getSequence()->push_back(valueParam);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300205 }
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300206
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100207 TIntermBlock *statementList = new TIntermBlock();
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300208 for (int i = 0; i < numCases; ++i)
209 {
210 TIntermCase *caseNode = new TIntermCase(CreateIntConstantNode(i));
211 statementList->getSequence()->push_back(caseNode);
212
213 TIntermBinary *indexNode =
Olli Etuahob79b3f92017-07-21 14:23:21 +0300214 new TIntermBinary(EOpIndexDirect, baseParam->deepCopy(), CreateIndexNode(i));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300215 if (write)
216 {
Olli Etuahob79b3f92017-07-21 14:23:21 +0300217 TIntermBinary *assignNode =
218 new TIntermBinary(EOpAssign, indexNode, valueParam->deepCopy());
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300219 statementList->getSequence()->push_back(assignNode);
220 TIntermBranch *returnNode = new TIntermBranch(EOpReturn, nullptr);
221 statementList->getSequence()->push_back(returnNode);
222 }
223 else
224 {
225 TIntermBranch *returnNode = new TIntermBranch(EOpReturn, indexNode);
226 statementList->getSequence()->push_back(returnNode);
227 }
228 }
229
230 // Default case
231 TIntermCase *defaultNode = new TIntermCase(nullptr);
232 statementList->getSequence()->push_back(defaultNode);
233 TIntermBranch *breakNode = new TIntermBranch(EOpBreak, nullptr);
234 statementList->getSequence()->push_back(breakNode);
235
Olli Etuahob79b3f92017-07-21 14:23:21 +0300236 TIntermSwitch *switchNode = new TIntermSwitch(indexParam->deepCopy(), statementList);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300237
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100238 TIntermBlock *bodyNode = new TIntermBlock();
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300239 bodyNode->getSequence()->push_back(switchNode);
240
Olli Etuaho3272a6d2016-08-29 17:54:50 +0300241 TIntermBinary *cond =
Olli Etuahob79b3f92017-07-21 14:23:21 +0300242 new TIntermBinary(EOpLessThan, indexParam->deepCopy(), CreateIntConstantNode(0));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300243 cond->setType(TType(EbtBool, EbpUndefined));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300244
245 // Two blocks: one accesses (either reads or writes) the first element and returns,
246 // the other accesses the last element.
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100247 TIntermBlock *useFirstBlock = new TIntermBlock();
248 TIntermBlock *useLastBlock = new TIntermBlock();
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300249 TIntermBinary *indexFirstNode =
Olli Etuahob79b3f92017-07-21 14:23:21 +0300250 new TIntermBinary(EOpIndexDirect, baseParam->deepCopy(), CreateIndexNode(0));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300251 TIntermBinary *indexLastNode =
Olli Etuahob79b3f92017-07-21 14:23:21 +0300252 new TIntermBinary(EOpIndexDirect, baseParam->deepCopy(), CreateIndexNode(numCases - 1));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300253 if (write)
254 {
Olli Etuahob79b3f92017-07-21 14:23:21 +0300255 TIntermBinary *assignFirstNode =
256 new TIntermBinary(EOpAssign, indexFirstNode, valueParam->deepCopy());
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300257 useFirstBlock->getSequence()->push_back(assignFirstNode);
258 TIntermBranch *returnNode = new TIntermBranch(EOpReturn, nullptr);
259 useFirstBlock->getSequence()->push_back(returnNode);
260
Olli Etuahob79b3f92017-07-21 14:23:21 +0300261 TIntermBinary *assignLastNode =
262 new TIntermBinary(EOpAssign, indexLastNode, valueParam->deepCopy());
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300263 useLastBlock->getSequence()->push_back(assignLastNode);
264 }
265 else
266 {
267 TIntermBranch *returnFirstNode = new TIntermBranch(EOpReturn, indexFirstNode);
268 useFirstBlock->getSequence()->push_back(returnFirstNode);
269
270 TIntermBranch *returnLastNode = new TIntermBranch(EOpReturn, indexLastNode);
271 useLastBlock->getSequence()->push_back(returnLastNode);
272 }
Olli Etuaho57961272016-09-14 13:57:46 +0300273 TIntermIfElse *ifNode = new TIntermIfElse(cond, useFirstBlock, nullptr);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300274 bodyNode->getSequence()->push_back(ifNode);
275 bodyNode->getSequence()->push_back(useLastBlock);
276
Olli Etuaho8ad9e752017-01-16 19:55:20 +0000277 TIntermFunctionDefinition *indexingFunction =
278 new TIntermFunctionDefinition(prototypeNode, bodyNode);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300279 return indexingFunction;
280}
281
282class RemoveDynamicIndexingTraverser : public TLValueTrackingTraverser
283{
284 public:
Olli Etuaho89a69a02017-10-23 12:20:45 +0300285 RemoveDynamicIndexingTraverser(TSymbolTable *symbolTable,
286 int shaderVersion,
287 PerformanceDiagnostics *perfDiagnostics);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300288
289 bool visitBinary(Visit visit, TIntermBinary *node) override;
290
291 void insertHelperDefinitions(TIntermNode *root);
292
293 void nextIteration();
294
295 bool usedTreeInsertion() const { return mUsedTreeInsertion; }
296
297 protected:
Olli Etuahofe486322017-03-21 09:30:54 +0000298 // Maps of types that are indexed to the indexing function ids used for them. Note that these
299 // can not store multiple variants of the same type with different precisions - only one
300 // precision gets stored.
Olli Etuahoa5e693a2017-07-13 16:07:26 +0300301 std::map<TType, TSymbolUniqueId *> mIndexedVecAndMatrixTypes;
302 std::map<TType, TSymbolUniqueId *> mWrittenVecAndMatrixTypes;
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300303
304 bool mUsedTreeInsertion;
305
306 // When true, the traverser will remove side effects from any indexing expression.
307 // This is done so that in code like
308 // V[j++][i]++.
309 // where V is an array of vectors, j++ will only be evaluated once.
310 bool mRemoveIndexSideEffectsInSubtree;
Olli Etuaho89a69a02017-10-23 12:20:45 +0300311
312 PerformanceDiagnostics *mPerfDiagnostics;
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300313};
314
Olli Etuaho89a69a02017-10-23 12:20:45 +0300315RemoveDynamicIndexingTraverser::RemoveDynamicIndexingTraverser(
316 TSymbolTable *symbolTable,
317 int shaderVersion,
318 PerformanceDiagnostics *perfDiagnostics)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300319 : TLValueTrackingTraverser(true, false, false, symbolTable, shaderVersion),
320 mUsedTreeInsertion(false),
Olli Etuaho89a69a02017-10-23 12:20:45 +0300321 mRemoveIndexSideEffectsInSubtree(false),
322 mPerfDiagnostics(perfDiagnostics)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300323{
324}
325
326void RemoveDynamicIndexingTraverser::insertHelperDefinitions(TIntermNode *root)
327{
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100328 TIntermBlock *rootBlock = root->getAsBlock();
329 ASSERT(rootBlock != nullptr);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300330 TIntermSequence insertions;
Olli Etuahofe486322017-03-21 09:30:54 +0000331 for (auto &type : mIndexedVecAndMatrixTypes)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300332 {
Olli Etuahob79b3f92017-07-21 14:23:21 +0300333 insertions.push_back(
334 GetIndexFunctionDefinition(type.first, false, *type.second, mSymbolTable));
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 Etuahob79b3f92017-07-21 14:23:21 +0300338 insertions.push_back(
339 GetIndexFunctionDefinition(type.first, true, *type.second, mSymbolTable));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300340 }
Olli Etuaho56612d62017-10-12 15:46:30 +0300341 rootBlock->insertChildNodes(0, insertions);
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.
Olli Etuaho495162b2017-12-08 10:19:01 +0100397 nextTemporaryId();
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300398
399 // Init the temp variable holding the index
Olli Etuaho13389b62016-10-16 11:48:18 +0100400 TIntermDeclaration *initIndex = createTempInitDeclaration(node->getRight());
Jamie Madill1048e432016-07-23 18:51:28 -0400401 insertStatementInParentBlock(initIndex);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300402 mUsedTreeInsertion = true;
403
404 // Replace the index with the temp variable
405 TIntermSymbol *tempIndex = createTempSymbol(node->getRight()->getType());
Jamie Madill03d863c2016-07-27 18:15:53 -0400406 queueReplacementWithParent(node, node->getRight(), tempIndex, OriginalNode::IS_DROPPED);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300407 }
Jamie Madill666f65a2016-08-26 01:34:37 +0000408 else if (IntermNodePatternMatcher::IsDynamicIndexingOfVectorOrMatrix(node))
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300409 {
Olli Etuaho89a69a02017-10-23 12:20:45 +0300410 mPerfDiagnostics->warning(node->getLine(),
411 "Performance: dynamic indexing of vectors and "
412 "matrices is emulated and can be slow.",
413 "[]");
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300414 bool write = isLValueRequiredHere();
415
Jamie Madill666f65a2016-08-26 01:34:37 +0000416#if defined(ANGLE_ENABLE_ASSERTS)
417 // Make sure that IntermNodePatternMatcher is consistent with the slightly differently
418 // implemented checks in this traverser.
419 IntermNodePatternMatcher matcher(
420 IntermNodePatternMatcher::kDynamicIndexingOfVectorOrMatrixInLValue);
421 ASSERT(matcher.match(node, getParentNode(), isLValueRequiredHere()) == write);
422#endif
423
Olli Etuahofe486322017-03-21 09:30:54 +0000424 const TType &type = node->getLeft()->getType();
Olli Etuahoa5e693a2017-07-13 16:07:26 +0300425 TSymbolUniqueId *indexingFunctionId = new TSymbolUniqueId(mSymbolTable);
Olli Etuahofe486322017-03-21 09:30:54 +0000426 if (mIndexedVecAndMatrixTypes.find(type) == mIndexedVecAndMatrixTypes.end())
427 {
428 mIndexedVecAndMatrixTypes[type] = indexingFunctionId;
429 }
430 else
431 {
432 indexingFunctionId = mIndexedVecAndMatrixTypes[type];
433 }
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300434
435 if (write)
436 {
437 // Convert:
438 // v_expr[index_expr]++;
439 // to this:
440 // int s0 = index_expr; float s1 = dyn_index(v_expr, s0); s1++;
441 // dyn_index_write(v_expr, s0, s1);
442 // This works even if index_expr has some side effects.
443 if (node->getLeft()->hasSideEffects())
444 {
445 // If v_expr has side effects, those need to be removed before proceeding.
446 // Otherwise the side effects of v_expr would be evaluated twice.
447 // The only case where an l-value can have side effects is when it is
448 // indexing. For example, it can be V[j++] where V is an array of vectors.
449 mRemoveIndexSideEffectsInSubtree = true;
450 return true;
451 }
Olli Etuaho8f6eb2a2017-01-12 17:04:58 +0000452
453 TIntermBinary *leftBinary = node->getLeft()->getAsBinaryNode();
454 if (leftBinary != nullptr &&
455 IntermNodePatternMatcher::IsDynamicIndexingOfVectorOrMatrix(leftBinary))
456 {
457 // This is a case like:
458 // mat2 m;
459 // m[a][b]++;
460 // Process the child node m[a] first.
461 return true;
462 }
463
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300464 // TODO(oetuaho@nvidia.com): This is not optimal if the expression using the value
465 // only writes it and doesn't need the previous value. http://anglebug.com/1116
466
Olli Etuahoa5e693a2017-07-13 16:07:26 +0300467 TSymbolUniqueId *indexedWriteFunctionId = new TSymbolUniqueId(mSymbolTable);
Olli Etuahofe486322017-03-21 09:30:54 +0000468 if (mWrittenVecAndMatrixTypes.find(type) == mWrittenVecAndMatrixTypes.end())
469 {
470 mWrittenVecAndMatrixTypes[type] = indexedWriteFunctionId;
471 }
472 else
473 {
474 indexedWriteFunctionId = mWrittenVecAndMatrixTypes[type];
475 }
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300476 TType fieldType = GetFieldType(type);
477
478 TIntermSequence insertionsBefore;
479 TIntermSequence insertionsAfter;
480
481 // Store the index in a temporary signed int variable.
Olli Etuaho495162b2017-12-08 10:19:01 +0100482 nextTemporaryId();
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300483 TIntermTyped *indexInitializer = EnsureSignedInt(node->getRight());
Olli Etuaho13389b62016-10-16 11:48:18 +0100484 TIntermDeclaration *initIndex = createTempInitDeclaration(indexInitializer);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300485 initIndex->setLine(node->getLine());
486 insertionsBefore.push_back(initIndex);
487
Olli Etuaho4dd06d52017-07-05 12:41:06 +0300488 // Create a node for referring to the index after the nextTemporaryId() call
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300489 // below.
490 TIntermSymbol *tempIndex = createTempSymbol(indexInitializer->getType());
491
Olli Etuahofe486322017-03-21 09:30:54 +0000492 TIntermAggregate *indexingCall =
Olli Etuahoa5e693a2017-07-13 16:07:26 +0300493 CreateIndexFunctionCall(node, tempIndex, *indexingFunctionId);
Olli Etuahofe486322017-03-21 09:30:54 +0000494
Olli Etuaho4dd06d52017-07-05 12:41:06 +0300495 nextTemporaryId(); // From now on, creating temporary symbols that refer to the
496 // field value.
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300497 insertionsBefore.push_back(createTempInitDeclaration(indexingCall));
498
Olli Etuahofe486322017-03-21 09:30:54 +0000499 TIntermAggregate *indexedWriteCall = CreateIndexedWriteFunctionCall(
Olli Etuahoa5e693a2017-07-13 16:07:26 +0300500 node, tempIndex, createTempSymbol(fieldType), *indexedWriteFunctionId);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300501 insertionsAfter.push_back(indexedWriteCall);
502 insertStatementsInParentBlock(insertionsBefore, insertionsAfter);
Olli Etuahoea39a222017-07-06 12:47:59 +0300503 queueReplacement(createTempSymbol(fieldType), OriginalNode::IS_DROPPED);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300504 mUsedTreeInsertion = true;
505 }
506 else
507 {
508 // The indexed value is not being written, so we can simply convert
509 // v_expr[index_expr]
510 // into
511 // dyn_index(v_expr, index_expr)
512 // If the index_expr is unsigned, we'll convert it to signed.
513 ASSERT(!mRemoveIndexSideEffectsInSubtree);
514 TIntermAggregate *indexingCall = CreateIndexFunctionCall(
Olli Etuahoa5e693a2017-07-13 16:07:26 +0300515 node, EnsureSignedInt(node->getRight()), *indexingFunctionId);
Olli Etuahoea39a222017-07-06 12:47:59 +0300516 queueReplacement(indexingCall, OriginalNode::IS_DROPPED);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300517 }
518 }
519 }
520 return !mUsedTreeInsertion;
521}
522
523void RemoveDynamicIndexingTraverser::nextIteration()
524{
525 mUsedTreeInsertion = false;
526 mRemoveIndexSideEffectsInSubtree = false;
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300527}
528
529} // namespace
530
Olli Etuaho89a69a02017-10-23 12:20:45 +0300531void RemoveDynamicIndexing(TIntermNode *root,
532 TSymbolTable *symbolTable,
533 int shaderVersion,
534 PerformanceDiagnostics *perfDiagnostics)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300535{
Olli Etuaho89a69a02017-10-23 12:20:45 +0300536 RemoveDynamicIndexingTraverser traverser(symbolTable, shaderVersion, perfDiagnostics);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300537 do
538 {
539 traverser.nextIteration();
540 root->traverse(&traverser);
541 traverser.updateTree();
542 } while (traverser.usedTreeInsertion());
Olli Etuaho56612d62017-10-12 15:46:30 +0300543 // TODO(oetuaho@nvidia.com): It might be nicer to add the helper definitions also in the middle
Olli Etuaho1ecd14b2017-01-26 13:54:15 -0800544 // of traversal. Now the tree ends up in an inconsistent state in the middle, since there are
545 // function call nodes with no corresponding definition nodes. This needs special handling in
546 // TIntermLValueTrackingTraverser, and creates intricacies that are not easily apparent from a
547 // superficial reading of the code.
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300548 traverser.insertHelperDefinitions(root);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300549}
Jamie Madill45bcc782016-11-07 13:58:48 -0500550
551} // namespace sh