Edit

kc3-lang/angle/src/tests/compiler_tests/ConstantFolding_test.cpp

Branch :

  • Show log

    Commit

  • Author : Olli Etuaho
    Date : 2017-02-01 15:37:48
    Hash : 74da73fe
    Message : 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>

  • src/tests/compiler_tests/ConstantFolding_test.cpp
  • //
    // Copyright (c) 2015 The ANGLE Project Authors. All rights reserved.
    // Use of this source code is governed by a BSD-style license that can be
    // found in the LICENSE file.
    //
    // ConstantFolding_test.cpp:
    //   Tests for constant folding
    //
    
    #include "tests/test_utils/ConstantFoldingTest.h"
    
    using namespace sh;
    
    // Test that zero, true or false are not found in AST when they are not expected. This is to make
    // sure that the subsequent tests run correctly.
    TEST_F(ConstantFoldingExpressionTest, FoldFloatTestSanityCheck)
    {
        const std::string &floatString = "1.0";
        evaluateFloat(floatString);
        ASSERT_FALSE(constantFoundInAST(0.0f));
        ASSERT_FALSE(constantFoundInAST(true));
        ASSERT_FALSE(constantFoundInAST(false));
    }
    
    TEST_F(ConstantFoldingTest, FoldIntegerAdd)
    {
        const std::string &shaderString =
            "#version 300 es\n"
            "precision mediump float;\n"
            "out int my_Int;\n"
            "void main() {\n"
            "   const int i = 1124 + 5;\n"
            "   my_Int = i;\n"
            "}\n";
        compileAssumeSuccess(shaderString);
        ASSERT_FALSE(constantFoundInAST(1124));
        ASSERT_FALSE(constantFoundInAST(5));
        ASSERT_TRUE(constantFoundInAST(1129));
    }
    
    TEST_F(ConstantFoldingTest, FoldIntegerSub)
    {
        const std::string &shaderString =
            "#version 300 es\n"
            "precision mediump float;\n"
            "out int my_Int;\n"
            "void main() {\n"
            "   const int i = 1124 - 5;\n"
            "   my_Int = i;\n"
            "}\n";
        compileAssumeSuccess(shaderString);
        ASSERT_FALSE(constantFoundInAST(1124));
        ASSERT_FALSE(constantFoundInAST(5));
        ASSERT_TRUE(constantFoundInAST(1119));
    }
    
    TEST_F(ConstantFoldingTest, FoldIntegerMul)
    {
        const std::string &shaderString =
            "#version 300 es\n"
            "precision mediump float;\n"
            "out int my_Int;\n"
            "void main() {\n"
            "   const int i = 1124 * 5;\n"
            "   my_Int = i;\n"
            "}\n";
        compileAssumeSuccess(shaderString);
        ASSERT_FALSE(constantFoundInAST(1124));
        ASSERT_FALSE(constantFoundInAST(5));
        ASSERT_TRUE(constantFoundInAST(5620));
    }
    
    TEST_F(ConstantFoldingTest, FoldIntegerDiv)
    {
        const std::string &shaderString =
            "#version 300 es\n"
            "precision mediump float;\n"
            "out int my_Int;\n"
            "void main() {\n"
            "   const int i = 1124 / 5;\n"
            "   my_Int = i;\n"
            "}\n";
        compileAssumeSuccess(shaderString);
        ASSERT_FALSE(constantFoundInAST(1124));
        ASSERT_FALSE(constantFoundInAST(5));
        // Rounding mode of division is undefined in the spec but ANGLE can be expected to round down.
        ASSERT_TRUE(constantFoundInAST(224));
    }
    
    TEST_F(ConstantFoldingTest, FoldIntegerModulus)
    {
        const std::string &shaderString =
            "#version 300 es\n"
            "precision mediump float;\n"
            "out int my_Int;\n"
            "void main() {\n"
            "   const int i = 1124 % 5;\n"
            "   my_Int = i;\n"
            "}\n";
        compileAssumeSuccess(shaderString);
        ASSERT_FALSE(constantFoundInAST(1124));
        ASSERT_FALSE(constantFoundInAST(5));
        ASSERT_TRUE(constantFoundInAST(4));
    }
    
    TEST_F(ConstantFoldingTest, FoldVectorCrossProduct)
    {
        const std::string &shaderString =
            "#version 300 es\n"
            "precision mediump float;\n"
            "out vec3 my_Vec3;"
            "void main() {\n"
            "   const vec3 v3 = cross(vec3(1.0f, 1.0f, 1.0f), vec3(1.0f, -1.0f, 1.0f));\n"
            "   my_Vec3 = v3;\n"
            "}\n";
        compileAssumeSuccess(shaderString);
        std::vector<float> input1(3, 1.0f);
        ASSERT_FALSE(constantVectorFoundInAST(input1));
        std::vector<float> input2;
        input2.push_back(1.0f);
        input2.push_back(-1.0f);
        input2.push_back(1.0f);
        ASSERT_FALSE(constantVectorFoundInAST(input2));
        std::vector<float> result;
        result.push_back(2.0f);
        result.push_back(0.0f);
        result.push_back(-2.0f);
        ASSERT_TRUE(constantVectorFoundInAST(result));
    }
    
    // FoldMxNMatrixInverse tests check if the matrix 'inverse' operation
    // on MxN matrix is constant folded when argument is constant expression and also
    // checks the correctness of the result returned by the constant folding operation.
    // All the matrices including matrices in the shader code are in column-major order.
    TEST_F(ConstantFoldingTest, Fold2x2MatrixInverse)
    {
        const std::string &shaderString =
            "#version 300 es\n"
            "precision mediump float;\n"
            "in float i;\n"
            "out vec2 my_Vec;\n"
            "void main() {\n"
            "   const mat2 m2 = inverse(mat2(2.0f, 3.0f,\n"
            "                                5.0f, 7.0f));\n"
            "   mat2 m = m2 * mat2(i);\n"
            "   my_Vec = m[0];\n"
            "}\n";
        compileAssumeSuccess(shaderString);
        float inputElements[] =
        {
            2.0f, 3.0f,
            5.0f, 7.0f
        };
        std::vector<float> input(inputElements, inputElements + 4);
        ASSERT_FALSE(constantColumnMajorMatrixFoundInAST(input));
        float outputElements[] =
        {
            -7.0f, 3.0f,
            5.0f, -2.0f
        };
        std::vector<float> result(outputElements, outputElements + 4);
        ASSERT_TRUE(constantColumnMajorMatrixFoundInAST(result));
    }
    
    // Check if the matrix 'inverse' operation on 3x3 matrix is constant folded.
    TEST_F(ConstantFoldingTest, Fold3x3MatrixInverse)
    {
        const std::string &shaderString =
            "#version 300 es\n"
            "precision mediump float;\n"
            "in float i;\n"
            "out vec3 my_Vec;\n"
            "void main() {\n"
            "   const mat3 m3 = inverse(mat3(11.0f, 13.0f, 19.0f,\n"
            "                                23.0f, 29.0f, 31.0f,\n"
            "                                37.0f, 41.0f, 43.0f));\n"
            "   mat3 m = m3 * mat3(i);\n"
            "   my_Vec = m3[0];\n"
            "}\n";
        compileAssumeSuccess(shaderString);
        float inputElements[] =
        {
            11.0f, 13.0f, 19.0f,
            23.0f, 29.0f, 31.0f,
            37.0f, 41.0f, 43.0f
        };
        std::vector<float> input(inputElements, inputElements + 9);
        ASSERT_FALSE(constantVectorFoundInAST(input));
        float outputElements[] =
        {
            3.0f / 85.0f, -11.0f / 34.0f, 37.0f / 170.0f,
            -79.0f / 340.0f, 23.0f / 68.0f, -12.0f / 85.0f,
            13.0f / 68.0f, -3.0f / 68.0f, -1.0f / 34.0f
        };
        std::vector<float> result(outputElements, outputElements + 9);
        const float floatFaultTolerance = 0.000001f;
        ASSERT_TRUE(constantVectorNearFoundInAST(result, floatFaultTolerance));
    }
    
    // Check if the matrix 'inverse' operation on 4x4 matrix is constant folded.
    TEST_F(ConstantFoldingTest, Fold4x4MatrixInverse)
    {
        const std::string &shaderString =
            "#version 300 es\n"
            "precision mediump float;\n"
            "in float i;\n"
            "out vec4 my_Vec;\n"
            "void main() {\n"
            "   const mat4 m4 = inverse(mat4(29.0f, 31.0f, 37.0f, 41.0f,\n"
            "                                43.0f, 47.0f, 53.0f, 59.0f,\n"
            "                                61.0f, 67.0f, 71.0f, 73.0f,\n"
            "                                79.0f, 83.0f, 89.0f, 97.0f));\n"
            "   mat4 m = m4 * mat4(i);\n"
            "   my_Vec = m[0];\n"
            "}\n";
        compileAssumeSuccess(shaderString);
        float inputElements[] =
        {
            29.0f, 31.0f, 37.0f, 41.0f,
            43.0f, 47.0f, 53.0f, 59.0f,
            61.0f, 67.0f, 71.0f, 73.0f,
            79.0f, 83.0f, 89.0f, 97.0f
        };
        std::vector<float> input(inputElements, inputElements + 16);
        ASSERT_FALSE(constantVectorFoundInAST(input));
        float outputElements[] =
        {
            43.0f / 126.0f, -11.0f / 21.0f, -2.0f / 21.0f, 31.0f / 126.0f,
            -5.0f / 7.0f, 9.0f / 14.0f, 1.0f / 14.0f, -1.0f / 7.0f,
            85.0f / 126.0f, -11.0f / 21.0f, 43.0f / 210.0f, -38.0f / 315.0f,
            -2.0f / 7.0f, 5.0f / 14.0f, -6.0f / 35.0f, 3.0f / 70.0f
        };
        std::vector<float> result(outputElements, outputElements + 16);
        const float floatFaultTolerance = 0.00001f;
        ASSERT_TRUE(constantVectorNearFoundInAST(result, floatFaultTolerance));
    }
    
    // FoldMxNMatrixDeterminant tests check if the matrix 'determinant' operation
    // on MxN matrix is constant folded when argument is constant expression and also
    // checks the correctness of the result returned by the constant folding operation.
    // All the matrices including matrices in the shader code are in column-major order.
    TEST_F(ConstantFoldingTest, Fold2x2MatrixDeterminant)
    {
        const std::string &shaderString =
            "#version 300 es\n"
            "precision mediump float;\n"
            "out float my_Float;"
            "void main() {\n"
            "   const float f = determinant(mat2(2.0f, 3.0f,\n"
            "                                    5.0f, 7.0f));\n"
            "   my_Float = f;\n"
            "}\n";
        compileAssumeSuccess(shaderString);
        float inputElements[] =
        {
            2.0f, 3.0f,
            5.0f, 7.0f
        };
        std::vector<float> input(inputElements, inputElements + 4);
        ASSERT_FALSE(constantColumnMajorMatrixFoundInAST(input));
        ASSERT_TRUE(constantFoundInAST(-1.0f));
    }
    
    // Check if the matrix 'determinant' operation on 3x3 matrix is constant folded.
    TEST_F(ConstantFoldingTest, Fold3x3MatrixDeterminant)
    {
        const std::string &shaderString =
            "#version 300 es\n"
            "precision mediump float;\n"
            "out float my_Float;"
            "void main() {\n"
            "   const float f = determinant(mat3(11.0f, 13.0f, 19.0f,\n"
                 "                               23.0f, 29.0f, 31.0f,\n"
            "                                    37.0f, 41.0f, 43.0f));\n"
            "   my_Float = f;\n"
            "}\n";
        compileAssumeSuccess(shaderString);
        float inputElements[] =
        {
            11.0f, 13.0f, 19.0f,
            23.0f, 29.0f, 31.0f,
            37.0f, 41.0f, 43.0f
        };
        std::vector<float> input(inputElements, inputElements + 9);
        ASSERT_FALSE(constantColumnMajorMatrixFoundInAST(input));
        ASSERT_TRUE(constantFoundInAST(-680.0f));
    }
    
    // Check if the matrix 'determinant' operation on 4x4 matrix is constant folded.
    TEST_F(ConstantFoldingTest, Fold4x4MatrixDeterminant)
    {
        const std::string &shaderString =
            "#version 300 es\n"
            "precision mediump float;\n"
            "out float my_Float;"
            "void main() {\n"
            "   const float f = determinant(mat4(29.0f, 31.0f, 37.0f, 41.0f,\n"
            "                                    43.0f, 47.0f, 53.0f, 59.0f,\n"
            "                                    61.0f, 67.0f, 71.0f, 73.0f,\n"
            "                                    79.0f, 83.0f, 89.0f, 97.0f));\n"
            "   my_Float = f;\n"
            "}\n";
        compileAssumeSuccess(shaderString);
        float inputElements[] =
        {
            29.0f, 31.0f, 37.0f, 41.0f,
            43.0f, 47.0f, 53.0f, 59.0f,
            61.0f, 67.0f, 71.0f, 73.0f,
            79.0f, 83.0f, 89.0f, 97.0f
        };
        std::vector<float> input(inputElements, inputElements + 16);
        ASSERT_FALSE(constantColumnMajorMatrixFoundInAST(input));
        ASSERT_TRUE(constantFoundInAST(-2520.0f));
    }
    
    // Check if the matrix 'transpose' operation on 3x3 matrix is constant folded.
    // All the matrices including matrices in the shader code are in column-major order.
    TEST_F(ConstantFoldingTest, Fold3x3MatrixTranspose)
    {
        const std::string &shaderString =
            "#version 300 es\n"
            "precision mediump float;\n"
            "in float i;\n"
            "out vec3 my_Vec;\n"
            "void main() {\n"
            "   const mat3 m3 = transpose(mat3(11.0f, 13.0f, 19.0f,\n"
            "                                  23.0f, 29.0f, 31.0f,\n"
            "                                  37.0f, 41.0f, 43.0f));\n"
            "   mat3 m = m3 * mat3(i);\n"
            "   my_Vec = m[0];\n"
            "}\n";
        compileAssumeSuccess(shaderString);
        float inputElements[] =
        {
            11.0f, 13.0f, 19.0f,
            23.0f, 29.0f, 31.0f,
            37.0f, 41.0f, 43.0f
        };
        std::vector<float> input(inputElements, inputElements + 9);
        ASSERT_FALSE(constantColumnMajorMatrixFoundInAST(input));
        float outputElements[] =
        {
            11.0f, 23.0f, 37.0f,
            13.0f, 29.0f, 41.0f,
            19.0f, 31.0f, 43.0f
        };
        std::vector<float> result(outputElements, outputElements + 9);
        ASSERT_TRUE(constantColumnMajorMatrixFoundInAST(result));
    }
    
    // Test that 0xFFFFFFFF wraps to -1 when parsed as integer.
    // This is featured in the examples of ESSL3 section 4.1.3. ESSL3 section 12.42
    // means that any 32-bit unsigned integer value is a valid literal.
    TEST_F(ConstantFoldingTest, ParseWrappedHexIntLiteral)
    {
        const std::string &shaderString =
            "#version 300 es\n"
            "precision mediump float;\n"
            "precision highp int;\n"
            "uniform int inInt;\n"
            "out vec4 my_Vec;\n"
            "void main() {\n"
            "   const int i = 0xFFFFFFFF;\n"
            "   my_Vec = vec4(i * inInt);\n"
            "}\n";
        compileAssumeSuccess(shaderString);
        ASSERT_TRUE(constantFoundInAST(-1));
    }
    
    // Test that 3000000000 wraps to -1294967296 when parsed as integer.
    // This is featured in the examples of GLSL 4.5, and ESSL behavior should match
    // desktop GLSL when it comes to integer parsing.
    TEST_F(ConstantFoldingTest, ParseWrappedDecimalIntLiteral)
    {
        const std::string &shaderString =
            "#version 300 es\n"
            "precision mediump float;\n"
            "precision highp int;\n"
            "uniform int inInt;\n"
            "out vec4 my_Vec;\n"
            "void main() {\n"
            "   const int i = 3000000000;\n"
            "   my_Vec = vec4(i * inInt);\n"
            "}\n";
        compileAssumeSuccess(shaderString);
        ASSERT_TRUE(constantFoundInAST(-1294967296));
    }
    
    // Test that 0xFFFFFFFFu is parsed correctly as an unsigned integer literal.
    // This is featured in the examples of ESSL3 section 4.1.3. ESSL3 section 12.42
    // means that any 32-bit unsigned integer value is a valid literal.
    TEST_F(ConstantFoldingTest, ParseMaxUintLiteral)
    {
        const std::string &shaderString =
            "#version 300 es\n"
            "precision mediump float;\n"
            "precision highp int;\n"
            "uniform uint inInt;\n"
            "out vec4 my_Vec;\n"
            "void main() {\n"
            "   const uint i = 0xFFFFFFFFu;\n"
            "   my_Vec = vec4(i * inInt);\n"
            "}\n";
        compileAssumeSuccess(shaderString);
        ASSERT_TRUE(constantFoundInAST(0xFFFFFFFFu));
    }
    
    // Test that unary minus applied to unsigned int is constant folded correctly.
    // This is featured in the examples of ESSL3 section 4.1.3. ESSL3 section 12.42
    // means that any 32-bit unsigned integer value is a valid literal.
    TEST_F(ConstantFoldingTest, FoldUnaryMinusOnUintLiteral)
    {
        const std::string &shaderString =
            "#version 300 es\n"
            "precision mediump float;\n"
            "precision highp int;\n"
            "uniform uint inInt;\n"
            "out vec4 my_Vec;\n"
            "void main() {\n"
            "   const uint i = -1u;\n"
            "   my_Vec = vec4(i * inInt);\n"
            "}\n";
        compileAssumeSuccess(shaderString);
        ASSERT_TRUE(constantFoundInAST(0xFFFFFFFFu));
    }
    
    // Test that constant mat2 initialization with a mat2 parameter works correctly.
    TEST_F(ConstantFoldingTest, FoldMat2ConstructorTakingMat2)
    {
        const std::string &shaderString =
            "precision mediump float;\n"
            "uniform float mult;\n"
            "void main() {\n"
            "   const mat2 cm = mat2(mat2(0.0, 1.0, 2.0, 3.0));\n"
            "   mat2 m = cm * mult;\n"
            "   gl_FragColor = vec4(m[0], m[1]);\n"
            "}\n";
        compileAssumeSuccess(shaderString);
        float outputElements[] =
        {
            0.0f, 1.0f,
            2.0f, 3.0f
        };
        std::vector<float> result(outputElements, outputElements + 4);
        ASSERT_TRUE(constantColumnMajorMatrixFoundInAST(result));
    }
    
    // Test that constant mat2 initialization with an int parameter works correctly.
    TEST_F(ConstantFoldingTest, FoldMat2ConstructorTakingScalar)
    {
        const std::string &shaderString =
            "precision mediump float;\n"
            "uniform float mult;\n"
            "void main() {\n"
            "   const mat2 cm = mat2(3);\n"
            "   mat2 m = cm * mult;\n"
            "   gl_FragColor = vec4(m[0], m[1]);\n"
            "}\n";
        compileAssumeSuccess(shaderString);
        float outputElements[] =
        {
            3.0f, 0.0f,
            0.0f, 3.0f
        };
        std::vector<float> result(outputElements, outputElements + 4);
        ASSERT_TRUE(constantColumnMajorMatrixFoundInAST(result));
    }
    
    // Test that constant mat2 initialization with a mix of parameters works correctly.
    TEST_F(ConstantFoldingTest, FoldMat2ConstructorTakingMix)
    {
        const std::string &shaderString =
            "precision mediump float;\n"
            "uniform float mult;\n"
            "void main() {\n"
            "   const mat2 cm = mat2(-1, vec2(0.0, 1.0), vec4(2.0));\n"
            "   mat2 m = cm * mult;\n"
            "   gl_FragColor = vec4(m[0], m[1]);\n"
            "}\n";
        compileAssumeSuccess(shaderString);
        float outputElements[] =
        {
            -1.0, 0.0f,
            1.0f, 2.0f
        };
        std::vector<float> result(outputElements, outputElements + 4);
        ASSERT_TRUE(constantColumnMajorMatrixFoundInAST(result));
    }
    
    // Test that constant mat2 initialization with a mat3 parameter works correctly.
    TEST_F(ConstantFoldingTest, FoldMat2ConstructorTakingMat3)
    {
        const std::string &shaderString =
            "precision mediump float;\n"
            "uniform float mult;\n"
            "void main() {\n"
            "   const mat2 cm = mat2(mat3(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0));\n"
            "   mat2 m = cm * mult;\n"
            "   gl_FragColor = vec4(m[0], m[1]);\n"
            "}\n";
        compileAssumeSuccess(shaderString);
        float outputElements[] =
        {
            0.0f, 1.0f,
            3.0f, 4.0f
        };
        std::vector<float> result(outputElements, outputElements + 4);
        ASSERT_TRUE(constantColumnMajorMatrixFoundInAST(result));
    }
    
    // Test that constant mat4x3 initialization with a mat3x2 parameter works correctly.
    TEST_F(ConstantFoldingTest, FoldMat4x3ConstructorTakingMat3x2)
    {
        const std::string &shaderString =
            "#version 300 es\n"
            "precision mediump float;\n"
            "uniform float mult;\n"
            "out vec4 my_FragColor;\n"
            "void main() {\n"
            "   const mat4x3 cm = mat4x3(mat3x2(1.0, 2.0,\n"
            "                                   3.0, 4.0,\n"
            "                                   5.0, 6.0));\n"
            "   mat4x3 m = cm * mult;\n"
            "   my_FragColor = vec4(m[0], m[1][0]);\n"
            "}\n";
        compileAssumeSuccess(shaderString);
        float outputElements[] =
        {
            1.0f, 2.0f, 0.0f,
            3.0f, 4.0f, 0.0f,
            5.0f, 6.0f, 1.0f,
            0.0f, 0.0f, 0.0f
        };
        std::vector<float> result(outputElements, outputElements + 12);
        ASSERT_TRUE(constantColumnMajorMatrixFoundInAST(result));
    }
    
    
    // Test that constant mat2 initialization with a vec4 parameter works correctly.
    TEST_F(ConstantFoldingTest, FoldMat2ConstructorTakingVec4)
    {
        const std::string &shaderString =
            "precision mediump float;\n"
            "uniform float mult;\n"
            "void main() {\n"
            "   const mat2 cm = mat2(vec4(0.0, 1.0, 2.0, 3.0));\n"
            "   mat2 m = cm * mult;\n"
            "   gl_FragColor = vec4(m[0], m[1]);\n"
            "}\n";
        compileAssumeSuccess(shaderString);
        float outputElements[] =
        {
            0.0f, 1.0f,
            2.0f, 3.0f
        };
        std::vector<float> result(outputElements, outputElements + 4);
        ASSERT_TRUE(constantColumnMajorMatrixFoundInAST(result));
    }
    
    // Test that equality comparison of two different structs with a nested struct inside returns false.
    TEST_F(ConstantFoldingTest, FoldNestedDifferentStructEqualityComparison)
    {
        const std::string &shaderString =
            "precision mediump float;\n"
            "struct nested {\n"
            "    float f\n;"
            "};\n"
            "struct S {\n"
            "    nested a;\n"
            "    float f;\n"
            "};\n"
            "uniform vec4 mult;\n"
            "void main()\n"
            "{\n"
            "    const S s1 = S(nested(0.0), 2.0);\n"
            "    const S s2 = S(nested(0.0), 3.0);\n"
            "    gl_FragColor = (s1 == s2 ? 1.0 : 0.5) * mult;\n"
            "}\n";
        compileAssumeSuccess(shaderString);
        ASSERT_TRUE(constantFoundInAST(0.5f));
    }
    
    // Test that equality comparison of two identical structs with a nested struct inside returns true.
    TEST_F(ConstantFoldingTest, FoldNestedIdenticalStructEqualityComparison)
    {
        const std::string &shaderString =
            "precision mediump float;\n"
            "struct nested {\n"
            "    float f\n;"
            "};\n"
            "struct S {\n"
            "    nested a;\n"
            "    float f;\n"
            "    int i;\n"
            "};\n"
            "uniform vec4 mult;\n"
            "void main()\n"
            "{\n"
            "    const S s1 = S(nested(0.0), 2.0, 3);\n"
            "    const S s2 = S(nested(0.0), 2.0, 3);\n"
            "    gl_FragColor = (s1 == s2 ? 1.0 : 0.5) * mult;\n"
            "}\n";
        compileAssumeSuccess(shaderString);
        ASSERT_TRUE(constantFoundInAST(1.0f));
    }
    
    // Test that right elements are chosen from non-square matrix
    TEST_F(ConstantFoldingTest, FoldNonSquareMatrixIndexing)
    {
        const std::string &shaderString =
            "#version 300 es\n"
            "precision mediump float;\n"
            "out vec4 my_FragColor;\n"
            "void main()\n"
            "{\n"
            "    my_FragColor = mat3x4(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)[1];\n"
            "}\n";
        compileAssumeSuccess(shaderString);
        float outputElements[] = {4.0f, 5.0f, 6.0f, 7.0f};
        std::vector<float> result(outputElements, outputElements + 4);
        ASSERT_TRUE(constantVectorFoundInAST(result));
    }
    
    // Test that folding outer product of vectors with non-matching lengths works.
    TEST_F(ConstantFoldingTest, FoldNonSquareOuterProduct)
    {
        const std::string &shaderString =
            "#version 300 es\n"
            "precision mediump float;\n"
            "out vec4 my_FragColor;\n"
            "void main()\n"
            "{\n"
            "    mat3x2 prod = outerProduct(vec2(2.0, 3.0), vec3(5.0, 7.0, 11.0));\n"
            "    my_FragColor = vec4(prod[0].x);\n"
            "}\n";
        compileAssumeSuccess(shaderString);
        // clang-format off
        float outputElements[] =
        {
            10.0f, 15.0f,
            14.0f, 21.0f,
            22.0f, 33.0f
        };
        // clang-format on
        std::vector<float> result(outputElements, outputElements + 6);
        ASSERT_TRUE(constantColumnMajorMatrixFoundInAST(result));
    }
    
    // Test that folding bit shift left with non-matching signedness works.
    TEST_F(ConstantFoldingTest, FoldBitShiftLeftDifferentSignedness)
    {
        const std::string &shaderString =
            "#version 300 es\n"
            "precision mediump float;\n"
            "out vec4 my_FragColor;\n"
            "void main()\n"
            "{\n"
            "    uint u = 0xffffffffu << 31;\n"
            "    my_FragColor = vec4(u);\n"
            "}\n";
        compileAssumeSuccess(shaderString);
        ASSERT_TRUE(constantFoundInAST(0x80000000u));
    }
    
    // Test that folding bit shift right with non-matching signedness works.
    TEST_F(ConstantFoldingTest, FoldBitShiftRightDifferentSignedness)
    {
        const std::string &shaderString =
            "#version 300 es\n"
            "precision mediump float;\n"
            "out vec4 my_FragColor;\n"
            "void main()\n"
            "{\n"
            "    uint u = 0xffffffffu >> 30;\n"
            "    my_FragColor = vec4(u);\n"
            "}\n";
        compileAssumeSuccess(shaderString);
        ASSERT_TRUE(constantFoundInAST(0x3u));
    }
    
    // Test that folding signed bit shift right extends the sign bit.
    // ESSL 3.00.6 section 5.9 Expressions.
    TEST_F(ConstantFoldingTest, FoldBitShiftRightExtendSignBit)
    {
        const std::string &shaderString =
            "#version 300 es\n"
            "precision mediump float;\n"
            "out vec4 my_FragColor;\n"
            "void main()\n"
            "{\n"
            "    const int i = 0x8fffe000 >> 6;\n"
            "    uint u = uint(i);"
            "    my_FragColor = vec4(u);\n"
            "}\n";
        compileAssumeSuccess(shaderString);
        // The bits of the operand are 0x8fffe000 = 1000 1111 1111 1111 1110 0000 0000 0000
        // After shifting, they become              1111 1110 0011 1111 1111 1111 1000 0000 = 0xfe3fff80
        ASSERT_TRUE(constantFoundInAST(0xfe3fff80u));
    }
    
    // Signed bit shift left should interpret its operand as a bit pattern. As a consequence a number
    // may turn from positive to negative when shifted left.
    // ESSL 3.00.6 section 5.9 Expressions.
    TEST_F(ConstantFoldingTest, FoldBitShiftLeftInterpretedAsBitPattern)
    {
        const std::string &shaderString =
            "#version 300 es\n"
            "precision mediump float;\n"
            "out vec4 my_FragColor;\n"
            "void main()\n"
            "{\n"
            "    const int i = 0x1fffffff << 3;\n"
            "    uint u = uint(i);"
            "    my_FragColor = vec4(u);\n"
            "}\n";
        compileAssumeSuccess(shaderString);
        ASSERT_TRUE(constantFoundInAST(0xfffffff8u));
    }
    
    // Test that dividing the minimum signed integer by -1 works.
    // ESSL 3.00.6 section 4.1.3 Integers:
    // "However, for the case where the minimum representable value is divided by -1, it is allowed to
    // return either the minimum representable value or the maximum representable value."
    TEST_F(ConstantFoldingTest, FoldDivideMinimumIntegerByMinusOne)
    {
        const std::string &shaderString =
            "#version 300 es\n"
            "precision mediump float;\n"
            "out vec4 my_FragColor;\n"
            "void main()\n"
            "{\n"
            "    int i = 0x80000000 / (-1);\n"
            "    my_FragColor = vec4(i);\n"
            "}\n";
        compileAssumeSuccess(shaderString);
        ASSERT_TRUE(constantFoundInAST(0x7fffffff) || constantFoundInAST(-0x7fffffff - 1));
    }
    
    // Test that folding an unsigned integer addition that overflows works.
    // ESSL 3.00.6 section 4.1.3 Integers:
    // "For all precisions, operations resulting in overflow or underflow will not cause any exception,
    // nor will they saturate, rather they will 'wrap' to yield the low-order n bits of the result where
    // n is the size in bits of the integer."
    TEST_F(ConstantFoldingTest, FoldUnsignedIntegerAddOverflow)
    {
        const std::string &shaderString =
            "#version 300 es\n"
            "precision mediump float;\n"
            "out vec4 my_FragColor;\n"
            "void main()\n"
            "{\n"
            "    uint u = 0xffffffffu + 43u;\n"
            "    my_FragColor = vec4(u);\n"
            "}\n";
        compileAssumeSuccess(shaderString);
        ASSERT_TRUE(constantFoundInAST(42u));
    }
    
    // Test that folding a signed integer addition that overflows works.
    // ESSL 3.00.6 section 4.1.3 Integers:
    // "For all precisions, operations resulting in overflow or underflow will not cause any exception,
    // nor will they saturate, rather they will 'wrap' to yield the low-order n bits of the result where
    // n is the size in bits of the integer."
    TEST_F(ConstantFoldingTest, FoldSignedIntegerAddOverflow)
    {
        const std::string &shaderString =
            "#version 300 es\n"
            "precision mediump float;\n"
            "out vec4 my_FragColor;\n"
            "void main()\n"
            "{\n"
            "    int i = 0x7fffffff + 4;\n"
            "    my_FragColor = vec4(i);\n"
            "}\n";
        compileAssumeSuccess(shaderString);
        ASSERT_TRUE(constantFoundInAST(-0x7ffffffd));
    }
    
    // Test that folding an unsigned integer subtraction that overflows works.
    // ESSL 3.00.6 section 4.1.3 Integers:
    // "For all precisions, operations resulting in overflow or underflow will not cause any exception,
    // nor will they saturate, rather they will 'wrap' to yield the low-order n bits of the result where
    // n is the size in bits of the integer."
    TEST_F(ConstantFoldingTest, FoldUnsignedIntegerDiffOverflow)
    {
        const std::string &shaderString =
            "#version 300 es\n"
            "precision mediump float;\n"
            "out vec4 my_FragColor;\n"
            "void main()\n"
            "{\n"
            "    uint u = 0u - 5u;\n"
            "    my_FragColor = vec4(u);\n"
            "}\n";
        compileAssumeSuccess(shaderString);
        ASSERT_TRUE(constantFoundInAST(0xfffffffbu));
    }
    
    // Test that folding a signed integer subtraction that overflows works.
    // ESSL 3.00.6 section 4.1.3 Integers:
    // "For all precisions, operations resulting in overflow or underflow will not cause any exception,
    // nor will they saturate, rather they will 'wrap' to yield the low-order n bits of the result where
    // n is the size in bits of the integer."
    TEST_F(ConstantFoldingTest, FoldSignedIntegerDiffOverflow)
    {
        const std::string &shaderString =
            "#version 300 es\n"
            "precision mediump float;\n"
            "out vec4 my_FragColor;\n"
            "void main()\n"
            "{\n"
            "    int i = -0x7fffffff - 7;\n"
            "    my_FragColor = vec4(i);\n"
            "}\n";
        compileAssumeSuccess(shaderString);
        ASSERT_TRUE(constantFoundInAST(0x7ffffffa));
    }
    
    // Test that folding an unsigned integer multiplication that overflows works.
    // ESSL 3.00.6 section 4.1.3 Integers:
    // "For all precisions, operations resulting in overflow or underflow will not cause any exception,
    // nor will they saturate, rather they will 'wrap' to yield the low-order n bits of the result where
    // n is the size in bits of the integer."
    TEST_F(ConstantFoldingTest, FoldUnsignedIntegerMultiplyOverflow)
    {
        const std::string &shaderString =
            "#version 300 es\n"
            "precision mediump float;\n"
            "out vec4 my_FragColor;\n"
            "void main()\n"
            "{\n"
            "    uint u = 0xffffffffu * 10u;\n"
            "    my_FragColor = vec4(u);\n"
            "}\n";
        compileAssumeSuccess(shaderString);
        ASSERT_TRUE(constantFoundInAST(0xfffffff6u));
    }
    
    // Test that folding a signed integer multiplication that overflows works.
    // ESSL 3.00.6 section 4.1.3 Integers:
    // "For all precisions, operations resulting in overflow or underflow will not cause any exception,
    // nor will they saturate, rather they will 'wrap' to yield the low-order n bits of the result where
    // n is the size in bits of the integer."
    TEST_F(ConstantFoldingTest, FoldSignedIntegerMultiplyOverflow)
    {
        const std::string &shaderString =
            "#version 300 es\n"
            "precision mediump float;\n"
            "out vec4 my_FragColor;\n"
            "void main()\n"
            "{\n"
            "    int i = 0x7fffffff * 42;\n"
            "    my_FragColor = vec4(i);\n"
            "}\n";
        compileAssumeSuccess(shaderString);
        ASSERT_TRUE(constantFoundInAST(-42));
    }
    
    // Test that folding of negating the minimum representable integer works. Note that in the test
    // "0x80000000" is a negative literal, and the minus sign before it is the negation operator.
    // ESSL 3.00.6 section 4.1.3 Integers:
    // "For all precisions, operations resulting in overflow or underflow will not cause any exception,
    // nor will they saturate, rather they will 'wrap' to yield the low-order n bits of the result where
    // n is the size in bits of the integer."
    TEST_F(ConstantFoldingTest, FoldMinimumSignedIntegerNegation)
    {
        const std::string &shaderString =
            "#version 300 es\n"
            "precision mediump float;\n"
            "out vec4 my_FragColor;\n"
            "void main()\n"
            "{\n"
            "    int i = -0x80000000;\n"
            "    my_FragColor = vec4(i);\n"
            "}\n";
        compileAssumeSuccess(shaderString);
        // Negating the minimum signed integer overflows the positive range, so it wraps back to itself.
        ASSERT_TRUE(constantFoundInAST(-0x7fffffff - 1));
    }
    
    // Test that folding of shifting the minimum representable integer works.
    TEST_F(ConstantFoldingTest, FoldMinimumSignedIntegerRightShift)
    {
        const std::string &shaderString =
            "#version 300 es\n"
            "precision mediump float;\n"
            "out vec4 my_FragColor;\n"
            "void main()\n"
            "{\n"
            "    int i = (0x80000000 >> 1);\n"
            "    int j = (0x80000000 >> 7);\n"
            "    my_FragColor = vec4(i, j, i, j);\n"
            "}\n";
        compileAssumeSuccess(shaderString);
        ASSERT_TRUE(constantFoundInAST(-0x40000000));
        ASSERT_TRUE(constantFoundInAST(-0x01000000));
    }
    
    // Test that folding of shifting by 0 works.
    TEST_F(ConstantFoldingTest, FoldShiftByZero)
    {
        const std::string &shaderString =
            "#version 300 es\n"
            "precision mediump float;\n"
            "out vec4 my_FragColor;\n"
            "void main()\n"
            "{\n"
            "    int i = (3 >> 0);\n"
            "    int j = (73 << 0);\n"
            "    my_FragColor = vec4(i, j, i, j);\n"
            "}\n";
        compileAssumeSuccess(shaderString);
        ASSERT_TRUE(constantFoundInAST(3));
        ASSERT_TRUE(constantFoundInAST(73));
    }
    
    // Test that folding IsInf results in true when the parameter is an out-of-range float literal.
    // ESSL 3.00.6 section 4.1.4 Floats:
    // "If the value of the floating point number is too large (small) to be stored as a single
    // precision value, it is converted to positive (negative) infinity."
    // ESSL 3.00.6 section 12.4:
    // "Mandate support for signed infinities."
    TEST_F(ConstantFoldingTest, FoldIsInfOutOfRangeFloatLiteral)
    {
        const std::string &shaderString =
            "#version 300 es\n"
            "precision mediump float;\n"
            "out vec4 my_FragColor;\n"
            "void main()\n"
            "{\n"
            "    bool b = isinf(1.0e2048);\n"
            "    my_FragColor = vec4(b);\n"
            "}\n";
        compileAssumeSuccess(shaderString);
        ASSERT_TRUE(constantFoundInAST(true));
    }
    
    // Test that floats that are too small to be represented get flushed to zero.
    // ESSL 3.00.6 section 4.1.4 Floats:
    // "A value with a magnitude too small to be represented as a mantissa and exponent is converted to
    // zero."
    TEST_F(ConstantFoldingExpressionTest, FoldTooSmallFloat)
    {
        const std::string &floatString = "1.0e-2048";
        evaluateFloat(floatString);
        ASSERT_TRUE(constantFoundInAST(0.0f));
    }
    
    // IEEE 754 dictates that behavior of infinity is derived from limiting cases of real arithmetic.
    // lim radians(x) x -> inf = inf
    // ESSL 3.00.6 section 4.5.1: "Infinities and zeroes are generated as dictated by IEEE".
    TEST_F(ConstantFoldingExpressionTest, FoldRadiansInfinity)
    {
        const std::string &floatString = "radians(1.0e2048)";
        evaluateFloat(floatString);
        ASSERT_TRUE(constantFoundInAST(std::numeric_limits<float>::infinity()));
    }
    
    // IEEE 754 dictates that behavior of infinity is derived from limiting cases of real arithmetic.
    // lim degrees(x) x -> inf = inf
    // ESSL 3.00.6 section 4.5.1: "Infinities and zeroes are generated as dictated by IEEE".
    TEST_F(ConstantFoldingExpressionTest, FoldDegreesInfinity)
    {
        const std::string &floatString = "degrees(1.0e2048)";
        evaluateFloat(floatString);
        ASSERT_TRUE(constantFoundInAST(std::numeric_limits<float>::infinity()));
    }
    
    // IEEE 754 dictates that sinh(inf) = inf.
    // ESSL 3.00.6 section 4.5.1: "Infinities and zeroes are generated as dictated by IEEE".
    TEST_F(ConstantFoldingExpressionTest, FoldSinhInfinity)
    {
        const std::string &floatString = "sinh(1.0e2048)";
        evaluateFloat(floatString);
        ASSERT_TRUE(constantFoundInAST(std::numeric_limits<float>::infinity()));
    }
    
    // IEEE 754 dictates that sinh(-inf) = -inf.
    // ESSL 3.00.6 section 4.5.1: "Infinities and zeroes are generated as dictated by IEEE".
    TEST_F(ConstantFoldingExpressionTest, FoldSinhNegativeInfinity)
    {
        const std::string &floatString = "sinh(-1.0e2048)";
        evaluateFloat(floatString);
        ASSERT_TRUE(constantFoundInAST(-std::numeric_limits<float>::infinity()));
    }
    
    // IEEE 754 dictates that cosh(inf) = inf.
    // ESSL 3.00.6 section 4.5.1: "Infinities and zeroes are generated as dictated by IEEE".
    TEST_F(ConstantFoldingExpressionTest, FoldCoshInfinity)
    {
        const std::string &floatString = "cosh(1.0e2048)";
        evaluateFloat(floatString);
        ASSERT_TRUE(constantFoundInAST(std::numeric_limits<float>::infinity()));
    }
    
    // IEEE 754 dictates that cosh(-inf) = inf.
    // ESSL 3.00.6 section 4.5.1: "Infinities and zeroes are generated as dictated by IEEE".
    TEST_F(ConstantFoldingExpressionTest, FoldCoshNegativeInfinity)
    {
        const std::string &floatString = "cosh(-1.0e2048)";
        evaluateFloat(floatString);
        ASSERT_TRUE(constantFoundInAST(std::numeric_limits<float>::infinity()));
    }
    
    // IEEE 754 dictates that asinh(inf) = inf.
    // ESSL 3.00.6 section 4.5.1: "Infinities and zeroes are generated as dictated by IEEE".
    TEST_F(ConstantFoldingExpressionTest, FoldAsinhInfinity)
    {
        const std::string &floatString = "asinh(1.0e2048)";
        evaluateFloat(floatString);
        ASSERT_TRUE(constantFoundInAST(std::numeric_limits<float>::infinity()));
    }
    
    // IEEE 754 dictates that asinh(-inf) = -inf.
    // ESSL 3.00.6 section 4.5.1: "Infinities and zeroes are generated as dictated by IEEE".
    TEST_F(ConstantFoldingExpressionTest, FoldAsinhNegativeInfinity)
    {
        const std::string &floatString = "asinh(-1.0e2048)";
        evaluateFloat(floatString);
        ASSERT_TRUE(constantFoundInAST(-std::numeric_limits<float>::infinity()));
    }
    
    // IEEE 754 dictates that acosh(inf) = inf.
    // ESSL 3.00.6 section 4.5.1: "Infinities and zeroes are generated as dictated by IEEE".
    TEST_F(ConstantFoldingExpressionTest, FoldAcoshInfinity)
    {
        const std::string &floatString = "acosh(1.0e2048)";
        evaluateFloat(floatString);
        ASSERT_TRUE(constantFoundInAST(std::numeric_limits<float>::infinity()));
    }
    
    // IEEE 754 dictates that pow or powr(0, inf) = 0.
    // ESSL 3.00.6 section 4.5.1: "Infinities and zeroes are generated as dictated by IEEE".
    TEST_F(ConstantFoldingExpressionTest, FoldPowInfinity)
    {
        const std::string &floatString = "pow(0.0, 1.0e2048)";
        evaluateFloat(floatString);
        ASSERT_TRUE(constantFoundInAST(0.0f));
    }
    
    // IEEE 754 dictates that exp(inf) = inf.
    // ESSL 3.00.6 section 4.5.1: "Infinities and zeroes are generated as dictated by IEEE".
    TEST_F(ConstantFoldingExpressionTest, FoldExpInfinity)
    {
        const std::string &floatString = "exp(1.0e2048)";
        evaluateFloat(floatString);
        ASSERT_TRUE(constantFoundInAST(std::numeric_limits<float>::infinity()));
    }
    
    // IEEE 754 dictates that exp(-inf) = 0.
    // ESSL 3.00.6 section 4.5.1: "Infinities and zeroes are generated as dictated by IEEE".
    TEST_F(ConstantFoldingExpressionTest, FoldExpNegativeInfinity)
    {
        const std::string &floatString = "exp(-1.0e2048)";
        evaluateFloat(floatString);
        ASSERT_TRUE(constantFoundInAST(0.0f));
    }
    
    // IEEE 754 dictates that log(inf) = inf.
    // ESSL 3.00.6 section 4.5.1: "Infinities and zeroes are generated as dictated by IEEE".
    TEST_F(ConstantFoldingExpressionTest, FoldLogInfinity)
    {
        const std::string &floatString = "log(1.0e2048)";
        evaluateFloat(floatString);
        ASSERT_TRUE(constantFoundInAST(std::numeric_limits<float>::infinity()));
    }
    
    // IEEE 754 dictates that exp2(inf) = inf.
    // ESSL 3.00.6 section 4.5.1: "Infinities and zeroes are generated as dictated by IEEE".
    TEST_F(ConstantFoldingExpressionTest, FoldExp2Infinity)
    {
        const std::string &floatString = "exp2(1.0e2048)";
        evaluateFloat(floatString);
        ASSERT_TRUE(constantFoundInAST(std::numeric_limits<float>::infinity()));
    }
    
    // IEEE 754 dictates that exp2(-inf) = 0.
    // ESSL 3.00.6 section 4.5.1: "Infinities and zeroes are generated as dictated by IEEE".
    TEST_F(ConstantFoldingExpressionTest, FoldExp2NegativeInfinity)
    {
        const std::string &floatString = "exp2(-1.0e2048)";
        evaluateFloat(floatString);
        ASSERT_TRUE(constantFoundInAST(0.0f));
    }
    
    // IEEE 754 dictates that log2(inf) = inf.
    // ESSL 3.00.6 section 4.5.1: "Infinities and zeroes are generated as dictated by IEEE".
    TEST_F(ConstantFoldingExpressionTest, FoldLog2Infinity)
    {
        const std::string &floatString = "log2(1.0e2048)";
        evaluateFloat(floatString);
        ASSERT_TRUE(constantFoundInAST(std::numeric_limits<float>::infinity()));
    }
    
    // IEEE 754 dictates that behavior of infinity is derived from limiting cases of real arithmetic.
    // lim sqrt(x) x -> inf = inf
    // ESSL 3.00.6 section 4.5.1: "Infinities and zeroes are generated as dictated by IEEE".
    TEST_F(ConstantFoldingExpressionTest, FoldSqrtInfinity)
    {
        const std::string &floatString = "sqrt(1.0e2048)";
        evaluateFloat(floatString);
        ASSERT_TRUE(constantFoundInAST(std::numeric_limits<float>::infinity()));
    }
    
    // IEEE 754 dictates that rSqrt(inf) = 0
    // ESSL 3.00.6 section 4.5.1: "Infinities and zeroes are generated as dictated by IEEE".
    TEST_F(ConstantFoldingExpressionTest, FoldInversesqrtInfinity)
    {
        const std::string &floatString = "inversesqrt(1.0e2048)";
        evaluateFloat(floatString);
        ASSERT_TRUE(constantFoundInAST(0.0f));
    }
    
    // IEEE 754 dictates that behavior of infinity is derived from limiting cases of real arithmetic.
    // lim length(x) x -> inf = inf
    // ESSL 3.00.6 section 4.5.1: "Infinities and zeroes are generated as dictated by IEEE".
    TEST_F(ConstantFoldingExpressionTest, FoldLengthInfinity)
    {
        const std::string &floatString = "length(1.0e2048)";
        evaluateFloat(floatString);
        ASSERT_TRUE(constantFoundInAST(std::numeric_limits<float>::infinity()));
    }
    
    // IEEE 754 dictates that behavior of infinity is derived from limiting cases of real arithmetic.
    // lim dot(x, y) x -> inf, y > 0 = inf
    // ESSL 3.00.6 section 4.5.1: "Infinities and zeroes are generated as dictated by IEEE".
    TEST_F(ConstantFoldingExpressionTest, FoldDotInfinity)
    {
        const std::string &floatString = "dot(1.0e2048, 1.0)";
        evaluateFloat(floatString);
        ASSERT_TRUE(constantFoundInAST(std::numeric_limits<float>::infinity()));
    }
    
    // IEEE 754 dictates that behavior of infinity is derived from limiting cases of real arithmetic.
    // lim dot(vec2(x, y), vec2(z)) x -> inf, finite y, z > 0 = inf
    // ESSL 3.00.6 section 4.5.1: "Infinities and zeroes are generated as dictated by IEEE".
    TEST_F(ConstantFoldingExpressionTest, FoldDotInfinity2)
    {
        const std::string &floatString = "dot(vec2(1.0e2048, -1.0), vec2(1.0))";
        evaluateFloat(floatString);
        ASSERT_TRUE(constantFoundInAST(std::numeric_limits<float>::infinity()));
    }
    
    // Faceforward behavior with infinity as a parameter can be derived from dot().
    // ESSL 3.00.6 section 4.5.1: "Infinities and zeroes are generated as dictated by IEEE".
    TEST_F(ConstantFoldingExpressionTest, FoldFaceForwardInfinity)
    {
        const std::string &floatString = "faceforward(4.0, 1.0e2048, 1.0)";
        evaluateFloat(floatString);
        ASSERT_TRUE(constantFoundInAST(-4.0f));
    }
    
    // Faceforward behavior with infinity as a parameter can be derived from dot().
    // ESSL 3.00.6 section 4.5.1: "Infinities and zeroes are generated as dictated by IEEE".
    TEST_F(ConstantFoldingExpressionTest, FoldFaceForwardInfinity2)
    {
        const std::string &floatString = "faceforward(vec2(4.0), vec2(1.0e2048, -1.0), vec2(1.0)).x";
        evaluateFloat(floatString);
        ASSERT_TRUE(constantFoundInAST(-4.0f));
    }
    
    // Test that infinity - finite value evaluates to infinity.
    // ESSL 3.00.6 section 4.5.1: "Infinities and zeroes are generated as dictated by IEEE".
    TEST_F(ConstantFoldingExpressionTest, FoldInfinityMinusFinite)
    {
        const std::string &floatString = "1.0e2048 - 1.0e20";
        evaluateFloat(floatString);
        ASSERT_TRUE(constantFoundInAST(std::numeric_limits<float>::infinity()));
    }
    
    // Test that -infinity + finite value evaluates to -infinity.
    // ESSL 3.00.6 section 4.5.1: "Infinities and zeroes are generated as dictated by IEEE".
    TEST_F(ConstantFoldingExpressionTest, FoldMinusInfinityPlusFinite)
    {
        const std::string &floatString = "(-1.0e2048) + 1.0e20";
        evaluateFloat(floatString);
        ASSERT_TRUE(constantFoundInAST(-std::numeric_limits<float>::infinity()));
    }
    
    // Test that infinity * finite value evaluates to infinity.
    // ESSL 3.00.6 section 4.5.1: "Infinities and zeroes are generated as dictated by IEEE".
    TEST_F(ConstantFoldingExpressionTest, FoldInfinityMultipliedByFinite)
    {
        const std::string &floatString = "1.0e2048 * 1.0e-20";
        evaluateFloat(floatString);
        ASSERT_TRUE(constantFoundInAST(std::numeric_limits<float>::infinity()));
    }
    
    // Test that infinity * infinity evaluates to infinity.
    // ESSL 3.00.6 section 4.5.1: "Infinities and zeroes are generated as dictated by IEEE".
    TEST_F(ConstantFoldingExpressionTest, FoldInfinityMultipliedByInfinity)
    {
        const std::string &floatString = "1.0e2048 * 1.0e2048";
        evaluateFloat(floatString);
        ASSERT_TRUE(constantFoundInAST(std::numeric_limits<float>::infinity()));
    }
    
    // Test that infinity * negative infinity evaluates to negative infinity.
    // ESSL 3.00.6 section 4.5.1: "Infinities and zeroes are generated as dictated by IEEE".
    TEST_F(ConstantFoldingExpressionTest, FoldInfinityMultipliedByNegativeInfinity)
    {
        const std::string &floatString = "1.0e2048 * (-1.0e2048)";
        evaluateFloat(floatString);
        ASSERT_TRUE(constantFoundInAST(-std::numeric_limits<float>::infinity()));
    }
    
    // Test that dividing by minus zero results in the appropriately signed infinity.
    // ESSL 3.00.6 section 4.5.1: "Infinities and zeroes are generated as dictated by IEEE".
    // "If both positive and negative zeros are implemented, the correctly signed Inf will be
    // generated".
    TEST_F(ConstantFoldingExpressionTest, FoldDivideByNegativeZero)
    {
        const std::string &floatString = "1.0 / (-0.0)";
        evaluateFloat(floatString);
        ASSERT_TRUE(constantFoundInAST(-std::numeric_limits<float>::infinity()));
        ASSERT_TRUE(hasWarning());
    }
    
    // Test that infinity divided by zero evaluates to infinity.
    // ESSL 3.00.6 section 4.5.1: "Infinities and zeroes are generated as dictated by IEEE".
    TEST_F(ConstantFoldingExpressionTest, FoldInfinityDividedByZero)
    {
        const std::string &floatString = "1.0e2048 / 0.0";
        evaluateFloat(floatString);
        ASSERT_TRUE(constantFoundInAST(std::numeric_limits<float>::infinity()));
        ASSERT_TRUE(hasWarning());
    }
    
    // Test that negative infinity divided by zero evaluates to negative infinity.
    // ESSL 3.00.6 section 4.5.1: "Infinities and zeroes are generated as dictated by IEEE".
    TEST_F(ConstantFoldingExpressionTest, FoldMinusInfinityDividedByZero)
    {
        const std::string &floatString = "(-1.0e2048) / 0.0";
        evaluateFloat(floatString);
        ASSERT_TRUE(constantFoundInAST(-std::numeric_limits<float>::infinity()));
        ASSERT_TRUE(hasWarning());
    }
    
    // Test that dividing a finite number by infinity results in zero.
    // ESSL 3.00.6 section 4.5.1: "Infinities and zeroes are generated as dictated by IEEE".
    TEST_F(ConstantFoldingExpressionTest, FoldDivideByInfinity)
    {
        const std::string &floatString = "1.0e30 / 1.0e2048";
        evaluateFloat(floatString);
        ASSERT_TRUE(constantFoundInAST(0.0f));
    }
    
    // Test that unsigned bitfieldExtract is folded correctly.
    TEST_F(ConstantFoldingExpressionTest, FoldUnsignedBitfieldExtract)
    {
        const std::string &uintString = "bitfieldExtract(0x00110000u, 16, 5)";
        evaluateUint(uintString);
        ASSERT_TRUE(constantFoundInAST(0x11u));
    }
    
    // Test that unsigned bitfieldExtract to extract 32 bits is folded correctly.
    TEST_F(ConstantFoldingExpressionTest, FoldUnsignedBitfieldExtract32Bits)
    {
        const std::string &uintString = "bitfieldExtract(0xff0000ffu, 0, 32)";
        evaluateUint(uintString);
        ASSERT_TRUE(constantFoundInAST(0xff0000ffu));
    }
    
    // Test that signed bitfieldExtract is folded correctly. The higher bits should be set to 1 if the
    // most significant bit of the extracted value is 1.
    TEST_F(ConstantFoldingExpressionTest, FoldSignedBitfieldExtract)
    {
        const std::string &intString = "bitfieldExtract(0x00110000, 16, 5)";
        evaluateInt(intString);
        // 0xfffffff1 == -15
        ASSERT_TRUE(constantFoundInAST(-15));
    }
    
    // Test that bitfieldInsert is folded correctly.
    TEST_F(ConstantFoldingExpressionTest, FoldBitfieldInsert)
    {
        const std::string &uintString = "bitfieldInsert(0x04501701u, 0x11u, 8, 5)";
        evaluateUint(uintString);
        ASSERT_TRUE(constantFoundInAST(0x04501101u));
    }
    
    // Test that bitfieldInsert to insert 32 bits is folded correctly.
    TEST_F(ConstantFoldingExpressionTest, FoldBitfieldInsert32Bits)
    {
        const std::string &uintString = "bitfieldInsert(0xff0000ffu, 0x11u, 0, 32)";
        evaluateUint(uintString);
        ASSERT_TRUE(constantFoundInAST(0x11u));
    }
    
    // Test that bitfieldReverse is folded correctly.
    TEST_F(ConstantFoldingExpressionTest, FoldBitfieldReverse)
    {
        const std::string &uintString = "bitfieldReverse((1u << 4u) | (1u << 7u))";
        evaluateUint(uintString);
        uint32_t flag1 = 1u << (31u - 4u);
        uint32_t flag2 = 1u << (31u - 7u);
        ASSERT_TRUE(constantFoundInAST(flag1 | flag2));
    }
    
    // Test that bitCount is folded correctly.
    TEST_F(ConstantFoldingExpressionTest, FoldBitCount)
    {
        const std::string &intString = "bitCount(0x17103121u)";
        evaluateInt(intString);
        ASSERT_TRUE(constantFoundInAST(10));
    }
    
    // Test that findLSB is folded correctly.
    TEST_F(ConstantFoldingExpressionTest, FoldFindLSB)
    {
        const std::string &intString = "findLSB(0x80010000u)";
        evaluateInt(intString);
        ASSERT_TRUE(constantFoundInAST(16));
    }
    
    // Test that findLSB is folded correctly when the operand is zero.
    TEST_F(ConstantFoldingExpressionTest, FoldFindLSBZero)
    {
        const std::string &intString = "findLSB(0u)";
        evaluateInt(intString);
        ASSERT_TRUE(constantFoundInAST(-1));
    }
    
    // Test that findMSB is folded correctly.
    TEST_F(ConstantFoldingExpressionTest, FoldFindMSB)
    {
        const std::string &intString = "findMSB(0x01000008u)";
        evaluateInt(intString);
        ASSERT_TRUE(constantFoundInAST(24));
    }
    
    // Test that findMSB is folded correctly when the operand is zero.
    TEST_F(ConstantFoldingExpressionTest, FoldFindMSBZero)
    {
        const std::string &intString = "findMSB(0u)";
        evaluateInt(intString);
        ASSERT_TRUE(constantFoundInAST(-1));
    }
    
    // Test that findMSB is folded correctly for a negative integer.
    // It is supposed to return the index of the most significant bit set to 0.
    TEST_F(ConstantFoldingExpressionTest, FoldFindMSBNegativeInt)
    {
        const std::string &intString = "findMSB(-8)";
        evaluateInt(intString);
        ASSERT_TRUE(constantFoundInAST(2));
    }
    
    // Test that findMSB is folded correctly for -1.
    TEST_F(ConstantFoldingExpressionTest, FoldFindMSBMinusOne)
    {
        const std::string &intString = "findMSB(-1)";
        evaluateInt(intString);
        ASSERT_TRUE(constantFoundInAST(-1));
    }
    
    // Test that packUnorm4x8 is folded correctly for a vector of zeroes.
    TEST_F(ConstantFoldingExpressionTest, FoldPackUnorm4x8Zero)
    {
        const std::string &intString = "packUnorm4x8(vec4(0.0))";
        evaluateUint(intString);
        ASSERT_TRUE(constantFoundInAST(0u));
    }
    
    // Test that packUnorm4x8 is folded correctly for a vector of ones.
    TEST_F(ConstantFoldingExpressionTest, FoldPackUnorm4x8One)
    {
        const std::string &intString = "packUnorm4x8(vec4(1.0))";
        evaluateUint(intString);
        ASSERT_TRUE(constantFoundInAST(0xffffffffu));
    }
    
    // Test that packSnorm4x8 is folded correctly for a vector of zeroes.
    TEST_F(ConstantFoldingExpressionTest, FoldPackSnorm4x8Zero)
    {
        const std::string &intString = "packSnorm4x8(vec4(0.0))";
        evaluateUint(intString);
        ASSERT_TRUE(constantFoundInAST(0u));
    }
    
    // Test that packSnorm4x8 is folded correctly for a vector of ones.
    TEST_F(ConstantFoldingExpressionTest, FoldPackSnorm4x8One)
    {
        const std::string &intString = "packSnorm4x8(vec4(1.0))";
        evaluateUint(intString);
        ASSERT_TRUE(constantFoundInAST(0x7f7f7f7fu));
    }
    
    // Test that packSnorm4x8 is folded correctly for a vector of minus ones.
    TEST_F(ConstantFoldingExpressionTest, FoldPackSnorm4x8MinusOne)
    {
        const std::string &intString = "packSnorm4x8(vec4(-1.0))";
        evaluateUint(intString);
        ASSERT_TRUE(constantFoundInAST(0x81818181u));
    }
    
    // Test that unpackSnorm4x8 is folded correctly when it needs to clamp the result.
    TEST_F(ConstantFoldingExpressionTest, FoldUnpackSnorm4x8Clamp)
    {
        const std::string &floatString = "unpackSnorm4x8(0x00000080u).x";
        evaluateFloat(floatString);
        ASSERT_TRUE(constantFoundInAST(-1.0f));
    }
    
    // Test that unpackUnorm4x8 is folded correctly.
    TEST_F(ConstantFoldingExpressionTest, FoldUnpackUnorm4x8)
    {
        const std::string &floatString = "unpackUnorm4x8(0x007bbeefu).z";
        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));
    }