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