blob: 093f5e5c20e0cef15414f16aebb81fe1d4a2e418 [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
104TType GetFieldType(const TType &indexedType)
105{
106 if (indexedType.isMatrix())
107 {
108 TType fieldType = TType(indexedType.getBasicType(), indexedType.getPrecision());
109 fieldType.setPrimarySize(static_cast<unsigned char>(indexedType.getRows()));
110 return fieldType;
111 }
112 else
113 {
114 return TType(indexedType.getBasicType(), indexedType.getPrecision());
115 }
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 Etuahob79b3f92017-07-21 14:23:21 +0300163 const TSymbolUniqueId &functionId,
164 TSymbolTable *symbolTable)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300165{
166 ASSERT(!type.isArray());
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300167
168 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 TType returnType(EbtVoid);
180 if (!write)
Olli Etuaho8ad9e752017-01-16 19:55:20 +0000181 {
Olli Etuahofe486322017-03-21 09:30:54 +0000182 returnType = fieldType;
Olli Etuaho8ad9e752017-01-16 19:55:20 +0000183 }
Olli Etuahofe486322017-03-21 09:30:54 +0000184
185 std::string functionName = GetIndexFunctionName(type, write);
Olli Etuaho3ec75682017-07-05 17:02:55 +0300186 TIntermFunctionPrototype *prototypeNode =
187 CreateInternalFunctionPrototypeNode(returnType, functionName.c_str(), functionId);
Olli Etuaho8ad9e752017-01-16 19:55:20 +0000188
Olli Etuaho195be942017-12-04 23:40:14 +0200189 TType baseType(type);
190 // Conservatively use highp here, even if the indexed type is not highp. That way the code can't
191 // end up using mediump version of an indexing function for a highp value, if both mediump and
192 // highp values are being indexed in the shader. For HLSL precision doesn't matter, but in
193 // principle this code could be used with multiple backends.
194 baseType.setPrecision(EbpHigh);
195 baseType.setQualifier(EvqInOut);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300196 if (!write)
Olli Etuaho195be942017-12-04 23:40:14 +0200197 baseType.setQualifier(EvqIn);
198
199 TIntermSymbol *baseParam = CreateBaseSymbol(baseType, symbolTable);
Olli Etuaho8ad9e752017-01-16 19:55:20 +0000200 prototypeNode->getSequence()->push_back(baseParam);
Olli Etuahob79b3f92017-07-21 14:23:21 +0300201 TIntermSymbol *indexParam = CreateIndexSymbol(symbolTable);
Olli Etuaho8ad9e752017-01-16 19:55:20 +0000202 prototypeNode->getSequence()->push_back(indexParam);
Olli Etuahob79b3f92017-07-21 14:23:21 +0300203 TIntermSymbol *valueParam = nullptr;
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300204 if (write)
205 {
Olli Etuahob79b3f92017-07-21 14:23:21 +0300206 valueParam = CreateValueSymbol(fieldType, symbolTable);
Olli Etuaho8ad9e752017-01-16 19:55:20 +0000207 prototypeNode->getSequence()->push_back(valueParam);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300208 }
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300209
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100210 TIntermBlock *statementList = new TIntermBlock();
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300211 for (int i = 0; i < numCases; ++i)
212 {
213 TIntermCase *caseNode = new TIntermCase(CreateIntConstantNode(i));
214 statementList->getSequence()->push_back(caseNode);
215
216 TIntermBinary *indexNode =
Olli Etuahob79b3f92017-07-21 14:23:21 +0300217 new TIntermBinary(EOpIndexDirect, baseParam->deepCopy(), CreateIndexNode(i));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300218 if (write)
219 {
Olli Etuahob79b3f92017-07-21 14:23:21 +0300220 TIntermBinary *assignNode =
221 new TIntermBinary(EOpAssign, indexNode, valueParam->deepCopy());
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300222 statementList->getSequence()->push_back(assignNode);
223 TIntermBranch *returnNode = new TIntermBranch(EOpReturn, nullptr);
224 statementList->getSequence()->push_back(returnNode);
225 }
226 else
227 {
228 TIntermBranch *returnNode = new TIntermBranch(EOpReturn, indexNode);
229 statementList->getSequence()->push_back(returnNode);
230 }
231 }
232
233 // Default case
234 TIntermCase *defaultNode = new TIntermCase(nullptr);
235 statementList->getSequence()->push_back(defaultNode);
236 TIntermBranch *breakNode = new TIntermBranch(EOpBreak, nullptr);
237 statementList->getSequence()->push_back(breakNode);
238
Olli Etuahob79b3f92017-07-21 14:23:21 +0300239 TIntermSwitch *switchNode = new TIntermSwitch(indexParam->deepCopy(), statementList);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300240
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100241 TIntermBlock *bodyNode = new TIntermBlock();
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300242 bodyNode->getSequence()->push_back(switchNode);
243
Olli Etuaho3272a6d2016-08-29 17:54:50 +0300244 TIntermBinary *cond =
Olli Etuahob79b3f92017-07-21 14:23:21 +0300245 new TIntermBinary(EOpLessThan, indexParam->deepCopy(), CreateIntConstantNode(0));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300246 cond->setType(TType(EbtBool, EbpUndefined));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300247
248 // Two blocks: one accesses (either reads or writes) the first element and returns,
249 // the other accesses the last element.
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100250 TIntermBlock *useFirstBlock = new TIntermBlock();
251 TIntermBlock *useLastBlock = new TIntermBlock();
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300252 TIntermBinary *indexFirstNode =
Olli Etuahob79b3f92017-07-21 14:23:21 +0300253 new TIntermBinary(EOpIndexDirect, baseParam->deepCopy(), CreateIndexNode(0));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300254 TIntermBinary *indexLastNode =
Olli Etuahob79b3f92017-07-21 14:23:21 +0300255 new TIntermBinary(EOpIndexDirect, baseParam->deepCopy(), CreateIndexNode(numCases - 1));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300256 if (write)
257 {
Olli Etuahob79b3f92017-07-21 14:23:21 +0300258 TIntermBinary *assignFirstNode =
259 new TIntermBinary(EOpAssign, indexFirstNode, valueParam->deepCopy());
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300260 useFirstBlock->getSequence()->push_back(assignFirstNode);
261 TIntermBranch *returnNode = new TIntermBranch(EOpReturn, nullptr);
262 useFirstBlock->getSequence()->push_back(returnNode);
263
Olli Etuahob79b3f92017-07-21 14:23:21 +0300264 TIntermBinary *assignLastNode =
265 new TIntermBinary(EOpAssign, indexLastNode, valueParam->deepCopy());
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300266 useLastBlock->getSequence()->push_back(assignLastNode);
267 }
268 else
269 {
270 TIntermBranch *returnFirstNode = new TIntermBranch(EOpReturn, indexFirstNode);
271 useFirstBlock->getSequence()->push_back(returnFirstNode);
272
273 TIntermBranch *returnLastNode = new TIntermBranch(EOpReturn, indexLastNode);
274 useLastBlock->getSequence()->push_back(returnLastNode);
275 }
Olli Etuaho57961272016-09-14 13:57:46 +0300276 TIntermIfElse *ifNode = new TIntermIfElse(cond, useFirstBlock, nullptr);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300277 bodyNode->getSequence()->push_back(ifNode);
278 bodyNode->getSequence()->push_back(useLastBlock);
279
Olli Etuaho8ad9e752017-01-16 19:55:20 +0000280 TIntermFunctionDefinition *indexingFunction =
281 new TIntermFunctionDefinition(prototypeNode, bodyNode);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300282 return indexingFunction;
283}
284
285class RemoveDynamicIndexingTraverser : public TLValueTrackingTraverser
286{
287 public:
Olli Etuaho89a69a02017-10-23 12:20:45 +0300288 RemoveDynamicIndexingTraverser(TSymbolTable *symbolTable,
289 int shaderVersion,
290 PerformanceDiagnostics *perfDiagnostics);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300291
292 bool visitBinary(Visit visit, TIntermBinary *node) override;
293
294 void insertHelperDefinitions(TIntermNode *root);
295
296 void nextIteration();
297
298 bool usedTreeInsertion() const { return mUsedTreeInsertion; }
299
300 protected:
Olli Etuahofe486322017-03-21 09:30:54 +0000301 // Maps of types that are indexed to the indexing function ids used for them. Note that these
302 // can not store multiple variants of the same type with different precisions - only one
303 // precision gets stored.
Olli Etuahoa5e693a2017-07-13 16:07:26 +0300304 std::map<TType, TSymbolUniqueId *> mIndexedVecAndMatrixTypes;
305 std::map<TType, TSymbolUniqueId *> mWrittenVecAndMatrixTypes;
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300306
307 bool mUsedTreeInsertion;
308
309 // When true, the traverser will remove side effects from any indexing expression.
310 // This is done so that in code like
311 // V[j++][i]++.
312 // where V is an array of vectors, j++ will only be evaluated once.
313 bool mRemoveIndexSideEffectsInSubtree;
Olli Etuaho89a69a02017-10-23 12:20:45 +0300314
315 PerformanceDiagnostics *mPerfDiagnostics;
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300316};
317
Olli Etuaho89a69a02017-10-23 12:20:45 +0300318RemoveDynamicIndexingTraverser::RemoveDynamicIndexingTraverser(
319 TSymbolTable *symbolTable,
320 int shaderVersion,
321 PerformanceDiagnostics *perfDiagnostics)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300322 : TLValueTrackingTraverser(true, false, false, symbolTable, shaderVersion),
323 mUsedTreeInsertion(false),
Olli Etuaho89a69a02017-10-23 12:20:45 +0300324 mRemoveIndexSideEffectsInSubtree(false),
325 mPerfDiagnostics(perfDiagnostics)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300326{
327}
328
329void RemoveDynamicIndexingTraverser::insertHelperDefinitions(TIntermNode *root)
330{
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100331 TIntermBlock *rootBlock = root->getAsBlock();
332 ASSERT(rootBlock != nullptr);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300333 TIntermSequence insertions;
Olli Etuahofe486322017-03-21 09:30:54 +0000334 for (auto &type : mIndexedVecAndMatrixTypes)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300335 {
Olli Etuahob79b3f92017-07-21 14:23:21 +0300336 insertions.push_back(
337 GetIndexFunctionDefinition(type.first, false, *type.second, mSymbolTable));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300338 }
Olli Etuahofe486322017-03-21 09:30:54 +0000339 for (auto &type : mWrittenVecAndMatrixTypes)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300340 {
Olli Etuahob79b3f92017-07-21 14:23:21 +0300341 insertions.push_back(
342 GetIndexFunctionDefinition(type.first, true, *type.second, mSymbolTable));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300343 }
Olli Etuaho56612d62017-10-12 15:46:30 +0300344 rootBlock->insertChildNodes(0, insertions);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300345}
346
347// Create a call to dyn_index_*() based on an indirect indexing op node
348TIntermAggregate *CreateIndexFunctionCall(TIntermBinary *node,
Olli Etuahofe486322017-03-21 09:30:54 +0000349 TIntermTyped *index,
350 const TSymbolUniqueId &functionId)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300351{
352 ASSERT(node->getOp() == EOpIndexIndirect);
Olli Etuahoaf6fc1b2017-01-26 17:45:35 -0800353 TIntermSequence *arguments = new TIntermSequence();
Olli Etuahofe486322017-03-21 09:30:54 +0000354 arguments->push_back(node->getLeft());
Olli Etuahoaf6fc1b2017-01-26 17:45:35 -0800355 arguments->push_back(index);
356
Olli Etuahofe486322017-03-21 09:30:54 +0000357 TType fieldType = GetFieldType(node->getLeft()->getType());
358 std::string functionName = GetIndexFunctionName(node->getLeft()->getType(), false);
Olli Etuaho3ec75682017-07-05 17:02:55 +0300359 TIntermAggregate *indexingCall =
360 CreateInternalFunctionCallNode(fieldType, functionName.c_str(), functionId, arguments);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300361 indexingCall->setLine(node->getLine());
Olli Etuahoa22aa4e2017-05-24 18:17:23 +0300362 indexingCall->getFunctionSymbolInfo()->setKnownToNotHaveSideEffects(true);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300363 return indexingCall;
364}
365
366TIntermAggregate *CreateIndexedWriteFunctionCall(TIntermBinary *node,
Olli Etuaho195be942017-12-04 23:40:14 +0200367 TVariable *index,
368 TVariable *writtenValue,
Olli Etuahofe486322017-03-21 09:30:54 +0000369 const TSymbolUniqueId &functionId)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300370{
Olli Etuahofe486322017-03-21 09:30:54 +0000371 ASSERT(node->getOp() == EOpIndexIndirect);
372 TIntermSequence *arguments = new TIntermSequence();
373 // Deep copy the child nodes so that two pointers to the same node don't end up in the tree.
374 arguments->push_back(node->getLeft()->deepCopy());
Olli Etuaho195be942017-12-04 23:40:14 +0200375 arguments->push_back(CreateTempSymbolNode(index));
376 arguments->push_back(CreateTempSymbolNode(writtenValue));
Olli Etuahofe486322017-03-21 09:30:54 +0000377
378 std::string functionName = GetIndexFunctionName(node->getLeft()->getType(), true);
Olli Etuaho3ec75682017-07-05 17:02:55 +0300379 TIntermAggregate *indexedWriteCall =
380 CreateInternalFunctionCallNode(TType(EbtVoid), functionName.c_str(), functionId, arguments);
Olli Etuahofe486322017-03-21 09:30:54 +0000381 indexedWriteCall->setLine(node->getLine());
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300382 return indexedWriteCall;
383}
384
385bool RemoveDynamicIndexingTraverser::visitBinary(Visit visit, TIntermBinary *node)
386{
387 if (mUsedTreeInsertion)
388 return false;
389
390 if (node->getOp() == EOpIndexIndirect)
391 {
392 if (mRemoveIndexSideEffectsInSubtree)
393 {
394 ASSERT(node->getRight()->hasSideEffects());
395 // In case we're just removing index side effects, convert
396 // v_expr[index_expr]
397 // to this:
398 // int s0 = index_expr; v_expr[s0];
399 // Now v_expr[s0] can be safely executed several times without unintended side effects.
Olli Etuaho195be942017-12-04 23:40:14 +0200400 TIntermDeclaration *indexVariableDeclaration = nullptr;
401 TVariable *indexVariable = DeclareTempVariable(mSymbolTable, node->getRight(),
402 EvqTemporary, &indexVariableDeclaration);
403 insertStatementInParentBlock(indexVariableDeclaration);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300404 mUsedTreeInsertion = true;
405
406 // Replace the index with the temp variable
Olli Etuaho195be942017-12-04 23:40:14 +0200407 TIntermSymbol *tempIndex = CreateTempSymbolNode(indexVariable);
Jamie Madill03d863c2016-07-27 18:15:53 -0400408 queueReplacementWithParent(node, node->getRight(), tempIndex, OriginalNode::IS_DROPPED);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300409 }
Jamie Madill666f65a2016-08-26 01:34:37 +0000410 else if (IntermNodePatternMatcher::IsDynamicIndexingOfVectorOrMatrix(node))
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300411 {
Olli Etuaho89a69a02017-10-23 12:20:45 +0300412 mPerfDiagnostics->warning(node->getLine(),
413 "Performance: dynamic indexing of vectors and "
414 "matrices is emulated and can be slow.",
415 "[]");
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300416 bool write = isLValueRequiredHere();
417
Jamie Madill666f65a2016-08-26 01:34:37 +0000418#if defined(ANGLE_ENABLE_ASSERTS)
419 // Make sure that IntermNodePatternMatcher is consistent with the slightly differently
420 // implemented checks in this traverser.
421 IntermNodePatternMatcher matcher(
422 IntermNodePatternMatcher::kDynamicIndexingOfVectorOrMatrixInLValue);
423 ASSERT(matcher.match(node, getParentNode(), isLValueRequiredHere()) == write);
424#endif
425
Olli Etuahofe486322017-03-21 09:30:54 +0000426 const TType &type = node->getLeft()->getType();
Olli Etuahoa5e693a2017-07-13 16:07:26 +0300427 TSymbolUniqueId *indexingFunctionId = new TSymbolUniqueId(mSymbolTable);
Olli Etuahofe486322017-03-21 09:30:54 +0000428 if (mIndexedVecAndMatrixTypes.find(type) == mIndexedVecAndMatrixTypes.end())
429 {
430 mIndexedVecAndMatrixTypes[type] = indexingFunctionId;
431 }
432 else
433 {
434 indexingFunctionId = mIndexedVecAndMatrixTypes[type];
435 }
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300436
437 if (write)
438 {
439 // Convert:
440 // v_expr[index_expr]++;
441 // to this:
442 // int s0 = index_expr; float s1 = dyn_index(v_expr, s0); s1++;
443 // dyn_index_write(v_expr, s0, s1);
444 // This works even if index_expr has some side effects.
445 if (node->getLeft()->hasSideEffects())
446 {
447 // If v_expr has side effects, those need to be removed before proceeding.
448 // Otherwise the side effects of v_expr would be evaluated twice.
449 // The only case where an l-value can have side effects is when it is
450 // indexing. For example, it can be V[j++] where V is an array of vectors.
451 mRemoveIndexSideEffectsInSubtree = true;
452 return true;
453 }
Olli Etuaho8f6eb2a2017-01-12 17:04:58 +0000454
455 TIntermBinary *leftBinary = node->getLeft()->getAsBinaryNode();
456 if (leftBinary != nullptr &&
457 IntermNodePatternMatcher::IsDynamicIndexingOfVectorOrMatrix(leftBinary))
458 {
459 // This is a case like:
460 // mat2 m;
461 // m[a][b]++;
462 // Process the child node m[a] first.
463 return true;
464 }
465
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300466 // TODO(oetuaho@nvidia.com): This is not optimal if the expression using the value
467 // only writes it and doesn't need the previous value. http://anglebug.com/1116
468
Olli Etuahoa5e693a2017-07-13 16:07:26 +0300469 TSymbolUniqueId *indexedWriteFunctionId = new TSymbolUniqueId(mSymbolTable);
Olli Etuahofe486322017-03-21 09:30:54 +0000470 if (mWrittenVecAndMatrixTypes.find(type) == mWrittenVecAndMatrixTypes.end())
471 {
472 mWrittenVecAndMatrixTypes[type] = indexedWriteFunctionId;
473 }
474 else
475 {
476 indexedWriteFunctionId = mWrittenVecAndMatrixTypes[type];
477 }
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(
492 node, CreateTempSymbolNode(indexVariable), *indexingFunctionId);
493 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 Etuaho195be942017-12-04 23:40:14 +0200500 node, indexVariable, fieldVariable, *indexedWriteFunctionId);
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 Etuahoa5e693a2017-07-13 16:07:26 +0300517 node, EnsureSignedInt(node->getRight()), *indexingFunctionId);
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