Add ESSL 3.10 ldexp/frexp builtins

This adds new built-ins found in ESSL 3.10 section 8.3 Common
Functions.

This includes constant folding support for ldexp and support for both
GLSL and HLSL output. In HLSL these functions need to be emulated.

BUG=angleproject:1730
TEST=angle_unittests

Change-Id: I1330e69978b0cf53efbc3416150194764414e96c
Reviewed-on: https://chromium-review.googlesource.com/435342
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/common/mathutil.h b/src/common/mathutil.h
index 436fd22..9914964 100644
--- a/src/common/mathutil.h
+++ b/src/common/mathutil.h
@@ -614,6 +614,22 @@
     size_t vertexIndexCount;
 };
 
+// Combine a floating-point value representing a mantissa (x) and an integer exponent (exp) into a
+// floating-point value. As in GLSL ldexp() built-in.
+inline float Ldexp(float x, int exp)
+{
+    if (exp > 128)
+    {
+        return std::numeric_limits<float>::infinity();
+    }
+    if (exp < -126)
+    {
+        return 0.0f;
+    }
+    double result = static_cast<double>(x) * std::pow(2.0, static_cast<double>(exp));
+    return static_cast<float>(result);
+}
+
 // First, both normalized floating-point values are converted into 16-bit integer values.
 // Then, the results are packed into the returned 32-bit unsigned integer.
 // The first float value will be written to the least significant bits of the output;
diff --git a/src/common/mathutil_unittest.cpp b/src/common/mathutil_unittest.cpp
index 3f3ded9..ed868a9 100644
--- a/src/common/mathutil_unittest.cpp
+++ b/src/common/mathutil_unittest.cpp
@@ -310,4 +310,13 @@
     EXPECT_EQ(31, gl::FindMSB(0x80000000u));
 }
 
+// Test Ldexp, which combines mantissa and exponent into a floating-point number.
+TEST(MathUtilTest, Ldexp)
+{
+    EXPECT_EQ(2.5f, Ldexp(0.625f, 2));
+    EXPECT_EQ(-5.0f, Ldexp(-0.625f, 3));
+    EXPECT_EQ(std::numeric_limits<float>::infinity(), Ldexp(0.625f, 129));
+    EXPECT_EQ(0.0f, Ldexp(1.0f, -129));
+}
+
 }  // anonymous namespace
diff --git a/src/compiler/translator/BuiltInFunctionEmulatorHLSL.cpp b/src/compiler/translator/BuiltInFunctionEmulatorHLSL.cpp
index 3f8acde..6d4ace3 100644
--- a/src/compiler/translator/BuiltInFunctionEmulatorHLSL.cpp
+++ b/src/compiler/translator/BuiltInFunctionEmulatorHLSL.cpp
@@ -126,6 +126,68 @@
                              "}\n"
                              "\n");
 
+    emu->addEmulatedFunction(EOpFrexp, float1, int1,
+                             "float webgl_frexp_emu(float x, out int exp)\n"
+                             "{\n"
+                             "    float fexp;\n"
+                             "    float mantissa = frexp(abs(x), fexp) * sign(x);\n"
+                             "    exp = int(fexp);\n"
+                             "    return mantissa;\n"
+                             "}\n"
+                             "\n");
+    emu->addEmulatedFunction(EOpFrexp, float2, int2,
+                             "float2 webgl_frexp_emu(float2 x, out int2 exp)\n"
+                             "{\n"
+                             "    float2 fexp;\n"
+                             "    float2 mantissa = frexp(abs(x), fexp) * sign(x);\n"
+                             "    exp = int2(fexp);\n"
+                             "    return mantissa;\n"
+                             "}\n"
+                             "\n");
+    emu->addEmulatedFunction(EOpFrexp, float3, int3,
+                             "float3 webgl_frexp_emu(float3 x, out int3 exp)\n"
+                             "{\n"
+                             "    float3 fexp;\n"
+                             "    float3 mantissa = frexp(abs(x), fexp) * sign(x);\n"
+                             "    exp = int3(fexp);\n"
+                             "    return mantissa;\n"
+                             "}\n"
+                             "\n");
+    emu->addEmulatedFunction(EOpFrexp, float4, int4,
+                             "float4 webgl_frexp_emu(float4 x, out int4 exp)\n"
+                             "{\n"
+                             "    float4 fexp;\n"
+                             "    float4 mantissa = frexp(abs(x), fexp) * sign(x);\n"
+                             "    exp = int4(fexp);\n"
+                             "    return mantissa;\n"
+                             "}\n"
+                             "\n");
+
+    emu->addEmulatedFunction(EOpLdexp, float1, int1,
+                             "float webgl_ldexp_emu(float x, int exp)\n"
+                             "{\n"
+                             "    return ldexp(x, float(exp));\n"
+                             "}\n"
+                             "\n");
+    emu->addEmulatedFunction(EOpLdexp, float2, int2,
+                             "float2 webgl_ldexp_emu(float2 x, int2 exp)\n"
+                             "{\n"
+                             "    return ldexp(x, float2(exp));\n"
+                             "}\n"
+                             "\n");
+    emu->addEmulatedFunction(EOpLdexp, float3, int3,
+                             "float3 webgl_ldexp_emu(float3 x, int3 exp)\n"
+                             "{\n"
+                             "    return ldexp(x, float3(exp));\n"
+                             "}\n"
+                             "\n");
+    emu->addEmulatedFunction(EOpLdexp, float4, int4,
+                             "float4 webgl_ldexp_emu(float4 x, int4 exp)\n"
+                             "{\n"
+                             "    return ldexp(x, float4(exp));\n"
+                             "}\n"
+                             "\n");
+
     emu->addEmulatedFunction(EOpFaceForward, float1, float1, float1,
                              "float webgl_faceforward_emu(float N, float I, float Nref)\n"
                              "{\n"
diff --git a/src/compiler/translator/Initialize.cpp b/src/compiler/translator/Initialize.cpp
index 8a6a63a..1bad320 100644
--- a/src/compiler/translator/Initialize.cpp
+++ b/src/compiler/translator/Initialize.cpp
@@ -111,6 +111,7 @@
     symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpSmoothStep, genType, float1, float1, genType);
 
     const TType *outGenType = TCache::getType(EbtGenType, EvqOut);
+    const TType *outGenIType = TCache::getType(EbtGenIType, EvqOut);
 
     symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpModf, genType, genType, outGenType);
 
@@ -121,6 +122,9 @@
     symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpIntBitsToFloat, genType, genIType);
     symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpUintBitsToFloat, genType, genUType);
 
+    symbolTable.insertBuiltInOp(ESSL3_1_BUILTINS, EOpFrexp, genType, genType, outGenIType);
+    symbolTable.insertBuiltInOp(ESSL3_1_BUILTINS, EOpLdexp, genType, genType, genIType);
+
     symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpPackSnorm2x16, uint1, float2);
     symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpPackUnorm2x16, uint1, float2);
     symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpPackHalf2x16, uint1, float2);
@@ -234,7 +238,6 @@
     // Integer functions
     //
     const TType *outGenUType = TCache::getType(EbtGenUType, EvqOut);
-    const TType *outGenIType = TCache::getType(EbtGenIType, EvqOut);
 
     symbolTable.insertBuiltInOp(ESSL3_1_BUILTINS, EOpBitfieldExtract, genIType, genIType, int1,
                                 int1);
diff --git a/src/compiler/translator/IntermNode.cpp b/src/compiler/translator/IntermNode.cpp
index 4039305..1401af7 100644
--- a/src/compiler/translator/IntermNode.cpp
+++ b/src/compiler/translator/IntermNode.cpp
@@ -3012,6 +3012,25 @@
             break;
         }
 
+        case EOpLdexp:
+        {
+            resultArray = new TConstantUnion[maxObjectSize];
+            for (size_t i = 0; i < maxObjectSize; i++)
+            {
+                float x = unionArrays[0][i].getFConst();
+                int exp = unionArrays[1][i].getIConst();
+                if (exp > 128)
+                {
+                    UndefinedConstantFoldingError(loc, op, basicType, diagnostics, &resultArray[i]);
+                }
+                else
+                {
+                    resultArray[i].setFConst(gl::Ldexp(x, exp));
+                }
+            }
+            break;
+        }
+
         case EOpFaceForward:
         {
             ASSERT(basicType == EbtFloat);
diff --git a/src/compiler/translator/Intermediate.cpp b/src/compiler/translator/Intermediate.cpp
index 084df31..0b38eb5 100644
--- a/src/compiler/translator/Intermediate.cpp
+++ b/src/compiler/translator/Intermediate.cpp
@@ -273,6 +273,7 @@
         case EOpMix:
         case EOpStep:
         case EOpSmoothStep:
+        case EOpLdexp:
         case EOpMulMatrixComponentWise:
         case EOpOuterProduct:
         case EOpEqualComponentWise:
diff --git a/src/compiler/translator/Operator.cpp b/src/compiler/translator/Operator.cpp
index 64e8052..fc2a981 100644
--- a/src/compiler/translator/Operator.cpp
+++ b/src/compiler/translator/Operator.cpp
@@ -197,6 +197,11 @@
         case EOpUintBitsToFloat:
             return "uintBitsToFloat";
 
+        case EOpFrexp:
+            return "frexp";
+        case EOpLdexp:
+            return "ldexp";
+
         case EOpPackSnorm2x16:
             return "packSnorm2x16";
         case EOpPackUnorm2x16:
diff --git a/src/compiler/translator/Operator.h b/src/compiler/translator/Operator.h
index 18e7ca5..83b957d 100644
--- a/src/compiler/translator/Operator.h
+++ b/src/compiler/translator/Operator.h
@@ -143,6 +143,9 @@
     EOpIntBitsToFloat,
     EOpUintBitsToFloat,
 
+    EOpFrexp,
+    EOpLdexp,
+
     EOpPackSnorm2x16,
     EOpPackUnorm2x16,
     EOpPackHalf2x16,
diff --git a/src/compiler/translator/OutputGLSLBase.cpp b/src/compiler/translator/OutputGLSLBase.cpp
index 4ae8651..7831b33 100644
--- a/src/compiler/translator/OutputGLSLBase.cpp
+++ b/src/compiler/translator/OutputGLSLBase.cpp
@@ -937,6 +937,8 @@
         case EOpMix:
         case EOpStep:
         case EOpSmoothStep:
+        case EOpFrexp:
+        case EOpLdexp:
         case EOpDistance:
         case EOpDot:
         case EOpCross:
diff --git a/src/compiler/translator/OutputHLSL.cpp b/src/compiler/translator/OutputHLSL.cpp
index df0fa9f..60dcfd3 100644
--- a/src/compiler/translator/OutputHLSL.cpp
+++ b/src/compiler/translator/OutputHLSL.cpp
@@ -1993,6 +1993,11 @@
         case EOpSmoothStep:
             outputTriplet(out, visit, "smoothstep(", ", ", ")");
             break;
+        case EOpFrexp:
+        case EOpLdexp:
+            ASSERT(node->getUseEmulatedFunction());
+            writeEmulatedFunctionTriplet(out, visit, node->getOp());
+            break;
         case EOpDistance:
             outputTriplet(out, visit, "distance(", ", ", ")");
             break;
diff --git a/src/tests/compiler_tests/ConstantFolding_test.cpp b/src/tests/compiler_tests/ConstantFolding_test.cpp
index 9bfce5b..0f67362 100644
--- a/src/tests/compiler_tests/ConstantFolding_test.cpp
+++ b/src/tests/compiler_tests/ConstantFolding_test.cpp
@@ -1409,3 +1409,11 @@
     evaluateFloat(floatString);
     ASSERT_TRUE(constantFoundInAST(123.0f / 255.0f));
 }
+
+// Test that ldexp is folded correctly.
+TEST_F(ConstantFoldingExpressionTest, FoldLdexp)
+{
+    const std::string &floatString = "ldexp(0.625, 1)";
+    evaluateFloat(floatString);
+    ASSERT_TRUE(constantFoundInAST(1.25f));
+}
diff --git a/src/tests/deqp_support/deqp_gles31_test_expectations.txt b/src/tests/deqp_support/deqp_gles31_test_expectations.txt
index 40646c3..4b49360 100644
--- a/src/tests/deqp_support/deqp_gles31_test_expectations.txt
+++ b/src/tests/deqp_support/deqp_gles31_test_expectations.txt
@@ -368,77 +368,29 @@
 1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.floatbitstouint.vec4_lowp_compute = FAIL
 1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.floatbitstouint.vec4_mediump_compute = FAIL
 1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.floatbitstouint.vec4_highp_compute = FAIL
-1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.frexp.float_lowp_vertex = FAIL
-1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.frexp.float_lowp_fragment = FAIL
 1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.frexp.float_lowp_compute = FAIL
-1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.frexp.float_mediump_vertex = FAIL
-1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.frexp.float_mediump_fragment = FAIL
 1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.frexp.float_mediump_compute = FAIL
-1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.frexp.float_highp_vertex = FAIL
-1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.frexp.float_highp_fragment = FAIL
 1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.frexp.float_highp_compute = FAIL
-1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.frexp.vec2_lowp_vertex = FAIL
-1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.frexp.vec2_lowp_fragment = FAIL
 1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.frexp.vec2_lowp_compute = FAIL
-1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.frexp.vec2_mediump_vertex = FAIL
-1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.frexp.vec2_mediump_fragment = FAIL
 1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.frexp.vec2_mediump_compute = FAIL
-1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.frexp.vec2_highp_vertex = FAIL
-1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.frexp.vec2_highp_fragment = FAIL
 1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.frexp.vec2_highp_compute = FAIL
-1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.frexp.vec3_lowp_vertex = FAIL
-1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.frexp.vec3_lowp_fragment = FAIL
 1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.frexp.vec3_lowp_compute = FAIL
-1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.frexp.vec3_mediump_vertex = FAIL
-1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.frexp.vec3_mediump_fragment = FAIL
 1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.frexp.vec3_mediump_compute = FAIL
-1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.frexp.vec3_highp_vertex = FAIL
-1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.frexp.vec3_highp_fragment = FAIL
 1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.frexp.vec3_highp_compute = FAIL
-1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.frexp.vec4_lowp_vertex = FAIL
-1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.frexp.vec4_lowp_fragment = FAIL
 1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.frexp.vec4_lowp_compute = FAIL
-1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.frexp.vec4_mediump_vertex = FAIL
-1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.frexp.vec4_mediump_fragment = FAIL
 1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.frexp.vec4_mediump_compute = FAIL
-1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.frexp.vec4_highp_vertex = FAIL
-1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.frexp.vec4_highp_fragment = FAIL
 1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.frexp.vec4_highp_compute = FAIL
-1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.ldexp.float_lowp_vertex = FAIL
-1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.ldexp.float_lowp_fragment = FAIL
 1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.ldexp.float_lowp_compute = FAIL
-1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.ldexp.float_mediump_vertex = FAIL
-1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.ldexp.float_mediump_fragment = FAIL
 1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.ldexp.float_mediump_compute = FAIL
-1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.ldexp.float_highp_vertex = FAIL
-1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.ldexp.float_highp_fragment = FAIL
 1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.ldexp.float_highp_compute = FAIL
-1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.ldexp.vec2_lowp_vertex = FAIL
-1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.ldexp.vec2_lowp_fragment = FAIL
 1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.ldexp.vec2_lowp_compute = FAIL
-1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.ldexp.vec2_mediump_vertex = FAIL
-1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.ldexp.vec2_mediump_fragment = FAIL
 1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.ldexp.vec2_mediump_compute = FAIL
-1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.ldexp.vec2_highp_vertex = FAIL
-1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.ldexp.vec2_highp_fragment = FAIL
 1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.ldexp.vec2_highp_compute = FAIL
-1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.ldexp.vec3_lowp_vertex = FAIL
-1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.ldexp.vec3_lowp_fragment = FAIL
 1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.ldexp.vec3_lowp_compute = FAIL
-1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.ldexp.vec3_mediump_vertex = FAIL
-1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.ldexp.vec3_mediump_fragment = FAIL
 1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.ldexp.vec3_mediump_compute = FAIL
-1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.ldexp.vec3_highp_vertex = FAIL
-1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.ldexp.vec3_highp_fragment = FAIL
 1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.ldexp.vec3_highp_compute = FAIL
-1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.ldexp.vec4_lowp_vertex = FAIL
-1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.ldexp.vec4_lowp_fragment = FAIL
 1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.ldexp.vec4_lowp_compute = FAIL
-1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.ldexp.vec4_mediump_vertex = FAIL
-1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.ldexp.vec4_mediump_fragment = FAIL
 1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.ldexp.vec4_mediump_compute = FAIL
-1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.ldexp.vec4_highp_vertex = FAIL
-1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.ldexp.vec4_highp_fragment = FAIL
 1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.ldexp.vec4_highp_compute = FAIL
 1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.intbitstofloat.int_highp_compute = FAIL
 1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.builtin_functions.common.intbitstofloat.ivec2_highp_compute = FAIL