• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright 2002 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 non-SSBO vectors and
7 // matrices, replacing them with calls to functions that choose which component to return or write.
8 // We don't need to consider dynamic indexing in SSBO since it can be directly as part of the offset
9 // of RWByteAddressBuffer.
10 //
11 
12 #include "compiler/translator/tree_ops/RemoveDynamicIndexing.h"
13 
14 #include "compiler/translator/Compiler.h"
15 #include "compiler/translator/Diagnostics.h"
16 #include "compiler/translator/InfoSink.h"
17 #include "compiler/translator/StaticType.h"
18 #include "compiler/translator/SymbolTable.h"
19 #include "compiler/translator/tree_util/IntermNodePatternMatcher.h"
20 #include "compiler/translator/tree_util/IntermNode_util.h"
21 #include "compiler/translator/tree_util/IntermTraverse.h"
22 
23 namespace sh
24 {
25 
26 namespace
27 {
28 
29 using DynamicIndexingNodeMatcher = std::function<bool(TIntermBinary *)>;
30 
31 const TType *kIndexType = StaticType::Get<EbtInt, EbpHigh, EvqParamIn, 1, 1>();
32 
33 constexpr const ImmutableString kBaseName("base");
34 constexpr const ImmutableString kIndexName("index");
35 constexpr const ImmutableString kValueName("value");
36 
GetIndexFunctionName(const TType & type,bool write)37 std::string GetIndexFunctionName(const TType &type, bool write)
38 {
39     TInfoSinkBase nameSink;
40     nameSink << "dyn_index_";
41     if (write)
42     {
43         nameSink << "write_";
44     }
45     if (type.isMatrix())
46     {
47         nameSink << "mat" << type.getCols() << "x" << type.getRows();
48     }
49     else
50     {
51         switch (type.getBasicType())
52         {
53             case EbtInt:
54                 nameSink << "ivec";
55                 break;
56             case EbtBool:
57                 nameSink << "bvec";
58                 break;
59             case EbtUInt:
60                 nameSink << "uvec";
61                 break;
62             case EbtFloat:
63                 nameSink << "vec";
64                 break;
65             default:
66                 UNREACHABLE();
67         }
68         nameSink << type.getNominalSize();
69     }
70     return nameSink.str();
71 }
72 
CreateIntConstantNode(int i)73 TIntermConstantUnion *CreateIntConstantNode(int i)
74 {
75     TConstantUnion *constant = new TConstantUnion();
76     constant->setIConst(i);
77     return new TIntermConstantUnion(constant, TType(EbtInt, EbpHigh));
78 }
79 
EnsureSignedInt(TIntermTyped * node)80 TIntermTyped *EnsureSignedInt(TIntermTyped *node)
81 {
82     if (node->getBasicType() == EbtInt)
83         return node;
84 
85     TIntermSequence arguments;
86     arguments.push_back(node);
87     return TIntermAggregate::CreateConstructor(TType(EbtInt), &arguments);
88 }
89 
GetFieldType(const TType & indexedType)90 TType *GetFieldType(const TType &indexedType)
91 {
92     TType *fieldType = new TType(indexedType);
93     if (indexedType.isMatrix())
94     {
95         fieldType->toMatrixColumnType();
96     }
97     else
98     {
99         ASSERT(indexedType.isVector());
100         fieldType->toComponentType();
101     }
102     // Default precision to highp if not specified.  For example in |vec3(0)[i], i < 0|, there is no
103     // precision assigned to vec3(0).
104     if (fieldType->getPrecision() == EbpUndefined)
105     {
106         fieldType->setPrecision(EbpHigh);
107     }
108     return fieldType;
109 }
110 
GetBaseType(const TType & type,bool write)111 const TType *GetBaseType(const TType &type, bool write)
112 {
113     TType *baseType = new TType(type);
114     // Conservatively use highp here, even if the indexed type is not highp. That way the code can't
115     // end up using mediump version of an indexing function for a highp value, if both mediump and
116     // highp values are being indexed in the shader. For HLSL precision doesn't matter, but in
117     // principle this code could be used with multiple backends.
118     baseType->setPrecision(EbpHigh);
119     baseType->setQualifier(EvqParamInOut);
120     if (!write)
121         baseType->setQualifier(EvqParamIn);
122     return baseType;
123 }
124 
125 // Generate a read or write function for one field in a vector/matrix.
126 // Out-of-range indices are clamped. This is consistent with how ANGLE handles out-of-range
127 // indices in other places.
128 // Note that indices can be either int or uint. We create only int versions of the functions,
129 // and convert uint indices to int at the call site.
130 // read function example:
131 // float dyn_index_vec2(in vec2 base, in int index)
132 // {
133 //    switch(index)
134 //    {
135 //      case (0):
136 //        return base[0];
137 //      case (1):
138 //        return base[1];
139 //      default:
140 //        break;
141 //    }
142 //    if (index < 0)
143 //      return base[0];
144 //    return base[1];
145 // }
146 // write function example:
147 // void dyn_index_write_vec2(inout vec2 base, in int index, in float value)
148 // {
149 //    switch(index)
150 //    {
151 //      case (0):
152 //        base[0] = value;
153 //        return;
154 //      case (1):
155 //        base[1] = value;
156 //        return;
157 //      default:
158 //        break;
159 //    }
160 //    if (index < 0)
161 //    {
162 //      base[0] = value;
163 //      return;
164 //    }
165 //    base[1] = value;
166 // }
167 // Note that else is not used in above functions to avoid the RewriteElseBlocks transformation.
GetIndexFunctionDefinition(const TType & type,bool write,const TFunction & func,TSymbolTable * symbolTable)168 TIntermFunctionDefinition *GetIndexFunctionDefinition(const TType &type,
169                                                       bool write,
170                                                       const TFunction &func,
171                                                       TSymbolTable *symbolTable)
172 {
173     ASSERT(!type.isArray());
174 
175     int numCases = 0;
176     if (type.isMatrix())
177     {
178         numCases = type.getCols();
179     }
180     else
181     {
182         numCases = type.getNominalSize();
183     }
184 
185     std::string functionName                = GetIndexFunctionName(type, write);
186     TIntermFunctionPrototype *prototypeNode = CreateInternalFunctionPrototypeNode(func);
187 
188     TIntermSymbol *baseParam  = new TIntermSymbol(func.getParam(0));
189     TIntermSymbol *indexParam = new TIntermSymbol(func.getParam(1));
190     TIntermSymbol *valueParam = nullptr;
191     if (write)
192     {
193         valueParam = new TIntermSymbol(func.getParam(2));
194     }
195 
196     TIntermBlock *statementList = new TIntermBlock();
197     for (int i = 0; i < numCases; ++i)
198     {
199         TIntermCase *caseNode = new TIntermCase(CreateIntConstantNode(i));
200         statementList->getSequence()->push_back(caseNode);
201 
202         TIntermBinary *indexNode =
203             new TIntermBinary(EOpIndexDirect, baseParam->deepCopy(), CreateIndexNode(i));
204         if (write)
205         {
206             TIntermBinary *assignNode =
207                 new TIntermBinary(EOpAssign, indexNode, valueParam->deepCopy());
208             statementList->getSequence()->push_back(assignNode);
209             TIntermBranch *returnNode = new TIntermBranch(EOpReturn, nullptr);
210             statementList->getSequence()->push_back(returnNode);
211         }
212         else
213         {
214             TIntermBranch *returnNode = new TIntermBranch(EOpReturn, indexNode);
215             statementList->getSequence()->push_back(returnNode);
216         }
217     }
218 
219     // Default case
220     TIntermCase *defaultNode = new TIntermCase(nullptr);
221     statementList->getSequence()->push_back(defaultNode);
222     TIntermBranch *breakNode = new TIntermBranch(EOpBreak, nullptr);
223     statementList->getSequence()->push_back(breakNode);
224 
225     TIntermSwitch *switchNode = new TIntermSwitch(indexParam->deepCopy(), statementList);
226 
227     TIntermBlock *bodyNode = new TIntermBlock();
228     bodyNode->getSequence()->push_back(switchNode);
229 
230     TIntermBinary *cond =
231         new TIntermBinary(EOpLessThan, indexParam->deepCopy(), CreateIntConstantNode(0));
232 
233     // Two blocks: one accesses (either reads or writes) the first element and returns,
234     // the other accesses the last element.
235     TIntermBlock *useFirstBlock = new TIntermBlock();
236     TIntermBlock *useLastBlock  = new TIntermBlock();
237     TIntermBinary *indexFirstNode =
238         new TIntermBinary(EOpIndexDirect, baseParam->deepCopy(), CreateIndexNode(0));
239     TIntermBinary *indexLastNode =
240         new TIntermBinary(EOpIndexDirect, baseParam->deepCopy(), CreateIndexNode(numCases - 1));
241     if (write)
242     {
243         TIntermBinary *assignFirstNode =
244             new TIntermBinary(EOpAssign, indexFirstNode, valueParam->deepCopy());
245         useFirstBlock->getSequence()->push_back(assignFirstNode);
246         TIntermBranch *returnNode = new TIntermBranch(EOpReturn, nullptr);
247         useFirstBlock->getSequence()->push_back(returnNode);
248 
249         TIntermBinary *assignLastNode =
250             new TIntermBinary(EOpAssign, indexLastNode, valueParam->deepCopy());
251         useLastBlock->getSequence()->push_back(assignLastNode);
252     }
253     else
254     {
255         TIntermBranch *returnFirstNode = new TIntermBranch(EOpReturn, indexFirstNode);
256         useFirstBlock->getSequence()->push_back(returnFirstNode);
257 
258         TIntermBranch *returnLastNode = new TIntermBranch(EOpReturn, indexLastNode);
259         useLastBlock->getSequence()->push_back(returnLastNode);
260     }
261     TIntermIfElse *ifNode = new TIntermIfElse(cond, useFirstBlock, nullptr);
262     bodyNode->getSequence()->push_back(ifNode);
263     bodyNode->getSequence()->push_back(useLastBlock);
264 
265     TIntermFunctionDefinition *indexingFunction =
266         new TIntermFunctionDefinition(prototypeNode, bodyNode);
267     return indexingFunction;
268 }
269 
270 class RemoveDynamicIndexingTraverser : public TLValueTrackingTraverser
271 {
272   public:
273     RemoveDynamicIndexingTraverser(DynamicIndexingNodeMatcher &&matcher,
274                                    TSymbolTable *symbolTable,
275                                    PerformanceDiagnostics *perfDiagnostics);
276 
277     bool visitBinary(Visit visit, TIntermBinary *node) override;
278 
279     void insertHelperDefinitions(TIntermNode *root);
280 
281     void nextIteration();
282 
usedTreeInsertion() const283     bool usedTreeInsertion() const { return mUsedTreeInsertion; }
284 
285   protected:
286     // Maps of types that are indexed to the indexing function ids used for them. Note that these
287     // can not store multiple variants of the same type with different precisions - only one
288     // precision gets stored.
289     std::map<TType, TFunction *> mIndexedVecAndMatrixTypes;
290     std::map<TType, TFunction *> mWrittenVecAndMatrixTypes;
291 
292     bool mUsedTreeInsertion;
293 
294     // When true, the traverser will remove side effects from any indexing expression.
295     // This is done so that in code like
296     //   V[j++][i]++.
297     // where V is an array of vectors, j++ will only be evaluated once.
298     bool mRemoveIndexSideEffectsInSubtree;
299 
300     DynamicIndexingNodeMatcher mMatcher;
301     PerformanceDiagnostics *mPerfDiagnostics;
302 };
303 
RemoveDynamicIndexingTraverser(DynamicIndexingNodeMatcher && matcher,TSymbolTable * symbolTable,PerformanceDiagnostics * perfDiagnostics)304 RemoveDynamicIndexingTraverser::RemoveDynamicIndexingTraverser(
305     DynamicIndexingNodeMatcher &&matcher,
306     TSymbolTable *symbolTable,
307     PerformanceDiagnostics *perfDiagnostics)
308     : TLValueTrackingTraverser(true, false, false, symbolTable),
309       mUsedTreeInsertion(false),
310       mRemoveIndexSideEffectsInSubtree(false),
311       mMatcher(matcher),
312       mPerfDiagnostics(perfDiagnostics)
313 {}
314 
insertHelperDefinitions(TIntermNode * root)315 void RemoveDynamicIndexingTraverser::insertHelperDefinitions(TIntermNode *root)
316 {
317     TIntermBlock *rootBlock = root->getAsBlock();
318     ASSERT(rootBlock != nullptr);
319     TIntermSequence insertions;
320     for (auto &type : mIndexedVecAndMatrixTypes)
321     {
322         insertions.push_back(
323             GetIndexFunctionDefinition(type.first, false, *type.second, mSymbolTable));
324     }
325     for (auto &type : mWrittenVecAndMatrixTypes)
326     {
327         insertions.push_back(
328             GetIndexFunctionDefinition(type.first, true, *type.second, mSymbolTable));
329     }
330     rootBlock->insertChildNodes(0, insertions);
331 }
332 
333 // Create a call to dyn_index_*() based on an indirect indexing op node
CreateIndexFunctionCall(TIntermBinary * node,TIntermTyped * index,TFunction * indexingFunction)334 TIntermAggregate *CreateIndexFunctionCall(TIntermBinary *node,
335                                           TIntermTyped *index,
336                                           TFunction *indexingFunction)
337 {
338     ASSERT(node->getOp() == EOpIndexIndirect);
339     TIntermSequence arguments;
340     arguments.push_back(node->getLeft());
341     arguments.push_back(index);
342 
343     TIntermAggregate *indexingCall =
344         TIntermAggregate::CreateFunctionCall(*indexingFunction, &arguments);
345     indexingCall->setLine(node->getLine());
346     return indexingCall;
347 }
348 
CreateIndexedWriteFunctionCall(TIntermBinary * node,TVariable * index,TVariable * writtenValue,TFunction * indexedWriteFunction)349 TIntermAggregate *CreateIndexedWriteFunctionCall(TIntermBinary *node,
350                                                  TVariable *index,
351                                                  TVariable *writtenValue,
352                                                  TFunction *indexedWriteFunction)
353 {
354     ASSERT(node->getOp() == EOpIndexIndirect);
355     TIntermSequence arguments;
356     // Deep copy the child nodes so that two pointers to the same node don't end up in the tree.
357     arguments.push_back(node->getLeft()->deepCopy());
358     arguments.push_back(CreateTempSymbolNode(index));
359     arguments.push_back(CreateTempSymbolNode(writtenValue));
360 
361     TIntermAggregate *indexedWriteCall =
362         TIntermAggregate::CreateFunctionCall(*indexedWriteFunction, &arguments);
363     indexedWriteCall->setLine(node->getLine());
364     return indexedWriteCall;
365 }
366 
visitBinary(Visit visit,TIntermBinary * node)367 bool RemoveDynamicIndexingTraverser::visitBinary(Visit visit, TIntermBinary *node)
368 {
369     if (mUsedTreeInsertion)
370         return false;
371 
372     if (node->getOp() == EOpIndexIndirect)
373     {
374         if (mRemoveIndexSideEffectsInSubtree)
375         {
376             ASSERT(node->getRight()->hasSideEffects());
377             // In case we're just removing index side effects, convert
378             //   v_expr[index_expr]
379             // to this:
380             //   int s0 = index_expr; v_expr[s0];
381             // Now v_expr[s0] can be safely executed several times without unintended side effects.
382             TIntermDeclaration *indexVariableDeclaration = nullptr;
383             TVariable *indexVariable = DeclareTempVariable(mSymbolTable, node->getRight(),
384                                                            EvqTemporary, &indexVariableDeclaration);
385             insertStatementInParentBlock(indexVariableDeclaration);
386             mUsedTreeInsertion = true;
387 
388             // Replace the index with the temp variable
389             TIntermSymbol *tempIndex = CreateTempSymbolNode(indexVariable);
390             queueReplacementWithParent(node, node->getRight(), tempIndex, OriginalNode::IS_DROPPED);
391         }
392         else if (mMatcher(node))
393         {
394             if (mPerfDiagnostics)
395             {
396                 mPerfDiagnostics->warning(node->getLine(),
397                                           "Performance: dynamic indexing of vectors and "
398                                           "matrices is emulated and can be slow.",
399                                           "[]");
400             }
401             bool write = isLValueRequiredHere();
402 
403 #if defined(ANGLE_ENABLE_ASSERTS)
404             // Make sure that IntermNodePatternMatcher is consistent with the slightly differently
405             // implemented checks in this traverser.
406             IntermNodePatternMatcher matcher(
407                 IntermNodePatternMatcher::kDynamicIndexingOfVectorOrMatrixInLValue);
408             ASSERT(matcher.match(node, getParentNode(), isLValueRequiredHere()) == write);
409 #endif
410 
411             const TType &type = node->getLeft()->getType();
412             ImmutableString indexingFunctionName(GetIndexFunctionName(type, false));
413             TFunction *indexingFunction = nullptr;
414             if (mIndexedVecAndMatrixTypes.find(type) == mIndexedVecAndMatrixTypes.end())
415             {
416                 indexingFunction =
417                     new TFunction(mSymbolTable, indexingFunctionName, SymbolType::AngleInternal,
418                                   GetFieldType(type), true);
419                 indexingFunction->addParameter(new TVariable(
420                     mSymbolTable, kBaseName, GetBaseType(type, false), SymbolType::AngleInternal));
421                 indexingFunction->addParameter(
422                     new TVariable(mSymbolTable, kIndexName, kIndexType, SymbolType::AngleInternal));
423                 mIndexedVecAndMatrixTypes[type] = indexingFunction;
424             }
425             else
426             {
427                 indexingFunction = mIndexedVecAndMatrixTypes[type];
428             }
429 
430             if (write)
431             {
432                 // Convert:
433                 //   v_expr[index_expr]++;
434                 // to this:
435                 //   int s0 = index_expr; float s1 = dyn_index(v_expr, s0); s1++;
436                 //   dyn_index_write(v_expr, s0, s1);
437                 // This works even if index_expr has some side effects.
438                 if (node->getLeft()->hasSideEffects())
439                 {
440                     // If v_expr has side effects, those need to be removed before proceeding.
441                     // Otherwise the side effects of v_expr would be evaluated twice.
442                     // The only case where an l-value can have side effects is when it is
443                     // indexing. For example, it can be V[j++] where V is an array of vectors.
444                     mRemoveIndexSideEffectsInSubtree = true;
445                     return true;
446                 }
447 
448                 TIntermBinary *leftBinary = node->getLeft()->getAsBinaryNode();
449                 if (leftBinary != nullptr && mMatcher(leftBinary))
450                 {
451                     // This is a case like:
452                     // mat2 m;
453                     // m[a][b]++;
454                     // Process the child node m[a] first.
455                     return true;
456                 }
457 
458                 // TODO(oetuaho@nvidia.com): This is not optimal if the expression using the value
459                 // only writes it and doesn't need the previous value. http://anglebug.com/1116
460 
461                 TFunction *indexedWriteFunction = nullptr;
462                 if (mWrittenVecAndMatrixTypes.find(type) == mWrittenVecAndMatrixTypes.end())
463                 {
464                     ImmutableString functionName(
465                         GetIndexFunctionName(node->getLeft()->getType(), true));
466                     indexedWriteFunction =
467                         new TFunction(mSymbolTable, functionName, SymbolType::AngleInternal,
468                                       StaticType::GetBasic<EbtVoid, EbpUndefined>(), false);
469                     indexedWriteFunction->addParameter(new TVariable(mSymbolTable, kBaseName,
470                                                                      GetBaseType(type, true),
471                                                                      SymbolType::AngleInternal));
472                     indexedWriteFunction->addParameter(new TVariable(
473                         mSymbolTable, kIndexName, kIndexType, SymbolType::AngleInternal));
474                     TType *valueType = GetFieldType(type);
475                     valueType->setQualifier(EvqParamIn);
476                     indexedWriteFunction->addParameter(new TVariable(
477                         mSymbolTable, kValueName, static_cast<const TType *>(valueType),
478                         SymbolType::AngleInternal));
479                     mWrittenVecAndMatrixTypes[type] = indexedWriteFunction;
480                 }
481                 else
482                 {
483                     indexedWriteFunction = mWrittenVecAndMatrixTypes[type];
484                 }
485 
486                 TIntermSequence insertionsBefore;
487                 TIntermSequence insertionsAfter;
488 
489                 // Store the index in a temporary signed int variable.
490                 // s0 = index_expr;
491                 TIntermTyped *indexInitializer               = EnsureSignedInt(node->getRight());
492                 TIntermDeclaration *indexVariableDeclaration = nullptr;
493                 TVariable *indexVariable                     = DeclareTempVariable(
494                     mSymbolTable, indexInitializer, EvqTemporary, &indexVariableDeclaration);
495                 insertionsBefore.push_back(indexVariableDeclaration);
496 
497                 // s1 = dyn_index(v_expr, s0);
498                 TIntermAggregate *indexingCall = CreateIndexFunctionCall(
499                     node, CreateTempSymbolNode(indexVariable), indexingFunction);
500                 TIntermDeclaration *fieldVariableDeclaration = nullptr;
501                 TVariable *fieldVariable                     = DeclareTempVariable(
502                     mSymbolTable, indexingCall, EvqTemporary, &fieldVariableDeclaration);
503                 insertionsBefore.push_back(fieldVariableDeclaration);
504 
505                 // dyn_index_write(v_expr, s0, s1);
506                 TIntermAggregate *indexedWriteCall = CreateIndexedWriteFunctionCall(
507                     node, indexVariable, fieldVariable, indexedWriteFunction);
508                 insertionsAfter.push_back(indexedWriteCall);
509                 insertStatementsInParentBlock(insertionsBefore, insertionsAfter);
510 
511                 // replace the node with s1
512                 queueReplacement(CreateTempSymbolNode(fieldVariable), OriginalNode::IS_DROPPED);
513                 mUsedTreeInsertion = true;
514             }
515             else
516             {
517                 // The indexed value is not being written, so we can simply convert
518                 //   v_expr[index_expr]
519                 // into
520                 //   dyn_index(v_expr, index_expr)
521                 // If the index_expr is unsigned, we'll convert it to signed.
522                 ASSERT(!mRemoveIndexSideEffectsInSubtree);
523                 TIntermAggregate *indexingCall = CreateIndexFunctionCall(
524                     node, EnsureSignedInt(node->getRight()), indexingFunction);
525                 queueReplacement(indexingCall, OriginalNode::IS_DROPPED);
526             }
527         }
528     }
529     return !mUsedTreeInsertion;
530 }
531 
nextIteration()532 void RemoveDynamicIndexingTraverser::nextIteration()
533 {
534     mUsedTreeInsertion               = false;
535     mRemoveIndexSideEffectsInSubtree = false;
536 }
537 
RemoveDynamicIndexingIf(DynamicIndexingNodeMatcher && matcher,TCompiler * compiler,TIntermNode * root,TSymbolTable * symbolTable,PerformanceDiagnostics * perfDiagnostics)538 bool RemoveDynamicIndexingIf(DynamicIndexingNodeMatcher &&matcher,
539                              TCompiler *compiler,
540                              TIntermNode *root,
541                              TSymbolTable *symbolTable,
542                              PerformanceDiagnostics *perfDiagnostics)
543 {
544     // This transformation adds function declarations after the fact and so some validation is
545     // momentarily disabled.
546     bool enableValidateFunctionCall = compiler->disableValidateFunctionCall();
547 
548     RemoveDynamicIndexingTraverser traverser(std::move(matcher), symbolTable, perfDiagnostics);
549     do
550     {
551         traverser.nextIteration();
552         root->traverse(&traverser);
553         if (!traverser.updateTree(compiler, root))
554         {
555             return false;
556         }
557     } while (traverser.usedTreeInsertion());
558     // TODO(oetuaho@nvidia.com): It might be nicer to add the helper definitions also in the middle
559     // of traversal. Now the tree ends up in an inconsistent state in the middle, since there are
560     // function call nodes with no corresponding definition nodes. This needs special handling in
561     // TIntermLValueTrackingTraverser, and creates intricacies that are not easily apparent from a
562     // superficial reading of the code.
563     traverser.insertHelperDefinitions(root);
564 
565     compiler->restoreValidateFunctionCall(enableValidateFunctionCall);
566     return compiler->validateAST(root);
567 }
568 
569 }  // namespace
570 
RemoveDynamicIndexingOfNonSSBOVectorOrMatrix(TCompiler * compiler,TIntermNode * root,TSymbolTable * symbolTable,PerformanceDiagnostics * perfDiagnostics)571 ANGLE_NO_DISCARD bool RemoveDynamicIndexingOfNonSSBOVectorOrMatrix(
572     TCompiler *compiler,
573     TIntermNode *root,
574     TSymbolTable *symbolTable,
575     PerformanceDiagnostics *perfDiagnostics)
576 {
577     DynamicIndexingNodeMatcher matcher = [](TIntermBinary *node) {
578         return IntermNodePatternMatcher::IsDynamicIndexingOfNonSSBOVectorOrMatrix(node);
579     };
580     return RemoveDynamicIndexingIf(std::move(matcher), compiler, root, symbolTable,
581                                    perfDiagnostics);
582 }
583 
RemoveDynamicIndexingOfSwizzledVector(TCompiler * compiler,TIntermNode * root,TSymbolTable * symbolTable,PerformanceDiagnostics * perfDiagnostics)584 ANGLE_NO_DISCARD bool RemoveDynamicIndexingOfSwizzledVector(TCompiler *compiler,
585                                                             TIntermNode *root,
586                                                             TSymbolTable *symbolTable,
587                                                             PerformanceDiagnostics *perfDiagnostics)
588 {
589     DynamicIndexingNodeMatcher matcher = [](TIntermBinary *node) {
590         return IntermNodePatternMatcher::IsDynamicIndexingOfSwizzledVector(node);
591     };
592     return RemoveDynamicIndexingIf(std::move(matcher), compiler, root, symbolTable,
593                                    perfDiagnostics);
594 }
595 
596 }  // namespace sh
597