blob: 155e3d1cba23620a59539fc705dc55fc8594dfdf [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 Etuahofbb1c792018-01-19 16:26:59 +020028constexpr const ImmutableString kBaseName("base");
29constexpr const ImmutableString kIndexName("index");
30constexpr const ImmutableString kValueName("value");
31
Olli Etuahofe486322017-03-21 09:30:54 +000032std::string GetIndexFunctionName(const TType &type, bool write)
Olli Etuaho5d91dda2015-06-18 15:47:46 +030033{
34 TInfoSinkBase nameSink;
35 nameSink << "dyn_index_";
36 if (write)
37 {
38 nameSink << "write_";
39 }
40 if (type.isMatrix())
41 {
42 nameSink << "mat" << type.getCols() << "x" << type.getRows();
43 }
44 else
45 {
46 switch (type.getBasicType())
47 {
48 case EbtInt:
49 nameSink << "ivec";
50 break;
51 case EbtBool:
52 nameSink << "bvec";
53 break;
54 case EbtUInt:
55 nameSink << "uvec";
56 break;
57 case EbtFloat:
58 nameSink << "vec";
59 break;
60 default:
61 UNREACHABLE();
62 }
63 nameSink << type.getNominalSize();
64 }
Olli Etuahofe486322017-03-21 09:30:54 +000065 return nameSink.str();
Olli Etuaho5d91dda2015-06-18 15:47:46 +030066}
67
Olli Etuaho68981eb2018-01-23 17:46:12 +020068TIntermSymbol *CreateParameterSymbol(const TConstParameter &parameter, TSymbolTable *symbolTable)
Olli Etuaho5d91dda2015-06-18 15:47:46 +030069{
Olli Etuaho68981eb2018-01-23 17:46:12 +020070 TVariable *variable =
71 new TVariable(symbolTable, parameter.name, parameter.type, SymbolType::AngleInternal);
72 return new TIntermSymbol(variable);
Olli Etuaho5d91dda2015-06-18 15:47:46 +030073}
74
75TIntermConstantUnion *CreateIntConstantNode(int i)
76{
77 TConstantUnion *constant = new TConstantUnion();
78 constant->setIConst(i);
79 return new TIntermConstantUnion(constant, TType(EbtInt, EbpHigh));
80}
81
Olli Etuaho5d91dda2015-06-18 15:47:46 +030082TIntermTyped *EnsureSignedInt(TIntermTyped *node)
83{
84 if (node->getBasicType() == EbtInt)
85 return node;
86
Olli Etuahoaf6fc1b2017-01-26 17:45:35 -080087 TIntermSequence *arguments = new TIntermSequence();
88 arguments->push_back(node);
Olli Etuahoa7ecec32017-05-08 17:43:55 +030089 return TIntermAggregate::CreateConstructor(TType(EbtInt), arguments);
Olli Etuaho5d91dda2015-06-18 15:47:46 +030090}
91
Olli Etuaho0c371002017-12-13 17:00:25 +040092TType *GetFieldType(const TType &indexedType)
Olli Etuaho5d91dda2015-06-18 15:47:46 +030093{
94 if (indexedType.isMatrix())
95 {
Olli Etuaho0c371002017-12-13 17:00:25 +040096 TType *fieldType = new TType(indexedType.getBasicType(), indexedType.getPrecision());
97 fieldType->setPrimarySize(static_cast<unsigned char>(indexedType.getRows()));
Olli Etuaho5d91dda2015-06-18 15:47:46 +030098 return fieldType;
99 }
100 else
101 {
Olli Etuaho0c371002017-12-13 17:00:25 +0400102 return new TType(indexedType.getBasicType(), indexedType.getPrecision());
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300103 }
104}
105
Olli Etuaho68981eb2018-01-23 17:46:12 +0200106const TType *GetBaseType(const TType &type, bool write)
107{
108 TType *baseType = new TType(type);
109 // Conservatively use highp here, even if the indexed type is not highp. That way the code can't
110 // end up using mediump version of an indexing function for a highp value, if both mediump and
111 // highp values are being indexed in the shader. For HLSL precision doesn't matter, but in
112 // principle this code could be used with multiple backends.
113 baseType->setPrecision(EbpHigh);
114 baseType->setQualifier(EvqInOut);
115 if (!write)
116 baseType->setQualifier(EvqIn);
117 return baseType;
118}
119
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300120// 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
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500170 int numCases = 0;
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300171 if (type.isMatrix())
172 {
173 numCases = type.getCols();
174 }
175 else
176 {
177 numCases = type.getNominalSize();
178 }
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300179
Olli Etuahofe486322017-03-21 09:30:54 +0000180 std::string functionName = GetIndexFunctionName(type, write);
Olli Etuaho0c371002017-12-13 17:00:25 +0400181 TIntermFunctionPrototype *prototypeNode = CreateInternalFunctionPrototypeNode(func);
Olli Etuaho8ad9e752017-01-16 19:55:20 +0000182
Olli Etuaho68981eb2018-01-23 17:46:12 +0200183 TIntermSymbol *baseParam = CreateParameterSymbol(func.getParam(0), symbolTable);
Olli Etuaho8ad9e752017-01-16 19:55:20 +0000184 prototypeNode->getSequence()->push_back(baseParam);
Olli Etuaho68981eb2018-01-23 17:46:12 +0200185 TIntermSymbol *indexParam = CreateParameterSymbol(func.getParam(1), symbolTable);
Olli Etuaho8ad9e752017-01-16 19:55:20 +0000186 prototypeNode->getSequence()->push_back(indexParam);
Olli Etuahob79b3f92017-07-21 14:23:21 +0300187 TIntermSymbol *valueParam = nullptr;
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300188 if (write)
189 {
Olli Etuaho68981eb2018-01-23 17:46:12 +0200190 valueParam = CreateParameterSymbol(func.getParam(2), symbolTable);
Olli Etuaho8ad9e752017-01-16 19:55:20 +0000191 prototypeNode->getSequence()->push_back(valueParam);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300192 }
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300193
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100194 TIntermBlock *statementList = new TIntermBlock();
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300195 for (int i = 0; i < numCases; ++i)
196 {
197 TIntermCase *caseNode = new TIntermCase(CreateIntConstantNode(i));
198 statementList->getSequence()->push_back(caseNode);
199
200 TIntermBinary *indexNode =
Olli Etuahob79b3f92017-07-21 14:23:21 +0300201 new TIntermBinary(EOpIndexDirect, baseParam->deepCopy(), CreateIndexNode(i));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300202 if (write)
203 {
Olli Etuahob79b3f92017-07-21 14:23:21 +0300204 TIntermBinary *assignNode =
205 new TIntermBinary(EOpAssign, indexNode, valueParam->deepCopy());
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300206 statementList->getSequence()->push_back(assignNode);
207 TIntermBranch *returnNode = new TIntermBranch(EOpReturn, nullptr);
208 statementList->getSequence()->push_back(returnNode);
209 }
210 else
211 {
212 TIntermBranch *returnNode = new TIntermBranch(EOpReturn, indexNode);
213 statementList->getSequence()->push_back(returnNode);
214 }
215 }
216
217 // Default case
218 TIntermCase *defaultNode = new TIntermCase(nullptr);
219 statementList->getSequence()->push_back(defaultNode);
220 TIntermBranch *breakNode = new TIntermBranch(EOpBreak, nullptr);
221 statementList->getSequence()->push_back(breakNode);
222
Olli Etuahob79b3f92017-07-21 14:23:21 +0300223 TIntermSwitch *switchNode = new TIntermSwitch(indexParam->deepCopy(), statementList);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300224
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100225 TIntermBlock *bodyNode = new TIntermBlock();
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300226 bodyNode->getSequence()->push_back(switchNode);
227
Olli Etuaho3272a6d2016-08-29 17:54:50 +0300228 TIntermBinary *cond =
Olli Etuahob79b3f92017-07-21 14:23:21 +0300229 new TIntermBinary(EOpLessThan, indexParam->deepCopy(), CreateIntConstantNode(0));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300230
231 // Two blocks: one accesses (either reads or writes) the first element and returns,
232 // the other accesses the last element.
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100233 TIntermBlock *useFirstBlock = new TIntermBlock();
234 TIntermBlock *useLastBlock = new TIntermBlock();
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300235 TIntermBinary *indexFirstNode =
Olli Etuahob79b3f92017-07-21 14:23:21 +0300236 new TIntermBinary(EOpIndexDirect, baseParam->deepCopy(), CreateIndexNode(0));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300237 TIntermBinary *indexLastNode =
Olli Etuahob79b3f92017-07-21 14:23:21 +0300238 new TIntermBinary(EOpIndexDirect, baseParam->deepCopy(), CreateIndexNode(numCases - 1));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300239 if (write)
240 {
Olli Etuahob79b3f92017-07-21 14:23:21 +0300241 TIntermBinary *assignFirstNode =
242 new TIntermBinary(EOpAssign, indexFirstNode, valueParam->deepCopy());
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300243 useFirstBlock->getSequence()->push_back(assignFirstNode);
244 TIntermBranch *returnNode = new TIntermBranch(EOpReturn, nullptr);
245 useFirstBlock->getSequence()->push_back(returnNode);
246
Olli Etuahob79b3f92017-07-21 14:23:21 +0300247 TIntermBinary *assignLastNode =
248 new TIntermBinary(EOpAssign, indexLastNode, valueParam->deepCopy());
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300249 useLastBlock->getSequence()->push_back(assignLastNode);
250 }
251 else
252 {
253 TIntermBranch *returnFirstNode = new TIntermBranch(EOpReturn, indexFirstNode);
254 useFirstBlock->getSequence()->push_back(returnFirstNode);
255
256 TIntermBranch *returnLastNode = new TIntermBranch(EOpReturn, indexLastNode);
257 useLastBlock->getSequence()->push_back(returnLastNode);
258 }
Olli Etuaho57961272016-09-14 13:57:46 +0300259 TIntermIfElse *ifNode = new TIntermIfElse(cond, useFirstBlock, nullptr);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300260 bodyNode->getSequence()->push_back(ifNode);
261 bodyNode->getSequence()->push_back(useLastBlock);
262
Olli Etuaho8ad9e752017-01-16 19:55:20 +0000263 TIntermFunctionDefinition *indexingFunction =
264 new TIntermFunctionDefinition(prototypeNode, bodyNode);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300265 return indexingFunction;
266}
267
268class RemoveDynamicIndexingTraverser : public TLValueTrackingTraverser
269{
270 public:
Olli Etuaho89a69a02017-10-23 12:20:45 +0300271 RemoveDynamicIndexingTraverser(TSymbolTable *symbolTable,
Olli Etuaho89a69a02017-10-23 12:20:45 +0300272 PerformanceDiagnostics *perfDiagnostics);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300273
274 bool visitBinary(Visit visit, TIntermBinary *node) override;
275
276 void insertHelperDefinitions(TIntermNode *root);
277
278 void nextIteration();
279
280 bool usedTreeInsertion() const { return mUsedTreeInsertion; }
281
282 protected:
Olli Etuahofe486322017-03-21 09:30:54 +0000283 // Maps of types that are indexed to the indexing function ids used for them. Note that these
284 // can not store multiple variants of the same type with different precisions - only one
285 // precision gets stored.
Olli Etuaho0c371002017-12-13 17:00:25 +0400286 std::map<TType, TFunction *> mIndexedVecAndMatrixTypes;
287 std::map<TType, TFunction *> mWrittenVecAndMatrixTypes;
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300288
289 bool mUsedTreeInsertion;
290
291 // When true, the traverser will remove side effects from any indexing expression.
292 // This is done so that in code like
293 // V[j++][i]++.
294 // where V is an array of vectors, j++ will only be evaluated once.
295 bool mRemoveIndexSideEffectsInSubtree;
Olli Etuaho89a69a02017-10-23 12:20:45 +0300296
297 PerformanceDiagnostics *mPerfDiagnostics;
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 Etuahofbb1c792018-01-19 16:26:59 +0200306 mPerfDiagnostics(perfDiagnostics)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300307{
308}
309
310void RemoveDynamicIndexingTraverser::insertHelperDefinitions(TIntermNode *root)
311{
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100312 TIntermBlock *rootBlock = root->getAsBlock();
313 ASSERT(rootBlock != nullptr);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300314 TIntermSequence insertions;
Olli Etuahofe486322017-03-21 09:30:54 +0000315 for (auto &type : mIndexedVecAndMatrixTypes)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300316 {
Olli Etuahob79b3f92017-07-21 14:23:21 +0300317 insertions.push_back(
318 GetIndexFunctionDefinition(type.first, false, *type.second, mSymbolTable));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300319 }
Olli Etuahofe486322017-03-21 09:30:54 +0000320 for (auto &type : mWrittenVecAndMatrixTypes)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300321 {
Olli Etuahob79b3f92017-07-21 14:23:21 +0300322 insertions.push_back(
323 GetIndexFunctionDefinition(type.first, true, *type.second, mSymbolTable));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300324 }
Olli Etuaho56612d62017-10-12 15:46:30 +0300325 rootBlock->insertChildNodes(0, insertions);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300326}
327
328// Create a call to dyn_index_*() based on an indirect indexing op node
329TIntermAggregate *CreateIndexFunctionCall(TIntermBinary *node,
Olli Etuahofe486322017-03-21 09:30:54 +0000330 TIntermTyped *index,
Olli Etuaho0c371002017-12-13 17:00:25 +0400331 TFunction *indexingFunction)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300332{
333 ASSERT(node->getOp() == EOpIndexIndirect);
Olli Etuahoaf6fc1b2017-01-26 17:45:35 -0800334 TIntermSequence *arguments = new TIntermSequence();
Olli Etuahofe486322017-03-21 09:30:54 +0000335 arguments->push_back(node->getLeft());
Olli Etuahoaf6fc1b2017-01-26 17:45:35 -0800336 arguments->push_back(index);
337
Olli Etuaho3ec75682017-07-05 17:02:55 +0300338 TIntermAggregate *indexingCall =
Olli Etuaho0c371002017-12-13 17:00:25 +0400339 TIntermAggregate::CreateFunctionCall(*indexingFunction, arguments);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300340 indexingCall->setLine(node->getLine());
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300341 return indexingCall;
342}
343
344TIntermAggregate *CreateIndexedWriteFunctionCall(TIntermBinary *node,
Olli Etuaho195be942017-12-04 23:40:14 +0200345 TVariable *index,
346 TVariable *writtenValue,
Olli Etuaho0c371002017-12-13 17:00:25 +0400347 TFunction *indexedWriteFunction)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300348{
Olli Etuahofe486322017-03-21 09:30:54 +0000349 ASSERT(node->getOp() == EOpIndexIndirect);
350 TIntermSequence *arguments = new TIntermSequence();
351 // Deep copy the child nodes so that two pointers to the same node don't end up in the tree.
352 arguments->push_back(node->getLeft()->deepCopy());
Olli Etuaho195be942017-12-04 23:40:14 +0200353 arguments->push_back(CreateTempSymbolNode(index));
354 arguments->push_back(CreateTempSymbolNode(writtenValue));
Olli Etuahofe486322017-03-21 09:30:54 +0000355
Olli Etuaho3ec75682017-07-05 17:02:55 +0300356 TIntermAggregate *indexedWriteCall =
Olli Etuaho0c371002017-12-13 17:00:25 +0400357 TIntermAggregate::CreateFunctionCall(*indexedWriteFunction, arguments);
Olli Etuahofe486322017-03-21 09:30:54 +0000358 indexedWriteCall->setLine(node->getLine());
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300359 return indexedWriteCall;
360}
361
362bool RemoveDynamicIndexingTraverser::visitBinary(Visit visit, TIntermBinary *node)
363{
364 if (mUsedTreeInsertion)
365 return false;
366
367 if (node->getOp() == EOpIndexIndirect)
368 {
369 if (mRemoveIndexSideEffectsInSubtree)
370 {
371 ASSERT(node->getRight()->hasSideEffects());
372 // In case we're just removing index side effects, convert
373 // v_expr[index_expr]
374 // to this:
375 // int s0 = index_expr; v_expr[s0];
376 // Now v_expr[s0] can be safely executed several times without unintended side effects.
Olli Etuaho195be942017-12-04 23:40:14 +0200377 TIntermDeclaration *indexVariableDeclaration = nullptr;
378 TVariable *indexVariable = DeclareTempVariable(mSymbolTable, node->getRight(),
379 EvqTemporary, &indexVariableDeclaration);
380 insertStatementInParentBlock(indexVariableDeclaration);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300381 mUsedTreeInsertion = true;
382
383 // Replace the index with the temp variable
Olli Etuaho195be942017-12-04 23:40:14 +0200384 TIntermSymbol *tempIndex = CreateTempSymbolNode(indexVariable);
Jamie Madill03d863c2016-07-27 18:15:53 -0400385 queueReplacementWithParent(node, node->getRight(), tempIndex, OriginalNode::IS_DROPPED);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300386 }
Jamie Madill666f65a2016-08-26 01:34:37 +0000387 else if (IntermNodePatternMatcher::IsDynamicIndexingOfVectorOrMatrix(node))
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300388 {
Olli Etuaho89a69a02017-10-23 12:20:45 +0300389 mPerfDiagnostics->warning(node->getLine(),
390 "Performance: dynamic indexing of vectors and "
391 "matrices is emulated and can be slow.",
392 "[]");
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300393 bool write = isLValueRequiredHere();
394
Jamie Madill666f65a2016-08-26 01:34:37 +0000395#if defined(ANGLE_ENABLE_ASSERTS)
396 // Make sure that IntermNodePatternMatcher is consistent with the slightly differently
397 // implemented checks in this traverser.
398 IntermNodePatternMatcher matcher(
399 IntermNodePatternMatcher::kDynamicIndexingOfVectorOrMatrixInLValue);
400 ASSERT(matcher.match(node, getParentNode(), isLValueRequiredHere()) == write);
401#endif
402
Olli Etuahofe486322017-03-21 09:30:54 +0000403 const TType &type = node->getLeft()->getType();
Olli Etuahofbb1c792018-01-19 16:26:59 +0200404 ImmutableString indexingFunctionName(GetIndexFunctionName(type, false));
Olli Etuaho0c371002017-12-13 17:00:25 +0400405 TFunction *indexingFunction = nullptr;
Olli Etuahofe486322017-03-21 09:30:54 +0000406 if (mIndexedVecAndMatrixTypes.find(type) == mIndexedVecAndMatrixTypes.end())
407 {
Olli Etuaho0c371002017-12-13 17:00:25 +0400408 indexingFunction =
409 new TFunction(mSymbolTable, indexingFunctionName, GetFieldType(type),
410 SymbolType::AngleInternal, true);
Olli Etuaho68981eb2018-01-23 17:46:12 +0200411 indexingFunction->addParameter(
Olli Etuahofbb1c792018-01-19 16:26:59 +0200412 TConstParameter(kBaseName, GetBaseType(type, false)));
413 indexingFunction->addParameter(TConstParameter(kIndexName, kIndexType));
Olli Etuaho0c371002017-12-13 17:00:25 +0400414 mIndexedVecAndMatrixTypes[type] = indexingFunction;
Olli Etuahofe486322017-03-21 09:30:54 +0000415 }
416 else
417 {
Olli Etuaho0c371002017-12-13 17:00:25 +0400418 indexingFunction = mIndexedVecAndMatrixTypes[type];
Olli Etuahofe486322017-03-21 09:30:54 +0000419 }
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300420
421 if (write)
422 {
423 // Convert:
424 // v_expr[index_expr]++;
425 // to this:
426 // int s0 = index_expr; float s1 = dyn_index(v_expr, s0); s1++;
427 // dyn_index_write(v_expr, s0, s1);
428 // This works even if index_expr has some side effects.
429 if (node->getLeft()->hasSideEffects())
430 {
431 // If v_expr has side effects, those need to be removed before proceeding.
432 // Otherwise the side effects of v_expr would be evaluated twice.
433 // The only case where an l-value can have side effects is when it is
434 // indexing. For example, it can be V[j++] where V is an array of vectors.
435 mRemoveIndexSideEffectsInSubtree = true;
436 return true;
437 }
Olli Etuaho8f6eb2a2017-01-12 17:04:58 +0000438
439 TIntermBinary *leftBinary = node->getLeft()->getAsBinaryNode();
440 if (leftBinary != nullptr &&
441 IntermNodePatternMatcher::IsDynamicIndexingOfVectorOrMatrix(leftBinary))
442 {
443 // This is a case like:
444 // mat2 m;
445 // m[a][b]++;
446 // Process the child node m[a] first.
447 return true;
448 }
449
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300450 // TODO(oetuaho@nvidia.com): This is not optimal if the expression using the value
451 // only writes it and doesn't need the previous value. http://anglebug.com/1116
452
Olli Etuaho0c371002017-12-13 17:00:25 +0400453 TFunction *indexedWriteFunction = nullptr;
Olli Etuahofe486322017-03-21 09:30:54 +0000454 if (mWrittenVecAndMatrixTypes.find(type) == mWrittenVecAndMatrixTypes.end())
455 {
Olli Etuahofbb1c792018-01-19 16:26:59 +0200456 ImmutableString functionName(
457 GetIndexFunctionName(node->getLeft()->getType(), true));
Olli Etuaho0c371002017-12-13 17:00:25 +0400458 indexedWriteFunction =
Olli Etuaho68981eb2018-01-23 17:46:12 +0200459 new TFunction(mSymbolTable, functionName, StaticType::GetBasic<EbtVoid>(),
Olli Etuaho0c371002017-12-13 17:00:25 +0400460 SymbolType::AngleInternal, false);
Olli Etuaho68981eb2018-01-23 17:46:12 +0200461 indexedWriteFunction->addParameter(
Olli Etuahofbb1c792018-01-19 16:26:59 +0200462 TConstParameter(kBaseName, GetBaseType(type, true)));
463 indexedWriteFunction->addParameter(TConstParameter(kIndexName, kIndexType));
Olli Etuaho68981eb2018-01-23 17:46:12 +0200464 TType *valueType = GetFieldType(type);
465 valueType->setQualifier(EvqIn);
466 indexedWriteFunction->addParameter(
Olli Etuahofbb1c792018-01-19 16:26:59 +0200467 TConstParameter(kValueName, static_cast<const TType *>(valueType)));
Olli Etuaho0c371002017-12-13 17:00:25 +0400468 mWrittenVecAndMatrixTypes[type] = indexedWriteFunction;
Olli Etuahofe486322017-03-21 09:30:54 +0000469 }
470 else
471 {
Olli Etuaho0c371002017-12-13 17:00:25 +0400472 indexedWriteFunction = mWrittenVecAndMatrixTypes[type];
Olli Etuahofe486322017-03-21 09:30:54 +0000473 }
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300474
475 TIntermSequence insertionsBefore;
476 TIntermSequence insertionsAfter;
477
478 // Store the index in a temporary signed int variable.
Olli Etuaho195be942017-12-04 23:40:14 +0200479 // s0 = index_expr;
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300480 TIntermTyped *indexInitializer = EnsureSignedInt(node->getRight());
Olli Etuaho195be942017-12-04 23:40:14 +0200481 TIntermDeclaration *indexVariableDeclaration = nullptr;
482 TVariable *indexVariable = DeclareTempVariable(
483 mSymbolTable, indexInitializer, EvqTemporary, &indexVariableDeclaration);
484 insertionsBefore.push_back(indexVariableDeclaration);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300485
Olli Etuaho195be942017-12-04 23:40:14 +0200486 // s1 = dyn_index(v_expr, s0);
487 TIntermAggregate *indexingCall = CreateIndexFunctionCall(
Olli Etuaho0c371002017-12-13 17:00:25 +0400488 node, CreateTempSymbolNode(indexVariable), indexingFunction);
Olli Etuaho195be942017-12-04 23:40:14 +0200489 TIntermDeclaration *fieldVariableDeclaration = nullptr;
490 TVariable *fieldVariable = DeclareTempVariable(
491 mSymbolTable, indexingCall, EvqTemporary, &fieldVariableDeclaration);
492 insertionsBefore.push_back(fieldVariableDeclaration);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300493
Olli Etuaho195be942017-12-04 23:40:14 +0200494 // dyn_index_write(v_expr, s0, s1);
Olli Etuahofe486322017-03-21 09:30:54 +0000495 TIntermAggregate *indexedWriteCall = CreateIndexedWriteFunctionCall(
Olli Etuaho0c371002017-12-13 17:00:25 +0400496 node, indexVariable, fieldVariable, indexedWriteFunction);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300497 insertionsAfter.push_back(indexedWriteCall);
498 insertStatementsInParentBlock(insertionsBefore, insertionsAfter);
Olli Etuaho195be942017-12-04 23:40:14 +0200499
500 // replace the node with s1
501 queueReplacement(CreateTempSymbolNode(fieldVariable), OriginalNode::IS_DROPPED);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300502 mUsedTreeInsertion = true;
503 }
504 else
505 {
506 // The indexed value is not being written, so we can simply convert
507 // v_expr[index_expr]
508 // into
509 // dyn_index(v_expr, index_expr)
510 // If the index_expr is unsigned, we'll convert it to signed.
511 ASSERT(!mRemoveIndexSideEffectsInSubtree);
512 TIntermAggregate *indexingCall = CreateIndexFunctionCall(
Olli Etuaho0c371002017-12-13 17:00:25 +0400513 node, EnsureSignedInt(node->getRight()), indexingFunction);
Olli Etuahoea39a222017-07-06 12:47:59 +0300514 queueReplacement(indexingCall, OriginalNode::IS_DROPPED);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300515 }
516 }
517 }
518 return !mUsedTreeInsertion;
519}
520
521void RemoveDynamicIndexingTraverser::nextIteration()
522{
523 mUsedTreeInsertion = false;
524 mRemoveIndexSideEffectsInSubtree = false;
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300525}
526
527} // namespace
528
Olli Etuaho89a69a02017-10-23 12:20:45 +0300529void RemoveDynamicIndexing(TIntermNode *root,
530 TSymbolTable *symbolTable,
Olli Etuaho89a69a02017-10-23 12:20:45 +0300531 PerformanceDiagnostics *perfDiagnostics)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300532{
Olli Etuaho68981eb2018-01-23 17:46:12 +0200533 RemoveDynamicIndexingTraverser traverser(symbolTable, perfDiagnostics);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300534 do
535 {
536 traverser.nextIteration();
537 root->traverse(&traverser);
538 traverser.updateTree();
539 } while (traverser.usedTreeInsertion());
Olli Etuaho56612d62017-10-12 15:46:30 +0300540 // TODO(oetuaho@nvidia.com): It might be nicer to add the helper definitions also in the middle
Olli Etuaho1ecd14b2017-01-26 13:54:15 -0800541 // of traversal. Now the tree ends up in an inconsistent state in the middle, since there are
542 // function call nodes with no corresponding definition nodes. This needs special handling in
543 // TIntermLValueTrackingTraverser, and creates intricacies that are not easily apparent from a
544 // superficial reading of the code.
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300545 traverser.insertHelperDefinitions(root);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300546}
Jamie Madill45bcc782016-11-07 13:58:48 -0500547
548} // namespace sh