blob: 4c63b59f7b5d11e258c51e915d64cba47ca98693 [file] [log] [blame]
//
// 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/IntermNode.h"
#include "compiler/translator/IntermNodePatternMatcher.h"
namespace sh
{
namespace
{
class SplitSequenceOperatorTraverser : public TLValueTrackingTraverser
{
public:
SplitSequenceOperatorTraverser(unsigned int patternsToSplitMask,
const TSymbolTable &symbolTable,
int shaderVersion);
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,
const TSymbolTable &symbolTable,
int shaderVersion)
: TLValueTrackingTraverser(true, false, true, symbolTable, shaderVersion),
mFoundExpressionToSplit(false),
mInsideSequenceOperator(0),
mPatternToSplitMatcher(patternsToSplitMask)
{
}
void SplitSequenceOperatorTraverser::nextIteration()
{
mFoundExpressionToSplit = false;
mInsideSequenceOperator = 0;
nextTemporaryIndex();
}
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::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, 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,
unsigned int *temporaryIndex,
const TSymbolTable &symbolTable,
int shaderVersion)
{
SplitSequenceOperatorTraverser traverser(patternsToSplitMask, symbolTable, shaderVersion);
ASSERT(temporaryIndex != nullptr);
traverser.useTemporaryIndex(temporaryIndex);
// 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