Handle constant folding arithmetic involving infinity

Constant folding arithmetic operations that involve infinity are now
handled correctly in the cases where the result is infinity or zero.
The implementation mostly relies on C++ to implement IEEE float
arithmetic correctly so that unnecessary overhead is avoided.

Constant folding arithmetic operations that result in overflow now
issue a warning but result in infinity. This is not mandated by the
spec but is a reasonable choice since it is the behavior of the
default IEEE rounding mode.

Constant folding arithmetic operations that result in NaN in IEEE will
generate a warning but the NaN is kept. This is also not mandated by
the spec, but is among the allowed behaviors.

There's no special handling for ESSL 1.00. ESSL 1.00 doesn't really
have the concept of NaN, but since it is not feasible to control
generating NaNs at shader run time either way, it should not be a big
issue if constant folding may generate them as well.

TEST=angle_unittests
BUG=chromium:661857

Change-Id: I06116c6fdd02f224939d4a651e4e62f2fd4c98a8
Reviewed-on: https://chromium-review.googlesource.com/414911
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
diff --git a/src/compiler/translator/IntermNode.cpp b/src/compiler/translator/IntermNode.cpp
index b91b43e..7a2184f 100644
--- a/src/compiler/translator/IntermNode.cpp
+++ b/src/compiler/translator/IntermNode.cpp
@@ -1312,19 +1312,51 @@
                 switch (getType().getBasicType())
                 {
                   case EbtFloat:
-                    if (rightArray[i] == 0.0f)
-                    {
-                        diagnostics->warning(
-                            getLine(), "Divide by zero error during constant folding", "/", "");
-                        resultArray[i].setFConst(leftArray[i].getFConst() < 0 ? -FLT_MAX : FLT_MAX);
-                    }
-                    else
-                    {
-                        ASSERT(op == EOpDiv);
-                        resultArray[i].setFConst(leftArray[i].getFConst() / rightArray[i].getFConst());
-                    }
-                    break;
-
+                  {
+                      ASSERT(op == EOpDiv);
+                      float dividend = leftArray[i].getFConst();
+                      float divisor  = rightArray[i].getFConst();
+                      if (divisor == 0.0f)
+                      {
+                          if (dividend == 0.0f)
+                          {
+                              diagnostics->warning(
+                                  getLine(),
+                                  "Zero divided by zero during constant folding generated NaN", "/",
+                                  "");
+                              resultArray[i].setFConst(std::numeric_limits<float>::quiet_NaN());
+                          }
+                          else
+                          {
+                              diagnostics->warning(
+                                  getLine(), "Divide by zero during constant folding", "/", "");
+                              bool negativeResult = std::signbit(dividend) != std::signbit(divisor);
+                              resultArray[i].setFConst(
+                                  negativeResult ? -std::numeric_limits<float>::infinity()
+                                                 : std::numeric_limits<float>::infinity());
+                          }
+                      }
+                      else if (gl::isInf(dividend) && gl::isInf(divisor))
+                      {
+                          diagnostics->warning(
+                              getLine(),
+                              "Infinity divided by infinity during constant folding generated NaN",
+                              "/", "");
+                          resultArray[i].setFConst(std::numeric_limits<float>::quiet_NaN());
+                      }
+                      else
+                      {
+                          float result = dividend / divisor;
+                          if (!gl::isInf(dividend) && gl::isInf(result))
+                          {
+                              diagnostics->warning(
+                                  getLine(), "Constant folded division overflowed to infinity", "/",
+                                  "");
+                          }
+                          resultArray[i].setFConst(result);
+                      }
+                      break;
+                  }
                   case EbtInt:
                     if (rightArray[i] == 0)
                     {