Add constant folding support for min,max and clamp

This change adds necessary mechanism to support constant folding of
built-ins that take more than one parameter and also adds constant
folding support for min, max and clamp built-ins.

BUG=angleproject:913
TESTS=dEQP tests
(126 tests started passing with this change)
dEQP-GLES3.functional.shaders.constant_expressions.builtin_functions.common.min_*
dEQP-GLES3.functional.shaders.constant_expressions.builtin_functions.common.max_*
dEQP-GLES3.functional.shaders.constant_expressions.builtin_functions.common.clamp_*

Change-Id: Iccc9bf503a536f2e3c144627e64572f2f95db9db
Reviewed-on: https://chromium-review.googlesource.com/271251
Reviewed-by: Olli Etuaho <oetuaho@nvidia.com>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Tested-by: Olli Etuaho <oetuaho@nvidia.com>
diff --git a/src/compiler/translator/IntermNode.cpp b/src/compiler/translator/IntermNode.cpp
index 2c29144..4b35fef 100644
--- a/src/compiler/translator/IntermNode.cpp
+++ b/src/compiler/translator/IntermNode.cpp
@@ -13,7 +13,9 @@
 #include <math.h>
 #include <stdlib.h>
 #include <algorithm>
+#include <vector>
 
+#include "common/mathutil.h"
 #include "compiler/translator/HashNames.h"
 #include "compiler/translator/IntermNode.h"
 #include "compiler/translator/SymbolTable.h"
@@ -130,6 +132,15 @@
     return true;
 }
 
+TConstantUnion *Vectorize(const TConstantUnion &constant, size_t size)
+{
+    TConstantUnion *constUnion = new TConstantUnion[size];
+    for (unsigned int i = 0; i < size; ++i)
+        constUnion[i] = constant;
+
+    return constUnion;
+}
+
 }  // namespace anonymous
 
 
@@ -702,21 +713,13 @@
         // for a case like float f = vec4(2, 3, 4, 5) + 1.2;
         if (rightNode->getType().getObjectSize() == 1 && objectSize > 1)
         {
-            rightUnionArray = new TConstantUnion[objectSize];
-            for (size_t i = 0; i < objectSize; ++i)
-            {
-                rightUnionArray[i] = *rightNode->getUnionArrayPointer();
-            }
+            rightUnionArray = Vectorize(*rightNode->getUnionArrayPointer(), objectSize);
             returnType = getType();
         }
         else if (rightNode->getType().getObjectSize() > 1 && objectSize == 1)
         {
             // for a case like float f = 1.2 + vec4(2, 3, 4, 5);
-            unionArray = new TConstantUnion[rightNode->getType().getObjectSize()];
-            for (size_t i = 0; i < rightNode->getType().getObjectSize(); ++i)
-            {
-                unionArray[i] = *getUnionArrayPointer();
-            }
+            unionArray = Vectorize(*getUnionArrayPointer(), rightNode->getType().getObjectSize());
             returnType = rightNode->getType();
             objectSize = rightNode->getType().getObjectSize();
         }
@@ -1490,6 +1493,176 @@
 }
 
 // static
+TIntermTyped *TIntermConstantUnion::FoldAggregateBuiltIn(TOperator op, TIntermAggregate *aggregate)
+{
+    TIntermSequence *sequence = aggregate->getSequence();
+    unsigned int paramsCount = sequence->size();
+    std::vector<TConstantUnion *> unionArrays(paramsCount);
+    std::vector<size_t> objectSizes(paramsCount);
+    TType *maxSizeType = nullptr;
+    TBasicType basicType = EbtVoid;
+    TSourceLoc loc;
+    for (unsigned int i = 0; i < paramsCount; i++)
+    {
+        TIntermConstantUnion *paramConstant = (*sequence)[i]->getAsConstantUnion();
+        // Make sure that all params are constant before actual constant folding.
+        if (!paramConstant)
+            return nullptr;
+
+        if (i == 0)
+        {
+            basicType = paramConstant->getType().getBasicType();
+            loc = paramConstant->getLine();
+        }
+        unionArrays[i] = paramConstant->getUnionArrayPointer();
+        objectSizes[i] = paramConstant->getType().getObjectSize();
+        if (maxSizeType == nullptr || (objectSizes[i] >= maxSizeType->getObjectSize()))
+            maxSizeType = paramConstant->getTypePointer();
+    }
+
+    size_t maxObjectSize = maxSizeType->getObjectSize();
+    for (unsigned int i = 0; i < paramsCount; i++)
+        if (objectSizes[i] != maxObjectSize)
+            unionArrays[i] = Vectorize(*unionArrays[i], maxObjectSize);
+
+    TConstantUnion *tempConstArray = nullptr;
+    TIntermConstantUnion *tempNode = nullptr;
+    TType returnType = *maxSizeType;
+    if (paramsCount == 2)
+    {
+        //
+        // Binary built-in
+        //
+        switch (op)
+        {
+          case EOpMin:
+            {
+                tempConstArray = new TConstantUnion[maxObjectSize];
+                for (size_t i = 0; i < maxObjectSize; i++)
+                {
+                    switch (basicType)
+                    {
+                      case EbtFloat:
+                        tempConstArray[i].setFConst(std::min(unionArrays[0][i].getFConst(), unionArrays[1][i].getFConst()));
+                        break;
+                      case EbtInt:
+                        tempConstArray[i].setIConst(std::min(unionArrays[0][i].getIConst(), unionArrays[1][i].getIConst()));
+                        break;
+                      case EbtUInt:
+                        tempConstArray[i].setUConst(std::min(unionArrays[0][i].getUConst(), unionArrays[1][i].getUConst()));
+                        break;
+                      default:
+                        UNREACHABLE();
+                        break;
+                    }
+                }
+            }
+            break;
+
+          case EOpMax:
+            {
+                tempConstArray = new TConstantUnion[maxObjectSize];
+                for (size_t i = 0; i < maxObjectSize; i++)
+                {
+                    switch (basicType)
+                    {
+                      case EbtFloat:
+                        tempConstArray[i].setFConst(std::max(unionArrays[0][i].getFConst(), unionArrays[1][i].getFConst()));
+                        break;
+                      case EbtInt:
+                        tempConstArray[i].setIConst(std::max(unionArrays[0][i].getIConst(), unionArrays[1][i].getIConst()));
+                        break;
+                      case EbtUInt:
+                        tempConstArray[i].setUConst(std::max(unionArrays[0][i].getUConst(), unionArrays[1][i].getUConst()));
+                        break;
+                      default:
+                        UNREACHABLE();
+                        break;
+                    }
+                }
+            }
+            break;
+
+          default:
+            UNREACHABLE();
+            // TODO: Add constant folding support for other built-in operations that take 2 parameters and not handled above.
+            return nullptr;
+        }
+    }
+    else if (paramsCount == 3)
+    {
+        //
+        // Ternary built-in
+        //
+        switch (op)
+        {
+          case EOpClamp:
+            {
+                tempConstArray = new TConstantUnion[maxObjectSize];
+                for (size_t i = 0; i < maxObjectSize; i++)
+                {
+                    switch (basicType)
+                    {
+                      case EbtFloat:
+                        {
+                            float x = unionArrays[0][i].getFConst();
+                            float min = unionArrays[1][i].getFConst();
+                            float max = unionArrays[2][i].getFConst();
+                            // Results are undefined if min > max.
+                            if (min > max)
+                                tempConstArray[i].setFConst(0.0f);
+                            else
+                                tempConstArray[i].setFConst(gl::clamp(x, min, max));
+                        }
+                        break;
+                      case EbtInt:
+                        {
+                            int x = unionArrays[0][i].getIConst();
+                            int min = unionArrays[1][i].getIConst();
+                            int max = unionArrays[2][i].getIConst();
+                            // Results are undefined if min > max.
+                            if (min > max)
+                                tempConstArray[i].setIConst(0);
+                            else
+                                tempConstArray[i].setIConst(gl::clamp(x, min, max));
+                        }
+                        break;
+                      case EbtUInt:
+                        {
+                            unsigned int x = unionArrays[0][i].getUConst();
+                            unsigned int min = unionArrays[1][i].getUConst();
+                            unsigned int max = unionArrays[2][i].getUConst();
+                            // Results are undefined if min > max.
+                            if (min > max)
+                                tempConstArray[i].setUConst(0u);
+                            else
+                                tempConstArray[i].setUConst(gl::clamp(x, min, max));
+                        }
+                        break;
+                      default:
+                        UNREACHABLE();
+                        break;
+                    }
+                }
+            }
+            break;
+
+          default:
+            UNREACHABLE();
+            // TODO: Add constant folding support for other built-in operations that take 3 parameters and not handled above.
+            return nullptr;
+        }
+    }
+
+    if (tempConstArray)
+    {
+        tempNode = new TIntermConstantUnion(tempConstArray, returnType);
+        tempNode->setLine(loc);
+    }
+    return tempNode;
+}
+
+// static
 TString TIntermTraverser::hash(const TString &name, ShHashFunction64 hashFunction)
 {
     if (hashFunction == NULL || name.empty())