Support constant folding of trigonometry built-ins

This change adds constant folding support for trigonometry built-in
functions. Constant folding for these functions also fixes constant
expression issues where constant initializer is a built-in trignometry
function whose arguments are all constant expressions.

BUG=angleproject:913
TEST= dEQP tests
dEQP-GLES3.functional.shaders.constant_expressions.builtin_functions.angle_and_trigonometry*
(112 out of 120 tests pass with this change)

Change-Id: I66275b2ae9faecef63d76763d21a9b67d9bb68fa
Reviewed-on: https://chromium-review.googlesource.com/265392
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 708dcb3..c7fea1c 100644
--- a/src/compiler/translator/IntermNode.cpp
+++ b/src/compiler/translator/IntermNode.cpp
@@ -10,6 +10,7 @@
 
 #include <float.h>
 #include <limits.h>
+#include <math.h>
 #include <algorithm>
 
 #include "compiler/translator/HashNames.h"
@@ -19,6 +20,10 @@
 namespace
 {
 
+const float kPi = 3.14159265358979323846f;
+const float kDegreesToRadiansMultiplier = kPi / 180.0f;
+const float kRadiansToDegreesMultiplier = 180.0f / kPi;
+
 TPrecision GetHigherPrecision(TPrecision left, TPrecision right)
 {
     return left > right ? left : right;
@@ -1185,6 +1190,100 @@
                 }
                 break;
 
+              case EOpRadians:
+                if (getType().getBasicType() == EbtFloat)
+                {
+                    tempConstArray[i].setFConst(kDegreesToRadiansMultiplier * unionArray[i].getFConst());
+                    break;
+                }
+                infoSink.info.message(
+                    EPrefixInternalError, getLine(),
+                    "Unary operation not folded into constant");
+                return nullptr;
+
+              case EOpDegrees:
+                if (getType().getBasicType() == EbtFloat)
+                {
+                    tempConstArray[i].setFConst(kRadiansToDegreesMultiplier * unionArray[i].getFConst());
+                    break;
+                }
+                infoSink.info.message(
+                    EPrefixInternalError, getLine(),
+                    "Unary operation not folded into constant");
+                return nullptr;
+
+              case EOpSin:
+                if (!foldFloatTypeUnary(unionArray[i], static_cast<FloatTypeUnaryFunc>(&std::sin), infoSink, &tempConstArray[i]))
+                   return nullptr;
+                break;
+
+              case EOpCos:
+                if (!foldFloatTypeUnary(unionArray[i], static_cast<FloatTypeUnaryFunc>(&std::cos), infoSink, &tempConstArray[i]))
+                    return nullptr;
+                break;
+
+              case EOpTan:
+                if (!foldFloatTypeUnary(unionArray[i], static_cast<FloatTypeUnaryFunc>(&std::tan), infoSink, &tempConstArray[i]))
+                    return nullptr;
+                break;
+
+              case EOpAsin:
+                // For asin(x), results are undefined if |x| > 1, we are choosing to set result to 0.
+                if (getType().getBasicType() == EbtFloat && std::abs(unionArray[i].getFConst()) > 1.0f)
+                    tempConstArray[i].setFConst(0.0f);
+                else if (!foldFloatTypeUnary(unionArray[i], static_cast<FloatTypeUnaryFunc>(&std::asin), infoSink, &tempConstArray[i]))
+                    return nullptr;
+                break;
+
+              case EOpAcos:
+                // For acos(x), results are undefined if |x| > 1, we are choosing to set result to 0.
+                if (getType().getBasicType() == EbtFloat && std::abs(unionArray[i].getFConst()) > 1.0f)
+                    tempConstArray[i].setFConst(0.0f);
+                else if (!foldFloatTypeUnary(unionArray[i], static_cast<FloatTypeUnaryFunc>(&std::acos), infoSink, &tempConstArray[i]))
+                    return nullptr;
+                break;
+
+              case EOpAtan:
+                if (!foldFloatTypeUnary(unionArray[i], static_cast<FloatTypeUnaryFunc>(&std::atan), infoSink, &tempConstArray[i]))
+                    return nullptr;
+                break;
+
+              case EOpSinh:
+                if (!foldFloatTypeUnary(unionArray[i], static_cast<FloatTypeUnaryFunc>(&std::sinh), infoSink, &tempConstArray[i]))
+                    return nullptr;
+                break;
+
+              case EOpCosh:
+                if (!foldFloatTypeUnary(unionArray[i], static_cast<FloatTypeUnaryFunc>(&std::cosh), infoSink, &tempConstArray[i]))
+                    return nullptr;
+                break;
+
+              case EOpTanh:
+                if (!foldFloatTypeUnary(unionArray[i], static_cast<FloatTypeUnaryFunc>(&std::tanh), infoSink, &tempConstArray[i]))
+                    return nullptr;
+                break;
+
+              case EOpAsinh:
+                if (!foldFloatTypeUnary(unionArray[i], static_cast<FloatTypeUnaryFunc>(&std::asinh), infoSink, &tempConstArray[i]))
+                    return nullptr;
+                break;
+
+              case EOpAcosh:
+                // For acosh(x), results are undefined if x < 1, we are choosing to set result to 0.
+                if (getType().getBasicType() == EbtFloat && unionArray[i].getFConst() < 1.0f)
+                    tempConstArray[i].setFConst(0.0f);
+                else if (!foldFloatTypeUnary(unionArray[i], static_cast<FloatTypeUnaryFunc>(&std::acosh), infoSink, &tempConstArray[i]))
+                    return nullptr;
+                break;
+
+              case EOpAtanh:
+                // For atanh(x), results are undefined if |x| >= 1, we are choosing to set result to 0.
+                if (getType().getBasicType() == EbtFloat && std::abs(unionArray[i].getFConst()) >= 1.0f)
+                    tempConstArray[i].setFConst(0.0f);
+                else if (!foldFloatTypeUnary(unionArray[i], static_cast<FloatTypeUnaryFunc>(&std::atanh), infoSink, &tempConstArray[i]))
+                    return nullptr;
+                break;
+
               default:
                 return NULL;
             }
@@ -1195,6 +1294,23 @@
     }
 }
 
+bool TIntermConstantUnion::foldFloatTypeUnary(const ConstantUnion &parameter, FloatTypeUnaryFunc builtinFunc,
+                                              TInfoSink &infoSink, ConstantUnion *result) const
+{
+    ASSERT(builtinFunc);
+
+    if (getType().getBasicType() == EbtFloat)
+    {
+        result->setFConst(builtinFunc(parameter.getFConst()));
+        return true;
+    }
+
+    infoSink.info.message(
+        EPrefixInternalError, getLine(),
+        "Unary operation not folded into constant");
+    return false;
+}
+
 // static
 TString TIntermTraverser::hash(const TString &name, ShHashFunction64 hashFunction)
 {