blob: 184c711872c93b5f109f62771f2e95220e7a4d7b [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 Etuahob60d30f2018-01-16 12:31:06 +020017#include "compiler/translator/StaticType.h"
Olli Etuaho5d91dda2015-06-18 15:47:46 +030018#include "compiler/translator/SymbolTable.h"
19
Jamie Madill45bcc782016-11-07 13:58:48 -050020namespace sh
21{
22
Olli Etuaho5d91dda2015-06-18 15:47:46 +030023namespace
24{
25
Olli Etuaho68981eb2018-01-23 17:46:12 +020026const TType *kIndexType = StaticType::Get<EbtInt, EbpHigh, EvqIn, 1, 1>();
27
Olli Etuahofe486322017-03-21 09:30:54 +000028std::string GetIndexFunctionName(const TType &type, bool write)
Olli Etuaho5d91dda2015-06-18 15:47:46 +030029{
30 TInfoSinkBase nameSink;
31 nameSink << "dyn_index_";
32 if (write)
33 {
34 nameSink << "write_";
35 }
36 if (type.isMatrix())
37 {
38 nameSink << "mat" << type.getCols() << "x" << type.getRows();
39 }
40 else
41 {
42 switch (type.getBasicType())
43 {
44 case EbtInt:
45 nameSink << "ivec";
46 break;
47 case EbtBool:
48 nameSink << "bvec";
49 break;
50 case EbtUInt:
51 nameSink << "uvec";
52 break;
53 case EbtFloat:
54 nameSink << "vec";
55 break;
56 default:
57 UNREACHABLE();
58 }
59 nameSink << type.getNominalSize();
60 }
Olli Etuahofe486322017-03-21 09:30:54 +000061 return nameSink.str();
Olli Etuaho5d91dda2015-06-18 15:47:46 +030062}
63
Olli Etuaho68981eb2018-01-23 17:46:12 +020064TIntermSymbol *CreateParameterSymbol(const TConstParameter &parameter, TSymbolTable *symbolTable)
Olli Etuaho5d91dda2015-06-18 15:47:46 +030065{
Olli Etuaho68981eb2018-01-23 17:46:12 +020066 TVariable *variable =
67 new TVariable(symbolTable, parameter.name, parameter.type, SymbolType::AngleInternal);
68 return new TIntermSymbol(variable);
Olli Etuaho5d91dda2015-06-18 15:47:46 +030069}
70
71TIntermConstantUnion *CreateIntConstantNode(int i)
72{
73 TConstantUnion *constant = new TConstantUnion();
74 constant->setIConst(i);
75 return new TIntermConstantUnion(constant, TType(EbtInt, EbpHigh));
76}
77
Olli Etuaho5d91dda2015-06-18 15:47:46 +030078TIntermTyped *EnsureSignedInt(TIntermTyped *node)
79{
80 if (node->getBasicType() == EbtInt)
81 return node;
82
Olli Etuahoaf6fc1b2017-01-26 17:45:35 -080083 TIntermSequence *arguments = new TIntermSequence();
84 arguments->push_back(node);
Olli Etuahoa7ecec32017-05-08 17:43:55 +030085 return TIntermAggregate::CreateConstructor(TType(EbtInt), arguments);
Olli Etuaho5d91dda2015-06-18 15:47:46 +030086}
87
Olli Etuaho0c371002017-12-13 17:00:25 +040088TType *GetFieldType(const TType &indexedType)
Olli Etuaho5d91dda2015-06-18 15:47:46 +030089{
90 if (indexedType.isMatrix())
91 {
Olli Etuaho0c371002017-12-13 17:00:25 +040092 TType *fieldType = new TType(indexedType.getBasicType(), indexedType.getPrecision());
93 fieldType->setPrimarySize(static_cast<unsigned char>(indexedType.getRows()));
Olli Etuaho5d91dda2015-06-18 15:47:46 +030094 return fieldType;
95 }
96 else
97 {
Olli Etuaho0c371002017-12-13 17:00:25 +040098 return new TType(indexedType.getBasicType(), indexedType.getPrecision());
Olli Etuaho5d91dda2015-06-18 15:47:46 +030099 }
100}
101
Olli Etuaho68981eb2018-01-23 17:46:12 +0200102const TType *GetBaseType(const TType &type, bool write)
103{
104 TType *baseType = new TType(type);
105 // Conservatively use highp here, even if the indexed type is not highp. That way the code can't
106 // end up using mediump version of an indexing function for a highp value, if both mediump and
107 // highp values are being indexed in the shader. For HLSL precision doesn't matter, but in
108 // principle this code could be used with multiple backends.
109 baseType->setPrecision(EbpHigh);
110 baseType->setQualifier(EvqInOut);
111 if (!write)
112 baseType->setQualifier(EvqIn);
113 return baseType;
114}
115
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300116// Generate a read or write function for one field in a vector/matrix.
117// Out-of-range indices are clamped. This is consistent with how ANGLE handles out-of-range
118// indices in other places.
119// Note that indices can be either int or uint. We create only int versions of the functions,
120// and convert uint indices to int at the call site.
121// read function example:
122// float dyn_index_vec2(in vec2 base, in int index)
123// {
124// switch(index)
125// {
126// case (0):
127// return base[0];
128// case (1):
129// return base[1];
130// default:
131// break;
132// }
133// if (index < 0)
134// return base[0];
135// return base[1];
136// }
137// write function example:
138// void dyn_index_write_vec2(inout vec2 base, in int index, in float value)
139// {
140// switch(index)
141// {
142// case (0):
143// base[0] = value;
144// return;
145// case (1):
146// base[1] = value;
147// return;
148// default:
149// break;
150// }
151// if (index < 0)
152// {
153// base[0] = value;
154// return;
155// }
156// base[1] = value;
157// }
158// Note that else is not used in above functions to avoid the RewriteElseBlocks transformation.
Olli Etuaho195be942017-12-04 23:40:14 +0200159TIntermFunctionDefinition *GetIndexFunctionDefinition(const TType &type,
Olli Etuahofe486322017-03-21 09:30:54 +0000160 bool write,
Olli Etuaho0c371002017-12-13 17:00:25 +0400161 const TFunction &func,
Olli Etuahob79b3f92017-07-21 14:23:21 +0300162 TSymbolTable *symbolTable)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300163{
164 ASSERT(!type.isArray());
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300165
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500166 int numCases = 0;
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300167 if (type.isMatrix())
168 {
169 numCases = type.getCols();
170 }
171 else
172 {
173 numCases = type.getNominalSize();
174 }
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300175
Olli Etuahofe486322017-03-21 09:30:54 +0000176 std::string functionName = GetIndexFunctionName(type, write);
Olli Etuaho0c371002017-12-13 17:00:25 +0400177 TIntermFunctionPrototype *prototypeNode = CreateInternalFunctionPrototypeNode(func);
Olli Etuaho8ad9e752017-01-16 19:55:20 +0000178
Olli Etuaho68981eb2018-01-23 17:46:12 +0200179 TIntermSymbol *baseParam = CreateParameterSymbol(func.getParam(0), symbolTable);
Olli Etuaho8ad9e752017-01-16 19:55:20 +0000180 prototypeNode->getSequence()->push_back(baseParam);
Olli Etuaho68981eb2018-01-23 17:46:12 +0200181 TIntermSymbol *indexParam = CreateParameterSymbol(func.getParam(1), symbolTable);
Olli Etuaho8ad9e752017-01-16 19:55:20 +0000182 prototypeNode->getSequence()->push_back(indexParam);
Olli Etuahob79b3f92017-07-21 14:23:21 +0300183 TIntermSymbol *valueParam = nullptr;
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300184 if (write)
185 {
Olli Etuaho68981eb2018-01-23 17:46:12 +0200186 valueParam = CreateParameterSymbol(func.getParam(2), symbolTable);
Olli Etuaho8ad9e752017-01-16 19:55:20 +0000187 prototypeNode->getSequence()->push_back(valueParam);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300188 }
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300189
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100190 TIntermBlock *statementList = new TIntermBlock();
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300191 for (int i = 0; i < numCases; ++i)
192 {
193 TIntermCase *caseNode = new TIntermCase(CreateIntConstantNode(i));
194 statementList->getSequence()->push_back(caseNode);
195
196 TIntermBinary *indexNode =
Olli Etuahob79b3f92017-07-21 14:23:21 +0300197 new TIntermBinary(EOpIndexDirect, baseParam->deepCopy(), CreateIndexNode(i));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300198 if (write)
199 {
Olli Etuahob79b3f92017-07-21 14:23:21 +0300200 TIntermBinary *assignNode =
201 new TIntermBinary(EOpAssign, indexNode, valueParam->deepCopy());
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300202 statementList->getSequence()->push_back(assignNode);
203 TIntermBranch *returnNode = new TIntermBranch(EOpReturn, nullptr);
204 statementList->getSequence()->push_back(returnNode);
205 }
206 else
207 {
208 TIntermBranch *returnNode = new TIntermBranch(EOpReturn, indexNode);
209 statementList->getSequence()->push_back(returnNode);
210 }
211 }
212
213 // Default case
214 TIntermCase *defaultNode = new TIntermCase(nullptr);
215 statementList->getSequence()->push_back(defaultNode);
216 TIntermBranch *breakNode = new TIntermBranch(EOpBreak, nullptr);
217 statementList->getSequence()->push_back(breakNode);
218
Olli Etuahob79b3f92017-07-21 14:23:21 +0300219 TIntermSwitch *switchNode = new TIntermSwitch(indexParam->deepCopy(), statementList);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300220
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100221 TIntermBlock *bodyNode = new TIntermBlock();
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300222 bodyNode->getSequence()->push_back(switchNode);
223
Olli Etuaho3272a6d2016-08-29 17:54:50 +0300224 TIntermBinary *cond =
Olli Etuahob79b3f92017-07-21 14:23:21 +0300225 new TIntermBinary(EOpLessThan, indexParam->deepCopy(), CreateIntConstantNode(0));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300226
227 // Two blocks: one accesses (either reads or writes) the first element and returns,
228 // the other accesses the last element.
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100229 TIntermBlock *useFirstBlock = new TIntermBlock();
230 TIntermBlock *useLastBlock = new TIntermBlock();
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300231 TIntermBinary *indexFirstNode =
Olli Etuahob79b3f92017-07-21 14:23:21 +0300232 new TIntermBinary(EOpIndexDirect, baseParam->deepCopy(), CreateIndexNode(0));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300233 TIntermBinary *indexLastNode =
Olli Etuahob79b3f92017-07-21 14:23:21 +0300234 new TIntermBinary(EOpIndexDirect, baseParam->deepCopy(), CreateIndexNode(numCases - 1));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300235 if (write)
236 {
Olli Etuahob79b3f92017-07-21 14:23:21 +0300237 TIntermBinary *assignFirstNode =
238 new TIntermBinary(EOpAssign, indexFirstNode, valueParam->deepCopy());
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300239 useFirstBlock->getSequence()->push_back(assignFirstNode);
240 TIntermBranch *returnNode = new TIntermBranch(EOpReturn, nullptr);
241 useFirstBlock->getSequence()->push_back(returnNode);
242
Olli Etuahob79b3f92017-07-21 14:23:21 +0300243 TIntermBinary *assignLastNode =
244 new TIntermBinary(EOpAssign, indexLastNode, valueParam->deepCopy());
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300245 useLastBlock->getSequence()->push_back(assignLastNode);
246 }
247 else
248 {
249 TIntermBranch *returnFirstNode = new TIntermBranch(EOpReturn, indexFirstNode);
250 useFirstBlock->getSequence()->push_back(returnFirstNode);
251
252 TIntermBranch *returnLastNode = new TIntermBranch(EOpReturn, indexLastNode);
253 useLastBlock->getSequence()->push_back(returnLastNode);
254 }
Olli Etuaho57961272016-09-14 13:57:46 +0300255 TIntermIfElse *ifNode = new TIntermIfElse(cond, useFirstBlock, nullptr);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300256 bodyNode->getSequence()->push_back(ifNode);
257 bodyNode->getSequence()->push_back(useLastBlock);
258
Olli Etuaho8ad9e752017-01-16 19:55:20 +0000259 TIntermFunctionDefinition *indexingFunction =
260 new TIntermFunctionDefinition(prototypeNode, bodyNode);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300261 return indexingFunction;
262}
263
264class RemoveDynamicIndexingTraverser : public TLValueTrackingTraverser
265{
266 public:
Olli Etuaho89a69a02017-10-23 12:20:45 +0300267 RemoveDynamicIndexingTraverser(TSymbolTable *symbolTable,
Olli Etuaho89a69a02017-10-23 12:20:45 +0300268 PerformanceDiagnostics *perfDiagnostics);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300269
270 bool visitBinary(Visit visit, TIntermBinary *node) override;
271
272 void insertHelperDefinitions(TIntermNode *root);
273
274 void nextIteration();
275
276 bool usedTreeInsertion() const { return mUsedTreeInsertion; }
277
278 protected:
Olli Etuahofe486322017-03-21 09:30:54 +0000279 // Maps of types that are indexed to the indexing function ids used for them. Note that these
280 // can not store multiple variants of the same type with different precisions - only one
281 // precision gets stored.
Olli Etuaho0c371002017-12-13 17:00:25 +0400282 std::map<TType, TFunction *> mIndexedVecAndMatrixTypes;
283 std::map<TType, TFunction *> mWrittenVecAndMatrixTypes;
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300284
285 bool mUsedTreeInsertion;
286
287 // When true, the traverser will remove side effects from any indexing expression.
288 // This is done so that in code like
289 // V[j++][i]++.
290 // where V is an array of vectors, j++ will only be evaluated once.
291 bool mRemoveIndexSideEffectsInSubtree;
Olli Etuaho89a69a02017-10-23 12:20:45 +0300292
293 PerformanceDiagnostics *mPerfDiagnostics;
Olli Etuaho68981eb2018-01-23 17:46:12 +0200294
295 const TString *mBaseName;
296 const TString *mIndexName;
297 const TString *mValueName;
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300298};
299
Olli Etuaho89a69a02017-10-23 12:20:45 +0300300RemoveDynamicIndexingTraverser::RemoveDynamicIndexingTraverser(
301 TSymbolTable *symbolTable,
Olli Etuaho89a69a02017-10-23 12:20:45 +0300302 PerformanceDiagnostics *perfDiagnostics)
Olli Etuaho68981eb2018-01-23 17:46:12 +0200303 : TLValueTrackingTraverser(true, false, false, symbolTable),
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300304 mUsedTreeInsertion(false),
Olli Etuaho89a69a02017-10-23 12:20:45 +0300305 mRemoveIndexSideEffectsInSubtree(false),
Olli Etuaho68981eb2018-01-23 17:46:12 +0200306 mPerfDiagnostics(perfDiagnostics),
307 mBaseName(NewPoolTString("base")),
308 mIndexName(NewPoolTString("index")),
309 mValueName(NewPoolTString("value"))
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300310{
311}
312
313void RemoveDynamicIndexingTraverser::insertHelperDefinitions(TIntermNode *root)
314{
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100315 TIntermBlock *rootBlock = root->getAsBlock();
316 ASSERT(rootBlock != nullptr);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300317 TIntermSequence insertions;
Olli Etuahofe486322017-03-21 09:30:54 +0000318 for (auto &type : mIndexedVecAndMatrixTypes)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300319 {
Olli Etuahob79b3f92017-07-21 14:23:21 +0300320 insertions.push_back(
321 GetIndexFunctionDefinition(type.first, false, *type.second, mSymbolTable));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300322 }
Olli Etuahofe486322017-03-21 09:30:54 +0000323 for (auto &type : mWrittenVecAndMatrixTypes)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300324 {
Olli Etuahob79b3f92017-07-21 14:23:21 +0300325 insertions.push_back(
326 GetIndexFunctionDefinition(type.first, true, *type.second, mSymbolTable));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300327 }
Olli Etuaho56612d62017-10-12 15:46:30 +0300328 rootBlock->insertChildNodes(0, insertions);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300329}
330
331// Create a call to dyn_index_*() based on an indirect indexing op node
332TIntermAggregate *CreateIndexFunctionCall(TIntermBinary *node,
Olli Etuahofe486322017-03-21 09:30:54 +0000333 TIntermTyped *index,
Olli Etuaho0c371002017-12-13 17:00:25 +0400334 TFunction *indexingFunction)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300335{
336 ASSERT(node->getOp() == EOpIndexIndirect);
Olli Etuahoaf6fc1b2017-01-26 17:45:35 -0800337 TIntermSequence *arguments = new TIntermSequence();
Olli Etuahofe486322017-03-21 09:30:54 +0000338 arguments->push_back(node->getLeft());
Olli Etuahoaf6fc1b2017-01-26 17:45:35 -0800339 arguments->push_back(index);
340
Olli Etuaho3ec75682017-07-05 17:02:55 +0300341 TIntermAggregate *indexingCall =
Olli Etuaho0c371002017-12-13 17:00:25 +0400342 TIntermAggregate::CreateFunctionCall(*indexingFunction, arguments);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300343 indexingCall->setLine(node->getLine());
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300344 return indexingCall;
345}
346
347TIntermAggregate *CreateIndexedWriteFunctionCall(TIntermBinary *node,
Olli Etuaho195be942017-12-04 23:40:14 +0200348 TVariable *index,
349 TVariable *writtenValue,
Olli Etuaho0c371002017-12-13 17:00:25 +0400350 TFunction *indexedWriteFunction)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300351{
Olli Etuahofe486322017-03-21 09:30:54 +0000352 ASSERT(node->getOp() == EOpIndexIndirect);
353 TIntermSequence *arguments = new TIntermSequence();
354 // Deep copy the child nodes so that two pointers to the same node don't end up in the tree.
355 arguments->push_back(node->getLeft()->deepCopy());
Olli Etuaho195be942017-12-04 23:40:14 +0200356 arguments->push_back(CreateTempSymbolNode(index));
357 arguments->push_back(CreateTempSymbolNode(writtenValue));
Olli Etuahofe486322017-03-21 09:30:54 +0000358
Olli Etuaho3ec75682017-07-05 17:02:55 +0300359 TIntermAggregate *indexedWriteCall =
Olli Etuaho0c371002017-12-13 17:00:25 +0400360 TIntermAggregate::CreateFunctionCall(*indexedWriteFunction, arguments);
Olli Etuahofe486322017-03-21 09:30:54 +0000361 indexedWriteCall->setLine(node->getLine());
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300362 return indexedWriteCall;
363}
364
365bool RemoveDynamicIndexingTraverser::visitBinary(Visit visit, TIntermBinary *node)
366{
367 if (mUsedTreeInsertion)
368 return false;
369
370 if (node->getOp() == EOpIndexIndirect)
371 {
372 if (mRemoveIndexSideEffectsInSubtree)
373 {
374 ASSERT(node->getRight()->hasSideEffects());
375 // In case we're just removing index side effects, convert
376 // v_expr[index_expr]
377 // to this:
378 // int s0 = index_expr; v_expr[s0];
379 // Now v_expr[s0] can be safely executed several times without unintended side effects.
Olli Etuaho195be942017-12-04 23:40:14 +0200380 TIntermDeclaration *indexVariableDeclaration = nullptr;
381 TVariable *indexVariable = DeclareTempVariable(mSymbolTable, node->getRight(),
382 EvqTemporary, &indexVariableDeclaration);
383 insertStatementInParentBlock(indexVariableDeclaration);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300384 mUsedTreeInsertion = true;
385
386 // Replace the index with the temp variable
Olli Etuaho195be942017-12-04 23:40:14 +0200387 TIntermSymbol *tempIndex = CreateTempSymbolNode(indexVariable);
Jamie Madill03d863c2016-07-27 18:15:53 -0400388 queueReplacementWithParent(node, node->getRight(), tempIndex, OriginalNode::IS_DROPPED);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300389 }
Jamie Madill666f65a2016-08-26 01:34:37 +0000390 else if (IntermNodePatternMatcher::IsDynamicIndexingOfVectorOrMatrix(node))
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300391 {
Olli Etuaho89a69a02017-10-23 12:20:45 +0300392 mPerfDiagnostics->warning(node->getLine(),
393 "Performance: dynamic indexing of vectors and "
394 "matrices is emulated and can be slow.",
395 "[]");
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300396 bool write = isLValueRequiredHere();
397
Jamie Madill666f65a2016-08-26 01:34:37 +0000398#if defined(ANGLE_ENABLE_ASSERTS)
399 // Make sure that IntermNodePatternMatcher is consistent with the slightly differently
400 // implemented checks in this traverser.
401 IntermNodePatternMatcher matcher(
402 IntermNodePatternMatcher::kDynamicIndexingOfVectorOrMatrixInLValue);
403 ASSERT(matcher.match(node, getParentNode(), isLValueRequiredHere()) == write);
404#endif
405
Olli Etuahofe486322017-03-21 09:30:54 +0000406 const TType &type = node->getLeft()->getType();
Olli Etuaho0c371002017-12-13 17:00:25 +0400407 TString *indexingFunctionName =
408 NewPoolTString(GetIndexFunctionName(type, false).c_str());
409 TFunction *indexingFunction = nullptr;
Olli Etuahofe486322017-03-21 09:30:54 +0000410 if (mIndexedVecAndMatrixTypes.find(type) == mIndexedVecAndMatrixTypes.end())
411 {
Olli Etuaho0c371002017-12-13 17:00:25 +0400412 indexingFunction =
413 new TFunction(mSymbolTable, indexingFunctionName, GetFieldType(type),
414 SymbolType::AngleInternal, true);
Olli Etuaho68981eb2018-01-23 17:46:12 +0200415 indexingFunction->addParameter(
416 TConstParameter(mBaseName, GetBaseType(type, false)));
417 indexingFunction->addParameter(TConstParameter(mIndexName, kIndexType));
Olli Etuaho0c371002017-12-13 17:00:25 +0400418 mIndexedVecAndMatrixTypes[type] = indexingFunction;
Olli Etuahofe486322017-03-21 09:30:54 +0000419 }
420 else
421 {
Olli Etuaho0c371002017-12-13 17:00:25 +0400422 indexingFunction = mIndexedVecAndMatrixTypes[type];
Olli Etuahofe486322017-03-21 09:30:54 +0000423 }
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300424
425 if (write)
426 {
427 // Convert:
428 // v_expr[index_expr]++;
429 // to this:
430 // int s0 = index_expr; float s1 = dyn_index(v_expr, s0); s1++;
431 // dyn_index_write(v_expr, s0, s1);
432 // This works even if index_expr has some side effects.
433 if (node->getLeft()->hasSideEffects())
434 {
435 // If v_expr has side effects, those need to be removed before proceeding.
436 // Otherwise the side effects of v_expr would be evaluated twice.
437 // The only case where an l-value can have side effects is when it is
438 // indexing. For example, it can be V[j++] where V is an array of vectors.
439 mRemoveIndexSideEffectsInSubtree = true;
440 return true;
441 }
Olli Etuaho8f6eb2a2017-01-12 17:04:58 +0000442
443 TIntermBinary *leftBinary = node->getLeft()->getAsBinaryNode();
444 if (leftBinary != nullptr &&
445 IntermNodePatternMatcher::IsDynamicIndexingOfVectorOrMatrix(leftBinary))
446 {
447 // This is a case like:
448 // mat2 m;
449 // m[a][b]++;
450 // Process the child node m[a] first.
451 return true;
452 }
453
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300454 // TODO(oetuaho@nvidia.com): This is not optimal if the expression using the value
455 // only writes it and doesn't need the previous value. http://anglebug.com/1116
456
Olli Etuaho0c371002017-12-13 17:00:25 +0400457 TFunction *indexedWriteFunction = nullptr;
Olli Etuahofe486322017-03-21 09:30:54 +0000458 if (mWrittenVecAndMatrixTypes.find(type) == mWrittenVecAndMatrixTypes.end())
459 {
Olli Etuaho0c371002017-12-13 17:00:25 +0400460 TString *functionName = NewPoolTString(
461 GetIndexFunctionName(node->getLeft()->getType(), true).c_str());
462 indexedWriteFunction =
Olli Etuaho68981eb2018-01-23 17:46:12 +0200463 new TFunction(mSymbolTable, functionName, StaticType::GetBasic<EbtVoid>(),
Olli Etuaho0c371002017-12-13 17:00:25 +0400464 SymbolType::AngleInternal, false);
Olli Etuaho68981eb2018-01-23 17:46:12 +0200465 indexedWriteFunction->addParameter(
466 TConstParameter(mBaseName, GetBaseType(type, true)));
467 indexedWriteFunction->addParameter(TConstParameter(mIndexName, kIndexType));
468 TType *valueType = GetFieldType(type);
469 valueType->setQualifier(EvqIn);
470 indexedWriteFunction->addParameter(
471 TConstParameter(mValueName, static_cast<const TType *>(valueType)));
Olli Etuaho0c371002017-12-13 17:00:25 +0400472 mWrittenVecAndMatrixTypes[type] = indexedWriteFunction;
Olli Etuahofe486322017-03-21 09:30:54 +0000473 }
474 else
475 {
Olli Etuaho0c371002017-12-13 17:00:25 +0400476 indexedWriteFunction = mWrittenVecAndMatrixTypes[type];
Olli Etuahofe486322017-03-21 09:30:54 +0000477 }
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300478
479 TIntermSequence insertionsBefore;
480 TIntermSequence insertionsAfter;
481
482 // Store the index in a temporary signed int variable.
Olli Etuaho195be942017-12-04 23:40:14 +0200483 // s0 = index_expr;
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300484 TIntermTyped *indexInitializer = EnsureSignedInt(node->getRight());
Olli Etuaho195be942017-12-04 23:40:14 +0200485 TIntermDeclaration *indexVariableDeclaration = nullptr;
486 TVariable *indexVariable = DeclareTempVariable(
487 mSymbolTable, indexInitializer, EvqTemporary, &indexVariableDeclaration);
488 insertionsBefore.push_back(indexVariableDeclaration);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300489
Olli Etuaho195be942017-12-04 23:40:14 +0200490 // s1 = dyn_index(v_expr, s0);
491 TIntermAggregate *indexingCall = CreateIndexFunctionCall(
Olli Etuaho0c371002017-12-13 17:00:25 +0400492 node, CreateTempSymbolNode(indexVariable), indexingFunction);
Olli Etuaho195be942017-12-04 23:40:14 +0200493 TIntermDeclaration *fieldVariableDeclaration = nullptr;
494 TVariable *fieldVariable = DeclareTempVariable(
495 mSymbolTable, indexingCall, EvqTemporary, &fieldVariableDeclaration);
496 insertionsBefore.push_back(fieldVariableDeclaration);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300497
Olli Etuaho195be942017-12-04 23:40:14 +0200498 // dyn_index_write(v_expr, s0, s1);
Olli Etuahofe486322017-03-21 09:30:54 +0000499 TIntermAggregate *indexedWriteCall = CreateIndexedWriteFunctionCall(
Olli Etuaho0c371002017-12-13 17:00:25 +0400500 node, indexVariable, fieldVariable, indexedWriteFunction);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300501 insertionsAfter.push_back(indexedWriteCall);
502 insertStatementsInParentBlock(insertionsBefore, insertionsAfter);
Olli Etuaho195be942017-12-04 23:40:14 +0200503
504 // replace the node with s1
505 queueReplacement(CreateTempSymbolNode(fieldVariable), OriginalNode::IS_DROPPED);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300506 mUsedTreeInsertion = true;
507 }
508 else
509 {
510 // The indexed value is not being written, so we can simply convert
511 // v_expr[index_expr]
512 // into
513 // dyn_index(v_expr, index_expr)
514 // If the index_expr is unsigned, we'll convert it to signed.
515 ASSERT(!mRemoveIndexSideEffectsInSubtree);
516 TIntermAggregate *indexingCall = CreateIndexFunctionCall(
Olli Etuaho0c371002017-12-13 17:00:25 +0400517 node, EnsureSignedInt(node->getRight()), indexingFunction);
Olli Etuahoea39a222017-07-06 12:47:59 +0300518 queueReplacement(indexingCall, OriginalNode::IS_DROPPED);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300519 }
520 }
521 }
522 return !mUsedTreeInsertion;
523}
524
525void RemoveDynamicIndexingTraverser::nextIteration()
526{
527 mUsedTreeInsertion = false;
528 mRemoveIndexSideEffectsInSubtree = false;
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300529}
530
531} // namespace
532
Olli Etuaho89a69a02017-10-23 12:20:45 +0300533void RemoveDynamicIndexing(TIntermNode *root,
534 TSymbolTable *symbolTable,
Olli Etuaho89a69a02017-10-23 12:20:45 +0300535 PerformanceDiagnostics *perfDiagnostics)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300536{
Olli Etuaho68981eb2018-01-23 17:46:12 +0200537 RemoveDynamicIndexingTraverser traverser(symbolTable, 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