blob: 1357676a3b6d0f12ba65088ec6b0c90132ae38cb [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 Etuaho5d91dda2015-06-18 15:47:46 +030068TIntermConstantUnion *CreateIntConstantNode(int i)
69{
70 TConstantUnion *constant = new TConstantUnion();
71 constant->setIConst(i);
72 return new TIntermConstantUnion(constant, TType(EbtInt, EbpHigh));
73}
74
Olli Etuaho5d91dda2015-06-18 15:47:46 +030075TIntermTyped *EnsureSignedInt(TIntermTyped *node)
76{
77 if (node->getBasicType() == EbtInt)
78 return node;
79
Olli Etuahoaf6fc1b2017-01-26 17:45:35 -080080 TIntermSequence *arguments = new TIntermSequence();
81 arguments->push_back(node);
Olli Etuahoa7ecec32017-05-08 17:43:55 +030082 return TIntermAggregate::CreateConstructor(TType(EbtInt), arguments);
Olli Etuaho5d91dda2015-06-18 15:47:46 +030083}
84
Olli Etuaho0c371002017-12-13 17:00:25 +040085TType *GetFieldType(const TType &indexedType)
Olli Etuaho5d91dda2015-06-18 15:47:46 +030086{
87 if (indexedType.isMatrix())
88 {
Olli Etuaho0c371002017-12-13 17:00:25 +040089 TType *fieldType = new TType(indexedType.getBasicType(), indexedType.getPrecision());
90 fieldType->setPrimarySize(static_cast<unsigned char>(indexedType.getRows()));
Olli Etuaho5d91dda2015-06-18 15:47:46 +030091 return fieldType;
92 }
93 else
94 {
Olli Etuaho0c371002017-12-13 17:00:25 +040095 return new TType(indexedType.getBasicType(), indexedType.getPrecision());
Olli Etuaho5d91dda2015-06-18 15:47:46 +030096 }
97}
98
Olli Etuaho68981eb2018-01-23 17:46:12 +020099const TType *GetBaseType(const TType &type, bool write)
100{
101 TType *baseType = new TType(type);
102 // Conservatively use highp here, even if the indexed type is not highp. That way the code can't
103 // end up using mediump version of an indexing function for a highp value, if both mediump and
104 // highp values are being indexed in the shader. For HLSL precision doesn't matter, but in
105 // principle this code could be used with multiple backends.
106 baseType->setPrecision(EbpHigh);
107 baseType->setQualifier(EvqInOut);
108 if (!write)
109 baseType->setQualifier(EvqIn);
110 return baseType;
111}
112
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300113// Generate a read or write function for one field in a vector/matrix.
114// Out-of-range indices are clamped. This is consistent with how ANGLE handles out-of-range
115// indices in other places.
116// Note that indices can be either int or uint. We create only int versions of the functions,
117// and convert uint indices to int at the call site.
118// read function example:
119// float dyn_index_vec2(in vec2 base, in int index)
120// {
121// switch(index)
122// {
123// case (0):
124// return base[0];
125// case (1):
126// return base[1];
127// default:
128// break;
129// }
130// if (index < 0)
131// return base[0];
132// return base[1];
133// }
134// write function example:
135// void dyn_index_write_vec2(inout vec2 base, in int index, in float value)
136// {
137// switch(index)
138// {
139// case (0):
140// base[0] = value;
141// return;
142// case (1):
143// base[1] = value;
144// return;
145// default:
146// break;
147// }
148// if (index < 0)
149// {
150// base[0] = value;
151// return;
152// }
153// base[1] = value;
154// }
155// Note that else is not used in above functions to avoid the RewriteElseBlocks transformation.
Olli Etuaho195be942017-12-04 23:40:14 +0200156TIntermFunctionDefinition *GetIndexFunctionDefinition(const TType &type,
Olli Etuahofe486322017-03-21 09:30:54 +0000157 bool write,
Olli Etuaho0c371002017-12-13 17:00:25 +0400158 const TFunction &func,
Olli Etuahob79b3f92017-07-21 14:23:21 +0300159 TSymbolTable *symbolTable)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300160{
161 ASSERT(!type.isArray());
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300162
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500163 int numCases = 0;
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300164 if (type.isMatrix())
165 {
166 numCases = type.getCols();
167 }
168 else
169 {
170 numCases = type.getNominalSize();
171 }
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300172
Olli Etuahofe486322017-03-21 09:30:54 +0000173 std::string functionName = GetIndexFunctionName(type, write);
Olli Etuaho0c371002017-12-13 17:00:25 +0400174 TIntermFunctionPrototype *prototypeNode = CreateInternalFunctionPrototypeNode(func);
Olli Etuaho8ad9e752017-01-16 19:55:20 +0000175
Olli Etuahod4bd9632018-03-08 16:32:44 +0200176 TIntermSymbol *baseParam = new TIntermSymbol(func.getParam(0));
177 TIntermSymbol *indexParam = new TIntermSymbol(func.getParam(1));
Olli Etuahob79b3f92017-07-21 14:23:21 +0300178 TIntermSymbol *valueParam = nullptr;
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300179 if (write)
180 {
Olli Etuahod4bd9632018-03-08 16:32:44 +0200181 valueParam = new TIntermSymbol(func.getParam(2));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300182 }
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300183
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100184 TIntermBlock *statementList = new TIntermBlock();
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300185 for (int i = 0; i < numCases; ++i)
186 {
187 TIntermCase *caseNode = new TIntermCase(CreateIntConstantNode(i));
188 statementList->getSequence()->push_back(caseNode);
189
190 TIntermBinary *indexNode =
Olli Etuahob79b3f92017-07-21 14:23:21 +0300191 new TIntermBinary(EOpIndexDirect, baseParam->deepCopy(), CreateIndexNode(i));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300192 if (write)
193 {
Olli Etuahob79b3f92017-07-21 14:23:21 +0300194 TIntermBinary *assignNode =
195 new TIntermBinary(EOpAssign, indexNode, valueParam->deepCopy());
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300196 statementList->getSequence()->push_back(assignNode);
197 TIntermBranch *returnNode = new TIntermBranch(EOpReturn, nullptr);
198 statementList->getSequence()->push_back(returnNode);
199 }
200 else
201 {
202 TIntermBranch *returnNode = new TIntermBranch(EOpReturn, indexNode);
203 statementList->getSequence()->push_back(returnNode);
204 }
205 }
206
207 // Default case
208 TIntermCase *defaultNode = new TIntermCase(nullptr);
209 statementList->getSequence()->push_back(defaultNode);
210 TIntermBranch *breakNode = new TIntermBranch(EOpBreak, nullptr);
211 statementList->getSequence()->push_back(breakNode);
212
Olli Etuahob79b3f92017-07-21 14:23:21 +0300213 TIntermSwitch *switchNode = new TIntermSwitch(indexParam->deepCopy(), statementList);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300214
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100215 TIntermBlock *bodyNode = new TIntermBlock();
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300216 bodyNode->getSequence()->push_back(switchNode);
217
Olli Etuaho3272a6d2016-08-29 17:54:50 +0300218 TIntermBinary *cond =
Olli Etuahob79b3f92017-07-21 14:23:21 +0300219 new TIntermBinary(EOpLessThan, indexParam->deepCopy(), CreateIntConstantNode(0));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300220
221 // Two blocks: one accesses (either reads or writes) the first element and returns,
222 // the other accesses the last element.
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100223 TIntermBlock *useFirstBlock = new TIntermBlock();
224 TIntermBlock *useLastBlock = new TIntermBlock();
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300225 TIntermBinary *indexFirstNode =
Olli Etuahob79b3f92017-07-21 14:23:21 +0300226 new TIntermBinary(EOpIndexDirect, baseParam->deepCopy(), CreateIndexNode(0));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300227 TIntermBinary *indexLastNode =
Olli Etuahob79b3f92017-07-21 14:23:21 +0300228 new TIntermBinary(EOpIndexDirect, baseParam->deepCopy(), CreateIndexNode(numCases - 1));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300229 if (write)
230 {
Olli Etuahob79b3f92017-07-21 14:23:21 +0300231 TIntermBinary *assignFirstNode =
232 new TIntermBinary(EOpAssign, indexFirstNode, valueParam->deepCopy());
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300233 useFirstBlock->getSequence()->push_back(assignFirstNode);
234 TIntermBranch *returnNode = new TIntermBranch(EOpReturn, nullptr);
235 useFirstBlock->getSequence()->push_back(returnNode);
236
Olli Etuahob79b3f92017-07-21 14:23:21 +0300237 TIntermBinary *assignLastNode =
238 new TIntermBinary(EOpAssign, indexLastNode, valueParam->deepCopy());
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300239 useLastBlock->getSequence()->push_back(assignLastNode);
240 }
241 else
242 {
243 TIntermBranch *returnFirstNode = new TIntermBranch(EOpReturn, indexFirstNode);
244 useFirstBlock->getSequence()->push_back(returnFirstNode);
245
246 TIntermBranch *returnLastNode = new TIntermBranch(EOpReturn, indexLastNode);
247 useLastBlock->getSequence()->push_back(returnLastNode);
248 }
Olli Etuaho57961272016-09-14 13:57:46 +0300249 TIntermIfElse *ifNode = new TIntermIfElse(cond, useFirstBlock, nullptr);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300250 bodyNode->getSequence()->push_back(ifNode);
251 bodyNode->getSequence()->push_back(useLastBlock);
252
Olli Etuaho8ad9e752017-01-16 19:55:20 +0000253 TIntermFunctionDefinition *indexingFunction =
254 new TIntermFunctionDefinition(prototypeNode, bodyNode);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300255 return indexingFunction;
256}
257
258class RemoveDynamicIndexingTraverser : public TLValueTrackingTraverser
259{
260 public:
Olli Etuaho89a69a02017-10-23 12:20:45 +0300261 RemoveDynamicIndexingTraverser(TSymbolTable *symbolTable,
Olli Etuaho89a69a02017-10-23 12:20:45 +0300262 PerformanceDiagnostics *perfDiagnostics);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300263
264 bool visitBinary(Visit visit, TIntermBinary *node) override;
265
266 void insertHelperDefinitions(TIntermNode *root);
267
268 void nextIteration();
269
270 bool usedTreeInsertion() const { return mUsedTreeInsertion; }
271
272 protected:
Olli Etuahofe486322017-03-21 09:30:54 +0000273 // Maps of types that are indexed to the indexing function ids used for them. Note that these
274 // can not store multiple variants of the same type with different precisions - only one
275 // precision gets stored.
Olli Etuaho0c371002017-12-13 17:00:25 +0400276 std::map<TType, TFunction *> mIndexedVecAndMatrixTypes;
277 std::map<TType, TFunction *> mWrittenVecAndMatrixTypes;
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300278
279 bool mUsedTreeInsertion;
280
281 // When true, the traverser will remove side effects from any indexing expression.
282 // This is done so that in code like
283 // V[j++][i]++.
284 // where V is an array of vectors, j++ will only be evaluated once.
285 bool mRemoveIndexSideEffectsInSubtree;
Olli Etuaho89a69a02017-10-23 12:20:45 +0300286
287 PerformanceDiagnostics *mPerfDiagnostics;
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300288};
289
Olli Etuaho89a69a02017-10-23 12:20:45 +0300290RemoveDynamicIndexingTraverser::RemoveDynamicIndexingTraverser(
291 TSymbolTable *symbolTable,
Olli Etuaho89a69a02017-10-23 12:20:45 +0300292 PerformanceDiagnostics *perfDiagnostics)
Olli Etuaho68981eb2018-01-23 17:46:12 +0200293 : TLValueTrackingTraverser(true, false, false, symbolTable),
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300294 mUsedTreeInsertion(false),
Olli Etuaho89a69a02017-10-23 12:20:45 +0300295 mRemoveIndexSideEffectsInSubtree(false),
Olli Etuahofbb1c792018-01-19 16:26:59 +0200296 mPerfDiagnostics(perfDiagnostics)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300297{
298}
299
300void RemoveDynamicIndexingTraverser::insertHelperDefinitions(TIntermNode *root)
301{
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100302 TIntermBlock *rootBlock = root->getAsBlock();
303 ASSERT(rootBlock != nullptr);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300304 TIntermSequence insertions;
Olli Etuahofe486322017-03-21 09:30:54 +0000305 for (auto &type : mIndexedVecAndMatrixTypes)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300306 {
Olli Etuahob79b3f92017-07-21 14:23:21 +0300307 insertions.push_back(
308 GetIndexFunctionDefinition(type.first, false, *type.second, mSymbolTable));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300309 }
Olli Etuahofe486322017-03-21 09:30:54 +0000310 for (auto &type : mWrittenVecAndMatrixTypes)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300311 {
Olli Etuahob79b3f92017-07-21 14:23:21 +0300312 insertions.push_back(
313 GetIndexFunctionDefinition(type.first, true, *type.second, mSymbolTable));
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300314 }
Olli Etuaho56612d62017-10-12 15:46:30 +0300315 rootBlock->insertChildNodes(0, insertions);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300316}
317
318// Create a call to dyn_index_*() based on an indirect indexing op node
319TIntermAggregate *CreateIndexFunctionCall(TIntermBinary *node,
Olli Etuahofe486322017-03-21 09:30:54 +0000320 TIntermTyped *index,
Olli Etuaho0c371002017-12-13 17:00:25 +0400321 TFunction *indexingFunction)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300322{
323 ASSERT(node->getOp() == EOpIndexIndirect);
Olli Etuahoaf6fc1b2017-01-26 17:45:35 -0800324 TIntermSequence *arguments = new TIntermSequence();
Olli Etuahofe486322017-03-21 09:30:54 +0000325 arguments->push_back(node->getLeft());
Olli Etuahoaf6fc1b2017-01-26 17:45:35 -0800326 arguments->push_back(index);
327
Olli Etuaho3ec75682017-07-05 17:02:55 +0300328 TIntermAggregate *indexingCall =
Olli Etuaho0c371002017-12-13 17:00:25 +0400329 TIntermAggregate::CreateFunctionCall(*indexingFunction, arguments);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300330 indexingCall->setLine(node->getLine());
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300331 return indexingCall;
332}
333
334TIntermAggregate *CreateIndexedWriteFunctionCall(TIntermBinary *node,
Olli Etuaho195be942017-12-04 23:40:14 +0200335 TVariable *index,
336 TVariable *writtenValue,
Olli Etuaho0c371002017-12-13 17:00:25 +0400337 TFunction *indexedWriteFunction)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300338{
Olli Etuahofe486322017-03-21 09:30:54 +0000339 ASSERT(node->getOp() == EOpIndexIndirect);
340 TIntermSequence *arguments = new TIntermSequence();
341 // Deep copy the child nodes so that two pointers to the same node don't end up in the tree.
342 arguments->push_back(node->getLeft()->deepCopy());
Olli Etuaho195be942017-12-04 23:40:14 +0200343 arguments->push_back(CreateTempSymbolNode(index));
344 arguments->push_back(CreateTempSymbolNode(writtenValue));
Olli Etuahofe486322017-03-21 09:30:54 +0000345
Olli Etuaho3ec75682017-07-05 17:02:55 +0300346 TIntermAggregate *indexedWriteCall =
Olli Etuaho0c371002017-12-13 17:00:25 +0400347 TIntermAggregate::CreateFunctionCall(*indexedWriteFunction, arguments);
Olli Etuahofe486322017-03-21 09:30:54 +0000348 indexedWriteCall->setLine(node->getLine());
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300349 return indexedWriteCall;
350}
351
352bool RemoveDynamicIndexingTraverser::visitBinary(Visit visit, TIntermBinary *node)
353{
354 if (mUsedTreeInsertion)
355 return false;
356
357 if (node->getOp() == EOpIndexIndirect)
358 {
359 if (mRemoveIndexSideEffectsInSubtree)
360 {
361 ASSERT(node->getRight()->hasSideEffects());
362 // In case we're just removing index side effects, convert
363 // v_expr[index_expr]
364 // to this:
365 // int s0 = index_expr; v_expr[s0];
366 // Now v_expr[s0] can be safely executed several times without unintended side effects.
Olli Etuaho195be942017-12-04 23:40:14 +0200367 TIntermDeclaration *indexVariableDeclaration = nullptr;
368 TVariable *indexVariable = DeclareTempVariable(mSymbolTable, node->getRight(),
369 EvqTemporary, &indexVariableDeclaration);
370 insertStatementInParentBlock(indexVariableDeclaration);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300371 mUsedTreeInsertion = true;
372
373 // Replace the index with the temp variable
Olli Etuaho195be942017-12-04 23:40:14 +0200374 TIntermSymbol *tempIndex = CreateTempSymbolNode(indexVariable);
Jamie Madill03d863c2016-07-27 18:15:53 -0400375 queueReplacementWithParent(node, node->getRight(), tempIndex, OriginalNode::IS_DROPPED);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300376 }
Jamie Madill666f65a2016-08-26 01:34:37 +0000377 else if (IntermNodePatternMatcher::IsDynamicIndexingOfVectorOrMatrix(node))
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300378 {
Olli Etuaho89a69a02017-10-23 12:20:45 +0300379 mPerfDiagnostics->warning(node->getLine(),
380 "Performance: dynamic indexing of vectors and "
381 "matrices is emulated and can be slow.",
382 "[]");
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300383 bool write = isLValueRequiredHere();
384
Jamie Madill666f65a2016-08-26 01:34:37 +0000385#if defined(ANGLE_ENABLE_ASSERTS)
386 // Make sure that IntermNodePatternMatcher is consistent with the slightly differently
387 // implemented checks in this traverser.
388 IntermNodePatternMatcher matcher(
389 IntermNodePatternMatcher::kDynamicIndexingOfVectorOrMatrixInLValue);
390 ASSERT(matcher.match(node, getParentNode(), isLValueRequiredHere()) == write);
391#endif
392
Olli Etuahofe486322017-03-21 09:30:54 +0000393 const TType &type = node->getLeft()->getType();
Olli Etuahofbb1c792018-01-19 16:26:59 +0200394 ImmutableString indexingFunctionName(GetIndexFunctionName(type, false));
Olli Etuaho0c371002017-12-13 17:00:25 +0400395 TFunction *indexingFunction = nullptr;
Olli Etuahofe486322017-03-21 09:30:54 +0000396 if (mIndexedVecAndMatrixTypes.find(type) == mIndexedVecAndMatrixTypes.end())
397 {
Olli Etuaho0c371002017-12-13 17:00:25 +0400398 indexingFunction =
Olli Etuaho029e8ca2018-02-16 14:06:49 +0200399 new TFunction(mSymbolTable, indexingFunctionName, SymbolType::AngleInternal,
400 GetFieldType(type), true);
Olli Etuahod4bd9632018-03-08 16:32:44 +0200401 indexingFunction->addParameter(new TVariable(
402 mSymbolTable, kBaseName, GetBaseType(type, false), SymbolType::AngleInternal));
Olli Etuaho68981eb2018-01-23 17:46:12 +0200403 indexingFunction->addParameter(
Olli Etuahod4bd9632018-03-08 16:32:44 +0200404 new TVariable(mSymbolTable, kIndexName, kIndexType, SymbolType::AngleInternal));
Olli Etuaho0c371002017-12-13 17:00:25 +0400405 mIndexedVecAndMatrixTypes[type] = indexingFunction;
Olli Etuahofe486322017-03-21 09:30:54 +0000406 }
407 else
408 {
Olli Etuaho0c371002017-12-13 17:00:25 +0400409 indexingFunction = mIndexedVecAndMatrixTypes[type];
Olli Etuahofe486322017-03-21 09:30:54 +0000410 }
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300411
412 if (write)
413 {
414 // Convert:
415 // v_expr[index_expr]++;
416 // to this:
417 // int s0 = index_expr; float s1 = dyn_index(v_expr, s0); s1++;
418 // dyn_index_write(v_expr, s0, s1);
419 // This works even if index_expr has some side effects.
420 if (node->getLeft()->hasSideEffects())
421 {
422 // If v_expr has side effects, those need to be removed before proceeding.
423 // Otherwise the side effects of v_expr would be evaluated twice.
424 // The only case where an l-value can have side effects is when it is
425 // indexing. For example, it can be V[j++] where V is an array of vectors.
426 mRemoveIndexSideEffectsInSubtree = true;
427 return true;
428 }
Olli Etuaho8f6eb2a2017-01-12 17:04:58 +0000429
430 TIntermBinary *leftBinary = node->getLeft()->getAsBinaryNode();
431 if (leftBinary != nullptr &&
432 IntermNodePatternMatcher::IsDynamicIndexingOfVectorOrMatrix(leftBinary))
433 {
434 // This is a case like:
435 // mat2 m;
436 // m[a][b]++;
437 // Process the child node m[a] first.
438 return true;
439 }
440
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300441 // TODO(oetuaho@nvidia.com): This is not optimal if the expression using the value
442 // only writes it and doesn't need the previous value. http://anglebug.com/1116
443
Olli Etuaho0c371002017-12-13 17:00:25 +0400444 TFunction *indexedWriteFunction = nullptr;
Olli Etuahofe486322017-03-21 09:30:54 +0000445 if (mWrittenVecAndMatrixTypes.find(type) == mWrittenVecAndMatrixTypes.end())
446 {
Olli Etuahofbb1c792018-01-19 16:26:59 +0200447 ImmutableString functionName(
448 GetIndexFunctionName(node->getLeft()->getType(), true));
Olli Etuaho0c371002017-12-13 17:00:25 +0400449 indexedWriteFunction =
Olli Etuaho029e8ca2018-02-16 14:06:49 +0200450 new TFunction(mSymbolTable, functionName, SymbolType::AngleInternal,
451 StaticType::GetBasic<EbtVoid>(), false);
Olli Etuahod4bd9632018-03-08 16:32:44 +0200452 indexedWriteFunction->addParameter(new TVariable(mSymbolTable, kBaseName,
453 GetBaseType(type, true),
454 SymbolType::AngleInternal));
455 indexedWriteFunction->addParameter(new TVariable(
456 mSymbolTable, kIndexName, kIndexType, SymbolType::AngleInternal));
Olli Etuaho68981eb2018-01-23 17:46:12 +0200457 TType *valueType = GetFieldType(type);
458 valueType->setQualifier(EvqIn);
Olli Etuahod4bd9632018-03-08 16:32:44 +0200459 indexedWriteFunction->addParameter(new TVariable(
460 mSymbolTable, kValueName, static_cast<const TType *>(valueType),
461 SymbolType::AngleInternal));
Olli Etuaho0c371002017-12-13 17:00:25 +0400462 mWrittenVecAndMatrixTypes[type] = indexedWriteFunction;
Olli Etuahofe486322017-03-21 09:30:54 +0000463 }
464 else
465 {
Olli Etuaho0c371002017-12-13 17:00:25 +0400466 indexedWriteFunction = mWrittenVecAndMatrixTypes[type];
Olli Etuahofe486322017-03-21 09:30:54 +0000467 }
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300468
469 TIntermSequence insertionsBefore;
470 TIntermSequence insertionsAfter;
471
472 // Store the index in a temporary signed int variable.
Olli Etuaho195be942017-12-04 23:40:14 +0200473 // s0 = index_expr;
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300474 TIntermTyped *indexInitializer = EnsureSignedInt(node->getRight());
Olli Etuaho195be942017-12-04 23:40:14 +0200475 TIntermDeclaration *indexVariableDeclaration = nullptr;
476 TVariable *indexVariable = DeclareTempVariable(
477 mSymbolTable, indexInitializer, EvqTemporary, &indexVariableDeclaration);
478 insertionsBefore.push_back(indexVariableDeclaration);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300479
Olli Etuaho195be942017-12-04 23:40:14 +0200480 // s1 = dyn_index(v_expr, s0);
481 TIntermAggregate *indexingCall = CreateIndexFunctionCall(
Olli Etuaho0c371002017-12-13 17:00:25 +0400482 node, CreateTempSymbolNode(indexVariable), indexingFunction);
Olli Etuaho195be942017-12-04 23:40:14 +0200483 TIntermDeclaration *fieldVariableDeclaration = nullptr;
484 TVariable *fieldVariable = DeclareTempVariable(
485 mSymbolTable, indexingCall, EvqTemporary, &fieldVariableDeclaration);
486 insertionsBefore.push_back(fieldVariableDeclaration);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300487
Olli Etuaho195be942017-12-04 23:40:14 +0200488 // dyn_index_write(v_expr, s0, s1);
Olli Etuahofe486322017-03-21 09:30:54 +0000489 TIntermAggregate *indexedWriteCall = CreateIndexedWriteFunctionCall(
Olli Etuaho0c371002017-12-13 17:00:25 +0400490 node, indexVariable, fieldVariable, indexedWriteFunction);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300491 insertionsAfter.push_back(indexedWriteCall);
492 insertStatementsInParentBlock(insertionsBefore, insertionsAfter);
Olli Etuaho195be942017-12-04 23:40:14 +0200493
494 // replace the node with s1
495 queueReplacement(CreateTempSymbolNode(fieldVariable), OriginalNode::IS_DROPPED);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300496 mUsedTreeInsertion = true;
497 }
498 else
499 {
500 // The indexed value is not being written, so we can simply convert
501 // v_expr[index_expr]
502 // into
503 // dyn_index(v_expr, index_expr)
504 // If the index_expr is unsigned, we'll convert it to signed.
505 ASSERT(!mRemoveIndexSideEffectsInSubtree);
506 TIntermAggregate *indexingCall = CreateIndexFunctionCall(
Olli Etuaho0c371002017-12-13 17:00:25 +0400507 node, EnsureSignedInt(node->getRight()), indexingFunction);
Olli Etuahoea39a222017-07-06 12:47:59 +0300508 queueReplacement(indexingCall, OriginalNode::IS_DROPPED);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300509 }
510 }
511 }
512 return !mUsedTreeInsertion;
513}
514
515void RemoveDynamicIndexingTraverser::nextIteration()
516{
517 mUsedTreeInsertion = false;
518 mRemoveIndexSideEffectsInSubtree = false;
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300519}
520
521} // namespace
522
Olli Etuaho89a69a02017-10-23 12:20:45 +0300523void RemoveDynamicIndexing(TIntermNode *root,
524 TSymbolTable *symbolTable,
Olli Etuaho89a69a02017-10-23 12:20:45 +0300525 PerformanceDiagnostics *perfDiagnostics)
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300526{
Olli Etuaho68981eb2018-01-23 17:46:12 +0200527 RemoveDynamicIndexingTraverser traverser(symbolTable, perfDiagnostics);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300528 do
529 {
530 traverser.nextIteration();
531 root->traverse(&traverser);
532 traverser.updateTree();
533 } while (traverser.usedTreeInsertion());
Olli Etuaho56612d62017-10-12 15:46:30 +0300534 // TODO(oetuaho@nvidia.com): It might be nicer to add the helper definitions also in the middle
Olli Etuaho1ecd14b2017-01-26 13:54:15 -0800535 // of traversal. Now the tree ends up in an inconsistent state in the middle, since there are
536 // function call nodes with no corresponding definition nodes. This needs special handling in
537 // TIntermLValueTrackingTraverser, and creates intricacies that are not easily apparent from a
538 // superficial reading of the code.
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300539 traverser.insertHelperDefinitions(root);
Olli Etuaho5d91dda2015-06-18 15:47:46 +0300540}
Jamie Madill45bcc782016-11-07 13:58:48 -0500541
542} // namespace sh