Implemented short-circuiting behavior for the ternary operator
TRAC #11444
This is achieved by turning the ternary operator into conditional code.
The UnfoldSelect intermediate code traverser places this conditional
code before the statement containing the ternary operator (aka. select).
The computed value is assigned to a temporary variable.
On outputting the actual statement the ternary operator is
replaced by the temporary variable.
Signed-off-by: Andrew Lewycky
Signed-off-by: Daniel Koch
Author: Nicolas Capens
git-svn-id: https://angleproject.googlecode.com/svn/trunk@148 736b8ea6-26fd-11df-bfd4-992fa37f6226
diff --git a/src/compiler/OutputHLSL.cpp b/src/compiler/OutputHLSL.cpp
index 38fd3c6..30d77df 100644
--- a/src/compiler/OutputHLSL.cpp
+++ b/src/compiler/OutputHLSL.cpp
@@ -6,6 +6,7 @@
#include "OutputHLSL.h"
+#include "UnfoldSelect.h"
#include "common/debug.h"
#include "InfoSink.h"
@@ -13,6 +14,8 @@
{
OutputHLSL::OutputHLSL(TParseContext &context) : TIntermTraverser(true, true, true), mContext(context)
{
+ mUnfoldSelect = new UnfoldSelect(context, this);
+
mUsesEqualMat2 = false;
mUsesEqualMat3 = false;
mUsesEqualMat4 = false;
@@ -27,6 +30,11 @@
mUsesEqualBVec4 = false;
}
+OutputHLSL::~OutputHLSL()
+{
+ delete mUnfoldSelect;
+}
+
void OutputHLSL::output()
{
mContext.treeRoot->traverse(this); // Output the body first to determine what has to go in the header and footer
@@ -38,6 +46,11 @@
mContext.infoSink.obj << mFooter.c_str();
}
+TInfoSinkBase &OutputHLSL::getBodyStream()
+{
+ return mBody;
+}
+
void OutputHLSL::header()
{
EShLanguage language = mContext.language;
@@ -933,7 +946,22 @@
switch (node->getOp())
{
- case EOpSequence: outputTriplet(visit, NULL, ";\n", ";\n"); break;
+ case EOpSequence:
+ {
+ for (TIntermSequence::iterator sit = node->getSequence().begin(); sit != node->getSequence().end(); sit++)
+ {
+ if (isSingleStatement(*sit))
+ {
+ mUnfoldSelect->traverse(*sit);
+ }
+
+ (*sit)->traverse(this);
+
+ out << ";\n";
+ }
+
+ return false;
+ }
case EOpDeclaration:
if (visit == PreVisit)
{
@@ -1200,16 +1228,12 @@
if (node->usesTernaryOperator())
{
- out << "(";
- node->getCondition()->traverse(this);
- out << ") ? (";
- node->getTrueBlock()->traverse(this);
- out << ") : (";
- node->getFalseBlock()->traverse(this);
- out << ")\n";
+ out << "t" << mUnfoldSelect->getTemporaryIndex();
}
else // if/else statement
{
+ mUnfoldSelect->traverse(node->getCondition());
+
out << "if(";
node->getCondition()->traverse(this);
@@ -1373,6 +1397,21 @@
}
else
{
+ if (node->getInit())
+ {
+ mUnfoldSelect->traverse(node->getInit());
+ }
+
+ if (node->getTest())
+ {
+ mUnfoldSelect->traverse(node->getTest());
+ }
+
+ if (node->getTerminal())
+ {
+ mUnfoldSelect->traverse(node->getTerminal());
+ }
+
out << "for(";
if (node->getInit())
@@ -1451,6 +1490,33 @@
return true;
}
+bool OutputHLSL::isSingleStatement(TIntermNode *node)
+{
+ TIntermAggregate *aggregate = node->getAsAggregate();
+
+ if (aggregate)
+ {
+ if (aggregate->getOp() == EOpSequence)
+ {
+ return false;
+ }
+ else
+ {
+ for (TIntermSequence::iterator sit = aggregate->getSequence().begin(); sit != aggregate->getSequence().end(); sit++)
+ {
+ if (!isSingleStatement(*sit))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+ }
+
+ return true;
+}
+
// Handle loops with more than 255 iterations (unsupported by D3D9) by splitting them
bool OutputHLSL::handleExcessiveLoop(TIntermLoop *node)
{
diff --git a/src/compiler/OutputHLSL.h b/src/compiler/OutputHLSL.h
index 43ee78c..aa65611 100644
--- a/src/compiler/OutputHLSL.h
+++ b/src/compiler/OutputHLSL.h
@@ -12,13 +12,25 @@
namespace sh
{
+class UnfoldSelect;
+
class OutputHLSL : public TIntermTraverser
{
public:
- OutputHLSL(TParseContext &context);
+ explicit OutputHLSL(TParseContext &context);
+ ~OutputHLSL();
void output();
+ TInfoSinkBase &getBodyStream();
+
+ static TString argumentString(const TIntermSymbol *symbol);
+ static TString qualifierString(TQualifier qualifier);
+ static TString typeString(const TType &type);
+ static TString arrayString(const TType &type);
+ static TString initializer(const TType &type);
+ static TString decorate(const TString &string); // Prepend an underscore to avoid naming clashes
+
protected:
void header();
void footer();
@@ -33,17 +45,12 @@
bool visitLoop(Visit visit, TIntermLoop*);
bool visitBranch(Visit visit, TIntermBranch*);
+ bool isSingleStatement(TIntermNode *node);
bool handleExcessiveLoop(TIntermLoop *node);
void outputTriplet(Visit visit, const char *preString, const char *inString, const char *postString);
- static TString argumentString(const TIntermSymbol *symbol);
- static TString qualifierString(TQualifier qualifier);
- static TString typeString(const TType &type);
- static TString arrayString(const TType &type);
- static TString initializer(const TType &type);
- static TString decorate(const TString &string); // Prepend an underscore to avoid naming clashes
-
TParseContext &mContext;
+ UnfoldSelect *mUnfoldSelect;
// Output streams
TInfoSinkBase mHeader;
diff --git a/src/compiler/UnfoldSelect.cpp b/src/compiler/UnfoldSelect.cpp
new file mode 100644
index 0000000..65ccfa6
--- /dev/null
+++ b/src/compiler/UnfoldSelect.cpp
@@ -0,0 +1,64 @@
+//
+// Copyright (c) 2002-2010 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 "UnfoldSelect.h"
+
+#include "OutputHLSL.h"
+#include "common/debug.h"
+#include "InfoSink.h"
+
+namespace sh
+{
+UnfoldSelect::UnfoldSelect(TParseContext &context, OutputHLSL *outputHLSL) : mContext(context), mOutputHLSL(outputHLSL)
+{
+ mTemporaryIndex = 0;
+}
+
+void UnfoldSelect::traverse(TIntermNode *node)
+{
+ mTemporaryIndex++;
+ node->traverse(this);
+}
+
+bool UnfoldSelect::visitSelection(Visit visit, TIntermSelection *node)
+{
+ TInfoSinkBase &out = mOutputHLSL->getBodyStream();
+
+ if (node->usesTernaryOperator())
+ {
+ int i = mTemporaryIndex++;
+
+ out << OutputHLSL::typeString(node->getType()) << " t" << i << ";\n";
+
+ node->getCondition()->traverse(this);
+ out << "if(";
+ node->getCondition()->traverse(mOutputHLSL);
+ out << ")\n"
+ "{\n";
+ node->getTrueBlock()->traverse(this);
+ out << " t" << i << " = ";
+ node->getTrueBlock()->traverse(mOutputHLSL);
+ out << ";\n"
+ "}\n"
+ "else\n"
+ "{\n";
+ node->getCondition()->traverse(this);
+ out << " t" << i << " = ";
+ node->getFalseBlock()->traverse(mOutputHLSL);
+ out << ";\n"
+ "}\n";
+
+ mTemporaryIndex--;
+ }
+
+ return false;
+}
+
+int UnfoldSelect::getTemporaryIndex()
+{
+ return mTemporaryIndex;
+}
+}
diff --git a/src/compiler/UnfoldSelect.h b/src/compiler/UnfoldSelect.h
new file mode 100644
index 0000000..44a706c
--- /dev/null
+++ b/src/compiler/UnfoldSelect.h
@@ -0,0 +1,35 @@
+//
+// Copyright (c) 2002-2010 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.
+//
+
+#ifndef COMPILER_UNFOLDSELECT_H_
+#define COMPILER_UNFOLDSELECT_H_
+
+#include "intermediate.h"
+#include "ParseHelper.h"
+
+namespace sh
+{
+class OutputHLSL;
+
+class UnfoldSelect : public TIntermTraverser
+{
+ public:
+ UnfoldSelect(TParseContext &context, OutputHLSL *outputHLSL);
+
+ void traverse(TIntermNode *node);
+ bool visitSelection(Visit visit, TIntermSelection *node);
+
+ int getTemporaryIndex();
+
+ protected:
+ TParseContext &mContext;
+ OutputHLSL *const mOutputHLSL;
+
+ int mTemporaryIndex;
+};
+}
+
+#endif // COMPILER_UNFOLDSELECT_H_
diff --git a/src/compiler/translator_hlsl.vcproj b/src/compiler/translator_hlsl.vcproj
index 7998ff3..d0b6c6f 100644
--- a/src/compiler/translator_hlsl.vcproj
+++ b/src/compiler/translator_hlsl.vcproj
@@ -162,6 +162,10 @@
RelativePath=".\TranslatorHLSL.cpp"
>
</File>
+ <File
+ RelativePath=".\UnfoldSelect.cpp"
+ >
+ </File>
</Filter>
<Filter
Name="Header Files"
@@ -176,6 +180,10 @@
RelativePath=".\TranslatorHLSL.h"
>
</File>
+ <File
+ RelativePath=".\UnfoldSelect.h"
+ >
+ </File>
</Filter>
</Files>
<Globals>