blob: 97fa96f60320ccc20a5fbd67760c0e141bafb401 [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 Etuaho195be942017-12-04 23:40:14 +020061TIntermSymbol *CreateBaseSymbol(const TType &type, TSymbolTable *symbolTable)
Olli Etuaho5d91dda2015-06-18 15:47:46 +030062{
Olli Etuaho195be942017-12-04 23:40:14 +020063 TString *baseString = NewPoolTString("base");
64 TVariable *baseVariable =
65 new TVariable(symbolTable, baseString, type, SymbolType::AngleInternal);
66 return new TIntermSymbol(baseVariable);
Olli Etuaho5d91dda2015-06-18 15:47:46 +030067}
68
Olli Etuahob79b3f92017-07-21 14:23:21 +030069TIntermSymbol *CreateIndexSymbol(TSymbolTable *symbolTable)
Olli Etuaho5d91dda2015-06-18 15:47:46 +030070{
Olli Etuaho195be942017-12-04 23:40:14 +020071 TString *indexString = NewPoolTString("index");
72 TVariable *indexVariable = new TVariable(
73 symbolTable, indexString, TType(EbtInt, EbpHigh, EvqIn), SymbolType::AngleInternal);
74 return new TIntermSymbol(indexVariable);
Olli Etuaho5d91dda2015-06-18 15:47:46 +030075}
76
Olli Etuahob79b3f92017-07-21 14:23:21 +030077TIntermSymbol *CreateValueSymbol(const TType &type, TSymbolTable *symbolTable)
Olli Etuaho5d91dda2015-06-18 15:47:46 +030078{
Olli Etuaho195be942017-12-04 23:40:14 +020079 TString *valueString = NewPoolTString("value");
80 TType valueType(type);
81 valueType.setQualifier(EvqIn);
82 TVariable *valueVariable =
83 new TVariable(symbolTable, valueString, valueType, SymbolType::AngleInternal);
84 return new TIntermSymbol(valueVariable);
Olli Etuaho5d91dda2015-06-18 15:47:46 +030085}
86
87TIntermConstantUnion *CreateIntConstantNode(int i)
88{
89 TConstantUnion *constant = new TConstantUnion();
90 constant->setIConst(i);
91 return new TIntermConstantUnion(constant, TType(EbtInt, EbpHigh));
92}
93
Olli Etuaho5d91dda2015-06-18 15:47:46 +030094TIntermTyped *EnsureSignedInt(TIntermTyped *node)
95{
96 if (node->getBasicType() == EbtInt)
97 return node;
98
Olli Etuahoaf6fc1b2017-01-26 17:45:35 -080099 TIntermSequence *arguments = new TIntermSequence();
100 arguments->push_back(node);
Olli Etuahoa7ecec32017-05-08 17:43:55 +0300101 return TIntermAggregate::CreateConstructor(TType(EbtInt), arguments);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300102}
103
Olli Etuaho0c371002017-12-13 17:00:25 +0400104TType *GetFieldType(const TType &indexedType)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300105{
106 if (indexedType.isMatrix())
107 {
Olli Etuaho0c371002017-12-13 17:00:25 +0400108 TType *fieldType = new TType(indexedType.getBasicType(), indexedType.getPrecision());
109 fieldType->setPrimarySize(static_cast<unsigned char>(indexedType.getRows()));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300110 return fieldType;
111 }
112 else
113 {
Olli Etuaho0c371002017-12-13 17:00:25 +0400114 return new TType(indexedType.getBasicType(), indexedType.getPrecision());
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300115 }
116}
117
118// Generate a read or write function for one field in a vector/matrix.
119// Out-of-range indices are clamped. This is consistent with how ANGLE handles out-of-range
120// indices in other places.
121// Note that indices can be either int or uint. We create only int versions of the functions,
122// and convert uint indices to int at the call site.
123// read function example:
124// float dyn_index_vec2(in vec2 base, in int index)
125// {
126// switch(index)
127// {
128// case (0):
129// return base[0];
130// case (1):
131// return base[1];
132// default:
133// break;
134// }
135// if (index < 0)
136// return base[0];
137// return base[1];
138// }
139// write function example:
140// void dyn_index_write_vec2(inout vec2 base, in int index, in float value)
141// {
142// switch(index)
143// {
144// case (0):
145// base[0] = value;
146// return;
147// case (1):
148// base[1] = value;
149// return;
150// default:
151// break;
152// }
153// if (index < 0)
154// {
155// base[0] = value;
156// return;
157// }
158// base[1] = value;
159// }
160// Note that else is not used in above functions to avoid the RewriteElseBlocks transformation.
Olli Etuaho195be942017-12-04 23:40:14 +0200161TIntermFunctionDefinition *GetIndexFunctionDefinition(const TType &type,
Olli Etuahofe486322017-03-21 09:30:54 +0000162 bool write,
Olli Etuaho0c371002017-12-13 17:00:25 +0400163 const TFunction &func,
Olli Etuahob79b3f92017-07-21 14:23:21 +0300164 TSymbolTable *symbolTable)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300165{
166 ASSERT(!type.isArray());
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300167
Olli Etuaho0c371002017-12-13 17:00:25 +0400168 const TType *fieldType = GetFieldType(type);
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500169 int numCases = 0;
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300170 if (type.isMatrix())
171 {
172 numCases = type.getCols();
173 }
174 else
175 {
176 numCases = type.getNominalSize();
177 }
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300178
Olli Etuahofe486322017-03-21 09:30:54 +0000179 std::string functionName = GetIndexFunctionName(type, write);
Olli Etuaho0c371002017-12-13 17:00:25 +0400180 TIntermFunctionPrototype *prototypeNode = CreateInternalFunctionPrototypeNode(func);
Olli Etuaho8ad9e752017-01-16 19:55:20 +0000181
Olli Etuaho195be942017-12-04 23:40:14 +0200182 TType baseType(type);
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 baseType.setPrecision(EbpHigh);
188 baseType.setQualifier(EvqInOut);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300189 if (!write)
Olli Etuaho195be942017-12-04 23:40:14 +0200190 baseType.setQualifier(EvqIn);
191
192 TIntermSymbol *baseParam = CreateBaseSymbol(baseType, symbolTable);
Olli Etuaho8ad9e752017-01-16 19:55:20 +0000193 prototypeNode->getSequence()->push_back(baseParam);
Olli Etuahob79b3f92017-07-21 14:23:21 +0300194 TIntermSymbol *indexParam = CreateIndexSymbol(symbolTable);
Olli Etuaho8ad9e752017-01-16 19:55:20 +0000195 prototypeNode->getSequence()->push_back(indexParam);
Olli Etuahob79b3f92017-07-21 14:23:21 +0300196 TIntermSymbol *valueParam = nullptr;
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300197 if (write)
198 {
Olli Etuaho0c371002017-12-13 17:00:25 +0400199 valueParam = CreateValueSymbol(*fieldType, symbolTable);
Olli Etuaho8ad9e752017-01-16 19:55:20 +0000200 prototypeNode->getSequence()->push_back(valueParam);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300201 }
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300202
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100203 TIntermBlock *statementList = new TIntermBlock();
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300204 for (int i = 0; i < numCases; ++i)
205 {
206 TIntermCase *caseNode = new TIntermCase(CreateIntConstantNode(i));
207 statementList->getSequence()->push_back(caseNode);
208
209 TIntermBinary *indexNode =
Olli Etuahob79b3f92017-07-21 14:23:21 +0300210 new TIntermBinary(EOpIndexDirect, baseParam->deepCopy(), CreateIndexNode(i));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300211 if (write)
212 {
Olli Etuahob79b3f92017-07-21 14:23:21 +0300213 TIntermBinary *assignNode =
214 new TIntermBinary(EOpAssign, indexNode, valueParam->deepCopy());
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300215 statementList->getSequence()->push_back(assignNode);
216 TIntermBranch *returnNode = new TIntermBranch(EOpReturn, nullptr);
217 statementList->getSequence()->push_back(returnNode);
218 }
219 else
220 {
221 TIntermBranch *returnNode = new TIntermBranch(EOpReturn, indexNode);
222 statementList->getSequence()->push_back(returnNode);
223 }
224 }
225
226 // Default case
227 TIntermCase *defaultNode = new TIntermCase(nullptr);
228 statementList->getSequence()->push_back(defaultNode);
229 TIntermBranch *breakNode = new TIntermBranch(EOpBreak, nullptr);
230 statementList->getSequence()->push_back(breakNode);
231
Olli Etuahob79b3f92017-07-21 14:23:21 +0300232 TIntermSwitch *switchNode = new TIntermSwitch(indexParam->deepCopy(), statementList);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300233
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100234 TIntermBlock *bodyNode = new TIntermBlock();
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300235 bodyNode->getSequence()->push_back(switchNode);
236
Olli Etuaho3272a6d2016-08-29 17:54:50 +0300237 TIntermBinary *cond =
Olli Etuahob79b3f92017-07-21 14:23:21 +0300238 new TIntermBinary(EOpLessThan, indexParam->deepCopy(), CreateIntConstantNode(0));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300239 cond->setType(TType(EbtBool, EbpUndefined));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300240
241 // Two blocks: one accesses (either reads or writes) the first element and returns,
242 // the other accesses the last element.
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100243 TIntermBlock *useFirstBlock = new TIntermBlock();
244 TIntermBlock *useLastBlock = new TIntermBlock();
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300245 TIntermBinary *indexFirstNode =
Olli Etuahob79b3f92017-07-21 14:23:21 +0300246 new TIntermBinary(EOpIndexDirect, baseParam->deepCopy(), CreateIndexNode(0));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300247 TIntermBinary *indexLastNode =
Olli Etuahob79b3f92017-07-21 14:23:21 +0300248 new TIntermBinary(EOpIndexDirect, baseParam->deepCopy(), CreateIndexNode(numCases - 1));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300249 if (write)
250 {
Olli Etuahob79b3f92017-07-21 14:23:21 +0300251 TIntermBinary *assignFirstNode =
252 new TIntermBinary(EOpAssign, indexFirstNode, valueParam->deepCopy());
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300253 useFirstBlock->getSequence()->push_back(assignFirstNode);
254 TIntermBranch *returnNode = new TIntermBranch(EOpReturn, nullptr);
255 useFirstBlock->getSequence()->push_back(returnNode);
256
Olli Etuahob79b3f92017-07-21 14:23:21 +0300257 TIntermBinary *assignLastNode =
258 new TIntermBinary(EOpAssign, indexLastNode, valueParam->deepCopy());
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300259 useLastBlock->getSequence()->push_back(assignLastNode);
260 }
261 else
262 {
263 TIntermBranch *returnFirstNode = new TIntermBranch(EOpReturn, indexFirstNode);
264 useFirstBlock->getSequence()->push_back(returnFirstNode);
265
266 TIntermBranch *returnLastNode = new TIntermBranch(EOpReturn, indexLastNode);
267 useLastBlock->getSequence()->push_back(returnLastNode);
268 }
Olli Etuaho57961272016-09-14 13:57:46 +0300269 TIntermIfElse *ifNode = new TIntermIfElse(cond, useFirstBlock, nullptr);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300270 bodyNode->getSequence()->push_back(ifNode);
271 bodyNode->getSequence()->push_back(useLastBlock);
272
Olli Etuaho8ad9e752017-01-16 19:55:20 +0000273 TIntermFunctionDefinition *indexingFunction =
274 new TIntermFunctionDefinition(prototypeNode, bodyNode);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300275 return indexingFunction;
276}
277
278class RemoveDynamicIndexingTraverser : public TLValueTrackingTraverser
279{
280 public:
Olli Etuaho89a69a02017-10-23 12:20:45 +0300281 RemoveDynamicIndexingTraverser(TSymbolTable *symbolTable,
282 int shaderVersion,
283 PerformanceDiagnostics *perfDiagnostics);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300284
285 bool visitBinary(Visit visit, TIntermBinary *node) override;
286
287 void insertHelperDefinitions(TIntermNode *root);
288
289 void nextIteration();
290
291 bool usedTreeInsertion() const { return mUsedTreeInsertion; }
292
293 protected:
Olli Etuahofe486322017-03-21 09:30:54 +0000294 // Maps of types that are indexed to the indexing function ids used for them. Note that these
295 // can not store multiple variants of the same type with different precisions - only one
296 // precision gets stored.
Olli Etuaho0c371002017-12-13 17:00:25 +0400297 std::map<TType, TFunction *> mIndexedVecAndMatrixTypes;
298 std::map<TType, TFunction *> mWrittenVecAndMatrixTypes;
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300299
300 bool mUsedTreeInsertion;
301
302 // When true, the traverser will remove side effects from any indexing expression.
303 // This is done so that in code like
304 // V[j++][i]++.
305 // where V is an array of vectors, j++ will only be evaluated once.
306 bool mRemoveIndexSideEffectsInSubtree;
Olli Etuaho89a69a02017-10-23 12:20:45 +0300307
308 PerformanceDiagnostics *mPerfDiagnostics;
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300309};
310
Olli Etuaho89a69a02017-10-23 12:20:45 +0300311RemoveDynamicIndexingTraverser::RemoveDynamicIndexingTraverser(
312 TSymbolTable *symbolTable,
313 int shaderVersion,
314 PerformanceDiagnostics *perfDiagnostics)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300315 : TLValueTrackingTraverser(true, false, false, symbolTable, shaderVersion),
316 mUsedTreeInsertion(false),
Olli Etuaho89a69a02017-10-23 12:20:45 +0300317 mRemoveIndexSideEffectsInSubtree(false),
318 mPerfDiagnostics(perfDiagnostics)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300319{
320}
321
322void RemoveDynamicIndexingTraverser::insertHelperDefinitions(TIntermNode *root)
323{
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100324 TIntermBlock *rootBlock = root->getAsBlock();
325 ASSERT(rootBlock != nullptr);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300326 TIntermSequence insertions;
Olli Etuahofe486322017-03-21 09:30:54 +0000327 for (auto &type : mIndexedVecAndMatrixTypes)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300328 {
Olli Etuahob79b3f92017-07-21 14:23:21 +0300329 insertions.push_back(
330 GetIndexFunctionDefinition(type.first, false, *type.second, mSymbolTable));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300331 }
Olli Etuahofe486322017-03-21 09:30:54 +0000332 for (auto &type : mWrittenVecAndMatrixTypes)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300333 {
Olli Etuahob79b3f92017-07-21 14:23:21 +0300334 insertions.push_back(
335 GetIndexFunctionDefinition(type.first, true, *type.second, mSymbolTable));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300336 }
Olli Etuaho56612d62017-10-12 15:46:30 +0300337 rootBlock->insertChildNodes(0, insertions);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300338}
339
340// Create a call to dyn_index_*() based on an indirect indexing op node
341TIntermAggregate *CreateIndexFunctionCall(TIntermBinary *node,
Olli Etuahofe486322017-03-21 09:30:54 +0000342 TIntermTyped *index,
Olli Etuaho0c371002017-12-13 17:00:25 +0400343 TFunction *indexingFunction)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300344{
345 ASSERT(node->getOp() == EOpIndexIndirect);
Olli Etuahoaf6fc1b2017-01-26 17:45:35 -0800346 TIntermSequence *arguments = new TIntermSequence();
Olli Etuahofe486322017-03-21 09:30:54 +0000347 arguments->push_back(node->getLeft());
Olli Etuahoaf6fc1b2017-01-26 17:45:35 -0800348 arguments->push_back(index);
349
Olli Etuaho3ec75682017-07-05 17:02:55 +0300350 TIntermAggregate *indexingCall =
Olli Etuaho0c371002017-12-13 17:00:25 +0400351 TIntermAggregate::CreateFunctionCall(*indexingFunction, arguments);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300352 indexingCall->setLine(node->getLine());
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300353 return indexingCall;
354}
355
356TIntermAggregate *CreateIndexedWriteFunctionCall(TIntermBinary *node,
Olli Etuaho195be942017-12-04 23:40:14 +0200357 TVariable *index,
358 TVariable *writtenValue,
Olli Etuaho0c371002017-12-13 17:00:25 +0400359 TFunction *indexedWriteFunction)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300360{
Olli Etuahofe486322017-03-21 09:30:54 +0000361 ASSERT(node->getOp() == EOpIndexIndirect);
362 TIntermSequence *arguments = new TIntermSequence();
363 // Deep copy the child nodes so that two pointers to the same node don't end up in the tree.
364 arguments->push_back(node->getLeft()->deepCopy());
Olli Etuaho195be942017-12-04 23:40:14 +0200365 arguments->push_back(CreateTempSymbolNode(index));
366 arguments->push_back(CreateTempSymbolNode(writtenValue));
Olli Etuahofe486322017-03-21 09:30:54 +0000367
Olli Etuaho3ec75682017-07-05 17:02:55 +0300368 TIntermAggregate *indexedWriteCall =
Olli Etuaho0c371002017-12-13 17:00:25 +0400369 TIntermAggregate::CreateFunctionCall(*indexedWriteFunction, arguments);
Olli Etuahofe486322017-03-21 09:30:54 +0000370 indexedWriteCall->setLine(node->getLine());
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300371 return indexedWriteCall;
372}
373
374bool RemoveDynamicIndexingTraverser::visitBinary(Visit visit, TIntermBinary *node)
375{
376 if (mUsedTreeInsertion)
377 return false;
378
379 if (node->getOp() == EOpIndexIndirect)
380 {
381 if (mRemoveIndexSideEffectsInSubtree)
382 {
383 ASSERT(node->getRight()->hasSideEffects());
384 // In case we're just removing index side effects, convert
385 // v_expr[index_expr]
386 // to this:
387 // int s0 = index_expr; v_expr[s0];
388 // Now v_expr[s0] can be safely executed several times without unintended side effects.
Olli Etuaho195be942017-12-04 23:40:14 +0200389 TIntermDeclaration *indexVariableDeclaration = nullptr;
390 TVariable *indexVariable = DeclareTempVariable(mSymbolTable, node->getRight(),
391 EvqTemporary, &indexVariableDeclaration);
392 insertStatementInParentBlock(indexVariableDeclaration);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300393 mUsedTreeInsertion = true;
394
395 // Replace the index with the temp variable
Olli Etuaho195be942017-12-04 23:40:14 +0200396 TIntermSymbol *tempIndex = CreateTempSymbolNode(indexVariable);
Jamie Madill03d863c2016-07-27 18:15:53 -0400397 queueReplacementWithParent(node, node->getRight(), tempIndex, OriginalNode::IS_DROPPED);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300398 }
Jamie Madill666f65a2016-08-26 01:34:37 +0000399 else if (IntermNodePatternMatcher::IsDynamicIndexingOfVectorOrMatrix(node))
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300400 {
Olli Etuaho89a69a02017-10-23 12:20:45 +0300401 mPerfDiagnostics->warning(node->getLine(),
402 "Performance: dynamic indexing of vectors and "
403 "matrices is emulated and can be slow.",
404 "[]");
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300405 bool write = isLValueRequiredHere();
406
Jamie Madill666f65a2016-08-26 01:34:37 +0000407#if defined(ANGLE_ENABLE_ASSERTS)
408 // Make sure that IntermNodePatternMatcher is consistent with the slightly differently
409 // implemented checks in this traverser.
410 IntermNodePatternMatcher matcher(
411 IntermNodePatternMatcher::kDynamicIndexingOfVectorOrMatrixInLValue);
412 ASSERT(matcher.match(node, getParentNode(), isLValueRequiredHere()) == write);
413#endif
414
Olli Etuahofe486322017-03-21 09:30:54 +0000415 const TType &type = node->getLeft()->getType();
Olli Etuaho0c371002017-12-13 17:00:25 +0400416 TString *indexingFunctionName =
417 NewPoolTString(GetIndexFunctionName(type, false).c_str());
418 TFunction *indexingFunction = nullptr;
Olli Etuahofe486322017-03-21 09:30:54 +0000419 if (mIndexedVecAndMatrixTypes.find(type) == mIndexedVecAndMatrixTypes.end())
420 {
Olli Etuaho0c371002017-12-13 17:00:25 +0400421 indexingFunction =
422 new TFunction(mSymbolTable, indexingFunctionName, GetFieldType(type),
423 SymbolType::AngleInternal, true);
424 mIndexedVecAndMatrixTypes[type] = indexingFunction;
Olli Etuahofe486322017-03-21 09:30:54 +0000425 }
426 else
427 {
Olli Etuaho0c371002017-12-13 17:00:25 +0400428 indexingFunction = mIndexedVecAndMatrixTypes[type];
Olli Etuahofe486322017-03-21 09:30:54 +0000429 }
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300430
431 if (write)
432 {
433 // Convert:
434 // v_expr[index_expr]++;
435 // to this:
436 // int s0 = index_expr; float s1 = dyn_index(v_expr, s0); s1++;
437 // dyn_index_write(v_expr, s0, s1);
438 // This works even if index_expr has some side effects.
439 if (node->getLeft()->hasSideEffects())
440 {
441 // If v_expr has side effects, those need to be removed before proceeding.
442 // Otherwise the side effects of v_expr would be evaluated twice.
443 // The only case where an l-value can have side effects is when it is
444 // indexing. For example, it can be V[j++] where V is an array of vectors.
445 mRemoveIndexSideEffectsInSubtree = true;
446 return true;
447 }
Olli Etuaho8f6eb2a2017-01-12 17:04:58 +0000448
449 TIntermBinary *leftBinary = node->getLeft()->getAsBinaryNode();
450 if (leftBinary != nullptr &&
451 IntermNodePatternMatcher::IsDynamicIndexingOfVectorOrMatrix(leftBinary))
452 {
453 // This is a case like:
454 // mat2 m;
455 // m[a][b]++;
456 // Process the child node m[a] first.
457 return true;
458 }
459
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300460 // TODO(oetuaho@nvidia.com): This is not optimal if the expression using the value
461 // only writes it and doesn't need the previous value. http://anglebug.com/1116
462
Olli Etuaho0c371002017-12-13 17:00:25 +0400463 TFunction *indexedWriteFunction = nullptr;
Olli Etuahofe486322017-03-21 09:30:54 +0000464 if (mWrittenVecAndMatrixTypes.find(type) == mWrittenVecAndMatrixTypes.end())
465 {
Olli Etuaho0c371002017-12-13 17:00:25 +0400466 TString *functionName = NewPoolTString(
467 GetIndexFunctionName(node->getLeft()->getType(), true).c_str());
468 indexedWriteFunction =
469 new TFunction(mSymbolTable, functionName, new TType(EbtVoid),
470 SymbolType::AngleInternal, false);
471 mWrittenVecAndMatrixTypes[type] = indexedWriteFunction;
Olli Etuahofe486322017-03-21 09:30:54 +0000472 }
473 else
474 {
Olli Etuaho0c371002017-12-13 17:00:25 +0400475 indexedWriteFunction = mWrittenVecAndMatrixTypes[type];
Olli Etuahofe486322017-03-21 09:30:54 +0000476 }
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300477
478 TIntermSequence insertionsBefore;
479 TIntermSequence insertionsAfter;
480
481 // Store the index in a temporary signed int variable.
Olli Etuaho195be942017-12-04 23:40:14 +0200482 // s0 = index_expr;
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300483 TIntermTyped *indexInitializer = EnsureSignedInt(node->getRight());
Olli Etuaho195be942017-12-04 23:40:14 +0200484 TIntermDeclaration *indexVariableDeclaration = nullptr;
485 TVariable *indexVariable = DeclareTempVariable(
486 mSymbolTable, indexInitializer, EvqTemporary, &indexVariableDeclaration);
487 insertionsBefore.push_back(indexVariableDeclaration);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300488
Olli Etuaho195be942017-12-04 23:40:14 +0200489 // s1 = dyn_index(v_expr, s0);
490 TIntermAggregate *indexingCall = CreateIndexFunctionCall(
Olli Etuaho0c371002017-12-13 17:00:25 +0400491 node, CreateTempSymbolNode(indexVariable), indexingFunction);
Olli Etuaho195be942017-12-04 23:40:14 +0200492 TIntermDeclaration *fieldVariableDeclaration = nullptr;
493 TVariable *fieldVariable = DeclareTempVariable(
494 mSymbolTable, indexingCall, EvqTemporary, &fieldVariableDeclaration);
495 insertionsBefore.push_back(fieldVariableDeclaration);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300496
Olli Etuaho195be942017-12-04 23:40:14 +0200497 // dyn_index_write(v_expr, s0, s1);
Olli Etuahofe486322017-03-21 09:30:54 +0000498 TIntermAggregate *indexedWriteCall = CreateIndexedWriteFunctionCall(
Olli Etuaho0c371002017-12-13 17:00:25 +0400499 node, indexVariable, fieldVariable, indexedWriteFunction);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300500 insertionsAfter.push_back(indexedWriteCall);
501 insertStatementsInParentBlock(insertionsBefore, insertionsAfter);
Olli Etuaho195be942017-12-04 23:40:14 +0200502
503 // replace the node with s1
504 queueReplacement(CreateTempSymbolNode(fieldVariable), OriginalNode::IS_DROPPED);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300505 mUsedTreeInsertion = true;
506 }
507 else
508 {
509 // The indexed value is not being written, so we can simply convert
510 // v_expr[index_expr]
511 // into
512 // dyn_index(v_expr, index_expr)
513 // If the index_expr is unsigned, we'll convert it to signed.
514 ASSERT(!mRemoveIndexSideEffectsInSubtree);
515 TIntermAggregate *indexingCall = CreateIndexFunctionCall(
Olli Etuaho0c371002017-12-13 17:00:25 +0400516 node, EnsureSignedInt(node->getRight()), indexingFunction);
Olli Etuahoea39a222017-07-06 12:47:59 +0300517 queueReplacement(indexingCall, OriginalNode::IS_DROPPED);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300518 }
519 }
520 }
521 return !mUsedTreeInsertion;
522}
523
524void RemoveDynamicIndexingTraverser::nextIteration()
525{
526 mUsedTreeInsertion = false;
527 mRemoveIndexSideEffectsInSubtree = false;
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300528}
529
530} // namespace
531
Olli Etuaho89a69a02017-10-23 12:20:45 +0300532void RemoveDynamicIndexing(TIntermNode *root,
533 TSymbolTable *symbolTable,
534 int shaderVersion,
535 PerformanceDiagnostics *perfDiagnostics)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300536{
Olli Etuaho89a69a02017-10-23 12:20:45 +0300537 RemoveDynamicIndexingTraverser traverser(symbolTable, shaderVersion, perfDiagnostics);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300538 do
539 {
540 traverser.nextIteration();
541 root->traverse(&traverser);
542 traverser.updateTree();
543 } while (traverser.usedTreeInsertion());
Olli Etuaho56612d62017-10-12 15:46:30 +0300544 // TODO(oetuaho@nvidia.com): It might be nicer to add the helper definitions also in the middle
Olli Etuaho1ecd14b2017-01-26 13:54:15 -0800545 // of traversal. Now the tree ends up in an inconsistent state in the middle, since there are
546 // function call nodes with no corresponding definition nodes. This needs special handling in
547 // TIntermLValueTrackingTraverser, and creates intricacies that are not easily apparent from a
548 // superficial reading of the code.
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300549 traverser.insertHelperDefinitions(root);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300550}
Jamie Madill45bcc782016-11-07 13:58:48 -0500551
552} // namespace sh