Commit 7cab38b5 by Zhenyao Mo Committed by Shannon Woods

Add an option to unfold short circuiting in AST.

We replace "a || b" with "a ? true : b", "a && b" with "a ? b : false". This is to work around short circuiting bug in Mac drivers. ANGLEBUG=482 TEST=webgl conformance tests R=alokp@chromium.org, kbr@chromium.org Review URL: https://codereview.appspot.com/14529048 Conflicts: src/build_angle.gypi src/compiler/translator/Compiler.cpp Change-Id: Ic2384a97d58f54294efcb3a012deb2007a9fc658 Reviewed-on: https://chromium-review.googlesource.com/178996Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Commit-Queue: Jamie Madill <jmadill@chromium.org> Tested-by: 's avatarShannon Woods <shannonwoods@chromium.org>
parent 6cb95f3a
......@@ -217,6 +217,13 @@ typedef enum {
// fragment shader. It is intended as a workaround for drivers which
// incorrectly fail to link programs if gl_Position is not written.
SH_INIT_GL_POSITION = 0x8000,
// This flag replaces
// "a && b" with "a ? b : false",
// "a || b" with "a ? true : b".
// This is to work around a MacOSX driver bug that |b| is executed
// independent of |a|'s value.
SH_UNFOLD_SHORT_CIRCUIT = 0x10000,
} ShCompileOptions;
// Defines alternate strategies for implementing array index clamping.
......
......@@ -129,6 +129,7 @@
<ClInclude Include="..\..\src\compiler\translator\BaseTypes.h"/>
<ClInclude Include="..\..\src\compiler\translator\compilerdebug.h"/>
<ClInclude Include="..\..\src\compiler\translator\Common.h"/>
<ClInclude Include="..\..\src\compiler\translator\UnfoldShortCircuitAST.h"/>
<ClInclude Include="..\..\src\compiler\translator\util.h"/>
<ClInclude Include="..\..\src\compiler\translator\OutputGLSL.h"/>
<ClInclude Include="..\..\src\compiler\translator\FlagStd140Structs.h"/>
......@@ -228,6 +229,7 @@
<ClCompile Include="..\..\src\compiler\translator\glslang_tab.cpp"/>
<ClCompile Include="..\..\src\compiler\translator\ForLoopUnroll.cpp"/>
<ClCompile Include="..\..\src\compiler\translator\SearchSymbol.cpp"/>
<ClCompile Include="..\..\src\compiler\translator\UnfoldShortCircuitAST.cpp"/>
<ClCompile Include="..\..\src\compiler\translator\TranslatorGLSL.cpp"/>
<ClCompile Include="..\..\src\compiler\translator\SymbolTable.cpp"/>
<ClCompile Include="..\..\src\compiler\translator\DirectiveHandler.cpp"/>
......
......@@ -94,6 +94,9 @@
<ClInclude Include="..\..\src\compiler\translator\Common.h">
<Filter>compiler\translator</Filter>
</ClInclude>
<ClInclude Include="..\..\src\compiler\translator\UnfoldShortCircuitAST.h">
<Filter>..\..\src\compiler\translator</Filter>
</ClInclude>
<ClCompile Include="..\..\src\compiler\translator\ValidateLimitations.cpp">
<Filter>compiler\translator</Filter>
</ClCompile>
......@@ -331,6 +334,9 @@
<ClInclude Include="..\..\src\compiler\translator\BlockLayoutEncoder.h">
<Filter>compiler\translator</Filter>
</ClInclude>
<ClCompile Include="..\..\src\compiler\translator\UnfoldShortCircuitAST.cpp">
<Filter>..\..\src\compiler\translator</Filter>
</ClCompile>
<ClInclude Include="..\..\src\compiler\translator\MMap.h">
<Filter>compiler\translator</Filter>
</ClInclude>
......
......@@ -129,6 +129,7 @@
<ClInclude Include="..\..\src\compiler\translator\BaseTypes.h"/>
<ClInclude Include="..\..\src\compiler\translator\compilerdebug.h"/>
<ClInclude Include="..\..\src\compiler\translator\Common.h"/>
<ClInclude Include="..\..\src\compiler\translator\UnfoldShortCircuitAST.h"/>
<ClInclude Include="..\..\src\compiler\translator\util.h"/>
<ClInclude Include="..\..\src\compiler\translator\OutputGLSL.h"/>
<ClInclude Include="..\..\src\compiler\translator\FlagStd140Structs.h"/>
......@@ -228,6 +229,7 @@
<ClCompile Include="..\..\src\compiler\translator\glslang_tab.cpp"/>
<ClCompile Include="..\..\src\compiler\translator\ForLoopUnroll.cpp"/>
<ClCompile Include="..\..\src\compiler\translator\SearchSymbol.cpp"/>
<ClCompile Include="..\..\src\compiler\translator\UnfoldShortCircuitAST.cpp"/>
<ClCompile Include="..\..\src\compiler\translator\TranslatorGLSL.cpp"/>
<ClCompile Include="..\..\src\compiler\translator\SymbolTable.cpp"/>
<ClCompile Include="..\..\src\compiler\translator\DirectiveHandler.cpp"/>
......
......@@ -94,6 +94,9 @@
<ClInclude Include="..\..\src\compiler\translator\Common.h">
<Filter>compiler\translator</Filter>
</ClInclude>
<ClInclude Include="..\..\src\compiler\translator\UnfoldShortCircuitAST.h">
<Filter>..\..\src\compiler\translator</Filter>
</ClInclude>
<ClCompile Include="..\..\src\compiler\translator\ValidateLimitations.cpp">
<Filter>compiler\translator</Filter>
</ClCompile>
......@@ -331,6 +334,9 @@
<ClInclude Include="..\..\src\compiler\translator\BlockLayoutEncoder.h">
<Filter>compiler\translator</Filter>
</ClInclude>
<ClCompile Include="..\..\src\compiler\translator\UnfoldShortCircuitAST.cpp">
<Filter>..\..\src\compiler\translator</Filter>
</ClCompile>
<ClInclude Include="..\..\src\compiler\translator\MMap.h">
<Filter>compiler\translator</Filter>
</ClInclude>
......
......@@ -14,6 +14,7 @@
#include "compiler/translator/ParseContext.h"
#include "compiler/translator/RenameFunction.h"
#include "compiler/translator/ShHandle.h"
#include "compiler/translator/UnfoldShortCircuitAST.h"
#include "compiler/translator/ValidateLimitations.h"
#include "compiler/translator/ValidateOutputs.h"
#include "compiler/translator/VariablePacker.h"
......@@ -202,6 +203,12 @@ bool TCompiler::compile(const char* const shaderStrings[],
root->traverse(&initGLPosition);
}
if (success && (compileOptions & SH_UNFOLD_SHORT_CIRCUIT)) {
UnfoldShortCircuitAST unfoldShortCircuit;
root->traverse(&unfoldShortCircuit);
unfoldShortCircuit.updateTree();
}
if (success && (compileOptions & SH_VARIABLES)) {
collectVariables(root);
if (compileOptions & SH_ENFORCE_PACKING_RESTRICTIONS) {
......
......@@ -51,7 +51,7 @@ void TIntermBinary::traverse(TIntermTraverser *it)
//
if (visit)
{
it->incrementDepth();
it->incrementDepth(this);
if (it->rightToLeft)
{
......@@ -98,7 +98,7 @@ void TIntermUnary::traverse(TIntermTraverser *it)
visit = it->visitUnary(PreVisit, this);
if (visit) {
it->incrementDepth();
it->incrementDepth(this);
operand->traverse(it);
it->decrementDepth();
}
......@@ -119,7 +119,7 @@ void TIntermAggregate::traverse(TIntermTraverser *it)
if (visit)
{
it->incrementDepth();
it->incrementDepth(this);
if (it->rightToLeft)
{
......@@ -166,7 +166,7 @@ void TIntermSelection::traverse(TIntermTraverser *it)
visit = it->visitSelection(PreVisit, this);
if (visit) {
it->incrementDepth();
it->incrementDepth(this);
if (it->rightToLeft) {
if (falseBlock)
falseBlock->traverse(it);
......@@ -199,7 +199,7 @@ void TIntermLoop::traverse(TIntermTraverser *it)
if (visit)
{
it->incrementDepth();
it->incrementDepth(this);
if (it->rightToLeft)
{
......@@ -248,7 +248,7 @@ void TIntermBranch::traverse(TIntermTraverser *it)
visit = it->visitBranch(PreVisit, this);
if (visit && expression) {
it->incrementDepth();
it->incrementDepth(this);
expression->traverse(it);
it->decrementDepth();
}
......
......@@ -20,11 +20,13 @@
bool CompareStructure(const TType& leftNodeType, ConstantUnion* rightUnionArray, ConstantUnion* leftUnionArray);
static TPrecision GetHigherPrecision( TPrecision left, TPrecision right ){
static TPrecision GetHigherPrecision(TPrecision left, TPrecision right)
{
return left > right ? left : right;
}
const char* getOperatorString(TOperator op) {
const char* getOperatorString(TOperator op)
{
switch (op) {
case EOpInitialize: return "=";
case EOpAssign: return "=";
......@@ -769,6 +771,63 @@ void TIntermediate::remove(TIntermNode* root)
//
////////////////////////////////////////////////////////////////
#define REPLACE_IF_IS(node, type, original, replacement) \
if (node == original) { \
node = static_cast<type *>(replacement); \
return true; \
}
bool TIntermLoop::replaceChildNode(
TIntermNode *original, TIntermNode *replacement)
{
REPLACE_IF_IS(init, TIntermNode, original, replacement);
REPLACE_IF_IS(cond, TIntermTyped, original, replacement);
REPLACE_IF_IS(expr, TIntermTyped, original, replacement);
REPLACE_IF_IS(body, TIntermNode, original, replacement);
return false;
}
bool TIntermBranch::replaceChildNode(
TIntermNode *original, TIntermNode *replacement)
{
REPLACE_IF_IS(expression, TIntermTyped, original, replacement);
return false;
}
bool TIntermBinary::replaceChildNode(
TIntermNode *original, TIntermNode *replacement)
{
REPLACE_IF_IS(left, TIntermTyped, original, replacement);
REPLACE_IF_IS(right, TIntermTyped, original, replacement);
return false;
}
bool TIntermUnary::replaceChildNode(
TIntermNode *original, TIntermNode *replacement)
{
REPLACE_IF_IS(operand, TIntermTyped, original, replacement);
return false;
}
bool TIntermAggregate::replaceChildNode(
TIntermNode *original, TIntermNode *replacement)
{
for (size_t ii = 0; ii < sequence.size(); ++ii)
{
REPLACE_IF_IS(sequence[ii], TIntermNode, original, replacement);
}
return false;
}
bool TIntermSelection::replaceChildNode(
TIntermNode *original, TIntermNode *replacement)
{
REPLACE_IF_IS(condition, TIntermTyped, original, replacement);
REPLACE_IF_IS(trueBlock, TIntermNode, original, replacement);
REPLACE_IF_IS(falseBlock, TIntermNode, original, replacement);
return false;
}
//
// Say whether or not an operation node changes the value of a variable.
//
......@@ -825,6 +884,7 @@ bool TIntermOperator::isConstructor() const
return false;
}
}
//
// Make sure the type of a unary operator is appropriate for its
// combination of operation and operand type.
......
......@@ -437,7 +437,7 @@ bool TOutputGLSLBase::visitSelection(Visit visit, TIntermSelection* node)
node->getCondition()->traverse(this);
out << ")\n";
incrementDepth();
incrementDepth(node);
visitCodeBlock(node->getTrueBlock());
if (node->getFalseBlock())
......@@ -462,7 +462,7 @@ bool TOutputGLSLBase::visitAggregate(Visit visit, TIntermAggregate* node)
// Scope the sequences except when at the global scope.
if (depth > 0) out << "{\n";
incrementDepth();
incrementDepth(node);
const TIntermSequence& sequence = node->getSequence();
for (TIntermSequence::const_iterator iter = sequence.begin();
iter != sequence.end(); ++iter)
......@@ -500,7 +500,7 @@ bool TOutputGLSLBase::visitAggregate(Visit visit, TIntermAggregate* node)
writeVariableType(node->getType());
out << " " << hashFunctionName(node->getName());
incrementDepth();
incrementDepth(node);
// Function definition node contains one or two children nodes
// representing function parameters and function body. The latter
// is not present in case of empty function bodies.
......@@ -640,7 +640,7 @@ bool TOutputGLSLBase::visitLoop(Visit visit, TIntermLoop* node)
{
TInfoSinkBase& out = objSink();
incrementDepth();
incrementDepth(node);
// Loop header.
TLoopType loopType = node->getType();
if (loopType == ELoopFor) // for loop
......
//
// 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.
//
#include "compiler/translator/UnfoldShortCircuitAST.h"
namespace
{
// "x || y" is equivalent to "x ? true : y".
TIntermSelection *UnfoldOR(TIntermTyped *x, TIntermTyped *y)
{
const TType boolType(EbtBool, EbpUndefined);
ConstantUnion *u = new ConstantUnion;
u->setBConst(true);
TIntermConstantUnion *trueNode = new TIntermConstantUnion(
u, TType(EbtBool, EbpUndefined, EvqConst, 1));
return new TIntermSelection(x, trueNode, y, boolType);
}
// "x && y" is equivalent to "x ? y : false".
TIntermSelection *UnfoldAND(TIntermTyped *x, TIntermTyped *y)
{
const TType boolType(EbtBool, EbpUndefined);
ConstantUnion *u = new ConstantUnion;
u->setBConst(false);
TIntermConstantUnion *falseNode = new TIntermConstantUnion(
u, TType(EbtBool, EbpUndefined, EvqConst, 1));
return new TIntermSelection(x, y, falseNode, boolType);
}
} // namespace anonymous
bool UnfoldShortCircuitAST::visitBinary(Visit visit, TIntermBinary *node)
{
TIntermSelection *replacement = NULL;
switch (node->getOp())
{
case EOpLogicalOr:
replacement = UnfoldOR(node->getLeft(), node->getRight());
break;
case EOpLogicalAnd:
replacement = UnfoldAND(node->getLeft(), node->getRight());
break;
default:
break;
}
if (replacement)
{
replacements.push_back(
NodeUpdateEntry(getParentNode(), node, replacement));
}
return true;
}
void UnfoldShortCircuitAST::updateTree()
{
for (size_t ii = 0; ii < replacements.size(); ++ii)
{
const NodeUpdateEntry& entry = replacements[ii];
ASSERT(entry.parent);
bool replaced = entry.parent->replaceChildNode(
entry.original, entry.replacement);
ASSERT(replaced);
// In AST traversing, a parent is visited before its children.
// After we replace a node, if an immediate child is to
// be replaced, we need to make sure we don't update the replaced
// node; instead, we update the replacement node.
for (size_t jj = ii + 1; jj < replacements.size(); ++jj)
{
NodeUpdateEntry& entry2 = replacements[jj];
if (entry2.parent == entry.original)
entry2.parent = entry.replacement;
}
}
}
//
// 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.
//
// UnfoldShortCircuitAST is an AST traverser to replace short-circuiting
// operations with ternary operations.
//
#ifndef COMPILER_UNFOLD_SHORT_CIRCUIT_AST_H_
#define COMPILER_UNFOLD_SHORT_CIRCUIT_AST_H_
#include "common/angleutils.h"
#include "compiler/translator/intermediate.h"
// This traverser identifies all the short circuit binary nodes that need to
// be replaced, and creates the corresponding replacement nodes. However,
// the actual replacements happen after the traverse through updateTree().
class UnfoldShortCircuitAST : public TIntermTraverser
{
public:
UnfoldShortCircuitAST() { }
virtual bool visitBinary(Visit visit, TIntermBinary *);
void updateTree();
private:
struct NodeUpdateEntry
{
NodeUpdateEntry(TIntermNode *_parent,
TIntermNode *_original,
TIntermNode *_replacement)
: parent(_parent),
original(_original),
replacement(_replacement) {}
TIntermNode *parent;
TIntermNode *original;
TIntermNode *replacement;
};
// During traversing, save all the replacements that need to happen;
// then replace them by calling updateNodes().
std::vector<NodeUpdateEntry> replacements;
DISALLOW_COPY_AND_ASSIGN(UnfoldShortCircuitAST);
};
#endif // COMPILER_UNFOLD_SHORT_CIRCUIT_AST_H_
......@@ -238,6 +238,11 @@ public:
virtual TIntermSymbol* getAsSymbolNode() { return 0; }
virtual TIntermLoop* getAsLoopNode() { return 0; }
// Replace a child node. Return true if |original| is a child
// node and it is replaced; otherwise, return false.
virtual bool replaceChildNode(
TIntermNode *original, TIntermNode *replacement) = 0;
protected:
TSourceLoc line;
};
......@@ -311,6 +316,8 @@ public:
virtual TIntermLoop* getAsLoopNode() { return this; }
virtual void traverse(TIntermTraverser*);
virtual bool replaceChildNode(
TIntermNode *original, TIntermNode *replacement);
TLoopType getType() const { return type; }
TIntermNode* getInit() { return init; }
......@@ -341,6 +348,8 @@ public:
expression(e) { }
virtual void traverse(TIntermTraverser*);
virtual bool replaceChildNode(
TIntermNode *original, TIntermNode *replacement);
TOperator getFlowOp() { return flowOp; }
TIntermTyped* getExpression() { return expression; }
......@@ -373,6 +382,7 @@ public:
virtual void traverse(TIntermTraverser*);
virtual TIntermSymbol* getAsSymbolNode() { return this; }
virtual bool replaceChildNode(TIntermNode *, TIntermNode *) { return false; }
protected:
int id;
......@@ -395,6 +405,7 @@ public:
virtual TIntermConstantUnion* getAsConstantUnion() { return this; }
virtual void traverse(TIntermTraverser*);
virtual bool replaceChildNode(TIntermNode *, TIntermNode *) { return false; }
TIntermTyped* fold(TOperator, TIntermTyped*, TInfoSink&);
......@@ -430,6 +441,8 @@ public:
virtual TIntermBinary* getAsBinaryNode() { return this; }
virtual void traverse(TIntermTraverser*);
virtual bool replaceChildNode(
TIntermNode *original, TIntermNode *replacement);
virtual bool hasSideEffects() const { return (isAssignment() || left->hasSideEffects() || right->hasSideEffects()); }
......@@ -460,6 +473,8 @@ public:
virtual void traverse(TIntermTraverser*);
virtual TIntermUnary* getAsUnaryNode() { return this; }
virtual bool replaceChildNode(
TIntermNode *original, TIntermNode *replacement);
virtual bool hasSideEffects() const { return (isAssignment() || operand->hasSideEffects()); }
......@@ -492,6 +507,8 @@ public:
virtual TIntermAggregate* getAsAggregate() { return this; }
virtual void traverse(TIntermTraverser*);
virtual bool replaceChildNode(
TIntermNode *original, TIntermNode *replacement);
// Conservatively assume function calls and other aggregate operators have side-effects
virtual bool hasSideEffects() const { return true; }
......@@ -538,6 +555,8 @@ public:
TIntermTyped(type), condition(cond), trueBlock(trueB), falseBlock(falseB) {}
virtual void traverse(TIntermTraverser*);
virtual bool replaceChildNode(
TIntermNode *original, TIntermNode *replacement);
// Conservatively assume selections have side-effects
virtual bool hasSideEffects() const { return true; }
......@@ -580,7 +599,7 @@ public:
rightToLeft(rightToLeft),
depth(0),
maxDepth(0) {}
virtual ~TIntermTraverser() {};
virtual ~TIntermTraverser() {}
virtual void visitSymbol(TIntermSymbol*) {}
virtual void visitConstantUnion(TIntermConstantUnion*) {}
......@@ -592,8 +611,24 @@ public:
virtual bool visitBranch(Visit visit, TIntermBranch*) {return true;}
int getMaxDepth() const {return maxDepth;}
void incrementDepth() {depth++; maxDepth = std::max(maxDepth, depth); }
void decrementDepth() {depth--;}
void incrementDepth(TIntermNode *current)
{
depth++;
maxDepth = std::max(maxDepth, depth);
path.push_back(current);
}
void decrementDepth()
{
depth--;
path.pop_back();
}
TIntermNode *getParentNode()
{
return path.size() == 0 ? NULL : path.back();
}
// Return the original name if hash function pointer is NULL;
// otherwise return the hashed name.
......@@ -607,6 +642,9 @@ public:
protected:
int depth;
int maxDepth;
// All the nodes from root to the current node's parent during traversing.
TVector<TIntermNode *> path;
};
#endif // __INTERMEDIATE_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