Commit 7da98506 by Olli Etuaho Committed by Commit Bot

Cover vector dynamic indexing case in SplitSequenceOperator

Vectors or matrices that are dynamically indexed as a part of an l-value generate new statements in the RemoveDynamicIndexing AST transformation step. SplitSequenceOperator needs to detect this case and split the sequence operator before statements are generated from its operands to ensure the correct order of execution. BUG=angleproject:1341 TEST=angle_end2end_tests Change-Id: I84e41a59c88fb5d0111669cab60312b930531a22 Reviewed-on: https://chromium-review.googlesource.com/361695Reviewed-by: 's avatarCorentin Wallez <cwallez@chromium.org> Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
parent e1d199bb
...@@ -27,7 +27,14 @@ IntermNodePatternMatcher::IntermNodePatternMatcher(const unsigned int mask) : mM ...@@ -27,7 +27,14 @@ IntermNodePatternMatcher::IntermNodePatternMatcher(const unsigned int mask) : mM
{ {
} }
bool IntermNodePatternMatcher::match(TIntermBinary *node, TIntermNode *parentNode) // static
bool IntermNodePatternMatcher::IsDynamicIndexingOfVectorOrMatrix(TIntermBinary *node)
{
return node->getOp() == EOpIndexIndirect && !node->getLeft()->isArray() &&
node->getLeft()->getBasicType() != EbtStruct;
}
bool IntermNodePatternMatcher::matchInternal(TIntermBinary *node, TIntermNode *parentNode)
{ {
if ((mMask & kExpressionReturningArray) != 0) if ((mMask & kExpressionReturningArray) != 0)
{ {
...@@ -49,6 +56,33 @@ bool IntermNodePatternMatcher::match(TIntermBinary *node, TIntermNode *parentNod ...@@ -49,6 +56,33 @@ bool IntermNodePatternMatcher::match(TIntermBinary *node, TIntermNode *parentNod
return false; return false;
} }
bool IntermNodePatternMatcher::match(TIntermBinary *node, TIntermNode *parentNode)
{
// L-value tracking information is needed to check for dynamic indexing in L-value.
// Traversers that don't track l-values can still use this class and match binary nodes with
// this variation of this method if they don't need to check for dynamic indexing in l-values.
ASSERT((mMask & kDynamicIndexingOfVectorOrMatrixInLValue) == 0);
return matchInternal(node, parentNode);
}
bool IntermNodePatternMatcher::match(TIntermBinary *node,
TIntermNode *parentNode,
bool isLValueRequiredHere)
{
if (matchInternal(node, parentNode))
{
return true;
}
if ((mMask & kDynamicIndexingOfVectorOrMatrixInLValue) != 0)
{
if (isLValueRequiredHere && IsDynamicIndexingOfVectorOrMatrix(node))
{
return true;
}
}
return false;
}
bool IntermNodePatternMatcher::match(TIntermAggregate *node, TIntermNode *parentNode) bool IntermNodePatternMatcher::match(TIntermAggregate *node, TIntermNode *parentNode)
{ {
if ((mMask & kExpressionReturningArray) != 0) if ((mMask & kExpressionReturningArray) != 0)
......
...@@ -19,22 +19,35 @@ class TIntermSelection; ...@@ -19,22 +19,35 @@ class TIntermSelection;
class IntermNodePatternMatcher class IntermNodePatternMatcher
{ {
public: public:
static bool IsDynamicIndexingOfVectorOrMatrix(TIntermBinary *node);
enum PatternType enum PatternType
{ {
// Matches expressions that are unfolded to if statements by UnfoldShortCircuitToIf
kUnfoldedShortCircuitExpression = 0x0001, kUnfoldedShortCircuitExpression = 0x0001,
// Matches expressions that return arrays with the exception of simple statements where a // Matches expressions that return arrays with the exception of simple statements where a
// constructor or function call result is assigned. // constructor or function call result is assigned.
kExpressionReturningArray = 0x0002 kExpressionReturningArray = 0x0002,
// Matches dynamic indexing of vectors or matrices in l-values.
kDynamicIndexingOfVectorOrMatrixInLValue = 0x0004
}; };
IntermNodePatternMatcher(const unsigned int mask); IntermNodePatternMatcher(const unsigned int mask);
bool match(TIntermBinary *node, TIntermNode *parentNode); bool match(TIntermBinary *node, TIntermNode *parentNode);
// Use this version for checking binary node matches in case you're using flag
// kDynamicIndexingOfVectorOrMatrixInLValue.
bool match(TIntermBinary *node, TIntermNode *parentNode, bool isLValueRequiredHere);
bool match(TIntermAggregate *node, TIntermNode *parentNode); bool match(TIntermAggregate *node, TIntermNode *parentNode);
bool match(TIntermSelection *node); bool match(TIntermSelection *node);
private: private:
const unsigned int mMask; const unsigned int mMask;
bool matchInternal(TIntermBinary *node, TIntermNode *parentNode);
}; };
#endif #endif
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "compiler/translator/InfoSink.h" #include "compiler/translator/InfoSink.h"
#include "compiler/translator/IntermNode.h" #include "compiler/translator/IntermNode.h"
#include "compiler/translator/IntermNodePatternMatcher.h"
#include "compiler/translator/SymbolTable.h" #include "compiler/translator/SymbolTable.h"
namespace namespace
...@@ -408,10 +409,18 @@ bool RemoveDynamicIndexingTraverser::visitBinary(Visit visit, TIntermBinary *nod ...@@ -408,10 +409,18 @@ bool RemoveDynamicIndexingTraverser::visitBinary(Visit visit, TIntermBinary *nod
NodeUpdateEntry replaceIndex(node, node->getRight(), tempIndex, false); NodeUpdateEntry replaceIndex(node, node->getRight(), tempIndex, false);
mReplacements.push_back(replaceIndex); mReplacements.push_back(replaceIndex);
} }
else if (!node->getLeft()->isArray() && node->getLeft()->getBasicType() != EbtStruct) else if (IntermNodePatternMatcher::IsDynamicIndexingOfVectorOrMatrix(node))
{ {
bool write = isLValueRequiredHere(); bool write = isLValueRequiredHere();
#if defined(ANGLE_ENABLE_ASSERTS)
// Make sure that IntermNodePatternMatcher is consistent with the slightly differently
// implemented checks in this traverser.
IntermNodePatternMatcher matcher(
IntermNodePatternMatcher::kDynamicIndexingOfVectorOrMatrixInLValue);
ASSERT(matcher.match(node, getParentNode(), isLValueRequiredHere()) == write);
#endif
TType type = node->getLeft()->getType(); TType type = node->getLeft()->getType();
mIndexedVecAndMatrixTypes.insert(type); mIndexedVecAndMatrixTypes.insert(type);
......
...@@ -17,10 +17,12 @@ ...@@ -17,10 +17,12 @@
namespace namespace
{ {
class SplitSequenceOperatorTraverser : public TIntermTraverser class SplitSequenceOperatorTraverser : public TLValueTrackingTraverser
{ {
public: public:
SplitSequenceOperatorTraverser(unsigned int patternsToSplitMask); SplitSequenceOperatorTraverser(unsigned int patternsToSplitMask,
const TSymbolTable &symbolTable,
int shaderVersion);
bool visitBinary(Visit visit, TIntermBinary *node) override; bool visitBinary(Visit visit, TIntermBinary *node) override;
bool visitAggregate(Visit visit, TIntermAggregate *node) override; bool visitAggregate(Visit visit, TIntermAggregate *node) override;
...@@ -38,8 +40,10 @@ class SplitSequenceOperatorTraverser : public TIntermTraverser ...@@ -38,8 +40,10 @@ class SplitSequenceOperatorTraverser : public TIntermTraverser
IntermNodePatternMatcher mPatternToSplitMatcher; IntermNodePatternMatcher mPatternToSplitMatcher;
}; };
SplitSequenceOperatorTraverser::SplitSequenceOperatorTraverser(unsigned int patternsToSplitMask) SplitSequenceOperatorTraverser::SplitSequenceOperatorTraverser(unsigned int patternsToSplitMask,
: TIntermTraverser(true, false, true), const TSymbolTable &symbolTable,
int shaderVersion)
: TLValueTrackingTraverser(true, false, true, symbolTable, shaderVersion),
mFoundExpressionToSplit(false), mFoundExpressionToSplit(false),
mInsideSequenceOperator(0), mInsideSequenceOperator(0),
mPatternToSplitMatcher(patternsToSplitMask) mPatternToSplitMatcher(patternsToSplitMask)
...@@ -61,7 +65,8 @@ bool SplitSequenceOperatorTraverser::visitBinary(Visit visit, TIntermBinary *nod ...@@ -61,7 +65,8 @@ bool SplitSequenceOperatorTraverser::visitBinary(Visit visit, TIntermBinary *nod
if (mInsideSequenceOperator > 0 && visit == PreVisit) if (mInsideSequenceOperator > 0 && visit == PreVisit)
{ {
// Detect expressions that need to be simplified // Detect expressions that need to be simplified
mFoundExpressionToSplit = mPatternToSplitMatcher.match(node, getParentNode()); mFoundExpressionToSplit =
mPatternToSplitMatcher.match(node, getParentNode(), isLValueRequiredHere());
return !mFoundExpressionToSplit; return !mFoundExpressionToSplit;
} }
...@@ -135,9 +140,13 @@ bool SplitSequenceOperatorTraverser::visitSelection(Visit visit, TIntermSelectio ...@@ -135,9 +140,13 @@ bool SplitSequenceOperatorTraverser::visitSelection(Visit visit, TIntermSelectio
} // namespace } // namespace
void SplitSequenceOperator(TIntermNode *root, int patternsToSplitMask, unsigned int *temporaryIndex) void SplitSequenceOperator(TIntermNode *root,
int patternsToSplitMask,
unsigned int *temporaryIndex,
const TSymbolTable &symbolTable,
int shaderVersion)
{ {
SplitSequenceOperatorTraverser traverser(patternsToSplitMask); SplitSequenceOperatorTraverser traverser(patternsToSplitMask, symbolTable, shaderVersion);
ASSERT(temporaryIndex != nullptr); ASSERT(temporaryIndex != nullptr);
traverser.useTemporaryIndex(temporaryIndex); traverser.useTemporaryIndex(temporaryIndex);
// Separate one expression at a time, and reset the traverser between iterations. // Separate one expression at a time, and reset the traverser between iterations.
......
...@@ -13,9 +13,12 @@ ...@@ -13,9 +13,12 @@
#define COMPILER_TRANSLATOR_SPLITSEQUENCEOPERATOR_H_ #define COMPILER_TRANSLATOR_SPLITSEQUENCEOPERATOR_H_
class TIntermNode; class TIntermNode;
class TSymbolTable;
void SplitSequenceOperator(TIntermNode *root, void SplitSequenceOperator(TIntermNode *root,
int patternsToSplitMask, int patternsToSplitMask,
unsigned int *temporaryIndex); unsigned int *temporaryIndex,
const TSymbolTable &symbolTable,
int shaderVersion);
#endif // COMPILER_TRANSLATOR_SPLITSEQUENCEOPERATOR_H_ #endif // COMPILER_TRANSLATOR_SPLITSEQUENCEOPERATOR_H_
...@@ -33,9 +33,11 @@ void TranslatorHLSL::translate(TIntermNode *root, int compileOptions) ...@@ -33,9 +33,11 @@ void TranslatorHLSL::translate(TIntermNode *root, int compileOptions)
// TODO (oetuaho): Sequence operators should also be split in case there is dynamic indexing of // TODO (oetuaho): Sequence operators should also be split in case there is dynamic indexing of
// a vector or matrix as an l-value inside (RemoveDynamicIndexing transformation step generates // a vector or matrix as an l-value inside (RemoveDynamicIndexing transformation step generates
// statements in this case). // statements in this case).
SplitSequenceOperator(root, IntermNodePatternMatcher::kExpressionReturningArray | SplitSequenceOperator(root,
IntermNodePatternMatcher::kUnfoldedShortCircuitExpression, IntermNodePatternMatcher::kExpressionReturningArray |
getTemporaryIndex()); IntermNodePatternMatcher::kUnfoldedShortCircuitExpression |
IntermNodePatternMatcher::kDynamicIndexingOfVectorOrMatrixInLValue,
getTemporaryIndex(), getSymbolTable(), getShaderVersion());
// Note that SeparateDeclarations needs to be run before UnfoldShortCircuitToIf. // Note that SeparateDeclarations needs to be run before UnfoldShortCircuitToIf.
UnfoldShortCircuitToIf(root, getTemporaryIndex()); UnfoldShortCircuitToIf(root, getTemporaryIndex());
......
...@@ -1953,6 +1953,35 @@ TEST_P(GLSLTest_ES3, SequenceOperatorEvaluationOrderShortCircuit) ...@@ -1953,6 +1953,35 @@ TEST_P(GLSLTest_ES3, SequenceOperatorEvaluationOrderShortCircuit)
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
} }
// Sequence operator evaluates operands from left to right (ESSL 3.00 section 5.9).
// Indexing the vector needs to be evaluated after func() for the right result.
TEST_P(GLSLTest_ES3, SequenceOperatorEvaluationOrderDynamicVectorIndexingInLValue)
{
const std::string &fragmentShaderSource =
"#version 300 es\n"
"precision mediump float;\n"
"out vec4 my_FragColor;\n"
"uniform int u_zero;\n"
"int sideEffectCount = 0;\n"
"float func() {\n"
" ++sideEffectCount;\n"
" return -1.0;\n"
"}\n"
"void main() {\n"
" vec4 v = vec4(0.0, 2.0, 4.0, 6.0); \n"
" float f = (func(), (++v[u_zero + sideEffectCount]));\n"
" bool green = abs(f - 3.0) < 0.01 && abs(v[1] - 3.0) < 0.01 && sideEffectCount == 1;\n"
" my_FragColor = vec4(0.0, (green ? 1.0 : 0.0), 0.0, 1.0);\n"
"}\n";
GLuint program = CompileProgram(mSimpleVSSource, fragmentShaderSource);
ASSERT_NE(0u, program);
drawQuad(program, "inputAttribute", 0.5f);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
}
// Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against. // Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against.
ANGLE_INSTANTIATE_TEST(GLSLTest, ANGLE_INSTANTIATE_TEST(GLSLTest,
ES2_D3D9(), ES2_D3D9(),
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment