// // Copyright (c) 2016 The ANGLE Project Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // // SplitSequenceOperator is an AST traverser that detects sequence operator expressions that // go through further AST transformations that generate statements, and splits them so that // possible side effects of earlier parts of the sequence operator expression are guaranteed to be // evaluated before the latter parts of the sequence operator expression are evaluated. // #include "compiler/translator/SplitSequenceOperator.h" #include "compiler/translator/IntermNodePatternMatcher.h" #include "compiler/translator/IntermTraverse.h" namespace sh { namespace { class SplitSequenceOperatorTraverser : public TLValueTrackingTraverser { public: SplitSequenceOperatorTraverser(unsigned int patternsToSplitMask, TSymbolTable *symbolTable, int shaderVersion); bool visitUnary(Visit visit, TIntermUnary *node) override; bool visitBinary(Visit visit, TIntermBinary *node) override; bool visitAggregate(Visit visit, TIntermAggregate *node) override; bool visitTernary(Visit visit, TIntermTernary *node) override; void nextIteration(); bool foundExpressionToSplit() const { return mFoundExpressionToSplit; } protected: // Marked to true once an operation that needs to be hoisted out of the expression has been // found. After that, no more AST updates are performed on that traversal. bool mFoundExpressionToSplit; int mInsideSequenceOperator; IntermNodePatternMatcher mPatternToSplitMatcher; }; SplitSequenceOperatorTraverser::SplitSequenceOperatorTraverser(unsigned int patternsToSplitMask, TSymbolTable *symbolTable, int shaderVersion) : TLValueTrackingTraverser(true, false, true, symbolTable, shaderVersion), mFoundExpressionToSplit(false), mInsideSequenceOperator(0), mPatternToSplitMatcher(patternsToSplitMask) { } void SplitSequenceOperatorTraverser::nextIteration() { mFoundExpressionToSplit = false; mInsideSequenceOperator = 0; nextTemporaryId(); } bool SplitSequenceOperatorTraverser::visitAggregate(Visit visit, TIntermAggregate *node) { if (mFoundExpressionToSplit) return false; if (mInsideSequenceOperator > 0 && visit == PreVisit) { // Detect expressions that need to be simplified mFoundExpressionToSplit = mPatternToSplitMatcher.match(node, getParentNode()); return !mFoundExpressionToSplit; } return true; } bool SplitSequenceOperatorTraverser::visitUnary(Visit visit, TIntermUnary *node) { if (mFoundExpressionToSplit) return false; if (mInsideSequenceOperator > 0 && visit == PreVisit) { // Detect expressions that need to be simplified mFoundExpressionToSplit = mPatternToSplitMatcher.match(node); return !mFoundExpressionToSplit; } return true; } bool SplitSequenceOperatorTraverser::visitBinary(Visit visit, TIntermBinary *node) { if (node->getOp() == EOpComma) { if (visit == PreVisit) { if (mFoundExpressionToSplit) { return false; } mInsideSequenceOperator++; } else if (visit == PostVisit) { // Split sequence operators starting from the outermost one to preserve correct // execution order. if (mFoundExpressionToSplit && mInsideSequenceOperator == 1) { // Move the left side operand into a separate statement in the parent block. TIntermSequence insertions; insertions.push_back(node->getLeft()); insertStatementsInParentBlock(insertions); // Replace the comma node with its right side operand. queueReplacement(node->getRight(), OriginalNode::IS_DROPPED); } mInsideSequenceOperator--; } return true; } if (mFoundExpressionToSplit) return false; if (mInsideSequenceOperator > 0 && visit == PreVisit) { // Detect expressions that need to be simplified mFoundExpressionToSplit = mPatternToSplitMatcher.match(node, getParentNode(), isLValueRequiredHere()); return !mFoundExpressionToSplit; } return true; } bool SplitSequenceOperatorTraverser::visitTernary(Visit visit, TIntermTernary *node) { if (mFoundExpressionToSplit) return false; if (mInsideSequenceOperator > 0 && visit == PreVisit) { // Detect expressions that need to be simplified mFoundExpressionToSplit = mPatternToSplitMatcher.match(node); return !mFoundExpressionToSplit; } return true; } } // namespace void SplitSequenceOperator(TIntermNode *root, int patternsToSplitMask, TSymbolTable *symbolTable, int shaderVersion) { SplitSequenceOperatorTraverser traverser(patternsToSplitMask, symbolTable, shaderVersion); // Separate one expression at a time, and reset the traverser between iterations. do { traverser.nextIteration(); root->traverse(&traverser); if (traverser.foundExpressionToSplit()) traverser.updateTree(); } while (traverser.foundExpressionToSplit()); } } // namespace sh