blob: 36300ed03d97e4182164137a9419129a90184df4 [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
240 // Two blocks: one accesses (either reads or writes) the first element and returns,
241 // the other accesses the last element.
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100242 TIntermBlock *useFirstBlock = new TIntermBlock();
243 TIntermBlock *useLastBlock = new TIntermBlock();
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300244 TIntermBinary *indexFirstNode =
Olli Etuahob79b3f92017-07-21 14:23:21 +0300245 new TIntermBinary(EOpIndexDirect, baseParam->deepCopy(), CreateIndexNode(0));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300246 TIntermBinary *indexLastNode =
Olli Etuahob79b3f92017-07-21 14:23:21 +0300247 new TIntermBinary(EOpIndexDirect, baseParam->deepCopy(), CreateIndexNode(numCases - 1));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300248 if (write)
249 {
Olli Etuahob79b3f92017-07-21 14:23:21 +0300250 TIntermBinary *assignFirstNode =
251 new TIntermBinary(EOpAssign, indexFirstNode, valueParam->deepCopy());
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300252 useFirstBlock->getSequence()->push_back(assignFirstNode);
253 TIntermBranch *returnNode = new TIntermBranch(EOpReturn, nullptr);
254 useFirstBlock->getSequence()->push_back(returnNode);
255
Olli Etuahob79b3f92017-07-21 14:23:21 +0300256 TIntermBinary *assignLastNode =
257 new TIntermBinary(EOpAssign, indexLastNode, valueParam->deepCopy());
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300258 useLastBlock->getSequence()->push_back(assignLastNode);
259 }
260 else
261 {
262 TIntermBranch *returnFirstNode = new TIntermBranch(EOpReturn, indexFirstNode);
263 useFirstBlock->getSequence()->push_back(returnFirstNode);
264
265 TIntermBranch *returnLastNode = new TIntermBranch(EOpReturn, indexLastNode);
266 useLastBlock->getSequence()->push_back(returnLastNode);
267 }
Olli Etuaho57961272016-09-14 13:57:46 +0300268 TIntermIfElse *ifNode = new TIntermIfElse(cond, useFirstBlock, nullptr);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300269 bodyNode->getSequence()->push_back(ifNode);
270 bodyNode->getSequence()->push_back(useLastBlock);
271
Olli Etuaho8ad9e752017-01-16 19:55:20 +0000272 TIntermFunctionDefinition *indexingFunction =
273 new TIntermFunctionDefinition(prototypeNode, bodyNode);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300274 return indexingFunction;
275}
276
277class RemoveDynamicIndexingTraverser : public TLValueTrackingTraverser
278{
279 public:
Olli Etuaho89a69a02017-10-23 12:20:45 +0300280 RemoveDynamicIndexingTraverser(TSymbolTable *symbolTable,
281 int shaderVersion,
282 PerformanceDiagnostics *perfDiagnostics);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300283
284 bool visitBinary(Visit visit, TIntermBinary *node) override;
285
286 void insertHelperDefinitions(TIntermNode *root);
287
288 void nextIteration();
289
290 bool usedTreeInsertion() const { return mUsedTreeInsertion; }
291
292 protected:
Olli Etuahofe486322017-03-21 09:30:54 +0000293 // Maps of types that are indexed to the indexing function ids used for them. Note that these
294 // can not store multiple variants of the same type with different precisions - only one
295 // precision gets stored.
Olli Etuaho0c371002017-12-13 17:00:25 +0400296 std::map<TType, TFunction *> mIndexedVecAndMatrixTypes;
297 std::map<TType, TFunction *> mWrittenVecAndMatrixTypes;
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300298
299 bool mUsedTreeInsertion;
300
301 // When true, the traverser will remove side effects from any indexing expression.
302 // This is done so that in code like
303 // V[j++][i]++.
304 // where V is an array of vectors, j++ will only be evaluated once.
305 bool mRemoveIndexSideEffectsInSubtree;
Olli Etuaho89a69a02017-10-23 12:20:45 +0300306
307 PerformanceDiagnostics *mPerfDiagnostics;
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300308};
309
Olli Etuaho89a69a02017-10-23 12:20:45 +0300310RemoveDynamicIndexingTraverser::RemoveDynamicIndexingTraverser(
311 TSymbolTable *symbolTable,
312 int shaderVersion,
313 PerformanceDiagnostics *perfDiagnostics)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300314 : TLValueTrackingTraverser(true, false, false, symbolTable, shaderVersion),
315 mUsedTreeInsertion(false),
Olli Etuaho89a69a02017-10-23 12:20:45 +0300316 mRemoveIndexSideEffectsInSubtree(false),
317 mPerfDiagnostics(perfDiagnostics)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300318{
319}
320
321void RemoveDynamicIndexingTraverser::insertHelperDefinitions(TIntermNode *root)
322{
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100323 TIntermBlock *rootBlock = root->getAsBlock();
324 ASSERT(rootBlock != nullptr);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300325 TIntermSequence insertions;
Olli Etuahofe486322017-03-21 09:30:54 +0000326 for (auto &type : mIndexedVecAndMatrixTypes)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300327 {
Olli Etuahob79b3f92017-07-21 14:23:21 +0300328 insertions.push_back(
329 GetIndexFunctionDefinition(type.first, false, *type.second, mSymbolTable));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300330 }
Olli Etuahofe486322017-03-21 09:30:54 +0000331 for (auto &type : mWrittenVecAndMatrixTypes)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300332 {
Olli Etuahob79b3f92017-07-21 14:23:21 +0300333 insertions.push_back(
334 GetIndexFunctionDefinition(type.first, true, *type.second, mSymbolTable));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300335 }
Olli Etuaho56612d62017-10-12 15:46:30 +0300336 rootBlock->insertChildNodes(0, insertions);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300337}
338
339// Create a call to dyn_index_*() based on an indirect indexing op node
340TIntermAggregate *CreateIndexFunctionCall(TIntermBinary *node,
Olli Etuahofe486322017-03-21 09:30:54 +0000341 TIntermTyped *index,
Olli Etuaho0c371002017-12-13 17:00:25 +0400342 TFunction *indexingFunction)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300343{
344 ASSERT(node->getOp() == EOpIndexIndirect);
Olli Etuahoaf6fc1b2017-01-26 17:45:35 -0800345 TIntermSequence *arguments = new TIntermSequence();
Olli Etuahofe486322017-03-21 09:30:54 +0000346 arguments->push_back(node->getLeft());
Olli Etuahoaf6fc1b2017-01-26 17:45:35 -0800347 arguments->push_back(index);
348
Olli Etuaho3ec75682017-07-05 17:02:55 +0300349 TIntermAggregate *indexingCall =
Olli Etuaho0c371002017-12-13 17:00:25 +0400350 TIntermAggregate::CreateFunctionCall(*indexingFunction, arguments);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300351 indexingCall->setLine(node->getLine());
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300352 return indexingCall;
353}
354
355TIntermAggregate *CreateIndexedWriteFunctionCall(TIntermBinary *node,
Olli Etuaho195be942017-12-04 23:40:14 +0200356 TVariable *index,
357 TVariable *writtenValue,
Olli Etuaho0c371002017-12-13 17:00:25 +0400358 TFunction *indexedWriteFunction)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300359{
Olli Etuahofe486322017-03-21 09:30:54 +0000360 ASSERT(node->getOp() == EOpIndexIndirect);
361 TIntermSequence *arguments = new TIntermSequence();
362 // Deep copy the child nodes so that two pointers to the same node don't end up in the tree.
363 arguments->push_back(node->getLeft()->deepCopy());
Olli Etuaho195be942017-12-04 23:40:14 +0200364 arguments->push_back(CreateTempSymbolNode(index));
365 arguments->push_back(CreateTempSymbolNode(writtenValue));
Olli Etuahofe486322017-03-21 09:30:54 +0000366
Olli Etuaho3ec75682017-07-05 17:02:55 +0300367 TIntermAggregate *indexedWriteCall =
Olli Etuaho0c371002017-12-13 17:00:25 +0400368 TIntermAggregate::CreateFunctionCall(*indexedWriteFunction, arguments);
Olli Etuahofe486322017-03-21 09:30:54 +0000369 indexedWriteCall->setLine(node->getLine());
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300370 return indexedWriteCall;
371}
372
373bool RemoveDynamicIndexingTraverser::visitBinary(Visit visit, TIntermBinary *node)
374{
375 if (mUsedTreeInsertion)
376 return false;
377
378 if (node->getOp() == EOpIndexIndirect)
379 {
380 if (mRemoveIndexSideEffectsInSubtree)
381 {
382 ASSERT(node->getRight()->hasSideEffects());
383 // In case we're just removing index side effects, convert
384 // v_expr[index_expr]
385 // to this:
386 // int s0 = index_expr; v_expr[s0];
387 // Now v_expr[s0] can be safely executed several times without unintended side effects.
Olli Etuaho195be942017-12-04 23:40:14 +0200388 TIntermDeclaration *indexVariableDeclaration = nullptr;
389 TVariable *indexVariable = DeclareTempVariable(mSymbolTable, node->getRight(),
390 EvqTemporary, &indexVariableDeclaration);
391 insertStatementInParentBlock(indexVariableDeclaration);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300392 mUsedTreeInsertion = true;
393
394 // Replace the index with the temp variable
Olli Etuaho195be942017-12-04 23:40:14 +0200395 TIntermSymbol *tempIndex = CreateTempSymbolNode(indexVariable);
Jamie Madill03d863c2016-07-27 18:15:53 -0400396 queueReplacementWithParent(node, node->getRight(), tempIndex, OriginalNode::IS_DROPPED);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300397 }
Jamie Madill666f65a2016-08-26 01:34:37 +0000398 else if (IntermNodePatternMatcher::IsDynamicIndexingOfVectorOrMatrix(node))
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300399 {
Olli Etuaho89a69a02017-10-23 12:20:45 +0300400 mPerfDiagnostics->warning(node->getLine(),
401 "Performance: dynamic indexing of vectors and "
402 "matrices is emulated and can be slow.",
403 "[]");
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300404 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 Etuahofe486322017-03-21 09:30:54 +0000414 const TType &type = node->getLeft()->getType();
Olli Etuaho0c371002017-12-13 17:00:25 +0400415 TString *indexingFunctionName =
416 NewPoolTString(GetIndexFunctionName(type, false).c_str());
417 TFunction *indexingFunction = nullptr;
Olli Etuahofe486322017-03-21 09:30:54 +0000418 if (mIndexedVecAndMatrixTypes.find(type) == mIndexedVecAndMatrixTypes.end())
419 {
Olli Etuaho0c371002017-12-13 17:00:25 +0400420 indexingFunction =
421 new TFunction(mSymbolTable, indexingFunctionName, GetFieldType(type),
422 SymbolType::AngleInternal, true);
423 mIndexedVecAndMatrixTypes[type] = indexingFunction;
Olli Etuahofe486322017-03-21 09:30:54 +0000424 }
425 else
426 {
Olli Etuaho0c371002017-12-13 17:00:25 +0400427 indexingFunction = mIndexedVecAndMatrixTypes[type];
Olli Etuahofe486322017-03-21 09:30:54 +0000428 }
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300429
430 if (write)
431 {
432 // Convert:
433 // v_expr[index_expr]++;
434 // to this:
435 // int s0 = index_expr; float s1 = dyn_index(v_expr, s0); s1++;
436 // dyn_index_write(v_expr, s0, s1);
437 // This works even if index_expr has some side effects.
438 if (node->getLeft()->hasSideEffects())
439 {
440 // If v_expr has side effects, those need to be removed before proceeding.
441 // Otherwise the side effects of v_expr would be evaluated twice.
442 // The only case where an l-value can have side effects is when it is
443 // indexing. For example, it can be V[j++] where V is an array of vectors.
444 mRemoveIndexSideEffectsInSubtree = true;
445 return true;
446 }
Olli Etuaho8f6eb2a2017-01-12 17:04:58 +0000447
448 TIntermBinary *leftBinary = node->getLeft()->getAsBinaryNode();
449 if (leftBinary != nullptr &&
450 IntermNodePatternMatcher::IsDynamicIndexingOfVectorOrMatrix(leftBinary))
451 {
452 // This is a case like:
453 // mat2 m;
454 // m[a][b]++;
455 // Process the child node m[a] first.
456 return true;
457 }
458
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300459 // TODO(oetuaho@nvidia.com): This is not optimal if the expression using the value
460 // only writes it and doesn't need the previous value. http://anglebug.com/1116
461
Olli Etuaho0c371002017-12-13 17:00:25 +0400462 TFunction *indexedWriteFunction = nullptr;
Olli Etuahofe486322017-03-21 09:30:54 +0000463 if (mWrittenVecAndMatrixTypes.find(type) == mWrittenVecAndMatrixTypes.end())
464 {
Olli Etuaho0c371002017-12-13 17:00:25 +0400465 TString *functionName = NewPoolTString(
466 GetIndexFunctionName(node->getLeft()->getType(), true).c_str());
467 indexedWriteFunction =
468 new TFunction(mSymbolTable, functionName, new TType(EbtVoid),
469 SymbolType::AngleInternal, false);
470 mWrittenVecAndMatrixTypes[type] = indexedWriteFunction;
Olli Etuahofe486322017-03-21 09:30:54 +0000471 }
472 else
473 {
Olli Etuaho0c371002017-12-13 17:00:25 +0400474 indexedWriteFunction = mWrittenVecAndMatrixTypes[type];
Olli Etuahofe486322017-03-21 09:30:54 +0000475 }
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300476
477 TIntermSequence insertionsBefore;
478 TIntermSequence insertionsAfter;
479
480 // Store the index in a temporary signed int variable.
Olli Etuaho195be942017-12-04 23:40:14 +0200481 // s0 = index_expr;
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300482 TIntermTyped *indexInitializer = EnsureSignedInt(node->getRight());
Olli Etuaho195be942017-12-04 23:40:14 +0200483 TIntermDeclaration *indexVariableDeclaration = nullptr;
484 TVariable *indexVariable = DeclareTempVariable(
485 mSymbolTable, indexInitializer, EvqTemporary, &indexVariableDeclaration);
486 insertionsBefore.push_back(indexVariableDeclaration);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300487
Olli Etuaho195be942017-12-04 23:40:14 +0200488 // s1 = dyn_index(v_expr, s0);
489 TIntermAggregate *indexingCall = CreateIndexFunctionCall(
Olli Etuaho0c371002017-12-13 17:00:25 +0400490 node, CreateTempSymbolNode(indexVariable), indexingFunction);
Olli Etuaho195be942017-12-04 23:40:14 +0200491 TIntermDeclaration *fieldVariableDeclaration = nullptr;
492 TVariable *fieldVariable = DeclareTempVariable(
493 mSymbolTable, indexingCall, EvqTemporary, &fieldVariableDeclaration);
494 insertionsBefore.push_back(fieldVariableDeclaration);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300495
Olli Etuaho195be942017-12-04 23:40:14 +0200496 // dyn_index_write(v_expr, s0, s1);
Olli Etuahofe486322017-03-21 09:30:54 +0000497 TIntermAggregate *indexedWriteCall = CreateIndexedWriteFunctionCall(
Olli Etuaho0c371002017-12-13 17:00:25 +0400498 node, indexVariable, fieldVariable, indexedWriteFunction);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300499 insertionsAfter.push_back(indexedWriteCall);
500 insertStatementsInParentBlock(insertionsBefore, insertionsAfter);
Olli Etuaho195be942017-12-04 23:40:14 +0200501
502 // replace the node with s1
503 queueReplacement(CreateTempSymbolNode(fieldVariable), 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 Etuaho0c371002017-12-13 17:00:25 +0400515 node, EnsureSignedInt(node->getRight()), indexingFunction);
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