Break up loops with over 255 iterations.
TRAC# 11724
fixes acos/asin conformance
Signed-off-by: Shannon Woods
Signed-off-by: Daniel Koch
Author: Nicolas Capens
git-svn-id: http://angleproject.googlecode.com/svn/trunk@105 736b8ea6-26fd-11df-bfd4-992fa37f6226
diff --git a/src/compiler/OutputHLSL.cpp b/src/compiler/OutputHLSL.cpp
index 94a9341..2a3d854 100644
--- a/src/compiler/OutputHLSL.cpp
+++ b/src/compiler/OutputHLSL.cpp
@@ -1165,6 +1165,11 @@
bool OutputHLSL::visitLoop(Visit visit, TIntermLoop *node)
{
+ if (handleExcessiveLoop(node))
+ {
+ return false;
+ }
+
TInfoSinkBase &out = context.infoSink.obj;
if (!node->testFirst())
@@ -1252,6 +1257,172 @@
return true;
}
+// Handle loops with more than 255 iterations (unsupported by D3D9) by splitting them
+bool OutputHLSL::handleExcessiveLoop(TIntermLoop *node)
+{
+ TInfoSinkBase &out = context.infoSink.obj;
+
+ // Parse loops of the form:
+ // for(int index = initial; index [comparator] limit; index += increment)
+ TIntermSymbol *index = NULL;
+ TOperator comparator = EOpNull;
+ int initial = 0;
+ int limit = 0;
+ int increment = 0;
+
+ // Parse index name and intial value
+ if (node->getInit())
+ {
+ TIntermAggregate *init = node->getInit()->getAsAggregate();
+
+ if (init)
+ {
+ TIntermSequence &sequence = init->getSequence();
+ TIntermTyped *variable = sequence[0]->getAsTyped();
+
+ if (variable && variable->getQualifier() == EvqTemporary)
+ {
+ TIntermBinary *assign = variable->getAsBinaryNode();
+
+ if (assign->getOp() == EOpInitialize)
+ {
+ TIntermSymbol *symbol = assign->getLeft()->getAsSymbolNode();
+ TIntermConstantUnion *constant = assign->getRight()->getAsConstantUnion();
+
+ if (symbol && constant)
+ {
+ if (constant->getBasicType() == EbtInt && constant->getSize() == 1)
+ {
+ index = symbol;
+ initial = constant->getUnionArrayPointer()[0].getIConst();
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Parse comparator and limit value
+ if (index != NULL && node->getTest())
+ {
+ TIntermBinary *test = node->getTest()->getAsBinaryNode();
+
+ if (test && test->getLeft()->getAsSymbolNode()->getId() == index->getId())
+ {
+ TIntermConstantUnion *constant = test->getRight()->getAsConstantUnion();
+
+ if (constant)
+ {
+ if (constant->getBasicType() == EbtInt && constant->getSize() == 1)
+ {
+ comparator = test->getOp();
+ limit = constant->getUnionArrayPointer()[0].getIConst();
+ }
+ }
+ }
+ }
+
+ // Parse increment
+ if (index != NULL && comparator != EOpNull && node->getTerminal())
+ {
+ TIntermBinary *binaryTerminal = node->getTerminal()->getAsBinaryNode();
+ TIntermUnary *unaryTerminal = node->getTerminal()->getAsUnaryNode();
+
+ if (binaryTerminal)
+ {
+ TOperator op = binaryTerminal->getOp();
+ TIntermConstantUnion *constant = binaryTerminal->getRight()->getAsConstantUnion();
+
+ if (constant)
+ {
+ if (constant->getBasicType() == EbtInt && constant->getSize() == 1)
+ {
+ int value = constant->getUnionArrayPointer()[0].getIConst();
+
+ switch (op)
+ {
+ case EOpAddAssign: increment = value; break;
+ case EOpSubAssign: increment = -value; break;
+ default: UNIMPLEMENTED();
+ }
+ }
+ }
+ }
+ else if (unaryTerminal)
+ {
+ TOperator op = unaryTerminal->getOp();
+
+ switch (op)
+ {
+ case EOpPostIncrement: increment = 1; break;
+ case EOpPostDecrement: increment = -1; break;
+ case EOpPreIncrement: increment = 1; break;
+ case EOpPreDecrement: increment = -1; break;
+ default: UNIMPLEMENTED();
+ }
+ }
+ }
+
+ if (index != NULL && comparator != EOpNull && increment != 0)
+ {
+ if (comparator == EOpLessThanEqual)
+ {
+ comparator = EOpLessThan;
+ limit += 1;
+ }
+
+ if (comparator == EOpLessThan)
+ {
+ int iterations = (limit - initial + 1) / increment;
+
+ if (iterations <= 255)
+ {
+ return false; // Not an excessive loop
+ }
+
+ while (iterations > 0)
+ {
+ int remainder = (limit - initial + 1) % increment;
+ int clampedLimit = initial + increment * min(255, iterations) - 1 - remainder;
+
+ // for(int index = initial; index < clampedLimit; index += increment)
+
+ out << "for(int ";
+ index->traverse(this);
+ out << " = ";
+ out << initial;
+
+ out << "; ";
+ index->traverse(this);
+ out << " < ";
+ out << clampedLimit;
+
+ out << "; ";
+ index->traverse(this);
+ out << " += ";
+ out << increment;
+ out << ")\n"
+ "{\n";
+
+ if (node->getBody())
+ {
+ node->getBody()->traverse(this);
+ }
+
+ out << "}\n";
+
+ initial += 255 * increment;
+ iterations -= 255;
+ }
+
+ return true;
+ }
+ else UNIMPLEMENTED();
+ }
+
+ return false; // Not handled as an excessive loop
+}
+
void OutputHLSL::outputTriplet(Visit visit, const char *preString, const char *inString, const char *postString)
{
TInfoSinkBase &out = context.infoSink.obj;
diff --git a/src/compiler/OutputHLSL.h b/src/compiler/OutputHLSL.h
index 2be9a86..fcb9488 100644
--- a/src/compiler/OutputHLSL.h
+++ b/src/compiler/OutputHLSL.h
@@ -30,6 +30,7 @@
bool visitLoop(Visit visit, TIntermLoop*);
bool visitBranch(Visit visit, TIntermBranch*);
+ bool handleExcessiveLoop(TIntermLoop *node);
void outputTriplet(Visit visit, const char *preString, const char *inString, const char *postString);
static TString typeString(const TType &type);
diff --git a/src/compiler/intermediate.h b/src/compiler/intermediate.h
index b3cbd8d..4f83b86 100644
--- a/src/compiler/intermediate.h
+++ b/src/compiler/intermediate.h
@@ -185,6 +185,7 @@
class TIntermTraverser;
class TIntermAggregate;
class TIntermBinary;
+class TIntermUnary;
class TIntermConstantUnion;
class TIntermSelection;
class TIntermTyped;
@@ -206,6 +207,7 @@
virtual TIntermConstantUnion* getAsConstantUnion() { return 0; }
virtual TIntermAggregate* getAsAggregate() { return 0; }
virtual TIntermBinary* getAsBinaryNode() { return 0; }
+ virtual TIntermUnary* getAsUnaryNode() { return 0; }
virtual TIntermSelection* getAsSelectionNode() { return 0; }
virtual TIntermSymbol* getAsSymbolNode() { return 0; }
virtual ~TIntermNode() { }
@@ -221,9 +223,6 @@
TIntermNode* node2;
};
-class TIntermSymbol;
-class TIntermBinary;
-
//
// Intermediate class for nodes that have a type.
//
@@ -365,6 +364,7 @@
virtual void traverse(TIntermTraverser*);
virtual void setOperand(TIntermTyped* o) { operand = o; }
virtual TIntermTyped* getOperand() { return operand; }
+ virtual TIntermUnary* getAsUnaryNode() { return this; }
virtual bool promote(TInfoSink&);
protected:
TIntermTyped* operand;