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: https://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;