Edit

kc3-lang/angle/src/compiler/translator/util.cpp

Branch :

  • Show log

    Commit

  • Author : Le Hoang Quyen
    Date : 2020-07-06 12:40:58
    Hash : 9277ee74
    Message : Metal: Implement MSAA default framebuffer. GL_SAMPLE_COVERAGE_VALUE is implemented by inserting gl_SampleMask writing logic to fragment shader. New test added: MultisampleTest.ContentPresevedAfterInterruption. - Skip on D3D11 (Bug: angleproject:4609) Bug: angleproject:2634 Change-Id: Ib44daf0baccc36ea320596d81713156047da059c Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2281783 Commit-Queue: Jamie Madill <jmadill@chromium.org> Reviewed-by: Jonah Ryan-Davis <jonahr@google.com> Reviewed-by: Jamie Madill <jmadill@chromium.org>

  • src/compiler/translator/util.cpp
  • //
    // Copyright 2010 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.
    //
    
    #include "compiler/translator/util.h"
    
    #include <limits>
    
    #include "common/utilities.h"
    #include "compiler/preprocessor/numeric_lex.h"
    #include "compiler/translator/ImmutableStringBuilder.h"
    #include "compiler/translator/SymbolTable.h"
    
    bool atoi_clamp(const char *str, unsigned int *value)
    {
        bool success = angle::pp::numeric_lex_int(str, value);
        if (!success)
            *value = std::numeric_limits<unsigned int>::max();
        return success;
    }
    
    namespace sh
    {
    
    namespace
    {
    
    bool IsInterpolationIn(TQualifier qualifier)
    {
        switch (qualifier)
        {
            case EvqSmoothIn:
            case EvqFlatIn:
            case EvqNoPerspectiveIn:
            case EvqCentroidIn:
                return true;
            default:
                return false;
        }
    }
    
    }  // anonymous namespace
    
    float NumericLexFloat32OutOfRangeToInfinity(const std::string &str)
    {
        // Parses a decimal string using scientific notation into a floating point number.
        // Out-of-range values are converted to infinity. Values that are too small to be
        // represented are converted to zero.
    
        // The mantissa in decimal scientific notation. The magnitude of the mantissa integer does not
        // matter.
        unsigned int decimalMantissa = 0;
        size_t i                     = 0;
        bool decimalPointSeen        = false;
        bool nonZeroSeenInMantissa   = false;
    
        // The exponent offset reflects the position of the decimal point.
        int exponentOffset = -1;
    
        // This is just a counter for how many decimal digits are written to decimalMantissa.
        int mantissaDecimalDigits = 0;
    
        while (i < str.length())
        {
            const char c = str[i];
            if (c == 'e' || c == 'E')
            {
                break;
            }
            if (c == '.')
            {
                decimalPointSeen = true;
                ++i;
                continue;
            }
    
            unsigned int digit = static_cast<unsigned int>(c - '0');
            ASSERT(digit < 10u);
            if (digit != 0u)
            {
                nonZeroSeenInMantissa = true;
            }
            if (nonZeroSeenInMantissa)
            {
                // Add bits to the mantissa until space runs out in 32-bit int. This should be
                // enough precision to make the resulting binary mantissa accurate to 1 ULP.
                if (decimalMantissa <= (std::numeric_limits<unsigned int>::max() - 9u) / 10u)
                {
                    decimalMantissa = decimalMantissa * 10u + digit;
                    ++mantissaDecimalDigits;
                }
                if (!decimalPointSeen)
                {
                    ++exponentOffset;
                }
            }
            else if (decimalPointSeen)
            {
                --exponentOffset;
            }
            ++i;
        }
        if (decimalMantissa == 0)
        {
            return 0.0f;
        }
        int exponent = 0;
        if (i < str.length())
        {
            ASSERT(str[i] == 'e' || str[i] == 'E');
            ++i;
            bool exponentOutOfRange = false;
            bool negativeExponent   = false;
            if (str[i] == '-')
            {
                negativeExponent = true;
                ++i;
            }
            else if (str[i] == '+')
            {
                ++i;
            }
            while (i < str.length())
            {
                const char c       = str[i];
                unsigned int digit = static_cast<unsigned int>(c - '0');
                ASSERT(digit < 10u);
                if (exponent <= (std::numeric_limits<int>::max() - 9) / 10)
                {
                    exponent = exponent * 10 + digit;
                }
                else
                {
                    exponentOutOfRange = true;
                }
                ++i;
            }
            if (negativeExponent)
            {
                exponent = -exponent;
            }
            if (exponentOutOfRange)
            {
                if (negativeExponent)
                {
                    return 0.0f;
                }
                else
                {
                    return std::numeric_limits<float>::infinity();
                }
            }
        }
        // Do the calculation in 64-bit to avoid overflow.
        long long exponentLong =
            static_cast<long long>(exponent) + static_cast<long long>(exponentOffset);
        if (exponentLong > std::numeric_limits<float>::max_exponent10)
        {
            return std::numeric_limits<float>::infinity();
        }
        else if (exponentLong < std::numeric_limits<float>::min_exponent10)
        {
            return 0.0f;
        }
        // The exponent is in range, so we need to actually evaluate the float.
        exponent     = static_cast<int>(exponentLong);
        double value = decimalMantissa;
    
        // Calculate the exponent offset to normalize the mantissa.
        int normalizationExponentOffset = 1 - mantissaDecimalDigits;
        // Apply the exponent.
        value *= std::pow(10.0, static_cast<double>(exponent + normalizationExponentOffset));
        if (value > static_cast<double>(std::numeric_limits<float>::max()))
        {
            return std::numeric_limits<float>::infinity();
        }
        if (value < static_cast<double>(std::numeric_limits<float>::min()))
        {
            return 0.0f;
        }
        return static_cast<float>(value);
    }
    
    bool strtof_clamp(const std::string &str, float *value)
    {
        // Custom float parsing that can handle the following corner cases:
        //   1. The decimal mantissa is very small but the exponent is very large, putting the resulting
        //   number inside the float range.
        //   2. The decimal mantissa is very large but the exponent is very small, putting the resulting
        //   number inside the float range.
        //   3. The value is out-of-range and should be evaluated as infinity.
        //   4. The value is too small and should be evaluated as zero.
        // See ESSL 3.00.6 section 4.1.4 for the relevant specification.
        *value = NumericLexFloat32OutOfRangeToInfinity(str);
        return !gl::isInf(*value);
    }
    
    GLenum GLVariableType(const TType &type)
    {
        if (type.getBasicType() == EbtFloat)
        {
            if (type.isVector())
            {
                switch (type.getNominalSize())
                {
                    case 2:
                        return GL_FLOAT_VEC2;
                    case 3:
                        return GL_FLOAT_VEC3;
                    case 4:
                        return GL_FLOAT_VEC4;
                    default:
                        UNREACHABLE();
    #if !UNREACHABLE_IS_NORETURN
                        return GL_NONE;
    #endif
                }
            }
            else if (type.isMatrix())
            {
                switch (type.getCols())
                {
                    case 2:
                        switch (type.getRows())
                        {
                            case 2:
                                return GL_FLOAT_MAT2;
                            case 3:
                                return GL_FLOAT_MAT2x3;
                            case 4:
                                return GL_FLOAT_MAT2x4;
                            default:
                                UNREACHABLE();
    #if !UNREACHABLE_IS_NORETURN
                                return GL_NONE;
    #endif
                        }
    
                    case 3:
                        switch (type.getRows())
                        {
                            case 2:
                                return GL_FLOAT_MAT3x2;
                            case 3:
                                return GL_FLOAT_MAT3;
                            case 4:
                                return GL_FLOAT_MAT3x4;
                            default:
                                UNREACHABLE();
    #if !UNREACHABLE_IS_NORETURN
                                return GL_NONE;
    #endif
                        }
    
                    case 4:
                        switch (type.getRows())
                        {
                            case 2:
                                return GL_FLOAT_MAT4x2;
                            case 3:
                                return GL_FLOAT_MAT4x3;
                            case 4:
                                return GL_FLOAT_MAT4;
                            default:
                                UNREACHABLE();
    #if !UNREACHABLE_IS_NORETURN
                                return GL_NONE;
    #endif
                        }
    
                    default:
                        UNREACHABLE();
    #if !UNREACHABLE_IS_NORETURN
                        return GL_NONE;
    #endif
                }
            }
            else
            {
                return GL_FLOAT;
            }
        }
        else if (type.getBasicType() == EbtInt)
        {
            if (type.isVector())
            {
                switch (type.getNominalSize())
                {
                    case 2:
                        return GL_INT_VEC2;
                    case 3:
                        return GL_INT_VEC3;
                    case 4:
                        return GL_INT_VEC4;
                    default:
                        UNREACHABLE();
    #if !UNREACHABLE_IS_NORETURN
                        return GL_NONE;
    #endif
                }
            }
            else
            {
                ASSERT(!type.isMatrix());
                return GL_INT;
            }
        }
        else if (type.getBasicType() == EbtUInt)
        {
            if (type.isVector())
            {
                switch (type.getNominalSize())
                {
                    case 2:
                        return GL_UNSIGNED_INT_VEC2;
                    case 3:
                        return GL_UNSIGNED_INT_VEC3;
                    case 4:
                        return GL_UNSIGNED_INT_VEC4;
                    default:
                        UNREACHABLE();
    #if !UNREACHABLE_IS_NORETURN
                        return GL_NONE;
    #endif
                }
            }
            else
            {
                ASSERT(!type.isMatrix());
                return GL_UNSIGNED_INT;
            }
        }
        else if (type.getBasicType() == EbtBool)
        {
            if (type.isVector())
            {
                switch (type.getNominalSize())
                {
                    case 2:
                        return GL_BOOL_VEC2;
                    case 3:
                        return GL_BOOL_VEC3;
                    case 4:
                        return GL_BOOL_VEC4;
                    default:
                        UNREACHABLE();
    #if !UNREACHABLE_IS_NORETURN
                        return GL_NONE;
    #endif
                }
            }
            else
            {
                ASSERT(!type.isMatrix());
                return GL_BOOL;
            }
        }
    
        switch (type.getBasicType())
        {
            case EbtSampler2D:
                return GL_SAMPLER_2D;
            case EbtSampler3D:
                return GL_SAMPLER_3D;
            case EbtSamplerCube:
                return GL_SAMPLER_CUBE;
            case EbtSamplerExternalOES:
                return GL_SAMPLER_EXTERNAL_OES;
            case EbtSamplerExternal2DY2YEXT:
                return GL_SAMPLER_EXTERNAL_2D_Y2Y_EXT;
            case EbtSampler2DRect:
                return GL_SAMPLER_2D_RECT_ANGLE;
            case EbtSampler2DArray:
                return GL_SAMPLER_2D_ARRAY;
            case EbtSampler2DMS:
                return GL_SAMPLER_2D_MULTISAMPLE;
            case EbtSampler2DMSArray:
                return GL_SAMPLER_2D_MULTISAMPLE_ARRAY;
            case EbtSamplerCubeArray:
                return GL_SAMPLER_CUBE_MAP_ARRAY;
            case EbtISampler2D:
                return GL_INT_SAMPLER_2D;
            case EbtISampler3D:
                return GL_INT_SAMPLER_3D;
            case EbtISamplerCube:
                return GL_INT_SAMPLER_CUBE;
            case EbtISampler2DArray:
                return GL_INT_SAMPLER_2D_ARRAY;
            case EbtISampler2DMS:
                return GL_INT_SAMPLER_2D_MULTISAMPLE;
            case EbtISampler2DMSArray:
                return GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY;
            case EbtISamplerCubeArray:
                return GL_INT_SAMPLER_CUBE_MAP_ARRAY;
            case EbtUSampler2D:
                return GL_UNSIGNED_INT_SAMPLER_2D;
            case EbtUSampler3D:
                return GL_UNSIGNED_INT_SAMPLER_3D;
            case EbtUSamplerCube:
                return GL_UNSIGNED_INT_SAMPLER_CUBE;
            case EbtUSampler2DArray:
                return GL_UNSIGNED_INT_SAMPLER_2D_ARRAY;
            case EbtUSampler2DMS:
                return GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE;
            case EbtUSampler2DMSArray:
                return GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY;
            case EbtUSamplerCubeArray:
                return GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY;
            case EbtSampler2DShadow:
                return GL_SAMPLER_2D_SHADOW;
            case EbtSamplerCubeShadow:
                return GL_SAMPLER_CUBE_SHADOW;
            case EbtSampler2DArrayShadow:
                return GL_SAMPLER_2D_ARRAY_SHADOW;
            case EbtSamplerCubeArrayShadow:
                return GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW;
            case EbtImage2D:
                return GL_IMAGE_2D;
            case EbtIImage2D:
                return GL_INT_IMAGE_2D;
            case EbtUImage2D:
                return GL_UNSIGNED_INT_IMAGE_2D;
            case EbtImage2DArray:
                return GL_IMAGE_2D_ARRAY;
            case EbtIImage2DArray:
                return GL_INT_IMAGE_2D_ARRAY;
            case EbtUImage2DArray:
                return GL_UNSIGNED_INT_IMAGE_2D_ARRAY;
            case EbtImage3D:
                return GL_IMAGE_3D;
            case EbtIImage3D:
                return GL_INT_IMAGE_3D;
            case EbtUImage3D:
                return GL_UNSIGNED_INT_IMAGE_3D;
            case EbtImageCube:
                return GL_IMAGE_CUBE;
            case EbtIImageCube:
                return GL_INT_IMAGE_CUBE;
            case EbtUImageCube:
                return GL_UNSIGNED_INT_IMAGE_CUBE;
            case EbtImageCubeArray:
                return GL_IMAGE_CUBE_MAP_ARRAY;
            case EbtIImageCubeArray:
                return GL_INT_IMAGE_CUBE_MAP_ARRAY;
            case EbtUImageCubeArray:
                return GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY;
            case EbtAtomicCounter:
                return GL_UNSIGNED_INT_ATOMIC_COUNTER;
            case EbtSamplerVideoWEBGL:
                return GL_SAMPLER_VIDEO_IMAGE_WEBGL;
            default:
                UNREACHABLE();
        }
    
        return GL_NONE;
    }
    
    GLenum GLVariablePrecision(const TType &type)
    {
        if (type.getBasicType() == EbtFloat)
        {
            switch (type.getPrecision())
            {
                case EbpHigh:
                    return GL_HIGH_FLOAT;
                case EbpMedium:
                    return GL_MEDIUM_FLOAT;
                case EbpLow:
                    return GL_LOW_FLOAT;
                case EbpUndefined:
                    // Desktop specs do not use precision
                    return GL_NONE;
                default:
                    UNREACHABLE();
            }
        }
        else if (type.getBasicType() == EbtInt || type.getBasicType() == EbtUInt)
        {
            switch (type.getPrecision())
            {
                case EbpHigh:
                    return GL_HIGH_INT;
                case EbpMedium:
                    return GL_MEDIUM_INT;
                case EbpLow:
                    return GL_LOW_INT;
                case EbpUndefined:
                    // Desktop specs do not use precision
                    return GL_NONE;
                default:
                    UNREACHABLE();
            }
        }
    
        // Other types (boolean, sampler) don't have a precision
        return GL_NONE;
    }
    
    ImmutableString ArrayString(const TType &type)
    {
        if (!type.isArray())
            return ImmutableString("");
    
        const TSpan<const unsigned int> &arraySizes     = type.getArraySizes();
        constexpr const size_t kMaxDecimalDigitsPerSize = 10u;
        ImmutableStringBuilder arrayString(arraySizes.size() * (kMaxDecimalDigitsPerSize + 2u));
        for (auto arraySizeIter = arraySizes.rbegin(); arraySizeIter != arraySizes.rend();
             ++arraySizeIter)
        {
            arrayString << "[";
            if (*arraySizeIter > 0)
            {
                arrayString.appendDecimal(*arraySizeIter);
            }
            arrayString << "]";
        }
        return arrayString;
    }
    
    ImmutableString GetTypeName(const TType &type, ShHashFunction64 hashFunction, NameMap *nameMap)
    {
        if (type.getBasicType() == EbtStruct)
            return HashName(type.getStruct(), hashFunction, nameMap);
        else
            return ImmutableString(type.getBuiltInTypeNameString());
    }
    
    bool IsVaryingOut(TQualifier qualifier)
    {
        switch (qualifier)
        {
            case EvqVaryingOut:
            case EvqSmoothOut:
            case EvqFlatOut:
            case EvqNoPerspectiveOut:
            case EvqCentroidOut:
            case EvqVertexOut:
            case EvqGeometryOut:
                return true;
    
            default:
                break;
        }
    
        return false;
    }
    
    bool IsVaryingIn(TQualifier qualifier)
    {
        switch (qualifier)
        {
            case EvqVaryingIn:
            case EvqSmoothIn:
            case EvqFlatIn:
            case EvqNoPerspectiveIn:
            case EvqCentroidIn:
            case EvqFragmentIn:
            case EvqGeometryIn:
                return true;
    
            default:
                break;
        }
    
        return false;
    }
    
    bool IsVarying(TQualifier qualifier)
    {
        return IsVaryingIn(qualifier) || IsVaryingOut(qualifier);
    }
    
    bool IsGeometryShaderInput(GLenum shaderType, TQualifier qualifier)
    {
        return (qualifier == EvqGeometryIn) ||
               ((shaderType == GL_GEOMETRY_SHADER_EXT) && IsInterpolationIn(qualifier));
    }
    
    InterpolationType GetInterpolationType(TQualifier qualifier)
    {
        switch (qualifier)
        {
            case EvqFlatIn:
            case EvqFlatOut:
                return INTERPOLATION_FLAT;
    
            case EvqNoPerspectiveIn:
            case EvqNoPerspectiveOut:
                return INTERPOLATION_NOPERSPECTIVE;
    
            case EvqSmoothIn:
            case EvqSmoothOut:
            case EvqVertexOut:
            case EvqFragmentIn:
            case EvqVaryingIn:
            case EvqVaryingOut:
            case EvqGeometryIn:
            case EvqGeometryOut:
                return INTERPOLATION_SMOOTH;
    
            case EvqCentroidIn:
            case EvqCentroidOut:
                return INTERPOLATION_CENTROID;
    
            default:
                UNREACHABLE();
    #if !UNREACHABLE_IS_NORETURN
                return INTERPOLATION_SMOOTH;
    #endif
        }
    }
    
    TType GetShaderVariableBasicType(const sh::ShaderVariable &var)
    {
        switch (var.type)
        {
            case GL_BOOL:
                return TType(EbtBool);
            case GL_BOOL_VEC2:
                return TType(EbtBool, 2);
            case GL_BOOL_VEC3:
                return TType(EbtBool, 3);
            case GL_BOOL_VEC4:
                return TType(EbtBool, 4);
            case GL_FLOAT:
                return TType(EbtFloat);
            case GL_FLOAT_VEC2:
                return TType(EbtFloat, 2);
            case GL_FLOAT_VEC3:
                return TType(EbtFloat, 3);
            case GL_FLOAT_VEC4:
                return TType(EbtFloat, 4);
            case GL_FLOAT_MAT2:
                return TType(EbtFloat, 2, 2);
            case GL_FLOAT_MAT3:
                return TType(EbtFloat, 3, 3);
            case GL_FLOAT_MAT4:
                return TType(EbtFloat, 4, 4);
            case GL_FLOAT_MAT2x3:
                return TType(EbtFloat, 2, 3);
            case GL_FLOAT_MAT2x4:
                return TType(EbtFloat, 2, 4);
            case GL_FLOAT_MAT3x2:
                return TType(EbtFloat, 3, 2);
            case GL_FLOAT_MAT3x4:
                return TType(EbtFloat, 3, 4);
            case GL_FLOAT_MAT4x2:
                return TType(EbtFloat, 4, 2);
            case GL_FLOAT_MAT4x3:
                return TType(EbtFloat, 4, 3);
            case GL_INT:
                return TType(EbtInt);
            case GL_INT_VEC2:
                return TType(EbtInt, 2);
            case GL_INT_VEC3:
                return TType(EbtInt, 3);
            case GL_INT_VEC4:
                return TType(EbtInt, 4);
            case GL_UNSIGNED_INT:
                return TType(EbtUInt);
            case GL_UNSIGNED_INT_VEC2:
                return TType(EbtUInt, 2);
            case GL_UNSIGNED_INT_VEC3:
                return TType(EbtUInt, 3);
            case GL_UNSIGNED_INT_VEC4:
                return TType(EbtUInt, 4);
            default:
                UNREACHABLE();
    #if !UNREACHABLE_IS_NORETURN
                return TType();
    #endif
        }
    }
    
    void DeclareGlobalVariable(TIntermBlock *root, const TVariable *variable)
    {
        TIntermDeclaration *declaration = new TIntermDeclaration();
        declaration->appendDeclarator(new TIntermSymbol(variable));
    
        TIntermSequence *globalSequence = root->getSequence();
        globalSequence->insert(globalSequence->begin(), declaration);
    }
    
    // GLSL ES 1.0.17 4.6.1 The Invariant Qualifier
    bool CanBeInvariantESSL1(TQualifier qualifier)
    {
        return IsVaryingIn(qualifier) || IsVaryingOut(qualifier) ||
               IsBuiltinOutputVariable(qualifier) ||
               (IsBuiltinFragmentInputVariable(qualifier) && qualifier != EvqFrontFacing);
    }
    
    // GLSL ES 3.00 Revision 6, 4.6.1 The Invariant Qualifier
    // GLSL ES 3.10 Revision 4, 4.8.1 The Invariant Qualifier
    bool CanBeInvariantESSL3OrGreater(TQualifier qualifier)
    {
        return IsVaryingOut(qualifier) || qualifier == EvqFragmentOut ||
               IsBuiltinOutputVariable(qualifier);
    }
    
    bool IsBuiltinOutputVariable(TQualifier qualifier)
    {
        switch (qualifier)
        {
            case EvqPosition:
            case EvqPointSize:
            case EvqFragDepth:
            case EvqFragDepthEXT:
            case EvqFragColor:
            case EvqSecondaryFragColorEXT:
            case EvqFragData:
            case EvqSecondaryFragDataEXT:
            case EvqClipDistance:
                return true;
            default:
                break;
        }
        return false;
    }
    
    bool IsBuiltinFragmentInputVariable(TQualifier qualifier)
    {
        switch (qualifier)
        {
            case EvqFragCoord:
            case EvqPointCoord:
            case EvqFrontFacing:
            case EvqHelperInvocation:
                return true;
            default:
                break;
        }
        return false;
    }
    
    bool IsShaderOutput(TQualifier qualifier)
    {
        return IsVaryingOut(qualifier) || IsBuiltinOutputVariable(qualifier);
    }
    
    bool IsOutputESSL(ShShaderOutput output)
    {
        return output == SH_ESSL_OUTPUT;
    }
    
    bool IsOutputGLSL(ShShaderOutput output)
    {
        switch (output)
        {
            case SH_GLSL_130_OUTPUT:
            case SH_GLSL_140_OUTPUT:
            case SH_GLSL_150_CORE_OUTPUT:
            case SH_GLSL_330_CORE_OUTPUT:
            case SH_GLSL_400_CORE_OUTPUT:
            case SH_GLSL_410_CORE_OUTPUT:
            case SH_GLSL_420_CORE_OUTPUT:
            case SH_GLSL_430_CORE_OUTPUT:
            case SH_GLSL_440_CORE_OUTPUT:
            case SH_GLSL_450_CORE_OUTPUT:
            case SH_GLSL_COMPATIBILITY_OUTPUT:
                return true;
            default:
                break;
        }
        return false;
    }
    bool IsOutputHLSL(ShShaderOutput output)
    {
        switch (output)
        {
            case SH_HLSL_3_0_OUTPUT:
            case SH_HLSL_4_1_OUTPUT:
            case SH_HLSL_4_0_FL9_3_OUTPUT:
                return true;
            default:
                break;
        }
        return false;
    }
    bool IsOutputVulkan(ShShaderOutput output)
    {
        return output == SH_GLSL_VULKAN_OUTPUT;
    }
    bool IsOutputMetal(ShShaderOutput output)
    {
        return output == SH_GLSL_METAL_OUTPUT;
    }
    
    bool IsInShaderStorageBlock(TIntermTyped *node)
    {
        TIntermSwizzle *swizzleNode = node->getAsSwizzleNode();
        if (swizzleNode)
        {
            return IsInShaderStorageBlock(swizzleNode->getOperand());
        }
    
        TIntermBinary *binaryNode = node->getAsBinaryNode();
        if (binaryNode)
        {
            switch (binaryNode->getOp())
            {
                case EOpIndexDirectInterfaceBlock:
                case EOpIndexIndirect:
                case EOpIndexDirect:
                case EOpIndexDirectStruct:
                    return IsInShaderStorageBlock(binaryNode->getLeft());
                default:
                    return false;
            }
        }
    
        const TType &type = node->getType();
        return type.getQualifier() == EvqBuffer;
    }
    
    GLenum GetImageInternalFormatType(TLayoutImageInternalFormat iifq)
    {
        switch (iifq)
        {
            case EiifRGBA32F:
                return GL_RGBA32F;
            case EiifRGBA16F:
                return GL_RGBA16F;
            case EiifR32F:
                return GL_R32F;
            case EiifRGBA32UI:
                return GL_RGBA32UI;
            case EiifRGBA16UI:
                return GL_RGBA16UI;
            case EiifRGBA8UI:
                return GL_RGBA8UI;
            case EiifR32UI:
                return GL_R32UI;
            case EiifRGBA32I:
                return GL_RGBA32I;
            case EiifRGBA16I:
                return GL_RGBA16I;
            case EiifRGBA8I:
                return GL_RGBA8I;
            case EiifR32I:
                return GL_R32I;
            case EiifRGBA8:
                return GL_RGBA8;
            case EiifRGBA8_SNORM:
                return GL_RGBA8_SNORM;
            default:
                return GL_NONE;
        }
    }
    
    bool IsSpecWithFunctionBodyNewScope(ShShaderSpec shaderSpec, int shaderVersion)
    {
        return (shaderVersion == 100 && !sh::IsWebGLBasedSpec(shaderSpec));
    }
    
    ImplicitTypeConversion GetConversion(TBasicType t1, TBasicType t2)
    {
        if (t1 == t2)
            return ImplicitTypeConversion::Same;
    
        switch (t1)
        {
            case EbtInt:
                switch (t2)
                {
                    case EbtInt:
                        UNREACHABLE();
                        break;
                    case EbtUInt:
                        return ImplicitTypeConversion::Invalid;
                    case EbtFloat:
                        return ImplicitTypeConversion::Left;
                    default:
                        return ImplicitTypeConversion::Invalid;
                }
                break;
            case EbtUInt:
                switch (t2)
                {
                    case EbtInt:
                        return ImplicitTypeConversion::Invalid;
                    case EbtUInt:
                        UNREACHABLE();
                        break;
                    case EbtFloat:
                        return ImplicitTypeConversion::Left;
                    default:
                        return ImplicitTypeConversion::Invalid;
                }
                break;
            case EbtFloat:
                switch (t2)
                {
                    case EbtInt:
                    case EbtUInt:
                        return ImplicitTypeConversion::Right;
                    case EbtFloat:
                        UNREACHABLE();
                        break;
                    default:
                        return ImplicitTypeConversion::Invalid;
                }
                break;
            default:
                return ImplicitTypeConversion::Invalid;
        }
        return ImplicitTypeConversion::Invalid;
    }
    
    bool IsValidImplicitConversion(sh::ImplicitTypeConversion conversion, TOperator op)
    {
        switch (conversion)
        {
            case sh::ImplicitTypeConversion::Same:
                return true;
            case sh::ImplicitTypeConversion::Left:
                switch (op)
                {
                    case EOpEqual:
                    case EOpNotEqual:
                    case EOpLessThan:
                    case EOpGreaterThan:
                    case EOpLessThanEqual:
                    case EOpGreaterThanEqual:
                    case EOpAdd:
                    case EOpSub:
                    case EOpMul:
                    case EOpDiv:
                        return true;
                    default:
                        break;
                }
                break;
            case sh::ImplicitTypeConversion::Right:
                switch (op)
                {
                    case EOpAssign:
                    case EOpInitialize:
                    case EOpEqual:
                    case EOpNotEqual:
                    case EOpLessThan:
                    case EOpGreaterThan:
                    case EOpLessThanEqual:
                    case EOpGreaterThanEqual:
                    case EOpAdd:
                    case EOpSub:
                    case EOpMul:
                    case EOpDiv:
                    case EOpAddAssign:
                    case EOpSubAssign:
                    case EOpMulAssign:
                    case EOpDivAssign:
                        return true;
                    default:
                        break;
                }
                break;
            case sh::ImplicitTypeConversion::Invalid:
                break;
        }
        return false;
    }
    
    size_t FindFieldIndex(const TFieldList &fieldList, const char *fieldName)
    {
        for (size_t fieldIndex = 0; fieldIndex < fieldList.size(); ++fieldIndex)
        {
            if (strcmp(fieldList[fieldIndex]->name().data(), fieldName) == 0)
            {
                return fieldIndex;
            }
        }
        UNREACHABLE();
        return 0;
    }
    
    }  // namespace sh