Commit a6f22096 by Olli Etuaho

Make UnfoldShortCircuit to change AST instead of writing output

This is needed to make way for further AST transformations to handle array expressions that need to work correctly together with unfolding short- circuiting operators. This also improves the maintainability of HLSL output by isolating the unfolding into a separate compilation step. The new version of UnfoldShortCircuit traverser will traverse the tree until an expression that needs to be unfolded is encountered. It then unfolds it and gets reset. The traverser will be run repeatedly until no more operations to unfold are found. This helps with keeping the traverser's design relatively simple. All declarations are separated to single declarations before short-circuit unfolding is run. Previously OutputHLSL already output every declaration separately. BUG=angleproject:960 TEST=WebGL conformance tests, angle_unittests, angle_end2end_tests Change-Id: Id769be396adbd4c0223e418980dc464dd855f019 Reviewed-on: https://chromium-review.googlesource.com/270460Tested-by: 's avatarOlli Etuaho <oetuaho@nvidia.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org>
parent 531e3d22
...@@ -167,8 +167,8 @@ ...@@ -167,8 +167,8 @@
'compiler/translator/StructureHLSL.h', 'compiler/translator/StructureHLSL.h',
'compiler/translator/TranslatorHLSL.cpp', 'compiler/translator/TranslatorHLSL.cpp',
'compiler/translator/TranslatorHLSL.h', 'compiler/translator/TranslatorHLSL.h',
'compiler/translator/UnfoldShortCircuit.cpp', 'compiler/translator/UnfoldShortCircuitToIf.cpp',
'compiler/translator/UnfoldShortCircuit.h', 'compiler/translator/UnfoldShortCircuitToIf.h',
'compiler/translator/UniformHLSL.cpp', 'compiler/translator/UniformHLSL.cpp',
'compiler/translator/UniformHLSL.h', 'compiler/translator/UniformHLSL.h',
'compiler/translator/UtilsHLSL.cpp', 'compiler/translator/UtilsHLSL.cpp',
......
...@@ -209,6 +209,21 @@ bool TIntermAggregate::replaceChildNodeWithMultiple(TIntermNode *original, TInte ...@@ -209,6 +209,21 @@ bool TIntermAggregate::replaceChildNodeWithMultiple(TIntermNode *original, TInte
return false; return false;
} }
bool TIntermAggregate::insertChildNodes(TIntermSequence::size_type position, TIntermSequence insertions)
{
TIntermSequence::size_type itPosition = 0;
for (auto it = mSequence.begin(); it < mSequence.end(); ++it)
{
if (itPosition == position)
{
mSequence.insert(it, insertions.begin(), insertions.end());
return true;
}
++itPosition;
}
return false;
}
void TIntermAggregate::setPrecisionFromChildren() void TIntermAggregate::setPrecisionFromChildren()
{ {
if (getBasicType() == EbtBool) if (getBasicType() == EbtBool)
...@@ -1488,6 +1503,14 @@ TString TIntermTraverser::hash(const TString &name, ShHashFunction64 hashFunctio ...@@ -1488,6 +1503,14 @@ TString TIntermTraverser::hash(const TString &name, ShHashFunction64 hashFunctio
void TIntermTraverser::updateTree() void TIntermTraverser::updateTree()
{ {
for (size_t ii = 0; ii < mInsertions.size(); ++ii)
{
const NodeInsertMultipleEntry &insertion = mInsertions[ii];
ASSERT(insertion.parent);
bool inserted = insertion.parent->insertChildNodes(insertion.position, insertion.insertions);
ASSERT(inserted);
UNUSED_ASSERTION_VARIABLE(inserted);
}
for (size_t ii = 0; ii < mReplacements.size(); ++ii) for (size_t ii = 0; ii < mReplacements.size(); ++ii)
{ {
const NodeUpdateEntry &replacement = mReplacements[ii]; const NodeUpdateEntry &replacement = mReplacements[ii];
......
...@@ -434,6 +434,7 @@ class TIntermAggregate : public TIntermOperator ...@@ -434,6 +434,7 @@ class TIntermAggregate : public TIntermOperator
virtual bool replaceChildNode( virtual bool replaceChildNode(
TIntermNode *original, TIntermNode *replacement); TIntermNode *original, TIntermNode *replacement);
bool replaceChildNodeWithMultiple(TIntermNode *original, TIntermSequence replacements); bool replaceChildNodeWithMultiple(TIntermNode *original, TIntermSequence replacements);
bool insertChildNodes(TIntermSequence::size_type position, TIntermSequence insertions);
// Conservatively assume function calls and other aggregate operators have side-effects // Conservatively assume function calls and other aggregate operators have side-effects
virtual bool hasSideEffects() const { return true; } virtual bool hasSideEffects() const { return true; }
...@@ -680,11 +681,27 @@ class TIntermTraverser : angle::NonCopyable ...@@ -680,11 +681,27 @@ class TIntermTraverser : angle::NonCopyable
TIntermSequence replacements; TIntermSequence replacements;
}; };
// To insert multiple nodes on the parent aggregate node
struct NodeInsertMultipleEntry
{
NodeInsertMultipleEntry(TIntermAggregate *_parent, TIntermSequence::size_type _position, TIntermSequence _insertions)
: parent(_parent),
position(_position),
insertions(_insertions)
{
}
TIntermAggregate *parent;
TIntermSequence::size_type position;
TIntermSequence insertions;
};
// During traversing, save all the changes that need to happen into // During traversing, save all the changes that need to happen into
// mReplacements/mMultiReplacements, then do them by calling updateTree(). // mReplacements/mMultiReplacements, then do them by calling updateTree().
// Multi replacements are processed after single replacements. // Multi replacements are processed after single replacements.
std::vector<NodeUpdateEntry> mReplacements; std::vector<NodeUpdateEntry> mReplacements;
std::vector<NodeReplaceWithMultipleEntry> mMultiReplacements; std::vector<NodeReplaceWithMultipleEntry> mMultiReplacements;
std::vector<NodeInsertMultipleEntry> mInsertions;
}; };
// //
......
...@@ -63,7 +63,6 @@ class OutputHLSL : public TIntermTraverser ...@@ -63,7 +63,6 @@ class OutputHLSL : public TIntermTraverser
bool visitLoop(Visit visit, TIntermLoop*); bool visitLoop(Visit visit, TIntermLoop*);
bool visitBranch(Visit visit, TIntermBranch*); bool visitBranch(Visit visit, TIntermBranch*);
void traverseStatements(TIntermNode *node);
bool isSingleStatement(TIntermNode *node); bool isSingleStatement(TIntermNode *node);
bool handleExcessiveLoop(TIntermLoop *node); bool handleExcessiveLoop(TIntermLoop *node);
...@@ -103,7 +102,6 @@ class OutputHLSL : public TIntermTraverser ...@@ -103,7 +102,6 @@ class OutputHLSL : public TIntermTraverser
const ShShaderOutput mOutputType; const ShShaderOutput mOutputType;
int mCompileOptions; int mCompileOptions;
UnfoldShortCircuit *mUnfoldShortCircuit;
bool mInsideFunction; bool mInsideFunction;
// Output streams // Output streams
......
...@@ -3,9 +3,10 @@ ...@@ -3,9 +3,10 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// //
// The SeparateArrayDeclarations function processes declarations that contain array declarators. Each declarator in // The SeparateDeclarations function processes declarations, so that in the end each declaration
// such declarations gets its own declaration. // contains only one declarator.
// This is useful as an intermediate step when initialization needs to be separated from declaration. // This is useful as an intermediate step when initialization needs to be separated from declaration,
// or when things need to be unfolded out of the initializer.
// Example: // Example:
// int a[1] = int[1](1), b[1] = int[1](2); // int a[1] = int[1](1), b[1] = int[1](2);
// gets transformed when run through this class into the AST equivalent of: // gets transformed when run through this class into the AST equivalent of:
...@@ -19,43 +20,33 @@ ...@@ -19,43 +20,33 @@
namespace namespace
{ {
class SeparateDeclarations : private TIntermTraverser class SeparateDeclarationsTraverser : private TIntermTraverser
{ {
public: public:
static void apply(TIntermNode *root); static void apply(TIntermNode *root);
private: private:
SeparateDeclarations(); SeparateDeclarationsTraverser();
bool visitAggregate(Visit, TIntermAggregate *node) override; bool visitAggregate(Visit, TIntermAggregate *node) override;
}; };
void SeparateDeclarations::apply(TIntermNode *root) void SeparateDeclarationsTraverser::apply(TIntermNode *root)
{ {
SeparateDeclarations separateDecl; SeparateDeclarationsTraverser separateDecl;
root->traverse(&separateDecl); root->traverse(&separateDecl);
separateDecl.updateTree(); separateDecl.updateTree();
} }
SeparateDeclarations::SeparateDeclarations() SeparateDeclarationsTraverser::SeparateDeclarationsTraverser()
: TIntermTraverser(true, false, false) : TIntermTraverser(true, false, false)
{ {
} }
bool SeparateDeclarations::visitAggregate(Visit, TIntermAggregate *node) bool SeparateDeclarationsTraverser::visitAggregate(Visit, TIntermAggregate *node)
{ {
if (node->getOp() == EOpDeclaration) if (node->getOp() == EOpDeclaration)
{ {
TIntermSequence *sequence = node->getSequence(); TIntermSequence *sequence = node->getSequence();
bool sequenceContainsArrays = false; if (sequence->size() > 1)
for (size_t ii = 0; ii < sequence->size(); ++ii)
{
TIntermTyped *typed = sequence->at(ii)->getAsTyped();
if (typed != nullptr && typed->isArray())
{
sequenceContainsArrays = true;
break;
}
}
if (sequence->size() > 1 && sequenceContainsArrays)
{ {
TIntermAggregate *parentAgg = getParentNode()->getAsAggregate(); TIntermAggregate *parentAgg = getParentNode()->getAsAggregate();
ASSERT(parentAgg != nullptr); ASSERT(parentAgg != nullptr);
...@@ -80,7 +71,7 @@ bool SeparateDeclarations::visitAggregate(Visit, TIntermAggregate *node) ...@@ -80,7 +71,7 @@ bool SeparateDeclarations::visitAggregate(Visit, TIntermAggregate *node)
} // namespace } // namespace
void SeparateArrayDeclarations(TIntermNode *root) void SeparateDeclarations(TIntermNode *root)
{ {
SeparateDeclarations::apply(root); SeparateDeclarationsTraverser::apply(root);
} }
...@@ -3,9 +3,10 @@ ...@@ -3,9 +3,10 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// //
// The SeparateArrayDeclarations function processes declarations that contain array declarators. Each declarator in // The SeparateDeclarations function processes declarations, so that in the end each declaration
// such declarations gets its own declaration. // contains only one declarator.
// This is useful as an intermediate step when initialization needs to be separated from declaration. // This is useful as an intermediate step when initialization needs to be separated from declaration,
// or when things need to be unfolded out of the initializer.
// Example: // Example:
// int a[1] = int[1](1), b[1] = int[1](2); // int a[1] = int[1](1), b[1] = int[1](2);
// gets transformed when run through this class into the AST equivalent of: // gets transformed when run through this class into the AST equivalent of:
...@@ -17,6 +18,6 @@ ...@@ -17,6 +18,6 @@
class TIntermNode; class TIntermNode;
void SeparateArrayDeclarations(TIntermNode *root); void SeparateDeclarations(TIntermNode *root);
#endif // COMPILER_TRANSLATOR_SEPARATEDECLARATIONS_H_ #endif // COMPILER_TRANSLATOR_SEPARATEDECLARATIONS_H_
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "compiler/translator/SeparateArrayInitialization.h" #include "compiler/translator/SeparateArrayInitialization.h"
#include "compiler/translator/SeparateDeclarations.h" #include "compiler/translator/SeparateDeclarations.h"
#include "compiler/translator/SimplifyArrayAssignment.h" #include "compiler/translator/SimplifyArrayAssignment.h"
#include "compiler/translator/UnfoldShortCircuitToIf.h"
TranslatorHLSL::TranslatorHLSL(sh::GLenum type, ShShaderSpec spec, ShShaderOutput output) TranslatorHLSL::TranslatorHLSL(sh::GLenum type, ShShaderSpec spec, ShShaderOutput output)
: TCompiler(type, spec, output) : TCompiler(type, spec, output)
...@@ -22,7 +23,10 @@ void TranslatorHLSL::translate(TIntermNode *root, int compileOptions) ...@@ -22,7 +23,10 @@ void TranslatorHLSL::translate(TIntermNode *root, int compileOptions)
const ShBuiltInResources &resources = getResources(); const ShBuiltInResources &resources = getResources();
int numRenderTargets = resources.EXT_draw_buffers ? resources.MaxDrawBuffers : 1; int numRenderTargets = resources.EXT_draw_buffers ? resources.MaxDrawBuffers : 1;
SeparateArrayDeclarations(root); SeparateDeclarations(root);
// Note that SeparateDeclarations needs to be run before UnfoldShortCircuitToIf.
UnfoldShortCircuitToIf(root);
// Note that SeparateDeclarations needs to be run before SeparateArrayInitialization. // Note that SeparateDeclarations needs to be run before SeparateArrayInitialization.
SeparateArrayInitialization(root); SeparateArrayInitialization(root);
......
//
// Copyright (c) 2002-2013 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.
//
// UnfoldShortCircuit is an AST traverser to output short-circuiting operators as if-else statements.
// The results are assigned to s# temporaries, which are used by the main translator instead of
// the original expression.
//
#include "compiler/translator/UnfoldShortCircuit.h"
#include "compiler/translator/InfoSink.h"
#include "compiler/translator/OutputHLSL.h"
#include "compiler/translator/UtilsHLSL.h"
namespace sh
{
UnfoldShortCircuit::UnfoldShortCircuit(OutputHLSL *outputHLSL) : mOutputHLSL(outputHLSL)
{
mTemporaryIndex = 0;
}
void UnfoldShortCircuit::traverse(TIntermNode *node)
{
int rewindIndex = mTemporaryIndex;
node->traverse(this);
mTemporaryIndex = rewindIndex;
}
bool UnfoldShortCircuit::visitBinary(Visit visit, TIntermBinary *node)
{
TInfoSinkBase &out = mOutputHLSL->getInfoSink();
// If our right node doesn't have side effects, we know we don't need to unfold this
// expression: there will be no short-circuiting side effects to avoid
// (note: unfolding doesn't depend on the left node -- it will always be evaluated)
if (!node->getRight()->hasSideEffects())
{
return true;
}
switch (node->getOp())
{
case EOpLogicalOr:
// "x || y" is equivalent to "x ? true : y", which unfolds to "bool s; if(x) s = true; else s = y;",
// and then further simplifies down to "bool s = x; if(!s) s = y;".
{
int i = mTemporaryIndex;
out << "bool s" << i << ";\n";
out << "{\n";
mTemporaryIndex = i + 1;
node->getLeft()->traverse(this);
out << "s" << i << " = ";
mTemporaryIndex = i + 1;
node->getLeft()->traverse(mOutputHLSL);
out << ";\n";
out << "if (!s" << i << ")\n"
"{\n";
mTemporaryIndex = i + 1;
node->getRight()->traverse(this);
out << " s" << i << " = ";
mTemporaryIndex = i + 1;
node->getRight()->traverse(mOutputHLSL);
out << ";\n"
"}\n";
out << "}\n";
mTemporaryIndex = i + 1;
}
return false;
case EOpLogicalAnd:
// "x && y" is equivalent to "x ? y : false", which unfolds to "bool s; if(x) s = y; else s = false;",
// and then further simplifies down to "bool s = x; if(s) s = y;".
{
int i = mTemporaryIndex;
out << "bool s" << i << ";\n";
out << "{\n";
mTemporaryIndex = i + 1;
node->getLeft()->traverse(this);
out << "s" << i << " = ";
mTemporaryIndex = i + 1;
node->getLeft()->traverse(mOutputHLSL);
out << ";\n";
out << "if (s" << i << ")\n"
"{\n";
mTemporaryIndex = i + 1;
node->getRight()->traverse(this);
out << " s" << i << " = ";
mTemporaryIndex = i + 1;
node->getRight()->traverse(mOutputHLSL);
out << ";\n"
"}\n";
out << "}\n";
mTemporaryIndex = i + 1;
}
return false;
default:
return true;
}
}
bool UnfoldShortCircuit::visitSelection(Visit visit, TIntermSelection *node)
{
TInfoSinkBase &out = mOutputHLSL->getInfoSink();
// Unfold "b ? x : y" into "type s; if(b) s = x; else s = y;"
if (node->usesTernaryOperator())
{
int i = mTemporaryIndex;
out << TypeString(node->getType()) << " s" << i << ";\n";
out << "{\n";
mTemporaryIndex = i + 1;
node->getCondition()->traverse(this);
out << "if (";
mTemporaryIndex = i + 1;
node->getCondition()->traverse(mOutputHLSL);
out << ")\n"
"{\n";
mTemporaryIndex = i + 1;
node->getTrueBlock()->traverse(this);
out << " s" << i << " = ";
mTemporaryIndex = i + 1;
node->getTrueBlock()->traverse(mOutputHLSL);
out << ";\n"
"}\n"
"else\n"
"{\n";
mTemporaryIndex = i + 1;
node->getFalseBlock()->traverse(this);
out << " s" << i << " = ";
mTemporaryIndex = i + 1;
node->getFalseBlock()->traverse(mOutputHLSL);
out << ";\n"
"}\n";
out << "}\n";
mTemporaryIndex = i + 1;
}
return false;
}
bool UnfoldShortCircuit::visitLoop(Visit visit, TIntermLoop *node)
{
int rewindIndex = mTemporaryIndex;
if (node->getInit())
{
node->getInit()->traverse(this);
}
if (node->getCondition())
{
node->getCondition()->traverse(this);
}
if (node->getExpression())
{
node->getExpression()->traverse(this);
}
mTemporaryIndex = rewindIndex;
return false;
}
int UnfoldShortCircuit::getNextTemporaryIndex()
{
return mTemporaryIndex++;
}
}
//
// Copyright (c) 2002-2012 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.
//
// UnfoldShortCircuit is an AST traverser to output short-circuiting operators as if-else statements
//
#ifndef COMPILER_TRANSLATOR_UNFOLDSHORTCIRCUIT_H_
#define COMPILER_TRANSLATOR_UNFOLDSHORTCIRCUIT_H_
#include "compiler/translator/IntermNode.h"
#include "compiler/translator/ParseContext.h"
namespace sh
{
class OutputHLSL;
class UnfoldShortCircuit : public TIntermTraverser
{
public:
UnfoldShortCircuit(OutputHLSL *outputHLSL);
void traverse(TIntermNode *node);
bool visitBinary(Visit visit, TIntermBinary*);
bool visitSelection(Visit visit, TIntermSelection *node);
bool visitLoop(Visit visit, TIntermLoop *node);
int getNextTemporaryIndex();
protected:
OutputHLSL *const mOutputHLSL;
int mTemporaryIndex;
};
}
#endif // COMPILER_TRANSLATOR_UNFOLDSHORTCIRCUIT_H_
//
// Copyright (c) 2002-2013 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.
//
// UnfoldShortCircuitToIf is an AST traverser to convert short-circuiting operators to if-else statements.
// The results are assigned to s# temporaries, which are used by the main translator instead of
// the original expression.
//
#include "compiler/translator/UnfoldShortCircuitToIf.h"
#include "compiler/translator/InfoSink.h"
#include "compiler/translator/IntermNode.h"
namespace
{
// Traverser that unfolds one short-circuiting operation at a time.
class UnfoldShortCircuitTraverser : public TIntermTraverser
{
public:
UnfoldShortCircuitTraverser();
void traverse(TIntermNode *node);
bool visitBinary(Visit visit, TIntermBinary *node) override;
bool visitAggregate(Visit visit, TIntermAggregate *node) override;
bool visitSelection(Visit visit, TIntermSelection *node) override;
void nextIteration();
bool foundShortCircuit() const { return mFoundShortCircuit; }
protected:
int mTemporaryIndex;
// Marked to true once an operation that needs to be unfolded has been found.
// After that, no more unfolding is performed on that traversal.
bool mFoundShortCircuit;
struct ParentBlock
{
ParentBlock(TIntermAggregate *_node, TIntermSequence::size_type _pos)
: node(_node),
pos(_pos)
{
}
TIntermAggregate *node;
TIntermSequence::size_type pos;
};
std::vector<ParentBlock> mParentBlockStack;
TIntermSymbol *createTempSymbol(const TType &type);
TIntermAggregate *createTempInitDeclaration(const TType &type, TIntermTyped *initializer);
TIntermBinary *createTempAssignment(const TType &type, TIntermTyped *rightNode);
};
UnfoldShortCircuitTraverser::UnfoldShortCircuitTraverser()
: TIntermTraverser(true, true, true),
mTemporaryIndex(0),
mFoundShortCircuit(false)
{
}
TIntermSymbol *UnfoldShortCircuitTraverser::createTempSymbol(const TType &type)
{
// Each traversal uses at most one temporary variable, so the index stays the same within a single traversal.
TInfoSinkBase symbolNameOut;
symbolNameOut << "s" << mTemporaryIndex;
TString symbolName = symbolNameOut.c_str();
TIntermSymbol *node = new TIntermSymbol(0, symbolName, type);
node->setInternal(true);
return node;
}
TIntermAggregate *UnfoldShortCircuitTraverser::createTempInitDeclaration(const TType &type, TIntermTyped *initializer)
{
ASSERT(initializer != nullptr);
TIntermSymbol *tempSymbol = createTempSymbol(type);
TIntermAggregate *tempDeclaration = new TIntermAggregate(EOpDeclaration);
TIntermBinary *tempInit = new TIntermBinary(EOpInitialize);
tempInit->setLeft(tempSymbol);
tempInit->setRight(initializer);
tempInit->setType(type);
tempDeclaration->getSequence()->push_back(tempInit);
return tempDeclaration;
}
TIntermBinary *UnfoldShortCircuitTraverser::createTempAssignment(const TType &type, TIntermTyped *rightNode)
{
ASSERT(rightNode != nullptr);
TIntermSymbol *tempSymbol = createTempSymbol(type);
TIntermBinary *assignment = new TIntermBinary(EOpAssign);
assignment->setLeft(tempSymbol);
assignment->setRight(rightNode);
assignment->setType(type);
return assignment;
}
bool UnfoldShortCircuitTraverser::visitBinary(Visit visit, TIntermBinary *node)
{
if (mFoundShortCircuit)
return false;
// If our right node doesn't have side effects, we know we don't need to unfold this
// expression: there will be no short-circuiting side effects to avoid
// (note: unfolding doesn't depend on the left node -- it will always be evaluated)
if (!node->getRight()->hasSideEffects())
{
return true;
}
switch (node->getOp())
{
case EOpLogicalOr:
mFoundShortCircuit = true;
// "x || y" is equivalent to "x ? true : y", which unfolds to "bool s; if(x) s = true; else s = y;",
// and then further simplifies down to "bool s = x; if(!s) s = y;".
{
TIntermSequence insertions;
TType boolType(EbtBool, EbpUndefined, EvqTemporary);
insertions.push_back(createTempInitDeclaration(boolType, node->getLeft()));
TIntermAggregate *assignRightBlock = new TIntermAggregate(EOpSequence);
assignRightBlock->getSequence()->push_back(createTempAssignment(boolType, node->getRight()));
TIntermUnary *notTempSymbol = new TIntermUnary(EOpLogicalNot, boolType);
notTempSymbol->setOperand(createTempSymbol(boolType));
TIntermSelection *ifNode = new TIntermSelection(notTempSymbol, assignRightBlock, nullptr);
insertions.push_back(ifNode);
NodeInsertMultipleEntry insert(mParentBlockStack.back().node, mParentBlockStack.back().pos, insertions);
mInsertions.push_back(insert);
NodeUpdateEntry replaceVariable(getParentNode(), node, createTempSymbol(boolType), false);
mReplacements.push_back(replaceVariable);
}
return false;
case EOpLogicalAnd:
mFoundShortCircuit = true;
// "x && y" is equivalent to "x ? y : false", which unfolds to "bool s; if(x) s = y; else s = false;",
// and then further simplifies down to "bool s = x; if(s) s = y;".
{
TIntermSequence insertions;
TType boolType(EbtBool, EbpUndefined, EvqTemporary);
insertions.push_back(createTempInitDeclaration(boolType, node->getLeft()));
TIntermAggregate *assignRightBlock = new TIntermAggregate(EOpSequence);
assignRightBlock->getSequence()->push_back(createTempAssignment(boolType, node->getRight()));
TIntermSelection *ifNode = new TIntermSelection(createTempSymbol(boolType), assignRightBlock, nullptr);
insertions.push_back(ifNode);
NodeInsertMultipleEntry insert(mParentBlockStack.back().node, mParentBlockStack.back().pos, insertions);
mInsertions.push_back(insert);
NodeUpdateEntry replaceVariable(getParentNode(), node, createTempSymbol(boolType), false);
mReplacements.push_back(replaceVariable);
}
return false;
default:
return true;
}
}
bool UnfoldShortCircuitTraverser::visitSelection(Visit visit, TIntermSelection *node)
{
if (mFoundShortCircuit)
return false;
// Unfold "b ? x : y" into "type s; if(b) s = x; else s = y;"
if (visit == PreVisit && node->usesTernaryOperator())
{
mFoundShortCircuit = true;
TIntermSequence insertions;
TIntermSymbol *tempSymbol = createTempSymbol(node->getType());
TIntermAggregate *tempDeclaration = new TIntermAggregate(EOpDeclaration);
tempDeclaration->getSequence()->push_back(tempSymbol);
insertions.push_back(tempDeclaration);
TIntermAggregate *trueBlock = new TIntermAggregate(EOpSequence);
TIntermBinary *trueAssignment = createTempAssignment(node->getType(), node->getTrueBlock()->getAsTyped());
trueBlock->getSequence()->push_back(trueAssignment);
TIntermAggregate *falseBlock = new TIntermAggregate(EOpSequence);
TIntermBinary *falseAssignment = createTempAssignment(node->getType(), node->getFalseBlock()->getAsTyped());
falseBlock->getSequence()->push_back(falseAssignment);
TIntermSelection *ifNode = new TIntermSelection(node->getCondition()->getAsTyped(), trueBlock, falseBlock);
insertions.push_back(ifNode);
NodeInsertMultipleEntry insert(mParentBlockStack.back().node, mParentBlockStack.back().pos, insertions);
mInsertions.push_back(insert);
TIntermSymbol *ternaryResult = createTempSymbol(node->getType());
NodeUpdateEntry replaceVariable(getParentNode(), node, ternaryResult, false);
mReplacements.push_back(replaceVariable);
return false;
}
return true;
}
bool UnfoldShortCircuitTraverser::visitAggregate(Visit visit, TIntermAggregate *node)
{
if (node->getOp() == EOpSequence)
{
if (visit == PreVisit)
{
mParentBlockStack.push_back(ParentBlock(node, 0));
}
else if (visit == InVisit)
{
++mParentBlockStack.back().pos;
}
else
{
ASSERT(visit == PostVisit);
mParentBlockStack.pop_back();
}
}
return true;
}
void UnfoldShortCircuitTraverser::nextIteration()
{
mFoundShortCircuit = false;
mTemporaryIndex++;
mReplacements.clear();
mMultiReplacements.clear();
mInsertions.clear();
}
} // namespace
void UnfoldShortCircuitToIf(TIntermNode *root)
{
UnfoldShortCircuitTraverser traverser;
// Unfold one operator at a time, and reset the traverser between iterations.
do
{
traverser.nextIteration();
root->traverse(&traverser);
if (traverser.foundShortCircuit())
traverser.updateTree();
}
while (traverser.foundShortCircuit());
}
//
// Copyright (c) 2002-2012 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.
//
// UnfoldShortCircuitToIf is an AST traverser to convert short-circuiting operators to if-else statements.
// The results are assigned to s# temporaries, which are used by the main translator instead of
// the original expression.
//
#ifndef COMPILER_TRANSLATOR_UNFOLDSHORTCIRCUIT_H_
#define COMPILER_TRANSLATOR_UNFOLDSHORTCIRCUIT_H_
class TIntermNode;
void UnfoldShortCircuitToIf(TIntermNode *root);
#endif // COMPILER_TRANSLATOR_UNFOLDSHORTCIRCUIT_H_
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