blob: 05593a02aaee00cd57fefda5d3c84370113c7dc7 [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 Etuahofe486322017-03-21 09:30:54 +000026std::string GetIndexFunctionName(const TType &type, bool write)
Olli Etuaho5d91dda2015-06-18 15:47:46 +030027{
28 TInfoSinkBase nameSink;
29 nameSink << "dyn_index_";
30 if (write)
31 {
32 nameSink << "write_";
33 }
34 if (type.isMatrix())
35 {
36 nameSink << "mat" << type.getCols() << "x" << type.getRows();
37 }
38 else
39 {
40 switch (type.getBasicType())
41 {
42 case EbtInt:
43 nameSink << "ivec";
44 break;
45 case EbtBool:
46 nameSink << "bvec";
47 break;
48 case EbtUInt:
49 nameSink << "uvec";
50 break;
51 case EbtFloat:
52 nameSink << "vec";
53 break;
54 default:
55 UNREACHABLE();
56 }
57 nameSink << type.getNominalSize();
58 }
Olli Etuahofe486322017-03-21 09:30:54 +000059 return nameSink.str();
Olli Etuaho5d91dda2015-06-18 15:47:46 +030060}
61
Olli Etuahob60d30f2018-01-16 12:31:06 +020062TIntermSymbol *CreateBaseSymbol(const TType *type, TSymbolTable *symbolTable)
Olli Etuaho5d91dda2015-06-18 15:47:46 +030063{
Olli Etuaho195be942017-12-04 23:40:14 +020064 TString *baseString = NewPoolTString("base");
65 TVariable *baseVariable =
66 new TVariable(symbolTable, baseString, type, SymbolType::AngleInternal);
67 return new TIntermSymbol(baseVariable);
Olli Etuaho5d91dda2015-06-18 15:47:46 +030068}
69
Olli Etuahob79b3f92017-07-21 14:23:21 +030070TIntermSymbol *CreateIndexSymbol(TSymbolTable *symbolTable)
Olli Etuaho5d91dda2015-06-18 15:47:46 +030071{
Olli Etuaho195be942017-12-04 23:40:14 +020072 TString *indexString = NewPoolTString("index");
Olli Etuahob60d30f2018-01-16 12:31:06 +020073 TVariable *indexVariable =
74 new TVariable(symbolTable, indexString, StaticType::Get<EbtInt, EbpHigh, EvqIn, 1, 1>(),
75 SymbolType::AngleInternal);
Olli Etuaho195be942017-12-04 23:40:14 +020076 return new TIntermSymbol(indexVariable);
Olli Etuaho5d91dda2015-06-18 15:47:46 +030077}
78
Olli Etuahob79b3f92017-07-21 14:23:21 +030079TIntermSymbol *CreateValueSymbol(const TType &type, TSymbolTable *symbolTable)
Olli Etuaho5d91dda2015-06-18 15:47:46 +030080{
Olli Etuaho195be942017-12-04 23:40:14 +020081 TString *valueString = NewPoolTString("value");
Olli Etuahob60d30f2018-01-16 12:31:06 +020082 TType *valueType = new TType(type);
83 valueType->setQualifier(EvqIn);
Olli Etuaho195be942017-12-04 23:40:14 +020084 TVariable *valueVariable =
85 new TVariable(symbolTable, valueString, valueType, SymbolType::AngleInternal);
86 return new TIntermSymbol(valueVariable);
Olli Etuaho5d91dda2015-06-18 15:47:46 +030087}
88
89TIntermConstantUnion *CreateIntConstantNode(int i)
90{
91 TConstantUnion *constant = new TConstantUnion();
92 constant->setIConst(i);
93 return new TIntermConstantUnion(constant, TType(EbtInt, EbpHigh));
94}
95
Olli Etuaho5d91dda2015-06-18 15:47:46 +030096TIntermTyped *EnsureSignedInt(TIntermTyped *node)
97{
98 if (node->getBasicType() == EbtInt)
99 return node;
100
Olli Etuahoaf6fc1b2017-01-26 17:45:35 -0800101 TIntermSequence *arguments = new TIntermSequence();
102 arguments->push_back(node);
Olli Etuahoa7ecec32017-05-08 17:43:55 +0300103 return TIntermAggregate::CreateConstructor(TType(EbtInt), arguments);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300104}
105
Olli Etuaho0c371002017-12-13 17:00:25 +0400106TType *GetFieldType(const TType &indexedType)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300107{
108 if (indexedType.isMatrix())
109 {
Olli Etuaho0c371002017-12-13 17:00:25 +0400110 TType *fieldType = new TType(indexedType.getBasicType(), indexedType.getPrecision());
111 fieldType->setPrimarySize(static_cast<unsigned char>(indexedType.getRows()));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300112 return fieldType;
113 }
114 else
115 {
Olli Etuaho0c371002017-12-13 17:00:25 +0400116 return new TType(indexedType.getBasicType(), indexedType.getPrecision());
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300117 }
118}
119
120// Generate a read or write function for one field in a vector/matrix.
121// Out-of-range indices are clamped. This is consistent with how ANGLE handles out-of-range
122// indices in other places.
123// Note that indices can be either int or uint. We create only int versions of the functions,
124// and convert uint indices to int at the call site.
125// read function example:
126// float dyn_index_vec2(in vec2 base, in int index)
127// {
128// switch(index)
129// {
130// case (0):
131// return base[0];
132// case (1):
133// return base[1];
134// default:
135// break;
136// }
137// if (index < 0)
138// return base[0];
139// return base[1];
140// }
141// write function example:
142// void dyn_index_write_vec2(inout vec2 base, in int index, in float value)
143// {
144// switch(index)
145// {
146// case (0):
147// base[0] = value;
148// return;
149// case (1):
150// base[1] = value;
151// return;
152// default:
153// break;
154// }
155// if (index < 0)
156// {
157// base[0] = value;
158// return;
159// }
160// base[1] = value;
161// }
162// Note that else is not used in above functions to avoid the RewriteElseBlocks transformation.
Olli Etuaho195be942017-12-04 23:40:14 +0200163TIntermFunctionDefinition *GetIndexFunctionDefinition(const TType &type,
Olli Etuahofe486322017-03-21 09:30:54 +0000164 bool write,
Olli Etuaho0c371002017-12-13 17:00:25 +0400165 const TFunction &func,
Olli Etuahob79b3f92017-07-21 14:23:21 +0300166 TSymbolTable *symbolTable)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300167{
168 ASSERT(!type.isArray());
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300169
Olli Etuaho0c371002017-12-13 17:00:25 +0400170 const TType *fieldType = GetFieldType(type);
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500171 int numCases = 0;
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300172 if (type.isMatrix())
173 {
174 numCases = type.getCols();
175 }
176 else
177 {
178 numCases = type.getNominalSize();
179 }
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300180
Olli Etuahofe486322017-03-21 09:30:54 +0000181 std::string functionName = GetIndexFunctionName(type, write);
Olli Etuaho0c371002017-12-13 17:00:25 +0400182 TIntermFunctionPrototype *prototypeNode = CreateInternalFunctionPrototypeNode(func);
Olli Etuaho8ad9e752017-01-16 19:55:20 +0000183
Olli Etuahob60d30f2018-01-16 12:31:06 +0200184 TType *baseType = new TType(type);
Olli Etuaho195be942017-12-04 23:40:14 +0200185 // Conservatively use highp here, even if the indexed type is not highp. That way the code can't
186 // end up using mediump version of an indexing function for a highp value, if both mediump and
187 // highp values are being indexed in the shader. For HLSL precision doesn't matter, but in
188 // principle this code could be used with multiple backends.
Olli Etuahob60d30f2018-01-16 12:31:06 +0200189 baseType->setPrecision(EbpHigh);
190 baseType->setQualifier(EvqInOut);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300191 if (!write)
Olli Etuahob60d30f2018-01-16 12:31:06 +0200192 baseType->setQualifier(EvqIn);
Olli Etuaho195be942017-12-04 23:40:14 +0200193
194 TIntermSymbol *baseParam = CreateBaseSymbol(baseType, symbolTable);
Olli Etuaho8ad9e752017-01-16 19:55:20 +0000195 prototypeNode->getSequence()->push_back(baseParam);
Olli Etuahob79b3f92017-07-21 14:23:21 +0300196 TIntermSymbol *indexParam = CreateIndexSymbol(symbolTable);
Olli Etuaho8ad9e752017-01-16 19:55:20 +0000197 prototypeNode->getSequence()->push_back(indexParam);
Olli Etuahob79b3f92017-07-21 14:23:21 +0300198 TIntermSymbol *valueParam = nullptr;
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300199 if (write)
200 {
Olli Etuaho0c371002017-12-13 17:00:25 +0400201 valueParam = CreateValueSymbol(*fieldType, symbolTable);
Olli Etuaho8ad9e752017-01-16 19:55:20 +0000202 prototypeNode->getSequence()->push_back(valueParam);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300203 }
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300204
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100205 TIntermBlock *statementList = new TIntermBlock();
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300206 for (int i = 0; i < numCases; ++i)
207 {
208 TIntermCase *caseNode = new TIntermCase(CreateIntConstantNode(i));
209 statementList->getSequence()->push_back(caseNode);
210
211 TIntermBinary *indexNode =
Olli Etuahob79b3f92017-07-21 14:23:21 +0300212 new TIntermBinary(EOpIndexDirect, baseParam->deepCopy(), CreateIndexNode(i));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300213 if (write)
214 {
Olli Etuahob79b3f92017-07-21 14:23:21 +0300215 TIntermBinary *assignNode =
216 new TIntermBinary(EOpAssign, indexNode, valueParam->deepCopy());
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300217 statementList->getSequence()->push_back(assignNode);
218 TIntermBranch *returnNode = new TIntermBranch(EOpReturn, nullptr);
219 statementList->getSequence()->push_back(returnNode);
220 }
221 else
222 {
223 TIntermBranch *returnNode = new TIntermBranch(EOpReturn, indexNode);
224 statementList->getSequence()->push_back(returnNode);
225 }
226 }
227
228 // Default case
229 TIntermCase *defaultNode = new TIntermCase(nullptr);
230 statementList->getSequence()->push_back(defaultNode);
231 TIntermBranch *breakNode = new TIntermBranch(EOpBreak, nullptr);
232 statementList->getSequence()->push_back(breakNode);
233
Olli Etuahob79b3f92017-07-21 14:23:21 +0300234 TIntermSwitch *switchNode = new TIntermSwitch(indexParam->deepCopy(), statementList);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300235
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100236 TIntermBlock *bodyNode = new TIntermBlock();
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300237 bodyNode->getSequence()->push_back(switchNode);
238
Olli Etuaho3272a6d2016-08-29 17:54:50 +0300239 TIntermBinary *cond =
Olli Etuahob79b3f92017-07-21 14:23:21 +0300240 new TIntermBinary(EOpLessThan, indexParam->deepCopy(), CreateIntConstantNode(0));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300241
242 // Two blocks: one accesses (either reads or writes) the first element and returns,
243 // the other accesses the last element.
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100244 TIntermBlock *useFirstBlock = new TIntermBlock();
245 TIntermBlock *useLastBlock = new TIntermBlock();
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300246 TIntermBinary *indexFirstNode =
Olli Etuahob79b3f92017-07-21 14:23:21 +0300247 new TIntermBinary(EOpIndexDirect, baseParam->deepCopy(), CreateIndexNode(0));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300248 TIntermBinary *indexLastNode =
Olli Etuahob79b3f92017-07-21 14:23:21 +0300249 new TIntermBinary(EOpIndexDirect, baseParam->deepCopy(), CreateIndexNode(numCases - 1));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300250 if (write)
251 {
Olli Etuahob79b3f92017-07-21 14:23:21 +0300252 TIntermBinary *assignFirstNode =
253 new TIntermBinary(EOpAssign, indexFirstNode, valueParam->deepCopy());
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300254 useFirstBlock->getSequence()->push_back(assignFirstNode);
255 TIntermBranch *returnNode = new TIntermBranch(EOpReturn, nullptr);
256 useFirstBlock->getSequence()->push_back(returnNode);
257
Olli Etuahob79b3f92017-07-21 14:23:21 +0300258 TIntermBinary *assignLastNode =
259 new TIntermBinary(EOpAssign, indexLastNode, valueParam->deepCopy());
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300260 useLastBlock->getSequence()->push_back(assignLastNode);
261 }
262 else
263 {
264 TIntermBranch *returnFirstNode = new TIntermBranch(EOpReturn, indexFirstNode);
265 useFirstBlock->getSequence()->push_back(returnFirstNode);
266
267 TIntermBranch *returnLastNode = new TIntermBranch(EOpReturn, indexLastNode);
268 useLastBlock->getSequence()->push_back(returnLastNode);
269 }
Olli Etuaho57961272016-09-14 13:57:46 +0300270 TIntermIfElse *ifNode = new TIntermIfElse(cond, useFirstBlock, nullptr);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300271 bodyNode->getSequence()->push_back(ifNode);
272 bodyNode->getSequence()->push_back(useLastBlock);
273
Olli Etuaho8ad9e752017-01-16 19:55:20 +0000274 TIntermFunctionDefinition *indexingFunction =
275 new TIntermFunctionDefinition(prototypeNode, bodyNode);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300276 return indexingFunction;
277}
278
279class RemoveDynamicIndexingTraverser : public TLValueTrackingTraverser
280{
281 public:
Olli Etuaho89a69a02017-10-23 12:20:45 +0300282 RemoveDynamicIndexingTraverser(TSymbolTable *symbolTable,
283 int shaderVersion,
284 PerformanceDiagnostics *perfDiagnostics);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300285
286 bool visitBinary(Visit visit, TIntermBinary *node) override;
287
288 void insertHelperDefinitions(TIntermNode *root);
289
290 void nextIteration();
291
292 bool usedTreeInsertion() const { return mUsedTreeInsertion; }
293
294 protected:
Olli Etuahofe486322017-03-21 09:30:54 +0000295 // Maps of types that are indexed to the indexing function ids used for them. Note that these
296 // can not store multiple variants of the same type with different precisions - only one
297 // precision gets stored.
Olli Etuaho0c371002017-12-13 17:00:25 +0400298 std::map<TType, TFunction *> mIndexedVecAndMatrixTypes;
299 std::map<TType, TFunction *> mWrittenVecAndMatrixTypes;
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300300
301 bool mUsedTreeInsertion;
302
303 // When true, the traverser will remove side effects from any indexing expression.
304 // This is done so that in code like
305 // V[j++][i]++.
306 // where V is an array of vectors, j++ will only be evaluated once.
307 bool mRemoveIndexSideEffectsInSubtree;
Olli Etuaho89a69a02017-10-23 12:20:45 +0300308
309 PerformanceDiagnostics *mPerfDiagnostics;
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300310};
311
Olli Etuaho89a69a02017-10-23 12:20:45 +0300312RemoveDynamicIndexingTraverser::RemoveDynamicIndexingTraverser(
313 TSymbolTable *symbolTable,
314 int shaderVersion,
315 PerformanceDiagnostics *perfDiagnostics)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300316 : TLValueTrackingTraverser(true, false, false, symbolTable, shaderVersion),
317 mUsedTreeInsertion(false),
Olli Etuaho89a69a02017-10-23 12:20:45 +0300318 mRemoveIndexSideEffectsInSubtree(false),
319 mPerfDiagnostics(perfDiagnostics)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300320{
321}
322
323void RemoveDynamicIndexingTraverser::insertHelperDefinitions(TIntermNode *root)
324{
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100325 TIntermBlock *rootBlock = root->getAsBlock();
326 ASSERT(rootBlock != nullptr);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300327 TIntermSequence insertions;
Olli Etuahofe486322017-03-21 09:30:54 +0000328 for (auto &type : mIndexedVecAndMatrixTypes)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300329 {
Olli Etuahob79b3f92017-07-21 14:23:21 +0300330 insertions.push_back(
331 GetIndexFunctionDefinition(type.first, false, *type.second, mSymbolTable));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300332 }
Olli Etuahofe486322017-03-21 09:30:54 +0000333 for (auto &type : mWrittenVecAndMatrixTypes)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300334 {
Olli Etuahob79b3f92017-07-21 14:23:21 +0300335 insertions.push_back(
336 GetIndexFunctionDefinition(type.first, true, *type.second, mSymbolTable));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300337 }
Olli Etuaho56612d62017-10-12 15:46:30 +0300338 rootBlock->insertChildNodes(0, insertions);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300339}
340
341// Create a call to dyn_index_*() based on an indirect indexing op node
342TIntermAggregate *CreateIndexFunctionCall(TIntermBinary *node,
Olli Etuahofe486322017-03-21 09:30:54 +0000343 TIntermTyped *index,
Olli Etuaho0c371002017-12-13 17:00:25 +0400344 TFunction *indexingFunction)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300345{
346 ASSERT(node->getOp() == EOpIndexIndirect);
Olli Etuahoaf6fc1b2017-01-26 17:45:35 -0800347 TIntermSequence *arguments = new TIntermSequence();
Olli Etuahofe486322017-03-21 09:30:54 +0000348 arguments->push_back(node->getLeft());
Olli Etuahoaf6fc1b2017-01-26 17:45:35 -0800349 arguments->push_back(index);
350
Olli Etuaho3ec75682017-07-05 17:02:55 +0300351 TIntermAggregate *indexingCall =
Olli Etuaho0c371002017-12-13 17:00:25 +0400352 TIntermAggregate::CreateFunctionCall(*indexingFunction, arguments);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300353 indexingCall->setLine(node->getLine());
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300354 return indexingCall;
355}
356
357TIntermAggregate *CreateIndexedWriteFunctionCall(TIntermBinary *node,
Olli Etuaho195be942017-12-04 23:40:14 +0200358 TVariable *index,
359 TVariable *writtenValue,
Olli Etuaho0c371002017-12-13 17:00:25 +0400360 TFunction *indexedWriteFunction)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300361{
Olli Etuahofe486322017-03-21 09:30:54 +0000362 ASSERT(node->getOp() == EOpIndexIndirect);
363 TIntermSequence *arguments = new TIntermSequence();
364 // Deep copy the child nodes so that two pointers to the same node don't end up in the tree.
365 arguments->push_back(node->getLeft()->deepCopy());
Olli Etuaho195be942017-12-04 23:40:14 +0200366 arguments->push_back(CreateTempSymbolNode(index));
367 arguments->push_back(CreateTempSymbolNode(writtenValue));
Olli Etuahofe486322017-03-21 09:30:54 +0000368
Olli Etuaho3ec75682017-07-05 17:02:55 +0300369 TIntermAggregate *indexedWriteCall =
Olli Etuaho0c371002017-12-13 17:00:25 +0400370 TIntermAggregate::CreateFunctionCall(*indexedWriteFunction, arguments);
Olli Etuahofe486322017-03-21 09:30:54 +0000371 indexedWriteCall->setLine(node->getLine());
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300372 return indexedWriteCall;
373}
374
375bool RemoveDynamicIndexingTraverser::visitBinary(Visit visit, TIntermBinary *node)
376{
377 if (mUsedTreeInsertion)
378 return false;
379
380 if (node->getOp() == EOpIndexIndirect)
381 {
382 if (mRemoveIndexSideEffectsInSubtree)
383 {
384 ASSERT(node->getRight()->hasSideEffects());
385 // In case we're just removing index side effects, convert
386 // v_expr[index_expr]
387 // to this:
388 // int s0 = index_expr; v_expr[s0];
389 // Now v_expr[s0] can be safely executed several times without unintended side effects.
Olli Etuaho195be942017-12-04 23:40:14 +0200390 TIntermDeclaration *indexVariableDeclaration = nullptr;
391 TVariable *indexVariable = DeclareTempVariable(mSymbolTable, node->getRight(),
392 EvqTemporary, &indexVariableDeclaration);
393 insertStatementInParentBlock(indexVariableDeclaration);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300394 mUsedTreeInsertion = true;
395
396 // Replace the index with the temp variable
Olli Etuaho195be942017-12-04 23:40:14 +0200397 TIntermSymbol *tempIndex = CreateTempSymbolNode(indexVariable);
Jamie Madill03d863c2016-07-27 18:15:53 -0400398 queueReplacementWithParent(node, node->getRight(), tempIndex, OriginalNode::IS_DROPPED);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300399 }
Jamie Madill666f65a2016-08-26 01:34:37 +0000400 else if (IntermNodePatternMatcher::IsDynamicIndexingOfVectorOrMatrix(node))
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300401 {
Olli Etuaho89a69a02017-10-23 12:20:45 +0300402 mPerfDiagnostics->warning(node->getLine(),
403 "Performance: dynamic indexing of vectors and "
404 "matrices is emulated and can be slow.",
405 "[]");
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300406 bool write = isLValueRequiredHere();
407
Jamie Madill666f65a2016-08-26 01:34:37 +0000408#if defined(ANGLE_ENABLE_ASSERTS)
409 // Make sure that IntermNodePatternMatcher is consistent with the slightly differently
410 // implemented checks in this traverser.
411 IntermNodePatternMatcher matcher(
412 IntermNodePatternMatcher::kDynamicIndexingOfVectorOrMatrixInLValue);
413 ASSERT(matcher.match(node, getParentNode(), isLValueRequiredHere()) == write);
414#endif
415
Olli Etuahofe486322017-03-21 09:30:54 +0000416 const TType &type = node->getLeft()->getType();
Olli Etuaho0c371002017-12-13 17:00:25 +0400417 TString *indexingFunctionName =
418 NewPoolTString(GetIndexFunctionName(type, false).c_str());
419 TFunction *indexingFunction = nullptr;
Olli Etuahofe486322017-03-21 09:30:54 +0000420 if (mIndexedVecAndMatrixTypes.find(type) == mIndexedVecAndMatrixTypes.end())
421 {
Olli Etuaho0c371002017-12-13 17:00:25 +0400422 indexingFunction =
423 new TFunction(mSymbolTable, indexingFunctionName, GetFieldType(type),
424 SymbolType::AngleInternal, true);
425 mIndexedVecAndMatrixTypes[type] = indexingFunction;
Olli Etuahofe486322017-03-21 09:30:54 +0000426 }
427 else
428 {
Olli Etuaho0c371002017-12-13 17:00:25 +0400429 indexingFunction = mIndexedVecAndMatrixTypes[type];
Olli Etuahofe486322017-03-21 09:30:54 +0000430 }
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300431
432 if (write)
433 {
434 // Convert:
435 // v_expr[index_expr]++;
436 // to this:
437 // int s0 = index_expr; float s1 = dyn_index(v_expr, s0); s1++;
438 // dyn_index_write(v_expr, s0, s1);
439 // This works even if index_expr has some side effects.
440 if (node->getLeft()->hasSideEffects())
441 {
442 // If v_expr has side effects, those need to be removed before proceeding.
443 // Otherwise the side effects of v_expr would be evaluated twice.
444 // The only case where an l-value can have side effects is when it is
445 // indexing. For example, it can be V[j++] where V is an array of vectors.
446 mRemoveIndexSideEffectsInSubtree = true;
447 return true;
448 }
Olli Etuaho8f6eb2a2017-01-12 17:04:58 +0000449
450 TIntermBinary *leftBinary = node->getLeft()->getAsBinaryNode();
451 if (leftBinary != nullptr &&
452 IntermNodePatternMatcher::IsDynamicIndexingOfVectorOrMatrix(leftBinary))
453 {
454 // This is a case like:
455 // mat2 m;
456 // m[a][b]++;
457 // Process the child node m[a] first.
458 return true;
459 }
460
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300461 // TODO(oetuaho@nvidia.com): This is not optimal if the expression using the value
462 // only writes it and doesn't need the previous value. http://anglebug.com/1116
463
Olli Etuaho0c371002017-12-13 17:00:25 +0400464 TFunction *indexedWriteFunction = nullptr;
Olli Etuahofe486322017-03-21 09:30:54 +0000465 if (mWrittenVecAndMatrixTypes.find(type) == mWrittenVecAndMatrixTypes.end())
466 {
Olli Etuaho0c371002017-12-13 17:00:25 +0400467 TString *functionName = NewPoolTString(
468 GetIndexFunctionName(node->getLeft()->getType(), true).c_str());
469 indexedWriteFunction =
470 new TFunction(mSymbolTable, functionName, new TType(EbtVoid),
471 SymbolType::AngleInternal, false);
472 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,
535 int shaderVersion,
536 PerformanceDiagnostics *perfDiagnostics)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300537{
Olli Etuaho89a69a02017-10-23 12:20:45 +0300538 RemoveDynamicIndexingTraverser traverser(symbolTable, shaderVersion, perfDiagnostics);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300539 do
540 {
541 traverser.nextIteration();
542 root->traverse(&traverser);
543 traverser.updateTree();
544 } while (traverser.usedTreeInsertion());
Olli Etuaho56612d62017-10-12 15:46:30 +0300545 // TODO(oetuaho@nvidia.com): It might be nicer to add the helper definitions also in the middle
Olli Etuaho1ecd14b2017-01-26 13:54:15 -0800546 // of traversal. Now the tree ends up in an inconsistent state in the middle, since there are
547 // function call nodes with no corresponding definition nodes. This needs special handling in
548 // TIntermLValueTrackingTraverser, and creates intricacies that are not easily apparent from a
549 // superficial reading of the code.
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300550 traverser.insertHelperDefinitions(root);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300551}
Jamie Madill45bcc782016-11-07 13:58:48 -0500552
553} // namespace sh