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;